From 2c8b323adef5a5500411e42e1c39a6bc49350a5b Mon Sep 17 00:00:00 2001 From: David Majda Date: Sat, 30 Jun 2012 21:39:35 +0200 Subject: [PATCH] Replace variable name computations by computations of indices This commit replaces all variable name computations in |computeVarNames| and |computeParams| passes by computations of indices. The actual names are computed later in the |generateCode| pass. This change makes the code generator the only place that deals with the actual variable names, making them easier to change for example. The code generator code seems bit more complicated after the change, but this complexity will pay off (and mostly disappear) later. --- spec/compiler/passes/compute-params.spec.js | 69 +++--- .../passes/compute-var-indices.spec.js | 229 ++++++++++++++++++ .../compiler/passes/compute-var-names.spec.js | 229 ------------------ spec/index.html | 2 +- src/compiler.js | 2 +- src/compiler/passes.js | 2 +- src/compiler/passes/compute-params.js | 15 +- ...te-var-names.js => compute-var-indices.js} | 43 ++-- src/compiler/passes/generate-code.js | 105 ++++---- 9 files changed, 353 insertions(+), 343 deletions(-) create mode 100644 spec/compiler/passes/compute-var-indices.spec.js delete mode 100644 spec/compiler/passes/compute-var-names.spec.js rename src/compiler/passes/{compute-var-names.js => compute-var-indices.js} (68%) diff --git a/spec/compiler/passes/compute-params.spec.js b/spec/compiler/passes/compute-params.spec.js index ec1bdee..16292f4 100644 --- a/spec/compiler/passes/compute-params.spec.js +++ b/spec/compiler/passes/compute-params.spec.js @@ -1,9 +1,20 @@ describe("compiler pass |computeParams|", function() { function pass(ast) { - PEG.compiler.passes.computeVarNames(ast); + PEG.compiler.passes.computeVarIndices(ast); PEG.compiler.passes.computeParams(ast); } + var result0 = { resultIndex: 0, subindices: [] }, + result0_0 = { resultIndex: 0, subindices: [0] }, + result0_1 = { resultIndex: 0, subindices: [1] }, + result0_1_0 = { resultIndex: 0, subindices: [1, 0] }, + result0_1_1 = { resultIndex: 0, subindices: [1, 1] }, + result0_1_2 = { resultIndex: 0, subindices: [1, 2] }, + result0_2 = { resultIndex: 0, subindices: [2] }, + result1_9 = { resultIndex: 1, subindices: [9] }, + result1 = { resultIndex: 1, subindices: [] }; + result2 = { resultIndex: 2, subindices: [] }; + function ruleDetails(details) { return { rules: [details] }; } function expressionDetails(details) { @@ -17,7 +28,7 @@ describe("compiler pass |computeParams|", function() { describe("basic cases", function() { it("computes params for an action", function() { expect(pass).toChangeAST('start = a:"a" { }', expressionDetails({ - params: { a: "result0" } + params: { a: result0 } })); }); @@ -25,7 +36,7 @@ describe("compiler pass |computeParams|", function() { expect(pass).toChangeAST('start = a:"a" &{ }', expressionDetails({ elements: [ {}, - { params: { a: "result0" } } + { params: { a: result0 } } ] })); }); @@ -34,7 +45,7 @@ describe("compiler pass |computeParams|", function() { expect(pass).toChangeAST('start = a:"a" !{ }', expressionDetails({ elements: [ {}, - { params: { a: "result0" } } + { params: { a: result0 } } ] })); }); @@ -44,7 +55,7 @@ describe("compiler pass |computeParams|", function() { it("computes params for a named", function() { expect(pass).toChangeAST( 'start "start" = a:"a" { }', - innerExpressionDetails({ params: { a: "result0" } }) + innerExpressionDetails({ params: { a: result0 } }) ); }); @@ -52,20 +63,20 @@ describe("compiler pass |computeParams|", function() { expect(pass).toChangeAST( 'start = a:"a" { } / "b" / "c"', expressionDetails({ - alternatives: [{ params: { a: "result0" } }, {}, {}] + alternatives: [{ params: { a: result0 } }, {}, {}] }) ); expect(pass).toChangeAST( 'start = "a" / "b" / c:"c" { }', expressionDetails({ - alternatives: [{}, {}, { params: { c: "result0" } }] + alternatives: [{}, {}, { params: { c: result0 } }] }) ); }); it("computes params for an action", function() { expect(pass).toChangeAST('start = (a:"a" { }) { }', innerExpressionDetails({ - params: { a: "result0" } + params: { a: result0 } })); }); @@ -73,50 +84,50 @@ describe("compiler pass |computeParams|", function() { expect(pass).toChangeAST( 'start = (a:"a" { }) "b" "c"', expressionDetails({ - elements: [{ params: { a: "result0" } }, {}, {}] + elements: [{ params: { a: result0 } }, {}, {}] }) ); expect(pass).toChangeAST( 'start = "a" "b" (c:"c" { })', expressionDetails({ - elements: [{}, {}, { params: { c: "result2" } }] + elements: [{}, {}, { params: { c: result2 } }] }) ); }); it("computes params for a labeled", function() { expect(pass).toChangeAST('start = a:(b:"b" { })', innerExpressionDetails({ - params: { b: "result0" } + params: { b: result0 } })); }); it("computes params for a simple and", function() { expect(pass).toChangeAST('start = &(a:"a" { })', innerExpressionDetails({ - params: { a: "result0" } + params: { a: result0 } })); }); it("computes params for a simple not", function() { expect(pass).toChangeAST('start = &(a:"a" { })', innerExpressionDetails({ - params: { a: "result0" } + params: { a: result0 } })); }); it("computes params for an optional", function() { expect(pass).toChangeAST('start = (a:"a" { })?', innerExpressionDetails({ - params: { a: "result0" } + params: { a: result0 } })); }); it("computes params for a zero or more", function() { expect(pass).toChangeAST('start = (a:"a" { })*', innerExpressionDetails({ - params: { a: "result1" } + params: { a: result1 } })); }); it("computes params for a one or more", function() { expect(pass).toChangeAST('start = (a:"a" { })+', innerExpressionDetails({ - params: { a: "result1" } + params: { a: result1 } })); }); }); @@ -139,38 +150,26 @@ describe("compiler pass |computeParams|", function() { expect(pass).toChangeAST( 'start = a:"a" b:"b" c:"c" { }', expressionDetails({ - params: { a: "result0[0]", b: "result0[1]", c: "result0[2]" } + params: { a: result0_0, b: result0_1, c: result0_2 } }) ); expect(pass).toChangeAST( 'start = a:"a" (b:"b" c:"c" d:"d") e:"e"{ }', expressionDetails({ params: { - a: "result0[0]", - b: "result0[1][0]", - c: "result0[1][1]", - d: "result0[1][2]", - e: "result0[2]" + a: result0_0, + b: result0_1_0, + c: result0_1_1, + d: result0_1_2, + e: result0_2 } }) ); - - /* - * Regression tests for a bug where e.g. resultVar names like |result10| - * were incorrectly treated as names derived from |result1|, leading to - * incorrect substitution. - */ - expect(pass).toChangeAST( - 'start = ("a" "b" "c" "d" "e" "f" "g" "h" "i" j:"j" { })*', - innerExpressionDetails({ - params: { j: "result1[9]" } // Buggy code put "result1[0]0" here. - }) - ); }); it("creates a new scope for a labeled", function() { expect(pass).toChangeAST('start = a:(b:"b") { }', expressionDetails({ - params: { a: "result0"} + params: { a: result0 } })); }); diff --git a/spec/compiler/passes/compute-var-indices.spec.js b/spec/compiler/passes/compute-var-indices.spec.js new file mode 100644 index 0000000..d57bac0 --- /dev/null +++ b/spec/compiler/passes/compute-var-indices.spec.js @@ -0,0 +1,229 @@ +describe("compiler pass |computeVarIndices|", function() { + var pass = PEG.compiler.passes.computeVarIndices; + + var leafDetails = { resultIndex: 0 }, + choiceDetails = { + resultIndex: 0, + alternatives: [ + { resultIndex: 0, posIndex: 0 }, + { resultIndex: 0, posIndex: 0 }, + { resultIndex: 0, posIndex: 0 } + ] + }, + sequenceDetails = { + resultIndex: 0, + posIndex: 0, + elements: [ + { resultIndex: 0, posIndex: 1 }, + { resultIndex: 1, posIndex: 1 }, + { resultIndex: 2, posIndex: 1 } + ] + }; + + function ruleDetails(details) { return { rules: [details] }; } + + it("computes variable indices for a named", function() { + expect(pass).toChangeAST('start "start" = &"a"', ruleDetails({ + resultIndices: [0], + posIndices: [0], + expression: { + resultIndex: 0, + expression: { resultIndex: 0, posIndex: 0 } + } + })); + }); + + it("computes variable indices for a choice", function() { + expect(pass).toChangeAST('start = &"a" / &"b" / &"c"', ruleDetails({ + resultIndices: [0], + posIndices: [0], + expression: choiceDetails + })); + expect(pass).toChangeAST('start = &"a" / &"b"* / &"c"', ruleDetails({ + resultIndices: [0, 1], + posIndices: [0], + expression: choiceDetails + })); + expect(pass).toChangeAST('start = &"a" / &(&"b") / &"c"', ruleDetails({ + resultIndices: [0], + posIndices: [0, 1], + expression: choiceDetails + })); + }); + + it("computes variable indices for an action", function() { + expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({ + resultIndices: [0], + posIndices: [0, 1], + expression: { + resultIndex: 0, + posIndex: 0, + expression: { resultIndex: 0, posIndex: 1 } + } + })); + }); + + it("computes variable indices for a sequence", function() { + expect(pass).toChangeAST('start = ', ruleDetails({ + resultIndices: [0], + posIndices: [0], + expression: { resultIndex: 0, posIndex: 0 } + })); + expect(pass).toChangeAST('start = &"a" &"b" &"c"', ruleDetails({ + resultIndices: [0, 1, 2], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &"a" &"b" &"c"*', ruleDetails({ + resultIndices: [0, 1, 2, 3], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &"a" &"b"* &"c"', ruleDetails({ + resultIndices: [0, 1, 2], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &"a" &("b"*)* &"c"', ruleDetails({ + resultIndices: [0, 1, 2, 3], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &"a"* &"b" &"c"', ruleDetails({ + resultIndices: [0, 1, 2], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &("a"*)* &"b" &"c"', ruleDetails({ + resultIndices: [0, 1, 2], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &(("a"*)*)* &"b" &"c"', ruleDetails({ + resultIndices: [0, 1, 2, 3], + posIndices: [0, 1], + expression: sequenceDetails + })); + expect(pass).toChangeAST('start = &"a" &(&"b") &"c"', ruleDetails({ + resultIndices: [0, 1, 2], + posIndices: [0, 1, 2], + expression: sequenceDetails + })); + }); + + it("computes variable indices for a labeled", function() { + expect(pass).toChangeAST('start = label:&"a"', ruleDetails({ + resultIndices: [0], + posIndices: [0], + expression: { + resultIndex: 0, + expression: { resultIndex: 0, posIndex: 0 } + } + })); + }); + + it("computes variable indices for a simple and", function() { + expect(pass).toChangeAST('start = &(&"a")', ruleDetails({ + resultIndices: [0], + posIndices: [0, 1], + expression: { + resultIndex: 0, + posIndex: 0, + expression: { resultIndex: 0, posIndex: 1 } + } + })); + }); + + it("computes variable indices for a simple not", function() { + expect(pass).toChangeAST('start = !(&"a")', ruleDetails({ + resultIndices: [0], + posIndices: [0, 1], + expression: { + resultIndex: 0, + posIndex: 0, + expression: { resultIndex: 0, posIndex: 1 } + } + })); + }); + + it("computes variable indices for a semantic and", function() { + expect(pass).toChangeAST('start = &{ code }', ruleDetails({ + resultIndices: [0], + posIndices: [], + expression: leafDetails + })); + }); + + it("computes variable indices for a semantic not", function() { + expect(pass).toChangeAST('start = !{ code }', ruleDetails({ + resultIndices: [0], + posIndices: [], + expression: leafDetails + })); + }); + + it("computes variable indices for an optional", function() { + expect(pass).toChangeAST('start = (&"a")?', ruleDetails({ + resultIndices: [0], + posIndices: [0], + expression: { + resultIndex: 0, + expression: { resultIndex: 0, posIndex: 0 } + } + })); + }); + + it("computes variable indices for a zero or more", function() { + expect(pass).toChangeAST('start = (&"a")*', ruleDetails({ + resultIndices: [0, 1], + posIndices: [0], + expression: { + resultIndex: 0, + expression: { resultIndex: 1, posIndex: 0 } + } + })); + }); + + it("computes variable indices for a one or more", function() { + expect(pass).toChangeAST('start = (&"a")+', ruleDetails({ + resultIndices: [0, 1], + posIndices: [0], + expression: { + resultIndex: 0, + expression: { resultIndex: 1, posIndex: 0 } + } + })); + }); + + it("computes variable indices for a rule reference", function() { + expect(pass).toChangeAST('start = a', ruleDetails({ + resultIndices: [0], + posIndices: [], + expression: leafDetails + })); + }); + + it("computes variable indices for a literal", function() { + expect(pass).toChangeAST('start = "a"', ruleDetails({ + resultIndices: [0], + posIndices: [], + expression: leafDetails + })); + }); + + it("computes variable indices for a class", function() { + expect(pass).toChangeAST('start = [a-z]', ruleDetails({ + resultIndices: [0], + posIndices: [], + expression: leafDetails + })); + }); + + it("computes variable indices for an any", function() { + expect(pass).toChangeAST('start = .', ruleDetails({ + resultIndices: [0], + posIndices: [], + expression: leafDetails + })); + }); +}); diff --git a/spec/compiler/passes/compute-var-names.spec.js b/spec/compiler/passes/compute-var-names.spec.js deleted file mode 100644 index 8d32efe..0000000 --- a/spec/compiler/passes/compute-var-names.spec.js +++ /dev/null @@ -1,229 +0,0 @@ -describe("compiler pass |computeVarNames|", function() { - var pass = PEG.compiler.passes.computeVarNames; - - var leafDetails = { resultVar: "result0" }, - choiceDetails = { - resultVar: "result0", - alternatives: [ - { resultVar: "result0", posVar: "pos0" }, - { resultVar: "result0", posVar: "pos0" }, - { resultVar: "result0", posVar: "pos0" } - ] - }, - sequenceDetails = { - resultVar: "result0", - posVar: "pos0", - elements: [ - { resultVar: "result0", posVar: "pos1" }, - { resultVar: "result1", posVar: "pos1" }, - { resultVar: "result2", posVar: "pos1" } - ] - }; - - function ruleDetails(details) { return { rules: [details] }; } - - it("computes variable names for a named", function() { - expect(pass).toChangeAST('start "start" = &"a"', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0"], - expression: { - resultVar: "result0", - expression: { resultVar: "result0", posVar: "pos0" } - } - })); - }); - - it("computes variable names for a choice", function() { - expect(pass).toChangeAST('start = &"a" / &"b" / &"c"', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0"], - expression: choiceDetails - })); - expect(pass).toChangeAST('start = &"a" / &"b"* / &"c"', ruleDetails({ - resultVars: ["result0", "result1"], - posVars: ["pos0"], - expression: choiceDetails - })); - expect(pass).toChangeAST('start = &"a" / &(&"b") / &"c"', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0", "pos1"], - expression: choiceDetails - })); - }); - - 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"], - posVars: ["pos0"], - expression: { resultVar: "result0", posVar: "pos0" } - })); - expect(pass).toChangeAST('start = &"a" &"b" &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &"a" &"b" &"c"*', ruleDetails({ - resultVars: ["result0", "result1", "result2", "result3"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &"a" &"b"* &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &"a" &("b"*)* &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2", "result3"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &"a"* &"b" &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &("a"*)* &"b" &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &(("a"*)*)* &"b" &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2", "result3"], - posVars: ["pos0", "pos1"], - expression: sequenceDetails - })); - expect(pass).toChangeAST('start = &"a" &(&"b") &"c"', ruleDetails({ - resultVars: ["result0", "result1", "result2"], - posVars: ["pos0", "pos1", "pos2"], - expression: sequenceDetails - })); - }); - - it("computes variable names for a labeled", function() { - expect(pass).toChangeAST('start = label:&"a"', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0"], - expression: { - resultVar: "result0", - expression: { resultVar: "result0", posVar: "pos0" } - } - })); - }); - - it("computes variable names for a simple and", function() { - expect(pass).toChangeAST('start = &(&"a")', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0", "pos1"], - expression: { - resultVar: "result0", - posVar: "pos0", - expression: { resultVar: "result0", posVar: "pos1" } - } - })); - }); - - it("computes variable names for a simple not", function() { - expect(pass).toChangeAST('start = !(&"a")', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0", "pos1"], - expression: { - resultVar: "result0", - posVar: "pos0", - expression: { resultVar: "result0", posVar: "pos1" } - } - })); - }); - - it("computes variable names for a semantic and", function() { - expect(pass).toChangeAST('start = &{ code }', ruleDetails({ - resultVars: ["result0"], - posVars: [], - expression: leafDetails - })); - }); - - it("computes variable names for a semantic not", function() { - expect(pass).toChangeAST('start = !{ code }', ruleDetails({ - resultVars: ["result0"], - posVars: [], - expression: leafDetails - })); - }); - - it("computes variable names for an optional", function() { - expect(pass).toChangeAST('start = (&"a")?', ruleDetails({ - resultVars: ["result0"], - posVars: ["pos0"], - expression: { - resultVar: "result0", - expression: { resultVar: "result0", posVar: "pos0" } - } - })); - }); - - it("computes variable names for a zero or more", function() { - expect(pass).toChangeAST('start = (&"a")*', ruleDetails({ - resultVars: ["result0", "result1"], - posVars: ["pos0"], - expression: { - resultVar: "result0", - expression: { resultVar: "result1", posVar: "pos0" } - } - })); - }); - - it("computes variable names for a one or more", function() { - expect(pass).toChangeAST('start = (&"a")+', ruleDetails({ - resultVars: ["result0", "result1"], - posVars: ["pos0"], - expression: { - resultVar: "result0", - expression: { resultVar: "result1", posVar: "pos0" } - } - })); - }); - - it("computes variable names for a rule reference", function() { - expect(pass).toChangeAST('start = a', ruleDetails({ - resultVars: ["result0"], - posVars: [], - expression: leafDetails - })); - }); - - it("computes variable names for a literal", function() { - expect(pass).toChangeAST('start = "a"', ruleDetails({ - resultVars: ["result0"], - posVars: [], - expression: leafDetails - })); - }); - - it("computes variable names for a class", function() { - expect(pass).toChangeAST('start = [a-z]', ruleDetails({ - resultVars: ["result0"], - posVars: [], - expression: leafDetails - })); - }); - - it("computes variable names for an any", function() { - expect(pass).toChangeAST('start = .', ruleDetails({ - resultVars: ["result0"], - posVars: [], - expression: leafDetails - })); - }); -}); diff --git a/spec/index.html b/spec/index.html index caff802..36dc248 100644 --- a/spec/index.html +++ b/spec/index.html @@ -12,7 +12,7 @@ - +