Move inLoop flag to ScopeBuilder

This commit is contained in:
Astrash
2023-07-24 18:05:43 +10:00
parent de3b213deb
commit f462b8198b
2 changed files with 38 additions and 26 deletions
@@ -91,19 +91,20 @@ public class Parser {
*/ */
public Executable parse() { public Executable parse() {
ScopeBuilder scopeBuilder = new ScopeBuilder(); 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) { private WhileKeyword parseWhileLoop(Tokenizer tokenizer, ScopeBuilder scopeBuilder) {
SourcePosition start = tokenizer.consume().getPosition(); SourcePosition start = tokenizer.consume().getPosition();
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN);
scopeBuilder = scopeBuilder.subInLoop();
Expression<?> first = parseLogicMathExpression(tokenizer, true, scopeBuilder); Expression<?> first = parseLogicMathExpression(tokenizer, true, scopeBuilder);
ParserUtil.ensureReturnType(first, Expression.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(first, Expression.ReturnType.BOOLEAN);
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END);
return new WhileKeyword(parseStatementBlock(tokenizer, true, scopeBuilder), (Expression<Boolean>) first, start); // While loop return new WhileKeyword(parseStatementBlock(tokenizer, scopeBuilder), (Expression<Boolean>) 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(); SourcePosition start = tokenizer.consume().getPosition();
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN);
Expression<?> condition = parseLogicMathExpression(tokenizer, true, scopeBuilder); Expression<?> condition = parseLogicMathExpression(tokenizer, true, scopeBuilder);
@@ -112,7 +113,7 @@ public class Parser {
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END);
Block elseBlock = null; Block elseBlock = null;
Block statement = parseStatementBlock(tokenizer, controlStructure, scopeBuilder); Block statement = parseStatementBlock(tokenizer, scopeBuilder);
List<Pair<Expression<Boolean>, Block>> elseIf = new ArrayList<>(); List<Pair<Expression<Boolean>, Block>> elseIf = new ArrayList<>();
@@ -122,9 +123,9 @@ public class Parser {
tokenizer.consume(); // Consume if. tokenizer.consume(); // Consume if.
Expression<?> elseCondition = parseLogicMathExpression(tokenizer, true, scopeBuilder); Expression<?> elseCondition = parseLogicMathExpression(tokenizer, true, scopeBuilder);
ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN);
elseIf.add(Pair.of((Expression<Boolean>) elseCondition, parseStatementBlock(tokenizer, controlStructure, scopeBuilder))); elseIf.add(Pair.of((Expression<Boolean>) elseCondition, parseStatementBlock(tokenizer, scopeBuilder)));
} else { } else {
elseBlock = parseStatementBlock(tokenizer, controlStructure, scopeBuilder); elseBlock = parseStatementBlock(tokenizer, scopeBuilder);
break; // Else must be last. break; // Else must be last.
} }
} }
@@ -132,22 +133,22 @@ public class Parser {
return new IfKeyword(statement, (Expression<Boolean>) condition, elseIf, elseBlock, start); // If statement return new IfKeyword(statement, (Expression<Boolean>) 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)) { if(tokenizer.current().isType(Token.Type.BLOCK_BEGIN)) {
ParserUtil.ensureType(tokenizer.consume(), 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); ParserUtil.ensureType(tokenizer.consume(), Token.Type.BLOCK_END);
return block; return block;
} else { } else {
SourcePosition position = tokenizer.current().getPosition(); 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) { private ForKeyword parseForLoop(Tokenizer tokenizer, ScopeBuilder scopeBuilder) {
SourcePosition start = tokenizer.consume().getPosition(); SourcePosition start = tokenizer.consume().getPosition();
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN);
scopeBuilder = scopeBuilder.sub(); // new scope scopeBuilder = scopeBuilder.subInLoop(); // new scope
Token f = tokenizer.current(); 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.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER);
Expression<?> initializer; Expression<?> initializer;
@@ -171,7 +172,7 @@ public class Parser {
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END);
return new ForKeyword(parseStatementBlock(tokenizer, true, scopeBuilder), initializer, (Expression<Boolean>) conditional, incrementer, return new ForKeyword(parseStatementBlock(tokenizer, scopeBuilder), initializer, (Expression<Boolean>) conditional, incrementer,
start); start);
} }
@@ -194,7 +195,7 @@ public class Parser {
if(id.isConstant()) { if(id.isConstant()) {
expression = parseConstantExpression(tokenizer); expression = parseConstantExpression(tokenizer);
} else if(id.isType(Token.Type.GROUP_BEGIN)) { // Parse grouped expression } else if(id.isType(Token.Type.GROUP_BEGIN)) { // Parse grouped expression
expression = parseGroup(tokenizer, scopeBuilder); expression = parseMathLogicGroup(tokenizer, scopeBuilder);
} else { } else {
if(functions.containsKey(id.getContent())) if(functions.containsKey(id.getContent()))
expression = parseFunction(tokenizer, false, scopeBuilder); 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); ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_BEGIN);
Expression<?> expression = parseLogicMathExpression(tokenizer, true, scopeBuilder); // Parse inside of group as a separate expression Expression<?> expression = parseLogicMathExpression(tokenizer, true, scopeBuilder); // Parse inside of group as a separate expression
ParserUtil.ensureType(tokenizer.consume(), Token.Type.GROUP_END); 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<Expression<?>> expressions = new ArrayList<>(); List<Expression<?>> expressions = new ArrayList<>();
scopeBuilder = scopeBuilder.sub(); scopeBuilder = scopeBuilder.sub();
SourcePosition startPosition = tokenizer.current().getPosition();
Token first = tokenizer.current(); // Parse each statement
while(tokenizer.hasNext()) { while(tokenizer.hasNext()) {
Token token = tokenizer.current(); Token token = tokenizer.current();
if(token.isType(Token.Type.BLOCK_END)) break; // Stop parsing at block end. 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) { if(expression != Function.NULL) {
expressions.add(expression); 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(); 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, // Include BREAK and CONTINUE as valid token types if scope is within a loop
Token.Type.RETURN, Token.Type.BREAK, Token.Type.CONTINUE, Token.Type.FAIL); 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, 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.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.RETURN,
Token.Type.FAIL); Token.Type.FAIL);
Expression<?> expression = switch(token.getType()) { Expression<?> expression = switch(token.getType()) {
case FOR_LOOP -> parseForLoop(tokenizer, scopeBuilder); 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 WHILE_LOOP -> parseWhileLoop(tokenizer, scopeBuilder);
case IDENTIFIER -> { case IDENTIFIER -> {
if(scopeBuilder.contains(token.getContent())) yield parseAssignment(tokenizer, scopeBuilder); // Assume variable assignment if(scopeBuilder.contains(token.getContent())) yield parseAssignment(tokenizer, scopeBuilder); // Assume variable assignment
@@ -50,16 +50,19 @@ public class Scope {
private int numSize, boolSize, strSize = 0; private int numSize, boolSize, strSize = 0;
private ScopeBuilder parent; private ScopeBuilder parent;
private boolean inLoop;
public ScopeBuilder() { public ScopeBuilder() {
this.indices = new HashMap<>(); this.indices = new HashMap<>();
} }
private ScopeBuilder(ScopeBuilder parent) { private ScopeBuilder(ScopeBuilder parent, boolean inLoop) {
this.parent = parent; this.parent = parent;
this.numSize = parent.numSize; this.numSize = parent.numSize;
this.boolSize = parent.boolSize; this.boolSize = parent.boolSize;
this.strSize = parent.strSize; this.strSize = parent.strSize;
this.indices = new HashMap<>(parent.indices); this.indices = new HashMap<>(parent.indices);
this.inLoop = inLoop;
} }
public Scope build() { public Scope build() {
@@ -67,9 +70,11 @@ public class Scope {
} }
public ScopeBuilder sub() { 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) { private String check(String id) {
if(indices.containsKey(id)) { if(indices.containsKey(id)) {
throw new IllegalArgumentException("Variable with ID " + id + " already registered."); throw new IllegalArgumentException("Variable with ID " + id + " already registered.");
@@ -77,6 +82,10 @@ public class Scope {
return id; return id;
} }
public boolean isInLoop() {
return inLoop;
}
public int num(String id) { public int num(String id) {
int num = numSize; int num = numSize;
indices.put(check(id), Pair.of(num, ReturnType.NUMBER)); indices.put(check(id), Pair.of(num, ReturnType.NUMBER));