Added the Session API
This commit is contained in:
parent
8e9be9afea
commit
f4c67993f6
|
@ -10,6 +10,7 @@ const reportInfiniteRecursion = require( "./passes/report-infinite-recursion" );
|
||||||
const reportInfiniteRepetition = require( "./passes/report-infinite-repetition" );
|
const reportInfiniteRepetition = require( "./passes/report-infinite-repetition" );
|
||||||
const reportUndefinedRules = require( "./passes/report-undefined-rules" );
|
const reportUndefinedRules = require( "./passes/report-undefined-rules" );
|
||||||
const inferenceMatchResult = require( "./passes/inference-match-result" );
|
const inferenceMatchResult = require( "./passes/inference-match-result" );
|
||||||
|
const Session = require( "./session" );
|
||||||
const util = require( "../util" );
|
const util = require( "../util" );
|
||||||
|
|
||||||
function processOptions( options, defaults ) {
|
function processOptions( options, defaults ) {
|
||||||
|
@ -25,6 +26,8 @@ function processOptions( options, defaults ) {
|
||||||
|
|
||||||
const compiler = {
|
const compiler = {
|
||||||
|
|
||||||
|
Session: Session,
|
||||||
|
|
||||||
// Compiler passes.
|
// Compiler passes.
|
||||||
//
|
//
|
||||||
// Each pass is a function that is passed the AST. It can perform checks on it
|
// Each pass is a function that is passed the AST. It can perform checks on it
|
||||||
|
@ -53,7 +56,7 @@ const compiler = {
|
||||||
// if the AST contains a semantic error. Note that not all errors are detected
|
// if the AST contains a semantic error. Note that not all errors are detected
|
||||||
// during the generation and some may protrude to the generated parser and
|
// during the generation and some may protrude to the generated parser and
|
||||||
// cause its malfunction.
|
// cause its malfunction.
|
||||||
compile( ast, passes, options ) {
|
compile( ast, session, options ) {
|
||||||
|
|
||||||
options = typeof options !== "undefined" ? options : {};
|
options = typeof options !== "undefined" ? options : {};
|
||||||
|
|
||||||
|
@ -68,11 +71,11 @@ const compiler = {
|
||||||
trace: false
|
trace: false
|
||||||
} );
|
} );
|
||||||
|
|
||||||
util.each( passes, stage => {
|
util.each( session.passes, stage => {
|
||||||
|
|
||||||
stage.forEach( pass => {
|
stage.forEach( pass => {
|
||||||
|
|
||||||
pass( ast, options );
|
pass( ast, session, options );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
|
|
||||||
// Determines if rule always used in disabled report failure context,
|
// Determines if rule always used in disabled report failure context,
|
||||||
// that means, that any failures, reported within it, are never will be
|
// that means, that any failures, reported within it, are never will be
|
||||||
// visible, so the no need to report it.
|
// visible, so the no need to report it.
|
||||||
function calcReportFailures( ast, options ) {
|
function calcReportFailures( ast, session, options ) {
|
||||||
|
|
||||||
// By default, not report failures for rules...
|
// By default, not report failures for rules...
|
||||||
ast.rules.forEach( rule => {
|
ast.rules.forEach( rule => {
|
||||||
|
@ -26,7 +24,7 @@ function calcReportFailures( ast, options ) {
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const calc = visitor.build( {
|
const calc = session.buildVisitor( {
|
||||||
rule( node ) {
|
rule( node ) {
|
||||||
|
|
||||||
calc( node.expression );
|
calc( node.expression );
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const op = require( "../opcodes" );
|
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
const util = require( "../../util" );
|
const util = require( "../../util" );
|
||||||
|
|
||||||
// Generates bytecode.
|
// Generates bytecode.
|
||||||
|
@ -199,7 +197,9 @@ const util = require( "../../util" );
|
||||||
// }
|
// }
|
||||||
// expected.top().variants.pushAll(value.variants);
|
// expected.top().variants.pushAll(value.variants);
|
||||||
// }
|
// }
|
||||||
function generateBytecode( ast ) {
|
function generateBytecode( ast, session ) {
|
||||||
|
|
||||||
|
const op = session.opcodes;
|
||||||
|
|
||||||
const literals = [];
|
const literals = [];
|
||||||
const classes = [];
|
const classes = [];
|
||||||
|
@ -333,7 +333,7 @@ function generateBytecode( ast ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generate = visitor.build( {
|
generate = session.buildVisitor( {
|
||||||
grammar( node ) {
|
grammar( node ) {
|
||||||
|
|
||||||
node.rules.forEach( generate );
|
node.rules.forEach( generate );
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const js = require( "../js" );
|
const js = require( "../js" );
|
||||||
const op = require( "../opcodes" );
|
|
||||||
|
|
||||||
// Generates parser JavaScript code.
|
// Generates parser JavaScript code.
|
||||||
function generateJS( ast, options ) {
|
function generateJS( ast, session, options ) {
|
||||||
|
|
||||||
|
const op = session.opcodes;
|
||||||
|
|
||||||
/* These only indent non-empty lines to avoid trailing whitespace. */
|
/* These only indent non-empty lines to avoid trailing whitespace. */
|
||||||
const lineMatchRE = /^([^`\r\n]+?(?:`[^`]*?`[^\r\n]*?)?)$/gm;
|
const lineMatchRE = /^([^`\r\n]+?(?:`[^`]*?`[^\r\n]*?)?)$/gm;
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
|
|
||||||
// Inference match result of the rule. Can be:
|
// Inference match result of the rule. Can be:
|
||||||
// -1: negative result, always fails
|
// -1: negative result, always fails
|
||||||
// 0: neutral result, may be fail, may be match
|
// 0: neutral result, may be fail, may be match
|
||||||
// 1: positive result, always match
|
// 1: positive result, always match
|
||||||
function inferenceMatchResult( ast ) {
|
function inferenceMatchResult( ast, session ) {
|
||||||
|
|
||||||
let inference;
|
let inference;
|
||||||
function sometimesMatch( node ) {
|
function sometimesMatch( node ) {
|
||||||
|
@ -72,7 +71,7 @@ function inferenceMatchResult( ast ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inference = visitor.build( {
|
inference = session.buildVisitor( {
|
||||||
rule( node ) {
|
rule( node ) {
|
||||||
|
|
||||||
let oldResult;
|
let oldResult;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const visitor = require( "../../ast" ).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, session, options ) {
|
||||||
|
|
||||||
function isProxyRule( node ) {
|
function isProxyRule( node ) {
|
||||||
|
|
||||||
|
@ -13,7 +11,7 @@ function removeProxyRules( ast, options ) {
|
||||||
|
|
||||||
function replaceRuleRefs( ast, from, to ) {
|
function replaceRuleRefs( ast, from, to ) {
|
||||||
|
|
||||||
const replace = visitor.build( {
|
const replace = session.buildVisitor( {
|
||||||
rule_ref( node ) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
if ( node.name === from ) {
|
if ( node.name === from ) {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
const util = require( "../../util" );
|
const util = require( "../../util" );
|
||||||
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
// Checks that each label is defined only once within each scope.
|
// Checks that each label is defined only once within each scope.
|
||||||
function reportDuplicateLabels( ast ) {
|
function reportDuplicateLabels( ast, session ) {
|
||||||
|
|
||||||
let check;
|
let check;
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ function reportDuplicateLabels( ast ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check = visitor.build( {
|
check = session.buildVisitor( {
|
||||||
rule( node ) {
|
rule( node ) {
|
||||||
|
|
||||||
check( node.expression, {} );
|
check( node.expression, {} );
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
// Checks that each rule is defined only once.
|
// Checks that each rule is defined only once.
|
||||||
function reportDuplicateRules( ast ) {
|
function reportDuplicateRules( ast, session ) {
|
||||||
|
|
||||||
const rules = {};
|
const rules = {};
|
||||||
|
|
||||||
const check = visitor.build( {
|
const check = session.buildVisitor( {
|
||||||
rule( node ) {
|
rule( node ) {
|
||||||
|
|
||||||
const name = node.name;
|
const name = node.name;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
|
|
||||||
// Reports left recursion in the grammar, which prevents infinite recursion in
|
// Reports left recursion in the grammar, which prevents infinite recursion in
|
||||||
// the generated parser.
|
// the generated parser.
|
||||||
|
@ -13,11 +12,11 @@ const visitor = require( "../../ast" ).visitor;
|
||||||
//
|
//
|
||||||
// In general, if a rule reference can be reached without consuming any input,
|
// In general, if a rule reference can be reached without consuming any input,
|
||||||
// it can lead to left recursion.
|
// it can lead to left recursion.
|
||||||
function reportInfiniteRecursion( ast ) {
|
function reportInfiniteRecursion( ast, session ) {
|
||||||
|
|
||||||
const visitedRules = [];
|
const visitedRules = [];
|
||||||
|
|
||||||
const check = visitor.build( {
|
const check = session.buildVisitor( {
|
||||||
rule( node ) {
|
rule( node ) {
|
||||||
|
|
||||||
visitedRules.push( node.name );
|
visitedRules.push( node.name );
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const visitor = require( "../../ast" ).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, session ) {
|
||||||
|
|
||||||
const check = visitor.build( {
|
const check = session.buildVisitor( {
|
||||||
zero_or_more( node ) {
|
zero_or_more( node ) {
|
||||||
|
|
||||||
if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) {
|
if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const visitor = require( "../../ast" ).visitor;
|
|
||||||
|
|
||||||
// Checks that all referenced rules exist.
|
// Checks that all referenced rules exist.
|
||||||
function reportUndefinedRules( ast, options ) {
|
function reportUndefinedRules( ast, session, options ) {
|
||||||
|
|
||||||
const check = visitor.build( {
|
const check = session.buildVisitor( {
|
||||||
rule_ref( node ) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
if ( ! ast.findRule( node.name ) ) {
|
if ( ! ast.findRule( node.name ) ) {
|
||||||
|
|
35
lib/compiler/session.js
Normal file
35
lib/compiler/session.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const ast = require( "../ast" );
|
||||||
|
const opcodes = require( "./opcodes" );
|
||||||
|
const parser = require( "../parser" );
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
|
||||||
|
constructor( options ) {
|
||||||
|
|
||||||
|
options = typeof options !== "undefined" ? options : {};
|
||||||
|
|
||||||
|
this.grammar = options.grammar;
|
||||||
|
this.opcodes = options.opcodes || opcodes;
|
||||||
|
this.parser = options.parser || parser;
|
||||||
|
this.passes = options.passes || [];
|
||||||
|
this.visitor = options.visitor || ast.visitor;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
parse( input, options ) {
|
||||||
|
|
||||||
|
return this.parser.parse( input, options );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buildVisitor( functions ) {
|
||||||
|
|
||||||
|
return this.visitor.build( functions );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Session;
|
12
lib/peg.js
12
lib/peg.js
|
@ -30,20 +30,20 @@ const peg = {
|
||||||
options = typeof options !== "undefined" ? options : {};
|
options = typeof options !== "undefined" ? options : {};
|
||||||
|
|
||||||
const plugins = "plugins" in options ? options.plugins : [];
|
const plugins = "plugins" in options ? options.plugins : [];
|
||||||
const config = {
|
const session = new compiler.Session( {
|
||||||
parser: parser,
|
grammar: grammar,
|
||||||
passes: util.convertPasses( compiler.passes )
|
passes: util.convertPasses( compiler.passes )
|
||||||
};
|
} );
|
||||||
|
|
||||||
plugins.forEach( p => {
|
plugins.forEach( p => {
|
||||||
|
|
||||||
p.use( config, options );
|
p.use( session, options );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
return compiler.compile(
|
return compiler.compile(
|
||||||
config.parser.parse( grammar, options.parser || {} ),
|
session.parse( grammar, options.parser || {} ),
|
||||||
config.passes,
|
session,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const LikeHelper = require( "chai-like" );
|
const LikeHelper = require( "chai-like" );
|
||||||
const parser = require( "pegjs-dev" ).parser;
|
const Session = require( "pegjs-dev" ).compiler.Session;
|
||||||
|
|
||||||
module.exports = function ( chai, utils ) {
|
module.exports = function ( chai, utils ) {
|
||||||
|
|
||||||
|
@ -14,7 +14,8 @@ module.exports = function ( chai, utils ) {
|
||||||
options = typeof options !== "undefined" ? options : {};
|
options = typeof options !== "undefined" ? options : {};
|
||||||
additionalRuleProps = typeof additionalRuleProps !== "undefined" ? additionalRuleProps : { reportFailures: true };
|
additionalRuleProps = typeof additionalRuleProps !== "undefined" ? additionalRuleProps : { reportFailures: true };
|
||||||
|
|
||||||
const ast = parser.parse( grammar );
|
const session = new Session( { grammar } );
|
||||||
|
const ast = session.parse( grammar );
|
||||||
|
|
||||||
if ( ! options.allowedStartRules ) {
|
if ( ! options.allowedStartRules ) {
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ module.exports = function ( chai, utils ) {
|
||||||
|
|
||||||
ast.rules = ast.rules.map( rule => Object.assign( rule, additionalRuleProps ) );
|
ast.rules = ast.rules.map( rule => Object.assign( rule, additionalRuleProps ) );
|
||||||
|
|
||||||
utils.flag( this, "object" )( ast, options );
|
utils.flag( this, "object" )( ast, session, options );
|
||||||
|
|
||||||
new Assertion( ast ).like( props );
|
new Assertion( ast ).like( props );
|
||||||
|
|
||||||
|
@ -36,7 +37,8 @@ module.exports = function ( chai, utils ) {
|
||||||
|
|
||||||
options = typeof options !== "undefined" ? options : {};
|
options = typeof options !== "undefined" ? options : {};
|
||||||
|
|
||||||
const ast = parser.parse( grammar );
|
const session = new Session( { grammar } );
|
||||||
|
const ast = session.parse( grammar );
|
||||||
|
|
||||||
if ( ! options.allowedStartRules ) {
|
if ( ! options.allowedStartRules ) {
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ module.exports = function ( chai, utils ) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
utils.flag( this, "object" )( ast, options );
|
utils.flag( this, "object" )( ast, session, options );
|
||||||
passed = true;
|
passed = true;
|
||||||
|
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
|
|
Loading…
Reference in a new issue