javascriptlexical-analysisjison

Jison ignores one of my rules


I'm trying to use Jison.

Here's my grammar:

var grammar = {
lex:{
    rules:[
        ["\\s+",            ""],
        ["then",            "return 'newline';"],
        ["goto",            "return 'goto';"],
        ["http[^\\s]*",     "return 'url';"],
        ["search",          "return 'search';"],
        ["should_exist",    "return 'exist';"],
        //["#(.)*",           "return 'elementById';"],
        //["$",               "return 'EOF';"]
    ]
},
bnf:{
    lines:[
        ['lines line',          "console.log('big expression is ',$3);  return ['l2',$1, $2];"],
        ['line',                "console.log('expression is ',$1); return ['l1',$1]"],
    ],
    line:[
        ["line newline",        "console.log('line newline', $1); $$ = $1"],
        ["goto url",            "console.log('goto', $2); $$ = {cmd:'goto', url: $2 } "],
        ["elementById exist",   "$$ = {cmd:'assert', elId: $1} "]
    ]
}
};

When I try to parse goto http://www.google.com then goto http://www.bing.com I only ever get [ 'l1', { cmd: 'goto', url: 'http://www.google.com' } ] returned.

I'm expecting to get both goto commands returned.

Any help with me figuring out my grammar?


Solution

  • The main problem in your code is the premature use of return. Using return will end the parsing right there. So if you use it in a rule that is not meant to terminate parsing, you're in trouble. I like to have one rule which is the entry point of the whole system and whose job is only to call return with something sensible.

    Here's something that works more like what you want. I've not changed anything to lex.

        bnf:{
            top: [
                ['lines', "console.log('top is ', $1);  return $1;"]
            ],
            lines:[
                ['lines line',          "console.log('big expression is ', $1);  $$ = ['l2', $1, $2];"],
                ['line',                "console.log('expression is ',$1); $$ = ['l1',$1]"],
            ],
            line:[
                ["line newline",        "console.log('line newline', $1); $$ = $1"],
                ["goto url",            "console.log('goto', $2); $$ = {cmd:'goto', url: $2 } "],
                ["elementById exist",   "$$ = {cmd:'assert', elId: $1} "]
            ]
        }
    

    The output I get with the above is:

    goto http://www.google.com
    line newline { cmd: 'goto', url: 'http://www.google.com' }
    expression is  { cmd: 'goto', url: 'http://www.google.com' }
    goto http://www.bing.com
    big expression is  [ 'l1', { cmd: 'goto', url: 'http://www.google.com' } ]
    top is  [ 'l2',
      [ 'l1', { cmd: 'goto', url: 'http://www.google.com' } ],
      { cmd: 'goto', url: 'http://www.bing.com' } ]
    

    Here's a diff between what you originally had and what I suggest:

    --- original.js 2014-02-23 08:10:37.605989877 -0500
    +++ parser.js   2014-02-23 08:35:06.674952990 -0500
    @@ -14,9 +14,12 @@
             ]
         },
         bnf:{
    +        top: [
    +            ['lines', "console.log('top is ', $1);  return $1;"]
    +        ],
             lines:[
    -            ['lines line',          "console.log('big expression is ',$3);  return ['l2',$1, $2];"],
    -            ['line',                "console.log('expression is ',$1); return ['l1',$1]"],
    +            ['lines line',          "console.log('big expression is ', $1);  $$ = ['l2', $1, $2];"],
    +            ['line',                "console.log('expression is ',$1); $$ = ['l1',$1]"],
             ],
             line:[
                 ["line newline",        "console.log('line newline', $1); $$ = $1"],