# Operators # --------- # * Operators # * Existential Operator (Binary) # * Existential Operator (Unary) # * Aliased Operators # * [not] in/of # * Chained Comparison test "binary (2-ary) math operators do not require spaces", -> a = 1 b = -1 eq +1, a*-b eq -1, a*+b eq +1, a/-b eq -1, a/+b test "operators should respect new lines as spaced", -> a = 123 + 456 eq 579, a b = "1#{2}3" + "456" eq '123456', b test "multiple operators should space themselves", -> eq (+ +1), (- -1) test "compound operators on successive lines", -> a = 1 a += 1 eq a, 2 test "bitwise operators", -> eq 2, (10 & 3) eq 11, (10 | 3) eq 9, (10 ^ 3) eq 80, (10 << 3) eq 1, (10 >> 3) eq 1, (10 >>> 3) num = 10; eq 2, (num &= 3) num = 10; eq 11, (num |= 3) num = 10; eq 9, (num ^= 3) num = 10; eq 80, (num <<= 3) num = 10; eq 1, (num >>= 3) num = 10; eq 1, (num >>>= 3) test "`instanceof`", -> ok new String instanceof String ok new Boolean instanceof Boolean # `instanceof` supports negation by prefixing the operator with `not` ok new Number not instanceof String ok new Array not instanceof Boolean test "use `::` operator on keywords `this` and `@`", -> nonce = {} obj = withAt: -> @::prop withThis: -> this::prop obj.prototype = prop: nonce eq nonce, obj.withAt() eq nonce, obj.withThis() # Existential Operator (Binary) test "binary existential operator", -> nonce = {} b = a ? nonce eq nonce, b a = null b = undefined b = a ? nonce eq nonce, b a = false b = a ? nonce eq false, b a = 0 b = a ? nonce eq 0, b test "binary existential operator conditionally evaluates second operand", -> i = 1 func = -> i -= 1 result = func() ? func() eq result, 0 test "binary existential operator with negative number", -> a = null ? - 1 eq -1, a # Existential Operator (Unary) test "postfix existential operator", -> ok (if nonexistent? then false else true) defined = true ok defined? defined = false ok defined? test "postfix existential operator only evaluates its operand once", -> semaphore = 0 fn = -> ok false if semaphore ++semaphore ok(if fn()? then true else false) test "negated postfix existential operator", -> ok !nothing?.value test "postfix existential operator on expressions", -> eq true, (1 or 0)?, true # `is`,`isnt`,`==`,`!=` test "`==` and `is` should be interchangeable", -> a = b = 1 ok a is 1 and b == 1 ok a == b ok a is b test "`!=` and `isnt` should be interchangeable", -> a = 0 b = 1 ok a isnt 1 and b != 0 ok a != b ok a isnt b # [not] in/of # - `in` should check if an array contains a value using `indexOf` # - `of` should check if a property is defined on an object using `in` test "in, of", -> arr = [1] ok 0 of arr ok 1 in arr # prefixing `not` to `in and `of` should negate them ok 1 not of arr ok 0 not in arr test "`in` should be able to operate on an array literal", -> ok 2 in [0, 1, 2, 3] ok 4 not in [0, 1, 2, 3] arr = [0, 1, 2, 3] ok 2 in arr ok 4 not in arr # should cache the value used to test the array arr = [0] val = 0 ok val++ in arr ok val++ not in arr val = 0 ok val++ of arr ok val++ not of arr test "`of` and `in` should be able to operate on instance variables", -> obj = { list: [2,3] in_list: (value) -> value in @list not_in_list: (value) -> value not in @list of_list: (value) -> value of @list not_of_list: (value) -> value not of @list } ok obj.in_list 3 ok obj.not_in_list 1 ok obj.of_list 0 ok obj.not_of_list 2 test "#???: `in` with cache and `__indexOf` should work in argument lists", -> eq 1, [Object() in Array()].length test "#737: `in` should have higher precedence than logical operators", -> eq 1, 1 in [1] and 1 test "#768: `in` should preserve evaluation order", -> share = 0 a = -> share++ if share is 0 b = -> share++ if share is 1 c = -> share++ if share is 2 ok a() not in [b(),c()] eq 3, share test "#1099: empty array after `in` should compile to `false`", -> eq 1, [5 in []].length eq false, do -> return 0 in [] test "#1354: optimized `in` checks should not happen when splats are present", -> a = [6, 9] eq 9 in [3, a...], true test "#1100: precedence in or-test compilation of `in`", -> ok 0 in [1 and 0] ok 0 in [1, 1 and 0] ok not (0 in [1, 0 or 1]) test "#1630: `in` should check `hasOwnProperty`", -> ok undefined not in length: 1 test "#1714: lexer bug with raw range `for` followed by `in`", -> 0 for [1..2] ok not ('a' in ['b']) 0 for [1..2]; ok not ('a' in ['b']) 0 for [1..10] # comment ending ok not ('a' in ['b']) # lexer state (specifically @seenFor) should be reset before each compilation CoffeeScript.compile "0 for [1..2]" CoffeeScript.compile "'a' in ['b']" test "#1099: statically determined `not in []` reporting incorrect result", -> ok 0 not in [] test "#1099: make sure expression tested gets evaluted when array is empty", -> a = 0 (do -> a = 1) in [] eq a, 1 # Chained Comparison test "chainable operators", -> ok 100 > 10 > 1 > 0 > -1 ok -1 < 0 < 1 < 10 < 100 test "`is` and `isnt` may be chained", -> ok true is not false is true is not false ok 0 is 0 isnt 1 is 1 test "different comparison operators (`>`,`<`,`is`,etc.) may be combined", -> ok 1 < 2 > 1 ok 10 < 20 > 2+3 is 5 test "some chainable operators can be negated by `unless`", -> ok (true unless 0==10!=100) test "operator precedence: `|` lower than `<`", -> eq 1, 1 | 2 < 3 < 4 test "preserve references", -> a = b = c = 1 # `a == b <= c` should become `a === b && b <= c` # (this test does not seem to test for this) ok a == b <= c test "chained operations should evaluate each value only once", -> a = 0 ok 1 > a++ < 1 test "#891: incorrect inversion of chained comparisons", -> ok (true unless 0 > 1 > 2) ok (true unless (this.NaN = 0/0) < 0/0 < this.NaN) test "#1234: Applying a splat to :: applies the splat to the wrong object", -> nonce = {} class C method: -> @nonce nonce: nonce arr = [] eq nonce, C::method arr... # should be applied to `C::` test "#1102: String literal prevents line continuation", -> eq "': '", '' + "': '" test "#1703, ---x is invalid JS", -> x = 2 eq (- --x), -1 test "Regression with implicit calls against an indented assignment", -> eq 1, a = 1 eq a, 1 test "#2155 ... conditional assignment to a closure", -> x = null func = -> x ?= (-> if true then 'hi') func() eq x(), 'hi' test "#2197: Existential existential double trouble", -> counter = 0 func = -> counter++ func()? ? 100 eq counter, 1 test "#2567: Optimization of negated existential produces correct result", -> a = 1 ok !(!a?) ok !b? test "#2508: Existential access of the prototype", -> eq NonExistent?::nothing, undefined ok Object?::toString test "floor division operator", -> eq 2, 7 // 3 eq -3, -7 // 3 eq NaN, 0 // 0 test "floor division operator compound assignment", -> a = 7 a //= 1 + 1 eq 3, a test "modulo operator", -> check = (a, b, expected) -> eq expected, a %% b, "expected #{a} %%%% #{b} to be #{expected}" check 0, 1, 0 check 0, -1, -0 check 1, 0, NaN check 1, 2, 1 check 1, -2, -1 check 1, 3, 1 check 2, 3, 2 check 3, 3, 0 check 4, 3, 1 check -1, 3, 2 check -2, 3, 1 check -3, 3, 0 check -4, 3, 2 check 5.5, 2.5, 0.5 check -5.5, 2.5, 2.0 test "modulo operator compound assignment", -> a = -2 a %%= 5 eq 3, a test "modulo operator converts arguments to numbers", -> eq 1, 1 %% '42' eq 1, '1' %% 42 eq 1, '1' %% '42' test "#3361: Modulo operator coerces right operand once", -> count = 0 res = 42 %% valueOf: -> count += 1 eq 1, count eq 0, res test "#3363: Modulo operator coercing order", -> count = 2 a = valueOf: -> count *= 2 b = valueOf: -> count += 1 eq 4, a %% b eq 5, count test "#3598: Unary + and - coerce the operand once when it is an identifier", -> # Unary + and - do not generate `_ref`s when the operand is a number, for # readability. To make sure that they do when the operand is an identifier, # test that they are consistent with another unary operator as well as another # complex expression. # Tip: Making one of the tests temporarily fail lets you easily inspect the # compiled JavaScript. assertOneCoercion = (fn) -> count = 0 value = valueOf: -> count++; 1 fn value eq 1, count eq 1, 1 ? 0 eq 1, +1 ? 0 eq -1, -1 ? 0 assertOneCoercion (a) -> eq 1, +a ? 0 assertOneCoercion (a) -> eq -1, -a ? 0 assertOneCoercion (a) -> eq -2, ~a ? 0 assertOneCoercion (a) -> eq 0.5, a / 2 ? 0 ok -2 <= 1 < 2 ok -2 <= +1 < 2 ok -2 <= -1 < 2 assertOneCoercion (a) -> ok -2 <= +a < 2 assertOneCoercion (a) -> ok -2 <= -a < 2 assertOneCoercion (a) -> ok -2 <= ~a < 2 assertOneCoercion (a) -> ok -2 <= a / 2 < 2 arrayEq [0], (n for n in [0] by 1) arrayEq [0], (n for n in [0] by +1) arrayEq [0], (n for n in [0] by -1) assertOneCoercion (a) -> arrayEq [0], (n for n in [0] by +a) assertOneCoercion (a) -> arrayEq [0], (n for n in [0] by -a) assertOneCoercion (a) -> arrayEq [0], (n for n in [0] by ~a) assertOneCoercion (a) -> arrayEq [0], (n for n in [0] by a * 2 / 2) ok 1 in [0, 1] ok +1 in [0, 1] ok -1 in [0, -1] assertOneCoercion (a) -> ok +a in [0, 1] assertOneCoercion (a) -> ok -a in [0, -1] assertOneCoercion (a) -> ok ~a in [0, -2] assertOneCoercion (a) -> ok a / 2 in [0, 0.5] test "'new' target", -> nonce = {} ctor = -> nonce eq (new ctor), nonce eq (new ctor()), nonce ok new class ctor = class ok (new ctor) instanceof ctor ok (new ctor()) instanceof ctor # Force an executable class body ctor = class then a = 1 ok (new ctor) instanceof ctor get = -> ctor ok (new get()) not instanceof ctor ok (new (get())()) instanceof ctor # classes must be called with `new`. In this case `new` applies to `get` only throws -> new get()()