Skip to content

Commit

Permalink
Reserved: Improve error when reserved word used as a label name and a…
Browse files Browse the repository at this point in the history
…dd documentation for plugins API

Before this commit error looks like (for input `start = break:'a'`)

> Expected "!", "$", "&", "(", "*", "+", ".", "/", "/*", "//", ";", "?", character class, code block, comment, end of line, identifier, literal, or whitespace but ":" found.

After this error looks like

> Expected identifier but reserved word "break" found.
  • Loading branch information
Mingun committed May 22, 2021
1 parent e0a482b commit 184b25b
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 5 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ Released: TBD
especially if your plugin replaces both `generateBytecode` as `generateJs` passes.

[@Mingun](https://github.com/peggyjs/peggy/pull/117)
- Add a new option `config.reservedWords: Iterable<string>`, avalible for plugins in their
`use()` method. Using this option plugin can change a list of words that wouldn't be used
as label names.

By default this new option contains an array with [reserved JavaScript words][reserved]
[@Mingun](https://github.com/peggyjs/peggy/pull/150)

[reserved]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_keywords_as_of_ecmascript_2015

### Bug fixes

Expand Down
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ object to `peg.generate`. The following options are supported:
- `output` — if set to `"parser"`, the method will return generated parser
object; if set to `"source"`, it will return parser source code as a string
(default: `"parser"`)
- `plugins` — plugins to use
- `plugins` — plugins to use. See the [Plugins API](#plugins-api) section
- `trace` — makes the parser trace its progress (default: `false`)
- `grammarSource` — this object will be passed to any `location()` objects as the
`source` property (default: `undefined`). This object will be used even if
Expand Down Expand Up @@ -640,6 +640,42 @@ note: Step 3: call itself without input consumption - left recursion
| ^^^^^
```

## Plugins API

Plugin is an object with the `use(config, options)` method. That method will be
called for all plugins in the `options.plugins` array, supplied to the `generate()`
method.

Plugin accepts the following parameters:
- `config` — object with the following properties:
- `parser``Parser` object, by default the `peg.parser` instance. That object
will be used to parse the grammar. Plugin can replace this object
- `passes` — mapping `{ [stage: string]: Pass[] }` that represents compilation
stages that would applied to the AST, returned by the `parser` object. That
mapping will contain at least the following keys:
- `check` — passes that check AST for correctness. They shouldn't change the AST
- `transform` — passes that performs various optimizations. They can change
the AST, add or remove nodes or their properties
- `generate` — passes used for actual code generating

Plugin that implement a pass usually should push it to the end of one of that
arrays. Pass is a simple function with signature `pass(ast, options)`:
- `ast` — the AST created by the `config.parser.parse()` method
- `options` — compilation options passed to the `peg.compiler.compile()` method.
If parser generation is started because `generate()` function was called that
is also an options, passed to the `generate()` method
- `reservedWords`[iterable] with a list of words that shouldn't be used as
label names. This list can be modified by plugins. That property is not required
to be sorted or not contain duplicates.

Default list contains [JavaScript reserved words][reserved], and can be found
in the `peg.RESERVED_WORDS` property.
- `options` — build options passed to the `generate()` method. A good tone for
a plugin would look for its own options under a `<plugin_name>` key.

[iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol
[reserved]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_keywords_as_of_ecmascript_2015

## Compatibility

Both the parser generator and generated parsers should run well in the following
Expand Down
62 changes: 58 additions & 4 deletions docs/documentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<meta name="keywords" content="parser generator, PEG, JavaScript, PEG.js">
<meta name="description" content="Peggy is a parser generator for JavaScript based on the parsing expression grammar formalism.">
<title>Documentation &raquo; Peggy &ndash; Parser Generator for JavaScript</title>
<link rel="stylesheet" href="/css/common.css">
<link rel="stylesheet" href="/css/layout-default.css">
<link rel="stylesheet" href="/css/content.css">
<link rel="stylesheet" href="./css/common.css">
<link rel="stylesheet" href="./css/layout-default.css">
<link rel="stylesheet" href="./css/content.css">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<!--[if IE]>
<script>
Expand Down Expand Up @@ -66,6 +66,7 @@ <h2 id="table-of-contents">Table of Contents</h2>
</ul>
</li>
<li><a href="#error-messages">Error Messages</a></li>
<li><a href="#plugins-api">Plugins API</a></li>
<li><a href="#compatibility">Compatibility</a></li>
</ul>

Expand Down Expand Up @@ -240,7 +241,7 @@ <h3 id="generating-a-parser-javascript-api">JavaScript API</h3>
a string (default: <code>"parser"</code>).</dd>

<dt><code>plugins</code></dt>
<dd>Plugins to use.</dd>
<dd>Plugins to use. See the [Plugins API](#plugins-api) section.</dd>

<dt><code>trace</code></dt>
<dd>Makes the parser trace its progress (default: <code>false</code>).</dd>
Expand Down Expand Up @@ -770,6 +771,59 @@ <h2 id="error-messages">Error Messages</h2>
3 | end = !start
| ^^^^^</code></pre>

<h2 id="plugins-api">Plugins API</h2>
<p>Plugin is an object with the <code>use(config, options)</code> method. That method will be
called for all plugins in the <code>options.plugins</code> array, supplied to the <code>generate()</code>
method.</p>
<p>Plugin accepts the following parameters:</p>

<h3><code>config</code></h3>
<p>Object with the following properties:</p>

<dl>
<dt><code>parser</code></dt>
<dd><code>Parser</code> object, by default the <code>peg.parser</code> instance. That object
will be used to parse the grammar. Plugin can replace this object</dd>

<dt><code>passes</code></dt>
<dd>
<p>Mapping <code>{ [stage: string]: Pass[] }</code> that represents compilation
stages that would applied to the AST, returned by the <code>parser</code> object. That
mapping will contain at least the following keys:</p>

<ul>
<li><code>check</code> — passes that check AST for correctness. They shouldn't change the AST</li>
<li><code>transform</code> — passes that performs various optimizations. They can change
the AST, add or remove nodes or their properties</li>
<li><code>generate</code> — passes used for actual code generating</li>
</ul>

<p>Plugin that implement a pass usually should push it to the end of one of that
arrays. Pass is a simple function with signature <code>pass(ast, options)</code>:</p>

<ul>
<li><code>ast</code> — the AST created by the <code>config.parser.parse()</code> method</li>
<li><code>options</code> — compilation options passed to the <code>peg.compiler.compile()</code> method.
If parser generation is started because <code>generate()</code> function was called that
is also an options, passed to the <code>generate()</code> method</li>
</ul>
</dd>

<dt><code>reservedWords</code></dt>
<dd>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol">Iterable</a> with a list of words that shouldn't be used as
label names. This list can be modified by plugins. That property is not required
to be sorted or not contain duplicates.</p>

<p>Default list contains <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_keywords_as_of_ecmascript_2015">JavaScript reserved words</a>, and can be found
in the <code>peg.RESERVED_WORDS</code> property.</p>
</dd>
</dl>

<h3><code>options</code></h3>
<dd>Build options passed to the <code>generate()</code> method. A good tone for
a plugin would look for its own options under a <code>&lt;plugin_name&gt;</code> key.</dd>

<h2 id="compatibility">Compatibility</h2>

<p>Both the parser generator and generated parsers should run well in the
Expand Down
20 changes: 20 additions & 0 deletions test/api/plugin-api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ describe("plugin API", function() {
config.passes.generate.forEach(pass => {
expect(pass).to.be.a("function");
});

expect(config.reservedWords).to.be.an("array");
config.reservedWords.forEach(word => {
expect(word).to.be.a("string");
});
}
};

Expand Down Expand Up @@ -104,6 +109,21 @@ describe("plugin API", function() {
expect(parser.parse("a")).to.equal(42);
});

it("can change list of reserved words", function() {
const plugin = {
use(config) {
config.reservedWords = [];
}
};

expect(() => {
peg.generate(
"start = " + peg.RESERVED_WORDS[0] + ":'a'",
{ plugins: [plugin], output: "source" }
);
}).to.not.throw();
});

it("can change options", function() {
const grammar = [
"a = 'x'",
Expand Down

0 comments on commit 184b25b

Please sign in to comment.