1 ## oils_failures_allowed: 0
2
3 #### Open proc (any number of args)
4 shopt --set parse_proc
5
6 proc f {
7 var x = 42
8 return $x
9 }
10 # this gets called with 3 args then?
11 f a b c
12 echo status=$?
13 ## STDOUT:
14 status=42
15 ## END
16
17 #### Closed proc with no args, passed too many
18 shopt --set parse_proc
19
20 proc f() {
21 return 42
22 }
23 f
24 echo status=$?
25
26 f a b # status 2
27
28 ## status: 3
29 ## STDOUT:
30 status=42
31 ## END
32
33 #### Open proc has ARGV
34 shopt -s ysh:all
35 proc 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 }
41 builtin set -- a b c
42 foo x y z
43 ## STDOUT:
44 ARGV x y z
45 dollar-at a b c
46 ## END
47
48 #### Closed proc has empty "$@" or ARGV
49 shopt -s ysh:all
50
51 proc foo(d, e, f) {
52 write params $d $e $f
53 argv.py dollar-at "$@"
54 argv.py ARGV @ARGV
55 }
56 builtin set -- a b c
57 foo x y z
58 ## STDOUT:
59 params
60 x
61 y
62 z
63 ['dollar-at', 'a', 'b', 'c']
64 ['ARGV']
65 ## END
66
67 #### Proc with default args
68 shopt --set parse_proc
69
70 proc f(x='foo') {
71 echo x=$x
72 }
73 f
74 ## STDOUT:
75 x=foo
76 ## END
77
78 #### Proc with word params
79 shopt --set parse_proc
80
81 # doesn't require ysh:all
82 proc 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?
88 f a b c
89 echo status=$?
90 ## STDOUT:
91 a b c
92 status=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
103 shopt -s ysh:all
104
105 proc f(...names) {
106 write names: @names
107 }
108 # this gets called with 3 args then?
109 f a b c
110 echo status=$?
111 ## STDOUT:
112 names:
113 a
114 b
115 c
116 status=0
117 ## END
118
119 #### word rest params 2
120 shopt --set ysh:all
121
122 proc f(first, ...rest) { # @ means "the rest of the arguments"
123 write --sep ' ' -- $first
124 write --sep ' ' -- @rest # @ means "splice this array"
125 }
126 f a b c
127 ## STDOUT:
128 a
129 b c
130 ## END
131
132 #### proc with typed args
133 shopt --set ysh:upgrade
134
135 # TODO: duplicate param names aren't allowed
136 proc 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
143 p WORD ([1,2,3], {name: 'bob'})
144
145 echo ---
146
147 p 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
160 shopt --set parse_proc parse_at
161
162 proc name-with-hyphen {
163 echo @ARGV
164 }
165 name-with-hyphen x y z
166 ## STDOUT:
167 x y z
168 ## END
169
170 #### Proc with block arg
171 shopt --set ysh:upgrade
172
173 # TODO: Test more of this
174 proc f(x, y ; ; ; block) {
175 echo f word $x $y
176
177 if (block) {
178 call io->eval(block)
179 }
180 }
181 f a b { echo FFF }
182
183 # With varargs and block
184 shopt --set parse_proc
185
186 proc 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 }
194 g a b c d {
195 echo GGG
196 }
197
198 ## STDOUT:
199 f word a b
200 FFF
201 g word a b
202 g rest c d
203 GGG
204 ## END
205
206 #### proc returning wrong type
207 shopt --set parse_proc
208
209 # this should print an error message
210 proc f {
211 var a = %(one two)
212 return $a
213 }
214 f
215 ## status: 3
216 ## STDOUT:
217 ## END
218
219 #### proc returning invalid string
220 shopt --set parse_proc
221
222 # this should print an error message
223 proc f {
224 var s = 'not an integer status'
225 return $s
226 }
227 f
228 ## status: 1
229 ## STDOUT:
230 ## END
231
232 #### 'return' doesn't accept expressions
233 proc p {
234 return 1 + 2
235 }
236 p
237 ## status: 2
238 ## STDOUT:
239 ## END
240
241 #### procs are in same namespace as variables
242 shopt --set parse_proc
243
244 proc myproc {
245 echo hi
246 }
247
248 echo "myproc is a $[type(myproc)]"
249
250 ## STDOUT:
251 myproc is a Proc
252 ## END
253
254 #### Nested proc is allowed
255 shopt --set parse_proc
256
257 proc f {
258 proc g {
259 echo 'G'
260 }
261 g
262 }
263 f
264 g # g is defined in the local scope of f
265 ## status: 127
266 ## STDOUT:
267 G
268 ## END
269
270 #### Procs defined inside compound statements
271
272 shopt --set ysh:upgrade
273
274 for x in 1 2 {
275 proc p {
276 echo 'loop'
277 }
278 }
279 p
280
281 {
282 proc p {
283 echo 'brace'
284 }
285 }
286 p
287
288 ## STDOUT:
289 loop
290 brace
291 ## END
292
293 #### Block can be passed literally, or as expression in third arg group
294 shopt --set ysh:upgrade
295
296 proc p ( ; ; ; block) {
297 call io->eval(block)
298 }
299
300 p { echo literal }
301
302 var block = ^(echo expression)
303 p (; ; block)
304
305 ## STDOUT:
306 literal
307 expression
308 ## END
309
310 #### Pass through all 4 kinds of args
311
312 shopt --set ysh:upgrade
313
314 proc p2 (...words; ...typed; ...named; block) {
315 pp test_ (words)
316 pp test_ (typed)
317 pp test_ (named)
318 #pp test_ (block)
319 # To avoid <Block 0x??> - could change pp test_
320 echo $[type(block)]
321 }
322
323 proc p1 (...words; ...typed; ...named; block) {
324 p2 @words (...typed; ...named; block)
325 }
326
327 p2 a b ('c', 'd', n=99) {
328 echo literal
329 }
330 echo
331
332 # Same thing
333 var block = ^(echo expression)
334
335 # Note: you need the second explicit ;
336
337 p2 a b ('c', 'd'; n=99; block)
338 echo
339
340 # what happens when you do this?
341 p2 a b ('c', 'd'; n=99; block) {
342 echo duplicate
343 }
344
345 ## status: 1
346 ## STDOUT:
347 (List) ["a","b"]
348 (List) ["c","d"]
349 (Dict) {"n":99}
350 Command
351
352 (List) ["a","b"]
353 (List) ["c","d"]
354 (Dict) {"n":99}
355 Command
356
357 ## END
358
359 #### Global and local ARGV, like "$@"
360 shopt -s parse_at
361 argv.py "$@"
362 argv.py @ARGV
363 #argv.py "${ARGV[@]}" # not useful, but it works!
364
365 set -- 'a b' c
366 argv.py "$@"
367 argv.py @ARGV # separate from the argv stack
368
369 f() {
370 argv.py "$@"
371 argv.py @ARGV # separate from the argv stack
372 }
373 f 1 '2 3'
374 ## STDOUT:
375 []
376 []
377 ['a b', 'c']
378 []
379 ['1', '2 3']
380 []
381 ## END
382
383
384 #### Mutating global ARGV
385
386 $SH -c '
387 shopt -s ysh:upgrade
388
389 argv.py global @ARGV
390
391 # should not be ignored
392 call ARGV->append("GG")
393
394 argv.py global @ARGV
395 '
396 ## STDOUT:
397 ['global']
398 ['global', 'GG']
399 ## END
400
401 #### Mutating local ARGV
402
403 $SH -c '
404 shopt -s ysh:upgrade
405
406 argv.py global @ARGV
407
408 proc p {
409 argv.py @ARGV
410 call ARGV->append("LL")
411 argv.py @ARGV
412 }
413
414 p local @ARGV
415
416 argv.py global @ARGV
417
418 ' dummy0 'a b' c
419
420 ## STDOUT:
421 ['global', 'a b', 'c']
422 ['local', 'a b', 'c']
423 ['local', 'a b', 'c', 'LL']
424 ['global', 'a b', 'c']
425 ## END
426
427
428 #### typed proc allows all kinds of args
429 shopt -s ysh:upgrade
430
431 typed proc p (w; t; n; block) {
432 pp test_ (w)
433 pp test_ (t)
434 pp test_ (n)
435 echo $[type(block)]
436 }
437
438 p word (42, n=99) {
439 echo block
440 }
441
442
443 ## STDOUT:
444 (Str) "word"
445 (Int) 42
446 (Int) 99
447 Command
448 ## END
449
450 #### can unset procs without -f
451 shopt -s ysh:upgrade
452
453 proc foo() {
454 echo bar
455 }
456
457 try { foo }
458 echo status=$[_error.code]
459
460 pp test_ (foo)
461 unset foo
462 #pp test_ (foo)
463
464 try { foo }
465 echo status=$[_error.code]
466
467 ## STDOUT:
468 bar
469 status=0
470 <Proc>
471 status=127
472 ## END
473
474 #### procs shadow sh-funcs
475 shopt -s ysh:upgrade
476
477 f() {
478 echo sh-func
479 }
480
481 proc f {
482 echo proc
483 }
484
485 f
486 ## STDOUT:
487 proc
488 ## END
489
490 #### first word skips non-proc variables
491 shopt -s ysh:upgrade
492
493 grep() {
494 echo 'sh-func grep'
495 }
496
497 var grep = 'variable grep'
498
499 grep
500
501 # We first find `var grep`, but it's a Str not a Proc, so we skip it and then
502 # find `function grep`.
503
504 ## STDOUT:
505 sh-func grep
506 ## END
507
508 #### proc resolution changes with the local scope
509 shopt -s ysh:upgrade
510
511 proc foo {
512 echo foo
513 }
514
515 proc bar {
516 echo bar
517 }
518
519 proc inner {
520 var foo = bar
521 foo # Will now reference `proc bar`
522 }
523
524 foo
525 inner
526 foo # Back to the global scope, foo still references `proc foo`
527
528 # Without this behavior, features like `eval(b, vars={ flag: __flag })`, needed
529 # by parseArgs, will not work. `eval` with `vars` adds a new frame to the end of
530 # `mem.var_stack` with a local `flag` set to `proc __flag`. However, then we
531 # cannot resolve `flag` by only checking `mem.var_stack[0]` like we could with
532 # a proc declared normally, so we must search `mem.var_stack` from last to first.
533
534 ## STDOUT:
535 foo
536 bar
537 foo
538 ## END
539
540
541 #### procs are defined in local scope
542 shopt -s ysh:upgrade
543
544 proc gen-proc {
545 eval 'proc localproc { echo hi }'
546 pp frame_vars_
547
548 }
549
550 gen-proc
551
552 # can't suppress 'grep' failure
553 if false {
554 try {
555 pp frame_vars_ | grep localproc
556 }
557 pp test_ (_pipeline_status)
558 #pp test_ (PIPESTATUS)
559 }
560
561 ## STDOUT:
562 [frame_vars_] ARGV localproc
563 ## END
564
565
566 #### declare -f -F only prints shell functions
567 shopt --set parse_proc
568
569 myfunc() {
570 echo hi
571 }
572
573 proc myproc {
574 echo hi
575 }
576
577 declare -F
578 echo ---
579
580 declare -F myproc
581 echo status=$?
582
583 declare -f myproc
584 echo status=$?
585
586 ## status: 0
587 ## STDOUT:
588 declare -f myfunc
589 ---
590 status=1
591 status=1
592 ## END
593
594 #### compgen -A function shows user-defined invokables - shell funcs, Proc, Obj
595 shopt --set ysh:upgrade
596
597 my-shell-func() {
598 echo hi
599 }
600
601 proc myproc {
602 echo hi
603 }
604
605 compgen -A function
606
607 echo ---
608
609 proc define-inner {
610 eval 'proc inner { echo inner }'
611 #eval 'proc myproc { echo inner }' # shadowed name
612 compgen -A function
613 }
614 define-inner
615
616 echo ---
617
618 proc myinvoke (w; self) {
619 pp test_ ([w, self])
620 }
621
622 var methods = Object(null, {__invoke__: myinvoke})
623 var myobj = Object(methods, {})
624
625 compgen -A function
626
627 ## STDOUT:
628 my-shell-func
629 myproc
630 ---
631 define-inner
632 inner
633 my-shell-func
634 myproc
635 ---
636 define-inner
637 my-shell-func
638 myinvoke
639 myobj
640 myproc
641 ## END
642
643 #### type / type -a builtin on invokables - shell func, proc, invokable
644 shopt --set ysh:upgrade
645
646 my-shell-func() {
647 echo hi
648 }
649
650 proc myproc {
651 echo hi
652 }
653
654 proc boundProc(; self) {
655 echo hi
656 }
657
658 var methods = Object(null, {__invoke__: boundProc})
659 var invokable = Object(methods, {})
660
661 type -t my-shell-func
662 type -t myproc
663 type -t invokable
664 try {
665 type -t methods # not invokable!
666 }
667 echo $[_error.code]
668
669 echo ---
670
671 type my-shell-func
672 type myproc
673 type invokable
674 try {
675 type methods # not invokable!
676 }
677 echo $[_error.code]
678
679 echo ---
680
681 type -a my-shell-func
682 type -a myproc
683 type -a invokable
684
685 echo ---
686
687 if false { # can't redefine right now
688 invokable() {
689 echo sh-func
690 }
691 type -a invokable
692 }
693
694 ## STDOUT:
695 function
696 proc
697 invokable
698 1
699 ---
700 my-shell-func is a shell function
701 myproc is a YSH proc
702 invokable is a YSH invokable
703 1
704 ---
705 my-shell-func is a shell function
706 myproc is a YSH proc
707 invokable is a YSH invokable
708 ---
709 ## END
710
711 #### invokable Obj that doesn't declare self
712 shopt --set ysh:upgrade
713
714 proc boundProc(no_self; ) {
715 echo 'bad'
716 }
717
718 var methods = Object(null, {__invoke__: boundProc})
719 var invokable = Object(methods, {x: 3, y: 5})
720
721 invokable no_self
722
723 ## status: 3
724 ## STDOUT:
725 ## END
726
727 #### invokable Obj is called with self
728 shopt --set ysh:upgrade
729
730 proc boundProc(; self) {
731 echo "sum = $[self.x + self.y]"
732 }
733
734 var methods = Object(null, {__invoke__: boundProc})
735 var invokable = Object(methods, {x: 3, y: 5})
736
737 invokable
738
739 ## STDOUT:
740 sum = 8
741 ## END
742
743
744 #### invokable Obj with more typed args
745 shopt --set ysh:upgrade
746
747 proc myInvoke (word1, word2; self, int1, int2) {
748 echo "sum = $[self.x + self.y]"
749 pp test_ (self)
750 pp test_ ([word1, word2, int1, int2])
751 }
752
753 # call it directly with 'self'
754 myInvoke a b ({x: 0, y: 1}, 42, 43)
755 echo
756
757 var methods = Object(null, {__invoke__: myInvoke})
758
759 var callable = Object(methods, {x: 2, y: 3})
760
761 # call it through the obj
762 callable a b (44, 45)
763
764 ## STDOUT:
765 sum = 1
766 (Dict) {"x":0,"y":1}
767 (List) ["a","b",42,43]
768
769 sum = 5
770 (Obj) ("x":2,"y":3) --> ("__invoke__":<Proc>)
771 (List) ["a","b",44,45]
772 ## END
773
774 #### two different objects can share the same __invoke__
775 shopt --set ysh:upgrade
776
777 proc boundProc(; self, more) {
778 echo "sum = $[self.x + self.y + more]"
779 }
780
781 var methods = Object(null, {__invoke__: boundProc})
782
783 var i1 = Object(methods, {x: 3, y: 5})
784 var i2 = Object(methods, {x: 10, y: 42})
785
786 i1 (1)
787 i2 (1)
788
789 ## STDOUT:
790 sum = 9
791 sum = 53
792 ## END
793
794
795 #### Stateful proc with counter
796 shopt --set ysh:upgrade
797
798 proc invokeCounter(; self, inc) {
799 setvar self.i += inc
800 echo "counter = $[self.i]"
801 }
802
803 var methods = Object(null, {__invoke__: invokeCounter})
804 var counter = Object(methods, {i: 0})
805
806 counter (1)
807 counter (2)
808 counter (3)
809
810 ## STDOUT:
811 counter = 1
812 counter = 3
813 counter = 6
814 ## END