Skip to content
Browse files
Don't parse __autoconcat__ to sconcat in strict mode
Parse `__autoconcat__()` to the new `__statements__()` instead of `sconcat()` in strict mode. `__statements__()` takes arguments of any type and returns `void` for typechecking, so compile errors will be generated in cases where `__autoconcat__()` used to insert `sconcat()`s, or in other words, where the user has either forgotten to put some `.` concat, or where the user has made a mistake.
This change does not affect non-strict mode, as automatically inserting concats is a feature there.
Alias syntax should also remain possible in strict mode, but only when the whole alias is nicely concatenated together by the user. Inserting multiple arguments/statements will cause the code block to be interpreted as a statements block and not as an alias redirect.
  • Loading branch information
Pieter12345 committed Nov 30, 2020
1 parent e36e76d commit 69f6baa
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 8 deletions.
@@ -2119,9 +2119,13 @@ private static void rewriteAutoconcats(ParseTree root, Environment env,
if(root.getData() instanceof CFunction && root.getData().val().equals(__autoconcat__.NAME)) {

// In non-strict mode, let __autoconcat__ glue arguments together with sconcat.
boolean returnSConcat = !root.getFileOptions().isStrict();

try {
ParseTree ret = ((Compiler.__autoconcat__) ((CFunction) root.getData()).getFunction())
.rewrite(root.getChildren(), true, envs);
.rewrite(root.getChildren(), returnSConcat, envs);
} catch (ConfigCompileException ex) {
@@ -45,7 +45,11 @@ private void optimize01(ParseTree tree, CompilerEnvironment compilerEnvironment)
= (com.laytonsmith.core.functions.Compiler.__autoconcat__)
FunctionList.getFunction("__autoconcat__", null, Target.UNKNOWN);
if(tree.getData() instanceof CFunction && tree.getData().val().equals("__autoconcat__")) {
ParseTree tempNode = autoconcat.rewrite(tree.getChildren(), true, null);

// In non-strict mode, let __autoconcat__ glue arguments together with sconcat.
boolean returnSConcat = !tree.getFileOptions().isStrict();

ParseTree tempNode = autoconcat.rewrite(tree.getChildren(), returnSConcat, null);
@@ -152,9 +152,10 @@ public String docs() {
* this __autoconcat__ node being replaced or in a compile error if the __autoconcat__ cannot be converted to
* an executable AST node. This being a function is merely a convenient way to defer processing until after
* parsing, meaning that it should ALWAYS be rewritten before executing the AST.
* @param list
* @param returnSConcat
* @param list - A list containing all {@link ParseTree} children of this __autoconcat__.
* @param returnSConcat - If parsing results in only one child function, then this argument is ignored.
* If {@code true}, the resulting parsed functions will be wrapped into {@link sconcat}.
* If {@code false}, the resulting parsed functions will be wrapped into {@link __statements__}.
* @return The executable AST node, representing the code/tokens in this __autoconcat__.
* @throws ConfigCompileException If this __autoconcat__ cannot be converted to an executable AST node.
@@ -547,14 +548,48 @@ public ParseTree rewrite(List<ParseTree> list, boolean returnSConcat,
if(returnSConcat) {
tree = new ParseTree(new CFunction(sconcat.NAME, t), options);
} else {
tree = new ParseTree(new CFunction(concat.NAME, t), options);
tree = new ParseTree(new CFunction(__statements__.NAME, t), options);
return tree;

@hide("This is only used internally by the compiler.")
public static class __statements__ extends DummyFunction {

public static final String NAME = "__statements__";

public String getName() {
return NAME;

public String docs() {
return "void {[...]} Used internally by the compiler. You shouldn't use it.";

public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntimeException {
return CVoid.VOID;

public CClassType getReturnType(Target t, List<CClassType> argTypes,
List<Target> argTargets, Environment env, Set<ConfigCompileException> exceptions) {
return CVoid.TYPE;

public boolean preResolveVariables() {
return false;

@hide("This is only used for testing unexpected error handling.")
@@ -45,6 +45,7 @@
import com.laytonsmith.core.exceptions.CRE.CRERangeException;
import com.laytonsmith.core.exceptions.CRE.CREThrowable;
import com.laytonsmith.core.functions.BasicLogic.and;
import com.laytonsmith.core.functions.Compiler.__statements__;
import com.laytonsmith.core.functions.Compiler.centry;
import com.laytonsmith.core.functions.DataHandling.assign;
import com.laytonsmith.core.functions.Math.dec;
@@ -647,7 +648,8 @@ public ParseTree postParseRewrite(ParseTree ast, Environment env,
List<ParseTree> children = ast.getChildren();
Target t = ast.getTarget();
if(children.size() > 1 && children.get(1).getData() instanceof CFunction
&& new StringHandling.sconcat().getName().equals(children.get(1).getData().val())) {
&& (sconcat.NAME.equals(children.get(1).getData().val())
|| __statements__.NAME.equals(children.get(1).getData().val()))) {
//This is the brace/case/default usage of switch, probably. We need
//to refactor the data into the old switch format.
List<ParseTree> newChildren = new ArrayList<>();
@@ -745,7 +747,7 @@ public ParseTree postParseRewrite(ParseTree ast, Environment env,
newChildren.add(new ParseTree(conditions, children.get(0).getFileOptions()));
if(lastCodeBlock.size() > 0) {
ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t),
ParseTree codeBlock = new ParseTree(new CFunction(sconcat.NAME, t),
for(ParseTree line : lastCodeBlock) {

0 comments on commit 69f6baa

Please sign in to comment.