/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. */
  28. package edu.vub.at.eval;
  29. import java.io.File;
  30. import java.io.FileInputStream;
  31. import java.io.IOException;
  32. import java.io.InputStream;
  33. import java.util.LinkedList;
  34. import org.apache.regexp.RE;
  35. import org.apache.regexp.REProgram;
  36. import edu.vub.at.exceptions.InterpreterException;
  37. import edu.vub.at.exceptions.XAmbienttalk;
  38. import edu.vub.at.objects.ATContext;
  39. import edu.vub.at.objects.ATObject;
  40. import edu.vub.at.objects.ATTable;
  41. import edu.vub.at.objects.mirrors.NATMirrorRoot;
  42. import edu.vub.at.objects.natives.NATException;
  43. import edu.vub.at.objects.natives.NATObject;
  44. import edu.vub.at.objects.natives.NATTable;
  45. import edu.vub.at.objects.natives.NATText;
  46. import edu.vub.at.objects.natives.OBJLexicalRoot;
  47. import edu.vub.at.objects.natives.NATNil;
  48. import edu.vub.at.objects.natives.grammar.AGSplice;
  49. import edu.vub.at.objects.natives.grammar.AGSymbol;
  50. import edu.vub.at.objects.symbiosis.JavaObject;
  51. import edu.vub.at.objects.symbiosis.JavaPackage;
  52. import edu.vub.at.objects.symbiosis.XJavaException;
  53. import edu.vub.at.util.logging.Logging;
  54. import edu.vub.util.Regexp;
  55. /**
  56. * The Evaluator class serves as a repository for auxiliary evaluation methods.
  57. *
  58. * @author tvcutsem
  59. */
  60. public final class Evaluator {
  61. // important symbols
  62. public static final AGSymbol _ANON_MTH_NAM_ = AGSymbol.jAlloc("nativelambda");
  63. public static final NATTable _ANON_MTH_ARGS_ = NATTable.of(new AGSplice(AGSymbol.jAlloc("args")));
  64. public static final AGSymbol _LAMBDA_ = AGSymbol.alloc(NATText.atValue("lambda"));
  65. public static final AGSymbol _APPLY_ = AGSymbol.alloc(NATText.atValue("apply"));
  66. public static final AGSymbol _INIT_ = AGSymbol.alloc(NATText.atValue("init"));
  67. public static final AGSymbol _CURNS_SYM_ = AGSymbol.jAlloc("~");
  68. /**
  69. * A thread-local variable is used to assign a unique global scope to
  70. * each separate actor. Each actor that invokes the getGlobalLexicalScope
  71. * method receives its own separate copy of the global scope
  72. */
  73. private static final ThreadLocal _GLOBAL_SCOPE_ = new ThreadLocal() {
  74. protected synchronized Object initialValue() {
  75. return createGlobalLexicalScope();
  76. }
  77. };
  78. /**
  79. * A thread-local variable is used to assign a unique lobby namespace to
  80. * each separate actor. Each actor that invokes the getLobby()
  81. * method receives its own separate copy of the lobby namespace
  82. */
  83. private static final ThreadLocal _LOBBY_NAMESPACE_ = new ThreadLocal() {
  84. protected synchronized Object initialValue() {
  85. return createLobbyNamespace();
  86. }
  87. };
  88. /**
  89. * A thread-local variable is used to assign a unique jlobby root to
  90. * each separate actor. The jlobby root is the root JavaPackage from
  91. * which other Java packages can be loaded. Each actor that invokes the getJLobbyRoot()
  92. * method receives its own separate copy of the jlobby root
  93. */
  94. private static final ThreadLocal _JLOBBY_ROOT_ = new ThreadLocal() {
  95. protected synchronized Object initialValue() {
  96. return createJLobbyRoot();
  97. }
  98. };
  99. /**
  100. * A thread-local variable is used to assign a unique mirror root to
  101. * each separate actor. The mirror root encapsulates the default semantics
  102. * for AmbientTalk objects and is the parent of most interecessive custom mirrors
  103. * defined by AmbientTalk programmers themselves.
  104. */
  105. private static final ThreadLocal _MIRROR_ROOT_ = new ThreadLocal() {
  106. protected synchronized Object initialValue() {
  107. return createMirrorRoot();
  108. }
  109. };
  110. /**
  111. * A thread-local variable is used to assign a unique nil object to
  112. * each separate actor. This object is the root of the delegation
  113. * chain of all objects owned by that actor.
  114. */
  115. private static final ThreadLocal _NIL_ = new ThreadLocal() {
  116. protected synchronized Object initialValue() {
  117. return createNil();
  118. }
  119. };
  120. /**
  121. * Auxiliary function used to print the elements of a table using various separators.
  122. */
  123. public final static NATText printElements(ATObject[] els,String start, String sep, String stop) throws InterpreterException {
  124. if (els.length == 0)
  125. return NATText.atValue(String.valueOf(start+stop));
  126. StringBuffer buff = new StringBuffer(start);
  127. for (int i = 0; i < els.length - 1; i++) {
  128. buff.append(els[i].meta_print().asNativeText().javaValue + sep);
  129. }
  130. buff.append(els[els.length-1].meta_print().asNativeText().javaValue + stop);
  131. return NATText.atValue(buff.toString());
  132. }
  133. /**
  134. * Auxiliary function used to print the elements of a table using various separators.
  135. */
  136. public final static NATText printElements(NATTable tab,String start, String sep, String stop) throws InterpreterException {
  137. return printElements(tab.elements_, start, sep, stop);
  138. }
  139. public static final NATText printAsStatements(ATTable tab) throws InterpreterException {
  140. return printElements(tab.asNativeTable(), "", "; ", "");
  141. }
  142. public static final NATText printAsList(ATTable tab) throws InterpreterException {
  143. return printElements(tab.asNativeTable(), "(", ", ", ")");
  144. }
  145. /**
  146. * This function is called whenever arguments to a function, message, method need to be evaluated.
  147. * TODO(coercers) currently does not work for user-defined tables
  148. */
  149. public static final NATTable evaluateArguments(NATTable args, ATContext ctx) throws InterpreterException {
  150. if (args == NATTable.EMPTY) return NATTable.EMPTY;
  151. ATObject[] els = args.elements_;
  152. LinkedList result = new LinkedList();
  153. int siz = els.length;
  154. for (int i = 0; i < els.length; i++) {
  155. if (els[i].isSplice()) {
  156. ATObject[] tbl = els[i].asSplice().base_expression().meta_eval(ctx).asNativeTable().elements_;
  157. for (int j = 0; j < tbl.length; j++) {
  158. result.add(tbl[j]);
  159. }
  160. siz += (tbl.length - 1); // -1 because we replace one element by a table of elements
  161. } else {
  162. result.add(els[i].meta_eval(ctx));
  163. }
  164. }
  165. return NATTable.atValue((ATObject[]) result.toArray(new ATObject[siz]));
  166. }
  167. /**
  168. * Given a formal parameter list, this auxiliary method returns a new table
  169. * consisting of the values of the bindings of the mandatory parameters of
  170. * formals within the context ctx.
  171. */
  172. public static NATTable evalMandatoryPars(ATTable formals, ATContext ctx) throws InterpreterException {
  173. if (formals == NATTable.EMPTY) {
  174. return NATTable.EMPTY;
  175. } else {
  176. ATObject[] pars = formals.asNativeTable().elements_;
  177. int numMandatory;
  178. for (numMandatory = 0; numMandatory < pars.length; numMandatory++) {
  179. if (!pars[numMandatory].isSymbol()) {
  180. break;
  181. }
  182. }
  183. if (numMandatory > 0) {
  184. ATObject[] bindings = new ATObject[numMandatory];
  185. for (int i = 0; i < bindings.length; i++) {
  186. bindings[i] = pars[i].asSymbol().meta_eval(ctx);
  187. }
  188. return NATTable.atValue(bindings);
  189. } else {
  190. return NATTable.EMPTY;
  191. }
  192. }
  193. }
  194. /**
  195. * Returns the raw contents of a file in a String (using this JVM's default character encoding)
  196. */
  197. public static String loadContentOfFile(File file) throws IOException {
  198. InputStream is = new FileInputStream(file);
  199. // Get the size of the file
  200. long length = file.length();
  201. // You cannot create an array using a long type.
  202. // It needs to be an int type.
  203. // Before converting to an int type, check
  204. // to ensure that file is not larger than Integer.MAX_VALUE.
  205. if (length > Integer.MAX_VALUE) {
  206. throw new IOException("File is too large: "+file.getName());
  207. }
  208. // Create the byte array to hold the data
  209. byte[] bytes = new byte[(int)length];
  210. // Read in the bytes
  211. int offset = 0;
  212. int numRead = 0;
  213. while (offset < bytes.length
  214. && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
  215. offset += numRead;
  216. }
  217. // Ensure all the bytes have been read in
  218. if (offset < bytes.length) {
  219. throw new IOException("Could not completely read file "+file.getName());
  220. }
  221. // Close the input stream and return bytes
  222. is.close();
  223. return new String(bytes);
  224. }
  225. /**
  226. * @return the 'global' lexical scope of an actor, which is a normal native object
  227. * whose lexical parent is OBJLexicalRoot.
  228. */
  229. public static NATObject getGlobalLexicalScope() {
  230. return (NATObject) _GLOBAL_SCOPE_.get();
  231. }
  232. /**
  233. * @return the lobby namespace of an actor, which is a normal empty object
  234. */
  235. public static NATObject getLobbyNamespace() {
  236. return (NATObject) _LOBBY_NAMESPACE_.get();
  237. }
  238. /**
  239. * @return the jlobby root package of an actor, which is a JavaPackage with an empty path prefix.
  240. */
  241. public static JavaPackage getJLobbyRoot() {
  242. return (JavaPackage) _JLOBBY_ROOT_.get();
  243. }
  244. /**
  245. * @return the mirror root of an actor, from which intercessive mirrors usually inherit.
  246. */
  247. public static NATMirrorRoot getMirrorRoot() {
  248. return (NATMirrorRoot) _MIRROR_ROOT_.get();
  249. }
  250. /**
  251. * @return the nil object of an actor, to which all objects owned by that actor eventually delegate.
  252. */
  253. public static NATNil getNil() {
  254. return (NATNil) _NIL_.get();
  255. }
  256. /**
  257. * Restores the global lexical scope to a fresh empty object.
  258. * Resets the lobby global namespace.
  259. */
  260. public static void resetEnvironment() {
  261. _GLOBAL_SCOPE_.set(createGlobalLexicalScope());
  262. _LOBBY_NAMESPACE_.set(createLobbyNamespace());
  263. _JLOBBY_ROOT_.set(createJLobbyRoot());
  264. _MIRROR_ROOT_.set(createMirrorRoot());
  265. }
  266. /**
  267. * A global scope has the sentinel instance as its lexical parent.
  268. */
  269. private static NATObject createGlobalLexicalScope() {
  270. NATObject root = new NATObject(OBJLexicalRoot._INSTANCE_);
  271. return root;
  272. }
  273. /**
  274. * A lobby namespace is a simple empty object
  275. */
  276. private static NATObject createLobbyNamespace() {
  277. return new NATObject();
  278. }
  279. /**
  280. * A jlobby root package is a JavaPackage with an empty path prefix
  281. */
  282. private static NATObject createJLobbyRoot() {
  283. return new JavaPackage("");
  284. }
  285. /**
  286. * The default mirror root, with an empty base-level object
  287. */
  288. private static NATMirrorRoot createMirrorRoot() {
  289. return new NATMirrorRoot();
  290. }
  291. /**
  292. * The default nil object.
  293. */
  294. private static NATNil createNil() {
  295. return new NATNil();
  296. }
  297. public static final String valueNameOf(Class c) {
  298. String name = getSimpleName(c);
  299. if (name.startsWith("AT")) {
  300. return "a" + classnameToValuename(name, "AT");
  301. } else if (name.startsWith("NAT")) {
  302. return "a native" + classnameToValuename(name, "NAT");
  303. } else if (name.startsWith("AG")) {
  304. return "a native AST" + classnameToValuename(name, "AG");
  305. } else if (name.startsWith("X")) {
  306. return "a native exception" + classnameToValuename(name, "X");
  307. } else if (name.startsWith("OBJ")) {
  308. return "the native object" + classnameToValuename(name, "OBJ");
  309. } else {
  310. return name;
  311. }
  312. }
  313. public static final String toString(ATObject obj) {
  314. try {
  315. return obj.meta_print().javaValue;
  316. } catch(InterpreterException e) {
  317. return "<unprintable: " + e.getMessage() + ">";
  318. }
  319. }
  320. private static final REProgram _UPPERCASE_ = Regexp.compile("[A-Z]");
  321. private static final String classnameToValuename(String classname, String prefix) {
  322. // first, get rid of the given prefix by replacing it with ""
  323. String classnameWithoutPrefix = new RE(prefix).subst(classname, "",RE.REPLACE_FIRSTONLY);
  324. // next, replace all uppercased letters "L" by " l"
  325. try {
  326. return Regexp.replaceAll(new RE(_UPPERCASE_), classnameWithoutPrefix, new Regexp.StringCallable() {
  327. public String call(String uppercaseLetter) {
  328. return " " + Character.toLowerCase(uppercaseLetter.charAt(0));
  329. }
  330. });
  331. } catch (InterpreterException e) { // all this just to make the compiler happy
  332. Logging.VirtualMachine_LOG.fatal("Unexpected exception: " + e.getMessage(), e);
  333. throw new RuntimeException("Unexpected exception: " + e.getMessage());
  334. }
  335. }
  336. // UTILITY METHODS
  337. /**
  338. * Returns the unqualified name of a class.
  339. */
  340. public static final String getSimpleName(Class c) {
  341. String nam = c.getName();
  342. return nam.substring(nam.lastIndexOf(".") + 1);
  343. }
  344. public static final Exception asJavaException(ATObject atObj) throws InterpreterException {
  345. if (atObj instanceof NATException) {
  346. return ((NATException)atObj).getWrappedException();
  347. }
  348. if (atObj.isJavaObjectUnderSymbiosis()) {
  349. JavaObject jObject = atObj.asJavaObjectUnderSymbiosis();
  350. Object object = jObject.getWrappedObject();
  351. if (object instanceof Exception) {
  352. return (Exception) object;
  353. }
  354. }
  355. return new XAmbienttalk(atObj);
  356. }
  357. public static final InterpreterException asNativeException(ATObject atObj) throws InterpreterException {
  358. Exception exc = asJavaException(atObj);
  359. if (exc instanceof InterpreterException) {
  360. return (InterpreterException) exc;
  361. } else {
  362. return new XJavaException(exc);
  363. }
  364. }
  365. }