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