From f462b8198b73ed5adfb0994425cbd575ab5003ca Mon Sep 17 00:00:00 2001 From: Astrash Date: Mon, 24 Jul 2023 18:05:43 +1000 Subject: [PATCH] Move inLoop flag to ScopeBuilder --- .../addons/terrascript/parser/Parser.java | 51 ++++++++++--------- .../addons/terrascript/parser/lang/Scope.java | 13 ++++- 2 files changed, 38 insertions(+), 26 deletions(-) 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 a6714f57f..b9ee608e4 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 @@ -91,19 +91,20 @@ public class Parser { */ public Executable parse() { ScopeBuilder scopeBuilder = new ScopeBuilder(); - return new Executable(parseBlock(new Tokenizer(source), false, scopeBuilder), scopeBuilder); + return new Executable(parseBlock(new Tokenizer(source), scopeBuilder), scopeBuilder); } private WhileKeyword parseWhileLoop(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { SourcePosition start = tokenizer.consume().getPosition(); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); + scopeBuilder = scopeBuilder.subInLoop(); Expression first = parseLogicMathExpression(tokenizer, true, scopeBuilder); ParserUtil.ensureReturnType(first, Expression.ReturnType.BOOLEAN); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); - return new WhileKeyword(parseStatementBlock(tokenizer, true, scopeBuilder), (Expression) first, start); // While loop + return new WhileKeyword(parseStatementBlock(tokenizer, scopeBuilder), (Expression) first, start); // While loop } - private IfKeyword parseIfStatement(Tokenizer tokenizer, boolean controlStructure, ScopeBuilder scopeBuilder) { + private IfKeyword parseIfStatement(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { SourcePosition start = tokenizer.consume().getPosition(); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); Expression condition = parseLogicMathExpression(tokenizer, true, scopeBuilder); @@ -112,7 +113,7 @@ public class Parser { ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); Block elseBlock = null; - Block statement = parseStatementBlock(tokenizer, controlStructure, scopeBuilder); + Block statement = parseStatementBlock(tokenizer, scopeBuilder); List, Block>> elseIf = new ArrayList<>(); @@ -122,9 +123,9 @@ public class Parser { tokenizer.consume(); // Consume if. Expression elseCondition = parseLogicMathExpression(tokenizer, true, scopeBuilder); ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN); - elseIf.add(Pair.of((Expression) elseCondition, parseStatementBlock(tokenizer, controlStructure, scopeBuilder))); + elseIf.add(Pair.of((Expression) elseCondition, parseStatementBlock(tokenizer, scopeBuilder))); } else { - elseBlock = parseStatementBlock(tokenizer, controlStructure, scopeBuilder); + elseBlock = parseStatementBlock(tokenizer, scopeBuilder); break; // Else must be last. } } @@ -132,22 +133,22 @@ public class Parser { return new IfKeyword(statement, (Expression) condition, elseIf, elseBlock, start); // If statement } - private Block parseStatementBlock(Tokenizer tokenizer, boolean controlStructure, ScopeBuilder scopeBuilder) { + private Block parseStatementBlock(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { if(tokenizer.current().isType(Token.Type.BLOCK_BEGIN)) { ParserUtil.ensureType(tokenizer.consume(), Token.Type.BLOCK_BEGIN); - Block block = parseBlock(tokenizer, controlStructure, scopeBuilder); + Block block = parseBlock(tokenizer, scopeBuilder); ParserUtil.ensureType(tokenizer.consume(), Token.Type.BLOCK_END); return block; } else { SourcePosition position = tokenizer.current().getPosition(); - return new Block(Collections.singletonList(parseExpression(tokenizer, controlStructure, scopeBuilder)), position); + return new Block(Collections.singletonList(parseExpression(tokenizer, scopeBuilder)), position); } } private ForKeyword parseForLoop(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { SourcePosition start = tokenizer.consume().getPosition(); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); - scopeBuilder = scopeBuilder.sub(); // new scope + scopeBuilder = scopeBuilder.subInLoop(); // new scope Token f = tokenizer.current(); ParserUtil.ensureType(f, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER); Expression initializer; @@ -171,7 +172,7 @@ public class Parser { ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); - return new ForKeyword(parseStatementBlock(tokenizer, true, scopeBuilder), initializer, (Expression) conditional, incrementer, + return new ForKeyword(parseStatementBlock(tokenizer, scopeBuilder), initializer, (Expression) conditional, incrementer, start); } @@ -194,7 +195,7 @@ public class Parser { if(id.isConstant()) { expression = parseConstantExpression(tokenizer); } else if(id.isType(Token.Type.GROUP_BEGIN)) { // Parse grouped expression - expression = parseGroup(tokenizer, scopeBuilder); + expression = parseMathLogicGroup(tokenizer, scopeBuilder); } else { if(functions.containsKey(id.getContent())) expression = parseFunction(tokenizer, false, scopeBuilder); @@ -240,7 +241,7 @@ public class Parser { }; } - private Expression parseGroup(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { + private Expression parseMathLogicGroup(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); Expression expression = parseLogicMathExpression(tokenizer, true, scopeBuilder); // Parse inside of group as a separate expression ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); @@ -329,36 +330,38 @@ public class Parser { }; } - private Block parseBlock(Tokenizer tokenizer, boolean controlStructure, ScopeBuilder scopeBuilder) { + private Block parseBlock(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { List> expressions = new ArrayList<>(); - scopeBuilder = scopeBuilder.sub(); + SourcePosition startPosition = tokenizer.current().getPosition(); - Token first = tokenizer.current(); - + // 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 = parseExpression(tokenizer, controlStructure, scopeBuilder); + Expression expression = parseExpression(tokenizer, scopeBuilder); if(expression != Function.NULL) { expressions.add(expression); } } - return new Block(expressions, first.getPosition()); + + return new Block(expressions, startPosition); } - private Expression parseExpression(Tokenizer tokenizer, boolean controlStructure, ScopeBuilder scopeBuilder) { + private Expression parseExpression(Tokenizer tokenizer, ScopeBuilder scopeBuilder) { Token token = tokenizer.current(); - if(controlStructure) 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.BREAK, Token.Type.CONTINUE, Token.Type.FAIL); + + // 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.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.FAIL); Expression expression = switch(token.getType()) { case FOR_LOOP -> parseForLoop(tokenizer, scopeBuilder); - case IF_STATEMENT -> parseIfStatement(tokenizer, controlStructure, scopeBuilder); + case IF_STATEMENT -> parseIfStatement(tokenizer, scopeBuilder); case WHILE_LOOP -> parseWhileLoop(tokenizer, scopeBuilder); case IDENTIFIER -> { if(scopeBuilder.contains(token.getContent())) yield parseAssignment(tokenizer, scopeBuilder); // Assume variable assignment 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 9c8e6aa81..c647cc124 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 @@ -50,16 +50,19 @@ public class Scope { private int numSize, boolSize, strSize = 0; private ScopeBuilder parent; + private boolean inLoop; + public ScopeBuilder() { this.indices = new HashMap<>(); } - private ScopeBuilder(ScopeBuilder parent) { + private ScopeBuilder(ScopeBuilder parent, boolean inLoop) { this.parent = parent; this.numSize = parent.numSize; this.boolSize = parent.boolSize; this.strSize = parent.strSize; this.indices = new HashMap<>(parent.indices); + this.inLoop = inLoop; } public Scope build() { @@ -67,15 +70,21 @@ public class Scope { } public ScopeBuilder sub() { - return new ScopeBuilder(this); + return new ScopeBuilder(this, inLoop); } + public ScopeBuilder subInLoop() { return new ScopeBuilder(this, true); } + private String check(String id) { if(indices.containsKey(id)) { throw new IllegalArgumentException("Variable with ID " + id + " already registered."); } return id; } + + public boolean isInLoop() { + return inLoop; + } public int num(String id) { int num = numSize;