@@ -5,7 +5,7 @@ import { TokenType, NumberType, isTokenOpenParen, isTokenDelim, isTokenComma, is
5
5
import { addition } from '../operation/addition' ;
6
6
import { division } from '../operation/division' ;
7
7
import { isCalculation , solve } from '../calculation' ;
8
- import { isCommentNode , FunctionNode , TokenNode , isFunctionNode , isSimpleBlockNode , isTokenNode , isWhitespaceNode } from '@csstools/css-parser-algorithms' ;
8
+ import { FunctionNode , TokenNode , isFunctionNode , isSimpleBlockNode , isTokenNode , isWhiteSpaceOrCommentNode } from '@csstools/css-parser-algorithms' ;
9
9
import { multiplication } from '../operation/multiplication' ;
10
10
import { resolveGlobalsAndConstants } from './globals-and-constants' ;
11
11
import { solveACos } from './acos' ;
@@ -32,6 +32,7 @@ import { unary } from '../operation/unary';
32
32
import { solveLog } from './log' ;
33
33
import { isNone } from '../util/is-none' ;
34
34
import type { conversionOptions } from '../options' ;
35
+ import { solveRandom } from './random' ;
35
36
36
37
type mathFunction = ( node : FunctionNode , globals : Globals , options : conversionOptions ) => Calculation | - 1
37
38
@@ -51,6 +52,7 @@ export const mathFunctions: Map<string, mathFunction> = new Map([
51
52
[ 'min' , min ] ,
52
53
[ 'mod' , mod ] ,
53
54
[ 'pow' , pow ] ,
55
+ [ 'random' , random ] ,
54
56
[ 'rem' , rem ] ,
55
57
[ 'round' , round ] ,
56
58
[ 'sign' , sign ] ,
@@ -61,7 +63,7 @@ export const mathFunctions: Map<string, mathFunction> = new Map([
61
63
62
64
function calc ( calcNode : FunctionNode | SimpleBlockNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
63
65
const nodes : Array < ComponentValue | Calculation > = resolveGlobalsAndConstants (
64
- [ ...( calcNode . value . filter ( x => ! isCommentNode ( x ) && ! isWhitespaceNode ( x ) ) ) ] ,
66
+ [ ...( calcNode . value . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ) ] ,
65
67
globals ,
66
68
) ;
67
69
@@ -215,8 +217,17 @@ function calc(calcNode: FunctionNode | SimpleBlockNode, globals: Globals, option
215
217
}
216
218
217
219
function singleNodeSolver ( fnNode : FunctionNode , globals : Globals , options : conversionOptions , solveFn : ( node : FunctionNode , a : TokenNode , options : conversionOptions ) => Calculation | - 1 ) : Calculation | - 1 {
220
+ const a = singleArgument ( fnNode . value , globals , options ) ;
221
+ if ( a === - 1 ) {
222
+ return - 1 ;
223
+ }
224
+
225
+ return solveFn ( fnNode , a , options ) ;
226
+ }
227
+
228
+ function singleArgument ( values : Array < ComponentValue > , globals : Globals , options : conversionOptions ) : TokenNode | - 1 {
218
229
const nodes : Array < ComponentValue > = resolveGlobalsAndConstants (
219
- [ ...( fnNode . value . filter ( x => ! isCommentNode ( x ) && ! isWhitespaceNode ( x ) ) ) ] ,
230
+ [ ...( values . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ) ] ,
220
231
globals ,
221
232
) ;
222
233
@@ -225,12 +236,23 @@ function singleNodeSolver(fnNode: FunctionNode, globals: Globals, options: conve
225
236
return - 1 ;
226
237
}
227
238
228
- return solveFn ( fnNode , a , options ) ;
239
+ return a ;
229
240
}
230
241
231
242
function twoCommaSeparatedNodesSolver ( fnNode : FunctionNode , globals : Globals , options : conversionOptions , solveFn : ( node : FunctionNode , a : TokenNode , b : TokenNode , options : conversionOptions ) => Calculation | - 1 ) : Calculation | - 1 {
243
+ const solvedNodes = twoCommaSeparatedArguments ( fnNode . value , globals , options ) ;
244
+ if ( solvedNodes === - 1 ) {
245
+ return - 1 ;
246
+ }
247
+
248
+ const [ a , b ] = solvedNodes ;
249
+
250
+ return solveFn ( fnNode , a , b , options ) ;
251
+ }
252
+
253
+ function twoCommaSeparatedArguments ( values : Array < ComponentValue > , globals : Globals , options : conversionOptions ) : [ TokenNode , TokenNode ] | - 1 {
232
254
const nodes : Array < ComponentValue > = resolveGlobalsAndConstants (
233
- [ ...( fnNode . value . filter ( x => ! isCommentNode ( x ) && ! isWhitespaceNode ( x ) ) ) ] ,
255
+ [ ...( values . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ) ] ,
234
256
globals ,
235
257
) ;
236
258
@@ -270,20 +292,29 @@ function twoCommaSeparatedNodesSolver(fnNode: FunctionNode, globals: Globals, op
270
292
return - 1 ;
271
293
}
272
294
273
- return solveFn ( fnNode , a , b , options ) ;
295
+ return [ a , b ] ;
296
+ }
297
+
298
+ function variadicNodesSolver ( fnNode : FunctionNode , values : Array < ComponentValue > , globals : Globals , options : conversionOptions , solveFn : ( node : FunctionNode , x : Array < ComponentValue > , options : conversionOptions ) => Calculation | - 1 ) : Calculation | - 1 {
299
+ const solvedNodes = variadicArguments ( fnNode . value , globals , options ) ;
300
+ if ( solvedNodes === - 1 ) {
301
+ return - 1 ;
302
+ }
303
+
304
+ return solveFn ( fnNode , solvedNodes , options ) ;
274
305
}
275
306
276
- function variadicNodesSolver ( fnNode : FunctionNode , globals : Globals , options : conversionOptions , solveFn : ( node : FunctionNode , x : Array < ComponentValue > , options : conversionOptions ) => Calculation | - 1 ) : Calculation | - 1 {
307
+ function variadicArguments ( values : Array < ComponentValue > , globals : Globals , options : conversionOptions ) : Array < TokenNode > | - 1 {
277
308
const nodes : Array < ComponentValue > = resolveGlobalsAndConstants (
278
- [ ...( fnNode . value . filter ( x => ! isCommentNode ( x ) && ! isWhitespaceNode ( x ) ) ) ] ,
309
+ [ ...( values . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ) ] ,
279
310
globals ,
280
311
) ;
281
312
282
- const solvedNodes : Array < ComponentValue > = [ ] ;
313
+ const solvedNodes : Array < TokenNode > = [ ] ;
283
314
284
315
{
285
- const chunks = [ ] ;
286
- let chunk = [ ] ;
316
+ const chunks : Array < Array < ComponentValue > > = [ ] ;
317
+ let chunk : Array < ComponentValue > = [ ] ;
287
318
for ( let i = 0 ; i < nodes . length ; i ++ ) {
288
319
const node = nodes [ i ] ;
289
320
if ( isTokenNode ( node ) && isTokenComma ( node . value ) ) {
@@ -311,12 +342,12 @@ function variadicNodesSolver(fnNode: FunctionNode, globals: Globals, options: co
311
342
}
312
343
}
313
344
314
- return solveFn ( fnNode , solvedNodes , options ) ;
345
+ return solvedNodes ;
315
346
}
316
347
317
348
function clamp ( clampNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
318
349
const nodes : Array < ComponentValue > = resolveGlobalsAndConstants (
319
- [ ...( clampNode . value . filter ( x => ! isCommentNode ( x ) && ! isWhitespaceNode ( x ) ) ) ] ,
350
+ [ ...( clampNode . value . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ) ] ,
320
351
globals ,
321
352
) ;
322
353
@@ -394,11 +425,11 @@ function clamp(clampNode: FunctionNode, globals: Globals, options: conversionOpt
394
425
}
395
426
396
427
function max ( maxNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
397
- return variadicNodesSolver ( maxNode , globals , options , solveMax ) ;
428
+ return variadicNodesSolver ( maxNode , maxNode . value , globals , options , solveMax ) ;
398
429
}
399
430
400
431
function min ( minNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
401
- return variadicNodesSolver ( minNode , globals , options , solveMin ) ;
432
+ return variadicNodesSolver ( minNode , minNode . value , globals , options , solveMin ) ;
402
433
}
403
434
404
435
const roundingStrategies = new Set ( [
@@ -410,7 +441,7 @@ const roundingStrategies = new Set([
410
441
411
442
function round ( roundNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
412
443
const nodes : Array < ComponentValue > = resolveGlobalsAndConstants (
413
- [ ...( roundNode . value . filter ( x => ! isCommentNode ( x ) && ! isWhitespaceNode ( x ) ) ) ] ,
444
+ [ ...( roundNode . value . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ) ] ,
414
445
globals ,
415
446
) ;
416
447
@@ -537,11 +568,73 @@ function pow(powNode: FunctionNode, globals: Globals, options: conversionOptions
537
568
}
538
569
539
570
function hypot ( hypotNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
540
- return variadicNodesSolver ( hypotNode , globals , options , solveHypot ) ;
571
+ return variadicNodesSolver ( hypotNode , hypotNode . value , globals , options , solveHypot ) ;
541
572
}
542
573
543
574
function log ( logNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
544
- return variadicNodesSolver ( logNode , globals , options , solveLog ) ;
575
+ return variadicNodesSolver ( logNode , logNode . value , globals , options , solveLog ) ;
576
+ }
577
+
578
+ function random ( randomNode : FunctionNode , globals : Globals , options : conversionOptions ) : Calculation | - 1 {
579
+ const nodes : Array < ComponentValue > = randomNode . value . filter ( x => ! isWhiteSpaceOrCommentNode ( x ) ) ;
580
+
581
+ let randomCachingOptions = '' ;
582
+ const stepValues : Array < ComponentValue > = [ ]
583
+ const values : Array < ComponentValue > = [ ]
584
+
585
+ {
586
+ for ( let i = 0 ; i < nodes . length ; i ++ ) {
587
+ const node = nodes [ i ] ;
588
+ if ( ! randomCachingOptions && values . length === 0 && isTokenNode ( node ) && isTokenIdent ( node . value ) ) {
589
+ const token = node . value ;
590
+ const tokenStr = token [ 4 ] . value . toLowerCase ( ) ;
591
+ if ( tokenStr === 'per-element' || tokenStr . startsWith ( '--' ) ) {
592
+ randomCachingOptions = tokenStr ;
593
+
594
+ const nextNode = nodes [ i + 1 ] ;
595
+ if ( ! isTokenNode ( nextNode ) || ! isTokenComma ( nextNode . value ) ) {
596
+ return - 1 ;
597
+ }
598
+
599
+ i ++ ;
600
+ continue ;
601
+ }
602
+ }
603
+
604
+ if ( isTokenNode ( node ) && isTokenComma ( node . value ) ) {
605
+ const nextNode = nodes [ i + 1 ] ;
606
+
607
+ if ( values . length > 0 && isTokenNode ( nextNode ) && isTokenIdent ( nextNode . value ) ) {
608
+ const token = nextNode . value ;
609
+ const tokenStr = token [ 4 ] . value . toLowerCase ( ) ;
610
+ if ( tokenStr === 'by' || tokenStr . startsWith ( '--' ) ) {
611
+ stepValues . push ( ...nodes . slice ( i + 2 ) ) ;
612
+
613
+ break ;
614
+ }
615
+ }
616
+ }
617
+
618
+ values . push ( node ) ;
619
+ }
620
+ }
621
+
622
+ const solvedValues = twoCommaSeparatedArguments ( values , globals , options ) ;
623
+ if ( solvedValues === - 1 ) {
624
+ return - 1 ;
625
+ }
626
+
627
+ const [ a , b ] = solvedValues ;
628
+
629
+ let solvedStepValue : TokenNode | - 1 | null = null ;
630
+ if ( stepValues . length ) {
631
+ solvedStepValue = singleArgument ( stepValues , globals , options ) ;
632
+ if ( solvedStepValue === - 1 ) {
633
+ return - 1 ;
634
+ }
635
+ }
636
+
637
+ return solveRandom ( randomNode , randomCachingOptions , a , b , solvedStepValue , options ) ;
545
638
}
546
639
547
640
function calcWrapper ( v : Array < ComponentValue > ) : FunctionNode {
0 commit comments