/interpreter/tags/at_build150307/src/edu/vub/at/eval/PartialBinder.java

http://ambienttalk.googlecode.com/ · Java · 474 lines · 245 code · 52 blank · 177 comment · 59 complexity · 0d48b39c88df5f3afe43152b615b63f4 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. public void bindParamToArg(ATObject scope, ATSymbol param, ATObject arg) throws InterpreterException {
  94. scope.meta_defineField(param, arg);
  95. }
  96. });
  97. }
  98. /**
  99. * Assign all of the formal parameter names in the scope object to the given arguments
  100. * The scope is defined as the lexical scope of the given context.
  101. */
  102. public static final void assignArgsToParams(PartialBinder residual, ATContext context, ATTable arguments) throws InterpreterException {
  103. residual.bind(arguments.asNativeTable().elements_, context, new BindClosure() {
  104. public void bindParamToArg(ATObject scope, ATSymbol param, ATObject arg) throws InterpreterException {
  105. scope.meta_assignVariable(param, arg);
  106. }
  107. });
  108. }
  109. /**
  110. * Performs the partial evaluation of the binding algorithm given the formal parameters.
  111. * @param forFunction the name of the function for which the parameter list is partially evaluated, for debugging purposes.
  112. * @param parameters the formal parameter list
  113. * @return a partial function which, when applied using the {@link PartialBinder#bind(ATTable, ATContext, BindClosure)}
  114. * method binds the formal parameters given here to the actual arguments supplied at function application time.
  115. * @throws XIllegalParameter when the formal parameter list does not adhere to the language format
  116. */
  117. public static PartialBinder calculateResidual(String forFunction, ATTable parameters) throws InterpreterException {
  118. if (parameters == NATTable.EMPTY) {
  119. return makeZeroArity(forFunction);
  120. }
  121. ATObject[] pars = parameters.asNativeTable().elements_;
  122. int numMandatoryArguments = 0;
  123. int numOptionalArguments = 0;
  124. int paridx = 0;
  125. // determine the number of mandatory arguments
  126. for (; paridx < pars.length && pars[paridx].isSymbol(); paridx++) {
  127. numMandatoryArguments++;
  128. }
  129. // determine the number of optional arguments
  130. for (; paridx < pars.length && pars[paridx].isVariableAssignment(); paridx++) {
  131. numOptionalArguments++;
  132. }
  133. boolean hasSplice;
  134. if (paridx != pars.length) {
  135. // not all formal parameters processed yet
  136. // this can only happen when the last parameter is a rest parameter
  137. if (pars[paridx].isSplice()) {
  138. hasSplice = true;
  139. // rest parameter should always be last
  140. if (paridx != pars.length - 1) {
  141. throw new XIllegalParameter(forFunction, "rest parameter " + pars[paridx] + " is not the last parameter");
  142. }
  143. } else {
  144. // optionals followed by mandatory parameter
  145. throw new XIllegalParameter(forFunction, "optional parameters followed by mandatory parameter " + pars[paridx]);
  146. }
  147. } else {
  148. // all parameters processed, there is no rest parameter
  149. hasSplice = false;
  150. }
  151. // decision tree for which partial function to return
  152. if (numMandatoryArguments > 0) {
  153. // mandatory parameters
  154. if (numOptionalArguments > 0) {
  155. // mandatory and optional parameters
  156. if (hasSplice) {
  157. // mandatory, optional and rest parameters
  158. return makeGeneric(forFunction, pars, numMandatoryArguments, numOptionalArguments);
  159. } else {
  160. // mandatory and optional but no rest parameters
  161. return makeMandatoryOptional(forFunction, pars, numMandatoryArguments, numOptionalArguments);
  162. }
  163. } else {
  164. // mandatory and no optional parameters
  165. if (hasSplice) {
  166. // mandatory and rest parameters, but no optional parameters
  167. return makeMandatoryVariable(forFunction, pars);
  168. } else {
  169. // mandatory parameters, but no optional or rest parameters
  170. return makeMandatory(forFunction, pars);
  171. }
  172. }
  173. } else {
  174. // no mandatory parameters
  175. if (numOptionalArguments > 0) {
  176. // no mandatory parameters but some optional parameters
  177. if (hasSplice) {
  178. // no mandatory, some optional and a rest parameter
  179. return makeOptionalVariable(forFunction, pars);
  180. } else {
  181. // optional parameters only
  182. return makeOptional(forFunction, pars);
  183. }
  184. } else {
  185. // no mandatory and no optional parameters
  186. if (hasSplice) {
  187. // only a rest parameter
  188. return makeVariableArity(forFunction, pars[paridx].asSplice().base_getExpression().asSymbol());
  189. } else {
  190. // no mandatory, no optional and no rest parameter: this can normally only happen when
  191. // the formal parameter list is empty, but this case is checked at the beginning
  192. // if we arrive here, this can only signify an illegal type of parameter
  193. throw new XIllegalParameter(forFunction, "unexpected formal parameter types in " + parameters);
  194. }
  195. }
  196. }
  197. }
  198. /* ============================
  199. * = The 8 residual functions =
  200. * ============================ */
  201. /**
  202. * - ZeroArity (0 0 0) example: f()
  203. */
  204. private static final PartialBinder makeZeroArity(final String funnam) {
  205. return new PartialBinder() {
  206. protected void bind(ATObject[] arguments, ATContext inContext, BindClosure binder) throws InterpreterException {
  207. if (arguments == NATTable.EMPTY.elements_)
  208. return; // no need to bind any arguments
  209. else
  210. throw new XArityMismatch(funnam, 0, arguments.length);
  211. }
  212. };
  213. }
  214. /**
  215. * - Mandatory (n 0 0) example: f(a,b)
  216. */
  217. private static final PartialBinder makeMandatory(final String funnam, final ATObject[] formals) {
  218. return new PartialBinder() {
  219. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  220. int numMandatoryArguments = formals.length;
  221. // perform arity check: number of arguments must match number of parameters exactly
  222. if (numMandatoryArguments != args.length) {
  223. // error: too many or not enough actuals
  224. throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
  225. }
  226. ATObject scope = inContext.base_getLexicalScope();
  227. // bind all mandatory arguments
  228. for (int paridx = 0; paridx < numMandatoryArguments; paridx++) {
  229. // bind formal to actual
  230. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  231. }
  232. }
  233. };
  234. }
  235. /**
  236. * - MandatoryOptional (n m 0) example: f(a,b,c:=1)
  237. */
  238. private static final PartialBinder makeMandatoryOptional(final String funnam, final ATObject[] formals,
  239. final int numMandatory, final int numOptional) {
  240. return new PartialBinder() {
  241. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  242. // perform arity check: number of arguments must at least equal number of mandatory arguments
  243. // and must not be greater than the total number of mandatory and optional arguments
  244. if (args.length < numMandatory || args.length > numMandatory + numOptional) {
  245. // error: not enough actuals or too many actuals
  246. throw new XArityMismatch(funnam, numMandatory, args.length);
  247. }
  248. int paridx = 0;
  249. ATObject scope = inContext.base_getLexicalScope();
  250. // bind all mandatory arguments
  251. for (; paridx < numMandatory; paridx++) {
  252. // bind formal to actual
  253. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  254. }
  255. // bind all optional arguments
  256. for (; paridx < numMandatory + numOptional; paridx++) {
  257. if (paridx < args.length) {
  258. // bind formal to actual and ignore default initialization expression
  259. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_getName(), args[paridx]);
  260. } else {
  261. // no more actuals: bind optional parameter to default initialization expression
  262. ATAssignVariable param = formals[paridx].asVariableAssignment();
  263. binder.bindParamToArg(scope, param.base_getName(), param.base_getValueExpression().meta_eval(inContext));
  264. }
  265. }
  266. }
  267. };
  268. }
  269. /**
  270. * - Optional (0 m 0) example: f(a:=1,b:=2)
  271. */
  272. private static final PartialBinder makeOptional(final String funnam, final ATObject[] formals) {
  273. return new PartialBinder() {
  274. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  275. int numOptional = formals.length;
  276. // perform arity check: number of arguments must not exceed number of optional arguments
  277. if (args.length > numOptional) {
  278. // error: too many actuals
  279. throw new XArityMismatch(funnam, numOptional, args.length);
  280. }
  281. ATObject scope = inContext.base_getLexicalScope();
  282. // bind all optional arguments
  283. for (int paridx = 0; paridx < numOptional; paridx++) {
  284. if (paridx < args.length) {
  285. // bind formal to actual and ignore default initialization expression
  286. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_getName(), args[paridx]);
  287. } else {
  288. // no more actuals: bind optional parameter to default initialization expression
  289. ATAssignVariable param = formals[paridx].asVariableAssignment();
  290. binder.bindParamToArg(scope, param.base_getName(), param.base_getValueExpression().meta_eval(inContext));
  291. }
  292. }
  293. }
  294. };
  295. }
  296. /**
  297. * - VariableArity (0 0 1) example: f(@rest)
  298. */
  299. private static final PartialBinder makeVariableArity(final String funnam, final ATSymbol formal) {
  300. return new PartialBinder() {
  301. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  302. // no arity check needed
  303. // bind the formal parameter to all given arguments
  304. binder.bindParamToArg(inContext.base_getLexicalScope(), formal, NATTable.atValue(args));
  305. }
  306. };
  307. }
  308. /**
  309. * - MandatoryVariable (n 0 1) example: f(a,b,@rest)
  310. */
  311. private static final PartialBinder makeMandatoryVariable(final String funnam, final ATObject[] formals) {
  312. return new PartialBinder() {
  313. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  314. int numMandatoryArguments = formals.length - 1;
  315. // perform arity check: number of arguments must be at least the number of mandatory arguments
  316. if (args.length < numMandatoryArguments) {
  317. // error: not enough actuals
  318. throw new XArityMismatch(funnam, numMandatoryArguments, args.length);
  319. }
  320. ATObject scope = inContext.base_getLexicalScope();
  321. // bind all mandatory arguments
  322. for (int paridx = 0; paridx < numMandatoryArguments; paridx++) {
  323. // bind formal to actual
  324. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  325. }
  326. // bind remaining arguments to the rest parameter
  327. int numRemainingArgs = args.length - numMandatoryArguments;
  328. ATObject[] restArgs = new ATObject[numRemainingArgs];
  329. for (int i = 0; i < numRemainingArgs; i++) {
  330. restArgs[i] = args[numMandatoryArguments + i];
  331. }
  332. ATSymbol restArgsName = formals[numMandatoryArguments].asSplice().base_getExpression().asSymbol();
  333. binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
  334. }
  335. };
  336. }
  337. /**
  338. * - OptionalVariable (0 m 1) example: f(a:=1,@rest)
  339. */
  340. private static final PartialBinder makeOptionalVariable(final String funnam, final ATObject[] formals) {
  341. return new PartialBinder() {
  342. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  343. int numOptional = formals.length - 1;
  344. // no arity check needed
  345. ATObject scope = inContext.base_getLexicalScope();
  346. // bind all optional arguments
  347. for (int paridx = 0; paridx < numOptional; paridx++) {
  348. if (paridx < args.length) {
  349. // bind formal to actual and ignore default initialization expression
  350. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_getName(), args[paridx]);
  351. } else {
  352. // no more actuals: bind optional parameter to default initialization expression
  353. ATAssignVariable param = formals[paridx].asVariableAssignment();
  354. binder.bindParamToArg(scope, param.base_getName(), param.base_getValueExpression().meta_eval(inContext));
  355. }
  356. }
  357. // bind remaining arguments to the rest parameter
  358. ATSymbol restArgsName = formals[numOptional+1].asSplice().base_getExpression().asSymbol();
  359. if (args.length <= numOptional) {
  360. // no more actual arguments to bind to the rest parameter
  361. binder.bindParamToArg(scope, restArgsName, NATTable.EMPTY);
  362. } else {
  363. int numRemainingArgs = args.length - numOptional;
  364. ATObject[] restArgs = new ATObject[numRemainingArgs];
  365. for (int i = 0; i < numRemainingArgs; i++) {
  366. restArgs[i] = args[numOptional + i];
  367. }
  368. binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
  369. }
  370. }
  371. };
  372. }
  373. /**
  374. * - Generic (n m 1) example: f(a,b:=1,@rest)
  375. */
  376. private static final PartialBinder makeGeneric(final String funnam, final ATObject[] formals,
  377. final int numMandatory, final int numOptional) {
  378. return new PartialBinder() {
  379. protected void bind(ATObject[] args, ATContext inContext, BindClosure binder) throws InterpreterException {
  380. // perform arity check: number of arguments must at least equal number of mandatory arguments
  381. if (args.length < numMandatory) {
  382. // error: not enough actuals
  383. throw new XArityMismatch(funnam, numMandatory, args.length);
  384. }
  385. int paridx = 0;
  386. ATObject scope = inContext.base_getLexicalScope();
  387. // bind all mandatory arguments
  388. for (; paridx < numMandatory; paridx++) {
  389. // bind formal to actual
  390. binder.bindParamToArg(scope, formals[paridx].asSymbol(), args[paridx]);
  391. }
  392. // bind all optional arguments
  393. // count the number of actuals used for filling in optional parameters
  394. int numFilledInOptionals = 0;
  395. for (; paridx < numMandatory + numOptional; paridx++) {
  396. if (paridx < args.length) {
  397. // bind formal to actual and ignore default initialization expression
  398. binder.bindParamToArg(scope, formals[paridx].asVariableAssignment().base_getName(), args[paridx]);
  399. numFilledInOptionals++;
  400. } else {
  401. // no more actuals: bind optional parameter to default initialization expression
  402. ATAssignVariable param = formals[paridx].asVariableAssignment();
  403. binder.bindParamToArg(scope, param.base_getName(), param.base_getValueExpression().meta_eval(inContext));
  404. }
  405. }
  406. // bind remaining arguments to the rest parameter
  407. ATSymbol restArgsName = formals[formals.length-1].asSplice().base_getExpression().asSymbol();
  408. if (args.length <= numMandatory + numOptional) {
  409. // no more actual arguments to bind to the rest parameter
  410. binder.bindParamToArg(scope, restArgsName, NATTable.EMPTY);
  411. } else {
  412. int numRemainingArgs = args.length - (numMandatory + numOptional);
  413. ATObject[] restArgs = new ATObject[numRemainingArgs];
  414. for (int i = 0; i < numRemainingArgs; i++) {
  415. restArgs[i] = args[(numMandatory + numOptional) + i];
  416. }
  417. binder.bindParamToArg(scope, restArgsName, NATTable.atValue(restArgs));
  418. }
  419. }
  420. };
  421. }
  422. }