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 #### Splice in array
67 shopt -s oil:basic
68 var a = %(one two three)
69 argv.py @a
70 ## STDOUT:
71 ['one', 'two', 'three']
72 ## END
73
74 #### Splice in assoc array
75 shopt -s oil:basic
76 declare -A A=(['foo']=bar, ['spam']=eggs)
77 write -- @A | sort
78 ## STDOUT:
79 foo
80 spam
81 ## END
82
83 #### Can't splice string
84 shopt -s oil:basic
85 var mystr = 'abc'
86 argv.py @mystr
87 ## status: 1
88 ## stdout-json: ""
89
90 #### Can't splice undefined
91 shopt -s oil:basic
92 argv.py @undefined
93 echo done
94 ## status: 1
95 ## stdout-json: ""
96
97 #### Set $HOME using 'var' (i.e. Oil string var in word evaluator)
98 var HOME = "foo"
99 echo $HOME
100 echo ~
101 ## STDOUT:
102 foo
103 foo
104 ## END
105
106 #### Use shell var in Oil expression
107 x='abc'
108 var length = len(x) # length in BYTES, unlike ${#x}
109 echo $length
110 ## STDOUT:
111 3
112 ## END
113
114 #### Length in two different contexts
115 x=(a b c)
116 x[10]=A
117 x[20]=B
118
119 # shell style: length is 5
120 echo shell=${#x[@]}
121
122 # Oil function call: length is 20. I think that makes sense? It's just a
123 # different notion of length.
124 echo oil=$len(x)
125
126 ## STDOUT:
127 shell=5
128 oil=21
129 ## END
130
131 #### $len(x) inside strings
132 var s = "abc"
133 echo -$len(s)-
134
135 # This already has a meaning ...
136 #echo "-$len(x)-"
137 #echo "-${len}(x)-"
138
139 ## STDOUT:
140 -3-
141 ## END
142
143 #### Func with multiple args in multiple contexts
144 var x = max(1+2, 3+4)
145 echo $x $max(1+2, 3+4)
146
147 ## STDOUT:
148 7 7
149 ## END
150
151
152 #### Trailing Comma in Param list
153 var x = max(1+2, 3+4,)
154 echo $x $max(1+2, 3+4,)
155
156 ## STDOUT:
157 7 7
158 ## END
159
160 #### @range()
161 shopt -s oil:all
162 write @range(10, 15, 2)
163 ## STDOUT:
164 10
165 12
166 14
167 ## END
168
169 #### Wrong sigil $range() shows representation of iterator?
170 shopt -s oil:basic
171 echo $range(10, 15, 2)
172 ## STDOUT:
173 TODO
174 ## END
175
176 #### Wrong sigil @max(3, 4)
177 shopt -s oil:basic
178 write @max(3, 4)
179 ## STDOUT:
180 TODO
181 ## END
182
183
184 #### nested expr contexts
185 var s = "123"
186
187 # lex_mode_e.ShCommand -> Expr -> ShCommand -> Expr
188 var x = $(echo 'len\n' $len(s))
189 echo $x
190 ## STDOUT:
191 len
192 3
193 ## END
194
195
196 # TODO:
197 # - test keyword args
198 # - test splatting *args, **kwargs
199 # - Multiline parsing
200 #
201 # var x = max(
202 # 1+2,
203 # 3+4,
204 # )
205 # echo $x $max(
206 # 1+2,
207 # 3+4,
208 # )
209
210 #### Test value.Obj inside shell arithmetic
211 var w = "3"
212 echo lt=$(( w < 4 ))
213 echo gt=$(( w > 4 ))
214
215 var z = 3
216 echo lt=$(( z < 4 ))
217 echo gt=$(( z > 4 ))
218 ## STDOUT:
219 lt=1
220 gt=0
221 lt=1
222 gt=0
223 ## END
224
225 #### Parse { setvar x = 1 }
226 shopt -s oil:basic
227 var x = 1
228 f() { setvar x = 2 }
229 f
230 echo x=$x
231 ## STDOUT:
232 x=2
233 ## END
234
235 #### double quoted
236 var foo = "bar"
237 var x = "-$foo-${foo}-${undef:-default}-"
238 echo $x
239 ## STDOUT:
240 -bar-bar-default-
241 ## END
242
243 #### double quoted respects strict_array
244 shopt -s oil:basic
245 var a = %(one two three)
246 var x = "-${a[@]}-"
247 echo $x
248 ## status: 1
249 ## stdout-json: ""
250
251 #### single quoted -- implicit and explicit raw
252 var x = 'foo bar'
253 echo $x
254 setvar x = r'foo bar' # Same string
255 echo $x
256 setvar x = r'\t\n' # This is raw
257 echo $x
258 ## STDOUT:
259 foo bar
260 foo bar
261 \t\n
262 ## END
263
264 #### Implicit raw single quote with backslash is a syntax error
265 var x = '\t\n'
266 echo $x
267 ## status: 2
268 ## stdout-json: ""
269
270 #### single quoted C strings: c'foo\n' and $'foo\n'
271 var x = c'foo\nbar'
272 echo "$x"
273 var y = $'foo\nbar'
274 echo "$y"
275 ## STDOUT:
276 foo
277 bar
278 foo
279 bar
280 ## END
281
282 #### simple var sub $name $0 $1 $? etc.
283 ( exit 42 )
284 var status = $?
285 echo status=$status
286
287 set -- a b c
288 var one = $1
289 var two = $2
290 echo $one $two
291
292 var named = $one # equivalent to 'one'
293 echo named=$named
294
295 ## STDOUT:
296 status=42
297 a b
298 named=a
299 ## END
300
301 #### braced var sub ${x:-default}
302
303 # without double quotes
304
305 var b = ${foo:-default}
306 echo $b
307 var c = ${bar:-"-$b-"}
308 echo $c
309
310 var d = "${bar:-"-$c-"}" # another one
311 echo $d
312
313 ## STDOUT:
314 default
315 -default-
316 --default--
317 ## END
318
319 #### braced var sub respects strict_array
320 set -- a b c
321 var x = ${undef:-"$@"}
322 echo $x
323 shopt -s strict_array
324 setvar x = ${undef:-"$@"}
325 echo $x
326 ## status: 1
327 ## STDOUT:
328 a b c
329 ## END
330
331
332 #### null / true / false
333 shopt -s oil:basic
334 var n = null
335 if (n) {
336 echo yes
337 } else {
338 echo no
339 }
340 var t = true
341 if (t) {
342 echo yes
343 } else {
344 echo no
345 }
346 var f = false
347 if (f) {
348 echo yes
349 } else {
350 echo no
351 }
352 ## STDOUT:
353 no
354 yes
355 no
356 ## END
357
358 #### Integer literals
359 var d = 123
360 var b = 0b11
361 var o = 0o123
362 var h = 0xff
363 echo $d $b $o $h
364 ## STDOUT:
365 123 3 83 255
366 ## END
367
368 #### Float Literals
369 shopt -s oil:basic
370 # 1+2 2.3
371 var x = 1.2 + 23.0e-1 # 3.5
372 if (x < 3.9) {
373 echo less
374 }
375 if (x > 3.4) {
376 echo great
377 }
378 ## STDOUT:
379 less
380 great
381 ## END
382
383 #### Float Literals with _ (requires re2c refinement)
384 shopt -s oil:basic
385 # 1+2 + 2.3
386 # add this _ here
387 var x = 1.2 + 2_3.0e-1 # 3.5
388 if (x < 3.9) {
389 echo less
390 }
391 if (x > 3.4) {
392 echo great
393 }
394 ## STDOUT:
395 less
396 great
397 ## END
398
399 #### Tuples
400 var zero = ()
401 var one = tup(42)
402 var two = (1,2)
403 echo $len(zero)
404 echo $len(one)
405 echo $len(two)
406 ## STDOUT:
407 0
408 1
409 2
410 ## END
411
412 #### List comprehension (deferred)
413 shopt -s oil:all
414
415 var n = [i*2 for i in range(5)]
416 write -sep ' ' @n
417
418 # TODO: Test this
419 #var n = [i*2 for i,j in range(5)]
420
421 var even = [i*2 for i in range(5) if i % 2 == 0]
422 write -sep ' ' @even
423 ## STDOUT:
424 0 2 4 6 8
425 0 4 8
426 ## END
427
428 #### in, not in
429 var d = [1,2,3]
430 var b = 1 in d
431 echo $b
432 setvar b = 0 in d
433 echo $b
434 setvar b = 0 not in d
435 echo $b
436 ## STDOUT:
437 True
438 False
439 True
440 ## END
441
442 #### Chained Comparisons
443 shopt -s oil:basic
444 if (1 < 2 < 3) {
445 echo '123'
446 }
447 if (1 < 2 <= 2 <= 3 < 4) {
448 echo '123'
449 }
450
451 if (1 < 2 < 2) {
452 echo '123'
453 } else {
454 echo 'no'
455 }
456 ## STDOUT:
457 123
458 123
459 no
460 ## END
461
462 #### dict with 'bare word' keys
463 var d0 = {}
464 echo len=$len(d0)
465 var d1 = {name: "hello"}
466 echo len=$len(d1)
467 var d2 = {name: "hello", other: 2}
468 echo len=$len(d2)
469 ## STDOUT:
470 len=0
471 len=1
472 len=2
473 ## END
474
475 #### dict with expression keys
476 var d1 = {['name']: "hello"}
477 echo len=$len(d1)
478 var v = d1['name']
479 echo $v
480
481 var key='k'
482 var d2 = {["$key"]: "bar"}
483 echo len=$len(d2)
484 var v2 = d2['k']
485 echo $v2
486
487 ## STDOUT:
488 len=1
489 hello
490 len=1
491 bar
492 ## END
493
494
495 #### dict literal with implicit value
496 var name = 'foo'
497 var d1 = {name}
498 echo len=$len(d1)
499 var v1 = d1['name']
500 echo $v1
501
502 var d2 = {name, other: 'val'}
503 echo len=$len(d2)
504 var v2 = d2['name']
505 echo $v2
506
507 ## STDOUT:
508 len=1
509 foo
510 len=2
511 foo
512 ## END
513
514 #### Dict literal with string keys
515 var d = {'sq': 123}
516 var v = d['sq']
517 echo $v
518
519 var x = "q"
520 var d2 = {"d$x": 456}
521 var v2 = d2["dq"]
522 echo $v2
523 ## STDOUT:
524 123
525 456
526 ## END
527
528 #### Bitwise logical
529 var a = 0b0101 & 0b0011
530 echo $a
531 var b = 0b0101 | 0b0011
532 echo $b
533 var c = 0b0101 ^ 0b0011
534 echo $c
535 var d = ~b
536 echo $d
537 ## STDOUT:
538 1
539 7
540 6
541 -8
542 ## END
543
544 #### Shift operators
545 var a = 1 << 4
546 echo $a
547 var b = 16 >> 4
548 echo $b
549 ## STDOUT:
550 16
551 1
552 ## END
553
554 #### Exponentiation with **
555 var x = 2**3
556 echo $x
557 var y = 2.0**3.0
558 echo $y
559 ## STDOUT:
560 8
561 8.0
562 ## END
563
564 #### Two Kinds of Division
565 var x = 5/2
566 echo $x
567 var y = 5 // 2
568 echo $y
569 ## STDOUT:
570 2.5
571 2
572 ## END
573
574 #### mod operator
575 = 5 % 3
576 = -5 % 3
577 ## STDOUT:
578 (Int) 2
579 (Int) 1
580 ## END
581
582 #### Logical operators
583 var a = not true
584 echo $a
585 var b = true and false
586 echo $b
587 var c = true or false
588 echo $c
589
590 # TODO: These should be spelled 'false' 'false' 'true'?
591
592 ## STDOUT:
593 False
594 False
595 True
596 ## END
597
598 #### x if b else y
599 var b = true
600 var i = 42
601 var t = i+1 if b else i-1
602 echo $t
603 var f = i+1 if false else i-1
604 echo $f
605 ## STDOUT:
606 43
607 41
608 ## END
609
610 #### multiline strings, list, tuples, etc.
611 var dq = "
612 dq
613 2
614 "
615 echo dq=$len(dq)
616
617 var sq = '
618 sq
619 2
620 '
621 echo sq=$len(sq)
622
623 var mylist = [
624 1,
625 2,
626 3,
627 ]
628 echo mylist=$len(mylist)
629
630 var mytuple = (1,
631 2, 3)
632 echo mytuple=$len(mytuple)
633
634 ## STDOUT:
635 dq=6
636 sq=6
637 mylist=3
638 mytuple=3
639 ## END
640
641 #### multiline dict
642
643 # Note: a pair has to be all on one line. We could relax that but there isn't
644 # a strong reason to now.
645
646 var mydict = { a:1,
647 b: 2,
648 }
649 echo mydict=$len(mydict)
650 ## STDOUT:
651 mydict=2
652 ## END
653
654 #### multiline array and command sub (only here docs disallowed)
655 var array = %(
656 one
657 two
658 three
659 )
660 echo array=$len(array)
661
662 var comsub = $(
663 echo hi
664 echo bye
665 )
666 echo comsub=$len(comsub)
667
668 ## STDOUT:
669 array=3
670 comsub=6
671 ## END
672
673 #### obj.attr and obj.method()
674 var s = 'hi'
675
676 # TODO: This does a bound method thing we probably don't want
677 var s2 = s.upper()
678 echo $s2
679 ## STDOUT:
680 HI
681 ## END
682
683 #### obj.method does NOT give you a bound method
684
685 # TODO: Not sure how to implement this
686
687 var s = 'hi'
688 var method = s.upper
689 echo $method
690 ## STDOUT:
691 ## END
692
693
694
695 #### d->key
696 var d = {name: 'andy'}
697 var x = d->name
698 echo $x
699 ## STDOUT:
700 andy
701 ## END
702
703 #### a ++ b for string/list concatenation
704 var i = 'abc'
705 var j = 'de'
706 var k = i ++ j
707 echo $k
708
709 var a = [1, 2]
710 var b = [3]
711 var c = a ++ b
712 echo len=$len(c)
713
714 ## STDOUT:
715 abcde
716 len=3
717 ## END
718
719 #### s ~~ glob and s !~~ glob
720 shopt -s oil:all
721
722 if ('foo.py' ~~ '*.py') {
723 echo yes
724 }
725 if ('foo.py' !~~ '*.sh') {
726 echo no
727 }
728 ## STDOUT:
729 yes
730 no
731 ## END
732
733 #### 3 ~== '3'
734 shopt -s oil:all
735
736 # TODO: should we have !~== too?
737 # Or just make it not (a ~== b)
738
739 if (3 ~== 3) {
740 echo exact
741 }
742 if (1 ~== 3) {
743 echo FAIL
744 }
745
746 if (3 ~== '3') {
747 echo int-str
748 }
749 if (3 ~== '4') {
750 echo FAIL
751 }
752
753 if (42 ~== 42.0) {
754 echo int-float
755 }
756 if (42 ~== 43.0) {
757 echo FAIL
758 }
759
760 if ('42' ~== 42.0) {
761 echo str-float
762 }
763 if ('42' ~== 43.0) {
764 echo FAIL
765 }
766
767 if (42 ~== '42.0') {
768 echo int-str-float
769 }
770 if (42 ~== '43.0') {
771 echo FAIL
772 }
773
774 ## STDOUT:
775 exact
776 int-str
777 int-float
778 str-float
779 int-str-float
780 ## END
781