//=========================================================================== // Copy of ohm-js/examples/ecmascript/es5.js to get browserify+brfs to work //=========================================================================== /* eslint-env node */ 'use strict'; // -------------------------------------------------------------------- // Imports // -------------------------------------------------------------------- var fs = require('fs'); var path = require('path'); var ohm = require('ohm-js'); // -------------------------------------------------------------------- // Helpers // -------------------------------------------------------------------- function isUndefined(x) { return x === void 0; } // Take an Array of nodes, and whenever an _iter node is encountered, splice in its // recursively-flattened children instead. function flattenIterNodes(nodes) { var result = []; for (var i = 0; i < nodes.length; ++i) { if (nodes[i]._node.ctorName === '_iter') { result.push.apply(result, flattenIterNodes(nodes[i].children)); } else { result.push(nodes[i]); } } return result; } // Comparison function for sorting nodes based on their interval's start index. function compareByInterval(node, otherNode) { return node.interval.startIdx - otherNode.interval.startIdx; } function translateNonterminalCode(children, nodeTranslator) { var flatChildren = flattenIterNodes(children).sort(compareByInterval); var childResults = flatChildren.map(nodeTranslator); if (flatChildren.length === 0 || childResults.every(isUndefined)) { return undefined; } var code = ''; var interval = flatChildren[0].interval.collapsedLeft(); for (var i = 0; i < flatChildren.length; ++i) { if (childResults[i] == null) { // Grow the interval to include this node. interval = interval.coverageWith(flatChildren[i].interval.collapsedRight()); } else { interval = interval.coverageWith(flatChildren[i].interval.collapsedLeft()); code += interval.contents + childResults[i]; interval = flatChildren[i].interval.collapsedRight(); } } code += interval.contents; return code; } // Semantic actions for the `modifiedSource` attribute (see below). var modifiedSourceActions = { _nonterminal: function(children) { return translateNonterminalCode(children, function(n) { return n.modifiedSource; }) || this.interval.contents; }, _iter: function(_) { throw new Error('_iter semantic action should never be hit'); }, _terminal: function() { return undefined; } }; // Instantiate the ES5 grammar. var contents = fs.readFileSync(path.join(__dirname, 'es5.ohm')); var g = ohm.grammars(contents).ES5; var semantics = g.semantics(); // An attribute whose value is either a string representing the modified source code for the // node, or undefined (which means that the original source code should be used). semantics.addAttribute('modifiedSource', modifiedSourceActions); // A simple wrapper around the `modifiedSource` attribute, which always returns a string // containing the ES5 source code for the node. semantics.addAttribute('asES5', { _nonterminal: function(children) { return isUndefined(this.modifiedSource) ? this.interval.contents : this.modifiedSource; } }); module.exports = { grammar: g, semantics: semantics, translateNonterminalCode: translateNonterminalCode };