66import de .vill .exception .ParseError ;
77import org .antlr .v4 .runtime .*;
88
9+ import java .util .Arrays ;
10+ import java .util .HashSet ;
911import java .util .List ;
12+ import java .util .Set ;
1013import java .util .regex .*;
1114
1215public class UVLErrorListener extends BaseErrorListener {
1316
17+ private static final Set <String > GROUP_KEYWORDS = new HashSet <>(Arrays .asList ( "mandatory" , "optional" , "alternative" , "or" ));
18+
1419 private final List <ParseError > errorList ;
1520
1621 public UVLErrorListener (List <ParseError > errorList ) {
@@ -24,7 +29,8 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
2429 }
2530
2631 private ErrorReport translateToReport (String message , Object offendingSymbol , Recognizer <?, ?> recognizer , int line , int charPosition ) {
27- // Token recognition error -> LEXICAL
32+
33+ // Token recognition error
2834 Matcher m = Pattern .compile ("token recognition error at: '(.*)'" ).matcher (message );
2935 if (m .find ()) {
3036 String character = m .group (1 );
@@ -37,7 +43,7 @@ private ErrorReport translateToReport(String message, Object offendingSymbol, Re
3743 .build ();
3844 }
3945
40- // Missing token -> SYNTAX
46+ // Missing token
4147 m = Pattern .compile ("missing '?([<>\\ w]+)'? at '?(.*?)'?$" ).matcher (message );
4248 if (m .find ()) {
4349 String missing = tokenToReadable (m .group (1 ));
@@ -50,10 +56,20 @@ private ErrorReport translateToReport(String message, Object offendingSymbol, Re
5056 .build ();
5157 }
5258
53- // Extraneous input -> SYNTAX
59+ // Extraneous input
5460 m = Pattern .compile ("extraneous input '?(.*?)'? expecting (.*)" ).matcher (message );
5561 if (m .find ()) {
56- String extra = tokenToReadable (m .group (1 ));
62+ String extraString = m .group (1 );
63+ String extra = tokenToReadable (extraString );
64+ if (Character .isDigit (extraString .charAt (0 ))){
65+ return new ErrorReport .Builder (ErrorCategory .SYNTAX ,
66+ "Wrong feature name: " + extra )
67+ .line (line ).charPosition (charPosition )
68+ .reference (m .group (1 ))
69+ .cause ("Feature names can not start with a number." )
70+ .hint ("Rename the feature." )
71+ .build ();
72+ }
5773 return new ErrorReport .Builder (ErrorCategory .SYNTAX ,
5874 "Unexpected input " + extra )
5975 .line (line ).charPosition (charPosition )
@@ -63,18 +79,14 @@ private ErrorReport translateToReport(String message, Object offendingSymbol, Re
6379 .build ();
6480 }
6581
66- // Mismatched input -> SYNTAX
82+ // Mismatched input
6783 m = Pattern .compile ("mismatched input '?(.*?)'? expecting (.*)" ).matcher (message );
6884 if (m .find ()) {
6985 String found = tokenToReadable (m .group (1 ));
7086 String expected = simplifySet (m .group (2 ));
7187
7288 // Sonderfall: nach Gruppierung ohne Features
73- // ANTLR meldet entweder "a new line" oder ein Gruppen-Keyword als found,
74- // wenn eine Gruppe leer ist und expected "an indentation" ist
75- if (expected .equals ("an indentation" ) && (found .equals ("a new line" )
76- || found .contains ("'mandatory'" ) || found .contains ("'optional'" )
77- || found .contains ("'alternative'" ) || found .contains ("'or'" ))) {
89+ if (expected .equals ("an indentation" ) && (found .equals ("a new line" ) || found .contains ("'mandatory'" ) || found .contains ("'optional'" )|| found .contains ("'alternative'" ) || found .contains ("'or'" ))) {
7890 return new ErrorReport .Builder (ErrorCategory .SYNTAX ,
7991 "Missing features after group type" )
8092 .line (line ).charPosition (charPosition )
@@ -84,8 +96,24 @@ private ErrorReport translateToReport(String message, Object offendingSymbol, Re
8496 .build ();
8597 }
8698
87- // Sonderfall: mehr als ein root Feature
99+ // Case: More than one root feature
88100 if (expected .equals ("an indentation or a dedentation" )) {
101+ // Check if the offending token is actually a group keyword
102+ String offendingText = null ;
103+ if (offendingSymbol instanceof Token ) {
104+ offendingText = ((Token ) offendingSymbol ).getText ();
105+ }
106+ if (offendingText != null && GROUP_KEYWORDS .contains (offendingText .toLowerCase ())) {
107+ return new ErrorReport .Builder (ErrorCategory .SYNTAX ,
108+ "Group keyword '" + offendingText + "' at wrong indentation level" )
109+ .line (line ).charPosition (charPosition )
110+ .field (ErrorField .GROUP )
111+ .reference (offendingText )
112+ .cause ("'" + offendingText + "' is a group type keyword but appears at the wrong indentation level." )
113+ .hint ("Indent '" + offendingText + "' further so it is nested under a parent feature." )
114+ .build ();
115+ }
116+
89117 return new ErrorReport .Builder (ErrorCategory .SYNTAX ,
90118 "More than one root feature detected" )
91119 .line (line ).charPosition (charPosition )
0 commit comments