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.
- package/README.md +17 -33
- package/package.json +8 -8
- package/src/lib/function-analyzers.js +119 -0
- package/src/lib/functions.js +25 -175
- package/src/lib/parser/opaque.js +3 -2
- package/src/lib/serialize/expression.js +110 -157
- package/src/lib/serialize/precedence.js +47 -0
- package/src/lib/serialize/precision.js +21 -21
- package/src/lib/serialize.js +1 -1
- package/src/lib/simplify/product-barrier.js +55 -0
- package/src/lib/simplify/product-quotient.js +141 -0
- package/src/lib/simplify/product.js +7 -178
- package/src/lib/types.js +94 -0
- package/types/lib/function-analyzers.d.ts +27 -0
- package/types/lib/functions.d.ts +3 -28
- package/types/lib/serialize/expression.d.ts +9 -49
- package/types/lib/serialize/precedence.d.ts +18 -0
- package/types/lib/serialize/precision.d.ts +11 -10
- package/types/lib/simplify/product-barrier.d.ts +26 -0
- package/types/lib/simplify/product-quotient.d.ts +48 -0
- package/types/lib/types.d.ts +48 -0
- package/types/lib/calculation-type.d.ts +0 -18
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { mkSum, mkProduct, mkGroup, num, dim } from '../node.js';
|
|
2
2
|
import { tryCancelPair } from './cancel.js';
|
|
3
|
-
import {
|
|
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
|
package/src/lib/types.js
ADDED
|
@@ -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, };
|
package/types/lib/functions.d.ts
CHANGED
|
@@ -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
|
|
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').
|
|
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 {
|
|
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, };
|