improve ParseException

This commit is contained in:
dfsek
2020-12-25 12:08:22 -07:00
parent bbab6e39ff
commit 1c579e8e5b
6 changed files with 46 additions and 32 deletions

View File

@@ -87,7 +87,7 @@ public class Parser {
try {
while(tokenizer.hasNext()) tokens.add(tokenizer.fetch());
} catch(TokenizerException e) {
throw new ParseException("Failed to tokenize input", e);
throw new ParseException("Failed to tokenize input", new Position(0, 0), e);
}
// Parse ID
@@ -102,9 +102,10 @@ public class Parser {
for(Token t : tokens.getTokens()) {
if(t.getType().equals(Token.Type.BLOCK_BEGIN)) blockLevel++;
else if(t.getType().equals(Token.Type.BLOCK_END)) blockLevel--;
if(blockLevel < 0) throw new ParseException("Dangling closing brace: " + t.getPosition());
if(blockLevel < 0) throw new ParseException("Dangling closing brace", t.getPosition());
}
if(blockLevel != 0) throw new ParseException("Dangling opening brace");
if(blockLevel != 0)
throw new ParseException("Dangling opening brace", tokens.getTokens().get(tokens.getTokens().size() - 1).getPosition());
return parseBlock(tokens, new HashMap<>());
}
@@ -129,7 +130,7 @@ public class Parser {
Token name = tokens.get();
if(functions.containsKey(name.getContent()) || variableMap.containsKey(name.getContent()) || builtinFunctions.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 = parseAssignment(forVar, tokens, variableMap);
variableMap.put(name.getContent(), forVar);
@@ -190,7 +191,7 @@ public class Parser {
else if(variableMap.containsKey(id.getContent())) {
ParserUtil.checkType(tokens.consume(), Token.Type.IDENTIFIER);
expression = new Getter(variableMap.get(id.getContent()));
} else throw new ParseException("Unexpected token: " + id.getContent() + " at " + id.getPosition());
} else throw new ParseException("Unexpected token \" " + id.getContent() + "\"", id.getPosition());
}
if(booleanInverted) { // Invert operation if boolean not detected
@@ -323,7 +324,7 @@ public class Parser {
ParserUtil.checkType(name, Token.Type.IDENTIFIER); // Name must be an identifier.
if(functions.containsKey(name.getContent()) || parsedVariables.containsKey(name.getContent()) || builtinFunctions.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());
parsedVariables.put(name.getContent(), temp);
@@ -361,7 +362,7 @@ public class Parser {
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); // First token must be identifier
if(!functions.containsKey(identifier.getContent()) && !builtinFunctions.contains(identifier.getContent()))
throw new ParseException("No such function " + identifier.getContent() + ": " + identifier.getPosition());
throw new ParseException("No such function \"" + identifier.getContent() + "\"", identifier.getPosition());
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); // Second is body begin
@@ -376,12 +377,12 @@ public class Parser {
FunctionBuilder<?> builder = functions.get(identifier.getContent());
if(builder.argNumber() != -1 && args.size() != builder.argNumber())
throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size() + ": " + identifier.getPosition());
throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition());
for(int i = 0; i < args.size(); i++) {
Returnable<?> argument = args.get(i);
if(builder.getArgument(i) == null)
throw new ParseException("Unexpected argument at position " + i + " in function " + identifier.getContent() + ": " + identifier.getPosition());
throw new ParseException("Unexpected argument at position " + i + " in function " + identifier.getContent(), identifier.getPosition());
ParserUtil.checkReturnType(argument, builder.getArgument(i));
}
return builder.build(args, identifier.getPosition());
@@ -390,18 +391,18 @@ public class Parser {
case "abs":
ParserUtil.checkReturnType(args.get(0), Returnable.ReturnType.NUMBER);
if(args.size() != 1)
throw new ParseException("Expected 1 arguments; found " + args.size() + ": " + identifier.getPosition());
throw new ParseException("Expected 1 argument; found " + args.size(), identifier.getPosition());
return new AbsFunction(identifier.getPosition(), (Returnable<Number>) args.get(0));
case "sqrt":
ParserUtil.checkReturnType(args.get(0), Returnable.ReturnType.NUMBER);
if(args.size() != 1)
throw new ParseException("Expected 1 arguments; found " + args.size() + ": " + identifier.getPosition());
throw new ParseException("Expected 1 argument; found " + args.size(), identifier.getPosition());
return new SqrtFunction(identifier.getPosition(), (Returnable<Number>) args.get(0));
case "pow":
ParserUtil.checkReturnType(args.get(0), Returnable.ReturnType.NUMBER);
ParserUtil.checkReturnType(args.get(1), Returnable.ReturnType.NUMBER);
if(args.size() != 2)
throw new ParseException("Expected 1 arguments; found " + args.size() + ": " + identifier.getPosition());
throw new ParseException("Expected 1 argument; found " + args.size(), identifier.getPosition());
return new PowFunction(identifier.getPosition(), (Returnable<Number>) args.get(0), (Returnable<Number>) args.get(1));
default:
throw new UnsupportedOperationException("Unsupported function: " + identifier.getContent());

View File

@@ -9,23 +9,23 @@ import java.util.Arrays;
public class ParserUtil {
public static 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.getPosition());
throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType(), token.getPosition());
}
public static void checkReturnType(Returnable<?> returnable, Returnable.ReturnType... types) throws ParseException {
for(Returnable.ReturnType type : types) if(returnable.returnType().equals(type)) return;
throw new ParseException("Expected " + Arrays.toString(types) + " but found " + returnable.returnType() + ": " + returnable.getPosition());
throw new ParseException("Expected " + Arrays.toString(types) + " but found " + returnable.returnType(), returnable.getPosition());
}
public static void checkArithmeticOperation(Returnable<?> left, Returnable<?> right, Token operation) throws ParseException {
if(!left.returnType().equals(Returnable.ReturnType.NUMBER) || !right.returnType().equals(Returnable.ReturnType.NUMBER)) {
throw new ParseException("Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType() + ": " + operation.getPosition());
throw new ParseException("Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType(), operation.getPosition());
}
}
public static void checkBooleanOperation(Returnable<?> left, Returnable<?> right, Token operation) throws ParseException {
if(!left.returnType().equals(Returnable.ReturnType.BOOLEAN) || !right.returnType().equals(Returnable.ReturnType.BOOLEAN)) {
throw new ParseException("Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType() + ": " + operation.getPosition());
throw new ParseException("Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType(), operation.getPosition());
}
}
@@ -33,7 +33,7 @@ public class ParserUtil {
if(returnType.equals(Returnable.ReturnType.STRING) && token.getType().equals(Token.Type.STRING_VARIABLE)) return;
if(returnType.equals(Returnable.ReturnType.NUMBER) && token.getType().equals(Token.Type.NUMBER_VARIABLE)) return;
if(returnType.equals(Returnable.ReturnType.BOOLEAN) && token.getType().equals(Token.Type.BOOLEAN_VARIABLE)) return;
throw new ParseException("Type mismatch, cannot convert from " + returnType + " to " + token.getType() + ": " + token.getPosition());
throw new ParseException("Type mismatch, cannot convert from " + returnType + " to " + token.getType(), token.getPosition());
}
/**
@@ -44,7 +44,7 @@ public class ParserUtil {
*/
public static void checkBinaryOperator(Token token) throws ParseException {
if(!token.isBinaryOperator())
throw new ParseException("Expected binary operator, found " + token.getType() + ": " + token.getPosition());
throw new ParseException("Expected binary operator, found " + token.getType(), token.getPosition());
}
public static Returnable.ReturnType getVariableReturnType(Token varToken) throws ParseException {
@@ -56,7 +56,7 @@ public class ParserUtil {
case BOOLEAN_VARIABLE:
return Returnable.ReturnType.BOOLEAN;
default:
throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration: " + varToken.getPosition());
throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration", varToken.getPosition());
}
}
}

View File

@@ -1,6 +1,7 @@
package com.dfsek.terra.api.structures.parser;
import com.dfsek.terra.api.structures.parser.exceptions.ParseException;
import com.dfsek.terra.api.structures.tokenizer.Position;
import com.dfsek.terra.api.structures.tokenizer.Token;
import com.dfsek.terra.api.util.GlueList;
@@ -11,6 +12,7 @@ import java.util.List;
*/
public class TokenHolder {
private final List<Token> tokens = new GlueList<>();
private Position last;
/**
* Add a token to the top of the stack.
@@ -28,8 +30,10 @@ public class TokenHolder {
* @throws ParseException If stack is empty
*/
public Token get() throws ParseException {
if(!hasNext()) throw new ParseException("Unexpected end of input");
return tokens.get(0);
if(!hasNext()) throw new ParseException("Unexpected end of input", last);
Token token = tokens.get(0);
last = token.getPosition();
return token;
}
/**
@@ -39,8 +43,10 @@ public class TokenHolder {
* @throws ParseException If stack is empty
*/
public Token consume() throws ParseException {
if(!hasNext()) throw new ParseException("Unexpected end of input");
return tokens.remove(0);
if(!hasNext()) throw new ParseException("Unexpected end of input", last);
Token token = tokens.remove(0);
last = token.getPosition();
return token;
}
public List<Token> getTokens() {

View File

@@ -1,19 +1,26 @@
package com.dfsek.terra.api.structures.parser.exceptions;
import com.dfsek.terra.api.structures.tokenizer.Position;
public class ParseException extends Exception {
public ParseException() {
super();
}
private final Position position;
public ParseException(String message) {
public ParseException(String message, Position position) {
super(message);
this.position = position;
}
public ParseException(String message, Throwable cause) {
public ParseException(String message, Position position, Throwable cause) {
super(message, cause);
this.position = position;
}
public ParseException(Throwable cause) {
super(cause);
@Override
public String getMessage() {
return super.getMessage() + ": " + position;
}
public Position getPosition() {
return position;
}
}

View File

@@ -23,7 +23,7 @@ public class StructureFunctionBuilder implements FunctionBuilder<StructureFuncti
@SuppressWarnings("unchecked")
@Override
public StructureFunction build(List<Returnable<?>> argumentList, Position position) throws ParseException {
if(argumentList.size() < 5) throw new ParseException("Expected rotations: " + position);
if(argumentList.size() < 5) throw new ParseException("Expected rotations", position);
return new StructureFunction((Returnable<Number>) argumentList.remove(0), (Returnable<Number>) argumentList.remove(0), (Returnable<Number>) argumentList.remove(0), (Returnable<String>) argumentList.remove(0),
argumentList.stream().map(item -> ((Returnable<String>) item)).collect(Collectors.toList()), registry, position, main);

View File

@@ -22,7 +22,7 @@ public class BlockFunction implements Function<Void> {
public BlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, TerraPlugin main, Position position) throws ParseException {
this.position = position;
if(!(data instanceof ConstantExpression)) throw new ParseException("Block data must be constant.");
if(!(data instanceof ConstantExpression)) throw new ParseException("Block data must be constant", data.getPosition());
this.data = main.getWorldHandle().createBlockData(((ConstantExpression<String>) data).getConstant());
this.x = x;