Add the ASTVisitor class (#451)

For compatibility with pre-0.11 plugins, this class lives on the same namespace as the orignal visitor helper and also exports a static method called 'build'.
master
Futago-za Ryuu 6 years ago
parent 93cc6c5b26
commit 7a19d46e8a

@ -3,92 +3,133 @@
const util = require( "../util" ); const util = require( "../util" );
const __slice = Array.prototype.slice; const __slice = Array.prototype.slice;
// Simple AST node visitor builder. // Abstract syntax tree visitor for PEG.js
const visitor = { class ASTVisitor {
build( functions ) {
function visit( node ) { // Will traverse the node, strictly assuming the visitor can handle the node type.
visit( node ) {
return functions[ node.type ].apply( null, arguments ); const args = __slice.call( arguments, 0 );
} return this[ node.type ].apply( this, args );
}
// Simple AST node visitor builder for PEG.js
static build( functions ) {
const visitor = new ASTVisitor();
util.extend( visitor, functions );
return visitor.visit.bind( visitor );
}
}
module.exports = ASTVisitor;
const visitNop = util.noop; // Helper's to create visitor's for use with the ASTVisitor class
const on = ASTVisitor.for = {
function visitExpression( node ) { // Visit a node that is defined as a property on another node
property( name ) {
return function visitProperty( node ) {
const extraArgs = __slice.call( arguments, 1 ); const extraArgs = __slice.call( arguments, 1 );
const value = node[ name ];
visit.apply( null, [ node.expression ].concat( extraArgs ) ); if ( extraArgs.length )
} this.visit.apply( this, [ value ].concat( extraArgs ) );
function visitChildren( children, extraArgs ) { else
const args = [ void 0 ].concat( extraArgs ); this.visit( value );
const cb = extraArgs.length
? function withArgs( child ) {
args[ 0 ] = child; };
visit.apply( null, args );
},
// Visit an array of nodes that are defined as a property on another node
children( name ) {
return function visitProperty( node ) {
const args = __slice.call( arguments, 0 );
const children = node[ name ];
const visitor = this;
const cb = args.length < 2
? function withoutArgs( child ) {
visitor.visit( child );
} }
: function withoutArgs( child ) { : function withArgs( child ) {
visit( child ); args[ 0 ] = child;
visitor.visit.apply( visitor, args );
}; };
children.forEach( cb ); children.forEach( cb );
} };
const DEFAULT_FUNCTIONS = { },
grammar( node ) {
const extraArgs = __slice.call( arguments, 1 ); };
if ( node.initializer ) { // Build the default ast visitor functions
visit.apply( null, [ node.initializer ].concat( extraArgs ) ); const visitNop = util.noop;
const visitExpression = on.property( "expression" );
} const DEFAULT_FUNCTIONS = {
node.rules.forEach( rule => { grammar( node ) {
visit.apply( null, [ rule ].concat( extraArgs ) );
} );
},
initializer: visitNop,
rule: visitExpression,
named: visitExpression,
choice: util.createVisitor( "alternatives", visitChildren ),
action: visitExpression,
sequence: util.createVisitor( "elements", visitChildren ),
labeled: visitExpression,
text: visitExpression,
simple_and: visitExpression,
simple_not: visitExpression,
optional: visitExpression,
zero_or_more: visitExpression,
one_or_more: visitExpression,
group: visitExpression,
semantic_and: visitNop,
semantic_not: visitNop,
rule_ref: visitNop,
literal: visitNop,
class: visitNop,
any: visitNop
};
util.extend( functions, DEFAULT_FUNCTIONS ); const extraArgs = __slice.call( arguments, 1 );
return visit; if ( node.initializer ) {
this.visit.apply( this, [ node.initializer ].concat( extraArgs ) );
}
node.rules.forEach( rule => {
this.visit.apply( this, [ rule ].concat( extraArgs ) );
} );
},
initializer: visitNop,
rule: visitExpression,
named: visitExpression,
choice: on.children( "alternatives" ),
action: visitExpression,
sequence: on.children( "elements" ),
labeled: visitExpression,
text: visitExpression,
simple_and: visitExpression,
simple_not: visitExpression,
optional: visitExpression,
zero_or_more: visitExpression,
one_or_more: visitExpression,
group: visitExpression,
semantic_and: visitNop,
semantic_not: visitNop,
rule_ref: visitNop,
literal: visitNop,
class: visitNop,
any: visitNop,
}
}; };
module.exports = visitor; util.each( DEFAULT_FUNCTIONS, ( fn, name ) => {
ASTVisitor.prototype[ name ] = fn;
} );

Loading…
Cancel
Save