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) ->
|
runTests = (CoffeeScript) ->
|
||||||
startTime = Date.now()
|
startTime = Date.now()
|
||||||
passedTests = failedTests = 0
|
passedTests = 0
|
||||||
|
failures = []
|
||||||
|
|
||||||
for name, func of require 'assert'
|
for name, func of require 'assert'
|
||||||
global[name] = ->
|
global[name] = ->
|
||||||
|
@ -154,14 +155,45 @@ runTests = (CoffeeScript) ->
|
||||||
|
|
||||||
global.eq = global.strictEqual
|
global.eq = global.strictEqual
|
||||||
global.CoffeeScript = CoffeeScript
|
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', ->
|
process.on 'exit', ->
|
||||||
time = ((Date.now() - startTime) / 1000).toFixed(2)
|
time = ((Date.now() - startTime) / 1000).toFixed(2)
|
||||||
message = "passed #{passedTests} tests in #{time} seconds#{reset}"
|
message = "passed #{passedTests} tests in #{time} seconds#{reset}"
|
||||||
if failedTests
|
if failures.length is 0
|
||||||
log "failed #{failedTests} and #{message}", red
|
|
||||||
else
|
|
||||||
log message, green
|
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) ->
|
fs.readdir 'test', (err, files) ->
|
||||||
files.forEach (file) ->
|
files.forEach (file) ->
|
||||||
|
@ -171,8 +203,7 @@ runTests = (CoffeeScript) ->
|
||||||
try
|
try
|
||||||
CoffeeScript.run code.toString(), {fileName}
|
CoffeeScript.run code.toString(), {fileName}
|
||||||
catch err
|
catch err
|
||||||
failedTests += 1
|
failures.push {file: fileName, error: err}
|
||||||
log "failed #{fileName}", red, '\n' + err.stack?.toString()
|
|
||||||
|
|
||||||
|
|
||||||
task 'test', 'run the CoffeeScript language test suite', ->
|
task 'test', 'run the CoffeeScript language test suite', ->
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
(function() {
|
(function() {
|
||||||
var Lexer, compile, fs, lexer, parser, path;
|
var Lexer, RESERVED, compile, fs, lexer, parser, path, _ref;
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
path = require('path');
|
path = require('path');
|
||||||
Lexer = require('./lexer').Lexer;
|
_ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED;
|
||||||
parser = require('./parser').parser;
|
parser = require('./parser').parser;
|
||||||
if (require.extensions) {
|
if (require.extensions) {
|
||||||
require.extensions['.coffee'] = function(module, filename) {
|
require.extensions['.coffee'] = function(module, filename) {
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.VERSION = '0.9.6';
|
exports.VERSION = '0.9.6';
|
||||||
|
exports.RESERVED = RESERVED;
|
||||||
exports.helpers = require('./helpers');
|
exports.helpers = require('./helpers');
|
||||||
exports.compile = compile = function(code, options) {
|
exports.compile = compile = function(code, options) {
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
|
|
|
@ -598,6 +598,7 @@
|
||||||
}
|
}
|
||||||
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'do', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice'];
|
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'do', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice'];
|
||||||
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
|
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
|
||||||
|
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS);
|
||||||
IDENTIFIER = /^([$A-Za-z_][$\w]*)([^\n\S]*:(?!:))?/;
|
IDENTIFIER = /^([$A-Za-z_][$\w]*)([^\n\S]*:(?!:))?/;
|
||||||
NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i;
|
NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i;
|
||||||
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
|
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
# If included on a webpage, it will automatically sniff out, compile, and
|
# If included on a webpage, it will automatically sniff out, compile, and
|
||||||
# execute all scripts present in `text/coffeescript` tags.
|
# execute all scripts present in `text/coffeescript` tags.
|
||||||
|
|
||||||
fs = require 'fs'
|
fs = require 'fs'
|
||||||
path = require 'path'
|
path = require 'path'
|
||||||
{Lexer} = require './lexer'
|
{Lexer,RESERVED} = require './lexer'
|
||||||
{parser} = require './parser'
|
{parser} = require './parser'
|
||||||
|
|
||||||
# TODO: Remove registerExtension when fully deprecated.
|
# TODO: Remove registerExtension when fully deprecated.
|
||||||
if require.extensions
|
if require.extensions
|
||||||
|
@ -22,6 +22,9 @@ else if require.registerExtension
|
||||||
# The current CoffeeScript version number.
|
# The current CoffeeScript version number.
|
||||||
exports.VERSION = '0.9.6'
|
exports.VERSION = '0.9.6'
|
||||||
|
|
||||||
|
# Words that cannot be used as identifiers in CoffeeScript code
|
||||||
|
exports.RESERVED = RESERVED
|
||||||
|
|
||||||
# Expose helpers for testing.
|
# Expose helpers for testing.
|
||||||
exports.helpers = require './helpers'
|
exports.helpers = require './helpers'
|
||||||
|
|
||||||
|
|
|
@ -523,6 +523,8 @@ RESERVED = [
|
||||||
# be used as identifiers or properties.
|
# be used as identifiers or properties.
|
||||||
JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED
|
JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED
|
||||||
|
|
||||||
|
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS)
|
||||||
|
|
||||||
# Token matching regexes.
|
# Token matching regexes.
|
||||||
IDENTIFIER = /// ^
|
IDENTIFIER = /// ^
|
||||||
( [$A-Za-z_][$\w]* )
|
( [$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