Merge branch 'master' of https://github.com/jashkenas/coffee-script into keyboard

This commit is contained in:
Will Moffat 2011-03-19 15:28:50 -07:00
commit 4c2ce2b45b
26 changed files with 1143 additions and 999 deletions

View File

@ -436,9 +436,10 @@
stack.push(tok);
break;
case '(':
case 'CALL_START':
if (stack.length) {
stack.pop();
} else {
} else if (tok[0] === '(') {
tok[0] = 'PARAM_START';
return this;
}
@ -623,7 +624,7 @@
HEREDOC_INDENT = /\n+([^\n\S]*)/g;
HEREDOC_ILLEGAL = /\*\//;
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/;
LINE_CONTINUER = /^\s*(?:,|\??\.(?!\.)|::)/;
LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|::)/;
TRAILING_SPACES = /\s+$/;
NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/;
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];

View File

@ -1,5 +1,5 @@
(function() {
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, Obj, Op, Param, Parens, Push, Range, Return, SIMPLENUM, Scope, Slice, Splat, Switch, TAB, THIS, TRAILING_WHITESPACE, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref;
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, Obj, Op, Param, Parens, Push, Range, Return, SIMPLENUM, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
@ -41,7 +41,7 @@
}
};
Base.prototype.compileClosure = function(o) {
if (this.jumps()) {
if (this.jumps() || this instanceof Throw) {
throw SyntaxError('cannot use a pure statement in an expression.');
}
o.sharedScope = true;
@ -273,7 +273,6 @@
o.scope = new Scope(null, this, null);
o.level = LEVEL_TOP;
code = this.compileWithDeclarations(o);
code = code.replace(TRAILING_WHITESPACE, '');
if (o.bare) {
return code;
} else {
@ -1316,6 +1315,9 @@
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
param = _ref[_i];
if (param.splat) {
if (param.name.value) {
o.scope.add(param.name.value, 'var');
}
splats = new Assign(new Value(new Arr((function() {
var _i, _len, _ref, _results;
_ref = this.params;
@ -2160,7 +2162,7 @@
}
func = new Code([], Block.wrap([expressions]));
args = [];
if ((mentionsArgs = expressions.contains(this.literalArgs)) || (expressions.contains(this.literalThis))) {
if ((mentionsArgs = expressions.contains(this.literalArgs)) || expressions.contains(this.literalThis)) {
meth = new Literal(mentionsArgs ? 'apply' : 'call');
args = [new Literal('this')];
if (mentionsArgs) {
@ -2206,7 +2208,6 @@
LEVEL_OP = 5;
LEVEL_ACCESS = 6;
TAB = ' ';
TRAILING_WHITESPACE = /[ \t]+$/gm;
IDENTIFIER = /^[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*$/;
SIMPLENUM = /^[+-]?\d+$/;
IS_STRING = /^['"]/;

View File

@ -1,22 +1,27 @@
{
"name": "coffee-script",
"description": "Unfancy JavaScript",
"keywords": ["javascript", "language", "coffeescript", "compiler"],
"author": "Jeremy Ashkenas",
"version": "1.1.0-pre",
"licenses": [{
"type": "MIT",
"url": "http://github.com/jashkenas/coffee-script/raw/master/LICENSE"
}],
"engines": {
"node": ">=0.2.5"
},
"directories" : {
"lib" : "./lib"
},
"main" : "./lib/coffee-script",
"bin": {
"coffee": "./bin/coffee",
"cake": "./bin/cake"
}
"name": "coffee-script",
"description": "Unfancy JavaScript",
"keywords": ["javascript", "language", "coffeescript", "compiler"],
"author": "Jeremy Ashkenas",
"version": "1.1.0-pre",
"licenses": [{
"type": "MIT",
"url": "http://github.com/jashkenas/coffee-script/raw/master/LICENSE"
}],
"engines": {
"node": ">=0.2.5"
},
"directories" : {
"lib" : "./lib"
},
"main" : "./lib/coffee-script",
"bin": {
"coffee": "./bin/coffee",
"cake": "./bin/cake"
},
"homepage": "http://coffeescript.org",
"repository": {
"type": "git",
"url": "git://github.com/jashkenas/coffee-script.git"
}
}

View File

@ -369,9 +369,9 @@ exports.Lexer = class Lexer
switch tok[0]
when ')'
stack.push tok
when '('
when '(', 'CALL_START'
if stack.length then stack.pop()
else
else if tok[0] is '('
tok[0] = 'PARAM_START'
return this
this
@ -601,7 +601,7 @@ HEREDOC_ILLEGAL = /\*\//
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/
LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?!\.) | :: ) ///
LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
TRAILING_SPACES = /\s+$/

View File

@ -48,7 +48,7 @@ exports.Base = class Base
# Statements converted into expressions via closure-wrapping share a scope
# object with their parent closure, to preserve the expected lexical scope.
compileClosure: (o) ->
if @jumps()
if @jumps() or this instanceof Throw
throw SyntaxError 'cannot use a pure statement in an expression.'
o.sharedScope = yes
Closure.wrap(this).compileNode o
@ -233,7 +233,6 @@ exports.Block = class Block extends Base
o.scope = new Scope null, this, null
o.level = LEVEL_TOP
code = @compileWithDeclarations o
code = code.replace TRAILING_WHITESPACE, ''
if o.bare then code else "(function() {\n#{code}\n}).call(this);\n"
# Compile the expressions body for the contents of a function, with
@ -1055,6 +1054,7 @@ exports.Code = class Code extends Base
vars = []
exprs = []
for param in @params when param.splat
o.scope.add param.name.value, 'var' if param.name.value
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
new Value new Literal 'arguments'
break
@ -1698,8 +1698,7 @@ Closure =
return expressions if expressions.jumps()
func = new Code [], Block.wrap [expressions]
args = []
if (mentionsArgs = expressions.contains @literalArgs) or
( expressions.contains @literalThis)
if (mentionsArgs = expressions.contains @literalArgs) or expressions.contains @literalThis
meth = new Literal if mentionsArgs then 'apply' else 'call'
args = [new Literal 'this']
args.push new Literal 'arguments' if mentionsArgs
@ -1770,10 +1769,6 @@ LEVEL_ACCESS = 6 # ...[0]
# Tabs are two spaces for pretty printing.
TAB = ' '
# Trim out all trailing whitespace, so that the generated code plays nice
# with Git.
TRAILING_WHITESPACE = /[ \t]+$/gm
IDENTIFIER = /^[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*$/
SIMPLENUM = /^[+-]?\d+$/

View File

@ -4,30 +4,30 @@
# * Array Literals
# * Splats in Array Literals
# TODO: refactor array literal tests
# TODO: add indexing and method invocation tests: [1][0] is 1, [].toString()
trailingComma = [1, 2, 3,]
ok (trailingComma[0] is 1) and (trailingComma[2] is 3) and (trailingComma.length is 3)
test "trailing commas", ->
trailingComma = [1, 2, 3,]
ok (trailingComma[0] is 1) and (trailingComma[2] is 3) and (trailingComma.length is 3)
trailingComma = [
1, 2, 3,
4, 5, 6
7, 8, 9,
]
(sum = (sum or 0) + n) for n in trailingComma
trailingComma = [
1, 2, 3,
4, 5, 6
7, 8, 9,
]
(sum = (sum or 0) + n) for n in trailingComma
a = [((x) -> x), ((x) -> x * x)]
ok a.length is 2
a = [((x) -> x), ((x) -> x * x)]
ok a.length is 2
# Funky indentation within non-comma-seperated arrays.
result = [['a']
{b: 'c'}]
ok result[0][0] is 'a'
ok result[1]['b'] is 'c'
test "incorrect indentation without commas", ->
result = [['a']
{b: 'c'}]
ok result[0][0] is 'a'
ok result[1]['b'] is 'c'
#### Splats in Array Literals
# Splats in Array Literals
test "array splat expansions with assignments", ->
nums = [1, 2, 3]
@ -36,6 +36,7 @@ test "array splat expansions with assignments", ->
eq 4, b
arrayEq [0,1,2,3,4], list
test "mixed shorthand objects in array lists", ->
arr = [
@ -56,3 +57,16 @@ test "mixed shorthand objects in array lists", ->
eq arr.length, 4
eq arr[2].b, 1
eq arr[3], 'b'
test "array splats with nested arrays", ->
nonce = {}
a = [nonce]
list = [1, 2, a...]
eq list[0], 1
eq list[2], nonce
a = [[nonce]]
list = [1, 2, a...]
arrayEq list, [1, 2, [nonce]]

View File

@ -1,8 +1,6 @@
# Assignment
# ----------
# TODO: organize assignment file
# * Assignment
# * Compound Assignment
# * Destructuring Assignment
@ -27,7 +25,7 @@ test "compound assignments should not declare", ->
eq Math, (-> Math or= 0)()
#### Compound Assignment
# Compound Assignment
test "boolean operators", ->
nonce = {}
@ -136,7 +134,7 @@ test "more compound assignment", ->
eq c, val
#### Destructuring Assignment
# Destructuring Assignment
test "empty destructuring assignment", ->
{} = [] = undefined
@ -251,7 +249,7 @@ test "#1024", ->
eq 2 * [] = 3 + 5, 16
#### Existential Assignment
# Existential Assignment
test "existential assignment", ->
nonce = {}

View File

@ -3,19 +3,19 @@
# TODO: add method invocation tests: true.toString() is "true"
#764: Boolean should be indexable
toString = Boolean::toString
test "#764 Booleans should be indexable", ->
toString = Boolean::toString
eq toString, true['toString']
eq toString, false['toString']
eq toString, yes['toString']
eq toString, no['toString']
eq toString, on['toString']
eq toString, off['toString']
eq toString, true['toString']
eq toString, false['toString']
eq toString, yes['toString']
eq toString, no['toString']
eq toString, on['toString']
eq toString, off['toString']
eq toString, true.toString
eq toString, false.toString
eq toString, yes.toString
eq toString, no.toString
eq toString, on.toString
eq toString, off.toString
eq toString, true.toString
eq toString, false.toString
eq toString, yes.toString
eq toString, no.toString
eq toString, on.toString
eq toString, off.toString

View File

@ -1,4 +0,0 @@
# Cake
# ----
# TODO: add tests

View File

@ -5,113 +5,119 @@
# * Class Instantiation
# * Inheritance and Super
# TODO: refactor class tests
test "classes with a four-level inheritance chain", ->
# Test classes with a four-level inheritance chain.
class Base
func: (string) ->
"zero/#{string}"
class Base
func: (string) ->
"zero/#{string}"
@static: (string) ->
"static/#{string}"
@static: (string) ->
"static/#{string}"
class FirstChild extends Base
func: (string) ->
class FirstChild extends Base
func: (string) ->
super('one/') + string
SecondChild = class extends FirstChild
func: (string) ->
super('two/') + string
thirdCtor = ->
@array = [1, 2, 3]
class ThirdChild extends SecondChild
constructor: -> thirdCtor.call this
# Gratuitous comment for testing.
func: (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok Base.static('word') is 'static/word'
FirstChild::func = (string) ->
super('one/').length + string
result = (new ThirdChild).func 'four'
ok result is '9two/three/four'
ok (new ThirdChild).array.join(' ') is '1 2 3'
test "constructors with inheritance and super", ->
identity = (f) -> f
class TopClass
constructor: (arg) ->
@prop = 'top-' + arg
class SuperClass extends TopClass
constructor: (arg) ->
identity super 'super-' + arg
class SubClass extends SuperClass
constructor: ->
identity super 'sub'
ok (new SubClass).prop is 'top-super-sub'
test "Overriding the static property new doesn't clobber Function::new", ->
class OneClass
@new: 'new'
function: 'function'
constructor: (name) -> @name = name
class TwoClass extends OneClass
delete TwoClass.new
Function.prototype.new = -> new this arguments...
ok (TwoClass.new('three')).name is 'three'
ok (new OneClass).function is 'function'
ok OneClass.new is 'new'
delete Function.prototype.new
test "basic classes, again, but in the manual prototype style", ->
Base = ->
Base::func = (string) ->
'zero/' + string
Base::['func-func'] = (string) ->
"dynamic-#{string}"
FirstChild = ->
SecondChild = ->
ThirdChild = ->
@array = [1, 2, 3]
this
ThirdChild extends SecondChild extends FirstChild extends Base
FirstChild::func = (string) ->
super('one/') + string
SecondChild = class extends FirstChild
func: (string) ->
SecondChild::func = (string) ->
super('two/') + string
thirdCtor = ->
@array = [1, 2, 3]
class ThirdChild extends SecondChild
constructor: -> thirdCtor.call this
# Gratuitous comment for testing.
func: (string) ->
ThirdChild::func = (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok Base.static('word') is 'static/word'
ok result is 'zero/one/two/three/four'
FirstChild::func = (string) ->
super('one/').length + string
result = (new ThirdChild).func 'four'
ok result is '9two/three/four'
ok (new ThirdChild).array.join(' ') is '1 2 3'
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
identity = (f) -> f
class TopClass
constructor: (arg) ->
@prop = 'top-' + arg
class SuperClass extends TopClass
constructor: (arg) ->
identity super 'super-' + arg
class SubClass extends SuperClass
constructor: ->
identity super 'sub'
ok (new SubClass).prop is 'top-super-sub'
class OneClass
@new: 'new'
function: 'function'
constructor: (name) -> @name = name
class TwoClass extends OneClass
delete TwoClass.new
Function.prototype.new = -> new this arguments...
ok (TwoClass.new('three')).name is 'three'
ok (new OneClass).function is 'function'
ok OneClass.new is 'new'
delete Function.prototype.new
# And now the same tests, but written in the manual style:
Base = ->
Base::func = (string) ->
'zero/' + string
Base::['func-func'] = (string) ->
"dynamic-#{string}"
FirstChild = ->
SecondChild = ->
ThirdChild = ->
@array = [1, 2, 3]
this
ThirdChild extends SecondChild extends FirstChild extends Base
FirstChild::func = (string) ->
super('one/') + string
SecondChild::func = (string) ->
super('two/') + string
ThirdChild::func = (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
test "super with plain ol' functions as the original constructors", ->
TopClass = (arg) ->
@prop = 'top-' + arg
@ -131,283 +137,302 @@ SubClass extends SuperClass
ok (new SubClass).prop is 'top-super-sub'
# '@' referring to the current instance, and not being coerced into a call.
class ClassName
amI: ->
@ instanceof ClassName
test "'@' referring to the current instance, and not being coerced into a call", ->
obj = new ClassName
ok obj.amI()
class ClassName
amI: ->
@ instanceof ClassName
obj = new ClassName
ok obj.amI()
# super() calls in constructors of classes that are defined as object properties.
class Hive
constructor: (name) -> @name = name
test "super() calls in constructors of classes that are defined as object properties", ->
class Hive.Bee extends Hive
constructor: (name) -> super
class Hive
constructor: (name) -> @name = name
maya = new Hive.Bee 'Maya'
ok maya.name is 'Maya'
class Hive.Bee extends Hive
constructor: (name) -> super
maya = new Hive.Bee 'Maya'
ok maya.name is 'Maya'
# Class with JS-keyword properties.
class Class
class: 'class'
name: -> @class
test "classes with JS-keyword properties", ->
instance = new Class
ok instance.class is 'class'
ok instance.name() is 'class'
class Class
class: 'class'
name: -> @class
instance = new Class
ok instance.class is 'class'
ok instance.name() is 'class'
# Classes with methods that are pre-bound to the instance.
# ... or statically, to the class.
class Dog
test "Classes with methods that are pre-bound to the instance, or statically, to the class", ->
constructor: (name) ->
@name = name
class Dog
constructor: (name) ->
@name = name
bark: =>
"#{@name} woofs!"
bark: =>
"#{@name} woofs!"
@static = =>
new this('Dog')
@static = =>
new this('Dog')
spark = new Dog('Spark')
fido = new Dog('Fido')
fido.bark = spark.bark
spark = new Dog('Spark')
fido = new Dog('Fido')
fido.bark = spark.bark
ok fido.bark() is 'Spark woofs!'
ok fido.bark() is 'Spark woofs!'
obj = func: Dog.static
obj = func: Dog.static
ok obj.func().name is 'Dog'
ok obj.func().name is 'Dog'
# Testing a bound function in a bound function.
class Mini
num: 10
generate: =>
for i in [1..3]
=>
@num
test "a bound function in a bound function", ->
m = new Mini
eq (func() for func in m.generate()).join(' '), '10 10 10'
class Mini
num: 10
generate: =>
for i in [1..3]
=>
@num
m = new Mini
eq (func() for func in m.generate()).join(' '), '10 10 10'
# Testing a contructor called with varargs.
class Connection
constructor: (one, two, three) ->
[@one, @two, @three] = [one, two, three]
test "contructor called with varargs", ->
out: ->
"#{@one}-#{@two}-#{@three}"
class Connection
constructor: (one, two, three) ->
[@one, @two, @three] = [one, two, three]
list = [3, 2, 1]
conn = new Connection list...
ok conn instanceof Connection
ok conn.out() is '3-2-1'
out: ->
"#{@one}-#{@two}-#{@three}"
list = [3, 2, 1]
conn = new Connection list...
ok conn instanceof Connection
ok conn.out() is '3-2-1'
# Test calling super and passing along all arguments.
class Parent
method: (args...) -> @args = args
test "calling super and passing along all arguments", ->
class Child extends Parent
method: -> super
class Parent
method: (args...) -> @args = args
c = new Child
c.method 1, 2, 3, 4
ok c.args.join(' ') is '1 2 3 4'
class Child extends Parent
method: -> super
c = new Child
c.method 1, 2, 3, 4
ok c.args.join(' ') is '1 2 3 4'
# Test classes wrapped in decorators.
func = (klass) ->
klass::prop = 'value'
klass
test "classes wrapped in decorators", ->
func class Test
prop2: 'value2'
func = (klass) ->
klass::prop = 'value'
klass
ok (new Test).prop is 'value'
ok (new Test).prop2 is 'value2'
func class Test
prop2: 'value2'
ok (new Test).prop is 'value'
ok (new Test).prop2 is 'value2'
# Test anonymous classes.
obj =
klass: class
method: -> 'value'
test "anonymous classes", ->
instance = new obj.klass
ok instance.method() is 'value'
obj =
klass: class
method: -> 'value'
instance = new obj.klass
ok instance.method() is 'value'
# Implicit objects as static properties.
class Static
@static =
one: 1
two: 2
test "Implicit objects as static properties", ->
ok Static.static.one is 1
ok Static.static.two is 2
class Static
@static =
one: 1
two: 2
ok Static.static.one is 1
ok Static.static.two is 2
# Nothing classes.
c = class
ok c instanceof Function
test "nothing classes", ->
c = class
ok c instanceof Function
# Classes with value'd constructors.
counter = 0
classMaker = ->
counter++
inner = counter
->
@value = inner
test "classes with value'd constructors", ->
class One
constructor: classMaker()
counter = 0
classMaker = ->
counter++
inner = counter
->
@value = inner
class Two
constructor: classMaker()
class One
constructor: classMaker()
ok (new One).value is 1
ok (new Two).value is 2
ok (new One).value is 1
ok (new Two).value is 2
class Two
constructor: classMaker()
ok (new One).value is 1
ok (new Two).value is 2
ok (new One).value is 1
ok (new Two).value is 2
# Exectuable class bodies.
class A
if true
b: 'b'
else
c: 'c'
test "exectuable class bodies", ->
a = new A
class A
if true
b: 'b'
else
c: 'c'
eq a.b, 'b'
eq a.c, undefined
a = new A
eq a.b, 'b'
eq a.c, undefined
# Light metaprogramming.
class Base
@attr: (name) ->
@::[name] = (val) ->
if arguments.length > 0
@["_#{name}"] = val
else
@["_#{name}"]
test "mild metaprogramming", ->
class Robot extends Base
@attr 'power'
@attr 'speed'
class Base
@attr: (name) ->
@::[name] = (val) ->
if arguments.length > 0
@["_#{name}"] = val
else
@["_#{name}"]
robby = new Robot
class Robot extends Base
@attr 'power'
@attr 'speed'
ok robby.power() is undefined
robby = new Robot
robby.power 11
robby.speed Infinity
ok robby.power() is undefined
eq robby.power(), 11
eq robby.speed(), Infinity
robby.power 11
robby.speed Infinity
eq robby.power(), 11
eq robby.speed(), Infinity
# Namespaced classes do not reserve their function name in outside scope.
one = {}
two = {}
test "namespaced classes do not reserve their function name in outside scope", ->
class one.Klass
@label = "one"
one = {}
two = {}
class two.Klass
@label = "two"
class one.Klass
@label = "one"
eq typeof Klass, 'undefined'
eq one.Klass.label, 'one'
eq two.Klass.label, 'two'
class two.Klass
@label = "two"
eq typeof Klass, 'undefined'
eq one.Klass.label, 'one'
eq two.Klass.label, 'two'
# Nested classes.
class Outer
constructor: ->
@label = 'outer'
test "nested classes", ->
class @Inner
class Outer
constructor: ->
@label = 'inner'
@label = 'outer'
eq (new Outer).label, 'outer'
eq (new Outer.Inner).label, 'inner'
class @Inner
constructor: ->
@label = 'inner'
eq (new Outer).label, 'outer'
eq (new Outer.Inner).label, 'inner'
# Variables in constructor bodies are correctly scoped.
class A
x = 1
constructor: ->
x = 10
y = 20
y = 2
captured: ->
{x, y}
test "variables in constructor bodies are correctly scoped", ->
a = new A
eq a.captured().x, 10
eq a.captured().y, 2
class A
x = 1
constructor: ->
x = 10
y = 20
y = 2
captured: ->
{x, y}
a = new A
eq a.captured().x, 10
eq a.captured().y, 2
# Issue #924: Static methods in nested classes.
class A
@B: class
@c = -> 5
test "Issue #924: Static methods in nested classes", ->
eq A.B.c(), 5
class A
@B: class
@c = -> 5
eq A.B.c(), 5
# `class extends this` ...
class A
func: -> 'A'
test "`class extends this`", ->
B = null
makeClass = ->
B = class extends this
func: -> super + ' B'
class A
func: -> 'A'
makeClass.call A
B = null
makeClass = ->
B = class extends this
func: -> super + ' B'
eq (new B()).func(), 'A B'
makeClass.call A
eq (new B()).func(), 'A B'
test "ensure that constructors invoked with splats return a new object", ->
args = [1, 2, 3]
Type = (@args) ->
type = new Type args
ok type and type instanceof Type
ok type.args and type.args instanceof Array
ok v is args[i] for v, i in type.args
Type1 = (@a, @b, @c) ->
type1 = new Type1 args...
ok type1 instanceof Type1
eq type1.constructor, Type1
ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2]
# Ensure that constructors invoked with splats cache the function.
called = 0
get = -> if called++ then false else class Type
new get() args...
test "`new` shouldn't add extra parens", ->
ok new Date().constructor is Date
# Ensure that constructors invoked with splats return a new object.
args = [1, 2, 3]
Type = (@args) ->
type = new Type args
test "`new` works against bare function", ->
ok type and type instanceof Type
ok type.args and type.args instanceof Array
ok v is args[i] for v, i in type.args
Type1 = (@a, @b, @c) ->
type1 = new Type1 args...
ok type1 instanceof Type1
eq type1.constructor, Type1
ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2]
# Ensure that constructors invoked with splats cache the function.
called = 0
get = -> if called++ then false else class Type
new get() args...
# `new` shouldn't add extra parens
ok new Date().constructor is Date
# `new` works against bare function
eq Date, new ->
eq this, new => this
Date
eq Date, new ->
eq this, new => this
Date

View File

@ -1,4 +0,0 @@
# Command
# -------
# TODO: add tests

View File

@ -110,7 +110,7 @@ test "spaced comments with conditional statements", ->
eq nonce, result
#### Block Comments
# Block Comments
###
This is a here-comment.

View File

@ -1,47 +1,43 @@
# Compilation
# -----------
# TODO: refactor compilation tests
# helper to assert that a string should fail compilation
cantCompile = (code) ->
throws -> CoffeeScript.compile code
# Ensure that carriage returns don't break compilation on Windows.
doesNotThrow -> CoffeeScript.compile 'one\r\ntwo', bare: on
test "ensure that carriage returns don't break compilation on Windows", ->
doesNotThrow -> CoffeeScript.compile 'one\r\ntwo', bare: on
# `globals: on` removes `var`s
eq -1, CoffeeScript.compile('x = y', bare: on, globals: on).indexOf 'var'
test "--bare and globals:on", ->
eq -1, CoffeeScript.compile('x = y', bare: on, globals: on).indexOf 'var'
ok 'passed' is CoffeeScript.eval '"passed"', bare: on, filename: 'test'
ok 'passed' is CoffeeScript.eval '"passed"', bare: on, filename: 'test'
# multiple generated references
(->
test "multiple generated references", ->
a = {b: []}
a.b[true] = -> this == a.b
c = 0
d = []
ok a.b[0<++c<2] d...
)()
# Splat on a line by itself is invalid.
cantCompile "x 'a'\n...\n"
test "splat on a line by itself is invalid", ->
cantCompile "x 'a'\n...\n"
#750
cantCompile 'f(->'
test "Issue 750", ->
cantCompile 'a = (break)'
cantCompile 'f(->'
cantCompile 'a = (return 5 for item in list)'
cantCompile 'a = (break)'
cantCompile 'a = (return 5 while condition)'
cantCompile 'a = (return 5 for item in list)'
cantCompile 'a = for x in y\n return 5'
cantCompile 'a = (return 5 while condition)'
# Issue #986: Unicode identifiers.
λ = 5
eq λ, 5
cantCompile 'a = for x in y\n return 5'
test "Issue #986: Unicode identifiers", ->
λ = 5
eq λ, 5
test "don't accidentally stringify keywords", ->
ok (-> this == 'this')() is false
@ -57,4 +53,4 @@ test "#1026", ->
'''
test "#1050", ->
cantCompile "### */ ###"
cantCompile "### */ ###"

View File

@ -9,323 +9,357 @@
# TODO: refactor comprehension tests
# Basic array comprehensions.
nums = (n * n for n in [1, 2, 3] when n & 1)
results = (n * 2 for n in nums)
test "Basic array comprehensions.", ->
ok results.join(',') is '2,18'
nums = (n * n for n in [1, 2, 3] when n & 1)
results = (n * 2 for n in nums)
ok results.join(',') is '2,18'
# Basic object comprehensions.
obj = {one: 1, two: 2, three: 3}
names = (prop + '!' for prop of obj)
odds = (prop + '!' for prop, value of obj when value & 1)
test "Basic object comprehensions.", ->
ok names.join(' ') is "one! two! three!"
ok odds.join(' ') is "one! three!"
obj = {one: 1, two: 2, three: 3}
names = (prop + '!' for prop of obj)
odds = (prop + '!' for prop, value of obj when value & 1)
ok names.join(' ') is "one! two! three!"
ok odds.join(' ') is "one! three!"
# Basic range comprehensions.
nums = (i * 3 for i in [1..3])
test "Basic range comprehensions.", ->
negs = (x for x in [-20..-5*2])
negs = negs[0..2]
nums = (i * 3 for i in [1..3])
result = nums.concat(negs).join(', ')
negs = (x for x in [-20..-5*2])
negs = negs[0..2]
ok result is '3, 6, 9, -20, -19, -18'
result = nums.concat(negs).join(', ')
ok result is '3, 6, 9, -20, -19, -18'
# With range comprehensions, you can loop in steps.
results = (x for x in [0...15] by 5)
ok results.join(' ') is '0 5 10'
test "With range comprehensions, you can loop in steps.", ->
results = (x for x in [0..100] by 10)
ok results.join(' ') is '0 10 20 30 40 50 60 70 80 90 100'
results = (x for x in [0...15] by 5)
ok results.join(' ') is '0 5 10'
results = (x for x in [0..100] by 10)
ok results.join(' ') is '0 10 20 30 40 50 60 70 80 90 100'
# And can loop downwards, with a negative step.
results = (x for x in [5..1])
test "And can loop downwards, with a negative step.", ->
ok results.join(' ') is '5 4 3 2 1'
ok results.join(' ') is [(10-5)..(-2+3)].join(' ')
results = (x for x in [5..1])
results = (x for x in [10..1])
ok results.join(' ') is [10..1].join(' ')
ok results.join(' ') is '5 4 3 2 1'
ok results.join(' ') is [(10-5)..(-2+3)].join(' ')
results = (x for x in [10...0] by -2)
ok results.join(' ') is [10, 8, 6, 4, 2].join(' ')
results = (x for x in [10..1])
ok results.join(' ') is [10..1].join(' ')
results = (x for x in [10...0] by -2)
ok results.join(' ') is [10, 8, 6, 4, 2].join(' ')
# Range comprehension gymnastics.
eq "#{i for i in [5..1]}", '5,4,3,2,1'
eq "#{i for i in [5..-5] by -5}", '5,0,-5'
test "Range comprehension gymnastics.", ->
a = 6
b = 0
c = -2
eq "#{i for i in [5..1]}", '5,4,3,2,1'
eq "#{i for i in [5..-5] by -5}", '5,0,-5'
eq "#{i for i in [a..b]}", '6,5,4,3,2,1,0'
eq "#{i for i in [a..b] by c}", '6,4,2,0'
a = 6
b = 0
c = -2
eq "#{i for i in [a..b]}", '6,5,4,3,2,1,0'
eq "#{i for i in [a..b] by c}", '6,4,2,0'
# Multiline array comprehension with filter.
evens = for num in [1, 2, 3, 4, 5, 6] when not (num & 1)
num *= -1
num -= 2
num * -1
eq evens + '', '4,6,8'
test "Multiline array comprehension with filter.", ->
evens = for num in [1, 2, 3, 4, 5, 6] when not (num & 1)
num *= -1
num -= 2
num * -1
eq evens + '', '4,6,8'
# The in operator still works, standalone.
ok 2 of evens
test "The in operator still works, standalone.", ->
# all isn't reserved.
all = 1
ok 2 of evens
# Ensure that the closure wrapper preserves local variables.
obj = {}
test "all isn't reserved.", ->
for method in ['one', 'two', 'three'] then do (method) ->
obj[method] = ->
"I'm " + method
ok obj.one() is "I'm one"
ok obj.two() is "I'm two"
ok obj.three() is "I'm three"
all = 1
# Index values at the end of a loop.
i = 0
for i in [1..3]
-> 'func'
break if false
ok i is 4
test "Ensure that the closure wrapper preserves local variables.", ->
obj = {}
for method in ['one', 'two', 'three'] then do (method) ->
obj[method] = ->
"I'm " + method
ok obj.one() is "I'm one"
ok obj.two() is "I'm two"
ok obj.three() is "I'm three"
# Ensure that local variables are closed over for range comprehensions.
funcs = for i in [1..3]
do (i) ->
-> -i
test "Index values at the end of a loop.", ->
eq (func() for func in funcs).join(' '), '-1 -2 -3'
ok i is 4
i = 0
for i in [1..3]
-> 'func'
break if false
ok i is 4
# Even when referenced in the filter.
list = ['one', 'two', 'three']
test "Ensure that local variables are closed over for range comprehensions.", ->
methods = for num, i in list when num isnt 'two' and i isnt 1
do (num, i) ->
-> num + ' ' + i
ok methods.length is 2
ok methods[0]() is 'one 0'
ok methods[1]() is 'three 2'
# Even a convoluted one.
funcs = []
for i in [1..3]
do (i) ->
x = i * 2
((z)->
funcs.push -> z + ' ' + i
)(x)
ok (func() for func in funcs).join(', ') is '2 1, 4 2, 6 3'
funcs = []
results = for i in [1..3]
do (i) ->
z = (x * 3 for x in [1..i])
((a, b, c) -> [a, b, c].join(' ')).apply this, z
ok results.join(', ') is '3 , 3 6 , 3 6 9'
# Naked ranges are expanded into arrays.
array = [0..10]
ok(num % 2 is 0 for num in array by 2)
# Nested shared scopes.
foo = ->
for i in [0..7]
funcs = for i in [1..3]
do (i) ->
for j in [0..7]
do (j) ->
-> i + j
-> -i
eq foo()[3][4](), 7
eq (func() for func in funcs).join(' '), '-1 -2 -3'
ok i is 4
# Scoped loop pattern matching.
a = [[0], [1]]
funcs = []
test "Even when referenced in the filter.", ->
for [v] in a
do (v) ->
funcs.push -> v
list = ['one', 'two', 'three']
eq funcs[0](), 0
eq funcs[1](), 1
methods = for num, i in list when num isnt 'two' and i isnt 1
do (num, i) ->
-> num + ' ' + i
ok methods.length is 2
ok methods[0]() is 'one 0'
ok methods[1]() is 'three 2'
# Nested comprehensions.
multiLiner =
for x in [3..5]
for y in [3..5]
[x, y]
test "Even a convoluted one.", ->
singleLiner =
(([x, y] for y in [3..5]) for x in [3..5])
funcs = []
ok multiLiner.length is singleLiner.length
ok 5 is multiLiner[2][2][1]
ok 5 is singleLiner[2][2][1]
for i in [1..3]
do (i) ->
x = i * 2
((z)->
funcs.push -> z + ' ' + i
)(x)
ok (func() for func in funcs).join(', ') is '2 1, 4 2, 6 3'
funcs = []
results = for i in [1..3]
do (i) ->
z = (x * 3 for x in [1..i])
((a, b, c) -> [a, b, c].join(' ')).apply this, z
ok results.join(', ') is '3 , 3 6 , 3 6 9'
# Comprehensions within parentheses.
result = null
store = (obj) -> result = obj
store (x * 2 for x in [3, 2, 1])
test "Naked ranges are expanded into arrays.", ->
ok result.join(' ') is '6 4 2'
array = [0..10]
ok(num % 2 is 0 for num in array by 2)
# Closure-wrapped comprehensions that refer to the "arguments" object.
expr = ->
result = (item * item for item in arguments)
test "Nested shared scopes.", ->
ok expr(2, 4, 8).join(' ') is '4 16 64'
foo = ->
for i in [0..7]
do (i) ->
for j in [0..7]
do (j) ->
-> i + j
eq foo()[3][4](), 7
# Fast object comprehensions over all properties, including prototypal ones.
class Cat
constructor: -> @name = 'Whiskers'
breed: 'tabby'
hair: 'cream'
test "Scoped loop pattern matching.", ->
whiskers = new Cat
own = (value for own key, value of whiskers)
all = (value for key, value of whiskers)
a = [[0], [1]]
funcs = []
ok own.join(' ') is 'Whiskers'
ok all.sort().join(' ') is 'Whiskers cream tabby'
for [v] in a
do (v) ->
funcs.push -> v
eq funcs[0](), 0
eq funcs[1](), 1
# Optimized range comprehensions.
exxes = ('x' for [0...10])
ok exxes.join(' ') is 'x x x x x x x x x x'
test "Nested comprehensions.", ->
multiLiner =
for x in [3..5]
for y in [3..5]
[x, y]
singleLiner =
(([x, y] for y in [3..5]) for x in [3..5])
ok multiLiner.length is singleLiner.length
ok 5 is multiLiner[2][2][1]
ok 5 is singleLiner[2][2][1]
# Comprehensions safely redeclare parameters if they're not present in closest
# scope.
rule = (x) -> x
test "Comprehensions within parentheses.", ->
learn = ->
rule for rule in [1, 2, 3]
result = null
store = (obj) -> result = obj
store (x * 2 for x in [3, 2, 1])
ok learn().join(' ') is '1 2 3'
ok rule(101) is 101
f = -> [-> ok no, 'should cache source']
ok yes for k of [f] = f()
ok result.join(' ') is '6 4 2'
# Lenient on pure statements not trying to reach out of the closure
val = for i in [1]
for j in [] then break
i
ok val[0] is i
test "Closure-wrapped comprehensions that refer to the 'arguments' object.", ->
expr = ->
result = (item * item for item in arguments)
ok expr(2, 4, 8).join(' ') is '4 16 64'
# Comprehensions only wrap their last line in a closure, allowing other lines
# to have pure expressions in them.
func = -> for i in [1]
break if i is 2
j for j in [1]
test "Fast object comprehensions over all properties, including prototypal ones.", ->
ok func()[0][0] is 1
class Cat
constructor: -> @name = 'Whiskers'
breed: 'tabby'
hair: 'cream'
i = 6
odds = while i--
continue unless i & 1
i
whiskers = new Cat
own = (value for own key, value of whiskers)
all = (value for key, value of whiskers)
ok odds.join(', ') is '5, 3, 1'
ok own.join(' ') is 'Whiskers'
ok all.sort().join(' ') is 'Whiskers cream tabby'
# Issue #897: Ensure that plucked function variables aren't leaked.
facets = {}
list = ['one', 'two']
test "Optimized range comprehensions.", ->
(->
for entity in list
facets[entity] = -> entity
)()
eq typeof entity, 'undefined'
eq facets['two'](), 'two'
exxes = ('x' for [0...10])
ok exxes.join(' ') is 'x x x x x x x x x x'
# Issue #905. Soaks as the for loop subject.
a = {b: {c: [1, 2, 3]}}
for d in a.b?.c
e = d
test "Comprehensions safely redeclare parameters if they're not present in closest scope.", ->
eq e, 3
rule = (x) -> x
learn = ->
rule for rule in [1, 2, 3]
ok learn().join(' ') is '1 2 3'
ok rule(101) is 101
f = -> [-> ok no, 'should cache source']
ok yes for k of [f] = f()
# Issue #948. Capturing loop variables.
funcs = []
list = ->
[1, 2, 3]
test "Lenient on pure statements not trying to reach out of the closure", ->
for y in list()
do (y) ->
z = y
funcs.push -> "y is #{y} and z is #{z}"
eq funcs[1](), "y is 2 and z is 2"
val = for i in [1]
for j in [] then break
i
ok val[0] is i
# Cancel the comprehension if there's a jump inside the loop.
result = try
for i in [0...10]
continue if i < 5
i
test "Comprehensions only wrap their last line in a closure, allowing other lines
to have pure expressions in them.", ->
eq result, 10
func = -> for i in [1]
break if i is 2
j for j in [1]
ok func()[0][0] is 1
i = 6
odds = while i--
continue unless i & 1
i
ok odds.join(', ') is '5, 3, 1'
# Comprehensions over break.
arrayEq (break for [1..10]), []
test "Issue #897: Ensure that plucked function variables aren't leaked.", ->
# Comprehensions over continue.
arrayEq (break for [1..10]), []
facets = {}
list = ['one', 'two']
(->
for entity in list
facets[entity] = -> entity
)()
eq typeof entity, 'undefined'
eq facets['two'](), 'two'
# Comprehensions over function literals.
a = 0
for f in [-> a = 1]
do (f) ->
do f
test "Issue #905. Soaks as the for loop subject.", ->
eq a, 1
a = {b: {c: [1, 2, 3]}}
for d in a.b?.c
e = d
eq e, 3
# Comprehensions that mention arguments.
list = [arguments: 10]
args = for f in list
do (f) ->
f.arguments
eq args[0], 10
test "Issue #948. Capturing loop variables.", ->
funcs = []
list = ->
[1, 2, 3]
for y in list()
do (y) ->
z = y
funcs.push -> "y is #{y} and z is #{z}"
eq funcs[1](), "y is 2 and z is 2"
test "Cancel the comprehension if there's a jump inside the loop.", ->
result = try
for i in [0...10]
continue if i < 5
i
eq result, 10
test "Comprehensions over break.", ->
arrayEq (break for [1..10]), []
test "Comprehensions over continue.", ->
arrayEq (continue for [1..10]), []
test "Comprehensions over function literals.", ->
a = 0
for f in [-> a = 1]
do (f) ->
do f
eq a, 1
test "Comprehensions that mention arguments.", ->
list = [arguments: 10]
args = for f in list
do (f) ->
f.arguments
eq args[0], 10
test "expression conversion under explicit returns", ->
@ -341,8 +375,6 @@ test "expression conversion under explicit returns", ->
arrayEq [nonce,nonce,nonce], fn()
#### Implicit Destructuring Assignment
test "implicit destructuring assignment in object of objects", ->
a={}; b={}; c={}
obj = {
@ -353,6 +385,7 @@ test "implicit destructuring assignment in object of objects", ->
result = ([y,z] for y, { d: z } of obj)
arrayEq [['a',a],['b',b],['c',c]], result
test "implicit destructuring assignment in array of objects", ->
a={}; b={}; c={}; d={}; e={}; f={}
arr = [
@ -363,6 +396,7 @@ test "implicit destructuring assignment in array of objects", ->
result = ([y,z] for { a: y, b: { c: z } } in arr)
arrayEq [[a,b],[c,d],[e,f]], result
test "implicit destructuring assignment in array of arrays", ->
a={}; b={}; c={}; d={}; e={}; f={}
arr = [[a, [b]], [c, [d]], [e, [f]]]

View File

@ -14,7 +14,7 @@
# shared identity function
id = (_) -> if arguments.length is 1 then _ else Array::slice.call(arguments)
#### Conditionals
# Conditionals
test "basic conditionals", ->
if false
@ -198,38 +198,38 @@ test "#748: trailing reserved identifiers", ->
eq nonce, result
#### For / While / Until / Loop
test "basic `while` loops", ->
# TODO: refactor while tests
i = 5
list = while i -= 1
i * 2
ok list.join(' ') is "8 6 4 2"
# While
i = 5
list = (i * 3 while i -= 1)
ok list.join(' ') is "12 9 6 3"
i = 5
list = while i -= 1
i * 2
ok list.join(' ') is "8 6 4 2"
i = 5
func = (num) -> i -= num
assert = -> ok i < 5 > 0
results = while func 1
assert()
i
ok results.join(' ') is '4 3 2 1'
i = 5
list = (i * 3 while i -= 1)
ok list.join(' ') is "12 9 6 3"
i = 10
results = while i -= 1 when i % 2 is 0
i * 2
ok results.join(' ') is '16 12 8 4'
i = 5
func = (num) -> i -= num
assert = -> ok i < 5 > 0
results = while func 1
assert()
i
ok results.join(' ') is '4 3 2 1'
i = 10
results = while i -= 1 when i % 2 is 0
i * 2
ok results.join(' ') is '16 12 8 4'
test "Issue 759: `if` within `while` condition", ->
2 while if 1 then 0
#759: `if` within `while` condition
2 while if 1 then 0
test "assignment inside the condition of a `while` loop", ->
nonce = {}
count = 1
a = nonce while count--
@ -239,47 +239,45 @@ test "assignment inside the condition of a `while` loop", ->
b = nonce
eq nonce, b
# While over break.
i = 0
result = while i < 10
i++
break
arrayEq result, []
# While over continue.
i = 0
result = while i < 10
i++
continue
arrayEq result, []
test "While over break.", ->
# Until
i = 0
result = while i < 10
i++
break
arrayEq result, []
# TODO: refactor until tests
# TODO: add until tests
value = false
i = 0
results = until value
value = true if i is 5
i++
ok i is 6
test "While over continue.", ->
# Loop
i = 0
result = while i < 10
i++
continue
arrayEq result, []
# TODO: refactor loop tests
# TODO: add loop tests
i = 5
list = []
loop
i -= 1
break if i is 0
list.push i * 2
ok list.join(' ') is '8 6 4 2'
test "Basic `until`", ->
value = false
i = 0
results = until value
value = true if i is 5
i++
ok i is 6
test "Basic `loop`", ->
i = 5
list = []
loop
i -= 1
break if i is 0
list.push i * 2
ok list.join(' ') is '8 6 4 2'
# TODO: refactor for tests
# TODO: add for tests
test "break at the top level", ->
for i in [1,2,3]
@ -289,7 +287,7 @@ test "break at the top level", ->
eq 2, result
test "break *not* at the top level", ->
someFunc = () ->
someFunc = ->
i = 0
while ++i < 3
result = i
@ -298,121 +296,125 @@ test "break *not* at the top level", ->
eq 2, someFunc()
#### Switch
test "basic `switch`", ->
# TODO: refactor switch tests
num = 10
result = switch num
when 5 then false
when 'a'
true
true
false
when 10 then true
# Mid-switch comment with whitespace
# and multi line
when 11 then false
else false
ok result
func = (num) ->
switch num
when 2, 4, 6
num = 10
result = switch num
when 5 then false
when 'a'
true
true
when 1, 3, 5
false
ok func(2)
ok func(6)
ok !func(3)
eq func(8), undefined
when 10 then true
# Ensure that trailing switch elses don't get rewritten.
result = false
switch "word"
when "one thing"
doSomething()
else
result = true unless false
# Mid-switch comment with whitespace
# and multi line
when 11 then false
else false
ok result
result = false
switch "word"
when "one thing"
doSomething()
when "other thing"
doSomething()
else
result = true unless false
ok result
ok result
# Should be able to handle switches sans-condition.
result = switch
when null then 0
when !1 then 1
when '' not of {''} then 2
when [] not instanceof Array then 3
when true is false then 4
when 'x' < 'y' > 'z' then 5
when 'a' in ['b', 'c'] then 6
when 'd' in (['e', 'f']) then 7
else ok
func = (num) ->
switch num
when 2, 4, 6
true
when 1, 3, 5
false
eq result, ok
ok func(2)
ok func(6)
ok !func(3)
eq func(8), undefined
# Should be able to use "@properties" within the switch clause.
obj = {
num: 101
func: ->
switch @num
when 101 then '101!'
else 'other'
}
test "Ensure that trailing switch elses don't get rewritten.", ->
ok obj.func() is '101!'
result = false
switch "word"
when "one thing"
doSomething()
else
result = true unless false
ok result
result = false
switch "word"
when "one thing"
doSomething()
when "other thing"
doSomething()
else
result = true unless false
ok result
# Should be able to use "@properties" within the switch cases.
obj = {
num: 101
func: (yesOrNo) ->
result = switch yesOrNo
when yes then @num
else 'other'
result
}
test "Should be able to handle switches sans-condition.", ->
ok obj.func(yes) is 101
result = switch
when null then 0
when !1 then 1
when '' not of {''} then 2
when [] not instanceof Array then 3
when true is false then 4
when 'x' < 'y' > 'z' then 5
when 'a' in ['b', 'c'] then 6
when 'd' in (['e', 'f']) then 7
else ok
eq result, ok
# Switch with break as the return value of a loop.
i = 10
results = while i > 0
i--
switch i % 2
when 1 then i
when 0 then break
test "Should be able to use `@properties` within the switch clause.", ->
eq results.join(', '), '9, , 7, , 5, , 3, , 1, '
obj = {
num: 101
func: ->
switch @num
when 101 then '101!'
else 'other'
}
ok obj.func() is '101!'
# Issue #997. Switch doesn't fallthrough.
val = 1
switch true
when true
if false
return 5
else
val = 2
test "Should be able to use `@properties` within the switch cases.", ->
eq val, 1
obj = {
num: 101
func: (yesOrNo) ->
result = switch yesOrNo
when yes then @num
else 'other'
result
}
ok obj.func(yes) is 101
test "Switch with break as the return value of a loop.", ->
i = 10
results = while i > 0
i--
switch i % 2
when 1 then i
when 0 then break
eq results.join(', '), '9, , 7, , 5, , 3, , 1, '
test "Issue #997. Switch doesn't fallthrough.", ->
val = 1
switch true
when true
if false
return 5
else
val = 2
eq val, 1

View File

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

View File

@ -21,7 +21,7 @@ test "multiple semicolon-separated statements in parentheticals", ->
eq nonce, (1; 2; nonce)
eq nonce, (-> return (1; 2; nonce))()
#### Line Continuation
# Line Continuation
# Property Access

View File

@ -7,9 +7,11 @@
# * Explicit Returns
# shared identity function
id = (_) -> if arguments.length is 1 then _ else Array::slice.call(arguments)
id = (_) -> if arguments.length is 1 then _ else [arguments...]
test "basic argument passing", ->
a = {}
b = {}
c = {}
@ -18,7 +20,9 @@ test "basic argument passing", ->
eq a, (id a)
eq c, (id a, b, c)[2]
test "passing arguments on separate lines", ->
a = {}
b = {}
c = {}
@ -37,7 +41,9 @@ test "passing arguments on separate lines", ->
eq b,
(id b)
test "optional parens can be used in a nested fashion", ->
call = (func) -> func()
add = (a,b) -> a + b
result = call ->
@ -45,7 +51,9 @@ test "optional parens can be used in a nested fashion", ->
add 5, 5
ok result is 10
test "hanging commas and semicolons in argument list", ->
fn = () -> arguments.length
eq 2, fn(0,1,)
eq 3, fn 0, 1,
@ -58,24 +66,32 @@ test "hanging commas and semicolons in argument list", ->
throws -> CoffeeScript.compile "fn(,0)"
throws -> CoffeeScript.compile "fn(;0)"
func = ->
return if true
eq undefined, func()
result = ("hello".slice) 3
ok result is 'lo'
test "function invocation", ->
# And even with strange things like this:
funcs = [((x) -> x), ((x) -> x * x)]
result = funcs[1] 5
ok result is 25
func = ->
return if true
eq undefined, func()
# More fun with optional parens.
fn = (arg) -> arg
ok fn(fn {prop: 101}).prop is 101
result = ("hello".slice) 3
ok result is 'lo'
test "And even with strange things like this:", ->
funcs = [((x) -> x), ((x) -> x * x)]
result = funcs[1] 5
ok result is 25
test "More fun with optional parens.", ->
fn = (arg) -> arg
ok fn(fn {prop: 101}).prop is 101
okFunc = (f) -> ok(f())
okFunc -> true
okFunc = (f) -> ok(f())
okFunc -> true
test "chained function calls", ->
nonce = {}
@ -84,31 +100,39 @@ test "chained function calls", ->
eq nonce, identityWrap(identityWrap(nonce))()()
eq nonce, (identityWrap identityWrap nonce)()()
# Multi-blocks with optional parens.
result = fn( ->
fn ->
"Wrapped"
)
ok result()() is 'Wrapped'
# method calls
fnId = (fn) -> -> fn.apply this, arguments
math = {
add: (a, b) -> a + b
anonymousAdd: (a, b) -> a + b
fastAdd: fnId (a, b) -> a + b
}
ok math.add(5, 5) is 10
ok math.anonymousAdd(10, 10) is 20
ok math.fastAdd(20, 20) is 40
test "Multi-blocks with optional parens.", ->
fn = (arg) -> arg
result = fn( ->
fn ->
"Wrapped"
)
ok result()() is 'Wrapped'
test "method calls", ->
fnId = (fn) -> -> fn.apply this, arguments
math = {
add: (a, b) -> a + b
anonymousAdd: (a, b) -> a + b
fastAdd: fnId (a, b) -> a + b
}
ok math.add(5, 5) is 10
ok math.anonymousAdd(10, 10) is 20
ok math.fastAdd(20, 20) is 40
test "Ensure that functions can have a trailing comma in their argument list", ->
mult = (x, mids..., y) ->
x *= n for n in mids
x *= y
ok mult(1, 2,) is 2
ok mult(1, 2, 3,) is 6
ok mult(10, (i for i in [1..6])...) is 7200
# Ensure that functions can have a trailing comma in their argument list
mult = (x, mids..., y) ->
x *= n for n in mids
x *= y
ok mult(1, 2,) is 2
ok mult(1, 2, 3,) is 6
ok mult(10, (i for i in [1..6])...) is 7200
test "`@` and `this` should both be able to invoke a method", ->
nonce = {}
@ -118,133 +142,167 @@ test "`@` and `this` should both be able to invoke a method", ->
fn.withAt()
fn.withThis()
# Trying an implicit object call with a trailing function.
a = null
meth = (arg, obj, func) -> a = [obj.a, arg, func()].join ' '
meth 'apple', b: 1, a: 13, ->
'orange'
ok a is '13 apple orange'
# Ensure that empty functions don't return mistaken values.
obj =
func: (@param, @rest...) ->
ok obj.func(101, 102, 103, 104) is undefined
ok obj.param is 101
ok obj.rest.join(' ') is '102 103 104'
test "Trying an implicit object call with a trailing function.", ->
# Passing multiple functions without paren-wrapping is legal, and should compile.
sum = (one, two) -> one() + two()
result = sum ->
7 + 9
, ->
1 + 3
ok result is 20
a = null
meth = (arg, obj, func) -> a = [obj.a, arg, func()].join ' '
meth 'apple', b: 1, a: 13, ->
'orange'
ok a is '13 apple orange'
# Implicit call with a trailing if statement as a param.
func = -> arguments[1]
result = func 'one', if false then 100 else 13
ok result is 13
# Test more function passing:
result = sum( ->
1 + 2
, ->
2 + 1
)
ok result is 6
test "Ensure that empty functions don't return mistaken values.", ->
sum = (a, b) -> a + b
result = sum(1
, 2)
ok result is 3
obj =
func: (@param, @rest...) ->
ok obj.func(101, 102, 103, 104) is undefined
ok obj.param is 101
ok obj.rest.join(' ') is '102 103 104'
# Chained blocks, with proper indentation levels:
counter =
results: []
tick: (func) ->
@results.push func()
this
counter
.tick ->
3
.tick ->
2
.tick ->
1
arrayEq [3,2,1], counter.results
# This is a crazy one.
x = (obj, func) -> func obj
ident = (x) -> x
result = x {one: ident 1}, (obj) ->
inner = ident(obj)
ident inner
ok result.one is 1
test "Passing multiple functions without paren-wrapping is legal, and should compile.", ->
# More paren compilation tests:
reverse = (obj) -> obj.reverse()
ok reverse([1, 2].concat 3).join(' ') is '3 2 1'
sum = (one, two) -> one() + two()
result = sum ->
7 + 9
, ->
1 + 3
ok result is 20
# Test for inline functions with parentheses and implicit calls.
combine = (func, num) -> func() * num
result = combine (-> 1 + 2), 3
ok result is 9
# Test for calls/parens/multiline-chains.
f = (x) -> x
result = (f 1).toString()
.length
ok result is 1
test "Implicit call with a trailing if statement as a param.", ->
# Test implicit calls in functions in parens:
result = ((val) ->
[].push val
val
)(10)
ok result is 10
func = -> arguments[1]
result = func 'one', if false then 100 else 13
ok result is 13
# Ensure that chained calls with indented implicit object literals below are
# alright.
result = null
obj =
method: (val) -> this
second: (hash) -> result = hash.three
obj
.method(
101
).second(
one:
two: 2
three: 3
test "Test more function passing:", ->
sum = (one, two) -> one() + two()
result = sum( ->
1 + 2
, ->
2 + 1
)
eq result, 3
ok result is 6
# Test newline-supressed call chains with nested functions.
obj =
call: -> this
func = ->
sum = (a, b) -> a + b
result = sum(1
, 2)
ok result is 3
test "Chained blocks, with proper indentation levels:", ->
counter =
results: []
tick: (func) ->
@results.push func()
this
counter
.tick ->
3
.tick ->
2
.tick ->
1
arrayEq [3,2,1], counter.results
test "This is a crazy one.", ->
x = (obj, func) -> func obj
ident = (x) -> x
result = x {one: ident 1}, (obj) ->
inner = ident(obj)
ident inner
ok result.one is 1
test "More paren compilation tests:", ->
reverse = (obj) -> obj.reverse()
ok reverse([1, 2].concat 3).join(' ') is '3 2 1'
test "Test for inline functions with parentheses and implicit calls.", ->
combine = (func, num) -> func() * num
result = combine (-> 1 + 2), 3
ok result is 9
test "Test for calls/parens/multiline-chains.", ->
f = (x) -> x
result = (f 1).toString()
.length
ok result is 1
test "Test implicit calls in functions in parens:", ->
result = ((val) ->
[].push val
val
)(10)
ok result is 10
test "Ensure that chained calls with indented implicit object literals below are alright.", ->
result = null
obj =
method: (val) -> this
second: (hash) -> result = hash.three
obj
.call ->
one two
.call ->
three four
101
eq func(), 101
.method(
101
).second(
one:
two: 2
three: 3
)
eq result, 3
# Implicit objects with number arguments.
func = (x, y) -> y
obj =
prop: func "a", 1
ok obj.prop is 1
# Non-spaced unary and binary operators should cause a function call.
func = (val) -> val + 1
ok (func +5) is 6
ok (func -5) is -4
test "Test newline-supressed call chains with nested functions.", ->
# Prefix unary assignment operators are allowed in parenless calls.
val = 5
ok (func --val) is 5
obj =
call: -> this
func = ->
obj
.call ->
one two
.call ->
three four
101
eq func(), 101
test "Implicit objects with number arguments.", ->
func = (x, y) -> y
obj =
prop: func "a", 1
ok obj.prop is 1
test "Non-spaced unary and binary operators should cause a function call.", ->
func = (val) -> val + 1
ok (func +5) is 6
ok (func -5) is -4
test "Prefix unary assignment operators are allowed in parenless calls.", ->
func = (val) -> val + 1
val = 5
ok (func --val) is 5
test "#855: execution context for `func arr...` should be `null`", ->
contextTest = -> eq @, global
@ -262,13 +320,14 @@ test "#904: Destructuring function arguments with same-named variables in scope"
eq nonce, a
eq nonce, b
obj =
index: 0
0: {method: -> this is obj[0]}
ok obj[obj.index++].method([]...), 'should cache base value'
test "caching base value", ->
obj =
index: 0
0: {method: -> this is obj[0]}
ok obj[obj.index++].method([]...)
#### Splats in Function Invocations
test "passing splats to functions", ->
arrayEq [0..4], id id [0..4]...
@ -280,33 +339,46 @@ test "passing splats to functions", ->
arrayEq [2..6], others
eq 7, last
#894: Splatting against constructor-chained functions.
x = null
class Foo
bar: (y) -> x = y
new Foo().bar([101]...)
eq x, 101
test "splat variables are local to the function", ->
outer = "x"
clobber = (avar, outer...) -> outer
clobber "foo", "bar"
eq "x", outer
# Functions with splats being called with too few arguments.
pen = null
method = (first, variable..., penultimate, ultimate) ->
pen = penultimate
method 1, 2, 3, 4, 5, 6, 7, 8, 9
ok pen is 8
method 1, 2, 3
ok pen is 2
method 1, 2
ok pen is 2
# splats with super() within classes.
class Parent
meth: (args...) ->
args
class Child extends Parent
meth: ->
nums = [3, 2, 1]
super nums...
ok (new Child).meth().join(' ') is '3 2 1'
test "Issue 894: Splatting against constructor-chained functions.", ->
x = null
class Foo
bar: (y) -> x = y
new Foo().bar([101]...)
eq x, 101
test "Functions with splats being called with too few arguments.", ->
pen = null
method = (first, variable..., penultimate, ultimate) ->
pen = penultimate
method 1, 2, 3, 4, 5, 6, 7, 8, 9
ok pen is 8
method 1, 2, 3
ok pen is 2
method 1, 2
ok pen is 2
test "splats with super() within classes.", ->
class Parent
meth: (args...) ->
args
class Child extends Parent
meth: ->
nums = [3, 2, 1]
super nums...
ok (new Child).meth().join(' ') is '3 2 1'
test "#1011: passing a splat to a method of a number", ->
eq '1011', 11.toString [2]...
@ -315,12 +387,13 @@ test "#1011: passing a splat to a method of a number", ->
eq '1011', (131.0).toString [5]...
#### Implicit Return
test "implicit return", ->
eq ok, new ->
ok
### Should `return` implicitly ###
### even with trailing comments. ###
eq ok, new ->
ok
### Should `return` implicitly ###
### even with trailing comments. ###
test "implicit returns with multiple branches", ->
nonce = {}
@ -332,6 +405,7 @@ test "implicit returns with multiple branches", ->
nonce
eq nonce, fn()
test "implicit returns with switches", ->
nonce = {}
fn = ->
@ -340,6 +414,7 @@ test "implicit returns with switches", ->
else return undefined
eq nonce, fn()
test "preserve context when generating closure wrappers for expression conversions", ->
nonce = {}
obj =
@ -355,9 +430,7 @@ test "preserve context when generating closure wrappers for expression conversio
eq nonce, obj.property
#### Explicit Returns
test "don't wrap \"pure\" statements in a closure", ->
test "don't wrap 'pure' statements in a closure", ->
nonce = {}
items = [0, 1, 2, 3, nonce, 4, 5]
fn = (items) ->

View File

@ -11,7 +11,7 @@
# * Parameter Destructuring
# * Default Parameters
#### Function Definition
# Function Definition
x = 1
y = {}
@ -46,7 +46,7 @@ del = -> 5
ok del() is 5
#### Bound Function Definition
# Bound Function Definition
obj =
bound: ->
@ -64,7 +64,7 @@ ok obj isnt obj.unbound()
eq obj, obj.nested()
#### Parameter List Features
# Parameter List Features
test "splats", ->
arrayEq [0, 1, 2], (((splat...) -> splat) 0, 1, 2)
@ -148,6 +148,9 @@ test "default values with splatted arguments", ->
eq 1, withSplats(1,1,1)
eq 2, withSplats(1,1,1,1)
test "default values with function calls", ->
doesNotThrow -> CoffeeScript.compile "(x = f()) ->"
test "arguments vs parameters", ->
doesNotThrow -> CoffeeScript.compile "f(x) ->"
f = (g) -> g()

View File

@ -5,7 +5,7 @@
{starts, ends, compact, count, merge, extend, flatten, del, last} = CoffeeScript.helpers
#### `starts`
# `starts`
test "the `starts` helper tests if a string starts with another string", ->
ok starts('01234', '012')
@ -16,7 +16,7 @@ test "the `starts` helper can take an optional offset", ->
ok not starts('01234', '01', 1)
#### `ends`
# `ends`
test "the `ends` helper tests if a string ends with another string", ->
ok ends('01234', '234')
@ -27,7 +27,7 @@ test "the `ends` helper can take an optional offset", ->
ok not ends('01234', '234', 6)
#### `compact`
# `compact`
test "the `compact` helper removes falsey values from an array, preserves truthy ones", ->
allValues = [1, 0, false, obj={}, [], '', ' ', -1, null, undefined, true]
@ -35,7 +35,7 @@ test "the `compact` helper removes falsey values from an array, preserves truthy
arrayEq truthyValues, compact(allValues)
#### `count`
# `count`
test "the `count` helper counts the number of occurances of a string in another string", ->
eq 1/0, count('abc', '')
@ -46,7 +46,7 @@ test "the `count` helper counts the number of occurances of a string in another
eq 2, count('abcdabcd','abc')
#### `merge`
# `merge`
test "the `merge` helper makes a new object with all properties of the objects given as its arguments", ->
ary = [0, 1, 2, 3, 4]
@ -58,7 +58,7 @@ test "the `merge` helper makes a new object with all properties of the objects g
eq val, merged[key]
#### `extend`
# `extend`
test "the `extend` helper performs a shallow copy", ->
ary = [0, 1, 2, 3]
@ -69,7 +69,7 @@ test "the `extend` helper performs a shallow copy", ->
eq 2, obj[2]
#### `flatten`
# `flatten`
test "the `flatten` helper flattens an array", ->
success = yes
@ -77,7 +77,7 @@ test "the `flatten` helper flattens an array", ->
ok success
#### `del`
# `del`
test "the `del` helper deletes a property from an object and returns the deleted value", ->
obj = [0, 1, 2]
@ -85,7 +85,7 @@ test "the `del` helper deletes a property from an object and returns the deleted
ok 1 not of obj
#### `last`
# `last`
test "the `last` helper returns the last item of an array-like object", ->
ary = [0, 1, 2, 3, 4]

View File

@ -4,7 +4,7 @@
# * String Interpolation
# * Regular Expression Interpolation
#### String Interpolation
# String Interpolation
# TODO: refactor string interpolation tests
@ -109,7 +109,7 @@ eq 'multiline nested "interpolations" work', """multiline #{
} work"""
#### Regular Expression Interpolation
# Regular Expression Interpolation
# TODO: improve heregex interpolation tests

View File

@ -9,7 +9,7 @@
# * Non-Integer Literals
#### Decimal Integer Literals
# Decimal Integer Literals
test "call methods directly on numbers", ->
eq 4, 4.valueOf()
@ -23,7 +23,7 @@ eq Number::toString, 42['toString']
eq Number::toString, 42.toString
#### Non-Integer Literals
# Non-Integer Literals
# Decimal number literals.
value = .25 + .75
@ -37,3 +37,8 @@ eq Number::toString, .42['toString']
eq Number::toString, 4.2.toString
eq Number::toString, .42.toString
test '#1168: leading floating point suppresses newline', ->
eq 1, do ->
1
.5 + 0.5

View File

@ -59,7 +59,7 @@ test "use `::` operator on keywords `this` and `@`", ->
eq nonce, obj.withThis()
#### Existential Operator (Binary)
# Existential Operator (Binary)
test "binary existential operator", ->
nonce = {}
@ -91,7 +91,7 @@ test "binary existential operator with negative number", ->
eq -1, a
#### Existential Operator (Unary)
# Existential Operator (Unary)
test "postfix existential operator", ->
ok (if nonexistent? then false else true)
@ -114,7 +114,7 @@ test "postfix existential operator on expressions", ->
eq true, (1 or 0)?, true
#### `is`,`isnt`,`==`,`!=`
# `is`,`isnt`,`==`,`!=`
test "`==` and `is` should be interchangeable", ->
a = b = 1
@ -130,7 +130,7 @@ test "`!=` and `isnt` should be interchangeable", ->
ok a isnt b
#### [not] in/of
# [not] in/of
# - `in` should check if an array contains a value using `indexOf`
# - `of` should check if a property is defined on an object using `in`
@ -185,7 +185,7 @@ test "#768: `in` should preserve evaluation order", ->
eq 3, share
#### Chained Comparison
# Chained Comparison
test "chainable operators", ->
ok 100 > 10 > 1 > 0 > -1

View File

@ -35,7 +35,7 @@ test "#584: slashes are allowed unescaped in character classes", ->
ok /^a\/[/]b$/.test 'a//b'
#### Heregexe(n|s)
# Heregexe(n|s)
test "a heregex will ignore whitespace and comments", ->
eq /^I'm\x20+[a]\s+Heregex?\/\/\//gim + '', ///

View File

@ -7,7 +7,7 @@
# shared array
shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#### Slicing
# Slicing
test "basic slicing", ->
arrayEq [7, 8, 9] , shared[7..9]
@ -53,7 +53,7 @@ test "string slicing", ->
ok str[-5..] is "vwxyz"
#### Splicing
# Splicing
test "basic splicing", ->
ary = [0..9]

View File

@ -6,7 +6,7 @@
# * Soaked Function Invocation
#### Soaked Property Access
# Soaked Property Access
test "soaked property access", ->
nonce = {}
@ -73,7 +73,7 @@ test "operations on soaked properties", ->
eq yes, delete a?.b.c
#### Soaked Method Invocation
# Soaked Method Invocation
test "soaked method invocation", ->
nonce = {}
@ -96,7 +96,7 @@ test "#733", ->
eq a.b?.c?([2, 3]...), 2
#### Soaked Function Invocation
# Soaked Function Invocation
test "soaked function invocation", ->
nonce = {}