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 reportUndefinedRules = require( "./passes/report-undefined-rules" );
|
||||
const inferenceMatchResult = require( "./passes/inference-match-result" );
|
||||
const Session = require( "./session" );
|
||||
const util = require( "../util" );
|
||||
|
||||
function processOptions( options, defaults ) {
|
||||
|
@ -25,6 +26,8 @@ function processOptions( options, defaults ) {
|
|||
|
||||
const compiler = {
|
||||
|
||||
Session: Session,
|
||||
|
||||
// Compiler passes.
|
||||
//
|
||||
// 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
|
||||
// during the generation and some may protrude to the generated parser and
|
||||
// cause its malfunction.
|
||||
compile( ast, passes, options ) {
|
||||
compile( ast, session, options ) {
|
||||
|
||||
options = typeof options !== "undefined" ? options : {};
|
||||
|
||||
|
@ -68,11 +71,11 @@ const compiler = {
|
|||
trace: false
|
||||
} );
|
||||
|
||||
util.each( passes, stage => {
|
||||
util.each( session.passes, stage => {
|
||||
|
||||
stage.forEach( pass => {
|
||||
|
||||
pass( ast, options );
|
||||
pass( ast, session, options );
|
||||
|
||||
} );
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
|
||||
// Determines if rule always used in disabled report failure context,
|
||||
// that means, that any failures, reported within it, are never will be
|
||||
// visible, so the no need to report it.
|
||||
function calcReportFailures( ast, options ) {
|
||||
function calcReportFailures( ast, session, options ) {
|
||||
|
||||
// By default, not report failures for rules...
|
||||
ast.rules.forEach( rule => {
|
||||
|
@ -26,7 +24,7 @@ function calcReportFailures( ast, options ) {
|
|||
|
||||
} );
|
||||
|
||||
const calc = visitor.build( {
|
||||
const calc = session.buildVisitor( {
|
||||
rule( node ) {
|
||||
|
||||
calc( node.expression );
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
const op = require( "../opcodes" );
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
const util = require( "../../util" );
|
||||
|
||||
// Generates bytecode.
|
||||
|
@ -199,7 +197,9 @@ const util = require( "../../util" );
|
|||
// }
|
||||
// expected.top().variants.pushAll(value.variants);
|
||||
// }
|
||||
function generateBytecode( ast ) {
|
||||
function generateBytecode( ast, session ) {
|
||||
|
||||
const op = session.opcodes;
|
||||
|
||||
const literals = [];
|
||||
const classes = [];
|
||||
|
@ -333,7 +333,7 @@ function generateBytecode( ast ) {
|
|||
|
||||
}
|
||||
|
||||
generate = visitor.build( {
|
||||
generate = session.buildVisitor( {
|
||||
grammar( node ) {
|
||||
|
||||
node.rules.forEach( generate );
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
"use strict";
|
||||
|
||||
const js = require( "../js" );
|
||||
const op = require( "../opcodes" );
|
||||
|
||||
// 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. */
|
||||
const lineMatchRE = /^([^`\r\n]+?(?:`[^`]*?`[^\r\n]*?)?)$/gm;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
|
||||
// Inference match result of the rule. Can be:
|
||||
// -1: negative result, always fails
|
||||
// 0: neutral result, may be fail, may be match
|
||||
// 1: positive result, always match
|
||||
function inferenceMatchResult( ast ) {
|
||||
function inferenceMatchResult( ast, session ) {
|
||||
|
||||
let inference;
|
||||
function sometimesMatch( node ) {
|
||||
|
@ -72,7 +71,7 @@ function inferenceMatchResult( ast ) {
|
|||
|
||||
}
|
||||
|
||||
inference = visitor.build( {
|
||||
inference = session.buildVisitor( {
|
||||
rule( node ) {
|
||||
|
||||
let oldResult;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
|
||||
// Removes proxy rules -- that is, rules that only delegate to other rule.
|
||||
function removeProxyRules( ast, options ) {
|
||||
function removeProxyRules( ast, session, options ) {
|
||||
|
||||
function isProxyRule( node ) {
|
||||
|
||||
|
@ -13,7 +11,7 @@ function removeProxyRules( ast, options ) {
|
|||
|
||||
function replaceRuleRefs( ast, from, to ) {
|
||||
|
||||
const replace = visitor.build( {
|
||||
const replace = session.buildVisitor( {
|
||||
rule_ref( node ) {
|
||||
|
||||
if ( node.name === from ) {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
const util = require( "../../util" );
|
||||
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Checks that each label is defined only once within each scope.
|
||||
function reportDuplicateLabels( ast ) {
|
||||
function reportDuplicateLabels( ast, session ) {
|
||||
|
||||
let check;
|
||||
|
||||
|
@ -16,7 +15,7 @@ function reportDuplicateLabels( ast ) {
|
|||
|
||||
}
|
||||
|
||||
check = visitor.build( {
|
||||
check = session.buildVisitor( {
|
||||
rule( node ) {
|
||||
|
||||
check( node.expression, {} );
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
"use strict";
|
||||
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Checks that each rule is defined only once.
|
||||
function reportDuplicateRules( ast ) {
|
||||
function reportDuplicateRules( ast, session ) {
|
||||
|
||||
const rules = {};
|
||||
|
||||
const check = visitor.build( {
|
||||
const check = session.buildVisitor( {
|
||||
rule( node ) {
|
||||
|
||||
const name = node.name;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
|
||||
// Reports left recursion in the grammar, which prevents infinite recursion in
|
||||
// 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,
|
||||
// it can lead to left recursion.
|
||||
function reportInfiniteRecursion( ast ) {
|
||||
function reportInfiniteRecursion( ast, session ) {
|
||||
|
||||
const visitedRules = [];
|
||||
|
||||
const check = visitor.build( {
|
||||
const check = session.buildVisitor( {
|
||||
rule( node ) {
|
||||
|
||||
visitedRules.push( node.name );
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const visitor = require( "../../ast" ).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 ) {
|
||||
function reportInfiniteRepetition( ast, session ) {
|
||||
|
||||
const check = visitor.build( {
|
||||
const check = session.buildVisitor( {
|
||||
zero_or_more( node ) {
|
||||
|
||||
if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
const GrammarError = require( "../../grammar-error" );
|
||||
const visitor = require( "../../ast" ).visitor;
|
||||
|
||||
// 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 ) {
|
||||
|
||||
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 : {};
|
||||
|
||||
const plugins = "plugins" in options ? options.plugins : [];
|
||||
const config = {
|
||||
parser: parser,
|
||||
const session = new compiler.Session( {
|
||||
grammar: grammar,
|
||||
passes: util.convertPasses( compiler.passes )
|
||||
};
|
||||
} );
|
||||
|
||||
plugins.forEach( p => {
|
||||
|
||||
p.use( config, options );
|
||||
p.use( session, options );
|
||||
|
||||
} );
|
||||
|
||||
return compiler.compile(
|
||||
config.parser.parse( grammar, options.parser || {} ),
|
||||
config.passes,
|
||||
session.parse( grammar, options.parser || {} ),
|
||||
session,
|
||||
options
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
const LikeHelper = require( "chai-like" );
|
||||
const parser = require( "pegjs-dev" ).parser;
|
||||
const Session = require( "pegjs-dev" ).compiler.Session;
|
||||
|
||||
module.exports = function ( chai, utils ) {
|
||||
|
||||
|
@ -14,7 +14,8 @@ module.exports = function ( chai, utils ) {
|
|||
options = typeof options !== "undefined" ? options : {};
|
||||
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 ) {
|
||||
|
||||
|
@ -26,7 +27,7 @@ module.exports = function ( chai, utils ) {
|
|||
|
||||
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 );
|
||||
|
||||
|
@ -36,7 +37,8 @@ module.exports = function ( chai, utils ) {
|
|||
|
||||
options = typeof options !== "undefined" ? options : {};
|
||||
|
||||
const ast = parser.parse( grammar );
|
||||
const session = new Session( { grammar } );
|
||||
const ast = session.parse( grammar );
|
||||
|
||||
if ( ! options.allowedStartRules ) {
|
||||
|
||||
|
@ -50,7 +52,7 @@ module.exports = function ( chai, utils ) {
|
|||
|
||||
try {
|
||||
|
||||
utils.flag( this, "object" )( ast, options );
|
||||
utils.flag( this, "object" )( ast, session, options );
|
||||
passed = true;
|
||||
|
||||
} catch ( e ) {
|
||||
|
|
Loading…
Reference in a new issue