From 05a6bad98983e9a3c15ad72dffddcadaa18e3830 Mon Sep 17 00:00:00 2001 From: David Majda Date: Sun, 11 Nov 2012 18:18:52 +0100 Subject: [PATCH] Kill the |toSource| method, introduce the |output| option Before this commit, |PEG.buildParser| always returned a parser object. The only way to get its source code was to call the |toSource| method on it. While this method worked for parsers produced by |PEG.buildParser| directly, it didn't work for parsers instantiated by executing their source code. In other words, it was unreliable. This commit remvoes the |toSource| method on generated parsers and introduces a new |output| option to |PEG.buildParser|. It allows callers to specify whether they want to get back the parser object (|options.output === "parser"|) or its source code (|options.output === "source"|). This is much better and more reliable API. --- README.md | 12 ++++++----- bin/pegjs | 7 ++++--- lib/compiler.js | 12 +++++------ lib/compiler/passes/generate-code.js | 5 +---- lib/parser.js | 5 +---- spec/generated-parser.spec.js | 31 ++++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 4c93b69..ace78e5 100644 --- a/README.md +++ b/README.md @@ -94,11 +94,10 @@ a parameter: var parser = PEG.buildParser("start = ('a' / 'b')+"); -The method will return generated parser object or throw an exception if the -grammar is invalid. The exception will contain `message` property with more -details about the error. - -To get parser’s source code, call the `toSource` method on the parser. +The method will return generated parser object or its source code as a string +(depending on the value of the `output` option — see below). It will throw an +exception if the grammar is invalid. The exception will contain `message` +property with more details about the error. You can tweak the generated parser by passing a second parameter with an options object to `PEG.buildParser`. The following options are supported: @@ -111,6 +110,9 @@ object to `PEG.buildParser`. The following options are supported: (default: `false`) * `allowedStartRules` — rules the parser will be allowed to start parsing from (default: the first rule in the grammar) + * `output` — if set to `"parser"`, the method will return generated parser + object; if set to `"source"`, it will return parser source code as a string + (default: `"parser"`) Using the Parser ---------------- diff --git a/bin/pegjs b/bin/pegjs index 0466617..4fe30ab 100755 --- a/bin/pegjs +++ b/bin/pegjs @@ -73,7 +73,8 @@ function readStream(inputStream, callback) { var exportVar = "module.exports"; var options = { cache: false, - trackLineAndColumn: false + trackLineAndColumn: false, + output: "source" }; while (args.length > 0 && isOption(args[0])) { @@ -158,7 +159,7 @@ switch (args.length) { readStream(inputStream, function(input) { try { - var parser = PEG.buildParser(input, options); + var source = PEG.buildParser(input, options); } catch (e) { if (e.line !== undefined && e.column !== undefined) { abort(e.line + ":" + e.column + ": " + e.message); @@ -167,7 +168,7 @@ readStream(inputStream, function(input) { } } - outputStream.write(exportVar + " = " + parser.toSource() + ";\n"); + outputStream.write(exportVar + " = " + source + ";\n"); if (outputStream !== process.stdout) { outputStream.end(); } diff --git a/lib/compiler.js b/lib/compiler.js index 9fc0a21..0ebd2cd 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -24,16 +24,16 @@ module.exports = { compile: function(ast, options) { if (options === undefined) { options = {}; } - var that = this; + var that = this, + output = options.output !== undefined ? options.output : "parser"; utils.each(this.appliedPassNames, function(passName) { that.passes[passName](ast, options); }); - var source = ast.code; - var result = eval(source); - result._source = source; - - return result; + switch (output) { + case "parser": return eval(ast.code); + case "source": return ast.code; + } } }; diff --git a/lib/compiler/passes/generate-code.js b/lib/compiler/passes/generate-code.js index 101ea64..f1ed480 100644 --- a/lib/compiler/passes/generate-code.js +++ b/lib/compiler/passes/generate-code.js @@ -516,10 +516,7 @@ module.exports = function(ast, options) { ' }', ' ', ' return result;', - ' },', - ' ', - ' /* Returns the parser source code. */', - ' toSource: function() { return this._source; }', + ' }', ' };', ' ', ' /* Thrown when a parser encounters a syntax error. */', diff --git a/lib/parser.js b/lib/parser.js index 8b47929..d03a5bd 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -2840,10 +2840,7 @@ module.exports = (function(){ } return result; - }, - - /* Returns the parser source code. */ - toSource: function() { return this._source; } + } }; /* Thrown when a parser encounters a syntax error. */ diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index 846a9ea..cd2e641 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -934,6 +934,37 @@ describe("generated parser", function() { }); }); + describe("output", function() { + var grammar = 'start = "a"'; + + describe("without the |output| option", function() { + it("returns a parser object", function() { + var parser = PEG.buildParser(grammar); + + expect(typeof parser).toBe("object"); + expect(parser).toParse("a", "a"); + }); + }); + + describe("when the |output| option is set to \"parser\"", function() { + it("returns a parser object", function() { + var parser = PEG.buildParser(grammar, { output: "parser" }); + + expect(typeof parser).toBe("object"); + expect(parser).toParse("a", "a"); + }); + }); + + describe("when the |output| option is set to \"source\"", function() { + it("returns a parser source code", function() { + var source = PEG.buildParser(grammar, { output: "source" }); + + expect(typeof source).toBe("string"); + expect(eval(source)).toParse("a", "a"); + }); + }); + }); + /* * Following examples are from Wikipedia, see * http://en.wikipedia.org/w/index.php?title=Parsing_expression_grammar&oldid=335106938.