PageRenderTime 49ms CodeModel.GetById 13ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist130208/src/edu/vub/at/eval/Evaluator.java

http://ambienttalk.googlecode.com/
Java | 506 lines | 278 code | 55 blank | 173 comment | 62 complexity | cad6fb4bac0f6282beb1d9be6f33fcc1 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 java.io.File;
 31import java.io.FileInputStream;
 32import java.io.IOException;
 33import java.io.InputStream;
 34import java.util.LinkedList;
 35
 36import edu.vub.at.exceptions.InterpreterException;
 37import edu.vub.at.exceptions.XAmbienttalk;
 38import edu.vub.at.exceptions.XArityMismatch;
 39import edu.vub.at.exceptions.XIllegalParameter;
 40import edu.vub.at.objects.ATContext;
 41import edu.vub.at.objects.ATObject;
 42import edu.vub.at.objects.ATTable;
 43import edu.vub.at.objects.grammar.ATAssignVariable;
 44import edu.vub.at.objects.grammar.ATSymbol;
 45import edu.vub.at.objects.mirrors.NATMirrorRoot;
 46import edu.vub.at.objects.natives.NATException;
 47import edu.vub.at.objects.natives.NATObject;
 48import edu.vub.at.objects.natives.NATTable;
 49import edu.vub.at.objects.natives.NATText;
 50import edu.vub.at.objects.natives.OBJLexicalRoot;
 51import edu.vub.at.objects.natives.grammar.AGSplice;
 52import edu.vub.at.objects.natives.grammar.AGSymbol;
 53import edu.vub.at.objects.symbiosis.JavaObject;
 54import edu.vub.at.objects.symbiosis.JavaPackage;
 55import edu.vub.at.objects.symbiosis.XJavaException;
 56import edu.vub.util.Matcher;
 57import edu.vub.util.Pattern;
 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_expression().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_length().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_lexicalScope();
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_name(), 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_name(), param.base_valueExpression().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_expression().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	 * Given a formal parameter list, this auxiliary method returns a new table
305	 * consisting of the values of the bindings of the mandatory parameters of
306	 * formals within the context ctx.
307	 */
308	public static NATTable evalMandatoryPars(ATTable formals, ATContext ctx) throws InterpreterException {
309		if (formals == NATTable.EMPTY) {
310			return NATTable.EMPTY;
311		} else {
312			ATObject[] pars = formals.asNativeTable().elements_;
313			int numMandatory;
314			for (numMandatory = 0; numMandatory < pars.length; numMandatory++) {
315				if (!pars[numMandatory].isSymbol()) {
316					break;
317				}
318			}
319			if (numMandatory > 0) {
320				ATObject[] bindings = new ATObject[numMandatory];
321				for (int i = 0; i < bindings.length; i++) {
322					bindings[i] = pars[i].asSymbol().meta_eval(ctx);
323				}
324				return NATTable.atValue(bindings);
325			} else {
326				return NATTable.EMPTY;
327			}
328		}
329	}
330	
331	/**
332	 * Returns the raw contents of a file in a String (using this JVM's default character encoding)
333	 */
334	public static String loadContentOfFile(File file) throws IOException {
335		InputStream is = new FileInputStream(file);
336		
337	    // Get the size of the file
338	    long length = file.length();
339	
340	    // You cannot create an array using a long type.
341	    // It needs to be an int type.
342	    // Before converting to an int type, check
343	    // to ensure that file is not larger than Integer.MAX_VALUE.
344	    if (length > Integer.MAX_VALUE) {
345	        throw new IOException("File is too large: "+file.getName());
346	    }
347	
348	    // Create the byte array to hold the data
349	    byte[] bytes = new byte[(int)length];
350	
351	    // Read in the bytes
352	    int offset = 0;
353	    int numRead = 0;
354	    while (offset < bytes.length
355	           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
356	        offset += numRead;
357	    }
358	
359	    // Ensure all the bytes have been read in
360	    if (offset < bytes.length) {
361	        throw new IOException("Could not completely read file "+file.getName());
362	    }
363	
364	    // Close the input stream and return bytes
365	    is.close();
366	    return new String(bytes);
367	}
368
369	/**
370	 * @return the 'global' lexical scope of an actor, which is a normal native object
371	 * whose lexical parent is OBJLexicalRoot.
372	 */
373	public static NATObject getGlobalLexicalScope() {
374		return (NATObject) _GLOBAL_SCOPE_.get();
375	}
376
377	/**
378	 * @return the lobby namespace of an actor, which is a normal empty object
379	 */
380	public static NATObject getLobbyNamespace() {
381		return (NATObject) _LOBBY_NAMESPACE_.get();
382	}
383	
384	/**
385	 * @return the jlobby root package of an actor, which is a JavaPackage with an empty path prefix.
386	 */
387	public static JavaPackage getJLobbyRoot() {
388		return (JavaPackage) _JLOBBY_ROOT_.get();
389	}
390	
391	/**
392	 * @return the mirror root of an actor, from which intercessive mirrors usually inherit.
393	 */
394	public static NATMirrorRoot getMirrorRoot() {
395		return (NATMirrorRoot) _MIRROR_ROOT_.get();
396	}
397	
398	/**
399	 * Restores the global lexical scope to a fresh empty object.
400	 * Resets the lobby global namespace.
401	 */
402	public static void resetEnvironment() {
403		_GLOBAL_SCOPE_.set(createGlobalLexicalScope());
404		_LOBBY_NAMESPACE_.set(createLobbyNamespace());
405		_JLOBBY_ROOT_.set(createJLobbyRoot());
406		_MIRROR_ROOT_.set(createMirrorRoot());
407	}
408	
409	/**
410	 * A global scope has the sentinel instance as its lexical parent.
411	 */
412	private static NATObject createGlobalLexicalScope() {
413		NATObject root = new NATObject(OBJLexicalRoot._INSTANCE_);
414		return root;
415	}
416	
417    /**
418     * A lobby namespace is a simple empty object
419     */
420	private static NATObject createLobbyNamespace() {
421		return new NATObject();
422	}
423
424    /**
425     * A jlobby root package is a JavaPackage with an empty path prefix
426     */
427	private static NATObject createJLobbyRoot() {
428		return new JavaPackage("");
429	}
430
431    /**
432     * The default mirror root, with an empty base-level object
433     */
434	private static NATMirrorRoot createMirrorRoot() {
435		return new NATMirrorRoot();
436	}
437	
438	public static final String valueNameOf(Class c) {
439		String name = getSimpleName(c);
440		if (name.startsWith("AT")) {
441			return "a" + classnameToValuename(name, "AT");
442		} else if (name.startsWith("NAT")) {
443			return "a native" + classnameToValuename(name, "NAT");
444		} else if (name.startsWith("AG")) {
445			return "a native AST" + classnameToValuename(name, "AG");
446		} else if (name.startsWith("X")) {
447			return "a native exception" + classnameToValuename(name, "X");
448		} else if (name.startsWith("OBJ")) {
449			return "the native object" + classnameToValuename(name, "OBJ");
450		} else {
451			return name;
452		}
453	}
454	
455	public static final String toString(ATObject obj) {
456		try {
457			return obj.meta_print().javaValue;
458		} catch(InterpreterException e) {
459			return "<unprintable: " + e.getMessage() + ">";
460		}
461	}
462	
463	private static final String classnameToValuename(String classname, String prefix) {
464		 // replace all uppercased letters "L" by " l"		 
465         // Backport from JDK 1.4 to 1.3
466         // Matcher m = p.matcher(classname.replaceFirst(prefix, ""));
467         Pattern p = Pattern.compile("[A-Z]");
468         Matcher m = p.matcher(new StringBuffer(Pattern.compile(prefix).matcher(new StringBuffer(classname)).replaceFirst("")));
469		 // Matcher m = p.matcher(classname.replaceFirst(prefix, ""));
470		 StringBuffer sb = new StringBuffer();
471		 while (m.find()) {
472		     m.appendReplacement(sb, " " + new Character(Character.toLowerCase(m.group().charAt(0))).toString());
473		 }
474		 m.appendTail(sb);
475		 return sb.toString();
476	}
477	
478	// UTILITY METHODS
479	
480	/**
481	 * Returns the unqualified name of a class.
482	 */
483	public static final String getSimpleName(Class c) {
484		String nam = c.getName();
485		return nam.substring(nam.lastIndexOf(".") + 1);
486	}
487	
488
489	public static final InterpreterException asNativeException(ATObject atObj) throws InterpreterException {
490		if (atObj instanceof NATException) {
491			return ((NATException)atObj).getWrappedException();
492		}
493		
494		if (atObj.isJavaObjectUnderSymbiosis()) {
495			JavaObject jObject = atObj.asJavaObjectUnderSymbiosis();
496			
497			Object object = jObject.getWrappedObject();
498			if (object instanceof InterpreterException) {
499				return (InterpreterException) object;				
500			} else if (object instanceof Throwable) {
501				return new XJavaException((Throwable)object);
502			}
503		}
504		return new XAmbienttalk(atObj);
505	}
506}