@tbela99/css-parser
Version:
CSS parser, minifier and validator for node and the browser
1,095 lines (1,092 loc) • 43.4 kB
JavaScript
import { isIdent, isPseudo } from '../../syntax/syntax.js';
import { EnumToken } from '../../ast/types.js';
import '../../ast/minify.js';
import '../../ast/walk.js';
import { getTokenType as getTokenType$1 } from '../../parser/parse.js';
import '../../parser/tokenize.js';
import '../../parser/utils/config.js';
import '../../syntax/color/utils/constants.js';
import '../../renderer/sourcemap/lib/encode.js';
var ValidationTokenEnum;
(function (ValidationTokenEnum) {
ValidationTokenEnum[ValidationTokenEnum["Root"] = 0] = "Root";
ValidationTokenEnum[ValidationTokenEnum["Keyword"] = 1] = "Keyword";
ValidationTokenEnum[ValidationTokenEnum["PropertyType"] = 2] = "PropertyType";
ValidationTokenEnum[ValidationTokenEnum["DeclarationType"] = 3] = "DeclarationType";
ValidationTokenEnum[ValidationTokenEnum["AtRule"] = 4] = "AtRule";
ValidationTokenEnum[ValidationTokenEnum["ValidationFunctionDefinition"] = 5] = "ValidationFunctionDefinition";
ValidationTokenEnum[ValidationTokenEnum["OpenBracket"] = 6] = "OpenBracket";
ValidationTokenEnum[ValidationTokenEnum["CloseBracket"] = 7] = "CloseBracket";
ValidationTokenEnum[ValidationTokenEnum["OpenParenthesis"] = 8] = "OpenParenthesis";
ValidationTokenEnum[ValidationTokenEnum["CloseParenthesis"] = 9] = "CloseParenthesis";
ValidationTokenEnum[ValidationTokenEnum["Comma"] = 10] = "Comma";
ValidationTokenEnum[ValidationTokenEnum["Pipe"] = 11] = "Pipe";
ValidationTokenEnum[ValidationTokenEnum["Column"] = 12] = "Column";
ValidationTokenEnum[ValidationTokenEnum["Star"] = 13] = "Star";
ValidationTokenEnum[ValidationTokenEnum["OpenCurlyBrace"] = 14] = "OpenCurlyBrace";
ValidationTokenEnum[ValidationTokenEnum["CloseCurlyBrace"] = 15] = "CloseCurlyBrace";
ValidationTokenEnum[ValidationTokenEnum["HashMark"] = 16] = "HashMark";
ValidationTokenEnum[ValidationTokenEnum["QuestionMark"] = 17] = "QuestionMark";
ValidationTokenEnum[ValidationTokenEnum["Function"] = 18] = "Function";
ValidationTokenEnum[ValidationTokenEnum["Number"] = 19] = "Number";
ValidationTokenEnum[ValidationTokenEnum["Whitespace"] = 20] = "Whitespace";
ValidationTokenEnum[ValidationTokenEnum["Parenthesis"] = 21] = "Parenthesis";
ValidationTokenEnum[ValidationTokenEnum["Bracket"] = 22] = "Bracket";
ValidationTokenEnum[ValidationTokenEnum["Block"] = 23] = "Block";
ValidationTokenEnum[ValidationTokenEnum["AtLeastOnce"] = 24] = "AtLeastOnce";
ValidationTokenEnum[ValidationTokenEnum["Separator"] = 25] = "Separator";
ValidationTokenEnum[ValidationTokenEnum["Exclamation"] = 26] = "Exclamation";
ValidationTokenEnum[ValidationTokenEnum["Ampersand"] = 27] = "Ampersand";
ValidationTokenEnum[ValidationTokenEnum["PipeToken"] = 28] = "PipeToken";
ValidationTokenEnum[ValidationTokenEnum["ColumnToken"] = 29] = "ColumnToken";
ValidationTokenEnum[ValidationTokenEnum["AmpersandToken"] = 30] = "AmpersandToken";
ValidationTokenEnum[ValidationTokenEnum["Parens"] = 31] = "Parens";
ValidationTokenEnum[ValidationTokenEnum["PseudoClassToken"] = 32] = "PseudoClassToken";
ValidationTokenEnum[ValidationTokenEnum["PseudoClassFunctionToken"] = 33] = "PseudoClassFunctionToken";
ValidationTokenEnum[ValidationTokenEnum["StringToken"] = 34] = "StringToken";
ValidationTokenEnum[ValidationTokenEnum["AtRuleDefinition"] = 35] = "AtRuleDefinition";
ValidationTokenEnum[ValidationTokenEnum["DeclarationNameToken"] = 36] = "DeclarationNameToken";
ValidationTokenEnum[ValidationTokenEnum["DeclarationDefinitionToken"] = 37] = "DeclarationDefinitionToken";
ValidationTokenEnum[ValidationTokenEnum["SemiColon"] = 38] = "SemiColon";
ValidationTokenEnum[ValidationTokenEnum["Character"] = 39] = "Character";
ValidationTokenEnum[ValidationTokenEnum["InfinityToken"] = 40] = "InfinityToken";
})(ValidationTokenEnum || (ValidationTokenEnum = {}));
var ValidationSyntaxGroupEnum;
(function (ValidationSyntaxGroupEnum) {
ValidationSyntaxGroupEnum["Declarations"] = "declarations";
ValidationSyntaxGroupEnum["Functions"] = "functions";
ValidationSyntaxGroupEnum["Syntaxes"] = "syntaxes";
ValidationSyntaxGroupEnum["Selectors"] = "selectors";
ValidationSyntaxGroupEnum["AtRules"] = "atRules";
})(ValidationSyntaxGroupEnum || (ValidationSyntaxGroupEnum = {}));
const skipped = [
ValidationTokenEnum.Star,
ValidationTokenEnum.HashMark,
ValidationTokenEnum.AtLeastOnce,
ValidationTokenEnum.Exclamation,
ValidationTokenEnum.QuestionMark,
ValidationTokenEnum.OpenCurlyBrace
];
const objectProperties = {
enumerable: false,
writable: true,
configurable: true
};
function* tokenize(syntax, position = { ind: 0, lin: 1, col: 0 }, currentPosition = {
ind: -1,
lin: 1,
col: 0
}) {
let i = -1;
let buffer = '';
while (++i < syntax.length) {
let chr = syntax.charAt(i);
move(currentPosition, chr);
switch (chr) {
case '∞':
if (buffer.length > 0) {
yield getTokenType(buffer, position, currentPosition);
}
yield getTokenType(chr, position, currentPosition);
buffer = '';
break;
case '\\':
if (buffer.length > 0) {
yield getTokenType(buffer, position, currentPosition);
buffer = '';
}
buffer += chr;
chr = syntax.charAt(++i);
buffer += chr;
move(currentPosition, chr);
break;
case ';':
if (buffer.length > 0) {
yield getTokenType(buffer, position, currentPosition);
buffer = '';
}
yield getTokenType(chr, position, currentPosition);
buffer = '';
break;
case ':':
if (isIdent(buffer)) {
yield Object.defineProperty({
typ: ValidationTokenEnum.DeclarationNameToken,
val: buffer
}, 'pos', { ...objectProperties, value: { ...position } });
buffer = '';
move(currentPosition, chr);
break;
}
buffer += chr;
break;
case '"':
case "'":
if (buffer.length > 0) {
yield getTokenType(buffer, position, currentPosition);
buffer = '';
}
buffer += chr;
while (i + 1 < syntax.length) {
chr = syntax.charAt(++i);
buffer += chr;
move(currentPosition, chr);
if (chr == '\\') {
chr = syntax.charAt(++i);
buffer += chr;
move(currentPosition, chr);
continue;
}
if (chr == buffer.charAt(0)) {
break;
}
}
// check for type "<property>" or "<'property'>"
if (buffer.at(1) == '<' && buffer.at(-2) == '>') {
yield Object.defineProperty({
typ: ValidationTokenEnum.Character,
val: chr
}, 'pos', { ...objectProperties, value: { ...position } });
position = { ...currentPosition };
move(currentPosition, chr);
buffer = buffer.slice(1, -1);
yield* tokenize(buffer, position, currentPosition);
yield Object.defineProperty({
typ: ValidationTokenEnum.Character,
val: chr
}, 'pos', { ...objectProperties, value: { ...position } });
position = { ...currentPosition };
move(currentPosition, chr);
buffer = '';
break;
}
yield getTokenType(buffer, position, currentPosition);
buffer = '';
break;
case '<':
buffer += chr;
while (i + 1 < syntax.length) {
chr = syntax.charAt(++i);
buffer += chr;
move(currentPosition, chr);
if (chr == '>') {
break;
}
}
yield getTokenType(buffer, position, currentPosition);
buffer = '';
break;
case ' ':
case '\t':
case '\n':
case '\r':
case '\f':
case '|':
case '#':
case '+':
case ',':
case '[':
case ']':
case '(':
case ')':
case '*':
case '{':
case '}':
case '?':
case '!':
case '&':
if (buffer.length > 0) {
yield getTokenType(buffer, position, currentPosition);
}
if (chr == '|' || chr == '&') {
if (syntax.charAt(i + 1) == chr) {
move(currentPosition, chr);
yield getTokenType(chr + chr, position, currentPosition);
i++;
buffer = '';
continue;
}
}
Object.assign(position, currentPosition);
yield getTokenType(chr, position, currentPosition);
buffer = '';
break;
default:
buffer += chr;
break;
}
}
if (buffer.length > 0) {
yield getTokenType(buffer, position, currentPosition);
}
}
function parseSyntax(syntax) {
const root = Object.defineProperty({
typ: ValidationTokenEnum.Root,
chi: []
}, 'pos', { ...objectProperties, value: { ind: 0, lin: 1, col: 0 } });
return minify(doParseSyntax(syntax, tokenize(syntax), root));
}
function matchParens(syntax, iterator) {
let item;
let func = null;
let items = [];
let match = 0;
while ((item = iterator.next()) && !item.done) {
switch (item.value.typ) {
case ValidationTokenEnum.OpenParenthesis:
if (match > 0) {
func.chi.push(item.value);
}
else if (match == 0) {
func = items.at(-1);
if (func == null || func.val == null || func.val === '') {
func = Object.defineProperty({
typ: ValidationTokenEnum.Parens,
val: '',
chi: []
}, 'pos', { ...objectProperties, value: item.value.pos });
items.push(func);
}
else {
// @ts-ignore
func.typ = func.typ == ValidationTokenEnum.PseudoClassToken ? ValidationTokenEnum.PseudoClassFunctionToken : ValidationTokenEnum.Function;
func.chi = [];
}
}
match++;
break;
case ValidationTokenEnum.CloseParenthesis:
match--;
if (match > 0) {
func.chi.push(item.value);
}
break;
default:
if (match > 0) {
func.chi.push(item.value);
}
else {
items.push(item.value);
}
break;
}
}
for (const item of items) {
if ('chi' in item) {
// @ts-ignore
item.chi = matchParens(syntax, item.chi[Symbol.iterator]());
}
}
return items;
}
function matchBrackets(syntax, iterator) {
let item;
let bracket = null;
let items = [];
let match = 0;
while ((item = iterator.next()) && !item.done) {
switch (item.value.typ) {
case ValidationTokenEnum.OpenBracket:
if (match > 0) {
bracket.chi.push(item.value);
}
else if (match == 0) {
bracket = Object.defineProperty({
typ: ValidationTokenEnum.Bracket,
chi: []
}, 'pos', { ...objectProperties, value: item.value.pos });
items.push(bracket);
}
match++;
break;
case ValidationTokenEnum.CloseBracket:
match--;
if (match > 0) {
bracket.chi.push(item.value);
}
break;
default:
if (match > 0) {
bracket.chi.push(item.value);
}
else {
items.push(item.value);
}
break;
}
}
for (const item of items) {
if ('chi' in item) {
// @ts-ignore
item.chi = matchBrackets(syntax, item.chi[Symbol.iterator]());
}
}
return items;
}
function matchCurlBraces(syntax, iterator) {
let item;
let block = null;
let items = [];
let match = 0;
while ((item = iterator.next()) && !item.done) {
switch (item.value.typ) {
case ValidationTokenEnum.OpenCurlyBrace:
if (match > 0) {
block.chi.push(item.value);
}
else if (match == 0) {
block = Object.defineProperty({
typ: ValidationTokenEnum.Block,
chi: []
}, 'pos', { ...objectProperties, value: item.value.pos });
items.push(block);
}
match++;
break;
case ValidationTokenEnum.CloseCurlyBrace:
match--;
if (match > 0) {
block.chi.push(item.value);
}
break;
default:
if (match > 0) {
block.chi.push(item.value);
}
else {
items.push(item.value);
}
if ('chi' in item.value) {
// @ts-ignore
item.value.chi = matchCurlBraces(syntax, item.value.chi[Symbol.iterator]());
}
break;
}
}
let it;
let i = 0;
for (; i < items.length; i++) {
it = items[i];
if (i > 0 && it.typ == ValidationTokenEnum.Block && it.chi[0]?.typ == ValidationTokenEnum.Number) {
items[i - 1].occurence = {
min: +it.chi[0].val,
max: it.chi[2]?.typ == ValidationTokenEnum.InfinityToken ? Number.POSITIVE_INFINITY : +(it.chi[2] ?? it.chi[0]).val
};
items.splice(i--, 1);
continue;
}
if ('chi' in it) {
// @ts-ignore
it.chi = matchBrackets(syntax, it.chi[Symbol.iterator]());
}
}
return items;
}
function matchAtRule(syntax, iterator) {
const children = [];
let item;
let token = null;
while ((item = iterator.next()) && !item.done) {
if (item.value.typ == ValidationTokenEnum.AtRule || item.value.typ == ValidationTokenEnum.AtRuleDefinition) {
token = Object.defineProperty({
...item.value,
typ: ValidationTokenEnum.AtRuleDefinition
}, 'pos', { ...objectProperties, value: item.value.pos });
children.push(token);
item = iterator.next();
if (item.done) {
// @ts-ignore
token.typ = ValidationTokenEnum.AtRule;
break;
}
item = iterator.next();
if (item.done) {
break;
}
if (item.value.typ == ValidationTokenEnum.Pipe) {
// @ts-ignore
token.typ = ValidationTokenEnum.AtRule;
children.push(item.value);
continue;
}
if (item.value.typ != ValidationTokenEnum.OpenCurlyBrace) {
if (!('prelude' in token)) {
token.prelude = [];
}
token.prelude.push(item.value);
}
let braces = 0;
let previousItem = item.value;
while ((item = iterator.next()) && !item.done) {
if (item.value.typ == ValidationTokenEnum.DeclarationNameToken) {
iterator.next();
const t = Object.defineProperty({
typ: ValidationTokenEnum.DeclarationDefinitionToken,
nam: item.value.val,
val: iterator.next().value
}, 'pos', {
...objectProperties,
value: item.value.pos
});
if (braces > 0) {
token.chi.push(t);
}
else {
token.prelude.push(t);
}
previousItem = t;
continue;
}
if (item.value.typ == ValidationTokenEnum.OpenCurlyBrace) {
previousItem = item.value;
continue;
}
if (item.value.typ == ValidationTokenEnum.Whitespace && previousItem?.typ == ValidationTokenEnum.OpenCurlyBrace) {
braces++;
if (braces == 1) {
previousItem = item.value;
continue;
}
}
if (previousItem?.typ == ValidationTokenEnum.Whitespace && item.value.typ == ValidationTokenEnum.CloseCurlyBrace) {
braces--;
if (braces == 0) {
break;
}
}
if (braces == 0) {
if (!('prelude' in token)) {
token.prelude = [];
}
token.prelude.push(item.value);
}
else {
if (!('chi' in token)) {
token.chi = [];
}
token.chi.push(item.value);
}
previousItem = item.value;
}
if ('prelude' in token) {
if (token?.prelude?.length == 1 && token.prelude[0].typ == ValidationTokenEnum.Whitespace) {
delete token.prelude;
}
else {
const t = { typ: ValidationTokenEnum.Root, chi: token.prelude };
doParseSyntax(syntax, t.chi[Symbol.iterator](), t);
token.prelude = t.chi;
minify(token.prelude);
}
}
// @ts-ignore
if (token?.chi?.length > 0) {
minify(doParseSyntax(syntax, token.chi[Symbol.iterator](), token));
}
}
else {
children.push(item.value);
}
}
return children;
}
function matchToken(syntax, iterator, validationToken) {
if (iterator.length == 0) {
return [];
}
if (Array.isArray(iterator[0])) {
const result = [];
for (let i = 0; i < iterator.length; i++) {
result.push(matchToken(syntax, iterator[i], validationToken));
}
return result;
}
let children = [];
let token = null;
let i;
if (validationToken == ValidationTokenEnum.Ampersand) {
// @ts-ignore
children = [...iterator];
let offsetL;
let offsetR;
for (i = 0; i < children.length; i++) {
if (children[i].typ == ValidationTokenEnum.Ampersand) {
offsetR = i + 1;
offsetL = i - 1;
while (offsetR < children.length - 1 && children[offsetR].typ == ValidationTokenEnum.Whitespace) {
offsetR++;
}
while (offsetR + 1 < children.length && skipped.includes(children[offsetR + 1].typ)) {
offsetR++;
}
while (offsetL > 0 && (children[offsetL].typ == ValidationTokenEnum.Whitespace || skipped.includes(children[offsetL].typ))) {
offsetL--;
}
token = Object.defineProperty({
typ: ValidationTokenEnum.AmpersandToken,
l: children.slice(offsetL, i),
r: children.slice(i + 1, offsetR + 1)
}, 'pos', { ...objectProperties, value: children[i].pos });
i = offsetL;
children.splice(offsetL, offsetR - offsetL + 1, token);
}
else if ('chi' in children[i]) {
// @ts-ignore
children[i].chi = matchToken(syntax, children[i].chi, validationToken);
}
else if ('l' in children[i]) {
// @ts-ignore
children[i].l = matchToken(syntax, children[i].l, validationToken);
// @ts-ignore
children[i].r = matchToken(syntax, children[i].r, validationToken);
}
}
return children;
}
let item;
for (i = 0; i < iterator.length; i++) {
item = iterator[i];
if (item.typ == validationToken) {
if (item.typ == ValidationTokenEnum.Pipe) {
token = Object.defineProperty({
typ: ValidationTokenEnum.PipeToken,
chi: [matchToken(syntax, children.slice(), validationToken)], //.concat(matchToken(syntaxes, children.slice()[Symbol.iterator]() as Iterator<ValidationTokenIteratorValue>, validationToken), matchToken(syntaxes, iterator, validationToken)),
// @ts-ignore
// l: matchToken(syntaxes, children.slice()[Symbol.iterator](), validationToken),
// r: matchToken(syntaxes, iterator, validationToken)
}, 'pos', {
...objectProperties,
value: item.pos
});
children.length = 0;
while ((item = iterator[++i]) != null) {
if (item.typ == ValidationTokenEnum.Pipe) {
token.chi.push(matchToken(syntax, children.slice(), validationToken));
children.length = 0;
}
else {
children.push(item);
}
}
if (children.length > 0) {
token.chi.push(matchToken(syntax, children.slice(), validationToken));
}
token.chi.sort((a, b) => {
if (a.some((t) => t.isList)) {
return -1;
}
if (b.some((t) => t.isList)) {
return 1;
}
if (a.some((t) => t.occurence != null)) {
return -1;
}
if (b.some((t) => t.occurence != null)) {
return -1;
}
if (a.some((t) => t.isRepeatableGroup)) {
return -1;
}
if (b.some((t) => t.isRepeatableGroup)) {
return 1;
}
if (a.some((t) => t.isRepeatable)) {
return -1;
}
if (b.some((t) => t.isRepeatable)) {
return 1;
}
if (a.some((t) => t.isOptional)) {
return 1;
}
if (b.some((t) => t.isOptional)) {
return -1;
}
return 0;
});
children = [token];
}
else {
token = Object.defineProperty({
typ: ValidationTokenEnum.ColumnToken,
l: matchToken(syntax, children.slice(), validationToken),
r: matchToken(syntax, iterator.slice(i + 1), validationToken)
}, 'pos', {
...objectProperties,
value: item.pos
});
i = iterator.length;
}
children.length = 0;
children.push(token);
while ((item = iterator[++i]) != null) {
children.push(item);
if ('chi' in item) {
// @ts-ignore
item.chi = matchToken(syntax, item.chi, validationToken);
}
else if ('l' in item) {
// @ts-ignore
item.l = matchToken(syntax, item.l, validationToken);
// @ts-ignore
item.r = matchToken(syntax, item.r, validationToken);
}
}
// @ts-ignore
return children;
}
else {
// @ts-ignore
children.push(item);
if ('chi' in item) {
// @ts-ignore
item.chi = matchToken(syntax, item.chi, validationToken);
}
else if ('l' in item) {
item.l = matchToken(syntax, item.l, validationToken);
// @ts-ignore
item.r = matchToken(syntax, item.r, validationToken);
}
}
}
return children;
}
function parseSyntaxTokens(syntax, iterator) {
const items = [];
let item;
let i;
while ((item = iterator.next()) && !item.done) {
if (Array.isArray(item.value)) {
// @ts-ignore
item.value = parseSyntaxTokens(syntax, item.value[Symbol.iterator]());
}
switch (item.value.typ) {
case ValidationTokenEnum.Star:
case ValidationTokenEnum.HashMark:
case ValidationTokenEnum.AtLeastOnce:
case ValidationTokenEnum.Exclamation:
case ValidationTokenEnum.QuestionMark:
case ValidationTokenEnum.OpenCurlyBrace:
i = items.length;
while (i--) {
if (items[i].typ != ValidationTokenEnum.Whitespace) {
break;
}
}
if (item.value.typ == ValidationTokenEnum.Exclamation) {
items[i].isRepeatableGroup = true;
}
else if (item.value.typ == ValidationTokenEnum.QuestionMark) {
items[i].isOptional = true;
}
else if (item.value.typ == ValidationTokenEnum.Star) {
items[i].isRepeatable = true;
}
else if (item.value.typ == ValidationTokenEnum.AtLeastOnce) {
items[i].atLeastOnce = true;
}
else if (item.value.typ == ValidationTokenEnum.HashMark) {
items[i].isList = true;
}
else if (item.value.typ == ValidationTokenEnum.OpenCurlyBrace) {
items[i].occurence = {
min: 0,
max: null
};
while ((item = iterator.next()) && !item.done) {
if (item.value.typ == ValidationTokenEnum.Number) {
// @ts-ignore
if (items[i].occurence.min == 0) {
// @ts-ignore
items[i].occurence.min = items[i].occurence.max = +item.value.val;
}
else {
// @ts-ignore
items[i].occurence.max = +item.value.val;
}
}
if (item.value.typ == ValidationTokenEnum.CloseCurlyBrace) {
break;
}
}
}
if ('occurence' in item.value) {
// @ts-ignore
items[i].occurence = { ...item.value.occurence };
}
break;
case ValidationTokenEnum.Pipe:
item.value.chi = item.value.chi.map((t) => parseSyntaxTokens(syntax, t[Symbol.iterator]()));
items.push(item.value);
break;
default:
items.push(item.value);
break;
}
}
for (i = 0; i < items.length; i++) {
if ('chi' in items[i]) {
// @ts-ignore
items[i].chi = parseSyntaxTokens(syntax, items[i].chi[Symbol.iterator]());
}
else if ('l' in items[i]) {
// @ts-ignore
items[i].l = parseSyntaxTokens(syntax, items[i].l[Symbol.iterator]());
// @ts-ignore
items[i].r = parseSyntaxTokens(syntax, items[i].r[Symbol.iterator]());
}
if (items[i].typ != ValidationTokenEnum.Bracket && (items[i].isOptional || items[i].isRepeatable)) {
let k = i;
while (--k > 0) {
if (items[k].typ == ValidationTokenEnum.Whitespace) {
continue;
}
if (items[k].typ == ValidationTokenEnum.Comma) {
const wrapper = Object.defineProperty({
typ: ValidationTokenEnum.Bracket,
chi: items.slice(k, i + 1)
}, 'pos', { ...objectProperties, value: items[k].pos });
if (items[i].isOptional) {
wrapper.isOptional = true;
delete items[i].isOptional;
}
if (items[i].isRepeatable) {
wrapper.isRepeatable = true;
delete items[i].isRepeatable;
}
items.splice(k, i - k + 1, wrapper);
i = k - 1;
}
break;
}
// }
}
}
return items;
}
function doParseSyntax(syntax, iterator, context) {
context.chi = matchParens(syntax, iterator);
// @ts-ignore
context.chi = matchAtRule(syntax, context.chi[Symbol.iterator]());
// @ts-ignore
context.chi = matchBrackets(syntax, context.chi[Symbol.iterator]());
// @ts-ignore
context.chi = matchCurlBraces(syntax, context.chi[Symbol.iterator]());
// @ts-ignore
context.chi = matchToken(syntax, context.chi, ValidationTokenEnum.Pipe);
// @ts-ignore
context.chi = matchToken(syntax, context.chi, ValidationTokenEnum.Column);
// @ts-ignore
context.chi = matchToken(syntax, context.chi, ValidationTokenEnum.Ampersand);
// @ts-ignore
context.chi = parseSyntaxTokens(syntax, context.chi[Symbol.iterator]());
return context;
}
function getTokenType(token, position, currentPosition) {
const pos = { ...position };
Object.assign(position, currentPosition);
// '∞'
if (token == '\u221e') {
return Object.defineProperty({
typ: ValidationTokenEnum.InfinityToken,
}, 'pos', { ...objectProperties, value: pos });
}
if (token.charAt(0) == '"' || token.charAt(0) == "'") {
return Object.defineProperty({
typ: ValidationTokenEnum.StringToken,
val: token
}, 'pos', { ...objectProperties, value: pos });
}
if (token == ';') {
return Object.defineProperty({
typ: ValidationTokenEnum.SemiColon
}, 'pos', { ...objectProperties, value: pos });
}
if (token.match(/^\s+$/)) {
return Object.defineProperty({
typ: ValidationTokenEnum.Whitespace,
}, 'pos', { ...objectProperties, value: pos });
}
if (token.match(/^\d+$/)) {
return Object.defineProperty({
typ: ValidationTokenEnum.Number,
val: Number(token)
}, 'pos', { ...objectProperties, value: pos });
}
if (isPseudo(token)) {
return Object.defineProperty({
typ: ValidationTokenEnum.PseudoClassToken,
val: token
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '!') {
return Object.defineProperty({
typ: ValidationTokenEnum.Exclamation
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '#') {
return Object.defineProperty({
typ: ValidationTokenEnum.HashMark
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '/') {
return Object.defineProperty({
typ: ValidationTokenEnum.Separator
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '+') {
return Object.defineProperty({
typ: ValidationTokenEnum.AtLeastOnce
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '|') {
return Object.defineProperty({
typ: ValidationTokenEnum.Pipe
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '&&') {
return Object.defineProperty({
typ: ValidationTokenEnum.Ampersand
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '||') {
return Object.defineProperty({
typ: ValidationTokenEnum.Column
}, 'pos', { ...objectProperties, value: pos });
}
if (token == ',') {
return Object.defineProperty({
typ: ValidationTokenEnum.Comma
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '[') {
return Object.defineProperty({
typ: ValidationTokenEnum.OpenBracket
}, 'pos', { ...objectProperties, value: pos });
}
if (token == ']') {
return Object.defineProperty({
typ: ValidationTokenEnum.CloseBracket
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '(') {
return Object.defineProperty({
typ: ValidationTokenEnum.OpenParenthesis
}, 'pos', { ...objectProperties, value: pos });
}
if (token == ')') {
return Object.defineProperty({
typ: ValidationTokenEnum.CloseParenthesis
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '{') {
return Object.defineProperty({
typ: ValidationTokenEnum.OpenCurlyBrace
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '}') {
return Object.defineProperty({
typ: ValidationTokenEnum.CloseCurlyBrace
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '*') {
return Object.defineProperty({
typ: ValidationTokenEnum.Star
}, 'pos', { ...objectProperties, value: pos });
}
if (token == '?') {
return Object.defineProperty({
typ: ValidationTokenEnum.QuestionMark
}, 'pos', { ...objectProperties, value: pos });
}
if (token.startsWith("<'")) {
return Object.defineProperty({
typ: ValidationTokenEnum.DeclarationType,
val: token.slice(2, -2)
}, 'pos', { ...objectProperties, value: pos });
}
if (token.startsWith('<')) {
// <number [1,1000]>
// <length [0,∞]>
let match = token.match(/<([a-z0-9-]+)(\s+\[([0-9]+[a-zA-Z]*),(([0-9]+[a-zA-Z]*)|∞)\])?>/);
if (match == null) {
let match = token.match(/<([a-zA-Z0-9-]+)\(\)>/);
if (match != null) {
return Object.defineProperty({
typ: ValidationTokenEnum.ValidationFunctionDefinition,
val: match[1]
}, 'pos', { ...objectProperties, value: pos });
}
throw new Error('invalid token at position: ' + position.lin + ':' + position.col + ' ' + token);
}
if (match[2] != null) {
const type = getTokenType$1(match[3]);
return Object.defineProperty({
typ: ValidationTokenEnum.PropertyType,
val: match[1],
unit: EnumToken[type.typ],
range: [+type.val, match[4] == '\u221e' ? null : +match[4]]
}, 'pos', { ...objectProperties, value: pos });
}
return Object.defineProperty({
typ: ValidationTokenEnum.PropertyType,
val: token.slice(1, -1)
}, 'pos', { ...objectProperties, value: pos });
}
if (token.startsWith('@') && isIdent(token.slice(1))) {
return Object.defineProperty({
typ: ValidationTokenEnum.AtRule,
val: token.slice(1)
}, 'pos', { ...objectProperties, value: pos });
}
return Object.defineProperty({
typ: ValidationTokenEnum.Keyword,
val: token
}, 'pos', { ...objectProperties, value: pos });
}
function move(position, chr) {
for (const c of chr) {
position.col++;
position.ind += c.length;
}
return position;
}
function renderSyntax(token, parent) {
let glue;
switch (token.typ) {
case ValidationTokenEnum.InfinityToken:
// '∞'
return '\u221e';
case ValidationTokenEnum.Root:
return token.chi.reduce((acc, curr) => acc + renderSyntax(curr), '');
case ValidationTokenEnum.Whitespace:
return ' ';
case ValidationTokenEnum.ValidationFunctionDefinition:
return '<' + token.val + '()>';
case ValidationTokenEnum.HashMark:
return '#';
case ValidationTokenEnum.Pipe:
return '|';
case ValidationTokenEnum.Column:
return '||';
case ValidationTokenEnum.PipeToken:
return token.chi.reduce((acc, curr) => acc + (acc.trim().length > 0 ? '|' : '') + curr.reduce((acc, curr) => acc + renderSyntax(curr), ''), '');
case ValidationTokenEnum.ColumnToken:
case ValidationTokenEnum.AmpersandToken:
glue = token.typ == ValidationTokenEnum.ColumnToken ? '||' : '&&';
return token.l.reduce((acc, curr) => acc + renderSyntax(curr), '') +
glue +
token.r.reduce((acc, curr) => acc + renderSyntax(curr), '');
case ValidationTokenEnum.Function:
case ValidationTokenEnum.PseudoClassFunctionToken:
case ValidationTokenEnum.Parens:
return token.val + '(' + token.chi.reduce((acc, curr) => acc + renderSyntax(curr), '') + ')' + renderAttributes(token);
case ValidationTokenEnum.Comma:
return ',';
case ValidationTokenEnum.Keyword:
return token.val + renderAttributes(token);
case ValidationTokenEnum.OpenBracket:
return '[';
case ValidationTokenEnum.Ampersand:
return '&&';
case ValidationTokenEnum.QuestionMark:
return '?';
case ValidationTokenEnum.Separator:
return '/';
case ValidationTokenEnum.Bracket:
return '[' + token.chi.reduce((acc, curr) => acc + renderSyntax(curr), '') + ']' + renderAttributes(token);
case ValidationTokenEnum.PropertyType:
return '<' + token.val + (Array.isArray(token.range) ? `[${token?.range?.[0]}, ${token?.range?.[1] ?? '\u221e'}]` : '') + '>' + renderAttributes(token);
case ValidationTokenEnum.DeclarationType:
return "<'" + token.val + "'>" + renderAttributes(token);
case ValidationTokenEnum.Number:
case ValidationTokenEnum.PseudoClassToken:
case ValidationTokenEnum.StringToken:
return token.val + '';
case ValidationTokenEnum.SemiColon:
return ';';
case ValidationTokenEnum.AtRule:
return '@' + token.val;
case ValidationTokenEnum.AtRuleDefinition:
return '@' + token.val +
(token.prelude == null ? '' : ' ' + token.prelude.reduce((acc, curr) => acc + renderSyntax(curr), '')) +
(token.chi == null ? '' : ' {\n' + token.chi.reduce((acc, curr) => acc + renderSyntax(curr), '')).slice(1, -1) + '\n}';
case ValidationTokenEnum.Block:
return '{' + token.chi.reduce((acc, t) => acc + renderSyntax(t), '') + '}';
case ValidationTokenEnum.DeclarationDefinitionToken:
return token.nam + ': ' + renderSyntax(token.val);
default:
throw new Error('Unhandled token: ' + JSON.stringify({ token }, null, 1));
}
}
function renderAttributes(token) {
let result = '';
if (token.isList) {
result += '#';
}
if (token.isOptional) {
result += '?';
}
if (token.isRepeatableGroup) {
result += '!';
}
if (token.isRepeatable) {
result += '*';
}
if (token.atLeastOnce) {
result += '+';
}
if (token.occurence != null) {
if (token.occurence.max == null || token.occurence.max == token.occurence.min || Number.isNaN(token.occurence.max)) {
result += '{' + token.occurence.min + '}';
}
else {
result += '{' + token.occurence.min + ',' + (Number.isFinite(token.occurence.max) ? token.occurence.max : '\u221e') + '}';
}
}
return result;
}
function minify(ast) {
if (Array.isArray(ast)) {
// @ts-ignore
while (ast.length > 0 && ast[0].typ == ValidationTokenEnum.Whitespace) {
// @ts-ignore
ast.shift();
}
while (ast.length > 0 && ast.at(-1).typ == ValidationTokenEnum.Whitespace) {
ast.pop();
}
for (let i = 0; i < ast.length; i++) {
minify(ast[i]);
if (ast[i].typ == ValidationTokenEnum.Whitespace && ast[i + 1]?.typ == ValidationTokenEnum.Whitespace) {
ast.splice(i + 1, 1);
i--;
continue;
}
if (ast[i].typ == ValidationTokenEnum.Separator) {
if (ast[i + 1]?.typ == ValidationTokenEnum.Whitespace) {
ast.splice(i + 1, 1);
}
if (ast[i - 1]?.typ == ValidationTokenEnum.Whitespace) {
ast.splice(--i, 1);
}
}
}
return ast;
}
if ('l' in ast) {
minify(ast.l);
}
if ('r' in ast) {
minify(ast.r);
}
if ('chi' in ast) {
minify(ast.chi);
}
if ('prelude' in ast) {
minify(ast.prelude);
}
return ast;
}
export { ValidationSyntaxGroupEnum, ValidationTokenEnum, parseSyntax, renderSyntax };