1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00
jashkenas--coffeescript/nodes.rb

253 lines
5.8 KiB
Ruby
Raw Normal View History

2009-12-13 18:37:29 -05:00
class Node
# Tabs are two spaces for pretty-printing.
TAB = ' '
def line_ending
';'
end
2009-12-13 20:29:44 -05:00
def compile(indent='', last=false)
end
2009-12-13 18:37:29 -05:00
end
2009-12-13 17:07:16 -05:00
# Collection of nodes each one representing an expression.
2009-12-13 18:37:29 -05:00
class Nodes < Node
2009-12-13 17:07:16 -05:00
attr_reader :nodes
def initialize(nodes)
@nodes = nodes
end
def <<(node)
@nodes << node
self
end
# Flatten redundant nested node lists until we have multiple nodes on the
# same level to work with.
def reduce
return nodes.first.reduce if nodes.length == 1 && nodes.first.is_a?(Nodes)
nodes
end
def compile(indent='')
2009-12-13 18:37:29 -05:00
reduce.map {|n| indent + n.compile(indent) + n.line_ending }.join("\n")
2009-12-13 17:07:16 -05:00
end
end
# Literals are static values that have a Ruby representation, eg.: a string, a number,
# true, false, nil, etc.
2009-12-13 18:37:29 -05:00
class LiteralNode < Node
2009-12-13 17:07:16 -05:00
def initialize(value)
@value = value
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 17:07:16 -05:00
@value.to_s
end
end
2009-12-13 18:37:29 -05:00
class ReturnNode < Node
def initialize(expression)
@expression = expression
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
"#{indent}return #{@expression.compile(indent)};"
2009-12-13 18:37:29 -05:00
end
end
2009-12-13 17:07:16 -05:00
# Node of a method call or local variable access, can take any of these forms:
#
# method # this form can also be a local variable
# method(argument1, argument2)
# receiver.method
# receiver.method(argument1, argument2)
#
2009-12-13 18:37:29 -05:00
class CallNode < Node
2009-12-13 20:29:44 -05:00
def initialize(variable, arguments=[], new_instance=false)
@variable, @arguments, @new = variable, arguments, new_instance
2009-12-13 17:07:16 -05:00
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 17:07:16 -05:00
args = @arguments.map{|a| a.compile(indent) }.join(', ')
2009-12-13 20:29:44 -05:00
prefix = @new ? "new " : ''
"#{prefix}#{@variable.compile(indent)}(#{args})"
2009-12-13 17:07:16 -05:00
end
end
2009-12-13 18:37:29 -05:00
class VariableNode < Node
2009-12-13 20:29:44 -05:00
def initialize(name, properties=[])
@name, @properties = name, properties
2009-12-13 17:07:16 -05:00
end
def <<(other)
@properties << other
self
end
2009-12-13 18:37:29 -05:00
def properties?
return !@properties.empty?
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
[@name, @properties].flatten.map { |v|
v.respond_to?(:compile) ? v.compile(indent) : v.to_s
}.join('')
end
end
class AccessorNode
def initialize(name)
@name = name
end
def compile(indent, last=false)
".#{@name}"
end
end
class IndexNode
def initialize(index)
@index = index
end
def compile(indent, last=false)
"[#{@index.compile(indent)}]"
2009-12-13 17:07:16 -05:00
end
end
# Setting the value of a local variable.
2009-12-13 18:37:29 -05:00
class AssignNode < Node
2009-12-13 17:07:16 -05:00
def initialize(variable, value, context=nil)
@variable, @value, @context = variable, value, context
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 17:07:16 -05:00
return "#{@variable}: #{@value.compile(indent + TAB)}" if @context == :object
2009-12-13 18:37:29 -05:00
var_part = @variable.compile(indent)
2009-12-13 20:29:44 -05:00
var_part = "var " + var_part unless @variable.properties? || last
2009-12-13 18:37:29 -05:00
"#{var_part} = #{@value.compile(indent)}"
2009-12-13 17:07:16 -05:00
end
end
# Simple Arithmetic and logical operations
2009-12-13 18:37:29 -05:00
class OpNode < Node
2009-12-13 17:07:16 -05:00
CONVERSIONS = {
"==" => "===",
"!=" => "!==",
'and' => '&&',
'or' => '||',
'is' => '===',
"aint" => "!==",
'not' => '!',
}
2009-12-13 20:29:44 -05:00
CONDITIONALS = ['||=', '&&=']
2009-12-13 17:07:16 -05:00
def initialize(operator, first, second=nil)
@first, @second = first, second
@operator = CONVERSIONS[operator] || operator
end
def unary?
@second.nil?
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
return compile_conditional(indent) if CONDITIONALS.include?(@operator)
2009-12-13 17:07:16 -05:00
"(#{@first.compile(indent)} #{@operator} #{@second.compile(indent)})"
end
2009-12-13 20:29:44 -05:00
def compile_conditional(indent)
first, second = @first.compile(indent), @second.compile(indent)
sym = @operator[0..1]
"(#{first} = #{first} #{sym} #{second})"
end
2009-12-13 17:07:16 -05:00
end
# Method definition.
2009-12-13 18:37:29 -05:00
class CodeNode < Node
2009-12-13 17:07:16 -05:00
def initialize(params, body)
@params = params
@body = body
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 23:25:00 -05:00
nodes = @body.respond_to?(:reduce) ? @body.reduce : [@body]
2009-12-13 18:37:29 -05:00
code = nodes.map { |node|
2009-12-13 20:29:44 -05:00
last = node == nodes.last
line = node.compile(indent + TAB, last)
line = "return #{line}" if last
2009-12-13 18:37:29 -05:00
indent + TAB + line + node.line_ending
}.join("\n")
"function(#{@params.join(', ')}) {\n#{code}\n#{indent}}"
2009-12-13 17:07:16 -05:00
end
end
2009-12-13 18:37:29 -05:00
class ObjectNode < Node
2009-12-13 17:07:16 -05:00
def initialize(properties = [])
@properties = properties
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 17:07:16 -05:00
props = @properties.map {|p| indent + TAB + p.compile(indent) }.join(",\n")
"{\n#{props}\n#{indent}}"
end
end
2009-12-13 18:37:29 -05:00
class ArrayNode < Node
2009-12-13 17:07:16 -05:00
def initialize(objects=[])
@objects = objects
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 17:07:16 -05:00
objects = @objects.map {|o| o.compile(indent) }.join(', ')
"[#{objects}]"
end
end
# "if-else" control structure. Look at this node if you want to implement other control
# structures like while, for, loop, etc.
2009-12-13 18:37:29 -05:00
class IfNode < Node
FORCE_STATEMENT = [Nodes, ReturnNode]
2009-12-13 17:07:16 -05:00
def initialize(condition, body, else_body=nil)
@condition, @body, @else_body = condition, body, else_body
end
2009-12-13 18:37:29 -05:00
def statement?
FORCE_STATEMENT.include?(@body.class) || FORCE_STATEMENT.include?(@else_body.class)
2009-12-13 17:07:16 -05:00
end
2009-12-13 18:37:29 -05:00
def line_ending
statement? ? '' : ';'
2009-12-13 17:07:16 -05:00
end
2009-12-13 20:29:44 -05:00
def compile(indent, last=false)
2009-12-13 18:37:29 -05:00
statement? ? compile_statement(indent) : compile_ternary(indent)
end
def compile_statement(indent)
2009-12-13 20:29:44 -05:00
if_part = "if (#{@condition.compile(indent)}) {\n#{@body.compile(indent + TAB)}\n#{indent}}"
2009-12-13 18:37:29 -05:00
else_part = @else_body ? " else {\n#{@else_body.compile(indent + TAB)}\n#{indent}}" : ''
if_part + else_part
end
def compile_ternary(indent)
2009-12-13 17:07:16 -05:00
if_part = "#{@condition.compile(indent)} ? #{@body.compile(indent)}"
else_part = @else_body ? "#{@else_body.compile(indent)}" : 'null'
"#{if_part} : #{else_part}"
end
2009-12-13 18:37:29 -05:00
end
2009-12-13 23:25:00 -05:00
class ParentheticalNode < Node
def initialize(expressions)
@expressions = expressions
end
def compile(indent, last=false)
compiled = @expressions.compile(indent)
compiled = compiled[0...-1] if compiled[-1..-1] == ';'
"(#{compiled})"
end
end