diff --git a/lib/compiler/asts.js b/lib/compiler/asts.js deleted file mode 100644 index 50c4ff7..0000000 --- a/lib/compiler/asts.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; - -const visitor = require( "./visitor" ); - -// AST utilities. -const asts = { - findRule( ast, name ) { - - for ( let i = 0; i < ast.rules.length; i++ ) { - - if ( ast.rules[ i ].name === name ) return ast.rules[ i ]; - - } - - return void 0; - - }, - - indexOfRule( ast, name ) { - - for ( let i = 0; i < ast.rules.length; i++ ) { - - if ( ast.rules[ i ].name === name ) return i; - - } - - return -1; - - }, - - alwaysConsumesOnSuccess( ast, node ) { - - let consumes; - - function consumesTrue() { - - return true; - - } - function consumesFalse() { - - return false; - - } - - function consumesExpression( node ) { - - return consumes( node.expression ); - - } - - consumes = visitor.build( { - rule: consumesExpression, - named: consumesExpression, - - choice( node ) { - - return node.alternatives.every( consumes ); - - }, - - action: consumesExpression, - - sequence( node ) { - - return node.elements.some( consumes ); - - }, - - labeled: consumesExpression, - text: consumesExpression, - simple_and: consumesFalse, - simple_not: consumesFalse, - optional: consumesFalse, - zero_or_more: consumesFalse, - one_or_more: consumesExpression, - group: consumesExpression, - semantic_and: consumesFalse, - semantic_not: consumesFalse, - - rule_ref( node ) { - - return consumes( asts.findRule( ast, node.name ) ); - - }, - - literal( node ) { - - return node.value !== ""; - - }, - - class: consumesTrue, - any: consumesTrue - } ); - - return consumes( node ); - - } -}; - -module.exports = asts; diff --git a/lib/compiler/passes/calc-report-failures.js b/lib/compiler/passes/calc-report-failures.js index d5f0033..1bc2ce1 100644 --- a/lib/compiler/passes/calc-report-failures.js +++ b/lib/compiler/passes/calc-report-failures.js @@ -1,6 +1,5 @@ "use strict"; -const asts = require( "../asts" ); const visitor = require( "../visitor" ); // Determines if rule always used in disabled report failure context, @@ -19,7 +18,7 @@ function calcReportFailures( ast, options ) { // always enabled const changedRules = options.allowedStartRules.map( name => { - const rule = asts.findRule( ast, name ); + const rule = ast.findRule( name ); rule.reportFailures = true; @@ -42,7 +41,7 @@ function calcReportFailures( ast, options ) { rule_ref( node ) { - const rule = asts.findRule( ast, node.name ); + const rule = ast.findRule( node.name ); // This function only called when rule can report failures. If so, we // need recalculate all rules that referenced from it. But do not do diff --git a/lib/compiler/passes/generate-bytecode.js b/lib/compiler/passes/generate-bytecode.js index e63c4da..f0bee29 100644 --- a/lib/compiler/passes/generate-bytecode.js +++ b/lib/compiler/passes/generate-bytecode.js @@ -1,6 +1,5 @@ "use strict"; -const asts = require( "../asts" ); const op = require( "../opcodes" ); const visitor = require( "../visitor" ); const util = require( "../../util" ); @@ -643,7 +642,7 @@ function generateBytecode( ast ) { rule_ref( node ) { - return [ op.RULE, asts.indexOfRule( ast, node.name ) ]; + return [ op.RULE, ast.indexOfRule( node.name ) ]; }, diff --git a/lib/compiler/passes/generate-js.js b/lib/compiler/passes/generate-js.js index 2c682d6..2d29776 100644 --- a/lib/compiler/passes/generate-js.js +++ b/lib/compiler/passes/generate-js.js @@ -2,7 +2,6 @@ "use strict"; -const asts = require( "../asts" ); const js = require( "../js" ); const op = require( "../opcodes" ); @@ -954,7 +953,7 @@ function generateJS( ast, options ) { parts.push( indent2( generateRuleHeader( "\"" + js.stringEscape( rule.name ) + "\"", - asts.indexOfRule( ast, rule.name ) + ast.indexOfRule( rule.name ) ) ) ); parts.push( indent2( code ) ); parts.push( indent2( generateRuleFooter( @@ -1173,10 +1172,10 @@ function generateJS( ast, options ) { const startRuleIndices = "{ " + options.allowedStartRules - .map( r => r + ": " + asts.indexOfRule( ast, r ) ) + .map( r => r + ": " + ast.indexOfRule( r ) ) .join( ", " ) + " }"; - const startRuleIndex = asts.indexOfRule( ast, options.allowedStartRules[ 0 ] ); + const startRuleIndex = ast.indexOfRule( options.allowedStartRules[ 0 ] ); parts.push( [ " var peg$startRuleIndices = " + startRuleIndices + ";", diff --git a/lib/compiler/passes/inference-match-result.js b/lib/compiler/passes/inference-match-result.js index 6e84f82..d834951 100644 --- a/lib/compiler/passes/inference-match-result.js +++ b/lib/compiler/passes/inference-match-result.js @@ -1,7 +1,6 @@ "use strict"; const visitor = require( "../visitor" ); -const asts = require( "../asts" ); const GrammarError = require( "../../grammar-error" ); // Inference match result of the rule. Can be: @@ -140,7 +139,7 @@ function inferenceMatchResult( ast ) { semantic_not: sometimesMatch, rule_ref( node ) { - const rule = asts.findRule( ast, node.name ); + const rule = ast.findRule( node.name ); node.match = inference( rule ); return node.match; diff --git a/lib/compiler/passes/report-infinite-recursion.js b/lib/compiler/passes/report-infinite-recursion.js index c7a6840..9118878 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 asts = require( "../asts" ); const visitor = require( "../visitor" ); // Reports left recursion in the grammar, which prevents infinite recursion in @@ -33,7 +32,7 @@ function reportInfiniteRecursion( ast ) { check( element ); - return ! asts.alwaysConsumesOnSuccess( ast, element ); + return ! ast.alwaysConsumesOnSuccess( element ); } ); @@ -53,7 +52,7 @@ function reportInfiniteRecursion( ast ) { } - check( asts.findRule( ast, node.name ) ); + check( ast.findRule( node.name ) ); } } ); diff --git a/lib/compiler/passes/report-infinite-repetition.js b/lib/compiler/passes/report-infinite-repetition.js index 4bf756f..f7e7b95 100644 --- a/lib/compiler/passes/report-infinite-repetition.js +++ b/lib/compiler/passes/report-infinite-repetition.js @@ -1,7 +1,6 @@ "use strict"; 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 @@ -11,7 +10,7 @@ function reportInfiniteRepetition( ast ) { const check = visitor.build( { zero_or_more( node ) { - if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) { + if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) { throw new GrammarError( "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).", @@ -24,7 +23,7 @@ function reportInfiniteRepetition( ast ) { one_or_more( node ) { - if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) { + if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) { throw new GrammarError( "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).", diff --git a/lib/compiler/passes/report-undefined-rules.js b/lib/compiler/passes/report-undefined-rules.js index e6a9b8e..4e5796e 100644 --- a/lib/compiler/passes/report-undefined-rules.js +++ b/lib/compiler/passes/report-undefined-rules.js @@ -1,7 +1,6 @@ "use strict"; const GrammarError = require( "../../grammar-error" ); -const asts = require( "../asts" ); const visitor = require( "../visitor" ); // Checks that all referenced rules exist. @@ -10,7 +9,7 @@ function reportUndefinedRules( ast, options ) { const check = visitor.build( { rule_ref( node ) { - if ( ! asts.findRule( ast, node.name ) ) { + if ( ! ast.findRule( node.name ) ) { throw new GrammarError( `Rule "${ node.name }" is not defined.`, @@ -26,7 +25,7 @@ function reportUndefinedRules( ast, options ) { options.allowedStartRules.forEach( rule => { - if ( ! asts.findRule( ast, rule ) ) { + if ( ! ast.findRule( rule ) ) { throw new GrammarError( `Start rule "${ rule }" is not defined.` ); diff --git a/lib/parser/ast.js b/lib/parser/ast.js index 7fb2a4b..917a511 100644 --- a/lib/parser/ast.js +++ b/lib/parser/ast.js @@ -1,5 +1,8 @@ "use strict"; +const visitor = require( "../compiler/visitor" ); +const util = require( "../util" ); + class Node { constructor( type, location ) { @@ -23,7 +26,110 @@ class Grammar extends Node { this.comments = comments; this.rules = rules; + this._alwaysConsumesOnSuccess = new AlwaysConsumesOnSuccess( this ); + + } + + alwaysConsumesOnSuccess( node ) { + + return this._alwaysConsumesOnSuccess.visit( node ); + + } + + findRule( name ) { + + return this.rules.find( rule => rule.name === name ); + + } + + indexOfRule( name ) { + + const rules = this.rules; + + for ( let i = 0; i < rules.length; ++i ) { + + if ( rules[ i ].name === name ) return i; + + } + + return -1; + } } exports.Grammar = Grammar; + +/* ***************************** @private ***************************** */ + +class AlwaysConsumesOnSuccess extends visitor.ASTVisitor { + + constructor( ast ) { + + super(); + this.ast = ast; + + } + + choice( node ) { + + return node.alternatives.every( this.visit, this ); + + } + + sequence( node ) { + + return node.elements.some( this.visit, this ); + + } + + rule_ref( node ) { + + return this.visit( this.ast.findRule( node.name ) ); + + } + + literal( node ) { + + return node.value !== ""; + + } + +} + +function consumesTrue() { + + return true; + +} + +function consumesFalse() { + + return false; + +} + +function consumesExpression( node ) { + + return this.visit( node.expression ); + +} + +util.extend( AlwaysConsumesOnSuccess.prototype, { + + rule: consumesExpression, + named: consumesExpression, + action: consumesExpression, + labeled: consumesExpression, + text: consumesExpression, + simple_and: consumesFalse, + simple_not: consumesFalse, + optional: consumesFalse, + zero_or_more: consumesFalse, + one_or_more: consumesExpression, + group: consumesExpression, + semantic_and: consumesFalse, + semantic_not: consumesFalse, + class: consumesTrue, + any: consumesTrue, + +} );