Skip to content

Commit 1e7d638

Browse files
committed
adding bound functions, with test
1 parent 0ceca07 commit 1e7d638

10 files changed

Lines changed: 85 additions & 74 deletions

File tree

documentation/speed.html

Lines changed: 24 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,63 +14,39 @@ <h1>Quickie CoffeeScript Speed Tests</h1>
1414
var arr = [];
1515
while (num--) arr.push(num);
1616

17-
JSLitmus.test('current comprehensions', function() {
18-
__a = arr;
19-
__c = [];
20-
for (__b in __a) {
21-
if (__a.hasOwnProperty(__b)) {
22-
num = __a[__b];
23-
__d = num;
24-
__c.push(num);
25-
}
26-
}
27-
});
17+
var f1 = function f1() {
18+
return arr;
19+
};
2820

29-
JSLitmus.test('raw for loop (best we can do)', function() {
30-
__a = arr;
31-
__c = new Array(__a.length);
32-
for (__b=0; __b < __a.length; __b++) {
33-
__c[__b] = __a[__b];
34-
}
21+
JSLitmus.test('regular function', function() {
22+
f1();
3523
});
3624

37-
JSLitmus.test('current without hasOwnProperty check', function() {
38-
__a = arr;
39-
__c = [];
40-
for (__b in __a) {
41-
num = __a[__b];
42-
__d = num;
43-
__c.push(num);
44-
}
45-
});
25+
var __this = this;
26+
27+
var f2 = function f2() {
28+
return (function() {
29+
return arr;
30+
}).apply(__this, arguments);
31+
};
4632

47-
JSLitmus.test('raw for..in loop', function() {
48-
__a = arr;
49-
__c = new Array(__a.length);
50-
for (__b in __a) {
51-
__c[__b] = __a[__b];
52-
}
33+
JSLitmus.test('bound function', function() {
34+
f2();
5335
});
5436

55-
JSLitmus.test('weepy\'s comprehensions', function() {
56-
__c = []; __a = arr;
57-
__d = function(num, __b) {
58-
__c.push(num);
37+
var f3 = (function() {
38+
__b = function() {
39+
return arr;
5940
};
60-
if (__a instanceof Array) {
61-
for (__b=0; __b<__a.length; __b++) __d(__a[__b], __b);
62-
} else {
63-
for (__b in __a) { if (__a.hasOwnProperty(__b)) __d(__a[__b], __b); }
64-
}
65-
});
41+
return (function f2() {
42+
return __b.apply(__this, arguments);
43+
});
44+
})();
6645

67-
JSLitmus.test('CoffeeScript 0.2.2 comprehensions', function() {
68-
__c = []; __a = arr;
69-
for (__b=0; __b<__a.length; __b++) {
70-
num = __a[__b];
71-
__c.push(num);
72-
}
46+
JSLitmus.test('prebound function', function() {
47+
f3();
7348
});
49+
7450
</script>
7551

7652
</body>

lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<key>comment</key>
4444
<string>match stuff like: funcName: =&gt;</string>
4545
<key>match</key>
46-
<string>([a-zA-Z0-9_?.$:*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
46+
<string>([a-zA-Z0-9_?.$:*]*?)\s*(=\b|:\b)\s*([\w,\s]*?)\s*(=+&gt;)</string>
4747
<key>name</key>
4848
<string>meta.function.coffee</string>
4949
</dict>
@@ -64,7 +64,7 @@
6464
<key>comment</key>
6565
<string>match stuff like: a =&gt;</string>
6666
<key>match</key>
67-
<string>([a-zA-Z0-9_?., $*]*)\s*(=&gt;)</string>
67+
<string>([a-zA-Z0-9_?., $*]*)\s*(=+&gt;)</string>
6868
<key>name</key>
6969
<string>meta.inline.function.coffee</string>
7070
</dict>

lib/coffee_script/grammar.y

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ prechigh
3939
left EXTENDS
4040
left ASSIGN '||=' '&&='
4141
right RETURN
42-
right '=>' UNLESS IF ELSE WHILE
42+
right '=>' '==>' UNLESS IF ELSE WHILE
4343
preclow
4444

4545
rule
@@ -198,8 +198,14 @@ rule
198198
199199
# Function definition.
200200
Code:
201-
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
202-
| "=>" Block { result = CodeNode.new([], val[1]) }
201+
ParamList FuncGlyph Block { result = CodeNode.new(val[0], val[2], val[1]) }
202+
| FuncGlyph Block { result = CodeNode.new([], val[1], val[0]) }
203+
;
204+
205+
# The symbols to signify functions, and bound functions.
206+
FuncGlyph:
207+
'=>' { result = :func }
208+
| '==>' { result = :boundfunc }
203209
;
204210
205211
# The parameters to a function definition.

lib/coffee_script/lexer.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Lexer
2727
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
2828
WHITESPACE = /\A([ \t]+)/
2929
COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/
30-
CODE = /\A(=>)/
30+
CODE = /\A(=?=>)/
3131
REGEX = /\A(\/(.*?)([^\\]|\\\\)\/[imgy]{0,4})/
3232
MULTI_DENT = /\A((\n([ \t]*))+)(\.)?/
3333
LAST_DENT = /\n([ \t]*)/
@@ -155,7 +155,7 @@ def indent_token
155155
@line += indent.scan(MULTILINER).size
156156
@i += indent.size
157157
next_character = @chunk[MULTI_DENT, 4]
158-
no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && last_value != "=>")
158+
no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && !last_value.match(CODE))
159159
return suppress_newlines(indent) if no_newlines
160160
size = indent.scan(LAST_DENT).last.last.length
161161
return newline_token(indent) if size == @indent

lib/coffee_script/nodes.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -546,14 +546,23 @@ def compile_unary(o)
546546

547547
# A function definition. The only node that creates a new Scope.
548548
class CodeNode < Node
549-
attr_reader :params, :body
549+
attr_reader :params, :body, :bound
550550

551-
def initialize(params, body)
551+
def initialize(params, body, tag=nil)
552552
@params = params
553-
@body = body
553+
@body = body
554+
@bound = tag == :boundfunc
555+
end
556+
557+
def statement?
558+
@bound
554559
end
555560

556561
def compile_node(o)
562+
if @bound
563+
o[:scope].assign("__this", "this")
564+
fvar = o[:scope].free_variable
565+
end
557566
shared_scope = o.delete(:shared_scope)
558567
o[:scope] = shared_scope || Scope.new(o[:scope], @body)
559568
o[:return] = true
@@ -568,9 +577,11 @@ def compile_node(o)
568577
@body.unshift(splat)
569578
end
570579
@params.each {|id| o[:scope].parameter(id.to_s) }
571-
code = @body.compile_with_declarations(o)
580+
code = "\n#{@body.compile_with_declarations(o)}\n"
572581
name_part = name ? " #{name}" : ''
573-
write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{idt}}")
582+
func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt}}"
583+
return write(func) unless @bound
584+
write("#{idt}#{fvar} = #{func};\n#{idt}#{o[:return] ? 'return ' : ''}(function#{name_part}() {\n#{idt(1)}return #{fvar}.apply(__this, arguments);\n#{idt}});")
574585
end
575586
end
576587

@@ -726,7 +737,7 @@ def compile_node(o)
726737
body = Expressions.wrap(IfNode.new(@filter, body))
727738
end
728739
if @object
729-
o[:scope].top_level_assign("__hasProp", "Object.prototype.hasOwnProperty")
740+
o[:scope].assign("__hasProp", "Object.prototype.hasOwnProperty", true)
730741
body = Expressions.wrap(IfNode.new(
731742
CallNode.new(ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]), [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]),
732743
Expressions.wrap(body),

lib/coffee_script/rewriter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class Rewriter
2626

2727
# Single-line flavors of block expressions that have unclosed endings.
2828
# The grammar can't disambiguate them, so we insert the implicit indentation.
29-
SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN]
29+
SINGLE_LINERS = [:ELSE, "=>", "==>", :TRY, :FINALLY, :THEN]
3030
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN]
3131

3232
# Rewrite the token stream in multiple passes, one logical filter at

lib/coffee_script/scope.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ def free_variable
4747
@temp_variable.dup
4848
end
4949

50-
# Ensure that an assignment is made at the top-level scope.
51-
# Takes two strings.
52-
def top_level_assign(name, value)
53-
return @parent.top_level_assign(name, value) if @parent
50+
# Ensure that an assignment is made at the top of scope (or top-level
51+
# scope, if requested).
52+
def assign(name, value, top=false)
53+
return @parent.assign(name, value, top) if top && @parent
5454
@variables[name.to_sym] = Value.new(value)
5555
end
5656

lib/coffee_script/value.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ def eql?(other)
4141
def hash
4242
@value.hash
4343
end
44+
45+
def match(regex)
46+
@value.match(regex)
47+
end
4448
end
4549

4650
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
x: 1
2+
y: {}
3+
y.x: => 3
4+
5+
print(x is 1)
6+
print(typeof(y.x) is 'function')
7+
print(y.x() is 3)
8+
print(y.x.name is 'x')
9+
10+
11+
obj: {
12+
name: "Fred"
13+
14+
bound: =>
15+
(==> print(this.name is "Fred"))()
16+
17+
unbound: =>
18+
(=> print(!this.name?))()
19+
}
20+
21+
obj.unbound()
22+
obj.bound()

test/fixtures/execution/test_named_functions.coffee

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)