/interpreter/tags/at2-build270707/src/edu/vub/at/eval/PartialBinder.java

http://ambienttalk.googlecode.com/ · Java · 495 lines · 255 code · 62 blank · 178 comment · 59 complexity · 53e10bf7cc58f5f7fafb99cb2a500afc MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * PartialBinder.java created on 26-dec-2006 at 17:25:12
  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.XArityMismatch;
  31. import edu.vub.at.exceptions.XIllegalParameter;
  32. import edu.vub.at.objects.ATContext;
  33. import edu.vub.at.objects.ATObject;
  34. import edu.vub.at.objects.ATTable;
  35. import edu.vub.at.objects.grammar.ATAssignVariable;
  36. import edu.vub.at.objects.grammar.ATSymbol;
  37. import edu.vub.at.objects.natives.NATTable;
  38. import java.io.Serializable;
  39. /**
  40. * Instances of the class PartialBinder represent 'partial functions' whose task it is
  41. * to bind the formal parameters of a function to the actual arguments upon function application.
  42. *
  43. * The binding process of formals to actuals is an ideal candidate for partial evaluation, because
  44. * the binding algorithm depends mainly on two parameters: the formal parameter list (which is known
  45. * at function definition time, and does not change) and the actual argument list (provided at
  46. * call time). At function definition time, one may partially evaluate the binding algorithm
  47. * according to the form of the formal parameter list. The resulting 'residual function' is an
  48. * instance of this class and can subsequently be used to bind (define or assign) actual arguments
  49. * at runtime, saving out a number of checks which would have otherwise been performed at every
  50. * function invocation.
  51. *
  52. * The general form of a function's formal parameter list is:
  53. * (mandatory arguments: ATSymbol)* (optional arguments: ATAssignVariable)* (rest argument: ATSplice)?
  54. *
  55. * Given such a formal parameter list, we define 8 different kinds of partial functions, each
  56. * specialized for the absence of one of the three different kinds of parameters: (the numbers behind
  57. * the partial function's name denote the number of mandatory, optional and rest args)
  58. *
  59. * - ZeroArity (0 0 0) example: f()
  60. * - Mandatory (n 0 0) example: f(a,b)
  61. * - MandatoryOptional (n m 0) example: f(a,b,c:=1)
  62. * - Optional (0 m 0) example: f(a:=1,b:=2)
  63. * - VariableArity (0 0 1) example: f(@rest)
  64. * - MandatoryVariable (n 0 1) example: f(a,b,@rest)
  65. * - OptionalVariable (0 m 1) example: f(a:=1,@rest)
  66. * - Generic (n m 1) example: f(a,b:=1,@rest)
  67. *
  68. * Note also that the partial evaluation of the binding algorithm at function definition time
  69. * allows the signalling of illegal parameter lists (e.g. when optional arguments are followed
  70. * by mandatory arguments) early, rather than latently detecting such illegal parameter lists
  71. * at method invocation time.
  72. *
  73. * @author tvcutsem
  74. */
  75. public abstract class PartialBinder implements Serializable {
  76. /**
  77. * Bind the given actual arguments to the formal parameters encapsulated by this partial bind function.
  78. * @param arguments the actual arguments to the function, supplied at function application time
  79. * @param inContext the context in which to bind the formal parameters and in which to evaluate default optional parameter expressions
  80. * @param binder a closure which determines whether to define or assign the formals in the scope
  81. */
  82. protected abstract void bind(ATObject[] arguments, ATContext inContext, BindClosure binder) throws InterpreterException;
  83. // auxiliary interface to support functor objects
  84. private interface BindClosure {
  85. public void bindParamToArg(ATObject inScope, ATSymbol param, ATObject arg) throws InterpreterException;
  86. }
  87. /**
  88. * Bind all of the given parameters as newly defined slots in the given scope to the given arguments.
  89. * The scope is defined as the lexical scope of the given context.
  90. */
  91. public static final void defineParamsForArgs(PartialBinder residual, ATContext context, ATTable arguments) throws InterpreterException {
  92. residual.bind(arguments.asNativeTable().elements_, context, new BindClosure() {
  93. private static final long serialVersionUID = -2475956316807558569L;
  94. public void bindParamToArg(ATObject scope, ATSymbol param, ATObject arg) throws InterpreterException {
  95. scope.meta_defineField(param, arg);
  96. }
  97. });
  98. }
  99. /**
  100. * Assign all of the formal parameter names in the scope object to the given arguments
  101. * The scope is defined as the lexical scope of the given context.
  102. */
  103. public static final void assignArgsToParams(PartialBinder residual, ATContext context, ATTable arguments) throws InterpreterException {
  104. residual.bind(arguments.asNativeTable().elements_, context, new BindClosure() {
  105. private static final long serialVersionUID = -247595631680755870L;
  106. public void bindParamToArg(ATObject scope, ATSymbol param, ATObject arg) throws InterpreterException {
  107. scope.impl_call(param.asAssignmentSymbol(), NATTable.of(arg));
  108. // scope.meta_assignVariable(param, arg);
  109. }
  110. });
  111. }
  112. /**
  113. * Performs the partial evaluation of the binding algorithm given the formal parameters.
  114. * @param forFunction the name of the function for which the parameter list is partially evaluated, for debugging purposes.
  115. * @param parameters the formal parameter list
  116. * @return a partial function which, when applied using the {@link PartialBinder#bind(ATTable, ATContext, BindClosure)}
  117. * method binds the formal parameters given here to the actual arguments supplied at function application time.
  118. * @throws XIllegalParameter when the formal parameter list does not adhere to the language format
  119. */
  120. public static PartialBinder calculateResidual(String forFunction, ATTable parameters) throws InterpreterException {
  121. if (parameters == NATTable.EMPTY) {
  122. return makeZeroArity(forFunction);
  123. }
  124. ATObject[] pars = parameters.asNativeTable().elements_;
  125. int numMandatoryArguments = 0;
  126. int numOptionalArguments = 0;
  127. int paridx = 0;
  128. // determine the number of mandatory arguments
  129. for (; paridx < pars.length && pars[paridx].isSymbol(); paridx++) {
  130. numMandatoryArguments++;
  131. }
  132. // determine the number of optional arguments
  133. for (; paridx < pars.length && pars[paridx].isVariableAssignment(); paridx++) {
  134. numOptionalArguments++;
  135. }
  136. boolean hasSplice;
  137. if (paridx != pars.length) {
  138. // not all formal parameters processed yet
  139. // this can only happen when the last parameter is a rest parameter
  140. if (pars[paridx].isSplice()) {
  141. hasSplice = true;
  142. // rest parameter should always be last
  143. if (paridx != pars.length - 1) {
  144. throw new XIllegalParameter(forFunction, "rest parameter " + pars[paridx] + " is not the last parameter");
  145. }
  146. } else {
  147. // optionals followed by mandatory parameter
  148. throw new XIllegalParameter(forFunction, "optional parameters followed by mandatory parameter " + pars[paridx]);
  149. }
  150. } else {
  151. // all parameters processed, there is no rest parameter
  152. hasSplice = false;
  153. }
  154. // decision tree for which partial function to return
  155. if (numMandatoryArguments > 0) {
  156. // mandatory parameters
  157. if (numOptionalArguments > 0) {
  158. // mandatory and optional parameters
  159. if (hasSplice) {
  160. // mandatory, optional and rest parameters
  161. return makeGeneric(forFunction, pars, numMandatoryArguments, numOptionalArguments);
  162. } else {
  163. // mandatory and optional but no rest parameters
  164. return makeMandatoryOptional(forFunction, pars, numMandatoryArguments, numOptionalArguments);
  165. }
  166. } else {
  167. // mandatory and no optional parameters
  168. if (hasSplice) {
  169. // mandatory and rest parameters, but no optional parameters
  170. return makeMandatoryVariable(forFunction, pars);
  171. } else {
  172. // mandatory parameters, but no optional or rest parameters
  173. return makeMandatory(forFunction, pars);
  174. }
  175. }
  176. } else {
  177. // no mandatory parameters
  178. if (numOptionalArguments > 0) {
  179. // no mandatory parameters but some optional parameters
  180. if (hasSplice) {
  181. // no mandatory, some optional and a rest parameter
  182. return makeOptionalVariable(forFunction, pars);
  183. } else {
  184. // optional parameters only
  185. return makeOptional(forFunction, pars);
  186. }
  187. } else {
  188. // no mandatory and no optional parameters
  189. if (hasSplice) {
  190. // only a rest parameter
  191. return makeVariableArity(forFunction, pars[paridx].asSplice().base_expression().asSymbol());
  192. } else {
  193. // no mandatory, no optional and no rest parameter: this can normally only happen when
  194. // the formal parameter list is empty, but this case is checked at the beginning
  195. // if we arrive here, this can only signify an illegal type of parameter
  196. throw new XIllegalParameter(forFunction, "unexpected formal parameter types in " + parameters);
  197. }
  198. }
  199. }
  200. }
  201. /* ============================
  202. * = The 8 residual functions =
  203. * ============================ */
  204. /**
  205. * - ZeroArity (0 0 0) example: f()
  206. */
  207. private static final PartialBinder makeZeroArity(final String funnam) {
  208. return new PartialBinder() {
  209. private static final long serialVersionUID = -2475956316807558574L;
  210. protected void bind(ATObject[] arguments, ATContext inContext, BindClosure binder) throws InterpreterException {
  211. if (arguments == NATTable.EMPTY.elements_)
  212. return; // no need to bind any arguments
  213. else
  214. throw new XArityMismatch(funnam, 0, arguments.length);
  215. }
  216. };
  217. }
  218. /**
  219. * - Mandatory (n 0 0) example: f(a,b)
  220. */
  221. private static final PartialBinder makeMandatory(final String funnam, final ATObject[] formals) {
  222. return new PartialBinder() {
  223. private static final long serialVersionUID = -2475956316807558583L;
  224. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  225. int numMandatoryArguments = formals.length;
  226. // perform arity check: number of arguments must match number of parameters exactly
  227. if (numMandatoryArguments != args.length) {
  228. // error: too many or not enough actuals
  229. throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
  230. }
  231. ATObject scope = inContext.base_lexicalScope();
  232. // bind all mandatory arguments
  233. for (int paridx = 0; paridx < numMandatoryArguments; paridx++) {
  234. // bind formal to actual
  235. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  236. }
  237. }
  238. };
  239. }
  240. /**
  241. * - MandatoryOptional (n m 0) example: f(a,b,c:=1)
  242. */
  243. private static final PartialBinder makeMandatoryOptional(final String funnam, final ATObject[] formals,
  244. final int numMandatory, final int numOptional) {
  245. return new PartialBinder() {
  246. private static final long serialVersionUID = -2475956316807558593L;
  247. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  248. // perform arity check: number of arguments must at least equal number of mandatory arguments
  249. // and must not be greater than the total number of mandatory and optional arguments
  250. if (args.length < numMandatory || args.length > numMandatory + numOptional) {
  251. // error: not enough actuals or too many actuals
  252. throw new XArityMismatch(funnam, numMandatory, args.length);
  253. }
  254. int paridx = 0;
  255. ATObject scope = inContext.base_lexicalScope();
  256. // bind all mandatory arguments
  257. for (; paridx < numMandatory; paridx++) {
  258. // bind formal to actual
  259. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  260. }
  261. // bind all optional arguments
  262. for (; paridx < numMandatory + numOptional; paridx++) {
  263. if (paridx < args.length) {
  264. // bind formal to actual and ignore default initialization expression
  265. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_name(), args[paridx]);
  266. } else {
  267. // no more actuals: bind optional parameter to default initialization expression
  268. ATAssignVariable param = formals[paridx].asVariableAssignment();
  269. binder.bindParamToArg(scope, param.base_name(), param.base_valueExpression().meta_eval(inContext));
  270. }
  271. }
  272. }
  273. };
  274. }
  275. /**
  276. * - Optional (0 m 0) example: f(a:=1,b:=2)
  277. */
  278. private static final PartialBinder makeOptional(final String funnam, final ATObject[] formals) {
  279. return new PartialBinder() {
  280. private static final long serialVersionUID = -2475956316807558603L;
  281. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  282. int numOptional = formals.length;
  283. // perform arity check: number of arguments must not exceed number of optional arguments
  284. if (args.length > numOptional) {
  285. // error: too many actuals
  286. throw new XArityMismatch(funnam, numOptional, args.length);
  287. }
  288. ATObject scope = inContext.base_lexicalScope();
  289. // bind all optional arguments
  290. for (int paridx = 0; paridx < numOptional; paridx++) {
  291. if (paridx < args.length) {
  292. // bind formal to actual and ignore default initialization expression
  293. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_name(), args[paridx]);
  294. } else {
  295. // no more actuals: bind optional parameter to default initialization expression
  296. ATAssignVariable param = formals[paridx].asVariableAssignment();
  297. binder.bindParamToArg(scope, param.base_name(), param.base_valueExpression().meta_eval(inContext));
  298. }
  299. }
  300. }
  301. };
  302. }
  303. /**
  304. * - VariableArity (0 0 1) example: f(@rest)
  305. */
  306. private static final PartialBinder makeVariableArity(final String funnam, final ATSymbol formal) {
  307. return new PartialBinder() {
  308. private static final long serialVersionUID = -2475956316807558103L;
  309. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  310. // no arity check needed
  311. // bind the formal parameter to all given arguments
  312. binder.bindParamToArg(inContext.base_lexicalScope(), formal, NATTable.atValue(args));
  313. }
  314. };
  315. }
  316. /**
  317. * - MandatoryVariable (n 0 1) example: f(a,b,@rest)
  318. */
  319. private static final PartialBinder makeMandatoryVariable(final String funnam, final ATObject[] formals) {
  320. return new PartialBinder() {
  321. private static final long serialVersionUID = -2475956316807558203L;
  322. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  323. int numMandatoryArguments = formals.length - 1;
  324. // perform arity check: number of arguments must be at least the number of mandatory arguments
  325. if (args.length < numMandatoryArguments) {
  326. // error: not enough actuals
  327. throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
  328. }
  329. ATObject scope = inContext.base_lexicalScope();
  330. // bind all mandatory arguments
  331. for (int paridx = 0; paridx < numMandatoryArguments; paridx++) {
  332. // bind formal to actual
  333. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  334. }
  335. // bind remaining arguments to the rest parameter
  336. int numRemainingArgs = args.length - numMandatoryArguments;
  337. ATObject[] restArgs = new ATObject[numRemainingArgs];
  338. for (int i = 0; i < numRemainingArgs; i++) {
  339. restArgs[i] = args[numMandatoryArguments + i];
  340. }
  341. ATSymbol restArgsName = formals[numMandatoryArguments].asSplice().base_expression().asSymbol();
  342. binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
  343. }
  344. };
  345. }
  346. /**
  347. * - OptionalVariable (0 m 1) example: f(a:=1,@rest)
  348. */
  349. private static final PartialBinder makeOptionalVariable(final String funnam, final ATObject[] formals) {
  350. return new PartialBinder() {
  351. private static final long serialVersionUID = -2475956316807558403L;
  352. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  353. int numOptional = formals.length - 1;
  354. // no arity check needed
  355. ATObject scope = inContext.base_lexicalScope();
  356. // bind all optional arguments
  357. for (int paridx = 0; paridx < numOptional; paridx++) {
  358. if (paridx < args.length) {
  359. // bind formal to actual and ignore default initialization expression
  360. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_name(), args[paridx]);
  361. } else {
  362. // no more actuals: bind optional parameter to default initialization expression
  363. ATAssignVariable param = formals[paridx].asVariableAssignment();
  364. binder.bindParamToArg(scope, param.base_name(), param.base_valueExpression().meta_eval(inContext));
  365. }
  366. }
  367. // bind remaining arguments to the rest parameter
  368. ATSymbol restArgsName = formals[numOptional+1].asSplice().base_expression().asSymbol();
  369. if (args.length <= numOptional) {
  370. // no more actual arguments to bind to the rest parameter
  371. binder.bindParamToArg(scope, restArgsName, NATTable.EMPTY);
  372. } else {
  373. int numRemainingArgs = args.length - numOptional;
  374. ATObject[] restArgs = new ATObject[numRemainingArgs];
  375. for (int i = 0; i < numRemainingArgs; i++) {
  376. restArgs[i] = args[numOptional + i];
  377. }
  378. binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
  379. }
  380. }
  381. };
  382. }
  383. /**
  384. * - Generic (n m 1) example: f(a,b:=1,@rest)
  385. */
  386. private static final PartialBinder makeGeneric(final String funnam, final ATObject[] formals,
  387. final int numMandatory, final int numOptional) {
  388. return new PartialBinder() {
  389. private static final long serialVersionUID = -2475956316807559003L;
  390. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  391. // perform arity check: number of arguments must at least equal number of mandatory arguments
  392. if (args.length < numMandatory) {
  393. // error: not enough actuals
  394. throw new XArityMismatch(funnam, numMandatory, args.length);
  395. }
  396. int paridx = 0;
  397. ATObject scope = inContext.base_lexicalScope();
  398. // bind all mandatory arguments
  399. for (; paridx < numMandatory; paridx++) {
  400. // bind formal to actual
  401. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  402. }
  403. // bind all optional arguments
  404. // count the number of actuals used for filling in optional parameters
  405. int numFilledInOptionals = 0;
  406. for (; paridx < numMandatory + numOptional; paridx++) {
  407. if (paridx < args.length) {
  408. // bind formal to actual and ignore default initialization expression
  409. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_name(), args[paridx]);
  410. numFilledInOptionals++;
  411. } else {
  412. // no more actuals: bind optional parameter to default initialization expression
  413. ATAssignVariable param = formals[paridx].asVariableAssignment();
  414. binder.bindParamToArg(scope, param.base_name(), param.base_valueExpression().meta_eval(inContext));
  415. }
  416. }
  417. // bind remaining arguments to the rest parameter
  418. ATSymbol restArgsName = formals[formals.length-1].asSplice().base_expression().asSymbol();
  419. if (args.length <= numMandatory + numOptional) {
  420. // no more actual arguments to bind to the rest parameter
  421. binder.bindParamToArg(scope, restArgsName, NATTable.EMPTY);
  422. } else {
  423. int numRemainingArgs = args.length - (numMandatory + numOptional);
  424. ATObject[] restArgs = new ATObject[numRemainingArgs];
  425. for (int i = 0; i < numRemainingArgs; i++) {
  426. restArgs[i] = args[(numMandatory + numOptional) + i];
  427. }
  428. binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
  429. }
  430. }
  431. };
  432. }
  433. }