Compare commits

...

10 Commits

@ -0,0 +1,3 @@
lib/parser.js
test/vendor/**/*
benchmark/vendor/**/*

@ -1,3 +1,3 @@
{ {
"extends": "dmajda" "extends": "@joepie91/eslint-config"
} }

4
.gitignore vendored

@ -1,3 +1,3 @@
browser/* browser
examples/*.js examples/*.js
node_modules/* node_modules

@ -1,9 +1,6 @@
[![Build status](https://img.shields.io/travis/pegjs/pegjs.svg)](https://travis-ci.org/pegjs/pegjs)
[![npm version](https://img.shields.io/npm/v/pegjs.svg)](https://www.npmjs.com/package/pegjs)
[![Bower version](https://img.shields.io/bower/v/pegjs.svg)](https://github.com/pegjs/bower)
[![License](https://img.shields.io/badge/license-mit-blue.svg)](https://opensource.org/licenses/MIT) [![License](https://img.shields.io/badge/license-mit-blue.svg)](https://opensource.org/licenses/MIT)
PEG.js PEG-Redux
====== ======
PEG.js is a simple parser generator for JavaScript that produces fast parsers PEG.js is a simple parser generator for JavaScript that produces fast parsers
@ -11,6 +8,10 @@ with excellent error reporting. You can use it to process complex data or
computer languages and build transformers, interpreters, compilers and other computer languages and build transformers, interpreters, compilers and other
tools easily. tools easily.
PEG-Redux is a __work-in-progress__ fork of PEG.js, with the aim of continuing
maintenance on the PEG.js project, while adding support for modern features
such as modules.
Features Features
-------- --------
@ -20,49 +21,48 @@ Features
* Based on [parsing expression * Based on [parsing expression
grammar](http://en.wikipedia.org/wiki/Parsing_expression_grammar) formalism grammar](http://en.wikipedia.org/wiki/Parsing_expression_grammar) formalism
— more powerful than traditional LL(*k*) and LR(*k*) parsers — more powerful than traditional LL(*k*) and LR(*k*) parsers
* Usable [from your browser](https://pegjs.org/online), from the command line, * Usable from your browser, from the command line, or via JavaScript API
or via JavaScript API
Getting Started Differences from the original PEG.js
--------------- --------
[Online version](https://pegjs.org/online) is the easiest way to generate a * The plugin API has been dropped for now, as it was underspecified and not very commonly used. A new, more robust and extensive plugin API may come to exist in the future, if it turns out that there is a high demand for customizations that wouldn't fit into the PEG-Redux project itself.
parser. Just enter your grammar, try parsing few inputs, and download generated * Bower and stand-alone browser builds have been discontinued. Please use a bundler (see below) instead.
parser code. * AMD, UMD and globals support have been discontinued. The generated parsers now only support CommonJS.
* Module support. Both for importing other PEGRedux files, and for `require()`ing JS modules.
Installation Installation
------------ ------------
### Node.js ### Node.js
To use the `pegjs` command, install PEG.js globally: To use the `pegjs` command, install PEG-Redux globally:
```console ```console
$ npm install -g pegjs $ npm install -g peg-redux
``` ```
To use the JavaScript API, install PEG.js locally: To use the JavaScript API, install PEG-Redux locally:
```console ```console
$ npm install pegjs $ npm install peg-redux
``` ```
If you need both the `pegjs` command and the JavaScript API, install PEG.js both If you need both the `pegjs` command and the JavaScript API, install PEG-Redux
ways. both ways.
### Browser ### Browser
[Download](https://pegjs.org/#download) the PEG.js library (regular or minified PEG-Redux works with bundlers such as [Browserify](http://browserify.org/), [Parcel](https://parceljs.org/) and [Webpack](https://webpack.js.org/).
version) or install it using Bower:
```console Simply `require()` and use the module like you would in Node.js. The one exception is that modules (either PEG-Redux or Javascript modules) are not currently supported in browser environments.
$ bower install pegjs
``` Bower and standalone builds have been discontinued in this fork. Getting started with Browserify will only take a few minutes, and give you a better developer experience.
Generating a Parser Generating a Parser
------------------- -------------------
PEG.js generates parser from a grammar that describes expected input and can PEG-Redux generates parser from a grammar that describes expected input and can
specify what the parser returns (using semantic actions on matched parts of the specify what the parser returns (using semantic actions on matched parts of the
input). Generated parser itself is a JavaScript object with a simple API. input). Generated parser itself is a JavaScript object with a simple API.
@ -100,26 +100,20 @@ You can tweak the generated parser with several options:
`peg.generate` `peg.generate`
* `--extra-options-file` — file with additional options (in JSON format) to * `--extra-options-file` — file with additional options (in JSON format) to
pass to `peg.generate` pass to `peg.generate`
* `--format` — format of the generated parser: `amd`, `commonjs`, `globals`,
`umd` (default: `commonjs`)
* `--optimize` — selects between optimizing the generated parser for parsing * `--optimize` — selects between optimizing the generated parser for parsing
speed (`speed`) or code size (`size`) (default: `speed`) speed (`speed`) or code size (`size`) (default: `speed`)
* `--plugin` — makes PEG.js use a specified plugin (can be specified multiple * `--plugin` — makes PEG-Redux use a specified plugin (can be specified multiple
times) times)
* `--trace` — makes the parser trace its progress * `--trace` — makes the parser trace its progress
### JavaScript API ### JavaScript API
In Node.js, require the PEG.js parser generator module: Require the PEG-Redux parser generator module:
```javascript ```javascript
var peg = require("pegjs"); var peg = require("peg-redux");
``` ```
In browser, include the PEG.js library in your web page or application using the
`<script>` tag. If PEG.js detects an AMD loader, it will define itself as a
module, otherwise the API will be available in the `peg` global object.
To generate a parser, call the `peg.generate` method and pass your grammar as a To generate a parser, call the `peg.generate` method and pass your grammar as a
parameter: parameter:
@ -142,14 +136,7 @@ object to `peg.generate`. The following options are supported:
`false`) `false`)
* `dependencies` — parser dependencies, the value is an object which maps * `dependencies` — parser dependencies, the value is an object which maps
variables used to access the dependencies in the parser to module IDs used 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"`, or to load them.
`"umd"` (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"`,
`"globals"`, or `"umd"`); valid only when `output` is set to `"source"`
(default: `"bare"`)
* `optimize`— selects between optimizing the generated parser for parsing * `optimize`— selects between optimizing the generated parser for parsing
speed (`"speed"`) or code size (`"size"`) (default: `"speed"`) speed (`"speed"`) or code size (`"size"`) (default: `"speed"`)
* `output` — if set to `"parser"`, the method will return generated parser * `output` — if set to `"parser"`, the method will return generated parser
@ -503,25 +490,14 @@ environments:
* Safari * Safari
* Opera * Opera
However, please note that it is currently only actively tested in Node.js and Firefox. This will likely change in the future.
Development Development
----------- -----------
* [Project website](https://pegjs.org/) PEG-Redux is maintained by [Sven Slootweg (joepie91)](http://cryto.net/~joepie91).
* [Wiki](https://github.com/pegjs/pegjs/wiki) The original PEG.js was developed by [David Majda](http://majda.cz/) ([@dmajda](http://twitter.com/dmajda)).
* [Source code](https://github.com/pegjs/pegjs)
* [Issue tracker](https://github.com/pegjs/pegjs/issues)
* [Google Group](http://groups.google.com/group/pegjs)
* [Twitter](http://twitter.com/peg_js)
PEG.js is developed by [David Majda](https://majda.cz/)
([@dmajda](http://twitter.com/dmajda)). The [Bower
package](https://github.com/pegjs/bower) is maintained by [Michel
Krämer](http://www.michel-kraemer.com/)
([@michelkraemer](https://twitter.com/michelkraemer)).
You are welcome to contribute code. Unless your contribution is really trivial You are welcome to contribute code. Unless your contribution is really trivial
you should get in touch with me first — this can prevent wasted effort on both you should get in touch with me first — this can prevent wasted effort on both
sides. You can send code both as a patch or a GitHub pull request. sides. You can send code both as a patch or a pull request.
Note that PEG.js is still very much work in progress. There are no compatibility
guarantees until version 1.0.

@ -67,6 +67,7 @@ $("#run").click(() => {
}; };
if (isNaN(runCount) || runCount <= 0) { if (isNaN(runCount) || runCount <= 0) {
// eslint-disable-next-line no-alert
alert("Number of runs must be a positive integer."); alert("Number of runs must be a positive integer.");
return; return;

@ -1,7 +1,5 @@
"use strict"; "use strict";
/* global setTimeout */
let peg = require("../lib/peg"); let peg = require("../lib/peg");
let Runner = { let Runner = {

@ -26,11 +26,12 @@ app.get("/bundle.js", (req, res) => {
}); });
browserify(files) browserify(files)
.transform(babelify, { presets: "es2015", compact: false }) .transform(babelify, { presets: "env", compact: false })
.bundle() .bundle()
.pipe(res); .pipe(res);
}); });
app.listen(8000, () => { app.listen(8000, () => {
// eslint-disable-next-line no-console
console.log("Benchmark server running at http://localhost:8000..."); console.log("Benchmark server running at http://localhost:8000...");
}); });

@ -0,0 +1,14 @@
"use strict";
const fs = require("fs");
const path = require("path");
const util = require("util");
const generate = require("../").generate;
const parser = require("../lib/parser");
let grammar = fs.readFileSync(path.join(__dirname, "test.pegjs"), { encoding: "utf-8" });
// let parseResult = parser.parse(grammar);
let parseResult = generate(grammar);
console.log(util.inspect(parseResult, { depth: null, colors: true }));

@ -0,0 +1,5 @@
import Foo from "./util.pegjs"
import { CommaDelimited as DelimitedNumber } from "./delimited-number"
TopLevelRule
= "hello"

@ -1,52 +1,9 @@
"use strict"; "use strict";
/* eslint-env node */
let babelify = require("babelify");
let browserify = require("browserify");
let buffer = require("vinyl-buffer");
let del = require("del");
let eslint = require("gulp-eslint");
let gulp = require("gulp"); let gulp = require("gulp");
let header = require("gulp-header");
let mocha = require("gulp-mocha");
let package_ = require("./package");
let peg = require("./lib/peg"); let peg = require("./lib/peg");
let rename = require("gulp-rename"); let rename = require("gulp-rename");
let runSequence = require("run-sequence");
let source = require("vinyl-source-stream");
let spawn = require("child_process").spawn;
let transform = require("gulp-transform"); let transform = require("gulp-transform");
let uglify = require("gulp-uglify");
const HEADER = [
"// PEG.js " + package_.version,
"//",
"// https://pegjs.org/",
"//",
"// Copyright (c) 2010-2016 David Majda",
"// Licensed under the MIT License.",
""
].map(line => `${line}\n`).join("");
const JS_FILES = [
"lib/**/*.js",
"!lib/parser.js",
"test/**/*.js",
"test/server",
"!test/vendor/**/*",
"benchmark/**/*.js",
"benchmark/run",
"benchmark/server",
"!benchmark/vendor/**/*",
"bin/pegjs",
"gulpfile.js"
];
const TEST_FILES = [
"test/**/*.js",
"!test/vendor/**/*"
];
function generate(contents) { function generate(contents) {
return peg.generate(contents.toString(), { return peg.generate(contents.toString(), {
@ -55,54 +12,10 @@ function generate(contents) {
}); });
} }
// Run ESLint on all JavaScript files.
gulp.task("lint", () =>
gulp.src(JS_FILES)
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError())
);
// Run tests.
gulp.task("test", () =>
gulp.src(TEST_FILES, { read: false })
.pipe(mocha())
);
// Run benchmarks.
gulp.task("benchmark", () =>
spawn("benchmark/run", { stdio: "inherit" })
);
// Create the browser build.
gulp.task("browser:build", () =>
browserify("lib/peg.js", { standalone: "peg" })
.transform(babelify, { presets: "es2015", compact: false })
.bundle()
.pipe(source("peg.js"))
.pipe(header(HEADER))
.pipe(gulp.dest("browser"))
.pipe(rename({ suffix: ".min" }))
.pipe(buffer())
.pipe(uglify())
.pipe(header(HEADER))
.pipe(gulp.dest("browser"))
);
// Delete the browser build.
gulp.task("browser:clean", () =>
del("browser")
);
// Generate the grammar parser. // Generate the grammar parser.
gulp.task("parser", () => gulp.task("parser", () =>
gulp.src("src/parser.pegjs") gulp.src("src/parser.pegjs")
.pipe(transform(generate)) .pipe(transform("utf8", generate))
.pipe(rename({ extname: ".js" })) .pipe(rename({ extname: ".js" }))
.pipe(gulp.dest("lib")) .pipe(gulp.dest("lib"))
); );
// Default task.
gulp.task("default", cb =>
runSequence("lint", "test", cb)
);

@ -11,19 +11,7 @@ let reportUndefinedRules = require("./passes/report-undefined-rules");
let visitor = require("./visitor"); let visitor = require("./visitor");
function processOptions(options, defaults) { function processOptions(options, defaults) {
let processedOptions = {}; return Object.assign({}, defaults, options);
Object.keys(options).forEach(name => {
processedOptions[name] = options[name];
});
Object.keys(defaults).forEach(name => {
if (!Object.prototype.hasOwnProperty.call(processedOptions, name)) {
processedOptions[name] = defaults[name];
}
});
return processedOptions;
} }
let compiler = { let compiler = {
@ -57,10 +45,8 @@ let compiler = {
// if the AST contains a semantic error. Note that not all errors are detected // if the AST contains a semantic error. Note that not all errors are detected
// during the generation and some may protrude to the generated parser and // during the generation and some may protrude to the generated parser and
// cause its malfunction. // cause its malfunction.
compile(ast, passes, options) { compile(ast, passes, options = {}) {
options = options !== undefined ? options : {}; let processedOptions = processOptions(options, {
options = processOptions(options, {
allowedStartRules: [ast.rules[0].name], allowedStartRules: [ast.rules[0].name],
cache: false, cache: false,
dependencies: {}, dependencies: {},
@ -71,19 +57,20 @@ let compiler = {
trace: false trace: false
}); });
Object.keys(passes).forEach(stage => { Object.values(passes).forEach((stagePasses) => {
passes[stage].forEach(p => { p(ast, options); }); stagePasses.forEach(pass => { pass(ast, processedOptions); });
}); });
switch (options.output) { switch (processedOptions.output) {
case "parser": case "parser":
return eval(ast.code); return eval(ast.code);
case "source": case "source":
return ast.code; return ast.code;
// FIXME: Move to Validatem code at entrypoint
default: default:
throw new Error("Invalid output format: " + options.output + "."); throw new Error("Invalid output format: " + processedOptions.output + ".");
} }
} }
}; };

@ -1,6 +1,11 @@
"use strict"; "use strict";
function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } function hex(character) {
return character
.charCodeAt(0)
.toString(16)
.toUpperCase();
}
// JavaScript code generation helpers. // JavaScript code generation helpers.
let js = { let js = {

@ -1204,7 +1204,7 @@ function generateJS(ast, options) {
function generateWrapper(toplevelCode) { function generateWrapper(toplevelCode) {
function generateGeneratedByComment() { function generateGeneratedByComment() {
return [ return [
"// Generated by PEG.js 0.10.0.", `// Generated by PEG.js ${require("../../../package.json").version}.`,
"//", "//",
"// https://pegjs.org/" "// https://pegjs.org/"
].join("\n"); ].join("\n");

File diff suppressed because one or more lines are too long

@ -1,12 +1,14 @@
"use strict"; "use strict";
const mapObj = require("map-obj");
let GrammarError = require("./grammar-error"); let GrammarError = require("./grammar-error");
let compiler = require("./compiler"); let compiler = require("./compiler");
let parser = require("./parser"); let parser = require("./parser");
let peg = { module.exports = {
// PEG.js version (uses semantic versioning). // PEG.js version (uses semantic versioning).
VERSION: "0.10.0", VERSION: require("../package.json").version,
GrammarError: GrammarError, GrammarError: GrammarError,
parser: parser, parser: parser,
@ -21,34 +23,29 @@ let peg = {
// |peg.GrammarError| if it contains a semantic error. Note that not all // |peg.GrammarError| if it contains a semantic error. Note that not all
// errors are detected during the generation and some may protrude to the // errors are detected during the generation and some may protrude to the
// generated parser and cause its malfunction. // generated parser and cause its malfunction.
generate(grammar, options) { generate(grammar, options = {}) {
options = options !== undefined ? options : {}; // FIXME: Validatem
function convertPasses(passes) {
let converted = {};
Object.keys(passes).forEach(stage => {
converted[stage] = Object.keys(passes[stage])
.map(name => passes[stage][name]);
});
return converted;
}
let plugins = "plugins" in options ? options.plugins : [];
let config = { let config = {
parser: peg.parser, parser: parser,
passes: convertPasses(peg.compiler.passes) passes: mapObj(compiler.passes, (stage, passesForStage) => {
return [ stage, Object.values(passesForStage) ];
})
}; };
plugins.forEach(p => { p.use(config, options); }); let parseResult = config.parser.parse(grammar);
return peg.compiler.compile( if (parseResult.imports.length === 0) {
config.parser.parse(grammar), return compiler.compile(
parseResult,
config.passes, config.passes,
options options
); );
} else {
throw new Error("`import` syntax can only be used with `generateFromFile`");
}
},
generateFromFile: function (path, options = {}) {
throw new Error(`Unimplemented`);
} }
}; };
module.exports = peg;

@ -0,0 +1,52 @@
Import syntax
import Foo from packagename
import { Foo, Bar } from ./util
import { Foo as Bar, Baz, Qux } from ../quz
need to mangle identifiers declared in the initializer!
===========
Benchmark before internals changes (best of 3):
$ yarn benchmark
yarn run v1.21.1
$ node benchmark/run
┌─────────────────────────────────────┬───────────┬────────────┬──────────────┐
│ Test │ Inp. size │ Avg. time │ Avg. speed │
├─────────────────────────────────────┴───────────┴────────────┴──────────────┤
│ JSON │
├─────────────────────────────────────┬───────────┬────────────┬──────────────┤
│ Example 1 │ 0.69 kB │ 0.40 ms │ 1723.63 kB/s │
│ Example 2 │ 0.24 kB │ 0.10 ms │ 2363.28 kB/s │
│ Example 3 │ 0.59 kB │ 0.40 ms │ 1474.61 kB/s │
│ Example 4 │ 3.39 kB │ 0.90 ms │ 3763.02 kB/s │
│ Example 5 │ 0.85 kB │ 0.20 ms │ 4262.70 kB/s │
├─────────────────────────────────────┼───────────┼────────────┼──────────────┤
│ JSON total │ 5.75 kB │ 2.00 ms │ 2877.44 kB/s │
├─────────────────────────────────────┴───────────┴────────────┴──────────────┤
│ CSS │
├─────────────────────────────────────┬───────────┬────────────┬──────────────┤
│ Blueprint - reset.css (source) │ 1.20 kB │ 3.00 ms │ 401.04 kB/s │
│ Blueprint - typography.css (source) │ 3.11 kB │ 5.00 ms │ 621.48 kB/s │
│ Blueprint - forms.css (source) │ 1.79 kB │ 2.00 ms │ 896.00 kB/s │
│ Blueprint - grid.css (source) │ 9.54 kB │ 6.50 ms │ 1467.25 kB/s │
│ Blueprint - print.css (source) │ 1.78 kB │ 1.10 ms │ 1621.09 kB/s │
│ Blueprint - screen.css (minified) │ 11.83 kB │ 8.00 ms │ 1478.64 kB/s │
│ Blueprint - print.css (minified) │ 1.25 kB │ 0.60 ms │ 2089.84 kB/s │
│ 960.gs - reset.css (source) │ 0.99 kB │ 0.50 ms │ 1980.47 kB/s │
│ 960.gs - text.css (source) │ 0.97 kB │ 0.40 ms │ 2426.76 kB/s │
│ 960.gs - 960.css (source) │ 8.94 kB │ 3.00 ms │ 2979.49 kB/s │
│ 960.gs - 960_24_col.css (source) │ 7.48 kB │ 2.60 ms │ 2877.85 kB/s │
│ 960.gs - reset.css (minified) │ 0.63 kB │ 0.50 ms │ 1265.63 kB/s │
│ 960.gs - text.css (minified) │ 0.41 kB │ 0.40 ms │ 1020.51 kB/s │
│ 960.gs - 960.css (minified) │ 5.21 kB │ 2.60 ms │ 2004.21 kB/s │
│ 960.gs - 960_24_col.css (minified) │ 4.94 kB │ 2.40 ms │ 2057.70 kB/s │
├─────────────────────────────────────┼───────────┼────────────┼──────────────┤
│ CSS total │ 60.08 kB │ 38.60 ms │ 1556.43 kB/s │
├─────────────────────────────────────┼───────────┼────────────┼──────────────┤
│ Total │ 65.83 kB │ 40.60 ms │ 1621.50 kB/s │
└─────────────────────────────────────┴───────────┴────────────┴──────────────┘
Done in 0.60s.

@ -1,15 +1,16 @@
{ {
"name": "pegjs", "name": "peg-redux",
"version": "0.10.0", "version": "0.10.0",
"description": "Parser generator for JavaScript", "description": "Parser generator for JavaScript",
"keywords": [ "keywords": [
"parser generator", "parser generator",
"PEG" "PEG"
], ],
"homepage": "https://pegjs.org/",
"bugs": "https://github.com/pegjs/pegjs/issues",
"license": "MIT", "license": "MIT",
"author": "David Majda <david@majda.cz> (https://majda.cz/)", "contributors": [
"David Majda <david@majda.cz> (http://majda.cz/)",
"Sven Slootweg <admin@cryto.net>"
],
"files": [ "files": [
"CHANGELOG.md", "CHANGELOG.md",
"LICENSE", "LICENSE",
@ -41,31 +42,32 @@
"bin": "bin/pegjs", "bin": "bin/pegjs",
"repository": "pegjs/pegjs", "repository": "pegjs/pegjs",
"scripts": { "scripts": {
"test": "gulp" "test": "mocha 'test/**/*.js' '!test.vendor.**/*'",
"benchmark": "node benchmark/run",
"lint": "eslint .",
"build": "gulp parser",
"validate": "yarn build && yarn lint && yarn test"
}, },
"devDependencies": { "devDependencies": {
"babel-preset-es2015": "6.14.0", "@joepie91/eslint-config": "^1.1.0",
"babelify": "7.3.0", "babelify": "^10.0.0",
"browserify": "13.1.0", "browserify": "^17.0.0",
"chai": "3.5.0", "chai": "3.5.0",
"del": "2.2.2", "express": "^4.17.1",
"eslint-config-dmajda": "1.0.0", "glob": "^7.1.6",
"express": "4.14.0", "gulp": "^4.0.2",
"glob": "7.0.6",
"gulp": "3.9.1",
"gulp-eslint": "3.0.1",
"gulp-header": "1.8.8",
"gulp-mocha": "3.0.1",
"gulp-rename": "1.2.2", "gulp-rename": "1.2.2",
"gulp-transform": "1.0.8", "gulp-transform": "^3.0.5",
"gulp-uglify": "2.0.0", "mocha": "^8.2.0",
"morgan": "1.7.0", "morgan": "1.7.0",
"run-sequence": "1.2.2",
"sinon": "1.17.6", "sinon": "1.17.6",
"vinyl-buffer": "1.0.0", "@babel/preset-env": "^7.12.1",
"vinyl-source-stream": "1.1.0" "eslint": "^7.12.0"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=4"
},
"dependencies": {
"map-obj": "^4.1.0"
} }
} }

@ -55,15 +55,45 @@
// ---- Syntactic Grammar ----- // ---- Syntactic Grammar -----
Grammar Grammar
= __ initializer:(Initializer __)? rules:(Rule __)+ { = __ imports:(ImportStatement __)* initializer:(Initializer __)? rules:(Rule __)+ {
return { return {
type: "grammar", type: "grammar",
initializer: extractOptional(initializer, 0), initializer: extractOptional(initializer, 0),
rules: extractList(rules, 0), rules: extractList(rules, 0),
imports: extractList(imports, 0),
location: location() location: location()
}; };
} }
ImportStatement
= "import" _ bindings:ImportBindings _ "from" _ path:StringLiteral EOS {
return {
type: "import",
path: path,
bindings: bindings,
location: location()
};
}
ImportBindings
= TopLevelImportBinding
/ NamedImportBindings
TopLevelImportBinding
= binding:ImportBinding {
return { type: "topLevelBinding", binding: binding, location: location() };
}
NamedImportBindings
= "{" __ head:(ImportBinding __) tail:("," __ ImportBinding __)* "}" {
return { type: "namedBindings", bindings: buildList(head[0], tail, 2), location: location() };
}
ImportBinding
= name:IdentifierName alias:(_ "as" _ IdentifierName)? {
return { type: "binding", name: name, alias: extractOptional(alias, 3), location: location() };
}
Initializer Initializer
= code:CodeBlock EOS { = code:CodeBlock EOS {
return { type: "initializer", code: code, location: location() }; return { type: "initializer", code: code, location: location() };
@ -242,6 +272,7 @@ SingleLineComment
Identifier Identifier
= !ReservedWord name:IdentifierName { return name; } = !ReservedWord name:IdentifierName { return name; }
// TODO: Can performance here be improved by using $?
IdentifierName "identifier" IdentifierName "identifier"
= head:IdentifierStart tail:IdentifierPart* { return head + tail.join(""); } = head:IdentifierStart tail:IdentifierPart* { return head + tail.join(""); }

@ -1,7 +1,6 @@
/* eslint-disable no-console */
"use strict"; "use strict";
/* global console */
let chai = require("chai"); let chai = require("chai");
let peg = require("../../lib/peg"); let peg = require("../../lib/peg");
let sinon = require("sinon"); let sinon = require("sinon");

@ -1,128 +0,0 @@
"use strict";
let chai = require("chai");
let peg = require("../../lib/peg");
let expect = chai.expect;
describe("plugin API", function() {
describe("use", function() {
let grammar = "start = 'a'";
it("is called for each plugin", function() {
let pluginsUsed = [false, false, false];
let plugins = [
{ use() { pluginsUsed[0] = true; } },
{ use() { pluginsUsed[1] = true; } },
{ use() { pluginsUsed[2] = true; } }
];
peg.generate(grammar, { plugins: plugins });
expect(pluginsUsed).to.deep.equal([true, true, true]);
});
it("receives configuration", function() {
let plugin = {
use(config) {
expect(config).to.be.an("object");
expect(config.parser).to.be.an("object");
expect(config.parser.parse("start = 'a'")).to.be.an("object");
expect(config.passes).to.be.an("object");
expect(config.passes.check).to.be.an("array");
config.passes.check.forEach(pass => {
expect(pass).to.be.a("function");
});
expect(config.passes.transform).to.be.an("array");
config.passes.transform.forEach(pass => {
expect(pass).to.be.a("function");
});
expect(config.passes.generate).to.be.an("array");
config.passes.generate.forEach(pass => {
expect(pass).to.be.a("function");
});
}
};
peg.generate(grammar, { plugins: [plugin] });
});
it("receives options", function() {
let plugin = {
use(config, options) {
expect(options).to.equal(generateOptions);
}
};
let generateOptions = { plugins: [plugin], foo: 42 };
peg.generate(grammar, generateOptions);
});
it("can replace parser", function() {
let plugin = {
use(config) {
let parser = peg.generate([
"start = .* {",
" return {",
" type: 'grammar',",
" rules: [",
" {",
" type: 'rule',",
" name: 'start',",
" expression: { type: 'literal', value: text(), ignoreCase: false }",
" }",
" ]",
" };",
"}"
].join("\n"));
config.parser = parser;
}
};
let parser = peg.generate("a", { plugins: [plugin] });
expect(parser.parse("a")).to.equal("a");
});
it("can change compiler passes", function() {
let plugin = {
use(config) {
function pass(ast) {
ast.code = "({ parse: function() { return 42; } })";
}
config.passes.generate = [pass];
}
};
let parser = peg.generate(grammar, { plugins: [plugin] });
expect(parser.parse("a")).to.equal(42);
});
it("can change options", function() {
let grammar = [
"a = 'x'",
"b = 'x'",
"c = 'x'"
].join("\n");
let plugin = {
use(config, options) {
options.allowedStartRules = ["b", "c"];
}
};
let parser = peg.generate(grammar, {
allowedStartRules: ["a"],
plugins: [plugin]
});
expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw();
expect(parser.parse("x", { startRule: "b" })).to.equal("x");
expect(parser.parse("x", { startRule: "c" })).to.equal("x");
});
});
});

@ -1,7 +1,6 @@
/* eslint-disable no-console */
"use strict"; "use strict";
/* global console */
let chai = require("chai"); let chai = require("chai");
let peg = require("../../lib/peg"); let peg = require("../../lib/peg");
let sinon = require("sinon"); let sinon = require("sinon");

@ -25,7 +25,7 @@ app.get("/bundle.js", (req, res) => {
}); });
browserify(files) browserify(files)
.transform(babelify, { presets: "es2015", compact: false }) .transform(babelify, { presets: "env", compact: false })
.bundle() .bundle()
.pipe(res); .pipe(res);
}); });

@ -65,6 +65,7 @@ describe("PEG.js grammar parser", function() {
function oneRuleGrammar(expression) { function oneRuleGrammar(expression) {
return { return {
type: "grammar", type: "grammar",
imports: [],
initializer: null, initializer: null,
rules: [{ type: "rule", name: "start", expression: expression }] rules: [{ type: "rule", name: "start", expression: expression }]
}; };
@ -102,6 +103,7 @@ describe("PEG.js grammar parser", function() {
let trivialGrammar = literalGrammar("abcd", false); let trivialGrammar = literalGrammar("abcd", false);
let twoRuleGrammar = { let twoRuleGrammar = {
type: "grammar", type: "grammar",
imports: [],
initializer: null, initializer: null,
rules: [ruleA, ruleB] rules: [ruleA, ruleB]
}; };
@ -226,20 +228,20 @@ describe("PEG.js grammar parser", function() {
// Canonical Grammar is "a = 'abcd'; b = 'efgh'; c = 'ijkl';". // Canonical Grammar is "a = 'abcd'; b = 'efgh'; c = 'ijkl';".
it("parses Grammar", function() { it("parses Grammar", function() {
expect("\na = 'abcd';\n").to.parseAs( expect("\na = 'abcd';\n").to.parseAs(
{ type: "grammar", initializer: null, rules: [ruleA] } { type: "grammar", imports: [], initializer: null, rules: [ruleA] }
); );
expect("\na = 'abcd';\nb = 'efgh';\nc = 'ijkl';\n").to.parseAs( expect("\na = 'abcd';\nb = 'efgh';\nc = 'ijkl';\n").to.parseAs(
{ type: "grammar", initializer: null, rules: [ruleA, ruleB, ruleC] } { type: "grammar", imports: [], initializer: null, rules: [ruleA, ruleB, ruleC] }
); );
expect("\n{ code };\na = 'abcd';\n").to.parseAs( expect("\n{ code };\na = 'abcd';\n").to.parseAs(
{ type: "grammar", initializer: initializer, rules: [ruleA] } { type: "grammar", imports: [], initializer: initializer, rules: [ruleA] }
); );
}); });
// Canonical Initializer is "{ code }". // Canonical Initializer is "{ code }".
it("parses Initializer", function() { it("parses Initializer", function() {
expect("{ code };start = 'abcd'").to.parseAs( expect("{ code };start = 'abcd'").to.parseAs(
{ type: "grammar", initializer: initializer, rules: [ruleStart] } { type: "grammar", imports: [], initializer: initializer, rules: [ruleStart] }
); );
}); });

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save