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