simplification of function and prototype naming -- last_assign, immediate_assign, and proto_assign are gone, in favor of 'name' and 'proto' properties on CodeNodes

This commit is contained in:
Jeremy Ashkenas 2010-01-16 15:44:07 -05:00
parent 62e946b8ce
commit c6c0c7d059
3 changed files with 54 additions and 28 deletions

View File

@ -83,9 +83,9 @@ module CoffeeScript
class Expressions < Node
statement
children :expressions
attr_accessor :function
TRAILING_WHITESPACE = /\s+$/
UPPERCASE = /[A-Z]/
# Wrap up a node as an Expressions, unless it already is.
def self.wrap(*nodes)
@ -125,12 +125,6 @@ module CoffeeScript
node == @expressions[@last_index]
end
# Determine if this is the expressions body within a constructor function.
# Constructors are capitalized by CoffeeScript convention.
def constructor?(o)
o[:top] && o[:last_assign] && o[:last_assign][0..0][UPPERCASE]
end
def compile(o={})
o[:scope] ? super(o) : compile_root(o)
end
@ -144,7 +138,7 @@ module CoffeeScript
def compile_root(o={})
indent = o[:no_wrap] ? '' : TAB
@indent = indent
o.merge!(:indent => indent, :scope => Scope.new(nil, self))
o.merge!(:indent => indent, :scope => Scope.new(nil, self, nil))
code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
code.gsub!(TRAILING_WHITESPACE, '')
write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
@ -173,10 +167,10 @@ module CoffeeScript
# If it's a statement, the node knows how to return itself.
return node.compile(o.merge(:return => true)) if node.statement?
# If it's not part of a constructor, we can just return the value of the expression.
return "#{idt}return #{node.compile(o)};" unless constructor?(o)
return "#{idt}return #{node.compile(o)};" unless o[:scope].function && o[:scope].function.constructor?
# It's the last line of a constructor, add a safety check.
temp = o[:scope].free_variable
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:scope].function.name} === this.constructor ? this : #{temp};"
end
end
@ -287,10 +281,11 @@ module CoffeeScript
# Compile a call against the superclass's implementation of the current function.
def compile_super(args, o)
methname = o[:last_assign]
methname = o[:scope].function.name
arg_part = args.empty? ? '' : ", #{args}"
meth = o[:proto_assign] ? "#{o[:proto_assign]}.__superClass__.#{methname}" :
"#{methname}.__superClass__.constructor"
meth = o[:scope].function.proto ?
"#{o[:scope].function.proto}.__superClass__.#{methname}" :
"#{methname}.__superClass__.constructor"
"#{meth}.call(this#{arg_part})"
end
@ -487,12 +482,14 @@ module CoffeeScript
def compile_node(o)
return compile_pattern_match(o) if statement?
return compile_splice(o) if value? && @variable.splice?
stmt = o.delete(:as_statement)
name = @variable.compile(o)
last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
proto = name[PROTO_ASSIGN, 1]
o = o.merge(:last_assign => last, :proto_assign => proto)
o[:immediate_assign] = last if @value.is_a?(CodeNode) && last.match(Lexer::IDENTIFIER)
stmt = o.delete(:as_statement)
name = @variable.compile(o)
last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
proto = name[PROTO_ASSIGN, 1]
if @value.is_a?(CodeNode)
@value.name = last if last.match(Lexer::IDENTIFIER)
@value.proto = proto if proto
end
return write("#{name}: #{@value.compile(o)}") if @context == :object
o[:scope].find(name) unless value? && @variable.properties?
val = "#{name} = #{@value.compile(o)}"
@ -588,8 +585,11 @@ module CoffeeScript
# A function definition. The only node that creates a new Scope.
# A CodeNode does not have any children -- they're within the new scope.
class CodeNode < Node
attr_reader :params, :body
attr_reader :bound
attr_reader :params, :body, :bound
attr_accessor :name, :proto
# Constructor functions start with an uppercase letter, by convention.
UPPERCASE = /[A-Z]/
def initialize(params, body, tag=nil)
@params = params
@ -601,17 +601,20 @@ module CoffeeScript
true
end
def constructor?
@name && @name[0..0][UPPERCASE]
end
def compile_node(o)
shared_scope = o.delete(:shared_scope)
top = o.delete(:top)
o[:scope] = shared_scope || Scope.new(o[:scope], @body)
o[:scope] = shared_scope || Scope.new(o[:scope], @body, self)
o[:return] = true
o[:top] = true
o[:indent] = idt(@bound ? 2 : 1)
o.delete(:no_wrap)
o.delete(:globals)
o.delete(:closure)
name = o.delete(:immediate_assign)
if @params.last.is_a?(SplatNode)
splat = @params.pop
splat.index = @params.length
@ -619,7 +622,7 @@ module CoffeeScript
end
@params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.empty? ? "" : "\n#{@body.compile_with_declarations(o)}\n"
name_part = name ? " #{name}" : ''
name_part = @name ? " #{@name}" : ''
func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt(@bound ? 1 : 0)}}"
func = "(#{func})" if top && !@bound
return write(func) unless @bound

View File

@ -5,12 +5,13 @@ module CoffeeScript
# whether a variable has been seen before or if it needs to be declared.
class Scope
attr_reader :parent, :expressions, :variables, :temp_variable
attr_reader :parent, :expressions, :function, :variables, :temp_variable
# Initialize a scope with its parent, for lookups up the chain,
# as well as the Expressions body where it should declare its variables.
def initialize(parent, expressions)
@parent, @expressions = parent, expressions
# as well as the Expressions body where it should declare its variables,
# and the function that it wraps.
def initialize(parent, expressions, function)
@parent, @expressions, @function = parent, expressions, function
@variables = {}
@temp_variable = @parent ? @parent.temp_variable.dup : '__a'
end

View File

@ -24,3 +24,25 @@ obj: {
obj.unbound()
obj.bound()
# The named function should be cleared out before a call occurs:
# Python decorator style wrapper that memoizes any function
memoize: fn =>
cache: {}
self: this
args... =>
key: args.toString()
return cache[key] if cache[key]
cache[key] = fn.apply(self, args)
Math: {
Add: a, b => a + b
AnonymousAdd: (a, b => a + b)
FastAdd: memoize() a, b => a + b
}
print(Math.Add(5, 5) is 10)
print(Math.AnonymousAdd(10, 10) is 20)
print(Math.FastAdd(20, 20) is 40)