1
1
import convertUnit from "./convertUnit" ;
2
2
3
- function isValueType ( type ) {
4
- switch ( type ) {
3
+ /**
4
+ * @param {import('../parser').CalcNode } node
5
+ * @return {node is import('../parser').ValueExpression }
6
+ */
7
+ function isValueType ( node ) {
8
+ switch ( node . type ) {
5
9
case 'LengthValue' :
6
10
case 'AngleValue' :
7
11
case 'TimeValue' :
@@ -22,22 +26,38 @@ function isValueType(type) {
22
26
return false ;
23
27
}
24
28
29
+ /** @param {'-'|'+' } operator */
25
30
function flip ( operator ) {
26
31
return operator === '+' ? '-' : '+' ;
27
32
}
28
33
34
+ /**
35
+ * @param {string } operator
36
+ * @returns {operator is '+'|'-' }
37
+ */
29
38
function isAddSubOperator ( operator ) {
30
39
return operator === '+' || operator === '-' ;
31
40
}
32
41
42
+ /**
43
+ * @typedef {{preOperator: '+'|'-', node: import('../parser').CalcNode} } Collectible
44
+ */
45
+
46
+ /**
47
+ * @param {'+'|'-' } preOperator
48
+ * @param {import('../parser').CalcNode } node
49
+ * @param {Collectible[] } collected
50
+ * @param {number } precision
51
+ */
33
52
function collectAddSubItems ( preOperator , node , collected , precision ) {
34
53
if ( ! isAddSubOperator ( preOperator ) ) { throw new Error ( `invalid operator ${ preOperator } ` ) ; }
35
- const type = node . type ;
36
- if ( isValueType ( type ) ) {
37
- const itemIndex = collected . findIndex ( x => x . node . type === type ) ;
54
+ if ( isValueType ( node ) ) {
55
+ const itemIndex = collected . findIndex ( x => x . node . type === node . type ) ;
38
56
if ( itemIndex >= 0 ) {
39
57
if ( node . value === 0 ) { return ; }
40
- const { left : reducedNode , right : current } = convertNodesUnits ( collected [ itemIndex ] . node , node , precision )
58
+ // can cast because of the criterion used to find itemIndex
59
+ const otherValueNode = /** @type import('../parser').ValueExpression*/ ( collected [ itemIndex ] . node ) ;
60
+ const { left : reducedNode , right : current } = convertNodesUnits ( otherValueNode , node , precision )
41
61
42
62
if ( collected [ itemIndex ] . preOperator === '-' ) {
43
63
collected [ itemIndex ] . preOperator = '+' ;
@@ -64,7 +84,7 @@ function collectAddSubItems(preOperator, node, collected, precision) {
64
84
collected . push ( { node, preOperator : flip ( preOperator ) } ) ;
65
85
}
66
86
}
67
- } else if ( type === "MathExpression" ) {
87
+ } else if ( node . type === "MathExpression" ) {
68
88
if ( isAddSubOperator ( node . operator ) ) {
69
89
collectAddSubItems ( preOperator , node . left , collected , precision ) ;
70
90
const collectRightOperator = preOperator === '-' ? flip ( node . operator ) : node . operator ;
@@ -85,27 +105,33 @@ function collectAddSubItems(preOperator, node, collected, precision) {
85
105
}
86
106
}
87
107
88
-
108
+ /**
109
+ * @param {import('../parser').CalcNode } node
110
+ * @param {number } precision
111
+ */
89
112
function reduceAddSubExpression ( node , precision ) {
113
+ /** @type Collectible[] */
90
114
const collected = [ ] ;
91
115
collectAddSubItems ( '+' , node , collected , precision ) ;
92
116
93
- const withoutZeroItem = collected . filter ( ( item ) => ! ( isValueType ( item . node . type ) && item . node . value === 0 ) ) ;
117
+ const withoutZeroItem = collected . filter ( ( item ) => ! ( isValueType ( item . node ) && item . node . value === 0 ) ) ;
94
118
const firstNonZeroItem = withoutZeroItem [ 0 ] ; // could be undefined
95
119
96
120
// prevent producing "calc(-var(--a))" or "calc()"
97
121
// which is invalid css
98
122
if ( ! firstNonZeroItem ||
99
123
firstNonZeroItem . preOperator === '-' &&
100
- ! isValueType ( firstNonZeroItem . node . type ) ) {
124
+ ! isValueType ( firstNonZeroItem . node ) ) {
101
125
const firstZeroItem = collected . find ( ( item ) =>
102
- isValueType ( item . node . type ) && item . node . value === 0 ) ;
103
- withoutZeroItem . unshift ( firstZeroItem )
126
+ isValueType ( item . node ) && item . node . value === 0 ) ;
127
+ if ( firstZeroItem ) {
128
+ withoutZeroItem . unshift ( firstZeroItem )
129
+ }
104
130
}
105
131
106
132
// make sure the preOperator of the first item is +
107
133
if ( withoutZeroItem [ 0 ] . preOperator === '-' &&
108
- isValueType ( withoutZeroItem [ 0 ] . node . type ) ) {
134
+ isValueType ( withoutZeroItem [ 0 ] . node ) ) {
109
135
withoutZeroItem [ 0 ] . node . value *= - 1 ;
110
136
withoutZeroItem [ 0 ] . preOperator = '+' ;
111
137
}
@@ -122,9 +148,11 @@ function reduceAddSubExpression(node, precision) {
122
148
123
149
return root ;
124
150
}
125
-
151
+ /**
152
+ * @param {import('../parser').MathExpression } node
153
+ */
126
154
function reduceDivisionExpression ( node ) {
127
- if ( ! isValueType ( node . right . type ) ) {
155
+ if ( ! isValueType ( node . right ) ) {
128
156
return node ;
129
157
}
130
158
@@ -135,12 +163,18 @@ function reduceDivisionExpression(node) {
135
163
return applyNumberDivision ( node . left , node . right . value )
136
164
}
137
165
138
- // apply (expr) / number
166
+ /**
167
+ * apply (expr) / number
168
+ *
169
+ * @param {import('../parser').CalcNode } node
170
+ * @param {number } divisor
171
+ * @return {import('../parser').CalcNode }
172
+ */
139
173
function applyNumberDivision ( node , divisor ) {
140
174
if ( divisor === 0 ) {
141
175
throw new Error ( 'Cannot divide by zero' ) ;
142
176
}
143
- if ( isValueType ( node . type ) ) {
177
+ if ( isValueType ( node ) ) {
144
178
node . value /= divisor ;
145
179
return node ;
146
180
}
@@ -169,7 +203,9 @@ function applyNumberDivision(node, divisor) {
169
203
}
170
204
}
171
205
}
172
-
206
+ /**
207
+ * @param {import('../parser').MathExpression } node
208
+ */
173
209
function reduceMultiplicationExpression ( node ) {
174
210
// (expr) * number
175
211
if ( node . right . type === 'Number' ) {
@@ -182,9 +218,14 @@ function reduceMultiplicationExpression(node) {
182
218
return node ;
183
219
}
184
220
185
- // apply (expr) / number
221
+ /**
222
+ * apply (expr) * number
223
+ * @param {number } multiplier
224
+ * @param {import('../parser').CalcNode } node
225
+ * @return {import('../parser').CalcNode }
226
+ */
186
227
function applyNumberMultiplication ( node , multiplier ) {
187
- if ( isValueType ( node . type ) ) {
228
+ if ( isValueType ( node ) ) {
188
229
node . value *= multiplier ;
189
230
return node ;
190
231
}
@@ -214,6 +255,11 @@ function applyNumberMultiplication(node, multiplier) {
214
255
}
215
256
}
216
257
258
+ /**
259
+ * @param {import('../parser').ValueExpression } left
260
+ * @param {import('../parser').ValueExpression } right
261
+ * @param {number } precision
262
+ */
217
263
function convertNodesUnits ( left , right , precision ) {
218
264
switch ( left . type ) {
219
265
case 'LengthValue' :
@@ -237,6 +283,10 @@ function convertNodesUnits(left, right, precision) {
237
283
}
238
284
}
239
285
286
+ /**
287
+ * @param {import('../parser').CalcNode } node
288
+ * @param {number } precision
289
+ */
240
290
function reduce ( node , precision ) {
241
291
if ( node . type === "MathExpression" ) {
242
292
if ( isAddSubOperator ( node . operator ) ) {
@@ -247,9 +297,9 @@ function reduce(node, precision) {
247
297
node . right = reduce ( node . right , precision ) ;
248
298
switch ( node . operator ) {
249
299
case "/" :
250
- return reduceDivisionExpression ( node , precision ) ;
300
+ return reduceDivisionExpression ( node ) ;
251
301
case "*" :
252
- return reduceMultiplicationExpression ( node , precision ) ;
302
+ return reduceMultiplicationExpression ( node ) ;
253
303
}
254
304
255
305
return node ;
0 commit comments