1 package gval
2
3 import (
4 "context"
5 "fmt"
6 "testing"
7 "text/scanner"
8 )
9
10 func TestNoParameter(t *testing.T) {
11 testEvaluate(
12 []evaluationTest{
13 {
14 name: "Number",
15 expression: "100",
16 want: 100.0,
17 },
18 {
19 name: "Single PLUS",
20 expression: "51 + 49",
21 want: 100.0,
22 },
23 {
24 name: "Single MINUS",
25 expression: "100 - 51",
26 want: 49.0,
27 },
28 {
29 name: "Single BITWISE AND",
30 expression: "100 & 50",
31 want: 32.0,
32 },
33 {
34 name: "Single BITWISE OR",
35 expression: "100 | 50",
36 want: 118.0,
37 },
38 {
39 name: "Single BITWISE XOR",
40 expression: "100 ^ 50",
41 want: 86.0,
42 },
43 {
44 name: "Single shift left",
45 expression: "2 << 1",
46 want: 4.0,
47 },
48 {
49 name: "Single shift right",
50 expression: "2 >> 1",
51 want: 1.0,
52 },
53 {
54 name: "Single BITWISE NOT",
55 expression: "~10",
56 want: -11.0,
57 },
58 {
59
60 name: "Single MULTIPLY",
61 expression: "5 * 20",
62 want: 100.0,
63 },
64 {
65
66 name: "Single DIVIDE",
67 expression: "100 / 20",
68 want: 5.0,
69 },
70 {
71
72 name: "Single even MODULUS",
73 expression: "100 % 2",
74 want: 0.0,
75 },
76 {
77 name: "Single odd MODULUS",
78 expression: "101 % 2",
79 want: 1.0,
80 },
81 {
82
83 name: "Single EXPONENT",
84 expression: "10 ** 2",
85 want: 100.0,
86 },
87 {
88
89 name: "Compound PLUS",
90 expression: "20 + 30 + 50",
91 want: 100.0,
92 },
93 {
94
95 name: "Compound BITWISE AND",
96 expression: "20 & 30 & 50",
97 want: 16.0,
98 },
99 {
100 name: "Mutiple operators",
101 expression: "20 * 5 - 49",
102 want: 51.0,
103 },
104 {
105 name: "Parenthesis usage",
106 expression: "100 - (5 * 10)",
107 want: 50.0,
108 },
109 {
110
111 name: "Nested parentheses",
112 expression: "50 + (5 * (15 - 5))",
113 want: 100.0,
114 },
115 {
116
117 name: "Nested parentheses with bitwise",
118 expression: "100 ^ (23 * (2 | 5))",
119 want: 197.0,
120 },
121 {
122 name: "Logical OR operation of two clauses",
123 expression: "(1 == 1) || (true == true)",
124 want: true,
125 },
126 {
127 name: "Logical AND operation of two clauses",
128 expression: "(1 == 1) && (true == true)",
129 want: true,
130 },
131 {
132
133 name: "Implicit boolean",
134 expression: "2 > 1",
135 want: true,
136 },
137 {
138 name: "Equal test minus numbers and no spaces",
139 expression: "-1==-1",
140 want: true,
141 },
142 {
143
144 name: "Compound boolean",
145 expression: "5 < 10 && 1 < 5",
146 want: true,
147 },
148 {
149 name: "Evaluated true && false operation (for issue #8)",
150 expression: "1 > 10 && 11 > 10",
151 want: false,
152 },
153 {
154
155 name: "Evaluated true && false operation (for issue #8)",
156 expression: "true == true && false == true",
157 want: false,
158 },
159 {
160
161 name: "Parenthesis boolean",
162 expression: "10 < 50 && (1 != 2 && 1 > 0)",
163 want: true,
164 },
165 {
166 name: "Comparison of string constants",
167 expression: `"foo" == "foo"`,
168 want: true,
169 },
170 {
171
172 name: "NEQ comparison of string constants",
173 expression: `"foo" != "bar"`,
174 want: true,
175 },
176 {
177
178 name: "REQ comparison of string constants",
179 expression: `"foobar" =~ "oba"`,
180 want: true,
181 },
182 {
183
184 name: "NREQ comparison of string constants",
185 expression: `"foo" !~ "bar"`,
186 want: true,
187 },
188 {
189
190 name: "Multiplicative/additive order",
191 expression: "5 + 10 * 2",
192 want: 25.0,
193 },
194 {
195 name: "Multiple constant multiplications",
196 expression: "10 * 10 * 10",
197 want: 1000.0,
198 },
199 {
200
201 name: "Multiple adds/multiplications",
202 expression: "10 * 10 * 10 + 1 * 10 * 10",
203 want: 1100.0,
204 },
205 {
206
207 name: "Modulus operatorPrecedence",
208 expression: "1 + 101 % 2 * 5",
209 want: 6.0,
210 },
211 {
212 name: "Exponent operatorPrecedence",
213 expression: "1 + 5 ** 3 % 2 * 5",
214 want: 6.0,
215 },
216 {
217
218 name: "Bit shift operatorPrecedence",
219 expression: "50 << 1 & 90",
220 want: 64.0,
221 },
222 {
223
224 name: "Bit shift operatorPrecedence",
225 expression: "90 & 50 << 1",
226 want: 64.0,
227 },
228 {
229
230 name: "Bit shift operatorPrecedence amongst non-bitwise",
231 expression: "90 + 50 << 1 * 5",
232 want: 4480.0,
233 },
234 {
235 name: "Order of non-commutative same-operatorPrecedence operators (additive)",
236 expression: "1 - 2 - 4 - 8",
237 want: -13.0,
238 },
239 {
240 name: "Order of non-commutative same-operatorPrecedence operators (multiplicative)",
241 expression: "1 * 4 / 2 * 8",
242 want: 16.0,
243 },
244 {
245 name: "Null coalesce operatorPrecedence",
246 expression: "true ?? true ? 100 + 200 : 400",
247 want: 300.0,
248 },
249 {
250 name: "Identical date equivalence",
251 expression: `"2014-01-02 14:12:22" == "2014-01-02 14:12:22"`,
252 want: true,
253 },
254 {
255 name: "Positive date GT",
256 expression: `"2014-01-02 14:12:22" > "2014-01-02 12:12:22"`,
257 want: true,
258 },
259 {
260 name: "Negative date GT",
261 expression: `"2014-01-02 14:12:22" > "2014-01-02 16:12:22"`,
262 want: false,
263 },
264 {
265 name: "Positive date GTE",
266 expression: `"2014-01-02 14:12:22" >= "2014-01-02 12:12:22"`,
267 want: true,
268 },
269 {
270 name: "Negative date GTE",
271 expression: `"2014-01-02 14:12:22" >= "2014-01-02 16:12:22"`,
272 want: false,
273 },
274 {
275 name: "Positive date LT",
276 expression: `"2014-01-02 14:12:22" < "2014-01-02 16:12:22"`,
277 want: true,
278 },
279 {
280
281 name: "Negative date LT",
282 expression: `"2014-01-02 14:12:22" < "2014-01-02 11:12:22"`,
283 want: false,
284 },
285 {
286
287 name: "Positive date LTE",
288 expression: `"2014-01-02 09:12:22" <= "2014-01-02 12:12:22"`,
289 want: true,
290 },
291 {
292 name: "Negative date LTE",
293 expression: `"2014-01-02 14:12:22" <= "2014-01-02 11:12:22"`,
294 want: false,
295 },
296 {
297
298 name: "Sign prefix comparison",
299 expression: "-1 < 0",
300 want: true,
301 },
302 {
303
304 name: "Lexicographic LT",
305 expression: `"ab" < "abc"`,
306 want: true,
307 },
308 {
309 name: "Lexicographic LTE",
310 expression: `"ab" <= "abc"`,
311 want: true,
312 },
313 {
314
315 name: "Lexicographic GT",
316 expression: `"aba" > "abc"`,
317 want: false,
318 },
319 {
320
321 name: "Lexicographic GTE",
322 expression: `"aba" >= "abc"`,
323 want: false,
324 },
325 {
326
327 name: "Boolean sign prefix comparison",
328 expression: "!true == false",
329 want: true,
330 },
331 {
332 name: "Inversion of clause",
333 expression: "!(10 < 0)",
334 want: true,
335 },
336 {
337
338 name: "Negation after modifier",
339 expression: "10 * -10",
340 want: -100.0,
341 },
342 {
343
344 name: "Ternary with single boolean",
345 expression: "true ? 10",
346 want: 10.0,
347 },
348 {
349
350 name: "Ternary nil with single boolean",
351 expression: "false ? 10",
352 want: nil,
353 },
354 {
355 name: "Ternary with comparator boolean",
356 expression: "10 > 5 ? 35.50",
357 want: 35.50,
358 },
359 {
360
361 name: "Ternary nil with comparator boolean",
362 expression: "1 > 5 ? 35.50",
363 want: nil,
364 },
365 {
366
367 name: "Ternary with parentheses",
368 expression: "(5 * (15 - 5)) > 5 ? 35.50",
369 want: 35.50,
370 },
371 {
372
373 name: "Ternary operatorPrecedence",
374 expression: "true ? 35.50 > 10",
375 want: true,
376 },
377 {
378 name: "Ternary-else",
379 expression: "false ? 35.50 : 50",
380 want: 50.0,
381 },
382 {
383
384 name: "Ternary-else inside clause",
385 expression: "(false ? 5 : 35.50) > 10",
386 want: true,
387 },
388 {
389
390 name: "Ternary-else (true-case) inside clause",
391 expression: "(true ? 1 : 5) < 10",
392 want: true,
393 },
394 {
395
396 name: "Ternary-else before comparator (negative case)",
397 expression: "true ? 1 : 5 > 10",
398 want: 1.0,
399 },
400 {
401 name: "Nested ternaries (#32)",
402 expression: "(2 == 2) ? 1 : (true ? 2 : 3)",
403 want: 1.0,
404 },
405 {
406
407 name: "Nested ternaries, right case (#32)",
408 expression: "false ? 1 : (true ? 2 : 3)",
409 want: 2.0,
410 },
411 {
412
413 name: "Doubly-nested ternaries (#32)",
414 expression: "true ? (false ? 1 : (false ? 2 : 3)) : (false ? 4 : 5)",
415 want: 3.0,
416 },
417 {
418
419 name: "String to string concat",
420 expression: `"foo" + "bar" == "foobar"`,
421 want: true,
422 },
423 {
424 name: "String to float64 concat",
425 expression: `"foo" + 123 == "foo123"`,
426 want: true,
427 },
428 {
429
430 name: "Float64 to string concat",
431 expression: `123 + "bar" == "123bar"`,
432 want: true,
433 },
434 {
435
436 name: "String to date concat",
437 expression: `"foo" + "02/05/1970" == "foobar"`,
438 want: false,
439 },
440 {
441
442 name: "String to bool concat",
443 expression: `"foo" + true == "footrue"`,
444 want: true,
445 },
446 {
447 name: "Bool to string concat",
448 expression: `true + "bar" == "truebar"`,
449 want: true,
450 },
451 {
452
453 name: "Null coalesce left",
454 expression: "1 ?? 2",
455 want: 1.0,
456 },
457 {
458
459 name: "Array membership literals",
460 expression: "1 in [1, 2, 3]",
461 want: true,
462 },
463 {
464
465 name: "Array membership literal with inversion",
466 expression: "!(1 in [1, 2, 3])",
467 want: false,
468 },
469 {
470 name: "Logical operator reordering (#30)",
471 expression: "(true && true) || (true && false)",
472 want: true,
473 },
474 {
475
476 name: "Logical operator reordering without parens (#30)",
477 expression: "true && true || true && false",
478 want: true,
479 },
480 {
481
482 name: "Logical operator reordering with multiple OR (#30)",
483 expression: "false || true && true || false",
484 want: true,
485 },
486 {
487 name: "Left-side multiple consecutive (should be reordered) operators",
488 expression: "(10 * 10 * 10) > 10",
489 want: true,
490 },
491 {
492
493 name: "Three-part non-paren logical op reordering (#44)",
494 expression: "false && true || true",
495 want: true,
496 },
497 {
498
499 name: "Three-part non-paren logical op reordering (#44), second one",
500 expression: "true || false && true",
501 want: true,
502 },
503 {
504 name: "Logical operator reordering without parens (#45)",
505 expression: "true && true || false && false",
506 want: true,
507 },
508 {
509 name: "Single function",
510 expression: "foo()",
511 extension: Function("foo", func(arguments ...interface{}) (interface{}, error) {
512 return true, nil
513 }),
514
515 want: true,
516 },
517 {
518 name: "Func with argument",
519 expression: "passthrough(1)",
520 extension: Function("passthrough", func(arguments ...interface{}) (interface{}, error) {
521 return arguments[0], nil
522 }),
523 want: 1.0,
524 },
525 {
526 name: "Func with arguments",
527 expression: "passthrough(1, 2)",
528 extension: Function("passthrough", func(arguments ...interface{}) (interface{}, error) {
529 return arguments[0].(float64) + arguments[1].(float64), nil
530 }),
531 want: 3.0,
532 },
533 {
534 name: "Nested function with operatorPrecedence",
535 expression: "sum(1, sum(2, 3), 2 + 2, true ? 4 : 5)",
536 extension: Function("sum", func(arguments ...interface{}) (interface{}, error) {
537 sum := 0.0
538 for _, v := range arguments {
539 sum += v.(float64)
540 }
541 return sum, nil
542 }),
543 want: 14.0,
544 },
545 {
546 name: "Empty function and modifier, compared",
547 expression: "numeric()-1 > 0",
548 extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) {
549 return 2.0, nil
550 }),
551 want: true,
552 },
553 {
554 name: "Empty function comparator",
555 expression: "numeric() > 0",
556 extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) {
557 return 2.0, nil
558 }),
559 want: true,
560 },
561 {
562
563 name: "Empty function logical operator",
564 expression: "success() && !false",
565 extension: Function("success", func(arguments ...interface{}) (interface{}, error) {
566 return true, nil
567 }),
568 want: true,
569 },
570 {
571 name: "Empty function ternary",
572 expression: "nope() ? 1 : 2.0",
573 extension: Function("nope", func(arguments ...interface{}) (interface{}, error) {
574 return false, nil
575 }),
576 want: 2.0,
577 },
578 {
579
580 name: "Empty function null coalesce",
581 expression: "null() ?? 2",
582 extension: Function("null", func(arguments ...interface{}) (interface{}, error) {
583 return nil, nil
584 }),
585 want: 2.0,
586 },
587 {
588 name: "Empty function with prefix",
589 expression: "-ten()",
590 extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
591 return 10.0, nil
592 }),
593 want: -10.0,
594 },
595 {
596 name: "Empty function as part of chain",
597 expression: "10 - numeric() - 2",
598 extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) {
599 return 5.0, nil
600 }),
601 want: 3.0,
602 },
603 {
604 name: "Empty function near separator",
605 expression: "10 in [1, 2, 3, ten(), 8]",
606 extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
607 return 10.0, nil
608 }),
609 want: true,
610 },
611 {
612 name: "Enclosed empty function with modifier and comparator (#28)",
613 expression: "(ten() - 1) > 3",
614 extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
615 return 10.0, nil
616 }),
617 want: true,
618 },
619 {
620 name: "Array",
621 expression: `[(ten() - 1) > 3, (ten() - 1),"hey"]`,
622 extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
623 return 10.0, nil
624 }),
625 want: []interface{}{true, 9., "hey"},
626 },
627 {
628 name: "Object",
629 expression: `{1: (ten() - 1) > 3, 7 + ".X" : (ten() - 1),"hello" : "hey"}`,
630 extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
631 return 10.0, nil
632 }),
633 want: map[string]interface{}{"1": true, "7.X": 9., "hello": "hey"},
634 },
635 {
636 name: "Object negativ value",
637 expression: `{1: -1,"hello" : "hey"}`,
638 want: map[string]interface{}{"1": -1., "hello": "hey"},
639 },
640 {
641 name: "Empty Array",
642 expression: `[]`,
643 want: []interface{}{},
644 },
645 {
646 name: "Empty Object",
647 expression: `{}`,
648 want: map[string]interface{}{},
649 },
650 {
651 name: "Variadic",
652 expression: `sum(1,2,3,4)`,
653 extension: Function("sum", func(arguments ...float64) (interface{}, error) {
654 sum := 0.
655 for _, a := range arguments {
656 sum += a
657 }
658 return sum, nil
659 }),
660 want: 10.0,
661 },
662 {
663 name: "Ident Operator",
664 expression: `1 plus 1`,
665 extension: InfixNumberOperator("plus", func(a, b float64) (interface{}, error) {
666 return a + b, nil
667 }),
668 want: 2.0,
669 },
670 {
671 name: "Postfix Operator",
672 expression: `4§`,
673 extension: PostfixOperator("§", func(_ context.Context, _ *Parser, eval Evaluable) (Evaluable, error) {
674 return func(ctx context.Context, parameter interface{}) (interface{}, error) {
675 i, err := eval.EvalInt(ctx, parameter)
676 if err != nil {
677 return nil, err
678 }
679 return fmt.Sprintf("§%d", i), nil
680 }, nil
681 }),
682 want: "§4",
683 },
684 {
685 name: "Tabs as non-whitespace",
686 expression: "4\t5\t6",
687 extension: NewLanguage(
688 Init(func(ctx context.Context, p *Parser) (Evaluable, error) {
689 p.SetWhitespace('\n', '\r', ' ')
690 return p.ParseExpression(ctx)
691 }),
692 InfixNumberOperator("\t", func(a, b float64) (interface{}, error) {
693 return a * b, nil
694 }),
695 ),
696 want: 120.0,
697 },
698 {
699 name: "Handle all other prefixes",
700 expression: "^foo + $bar + &baz",
701 extension: DefaultExtension(func(ctx context.Context, p *Parser) (Evaluable, error) {
702 var mul int
703 switch p.TokenText() {
704 case "^":
705 mul = 1
706 case "$":
707 mul = 2
708 case "&":
709 mul = 3
710 }
711
712 switch p.Scan() {
713 case scanner.Ident:
714 return p.Const(mul * len(p.TokenText())), nil
715 default:
716 return nil, p.Expected("length multiplier", scanner.Ident)
717 }
718 }),
719 want: 18.0,
720 },
721 {
722 name: "Embed languages",
723 expression: "left { 5 + 5 } right",
724 extension: func() Language {
725 step := func(ctx context.Context, p *Parser, cur Evaluable) (Evaluable, error) {
726 next, err := p.ParseExpression(ctx)
727 if err != nil {
728 return nil, err
729 }
730
731 return func(ctx context.Context, parameter interface{}) (interface{}, error) {
732 us, err := cur.EvalString(ctx, parameter)
733 if err != nil {
734 return nil, err
735 }
736
737 them, err := next.EvalString(ctx, parameter)
738 if err != nil {
739 return nil, err
740 }
741
742 return us + them, nil
743 }, nil
744 }
745
746 return NewLanguage(
747 Init(func(ctx context.Context, p *Parser) (Evaluable, error) {
748 p.SetWhitespace()
749 p.SetMode(0)
750
751 return p.ParseExpression(ctx)
752 }),
753 DefaultExtension(func(ctx context.Context, p *Parser) (Evaluable, error) {
754 return step(ctx, p, p.Const(p.TokenText()))
755 }),
756 PrefixExtension(scanner.EOF, func(ctx context.Context, p *Parser) (Evaluable, error) {
757 return p.Const(""), nil
758 }),
759 PrefixExtension('{', func(ctx context.Context, p *Parser) (Evaluable, error) {
760 eval, err := p.ParseSublanguage(ctx, Full())
761 if err != nil {
762 return nil, err
763 }
764
765 switch p.Scan() {
766 case '}':
767 default:
768 return nil, p.Expected("embedded", '}')
769 }
770
771 return step(ctx, p, eval)
772 }),
773 )
774 }(),
775 want: "left 10 right",
776 },
777 {
778 name: "Late binding",
779 expression: "5 * [ 10 * { 20 / [ 10 ] } ]",
780 extension: func() Language {
781 var inner, outer Language
782
783 parseCurly := func(ctx context.Context, p *Parser) (Evaluable, error) {
784 eval, err := p.ParseSublanguage(ctx, outer)
785 if err != nil {
786 return nil, err
787 }
788
789 if p.Scan() != '}' {
790 return nil, p.Expected("end", '}')
791 }
792
793 return eval, nil
794 }
795
796 parseSquare := func(ctx context.Context, p *Parser) (Evaluable, error) {
797 eval, err := p.ParseSublanguage(ctx, inner)
798 if err != nil {
799 return nil, err
800 }
801
802 if p.Scan() != ']' {
803 return nil, p.Expected("end", ']')
804 }
805
806 return eval, nil
807 }
808
809 inner = Full(PrefixExtension('{', parseCurly))
810 outer = Full(PrefixExtension('[', parseSquare))
811 return outer
812 }(),
813 want: 100.0,
814 },
815 },
816 t,
817 )
818 }
819
View as plain text