UNPKG

@tbela99/css-parser

Version:

CSS parser for node and the browser

1,053 lines (1,050 loc) 40.4 kB
import { ValidationTokenEnum } from './types.js'; import { isIdent, isPseudo } from '../../syntax/syntax.js'; var WalkValidationTokenEnum; (function (WalkValidationTokenEnum) { WalkValidationTokenEnum[WalkValidationTokenEnum["IgnoreChildren"] = 0] = "IgnoreChildren"; WalkValidationTokenEnum[WalkValidationTokenEnum["IgnoreNode"] = 1] = "IgnoreNode"; WalkValidationTokenEnum[WalkValidationTokenEnum["IgnoreAll"] = 2] = "IgnoreAll"; })(WalkValidationTokenEnum || (WalkValidationTokenEnum = {})); var WalkValidationTokenKeyTypeEnum; (function (WalkValidationTokenKeyTypeEnum) { WalkValidationTokenKeyTypeEnum["Array"] = "array"; WalkValidationTokenKeyTypeEnum["Children"] = "chi"; WalkValidationTokenKeyTypeEnum["Left"] = "l"; WalkValidationTokenKeyTypeEnum["Right"] = "r"; WalkValidationTokenKeyTypeEnum["Prelude"] = "prelude"; })(WalkValidationTokenKeyTypeEnum || (WalkValidationTokenKeyTypeEnum = {})); const skipped = [ ValidationTokenEnum.Star, ValidationTokenEnum.HashMark, ValidationTokenEnum.AtLeastOnce, ValidationTokenEnum.Exclamation, ValidationTokenEnum.QuestionMark, ValidationTokenEnum.OpenCurlyBrace ]; const objectProperties = { enumerable: false, writable: true, configurable: true }; // syntaxes: keyword | <'property'> | <function> // "none | [ [<dashed-ident> || <try-tactic>] | inset-area( <'inset-area'> ) ]#" // "" // : "<outline-radius>{1,4} [ / <outline-radius>{1,4} ]? // "" // false | true // [ <mask-reference> || <position> [ / <bg-size> ]? || <repeat-style> || [ <box> | border | padding | content | text ] || [ <box> | border | padding | content ] ]# // false | true | green | pipe // keyword | <'property'> | <function> // [<dashed-ident> || <try-tactic>] // [ <mask-reference> || <position> [ / <bg-size> ]? || <repeat-style> || [ <box> | border | padding | content | text ] || [ <box> | border | padding | content ] ]# // none | [ [<dashed-ident> || <try-tactic>] | inset-area( <'inset-area'> ) ] 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); 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 columnCallback(token, parent, key) { if (key == WalkValidationTokenKeyTypeEnum.Prelude) { return WalkValidationTokenEnum.IgnoreAll; } if (token.typ == ValidationTokenEnum.ColumnToken || token.typ == ValidationTokenEnum.Whitespace) { return WalkValidationTokenEnum.IgnoreNode; } return WalkValidationTokenEnum.IgnoreChildren; } function toColumnArray(ast, parent) { const result = new Map; // @ts-ignore for (const { token } of walkValidationToken(ast, null, columnCallback)) { result.set(JSON.stringify(token), token); } const node = { typ: ValidationTokenEnum.ColumnArrayToken, chi: [...result.values()] }; if (parent != null) { replaceNode(parent, ast, node); } return node; } function replaceNode(parent, target, node) { if ('l' in parent && parent.l == target) { parent.l = node; } if ('r' in parent && parent.r == target) { parent.r = node; } if ('chi' in parent) { // @ts-ignore for (let i = 0; i < parent.chi.length; i++) { // @ts-ignore if (parent.chi[i] == target) { // @ts-ignore parent.chi[i] = node; break; } } } } function transform(context) { for (const { token, parent } of walkValidationToken(context)) { switch (token.typ) { case ValidationTokenEnum.ColumnToken: toColumnArray(token, parent); break; } } return context; } function parseSyntax(syntax) { const root = Object.defineProperty({ typ: ValidationTokenEnum.Root, chi: [] }, 'pos', { ...objectProperties, value: { ind: 0, lin: 1, col: 0 } }); // return minify(doParseSyntax(syntaxes, tokenize(syntaxes), root)) as ValidationRootToken; return minify(transform(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 = 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); } 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] ?? 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; } if (item.value.typ != ValidationTokenEnum.Whitespace) { console.error('unexpected token', item.value); } 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) { let children = []; let item; 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[Symbol.iterator](), validationToken); } else if ('l' in children[i]) { // @ts-ignore children[i].l = matchToken(syntax, children[i].l[Symbol.iterator](), validationToken); // @ts-ignore children[i].r = matchToken(syntax, children[i].r[Symbol.iterator](), validationToken); } } return children; } while ((item = iterator.next()) && !item.done) { if (item.value.typ == validationToken) { if (item.value.typ == ValidationTokenEnum.Pipe) { token = Object.defineProperty({ typ: ValidationTokenEnum.PipeToken, chi: [matchToken(syntax, children.slice()[Symbol.iterator](), 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.value.pos }); children.length = 0; while ((item = iterator.next()) && !item.done) { if (item.value.typ == ValidationTokenEnum.Pipe) { token.chi.push(matchToken(syntax, children.slice()[Symbol.iterator](), validationToken)); children.length = 0; } else { children.push(item.value); } } if (children.length > 0) { token.chi.push(matchToken(syntax, children.slice()[Symbol.iterator](), 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()[Symbol.iterator](), validationToken), r: matchToken(syntax, iterator, validationToken) }, 'pos', { ...objectProperties, value: item.value.pos }); } children.length = 0; children.push(token); while ((item = iterator.next()) && !item.done) { children.push(item.value); if ('chi' in item.value) { // @ts-ignore item.value.chi = matchToken(syntax, item.value.chi[Symbol.iterator](), validationToken); } else if ('l' in item.value) { // @ts-ignore item.value.l = matchToken(syntax, item.value.l[Symbol.iterator](), validationToken); // @ts-ignore item.value.r = matchToken(syntax, item.value.r[Symbol.iterator](), validationToken); } } // @ts-ignore return children; } else { children.push(item.value); if ('chi' in item.value) { // @ts-ignore item.value.chi = matchToken(syntax, item.value.chi[Symbol.iterator](), validationToken); } else if ('l' in item.value) { // @ts-ignore item.value.l = matchToken(syntax, item.value.l[Symbol.iterator](), validationToken); // @ts-ignore item.value.r = matchToken(syntax, item.value.r[Symbol.iterator](), validationToken); } } } return children; } function parseTokens(syntax, iterator) { const items = []; let item; let i; while ((item = iterator.next()) && !item.done) { if (Array.isArray(item.value)) { // @ts-ignore item.value = parseTokens(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: 0 }; 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 = +item.value.val; } else { // @ts-ignore items[i].occurence.max = +item.value.val; } } if (item.value.typ == ValidationTokenEnum.CloseCurlyBrace) { break; } } } break; case ValidationTokenEnum.Pipe: item.value.chi = item.value.chi.map((t) => parseTokens(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 = parseTokens(syntax, items[i].chi[Symbol.iterator]()); } else if ('l' in items[i]) { // @ts-ignore items[i].l = parseTokens(syntax, items[i].l[Symbol.iterator]()); // @ts-ignore items[i].r = parseTokens(syntax, items[i].r[Symbol.iterator]()); } if (items[i].isOptional || items[i].isRepeatable) { if (i <= 1) { let k = i; while (++k < items.length) { if (items[k].typ == ValidationTokenEnum.Whitespace) { continue; } if (items[k].typ == ValidationTokenEnum.Comma) { items.splice(i, k - i + 1, Object.defineProperty({ typ: ValidationTokenEnum.Bracket, chi: items.slice(i, k + 1) }, 'pos', { ...objectProperties, value: items[i].pos })); } break; } } else { let k = i; while (--k > 0) { if (items[k].typ == ValidationTokenEnum.Whitespace) { continue; } if (items[k].typ == ValidationTokenEnum.Comma) { items.splice(k, i - k + 1, Object.defineProperty({ typ: ValidationTokenEnum.Bracket, chi: items.slice(k, i + 1) }, 'pos', { ...objectProperties, value: items[k].pos })); 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[Symbol.iterator](), ValidationTokenEnum.Column); // @ts-ignore context.chi = matchToken(syntax, context.chi[Symbol.iterator](), ValidationTokenEnum.Pipe); // @ts-ignore context.chi = parseTokens(syntax, context.chi[Symbol.iterator]()); // @ts-ignore context.chi = matchToken(syntax, context.chi[Symbol.iterator](), ValidationTokenEnum.Ampersand); return context; } function getTokenType(token, position, currentPosition) { const pos = { ...position }; Object.assign(position, currentPosition); 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]+),(([0-9]+)|∞)\])?>/); 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) { return Object.defineProperty({ typ: ValidationTokenEnum.PropertyType, val: match[1], range: [+match[3], match[4] == '∞' ? Infinity : +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 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++) { // if ([ValidationTokenEnum.ColumnToken, ValidationTokenEnum.PipeToken, ValidationTokenEnum.AmpersandToken].includes(ast[i].typ)) { // for (const j of (ast[i] as ValidationPipeToken | ValidationColumnToken | ValidationAmpersandToken).l) { minify(ast[i]); // } // for (const j of (ast[i] as ValidationPipeToken | ValidationColumnToken | ValidationAmpersandToken).r) { // // minify(j); // } // } else // if ('chi' in ast[i]) { // // minify((ast[i] as ValidationFunctionToken | ValidationBracketToken).chi as ValidationToken[]); // // } else 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; } // else { // if ([ValidationTokenEnum.ColumnToken, ValidationTokenEnum.PipeToken, ValidationTokenEnum.AmpersandToken].includes(ast.typ)) { // for (const j of (ast as ValidationPipeToken | ValidationColumnToken | ValidationAmpersandToken).l) { if ('l' in ast) { minify(ast.l); } // } // for (const j of (ast as ValidationPipeToken | ValidationColumnToken | ValidationAmpersandToken).r) { if ('r' in ast) { minify(ast.r); } if ('chi' in ast) { minify(ast.chi); } if ('prelude' in ast) { minify(ast.prelude); } return ast; } function* walkValidationToken(token, parent, callback, key) { if (Array.isArray(token)) { for (const child of token) { yield* walkValidationToken(child, parent, callback, WalkValidationTokenKeyTypeEnum.Array); } return; } let action = null; if (callback != null) { // @ts-ignore action = callback(token, parent, key); } if (action != WalkValidationTokenEnum.IgnoreNode && action != WalkValidationTokenEnum.IgnoreAll) { yield { token, parent }; } if (action != WalkValidationTokenEnum.IgnoreChildren && action != WalkValidationTokenEnum.IgnoreAll && 'prelude' in token) { for (const child of token.prelude) { yield* walkValidationToken(child, token, callback, WalkValidationTokenKeyTypeEnum.Prelude); } } if (action != WalkValidationTokenEnum.IgnoreChildren && 'chi' in token) { // @ts-ignore for (const child of token.chi) { yield* walkValidationToken(child, token, callback, WalkValidationTokenKeyTypeEnum.Children); } } if (action != WalkValidationTokenEnum.IgnoreChildren && action != WalkValidationTokenEnum.IgnoreAll && 'l' in token) { // @ts-ignore for (const child of token.l) { yield* walkValidationToken(child, token, callback, WalkValidationTokenKeyTypeEnum.Left); } } if (action != WalkValidationTokenEnum.IgnoreChildren && action != WalkValidationTokenEnum.IgnoreAll && 'r' in token) { // @ts-ignore for (const child of token.r) { yield* walkValidationToken(child, token, callback, WalkValidationTokenKeyTypeEnum.Right); } } } export { WalkValidationTokenEnum, WalkValidationTokenKeyTypeEnum, parseSyntax, walkValidationToken };