"use strict"; // Simple AST node visitor builder. const visitor = { build(functions) { function visit(node, ...args) { return functions[node.type](node, ...args); } function visitNop() { // Do nothing. } function visitExpression(node, ...args) { return visit(node.expression, ...args); } function visitChildren(property) { return function(node, ...args) { // We do not use .map() here, because if you need the result // of applying visitor to children you probable also need to // process it in some way, therefore you anyway have to override // this method. If you do not needed that, we do not waste time // and memory for creating the output array node[property].forEach(child => visit(child, ...args)); }; } const DEFAULT_FUNCTIONS = { grammar(node, ...args) { for (const imp of node.imports) { visit(imp, ...args); } if (node.topLevelInitializer) { if (Array.isArray(node.topLevelInitializer)) { for (const tli of node.topLevelInitializer) { visit(tli, ...args); } } else { visit(node.topLevelInitializer, ...args); } } if (node.initializer) { if (Array.isArray(node.initializer)) { for (const init of node.initializer) { visit(init, ...args); } } else { visit(node.initializer, ...args); } } node.rules.forEach(rule => visit(rule, ...args)); }, grammar_import: visitNop, top_level_initializer: visitNop, initializer: visitNop, rule: visitExpression, named: visitExpression, choice: visitChildren("alternatives"), action: visitExpression, sequence: visitChildren("elements"), labeled: visitExpression, text: visitExpression, simple_and: visitExpression, simple_not: visitExpression, optional: visitExpression, zero_or_more: visitExpression, one_or_more: visitExpression, repeated(node, ...args) { if (node.delimiter) { visit(node.delimiter, ...args); } return visit(node.expression, ...args); }, group: visitExpression, semantic_and: visitNop, semantic_not: visitNop, rule_ref: visitNop, library_ref: visitNop, literal: visitNop, class: visitNop, any: visitNop, }; Object.keys(DEFAULT_FUNCTIONS).forEach(type => { if (!Object.prototype.hasOwnProperty.call(functions, type)) { functions[type] = DEFAULT_FUNCTIONS[type]; } }); return visit; }, }; module.exports = visitor;