| '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; |