diff --git a/lib/compiler/index.js b/lib/compiler/index.js index 44ebd29..e788e31 100644 --- a/lib/compiler/index.js +++ b/lib/compiler/index.js @@ -11,19 +11,7 @@ let reportUndefinedRules = require("./passes/report-undefined-rules"); let visitor = require("./visitor"); function processOptions(options, defaults) { - let processedOptions = {}; - - Object.keys(options).forEach(name => { - processedOptions[name] = options[name]; - }); - - Object.keys(defaults).forEach(name => { - if (!Object.prototype.hasOwnProperty.call(processedOptions, name)) { - processedOptions[name] = defaults[name]; - } - }); - - return processedOptions; + return Object.assign({}, defaults, options); } let compiler = { @@ -57,10 +45,8 @@ let compiler = { // if the AST contains a semantic error. Note that not all errors are detected // during the generation and some may protrude to the generated parser and // cause its malfunction. - compile(ast, passes, options) { - options = options !== undefined ? options : {}; - - options = processOptions(options, { + compile(ast, passes, options = {}) { + let processedOptions = processOptions(options, { allowedStartRules: [ast.rules[0].name], cache: false, dependencies: {}, @@ -71,19 +57,20 @@ let compiler = { trace: false }); - Object.keys(passes).forEach(stage => { - passes[stage].forEach(p => { p(ast, options); }); + Object.values(passes).forEach((stagePasses) => { + stagePasses.forEach(pass => { pass(ast, processedOptions); }); }); - switch (options.output) { + switch (processedOptions.output) { case "parser": return eval(ast.code); - + case "source": return ast.code; - + + // FIXME: Move to Validatem code at entrypoint default: - throw new Error("Invalid output format: " + options.output + "."); + throw new Error("Invalid output format: " + processedOptions.output + "."); } } }; diff --git a/lib/compiler/js.js b/lib/compiler/js.js index 8c70c71..b3ad50f 100644 --- a/lib/compiler/js.js +++ b/lib/compiler/js.js @@ -1,6 +1,11 @@ "use strict"; -function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } +function hex(character) { + return character + .charCodeAt(0) + .toString(16) + .toUpperCase(); +} // JavaScript code generation helpers. let js = { diff --git a/lib/compiler/passes/generate-js.js b/lib/compiler/passes/generate-js.js index 12c1da8..150d085 100644 --- a/lib/compiler/passes/generate-js.js +++ b/lib/compiler/passes/generate-js.js @@ -1204,7 +1204,7 @@ function generateJS(ast, options) { function generateWrapper(toplevelCode) { function generateGeneratedByComment() { return [ - "// Generated by PEG.js 0.10.0.", + `// Generated by PEG.js ${require("../../../package.json").version}.`, "//", "// https://pegjs.org/" ].join("\n"); diff --git a/lib/peg.js b/lib/peg.js index 4da4195..0914a3a 100644 --- a/lib/peg.js +++ b/lib/peg.js @@ -1,12 +1,14 @@ "use strict"; +const mapObj = require("map-obj"); + let GrammarError = require("./grammar-error"); let compiler = require("./compiler"); let parser = require("./parser"); -let peg = { +module.exports = { // PEG.js version (uses semantic versioning). - VERSION: "0.10.0", + VERSION: require("../package.json").version, GrammarError: GrammarError, parser: parser, @@ -21,34 +23,20 @@ let peg = { // |peg.GrammarError| if it contains a semantic error. Note that not all // errors are detected during the generation and some may protrude to the // generated parser and cause its malfunction. - generate(grammar, options) { - options = options !== undefined ? options : {}; - - function convertPasses(passes) { - let converted = {}; - - Object.keys(passes).forEach(stage => { - converted[stage] = Object.keys(passes[stage]) - .map(name => passes[stage][name]); - }); - - return converted; - } - - let plugins = "plugins" in options ? options.plugins : []; + generate(grammar, options = {}) { + // FIXME: Validatem + let config = { - parser: peg.parser, - passes: convertPasses(peg.compiler.passes) + parser: parser, + passes: mapObj(compiler.passes, (stage, passesForStage) => { + return [ stage, Object.values(passesForStage) ]; + }) }; - plugins.forEach(p => { p.use(config, options); }); - - return peg.compiler.compile( + return compiler.compile( config.parser.parse(grammar), config.passes, options ); } }; - -module.exports = peg; diff --git a/package.json b/package.json index 816761c..b5f8936 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ }, "dependencies": { "@babel/preset-env": "^7.12.1", - "eslint": "^7.12.0" + "eslint": "^7.12.0", + "map-obj": "^4.1.0" } } diff --git a/test/api/plugin-api.spec.js b/test/api/plugin-api.spec.js deleted file mode 100644 index d1738e6..0000000 --- a/test/api/plugin-api.spec.js +++ /dev/null @@ -1,128 +0,0 @@ -"use strict"; - -let chai = require("chai"); -let peg = require("../../lib/peg"); - -let expect = chai.expect; - -describe("plugin API", function() { - describe("use", function() { - let grammar = "start = 'a'"; - - it("is called for each plugin", function() { - let pluginsUsed = [false, false, false]; - let plugins = [ - { use() { pluginsUsed[0] = true; } }, - { use() { pluginsUsed[1] = true; } }, - { use() { pluginsUsed[2] = true; } } - ]; - - peg.generate(grammar, { plugins: plugins }); - - expect(pluginsUsed).to.deep.equal([true, true, true]); - }); - - it("receives configuration", function() { - let plugin = { - use(config) { - expect(config).to.be.an("object"); - - expect(config.parser).to.be.an("object"); - expect(config.parser.parse("start = 'a'")).to.be.an("object"); - - expect(config.passes).to.be.an("object"); - - expect(config.passes.check).to.be.an("array"); - config.passes.check.forEach(pass => { - expect(pass).to.be.a("function"); - }); - - expect(config.passes.transform).to.be.an("array"); - config.passes.transform.forEach(pass => { - expect(pass).to.be.a("function"); - }); - - expect(config.passes.generate).to.be.an("array"); - config.passes.generate.forEach(pass => { - expect(pass).to.be.a("function"); - }); - } - }; - - peg.generate(grammar, { plugins: [plugin] }); - }); - - it("receives options", function() { - let plugin = { - use(config, options) { - expect(options).to.equal(generateOptions); - } - }; - let generateOptions = { plugins: [plugin], foo: 42 }; - - peg.generate(grammar, generateOptions); - }); - - it("can replace parser", function() { - let plugin = { - use(config) { - let parser = peg.generate([ - "start = .* {", - " return {", - " type: 'grammar',", - " rules: [", - " {", - " type: 'rule',", - " name: 'start',", - " expression: { type: 'literal', value: text(), ignoreCase: false }", - " }", - " ]", - " };", - "}" - ].join("\n")); - - config.parser = parser; - } - }; - let parser = peg.generate("a", { plugins: [plugin] }); - - expect(parser.parse("a")).to.equal("a"); - }); - - it("can change compiler passes", function() { - let plugin = { - use(config) { - function pass(ast) { - ast.code = "({ parse: function() { return 42; } })"; - } - - config.passes.generate = [pass]; - } - }; - let parser = peg.generate(grammar, { plugins: [plugin] }); - - expect(parser.parse("a")).to.equal(42); - }); - - it("can change options", function() { - let grammar = [ - "a = 'x'", - "b = 'x'", - "c = 'x'" - ].join("\n"); - let plugin = { - use(config, options) { - options.allowedStartRules = ["b", "c"]; - } - }; - let parser = peg.generate(grammar, { - allowedStartRules: ["a"], - plugins: [plugin] - }); - - expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw(); - expect(parser.parse("x", { startRule: "b" })).to.equal("x"); - expect(parser.parse("x", { startRule: "c" })).to.equal("x"); - }); - }); -}); diff --git a/yarn.lock b/yarn.lock index 4b8b55d..0034b0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3699,6 +3699,11 @@ map-cache@^0.2.0, map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"