adding bound functions, with test

This commit is contained in:
Jeremy Ashkenas
2010-01-13 20:59:57 -05:00
parent 0ceca0778c
commit 1e7d638435
10 changed files with 87 additions and 76 deletions

View File

@@ -14,63 +14,39 @@
var arr = []; var arr = [];
while (num--) arr.push(num); while (num--) arr.push(num);
JSLitmus.test('current comprehensions', function() { var f1 = function f1() {
__a = arr; return arr;
__c = []; };
for (__b in __a) {
if (__a.hasOwnProperty(__b)) { JSLitmus.test('regular function', function() {
num = __a[__b]; f1();
__d = num;
__c.push(num);
}
}
}); });
JSLitmus.test('raw for loop (best we can do)', function() { var __this = this;
__a = arr;
__c = new Array(__a.length); var f2 = function f2() {
for (__b=0; __b < __a.length; __b++) { return (function() {
__c[__b] = __a[__b]; return arr;
} }).apply(__this, arguments);
};
JSLitmus.test('bound function', function() {
f2();
}); });
JSLitmus.test('current without hasOwnProperty check', function() { var f3 = (function() {
__a = arr; __b = function() {
__c = []; return arr;
for (__b in __a) {
num = __a[__b];
__d = num;
__c.push(num);
}
});
JSLitmus.test('raw for..in loop', function() {
__a = arr;
__c = new Array(__a.length);
for (__b in __a) {
__c[__b] = __a[__b];
}
});
JSLitmus.test('weepy\'s comprehensions', function() {
__c = []; __a = arr;
__d = function(num, __b) {
__c.push(num);
}; };
if (__a instanceof Array) { return (function f2() {
for (__b=0; __b<__a.length; __b++) __d(__a[__b], __b); return __b.apply(__this, arguments);
} else { });
for (__b in __a) { if (__a.hasOwnProperty(__b)) __d(__a[__b], __b); } })();
}
JSLitmus.test('prebound function', function() {
f3();
}); });
JSLitmus.test('CoffeeScript 0.2.2 comprehensions', function() {
__c = []; __a = arr;
for (__b=0; __b<__a.length; __b++) {
num = __a[__b];
__c.push(num);
}
});
</script> </script>
</body> </body>

View File

@@ -43,7 +43,7 @@
<key>comment</key> <key>comment</key>
<string>match stuff like: funcName: =&gt; … </string> <string>match stuff like: funcName: =&gt; … </string>
<key>match</key> <key>match</key>
<string>([a-zA-Z0-9_?.$:*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string> <string>([a-zA-Z0-9_?.$:*]*?)\s*(=\b|:\b)\s*([\w,\s]*?)\s*(=+&gt;)</string>
<key>name</key> <key>name</key>
<string>meta.function.coffee</string> <string>meta.function.coffee</string>
</dict> </dict>
@@ -64,7 +64,7 @@
<key>comment</key> <key>comment</key>
<string>match stuff like: a =&gt; … </string> <string>match stuff like: a =&gt; … </string>
<key>match</key> <key>match</key>
<string>([a-zA-Z0-9_?., $*]*)\s*(=&gt;)</string> <string>([a-zA-Z0-9_?., $*]*)\s*(=+&gt;)</string>
<key>name</key> <key>name</key>
<string>meta.inline.function.coffee</string> <string>meta.inline.function.coffee</string>
</dict> </dict>

View File

@@ -39,7 +39,7 @@ prechigh
left EXTENDS left EXTENDS
left ASSIGN '||=' '&&=' left ASSIGN '||=' '&&='
right RETURN right RETURN
right '=>' UNLESS IF ELSE WHILE right '=>' '==>' UNLESS IF ELSE WHILE
preclow preclow
rule rule
@@ -198,8 +198,14 @@ rule
# Function definition. # Function definition.
Code: Code:
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) } ParamList FuncGlyph Block { result = CodeNode.new(val[0], val[2], val[1]) }
| "=>" Block { result = CodeNode.new([], val[1]) } | FuncGlyph Block { result = CodeNode.new([], val[1], val[0]) }
;
# The symbols to signify functions, and bound functions.
FuncGlyph:
'=>' { result = :func }
| '==>' { result = :boundfunc }
; ;
# The parameters to a function definition. # The parameters to a function definition.

View File

@@ -27,7 +27,7 @@ module CoffeeScript
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/ OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
WHITESPACE = /\A([ \t]+)/ WHITESPACE = /\A([ \t]+)/
COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/ COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/
CODE = /\A(=>)/ CODE = /\A(=?=>)/
REGEX = /\A(\/(.*?)([^\\]|\\\\)\/[imgy]{0,4})/ REGEX = /\A(\/(.*?)([^\\]|\\\\)\/[imgy]{0,4})/
MULTI_DENT = /\A((\n([ \t]*))+)(\.)?/ MULTI_DENT = /\A((\n([ \t]*))+)(\.)?/
LAST_DENT = /\n([ \t]*)/ LAST_DENT = /\n([ \t]*)/
@@ -155,7 +155,7 @@ module CoffeeScript
@line += indent.scan(MULTILINER).size @line += indent.scan(MULTILINER).size
@i += indent.size @i += indent.size
next_character = @chunk[MULTI_DENT, 4] next_character = @chunk[MULTI_DENT, 4]
no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && last_value != "=>") no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && !last_value.match(CODE))
return suppress_newlines(indent) if no_newlines return suppress_newlines(indent) if no_newlines
size = indent.scan(LAST_DENT).last.last.length size = indent.scan(LAST_DENT).last.last.length
return newline_token(indent) if size == @indent return newline_token(indent) if size == @indent

View File

@@ -546,14 +546,23 @@ module CoffeeScript
# A function definition. The only node that creates a new Scope. # A function definition. The only node that creates a new Scope.
class CodeNode < Node class CodeNode < Node
attr_reader :params, :body attr_reader :params, :body, :bound
def initialize(params, body) def initialize(params, body, tag=nil)
@params = params @params = params
@body = body @body = body
@bound = tag == :boundfunc
end
def statement?
@bound
end end
def compile_node(o) def compile_node(o)
if @bound
o[:scope].assign("__this", "this")
fvar = o[:scope].free_variable
end
shared_scope = o.delete(:shared_scope) shared_scope = o.delete(:shared_scope)
o[:scope] = shared_scope || Scope.new(o[:scope], @body) o[:scope] = shared_scope || Scope.new(o[:scope], @body)
o[:return] = true o[:return] = true
@@ -568,9 +577,11 @@ module CoffeeScript
@body.unshift(splat) @body.unshift(splat)
end end
@params.each {|id| o[:scope].parameter(id.to_s) } @params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.compile_with_declarations(o) code = "\n#{@body.compile_with_declarations(o)}\n"
name_part = name ? " #{name}" : '' name_part = name ? " #{name}" : ''
write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{idt}}") func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt}}"
return write(func) unless @bound
write("#{idt}#{fvar} = #{func};\n#{idt}#{o[:return] ? 'return ' : ''}(function#{name_part}() {\n#{idt(1)}return #{fvar}.apply(__this, arguments);\n#{idt}});")
end end
end end
@@ -726,7 +737,7 @@ module CoffeeScript
body = Expressions.wrap(IfNode.new(@filter, body)) body = Expressions.wrap(IfNode.new(@filter, body))
end end
if @object if @object
o[:scope].top_level_assign("__hasProp", "Object.prototype.hasOwnProperty") o[:scope].assign("__hasProp", "Object.prototype.hasOwnProperty", true)
body = Expressions.wrap(IfNode.new( body = Expressions.wrap(IfNode.new(
CallNode.new(ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]), [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]), CallNode.new(ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]), [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]),
Expressions.wrap(body), Expressions.wrap(body),

View File

@@ -26,7 +26,7 @@ module CoffeeScript
# Single-line flavors of block expressions that have unclosed endings. # Single-line flavors of block expressions that have unclosed endings.
# The grammar can't disambiguate them, so we insert the implicit indentation. # The grammar can't disambiguate them, so we insert the implicit indentation.
SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN] SINGLE_LINERS = [:ELSE, "=>", "==>", :TRY, :FINALLY, :THEN]
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN] SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN]
# Rewrite the token stream in multiple passes, one logical filter at # Rewrite the token stream in multiple passes, one logical filter at

View File

@@ -47,10 +47,10 @@ module CoffeeScript
@temp_variable.dup @temp_variable.dup
end end
# Ensure that an assignment is made at the top-level scope. # Ensure that an assignment is made at the top of scope (or top-level
# Takes two strings. # scope, if requested).
def top_level_assign(name, value) def assign(name, value, top=false)
return @parent.top_level_assign(name, value) if @parent return @parent.assign(name, value, top) if top && @parent
@variables[name.to_sym] = Value.new(value) @variables[name.to_sym] = Value.new(value)
end end

View File

@@ -41,6 +41,10 @@ module CoffeeScript
def hash def hash
@value.hash @value.hash
end end
def match(regex)
@value.match(regex)
end
end end
end end

View File

@@ -0,0 +1,22 @@
x: 1
y: {}
y.x: => 3
print(x is 1)
print(typeof(y.x) is 'function')
print(y.x() is 3)
print(y.x.name is 'x')
obj: {
name: "Fred"
bound: =>
(==> print(this.name is "Fred"))()
unbound: =>
(=> print(!this.name?))()
}
obj.unbound()
obj.bound()

View File

@@ -1,8 +0,0 @@
x: 1
y: {}
y.x: => 3
print(x is 1)
print(typeof(y.x) is 'function')
print(y.x() is 3)
print(y.x.name is 'x')