Skip to content
Permalink
Browse files

Support executing closures in variables using function syntax.

This is an early implementation that makes `@closure(@Arg1, @ARG2)` equivalent to `execute(@Arg1, @ARG2, @closure)`. Also fixes LangServ incorrectly checking procedures as functions.
  • Loading branch information...
PseudoKnight committed Sep 1, 2019
1 parent 2cbffbd commit 7e489298dbafdabba9757b21904b7962ab938129
@@ -1824,8 +1824,8 @@ private static void checkBreaks0(ParseTree tree, long currentLoops, String lastU
//Don't care about these
return;
}
if(tree.getData().val().startsWith("_")) {
//It's a proc. We need to recurse, but not check this "function"
if(!((CFunction) tree.getData()).hasFunction()) {
//We need to recurse, but this is not expected to be a function
for(ParseTree child : tree.getChildren()) {
checkBreaks0(child, currentLoops, lastUnbreakable, compilerErrors);
}
@@ -2094,7 +2094,7 @@ private static void link(ParseTree tree, Set<ConfigCompileException> compilerErr
// Walk the children
for(ParseTree child : tree.getChildren()) {
if(child.getData() instanceof CFunction) {
if(child.getData().val().charAt(0) != '_' || child.getData().val().charAt(1) == '_') {
if(((CFunction) child.getData()).hasFunction()) {
// This will throw an exception if the function doesn't exist.
try {
FunctionList.getFunction((CFunction) child.getData(), envs);
@@ -2430,7 +2430,7 @@ private static boolean eliminateDeadCode(ParseTree tree, Environment env, Set<Cl
//For the time being, we will simply say that if a function uses execs, it
//is a branch (branches always use execs, though using execs doesn't strictly
//mean you are a branch type function).
if(tree.getData() instanceof CFunction) {
if(tree.getData() instanceof CFunction && ((CFunction) tree.getData()).hasFunction()) {
Function f;
try {
f = (Function) FunctionList.getFunction(((CFunction) tree.getData()), envs);
@@ -2471,6 +2471,9 @@ private static boolean eliminateDeadCode(ParseTree tree, Environment env, Set<Cl
}
ParseTree child = children.get(m);
if(child.getData() instanceof CFunction) {
if(!((CFunction) child.getData()).hasFunction()) {
continue;
}
Function c;
try {
c = (Function) FunctionList.getFunction(((CFunction) child.getData()), envs);
@@ -26,6 +26,7 @@
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.environments.InvalidEnvironmentException;
import com.laytonsmith.core.exceptions.CRE.AbstractCREException;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREInsufficientPermissionException;
import com.laytonsmith.core.exceptions.CRE.CREInvalidProcedureException;
import com.laytonsmith.core.exceptions.CRE.CREStackOverflowError;
@@ -280,6 +281,14 @@ public Mixed eval(ParseTree c, final Environment env) throws CancelCommandExcept
}
}

final CFunction possibleFunction;
try {
possibleFunction = (CFunction) m;
} catch (ClassCastException e) {
throw ConfigRuntimeException.CreateUncatchableException("Expected to find CFunction at runtime but found: "
+ m.val(), m.getTarget());
}

StackTraceManager stManager = env.getEnv(GlobalEnv.class).GetStackTraceManager();
boolean addedRootStackElement = false;
try {
@@ -290,7 +299,8 @@ public Mixed eval(ParseTree c, final Environment env) throws CancelCommandExcept
}
stManager.setCurrentTarget(c.getTarget());
env.getEnv(GlobalEnv.class).SetScript(this);
if(m.val().charAt(0) == '_' && m.val().charAt(1) != '_') {

if(possibleFunction.hasProcedure()) {
//Not really a function, so we can't put it in Function.
Procedure p = getProc(m.val());
if(p == null) {
@@ -307,13 +317,27 @@ public Mixed eval(ParseTree c, final Environment env) throws CancelCommandExcept
pp.stop();
}
return ret;
} else if(possibleFunction.hasIVariable()) {
//Check if this ivar is a closure and execute it
Mixed closure = env.getEnv(GlobalEnv.class).GetVarList().get(m.val(), m.getTarget(), env).ival();
if(!closure.isInstanceOf(CClosure.TYPE)) {
throw new CRECastException("Expecting variable to contain a closure to execute, but found type: "
+ closure.typeof().getSimpleName(), m.getTarget());
}
Mixed[] list = new Mixed[c.numberOfChildren()];
for(int i = 0; i < c.numberOfChildren(); i++) {
list[i] = env.getEnv(GlobalEnv.class).GetScript().seval(c.getChildAt(i), env);
}
return ((CClosure) closure).executeCallable(list);
}

final Function f;
try {
f = ((CFunction) m).getFunction();
} catch (ConfigCompileException | ClassCastException e) {
f = possibleFunction.getFunction();
} catch (ConfigCompileException e) {
//Turn it into a config runtime exception. This shouldn't ever happen though.
throw ConfigRuntimeException.CreateUncatchableException("Unable to find function " + m.val(), m.getTarget());
throw ConfigRuntimeException.CreateUncatchableException("Unable to find function at runtime: "
+ m.val(), m.getTarget());
}

ArrayList<Mixed> args = new ArrayList<>();
@@ -37,6 +37,33 @@ public boolean isDynamic() {
return true;
}

/**
* Returns true if this CFunction is expected to represent a procedure based on the format.
*
* @return
*/
public boolean hasProcedure() {
return val().charAt(0) == '_' && val().charAt(1) != '_';
}

/**
* Returns true if this CFunction is expected to represent an IVariable based on the format.
*
* @return
*/
public boolean hasIVariable() {
return val().charAt(0) == '@';
}

/**
* Returns true if this CFunction is expected to represent a function based on the format.
*
* @return
*/
public boolean hasFunction() {
return !hasProcedure() && !hasIVariable();
}

/**
* Returns the underlying function for this construct.
*
@@ -845,7 +845,7 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
}
List<DocumentLink> links = new ArrayList<>();
tree.getAllNodes().forEach(node -> {
if(node.getData() instanceof CFunction) {
if(node.getData() instanceof CFunction && ((CFunction) (node.getData())).hasFunction()) {
try {
Function f = ((CFunction) (node.getData())).getFunction();
if(f instanceof DocumentLinkProvider) {
@@ -196,7 +196,7 @@ public void testClone() throws Exception {
CArray c1 = C.Array(C.Void(), C.Void()).clone();
CBoolean c2 = C.Boolean(true).clone();
CDouble c4 = C.Double(1).clone();
CFunction c5 = new CFunction("", Target.UNKNOWN).clone();
CFunction c5 = new CFunction("__", Target.UNKNOWN).clone();
CInt c6 = C.Int(1).clone();
CNull c7 = C.Null().clone();
CString c8 = C.String("").clone();

0 comments on commit 7e48929

Please sign in to comment.
You can’t perform that action at this time.