1 | suite 'Assignment', ->
|
2 | # ----------
|
3 |
|
4 | # * Assignment
|
5 | # * Compound Assignment
|
6 | # * Destructuring Assignment
|
7 | # * Context Property (@) Assignment
|
8 | # * Existential Assignment (?=)
|
9 |
|
10 | suite 'Regular Assignment', ->
|
11 |
|
12 | test "context property assignment (using @)", ->
|
13 | nonce = {}
|
14 | addMethod = ->
|
15 | @method = -> nonce
|
16 | this
|
17 | eq nonce, addMethod.call({}).method()
|
18 |
|
19 | #test "unassignable values", ->
|
20 | # nonce = {}
|
21 | # for nonref in ['', '""', '0', 'f()'].concat CoffeeScript.RESERVED
|
22 | # eq nonce, (try CoffeeScript.compile "#{nonref} = v" catch e then nonce)
|
23 |
|
24 | suite 'Compound Assignment', ->
|
25 |
|
26 | test "boolean operators", ->
|
27 | nonce = {}
|
28 |
|
29 | a = 0
|
30 | a or= nonce
|
31 | eq nonce, a
|
32 |
|
33 | b = 1
|
34 | b or= nonce
|
35 | eq 1, b
|
36 |
|
37 | c = 0
|
38 | c and= nonce
|
39 | eq 0, c
|
40 |
|
41 | d = 1
|
42 | d and= nonce
|
43 | eq nonce, d
|
44 |
|
45 | # ensure that RHS is treated as a group
|
46 | e = f = false
|
47 | e and= f or true
|
48 | eq false, e
|
49 |
|
50 | test "compound assignment as a sub expression", ->
|
51 | [a, b, c] = [1, 2, 3]
|
52 | eq 6, a + (b += c)
|
53 | eq 1, a
|
54 | eq 5, b
|
55 | eq 3, c
|
56 |
|
57 | # *note: this test could still use refactoring*
|
58 | #test "compound assignment should be careful about caching variables", ->
|
59 | # count = 0
|
60 | # list = []
|
61 |
|
62 | # list[++count] or= 1
|
63 | # eq 1, list[1]
|
64 | # eq 1, count
|
65 |
|
66 | # list[++count] ?= 2
|
67 | # eq 2, list[2]
|
68 | # eq 2, count
|
69 |
|
70 | # list[count++] and= 6
|
71 | # eq 6, list[2]
|
72 | # eq 3, count
|
73 |
|
74 | # base = ->
|
75 | # ++count
|
76 | # base
|
77 |
|
78 | # base().four or= 4
|
79 | # eq 4, base.four
|
80 | # eq 4, count
|
81 |
|
82 | # base().five ?= 5
|
83 | # eq 5, base.five
|
84 | # eq 5, count
|
85 |
|
86 | test "compound assignment with implicit objects", ->
|
87 | obj = undefined
|
88 | obj ?=
|
89 | one: 1
|
90 |
|
91 | eq 1, obj.one
|
92 |
|
93 | obj and=
|
94 | two: 2
|
95 |
|
96 | eq undefined, obj.one
|
97 | eq 2, obj.two
|
98 |
|
99 | test "compound assignment (math operators)", ->
|
100 | num = 10
|
101 | num -= 5
|
102 | eq 5, num
|
103 |
|
104 | num *= 10
|
105 | eq 50, num
|
106 |
|
107 | num /= 10
|
108 | eq 5, num
|
109 |
|
110 | num %= 3
|
111 | eq 2, num
|
112 |
|
113 | test "more compound assignment", ->
|
114 | a = {}
|
115 | val = undefined
|
116 | val ||= a
|
117 | val ||= true
|
118 | eq a, val
|
119 |
|
120 | b = {}
|
121 | val &&= true
|
122 | eq val, true
|
123 | val &&= b
|
124 | eq b, val
|
125 |
|
126 | c = {}
|
127 | val = null
|
128 | val ?= c
|
129 | val ?= true
|
130 | eq c, val
|
131 |
|
132 |
|
133 | suite 'Destructuring Assignment', ->
|
134 |
|
135 | test "empty destructuring assignment", ->
|
136 | {} = [] = undefined
|
137 |
|
138 | test "chained destructuring assignments", ->
|
139 | [a] = {0: b} = {'0': c} = [nonce={}]
|
140 | eq nonce, a
|
141 | eq nonce, b
|
142 | eq nonce, c
|
143 |
|
144 | test "variable swapping to verify caching of RHS values when appropriate", ->
|
145 | a = nonceA = {}
|
146 | b = nonceB = {}
|
147 | c = nonceC = {}
|
148 | [a, b, c] = [b, c, a]
|
149 | eq nonceB, a
|
150 | eq nonceC, b
|
151 | eq nonceA, c
|
152 | [a, b, c] = [b, c, a]
|
153 | eq nonceC, a
|
154 | eq nonceA, b
|
155 | eq nonceB, c
|
156 | fn = ->
|
157 | [a, b, c] = [b, c, a]
|
158 | arrayEq [nonceA,nonceB,nonceC], fn()
|
159 | eq nonceA, a
|
160 | eq nonceB, b
|
161 | eq nonceC, c
|
162 |
|
163 | test "#713", ->
|
164 | nonces = [nonceA={},nonceB={}]
|
165 | eq nonces, [a, b] = [c, d] = nonces
|
166 | eq nonceA, a
|
167 | eq nonceA, c
|
168 | eq nonceB, b
|
169 | eq nonceB, d
|
170 |
|
171 | test "destructuring assignment with splats", ->
|
172 | a = {}; b = {}; c = {}; d = {}; e = {}
|
173 | [x,y...,z] = [a,b,c,d,e]
|
174 | eq a, x
|
175 | arrayEq [b,c,d], y
|
176 | eq e, z
|
177 |
|
178 | test "deep destructuring assignment with splats", ->
|
179 | a={}; b={}; c={}; d={}; e={}; f={}; g={}; h={}; i={}
|
180 | [u, [v, w..., x], y..., z] = [a, [b, c, d, e], f, g, h, i]
|
181 | eq a, u
|
182 | eq b, v
|
183 | arrayEq [c,d], w
|
184 | eq e, x
|
185 | arrayEq [f,g,h], y
|
186 | eq i, z
|
187 |
|
188 | test "destructuring assignment with objects", ->
|
189 | a={}; b={}; c={}
|
190 | obj = {a,b,c}
|
191 | {a:x, b:y, c:z} = obj
|
192 | eq a, x
|
193 | eq b, y
|
194 | eq c, z
|
195 |
|
196 | test "deep destructuring assignment with objects", ->
|
197 | a={}; b={}; c={}; d={}
|
198 | obj = {a, b: {'c': {d: [b, {e: c, f: d}]}}}
|
199 | {a: w, 'b': {c: {d: [x, {'f': z, e: y}]}}} = obj
|
200 | eq a, w
|
201 | eq b, x
|
202 | eq c, y
|
203 | eq d, z
|
204 |
|
205 | test "destructuring assignment with objects and splats", ->
|
206 | a={}; b={}; c={}; d={}
|
207 | obj = a: b: [a, b, c, d]
|
208 | {a: {b: [y, z...]}} = obj
|
209 | eq a, y
|
210 | arrayEq [b,c,d], z
|
211 |
|
212 | test "destructuring assignment against an expression", ->
|
213 | a={}; b={}
|
214 | [y, z] = if true then [a, b] else [b, a]
|
215 | eq a, y
|
216 | eq b, z
|
217 |
|
218 | test "bracket insertion when necessary", ->
|
219 | [a] = [0] ? [1]
|
220 | eq a, 0
|
221 |
|
222 | # for implicit destructuring assignment in comprehensions, see the comprehension tests
|
223 |
|
224 | test "destructuring assignment with context (@) properties", ->
|
225 | a={}; b={}; c={}; d={}; e={}
|
226 | obj =
|
227 | fn: ->
|
228 | local = [a, {b, c}, d, e]
|
229 | [@a, {b: @b, c: @c}, @d, @e] = local
|
230 | eq undefined, obj[key] for key in ['a','b','c','d','e']
|
231 | obj.fn()
|
232 | eq a, obj.a
|
233 | eq b, obj.b
|
234 | eq c, obj.c
|
235 | eq d, obj.d
|
236 | eq e, obj.e
|
237 |
|
238 | #test "#1024", ->
|
239 | # eq 2 * ([] = 3 + 5), 16
|
240 |
|
241 | #test "#1005: invalid identifiers allowed on LHS of destructuring assignment", ->
|
242 | # disallowed = ['eval', 'arguments'].concat CoffeeScript.RESERVED
|
243 | # throws (-> CoffeeScript.compile "[#{disallowed.join ', '}] = x"), null, 'all disallowed'
|
244 | # throws (-> CoffeeScript.compile "[#{disallowed.join '..., '}...] = x"), null, 'all disallowed as splats'
|
245 | # t = tSplat = null
|
246 | # for v in disallowed when v isnt 'class' # `class` by itself is an expression
|
247 | # throws (-> CoffeeScript.compile t), null, t = "[#{v}] = x"
|
248 | # throws (-> CoffeeScript.compile tSplat), null, tSplat = "[#{v}...] = x"
|
249 | # doesNotThrow ->
|
250 | # for v in disallowed
|
251 | # CoffeeScript.compile "[a.#{v}] = x"
|
252 | # CoffeeScript.compile "[a.#{v}...] = x"
|
253 | # CoffeeScript.compile "[@#{v}] = x"
|
254 | # CoffeeScript.compile "[@#{v}...] = x"
|
255 |
|
256 | test "#2055: destructuring assignment with `new`", ->
|
257 | {length} = new Array
|
258 | eq 0, length
|
259 |
|
260 |
|
261 | suite 'Existential Assignment', ->
|
262 |
|
263 | test "existential assignment", ->
|
264 | nonce = {}
|
265 | a = false
|
266 | a ?= nonce
|
267 | eq false, a
|
268 | b = undefined
|
269 | b ?= nonce
|
270 | eq nonce, b
|
271 | c = null
|
272 | c ?= nonce
|
273 | eq nonce, c
|
274 |
|
275 | #test "#1627: prohibit conditional assignment of undefined variables", ->
|
276 | # throws (-> CoffeeScript.compile "x ?= 10"), null, "prohibit (x ?= 10)"
|
277 | # throws (-> CoffeeScript.compile "x ||= 10"), null, "prohibit (x ||= 10)"
|
278 | # throws (-> CoffeeScript.compile "x or= 10"), null, "prohibit (x or= 10)"
|
279 | # throws (-> CoffeeScript.compile "do -> x ?= 10"), null, "prohibit (do -> x ?= 10)"
|
280 | # throws (-> CoffeeScript.compile "do -> x ||= 10"), null, "prohibit (do -> x ||= 10)"
|
281 | # throws (-> CoffeeScript.compile "do -> x or= 10"), null, "prohibit (do -> x or= 10)"
|
282 | # doesNotThrow (-> CoffeeScript.compile "x = null; x ?= 10"), "allow (x = null; x ?= 10)"
|
283 | # doesNotThrow (-> CoffeeScript.compile "x = null; x ||= 10"), "allow (x = null; x ||= 10)"
|
284 | # doesNotThrow (-> CoffeeScript.compile "x = null; x or= 10"), "allow (x = null; x or= 10)"
|
285 | # doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ?= 10"), "allow (x = null; do -> x ?= 10)"
|
286 | # doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ||= 10"), "allow (x = null; do -> x ||= 10)"
|
287 | # doesNotThrow (-> CoffeeScript.compile "x = null; do -> x or= 10"), "allow (x = null; do -> x or= 10)"
|
288 |
|
289 | # throws (-> CoffeeScript.compile "-> -> -> x ?= 10"), null, "prohibit (-> -> -> x ?= 10)"
|
290 | # doesNotThrow (-> CoffeeScript.compile "x = null; -> -> -> x ?= 10"), "allow (x = null; -> -> -> x ?= 10)"
|
291 |
|
292 | test "more existential assignment", ->
|
293 | obj = {}
|
294 | obj.temp ?= 0
|
295 | eq obj.temp, 0
|
296 | obj.temp or= 100
|
297 | eq obj.temp, 100
|
298 |
|
299 | test "#1348, #1216: existential assignment compilation", ->
|
300 | nonce = {}
|
301 | a = nonce
|
302 | b = (a ?= 0)
|
303 | eq nonce, b
|
304 | #the first ?= compiles into a statement; the second ?= compiles to a ternary expression
|
305 | eq a ?= b ?= 1, nonce
|
306 |
|
307 | if a then a ?= 2 else a = 3
|
308 | eq a, nonce
|
309 |
|
310 | #test "#1591, #1101: splatted expressions in destructuring assignment must be assignable", ->
|
311 | # nonce = {}
|
312 | # for nonref in ['', '""', '0', 'f()', '(->)'].concat CoffeeScript.RESERVED
|
313 | # eq nonce, (try CoffeeScript.compile "[#{nonref}...] = v" catch e then nonce)
|
314 |
|
315 | #test "#1643: splatted accesses in destructuring assignments should not be declared as variables", ->
|
316 | # nonce = {}
|
317 | # accesses = ['o.a', 'o["a"]', '(o.a)', '(o.a).a', '@o.a', 'C::a', 'C::', 'f().a', 'o?.a', 'o?.a.b', 'f?().a']
|
318 | # for access in accesses
|
319 | # for i,j in [1,2,3] #position can matter
|
320 | # code =
|
321 | # """
|
322 | # nonce = {}; nonce2 = {}; nonce3 = {};
|
323 | # @o = o = new (class C then a:{}); f = -> o
|
324 | # [#{new Array(i).join('x,')}#{access}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
|
325 | # unless #{access}[0] is nonce and #{access}[1] is nonce2 and #{access}[2] is nonce3 then throw new Error('[...]')
|
326 | # """
|
327 | # eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
|
328 | # # subpatterns like `[[a]...]` and `[{a}...]`
|
329 | # subpatterns = ['[sub, sub2, sub3]', '{0: sub, 1: sub2, 2: sub3}']
|
330 | # for subpattern in subpatterns
|
331 | # for i,j in [1,2,3]
|
332 | # code =
|
333 | # """
|
334 | # nonce = {}; nonce2 = {}; nonce3 = {};
|
335 | # [#{new Array(i).join('x,')}#{subpattern}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
|
336 | # unless sub is nonce and sub2 is nonce2 and sub3 is nonce3 then throw new Error('[sub...]')
|
337 | # """
|
338 | # eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
|
339 |
|
340 | test "#1838: Regression with variable assignment", ->
|
341 | name =
|
342 | 'dave'
|
343 |
|
344 | eq name, 'dave'
|
345 |
|
346 | #test 'jashkenas/coffee-script#2211: splats in destructured parameters', ->
|
347 | # doesNotThrow -> CoffeeScript.compile '([a...]) ->'
|
348 | # doesNotThrow -> CoffeeScript.compile '([a...],b) ->'
|
349 | # doesNotThrow -> CoffeeScript.compile '([a...],[b...]) ->'
|
350 | # throws -> CoffeeScript.compile '([a...,[a...]]) ->'
|
351 | # doesNotThrow -> CoffeeScript.compile '([a...,[b...]]) ->'
|
352 |
|
353 | #test 'jashkenas/coffee-script#2213: invocations within destructured parameters', ->
|
354 | # throws -> CoffeeScript.compile '([a()])->'
|
355 | # throws -> CoffeeScript.compile '([a:b()])->'
|
356 | # throws -> CoffeeScript.compile '([a:b.c()])->'
|
357 | # throws -> CoffeeScript.compile '({a()})->'
|
358 | # throws -> CoffeeScript.compile '({a:b()})->'
|
359 | # throws -> CoffeeScript.compile '({a:b.c()})->'
|
360 |
|
361 | test '#72: parsing assignment fails when the assignee is member access of a result of a call', ->
|
362 | f = (o) -> o
|
363 | g = -> this
|
364 | nonce = {}
|
365 |
|
366 | obj = {}
|
367 | f(obj).a = nonce
|
368 | eq nonce, obj.a
|
369 |
|
370 | obj = {g: g}
|
371 | obj.g().a = nonce
|
372 | eq nonce, obj.a
|