This commit is contained in:
Jeremy Ashkenas 2010-12-11 19:47:35 -05:00
commit 450ae723cb
17 changed files with 811 additions and 571 deletions

View File

@ -145,7 +145,8 @@ task 'loc', 'count the lines of source code in the CoffeeScript compiler', ->
runTests = (CoffeeScript) ->
startTime = Date.now()
passedTests = failedTests = 0
passedTests = 0
failures = []
for name, func of require 'assert'
global[name] = ->
@ -154,14 +155,45 @@ runTests = (CoffeeScript) ->
global.eq = global.strictEqual
global.CoffeeScript = CoffeeScript
global.test = (description, fn) ->
try fn()
catch e
e.message = description if description?
e.source = fn.toString() if fn.toString?
throw e
process.on 'exit', ->
time = ((Date.now() - startTime) / 1000).toFixed(2)
message = "passed #{passedTests} tests in #{time} seconds#{reset}"
if failedTests
log "failed #{failedTests} and #{message}", red
else
if failures.length is 0
log message, green
else
log "failed #{failures.length} and #{message}", red
for fail in failures
match = fail.error.stack.match(new RegExp(fail.file+":(\\d+):(\\d+)"))
[match,line,column] = match if match
line ?= "unknown"
column ?= "unknown"
log " #{fail.file.replace(/\.coffee$/,'.js')}: line #{line}, column #{column}", red
console.log " #{fail.error.message}" if fail.error.message?
# output a cleaned-up version of the function source
if fail.error.source?
source = fail.error.source
splitSource = source.split("\n")
# count the number of spaces on the last line to determine indentation level
[lastLineSpaces] = splitSource[splitSource.length-1].match(/\ */)
if splitSource.length > 1 and lastLineSpaces
paddedSource = []
splitSource[0] = lastLineSpaces + splitSource[0]
for line in splitSource
# this should read a single value for indentation size (currently 4)
newLine = if lastLineSpaces.length > 4
line[(lastLineSpaces.length-4)..]
else
" "[lastLineSpaces.length..] + line
paddedSource.push newLine
source = paddedSource.join("\n")
console.log source
fs.readdir 'test', (err, files) ->
files.forEach (file) ->
@ -171,8 +203,7 @@ runTests = (CoffeeScript) ->
try
CoffeeScript.run code.toString(), {fileName}
catch err
failedTests += 1
log "failed #{fileName}", red, '\n' + err.stack?.toString()
failures.push {file: fileName, error: err}
task 'test', 'run the CoffeeScript language test suite', ->

View File

@ -1,8 +1,8 @@
(function() {
var Lexer, compile, fs, lexer, parser, path;
var Lexer, RESERVED, compile, fs, lexer, parser, path, _ref;
fs = require('fs');
path = require('path');
Lexer = require('./lexer').Lexer;
_ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED;
parser = require('./parser').parser;
if (require.extensions) {
require.extensions['.coffee'] = function(module, filename) {
@ -16,6 +16,7 @@
});
}
exports.VERSION = '0.9.6';
exports.RESERVED = RESERVED;
exports.helpers = require('./helpers');
exports.compile = compile = function(code, options) {
if (options == null) {

View File

@ -598,6 +598,7 @@
}
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'do', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice'];
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS);
IDENTIFIER = /^([$A-Za-z_][$\w]*)([^\n\S]*:(?!:))?/;
NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i;
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;

View File

@ -6,10 +6,10 @@
# If included on a webpage, it will automatically sniff out, compile, and
# execute all scripts present in `text/coffeescript` tags.
fs = require 'fs'
path = require 'path'
{Lexer} = require './lexer'
{parser} = require './parser'
fs = require 'fs'
path = require 'path'
{Lexer,RESERVED} = require './lexer'
{parser} = require './parser'
# TODO: Remove registerExtension when fully deprecated.
if require.extensions
@ -22,6 +22,9 @@ else if require.registerExtension
# The current CoffeeScript version number.
exports.VERSION = '0.9.6'
# Words that cannot be used as identifiers in CoffeeScript code
exports.RESERVED = RESERVED
# Expose helpers for testing.
exports.helpers = require './helpers'

View File

@ -523,6 +523,8 @@ RESERVED = [
# be used as identifiers or properties.
JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS)
# Token matching regexes.
IDENTIFIER = /// ^
( [$A-Za-z_][$\w]* )

127
test/arguments.coffee Normal file
View File

@ -0,0 +1,127 @@
###############
## Arguments ##
###############
id = (_) ->
if arguments.length is 1 then _ else Array::slice.call(arguments)
test "basic argument passing tests", ->
a = {}
b = {}
c = {}
eq 1, (id 1)
eq 2, (id 1, 2)[1]
eq a, (id a)
eq c, (id a, b, c)[2]
test "passing arguments on separate lines", ->
a = {}
b = {}
c = {}
ok(id(
a
b
c
)[1] is b)
eq(0, id(
0
10
)[0])
eq(a,id(
a
))
eq b,
(id b)
test "reference `arguments` inside of functions", ->
sumOfArgs = ->
sum = (a,b)-> a + b
Array::reduce.call(arguments,sum,0)
eq 10, sumOfArgs(0, 1, 2, 3, 4)
#### Parameter List Features
test "splats", ->
eq 2, (((splat...) -> splat) 0,1,2)[2]
eq 3, (((_, _, splat...) -> splat) 0,1,2,3)[1]
eq 1, (((splat..., _, _) -> splat) 0,1,2,3)[1]
eq 2, (((_, _, splat..., _) -> splat) 0,1,2,3)[0]
test "@-parameters: automatically assign an argument's value to a property of the context", ->
nonce = {}
((@prop) ->).call context = {}, nonce
eq nonce, context.prop
# allow splats along side the special argument
((splat..., @prop) ->).apply context = {}, [0, 0, nonce]
eq nonce, context.prop
# allow the argument itself to be a splat
((@prop...) ->).call context = {}, 0, nonce, 0
eq nonce, context.prop[1]
# the argument should still be able to be referenced normally
eq nonce, (((@prop) -> prop).call {}, nonce)
test "@-parameters and splats with constructors", ->
a = {}
b = {}
class Klass
constructor: (@first, splat..., @last) ->
obj = new Klass a, 0, 0, b
eq a, obj.first
eq b, obj.last
test "destructuring in function definition", ->
(([{a: [b], c}]...) ->
eq 1, b
eq 2, c
) {a: [1], c: 2}
test "default values", ->
nonceA = {}
nonceB = {}
a = (_,_,arg=nonceA) -> arg
eq nonceA, a()
eq nonceA, a(0)
eq nonceB, a(0,0,nonceB)
eq nonceA, a(0,0,undefined)
eq nonceA, a(0,0,null)
eq false , a(0,0,false)
eq nonceB, a(undefined,undefined,nonceB,undefined)
b = (_,arg=nonceA,_,_) -> arg
eq nonceA, b()
eq nonceA, b(0)
eq nonceB, b(0,nonceB)
eq nonceA, b(0,undefined)
eq nonceA, b(0,null)
eq false , b(0,false)
eq nonceB, b(undefined,nonceB,undefined)
c = (arg=nonceA,_,_) -> arg
eq nonceA, c()
eq 0, c(0)
eq nonceB, c(nonceB)
eq nonceA, c(undefined)
eq nonceA, c(null)
eq false , c(false)
eq nonceB, c(nonceB,undefined,undefined)
test "default values with @-parameters", ->
a = {}
b = {}
obj = f: (q = a, @p = b) -> q
eq a, obj.f()
eq b, obj.p
test "default values with splatted arguments", ->
withSplats = (a = 2, b..., c = 3, d = 5) -> a * (b.length + 1) * c * d
eq 30, withSplats()
eq 15, withSplats(1)
eq 5, withSplats(1,1)
eq 1, withSplats(1,1,1)
eq 2, withSplats(1,1,1,1)

100
test/assignment.coffee Normal file
View File

@ -0,0 +1,100 @@
################
## Assignment ##
################
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)
test "compound assignments should not declare", ->
# TODO: make description more clear
# TODO: remove reference to Math
eq Math, (-> Math or= 0)()
#### Statements as Expressions
test "assign the result of a try/catch block", ->
# multiline
result = try
nonexistent * missing
catch error
true
eq true, result
# single line
result = try nonexistent * missing catch error then true
eq true, result
test "conditionals", ->
# assign inside the condition of a conditional statement
nonce = {}
if a = nonce then 1
eq nonce, a
1 if b = nonce
eq nonce, b
# assign the result of a conditional statement
c = if true then nonce
eq nonce, c
test "assign inside the condition of a `while` loop", ->
nonce = {}
count = 1
a = nonce while count--
eq nonce, a
count = 1
while count--
b = nonce
eq nonce, b
#### Compound Assignment
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
#### Destructuring Assignment
# NO TESTS?!
# TODO: make tests for destructuring assignment

20
test/break.coffee Normal file
View File

@ -0,0 +1,20 @@
###########
## Break ##
###########
test "break at the top level", ->
for i in [1,2,3]
result = i
if i == 2
break
eq 2, result
test "break *not* at the top level", ->
someFunc = () ->
i = 0
while ++i < 3
result = i
break if i > 1
result
eq 2, someFunc()

202
test/comments.coffee Normal file
View File

@ -0,0 +1,202 @@
##############
## Comments ##
##############
# note: awkward spacing seen in some tests is likely intentional
test "comments in objects", ->
obj1 = {
# comment
# comment
# comment
one: 1
# comment
two: 2
# comment
}
ok Object::hasOwnProperty.call(obj1,'one')
eq obj1.one, 1
ok Object::hasOwnProperty.call(obj1,'two')
eq obj1.two, 2
test "comments in YAML-style objects", ->
obj2 =
# comment
# comment
# comment
three: 3
# comment
four: 4
# comment
ok Object::hasOwnProperty.call(obj2,'three')
eq obj2.three, 3
ok Object::hasOwnProperty.call(obj2,'four')
eq obj2.four, 4
test "comments following operators that continue lines", ->
sum =
1 +
1 + # comment
1
eq 3, sum
test "comments in functions", ->
fn = ->
# comment
false
false # comment
false
# comment
# comment
true
ok fn()
fn2 = -> #comment
fn()
# comment
ok fn2()
test "trailing comment before an outdent", ->
nonce = {}
fn3 = ->
if true
undefined # comment
nonce
eq nonce, fn3()
test "comments in a switch", ->
nonce = {}
result = switch nonce #comment
# comment
when false then undefined
# comment
when null #comment
undefined
else nonce # comment
eq nonce, result
test "comment with conditional statements", ->
nonce = {}
result = if false # comment
undefined
#comment
else # comment
nonce
# comment
eq nonce, result
test "spaced comments with conditional statements", ->
nonce = {}
result = if false
undefined
# comment
else if false
undefined
# comment
else
nonce
eq nonce, result
#### Block Comments
###
This is a here-comment.
Kind of like a heredoc.
###
test "block comments in objects", ->
a = {}
b = {}
obj = {
a: a
###
comment
###
b: b
}
eq a, obj.a
eq b, obj.b
test "block comments in YAML-style", ->
a = {}
b = {}
obj =
a: a
###
comment
###
b: b
eq a, obj.a
eq b, obj.b
test "block comments in functions", ->
nonce = {}
fn1 = ->
true
###
false
###
ok fn1()
fn2 = ->
###
block comment
###
nonce
eq nonce, fn2()
fn3 = ->
nonce
###
block comment
###
eq nonce, fn3()
fn4 = ->
one = ->
###
block comment
###
two = ->
three = ->
nonce
eq nonce, fn4()()()()
test "block comments inside class bodies", ->
class A
a: ->
###
Comment
###
b: ->
ok A.prototype.b instanceof Function
class B
###
Comment
###
a: ->
b: ->
ok B.prototype.a instanceof Function

View File

@ -0,0 +1,85 @@
################
## Exceptions ##
################
# shared nonce
nonce = {}
#### Throw
test "basic exception throwing", ->
throws (-> throw ->), ->
throws (-> throw new ->), ->
#### Empty Try/Catch/Finally
test "try can exist alone", ->
try
test "try/catch with empty try, empty catch", ->
try
# nothing
catch err
# nothing
test "single-line try/catch with empty try, empty catch", ->
try catch err
test "try/finally with empty try, empty finally", ->
try
# nothing
finally
# nothing
test "single-line try/finally with empty try, empty finally", ->
try finally
test "try/catch/finally with empty try, empty catch, empty finally", ->
try
catch err
finally
test "single-line try/catch/finally with empty try, empty catch, empty finally", ->
try catch err then finally
#### Try/Catch/Finally as an Expression
test "return the result of try when no exception is thrown", ->
result = try
nonce
catch err
undefined
finally
undefined
eq nonce, result
test "single-line result of try when no exception is thrown", ->
result = try nonce catch err then undefined
eq nonce, result
test "return the result of catch when an exception is thrown", ->
result = try
throw ->
catch err
nonce
eq nonce, result
test "single-line result of catch when an exception is thrown", ->
result = try throw -> catch err then nonce
eq nonce, result
test "optional catch", ->
fn = ->
try throw ->
nonce
eq nonce, fn()
#### Try/Catch/Finally Interaction With Other Constructs
test "try/catch with empty catch as last statement in a function body", ->
fn = ->
try nonce
catch err
eq nonce, fn()

227
test/operators.coffee Normal file
View File

@ -0,0 +1,227 @@
###############
## Operators ##
###############
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 "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
#### Compound Assignment Operators
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
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
#### `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
#### `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
#### Chainable Operators
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 (NaN = 0/0) < 0/0 < NaN)

View File

@ -1,63 +0,0 @@
area = (x, y, x1, y1) ->
(x - x1) * (x - y1)
x = y = 10
x1 = y1 = 20
ok area(x, y, x1, y1) is 100
# ok(area(x, y,
# x1, y1) is 100)
ok(area(
x
y
x1
y1
) is 100)
sumOfArgs = ->
sum = 0
sum += val for val in arguments
sum
ok sumOfArgs(1, 2, 3, 4, 5) is 15
((@arg) ->).call context = {}, 1
ok context.arg is 1
((splat..., @arg) ->).call context, 1, 2, 3
eq context.arg, 3
((@arg...) ->).call context, 1, 2, 3
eq context.arg.join(' '), '1 2 3'
class Klass
constructor: (@one, @two) ->
obj = new Klass 1, 2
eq obj.one, 1
eq obj.two, 2
# Destructuring.
(([{a: [b], c}]...) ->
eq b, 123
eq c, 456
) {a: [123], c: 456}
# Default values.
obj = f: (q = 123, @p = 456) -> q
eq obj.f(), 123
eq obj.p , 456
withSplats = (a = 2, b..., c = 3, d = 5) -> a * (b.length + 1) * c * d
eq 30, withSplats()
eq 15, withSplats 1
eq 5, withSplats 1, 1
eq 1, withSplats 1, 1, 1
eq 2, withSplats 1, 1, 1, 1

View File

@ -1,72 +0,0 @@
# Can assign the result of a try/catch block.
result = try
nonexistent * missing
catch error
true
result2 = try nonexistent * missing catch error then true
ok result is true and result2 is true
# Can assign a conditional statement.
getX = -> 10
if x = getX() then 100
ok x is 10
x = if getX() then 100
ok x is 100
# This-assignment.
tester = ->
@example = -> 'example function'
this
ok tester().example() is 'example function'
try throw CoffeeScript.tokens 'in = 1'
catch e then eq e.message, 'Reserved word "in" on line 1 can\'t be assigned'
num = 10
num -= 5
eq num, 5
num *= 10
eq num, 50
num /= 10
eq num, 5
num %= 3
eq num, 2
val = false
val ||= 'value'
val ||= 'eulav'
eq val, 'value'
val &&= 'rehto'
val &&= 'other'
eq val, 'other'
val = null
val ?= 'value'
val ?= 'eulav'
eq val, 'value'
for nonref in ['""', '0', 'f()']
try
ok not CoffeeScript.compile "{k: #{nonref}} = v"
catch e
eq e.message, "\"#{nonref}\" cannot be assigned."
# Compound assignments should not declare.
eq Math, (-> Math or= 0)()

View File

@ -1,28 +0,0 @@
# Test with break at the top level.
array = [1,2,3]
callWithLambda = (l) -> null
for i in array
result = callWithLambda(->)
if i == 2
console.log "i = 2"
else
break
ok result is null
# Test with break *not* at the top level.
someFunc = (input) ->
takesLambda = (l) -> null
for i in [1,2]
result = takesLambda(->)
if input == 1
return 1
else
break
return 2
ok someFunc(1) is 1
ok someFunc(2) is 2

View File

@ -1,158 +0,0 @@
# comment before a ...
###
... block comment.
###
# comment
func = ->
# comment
false
false # comment
false
# comment
true
switch 'string'
# comment
when false then something()
# comment
when null
somethingElse()
->
code()
# comment
ok func()
func
func
# Line3
obj = {
# comment
# comment
# comment
one: 1
# comment
two: 2
# comment
}
result = if true # comment
false
ok not result
result = if false
false
else # comment
45
ok result is 45
test =
'test ' +
'test ' + # comment
'test'
ok test is 'test test test'
###
This is a here-comment.
Kind of like a heredoc.
###
func = ->
###
Another block comment.
###
code
func = ->
one = ->
two = ->
three = ->
###
block.
###
four = ->
fn1 = ->
oneLevel = null
###
This isn't fine.
###
ok ok
obj = {
a: 'b'
###
comment
###
c: 'd'
}
# Spaced comments in if / elses.
result = if false
1
# comment
else if false
2
# comment
else
3
ok result is 3
result = switch 'z'
when 'z' then 7
# comment
ok result is 7
# Trailing-line comment before an outdent.
func = ->
if true
true # comment
7
ok func() is 7
# Trailing herecomment in a function.
fn = ->
code
###
debug code commented
###
fn2 = ->
class A
b: ->
###
Comment
###
c: ->
ok A.prototype.c instanceof Function
class A
###
Comment
###
b: ->
c: ->
ok A.prototype.b instanceof Function

View File

@ -1,184 +0,0 @@
# CoffeeScript's operations should be chainable, like Python's.
ok 500 > 50 > 5 > -5
# Some chainable operators can be negated by `unless`
ok (true unless 0==10!=100)
ok true is not false is true is not false
ok 0 is 0 isnt 50 is 50
ok 10 < 20 > 10
ok 50 > 10 > 5 is parseInt('5', 10)
eq 1, 1 | 2 < 3 < 4
ok 1 == 1 <= 1, '`x == y <= z` should become `x === y && y <= z`'
i = 0
ok 1 > i++ < 1, 'chained operations should evaluate each value only once'
# `==` and `is` should be interchangeable.
a = b = 1
ok a is 1 and b is 1
ok a == b
ok a is b
# Allow "if x not in y"
obj = {a: true}
ok 'a' of obj
ok 'b' not of obj
# And for "a in b" with array presence.
ok 200 in [100, 200, 300]
array = [100, 200, 300]
ok 200 in array
ok 1 not in array
ok array[0]++ in [99, 100], 'should cache testee'
# And with array presence on an instance variable.
obj = {
list: [1, 2, 3, 4, 5]
in_list: (value) -> value in @list
}
ok obj.in_list 4
ok not obj.in_list 0
# Non-spaced values still work.
x = 10
y = -5
ok x*-y is 50
ok x*+y is -50
# Compound operators.
one = 1
two = 0
one or= 2
two or= 2
eq one, 1
eq two, 2
zero = 0
zero and= 'one'
one and= 'one'
eq zero, 0
eq one , 'one'
# Compound assignment should be careful about caching variables.
count = 0
list = []
list[++count] or= 1
eq list[1], 1
eq count, 1
list[++count] ?= 2
eq list[2], 2
eq count, 2
list[count++] and= 'two'
eq list[2], 'two'
eq count, 3
base = -> ++count; base
base().four or= 4
eq base.four, 4
eq count, 4
base().five ?= 5
eq base.five, 5
eq count, 5
# Ensure that RHS is treated as a group.
a = b = false
a and= b or true
ok a is false
# Bitwise operators:
ok (10 & 3) is 2
ok (10 | 3) is 11
ok (10 ^ 3) is 9
ok (10 << 3) is 80
ok (10 >> 3) is 1
ok (10 >>> 3) is 1
num = 10; ok (num <<= 3) is 80
num = 10; ok (num >>= 3) is 1
num = 10; ok (num >>>= 3) is 1
num = 10; ok (num &= 3) is 2
num = 10; ok (num ^= 3) is 9
num = 10; ok (num |= 3) is 11
# Compound assignment with implicit objects.
obj = undefined
obj ?=
one: 1
ok obj.one is 1
obj and=
two: 2
ok not obj.one
ok obj.two is 2
# Compound assignment as a sub expression.
[a, b, c] = [1, 2, 3]
ok (a + b += c) is 6
ok a is 1
ok b is 5
ok c is 3
# Instanceof.
ok new String instanceof String
ok new Number not instanceof String
#737: `in` should have higher precedence than logical operators.
eq 1, 1 in [1] and 1
#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()] and share is 3
# `in` with cache and `__indexOf` should work in commaed lists.
eq [Object() in Array()].length, 1
# Operators should respect new lines as spaced.
a = (123) +
456
ok a is 579
a = "1#{2}3" +
"456"
ok a is '123456'
# Multiple operators should space themselves.
ok + +1 is - -1
#891: incorrect inversion of chained comparisons
(->
ok (true unless 0 > 1 > 2)
ok (true unless (NaN = 0/0) < 0/0 < NaN)
)()

View File

@ -1,54 +0,0 @@
# Basic exception throwing.
block = -> throw 'up'
throws block, 'up'
# Basic try/catch.
result = try
10
finally
15
ok result is 10
result = try
throw 'up'
catch err
err.length
ok result is 2
result = try throw 'error' catch err then err.length
ok result is 5
try throw 'catch is optional'
# try/catch with empty clauses still compiles.
try
try
# nothing
catch err
# nothing
try
# nothing
finally
# nothing
try
catch err
finally
ok yes
# Try catch with empty clause in a function body.
func = ->
try
100
catch err
ok func() is 100