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

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

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

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