Get rid of two parser variable stacks

One stack is conceptually simpler, requires less code and will make a
transition to a register-based machine easier.

Note that the stack variables are now named a bit incorrectly
(|result0|, |result1|, etc. even when they store also parse positions).
I didn't bother with renaming because a transition to a register-based
machine will follow soon and the names will change anyway.

The speed/size impact is insignificant.

Speed impact
------------
Before:     839.05 kB/s
After:      839.67 kB/s
Difference: 0.07%

Size impact
-----------
Before:     949783 b
After:      961578 b
Difference: 1.24%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
redux
David Majda 12 years ago
parent 890140d73b
commit 42d4fc6dd4

@ -13,7 +13,8 @@ describe("compiler pass |computeParams|", function() {
result0_2 = { resultIndex: 0, subindices: [2] }, result0_2 = { resultIndex: 0, subindices: [2] },
result1_9 = { resultIndex: 1, subindices: [9] }, result1_9 = { resultIndex: 1, subindices: [9] },
result1 = { resultIndex: 1, subindices: [] }; result1 = { resultIndex: 1, subindices: [] };
result2 = { resultIndex: 2, subindices: [] }; result2 = { resultIndex: 2, subindices: [] },
result4 = { resultIndex: 4, subindices: [] };
function ruleDetails(details) { return { rules: [details] }; } function ruleDetails(details) { return { rules: [details] }; }
@ -36,7 +37,7 @@ describe("compiler pass |computeParams|", function() {
expect(pass).toChangeAST('start = a:"a" &{ }', expressionDetails({ expect(pass).toChangeAST('start = a:"a" &{ }', expressionDetails({
elements: [ elements: [
{}, {},
{ params: { a: result0 } } { params: { a: result2 } }
] ]
})); }));
}); });
@ -45,7 +46,7 @@ describe("compiler pass |computeParams|", function() {
expect(pass).toChangeAST('start = a:"a" !{ }', expressionDetails({ expect(pass).toChangeAST('start = a:"a" !{ }', expressionDetails({
elements: [ elements: [
{}, {},
{ params: { a: result0 } } { params: { a: result2 } }
] ]
})); }));
}); });
@ -84,13 +85,13 @@ describe("compiler pass |computeParams|", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
'start = (a:"a" { }) "b" "c"', 'start = (a:"a" { }) "b" "c"',
expressionDetails({ expressionDetails({
elements: [{ params: { a: result0 } }, {}, {}] elements: [{ params: { a: result2 } }, {}, {}]
}) })
); );
expect(pass).toChangeAST( expect(pass).toChangeAST(
'start = "a" "b" (c:"c" { })', 'start = "a" "b" (c:"c" { })',
expressionDetails({ expressionDetails({
elements: [{}, {}, { params: { c: result2 } }] elements: [{}, {}, { params: { c: result4 } }]
}) })
); );
}); });

@ -5,18 +5,18 @@ describe("compiler pass |computeVarIndices|", function() {
choiceDetails = { choiceDetails = {
resultIndex: 0, resultIndex: 0,
alternatives: [ alternatives: [
{ resultIndex: 0, posIndex: 0 }, { resultIndex: 0, posIndex: 1 },
{ resultIndex: 0, posIndex: 0 }, { resultIndex: 0, posIndex: 1 },
{ resultIndex: 0, posIndex: 0 } { resultIndex: 0, posIndex: 1 }
] ]
}, },
sequenceDetails = { sequenceDetails = {
resultIndex: 0, resultIndex: 0,
posIndex: 0, posIndex: 1,
elements: [ elements: [
{ resultIndex: 0, posIndex: 1 }, { resultIndex: 2, posIndex: 3 },
{ resultIndex: 1, posIndex: 1 }, { resultIndex: 3, posIndex: 4 },
{ resultIndex: 2, posIndex: 1 } { resultIndex: 4, posIndex: 5 }
] ]
}; };
@ -24,124 +24,107 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for a named", function() { it("computes variable indices for a named", function() {
expect(pass).toChangeAST('start "start" = &"a"', ruleDetails({ expect(pass).toChangeAST('start "start" = &"a"', ruleDetails({
resultIndices: [0], resultIndices: [0, 1],
posIndices: [0],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 0, posIndex: 0 } expression: { resultIndex: 0, posIndex: 1 }
} }
})); }));
}); });
it("computes variable indices for a choice", function() { it("computes variable indices for a choice", function() {
expect(pass).toChangeAST('start = &"a" / &"b" / &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" / &"b" / &"c"', ruleDetails({
resultIndices: [0], resultIndices: [0, 1],
posIndices: [0],
expression: choiceDetails expression: choiceDetails
})); }));
expect(pass).toChangeAST('start = &"a" / &"b"* / &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" / &"b"* / &"c"', ruleDetails({
resultIndices: [0, 1], resultIndices: [0, 1, 2],
posIndices: [0],
expression: choiceDetails expression: choiceDetails
})); }));
expect(pass).toChangeAST('start = &"a" / &(&"b") / &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" / &(&"b") / &"c"', ruleDetails({
resultIndices: [0], resultIndices: [0, 1, 2],
posIndices: [0, 1],
expression: choiceDetails expression: choiceDetails
})); }));
}); });
it("computes variable indices for an action", function() { it("computes variable indices for an action", function() {
expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({ expect(pass).toChangeAST('start = &"a" { code }', ruleDetails({
resultIndices: [0], resultIndices: [0, 1, 2],
posIndices: [0, 1],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
posIndex: 0, posIndex: 1,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 2 }
} }
})); }));
}); });
it("computes variable indices for a sequence", function() { it("computes variable indices for a sequence", function() {
expect(pass).toChangeAST('start = ', ruleDetails({ expect(pass).toChangeAST('start = ', ruleDetails({
resultIndices: [0], resultIndices: [0, 1],
posIndices: [0], expression: { resultIndex: 0, posIndex: 1 }
expression: { resultIndex: 0, posIndex: 0 }
})); }));
expect(pass).toChangeAST('start = &"a" &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &"b" &"c"', ruleDetails({
resultIndices: [0, 1, 2], resultIndices: [0, 1, 2, 3, 4, 5],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &"b" &"c"*', ruleDetails({ expect(pass).toChangeAST('start = &"a" &"b" &"c"*', ruleDetails({
resultIndices: [0, 1, 2, 3], resultIndices: [0, 1, 2, 3, 4, 5, 6],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &"b"* &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &"b"* &"c"', ruleDetails({
resultIndices: [0, 1, 2], resultIndices: [0, 1, 2, 3, 4, 5],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &("b"*)* &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &("b"*)* &"c"', ruleDetails({
resultIndices: [0, 1, 2, 3], resultIndices: [0, 1, 2, 3, 4, 5, 6],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a"* &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a"* &"b" &"c"', ruleDetails({
resultIndices: [0, 1, 2], resultIndices: [0, 1, 2, 3, 4, 5],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &("a"*)* &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &("a"*)* &"b" &"c"', ruleDetails({
resultIndices: [0, 1, 2], resultIndices: [0, 1, 2, 3, 4, 5],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &(("a"*)*)* &"b" &"c"', ruleDetails({ expect(pass).toChangeAST('start = &(("a"*)*)* &"b" &"c"', ruleDetails({
resultIndices: [0, 1, 2, 3], resultIndices: [0, 1, 2, 3, 4, 5, 6],
posIndices: [0, 1],
expression: sequenceDetails expression: sequenceDetails
})); }));
expect(pass).toChangeAST('start = &"a" &(&"b") &"c"', ruleDetails({ expect(pass).toChangeAST('start = &"a" &(&"b") &"c"', ruleDetails({
resultIndices: [0, 1, 2], resultIndices: [0, 1, 2, 3, 4, 5],
posIndices: [0, 1, 2],
expression: sequenceDetails expression: sequenceDetails
})); }));
}); });
it("computes variable indices for a labeled", function() { it("computes variable indices for a labeled", function() {
expect(pass).toChangeAST('start = label:&"a"', ruleDetails({ expect(pass).toChangeAST('start = label:&"a"', ruleDetails({
resultIndices: [0], resultIndices: [0, 1],
posIndices: [0],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 0, posIndex: 0 } expression: { resultIndex: 0, posIndex: 1 }
} }
})); }));
}); });
it("computes variable indices for a simple and", function() { it("computes variable indices for a simple and", function() {
expect(pass).toChangeAST('start = &(&"a")', ruleDetails({ expect(pass).toChangeAST('start = &(&"a")', ruleDetails({
resultIndices: [0], resultIndices: [0, 1, 2],
posIndices: [0, 1],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
posIndex: 0, posIndex: 1,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 2 }
} }
})); }));
}); });
it("computes variable indices for a simple not", function() { it("computes variable indices for a simple not", function() {
expect(pass).toChangeAST('start = !(&"a")', ruleDetails({ expect(pass).toChangeAST('start = !(&"a")', ruleDetails({
resultIndices: [0], resultIndices: [0, 1, 2],
posIndices: [0, 1],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
posIndex: 0, posIndex: 1,
expression: { resultIndex: 0, posIndex: 1 } expression: { resultIndex: 0, posIndex: 2 }
} }
})); }));
}); });
@ -149,7 +132,6 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for a semantic and", function() { it("computes variable indices for a semantic and", function() {
expect(pass).toChangeAST('start = &{ code }', ruleDetails({ expect(pass).toChangeAST('start = &{ code }', ruleDetails({
resultIndices: [0], resultIndices: [0],
posIndices: [],
expression: leafDetails expression: leafDetails
})); }));
}); });
@ -157,40 +139,36 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for a semantic not", function() { it("computes variable indices for a semantic not", function() {
expect(pass).toChangeAST('start = !{ code }', ruleDetails({ expect(pass).toChangeAST('start = !{ code }', ruleDetails({
resultIndices: [0], resultIndices: [0],
posIndices: [],
expression: leafDetails expression: leafDetails
})); }));
}); });
it("computes variable indices for an optional", function() { it("computes variable indices for an optional", function() {
expect(pass).toChangeAST('start = (&"a")?', ruleDetails({ expect(pass).toChangeAST('start = (&"a")?', ruleDetails({
resultIndices: [0], resultIndices: [0, 1],
posIndices: [0],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 0, posIndex: 0 } expression: { resultIndex: 0, posIndex: 1 }
} }
})); }));
}); });
it("computes variable indices for a zero or more", function() { it("computes variable indices for a zero or more", function() {
expect(pass).toChangeAST('start = (&"a")*', ruleDetails({ expect(pass).toChangeAST('start = (&"a")*', ruleDetails({
resultIndices: [0, 1], resultIndices: [0, 1, 2],
posIndices: [0],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 1, posIndex: 0 } expression: { resultIndex: 1, posIndex: 2 }
} }
})); }));
}); });
it("computes variable indices for a one or more", function() { it("computes variable indices for a one or more", function() {
expect(pass).toChangeAST('start = (&"a")+', ruleDetails({ expect(pass).toChangeAST('start = (&"a")+', ruleDetails({
resultIndices: [0, 1], resultIndices: [0, 1, 2],
posIndices: [0],
expression: { expression: {
resultIndex: 0, resultIndex: 0,
expression: { resultIndex: 1, posIndex: 0 } expression: { resultIndex: 1, posIndex: 2 }
} }
})); }));
}); });
@ -198,7 +176,6 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for a rule reference", function() { it("computes variable indices for a rule reference", function() {
expect(pass).toChangeAST('start = a', ruleDetails({ expect(pass).toChangeAST('start = a', ruleDetails({
resultIndices: [0], resultIndices: [0],
posIndices: [],
expression: leafDetails expression: leafDetails
})); }));
}); });
@ -206,7 +183,6 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for a literal", function() { it("computes variable indices for a literal", function() {
expect(pass).toChangeAST('start = "a"', ruleDetails({ expect(pass).toChangeAST('start = "a"', ruleDetails({
resultIndices: [0], resultIndices: [0],
posIndices: [],
expression: leafDetails expression: leafDetails
})); }));
}); });
@ -214,7 +190,6 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for a class", function() { it("computes variable indices for a class", function() {
expect(pass).toChangeAST('start = [a-z]', ruleDetails({ expect(pass).toChangeAST('start = [a-z]', ruleDetails({
resultIndices: [0], resultIndices: [0],
posIndices: [],
expression: leafDetails expression: leafDetails
})); }));
}); });
@ -222,7 +197,6 @@ describe("compiler pass |computeVarIndices|", function() {
it("computes variable indices for an any", function() { it("computes variable indices for an any", function() {
expect(pass).toChangeAST('start = .', ruleDetails({ expect(pass).toChangeAST('start = .', ruleDetails({
resultIndices: [0], resultIndices: [0],
posIndices: [],
expression: leafDetails expression: leafDetails
})); }));
}); });

@ -1,7 +1,7 @@
/* /*
* Computes indices of variables used for storing match results and parse * Computes indices of variables used for storing match results and parse
* positions in generated code. These variables are organized as two stacks. * positions in generated code. These variables are organized as one stack. The
* The following will hold after running this pass: * following will hold after running this 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 the variable that will store a
@ -10,36 +10,31 @@
* * 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
* the variable that will store a parse position in generated code. * the variable that will store a parse position in generated code.
* *
* * All "rule" nodes will contain |resultIndices| and |posIndices| * * All "rule" nodes will contain |resultIndices| property. It will contain a
* properties. They will contain a list of values of |resultIndex| and * list of values of |resultIndex| and |posIndex| properties used in rule's
* |posIndex| properties used in rule's subnodes. (This is useful to declare * subnodes. (This is useful to declare variables in generated code.)
* variables in generated code.)
*/ */
PEG.compiler.passes.computeVarIndices = function(ast) { PEG.compiler.passes.computeVarIndices = function(ast) {
function computeLeaf(node, index) { return { result: 0, pos: 0 }; } function computeLeaf(node, index) { return 0; }
function computeFromExpression(delta) { function computeFromExpression(delta) {
return function(node, index) { return function(node, index) {
var depth; var depth;
node.expression.resultIndex = index.result + delta.result; node.expression.resultIndex = delta.result > 0
? index + delta.result + delta.pos
: node.resultIndex;
depth = compute( depth = compute(
node.expression, node.expression,
{ index + delta.result + delta.pos
result: index.result + delta.result,
pos: index.pos + delta.pos
}
); );
if (delta.pos !== 0) { if (delta.pos !== 0) {
node.posIndex = index.pos; node.posIndex = index + delta.pos;
} }
return { return depth + delta.result + delta.pos;
result: depth.result + delta.result,
pos: depth.pos + delta.pos
};
}; };
} }
@ -47,7 +42,7 @@ PEG.compiler.passes.computeVarIndices = function(ast) {
grammar: grammar:
function(node, index) { function(node, index) {
each(node.rules, function(node) { each(node.rules, function(node) {
node.resultIndex = index.result; node.resultIndex = index;
compute(node, index); compute(node, index);
}); });
}, },
@ -60,8 +55,7 @@ PEG.compiler.passes.computeVarIndices = function(ast) {
depth = compute(node.expression, index); depth = compute(node.expression, index);
node.resultIndices = range(depth.result + 1); node.resultIndices = range(depth + 1);
node.posIndices = range(depth.pos);
}, },
named: computeFromExpression({ result: 0, pos: 0 }), named: computeFromExpression({ result: 0, pos: 0 }),
@ -74,10 +68,7 @@ PEG.compiler.passes.computeVarIndices = function(ast) {
return compute(alternative, index); return compute(alternative, index);
}); });
return { return Math.max.apply(null, depths);
result: Math.max.apply(null, pluck(depths, "result")),
pos: Math.max.apply(null, pluck(depths, "pos"))
};
}, },
action: computeFromExpression({ result: 0, pos: 1 }), action: computeFromExpression({ result: 0, pos: 1 }),
@ -85,30 +76,20 @@ PEG.compiler.passes.computeVarIndices = function(ast) {
sequence: sequence:
function(node, index) { function(node, index) {
var depths = map(node.elements, function(element, i) { var depths = map(node.elements, function(element, i) {
element.resultIndex = index.result + i; element.resultIndex = index + i + 2;
return compute( return compute(element, index + i + 2);
element,
{ result: index.result + i, pos: index.pos + 1 }
);
}); });
node.posIndex = index.pos; node.posIndex = index + 1;
return { return node.elements.length > 0
result: ? Math.max.apply(
node.elements.length > 0 null,
? Math.max.apply( map(depths, function(d, i) { return i + d; })
null, )
map(depths, function(d, i) { return i + d.result; }) + 2
) : 1;
: 0,
pos:
node.elements.length > 0
? 1 + Math.max.apply(null, pluck(depths, "pos"))
: 1
};
}, },
labeled: computeFromExpression({ result: 0, pos: 0 }), labeled: computeFromExpression({ result: 0, pos: 0 }),
@ -125,5 +106,5 @@ PEG.compiler.passes.computeVarIndices = function(ast) {
any: computeLeaf any: computeLeaf
}); });
compute(ast, { result: 0, pos: 0 }); compute(ast, 0);
}; };

@ -561,9 +561,6 @@ PEG.compiler.passes.generateCode = function(ast, options) {
' #if node.resultIndices.length > 0', ' #if node.resultIndices.length > 0',
' var #{map(node.resultIndices, resultVar).join(", ")};', ' var #{map(node.resultIndices, resultVar).join(", ")};',
' #end', ' #end',
' #if node.posIndices.length > 0',
' var #{map(node.posIndices, posVar).join(", ")};',
' #end',
' ', ' ',
' #block emit(node.expression)', ' #block emit(node.expression)',
' #if options.cache', ' #if options.cache',
@ -597,7 +594,7 @@ PEG.compiler.passes.generateCode = function(ast, options) {
'#{posSave(node)};', '#{posSave(node)};',
'#block emit(node.expression)', '#block emit(node.expression)',
'if (#{resultVar(node.resultIndex)} !== null) {', 'if (#{resultVar(node.resultIndex)} !== null) {',
' #{resultVar(node.resultIndex)} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? [posVar(node.posIndex) + ".offset", posVar(node.posIndex) + ".line", posVar(node.posIndex) + ".column"] : [posVar(node.posIndex)]).concat(map(values(node.params), param)).join(", ")});', ' #{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(", ")});',
'}', '}',
'if (#{resultVar(node.resultIndex)} === null) {', 'if (#{resultVar(node.resultIndex)} === null) {',
' #{posRestore(node)};', ' #{posRestore(node)};',
@ -752,7 +749,6 @@ PEG.compiler.passes.generateCode = function(ast, options) {
vars.options = options; vars.options = options;
vars.resultVar = function(index) { return "result" + index; }; vars.resultVar = function(index) { return "result" + index; };
vars.posVar = function(index) { return "pos" + index; };
vars.param = function(param) { vars.param = function(param) {
return vars.resultVar(param.resultIndex) return vars.resultVar(param.resultIndex)
@ -781,10 +777,10 @@ PEG.compiler.passes.generateCode = function(ast, options) {
}; };
} }
vars.posSave = function(node) { vars.posSave = function(node) {
return vars.posVar(node.posIndex) + " = " + vars.posClone("pos"); return vars.resultVar(node.posIndex) + " = " + vars.posClone("pos");
}; };
vars.posRestore = function(node) { vars.posRestore = function(node) {
return "pos" + " = " + vars.posClone(vars.posVar(node.posIndex)); return "pos" + " = " + vars.posClone(vars.resultVar(node.posIndex));
}; };
return templates[name](vars); return templates[name](vars);
@ -818,9 +814,9 @@ PEG.compiler.passes.generateCode = function(ast, options) {
* set to the original value and it sets a variable with a name * set to the original value and it sets a variable with a name
* |resultVar(node.resultIndex)| to |null|. * |resultVar(node.resultIndex)| to |null|.
* *
* The code can use variables with names |resultVar(node.resultIndex)| and * The code can use variables with names |resultVar(node.resultIndex)|
* |posVar(node.posIndex)| where |node| is any of the current node's * where |node| is any of the current node's subnodes. It can't use any
* subnodes. It can't use any other variables. * other variables.
*/ */
named: emitSimple("named"), named: emitSimple("named"),

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