Reputation: 193
I have a problem with error handling with Antlr 4 , when I call the method notifyErrorListeners("msg")
in my custom listener Antlr does not give me the exact line and position of the error.
Here is part of my dsl.
study( region = "region" , timestamp = 12:10 , processType = "processType")
...
My problem is that the grammar accepts the duplication of variables in the study , for example.
study( region = "region" , region = "region" , timestamp = 12:10)
...
So as a solution I decide to manage this in my custom listener .Here is my listener
@Getter
public class StudyPatternListener extends StudyBaseListener {
private Map<String, Object> studyParams = new HashMap<>();
@Setter
private StudyParser parser;
@Override
public void enterStudyAssignProcessType(StudyAssignProcessTypeContext ctx) {
String processType = ctx.STRING().getText().replace("\"", "");
if (!this.studyParams.containsKey("processType")) {
this.studyParams.put("processType", processType);
} else {
// here Antlr doesn't give me the right line and the right position of the error.
parser.notifyErrorListeners("processType parameter is already assigned");
}
}
Here is my custom error listener.
public class StudyErrorListener extends BaseErrorListener {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) {
System.err.println("line = " + line + ", position = " + charPositionInLine + " , msg = " + msg);
}
}
Here is my grammar.
grammar Study;
program: main EOF; // the program rule.
// LINE_COMMENT*
main: NEWLINE* study NEWLINE* ; // the main rule;
study : 'study' '(' studyParameters ')';
studyParameters: (| ( studyAssign (',' studyAssign)*) ); // assign in the study
studyAssign: 'timestamp' '=' TIMESTAMP # studyAssignTimestamp
| 'region' '=' STRING # studyAssignRegion
| 'processType' '=' STRING # studyAssignProcessType
| 'businessDate' '=' DATE # studyAssignBusinessDate
; // valid study parameters .
fragment LETTER : [A-Za-z];
fragment DIGIT : [0-9];
fragment TWODIGIT : DIGIT DIGIT;
TIMESTAMP: TWODIGIT ':' TWODIGIT;
DATE : TWODIGIT TWODIGIT ;
ID : LETTER+;
STRING : '"' ( ~ '"' )* '"' ;
NEWLINE:'\r'? '\n' ;
LINE_COMMENT: '#' ~[\r\n]* -> skip;
WS : [ \t]+ -> skip ;
Upvotes: 1
Views: 382
Reputation: 6001
Reporting back through the parser error listener during a validation tree walk is going to pick up the last position of the parser. Not what you want (the parser will have completed).
Need to dig the information from the context node and convert to a reasonably understandable form:
@Override
public void enterStudyAssignProcessType(StudyAssignProcessTypeContext ctx) {
...
// validate key/value pairs specified by an 'attribute' rule
for (AttributeContext attribute : ctx.attribute()) {
String id = attribute.id().getText();
Props props = AttrMap.get(id); // external map of allowed keys & values
if (props.name.equals(KeyAttr.INVALID)) { // whatever error check(s)
String cause = "Invalid name '" + id + "' at %s:%s[%s]";
reportProblem(IMarker.SEVERITY_ERROR, Kind.ATTRIBUTE, attribute.id().name, cause);
}
}
}
private void reportProblem(int severity, Kind kind, String cause, Token... tokens) {
Token beg = tokens[0];
Token end = tokens[tokens.length - 1];
errLine = beg.getLine();
errPos = beg.getCharPositionInLine();
errOffset = beg.getStartIndex();
errLen = end.getStopIndex() - errOffset + 1;
displayProblem(severity, kind, calcVisual(cause)); // your presentation
}
private String calcVisual(String cause) {
int beg = errOffset - errPos; // beg of line
if (beg > 0) {
String txt = Strings.EMPTY; // ""
Interval range = Interval.of(beg, errOffset);
try {
txt = record.cs.getText(range);
errVisPos = Strings.measureVisualWidth(txt, tabWidth); // adj for embedded tabs
} catch (Exception e) {
String name = ...; // source name
Log.error(this, "Bad visual position %s: %s (%s: %s)", e.getMessage(), name, range, txt);
}
txt = Strings.EMPTY;
range = Interval.of(beg, errOffset + errLen);
try {
txt = record.cs.getText(range);
errVisLen = Strings.measureVisualWidth(txt, tabWidth) - errVisPos;
} catch (Exception e) {
String name = ...;
Log.error(this, "Bad visual position %s: %s (%s: %s)", e.getMessage(), name, range, txt);
}
}
return String.format(cause, errLine, errVisPos, errVisLen);
}
Upvotes: 1