PageRenderTime 52ms CodeModel.GetById 13ms app.highlight 33ms RepoModel.GetById 2ms app.codeStats 0ms

/interpreter/tags/at2dist220411/src/edu/vub/at/objects/natives/NATTable.java

http://ambienttalk.googlecode.com/
Java | 533 lines | 390 code | 51 blank | 92 comment | 45 complexity | c6de06a7d3be43d5cbb1f9871dceee00 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * NATTable.java created on 26-jul-2006 at 16:48:34
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.natives;
 29
 30import edu.vub.at.eval.Evaluator;
 31import edu.vub.at.exceptions.InterpreterException;
 32import edu.vub.at.exceptions.XIndexOutOfBounds;
 33import edu.vub.at.objects.ATBoolean;
 34import edu.vub.at.objects.ATClosure;
 35import edu.vub.at.objects.ATContext;
 36import edu.vub.at.objects.ATMethod;
 37import edu.vub.at.objects.ATNil;
 38import edu.vub.at.objects.ATNumber;
 39import edu.vub.at.objects.ATObject;
 40import edu.vub.at.objects.ATTable;
 41import edu.vub.at.objects.ATText;
 42import edu.vub.at.objects.coercion.NativeTypeTags;
 43import edu.vub.at.objects.grammar.ATSymbol;
 44import edu.vub.at.objects.mirrors.DirectNativeMethod;
 45import edu.vub.at.objects.mirrors.NativeClosure;
 46import edu.vub.at.objects.natives.grammar.AGExpression;
 47import edu.vub.at.parser.SourceLocation;
 48import edu.vub.util.TempFieldGenerator;
 49
 50import java.util.HashMap;
 51import java.util.HashSet;
 52import java.util.LinkedList;
 53import java.util.Set;
 54import java.util.Vector;
 55
 56/**
 57 * The native implementation of an AmbientTalk table.
 58 * A table is implemented by a java array.
 59 * 
 60 * An important distinction between AT tables and Java arrays is that
 61 * ATTable objects are indexed from [1..size] rather than [0..size[
 62 * 
 63 * @author tvcutsem
 64 */
 65public class NATTable extends AGExpression implements ATTable {
 66
 67	/**
 68	 * The empty table. This instance is shared between all actors on this VM,
 69	 * which is safe since it is an immutable object.
 70	 */
 71	public final static NATTable EMPTY = new NATTable(new ATObject[] {}) {
 72		// since the empty table is shared, its source location is meaningless
 73	    public SourceLocation impl_getLocation() { return null; }
 74	    public void impl_setLocation(SourceLocation loc) {}
 75	    
 76	    static final long serialVersionUID = 4036096689737987809L;
 77	};
 78	
 79	public final ATObject[] elements_;
 80	
 81	/**
 82	 * Table factory method. Used to enforce that only one empty table
 83	 * in the system exists.
 84	 */
 85	public static final NATTable atValue(ATObject[] array) {
 86		if (array.length == 0)
 87			return NATTable.EMPTY;
 88		else
 89			return new NATTable(array);
 90	}
 91	
 92	/**
 93	 * @return a table of the given size, filled with nil
 94	 */
 95	public static final NATTable ofSize(int size) {
 96		ATObject[] array = new ATObject[size];
 97		for (int i = 0; i < size; i++) {
 98			array[i] = Evaluator.getNil();
 99		}
100		return atValue(array);
101	}
102	
103	/*
104	 * Auxiliary methods to create tables more easily.
105	 */
106	
107	public static final NATTable of(ATObject one) {
108		return new NATTable(new ATObject[] { one });
109	}
110	
111	public static final NATTable of(ATObject one, ATObject two) {
112		return new NATTable(new ATObject[] { one, two });
113	}
114	
115	public static final NATTable of(ATObject one, ATObject two, ATObject three) {
116		return new NATTable(new ATObject[] { one, two, three });
117	}
118	
119	private NATTable(ATObject[] elements) {
120		// assert elements.length > 0
121		elements_ = elements;
122	}
123	
124    public ATTable asTable() { return this; }
125	
126    public boolean isTable() { return true; }
127    
128	public NATTable asNativeTable() { return this; }
129	
130	/**
131	 * To evaluate a table, evaluate all of its constituent expressions, taking
132	 * special care to take into account spliced expressions.
133	 * 
134	 * NATTAB(exps).eval(ctx) = NATTAB(map eval(ctx) over exps)
135	 * 
136	 * @return a table of evaluated arguments
137	 */
138	public ATObject meta_eval(ATContext ctx) throws InterpreterException {
139		if (this == EMPTY) return EMPTY;
140		
141		LinkedList result = new LinkedList();
142		int siz = elements_.length;
143		for (int i = 0; i < elements_.length; i++) {
144			if (elements_[i].isSplice()) {
145				ATObject[] tbl = elements_[i].asSplice().base_expression().meta_eval(ctx).asNativeTable().elements_;
146				for (int j = 0; j < tbl.length; j++) {
147					result.add(tbl[j]);
148				}
149				siz += (tbl.length - 1); // -1 because we replace one element by a table of elements
150			} else {
151				result.add(elements_[i].meta_eval(ctx));
152			}
153		}
154		return atValue((ATObject[]) result.toArray(new ATObject[siz]));
155	}
156	
157	/**
158	 * To quote a table, quote all elements of the table.
159	 * Special care needs to be taken in order to properly deal with unquote-spliced elements.
160	 * When one of the direct elements of the table is an unquote-splice element, the resulting
161	 * unquotation must result in a table whose elements are directly added to this table's elements.
162	 */
163	public ATObject meta_quote(ATContext ctx) throws InterpreterException {
164		if (this == EMPTY) return EMPTY;
165		
166		LinkedList result = new LinkedList();
167		int siz = elements_.length;
168		for (int i = 0; i < elements_.length; i++) {
169			if (elements_[i].isUnquoteSplice()) {
170				ATObject[] tbl = elements_[i].asUnquoteSplice().base_expression().meta_eval(ctx).asNativeTable().elements_;
171				for (int j = 0; j < tbl.length; j++) {
172					result.add(tbl[j]);
173				}
174				siz += (tbl.length - 1); // -1 because we replace one element by a table of elements
175			} else {
176				result.add(elements_[i].meta_quote(ctx));
177			}
178		}
179		return atValue((ATObject[]) result.toArray(new ATObject[siz]));
180	}
181	
182	public NATText meta_print() throws InterpreterException {
183		return Evaluator.printElements(this, "[", ", ","]");
184	}
185	
186	public NATText impl_asCode(TempFieldGenerator objectMap) throws InterpreterException {
187		if(objectMap.contains(this)) {
188			return objectMap.getName(this);
189		}
190		
191		StringBuffer out = new StringBuffer("[");
192		for(int i = 0 ; i < elements_.length ; i++) {
193			if(i > 0) { out.append(", "); }
194			out.append(elements_[i].impl_asCode(objectMap).javaValue);
195		}
196		out.append("]");
197		NATText code = NATText.atValue(out.toString());
198		NATText name = objectMap.put(this, code);
199		return name;
200	}
201	
202	public NATText impl_asUnquotedCode(TempFieldGenerator objectMap) throws InterpreterException {
203		StringBuffer out = new StringBuffer("[");
204		for(int i = 0 ; i < elements_.length ; i++) {
205			if(i > 0) { out.append(", "); }
206			out.append(elements_[i].impl_asUnquotedCode(objectMap).javaValue);
207		}
208		out.append("]");
209		NATText code = NATText.atValue(out.toString());
210		return code;
211	}
212	
213    public ATTable meta_typeTags() throws InterpreterException {
214    	return NATTable.of(NativeTypeTags._TABLE_);
215    }
216	
217	public ATNumber base_length() { return NATNumber.atValue(elements_.length); }
218
219	public ATObject base_at(ATNumber index) throws InterpreterException {
220		return elements_[extractIndex(index)];
221	}
222
223	public ATObject base_atPut(ATNumber index, ATObject value) throws InterpreterException {
224		elements_[extractIndex(index)] = value;
225		return value;
226	}
227	
228	public ATBoolean base_isEmpty() {
229		return NATBoolean.atValue(elements_.length == 0);
230	}
231	
232	public ATNil base_each_(ATClosure clo) throws InterpreterException {
233		for (int i = 0; i < elements_.length; i++) {
234			clo.base_apply(atValue(new ATObject[] { elements_[i] }));
235		}
236		return Evaluator.getNil();
237	}
238
239	public ATTable base_map_(ATClosure clo) throws InterpreterException {
240		if (this == EMPTY) return EMPTY;
241		
242		ATObject[] result = new ATObject[elements_.length];
243		for (int i = 0; i < elements_.length; i++) {
244			result[i] = clo.base_apply(atValue(new ATObject[] { elements_[i] }));
245		}
246		return atValue(result);
247	}
248	
249	public ATObject base_inject_into_(ATObject init, ATClosure clo) throws InterpreterException {
250		ATObject total = init;
251		for (int i = 0; i < elements_.length; i++) {
252			total = clo.base_apply(atValue(new ATObject[] { total, elements_[i] }));
253		}
254		return total;
255	}
256	
257	public ATTable base_filter_(ATClosure clo) throws InterpreterException {
258		Vector matchingElements = new Vector(elements_.length);
259		for (int i = 0; i < elements_.length; i++) {
260			if (clo.base_apply(atValue(new ATObject[] { elements_[i] })).asNativeBoolean().javaValue) {
261				matchingElements.add(elements_[i]);
262			}
263		}
264		return atValue((ATObject[]) matchingElements.toArray(new ATObject[matchingElements.size()]));
265	}
266	
267	public ATObject base_find_(ATClosure clo) throws InterpreterException {
268		for (int i = 0; i < elements_.length; i++) {
269			if (clo.base_apply(atValue(new ATObject[] { elements_[i] })).asNativeBoolean().javaValue) {
270				return NATNumber.atValue(i+1);
271			}
272		}
273		return Evaluator.getNil();
274	}
275	
276	public ATBoolean base_contains(ATObject obj) throws InterpreterException {
277		for (int i = 0; i < elements_.length; i++) {
278			if (obj.equals(elements_[i])) {
279				return NATBoolean._TRUE_;
280			}
281		}
282		return NATBoolean._FALSE_;
283	}
284	
285	public ATText base_implode() throws InterpreterException {
286		StringBuffer buff = new StringBuffer("");
287		for (int i = 0; i < elements_.length; i++) {
288			buff.append(elements_[i].asNativeText().javaValue);
289		}
290		return NATText.atValue(buff.toString());
291	}
292
293	public ATText base_join(ATText sep) throws InterpreterException {
294		String separator = sep.asNativeText().javaValue;
295		StringBuffer buff = new StringBuffer("");
296		for (int i = 0; i < elements_.length-1; i++) {
297			buff.append(elements_[i].asNativeText().javaValue);
298			buff.append(separator);
299		}
300		if (elements_.length > 0)
301			buff.append(elements_[elements_.length-1].asNativeText().javaValue);
302		return NATText.atValue(buff.toString());
303	}
304	
305	/**
306	 * tab.select(start, stop) == els = [ ] ; start.to: stop do: { |i| els << tab[i] } ; els
307	 */
308	public ATTable base_select(ATNumber first, ATNumber last) throws InterpreterException {
309		final LinkedList selection = new LinkedList();
310		first.base_to_do_(last, new NativeClosure(this) {
311			public ATObject base_apply(ATTable args) throws InterpreterException {
312			    selection.add(base_at(args.base_at(NATNumber.ONE).asNumber()));
313			    return Evaluator.getNil();
314			}
315		});
316		return NATTable.atValue((ATObject[]) selection.toArray(new ATObject[selection.size()]));
317	}
318	
319	public ATTable base__oppls_(ATTable other) throws InterpreterException {
320		return NATTable.atValue(collate(elements_, other.asNativeTable().elements_));
321	}
322	
323	protected int extractIndex(ATNumber atIndex) throws InterpreterException {
324		int javaIndex = atIndex.asNativeNumber().javaValue - 1;
325		if ((javaIndex < 0) || (javaIndex >= elements_.length))
326			throw new XIndexOutOfBounds(javaIndex + 1, elements_.length);
327		else
328			return javaIndex;
329	}
330
331	/**
332	 * Auxiliary method to collate two Java arrays
333	 * @return an array containing first the elements of ary1, then the elements of ary2
334	 */
335	public static final ATObject[] collate(ATObject[] ary1, ATObject[] ary2) {
336	    int siz1 = ary1.length;
337	    int siz2 = ary2.length;
338	    ATObject[] union = new ATObject[siz1 + siz2];
339	    System.arraycopy(ary1, 0, union, 0, siz1);
340	    System.arraycopy(ary2, 0, union, siz1, siz2);
341	    return union;
342	}
343	
344	public ATObject meta_clone() throws InterpreterException {
345		ATObject[] clonedArray = new ATObject[elements_.length];
346		System.arraycopy(elements_, 0, clonedArray, 0, elements_.length);
347		return NATTable.atValue(clonedArray);
348	}
349	
350	public ATObject meta_resolve() throws InterpreterException {
351		if (elements_.length == 0)
352			return NATTable.EMPTY;
353		else
354			return this;
355	}
356	
357	/**
358	 * FV([exp1, exp2, ...]) = FV(exp1) U FV(exp2) U ...
359	 */
360	public Set impl_freeVariables() throws InterpreterException {
361        HashSet freeVars = new HashSet();
362        for (int i = 0; i < elements_.length; i++) {
363			freeVars.addAll(elements_[i].asExpression().impl_freeVariables());
364		}
365        return freeVars;
366	}
367	
368	public Set impl_quotedFreeVariables() throws InterpreterException {
369        HashSet freeVars = new HashSet();
370        for (int i = 0; i < elements_.length; i++) {
371			freeVars.addAll(elements_[i].asExpression().impl_quotedFreeVariables());
372		}
373        return freeVars;
374	}
375	
376	/**
377	 * This hashmap stores all native methods of native AmbientTalk tables.
378	 * It is populated when this class is loaded, and shared between all
379	 * AmbientTalk actors on this VM. This is safe, since {@link DirectNativeMethod}
380	 * instances are all immutable.
381	 */
382	private static final HashMap<String, ATMethod> _meths = new HashMap<String, ATMethod>();
383	
384	// initialize NATTable methods
385	static {
386		_meths.put("length", new DirectNativeMethod("length") {
387			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
388				NATTable self = ctx.base_receiver().asNativeTable();
389				checkArity(args, 0);
390				return self.base_length();
391			}
392		});
393		_meths.put("at", new DirectNativeMethod("at") {
394			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
395				NATTable self = ctx.base_receiver().asNativeTable();
396				checkArity(args, 1);
397				ATNumber index = get(args, 1).asNumber();
398				return self.base_at(index);
399			}
400		});
401		_meths.put("atPut", new DirectNativeMethod("atPut") {
402			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
403				NATTable self = ctx.base_receiver().asNativeTable();
404				checkArity(args, 2);
405				ATNumber index = get(args, 1).asNumber();
406				ATObject value = get(args, 2);
407				return self.base_atPut(index, value);
408			}
409		});
410		_meths.put("isEmpty", new DirectNativeMethod("isEmpty") {
411			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
412				NATTable self = ctx.base_receiver().asNativeTable();
413				checkArity(args, 0);
414				return self.base_isEmpty();
415			}
416		});
417		_meths.put("each:", new DirectNativeMethod("each:") {
418			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
419				NATTable self = ctx.base_receiver().asNativeTable();
420				checkArity(args, 1);
421				ATClosure clo = get(args, 1).asClosure();
422				return self.base_each_(clo);
423			}
424		});
425		_meths.put("map:", new DirectNativeMethod("map:") {
426			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
427				NATTable self = ctx.base_receiver().asNativeTable();
428				checkArity(args, 1);
429				ATClosure clo = get(args, 1).asClosure();
430				return self.base_map_(clo);
431			}
432		});
433		_meths.put("inject:into:", new DirectNativeMethod("inject:into:") {
434			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
435				NATTable self = ctx.base_receiver().asNativeTable();
436				checkArity(args, 2);
437				ATObject init = get(args, 1);
438				ATClosure clo = get(args, 2).asClosure();
439				return self.base_inject_into_(init, clo);
440			}
441		});
442		_meths.put("filter:", new DirectNativeMethod("filter:") {
443			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
444				NATTable self = ctx.base_receiver().asNativeTable();
445				checkArity(args, 1);
446				ATClosure clo = get(args, 1).asClosure();
447				return self.base_filter_(clo);
448			}
449		});
450		_meths.put("find:", new DirectNativeMethod("find:") {
451			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
452				NATTable self = ctx.base_receiver().asNativeTable();
453				checkArity(args, 1);
454				ATClosure clo = get(args, 1).asClosure();
455				return self.base_find_(clo);
456			}
457		});
458		_meths.put("contains", new DirectNativeMethod("contains") {
459			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
460				NATTable self = ctx.base_receiver().asNativeTable();
461				checkArity(args, 1);
462				ATObject obj = get(args, 1);
463				return self.base_contains(obj);
464			}
465		});
466		_meths.put("implode", new DirectNativeMethod("implode") {
467			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
468				NATTable self = ctx.base_receiver().asNativeTable();
469				checkArity(args, 0);
470				return self.base_implode();
471			}
472		});
473		_meths.put("join", new DirectNativeMethod("join") {
474			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
475				NATTable self = ctx.base_receiver().asNativeTable();
476				checkArity(args, 1);
477				ATText sep = get(args, 1).asNativeText();
478				return self.base_join(sep);
479			}
480		});
481		_meths.put("select", new DirectNativeMethod("select") {
482			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
483				NATTable self = ctx.base_receiver().asNativeTable();
484				checkArity(args, 2);
485				ATNumber first = get(args, 1).asNumber();
486				ATNumber last = get(args, 2).asNumber();
487				return self.base_select(first, last);
488			}
489		});
490		_meths.put("+", new DirectNativeMethod("+") {
491			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
492				NATTable self = ctx.base_receiver().asNativeTable();
493				checkArity(args, 1);
494				ATTable other = get(args, 1).asTable();
495				return self.base__oppls_(other);
496			}
497		});
498		_meths.put("==", new DirectNativeMethod("==") {
499			public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException {
500				NATTable self = ctx.base_receiver().asNativeTable();
501				checkArity(args, 1);
502				ATObject other = get(args, 1);
503				return self.base__opeql__opeql_(other);
504			}
505		});
506	}
507	
508	/**
509	 * Overrides the default AmbientTalk native object behavior of extracting native
510	 * methods based on the 'base_' naming convention. Instead, native AT tables use
511	 * an explicit hashmap of native methods. This is much faster than the default
512	 * behavior, which requires reflection.
513	 */
514	protected boolean hasLocalMethod(ATSymbol atSelector) throws InterpreterException {
515		if  (_meths.containsKey(atSelector.base_text().asNativeText().javaValue)) {
516			return true;
517		} else {
518			return super.hasLocalMethod(atSelector);
519		}
520	}
521	
522	/**
523	 * @see NATTable#hasLocalMethod(ATSymbol)
524	 */
525	protected ATMethod getLocalMethod(ATSymbol selector) throws InterpreterException {
526		ATMethod val = _meths.get(selector.base_text().asNativeText().javaValue);
527		if (val == null) {
528			return super.getLocalMethod(selector);
529		}
530		return val;
531	}
532	
533}