Simplify code

This commit is contained in:
Astrash
2023-07-29 08:57:43 +10:00
parent f5b115e618
commit 719b9a06f4
6 changed files with 148 additions and 145 deletions
@@ -83,17 +83,17 @@ public class Lexer {
// Check if operator token // Check if operator token
if(reader.matchesString("==", true)) if(reader.matchesString("==", true))
return new Token("==", TokenType.EQUALS_OPERATOR, reader.getPosition()); return new Token("==", TokenType.EQUALS_EQUALS, reader.getPosition());
if(reader.matchesString("!=", true)) if(reader.matchesString("!=", true))
return new Token("!=", TokenType.NOT_EQUALS_OPERATOR, reader.getPosition()); return new Token("!=", TokenType.BANG_EQUALS, reader.getPosition());
if(reader.matchesString(">=", true)) if(reader.matchesString(">=", true))
return new Token(">=", TokenType.GREATER_THAN_OR_EQUALS_OPERATOR, reader.getPosition()); return new Token(">=", TokenType.GREATER_EQUAL, reader.getPosition());
if(reader.matchesString("<=", true)) if(reader.matchesString("<=", true))
return new Token("<=", TokenType.LESS_THAN_OR_EQUALS_OPERATOR, reader.getPosition()); return new Token("<=", TokenType.LESS_EQUALS, reader.getPosition());
if(reader.matchesString(">", true)) if(reader.matchesString(">", true))
return new Token(">", TokenType.GREATER_THAN_OPERATOR, reader.getPosition()); return new Token(">", TokenType.GREATER, reader.getPosition());
if(reader.matchesString("<", true)) if(reader.matchesString("<", true))
return new Token("<", TokenType.LESS_THAN_OPERATOR, reader.getPosition()); return new Token("<", TokenType.LESS, reader.getPosition());
// Check if logical operator // Check if logical operator
if(reader.matchesString("||", true)) if(reader.matchesString("||", true))
@@ -131,9 +131,9 @@ public class Lexer {
} }
if(reader.current().is('(')) if(reader.current().is('('))
return new Token(reader.consume().toString(), TokenType.GROUP_BEGIN, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.OPEN_PAREN, reader.getPosition());
if(reader.current().is(')')) if(reader.current().is(')'))
return new Token(reader.consume().toString(), TokenType.GROUP_END, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.CLOSE_PAREN, reader.getPosition());
if(reader.current().is(';')) if(reader.current().is(';'))
return new Token(reader.consume().toString(), TokenType.STATEMENT_END, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.STATEMENT_END, reader.getPosition());
if(reader.current().is(',')) if(reader.current().is(','))
@@ -153,19 +153,19 @@ public class Lexer {
if(reader.current().is('=')) if(reader.current().is('='))
return new Token(reader.consume().toString(), TokenType.ASSIGNMENT, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.ASSIGNMENT, reader.getPosition());
if(reader.current().is('+')) if(reader.current().is('+'))
return new Token(reader.consume().toString(), TokenType.ADDITION_OPERATOR, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.PLUS, reader.getPosition());
if(reader.current().is('-')) if(reader.current().is('-'))
return new Token(reader.consume().toString(), TokenType.SUBTRACTION_OPERATOR, return new Token(reader.consume().toString(), TokenType.MINUS,
reader.getPosition()); reader.getPosition());
if(reader.current().is('*')) if(reader.current().is('*'))
return new Token(reader.consume().toString(), TokenType.MULTIPLICATION_OPERATOR, return new Token(reader.consume().toString(), TokenType.STAR,
reader.getPosition()); reader.getPosition());
if(reader.current().is('/')) if(reader.current().is('/'))
return new Token(reader.consume().toString(), TokenType.DIVISION_OPERATOR, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.FORWARD_SLASH, reader.getPosition());
if(reader.current().is('%')) if(reader.current().is('%'))
return new Token(reader.consume().toString(), TokenType.MODULO_OPERATOR, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.MODULO_OPERATOR, reader.getPosition());
if(reader.current().is('!')) if(reader.current().is('!'))
return new Token(reader.consume().toString(), TokenType.BOOLEAN_NOT, reader.getPosition()); return new Token(reader.consume().toString(), TokenType.BANG, reader.getPosition());
// Read word // Read word
StringBuilder token = new StringBuilder(); StringBuilder token = new StringBuilder();
@@ -44,29 +44,29 @@ public class Token {
} }
public boolean isBinaryOperator() { public boolean isBinaryOperator() {
return type.equals(TokenType.ADDITION_OPERATOR) return type.equals(TokenType.PLUS)
|| type.equals(TokenType.SUBTRACTION_OPERATOR) || type.equals(TokenType.MINUS)
|| type.equals(TokenType.MULTIPLICATION_OPERATOR) || type.equals(TokenType.STAR)
|| type.equals(TokenType.DIVISION_OPERATOR) || type.equals(TokenType.FORWARD_SLASH)
|| type.equals(TokenType.EQUALS_OPERATOR) || type.equals(TokenType.EQUALS_EQUALS)
|| type.equals(TokenType.NOT_EQUALS_OPERATOR) || type.equals(TokenType.BANG_EQUALS)
|| type.equals(TokenType.LESS_THAN_OPERATOR) || type.equals(TokenType.LESS)
|| type.equals(TokenType.GREATER_THAN_OPERATOR) || type.equals(TokenType.GREATER)
|| type.equals(TokenType.LESS_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.LESS_EQUALS)
|| type.equals(TokenType.GREATER_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.GREATER_EQUAL)
|| type.equals(TokenType.BOOLEAN_OR) || type.equals(TokenType.BOOLEAN_OR)
|| type.equals(TokenType.BOOLEAN_AND) || type.equals(TokenType.BOOLEAN_AND)
|| type.equals(TokenType.MODULO_OPERATOR); || type.equals(TokenType.MODULO_OPERATOR);
} }
public boolean isStrictNumericOperator() { public boolean isStrictNumericOperator() {
return type.equals(TokenType.SUBTRACTION_OPERATOR) return type.equals(TokenType.MINUS)
|| type.equals(TokenType.MULTIPLICATION_OPERATOR) || type.equals(TokenType.STAR)
|| type.equals(TokenType.DIVISION_OPERATOR) || type.equals(TokenType.FORWARD_SLASH)
|| type.equals(TokenType.GREATER_THAN_OPERATOR) || type.equals(TokenType.GREATER)
|| type.equals(TokenType.LESS_THAN_OPERATOR) || type.equals(TokenType.LESS)
|| type.equals(TokenType.LESS_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.LESS_EQUALS)
|| type.equals(TokenType.GREATER_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.GREATER_EQUAL)
|| type.equals(TokenType.MODULO_OPERATOR); || type.equals(TokenType.MODULO_OPERATOR);
} }
@@ -108,11 +108,11 @@ public class Token {
/** /**
* Beginning of group * Beginning of group
*/ */
GROUP_BEGIN, OPEN_PAREN,
/** /**
* Ending of group * Ending of group
*/ */
GROUP_END, CLOSE_PAREN,
/** /**
* End of statement * End of statement
*/ */
@@ -136,43 +136,43 @@ public class Token {
/** /**
* Boolean equals operator * Boolean equals operator
*/ */
EQUALS_OPERATOR, EQUALS_EQUALS,
/** /**
* Boolean not equals operator * Boolean not equals operator
*/ */
NOT_EQUALS_OPERATOR, BANG_EQUALS,
/** /**
* Boolean greater than operator * Boolean greater than operator
*/ */
GREATER_THAN_OPERATOR, GREATER,
/** /**
* Boolean less than operator * Boolean less than operator
*/ */
LESS_THAN_OPERATOR, LESS,
/** /**
* Boolean greater than or equal to operator * Boolean greater than or equal to operator
*/ */
GREATER_THAN_OR_EQUALS_OPERATOR, GREATER_EQUAL,
/** /**
* Boolean less than or equal to operator * Boolean less than or equal to operator
*/ */
LESS_THAN_OR_EQUALS_OPERATOR, LESS_EQUALS,
/** /**
* Addition/concatenation operator * Addition/concatenation operator
*/ */
ADDITION_OPERATOR, PLUS,
/** /**
* Subtraction operator * Subtraction operator
*/ */
SUBTRACTION_OPERATOR, MINUS,
/** /**
* Multiplication operator * Multiplication operator
*/ */
MULTIPLICATION_OPERATOR, STAR,
/** /**
* Division operator * Division operator
*/ */
DIVISION_OPERATOR, FORWARD_SLASH,
/** /**
* Modulo operator. * Modulo operator.
*/ */
@@ -180,7 +180,7 @@ public class Token {
/** /**
* Boolean not operator * Boolean not operator
*/ */
BOOLEAN_NOT, BANG,
/** /**
* Boolean or * Boolean or
*/ */
@@ -63,11 +63,12 @@ import com.dfsek.terra.api.util.generic.pair.Pair;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class Parser { public class Parser {
private final String source;
private final List<String> ignoredFunctions = new ArrayList<>(); private final List<String> ignoredFunctions = new ArrayList<>();
public Parser(String source) { private final Lexer lexer;
this.source = source;
public Parser(Lexer lexer) {
this.lexer = lexer;
} }
/** /**
@@ -78,30 +79,30 @@ public class Parser {
* @throws ParseException If parsing fails. * @throws ParseException If parsing fails.
*/ */
public Executable parse(ScopeBuilder scopeBuilder) { public Executable parse(ScopeBuilder scopeBuilder) {
return new Executable(parseBlock(new Lexer(source), scopeBuilder, ReturnType.VOID), scopeBuilder); return new Executable(parseBlock(scopeBuilder, ReturnType.VOID), scopeBuilder);
} }
private WhileKeyword parseWhileLoop(Lexer lexer, ScopeBuilder scopeBuilder) { private WhileKeyword parseWhileLoop(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume().getPosition(); SourcePosition start = lexer.consume().getPosition();
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_BEGIN); ParserUtil.ensureType(lexer.consume(), TokenType.OPEN_PAREN);
scopeBuilder = scopeBuilder.innerLoopScope(); scopeBuilder = scopeBuilder.innerLoopScope();
Expression<?> condition = parseExpression(lexer, true, scopeBuilder); Expression<?> condition = parseExpression(true, scopeBuilder);
ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN);
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_END); ParserUtil.ensureType(lexer.consume(), TokenType.CLOSE_PAREN);
return new WhileKeyword(parseStatementBlock(lexer, scopeBuilder, ReturnType.VOID), (Expression<Boolean>) condition, return new WhileKeyword(parseStatementBlock(scopeBuilder, ReturnType.VOID), (Expression<Boolean>) condition,
start); // While loop start); // While loop
} }
private IfKeyword parseIfStatement(Lexer lexer, ScopeBuilder scopeBuilder) { private IfKeyword parseIfStatement(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume().getPosition(); SourcePosition start = lexer.consume().getPosition();
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_BEGIN); ParserUtil.ensureType(lexer.consume(), TokenType.OPEN_PAREN);
Expression<?> condition = parseExpression(lexer, true, scopeBuilder); Expression<?> condition = parseExpression(true, scopeBuilder);
ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN);
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_END); ParserUtil.ensureType(lexer.consume(), TokenType.CLOSE_PAREN);
Block elseBlock = null; Block elseBlock = null;
Block statement = parseStatementBlock(lexer, scopeBuilder, ReturnType.VOID); Block statement = parseStatementBlock(scopeBuilder, ReturnType.VOID);
List<Pair<Expression<Boolean>, Block>> elseIf = new ArrayList<>(); List<Pair<Expression<Boolean>, Block>> elseIf = new ArrayList<>();
@@ -109,11 +110,11 @@ public class Parser {
lexer.consume(); // Consume else. lexer.consume(); // Consume else.
if(lexer.current().isType(TokenType.IF_STATEMENT)) { if(lexer.current().isType(TokenType.IF_STATEMENT)) {
lexer.consume(); // Consume if. lexer.consume(); // Consume if.
Expression<?> elseCondition = parseExpression(lexer, true, scopeBuilder); Expression<?> elseCondition = parseExpression(true, scopeBuilder);
ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN);
elseIf.add(Pair.of((Expression<Boolean>) elseCondition, parseStatementBlock(lexer, scopeBuilder, ReturnType.VOID))); elseIf.add(Pair.of((Expression<Boolean>) elseCondition, parseStatementBlock(scopeBuilder, ReturnType.VOID)));
} else { } else {
elseBlock = parseStatementBlock(lexer, scopeBuilder, ReturnType.VOID); elseBlock = parseStatementBlock(scopeBuilder, ReturnType.VOID);
break; // Else must be last. break; // Else must be last.
} }
} }
@@ -121,10 +122,10 @@ 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(Lexer lexer, ScopeBuilder scopeBuilder, ReturnType blockReturnType) { private Block parseStatementBlock(ScopeBuilder scopeBuilder, ReturnType blockReturnType) {
if(lexer.current().isType(TokenType.BLOCK_BEGIN)) { if(lexer.current().isType(TokenType.BLOCK_BEGIN)) {
ParserUtil.ensureType(lexer.consume(), TokenType.BLOCK_BEGIN); ParserUtil.ensureType(lexer.consume(), TokenType.BLOCK_BEGIN);
Block block = parseBlock(lexer, scopeBuilder, blockReturnType); Block block = parseBlock(scopeBuilder, blockReturnType);
ParserUtil.ensureType(lexer.consume(), TokenType.BLOCK_END); ParserUtil.ensureType(lexer.consume(), TokenType.BLOCK_END);
return block; return block;
} else { } else {
@@ -133,61 +134,61 @@ public class Parser {
} }
} }
private ForKeyword parseForLoop(Lexer lexer, ScopeBuilder scopeBuilder) { private ForKeyword parseForLoop(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume().getPosition(); SourcePosition start = lexer.consume().getPosition();
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_BEGIN); ParserUtil.ensureType(lexer.consume(), TokenType.OPEN_PAREN);
scopeBuilder = scopeBuilder.innerLoopScope(); // new scope scopeBuilder = scopeBuilder.innerLoopScope(); // new scope
Token f = lexer.current(); Token f = lexer.current();
ParserUtil.ensureType(f, TokenType.TYPE_NUMBER, TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.IDENTIFIER); ParserUtil.ensureType(f, TokenType.TYPE_NUMBER, TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.IDENTIFIER);
Expression<?> initializer; Expression<?> initializer;
if(f.isVariableDeclaration()) { if(f.isVariableDeclaration()) {
Expression<?> forVar = parseDeclaration(lexer, scopeBuilder); Expression<?> forVar = parseDeclaration(scopeBuilder);
Token name = lexer.current(); Token name = lexer.current();
if(scopeBuilder.containsFunction(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()); throw new ParseException(name.getContent() + " is already defined in this scope", name.getPosition());
initializer = forVar; initializer = forVar;
} else initializer = parseExpression(lexer, true, scopeBuilder); } else initializer = parseExpression(true, scopeBuilder);
ParserUtil.ensureType(lexer.consume(), TokenType.STATEMENT_END); ParserUtil.ensureType(lexer.consume(), TokenType.STATEMENT_END);
Expression<?> conditional = parseExpression(lexer, true, scopeBuilder); Expression<?> conditional = parseExpression(true, scopeBuilder);
ParserUtil.ensureReturnType(conditional, Expression.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(conditional, Expression.ReturnType.BOOLEAN);
ParserUtil.ensureType(lexer.consume(), TokenType.STATEMENT_END); ParserUtil.ensureType(lexer.consume(), TokenType.STATEMENT_END);
Expression<?> incrementer; Expression<?> incrementer;
Token token = lexer.current(); Token token = lexer.current();
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment
incrementer = parseAssignment(lexer, scopeBuilder); incrementer = parseAssignment(scopeBuilder);
} else incrementer = parseFunctionInvocation(lexer, true, scopeBuilder); } else incrementer = parseFunctionInvocation(true, scopeBuilder);
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_END); ParserUtil.ensureType(lexer.consume(), TokenType.CLOSE_PAREN);
return new ForKeyword(parseStatementBlock(lexer, scopeBuilder, ReturnType.VOID), initializer, (Expression<Boolean>) conditional, return new ForKeyword(parseStatementBlock(scopeBuilder, ReturnType.VOID), initializer, (Expression<Boolean>) conditional,
incrementer, incrementer,
start); start);
} }
private Expression<?> parseExpression(Lexer lexer, boolean full, ScopeBuilder scopeBuilder) { private Expression<?> parseExpression(boolean full, ScopeBuilder scopeBuilder) {
boolean booleanInverted = false; // Check for boolean not operator boolean booleanInverted = false; // Check for boolean not operator
boolean negate = false; boolean negate = false;
if(lexer.current().isType(TokenType.BOOLEAN_NOT)) { if(lexer.current().isType(TokenType.BANG)) {
booleanInverted = true; booleanInverted = true;
lexer.consume(); lexer.consume();
} else if(lexer.current().isType(TokenType.SUBTRACTION_OPERATOR)) { } else if(lexer.current().isType(TokenType.MINUS)) {
negate = true; negate = true;
lexer.consume(); lexer.consume();
} }
Token id = lexer.current(); Token id = lexer.current();
ParserUtil.ensureType(id, TokenType.IDENTIFIER, TokenType.BOOLEAN, TokenType.STRING, TokenType.NUMBER, TokenType.GROUP_BEGIN); ParserUtil.ensureType(id, TokenType.IDENTIFIER, TokenType.BOOLEAN, TokenType.STRING, TokenType.NUMBER, TokenType.OPEN_PAREN);
Expression<?> expression; Expression<?> expression;
if(id.isConstant()) { if(id.isConstant()) {
expression = parseConstantExpression(lexer); expression = parseConstantExpression(lexer);
} else if(id.isType(TokenType.GROUP_BEGIN)) { // Parse grouped expression } else if(id.isType(TokenType.OPEN_PAREN)) { // Parse grouped expression
expression = parseExpressionGroup(lexer, scopeBuilder); expression = parseExpressionGroup(scopeBuilder);
} else { } else {
if(scopeBuilder.containsFunction(id.getContent())) if(scopeBuilder.containsFunction(id.getContent()))
expression = parseFunctionInvocation(lexer, false, scopeBuilder); expression = parseFunctionInvocation(false, scopeBuilder);
else if(scopeBuilder.contains(id.getContent())) { else if(scopeBuilder.contains(id.getContent())) {
ParserUtil.ensureType(lexer.consume(), TokenType.IDENTIFIER); ParserUtil.ensureType(lexer.consume(), TokenType.IDENTIFIER);
String varId = id.getContent(); String varId = id.getContent();
@@ -211,7 +212,7 @@ public class Parser {
} }
if(full && lexer.current().isBinaryOperator()) { // Parse binary operations if(full && lexer.current().isBinaryOperator()) { // Parse binary operations
return parseBinaryOperation(expression, lexer, scopeBuilder); return parseBinaryOperation(expression, scopeBuilder);
} }
return expression; return expression;
} }
@@ -231,27 +232,26 @@ public class Parser {
}; };
} }
private Expression<?> parseExpressionGroup(Lexer lexer, ScopeBuilder scopeBuilder) { private Expression<?> parseExpressionGroup(ScopeBuilder scopeBuilder) {
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_BEGIN); ParserUtil.ensureType(lexer.consume(), TokenType.OPEN_PAREN);
Expression<?> expression = parseExpression(lexer, true, scopeBuilder); // Parse inside of group as a separate expression Expression<?> expression = parseExpression(true, scopeBuilder); // Parse inside of group as a separate expression
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_END); ParserUtil.ensureType(lexer.consume(), TokenType.CLOSE_PAREN);
return expression; return expression;
} }
private BinaryOperation<?, ?> parseBinaryOperation(Expression<?> left, Lexer lexer, private BinaryOperation<?, ?> parseBinaryOperation(Expression<?> left, ScopeBuilder scopeBuilder) {
ScopeBuilder scopeBuilder) {
Token binaryOperator = lexer.consume(); Token binaryOperator = lexer.consume();
ParserUtil.checkBinaryOperator(binaryOperator); ParserUtil.checkBinaryOperator(binaryOperator);
Expression<?> right = parseExpression(lexer, false, scopeBuilder); Expression<?> right = parseExpression(false, scopeBuilder);
Token other = lexer.current(); Token other = lexer.current();
if(ParserUtil.hasPrecedence(binaryOperator.getType(), other.getType())) if(ParserUtil.hasPrecedence(binaryOperator.getType(), other.getType()))
return assemble(left, parseBinaryOperation(right, lexer, scopeBuilder), binaryOperator); return assemble(left, parseBinaryOperation(right, scopeBuilder), binaryOperator);
if(other.isBinaryOperator()) if(other.isBinaryOperator())
return parseBinaryOperation(assemble(left, right, binaryOperator), lexer, scopeBuilder); return parseBinaryOperation(assemble(left, right, binaryOperator), scopeBuilder);
return assemble(left, right, binaryOperator); return assemble(left, right, binaryOperator);
} }
@@ -261,29 +261,29 @@ public class Parser {
ParserUtil.checkArithmeticOperation(left, right, binaryOperator); // Numeric type checking ParserUtil.checkArithmeticOperation(left, right, binaryOperator); // Numeric type checking
if(binaryOperator.isStrictBooleanOperator()) ParserUtil.checkBooleanOperation(left, right, binaryOperator); // Boolean type checking if(binaryOperator.isStrictBooleanOperator()) ParserUtil.checkBooleanOperation(left, right, binaryOperator); // Boolean type checking
switch(binaryOperator.getType()) { switch(binaryOperator.getType()) {
case ADDITION_OPERATOR: case PLUS:
if(left.returnType().equals(Expression.ReturnType.NUMBER) && right.returnType().equals(Expression.ReturnType.NUMBER)) { if(left.returnType().equals(Expression.ReturnType.NUMBER) && right.returnType().equals(Expression.ReturnType.NUMBER)) {
return new NumberAdditionOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new NumberAdditionOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
} }
return new ConcatenationOperation((Expression<Object>) left, (Expression<Object>) right, binaryOperator.getPosition()); return new ConcatenationOperation((Expression<Object>) left, (Expression<Object>) right, binaryOperator.getPosition());
case SUBTRACTION_OPERATOR: case MINUS:
return new SubtractionOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new SubtractionOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
case MULTIPLICATION_OPERATOR: case STAR:
return new MultiplicationOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new MultiplicationOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
case DIVISION_OPERATOR: case FORWARD_SLASH:
return new DivisionOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new DivisionOperation((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
case EQUALS_OPERATOR: case EQUALS_EQUALS:
return new EqualsStatement((Expression<Object>) left, (Expression<Object>) right, binaryOperator.getPosition()); return new EqualsStatement((Expression<Object>) left, (Expression<Object>) right, binaryOperator.getPosition());
case NOT_EQUALS_OPERATOR: case BANG_EQUALS:
return new NotEqualsStatement((Expression<Object>) left, (Expression<Object>) right, binaryOperator.getPosition()); return new NotEqualsStatement((Expression<Object>) left, (Expression<Object>) right, binaryOperator.getPosition());
case GREATER_THAN_OPERATOR: case GREATER:
return new GreaterThanStatement((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new GreaterThanStatement((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
case LESS_THAN_OPERATOR: case LESS:
return new LessThanStatement((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new LessThanStatement((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
case GREATER_THAN_OR_EQUALS_OPERATOR: case GREATER_EQUAL:
return new GreaterOrEqualsThanStatement((Expression<Number>) left, (Expression<Number>) right, return new GreaterOrEqualsThanStatement((Expression<Number>) left, (Expression<Number>) right,
binaryOperator.getPosition()); binaryOperator.getPosition());
case LESS_THAN_OR_EQUALS_OPERATOR: case LESS_EQUALS:
return new LessThanOrEqualsStatement((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition()); return new LessThanOrEqualsStatement((Expression<Number>) left, (Expression<Number>) right, binaryOperator.getPosition());
case BOOLEAN_AND: case BOOLEAN_AND:
return new BooleanAndOperation((Expression<Boolean>) left, (Expression<Boolean>) right, binaryOperator.getPosition()); return new BooleanAndOperation((Expression<Boolean>) left, (Expression<Boolean>) right, binaryOperator.getPosition());
@@ -296,30 +296,30 @@ public class Parser {
} }
} }
private Expression<?> parseDeclaration(Lexer lexer, ScopeBuilder scopeBuilder) { private Expression<?> parseDeclaration(ScopeBuilder scopeBuilder) {
Token type = lexer.consume(); Token type = lexer.consume();
Token identifier = lexer.consume(); Token identifier = lexer.consume();
ParserUtil.ensureType(identifier, TokenType.IDENTIFIER); ParserUtil.ensureType(identifier, TokenType.IDENTIFIER);
Token declarationType = lexer.consume(); Token declarationType = lexer.consume();
ParserUtil.ensureType(declarationType, TokenType.ASSIGNMENT, TokenType.GROUP_BEGIN); ParserUtil.ensureType(declarationType, TokenType.ASSIGNMENT, TokenType.OPEN_PAREN);
return switch(declarationType.getType()) { return switch(declarationType.getType()) {
case ASSIGNMENT -> parseVariableDeclaration(lexer, scopeBuilder, type, identifier); case ASSIGNMENT -> parseVariableDeclaration(scopeBuilder, type, identifier);
case GROUP_BEGIN -> parseFunctionDeclaration(lexer, scopeBuilder, type, identifier); case OPEN_PAREN -> parseFunctionDeclaration(scopeBuilder, type, identifier);
default -> throw new ParseException("Illegal type for declaration: " + type, declarationType.getPosition()); default -> throw new ParseException("Illegal type for declaration: " + type, declarationType.getPosition());
}; };
} }
private Expression<?> parseVariableDeclaration(Lexer lexer, ScopeBuilder scopeBuilder, Token type, Token identifier) { private Expression<?> parseVariableDeclaration(ScopeBuilder scopeBuilder, Token type, Token identifier) {
ParserUtil.ensureType(type, TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.TYPE_NUMBER); ParserUtil.ensureType(type, TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.TYPE_NUMBER);
if(scopeBuilder.contains(identifier.getContent())) if(scopeBuilder.contains(identifier.getContent()))
throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition()); throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition());
Expression<?> value = parseExpression(lexer, true, scopeBuilder); Expression<?> value = parseExpression(true, scopeBuilder);
ParserUtil.ensureReturnType(value, ParserUtil.getVariableReturnType(type)); ParserUtil.ensureReturnType(value, ParserUtil.getVariableReturnType(type));
String variableName = identifier.getContent(); String variableName = identifier.getContent();
@@ -334,7 +334,7 @@ public class Parser {
}; };
} }
private Expression<?> parseFunctionDeclaration(Lexer lexer, ScopeBuilder scopeBuilder, Token type, Token identifier) { private Expression<?> parseFunctionDeclaration(ScopeBuilder scopeBuilder, Token type, Token identifier) {
ParserUtil.ensureType(type, TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.TYPE_NUMBER, TokenType.TYPE_VOID); ParserUtil.ensureType(type, TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.TYPE_NUMBER, TokenType.TYPE_VOID);
if(scopeBuilder.contains(identifier.getContent())) if(scopeBuilder.contains(identifier.getContent()))
@@ -345,7 +345,7 @@ public class Parser {
ScopeBuilder functionBodyScope = scopeBuilder.functionScope(); ScopeBuilder functionBodyScope = scopeBuilder.functionScope();
// Declare argument names into function body scope // Declare argument names into function body scope
List<Pair<Integer, ReturnType>> argumentInfo = getFunctionArgumentsDeclaration(lexer).stream().map( List<Pair<Integer, ReturnType>> argumentInfo = getFunctionArgumentsDeclaration().stream().map(
arg -> Pair.of(switch(arg.getRight()) { arg -> Pair.of(switch(arg.getRight()) {
case NUMBER -> functionBodyScope.declareNum(arg.getLeft()); case NUMBER -> functionBodyScope.declareNum(arg.getLeft());
case BOOLEAN -> functionBodyScope.declareBool(arg.getLeft()); case BOOLEAN -> functionBodyScope.declareBool(arg.getLeft());
@@ -353,7 +353,7 @@ public class Parser {
default -> throw new IllegalArgumentException("Unsupported argument type: " + arg.getRight()); default -> throw new IllegalArgumentException("Unsupported argument type: " + arg.getRight());
}, arg.getRight())).toList(); }, arg.getRight())).toList();
Block body = parseStatementBlock(lexer, functionBodyScope, returnType); Block body = parseStatementBlock(functionBodyScope, returnType);
FunctionBuilder<?> functionBuilder = new UserDefinedFunctionBuilder<>(returnType, argumentInfo, body, functionBodyScope); FunctionBuilder<?> functionBuilder = new UserDefinedFunctionBuilder<>(returnType, argumentInfo, body, functionBodyScope);
@@ -361,9 +361,9 @@ public class Parser {
return Expression.NOOP; return Expression.NOOP;
} }
private List<Pair<String, ReturnType>> getFunctionArgumentsDeclaration(Lexer lexer) { private List<Pair<String, ReturnType>> getFunctionArgumentsDeclaration() {
List<Pair<String, ReturnType>> arguments = new ArrayList<>(); List<Pair<String, ReturnType>> arguments = new ArrayList<>();
while(lexer.current().getType() != TokenType.GROUP_END) { while(lexer.current().getType() != TokenType.CLOSE_PAREN) {
// Parse argument type // Parse argument type
Token typeToken = lexer.consume(); Token typeToken = lexer.consume();
ParserUtil.ensureType(typeToken, TokenType.TYPE_BOOLEAN, TokenType.TYPE_STRING, TokenType.TYPE_NUMBER); ParserUtil.ensureType(typeToken, TokenType.TYPE_BOOLEAN, TokenType.TYPE_STRING, TokenType.TYPE_NUMBER);
@@ -379,11 +379,11 @@ public class Parser {
// Consume separator if present, trailing separators are allowed // Consume separator if present, trailing separators are allowed
if(lexer.current().isType(TokenType.SEPARATOR)) lexer.consume(); if(lexer.current().isType(TokenType.SEPARATOR)) lexer.consume();
} }
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_END); ParserUtil.ensureType(lexer.consume(), TokenType.CLOSE_PAREN);
return arguments; return arguments;
} }
private Block parseBlock(Lexer lexer, ScopeBuilder scopeBuilder, ReturnType blockReturnType) { private Block parseBlock(ScopeBuilder scopeBuilder, ReturnType blockReturnType) {
List<Expression<?>> expressions = new ArrayList<>(); List<Expression<?>> expressions = new ArrayList<>();
scopeBuilder = scopeBuilder.innerScope(); scopeBuilder = scopeBuilder.innerScope();
SourcePosition startPosition = lexer.current().getPosition(); SourcePosition startPosition = lexer.current().getPosition();
@@ -428,15 +428,15 @@ public class Parser {
TokenType.FAIL); TokenType.FAIL);
Expression<?> expression = switch(token.getType()) { Expression<?> expression = switch(token.getType()) {
case FOR_LOOP -> parseForLoop(lexer, scopeBuilder); case FOR_LOOP -> parseForLoop(scopeBuilder);
case IF_STATEMENT -> parseIfStatement(lexer, scopeBuilder); case IF_STATEMENT -> parseIfStatement(scopeBuilder);
case WHILE_LOOP -> parseWhileLoop(lexer, scopeBuilder); case WHILE_LOOP -> parseWhileLoop(scopeBuilder);
case IDENTIFIER -> { case IDENTIFIER -> {
if(scopeBuilder.contains(token.getContent())) yield parseAssignment(lexer, scopeBuilder); // Assume variable assignment if(scopeBuilder.contains(token.getContent())) yield parseAssignment(scopeBuilder); // Assume variable assignment
else yield parseFunctionInvocation(lexer, true, scopeBuilder); else yield parseFunctionInvocation(true, scopeBuilder);
} }
case TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN, TYPE_VOID -> parseDeclaration(lexer, scopeBuilder); case TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN, TYPE_VOID -> parseDeclaration(scopeBuilder);
case RETURN -> parseReturn(lexer, scopeBuilder); case RETURN -> parseReturn(scopeBuilder);
case BREAK -> new BreakKeyword(lexer.consume().getPosition()); case BREAK -> new BreakKeyword(lexer.consume().getPosition());
case CONTINUE -> new ContinueKeyword(lexer.consume().getPosition()); case CONTINUE -> new ContinueKeyword(lexer.consume().getPosition());
case FAIL -> new FailKeyword(lexer.consume().getPosition()); case FAIL -> new FailKeyword(lexer.consume().getPosition());
@@ -446,24 +446,24 @@ public class Parser {
return expression; return expression;
} }
private ReturnKeyword parseReturn(Lexer lexer, ScopeBuilder scopeBuilder) { private ReturnKeyword parseReturn(ScopeBuilder scopeBuilder) {
Token returnToken = lexer.consume(); Token returnToken = lexer.consume();
ParserUtil.ensureType(returnToken, TokenType.RETURN); ParserUtil.ensureType(returnToken, TokenType.RETURN);
Expression<?> data = null; Expression<?> data = null;
if(!lexer.current().isType(TokenType.STATEMENT_END)) { if(!lexer.current().isType(TokenType.STATEMENT_END)) {
data = parseExpression(lexer, true, scopeBuilder); data = parseExpression(true, scopeBuilder);
} }
return new ReturnKeyword(data, returnToken.getPosition()); return new ReturnKeyword(data, returnToken.getPosition());
} }
private VariableAssignmentNode<?> parseAssignment(Lexer lexer, ScopeBuilder scopeBuilder) { private VariableAssignmentNode<?> parseAssignment(ScopeBuilder scopeBuilder) {
Token identifier = lexer.consume(); Token identifier = lexer.consume();
ParserUtil.ensureType(identifier, TokenType.IDENTIFIER); ParserUtil.ensureType(identifier, TokenType.IDENTIFIER);
ParserUtil.ensureType(lexer.consume(), TokenType.ASSIGNMENT); ParserUtil.ensureType(lexer.consume(), TokenType.ASSIGNMENT);
Expression<?> value = parseExpression(lexer, true, scopeBuilder); Expression<?> value = parseExpression(true, scopeBuilder);
String id = identifier.getContent(); String id = identifier.getContent();
@@ -479,18 +479,18 @@ public class Parser {
}; };
} }
private Expression<?> parseFunctionInvocation(Lexer lexer, boolean fullStatement, ScopeBuilder scopeBuilder) { private Expression<?> parseFunctionInvocation(boolean fullStatement, ScopeBuilder scopeBuilder) {
Token identifier = lexer.consume(); Token identifier = lexer.consume();
ParserUtil.ensureType(identifier, TokenType.IDENTIFIER); // First token must be identifier ParserUtil.ensureType(identifier, TokenType.IDENTIFIER); // First token must be identifier
if(!scopeBuilder.containsFunction(identifier.getContent())) if(!scopeBuilder.containsFunction(identifier.getContent()))
throw new ParseException("Function \"" + identifier.getContent() + "\" is not defined in this scope", identifier.getPosition()); throw new ParseException("Function \"" + identifier.getContent() + "\" is not defined in this scope", identifier.getPosition());
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_BEGIN); // Second is body begin ParserUtil.ensureType(lexer.consume(), TokenType.OPEN_PAREN); // Second is body begin
List<Expression<?>> args = getFunctionArgs(lexer, scopeBuilder); // Extract arguments, consume the rest. List<Expression<?>> args = getFunctionArgs(scopeBuilder); // Extract arguments, consume the rest.
ParserUtil.ensureType(lexer.consume(), TokenType.GROUP_END); // Remove body end ParserUtil.ensureType(lexer.consume(), TokenType.CLOSE_PAREN); // Remove body end
if(fullStatement) ParserUtil.ensureType(lexer.current(), TokenType.STATEMENT_END); if(fullStatement) ParserUtil.ensureType(lexer.current(), TokenType.STATEMENT_END);
@@ -516,12 +516,12 @@ public class Parser {
throw new UnsupportedOperationException("Unsupported function: " + identifier.getContent()); throw new UnsupportedOperationException("Unsupported function: " + identifier.getContent());
} }
private List<Expression<?>> getFunctionArgs(Lexer lexer, ScopeBuilder scopeBuilder) { private List<Expression<?>> getFunctionArgs(ScopeBuilder scopeBuilder) {
List<Expression<?>> args = new ArrayList<>(); List<Expression<?>> args = new ArrayList<>();
while(!lexer.current().isType(TokenType.GROUP_END)) { while(!lexer.current().isType(TokenType.CLOSE_PAREN)) {
args.add(parseExpression(lexer, true, scopeBuilder)); args.add(parseExpression(true, scopeBuilder));
ParserUtil.ensureType(lexer.current(), TokenType.SEPARATOR, TokenType.GROUP_END); ParserUtil.ensureType(lexer.current(), TokenType.SEPARATOR, TokenType.CLOSE_PAREN);
if(lexer.current().isType(TokenType.SEPARATOR)) lexer.consume(); if(lexer.current().isType(TokenType.SEPARATOR)) lexer.consume();
} }
return args; return args;
@@ -21,21 +21,21 @@ import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class ParserUtil { public class ParserUtil {
private static final Map<TokenType, Map<TokenType, Boolean>> PRECEDENCE = new HashMap<>(); // If second has precedence, true. private static final Map<TokenType, Map<TokenType, Boolean>> PRECEDENCE = new HashMap<>(); // If second has precedence, true.
private static final List<TokenType> ARITHMETIC = Arrays.asList(TokenType.ADDITION_OPERATOR, TokenType.SUBTRACTION_OPERATOR, private static final List<TokenType> ARITHMETIC = Arrays.asList(TokenType.PLUS, TokenType.MINUS,
TokenType.MULTIPLICATION_OPERATOR, TokenType.DIVISION_OPERATOR, TokenType.STAR, TokenType.FORWARD_SLASH,
TokenType.MODULO_OPERATOR); TokenType.MODULO_OPERATOR);
private static final List<TokenType> COMPARISON = Arrays.asList(TokenType.EQUALS_OPERATOR, TokenType.NOT_EQUALS_OPERATOR, private static final List<TokenType> COMPARISON = Arrays.asList(TokenType.EQUALS_EQUALS, TokenType.BANG_EQUALS,
TokenType.LESS_THAN_OPERATOR, TokenType.LESS_THAN_OR_EQUALS_OPERATOR, TokenType.LESS, TokenType.LESS_EQUALS,
TokenType.GREATER_THAN_OPERATOR, TokenType.GREATER,
TokenType.GREATER_THAN_OR_EQUALS_OPERATOR); TokenType.GREATER_EQUAL);
static { // Setup precedence static { // Setup precedence
Map<TokenType, Boolean> add = new HashMap<>(); // Addition/subtraction before Multiplication/division. Map<TokenType, Boolean> add = new HashMap<>(); // Addition/subtraction before Multiplication/division.
add.put(TokenType.MULTIPLICATION_OPERATOR, true); add.put(TokenType.STAR, true);
add.put(TokenType.DIVISION_OPERATOR, true); add.put(TokenType.FORWARD_SLASH, true);
PRECEDENCE.put(TokenType.ADDITION_OPERATOR, add); PRECEDENCE.put(TokenType.PLUS, add);
PRECEDENCE.put(TokenType.SUBTRACTION_OPERATOR, add); PRECEDENCE.put(TokenType.MINUS, add);
Map<TokenType, Boolean> numericBoolean = new HashMap<>(); Map<TokenType, Boolean> numericBoolean = new HashMap<>();
@@ -7,6 +7,8 @@
package com.dfsek.terra.addons.terrascript.script; package com.dfsek.terra.addons.terrascript.script;
import com.dfsek.terra.addons.terrascript.lexer.Lexer;
import net.jafama.FastMath; import net.jafama.FastMath;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -62,18 +64,18 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
public StructureScript(InputStream source, RegistryKey id, Platform platform, Registry<Structure> structureRegistry, public StructureScript(InputStream source, RegistryKey id, Platform platform, Registry<Structure> structureRegistry,
Registry<LootTable> lootRegistry, Registry<LootTable> lootRegistry,
Registry<FunctionBuilder> functionRegistry) { Registry<FunctionBuilder> functionRegistry) {
Parser parser; Lexer lexer;
try { try {
parser = new Parser(IOUtils.toString(source, Charset.defaultCharset())); lexer = new Lexer(IOUtils.toString(source, Charset.defaultCharset()));
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
Parser parser = new Parser(lexer);
this.id = id; this.id = id;
this.profile = "terrascript_direct:" + id; this.profile = "terrascript_direct:" + id;
ScopeBuilder scope = new ScopeBuilder(); ScopeBuilder scope = new ScopeBuilder();
//noinspection unchecked
functionRegistry.forEach((key, function) -> scope.registerFunction(key.getID(), function)); // Register registry functions. functionRegistry.forEach((key, function) -> scope.registerFunction(key.getID(), function)); // Register registry functions.
scope scope
@@ -8,6 +8,7 @@
package structure; package structure;
import com.dfsek.terra.addons.terrascript.lexer.Lexer;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder; import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -32,8 +33,8 @@ import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public class ParserTest { public class ParserTest {
@Test @Test
public void parse() throws IOException, ParseException { public void parse() throws IOException, ParseException {
Parser parser = new Parser( Lexer lexer = new Lexer(IOUtils.toString(Objects.requireNonNull(getClass().getResourceAsStream("/test.tesf")), Charset.defaultCharset()));
IOUtils.toString(Objects.requireNonNull(getClass().getResourceAsStream("/test.tesf")), Charset.defaultCharset())); Parser parser = new Parser(lexer);
ScopeBuilder scope = new ScopeBuilder(); ScopeBuilder scope = new ScopeBuilder();
scope.registerFunction("test", new FunctionBuilder<Test1>() { scope.registerFunction("test", new FunctionBuilder<Test1>() {