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