Skip to content

Commit

Permalink
Merge pull request #274 from hildjj/all-starts
Browse files Browse the repository at this point in the history
allowedStartRule of "*" means all
  • Loading branch information
hildjj authored Jun 1, 2022
2 parents cfbcd99 + 9555760 commit 74b70ad
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 18 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <rule>` or `--start-rule <rule>` to specify the start rule when testing,
from @hildjj

### Bug Fixes

Expand Down
33 changes: 23 additions & 10 deletions bin/peggy-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down Expand Up @@ -174,13 +174,17 @@ class PeggyCLI extends Command {
"Generate a source map. If name is not specified, the source map will be named \"<input_file>.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 <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 <text>",
"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 <filename>",
"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.
Expand All @@ -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
Expand Down Expand Up @@ -306,9 +315,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";
}
Expand Down Expand Up @@ -359,8 +365,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) {
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 7 additions & 2 deletions docs/documentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ <h3 id="generating-a-parser-command-line">Command Line</h3>
<code>-T/--test-file</code> options unless <code>-o/--output</code> is also
specified</dd>

<dt><code>-S</code>, <code>--start-rule &lt;rule&gt;</code></dt>
<dd>When testing, use this rule as the start rule. Automatically added to
the allowedStartRules.</dd>

<dt><code>-t</code>, <code>--test &lt;text&gt;</code></dt>
<dd>Test the parser with the given text, outputting the result of running
the parser against this input.
Expand Down Expand Up @@ -321,8 +325,9 @@ <h3 id="generating-a-parser-javascript-api">JavaScript API</h3>

<dl>
<dt><code>allowedStartRules</code></dt>
<dd>Rules the parser will be allowed to start parsing from (default: the first
rule in the grammar).</dd>
<dd>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.</dd>

<dt><code>cache</code></dt>
<dd>If <code>true</code>, makes the parser cache results, avoiding exponential
Expand Down
11 changes: 8 additions & 3 deletions lib/compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}"`);
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions test/api/plugin-api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
});
});
17 changes: 15 additions & 2 deletions test/cli/run.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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 <text> Test the parser with the given text,
outputting the result of running the parser
instead of the parser itself. If the input
Expand Down Expand Up @@ -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");

Expand All @@ -948,9 +961,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 <filename>' cannot be used with option '-t, --test <text>'",
});

await exec({
Expand Down

0 comments on commit 74b70ad

Please sign in to comment.