Джейсон игнорирует одно из моих правил
Я пытаюсь использовать 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/