PageRenderTime 49ms CodeModel.GetById 18ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

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