lib/to_source/visitor.rb in to_source-0.1.3 vs lib/to_source/visitor.rb in to_source-0.2.0
- old
+ new
@@ -1,191 +1,1278 @@
module ToSource
+ # Converter from AST to source
class Visitor
- def initialize
+
+ # Create source code from AST node
+ #
+ # @param [Rubinius::AST::Node] node
+ # the node to convert to source code
+ #
+ # @return [String]
+ # returns the source code for ast node
+ #
+ # @api private
+ #
+ def self.run(node)
+ new(node).output
+ end
+
+ # Return the source code of AST
+ #
+ # @return [String]
+ #
+ # @api private
+ #
+ def output
+ @output.join
+ end
+
+ private
+
+ # Initialize visitor
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def initialize(node)
@output = []
@indentation = 0
+ dispatch(node)
end
- def emit(code)
- @output.push code
+ # Dispatch node
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def dispatch(node)
+ name = node.node_name
+ name = "#{name}_def" if %w[ class module ].include?(name)
+ __send__(name, node)
end
- def output
- @output.join
+ # Emit file
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def file(node)
+ emit('__FILE__')
end
+ # Emit element assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def element_assignment(node)
+ index, value = node.arguments.array
+ dispatch(node.receiver)
+ emit('[')
+ dispatch(index)
+ emit('] = ')
+ dispatch(value)
+ end
+
+ # Emit alias
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def alias(node)
+ emit("alias #{node.to.value} #{node.from.value}")
+ end
+
+ # Emit match operator
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def match3(node)
+ dispatch(node.value)
+ emit(' =~ ')
+ dispatch(node.pattern)
+ end
+
+ # Emit break
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def break(node)
+ emit('break')
+ end
+
+ # Emit next
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def next(node)
+ emit('next')
+ end
+
+ # Emit conditional element assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def op_assign1(node)
+ receiver(node)
+ emit('[')
+ dispatch(node.arguments.array.first)
+ emit('] ||= ')
+ dispatch(node.value)
+ end
+
+ # Emit attribute assignment after merge
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def op_assign2(node)
+ dispatch(node.receiver)
+ emit('.')
+ emit(node.name)
+ emit(' |= ')
+ dispatch(node.value)
+ end
+
+ # Emit rescue
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def rescue(node)
+ body(node.body)
+ rescue_condition(node.rescue)
+ end
+
+ # Emit rescue condition
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def rescue_condition(node)
+ emit('rescue')
+ if node.conditions
+ body = node.conditions.body
+ first = body.first
+ unless body.one? and first.kind_of?(Rubinius::AST::ConstantAccess) and first.name == :StandardError
+ emit(' ')
+ array_body(body)
+ end
+ end
+
+ if node.splat
+ emit(',') if node.conditions
+ emit(' ')
+ dispatch(node.splat)
+ end
+
+ if node.assignment
+ emit(' => ')
+ emit(node.assignment.name)
+ end
+ nl
+ body(node.body)
+
+ if node.next
+ dispatch(node.next)
+ end
+ end
+
+ # Emit rescue splat
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def rescue_splat(node)
+ emit('*')
+ dispatch(node.value)
+ end
+
+ # Emit ensure
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def ensure(node)
+ body(node.body)
+ emit('ensure')
+ nl
+ body(node.ensure)
+ end
+
+ # Emit attribute assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def attribute_assignment(node)
+ dispatch(node.receiver)
+ emit('.')
+ emit(node.name)
+ emit(' ')
+ actual_arguments(node.arguments)
+ end
+
+ # Emit body with taking care on indentation
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def body(node)
+ @indentation+=1
+ node =
+ case node
+ when Rubinius::AST::EmptyBody
+ node
+ when Rubinius::AST::Block
+ # Hack to correctly indent ensure or rescue
+ noindent = [Rubinius::AST::Ensure, Rubinius::AST::Rescue]
+ if node.array.one? && noindent.include?(node.array.first.class)
+ @indentation-=1
+ dispatch(node)
+ return
+ end
+ node
+ else
+ Rubinius::AST::Block.new(node.line, [node])
+ end
+
+ dispatch(node)
+ nl
+ @indentation-=1
+ end
+
+ # Emit end keyword
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def kend
+ emit(current_indentation)
+ emit('end')
+ end
+
+ # Emit newline
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def nl
+ emit("\n")
+ end
+
+ # Emit pice of code
+ #
+ # @param [String] code
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def emit(code)
+ @output << code
+ end
+
+ # Return current indentation
+ #
+ # @return [String]
+ #
+ # @api private
+ #
def current_indentation
' ' * @indentation
end
- def class_def(node, parent)
- emit "class %s" % node.name.name
+ # Emit dynamic regexp
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def dynamic_regex(node)
+ emit('/')
+ emit(node.string)
+ node.array.each do |member|
+ case member
+ when Rubinius::AST::ToString
+ emit('#{')
+ dispatch(member.value)
+ emit('}')
+ when Rubinius::AST::StringLiteral
+ emit(member.string)
+ end
+ end
+ emit('/')
+ end
+ # Emit dynamic string body
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def dynamic_string_body(node)
+ emit(node.string.inspect[1..-2])
+ node.array.each do |member|
+ case member
+ when Rubinius::AST::ToString
+ emit('#{')
+ dispatch(member.value)
+ emit('}')
+ when Rubinius::AST::StringLiteral
+ emit(member.string.inspect[1..-2])
+ end
+ end
+ end
+
+ # Emit dynamic execute string
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def dynamic_execute_string(node)
+ emit('`')
+ dynamic_string_body(node)
+ emit('`')
+ end
+
+ # Emit dynamic string
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def dynamic_string(node)
+ emit('"')
+ dynamic_string_body(node)
+ emit('"')
+ end
+
+ # Emit dynamic symbol
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def dynamic_symbol(node)
+ emit(':')
+ dynamic_string(node)
+ end
+
+ # Emit singleton class inheritance
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def s_class(node)
+ emit('class << ')
+ dispatch(node.receiver)
+ nl
+ # FIXME: attr_reader missing on Rubinius::AST::SClass
+ scope = node.instance_variable_get(:@body)
+ body = scope.body
+ if body
+ body(body)
+ end
+ kend
+ end
+
+ # Emit to array
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def to_array(node)
+ dispatch(node.value)
+ end
+
+ # Emit multiple assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def multiple_assignment(node)
+ body = node.left.body
+
+ array_body(node.left.body)
+
+ emit(' = ')
+
+ right = node.right
+
+ if node.right.kind_of?(Rubinius::AST::ArrayLiteral)
+ array_body(right.body)
+ else
+ dispatch(right)
+ end
+ end
+
+ # Emit constant assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def constant_assignment(node)
+ dispatch(node.constant)
+ emit(' = ')
+ dispatch(node.value)
+ end
+
+ # Emit negation
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def negate(node)
+ emit('-')
+ dispatch(node.value)
+ end
+
+ # Emit class definition
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def class_def(node)
+ emit('class ')
+
+ dispatch(node.name)
+
superclass = node.superclass
unless superclass.is_a?(Rubinius::AST::NilLiteral)
- emit " < %s" % superclass.name
+ emit ' < '
+ dispatch(superclass)
end
+ nl
- node.body.lazy_visit self, node, true
+ dispatch(node.body)
- emit "\n"
- emit "end"
+ kend
end
- def module_def(node, parent)
- emit "module %s" % node.name.name
+ # Emit class name
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def class_name(node)
+ emit(node.name)
+ end
- node.body.lazy_visit self, node, true
+ # Emit module name
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def module_name(node)
+ emit(node.name)
+ end
- emit "\n"
- emit "end"
+ # Emit module definition
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def module_def(node)
+ emit "module "
+ dispatch(node.name)
+ nl
+
+ dispatch(node.body)
+
+ kend
end
- def empty_body(*)
+ # Emit empty body
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def empty_body(node)
# do nothing
end
- def class_scope(node, parent, indent)
- emit "\n"
- @indentation += 1 if indent
- node.body.lazy_visit self, node, indent
- ensure
- @indentation -= 1 if indent
+ # Emit class scope
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def class_scope(node)
+ body(node.body)
end
- alias module_scope class_scope
- def local_variable_assignment(node, parent)
- emit "%s = " % node.name
- node.value.lazy_visit self, node
+ # Emit module scope
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def module_scope(node)
+ body(node.body)
end
- def local_variable_access(node, parent)
- emit node.name
+ # Emit class variable assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def class_variable_assignment(node)
+ if node.value
+ emit("#{node.name} = ")
+ dispatch(node.value)
+ else
+ emit(node.name)
+ end
end
- def instance_variable_assignment(node, parent)
- emit "%s = " % node.name
- node.value.lazy_visit self, node
+ # Emit local variable assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def local_variable_assignment(node)
+ if node.value
+ emit("#{node.name} = ")
+ dispatch(node.value)
+ else
+ emit(node.name)
+ end
end
- def instance_variable_access(node, parent)
- emit node.name
+ # Emit class variable
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def class_variable_access(node)
+ emit(node.name)
end
- def fixnum_literal(node, parent)
- emit node.value.to_s
+ # Emit local variable access
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def local_variable_access(node)
+ emit(node.name)
end
- def float_literal(node, parent)
- emit node.value.to_s
+ # Emit global variable access
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def global_variable_access(node)
+ emit(node.name)
end
- def string_literal(node, parent)
- emit '"' << node.string.to_s << '"'
+ # Emit global variable assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def global_variable_assignment(node)
+ if(node.value)
+ emit("%s = " % node.name)
+ dispatch(node.value)
+ else
+ emit(node.name)
+ end
end
- def symbol_literal(node, parent)
- emit ':' << node.value.to_s
+ # Emit nref global variable access
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def nth_ref(node)
+ emit("$#{node.which}")
end
- def true_literal(node, parent)
+ # Emit instance variable assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def instance_variable_assignment(node)
+ if(node.value)
+ emit("%s = " % node.name)
+ dispatch(node.value)
+ else
+ emit(node.name)
+ end
+ end
+
+ # Emit instance variable access
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def instance_variable_access(node)
+ emit(node.name)
+ end
+
+ # Emit defined check
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def defined(node)
+ emit('defined?(')
+ dispatch(node.expression)
+ emit(')')
+ end
+
+ # Emit fixnum literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def fixnum_literal(node)
+ emit(node.value.to_s)
+ end
+
+ # Emit float literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def float_literal(node)
+ emit(node.value.to_s)
+ end
+
+ # Emit string literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def string_literal(node)
+ emit(node.string.inspect)
+ end
+
+ # Emit execute string
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def execute_string(node)
+ emit("`#{node.string.inspect[1..-2]}`")
+ end
+
+ # Emit symbol literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def symbol_literal(node)
+ emit ":#{node.value.to_s}"
+ end
+
+ # Emit true literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def true_literal(node)
emit 'true'
end
- def false_literal(node, parent)
+ # Emit false literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def false_literal(node)
emit 'false'
end
- def nil_literal(node, parent)
+ # Emit nil literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def nil_literal(node)
emit 'nil'
end
- def array_literal(node, parent)
- body = node.body
+ # Emit argumentless super
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def z_super(node)
+ emit('super')
+ end
- emit '['
+ # Emit super
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def super(node)
+ z_super(node)
+ arguments(node)
+ end
+
+ # Emit concat args
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def concat_args(node)
+ emit('[')
+ array_body(node.array.body)
+ emit(', ')
+ emit('*')
+ dispatch(node.rest)
+ emit(']')
+ end
+
+ # Emit array body
+ #
+ # @param [Array] body
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def array_body(body)
body.each_with_index do |node, index|
- node.lazy_visit self, node
+ dispatch(node)
emit ', ' unless body.length == index + 1 # last element
end
- emit ']'
end
- def hash_literal(node, parent)
+ # Emit array literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def array_literal(node)
+ emit('[')
+ array_body(node.body)
+ emit(']')
+ end
+
+
+ # Emit emtpy array literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def empty_array(node)
+ emit('[]')
+ end
+
+ # Emit hash literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def hash_literal(node)
body = node.array.each_slice(2)
emit '{'
body.each_with_index do |slice, index|
key, value = slice
- key.lazy_visit self, node
+ dispatch(key)
emit " => "
- value.lazy_visit self, node
+ dispatch(value)
emit ', ' unless body.to_a.length == index + 1 # last element
end
emit '}'
end
- def range(node, parent)
- node.start.lazy_visit self, node
+ # Emit inclusive range literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def range(node)
+ dispatch(node.start)
emit '..'
- node.finish.lazy_visit self, node
+ dispatch(node.finish)
end
- def range_exclude(node, parent)
- node.start.lazy_visit self, node
+ # Emit exlusive range literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def range_exclude(node)
+ dispatch(node.start)
emit '...'
- node.finish.lazy_visit self, node
+ dispatch(node.finish)
end
- def regex_literal(node, parent)
+ # Emit range literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def regex_literal(node)
emit '/'
emit node.source
emit '/'
end
- def send(node, parent)
- unless node.receiver.is_a?(Rubinius::AST::Self)
- node.receiver.lazy_visit self, node
- emit '.'
+
+ # Emit receiver
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [true]
+ # returns true if there is an explicit receiver
+ #
+ # @return [false]
+ # returns false otherwise
+ #
+ # @api private
+ #
+ def receiver(node)
+ unless node.receiver.is_a?(Rubinius::AST::Self) and node.privately
+ dispatch(node.receiver)
+ true
+ else
+ false
end
- emit node.name
+ end
- if node.block
- emit ' '
- node.block.lazy_visit self, node if node.block
+ # Emit send literal
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def send(node)
+ if node.name == :'!'
+ emit('!')
+ dispatch(node.receiver)
+ return
end
+
+ if receiver(node)
+ emit('.')
+ end
+
+ emit(node.name)
+
+ block = node.block
+
+ if(block)
+ if block.kind_of?(Rubinius::AST::BlockPass)
+ emit('(')
+ block_pass(block)
+ emit(')')
+ else
+ dispatch(block)
+ end
+ end
end
- def send_with_arguments(node, parent)
- return if process_binary_operator(node, parent) # 1 * 2, a / 3, true && false
+ # Emit arguments
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def arguments(node, open='(', close=')')
+ arguments = node.arguments
+ array, block = arguments.array, node.block
+ return if array.empty? and block.nil? and arguments.splat.nil?
+
+ emit(open)
+
+ array_body(array)
+ is_block_pass = block.kind_of?(Rubinius::AST::BlockPass)
+
+ if arguments.splat
+ emit(', ') unless array.empty?
+ dispatch(arguments.splat)
+ end
+
+ if is_block_pass
+ emit(', ') unless array.empty?
+ block_pass(block)
+ end
+
+ emit(close)
+
+ if block and !is_block_pass
+ dispatch(node.block)
+ end
+ end
+
+ # Emit self
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def self(node)
+ emit('self')
+ end
+
+ # Emit element reference
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def element_reference(node)
unless node.receiver.is_a?(Rubinius::AST::Self)
- node.receiver.lazy_visit self, node
- emit '.'
+ dispatch(node.receiver)
end
- emit node.name
- emit '('
- node.arguments.lazy_visit self, node
- emit ')'
- if node.block
- emit ' '
- node.block.lazy_visit self, node if node.block
+ arguments(node,'[',']')
+ end
+
+ # Emit send with arguments
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def send_with_arguments(node)
+ if node.name == :[]
+ return element_reference(node)
end
+ return if process_binary_operator(node)
+
+ if receiver(node)
+ emit('.')
+ end
+
+ emit(node.name)
+
+ arguments(node)
end
- def actual_arguments(node, parent)
- body = node.array
- body.each_with_index do |argument, index|
- argument.lazy_visit self, parent
- emit ', ' unless body.length == index + 1 # last element
+ # Emit yield
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def yield(node)
+ emit('yield')
+ arguments(node)
+ end
+
+ # Emit receiver case statment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def receiver_case(node)
+ emit('case ')
+ dispatch(node.receiver)
+ nl
+ node.whens.each do |branch|
+ dispatch(branch)
end
+ else_body = node.else
+ unless else_body.kind_of?(Rubinius::AST::NilLiteral)
+ emit('else')
+ nl
+ body(else_body)
+ end
+ kend
end
- def iter_arguments(node, parent)
+ # Emit when
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def when(node)
+ emit('when ')
+ if node.single
+ dispatch(node.single)
+ end
+ if node.conditions
+ array_body(node.conditions.body)
+ end
+ if node.splat
+ dispatch(node.splat)
+ end
+ nl
+ body(node.body)
+ end
+
+ # Emit splat when
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def splat_when(node)
+ emit('*')
+ dispatch(node.condition)
+ end
+
+ # Emit splat value
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def splat_value(node)
+ emit('*')
+ dispatch(node.value)
+ end
+
+ # Emit acutal arguments
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def actual_arguments(node)
+ array_body(node.array)
+ end
+
+ # Emit iteration
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def iter(node)
+ emit(' do')
+
+ arguments = node.arguments
+ unless arguments.names.empty?
+ emit(' ')
+ iter_arguments(node.arguments)
+ end
+
+ nl
+ body(node.body)
+
+ kend
+ end
+
+ # Emit iteration arguments for ruby18 mode
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def iter_arguments(node)
body = if node.prelude == :single
Array(node.arguments.name)
else
node.arguments.left.body.map(&:name)
end
@@ -196,182 +1283,465 @@
emit ', ' unless body.length == index + 1 # last element
end
emit '|'
end
- def iter(node, parent)
- emit 'do'
+ # Emit iteration arguments for ruby19 mode
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def iter19(node)
+ emit(' do')
- if node.arguments && node.arguments.arity != -1
- emit ' '
- node.arguments.lazy_visit self, parent
+ arguments = node.arguments
+ unless arguments.names.empty?
+ emit(' ')
+ formal_arguments_generic(node.arguments,'|','|')
end
- emit "\n"
- @indentation += 1
+ nl
+ body(node.body)
- if node.body.is_a?(Rubinius::AST::Block)
- node.body.lazy_visit self, parent, true
- else
- emit current_indentation
- node.body.lazy_visit self, parent
- end
-
- emit "\n"
- emit 'end'
+ kend
end
- def block(node, parent, indent=false)
+ # Emit block
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def block(node)
body = node.array
- body.each_with_index do |expression, index|
- emit current_indentation if indent
- expression.lazy_visit self, parent
- emit "\n" unless body.length == index + 1 # last element
+ body.each_with_index do |expression,index|
+ emit(current_indentation)
+ dispatch(expression)
+ nl unless body.length == index+1
end
end
- def not(node, parent)
- emit '!'
- node.value.lazy_visit self, parent
+ # Emit not
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def not(node)
+ emit('!')
+ dispatch(node.value)
end
- def and(node, parent)
- node.left.lazy_visit self, node
- emit ' && '
- node.right.lazy_visit self, node
+ # Emit and
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def and(node)
+ dispatch(node.left)
+ emit(' && ')
+ dispatch(node.right)
end
- def or(node, parent)
- node.left.lazy_visit self, node
- emit ' || '
- node.right.lazy_visit self, node
+ # Emit or
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def or(node)
+ dispatch(node.left)
+ emit(' || ')
+ dispatch(node.right)
end
- def op_assign_and(node, parent)
- node.left.lazy_visit self, node
- emit ' && '
- node.right.lazy_visit self, node
+ # Emit and operation with assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def op_assign_and(node)
+ dispatch(node.left)
+ emit(' && ')
+ dispatch(node.right)
end
- def op_assign_or(node, parent)
- node.left.lazy_visit self, node
- emit ' || '
- node.right.lazy_visit self, node
+ # Emit or operation with assignment
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def op_assign_or(node)
+ dispatch(node.left)
+ emit(' || ')
+ dispatch(node.right)
end
+ alias_method :op_assign_or19, :op_assign_or
- def toplevel_constant(node, parent)
- emit "::"
- emit node.name
+ # Emit toplevel constant
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def toplevel_constant(node)
+ emit('::')
+ emit(node.name)
end
- def constant_access(node, parent)
- emit node.name
+ # Emit constant accesws
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def constant_access(node)
+ emit(node.name)
end
- def scoped_constant(node, parent)
- node.parent.lazy_visit self, node
- emit '::'
- emit node.name
+ # Emit scoped constant
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def scoped_constant(node)
+ dispatch(node.parent)
+ emit('::')
+ emit(node.name)
end
+ alias_method :scoped_class_name, :scoped_constant
+ alias_method :scoped_module_name, :scoped_constant
- def if(node, parent)
+ # Emit toplevel class name
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def toplevel_class_name(node)
+ emit("::#{node.name}")
+ end
+
+ # Emit if expression
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def if(node)
body, else_body = node.body, node.else
+
keyword = 'if'
if node.body.is_a?(Rubinius::AST::NilLiteral) && !node.else.is_a?(Rubinius::AST::NilLiteral)
-
body, else_body = else_body, body
keyword = 'unless'
end
- emit keyword << ' '
- node.condition.lazy_visit self, node
- emit "\n"
+ emit(keyword)
+ emit(' ')
+ dispatch(node.condition)
+ nl
- @indentation += 1
+ body(body)
- if body.is_a?(Rubinius::AST::Block)
- body.lazy_visit self, parent, true
- else
- emit current_indentation
- body.lazy_visit self, parent
- end
-
- emit "\n"
-
if else_body.is_a?(Rubinius::AST::NilLiteral)
- emit 'end'
+ kend
return
end
- emit "else\n"
+ emit('else')
+ nl
- if else_body.is_a?(Rubinius::AST::Block)
- else_body.lazy_visit self, parent, true
- else
- emit current_indentation
- else_body.lazy_visit self, parent
- end
+ body(else_body)
- emit "\n"
- emit 'end'
+ kend
end
- def while(node, parent)
+ # Dispatch node
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def while(node)
emit 'while '
- node.condition.lazy_visit self, node
- emit "\n"
+ dispatch(node.condition)
+ nl
- @indentation += 1
+ body(node.body)
- if node.body.is_a?(Rubinius::AST::Block)
- node.body.lazy_visit self, parent, true
- else
- emit current_indentation
- node.body.lazy_visit self, parent
+ kend
+ end
+
+ # Emit until
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def until(node)
+ emit 'until '
+ dispatch(node.condition)
+ nl
+
+ body(node.body)
+
+ kend
+ end
+
+ # Emit formal arguments as shared between ruby18 and ruby19 mode
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def formal_arguments_generic(node,open,close)
+ return if node.names.empty?
+ required, defaults, splat = node.required, node.defaults, node.splat
+
+ emit(open)
+ emit(required.join(', '))
+
+ empty = required.empty?
+
+ if defaults
+ emit(', ') unless empty
+ dispatch(node.defaults)
end
- emit "\n"
- emit "end"
+ if splat
+ emit(', ') unless empty
+ emit('*')
+ unless splat == :@unnamed_splat
+ emit(splat)
+ end
+ end
+
+ if node.block_arg
+ emit(', ') unless empty
+
+ dispatch(node.block_arg)
+ end
+
+ emit(close)
end
- def until(node, parent)
- emit 'until '
- node.condition.lazy_visit self, node
- emit "\n"
+ # Emit formal arguments for ruby19 and ruby18
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def formal_arguments(node)
+ formal_arguments_generic(node,'(',')')
+ end
+ alias_method :formal_arguments19, :formal_arguments
- @indentation += 1
+ # Emit block argument
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def block_argument(node)
+ emit('&')
+ emit(node.name)
+ end
- if node.body.is_a?(Rubinius::AST::Block)
- node.body.lazy_visit self, parent, true
+ # Emit default arguments
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def default_arguments(node)
+ last = node.arguments.length - 1
+ node.arguments.each_with_index do |argument, index|
+ dispatch(argument)
+ emit(',') unless index == last
+ end
+ end
+
+ # Emit begin
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def begin(node)
+ emit('begin')
+ nl
+
+ body = node.rescue
+ case body
+ when Rubinius::AST::Rescue
+ # Rescue is reserved keyword
+ __send__(:rescue,body)
+ when Rubinius::AST::Ensure
+ # Ensure is reserved keyword
+ __send__(:ensure,body)
else
- emit current_indentation
- node.body.lazy_visit self, parent
+ body(node.rescue)
end
- emit "\n"
- emit "end"
+ kend
end
- def return(node, parent)
- emit 'return '
- node.value.lazy_visit self, parent
+ # Emit define on instances
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def define(node)
+ emit('def ')
+
+ emit(node.name)
+ dispatch(node.arguments)
+ nl
+
+ body(node.body)
+ kend
end
- private
+ # Emit define on singletons
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def define_singleton(node)
+ emit('def ')
+ dispatch(node.receiver)
+ emit('.')
+ dispatch(node.body)
+ end
- def process_binary_operator(node, parent)
- operators = %w(+ - * / & | <<).map(&:to_sym)
- return false unless operators.include?(node.name)
- return false if node.arguments.array.length != 1
+ # Emit singleton scope
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def define_singleton_scope(node)
+ emit(node.name)
+ dispatch(node.arguments)
+ nl
+
+ body(node.body)
- operand = node.arguments.array[0]
+ kend
+ end
- unless node.receiver.is_a?(Rubinius::AST::Self)
- node.receiver.lazy_visit self, node
+ # Emit block pass
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def block_pass(node)
+ emit('&')
+ dispatch(node.body)
+ end
+
+ # Emit return statement
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [undefined]
+ #
+ # @api private
+ #
+ def return(node)
+ emit('return')
+ if node.value
+ emit(' ')
+ dispatch(node.value)
end
+ end
- emit ' ' << node.name.to_s << ' '
- operand.lazy_visit self, node
+ OPERATORS = %w(
+ + - * / & | && || << >> ==
+ === != <= < <=> > >= =~ !~ ^
+ **
+ ).map(&:to_sym).to_set
+
+ # Process binary operator
+ #
+ # @param [Rubinius::AST::Node] node
+ #
+ # @return [self]
+ # if node was handled
+ #
+ # @return [nil]
+ # otherwise
+ #
+ # @api private
+ #
+ def process_binary_operator(node)
+ name = node.name
+ return unless OPERATORS.include?(name)
+ return if node.arguments.array.length != 1
+
+ operand = node.arguments.array[0]
+
+ dispatch(node.receiver)
+
+ emit(" #{name.to_s} ")
+ dispatch(operand)
+
+ self
end
end
end