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

768 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
210___g=G
211
212show-vars() {
213 local ___x=X
214 declare -p | grep '___'
215 echo status=$?
216
217 echo -
218 declare -p ___y | grep '___'
219 echo status=$?
220}
221
222demo() {
223 local ___y=Y
224
225 show-vars
226 echo ---
227 shopt --unset dynamic_scope
228 show-vars
229}
230
231demo
232
233## STDOUT:
234declare -- ___g=G
235declare -- ___x=X
236declare -- ___y=Y
237status=0
238-
239declare -- ___y=Y
240status=0
241---
242declare -- ___g=G
243declare -- ___x=X
244status=0
245-
246status=1
247## END
248
249
250#### OshLanguageSetValue constructs
251
252f() {
253 (( x = 42 ))
254}
255demo() {
256 f
257 echo x=$x
258}
259
260demo
261
262echo ---
263
264shopt --unset dynamic_scope
265
266unset x
267
268demo
269
270echo --- global
271echo x=$x
272## STDOUT:
273x=42
274---
275x=
276--- global
277x=
278## END
279
280
281#### shell assignments 'neutered' inside 'proc'
282shopt --set parse_proc
283
284# They can't mutate globals or anything higher on the stack
285
286proc p {
287 declare g=PROC
288 export e=PROC
289}
290
291f() {
292 g=SH
293 export e=SH
294}
295
296e=E
297g=G
298p
299echo e=$e g=$g
300
301p
302echo e=$e g=$g
303
304f
305echo e=$e g=$g
306
307## STDOUT:
308e=E g=G
309e=E g=G
310e=SH g=SH
311## END
312
313#### setglobal still allows setting globals
314shopt --set parse_proc
315
316proc p {
317 setglobal new_global = 'p'
318 setglobal g = 'p'
319}
320
321var g = 'G'
322
323p
324
325echo g=$g new_global=$new_global
326## STDOUT:
327g=p new_global=p
328## END
329
330#### setglobal d[key] inside proc should mutate global (bug #1841)
331
332shopt -s ysh:upgrade
333
334var g = {}
335
336proc mutate {
337 var g = {'local': 1} # shadows global var
338
339 setglobal g.key = 'mutated'
340 setglobal g['key2'] = 'mutated'
341
342 echo 'local that is ignored'
343 pp test_ (g)
344}
345
346echo 'BEFORE mutate global'
347pp test_ (g)
348
349mutate
350
351echo 'AFTER mutate global'
352pp test_ (g)
353
354## STDOUT:
355BEFORE mutate global
356(Dict) {}
357local that is ignored
358(Dict) {"local":1}
359AFTER mutate global
360(Dict) {"key":"mutated","key2":"mutated"}
361## END
362
363#### setglobal a[i] inside proc
364shopt -s ysh:upgrade
365
366var a = [0]
367
368proc mutate {
369 var a = [1] # shadows global var
370
371 echo 'local that is ignored'
372 setglobal a[0] = 42
373
374 pp test_ (a)
375}
376
377echo 'BEFORE mutate global'
378pp test_ (a)
379
380mutate
381
382echo 'AFTER mutate global'
383pp test_ (a)
384
385## STDOUT:
386BEFORE mutate global
387(List) [0]
388local that is ignored
389(List) [1]
390AFTER mutate global
391(List) [42]
392## END
393
394#### setglobal a[i] += and d.key +=
395shopt -s ysh:upgrade
396
397var mylist = [0]
398var mydict = {k: 0}
399
400proc mutate {
401 # these locals are ignored
402 var mylist = []
403 var mydict = {}
404
405 setglobal mylist[0] += 5
406 setglobal mydict['k'] += 5
407}
408
409mutate
410
411pp test_ (mylist)
412pp test_ (mydict)
413
414## STDOUT:
415(List) [5]
416(Dict) {"k":5}
417## END
418
419#### setglobal a[i] - i can be local or global
420shopt -s ysh:upgrade
421
422var mylist = [0, 1]
423var mydict = {k: 0, n: 1}
424
425var i = 0
426var key = 'k'
427
428proc mutate1 {
429 var mylist = [] # IGNORED
430 var mydict = {} # IGNORED
431
432 var i = 1
433 var key = 'n'
434
435 setglobal mylist[i] = 11
436 setglobal mydict[key] = 11
437}
438
439# Same thing without locals
440proc mutate2 {
441 var mylist = [] # IGNORED
442 var mydict = {} # IGNORED
443
444 setglobal mylist[i] = 22
445 setglobal mydict[key] = 22
446}
447
448mutate1
449
450pp test_ (mylist)
451pp test_ (mydict)
452echo
453
454mutate2
455
456pp test_ (mylist)
457pp test_ (mydict)
458
459## STDOUT:
460(List) [0,11]
461(Dict) {"k":0,"n":11}
462
463(List) [22,11]
464(Dict) {"k":22,"n":11}
465## END
466
467#### unset inside proc uses local scope
468shopt --set parse_brace
469shopt --set parse_proc
470
471f() {
472 unset x
473}
474
475proc p() {
476 unset x
477}
478
479proc p2() {
480 shopt --set dynamic_scope { # turn it back on
481 unset x
482 }
483}
484
485x=foo
486f
487echo f x=$x
488
489x=bar
490p
491echo p x=$x
492
493x=spam
494p2
495echo p2 x=$x
496
497## STDOUT:
498f x=
499p x=bar
500p2 x=
501## END
502
503#### unset composes when you turn on dynamic scope
504shopt -s ysh:all
505
506proc unset-two (v, w) {
507 shopt --set dynamic_scope {
508 unset $v
509 unset $w
510 }
511}
512
513demo() {
514 local x=X
515 local y=Y
516
517 echo "x=$x y=$y"
518
519 unset-two x y
520
521 shopt --unset nounset
522 echo "x=$x y=$y"
523}
524
525demo
526## STDOUT:
527x=X y=Y
528x= y=
529## END
530
531#### Temp Bindings
532shopt --set parse_proc
533
534myfunc() {
535 echo myfunc FOO=$FOO
536}
537proc myproc() {
538 echo myproc FOO=$FOO
539}
540
541FOO=bar myfunc
542FOO=bar myproc
543FOO=bar echo inline FOO=$FOO
544FOO=bar printenv.py FOO
545
546## STDOUT:
547myfunc FOO=bar
548myproc FOO=
549inline FOO=
550bar
551## END
552
553#### cd blocks don't introduce new scopes
554shopt --set oil:upgrade
555
556var x = 42
557cd / {
558 var y = 0
559 var z = 1
560 echo $x $y $z
561 setvar y = 43
562}
563setvar z = 44
564echo $x $y $z
565
566## STDOUT:
56742 0 1
56842 43 44
569## END
570
571#### IFS=: myproc exports when it doesn't need to
572shopt --set parse_proc
573shopt --set parse_brace
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
624# no block
625shvar
626echo status=$?
627
628shvar { # no arg
629 true
630}
631echo status=$?
632
633shvar foo { # should be name=value
634 true
635}
636echo status=$?
637## STDOUT:
638status=2
639status=2
640status=2
641## END
642
643#### shvar global
644shopt --set oil:upgrade
645shopt --unset nounset
646
647echo _ESCAPER=$_ESCAPER
648echo _DIALECT=$_DIALECT
649
650shvar _ESCAPER=html _DIALECT=ninja {
651 echo block _ESCAPER=$_ESCAPER
652 echo block _DIALECT=$_DIALECT
653}
654
655echo _ESCAPER=$_ESCAPER
656echo _DIALECT=$_DIALECT
657
658# Now set them
659_ESCAPER=foo
660_DIALECT=bar
661
662echo ___
663
664echo _ESCAPER=$_ESCAPER
665echo _DIALECT=$_DIALECT
666
667shvar _ESCAPER=html _DIALECT=ninja {
668 echo block _ESCAPER=$_ESCAPER
669 echo block _DIALECT=$_DIALECT
670
671 shvar _ESCAPER=nested {
672 echo nested _ESCAPER=$_ESCAPER
673 echo nested _DIALECT=$_DIALECT
674 }
675}
676
677echo _ESCAPER=$_ESCAPER
678echo _DIALECT=$_DIALECT
679
680## STDOUT:
681_ESCAPER=
682_DIALECT=
683block _ESCAPER=html
684block _DIALECT=ninja
685_ESCAPER=
686_DIALECT=
687___
688_ESCAPER=foo
689_DIALECT=bar
690block _ESCAPER=html
691block _DIALECT=ninja
692nested _ESCAPER=nested
693nested _DIALECT=ninja
694_ESCAPER=foo
695_DIALECT=bar
696## END
697
698#### shvar local
699shopt --set oil:upgrade # blocks
700shopt --unset simple_word_eval # test word splitting
701
702proc foo {
703 shvar IFS=x MYTEMP=foo {
704 echo IFS="$IFS"
705 argv.py $s
706 echo MYTEMP=${MYTEMP:-undef}
707 }
708}
709var s = 'a b c'
710argv.py $s
711foo
712argv.py $s
713echo MYTEMP=${MYTEMP:-undef}
714## STDOUT:
715['a', 'b', 'c']
716IFS=x
717['a b c']
718MYTEMP=foo
719['a', 'b', 'c']
720MYTEMP=undef
721## END
722
723#### shvar IFS
724shopt --set oil:upgrade
725
726proc myproc() {
727 echo "$IFS" | od -A n -t x1
728
729 local mylocal=x
730 shvar IFS=w {
731 echo inside IFS="$IFS"
732 echo mylocal="$mylocal" # I do NOT want a new scope!
733 }
734 echo "$IFS" | od -A n -t x1
735}
736
737myproc
738## STDOUT:
739 20 09 0a 0a
740inside IFS=w
741mylocal=x
742 20 09 0a 0a
743## END
744
745#### shvarGet()
746shopt --set parse_proc
747
748s='xzx zxz'
749
750proc myproc {
751 echo wrong IFS="$IFS" # NOT what's used
752 echo shvar IFS=$[shvarGet('IFS')] # what IS used: dynamic scope
753 argv.py $s
754}
755
756IFS=x
757IFS=z myproc
758
759# null
760echo $[shvarGet('nonexistent')]
761
762## STDOUT:
763wrong IFS=x
764shvar IFS=z
765['x', 'x ', 'x']
766null
767## END
768