From 725927e05fe2d4a158e5e5201990f81092c8058b Mon Sep 17 00:00:00 2001 From: David Majda Date: Tue, 26 Jun 2012 20:28:06 +0200 Subject: [PATCH] Change ordering of "action" code Places all code that does something with "action" AST nodes under code handling "choice" nodes. This ordering is logical because now all the node handling code matches the sequence in which various node types usually appear when descending through the AST tree. --- spec/compiler/passes/compute-params.spec.js | 24 +-- .../compiler/passes/compute-var-names.spec.js | 24 +-- .../passes/remove-proxy-rules.spec.js | 8 +- .../passes/report-left-recursion.spec.js | 8 +- .../passes/report-missing-rules.spec.js | 8 +- spec/generated-parser.spec.js | 180 +++++++++--------- src/compiler/passes/compute-params.js | 17 +- src/compiler/passes/compute-var-names.js | 3 +- src/compiler/passes/generate-code.js | 23 +-- src/compiler/passes/report-left-recursion.js | 2 +- src/compiler/passes/report-missing-rules.js | 2 +- 11 files changed, 150 insertions(+), 149 deletions(-) diff --git a/spec/compiler/passes/compute-params.spec.js b/spec/compiler/passes/compute-params.spec.js index 4355836..ec1bdee 100644 --- a/spec/compiler/passes/compute-params.spec.js +++ b/spec/compiler/passes/compute-params.spec.js @@ -63,6 +63,12 @@ describe("compiler pass |computeParams|", function() { ); }); + it("computes params for an action", function() { + expect(pass).toChangeAST('start = (a:"a" { }) { }', innerExpressionDetails({ + params: { a: "result0" } + })); + }); + it("computes params for a sequence", function() { expect(pass).toChangeAST( 'start = (a:"a" { }) "b" "c"', @@ -113,12 +119,6 @@ describe("compiler pass |computeParams|", function() { params: { a: "result1" } })); }); - - it("computes params for an action", function() { - expect(pass).toChangeAST('start = (a:"a" { }) { }', innerExpressionDetails({ - params: { a: "result0" } - })); - }); }); describe("scoping", function() { @@ -129,6 +129,12 @@ describe("compiler pass |computeParams|", function() { ); }); + it("creates a new scope for an action", function() { + expect(pass).toChangeAST('start = (a:"a" { }) { }', expressionDetails({ + params: {} + })); + }); + it("does not create a new scope for a sequence", function() { expect(pass).toChangeAST( 'start = a:"a" b:"b" c:"c" { }', @@ -197,11 +203,5 @@ describe("compiler pass |computeParams|", function() { params: {} })); }); - - it("creates a new scope for an action", function() { - expect(pass).toChangeAST('start = (a:"a" { }) { }', expressionDetails({ - params: {} - })); - }); }); }); diff --git a/spec/compiler/passes/compute-var-names.spec.js b/spec/compiler/passes/compute-var-names.spec.js index bbaf3b7..8d32efe 100644 --- a/spec/compiler/passes/compute-var-names.spec.js +++ b/spec/compiler/passes/compute-var-names.spec.js @@ -51,6 +51,18 @@ describe("compiler pass |computeVarNames|", function() { })); }); + it("computes variable names for an action", function() { + expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({ + resultVars: ["result0"], + posVars: ["pos0", "pos1"], + expression: { + resultVar: "result0", + posVar: "pos0", + expression: { resultVar: "result0", posVar: "pos1" } + } + })); + }); + it("computes variable names for a sequence", function() { expect(pass).toChangeAST('start = ', ruleDetails({ resultVars: ["result0"], @@ -183,18 +195,6 @@ describe("compiler pass |computeVarNames|", function() { })); }); - it("computes variable names for an action", function() { - expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0", "pos1"], - expression: { - resultVar: "result0", - posVar: "pos0", - expression: { resultVar: "result0", posVar: "pos1" } - } - })); - }); - it("computes variable names for a rule reference", function() { expect(pass).toChangeAST('start = a', ruleDetails({ resultVars: ["result0"], diff --git a/spec/compiler/passes/remove-proxy-rules.spec.js b/spec/compiler/passes/remove-proxy-rules.spec.js index 6cde306..4030239 100644 --- a/spec/compiler/passes/remove-proxy-rules.spec.js +++ b/spec/compiler/passes/remove-proxy-rules.spec.js @@ -41,6 +41,10 @@ describe("compiler pass |removeProxyRules|", function() { ); }); + it("removes proxy rule from an action", function() { + expect(pass).toChangeAST(proxyGrammar('start = proxy { }'), simpleDetails); + }); + it("removes proxy rule from a sequence", function() { expect(pass).toChangeAST( proxyGrammar('start = proxy "a" "b"'), @@ -75,8 +79,4 @@ describe("compiler pass |removeProxyRules|", function() { it("removes proxy rule from a one or more", function() { expect(pass).toChangeAST(proxyGrammar('start = proxy+'), simpleDetails); }); - - it("removes proxy rule from an action", function() { - expect(pass).toChangeAST(proxyGrammar('start = proxy { }'), simpleDetails); - }); }); diff --git a/spec/compiler/passes/report-left-recursion.spec.js b/spec/compiler/passes/report-left-recursion.spec.js index 1155b02..68f8e09 100644 --- a/spec/compiler/passes/report-left-recursion.spec.js +++ b/spec/compiler/passes/report-left-recursion.spec.js @@ -51,6 +51,10 @@ describe("compiler pass |reportLeftRecursion|", function() { expect(pass).toReportLeftRecursionIn('start = "a" / "b" / start'); }); + it("reports left recursion inside an action", function() { + expect(pass).toReportLeftRecursionIn('start = start { }'); + }); + it("reports left recursion inside a sequence", function() { expect(pass).toReportLeftRecursionIn('start = start "a" "b"'); }); @@ -79,10 +83,6 @@ describe("compiler pass |reportLeftRecursion|", function() { expect(pass).toReportLeftRecursionIn('start = start+'); }); - it("reports left recursion inside an action", function() { - expect(pass).toReportLeftRecursionIn('start = start { }'); - }); - it("reports indirect left recursion", function() { expect(pass).toReportLeftRecursionIn([ 'start = stop', diff --git a/spec/compiler/passes/report-missing-rules.spec.js b/spec/compiler/passes/report-missing-rules.spec.js index edd221d..6acaa47 100644 --- a/spec/compiler/passes/report-missing-rules.spec.js +++ b/spec/compiler/passes/report-missing-rules.spec.js @@ -51,6 +51,10 @@ describe("compiler pass |reportMissingRules|", function() { expect(pass).toReportMissingRuleIn('start = "a" / "b" / missing'); }); + it("reports missing rule referenced from an action", function() { + expect(pass).toReportMissingRuleIn('start = missing { }'); + }); + it("reports missing rule referenced from a sequence", function() { expect(pass).toReportMissingRuleIn('start = missing "a" "b"'); expect(pass).toReportMissingRuleIn('start = "a" "b" missing'); @@ -79,8 +83,4 @@ describe("compiler pass |reportMissingRules|", function() { it("reports missing rule referenced from a one or more", function() { expect(pass).toReportMissingRuleIn('start = missing+'); }); - - it("reports missing rule referenced from an action", function() { - expect(pass).toReportMissingRuleIn('start = missing { }'); - }); }); diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index adc9bd9..0310388 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -197,6 +197,96 @@ describe("generated parser", function() { }); }); + describe("action code", function() { + it("tranforms the expression result by returnung a non-|null| value", function() { + var parser = PEG.buildParser('start = "a" { return 42; }', options); + + expect(parser).toParse("a", 42); + }); + + it("causes match failure by returning |null|", function() { + var parser = PEG.buildParser('start = "a" { return null; }', options); + + expect(parser).toFailToParse("a"); + }); + + it("is not called when the expression does not match", function() { + var parser = PEG.buildParser( + 'start = "a" { throw "Boom!"; } / "b"', + options + ); + + expect(parser).toParse("b", "b"); + }); + + it("can use label variables", function() { + var parser = PEG.buildParser('start = a:"a" { return a; }', options); + + expect(parser).toParse("a", "a"); + }); + + it("can use the |offset| variable to get the current parse position", function() { + var parser = PEG.buildParser( + 'start = "a" ("b" { return offset; })', + options + ); + + expect(parser).toParse("ab", ["a", 1]); + }); + + if (options.trackLineAndColumn) { + it("can use the |line| and |column| variables to get the current line and column", function() { + var parser = PEG.buildParser([ + '{ var result; }', + 'start = line (nl+ line)* { return result; }', + 'line = thing (" "+ thing)*', + 'thing = digit / mark', + 'digit = [0-9]', + 'mark = "x" { result = [line, column]; }', + 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' + ].join("\n"), options); + + expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); + + /* Non-Unix newlines */ + expect(parser).toParse("1\rx", [2, 1]); // Old Mac + expect(parser).toParse("1\r\nx", [2, 1]); // Windows + expect(parser).toParse("1\n\rx", [3, 1]); // mismatched + + /* Strange newlines */ + expect(parser).toParse("1\u2028x", [2, 1]); // line separator + expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator + }); + } + + it("can use variables defined in the initializer", function() { + var parser = PEG.buildParser([ + '{ var v = 42 }', + 'start = "a" { return v; }' + ].join("\n"), options); + + expect(parser).toParse("a", 42); + }); + + it("can use functions defined in the initializer", function() { + var parser = PEG.buildParser([ + '{ function f() { return 42; } }', + 'start = "a" { return f(); }' + ].join("\n"), options); + + expect(parser).toParse("a", 42); + }); + + it("does not advance position when the expression matches but the action returns |null|", function() { + var parser = PEG.buildParser( + 'start = "a" { return null; } / "a"', + options + ); + + expect(parser).toParse("a", "a"); + }); + }); + describe("sequence matching", function() { it("matches empty sequence correctly", function() { var parser = PEG.buildParser('start = ', options); @@ -447,96 +537,6 @@ describe("generated parser", function() { }); }); - describe("action code", function() { - it("tranforms the expression result by returnung a non-|null| value", function() { - var parser = PEG.buildParser('start = "a" { return 42; }', options); - - expect(parser).toParse("a", 42); - }); - - it("causes match failure by returning |null|", function() { - var parser = PEG.buildParser('start = "a" { return null; }', options); - - expect(parser).toFailToParse("a"); - }); - - it("is not called when the expression does not match", function() { - var parser = PEG.buildParser( - 'start = "a" { throw "Boom!"; } / "b"', - options - ); - - expect(parser).toParse("b", "b"); - }); - - it("can use label variables", function() { - var parser = PEG.buildParser('start = a:"a" { return a; }', options); - - expect(parser).toParse("a", "a"); - }); - - it("can use the |offset| variable to get the current parse position", function() { - var parser = PEG.buildParser( - 'start = "a" ("b" { return offset; })', - options - ); - - expect(parser).toParse("ab", ["a", 1]); - }); - - if (options.trackLineAndColumn) { - it("can use the |line| and |column| variables to get the current line and column", function() { - var parser = PEG.buildParser([ - '{ var result; }', - 'start = line (nl+ line)* { return result; }', - 'line = thing (" "+ thing)*', - 'thing = digit / mark', - 'digit = [0-9]', - 'mark = "x" { result = [line, column]; }', - 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' - ].join("\n"), options); - - expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); - - /* Non-Unix newlines */ - expect(parser).toParse("1\rx", [2, 1]); // Old Mac - expect(parser).toParse("1\r\nx", [2, 1]); // Windows - expect(parser).toParse("1\n\rx", [3, 1]); // mismatched - - /* Strange newlines */ - expect(parser).toParse("1\u2028x", [2, 1]); // line separator - expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator - }); - } - - it("can use variables defined in the initializer", function() { - var parser = PEG.buildParser([ - '{ var v = 42 }', - 'start = "a" { return v; }' - ].join("\n"), options); - - expect(parser).toParse("a", 42); - }); - - it("can use functions defined in the initializer", function() { - var parser = PEG.buildParser([ - '{ function f() { return 42; } }', - 'start = "a" { return f(); }' - ].join("\n"), options); - - expect(parser).toParse("a", 42); - }); - - it("does not advance position when the expression matches but the action returns |null|", function() { - var parser = PEG.buildParser( - 'start = "a" { return null; } / "a"', - options - ); - - expect(parser).toParse("a", "a"); - }); - }); - describe("rule reference matching", function() { it("follows rule references", function() { var parser = PEG.buildParser([ diff --git a/src/compiler/passes/compute-params.js b/src/compiler/passes/compute-params.js index 68379e8..926c7de 100644 --- a/src/compiler/passes/compute-params.js +++ b/src/compiler/passes/compute-params.js @@ -52,6 +52,14 @@ PEG.compiler.passes.computeParams = function(ast) { scoped(function() { each(node.alternatives, compute); }); }, + action: + function(node) { + scoped(function() { + compute(node.expression); + computeParams(node); + }); + }, + sequence: function(node) { var env = envs[envs.length - 1], name; @@ -86,15 +94,6 @@ PEG.compiler.passes.computeParams = function(ast) { optional: computeForScopedExpression, zero_or_more: computeForScopedExpression, one_or_more: computeForScopedExpression, - - action: - function(node) { - scoped(function() { - compute(node.expression); - computeParams(node); - }); - }, - rule_ref: nop, literal: nop, "class": nop, diff --git a/src/compiler/passes/compute-var-names.js b/src/compiler/passes/compute-var-names.js index 7a28fc5..6121fd3 100644 --- a/src/compiler/passes/compute-var-names.js +++ b/src/compiler/passes/compute-var-names.js @@ -80,6 +80,8 @@ PEG.compiler.passes.computeVarNames = function(ast) { }; }, + action: computeFromExpression({ result: 0, pos: 1 }), + sequence: function(node, index) { var depths = map(node.elements, function(element, i) { @@ -116,7 +118,6 @@ PEG.compiler.passes.computeVarNames = function(ast) { optional: computeFromExpression({ result: 0, pos: 0 }), zero_or_more: computeFromExpression({ result: 1, pos: 0 }), one_or_more: computeFromExpression({ result: 1, pos: 0 }), - action: computeFromExpression({ result: 0, pos: 1 }), rule_ref: computeLeaf, literal: computeLeaf, "class": computeLeaf, diff --git a/src/compiler/passes/generate-code.js b/src/compiler/passes/generate-code.js index 67d89be..a74f089 100644 --- a/src/compiler/passes/generate-code.js +++ b/src/compiler/passes/generate-code.js @@ -593,6 +593,16 @@ PEG.compiler.passes.generateCode = function(ast, options) { ' #block code', '}' ], + action: [ + '#{posSave(node)};', + '#block emit(node.expression)', + 'if (#{node.resultVar} !== null) {', + ' #{node.resultVar} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? [node.posVar + ".offset", node.posVar + ".line", node.posVar + ".column"] : [node.posVar]).concat(values(node.params)).join(", ")});', + '}', + 'if (#{node.resultVar} === null) {', + ' #{posRestore(node)};', + '}' + ], sequence: [ '#{posSave(node)};', '#block code' @@ -663,16 +673,6 @@ PEG.compiler.passes.generateCode = function(ast, options) { ' #{node.resultVar} = null;', '}' ], - action: [ - '#{posSave(node)};', - '#block emit(node.expression)', - 'if (#{node.resultVar} !== null) {', - ' #{node.resultVar} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? [node.posVar + ".offset", node.posVar + ".line", node.posVar + ".column"] : [node.posVar]).concat(values(node.params)).join(", ")});', - '}', - 'if (#{node.resultVar} === null) {', - ' #{posRestore(node)};', - '}' - ], rule_ref: [ '#{node.resultVar} = parse_#{node.name}();' ], @@ -832,6 +832,8 @@ PEG.compiler.passes.generateCode = function(ast, options) { return code; }, + action: emitSimple("action"), + sequence: function(node) { var code = fill("sequence.inner", { node: node }); @@ -855,7 +857,6 @@ PEG.compiler.passes.generateCode = function(ast, options) { optional: emitSimple("optional"), zero_or_more: emitSimple("zero_or_more"), one_or_more: emitSimple("one_or_more"), - action: emitSimple("action"), rule_ref: emitSimple("rule_ref"), literal: emitSimple("literal"), diff --git a/src/compiler/passes/report-left-recursion.js b/src/compiler/passes/report-left-recursion.js index caef1cf..60251e8 100644 --- a/src/compiler/passes/report-left-recursion.js +++ b/src/compiler/passes/report-left-recursion.js @@ -24,6 +24,7 @@ PEG.compiler.passes.reportLeftRecursion = function(ast) { named: checkExpression, choice: checkSubnodes("alternatives"), + action: checkExpression, sequence: function(node, appliedRules) { @@ -40,7 +41,6 @@ PEG.compiler.passes.reportLeftRecursion = function(ast) { optional: checkExpression, zero_or_more: checkExpression, one_or_more: checkExpression, - action: checkExpression, rule_ref: function(node, appliedRules) { diff --git a/src/compiler/passes/report-missing-rules.js b/src/compiler/passes/report-missing-rules.js index 35add74..0a14877 100644 --- a/src/compiler/passes/report-missing-rules.js +++ b/src/compiler/passes/report-missing-rules.js @@ -13,6 +13,7 @@ PEG.compiler.passes.reportMissingRules = function(ast) { rule: checkExpression, named: checkExpression, choice: checkSubnodes("alternatives"), + action: checkExpression, sequence: checkSubnodes("elements"), labeled: checkExpression, simple_and: checkExpression, @@ -22,7 +23,6 @@ PEG.compiler.passes.reportMissingRules = function(ast) { optional: checkExpression, zero_or_more: checkExpression, one_or_more: checkExpression, - action: checkExpression, rule_ref: function(node) {