1 # YSH specific features of eval
2
3 ## our_shell: ysh
4 ## oils_failures_allowed: 1
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 = _reply => split()
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 = _reply => split()
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 'mystring' vs. eval (myblock)
282
283 eval 'echo plain'
284 echo plain=$?
285 var b = ^(echo plain)
286 eval (b)
287 echo plain=$?
288
289 echo
290
291 # This calls main_loop.Batch(), which catches
292 # - error.Parse
293 # - error.ErrExit
294 # - error.FatalRuntime - glob errors, etc.?
295
296 try {
297 eval 'echo one; false; echo two'
298 }
299 pp test_ (_error)
300
301 # This calls CommandEvaluator.EvalCommand(), as blocks do
302
303 var b = ^(echo one; false; echo two)
304 try {
305 eval (b)
306 }
307 pp test_ (_error)
308
309 ## STDOUT:
310 plain
311 plain=0
312 plain
313 plain=0
314
315 one
316 (Dict) {"code":1}
317 one
318 (Dict) {"code":1}
319 ## END