1 # Test Oil expressions
2
3 #### command sub $(echo hi)
4 var x = $(echo hi)
5 var y = $(echo '')
6 # Make sure we can operate on these values
7 echo x=${x:-default} y=${y:-default}
8 ## STDOUT:
9 x=hi y=default
10 ## END
11
12 #### shell array %(a 'b c')
13 shopt -s parse_at
14 var x = %(a 'b c')
15 var empty = %()
16 argv.py / @x @empty /
17
18 ## STDOUT:
19 ['/', 'a', 'b c', '/']
20 ## END
21
22 #### empty array and simple_word_eval (regression test)
23 shopt -s parse_at simple_word_eval
24 var empty = %()
25 echo len=${#empty[@]}
26 argv.py / @empty /
27
28 ## STDOUT:
29 len=0
30 ['/', '/']
31 ## END
32
33 #### Empty array and assignment builtin (regression)
34 # Bug happens with shell arrays too
35 empty=()
36 declare z=1 "${empty[@]}"
37 echo z=$z
38 ## STDOUT:
39 z=1
40 ## END
41
42 #### Shell arrays support tilde detection, static globbing, brace detection
43 shopt -s parse_at simple_word_eval
44 touch {foo,bar}.py
45 HOME=/home/bob
46 no_dynamic_glob='*.py'
47
48 var x = %(~/src *.py {andy,bob}@example.com $no_dynamic_glob)
49 argv.py @x
50 ## STDOUT:
51 ['/home/bob/src', 'bar.py', 'foo.py', 'andy@example.com', 'bob@example.com', '*.py']
52 ## END
53
54 #### augmented assignment doesn't work on shell arrays
55 shopt -s parse_at simple_word_eval
56 var x = %(a 'b c')
57 argv.py @x
58
59 setvar x += %(d e) # fatal error
60 argv.py @x
61 ## status: 1
62 ## STDOUT:
63 ['a', 'b c']
64 ## END
65
66 #### Set $HOME using 'var' (i.e. Oil string var in word evaluator)
67 var HOME = "foo"
68 echo $HOME
69 echo ~
70 ## STDOUT:
71 foo
72 foo
73 ## END
74
75 #### Use shell var in Oil expression
76 x='abc'
77 var length = len(x) # length in BYTES, unlike ${#x}
78 echo $length
79 ## STDOUT:
80 3
81 ## END
82
83 #### Length in two different contexts
84 x=(a b c)
85 x[10]=A
86 x[20]=B
87
88 # shell style: length is 5
89 echo shell=${#x[@]}
90
91 # Oil function call: length is 20. I think that makes sense? It's just a
92 # different notion of length.
93 echo oil=$len(x)
94
95 ## STDOUT:
96 shell=5
97 oil=21
98 ## END
99
100 #### $len(x) inside strings
101 var s = "abc"
102 echo -$len(s)-
103
104 # This already has a meaning ...
105 #echo "-$len(x)-"
106 #echo "-${len}(x)-"
107
108 ## STDOUT:
109 -3-
110 ## END
111
112 #### Func with multiple args in multiple contexts
113 var x = max(1+2, 3+4)
114 echo $x $max(1+2, 3+4)
115
116 ## STDOUT:
117 7 7
118 ## END
119
120
121 #### Trailing Comma in Param list
122 var x = max(1+2, 3+4,)
123 echo $x $max(1+2, 3+4,)
124
125 ## STDOUT:
126 7 7
127 ## END
128
129 #### @range()
130 shopt -s oil:all
131 write @range(10, 15, 2)
132 ## STDOUT:
133 10
134 12
135 14
136 ## END
137
138 #### Wrong sigil $range() shows representation of iterator?
139 shopt -s oil:basic
140 echo $range(10, 15, 2)
141 ## STDOUT:
142 TODO
143 ## END
144
145 #### Wrong sigil @max(3, 4)
146 shopt -s oil:basic
147 write @max(3, 4)
148 ## STDOUT:
149 TODO
150 ## END
151
152
153 #### nested expr contexts
154 var s = "123"
155
156 # lex_mode_e.ShCommand -> Expr -> ShCommand -> Expr
157 var x = $(echo $'len\n' $len(s))
158 echo $x
159 ## STDOUT:
160 len 3
161 ## END
162
163
164 # TODO:
165 # - test keyword args
166 # - test splatting *args, **kwargs
167 # - Multiline parsing
168 #
169 # var x = max(
170 # 1+2,
171 # 3+4,
172 # )
173 # echo $x $max(
174 # 1+2,
175 # 3+4,
176 # )
177
178 #### Test value.Obj inside shell arithmetic
179 var w = "3"
180 echo lt=$(( w < 4 ))
181 echo gt=$(( w > 4 ))
182
183 var z = 3
184 echo lt=$(( z < 4 ))
185 echo gt=$(( z > 4 ))
186 ## STDOUT:
187 lt=1
188 gt=0
189 lt=1
190 gt=0
191 ## END
192
193 #### Parse { var x = 42 }
194 shopt -s oil:basic
195 g() { var x = 42 }
196
197 var x = 1
198 f() { var x = 42; setvar x = 43 }
199 f
200 echo x=$x
201 ## STDOUT:
202 x=1
203 ## END
204
205 #### double quoted
206 var foo = "bar"
207 var x = "-$foo-${foo}-${undef:-default}-"
208 echo $x
209 ## STDOUT:
210 -bar-bar-default-
211 ## END
212
213 #### double quoted respects strict_array
214 shopt -s strict:all
215 var a = %(one two three)
216 var x = "-${a[@]}-"
217 echo $x
218 ## status: 1
219 ## stdout-json: ""
220
221 #### simple var sub $name $0 $1 $? etc.
222 ( exit 42 )
223 var status = $?
224 echo status=$status
225
226 set -- a b c
227 var one = $1
228 var two = $2
229 echo $one $two
230
231 var named = $one # equivalent to 'one'
232 echo named=$named
233
234 ## STDOUT:
235 status=42
236 a b
237 named=a
238 ## END
239
240 #### braced var sub ${x:-default}
241
242 # without double quotes
243
244 var b = ${foo:-default}
245 echo $b
246 var c = ${bar:-"-$b-"}
247 echo $c
248
249 var d = "${bar:-"-$c-"}" # another one
250 echo $d
251
252 ## STDOUT:
253 default
254 -default-
255 --default--
256 ## END
257
258 #### braced var sub respects strict_array
259 set -- a b c
260 var x = ${undef:-"$@"}
261 echo $x
262 shopt -s strict_array
263 setvar x = ${undef:-"$@"}
264 echo $x
265 ## status: 1
266 ## STDOUT:
267 a b c
268 ## END
269
270
271 #### null / true / false
272 shopt -s oil:basic
273 var n = null
274 if (n) {
275 echo yes
276 } else {
277 echo no
278 }
279 var t = true
280 if (t) {
281 echo yes
282 } else {
283 echo no
284 }
285 var f = false
286 if (f) {
287 echo yes
288 } else {
289 echo no
290 }
291 ## STDOUT:
292 no
293 yes
294 no
295 ## END
296
297 #### Integer literals
298 var d = 123
299 var b = 0b11
300 var o = 0o123
301 var h = 0xff
302 echo $d $b $o $h
303 ## STDOUT:
304 123 3 83 255
305 ## END
306
307 #### Integer literals with underscores
308 const dec = 65_536
309 const bin = 0b0001_0101
310 const oct = 0o001_755
311 const hex = 0x0001_000f
312
313 echo SHELL
314 echo $dec
315 echo $bin
316 echo $oct
317 echo $hex
318 const x = 1_1 + 0b1_1 + 0o1_1 + 0x1_1
319 echo sum $x
320
321 # This works under Python 3.6, but the continuous build has earlier versions
322 if false; then
323 echo ---
324 echo PYTHON
325
326 python3 -c '
327 print(65_536)
328 print(0b0001_0101)
329 print(0o001_755)
330 print(0x0001_000f)
331
332 # Weird syntax
333 print("sum", 1_1 + 0b1_1 + 0o1_1 + 0x1_1)
334 '
335 fi
336
337 ## STDOUT:
338 SHELL
339 65536
340 21
341 1005
342 65551
343 sum 40
344 ## END
345
346 #### Backslash char literal (is an integer)
347 const newline = \n
348 const backslash = \\
349 const sq = \'
350 const dq = \"
351 echo "$newline $backslash $sq $dq"
352 ## STDOUT:
353 10 92 39 34
354 ## END
355
356 #### \u{3bc} is char literal
357 shopt -s oil:all
358
359 var mu = \u{3bc}
360 if (mu === 0x3bc) { # this is the same!
361 echo 'yes'
362 }
363 echo "mu $mu"
364 ## STDOUT:
365 yes
366 mu 956
367 ## END
368
369 #### Pound char literal (is an integer)
370 const a = #'a'
371 const A = #'A'
372 echo "$a $A"
373 ## STDOUT:
374 97 65
375 ## END
376
377 #### The literal #''' isn't accepted (use \' instead)
378
379 # This looks too much like triple quoted strings!
380
381 echo nope
382 const bad = #'''
383 echo "$bad"
384
385 ## status: 2
386 ## STDOUT:
387 nope
388 ## END
389
390 #### Float Literals
391 shopt -s oil:basic
392 # 1+2 2.3
393 var x = 1.2 + 23.0e-1 # 3.5
394 if (x < 3.9) {
395 echo less
396 }
397 if (x > 3.4) {
398 echo great
399 }
400 ## STDOUT:
401 less
402 great
403 ## END
404
405 #### Float Literals with _ (requires re2c refinement)
406 shopt -s oil:basic
407 # 1+2 + 2.3
408 # add this _ here
409 var x = 1.2 + 2_3.0e-1 # 3.5
410 if (x < 3.9) {
411 echo less
412 }
413 if (x > 3.4) {
414 echo great
415 }
416 ## STDOUT:
417 less
418 great
419 ## END
420
421 #### Tuples
422 var zero = ()
423 var one = tup(42)
424 var two = (1,2)
425 echo $len(zero)
426 echo $len(one)
427 echo $len(two)
428 ## STDOUT:
429 0
430 1
431 2
432 ## END
433
434 #### List comprehension (deferred)
435 shopt -s oil:all
436
437 var n = [i*2 for i in range(5)]
438 write --sep ' ' @n
439
440 # TODO: Test this
441 #var n = [i*2 for i,j in range(5)]
442
443 var even = [i*2 for i in range(5) if i % 2 === 0]
444 write --sep ' ' @even
445 ## STDOUT:
446 0 2 4 6 8
447 0 4 8
448 ## END
449
450 #### in, not in
451 var d = [1,2,3]
452 var b = 1 in d
453 echo $b
454 setvar b = 0 in d
455 echo $b
456 setvar b = 0 not in d
457 echo $b
458 ## STDOUT:
459 true
460 false
461 true
462 ## END
463
464 #### Chained Comparisons
465 shopt -s oil:basic
466 if (1 < 2 < 3) {
467 echo '123'
468 }
469 if (1 < 2 <= 2 <= 3 < 4) {
470 echo '123'
471 }
472
473 if (1 < 2 < 2) {
474 echo '123'
475 } else {
476 echo 'no'
477 }
478 ## STDOUT:
479 123
480 123
481 no
482 ## END
483
484 #### dict with 'bare word' keys
485 var d0 = {}
486 echo len=$len(d0)
487 var d1 = {name: "hello"}
488 echo len=$len(d1)
489 var d2 = {name: "hello", other: 2}
490 echo len=$len(d2)
491 ## STDOUT:
492 len=0
493 len=1
494 len=2
495 ## END
496
497 #### dict with expression keys
498 var d1 = {['name']: "hello"}
499 echo len=$len(d1)
500 var v = d1['name']
501 echo $v
502
503 var key='k'
504 var d2 = {["$key"]: "bar"}
505 echo len=$len(d2)
506 var v2 = d2['k']
507 echo $v2
508
509 ## STDOUT:
510 len=1
511 hello
512 len=1
513 bar
514 ## END
515
516
517 #### dict literal with implicit value
518 var name = 'foo'
519 var d1 = {name}
520 echo len=$len(d1)
521 var v1 = d1['name']
522 echo $v1
523
524 var d2 = {name, other: 'val'}
525 echo len=$len(d2)
526 var v2 = d2['name']
527 echo $v2
528
529 ## STDOUT:
530 len=1
531 foo
532 len=2
533 foo
534 ## END
535
536 #### Dict literal with string keys
537 var d = {'sq': 123}
538 var v = d['sq']
539 echo $v
540
541 var x = "q"
542 var d2 = {"d$x": 456}
543 var v2 = d2["dq"]
544 echo $v2
545 ## STDOUT:
546 123
547 456
548 ## END
549
550 #### Bitwise logical
551 var a = 0b0101 & 0b0011
552 echo $a
553 var b = 0b0101 | 0b0011
554 echo $b
555 var c = 0b0101 ^ 0b0011
556 echo $c
557 var d = ~b
558 echo $d
559 ## STDOUT:
560 1
561 7
562 6
563 -8
564 ## END
565
566 #### Shift operators
567 var a = 1 << 4
568 echo $a
569 var b = 16 >> 4
570 echo $b
571 ## STDOUT:
572 16
573 1
574 ## END
575
576 #### Exponentiation with **
577 var x = 2**3
578 echo $x
579 var y = 2.0**3.0
580 echo $y
581 ## STDOUT:
582 8
583 8.0
584 ## END
585
586 #### Two Kinds of Division
587 var x = 5/2
588 echo $x
589 var y = 5 // 2
590 echo $y
591 ## STDOUT:
592 2.5
593 2
594 ## END
595
596 #### mod operator
597 = 5 % 3
598 = -5 % 3
599 ## STDOUT:
600 (Int) 2
601 (Int) 1
602 ## END
603
604 #### Logical operators
605 var a = not true
606 echo $a
607 var b = true and false
608 echo $b
609 var c = true or false
610 echo $c
611
612 # TODO: These should be spelled 'false' 'false' 'true'?
613
614 ## STDOUT:
615 false
616 false
617 true
618 ## END
619
620 #### x if b else y
621 var b = true
622 var i = 42
623 var t = i+1 if b else i-1
624 echo $t
625 var f = i+1 if false else i-1
626 echo $f
627 ## STDOUT:
628 43
629 41
630 ## END
631
632 #### multiline strings, list, tuples, etc.
633 var dq = "
634 dq
635 2
636 "
637 echo dq=$len(dq)
638
639 var sq = '
640 sq
641 2
642 '
643 echo sq=$len(sq)
644
645 var mylist = [
646 1,
647 2,
648 3,
649 ]
650 echo mylist=$len(mylist)
651
652 var mytuple = (1,
653 2, 3)
654 echo mytuple=$len(mytuple)
655
656 ## STDOUT:
657 dq=6
658 sq=6
659 mylist=3
660 mytuple=3
661 ## END
662
663 #### multiline dict
664
665 # Note: a pair has to be all on one line. We could relax that but there isn't
666 # a strong reason to now.
667
668 var mydict = { a:1,
669 b: 2,
670 }
671 echo mydict=$len(mydict)
672 ## STDOUT:
673 mydict=2
674 ## END
675
676 #### multiline array and command sub (only here docs disallowed)
677 var array = %(
678 one
679 two
680 three
681 )
682 echo array=$len(array)
683
684 var comsub = $(
685 echo hi
686 echo bye
687 )
688 echo comsub=$len(comsub)
689
690 ## STDOUT:
691 array=3
692 comsub=6
693 ## END
694
695 #### obj.attr and obj.method()
696 var s = 'hi'
697
698 # TODO: This does a bound method thing we probably don't want
699 var s2 = s.upper()
700 echo $s2
701 ## STDOUT:
702 HI
703 ## END
704
705 #### obj.method does NOT give you a bound method
706 var s = 'hi'
707 var method = s.upper
708 echo $method
709 ## status: 2
710 ## stdout-json: ""
711
712 #### d->key
713 var d = {name: 'andy'}
714 var x = d->name
715 echo $x
716 ## STDOUT:
717 andy
718 ## END
719
720 #### a ++ b for string/list concatenation
721 var i = 'abc'
722 var j = 'de'
723 var k = i ++ j
724 echo $k
725
726 var a = [1, 2]
727 var b = [3]
728 var c = a ++ b
729 echo len=$len(c)
730
731 ## STDOUT:
732 abcde
733 len=3
734 ## END
735
736 #### s ~~ glob and s !~~ glob
737 shopt -s oil:all
738
739 if ('foo.py' ~~ '*.py') {
740 echo yes
741 }
742 if ('foo.py' !~~ '*.sh') {
743 echo no
744 }
745 ## STDOUT:
746 yes
747 no
748 ## END
749
750 #### Exact equality with === and !==
751 shopt -s oil:all
752
753 if (3 === 3) {
754 echo 'ok'
755 }
756 if (3 === '3') {
757 echo 'FAIL'
758 }
759
760 if (3 !== 3) {
761 echo 'FAIL'
762 }
763 if (3 !== '3') {
764 echo 'ok'
765 }
766
767 ## STDOUT:
768 ok
769 ok
770 ## END
771
772 #### Approximate equality of Str x {Str, Int, Bool} with ~==
773 shopt -s oil:all
774
775 # Note: for now there's no !~== operator. Use: not (a ~== b)
776
777 if (' foo ' ~== 'foo') {
778 echo Str-Str
779 }
780 if (' BAD ' ~== 'foo') {
781 echo FAIL
782 }
783
784 if ('3 ' ~== 3) {
785 echo Str-Int
786 }
787 if ('4 ' ~== '3') {
788 echo FAIL
789 }
790
791 if (' true ' ~== true) {
792 echo Str-Bool
793 }
794 if (' true ' ~== false) {
795 echo FAIL
796 }
797
798 const matrix = [
799 ' TRue ' ~== true, # case insentiive
800 ' FALse ' ~== false,
801
802 # Note this is the opposite of exit codes :-(
803 # Maybe we should encourage 'if try' instead of 'if'
804 ' 1 ' ~== true,
805 ' 0 ' ~== false,
806 ]
807
808 # = matrix
809 if (matrix === [true, true, true, true]) {
810 echo 'bool matrix'
811 }
812
813 ## STDOUT:
814 Str-Str
815 Str-Int
816 Str-Bool
817 bool matrix
818 ## END
819
820 #### Wrong Types with ~==
821 shopt -s oil:all
822
823 # The LHS side should be a string
824
825 echo one
826 if (['1'] ~== ['1']) {
827 echo bad
828 }
829 echo two
830
831 if (3 ~== 3) {
832 echo bad
833 }
834
835 ## status: 1
836 ## STDOUT:
837 one
838 ## END
839
840
841 #### Equality of ~== with Float (deferred)
842 shopt -s oil:all
843
844 if (42 ~== 42.0) {
845 echo int-float
846 }
847 if (42 ~== 43.0) {
848 echo FAIL
849 }
850
851 if ('42' ~== 42.0) {
852 echo str-float
853 }
854 if ('42' ~== 43.0) {
855 echo FAIL
856 }
857
858 if (42 ~== '42.0') {
859 echo int-str-float
860 }
861 if (42 ~== '43.0') {
862 echo FAIL
863 }
864 ## STDOUT:
865 ## END
866
867 #### Type Errors
868 shopt --set parse_brace
869
870 # TODO: It might be nice to get a message
871 try {
872 var x = {} + []
873 }
874 echo $_status
875
876 try {
877 setvar x = {} + 3
878 }
879 echo $_status
880
881 try {
882 _ 'foo' ++ 3
883 }
884 echo $_status
885
886 try {
887 = 'foo' ++ 3
888 }
889 echo $_status
890
891 ## STDOUT:
892 3
893 3
894 3
895 3
896 ## END