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 var one = tup(42)
395 var two = (1,2)
396 echo $len(zero)
397 echo $len(one)
398 echo $len(two)
399 ## STDOUT:
400 0
401 1
402 2
403 ## END
404
405 #### List comprehension
406 shopt -s oil:all
407
408 var n = [i*2 for i in range(5)]
409 echo -sep ' ' @n
410
411 # TODO: Test this
412 #var n = [i*2 for i,j in range(5)]
413
414 var even = [i*2 for i in range(5) if i mod 2 == 0]
415 echo -sep ' ' @even
416 ## STDOUT:
417 0 2 4 6 8
418 0 4 8
419 ## END
420
421 #### in, not in
422 var d = [1,2,3]
423 var b = 1 in d
424 echo $b
425 setvar b = 0 in d
426 echo $b
427 setvar b = 0 not in d
428 echo $b
429 ## STDOUT:
430 True
431 False
432 True
433 ## END
434
435 #### Chained Comparisons
436 shopt -s oil:basic
437 if (1 < 2 < 3) {
438 echo '123'
439 }
440 if (1 < 2 <= 2 <= 3 < 4) {
441 echo '123'
442 }
443
444 if (1 < 2 < 2) {
445 echo '123'
446 } else {
447 echo 'no'
448 }
449 ## STDOUT:
450 123
451 123
452 no
453 ## END
454
455 #### dict with 'bare word' keys
456 var d0 = {}
457 echo len=$len(d0)
458 var d1 = {name: "hello"}
459 echo len=$len(d1)
460 var d2 = {name: "hello", other: 2}
461 echo len=$len(d2)
462 ## STDOUT:
463 len=0
464 len=1
465 len=2
466 ## END
467
468 #### dict with expression keys
469 var d1 = {['name']: "hello"}
470 echo len=$len(d1)
471 var v = d1['name']
472 echo $v
473
474 var key='k'
475 var d2 = {["$key"]: "bar"}
476 echo len=$len(d2)
477 var v2 = d2['k']
478 echo $v2
479
480 ## STDOUT:
481 len=1
482 hello
483 len=1
484 bar
485 ## END
486
487
488 #### dict literal with implicit value
489 var name = 'foo'
490 var d1 = {name}
491 echo len=$len(d1)
492 var v1 = d1['name']
493 echo $v1
494
495 var d2 = {name, other: 'val'}
496 echo len=$len(d2)
497 var v2 = d2['name']
498 echo $v2
499
500 ## STDOUT:
501 len=1
502 foo
503 len=2
504 foo
505 ## END
506
507 #### Dict literal with string keys
508 var d = {'sq': 123}
509 var v = d['sq']
510 echo $v
511
512 var x = "q"
513 var d2 = {"d$x": 456}
514 var v2 = d2["dq"]
515 echo $v2
516 ## STDOUT:
517 123
518 456
519 ## END
520
521 #### Bitwise logical
522 var a = 0b0101 & 0b0011
523 echo $a
524 var b = 0b0101 | 0b0011
525 echo $b
526 var c = 0b0101 xor 0b0011
527 echo $c
528 var d = ~b
529 echo $d
530 ## STDOUT:
531 1
532 7
533 6
534 -8
535 ## END
536
537 #### Shift operators
538 var a = 1 << 4
539 echo $a
540 var b = 16 >> 4
541 echo $b
542 ## STDOUT:
543 16
544 1
545 ## END
546
547 #### Exponent is ^
548 var x = 2^3
549 echo $x
550 var y = 2.0^3.0
551 echo $y
552 ## STDOUT:
553 8
554 8.0
555 ## END
556
557 #### Two Kinds of Division
558 var x = 5/2
559 echo $x
560 var y = 5 div 2
561 echo $y
562 ## STDOUT:
563 2.5
564 2
565 ## END
566
567 #### mod operator
568 = 5 mod 3
569 = -5 mod 3
570 ## STDOUT:
571 (Int) 2
572 (Int) 1
573 ## END
574
575 #### Logical operators
576 var a = not true
577 echo $a
578 var b = true and false
579 echo $b
580 var c = true or false
581 echo $c
582
583 # TODO: These should be spelled 'false' 'false' 'true'?
584
585 ## STDOUT:
586 False
587 False
588 True
589 ## END
590
591 #### x if b else y
592 var b = true
593 var i = 42
594 var t = i+1 if b else i-1
595 echo $t
596 var f = i+1 if false else i-1
597 echo $f
598 ## STDOUT:
599 43
600 41
601 ## END
602
603 #### multiline strings, dict, list, tuples, etc.
604 var dq = "
605 dq
606 2
607 "
608 echo dq=$len(dq)
609
610 var sq = '
611 sq
612 2
613 '
614 echo sq=$len(sq)
615
616 var mylist = [
617 1,
618 2,
619 3,
620 ]
621 echo mylist=$len(mylist)
622
623 var mydict = { a:1,
624 b:
625 2,
626 }
627 echo mydict=$len(mydict)
628
629 var mytuple = (1,
630 2, 3)
631 echo mytuple=$len(mytuple)
632
633 ## STDOUT:
634 dq=6
635 sq=6
636 mylist=3
637 mydict=2
638 mytuple=3
639 ## END
640
641 #### multiline array and command sub (only here docs disallowed)
642 var array = @(
643 one
644 two
645 three
646 )
647 echo array=$len(array)
648
649 var comsub = $(
650 echo hi
651 echo bye
652 )
653 echo comsub=$len(comsub)
654
655 ## STDOUT:
656 array=3
657 comsub=6
658 ## END
659
660 #### s ~ regex and s !~ regex
661 shopt -s oil:basic
662
663 var s = 'foo'
664 if (s ~ '.([[:alpha:]]+)') { # ERE syntax
665 echo matches
666 argv.py @M
667 }
668 if (s !~ '[[:digit:]]+') {
669 echo "does not match"
670 argv.py @M
671 }
672
673 if (s ~ '[[:digit:]]+') {
674 echo "matches"
675 }
676 # Should be cleared now
677 argv.py @M
678
679 ## STDOUT:
680 matches
681 ['foo', 'oo']
682 does not match
683 ['foo', 'oo']
684 []
685 ## END
686
687 #### s ~ regex sets a local, not a global
688 shopt -s oil:basic
689 proc f {
690 if ('foo' ~ '.([[:alpha:]]+)') { # ERE syntax
691 echo matches
692 argv.py @M
693 }
694 }
695 f
696 echo ${M:-default}
697 ## STDOUT:
698 matches
699 ['foo', 'oo']
700 default
701 ## END
702
703
704 #### M can be saved and used later
705 shopt -s oil:basic
706
707 var pat = '.([[:alpha:]]+)' # ERE syntax
708 if ('foo' ~ pat) {
709 var m1 = M
710 if ('bar' ~ pat) {
711 var m2 = M
712 }
713 }
714 argv.py @m1
715 argv.py @m2
716 ## STDOUT:
717 ['foo', 'oo']
718 ['bar', 'ar']
719 ## END
720
721
722 #### obj.attr and obj.method()
723 var s = 'hi'
724
725 # TODO: This does a bound method thing we probably don't want
726 var s2 = s.upper()
727 echo $s2
728 ## STDOUT:
729 HI
730 ## END
731
732 #### obj.method does NOT give you a bound method
733
734 # TODO: Not sure how to implement this
735
736 var s = 'hi'
737 var method = s.upper
738 echo $method
739 ## STDOUT:
740 ## END
741
742
743
744 #### d->key
745 var d = {name: 'andy'}
746 var x = d->name
747 echo $x
748 ## STDOUT:
749 andy
750 ## END