1 # Test var / setvar / etc.
2
3 # TODO: GetVar needs a mode where Obj[str] gets translated to value.Str?
4 # Then all code will work.
5 #
6 # word_eval:
7 #
8 # val = self.mem.GetVar(var_name) ->
9 # val = GetWordVar(self.mem, var_name)
10 #
11 # Conversely, in ysh/expr_eval.py:
12 # LookupVar gives you a plain Python object. I don't think there's any
13 # downside here.
14 #
15 # pp exposes the differences.
16 #
17 # Notes:
18 #
19 # - osh/cmd_exec.py handles OilAssign, which gets wrapped in value.Obj()
20 # - osh/word_eval.py _ValueToPartValue handles 3 value types. Used in:
21 # - _EvalBracedVarSub
22 # - SimpleVarSub in _EvalWordPart
23 # - osh/expr_eval.py: _LookupVar wrapper should disallow using Oil values
24 # - this is legacy stuff. Both (( )) and [[ ]]
25 # - LhsIndexedName should not reference Oil vars either
26
27
28 #### Augmented assignment doesn't work on shell arrays
29
30 # I suppose the logic is that string and array concat is ++
31 #
32 # I wonder if a ++= operator makes sense?
33
34 shopt -s parse_at simple_word_eval
35 var x = %(a 'b c')
36 argv.py @x
37
38 setvar x += %(d e) # fatal error
39 argv.py @x
40 ## status: 3
41 ## STDOUT:
42 ['a', 'b c']
43 ## END
44
45
46 #### Augmented assignment with integers
47 var x = 1 + 2 * 3
48 echo x=$x
49
50 setvar x += 4 * 1
51 echo x=$x
52 ## STDOUT:
53 x=7
54 x=11
55 ## END
56
57 #### Augmented assignment on string changes to integer
58
59 var x = '42'
60 = x
61
62 setvar x += 4 * 1
63 = x
64
65 setvar x += '9'
66 = x
67
68 ## STDOUT:
69 (Str) '42'
70 (Int) 46
71 (Int) 55
72 ## END
73
74 #### Augmented assignment doesn't work with multiple LHS
75
76 var x = 3
77 var y = 4
78 setvar x, y += 2
79 echo $x $y
80
81 ## status: 2
82 ## STDOUT:
83 ## END
84
85
86 #### proc static check: const can't be mutated
87 proc f {
88 const x = 'local'
89 echo x=$x
90 setvar x = 'mutated'
91 echo x=$x
92 }
93 ## status: 2
94 ## STDOUT:
95 ## END
96
97 #### top-level dynamic check: const can't be be mutated
98 shopt -s oil:all
99
100 const x = 'foo'
101 echo x=$x
102 const x = 'bar'
103 echo x=$x
104 ## status: 1
105 ## STDOUT:
106 x=foo
107 ## END
108
109 #### top level: var can be redefined by var/const
110 var x = "global"
111 echo x=$x
112 f() {
113 var x = "local"
114 echo x=$x
115 }
116 f
117 var x = "g2"
118 echo x=$x
119 const x = 'now-const'
120 echo x=$x
121 const x = 'oops'
122 echo x=$x
123 ## status: 1
124 ## STDOUT:
125 x=global
126 x=local
127 x=g2
128 x=now-const
129 ## END
130
131 #### setvar mutates local
132 proc f {
133 var x = 'local'
134 echo x=$x
135 setvar x = 'mutated'
136 echo x=$x
137 }
138
139 var x = 'global'
140 echo x=$x
141 f
142 echo x=$x
143 ## STDOUT:
144 x=global
145 x=local
146 x=mutated
147 x=global
148 ## END
149
150 #### top level: setvar creates global
151 setvar x = 'global'
152 echo x=$x
153 setvar x = 'g2'
154 echo x=$x
155 ## STDOUT:
156 x=global
157 x=g2
158 ## END
159
160 #### top level: setvar mutates var
161 var x = 1
162 setvar x = 42 # this is allowed
163 echo $x
164 setvar y = 50 # error because it's not declared
165 echo $y
166 ## STDOUT:
167 42
168 50
169 ## END
170
171 #### proc static check: variable changed by setvar must be declared
172 shopt -s oil:all
173
174 var x = 1
175 f() {
176 # setting global is OK
177 setglobal x = 'XX'
178 echo x=$x
179
180 # local NOT DECLARED
181 setvar x = 'YY'
182 echo y=$y
183 }
184 ## status: 2
185 ## STDOUT:
186 ## END
187
188 #### setglobal
189 f() {
190 var x = 'local'
191 echo x=$x
192 setglobal x = 'mutated'
193 }
194 var x = 'global'
195 echo x=$x
196 f
197 echo x=$x
198 ## STDOUT:
199 x=global
200 x=local
201 x=mutated
202 ## END
203
204 #### setglobal of undeclared var is allowed
205 var x = 'XX'
206 echo x=$x
207 setglobal x = 'xx'
208 echo x=$x
209
210 # fatal error
211 setglobal y = 'YY'
212
213 ## STDOUT:
214 x=XX
215 x=xx
216 ## END
217
218 #### var x, y = 1, 2 is NOT allowed
219
220 # The syntax consistent with JavaScript woudl be
221 # var x = 1, y = 2;
222
223 var x, y = 1, 2
224
225 ## status: 2
226 ## STDOUT:
227 ## END
228
229
230 #### setvar x, y = 1, 2
231
232 # Python doesn't allow you to have annotation on each variable!
233 # https://www.python.org/dev/peps/pep-0526/#where-annotations-aren-t-allowed
234 var x Int = 3
235 var y Int = 4
236 echo "x=$x y=$y"
237
238 setvar x, y = 1, 9
239 echo "x=$x y=$y"
240
241 setvar y, x = x, y
242 echo "x=$x y=$y"
243
244 setvar x, y = x*2, x*3
245 echo "x=$x y=$y"
246
247 ## STDOUT:
248 x=3 y=4
249 x=1 y=9
250 x=9 y=1
251 x=18 y=27
252 ## END
253
254 #### setvar to swap List and Dict elements
255 var x = [1, 2, 3]
256 echo @x
257
258 setvar x[0], x[1] = x[1], x[0]
259
260 echo @x
261
262 var d = {int: 42}
263
264 setvar x[0], d.int = d.int, x[0]
265
266 echo @x
267 json write (d)
268
269 ## STDOUT:
270 1 2 3
271 2 1 3
272 42 1 3
273 {
274 "int": 2
275 }
276 ## END
277
278 #### setvar d.key = 42 (setitem)
279 shopt -s oil:all
280
281 var d = {}
282 setvar d['f2'] = 42
283 setvar d.f3 = 43
284
285 # Use the opposite thing to retrieve
286 var f3 = d['f3']
287 var f2 = d.f2
288 echo f3=$f3
289 echo f2=$f2
290 ## STDOUT:
291 f3=43
292 f2=42
293 ## END
294
295 #### setvar mylist[1] = 42 (setitem)
296 shopt -s oil:all
297 var mylist = [1,2,3]
298 setvar mylist[1] = 42
299
300 write --sep ' ' @mylist
301 ## STDOUT:
302 1 42 3
303 ## END
304
305 #### mixing assignment builtins and Oil assignment
306 shopt -s oil:all parse_equals
307
308 proc local-var {
309 local x=1
310 var x = 2
311 echo x=$x
312 }
313
314 proc readonly-const {
315 readonly x=1
316 const x = 2
317 echo x=$x
318 }
319
320 try eval 'local-var'
321 echo status=$_status
322 try eval 'readonly-const'
323 echo status=$_status
324
325 ## STDOUT:
326 x=2
327 status=0
328 status=1
329 ## END
330
331 #### setref out = 'YY'
332 proc p (s, out Ref) {
333 setref out = 'YY'
334 }
335 var x = 'XX'
336 echo x=$x
337 p abcd :x
338 echo x=$x
339
340 p zz :undefined_var
341 echo u=$undefined_var
342
343 ## STDOUT:
344 x=XX
345 x=YY
346 u=YY
347 ## END
348
349 #### setref composes: 2 levels deep
350 proc q(s, out Ref) {
351 echo "q s=$s"
352 setref out = 'YY'
353 }
354 proc p(out Ref) {
355 # NOTE: This doesn't work
356 # q dummy :out
357 var tmp = ''
358 q dummy :tmp
359 setref out = tmp
360 }
361
362 var x = 'XX'
363 echo x=$x
364 p :x
365 echo x=$x
366
367 ## STDOUT:
368 x=XX
369 q s=dummy
370 x=YY
371 ## END
372
373 #### circular dict - TODO 2023-06 REGRESS
374 var d = {name: 'foo'}
375 = d
376 setvar d['name'] = 123
377 = d
378 setvar d['name'] = 'mystr'
379 = d
380 #setvar d['name'] = d
381 #= d
382
383 # This used to print ...
384
385 ## STDOUT:
386 (OrderedDict) <'name': 'foo'>
387 (OrderedDict) <'name': 123>
388 (OrderedDict) <'name': 'mystr'>
389 (OrderedDict) <'name': ...>
390 ## END
391
392 #### circular list - TODO 2023-06 REGRESS
393 var L = [1,2,3]
394 = L
395 #setvar L[0] = L
396 #= L
397 ## STDOUT:
398 (List) [1, 2, 3]
399 (List) [[...], 2, 3]
400 ## END
401
402
403 #### exit code of var, const, setvar with command sub
404
405 # NOTE: This feels PROBLEMATIC without command_sub_errexit feels like it should
406 # be the last one ...
407
408 run() {
409 $REPO_ROOT/bin/osh -O parse_proc -c "$@"
410
411 # Identical
412 # $SH +O oil:all -O parse_proc -c "$@"
413 }
414
415 set +o errexit
416
417 run '
418 var x = $(false)
419 echo inside1=$?
420 '
421 echo outside1=$?
422
423 run '
424 setvar x = $(false)
425 echo inside2=$?
426 '
427 echo outside2=$?
428
429 # Argument list
430 run '
431 _ split( $(false) )
432 echo inside3=$?
433 '
434 echo outside3=$?
435
436 # Place expression
437 run '
438 var d = {}
439 setvar d[ $(false) ] = 42
440 echo inside4=$?
441 '
442 echo outside4=$?
443
444 ## STDOUT:
445 outside1=1
446 outside2=1
447 outside3=1
448 outside4=1
449 ## END
450
451 #### setvar obj[INVALID TYPE] =
452
453 set +o errexit
454
455 $SH -c '
456 var d = {}
457 setvar d["key"] = 5
458 echo "d.key = $[d.key]"
459 setvar d[42] = 6
460 echo "should not get here"
461 '
462 echo outside1=$?
463
464 $SH -c '
465 var L = [42]
466 setvar L[0] = 43
467 echo "L[0] = $[L[0]]"
468 setvar L["key"] = 44
469 '
470 echo outside2=$?
471
472 ## STDOUT:
473 d.key = 5
474 outside1=3
475 L[0] = 43
476 outside2=3
477 ## END