Update code format and style
This is related to my last commit. I've updated all the JavaScript files to satisfy 'eslint-config-futagozaryuu', my eslint configuration. I'm sure I've probally missed something, but I've run all NPM scripts and Gulp tasks, fixed any bugs that cropped up, and updated some stuff (mainly related to generated messages), so as far as I can, tell this conversion is over (I know I've probally jixed it just by saying this ;P).master
parent
3c6523ff83
commit
e6d018a88d
File diff suppressed because it is too large
Load Diff
@ -1,39 +1,59 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let visitor = require("../visitor");
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Removes proxy rules -- that is, rules that only delegate to other rule.
|
// Removes proxy rules -- that is, rules that only delegate to other rule.
|
||||||
function removeProxyRules(ast, options) {
|
function removeProxyRules( ast, options ) {
|
||||||
function isProxyRule(node) {
|
|
||||||
|
function isProxyRule( node ) {
|
||||||
|
|
||||||
return node.type === "rule" && node.expression.type === "rule_ref";
|
return node.type === "rule" && node.expression.type === "rule_ref";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceRuleRefs(ast, from, to) {
|
function replaceRuleRefs( ast, from, to ) {
|
||||||
let replace = visitor.build({
|
|
||||||
rule_ref(node) {
|
const replace = visitor.build( {
|
||||||
if (node.name === from) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
|
if ( node.name === from ) {
|
||||||
|
|
||||||
node.name = to;
|
node.name = to;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
|
|
||||||
|
replace( ast );
|
||||||
|
|
||||||
replace(ast);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let indices = [];
|
const indices = [];
|
||||||
|
|
||||||
|
ast.rules.forEach( ( rule, i ) => {
|
||||||
|
|
||||||
|
if ( isProxyRule( rule ) ) {
|
||||||
|
|
||||||
|
replaceRuleRefs( ast, rule.name, rule.expression.name );
|
||||||
|
if ( options.allowedStartRules.indexOf( rule.name ) === -1 ) {
|
||||||
|
|
||||||
|
indices.push( i );
|
||||||
|
|
||||||
ast.rules.forEach((rule, i) => {
|
|
||||||
if (isProxyRule(rule)) {
|
|
||||||
replaceRuleRefs(ast, rule.name, rule.expression.name);
|
|
||||||
if (options.allowedStartRules.indexOf(rule.name) === -1) {
|
|
||||||
indices.push(i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
indices.reverse();
|
indices.reverse();
|
||||||
|
|
||||||
indices.forEach(i => { ast.rules.splice(i, 1); });
|
indices.forEach( i => {
|
||||||
|
|
||||||
|
ast.rules.splice( i, 1 );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = removeProxyRules;
|
module.exports = removeProxyRules;
|
||||||
|
@ -1,28 +1,36 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let GrammarError = require("../../grammar-error");
|
const GrammarError = require( "../../grammar-error" );
|
||||||
let visitor = require("../visitor");
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Checks that each rule is defined only once.
|
// Checks that each rule is defined only once.
|
||||||
function reportDuplicateRules(ast) {
|
function reportDuplicateRules( ast ) {
|
||||||
let rules = {};
|
|
||||||
|
const rules = {};
|
||||||
|
|
||||||
|
const check = visitor.build( {
|
||||||
|
rule( node ) {
|
||||||
|
|
||||||
|
const name = node.name;
|
||||||
|
|
||||||
|
if ( Object.prototype.hasOwnProperty.call( rules, name ) ) {
|
||||||
|
|
||||||
|
const start = rules[ name ].start;
|
||||||
|
|
||||||
let check = visitor.build({
|
|
||||||
rule(node) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(rules, node.name)) {
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
"Rule \"" + node.name + "\" is already defined "
|
`Rule "${ name }" is already defined at line ${ start.line }, column ${ start.column }.`,
|
||||||
+ "at line " + rules[node.name].start.line + ", "
|
|
||||||
+ "column " + rules[node.name].start.column + ".",
|
|
||||||
node.location
|
node.location
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rules[node.name] = node.location;
|
rules[ node.name ] = node.location;
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
|
|
||||||
|
check( ast );
|
||||||
|
|
||||||
check(ast);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = reportDuplicateRules;
|
module.exports = reportDuplicateRules;
|
||||||
|
@ -1,33 +1,43 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let GrammarError = require("../../grammar-error");
|
const GrammarError = require( "../../grammar-error" );
|
||||||
let asts = require("../asts");
|
const asts = require( "../asts" );
|
||||||
let visitor = require("../visitor");
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Reports expressions that don't consume any input inside |*| or |+| in the
|
// Reports expressions that don't consume any input inside |*| or |+| in the
|
||||||
// grammar, which prevents infinite loops in the generated parser.
|
// grammar, which prevents infinite loops in the generated parser.
|
||||||
function reportInfiniteRepetition(ast) {
|
function reportInfiniteRepetition( ast ) {
|
||||||
let check = visitor.build({
|
|
||||||
zero_or_more(node) {
|
const check = visitor.build( {
|
||||||
if (!asts.alwaysConsumesOnSuccess(ast, node.expression)) {
|
zero_or_more( node ) {
|
||||||
|
|
||||||
|
if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) {
|
||||||
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||||
node.location
|
node.location
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
one_or_more(node) {
|
one_or_more( node ) {
|
||||||
if (!asts.alwaysConsumesOnSuccess(ast, node.expression)) {
|
|
||||||
|
if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) {
|
||||||
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||||
node.location
|
node.location
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
|
|
||||||
|
check( ast );
|
||||||
|
|
||||||
check(ast);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = reportInfiniteRepetition;
|
module.exports = reportInfiniteRepetition;
|
||||||
|
@ -1,32 +1,43 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let GrammarError = require("../../grammar-error");
|
const GrammarError = require( "../../grammar-error" );
|
||||||
let asts = require("../asts");
|
const asts = require( "../asts" );
|
||||||
let visitor = require("../visitor");
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Checks that all referenced rules exist.
|
// Checks that all referenced rules exist.
|
||||||
function reportUndefinedRules(ast, options) {
|
function reportUndefinedRules( ast, options ) {
|
||||||
let check = visitor.build({
|
|
||||||
rule_ref(node) {
|
const check = visitor.build( {
|
||||||
if (!asts.findRule(ast, node.name)) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
|
if ( ! asts.findRule( ast, node.name ) ) {
|
||||||
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
"Rule \"" + node.name + "\" is not defined.",
|
`Rule "${ node.name }" is not defined.`,
|
||||||
node.location
|
node.location
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
|
|
||||||
check(ast);
|
check( ast );
|
||||||
|
|
||||||
|
if ( options.allowedStartRules ) {
|
||||||
|
|
||||||
|
options.allowedStartRules.forEach( rule => {
|
||||||
|
|
||||||
|
if ( ! asts.findRule( ast, rule ) ) {
|
||||||
|
|
||||||
|
throw new GrammarError( `Start rule "${ rule }" is not defined.` );
|
||||||
|
|
||||||
if (options.allowedStartRules) {
|
|
||||||
options.allowedStartRules.forEach(rule => {
|
|
||||||
if (!asts.findRule(ast, rule)) {
|
|
||||||
throw new GrammarError(
|
|
||||||
"Start rule \"" + rule + "\" is not defined.");
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = reportUndefinedRules;
|
module.exports = reportUndefinedRules;
|
||||||
|
@ -1,150 +1,179 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
/* eslint camelcase:0, max-len:0, one-var:0 */
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Measures impact of a Git commit (or multiple commits) on generated parsers
|
// Measures impact of a Git commit (or multiple commits) on generated parsers
|
||||||
// speed and size. Makes sense to use only on PEG.js git repository checkout.
|
// speed and size. Makes sense to use only on PEG.js git repository checkout.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
/* eslint prefer-const: 0 */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let child_process = require("child_process");
|
const child_process = require( "child_process" );
|
||||||
let fs = require("fs");
|
const fs = require( "fs" );
|
||||||
let os = require("os");
|
const os = require( "os" );
|
||||||
let path = require("path");
|
const path = require( "path" );
|
||||||
let glob = require("glob");
|
const dedent = require( "dedent" );
|
||||||
|
const glob = require( "glob" );
|
||||||
|
|
||||||
// Current Working Directory
|
// Current Working Directory
|
||||||
|
|
||||||
let cwd = path.join(__dirname, "..");
|
const cwd = path.join( __dirname, ".." );
|
||||||
|
if ( process.cwd() !== cwd ) process.chdir( cwd );
|
||||||
if (process.cwd() !== cwd) {
|
|
||||||
process.chdir(cwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execution Files
|
// Execution Files
|
||||||
|
|
||||||
let PEGJS_BIN = "bin/peg.js";
|
let PEGJS_BIN = "bin/peg.js";
|
||||||
let BENCHMARK_BIN = "test/benchmark/run";
|
let BENCHMARK_BIN = "test/benchmark/run";
|
||||||
|
|
||||||
if (!fs.existsSync(PEGJS_BIN)) {
|
if ( ! fs.existsSync( PEGJS_BIN ) ) {
|
||||||
|
|
||||||
PEGJS_BIN = "bin/pegjs";
|
PEGJS_BIN = "bin/pegjs";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(BENCHMARK_BIN)) {
|
if ( ! fs.existsSync( BENCHMARK_BIN ) ) {
|
||||||
|
|
||||||
BENCHMARK_BIN = "benchmark/run";
|
BENCHMARK_BIN = "benchmark/run";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
||||||
let print = console.log;
|
function echo( message ) {
|
||||||
|
|
||||||
|
process.stdout.write( message );
|
||||||
|
|
||||||
function echo(message) {
|
|
||||||
process.stdout.write(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exec(command) {
|
function exec( command ) {
|
||||||
return child_process.execSync(command, { encoding: "utf8" });
|
|
||||||
|
return child_process.execSync( command, { encoding: "utf8" } );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepare(commit) {
|
function prepare( commit ) {
|
||||||
exec(`git checkout --quiet "${commit}"`);
|
|
||||||
|
exec( `git checkout --quiet "${ commit }"` );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runBenchmark() {
|
function runBenchmark() {
|
||||||
|
|
||||||
return parseFloat(
|
return parseFloat(
|
||||||
exec("node " + BENCHMARK_BIN)
|
exec( "node " + BENCHMARK_BIN )
|
||||||
|
|
||||||
// Split by table seprator, reverse and return the total bytes per second
|
// Split by table seprator, reverse and return the total bytes per second
|
||||||
.split("│")
|
.split( "│" )
|
||||||
.reverse()[1]
|
.reverse()[ 1 ]
|
||||||
|
|
||||||
// Trim the whitespaces and remove ` kB/s` from the end
|
// Trim the whitespaces and remove ` kB/s` from the end
|
||||||
.trim()
|
.trim()
|
||||||
.slice(0, -5)
|
.slice( 0, -5 )
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function measureSpeed() {
|
function measureSpeed() {
|
||||||
return (runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() / 5).toFixed(2);
|
|
||||||
|
return ( runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() / 5 ).toFixed( 2 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function measureSize() {
|
function measureSize() {
|
||||||
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
|
|
||||||
glob.sync("examples/*.pegjs")
|
glob.sync( "examples/*.pegjs" )
|
||||||
.forEach(example => {
|
.forEach( example => {
|
||||||
exec(`node ${PEGJS_BIN} ${example}`);
|
|
||||||
example = example.slice(0, -5) + "js";
|
exec( `node ${ PEGJS_BIN } ${ example }` );
|
||||||
size += fs.statSync(example).size;
|
example = example.slice( 0, -5 ) + "js";
|
||||||
fs.unlinkSync(example);
|
size += fs.statSync( example ).size;
|
||||||
});
|
fs.unlinkSync( example );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function difference($1, $2) {
|
function difference( $1, $2 ) {
|
||||||
return (($2 / $1 - 1) * 100).toFixed(4);
|
|
||||||
|
return ( ( $2 / $1 - 1 ) * 100 ).toFixed( 4 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare
|
// Prepare
|
||||||
|
|
||||||
let argv = process.argv.slice(2);
|
const argv = process.argv.slice( 2 );
|
||||||
let commit_before, commit_after;
|
let commit_before, commit_after;
|
||||||
|
|
||||||
if (argv.length === 1) {
|
if ( argv.length === 1 ) {
|
||||||
commit_before = argv[0] + "~1";
|
|
||||||
commit_after = argv[0];
|
commit_before = argv[ 0 ] + "~1";
|
||||||
} else if (argv.length === 2) {
|
commit_after = argv[ 0 ];
|
||||||
commit_before = argv[0];
|
|
||||||
commit_after = argv[1];
|
} else if ( argv.length === 2 ) {
|
||||||
|
|
||||||
|
commit_before = argv[ 0 ];
|
||||||
|
commit_after = argv[ 1 ];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
print("Usage:");
|
|
||||||
print("");
|
console.log( dedent`
|
||||||
print(" test/impact <commit>");
|
|
||||||
print(" test/impact <commit_before> <commit_after>");
|
Usage:
|
||||||
print("");
|
|
||||||
print("Measures impact of a Git commit (or multiple commits) on generated parsers'");
|
test/impact <commit>
|
||||||
print("speed and size. Makes sense to use only on PEG.js Git repository checkout.");
|
test/impact <commit_before> <commit_after>
|
||||||
print("");
|
|
||||||
process.exit(1);
|
Measures impact of a Git commit (or multiple commits) on generated parser's
|
||||||
|
speed and size. Makes sense to use only on PEG.js Git repository checkout.
|
||||||
|
|
||||||
|
` );
|
||||||
|
process.exit( 1 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Measure
|
// Measure
|
||||||
|
|
||||||
let branch = exec("git rev-parse --abbrev-ref HEAD");
|
const branch = exec( "git rev-parse --abbrev-ref HEAD" );
|
||||||
let speed1, size1, speed2, size2;
|
let speed1, size1, speed2, size2;
|
||||||
|
|
||||||
echo(`Measuring commit ${commit_before}...`);
|
echo( `Measuring commit ${ commit_before }...` );
|
||||||
prepare(commit_before);
|
prepare( commit_before );
|
||||||
speed1 = measureSpeed();
|
speed1 = measureSpeed();
|
||||||
size1 = measureSize();
|
size1 = measureSize();
|
||||||
echo(" OK" + os.EOL);
|
echo( " OK" + os.EOL );
|
||||||
|
|
||||||
echo(`Measuring commit ${commit_after}...`);
|
echo( `Measuring commit ${ commit_after }...` );
|
||||||
prepare(commit_after);
|
prepare( commit_after );
|
||||||
speed2 = measureSpeed();
|
speed2 = measureSpeed();
|
||||||
size2 = measureSize();
|
size2 = measureSize();
|
||||||
echo(" OK" + os.EOL);
|
echo( " OK" + os.EOL );
|
||||||
|
|
||||||
// Finish
|
// Finish
|
||||||
|
|
||||||
prepare(branch);
|
prepare( branch );
|
||||||
|
|
||||||
print(`
|
console.log( dedent`
|
||||||
test/impact ${commit_before} ${commit_after}
|
|
||||||
|
test/impact ${ commit_before } ${ commit_after }
|
||||||
|
|
||||||
Speed impact
|
Speed impact
|
||||||
------------
|
------------
|
||||||
Before: ${speed1} kB/s
|
Before: ${ speed1 } kB/s
|
||||||
After: ${speed2} kB/s
|
After: ${ speed2 } kB/s
|
||||||
Difference: ${difference(parseFloat(speed1), parseFloat(speed2))}%
|
Difference: ${ difference( parseFloat( speed1 ), parseFloat( speed2 ) ) }%
|
||||||
|
|
||||||
Size impact
|
Size impact
|
||||||
-----------
|
-----------
|
||||||
Before: ${size1} b
|
Before: ${ size1 } b
|
||||||
After: ${size2} b
|
After: ${ size2 } b
|
||||||
Difference: ${difference(size1, size2)}%
|
Difference: ${ difference( size1, size2 ) }%
|
||||||
|
|
||||||
|
- Measured by /test/impact with Node.js ${ process.version }
|
||||||
|
- Your system: ${ os.type() } ${ os.release() } ${ os.arch() }.
|
||||||
|
|
||||||
- Measured by /test/impact with Node.js ${process.version}
|
` );
|
||||||
- Your system: ${os.type()} ${os.release()} ${os.arch()}.
|
|
||||||
`);
|
|
||||||
|
@ -1,128 +1,185 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let chai = require("chai");
|
const chai = require( "chai" );
|
||||||
let peg = require("../../../lib/peg");
|
const peg = require( "../../../lib/peg" );
|
||||||
|
|
||||||
let expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("plugin API", function() {
|
describe( "plugin API", function () {
|
||||||
describe("use", function() {
|
|
||||||
let grammar = "start = 'a'";
|
|
||||||
|
|
||||||
it("is called for each plugin", function() {
|
describe( "use", function () {
|
||||||
let pluginsUsed = [false, false, false];
|
|
||||||
let plugins = [
|
const grammar = "start = 'a'";
|
||||||
{ use() { pluginsUsed[0] = true; } },
|
|
||||||
{ use() { pluginsUsed[1] = true; } },
|
it( "is called for each plugin", function () {
|
||||||
{ use() { pluginsUsed[2] = true; } }
|
|
||||||
|
const pluginsUsed = [ false, false, false ];
|
||||||
|
const plugins = [
|
||||||
|
{ use() {
|
||||||
|
|
||||||
|
pluginsUsed[ 0 ] = true;
|
||||||
|
|
||||||
|
} },
|
||||||
|
{ use() {
|
||||||
|
|
||||||
|
pluginsUsed[ 1 ] = true;
|
||||||
|
|
||||||
|
} },
|
||||||
|
{ use() {
|
||||||
|
|
||||||
|
pluginsUsed[ 2 ] = true;
|
||||||
|
|
||||||
|
} }
|
||||||
];
|
];
|
||||||
|
|
||||||
peg.generate(grammar, { plugins: plugins });
|
peg.generate( grammar, { plugins: plugins } );
|
||||||
|
|
||||||
|
expect( pluginsUsed ).to.deep.equal( [ true, true, true ] );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( "receives configuration", function () {
|
||||||
|
|
||||||
|
const plugin = {
|
||||||
|
use( config ) {
|
||||||
|
|
||||||
|
expect( config ).to.be.an( "object" );
|
||||||
|
|
||||||
expect(pluginsUsed).to.deep.equal([true, true, true]);
|
expect( config.parser ).to.be.an( "object" );
|
||||||
});
|
expect( config.parser.parse( "start = 'a'" ) ).to.be.an( "object" );
|
||||||
|
|
||||||
it("receives configuration", function() {
|
expect( config.passes ).to.be.an( "object" );
|
||||||
let plugin = {
|
|
||||||
use(config) {
|
|
||||||
expect(config).to.be.an("object");
|
|
||||||
|
|
||||||
expect(config.parser).to.be.an("object");
|
expect( config.passes.check ).to.be.an( "array" );
|
||||||
expect(config.parser.parse("start = 'a'")).to.be.an("object");
|
config.passes.check.forEach( pass => {
|
||||||
|
|
||||||
expect(config.passes).to.be.an("object");
|
expect( pass ).to.be.a( "function" );
|
||||||
|
|
||||||
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");
|
expect( config.passes.transform ).to.be.an( "array" );
|
||||||
config.passes.transform.forEach(pass => {
|
config.passes.transform.forEach( pass => {
|
||||||
expect(pass).to.be.a("function");
|
|
||||||
});
|
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" );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
expect(config.passes.generate).to.be.an("array");
|
|
||||||
config.passes.generate.forEach(pass => {
|
|
||||||
expect(pass).to.be.a("function");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
peg.generate(grammar, { plugins: [plugin] });
|
peg.generate( grammar, { plugins: [ plugin ] } );
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( "receives options", function () {
|
||||||
|
|
||||||
|
const generateOptions = {
|
||||||
|
plugins: [ {
|
||||||
|
use( config, options ) {
|
||||||
|
|
||||||
|
expect( options ).to.equal( generateOptions );
|
||||||
|
|
||||||
it("receives options", function() {
|
|
||||||
let plugin = {
|
|
||||||
use(config, options) {
|
|
||||||
expect(options).to.equal(generateOptions);
|
|
||||||
}
|
}
|
||||||
|
} ],
|
||||||
|
foo: 42
|
||||||
};
|
};
|
||||||
let generateOptions = { plugins: [plugin], foo: 42 };
|
|
||||||
|
peg.generate( grammar, generateOptions );
|
||||||
peg.generate(grammar, generateOptions);
|
|
||||||
});
|
} );
|
||||||
|
|
||||||
it("can replace parser", function() {
|
it( "can replace parser", function () {
|
||||||
let plugin = {
|
|
||||||
use(config) {
|
const plugin = {
|
||||||
let parser = peg.generate([
|
use( config ) {
|
||||||
"start = .* {",
|
|
||||||
" return {",
|
config.parser = peg.generate( `
|
||||||
" type: 'grammar',",
|
|
||||||
" rules: [",
|
start = .* {
|
||||||
" {",
|
return {
|
||||||
" type: 'rule',",
|
type: 'grammar',
|
||||||
" name: 'start',",
|
rules: [{
|
||||||
" expression: { type: 'literal', value: text(), ignoreCase: false }",
|
type: 'rule',
|
||||||
" }",
|
name: 'start',
|
||||||
" ]",
|
expression: {
|
||||||
" };",
|
type: 'literal',
|
||||||
"}"
|
value: text(),
|
||||||
].join("\n"));
|
ignoreCase: false
|
||||||
|
|
||||||
config.parser = parser;
|
|
||||||
}
|
}
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
let parser = peg.generate("a", { plugins: [plugin] });
|
}
|
||||||
|
|
||||||
|
` );
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parser = peg.generate( "a", { plugins: [ plugin ] } );
|
||||||
|
expect( parser.parse( "a" ) ).to.equal( "a" );
|
||||||
|
|
||||||
expect(parser.parse("a")).to.equal("a");
|
} );
|
||||||
});
|
|
||||||
|
it( "can change compiler passes", function () {
|
||||||
|
|
||||||
|
const plugin = {
|
||||||
|
use( config ) {
|
||||||
|
|
||||||
|
function pass( ast ) {
|
||||||
|
|
||||||
it("can change compiler passes", function() {
|
|
||||||
let plugin = {
|
|
||||||
use(config) {
|
|
||||||
function pass(ast) {
|
|
||||||
ast.code = "({ parse: function() { return 42; } })";
|
ast.code = "({ parse: function() { return 42; } })";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.passes.generate = [pass];
|
config.passes.generate = [ pass ];
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let parser = peg.generate(grammar, { plugins: [plugin] });
|
|
||||||
|
const parser = peg.generate( grammar, { plugins: [ plugin ] } );
|
||||||
expect(parser.parse("a")).to.equal(42);
|
expect( parser.parse( "a" ) ).to.equal( 42 );
|
||||||
});
|
|
||||||
|
} );
|
||||||
it("can change options", function() {
|
|
||||||
let grammar = [
|
it( "can change options", function () {
|
||||||
"a = 'x'",
|
|
||||||
"b = 'x'",
|
const grammar = `
|
||||||
"c = 'x'"
|
|
||||||
].join("\n");
|
a = 'x'
|
||||||
let plugin = {
|
b = 'x'
|
||||||
use(config, options) {
|
c = 'x'
|
||||||
options.allowedStartRules = ["b", "c"];
|
|
||||||
|
`;
|
||||||
|
const plugin = {
|
||||||
|
use( config, options ) {
|
||||||
|
|
||||||
|
options.allowedStartRules = [ "b", "c" ];
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let parser = peg.generate(grammar, {
|
|
||||||
allowedStartRules: ["a"],
|
const parser = peg.generate( grammar, {
|
||||||
plugins: [plugin]
|
allowedStartRules: [ "a" ],
|
||||||
});
|
plugins: [ plugin ]
|
||||||
|
} );
|
||||||
expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw();
|
|
||||||
expect(parser.parse("x", { startRule: "b" })).to.equal("x");
|
expect( () => {
|
||||||
expect(parser.parse("x", { startRule: "c" })).to.equal("x");
|
|
||||||
});
|
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" );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,90 +1,112 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let parser = require("../../../../../lib/parser");
|
const parser = require( "../../../../../lib/parser" );
|
||||||
|
|
||||||
module.exports = function(chai, utils) {
|
module.exports = function ( chai, utils ) {
|
||||||
let Assertion = chai.Assertion;
|
|
||||||
|
|
||||||
Assertion.addMethod("changeAST", function(grammar, props, options) {
|
const Assertion = chai.Assertion;
|
||||||
options = options !== undefined ? options : {};
|
|
||||||
|
|
||||||
function matchProps(value, props) {
|
Assertion.addMethod( "changeAST", function ( grammar, props, options ) {
|
||||||
function isArray(value) {
|
|
||||||
return Object.prototype.toString.apply(value) === "[object Array]";
|
options = typeof options !== "undefined" ? options : {};
|
||||||
}
|
|
||||||
|
function matchProps( value, props ) {
|
||||||
|
|
||||||
|
function isObject( value ) {
|
||||||
|
|
||||||
function isObject(value) {
|
|
||||||
return value !== null && typeof value === "object";
|
return value !== null && typeof value === "object";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isArray(props)) {
|
if ( Array.isArray( props ) ) {
|
||||||
if (!isArray(value)) { return false; }
|
|
||||||
|
if ( ! Array.isArray( value ) ) return false;
|
||||||
|
if ( value.length !== props.length ) return false;
|
||||||
|
|
||||||
|
for ( let i = 0; i < props.length; i++ ) {
|
||||||
|
|
||||||
|
if ( ! matchProps( value[ i ], props[ i ] ) ) return false;
|
||||||
|
|
||||||
if (value.length !== props.length) { return false; }
|
|
||||||
for (let i = 0; i < props.length; i++) {
|
|
||||||
if (!matchProps(value[i], props[i])) { return false; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (isObject(props)) {
|
|
||||||
if (!isObject(value)) { return false; }
|
|
||||||
|
|
||||||
let keys = Object.keys(props);
|
} else if ( isObject( props ) ) {
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
let key = keys[i];
|
if ( ! isObject( value ) ) return false;
|
||||||
|
|
||||||
|
const keys = Object.keys( props );
|
||||||
|
for ( let i = 0; i < keys.length; i++ ) {
|
||||||
|
|
||||||
|
const key = keys[ i ];
|
||||||
|
|
||||||
if (!(key in value)) { return false; }
|
if ( ! ( key in value ) ) return false;
|
||||||
|
if ( ! matchProps( value[ key ], props[ key ] ) ) return false;
|
||||||
|
|
||||||
if (!matchProps(value[key], props[key])) { return false; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return value === props;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return value === props;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ast = parser.parse(grammar);
|
const ast = parser.parse( grammar );
|
||||||
|
|
||||||
utils.flag(this, "object")(ast, options);
|
utils.flag( this, "object" )( ast, options );
|
||||||
|
|
||||||
this.assert(
|
this.assert(
|
||||||
matchProps(ast, props),
|
matchProps( ast, props ),
|
||||||
"expected #{this} to change the AST to match #{exp}",
|
"expected #{this} to change the AST to match #{exp}",
|
||||||
"expected #{this} to not change the AST to match #{exp}",
|
"expected #{this} to not change the AST to match #{exp}",
|
||||||
props,
|
props,
|
||||||
ast
|
ast
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
Assertion.addMethod("reportError", function(grammar, props, options) {
|
} );
|
||||||
options = options !== undefined ? options : {};
|
|
||||||
|
Assertion.addMethod( "reportError", function ( grammar, props, options ) {
|
||||||
|
|
||||||
|
options = typeof options !== "undefined" ? options : {};
|
||||||
|
|
||||||
let ast = parser.parse(grammar);
|
const ast = parser.parse( grammar );
|
||||||
|
|
||||||
let passed, result;
|
let passed, result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
utils.flag(this, "object")(ast, options);
|
|
||||||
|
utils.flag( this, "object" )( ast, options );
|
||||||
passed = true;
|
passed = true;
|
||||||
} catch (e) {
|
|
||||||
|
} catch ( e ) {
|
||||||
|
|
||||||
result = e;
|
result = e;
|
||||||
passed = false;
|
passed = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assert(
|
this.assert(
|
||||||
!passed,
|
! passed,
|
||||||
"expected #{this} to report an error but it didn't",
|
"expected #{this} to report an error but it didn't",
|
||||||
"expected #{this} to not report an error but #{act} was reported",
|
"expected #{this} to not report an error but #{act} was reported",
|
||||||
null,
|
null,
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!passed && props !== undefined) {
|
if ( ! passed && typeof props !== "undefined" ) {
|
||||||
Object.keys(props).forEach(key => {
|
|
||||||
new Assertion(result).to.have.property(key)
|
Object.keys( props ).forEach( key => {
|
||||||
.that.is.deep.equal(props[key]);
|
|
||||||
});
|
new Assertion( result )
|
||||||
|
.to.have.property( key )
|
||||||
|
.that.is.deep.equal( props[ key ] );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,63 +1,83 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let chai = require("chai");
|
const chai = require( "chai" );
|
||||||
let helpers = require("./helpers");
|
const helpers = require( "./helpers" );
|
||||||
let pass = require("../../../../../lib/compiler/passes/report-duplicate-labels");
|
const pass = require( "../../../../../lib/compiler/passes/report-duplicate-labels" );
|
||||||
|
|
||||||
chai.use(helpers);
|
chai.use( helpers );
|
||||||
|
|
||||||
let expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("compiler pass |reportDuplicateLabels|", function() {
|
describe( "compiler pass |reportDuplicateLabels|", function () {
|
||||||
describe("in a sequence", function() {
|
|
||||||
it("reports labels duplicate with labels of preceding elements", function() {
|
describe( "in a sequence", function () {
|
||||||
expect(pass).to.reportError("start = a:'a' a:'a'", {
|
|
||||||
|
it( "reports labels duplicate with labels of preceding elements", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = a:'a' a:'a'", {
|
||||||
message: "Label \"a\" is already defined at line 1, column 9.",
|
message: "Label \"a\" is already defined at line 1, column 9.",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 14, line: 1, column: 15 },
|
start: { offset: 14, line: 1, column: 15 },
|
||||||
end: { offset: 19, line: 1, column: 20 }
|
end: { offset: 19, line: 1, column: 20 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
} );
|
||||||
it("doesn't report labels duplicate with labels in subexpressions", function() {
|
|
||||||
expect(pass).to.not.reportError("start = ('a' / a:'a' / 'a') a:'a'");
|
it( "doesn't report labels duplicate with labels in subexpressions", function () {
|
||||||
expect(pass).to.not.reportError("start = (a:'a' { }) a:'a'");
|
|
||||||
expect(pass).to.not.reportError("start = ('a' a:'a' 'a') a:'a'");
|
expect( pass ).to.not.reportError( "start = ('a' / a:'a' / 'a') a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = b:(a:'a') a:'a'");
|
expect( pass ).to.not.reportError( "start = (a:'a' { }) a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = $(a:'a') a:'a'");
|
expect( pass ).to.not.reportError( "start = ('a' a:'a' 'a') a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = &(a:'a') a:'a'");
|
expect( pass ).to.not.reportError( "start = b:(a:'a') a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = !(a:'a') a:'a'");
|
expect( pass ).to.not.reportError( "start = $(a:'a') a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = (a:'a')? a:'a'");
|
expect( pass ).to.not.reportError( "start = &(a:'a') a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = (a:'a')* a:'a'");
|
expect( pass ).to.not.reportError( "start = !(a:'a') a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = (a:'a')+ a:'a'");
|
expect( pass ).to.not.reportError( "start = (a:'a')? a:'a'" );
|
||||||
expect(pass).to.not.reportError("start = (a:'a') a:'a'");
|
expect( pass ).to.not.reportError( "start = (a:'a')* a:'a'" );
|
||||||
});
|
expect( pass ).to.not.reportError( "start = (a:'a')+ a:'a'" );
|
||||||
});
|
expect( pass ).to.not.reportError( "start = (a:'a') a:'a'" );
|
||||||
|
|
||||||
describe("in a choice", function() {
|
} );
|
||||||
it("doesn't report labels duplicate with labels of preceding alternatives", function() {
|
|
||||||
expect(pass).to.not.reportError("start = a:'a' / a:'a'");
|
} );
|
||||||
});
|
|
||||||
});
|
describe( "in a choice", function () {
|
||||||
|
|
||||||
describe("in outer sequence", function() {
|
it( "doesn't report labels duplicate with labels of preceding alternatives", function () {
|
||||||
it("reports labels duplicate with labels of preceding elements", function() {
|
|
||||||
expect(pass).to.reportError("start = a:'a' (a:'a')", {
|
expect( pass ).to.not.reportError( "start = a:'a' / a:'a'" );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
describe( "in outer sequence", function () {
|
||||||
|
|
||||||
|
it( "reports labels duplicate with labels of preceding elements", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = a:'a' (a:'a')", {
|
||||||
message: "Label \"a\" is already defined at line 1, column 9.",
|
message: "Label \"a\" is already defined at line 1, column 9.",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 15, line: 1, column: 16 },
|
start: { offset: 15, line: 1, column: 16 },
|
||||||
end: { offset: 20, line: 1, column: 21 }
|
end: { offset: 20, line: 1, column: 21 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
} );
|
||||||
it("doesn't report labels duplicate with the label of the current element", function() {
|
|
||||||
expect(pass).to.not.reportError("start = a:(a:'a')");
|
it( "doesn't report labels duplicate with the label of the current element", function () {
|
||||||
});
|
|
||||||
|
expect( pass ).to.not.reportError( "start = a:(a:'a')" );
|
||||||
it("doesn't report labels duplicate with labels of following elements", function() {
|
|
||||||
expect(pass).to.not.reportError("start = (a:'a') a:'a'");
|
} );
|
||||||
});
|
|
||||||
});
|
it( "doesn't report labels duplicate with labels of following elements", function () {
|
||||||
});
|
|
||||||
|
expect( pass ).to.not.reportError( "start = (a:'a') a:'a'" );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
@ -1,24 +1,28 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let chai = require("chai");
|
const chai = require( "chai" );
|
||||||
let helpers = require("./helpers");
|
const helpers = require( "./helpers" );
|
||||||
let pass = require("../../../../../lib/compiler/passes/report-duplicate-rules");
|
const pass = require( "../../../../../lib/compiler/passes/report-duplicate-rules" );
|
||||||
|
|
||||||
chai.use(helpers);
|
chai.use( helpers );
|
||||||
|
|
||||||
let expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("compiler pass |reportDuplicateRules|", function() {
|
describe( "compiler pass |reportDuplicateRules|", function () {
|
||||||
it("reports duplicate rules", function() {
|
|
||||||
expect(pass).to.reportError([
|
it( "reports duplicate rules", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( [
|
||||||
"start = 'a'",
|
"start = 'a'",
|
||||||
"start = 'b'"
|
"start = 'b'"
|
||||||
].join("\n"), {
|
].join( "\n" ), {
|
||||||
message: "Rule \"start\" is already defined at line 1, column 1.",
|
message: "Rule \"start\" is already defined at line 1, column 1.",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 12, line: 2, column: 1 },
|
start: { offset: 12, line: 2, column: 1 },
|
||||||
end: { offset: 23, line: 2, column: 12 }
|
end: { offset: 23, line: 2, column: 12 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
});
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
@ -1,119 +1,135 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let chai = require("chai");
|
const chai = require( "chai" );
|
||||||
let helpers = require("./helpers");
|
const helpers = require( "./helpers" );
|
||||||
let pass = require("../../../../../lib/compiler/passes/report-infinite-recursion");
|
const pass = require( "../../../../../lib/compiler/passes/report-infinite-recursion" );
|
||||||
|
|
||||||
chai.use(helpers);
|
chai.use( helpers );
|
||||||
|
|
||||||
let expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("compiler pass |reportInfiniteRecursion|", function() {
|
describe( "compiler pass |reportInfiniteRecursion|", function () {
|
||||||
it("reports direct left recursion", function() {
|
|
||||||
expect(pass).to.reportError("start = start", {
|
it( "reports direct left recursion", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = start", {
|
||||||
message: "Possible infinite loop when parsing (left recursion: start -> start).",
|
message: "Possible infinite loop when parsing (left recursion: start -> start).",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 8, line: 1, column: 9 },
|
start: { offset: 8, line: 1, column: 9 },
|
||||||
end: { offset: 13, line: 1, column: 14 }
|
end: { offset: 13, line: 1, column: 14 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( "reports indirect left recursion", function () {
|
||||||
|
|
||||||
it("reports indirect left recursion", function() {
|
expect( pass ).to.reportError( [
|
||||||
expect(pass).to.reportError([
|
|
||||||
"start = stop",
|
"start = stop",
|
||||||
"stop = start"
|
"stop = start"
|
||||||
].join("\n"), {
|
].join( "\n" ), {
|
||||||
message: "Possible infinite loop when parsing (left recursion: start -> stop -> start).",
|
message: "Possible infinite loop when parsing (left recursion: start -> stop -> start).",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 20, line: 2, column: 8 },
|
start: { offset: 20, line: 2, column: 8 },
|
||||||
end: { offset: 25, line: 2, column: 13 }
|
end: { offset: 25, line: 2, column: 13 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
describe( "in sequences", function () {
|
||||||
|
|
||||||
|
it( "reports left recursion if all preceding elements match empty string", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = '' '' '' start" );
|
||||||
|
|
||||||
describe("in sequences", function() {
|
} );
|
||||||
it("reports left recursion if all preceding elements match empty string", function() {
|
|
||||||
expect(pass).to.reportError("start = '' '' '' start");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't report left recursion if some preceding element doesn't match empty string", function() {
|
it( "doesn't report left recursion if some preceding element doesn't match empty string", function () {
|
||||||
expect(pass).to.not.reportError("start = 'a' '' '' start");
|
|
||||||
expect(pass).to.not.reportError("start = '' 'a' '' start");
|
expect( pass ).to.not.reportError( "start = 'a' '' '' start" );
|
||||||
expect(pass).to.not.reportError("start = '' '' 'a' start");
|
expect( pass ).to.not.reportError( "start = '' 'a' '' start" );
|
||||||
});
|
expect( pass ).to.not.reportError( "start = '' '' 'a' start" );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
// Regression test for #359.
|
// Regression test for #359.
|
||||||
it("reports left recursion when rule reference is wrapped in an expression", function() {
|
it( "reports left recursion when rule reference is wrapped in an expression", function () {
|
||||||
expect(pass).to.reportError("start = '' start?");
|
|
||||||
});
|
expect( pass ).to.reportError( "start = '' start?" );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
it("computes expressions that always consume input on success correctly", function() {
|
it( "computes expressions that always consume input on success correctly", function () {
|
||||||
expect(pass).to.reportError([
|
|
||||||
|
expect( pass ).to.reportError( [
|
||||||
"start = a start",
|
"start = a start",
|
||||||
"a 'a' = ''"
|
"a 'a' = ''"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
expect(pass).to.not.reportError([
|
expect( pass ).to.not.reportError( [
|
||||||
"start = a start",
|
"start = a start",
|
||||||
"a 'a' = 'a'"
|
"a 'a' = 'a'"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('' / 'a' / 'b') start");
|
expect( pass ).to.reportError( "start = ('' / 'a' / 'b') start" );
|
||||||
expect(pass).to.reportError("start = ('a' / '' / 'b') start");
|
expect( pass ).to.reportError( "start = ('a' / '' / 'b') start" );
|
||||||
expect(pass).to.reportError("start = ('a' / 'b' / '') start");
|
expect( pass ).to.reportError( "start = ('a' / 'b' / '') start" );
|
||||||
expect(pass).to.not.reportError("start = ('a' / 'b' / 'c') start");
|
expect( pass ).to.not.reportError( "start = ('a' / 'b' / 'c') start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('' { }) start");
|
expect( pass ).to.reportError( "start = ('' { }) start" );
|
||||||
expect(pass).to.not.reportError("start = ('a' { }) start");
|
expect( pass ).to.not.reportError( "start = ('a' { }) start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('' '' '') start");
|
expect( pass ).to.reportError( "start = ('' '' '') start" );
|
||||||
expect(pass).to.not.reportError("start = ('a' '' '') start");
|
expect( pass ).to.not.reportError( "start = ('a' '' '') start" );
|
||||||
expect(pass).to.not.reportError("start = ('' 'a' '') start");
|
expect( pass ).to.not.reportError( "start = ('' 'a' '') start" );
|
||||||
expect(pass).to.not.reportError("start = ('' '' 'a') start");
|
expect( pass ).to.not.reportError( "start = ('' '' 'a') start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = a:'' start");
|
expect( pass ).to.reportError( "start = a:'' start" );
|
||||||
expect(pass).to.not.reportError("start = a:'a' start");
|
expect( pass ).to.not.reportError( "start = a:'a' start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = $'' start");
|
expect( pass ).to.reportError( "start = $'' start" );
|
||||||
expect(pass).to.not.reportError("start = $'a' start");
|
expect( pass ).to.not.reportError( "start = $'a' start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = &'' start");
|
expect( pass ).to.reportError( "start = &'' start" );
|
||||||
expect(pass).to.reportError("start = &'a' start");
|
expect( pass ).to.reportError( "start = &'a' start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = !'' start");
|
expect( pass ).to.reportError( "start = !'' start" );
|
||||||
expect(pass).to.reportError("start = !'a' start");
|
expect( pass ).to.reportError( "start = !'a' start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ''? start");
|
expect( pass ).to.reportError( "start = ''? start" );
|
||||||
expect(pass).to.reportError("start = 'a'? start");
|
expect( pass ).to.reportError( "start = 'a'? start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ''* start");
|
expect( pass ).to.reportError( "start = ''* start" );
|
||||||
expect(pass).to.reportError("start = 'a'* start");
|
expect( pass ).to.reportError( "start = 'a'* start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ''+ start");
|
expect( pass ).to.reportError( "start = ''+ start" );
|
||||||
expect(pass).to.not.reportError("start = 'a'+ start");
|
expect( pass ).to.not.reportError( "start = 'a'+ start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('') start");
|
expect( pass ).to.reportError( "start = ('') start" );
|
||||||
expect(pass).to.not.reportError("start = ('a') start");
|
expect( pass ).to.not.reportError( "start = ('a') start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = &{ } start");
|
expect( pass ).to.reportError( "start = &{ } start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = !{ } start");
|
expect( pass ).to.reportError( "start = !{ } start" );
|
||||||
|
|
||||||
expect(pass).to.reportError([
|
expect( pass ).to.reportError( [
|
||||||
"start = a start",
|
"start = a start",
|
||||||
"a = ''"
|
"a = ''"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
expect(pass).to.not.reportError([
|
expect( pass ).to.not.reportError( [
|
||||||
"start = a start",
|
"start = a start",
|
||||||
"a = 'a'"
|
"a = 'a'"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = '' start" );
|
||||||
|
expect( pass ).to.not.reportError( "start = 'a' start" );
|
||||||
|
|
||||||
|
expect( pass ).to.not.reportError( "start = [a-d] start" );
|
||||||
|
|
||||||
|
expect( pass ).to.not.reportError( "start = . start" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = '' start");
|
} );
|
||||||
expect(pass).to.not.reportError("start = 'a' start");
|
|
||||||
|
|
||||||
expect(pass).to.not.reportError("start = [a-d] start");
|
} );
|
||||||
|
|
||||||
expect(pass).to.not.reportError("start = . start");
|
} );
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -1,99 +1,107 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let chai = require("chai");
|
const chai = require( "chai" );
|
||||||
let helpers = require("./helpers");
|
const helpers = require( "./helpers" );
|
||||||
let pass = require("../../../../../lib/compiler/passes/report-infinite-repetition");
|
const pass = require( "../../../../../lib/compiler/passes/report-infinite-repetition" );
|
||||||
|
|
||||||
chai.use(helpers);
|
chai.use( helpers );
|
||||||
|
|
||||||
let expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("compiler pass |reportInfiniteRepetition|", function() {
|
describe( "compiler pass |reportInfiniteRepetition|", function () {
|
||||||
it("reports infinite loops for zero_or_more", function() {
|
|
||||||
expect(pass).to.reportError("start = ('')*", {
|
it( "reports infinite loops for zero_or_more", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = ('')*", {
|
||||||
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 8, line: 1, column: 9 },
|
start: { offset: 8, line: 1, column: 9 },
|
||||||
end: { offset: 13, line: 1, column: 14 }
|
end: { offset: 13, line: 1, column: 14 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( "reports infinite loops for one_or_more", function () {
|
||||||
|
|
||||||
it("reports infinite loops for one_or_more", function() {
|
expect( pass ).to.reportError( "start = ('')+", {
|
||||||
expect(pass).to.reportError("start = ('')+", {
|
|
||||||
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 8, line: 1, column: 9 },
|
start: { offset: 8, line: 1, column: 9 },
|
||||||
end: { offset: 13, line: 1, column: 14 }
|
end: { offset: 13, line: 1, column: 14 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
|
||||||
it("computes expressions that always consume input on success correctly", function() {
|
} );
|
||||||
expect(pass).to.reportError([
|
|
||||||
|
it( "computes expressions that always consume input on success correctly", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( [
|
||||||
"start = a*",
|
"start = a*",
|
||||||
"a 'a' = ''"
|
"a 'a' = ''"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
expect(pass).to.not.reportError([
|
expect( pass ).to.not.reportError( [
|
||||||
"start = a*",
|
"start = a*",
|
||||||
"a 'a' = 'a'"
|
"a 'a' = 'a'"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('' / 'a' / 'b')*");
|
expect( pass ).to.reportError( "start = ('' / 'a' / 'b')*" );
|
||||||
expect(pass).to.reportError("start = ('a' / '' / 'b')*");
|
expect( pass ).to.reportError( "start = ('a' / '' / 'b')*" );
|
||||||
expect(pass).to.reportError("start = ('a' / 'b' / '')*");
|
expect( pass ).to.reportError( "start = ('a' / 'b' / '')*" );
|
||||||
expect(pass).to.not.reportError("start = ('a' / 'b' / 'c')*");
|
expect( pass ).to.not.reportError( "start = ('a' / 'b' / 'c')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('' { })*");
|
expect( pass ).to.reportError( "start = ('' { })*" );
|
||||||
expect(pass).to.not.reportError("start = ('a' { })*");
|
expect( pass ).to.not.reportError( "start = ('a' { })*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('' '' '')*");
|
expect( pass ).to.reportError( "start = ('' '' '')*" );
|
||||||
expect(pass).to.not.reportError("start = ('a' '' '')*");
|
expect( pass ).to.not.reportError( "start = ('a' '' '')*" );
|
||||||
expect(pass).to.not.reportError("start = ('' 'a' '')*");
|
expect( pass ).to.not.reportError( "start = ('' 'a' '')*" );
|
||||||
expect(pass).to.not.reportError("start = ('' '' 'a')*");
|
expect( pass ).to.not.reportError( "start = ('' '' 'a')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (a:'')*");
|
expect( pass ).to.reportError( "start = (a:'')*" );
|
||||||
expect(pass).to.not.reportError("start = (a:'a')*");
|
expect( pass ).to.not.reportError( "start = (a:'a')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ($'')*");
|
expect( pass ).to.reportError( "start = ($'')*" );
|
||||||
expect(pass).to.not.reportError("start = ($'a')*");
|
expect( pass ).to.not.reportError( "start = ($'a')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (&'')*");
|
expect( pass ).to.reportError( "start = (&'')*" );
|
||||||
expect(pass).to.reportError("start = (&'a')*");
|
expect( pass ).to.reportError( "start = (&'a')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (!'')*");
|
expect( pass ).to.reportError( "start = (!'')*" );
|
||||||
expect(pass).to.reportError("start = (!'a')*");
|
expect( pass ).to.reportError( "start = (!'a')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (''?)*");
|
expect( pass ).to.reportError( "start = (''?)*" );
|
||||||
expect(pass).to.reportError("start = ('a'?)*");
|
expect( pass ).to.reportError( "start = ('a'?)*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (''*)*");
|
expect( pass ).to.reportError( "start = (''*)*" );
|
||||||
expect(pass).to.reportError("start = ('a'*)*");
|
expect( pass ).to.reportError( "start = ('a'*)*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (''+)*");
|
expect( pass ).to.reportError( "start = (''+)*" );
|
||||||
expect(pass).to.not.reportError("start = ('a'+)*");
|
expect( pass ).to.not.reportError( "start = ('a'+)*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ('')*");
|
expect( pass ).to.reportError( "start = ('')*" );
|
||||||
expect(pass).to.not.reportError("start = ('a')*");
|
expect( pass ).to.not.reportError( "start = ('a')*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (&{ })*");
|
expect( pass ).to.reportError( "start = (&{ })*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = (!{ })*");
|
expect( pass ).to.reportError( "start = (!{ })*" );
|
||||||
|
|
||||||
expect(pass).to.reportError([
|
expect( pass ).to.reportError( [
|
||||||
"start = a*",
|
"start = a*",
|
||||||
"a = ''"
|
"a = ''"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
expect(pass).to.not.reportError([
|
expect( pass ).to.not.reportError( [
|
||||||
"start = a*",
|
"start = a*",
|
||||||
"a = 'a'"
|
"a = 'a'"
|
||||||
].join("\n"));
|
].join( "\n" ) );
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = ''*" );
|
||||||
|
expect( pass ).to.not.reportError( "start = 'a'*" );
|
||||||
|
|
||||||
|
expect( pass ).to.not.reportError( "start = [a-d]*" );
|
||||||
|
|
||||||
expect(pass).to.reportError("start = ''*");
|
expect( pass ).to.not.reportError( "start = .*" );
|
||||||
expect(pass).to.not.reportError("start = 'a'*");
|
|
||||||
|
|
||||||
expect(pass).to.not.reportError("start = [a-d]*");
|
} );
|
||||||
|
|
||||||
expect(pass).to.not.reportError("start = .*");
|
} );
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -1,29 +1,35 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let chai = require("chai");
|
const chai = require( "chai" );
|
||||||
let helpers = require("./helpers");
|
const helpers = require( "./helpers" );
|
||||||
let pass = require("../../../../../lib/compiler/passes/report-undefined-rules");
|
const pass = require( "../../../../../lib/compiler/passes/report-undefined-rules" );
|
||||||
|
|
||||||
chai.use(helpers);
|
chai.use( helpers );
|
||||||
|
|
||||||
let expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("compiler pass |reportUndefinedRules|", function() {
|
describe( "compiler pass |reportUndefinedRules|", function () {
|
||||||
it("reports undefined rules", function() {
|
|
||||||
expect(pass).to.reportError("start = undefined", {
|
it( "reports undefined rules", function () {
|
||||||
|
|
||||||
|
expect( pass ).to.reportError( "start = undefined", {
|
||||||
message: "Rule \"undefined\" is not defined.",
|
message: "Rule \"undefined\" is not defined.",
|
||||||
location: {
|
location: {
|
||||||
start: { offset: 8, line: 1, column: 9 },
|
start: { offset: 8, line: 1, column: 9 },
|
||||||
end: { offset: 17, line: 1, column: 18 }
|
end: { offset: 17, line: 1, column: 18 }
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
} );
|
||||||
|
|
||||||
it("checks allowedStartRules", function() {
|
it( "checks allowedStartRules", function () {
|
||||||
expect(pass).to.reportError("start = 'a'", {
|
|
||||||
|
expect( pass ).to.reportError( "start = 'a'", {
|
||||||
message: "Start rule \"missing\" is not defined."
|
message: "Start rule \"missing\" is not defined."
|
||||||
}, {
|
}, {
|
||||||
allowedStartRules: ["missing"]
|
allowedStartRules: [ "missing" ]
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
});
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue