postcss-calc 11.2.2 → 11.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,12 @@
1
1
  import { mkSum, mkProduct, mkGroup, num, dim } from '../node.js';
2
2
  import { tryCancelPair } from './cancel.js';
3
- import { isExact } from './exact.js';
3
+ import { flattenFactor, isBarrier } from './product-barrier.js';
4
+ import {
5
+ chainValue,
6
+ foldsExactly,
7
+ isScalarSum,
8
+ rationalProduct,
9
+ } from './product-quotient.js';
4
10
 
5
11
  /**
6
12
  * @typedef {import('../node.js').Node} Node
@@ -9,136 +15,6 @@ import { isExact } from './exact.js';
9
15
  * @typedef {import('../simplify.js').SimplifyFn} SimplifyFn
10
16
  */
11
17
 
12
- /**
13
- * @param {number} a non-negative
14
- * @param {number} b non-negative
15
- * @return {number}
16
- */
17
- function gcd(a, b) {
18
- while (b !== 0) {
19
- [a, b] = [b, a % b];
20
- }
21
- return a || 1;
22
- }
23
-
24
- /**
25
- * @param {{exponent: 1 | -1, value: number}[]} chain
26
- * @return {number}
27
- */
28
- function chainValue(chain) {
29
- let value = 1;
30
- for (const f of chain) {
31
- value = f.exponent === 1 ? value * f.value : value / f.value;
32
- }
33
- return value;
34
- }
35
-
36
- /**
37
- * Whether the opaque factors are a single Sum of only Num/Dim terms.
38
- * @param {ProductFactor[]} opaque
39
- * @return {boolean}
40
- */
41
- function isScalarSum(opaque) {
42
- return (
43
- opaque.length === 1 &&
44
- opaque[0].exponent === 1 &&
45
- opaque[0].node.type === 'Sum' &&
46
- opaque[0].node.terms.every(
47
- (t) => t.node.type === 'Num' || t.node.type === 'Dim'
48
- )
49
- );
50
- }
51
-
52
- /**
53
- * Whether a folded quotient prints exactly. A quotient distributed over a
54
- * scalar Sum is judged term by term, so `(3px + 6em) / 3` still folds.
55
- * @param {number} value
56
- * @param {ProductFactor[]} opaque
57
- * @param {boolean} distributable
58
- * @param {number | false} precision
59
- * @return {boolean}
60
- */
61
- function foldsExactly(value, opaque, distributable, precision) {
62
- if (!Number.isFinite(value) || isExact(value, precision)) {
63
- return true;
64
- }
65
- if (!distributable) {
66
- return false;
67
- }
68
- const sum = /** @type {import('../node.js').Sum} */ (opaque[0].node);
69
- return sum.terms.every((t) =>
70
- isExact(
71
- value *
72
- /** @type {import('../node.js').Num | import('../node.js').Dim} */ (
73
- t.node
74
- ).value,
75
- precision
76
- )
77
- );
78
- }
79
-
80
- /**
81
- * Emit an inexact quotient as `numerator * dims * opaque / denominator`, with
82
- * integer parts reduced by their gcd. A lone Dim absorbs the numerator.
83
- * @param {number} numerator
84
- * @param {number} denominator
85
- * @param {{value: number, unit: string, rawUnit?: string} | null} loneDim
86
- * @param {{exponent: 1 | -1, value: number, unit: string, rawUnit?: string}[]} dims
87
- * @param {ProductFactor[]} opaque
88
- * @param {number | false} precision
89
- * @return {Node | null} null when a part would be rounded on output
90
- */
91
- function rationalProduct(
92
- numerator,
93
- denominator,
94
- loneDim,
95
- dims,
96
- opaque,
97
- precision
98
- ) {
99
- // Drop float noise (`1.9999999999999993 * 100`) so the parts print as
100
- // written and the gcd below can reduce them.
101
- let n = Number(
102
- (loneDim === null ? numerator : numerator * loneDim.value).toPrecision(15)
103
- );
104
- let d = Number(denominator.toPrecision(15));
105
- if (!isExact(n, precision) || !isExact(d, precision)) {
106
- return null;
107
- }
108
- if (Number.isSafeInteger(n) && Number.isSafeInteger(d)) {
109
- const g = gcd(Math.abs(n), Math.abs(d));
110
- n /= g;
111
- d /= g;
112
- }
113
- if (d < 0) {
114
- n = -n;
115
- d = -d;
116
- }
117
- /** @type {ProductFactor[]} */
118
- const factors = [];
119
- if (loneDim !== null) {
120
- factors.push({
121
- exponent: 1,
122
- node: dim(n, loneDim.unit, loneDim.rawUnit),
123
- });
124
- } else {
125
- if (n !== 1) {
126
- factors.push({ exponent: 1, node: num(n) });
127
- }
128
- for (const dm of dims) {
129
- factors.push({
130
- exponent: dm.exponent,
131
- node: dim(dm.value, dm.unit, dm.rawUnit),
132
- });
133
- }
134
- }
135
- factors.push(...opaque);
136
- if (d !== 1) {
137
- factors.push({ exponent: -1, node: num(d) });
138
- }
139
- return mkProduct(factors);
140
- }
141
-
142
18
  /**
143
19
  * Fold a run of factors that contains no substitution barrier.
144
20
  * @param {ProductFactor[]} items Simplified factors, flattened by the caller.
@@ -266,53 +142,6 @@ function foldFactors(items, start, end, simplify, precision) {
266
142
  return mkProduct(factors);
267
143
  }
268
144
 
269
- /**
270
- * A substitution function (`var()`, `env()`, `attr()`, ...) is replaced by
271
- * tokens, not by a value, so factors cannot move or cancel across it. Any
272
- * function the parser does not recognise (`anchor()`, `foo()`, ...) is also an
273
- * OpaqueCall and is treated the same, conservatively. A grouped Product is a
274
- * parenthesized substitution and is equally opaque.
275
- * @param {Node} node
276
- * @return {boolean}
277
- */
278
- function isBarrier(node) {
279
- return (
280
- node.type === 'OpaqueCall' ||
281
- (node.type === 'Product' && node.grouped === true)
282
- );
283
- }
284
-
285
- /**
286
- * Flatten a simplified factor through nested ungrouped Products and the
287
- * canonical negation form, so barriers are visible at the top level.
288
- * @param {ProductFactor[]} out
289
- * @param {1 | -1} exponent
290
- * @param {Node} node
291
- * @return {boolean} Whether a barrier was pushed.
292
- */
293
- function flattenFactor(out, exponent, node) {
294
- if (node.type === 'Product' && !node.grouped) {
295
- let barrier = false;
296
- for (const inner of node.factors) {
297
- if (
298
- flattenFactor(
299
- out,
300
- /** @type {1 | -1} */ (exponent * inner.exponent),
301
- inner.node
302
- )
303
- )
304
- barrier = true;
305
- }
306
- return barrier;
307
- }
308
- if (node.type === 'Sum' && node.terms.length === 1) {
309
- out.push({ exponent, node: num(-1) });
310
- return flattenFactor(out, exponent, node.terms[0].node);
311
- }
312
- out.push({ exponent, node });
313
- return isBarrier(node);
314
- }
315
-
316
145
  /**
317
146
  * @param {Product} product
318
147
  * @param {SimplifyFn} simplify
@@ -0,0 +1,94 @@
1
+ /**
2
+ * `percent` marks a value that resolves in the same percentage context as its
3
+ * peers (a pure percentage). It is only produced at leaves, by abs(), and by
4
+ * homogeneous sums and calls — never by a product, where an unpaired
5
+ * percentage could no longer cancel against anything.
6
+ * @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown', percent?: true} | {kind: 'failure'}} CalculationType
7
+ */
8
+
9
+ /** @type {CalculationType} */ const numberType = { kind: 'number' };
10
+ /** @type {CalculationType} */ const unknownType = { kind: 'unknown' };
11
+ /** @type {CalculationType} */ const percentageType = {
12
+ kind: 'unknown',
13
+ percent: true,
14
+ };
15
+ /** @type {CalculationType} */ const failureType = { kind: 'failure' };
16
+
17
+ /** @param {CalculationType} type @return {boolean} */
18
+ function isFailure(type) {
19
+ return type.kind === 'failure';
20
+ }
21
+
22
+ /** @param {CalculationType} type @return {boolean} */
23
+ function isPercentage(type) {
24
+ return type.kind === 'unknown' && type.percent === true;
25
+ }
26
+
27
+ /** @param {CalculationType} a @param {CalculationType} b @return {CalculationType} */
28
+ function addTypes(a, b) {
29
+ if (isFailure(a) || isFailure(b)) return failureType;
30
+ if (a.kind === 'unknown' || b.kind === 'unknown') {
31
+ // A pure percentage keeps its contextual type so a surrounding product
32
+ // can cancel `% / %`; mixing it with any other opaque operand loses the
33
+ // guarantee that it resolves in the same context as its peers. A known
34
+ // number + percentage sum is likewise kept unknown and valid: the spec
35
+ // resolves the percentage against its surrounding context, which this
36
+ // coarse type model does not track.
37
+ return isPercentage(a) && isPercentage(b) ? percentageType : unknownType;
38
+ }
39
+ if (a.kind === 'number' && b.kind === 'number') return numberType;
40
+ if (a.kind === 'dimension' && b.kind === 'dimension') {
41
+ if (a.base === null || b.base === null) return unknownType;
42
+ return a.base === b.base ? a : failureType;
43
+ }
44
+ return failureType;
45
+ }
46
+
47
+ /**
48
+ * Check an all-number function without assuming anything about an unresolved
49
+ * operand. A concrete dimension can never become a number, so it is still a
50
+ * definite error when another argument is opaque.
51
+ * @param {CalculationType[]} args
52
+ * @param {number} min
53
+ * @param {number} max
54
+ * @return {CalculationType}
55
+ */
56
+ function numberArguments(args, min, max) {
57
+ if (args.length < min || args.length > max) return failureType;
58
+ let hasUnknown = false;
59
+ for (const arg of args) {
60
+ if (arg.kind === 'dimension') return failureType;
61
+ hasUnknown = hasUnknown || arg.kind === 'unknown';
62
+ }
63
+ return hasUnknown ? unknownType : numberType;
64
+ }
65
+
66
+ /**
67
+ * Check values that must share a calculation type. Unknown operands remain
68
+ * unknown: they might resolve to the concrete type required by their peers.
69
+ * @param {CalculationType[]} args
70
+ * @param {number} min
71
+ * @param {number} max
72
+ * @return {CalculationType}
73
+ */
74
+ function matchingArguments(args, min, max) {
75
+ if (args.length < min || args.length > max) return failureType;
76
+ let result = args[0];
77
+ for (let index = 1; index < args.length; index++) {
78
+ result = addTypes(result, args[index]);
79
+ if (isFailure(result)) return failureType;
80
+ }
81
+ return result;
82
+ }
83
+
84
+ export {
85
+ addTypes,
86
+ failureType,
87
+ isFailure,
88
+ isPercentage,
89
+ matchingArguments,
90
+ numberArguments,
91
+ numberType,
92
+ percentageType,
93
+ unknownType,
94
+ };
@@ -0,0 +1,27 @@
1
+ export type Node = import('./node.js').Node;
2
+ export type CalculationType = import('./types.js').CalculationType;
3
+ /**
4
+ * @typedef {import('./node.js').Node} Node
5
+ * @typedef {import('./types.js').CalculationType} CalculationType
6
+ */
7
+ /** @param {CalculationType[]} args @return {CalculationType} */
8
+ declare function analyzeTrig(args: CalculationType[]): CalculationType;
9
+ /** @param {CalculationType[]} args @return {CalculationType} */
10
+ declare function analyzeInverseTrig(args: CalculationType[]): CalculationType;
11
+ /** @param {CalculationType[]} args @return {CalculationType} */
12
+ declare function analyzeIdentity(args: CalculationType[]): CalculationType;
13
+ /** @param {CalculationType[]} args @return {CalculationType} */
14
+ declare function analyzeSign(args: CalculationType[]): CalculationType;
15
+ /** @param {CalculationType[]} args @param {Node[]} nodes @return {CalculationType} */
16
+ declare function analyzeRound(args: CalculationType[], nodes: Node[]): CalculationType;
17
+ /** @param {CalculationType[]} args @return {CalculationType} */
18
+ declare function analyzeAtan2(args: CalculationType[]): CalculationType;
19
+ /** @param {CalculationType[]} args @return {CalculationType} */
20
+ declare function analyzeCalc(args: CalculationType[]): CalculationType;
21
+ /** @param {Node} node @return {boolean} */
22
+ declare function isRoundStrategy(node: Node): boolean;
23
+ /** @param {Node} node @param {number} index @return {boolean} */
24
+ declare function isClampKeyword(node: Node, index: number): boolean;
25
+ /** @param {CalculationType[]} args @param {Node[]} nodes @return {CalculationType} */
26
+ declare function analyzeClamp(args: CalculationType[], nodes: Node[]): CalculationType;
27
+ export { analyzeAtan2, analyzeCalc, analyzeClamp, analyzeIdentity, analyzeInverseTrig, analyzeRound, analyzeSign, analyzeTrig, isClampKeyword, isRoundStrategy, };
@@ -1,15 +1,6 @@
1
+ import { addTypes, failureType, isFailure, isPercentage, numberType, percentageType, unknownType } from './types.js';
1
2
  export type Node = import('./node.js').Node;
2
- export type CalculationType = {
3
- kind: 'number';
4
- } | {
5
- kind: 'dimension';
6
- base: string | null;
7
- } | {
8
- kind: 'unknown';
9
- percent?: true;
10
- } | {
11
- kind: 'failure';
12
- };
3
+ export type CalculationType = import('./types.js').CalculationType;
13
4
  export type MathSimplifier = (name: string, args: Node[]) => Node;
14
5
  export type TypeAnalyzer = (args: CalculationType[], nodes: Node[]) => CalculationType;
15
6
  export type MathFunction = {
@@ -19,26 +10,10 @@ export type MathFunction = {
19
10
  calculation?: boolean;
20
11
  };
21
12
  /** @typedef {import('./node.js').Node} Node */
22
- /**
23
- * `percent` marks a value that resolves in the same percentage context as its
24
- * peers (a pure percentage). It is only produced at leaves, by abs(), and by
25
- * homogeneous sums and calls — never by a product, where an unpaired
26
- * percentage could no longer cancel against anything.
27
- * @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown', percent?: true} | {kind: 'failure'}} CalculationType
28
- */
13
+ /** @typedef {import('./types.js').CalculationType} CalculationType */
29
14
  /** @typedef {(name: string, args: Node[]) => Node} MathSimplifier */
30
15
  /** @typedef {(args: CalculationType[], nodes: Node[]) => CalculationType} TypeAnalyzer */
31
16
  /** @typedef {{analyze: TypeAnalyzer, simplify?: MathSimplifier, isKeyword?: (node: Node, index: number) => boolean, calculation?: boolean}} MathFunction */
32
- /** @type {CalculationType} */ declare const numberType: CalculationType;
33
- /** @type {CalculationType} */ declare const unknownType: CalculationType;
34
- /** @type {CalculationType} */ declare const percentageType: CalculationType;
35
- /** @type {CalculationType} */ declare const failureType: CalculationType;
36
- /** @param {CalculationType} type @return {boolean} */
37
- declare function isFailure(type: CalculationType): boolean;
38
- /** @param {CalculationType} type @return {boolean} */
39
- declare function isPercentage(type: CalculationType): boolean;
40
- /** @param {CalculationType} a @param {CalculationType} b @return {CalculationType} */
41
- declare function addTypes(a: CalculationType, b: CalculationType): CalculationType;
42
17
  declare const mathFunctions: Map<string, MathFunction>;
43
18
  /**
44
19
  * @param {string} name
@@ -1,28 +1,15 @@
1
1
  export type Node = import('../node.js').Node;
2
2
  export type Sum = import('../node.js').Sum;
3
- export type Product = import('../node.js').Product;
3
+ export type SumTerm = import('../node.js').SumTerm;
4
4
  export type ProductFactor = import('../node.js').ProductFactor;
5
5
  export type SerializeSession = import('./precision.js').SerializeSession;
6
6
  /**
7
7
  * @typedef {import('../node.js').Node} Node
8
8
  * @typedef {import('../node.js').Sum} Sum
9
- * @typedef {import('../node.js').Product} Product
9
+ * @typedef {import('../node.js').SumTerm} SumTerm
10
10
  * @typedef {import('../node.js').ProductFactor} ProductFactor
11
11
  * @typedef {import('./precision.js').SerializeSession} SerializeSession
12
12
  */
13
- declare const SUM_PRECEDENCE = 1;
14
- declare const PRODUCT_PRECEDENCE = 2;
15
- declare const ATOMIC_PRECEDENCE = 3;
16
- declare const UNARY_PRECEDENCE = 3;
17
- /** @param {Node} node @return {number} */
18
- declare function precedence(node: Node): number;
19
- /**
20
- * @param {Node} node
21
- * @param {number} parentPrecedence
22
- * @param {boolean} groupedRequired
23
- * @return {boolean}
24
- */
25
- declare function needsParentheses(node: Node, parentPrecedence: number, groupedRequired: boolean): boolean;
26
13
  /**
27
14
  * @param {Node} node
28
15
  * @param {SerializeSession} session
@@ -31,6 +18,12 @@ declare function needsParentheses(node: Node, parentPrecedence: number, groupedR
31
18
  * @return {void}
32
19
  */
33
20
  declare function emitNode(node: Node, session: SerializeSession, parentPrecedence?: number, groupedRequired?: boolean): void;
21
+ /**
22
+ * @param {Node} node
23
+ * @param {SerializeSession} session
24
+ * @return {void}
25
+ */
26
+ declare function emitLeadingNeg(node: Node, session: SerializeSession): void;
34
27
  /**
35
28
  * @param {import('../node.js').Call} node
36
29
  * @param {SerializeSession} session
@@ -45,39 +38,6 @@ declare function emitCall(node: import('../node.js').Call, session: SerializeSes
45
38
  * @return {void}
46
39
  */
47
40
  declare function emitOpaqueCall(node: import('../node.js').OpaqueCall, session: SerializeSession, callNameOverride?: string): void;
48
- /**
49
- * @param {import('../node.js').SumTerm} term
50
- * @param {1 | -1} multiplier
51
- * @param {number | false} precision
52
- * @return {1 | -1}
53
- */
54
- declare function termSign(term: import('../node.js').SumTerm, multiplier: 1 | -1, precision: number | false): 1 | -1;
55
- /**
56
- * @param {import('../node.js').SumTerm[]} terms
57
- * @param {SerializeSession} session
58
- * @param {1 | -1} [multiplier]
59
- * @return {void}
60
- */
61
- declare function emitSumTerms(terms: import('../node.js').SumTerm[], session: SerializeSession, multiplier?: 1 | -1): void;
62
- /** @param {Sum} sum @param {SerializeSession} session @return {void} */
63
- declare function emitSum(sum: Sum, session: SerializeSession): void;
64
- /**
65
- * @param {Node} node
66
- * @param {SerializeSession} session
67
- * @return {void}
68
- */
69
- declare function emitLeadingNeg(node: Node, session: SerializeSession): void;
70
- /**
71
- * @param {ProductFactor[]} factors
72
- * @param {SerializeSession} session
73
- * @param {number} [start]
74
- * @param {number} [coefficientValue]
75
- * @param {import('../node.js').Num} [coefficientNode]
76
- * @return {void}
77
- */
78
- declare function emitProductFactors(factors: ProductFactor[], session: SerializeSession, start?: number, coefficientValue?: number, coefficientNode?: import('../node.js').Num): void;
79
- /** @param {Product} product @param {SerializeSession} session @return {void} */
80
- declare function emitProduct(product: Product, session: SerializeSession): void;
81
41
  /**
82
42
  * @param {Node} node
83
43
  * @param {SerializeSession} session
@@ -92,4 +52,4 @@ declare function emitMathResult(node: Node, session: SerializeSession, wrapper:
92
52
  * @return {void}
93
53
  */
94
54
  declare function emitNestedMathResult(node: Node, session: SerializeSession, buffer?: string[]): void;
95
- export { SUM_PRECEDENCE, PRODUCT_PRECEDENCE, ATOMIC_PRECEDENCE, UNARY_PRECEDENCE, precedence, needsParentheses, emitNode, emitCall, emitOpaqueCall, termSign, emitSumTerms, emitSum, emitLeadingNeg, emitProductFactors, emitProduct, emitMathResult, emitNestedMathResult, };
55
+ export { emitNode, emitLeadingNeg, emitCall, emitOpaqueCall, emitMathResult, emitNestedMathResult, };
@@ -0,0 +1,18 @@
1
+ export type Node = import('../node.js').Node;
2
+ /**
3
+ * @typedef {import('../node.js').Node} Node
4
+ */
5
+ declare const SUM_PRECEDENCE = 1;
6
+ declare const PRODUCT_PRECEDENCE = 2;
7
+ declare const ATOMIC_PRECEDENCE = 3;
8
+ declare const UNARY_PRECEDENCE = 3;
9
+ /** @param {Node} node @return {number} */
10
+ declare function precedence(node: Node): number;
11
+ /**
12
+ * @param {Node} node
13
+ * @param {number} parentPrecedence
14
+ * @param {boolean} groupedRequired
15
+ * @return {boolean}
16
+ */
17
+ declare function needsParentheses(node: Node, parentPrecedence: number, groupedRequired: boolean): boolean;
18
+ export { SUM_PRECEDENCE, PRODUCT_PRECEDENCE, ATOMIC_PRECEDENCE, UNARY_PRECEDENCE, precedence, needsParentheses, };
@@ -77,25 +77,26 @@ declare function emitFiniteScalar(node: import('../node.js').Num | import('../no
77
77
  * @param {import('../node.js').Num | import('../node.js').Dim} node
78
78
  * @param {SerializeSession} session
79
79
  * @param {number} [value]
80
+ * @param {boolean} [bare] emit negative zero without its calc() wrapper
80
81
  * @return {void}
81
82
  */
82
- declare function emitScalar(node: import('../node.js').Num | import('../node.js').Dim, session: SerializeSession, value?: number): void;
83
+ declare function emitScalar(node: import('../node.js').Num | import('../node.js').Dim, session: SerializeSession, value?: number, bare?: boolean): void;
83
84
  /**
84
85
  * @param {string[]} buffer
85
86
  * @param {import('../node.js').Num | import('../node.js').Dim} node
86
87
  * @return {void}
87
88
  */
88
89
  declare function emitSignedZero(buffer: string[], node: import('../node.js').Num | import('../node.js').Dim): void;
90
+ /**
91
+ * Negative zero as an unwrapped product, valid only where a `<calc-product>`
92
+ * may appear without changing how the surrounding expression parses.
93
+ * @param {string[]} buffer
94
+ * @param {import('../node.js').Num | import('../node.js').Dim} node
95
+ * @return {void}
96
+ */
97
+ declare function emitBareSignedZero(buffer: string[], node: import('../node.js').Num | import('../node.js').Dim): void;
89
98
  /** @param {Node} node @return {node is import('../node.js').Num | import('../node.js').Dim} */
90
99
  declare function isScalar(node: Node): node is import('../node.js').Num | import('../node.js').Dim;
91
100
  /** @param {Node} node @return {node is import('../node.js').Num | import('../node.js').Dim} */
92
101
  declare function isSignedZero(node: Node): node is import('../node.js').Num | import('../node.js').Dim;
93
- /**
94
- * Whether a scalar node is strictly negative after precision rounding
95
- * (excluding signed zero and sub-precision values that round to zero).
96
- * @param {Node} node
97
- * @param {number | false} precision
98
- * @return {node is import('../node.js').Num | import('../node.js').Dim}
99
- */
100
- declare function isEffectivelyNegative(node: Node, precision: number | false): node is import('../node.js').Num | import('../node.js').Dim;
101
- export { NOISE_FLOOR, divideByPowerOfTen, roundDecimal, round, isDegenerate, degenerateKeyword, serializeNumber, roundedScalarValue, emitRoundedScalar, emitFiniteScalar, emitScalar, emitSignedZero, isScalar, isSignedZero, isEffectivelyNegative, };
102
+ export { NOISE_FLOOR, divideByPowerOfTen, roundDecimal, round, isDegenerate, degenerateKeyword, serializeNumber, roundedScalarValue, emitRoundedScalar, emitFiniteScalar, emitScalar, emitSignedZero, emitBareSignedZero, isScalar, isSignedZero, };
@@ -0,0 +1,26 @@
1
+ export type Node = import('../node.js').Node;
2
+ export type ProductFactor = import('../node.js').ProductFactor;
3
+ /**
4
+ * @typedef {import('../node.js').Node} Node
5
+ * @typedef {import('../node.js').ProductFactor} ProductFactor
6
+ */
7
+ /**
8
+ * A substitution function (`var()`, `env()`, `attr()`, ...) is replaced by
9
+ * tokens, not by a value, so factors cannot move or cancel across it. Any
10
+ * function the parser does not recognise (`anchor()`, `foo()`, ...) is also an
11
+ * OpaqueCall and is treated the same, conservatively. A grouped Product is a
12
+ * parenthesized substitution and is equally opaque.
13
+ * @param {Node} node
14
+ * @return {boolean}
15
+ */
16
+ declare function isBarrier(node: Node): boolean;
17
+ /**
18
+ * Flatten a simplified factor through nested ungrouped Products and the
19
+ * canonical negation form, so barriers are visible at the top level.
20
+ * @param {ProductFactor[]} out
21
+ * @param {1 | -1} exponent
22
+ * @param {Node} node
23
+ * @return {boolean} Whether a barrier was pushed.
24
+ */
25
+ declare function flattenFactor(out: ProductFactor[], exponent: 1 | -1, node: Node): boolean;
26
+ export { flattenFactor, isBarrier };
@@ -0,0 +1,48 @@
1
+ export type Node = import('../node.js').Node;
2
+ export type ProductFactor = import('../node.js').ProductFactor;
3
+ /**
4
+ * @param {{exponent: 1 | -1, value: number}[]} chain
5
+ * @return {number}
6
+ */
7
+ declare function chainValue(chain: {
8
+ exponent: 1 | -1;
9
+ value: number;
10
+ }[]): number;
11
+ /**
12
+ * Whether the opaque factors are a single Sum of only Num/Dim terms.
13
+ * @param {ProductFactor[]} opaque
14
+ * @return {boolean}
15
+ */
16
+ declare function isScalarSum(opaque: ProductFactor[]): boolean;
17
+ /**
18
+ * Whether a folded quotient prints exactly. A quotient distributed over a
19
+ * scalar Sum is judged term by term, so `(3px + 6em) / 3` still folds.
20
+ * @param {number} value
21
+ * @param {ProductFactor[]} opaque
22
+ * @param {boolean} distributable
23
+ * @param {number | false} precision
24
+ * @return {boolean}
25
+ */
26
+ declare function foldsExactly(value: number, opaque: ProductFactor[], distributable: boolean, precision: number | false): boolean;
27
+ /**
28
+ * Emit an inexact quotient as `numerator * dims * opaque / denominator`, with
29
+ * integer parts reduced by their gcd. A lone Dim absorbs the numerator.
30
+ * @param {number} numerator
31
+ * @param {number} denominator
32
+ * @param {{value: number, unit: string, rawUnit?: string} | null} loneDim
33
+ * @param {{exponent: 1 | -1, value: number, unit: string, rawUnit?: string}[]} dims
34
+ * @param {ProductFactor[]} opaque
35
+ * @param {number | false} precision
36
+ * @return {Node | null} null when a part would be rounded on output
37
+ */
38
+ declare function rationalProduct(numerator: number, denominator: number, loneDim: {
39
+ value: number;
40
+ unit: string;
41
+ rawUnit?: string;
42
+ } | null, dims: {
43
+ exponent: 1 | -1;
44
+ value: number;
45
+ unit: string;
46
+ rawUnit?: string;
47
+ }[], opaque: ProductFactor[], precision: number | false): Node | null;
48
+ export { chainValue, foldsExactly, isScalarSum, rationalProduct };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `percent` marks a value that resolves in the same percentage context as its
3
+ * peers (a pure percentage). It is only produced at leaves, by abs(), and by
4
+ * homogeneous sums and calls — never by a product, where an unpaired
5
+ * percentage could no longer cancel against anything.
6
+ * @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown', percent?: true} | {kind: 'failure'}} CalculationType
7
+ */
8
+ export type CalculationType = {
9
+ kind: 'number';
10
+ } | {
11
+ kind: 'dimension';
12
+ base: string | null;
13
+ } | {
14
+ kind: 'unknown';
15
+ percent?: true;
16
+ } | {
17
+ kind: 'failure';
18
+ };
19
+ /** @type {CalculationType} */ declare const numberType: CalculationType;
20
+ /** @type {CalculationType} */ declare const unknownType: CalculationType;
21
+ /** @type {CalculationType} */ declare const percentageType: CalculationType;
22
+ /** @type {CalculationType} */ declare const failureType: CalculationType;
23
+ /** @param {CalculationType} type @return {boolean} */
24
+ declare function isFailure(type: CalculationType): boolean;
25
+ /** @param {CalculationType} type @return {boolean} */
26
+ declare function isPercentage(type: CalculationType): boolean;
27
+ /** @param {CalculationType} a @param {CalculationType} b @return {CalculationType} */
28
+ declare function addTypes(a: CalculationType, b: CalculationType): CalculationType;
29
+ /**
30
+ * Check an all-number function without assuming anything about an unresolved
31
+ * operand. A concrete dimension can never become a number, so it is still a
32
+ * definite error when another argument is opaque.
33
+ * @param {CalculationType[]} args
34
+ * @param {number} min
35
+ * @param {number} max
36
+ * @return {CalculationType}
37
+ */
38
+ declare function numberArguments(args: CalculationType[], min: number, max: number): CalculationType;
39
+ /**
40
+ * Check values that must share a calculation type. Unknown operands remain
41
+ * unknown: they might resolve to the concrete type required by their peers.
42
+ * @param {CalculationType[]} args
43
+ * @param {number} min
44
+ * @param {number} max
45
+ * @return {CalculationType}
46
+ */
47
+ declare function matchingArguments(args: CalculationType[], min: number, max: number): CalculationType;
48
+ export { addTypes, failureType, isFailure, isPercentage, matchingArguments, numberArguments, numberType, percentageType, unknownType, };
@@ -1,18 +0,0 @@
1
- import { analyze } from './analyze.js';
2
- import { MAX_CALCULATION_DEPTH, checkCalculationDepth } from './limits.js';
3
- export type Node = import('./node.js').Node;
4
- export type CalculationType = {
5
- kind: 'number';
6
- } | {
7
- kind: 'dimension';
8
- base: string | null;
9
- } | {
10
- kind: 'unknown';
11
- } | {
12
- kind: 'failure';
13
- };
14
- /** @typedef {import('./node.js').Node} Node */
15
- /** @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown'} | {kind: 'failure'}} CalculationType */
16
- /** @param {Node} node @return {CalculationType} */
17
- declare function checkCalculationType(node: Node): CalculationType;
18
- export { MAX_CALCULATION_DEPTH, checkCalculationDepth, checkCalculationType, analyze, };