diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java index 5c5b2e1a3..fcfd5b162 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java @@ -6,6 +6,7 @@ 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.parser.lang.constants.BooleanConstant; +import com.dfsek.terra.api.structures.parser.lang.constants.ConstantExpression; import com.dfsek.terra.api.structures.parser.lang.constants.NumericConstant; import com.dfsek.terra.api.structures.parser.lang.constants.StringConstant; import com.dfsek.terra.api.structures.parser.lang.functions.Function; @@ -38,19 +39,14 @@ import com.dfsek.terra.api.structures.tokenizer.Token; import com.dfsek.terra.api.structures.tokenizer.Tokenizer; import com.dfsek.terra.api.structures.tokenizer.exceptions.TokenizerException; import com.dfsek.terra.api.util.GlueList; -import com.google.common.collect.Sets; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; public class Parser { private final String data; private final Map>> functions = new HashMap<>(); - private final Set keywords = Sets.newHashSet("if", "return", "while"); - - Set allowedArguments = Sets.newHashSet(Token.Type.STRING, Token.Type.NUMBER, Token.Type.IDENTIFIER); public Parser(String data) { this.data = data; @@ -87,63 +83,41 @@ public class Parser { private Keyword parseKeyword(List tokens, Map> variableMap) throws ParseException { Token identifier = tokens.remove(0); - ParserUtil.checkType(identifier, Token.Type.KEYWORD); - if(!keywords.contains(identifier.getContent())) - throw new ParseException("No such keyword " + identifier.getContent() + ": " + identifier.getPosition()); - Keyword k = null; - if(identifier.getContent().equals("if") || identifier.getContent().equals("while")) { + ParserUtil.checkType(identifier, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP); - ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_BEGIN); + ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_BEGIN); - Returnable comparator = parseExpression(tokens, true, variableMap); - ParserUtil.checkReturnType(comparator, Returnable.ReturnType.BOOLEAN); + Returnable comparator = parseExpression(tokens, true, variableMap); + ParserUtil.checkReturnType(comparator, Returnable.ReturnType.BOOLEAN); - ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_END); + ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_END); - ParserUtil.checkType(tokens.remove(0), Token.Type.BLOCK_BEGIN); + ParserUtil.checkType(tokens.remove(0), Token.Type.BLOCK_BEGIN); - if(identifier.getContent().equals("if")) - k = new IfKeyword(parseBlock(tokens, variableMap), (Returnable) comparator, identifier.getPosition()); // If statement - else - k = new WhileKeyword(parseBlock(tokens, variableMap), (Returnable) comparator, identifier.getPosition()); // While loop - - } - return k; + if(identifier.getType().equals(Token.Type.IF_STATEMENT)) + return new IfKeyword(parseBlock(tokens, variableMap), (Returnable) comparator, identifier.getPosition()); // If statement + else if(identifier.getType().equals(Token.Type.WHILE_LOOP)) + return new WhileKeyword(parseBlock(tokens, variableMap), (Returnable) comparator, identifier.getPosition()); // While loop + else throw new UnsupportedOperationException("Unknown keyword " + identifier.getContent() + ": " + identifier.getPosition()); } @SuppressWarnings("unchecked") private Returnable parseExpression(List tokens, boolean full, Map> variableMap) throws ParseException { - Token first = tokens.get(0); - - if(first.getType().equals(Token.Type.GROUP_BEGIN)) return parseGroup(tokens, variableMap); - - ParserUtil.checkType(first, Token.Type.IDENTIFIER, Token.Type.BOOLEAN, Token.Type.STRING, Token.Type.NUMBER, Token.Type.BOOLEAN_NOT, Token.Type.GROUP_BEGIN); - - boolean not = false; - if(first.getType().equals(Token.Type.BOOLEAN_NOT)) { - not = true; + boolean booleanInverted = false; // Check for boolean not operator + if(tokens.get(0).getType().equals(Token.Type.BOOLEAN_NOT)) { + booleanInverted = true; tokens.remove(0); } Token id = tokens.get(0); + + ParserUtil.checkType(id, Token.Type.IDENTIFIER, Token.Type.BOOLEAN, Token.Type.STRING, Token.Type.NUMBER, Token.Type.GROUP_BEGIN); + Returnable expression; if(id.isConstant()) { - Token constantToken = tokens.remove(0); - Position position = constantToken.getPosition(); - switch(constantToken.getType()) { - case NUMBER: - String content = constantToken.getContent(); - expression = new NumericConstant(content.contains(".") ? Double.parseDouble(content) : Integer.parseInt(content), position); - break; - case STRING: - expression = new StringConstant(constantToken.getContent(), position); - break; - case BOOLEAN: - expression = new BooleanConstant(Boolean.parseBoolean(constantToken.getContent()), position); - break; - default: - throw new UnsupportedOperationException("Unsupported constant token: " + constantToken.getType() + " at position: " + position); - } + expression = parseConstantExpression(tokens); + } else if(id.getType().equals(Token.Type.GROUP_BEGIN)) { // Parse grouped expression + expression = parseGroup(tokens, variableMap); } else { if(functions.containsKey(id.getContent())) expression = parseFunction(tokens, false, variableMap); else if(variableMap.containsKey(id.getContent())) { @@ -152,17 +126,33 @@ public class Parser { } else throw new ParseException("Unexpected token: " + id.getContent() + " at " + id.getPosition()); } - - if(not) { + if(booleanInverted) { // Invert operation if boolean not detected ParserUtil.checkReturnType(expression, Returnable.ReturnType.BOOLEAN); expression = new BooleanNotOperation((Returnable) expression, expression.getPosition()); } - if(full && tokens.get(0).isBinaryOperator()) { + + if(full && tokens.get(0).isBinaryOperator()) { // Parse binary operations return parseBinaryOperation(expression, tokens, variableMap); } return expression; } + private ConstantExpression parseConstantExpression(List tokens) { + Token constantToken = tokens.remove(0); + Position position = constantToken.getPosition(); + switch(constantToken.getType()) { + case NUMBER: + String content = constantToken.getContent(); + return new NumericConstant(content.contains(".") ? Double.parseDouble(content) : Integer.parseInt(content), position); + case STRING: + return new StringConstant(constantToken.getContent(), position); + case BOOLEAN: + return new BooleanConstant(Boolean.parseBoolean(constantToken.getContent()), position); + default: + throw new UnsupportedOperationException("Unsupported constant token: " + constantToken.getType() + " at position: " + position); + } + } + private Returnable parseGroup(List tokens, Map> variableMap) throws ParseException { ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_BEGIN); Returnable expression = parseExpression(tokens, true, variableMap); // Parse inside of group as a separate expression @@ -243,17 +233,18 @@ public class Parser { Map> parsedVariables = new HashMap<>(superVars); // New hashmap as to not mutate parent scope's declarations. Token first = tokens.get(0); + ParserUtil.checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE); - ParserUtil.checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE); main: while(tokens.size() > 0) { Token token = tokens.get(0); - ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.BLOCK_END, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE); + ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.BLOCK_END, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE); switch(token.getType()) { - case KEYWORD: + case IF_STATEMENT: + case WHILE_LOOP: parsedItems.add(parseKeyword(tokens, parsedVariables)); if(tokens.isEmpty()) break; - ParserUtil.checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.BLOCK_END); + ParserUtil.checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.BLOCK_END); break; case IDENTIFIER: if(parsedVariables.containsKey(token.getContent())) { diff --git a/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Token.java b/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Token.java index ab1d9bea5..b85444e66 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Token.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Token.java @@ -68,10 +68,6 @@ public class Token { */ IDENTIFIER, - /** - * Language keyword - */ - KEYWORD, /** * Numeric literal */ @@ -169,6 +165,20 @@ public class Token { */ NUMBER_VARIABLE, STRING_VARIABLE, - BOOLEAN_VARIABLE + BOOLEAN_VARIABLE, + + IF_STATEMENT, + WHILE_LOOP + } + + public enum Group { + KEYWORD(Type.IF_STATEMENT, Type.WHILE_LOOP, Type.BOOLEAN_VARIABLE, Type.STRING_VARIABLE, Type.NUMBER_VARIABLE), + IDENTIFIER(Type.IDENTIFIER), + BINARY_OPERATOR(Type.BOOLEAN_AND, Type.BOOLEAN_OR, Type.ADDITION_OPERATOR, Type.MULTIPLICATION_OPERATOR, Type.SUBTRACTION_OPERATOR, Type.DIVISION_OPERATOR, Type.GREATER_THAN_OPERATOR, Type.GREATER_THAN_OR_EQUALS_OPERATOR, Type.LESS_THAN_OPERATOR, Type.LESS_THAN_OR_EQUALS_OPERATOR, Type.EQUALS_OPERATOR, Type.NOT_EQUALS_OPERATOR); + private final Type[] types; + + Group(Type... types) { + this.types = types; + } } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Tokenizer.java b/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Tokenizer.java index daa5204d7..37292f3a9 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Tokenizer.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/tokenizer/Tokenizer.java @@ -130,9 +130,9 @@ public class Tokenizer { return new Token(tokenString, Token.Type.BOOLEAN_VARIABLE, new Position(reader.getLine(), reader.getIndex())); if(tokenString.equals("if")) - return new Token(tokenString, Token.Type.KEYWORD, new Position(reader.getLine(), reader.getIndex())); + return new Token(tokenString, Token.Type.IF_STATEMENT, new Position(reader.getLine(), reader.getIndex())); if(tokenString.equals("while")) - return new Token(tokenString, Token.Type.KEYWORD, new Position(reader.getLine(), reader.getIndex())); + return new Token(tokenString, Token.Type.WHILE_LOOP, new Position(reader.getLine(), reader.getIndex())); return new Token(tokenString, Token.Type.IDENTIFIER, new Position(reader.getLine(), reader.getIndex())); } diff --git a/common/src/test/resources/test.tesf b/common/src/test/resources/test.tesf index fdc3805f3..a7ebcab75 100644 --- a/common/src/test/resources/test.tesf +++ b/common/src/test/resources/test.tesf @@ -17,9 +17,9 @@ while(iterator < 5) { } -if(true && boolean) { +if(true && !(boolean && false) && true) { num scopedVar = 2; - test("fdsgdf" + 2 + stringVar, 1 + testVar + scopedVar); + test("if statement" + 2 + stringVar, 1 + testVar + scopedVar); } // comment