You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
4.3 KiB
220 lines
4.3 KiB
1 year ago
|
'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;
|