2010-12-29 00:48:54 -05:00
|
|
|
# Assignment
|
|
|
|
# ----------
|
|
|
|
|
2010-12-29 14:06:57 -05:00
|
|
|
# * Assignment
|
|
|
|
# * Compound Assignment
|
|
|
|
# * Destructuring Assignment
|
|
|
|
# * Context Property (@) Assignment
|
|
|
|
# * Existential Assignment (?=)
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
# * Assignment to variables similar to generated variables
|
2010-12-29 14:06:57 -05:00
|
|
|
|
2010-12-29 00:48:54 -05:00
|
|
|
test "context property assignment (using @)", ->
|
|
|
|
nonce = {}
|
|
|
|
addMethod = ->
|
|
|
|
@method = -> nonce
|
|
|
|
this
|
|
|
|
eq nonce, addMethod.call({}).method()
|
|
|
|
|
|
|
|
test "unassignable values", ->
|
|
|
|
nonce = {}
|
|
|
|
for nonref in ['', '""', '0', 'f()'].concat CoffeeScript.RESERVED
|
|
|
|
eq nonce, (try CoffeeScript.compile "#{nonref} = v" catch e then nonce)
|
|
|
|
|
2011-03-11 21:41:12 -05:00
|
|
|
# Compound Assignment
|
2010-12-29 00:48:54 -05:00
|
|
|
|
2011-01-03 04:17:00 -05:00
|
|
|
test "boolean operators", ->
|
|
|
|
nonce = {}
|
|
|
|
|
|
|
|
a = 0
|
|
|
|
a or= nonce
|
|
|
|
eq nonce, a
|
|
|
|
|
|
|
|
b = 1
|
|
|
|
b or= nonce
|
|
|
|
eq 1, b
|
|
|
|
|
|
|
|
c = 0
|
|
|
|
c and= nonce
|
|
|
|
eq 0, c
|
|
|
|
|
|
|
|
d = 1
|
|
|
|
d and= nonce
|
|
|
|
eq nonce, d
|
|
|
|
|
|
|
|
# ensure that RHS is treated as a group
|
|
|
|
e = f = false
|
|
|
|
e and= f or true
|
|
|
|
eq false, e
|
|
|
|
|
|
|
|
test "compound assignment as a sub expression", ->
|
|
|
|
[a, b, c] = [1, 2, 3]
|
|
|
|
eq 6, (a + b += c)
|
|
|
|
eq 1, a
|
|
|
|
eq 5, b
|
|
|
|
eq 3, c
|
|
|
|
|
|
|
|
# *note: this test could still use refactoring*
|
|
|
|
test "compound assignment should be careful about caching variables", ->
|
|
|
|
count = 0
|
|
|
|
list = []
|
|
|
|
|
|
|
|
list[++count] or= 1
|
|
|
|
eq 1, list[1]
|
|
|
|
eq 1, count
|
|
|
|
|
|
|
|
list[++count] ?= 2
|
|
|
|
eq 2, list[2]
|
|
|
|
eq 2, count
|
|
|
|
|
|
|
|
list[count++] and= 6
|
|
|
|
eq 6, list[2]
|
|
|
|
eq 3, count
|
|
|
|
|
|
|
|
base = ->
|
|
|
|
++count
|
|
|
|
base
|
|
|
|
|
|
|
|
base().four or= 4
|
|
|
|
eq 4, base.four
|
|
|
|
eq 4, count
|
|
|
|
|
|
|
|
base().five ?= 5
|
|
|
|
eq 5, base.five
|
|
|
|
eq 5, count
|
|
|
|
|
2013-10-20 16:59:01 -04:00
|
|
|
eq 5, base().five ?= 6
|
|
|
|
eq 6, count
|
|
|
|
|
2011-01-03 04:17:00 -05:00
|
|
|
test "compound assignment with implicit objects", ->
|
|
|
|
obj = undefined
|
|
|
|
obj ?=
|
|
|
|
one: 1
|
|
|
|
|
|
|
|
eq 1, obj.one
|
|
|
|
|
|
|
|
obj and=
|
|
|
|
two: 2
|
|
|
|
|
|
|
|
eq undefined, obj.one
|
|
|
|
eq 2, obj.two
|
|
|
|
|
2010-12-29 00:48:54 -05:00
|
|
|
test "compound assignment (math operators)", ->
|
|
|
|
num = 10
|
|
|
|
num -= 5
|
|
|
|
eq 5, num
|
|
|
|
|
|
|
|
num *= 10
|
|
|
|
eq 50, num
|
|
|
|
|
|
|
|
num /= 10
|
|
|
|
eq 5, num
|
|
|
|
|
|
|
|
num %= 3
|
|
|
|
eq 2, num
|
|
|
|
|
|
|
|
test "more compound assignment", ->
|
|
|
|
a = {}
|
|
|
|
val = undefined
|
|
|
|
val ||= a
|
|
|
|
val ||= true
|
|
|
|
eq a, val
|
|
|
|
|
|
|
|
b = {}
|
|
|
|
val &&= true
|
|
|
|
eq val, true
|
|
|
|
val &&= b
|
|
|
|
eq b, val
|
|
|
|
|
|
|
|
c = {}
|
|
|
|
val = null
|
|
|
|
val ?= c
|
|
|
|
val ?= true
|
|
|
|
eq c, val
|
|
|
|
|
2015-08-22 07:20:20 -04:00
|
|
|
test "#1192: assignment starting with object literals", ->
|
|
|
|
doesNotThrow (-> CoffeeScript.run "{}.p = 0")
|
|
|
|
doesNotThrow (-> CoffeeScript.run "{}.p++")
|
|
|
|
doesNotThrow (-> CoffeeScript.run "{}[0] = 1")
|
|
|
|
doesNotThrow (-> CoffeeScript.run """{a: 1, 'b', "#{1}": 2}.p = 0""")
|
|
|
|
doesNotThrow (-> CoffeeScript.run "{a:{0:{}}}.a[0] = 0")
|
|
|
|
|
2010-12-29 00:48:54 -05:00
|
|
|
|
2011-03-11 21:41:12 -05:00
|
|
|
# Destructuring Assignment
|
2010-12-29 00:48:54 -05:00
|
|
|
|
2011-01-03 04:17:00 -05:00
|
|
|
test "empty destructuring assignment", ->
|
|
|
|
{} = [] = undefined
|
|
|
|
|
|
|
|
test "chained destructuring assignments", ->
|
|
|
|
[a] = {0: b} = {'0': c} = [nonce={}]
|
|
|
|
eq nonce, a
|
|
|
|
eq nonce, b
|
|
|
|
eq nonce, c
|
|
|
|
|
|
|
|
test "variable swapping to verify caching of RHS values when appropriate", ->
|
|
|
|
a = nonceA = {}
|
|
|
|
b = nonceB = {}
|
|
|
|
c = nonceC = {}
|
|
|
|
[a, b, c] = [b, c, a]
|
|
|
|
eq nonceB, a
|
|
|
|
eq nonceC, b
|
|
|
|
eq nonceA, c
|
|
|
|
[a, b, c] = [b, c, a]
|
|
|
|
eq nonceC, a
|
|
|
|
eq nonceA, b
|
|
|
|
eq nonceB, c
|
|
|
|
fn = ->
|
|
|
|
[a, b, c] = [b, c, a]
|
|
|
|
arrayEq [nonceA,nonceB,nonceC], fn()
|
|
|
|
eq nonceA, a
|
|
|
|
eq nonceB, b
|
|
|
|
eq nonceC, c
|
|
|
|
|
2015-02-23 18:39:05 -05:00
|
|
|
test "#713: destructuring assignment should return right-hand-side value", ->
|
2011-01-03 04:17:00 -05:00
|
|
|
nonces = [nonceA={},nonceB={}]
|
|
|
|
eq nonces, [a, b] = [c, d] = nonces
|
|
|
|
eq nonceA, a
|
|
|
|
eq nonceA, c
|
|
|
|
eq nonceB, b
|
|
|
|
eq nonceB, d
|
|
|
|
|
|
|
|
test "destructuring assignment with splats", ->
|
|
|
|
a = {}; b = {}; c = {}; d = {}; e = {}
|
|
|
|
[x,y...,z] = [a,b,c,d,e]
|
|
|
|
eq a, x
|
|
|
|
arrayEq [b,c,d], y
|
|
|
|
eq e, z
|
|
|
|
|
|
|
|
test "deep destructuring assignment with splats", ->
|
|
|
|
a={}; b={}; c={}; d={}; e={}; f={}; g={}; h={}; i={}
|
|
|
|
[u, [v, w..., x], y..., z] = [a, [b, c, d, e], f, g, h, i]
|
|
|
|
eq a, u
|
|
|
|
eq b, v
|
|
|
|
arrayEq [c,d], w
|
|
|
|
eq e, x
|
|
|
|
arrayEq [f,g,h], y
|
|
|
|
eq i, z
|
|
|
|
|
|
|
|
test "destructuring assignment with objects", ->
|
|
|
|
a={}; b={}; c={}
|
|
|
|
obj = {a,b,c}
|
|
|
|
{a:x, b:y, c:z} = obj
|
|
|
|
eq a, x
|
|
|
|
eq b, y
|
|
|
|
eq c, z
|
|
|
|
|
|
|
|
test "deep destructuring assignment with objects", ->
|
|
|
|
a={}; b={}; c={}; d={}
|
|
|
|
obj = {
|
|
|
|
a
|
|
|
|
b: {
|
|
|
|
'c': {
|
|
|
|
d: [
|
|
|
|
b
|
|
|
|
{e: c, f: d}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{a: w, 'b': {c: d: [x, {'f': z, e: y}]}} = obj
|
|
|
|
eq a, w
|
|
|
|
eq b, x
|
|
|
|
eq c, y
|
|
|
|
eq d, z
|
|
|
|
|
|
|
|
test "destructuring assignment with objects and splats", ->
|
|
|
|
a={}; b={}; c={}; d={}
|
|
|
|
obj = a: b: [a, b, c, d]
|
|
|
|
{a: b: [y, z...]} = obj
|
|
|
|
eq a, y
|
|
|
|
arrayEq [b,c,d], z
|
|
|
|
|
|
|
|
test "destructuring assignment against an expression", ->
|
|
|
|
a={}; b={}
|
|
|
|
[y, z] = if true then [a, b] else [b, a]
|
|
|
|
eq a, y
|
|
|
|
eq b, z
|
|
|
|
|
2011-03-27 15:22:09 -04:00
|
|
|
test "bracket insertion when necessary", ->
|
|
|
|
[a] = [0] ? [1]
|
|
|
|
eq a, 0
|
|
|
|
|
2011-01-03 04:17:00 -05:00
|
|
|
# for implicit destructuring assignment in comprehensions, see the comprehension tests
|
|
|
|
|
|
|
|
test "destructuring assignment with context (@) properties", ->
|
|
|
|
a={}; b={}; c={}; d={}; e={}
|
|
|
|
obj =
|
|
|
|
fn: () ->
|
|
|
|
local = [a, {b, c}, d, e]
|
|
|
|
[@a, {b: @b, c: @c}, @d, @e] = local
|
|
|
|
eq undefined, obj[key] for key in ['a','b','c','d','e']
|
|
|
|
obj.fn()
|
|
|
|
eq a, obj.a
|
|
|
|
eq b, obj.b
|
|
|
|
eq c, obj.c
|
|
|
|
eq d, obj.d
|
|
|
|
eq e, obj.e
|
|
|
|
|
2015-02-23 18:39:05 -05:00
|
|
|
test "#1024: destructure empty assignments to produce javascript-like results", ->
|
2011-01-10 23:09:21 -05:00
|
|
|
eq 2 * [] = 3 + 5, 16
|
|
|
|
|
2011-08-11 01:11:33 -04:00
|
|
|
test "#1005: invalid identifiers allowed on LHS of destructuring assignment", ->
|
|
|
|
disallowed = ['eval', 'arguments'].concat CoffeeScript.RESERVED
|
2011-08-11 02:17:48 -04:00
|
|
|
throws (-> CoffeeScript.compile "[#{disallowed.join ', '}] = x"), null, 'all disallowed'
|
|
|
|
throws (-> CoffeeScript.compile "[#{disallowed.join '..., '}...] = x"), null, 'all disallowed as splats'
|
|
|
|
t = tSplat = null
|
2011-08-11 01:11:33 -04:00
|
|
|
for v in disallowed when v isnt 'class' # `class` by itself is an expression
|
2011-08-11 02:17:48 -04:00
|
|
|
throws (-> CoffeeScript.compile t), null, t = "[#{v}] = x"
|
|
|
|
throws (-> CoffeeScript.compile tSplat), null, tSplat = "[#{v}...] = x"
|
2011-08-11 01:11:33 -04:00
|
|
|
doesNotThrow ->
|
|
|
|
for v in disallowed
|
|
|
|
CoffeeScript.compile "[a.#{v}] = x"
|
|
|
|
CoffeeScript.compile "[a.#{v}...] = x"
|
|
|
|
CoffeeScript.compile "[@#{v}] = x"
|
|
|
|
CoffeeScript.compile "[@#{v}...] = x"
|
|
|
|
|
2012-01-18 23:12:50 -05:00
|
|
|
test "#2055: destructuring assignment with `new`", ->
|
|
|
|
{length} = new Array
|
|
|
|
eq 0, length
|
|
|
|
|
2014-01-24 11:00:34 -05:00
|
|
|
test "#156: destructuring with expansion", ->
|
|
|
|
array = [1..5]
|
|
|
|
[first, ..., last] = array
|
|
|
|
eq 1, first
|
|
|
|
eq 5, last
|
|
|
|
[..., lastButOne, last] = array
|
|
|
|
eq 4, lastButOne
|
|
|
|
eq 5, last
|
|
|
|
[first, second, ..., last] = array
|
|
|
|
eq 2, second
|
|
|
|
[..., last] = 'strings as well -> x'
|
|
|
|
eq 'x', last
|
|
|
|
throws (-> CoffeeScript.compile "[1, ..., 3]"), null, "prohibit expansion outside of assignment"
|
|
|
|
throws (-> CoffeeScript.compile "[..., a, b...] = c"), null, "prohibit expansion and a splat"
|
|
|
|
throws (-> CoffeeScript.compile "[...] = c"), null, "prohibit lone expansion"
|
|
|
|
|
Fix #3597: Allow interpolations in object keys
The following is now allowed:
o =
a: 1
b: 2
"#{'c'}": 3
"#{'d'}": 4
e: 5
"#{'f'}": 6
g: 7
It compiles to:
o = (
obj = {
a: 1,
b: 2
},
obj["" + 'c'] = 3,
obj["" + 'd'] = 4,
obj.e = 5,
obj["" + 'f'] = 6,
obj.g = 7,
obj
);
- Closes #3039. Empty interpolations in object keys are now _supposed_ to be
allowed.
- Closes #1131. No need to improve error messages for attempted key
interpolation anymore.
- Implementing this required fixing the following bug: `("" + a): 1` used to
error out on the colon, saying "unexpected colon". But really, it is the
attempted object key that is unexpected. Now the error is on the opening
parenthesis instead.
- However, the above fix broke some error message tests for regexes. The easiest
way to fix this was to make a seemingly unrelated change: The error messages
for unexpected identifiers, numbers, strings and regexes now say for example
'unexpected string' instead of 'unexpected """some #{really long} string"""'.
In other words, the tag _name_ is used instead of the tag _value_.
This was way easier to implement, and is more helpful to the user. Using the
tag value is good for operators, reserved words and the like, but not for
tokens which can contain any text. For example, 'unexpected identifier' is
better than 'unexpected expected' (if a variable called 'expected' was used
erraneously).
- While writing tests for the above point I found a few minor bugs with string
locations which have been fixed.
2015-02-07 14:16:59 -05:00
|
|
|
test "destructuring with dynamic keys", ->
|
|
|
|
{"#{'a'}": a, """#{'b'}""": b, c} = {a: 1, b: 2, c: 3}
|
|
|
|
eq 1, a
|
|
|
|
eq 2, b
|
|
|
|
eq 3, c
|
|
|
|
throws -> CoffeeScript.compile '{"#{a}"} = b'
|
|
|
|
|
2015-08-22 15:39:26 -04:00
|
|
|
test "simple array destructuring defaults", ->
|
|
|
|
[a = 1] = []
|
|
|
|
eq 1, a
|
|
|
|
[a = 2] = [undefined]
|
|
|
|
eq 2, a
|
|
|
|
[a = 3] = [null]
|
|
|
|
eq 3, a
|
|
|
|
[a = 4] = [0]
|
|
|
|
eq 0, a
|
|
|
|
arr = [a = 5]
|
|
|
|
eq 5, a
|
|
|
|
arrayEq [5], arr
|
|
|
|
|
|
|
|
test "simple object destructuring defaults", ->
|
|
|
|
{b = 1} = {}
|
|
|
|
eq b, 1
|
|
|
|
{b = 2} = {b: undefined}
|
|
|
|
eq b, 2
|
|
|
|
{b = 3} = {b: null}
|
|
|
|
eq b, 3
|
|
|
|
{b = 4} = {b: 0}
|
|
|
|
eq b, 0
|
|
|
|
|
|
|
|
{b: c = 1} = {}
|
|
|
|
eq c, 1
|
|
|
|
{b: c = 2} = {b: undefined}
|
|
|
|
eq c, 2
|
|
|
|
{b: c = 3} = {b: null}
|
|
|
|
eq c, 3
|
|
|
|
{b: c = 4} = {b: 0}
|
|
|
|
eq c, 0
|
|
|
|
|
|
|
|
test "multiple array destructuring defaults", ->
|
|
|
|
[a = 1, b = 2, c] = [null, 12, 13]
|
|
|
|
eq a, 1
|
|
|
|
eq b, 12
|
|
|
|
eq c, 13
|
|
|
|
[a, b = 2, c = 3] = [null, 12, 13]
|
|
|
|
eq a, null
|
|
|
|
eq b, 12
|
|
|
|
eq c, 13
|
|
|
|
[a = 1, b, c = 3] = [11, 12]
|
|
|
|
eq a, 11
|
|
|
|
eq b, 12
|
|
|
|
eq c, 3
|
|
|
|
|
|
|
|
test "multiple object destructuring defaults", ->
|
|
|
|
{a = 1, b: bb = 2, 'c': c = 3, "#{0}": d = 4} = {"#{'b'}": 12}
|
|
|
|
eq a, 1
|
|
|
|
eq bb, 12
|
|
|
|
eq c, 3
|
|
|
|
eq d, 4
|
|
|
|
|
|
|
|
test "array destructuring defaults with splats", ->
|
|
|
|
[..., a = 9] = []
|
|
|
|
eq a, 9
|
|
|
|
[..., b = 9] = [19]
|
|
|
|
eq b, 19
|
|
|
|
|
|
|
|
test "deep destructuring assignment with defaults", ->
|
|
|
|
[a, [{b = 1, c = 3}] = [c: 2]] = [0]
|
|
|
|
eq a, 0
|
|
|
|
eq b, 1
|
|
|
|
eq c, 2
|
|
|
|
|
|
|
|
test "destructuring assignment with context (@) properties and defaults", ->
|
|
|
|
a={}; b={}; c={}; d={}; e={}
|
|
|
|
obj =
|
|
|
|
fn: () ->
|
|
|
|
local = [a, {b, c: null}, d]
|
|
|
|
[@a, {b: @b = b, @c = c}, @d, @e = e] = local
|
|
|
|
eq undefined, obj[key] for key in ['a','b','c','d','e']
|
|
|
|
obj.fn()
|
|
|
|
eq a, obj.a
|
|
|
|
eq b, obj.b
|
|
|
|
eq c, obj.c
|
|
|
|
eq d, obj.d
|
|
|
|
eq e, obj.e
|
|
|
|
|
|
|
|
test "destructuring assignment with defaults single evaluation", ->
|
|
|
|
callCount = 0
|
|
|
|
fn = -> callCount++
|
|
|
|
[a = fn()] = []
|
|
|
|
eq 0, a
|
|
|
|
eq 1, callCount
|
|
|
|
[a = fn()] = [10]
|
|
|
|
eq 10, a
|
|
|
|
eq 1, callCount
|
|
|
|
{a = fn(), b: c = fn()} = {a: 20, b: null}
|
|
|
|
eq 20, a
|
|
|
|
eq c, 1
|
|
|
|
eq callCount, 2
|
|
|
|
|
2011-01-03 04:17:00 -05:00
|
|
|
|
2011-03-11 21:41:12 -05:00
|
|
|
# Existential Assignment
|
2011-01-03 04:17:00 -05:00
|
|
|
|
|
|
|
test "existential assignment", ->
|
|
|
|
nonce = {}
|
|
|
|
a = false
|
|
|
|
a ?= nonce
|
|
|
|
eq false, a
|
|
|
|
b = undefined
|
|
|
|
b ?= nonce
|
|
|
|
eq nonce, b
|
|
|
|
c = null
|
|
|
|
c ?= nonce
|
|
|
|
eq nonce, c
|
2011-09-21 23:01:05 -04:00
|
|
|
|
|
|
|
test "#1627: prohibit conditional assignment of undefined variables", ->
|
2011-10-03 21:57:33 -04:00
|
|
|
throws (-> CoffeeScript.compile "x ?= 10"), null, "prohibit (x ?= 10)"
|
|
|
|
throws (-> CoffeeScript.compile "x ||= 10"), null, "prohibit (x ||= 10)"
|
|
|
|
throws (-> CoffeeScript.compile "x or= 10"), null, "prohibit (x or= 10)"
|
|
|
|
throws (-> CoffeeScript.compile "do -> x ?= 10"), null, "prohibit (do -> x ?= 10)"
|
|
|
|
throws (-> CoffeeScript.compile "do -> x ||= 10"), null, "prohibit (do -> x ||= 10)"
|
|
|
|
throws (-> CoffeeScript.compile "do -> x or= 10"), null, "prohibit (do -> x or= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; x ?= 10"), "allow (x = null; x ?= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; x ||= 10"), "allow (x = null; x ||= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; x or= 10"), "allow (x = null; x or= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ?= 10"), "allow (x = null; do -> x ?= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ||= 10"), "allow (x = null; do -> x ||= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; do -> x or= 10"), "allow (x = null; do -> x or= 10)"
|
2013-02-01 06:07:19 -05:00
|
|
|
|
2011-10-03 21:57:33 -04:00
|
|
|
throws (-> CoffeeScript.compile "-> -> -> x ?= 10"), null, "prohibit (-> -> -> x ?= 10)"
|
|
|
|
doesNotThrow (-> CoffeeScript.compile "x = null; -> -> -> x ?= 10"), "allow (x = null; -> -> -> x ?= 10)"
|
2013-02-01 06:07:19 -05:00
|
|
|
|
2012-04-10 17:07:38 -04:00
|
|
|
test "more existential assignment", ->
|
|
|
|
global.temp ?= 0
|
|
|
|
eq global.temp, 0
|
|
|
|
global.temp or= 100
|
|
|
|
eq global.temp, 100
|
|
|
|
delete global.temp
|
2011-05-11 00:08:24 -04:00
|
|
|
|
2011-05-11 09:11:41 -04:00
|
|
|
test "#1348, #1216: existential assignment compilation", ->
|
2011-05-11 00:08:24 -04:00
|
|
|
nonce = {}
|
|
|
|
a = nonce
|
|
|
|
b = (a ?= 0)
|
|
|
|
eq nonce, b
|
2011-05-11 09:11:41 -04:00
|
|
|
#the first ?= compiles into a statement; the second ?= compiles to a ternary expression
|
|
|
|
eq a ?= b ?= 1, nonce
|
2013-02-01 06:07:19 -05:00
|
|
|
|
2011-05-11 09:11:41 -04:00
|
|
|
if a then a ?= 2 else a = 3
|
|
|
|
eq a, nonce
|
2011-08-30 11:12:39 -04:00
|
|
|
|
|
|
|
test "#1591, #1101: splatted expressions in destructuring assignment must be assignable", ->
|
|
|
|
nonce = {}
|
|
|
|
for nonref in ['', '""', '0', 'f()', '(->)'].concat CoffeeScript.RESERVED
|
|
|
|
eq nonce, (try CoffeeScript.compile "[#{nonref}...] = v" catch e then nonce)
|
2011-09-01 14:47:30 -04:00
|
|
|
|
2011-12-24 07:04:34 -05:00
|
|
|
test "#1643: splatted accesses in destructuring assignments should not be declared as variables", ->
|
2011-09-01 14:47:30 -04:00
|
|
|
nonce = {}
|
2011-09-09 18:59:35 -04:00
|
|
|
accesses = ['o.a', 'o["a"]', '(o.a)', '(o.a).a', '@o.a', 'C::a', 'C::', 'f().a', 'o?.a', 'o?.a.b', 'f?().a']
|
2011-09-01 14:47:30 -04:00
|
|
|
for access in accesses
|
2011-09-09 18:59:35 -04:00
|
|
|
for i,j in [1,2,3] #position can matter
|
2011-12-24 07:04:34 -05:00
|
|
|
code =
|
2011-09-09 21:30:00 -04:00
|
|
|
"""
|
2011-12-24 07:04:34 -05:00
|
|
|
nonce = {}; nonce2 = {}; nonce3 = {};
|
2011-09-09 21:30:00 -04:00
|
|
|
@o = o = new (class C then a:{}); f = -> o
|
|
|
|
[#{new Array(i).join('x,')}#{access}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
|
|
|
|
unless #{access}[0] is nonce and #{access}[1] is nonce2 and #{access}[2] is nonce3 then throw new Error('[...]')
|
|
|
|
"""
|
2011-09-09 18:59:35 -04:00
|
|
|
eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
|
|
|
|
# subpatterns like `[[a]...]` and `[{a}...]`
|
|
|
|
subpatterns = ['[sub, sub2, sub3]', '{0: sub, 1: sub2, 2: sub3}']
|
|
|
|
for subpattern in subpatterns
|
|
|
|
for i,j in [1,2,3]
|
|
|
|
code =
|
2011-09-09 21:30:00 -04:00
|
|
|
"""
|
2011-12-24 07:04:34 -05:00
|
|
|
nonce = {}; nonce2 = {}; nonce3 = {};
|
2011-09-09 21:30:00 -04:00
|
|
|
[#{new Array(i).join('x,')}#{subpattern}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
|
|
|
|
unless sub is nonce and sub2 is nonce2 and sub3 is nonce3 then throw new Error('[sub...]')
|
|
|
|
"""
|
2011-09-09 18:59:35 -04:00
|
|
|
eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
|
2011-12-14 18:31:20 -05:00
|
|
|
|
|
|
|
test "#1838: Regression with variable assignment", ->
|
|
|
|
name =
|
|
|
|
'dave'
|
2011-12-24 07:04:34 -05:00
|
|
|
|
|
|
|
eq name, 'dave'
|
2012-03-23 13:20:15 -04:00
|
|
|
|
|
|
|
test '#2211: splats in destructured parameters', ->
|
|
|
|
doesNotThrow -> CoffeeScript.compile '([a...]) ->'
|
|
|
|
doesNotThrow -> CoffeeScript.compile '([a...],b) ->'
|
|
|
|
doesNotThrow -> CoffeeScript.compile '([a...],[b...]) ->'
|
|
|
|
throws -> CoffeeScript.compile '([a...,[a...]]) ->'
|
|
|
|
doesNotThrow -> CoffeeScript.compile '([a...,[b...]]) ->'
|
2012-05-02 18:03:32 -04:00
|
|
|
|
|
|
|
test '#2213: invocations within destructured parameters', ->
|
|
|
|
throws -> CoffeeScript.compile '([a()])->'
|
|
|
|
throws -> CoffeeScript.compile '([a:b()])->'
|
|
|
|
throws -> CoffeeScript.compile '([a:b.c()])->'
|
|
|
|
throws -> CoffeeScript.compile '({a()})->'
|
|
|
|
throws -> CoffeeScript.compile '({a:b()})->'
|
|
|
|
throws -> CoffeeScript.compile '({a:b.c()})->'
|
2012-11-27 20:11:01 -05:00
|
|
|
|
|
|
|
test '#2532: compound assignment with terminator', ->
|
|
|
|
doesNotThrow -> CoffeeScript.compile """
|
|
|
|
a = "hello"
|
|
|
|
a +=
|
|
|
|
"
|
|
|
|
world
|
|
|
|
!
|
|
|
|
"
|
|
|
|
"""
|
2013-02-01 06:07:19 -05:00
|
|
|
|
|
|
|
test "#2613: parens on LHS of destructuring", ->
|
|
|
|
a = {}
|
|
|
|
[(a).b] = [1, 2, 3]
|
|
|
|
eq a.b, 1
|
2013-10-20 16:59:01 -04:00
|
|
|
|
|
|
|
test "#2181: conditional assignment as a subexpression", ->
|
|
|
|
a = false
|
|
|
|
false && a or= true
|
|
|
|
eq false, a
|
|
|
|
eq false, not a or= true
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
|
|
|
|
test "#1500: Assignment to variables similar to generated variables", ->
|
2015-01-30 14:33:03 -05:00
|
|
|
len = 0
|
|
|
|
x = ((results = null; n) for n in [1, 2, 3])
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
arrayEq [1, 2, 3], x
|
2015-01-30 14:33:03 -05:00
|
|
|
eq 0, len
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
|
|
|
|
for x in [1, 2, 3]
|
|
|
|
f = ->
|
2015-01-30 14:33:03 -05:00
|
|
|
i = 0
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
f()
|
2015-01-30 14:33:03 -05:00
|
|
|
eq 'undefined', typeof i
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
|
2015-01-30 14:33:03 -05:00
|
|
|
ref = 2
|
|
|
|
x = ref * 2 ? 1
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
eq x, 4
|
2015-01-30 14:33:03 -05:00
|
|
|
eq 'undefined', typeof ref1
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
|
|
|
|
x = {}
|
2015-01-30 14:33:03 -05:00
|
|
|
base = -> x
|
|
|
|
name = -1
|
|
|
|
base()[-name] ?= 2
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
eq x[1], 2
|
2015-01-30 14:33:03 -05:00
|
|
|
eq base(), x
|
|
|
|
eq name, -1
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
|
2015-01-30 14:33:03 -05:00
|
|
|
f = (@a, a) -> [@a, a]
|
|
|
|
arrayEq [1, 2], f.call scope = {}, 1, 2
|
Fix #1500, #1574, #3318: Name generated vars uniquely
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
2015-01-10 17:04:30 -05:00
|
|
|
eq 1, scope.a
|
|
|
|
|
2015-08-16 16:27:28 -04:00
|
|
|
try throw 'foo'
|
2015-08-16 16:32:16 -04:00
|
|
|
catch error
|
|
|
|
eq error, 'foo'
|
2015-08-16 16:27:28 -04:00
|
|
|
|
2015-08-16 16:32:16 -04:00
|
|
|
eq error, 'foo'
|
2015-08-16 16:27:28 -04:00
|
|
|
|
2015-01-30 14:33:03 -05:00
|
|
|
doesNotThrow -> CoffeeScript.compile '(@slice...) ->'
|
2015-01-11 06:12:40 -05:00
|
|
|
|
|
|
|
test "Assignment to variables similar to helper functions", ->
|
2015-01-30 14:33:03 -05:00
|
|
|
f = (slice...) -> slice
|
2015-01-11 06:12:40 -05:00
|
|
|
arrayEq [1, 2, 3], f 1, 2, 3
|
2015-01-30 14:33:03 -05:00
|
|
|
eq 'undefined', typeof slice1
|
2015-01-11 06:12:40 -05:00
|
|
|
|
|
|
|
class A
|
|
|
|
class B extends A
|
2015-01-30 14:33:03 -05:00
|
|
|
extend = 3
|
|
|
|
hasProp = 4
|
2015-01-11 06:12:40 -05:00
|
|
|
value: 5
|
2015-01-30 14:33:03 -05:00
|
|
|
method: (bind, bind1) => [bind, bind1, extend, hasProp, @value]
|
2015-01-11 06:12:40 -05:00
|
|
|
{method} = new B
|
|
|
|
arrayEq [1, 2, 3, 4, 5], method 1, 2
|
|
|
|
|
2015-01-30 14:33:03 -05:00
|
|
|
modulo = -1 %% 3
|
|
|
|
eq 2, modulo
|
2015-01-11 06:12:40 -05:00
|
|
|
|
2015-01-30 14:33:03 -05:00
|
|
|
indexOf = [1, 2, 3]
|
|
|
|
ok 2 in indexOf
|