@@ -384,3 +384,175 @@ func TestCompile_OpCallFast(t *testing.T) {
384
384
require .Equal (t , vm .OpCallFast , program .Bytecode [4 ])
385
385
require .Equal (t , 3 , program .Arguments [4 ])
386
386
}
387
+
388
+ func TestCompile_optimizes_jumps (t * testing.T ) {
389
+ env := map [string ]any {
390
+ "a" : true ,
391
+ "b" : true ,
392
+ "c" : true ,
393
+ "d" : true ,
394
+ }
395
+ type op struct {
396
+ Bytecode vm.Opcode
397
+ Arg int
398
+ }
399
+ tests := []struct {
400
+ code string
401
+ want []op
402
+ }{
403
+ {
404
+ `let foo = true; let bar = false; let baz = true; foo || bar || baz` ,
405
+ []op {
406
+ {vm .OpTrue , 0 },
407
+ {vm .OpStore , 0 },
408
+ {vm .OpFalse , 0 },
409
+ {vm .OpStore , 1 },
410
+ {vm .OpTrue , 0 },
411
+ {vm .OpStore , 2 },
412
+ {vm .OpLoadVar , 0 },
413
+ {vm .OpJumpIfTrue , 5 },
414
+ {vm .OpPop , 0 },
415
+ {vm .OpLoadVar , 1 },
416
+ {vm .OpJumpIfTrue , 2 },
417
+ {vm .OpPop , 0 },
418
+ {vm .OpLoadVar , 2 },
419
+ },
420
+ },
421
+ {
422
+ `a && b && c` ,
423
+ []op {
424
+ {vm .OpLoadFast , 0 },
425
+ {vm .OpJumpIfFalse , 5 },
426
+ {vm .OpPop , 0 },
427
+ {vm .OpLoadFast , 1 },
428
+ {vm .OpJumpIfFalse , 2 },
429
+ {vm .OpPop , 0 },
430
+ {vm .OpLoadFast , 2 },
431
+ },
432
+ },
433
+ {
434
+ `a && b || c && d` ,
435
+ []op {
436
+ {vm .OpLoadFast , 0 },
437
+ {vm .OpJumpIfFalse , 2 },
438
+ {vm .OpPop , 0 },
439
+ {vm .OpLoadFast , 1 },
440
+ {vm .OpJumpIfTrue , 5 },
441
+ {vm .OpPop , 0 },
442
+ {vm .OpLoadFast , 2 },
443
+ {vm .OpJumpIfFalse , 2 },
444
+ {vm .OpPop , 0 },
445
+ {vm .OpLoadFast , 3 },
446
+ },
447
+ },
448
+ {
449
+ `filter([1, 2, 3, 4, 5], # > 3 && # != 4 && # != 5)` ,
450
+ []op {
451
+ {vm .OpPush , 0 },
452
+ {vm .OpBegin , 0 },
453
+ {vm .OpJumpIfEnd , 26 },
454
+ {vm .OpPointer , 0 },
455
+ {vm .OpDeref , 0 },
456
+ {vm .OpPush , 1 },
457
+ {vm .OpMore , 0 },
458
+ {vm .OpJumpIfFalse , 18 },
459
+ {vm .OpPop , 0 },
460
+ {vm .OpPointer , 0 },
461
+ {vm .OpDeref , 0 },
462
+ {vm .OpPush , 2 },
463
+ {vm .OpEqual , 0 },
464
+ {vm .OpNot , 0 },
465
+ {vm .OpJumpIfFalse , 11 },
466
+ {vm .OpPop , 0 },
467
+ {vm .OpPointer , 0 },
468
+ {vm .OpDeref , 0 },
469
+ {vm .OpPush , 3 },
470
+ {vm .OpEqual , 0 },
471
+ {vm .OpNot , 0 },
472
+ {vm .OpJumpIfFalse , 4 },
473
+ {vm .OpPop , 0 },
474
+ {vm .OpIncrementCount , 0 },
475
+ {vm .OpPointer , 0 },
476
+ {vm .OpJump , 1 },
477
+ {vm .OpPop , 0 },
478
+ {vm .OpIncrementIndex , 0 },
479
+ {vm .OpJumpBackward , 27 },
480
+ {vm .OpGetCount , 0 },
481
+ {vm .OpEnd , 0 },
482
+ {vm .OpArray , 0 },
483
+ },
484
+ },
485
+ {
486
+ `let foo = true; let bar = false; let baz = true; foo && bar || baz` ,
487
+ []op {
488
+ {vm .OpTrue , 0 },
489
+ {vm .OpStore , 0 },
490
+ {vm .OpFalse , 0 },
491
+ {vm .OpStore , 1 },
492
+ {vm .OpTrue , 0 },
493
+ {vm .OpStore , 2 },
494
+ {vm .OpLoadVar , 0 },
495
+ {vm .OpJumpIfFalse , 2 },
496
+ {vm .OpPop , 0 },
497
+ {vm .OpLoadVar , 1 },
498
+ {vm .OpJumpIfTrue , 2 },
499
+ {vm .OpPop , 0 },
500
+ {vm .OpLoadVar , 2 },
501
+ },
502
+ },
503
+ {
504
+ `true ?? nil ?? nil ?? nil` ,
505
+ []op {
506
+ {vm .OpTrue , 0 },
507
+ {vm .OpJumpIfNotNil , 8 },
508
+ {vm .OpPop , 0 },
509
+ {vm .OpNil , 0 },
510
+ {vm .OpJumpIfNotNil , 5 },
511
+ {vm .OpPop , 0 },
512
+ {vm .OpNil , 0 },
513
+ {vm .OpJumpIfNotNil , 2 },
514
+ {vm .OpPop , 0 },
515
+ {vm .OpNil , 0 },
516
+ },
517
+ },
518
+ {
519
+ `let m = {"a": {"b": {"c": 1}}}; m?.a?.b?.c` ,
520
+ []op {
521
+ {vm .OpPush , 0 },
522
+ {vm .OpPush , 1 },
523
+ {vm .OpPush , 2 },
524
+ {vm .OpPush , 3 },
525
+ {vm .OpPush , 3 },
526
+ {vm .OpMap , 0 },
527
+ {vm .OpPush , 3 },
528
+ {vm .OpMap , 0 },
529
+ {vm .OpPush , 3 },
530
+ {vm .OpMap , 0 },
531
+ {vm .OpStore , 0 },
532
+ {vm .OpLoadVar , 0 },
533
+ {vm .OpJumpIfNil , 8 },
534
+ {vm .OpPush , 0 },
535
+ {vm .OpFetch , 0 },
536
+ {vm .OpJumpIfNil , 5 },
537
+ {vm .OpPush , 1 },
538
+ {vm .OpFetch , 0 },
539
+ {vm .OpJumpIfNil , 2 },
540
+ {vm .OpPush , 2 },
541
+ {vm .OpFetch , 0 },
542
+ },
543
+ },
544
+ }
545
+
546
+ for _ , test := range tests {
547
+ t .Run (test .code , func (t * testing.T ) {
548
+ program , err := expr .Compile (test .code , expr .Env (env ))
549
+ require .NoError (t , err )
550
+
551
+ require .Equal (t , len (test .want ), len (program .Bytecode ))
552
+ for i , op := range test .want {
553
+ require .Equal (t , op .Bytecode , program .Bytecode [i ])
554
+ require .Equalf (t , op .Arg , program .Arguments [i ], "at %d" , i )
555
+ }
556
+ })
557
+ }
558
+ }
0 commit comments