diff --git a/lib/compiler/index.js b/lib/compiler/index.js index c0515dc..16bd832 100644 --- a/lib/compiler/index.js +++ b/lib/compiler/index.js @@ -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 ); } ); diff --git a/lib/compiler/passes/calc-report-failures.js b/lib/compiler/passes/calc-report-failures.js index 70d3676..23b8c4b 100644 --- a/lib/compiler/passes/calc-report-failures.js +++ b/lib/compiler/passes/calc-report-failures.js @@ -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 ); diff --git a/lib/compiler/passes/generate-bytecode.js b/lib/compiler/passes/generate-bytecode.js index 2e679fe..4b89a05 100644 --- a/lib/compiler/passes/generate-bytecode.js +++ b/lib/compiler/passes/generate-bytecode.js @@ -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 ); diff --git a/lib/compiler/passes/generate-js.js b/lib/compiler/passes/generate-js.js index 2d29776..fcb4cc0 100644 --- a/lib/compiler/passes/generate-js.js +++ b/lib/compiler/passes/generate-js.js @@ -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; diff --git a/lib/compiler/passes/inference-match-result.js b/lib/compiler/passes/inference-match-result.js index 8aed1d4..2546c4d 100644 --- a/lib/compiler/passes/inference-match-result.js +++ b/lib/compiler/passes/inference-match-result.js @@ -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; diff --git a/lib/compiler/passes/remove-proxy-rules.js b/lib/compiler/passes/remove-proxy-rules.js index db32cc5..690dc08 100644 --- a/lib/compiler/passes/remove-proxy-rules.js +++ b/lib/compiler/passes/remove-proxy-rules.js @@ -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 ) { diff --git a/lib/compiler/passes/report-duplicate-labels.js b/lib/compiler/passes/report-duplicate-labels.js index 28564fc..5cf796b 100644 --- a/lib/compiler/passes/report-duplicate-labels.js +++ b/lib/compiler/passes/report-duplicate-labels.js @@ -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, {} ); diff --git a/lib/compiler/passes/report-duplicate-rules.js b/lib/compiler/passes/report-duplicate-rules.js index 287554b..82da138 100644 --- a/lib/compiler/passes/report-duplicate-rules.js +++ b/lib/compiler/passes/report-duplicate-rules.js @@ -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; diff --git a/lib/compiler/passes/report-infinite-recursion.js b/lib/compiler/passes/report-infinite-recursion.js index def253f..1f6c270 100644 --- a/lib/compiler/passes/report-infinite-recursion.js +++ b/lib/compiler/passes/report-infinite-recursion.js @@ -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 ); diff --git a/lib/compiler/passes/report-infinite-repetition.js b/lib/compiler/passes/report-infinite-repetition.js index 72d9dc9..1125f53 100644 --- a/lib/compiler/passes/report-infinite-repetition.js +++ b/lib/compiler/passes/report-infinite-repetition.js @@ -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 ) ) { diff --git a/lib/compiler/passes/report-undefined-rules.js b/lib/compiler/passes/report-undefined-rules.js index ea8ef24..1b47f46 100644 --- a/lib/compiler/passes/report-undefined-rules.js +++ b/lib/compiler/passes/report-undefined-rules.js @@ -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 ) ) { diff --git a/lib/compiler/session.js b/lib/compiler/session.js new file mode 100644 index 0000000..a33ac13 --- /dev/null +++ b/lib/compiler/session.js @@ -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; diff --git a/lib/peg.js b/lib/peg.js index 66b2d70..d742e6a 100644 --- a/lib/peg.js +++ b/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 ); diff --git a/test/spec/unit/compiler/passes/helpers.js b/test/spec/unit/compiler/passes/helpers.js index 533db6b..1cd5469 100644 --- a/test/spec/unit/compiler/passes/helpers.js +++ b/test/spec/unit/compiler/passes/helpers.js @@ -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 ) {