PageRenderTime 58ms CodeModel.GetById 13ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 415 lines | 237 code | 50 blank | 128 comment | 36 complexity | dc2722c813ed66476ddb566abffc63bd 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 org.apache.regexp.RE;
 37import org.apache.regexp.REProgram;
 38
 39import edu.vub.at.exceptions.InterpreterException;
 40import edu.vub.at.exceptions.XAmbienttalk;
 41import edu.vub.at.objects.ATContext;
 42import edu.vub.at.objects.ATObject;
 43import edu.vub.at.objects.ATTable;
 44import edu.vub.at.objects.mirrors.NATMirrorRoot;
 45import edu.vub.at.objects.natives.NATException;
 46import edu.vub.at.objects.natives.NATObject;
 47import edu.vub.at.objects.natives.NATTable;
 48import edu.vub.at.objects.natives.NATText;
 49import edu.vub.at.objects.natives.OBJLexicalRoot;
 50import edu.vub.at.objects.natives.NATNil;
 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.at.util.logging.Logging;
 57import edu.vub.util.Regexp;
 58
 59/**
 60 * The Evaluator class serves as a repository for auxiliary evaluation methods.
 61 * 
 62 * @author tvcutsem
 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	 * A thread-local variable is used to assign a unique nil object to
123	 * each separate actor. This object is the root of the delegation
124	 * chain of all objects owned by that actor.
125	 */
126	private static final ThreadLocal _NIL_ = new ThreadLocal() {
127	    protected synchronized Object initialValue() {
128	        return createNil();
129	    }
130	};
131	
132	
133	/**
134	 * Auxiliary function used to print the elements of a table using various separators.
135	 */
136	public final static NATText printElements(ATObject[] els,String start, String sep, String stop) throws InterpreterException {
137		if (els.length == 0)
138			return NATText.atValue(String.valueOf(start+stop));
139		
140	    StringBuffer buff = new StringBuffer(start);
141		for (int i = 0; i < els.length - 1; i++) {
142			buff.append(els[i].meta_print().asNativeText().javaValue + sep);
143		}
144		buff.append(els[els.length-1].meta_print().asNativeText().javaValue + stop);
145	    return NATText.atValue(buff.toString());
146	}
147	
148	/**
149	 * Auxiliary function used to print the elements of a table using various separators.
150	 */
151	public final static NATText printElements(NATTable tab,String start, String sep, String stop) throws InterpreterException {
152		return printElements(tab.elements_, start, sep, stop);
153	}
154
155	public static final NATText printAsStatements(ATTable tab) throws InterpreterException {
156		return printElements(tab.asNativeTable(), "", "; ", "");
157	}
158
159	public static final NATText printAsList(ATTable tab) throws InterpreterException {
160		return printElements(tab.asNativeTable(), "(", ", ", ")");
161	}
162
163	/**
164	 * This function is called whenever arguments to a function, message, method need to be evaluated.
165	 * TODO(coercers) currently does not work for user-defined tables
166	 */
167	public static final NATTable evaluateArguments(NATTable args, ATContext ctx) throws InterpreterException {
168		if (args == NATTable.EMPTY) return NATTable.EMPTY;
169		
170		ATObject[] els = args.elements_;
171		
172		LinkedList result = new LinkedList();
173		int siz = els.length;
174		for (int i = 0; i < els.length; i++) {
175			if (els[i].isSplice()) {
176				ATObject[] tbl = els[i].asSplice().base_expression().meta_eval(ctx).asNativeTable().elements_;
177				for (int j = 0; j < tbl.length; j++) {
178					result.add(tbl[j]);
179				}
180				siz += (tbl.length - 1); // -1 because we replace one element by a table of elements
181			} else {
182				result.add(els[i].meta_eval(ctx));
183			}
184		}
185		return NATTable.atValue((ATObject[]) result.toArray(new ATObject[siz]));
186	}
187	
188	/**
189	 * Given a formal parameter list, this auxiliary method returns a new table
190	 * consisting of the values of the bindings of the mandatory parameters of
191	 * formals within the context ctx.
192	 */
193	public static NATTable evalMandatoryPars(ATTable formals, ATContext ctx) throws InterpreterException {
194		if (formals == NATTable.EMPTY) {
195			return NATTable.EMPTY;
196		} else {
197			ATObject[] pars = formals.asNativeTable().elements_;
198			int numMandatory;
199			for (numMandatory = 0; numMandatory < pars.length; numMandatory++) {
200				if (!pars[numMandatory].isSymbol()) {
201					break;
202				}
203			}
204			if (numMandatory > 0) {
205				ATObject[] bindings = new ATObject[numMandatory];
206				for (int i = 0; i < bindings.length; i++) {
207					bindings[i] = pars[i].asSymbol().meta_eval(ctx);
208				}
209				return NATTable.atValue(bindings);
210			} else {
211				return NATTable.EMPTY;
212			}
213		}
214	}
215	
216	/**
217	 * Returns the raw contents of a file in a String (using this JVM's default character encoding)
218	 */
219	public static String loadContentOfFile(File file) throws IOException {
220		InputStream is = new FileInputStream(file);
221		
222	    // Get the size of the file
223	    long length = file.length();
224	
225	    // You cannot create an array using a long type.
226	    // It needs to be an int type.
227	    // Before converting to an int type, check
228	    // to ensure that file is not larger than Integer.MAX_VALUE.
229	    if (length > Integer.MAX_VALUE) {
230	        throw new IOException("File is too large: "+file.getName());
231	    }
232	
233	    // Create the byte array to hold the data
234	    byte[] bytes = new byte[(int)length];
235	
236	    // Read in the bytes
237	    int offset = 0;
238	    int numRead = 0;
239	    while (offset < bytes.length
240	           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
241	        offset += numRead;
242	    }
243	
244	    // Ensure all the bytes have been read in
245	    if (offset < bytes.length) {
246	        throw new IOException("Could not completely read file "+file.getName());
247	    }
248	
249	    // Close the input stream and return bytes
250	    is.close();
251	    return new String(bytes);
252	}
253
254	/**
255	 * @return the 'global' lexical scope of an actor, which is a normal native object
256	 * whose lexical parent is OBJLexicalRoot.
257	 */
258	public static NATObject getGlobalLexicalScope() {
259		return (NATObject) _GLOBAL_SCOPE_.get();
260	}
261
262	/**
263	 * @return the lobby namespace of an actor, which is a normal empty object
264	 */
265	public static NATObject getLobbyNamespace() {
266		return (NATObject) _LOBBY_NAMESPACE_.get();
267	}
268	
269	/**
270	 * @return the jlobby root package of an actor, which is a JavaPackage with an empty path prefix.
271	 */
272	public static JavaPackage getJLobbyRoot() {
273		return (JavaPackage) _JLOBBY_ROOT_.get();
274	}
275	
276	/**
277	 * @return the mirror root of an actor, from which intercessive mirrors usually inherit.
278	 */
279	public static NATMirrorRoot getMirrorRoot() {
280		return (NATMirrorRoot) _MIRROR_ROOT_.get();
281	}
282	
283	/**
284	 * @return the nil object of an actor, to which all objects owned by that actor eventually delegate.
285	 */
286	public static NATNil getNil() {
287		return (NATNil) _NIL_.get();
288	}
289	
290	/**
291	 * Restores the global lexical scope to a fresh empty object.
292	 * Resets the lobby global namespace.
293	 */
294	public static void resetEnvironment() {
295		_GLOBAL_SCOPE_.set(createGlobalLexicalScope());
296		_LOBBY_NAMESPACE_.set(createLobbyNamespace());
297		_JLOBBY_ROOT_.set(createJLobbyRoot());
298		_MIRROR_ROOT_.set(createMirrorRoot());
299	}
300	
301	/**
302	 * A global scope has the sentinel instance as its lexical parent.
303	 */
304	private static NATObject createGlobalLexicalScope() {
305		NATObject root = new NATObject(OBJLexicalRoot._INSTANCE_);
306		return root;
307	}
308	
309    /**
310     * A lobby namespace is a simple empty object
311     */
312	private static NATObject createLobbyNamespace() {
313		return new NATObject();
314	}
315
316    /**
317     * A jlobby root package is a JavaPackage with an empty path prefix
318     */
319	private static NATObject createJLobbyRoot() {
320		return new JavaPackage("");
321	}
322
323    /**
324     * The default mirror root, with an empty base-level object
325     */
326	private static NATMirrorRoot createMirrorRoot() {
327		return new NATMirrorRoot();
328	}
329	
330    /**
331     * The default nil object.
332     */
333	private static NATNil createNil() {
334		return new NATNil();
335	}
336	
337	public static final String valueNameOf(Class c) {
338		String name = getSimpleName(c);
339		if (name.startsWith("AT")) {
340			return "a" + classnameToValuename(name, "AT");
341		} else if (name.startsWith("NAT")) {
342			return "a native" + classnameToValuename(name, "NAT");
343		} else if (name.startsWith("AG")) {
344			return "a native AST" + classnameToValuename(name, "AG");
345		} else if (name.startsWith("X")) {
346			return "a native exception" + classnameToValuename(name, "X");
347		} else if (name.startsWith("OBJ")) {
348			return "the native object" + classnameToValuename(name, "OBJ");
349		} else {
350			return name;
351		}
352	}
353	
354	public static final String toString(ATObject obj) {
355		try {
356			return obj.meta_print().javaValue;
357		} catch(InterpreterException e) {
358			return "<unprintable: " + e.getMessage() + ">";
359		}
360	}
361	
362    private static final REProgram _UPPERCASE_ = Regexp.compile("[A-Z]");
363	
364	private static final String classnameToValuename(String classname, String prefix) {
365		// first, get rid of the given prefix by replacing it with ""
366		String classnameWithoutPrefix = new RE(prefix).subst(classname, "",RE.REPLACE_FIRSTONLY);
367		 // next, replace all uppercased letters "L" by " l"		 
368        try {
369        	return Regexp.replaceAll(new RE(_UPPERCASE_), classnameWithoutPrefix, new Regexp.StringCallable() {
370            	public String call(String uppercaseLetter) {
371            		return " " + Character.toLowerCase(uppercaseLetter.charAt(0));
372            	}
373            });
374        } catch (InterpreterException e) { // all this just to make the compiler happy
375        	Logging.VirtualMachine_LOG.fatal("Unexpected exception: " + e.getMessage(), e);
376        	throw new RuntimeException("Unexpected exception: " + e.getMessage());
377        }
378	}
379	
380	// UTILITY METHODS
381	
382	/**
383	 * Returns the unqualified name of a class.
384	 */
385	public static final String getSimpleName(Class c) {
386		String nam = c.getName();
387		return nam.substring(nam.lastIndexOf(".") + 1);
388	}
389	
390	public static final Exception asJavaException(ATObject atObj) throws InterpreterException {
391		if (atObj instanceof NATException) {
392			return ((NATException)atObj).getWrappedException();
393		}
394		
395		if (atObj.isJavaObjectUnderSymbiosis()) {
396			JavaObject jObject = atObj.asJavaObjectUnderSymbiosis();
397			
398			Object object = jObject.getWrappedObject();
399			if (object instanceof Exception) {
400				return (Exception) object;				
401			}
402		}
403		
404		return new XAmbienttalk(atObj);
405	}
406	
407	public static final InterpreterException asNativeException(ATObject atObj) throws InterpreterException {
408		Exception exc = asJavaException(atObj);
409		if (exc instanceof InterpreterException) {
410			return (InterpreterException) exc;
411		} else {
412			return new XJavaException(exc);
413		}
414	}
415}