Merge branch 'refactorTests' of http://github.com/michaelficarra/coffee-script
This commit is contained in:
commit
450ae723cb
43
Cakefile
43
Cakefile
|
@ -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', ->
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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/;
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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]* )
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
|
@ -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()
|
|
@ -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)
|
|
@ -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
|
|
@ -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)()
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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)
|
||||
)()
|
|
@ -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
|
Loading…
Reference in New Issue