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