'use strict'; var utils = require('./utils'); var define = require('define-property'); /** * Text regex */ var TEXT_REGEX = '(\\[(?=.*\\])|\\])+'; var not = utils.createRegex(TEXT_REGEX); /** * Brackets parsers */ function parsers(brackets) { brackets.state = brackets.state || {}; brackets.parser.sets.bracket = brackets.parser.sets.bracket || []; brackets.parser .capture('escape', function() { if (this.isInside('bracket')) return; var pos = this.position(); var m = this.match(/^\\(.)/); if (!m) return; return pos({ type: 'escape', val: m[0] }); }) /** * Text parser */ .capture('text', function() { if (this.isInside('bracket')) return; var pos = this.position(); var m = this.match(not); if (!m || !m[0]) return; return pos({ type: 'text', val: m[0] }); }) /** * POSIX character classes: "[[:alpha:][:digits:]]" */ .capture('posix', function() { var pos = this.position(); var m = this.match(/^\[:(.*?):\](?=.*\])/); if (!m) return; var inside = this.isInside('bracket'); if (inside) { brackets.posix++; } return pos({ type: 'posix', insideBracket: inside, inner: m[1], val: m[0] }); }) /** * Bracket (noop) */ .capture('bracket', function() {}) /** * Open: '[' */ .capture('bracket.open', function() { var parsed = this.parsed; var pos = this.position(); var m = this.match(/^\[(?=.*\])/); if (!m) return; var prev = this.prev(); var last = utils.last(prev.nodes); if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) { last.val = last.val.slice(0, last.val.length - 1); return pos({ type: 'escape', val: m[0] }); } var open = pos({ type: 'bracket.open', val: m[0] }); if (last.type === 'bracket.open' || this.isInside('bracket')) { open.val = '\\' + open.val; open.type = 'bracket.inner'; open.escaped = true; return open; } var node = pos({ type: 'bracket', nodes: [open] }); define(node, 'parent', prev); define(open, 'parent', node); this.push('bracket', node); prev.nodes.push(node); }) /** * Bracket text */ .capture('bracket.inner', function() { if (!this.isInside('bracket')) return; var pos = this.position(); var m = this.match(not); if (!m || !m[0]) return; var next = this.input.charAt(0); var val = m[0]; var node = pos({ type: 'bracket.inner', val: val }); if (val === '\\\\') { return node; } var first = val.charAt(0); var last = val.slice(-1); if (first === '!') { val = '^' + val.slice(1); } if (last === '\\' || (val === '^' && next === ']')) { val += this.input[0]; this.consume(1); } node.val = val; return node; }) /** * Close: ']' */ .capture('bracket.close', function() { var parsed = this.parsed; var pos = this.position(); var m = this.match(/^\]/); if (!m) return; var prev = this.prev(); var last = utils.last(prev.nodes); if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) { last.val = last.val.slice(0, last.val.length - 1); return pos({ type: 'escape', val: m[0] }); } var node = pos({ type: 'bracket.close', rest: this.input, val: m[0] }); if (last.type === 'bracket.open') { node.type = 'bracket.inner'; node.escaped = true; return node; } var bracket = this.pop('bracket'); if (!this.isType(bracket, 'bracket')) { if (this.options.strict) { throw new Error('missing opening "["'); } node.type = 'bracket.inner'; node.escaped = true; return node; } bracket.nodes.push(node); define(node, 'parent', bracket); }); } /** * Brackets parsers */ module.exports = parsers; /** * Expose text regex */ module.exports.TEXT_REGEX = TEXT_REGEX;