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