/* --------------------------------------------------------- *
* __________ 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;}
}