diff --git a/.github/workflows/jsr.yml b/.github/workflows/jsr.yml index 6fd7c3cf..bbcbe825 100644 --- a/.github/workflows/jsr.yml +++ b/.github/workflows/jsr.yml @@ -14,5 +14,7 @@ jobs: contents: read id-token: write # The OIDC ID token is used for authentication with JSR. steps: - - uses: actions/checkout@v7 - - run: npx jsr publish \ No newline at end of file + - uses: actions/checkout@v6 + - run: npx jsr publish + + diff --git a/CHANGELOG.md b/CHANGELOG.md index c596644b..189a8514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## v1.4.7 + +### Improvements + +- [x] Added support for parsing wrapped function arguments as defined in the CSS Values & Units Level 5 specification for component functions. + + ```css + font-family: random-item(--x,{Times,serif},{Arial,sans-serif},{Courier,monospace}) + ``` +### Fixes + +- [x] Fixed a performance regression, resulting in approximately ***25%*** performance gain. +- [x] Fixed a `conic-gradient()` minification bug. +- [x] Fixed missing characters by the streaming tokenizer +- [x] `walk()` with reverse parameter enabled was reversing the node's children +- [x] merging nodes could introduce a null reference + ## v1.4.6 - [x] Fix if syntax expansion regression bug #134 diff --git a/README.md b/README.md index 6e9655d0..047e7491 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Try it [online](https://tbela99.github.io/css-parser/playground/) - [CSS Modules](https://tbela99.github.io/css-parser/docs/documents/Guide.CSS_Modules.html) - [Minification](https://tbela99.github.io/css-parser/docs/documents/Guide.Minification.html) - [Custom Transform](https://tbela99.github.io/css-parser/docs/documents/Guide.Custom_Transform.html) +- [Syntax Lowering](https://tbela99.github.io/css-parser/docs/documents/Guide.Syntax_Lowering.html) - [Ast Manipulation](https://tbela99.github.io/css-parser/docs/documents/Guide.Ast_Manipulation.html) - [Utility Functions](https://tbela99.github.io/css-parser/docs/documents/Guide.Utility_Functions.html) - diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 9d03e05e..bc902180 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -574,8 +574,8 @@ EnumToken[EnumToken["RawNodeTokenType"] = 117] = "RawNodeTokenType"; /** * media query boolean token type - * @media not () - * @media only () + * at-rule media not () + * at-rule media only () */ EnumToken[EnumToken["MediaQueryUnaryFeatureTokenType"] = 118] = "MediaQueryUnaryFeatureTokenType"; /** @@ -662,6 +662,10 @@ * if-Else condition token */ EnumToken[EnumToken["IfElseConditionTokenType"] = 139] = "IfElseConditionTokenType"; + /** + * wrapped values token type like {Arial, sans-serif} + */ + EnumToken[EnumToken["WrappedValuesTokenType"] = 140] = "WrappedValuesTokenType"; /* aliases */ /** * alias for time token type @@ -1032,360 +1036,6 @@ return val; } - /** - * Options for the walk function - */ - exports.WalkerOptionEnum = void 0; - (function (WalkerOptionEnum) { - /** - * ignore the current node and its children - */ - WalkerOptionEnum[WalkerOptionEnum["Ignore"] = 1] = "Ignore"; - /** - * stop walking the tree - */ - WalkerOptionEnum[WalkerOptionEnum["Stop"] = 2] = "Stop"; - /** - * ignore the current node and process its children - */ - WalkerOptionEnum[WalkerOptionEnum["Children"] = 4] = "Children"; - /** - * ignore the current node children - */ - WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 8] = "IgnoreChildren"; - })(exports.WalkerOptionEnum || (exports.WalkerOptionEnum = {})); - /** - * Event types for the walkValues function - */ - exports.WalkerEvent = void 0; - (function (WalkerEvent) { - /** - * enter node - */ - WalkerEvent[WalkerEvent["Enter"] = 1] = "Enter"; - /** - * leave node - */ - WalkerEvent[WalkerEvent["Leave"] = 2] = "Leave"; - })(exports.WalkerEvent || (exports.WalkerEvent = {})); - /** - * Walk ast nodes - * @param node initial node - * @param filter control the walk process - * @param reverse walk in reverse order - * - * ```ts - * - * import {walk} from '@tbela99/css-parser'; - * - * const css = ` - * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } - * - * html, - * body { - * line-height: 1.474; - * } - * - * .ruler { - * - * height: 10px; - * } - * `; - * - * for (const {node, parent, root} of walk(ast)) { - * - * // do something with node - * } - * ``` - * - * Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}. - * - * ```ts - * import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser'; - * - * const css = ` - * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } - * - * html, - * body { - * line-height: 1.474; - * } - * - * .ruler { - * - * height: 10px; - * } - * `; - * - * function filter(node) { - * - * if (node.typ == EnumToken.AstRule && node.sel.includes('html')) { - * - * // skip the children of the current node - * return WalkerOptionEnum.IgnoreChildren; - * } - * } - * - * const result = await transform(css); - * for (const {node} of walk(result.ast, filter)) { - * - * console.error([EnumToken[node.typ]]); - * } - * - * // [ "StyleSheetNodeType" ] - * // [ "RuleNodeType" ] - * // [ "DeclarationNodeType" ] - * // [ "RuleNodeType" ] - * // [ "DeclarationNodeType" ] - * // [ "RuleNodeType" ] - * // [ "DeclarationNodeType" ] - * ``` - */ - function* walk(node, filter, reverse) { - const parents = [node]; - const root = node; - const map = new Map(); - let isNumeric = false; - let i = 0; - while ((node = parents[i++])) { - let option = null; - if (filter != null) { - option = filter(node); - isNumeric = typeof option == "number"; - if (isNumeric) { - if (option & exports.WalkerOptionEnum.Ignore) { - continue; - } - if (option & exports.WalkerOptionEnum.Stop) { - break; - } - } - } - if (!isNumeric || (option & exports.WalkerOptionEnum.Children) === 0) { - // @ts-ignore - yield { - node, - parent: map.get(node), - root, - // @ts-expect-error - parents: function* () { - let parent = map.get(node); - while (parent != null) { - yield parent; - parent = map.get(parent); - } - }, - }; - } - if ("chi" in node && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { - parents.splice(i, 0, ...node.chi[reverse ? "reverse" : "slice"]()); - for (const child of node.chi) { - map.set(child, node); - } - } - } - } - /** - * Walk ast node value tokens - * @param values - * @param root - * @param filter - * @param reverse - * - * Example: - * - * ```ts - * - * import {AstDeclaration, EnumToken, transform, walkValues} from '@tbela99/css-parser'; - * - * const css = ` - * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } - * `; - * - * const result = await transform(css); - * const declaration = result.ast.chi[0].chi[0] as AstDeclaration; - * - * // walk the node attribute's tokens in reverse order - * for (const {value} of walkValues(declaration.val, null, null,true)) { - * - * console.error([EnumToken[value.typ], value.val]); - * } - * - * // [ "Color", "color" ] - * // [ "FunctionTokenType", "calc" ] - * // [ "Number", 0.15 ] - * // [ "Add", undefined ] - * // [ "Iden", "b" ] - * // [ "Whitespace", undefined ] - * // [ "FunctionTokenType", "calc" ] - * // [ "Number", 0.24 ] - * // [ "Add", undefined ] - * // [ "Iden", "g" ] - * // [ "Whitespace", undefined ] - * // [ "Iden", "r" ] - * // [ "Whitespace", undefined ] - * // [ "Iden", "display-p3" ] - * // [ "Whitespace", undefined ] - * // [ "FunctionTokenType", "var" ] - * // [ "DashedIden", "--base-color" ] - * // [ "Whitespace", undefined ] - * // [ "Iden", "from" ] - * ``` - */ - function* walkValues(values, root = null, filter, reverse) { - const stack = values.slice(); - const map = new Map(); - const used = new Set(); - let previous = null; - if (filter != null && typeof filter == "function") { - filter = { - event: exports.WalkerEvent.Enter, - fn: filter, - }; - } - else if (filter == null) { - filter = { - event: exports.WalkerEvent.Enter, - }; - } - let isNumeric = false; - let value; - let option; - let node; - // const parents: Token[] = []; - const eventType = filter.event ?? exports.WalkerEvent.Enter; - while (stack.length > 0) { - value = reverse ? stack.pop() : stack.shift(); - option = null; - node = map.get(value) ?? null; - if (used.has(value)) { - continue; - } - used.add(value); - // parents.length = 0; - // while (node != null) { - // parents.push(node); - // node = map.get(node) ?? null; - // } - if (filter.fn != null && eventType & exports.WalkerEvent.Enter) { - const isValid = filter.type == null || - value.typ == filter.type || - (Array.isArray(filter.type) && filter.type.includes(value.typ)) || - (typeof filter.type == "function" && filter.type(value)); - if (isValid) { - option = filter.fn(value, map.get(value) ?? root, exports.WalkerEvent.Enter, - // @ts-expect-error - function* () { - // @ts-expect-error - let parent = map.get(node); - while (parent != null) { - yield parent; - parent = map.get(parent); - } - }); - isNumeric = typeof option == "number"; - if (isNumeric && option & exports.WalkerOptionEnum.Stop) { - return; - } - if (isNumeric && option & exports.WalkerOptionEnum.Ignore) { - continue; - } - // @ts-ignore - if (option != null && typeof option == "object" && ("typ" in option || Array.isArray(option))) { - const op = Array.isArray(option) ? option : [option]; - for (const o of op) { - map.set(o, map.get(value) ?? root); - } - stack[reverse ? "push" : "unshift"](...op); - } - } - } - yield { - value, - parent: map.get(value) ?? root, - previousValue: previous, - nextValue: stack[0] ?? null, - // @ts-ignore - root: root ?? null, - // @ts-expect-error - parents: function* () { - // @ts-expect-error - let result = map.get(node) ?? root; - let next; - do { - yield result; - next = map.get(result) ?? root; - if (next == result) { - break; - } - result = next; - } while (result != null); - }, - }; - if (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0) { - if ("chi" in value) { - const sliced = value.chi.slice(); - for (const child of sliced) { - map.set(child, value); - } - stack[reverse ? "push" : "unshift"](...sliced); - } - else { - const values = []; - if ("l" in value && value.l != null) { - // @ts-ignore - values.push(value.l); - // @ts-ignore - map.set(value.l, value); - } - if ("op" in value && typeof value.op == "object") { - // @ts-ignore - values.push(value.op); - // @ts-ignore - map.set(value.op, value); - } - if ("r" in value && value.r != null) { - if (Array.isArray(value.r)) { - for (const r of value.r) { - // @ts-ignore - values.push(r); - // @ts-ignore - map.set(r, value); - } - } - else { - // @ts-ignore - values.push(value.r); - // @ts-ignore - map.set(value.r, value); - } - } - if (values.length > 0) { - stack[reverse ? "push" : "unshift"](...values); - } - } - } - if (eventType & exports.WalkerEvent.Leave && filter.fn != null) { - const isValid = filter.type == null || - value.typ == filter.type || - (Array.isArray(filter.type) && filter.type.includes(value.typ)) || - (typeof filter.type == "function" && filter.type(value)); - if (isValid) { - option = filter.fn(value, map.get(value), exports.WalkerEvent.Leave); - // @ts-ignore - if (option != null && ("typ" in option || Array.isArray(option))) { - const op = Array.isArray(option) ? option : [option]; - for (const o of op) { - map.set(o, map.get(value) ?? root); - } - stack[reverse ? "push" : "unshift"](...op); - } - } - } - previous = value; - } - } - var declarations = { "-ms-accelerator": { syntax: "false | true" @@ -6721,6 +6371,15 @@ mediaFeatures: mediaFeatures }; + const LOC = Symbol.for("loc"); + const RAW = Symbol.for("raw"); + const STATE = Symbol.for("state"); + const ROOT = Symbol.for("root"); + const ERRORS = Symbol.for("errors"); + const TOKENS = Symbol.for("tokens"); + const PARENT = Symbol.for("parent"); + const OPTIMIZED = Symbol.for("optimized"); + const PROPERTYNAME = Symbol.for("propertyName"); const regMatchLinearGradient = /^-((webkit)|o|moz)(-repeating)?-linear-gradient$/i; const regMatchRadialGradient = /^-((webkit)|o|moz)(-repeating)?-radial-gradient$/i; const mFLT = new Set([exports.EnumToken.LtTokenType, exports.EnumToken.LteTokenType]); @@ -6790,6 +6449,17 @@ b: [0, 0.4], }, }; + // https://www.w3.org/TR/css-values-4/#math-function + const pseudoElements = [ + ":before", + ":after", + ":first-line", + ":first-letter", + "::before", + "::after", + "::first-line", + "::first-letter", + ]; const wildCardFuncs = ["var", "env", "if"]; const mathFuncs = [ "minmax", @@ -6857,7 +6527,7 @@ const urlFunc = ["url"]; const timelineFunc = ["view", "scroll"]; const gridTemplateFunc = ["minmax", "fit-content", "repeat"]; - const generalEnclosedFunc = []; + // export const generalEnclosedFunc: string[] = []; const supportFunc = ["selector", "font-tech", "font-format", "at-rule", "named-feature"]; const whenElseFunc = ["media", "supports"]; const containerFunc = ["style", "scroll-state"]; @@ -7178,7 +6848,6 @@ exports.EnumToken.SupportsQueryConditionTokenType, exports.EnumToken.SupportsQueryUnaryConditionTokenType, ]); - const definedPropertySettings = { configurable: true, enumerable: false, writable: true }; const combinators = ["+", ">", "~", "||", "|"]; function dasherize(value) { @@ -7205,14 +6874,13 @@ } function getColorComponents(token) { - if (token.typ === exports.EnumToken.IdenTokenType) { - if (isColor(token)) { - parseColor(token); - } - else { - return null; - } - } + // if (token.typ === EnumToken.IdenTokenType) { + // if (isColor(token)) { + // parseColor(token); + // } else { + // return null; + // } + // } if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { if (equalsIgnoreCase('currentcolor', token.val)) { return null; @@ -7236,23 +6904,25 @@ ].includes(child.typ)) { continue; } - if (child.typ === exports.EnumToken.IdenTokenType && isColor(child)) { - parseColor(child); - } + // if (child.typ === EnumToken.IdenTokenType && isColor(child)) { + // parseColor(child); + // } if (child.typ === exports.EnumToken.FunctionTokenType || child.typ === exports.EnumToken.WildCardFunctionTokenType || child.typ === exports.EnumToken.MathFunctionTokenType) { if ("var" == child.val.toLowerCase()) { return null; } - else { - for (const { value } of walkValues(child.chi)) { - if (value.typ == exports.EnumToken.WildCardFunctionTokenDefType && - "var" === value.val.toLowerCase()) { - return null; - } - } - } + // else { + // for (const { value } of walkValues((child as FunctionToken).chi)) { + // if ( + // value.typ == EnumToken.WildCardFunctionTokenDefType && + // "var" === (value as FunctionToken).val.toLowerCase() + // ) { + // return null; + // } + // } + // } } if (child.typ == exports.EnumToken.ColorTokenType && equalsIgnoreCase("currentcolor", child.val)) { return null; @@ -7270,27 +6940,28 @@ */ // A is m x n. B is n x p. product is m x p. function multiplyMatrices(A, B) { - let m = A.length; - if (!Array.isArray(A[0])) { - // A is vector, convert to [[a, b, c, ...]] - A = [A]; - } + // if (!Array.isArray(A[0])) { + // // A is vector, convert to [[a, b, c, ...]] + // A = [A]; + // } if (!Array.isArray(B[0])) { // B is vector, convert to [[a], [b], [c], ...]] B = B.map((x) => [x]); } let p = B[0].length; let B_cols = B[0].map((_, i) => B.map((x) => x[i])); // transpose B + // @ts-expect-error let product = A.map((row) => B_cols.map((col) => { - if (!Array.isArray(row)) { - return col.reduce((a, c) => a + c * row, 0); - } + // if (!Array.isArray(row)) { + // return col.reduce((a: number, c: number) => a + c * row, 0); + // } return row.reduce((a, c, i) => a + c * (col[i] || 0), 0); })); - if (m === 1) { - product = product[0]; // Avoid [[a, b, c, ...]] - } + // if (m === 1) { + // product = product[0]; // Avoid [[a, b, c, ...]] + // } if (p === 1) { + // @ts-expect-error return product.map((x) => x[0]); // Avoid [[a], [b], [c], ...]] } return product; @@ -9735,2722 +9406,2253 @@ return null; } - function gcd(x, y) { - x = Math.abs(x); - y = Math.abs(y); - if (x == y) { - return x; - } - let t; - if (y > x) { - [x, y] = [y, x]; - } - while (y) { - t = y; - y = x % y; - x = t; - } - return x; - } - function compute$1(a, b, op) { - if (typeof a == "number" && typeof b == "number") { - switch (op) { - case exports.EnumToken.Add: - return a + b; - case exports.EnumToken.Sub: - return a - b; - case exports.EnumToken.Mul: - return a * b; - case exports.EnumToken.Div: - const r = simplify(a, b); - if (r[1] == 1) { - return r[0]; - } - const result = a / b; - const r2 = minifyNumber(r[0]) + "/" + minifyNumber(r[1]); - return minifyNumber(result).length < r2.length - ? result - : { - typ: exports.EnumToken.FractionTokenType, - l: { typ: exports.EnumToken.NumberTokenType, val: r[0] }, - r: { typ: exports.EnumToken.NumberTokenType, val: r[1] }, - }; - } - } - let l1 = typeof a == "number" - ? { - l: { val: a }, - r: { val: 1 }, - } - : a; - let r1 = typeof b == "number" - ? { - l: { val: b }, - r: { val: 1 }, - } - : b; - let l2; - let r2; - switch (op) { - case exports.EnumToken.Add: - // @ts-ignore - l2 = l1.l.val * r1.r.val + l1.r.val * r1.l.val; - // @ts-ignore - r2 = l1.r.val * r1.r.val; - break; - case exports.EnumToken.Sub: - // @ts-ignore - l2 = l1.l.val * r1.r.val - l1.r.val * r1.l.val; - // @ts-ignore - r2 = l1.r.val * r1.r.val; - break; - case exports.EnumToken.Mul: - // @ts-ignore - l2 = l1.l.val * r1.l.val; - // @ts-ignore - r2 = l1.r.val * r1.r.val; - break; - case exports.EnumToken.Div: - // @ts-ignore - l2 = l1.l.val * r1.r.val; - // @ts-ignore - r2 = l1.r.val * r1.l.val; - break; - } - // @ts-ignore - const a2 = simplify(l2, r2); - if (a2[1] == 1) { - return a2[0]; - } - const result = a2[0] / a2[1]; - return minifyNumber(result).length <= minifyNumber(a2[0]).length + 1 + minifyNumber(a2[1]).length - ? result - : { - typ: exports.EnumToken.FractionTokenType, - l: { typ: exports.EnumToken.NumberTokenType, val: a2[0] }, - r: { typ: exports.EnumToken.NumberTokenType, val: a2[1] }, - }; - } - function rem(...a) { - if (a.some((i) => !Number.isInteger(i))) { - return a.reduce((a, b) => Math.max(a, String(b).split(".")[1]?.length ?? 0), 0); - } - return 0; - } - function simplify(a, b) { - const g = gcd(a, b); - return g > 1 ? [a / g, b / g] : [a, b]; - } - /** - * evaluate an array of tokens - * @param tokens + * Options for the walk function */ - function evaluate(tokens) { - let nodes; - if (tokens.length == 1 && - (tokens[0].typ == exports.EnumToken.MathFunctionTokenType || tokens[0].typ == exports.EnumToken.FunctionTokenType) && - mathFuncs.includes(tokens[0].val)) { - const chi = tokens[0].chi.reduce((acc, t) => { - if (acc.length == 0 || t.typ == exports.EnumToken.CommaTokenType) { - acc.push([]); - } - if ([exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommaTokenType, exports.EnumToken.CommaTokenType].includes(t.typ)) { - return acc; - } - acc.at(-1).push(t); - return acc; - }, []); - for (let i = 0; i < chi.length; i++) { - chi[i] = evaluate(chi[i]); - } - tokens[0].chi = chi.reduce((acc, t) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); - } - acc.push(...t); - return acc; - }); - const result = evaluateFunc(tokens[0]); - if (result == null) { - return tokens; - } - if (result[0].typ === exports.EnumToken.MathFunctionTokenType && - result[0].val === "calc" && - result[0].chi.length === 1) { - return result[0].chi.slice(); - } - return result; - } - nodes = inlineExpression$1(evaluateExpression(buildExpression(tokens))); - if (nodes.length <= 1) { - if (nodes.length == 1) { - if (nodes[0].typ == exports.EnumToken.BinaryExpressionTokenType) { - return inlineExpression$1(nodes[0]); + exports.WalkerOptionEnum = void 0; + (function (WalkerOptionEnum) { + /** + * ignore the current node and its children + */ + WalkerOptionEnum[WalkerOptionEnum["Ignore"] = 1] = "Ignore"; + /** + * stop walking the tree + */ + WalkerOptionEnum[WalkerOptionEnum["Stop"] = 2] = "Stop"; + /** + * ignore the current node and process its children + */ + WalkerOptionEnum[WalkerOptionEnum["Children"] = 4] = "Children"; + /** + * ignore the current node children + */ + WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 8] = "IgnoreChildren"; + })(exports.WalkerOptionEnum || (exports.WalkerOptionEnum = {})); + /** + * Event types for the walkValues function + */ + exports.WalkerEvent = void 0; + (function (WalkerEvent) { + /** + * enter node + */ + WalkerEvent[WalkerEvent["Enter"] = 1] = "Enter"; + /** + * leave node + */ + WalkerEvent[WalkerEvent["Leave"] = 2] = "Leave"; + })(exports.WalkerEvent || (exports.WalkerEvent = {})); + /** + * Walk ast nodes + * @param node initial node + * @param filter control the walk process + * @param reverse walk in reverse order + * + * ```ts + * + * import {walk} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * for (const {node, parent, root} of walk(ast)) { + * + * // do something with node + * } + * ``` + * + * Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}. + * + * ```ts + * import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * function filter(node) { + * + * if (node.typ == EnumToken.AstRule && node.sel.includes('html')) { + * + * // skip the children of the current node + * return WalkerOptionEnum.IgnoreChildren; + * } + * } + * + * const result = await transform(css); + * for (const {node} of walk(result.ast, filter)) { + * + * console.error([EnumToken[node.typ]]); + * } + * + * // [ "StyleSheetNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * ``` + */ + function* walk(node, filter, reverse) { + const parents = [node]; + const root = node; + const map = new Map(); + let isNumeric = false; + let i = 0; + while ((node = parents[i++])) { + let option = null; + if (filter != null) { + option = filter(node); + isNumeric = typeof option == "number"; + if (isNumeric) { + if (option & exports.WalkerOptionEnum.Ignore) { + continue; + } + if (option & exports.WalkerOptionEnum.Stop) { + break; + } } + } + if (!isNumeric || (option & exports.WalkerOptionEnum.Children) === 0) { // @ts-ignore - if (nodes[0].typ == exports.EnumToken.IdenTokenType && + yield { + node, + parent: map.get(node), + root, // @ts-expect-error - typeof Math[nodes[0].val.toUpperCase()] == "number") { - return [ - { - ...nodes[0], - // @ts-ignore - val: Math[nodes[0].val.toUpperCase()], - typ: exports.EnumToken.NumberTokenType, - }, - ]; - } - } - return nodes; - } - const map = new Map(); - let token; - let i; - for (i = 0; i < nodes.length; i++) { - token = nodes[i]; - if (token.typ == exports.EnumToken.Add || token.typ == exports.EnumToken.Plus) { - continue; + parents: function* () { + let parent = map.get(node); + while (parent != null) { + yield parent; + parent = map.get(parent); + } + }, + }; } - if (token.typ == exports.EnumToken.Sub) { - if (!isScalarToken(nodes[i + 1])) { - token = { typ: exports.EnumToken.ListToken, chi: [nodes[i], nodes[i + 1]] }; - } - else { - token = doEvaluate(nodes[i + 1], { typ: exports.EnumToken.NumberTokenType, val: -1 }, exports.EnumToken.Mul); + if ("chi" in node && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + parents.splice(i, 0, ...node.chi[reverse ? "toReversed" : "slice"]()); + for (const child of node.chi) { + map.set(child, node); } - i++; - } - if (!map.has(token.typ)) { - map.set(token.typ, [token]); - } - else { - map.get(token.typ).push(token); } } - return [...map].reduce((acc, curr) => { - const token = curr[1].reduce((acc, curr) => doEvaluate(acc, curr, exports.EnumToken.Add)); - if (token.typ != exports.EnumToken.BinaryExpressionTokenType) { - if ("val" in token && +token.val < 0) { - acc.push({ typ: exports.EnumToken.Sub }, { ...token, val: -token.val }); - return acc; - } - } - if (acc.length > 0 && curr[0] != exports.EnumToken.ListToken) { - acc.push({ typ: exports.EnumToken.Add }); - } - acc.push(token); - return acc; - }, []); } /** - * evaluate arithmetic operation - * @param l - * @param r - * @param op + * Walk ast node value tokens + * @param values + * @param root + * @param filter + * @param reverse + * + * Example: + * + * ```ts + * + * import {AstDeclaration, EnumToken, transform, walkValues} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * `; + * + * const result = await transform(css); + * const declaration = result.ast.chi[0].chi[0] as AstDeclaration; + * + * // walk the node attribute's tokens in reverse order + * for (const {value} of walkValues(declaration.val, null, null,true)) { + * + * console.error([EnumToken[value.typ], value.val]); + * } + * + * // [ "Color", "color" ] + * // [ "FunctionTokenType", "calc" ] + * // [ "Number", 0.15 ] + * // [ "Add", undefined ] + * // [ "Iden", "b" ] + * // [ "Whitespace", undefined ] + * // [ "FunctionTokenType", "calc" ] + * // [ "Number", 0.24 ] + * // [ "Add", undefined ] + * // [ "Iden", "g" ] + * // [ "Whitespace", undefined ] + * // [ "Iden", "r" ] + * // [ "Whitespace", undefined ] + * // [ "Iden", "display-p3" ] + * // [ "Whitespace", undefined ] + * // [ "FunctionTokenType", "var" ] + * // [ "DashedIden", "--base-color" ] + * // [ "Whitespace", undefined ] + * // [ "Iden", "from" ] + * ``` */ - function doEvaluate(l, r, op) { - const defaultReturn = { - typ: exports.EnumToken.BinaryExpressionTokenType, - op, - l, - r, - }; - if (!isScalarToken(l) || !isScalarToken(r) || (l.typ == r.typ && "unit" in l && "unit" in r && l.unit != r.unit)) { - return defaultReturn; + function* walkValues(values, root = null, filter, reverse) { + const stack = values.slice(); + const map = new Map(); + const used = new Set(); + let previous = null; + if (filter != null && typeof filter == "function") { + filter = { + event: exports.WalkerEvent.Enter, + fn: filter, + }; } - if (r.typ == exports.EnumToken.FunctionTokenType || r.typ == exports.EnumToken.MathFunctionTokenType) { - const val = evaluateFunc(r); - if (val == null) { - return defaultReturn; - } - if (val.length == 1) { - r = val[0]; - } + else if (filter == null) { + filter = { + event: exports.WalkerEvent.Enter, + }; } - if (op == exports.EnumToken.Add || op == exports.EnumToken.Plus || op == exports.EnumToken.Sub) { - // @ts-ignore - if (l.typ != r.typ) { - return defaultReturn; + let isNumeric = false; + let value; + let option; + let node; + // const parents: Token[] = []; + const eventType = filter.event ?? exports.WalkerEvent.Enter; + while (stack.length > 0) { + value = reverse ? stack.pop() : stack.shift(); + option = null; + node = map.get(value) ?? null; + if (used.has(value)) { + continue; } - } - let typ = l.typ == exports.EnumToken.NumberTokenType || l.typ === exports.EnumToken.IdenTokenType - ? r.typ - : r.typ == exports.EnumToken.NumberTokenType - ? l.typ - : l.typ == exports.EnumToken.PercentageTokenType - ? r.typ - : l.typ; - // @ts-expect-error - let v1 = l.val?.typ == exports.EnumToken.FractionTokenType ? l.val : getValue$1(l); - let v2 = r.val?.typ == exports.EnumToken.FractionTokenType - ? // @ts-expect-error - r.val - : getValue$1(r); - if (op == exports.EnumToken.Mul) { - if (l.typ != exports.EnumToken.NumberTokenType && r.typ != exports.EnumToken.NumberTokenType) { - if (typeof v1 == "number" && l.typ == exports.EnumToken.PercentageTokenType) { - v1 = { - typ: exports.EnumToken.FractionTokenType, - l: { typ: exports.EnumToken.NumberTokenType, val: v1 }, - r: { typ: exports.EnumToken.NumberTokenType, val: 100 }, - }; - } - else if (typeof v2 == "number" && r.typ == exports.EnumToken.PercentageTokenType) { - v2 = { - typ: exports.EnumToken.FractionTokenType, - l: { typ: exports.EnumToken.NumberTokenType, val: v2 }, - r: { typ: exports.EnumToken.NumberTokenType, val: 100 }, - }; + used.add(value); + // parents.length = 0; + // while (node != null) { + // parents.push(node); + // node = map.get(node) ?? null; + // } + if (filter.fn != null && eventType & exports.WalkerEvent.Enter) { + const isValid = filter.type == null || + value.typ == filter.type || + (Array.isArray(filter.type) && filter.type.includes(value.typ)) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + option = filter.fn(value, map.get(value) ?? root, exports.WalkerEvent.Enter, + // @ts-expect-error + function* () { + // @ts-expect-error + let parent = map.get(node); + while (parent != null) { + yield parent; + parent = map.get(parent); + } + }); + isNumeric = typeof option == "number"; + if (isNumeric && option & exports.WalkerOptionEnum.Stop) { + return; + } + if (isNumeric && option & exports.WalkerOptionEnum.Ignore) { + continue; + } + // @ts-ignore + if (option != null && typeof option == "object" && ("typ" in option || Array.isArray(option))) { + const op = Array.isArray(option) ? option : [option]; + for (const o of op) { + map.set(o, map.get(value) ?? root); + } + stack[reverse ? "push" : "unshift"](...op); + } } } - } - // @ts-ignore - const val = compute$1(v1, v2, op); - const token = { - ...(l.typ === exports.EnumToken.NumberTokenType || l.typ === exports.EnumToken.IdenTokenType ? r : l), - typ, - val /* : typeof val == 'number' ? minifyNumber(val) : val */, - }; - if (token.typ == exports.EnumToken.IdenTokenType) { - // @ts-ignore - token.typ = exports.EnumToken.NumberTokenType; - } - return token; - } - function getValue$1(t) { - if (t.typ == exports.EnumToken.IdenTokenType) { - // @ts-ignore - return Math[t.val.toUpperCase()]; - } - // @ts-ignore - return t.typ == exports.EnumToken.FractionTokenType ? t.l.val / t.r.val : +t.val; - } - function evaluateFunc(token) { - const values = token.chi.slice(); - switch (token.val) { - case "abs": - case "sin": - case "cos": - case "tan": - case "asin": - case "acos": - case "atan": - case "sign": - case "sqrt": - case "exp": { - const value = evaluate(values); + yield { + value, + parent: map.get(value) ?? root, + previousValue: previous, + nextValue: stack[0] ?? null, // @ts-ignore - let val = value[0].typ == exports.EnumToken.NumberTokenType - ? +value[0].val - : // @ts-expect-error - value[0].l.val / value[0].r.val; - return [ - { - typ: exports.EnumToken.NumberTokenType, - val: Math[token.val](val), - }, - ]; + root: root ?? null, + // @ts-expect-error + parents: function* () { + // @ts-expect-error + let result = map.get(node) ?? root; + let next; + do { + yield result; + next = map.get(result) ?? root; + if (next == result) { + break; + } + result = next; + } while (result != null); + }, + }; + if (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0) { + if ("chi" in value) { + const sliced = value.chi.slice(); + for (const child of sliced) { + map.set(child, value); + } + stack[reverse ? "push" : "unshift"](...sliced); + } + else { + const values = []; + if ("l" in value && value.l != null) { + // @ts-ignore + values.push(value.l); + // @ts-ignore + map.set(value.l, value); + } + if ("op" in value && typeof value.op == "object") { + // @ts-ignore + values.push(value.op); + // @ts-ignore + map.set(value.op, value); + } + if ("r" in value && value.r != null) { + if (Array.isArray(value.r)) { + for (const r of value.r) { + // @ts-ignore + values.push(r); + // @ts-ignore + map.set(r, value); + } + } + else { + // @ts-ignore + values.push(value.r); + // @ts-ignore + map.set(value.r, value); + } + } + if (values.length > 0) { + stack[reverse ? "push" : "unshift"](...values); + } + } } - case "hypot": { - const chi = values.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType, exports.EnumToken.CommaTokenType].includes(t.typ)); - let all = []; - let ref = chi[0]; - let value = 0; - for (let i = 0; i < chi.length; i++) { + if (eventType & exports.WalkerEvent.Leave && filter.fn != null) { + const isValid = filter.type == null || + value.typ == filter.type || + (Array.isArray(filter.type) && filter.type.includes(value.typ)) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + option = filter.fn(value, map.get(value), exports.WalkerEvent.Leave); // @ts-ignore - const val = getValue$1(chi[i]); - if (Number.isNaN(val)) { - return null; + if (option != null && ("typ" in option || Array.isArray(option))) { + const op = Array.isArray(option) ? option : [option]; + for (const o of op) { + map.set(o, map.get(value) ?? root); + } + stack[reverse ? "push" : "unshift"](...op); } - all.push(val); - value += val * val; } - return [ - { - ...ref, - val: +Math.sqrt(value).toFixed(rem(...all)), - }, - ]; } - case "atan2": - case "pow": - case "rem": - case "mod": { - const chi = values.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(t.typ)); - // https://developer.mozilla.org/en-US/docs/Web/CSS/mod - const v1 = evaluate([chi[0]]); - const v2 = evaluate([chi[2]]); - // @ts-ignore - const val1 = getValue$1(v1[0]); - // @ts-ignore - const val2 = getValue$1(v2[0]); - if (token.val == "rem") { - return [ - { - ...v1[0], - val: +(val1 % val2).toFixed(rem(val1, val2)), - }, - ]; - } - if (token.val == "pow") { - return [ - { - ...v1[0], - val: Math.pow(val1, val2), - }, - ]; - } - if (token.val == "atan2") { - return [ - { - ...{}, - ...v1[0], - val: Math.atan2(val1, val2), - }, - ]; - } - return [ - { - ...v1[0], - val: val2 == 0 ? val1 : val1 - Math.floor(val1 / val2) * val2, - }, - ]; - } - case "clamp": - token.chi = values; - return [token]; - case "log": - case "round": - case "min": - case "max": { - const strategy = token.val == "round" && values[0]?.typ == exports.EnumToken.IdenTokenType - ? values.shift().val - : null; - const valuesMap = new Map(); - for (const curr of values) { - if (curr.typ == exports.EnumToken.CommaTokenType || - curr.typ == exports.EnumToken.WhitespaceTokenType || - curr.typ == exports.EnumToken.CommentTokenType) { - continue; - } - const result = evaluate([curr]); - const key = result[0].typ + ("unit" in result[0] ? result[0].unit : ""); - if (!valuesMap.has(key)) { - valuesMap.set(key, []); - } - valuesMap.get(key).push(result[0]); - } - if (valuesMap.size == 1) { - const values = valuesMap.values().next().value; - if (token.val == "log") { - const val1 = getValue$1(values[0]); - const val2 = values.length == 2 ? getValue$1(values[1]) : null; - return [ - { - ...values[0], - val: Math.log(val1) / Math.log(val2), - }, - ]; - } - if (token.val == "min" || token.val == "max") { - let val = getValue$1(values[0]); - let val2 = val; - let ret = values[0]; - for (const curr of values.slice(1)) { - val2 = getValue$1(curr); - if (val2 < val && token.val == "min") { - val = val2; - ret = curr; - } - else if (val2 > val && token.val == "max") { - val = val2; - ret = curr; - } - } - return [ret]; - } - if (token.val == "round") { - let val = getValue$1(values[0]); - let val2 = getValue$1(values[1]); - if (strategy == null || strategy == "down") { - val = val - (val % val2); - } - else { - val = - strategy == "to-zero" - ? Math.trunc(val / val2) * val2 - : strategy == "nearest" - ? Math.round(val / val2) * val2 - : Math.ceil(val / val2) * val2; - } - // @ts-ignore - return [{ ...values[0], val }]; + previous = value; + } + } + + function gcd(x, y) { + x = Math.abs(x); + y = Math.abs(y); + if (x == y) { + return x; + } + let t; + if (y > x) { + [x, y] = [y, x]; + } + while (y) { + t = y; + y = x % y; + x = t; + } + return x; + } + function compute$1(a, b, op) { + if (typeof a == "number" && typeof b == "number") { + switch (op) { + case exports.EnumToken.Add: + return a + b; + case exports.EnumToken.Sub: + return a - b; + case exports.EnumToken.Mul: + return a * b; + case exports.EnumToken.Div: + const r = simplify(a, b); + if (r[1] == 1) { + return r[0]; } - } + const result = a / b; + const r2 = minifyNumber(r[0]) + "/" + minifyNumber(r[1]); + return minifyNumber(result).length < r2.length + ? result + : { + typ: exports.EnumToken.FractionTokenType, + l: { typ: exports.EnumToken.NumberTokenType, val: r[0] }, + r: { typ: exports.EnumToken.NumberTokenType, val: r[1] }, + }; } } - return [token]; - } - /** - * convert BinaryExpression into an array - * @param token - */ - function inlineExpression$1(token) { - const result = []; - if (token.typ == exports.EnumToken.BinaryExpressionTokenType) { - if ([exports.EnumToken.Mul, exports.EnumToken.Div].includes(token.op)) { - result.push(token); + let l1 = typeof a == "number" + ? { + l: { val: a }, + r: { val: 1 }, } - else { - result.push(...inlineExpression$1(token.l), { typ: token.op }, ...inlineExpression$1(token.r)); + : a; + let r1 = typeof b == "number" + ? { + l: { val: b }, + r: { val: 1 }, } + : b; + let l2; + let r2; + switch (op) { + case exports.EnumToken.Add: + // @ts-ignore + l2 = l1.l.val * r1.r.val + l1.r.val * r1.l.val; + // @ts-ignore + r2 = l1.r.val * r1.r.val; + break; + case exports.EnumToken.Sub: + // @ts-ignore + l2 = l1.l.val * r1.r.val - l1.r.val * r1.l.val; + // @ts-ignore + r2 = l1.r.val * r1.r.val; + break; + case exports.EnumToken.Mul: + // @ts-ignore + l2 = l1.l.val * r1.l.val; + // @ts-ignore + r2 = l1.r.val * r1.r.val; + break; + case exports.EnumToken.Div: + // @ts-ignore + l2 = l1.l.val * r1.r.val; + // @ts-ignore + r2 = l1.r.val * r1.l.val; + break; } - else { - result.push(token); + // @ts-ignore + const a2 = simplify(l2, r2); + if (a2[1] == 1) { + return a2[0]; } - return result; + const result = a2[0] / a2[1]; + return minifyNumber(result).length <= minifyNumber(a2[0]).length + 1 + minifyNumber(a2[1]).length + ? result + : { + typ: exports.EnumToken.FractionTokenType, + l: { typ: exports.EnumToken.NumberTokenType, val: a2[0] }, + r: { typ: exports.EnumToken.NumberTokenType, val: a2[1] }, + }; } - /** - * evaluate expression - * @param token - */ - function evaluateExpression(token) { - if (token.typ != exports.EnumToken.BinaryExpressionTokenType) { - return token; - } - if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType) { - token.r = (evaluateExpression(token.r)); - } - if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType) { - token.l = (evaluateExpression(token.l)); + function rem(...a) { + if (a.some((i) => !Number.isInteger(i))) { + return a.reduce((a, b) => Math.max(a, String(b).split(".")[1]?.length ?? 0), 0); } - return doEvaluate(token.l, token.r, token.op); + return 0; } - function isScalarToken(token) { - return ("unit" in token || - token.typ == exports.EnumToken.MathFunctionTokenType || - (token.typ == exports.EnumToken.FunctionTokenType && mathFuncs.includes(token.val)) || - // @ts-ignore - (token.typ == exports.EnumToken.IdenTokenType && typeof Math[token.val.toUpperCase()] == "number") || - [exports.EnumToken.NumberTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.PercentageTokenType].includes(token.typ)); + function simplify(a, b) { + const g = gcd(a, b); + return g > 1 ? [a / g, b / g] : [a, b]; } + /** - * - * generate a binary expression tree + * evaluate an array of tokens * @param tokens */ - function buildExpression(tokens) { - return factor(factor(tokens.filter((t) => t.typ != exports.EnumToken.WhitespaceTokenType), ["/", "*"]), ["+", "-"])[0]; - } - function getArithmeticOperation(op) { - if (op == "+") { - return exports.EnumToken.Add; - } - if (op == "-") { - return exports.EnumToken.Sub; - } - if (op == "/") { - return exports.EnumToken.Div; + function evaluate(tokens) { + let nodes; + if (tokens.length == 1 && + (tokens[0].typ == exports.EnumToken.MathFunctionTokenType || tokens[0].typ == exports.EnumToken.FunctionTokenType) && + mathFuncs.includes(tokens[0].val)) { + const chi = tokens[0].chi.reduce((acc, t) => { + if (acc.length == 0 || t.typ == exports.EnumToken.CommaTokenType) { + acc.push([]); + } + if ([exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommaTokenType, exports.EnumToken.CommaTokenType].includes(t.typ)) { + return acc; + } + acc.at(-1).push(t); + return acc; + }, []); + for (let i = 0; i < chi.length; i++) { + chi[i] = evaluate(chi[i]); + } + tokens[0].chi = chi.reduce((acc, t) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.CommaTokenType }); + } + acc.push(...t); + return acc; + }); + const result = evaluateFunc(tokens[0]); + if (result == null) { + return tokens; + } + if (result[0].typ === exports.EnumToken.MathFunctionTokenType && + result[0].val === "calc" && + result[0].chi.length === 1) { + return result[0].chi.slice(); + } + return result; } - return exports.EnumToken.Mul; - } - /** - * - * generate a binary expression tree - * @param token - */ - function factorToken(token) { - if (token.typ == exports.EnumToken.ParensTokenType || - ((token.typ == exports.EnumToken.MathFunctionTokenType || token.typ == exports.EnumToken.FunctionTokenType) && - token.val == "calc")) { - if ((token.typ == exports.EnumToken.MathFunctionTokenType || token.typ == exports.EnumToken.FunctionTokenType) && - token.val == "calc") { - token = { ...token, typ: exports.EnumToken.ParensTokenType }; + nodes = inlineExpression$1(evaluateExpression(buildExpression(tokens))); + if (nodes.length <= 1) { + if (nodes.length == 1) { + if (nodes[0].typ == exports.EnumToken.BinaryExpressionTokenType) { + return inlineExpression$1(nodes[0]); + } // @ts-ignore - delete token.val; + if (nodes[0].typ == exports.EnumToken.IdenTokenType && + // @ts-expect-error + typeof Math[nodes[0].val.toUpperCase()] == "number") { + return [ + { + ...nodes[0], + // @ts-ignore + val: Math[nodes[0].val.toUpperCase()], + typ: exports.EnumToken.NumberTokenType, + }, + ]; + } } - return buildExpression(token.chi); - } - return token; - } - /** - * generate a binary expression tree - * @param tokens - * @param ops - */ - function factor(tokens, ops) { - let isOp; - const opList = ops.map((x) => getArithmeticOperation(x)); - if (tokens.length == 1) { - return [factorToken(tokens[0])]; + return nodes; } - for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.ListToken) { - // @ts-ignore - tokens.splice(i, 1, ...tokens[i].chi); + const map = new Map(); + let token; + let i; + for (i = 0; i < nodes.length; i++) { + token = nodes[i]; + if (token.typ == exports.EnumToken.Add || token.typ == exports.EnumToken.Plus) { + continue; } - isOp = opList.includes(tokens[i].typ === exports.EnumToken.Plus ? exports.EnumToken.Add : tokens[i].typ); - if (isOp || - tokens[i].typ === exports.EnumToken.Star || - // @ts-ignore - (tokens[i].typ == exports.EnumToken.LiteralTokenType && ops.includes(tokens[i].val))) { - tokens.splice(i - 1, 3, { - typ: exports.EnumToken.BinaryExpressionTokenType, - op: isOp - ? tokens[i].typ === exports.EnumToken.Plus - ? exports.EnumToken.Add - : tokens[i].typ - : getArithmeticOperation(tokens[i].val), - l: factorToken(tokens[i - 1]), - r: factorToken(tokens[i + 1]), - }); - i--; + if (token.typ == exports.EnumToken.Sub) { + if (!isScalarToken(nodes[i + 1])) { + token = { typ: exports.EnumToken.ListToken, chi: [nodes[i], nodes[i + 1]] }; + } + else { + token = doEvaluate(nodes[i + 1], { typ: exports.EnumToken.NumberTokenType, val: -1 }, exports.EnumToken.Mul); + } + i++; + } + if (!map.has(token.typ)) { + map.set(token.typ, [token]); + } + else { + map.get(token.typ).push(token); } } - return tokens; + return [...map].reduce((acc, curr) => { + const token = curr[1].reduce((acc, curr) => doEvaluate(acc, curr, exports.EnumToken.Add)); + if (token.typ != exports.EnumToken.BinaryExpressionTokenType) { + if ("val" in token && +token.val < 0) { + acc.push({ typ: exports.EnumToken.Sub }, { ...token, val: -token.val }); + return acc; + } + } + if (acc.length > 0 && curr[0] != exports.EnumToken.ListToken) { + acc.push({ typ: exports.EnumToken.Add }); + } + acc.push(token); + return acc; + }, []); } - /** - * Parse relative color components - * @param relativeKeys - * @param original - * @param rExp - * @param gExp - * @param bExp - * @param aExp - * @returns + * evaluate arithmetic operation + * @param l + * @param r + * @param op */ - function parseRelativeColorComponents(relativeKeys, original, rExp, gExp, bExp, aExp) { - let r; - let g; - let b; - let alpha = null; - let keys = {}; - let values = {}; - // colorFuncColorSpace x,y,z or r,g,b - const names = relativeKeys.startsWith("xyz") - ? "xyz" - : ["srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "rgb"].some((t) => equalsIgnoreCase(t, relativeKeys)) - ? "rgb" - : relativeKeys.slice(-3); - const allComponents = [rExp, gExp, bExp, aExp]; - const components = getColorComponents(original); - const validKeys = names.split(""); - let val = ""; - if (components != null) { - allComponents.push(...components); + function doEvaluate(l, r, op) { + const defaultReturn = { + typ: exports.EnumToken.BinaryExpressionTokenType, + op, + l, + r, + }; + if (!isScalarToken(l) || !isScalarToken(r) || (l.typ == r.typ && "unit" in l && "unit" in r && l.unit != r.unit)) { + return defaultReturn; } - // ensure all components are valid for the color space - for (const component of allComponents) { - if (component == null) { - continue; - } - if (component.typ == exports.EnumToken.IdenTokenType) { - val = component.val.toLowerCase(); - if ( - // @ts-expect-error - typeof Math[val.toUpperCase()] !== "number" && - val != "in" && - val != "hue" && - val != "from" && - val != "alpha" && - val != "none" && - val != "shorter" && - val != "longer" && - val != "increasing" && - val != "decreasing" && - !colorsFunc.includes(val) && - !colorFuncColorSpace.includes(val) && - !validKeys.includes(val)) { - return null; - } - continue; + if (r.typ == exports.EnumToken.FunctionTokenType || r.typ == exports.EnumToken.MathFunctionTokenType) { + const val = evaluateFunc(r); + if (val == null) { + return defaultReturn; } - if (component.typ === exports.EnumToken.MathFunctionTokenType && - equalsIgnoreCase("calc", component.val)) { - for (const { value } of walkValues(component.chi)) { - if (value.typ == exports.EnumToken.IdenTokenType) { - val = value.val.toLowerCase(); - if ( - // @ts-expect-error - typeof Math[val.toUpperCase()] !== "number" && - val != "in" && - val != "hue" && - val != "from" && - val != "alpha" && - val != "none" && - val != "shorter" && - val != "longer" && - val != "increasing" && - val != "decreasing" && - !colorsFunc.includes(val) && - !colorFuncColorSpace.includes(val) && - !validKeys.includes(val)) { - return null; - } - } - } + if (val.length == 1) { + r = val[0]; } } - const converted = (convertColor(original, exports.ColorType[relativeKeys.toUpperCase().replaceAll("-", "_")])); - if (converted == null) { - return null; - } - const children = converted.chi.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.LiteralTokenType, exports.EnumToken.CommentTokenType].includes(t.typ)); - [r, g, b, alpha] = converted.kin == exports.ColorType.COLOR ? children.slice(1) : children; - values = { - [names[0]]: getValue(r, converted, names[0]), - [names[1]]: getValue(g, converted, names[1]), // string, - [names[2]]: getValue(b, converted, names[2]), + if (op == exports.EnumToken.Add || op == exports.EnumToken.Plus || op == exports.EnumToken.Sub) { // @ts-ignore - alpha: alpha == null - ? { - typ: exports.EnumToken.NumberTokenType, - val: 1, - } - : alpha.typ == exports.EnumToken.IdenTokenType && alpha.val == "none" - ? { - typ: exports.EnumToken.NumberTokenType, - val: 0, - } - : alpha.typ == exports.EnumToken.PercentageTokenType - ? { - typ: exports.EnumToken.NumberTokenType, - val: getNumber(alpha), - } - : alpha, - }; - keys = { - [names[0]]: getValue(rExp, converted, names[0]), - [names[1]]: getValue(gExp, converted, names[1]), - [names[2]]: getValue(bExp, converted, names[2]), - // @ts-ignore - alpha: getValue(aExp == null - ? { - typ: exports.EnumToken.NumberTokenType, - val: 1, - } - : aExp.typ == exports.EnumToken.IdenTokenType && aExp.val == "none" - ? { - typ: exports.EnumToken.NumberTokenType, - val: 0, - } - : aExp), - }; - const result = computeComponentValue(keys, values); - if (result?.alpha?.typ == exports.EnumToken.NumberTokenType && result.alpha.val === 1) { - const { alpha, ...components } = result; - return components; + if (l.typ != r.typ) { + return defaultReturn; + } } - return result; - } - /** - * Get token numeric value - * @param t - * @param converted - * @param component - * @returns - */ - function getValue(t, converted, component) { - if (t.typ == exports.EnumToken.PercentageTokenType) { - let value = getNumber(t); - if (converted != null) { - let colorSpace = exports.ColorType[converted.kin].toLowerCase().replaceAll("-", "_"); - if (colorSpace in colorRange) { - // @ts-ignore - value *= colorRange[colorSpace][component].at(-1); + let typ = l.typ == exports.EnumToken.NumberTokenType || l.typ === exports.EnumToken.IdenTokenType + ? r.typ + : r.typ == exports.EnumToken.NumberTokenType + ? l.typ + : l.typ == exports.EnumToken.PercentageTokenType + ? r.typ + : l.typ; + // @ts-expect-error + let v1 = l.val?.typ == exports.EnumToken.FractionTokenType ? l.val : getValue$1(l); + let v2 = r.val?.typ == exports.EnumToken.FractionTokenType + ? // @ts-expect-error + r.val + : getValue$1(r); + if (op == exports.EnumToken.Mul) { + if (l.typ != exports.EnumToken.NumberTokenType && r.typ != exports.EnumToken.NumberTokenType) { + if (typeof v1 == "number" && l.typ == exports.EnumToken.PercentageTokenType) { + v1 = { + typ: exports.EnumToken.FractionTokenType, + l: { typ: exports.EnumToken.NumberTokenType, val: v1 }, + r: { typ: exports.EnumToken.NumberTokenType, val: 100 }, + }; + } + else if (typeof v2 == "number" && r.typ == exports.EnumToken.PercentageTokenType) { + v2 = { + typ: exports.EnumToken.FractionTokenType, + l: { typ: exports.EnumToken.NumberTokenType, val: v2 }, + r: { typ: exports.EnumToken.NumberTokenType, val: 100 }, + }; } } - return { - typ: exports.EnumToken.NumberTokenType, - val: value, - }; } - return t; + // @ts-ignore + const val = compute$1(v1, v2, op); + const token = { + ...(l.typ === exports.EnumToken.NumberTokenType || l.typ === exports.EnumToken.IdenTokenType ? r : l), + typ, + val /* : typeof val == 'number' ? minifyNumber(val) : val */, + }; + if (token.typ == exports.EnumToken.IdenTokenType) { + // @ts-ignore + token.typ = exports.EnumToken.NumberTokenType; + } + return token; } - /** - * Compute component value - * @param expr - * @param values - * @returns - */ - function computeComponentValue(expr, values) { - for (const object of [values, expr]) { - if ("h" in object) { - // normalize hue - for (const k of walkValues([object.h])) { - if (k.value.typ == exports.EnumToken.AngleTokenType && k.value.unit == "deg") { - k.value.typ = exports.EnumToken.NumberTokenType; - } - } - } + function getValue$1(t) { + if (t.typ == exports.EnumToken.IdenTokenType) { + // @ts-ignore + return Math[t.val.toUpperCase()]; } - for (const [key, exp] of Object.entries(expr)) { - if ([ - exports.EnumToken.NumberTokenType, - exports.EnumToken.PercentageTokenType, - exports.EnumToken.AngleTokenType, - exports.EnumToken.LengthTokenType, - ].includes(exp.typ)) ; - else if (exp.typ == exports.EnumToken.IdenTokenType && exp.val in values) { - expr[key] = values[exp.val]; + // @ts-ignore + return t.typ == exports.EnumToken.FractionTokenType ? t.l.val / t.r.val : +t.val; + } + function evaluateFunc(token) { + const values = token.chi.slice(); + switch (token.val) { + case "abs": + case "sin": + case "cos": + case "tan": + case "asin": + case "acos": + case "atan": + case "sign": + case "sqrt": + case "exp": { + const value = evaluate(values); + // @ts-ignore + let val = value[0].typ == exports.EnumToken.NumberTokenType + ? +value[0].val + : // @ts-expect-error + value[0].l.val / value[0].r.val; + return [ + { + typ: exports.EnumToken.NumberTokenType, + val: Math[token.val](val), + }, + ]; } - else if (exp.typ == exports.EnumToken.MathFunctionTokenType || - (exp.typ == exports.EnumToken.FunctionTokenType && mathFuncs.includes(exp.val))) { - for (let { value, parent } of walkValues(exp.chi, exp)) { - if (value.typ == exports.EnumToken.IdenTokenType) { - // @ts-ignore - replaceValue(parent, value, - // @ts-expect-error - values[value.val] ?? - { - typ: exports.EnumToken.NumberTokenType, - // @ts-ignore - val: "" + Math[value.val.toUpperCase()], - // @ts-ignore - }); + case "hypot": { + const chi = values.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType, exports.EnumToken.CommaTokenType].includes(t.typ)); + let all = []; + let ref = chi[0]; + let value = 0; + for (let i = 0; i < chi.length; i++) { + // @ts-ignore + const val = getValue$1(chi[i]); + if (Number.isNaN(val)) { + return null; } + all.push(val); + value += val * val; } - const result = (exp.typ === exports.EnumToken.MathFunctionTokenType || - (exp.typ == exports.EnumToken.FunctionTokenType && mathFuncs.includes(exp.val))) && - exp.val !== "calc" - ? evaluateFunc(exp) - : evaluate(exp.chi); - if (result.length == 1 && result[0].typ != exports.EnumToken.BinaryExpressionTokenType) { - expr[key] = result[0]; - } + return [ + { + ...ref, + val: +Math.sqrt(value).toFixed(rem(...all)), + }, + ]; } - } - return expr; - } - function replaceValue(parent, value, newValue) { - for (const { value: val, parent: pr } of walkValues([parent])) { - if (val.typ == value.typ && val.val == value.val) { - if (pr.typ == exports.EnumToken.BinaryExpressionTokenType) { - if (pr.l == val) { - pr.l = newValue; - return; - } - else { - pr.r = newValue; - return; - } + case "atan2": + case "pow": + case "rem": + case "mod": { + const chi = values.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(t.typ)); + // https://developer.mozilla.org/en-US/docs/Web/CSS/mod + const v1 = evaluate([chi[0]]); + const v2 = evaluate([chi[2]]); + // @ts-ignore + const val1 = getValue$1(v1[0]); + // @ts-ignore + const val2 = getValue$1(v2[0]); + if (token.val == "rem") { + return [ + { + ...v1[0], + val: +(val1 % val2).toFixed(rem(val1, val2)), + }, + ]; } - else { - pr.chi.splice(pr.chi.indexOf(val), 1, newValue); - return; + if (token.val == "pow") { + return [ + { + ...v1[0], + val: Math.pow(val1, val2), + }, + ]; + } + if (token.val == "atan2") { + return [ + { + ...{}, + ...v1[0], + val: Math.atan2(val1, val2), + }, + ]; + } + return [ + { + ...v1[0], + val: val2 == 0 ? val1 : val1 - Math.floor(val1 / val2) * val2, + }, + ]; + } + case "clamp": + token.chi = values; + return [token]; + case "log": + case "round": + case "min": + case "max": { + const strategy = token.val == "round" && values[0]?.typ == exports.EnumToken.IdenTokenType + ? values.shift().val + : null; + const valuesMap = new Map(); + for (const curr of values) { + if (curr.typ == exports.EnumToken.CommaTokenType || + curr.typ == exports.EnumToken.WhitespaceTokenType || + curr.typ == exports.EnumToken.CommentTokenType) { + continue; + } + const result = evaluate([curr]); + const key = result[0].typ + ("unit" in result[0] ? result[0].unit : ""); + if (!valuesMap.has(key)) { + valuesMap.set(key, []); + } + valuesMap.get(key).push(result[0]); + } + if (valuesMap.size == 1) { + const values = valuesMap.values().next().value; + if (token.val == "log") { + const val1 = getValue$1(values[0]); + const val2 = values.length == 2 ? getValue$1(values[1]) : null; + return [ + { + ...values[0], + val: Math.log(val1) / Math.log(val2), + }, + ]; + } + if (token.val == "min" || token.val == "max") { + let val = getValue$1(values[0]); + let val2 = val; + let ret = values[0]; + for (const curr of values.slice(1)) { + val2 = getValue$1(curr); + if (val2 < val && token.val == "min") { + val = val2; + ret = curr; + } + else if (val2 > val && token.val == "max") { + val = val2; + ret = curr; + } + } + return [ret]; + } + if (token.val == "round") { + let val = getValue$1(values[0]); + let val2 = getValue$1(values[1]); + if (strategy == null || strategy == "down") { + val = val - (val % val2); + } + else { + val = + strategy == "to-zero" + ? Math.trunc(val / val2) * val2 + : strategy == "nearest" + ? Math.round(val / val2) * val2 + : Math.ceil(val / val2) * val2; + } + // @ts-ignore + return [{ ...values[0], val }]; + } } } } + return [token]; } - - function rgb2cmykToken(token) { - const components = rgb2srgbvalues(token); - if (components == null || components.length < 3) { - return null; - } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...components)); - } - function hsl2cmykToken(token) { - const values = hsl2srgbvalues(token); - if (values == null) { - return null; + /** + * convert BinaryExpression into an array + * @param token + */ + function inlineExpression$1(token) { + const result = []; + if (token.typ == exports.EnumToken.BinaryExpressionTokenType) { + if ([exports.EnumToken.Mul, exports.EnumToken.Div].includes(token.op)) { + result.push(token); + } + else { + result.push(...inlineExpression$1(token.l), { typ: token.op }, ...inlineExpression$1(token.r)); + } } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...values)); - } - function hwb2cmykToken(token) { - const values = hwb2srgbvalues(token); - if (values == null) { - return null; + else { + result.push(token); } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...values)); + return result; } - function lab2cmykToken(token) { - const components = lab2srgbvalues(token); - if (components == null || components.length < 3) { - return null; + /** + * evaluate expression + * @param token + */ + function evaluateExpression(token) { + if (token.typ != exports.EnumToken.BinaryExpressionTokenType) { + return token; } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...components)); - } - function lch2cmykToken(token) { - const components = lch2srgbvalues(token); - if (components == null || components.length < 3) { - return null; + if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType) { + token.r = (evaluateExpression(token.r)); } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...components)); - } - function oklab2cmyk(token) { - const components = oklab2srgbvalues(token); - if (components == null || components.length < 3) { - return null; + if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType) { + token.l = (evaluateExpression(token.l)); } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...components)); + return doEvaluate(token.l, token.r, token.op); } - function oklch2cmykToken(token) { - const components = oklch2srgbvalues(token); - if (components == null || components.length < 3) { - return null; + function isScalarToken(token) { + return ("unit" in token || + token.typ == exports.EnumToken.MathFunctionTokenType || + (token.typ == exports.EnumToken.FunctionTokenType && mathFuncs.includes(token.val)) || + // @ts-ignore + (token.typ == exports.EnumToken.IdenTokenType && typeof Math[token.val.toUpperCase()] == "number") || + [exports.EnumToken.NumberTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.PercentageTokenType].includes(token.typ)); + } + /** + * + * generate a binary expression tree + * @param tokens + */ + function buildExpression(tokens) { + return factor(factor(tokens.filter((t) => t.typ != exports.EnumToken.WhitespaceTokenType), ["/", "*"]), ["+", "-"])[0]; + } + function getArithmeticOperation(op) { + if (op == "+") { + return exports.EnumToken.Add; } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...components)); + if (op == "-") { + return exports.EnumToken.Sub; + } + if (op == "/") { + return exports.EnumToken.Div; + } + return exports.EnumToken.Mul; } - function color2cmykToken(token) { - const values = color2srgbvalues(token); - if (values == null) { - return null; + /** + * + * generate a binary expression tree + * @param token + */ + function factorToken(token) { + if (token.typ == exports.EnumToken.ParensTokenType || + ((token.typ == exports.EnumToken.MathFunctionTokenType || token.typ == exports.EnumToken.FunctionTokenType) && + token.val == "calc")) { + if ((token.typ == exports.EnumToken.MathFunctionTokenType || token.typ == exports.EnumToken.FunctionTokenType) && + token.val == "calc") { + token = { ...token, typ: exports.EnumToken.ParensTokenType }; + // @ts-ignore + delete token.val; + } + return buildExpression(token.chi); } - // @ts-ignore - return cmyktoken(srgb2cmykvalues(...values)); + return token; } - function srgb2cmykvalues(r, g, b, a = null) { - const k = 1 - Math.max(r, g, b); - const c = k == 1 ? 0 : (1 - r - k) / (1 - k); - const m = k == 1 ? 0 : (1 - g - k) / (1 - k); - const y = k == 1 ? 0 : (1 - b - k) / (1 - k); - const result = [c, m, y, k]; - if (a != null && a < 1) { - result.push(a); + /** + * generate a binary expression tree + * @param tokens + * @param ops + */ + function factor(tokens, ops) { + let isOp; + const opList = ops.map((x) => getArithmeticOperation(x)); + if (tokens.length == 1) { + return [factorToken(tokens[0])]; } - return result; + for (let i = 0; i < tokens.length; i++) { + if (tokens[i].typ == exports.EnumToken.ListToken) { + // @ts-ignore + tokens.splice(i, 1, ...tokens[i].chi); + } + isOp = opList.includes(tokens[i].typ === exports.EnumToken.Plus ? exports.EnumToken.Add : tokens[i].typ); + if (isOp || + tokens[i].typ === exports.EnumToken.Star || + // @ts-ignore + (tokens[i].typ == exports.EnumToken.LiteralTokenType && ops.includes(tokens[i].val))) { + tokens.splice(i - 1, 3, { + typ: exports.EnumToken.BinaryExpressionTokenType, + op: isOp + ? tokens[i].typ === exports.EnumToken.Plus + ? exports.EnumToken.Add + : tokens[i].typ + : getArithmeticOperation(tokens[i].val), + l: factorToken(tokens[i - 1]), + r: factorToken(tokens[i + 1]), + }); + i--; + } + } + return tokens; } - function cmyktoken(values) { - return { - typ: exports.EnumToken.ColorTokenType, - val: "device-cmyk", - chi: values.reduce((acc, curr, index) => index < 4 - ? [ - ...acc, - { - typ: exports.EnumToken.PercentageTokenType, - // @ts-ignore - val: toPrecisionValue(curr * 100), - }, - ] - : [ - ...acc, - { - typ: exports.EnumToken.LiteralTokenType, - val: "/", - }, - { - typ: exports.EnumToken.PercentageTokenType, - val: toPrecisionValue(curr, 2) * 100, - }, - ], []), - kin: exports.ColorType.DEVICE_CMYK, - }; - } - - function a98rgb2srgbvalues(r, g, b, a = null) { - // @ts-ignore - return xyz2srgb(...la98rgb2xyz(...a98rgb2la98(r, g, b, a))); - } - function srgb2a98values$1(r, g, b, a = null) { - // @ts-ignore - return la98rgb2a98rgb(...xyz2la98rgb(...srgb2xyz(r, g, b, a))); - } - // a98-rgb functions - function a98rgb2la98(r, g, b, a = null) { - // convert an array of a98-rgb values in the range 0.0 - 1.0 - // to linear light (un-companded) form. - // negative values are also now accepted - return [r, g, b] - .map(function (val) { - let sign = val < 0 ? -1 : 1; - let abs = Math.abs(val); - return sign * Math.pow(abs, 563 / 256); - }) - .concat(a == null || a == 1 ? [] : [a]); - } - function la98rgb2a98rgb(r, g, b, a = null) { - // convert an array of linear-light a98-rgb in the range 0.0-1.0 - // to gamma corrected form - // negative values are also now accepted - return [r, b, g] - .map(function (val) { - let sign = val < 0 ? -1 : 1; - let abs = Math.abs(val); - return sign * Math.pow(abs, 256 / 563); - }) - .concat(a == null || a == 1 ? [] : [a]); - } - function la98rgb2xyz(r, g, b, a = null) { - // convert an array of linear-light a98-rgb values to CIE XYZ - // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html - // has greater numerical precision than section 4.3.5.3 of - // https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf - // but the values below were calculated from first principles - // from the chromaticity coordinates of R G B W - // see matrixmaker.html - var M = [ - [573536 / 994567, 263643 / 1420810, 187206 / 994567], - [591459 / 1989134, 6239551 / 9945670, 374412 / 4972835], - [53769 / 1989134, 351524 / 4972835, 4929758 / 4972835], - ]; - return multiplyMatrices(M, [r, g, b]).concat(a == null || a == 1 ? [] : [a]); - } - function xyz2la98rgb(x, y, z, a = null) { - // convert XYZ to linear-light a98-rgb - var M = [ - [1829569 / 896150, -506331 / 896150, -308931 / 896150], - [-851781 / 878810, 1648619 / 878810, 36519 / 878810], - [16779 / 1248040, -147721 / 1248040, 1266979 / 1248040], - ]; - return multiplyMatrices(M, [x, y, z]).concat(a == null || a == 1 ? [] : [a]); - } - - 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["FunctionDefinition"] = 5] = "FunctionDefinition"; - 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["Plus"] = 24] = "Plus"; - 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["LessThan"] = 41] = "LessThan"; - ValidationTokenEnum[ValidationTokenEnum["GreaterThan"] = 42] = "GreaterThan"; - /** - * end of token stream - */ - ValidationTokenEnum[ValidationTokenEnum["EOF"] = 43] = "EOF"; - /** - * optional group or tokens, used to group validation tokens - * - * ```ts - * // #? , -> [#? ,]? - * // , ]#? -> [, ]#?]? - * ``` - */ - ValidationTokenEnum[ValidationTokenEnum["OptionalGroupToken"] = 44] = "OptionalGroupToken"; - /** - * dimension token - * - * ```ts - * //