From cdde0535ca55f39f53241714307ddf257ff06251 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Wed, 1 Jun 2022 12:17:45 -0600 Subject: [PATCH 1/4] Use commander's new .conflict() feature. Fix 'Error error:' problems in CLI. --- bin/peggy-cli.js | 16 ++++++++-------- test/cli/run.spec.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bin/peggy-cli.js b/bin/peggy-cli.js index 9d43d980..d97fc452 100644 --- a/bin/peggy-cli.js +++ b/bin/peggy-cli.js @@ -173,14 +173,14 @@ class PeggyCLI extends Command { "-m, --source-map [mapfile]", "Generate a source map. If name is not specified, the source map will be named \".map\" if input is a file and \"source.map\" if input is a standard input. If the special filename `inline` is given, the sourcemap will be embedded in the output file as a data URI. If the filename is prefixed with `hidden:`, no mapping URL will be included so that the mapping can be specified with an HTTP SourceMap: header. This option conflicts with the `-t/--test` and `-T/--test-file` options unless `-o/--output` is also specified" ) - .option( + .addOption(new Option( "-t, --test ", "Test the parser with the given text, outputting the result of running the parser instead of the parser itself. If the input to be tested is not parsed, the CLI will exit with code 2" - ) - .option( + ).conflicts("test-file")) + .addOption(new Option( "-T, --test-file ", "Test the parser with the contents of the given file, outputting the result of running the parser instead of the parser itself. If the input to be tested is not parsed, the CLI will exit with code 2" - ) + ).conflicts("test")) .option("--trace", "Enable tracing in generated parser", false) .addOption( // Not interesting yet. If it becomes so, unhide the help. @@ -306,9 +306,6 @@ class PeggyCLI extends Command { // Empty string is a valid test input. Don't just test for falsy. if (typeof this.progOptions.test === "string") { - if (this.progOptions.testFile) { - this.error("The -t/--test and -T/--test-file options are mutually exclusive."); - } this.testText = this.progOptions.test; this.testGrammarSource = "command line"; } @@ -359,8 +356,11 @@ class PeggyCLI extends Command { : `${message}\n${opts.error.message}`; } } + if (!/^error/i.test(message)) { + message = `Error ${message}`; + } - super.error(`Error ${message}`, opts); + super.error(message, opts); } print(stream, ...args) { diff --git a/test/cli/run.spec.ts b/test/cli/run.spec.ts index 605dd0c3..928280bd 100644 --- a/test/cli/run.spec.ts +++ b/test/cli/run.spec.ts @@ -948,9 +948,9 @@ Options: await exec({ args: ["-t", "boo", "-T", "foo"], - errorCode: "peggy.invalidArgument", + errorCode: "commander.conflictingOption", exitCode: 1, - error: "The -t/--test and -T/--test-file options are mutually exclusive.", + error: "error: option '-T, --test-file ' cannot be used with option '-t, --test '", }); await exec({ From db8ac6afbedb131588d1f172dad8454ac5aac502 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Wed, 1 Jun 2022 12:38:09 -0600 Subject: [PATCH 2/4] An allowedStartRule of '*' means 'all'. --- docs/documentation.html | 5 +++-- lib/compiler/index.js | 11 ++++++++--- test/api/plugin-api.spec.js | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/documentation.html b/docs/documentation.html index a2d4ad93..43441fe0 100644 --- a/docs/documentation.html +++ b/docs/documentation.html @@ -321,8 +321,9 @@

JavaScript API

allowedStartRules
-
Rules the parser will be allowed to start parsing from (default: the first - rule in the grammar).
+
Rules the parser will be allowed to start parsing from (default: the + first rule in the grammar). If any of the rules specified is "*", any of + the rules in the grammar can be used as start rules.
cache
If true, makes the parser cache results, avoiding exponential diff --git a/lib/compiler/index.js b/lib/compiler/index.js index d8b9a19b..b10bb0c7 100644 --- a/lib/compiler/index.js +++ b/lib/compiler/index.js @@ -82,9 +82,14 @@ const compiler = { 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}"`); + // "*" means all rules are start rules. "*" is not a valid rule name. + if (options.allowedStartRules.some(r => r === "*")) { + options.allowedStartRules = allRules; + } else { + for (const rule of options.allowedStartRules) { + if (allRules.indexOf(rule) === -1) { + throw new Error(`Unknown start rule "${rule}"`); + } } } diff --git a/test/api/plugin-api.spec.js b/test/api/plugin-api.spec.js index daa56a40..0804b32d 100644 --- a/test/api/plugin-api.spec.js +++ b/test/api/plugin-api.spec.js @@ -151,5 +151,19 @@ describe("plugin API", () => { expect(parser.parse("x", { startRule: "b" })).to.equal("x"); expect(parser.parse("x", { startRule: "c" })).to.equal("x"); }); + + it("can use star for start rules", () => { + const grammar = [ + "a = 'x'", + "b = 'x'", + "c = 'x'", + ].join("\n"); + const parser = peg.generate(grammar, { + allowedStartRules: ["*"], + }); + expect(parser.parse("x", { startRule: "a" })).to.equal("x"); + expect(parser.parse("x", { startRule: "b" })).to.equal("x"); + expect(parser.parse("x", { startRule: "c" })).to.equal("x"); + }); }); }); From 2a27d4d2487ff92f54a8b5a5908c7c5df499faa5 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Wed, 1 Jun 2022 13:05:07 -0600 Subject: [PATCH 3/4] Add --start-rule to CLI. Fixes #229. --- bin/peggy-cli.js | 19 ++++++++++++++++--- docs/documentation.html | 4 ++++ test/cli/run.spec.ts | 13 +++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/bin/peggy-cli.js b/bin/peggy-cli.js index d97fc452..06aa1377 100644 --- a/bin/peggy-cli.js +++ b/bin/peggy-cli.js @@ -14,7 +14,7 @@ exports.CommanderError = CommanderError; exports.InvalidArgumentError = InvalidArgumentError; // Options that aren't for the API directly: -const PROG_OPTIONS = ["input", "output", "sourceMap", "test", "testFile", "verbose"]; +const PROG_OPTIONS = ["input", "output", "sourceMap", "startRule", "test", "testFile", "verbose"]; const MODULE_FORMATS = ["amd", "bare", "commonjs", "es", "globals", "umd"]; const MODULE_FORMATS_WITH_DEPS = ["amd", "commonjs", "es", "umd"]; const MODULE_FORMATS_WITH_GLOBAL = ["globals", "umd"]; @@ -173,6 +173,10 @@ class PeggyCLI extends Command { "-m, --source-map [mapfile]", "Generate a source map. If name is not specified, the source map will be named \".map\" if input is a file and \"source.map\" if input is a standard input. If the special filename `inline` is given, the sourcemap will be embedded in the output file as a data URI. If the filename is prefixed with `hidden:`, no mapping URL will be included so that the mapping can be specified with an HTTP SourceMap: header. This option conflicts with the `-t/--test` and `-T/--test-file` options unless `-o/--output` is also specified" ) + .option( + "-S, --start-rule ", + "When testing, use the given rule as the start rule. If this rule is not in the allowed start rules, it will be added." + ) .addOption(new Option( "-t, --test ", "Test the parser with the given text, outputting the result of running the parser instead of the parser itself. If the input to be tested is not parsed, the CLI will exit with code 2" @@ -202,6 +206,11 @@ class PeggyCLI extends Command { this.inputFile = inputFile; this.argv = opts; + if ((typeof this.argv.startRule === "string") + && !this.argv.allowedStartRules.includes(this.argv.startRule)) { + this.argv.allowedStartRules.push(this.argv.startRule); + } + if (this.argv.allowedStartRules.length === 0) { // [] is an invalid input, as is null // undefined doesn't work as a default in commander @@ -579,9 +588,13 @@ class PeggyCLI extends Command { setTimeout, }); - const results = exec.parse(this.testText, { + const opts = { grammarSource: this.testGrammarSource, - }); + }; + if (typeof this.progOptions.startRule === "string") { + opts.startRule = this.progOptions.startRule; + } + const results = exec.parse(this.testText, opts); this.print(this.std.out, util.inspect(results, { depth: Infinity, colors: this.colors, diff --git a/docs/documentation.html b/docs/documentation.html index 43441fe0..2d53da2f 100644 --- a/docs/documentation.html +++ b/docs/documentation.html @@ -199,6 +199,10 @@

Command Line

-T/--test-file options unless -o/--output is also specified
+
-S, --start-rule <rule>
+
When testing, use this rule as the start rule. Automatically added to + the allowedStartRules.
+
-t, --test <text>
Test the parser with the given text, outputting the result of running the parser against this input. diff --git a/test/cli/run.spec.ts b/test/cli/run.spec.ts index 928280bd..9cd24d36 100644 --- a/test/cli/run.spec.ts +++ b/test/cli/run.spec.ts @@ -334,6 +334,9 @@ Options: This option conflicts with the \`-t/--test\` and \`-T/--test-file\` options unless \`-o/--output\` is also specified + -S, --start-rule When testing, use the given rule as the + start rule. If this rule is not in the + allowed start rules, it will be added. -t, --test Test the parser with the given text, outputting the result of running the parser instead of the parser itself. If the input @@ -931,6 +934,16 @@ Options: expected: "'boo'\n", }); + // Start rule + await exec({ + args: ["-t", "2", "-S", "bar"], + stdin: `\ +foo='1' { throw new Error('bar') } +bar = '2' +`, + expected: "'2'\n", + }); + const grammarFile = path.join(__dirname, "..", "..", "examples", "json.pegjs"); const testFile = path.join(__dirname, "..", "..", "package.json"); From 9555760efa1d67fbf12abe49ffb341e72b4f5328 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Wed, 1 Jun 2022 13:37:12 -0600 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eff3e95..b08a6645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,12 @@ Released: TBD ### Minor Changes -- None +- [TBD] Use commander's new `.conflicts()` to check for mutually-exclusive CLI + options, from @hildjj +- [TBD] `"*"` is now a valid `allowedStartRule`, which means all rules are allowed, from @hildjj +- [#229](https://github.com/peggyjs/peggy/issues/229) new CLI option + `-S ` or `--start-rule ` to specify the start rule when testing, + from @hildjj ### Bug Fixes