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.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<Token> tokens, List<Token> functionAndArguments) throws ParseException {
private Keyword<?> parseKeyword(List<Token> 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<Token> tokens) throws ParseException {
private Executable<?> parseExpression(List<Token> 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<Token> tokens) throws ParseException {
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) {
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<String> 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());
}
}

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.platform.world.Chunk;
public class ConstantExpression implements Expression<Object> {
public class ConstantExpression implements Executable<Object> {
private final Object constant;
public ConstantExpression(Object constant) {

View File

@@ -1,4 +1,4 @@
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;
public interface Function<T> extends Expression<T> {
public interface Function<T> extends Executable<T> {
String name();
}

View File

@@ -1,4 +1,4 @@
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
*/
IDENTIFIER,
/**
* Language keyword
*/
KEYWORD,
/**
* Numeric literal
*/

View File

@@ -11,8 +11,8 @@ import java.util.Set;
public class Tokenizer {
private final Lookahead reader;
private final Set<Character> syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', // Currently used chars
'{', '}'); // Reserved chars
private final Set<Character> syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', '{', '}', '+'); // Reserved chars
private final Set<String> 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() {

View File

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