ANTLR4 no viable alternative; problem with spaces

Viewed 51

grammar Hello;

@parser::header {
    import java.util.*;
}

@parser::members {
    Map<Integer, String> map = new HashMap<Integer, String>();
    int A = 0;
    int B = 0;
    int max = 0;
}

prog
@after {
    List<Integer> msgs = new ArrayList<>(map.keySet());
    Collections.sort(msgs);
    for (int i=0; i< msgs.size(); i++){
        System.out.println(map.get(msgs.get(i)));
    }
    System.out.println("Alice: "+ A +", Bob: "+B);
    System.out.println(max);
}
: stat+;

stat: message NL | message;

message: h=T_NUM ':' m=T_NUM ':' s=T_NUM 'A' ':' T_MSG {
    String msg = $h.getText() + ":" + $m.getText() +  ":" + $s.getText() + " A: " + $T_MSG.getText();
    int len = $T_MSG.getText().length();
    if (len > max) max = len;
    A++;
    int id = Integer.parseInt($h.getText()) * 3600 + Integer.parseInt($m.getText()) * 60 + Integer.parseInt($s.getText());
    map.put(id, msg);
} | h=T_NUM ':' m=T_NUM ':' s=T_NUM 'B' ':' T_MSG {
    String msg = $h.getText() + ":" + $m.getText() +  ":" + $s.getText() + " A: " + $T_MSG.getText();
    int len = $T_MSG.getText().length();
    if (len > max) max = len;
    B++;
    int id = Integer.parseInt($h.getText()) * 3600 + Integer.parseInt($m.getText()) * 60 + Integer.parseInt($s.getText());
    map.put(id, msg);
};


T_NUM: [0-9][0-9];
T_MSG: [A-Za-z0-9.,!? ]+;
NL: [\n]+;
WS : [ \t\r]+ -> skip ; // skip spaces, tabs, newlines


Hello! So I have a task to write grammar and parser in ANTLR4 which recognizes this kind of input:

00:10:11 A: Message 1 
23:12:12 B: Message 5 
11:12:13 A: Message 2 
12:21:12 B: Message 4 
11:12:15 A: Message 3

and as an output, it has to sort out messages by time. Now my problem is with spaces. I want to be able to recognize spaces in messages but I get an error:

line 1:6 no viable alternative at input '00:10:11 A' Alice: 0, Bob: 0 0

When I remove space from the T_MSG token and obviously input it works. But I don't know how to make it work for it to be able to recognize spaces in messages.

1 Answers

Always dump your token stream to see what the Lexer produces for the Parser to consume.

For the first line of your test input, (using grun Hello prog -tokens < Hello.txt), I get:

[@0,0:1='00',<T_NUM>,1:0]
[@1,2:2=':',<':'>,1:2]
[@2,3:4='10',<T_NUM>,1:3]
[@3,5:5=':',<':'>,1:5]
[@4,6:9='11 A',<T_MSG>,1:6]
[@5,10:10=':',<':'>,1:10]
[@6,11:21=' Message 1 ',<T_MSG>,1:11]
[@7,22:22='\n',<NL>,1:22]
[@8,23:22='<EOF>',<EOF>,2:0]
line 1:6 no viable alternative at input '00:10:11 A'
Alice: 0, Bob: 0
0

in particular notice the line

[@4,6:9='11 A',<T_MSG>,1:6]

Your parser isn't seeing the stream of tokens that your parser rules assume it will see.

This is because "11 A" matches the T_MSG Lexer rule. Note: even though the T_NUM rule matches the "11" input, ANTLR's Lexer will use the Lexer rule that consumes the most input, so ANTLR will produce a T_MSG token.

That's why you're getting the observed error.


There are ways using Lexer modes, not skipping WS (which means accounting for all places where WS can occur in your parser rules), or maybe a couple of other techniques.

That said, you're really applying the wrong tool to the job. Reading this input in line by line and applying a Regex with capture groups will be MUCH simpler. There's nothing about your input that requires a full-fledged parser.

IF your push forward with ANTLR, you're probably also much better off just working out the grammar to get the correct parse tree and then using a listener to handle the results. All of the @parser* , prog {...} and parser rule actions, are distractions at best if you're not yet building the correct parse tree.

Related