code cosmetics
This commit is contained in:
parent
224771b34e
commit
79e95eadf2
|
@ -1020,11 +1020,11 @@ self.parserlib = (() => {
|
||||||
'hsl': '<hsl-color>',
|
'hsl': '<hsl-color>',
|
||||||
'hsla': '<hsl-color>',
|
'hsla': '<hsl-color>',
|
||||||
'hwb': '<hsl-color>',
|
'hwb': '<hsl-color>',
|
||||||
'gray': '<number> [ / <nonnegative-number-or-percentage> ]? )',
|
'gray': '<number> [ / <nonnegative-number-or-percentage> ]?',
|
||||||
'device-cmyk': '<number-percentage>{4} [ / <nonnegative-number-or-percentage> ]? , <color>?',
|
'device-cmyk': '<number-percentage>{4} [ / <nonnegative-number-or-percentage> ]? , <color>?',
|
||||||
|
|
||||||
'color': '[ <color> | [ <number> | <angle> ] ] <color-adjuster>*',
|
'color': '[ <color> | [ <number> | <angle> ] ] <color-adjuster>*',
|
||||||
'content': '[ text | before | after | first-letter | marker ]?',
|
'content': 'text | before | after | first-letter | marker',
|
||||||
'cubic-bezier': '<number>#{4}',
|
'cubic-bezier': '<number>#{4}',
|
||||||
'frames': '<integer>',
|
'frames': '<integer>',
|
||||||
'steps': '<integer> [ , [ start | end ] ]?',
|
'steps': '<integer> [ , [ start | end ] ]?',
|
||||||
|
@ -1074,6 +1074,7 @@ self.parserlib = (() => {
|
||||||
// omitted values default to the initial value for interpolation
|
// omitted values default to the initial value for interpolation
|
||||||
'blur', 'brightness', 'contrast', 'drop-shadow', 'grayscale',
|
'blur', 'brightness', 'contrast', 'drop-shadow', 'grayscale',
|
||||||
'hue-rotate', 'invert', 'opacity', 'saturate', 'sepia',
|
'hue-rotate', 'invert', 'opacity', 'saturate', 'sepia',
|
||||||
|
'content',
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1571,9 +1572,10 @@ self.parserlib = (() => {
|
||||||
*/
|
*/
|
||||||
class Matcher {
|
class Matcher {
|
||||||
|
|
||||||
constructor(matchFunc, toString) {
|
constructor(matchFunc, toString, options) {
|
||||||
this.matchFunc = matchFunc;
|
this.matchFunc = matchFunc;
|
||||||
this.toString = typeof toString === 'function' ? toString : () => toString;
|
this.toString = typeof toString === 'function' ? toString : () => toString;
|
||||||
|
if (options) this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
static parse(str) {
|
static parse(str) {
|
||||||
|
@ -1587,80 +1589,7 @@ self.parserlib = (() => {
|
||||||
/** Simple recursive-descent grammar to build matchers from strings. */
|
/** Simple recursive-descent grammar to build matchers from strings. */
|
||||||
static doParse(str) {
|
static doParse(str) {
|
||||||
const reader = new StringReader(str);
|
const reader = new StringReader(str);
|
||||||
function eat(matcher) {
|
const result = Matcher.grammarParser.parse(reader);
|
||||||
const result = reader.readMatch(matcher);
|
|
||||||
if (result === null) {
|
|
||||||
throw new Error(`Internal error. Expected ${matcher} at ${reader._line}:${reader._col}.`);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
function expr() {
|
|
||||||
// expr = oror (" | " oror)*
|
|
||||||
const m = [oror()];
|
|
||||||
while (reader.readMatch(' | ')) {
|
|
||||||
m.push(oror());
|
|
||||||
}
|
|
||||||
return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
|
|
||||||
}
|
|
||||||
function oror() {
|
|
||||||
// oror = andand ( " || " andand)*
|
|
||||||
const m = [andand()];
|
|
||||||
while (reader.readMatch(' || ')) {
|
|
||||||
m.push(andand());
|
|
||||||
}
|
|
||||||
return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
|
|
||||||
}
|
|
||||||
function andand() {
|
|
||||||
// andand = seq ( " && " seq)*
|
|
||||||
const m = [seq()];
|
|
||||||
while (reader.readMatch(' && ')) {
|
|
||||||
m.push(seq());
|
|
||||||
}
|
|
||||||
return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
|
|
||||||
}
|
|
||||||
function seq() {
|
|
||||||
// seq = mod ( " " mod)*
|
|
||||||
const m = [mod()];
|
|
||||||
while (reader.readMatch(/\s(?![&|\]])/y)) {
|
|
||||||
m.push(mod());
|
|
||||||
}
|
|
||||||
return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
|
|
||||||
}
|
|
||||||
function mod() {
|
|
||||||
// mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
|
|
||||||
const m = term();
|
|
||||||
reader.mark();
|
|
||||||
let hash;
|
|
||||||
switch (reader.read()) {
|
|
||||||
case '?': return m.question();
|
|
||||||
case '*': return m.star();
|
|
||||||
case '+': return m.plus();
|
|
||||||
case '#':
|
|
||||||
if (reader.peek() !== '{') return m.hash();
|
|
||||||
reader.read();
|
|
||||||
hash = '#';
|
|
||||||
// fallthrough
|
|
||||||
case '{': {
|
|
||||||
const min = eat(/\s*\d+\s*/y).trim();
|
|
||||||
const c = eat(/[,}]/y);
|
|
||||||
const max = c === ',' ? eat(/\s*\d+\s*}/y).slice(0, -1).trim() : min;
|
|
||||||
return m.braces(Number(min), Number(max), hash, hash && Matcher.cast(','));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
reader.reset();
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
function term() {
|
|
||||||
// term = <nt> | literal | "[ " expression " ]"
|
|
||||||
if (reader.readMatch('[ ')) {
|
|
||||||
const m = expr();
|
|
||||||
eat(' ]');
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
return Matcher.fromType(eat(/[^\s?*+#{]+/y));
|
|
||||||
}
|
|
||||||
const result = expr();
|
|
||||||
if (!reader.eof()) {
|
if (!reader.eof()) {
|
||||||
throw new Error(`Internal error. Expected end of string ${reader._line}:${reader._col}.`);
|
throw new Error(`Internal error. Expected end of string ${reader._line}:${reader._col}.`);
|
||||||
}
|
}
|
||||||
|
@ -1675,7 +1604,7 @@ self.parserlib = (() => {
|
||||||
static fromType(type) {
|
static fromType(type) {
|
||||||
let m = cachedMatcher.get(type);
|
let m = cachedMatcher.get(type);
|
||||||
if (m) return m;
|
if (m) return m;
|
||||||
m = new Matcher(expr => expr.hasNext() && ValidationTypes.isType(expr, type), type);
|
m = new Matcher(Matcher.matchFunc.fromType, type, type);
|
||||||
cachedMatcher.set(type, m);
|
cachedMatcher.set(type, m);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
@ -1684,26 +1613,14 @@ self.parserlib = (() => {
|
||||||
static seq(...args) {
|
static seq(...args) {
|
||||||
const ms = args.map(Matcher.cast);
|
const ms = args.map(Matcher.cast);
|
||||||
if (ms.length === 1) return ms[0];
|
if (ms.length === 1) return ms[0];
|
||||||
return new Matcher(
|
return new Matcher(Matcher.matchFunc.seq, Matcher.toStringFunc.seq, ms);
|
||||||
expression => ms.every(m => m.match(expression)),
|
|
||||||
prec => {
|
|
||||||
const p = Matcher.prec.SEQ;
|
|
||||||
const s = ms.map(m => m.toString(p)).join(' ');
|
|
||||||
return prec > p ? `[ ${s} ]` : s;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher for one or more alternatives, where exactly one must occur.
|
// Matcher for one or more alternatives, where exactly one must occur.
|
||||||
static alt(...args) {
|
static alt(...args) {
|
||||||
const ms = args.map(Matcher.cast);
|
const ms = args.map(Matcher.cast);
|
||||||
if (ms.length === 1) return ms[0];
|
if (ms.length === 1) return ms[0];
|
||||||
return new Matcher(
|
return new Matcher(Matcher.matchFunc.alt, Matcher.toStringFunc.alt, ms);
|
||||||
expression => ms.some(m => m.match(expression)),
|
|
||||||
prec => {
|
|
||||||
const p = Matcher.prec.ALT;
|
|
||||||
const s = ms.map(m => m.toString(p)).join(' | ');
|
|
||||||
return prec > p ? `[ ${s} ]` : s;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1721,71 +1638,9 @@ self.parserlib = (() => {
|
||||||
ms.push(Matcher.cast(arg));
|
ms.push(Matcher.cast(arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const m = new Matcher(Matcher.matchFunc.many, Matcher.toStringFunc.many, ms);
|
||||||
if (required === true) required = new Array(ms.length).fill(true);
|
m.required = required === true ? new Array(ms.length).fill(true) : required;
|
||||||
|
return m;
|
||||||
const result = new Matcher(expression => {
|
|
||||||
const seen = [];
|
|
||||||
let max = 0;
|
|
||||||
let pass = 0;
|
|
||||||
// If couldn't get a complete match, retrace our steps to make the
|
|
||||||
// match with the maximum # of required elements.
|
|
||||||
if (!tryMatch(0)) {
|
|
||||||
pass++;
|
|
||||||
tryMatch(0);
|
|
||||||
}
|
|
||||||
if (required === false) {
|
|
||||||
return max > 0;
|
|
||||||
}
|
|
||||||
// Use finer-grained specification of which matchers are required.
|
|
||||||
for (let i = 0; i < ms.length; i++) {
|
|
||||||
if (required[i] && !seen[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
function tryMatch(matchCount) {
|
|
||||||
for (let i = 0; i < ms.length; i++) {
|
|
||||||
if (seen[i]) continue;
|
|
||||||
expression.mark();
|
|
||||||
if (!ms[i].match(expression)) {
|
|
||||||
expression.drop();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
seen[i] = true;
|
|
||||||
// Increase matchCount if this was a required element
|
|
||||||
// (or if all the elements are optional)
|
|
||||||
if (tryMatch(matchCount + (required === false || required[i] ? 1 : 0))) {
|
|
||||||
expression.drop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Backtrack: try *not* matching using this rule, and
|
|
||||||
// let's see if it leads to a better overall match.
|
|
||||||
expression.restore();
|
|
||||||
seen[i] = false;
|
|
||||||
}
|
|
||||||
if (pass === 0) {
|
|
||||||
max = Math.max(matchCount, max);
|
|
||||||
return matchCount === ms.length;
|
|
||||||
} else {
|
|
||||||
return matchCount === max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, prec => {
|
|
||||||
const p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
|
|
||||||
const s = ms.map((m, i) => {
|
|
||||||
if (required !== false && !required[i]) {
|
|
||||||
return m.toString(Matcher.prec.MOD) + '?';
|
|
||||||
}
|
|
||||||
return m.toString(p);
|
|
||||||
}).join(required === false ? ' || ' : ' && ');
|
|
||||||
return prec > p ? `[ ${s} ]` : s;
|
|
||||||
});
|
|
||||||
|
|
||||||
result.options = ms;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher for two or more options in any order, all mandatory.
|
// Matcher for two or more options in any order, all mandatory.
|
||||||
|
@ -1873,6 +1728,195 @@ self.parserlib = (() => {
|
||||||
ALT: 1,
|
ALT: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Matcher.matchFunc = {
|
||||||
|
|
||||||
|
alt(expression) {
|
||||||
|
for (const m of this.options) {
|
||||||
|
if (m.match(expression)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromType(expr) {
|
||||||
|
return expr.hasNext() && ValidationTypes.isType(expr, this.options);
|
||||||
|
},
|
||||||
|
|
||||||
|
seq(expression) {
|
||||||
|
for (const m of this.options) {
|
||||||
|
if (!m.match(expression)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
many(expression) {
|
||||||
|
const seen = [];
|
||||||
|
const {options: ms, required} = this;
|
||||||
|
let max = 0;
|
||||||
|
let pass = 0;
|
||||||
|
// If couldn't get a complete match, retrace our steps to make the
|
||||||
|
// match with the maximum # of required elements.
|
||||||
|
if (!tryMatch(0)) {
|
||||||
|
pass++;
|
||||||
|
tryMatch(0);
|
||||||
|
}
|
||||||
|
if (required === false) {
|
||||||
|
return max > 0;
|
||||||
|
}
|
||||||
|
// Use finer-grained specification of which matchers are required.
|
||||||
|
for (let i = 0; i < ms.length; i++) {
|
||||||
|
if (required[i] && !seen[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
function tryMatch(matchCount) {
|
||||||
|
for (let i = 0; i < ms.length; i++) {
|
||||||
|
if (seen[i]) continue;
|
||||||
|
expression.mark();
|
||||||
|
if (!ms[i].match(expression)) {
|
||||||
|
expression.drop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen[i] = true;
|
||||||
|
// Increase matchCount if this was a required element
|
||||||
|
// (or if all the elements are optional)
|
||||||
|
if (tryMatch(matchCount + (required === false || required[i] ? 1 : 0))) {
|
||||||
|
expression.drop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Backtrack: try *not* matching using this rule, and
|
||||||
|
// let's see if it leads to a better overall match.
|
||||||
|
expression.restore();
|
||||||
|
seen[i] = false;
|
||||||
|
}
|
||||||
|
if (pass === 0) {
|
||||||
|
max = Math.max(matchCount, max);
|
||||||
|
return matchCount === ms.length;
|
||||||
|
} else {
|
||||||
|
return matchCount === max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Matcher.toStringFunc = {
|
||||||
|
|
||||||
|
alt(prec) {
|
||||||
|
const p = Matcher.prec.ALT;
|
||||||
|
const s = this.options.map(m => m.toString(p)).join(' | ');
|
||||||
|
return prec > p ? `[ ${s} ]` : s;
|
||||||
|
},
|
||||||
|
|
||||||
|
seq(prec) {
|
||||||
|
const p = Matcher.prec.SEQ;
|
||||||
|
const s = this.options.map(m => m.toString(p)).join(' ');
|
||||||
|
return prec > p ? `[ ${s} ]` : s;
|
||||||
|
},
|
||||||
|
|
||||||
|
many(prec) {
|
||||||
|
const {options: ms, required} = this;
|
||||||
|
const p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
|
||||||
|
const s = ms.map((m, i) => {
|
||||||
|
if (required !== false && !required[i]) {
|
||||||
|
return m.toString(Matcher.prec.MOD) + '?';
|
||||||
|
}
|
||||||
|
return m.toString(p);
|
||||||
|
}).join(required === false ? ' || ' : ' && ');
|
||||||
|
return prec > p ? `[ ${s} ]` : s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Matcher.grammarParser = (() => {
|
||||||
|
let reader;
|
||||||
|
return {parse};
|
||||||
|
|
||||||
|
function parse(newReader) {
|
||||||
|
reader = newReader;
|
||||||
|
return expr();
|
||||||
|
}
|
||||||
|
|
||||||
|
function expr() {
|
||||||
|
// expr = oror (" | " oror)*
|
||||||
|
const m = [oror()];
|
||||||
|
while (reader.readMatch(' | ')) {
|
||||||
|
m.push(oror());
|
||||||
|
}
|
||||||
|
return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
function oror() {
|
||||||
|
// oror = andand ( " || " andand)*
|
||||||
|
const m = [andand()];
|
||||||
|
while (reader.readMatch(' || ')) {
|
||||||
|
m.push(andand());
|
||||||
|
}
|
||||||
|
return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
function andand() {
|
||||||
|
// andand = seq ( " && " seq)*
|
||||||
|
const m = [seq()];
|
||||||
|
while (reader.readMatch(' && ')) {
|
||||||
|
m.push(seq());
|
||||||
|
}
|
||||||
|
return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
function seq() {
|
||||||
|
// seq = mod ( " " mod)*
|
||||||
|
const m = [mod()];
|
||||||
|
while (reader.readMatch(/\s(?![&|\]])/y)) {
|
||||||
|
m.push(mod());
|
||||||
|
}
|
||||||
|
return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mod() {
|
||||||
|
// mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
|
||||||
|
const m = term();
|
||||||
|
reader.mark();
|
||||||
|
let hash;
|
||||||
|
switch (reader.read()) {
|
||||||
|
case '?': return m.question();
|
||||||
|
case '*': return m.star();
|
||||||
|
case '+': return m.plus();
|
||||||
|
case '#':
|
||||||
|
if (reader.peek() !== '{') return m.hash();
|
||||||
|
reader.read();
|
||||||
|
hash = '#';
|
||||||
|
// fallthrough
|
||||||
|
case '{': {
|
||||||
|
const min = eat(/\s*\d+\s*/y).trim();
|
||||||
|
const c = eat(/[,}]/y);
|
||||||
|
const max = c === ',' ? eat(/\s*\d+\s*}/y).slice(0, -1).trim() : min;
|
||||||
|
return m.braces(Number(min), Number(max), hash, hash && Matcher.cast(','));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
reader.reset();
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
function term() {
|
||||||
|
// term = <nt> | literal | "[ " expression " ]"
|
||||||
|
if (reader.readMatch('[ ')) {
|
||||||
|
const m = expr();
|
||||||
|
eat(' ]');
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
return Matcher.fromType(eat(/[^\s?*+#{]+/y));
|
||||||
|
}
|
||||||
|
|
||||||
|
function eat(matcher) {
|
||||||
|
const result = reader.readMatch(matcher);
|
||||||
|
if (result === null) {
|
||||||
|
throw new Error(`Internal error. Expected ${matcher} at ${reader._line}:${reader._col}.`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region EventTarget
|
//region EventTarget
|
||||||
|
|
||||||
|
@ -2464,21 +2508,22 @@ self.parserlib = (() => {
|
||||||
|
|
||||||
isType(expression, type) {
|
isType(expression, type) {
|
||||||
const part = expression.peek();
|
const part = expression.peek();
|
||||||
let result;
|
let result, m;
|
||||||
|
|
||||||
if (this.simple['<var>'](part)) {
|
if (this.simple['<var>'](part)) {
|
||||||
result = true;
|
result = true;
|
||||||
|
|
||||||
} else if (type.charAt(0) !== '<') {
|
} else if (!type.startsWith('<')) {
|
||||||
result = this.isLiteral(part, type);
|
result = this.isLiteral(part, type);
|
||||||
|
|
||||||
} else if (this.simple[type]) {
|
} else if ((m = this.simple[type])) {
|
||||||
result = this.simple[type](part);
|
result = m.call(this.simple, part);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return this.complex[type] instanceof Matcher ?
|
m = this.complex[type];
|
||||||
this.complex[type].match(expression) :
|
return m instanceof Matcher ?
|
||||||
this.complex[type](expression);
|
m.match(expression) :
|
||||||
|
m.call(this.complex, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) expression.next();
|
if (result) expression.next();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user