Mental model change: Variables do not form a stack, they are registers

This commit changes the model underlying parser variables used to store
match results and parse positions. Until now they were treated as a
stack, now they are thought of as registers. The actual behavior does
not change (yet), only the terminology.

More specifically, this commit:

  * Changes parser variable names from |result0|, |result1|, etc. to
    |r0|, |r1|, etc.

  * Changes various internal names and comments to match the new model.

  * Renames the |computeVarIndices| pass to |allocateRegisters|.
redux
David Majda 12 years ago
parent 2f3dd951e9
commit 2d36ebeb59

@ -1,5 +1,5 @@
describe("compiler pass |computeVarIndices|", function() { describe("compiler pass |allocateRegisters|", function() {
var pass = PEG.compiler.passes.computeVarIndices; var pass = PEG.compiler.passes.allocateRegisters;
var leafDetails = { resultIndex: 0 }, var leafDetails = { resultIndex: 0 },
choiceDetails = { choiceDetails = {
@ -22,35 +22,35 @@ describe("compiler pass |computeVarIndices|", function() {
function ruleDetails(details) { return { rules: [details] }; } function ruleDetails(details) { return { rules: [details] }; }
it("computes variable indices for a named", function() { it("allocates registers for a named", function() {
expect(pass).toChangeAST('start "start" = &"a"', ruleDetails({ expect(pass).toChangeAST('start "start" = &"a"', ruleDetails({
resultCount: 2, registerCount: 2,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 1 }
} }
})); }));
}); });
it("computes variable indices for a choice", function() { it("allocates registers for a choice", function() {
expect(pass).toChangeAST('start = &"a" / &"b" / &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" / &"b" / &"c"', ruleDetails({
resultCount: 2, registerCount: 2,
expression: choiceDetails expression: choiceDetails
})); }));
expect(pass).toChangeAST('start = &"a" / &"b"* / &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" / &"b"* / &"c"', ruleDetails({
resultCount: 3, registerCount: 3,
expression: choiceDetails expression: choiceDetails
})); }));
expect(pass).toChangeAST('start = &"a" / &(&"b") / &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" / &(&"b") / &"c"', ruleDetails({
resultCount: 3, registerCount: 3,
expression: choiceDetails expression: choiceDetails
})); }));
}); });
it("computes variable indices for an action", function() { it("allocates registers for an action", function() {
expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({ expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({
resultCount: 3, registerCount: 3,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
posIndex: 1, posIndex: 1,
expression: { resultIndex: 0, posIndex: 2 } expression: { resultIndex: 0, posIndex: 2 }
@ -58,59 +58,59 @@ describe("compiler pass |computeVarIndices|", function() {
})); }));
}); });
it("computes variable indices for a sequence", function() { it("allocates registers for a sequence", function() {
expect(pass).toChangeAST('start = ', ruleDetails({ expect(pass).toChangeAST('start = ', ruleDetails({
resultCount: 2, registerCount: 2,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 1 }
})); }));
expect(pass).toChangeAST('start = &"a" &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &"b" &"c"', ruleDetails({
resultCount: 6, registerCount: 6,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &"b" &"c"*', ruleDetails({ expect(pass).toChangeAST('start = &"a" &"b" &"c"*', ruleDetails({
resultCount: 7, registerCount: 7,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &"b"* &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &"b"* &"c"', ruleDetails({
resultCount: 6, registerCount: 6,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &("b"*)* &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &("b"*)* &"c"', ruleDetails({
resultCount: 7, registerCount: 7,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a"* &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a"* &"b" &"c"', ruleDetails({
resultCount: 6, registerCount: 6,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &("a"*)* &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &("a"*)* &"b" &"c"', ruleDetails({
resultCount: 6, registerCount: 6,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &(("a"*)*)* &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &(("a"*)*)* &"b" &"c"', ruleDetails({
resultCount: 7, registerCount: 7,
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &(&"b") &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &(&"b") &"c"', ruleDetails({
resultCount: 6, registerCount: 6,
expression: sequenceDetails expression: sequenceDetails
})); }));
}); });
it("computes variable indices for a labeled", function() { it("allocates registers for a labeled", function() {
expect(pass).toChangeAST('start = label:&"a"', ruleDetails({ expect(pass).toChangeAST('start = label:&"a"', ruleDetails({
resultCount: 2, registerCount: 2,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 1 }
} }
})); }));
}); });
it("computes variable indices for a simple and", function() { it("allocates registers for a simple and", function() {
expect(pass).toChangeAST('start = &(&"a")', ruleDetails({ expect(pass).toChangeAST('start = &(&"a")', ruleDetails({
resultCount: 3, registerCount: 3,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
posIndex: 1, posIndex: 1,
expression: { resultIndex: 0, posIndex: 2 } expression: { resultIndex: 0, posIndex: 2 }
@ -118,10 +118,10 @@ describe("compiler pass |computeVarIndices|", function() {
})); }));
}); });
it("computes variable indices for a simple not", function() { it("allocates registers for a simple not", function() {
expect(pass).toChangeAST('start = !(&"a")', ruleDetails({ expect(pass).toChangeAST('start = !(&"a")', ruleDetails({
resultCount: 3, registerCount: 3,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
posIndex: 1, posIndex: 1,
expression: { resultIndex: 0, posIndex: 2 } expression: { resultIndex: 0, posIndex: 2 }
@ -129,75 +129,75 @@ describe("compiler pass |computeVarIndices|", function() {
})); }));
}); });
it("computes variable indices for a semantic and", function() { it("allocates registers for a semantic and", function() {
expect(pass).toChangeAST('start = &{ code }', ruleDetails({ expect(pass).toChangeAST('start = &{ code }', ruleDetails({
resultCount: 1, registerCount: 1,
expression: leafDetails expression: leafDetails
})); }));
}); });
it("computes variable indices for a semantic not", function() { it("allocates registers for a semantic not", function() {
expect(pass).toChangeAST('start = !{ code }', ruleDetails({ expect(pass).toChangeAST('start = !{ code }', ruleDetails({
resultCount: 1, registerCount: 1,
expression: leafDetails expression: leafDetails
})); }));
}); });
it("computes variable indices for an optional", function() { it("allocates registers for an optional", function() {
expect(pass).toChangeAST('start = (&"a")?', ruleDetails({ expect(pass).toChangeAST('start = (&"a")?', ruleDetails({
resultCount: 2, registerCount: 2,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 1 }
} }
})); }));
}); });
it("computes variable indices for a zero or more", function() { it("allocates registers for a zero or more", function() {
expect(pass).toChangeAST('start = (&"a")*', ruleDetails({ expect(pass).toChangeAST('start = (&"a")*', ruleDetails({
resultCount: 3, registerCount: 3,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 1, posIndex: 2 } expression: { resultIndex: 1, posIndex: 2 }
} }
})); }));
}); });
it("computes variable indices for a one or more", function() { it("allocates registers for a one or more", function() {
expect(pass).toChangeAST('start = (&"a")+', ruleDetails({ expect(pass).toChangeAST('start = (&"a")+', ruleDetails({
resultCount: 3, registerCount: 3,
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 1, posIndex: 2 } expression: { resultIndex: 1, posIndex: 2 }
} }
})); }));
}); });
it("computes variable indices for a rule reference", function() { it("allocates registers for a rule reference", function() {
expect(pass).toChangeAST('start = a', ruleDetails({ expect(pass).toChangeAST('start = a', ruleDetails({
resultCount: 1, registerCount: 1,
expression: leafDetails expression: leafDetails
})); }));
}); });
it("computes variable indices for a literal", function() { it("allocates registers for a literal", function() {
expect(pass).toChangeAST('start = "a"', ruleDetails({ expect(pass).toChangeAST('start = "a"', ruleDetails({
resultCount: 1, registerCount: 1,
expression: leafDetails expression: leafDetails
})); }));
}); });
it("computes variable indices for a class", function() { it("allocates registers for a class", function() {
expect(pass).toChangeAST('start = [a-z]', ruleDetails({ expect(pass).toChangeAST('start = [a-z]', ruleDetails({
resultCount: 1, registerCount: 1,
expression: leafDetails expression: leafDetails
})); }));
}); });
it("computes variable indices for an any", function() { it("allocates registers for an any", function() {
expect(pass).toChangeAST('start = .', ruleDetails({ expect(pass).toChangeAST('start = .', ruleDetails({
resultCount: 1, registerCount: 1,
expression: leafDetails expression: leafDetails
})); }));
}); });
}); });

@ -1,6 +1,6 @@
describe("compiler pass |computeParams|", function() { describe("compiler pass |computeParams|", function() {
function pass(ast) { function pass(ast) {
PEG.compiler.passes.computeVarIndices(ast); PEG.compiler.passes.allocateRegisters(ast);
PEG.compiler.passes.computeParams(ast); PEG.compiler.passes.computeParams(ast);
} }

@ -12,7 +12,7 @@
<script src="compiler/passes/report-missing-rules.spec.js"></script> <script src="compiler/passes/report-missing-rules.spec.js"></script>
<script src="compiler/passes/report-left-recursion.spec.js"></script> <script src="compiler/passes/report-left-recursion.spec.js"></script>
<script src="compiler/passes/remove-proxy-rules.spec.js"></script> <script src="compiler/passes/remove-proxy-rules.spec.js"></script>
<script src="compiler/passes/compute-var-indices.spec.js"></script> <script src="compiler/passes/allocate-registers.spec.js"></script>
<script src="compiler/passes/compute-params.spec.js"></script> <script src="compiler/passes/compute-params.spec.js"></script>
<script> <script>
(function() { (function() {

@ -7,7 +7,7 @@ PEG.compiler = {
"reportMissingRules", "reportMissingRules",
"reportLeftRecursion", "reportLeftRecursion",
"removeProxyRules", "removeProxyRules",
"computeVarIndices", "allocateRegisters",
"computeParams", "computeParams",
"generateCode" "generateCode"
], ],

@ -10,6 +10,6 @@ PEG.compiler.passes = {};
// @include "passes/report-missing-rules.js" // @include "passes/report-missing-rules.js"
// @include "passes/report-left-recursion.js" // @include "passes/report-left-recursion.js"
// @include "passes/remove-proxy-rules.js" // @include "passes/remove-proxy-rules.js"
// @include "passes/compute-var-indices.js" // @include "passes/allocate-registers.js"
// @include "passes/compute-params.js" // @include "passes/compute-params.js"
// @include "passes/generate-code.js" // @include "passes/generate-code.js"

@ -1,21 +1,20 @@
/* /*
* Computes indices of variables used for storing match results and parse * Allocates registers that the generated code for each node will use to store
* positions in generated code. These variables are organized as one stack. The * match results and parse positions. The following will hold after running this
* following will hold after running this pass: * pass:
* *
* * All nodes except "grammar" and "rule" nodes will have a |resultIndex| * * All nodes except "grammar" and "rule" nodes will have a |resultIndex|
* property. It will contain an index of the variable that will store a * property. It will contain an index of a register that will store a match
* match result of the expression represented by the node in generated code. * result of the expression represented by the node in generated code.
* *
* * Some nodes will have a |posIndex| property. It will contain an index of * * Some nodes will have a |posIndex| property. It will contain an index of a
* the variable that will store a parse position in generated code. * register that will store a saved parse position in generated code.
* *
* * All "rule" nodes will contain |resultCount| property. It will contain a * * All "rule" nodes will contain a |registerCount| property. It will contain
* count of distinct values of |resultIndex| and |posIndex| properties used * the number of registers that will be used by code generated for the
* in rule's subnodes. (This is useful to declare variables in generated * rule's expression.
* code.)
*/ */
PEG.compiler.passes.computeVarIndices = function(ast) { PEG.compiler.passes.allocateRegisters = function(ast) {
function computeLeaf(node, index) { return 0; } function computeLeaf(node, index) { return 0; }
function computeFromExpression(delta) { function computeFromExpression(delta) {
@ -56,7 +55,7 @@ PEG.compiler.passes.computeVarIndices = function(ast) {
depth = compute(node.expression, index); depth = compute(node.expression, index);
node.resultCount = depth + 1; node.registerCount = depth + 1;
}, },
named: computeFromExpression({ result: 0, pos: 0 }), named: computeFromExpression({ result: 0, pos: 0 }),

@ -558,8 +558,8 @@ PEG.compiler.passes.generateCode = function(ast, options) {
' }', ' }',
' ', ' ',
' #end', ' #end',
' #if node.resultCount > 0', ' #if node.registerCount > 0',
' var #{map(range(node.resultCount), resultVar).join(", ")};', ' var #{map(range(node.registerCount), r).join(", ")};',
' #end', ' #end',
' ', ' ',
' #block emit(node.expression)', ' #block emit(node.expression)',
@ -567,17 +567,17 @@ PEG.compiler.passes.generateCode = function(ast, options) {
' ', ' ',
' cache[cacheKey] = {', ' cache[cacheKey] = {',
' nextPos: #{posClone("pos")},', ' nextPos: #{posClone("pos")},',
' result: #{resultVar(node.resultIndex)}', ' result: #{r(node.resultIndex)}',
' };', ' };',
' #end', ' #end',
' return #{resultVar(node.resultIndex)};', ' return #{r(node.resultIndex)};',
'}' '}'
], ],
named: [ named: [
'reportFailures++;', 'reportFailures++;',
'#block emit(node.expression)', '#block emit(node.expression)',
'reportFailures--;', 'reportFailures--;',
'if (reportFailures === 0 && #{resultVar(node.resultIndex)} === null) {', 'if (reportFailures === 0 && #{r(node.resultIndex)} === null) {',
' matchFailed(#{string(node.name)});', ' matchFailed(#{string(node.name)});',
'}' '}'
], ],
@ -586,17 +586,17 @@ PEG.compiler.passes.generateCode = function(ast, options) {
'#block nextAlternativesCode' '#block nextAlternativesCode'
], ],
"choice.next": [ "choice.next": [
'if (#{resultVar(node.resultIndex)} === null) {', 'if (#{r(node.resultIndex)} === null) {',
' #block code', ' #block code',
'}' '}'
], ],
action: [ action: [
'#{posSave(node)};', '#{posSave(node)};',
'#block emit(node.expression)', '#block emit(node.expression)',
'if (#{resultVar(node.resultIndex)} !== null) {', 'if (#{r(node.resultIndex)} !== null) {',
' #{resultVar(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? [resultVar(node.posIndex) + ".offset", resultVar(node.posIndex) + ".line", resultVar(node.posIndex) + ".column"] : [resultVar(node.posIndex)]).concat(map(values(node.params), param)).join(", ")});', ' #{r(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? [r(node.posIndex) + ".offset", r(node.posIndex) + ".line", r(node.posIndex) + ".column"] : [r(node.posIndex)]).concat(map(values(node.params), param)).join(", ")});',
'}', '}',
'if (#{resultVar(node.resultIndex)} === null) {', 'if (#{r(node.resultIndex)} === null) {',
' #{posRestore(node)};', ' #{posRestore(node)};',
'}' '}'
], ],
@ -606,26 +606,26 @@ PEG.compiler.passes.generateCode = function(ast, options) {
], ],
"sequence.iteration": [ "sequence.iteration": [
'#block emit(element)', '#block emit(element)',
'if (#{resultVar(element.resultIndex)} !== null) {', 'if (#{r(element.resultIndex)} !== null) {',
' #block code', ' #block code',
'} else {', '} else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
' #{posRestore(node)};', ' #{posRestore(node)};',
'}' '}'
], ],
"sequence.inner": [ "sequence.inner": [
'#{resultVar(node.resultIndex)} = [#{map(pluck(node.elements, "resultIndex"), resultVar).join(", ")}];' '#{r(node.resultIndex)} = [#{map(pluck(node.elements, "resultIndex"), r).join(", ")}];'
], ],
simple_and: [ simple_and: [
'#{posSave(node)};', '#{posSave(node)};',
'reportFailures++;', 'reportFailures++;',
'#block emit(node.expression)', '#block emit(node.expression)',
'reportFailures--;', 'reportFailures--;',
'if (#{resultVar(node.resultIndex)} !== null) {', 'if (#{r(node.resultIndex)} !== null) {',
' #{resultVar(node.resultIndex)} = "";', ' #{r(node.resultIndex)} = "";',
' #{posRestore(node)};', ' #{posRestore(node)};',
'} else {', '} else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
'}' '}'
], ],
simple_not: [ simple_not: [
@ -633,49 +633,49 @@ PEG.compiler.passes.generateCode = function(ast, options) {
'reportFailures++;', 'reportFailures++;',
'#block emit(node.expression)', '#block emit(node.expression)',
'reportFailures--;', 'reportFailures--;',
'if (#{resultVar(node.resultIndex)} === null) {', 'if (#{r(node.resultIndex)} === null) {',
' #{resultVar(node.resultIndex)} = "";', ' #{r(node.resultIndex)} = "";',
'} else {', '} else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
' #{posRestore(node)};', ' #{posRestore(node)};',
'}' '}'
], ],
semantic_and: [ semantic_and: [
'#{resultVar(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? ["pos.offset", "pos.line", "pos.column"] : ["pos"]).concat(map(values(node.params), param)).join(", ")}) ? "" : null;' '#{r(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? ["pos.offset", "pos.line", "pos.column"] : ["pos"]).concat(map(values(node.params), param)).join(", ")}) ? "" : null;'
], ],
semantic_not: [ semantic_not: [
'#{resultVar(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? ["pos.offset", "pos.line", "pos.column"] : ["pos"]).concat(map(values(node.params), param)).join(", ")}) ? null : "";' '#{r(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? ["pos.offset", "pos.line", "pos.column"] : ["pos"]).concat(map(values(node.params), param)).join(", ")}) ? null : "";'
], ],
optional: [ optional: [
'#block emit(node.expression)', '#block emit(node.expression)',
'#{resultVar(node.resultIndex)} = #{resultVar(node.resultIndex)} !== null ? #{resultVar(node.resultIndex)} : "";' '#{r(node.resultIndex)} = #{r(node.resultIndex)} !== null ? #{r(node.resultIndex)} : "";'
], ],
zero_or_more: [ zero_or_more: [
'#{resultVar(node.resultIndex)} = [];', '#{r(node.resultIndex)} = [];',
'#block emit(node.expression)', '#block emit(node.expression)',
'while (#{resultVar(node.expression.resultIndex)} !== null) {', 'while (#{r(node.expression.resultIndex)} !== null) {',
' #{resultVar(node.resultIndex)}.push(#{resultVar(node.expression.resultIndex)});', ' #{r(node.resultIndex)}.push(#{r(node.expression.resultIndex)});',
' #block emit(node.expression)', ' #block emit(node.expression)',
'}' '}'
], ],
one_or_more: [ one_or_more: [
'#block emit(node.expression)', '#block emit(node.expression)',
'if (#{resultVar(node.expression.resultIndex)} !== null) {', 'if (#{r(node.expression.resultIndex)} !== null) {',
' #{resultVar(node.resultIndex)} = [];', ' #{r(node.resultIndex)} = [];',
' while (#{resultVar(node.expression.resultIndex)} !== null) {', ' while (#{r(node.expression.resultIndex)} !== null) {',
' #{resultVar(node.resultIndex)}.push(#{resultVar(node.expression.resultIndex)});', ' #{r(node.resultIndex)}.push(#{r(node.expression.resultIndex)});',
' #block emit(node.expression)', ' #block emit(node.expression)',
' }', ' }',
'} else {', '} else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
'}' '}'
], ],
rule_ref: [ rule_ref: [
'#{resultVar(node.resultIndex)} = parse_#{node.name}();' '#{r(node.resultIndex)} = parse_#{node.name}();'
], ],
literal: [ literal: [
'#if node.value.length === 0', '#if node.value.length === 0',
' #{resultVar(node.resultIndex)} = "";', ' #{r(node.resultIndex)} = "";',
'#else', '#else',
' #if !node.ignoreCase', ' #if !node.ignoreCase',
' #if node.value.length === 1', ' #if node.value.length === 1',
@ -695,13 +695,13 @@ PEG.compiler.passes.generateCode = function(ast, options) {
' if (input.substr(#{posOffset("pos")}, #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {', ' if (input.substr(#{posOffset("pos")}, #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {',
' #end', ' #end',
' #if !node.ignoreCase', ' #if !node.ignoreCase',
' #{resultVar(node.resultIndex)} = #{string(node.value)};', ' #{r(node.resultIndex)} = #{string(node.value)};',
' #else', ' #else',
' #{resultVar(node.resultIndex)} = input.substr(#{posOffset("pos")}, #{node.value.length});', ' #{r(node.resultIndex)} = input.substr(#{posOffset("pos")}, #{node.value.length});',
' #end', ' #end',
' #{posAdvance(node.value.length)};', ' #{posAdvance(node.value.length)};',
' } else {', ' } else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
' if (reportFailures === 0) {', ' if (reportFailures === 0) {',
' matchFailed(#{string(string(node.value))});', ' matchFailed(#{string(string(node.value))});',
' }', ' }',
@ -710,10 +710,10 @@ PEG.compiler.passes.generateCode = function(ast, options) {
], ],
"class": [ "class": [
'if (#{regexp}.test(input.charAt(#{posOffset("pos")}))) {', 'if (#{regexp}.test(input.charAt(#{posOffset("pos")}))) {',
' #{resultVar(node.resultIndex)} = input.charAt(#{posOffset("pos")});', ' #{r(node.resultIndex)} = input.charAt(#{posOffset("pos")});',
' #{posAdvance(1)};', ' #{posAdvance(1)};',
'} else {', '} else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
' if (reportFailures === 0) {', ' if (reportFailures === 0) {',
' matchFailed(#{string(node.rawText)});', ' matchFailed(#{string(node.rawText)});',
' }', ' }',
@ -721,10 +721,10 @@ PEG.compiler.passes.generateCode = function(ast, options) {
], ],
any: [ any: [
'if (input.length > #{posOffset("pos")}) {', 'if (input.length > #{posOffset("pos")}) {',
' #{resultVar(node.resultIndex)} = input.charAt(#{posOffset("pos")});', ' #{r(node.resultIndex)} = input.charAt(#{posOffset("pos")});',
' #{posAdvance(1)};', ' #{posAdvance(1)};',
'} else {', '} else {',
' #{resultVar(node.resultIndex)} = null;', ' #{r(node.resultIndex)} = null;',
' if (reportFailures === 0) {', ' if (reportFailures === 0) {',
' matchFailed("any character");', ' matchFailed("any character");',
' }', ' }',
@ -749,10 +749,10 @@ PEG.compiler.passes.generateCode = function(ast, options) {
vars.emit = emit; vars.emit = emit;
vars.options = options; vars.options = options;
vars.resultVar = function(index) { return "result" + index; }; vars.r = function(index) { return "r" + index; };
vars.param = function(param) { vars.param = function(param) {
return vars.resultVar(param.resultIndex) return vars.r(param.resultIndex)
+ map(param.subindices, function(i) { return "[" + i + "]"; }); + map(param.subindices, function(i) { return "[" + i + "]"; });
}; };
@ -778,10 +778,10 @@ PEG.compiler.passes.generateCode = function(ast, options) {
}; };
} }
vars.posSave = function(node) { vars.posSave = function(node) {
return vars.resultVar(node.posIndex) + " = " + vars.posClone("pos"); return vars.r(node.posIndex) + " = " + vars.posClone("pos");
}; };
vars.posRestore = function(node) { vars.posRestore = function(node) {
return "pos" + " = " + vars.posClone(vars.resultVar(node.posIndex)); return "pos" + " = " + vars.posClone(vars.r(node.posIndex));
}; };
return templates[name](vars); return templates[name](vars);
@ -807,17 +807,17 @@ PEG.compiler.passes.generateCode = function(ast, options) {
* input. * input.
* *
* * If the code fragment matches the input, it advances |pos| to point to * * If the code fragment matches the input, it advances |pos| to point to
* the first chracter following the matched part of the input and sets * the first chracter following the matched part of the input and sets a
* variable with a name |resultVar(node.resultIndex)| to an appropriate * register with index specified by |node.resultIndex| to an appropriate
* value. This value is always non-|null|. * value. This value is always non-|null|.
* *
* * If the code fragment does not match the input, it returns with |pos| * * If the code fragment does not match the input, it returns with |pos|
* set to the original value and it sets a variable with a name * set to the original value and it sets a register with index specified
* |resultVar(node.resultIndex)| to |null|. * by |node.posIndex| to |null|.
* *
* The code can use variables with names |resultVar(node.resultIndex)| * The code uses only registers with indices specified by |node.resultIndex|
* where |node| is any of the current node's subnodes. It can't use any * and |node.posIndex| where |node| is the processed node or some of its
* other variables. * subnodes. It does not use any other registers.
*/ */
named: emitSimple("named"), named: emitSimple("named"),

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save