PageRenderTime 40ms CodeModel.GetById 11ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 1ms

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