class Ikra::AST::Builder

Builds an Ikra (Ruby) AST from a Parser RubyGem AST

Public Class Methods

from_parser_ast(node) click to toggle source
# File lib/ast/builder.rb, line 8
def self.from_parser_ast(node)
    return self.new.from_parser_ast(node)
end

Public Instance Methods

from_parser_ast(node) click to toggle source
# File lib/ast/builder.rb, line 12
def from_parser_ast(node)
    return RootNode.new(single_child: translate_node(node))
end

Protected Instance Methods

extract_begin_single_statement(node, should_return = false) click to toggle source
# File lib/ast/builder.rb, line 145
def extract_begin_single_statement(node, should_return = false)
    next_node = node
    while next_node.type == :begin
        if next_node.children.size != 1
            raise AssertionError.new("Begin node contains more than one statement")
        end
        
        next_node = next_node.children[0]
    end
    
    return next_node
end
translate_and(node) click to toggle source
# File lib/ast/builder.rb, line 115
def translate_and(node)
    return SendNode.new(receiver: translate_node(node.children[0]),
        selector: :"&&",
        arguments: [translate_node(node.children[1])])
end
translate_array(node) click to toggle source
# File lib/ast/builder.rb, line 47
def translate_array(node)
    return ArrayNode.new(values: node.children.map do |n| translate_node(n) end)
end
translate_begin(node) click to toggle source
# File lib/ast/builder.rb, line 237
def translate_begin(node)
    return BeginNode.new(body_stmts: node.children.map do |stmt|
        translate_node(stmt) end)
end
translate_bool(node) click to toggle source
# File lib/ast/builder.rb, line 77
def translate_bool(node)
    return BoolLiteralNode.new(value: node.children[0])
end
translate_break(node) click to toggle source
# File lib/ast/builder.rb, line 212
def translate_break(node)
    return BreakNode.new
end
translate_const(node) click to toggle source
# File lib/ast/builder.rb, line 51
def translate_const(node)
    full_const_string = "#{node.children[1]}"
    current_const = node

    while current_const.children[0] != nil
        # First receiver is enclosing class/module
        current_const = current_const.children[0]

        if current_const.type != :const
            raise AssertionError.new("Expected :const")
        end

        full_const_string = current_const.children[1].to_s + "::" + full_const_string
    end

    return ConstNode.new(identifier: full_const_string)
end
translate_false(node) click to toggle source
# File lib/ast/builder.rb, line 85
def translate_false(node)
    return BoolLiteralNode.new(value: false)
end
translate_float(node) click to toggle source
# File lib/ast/builder.rb, line 73
def translate_float(node)
    return FloatLiteralNode.new(value: node.children[0])
end
translate_for(node) click to toggle source
# File lib/ast/builder.rb, line 158
def translate_for(node)
    if node.children[0].type == :lvasgn and extract_begin_single_statement(node.children[1]).type == :irange
        range = extract_begin_single_statement(node.children[1])
        
        return ForNode.new(iterator_identifier: node.children[0].children[0],
            range_from: translate_node(range.children[0]),
            range_to: translate_node(range.children[1]),
            body_stmts: wrap_in_begin(translate_node(node.children[2])))
    elsif node.children[0].type == :lvasgn and extract_begin_single_statement(node.children[1]).type == :erange
        # Convert exclusive range to inclusive range
        range = extract_begin_single_statement(node.children[1])
        range_to = translate_node(range.children[1])
        range_to_inclusive = SendNode.new(
            receiver: range_to,
            selector: :-,
            arguments: [IntLiteralNode.new(value: 1)])

        return ForNode.new(iterator_identifier: node.children[0].children[0],
            range_from: translate_node(range.children[0]),
            range_to: range_to_inclusive,
            body_stmts: wrap_in_begin(translate_node(node.children[2])))
    else
        raise NotImplementedError.new("Can only handle simple For loops at the moment")
    end
end
translate_hash(node) click to toggle source
# File lib/ast/builder.rb, line 101
def translate_hash(node)
    hash = {}

    node.children.each do |child|
        if child.type != :pair
            raise NotImplementedError.new("Expected :pair, found #{child.type}")
        end

        hash[translate_node(child.children[0])] = translate_node(child.children[1])
    end

    return HashNode.new(hash: hash)
end
translate_if(node) click to toggle source
# File lib/ast/builder.rb, line 139
def translate_if(node)
    return IfNode.new(condition: translate_node(node.children[0]),
        true_body_stmts: wrap_in_begin(translate_node(node.children[1])),
        false_body_stmts: wrap_in_begin(translate_node(node.children[2])))
end
translate_int(node) click to toggle source
# File lib/ast/builder.rb, line 69
def translate_int(node)
    return IntLiteralNode.new(value: node.children[0])
end
translate_ivar(node) click to toggle source
# File lib/ast/builder.rb, line 135
def translate_ivar(node)
    return IVarReadNode.new(identifier: node.children[0])
end
translate_kwbegin(node) click to toggle source
# File lib/ast/builder.rb, line 242
def translate_kwbegin(node)
    return BeginNode.new(body_stmts: node.children.map do |stmt|
        translate_node(stmt) end)
end
translate_lvar(node) click to toggle source
# File lib/ast/builder.rb, line 127
def translate_lvar(node)
    return LVarReadNode.new(identifier: node.children[0])
end
translate_lvasgn(node) click to toggle source
# File lib/ast/builder.rb, line 131
def translate_lvasgn(node)
    return LVarWriteNode.new(identifier: node.children[0], value: translate_node(node.children[1]))
end
translate_nil(node) click to toggle source
# File lib/ast/builder.rb, line 89
def translate_nil(node)
    return NilLiteralNode.new
end
translate_node(node) click to toggle source
# File lib/ast/builder.rb, line 31
def translate_node(node)
    if node == nil
        return nil
    else
        if node.is_a?(Array)
            # An array of nodes
            return node.map do |n|
                send("translate_#{n.type.to_s.gsub("-", "_")}".to_sym, n)
            end
        else
            # A single node
            return send("translate_#{node.type.to_s.gsub("-", "_")}".to_sym, node)
        end
    end
end
translate_or(node) click to toggle source
# File lib/ast/builder.rb, line 121
def translate_or(node)
    return SendNode.new(receiver: translate_node(node.children[0]),
        selector: :"||",
        arguments: [translate_node(node.children[1])])
end
translate_return(node) click to toggle source
# File lib/ast/builder.rb, line 216
def translate_return(node)
    return ReturnNode.new(value: translate_node(node.children[0]))
end
translate_send(node) click to toggle source
# File lib/ast/builder.rb, line 220
def translate_send(node)
    receiver = nil

    if node.children[0] == nil
        # Implicit receiver
        # TODO: Assuming Object for now, but this is not always correct
        receiver = ConstNode.new(identifier: :Object)
    else
        receiver = translate_node(node.children[0])
    end

    return SendNode.new(receiver: receiver,
        selector: node.children[1],
        arguments: node.children[2..-1].map do |arg|
            translate_node(arg) end)
end
translate_str(node) click to toggle source
# File lib/ast/builder.rb, line 97
def translate_str(node)
    return StringLiteralNode.new(value: node.children[0])
end
translate_sym(node) click to toggle source
# File lib/ast/builder.rb, line 93
def translate_sym(node)
    return SymbolLiteralNode.new(value: node.children[0])
end
translate_true(node) click to toggle source
# File lib/ast/builder.rb, line 81
def translate_true(node)
    return BoolLiteralNode.new(value: true)
end
translate_until(node) click to toggle source
# File lib/ast/builder.rb, line 196
def translate_until(node)
    return UntilNode.new(
        condition: (SendNode.new(receiver: translate_node(node.children[0]),
            selector: :^,
            arguments: [BoolLiteralNode.new(value: true)])),
        body_stmts: wrap_in_begin(translate_node(node.children[1])))
end
translate_until_post(node) click to toggle source
# File lib/ast/builder.rb, line 204
def translate_until_post(node)
    return UntilPostNode.new(
        condition: (SendNode.new(receiver: translate_node(node.children[0]),
            selector: :^,
            arguments: [BoolLiteralNode.new(value: true)])),
        body_stmts: wrap_in_begin(translate_node(node.children[1])))
end
translate_while(node) click to toggle source
# File lib/ast/builder.rb, line 184
def translate_while(node)
    return WhileNode.new(
        condition: translate_node(node.children[0]),
        body_stmts: wrap_in_begin(translate_node(node.children[1])))
end
translate_while_post(node) click to toggle source
# File lib/ast/builder.rb, line 190
def translate_while_post(node)
    return WhilePostNode.new(
        condition: translate_node(node.children[0]),
        body_stmts: wrap_in_begin(translate_node(node.children[1])))
end
wrap_in_begin(translated_node) click to toggle source
# File lib/ast/builder.rb, line 18
def wrap_in_begin(translated_node)
    if translated_node != nil
        if translated_node.is_a?(BeginNode)
            # Is already a `BeginNode`
            return translated_node
        else
            return BeginNode.new(body_stmts: [translated_node])
        end
    else
        return nil
    end
end