From 1c579e8e5be273cc15e036a363e8adb5cc16def1 Mon Sep 17 00:00:00 2001 From: dfsek Date: Fri, 25 Dec 2020 12:08:22 -0700 Subject: [PATCH] improve ParseException --- .../terra/api/structures/parser/Parser.java | 25 ++++++++++--------- .../api/structures/parser/ParserUtil.java | 14 +++++------ .../api/structures/parser/TokenHolder.java | 14 ++++++++--- .../parser/exceptions/ParseException.java | 21 ++++++++++------ .../builders/StructureFunctionBuilder.java | 2 +- .../script/functions/BlockFunction.java | 2 +- 6 files changed, 46 insertions(+), 32 deletions(-) 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 2d4f4bf1d..df01dd94f 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 @@ -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) 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) 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) args.get(0), (Returnable) args.get(1)); default: throw new UnsupportedOperationException("Unsupported function: " + identifier.getContent()); diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/ParserUtil.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/ParserUtil.java index a44b7a634..c54169044 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/ParserUtil.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/ParserUtil.java @@ -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()); } } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/TokenHolder.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/TokenHolder.java index 6affc2cb4..b8bb90023 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/TokenHolder.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/TokenHolder.java @@ -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 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 getTokens() { diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/exceptions/ParseException.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/exceptions/ParseException.java index c8e3794d6..df833972f 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/exceptions/ParseException.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/exceptions/ParseException.java @@ -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; } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StructureFunctionBuilder.java b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StructureFunctionBuilder.java index 044bd97d6..e61a3527b 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StructureFunctionBuilder.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StructureFunctionBuilder.java @@ -23,7 +23,7 @@ public class StructureFunctionBuilder implements FunctionBuilder> 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) argumentList.remove(0), (Returnable) argumentList.remove(0), (Returnable) argumentList.remove(0), (Returnable) argumentList.remove(0), argumentList.stream().map(item -> ((Returnable) item)).collect(Collectors.toList()), registry, position, main); diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/BlockFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/BlockFunction.java index 60f2f6c11..8f4b1541e 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/BlockFunction.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/BlockFunction.java @@ -22,7 +22,7 @@ public class BlockFunction implements Function { public BlockFunction(Returnable x, Returnable y, Returnable z, Returnable 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) data).getConstant()); this.x = x;