1 | # YSH specific features of eval
|
2 |
|
3 | ## our_shell: ysh
|
4 | ## oils_failures_allowed: 4
|
5 |
|
6 | #### eval builtin does not take a literal block - can restore this later
|
7 |
|
8 | var b = ^(echo obj)
|
9 | call io->eval (b)
|
10 |
|
11 | call io->eval (^(echo command literal))
|
12 |
|
13 | # Doesn't work because it's a positional arg
|
14 | eval { echo block }
|
15 |
|
16 | ## status: 2
|
17 | ## STDOUT:
|
18 | obj
|
19 | command literal
|
20 | ## END
|
21 |
|
22 |
|
23 | #### Eval a block within a proc
|
24 | proc run (;;; block) {
|
25 | call io->eval(block)
|
26 | }
|
27 |
|
28 | run {
|
29 | echo 'In a block!'
|
30 | }
|
31 | ## STDOUT:
|
32 | In a block!
|
33 | ## END
|
34 |
|
35 | #### Eval block created by calling a proc
|
36 | proc lazy-block ( ; out; ; block) {
|
37 | call out->setValue(block)
|
38 | }
|
39 |
|
40 | var myglobal = 0
|
41 |
|
42 | lazy-block (&my_block) {
|
43 | json write (myglobal)
|
44 | }
|
45 |
|
46 | call io->eval(my_block)
|
47 | setvar myglobal = 1
|
48 | call io->eval(my_block)
|
49 | ## STDOUT:
|
50 | 0
|
51 | 1
|
52 | ## END
|
53 |
|
54 | #### io->eval(block) can read variables like eval ''
|
55 |
|
56 | # NO LONGER WORKS, but is this a feature rather than a bug?
|
57 |
|
58 | proc p2(code_str) {
|
59 | var mylocal = 42
|
60 | eval $code_str
|
61 | }
|
62 |
|
63 | p2 'echo mylocal=$mylocal'
|
64 |
|
65 | proc p (;;; block) {
|
66 | var mylocal = 99
|
67 | call io->eval(block)
|
68 | }
|
69 |
|
70 | p {
|
71 | echo mylocal=$mylocal
|
72 | }
|
73 |
|
74 |
|
75 | ## STDOUT:
|
76 | mylocal=42
|
77 | mylocal=99
|
78 | ## END
|
79 |
|
80 | #### eval should have a sandboxed mode
|
81 |
|
82 | proc p (;;; block) {
|
83 | var this = 42
|
84 |
|
85 | # like push-registers? Not sure
|
86 | # We could use state.ctx_Temp ? There's also ctx_FuncCall etc.
|
87 | #
|
88 | # I think we want to provide full control over the stack.
|
89 | push-frame {
|
90 | call io->eval(block)
|
91 | }
|
92 | }
|
93 |
|
94 | p {
|
95 | echo $this
|
96 | }
|
97 |
|
98 | ## status: 1
|
99 | ## STDOUT:
|
100 | TODO
|
101 | ## END
|
102 |
|
103 | #### io->eval with argv bindings
|
104 | call io->eval(^(echo "$@"), pos_args=:| foo bar baz |)
|
105 | call io->eval(^(pp test_ (:| $1 $2 $3 |)), pos_args=:| foo bar baz |)
|
106 | ## STDOUT:
|
107 | foo bar baz
|
108 | (List) ["foo","bar","baz"]
|
109 | ## END
|
110 |
|
111 | #### eval lines with argv bindings
|
112 | proc my-split (;;; block) {
|
113 | while read --raw-line {
|
114 | var cols = split(_reply)
|
115 | call io->eval(block, pos_args=cols)
|
116 | }
|
117 | }
|
118 |
|
119 | printf 'a b\nc d\n' | my-split {
|
120 | echo "$2 $1"
|
121 | }
|
122 |
|
123 | printf 'a b\nc d\n' | my-split {
|
124 | var mylocal = 'mylocal'
|
125 | echo "$2 $1 $mylocal"
|
126 | }
|
127 |
|
128 | # Now do the same thing inside a proc
|
129 | proc p {
|
130 | printf 'a b\nc d\n' | my-split {
|
131 | var local2 = 'local2'
|
132 | echo "$2 $1 $local2"
|
133 | }
|
134 | }
|
135 |
|
136 | echo
|
137 | p
|
138 |
|
139 | ## STDOUT:
|
140 | b a
|
141 | d c
|
142 | b a mylocal
|
143 | d c mylocal
|
144 |
|
145 | b a local2
|
146 | d c local2
|
147 | ## END
|
148 |
|
149 | #### eval lines with var bindings
|
150 |
|
151 | proc my-split (;;; block) {
|
152 | while read --raw-line {
|
153 | var cols = split(_reply)
|
154 | call io->eval(block, vars={_line: _reply, _first: cols[0]})
|
155 | }
|
156 | }
|
157 |
|
158 | printf 'a b\nc d\n' | my-split {
|
159 | var mylocal = 'mylocal'
|
160 | echo "$_line | $_first $mylocal"
|
161 | }
|
162 |
|
163 | # Now do the same thing inside a proc
|
164 | proc p {
|
165 | printf 'a b\nc d\n' | my-split {
|
166 | var local2 = 'local2'
|
167 | echo "$_line | $_first $local2"
|
168 | }
|
169 | }
|
170 |
|
171 | echo
|
172 | p
|
173 |
|
174 | ## STDOUT:
|
175 | a b | a mylocal
|
176 | c d | c mylocal
|
177 |
|
178 | a b | a local2
|
179 | c d | c local2
|
180 | ## END
|
181 |
|
182 | #### eval with custom dollar0
|
183 | var b = ^(write $0)
|
184 | call io->eval(b, dollar0="my arg0")
|
185 | ## STDOUT:
|
186 | my arg0
|
187 | ## END
|
188 |
|
189 | #### eval with vars bindings
|
190 | var myVar = "abc"
|
191 | call io->eval(^(pp test_ (myVar)))
|
192 | call io->eval(^(pp test_ (myVar)), vars={ 'myVar': '123' })
|
193 |
|
194 | # eval doesn't modify it's environment
|
195 | call io->eval(^(pp test_ (myVar)))
|
196 |
|
197 | ## STDOUT:
|
198 | (Str) "abc"
|
199 | (Str) "123"
|
200 | (Str) "abc"
|
201 | ## END
|
202 |
|
203 | #### dynamic binding names and mutation
|
204 | proc foreach (binding, in_; list ;; block) {
|
205 | if (in_ !== "in") {
|
206 | error 'Must use the "syntax" `foreach <binding> in (<expr>) { ... }`'
|
207 | }
|
208 |
|
209 | for item in (list) {
|
210 | call io->eval(block, vars={ [binding]: item })
|
211 | }
|
212 | }
|
213 |
|
214 | var mydicts = [{'a': 1}, {'b': 2}, {'c': 3}]
|
215 | foreach mydict in (mydicts) {
|
216 | var mylocal = 'z'
|
217 | setvar mydict.z = mylocal
|
218 |
|
219 | pp test_ (mydict)
|
220 | setvar mydict.d = 0
|
221 | }
|
222 | echo
|
223 |
|
224 | for d in (mydicts) {
|
225 | pp test_ (d)
|
226 | }
|
227 |
|
228 | ## STDOUT:
|
229 | (Dict) {"a":1,"z":"z"}
|
230 | (Dict) {"b":2,"z":"z"}
|
231 | (Dict) {"c":3,"z":"z"}
|
232 |
|
233 | (Dict) {"a":1,"z":"z","d":0}
|
234 | (Dict) {"b":2,"z":"z","d":0}
|
235 | (Dict) {"c":3,"z":"z","d":0}
|
236 | ## END
|
237 |
|
238 | #### binding procs in the eval-ed namespace
|
239 | proc __flag (short, long) {
|
240 | echo "flag $short $long"
|
241 | }
|
242 |
|
243 | proc __arg (name) {
|
244 | echo "arg $name"
|
245 | }
|
246 |
|
247 | proc parser (; spec ;; block) {
|
248 | call io->eval(block, vars={ 'flag': __flag, 'arg': __arg })
|
249 | }
|
250 |
|
251 | parser (&spec) {
|
252 | flag -h --help
|
253 | arg file
|
254 | }
|
255 |
|
256 | # but flag/arg are unavailable outside of `parser`
|
257 | # _error.code = 127 is set on "command not found" errors
|
258 |
|
259 | try { flag }
|
260 | if (_error.code !== 127) { error 'expected failure' }
|
261 |
|
262 | try { arg }
|
263 | if (_error.code !== 127) { error 'expected failure' }
|
264 |
|
265 | ## STDOUT:
|
266 | flag -h --help
|
267 | arg file
|
268 | ## END
|
269 |
|
270 | #### vars initializes the variable frame, but does not remember it
|
271 | var vars = { 'foo': 123 }
|
272 | call io->eval(^(var bar = 321;), vars=vars)
|
273 | pp test_ (vars)
|
274 |
|
275 | ## STDOUT:
|
276 | (Dict) {"foo":123}
|
277 | ## END
|
278 |
|
279 | #### eval pos_args must be strings
|
280 | call io->eval(^(true), pos_args=[1, 2, 3])
|
281 | ## status: 3
|
282 |
|
283 | #### eval with vars follows same scoping as without
|
284 |
|
285 | proc local-scope {
|
286 | var myVar = "foo"
|
287 | call io->eval(^(echo $myVar), vars={ someOtherVar: "bar" })
|
288 | call io->eval(^(echo $myVar))
|
289 | }
|
290 |
|
291 | # In global scope
|
292 | var myVar = "baz"
|
293 | call io->eval(^(echo $myVar), vars={ someOtherVar: "bar" })
|
294 | call io->eval (^(echo $myVar))
|
295 |
|
296 | local-scope
|
297 | ## STDOUT:
|
298 | baz
|
299 | baz
|
300 | foo
|
301 | foo
|
302 | ## END
|
303 |
|
304 | #### eval 'mystring' vs. call io->eval(myblock)
|
305 |
|
306 | eval 'echo plain'
|
307 | echo plain=$?
|
308 | var b = ^(echo plain)
|
309 | call io->eval(b)
|
310 | echo plain=$?
|
311 |
|
312 | echo
|
313 |
|
314 | # This calls main_loop.Batch(), which catches
|
315 | # - error.Parse
|
316 | # - error.ErrExit
|
317 | # - error.FatalRuntime - glob errors, etc.?
|
318 |
|
319 | try {
|
320 | eval 'echo one; false; echo two'
|
321 | }
|
322 | pp test_ (_error)
|
323 |
|
324 | # This calls CommandEvaluator.EvalCommand(), as blocks do
|
325 |
|
326 | var b = ^(echo one; false; echo two)
|
327 | try {
|
328 | call io->eval(b)
|
329 | }
|
330 | pp test_ (_error)
|
331 |
|
332 | ## STDOUT:
|
333 | plain
|
334 | plain=0
|
335 | plain
|
336 | plain=0
|
337 |
|
338 | one
|
339 | (Dict) {"code":1}
|
340 | one
|
341 | (Dict) {"code":1}
|
342 | ## END
|
343 |
|
344 | #### io->evalToDict() - local and global
|
345 |
|
346 | var g = 'global'
|
347 |
|
348 | # in the global frame
|
349 | var d = io->evalToDict(^(var foo = 42; var bar = g;))
|
350 | pp test_ (d)
|
351 |
|
352 | # Same thing in a local frame
|
353 | proc p (myparam) {
|
354 | var mylocal = 'local'
|
355 | # TODO: ^() needs to capture
|
356 | var cmd = ^(
|
357 | var foo = 42
|
358 | var g = "-$g"
|
359 | var p = "-$myparam"
|
360 | var L = "-$mylocal"
|
361 | )
|
362 | var d = io->evalToDict(cmd)
|
363 | pp test_ (d)
|
364 | }
|
365 | p param
|
366 |
|
367 | ## STDOUT:
|
368 | (Dict) {"foo":42,"bar":"global"}
|
369 | (Dict) {"foo":42,"g":"-global","p":"-param","L":"-local"}
|
370 | ## END
|
371 |
|
372 | #### parseCommand then io->evalToDict() - in global scope
|
373 |
|
374 | var g = 'global'
|
375 | var cmd = parseCommand('var x = 42; echo hi; var y = g')
|
376 | #var cmd = parseCommand('echo hi')
|
377 |
|
378 | pp test_ (cmd)
|
379 | #pp asdl_ (cmd)
|
380 |
|
381 | var d = io->evalToDict(cmd)
|
382 |
|
383 | pp test_ (d)
|
384 |
|
385 | ## STDOUT:
|
386 | <Block>
|
387 | hi
|
388 | (Dict) {"x":42,"y":"global"}
|
389 | ## END
|
390 |
|
391 | #### parseCommand with syntax error
|
392 |
|
393 | try {
|
394 | var cmd = parseCommand('echo >')
|
395 | }
|
396 | pp test_ (_error)
|
397 |
|
398 | ## STDOUT:
|
399 | (Dict) {"code":3,"message":"Syntax error in parseCommand()"}
|
400 | ## END
|
401 |
|
402 |
|
403 | #### Dict (&d) { ... } converts frame to dict
|
404 |
|
405 | proc Dict ( ; out; ; block) {
|
406 | var d = io->evalToDict(block)
|
407 | call out->setValue(d)
|
408 | }
|
409 |
|
410 | # it can read f
|
411 |
|
412 | var myglobal = 'global'
|
413 | var k = 'k-shadowed'
|
414 | var k2 = 'k2-shadowed'
|
415 |
|
416 | Dict (&d) {
|
417 | bare = 42
|
418 |
|
419 | # uh these find the wrong one
|
420 | # This is like redeclaring the one above, but WITHOUT the static error
|
421 | # HM HM HM
|
422 | var k = 'k-block'
|
423 | setvar k = 'k-block-mutated'
|
424 |
|
425 | # Finds the global, so not checked
|
426 | setvar k2 = 'k2-block'
|
427 |
|
428 | # This one is allowed
|
429 | setvar k3 = 'k3'
|
430 |
|
431 | # do we allow this?
|
432 | setvar myglobal = 'global'
|
433 | }
|
434 |
|
435 | pp test_ (d)
|
436 |
|
437 | exit
|
438 |
|
439 | # restored to the shadowed values
|
440 | echo k=$k
|
441 | echo k2=$k2
|
442 |
|
443 | proc p {
|
444 | Dict (&d) {
|
445 | var k = 'k-proc'
|
446 | setvar k = 'k-proc-mutated'
|
447 |
|
448 | # Not allowed STATICALLY, because o fproc check
|
449 | #setvar k2 = 'k2-proc' # local, so it's checked
|
450 | }
|
451 | }
|
452 |
|
453 | ## STDOUT:
|
454 | ## END
|
455 |
|
456 | #### block in Dict (&d) { ... } can read from outer scope
|
457 |
|
458 | proc Dict ( ; out; ; block) {
|
459 | var d = io->evalToDict(block)
|
460 | call out->setValue(d)
|
461 | }
|
462 |
|
463 | func f() {
|
464 | var x = 42
|
465 |
|
466 | Dict (&d) {
|
467 | y = x + 1 # x is from outer scope
|
468 | }
|
469 | return (d)
|
470 | }
|
471 |
|
472 | var mydict = f()
|
473 |
|
474 | pp test_ (mydict)
|
475 |
|
476 | ## STDOUT:
|
477 | (Dict) {"y":43}
|
478 | ## END
|
479 |
|
480 | #### block in yb-capture Dict (&d) can read from outer scope
|
481 |
|
482 | proc yb-capture(; out; ; block) {
|
483 | # capture status and stdout
|
484 |
|
485 | var stdout = ''
|
486 | try {
|
487 | { call io->eval(block) } | read --all (&stdout)
|
488 | }
|
489 | var result = {status: _pipeline_status[0], stdout}
|
490 |
|
491 | call out->setValue(result)
|
492 | }
|
493 |
|
494 | func f() {
|
495 | var x = 42
|
496 |
|
497 | yb-capture (&r) {
|
498 | echo $[x + 1]
|
499 | }
|
500 |
|
501 | return (r)
|
502 | }
|
503 |
|
504 | var result = f()
|
505 |
|
506 | pp test_ (result)
|
507 |
|
508 | ## STDOUT:
|
509 | (Dict) {"status":0,"stdout":"43\n"}
|
510 | ## END
|
511 |
|
512 |
|
513 | #### Dict (&d) and setvar
|
514 |
|
515 | proc Dict ( ; out; ; block) {
|
516 | echo "Dict proc global outer=$outer"
|
517 | var d = io->evalToDict(block)
|
518 |
|
519 | #echo 'proc Dict frame after evalToDict'
|
520 | #pp frame_vars_
|
521 |
|
522 | #echo "Dict outer2=$outer2"
|
523 | call out->setValue(d)
|
524 | }
|
525 |
|
526 | var outer = 'xx'
|
527 |
|
528 | Dict (&d) {
|
529 | # new variable in the front frame
|
530 | outer2 = 'outer2'
|
531 |
|
532 | echo "inside Dict outer=$outer"
|
533 | setvar outer = 'zz'
|
534 |
|
535 | setvar not_declared = 'yy'
|
536 |
|
537 | #echo 'inside Dict block'
|
538 | #pp frame_vars_
|
539 | }
|
540 |
|
541 | pp test_ (d)
|
542 | echo "after Dict outer=$outer"
|
543 |
|
544 | echo
|
545 |
|
546 |
|
547 | # Now do the same thing inside a proc
|
548 |
|
549 | proc p {
|
550 | var outer = 'p-outer'
|
551 |
|
552 | Dict (&d) {
|
553 | p = 99
|
554 | setvar outer = 'p-outer-mutated'
|
555 | }
|
556 |
|
557 | pp test_ (d)
|
558 | echo "[p] after Dict outer=$outer"
|
559 | }
|
560 |
|
561 | p
|
562 |
|
563 | echo "after p outer=$outer"
|
564 |
|
565 | ## STDOUT:
|
566 | Dict proc global outer=xx
|
567 | inside Dict outer=xx
|
568 | (Dict) {"outer2":"outer2","not_declared":"yy"}
|
569 | after Dict outer=zz
|
570 |
|
571 | Dict proc global outer=zz
|
572 | (Dict) {"p":99}
|
573 | [p] after Dict outer=p-outer-mutated
|
574 | after p outer=zz
|
575 | ## END
|
576 |
|
577 | #### Dict (&d) and setglobal
|
578 |
|
579 | proc Dict ( ; out; ; block) {
|
580 | var d = io->evalToDict(block)
|
581 | call out->setValue(d)
|
582 | }
|
583 |
|
584 | var g = 'xx'
|
585 |
|
586 | Dict (&d) {
|
587 | setglobal g = 'zz'
|
588 |
|
589 | a = 42
|
590 | pp frame_vars_
|
591 | }
|
592 | echo
|
593 |
|
594 | pp test_ (d)
|
595 | echo g=$g
|
596 |
|
597 | #pp frame_vars_
|
598 |
|
599 | ## STDOUT:
|
600 | [frame_vars_] __rear__ a
|
601 |
|
602 | (Dict) {"a":42}
|
603 | g=zz
|
604 | ## END
|
605 |
|
606 | #### bindings created shvar persist, which is different than evalToDict()
|
607 |
|
608 | var a = 'a'
|
609 | shvar IFS=: a='b' {
|
610 | echo a=$a
|
611 | inner=z
|
612 | var inner2 = 'z'
|
613 | }
|
614 | echo a=$a
|
615 | echo inner=$inner
|
616 | echo inner2=$inner2
|
617 |
|
618 | ## STDOUT:
|
619 | a=b
|
620 | a=a
|
621 | inner=z
|
622 | inner2=z
|
623 | ## END
|
624 |
|
625 | #### Block Closures in a Loop !
|
626 |
|
627 | proc task (; tasks; ; b) {
|
628 | call tasks->append(b)
|
629 | }
|
630 |
|
631 | func makeTasks() {
|
632 | var tasks = []
|
633 | for i in (0 .. 3) {
|
634 | task (tasks) { echo "i = $i" }
|
635 | }
|
636 | return (tasks)
|
637 | }
|
638 |
|
639 | var blocks = makeTasks()
|
640 | #= blocks
|
641 |
|
642 | for b in (blocks) {
|
643 | call io->eval(b)
|
644 | }
|
645 |
|
646 | ## STDOUT:
|
647 | ## END
|