refactoring and commenting nodes.rb Expressions

This commit is contained in:
Jeremy Ashkenas
2010-01-10 22:04:38 -05:00
parent a1528f3f19
commit d9d09a9a72

View File

@@ -41,7 +41,7 @@ module CoffeeScript
"(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()" "(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
end end
# Quick method for the current indentation level, plus tabs out. # Quick short method for the current indentation level, plus tabbing in.
def idt(tabs=0) def idt(tabs=0)
@indent + (TAB * tabs) @indent + (TAB * tabs)
end end
@@ -57,7 +57,8 @@ module CoffeeScript
statement statement
attr_reader :expressions attr_reader :expressions
STRIP_TRAILING_WHITESPACE = /\s+$/ TRAILING_WHITESPACE = /\s+$/
UPPERCASE = /[A-Z]/
# Wrap up a node as an Expressions, unless it already is. # Wrap up a node as an Expressions, unless it already is.
def self.wrap(*nodes) def self.wrap(*nodes)
@@ -69,12 +70,13 @@ module CoffeeScript
@expressions = nodes.flatten @expressions = nodes.flatten
end end
# Tack an expression onto the end of this node. # Tack an expression on to the end of this expression list.
def <<(node) def <<(node)
@expressions << node @expressions << node
self self
end end
# Tack an expression on to the beginning of this expression list.
def unshift(node) def unshift(node)
@expressions.unshift(node) @expressions.unshift(node)
self self
@@ -91,36 +93,19 @@ module CoffeeScript
node == @expressions[@last_index] node == @expressions[@last_index]
end 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={}) def compile(o={})
o[:scope] ? super(o) : compile_root(o) o[:scope] ? super(o) : compile_root(o)
end end
# The extra fancy is to handle pushing down returns to the final lines of # Compile each expression in the Expressions body.
# inner statements. Variables first defined within the Expressions body
# have their declarations pushed up top of the closest scope.
def compile_node(options={}) def compile_node(options={})
compiled = @expressions.map do |node| write(@expressions.map {|n| compile_expression(n, options.dup) }.join("\n"))
o = options.dup
@indent = o[:indent]
returns = o.delete(:return)
if last?(node) && returns && !node.statement_only?
if node.statement?
node.compile(o.merge(:return => true))
else
if o[:top] && o[:last_assign] && o[:last_assign][0..0][/[A-Z]/]
temp = o[:scope].free_variable
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
else
"#{idt}return #{node.compile(o)};"
end
end
else
ending = node.statement? ? '' : ';'
indent = node.statement? ? '' : idt
"#{indent}#{node.compile(o.merge(:top => true))}#{ending}"
end
end
write(compiled.join("\n"))
end end
# If this is the top-level Expressions, wrap everything in a safety closure. # If this is the top-level Expressions, wrap everything in a safety closure.
@@ -129,7 +114,7 @@ module CoffeeScript
@indent = indent @indent = indent
o.merge!(:indent => indent, :scope => Scope.new(nil, self)) o.merge!(:indent => indent, :scope => Scope.new(nil, self))
code = o[:globals] ? compile_node(o) : compile_with_declarations(o) code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
code.gsub!(STRIP_TRAILING_WHITESPACE, '') code.gsub!(TRAILING_WHITESPACE, '')
o[:no_wrap] ? code : "(function(){\n#{code}\n})();" o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
end end
@@ -140,19 +125,39 @@ module CoffeeScript
decls + code decls + code
end end
def compile_expression(node, o)
@indent = o[:indent]
stmt = node.statement?
# We need to return the result if this is the last node in the expressions body.
returns = o.delete(:return) && last?(node) && !node.statement_only?
# Return the regular compile of the node, unless we need to return the result.
return "#{stmt ? '' : idt}#{node.compile(o.merge(:top => true))}#{stmt ? '' : ';'}" unless returns
# 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)
# 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};"
end
end end
# Literals are static values that have a Ruby representation, eg.: a string, a number, # Literals are static values that have a Ruby representation, eg.: a string, a number,
# true, false, nil, etc. # true, false, nil, etc.
class LiteralNode < Node class LiteralNode < Node
# Values of a literal node that much be treated as a statement -- no
# sense returning or assigning them.
STATEMENTS = ['break', 'continue'] STATEMENTS = ['break', 'continue']
CONVERSIONS = { # If we get handed a literal reference to an arguments object, convert
'arguments' => 'Array.prototype.slice.call(arguments, 0)' # it to an array.
} ARG_ARRAY = 'Array.prototype.slice.call(arguments, 0)'
attr_reader :value attr_reader :value
# Wrap up a compiler-generated string as a LiteralNode.
def self.wrap(string) def self.wrap(string)
self.new(Value.new(string)) self.new(Value.new(string))
end end
@@ -167,14 +172,14 @@ module CoffeeScript
alias_method :statement_only?, :statement? alias_method :statement_only?, :statement?
def compile_node(o) def compile_node(o)
val = CONVERSIONS[@value.to_s] || @value.to_s @value = ARG_ARRAY if @value.to_s.to_sym == :arguments
indent = statement? ? idt : '' indent = statement? ? idt : ''
ending = statement? ? ';' : '' ending = statement? ? ';' : ''
write("#{indent}#{val}#{ending}") write "#{indent}#{@value}#{ending}"
end end
end end
# Try to return your expression, or tell it to return itself. # Return an expression, or wrap it in a closure and return it.
class ReturnNode < Node class ReturnNode < Node
statement_only statement_only
@@ -657,8 +662,7 @@ module CoffeeScript
else else
index_var = nil index_var = nil
source_part = "#{svar} = #{source.compile(o)};\n#{idt}" source_part = "#{svar} = #{source.compile(o)};\n#{idt}"
for_part = "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++" for_part = @object ? "#{ivar} in #{svar}" : "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++"
for_part = "#{ivar} in #{svar}" if @object
var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : '' var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : ''
end end
body = @body body = @body