github.com/expr-lang/expr@v1.16.9/parser/parser_test.go (about) 1 package parser_test 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/expr-lang/expr/internal/testify/assert" 9 "github.com/expr-lang/expr/internal/testify/require" 10 11 . "github.com/expr-lang/expr/ast" 12 "github.com/expr-lang/expr/parser" 13 ) 14 15 func TestParse(t *testing.T) { 16 tests := []struct { 17 input string 18 want Node 19 }{ 20 { 21 "a", 22 &IdentifierNode{Value: "a"}, 23 }, 24 { 25 `"str"`, 26 &StringNode{Value: "str"}, 27 }, 28 { 29 "`hello\nworld`", 30 &StringNode{Value: `hello 31 world`}, 32 }, 33 { 34 "3", 35 &IntegerNode{Value: 3}, 36 }, 37 { 38 "0xFF", 39 &IntegerNode{Value: 255}, 40 }, 41 { 42 "0x6E", 43 &IntegerNode{Value: 110}, 44 }, 45 { 46 "0X63", 47 &IntegerNode{Value: 99}, 48 }, 49 { 50 "0o600", 51 &IntegerNode{Value: 384}, 52 }, 53 { 54 "0O45", 55 &IntegerNode{Value: 37}, 56 }, 57 { 58 "0b10", 59 &IntegerNode{Value: 2}, 60 }, 61 { 62 "0B101011", 63 &IntegerNode{Value: 43}, 64 }, 65 { 66 "10_000_000", 67 &IntegerNode{Value: 10_000_000}, 68 }, 69 { 70 "2.5", 71 &FloatNode{Value: 2.5}, 72 }, 73 { 74 "1e9", 75 &FloatNode{Value: 1e9}, 76 }, 77 { 78 "true", 79 &BoolNode{Value: true}, 80 }, 81 { 82 "false", 83 &BoolNode{Value: false}, 84 }, 85 { 86 "nil", 87 &NilNode{}, 88 }, 89 { 90 "-3", 91 &UnaryNode{Operator: "-", 92 Node: &IntegerNode{Value: 3}}, 93 }, 94 { 95 "-2^2", 96 &UnaryNode{ 97 Operator: "-", 98 Node: &BinaryNode{ 99 Operator: "^", 100 Left: &IntegerNode{Value: 2}, 101 Right: &IntegerNode{Value: 2}, 102 }, 103 }, 104 }, 105 { 106 "1 - 2", 107 &BinaryNode{Operator: "-", 108 Left: &IntegerNode{Value: 1}, 109 Right: &IntegerNode{Value: 2}}, 110 }, 111 { 112 "(1 - 2) * 3", 113 &BinaryNode{ 114 Operator: "*", 115 Left: &BinaryNode{ 116 Operator: "-", 117 Left: &IntegerNode{Value: 1}, 118 Right: &IntegerNode{Value: 2}, 119 }, 120 Right: &IntegerNode{Value: 3}, 121 }, 122 }, 123 { 124 "a or b or c", 125 &BinaryNode{Operator: "or", 126 Left: &BinaryNode{Operator: "or", 127 Left: &IdentifierNode{Value: "a"}, 128 Right: &IdentifierNode{Value: "b"}}, 129 Right: &IdentifierNode{Value: "c"}}, 130 }, 131 { 132 "a or b and c", 133 &BinaryNode{Operator: "or", 134 Left: &IdentifierNode{Value: "a"}, 135 Right: &BinaryNode{Operator: "and", 136 Left: &IdentifierNode{Value: "b"}, 137 Right: &IdentifierNode{Value: "c"}}}, 138 }, 139 { 140 "(a or b) and c", 141 &BinaryNode{Operator: "and", 142 Left: &BinaryNode{Operator: "or", 143 Left: &IdentifierNode{Value: "a"}, 144 Right: &IdentifierNode{Value: "b"}}, 145 Right: &IdentifierNode{Value: "c"}}, 146 }, 147 { 148 "2**4-1", 149 &BinaryNode{Operator: "-", 150 Left: &BinaryNode{Operator: "**", 151 Left: &IntegerNode{Value: 2}, 152 Right: &IntegerNode{Value: 4}}, 153 Right: &IntegerNode{Value: 1}}, 154 }, 155 { 156 "foo(bar())", 157 &CallNode{Callee: &IdentifierNode{Value: "foo"}, 158 Arguments: []Node{&CallNode{Callee: &IdentifierNode{Value: "bar"}, 159 Arguments: []Node{}}}}, 160 }, 161 { 162 `foo("arg1", 2, true)`, 163 &CallNode{Callee: &IdentifierNode{Value: "foo"}, 164 Arguments: []Node{&StringNode{Value: "arg1"}, 165 &IntegerNode{Value: 2}, 166 &BoolNode{Value: true}}}, 167 }, 168 { 169 "foo.bar", 170 &MemberNode{Node: &IdentifierNode{Value: "foo"}, 171 Property: &StringNode{Value: "bar"}}, 172 }, 173 { 174 "foo['all']", 175 &MemberNode{Node: &IdentifierNode{Value: "foo"}, 176 Property: &StringNode{Value: "all"}}, 177 }, 178 { 179 "foo.bar()", 180 &CallNode{Callee: &MemberNode{Node: &IdentifierNode{Value: "foo"}, 181 Property: &StringNode{Value: "bar"}, Method: true}, 182 Arguments: []Node{}}, 183 }, 184 { 185 `foo.bar("arg1", 2, true)`, 186 &CallNode{Callee: &MemberNode{Node: &IdentifierNode{Value: "foo"}, 187 Property: &StringNode{Value: "bar"}, Method: true}, 188 Arguments: []Node{&StringNode{Value: "arg1"}, 189 &IntegerNode{Value: 2}, 190 &BoolNode{Value: true}}}, 191 }, 192 { 193 "foo[3]", 194 &MemberNode{Node: &IdentifierNode{Value: "foo"}, 195 Property: &IntegerNode{Value: 3}}, 196 }, 197 { 198 "true ? true : false", 199 &ConditionalNode{Cond: &BoolNode{Value: true}, 200 Exp1: &BoolNode{Value: true}, 201 Exp2: &BoolNode{}}, 202 }, 203 { 204 "a?[b]:c", 205 &ConditionalNode{Cond: &IdentifierNode{Value: "a"}, 206 Exp1: &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "b"}}}, 207 Exp2: &IdentifierNode{Value: "c"}}, 208 }, 209 { 210 "a.b().c().d[33]", 211 &MemberNode{ 212 Node: &MemberNode{ 213 Node: &CallNode{ 214 Callee: &MemberNode{ 215 Node: &CallNode{ 216 Callee: &MemberNode{ 217 Node: &IdentifierNode{ 218 Value: "a", 219 }, 220 Property: &StringNode{ 221 Value: "b", 222 }, 223 Method: true, 224 }, 225 Arguments: []Node{}, 226 }, 227 Property: &StringNode{ 228 Value: "c", 229 }, 230 Method: true, 231 }, 232 Arguments: []Node{}, 233 }, 234 Property: &StringNode{ 235 Value: "d", 236 }, 237 }, 238 Property: &IntegerNode{Value: 33}}, 239 }, 240 { 241 "'a' == 'b'", 242 &BinaryNode{Operator: "==", 243 Left: &StringNode{Value: "a"}, 244 Right: &StringNode{Value: "b"}}, 245 }, 246 { 247 "+0 != -0", 248 &BinaryNode{Operator: "!=", 249 Left: &UnaryNode{Operator: "+", 250 Node: &IntegerNode{}}, 251 Right: &UnaryNode{Operator: "-", 252 Node: &IntegerNode{}}}, 253 }, 254 { 255 "[a, b, c]", 256 &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "a"}, 257 &IdentifierNode{Value: "b"}, 258 &IdentifierNode{Value: "c"}}}, 259 }, 260 { 261 "{foo:1, bar:2}", 262 &MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "foo"}, 263 Value: &IntegerNode{Value: 1}}, 264 &PairNode{Key: &StringNode{Value: "bar"}, 265 Value: &IntegerNode{Value: 2}}}}, 266 }, 267 { 268 "{foo:1, bar:2, }", 269 &MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "foo"}, 270 Value: &IntegerNode{Value: 1}}, 271 &PairNode{Key: &StringNode{Value: "bar"}, 272 Value: &IntegerNode{Value: 2}}}}, 273 }, 274 { 275 `{"a": 1, 'b': 2}`, 276 &MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "a"}, 277 Value: &IntegerNode{Value: 1}}, 278 &PairNode{Key: &StringNode{Value: "b"}, 279 Value: &IntegerNode{Value: 2}}}}, 280 }, 281 { 282 "[1].foo", 283 &MemberNode{Node: &ArrayNode{Nodes: []Node{&IntegerNode{Value: 1}}}, 284 Property: &StringNode{Value: "foo"}}, 285 }, 286 { 287 "{foo:1}.bar", 288 &MemberNode{Node: &MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "foo"}, 289 Value: &IntegerNode{Value: 1}}}}, 290 Property: &StringNode{Value: "bar"}}, 291 }, 292 { 293 "len(foo)", 294 &BuiltinNode{ 295 Name: "len", 296 Arguments: []Node{ 297 &IdentifierNode{Value: "foo"}, 298 }, 299 }, 300 }, 301 { 302 `foo matches "foo"`, 303 &BinaryNode{ 304 Operator: "matches", 305 Left: &IdentifierNode{Value: "foo"}, 306 Right: &StringNode{Value: "foo"}}, 307 }, 308 { 309 `foo not matches "foo"`, 310 &UnaryNode{ 311 Operator: "not", 312 Node: &BinaryNode{ 313 Operator: "matches", 314 Left: &IdentifierNode{Value: "foo"}, 315 Right: &StringNode{Value: "foo"}}}, 316 }, 317 { 318 `foo matches regex`, 319 &BinaryNode{ 320 Operator: "matches", 321 Left: &IdentifierNode{Value: "foo"}, 322 Right: &IdentifierNode{Value: "regex"}}, 323 }, 324 { 325 `foo contains "foo"`, 326 &BinaryNode{ 327 Operator: "contains", 328 Left: &IdentifierNode{Value: "foo"}, 329 Right: &StringNode{Value: "foo"}}, 330 }, 331 { 332 `foo not contains "foo"`, 333 &UnaryNode{ 334 Operator: "not", 335 Node: &BinaryNode{Operator: "contains", 336 Left: &IdentifierNode{Value: "foo"}, 337 Right: &StringNode{Value: "foo"}}}, 338 }, 339 { 340 `foo startsWith "foo"`, 341 &BinaryNode{Operator: "startsWith", 342 Left: &IdentifierNode{Value: "foo"}, 343 Right: &StringNode{Value: "foo"}}, 344 }, 345 { 346 `foo endsWith "foo"`, 347 &BinaryNode{Operator: "endsWith", 348 Left: &IdentifierNode{Value: "foo"}, 349 Right: &StringNode{Value: "foo"}}, 350 }, 351 { 352 "1..9", 353 &BinaryNode{Operator: "..", 354 Left: &IntegerNode{Value: 1}, 355 Right: &IntegerNode{Value: 9}}, 356 }, 357 { 358 "0 in []", 359 &BinaryNode{Operator: "in", 360 Left: &IntegerNode{}, 361 Right: &ArrayNode{Nodes: []Node{}}}, 362 }, 363 { 364 "not in_var", 365 &UnaryNode{Operator: "not", 366 Node: &IdentifierNode{Value: "in_var"}}, 367 }, 368 { 369 "-1 not in [1, 2, 3, 4]", 370 &UnaryNode{Operator: "not", 371 Node: &BinaryNode{Operator: "in", 372 Left: &UnaryNode{Operator: "-", Node: &IntegerNode{Value: 1}}, 373 Right: &ArrayNode{Nodes: []Node{ 374 &IntegerNode{Value: 1}, 375 &IntegerNode{Value: 2}, 376 &IntegerNode{Value: 3}, 377 &IntegerNode{Value: 4}, 378 }}}}, 379 }, 380 { 381 "1*8 not in [1, 2, 3, 4]", 382 &UnaryNode{Operator: "not", 383 Node: &BinaryNode{Operator: "in", 384 Left: &BinaryNode{Operator: "*", 385 Left: &IntegerNode{Value: 1}, 386 Right: &IntegerNode{Value: 8}, 387 }, 388 Right: &ArrayNode{Nodes: []Node{ 389 &IntegerNode{Value: 1}, 390 &IntegerNode{Value: 2}, 391 &IntegerNode{Value: 3}, 392 &IntegerNode{Value: 4}, 393 }}}}, 394 }, 395 { 396 "2==2 ? false : 3 not in [1, 2, 5]", 397 &ConditionalNode{ 398 Cond: &BinaryNode{ 399 Operator: "==", 400 Left: &IntegerNode{Value: 2}, 401 Right: &IntegerNode{Value: 2}, 402 }, 403 Exp1: &BoolNode{Value: false}, 404 Exp2: &UnaryNode{ 405 Operator: "not", 406 Node: &BinaryNode{ 407 Operator: "in", 408 Left: &IntegerNode{Value: 3}, 409 Right: &ArrayNode{Nodes: []Node{ 410 &IntegerNode{Value: 1}, 411 &IntegerNode{Value: 2}, 412 &IntegerNode{Value: 5}, 413 }}}}}, 414 }, 415 { 416 "'foo' + 'bar' not matches 'foobar'", 417 &UnaryNode{Operator: "not", 418 Node: &BinaryNode{Operator: "matches", 419 Left: &BinaryNode{Operator: "+", 420 Left: &StringNode{Value: "foo"}, 421 Right: &StringNode{Value: "bar"}}, 422 Right: &StringNode{Value: "foobar"}}}, 423 }, 424 { 425 "all(Tickets, #)", 426 &BuiltinNode{ 427 Name: "all", 428 Arguments: []Node{ 429 &IdentifierNode{Value: "Tickets"}, 430 &ClosureNode{ 431 Node: &PointerNode{}, 432 }}}, 433 }, 434 { 435 "all(Tickets, {.Price > 0})", 436 &BuiltinNode{ 437 Name: "all", 438 Arguments: []Node{ 439 &IdentifierNode{Value: "Tickets"}, 440 &ClosureNode{ 441 Node: &BinaryNode{ 442 Operator: ">", 443 Left: &MemberNode{Node: &PointerNode{}, 444 Property: &StringNode{Value: "Price"}}, 445 Right: &IntegerNode{Value: 0}}}}}, 446 }, 447 { 448 "one(Tickets, {#.Price > 0})", 449 &BuiltinNode{ 450 Name: "one", 451 Arguments: []Node{ 452 &IdentifierNode{Value: "Tickets"}, 453 &ClosureNode{ 454 Node: &BinaryNode{ 455 Operator: ">", 456 Left: &MemberNode{ 457 Node: &PointerNode{}, 458 Property: &StringNode{Value: "Price"}, 459 }, 460 Right: &IntegerNode{Value: 0}}}}}, 461 }, 462 { 463 "filter(Prices, {# > 100})", 464 &BuiltinNode{Name: "filter", 465 Arguments: []Node{&IdentifierNode{Value: "Prices"}, 466 &ClosureNode{Node: &BinaryNode{Operator: ">", 467 Left: &PointerNode{}, 468 Right: &IntegerNode{Value: 100}}}}}, 469 }, 470 { 471 "array[1:2]", 472 &SliceNode{Node: &IdentifierNode{Value: "array"}, 473 From: &IntegerNode{Value: 1}, 474 To: &IntegerNode{Value: 2}}, 475 }, 476 { 477 "array[:2]", 478 &SliceNode{Node: &IdentifierNode{Value: "array"}, 479 To: &IntegerNode{Value: 2}}, 480 }, 481 { 482 "array[1:]", 483 &SliceNode{Node: &IdentifierNode{Value: "array"}, 484 From: &IntegerNode{Value: 1}}, 485 }, 486 { 487 "array[:]", 488 &SliceNode{Node: &IdentifierNode{Value: "array"}}, 489 }, 490 { 491 "[]", 492 &ArrayNode{}, 493 }, 494 { 495 "foo ?? bar", 496 &BinaryNode{Operator: "??", 497 Left: &IdentifierNode{Value: "foo"}, 498 Right: &IdentifierNode{Value: "bar"}}, 499 }, 500 { 501 "foo ?? bar ?? baz", 502 &BinaryNode{Operator: "??", 503 Left: &BinaryNode{Operator: "??", 504 Left: &IdentifierNode{Value: "foo"}, 505 Right: &IdentifierNode{Value: "bar"}}, 506 Right: &IdentifierNode{Value: "baz"}}, 507 }, 508 { 509 "foo ?? (bar || baz)", 510 &BinaryNode{Operator: "??", 511 Left: &IdentifierNode{Value: "foo"}, 512 Right: &BinaryNode{Operator: "||", 513 Left: &IdentifierNode{Value: "bar"}, 514 Right: &IdentifierNode{Value: "baz"}}}, 515 }, 516 { 517 "foo || bar ?? baz", 518 &BinaryNode{Operator: "||", 519 Left: &IdentifierNode{Value: "foo"}, 520 Right: &BinaryNode{Operator: "??", 521 Left: &IdentifierNode{Value: "bar"}, 522 Right: &IdentifierNode{Value: "baz"}}}, 523 }, 524 { 525 "foo ?? bar()", 526 &BinaryNode{Operator: "??", 527 Left: &IdentifierNode{Value: "foo"}, 528 Right: &CallNode{Callee: &IdentifierNode{Value: "bar"}}}, 529 }, 530 { 531 "true | ok()", 532 &CallNode{ 533 Callee: &IdentifierNode{Value: "ok"}, 534 Arguments: []Node{ 535 &BoolNode{Value: true}}}}, 536 { 537 `let foo = a + b; foo + c`, 538 &VariableDeclaratorNode{ 539 Name: "foo", 540 Value: &BinaryNode{Operator: "+", 541 Left: &IdentifierNode{Value: "a"}, 542 Right: &IdentifierNode{Value: "b"}}, 543 Expr: &BinaryNode{Operator: "+", 544 Left: &IdentifierNode{Value: "foo"}, 545 Right: &IdentifierNode{Value: "c"}}}, 546 }, 547 { 548 `map([], #index)`, 549 &BuiltinNode{ 550 Name: "map", 551 Arguments: []Node{ 552 &ArrayNode{}, 553 &ClosureNode{ 554 Node: &PointerNode{Name: "index"}, 555 }, 556 }, 557 }, 558 }, 559 { 560 `::split("a,b,c", ",")`, 561 &BuiltinNode{ 562 Name: "split", 563 Arguments: []Node{ 564 &StringNode{Value: "a,b,c"}, 565 &StringNode{Value: ","}, 566 }, 567 }, 568 }, 569 { 570 `::split("a,b,c", ",")[0]`, 571 &MemberNode{ 572 Node: &BuiltinNode{ 573 Name: "split", 574 Arguments: []Node{ 575 &StringNode{Value: "a,b,c"}, 576 &StringNode{Value: ","}, 577 }, 578 }, 579 Property: &IntegerNode{Value: 0}, 580 }, 581 }, 582 { 583 `"hello"[1:3]`, 584 &SliceNode{ 585 Node: &StringNode{Value: "hello"}, 586 From: &IntegerNode{Value: 1}, 587 To: &IntegerNode{Value: 3}, 588 }, 589 }, 590 { 591 `1 < 2 > 3`, 592 &BinaryNode{ 593 Operator: "&&", 594 Left: &BinaryNode{ 595 Operator: "<", 596 Left: &IntegerNode{Value: 1}, 597 Right: &IntegerNode{Value: 2}, 598 }, 599 Right: &BinaryNode{ 600 Operator: ">", 601 Left: &IntegerNode{Value: 2}, 602 Right: &IntegerNode{Value: 3}, 603 }, 604 }, 605 }, 606 { 607 `1 < 2 < 3 < 4`, 608 &BinaryNode{ 609 Operator: "&&", 610 Left: &BinaryNode{ 611 Operator: "&&", 612 Left: &BinaryNode{ 613 Operator: "<", 614 Left: &IntegerNode{Value: 1}, 615 Right: &IntegerNode{Value: 2}, 616 }, 617 Right: &BinaryNode{ 618 Operator: "<", 619 Left: &IntegerNode{Value: 2}, 620 Right: &IntegerNode{Value: 3}, 621 }, 622 }, 623 Right: &BinaryNode{ 624 Operator: "<", 625 Left: &IntegerNode{Value: 3}, 626 Right: &IntegerNode{Value: 4}, 627 }, 628 }, 629 }, 630 { 631 `1 < 2 < 3 == true`, 632 &BinaryNode{ 633 Operator: "==", 634 Left: &BinaryNode{ 635 Operator: "&&", 636 Left: &BinaryNode{ 637 Operator: "<", 638 Left: &IntegerNode{Value: 1}, 639 Right: &IntegerNode{Value: 2}, 640 }, 641 Right: &BinaryNode{ 642 Operator: "<", 643 Left: &IntegerNode{Value: 2}, 644 Right: &IntegerNode{Value: 3}, 645 }, 646 }, 647 Right: &BoolNode{Value: true}, 648 }, 649 }, 650 } 651 for _, test := range tests { 652 t.Run(test.input, func(t *testing.T) { 653 actual, err := parser.Parse(test.input) 654 require.NoError(t, err) 655 assert.Equal(t, Dump(test.want), Dump(actual.Node)) 656 }) 657 } 658 } 659 660 const errorTests = ` 661 foo. 662 unexpected end of expression (1:4) 663 | foo. 664 | ...^ 665 666 a+ 667 unexpected token EOF (1:2) 668 | a+ 669 | .^ 670 671 a ? (1+2) c 672 unexpected token Identifier("c") (1:11) 673 | a ? (1+2) c 674 | ..........^ 675 676 [a b] 677 unexpected token Identifier("b") (1:4) 678 | [a b] 679 | ...^ 680 681 foo.bar(a b) 682 unexpected token Identifier("b") (1:11) 683 | foo.bar(a b) 684 | ..........^ 685 686 {-} 687 a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator("-")) (1:2) 688 | {-} 689 | .^ 690 691 foo({.bar}) 692 a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(".")) (1:6) 693 | foo({.bar}) 694 | .....^ 695 696 .foo 697 cannot use pointer accessor outside closure (1:1) 698 | .foo 699 | ^ 700 701 [1, 2, 3,,] 702 unexpected token Operator(",") (1:10) 703 | [1, 2, 3,,] 704 | .........^ 705 706 [,] 707 unexpected token Operator(",") (1:2) 708 | [,] 709 | .^ 710 711 {,} 712 a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(",")) (1:2) 713 | {,} 714 | .^ 715 716 {foo:1, bar:2, ,} 717 unexpected token Operator(",") (1:16) 718 | {foo:1, bar:2, ,} 719 | ...............^ 720 721 foo ?? bar || baz 722 Operator (||) and coalesce expressions (??) cannot be mixed. Wrap either by parentheses. (1:12) 723 | foo ?? bar || baz 724 | ...........^ 725 726 0b15 727 bad number syntax: "0b15" (1:4) 728 | 0b15 729 | ...^ 730 731 0X10G 732 bad number syntax: "0X10G" (1:5) 733 | 0X10G 734 | ....^ 735 736 0o1E 737 invalid float literal: strconv.ParseFloat: parsing "0o1E": invalid syntax (1:4) 738 | 0o1E 739 | ...^ 740 741 0b1E 742 invalid float literal: strconv.ParseFloat: parsing "0b1E": invalid syntax (1:4) 743 | 0b1E 744 | ...^ 745 746 0b1E+6 747 bad number syntax: "0b1E+6" (1:6) 748 | 0b1E+6 749 | .....^ 750 751 0b1E+1 752 invalid float literal: strconv.ParseFloat: parsing "0b1E+1": invalid syntax (1:6) 753 | 0b1E+1 754 | .....^ 755 756 0o1E+1 757 invalid float literal: strconv.ParseFloat: parsing "0o1E+1": invalid syntax (1:6) 758 | 0o1E+1 759 | .....^ 760 761 1E 762 invalid float literal: strconv.ParseFloat: parsing "1E": invalid syntax (1:2) 763 | 1E 764 | .^ 765 766 1 not == [1, 2, 5] 767 unexpected token Operator("==") (1:7) 768 | 1 not == [1, 2, 5] 769 | ......^ 770 ` 771 772 func TestParse_error(t *testing.T) { 773 tests := strings.Split(strings.Trim(errorTests, "\n"), "\n\n") 774 for _, test := range tests { 775 input := strings.SplitN(test, "\n", 2) 776 if len(input) != 2 { 777 t.Errorf("syntax error in test: %q", test) 778 break 779 } 780 _, err := parser.Parse(input[0]) 781 if err == nil { 782 err = fmt.Errorf("<nil>") 783 } 784 assert.Equal(t, input[1], err.Error(), input[0]) 785 } 786 } 787 788 func TestParse_optional_chaining(t *testing.T) { 789 parseTests := []struct { 790 input string 791 expected Node 792 }{ 793 { 794 "foo?.bar.baz", 795 &ChainNode{ 796 Node: &MemberNode{ 797 Node: &MemberNode{ 798 Node: &IdentifierNode{Value: "foo"}, 799 Property: &StringNode{Value: "bar"}, 800 Optional: true, 801 }, 802 Property: &StringNode{Value: "baz"}, 803 }, 804 }, 805 }, 806 { 807 "foo.bar?.baz", 808 &ChainNode{ 809 Node: &MemberNode{ 810 Node: &MemberNode{ 811 Node: &IdentifierNode{Value: "foo"}, 812 Property: &StringNode{Value: "bar"}, 813 }, 814 Property: &StringNode{Value: "baz"}, 815 Optional: true, 816 }, 817 }, 818 }, 819 { 820 "foo?.bar?.baz", 821 &ChainNode{ 822 Node: &MemberNode{ 823 Node: &MemberNode{ 824 Node: &IdentifierNode{Value: "foo"}, 825 Property: &StringNode{Value: "bar"}, 826 Optional: true, 827 }, 828 Property: &StringNode{Value: "baz"}, 829 Optional: true, 830 }, 831 }, 832 }, 833 { 834 "!foo?.bar.baz", 835 &UnaryNode{ 836 Operator: "!", 837 Node: &ChainNode{ 838 Node: &MemberNode{ 839 Node: &MemberNode{ 840 Node: &IdentifierNode{Value: "foo"}, 841 Property: &StringNode{Value: "bar"}, 842 Optional: true, 843 }, 844 Property: &StringNode{Value: "baz"}, 845 }, 846 }, 847 }, 848 }, 849 { 850 "foo.bar[a?.b]?.baz", 851 &ChainNode{ 852 Node: &MemberNode{ 853 Node: &MemberNode{ 854 Node: &MemberNode{ 855 Node: &IdentifierNode{Value: "foo"}, 856 Property: &StringNode{Value: "bar"}, 857 }, 858 Property: &ChainNode{ 859 Node: &MemberNode{ 860 Node: &IdentifierNode{Value: "a"}, 861 Property: &StringNode{Value: "b"}, 862 Optional: true, 863 }, 864 }, 865 }, 866 Property: &StringNode{Value: "baz"}, 867 Optional: true, 868 }, 869 }, 870 }, 871 { 872 "foo.bar?.[0]", 873 &ChainNode{ 874 Node: &MemberNode{ 875 Node: &MemberNode{ 876 Node: &IdentifierNode{Value: "foo"}, 877 Property: &StringNode{Value: "bar"}, 878 }, 879 Property: &IntegerNode{Value: 0}, 880 Optional: true, 881 }, 882 }, 883 }, 884 } 885 for _, test := range parseTests { 886 actual, err := parser.Parse(test.input) 887 if err != nil { 888 t.Errorf("%s:\n%v", test.input, err) 889 continue 890 } 891 assert.Equal(t, Dump(test.expected), Dump(actual.Node), test.input) 892 } 893 } 894 895 func TestParse_pipe_operator(t *testing.T) { 896 input := "arr | map(.foo) | len() | Foo()" 897 expect := &CallNode{ 898 Callee: &IdentifierNode{Value: "Foo"}, 899 Arguments: []Node{ 900 &BuiltinNode{ 901 Name: "len", 902 Arguments: []Node{ 903 &BuiltinNode{ 904 Name: "map", 905 Arguments: []Node{ 906 &IdentifierNode{Value: "arr"}, 907 &ClosureNode{ 908 Node: &MemberNode{ 909 Node: &PointerNode{}, 910 Property: &StringNode{Value: "foo"}, 911 }}}}}}}} 912 913 actual, err := parser.Parse(input) 914 require.NoError(t, err) 915 assert.Equal(t, Dump(expect), Dump(actual.Node)) 916 }