PageRenderTime 75ms CodeModel.GetById 14ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2-build060407/src/edu/vub/at/eval/Evaluator.java

http://ambienttalk.googlecode.com/
Java | 532 lines | 292 code | 57 blank | 183 comment | 62 complexity | d01d2df2ccf0d4525a38709e3d667276 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * Evaluator.java created on 27-sep-2006 at 15:53:39
  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.eval;
 29
 30import edu.vub.at.exceptions.InterpreterException;
 31import edu.vub.at.exceptions.XAmbienttalk;
 32import edu.vub.at.exceptions.XArityMismatch;
 33import edu.vub.at.exceptions.XIllegalParameter;
 34import edu.vub.at.objects.ATContext;
 35import edu.vub.at.objects.ATObject;
 36import edu.vub.at.objects.ATTable;
 37import edu.vub.at.objects.grammar.ATAssignVariable;
 38import edu.vub.at.objects.grammar.ATSymbol;
 39import edu.vub.at.objects.mirrors.OBJMirrorRoot;
 40import edu.vub.at.objects.natives.NATException;
 41import edu.vub.at.objects.natives.NATObject;
 42import edu.vub.at.objects.natives.NATTable;
 43import edu.vub.at.objects.natives.NATText;
 44import edu.vub.at.objects.natives.OBJLexicalRoot;
 45import edu.vub.at.objects.natives.grammar.AGSplice;
 46import edu.vub.at.objects.natives.grammar.AGSymbol;
 47import edu.vub.at.objects.symbiosis.JavaObject;
 48import edu.vub.at.objects.symbiosis.JavaPackage;
 49import edu.vub.at.objects.symbiosis.XJavaException;
 50import edu.vub.util.Matcher;
 51import edu.vub.util.Pattern;
 52
 53import java.io.File;
 54import java.io.FileInputStream;
 55import java.io.IOException;
 56import java.io.InputStream;
 57import java.util.LinkedList;
 58
 59/**
 60 * The Evaluator class serves as a repository for auxiliary evaluation methods.
 61 * 
 62 * @author tvc
 63 */
 64public final class Evaluator {
 65	
 66	// important symbols
 67	
 68	public static final AGSymbol _ANON_MTH_NAM_ = AGSymbol.jAlloc("nativelambda");
 69	public static final NATTable _ANON_MTH_ARGS_ = NATTable.of(new AGSplice(AGSymbol.jAlloc("args")));
 70	public static final AGSymbol _LAMBDA_    = AGSymbol.alloc(NATText.atValue("lambda"));
 71	public static final AGSymbol _APPLY_     = AGSymbol.alloc(NATText.atValue("apply"));
 72	public static final AGSymbol _INIT_      = AGSymbol.alloc(NATText.atValue("init"));
 73	public static final AGSymbol _CURNS_SYM_ = AGSymbol.jAlloc("~");
 74	
 75	/**
 76	 * A thread-local variable is used to assign a unique global scope to
 77	 * each separate actor. Each actor that invokes the getGlobalLexicalScope
 78	 * method receives its own separate copy of the global scope
 79	 */
 80	private static final ThreadLocal _GLOBAL_SCOPE_ = new ThreadLocal() {
 81	    protected synchronized Object initialValue() {
 82	        return createGlobalLexicalScope();
 83	    }
 84	};
 85	
 86	/**
 87	 * A thread-local variable is used to assign a unique lobby namespace to
 88	 * each separate actor. Each actor that invokes the getLobby()
 89	 * method receives its own separate copy of the lobby namespace
 90	 */
 91	private static final ThreadLocal _LOBBY_NAMESPACE_ = new ThreadLocal() {
 92	    protected synchronized Object initialValue() {
 93	        return createLobbyNamespace();
 94	    }
 95	};
 96	
 97	/**
 98	 * A thread-local variable is used to assign a unique jlobby root to
 99	 * each separate actor. The jlobby root is the root JavaPackage from
100	 * which other Java packages can be loaded. Each actor that invokes the getJLobbyRoot()
101	 * method receives its own separate copy of the jlobby root
102	 */
103	private static final ThreadLocal _JLOBBY_ROOT_ = new ThreadLocal() {
104	    protected synchronized Object initialValue() {
105	        return createJLobbyRoot();
106	    }
107	};
108	
109	/**
110	 * A thread-local variable is used to assign a unique mirror root to
111	 * each separate actor. The mirror root encapsulates the default semantics
112	 * for AmbientTalk objects and is the parent of most interecessive custom mirrors
113	 * defined by AmbientTalk programmers themselves.
114	 */
115	private static final ThreadLocal _MIRROR_ROOT_ = new ThreadLocal() {
116	    protected synchronized Object initialValue() {
117	        return createMirrorRoot();
118	    }
119	};
120	
121	
122	/**
123	 * Auxiliary function used to print the elements of a table using various separators.
124	 */
125	public final static NATText printElements(ATObject[] els,String start, String sep, String stop) throws InterpreterException {
126		if (els.length == 0)
127			return NATText.atValue(String.valueOf(start+stop));
128		
129	    StringBuffer buff = new StringBuffer(start);
130		for (int i = 0; i < els.length - 1; i++) {
131			buff.append(els[i].meta_print().asNativeText().javaValue + sep);
132		}
133		buff.append(els[els.length-1].meta_print().asNativeText().javaValue + stop);
134	    return NATText.atValue(buff.toString());
135	}
136	
137	/**
138	 * Auxiliary function used to print the elements of a table using various separators.
139	 */
140	public final static NATText printElements(NATTable tab,String start, String sep, String stop) throws InterpreterException {
141		return printElements(tab.elements_, start, sep, stop);
142	}
143
144	public static final NATText printAsStatements(ATTable tab) throws InterpreterException {
145		return printElements(tab.asNativeTable(), "", "; ", "");
146	}
147
148	public static final NATText printAsList(ATTable tab) throws InterpreterException {
149		return printElements(tab.asNativeTable(), "(", ", ", ")");
150	}
151
152	/**
153	 * This function is called whenever arguments to a function, message, method need to be evaluated.
154	 * TODO(coercers) currently does not work for user-defined tables
155	 */
156	public static final NATTable evaluateArguments(NATTable args, ATContext ctx) throws InterpreterException {
157		if (args == NATTable.EMPTY) return NATTable.EMPTY;
158		
159		ATObject[] els = args.elements_;
160		
161		LinkedList result = new LinkedList();
162		int siz = els.length;
163		for (int i = 0; i < els.length; i++) {
164			if (els[i].isSplice()) {
165				ATObject[] tbl = els[i].asSplice().base_getExpression().meta_eval(ctx).asNativeTable().elements_;
166				for (int j = 0; j < tbl.length; j++) {
167					result.add(tbl[j]);
168				}
169				siz += (tbl.length - 1); // -1 because we replace one element by a table of elements
170			} else {
171				result.add(els[i].meta_eval(ctx));
172			}
173		}
174		return NATTable.atValue((ATObject[]) result.toArray(new ATObject[siz]));
175	}
176
177	// auxiliary interface to support functor objects
178	private interface BindClosure {
179		public void bindParamToArg(ATObject inScope, ATSymbol param, ATObject arg) throws InterpreterException;
180	}
181	
182	/**
183	 * Auxiliary function to bind formal parameters to actual arguments within a certain scope.
184	 * 
185	 * A formal parameter list is defined as:
186	 * (mandatory arg: ATSymbol)* , (optional arg: ATVarAssignment)*, (rest arg: ATSplice)?
187	 * 
188	 * An actual argument list is defined as:
189	 * (actual arg: ATObject)*
190	 * 
191	 * @deprecated use partial evalation using {@link PartialBinder#bind(ATObject[], ATContext, edu.vub.at.eval.PartialBinder.BindClosure)} instead. 
192	 * 
193	 * @param funnam the name of the function for which to bind these elements, for debugging purposes only
194	 * @param context the context whose lexical scope denotes the frame in which to store the bindings
195	 * The context is also the context in which to evaluate optional argument expressions.
196	 * @param parameters the formal parameter references (of which the last element may be a 'rest' arg to collect left-over arguments)
197	 * @param arguments the actual arguments, already evaluated
198	 * @param binder a functor object describing the strategy to bind an argument to a parameter (assign or define the parameter)
199	 * @throws XArityMismatch when the formals don't match the actuals
200	 */
201	private static final void bindArguments(String funnam, ATContext context, ATTable parameters, ATTable arguments, BindClosure binder) throws InterpreterException {
202		if (parameters == NATTable.EMPTY) {
203			if (arguments == NATTable.EMPTY)
204				return; // no need to bind any arguments
205			else
206				throw new XArityMismatch(funnam, 0, arguments.base_getLength().asNativeNumber().javaValue); 
207		}
208		
209		ATObject[] pars = parameters.asNativeTable().elements_;
210		ATObject[] args = arguments.asNativeTable().elements_;
211		
212		/* Traverse formal parameter list conceptually according to
213		 * the following state diagram:
214		 * 
215		 *  state: mandatory [start state, end state]
216		 *    case Symbol => mandatory
217		 *    case Assignment => optional
218		 *    case Splice => rest-arg
219		 *  state: optional [end state]
220		 *    case Symbol => error // no mandatory pars after optional pars
221		 *    case Assignment => optional
222		 *    case Splice => rest-arg
223		 *  state: rest-arg [end state]
224		 *    case * => error // rest-arg should be last
225		 *  state: error [end state]
226		 *    case * => error
227		 */
228		
229		int paridx = 0;
230		int numMandatoryArguments = 0;
231		ATObject scope = context.base_getLexicalScope();
232		
233		// determine number of mandatory arguments
234		for (; paridx < pars.length && pars[paridx].isSymbol(); paridx++) {
235			numMandatoryArguments++;
236		}
237		
238		// are there enough actual arguments to satisfy all mandatory args?
239		if (numMandatoryArguments > args.length) {
240			// error: not enough actuals
241			throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
242		}
243		
244		// bind all mandatory arguments
245		for (paridx = 0; paridx < numMandatoryArguments; paridx++) {
246			// bind formal to actual
247			binder.bindParamToArg(scope, pars[paridx].asSymbol(), args[paridx]);			
248		}
249		
250		// if there are no more parameters, make sure all actuals are processed
251		if (numMandatoryArguments == pars.length) {
252			if (numMandatoryArguments < args.length) {
253				// error: too many actuals
254				throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
255			} // else { return; }
256		} else {
257		    // if there are more parameters, process optionals first and then rest parameter
258			int numDefaultOptionals = 0; // count the number of optional arguments that had no corresponding actual
259			// determine number of optional arguments
260			for (; paridx < pars.length && pars[paridx].isVariableAssignment(); paridx++) {
261				if (paridx < args.length) {
262					// bind formal to actual and ignore default initialization expression
263					binder.bindParamToArg(scope, pars[paridx].asVariableAssignment().base_getName(), args[paridx]);	
264				} else {
265					// no more actuals: bind optional parameter to default initialization expression
266					ATAssignVariable param = pars[paridx].asVariableAssignment();
267					binder.bindParamToArg(scope, param.base_getName(), param.base_getValueExpression().meta_eval(context));
268					numDefaultOptionals++;
269				}
270			}
271			
272			// if there are no more parameters, make sure all actuals are processed
273			if (paridx == pars.length) {
274				if (paridx < args.length) {
275					// error: too many actuals
276					throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
277				} // else { return; }
278			} else {
279				// all that is left to process is an optional rest-parameter
280				// check whether last param is spliced, which indicates variable parameter list
281				if (pars[paridx].isSplice()) {
282					// bind the last parameter to the remaining arguments
283					int numRemainingArgs = args.length - paridx + numDefaultOptionals; // #actuals - #actuals used to fill in mandatory or optional args 
284					ATObject[] restArgs = new ATObject[numRemainingArgs];
285					for (int i = 0; i < numRemainingArgs; i++) {
286						restArgs[i] = args[i + paridx];
287					}
288					ATSymbol restArgsName = pars[paridx].asSplice().base_getExpression().asSymbol();
289					binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
290					
291					// rest parameter should always be last
292					if (paridx != pars.length - 1) {
293						throw new XIllegalParameter(funnam, "rest parameter is not the last parameter: " + pars[paridx]);
294					} // else { return; }
295				} else {
296					// optionals followed by mandatory parameter
297					throw new XIllegalParameter(funnam, "optional parameters followed by mandatory parameter " + pars[paridx]);
298				}
299			}
300		}
301	}
302	
303	/**
304	 * Bind all of the given parameters as newly defined slots in the given scope to the given arguments.
305	 * The scope is defined as the lexical scope of the given context.
306	 * @deprecated use partial evalation using {@link PartialBinder#bind(ATObject[], ATContext, edu.vub.at.eval.PartialBinder.BindClosure)} instead.
307	 */
308	public static final void defineParamsForArgs(String funnam, ATContext context, ATTable parameters, ATTable arguments) throws InterpreterException {
309		bindArguments(funnam, context, parameters, arguments, new BindClosure() {
310			public void bindParamToArg(ATObject scope, ATSymbol param, ATObject arg) throws InterpreterException {
311				scope.meta_defineField(param, arg);
312			}
313		});
314	}
315	
316	/**
317	 * Assign all of the formal parameter names in the scope object to the given arguments
318	 * The scope is defined as the lexical scope of the given context.
319	 * @deprecated use partial evalation using {@link PartialBinder#bind(ATObject[], ATContext, edu.vub.at.eval.PartialBinder.BindClosure)} instead.
320	 */
321	public static final void assignArgsToParams(String funnam, ATContext context, ATTable parameters, ATTable arguments) throws InterpreterException {
322		bindArguments(funnam, context, parameters, arguments, new BindClosure() {
323			public void bindParamToArg(ATObject scope, ATSymbol param, ATObject arg) throws InterpreterException {
324				scope.meta_assignVariable(param, arg);
325			}
326		});
327	}
328	
329	/**
330	 * Given a formal parameter list, this auxiliary method returns a new table
331	 * consisting of the values of the bindings of the mandatory parameters of
332	 * formals within the context ctx.
333	 */
334	public static NATTable evalMandatoryPars(ATTable formals, ATContext ctx) throws InterpreterException {
335		if (formals == NATTable.EMPTY) {
336			return NATTable.EMPTY;
337		} else {
338			ATObject[] pars = formals.asNativeTable().elements_;
339			int numMandatory;
340			for (numMandatory = 0; numMandatory < pars.length; numMandatory++) {
341				if (!pars[numMandatory].isSymbol()) {
342					break;
343				}
344			}
345			if (numMandatory > 0) {
346				ATObject[] bindings = new ATObject[numMandatory];
347				for (int i = 0; i < bindings.length; i++) {
348					bindings[i] = pars[i].asSymbol().meta_eval(ctx);
349				}
350				return NATTable.atValue(bindings);
351			} else {
352				return NATTable.EMPTY;
353			}
354		}
355	}
356	
357	/**
358	 * Returns the raw contents of a file in a String (using this JVM's default character encoding)
359	 */
360	public static String loadContentOfFile(File file) throws IOException {
361		InputStream is = new FileInputStream(file);
362		
363	    // Get the size of the file
364	    long length = file.length();
365	
366	    // You cannot create an array using a long type.
367	    // It needs to be an int type.
368	    // Before converting to an int type, check
369	    // to ensure that file is not larger than Integer.MAX_VALUE.
370	    if (length > Integer.MAX_VALUE) {
371	        throw new IOException("File is too large: "+file.getName());
372	    }
373	
374	    // Create the byte array to hold the data
375	    byte[] bytes = new byte[(int)length];
376	
377	    // Read in the bytes
378	    int offset = 0;
379	    int numRead = 0;
380	    while (offset < bytes.length
381	           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
382	        offset += numRead;
383	    }
384	
385	    // Ensure all the bytes have been read in
386	    if (offset < bytes.length) {
387	        throw new IOException("Could not completely read file "+file.getName());
388	    }
389	
390	    // Close the input stream and return bytes
391	    is.close();
392	    return new String(bytes);
393	}
394
395	/**
396	 * @return the 'global' lexical scope of an actor, which is a normal native object
397	 * whose lexical parent is OBJLexicalRoot.
398	 */
399	public static NATObject getGlobalLexicalScope() {
400		return (NATObject) _GLOBAL_SCOPE_.get();
401	}
402
403	/**
404	 * @return the lobby namespace of an actor, which is a normal empty object
405	 */
406	public static NATObject getLobbyNamespace() {
407		return (NATObject) _LOBBY_NAMESPACE_.get();
408	}
409	
410	/**
411	 * @return the jlobby root package of an actor, which is a JavaPackage with an empty path prefix.
412	 */
413	public static JavaPackage getJLobbyRoot() {
414		return (JavaPackage) _JLOBBY_ROOT_.get();
415	}
416	
417	/**
418	 * @return the mirror root of an actor, from which intercessive mirrors usually inherit.
419	 */
420	public static OBJMirrorRoot getMirrorRoot() {
421		return (OBJMirrorRoot) _MIRROR_ROOT_.get();
422	}
423	
424	/**
425	 * Restores the global lexical scope to a fresh empty object.
426	 * Resets the lobby global namespace.
427	 */
428	public static void resetEnvironment() {
429		_GLOBAL_SCOPE_.set(createGlobalLexicalScope());
430		_LOBBY_NAMESPACE_.set(createLobbyNamespace());
431		_JLOBBY_ROOT_.set(createJLobbyRoot());
432		_MIRROR_ROOT_.set(createMirrorRoot());
433	}
434	
435	/**
436	 * A global scope has the sentinel instance as its lexical parent.
437	 */
438	private static NATObject createGlobalLexicalScope() {
439		NATObject root = new NATObject(OBJLexicalRoot._INSTANCE_);
440		return root;
441	}
442	
443    /**
444     * A lobby namespace is a simple empty object
445     */
446	private static NATObject createLobbyNamespace() {
447		return new NATObject();
448	}
449
450    /**
451     * A jlobby root package is a JavaPackage with an empty path prefix
452     */
453	private static NATObject createJLobbyRoot() {
454		return new JavaPackage("");
455	}
456
457    /**
458     * The default mirror root, with an empty base-level object
459     */
460	private static OBJMirrorRoot createMirrorRoot() {
461		return new OBJMirrorRoot();
462	}
463	
464	public static final String valueNameOf(Class c) {
465		String name = getSimpleName(c);
466		if (name.startsWith("AT")) {
467			return "a" + classnameToValuename(name, "AT");
468		} else if (name.startsWith("NAT")) {
469			return "a native" + classnameToValuename(name, "NAT");
470		} else if (name.startsWith("AG")) {
471			return "a native AST" + classnameToValuename(name, "AG");
472		} else if (name.startsWith("X")) {
473			return "a native exception" + classnameToValuename(name, "X");
474		} else if (name.startsWith("OBJ")) {
475			return "the native object" + classnameToValuename(name, "OBJ");
476		} else {
477			return name;
478		}
479	}
480	
481	public static final String toString(ATObject obj) {
482		try {
483			return obj.meta_print().javaValue;
484		} catch(InterpreterException e) {
485			return "<unprintable: " + e.getMessage() + ">";
486		}
487	}
488	
489	private static final String classnameToValuename(String classname, String prefix) {
490		 // replace all uppercased letters "L" by " l"		 
491         // Backport from JDK 1.4 to 1.3
492         // Matcher m = p.matcher(classname.replaceFirst(prefix, ""));
493         Pattern p = Pattern.compile("[A-Z]");
494         Matcher m = p.matcher(new StringBuffer(Pattern.compile(prefix).matcher(new StringBuffer(classname)).replaceFirst("")));
495		 // Matcher m = p.matcher(classname.replaceFirst(prefix, ""));
496		 StringBuffer sb = new StringBuffer();
497		 while (m.find()) {
498		     m.appendReplacement(sb, " " + new Character(Character.toLowerCase(m.group().charAt(0))).toString());
499		 }
500		 m.appendTail(sb);
501		 return sb.toString();
502	}
503	
504	// UTILITY METHODS
505	
506	/**
507	 * Returns the unqualified name of a class.
508	 */
509	public static final String getSimpleName(Class c) {
510		String nam = c.getName();
511		return nam.substring(nam.lastIndexOf(".") + 1);
512	}
513	
514
515	public static final InterpreterException asNativeException(ATObject atObj) throws InterpreterException {
516		if (atObj instanceof NATException) {
517			return ((NATException)atObj).getWrappedException();
518		}
519		
520		if (atObj instanceof JavaObject) {
521			JavaObject jObject = (JavaObject) atObj;
522			
523			Object object = jObject.getWrappedObject();
524			if (object instanceof InterpreterException) {
525				return (InterpreterException) object;				
526			} else if (object instanceof Throwable) {
527				return new XJavaException((Throwable)object);
528			}
529		}
530		return new XAmbienttalk(atObj);
531	}
532}