/* --------------------------------------------------------- * * __________ D E L T A S C R I P T * * (_________() * * / === / - A fast, dynamic scripting language * * | == | - Version 4.13.11.0 * * / === / - Developed by Adam R. Nelson * * | = = | - 2011-2013 * * / === / - Distributed under GNU LGPL v3 * * (________() - http://github.com/ar-nelson/deltascript * * * * --------------------------------------------------------- */ package com.sector91.delta.script.objects; import com.sector91.delta.script.DScriptErr; import com.sector91.delta.script.DeltaScript; import com.sector91.delta.script.annotations.DSDynamicField; import com.sector91.delta.script.annotations.DSInaccessible; import com.sector91.delta.script.annotations.DSName; import com.sector91.delta.script.annotations.DSType; import com.sector91.delta.script.instrs.DSInstr; import com.sector91.delta.script.instrs.VarCountVisitor; import com.sector91.delta.script.objects.reflect.DS_JavaClass; /** * <p>A DeltaScript function (or <i>closure</i>). Functions are defined with * the {@code func} keyword or the {@code ->} operator.</p> * * <p>DeltaScript functions <i>close over</i> the scope in which they were * declared; every function contains a reference to the scope it was declared * in. A function's scope can be reassigned with {@link #bind(DS_Scope)}.</p> * * <p>Similarly to JavaScript, DeltaScript allows functions to be called with * a different number of arguments than were declared, setting missing * arguments to {@code blank} and storing the complete argument list in the * special scope variable {@code $args}.</p> * * @author Adam R. Nelson * @version 4.13.11.0 */ @DSType("Function") public class DS_Function extends DS_AbstractObject implements DS_Object, DS_Callable { public static final String TYPE_NAME = "Function"; private static final DS_JavaClass DSCLASS = DS_JavaClass.fromClass( DS_Function.class); protected final DS_Tag[] argNames; private final String name; private final DSInstr instr; private final int variableCount; private final DS_Scope scope; @DSInaccessible public DS_Function(DSInstr instr, DS_Scope scope) {this(instr, scope, new DS_Tag[0]);} @DSInaccessible public DS_Function(String name, DSInstr instr, DS_Scope scope) {this(name, instr, scope, new DS_Tag[0]);} @DSInaccessible public DS_Function(String name, DSInstr instr, int variableCount, DS_Scope scope) {this(name, instr, scope, variableCount, new DS_Tag[0]);} @DSInaccessible public DS_Function(DSInstr instr, DS_Scope scope, DS_Tag... argNames) {this("func", instr, scope, argNames);} @DSInaccessible public DS_Function(String name, DSInstr instr, DS_Scope scope, DS_Tag... argNames) {this(name, instr, scope, countVars(argNames, instr), argNames);} @DSInaccessible public DS_Function(String name, DSInstr instr, DS_Scope scope, int variableCount, DS_Tag... argNames) { this.name = name; this.instr = instr; this.argNames = argNames; this.scope = scope; this.variableCount = variableCount; } private static int countVars(DS_Tag[] argNames, DSInstr instr) { final VarCountVisitor visitor = new VarCountVisitor(argNames); instr.accept(visitor); return visitor.getVariableCount(); } // API Methods // ---------------------------------------------------- @DSName("name") @DSDynamicField public String getName() {return name;} @DSName("boundScope") @DSDynamicField public DS_Scope getBoundScope() {return scope;} @DSName("bind") public DS_Function bind(DS_Scope scope) {return new DS_Function(getName(), instr, scope, argNames);} // DS_Callable Methods // ---------------------------------------------------- public DS_Object call(DS_Object... args) throws DScriptErr { final DS_Scope functionScope = scope.createSubscope(variableCount); for (int i=0; i<argNames.length; i++) functionScope.setLocal(argNames[i], args.length > i ? args[i] : DS_Blank.BLANK); functionScope.addIdentity(this); return DeltaScript.execAsFunctionCall(instr, functionScope, scope.getContext()); } @DSName("argNames") @DSDynamicField public String[] getArgNames() { String[] strs = new String[argNames.length]; for (int i=0; i<argNames.length; i++) strs[i] = argNames[i].str; return strs; } @DSName("argCount") @DSDynamicField public int getArgCount() {return argNames.length;} // DS_Object Methods // ---------------------------------------------------- public DS_Function unbox() {return this;} @Override public boolean booleanValue() {return true;} public String getTypeName() {return TYPE_NAME;} @Override public String toString() { StringBuilder argStr = new StringBuilder(); String[] argNames = getArgNames(); for (int i=0;i<argNames.length;i++) argStr.append(i==0?argNames[i]:", "+argNames[i]); return standardToString(getName() + "(" + argStr.toString() + ")"); } public boolean equals(DS_Object other) {return other == this;} @Override public DS_Function clone() {return new DS_Function(getName(), instr, scope, argNames);} @Override public boolean is(DS_Object typeObj) { return typeObj == this; // TODO: Figure out how 'is' should work for functions. } @Override protected DS_JavaClass getDeltaScriptClass() {return DSCLASS;} }