From 3c51c3be85bb0d1bdb87ea0d6632f1c256912f27 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sun, 6 Oct 2019 21:37:45 +0200 Subject: build: Add some required modules for node --- node_modules/snapdragon/lib/parser.js | 533 ++++++++++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 node_modules/snapdragon/lib/parser.js (limited to 'node_modules/snapdragon/lib/parser.js') diff --git a/node_modules/snapdragon/lib/parser.js b/node_modules/snapdragon/lib/parser.js new file mode 100644 index 0000000..a5a9b31 --- /dev/null +++ b/node_modules/snapdragon/lib/parser.js @@ -0,0 +1,533 @@ +'use strict'; + +var use = require('use'); +var util = require('util'); +var Cache = require('map-cache'); +var define = require('define-property'); +var debug = require('debug')('snapdragon:parser'); +var Position = require('./position'); +var utils = require('./utils'); + +/** + * Create a new `Parser` with the given `input` and `options`. + * @param {String} `input` + * @param {Object} `options` + * @api public + */ + +function Parser(options) { + debug('initializing', __filename); + this.options = utils.extend({source: 'string'}, options); + this.init(this.options); + use(this); +} + +/** + * Prototype methods + */ + +Parser.prototype = { + constructor: Parser, + + init: function(options) { + this.orig = ''; + this.input = ''; + this.parsed = ''; + + this.column = 1; + this.line = 1; + + this.regex = new Cache(); + this.errors = this.errors || []; + this.parsers = this.parsers || {}; + this.types = this.types || []; + this.sets = this.sets || {}; + this.fns = this.fns || []; + this.currentType = 'root'; + + var pos = this.position(); + this.bos = pos({type: 'bos', val: ''}); + + this.ast = { + type: 'root', + errors: this.errors, + nodes: [this.bos] + }; + + define(this.bos, 'parent', this.ast); + this.nodes = [this.ast]; + + this.count = 0; + this.setCount = 0; + this.stack = []; + }, + + /** + * Throw a formatted error with the cursor column and `msg`. + * @param {String} `msg` Message to use in the Error. + */ + + error: function(msg, node) { + var pos = node.position || {start: {column: 0, line: 0}}; + var line = pos.start.line; + var column = pos.start.column; + var source = this.options.source; + + var message = source + ' : ' + msg; + var err = new Error(message); + err.source = source; + err.reason = msg; + err.pos = pos; + + if (this.options.silent) { + this.errors.push(err); + } else { + throw err; + } + }, + + /** + * Define a non-enumberable property on the `Parser` instance. + * + * ```js + * parser.define('foo', 'bar'); + * ``` + * @name .define + * @param {String} `key` propery name + * @param {any} `val` property value + * @return {Object} Returns the Parser instance for chaining. + * @api public + */ + + define: function(key, val) { + define(this, key, val); + return this; + }, + + /** + * Mark position and patch `node.position`. + */ + + position: function() { + var start = { line: this.line, column: this.column }; + var self = this; + + return function(node) { + define(node, 'position', new Position(start, self)); + return node; + }; + }, + + /** + * Set parser `name` with the given `fn` + * @param {String} `name` + * @param {Function} `fn` + * @api public + */ + + set: function(type, fn) { + if (this.types.indexOf(type) === -1) { + this.types.push(type); + } + this.parsers[type] = fn.bind(this); + return this; + }, + + /** + * Get parser `name` + * @param {String} `name` + * @api public + */ + + get: function(name) { + return this.parsers[name]; + }, + + /** + * Push a `token` onto the `type` stack. + * + * @param {String} `type` + * @return {Object} `token` + * @api public + */ + + push: function(type, token) { + this.sets[type] = this.sets[type] || []; + this.count++; + this.stack.push(token); + return this.sets[type].push(token); + }, + + /** + * Pop a token off of the `type` stack + * @param {String} `type` + * @returns {Object} Returns a token + * @api public + */ + + pop: function(type) { + this.sets[type] = this.sets[type] || []; + this.count--; + this.stack.pop(); + return this.sets[type].pop(); + }, + + /** + * Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`. + * + * @param {String} `type` + * @return {Boolean} + * @api public + */ + + isInside: function(type) { + this.sets[type] = this.sets[type] || []; + return this.sets[type].length > 0; + }, + + /** + * Return true if `node` is the given `type`. + * + * ```js + * parser.isType(node, 'brace'); + * ``` + * @param {Object} `node` + * @param {String} `type` + * @return {Boolean} + * @api public + */ + + isType: function(node, type) { + return node && node.type === type; + }, + + /** + * Get the previous AST node + * @return {Object} + */ + + prev: function(n) { + return this.stack.length > 0 + ? utils.last(this.stack, n) + : utils.last(this.nodes, n); + }, + + /** + * Update line and column based on `str`. + */ + + consume: function(len) { + this.input = this.input.substr(len); + }, + + /** + * Update column based on `str`. + */ + + updatePosition: function(str, len) { + var lines = str.match(/\n/g); + if (lines) this.line += lines.length; + var i = str.lastIndexOf('\n'); + this.column = ~i ? len - i : this.column + len; + this.parsed += str; + this.consume(len); + }, + + /** + * Match `regex`, return captures, and update the cursor position by `match[0]` length. + * @param {RegExp} `regex` + * @return {Object} + */ + + match: function(regex) { + var m = regex.exec(this.input); + if (m) { + this.updatePosition(m[0], m[0].length); + return m; + } + }, + + /** + * Capture `type` with the given regex. + * @param {String} `type` + * @param {RegExp} `regex` + * @return {Function} + */ + + capture: function(type, regex) { + if (typeof regex === 'function') { + return this.set.apply(this, arguments); + } + + this.regex.set(type, regex); + this.set(type, function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(regex); + if (!m || !m[0]) return; + + var prev = this.prev(); + var node = pos({ + type: type, + val: m[0], + parsed: parsed, + rest: this.input + }); + + if (m[1]) { + node.inner = m[1]; + } + + define(node, 'inside', this.stack.length > 0); + define(node, 'parent', prev); + prev.nodes.push(node); + }.bind(this)); + return this; + }, + + /** + * Create a parser with open and close for parens, + * brackets or braces + */ + + capturePair: function(type, openRegex, closeRegex, fn) { + this.sets[type] = this.sets[type] || []; + + /** + * Open + */ + + this.set(type + '.open', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(openRegex); + if (!m || !m[0]) return; + + var val = m[0]; + this.setCount++; + this.specialChars = true; + var open = pos({ + type: type + '.open', + val: val, + rest: this.input + }); + + if (typeof m[1] !== 'undefined') { + open.inner = m[1]; + } + + var prev = this.prev(); + var node = pos({ + type: type, + nodes: [open] + }); + + define(node, 'rest', this.input); + define(node, 'parsed', parsed); + define(node, 'prefix', m[1]); + define(node, 'parent', prev); + define(open, 'parent', node); + + if (typeof fn === 'function') { + fn.call(this, open, node); + } + + this.push(type, node); + prev.nodes.push(node); + }); + + /** + * Close + */ + + this.set(type + '.close', function() { + var pos = this.position(); + var m = this.match(closeRegex); + if (!m || !m[0]) return; + + var parent = this.pop(type); + var node = pos({ + type: type + '.close', + rest: this.input, + suffix: m[1], + val: m[0] + }); + + if (!this.isType(parent, type)) { + if (this.options.strict) { + throw new Error('missing opening "' + type + '"'); + } + + this.setCount--; + node.escaped = true; + return node; + } + + if (node.suffix === '\\') { + parent.escaped = true; + node.escaped = true; + } + + parent.nodes.push(node); + define(node, 'parent', parent); + }); + + return this; + }, + + /** + * Capture end-of-string + */ + + eos: function() { + var pos = this.position(); + if (this.input) return; + var prev = this.prev(); + + while (prev.type !== 'root' && !prev.visited) { + if (this.options.strict === true) { + throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2)); + } + + if (!hasDelims(prev)) { + prev.parent.escaped = true; + prev.escaped = true; + } + + visit(prev, function(node) { + if (!hasDelims(node.parent)) { + node.parent.escaped = true; + node.escaped = true; + } + }); + + prev = prev.parent; + } + + var tok = pos({ + type: 'eos', + val: this.append || '' + }); + + define(tok, 'parent', this.ast); + return tok; + }, + + /** + * Run parsers to advance the cursor position + */ + + next: function() { + var parsed = this.parsed; + var len = this.types.length; + var idx = -1; + var tok; + + while (++idx < len) { + if ((tok = this.parsers[this.types[idx]].call(this))) { + define(tok, 'rest', this.input); + define(tok, 'parsed', parsed); + this.last = tok; + return tok; + } + } + }, + + /** + * Parse the given string. + * @return {Array} + */ + + parse: function(input) { + if (typeof input !== 'string') { + throw new TypeError('expected a string'); + } + + this.init(this.options); + this.orig = input; + this.input = input; + var self = this; + + function parse() { + // check input before calling `.next()` + input = self.input; + + // get the next AST ndoe + var node = self.next(); + if (node) { + var prev = self.prev(); + if (prev) { + define(node, 'parent', prev); + if (prev.nodes) { + prev.nodes.push(node); + } + } + + if (self.sets.hasOwnProperty(prev.type)) { + self.currentType = prev.type; + } + } + + // if we got here but input is not changed, throw an error + if (self.input && input === self.input) { + throw new Error('no parsers registered for: "' + self.input.slice(0, 5) + '"'); + } + } + + while (this.input) parse(); + if (this.stack.length && this.options.strict) { + var node = this.stack.pop(); + throw this.error('missing opening ' + node.type + ': "' + this.orig + '"'); + } + + var eos = this.eos(); + var tok = this.prev(); + if (tok.type !== 'eos') { + this.ast.nodes.push(eos); + } + + return this.ast; + } +}; + +/** + * Visit `node` with the given `fn` + */ + +function visit(node, fn) { + if (!node.visited) { + define(node, 'visited', true); + return node.nodes ? mapVisit(node.nodes, fn) : fn(node); + } + return node; +} + +/** + * Map visit over array of `nodes`. + */ + +function mapVisit(nodes, fn) { + var len = nodes.length; + var idx = -1; + while (++idx < len) { + visit(nodes[idx], fn); + } +} + +function hasOpen(node) { + return node.nodes && node.nodes[0].type === (node.type + '.open'); +} + +function hasClose(node) { + return node.nodes && utils.last(node.nodes).type === (node.type + '.close'); +} + +function hasDelims(node) { + return hasOpen(node) && hasClose(node); +} + +/** + * Expose `Parser` + */ + +module.exports = Parser; -- cgit v1.2.3