Add for loops

This commit is contained in:
dfsek
2020-12-25 00:12:28 -07:00
parent aaa0c99524
commit abc59901c1
9 changed files with 128 additions and 21 deletions

View File

@@ -17,9 +17,10 @@ import com.dfsek.terra.api.structures.parser.lang.functions.builtin.SqrtFunction
import com.dfsek.terra.api.structures.parser.lang.keywords.BreakKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.ContinueKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.FailKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.IfKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.ReturnKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.WhileKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.looplike.ForKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.looplike.IfKeyword;
import com.dfsek.terra.api.structures.parser.lang.keywords.looplike.WhileKeyword;
import com.dfsek.terra.api.structures.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.api.structures.parser.lang.operations.BooleanAndOperation;
import com.dfsek.terra.api.structures.parser.lang.operations.BooleanNotOperation;
@@ -113,21 +114,52 @@ public class Parser {
private Keyword<?> parseLoopLike(List<Token> tokens, Map<String, Variable<?>> variableMap) throws ParseException {
Token identifier = tokens.remove(0);
ParserUtil.checkType(identifier, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP);
ParserUtil.checkType(identifier, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP);
ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_BEGIN);
Returnable<?> comparator = parseExpression(tokens, true, variableMap);
ParserUtil.checkReturnType(comparator, Returnable.ReturnType.BOOLEAN);
if(identifier.getType().equals(Token.Type.FOR_LOOP)) {
Token f = tokens.get(0);
ParserUtil.checkType(f, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER);
Item<?> initializer;
if(f.isVariableDeclaration()) {
Variable<?> forVar = parseVariableDeclaration(tokens, ParserUtil.getVariableReturnType(f));
Token name = tokens.get(1);
ParserUtil.checkType(tokens.remove(0), Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.NUMBER_VARIABLE);
initializer = parseAssignment(forVar, tokens, variableMap);
variableMap.put(name.getContent(), forVar);
} else initializer = parseExpression(tokens, true, variableMap);
ParserUtil.checkType(tokens.remove(0), Token.Type.STATEMENT_END);
Returnable<?> conditional = parseExpression(tokens, true, variableMap);
ParserUtil.checkReturnType(conditional, Returnable.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.remove(0), Token.Type.STATEMENT_END);
Item<?> incrementer;
Token token = tokens.get(0);
if(variableMap.containsKey(token.getContent())) { // Assume variable assignment
Variable<?> variable = variableMap.get(token.getContent());
incrementer = parseAssignment(variable, tokens, variableMap);
} else incrementer = parseFunction(tokens, true, variableMap);
ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_END);
ParserUtil.checkType(tokens.remove(0), Token.Type.BLOCK_BEGIN);
return new ForKeyword(parseBlock(tokens, variableMap), initializer, (Returnable<Boolean>) conditional, incrementer, identifier.getPosition());
}
Returnable<?> first = parseExpression(tokens, true, variableMap);
ParserUtil.checkReturnType(first, Returnable.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_END);
ParserUtil.checkType(tokens.remove(0), Token.Type.BLOCK_BEGIN);
if(identifier.getType().equals(Token.Type.IF_STATEMENT))
return new IfKeyword(parseBlock(tokens, variableMap), (Returnable<Boolean>) comparator, identifier.getPosition()); // If statement
return new IfKeyword(parseBlock(tokens, variableMap), (Returnable<Boolean>) first, identifier.getPosition()); // If statement
else if(identifier.getType().equals(Token.Type.WHILE_LOOP))
return new WhileKeyword(parseBlock(tokens, variableMap), (Returnable<Boolean>) comparator, identifier.getPosition()); // While loop
return new WhileKeyword(parseBlock(tokens, variableMap), (Returnable<Boolean>) first, identifier.getPosition()); // While loop
else throw new UnsupportedOperationException("Unknown keyword " + identifier.getContent() + ": " + identifier.getPosition());
}
@@ -269,7 +301,8 @@ public class Parser {
Token token = tokens.get(0);
if(token.getType().equals(Token.Type.BLOCK_END)) break; // Stop parsing at block end.
ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_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);
ParserUtil.checkType(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);
if(token.isLoopLike()) { // Parse loop-like tokens (if, while, etc)
parsedItems.add(parseLoopLike(tokens, parsedVariables));
@@ -279,14 +312,8 @@ public class Parser {
parsedItems.add(parseAssignment(variable, tokens, parsedVariables));
} else parsedItems.add(parseFunction(tokens, true, parsedVariables));
} else if(token.isVariableDeclaration()) {
Variable<?> temp;
if(token.getType().equals(Token.Type.NUMBER_VARIABLE))
temp = parseVariableDeclaration(tokens, Returnable.ReturnType.NUMBER);
else if(token.getType().equals(Token.Type.STRING_VARIABLE))
temp = parseVariableDeclaration(tokens, Returnable.ReturnType.STRING);
else temp = parseVariableDeclaration(tokens, Returnable.ReturnType.BOOLEAN);
Variable<?> temp = parseVariableDeclaration(tokens, ParserUtil.getVariableReturnType(token));
Token name = tokens.get(1);
ParserUtil.checkType(name, Token.Type.IDENTIFIER); // Name must be an identifier.
if(functions.containsKey(name.getContent()) || parsedVariables.containsKey(name.getContent()) || builtinFunctions.contains(name.getContent()))

View File

@@ -46,4 +46,17 @@ public class ParserUtil {
if(!token.isBinaryOperator())
throw new ParseException("Expected binary operator, found " + token.getType() + ": " + token.getPosition());
}
public static Returnable.ReturnType getVariableReturnType(Token varToken) throws ParseException {
switch(varToken.getType()) {
case NUMBER_VARIABLE:
return Returnable.ReturnType.NUMBER;
case STRING_VARIABLE:
return Returnable.ReturnType.STRING;
case BOOLEAN_VARIABLE:
return Returnable.ReturnType.BOOLEAN;
default:
throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration: " + varToken.getPosition());
}
}
}

View File

@@ -37,6 +37,16 @@ public class Block implements Item<Block.ReturnLevel> {
}
public enum ReturnLevel {
NONE, BREAK, CONTINUE, RETURN, FAIL
NONE(false), BREAK(false), CONTINUE(false), RETURN(true), FAIL(true);
private final boolean returnFast;
ReturnLevel(boolean returnFast) {
this.returnFast = returnFast;
}
public boolean isReturnFast() {
return returnFast;
}
}
}

View File

@@ -0,0 +1,45 @@
package com.dfsek.terra.api.structures.parser.lang.keywords.looplike;
import com.dfsek.terra.api.structures.parser.lang.Block;
import com.dfsek.terra.api.structures.parser.lang.Item;
import com.dfsek.terra.api.structures.parser.lang.Keyword;
import com.dfsek.terra.api.structures.parser.lang.Returnable;
import com.dfsek.terra.api.structures.structure.Rotation;
import com.dfsek.terra.api.structures.structure.buffer.Buffer;
import com.dfsek.terra.api.structures.tokenizer.Position;
public class ForKeyword implements Keyword<Block.ReturnLevel> {
private final Block conditional;
private final Item<?> initializer;
private final Returnable<Boolean> statement;
private final Item<?> incrementer;
private final Position position;
public ForKeyword(Block conditional, Item<?> initializer, Returnable<Boolean> statement, Item<?> incrementer, Position position) {
this.conditional = conditional;
this.initializer = initializer;
this.statement = statement;
this.incrementer = incrementer;
this.position = position;
}
@Override
public Block.ReturnLevel apply(Buffer buffer, Rotation rotation, int recursions) {
for(initializer.apply(buffer, rotation, recursions); statement.apply(buffer, rotation, recursions); incrementer.apply(buffer, rotation, recursions)) {
Block.ReturnLevel level = conditional.apply(buffer, rotation, recursions);
if(level.equals(Block.ReturnLevel.BREAK)) break;
if(level.isReturnFast()) return level;
}
return Block.ReturnLevel.NONE;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.api.structures.parser.lang.keywords;
package com.dfsek.terra.api.structures.parser.lang.keywords.looplike;
import com.dfsek.terra.api.structures.parser.lang.Block;
import com.dfsek.terra.api.structures.parser.lang.Keyword;

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.api.structures.parser.lang.keywords;
package com.dfsek.terra.api.structures.parser.lang.keywords.looplike;
import com.dfsek.terra.api.structures.parser.lang.Block;
import com.dfsek.terra.api.structures.parser.lang.Keyword;
@@ -23,7 +23,7 @@ public class WhileKeyword implements Keyword<Block.ReturnLevel> {
while(statement.apply(buffer, rotation, recursions)) {
Block.ReturnLevel level = conditional.apply(buffer, rotation, recursions);
if(level.equals(Block.ReturnLevel.BREAK)) break;
if(level.equals(Block.ReturnLevel.RETURN)) return Block.ReturnLevel.RETURN;
if(level.isReturnFast()) return level;
}
return Block.ReturnLevel.NONE;
}

View File

@@ -70,7 +70,8 @@ public class Token {
public boolean isLoopLike() {
return type.equals(Type.IF_STATEMENT)
|| type.equals(Type.WHILE_LOOP);
|| type.equals(Type.WHILE_LOOP)
|| type.equals(Type.FOR_LOOP);
}
public boolean isIdentifier() {
@@ -214,6 +215,10 @@ public class Token {
/**
* ID declaration
*/
ID
ID,
/**
* For loop initializer token
*/
FOR_LOOP
}
}

View File

@@ -133,6 +133,8 @@ public class Tokenizer {
return new Token(tokenString, Token.Type.IF_STATEMENT, new Position(reader.getLine(), reader.getIndex()));
if(tokenString.equals("while"))
return new Token(tokenString, Token.Type.WHILE_LOOP, new Position(reader.getLine(), reader.getIndex()));
if(tokenString.equals("for"))
return new Token(tokenString, Token.Type.FOR_LOOP, new Position(reader.getLine(), reader.getIndex()));
if(tokenString.equals("return"))
return new Token(tokenString, Token.Type.RETURN, new Position(reader.getLine(), reader.getIndex()));