OILS / spec / ysh-proc.test.sh View on Github | oilshell.org

610 lines, 330 significant
1## oils_failures_allowed: 1
2
3#### Open proc (any number of args)
4shopt --set parse_proc
5
6proc f {
7 var x = 42
8 return $x
9}
10# this gets called with 3 args then?
11f a b c
12echo status=$?
13## STDOUT:
14status=42
15## END
16
17#### Closed proc with no args, passed too many
18shopt --set parse_proc
19
20proc f() {
21 return 42
22}
23f
24echo status=$?
25
26f a b # status 2
27
28## status: 3
29## STDOUT:
30status=42
31## END
32
33#### Open proc has ARGV
34shopt -s ysh:all
35proc foo {
36 echo ARGV @ARGV
37 # do we care about this? I think we want to syntactically remove it from YSH
38 # but it can still be used for legacy
39 echo dollar-at "$@"
40}
41builtin set -- a b c
42foo x y z
43## STDOUT:
44ARGV x y z
45dollar-at a b c
46## END
47
48#### Closed proc has empty "$@" or ARGV
49shopt -s ysh:all
50
51proc foo(d, e, f) {
52 write params $d $e $f
53 argv.py dollar-at "$@"
54 argv.py ARGV @ARGV
55}
56builtin set -- a b c
57foo x y z
58## STDOUT:
59params
60x
61y
62z
63['dollar-at', 'a', 'b', 'c']
64['ARGV']
65## END
66
67#### Proc with default args
68shopt --set parse_proc
69
70proc f(x='foo') {
71 echo x=$x
72}
73f
74## STDOUT:
75x=foo
76## END
77
78#### Proc with word params
79shopt --set parse_proc
80
81# doesn't require ysh:all
82proc f(x, y, z) {
83 echo $x $y $z
84 var ret = 42
85 return $ret
86}
87# this gets called with 3 args then?
88f a b c
89echo status=$?
90## STDOUT:
91a b c
92status=42
93## END
94
95#### Proc with ... "rest" word params
96
97# TODO: opts goes with this
98# var opt = grep_opts.parse(ARGV)
99#
100# func(**opt) # Assumes keyword args match?
101# parse :grep_opts :opt @ARGV
102
103shopt -s ysh:all
104
105proc f(...names) {
106 write names: @names
107}
108# this gets called with 3 args then?
109f a b c
110echo status=$?
111## STDOUT:
112names:
113a
114b
115c
116status=0
117## END
118
119#### word rest params 2
120shopt --set ysh:all
121
122proc f(first, ...rest) { # @ means "the rest of the arguments"
123 write --sep ' ' -- $first
124 write --sep ' ' -- @rest # @ means "splice this array"
125}
126f a b c
127## STDOUT:
128a
129b c
130## END
131
132#### proc with typed args
133shopt --set ysh:upgrade
134
135# TODO: duplicate param names aren't allowed
136proc p (a; mylist, mydict; opt Int = 42) {
137 pp test_ (a)
138 pp test_ (mylist)
139 pp test_ (mydict)
140 #pp test_ (opt)
141}
142
143p WORD ([1,2,3], {name: 'bob'})
144
145echo ---
146
147p x (:| a b |, {bob: 42}, a = 5)
148
149## STDOUT:
150(Str) "WORD"
151(List) [1,2,3]
152(Dict) {"name":"bob"}
153---
154(Str) "x"
155(List) ["a","b"]
156(Dict) {"bob":42}
157## END
158
159#### Proc name-with-hyphen
160shopt --set parse_proc parse_at
161
162proc name-with-hyphen {
163 echo @ARGV
164}
165name-with-hyphen x y z
166## STDOUT:
167x y z
168## END
169
170#### Proc with block arg
171shopt --set ysh:upgrade
172
173# TODO: Test more of this
174proc f(x, y ; ; ; block) {
175 echo f word $x $y
176
177 if (block) {
178 call io->eval(block)
179 }
180}
181f a b { echo FFF }
182
183# With varargs and block
184shopt --set parse_proc
185
186proc g(x, y, ...rest ; ; ; block) {
187 echo g word $x $y
188 echo g rest @rest
189
190 if (block) {
191 call io->eval(block)
192 }
193}
194g a b c d {
195 echo GGG
196}
197
198## STDOUT:
199f word a b
200FFF
201g word a b
202g rest c d
203GGG
204## END
205
206#### proc returning wrong type
207shopt --set parse_proc
208
209# this should print an error message
210proc f {
211 var a = %(one two)
212 return $a
213}
214f
215## status: 3
216## STDOUT:
217## END
218
219#### proc returning invalid string
220shopt --set parse_proc
221
222# this should print an error message
223proc f {
224 var s = 'not an integer status'
225 return $s
226}
227f
228## status: 1
229## STDOUT:
230## END
231
232#### 'return' doesn't accept expressions
233proc p {
234 return 1 + 2
235}
236p
237## status: 2
238## STDOUT:
239## END
240
241#### declare -F only prints shell functions
242shopt --set parse_proc
243
244myfunc() {
245 echo hi
246}
247
248proc myproc {
249 echo hi
250}
251
252declare -F
253
254## status: 0
255## STDOUT:
256declare -f myfunc
257## END
258
259#### sh-func vs. proc vs. Obj: type -a, pp proc, runproc, declare -p -F, etc.
260shopt --set ysh:upgrade
261
262myfunc() {
263 echo hi
264}
265
266proc myproc {
267 echo hi
268}
269
270type myfunc
271echo
272
273type myproc
274echo
275
276pp proc
277echo
278
279declare -p
280echo
281
282declare -F
283echo
284
285## STDOUT:
286## END
287
288#### procs are in same namespace as variables
289shopt --set parse_proc
290
291proc myproc {
292 echo hi
293}
294
295echo "myproc is a $[type(myproc)]"
296
297## STDOUT:
298myproc is a Proc
299## END
300
301#### Nested proc is disallowed at parse time
302shopt --set parse_proc
303
304# NOTE: we can disallow this in Oil statically ...
305proc f {
306 proc g {
307 echo 'G'
308 }
309 g
310}
311f
312g
313## status: 2
314## stdout-json: ""
315
316#### Procs defined inside compound statements (with redefine_proc)
317
318shopt --set ysh:upgrade
319shopt --set redefine_proc_func
320
321for x in 1 2 {
322 proc p {
323 echo 'loop'
324 }
325}
326p
327
328{
329 proc p {
330 echo 'brace'
331 }
332}
333p
334
335## STDOUT:
336loop
337brace
338## END
339
340#### Block can be passed literally, or as expression in third arg group
341shopt --set ysh:upgrade
342
343proc p ( ; ; ; block) {
344 call io->eval(block)
345}
346
347p { echo literal }
348
349var block = ^(echo expression)
350p (; ; block)
351
352## STDOUT:
353literal
354expression
355## END
356
357#### Pass through all 4 kinds of args
358
359shopt --set ysh:upgrade
360
361proc p2 (...words; ...typed; ...named; block) {
362 pp test_ (words)
363 pp test_ (typed)
364 pp test_ (named)
365 #pp test_ (block)
366 # To avoid <Block 0x??> - could change pp test_
367 echo $[type(block)]
368}
369
370proc p1 (...words; ...typed; ...named; block) {
371 p2 @words (...typed; ...named; block)
372}
373
374p2 a b ('c', 'd', n=99) {
375 echo literal
376}
377echo
378
379# Same thing
380var block = ^(echo expression)
381
382# Note: you need the second explicit ;
383
384p2 a b ('c', 'd'; n=99; block)
385echo
386
387# what happens when you do this?
388p2 a b ('c', 'd'; n=99; block) {
389 echo duplicate
390}
391
392## status: 1
393## STDOUT:
394(List) ["a","b"]
395(List) ["c","d"]
396(Dict) {"n":99}
397Block
398
399(List) ["a","b"]
400(List) ["c","d"]
401(Dict) {"n":99}
402Command
403
404## END
405
406#### Global and local ARGV, like "$@"
407shopt -s parse_at
408argv.py "$@"
409argv.py @ARGV
410#argv.py "${ARGV[@]}" # not useful, but it works!
411
412set -- 'a b' c
413argv.py "$@"
414argv.py @ARGV # separate from the argv stack
415
416f() {
417 argv.py "$@"
418 argv.py @ARGV # separate from the argv stack
419}
420f 1 '2 3'
421## STDOUT:
422[]
423[]
424['a b', 'c']
425[]
426['1', '2 3']
427[]
428## END
429
430
431#### Mutating global ARGV
432
433$SH -c '
434shopt -s ysh:upgrade
435
436argv.py global @ARGV
437
438# should not be ignored
439call ARGV->append("GG")
440
441argv.py global @ARGV
442'
443## STDOUT:
444['global']
445['global', 'GG']
446## END
447
448#### Mutating local ARGV
449
450$SH -c '
451shopt -s ysh:upgrade
452
453argv.py global @ARGV
454
455proc p {
456 argv.py @ARGV
457 call ARGV->append("LL")
458 argv.py @ARGV
459}
460
461p local @ARGV
462
463argv.py global @ARGV
464
465' dummy0 'a b' c
466
467## STDOUT:
468['global', 'a b', 'c']
469['local', 'a b', 'c']
470['local', 'a b', 'c', 'LL']
471['global', 'a b', 'c']
472## END
473
474
475#### typed proc allows all kinds of args
476shopt -s ysh:upgrade
477
478typed proc p (w; t; n; block) {
479 pp test_ (w)
480 pp test_ (t)
481 pp test_ (n)
482 echo $[type(block)]
483}
484
485p word (42, n=99) {
486 echo block
487}
488
489
490## STDOUT:
491(Str) "word"
492(Int) 42
493(Int) 99
494Block
495## END
496
497#### can unset procs without -f
498shopt -s ysh:upgrade
499
500proc foo() {
501 echo bar
502}
503
504try { foo }
505echo status=$[_error.code]
506
507pp test_ (foo)
508unset foo
509#pp test_ (foo)
510
511try { foo }
512echo status=$[_error.code]
513
514## STDOUT:
515bar
516status=0
517<Proc>
518status=127
519## END
520
521#### procs shadow sh-funcs
522shopt -s ysh:upgrade redefine_proc_func
523
524f() {
525 echo sh-func
526}
527
528proc f {
529 echo proc
530}
531
532f
533## STDOUT:
534proc
535## END
536
537#### first word skips non-proc variables
538shopt -s ysh:upgrade
539
540grep() {
541 echo 'sh-func grep'
542}
543
544var grep = 'variable grep'
545
546grep
547
548# We first find `var grep`, but it's a Str not a Proc, so we skip it and then
549# find `function grep`.
550
551## STDOUT:
552sh-func grep
553## END
554
555#### proc resolution changes with the local scope
556shopt -s ysh:upgrade
557
558proc foo {
559 echo foo
560}
561
562proc bar {
563 echo bar
564}
565
566proc inner {
567 var foo = bar
568 foo # Will now reference `proc bar`
569}
570
571foo
572inner
573foo # Back to the global scope, foo still references `proc foo`
574
575# Without this behavior, features like `eval(b, vars={ flag: __flag })`, needed
576# by parseArgs, will not work. `eval` with `vars` adds a new frame to the end of
577# `mem.var_stack` with a local `flag` set to `proc __flag`. However, then we
578# cannot resolve `flag` by only checking `mem.var_stack[0]` like we could with
579# a proc declared normally, so we must search `mem.var_stack` from last to first.
580
581## STDOUT:
582foo
583bar
584foo
585## END
586
587
588#### procs are defined in local scope
589shopt -s ysh:upgrade
590
591proc gen-proc {
592 eval 'proc localproc { echo hi }'
593 pp frame_vars_
594
595}
596
597gen-proc
598
599# can't suppress 'grep' failure
600if false {
601 try {
602 pp frame_vars_ | grep localproc
603 }
604 pp test_ (_pipeline_status)
605 #pp test_ (PIPESTATUS)
606}
607
608## STDOUT:
609 [frame_vars_] ARGV localproc
610## END