From 29edafc1b9663550232531719eb9a371ca0452f7 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Mon, 16 Aug 2021 16:29:24 -0600 Subject: [PATCH] Check allowedStartRules. (#175) Check allowedStartRules. Fixes #166. --- CHANGELOG.md | 1 + lib/compiler/index.js | 13 +++++++++++++ test/cli/run.spec.ts | 16 +++++++++------- test/unit/compiler.spec.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 test/unit/compiler.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d871179..0801d76b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Released: TBD - New CLI [@hildjj](https://github.com/peggyjs/peggy/pull/167) - Backward compatible with the previous - New -t/--test and -T/--testfile flags to directly test the generated grammar +- Check allowedStartRules for validity [@hildjj](https://github.com/peggyjs/peggy/pull/175) 1.2.0 ----- diff --git a/lib/compiler/index.js b/lib/compiler/index.js index dd80c44e..d50e9932 100644 --- a/lib/compiler/index.js +++ b/lib/compiler/index.js @@ -74,6 +74,19 @@ const compiler = { trace: false, }); + if (!Array.isArray(options.allowedStartRules)) { + throw new Error("allowedStartRules must be an array"); + } + if (options.allowedStartRules.length === 0) { + throw new Error("Must have at least one start rule"); + } + const allRules = ast.rules.map(r => r.name); + for (const rule of options.allowedStartRules) { + if (allRules.indexOf(rule) === -1) { + throw new Error(`Unknown start rule "${rule}"`); + } + } + Object.keys(passes).forEach(stage => { passes[stage].forEach(p => { p(ast, options); }); }); diff --git a/test/cli/run.spec.ts b/test/cli/run.spec.ts index 0fe725be..e4441887 100644 --- a/test/cli/run.spec.ts +++ b/test/cli/run.spec.ts @@ -12,6 +12,12 @@ type Options = { stdin?: string | Buffer; }; +const foobarbaz = `\ +foo = '1' +bar = '2' +baz = '3' +`; + /** Execution failed */ class ExecError extends Error { /** Result error code, always non-zero */ @@ -141,11 +147,7 @@ Options: it("handles start rules", async() => { await expect(exec({ args: ["--allowed-start-rules", "foo,bar,baz"], - stdin: `\ -foo = "1" -bar = "2" -baz = "3" -`, + stdin: foobarbaz, })).resolves.toMatch( /startRuleFunctions = { foo: [^, ]+, bar: [^, ]+, baz: \S+ }/ ); @@ -239,7 +241,7 @@ baz = "3" const res = await exec({ args: ["--extra-options-file", optFile], - stdin: "foo = '1'", + stdin: foobarbaz, }); expect(res).toMatch( /startRuleFunctions = { foo: [^, ]+, bar: [^, ]+, baz: \S+ }/ @@ -249,7 +251,7 @@ baz = "3" // Intentional overwrite await expect(exec({ args: ["-c", optFile, "--format", "amd"], - stdin: "foo = '1'", + stdin: foobarbaz, })).resolves.toMatch(/^define\(/m); await expect(exec({ diff --git a/test/unit/compiler.spec.js b/test/unit/compiler.spec.js new file mode 100644 index 00000000..2bf29fb9 --- /dev/null +++ b/test/unit/compiler.spec.js @@ -0,0 +1,33 @@ +"use strict"; + +const chai = require("chai"); +const parser = require("../../lib/parser"); +const compiler = require("../../lib/compiler/index"); + +const expect = chai.expect; + +describe("Peggy compiler", () => { + it("checks start rules", () => { + const ast = parser.parse("foo='1'"); + expect(compiler.compile(ast, compiler.passes)).to.be.an("object"); + expect(() => compiler.compile(ast, compiler.passes, { + allowedStartRules: null, + })).to.throw("allowedStartRules must be an array"); + expect(() => compiler.compile(ast, compiler.passes, { + allowedStartRules: [], + })).to.throw("Must have at least one start rule"); + expect(() => compiler.compile(ast, compiler.passes, { + allowedStartRules: ["bar"], + })).to.throw('Unknown start rule "bar"'); + }); + + it("checks ouput type", () => { + const ast = parser.parse("foo='1'"); + expect(compiler.compile(ast, compiler.passes, { + output: "source", + })).to.be.a("string"); + expect(() => compiler.compile(ast, compiler.passes, { + output: "INVALID OUTPUT TYPE", + })).to.throw("Invalid output format: INVALID OUTPUT TYPE."); + }); +});