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