parse things in a less dumb way

This commit is contained in:
dfsek
2020-12-20 14:04:33 -07:00
parent e1cb46c8fd
commit 7cbf8dffbe
10 changed files with 89 additions and 34 deletions

View File

@@ -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.exceptions.ParseException;
import com.dfsek.terra.api.structures.parser.lang.Block; 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.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.Function;
import com.dfsek.terra.api.structures.parser.lang.Item; 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.Keyword;
import com.dfsek.terra.api.structures.parser.lang.Statement; 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.keywords.IfKeyword;
import com.dfsek.terra.api.structures.parser.lang.statements.EqualsStatement; 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.Token;
import com.dfsek.terra.api.structures.tokenizer.Tokenizer; import com.dfsek.terra.api.structures.tokenizer.Tokenizer;
import com.dfsek.terra.api.structures.tokenizer.exceptions.TokenizerException; import com.dfsek.terra.api.structures.tokenizer.exceptions.TokenizerException;
import com.dfsek.terra.api.util.GlueList; import com.dfsek.terra.api.util.GlueList;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -60,37 +62,41 @@ public class Parser {
} }
private Keyword<?> parseKeyword(List<Token> tokens, List<Token> functionAndArguments) throws ParseException { private Keyword<?> parseKeyword(List<Token> tokens) throws ParseException {
Token identifier = functionAndArguments.remove(0); Token identifier = tokens.remove(0);
checkType(identifier, Token.Type.IDENTIFIER); checkType(identifier, Token.Type.KEYWORD);
if(!keywords.contains(identifier.getContent())) if(!keywords.contains(identifier.getContent()))
throw new ParseException("No such keyword " + identifier.getContent() + ": " + identifier.getStart()); throw new ParseException("No such keyword " + identifier.getContent() + ": " + identifier.getStart());
Keyword<?> k = null; Keyword<?> k = null;
if(identifier.getContent().equals("if")) { 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; Statement statement = null;
Token comparator = functionAndArguments.remove(0); Token comparator = tokens.remove(0);
checkType(comparator, Token.Type.BOOLEAN_OPERATOR); 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("==")) { if(comparator.getContent().equals("==")) {
statement = new EqualsStatement(left, right); 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); k = new IfKeyword(parseBlock(tokens), statement);
} }
return k; return k;
} }
private Expression<?> parseExpression(List<Token> tokens) throws ParseException { private Executable<?> parseExpression(List<Token> tokens) throws ParseException {
if(tokens.get(0).isConstant()) { if(tokens.get(0).isConstant()) {
return new ConstantExpression(tokens.remove(0).getContent()); return new ConstantExpression(tokens.remove(0).getContent());
} else return parseFunction(tokens, false); } else return parseFunction(tokens, false);
@@ -98,18 +104,25 @@ public class Parser {
private Block parseBlock(List<Token> tokens) throws ParseException { private Block parseBlock(List<Token> tokens) throws ParseException {
List<Item<?>> parsedItems = new GlueList<>(); List<Item<?>> parsedItems = new GlueList<>();
List<Token> functionArgs = new GlueList<>(); checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.KEYWORD);
main:
while(tokens.size() > 0) { while(tokens.size() > 0) {
Token token = tokens.remove(0); Token token = tokens.get(0);
if(token.getType().equals(Token.Type.BLOCK_END)) break; checkType(token, Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.BLOCK_END);
functionArgs.add(token); switch(token.getType()) {
if(token.getType().equals(Token.Type.STATEMENT_END)) { case KEYWORD:
parsedItems.add(parseFunction(functionArgs, true)); parsedItems.add(parseKeyword(tokens));
functionArgs.clear(); if(tokens.isEmpty()) break;
} else if(token.getType().equals(Token.Type.BLOCK_BEGIN)) { checkType(tokens.get(0), Token.Type.IDENTIFIER, Token.Type.KEYWORD, Token.Type.BLOCK_END);
parsedItems.add(parseKeyword(tokens, functionArgs)); break;
functionArgs.clear(); 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); return new Block(parsedItems);
@@ -129,7 +142,7 @@ public class Parser {
functionAndArguments.remove(0); // Remove body end 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<String> arg = args.stream().map(Token::getContent).collect(Collectors.toList()); List<String> arg = args.stream().map(Token::getContent).collect(Collectors.toList());
@@ -158,8 +171,8 @@ public class Parser {
return args; return args;
} }
private void checkType(Token token, Token.Type expected) throws ParseException { private void checkType(Token token, Token.Type... expected) throws ParseException {
if(!token.getType().equals(expected)) for(Token.Type type : expected) if(token.getType().equals(type)) return;
throw new ParseException("Expected " + expected + " but found " + token.getType() + ": " + token.getStart()); throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType() + ": " + token.getStart());
} }
} }

View File

@@ -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.math.vector.Location;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
public class ConstantExpression implements Expression<Object> { public class ConstantExpression implements Executable<Object> {
private final Object constant; private final Object constant;
public ConstantExpression(Object constant) { public ConstantExpression(Object constant) {

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.api.structures.parser.lang; package com.dfsek.terra.api.structures.parser.lang;
public interface Expression<T> extends Item<T> { public interface Executable<T> extends Item<T> {
} }

View File

@@ -1,6 +1,5 @@
package com.dfsek.terra.api.structures.parser.lang; package com.dfsek.terra.api.structures.parser.lang;
public interface Function<T> extends Expression<T> { public interface Function<T> extends Executable<T> {
String name(); String name();
} }

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.api.structures.parser.lang; package com.dfsek.terra.api.structures.parser.lang;
public interface Keyword<T> extends Expression<T> { public interface Keyword<T> extends Executable<T> {
} }

View File

@@ -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));
}
}

View File

@@ -37,6 +37,11 @@ public class Token {
* Function identifier or language keyword * Function identifier or language keyword
*/ */
IDENTIFIER, IDENTIFIER,
/**
* Language keyword
*/
KEYWORD,
/** /**
* Numeric literal * Numeric literal
*/ */

View File

@@ -11,8 +11,8 @@ import java.util.Set;
public class Tokenizer { public class Tokenizer {
private final Lookahead reader; private final Lookahead reader;
private final Set<Character> syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', // Currently used chars private final Set<Character> syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', '{', '}', '+'); // Reserved chars
'{', '}'); // Reserved chars private final Set<String> keywords = Sets.newHashSet("if", "return");
public Tokenizer(String data) { public Tokenizer(String data) {
@@ -38,6 +38,8 @@ public class Tokenizer {
return new Token("false", Token.Type.BOOLEAN, new Position(reader.getLine(), reader.getIndex())); return new Token("false", Token.Type.BOOLEAN, new Position(reader.getLine(), reader.getIndex()));
if(reader.matches("==", true)) if(reader.matches("==", true))
return new Token("==", Token.Type.BOOLEAN_OPERATOR, new Position(reader.getLine(), reader.getIndex())); 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()) { if(isNumberStart()) {
StringBuilder num = new StringBuilder(); StringBuilder num = new StringBuilder();
@@ -90,7 +92,7 @@ public class Tokenizer {
String tokenString = token.toString(); 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() { private boolean isNumberLike() {

View File

@@ -16,7 +16,7 @@ import java.util.List;
public class ParserTest { public class ParserTest {
@Test @Test
public void parse() throws IOException, ParseException { 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<Test1>() { parser.addFunction("test", new FunctionBuilder<Test1>() {
@Override @Override

View File

@@ -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);
}
}