| 'use strict'; |
| |
| var brackets = require('expand-brackets'); |
| var define = require('define-property'); |
| var utils = require('./utils'); |
| |
| /** |
| * Characters to use in text regex (we want to "not" match |
| * characters that are matched by other parsers) |
| */ |
| |
| var TEXT_REGEX = '([!@*?+]?\\(|\\)|[*?.+\\\\]|\\[:?(?=.*\\])|:?\\])+'; |
| var not = utils.createRegex(TEXT_REGEX); |
| |
| /** |
| * Extglob parsers |
| */ |
| |
| function parsers(extglob) { |
| extglob.state = extglob.state || {}; |
| |
| /** |
| * Use `expand-brackets` parsers |
| */ |
| |
| extglob.use(brackets.parsers); |
| extglob.parser.sets.paren = extglob.parser.sets.paren || []; |
| extglob.parser |
| |
| /** |
| * Extglob open: "*(" |
| */ |
| |
| .capture('paren.open', function() { |
| var parsed = this.parsed; |
| var pos = this.position(); |
| var m = this.match(/^([!@*?+])?\(/); |
| if (!m) return; |
| |
| var prev = this.prev(); |
| var prefix = m[1]; |
| var val = m[0]; |
| |
| var open = pos({ |
| type: 'paren.open', |
| parsed: parsed, |
| val: val |
| }); |
| |
| var node = pos({ |
| type: 'paren', |
| prefix: prefix, |
| nodes: [open] |
| }); |
| |
| // if nested negation extglobs, just cancel them out to simplify |
| if (prefix === '!' && prev.type === 'paren' && prev.prefix === '!') { |
| prev.prefix = '@'; |
| node.prefix = '@'; |
| } |
| |
| define(node, 'rest', this.input); |
| define(node, 'parsed', parsed); |
| define(node, 'parent', prev); |
| define(open, 'parent', node); |
| |
| this.push('paren', node); |
| prev.nodes.push(node); |
| }) |
| |
| /** |
| * Extglob close: ")" |
| */ |
| |
| .capture('paren.close', function() { |
| var parsed = this.parsed; |
| var pos = this.position(); |
| var m = this.match(/^\)/); |
| if (!m) return; |
| |
| var parent = this.pop('paren'); |
| var node = pos({ |
| type: 'paren.close', |
| rest: this.input, |
| parsed: parsed, |
| val: m[0] |
| }); |
| |
| if (!this.isType(parent, 'paren')) { |
| if (this.options.strict) { |
| throw new Error('missing opening paren: "("'); |
| } |
| node.escaped = true; |
| return node; |
| } |
| |
| node.prefix = parent.prefix; |
| parent.nodes.push(node); |
| define(node, 'parent', parent); |
| }) |
| |
| /** |
| * Escape: "\\." |
| */ |
| |
| .capture('escape', function() { |
| var pos = this.position(); |
| var m = this.match(/^\\(.)/); |
| if (!m) return; |
| |
| return pos({ |
| type: 'escape', |
| val: m[0], |
| ch: m[1] |
| }); |
| }) |
| |
| /** |
| * Question marks: "?" |
| */ |
| |
| .capture('qmark', function() { |
| var parsed = this.parsed; |
| var pos = this.position(); |
| var m = this.match(/^\?+(?!\()/); |
| if (!m) return; |
| extglob.state.metachar = true; |
| return pos({ |
| type: 'qmark', |
| rest: this.input, |
| parsed: parsed, |
| val: m[0] |
| }); |
| }) |
| |
| /** |
| * Character parsers |
| */ |
| |
| .capture('star', /^\*(?!\()/) |
| .capture('plus', /^\+(?!\()/) |
| .capture('dot', /^\./) |
| .capture('text', not); |
| }; |
| |
| /** |
| * Expose text regex string |
| */ |
| |
| module.exports.TEXT_REGEX = TEXT_REGEX; |
| |
| /** |
| * Extglob parsers |
| */ |
| |
| module.exports = parsers; |