ANTLR v3.5 parser errors during runtime

110 Views Asked by At

So I am having some problems with my grammar that I just can't seem to figure out. I am not sure exactly what the problem is or where it lies, but I will try to do my best to explain what's going on.

Just as a little bit of background so that the grammar may make a little more sense, the goal here is to take an input program written in pseudo abc-notation and play it back using the java library, jMusic.

Also, it is worth noting that I am using custom AST code, not ANTLR's built in AST builder.

The problem seems to be occurring first when parsing the 'fields' within the 'header', but I also tried removing the header from the grammar entirely to try and narrow down where the problem could be arising, but I get similar problems when trying to parse 'notes' so I have a feeling that there is a more general problem going on here.

Here is my grammar:

grammar simpleABC;

// grammar rules

options{
    backtrack = true;
}

@members{
    // override the default error reporting functions
    public void reportError(RecognitionException e) {
        // call the Parser member function to report the error
        displayRecognitionError(this.getTokenNames(), e);
        // exit with error
        System.exit(1);
    }
}

program returns [AST ast]
    :   { $ast = new Program(); }
        (header {$ast.addAST($header.ast);})
        (score {$ast.addAST($score.ast);})
    ;

header returns [Header ast]
    :   'header' '{'
            (field {$ast.addAST($field.ast);})+
        '}'
    ;

field returns [Field ast]
    :   'X:' x=NUM                      { $ast = (new ReferenceNumber($x.text)); }
    |   'T:' t=STRING                   { $ast = (new Title($t.text)); }
    |   'C:' c=STRING                   { $ast = (new Composer($c.text)); }
    |   'M:' (b=NUM '/' l=NUM)          { $ast = (new Meter($b.text, $l.text)); }
    |   'M:' 'C'                        { $ast = (new Meter("4", "4")); }
    |   'K:' k=PITCH                    { $ast = (new Key($k.text)); }
    |   'L:' l=NUM                      { $ast = (new Length($l.text)); }
    |   'L:' '/' l=NUM                  { $ast = (new Length("/" + $l.text)); }
    |   'tempo:' tempo=NUM              { $ast = (new Tempo($tempo.text)); }
    ;

score returns [Score ast]
    :   'score' '{'
            (part {$ast.addAST($part.ast);})+
        '}'
    ;

part returns [Part ast]
    :   instrument=STRING id=VAR '{' {$ast = new Part($instrument.text, $id.text);}
            (phrase {$ast.addAST($phrase.ast);})+
        '}'
    ;

phrase returns [Phrase ast]
    :   '|'  (b1=bar {$ast.addAST($b1.ast);} '|')+                                                  // typical bar
/*  |   '|:' (b2=bar {$ast.addAST($b2.ast);} '|')+ (b3=bar {$ast.addAST($b3.ast);} ':|')            // single repeat
    |   '|:' (b4=bar {$ast.addAST($b4.ast);} '|')+                                                  // multi-ending repeat
            '1' (b5=bar {$ast.addAST($b5.ast);} )+ ':|'                                                 // 1st ending
            '2' (b6=bar {$ast.addAST($b6.ast);} )+                                                      // 2nd ending

    TODO: fix repeats
*/
    ;

bar returns [Bar ast]
    :   (note {$ast.addAST($note.ast);})+
    ;

note returns [Note ast]
    :   pitch=PITCH length=NUM  { $ast = new Note($pitch.text, $length.text); }
    |   pitch=PITCH             { $ast = new Note($pitch.text); }
    ;

// lexical analysis stuff   

NUM     :   ('0'..'9')+
        ;
VAR     :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
        ;
PITCH   :   (('A'..'G') | ('a'..'g'))   (',' | '\'')?
        ;
COMMENT :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
        ;
WS      :   ( ' ' | '\t' | '\r' | '\n' ) {$channel=HIDDEN;}
        ;
STRING  :   '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
        ;

fragment
ESC_SEQ :       '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
        ;

Here is a simple input program that I have been using:

header {
    X:1
    T:"Notes"
    M:C
    K:C
    L:1/4
}
score {
    PIANO pianoPart {
        |C, D, E, F,|G, A, B, C|D E F G|A B c d| e f g a|b c' d' e'|f' g' a' b'|
    }
}

When I run this code, I get the following error:

Exception in thread "main" java.lang.NullPointerException
    at simpleABCParser.header(simpleABCParser.java:151)
    at simpleABCParser.program(simpleABCParser.java:87)
    at simpleABCParser.main(simpleABC.java:32)

Ok, so it tells me there is a null pointer exception in the parser. Here's a look at where this null pointer is happening.

// $ANTLR start "header"
// simpleABC.g:25:1: header returns [Header ast] : 'header' '{' ( field )+ '}' ;

public final Header header() throws RecognitionException {
        Header ast = null;


        Field field3 =null;

        try {
            // simpleABC.g:26:2: ( 'header' '{' ( field )+ '}' )
            // simpleABC.g:26:4: 'header' '{' ( field )+ '}'
            {
            match(input,19,FOLLOW_19_in_header67); if (state.failed) return ast;
            match(input,22,FOLLOW_22_in_header69); if (state.failed) return ast;
            // simpleABC.g:27:4: ( field )+
            int cnt1=0;
            loop1:
            while (true) {
                int alt1=2;
                int LA1_0 = input.LA(1);
                if ( ((LA1_0 >= 13 && LA1_0 <= 18)||LA1_0==21) ) {
                    alt1=1;
                }

                switch (alt1) {
                case 1 :
                    // simpleABC.g:27:5: field
                    {
                    pushFollow(FOLLOW_field_in_header75);
                    field3=field();
                    state._fsp--;
                    if (state.failed) return ast;
                    if ( state.backtracking==0 ) {ast.addAST(field3);}
                    }
                    break;

                default :
                    if ( cnt1 >= 1 ) break loop1;
                    if (state.backtracking>0) {state.failed=true; return ast;}
                    EarlyExitException eee = new EarlyExitException(1, input);
                    throw eee;
                }
                cnt1++;
            }

            match(input,24,FOLLOW_24_in_header83); if (state.failed) return ast;
            }

        }
        catch (RecognitionException re) {
            reportError(re);
            recover(input,re);
        }
        finally {
            // do for sure before leaving
        }
        return ast;
    }

// $ANTLR end "header"

It seems like the line field3=field(); is returning null, so let's look at the field() function. It is quite long but I will post it just to be thorough.

// $ANTLR start "field"
    // simpleABC.g:31:1: field returns [Field ast] : ( 'X:' x= NUM | 'T:' t= STRING | 'C:' c= STRING | 'M:' (b= NUM '/' l= NUM ) | 'M:' 'C' | 'K:' k= PITCH | 'L:' l= NUM | 'L:' '/' l= NUM | 'tempo:' tempo= NUM );
    public final Field field() throws RecognitionException {
        Field ast = null;


        Token x=null;
        Token t=null;
        Token c=null;
        Token b=null;
        Token l=null;
        Token k=null;
        Token tempo=null;

        try {
            // simpleABC.g:32:2: ( 'X:' x= NUM | 'T:' t= STRING | 'C:' c= STRING | 'M:' (b= NUM '/' l= NUM ) | 'M:' 'C' | 'K:' k= PITCH | 'L:' l= NUM | 'L:' '/' l= NUM | 'tempo:' tempo= NUM )
            int alt2=9;
            switch ( input.LA(1) ) {
            case 18:
                {
                alt2=1;
                }
                break;
            case 17:
                {
                alt2=2;
                }
                break;
            case 13:
                {
                alt2=3;
                }
                break;
            case 16:
                {
                int LA2_4 = input.LA(2);
                if ( (LA2_4==12) ) {
                    alt2=5;
                }
                else if ( (LA2_4==NUM) ) {
                    alt2=4;
                }

                else {
                    if (state.backtracking>0) {state.failed=true; return ast;}
                    int nvaeMark = input.mark();
                    try {
                        input.consume();
                        NoViableAltException nvae =
                            new NoViableAltException("", 2, 4, input);
                        throw nvae;
                    } finally {
                        input.rewind(nvaeMark);
                    }
                }

                }
                break;
            case 14:
                {
                alt2=6;
                }
                break;
            case 15:
                {
                int LA2_6 = input.LA(2);
                if ( (LA2_6==NUM) ) {
                    alt2=7;
                }
                else if ( (LA2_6==11) ) {
                    alt2=8;
                }

                else {
                    if (state.backtracking>0) {state.failed=true; return ast;}
                    int nvaeMark = input.mark();
                    try {
                        input.consume();
                        NoViableAltException nvae =
                            new NoViableAltException("", 2, 6, input);
                        throw nvae;
                    } finally {
                        input.rewind(nvaeMark);
                    }
                }

                }
                break;
            case 21:
                {
                alt2=9;
                }
                break;
            default:
                if (state.backtracking>0) {state.failed=true; return ast;}
                NoViableAltException nvae =
                    new NoViableAltException("", 2, 0, input);
                throw nvae;
            }
            switch (alt2) {
                case 1 :
                    // simpleABC.g:32:4: 'X:' x= NUM
                    {
                    match(input,18,FOLLOW_18_in_field99); if (state.failed) return ast;
                    x=(Token)match(input,NUM,FOLLOW_NUM_in_field103); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new ReferenceNumber((x!=null?x.getText():null))); }
                    }
                    break;
                case 2 :
                    // simpleABC.g:33:4: 'T:' t= STRING
                    {
                    match(input,17,FOLLOW_17_in_field115); if (state.failed) return ast;
                    t=(Token)match(input,STRING,FOLLOW_STRING_in_field119); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Title((t!=null?t.getText():null))); }
                    }
                    break;
                case 3 :
                    // simpleABC.g:34:4: 'C:' c= STRING
                    {
                    match(input,13,FOLLOW_13_in_field130); if (state.failed) return ast;
                    c=(Token)match(input,STRING,FOLLOW_STRING_in_field134); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Composer((c!=null?c.getText():null))); }
                    }
                    break;
                case 4 :
                    // simpleABC.g:35:4: 'M:' (b= NUM '/' l= NUM )
                    {
                    match(input,16,FOLLOW_16_in_field145); if (state.failed) return ast;
                    // simpleABC.g:35:9: (b= NUM '/' l= NUM )
                    // simpleABC.g:35:10: b= NUM '/' l= NUM
                    {
                    b=(Token)match(input,NUM,FOLLOW_NUM_in_field150); if (state.failed) return ast;
                    match(input,11,FOLLOW_11_in_field152); if (state.failed) return ast;
                    l=(Token)match(input,NUM,FOLLOW_NUM_in_field156); if (state.failed) return ast;
                    }

                    if ( state.backtracking==0 ) { ast = (new Meter((b!=null?b.getText():null), (l!=null?l.getText():null))); }
                    }
                    break;
                case 5 :
                    // simpleABC.g:36:4: 'M:' 'C'
                    {
                    match(input,16,FOLLOW_16_in_field166); if (state.failed) return ast;
                    match(input,12,FOLLOW_12_in_field168); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Meter("4", "4")); }
                    }
                    break;
                case 6 :
                    // simpleABC.g:37:4: 'K:' k= PITCH
                    {
                    match(input,14,FOLLOW_14_in_field180); if (state.failed) return ast;
                    k=(Token)match(input,PITCH,FOLLOW_PITCH_in_field184); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Key((k!=null?k.getText():null))); }
                    }
                    break;
                case 7 :
                    // simpleABC.g:38:4: 'L:' l= NUM
                    {
                    match(input,15,FOLLOW_15_in_field195); if (state.failed) return ast;
                    l=(Token)match(input,NUM,FOLLOW_NUM_in_field199); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Length((l!=null?l.getText():null))); }
                    }
                    break;
                case 8 :
                    // simpleABC.g:39:4: 'L:' '/' l= NUM
                    {
                    match(input,15,FOLLOW_15_in_field211); if (state.failed) return ast;
                    match(input,11,FOLLOW_11_in_field213); if (state.failed) return ast;
                    l=(Token)match(input,NUM,FOLLOW_NUM_in_field217); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Length("/" + (l!=null?l.getText():null))); }
                    }
                    break;
                case 9 :
                    // simpleABC.g:40:4: 'tempo:' tempo= NUM
                    {
                    match(input,21,FOLLOW_21_in_field228); if (state.failed) return ast;
                    tempo=(Token)match(input,NUM,FOLLOW_NUM_in_field232); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new Tempo((tempo!=null?tempo.getText():null))); }
                    }
                    break;

            }
        }
        catch (RecognitionException re) {
            reportError(re);
            recover(input,re);
        }
        finally {
            // do for sure before leaving
        }
        return ast;
    }
    // $ANTLR end "field"

It seems to me that the match() function in the second switch-case statement is returning null, which I would think means it is not matching the input tokens to the expected tokens. This next snippet is in the field() function above, but just in case you don't feel like sifting through all of that, here is the snippet I am talking about:

switch (alt2) {
                case 1 :
                    // simpleABC.g:32:4: 'X:' x= NUM
                    {
                    match(input,18,FOLLOW_18_in_field99); if (state.failed) return ast;
                    x=(Token)match(input,NUM,FOLLOW_NUM_in_field103); if (state.failed) return ast;
                    if ( state.backtracking==0 ) { ast = (new ReferenceNumber((x!=null?x.getText():null))); }
                    }
                    break;

So I think I've traced this back about as far as I can without diving deep into ANTLR. To me, the evidence points to something being wrong with the grammar. I have checked it many times and tried to isolate the issue but I haven't had any luck.

It's got to be something incredibly stupid that I am just missing, or something that's beyond my understanding of ANTLR at this point, so any advice, guesses, or solutions are welcome. Please let me know if there is any additional information that you would like for me to provide, and thank you to anyone who even made it through this post haha.

0

There are 0 best solutions below