From 700f4bbde508a27dbc89fcc598c27d2055c04458 Mon Sep 17 00:00:00 2001 From: uogau Date: Fri, 2 Jul 2021 16:10:49 +0200 Subject: [PATCH 1/2] Error messages for Type Assumptions now show up correctly --- .../typicalc/model/parser/TypeAssumptionParser.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java b/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java index 22bf1c0..5231c28 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java +++ b/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java @@ -17,6 +17,7 @@ import java.util.regex.Pattern; public class TypeAssumptionParser { public static final Pattern TYPE_NAME_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9]*"); + public static final Pattern NEGATED_TYPE_NAME_PATTERN = Pattern.compile("(?![a-zA-Z][a-zA-Z0-9]*)"); private static final Pattern TYPE_VARIABLE_PATTERN = Pattern.compile("t(\\d+)"); private static final Set END_TOKENS @@ -33,7 +34,11 @@ public class TypeAssumptionParser { for (Map.Entry entry : assumptions.entrySet()) { String typeName = entry.getKey(); if (!TYPE_NAME_PATTERN.matcher(typeName).matches()) { - return new Result<>(null, ParseError.UNEXPECTED_CHARACTER); + Matcher matcher = NEGATED_TYPE_NAME_PATTERN.matcher(typeName); + if (matcher.find()) { + return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter( + typeName.charAt(matcher.start()), matcher.start(), typeName)); + } } VarTerm var = new VarTerm(typeName); Result typeAbs = parseTypeDefinition(entry.getValue()); @@ -133,7 +138,7 @@ public class TypeAssumptionParser { case ARROW: if (type == null) { // there was no type in front of the arrow - return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, "")); + return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, lexer.getTerm())); } // recursive call, keep open parentheses count Result, ParseError> nextType = parseType(lexer, parenCount); @@ -144,7 +149,7 @@ public class TypeAssumptionParser { case EOF: break; default: - return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, "")); + return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, lexer.getTerm())); } // update type based on Result if (typeResult != null && typeResult.isError()) { @@ -158,7 +163,7 @@ public class TypeAssumptionParser { } if (parenCount - removedParens < 0) { // too many closing parenthesis - return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, "")); + return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, lexer.getTerm())); } else if (END_TOKENS.contains(t.getType())) { // potential end of type if (parenCount - removedParens == 0) { From 6f2c6b3481d3c5e2b0bf81928073c2dd4c00e233 Mon Sep 17 00:00:00 2001 From: uogau Date: Fri, 2 Jul 2021 16:46:01 +0200 Subject: [PATCH 2/2] Error messages now show whether the error is in the Term or the Type Assumptions --- .../typicalc/model/parser/LambdaLexer.java | 7 ++-- .../typicalc/model/parser/LambdaParser.java | 8 +++-- .../kit/typicalc/model/parser/ParseError.java | 35 +++++++++++++++++-- .../model/parser/TypeAssumptionParser.java | 23 +++++++----- .../view/content/errorcontent/ErrorView.java | 29 +++++++++------ .../language/translation_de.properties | 7 ++-- .../language/translation_en.properties | 7 ++-- .../parser/TypeAssumptionParserTest.java | 12 ++++--- 8 files changed, 92 insertions(+), 36 deletions(-) diff --git a/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java b/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java index c9710b1..76bd25c 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java +++ b/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java @@ -90,7 +90,7 @@ public class LambdaLexer { return new Result<>(t); } else { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER - .withCharacter(term.charAt(pos + 1), pos + 1, term)); + .withCharacter(term.charAt(pos + 1), pos + 1, term, ParseError.ErrorType.TERM_ERROR)); } } else { return new Result<>(null, ParseError.TOO_FEW_TOKENS); // actually too few *characters* .. @@ -135,7 +135,7 @@ public class LambdaLexer { && (int) term.charAt(pos) < 128); if (pos < term.length() && (int) term.charAt(pos) >= 128) { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER - .withCharacter(term.charAt(pos), pos, term)); + .withCharacter(term.charAt(pos), pos, term, ParseError.ErrorType.TERM_ERROR)); } String s = sb.toString(); TokenType type; @@ -167,7 +167,8 @@ public class LambdaLexer { } while (pos < term.length() && Character.isDigit(term.charAt(pos))); return new Result<>(new Token(TokenType.NUMBER, sb.toString(), startPos)); } else { - return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter(c, pos, term)); + return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter(c, pos, term, + ParseError.ErrorType.TERM_ERROR)); } } diff --git a/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java b/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java index 4669f25..6977542 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java +++ b/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java @@ -69,7 +69,8 @@ public class LambdaParser { TokenType current = token.getType(); Optional error = nextToken(); if (current != type) { - return Optional.of(ParseError.UNEXPECTED_TOKEN.withToken(lastToken, lexer.getTerm()).expectedType(type)); + return Optional.of(ParseError.UNEXPECTED_TOKEN.withToken(lastToken, lexer.getTerm(), + ParseError.ErrorType.TERM_ERROR).expectedType(type)); } return error; } @@ -91,7 +92,7 @@ public class LambdaParser { } return new Result<>(null, (last.getType() == TokenType.EOF ? ParseError.TOO_FEW_TOKENS : ParseError.UNEXPECTED_TOKEN) - .withToken(last, lexer.getTerm()) + .withToken(last, lexer.getTerm(), ParseError.ErrorType.TERM_ERROR) .expectedTypes(ATOM_START_TOKENS)); } @@ -207,7 +208,8 @@ public class LambdaParser { try { n = Integer.parseInt(number); } catch (NumberFormatException e) { - return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken(token, lexer.getTerm())); + return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken( + token, lexer.getTerm(), ParseError.ErrorType.TERM_ERROR)); } error = nextToken(); if (error.isEmpty()) { diff --git a/src/main/java/edu/kit/typicalc/model/parser/ParseError.java b/src/main/java/edu/kit/typicalc/model/parser/ParseError.java index 0678874..a27fba9 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/ParseError.java +++ b/src/main/java/edu/kit/typicalc/model/parser/ParseError.java @@ -27,12 +27,30 @@ public enum ParseError { */ UNEXPECTED_CHARACTER; + public enum ErrorType { + /** + * This error was created when parsing the input term + */ + TERM_ERROR, + + /** + * This error was created when parsing the type assumptions + */ + TYPE_ASSUMPTION_ERROR, + + /** + * initial error type + */ + INITIAL_ERROR + } + private Optional cause = Optional.empty(); private Optional> needed = Optional.empty(); private String term = ""; private char wrongChar = '\0'; private char correctChar = '\0'; private int position = -1; + private ErrorType errorType = ErrorType.INITIAL_ERROR; /** * Attach a token to this error. @@ -41,9 +59,10 @@ public enum ParseError { * @param term the term that is parsed * @return this object */ - public ParseError withToken(Token cause, String term) { + public ParseError withToken(Token cause, String term, ErrorType errorType) { this.cause = Optional.of(cause); this.term = term; + this.errorType = errorType; return this; } @@ -88,10 +107,11 @@ public enum ParseError { * @param term the term that is parsed * @return this object */ - public ParseError withCharacter(char cause, int position, String term) { + public ParseError withCharacter(char cause, int position, String term, ErrorType errorType) { this.wrongChar = cause; this.position = position; this.term = term; + this.errorType = errorType; return this; } @@ -137,6 +157,17 @@ public enum ParseError { return term; } + /** + * @return the error type + */ + public ErrorType getErrorType() { + return errorType; + } + + protected void setErrorType(ErrorType errorType) { + this.errorType = errorType; + } + ParseError() { } diff --git a/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java b/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java index 5231c28..b9a1f80 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java +++ b/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java @@ -37,7 +37,8 @@ public class TypeAssumptionParser { Matcher matcher = NEGATED_TYPE_NAME_PATTERN.matcher(typeName); if (matcher.find()) { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter( - typeName.charAt(matcher.start()), matcher.start(), typeName)); + typeName.charAt(matcher.start()), matcher.start(), typeName, + ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); } } VarTerm var = new VarTerm(typeName); @@ -64,20 +65,22 @@ public class TypeAssumptionParser { int colonIndex = definition.indexOf(':'); if (colonIndex >= 0) { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter( - ':', colonIndex, definition).expectedCharacter('.') + ':', colonIndex, definition, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) + .expectedCharacter('.') ); } return new Result<>(null, ParseError.TOO_FEW_TOKENS.expectedType(TokenType.DOT)); } else if (parts.length > 2) { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter( - '.', parts[0].length() + 1 + parts[1].length(), definition)); + '.', parts[0].length() + 1 + parts[1].length(), + definition, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); } for (String quantified : parts[0].substring(1).split(",")) { quantified = quantified.trim(); if (!quantified.matches("t\\d+")) { return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken( - new Token(TokenType.VARIABLE, quantified, parts[0].indexOf(quantified)), parts[0] - )); + new Token(TokenType.VARIABLE, quantified, parts[0].indexOf(quantified)), + parts[0], ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); } int i = Integer.parseInt(quantified.substring(1)); allQuantified.add(new TypeVariable(TypeVariableKind.USER_INPUT, i)); @@ -117,6 +120,7 @@ public class TypeAssumptionParser { while (true) { Result token = lexer.nextToken(); if (token.isError()) { + token.unwrapError().setErrorType(ParseError.ErrorType.TYPE_ASSUMPTION_ERROR); return new Result<>(token); } Token t = token.unwrap(); @@ -138,7 +142,8 @@ public class TypeAssumptionParser { case ARROW: if (type == null) { // there was no type in front of the arrow - return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, lexer.getTerm())); + return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken( + t, lexer.getTerm(), ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); } // recursive call, keep open parentheses count Result, ParseError> nextType = parseType(lexer, parenCount); @@ -149,7 +154,8 @@ public class TypeAssumptionParser { case EOF: break; default: - return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, lexer.getTerm())); + return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken( + t, lexer.getTerm(), ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); } // update type based on Result if (typeResult != null && typeResult.isError()) { @@ -163,7 +169,8 @@ public class TypeAssumptionParser { } if (parenCount - removedParens < 0) { // too many closing parenthesis - return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken(t, lexer.getTerm())); + return new Result<>(null, ParseError.UNEXPECTED_TOKEN.withToken( + t, lexer.getTerm(), ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); } else if (END_TOKENS.contains(t.getType())) { // potential end of type if (parenCount - removedParens == 0) { diff --git a/src/main/java/edu/kit/typicalc/view/content/errorcontent/ErrorView.java b/src/main/java/edu/kit/typicalc/view/content/errorcontent/ErrorView.java index 7ca9f89..9a31116 100644 --- a/src/main/java/edu/kit/typicalc/view/content/errorcontent/ErrorView.java +++ b/src/main/java/edu/kit/typicalc/view/content/errorcontent/ErrorView.java @@ -53,27 +53,36 @@ public class ErrorView extends VerticalLayout implements LocaleChangeObserver { Paragraph summary = new Paragraph(getTranslation("root." + error.toString())); summary.setId(ERROR_SUMMARY_ID); String term = error.getTerm(); - + String descriptionForError; + ParseError.ErrorType errorType = error.getErrorType(); + if (errorType == ParseError.ErrorType.TERM_ERROR) { + descriptionForError = "error.termForError"; + } else if (errorType == ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) { + descriptionForError = "error.typeAssumptionForError"; + } else { + //should never happen + descriptionForError = "error"; + } switch (error) { case TOO_FEW_TOKENS: - additionalInformation.add(new Span(getTranslation("root.tooFewTokensHelp"))); + additionalInformation.add(new Span(getTranslation("error.tooFewTokensHelp"))); break; case UNEXPECTED_TOKEN: Optional cause = error.getCause(); if (cause.isPresent()) { - additionalInformation.add(new Span(new Pre(getTranslation("root.termForError") + term - + "\n" + " ".repeat(Math.max(getTranslation("root.termForError").length(), - cause.get().getPos() + getTranslation("root.termForError").length())) - + "^ " + getTranslation("root.wrongCharacter") + cause.get().getText()))); + additionalInformation.add(new Span(new Pre(getTranslation(descriptionForError) + term + + "\n" + " ".repeat(Math.max(getTranslation(descriptionForError).length(), + cause.get().getPos() + getTranslation(descriptionForError).length())) + + "^ " + getTranslation("error.wrongCharacter") + cause.get().getText()))); } break; case UNEXPECTED_CHARACTER: char c = error.getWrongCharacter(); if (c != '\0') { - additionalInformation.add(new Span(new Pre(getTranslation("root.termForError") + term - + "\n" + " ".repeat(Math.max(getTranslation("root.termForError").length(), - error.getPosition() + getTranslation("root.termForError").length())) - + "^ " + getTranslation("root.wrongCharacter") + c))); + additionalInformation.add(new Span(new Pre(getTranslation(descriptionForError) + term + + "\n" + " ".repeat(Math.max(getTranslation(descriptionForError).length(), + error.getPosition() + getTranslation(descriptionForError).length())) + + "^ " + getTranslation("error.wrongCharacter") + c))); } else { return summary; } diff --git a/src/main/resources/language/translation_de.properties b/src/main/resources/language/translation_de.properties index bd4095c..e6c026a 100644 --- a/src/main/resources/language/translation_de.properties +++ b/src/main/resources/language/translation_de.properties @@ -101,12 +101,13 @@ help.typicalcInfo=\ Johanna Stuber
root.TOO_FEW_TOKENS=Falsche Eingabe! Die Eingabe endet abrupt. -root.tooFewTokensHelp=Überprüfe, ob alle Let-, Abs- und App-Terme über die nötigen Argumente verfügen. +error.tooFewTokensHelp=Überprüfe, ob alle Let-, Abs- und App-Terme über die nötigen Argumente verfügen. root.UNEXPECTED_TOKEN=Die Eingabe entspricht nicht der im Info-Dialog spezifizierten Syntax! root.UNEXPECTED_CHARACTER=Die Eingabe enthält ein Zeichen, welches an dieser Stelle nicht erlaubt ist! error.heading=Syntaktisch falsche Eingabe! -root.termForError=Term:\u0020 -root.wrongCharacter=Falsches Zeichen:\u0020 +error.termForError=Term:\u0020 +error.typeAssumptionForError=Typannahme:\u0020 +error.wrongCharacter=Falsches Zeichen:\u0020 error.expectedToken=Erwartet: {0} error.hint=Die Grammatiken, die die korrekte Syntax eines Terms und der Typannahmen beschreiben, \ sind auch über das Info-Symbol zu erreichen. diff --git a/src/main/resources/language/translation_en.properties b/src/main/resources/language/translation_en.properties index 1f5d5ec..1ac7d48 100644 --- a/src/main/resources/language/translation_en.properties +++ b/src/main/resources/language/translation_en.properties @@ -94,12 +94,13 @@ help.typicalcInfo=\ Johanna Stuber root.TOO_FEW_TOKENS=Wrong input! The term ends abruptly. -root.tooFewTokensHelp=Check if all let, abstraction and application terms consist of the required arguments. +error.tooFewTokensHelp=Check if all let, abstraction and application terms consist of the required arguments. root.UNEXPECTED_TOKEN=The input does not match the syntax specified in the info dialog. root.UNEXPECTED_CHARACTER=The input contains a character which is not allowed at this position. error.heading=Input is syntactically wrong! -root.wrongCharacter=Wrong character:\u0020 -root.termForError=Term:\u0020 +error.wrongCharacter=Wrong character:\u0020 +error.termForError=Term:\u0020 +error.typeAssumptionForError=Type Assumption:\u0020 error.expectedToken=Expected: {0} error.hint=The grammars describing the correct syntax of a term or a type assumption can also be reached \ via the info icon. diff --git a/src/test/java/edu/kit/typicalc/model/parser/TypeAssumptionParserTest.java b/src/test/java/edu/kit/typicalc/model/parser/TypeAssumptionParserTest.java index 7bcac09..94679dc 100644 --- a/src/test/java/edu/kit/typicalc/model/parser/TypeAssumptionParserTest.java +++ b/src/test/java/edu/kit/typicalc/model/parser/TypeAssumptionParserTest.java @@ -233,11 +233,15 @@ class TypeAssumptionParserTest { tests.put("", ParseError.TOO_FEW_TOKENS); tests.put("ö", ParseError.UNEXPECTED_CHARACTER); tests.put("(x", ParseError.TOO_FEW_TOKENS); - tests.put("-> x", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.ARROW, "->", 0), "")); - tests.put("x 11", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.NUMBER, "11", 2), "")); - tests.put("x )", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", 2), "")); + tests.put("-> x", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.ARROW, "->", 0), + "", ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); + tests.put("x 11", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.NUMBER, "11", 2), + "", ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); + tests.put("x )", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", + 2), "", ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); tests.put("x -> (x) )", ParseError.UNEXPECTED_TOKEN - .withToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", 9), "")); + .withToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", 9), "", + ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); for (Map.Entry entry : tests.entrySet()) { TypeAssumptionParser parser = new TypeAssumptionParser(); Result, ParseError> type = parser.parse(Map.of("type1", entry.getKey()));