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

776 lines, 513 significant
1## oils_failures_allowed: 0
2
3# Demonstrations for users. Could go in docs.
4
5#### GetValue scope and shopt --unset dynamic_scope
6shopt --set parse_proc
7
8f() {
9 echo "sh x=$x"
10}
11
12proc p {
13 echo "oil x=$x"
14}
15
16demo() {
17 local x=dynamic
18 f
19 p
20
21 shopt --unset dynamic_scope
22 f
23}
24
25x=global
26demo
27echo x=$x
28
29## STDOUT:
30sh x=dynamic
31oil x=global
32sh x=global
33x=global
34## END
35
36
37#### SetValue scope and shopt --unset dynamic_scope
38shopt --set parse_proc
39
40f() {
41 x=f
42}
43
44proc p {
45 # x=p not allowed at parse time
46 declare x=p
47}
48
49demo() {
50 local x=stack
51 echo x=$x
52 echo ---
53
54 f
55 echo f x=$x
56
57 x=stack
58 p
59 echo p x=$x
60
61 shopt --unset dynamic_scope
62 x=stack
63 f
64 echo funset x=$x
65}
66
67x=global
68demo
69
70echo ---
71echo x=$x
72
73## STDOUT:
74x=stack
75---
76f x=f
77p x=stack
78funset x=stack
79---
80x=global
81## END
82
83#### read scope
84set -o errexit
85
86read-x() {
87 echo dynamic-scope | read x
88}
89demo() {
90 local x=42
91 echo x_before=$x
92 read-x
93 echo x_after=$x
94}
95demo
96echo x=$x
97
98echo ---
99
100# Now 'read x' creates a local variable
101shopt --unset dynamic_scope
102demo
103echo x=$x
104
105## STDOUT:
106x_before=42
107x_after=dynamic-scope
108x=
109---
110x_before=42
111x_after=42
112x=
113## END
114
115#### printf -v x respects dynamic_scope
116set -o errexit
117
118set-x() {
119 printf -v x "%s" dynamic-scope
120}
121demo() {
122 local x=42
123 echo x=$x
124 set-x
125 echo x=$x
126}
127demo
128echo x=$x
129
130echo ---
131
132shopt --unset dynamic_scope # should NOT affect read
133demo
134echo x=$x
135
136## STDOUT:
137x=42
138x=dynamic-scope
139x=
140---
141x=42
142x=42
143x=
144## END
145
146#### printf -v a[i] respects dynamic_scope
147set -o errexit
148
149set-item() {
150 printf -v 'a[1]' "%s" dynamic-scope
151}
152demo() {
153 local -a a=(41 42 43)
154 echo "a[1]=${a[1]}"
155 set-item
156 echo "a[1]=${a[1]}"
157}
158demo
159echo "a[1]=${a[1]}"
160
161echo ---
162
163shopt --unset dynamic_scope # should NOT affect read
164demo
165echo "a[1]=${a[1]}"
166
167## STDOUT:
168a[1]=42
169a[1]=dynamic-scope
170a[1]=
171---
172a[1]=42
173a[1]=42
174a[1]=
175## END
176
177#### ${undef=a} and shopt --unset dynamic_scope
178
179set-x() {
180 : ${x=new}
181}
182demo() {
183 local x
184 echo x=$x
185 set-x
186 echo x=$x
187}
188
189demo
190echo x=$x
191
192echo ---
193
194# Now this IS affected?
195shopt --unset dynamic_scope
196demo
197echo x=$x
198## STDOUT:
199x=
200x=new
201x=
202---
203x=
204x=
205x=
206## END
207
208#### declare -p respects it
209__g=G
210show-vars() {
211 local __x=X
212 declare -p | grep '__'
213 echo status=$?
214
215 echo -
216 declare -p __y | grep '__'
217 echo status=$?
218}
219
220demo() {
221 local __y=Y
222
223 show-vars
224 echo ---
225 shopt --unset dynamic_scope
226 show-vars
227}
228
229demo
230
231## STDOUT:
232declare -- __g=G
233declare -- __x=X
234declare -- __y=Y
235status=0
236-
237declare -- __y=Y
238status=0
239---
240declare -- __g=G
241declare -- __x=X
242status=0
243-
244status=1
245## END
246
247
248#### OshLanguageSetValue constructs
249
250f() {
251 (( x = 42 ))
252}
253demo() {
254 f
255 echo x=$x
256}
257
258demo
259
260echo ---
261
262shopt --unset dynamic_scope
263
264unset x
265
266demo
267
268echo --- global
269echo x=$x
270## STDOUT:
271x=42
272---
273x=
274--- global
275x=
276## END
277
278
279#### shell assignments 'neutered' inside 'proc'
280shopt --set parse_proc
281
282# They can't mutate globals or anything higher on the stack
283
284proc p {
285 declare g=PROC
286 export e=PROC
287}
288
289f() {
290 g=SH
291 export e=SH
292}
293
294e=E
295g=G
296p
297echo e=$e g=$g
298
299p
300echo e=$e g=$g
301
302f
303echo e=$e g=$g
304
305## STDOUT:
306e=E g=G
307e=E g=G
308e=SH g=SH
309## END
310
311#### setglobal still allows setting globals
312shopt --set parse_proc
313
314proc p {
315 setglobal new_global = 'p'
316 setglobal g = 'p'
317}
318
319var g = 'G'
320
321p
322
323echo g=$g new_global=$new_global
324## STDOUT:
325g=p new_global=p
326## END
327
328#### setglobal d[key] inside proc should mutate global (bug #1841)
329
330shopt -s ysh:upgrade
331
332var g = {}
333
334proc mutate {
335 var g = {'local': 1} # shadows global var
336
337 setglobal g.key = 'mutated'
338 setglobal g['key2'] = 'mutated'
339
340 echo 'local that is ignored'
341 pp test_ (g)
342}
343
344echo 'BEFORE mutate global'
345pp test_ (g)
346
347mutate
348
349echo 'AFTER mutate global'
350pp test_ (g)
351
352## STDOUT:
353BEFORE mutate global
354(Dict) {}
355local that is ignored
356(Dict) {"local":1}
357AFTER mutate global
358(Dict) {"key":"mutated","key2":"mutated"}
359## END
360
361#### setglobal a[i] inside proc
362shopt -s ysh:upgrade
363
364var a = [0]
365
366proc mutate {
367 var a = [1] # shadows global var
368
369 echo 'local that is ignored'
370 setglobal a[0] = 42
371
372 pp test_ (a)
373}
374
375echo 'BEFORE mutate global'
376pp test_ (a)
377
378mutate
379
380echo 'AFTER mutate global'
381pp test_ (a)
382
383## STDOUT:
384BEFORE mutate global
385(List) [0]
386local that is ignored
387(List) [1]
388AFTER mutate global
389(List) [42]
390## END
391
392#### setglobal a[i] += and d.key +=
393shopt -s ysh:upgrade
394
395var mylist = [0]
396var mydict = {k: 0}
397
398proc mutate {
399 # these locals are ignored
400 var mylist = []
401 var mydict = {}
402
403 setglobal mylist[0] += 5
404 setglobal mydict['k'] += 5
405}
406
407mutate
408
409pp test_ (mylist)
410pp test_ (mydict)
411
412## STDOUT:
413(List) [5]
414(Dict) {"k":5}
415## END
416
417#### setglobal a[i] - i can be local or global
418shopt -s ysh:upgrade
419
420var mylist = [0, 1]
421var mydict = {k: 0, n: 1}
422
423var i = 0
424var key = 'k'
425
426proc mutate1 {
427 var mylist = [] # IGNORED
428 var mydict = {} # IGNORED
429
430 var i = 1
431 var key = 'n'
432
433 setglobal mylist[i] = 11
434 setglobal mydict[key] = 11
435}
436
437# Same thing without locals
438proc mutate2 {
439 var mylist = [] # IGNORED
440 var mydict = {} # IGNORED
441
442 setglobal mylist[i] = 22
443 setglobal mydict[key] = 22
444}
445
446mutate1
447
448pp test_ (mylist)
449pp test_ (mydict)
450echo
451
452mutate2
453
454pp test_ (mylist)
455pp test_ (mydict)
456
457## STDOUT:
458(List) [0,11]
459(Dict) {"k":0,"n":11}
460
461(List) [22,11]
462(Dict) {"k":22,"n":11}
463## END
464
465#### unset inside proc uses local scope
466shopt --set parse_brace
467shopt --set parse_proc
468
469f() {
470 unset x
471}
472
473proc p() {
474 unset x
475}
476
477proc p2() {
478 shopt --set dynamic_scope { # turn it back on
479 unset x
480 }
481}
482
483x=foo
484f
485echo f x=$x
486
487x=bar
488p
489echo p x=$x
490
491x=spam
492p2
493echo p2 x=$x
494
495## STDOUT:
496f x=
497p x=bar
498p2 x=
499## END
500
501#### unset composes when you turn on dynamic scope
502shopt -s ysh:all
503
504proc unset-two (v, w) {
505 shopt --set dynamic_scope {
506 unset $v
507 unset $w
508 }
509}
510
511demo() {
512 local x=X
513 local y=Y
514
515 echo "x=$x y=$y"
516
517 unset-two x y
518
519 shopt --unset nounset
520 echo "x=$x y=$y"
521}
522
523demo
524## STDOUT:
525x=X y=Y
526x= y=
527## END
528
529#### Temp Bindings
530shopt --set parse_proc
531
532myfunc() {
533 echo myfunc FOO=$FOO
534}
535proc myproc() {
536 echo myproc FOO=$FOO
537}
538
539FOO=bar myfunc
540FOO=bar myproc
541FOO=bar echo inline FOO=$FOO
542FOO=bar printenv.py FOO
543
544## STDOUT:
545myfunc FOO=bar
546myproc FOO=
547inline FOO=
548bar
549## END
550
551#### cd blocks don't introduce new scopes
552shopt --set oil:upgrade
553
554var x = 42
555cd / {
556 var y = 0
557 var z = 1
558 echo $x $y $z
559 setvar y = 43
560}
561setvar z = 44
562echo $x $y $z
563
564## STDOUT:
56542 0 1
56642 43 44
567## END
568
569#### IFS=: myproc exports when it doesn't need to
570shopt --set parse_proc
571shopt --set parse_brace
572
573source --builtin ysh/shvar.ysh
574
575s='xzx zxz'
576
577myfunc() {
578 echo myfunc IFS="$IFS"
579 argv.py $s
580}
581
582proc myproc() {
583 echo myproc IFS="$IFS"
584 argv.py $s
585}
586
587IFS=: $REPO_ROOT/spec/bin/printenv.py IFS
588
589# default value
590echo "$IFS" | od -A n -t x1
591
592IFS=' z'
593echo IFS="$IFS"
594
595IFS=' x' myfunc
596
597# Problem: $IFS in procs only finds GLOBAL values. But when actually
598# splitting, $IFS is a 'shvar' which respects DYNAMIC scope.
599# Use shvarGet('IFS') instead
600
601IFS=' x' myproc
602
603# Oil solution to the problem
604shvar IFS=' x' {
605 myproc
606}
607
608## STDOUT:
609:
610 20 09 0a 0a
611IFS= z
612myfunc IFS= x
613['', 'z', 'z', 'z']
614myproc IFS= z
615['', 'z', 'z', 'z']
616myproc IFS= x
617['', 'z', 'z', 'z']
618## END
619
620#### shvar usage
621shopt --set oil:upgrade
622shopt --unset errexit
623
624source --builtin ysh/shvar.ysh
625
626# no block
627shvar
628echo status=$?
629
630shvar { # no arg
631 true
632}
633echo status=$?
634
635shvar foo { # should be name=value
636 true
637}
638echo status=$?
639## STDOUT:
640status=2
641status=2
642status=2
643## END
644
645#### shvar global
646shopt --set oil:upgrade
647shopt --unset nounset
648
649source --builtin ysh/shvar.ysh
650
651echo _ESCAPER=$_ESCAPER
652echo _DIALECT=$_DIALECT
653
654shvar _ESCAPER=html _DIALECT=ninja {
655 echo block _ESCAPER=$_ESCAPER
656 echo block _DIALECT=$_DIALECT
657}
658
659echo _ESCAPER=$_ESCAPER
660echo _DIALECT=$_DIALECT
661
662# Now set them
663_ESCAPER=foo
664_DIALECT=bar
665
666echo ___
667
668echo _ESCAPER=$_ESCAPER
669echo _DIALECT=$_DIALECT
670
671shvar _ESCAPER=html _DIALECT=ninja {
672 echo block _ESCAPER=$_ESCAPER
673 echo block _DIALECT=$_DIALECT
674
675 shvar _ESCAPER=nested {
676 echo nested _ESCAPER=$_ESCAPER
677 echo nested _DIALECT=$_DIALECT
678 }
679}
680
681echo _ESCAPER=$_ESCAPER
682echo _DIALECT=$_DIALECT
683
684## STDOUT:
685_ESCAPER=
686_DIALECT=
687block _ESCAPER=html
688block _DIALECT=ninja
689_ESCAPER=
690_DIALECT=
691___
692_ESCAPER=foo
693_DIALECT=bar
694block _ESCAPER=html
695block _DIALECT=ninja
696nested _ESCAPER=nested
697nested _DIALECT=ninja
698_ESCAPER=foo
699_DIALECT=bar
700## END
701
702#### shvar local
703shopt --set oil:upgrade # blocks
704shopt --unset simple_word_eval # test word splitting
705
706source --builtin ysh/shvar.ysh
707
708proc foo {
709 shvar IFS=x MYTEMP=foo {
710 echo IFS="$IFS"
711 argv.py $s
712 echo MYTEMP=${MYTEMP:-undef}
713 }
714}
715var s = 'a b c'
716argv.py $s
717foo
718argv.py $s
719echo MYTEMP=${MYTEMP:-undef}
720## STDOUT:
721['a', 'b', 'c']
722IFS=x
723['a b c']
724MYTEMP=foo
725['a', 'b', 'c']
726MYTEMP=undef
727## END
728
729#### shvar IFS
730shopt --set oil:upgrade
731
732source --builtin ysh/shvar.ysh
733
734proc myproc() {
735 echo "$IFS" | od -A n -t x1
736
737 local mylocal=x
738 shvar IFS=w {
739 echo inside IFS="$IFS"
740 echo mylocal="$mylocal" # I do NOT want a new scope!
741 }
742 echo "$IFS" | od -A n -t x1
743}
744
745myproc
746## STDOUT:
747 20 09 0a 0a
748inside IFS=w
749mylocal=x
750 20 09 0a 0a
751## END
752
753#### shvarGet()
754shopt --set parse_proc
755
756s='xzx zxz'
757
758proc myproc {
759 echo wrong IFS="$IFS" # NOT what's used
760 echo shvar IFS=$[shvarGet('IFS')] # what IS used: dynamic scope
761 argv.py $s
762}
763
764IFS=x
765IFS=z myproc
766
767# null
768echo $[shvarGet('nonexistent')]
769
770## STDOUT:
771wrong IFS=x
772shvar IFS=z
773['x', 'x ', 'x']
774null
775## END
776