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 9df204285..5751b1533 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 @@ -3,19 +3,21 @@ package com.dfsek.terra.api.structures.parser; import com.dfsek.terra.api.structures.parser.exceptions.ParseException; import com.dfsek.terra.api.structures.parser.lang.Block; import com.dfsek.terra.api.structures.parser.lang.ConstantExpression; -import com.dfsek.terra.api.structures.parser.lang.Expression; +import com.dfsek.terra.api.structures.parser.lang.Executable; import com.dfsek.terra.api.structures.parser.lang.Function; 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.Statement; import com.dfsek.terra.api.structures.parser.lang.keywords.IfKeyword; import com.dfsek.terra.api.structures.parser.lang.statements.EqualsStatement; +import com.dfsek.terra.api.structures.parser.lang.statements.NotEqualsStatement; 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.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -60,37 +62,41 @@ public class Parser { } - private Keyword parseKeyword(List tokens, List functionAndArguments) throws ParseException { + private Keyword parseKeyword(List tokens) throws ParseException { - Token identifier = functionAndArguments.remove(0); - checkType(identifier, Token.Type.IDENTIFIER); + Token identifier = tokens.remove(0); + checkType(identifier, Token.Type.KEYWORD); if(!keywords.contains(identifier.getContent())) throw new ParseException("No such keyword " + identifier.getContent() + ": " + identifier.getStart()); Keyword k = null; if(identifier.getContent().equals("if")) { - checkType(functionAndArguments.remove(0), Token.Type.BODY_BEGIN); + checkType(tokens.remove(0), Token.Type.BODY_BEGIN); - Expression left = parseExpression(functionAndArguments); + Executable left = parseExpression(tokens); Statement statement = null; - Token comparator = functionAndArguments.remove(0); + Token comparator = tokens.remove(0); checkType(comparator, Token.Type.BOOLEAN_OPERATOR); - Expression right = parseExpression(functionAndArguments); + Executable right = parseExpression(tokens); - checkType(functionAndArguments.remove(0), Token.Type.BODY_END); + checkType(tokens.remove(0), Token.Type.BODY_END); if(comparator.getContent().equals("==")) { statement = new EqualsStatement(left, right); + } else if(comparator.getContent().equals("!=")) { + statement = new NotEqualsStatement(left, right); } + checkType(tokens.remove(0), Token.Type.BLOCK_BEGIN); + k = new IfKeyword(parseBlock(tokens), statement); } return k; } - private Expression parseExpression(List tokens) throws ParseException { + private Executable parseExpression(List tokens) throws ParseException { if(tokens.get(0).isConstant()) { return new ConstantExpression(tokens.remove(0).getContent()); } else return parseFunction(tokens, false); @@ -98,18 +104,25 @@ public class Parser { private Block parseBlock(List tokens) throws ParseException { List> parsedItems = new GlueList<>(); - List functionArgs = new GlueList<>(); - + checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.KEYWORD); + main: while(tokens.size() > 0) { - Token token = tokens.remove(0); - if(token.getType().equals(Token.Type.BLOCK_END)) break; - functionArgs.add(token); - if(token.getType().equals(Token.Type.STATEMENT_END)) { - parsedItems.add(parseFunction(functionArgs, true)); - functionArgs.clear(); - } else if(token.getType().equals(Token.Type.BLOCK_BEGIN)) { - parsedItems.add(parseKeyword(tokens, functionArgs)); - functionArgs.clear(); + Token token = tokens.get(0); + checkType(token, Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.BLOCK_END); + switch(token.getType()) { + case KEYWORD: + parsedItems.add(parseKeyword(tokens)); + if(tokens.isEmpty()) break; + checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.BLOCK_END); + break; + case IDENTIFIER: + parsedItems.add(parseFunction(tokens, true)); + if(tokens.isEmpty()) break; + checkType(tokens.remove(0), Token.Type.STATEMENT_END, Token.Type.BLOCK_END); + break; + case BLOCK_END: + tokens.remove(0); + break main; } } return new Block(parsedItems); @@ -129,7 +142,7 @@ public class Parser { functionAndArguments.remove(0); // Remove body end - if(fullStatement) checkType(functionAndArguments.remove(0), Token.Type.STATEMENT_END); + if(fullStatement) checkType(functionAndArguments.get(0), Token.Type.STATEMENT_END); List arg = args.stream().map(Token::getContent).collect(Collectors.toList()); @@ -158,8 +171,8 @@ public class Parser { return args; } - private void checkType(Token token, Token.Type expected) throws ParseException { - if(!token.getType().equals(expected)) - throw new ParseException("Expected " + expected + " but found " + token.getType() + ": " + token.getStart()); + private void checkType(Token token, Token.Type... expected) throws ParseException { + for(Token.Type type : expected) if(token.getType().equals(type)) return; + throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType() + ": " + token.getStart()); } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/ConstantExpression.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/ConstantExpression.java index fdd9a0f2a..59e25c759 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/ConstantExpression.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/ConstantExpression.java @@ -3,7 +3,7 @@ package com.dfsek.terra.api.structures.parser.lang; import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.platform.world.Chunk; -public class ConstantExpression implements Expression { +public class ConstantExpression implements Executable { private final Object constant; public ConstantExpression(Object constant) { diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Expression.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Executable.java similarity index 52% rename from common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Expression.java rename to common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Executable.java index 8a25fd846..df9ca9665 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Expression.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Executable.java @@ -1,4 +1,4 @@ package com.dfsek.terra.api.structures.parser.lang; -public interface Expression extends Item { +public interface Executable extends Item { } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Function.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Function.java index 884516e4c..293f59e41 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Function.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Function.java @@ -1,6 +1,5 @@ package com.dfsek.terra.api.structures.parser.lang; -public interface Function extends Expression { - +public interface Function extends Executable { String name(); } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Keyword.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Keyword.java index 5644784de..7407c888e 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Keyword.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/Keyword.java @@ -1,4 +1,4 @@ package com.dfsek.terra.api.structures.parser.lang; -public interface Keyword extends Expression { +public interface Keyword extends Executable { } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/statements/NotEqualsStatement.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/statements/NotEqualsStatement.java new file mode 100644 index 000000000..bcb5e8090 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/statements/NotEqualsStatement.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.api.structures.parser.lang.statements; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.structures.parser.lang.Item; +import com.dfsek.terra.api.structures.parser.lang.Statement; + +public class NotEqualsStatement implements Statement { + private final Item left; + private final Item right; + + public NotEqualsStatement(Item left, Item right) { + this.left = left; + this.right = right; + } + + @Override + public Boolean apply(Location location) { + System.out.println(left.apply(location)); + System.out.println(right.apply(location)); + return !left.apply(location).equals(right.apply(location)); + } + + @Override + public Boolean apply(Location location, Chunk chunk) { + return left.apply(location, chunk).equals(right.apply(location, chunk)); + } +} 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 9696943bf..4dfb55eb7 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 @@ -37,6 +37,11 @@ public class Token { * Function identifier or language keyword */ IDENTIFIER, + + /** + * Language keyword + */ + KEYWORD, /** * Numeric literal */ 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 d4a77b5ff..39fb54914 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 @@ -11,8 +11,8 @@ import java.util.Set; public class Tokenizer { private final Lookahead reader; - private final Set syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', // Currently used chars - '{', '}'); // Reserved chars + private final Set syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', '{', '}', '+'); // Reserved chars + private final Set keywords = Sets.newHashSet("if", "return"); public Tokenizer(String data) { @@ -38,6 +38,8 @@ public class Tokenizer { return new Token("false", Token.Type.BOOLEAN, new Position(reader.getLine(), reader.getIndex())); if(reader.matches("==", true)) return new Token("==", Token.Type.BOOLEAN_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches("!=", true)) + return new Token("!=", Token.Type.BOOLEAN_OPERATOR, new Position(reader.getLine(), reader.getIndex())); if(isNumberStart()) { StringBuilder num = new StringBuilder(); @@ -90,7 +92,7 @@ public class Tokenizer { String tokenString = token.toString(); - return new Token(tokenString, Token.Type.IDENTIFIER, new Position(reader.getLine(), reader.getIndex())); + return new Token(tokenString, keywords.contains(tokenString) ? Token.Type.KEYWORD : Token.Type.IDENTIFIER, new Position(reader.getLine(), reader.getIndex())); } private boolean isNumberLike() { diff --git a/common/src/test/java/structure/ParserTest.java b/common/src/test/java/structure/ParserTest.java index 504b3de16..29659085f 100644 --- a/common/src/test/java/structure/ParserTest.java +++ b/common/src/test/java/structure/ParserTest.java @@ -16,7 +16,7 @@ import java.util.List; public class ParserTest { @Test public void parse() throws IOException, ParseException { - Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/target/server/plugins/Terra/test.tesf"))); + Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"))); parser.addFunction("test", new FunctionBuilder() { @Override diff --git a/common/src/test/resources/test.tesf b/common/src/test/resources/test.tesf new file mode 100644 index 000000000..8f82d7331 --- /dev/null +++ b/common/src/test/resources/test.tesf @@ -0,0 +1,8 @@ +test("hello", 2); + +if(test("fsdfsdf", 3) == 2) { + test("fdsgdf", 3.4); + if(test("fsdfsdf", 3) == 2) { + test("fdsgdf", 3.4); + } +} \ No newline at end of file