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,31 @@ 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
+ withoutZeroItem . unshift ( /** @type Collectible*/ ( firstZeroItem ) )
104
128
}
105
129
106
130
// make sure the preOperator of the first item is +
107
131
if ( withoutZeroItem [ 0 ] . preOperator === '-' &&
108
- isValueType ( withoutZeroItem [ 0 ] . node . type ) ) {
132
+ isValueType ( withoutZeroItem [ 0 ] . node ) ) {
109
133
withoutZeroItem [ 0 ] . node . value *= - 1 ;
110
134
withoutZeroItem [ 0 ] . preOperator = '+' ;
111
135
}
@@ -122,9 +146,11 @@ function reduceAddSubExpression(node, precision) {
122
146
123
147
return root ;
124
148
}
125
-
149
+ /**
150
+ * @param {import('../parser').MathExpression } node
151
+ */
126
152
function reduceDivisionExpression ( node ) {
127
- if ( ! isValueType ( node . right . type ) ) {
153
+ if ( ! isValueType ( node . right ) ) {
128
154
return node ;
129
155
}
130
156
@@ -135,12 +161,18 @@ function reduceDivisionExpression(node) {
135
161
return applyNumberDivision ( node . left , node . right . value )
136
162
}
137
163
138
- // apply (expr) / number
164
+ /**
165
+ * apply (expr) / number
166
+ *
167
+ * @param {import('../parser').CalcNode } node
168
+ * @param {number } divisor
169
+ * @return {import('../parser').CalcNode }
170
+ */
139
171
function applyNumberDivision ( node , divisor ) {
140
172
if ( divisor === 0 ) {
141
173
throw new Error ( 'Cannot divide by zero' ) ;
142
174
}
143
- if ( isValueType ( node . type ) ) {
175
+ if ( isValueType ( node ) ) {
144
176
node . value /= divisor ;
145
177
return node ;
146
178
}
@@ -169,7 +201,9 @@ function applyNumberDivision(node, divisor) {
169
201
}
170
202
}
171
203
}
172
-
204
+ /**
205
+ * @param {import('../parser').MathExpression } node
206
+ */
173
207
function reduceMultiplicationExpression ( node ) {
174
208
// (expr) * number
175
209
if ( node . right . type === 'Number' ) {
@@ -182,9 +216,14 @@ function reduceMultiplicationExpression(node) {
182
216
return node ;
183
217
}
184
218
185
- // apply (expr) / number
219
+ /**
220
+ * apply (expr) * number
221
+ * @param {number } multiplier
222
+ * @param {import('../parser').CalcNode } node
223
+ * @return {import('../parser').CalcNode }
224
+ */
186
225
function applyNumberMultiplication ( node , multiplier ) {
187
- if ( isValueType ( node . type ) ) {
226
+ if ( isValueType ( node ) ) {
188
227
node . value *= multiplier ;
189
228
return node ;
190
229
}
@@ -214,6 +253,11 @@ function applyNumberMultiplication(node, multiplier) {
214
253
}
215
254
}
216
255
256
+ /**
257
+ * @param {import('../parser').ValueExpression } left
258
+ * @param {import('../parser').ValueExpression } right
259
+ * @param {number } precision
260
+ */
217
261
function convertNodesUnits ( left , right , precision ) {
218
262
switch ( left . type ) {
219
263
case 'LengthValue' :
@@ -237,6 +281,10 @@ function convertNodesUnits(left, right, precision) {
237
281
}
238
282
}
239
283
284
+ /**
285
+ * @param {import('../parser').CalcNode } node
286
+ * @param {number } precision
287
+ */
240
288
function reduce ( node , precision ) {
241
289
if ( node . type === "MathExpression" ) {
242
290
if ( isAddSubOperator ( node . operator ) ) {
@@ -247,9 +295,9 @@ function reduce(node, precision) {
247
295
node . right = reduce ( node . right , precision ) ;
248
296
switch ( node . operator ) {
249
297
case "/" :
250
- return reduceDivisionExpression ( node , precision ) ;
298
+ return reduceDivisionExpression ( node ) ;
251
299
case "*" :
252
- return reduceMultiplicationExpression ( node , precision ) ;
300
+ return reduceMultiplicationExpression ( node ) ;
253
301
}
254
302
255
303
return node ;
0 commit comments