From 9797d13041d77b79b152d495f2e1e160ecba318d Mon Sep 17 00:00:00 2001 From: uogau Date: Mon, 9 Aug 2021 18:05:45 +0200 Subject: [PATCH] Improved documentation for parser and improved error messages see #15 --- .../model/parser/AdditionalInformation.java | 8 +++++ .../typicalc/model/parser/ExpectedInput.java | 6 +++- .../kit/typicalc/model/parser/ParseError.java | 19 ++++++++++ .../model/parser/TypeAssumptionParser.java | 35 ++++++++++++++++--- .../view/content/errorcontent/ErrorView.java | 10 ++++++ .../language/translation_de.properties | 3 ++ .../language/translation_en.properties | 3 ++ 7 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 src/main/java/edu/kit/typicalc/model/parser/AdditionalInformation.java diff --git a/src/main/java/edu/kit/typicalc/model/parser/AdditionalInformation.java b/src/main/java/edu/kit/typicalc/model/parser/AdditionalInformation.java new file mode 100644 index 0000000..c1df3e1 --- /dev/null +++ b/src/main/java/edu/kit/typicalc/model/parser/AdditionalInformation.java @@ -0,0 +1,8 @@ +package edu.kit.typicalc.model.parser; + +public enum AdditionalInformation { + /** + * Duplicate VarType after Universal Quantifier. + */ + DUPLICATETYPE +} diff --git a/src/main/java/edu/kit/typicalc/model/parser/ExpectedInput.java b/src/main/java/edu/kit/typicalc/model/parser/ExpectedInput.java index 8fb4556..dcd73ef 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/ExpectedInput.java +++ b/src/main/java/edu/kit/typicalc/model/parser/ExpectedInput.java @@ -12,5 +12,9 @@ public enum ExpectedInput { /** * Any kind of type. */ - TYPE + TYPE, + /** + * // t[0-9]+ + */ + VARTYPE } 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 9cd9d0b..d6664c3 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/ParseError.java +++ b/src/main/java/edu/kit/typicalc/model/parser/ParseError.java @@ -62,6 +62,7 @@ public final class ParseError { private Optional cause = Optional.empty(); private Optional> needed = Optional.empty(); private Optional expected = Optional.empty(); + private Optional additional = Optional.empty(); private String term = ""; private char wrongChar = '\0'; private char correctChar = '\0'; @@ -93,6 +94,17 @@ public final class ParseError { return this; } + /** + * Attach additional information to this error. + * + * @param additional the additional information + * @return this object + */ + public ParseError additionalInformation(AdditionalInformation additional) { + this.additional = Optional.of(additional); + return this; + } + /** * Set expected token types of this error. * @@ -210,6 +222,13 @@ public final class ParseError { return errorType; } + /** + * @return the additional information + */ + public Optional getAdditionalInformation() { + return additional; + } + protected void setErrorType(ErrorType errorType) { this.errorType = Optional.of(errorType); } 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 701d472..f21b68a 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java +++ b/src/main/java/edu/kit/typicalc/model/parser/TypeAssumptionParser.java @@ -10,6 +10,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Parser for type assumptions. + * Parses according to the following grammar: + * + * TypeEnvironment --> ɛ | TypeAbstraction, TypeEnvironment | TypeAbstraction + * TypeAbstraction --> Variable | Type + * Type --> ∀ VarType.SingleType | SingleType + * SingleType --> SimpleType Rest + * SimpleType --> Variable | (SingleType) + * Rest --> ɛ | -> SingleType */ public class TypeAssumptionParser { @@ -47,14 +55,14 @@ public class TypeAssumptionParser { public Result, ParseError> parse(String assumptions) { lexer = new LambdaLexer( cleanAssumptionText(assumptions), ParseError.ErrorType.TYPE_ASSUMPTION_ERROR); - return parseTU(); + return parseTE(); } /** * parses Type Environment * @return if successful, a map of the type assumptions, otherwise an error */ - public Result, ParseError> parseTU() { + public Result, ParseError> parseTE() { HashMap map = new HashMap<>(); while (true) { Result nextLexerToken = lexer.nextToken(); @@ -115,6 +123,11 @@ public class TypeAssumptionParser { return new Result<>(new MapEntry<>(term, result.unwrap())); } + + /** + * Parses a Type + * @return if successful a type abstraction, otherwise an error. + */ public Result parseType() { Result nextLexerToken = lexer.nextToken(); if (nextLexerToken.isError()) { @@ -139,7 +152,8 @@ public class TypeAssumptionParser { String input = currentToken.getText(); if (!TYPE_VARIABLE_PATTERN.matcher(input).matches()) { return new Result<>(null, - ParseError.unexpectedToken(currentToken, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); + ParseError.unexpectedToken(currentToken, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) + .expectedInput(ExpectedInput.VARTYPE)); } int i = Integer.parseInt(input.substring(1)); TypeVariable v = new TypeVariable(TypeVariableKind.USER_INPUT, i); @@ -148,7 +162,8 @@ public class TypeAssumptionParser { for (TypeVariable var : quantifiedVariables) { if (var.equals(v)) { return new Result<>(null, - ParseError.unexpectedToken(currentToken, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); + ParseError.unexpectedToken(currentToken, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) + .additionalInformation(AdditionalInformation.DUPLICATETYPE)); } } quantifiedVariables.add(v); @@ -190,6 +205,10 @@ public class TypeAssumptionParser { return new Result<>(new TypeAbstraction(result.unwrap(), quantifiedVariables)); } + /** + * parses a single Type + * @return if successful a type, otherwise an error + */ public Result parseSingleType() { Result result = parseSimpleType(); @@ -216,6 +235,10 @@ public class TypeAssumptionParser { } } + /** + * parses a simple type + * @return if successful a type, otherwise an error + */ public Result parseSimpleType() { if (currentToken.getType() == Token.TokenType.VARIABLE) { Type type = parseLiteral(currentToken.getText()); @@ -244,6 +267,10 @@ public class TypeAssumptionParser { ParseError.ErrorType.TYPE_ASSUMPTION_ERROR).expectedInput(ExpectedInput.TYPE)); } + /** + * parses the rest of a single type + * @return if successful a type or an empty optional, otherwise an error + */ public Result, ParseError> parseRest() { switch (currentToken.getType()) { case ARROW: 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 7acf54f..844e62c 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 @@ -10,6 +10,7 @@ import com.vaadin.flow.component.html.Pre; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.i18n.LocaleChangeEvent; import com.vaadin.flow.i18n.LocaleChangeObserver; +import edu.kit.typicalc.model.parser.AdditionalInformation; import edu.kit.typicalc.model.parser.ExpectedInput; import edu.kit.typicalc.model.parser.ParseError; import edu.kit.typicalc.model.parser.Token; @@ -130,6 +131,15 @@ public class ErrorView extends VerticalLayout implements LocaleChangeObserver { getTranslation("error.expectedToken", correct)))); } + // state more additional information, if available + Optional moreAdditionalInformation = error.getAdditionalInformation(); + if (moreAdditionalInformation.isPresent()) { + AdditionalInformation e = moreAdditionalInformation.get(); + additionalInformation.add(new Span(new Pre( + getTranslation("error.additionalInformation", + getTranslation("additionalInformation." + e))))); + } + return new Div(additionalInformation); } diff --git a/src/main/resources/language/translation_de.properties b/src/main/resources/language/translation_de.properties index a1d5690..7c80e51 100644 --- a/src/main/resources/language/translation_de.properties +++ b/src/main/resources/language/translation_de.properties @@ -110,6 +110,7 @@ error.termForError=Term:\u0020 error.typeAssumptionForError=Typannahme:\u0020 error.wrongCharacter=Falsches Zeichen:\u0020 error.expectedToken=Erwartet: {0} +error.additionalInformation = Information: {0} error.hint=Die Grammatiken, die die korrekte Syntax eines Terms und der Typannahmen beschreiben, \ sind auch über das Info-Symbol zu erreichen. tokentype.UNIVERSAL_QUANTIFIER=Allquantor @@ -128,6 +129,8 @@ tokentype.COLON=: tokentype.EQUALS== tokentype.ARROW=-> tokentype.EOF=Ende der Eingabe +expectedinput.VARTYPE= Variable der Form t[0-9]+ +additionalInformation.DUPLICATETYPE= Duplikat expectedinput.TERM=beliebiger Term expectedinput.TYPE=beliebiger Typ root.or=oder diff --git a/src/main/resources/language/translation_en.properties b/src/main/resources/language/translation_en.properties index 5413aa2..904a8e2 100644 --- a/src/main/resources/language/translation_en.properties +++ b/src/main/resources/language/translation_en.properties @@ -102,6 +102,7 @@ error.wrongCharacter=Wrong character:\u0020 error.termForError=Term:\u0020 error.typeAssumptionForError=Type assumption:\u0020 error.expectedToken=Expected: {0} +error.additionalInformation = Information: {0} error.hint=The grammars describing the correct syntax of a term or a type assumption can also be reached \ via the info icon. tokentype.UNIVERSAL_QUANTIFIER=universal quantifier @@ -120,6 +121,8 @@ tokentype.COLON=: tokentype.EQUALS== tokentype.ARROW=-> tokentype.EOF=End of input +additionalInformation.DUPLICATETYPE= Duplicate +expectedinput.VARTYPE= Variable of the form t[0-9]+ expectedinput.TERM=any lambda term expectedinput.TYPE=any type root.or=or