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 oil_lang/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 #### integers expression and augmented assignment
29 var x = 1 + 2 * 3
30 echo x=$x
31
32 setvar x += 4
33 echo x=$x
34 ## STDOUT:
35 x=7
36 x=11
37 ## END
38
39 #### proc static check: const can't be mutated
40 proc f {
41 const x = 'local'
42 echo x=$x
43 setvar x = 'mutated'
44 echo x=$x
45 }
46 ## status: 2
47 ## STDOUT:
48 ## END
49
50 #### top-level dynamic check: const can't be be mutated
51 shopt -s oil:all
52
53 x = 'foo'
54 echo x=$x
55 const x = 'bar'
56 echo x=$x
57 ## status: 1
58 ## STDOUT:
59 x=foo
60 ## END
61
62 #### top level: var can be redefined by var/const
63 var x = "global"
64 echo x=$x
65 f() {
66 var x = "local"
67 echo x=$x
68 }
69 f
70 var x = "g2"
71 echo x=$x
72 const x = 'now-const'
73 echo x=$x
74 const x = 'oops'
75 echo x=$x
76 ## status: 1
77 ## STDOUT:
78 x=global
79 x=local
80 x=g2
81 x=now-const
82 ## END
83
84 #### setvar mutates local
85 proc f {
86 var x = 'local'
87 echo x=$x
88 setvar x = 'mutated'
89 echo x=$x
90 }
91
92 var x = 'global'
93 echo x=$x
94 f
95 echo x=$x
96 ## STDOUT:
97 x=global
98 x=local
99 x=mutated
100 x=global
101 ## END
102
103 #### top level: setvar creates global
104 setvar x = 'global'
105 echo x=$x
106 setvar x = 'g2'
107 echo x=$x
108 ## STDOUT:
109 x=global
110 x=g2
111 ## END
112
113 #### top level: setvar mutates var
114 var x = 1
115 setvar x = 42 # this is allowed
116 echo $x
117 setvar y = 50 # error because it's not declared
118 echo $y
119 ## STDOUT:
120 42
121 50
122 ## END
123
124 #### proc static check: variable changed by setvar must be declared
125 shopt -s oil:all
126
127 var x = 1
128 f() {
129 # setting global is OK
130 setglobal x = 'XX'
131 echo x=$x
132
133 # local NOT DECLARED
134 setvar x = 'YY'
135 echo y=$y
136 }
137 ## status: 2
138 ## STDOUT:
139 ## END
140
141 #### setglobal
142 f() {
143 var x = 'local'
144 echo x=$x
145 setglobal x = 'mutated'
146 }
147 var x = 'global'
148 echo x=$x
149 f
150 echo x=$x
151 ## STDOUT:
152 x=global
153 x=local
154 x=mutated
155 ## END
156
157 #### setglobal of undeclared var is allowed
158 var x = 'XX'
159 echo x=$x
160 setglobal x = 'xx'
161 echo x=$x
162
163 # fatal error
164 setglobal y = 'YY'
165
166 ## STDOUT:
167 x=XX
168 x=xx
169 ## END
170
171 #### var/setvar x, y = 1, 2
172
173 # Python doesn't allow you to have annotation on each variable!
174 # https://www.python.org/dev/peps/pep-0526/#where-annotations-aren-t-allowed
175 var x Int, y Int = 3, 4
176 echo x=$x y=$y
177
178 setvar x, y = 1, 9
179 echo x=$x y=$y
180
181 setvar y, x = x, y
182 echo x=$x y=$y
183
184 ## STDOUT:
185 x=3 y=4
186 x=1 y=9
187 x=9 y=1
188 ## END
189
190 #### setvar d->key = 42 (setitem)
191 shopt -s oil:all
192
193 var d = {}
194 setvar d['f2'] = 42
195 setvar d->f3 = 43
196
197 # Use the opposite thing to retrieve
198 var f3 = d['f3']
199 var f2 = d->f2
200 echo f3=$f3
201 echo f2=$f2
202 ## STDOUT:
203 f3=43
204 f2=42
205 ## END
206
207 #### setvar mylist[1] = 42 (setitem)
208 shopt -s oil:all
209 var mylist = [1,2,3]
210 setvar mylist[1] = 42
211
212 write --sep ' ' @mylist
213 ## STDOUT:
214 1 42 3
215 ## END
216
217 #### mixing assignment builtins and Oil assignment
218 shopt -s oil:all
219
220 proc local-var {
221 local x=1
222 var x = 2
223 echo x=$x
224 }
225
226 proc readonly-const {
227 readonly x=1
228 x = 2
229 echo x=$x
230 }
231
232 try --assign :st eval 'local-var'
233 echo status=$st
234 try --assign :st eval 'readonly-const' || true
235 echo status=$st
236
237 ## STDOUT:
238 x=2
239 status=0
240 status=1
241 ## END
242
243 #### setref out = 'YY'
244 proc p (s, :out) {
245 setref out = 'YY'
246 }
247 var x = 'XX'
248 echo x=$x
249 p abcd :x
250 echo x=$x
251
252 p zz :undefined_var
253 echo u=$undefined_var
254
255 ## STDOUT:
256 x=XX
257 x=YY
258 u=YY
259 ## END
260
261 #### setref composes: 2 levels deep
262 proc q(s, :out) {
263 echo "q s=$s"
264 setref out = 'YY'
265 }
266 proc p(:out) {
267 # NOTE: This doesn't work
268 # q dummy :out
269 var tmp = ''
270 q dummy :tmp
271 setref out = tmp
272 }
273
274 var x = 'XX'
275 echo x=$x
276 p :x
277 echo x=$x
278
279 ## STDOUT:
280 x=XX
281 q s=dummy
282 x=YY
283 ## END
284
285 #### circular dict
286 var d = {name: 'foo'}
287 = d
288 setvar d['name'] = 123
289 = d
290 setvar d['name'] = 'mystr'
291 = d
292 setvar d['name'] = d
293 = d
294 ## STDOUT:
295 (Dict) {'name': 'foo'}
296 (Dict) {'name': 123}
297 (Dict) {'name': 'mystr'}
298 (Dict) {'name': {...}}
299 ## END
300
301 #### circular list
302 var L = [1,2,3]
303 = L
304 setvar L[0] = L
305 = L
306 ## STDOUT:
307 (List) [1, 2, 3]
308 (List) [[...], 2, 3]
309 ## END
310
311
312 #### exit code of var, const, setvar
313 var x = $(false)
314 echo x=$x status=$?
315
316 setvar x = "$(false)/$(echo 42; exit 42)"
317 echo x=$x status=$?
318
319 const y = "$(false)/$(echo 43; exit 43)/$(echo 44; exit 44)"
320 echo y=$y status=$?
321
322 ## STDOUT:
323 x= status=1
324 x=/42 status=1
325 x=/43/44 status=1
326 ## END