class Ikra::Translator::ASTTranslator::ExpressionTranslator

Attributes

translator[R]

This class should not inherit from [AST::Visitor]. Otherwise, the dispatch mechanisum to [StatementTranslator] will not work properly anymore.

Public Class Methods

new(translator) click to toggle source
# File lib/translator/ast_translator.rb, line 17
def initialize(translator)
    @translator = translator
end

Public Instance Methods

build_switch_for_args(nodes, accumulator = []) { |accumulator| ... } click to toggle source
# File lib/translator/ast_translator.rb, line 247
def build_switch_for_args(nodes, accumulator = [], &block)
    if nodes.size == 0
        # This was the last argument, we are done with nesting switch
        # stmts. The accumulator contains all singleton-typed self_nodes.
        return yield(accumulator)
    end

    next_node = nodes.first

    if next_node.get_type.is_singleton?
        # This node has a singleton type. We're done with this one.
        return build_switch_for_args(nodes.drop(1), accumulator + [next_node], &block)
    elsif next_node.get_type.size == 0
        raise "AssertionError: Found empty UnionType"
    else
        return generate_polymorphic_switch(next_node) do |sing_node|
            build_switch_for_args(nodes.drop(1), accumulator + [sing_node], &block)
        end
    end
end
build_synthetic_code_node(code, type) click to toggle source

Builds a synthetic [AST::SourceCodeExprNode] with a type and a translation.

# File lib/translator/ast_translator.rb, line 140
def build_synthetic_code_node(code, type)
    node = AST::SourceCodeExprNode.new(code: code)
    node.merge_union_type(type.to_union_type)
    return node
end
expression_translator() click to toggle source
# File lib/translator/ast_translator.rb, line 21
def expression_translator
    return self
end
generate_polymorphic_switch(node, &block) click to toggle source
# File lib/translator/ast_translator.rb, line 146
def generate_polymorphic_switch(node, &block)
    poly_id = temp_identifier_id
    node_identifer = "_polytemp_expr_#{poly_id}"
    header = "#{define_assign_variable(node_identifer, node)}\nswitch (#{node_identifer}.class_id)\n"
    case_statements = []

    if node.get_type.size == 0
        raise AssertionError.new("Cannot generate switch for empty UnionType")
    end

    for type in node.get_type
        if type == Types::PrimitiveType::Int
            self_node = build_synthetic_code_node(
                "#{node_identifer}.value.int_", type)
        elsif type == Types::PrimitiveType::Float
            self_node = build_synthetic_code_node(
                "#{node_identifer}.value.float_", type)
        elsif type == Types::PrimitiveType::Bool
            self_node = build_synthetic_code_node(
                "#{node_identifer}.value.bool_", type)
        elsif type == Types::PrimitiveType::Nil
            self_node = build_synthetic_code_node(
                "#{node_identifer}.value.int_", type)
        elsif type.is_a?(Symbolic::ArrayCommand)
            self_node = build_synthetic_code_node(
                "(#{type.to_c_type}) #{node_identifer}.value.pointer", type)
        elsif type.is_a?(Types::LocationAwareArrayType)
            # TODO: Should not use variable_size_array for fixed size arrays
            self_node = build_synthetic_code_node(
                "#{node_identifer}.value.variable_size_array", type)
        else
            self_node = build_synthetic_code_node(
                "#{node_identifer}.value.object_id", type)
        end

        debug_info = "#{type.to_s} (#{type.to_ruby_type.to_s})"

        case_statements.push("case #{type.class_id}: /* #{debug_info} */ #{yield(self_node)} break;")
    end

    return header + wrap_in_c_block(case_statements.join("\n"))
end
generate_send_for_singleton(node, singleton_recv, return_type) click to toggle source
# File lib/translator/ast_translator.rb, line 268
def generate_send_for_singleton(node, singleton_recv, return_type)
    recv_type = singleton_recv.get_type.singleton_type

    if RubyIntegration.is_interpreter_only?(recv_type)
        # No translation necessary
        return nil
    elsif RubyIntegration.has_implementation?(recv_type, node.selector)
        # Some implementations accept only singleton-typed arguments
        if RubyIntegration.expect_singleton_args?(recv_type, node.selector)
            # Generate additional switch statements (one per non-sing. arg.).
            # Go through all possible combinations of types (for arguments).
            result_identifier = "_polytemp_result_#{temp_identifier_id}"
            declare_result_var = "#{return_type.to_c_type} #{result_identifier};\n"

            case_stmts = build_switch_for_args(node.arguments) do |all_sing_args|
                # TODO: Do we really have to redo type inference here?
                all_sing_arg_types = all_sing_args.map do |arg|
                    arg.get_type
                end

                this_return_type = RubyIntegration.get_return_type(
                    singleton_recv.get_type.singleton_type, 
                    node.selector, 
                    *all_sing_arg_types, 
                    send_node: node)

                impl = RubyIntegration.get_implementation(
                    singleton_recv,
                    node.selector, 
                    all_sing_args, 
                    translator,
                    this_return_type)

                if this_return_type.is_singleton? and
                    !return_type.is_singleton?

                    impl = wrap_in_union_type(
                        impl, 
                        this_return_type.singleton_type)
                end

                "#{result_identifier} = #{impl};"
            end

            return "(" + wrap_in_c_block(
                declare_result_var + 
                wrap_in_c_block(case_stmts) + 
                    result_identifier + ";")[0..-2] + ")"
        else
            # The easy case: Anything is fine (but might fail in ruby_integration)
            return RubyIntegration.get_implementation(
                singleton_recv,
                node.selector, 
                node.arguments, 
                translator,
                return_type)
        end
    elsif recv_type.is_a?(Types::StructType)
        first_arg = node.arguments.first

        if first_arg.is_a?(AST::IntLiteralNode)
            # Reading the struct at a constant position
            return recv_type.generate_read(
                singleton_recv.accept(self), 
                node.selector, 
                first_arg.accept(self))
        else
            # Reading the struct at a non-constant position
            id = temp_identifier_id
            name = "_temp_var_#{id}"
            first_arg_eval = first_arg.accept(self)

            # Store index in local variable, then generate non-constant access
            # TODO: Statement expression is potentially inefficient
            return "({ int #{name} = #{first_arg_eval};\n" +
                recv_type.generate_non_constant_read(
                    singleton_recv.accept(self),
                    node.selector,
                    name) + "; })"
        end
    else
        args = [Constants::ENV_IDENTIFIER]

        if recv_type.should_generate_self_arg?
            args.push(singleton_recv.accept(self))
        else
            args.push("NULL")
        end

        args.push(*(node.arguments.map do |arg| arg.accept(self) end))
        args_string = args.join(", ")

        return "#{node.receiver.get_type.singleton_type.mangled_method_name(node.selector)}(#{args_string})"
    end
end
method_missing(symbol, *args) click to toggle source
Calls superclass method
# File lib/translator/ast_translator.rb, line 29
def method_missing(symbol, *args)
    if symbol.to_s.start_with?("visit_")
        if statement_translator.respond_to?(symbol)
            return statements_as_expression(
                statement_translator.send(symbol, *args) + "return NULL;")
        else
            super
        end
    else
        return translator.send(symbol, *args)
    end
end
statement_translator() click to toggle source
# File lib/translator/ast_translator.rb, line 25
def statement_translator
    return translator.statement_translator
end
visit_array_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 87
def visit_array_node(node)
    elements = node.values.map do |value|
        value.accept(self)
    end
    
    return "(new #{node.get_type.to_c_type[0..-2]} {#{elements.join(', ')}})"
end
visit_begin_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 124
def visit_begin_node(node)
    if node.body_stmts.size == 0
        raise AssertionError.new("Empty BeginNode cannot be an expression")
    elsif node.body_stmts.size == 1
        # Preserve brackets
        return "(#{node.body_stmts.first.accept(self)})"
    else
        # Wrap in lambda
        # Do not worry about scope of varibles, they will all be declared at the
        # beginning of the function
        node.accept(LastStatementReturnsVisitor.new)
        return statements_as_expression(node.accept(statement_translator))
    end
end
visit_behavior_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 42
def visit_behavior_node(node)
    raise AssertionError.new(
        "Methods/blocks cannot be translated as an expression")
end
visit_bool_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 107
def visit_bool_node(node)
    return node.value.to_s
end
visit_const_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 56
def visit_const_node(node)
    raise NotImplementedError.new
end
visit_float_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 103
def visit_float_node(node)
    return node.value.to_s
end
visit_hash_node(node) click to toggle source

TODO: Should never translate a hash node (check `with_index` in host section)

# File lib/translator/ast_translator.rb, line 52
def visit_hash_node(node)
    return ""
end
visit_if_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 111
def visit_if_node(node)
    # Make every branch return
    node.accept(LastStatementReturnsVisitor.new)

    # Wrap in StatementExpression
    return statements_as_expression(node.accept(statement_translator))
end
visit_int_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 95
def visit_int_node(node)
    return node.value.to_s
end
visit_ivar_read_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 82
def visit_ivar_read_node(node)
    array_identifier = node.enclosing_class.ruby_class.to_ikra_type.inst_var_array_name(identifier)
    return "#{Constants::ENV_IDENTIFIER}->#{array_identifier}[#{Constants::SELF_IDENTIFIER}]"
end
visit_lvar_read_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 60
def visit_lvar_read_node(node)
    return node.mangled_identifier.to_s
end
visit_lvar_write_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 64
def visit_lvar_write_node(node)
    if node.value.get_type.is_singleton? and !node.symbol_table[node.identifier].is_singleton?
        # The assigned value is singleton, but the variable is not
        singleton_assignment = wrap_in_union_type(
            node.value.accept(expression_translator), 
            node.value.get_type.singleton_type)
        return Translator.read_file(file_name: "ast/assignment.cpp",
            replacements: { 
                "source" => singleton_assignment,
                "target" => node.mangled_identifier.to_s})
    else
        return Translator.read_file(file_name: "ast/assignment.cpp",
            replacements: { 
                "source" => node.value.accept(expression_translator),
                "target" => node.mangled_identifier.to_s})
    end
end
visit_nil_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 99
def visit_nil_node(node)
    return "0"
end
visit_return_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 364
def visit_return_node(node)
    raise AssertionError.new("ReturnNode is never an expression")
end
visit_send_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 189
def visit_send_node(node)
    if node.receiver.get_type.is_singleton?
        return_type = node.return_type_by_recv_type[
            node.receiver.get_type.singleton_type]

        invocation = generate_send_for_singleton(
            node, 
            node.receiver,
            return_type)

            if return_type.is_singleton? and
                !node.get_type.is_singleton?

                invocation = wrap_in_union_type(
                    invocation, 
                    return_type.singleton_type)
            end

            return invocation
    else
        # Polymorphic case
        result_identifier = "_polytemp_result_#{temp_identifier_id}"
        declare_result_var = "#{node.get_type.to_c_type} #{result_identifier};\n"

        case_statements = generate_polymorphic_switch(node.receiver) do |self_node|
            # The singleton type in the current case
            type = self_node.get_type.singleton_type

            # The return type (result type) in the current case (could be polym.)
            return_type = node.return_type_by_recv_type[type]

            # Generate method invocation
            invocation = generate_send_for_singleton(
                node, 
                self_node,
                return_type)

            if return_type.is_singleton? and
                !node.get_type.is_singleton?
                # The return value of this particular invocation (singleton type
                # recv) is singleton, but in general this send can return many
                # types
                invocation = wrap_in_union_type(
                    invocation, 
                    return_type.singleton_type)
            end

            "#{result_identifier} = #{invocation};"
        end

        # TODO: compound statements only work with the GNU C++ compiler
        return "(" + wrap_in_c_block(
            declare_result_var + 
            wrap_in_c_block(case_statements) + 
                result_identifier + ";")[0..-2] + ")"
    end
end
visit_source_code_expr_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 47
def visit_source_code_expr_node(node)
    return node.code
end
visit_ternary_node(node) click to toggle source
# File lib/translator/ast_translator.rb, line 119
def visit_ternary_node(node)
    return "((#{node.condition.accept(expression_translator)}) ? (#{node.true_val.accept(expression_translator)}) : (#{node.false_val.accept(expression_translator)}))"
end