Джейсон игнорирует одно из моих правил

Я пытаюсь использовать Jison.

Вот моя грамматика:

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} "]
    ]
}
};

Когда я пытаюсь разобрать goto http://www.google.com then goto http://www.bing.com Я только когда-либо получаю [ 'l1', { cmd: 'goto', url: 'http://www.google.com' } ] вернулся.

Я ожидаю получить обе команды goto.

Любая помощь со мной в выяснении моей грамматики?

2 ответа

Решение

Основной проблемой в вашем коде является преждевременное использование return, С помощью return закончит разбор прямо там. Поэтому, если вы используете его в правиле, которое не предназначено для прекращения анализа, у вас возникнут проблемы. Мне нравится иметь одно правило, которое является точкой входа всей системы и работа которой заключается только в вызове return с чем-то разумным.

Вот то, что работает больше как то, что вы хотите. Я ничего не изменил на 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} "]
        ]
    }

Вывод, который я получаю с выше:

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' } ]

Вот разница между тем, что у вас изначально было, и тем, что я предлагаю:

--- 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"],

Я бы оспорил вашу потребность в полноценном парсере для этой цели. Например, вы можете сделать то же самое, просто используя лексер и рукописный анализатор рекурсивного спуска:

var lexer = new Lexer;

lexer.addRule(/\s+/, function () {
    // skip whitespace
});

lexer.addRule(/then|goto|search/, function (lexeme) {
    return lexeme.toUpperCase();
});

lexer.addRule(/https?:\/\/[^\s]+/, function (lexeme) {
    this.yytext = lexeme;
    return "URL";
});

lexer.addRule(/$/, function () {
    return "EOF";
});

Теперь, когда у нас есть лексический анализатор, мы создадим парсер рекурсивного спуска:

function parse(input) {
    lexer.setInput(input);
    return statement();
}

function statement() {
    var condition = command();
    match("THEN");
    var branch = command();
    match("EOF");

    return {
        condition: condition,
        branch: branch
    };
}

function command() {
    match("GOTO");
    var url = match("URL");

    return {
        command: "GOTO",
        url: url
    };
}

function match(expected) {
    var token = lexer.lex();
    if (token === expected) return lexer.yytext;
    else throw new Error("Unexpected token: " + token);
}

Теперь все, что вам нужно сделать, это позвонить parse:

var output = parse("goto http://www.google.com/ then goto http://www.bing.com/");

alert(JSON.stringify(output, null, 4));

Смотрите демо для себя: http://jsfiddle.net/r4RH2/1/

Другие вопросы по тегам