1 ## oils_failures_allowed: 1
2
3 #### GetValue scope and shopt --unset dynamic_scope
4 shopt --set parse_proc
5
6 f() {
7 echo "sh x=$x"
8 }
9
10 proc p {
11 echo "ysh x=$x"
12 }
13
14 demo() {
15 local x=dynamic
16 f
17 p
18
19 shopt --unset dynamic_scope
20 f
21 }
22
23 x=global
24 demo
25 echo x=$x
26
27 ## STDOUT:
28 sh x=dynamic
29 ysh x=global
30 sh x=global
31 x=global
32 ## END
33
34
35 #### SetValue scope and shopt --unset dynamic_scope
36 shopt --set parse_proc
37
38 f() {
39 x=f
40 }
41
42 proc p {
43 var x = 'p'
44 }
45
46 demo() {
47 local x=stack
48 echo x=$x
49 echo ---
50
51 f
52 echo f x=$x
53
54 x=stack
55 p
56 echo p x=$x
57
58 shopt --unset dynamic_scope
59 x=stack
60 f
61 echo funset x=$x
62 }
63
64 x=global
65 demo
66
67 echo ---
68 echo x=$x
69
70 ## STDOUT:
71 x=stack
72 ---
73 f x=f
74 p x=stack
75 funset x=stack
76 ---
77 x=global
78 ## END
79
80 #### read scope
81 set -o errexit
82
83 read-x() {
84 echo dynamic-scope | read x
85 }
86 demo() {
87 local x=42
88 echo x_before=$x
89 read-x
90 echo x_after=$x
91 }
92 demo
93 echo x=$x
94
95 echo ---
96
97 # Now 'read x' creates a local variable
98 shopt --unset dynamic_scope
99 demo
100 echo x=$x
101
102 ## STDOUT:
103 x_before=42
104 x_after=dynamic-scope
105 x=
106 ---
107 x_before=42
108 x_after=42
109 x=
110 ## END
111
112 #### printf -v x respects dynamic_scope
113 set -o errexit
114
115 set-x() {
116 printf -v x "%s" dynamic-scope
117 }
118 demo() {
119 local x=42
120 echo x=$x
121 set-x
122 echo x=$x
123 }
124 demo
125 echo x=$x
126
127 echo ---
128
129 shopt --unset dynamic_scope # should NOT affect read
130 demo
131 echo x=$x
132
133 ## STDOUT:
134 x=42
135 x=dynamic-scope
136 x=
137 ---
138 x=42
139 x=42
140 x=
141 ## END
142
143 #### printf -v a[i] respects dynamic_scope
144 set -o errexit
145
146 set-item() {
147 printf -v 'a[1]' "%s" dynamic-scope
148 }
149 demo() {
150 local -a a=(41 42 43)
151 echo "a[1]=${a[1]}"
152 set-item
153 echo "a[1]=${a[1]}"
154 }
155 demo
156 echo "a[1]=${a[1]}"
157
158 echo ---
159
160 shopt --unset dynamic_scope # should NOT affect read
161 demo
162 echo "a[1]=${a[1]}"
163
164 ## STDOUT:
165 a[1]=42
166 a[1]=dynamic-scope
167 a[1]=
168 ---
169 a[1]=42
170 a[1]=42
171 a[1]=
172 ## END
173
174 #### ${undef=a} and shopt --unset dynamic_scope
175
176 set-x() {
177 : ${x=new}
178 }
179 demo() {
180 local x
181 echo x=$x
182 set-x
183 echo x=$x
184 }
185
186 demo
187 echo x=$x
188
189 echo ---
190
191 # Now this IS affected?
192 shopt --unset dynamic_scope
193 demo
194 echo x=$x
195 ## STDOUT:
196 x=
197 x=new
198 x=
199 ---
200 x=
201 x=
202 x=
203 ## END
204
205 #### declare -p respects it
206
207 ___g=G
208
209 show-vars() {
210 local ___x=X
211 declare -p | grep '___'
212 echo status=$?
213
214 echo -
215 declare -p ___y | grep '___'
216 echo status=$?
217 }
218
219 demo() {
220 local ___y=Y
221
222 show-vars
223 echo ---
224 shopt --unset dynamic_scope
225 show-vars
226 }
227
228 demo
229
230 ## STDOUT:
231 declare -- ___g=G
232 declare -- ___x=X
233 declare -- ___y=Y
234 status=0
235 -
236 declare -- ___y=Y
237 status=0
238 ---
239 declare -- ___g=G
240 declare -- ___x=X
241 status=0
242 -
243 status=1
244 ## END
245
246
247 #### OshLanguageSetValue constructs
248
249 f() {
250 (( x = 42 ))
251 }
252 demo() {
253 f
254 echo x=$x
255 }
256
257 demo
258
259 echo ---
260
261 shopt --unset dynamic_scope
262
263 unset x
264
265 demo
266
267 echo --- global
268 echo x=$x
269 ## STDOUT:
270 x=42
271 ---
272 x=
273 --- global
274 x=
275 ## END
276
277
278 #### shell assignments 'neutered' inside 'proc'
279 shopt --set parse_proc
280
281 # They can't mutate globals or anything higher on the stack
282
283 proc p {
284 # TODO: declare should be disallowed in YSH, just like shell functions.
285
286 #declare g=PROC
287 #export e=PROC
288 var g = 'PROC'
289 var e = 'PROC'
290 }
291
292 f() {
293 g=SH
294 export e=SH
295 }
296
297 e=E
298 g=G
299 p
300 echo e=$e g=$g
301
302 p
303 echo e=$e g=$g
304
305 f
306 echo e=$e g=$g
307
308 ## STDOUT:
309 e=E g=G
310 e=E g=G
311 e=SH g=SH
312 ## END
313
314 #### setglobal still allows setting globals
315 shopt --set parse_proc
316
317 proc p {
318 setglobal new_global = 'p'
319 setglobal g = 'p'
320 }
321
322 var g = 'G'
323
324 p
325
326 echo g=$g new_global=$new_global
327 ## STDOUT:
328 g=p new_global=p
329 ## END
330
331 #### setglobal d[key] inside proc should mutate global (bug #1841)
332
333 shopt -s ysh:upgrade
334
335 var g = {}
336
337 proc mutate {
338 var g = {'local': 1} # shadows global var
339
340 setglobal g.key = 'mutated'
341 setglobal g['key2'] = 'mutated'
342
343 echo 'local that is ignored'
344 pp test_ (g)
345 }
346
347 echo 'BEFORE mutate global'
348 pp test_ (g)
349
350 mutate
351
352 echo 'AFTER mutate global'
353 pp test_ (g)
354
355 ## STDOUT:
356 BEFORE mutate global
357 (Dict) {}
358 local that is ignored
359 (Dict) {"local":1}
360 AFTER mutate global
361 (Dict) {"key":"mutated","key2":"mutated"}
362 ## END
363
364 #### setglobal a[i] inside proc
365 shopt -s ysh:upgrade
366
367 var a = [0]
368
369 proc mutate {
370 var a = [1] # shadows global var
371
372 echo 'local that is ignored'
373 setglobal a[0] = 42
374
375 pp test_ (a)
376 }
377
378 echo 'BEFORE mutate global'
379 pp test_ (a)
380
381 mutate
382
383 echo 'AFTER mutate global'
384 pp test_ (a)
385
386 ## STDOUT:
387 BEFORE mutate global
388 (List) [0]
389 local that is ignored
390 (List) [1]
391 AFTER mutate global
392 (List) [42]
393 ## END
394
395 #### setglobal a[i] += and d.key +=
396 shopt -s ysh:upgrade
397
398 var mylist = [0]
399 var mydict = {k: 0}
400
401 proc mutate {
402 # these locals are ignored
403 var mylist = []
404 var mydict = {}
405
406 setglobal mylist[0] += 5
407 setglobal mydict['k'] += 5
408 }
409
410 mutate
411
412 pp test_ (mylist)
413 pp test_ (mydict)
414
415 ## STDOUT:
416 (List) [5]
417 (Dict) {"k":5}
418 ## END
419
420 #### setglobal a[i] - i can be local or global
421 shopt -s ysh:upgrade
422
423 var mylist = [0, 1]
424 var mydict = {k: 0, n: 1}
425
426 var i = 0
427 var key = 'k'
428
429 proc mutate1 {
430 var mylist = [] # IGNORED
431 var mydict = {} # IGNORED
432
433 var i = 1
434 var key = 'n'
435
436 setglobal mylist[i] = 11
437 setglobal mydict[key] = 11
438 }
439
440 # Same thing without locals
441 proc mutate2 {
442 var mylist = [] # IGNORED
443 var mydict = {} # IGNORED
444
445 setglobal mylist[i] = 22
446 setglobal mydict[key] = 22
447 }
448
449 mutate1
450
451 pp test_ (mylist)
452 pp test_ (mydict)
453 echo
454
455 mutate2
456
457 pp test_ (mylist)
458 pp test_ (mydict)
459
460 ## STDOUT:
461 (List) [0,11]
462 (Dict) {"k":0,"n":11}
463
464 (List) [22,11]
465 (Dict) {"k":22,"n":11}
466 ## END
467
468 #### unset inside proc - closures and dynamic scope
469 shopt --set parse_brace
470 shopt --set parse_proc
471
472 shellfunc() {
473 unset x
474 }
475
476 proc unset-proc() {
477 unset x
478 }
479
480 proc unset-proc-dynamic-scope() {
481 shopt --set dynamic_scope { # turn it back on
482 unset x
483 }
484 }
485
486 x=foo
487 shellfunc
488 echo shellfunc x=$x
489
490 x=bar
491 unset-proc
492 echo unset-proc x=$x
493
494 x=spam
495 unset-proc
496 echo unset-proc-dynamic-scope x=$x
497
498 ## STDOUT:
499 shellfunc x=
500 unset-proc x=
501 unset-proc-dynamic-scope x=
502 ## END
503
504 #### unset composes when you turn on dynamic scope
505 shopt -s ysh:all
506 shopt --unset no_osh_builtins
507
508 proc unset-two (v, w) {
509 shopt --set dynamic_scope {
510 unset $v
511 unset $w
512 }
513 }
514
515 demo() {
516 local x=X
517 local y=Y
518
519 echo "x=$x y=$y"
520
521 unset-two x y
522
523 shopt --unset nounset
524 echo "x=$x y=$y"
525 }
526
527 demo
528 ## STDOUT:
529 x=X y=Y
530 x= y=
531 ## END
532
533 #### Temp Bindings
534 shopt --set parse_proc
535
536 myfunc() {
537 echo myfunc FOO=$FOO
538 }
539 proc myproc() {
540 echo myproc FOO=$FOO
541 }
542
543 FOO=bar myfunc
544 FOO=bar myproc
545 FOO=bar echo inline FOO=$FOO
546 FOO=bar printenv.py FOO
547
548 ## STDOUT:
549 myfunc FOO=bar
550 myproc FOO=
551 inline FOO=
552 bar
553 ## END
554
555 #### cd blocks don't introduce new scopes
556 shopt --set ysh:upgrade
557
558 var x = 42
559 cd / {
560 var y = 0
561 var z = 1
562 echo $x $y $z
563 setvar y = 43
564 }
565 setvar z = 44
566 echo $x $y $z
567
568 ## STDOUT:
569 42 0 1
570 42 43 44
571 ## END
572
573 #### shvar IFS=x { myproc } rather than IFS=x myproc - no dynamic scope
574
575 # Note: osh/split.py uses dynamic scope to look up IFS
576 # TODO: Should use LANG example to demonstrate
577
578 #shopt --set ysh:upgrade # this would disable word splitting
579
580 shopt --set parse_proc
581 shopt --set parse_brace
582 #shopt --set env_obj
583
584 s='xzx zxz'
585
586 shellfunc() {
587 echo shellfunc IFS="$IFS"
588 argv.py $s
589 }
590
591 proc myproc() {
592 echo myproc IFS="$IFS"
593 argv.py $s
594 }
595
596 IFS=: $REPO_ROOT/spec/bin/printenv.py IFS
597
598 # default value
599 echo "$IFS" | od -A n -t x1
600
601 IFS=' z'
602 echo IFS="$IFS"
603 echo
604
605 shellfunc
606 echo
607
608 IFS=' x' shellfunc
609 echo
610
611 # Problem: $IFS in procs only finds GLOBAL values, so we get IFS=' z' rather than IFS=' x'.
612 # But when actually splitting, $IFS is a 'shvar' which respects DYNAMIC scope.
613 #
614 # Can use shvarGet('IFS') instead
615
616 IFS=' x' myproc
617 echo
618
619 # YSH solution to the problem
620 shvar IFS=' x' {
621 myproc
622 }
623
624 ## STDOUT:
625 :
626 20 09 0a 0a
627 IFS= z
628
629 shellfunc IFS= z
630 ['x', 'x', 'x']
631
632 shellfunc IFS= x
633 ['', 'z', 'z', 'z']
634
635 myproc IFS= z
636 ['x', 'x', 'x']
637
638 myproc IFS= x
639 ['', 'z', 'z', 'z']
640 ## END
641
642 #### shvar builtin syntax
643 shopt --set ysh:upgrade
644 shopt --unset errexit
645
646 # no block
647 shvar
648 echo status=$?
649
650 shvar { # no arg
651 true
652 }
653 echo status=$?
654
655 shvar foo { # should be name=value
656 true
657 }
658 echo status=$?
659 ## STDOUT:
660 status=2
661 status=2
662 status=2
663 ## END
664
665
666 #### shvar and shvarGet() obey dynamic scope
667
668 # On the other hand, in YSH
669 # - $x does local/closure/global scope
670 # - FOO=foo mycommand modifies the ENV object - shopt --set env_obj
671
672 shopt --set ysh:all
673
674 proc p3 {
675 echo FOO=$[shvarGet('FOO')] # dynamic scope
676 echo FOO=$FOO # fails, not dynamic scope
677 }
678
679 proc p2 {
680 p3
681 }
682
683 proc p {
684 shvar FOO=foo {
685 p2
686 }
687 }
688
689 p
690
691 ## status: 1
692 ## STDOUT:
693 FOO=foo
694 ## END
695
696
697 #### shvar global
698 shopt --set ysh:upgrade
699 shopt --unset nounset
700
701 echo _ESCAPER=$_ESCAPER
702 echo _DIALECT=$_DIALECT
703
704 shvar _ESCAPER=html _DIALECT=ninja {
705 echo block _ESCAPER=$_ESCAPER
706 echo block _DIALECT=$_DIALECT
707 }
708
709 echo _ESCAPER=$_ESCAPER
710 echo _DIALECT=$_DIALECT
711
712 # Now set them
713 _ESCAPER=foo
714 _DIALECT=bar
715
716 echo ___
717
718 echo _ESCAPER=$_ESCAPER
719 echo _DIALECT=$_DIALECT
720
721 shvar _ESCAPER=html _DIALECT=ninja {
722 echo block _ESCAPER=$_ESCAPER
723 echo block _DIALECT=$_DIALECT
724
725 shvar _ESCAPER=nested {
726 echo nested _ESCAPER=$_ESCAPER
727 echo nested _DIALECT=$_DIALECT
728 }
729 }
730
731 echo _ESCAPER=$_ESCAPER
732 echo _DIALECT=$_DIALECT
733
734 ## STDOUT:
735 _ESCAPER=
736 _DIALECT=
737 block _ESCAPER=html
738 block _DIALECT=ninja
739 _ESCAPER=
740 _DIALECT=
741 ___
742 _ESCAPER=foo
743 _DIALECT=bar
744 block _ESCAPER=html
745 block _DIALECT=ninja
746 nested _ESCAPER=nested
747 nested _DIALECT=ninja
748 _ESCAPER=foo
749 _DIALECT=bar
750 ## END
751
752 #### shvar local
753 shopt --set ysh:upgrade # blocks
754 shopt --unset simple_word_eval # test word splitting
755
756 proc foo {
757 shvar IFS=x MYTEMP=foo {
758 echo IFS="$IFS"
759 argv.py $s
760 echo MYTEMP=${MYTEMP:-undef}
761 }
762 }
763 var s = 'a b c'
764 argv.py $s
765 foo
766 argv.py $s
767 echo MYTEMP=${MYTEMP:-undef}
768 ## STDOUT:
769 ['a', 'b', 'c']
770 IFS=x
771 ['a b c']
772 MYTEMP=foo
773 ['a', 'b', 'c']
774 MYTEMP=undef
775 ## END
776
777 #### shvar IFS
778 shopt --set ysh:upgrade
779
780 proc myproc() {
781 echo "$IFS" | od -A n -t x1
782
783 local mylocal=x
784 shvar IFS=w {
785 echo inside IFS="$IFS"
786 echo mylocal="$mylocal" # I do NOT want a new scope!
787 }
788 echo "$IFS" | od -A n -t x1
789 }
790
791 myproc
792 ## STDOUT:
793 20 09 0a 0a
794 inside IFS=w
795 mylocal=x
796 20 09 0a 0a
797 ## END
798
799 #### Compare shell func vs. proc, $IFS vs. shvarGet('IFS')
800
801 shopt --set parse_proc
802
803 s='xzx zxz'
804
805 shellfunc() { # dynamic scope everywhere
806 echo shellfunc
807 echo IFS="$IFS"
808 echo shvarGet IFS=$[shvarGet('IFS')]
809 argv.py $s
810 }
811
812 proc myproc { # no dynamic scope
813
814 # Subtle behavior: we see 'x' rather than "temp frame" 'z' - I think because
815 # there is a CHAIN of __E__ enclosed scopes, up to the global frame.
816 #
817 # That frame comes FIRST. That seems OK, but it changed when procs became closures.
818 proc p2 {
819 echo "myproc -> p2"
820 echo IFS="$IFS"
821 echo shvarGet IFS=$[shvarGet('IFS')] # dynamic scope opt-in
822 argv.py $s # dynamic scope in osh/split.py
823 }
824
825 p2
826 }
827
828 IFS=x
829
830 IFS=z shellfunc
831 echo
832
833 # this makes a temp frame, but the proc can't see it?
834 IFS=z myproc
835 echo
836
837 # null
838 echo $[shvarGet('nonexistent')]
839
840 ## STDOUT:
841 shellfunc
842 IFS=z
843 shvarGet IFS=z
844 ['x', 'x ', 'x']
845
846 myproc -> p2
847 IFS=x
848 shvarGet IFS=x
849 ['', 'z', ' z', 'z']
850
851 null
852 ## END
853
854 #### func and proc are like var, with respect to closures
855 shopt --set ysh:all
856
857 proc test-var {
858 var x = 'outer'
859 proc inner {
860 var x = 'inner'
861 # note: static check is broken now
862 #setvar x = 'inner'
863 echo "inner $x"
864 }
865 inner
866 echo "outer $x"
867 }
868
869 # Note: state.YshDecl flag somehow doesn't make a difference here?
870 proc test-func {
871 func x() { return ('outer') }
872 proc inner2 {
873 func x() { return ('inner') }
874 echo "inner $[x()]"
875 }
876 inner2
877 echo "outer $[x()]"
878 }
879
880 proc test-proc {
881 proc x { echo 'outer' }
882 proc inner3 {
883 proc x { echo 'inner' }
884 x
885 }
886 inner3
887 x
888 }
889
890
891 test-var
892 echo
893
894 test-func
895 echo
896
897 test-proc
898
899 ## STDOUT:
900 inner inner
901 outer outer
902
903 inner inner
904 outer outer
905
906 inner
907 outer
908 ## END