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

Adds top level initializer block. #73

Merged
merged 7 commits into from
Apr 19, 2021
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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Futago-za Ryuu <futagoza.ryuu@gmail.com> (https://github.com/futagoza/)
Jakub Vrana <jakub@vrana.cz> (https://github.com/vrana/)
Jason Davies <jason@jasondavies.com> (https://github.com/jasondavies/)
Joseph Frazier <joseph@onsip.com> (https://github.com/joseph-onsip/)
Julian Aubourg <j@ubourg.net> (https://github.com/jaubourg)
Justin Blank <justin.blank@gmail.com> (https://github.com/hyperpape/)
Marco Baumgartl <marco.baumgartl@boerse-go.de>
Mingun <alexander_sergey@mail.ru> (https://github.com/Mingun/)
Expand Down
45 changes: 34 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,15 @@ object to `peg.generate`. The following options are supported:
`false`)
- `dependencies` — parser dependencies, the value is an object which maps
variables used to access the dependencies in the parser to module IDs used
to load them; valid only when `format` is set to `"amd"`, `"commonjs"`, `"es"`, or
`"umd"` (default: `{}`)
to load them; valid only when `format` is set to `"amd"`, `"commonjs"`,
`"es"`, or `"umd"`. Dependencies variables will be available in both the
_general initializer_ and the _per-parse initializer_. Unless the parser is
to be generated in different formats, it is recommended to rather import
dependencies from within the _global initializer_. (default: `{}`)
- `exportVar` — name of a global variable into which the parser object is
assigned to when no module loader is detected; valid only when `format` is
set to `"globals"` or `"umd"` (default: `null`)
- `format` — format of the genreated parser (`"amd"`, `"bare"`, `"commonjs"`,
- `format` — format of the generated parser (`"amd"`, `"bare"`, `"commonjs"`,
`"es"`, `"globals"`, or `"umd"`); valid only when `output` is set to `"source"`
(default: `"bare"`)
- `optimize`— selects between optimizing the generated parser for parsing
Expand Down Expand Up @@ -235,19 +238,39 @@ written as a JavaScript string between the name and separating equality sign.
Rules need to be separated only by whitespace (their beginning is easily
recognizable), but a semicolon (“;”) after the parsing expression is allowed.

The first rule can be preceded by an _initializer_ — a piece of JavaScript code
in curly braces (“{” and “}”). This code is executed before the generated parser
starts parsing. All variables and functions defined in the initializer are
accessible in rule actions and semantic predicates. The code inside the
initializer can access options passed to the parser using the `options`
variable. Curly braces in the initializer code must be balanced. Let's look at
the example grammar from above using a simple initializer.
The first rule can be preceded by a _global initializer_ and/or a _per-parse
initializer_, in that order. Both are pieces of JavaScript code in double
curly braces (“{{” and “}}”) and single curly braces (“{” and “}”) respectively.
All variables and functions defined in both _initializers_ are accessible in
rule actions and semantic predicates. Curly braces in both _initializers_ code
must be balanced.

The _global initializer_ is executed once and only once, when the generated
parser is loaded (through a `require` or an `import` statement for instance). It
is the ideal location to require, to import or to declare utility functions to
be used in rule actions and semantic predicates.

The _per-parse initializer_ is called before the generated parser starts
parsing. The code inside the _per-parse initializer_ can access the input
string and the options passed to the parser using the `input` variable and the
`options` variable respectively. It is the ideal location to create data
structures that are unique to each parse or to modify the input before the
parse.

Let's look at the example grammar from above using a _global initializer_ and
a _per-parse initializer_:

```peggy
{
{{
function makeInteger(o) {
return parseInt(o.join(""), 10);
}
}}

{
if (options.multiplier) {
input = "(" + input + ")*(" + options.multiplier + ")";
}
}

start
Expand Down
49 changes: 35 additions & 14 deletions docs/documentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ <h3 id="generating-a-parser-javascript-api">JavaScript API</h3>
<dd>Parser dependencies, the value is an object which maps variables used to
access the dependencies in the parser to module IDs used to load them; valid
only when <code>format</code> is set to <code>"amd"</code>,
<code>"commonjs"</code>, or <code>"umd"</code> (default:
<code>"commonjs"</code>, <code>"es"</code>, or <code>"umd"</code>.
Dependencies variables will be available in both the <em>general
initializer</em> and the <em>per-parse initializer</em>. Unless the parser is
to be generated in different formats, it is recommended to rather import
dependencies from within the <em>global initializer</em> (default:
<code>{}</code>).</dd>

<dt><code>exportVar</code></dt>
Expand All @@ -222,9 +226,9 @@ <h3 id="generating-a-parser-javascript-api">JavaScript API</h3>

<dt><code>format</code></dt>
<dd>format of the generated parser (<code>"amd"</code>, <code>"bare"</code>,
<code>"commonjs"</code>, <code>"globals"</code>, or <code>"umd"</code>); valid
only when <code>output</code> is set to <code>"source"</code> (default:
<code>"bare"</code>).</dd>
<code>"commonjs"</code>, <code>"es"</code>, <code>"globals"</code>, or
<code>"umd"</code>); valid only when <code>output</code> is set to
<code>"source"</code> (default: <code>"bare"</code>).</dd>

<dt><code>optimize</code></dt>
<dd>Selects between optimizing the generated parser for parsing speed
Expand Down Expand Up @@ -316,19 +320,36 @@ <h2 id="grammar-syntax-and-semantics">Grammar Syntax and Semantics</h2>
recognizable), but a semicolon (“;”) after the parsing expression is
allowed.</p>

<p>The first rule can be preceded by an <em>initializer</em> &mdash; a piece of
JavaScript code in curly braces (“{” and “}”). This code is executed before the
generated parser starts parsing. All variables and functions defined in the
initializer are accessible in rule actions and semantic predicates. The code
inside the initializer can access options passed to the parser using the
<code>options</code> variable. Curly braces in the initializer code must be
balanced. Let's look at the example grammar from above using a simple
initializer.</p>

<pre><code>{
<p>The first rule can be preceded by a <em>global initializer</em> and/or a
<em>per-parse initializer</em>, in that order. Both are pieces of JavaScript
code in double curly braces (“{{” and “}}”) and single curly braces (“{” and
“}”) respectively. All variables and functions defined in both
<em>initializers</em> are accessible in rule actions and semantic predicates.
Curly braces in both <em>initializers</em> code must be balanced.</p>
<p>The <em>global initializer</em> is executed once and only once, when the
generated parser is loaded (through a <code>require</code> or an
<code>import</code> statement for instance). It is the ideal location to
require, to import or to declare utility functions to be used in rule actions
and semantic predicates.</p>
<p>The <em>per-parse initializer</em> is called before the generated parser
starts parsing. The code inside the <em>per-parse initializer</em> can access
the input string and the options passed to the parser using the
<code>input</code> variable and the <code>options</code> variable respectively.
It is the ideal location to create data structures that are unique to each
parse or to modify the input before the parse.</p>
<p>Let's look at the example grammar from above using a <em>global
initializer</em> and a <em>per-parse initializer</em>:</p>

<pre><code>{{
function makeInteger(o) {
return parseInt(o.join(""), 10);
}
}}

{
if (options.multiplier) {
input = "(" + input + ")*(" + options.multiplier + ")";
}
}

start
Expand Down
4 changes: 2 additions & 2 deletions examples/css.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// [2] http://www.w3.org/Style/css2-updates/REC-CSS2-20110607-errata.html
// [3] http://www.w3.org/TR/DOM-Level-2-Style/css.html

{
{{
function extractOptional(optional, index) {
return optional ? optional[index] : null;
}
Expand All @@ -41,7 +41,7 @@
};
}, head);
}
}
}}

start
= stylesheet:stylesheet comment* { return stylesheet; }
Expand Down
4 changes: 2 additions & 2 deletions examples/javascript.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
// [3] http://inimino.org/~inimino/blog/
// [4] http://boshi.inimino.org/3box/asof/1270029991384/PEG/ECMAScript_unified.peg

{
{{
var TYPES_TO_PROPERTY_NAMES = {
CallExpression: "callee",
MemberExpression: "object",
Expand Down Expand Up @@ -78,7 +78,7 @@
function optionalList(value) {
return value !== null ? value : [];
}
}
}}

Start
= __ program:Program __ { return program; }
Expand Down
5 changes: 5 additions & 0 deletions lib/compiler/passes/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,11 @@ function generateJS(ast, options) {
function generateToplevel() {
const parts = [];

if (ast.topLevelInitializer) {
parts.push(ast.topLevelInitializer.code);
parts.push("");
}

parts.push([
"function peg$subclass(child, parent) {",
" function C() { this.constructor = child; }",
Expand Down
5 changes: 5 additions & 0 deletions lib/compiler/visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const visitor = {
grammar(node) {
const extraArgs = Array.prototype.slice.call(arguments, 1);

if (node.topLevelInitializer) {
visit.apply(null, [node.topLevelInitializer].concat(extraArgs));
}

if (node.initializer) {
visit.apply(null, [node.initializer].concat(extraArgs));
}
Expand All @@ -40,6 +44,7 @@ const visitor = {
});
},

top_level_initializer: visitNop,
initializer: visitNop,
rule: visitExpression,
named: visitExpression,
Expand Down
Loading