From e1e4a635174f2a2b3fc3219a2a9d688452d3e914 Mon Sep 17 00:00:00 2001 From: Astrash Date: Thu, 27 Jul 2023 11:27:15 +1000 Subject: [PATCH] Add basic user defined function support --- .../addons/terrascript/parser/Parser.java | 169 +++++++++++++----- .../addons/terrascript/parser/ParserUtil.java | 13 +- .../addons/terrascript/parser/lang/Block.java | 40 ++--- .../terrascript/parser/lang/Executable.java | 2 +- .../terrascript/parser/lang/Expression.java | 17 ++ .../addons/terrascript/parser/lang/Scope.java | 24 ++- .../parser/lang/functions/Function.java | 18 -- .../lang/functions/FunctionSignature.java | 8 + .../functions/UserDefinedFunctionBuilder.java | 73 ++++++++ .../lang/keywords/flow/BreakKeyword.java | 9 +- .../lang/keywords/flow/ContinueKeyword.java | 9 +- .../lang/keywords/flow/FailKeyword.java | 9 +- .../lang/keywords/flow/ReturnKeyword.java | 25 ++- .../lang/keywords/looplike/ForKeyword.java | 13 +- .../lang/keywords/looplike/IfKeyword.java | 8 +- .../lang/keywords/looplike/WhileKeyword.java | 14 +- .../addons/terrascript/tokenizer/Token.java | 16 +- .../terrascript/tokenizer/Tokenizer.java | 8 +- 18 files changed, 328 insertions(+), 147 deletions(-) create mode 100644 common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionSignature.java create mode 100644 common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/UserDefinedFunctionBuilder.java diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java index 7d092d84d..e8a576bd9 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java @@ -21,8 +21,8 @@ import com.dfsek.terra.addons.terrascript.parser.lang.constants.BooleanConstant; import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression; import com.dfsek.terra.addons.terrascript.parser.lang.constants.NumericConstant; import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant; -import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.UserDefinedFunctionBuilder; import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.BreakKeyword; import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.ContinueKeyword; import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.FailKeyword; @@ -77,17 +77,17 @@ public class Parser { * @throws ParseException If parsing fails. */ public Executable parse(ScopeBuilder scopeBuilder) { - return new Executable(parseBlock(new Tokenizer(source), scopeBuilder), scopeBuilder); + return new Executable(parseBlock(new Tokenizer(source), scopeBuilder, ReturnType.VOID), scopeBuilder); } private WhileKeyword parseWhileLoop(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { SourcePosition start = tokenizer.consume().getPosition(); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); - scopeBuilder = scopeBuilder.subInLoop(); + scopeBuilder = scopeBuilder.innerLoopScope(); Expression condition = parseExpression(tokenizer, true, scopeBuilder); ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); - return new WhileKeyword(parseStatementBlock(tokenizer, scopeBuilder), (Expression) condition, start); // While loop + return new WhileKeyword(parseStatementBlock(tokenizer, scopeBuilder, ReturnType.VOID), (Expression) condition, start); // While loop } private IfKeyword parseIfStatement(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { @@ -99,7 +99,7 @@ public class Parser { ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); Block elseBlock = null; - Block statement = parseStatementBlock(tokenizer, scopeBuilder); + Block statement = parseStatementBlock(tokenizer, scopeBuilder, ReturnType.VOID); List, Block>> elseIf = new ArrayList<>(); @@ -109,9 +109,9 @@ public class Parser { tokenizer.consume(); // Consume if. Expression elseCondition = parseExpression(tokenizer, true, scopeBuilder); ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN); - elseIf.add(Pair.of((Expression) elseCondition, parseStatementBlock(tokenizer, scopeBuilder))); + elseIf.add(Pair.of((Expression) elseCondition, parseStatementBlock(tokenizer, scopeBuilder, ReturnType.VOID))); } else { - elseBlock = parseStatementBlock(tokenizer, scopeBuilder); + elseBlock = parseStatementBlock(tokenizer, scopeBuilder, ReturnType.VOID); break; // Else must be last. } } @@ -119,29 +119,29 @@ public class Parser { return new IfKeyword(statement, (Expression) condition, elseIf, elseBlock, start); // If statement } - private Block parseStatementBlock(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { + private Block parseStatementBlock(Tokenizer tokenizer, ScopeBuilder scopeBuilder, ReturnType blockReturnType) { if(tokenizer.current().isType(Token.Type.BLOCK_BEGIN)) { ParserUtil.ensureType(tokenizer.consume(), Token.Type.BLOCK_BEGIN); - Block block = parseBlock(tokenizer, scopeBuilder); + Block block = parseBlock(tokenizer, scopeBuilder, blockReturnType); ParserUtil.ensureType(tokenizer.consume(), Token.Type.BLOCK_END); return block; } else { SourcePosition position = tokenizer.current().getPosition(); - return new Block(Collections.singletonList(parseStatement(tokenizer, scopeBuilder)), position); + return new Block(Collections.singletonList(parseStatement(tokenizer, scopeBuilder)), position, blockReturnType); } } private ForKeyword parseForLoop(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { SourcePosition start = tokenizer.consume().getPosition(); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); - scopeBuilder = scopeBuilder.subInLoop(); // new scope + scopeBuilder = scopeBuilder.innerLoopScope(); // new scope Token f = tokenizer.current(); - ParserUtil.ensureType(f, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER); + ParserUtil.ensureType(f, Token.Type.TYPE_NUMBER, Token.Type.TYPE_STRING, Token.Type.TYPE_BOOLEAN, Token.Type.IDENTIFIER); Expression initializer; if(f.isVariableDeclaration()) { - VariableAssignmentNode forVar = parseVariableDeclaration(tokenizer, scopeBuilder); + Expression forVar = parseDeclaration(tokenizer, scopeBuilder); Token name = tokenizer.current(); - if(scopeBuilder.containsKey(name.getContent()) || scopeBuilder.contains(name.getContent())) + if(scopeBuilder.containsFunction(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); @@ -158,7 +158,7 @@ public class Parser { ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); - return new ForKeyword(parseStatementBlock(tokenizer, scopeBuilder), initializer, (Expression) conditional, incrementer, + return new ForKeyword(parseStatementBlock(tokenizer, scopeBuilder, ReturnType.VOID), initializer, (Expression) conditional, incrementer, start); } @@ -183,7 +183,7 @@ public class Parser { } else if(id.isType(Token.Type.GROUP_BEGIN)) { // Parse grouped expression expression = parseExpressionGroup(tokenizer, scopeBuilder); } else { - if(scopeBuilder.containsKey(id.getContent())) + if(scopeBuilder.containsFunction(id.getContent())) expression = parseFunctionInvocation(tokenizer, false, scopeBuilder); else if(scopeBuilder.contains(id.getContent())) { ParserUtil.ensureType(tokenizer.consume(), Token.Type.IDENTIFIER); @@ -196,7 +196,7 @@ public class Parser { default -> throw new ParseException("Illegal type for variable reference: " + varType, id.getPosition()); }; - } else throw new ParseException("Unexpected token \" " + id.getContent() + "\"", id.getPosition()); + } else throw new ParseException("Unexpected token \"" + id.getContent() + "\"", id.getPosition()); } if(booleanInverted) { // Invert operation if boolean not detected @@ -292,48 +292,115 @@ public class Parser { } } - private VariableAssignmentNode parseVariableDeclaration(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { + private Expression parseDeclaration(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { Token type = tokenizer.consume(); - ParserUtil.ensureType(type, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.NUMBER_VARIABLE); - Expression.ReturnType returnType = ParserUtil.getVariableReturnType(type); - - ParserUtil.checkVarType(type, returnType); // Check for type mismatch Token identifier = tokenizer.consume(); ParserUtil.ensureType(identifier, Token.Type.IDENTIFIER); - if(scopeBuilder.containsKey(identifier.getContent()) || scopeBuilder.contains(identifier.getContent())) + + Token declarationType = tokenizer.consume(); + ParserUtil.ensureType(declarationType, Token.Type.ASSIGNMENT, Token.Type.GROUP_BEGIN); + + return switch(declarationType.getType()) { + case ASSIGNMENT -> parseVariableDeclaration(tokenizer, scopeBuilder, type, identifier); + case GROUP_BEGIN -> parseFunctionDeclaration(tokenizer, scopeBuilder, type, identifier); + default -> throw new ParseException("Illegal type for declaration: " + type, declarationType.getPosition()); + }; + } + + + private Expression parseVariableDeclaration(Tokenizer tokenizer, ScopeBuilder scopeBuilder, Token type, Token identifier) { + ParserUtil.ensureType(type, Token.Type.TYPE_STRING, Token.Type.TYPE_BOOLEAN, Token.Type.TYPE_NUMBER); + + if(scopeBuilder.contains(identifier.getContent())) throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition()); - ParserUtil.ensureType(tokenizer.consume(), Token.Type.ASSIGNMENT); Expression value = parseExpression(tokenizer, true, scopeBuilder); - ParserUtil.ensureReturnType(value, returnType); - - String id = identifier.getContent(); - + ParserUtil.ensureReturnType(value, ParserUtil.getVariableReturnType(type)); + + String variableName = identifier.getContent(); return switch(value.returnType()) { - case NUMBER -> new NumAssignmentNode((Expression) value, identifier.getPosition(), scopeBuilder.num(id)); - case STRING -> new StrAssignmentNode((Expression) value, identifier.getPosition(), scopeBuilder.str(id)); - case BOOLEAN -> new BoolAssignmentNode((Expression) value, identifier.getPosition(), scopeBuilder.bool(id)); + case NUMBER -> new NumAssignmentNode((Expression) value, identifier.getPosition(), scopeBuilder.declareNum(variableName)); + case STRING -> new StrAssignmentNode((Expression) value, identifier.getPosition(), scopeBuilder.declareStr(variableName)); + case BOOLEAN -> new BoolAssignmentNode((Expression) value, identifier.getPosition(), scopeBuilder.declareBool(variableName)); default -> throw new ParseException("Illegal type for variable declaration: " + type, value.getPosition()); }; } - private Block parseBlock(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { + private Expression parseFunctionDeclaration(Tokenizer tokenizer, ScopeBuilder scopeBuilder, Token type, Token identifier) { + ParserUtil.ensureType(type, Token.Type.TYPE_STRING, Token.Type.TYPE_BOOLEAN, Token.Type.TYPE_NUMBER, Token.Type.TYPE_VOID); + + if(scopeBuilder.contains(identifier.getContent())) + throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition()); + + ReturnType returnType = ParserUtil.getVariableReturnType(type); + + ScopeBuilder functionBodyScope = scopeBuilder.functionScope(); + + // Declare argument names into function body scope + List> argumentInfo = getFunctionArgumentsDeclaration(tokenizer).stream().map(arg -> Pair.of(switch(arg.getRight()) { + case NUMBER -> functionBodyScope.declareNum(arg.getLeft()); + case BOOLEAN -> functionBodyScope.declareBool(arg.getLeft()); + case STRING -> functionBodyScope.declareStr(arg.getLeft()); + default -> throw new IllegalArgumentException("Unsupported argument type: " + arg.getRight()); + }, arg.getRight())).toList(); + + Block body = parseStatementBlock(tokenizer, functionBodyScope, returnType); + + FunctionBuilder functionBuilder = new UserDefinedFunctionBuilder<>(returnType, argumentInfo, body, functionBodyScope); + + scopeBuilder.registerFunction(identifier.getContent(), functionBuilder); + return Expression.NOOP; + } + + private List> getFunctionArgumentsDeclaration(Tokenizer tokenizer) { + List> arguments = new ArrayList<>(); + while (tokenizer.current().getType() != Token.Type.GROUP_END) { + // Parse argument type + Token typeToken = tokenizer.consume(); + ParserUtil.ensureType(typeToken, Token.Type.TYPE_BOOLEAN, Token.Type.TYPE_STRING, Token.Type.TYPE_NUMBER); + ReturnType argType = ParserUtil.getVariableReturnType(typeToken); + + // Parse argument name + Token identifierToken = tokenizer.consume(); + ParserUtil.ensureType(identifierToken, Token.Type.IDENTIFIER); + String argName = identifierToken.getContent(); + + arguments.add(Pair.of(argName, argType)); + + // Consume separator if present, trailing separators are allowed + if (tokenizer.current().isType(Token.Type.SEPARATOR)) tokenizer.consume(); + } + ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); + return arguments; + } + + private Block parseBlock(Tokenizer tokenizer, ScopeBuilder scopeBuilder, ReturnType blockReturnType) { List> expressions = new ArrayList<>(); - scopeBuilder = scopeBuilder.sub(); + scopeBuilder = scopeBuilder.innerScope(); SourcePosition startPosition = tokenizer.current().getPosition(); + boolean hasReturn = false; + // Parse each statement while(tokenizer.hasNext()) { Token token = tokenizer.current(); if(token.isType(Token.Type.BLOCK_END)) break; // Stop parsing at block end. Expression expression = parseStatement(tokenizer, scopeBuilder); - if(expression != Function.NULL) { + if(expression != Expression.NOOP) { expressions.add(expression); } + if(expression instanceof ReturnKeyword returnKeyword) { + hasReturn = true; + if (returnKeyword.dataReturnType() != blockReturnType) + throw new ParseException("Invalid return type, expected " + blockReturnType + ", found " + returnKeyword.dataReturnType(), expression.getPosition()); + } } - return new Block(expressions, startPosition); + if (blockReturnType != ReturnType.VOID && !hasReturn) + throw new ParseException("Block does not contain a return statement, must return type " + blockReturnType, startPosition); + + return new Block(expressions, startPosition, blockReturnType); } private Expression parseStatement(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { @@ -341,10 +408,10 @@ public class Parser { // Include BREAK and CONTINUE as valid token types if scope is within a loop if(scopeBuilder.isInLoop()) ParserUtil.ensureType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP, - Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, + Token.Type.TYPE_NUMBER, Token.Type.TYPE_STRING, Token.Type.TYPE_BOOLEAN, Token.Type.TYPE_VOID, Token.Type.RETURN, Token.Type.BREAK, Token.Type.CONTINUE, Token.Type.FAIL); else ParserUtil.ensureType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP, - Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.RETURN, + Token.Type.TYPE_NUMBER, Token.Type.TYPE_STRING, Token.Type.TYPE_BOOLEAN, Token.Type.TYPE_VOID, Token.Type.RETURN, Token.Type.FAIL); Expression expression = switch(token.getType()) { @@ -355,17 +422,27 @@ public class Parser { if(scopeBuilder.contains(token.getContent())) yield parseAssignment(tokenizer, scopeBuilder); // Assume variable assignment else yield parseFunctionInvocation(tokenizer, true, scopeBuilder); } - case NUMBER_VARIABLE, STRING_VARIABLE, BOOLEAN_VARIABLE -> parseVariableDeclaration(tokenizer, scopeBuilder); - case RETURN -> new ReturnKeyword(tokenizer.consume().getPosition()); + case TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN, TYPE_VOID -> parseDeclaration(tokenizer, scopeBuilder); + case RETURN -> parseReturn(tokenizer, scopeBuilder); case BREAK -> new BreakKeyword(tokenizer.consume().getPosition()); case CONTINUE -> new ContinueKeyword(tokenizer.consume().getPosition()); case FAIL -> new FailKeyword(tokenizer.consume().getPosition()); default -> throw new UnsupportedOperationException("Unexpected token " + token.getType() + ": " + token.getPosition()); }; - if(!token.isControlStructure()) ParserUtil.ensureType(tokenizer.consume(), Token.Type.STATEMENT_END); + if(!token.isControlStructure() && expression != Expression.NOOP) ParserUtil.ensureType(tokenizer.consume(), Token.Type.STATEMENT_END); return expression; } + private ReturnKeyword parseReturn(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { + Token returnToken = tokenizer.consume(); + ParserUtil.ensureType(returnToken, Token.Type.RETURN); + Expression data = null; + if (!tokenizer.current().isType(Token.Type.STATEMENT_END)) { + data = parseExpression(tokenizer, true, scopeBuilder); + } + return new ReturnKeyword(data, returnToken.getPosition()); + } + private VariableAssignmentNode parseAssignment(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { Token identifier = tokenizer.consume(); @@ -389,12 +466,12 @@ public class Parser { }; } - private Function parseFunctionInvocation(Tokenizer tokenizer, boolean fullStatement, ScopeBuilder scopeBuilder) { + private Expression parseFunctionInvocation(Tokenizer tokenizer, boolean fullStatement, ScopeBuilder scopeBuilder) { Token identifier = tokenizer.consume(); ParserUtil.ensureType(identifier, Token.Type.IDENTIFIER); // First token must be identifier - if(!scopeBuilder.containsKey(identifier.getContent())) - throw new ParseException("No such function \"" + identifier.getContent() + "\"", identifier.getPosition()); + if(!scopeBuilder.containsFunction(identifier.getContent())) + throw new ParseException("Function \"" + identifier.getContent() + "\" is not defined in this scope", identifier.getPosition()); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); // Second is body begin @@ -405,11 +482,11 @@ public class Parser { if(fullStatement) ParserUtil.ensureType(tokenizer.current(), Token.Type.STATEMENT_END); if(ignoredFunctions.contains(identifier.getContent())) { - return Function.NULL; + return Expression.NOOP; } - if(scopeBuilder.containsKey(identifier.getContent())) { - FunctionBuilder builder = scopeBuilder.get(identifier.getContent()); + if(scopeBuilder.containsFunction(identifier.getContent())) { + FunctionBuilder builder = scopeBuilder.getFunction(identifier.getContent()); if(builder.argNumber() != -1 && args.size() != builder.argNumber()) throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition()); diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java index 24168d079..03b8ef262 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java @@ -78,9 +78,9 @@ public class ParserUtil { } public static void checkVarType(Token token, Expression.ReturnType returnType) { - if(returnType.equals(Expression.ReturnType.STRING) && token.getType().equals(Token.Type.STRING_VARIABLE)) return; - if(returnType.equals(Expression.ReturnType.NUMBER) && token.getType().equals(Token.Type.NUMBER_VARIABLE)) return; - if(returnType.equals(Expression.ReturnType.BOOLEAN) && token.getType().equals(Token.Type.BOOLEAN_VARIABLE)) return; + if(returnType.equals(Expression.ReturnType.STRING) && token.getType().equals(Token.Type.TYPE_STRING)) return; + if(returnType.equals(Expression.ReturnType.NUMBER) && token.getType().equals(Token.Type.TYPE_NUMBER)) return; + if(returnType.equals(Expression.ReturnType.BOOLEAN) && token.getType().equals(Token.Type.TYPE_BOOLEAN)) return; throw new ParseException("Type mismatch, cannot convert from " + returnType + " to " + token.getType(), token.getPosition()); } @@ -98,9 +98,10 @@ public class ParserUtil { public static Expression.ReturnType getVariableReturnType(Token varToken) { return switch(varToken.getType()) { - case NUMBER_VARIABLE -> Expression.ReturnType.NUMBER; - case STRING_VARIABLE -> Expression.ReturnType.STRING; - case BOOLEAN_VARIABLE -> Expression.ReturnType.BOOLEAN; + case TYPE_NUMBER -> Expression.ReturnType.NUMBER; + case TYPE_STRING -> Expression.ReturnType.STRING; + case TYPE_BOOLEAN -> Expression.ReturnType.BOOLEAN; + case TYPE_VOID -> Expression.ReturnType.VOID; default -> throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration", varToken.getPosition()); }; diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java index ea70d5ca0..fae90f49f 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java @@ -9,33 +9,36 @@ package com.dfsek.terra.addons.terrascript.parser.lang; import java.util.List; -import com.dfsek.terra.addons.terrascript.parser.lang.Block.ReturnInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; -public class Block implements Expression> { +public class Block implements Expression> { private final List> items; private final SourcePosition position; + private final ReturnType returnType; - public Block(List> items, SourcePosition position) { + public Block(List> items, SourcePosition position, ReturnType returnType) { this.items = items; this.position = position; + this.returnType = returnType; } @Override public ReturnType returnType() { - return ReturnType.VOID; + return returnType; } @Override - public ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + public EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { for(Expression item : items) { Object result = item.evaluate(implementationArguments, scope); - if(result instanceof ReturnInfo level) { - if(!level.getLevel().equals(ReturnLevel.NONE)) return level; + if(result instanceof EvaluationInfo evalInfo) { + if(!evalInfo.level().equals(EvaluationLevel.NONE)) return evalInfo; } } - return new ReturnInfo<>(ReturnLevel.NONE, null); + return new EvaluationInfo<>(EvaluationLevel.NONE, Expression.NOOP); } @Override @@ -43,7 +46,7 @@ public class Block implements Expression> { return position; } - public enum ReturnLevel { + public enum EvaluationLevel { NONE(false), BREAK(false), CONTINUE(false), @@ -52,7 +55,7 @@ public class Block implements Expression> { private final boolean returnFast; - ReturnLevel(boolean returnFast) { + EvaluationLevel(boolean returnFast) { this.returnFast = returnFast; } @@ -62,21 +65,6 @@ public class Block implements Expression> { } - public static class ReturnInfo { - private final ReturnLevel level; - private final T data; - - public ReturnInfo(ReturnLevel level, T data) { - this.level = level; - this.data = data; - } - - public ReturnLevel getLevel() { - return level; - } - - public T getData() { - return data; - } + public record EvaluationInfo>(EvaluationLevel level, T data) { } } diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Executable.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Executable.java index f4b2077fa..c7831ae61 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Executable.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Executable.java @@ -14,6 +14,6 @@ public class Executable { } public boolean execute(ImplementationArguments arguments) { - return script.evaluate(arguments, scope.get()).getLevel() != Block.ReturnLevel.FAIL; + return script.evaluate(arguments, scope.get()).level() != Block.EvaluationLevel.FAIL; } } diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Expression.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Expression.java index 580c26c17..9e8820a9f 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Expression.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Expression.java @@ -25,6 +25,23 @@ public interface Expression { SourcePosition getPosition(); + Expression NOOP = new Expression<>() { + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } + + @Override + public Void evaluate(ImplementationArguments implementationArguments, Scope scope) { + return null; + } + + @Override + public SourcePosition getPosition() { + return null; + } + }; + enum ReturnType { NUMBER(true), STRING(true), diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Scope.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Scope.java index 671271c82..57126b1bf 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Scope.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Scope.java @@ -1,7 +1,6 @@ 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; @@ -73,26 +72,33 @@ public class Scope { this.inLoop = inLoop; } + private ScopeBuilder(Map>> functions) { + this.functions = new HashMap<>(functions); + this.indices = new HashMap<>(); + } + public Scope build() { return new Scope(numSize, boolSize, strSize); } - public ScopeBuilder sub() { + public ScopeBuilder innerScope() { return new ScopeBuilder(this, inLoop); } - public ScopeBuilder subInLoop() { return new ScopeBuilder(this, true); } + public ScopeBuilder innerLoopScope() { return new ScopeBuilder(this, true); } + + public ScopeBuilder functionScope() { return new ScopeBuilder(functions); } public ScopeBuilder registerFunction(String name, FunctionBuilder> functionBuilder) { functions.put(name, functionBuilder); return this; } - public boolean containsKey(String functionName) { + public boolean containsFunction(String functionName) { return functions.containsKey(functionName); } - public FunctionBuilder get(String functionName) { + public FunctionBuilder getFunction(String functionName) { return functions.get(functionName); } @@ -106,8 +112,8 @@ public class Scope { public boolean isInLoop() { return inLoop; } - - public int num(String id) { + + public int declareNum(String id) { int num = numSize; indices.put(check(id), Pair.of(num, ReturnType.NUMBER)); numSize++; @@ -115,7 +121,7 @@ public class Scope { return num; } - public int str(String id) { + public int declareStr(String id) { int str = strSize; indices.put(check(id), Pair.of(str, ReturnType.STRING)); strSize++; @@ -123,7 +129,7 @@ public class Scope { return str; } - public int bool(String id) { + public int declareBool(String id) { int bool = boolSize; indices.put(check(id), Pair.of(bool, ReturnType.BOOLEAN)); boolSize++; diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java index 2647528f6..d25b31510 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java @@ -10,26 +10,8 @@ package com.dfsek.terra.addons.terrascript.parser.lang.functions; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Expression; import com.dfsek.terra.addons.terrascript.parser.lang.Scope; -import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; - public interface Function extends Expression { - Function NULL = new Function<>() { - @Override - public ReturnType returnType() { - return null; - } - - @Override - public Object evaluate(ImplementationArguments implementationArguments, Scope scope) { - return null; - } - - @Override - public SourcePosition getPosition() { - return null; - } - }; @Override default double applyDouble(ImplementationArguments implementationArguments, Scope scope) { diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionSignature.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionSignature.java new file mode 100644 index 000000000..3a4e8833b --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionSignature.java @@ -0,0 +1,8 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.functions; + +import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType; +import com.dfsek.terra.api.util.generic.pair.Pair; + + +public record FunctionSignature(ReturnType returnType, Pair[] arguments) { +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/UserDefinedFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/UserDefinedFunctionBuilder.java new file mode 100644 index 000000000..061cec7a7 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/UserDefinedFunctionBuilder.java @@ -0,0 +1,73 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.functions; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Expression; +import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Scope; +import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder; +import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; +import com.dfsek.terra.api.util.generic.pair.Pair; + +import java.util.List; + + +public class UserDefinedFunctionBuilder> implements FunctionBuilder { + + private final ReturnType returnType; + private final List> argumentInfo; + private final ScopeBuilder bodyScopeBuilder; + private final Block body; + + public UserDefinedFunctionBuilder(ReturnType returnType, List> argumentInfo, Block body, ScopeBuilder functionBodyScope) { + this.returnType = returnType; + this.bodyScopeBuilder = functionBodyScope; + this.body = body; + this.argumentInfo = argumentInfo; + } + + @Override + public T build(List> argumentList, SourcePosition position) { + //noinspection unchecked + return (T) new Function() { + + private final ThreadLocal threadLocalScope = ThreadLocal.withInitial(bodyScopeBuilder::build); + + @Override + public ReturnType returnType() { + return returnType; + } + + @Override + public Object evaluate(ImplementationArguments implementationArguments, Scope scope) { + Scope bodyScope = threadLocalScope.get(); + // Pass arguments into scope of function body + for (int i = 0; i < argumentList.size(); i++) { + Pair argInfo = argumentInfo.get(i); + Expression argExpression = argumentList.get(i); + switch(argInfo.getRight()) { + case NUMBER -> bodyScope.setNum(argInfo.getLeft(), argExpression.applyDouble(implementationArguments, scope)); + case BOOLEAN -> bodyScope.setBool(argInfo.getLeft(), argExpression.applyBoolean(implementationArguments, scope)); + case STRING -> bodyScope.setStr(argInfo.getLeft(), (String) argExpression.evaluate(implementationArguments, scope)); + } + } + return body.evaluate(implementationArguments, bodyScope).data().evaluate(implementationArguments, scope); + } + + @Override + public SourcePosition getPosition() { + return position; + } + }; + } + + @Override + public int argNumber() { + return argumentInfo.size(); + } + + @Override + public ReturnType getArgument(int position) { + return argumentInfo.get(position).getRight(); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java index 37d5d7e60..355a988df 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java @@ -7,14 +7,15 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; -import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; -public class BreakKeyword implements Keyword> { +public class BreakKeyword implements Keyword> { private final SourcePosition position; public BreakKeyword(SourcePosition position) { @@ -22,8 +23,8 @@ public class BreakKeyword implements Keyword> { } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { - return new Block.ReturnInfo<>(Block.ReturnLevel.BREAK, null); + public EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + return new EvaluationInfo<>(EvaluationLevel.BREAK, null); } @Override diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java index 62d6eaa5a..5ee52af54 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java @@ -7,14 +7,15 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; -import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; -public class ContinueKeyword implements Keyword> { +public class ContinueKeyword implements Keyword> { private final SourcePosition position; public ContinueKeyword(SourcePosition position) { @@ -22,8 +23,8 @@ public class ContinueKeyword implements Keyword> { } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { - return new Block.ReturnInfo<>(Block.ReturnLevel.CONTINUE, null); + public EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + return new EvaluationInfo<>(EvaluationLevel.CONTINUE, null); } @Override diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java index faf3eb58e..94a7c402f 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java @@ -7,14 +7,15 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; -import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; -public class FailKeyword implements Keyword> { +public class FailKeyword implements Keyword> { private final SourcePosition position; public FailKeyword(SourcePosition position) { @@ -22,8 +23,8 @@ public class FailKeyword implements Keyword> { } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { - return new Block.ReturnInfo<>(Block.ReturnLevel.FAIL, null); + public EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + return new EvaluationInfo<>(EvaluationLevel.FAIL, null); } @Override diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java index 147632b3a..05b56858b 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java @@ -7,23 +7,30 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; -import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; +import com.dfsek.terra.addons.terrascript.parser.lang.Expression; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; +import javax.annotation.Nullable; -public class ReturnKeyword implements Keyword> { + +public class ReturnKeyword implements Keyword> { private final SourcePosition position; - public ReturnKeyword(SourcePosition position) { + private final Expression data; + + public ReturnKeyword(@Nullable Expression data, SourcePosition position) { + this.data = data; this.position = position; } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { - return new Block.ReturnInfo<>(Block.ReturnLevel.RETURN, null); + public EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + return new EvaluationInfo<>(EvaluationLevel.RETURN, data); } @Override @@ -35,4 +42,12 @@ public class ReturnKeyword implements Keyword> { public ReturnType returnType() { return ReturnType.VOID; } + + public ReturnType dataReturnType() { + if(data != null) { + return data.returnType(); + } else { + return ReturnType.VOID; + } + } } diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java index a1fe59cb6..baa4cdb2a 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java @@ -8,6 +8,7 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Expression; @@ -15,7 +16,7 @@ import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; -public class ForKeyword implements Keyword> { +public class ForKeyword implements Keyword> { private final Block conditional; private final Expression initializer; private final Expression statement; @@ -31,15 +32,15 @@ public class ForKeyword implements Keyword> { } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + public Block.EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { for(initializer.evaluate(implementationArguments, scope); statement.evaluate(implementationArguments, scope); incrementer.evaluate(implementationArguments, scope)) { - Block.ReturnInfo level = conditional.evaluate(implementationArguments, scope); - if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break; - if(level.getLevel().isReturnFast()) return level; + Block.EvaluationInfo level = conditional.evaluate(implementationArguments, scope); + if(level.level().equals(EvaluationLevel.BREAK)) break; + if(level.level().isReturnFast()) return level; } - return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); + return new Block.EvaluationInfo<>(EvaluationLevel.NONE, null); } @Override diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java index f2c02bc68..b97c9736a 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java @@ -7,6 +7,8 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; + import org.jetbrains.annotations.Nullable; import java.util.List; @@ -20,7 +22,7 @@ import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; import com.dfsek.terra.api.util.generic.pair.Pair; -public class IfKeyword implements Keyword> { +public class IfKeyword implements Keyword> { private final Block conditional; private final Expression statement; private final SourcePosition position; @@ -37,7 +39,7 @@ public class IfKeyword implements Keyword> { } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + public Block.EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { if(statement.evaluate(implementationArguments, scope)) return conditional.evaluate(implementationArguments, scope); else { for(Pair, Block> pair : elseIf) { @@ -47,7 +49,7 @@ public class IfKeyword implements Keyword> { } if(elseBlock != null) return elseBlock.evaluate(implementationArguments, scope); } - return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); + return new Block.EvaluationInfo<>(EvaluationLevel.NONE, null); } @Override diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java index 518e9df12..fcf9f47f9 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java @@ -8,6 +8,8 @@ package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo; +import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Expression; @@ -15,7 +17,7 @@ import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.tokenizer.SourcePosition; -public class WhileKeyword implements Keyword> { +public class WhileKeyword implements Keyword> { private final Block conditional; private final Expression statement; private final SourcePosition position; @@ -27,13 +29,13 @@ public class WhileKeyword implements Keyword> { } @Override - public Block.ReturnInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { + public EvaluationInfo evaluate(ImplementationArguments implementationArguments, Scope scope) { while(statement.evaluate(implementationArguments, scope)) { - Block.ReturnInfo level = conditional.evaluate(implementationArguments, scope); - if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break; - if(level.getLevel().isReturnFast()) return level; + EvaluationInfo level = conditional.evaluate(implementationArguments, scope); + if(level.level().equals(EvaluationLevel.BREAK)) break; + if(level.level().isReturnFast()) return level; } - return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); + return new EvaluationInfo<>(EvaluationLevel.NONE, null); } @Override diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java index b26020ee7..3c841592f 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java @@ -76,9 +76,9 @@ public class Token { } public boolean isVariableDeclaration() { - return type.equals(Type.STRING_VARIABLE) - || type.equals(Type.BOOLEAN_VARIABLE) - || type.equals(Type.NUMBER_VARIABLE); + return type.equals(Type.TYPE_STRING) + || type.equals(Type.TYPE_BOOLEAN) + || type.equals(Type.TYPE_NUMBER); } public boolean isControlStructure() { @@ -192,15 +192,19 @@ public class Token { /** * Numeric variable declaration */ - NUMBER_VARIABLE, + TYPE_NUMBER, /** * String variable declaration */ - STRING_VARIABLE, + TYPE_STRING, /** * Boolean variable declaration */ - BOOLEAN_VARIABLE, + TYPE_BOOLEAN, + /** + * Void type declaration + */ + TYPE_VOID, /** * If statement declaration */ diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java index f93280f95..36c1d6872 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java @@ -182,11 +182,13 @@ public class Tokenizer { return new Token(tokenString, Token.Type.BOOLEAN, reader.getPosition()); if(tokenString.equals("num")) - return new Token(tokenString, Token.Type.NUMBER_VARIABLE, reader.getPosition()); + return new Token(tokenString, Token.Type.TYPE_NUMBER, reader.getPosition()); if(tokenString.equals("str")) - return new Token(tokenString, Token.Type.STRING_VARIABLE, reader.getPosition()); + return new Token(tokenString, Token.Type.TYPE_STRING, reader.getPosition()); if(tokenString.equals("bool")) - return new Token(tokenString, Token.Type.BOOLEAN_VARIABLE, reader.getPosition()); + return new Token(tokenString, Token.Type.TYPE_BOOLEAN, reader.getPosition()); + if(tokenString.equals("void")) + return new Token(tokenString, Token.Type.TYPE_VOID, reader.getPosition()); if(tokenString.equals("if")) return new Token(tokenString, Token.Type.IF_STATEMENT, reader.getPosition());