Skip to content

Commit

Permalink
Plugin API: Split compiler passes into stages
Browse files Browse the repository at this point in the history
The compiler passes are now split into three stages:

  * check -- passes that check for various error conditions

  * transform -- passes that transform the AST (e.g. to perform
    optimizations)

  * generate -- passes that are related to code generation

Splitting the passes into stages is important for plugins. For example,
if a plugin wants to add a new optimization pass, it can add it at the
end of the "transform" stage without any knowledge about other passes it
contains. Similarly, if it wants to generate something else than the
default code generator does from the AST, it can just replace all passes
in the "generate" stage by its own one(s).

More generally, the stages make it possible to write plugins that do not
depend on names and actions of specific passes (which I consider
internal and subject of change), just on the definition of stages (which
I consider a public API with to which semver rules apply).

Implements part of pegjsGH-106.
  • Loading branch information
dmajda committed Jan 13, 2013
1 parent 76f5c88 commit e4b5588
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 12 deletions.
31 changes: 24 additions & 7 deletions lib/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ module.exports = {
* |PEG.GrammarError|.
*/
passes: {
reportMissingRules: require("./compiler/passes/report-missing-rules"),
reportLeftRecursion: require("./compiler/passes/report-left-recursion"),
removeProxyRules: require("./compiler/passes/remove-proxy-rules"),
generateBytecode: require("./compiler/passes/generate-bytecode"),
generateJavascript: require("./compiler/passes/generate-javascript")
check: {
reportMissingRules: require("./compiler/passes/report-missing-rules"),
reportLeftRecursion: require("./compiler/passes/report-left-recursion")
},
transform: {
removeProxyRules: require("./compiler/passes/remove-proxy-rules")
},
generate: {
generateBytecode: require("./compiler/passes/generate-bytecode"),
generateJavascript: require("./compiler/passes/generate-javascript")
}
},

/*
Expand All @@ -23,7 +29,16 @@ module.exports = {
* cause its malfunction.
*/
compile: function(ast, passes) {
var options = arguments.length > 2 ? utils.clone(arguments[2]) : {};
var options = arguments.length > 2 ? utils.clone(arguments[2]) : {},
stage;

/*
* Extracted into a function just to silence JSHint complaining about
* creating functions in a loop.
*/
function runPass(pass) {
pass(ast, options);
}

utils.defaults(options, {
allowedStartRules: [ast.rules[0].name],
Expand All @@ -32,7 +47,9 @@ module.exports = {
output: "parser"
});

utils.each(passes, function(p) { p(ast, options); });
for (stage in passes) {
utils.each(passes[stage], runPass);
}

switch (options.output) {
case "parser": return eval(ast.code);
Expand Down
12 changes: 11 additions & 1 deletion lib/peg.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,21 @@ module.exports = {
* generated parser and cause its malfunction.
*/
buildParser: function(grammar) {
function convertPasses(passes) {
var converted = {}, stage;

for (stage in passes) {
converted[stage] = utils.values(passes[stage]);
}

return converted;
}

var options = arguments.length > 1 ? utils.clone(arguments[1]) : {},
plugins = "plugins" in options ? options.plugins : [],
config = {
parser: this.parser,
passes: utils.values(this.compiler.passes)
passes: convertPasses(this.compiler.passes)
};

utils.each(plugins, function(p) { p.use(config, options); });
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/passes/generate-bytecode.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
describe("compiler pass |generateBytecode|", function() {
var pass = PEG.compiler.passes.generateBytecode;
var pass = PEG.compiler.passes.generate.generateBytecode;

function bytecodeDetails(bytecode) {
return {
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/passes/remove-proxy-rules.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
describe("compiler pass |removeProxyRules|", function() {
var pass = PEG.compiler.passes.removeProxyRules;
var pass = PEG.compiler.passes.transform.removeProxyRules;

function proxyGrammar(rule) {
return [rule, 'proxy = proxied', 'proxied = "a"'].join("\n");
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/passes/report-left-recursion.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
describe("compiler pass |reportLeftRecursion|", function() {
var pass = PEG.compiler.passes.reportLeftRecursion;
var pass = PEG.compiler.passes.check.reportLeftRecursion;

beforeEach(function() {
this.addMatchers({
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/passes/report-missing-rules.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
describe("compiler pass |reportMissingRules|", function() {
var pass = PEG.compiler.passes.reportMissingRules;
var pass = PEG.compiler.passes.check.reportMissingRules;

beforeEach(function() {
this.addMatchers({
Expand Down

0 comments on commit e4b5588

Please sign in to comment.