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 mod 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 xor 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 #### Exponent is ^
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 div 2
568 echo $y
569 ## STDOUT:
570 2.5
571 2
572 ## END
573
574 #### mod operator
575 = 5 mod 3
576 = -5 mod 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, dict, 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 mydict = { a:1,
631 b:
632 2,
633 }
634 echo mydict=$len(mydict)
635
636 var mytuple = (1,
637 2, 3)
638 echo mytuple=$len(mytuple)
639
640 ## STDOUT:
641 dq=6
642 sq=6
643 mylist=3
644 mydict=2
645 mytuple=3
646 ## END
647
648 #### multiline array and command sub (only here docs disallowed)
649 var array = @(
650 one
651 two
652 three
653 )
654 echo array=$len(array)
655
656 var comsub = $(
657 echo hi
658 echo bye
659 )
660 echo comsub=$len(comsub)
661
662 ## STDOUT:
663 array=3
664 comsub=6
665 ## END
666
667 #### s ~ regex and s !~ regex
668 shopt -s oil:basic
669
670 var s = 'foo'
671 if (s ~ '.([[:alpha:]]+)') { # ERE syntax
672 echo matches
673 argv.py @M
674 }
675 if (s !~ '[[:digit:]]+') {
676 echo "does not match"
677 argv.py @M
678 }
679
680 if (s ~ '[[:digit:]]+') {
681 echo "matches"
682 }
683 # Should be cleared now
684 argv.py @M
685
686 ## STDOUT:
687 matches
688 ['foo', 'oo']
689 does not match
690 ['foo', 'oo']
691 []
692 ## END
693
694 #### s ~ regex sets a local, not a global
695 shopt -s oil:basic
696 proc f {
697 if ('foo' ~ '.([[:alpha:]]+)') { # ERE syntax
698 echo matches
699 argv.py @M
700 }
701 }
702 f
703 echo ${M:-default}
704 ## STDOUT:
705 matches
706 ['foo', 'oo']
707 default
708 ## END
709
710
711 #### M can be saved and used later (deferred)
712 shopt -s oil:basic
713
714 var pat = '.([[:alpha:]]+)' # ERE syntax
715 if ('foo' ~ pat) {
716 var m1 = copy(M)
717 if ('bar' ~ pat) {
718 var m2 = copy(M)
719 }
720 }
721 argv.py @m1
722 argv.py @m2
723 # STDOUT:
724 #['foo', 'oo']
725 #['bar', 'ar']
726 # END
727
728 ## status: 1
729
730
731
732 #### obj.attr and obj.method()
733 var s = 'hi'
734
735 # TODO: This does a bound method thing we probably don't want
736 var s2 = s.upper()
737 echo $s2
738 ## STDOUT:
739 HI
740 ## END
741
742 #### obj.method does NOT give you a bound method
743
744 # TODO: Not sure how to implement this
745
746 var s = 'hi'
747 var method = s.upper
748 echo $method
749 ## STDOUT:
750 ## END
751
752
753
754 #### d->key
755 var d = {name: 'andy'}
756 var x = d->name
757 echo $x
758 ## STDOUT:
759 andy
760 ## END