From 7a19d46e8a291ca7a80f7507a514455b9e789677 Mon Sep 17 00:00:00 2001 From: Futago-za Ryuu Date: Tue, 16 Jan 2018 02:54:14 +0000 Subject: [PATCH] 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'. --- lib/compiler/visitor.js | 157 +++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 58 deletions(-) diff --git a/lib/compiler/visitor.js b/lib/compiler/visitor.js index 6174267..44d5214 100644 --- a/lib/compiler/visitor.js +++ b/lib/compiler/visitor.js @@ -3,92 +3,133 @@ const util = require( "../util" ); const __slice = Array.prototype.slice; -// Simple AST node visitor builder. -const visitor = { - build( functions ) { +// Abstract syntax tree visitor for PEG.js +class ASTVisitor { - 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 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 ); - const cb = extraArgs.length - ? function withArgs( child ) { + this.visit( value ); - 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 ); - } + }; - 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 => { - - 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 - }; + grammar( node ) { - 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; + +} );