mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-02-17 19:11:22 -05:00
more refactors to nodes
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
module CoffeeScript
|
module CoffeeScript
|
||||||
|
|
||||||
# The abstract base class for all CoffeeScript nodes.
|
# The abstract base class for all CoffeeScript nodes.
|
||||||
|
# All nodes are implement a "compile_node" method, which performs the
|
||||||
|
# code generation for that node. To compile a node, call the "compile"
|
||||||
|
# method, which wraps "compile_node" in some extra smarts, to know when the
|
||||||
|
# generated code should be wrapped up in a closure. An options hash is passed
|
||||||
|
# and cloned throughout, containing messages from higher in the AST,
|
||||||
|
# information about the current scope, and indentation level.
|
||||||
class Node
|
class Node
|
||||||
# Tabs are two spaces for pretty-printing.
|
# Tabs are two spaces for pretty-printing.
|
||||||
TAB = ' '
|
TAB = ' '
|
||||||
@@ -115,16 +121,18 @@ module CoffeeScript
|
|||||||
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!(TRAILING_WHITESPACE, '')
|
code.gsub!(TRAILING_WHITESPACE, '')
|
||||||
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
|
write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compile the expressions body, with declarations of all inner variables
|
||||||
|
# at the top.
|
||||||
def compile_with_declarations(o={})
|
def compile_with_declarations(o={})
|
||||||
code = compile_node(o)
|
code = compile_node(o)
|
||||||
decls = ''
|
return code unless o[:scope].declarations?(self)
|
||||||
decls = "#{idt}var #{o[:scope].declared_variables.join(', ')};\n" if o[:scope].declarations?(self)
|
write("#{idt}var #{o[:scope].declared_variables.join(', ')};\n#{code}")
|
||||||
decls + code
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compiles a single expression within the expression list.
|
||||||
def compile_expression(node, o)
|
def compile_expression(node, o)
|
||||||
@indent = o[:indent]
|
@indent = o[:indent]
|
||||||
stmt = node.statement?
|
stmt = node.statement?
|
||||||
@@ -207,8 +215,7 @@ module CoffeeScript
|
|||||||
|
|
||||||
def compile_node(o={})
|
def compile_node(o={})
|
||||||
delimiter = "\n#{idt}//"
|
delimiter = "\n#{idt}//"
|
||||||
comment = "#{delimiter}#{@lines.join(delimiter)}"
|
write("#{delimiter}#{@lines.join(delimiter)}")
|
||||||
write(comment)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -243,6 +250,7 @@ module CoffeeScript
|
|||||||
@arguments << argument
|
@arguments << argument
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compile a vanilla function call.
|
||||||
def compile_node(o)
|
def compile_node(o)
|
||||||
return write(compile_splat(o)) if splat?
|
return write(compile_splat(o)) if splat?
|
||||||
args = @arguments.map{|a| a.compile(o) }.join(', ')
|
args = @arguments.map{|a| a.compile(o) }.join(', ')
|
||||||
@@ -250,6 +258,7 @@ module CoffeeScript
|
|||||||
write("#{prefix}#{@variable.compile(o)}(#{args})")
|
write("#{prefix}#{@variable.compile(o)}(#{args})")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compile a call against the superclass's implementation of the current function.
|
||||||
def compile_super(args, o)
|
def compile_super(args, o)
|
||||||
methname = o[:last_assign]
|
methname = o[:last_assign]
|
||||||
arg_part = args.empty? ? '' : ", #{args}"
|
arg_part = args.empty? ? '' : ", #{args}"
|
||||||
@@ -258,6 +267,7 @@ module CoffeeScript
|
|||||||
"#{meth}.call(this#{arg_part})"
|
"#{meth}.call(this#{arg_part})"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compile a function call being passed variable arguments.
|
||||||
def compile_splat(o)
|
def compile_splat(o)
|
||||||
meth = @variable.compile(o)
|
meth = @variable.compile(o)
|
||||||
obj = @variable.source || 'this'
|
obj = @variable.source || 'this'
|
||||||
@@ -280,6 +290,7 @@ module CoffeeScript
|
|||||||
@sub_object, @super_object = sub_object, super_object
|
@sub_object, @super_object = sub_object, super_object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Hooking one constructor into another's prototype chain.
|
||||||
def compile_node(o={})
|
def compile_node(o={})
|
||||||
constructor = o[:scope].free_variable
|
constructor = o[:scope].free_variable
|
||||||
sub, sup = @sub_object.compile(o), @super_object.compile(o)
|
sub, sup = @sub_object.compile(o), @super_object.compile(o)
|
||||||
@@ -294,10 +305,10 @@ module CoffeeScript
|
|||||||
|
|
||||||
# A value, indexed or dotted into, or vanilla.
|
# A value, indexed or dotted into, or vanilla.
|
||||||
class ValueNode < Node
|
class ValueNode < Node
|
||||||
attr_reader :literal, :properties, :last, :source
|
attr_reader :base, :properties, :last, :source
|
||||||
|
|
||||||
def initialize(literal, properties=[])
|
def initialize(base, properties=[])
|
||||||
@literal, @properties = literal, properties
|
@base, @properties = base, properties
|
||||||
end
|
end
|
||||||
|
|
||||||
def <<(other)
|
def <<(other)
|
||||||
@@ -309,23 +320,23 @@ module CoffeeScript
|
|||||||
return !@properties.empty?
|
return !@properties.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Values are statements if their base is a statement.
|
||||||
def statement?
|
def statement?
|
||||||
@literal.is_a?(Node) && @literal.statement? && !properties?
|
@base.is_a?(Node) && @base.statement? && !properties?
|
||||||
end
|
end
|
||||||
|
|
||||||
def compile_node(o)
|
def compile_node(o)
|
||||||
only = o.delete(:only_first)
|
only = o.delete(:only_first)
|
||||||
props = only ? @properties[0...-1] : @properties
|
props = only ? @properties[0...-1] : @properties
|
||||||
parts = [@literal, props].flatten.map do |val|
|
parts = [@base, props].flatten.map {|val| val.compile(o) }
|
||||||
val.respond_to?(:compile) ? val.compile(o) : val.to_s
|
|
||||||
end
|
|
||||||
@last = parts.last
|
@last = parts.last
|
||||||
@source = parts.length > 1 ? parts[0...-1].join('') : nil
|
@source = parts.length > 1 ? parts[0...-1].join('') : nil
|
||||||
write(parts.join(''))
|
write(parts.join(''))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A dotted accessor into a part of a value.
|
# A dotted accessor into a part of a value, or the :: shorthand for
|
||||||
|
# an accessor into the object's prototype.
|
||||||
class AccessorNode < Node
|
class AccessorNode < Node
|
||||||
attr_reader :name
|
attr_reader :name
|
||||||
|
|
||||||
@@ -365,14 +376,6 @@ module CoffeeScript
|
|||||||
@exclusive
|
@exclusive
|
||||||
end
|
end
|
||||||
|
|
||||||
def less_operator
|
|
||||||
@exclusive ? '<' : '<='
|
|
||||||
end
|
|
||||||
|
|
||||||
def greater_operator
|
|
||||||
@exclusive ? '>' : '>='
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_variables(o)
|
def compile_variables(o)
|
||||||
@indent = o[:indent]
|
@indent = o[:indent]
|
||||||
@from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
|
@from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
|
||||||
@@ -381,12 +384,13 @@ module CoffeeScript
|
|||||||
end
|
end
|
||||||
|
|
||||||
def compile_node(o)
|
def compile_node(o)
|
||||||
|
return compile_array(o) unless o[:index]
|
||||||
idx, step = o.delete(:index), o.delete(:step)
|
idx, step = o.delete(:index), o.delete(:step)
|
||||||
return compile_array(o) unless idx
|
vars = "#{idx}=#{@from_var}"
|
||||||
vars = "#{idx}=#{@from_var}"
|
step = step ? step.compile(o) : '1'
|
||||||
step = step ? step.compile(o) : '1'
|
equals = @exclusive ? '' : '='
|
||||||
compare = "(#{@from_var} <= #{@to_var} ? #{idx} #{less_operator} #{@to_var} : #{idx} #{greater_operator} #{@to_var})"
|
compare = "(#{@from_var} <= #{@to_var} ? #{idx} <#{equals} #{@to_var} : #{idx} >#{equals} #{@to_var})"
|
||||||
incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
|
incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
|
||||||
write("#{vars}; #{compare}; #{incr}")
|
write("#{vars}; #{compare}; #{incr}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -645,8 +649,8 @@ module CoffeeScript
|
|||||||
|
|
||||||
def compile_node(o)
|
def compile_node(o)
|
||||||
top_level = o.delete(:top) && !o[:return]
|
top_level = o.delete(:top) && !o[:return]
|
||||||
range = @source.is_a?(ValueNode) && @source.literal.is_a?(RangeNode) && @source.properties.empty?
|
range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
|
||||||
source = range ? @source.literal : @source
|
source = range ? @source.base : @source
|
||||||
scope = o[:scope]
|
scope = o[:scope]
|
||||||
name_found = @name && scope.find(@name)
|
name_found = @name && scope.find(@name)
|
||||||
index_found = @index && scope.find(@index)
|
index_found = @index && scope.find(@index)
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ module CoffeeScript
|
|||||||
to_str.to_sym
|
to_str.to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compile(o={})
|
||||||
|
to_s
|
||||||
|
end
|
||||||
|
|
||||||
def inspect
|
def inspect
|
||||||
@value.inspect
|
@value.inspect
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ class ParserTest < Test::Unit::TestCase
|
|||||||
assert nodes.length == 1
|
assert nodes.length == 1
|
||||||
assign = nodes.first
|
assign = nodes.first
|
||||||
assert assign.is_a?(AssignNode)
|
assert assign.is_a?(AssignNode)
|
||||||
assert assign.variable.literal == 'a'
|
assert assign.variable.base == 'a'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parsing_an_object_literal
|
def test_parsing_an_object_literal
|
||||||
nodes = @par.parse("{one : 1\ntwo : 2}").expressions
|
nodes = @par.parse("{one : 1\ntwo : 2}").expressions
|
||||||
obj = nodes.first.literal
|
obj = nodes.first.base
|
||||||
assert obj.is_a?(ObjectNode)
|
assert obj.is_a?(ObjectNode)
|
||||||
assert obj.properties.first.variable.literal.value == "one"
|
assert obj.properties.first.variable.base.value == "one"
|
||||||
assert obj.properties.last.variable.literal.value == "two"
|
assert obj.properties.last.variable.base.value == "two"
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parsing_an_function_definition
|
def test_parsing_an_function_definition
|
||||||
@@ -39,17 +39,17 @@ class ParserTest < Test::Unit::TestCase
|
|||||||
def test_parsing_if_statement
|
def test_parsing_if_statement
|
||||||
the_if = @par.parse("clap_your_hands() if happy").expressions.first
|
the_if = @par.parse("clap_your_hands() if happy").expressions.first
|
||||||
assert the_if.is_a?(IfNode)
|
assert the_if.is_a?(IfNode)
|
||||||
assert the_if.condition.literal == 'happy'
|
assert the_if.condition.base == 'happy'
|
||||||
assert the_if.body.is_a?(CallNode)
|
assert the_if.body.is_a?(CallNode)
|
||||||
assert the_if.body.variable.literal == 'clap_your_hands'
|
assert the_if.body.variable.base == 'clap_your_hands'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parsing_array_comprehension
|
def test_parsing_array_comprehension
|
||||||
nodes = @par.parse("i for x, i in [10, 9, 8, 7, 6, 5] when i % 2 is 0").expressions
|
nodes = @par.parse("i for x, i in [10, 9, 8, 7, 6, 5] when i % 2 is 0").expressions
|
||||||
assert nodes.first.is_a?(ForNode)
|
assert nodes.first.is_a?(ForNode)
|
||||||
assert nodes.first.body.literal == 'i'
|
assert nodes.first.body.base == 'i'
|
||||||
assert nodes.first.filter.operator == '==='
|
assert nodes.first.filter.operator == '==='
|
||||||
assert nodes.first.source.literal.objects.last.literal.value == "5"
|
assert nodes.first.source.base.objects.last.base.value == "5"
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parsing_comment
|
def test_parsing_comment
|
||||||
@@ -66,7 +66,7 @@ class ParserTest < Test::Unit::TestCase
|
|||||||
nodes = @par.parse(File.read('test/fixtures/generation/each.coffee'))
|
nodes = @par.parse(File.read('test/fixtures/generation/each.coffee'))
|
||||||
assign = nodes.expressions[1]
|
assign = nodes.expressions[1]
|
||||||
assert assign.is_a?(AssignNode)
|
assert assign.is_a?(AssignNode)
|
||||||
assert assign.variable.literal == '_'
|
assert assign.variable.base == '_'
|
||||||
assert assign.value.is_a?(CodeNode)
|
assert assign.value.is_a?(CodeNode)
|
||||||
assert assign.value.params == ['obj', 'iterator', 'context']
|
assert assign.value.params == ['obj', 'iterator', 'context']
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user