diff --git a/polymod/hscript/_internal/Parser.hx b/polymod/hscript/_internal/Parser.hx index c3f8c12d..bf717b5d 100644 --- a/polymod/hscript/_internal/Parser.hx +++ b/polymod/hscript/_internal/Parser.hx @@ -570,9 +570,17 @@ class Parser { while (true) { - var id = getIdent(); - var t = maybe(TDoubleDot) ? parseType() : null; - args.push({name: id, t: t}); + var arg:Argument = {name: null}; + if (maybe(TQuestion)) arg.opt = true; + arg.name = getIdent(); + + if (allowTypes) + { + if (maybe(TDoubleDot)) arg.t = parseType(); + if (maybe(TOp("="))) arg.value = parseExpr(); + } + args.push(arg); + var tk = token(); switch (tk) { diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index db9c68e8..5e68e96c 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -664,47 +664,55 @@ class PolymodInterpEx extends Interp { hasOpt = true; } - else + if (!p.opt && p.value == null) { minParams++; } } - // This CREATES a new function in memory, that we call later. - var newFun:Dynamic = function(args:Array) { - if (((args == null) ? 0 : args.length) != params.length) + // This CREATES a new function in memory, that we call later. + var newFun:Dynamic = function(args:Array) + { + if (args == null) args = []; + + if (args.length < minParams) + { + var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams; + if (name != null) + str += " for function '" + name + "'"; + errorEx(ECustom(str)); + } + + // make sure mandatory args are forced + var args2 = []; + var pos = 0; + for (p in params) { - if (args.length < minParams) + if (pos < args.length) { - var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams; - if (name != null) str += " for function '" + name + "'"; - errorEx(ECustom(str)); + var arg = args[pos++]; + if (arg == null && p.value != null) + { + args2.push(expr(p.value)); + } + else + { + args2.push(arg); + } } - // make sure mandatory args are forced - var args2 = []; - var extraParams = args.length - minParams; - var pos = 0; - for (p in params) + else { - if (p.opt) + if (p.value != null) { - if (extraParams > 0) - { - args2.push(args[pos++]); - extraParams--; - } - else - { - args2.push(null); - } + args2.push(expr(p.value)); } else { - args2.push(args[pos++]); + args2.push(null); } } - args = args2; } + args = args2; clone.depth++; @@ -1657,31 +1665,9 @@ public function callScriptClassStaticFunction(clsName:String, fnName:String, arg { // Populate function arguments. - // previousValues is used to restore variables after they are shadowed in the local scope. var previousClassDecl = _classDeclOverride; - var previousValues:Map = []; - var i = 0; - for (a in fn.args) - { - var value:Dynamic = null; - - if (args != null && i < args.length) - { - value = args[i]; - } - else if (a.value != null) - { - value = this.expr(a.value); - } - - // NOTE: We assign these as variables rather than locals because those get wiped when we enter the function. - if (this.variables.exists(a.name)) - { - previousValues.set(a.name, this.variables.get(a.name)); - } - this.variables.set(a.name, value); - i++; - } + // previousValues is used to restore variables after they are shadowed in the local scope. + var previousValues:Map = setFunctionValues(fn, args); this._classDeclOverride = cls; @@ -1732,6 +1718,45 @@ public function callScriptClassStaticFunction(clsName:String, fnName:String, arg } } + /** + * Initializes function arguments within the interpreter scope. + * + * @param fn The function declaration to extract arguments from. + * @param args The arguments to pass to the function. + * @return The Map containing the variable values before they are shadowed in the local scope. + */ + public function setFunctionValues(fn:Null, args:Array = null):Map + { + var previousValues:Map = []; + if (fn == null) return previousValues; + + var i = 0; + for (a in fn.args) + { + var value:Dynamic = null; + + // Uses the passed value if provided and not null, if not fall back to the default value defined in the function argument. + if (args != null && i < args.length && args[i] != null) + { + value = args[i]; + } + else if (a.value != null) + { + value = this.expr(a.value); + } + + // NOTE: We assign these as variables rather than locals because those get wiped when we enter the function. + if (this.variables.exists(a.name)) + { + previousValues.set(a.name, this.variables.get(a.name)); + } + this.variables.set(a.name, value); + i++; + } + + return previousValues; + } + public function hasScriptClassStaticFunction(clsName:String, fnName:String):Bool { var imports:Map = []; diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 95220013..dc455942 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -668,29 +668,7 @@ class PolymodScriptClass if (fn != null) { // previousValues is used to restore variables after they are shadowed in the local scope. - var previousValues:Map = []; - var i = 0; - for (a in fn.args) - { - var value:Dynamic = null; - - if (args != null && i < args.length) - { - value = args[i]; - } - else if (a.value != null) - { - value = _interp.expr(a.value); - } - - // NOTE: We assign these as variables rather than locals because those get wiped when we enter the function. - if (_interp.variables.exists(a.name)) - { - previousValues.set(a.name, _interp.variables.get(a.name)); - } - _interp.variables.set(a.name, value); - i++; - } + var previousValues:Map = _interp.setFunctionValues(fn, args); // Polymod.debug('Calling scripted class function "${fullyQualifiedName}.${fnName}(${args})"', null);