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