/interpreter/tags/at2dist041108/src/edu/vub/at/eval/PartialBinder.java
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}