Class: Ikra::Translator::ASTTranslator::ExpressionTranslator

Inherits:
Object
  • Object
show all
Defined in:
lib/translator/ast_translator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(translator) ⇒ ExpressionTranslator

Returns a new instance of ExpressionTranslator.



17
18
19
# File 'lib/translator/ast_translator.rb', line 17

def initialize(translator)
    @translator = translator
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# 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

Instance Attribute Details

#translatorObject (readonly)

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



15
16
17
# File 'lib/translator/ast_translator.rb', line 15

def translator
  @translator
end

Instance Method Details

#build_switch_for_args(nodes, accumulator = [], &block) ⇒ Object



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# 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) ⇒ Object

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



140
141
142
143
144
# 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_translatorObject



21
22
23
# File 'lib/translator/ast_translator.rb', line 21

def expression_translator
    return self
end

#generate_polymorphic_switch(node, &block) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# 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) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# 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

#statement_translatorObject



25
26
27
# File 'lib/translator/ast_translator.rb', line 25

def statement_translator
    return translator.statement_translator
end

#visit_array_node(node) ⇒ Object



87
88
89
90
91
92
93
# 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) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 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) ⇒ Object

Raises:



42
43
44
45
# 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) ⇒ Object



107
108
109
# File 'lib/translator/ast_translator.rb', line 107

def visit_bool_node(node)
    return node.value.to_s
end

#visit_const_node(node) ⇒ Object

Raises:

  • (NotImplementedError)


56
57
58
# File 'lib/translator/ast_translator.rb', line 56

def visit_const_node(node)
    raise NotImplementedError.new
end

#visit_float_node(node) ⇒ Object



103
104
105
# File 'lib/translator/ast_translator.rb', line 103

def visit_float_node(node)
    return node.value.to_s
end

#visit_hash_node(node) ⇒ Object

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



52
53
54
# File 'lib/translator/ast_translator.rb', line 52

def visit_hash_node(node)
    return ""
end

#visit_if_node(node) ⇒ Object



111
112
113
114
115
116
117
# 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) ⇒ Object



95
96
97
# File 'lib/translator/ast_translator.rb', line 95

def visit_int_node(node)
    return node.value.to_s
end

#visit_ivar_read_node(node) ⇒ Object



82
83
84
85
# 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) ⇒ Object



60
61
62
# 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) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 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) ⇒ Object



99
100
101
# File 'lib/translator/ast_translator.rb', line 99

def visit_nil_node(node)
    return "0"
end

#visit_return_node(node) ⇒ Object

Raises:



364
365
366
# 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) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# 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) ⇒ Object



47
48
49
# File 'lib/translator/ast_translator.rb', line 47

def visit_source_code_expr_node(node)
    return node.code
end

#visit_ternary_node(node) ⇒ Object



119
120
121
# 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