mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-02-18 03:21:20 -05:00
major internal reworking -- all variable declarations have been pushed up to the first line of the block scope -- all assignment is now an inherent expression
This commit is contained in:
@@ -119,8 +119,8 @@ rule
|
||||
|
||||
# Assignment within an object literal.
|
||||
AssignObj:
|
||||
IDENTIFIER ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) }
|
||||
| STRING ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) }
|
||||
IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
|
||||
| STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
|
||||
| Comment { result = val[0] }
|
||||
;
|
||||
|
||||
@@ -143,10 +143,10 @@ rule
|
||||
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
|
||||
| NOT Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '~' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '--' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '++' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
| '--' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '++' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
|
||||
| Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
@@ -77,16 +77,18 @@ module CoffeeScript
|
||||
# If this is the top-level Expressions, wrap everything in a safety closure.
|
||||
def root_compile(o={})
|
||||
indent = o[:no_wrap] ? '' : TAB
|
||||
code = compile(o.merge(:indent => indent, :scope => Scope.new))
|
||||
code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
|
||||
code.gsub!(STRIP_TRAILING_WHITESPACE, '')
|
||||
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
|
||||
end
|
||||
|
||||
# The extra fancy is to handle pushing down returns and assignments
|
||||
# recursively to the final lines of inner statements.
|
||||
def compile(options={})
|
||||
# Variables first defined within the Expressions body have their
|
||||
# declarations pushed up to the top scope.
|
||||
def compile(options={}, parent=nil)
|
||||
return root_compile(options) unless options[:scope]
|
||||
code = @expressions.map { |node|
|
||||
compiled = @expressions.map do |node|
|
||||
o = super(options)
|
||||
if last?(node) && (o[:return] || o[:assign])
|
||||
if o[:return]
|
||||
@@ -99,14 +101,17 @@ module CoffeeScript
|
||||
if node.statement? || node.custom_assign?
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
else
|
||||
"#{o[:indent]}#{AssignNode.new(ValueNode.new(LiteralNode.new(o[:assign])), node).compile(o)};"
|
||||
"#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
|
||||
end
|
||||
end
|
||||
else
|
||||
o.delete(:return) and o.delete(:assign)
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
end
|
||||
}.join("\n")
|
||||
end
|
||||
scope = options[:scope]
|
||||
declarations = scope.any_declared? && parent == :code ? "#{options[:indent]}var #{scope.declared_variables.join(', ')};\n" : ''
|
||||
code = declarations + compiled.join("\n")
|
||||
write(code)
|
||||
end
|
||||
end
|
||||
@@ -338,10 +343,8 @@ module CoffeeScript
|
||||
|
||||
# Setting the value of a local variable, or the value of an object property.
|
||||
class AssignNode < Node
|
||||
LEADING_VAR = /\Avar\s+/
|
||||
PROTO_ASSIGN = /\A(\S+)\.prototype/
|
||||
|
||||
statement
|
||||
custom_return
|
||||
|
||||
attr_reader :variable, :value, :context
|
||||
@@ -356,19 +359,16 @@ module CoffeeScript
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable.to_s
|
||||
last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
|
||||
name = @variable.compile(o)
|
||||
last = @variable.last.to_s
|
||||
proto = name[PROTO_ASSIGN, 1]
|
||||
o = o.merge(:assign => name, :last_assign => last, :proto_assign => proto)
|
||||
o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
|
||||
postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
|
||||
return write("#{@variable}: #{@value.compile(o)}") if @context == :object
|
||||
return write("#{name}: #{@value.compile(o)}") if @context == :object
|
||||
return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
|
||||
defined = o[:scope].find(name)
|
||||
def_part = defined || @variable.properties? || o[:no_wrap] ? "" : "var #{name};\n#{o[:indent]}"
|
||||
return write(def_part + @value.compile(o)) if @value.custom_assign?
|
||||
def_part = defined || o[:no_wrap] ? name : "var #{name}"
|
||||
val_part = @value.compile(o).sub(LEADING_VAR, '')
|
||||
write("#{def_part} = #{val_part}#{postfix}")
|
||||
o[:scope].find(name) unless @variable.properties?
|
||||
return write(@value.compile(o)) if @value.custom_assign?
|
||||
write("#{name} = #{@value.compile(o)}#{postfix}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -436,8 +436,8 @@ module CoffeeScript
|
||||
o[:indent] += TAB
|
||||
o.delete(:assign)
|
||||
o.delete(:no_wrap)
|
||||
@params.each {|id| o[:scope].find(id.to_s) }
|
||||
code = @body.compile(o)
|
||||
@params.each {|id| o[:scope].parameter(id.to_s) }
|
||||
code = @body.compile(o, :code)
|
||||
write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
|
||||
end
|
||||
end
|
||||
@@ -533,20 +533,19 @@ module CoffeeScript
|
||||
ivar = scope.free_variable
|
||||
lvar = scope.free_variable
|
||||
rvar = scope.free_variable
|
||||
name_part = name_found ? @name : "var #{@name}"
|
||||
index_name = @index ? (index_found ? @index : "var #{@index}") : nil
|
||||
source_part = "var #{svar} = #{@source.compile(o)};"
|
||||
for_part = "var #{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
|
||||
var_part = "\n#{o[:indent] + TAB}#{name_part} = #{svar}[#{ivar}];\n"
|
||||
index_name = @index ? @index : nil
|
||||
source_part = "#{svar} = #{@source.compile(o)};"
|
||||
for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
|
||||
var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];\n"
|
||||
index_part = @index ? "#{o[:indent] + TAB}#{index_name} = #{ivar};\n" : ''
|
||||
body = @body
|
||||
suffix = ';'
|
||||
set_result = "var #{rvar} = [];\n#{o[:indent]}"
|
||||
set_result = "#{rvar} = [];\n#{o[:indent]}"
|
||||
save_result = "#{rvar}[#{ivar}] = "
|
||||
return_result = rvar
|
||||
|
||||
if o[:return] || o[:assign]
|
||||
return_result = "#{o[:assign]} = #{return_result}" if o[:assign]
|
||||
return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign]
|
||||
return_result = "return #{return_result}" if o[:return]
|
||||
if @filter
|
||||
body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
|
||||
|
||||
@@ -5,7 +5,7 @@ module CoffeeScript
|
||||
# whether a variable has been seen before or if it needs to be declared.
|
||||
class Scope
|
||||
|
||||
attr_reader :parent, :temp_variable
|
||||
attr_reader :parent, :variables, :temp_variable
|
||||
|
||||
# Initialize a scope with its parent, for lookups up the chain.
|
||||
def initialize(parent=nil)
|
||||
@@ -18,10 +18,16 @@ module CoffeeScript
|
||||
def find(name, remote=false)
|
||||
found = check(name, remote)
|
||||
return found if found || remote
|
||||
@variables[name.to_sym] = true
|
||||
@variables[name.to_sym] = :var
|
||||
found
|
||||
end
|
||||
|
||||
# Define a local variable as originating from a parameter in current scope
|
||||
# -- no var required.
|
||||
def parameter(name)
|
||||
@variables[name.to_sym] = :param
|
||||
end
|
||||
|
||||
# Just check to see if a variable has already been declared.
|
||||
def check(name, remote=false)
|
||||
return true if @variables[name.to_sym]
|
||||
@@ -36,10 +42,19 @@ module CoffeeScript
|
||||
# Find an available, short, name for a compiler-generated variable.
|
||||
def free_variable
|
||||
@temp_variable.succ! while check(@temp_variable)
|
||||
@variables[@temp_variable.to_sym] = true
|
||||
@variables[@temp_variable.to_sym] = :var
|
||||
@temp_variable.dup
|
||||
end
|
||||
|
||||
def any_declared?
|
||||
!declared_variables.empty?
|
||||
end
|
||||
|
||||
# Return the list of variables first declared in current scope.
|
||||
def declared_variables
|
||||
@variables.select {|k, v| v == :var }.map {|pair| pair[0].to_s }.sort
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user