github.com/expr-lang/expr@v1.16.9/expr_test.go (about) 1 package expr_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "os" 8 "reflect" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/expr-lang/expr/internal/testify/assert" 14 "github.com/expr-lang/expr/internal/testify/require" 15 16 "github.com/expr-lang/expr" 17 "github.com/expr-lang/expr/ast" 18 "github.com/expr-lang/expr/file" 19 "github.com/expr-lang/expr/test/mock" 20 ) 21 22 func ExampleEval() { 23 output, err := expr.Eval("greet + name", map[string]any{ 24 "greet": "Hello, ", 25 "name": "world!", 26 }) 27 if err != nil { 28 fmt.Printf("err: %v", err) 29 return 30 } 31 32 fmt.Printf("%v", output) 33 34 // Output: Hello, world! 35 } 36 37 func ExampleEval_runtime_error() { 38 _, err := expr.Eval(`map(1..3, {1 % (# - 3)})`, nil) 39 fmt.Print(err) 40 41 // Output: runtime error: integer divide by zero (1:14) 42 // | map(1..3, {1 % (# - 3)}) 43 // | .............^ 44 } 45 46 func ExampleCompile() { 47 env := map[string]any{ 48 "foo": 1, 49 "bar": 99, 50 } 51 52 program, err := expr.Compile("foo in 1..99 and bar in 1..99", expr.Env(env)) 53 if err != nil { 54 fmt.Printf("%v", err) 55 return 56 } 57 58 output, err := expr.Run(program, env) 59 if err != nil { 60 fmt.Printf("%v", err) 61 return 62 } 63 64 fmt.Printf("%v", output) 65 66 // Output: true 67 } 68 69 func ExampleEnv() { 70 type Segment struct { 71 Origin string 72 } 73 type Passengers struct { 74 Adults int 75 } 76 type Meta struct { 77 Tags map[string]string 78 } 79 type Env struct { 80 Meta 81 Segments []*Segment 82 Passengers *Passengers 83 Marker string 84 } 85 86 code := `all(Segments, {.Origin == "MOW"}) && Passengers.Adults > 0 && Tags["foo"] startsWith "bar"` 87 88 program, err := expr.Compile(code, expr.Env(Env{})) 89 if err != nil { 90 fmt.Printf("%v", err) 91 return 92 } 93 94 env := Env{ 95 Meta: Meta{ 96 Tags: map[string]string{ 97 "foo": "bar", 98 }, 99 }, 100 Segments: []*Segment{ 101 {Origin: "MOW"}, 102 }, 103 Passengers: &Passengers{ 104 Adults: 2, 105 }, 106 Marker: "test", 107 } 108 109 output, err := expr.Run(program, env) 110 if err != nil { 111 fmt.Printf("%v", err) 112 return 113 } 114 115 fmt.Printf("%v", output) 116 117 // Output: true 118 } 119 120 func ExampleEnv_tagged_field_names() { 121 env := struct { 122 FirstWord string 123 Separator string `expr:"Space"` 124 SecondWord string `expr:"second_word"` 125 }{ 126 FirstWord: "Hello", 127 Separator: " ", 128 SecondWord: "World", 129 } 130 131 output, err := expr.Eval(`FirstWord + Space + second_word`, env) 132 if err != nil { 133 fmt.Printf("%v", err) 134 return 135 } 136 137 fmt.Printf("%v", output) 138 139 // Output : Hello World 140 } 141 142 func ExampleAsKind() { 143 program, err := expr.Compile("{a: 1, b: 2}", expr.AsKind(reflect.Map)) 144 if err != nil { 145 fmt.Printf("%v", err) 146 return 147 } 148 149 output, err := expr.Run(program, nil) 150 if err != nil { 151 fmt.Printf("%v", err) 152 return 153 } 154 155 fmt.Printf("%v", output) 156 157 // Output: map[a:1 b:2] 158 } 159 160 func ExampleAsBool() { 161 env := map[string]int{ 162 "foo": 0, 163 } 164 165 program, err := expr.Compile("foo >= 0", expr.Env(env), expr.AsBool()) 166 if err != nil { 167 fmt.Printf("%v", err) 168 return 169 } 170 171 output, err := expr.Run(program, env) 172 if err != nil { 173 fmt.Printf("%v", err) 174 return 175 } 176 177 fmt.Printf("%v", output.(bool)) 178 179 // Output: true 180 } 181 182 func ExampleAsBool_error() { 183 env := map[string]any{ 184 "foo": 0, 185 } 186 187 _, err := expr.Compile("foo + 42", expr.Env(env), expr.AsBool()) 188 189 fmt.Printf("%v", err) 190 191 // Output: expected bool, but got int 192 } 193 194 func ExampleAsInt() { 195 program, err := expr.Compile("42", expr.AsInt()) 196 if err != nil { 197 fmt.Printf("%v", err) 198 return 199 } 200 201 output, err := expr.Run(program, nil) 202 if err != nil { 203 fmt.Printf("%v", err) 204 return 205 } 206 207 fmt.Printf("%T(%v)", output, output) 208 209 // Output: int(42) 210 } 211 212 func ExampleAsInt64() { 213 env := map[string]any{ 214 "rating": 5.5, 215 } 216 217 program, err := expr.Compile("rating", expr.Env(env), expr.AsInt64()) 218 if err != nil { 219 fmt.Printf("%v", err) 220 return 221 } 222 223 output, err := expr.Run(program, env) 224 if err != nil { 225 fmt.Printf("%v", err) 226 return 227 } 228 229 fmt.Printf("%v", output.(int64)) 230 231 // Output: 5 232 } 233 234 func ExampleAsFloat64() { 235 program, err := expr.Compile("42", expr.AsFloat64()) 236 if err != nil { 237 fmt.Printf("%v", err) 238 return 239 } 240 241 output, err := expr.Run(program, nil) 242 if err != nil { 243 fmt.Printf("%v", err) 244 return 245 } 246 247 fmt.Printf("%v", output.(float64)) 248 249 // Output: 42 250 } 251 252 func ExampleAsFloat64_error() { 253 _, err := expr.Compile(`!!true`, expr.AsFloat64()) 254 255 fmt.Printf("%v", err) 256 257 // Output: expected float64, but got bool 258 } 259 260 func ExampleWarnOnAny() { 261 // Arrays always have []any type. The expression return type is any. 262 // AsInt() instructs compiler to expect int or any, and cast to int, 263 // if possible. WarnOnAny() instructs to return an error on any type. 264 _, err := expr.Compile(`[42, true, "yes"][0]`, expr.AsInt(), expr.WarnOnAny()) 265 266 fmt.Printf("%v", err) 267 268 // Output: expected int, but got interface {} 269 } 270 271 func ExampleOperator() { 272 code := ` 273 Now() > CreatedAt && 274 (Now() - CreatedAt).Hours() > 24 275 ` 276 277 type Env struct { 278 CreatedAt time.Time 279 Now func() time.Time 280 Sub func(a, b time.Time) time.Duration 281 After func(a, b time.Time) bool 282 } 283 284 options := []expr.Option{ 285 expr.Env(Env{}), 286 expr.Operator(">", "After"), 287 expr.Operator("-", "Sub"), 288 } 289 290 program, err := expr.Compile(code, options...) 291 if err != nil { 292 fmt.Printf("%v", err) 293 return 294 } 295 296 env := Env{ 297 CreatedAt: time.Date(2018, 7, 14, 0, 0, 0, 0, time.UTC), 298 Now: func() time.Time { return time.Now() }, 299 Sub: func(a, b time.Time) time.Duration { return a.Sub(b) }, 300 After: func(a, b time.Time) bool { return a.After(b) }, 301 } 302 303 output, err := expr.Run(program, env) 304 if err != nil { 305 fmt.Printf("%v", err) 306 return 307 } 308 309 fmt.Printf("%v", output) 310 311 // Output: true 312 } 313 314 func ExampleOperator_Decimal() { 315 type Decimal struct{ N float64 } 316 code := `A + B - C` 317 318 type Env struct { 319 A, B, C Decimal 320 Sub func(a, b Decimal) Decimal 321 Add func(a, b Decimal) Decimal 322 } 323 324 options := []expr.Option{ 325 expr.Env(Env{}), 326 expr.Operator("+", "Add"), 327 expr.Operator("-", "Sub"), 328 } 329 330 program, err := expr.Compile(code, options...) 331 if err != nil { 332 fmt.Printf("Compile error: %v", err) 333 return 334 } 335 336 env := Env{ 337 A: Decimal{3}, 338 B: Decimal{2}, 339 C: Decimal{1}, 340 Sub: func(a, b Decimal) Decimal { return Decimal{a.N - b.N} }, 341 Add: func(a, b Decimal) Decimal { return Decimal{a.N + b.N} }, 342 } 343 344 output, err := expr.Run(program, env) 345 if err != nil { 346 fmt.Printf("%v", err) 347 return 348 } 349 350 fmt.Printf("%v", output) 351 352 // Output: {4} 353 } 354 355 func fib(n int) int { 356 if n <= 1 { 357 return n 358 } 359 return fib(n-1) + fib(n-2) 360 } 361 362 func ExampleConstExpr() { 363 code := `[fib(5), fib(3+3), fib(dyn)]` 364 365 env := map[string]any{ 366 "fib": fib, 367 "dyn": 0, 368 } 369 370 options := []expr.Option{ 371 expr.Env(env), 372 expr.ConstExpr("fib"), // Mark fib func as constant expression. 373 } 374 375 program, err := expr.Compile(code, options...) 376 if err != nil { 377 fmt.Printf("%v", err) 378 return 379 } 380 381 // Only fib(5) and fib(6) calculated on Compile, fib(dyn) can be called at runtime. 382 env["dyn"] = 7 383 384 output, err := expr.Run(program, env) 385 if err != nil { 386 fmt.Printf("%v", err) 387 return 388 } 389 390 fmt.Printf("%v\n", output) 391 392 // Output: [5 8 13] 393 } 394 395 func ExampleAllowUndefinedVariables() { 396 code := `name == nil ? "Hello, world!" : sprintf("Hello, %v!", name)` 397 398 env := map[string]any{ 399 "sprintf": fmt.Sprintf, 400 } 401 402 options := []expr.Option{ 403 expr.Env(env), 404 expr.AllowUndefinedVariables(), // Allow to use undefined variables. 405 } 406 407 program, err := expr.Compile(code, options...) 408 if err != nil { 409 fmt.Printf("%v", err) 410 return 411 } 412 413 output, err := expr.Run(program, env) 414 if err != nil { 415 fmt.Printf("%v", err) 416 return 417 } 418 fmt.Printf("%v\n", output) 419 420 env["name"] = "you" // Define variables later on. 421 422 output, err = expr.Run(program, env) 423 if err != nil { 424 fmt.Printf("%v", err) 425 return 426 } 427 fmt.Printf("%v\n", output) 428 429 // Output: Hello, world! 430 // Hello, you! 431 } 432 433 func ExampleAllowUndefinedVariables_zero_value() { 434 code := `name == "" ? foo + bar : foo + name` 435 436 // If environment has different zero values, then undefined variables 437 // will have it as default value. 438 env := map[string]string{} 439 440 options := []expr.Option{ 441 expr.Env(env), 442 expr.AllowUndefinedVariables(), // Allow to use undefined variables. 443 } 444 445 program, err := expr.Compile(code, options...) 446 if err != nil { 447 fmt.Printf("%v", err) 448 return 449 } 450 451 env = map[string]string{ 452 "foo": "Hello, ", 453 "bar": "world!", 454 } 455 456 output, err := expr.Run(program, env) 457 if err != nil { 458 fmt.Printf("%v", err) 459 return 460 } 461 fmt.Printf("%v", output) 462 463 // Output: Hello, world! 464 } 465 466 func ExampleAllowUndefinedVariables_zero_value_functions() { 467 code := `words == "" ? Split("foo,bar", ",") : Split(words, ",")` 468 469 // Env is map[string]string type on which methods are defined. 470 env := mock.MapStringStringEnv{} 471 472 options := []expr.Option{ 473 expr.Env(env), 474 expr.AllowUndefinedVariables(), // Allow to use undefined variables. 475 } 476 477 program, err := expr.Compile(code, options...) 478 if err != nil { 479 fmt.Printf("%v", err) 480 return 481 } 482 483 output, err := expr.Run(program, env) 484 if err != nil { 485 fmt.Printf("%v", err) 486 return 487 } 488 fmt.Printf("%v", output) 489 490 // Output: [foo bar] 491 } 492 493 type patcher struct{} 494 495 func (p *patcher) Visit(node *ast.Node) { 496 switch n := (*node).(type) { 497 case *ast.MemberNode: 498 ast.Patch(node, &ast.CallNode{ 499 Callee: &ast.IdentifierNode{Value: "get"}, 500 Arguments: []ast.Node{n.Node, n.Property}, 501 }) 502 } 503 } 504 505 func ExamplePatch() { 506 /* 507 type patcher struct{} 508 509 func (p *patcher) Visit(node *ast.Node) { 510 switch n := (*node).(type) { 511 case *ast.MemberNode: 512 ast.Patch(node, &ast.CallNode{ 513 Callee: &ast.IdentifierNode{Value: "get"}, 514 Arguments: []ast.Node{n.Node, n.Property}, 515 }) 516 } 517 } 518 */ 519 520 program, err := expr.Compile( 521 `greet.you.world + "!"`, 522 expr.Patch(&patcher{}), 523 ) 524 if err != nil { 525 fmt.Printf("%v", err) 526 return 527 } 528 529 env := map[string]any{ 530 "greet": "Hello", 531 "get": func(a, b string) string { 532 return a + ", " + b 533 }, 534 } 535 536 output, err := expr.Run(program, env) 537 if err != nil { 538 fmt.Printf("%v", err) 539 return 540 } 541 fmt.Printf("%v", output) 542 543 // Output : Hello, you, world! 544 } 545 546 func ExampleWithContext() { 547 env := map[string]any{ 548 "fn": func(ctx context.Context, _, _ int) int { 549 // An infinite loop that can be canceled by context. 550 for { 551 select { 552 case <-ctx.Done(): 553 return 42 554 } 555 } 556 }, 557 "ctx": context.TODO(), // Context should be passed as a variable. 558 } 559 560 program, err := expr.Compile(`fn(1, 2)`, 561 expr.Env(env), 562 expr.WithContext("ctx"), // Pass context variable name. 563 ) 564 if err != nil { 565 fmt.Printf("%v", err) 566 return 567 } 568 569 // Cancel context after 100 milliseconds. 570 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) 571 defer cancel() 572 573 // After program is compiled, context can be passed to Run. 574 env["ctx"] = ctx 575 576 // Run will return 42 after 100 milliseconds. 577 output, err := expr.Run(program, env) 578 if err != nil { 579 fmt.Printf("%v", err) 580 return 581 } 582 583 fmt.Printf("%v", output) 584 // Output: 42 585 } 586 587 func ExampleWithTimezone() { 588 program, err := expr.Compile(`now().Location().String()`, expr.Timezone("Asia/Kamchatka")) 589 if err != nil { 590 fmt.Printf("%v", err) 591 return 592 } 593 594 output, err := expr.Run(program, nil) 595 if err != nil { 596 fmt.Printf("%v", err) 597 return 598 } 599 600 fmt.Printf("%v", output) 601 // Output: Asia/Kamchatka 602 } 603 604 func TestExpr_readme_example(t *testing.T) { 605 env := map[string]any{ 606 "greet": "Hello, %v!", 607 "names": []string{"world", "you"}, 608 "sprintf": fmt.Sprintf, 609 } 610 611 code := `sprintf(greet, names[0])` 612 613 program, err := expr.Compile(code, expr.Env(env)) 614 require.NoError(t, err) 615 616 output, err := expr.Run(program, env) 617 require.NoError(t, err) 618 619 require.Equal(t, "Hello, world!", output) 620 } 621 622 func TestExpr(t *testing.T) { 623 date := time.Date(2017, time.October, 23, 18, 30, 0, 0, time.UTC) 624 oneDay, _ := time.ParseDuration("24h") 625 timeNowPlusOneDay := date.Add(oneDay) 626 627 env := mock.Env{ 628 Embed: mock.Embed{}, 629 Ambiguous: "", 630 Any: nil, 631 Bool: true, 632 Float: 0, 633 Int64: 0, 634 Int32: 0, 635 Int: 0, 636 One: 1, 637 Two: 2, 638 Uint32: 0, 639 String: "string", 640 BoolPtr: nil, 641 FloatPtr: nil, 642 IntPtr: nil, 643 IntPtrPtr: nil, 644 StringPtr: nil, 645 Foo: mock.Foo{ 646 Value: "foo", 647 Bar: mock.Bar{ 648 Baz: "baz", 649 }, 650 }, 651 Abstract: nil, 652 ArrayOfAny: nil, 653 ArrayOfInt: []int{1, 2, 3, 4, 5}, 654 ArrayOfFoo: []*mock.Foo{{Value: "foo"}, {Value: "bar"}, {Value: "baz"}}, 655 MapOfFoo: nil, 656 MapOfAny: nil, 657 FuncParam: nil, 658 FuncParamAny: nil, 659 FuncTooManyReturns: nil, 660 FuncNamed: nil, 661 NilAny: nil, 662 NilFn: nil, 663 NilStruct: nil, 664 Variadic: func(head int, xs ...int) bool { 665 sum := 0 666 for _, x := range xs { 667 sum += x 668 } 669 return head == sum 670 }, 671 Fast: nil, 672 Time: date, 673 TimePlusDay: timeNowPlusOneDay, 674 Duration: oneDay, 675 } 676 677 tests := []struct { 678 code string 679 want any 680 }{ 681 { 682 `1`, 683 1, 684 }, 685 { 686 `-.5`, 687 -.5, 688 }, 689 { 690 `true && false || false`, 691 false, 692 }, 693 { 694 `Int == 0 && Int32 == 0 && Int64 == 0 && Float64 == 0 && Bool && String == "string"`, 695 true, 696 }, 697 { 698 `-Int64 == 0`, 699 true, 700 }, 701 { 702 `"a" != "b"`, 703 true, 704 }, 705 { 706 `"a" != "b" || 1 == 2`, 707 true, 708 }, 709 { 710 `Int + 0`, 711 0, 712 }, 713 { 714 `Uint64 + 0`, 715 0, 716 }, 717 { 718 `Uint64 + Int64`, 719 0, 720 }, 721 { 722 `Int32 + Int64`, 723 0, 724 }, 725 { 726 `Float64 + 0`, 727 float64(0), 728 }, 729 { 730 `0 + Float64`, 731 float64(0), 732 }, 733 { 734 `0 <= Float64`, 735 true, 736 }, 737 { 738 `Float64 < 1`, 739 true, 740 }, 741 { 742 `Int < 1`, 743 true, 744 }, 745 { 746 `2 + 2 == 4`, 747 true, 748 }, 749 { 750 `8 % 3`, 751 2, 752 }, 753 { 754 `2 ** 8`, 755 float64(256), 756 }, 757 { 758 `2 ^ 8`, 759 float64(256), 760 }, 761 { 762 `-(2-5)**3-2/(+4-3)+-2`, 763 float64(23), 764 }, 765 { 766 `"hello" + " " + "world"`, 767 "hello world", 768 }, 769 { 770 `0 in -1..1 and 1 in 1..1`, 771 true, 772 }, 773 { 774 `Int in 0..1`, 775 true, 776 }, 777 { 778 `Int32 in 0..1`, 779 true, 780 }, 781 { 782 `Int64 in 0..1`, 783 true, 784 }, 785 { 786 `1 in [1, 2, 3] && "foo" in {foo: 0, bar: 1} && "Bar" in Foo`, 787 true, 788 }, 789 { 790 `1 in [1.5] || 1 not in [1]`, 791 false, 792 }, 793 { 794 `One in 0..1 && Two not in 0..1`, 795 true, 796 }, 797 { 798 `Two not in 0..1`, 799 true, 800 }, 801 { 802 `Two not in 0..1`, 803 true, 804 }, 805 { 806 `-1 not in [1]`, 807 true, 808 }, 809 { 810 `Int32 in [10, 20]`, 811 false, 812 }, 813 { 814 `String matches "s.+"`, 815 true, 816 }, 817 { 818 `String matches ("^" + String + "$")`, 819 true, 820 }, 821 { 822 `'foo' + 'bar' not matches 'foobar'`, 823 false, 824 }, 825 { 826 `"foobar" contains "bar"`, 827 true, 828 }, 829 { 830 `"foobar" startsWith "foo"`, 831 true, 832 }, 833 { 834 `"foobar" endsWith "bar"`, 835 true, 836 }, 837 { 838 `(0..10)[5]`, 839 5, 840 }, 841 { 842 `Foo.Bar.Baz`, 843 "baz", 844 }, 845 { 846 `Add(10, 5) + GetInt()`, 847 15, 848 }, 849 { 850 `Foo.Method().Baz`, 851 `baz (from Foo.Method)`, 852 }, 853 { 854 `Foo.MethodWithArgs("prefix ")`, 855 "prefix foo", 856 }, 857 { 858 `len([1, 2, 3])`, 859 3, 860 }, 861 { 862 `len([1, Two, 3])`, 863 3, 864 }, 865 { 866 `len(["hello", "world"])`, 867 2, 868 }, 869 { 870 `len("hello, world")`, 871 12, 872 }, 873 { 874 `len(ArrayOfInt)`, 875 5, 876 }, 877 { 878 `len({a: 1, b: 2, c: 2})`, 879 3, 880 }, 881 { 882 `max([1, 2, 3])`, 883 3, 884 }, 885 { 886 `max(1, 2, 3)`, 887 3, 888 }, 889 { 890 `min([1, 2, 3])`, 891 1, 892 }, 893 { 894 `min(1, 2, 3)`, 895 1, 896 }, 897 { 898 `{foo: 0, bar: 1}`, 899 map[string]any{"foo": 0, "bar": 1}, 900 }, 901 { 902 `{foo: 0, bar: 1}`, 903 map[string]any{"foo": 0, "bar": 1}, 904 }, 905 { 906 `(true ? 0+1 : 2+3) + (false ? -1 : -2)`, 907 -1, 908 }, 909 { 910 `filter(1..9, {# > 7})`, 911 []any{8, 9}, 912 }, 913 { 914 `map(1..3, {# * #})`, 915 []any{1, 4, 9}, 916 }, 917 { 918 `all(1..3, {# > 0})`, 919 true, 920 }, 921 { 922 `count(1..30, {# % 3 == 0})`, 923 10, 924 }, 925 { 926 `count([true, true, false])`, 927 2, 928 }, 929 { 930 `"a" < "b"`, 931 true, 932 }, 933 { 934 `Time.Sub(Time).String() == "0s"`, 935 true, 936 }, 937 { 938 `1 + 1`, 939 2, 940 }, 941 { 942 `(One * Two) * 3 == One * (Two * 3)`, 943 true, 944 }, 945 { 946 `ArrayOfInt[1]`, 947 2, 948 }, 949 { 950 `ArrayOfInt[0] < ArrayOfInt[1]`, 951 true, 952 }, 953 { 954 `ArrayOfInt[-1]`, 955 5, 956 }, 957 { 958 `ArrayOfInt[1:2]`, 959 []int{2}, 960 }, 961 { 962 `ArrayOfInt[1:4]`, 963 []int{2, 3, 4}, 964 }, 965 { 966 `ArrayOfInt[-4:-1]`, 967 []int{2, 3, 4}, 968 }, 969 { 970 `ArrayOfInt[:3]`, 971 []int{1, 2, 3}, 972 }, 973 { 974 `ArrayOfInt[3:]`, 975 []int{4, 5}, 976 }, 977 { 978 `ArrayOfInt[0:5] == ArrayOfInt`, 979 true, 980 }, 981 { 982 `ArrayOfInt[0:] == ArrayOfInt`, 983 true, 984 }, 985 { 986 `ArrayOfInt[:5] == ArrayOfInt`, 987 true, 988 }, 989 { 990 `ArrayOfInt[:] == ArrayOfInt`, 991 true, 992 }, 993 { 994 `4 in 5..1`, 995 false, 996 }, 997 { 998 `4..0`, 999 []int{}, 1000 }, 1001 { 1002 `NilStruct`, 1003 (*mock.Foo)(nil), 1004 }, 1005 { 1006 `NilAny == nil && nil == NilAny && nil == nil && NilAny == NilAny && NilInt == nil && NilSlice == nil && NilStruct == nil`, 1007 true, 1008 }, 1009 { 1010 `0 == nil || "str" == nil || true == nil`, 1011 false, 1012 }, 1013 { 1014 `Variadic(6, 1, 2, 3)`, 1015 true, 1016 }, 1017 { 1018 `Variadic(0)`, 1019 true, 1020 }, 1021 { 1022 `String[:]`, 1023 "string", 1024 }, 1025 { 1026 `String[:3]`, 1027 "str", 1028 }, 1029 { 1030 `String[:9]`, 1031 "string", 1032 }, 1033 { 1034 `String[3:9]`, 1035 "ing", 1036 }, 1037 { 1038 `String[7:9]`, 1039 "", 1040 }, 1041 { 1042 `map(filter(ArrayOfInt, # >= 3), # + 1)`, 1043 []any{4, 5, 6}, 1044 }, 1045 { 1046 `Time < Time + Duration`, 1047 true, 1048 }, 1049 { 1050 `Time + Duration > Time`, 1051 true, 1052 }, 1053 { 1054 `Time == Time`, 1055 true, 1056 }, 1057 { 1058 `Time >= Time`, 1059 true, 1060 }, 1061 { 1062 `Time <= Time`, 1063 true, 1064 }, 1065 { 1066 `Time == Time + Duration`, 1067 false, 1068 }, 1069 { 1070 `Time != Time`, 1071 false, 1072 }, 1073 { 1074 `TimePlusDay - Duration`, 1075 date, 1076 }, 1077 { 1078 `duration("1h") == duration("1h")`, 1079 true, 1080 }, 1081 { 1082 `TimePlusDay - Time >= duration("24h")`, 1083 true, 1084 }, 1085 { 1086 `duration("1h") > duration("1m")`, 1087 true, 1088 }, 1089 { 1090 `duration("1h") < duration("1m")`, 1091 false, 1092 }, 1093 { 1094 `duration("1h") >= duration("1m")`, 1095 true, 1096 }, 1097 { 1098 `duration("1h") <= duration("1m")`, 1099 false, 1100 }, 1101 { 1102 `duration("1h") > duration("1m")`, 1103 true, 1104 }, 1105 { 1106 `duration("1h") + duration("1m")`, 1107 time.Hour + time.Minute, 1108 }, 1109 { 1110 `7 * duration("1h")`, 1111 7 * time.Hour, 1112 }, 1113 { 1114 `duration("1h") * 7`, 1115 7 * time.Hour, 1116 }, 1117 { 1118 `duration("1s") * .5`, 1119 5e8, 1120 }, 1121 { 1122 `1 /* one */ + 2 // two`, 1123 3, 1124 }, 1125 { 1126 `let x = 1; x + 2`, 1127 3, 1128 }, 1129 { 1130 `map(1..3, let x = #; let y = x * x; y * y)`, 1131 []any{1, 16, 81}, 1132 }, 1133 { 1134 `map(1..2, let x = #; map(2..3, let y = #; x + y))`, 1135 []any{[]any{3, 4}, []any{4, 5}}, 1136 }, 1137 { 1138 `len(filter(1..99, # % 7 == 0))`, 1139 14, 1140 }, 1141 { 1142 `find(ArrayOfFoo, .Value == "baz")`, 1143 env.ArrayOfFoo[2], 1144 }, 1145 { 1146 `findIndex(ArrayOfFoo, .Value == "baz")`, 1147 2, 1148 }, 1149 { 1150 `filter(ArrayOfFoo, .Value == "baz")[0]`, 1151 env.ArrayOfFoo[2], 1152 }, 1153 { 1154 `first(filter(ArrayOfFoo, .Value == "baz"))`, 1155 env.ArrayOfFoo[2], 1156 }, 1157 { 1158 `first(filter(ArrayOfFoo, false))`, 1159 nil, 1160 }, 1161 { 1162 `findLast(1..9, # % 2 == 0)`, 1163 8, 1164 }, 1165 { 1166 `findLastIndex(1..9, # % 2 == 0)`, 1167 7, 1168 }, 1169 { 1170 `filter(1..9, # % 2 == 0)[-1]`, 1171 8, 1172 }, 1173 { 1174 `last(filter(1..9, # % 2 == 0))`, 1175 8, 1176 }, 1177 { 1178 `map(filter(1..9, # % 2 == 0), # * 2)`, 1179 []any{4, 8, 12, 16}, 1180 }, 1181 { 1182 `map(map(filter(1..9, # % 2 == 0), # * 2), # * 2)`, 1183 []any{8, 16, 24, 32}, 1184 }, 1185 { 1186 `first(map(filter(1..9, # % 2 == 0), # * 2))`, 1187 4, 1188 }, 1189 { 1190 `map(filter(1..9, # % 2 == 0), # * 2)[-1]`, 1191 16, 1192 }, 1193 { 1194 `len(map(filter(1..9, # % 2 == 0), # * 2))`, 1195 4, 1196 }, 1197 { 1198 `len(filter(map(1..9, # * 2), # % 2 == 0))`, 1199 9, 1200 }, 1201 { 1202 `first(filter(map(1..9, # * 2), # % 2 == 0))`, 1203 2, 1204 }, 1205 { 1206 `first(map(filter(1..9, # % 2 == 0), # * 2))`, 1207 4, 1208 }, 1209 { 1210 `2^3 == 8`, 1211 true, 1212 }, 1213 { 1214 `4/2 == 2`, 1215 true, 1216 }, 1217 { 1218 `.5 in 0..1`, 1219 false, 1220 }, 1221 { 1222 `.5 in ArrayOfInt`, 1223 false, 1224 }, 1225 { 1226 `bitnot(10)`, 1227 -11, 1228 }, 1229 { 1230 `bitxor(15, 32)`, 1231 47, 1232 }, 1233 { 1234 `bitand(90, 34)`, 1235 2, 1236 }, 1237 { 1238 `bitnand(35, 9)`, 1239 34, 1240 }, 1241 { 1242 `bitor(10, 5)`, 1243 15, 1244 }, 1245 { 1246 `bitshr(7, 2)`, 1247 1, 1248 }, 1249 { 1250 `bitshl(7, 2)`, 1251 28, 1252 }, 1253 { 1254 `bitushr(-100, 5)`, 1255 576460752303423484, 1256 }, 1257 { 1258 `"hello"[1:3]`, 1259 "el", 1260 }, 1261 { 1262 `[1, 2, 3]?.[0]`, 1263 1, 1264 }, 1265 { 1266 `[[1, 2], 3, 4]?.[0]?.[1]`, 1267 2, 1268 }, 1269 { 1270 `[nil, 3, 4]?.[0]?.[1]`, 1271 nil, 1272 }, 1273 { 1274 `1 > 2 < 3`, 1275 false, 1276 }, 1277 { 1278 `1 < 2 < 3`, 1279 true, 1280 }, 1281 { 1282 `1 < 2 < 3 > 4`, 1283 false, 1284 }, 1285 { 1286 `1 < 2 < 3 > 2`, 1287 true, 1288 }, 1289 { 1290 `1 < 2 < 3 == true`, 1291 true, 1292 }, 1293 } 1294 1295 for _, tt := range tests { 1296 t.Run(tt.code, func(t *testing.T) { 1297 { 1298 program, err := expr.Compile(tt.code, expr.Env(mock.Env{})) 1299 require.NoError(t, err, "compile error") 1300 1301 got, err := expr.Run(program, env) 1302 require.NoError(t, err, "run error") 1303 assert.Equal(t, tt.want, got) 1304 } 1305 { 1306 program, err := expr.Compile(tt.code, expr.Optimize(false)) 1307 require.NoError(t, err, "unoptimized") 1308 1309 got, err := expr.Run(program, env) 1310 require.NoError(t, err, "unoptimized") 1311 assert.Equal(t, tt.want, got, "unoptimized") 1312 } 1313 { 1314 got, err := expr.Eval(tt.code, env) 1315 require.NoError(t, err, "eval") 1316 assert.Equal(t, tt.want, got, "eval") 1317 } 1318 { 1319 program, err := expr.Compile(tt.code, expr.Env(mock.Env{}), expr.Optimize(false)) 1320 require.NoError(t, err) 1321 1322 code := program.Node().String() 1323 got, err := expr.Eval(code, env) 1324 require.NoError(t, err, code) 1325 assert.Equal(t, tt.want, got, code) 1326 } 1327 }) 1328 } 1329 } 1330 1331 func TestExpr_error(t *testing.T) { 1332 env := mock.Env{ 1333 ArrayOfAny: []any{1, "2", 3, true}, 1334 } 1335 1336 tests := []struct { 1337 code string 1338 want string 1339 }{ 1340 { 1341 `filter(1..9, # > 9)[0]`, 1342 `reflect: slice index out of range (1:20) 1343 | filter(1..9, # > 9)[0] 1344 | ...................^`, 1345 }, 1346 { 1347 `ArrayOfAny[-7]`, 1348 `index out of range: -3 (array length is 4) (1:11) 1349 | ArrayOfAny[-7] 1350 | ..........^`, 1351 }, 1352 } 1353 1354 for _, tt := range tests { 1355 t.Run(tt.code, func(t *testing.T) { 1356 program, err := expr.Compile(tt.code, expr.Env(mock.Env{})) 1357 require.NoError(t, err) 1358 1359 _, err = expr.Run(program, env) 1360 require.Error(t, err) 1361 assert.Equal(t, tt.want, err.Error()) 1362 }) 1363 } 1364 } 1365 1366 func TestExpr_optional_chaining(t *testing.T) { 1367 env := map[string]any{} 1368 program, err := expr.Compile("foo?.bar.baz", expr.Env(env), expr.AllowUndefinedVariables()) 1369 require.NoError(t, err) 1370 1371 got, err := expr.Run(program, env) 1372 require.NoError(t, err) 1373 assert.Equal(t, nil, got) 1374 } 1375 1376 func TestExpr_optional_chaining_property(t *testing.T) { 1377 env := map[string]any{ 1378 "foo": map[string]any{}, 1379 } 1380 program, err := expr.Compile("foo.bar?.baz", expr.Env(env)) 1381 require.NoError(t, err) 1382 1383 got, err := expr.Run(program, env) 1384 require.NoError(t, err) 1385 assert.Equal(t, nil, got) 1386 } 1387 1388 func TestExpr_optional_chaining_nested_chains(t *testing.T) { 1389 env := map[string]any{ 1390 "foo": map[string]any{ 1391 "id": 1, 1392 "bar": []map[string]any{ 1393 1: { 1394 "baz": "baz", 1395 }, 1396 }, 1397 }, 1398 } 1399 program, err := expr.Compile("foo?.bar[foo?.id]?.baz", expr.Env(env)) 1400 require.NoError(t, err) 1401 1402 got, err := expr.Run(program, env) 1403 require.NoError(t, err) 1404 assert.Equal(t, "baz", got) 1405 } 1406 1407 func TestExpr_optional_chaining_array(t *testing.T) { 1408 env := map[string]any{} 1409 program, err := expr.Compile("foo?.[1]?.[2]?.[3]", expr.Env(env), expr.AllowUndefinedVariables()) 1410 require.NoError(t, err) 1411 1412 got, err := expr.Run(program, env) 1413 require.NoError(t, err) 1414 assert.Equal(t, nil, got) 1415 } 1416 1417 func TestExpr_eval_with_env(t *testing.T) { 1418 _, err := expr.Eval("true", expr.Env(map[string]any{})) 1419 assert.Error(t, err) 1420 assert.Contains(t, err.Error(), "misused") 1421 } 1422 1423 func TestExpr_fetch_from_func(t *testing.T) { 1424 _, err := expr.Eval("foo.Value", map[string]any{ 1425 "foo": func() {}, 1426 }) 1427 assert.Error(t, err) 1428 assert.Contains(t, err.Error(), "cannot fetch Value from func()") 1429 } 1430 1431 func TestExpr_map_default_values(t *testing.T) { 1432 env := map[string]any{ 1433 "foo": map[string]string{}, 1434 "bar": map[string]*string{}, 1435 } 1436 1437 input := `foo['missing'] == '' && bar['missing'] == nil` 1438 1439 program, err := expr.Compile(input, expr.Env(env)) 1440 require.NoError(t, err) 1441 1442 output, err := expr.Run(program, env) 1443 require.NoError(t, err) 1444 require.Equal(t, true, output) 1445 } 1446 1447 func TestExpr_map_default_values_compile_check(t *testing.T) { 1448 tests := []struct { 1449 env any 1450 input string 1451 }{ 1452 { 1453 mock.MapStringStringEnv{"foo": "bar"}, 1454 `Split(foo, sep)`, 1455 }, 1456 { 1457 mock.MapStringIntEnv{"foo": 1}, 1458 `foo / bar`, 1459 }, 1460 } 1461 for _, tt := range tests { 1462 _, err := expr.Compile(tt.input, expr.Env(tt.env), expr.AllowUndefinedVariables()) 1463 require.NoError(t, err) 1464 } 1465 } 1466 1467 func TestExpr_calls_with_nil(t *testing.T) { 1468 env := map[string]any{ 1469 "equals": func(a, b any) any { 1470 assert.Nil(t, a, "a is not nil") 1471 assert.Nil(t, b, "b is not nil") 1472 return a == b 1473 }, 1474 "is": mock.Is{}, 1475 } 1476 1477 p, err := expr.Compile( 1478 "a == nil && equals(b, nil) && is.Nil(c)", 1479 expr.Env(env), 1480 expr.Operator("==", "equals"), 1481 expr.AllowUndefinedVariables(), 1482 ) 1483 require.NoError(t, err) 1484 1485 out, err := expr.Run(p, env) 1486 require.NoError(t, err) 1487 require.Equal(t, true, out) 1488 } 1489 1490 func TestExpr_call_float_arg_func_with_int(t *testing.T) { 1491 env := map[string]any{ 1492 "cnv": func(f float64) any { 1493 return f 1494 }, 1495 } 1496 tests := []struct { 1497 input string 1498 expected float64 1499 }{ 1500 {"-1", -1.0}, 1501 {"1+1", 2.0}, 1502 {"+1", 1.0}, 1503 {"1-1", 0.0}, 1504 {"1/1", 1.0}, 1505 {"1*1", 1.0}, 1506 {"1^1", 1.0}, 1507 } 1508 for _, tt := range tests { 1509 t.Run(tt.input, func(t *testing.T) { 1510 p, err := expr.Compile(fmt.Sprintf("cnv(%s)", tt.input), expr.Env(env)) 1511 require.NoError(t, err) 1512 1513 out, err := expr.Run(p, env) 1514 require.NoError(t, err) 1515 require.Equal(t, tt.expected, out) 1516 }) 1517 } 1518 } 1519 1520 func TestConstExpr_error_panic(t *testing.T) { 1521 env := map[string]any{ 1522 "divide": func(a, b int) int { return a / b }, 1523 } 1524 1525 _, err := expr.Compile( 1526 `1 + divide(1, 0)`, 1527 expr.Env(env), 1528 expr.ConstExpr("divide"), 1529 ) 1530 require.Error(t, err) 1531 require.Equal(t, "compile error: integer divide by zero (1:5)\n | 1 + divide(1, 0)\n | ....^", err.Error()) 1532 } 1533 1534 type divideError struct{ Message string } 1535 1536 func (e divideError) Error() string { 1537 return e.Message 1538 } 1539 1540 func TestConstExpr_error_as_error(t *testing.T) { 1541 env := map[string]any{ 1542 "divide": func(a, b int) (int, error) { 1543 if b == 0 { 1544 return 0, divideError{"integer divide by zero"} 1545 } 1546 return a / b, nil 1547 }, 1548 } 1549 1550 _, err := expr.Compile( 1551 `1 + divide(1, 0)`, 1552 expr.Env(env), 1553 expr.ConstExpr("divide"), 1554 ) 1555 require.Error(t, err) 1556 require.Equal(t, "integer divide by zero", err.Error()) 1557 require.IsType(t, divideError{}, err) 1558 } 1559 1560 func TestConstExpr_error_wrong_type(t *testing.T) { 1561 env := map[string]any{ 1562 "divide": 0, 1563 } 1564 assert.Panics(t, func() { 1565 _, _ = expr.Compile( 1566 `1 + divide(1, 0)`, 1567 expr.Env(env), 1568 expr.ConstExpr("divide"), 1569 ) 1570 }) 1571 } 1572 1573 func TestConstExpr_error_no_env(t *testing.T) { 1574 assert.Panics(t, func() { 1575 _, _ = expr.Compile( 1576 `1 + divide(1, 0)`, 1577 expr.ConstExpr("divide"), 1578 ) 1579 }) 1580 } 1581 1582 var stringer = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() 1583 1584 type stringerPatcher struct{} 1585 1586 func (p *stringerPatcher) Visit(node *ast.Node) { 1587 t := (*node).Type() 1588 if t == nil { 1589 return 1590 } 1591 if t.Implements(stringer) { 1592 ast.Patch(node, &ast.CallNode{ 1593 Callee: &ast.MemberNode{ 1594 Node: *node, 1595 Property: &ast.StringNode{Value: "String"}, 1596 }, 1597 }) 1598 } 1599 } 1600 1601 func TestPatch(t *testing.T) { 1602 program, err := expr.Compile( 1603 `Foo == "Foo.String"`, 1604 expr.Env(mock.Env{}), 1605 expr.Patch(&mock.StringerPatcher{}), 1606 ) 1607 require.NoError(t, err) 1608 1609 output, err := expr.Run(program, mock.Env{}) 1610 require.NoError(t, err) 1611 require.Equal(t, true, output) 1612 } 1613 1614 func TestCompile_exposed_error(t *testing.T) { 1615 _, err := expr.Compile(`1 == true`) 1616 require.Error(t, err) 1617 1618 fileError, ok := err.(*file.Error) 1619 require.True(t, ok, "error should be of type *file.Error") 1620 require.Equal(t, "invalid operation: == (mismatched types int and bool) (1:3)\n | 1 == true\n | ..^", fileError.Error()) 1621 require.Equal(t, 2, fileError.Column) 1622 require.Equal(t, 1, fileError.Line) 1623 1624 b, err := json.Marshal(err) 1625 require.NoError(t, err) 1626 require.Equal(t, 1627 `{"from":2,"to":4,"line":1,"column":2,"message":"invalid operation: == (mismatched types int and bool)","snippet":"\n | 1 == true\n | ..^","prev":null}`, 1628 string(b), 1629 ) 1630 } 1631 1632 func TestAsBool_exposed_error(t *testing.T) { 1633 _, err := expr.Compile(`42`, expr.AsBool()) 1634 require.Error(t, err) 1635 1636 _, ok := err.(*file.Error) 1637 require.False(t, ok, "error must not be of type *file.Error") 1638 require.Equal(t, "expected bool, but got int", err.Error()) 1639 } 1640 1641 func TestEval_exposed_error(t *testing.T) { 1642 _, err := expr.Eval(`1 % 0`, nil) 1643 require.Error(t, err) 1644 1645 fileError, ok := err.(*file.Error) 1646 require.True(t, ok, "error should be of type *file.Error") 1647 require.Equal(t, "integer divide by zero (1:3)\n | 1 % 0\n | ..^", fileError.Error()) 1648 require.Equal(t, 2, fileError.Column) 1649 require.Equal(t, 1, fileError.Line) 1650 } 1651 1652 func TestIssue105(t *testing.T) { 1653 type A struct { 1654 Field string 1655 } 1656 type B struct { 1657 Field int 1658 } 1659 type C struct { 1660 A 1661 B 1662 } 1663 type Env struct { 1664 C 1665 } 1666 1667 code := ` 1668 A.Field == '' && 1669 C.A.Field == '' && 1670 B.Field == 0 && 1671 C.B.Field == 0 1672 ` 1673 1674 _, err := expr.Compile(code, expr.Env(Env{})) 1675 require.NoError(t, err) 1676 1677 _, err = expr.Compile(`Field == ''`, expr.Env(Env{})) 1678 require.Error(t, err) 1679 require.Contains(t, err.Error(), "ambiguous identifier Field") 1680 } 1681 1682 func TestIssue_nested_closures(t *testing.T) { 1683 code := `all(1..3, { all(1..3, { # > 0 }) and # > 0 })` 1684 1685 program, err := expr.Compile(code) 1686 require.NoError(t, err) 1687 1688 output, err := expr.Run(program, nil) 1689 require.NoError(t, err) 1690 require.True(t, output.(bool)) 1691 } 1692 1693 func TestIssue138(t *testing.T) { 1694 env := map[string]any{} 1695 1696 _, err := expr.Compile(`1 / (1 - 1)`, expr.Env(env)) 1697 require.NoError(t, err) 1698 1699 _, err = expr.Compile(`1 % 0`, expr.Env(env)) 1700 require.Error(t, err) 1701 require.Equal(t, "integer divide by zero (1:3)\n | 1 % 0\n | ..^", err.Error()) 1702 } 1703 1704 func TestIssue154(t *testing.T) { 1705 type Data struct { 1706 Array *[2]any 1707 Slice *[]any 1708 Map *map[string]any 1709 String *string 1710 } 1711 1712 type Env struct { 1713 Data *Data 1714 } 1715 1716 b := true 1717 i := 10 1718 s := "value" 1719 1720 Array := [2]any{ 1721 &b, 1722 &i, 1723 } 1724 1725 Slice := []any{ 1726 &b, 1727 &i, 1728 } 1729 1730 Map := map[string]any{ 1731 "Bool": &b, 1732 "Int": &i, 1733 } 1734 1735 env := Env{ 1736 Data: &Data{ 1737 Array: &Array, 1738 Slice: &Slice, 1739 Map: &Map, 1740 String: &s, 1741 }, 1742 } 1743 1744 tests := []string{ 1745 `Data.Array[0] == true`, 1746 `Data.Array[1] == 10`, 1747 `Data.Slice[0] == true`, 1748 `Data.Slice[1] == 10`, 1749 `Data.Map["Bool"] == true`, 1750 `Data.Map["Int"] == 10`, 1751 `Data.String == "value"`, 1752 } 1753 1754 for _, input := range tests { 1755 program, err := expr.Compile(input, expr.Env(env)) 1756 require.NoError(t, err, input) 1757 1758 output, err := expr.Run(program, env) 1759 require.NoError(t, err) 1760 assert.True(t, output.(bool), input) 1761 } 1762 } 1763 1764 func TestIssue270(t *testing.T) { 1765 env := map[string]any{ 1766 "int8": int8(1), 1767 "int16": int16(3), 1768 "int32": int32(5), 1769 "int64": int64(7), 1770 "uint8": uint8(11), 1771 "uint16": uint16(13), 1772 "uint32": uint32(17), 1773 "uint64": uint64(19), 1774 "int8a": uint(23), 1775 "int8b": uint(29), 1776 "int16a": uint(31), 1777 "int16b": uint(37), 1778 "int32a": uint(41), 1779 "int32b": uint(43), 1780 "int64a": uint(47), 1781 "int64b": uint(53), 1782 "uint8a": uint(59), 1783 "uint8b": uint(61), 1784 "uint16a": uint(67), 1785 "uint16b": uint(71), 1786 "uint32a": uint(73), 1787 "uint32b": uint(79), 1788 "uint64a": uint(83), 1789 "uint64b": uint(89), 1790 "float32a": float32(97), 1791 "float32b": float32(101), 1792 "float64a": float64(103), 1793 "float64b": float64(107), 1794 } 1795 for _, each := range []struct { 1796 input string 1797 }{ 1798 {"int8 / int16"}, 1799 {"int32 / int64"}, 1800 {"uint8 / uint16"}, 1801 {"uint32 / uint64"}, 1802 {"int8 / uint64"}, 1803 {"int64 / uint8"}, 1804 {"int8a / int8b"}, 1805 {"int16a / int16b"}, 1806 {"int32a / int32b"}, 1807 {"int64a / int64b"}, 1808 {"uint8a / uint8b"}, 1809 {"uint16a / uint16b"}, 1810 {"uint32a / uint32b"}, 1811 {"uint64a / uint64b"}, 1812 {"float32a / float32b"}, 1813 {"float64a / float64b"}, 1814 } { 1815 p, err := expr.Compile(each.input, expr.Env(env)) 1816 require.NoError(t, err) 1817 1818 out, err := expr.Run(p, env) 1819 require.NoError(t, err) 1820 require.IsType(t, float64(0), out) 1821 } 1822 } 1823 1824 func TestIssue271(t *testing.T) { 1825 type BarArray []float64 1826 1827 type Foo struct { 1828 Bar BarArray 1829 Baz int 1830 } 1831 1832 type Env struct { 1833 Foo Foo 1834 } 1835 1836 code := `Foo.Bar[0]` 1837 1838 program, err := expr.Compile(code, expr.Env(Env{})) 1839 require.NoError(t, err) 1840 1841 output, err := expr.Run(program, Env{ 1842 Foo: Foo{ 1843 Bar: BarArray{1.0, 2.0, 3.0}, 1844 }, 1845 }) 1846 require.NoError(t, err) 1847 require.Equal(t, 1.0, output) 1848 } 1849 1850 type Issue346Array []Issue346Type 1851 1852 type Issue346Type struct { 1853 Bar string 1854 } 1855 1856 func (i Issue346Array) Len() int { 1857 return len(i) 1858 } 1859 1860 func TestIssue346(t *testing.T) { 1861 code := `Foo[0].Bar` 1862 1863 env := map[string]any{ 1864 "Foo": Issue346Array{ 1865 {Bar: "bar"}, 1866 }, 1867 } 1868 program, err := expr.Compile(code, expr.Env(env)) 1869 require.NoError(t, err) 1870 1871 output, err := expr.Run(program, env) 1872 require.NoError(t, err) 1873 require.Equal(t, "bar", output) 1874 } 1875 1876 func TestCompile_allow_to_use_interface_to_get_an_element_from_map(t *testing.T) { 1877 code := `{"value": "ok"}[vars.key]` 1878 env := map[string]any{ 1879 "vars": map[string]any{ 1880 "key": "value", 1881 }, 1882 } 1883 1884 program, err := expr.Compile(code, expr.Env(env)) 1885 assert.NoError(t, err) 1886 1887 out, err := expr.Run(program, env) 1888 assert.NoError(t, err) 1889 assert.Equal(t, "ok", out) 1890 1891 t.Run("with allow undefined variables", func(t *testing.T) { 1892 code := `{'key': 'value'}[Key]` 1893 env := mock.MapStringStringEnv{} 1894 options := []expr.Option{ 1895 expr.AllowUndefinedVariables(), 1896 } 1897 1898 program, err := expr.Compile(code, options...) 1899 assert.NoError(t, err) 1900 1901 out, err := expr.Run(program, env) 1902 assert.NoError(t, err) 1903 assert.Equal(t, nil, out) 1904 }) 1905 } 1906 1907 func TestFastCall(t *testing.T) { 1908 env := map[string]any{ 1909 "func": func(in any) float64 { 1910 return 8 1911 }, 1912 } 1913 code := `func("8")` 1914 1915 program, err := expr.Compile(code, expr.Env(env)) 1916 assert.NoError(t, err) 1917 1918 out, err := expr.Run(program, env) 1919 assert.NoError(t, err) 1920 assert.Equal(t, float64(8), out) 1921 } 1922 1923 func TestFastCall_OpCallFastErr(t *testing.T) { 1924 env := map[string]any{ 1925 "func": func(...any) (any, error) { 1926 return 8, nil 1927 }, 1928 } 1929 code := `func("8")` 1930 1931 program, err := expr.Compile(code, expr.Env(env)) 1932 assert.NoError(t, err) 1933 1934 out, err := expr.Run(program, env) 1935 assert.NoError(t, err) 1936 assert.Equal(t, 8, out) 1937 } 1938 1939 func TestRun_custom_func_returns_an_error_as_second_arg(t *testing.T) { 1940 env := map[string]any{ 1941 "semver": func(value string, cmp string) (bool, error) { return true, nil }, 1942 } 1943 1944 p, err := expr.Compile(`semver("1.2.3", "= 1.2.3")`, expr.Env(env)) 1945 assert.NoError(t, err) 1946 1947 out, err := expr.Run(p, env) 1948 assert.NoError(t, err) 1949 assert.Equal(t, true, out) 1950 } 1951 1952 func TestFunction(t *testing.T) { 1953 add := expr.Function( 1954 "add", 1955 func(p ...any) (any, error) { 1956 out := 0 1957 for _, each := range p { 1958 out += each.(int) 1959 } 1960 return out, nil 1961 }, 1962 new(func(...int) int), 1963 ) 1964 1965 p, err := expr.Compile(`add() + add(1) + add(1, 2) + add(1, 2, 3) + add(1, 2, 3, 4)`, add) 1966 assert.NoError(t, err) 1967 1968 out, err := expr.Run(p, nil) 1969 assert.NoError(t, err) 1970 assert.Equal(t, 20, out) 1971 } 1972 1973 // Nil coalescing operator 1974 func TestRun_NilCoalescingOperator(t *testing.T) { 1975 env := map[string]any{ 1976 "foo": map[string]any{ 1977 "bar": "value", 1978 }, 1979 } 1980 1981 t.Run("value", func(t *testing.T) { 1982 p, err := expr.Compile(`foo.bar ?? "default"`, expr.Env(env)) 1983 assert.NoError(t, err) 1984 1985 out, err := expr.Run(p, env) 1986 assert.NoError(t, err) 1987 assert.Equal(t, "value", out) 1988 }) 1989 1990 t.Run("default", func(t *testing.T) { 1991 p, err := expr.Compile(`foo.baz ?? "default"`, expr.Env(env)) 1992 assert.NoError(t, err) 1993 1994 out, err := expr.Run(p, env) 1995 assert.NoError(t, err) 1996 assert.Equal(t, "default", out) 1997 }) 1998 1999 t.Run("default with chain", func(t *testing.T) { 2000 p, err := expr.Compile(`foo?.bar ?? "default"`, expr.Env(env)) 2001 assert.NoError(t, err) 2002 2003 out, err := expr.Run(p, map[string]any{}) 2004 assert.NoError(t, err) 2005 assert.Equal(t, "default", out) 2006 }) 2007 } 2008 2009 func TestEval_nil_in_maps(t *testing.T) { 2010 env := map[string]any{ 2011 "m": map[any]any{nil: "bar"}, 2012 "empty": map[any]any{}, 2013 } 2014 t.Run("nil key exists", func(t *testing.T) { 2015 p, err := expr.Compile(`m[nil]`, expr.Env(env)) 2016 assert.NoError(t, err) 2017 2018 out, err := expr.Run(p, env) 2019 assert.NoError(t, err) 2020 assert.Equal(t, "bar", out) 2021 }) 2022 t.Run("no nil key", func(t *testing.T) { 2023 p, err := expr.Compile(`empty[nil]`, expr.Env(env)) 2024 assert.NoError(t, err) 2025 2026 out, err := expr.Run(p, env) 2027 assert.NoError(t, err) 2028 assert.Equal(t, nil, out) 2029 }) 2030 t.Run("nil in m", func(t *testing.T) { 2031 p, err := expr.Compile(`nil in m`, expr.Env(env)) 2032 assert.NoError(t, err) 2033 2034 out, err := expr.Run(p, env) 2035 assert.NoError(t, err) 2036 assert.Equal(t, true, out) 2037 }) 2038 t.Run("nil in empty", func(t *testing.T) { 2039 p, err := expr.Compile(`nil in empty`, expr.Env(env)) 2040 assert.NoError(t, err) 2041 2042 out, err := expr.Run(p, env) 2043 assert.NoError(t, err) 2044 assert.Equal(t, false, out) 2045 }) 2046 } 2047 2048 // Test the use of env keyword. Forms env[] and env[”] are valid. 2049 // The enclosed identifier must be in the expression env. 2050 func TestEnv_keyword(t *testing.T) { 2051 env := map[string]any{ 2052 "space test": "ok", 2053 "space_test": "not ok", // Seems to be some underscore substituting happening, check that. 2054 "Section 1-2a": "ok", 2055 `c:\ndrive\2015 Information Table`: "ok", 2056 "%*worst function name ever!!": func() string { 2057 return "ok" 2058 }(), 2059 "1": "o", 2060 "2": "k", 2061 "num": 10, 2062 "mylist": []int{1, 2, 3, 4, 5}, 2063 "MIN": func(a, b int) int { 2064 if a < b { 2065 return a 2066 } else { 2067 return b 2068 } 2069 }, 2070 "red": "n", 2071 "irect": "um", 2072 "String Map": map[string]string{ 2073 "one": "two", 2074 "three": "four", 2075 }, 2076 "OtherMap": map[string]string{ 2077 "a": "b", 2078 "c": "d", 2079 }, 2080 } 2081 2082 // No error cases 2083 var tests = []struct { 2084 code string 2085 want any 2086 }{ 2087 {"$env['space test']", "ok"}, 2088 {"$env['Section 1-2a']", "ok"}, 2089 {`$env["c:\\ndrive\\2015 Information Table"]`, "ok"}, 2090 {"$env['%*worst function name ever!!']", "ok"}, 2091 {"$env['String Map'].one", "two"}, 2092 {"$env['1'] + $env['2']", "ok"}, 2093 {"1 + $env['num'] + $env['num']", 21}, 2094 {"MIN($env['num'],0)", 0}, 2095 {"$env['nu' + 'm']", 10}, 2096 {"$env[red + irect]", 10}, 2097 {"$env['String Map']?.five", ""}, 2098 {"$env.red", "n"}, 2099 {"$env?.unknown", nil}, 2100 {"$env.mylist[1]", 2}, 2101 {"$env?.OtherMap?.a", "b"}, 2102 {"$env?.OtherMap?.d", ""}, 2103 {"'num' in $env", true}, 2104 {"get($env, 'num')", 10}, 2105 } 2106 2107 for _, tt := range tests { 2108 t.Run(tt.code, func(t *testing.T) { 2109 2110 program, err := expr.Compile(tt.code, expr.Env(env)) 2111 require.NoError(t, err, "compile error") 2112 2113 got, err := expr.Run(program, env) 2114 require.NoError(t, err, "execution error") 2115 2116 assert.Equal(t, tt.want, got, tt.code) 2117 }) 2118 } 2119 2120 for _, tt := range tests { 2121 t.Run(tt.code, func(t *testing.T) { 2122 got, err := expr.Eval(tt.code, env) 2123 require.NoError(t, err, "eval error: "+tt.code) 2124 2125 assert.Equal(t, tt.want, got, "eval: "+tt.code) 2126 }) 2127 } 2128 2129 // error cases 2130 tests = []struct { 2131 code string 2132 want any 2133 }{ 2134 {"env()", "bad"}, 2135 } 2136 2137 for _, tt := range tests { 2138 t.Run(tt.code, func(t *testing.T) { 2139 _, err := expr.Eval(tt.code, expr.Env(env)) 2140 require.Error(t, err, "compile error") 2141 2142 }) 2143 } 2144 } 2145 2146 func TestEnv_keyword_with_custom_functions(t *testing.T) { 2147 fn := expr.Function("fn", func(params ...any) (any, error) { 2148 return "ok", nil 2149 }) 2150 2151 var tests = []struct { 2152 code string 2153 error bool 2154 }{ 2155 {`fn()`, false}, 2156 {`$env.fn()`, true}, 2157 {`$env["fn"]`, true}, 2158 } 2159 2160 for _, tt := range tests { 2161 t.Run(tt.code, func(t *testing.T) { 2162 _, err := expr.Compile(tt.code, expr.Env(mock.Env{}), fn) 2163 if tt.error { 2164 require.Error(t, err) 2165 } else { 2166 require.NoError(t, err) 2167 } 2168 }) 2169 } 2170 } 2171 2172 func TestIssue401(t *testing.T) { 2173 program, err := expr.Compile("(a - b + c) / d", expr.AllowUndefinedVariables()) 2174 require.NoError(t, err, "compile error") 2175 2176 output, err := expr.Run(program, map[string]any{ 2177 "a": 1, 2178 "b": 2, 2179 "c": 3, 2180 "d": 4, 2181 }) 2182 require.NoError(t, err, "run error") 2183 require.Equal(t, 0.5, output) 2184 } 2185 2186 func TestEval_slices_out_of_bound(t *testing.T) { 2187 tests := []struct { 2188 code string 2189 want any 2190 }{ 2191 {"[1, 2, 3][:99]", []any{1, 2, 3}}, 2192 {"[1, 2, 3][99:]", []any{}}, 2193 {"[1, 2, 3][:-99]", []any{}}, 2194 {"[1, 2, 3][-99:]", []any{1, 2, 3}}, 2195 } 2196 2197 for _, tt := range tests { 2198 t.Run(tt.code, func(t *testing.T) { 2199 got, err := expr.Eval(tt.code, nil) 2200 require.NoError(t, err, "eval error: "+tt.code) 2201 assert.Equal(t, tt.want, got, "eval: "+tt.code) 2202 }) 2203 } 2204 } 2205 2206 func TestMemoryBudget(t *testing.T) { 2207 tests := []struct { 2208 code string 2209 }{ 2210 {`map(1..100, {map(1..100, {map(1..100, {0})})})`}, 2211 {`len(1..10000000)`}, 2212 } 2213 2214 for _, tt := range tests { 2215 t.Run(tt.code, func(t *testing.T) { 2216 program, err := expr.Compile(tt.code) 2217 require.NoError(t, err, "compile error") 2218 2219 _, err = expr.Run(program, nil) 2220 assert.Error(t, err, "run error") 2221 assert.Contains(t, err.Error(), "memory budget exceeded") 2222 }) 2223 } 2224 } 2225 2226 func TestExpr_custom_tests(t *testing.T) { 2227 f, err := os.Open("custom_tests.json") 2228 if os.IsNotExist(err) { 2229 t.Skip("no custom tests") 2230 return 2231 } 2232 2233 require.NoError(t, err, "open file error") 2234 defer f.Close() 2235 2236 var tests []string 2237 err = json.NewDecoder(f).Decode(&tests) 2238 require.NoError(t, err, "decode json error") 2239 2240 for id, tt := range tests { 2241 t.Run(fmt.Sprintf("line %v", id+2), func(t *testing.T) { 2242 program, err := expr.Compile(tt) 2243 require.NoError(t, err) 2244 2245 timeout := make(chan bool, 1) 2246 go func() { 2247 time.Sleep(time.Second) 2248 timeout <- true 2249 }() 2250 2251 done := make(chan bool, 1) 2252 go func() { 2253 out, err := expr.Run(program, nil) 2254 // Make sure out is used. 2255 _ = fmt.Sprintf("%v", out) 2256 assert.Error(t, err) 2257 done <- true 2258 }() 2259 2260 select { 2261 case <-done: 2262 // Success. 2263 case <-timeout: 2264 t.Fatal("timeout") 2265 } 2266 }) 2267 } 2268 } 2269 2270 func TestIssue432(t *testing.T) { 2271 env := map[string]any{ 2272 "func": func( 2273 paramUint32 uint32, 2274 paramUint16 uint16, 2275 paramUint8 uint8, 2276 paramUint uint, 2277 paramInt32 int32, 2278 paramInt16 int16, 2279 paramInt8 int8, 2280 paramInt int, 2281 paramFloat64 float64, 2282 paramFloat32 float32, 2283 ) float64 { 2284 return float64(paramUint32) + float64(paramUint16) + float64(paramUint8) + float64(paramUint) + 2285 float64(paramInt32) + float64(paramInt16) + float64(paramInt8) + float64(paramInt) + 2286 float64(paramFloat64) + float64(paramFloat32) 2287 }, 2288 } 2289 code := `func(1,1,1,1,1,1,1,1,1,1)` 2290 2291 program, err := expr.Compile(code, expr.Env(env)) 2292 assert.NoError(t, err) 2293 2294 out, err := expr.Run(program, env) 2295 assert.NoError(t, err) 2296 assert.Equal(t, float64(10), out) 2297 } 2298 2299 func TestIssue453(t *testing.T) { 2300 env := map[string]any{ 2301 "foo": nil, 2302 } 2303 _, err := expr.Compile(`foo()`, expr.Env(env)) 2304 require.Error(t, err) 2305 } 2306 2307 func TestIssue461(t *testing.T) { 2308 type EnvStr string 2309 type EnvField struct { 2310 S EnvStr 2311 Str string 2312 } 2313 type Env struct { 2314 S EnvStr 2315 Str string 2316 EnvField EnvField 2317 } 2318 var tests = []struct { 2319 input string 2320 env Env 2321 want bool 2322 }{ 2323 { 2324 input: "Str == S", 2325 env: Env{S: "string", Str: "string"}, 2326 want: false, 2327 }, 2328 { 2329 input: "Str == Str", 2330 env: Env{Str: "string"}, 2331 want: true, 2332 }, 2333 { 2334 input: "S == S", 2335 env: Env{Str: "string"}, 2336 want: true, 2337 }, 2338 { 2339 input: `Str == "string"`, 2340 env: Env{Str: "string"}, 2341 want: true, 2342 }, 2343 { 2344 input: `S == "string"`, 2345 env: Env{Str: "string"}, 2346 want: false, 2347 }, 2348 { 2349 input: "EnvField.Str == EnvField.S", 2350 env: Env{EnvField: EnvField{S: "string", Str: "string"}}, 2351 want: false, 2352 }, 2353 { 2354 input: "EnvField.Str == EnvField.Str", 2355 env: Env{EnvField: EnvField{Str: "string"}}, 2356 want: true, 2357 }, 2358 { 2359 input: "EnvField.S == EnvField.S", 2360 env: Env{EnvField: EnvField{Str: "string"}}, 2361 want: true, 2362 }, 2363 { 2364 input: `EnvField.Str == "string"`, 2365 env: Env{EnvField: EnvField{Str: "string"}}, 2366 want: true, 2367 }, 2368 { 2369 input: `EnvField.S == "string"`, 2370 env: Env{EnvField: EnvField{Str: "string"}}, 2371 want: false, 2372 }, 2373 } 2374 2375 for _, tt := range tests { 2376 t.Run(tt.input, func(t *testing.T) { 2377 program, err := expr.Compile(tt.input, expr.Env(tt.env), expr.AsBool()) 2378 2379 out, err := expr.Run(program, tt.env) 2380 require.NoError(t, err) 2381 2382 require.Equal(t, tt.want, out) 2383 }) 2384 } 2385 } 2386 2387 func TestIssue462(t *testing.T) { 2388 env := map[string]any{ 2389 "foo": func() (string, error) { 2390 return "bar", nil 2391 }, 2392 } 2393 _, err := expr.Compile(`$env.unknown(int())`, expr.Env(env)) 2394 require.Error(t, err) 2395 } 2396 2397 func TestIssue_embedded_pointer_struct(t *testing.T) { 2398 var tests = []struct { 2399 input string 2400 env mock.Env 2401 want any 2402 }{ 2403 { 2404 input: "EmbedPointerEmbedInt > 0", 2405 env: mock.Env{ 2406 Embed: mock.Embed{ 2407 EmbedPointerEmbed: &mock.EmbedPointerEmbed{ 2408 EmbedPointerEmbedInt: 123, 2409 }, 2410 }, 2411 }, 2412 want: true, 2413 }, 2414 { 2415 input: "(Embed).EmbedPointerEmbedInt > 0", 2416 env: mock.Env{ 2417 Embed: mock.Embed{ 2418 EmbedPointerEmbed: &mock.EmbedPointerEmbed{ 2419 EmbedPointerEmbedInt: 123, 2420 }, 2421 }, 2422 }, 2423 want: true, 2424 }, 2425 { 2426 input: "(Embed).EmbedPointerEmbedInt > 0", 2427 env: mock.Env{ 2428 Embed: mock.Embed{ 2429 EmbedPointerEmbed: &mock.EmbedPointerEmbed{ 2430 EmbedPointerEmbedInt: 0, 2431 }, 2432 }, 2433 }, 2434 want: false, 2435 }, 2436 { 2437 input: "(Embed).EmbedPointerEmbedMethod(0)", 2438 env: mock.Env{ 2439 Embed: mock.Embed{ 2440 EmbedPointerEmbed: &mock.EmbedPointerEmbed{ 2441 EmbedPointerEmbedInt: 0, 2442 }, 2443 }, 2444 }, 2445 want: "", 2446 }, 2447 { 2448 input: "(Embed).EmbedPointerEmbedPointerReceiverMethod(0)", 2449 env: mock.Env{ 2450 Embed: mock.Embed{ 2451 EmbedPointerEmbed: nil, 2452 }, 2453 }, 2454 want: "", 2455 }, 2456 } 2457 for _, tt := range tests { 2458 t.Run(tt.input, func(t *testing.T) { 2459 program, err := expr.Compile(tt.input, expr.Env(tt.env)) 2460 require.NoError(t, err) 2461 2462 out, err := expr.Run(program, tt.env) 2463 require.NoError(t, err) 2464 2465 require.Equal(t, tt.want, out) 2466 }) 2467 } 2468 } 2469 2470 func TestIssue474(t *testing.T) { 2471 testCases := []struct { 2472 code string 2473 fail bool 2474 }{ 2475 { 2476 code: `func("invalid")`, 2477 fail: true, 2478 }, 2479 { 2480 code: `func(true)`, 2481 fail: true, 2482 }, 2483 { 2484 code: `func([])`, 2485 fail: true, 2486 }, 2487 { 2488 code: `func({})`, 2489 fail: true, 2490 }, 2491 { 2492 code: `func(1)`, 2493 fail: false, 2494 }, 2495 { 2496 code: `func(1.5)`, 2497 fail: false, 2498 }, 2499 } 2500 2501 for _, tc := range testCases { 2502 ltc := tc 2503 t.Run(ltc.code, func(t *testing.T) { 2504 t.Parallel() 2505 function := expr.Function("func", func(params ...any) (any, error) { 2506 return true, nil 2507 }, new(func(float64) bool)) 2508 _, err := expr.Compile(ltc.code, function) 2509 if ltc.fail { 2510 if err == nil { 2511 t.Error("expected an error, but it was nil") 2512 t.FailNow() 2513 } 2514 } else { 2515 if err != nil { 2516 t.Errorf("expected nil, but it was %v", err) 2517 t.FailNow() 2518 } 2519 } 2520 }) 2521 } 2522 } 2523 2524 func TestRaceCondition_variables(t *testing.T) { 2525 program, err := expr.Compile(`let foo = 1; foo + 1`, expr.Env(mock.Env{})) 2526 require.NoError(t, err) 2527 2528 var wg sync.WaitGroup 2529 2530 for i := 0; i < 10; i++ { 2531 wg.Add(1) 2532 go func() { 2533 defer wg.Done() 2534 out, err := expr.Run(program, mock.Env{}) 2535 require.NoError(t, err) 2536 require.Equal(t, 2, out) 2537 }() 2538 } 2539 2540 wg.Wait() 2541 } 2542 2543 func TestOperatorDependsOnEnv(t *testing.T) { 2544 env := map[string]any{ 2545 "plus": func(a, b int) int { 2546 return 42 2547 }, 2548 } 2549 program, err := expr.Compile(`1 + 2`, expr.Operator("+", "plus"), expr.Env(env)) 2550 require.NoError(t, err) 2551 2552 out, err := expr.Run(program, env) 2553 require.NoError(t, err) 2554 assert.Equal(t, 42, out) 2555 } 2556 2557 func TestIssue624(t *testing.T) { 2558 type tag struct { 2559 Name string 2560 } 2561 2562 type item struct { 2563 Tags []tag 2564 } 2565 2566 i := item{ 2567 Tags: []tag{ 2568 {Name: "one"}, 2569 {Name: "two"}, 2570 }, 2571 } 2572 2573 rule := `[ 2574 true && true, 2575 one(Tags, .Name in ["one"]), 2576 one(Tags, .Name in ["two"]), 2577 one(Tags, .Name in ["one"]) && one(Tags, .Name in ["two"]) 2578 ]` 2579 resp, err := expr.Eval(rule, i) 2580 require.NoError(t, err) 2581 require.Equal(t, []interface{}{true, true, true, true}, resp) 2582 } 2583 2584 func TestPredicateCombination(t *testing.T) { 2585 tests := []struct { 2586 code1 string 2587 code2 string 2588 }{ 2589 {"all(1..3, {# > 0}) && all(1..3, {# < 4})", "all(1..3, {# > 0 && # < 4})"}, 2590 {"all(1..3, {# > 1}) && all(1..3, {# < 4})", "all(1..3, {# > 1 && # < 4})"}, 2591 {"all(1..3, {# > 0}) && all(1..3, {# < 2})", "all(1..3, {# > 0 && # < 2})"}, 2592 {"all(1..3, {# > 1}) && all(1..3, {# < 2})", "all(1..3, {# > 1 && # < 2})"}, 2593 2594 {"any(1..3, {# > 0}) || any(1..3, {# < 4})", "any(1..3, {# > 0 || # < 4})"}, 2595 {"any(1..3, {# > 1}) || any(1..3, {# < 4})", "any(1..3, {# > 1 || # < 4})"}, 2596 {"any(1..3, {# > 0}) || any(1..3, {# < 2})", "any(1..3, {# > 0 || # < 2})"}, 2597 {"any(1..3, {# > 1}) || any(1..3, {# < 2})", "any(1..3, {# > 1 || # < 2})"}, 2598 2599 {"none(1..3, {# > 0}) && none(1..3, {# < 4})", "none(1..3, {# > 0 || # < 4})"}, 2600 {"none(1..3, {# > 1}) && none(1..3, {# < 4})", "none(1..3, {# > 1 || # < 4})"}, 2601 {"none(1..3, {# > 0}) && none(1..3, {# < 2})", "none(1..3, {# > 0 || # < 2})"}, 2602 {"none(1..3, {# > 1}) && none(1..3, {# < 2})", "none(1..3, {# > 1 || # < 2})"}, 2603 } 2604 for _, tt := range tests { 2605 t.Run(tt.code1, func(t *testing.T) { 2606 out1, err := expr.Eval(tt.code1, nil) 2607 require.NoError(t, err) 2608 2609 out2, err := expr.Eval(tt.code2, nil) 2610 require.NoError(t, err) 2611 2612 require.Equal(t, out1, out2) 2613 }) 2614 } 2615 } 2616 2617 func TestArrayComparison(t *testing.T) { 2618 tests := []struct { 2619 env any 2620 code string 2621 }{ 2622 {[]string{"A", "B"}, "foo == ['A', 'B']"}, 2623 {[]int{1, 2}, "foo == [1, 2]"}, 2624 {[]uint8{1, 2}, "foo == [1, 2]"}, 2625 {[]float64{1.1, 2.2}, "foo == [1.1, 2.2]"}, 2626 {[]any{"A", 1, 1.1, true}, "foo == ['A', 1, 1.1, true]"}, 2627 {[]string{"A", "B"}, "foo != [1, 2]"}, 2628 } 2629 2630 for _, tt := range tests { 2631 t.Run(tt.code, func(t *testing.T) { 2632 env := map[string]any{"foo": tt.env} 2633 program, err := expr.Compile(tt.code, expr.Env(env)) 2634 require.NoError(t, err) 2635 2636 out, err := expr.Run(program, env) 2637 require.NoError(t, err) 2638 require.Equal(t, true, out) 2639 }) 2640 } 2641 } 2642 2643 func TestIssue_570(t *testing.T) { 2644 type Student struct { 2645 Name string 2646 } 2647 2648 env := map[string]any{ 2649 "student": (*Student)(nil), 2650 } 2651 2652 program, err := expr.Compile("student?.Name", expr.Env(env)) 2653 require.NoError(t, err) 2654 2655 out, err := expr.Run(program, env) 2656 require.NoError(t, err) 2657 require.IsType(t, nil, out) 2658 } 2659 2660 func TestIssue_integer_truncated_by_compiler(t *testing.T) { 2661 env := map[string]any{ 2662 "fn": func(x byte) byte { 2663 return x 2664 }, 2665 } 2666 2667 _, err := expr.Compile("fn(255)", expr.Env(env)) 2668 require.NoError(t, err) 2669 2670 _, err = expr.Compile("fn(256)", expr.Env(env)) 2671 require.Error(t, err) 2672 } 2673 2674 func TestExpr_crash(t *testing.T) { 2675 content, err := os.ReadFile("testdata/crash.txt") 2676 require.NoError(t, err) 2677 2678 _, err = expr.Compile(string(content)) 2679 require.Error(t, err) 2680 } 2681 2682 func TestExpr_nil_op_str(t *testing.T) { 2683 // Let's test operators, which do `.(string)` in VM, also check for nil. 2684 2685 var str *string = nil 2686 env := map[string]any{ 2687 "nilString": str, 2688 } 2689 2690 tests := []struct{ code string }{ 2691 {`nilString == "str"`}, 2692 {`nilString contains "str"`}, 2693 {`nilString matches "str"`}, 2694 {`nilString startsWith "str"`}, 2695 {`nilString endsWith "str"`}, 2696 2697 {`"str" == nilString`}, 2698 {`"str" contains nilString`}, 2699 {`"str" matches nilString`}, 2700 {`"str" startsWith nilString`}, 2701 {`"str" endsWith nilString`}, 2702 } 2703 2704 for _, tt := range tests { 2705 t.Run(tt.code, func(t *testing.T) { 2706 program, err := expr.Compile(tt.code) 2707 require.NoError(t, err) 2708 2709 output, err := expr.Run(program, env) 2710 require.NoError(t, err) 2711 require.Equal(t, false, output) 2712 }) 2713 } 2714 }