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

691 lines, 462 significant
1## oils_failures_allowed: 2
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 = {} # shadows global var
336
337 echo 'hi from mutate'
338 setglobal g.key = 'mutated'
339 setglobal g['key2'] = 'mutated'
340
341
342 pp line (g)
343}
344
345echo 'BEFORE mutate'
346pp line (g)
347
348mutate
349
350echo 'AFTER mutate'
351pp line (g)
352
353## STDOUT:
354BEFORE mutate
355(Dict) {}
356hi from mutate
357(Dict) {"key":"mutated","key2":"mutated"}
358AFTER mutate
359(Dict) {"key":"mutated","key2":"mutated"}
360## END
361
362#### setglobal a[i] inside proc
363
364shopt -s ysh:upgrade
365
366var a = [0]
367
368proc mutate {
369 var a = [1] # shadows global var
370
371 echo 'hi from mutate'
372 setglobal a[0] = 42
373
374 pp line (a)
375}
376
377echo 'BEFORE mutate'
378pp line (a)
379
380mutate
381
382echo 'AFTER mutate'
383pp line (a)
384
385## STDOUT:
386BEFORE mutate
387(List) [0]
388hi from mutate
389(List) [42]
390AFTER mutate
391(List) [42]
392## END
393
394
395#### unset inside proc uses local scope
396shopt --set parse_brace
397shopt --set parse_proc
398
399f() {
400 unset x
401}
402
403proc p() {
404 unset x
405}
406
407proc p2() {
408 shopt --set dynamic_scope { # turn it back on
409 unset x
410 }
411}
412
413x=foo
414f
415echo f x=$x
416
417x=bar
418p
419echo p x=$x
420
421x=spam
422p2
423echo p2 x=$x
424
425## STDOUT:
426f x=
427p x=bar
428p2 x=
429## END
430
431#### unset composes when you turn on dynamic scope
432shopt -s oil:all
433
434proc unset-two {
435 shopt --set dynamic_scope {
436 unset $1
437 unset $2
438 }
439}
440
441demo() {
442 local x=X
443 local y=Y
444
445 echo "x=$x y=$y"
446
447 unset-two x y
448
449 shopt --unset nounset
450 echo "x=$x y=$y"
451}
452
453demo
454## STDOUT:
455x=X y=Y
456x= y=
457## END
458
459#### Temp Bindings
460shopt --set parse_proc
461
462myfunc() {
463 echo myfunc FOO=$FOO
464}
465proc myproc() {
466 echo myproc FOO=$FOO
467}
468
469FOO=bar myfunc
470FOO=bar myproc
471FOO=bar echo inline FOO=$FOO
472FOO=bar printenv.py FOO
473
474## STDOUT:
475myfunc FOO=bar
476myproc FOO=
477inline FOO=
478bar
479## END
480
481#### cd blocks don't introduce new scopes
482shopt --set oil:upgrade
483
484var x = 42
485cd / {
486 var y = 0
487 var z = 1
488 echo $x $y $z
489 setvar y = 43
490}
491setvar z = 44
492echo $x $y $z
493
494## STDOUT:
49542 0 1
49642 43 44
497## END
498
499#### IFS=: myproc exports when it doesn't need to
500shopt --set parse_proc
501shopt --set parse_brace
502
503s='xzx zxz'
504
505myfunc() {
506 echo myfunc IFS="$IFS"
507 argv.py $s
508}
509
510proc myproc() {
511 echo myproc IFS="$IFS"
512 argv.py $s
513}
514
515IFS=: $REPO_ROOT/spec/bin/printenv.py IFS
516
517# default value
518echo "$IFS" | od -A n -t x1
519
520IFS=' z'
521echo IFS="$IFS"
522
523IFS=' x' myfunc
524
525# Problem: $IFS in procs only finds GLOBAL values. But when actually
526# splitting, $IFS is a 'shvar' which respects DYNAMIC scope.
527# Use shvarGet('IFS') instead
528
529IFS=' x' myproc
530
531# Oil solution to the problem
532shvar IFS=' x' {
533 myproc
534}
535
536## STDOUT:
537:
538 20 09 0a 0a
539IFS= z
540myfunc IFS= x
541['', 'z', 'z', 'z']
542myproc IFS= z
543['', 'z', 'z', 'z']
544myproc IFS= x
545['', 'z', 'z', 'z']
546## END
547
548#### shvar usage
549shopt --set oil:upgrade
550shopt --unset errexit
551
552# no block
553shvar
554echo status=$?
555
556shvar { # no arg
557 true
558}
559echo status=$?
560
561shvar foo { # should be name=value
562 true
563}
564echo status=$?
565## STDOUT:
566status=2
567status=2
568status=2
569## END
570
571#### shvar global
572shopt --set oil:upgrade
573shopt --unset nounset
574
575echo _ESCAPER=$_ESCAPER
576echo _DIALECT=$_DIALECT
577
578shvar _ESCAPER=html _DIALECT=ninja {
579 echo block _ESCAPER=$_ESCAPER
580 echo block _DIALECT=$_DIALECT
581}
582
583echo _ESCAPER=$_ESCAPER
584echo _DIALECT=$_DIALECT
585
586# Now set them
587_ESCAPER=foo
588_DIALECT=bar
589
590echo ___
591
592echo _ESCAPER=$_ESCAPER
593echo _DIALECT=$_DIALECT
594
595shvar _ESCAPER=html _DIALECT=ninja {
596 echo block _ESCAPER=$_ESCAPER
597 echo block _DIALECT=$_DIALECT
598
599 shvar _ESCAPER=nested {
600 echo nested _ESCAPER=$_ESCAPER
601 echo nested _DIALECT=$_DIALECT
602 }
603}
604
605echo _ESCAPER=$_ESCAPER
606echo _DIALECT=$_DIALECT
607
608## STDOUT:
609_ESCAPER=
610_DIALECT=
611block _ESCAPER=html
612block _DIALECT=ninja
613_ESCAPER=
614_DIALECT=
615___
616_ESCAPER=foo
617_DIALECT=bar
618block _ESCAPER=html
619block _DIALECT=ninja
620nested _ESCAPER=nested
621nested _DIALECT=ninja
622_ESCAPER=foo
623_DIALECT=bar
624## END
625
626#### shvar local
627shopt --set oil:upgrade # blocks
628shopt --unset simple_word_eval # test word splitting
629
630proc foo {
631 shvar IFS=x MYTEMP=foo {
632 echo IFS="$IFS"
633 argv.py $s
634 echo MYTEMP=${MYTEMP:-undef}
635 }
636}
637var s = 'a b c'
638argv.py $s
639foo
640argv.py $s
641echo MYTEMP=${MYTEMP:-undef}
642## STDOUT:
643['a', 'b', 'c']
644IFS=x
645['a b c']
646MYTEMP=foo
647['a', 'b', 'c']
648MYTEMP=undef
649## END
650
651#### shvar IFS
652shopt --set oil:upgrade
653
654proc myproc() {
655 echo "$IFS" | od -A n -t x1
656
657 local mylocal=x
658 shvar IFS=w {
659 echo inside IFS="$IFS"
660 echo mylocal="$mylocal" # I do NOT want a new scope!
661 }
662 echo "$IFS" | od -A n -t x1
663}
664
665myproc
666## STDOUT:
667 20 09 0a 0a
668inside IFS=w
669mylocal=x
670 20 09 0a 0a
671## END
672
673#### shvarGet()
674shopt --set parse_proc
675
676s='xzx zxz'
677
678proc myproc {
679 echo wrong IFS="$IFS" # NOT what's used
680 echo shvar IFS=$[shvarGet('IFS')] # what IS used: dynamic scope
681 argv.py $s
682}
683
684IFS=x
685IFS=z myproc
686## STDOUT:
687wrong IFS=x
688shvar IFS=z
689['x', 'x ', 'x']
690## END
691