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";
|
||||
|
||||
let visitor = require("../visitor");
|
||||
const visitor = require( "../visitor" );
|
||||
|
||||
// Removes proxy rules -- that is, rules that only delegate to other rule.
|
||||
function removeProxyRules(ast, options) {
|
||||
function isProxyRule(node) {
|
||||
function removeProxyRules( ast, options ) {
|
||||
|
||||
function isProxyRule( node ) {
|
||||
|
||||
return node.type === "rule" && node.expression.type === "rule_ref";
|
||||
|
||||
}
|
||||
|
||||
function replaceRuleRefs(ast, from, to) {
|
||||
let replace = visitor.build({
|
||||
rule_ref(node) {
|
||||
if (node.name === from) {
|
||||
function replaceRuleRefs( ast, from, to ) {
|
||||
|
||||
const replace = visitor.build( {
|
||||
rule_ref( node ) {
|
||||
|
||||
if ( node.name === from ) {
|
||||
|
||||
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.forEach(i => { ast.rules.splice(i, 1); });
|
||||
indices.forEach( i => {
|
||||
|
||||
ast.rules.splice( i, 1 );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
module.exports = removeProxyRules;
|
||||
|
@ -1,28 +1,36 @@
|
||||
"use strict";
|
||||
|
||||
let GrammarError = require("../../grammar-error");
|
||||
let visitor = require("../visitor");
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const visitor = require( "../visitor" );
|
||||
|
||||
// Checks that each rule is defined only once.
|
||||
function reportDuplicateRules(ast) {
|
||||
let rules = {};
|
||||
function reportDuplicateRules( ast ) {
|
||||
|
||||
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(
|
||||
"Rule \"" + node.name + "\" is already defined "
|
||||
+ "at line " + rules[node.name].start.line + ", "
|
||||
+ "column " + rules[node.name].start.column + ".",
|
||||
`Rule "${ name }" is already defined at line ${ start.line }, column ${ start.column }.`,
|
||||
node.location
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
rules[node.name] = node.location;
|
||||
rules[ node.name ] = node.location;
|
||||
|
||||
}
|
||||
});
|
||||
} );
|
||||
|
||||
check( ast );
|
||||
|
||||
check(ast);
|
||||
}
|
||||
|
||||
module.exports = reportDuplicateRules;
|
||||
|
@ -1,33 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
let GrammarError = require("../../grammar-error");
|
||||
let asts = require("../asts");
|
||||
let visitor = require("../visitor");
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const asts = require( "../asts" );
|
||||
const visitor = require( "../visitor" );
|
||||
|
||||
// Reports expressions that don't consume any input inside |*| or |+| in the
|
||||
// grammar, which prevents infinite loops in the generated parser.
|
||||
function reportInfiniteRepetition(ast) {
|
||||
let check = visitor.build({
|
||||
zero_or_more(node) {
|
||||
if (!asts.alwaysConsumesOnSuccess(ast, node.expression)) {
|
||||
function reportInfiniteRepetition( ast ) {
|
||||
|
||||
const check = visitor.build( {
|
||||
zero_or_more( node ) {
|
||||
|
||||
if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) {
|
||||
|
||||
throw new GrammarError(
|
||||
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||
node.location
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
one_or_more(node) {
|
||||
if (!asts.alwaysConsumesOnSuccess(ast, node.expression)) {
|
||||
one_or_more( node ) {
|
||||
|
||||
if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) {
|
||||
|
||||
throw new GrammarError(
|
||||
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||
node.location
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} );
|
||||
|
||||
check( ast );
|
||||
|
||||
check(ast);
|
||||
}
|
||||
|
||||
module.exports = reportInfiniteRepetition;
|
||||
|
@ -1,32 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
let GrammarError = require("../../grammar-error");
|
||||
let asts = require("../asts");
|
||||
let visitor = require("../visitor");
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const asts = require( "../asts" );
|
||||
const visitor = require( "../visitor" );
|
||||
|
||||
// Checks that all referenced rules exist.
|
||||
function reportUndefinedRules(ast, options) {
|
||||
let check = visitor.build({
|
||||
rule_ref(node) {
|
||||
if (!asts.findRule(ast, node.name)) {
|
||||
function reportUndefinedRules( ast, options ) {
|
||||
|
||||
const check = visitor.build( {
|
||||
rule_ref( node ) {
|
||||
|
||||
if ( ! asts.findRule( ast, node.name ) ) {
|
||||
|
||||
throw new GrammarError(
|
||||
"Rule \"" + node.name + "\" is not defined.",
|
||||
`Rule "${ node.name }" is not defined.`,
|
||||
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;
|
||||
|
@ -1,150 +1,179 @@
|
||||
#!/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
|
||||
// speed and size. Makes sense to use only on PEG.js git repository checkout.
|
||||
//
|
||||
|
||||
/* eslint prefer-const: 0 */
|
||||
|
||||
"use strict";
|
||||
|
||||
let child_process = require("child_process");
|
||||
let fs = require("fs");
|
||||
let os = require("os");
|
||||
let path = require("path");
|
||||
let glob = require("glob");
|
||||
const child_process = require( "child_process" );
|
||||
const fs = require( "fs" );
|
||||
const os = require( "os" );
|
||||
const path = require( "path" );
|
||||
const dedent = require( "dedent" );
|
||||
const glob = require( "glob" );
|
||||
|
||||
// Current Working Directory
|
||||
|
||||
let cwd = path.join(__dirname, "..");
|
||||
|
||||
if (process.cwd() !== cwd) {
|
||||
process.chdir(cwd);
|
||||
}
|
||||
const cwd = path.join( __dirname, ".." );
|
||||
if ( process.cwd() !== cwd ) process.chdir( cwd );
|
||||
|
||||
// Execution Files
|
||||
|
||||
let PEGJS_BIN = "bin/peg.js";
|
||||
let BENCHMARK_BIN = "test/benchmark/run";
|
||||
|
||||
if (!fs.existsSync(PEGJS_BIN)) {
|
||||
if ( ! fs.existsSync( PEGJS_BIN ) ) {
|
||||
|
||||
PEGJS_BIN = "bin/pegjs";
|
||||
|
||||
}
|
||||
|
||||
if (!fs.existsSync(BENCHMARK_BIN)) {
|
||||
if ( ! fs.existsSync( BENCHMARK_BIN ) ) {
|
||||
|
||||
BENCHMARK_BIN = "benchmark/run";
|
||||
|
||||
}
|
||||
|
||||
// Utils
|
||||
|
||||
let print = console.log;
|
||||
function echo( message ) {
|
||||
|
||||
process.stdout.write( message );
|
||||
|
||||
function echo(message) {
|
||||
process.stdout.write(message);
|
||||
}
|
||||
|
||||
function exec(command) {
|
||||
return child_process.execSync(command, { encoding: "utf8" });
|
||||
function exec( command ) {
|
||||
|
||||
return child_process.execSync( command, { encoding: "utf8" } );
|
||||
|
||||
}
|
||||
|
||||
function prepare(commit) {
|
||||
exec(`git checkout --quiet "${commit}"`);
|
||||
function prepare( commit ) {
|
||||
|
||||
exec( `git checkout --quiet "${ commit }"` );
|
||||
|
||||
}
|
||||
|
||||
function runBenchmark() {
|
||||
|
||||
return parseFloat(
|
||||
exec("node " + BENCHMARK_BIN)
|
||||
exec( "node " + BENCHMARK_BIN )
|
||||
|
||||
// Split by table seprator, reverse and return the total bytes per second
|
||||
.split("│")
|
||||
.reverse()[1]
|
||||
.split( "│" )
|
||||
.reverse()[ 1 ]
|
||||
|
||||
// Trim the whitespaces and remove ` kB/s` from the end
|
||||
.trim()
|
||||
.slice(0, -5)
|
||||
.slice( 0, -5 )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function measureSpeed() {
|
||||
return (runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() / 5).toFixed(2);
|
||||
|
||||
return ( runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() + runBenchmark() / 5 ).toFixed( 2 );
|
||||
|
||||
}
|
||||
|
||||
function measureSize() {
|
||||
|
||||
let size = 0;
|
||||
|
||||
glob.sync("examples/*.pegjs")
|
||||
.forEach(example => {
|
||||
exec(`node ${PEGJS_BIN} ${example}`);
|
||||
example = example.slice(0, -5) + "js";
|
||||
size += fs.statSync(example).size;
|
||||
fs.unlinkSync(example);
|
||||
});
|
||||
glob.sync( "examples/*.pegjs" )
|
||||
.forEach( example => {
|
||||
|
||||
exec( `node ${ PEGJS_BIN } ${ example }` );
|
||||
example = example.slice( 0, -5 ) + "js";
|
||||
size += fs.statSync( example ).size;
|
||||
fs.unlinkSync( example );
|
||||
|
||||
} );
|
||||
|
||||
return size;
|
||||
|
||||
}
|
||||
|
||||
function difference($1, $2) {
|
||||
return (($2 / $1 - 1) * 100).toFixed(4);
|
||||
function difference( $1, $2 ) {
|
||||
|
||||
return ( ( $2 / $1 - 1 ) * 100 ).toFixed( 4 );
|
||||
|
||||
}
|
||||
|
||||
// Prepare
|
||||
|
||||
let argv = process.argv.slice(2);
|
||||
const argv = process.argv.slice( 2 );
|
||||
let commit_before, commit_after;
|
||||
|
||||
if (argv.length === 1) {
|
||||
commit_before = argv[0] + "~1";
|
||||
commit_after = argv[0];
|
||||
} else if (argv.length === 2) {
|
||||
commit_before = argv[0];
|
||||
commit_after = argv[1];
|
||||
if ( argv.length === 1 ) {
|
||||
|
||||
commit_before = argv[ 0 ] + "~1";
|
||||
commit_after = argv[ 0 ];
|
||||
|
||||
} else if ( argv.length === 2 ) {
|
||||
|
||||
commit_before = argv[ 0 ];
|
||||
commit_after = argv[ 1 ];
|
||||
|
||||
} else {
|
||||
print("Usage:");
|
||||
print("");
|
||||
print(" test/impact <commit>");
|
||||
print(" test/impact <commit_before> <commit_after>");
|
||||
print("");
|
||||
print("Measures impact of a Git commit (or multiple commits) on generated parsers'");
|
||||
print("speed and size. Makes sense to use only on PEG.js Git repository checkout.");
|
||||
print("");
|
||||
process.exit(1);
|
||||
|
||||
console.log( dedent`
|
||||
|
||||
Usage:
|
||||
|
||||
test/impact <commit>
|
||||
test/impact <commit_before> <commit_after>
|
||||
|
||||
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
|
||||
|
||||
let branch = exec("git rev-parse --abbrev-ref HEAD");
|
||||
const branch = exec( "git rev-parse --abbrev-ref HEAD" );
|
||||
let speed1, size1, speed2, size2;
|
||||
|
||||
echo(`Measuring commit ${commit_before}...`);
|
||||
prepare(commit_before);
|
||||
echo( `Measuring commit ${ commit_before }...` );
|
||||
prepare( commit_before );
|
||||
speed1 = measureSpeed();
|
||||
size1 = measureSize();
|
||||
echo(" OK" + os.EOL);
|
||||
echo( " OK" + os.EOL );
|
||||
|
||||
echo(`Measuring commit ${commit_after}...`);
|
||||
prepare(commit_after);
|
||||
echo( `Measuring commit ${ commit_after }...` );
|
||||
prepare( commit_after );
|
||||
speed2 = measureSpeed();
|
||||
size2 = measureSize();
|
||||
echo(" OK" + os.EOL);
|
||||
echo( " OK" + os.EOL );
|
||||
|
||||
// Finish
|
||||
|
||||
prepare(branch);
|
||||
prepare( branch );
|
||||
|
||||
print(`
|
||||
test/impact ${commit_before} ${commit_after}
|
||||
console.log( dedent`
|
||||
|
||||
test/impact ${ commit_before } ${ commit_after }
|
||||
|
||||
Speed impact
|
||||
------------
|
||||
Before: ${speed1} kB/s
|
||||
After: ${speed2} kB/s
|
||||
Difference: ${difference(parseFloat(speed1), parseFloat(speed2))}%
|
||||
Before: ${ speed1 } kB/s
|
||||
After: ${ speed2 } kB/s
|
||||
Difference: ${ difference( parseFloat( speed1 ), parseFloat( speed2 ) ) }%
|
||||
|
||||
Size impact
|
||||
-----------
|
||||
Before: ${size1} b
|
||||
After: ${size2} b
|
||||
Difference: ${difference(size1, size2)}%
|
||||
Before: ${ size1 } b
|
||||
After: ${ size2 } b
|
||||
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";
|
||||
|
||||
let chai = require("chai");
|
||||
let peg = require("../../../lib/peg");
|
||||
const chai = require( "chai" );
|
||||
const peg = require( "../../../lib/peg" );
|
||||
|
||||
let expect = chai.expect;
|
||||
const expect = chai.expect;
|
||||
|
||||
describe("plugin API", function() {
|
||||
describe("use", function() {
|
||||
let grammar = "start = 'a'";
|
||||
describe( "plugin API", function () {
|
||||
|
||||
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; } }
|
||||
describe( "use", function () {
|
||||
|
||||
const grammar = "start = 'a'";
|
||||
|
||||
it( "is called for each plugin", function () {
|
||||
|
||||
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() {
|
||||
let plugin = {
|
||||
use(config) {
|
||||
expect(config).to.be.an("object");
|
||||
expect( config.passes ).to.be.an( "object" );
|
||||
|
||||
expect(config.parser).to.be.an("object");
|
||||
expect(config.parser.parse("start = 'a'")).to.be.an("object");
|
||||
expect( config.passes.check ).to.be.an( "array" );
|
||||
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");
|
||||
config.passes.transform.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" );
|
||||
|
||||
} );
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
peg.generate( grammar, generateOptions );
|
||||
|
||||
} );
|
||||
|
||||
it( "can replace parser", function () {
|
||||
|
||||
const plugin = {
|
||||
use( config ) {
|
||||
|
||||
config.parser = peg.generate( `
|
||||
|
||||
start = .* {
|
||||
return {
|
||||
type: 'grammar',
|
||||
rules: [{
|
||||
type: 'rule',
|
||||
name: 'start',
|
||||
expression: {
|
||||
type: 'literal',
|
||||
value: text(),
|
||||
ignoreCase: false
|
||||
}
|
||||
}]
|
||||
};
|
||||
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; } })";
|
||||
|
||||
}
|
||||
|
||||
config.passes.generate = [pass];
|
||||
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"];
|
||||
|
||||
const parser = peg.generate( grammar, { plugins: [ plugin ] } );
|
||||
expect( parser.parse( "a" ) ).to.equal( 42 );
|
||||
|
||||
} );
|
||||
|
||||
it( "can change options", function () {
|
||||
|
||||
const grammar = `
|
||||
|
||||
a = 'x'
|
||||
b = 'x'
|
||||
c = 'x'
|
||||
|
||||
`;
|
||||
const 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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const 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" );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
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";
|
||||
|
||||
let parser = require("../../../../../lib/parser");
|
||||
const parser = require( "../../../../../lib/parser" );
|
||||
|
||||
module.exports = function(chai, utils) {
|
||||
let Assertion = chai.Assertion;
|
||||
module.exports = function ( chai, utils ) {
|
||||
|
||||
Assertion.addMethod("changeAST", function(grammar, props, options) {
|
||||
options = options !== undefined ? options : {};
|
||||
const Assertion = chai.Assertion;
|
||||
|
||||
function matchProps(value, props) {
|
||||
function isArray(value) {
|
||||
return Object.prototype.toString.apply(value) === "[object Array]";
|
||||
}
|
||||
Assertion.addMethod( "changeAST", function ( grammar, props, options ) {
|
||||
|
||||
options = typeof options !== "undefined" ? options : {};
|
||||
|
||||
function matchProps( value, props ) {
|
||||
|
||||
function isObject( value ) {
|
||||
|
||||
function isObject(value) {
|
||||
return value !== null && typeof value === "object";
|
||||
|
||||
}
|
||||
|
||||
if (isArray(props)) {
|
||||
if (!isArray(value)) { return false; }
|
||||
if ( Array.isArray( props ) ) {
|
||||
|
||||
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;
|
||||
} else if (isObject(props)) {
|
||||
if (!isObject(value)) { return false; }
|
||||
|
||||
let keys = Object.keys(props);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
} else if ( isObject( props ) ) {
|
||||
|
||||
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;
|
||||
} 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(
|
||||
matchProps(ast, props),
|
||||
matchProps( ast, props ),
|
||||
"expected #{this} to change the AST to match #{exp}",
|
||||
"expected #{this} to not change the AST to match #{exp}",
|
||||
props,
|
||||
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;
|
||||
|
||||
try {
|
||||
utils.flag(this, "object")(ast, options);
|
||||
|
||||
utils.flag( this, "object" )( ast, options );
|
||||
passed = true;
|
||||
} catch (e) {
|
||||
|
||||
} catch ( e ) {
|
||||
|
||||
result = e;
|
||||
passed = false;
|
||||
|
||||
}
|
||||
|
||||
this.assert(
|
||||
!passed,
|
||||
! passed,
|
||||
"expected #{this} to report an error but it didn't",
|
||||
"expected #{this} to not report an error but #{act} was reported",
|
||||
null,
|
||||
result
|
||||
);
|
||||
|
||||
if (!passed && props !== undefined) {
|
||||
Object.keys(props).forEach(key => {
|
||||
new Assertion(result).to.have.property(key)
|
||||
.that.is.deep.equal(props[key]);
|
||||
});
|
||||
if ( ! passed && typeof props !== "undefined" ) {
|
||||
|
||||
Object.keys( props ).forEach( key => {
|
||||
|
||||
new Assertion( result )
|
||||
.to.have.property( key )
|
||||
.that.is.deep.equal( props[ key ] );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
};
|
||||
|
@ -1,63 +1,83 @@
|
||||
"use strict";
|
||||
|
||||
let chai = require("chai");
|
||||
let helpers = require("./helpers");
|
||||
let pass = require("../../../../../lib/compiler/passes/report-duplicate-labels");
|
||||
const chai = require( "chai" );
|
||||
const helpers = require( "./helpers" );
|
||||
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("in a sequence", function() {
|
||||
it("reports labels duplicate with labels of preceding elements", function() {
|
||||
expect(pass).to.reportError("start = a:'a' a:'a'", {
|
||||
describe( "compiler pass |reportDuplicateLabels|", function () {
|
||||
|
||||
describe( "in a 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.",
|
||||
location: {
|
||||
start: { offset: 14, line: 1, column: 15 },
|
||||
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'");
|
||||
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 = 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'");
|
||||
});
|
||||
});
|
||||
|
||||
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 outer sequence", function() {
|
||||
it("reports labels duplicate with labels of preceding elements", function() {
|
||||
expect(pass).to.reportError("start = 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') 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 = 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'" );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
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 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.",
|
||||
location: {
|
||||
start: { offset: 15, line: 1, column: 16 },
|
||||
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 labels of following elements", function() {
|
||||
expect(pass).to.not.reportError("start = (a:'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'" );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
@ -1,24 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
let chai = require("chai");
|
||||
let helpers = require("./helpers");
|
||||
let pass = require("../../../../../lib/compiler/passes/report-duplicate-rules");
|
||||
const chai = require( "chai" );
|
||||
const helpers = require( "./helpers" );
|
||||
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() {
|
||||
it("reports duplicate rules", function() {
|
||||
expect(pass).to.reportError([
|
||||
describe( "compiler pass |reportDuplicateRules|", function () {
|
||||
|
||||
it( "reports duplicate rules", function () {
|
||||
|
||||
expect( pass ).to.reportError( [
|
||||
"start = 'a'",
|
||||
"start = 'b'"
|
||||
].join("\n"), {
|
||||
].join( "\n" ), {
|
||||
message: "Rule \"start\" is already defined at line 1, column 1.",
|
||||
location: {
|
||||
start: { offset: 12, line: 2, column: 1 },
|
||||
end: { offset: 23, line: 2, column: 12 }
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
@ -1,119 +1,135 @@
|
||||
"use strict";
|
||||
|
||||
let chai = require("chai");
|
||||
let helpers = require("./helpers");
|
||||
let pass = require("../../../../../lib/compiler/passes/report-infinite-recursion");
|
||||
const chai = require( "chai" );
|
||||
const helpers = require( "./helpers" );
|
||||
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() {
|
||||
it("reports direct left recursion", function() {
|
||||
expect(pass).to.reportError("start = start", {
|
||||
describe( "compiler pass |reportInfiniteRecursion|", function () {
|
||||
|
||||
it( "reports direct left recursion", function () {
|
||||
|
||||
expect( pass ).to.reportError( "start = start", {
|
||||
message: "Possible infinite loop when parsing (left recursion: start -> start).",
|
||||
location: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
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",
|
||||
"stop = start"
|
||||
].join("\n"), {
|
||||
].join( "\n" ), {
|
||||
message: "Possible infinite loop when parsing (left recursion: start -> stop -> start).",
|
||||
location: {
|
||||
start: { offset: 20, line: 2, column: 8 },
|
||||
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() {
|
||||
expect(pass).to.not.reportError("start = 'a' '' '' start");
|
||||
expect(pass).to.not.reportError("start = '' 'a' '' start");
|
||||
expect(pass).to.not.reportError("start = '' '' 'a' start");
|
||||
});
|
||||
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" );
|
||||
|
||||
} );
|
||||
|
||||
// Regression test for #359.
|
||||
it("reports left recursion when rule reference is wrapped in an expression", function() {
|
||||
expect(pass).to.reportError("start = '' start?");
|
||||
});
|
||||
it( "reports left recursion when rule reference is wrapped in an expression", function () {
|
||||
|
||||
expect( pass ).to.reportError( "start = '' start?" );
|
||||
|
||||
} );
|
||||
|
||||
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' = ''"
|
||||
].join("\n"));
|
||||
expect(pass).to.not.reportError([
|
||||
].join( "\n" ) );
|
||||
expect( pass ).to.not.reportError( [
|
||||
"start = a start",
|
||||
"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.not.reportError("start = ('a' / 'b' / 'c') 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.reportError("start = ('' { }) start");
|
||||
expect(pass).to.not.reportError("start = ('a' { }) start");
|
||||
expect( pass ).to.reportError( "start = ('' { }) start" );
|
||||
expect( pass ).to.not.reportError( "start = ('a' { }) 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.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.reportError("start = a:'' start");
|
||||
expect(pass).to.not.reportError("start = a:'a' start");
|
||||
expect( pass ).to.reportError( "start = a:'' start" );
|
||||
expect( pass ).to.not.reportError( "start = a:'a' start" );
|
||||
|
||||
expect(pass).to.reportError("start = $'' start");
|
||||
expect(pass).to.not.reportError("start = $'a' start");
|
||||
expect( pass ).to.reportError( "start = $'' start" );
|
||||
expect( pass ).to.not.reportError( "start = $'a' start" );
|
||||
|
||||
expect(pass).to.reportError("start = &'' start");
|
||||
expect(pass).to.reportError("start = &'a' start");
|
||||
expect( pass ).to.reportError( "start = &'' start" );
|
||||
expect( pass ).to.reportError( "start = &'a' start" );
|
||||
|
||||
expect(pass).to.reportError("start = !'' start");
|
||||
expect(pass).to.reportError("start = !'a' start");
|
||||
expect( pass ).to.reportError( "start = !'' start" );
|
||||
expect( pass ).to.reportError( "start = !'a' start" );
|
||||
|
||||
expect(pass).to.reportError("start = ''? start");
|
||||
expect(pass).to.reportError("start = 'a'? start");
|
||||
expect( pass ).to.reportError( "start = ''? start" );
|
||||
expect( pass ).to.reportError( "start = 'a'? start" );
|
||||
|
||||
expect(pass).to.reportError("start = ''* start");
|
||||
expect(pass).to.reportError("start = 'a'* start");
|
||||
expect( pass ).to.reportError( "start = ''* start" );
|
||||
expect( pass ).to.reportError( "start = 'a'* start" );
|
||||
|
||||
expect(pass).to.reportError("start = ''+ start");
|
||||
expect(pass).to.not.reportError("start = 'a'+ start");
|
||||
expect( pass ).to.reportError( "start = ''+ start" );
|
||||
expect( pass ).to.not.reportError( "start = 'a'+ start" );
|
||||
|
||||
expect(pass).to.reportError("start = ('') start");
|
||||
expect(pass).to.not.reportError("start = ('a') start");
|
||||
expect( pass ).to.reportError( "start = ('') 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",
|
||||
"a = ''"
|
||||
].join("\n"));
|
||||
expect(pass).to.not.reportError([
|
||||
].join( "\n" ) );
|
||||
expect( pass ).to.not.reportError( [
|
||||
"start = a start",
|
||||
"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";
|
||||
|
||||
let chai = require("chai");
|
||||
let helpers = require("./helpers");
|
||||
let pass = require("../../../../../lib/compiler/passes/report-infinite-repetition");
|
||||
const chai = require( "chai" );
|
||||
const helpers = require( "./helpers" );
|
||||
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() {
|
||||
it("reports infinite loops for zero_or_more", function() {
|
||||
expect(pass).to.reportError("start = ('')*", {
|
||||
describe( "compiler pass |reportInfiniteRepetition|", function () {
|
||||
|
||||
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).",
|
||||
location: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
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).",
|
||||
location: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
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*",
|
||||
"a 'a' = ''"
|
||||
].join("\n"));
|
||||
expect(pass).to.not.reportError([
|
||||
].join( "\n" ) );
|
||||
expect( pass ).to.not.reportError( [
|
||||
"start = 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.not.reportError("start = ('a' / 'b' / 'c')*");
|
||||
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.reportError("start = ('' { })*");
|
||||
expect(pass).to.not.reportError("start = ('a' { })*");
|
||||
expect( pass ).to.reportError( "start = ('' { })*" );
|
||||
expect( pass ).to.not.reportError( "start = ('a' { })*" );
|
||||
|
||||
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.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.reportError("start = (a:'')*");
|
||||
expect(pass).to.not.reportError("start = (a:'a')*");
|
||||
expect( pass ).to.reportError( "start = (a:'')*" );
|
||||
expect( pass ).to.not.reportError( "start = (a:'a')*" );
|
||||
|
||||
expect(pass).to.reportError("start = ($'')*");
|
||||
expect(pass).to.not.reportError("start = ($'a')*");
|
||||
expect( pass ).to.reportError( "start = ($'')*" );
|
||||
expect( pass ).to.not.reportError( "start = ($'a')*" );
|
||||
|
||||
expect(pass).to.reportError("start = (&'')*");
|
||||
expect(pass).to.reportError("start = (&'a')*");
|
||||
expect( pass ).to.reportError( "start = (&'')*" );
|
||||
expect( pass ).to.reportError( "start = (&'a')*" );
|
||||
|
||||
expect(pass).to.reportError("start = (!'')*");
|
||||
expect(pass).to.reportError("start = (!'a')*");
|
||||
expect( pass ).to.reportError( "start = (!'')*" );
|
||||
expect( pass ).to.reportError( "start = (!'a')*" );
|
||||
|
||||
expect(pass).to.reportError("start = (''?)*");
|
||||
expect(pass).to.reportError("start = ('a'?)*");
|
||||
expect( pass ).to.reportError( "start = (''?)*" );
|
||||
expect( pass ).to.reportError( "start = ('a'?)*" );
|
||||
|
||||
expect(pass).to.reportError("start = (''*)*");
|
||||
expect(pass).to.reportError("start = ('a'*)*");
|
||||
expect( pass ).to.reportError( "start = (''*)*" );
|
||||
expect( pass ).to.reportError( "start = ('a'*)*" );
|
||||
|
||||
expect(pass).to.reportError("start = (''+)*");
|
||||
expect(pass).to.not.reportError("start = ('a'+)*");
|
||||
expect( pass ).to.reportError( "start = (''+)*" );
|
||||
expect( pass ).to.not.reportError( "start = ('a'+)*" );
|
||||
|
||||
expect(pass).to.reportError("start = ('')*");
|
||||
expect(pass).to.not.reportError("start = ('a')*");
|
||||
expect( pass ).to.reportError( "start = ('')*" );
|
||||
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*",
|
||||
"a = ''"
|
||||
].join("\n"));
|
||||
expect(pass).to.not.reportError([
|
||||
].join( "\n" ) );
|
||||
expect( pass ).to.not.reportError( [
|
||||
"start = 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 = 'a'*");
|
||||
expect( pass ).to.not.reportError( "start = .*" );
|
||||
|
||||
expect(pass).to.not.reportError("start = [a-d]*");
|
||||
} );
|
||||
|
||||
expect(pass).to.not.reportError("start = .*");
|
||||
});
|
||||
});
|
||||
} );
|
||||
|
@ -1,29 +1,35 @@
|
||||
"use strict";
|
||||
|
||||
let chai = require("chai");
|
||||
let helpers = require("./helpers");
|
||||
let pass = require("../../../../../lib/compiler/passes/report-undefined-rules");
|
||||
const chai = require( "chai" );
|
||||
const helpers = require( "./helpers" );
|
||||
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() {
|
||||
it("reports undefined rules", function() {
|
||||
expect(pass).to.reportError("start = undefined", {
|
||||
describe( "compiler pass |reportUndefinedRules|", function () {
|
||||
|
||||
it( "reports undefined rules", function () {
|
||||
|
||||
expect( pass ).to.reportError( "start = undefined", {
|
||||
message: "Rule \"undefined\" is not defined.",
|
||||
location: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
end: { offset: 17, line: 1, column: 18 }
|
||||
}
|
||||
});
|
||||
});
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
it("checks allowedStartRules", function() {
|
||||
expect(pass).to.reportError("start = 'a'", {
|
||||
it( "checks allowedStartRules", function () {
|
||||
|
||||
expect( pass ).to.reportError( "start = 'a'", {
|
||||
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