From da40bfd5858a43487e820b3a7b72d60716632ef4 Mon Sep 17 00:00:00 2001 From: Zachary Carter Date: Sat, 3 Aug 2013 13:35:11 -0700 Subject: [PATCH] allow access to value stack - fixes #168 --- examples/inherited.y | 35 ++++++++++++++++++++++++++++++++ lib/jison.js | 29 +++++++++++++++----------- tests/parser/actions.js | 45 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 examples/inherited.y diff --git a/examples/inherited.y b/examples/inherited.y new file mode 100644 index 000000000..c40a5ae05 --- /dev/null +++ b/examples/inherited.y @@ -0,0 +1,35 @@ +%lex +%% +\s+ {} +(global|local|integer|float) { return yytext; } +[a-zA-Z_]\w* { return 'id'; } +. { return yytext; } +/lex +%% +D + : C T L + ; + +C + : global + | local + ; + +T + : integer + | float + ; + +L + : L ',' id { + console.log("L -> L ',' id ("+yytext+")"); + console.log($id + ' is of type ' + $0); + console.log($1 + ' is of class ' + $-1); + } + | id { + console.log("L -> id ("+yytext+")"); + console.log($id + ' is of type ' + $0); + console.log($1 + ' is of class ' + $-1); + } + ; +%% diff --git a/lib/jison.js b/lib/jison.js index c799ad796..9878a998c 100755 --- a/lib/jison.js +++ b/lib/jison.js @@ -191,7 +191,7 @@ function processOperators (ops) { generator.buildProductions = function buildProductions(bnf, productions, nonterminals, symbols, operators) { var actions = [ - '/* this == yyval */', + '/* this == yyval */', this.actionInclude || '', 'var $0 = $$.length - 1;', 'switch (yystate) {' @@ -289,11 +289,16 @@ generator.buildProductions = function buildProductions(bnf, productions, nonterm return names[pl] ? '@'+names[pl] : pl; }); } - action = action.replace(/([^'"])\$\$|^\$\$/g, '$1this.$').replace(/@[0$]/g, "this._$") - .replace(/\$(\d+)/g, function (_, n) { - return "$$[$0" + (n - rhs.length || '') + "]"; + action = action + // replace references to $$ with this.$, and @$ with this._$ + .replace(/([^'"])\$\$|^\$\$/g, '$1this.$').replace(/@[0$]/g, "this._$") + + // replace semantic value references ($n) with stack value (stack[n]) + .replace(/\$(-?\d+)/g, function (_, n) { + return "$$[$0" + (parseInt(n, 10) - rhs.length || '') + "]"; }) - .replace(/@(\d+)/g, function (_, n) { + // same as above for location references (@n) + .replace(/@(-?\d+)/g, function (_, n) { return "_$[$0" + (n - rhs.length || '') + "]"; }); actions.push(action); @@ -1294,13 +1299,13 @@ _handle_error: ("'"+(this.terminals_[symbol] || symbol)+"'")); } this.parseError(errStr, { - text: this.lexer.match, - token: this.terminals_[symbol] || symbol, - line: this.lexer.yylineno, - loc: yyloc, - expected: expected, - recoverable: (error_rule_depth !== false) - }); + text: this.lexer.match, + token: this.terminals_[symbol] || symbol, + line: this.lexer.yylineno, + loc: yyloc, + expected: expected, + recoverable: (error_rule_depth !== false) + }); } else if (preErrorSymbol !== EOF) { error_rule_depth = locateNearestErrorRecoveryRule(state); } diff --git a/tests/parser/actions.js b/tests/parser/actions.js index 37eb11c4d..61b110ce5 100644 --- a/tests/parser/actions.js +++ b/tests/parser/actions.js @@ -150,6 +150,51 @@ exports["test ambiguous named semantic value"] = function() { assert.equal(parser.parse('xyx'), "xyx", "return first after reduction"); }; +exports["test previous semantic value lookup ($0)"] = function() { + var lexData = { + rules: [ + ["x", "return 'x';"], + ["y", "return 'y';"] + ] + }; + var grammar = { + bnf: { + "S" :[ ["A B", "return $A + $B"] ], + "A" :[ ['A x', "$$ = $A+'x'"], ['x', "$$ = $1"] ], + "B" :[ ["y", "$$ = $0"] ], + } + }; + + var parser = new Jison.Parser(grammar); + parser.lexer = new RegExpLexer(lexData); + + assert.equal(parser.parse('xxy'), "xxxx", "return first after reduction"); +}; + + +exports["test negative semantic value lookup ($-1)"] = function() { + var lexData = { + rules: [ + ["x", "return 'x';"], + ["y", "return 'y';"], + ["z", "return 'z';"] + ] + }; + var grammar = { + bnf: { + "S" :[ ["G A B", "return $G + $A + $B"] ], + "G" :[ ['z', "$$ = $1"] ], + "A" :[ ['A x', "$$ = $A+'x'"], ['x', "$$ = $1"] ], + "B" :[ ["y", "$$ = $-1"] ], + } + }; + + var parser = new Jison.Parser(grammar); + parser.lexer = new RegExpLexer(lexData); + + assert.equal(parser.parse('zxy'), "zxz", "return first after reduction"); +}; + exports["test Build AST"] = function() { var lexData = { rules: [