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