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

766 lines, 508 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
573s='xzx zxz'
574
575myfunc() {
576 echo myfunc IFS="$IFS"
577 argv.py $s
578}
579
580proc myproc() {
581 echo myproc IFS="$IFS"
582 argv.py $s
583}
584
585IFS=: $REPO_ROOT/spec/bin/printenv.py IFS
586
587# default value
588echo "$IFS" | od -A n -t x1
589
590IFS=' z'
591echo IFS="$IFS"
592
593IFS=' x' myfunc
594
595# Problem: $IFS in procs only finds GLOBAL values. But when actually
596# splitting, $IFS is a 'shvar' which respects DYNAMIC scope.
597# Use shvarGet('IFS') instead
598
599IFS=' x' myproc
600
601# Oil solution to the problem
602shvar IFS=' x' {
603 myproc
604}
605
606## STDOUT:
607:
608 20 09 0a 0a
609IFS= z
610myfunc IFS= x
611['', 'z', 'z', 'z']
612myproc IFS= z
613['', 'z', 'z', 'z']
614myproc IFS= x
615['', 'z', 'z', 'z']
616## END
617
618#### shvar usage
619shopt --set oil:upgrade
620shopt --unset errexit
621
622# no block
623shvar
624echo status=$?
625
626shvar { # no arg
627 true
628}
629echo status=$?
630
631shvar foo { # should be name=value
632 true
633}
634echo status=$?
635## STDOUT:
636status=2
637status=2
638status=2
639## END
640
641#### shvar global
642shopt --set oil:upgrade
643shopt --unset nounset
644
645echo _ESCAPER=$_ESCAPER
646echo _DIALECT=$_DIALECT
647
648shvar _ESCAPER=html _DIALECT=ninja {
649 echo block _ESCAPER=$_ESCAPER
650 echo block _DIALECT=$_DIALECT
651}
652
653echo _ESCAPER=$_ESCAPER
654echo _DIALECT=$_DIALECT
655
656# Now set them
657_ESCAPER=foo
658_DIALECT=bar
659
660echo ___
661
662echo _ESCAPER=$_ESCAPER
663echo _DIALECT=$_DIALECT
664
665shvar _ESCAPER=html _DIALECT=ninja {
666 echo block _ESCAPER=$_ESCAPER
667 echo block _DIALECT=$_DIALECT
668
669 shvar _ESCAPER=nested {
670 echo nested _ESCAPER=$_ESCAPER
671 echo nested _DIALECT=$_DIALECT
672 }
673}
674
675echo _ESCAPER=$_ESCAPER
676echo _DIALECT=$_DIALECT
677
678## STDOUT:
679_ESCAPER=
680_DIALECT=
681block _ESCAPER=html
682block _DIALECT=ninja
683_ESCAPER=
684_DIALECT=
685___
686_ESCAPER=foo
687_DIALECT=bar
688block _ESCAPER=html
689block _DIALECT=ninja
690nested _ESCAPER=nested
691nested _DIALECT=ninja
692_ESCAPER=foo
693_DIALECT=bar
694## END
695
696#### shvar local
697shopt --set oil:upgrade # blocks
698shopt --unset simple_word_eval # test word splitting
699
700proc foo {
701 shvar IFS=x MYTEMP=foo {
702 echo IFS="$IFS"
703 argv.py $s
704 echo MYTEMP=${MYTEMP:-undef}
705 }
706}
707var s = 'a b c'
708argv.py $s
709foo
710argv.py $s
711echo MYTEMP=${MYTEMP:-undef}
712## STDOUT:
713['a', 'b', 'c']
714IFS=x
715['a b c']
716MYTEMP=foo
717['a', 'b', 'c']
718MYTEMP=undef
719## END
720
721#### shvar IFS
722shopt --set oil:upgrade
723
724proc myproc() {
725 echo "$IFS" | od -A n -t x1
726
727 local mylocal=x
728 shvar IFS=w {
729 echo inside IFS="$IFS"
730 echo mylocal="$mylocal" # I do NOT want a new scope!
731 }
732 echo "$IFS" | od -A n -t x1
733}
734
735myproc
736## STDOUT:
737 20 09 0a 0a
738inside IFS=w
739mylocal=x
740 20 09 0a 0a
741## END
742
743#### shvarGet()
744shopt --set parse_proc
745
746s='xzx zxz'
747
748proc myproc {
749 echo wrong IFS="$IFS" # NOT what's used
750 echo shvar IFS=$[shvarGet('IFS')] # what IS used: dynamic scope
751 argv.py $s
752}
753
754IFS=x
755IFS=z myproc
756
757# null
758echo $[shvarGet('nonexistent')]
759
760## STDOUT:
761wrong IFS=x
762shvar IFS=z
763['x', 'x ', 'x']
764null
765## END
766