Merge ast utils into Grammar class
This commit is contained in:
parent
1a713d0175
commit
9d266625b4
|
@ -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;
|
|
|
@ -1,6 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const asts = require( "../asts" );
|
|
||||||
const visitor = require( "../visitor" );
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Determines if rule always used in disabled report failure context,
|
// Determines if rule always used in disabled report failure context,
|
||||||
|
@ -19,7 +18,7 @@ function calcReportFailures( ast, options ) {
|
||||||
// always enabled
|
// always enabled
|
||||||
const changedRules = options.allowedStartRules.map( name => {
|
const changedRules = options.allowedStartRules.map( name => {
|
||||||
|
|
||||||
const rule = asts.findRule( ast, name );
|
const rule = ast.findRule( name );
|
||||||
|
|
||||||
rule.reportFailures = true;
|
rule.reportFailures = true;
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ function calcReportFailures( ast, options ) {
|
||||||
|
|
||||||
rule_ref( node ) {
|
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
|
// This function only called when rule can report failures. If so, we
|
||||||
// need recalculate all rules that referenced from it. But do not do
|
// need recalculate all rules that referenced from it. But do not do
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const asts = require( "../asts" );
|
|
||||||
const op = require( "../opcodes" );
|
const op = require( "../opcodes" );
|
||||||
const visitor = require( "../visitor" );
|
const visitor = require( "../visitor" );
|
||||||
const util = require( "../../util" );
|
const util = require( "../../util" );
|
||||||
|
@ -643,7 +642,7 @@ function generateBytecode( ast ) {
|
||||||
|
|
||||||
rule_ref( node ) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
return [ op.RULE, asts.indexOfRule( ast, node.name ) ];
|
return [ op.RULE, ast.indexOfRule( node.name ) ];
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const asts = require( "../asts" );
|
|
||||||
const js = require( "../js" );
|
const js = require( "../js" );
|
||||||
const op = require( "../opcodes" );
|
const op = require( "../opcodes" );
|
||||||
|
|
||||||
|
@ -954,7 +953,7 @@ function generateJS( ast, options ) {
|
||||||
|
|
||||||
parts.push( indent2( generateRuleHeader(
|
parts.push( indent2( generateRuleHeader(
|
||||||
"\"" + js.stringEscape( rule.name ) + "\"",
|
"\"" + js.stringEscape( rule.name ) + "\"",
|
||||||
asts.indexOfRule( ast, rule.name )
|
ast.indexOfRule( rule.name )
|
||||||
) ) );
|
) ) );
|
||||||
parts.push( indent2( code ) );
|
parts.push( indent2( code ) );
|
||||||
parts.push( indent2( generateRuleFooter(
|
parts.push( indent2( generateRuleFooter(
|
||||||
|
@ -1173,10 +1172,10 @@ function generateJS( ast, options ) {
|
||||||
|
|
||||||
const startRuleIndices = "{ "
|
const startRuleIndices = "{ "
|
||||||
+ options.allowedStartRules
|
+ options.allowedStartRules
|
||||||
.map( r => r + ": " + asts.indexOfRule( ast, r ) )
|
.map( r => r + ": " + ast.indexOfRule( r ) )
|
||||||
.join( ", " )
|
.join( ", " )
|
||||||
+ " }";
|
+ " }";
|
||||||
const startRuleIndex = asts.indexOfRule( ast, options.allowedStartRules[ 0 ] );
|
const startRuleIndex = ast.indexOfRule( options.allowedStartRules[ 0 ] );
|
||||||
|
|
||||||
parts.push( [
|
parts.push( [
|
||||||
" var peg$startRuleIndices = " + startRuleIndices + ";",
|
" var peg$startRuleIndices = " + startRuleIndices + ";",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const visitor = require( "../visitor" );
|
const visitor = require( "../visitor" );
|
||||||
const asts = require( "../asts" );
|
|
||||||
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:
|
||||||
|
@ -140,7 +139,7 @@ function inferenceMatchResult( ast ) {
|
||||||
semantic_not: sometimesMatch,
|
semantic_not: sometimesMatch,
|
||||||
rule_ref( node ) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
const rule = asts.findRule( ast, node.name );
|
const rule = ast.findRule( node.name );
|
||||||
node.match = inference( rule );
|
node.match = inference( rule );
|
||||||
|
|
||||||
return node.match;
|
return node.match;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const asts = require( "../asts" );
|
|
||||||
const visitor = require( "../visitor" );
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Reports left recursion in the grammar, which prevents infinite recursion in
|
// Reports left recursion in the grammar, which prevents infinite recursion in
|
||||||
|
@ -33,7 +32,7 @@ function reportInfiniteRecursion( ast ) {
|
||||||
|
|
||||||
check( element );
|
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 ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const asts = require( "../asts" );
|
|
||||||
const visitor = require( "../visitor" );
|
const visitor = require( "../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
|
||||||
|
@ -11,7 +10,7 @@ function reportInfiniteRepetition( ast ) {
|
||||||
const check = visitor.build( {
|
const check = visitor.build( {
|
||||||
zero_or_more( node ) {
|
zero_or_more( node ) {
|
||||||
|
|
||||||
if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) {
|
if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) {
|
||||||
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
"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 ) {
|
one_or_more( node ) {
|
||||||
|
|
||||||
if ( ! asts.alwaysConsumesOnSuccess( ast, node.expression ) ) {
|
if ( ! ast.alwaysConsumesOnSuccess( node.expression ) ) {
|
||||||
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const GrammarError = require( "../../grammar-error" );
|
const GrammarError = require( "../../grammar-error" );
|
||||||
const asts = require( "../asts" );
|
|
||||||
const visitor = require( "../visitor" );
|
const visitor = require( "../visitor" );
|
||||||
|
|
||||||
// Checks that all referenced rules exist.
|
// Checks that all referenced rules exist.
|
||||||
|
@ -10,7 +9,7 @@ function reportUndefinedRules( ast, options ) {
|
||||||
const check = visitor.build( {
|
const check = visitor.build( {
|
||||||
rule_ref( node ) {
|
rule_ref( node ) {
|
||||||
|
|
||||||
if ( ! asts.findRule( ast, node.name ) ) {
|
if ( ! ast.findRule( node.name ) ) {
|
||||||
|
|
||||||
throw new GrammarError(
|
throw new GrammarError(
|
||||||
`Rule "${ node.name }" is not defined.`,
|
`Rule "${ node.name }" is not defined.`,
|
||||||
|
@ -26,7 +25,7 @@ function reportUndefinedRules( ast, options ) {
|
||||||
|
|
||||||
options.allowedStartRules.forEach( rule => {
|
options.allowedStartRules.forEach( rule => {
|
||||||
|
|
||||||
if ( ! asts.findRule( ast, rule ) ) {
|
if ( ! ast.findRule( rule ) ) {
|
||||||
|
|
||||||
throw new GrammarError( `Start rule "${ rule }" is not defined.` );
|
throw new GrammarError( `Start rule "${ rule }" is not defined.` );
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const visitor = require( "../compiler/visitor" );
|
||||||
|
const util = require( "../util" );
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
|
|
||||||
constructor( type, location ) {
|
constructor( type, location ) {
|
||||||
|
@ -23,7 +26,110 @@ class Grammar extends Node {
|
||||||
this.comments = comments;
|
this.comments = comments;
|
||||||
this.rules = rules;
|
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;
|
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,
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
Loading…
Reference in a new issue