Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class IncludeReference extends Reference {
* @param identifier - The include identifier (the path passed to include(), which should resolve to a file).
* @param inScope - The parent scope which the include should be linked to.
* @param outScope - The scope in which the include should be usable.
* @param t - The target of the declaration.
* @param t - The target of the reference.
*/
public IncludeReference(String identifier, Scope inScope, Scope outScope, Target t) {
super(Namespace.INCLUDE, identifier, t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public enum Namespace {
IVARIABLE,
IVARIABLE_ASSIGN,
PROCEDURE,
INCLUDE
INCLUDE,
RETURNABLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.laytonsmith.core.compiler.analysis;

import com.laytonsmith.core.NodeModifiers;
import com.laytonsmith.core.constructs.CClassType;
import com.laytonsmith.core.constructs.Target;

/**
* Represents a returnable declaration in a scope graph.
* @author P.J.S. Kools
*/
public class ReturnableDeclaration extends Declaration {

/**
* Creates a new {@link ReturnableDeclaration} in the {@link Namespace#RETURNABLE} namespace.
* @param type The expected return {@link CClassType}.
* @param modifiers The node modifiers.
* @param t The returnable target.
*/
public ReturnableDeclaration(CClassType type, NodeModifiers modifiers, Target t) {
super(Namespace.RETURNABLE, null, type, modifiers, t);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.laytonsmith.core.compiler.analysis;

import com.laytonsmith.core.constructs.Target;

/**
* Represents a reference to a returnable in a scope graph.
* @author P.J.S. Kools
*/
public class ReturnableReference extends Reference {

/**
* Creates a new {@link ReturnableReference} in the {@link Namespace#RETURNABLE} scope.
* @param t - The target of the reference.
*/
public ReturnableReference(Target t) {
super(Namespace.RETURNABLE, null, t);
}
}
58 changes: 58 additions & 0 deletions src/main/java/com/laytonsmith/core/functions/ControlFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import com.laytonsmith.core.compiler.FileOptions;
import com.laytonsmith.core.compiler.SelfStatement;
import com.laytonsmith.core.compiler.VariableScope;
import com.laytonsmith.core.compiler.analysis.Declaration;
import com.laytonsmith.core.compiler.analysis.Namespace;
import com.laytonsmith.core.compiler.analysis.ReturnableReference;
import com.laytonsmith.core.compiler.analysis.Scope;
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
import com.laytonsmith.core.compiler.signature.FunctionSignatures;
Expand Down Expand Up @@ -2411,11 +2414,66 @@ public String docs() {
+ " see the docs on try/catch, particularly the finally clause for example).";
}

@Override
public CClassType typecheck(StaticAnalysis analysis,
ParseTree ast, Environment env, Set<ConfigCompileException> exceptions) {

// Get value type.
CClassType valType;
Target valTarget;
if(ast.numberOfChildren() == 0) {
valType = CVoid.TYPE;
valTarget = ast.getTarget();
} else if(ast.numberOfChildren() == 1) {
ParseTree valNode = ast.getChildAt(0);
valType = analysis.typecheck(valNode, env, exceptions);
valTarget = valNode.getTarget();
} else {

// Fall back to default behavior for invalid usage.
return super.typecheck(analysis, ast, env, exceptions);
}

// Resolve this returnable reference to its returnable declaration to get its required return type.
Scope scope = analysis.getTermScope(ast);
if(scope != null) {
Set<Declaration> decls = scope.getDeclarations(Namespace.RETURNABLE, null);
if(decls.size() == 0) {
exceptions.add(new ConfigCompileException("Return is not valid in this context.", ast.getTarget()));
} else {

// Type check return value for all found declared return types.
for(Declaration decl : decls) {
StaticAnalysis.requireType(valType, decl.getType(), valTarget, env, exceptions);
}
}
}

// Return void.
return CVoid.TYPE;
}

@Override
public Class<? extends CREThrowable>[] thrown() {
return null;
}

@Override
public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast,
Environment env, Set<ConfigCompileException> exceptions) {

// Handle children. These will execute before this return().
Scope scope = super.linkScope(analysis, parentScope, ast, env, exceptions);

// Add returnable reference in new scope.
scope = analysis.createNewScope(scope);
scope.addReference(new ReturnableReference(ast.getTarget()));
analysis.setTermScope(ast, scope);

// Return scope.
return scope;
}

@Override
public boolean isRestricted() {
return false;
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/laytonsmith/core/functions/DataHandling.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.laytonsmith.core.compiler.analysis.ProcDeclaration;
import com.laytonsmith.core.compiler.analysis.ProcRootDeclaration;
import com.laytonsmith.core.compiler.analysis.Reference;
import com.laytonsmith.core.compiler.analysis.ReturnableDeclaration;
import com.laytonsmith.core.compiler.analysis.Scope;
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
import com.laytonsmith.core.compiler.signature.FunctionSignatures;
Expand Down Expand Up @@ -1626,6 +1627,9 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast
// Allow procedures to perform lookups in the decl scope.
paramScope.addSpecificParent(declScope, Namespace.PROCEDURE);

// Create returnable declaration in the inner root scope.
paramScope.addDeclaration(new ReturnableDeclaration(retType, ast.getNodeModifiers(), ast.getTarget()));

// Return the declaration scope. Parameters and their default values are not accessible after the procedure.
return declScope;
}
Expand Down Expand Up @@ -2703,10 +2707,16 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast
return parentScope;
}

// Handle optional return type argument.
// Handle optional return type argument (CClassType or CVoid, default to AUTO).
int ind = 0;
if(ast.getChildAt(ind).getData().isInstanceOf(CClassType.TYPE)) {
analysis.linkScope(parentScope, ast.getChildAt(ind++), env, exceptions);
CClassType retType;
if(ast.getChildAt(ind).getData() instanceof CClassType) {
retType = (CClassType) ast.getChildAt(ind++).getData();
} else if(ast.getChildAt(ind).getData().equals(CVoid.VOID)) {
ind++;
retType = CVoid.TYPE;
} else {
retType = CClassType.AUTO;
}

// Create parameter scope. Set parent scope if this closure type is allowed to resolve in the parent scope.
Expand All @@ -2732,6 +2742,9 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast
paramScope = scopes[0];
}

// Create returnable declaration in the inner root scope.
paramScope.addDeclaration(new ReturnableDeclaration(retType, ast.getNodeModifiers(), ast.getTarget()));

// Handle closure code.
ParseTree code = ast.getChildAt(ast.numberOfChildren() - 1);
analysis.linkScope(paramScope, code, env, exceptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.laytonsmith.core.compiler.SelfStatement;
import com.laytonsmith.core.compiler.VariableScope;
import com.laytonsmith.core.compiler.analysis.Namespace;
import com.laytonsmith.core.compiler.analysis.ReturnableDeclaration;
import com.laytonsmith.core.compiler.analysis.Scope;
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
import com.laytonsmith.core.constructs.CArray;
Expand Down Expand Up @@ -337,6 +338,9 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope,
}
analysis.linkScope(paramScope, code, env, exceptions);

// Create returnable declaration in the inner root scope.
paramScope.addDeclaration(new ReturnableDeclaration(CVoid.TYPE, ast.getNodeModifiers(), ast.getTarget()));

// Allow code after bind() to access declarations in assigned values, but not parameters themselves.
return valScope;
}
Expand Down
Loading