Handle functions in scope

This commit is contained in:
Astrash
2023-07-25 14:08:09 +10:00
parent a184fe40d0
commit 0dc1492c4d
4 changed files with 43 additions and 31 deletions

View File

@@ -9,9 +9,7 @@ package com.dfsek.terra.addons.terrascript.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.parser.lang.Block;
@@ -65,23 +63,12 @@ import com.dfsek.terra.api.util.generic.pair.Pair;
@SuppressWarnings("unchecked")
public class Parser {
private final String source;
private final Map<String, FunctionBuilder<? extends Function<?>>> functions = new HashMap<>();
private final List<String> ignoredFunctions = new ArrayList<>();
public Parser(String source) {
this.source = source;
}
public Parser registerFunction(String name, FunctionBuilder<? extends Function<?>> functionBuilder) {
functions.put(name, functionBuilder);
return this;
}
public Parser ignoreFunction(String name) {
ignoredFunctions.add(name);
return this;
}
/**
* Parse input
*
@@ -89,8 +76,7 @@ public class Parser {
*
* @throws ParseException If parsing fails.
*/
public Executable parse() {
ScopeBuilder scopeBuilder = new ScopeBuilder();
public Executable parse(ScopeBuilder scopeBuilder) {
return new Executable(parseBlock(new Tokenizer(source), scopeBuilder), scopeBuilder);
}
@@ -155,7 +141,7 @@ public class Parser {
if(f.isVariableDeclaration()) {
VariableAssignmentNode<?> forVar = parseVariableDeclaration(tokenizer, scopeBuilder);
Token name = tokenizer.current();
if(functions.containsKey(name.getContent()) || scopeBuilder.contains(name.getContent()))
if(scopeBuilder.containsKey(name.getContent()) || scopeBuilder.contains(name.getContent()))
throw new ParseException(name.getContent() + " is already defined in this scope", name.getPosition());
initializer = forVar;
} else initializer = parseExpression(tokenizer, true, scopeBuilder);
@@ -197,7 +183,7 @@ public class Parser {
} else if(id.isType(Token.Type.GROUP_BEGIN)) { // Parse grouped expression
expression = parseExpressionGroup(tokenizer, scopeBuilder);
} else {
if(functions.containsKey(id.getContent()))
if(scopeBuilder.containsKey(id.getContent()))
expression = parseFunctionInvocation(tokenizer, false, scopeBuilder);
else if(scopeBuilder.contains(id.getContent())) {
ParserUtil.ensureType(tokenizer.consume(), Token.Type.IDENTIFIER);
@@ -315,7 +301,7 @@ public class Parser {
ParserUtil.checkVarType(type, returnType); // Check for type mismatch
Token identifier = tokenizer.consume();
ParserUtil.ensureType(identifier, Token.Type.IDENTIFIER);
if(functions.containsKey(identifier.getContent()) || scopeBuilder.contains(identifier.getContent()))
if(scopeBuilder.containsKey(identifier.getContent()) || scopeBuilder.contains(identifier.getContent()))
throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition());
ParserUtil.ensureType(tokenizer.consume(), Token.Type.ASSIGNMENT);
@@ -407,7 +393,7 @@ public class Parser {
Token identifier = tokenizer.consume();
ParserUtil.ensureType(identifier, Token.Type.IDENTIFIER); // First token must be identifier
if(!functions.containsKey(identifier.getContent()))
if(!scopeBuilder.containsKey(identifier.getContent()))
throw new ParseException("No such function \"" + identifier.getContent() + "\"", identifier.getPosition());
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); // Second is body begin
@@ -422,8 +408,8 @@ public class Parser {
return Function.NULL;
}
if(functions.containsKey(identifier.getContent())) {
FunctionBuilder<?> builder = functions.get(identifier.getContent());
if(scopeBuilder.containsKey(identifier.getContent())) {
FunctionBuilder<?> builder = scopeBuilder.get(identifier.getContent());
if(builder.argNumber() != -1 && args.size() != builder.argNumber())
throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition());

View File

@@ -1,6 +1,10 @@
package com.dfsek.terra.addons.terrascript.parser.lang;
import com.dfsek.terra.addons.terrascript.parser.Parser;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import net.jafama.FastMath;
import java.util.HashMap;
@@ -46,6 +50,8 @@ public class Scope {
}
public static final class ScopeBuilder {
private final Map<String, FunctionBuilder<? extends Function<?>>> functions;
private final Map<String, Pair<Integer, ReturnType>> indices;
private int numSize, boolSize, strSize = 0;
private ScopeBuilder parent;
@@ -53,6 +59,7 @@ public class Scope {
private boolean inLoop;
public ScopeBuilder() {
this.functions = new HashMap<>();
this.indices = new HashMap<>();
}
@@ -61,6 +68,7 @@ public class Scope {
this.numSize = parent.numSize;
this.boolSize = parent.boolSize;
this.strSize = parent.strSize;
this.functions = new HashMap<>(parent.functions);
this.indices = new HashMap<>(parent.indices);
this.inLoop = inLoop;
}
@@ -75,6 +83,19 @@ public class Scope {
public ScopeBuilder subInLoop() { return new ScopeBuilder(this, true); }
public ScopeBuilder registerFunction(String name, FunctionBuilder<? extends Function<?>> functionBuilder) {
functions.put(name, functionBuilder);
return this;
}
public boolean containsKey(String functionName) {
return functions.containsKey(functionName);
}
public FunctionBuilder<?> get(String functionName) {
return functions.get(functionName);
}
private String check(String id) {
if(indices.containsKey(id)) {
throw new IllegalArgumentException("Variable with ID " + id + " already registered.");

View File

@@ -7,6 +7,10 @@
package com.dfsek.terra.addons.terrascript.script;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
import net.jafama.FastMath;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
@@ -70,10 +74,12 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
this.id = id;
this.profile = "terrascript_direct:" + id;
//noinspection unchecked
functionRegistry.forEach((key, function) -> parser.registerFunction(key.getID(), function)); // Register registry functions.
ScopeBuilder scope = new ScopeBuilder();
parser
//noinspection unchecked
functionRegistry.forEach((key, function) -> scope.registerFunction(key.getID(), function)); // Register registry functions.
scope
.registerFunction("block", new BlockFunctionBuilder(platform))
.registerFunction("debugBlock", new BlockFunctionBuilder(platform))
.registerFunction("structure", new StructureFunctionBuilder(structureRegistry, platform))
@@ -120,11 +126,7 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
.registerFunction("min", new BinaryNumberFunctionBuilder(
(number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue())));
if(!platform.getTerraConfig().isDebugScript()) {
parser.ignoreFunction("debugBlock");
}
executable = parser.parse();
executable = parser.parse(scope);
this.platform = platform;
}

View File

@@ -8,6 +8,8 @@
package structure;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
@@ -33,7 +35,8 @@ public class ParserTest {
Parser parser = new Parser(
IOUtils.toString(Objects.requireNonNull(getClass().getResourceAsStream("/test.tesf")), Charset.defaultCharset()));
parser.registerFunction("test", new FunctionBuilder<Test1>() {
ScopeBuilder scope = new ScopeBuilder();
scope.registerFunction("test", new FunctionBuilder<Test1>() {
@Override
public Test1 build(List<Expression<?>> argumentList, SourcePosition position) {
return new Test1(argumentList.get(0), argumentList.get(1), position);
@@ -56,7 +59,7 @@ public class ParserTest {
});
long l = System.nanoTime();
Executable block = parser.parse();
Executable block = parser.parse(scope);
long t = System.nanoTime() - l;
System.out.println("Took " + (double) t / 1000000);