Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allowedStartRule of "*" means all #274

Merged
merged 4 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip: I usually check the last PR/issue number on GitHub (there is continuous numbering) and set number in advance :). If do that just before push there is a high chance that number will be correct

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