rubocop-boochtek 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +115 -5
- data/LICENSE.md +2 -2
- data/README.md +53 -106
- data/lib/disable_ssl_verify.rb +5 -0
- data/lib/extensions/argf.rb +8 -0
- data/lib/extensions/boolean.rb +48 -0
- data/lib/extensions/class.rb +10 -0
- data/lib/extensions/enumerable.rb +42 -0
- data/lib/extensions/integer.rb +7 -0
- data/lib/extensions/llvm_module.rb +101 -0
- data/lib/extensions/module.rb +34 -0
- data/lib/extensions/string.rb +29 -0
- data/lib/stone/ast/block.rb +88 -0
- data/lib/stone/ast/boolean_literal.rb +43 -0
- data/lib/stone/ast/computed_property_definition.rb +32 -0
- data/lib/stone/ast/constant_definition.rb +124 -0
- data/lib/stone/ast/expression.rb +12 -0
- data/lib/stone/ast/function_call.rb +291 -0
- data/lib/stone/ast/function_type_annotation.rb +31 -0
- data/lib/stone/ast/integer_literal.rb +46 -0
- data/lib/stone/ast/lambda.rb +126 -0
- data/lib/stone/ast/null_literal.rb +29 -0
- data/lib/stone/ast/program_unit/top_function.rb +209 -0
- data/lib/stone/ast/program_unit.rb +412 -0
- data/lib/stone/ast/property_access.rb +557 -0
- data/lib/stone/ast/record_definition.rb +180 -0
- data/lib/stone/ast/record_instantiation.rb +141 -0
- data/lib/stone/ast/reference.rb +145 -0
- data/lib/stone/ast/string_literal.rb +79 -0
- data/lib/stone/ast/two_phase_processing.rb +36 -0
- data/lib/stone/ast/type_annotation.rb +26 -0
- data/lib/stone/ast/type_declaration.rb +28 -0
- data/lib/stone/ast/type_of_expression.rb +41 -0
- data/lib/stone/ast/type_reference.rb +28 -0
- data/lib/stone/ast/union_type_annotation.rb +26 -0
- data/lib/stone/ast.rb +64 -0
- data/lib/stone/built_ins.rb +245 -0
- data/lib/stone/error/argument_error.rb +7 -0
- data/lib/stone/error/arity_error.rb +7 -0
- data/lib/stone/error/overflow.rb +18 -0
- data/lib/stone/error/property_error.rb +7 -0
- data/lib/stone/error/reference_error.rb +4 -0
- data/lib/stone/error/type_error.rb +7 -0
- data/lib/stone/error.rb +12 -0
- data/lib/stone/grammar.rb +124 -0
- data/lib/stone/libc.rb +27 -0
- data/lib/stone/prelude.stone +11 -0
- data/lib/stone/rtti.rb +182 -0
- data/lib/stone/scope.rb +85 -0
- data/lib/stone/transform.rb +410 -0
- data/lib/stone/type.rb +323 -0
- data/lib/stone/type_context.rb +38 -0
- data/lib/stone/type_registry.rb +73 -0
- data/lib/stone/types.rb +104 -0
- data/lib/stone.rb +70 -0
- metadata +54 -24
- data/CHANGELOG.md +0 -28
- data/Rakefile +0 -5
- data/config/default.yml +0 -277
- data/lib/rubocop/boochtek/plugin.rb +0 -43
- data/lib/rubocop/boochtek/version.rb +0 -7
- data/lib/rubocop/boochtek.rb +0 -18
- data/lib/rubocop/cop/boochtek/compact_endless_methods.rb +0 -103
- data/lib/rubocop-boochtek.rb +0 -3
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
require "stone/ast"
|
|
2
|
+
require "stone/ast/program_unit/top_function"
|
|
3
|
+
require "stone/built_ins"
|
|
4
|
+
require "llvm/core"
|
|
5
|
+
require "llvm/execution_engine"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module Stone
|
|
9
|
+
class AST
|
|
10
|
+
class ProgramUnit < Stone::AST
|
|
11
|
+
|
|
12
|
+
def initialize(children)
|
|
13
|
+
super(:program_unit, children)
|
|
14
|
+
@top_function = TopFunction.new(children)
|
|
15
|
+
LLVM.init_jit
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_llir
|
|
19
|
+
module_ref
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def eval(function_name = "__top__")
|
|
23
|
+
result, result_type = run_function(global_function(function_name))
|
|
24
|
+
convert_to_ruby(result, result_type)
|
|
25
|
+
ensure
|
|
26
|
+
@type_constant_addresses = nil # Clear cached addresses on dispose
|
|
27
|
+
jit_engine&.dispose
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private def run_function(func)
|
|
31
|
+
result = jit_engine.run_function(func)
|
|
32
|
+
[result, last_expression_type]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Determine the Stone type of the last expression for proper Ruby conversion
|
|
36
|
+
# Must match the logic in TopFunction#compute_return_type
|
|
37
|
+
private def last_expression_type
|
|
38
|
+
last_child = children&.last
|
|
39
|
+
return :null unless last_child
|
|
40
|
+
|
|
41
|
+
# Check for tagged union field access first (e.g., Bool | Int)
|
|
42
|
+
# These return a pointer to the union struct for runtime type dispatch
|
|
43
|
+
return :tagged_union_ptr if tagged_union_field_access?(last_child)
|
|
44
|
+
|
|
45
|
+
# Check for mixed union field access (PropertyAccess on mixed union field)
|
|
46
|
+
# Mixed unions now return i64 (payload extracted directly)
|
|
47
|
+
return :mixed_union_value if mixed_union_field_access?(last_child)
|
|
48
|
+
|
|
49
|
+
# Check for homogeneous union field access
|
|
50
|
+
return :union_value if union_field_access?(last_child)
|
|
51
|
+
|
|
52
|
+
# Check for string-returning properties (Type.as_String, FieldList.name)
|
|
53
|
+
return :string if string_returning_property?(last_child)
|
|
54
|
+
|
|
55
|
+
# Use the Stone type system for everything else
|
|
56
|
+
# Rescue errors since computed properties may not be registered in type system
|
|
57
|
+
stone_type = safe_get_type(last_child)
|
|
58
|
+
stone_type_to_result_type(stone_type)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private def lookup_type_declaration(identifier)
|
|
62
|
+
type_decl = children&.find { |c|
|
|
63
|
+
c.is_a?(Stone::AST::TypeDeclaration) && c.identifier == identifier
|
|
64
|
+
}
|
|
65
|
+
return nil unless type_decl
|
|
66
|
+
|
|
67
|
+
type_decl.type_annotation.to_type(Stone::TypeRegistry.instance)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private def safe_get_type(node)
|
|
71
|
+
node.type(module_ref)
|
|
72
|
+
rescue Stone::PropertyError, Stone::TypeError
|
|
73
|
+
# Type couldn't be determined (computed properties, etc.) - default to nil (becomes i64)
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# PropertyAccess patterns that return string pointers
|
|
78
|
+
private def string_returning_property?(node)
|
|
79
|
+
return false unless node.is_a?(Stone::AST::PropertyAccess)
|
|
80
|
+
|
|
81
|
+
property = node.property
|
|
82
|
+
# Type.as_String returns a string pointer
|
|
83
|
+
return true if property == "as_String"
|
|
84
|
+
# FieldList.name returns a string pointer
|
|
85
|
+
return true if property == "name"
|
|
86
|
+
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private def stone_type_to_result_type(stone_type)
|
|
91
|
+
# Default to i64 for unknown types (matches original behavior where all values were i64)
|
|
92
|
+
return :i64 unless stone_type
|
|
93
|
+
|
|
94
|
+
# Handle union types
|
|
95
|
+
return union_result_type(stone_type) if stone_type.is_a?(Stone::Type::Union)
|
|
96
|
+
|
|
97
|
+
case stone_type.name
|
|
98
|
+
when "Null" then :null
|
|
99
|
+
when "Bool" then :boolean
|
|
100
|
+
when "Int" then :i64
|
|
101
|
+
when "String" then :string
|
|
102
|
+
else :pointer # Records and other pointer types
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private def union_result_type(_union_type)
|
|
107
|
+
# All union variables return the union struct, which is extracted by MixedUnionExtractor
|
|
108
|
+
:mixed_union_ptr
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private def tagged_union_field_access?(node)
|
|
112
|
+
return false unless node.is_a?(Stone::AST::PropertyAccess)
|
|
113
|
+
return false unless node.union_field_access?(module_ref)
|
|
114
|
+
|
|
115
|
+
union_type = get_union_type_for_property_access(node)
|
|
116
|
+
union_type&.needs_runtime_type_tag?
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private def mixed_union_field_access?(node)
|
|
120
|
+
return false unless node.is_a?(Stone::AST::PropertyAccess)
|
|
121
|
+
return false unless node.union_field_access?(module_ref)
|
|
122
|
+
|
|
123
|
+
union_type = get_union_type_for_property_access(node)
|
|
124
|
+
union_type && !union_type.homogeneous?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private def union_field_access?(node)
|
|
128
|
+
return false unless node.is_a?(Stone::AST::PropertyAccess)
|
|
129
|
+
|
|
130
|
+
node.union_field_access?(module_ref)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
RESULT_CONVERTERS = {
|
|
134
|
+
"i64" => ->(r, _) { r.to_i },
|
|
135
|
+
"boolean" => ->(r, _) { r.to_i != 0 },
|
|
136
|
+
"null" => ->(_r, _) { nil },
|
|
137
|
+
"pointer" => ->(r, _) { r.to_value_ptr.to_i }
|
|
138
|
+
}.freeze
|
|
139
|
+
|
|
140
|
+
# Convert JIT result to Ruby value based on result type.
|
|
141
|
+
# - i64 results use result.to_i
|
|
142
|
+
# - ptr results use result.to_value_ptr (not to_ptr!)
|
|
143
|
+
private def convert_to_ruby(result, result_type)
|
|
144
|
+
converter = RESULT_CONVERTERS[result_type.to_s]
|
|
145
|
+
return converter.call(result, self) if converter
|
|
146
|
+
|
|
147
|
+
convert_complex_result(result, result_type)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private def convert_complex_result(result, result_type)
|
|
151
|
+
case result_type.to_s
|
|
152
|
+
when "string" then read_string_from_pointer(result.to_value_ptr.to_i)
|
|
153
|
+
when "union_value" then convert_union_value_to_ruby(result.to_i)
|
|
154
|
+
when "mixed_union_value" then convert_mixed_union_value(result.to_i)
|
|
155
|
+
when "tagged_union_ptr" then convert_tagged_union_ptr(result.to_value_ptr.to_i)
|
|
156
|
+
else fail "Don't know how to convert result type to Ruby: #{result_type}"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private def convert_tagged_union_ptr(ptr_addr)
|
|
161
|
+
return nil if ptr_addr.zero?
|
|
162
|
+
|
|
163
|
+
last_child = children&.last
|
|
164
|
+
union_type = get_union_type_for_property_access(last_child)
|
|
165
|
+
return nil unless union_type
|
|
166
|
+
|
|
167
|
+
TaggedUnionConverter.new(ptr_addr, union_type, method(:type_constant_address)).convert
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Get the JIT address of a type constant by name.
|
|
171
|
+
# Caches addresses since they're stable for the JIT session.
|
|
172
|
+
private def type_constant_address(type_name)
|
|
173
|
+
@type_constant_addresses ||= {}
|
|
174
|
+
@type_constant_addresses[type_name] ||= lookup_type_constant_address(type_name)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private def lookup_type_constant_address(type_name)
|
|
178
|
+
global_name = "Stone.Type.#{type_name}"
|
|
179
|
+
global = module_ref.globals[global_name]
|
|
180
|
+
return 0 unless global
|
|
181
|
+
|
|
182
|
+
jit_engine.pointer_to_global(global).to_i
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private def convert_union_value_to_ruby(value)
|
|
186
|
+
# Get the union type to determine how to interpret the value
|
|
187
|
+
last_child = children&.last
|
|
188
|
+
return value unless last_child.is_a?(Stone::AST::PropertyAccess)
|
|
189
|
+
|
|
190
|
+
union_type = get_union_type_for_property_access(last_child)
|
|
191
|
+
return value unless union_type
|
|
192
|
+
|
|
193
|
+
convert_based_on_union_type(value, union_type)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private def convert_mixed_union_value(value)
|
|
197
|
+
last_child = children&.last
|
|
198
|
+
union_type = get_union_type_for_last_child(last_child)
|
|
199
|
+
return value unless union_type
|
|
200
|
+
|
|
201
|
+
MixedUnionValueConverter.new(value, union_type).convert
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
private def get_union_type_for_last_child(node)
|
|
205
|
+
case node
|
|
206
|
+
when Stone::AST::PropertyAccess
|
|
207
|
+
get_union_type_for_property_access(node)
|
|
208
|
+
when Stone::AST::Reference
|
|
209
|
+
# For References, look up the type declaration
|
|
210
|
+
lookup_type_declaration(node.identifier)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
private def get_union_type_for_property_access(property_access)
|
|
215
|
+
record_type_name = property_access.get_record_type_name(module_ref)
|
|
216
|
+
return nil unless record_type_name
|
|
217
|
+
|
|
218
|
+
record_def = module_ref.record_types[record_type_name]
|
|
219
|
+
return nil unless record_def
|
|
220
|
+
|
|
221
|
+
annotation = record_def.field_type_annotation(property_access.property)
|
|
222
|
+
return nil unless Stone::AST::FieldHelpers.union_annotation?(annotation)
|
|
223
|
+
|
|
224
|
+
Stone::AST::FieldHelpers.resolve_field_type({type: annotation})
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
private def convert_based_on_union_type(value, union_type)
|
|
228
|
+
UnionValueConverter.new(value, union_type).convert
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Converts tagged union structs from memory to Ruby values using the runtime type tag.
|
|
232
|
+
# The union struct layout is: { ptr type_tag, [N x i8] payload }
|
|
233
|
+
# The type_tag is a pointer to a Stone::Type constant, which we compare to known type constants.
|
|
234
|
+
class TaggedUnionConverter
|
|
235
|
+
def initialize(union_ptr_addr, union_type, address_lookup)
|
|
236
|
+
@union_ptr = FFI::Pointer.new(union_ptr_addr)
|
|
237
|
+
@union_type = union_type
|
|
238
|
+
@alternatives = union_type.alternatives
|
|
239
|
+
@address_lookup = address_lookup # Proc that takes type_name and returns JIT address
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def convert
|
|
243
|
+
type_tag_addr = read_type_tag
|
|
244
|
+
type_name = identify_type(type_tag_addr)
|
|
245
|
+
read_and_convert_payload(type_name)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
private def read_type_tag
|
|
249
|
+
# Type tag is at offset 0, it's a pointer (8 bytes)
|
|
250
|
+
@union_ptr.read_pointer.to_i
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private def payload_ptr
|
|
254
|
+
# Payload starts at offset 8 (after the type tag pointer)
|
|
255
|
+
@union_ptr + 8
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private def identify_type(type_tag_addr)
|
|
259
|
+
# Compare the type tag address against known type constants
|
|
260
|
+
@alternatives.each do |alt|
|
|
261
|
+
expected_addr = @address_lookup.call(alt.name)
|
|
262
|
+
return alt.name if type_tag_addr == expected_addr
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Fallback: if we can't identify the type, return the first non-null alternative
|
|
266
|
+
@alternatives.find { |alt| alt.name != "Null" }&.name || "Int"
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Read payload with the correct size based on type, then convert to Ruby
|
|
270
|
+
private def read_and_convert_payload(type_name)
|
|
271
|
+
case type_name
|
|
272
|
+
when "Null" then nil
|
|
273
|
+
when "Bool" then payload_ptr.read_uint8 != 0
|
|
274
|
+
when "Int" then payload_ptr.read_int64
|
|
275
|
+
when "String" then read_string(payload_ptr.read_pointer.to_i)
|
|
276
|
+
else payload_ptr.read_int64 # Records and other pointer types
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
private def read_string(ptr_addr)
|
|
281
|
+
return "" if ptr_addr.zero?
|
|
282
|
+
|
|
283
|
+
FFI::Pointer.new(ptr_addr).read_string.force_encoding(Encoding::UTF_8)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Converts raw i64 values from LLVM to Ruby values based on union type alternatives.
|
|
288
|
+
# NOTE: This is a temporary solution until proper runtime type dispatch is implemented.
|
|
289
|
+
# The limitation is that Int(0) in Int | Null will return nil, not 0.
|
|
290
|
+
class UnionValueConverter
|
|
291
|
+
def initialize(value, union_type)
|
|
292
|
+
@value = value
|
|
293
|
+
@alternatives = union_type.alternatives
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def convert
|
|
297
|
+
return nil if null_value?
|
|
298
|
+
return convert_string if string_only?
|
|
299
|
+
return convert_record if record_only?
|
|
300
|
+
return @value if has?("Int")
|
|
301
|
+
return @value != 0 if has?("Bool")
|
|
302
|
+
|
|
303
|
+
@value
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
private def null_value? = @value.zero? && has?("Null")
|
|
307
|
+
private def string_only? = has?("String") && !has?("Int") && !has?("Bool") && !any_record?
|
|
308
|
+
private def record_only? = any_record? && !has?("Int") && !has?("Bool") && !has?("String")
|
|
309
|
+
private def has?(name) = @alternatives.any? { |alt| alt.name == name }
|
|
310
|
+
private def any_record? = @alternatives.any?(&:record?)
|
|
311
|
+
|
|
312
|
+
private def convert_string
|
|
313
|
+
return "" if @value.zero?
|
|
314
|
+
|
|
315
|
+
FFI::Pointer.new(@value).read_string.force_encoding(Encoding::UTF_8)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
private def convert_record
|
|
319
|
+
return nil if @value.zero?
|
|
320
|
+
|
|
321
|
+
# Returns raw pointer value. Chained property access (e.g., o.value.x)
|
|
322
|
+
# requires compile-time handling in PropertyAccess, not Ruby conversion.
|
|
323
|
+
@value
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Converts raw i64 payload values from mixed unions to Ruby values.
|
|
328
|
+
# For mixed unions (e.g., Int | String), the payload is extracted as i64.
|
|
329
|
+
# Without runtime type tag access, we use heuristics based on the alternatives:
|
|
330
|
+
# - If Int is an alternative, and value looks like a valid Int, return as Int
|
|
331
|
+
# - If String is an alternative, and value looks like a pointer, read as String
|
|
332
|
+
# NOTE: This has limitations - we can't distinguish Int(0) from NULL, or
|
|
333
|
+
# a small Int that happens to look like a valid pointer address.
|
|
334
|
+
class MixedUnionValueConverter
|
|
335
|
+
def initialize(value, union_type)
|
|
336
|
+
@value = value
|
|
337
|
+
@alternatives = union_type.alternatives
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def convert
|
|
341
|
+
return nil if null_value?
|
|
342
|
+
return convert_int_or_string if has?("Int") && has?("String")
|
|
343
|
+
|
|
344
|
+
convert_single_type
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
private def null_value? = @value.zero? && has?("Null")
|
|
348
|
+
|
|
349
|
+
private def convert_int_or_string
|
|
350
|
+
looks_like_pointer? ? read_string : @value
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
private def convert_single_type
|
|
354
|
+
return @value if has?("Int")
|
|
355
|
+
return @value != 0 if has?("Bool")
|
|
356
|
+
return read_string if has?("String")
|
|
357
|
+
|
|
358
|
+
@value
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
private def has?(name) = @alternatives.any? { |alt| alt.name == name }
|
|
362
|
+
|
|
363
|
+
private def looks_like_pointer?
|
|
364
|
+
# Heuristic: heap pointers are typically large addresses, aligned to 8 bytes
|
|
365
|
+
# Small values (< 0x1000) are likely integers
|
|
366
|
+
# This is imperfect but works for common cases
|
|
367
|
+
@value > 0x1000 && (@value % 8).zero?
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
private def read_string
|
|
371
|
+
return "" if @value.zero?
|
|
372
|
+
|
|
373
|
+
FFI::Pointer.new(@value).read_string.force_encoding(Encoding::UTF_8)
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
private def read_string_from_pointer(ptr_addr)
|
|
378
|
+
return "" if ptr_addr.zero?
|
|
379
|
+
|
|
380
|
+
FFI::Pointer.new(ptr_addr).read_string.force_encoding(Encoding::UTF_8)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
private def jit_engine
|
|
384
|
+
@jit_engine ||= LLVM::JITCompiler.new(module_ref)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
private def global_function(function_name)
|
|
388
|
+
module_ref.functions[function_name]
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
private def module_ref
|
|
392
|
+
@module_ref ||= create_module
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
private def create_module
|
|
396
|
+
LLVM::Module.new("__program_unit__").tap do |mod|
|
|
397
|
+
Stone::BuiltIns.new(mod).setup
|
|
398
|
+
generate_top_function(mod)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Generate IR for all code that's directly in the module.
|
|
403
|
+
# TODO: Maybe pass in `ARGV` and `ENV`.
|
|
404
|
+
# ... for `ARGV`, we'll probably need to implement `main(argc, argv, envp)`.
|
|
405
|
+
# ... for `ENV`, we can probably call `getenv`, maybe `environ`.
|
|
406
|
+
# Look into run_function_as_main(engine, fn, argc, argv, envp)
|
|
407
|
+
private def generate_top_function(mod)
|
|
408
|
+
@top_function.generate(mod)
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end
|