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

558 lines, 302 significant
1## oils_failures_allowed: 0
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 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 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 prints procs and shell-funcs
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
257declare -f myproc
258## END
259
260#### procs are in same namespace as variables
261shopt --set parse_proc
262
263proc myproc {
264 echo hi
265}
266
267echo "myproc is a $[type(myproc)]"
268
269## STDOUT:
270myproc is a Proc
271## END
272
273#### Nested proc is disallowed at parse time
274shopt --set parse_proc
275
276# NOTE: we can disallow this in Oil statically ...
277proc f {
278 proc g {
279 echo 'G'
280 }
281 g
282}
283f
284g
285## status: 2
286## stdout-json: ""
287
288#### Procs defined inside compound statements (with redefine_proc)
289
290shopt --set ysh:upgrade
291shopt --set redefine_proc_func
292
293for x in 1 2 {
294 proc p {
295 echo 'loop'
296 }
297}
298p
299
300{
301 proc p {
302 echo 'brace'
303 }
304}
305p
306
307## STDOUT:
308loop
309brace
310## END
311
312#### Block can be passed literally, or as expression in third arg group
313shopt --set ysh:upgrade
314
315proc p ( ; ; ; block) {
316 eval (block)
317}
318
319p { echo literal }
320
321var block = ^(echo expression)
322p (; ; block)
323
324## STDOUT:
325literal
326expression
327## END
328
329#### Pass through all 4 kinds of args
330
331shopt --set ysh:upgrade
332
333proc p2 (...words; ...typed; ...named; block) {
334 pp test_ (words)
335 pp test_ (typed)
336 pp test_ (named)
337 #pp test_ (block)
338 # To avoid <Block 0x??> - could change pp test_
339 echo $[type(block)]
340}
341
342proc p1 (...words; ...typed; ...named; block) {
343 p2 @words (...typed; ...named; block)
344}
345
346p2 a b ('c', 'd', n=99) {
347 echo literal
348}
349echo
350
351# Same thing
352var block = ^(echo expression)
353
354# Note: you need the second explicit ;
355
356p2 a b ('c', 'd'; n=99; block)
357echo
358
359# what happens when you do this?
360p2 a b ('c', 'd'; n=99; block) {
361 echo duplicate
362}
363
364## status: 1
365## STDOUT:
366(List) ["a","b"]
367(List) ["c","d"]
368(Dict) {"n":99}
369Block
370
371(List) ["a","b"]
372(List) ["c","d"]
373(Dict) {"n":99}
374Command
375
376## END
377
378#### Global and local ARGV, like "$@"
379shopt -s parse_at
380argv.py "$@"
381argv.py @ARGV
382#argv.py "${ARGV[@]}" # not useful, but it works!
383
384set -- 'a b' c
385argv.py "$@"
386argv.py @ARGV # separate from the argv stack
387
388f() {
389 argv.py "$@"
390 argv.py @ARGV # separate from the argv stack
391}
392f 1 '2 3'
393## STDOUT:
394[]
395[]
396['a b', 'c']
397[]
398['1', '2 3']
399[]
400## END
401
402
403#### Mutating global ARGV
404
405$SH -c '
406shopt -s ysh:upgrade
407
408argv.py global @ARGV
409
410# should not be ignored
411call ARGV->append("GG")
412
413argv.py global @ARGV
414'
415## STDOUT:
416['global']
417['global', 'GG']
418## END
419
420#### Mutating local ARGV
421
422$SH -c '
423shopt -s ysh:upgrade
424
425argv.py global @ARGV
426
427proc p {
428 argv.py @ARGV
429 call ARGV->append("LL")
430 argv.py @ARGV
431}
432
433p local @ARGV
434
435argv.py global @ARGV
436
437' dummy0 'a b' c
438
439## STDOUT:
440['global', 'a b', 'c']
441['local', 'a b', 'c']
442['local', 'a b', 'c', 'LL']
443['global', 'a b', 'c']
444## END
445
446
447#### typed proc allows all kinds of args
448shopt -s ysh:upgrade
449
450typed proc p (w; t; n; block) {
451 pp test_ (w)
452 pp test_ (t)
453 pp test_ (n)
454 echo $[type(block)]
455}
456
457p word (42, n=99) {
458 echo block
459}
460
461
462## STDOUT:
463(Str) "word"
464(Int) 42
465(Int) 99
466Block
467## END
468
469#### can unset procs without -f
470shopt -s ysh:upgrade
471
472proc foo() {
473 echo bar
474}
475
476try { foo }
477echo status=$[_error.code]
478
479# TODO: should we abandon declare -F in favour of `pp proc`?
480declare -F
481unset foo
482declare -F
483
484try { foo }
485echo status=$[_error.code]
486
487## STDOUT:
488bar
489status=0
490declare -f foo
491status=127
492## END
493
494#### procs shadow sh-funcs
495shopt -s ysh:upgrade redefine_proc_func
496
497f() {
498 echo sh-func
499}
500
501proc f {
502 echo proc
503}
504
505f
506## STDOUT:
507proc
508## END
509
510#### first word skips non-proc variables
511shopt -s ysh:upgrade
512
513grep() {
514 echo 'sh-func grep'
515}
516
517var grep = 'variable grep'
518
519grep
520
521# We first find `var grep`, but it's a Str not a Proc, so we skip it and then
522# find `function grep`.
523
524## STDOUT:
525sh-func grep
526## END
527
528#### proc resolution changes with the local scope
529shopt -s ysh:upgrade
530
531proc foo {
532 echo foo
533}
534
535proc bar {
536 echo bar
537}
538
539proc inner {
540 var foo = bar
541 foo # Will now reference `proc bar`
542}
543
544foo
545inner
546foo # Back to the global scope, foo still references `proc foo`
547
548# Without this behavior, features like `eval(b, vars={ flag: __flag })`, needed
549# by parseArgs, will not work. `eval` with `vars` adds a new frame to the end of
550# `mem.var_stack` with a local `flag` set to `proc __flag`. However, then we
551# cannot resolve `flag` by only checking `mem.var_stack[0]` like we could with
552# a proc declared normally, so we must search `mem.var_stack` from last to first.
553
554## STDOUT:
555foo
556bar
557foo
558## END