You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pegjs/src/compiler/passes/allocate-registers.js

228 lines
6.0 KiB
JavaScript

/*
* Allocates registers that the generated code for each node will use to store
* match results and parse positions. For "action", "semantic_and" and
* "semantic_or" nodes it also computes visibility of labels at the point of
* action/predicate code execution and a mapping from label names to registers
* that will contain the labeled values.
*
* 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 a register that will store a match
* result of the expression represented by the node in generated code.
*
* * Some nodes will have a |posIndex| property. It will contain an index of a
* register that will store a saved parse position in generated code.
*
* * All "rule" nodes will contain a |registerCount| property. It will contain
* the number of registers that will be used by code generated for the
* rule's expression.
*
* * All "action", "semantic_and" and "semantic_or" nodes will have a |params|
* property. It will contain a mapping from names of labels visible at the
* point of action/predicate code execution to registers that will contain
* the labeled values.
*/
PEG.compiler.passes.allocateRegisters = function(ast) {
/*
* Register allocator that allocates registers from an unlimited
* integer-indexed pool. It allows allocating and releaseing registers in any
* order. It also supports reference counting (this simplifies tracking active
* registers when they store values passed to action/predicate code).
* Allocating a register allways uses the first free register (the one with
* the lowest index).
*/
var registers = (function() {
var refCounts = []; // reference count for each register that was
// allocated at least once
return {
alloc: function() {
var i;
for (i = 0; i < refCounts.length; i++) {
if (refCounts[i] === 0) {
refCounts[i] = 1;
return i;
}
}
refCounts.push(1);
return refCounts.length - 1;
},
use: function(index) {
refCounts[index]++;
},
release: function(index) {
refCounts[index]--;
},
maxIndex: function() {
return refCounts.length - 1;
},
reset: function() {
refCounts = [];
}
};
})();
/*
* Manages mapping of label names to indices of registers that will store the
* labeled values as long as they are in scope.
*/
var vars = (function(registers) {
var envs = []; // stack of nested environments
return {
beginScope: function() {
envs.push({});
},
endScope: function() {
var env = envs.pop(), name;
for (name in env) {
registers.release(env[name]);
}
},
add: function(name, index) {
envs[envs.length - 1][name] = index;
registers.use(index);
},
buildParams: function() {
var env = envs[envs.length - 1], params = {}, name;
for (name in env) {
params[name] = env[name];
}
return params;
}
};
})(registers);
function savePos(node, f) {
node.posIndex = registers.alloc();
f();
registers.release(node.posIndex);
}
function reuseResult(node, subnode) {
subnode.resultIndex = node.resultIndex;
}
function allocResult(node, f) {
node.resultIndex = registers.alloc();
f();
registers.release(node.resultIndex);
}
function scoped(f) {
vars.beginScope();
f();
vars.endScope();
}
function nop() {}
function computeExpressionScoped(node) {
scoped(function() { compute(node.expression); })
}
function computeExpressionScopedReuseResult(node) {
reuseResult(node, node.expression);
computeExpressionScoped(node);
}
function computeExpressionScopedAllocResult(node) {
allocResult(node.expression, function() { computeExpressionScoped(node); });
}
function computeExpressionScopedReuseResultSavePos(node) {
savePos(node, function() { computeExpressionScopedReuseResult(node); });
}
function computeParams(node) {
node.params = vars.buildParams();
}
var compute = buildNodeVisitor({
grammar:
function(node) {
each(node.rules, compute);
},
rule:
function(node) {
registers.reset();
computeExpressionScopedAllocResult(node);
node.registerCount = registers.maxIndex() + 1;
},
named:
function(node) {
reuseResult(node, node.expression);
compute(node.expression);
},
choice:
function(node) {
each(node.alternatives, function(alternative) {
reuseResult(node, alternative);
scoped(function() {
compute(alternative);
});
});
},
action:
function(node) {
savePos(node, function() {
reuseResult(node, node.expression);
scoped(function() {
compute(node.expression);
computeParams(node);
});
});
},
sequence:
function(node) {
savePos(node, function() {
each(node.elements, function(element) {
element.resultIndex = registers.alloc();
compute(element);
});
each(node.elements, function(element) {
registers.release(element.resultIndex);
});
});
},
labeled:
function(node) {
vars.add(node.label, node.resultIndex);
computeExpressionScopedReuseResult(node);
},
simple_and: computeExpressionScopedReuseResultSavePos,
simple_not: computeExpressionScopedReuseResultSavePos,
semantic_and: computeParams,
semantic_not: computeParams,
optional: computeExpressionScopedReuseResult,
zero_or_more: computeExpressionScopedAllocResult,
one_or_more: computeExpressionScopedAllocResult,
rule_ref: nop,
literal: nop,
"class": nop,
any: nop
});
compute(ast);
};