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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +115 -5
  3. data/LICENSE.md +2 -2
  4. data/README.md +53 -106
  5. data/lib/disable_ssl_verify.rb +5 -0
  6. data/lib/extensions/argf.rb +8 -0
  7. data/lib/extensions/boolean.rb +48 -0
  8. data/lib/extensions/class.rb +10 -0
  9. data/lib/extensions/enumerable.rb +42 -0
  10. data/lib/extensions/integer.rb +7 -0
  11. data/lib/extensions/llvm_module.rb +101 -0
  12. data/lib/extensions/module.rb +34 -0
  13. data/lib/extensions/string.rb +29 -0
  14. data/lib/stone/ast/block.rb +88 -0
  15. data/lib/stone/ast/boolean_literal.rb +43 -0
  16. data/lib/stone/ast/computed_property_definition.rb +32 -0
  17. data/lib/stone/ast/constant_definition.rb +124 -0
  18. data/lib/stone/ast/expression.rb +12 -0
  19. data/lib/stone/ast/function_call.rb +291 -0
  20. data/lib/stone/ast/function_type_annotation.rb +31 -0
  21. data/lib/stone/ast/integer_literal.rb +46 -0
  22. data/lib/stone/ast/lambda.rb +126 -0
  23. data/lib/stone/ast/null_literal.rb +29 -0
  24. data/lib/stone/ast/program_unit/top_function.rb +209 -0
  25. data/lib/stone/ast/program_unit.rb +412 -0
  26. data/lib/stone/ast/property_access.rb +557 -0
  27. data/lib/stone/ast/record_definition.rb +180 -0
  28. data/lib/stone/ast/record_instantiation.rb +141 -0
  29. data/lib/stone/ast/reference.rb +145 -0
  30. data/lib/stone/ast/string_literal.rb +79 -0
  31. data/lib/stone/ast/two_phase_processing.rb +36 -0
  32. data/lib/stone/ast/type_annotation.rb +26 -0
  33. data/lib/stone/ast/type_declaration.rb +28 -0
  34. data/lib/stone/ast/type_of_expression.rb +41 -0
  35. data/lib/stone/ast/type_reference.rb +28 -0
  36. data/lib/stone/ast/union_type_annotation.rb +26 -0
  37. data/lib/stone/ast.rb +64 -0
  38. data/lib/stone/built_ins.rb +245 -0
  39. data/lib/stone/error/argument_error.rb +7 -0
  40. data/lib/stone/error/arity_error.rb +7 -0
  41. data/lib/stone/error/overflow.rb +18 -0
  42. data/lib/stone/error/property_error.rb +7 -0
  43. data/lib/stone/error/reference_error.rb +4 -0
  44. data/lib/stone/error/type_error.rb +7 -0
  45. data/lib/stone/error.rb +12 -0
  46. data/lib/stone/grammar.rb +124 -0
  47. data/lib/stone/libc.rb +27 -0
  48. data/lib/stone/prelude.stone +11 -0
  49. data/lib/stone/rtti.rb +182 -0
  50. data/lib/stone/scope.rb +85 -0
  51. data/lib/stone/transform.rb +410 -0
  52. data/lib/stone/type.rb +323 -0
  53. data/lib/stone/type_context.rb +38 -0
  54. data/lib/stone/type_registry.rb +73 -0
  55. data/lib/stone/types.rb +104 -0
  56. data/lib/stone.rb +70 -0
  57. metadata +54 -24
  58. data/CHANGELOG.md +0 -28
  59. data/Rakefile +0 -5
  60. data/config/default.yml +0 -277
  61. data/lib/rubocop/boochtek/plugin.rb +0 -43
  62. data/lib/rubocop/boochtek/version.rb +0 -7
  63. data/lib/rubocop/boochtek.rb +0 -18
  64. data/lib/rubocop/cop/boochtek/compact_endless_methods.rb +0 -103
  65. data/lib/rubocop-boochtek.rb +0 -3
@@ -0,0 +1,180 @@
1
+ require "stone/ast/expression"
2
+ require "stone/error/type_error"
3
+ require "stone/rtti"
4
+
5
+
6
+ module Stone
7
+ class AST
8
+ # TODO: Records should be first-class Type objects, not AST nodes.
9
+ # When the type system is refactored:
10
+ # - Record types should be instances of a RecordType class
11
+ # - Record types should be global constants with properties
12
+ # - Record types should have vtables for polymorphic operations
13
+ # - Record instantiation should work like any other function call
14
+ class RecordDefinition < Stone::AST::Expression
15
+
16
+ attr_reader :fields
17
+ attr_accessor :assigned_name
18
+
19
+ # fields is an array of { name: "field_name", type: "TypeName" } hashes
20
+ def initialize(fields)
21
+ @name = :record_definition
22
+ @fields = fields
23
+ @assigned_name = nil
24
+ end
25
+
26
+ def to_llir(_builder, mod, scope = Stone::Scope.top_level)
27
+ # Validate all field types resolve in current scope
28
+ validate_field_types(scope, mod)
29
+ # Generate the type constant for this record
30
+ generate_type_constant(mod, scope) if @assigned_name
31
+ # Generate and return a constructor function that creates instances of this record
32
+ generate_constructor_function(mod, scope)
33
+ end
34
+
35
+ private def generate_type_constant(mod, scope)
36
+ struct_type = llvm_type(mod, scope)
37
+ size_bytes = calculate_struct_size(struct_type)
38
+ Stone::RTTI.generate_record_type_constant(mod, @assigned_name, size_bytes, @fields)
39
+ end
40
+
41
+ private def calculate_struct_size(struct_type)
42
+ # Calculate size based on field types (simplified - assumes packed alignment)
43
+ total = 0
44
+ struct_type.element_types.each do |elem_type|
45
+ total += element_type_size(elem_type)
46
+ end
47
+ total
48
+ end
49
+
50
+ private def element_type_size(llvm_type)
51
+ case llvm_type.kind
52
+ when :integer then (llvm_type.width + 7) / 8 # Round up to bytes
53
+ when :pointer then 8 # 64-bit pointers
54
+ when :struct then llvm_type.element_types.sum { |t| element_type_size(t) }
55
+ else 8 # Default to 8 bytes
56
+ end
57
+ end
58
+
59
+ private def validate_field_types(scope, mod)
60
+ @fields.each { |field| validate_field_type(field, scope, mod) }
61
+ end
62
+
63
+ private def validate_field_type(field, scope, mod)
64
+ return if Stone::AST::FieldHelpers.union_annotation?(field[:type])
65
+
66
+ type_name = Stone::AST::FieldHelpers.field_type_name(field)
67
+ return if known_type?(type_name, scope, mod)
68
+
69
+ fail Stone::TypeError, "Unknown type: #{type_name}"
70
+ end
71
+
72
+ private def known_type?(type_name, scope, mod)
73
+ type_name == @assigned_name || mod&.record_type?(type_name) || scope.lookup_type(type_name)
74
+ end
75
+
76
+ def field_names
77
+ @fields.map { |f| f[:name] }
78
+ end
79
+
80
+ def field_types
81
+ @fields.map { |f| Stone::AST::FieldHelpers.field_type_name(f) }
82
+ end
83
+
84
+ def field_type_annotation(field_name)
85
+ field = @fields.find { |f| f[:name] == field_name }
86
+ field&.dig(:type)
87
+ end
88
+
89
+ def field_index(field_name)
90
+ field_names.index(field_name)
91
+ end
92
+
93
+ def llvm_type(mod = nil, scope = Stone::Scope.top_level)
94
+ # Convert field types to LLVM types
95
+ llvm_field_types = @fields.map { |field| llvm_type_for_field(field, mod, scope) }
96
+ LLVM::Type.struct(llvm_field_types, false)
97
+ end
98
+
99
+ def to_s
100
+ field_strs = @fields.map { |f| "#{f[:name]} :: #{f[:type_name]}" }
101
+ "Record(#{field_strs.join(', ')})"
102
+ end
103
+
104
+ def type(_context = nil)
105
+ return nil unless @assigned_name
106
+
107
+ record_type = Stone::Type::Registry.lookup(@assigned_name)
108
+ return nil unless record_type
109
+
110
+ param_types = @fields.map { |f| resolve_field_stone_type(f) }
111
+ return nil if param_types.any?(&:nil?)
112
+
113
+ Stone::Type.function(param_types:, return_type: record_type)
114
+ end
115
+
116
+ private def resolve_field_stone_type(field)
117
+ Stone::AST::FieldHelpers.resolve_field_type(field)
118
+ end
119
+
120
+ # Returns the LLVM type for a field. Handles union types, primitives, and record types.
121
+ private def llvm_type_for_field(field, mod, scope)
122
+ return llvm_type_for_union(field[:type]) if Stone::AST::FieldHelpers.union_annotation?(field[:type])
123
+
124
+ type_name = Stone::AST::FieldHelpers.field_type_name(field)
125
+ resolve_simple_llvm_type(type_name, mod, scope)
126
+ end
127
+
128
+ private def resolve_simple_llvm_type(type_name, mod, scope)
129
+ return LLVM::Type.ptr if record_reference?(type_name, mod)
130
+ return Stone::Type::Registry.lookup(type_name).llvm_type if primitive_type?(type_name)
131
+ return LLVM::Type.ptr if scope.lookup_type(type_name)
132
+
133
+ fail Stone::TypeError, "Unknown type: #{type_name}"
134
+ end
135
+
136
+ private def record_reference?(type_name, mod)
137
+ type_name == @assigned_name || mod&.record_type?(type_name)
138
+ end
139
+
140
+ private def primitive_type?(type_name)
141
+ Stone::Type::Registry.lookup(type_name)
142
+ end
143
+
144
+ private def llvm_type_for_union(annotation)
145
+ stone_type = annotation.to_type(Stone::Type::Registry)
146
+ stone_type.llvm_type
147
+ end
148
+
149
+ private def generate_constructor_function(mod, scope)
150
+ func_name = "__record_constructor_#{object_id}__"
151
+ func_type = constructor_function_type(mod, scope)
152
+
153
+ mod.functions.add(func_name, func_type).tap do |func|
154
+ build_constructor_body(func, mod, scope)
155
+ end
156
+ end
157
+
158
+ private def constructor_function_type(mod, scope)
159
+ # Constructor function signature: (field_types...) -> struct_type
160
+ field_llvm_types = @fields.map { |field| llvm_type_for_field(field, mod, scope) }
161
+ LLVM::Type.function(field_llvm_types, llvm_type(mod, scope))
162
+ end
163
+
164
+ private def build_constructor_body(func, mod, scope)
165
+ func.basic_blocks.append("entry").build do |builder|
166
+ # Start with null/undef struct value
167
+ struct_value = llvm_type(mod, scope).null
168
+
169
+ # Insert each field value from function parameters
170
+ @fields.each_with_index do |_field, index|
171
+ struct_value = builder.insert_value(struct_value, func.params[index], index)
172
+ end
173
+
174
+ builder.ret(struct_value)
175
+ end
176
+ end
177
+
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,141 @@
1
+ require "stone/ast/expression"
2
+ require "stone/error/arity_error"
3
+ require "stone/rtti"
4
+
5
+
6
+ module Stone
7
+ class AST
8
+ class RecordInstantiation < Stone::AST::Expression
9
+
10
+ attr_reader :record_type_name, :field_values, :record_definition
11
+
12
+ def initialize(record_type_name, field_values)
13
+ @name = :record_instantiation
14
+ @record_type_name = record_type_name
15
+ @field_values = field_values
16
+ @record_definition = nil
17
+ end
18
+
19
+ def to_llir(builder, mod, scope = Stone::Scope.top_level)
20
+ record_def = mod.record_types[@record_type_name]
21
+ fail "Unknown record type: #{@record_type_name}" unless record_def
22
+
23
+ # Store record definition for later access
24
+ @record_definition = record_def
25
+
26
+ # Verify field count matches
27
+ if @field_values.size != record_def.fields.size
28
+ fail Stone::ArityError, "wrong number of arguments for #{@record_type_name} (given #{@field_values.size}, expected #{record_def.fields.size})"
29
+ end
30
+
31
+ # Evaluate each field value
32
+ llvm_values = @field_values.map { |field_ast| field_ast.to_llir(builder, mod, scope) }
33
+
34
+ # Convert struct values to pointers for record-typed fields
35
+ llvm_values = convert_structs_to_pointers(builder, mod, record_def, llvm_values)
36
+
37
+ # Create struct value
38
+ create_struct(record_def.llvm_type(mod), llvm_values, builder)
39
+ end
40
+
41
+ def to_s
42
+ "#{@record_type_name}(#{@field_values.map(&:to_s).join(', ')})"
43
+ end
44
+
45
+ def type(_context = nil)
46
+ Stone::Type::Registry.lookup(@record_type_name)
47
+ end
48
+
49
+ private def verify_field_type(field_name, expected_type, llvm_value)
50
+ expected_llvm_type = llvm_type_for(expected_type)
51
+ actual_llvm_type = llvm_value.type
52
+
53
+ # Compare LLVM types
54
+ return if types_match?(expected_llvm_type, actual_llvm_type)
55
+
56
+ fail "Type mismatch for field '#{field_name}': expected #{expected_type}, got #{actual_llvm_type}"
57
+ end
58
+
59
+ private def types_match?(expected, actual)
60
+ expected.to_s == actual.to_s
61
+ end
62
+
63
+ private def llvm_type_for(type_name)
64
+ case type_name
65
+ when "Int" then LLVM::Int64
66
+ when "Bool" then LLVM::Int1
67
+ when "String" then Stone::Type::String.llvm_type
68
+ else
69
+ fail "Unknown type: #{type_name}"
70
+ end
71
+ end
72
+
73
+ private def convert_structs_to_pointers(builder, mod, record_def, llvm_values)
74
+ llvm_values.each_with_index.map do |value, index|
75
+ convert_field_value(builder, mod, record_def.fields[index], value, index)
76
+ end
77
+ end
78
+
79
+ private def convert_field_value(builder, mod, field, value, index)
80
+ if Stone::AST::FieldHelpers.union_annotation?(field[:type])
81
+ union_type = Stone::AST::FieldHelpers.resolve_field_type(field)
82
+ return wrap_in_tagged_union(builder, mod, value, @field_values[index], union_type)
83
+ end
84
+ return allocate_and_store(builder, value) if needs_pointer_conversion?(field, mod, value)
85
+
86
+ value
87
+ end
88
+
89
+ private def needs_pointer_conversion?(field, mod, value)
90
+ field_expects_pointer?(field[:type_name], mod) && value.type.kind == :struct
91
+ end
92
+
93
+ private def wrap_in_tagged_union(builder, mod, llvm_value, ast_value, union_type)
94
+ value_type = ast_value.type(Stone::TypeContext.new(mod))
95
+ type_constant = Stone::RTTI.type_constant_for(mod, value_type)
96
+
97
+ # Allocate union on stack
98
+ union_ptr = builder.alloca(union_type.llvm_type, "union_alloca")
99
+
100
+ # Store type tag (field 0)
101
+ tag_ptr = builder.struct_gep2(union_type.llvm_type, union_ptr, 0, "tag_ptr")
102
+ builder.store(type_constant, tag_ptr)
103
+
104
+ # Store payload (field 1) - no ptr2int needed
105
+ payload_ptr = builder.struct_gep2(union_type.llvm_type, union_ptr, 1, "payload_ptr")
106
+ store_payload(builder, payload_ptr, llvm_value, value_type)
107
+
108
+ # Load and return the union value
109
+ builder.load2(union_type.llvm_type, union_ptr, "union_value")
110
+ end
111
+
112
+ private def store_payload(builder, payload_ptr, llvm_value, _value_type)
113
+ value_to_store = llvm_value.type.kind == :struct ? allocate_and_store(builder, llvm_value) : llvm_value
114
+ builder.store(value_to_store, payload_ptr)
115
+ end
116
+
117
+ private def field_expects_pointer?(field_type_name, mod)
118
+ field_type_name == @record_type_name || mod.record_type?(field_type_name)
119
+ end
120
+
121
+ private def allocate_and_store(builder, struct_value)
122
+ ptr = builder.alloca(struct_value.type, "nested_record")
123
+ builder.store(struct_value, ptr)
124
+ ptr
125
+ end
126
+
127
+ private def create_struct(struct_type, values, builder)
128
+ # Start with a null/undef struct value
129
+ struct_value = struct_type.null
130
+
131
+ # Insert each field value
132
+ values.each_with_index do |value, index|
133
+ struct_value = builder.insert_value(struct_value, value, index)
134
+ end
135
+
136
+ struct_value
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,145 @@
1
+ require "stone/ast/expression"
2
+ require "stone/error/type_error"
3
+
4
+
5
+ module Stone
6
+ class AST
7
+ class Reference < Stone::AST::Expression
8
+
9
+ attr_reader :identifier
10
+
11
+ def initialize(identifier, type: nil)
12
+ @identifier = identifier
13
+ @type = type
14
+ @name = :reference
15
+ end
16
+
17
+ def type(context = nil)
18
+ return @type if @type
19
+ return nil unless context
20
+
21
+ type_from_context(context) || type_from_module(context)
22
+ end
23
+
24
+ private def type_from_context(context)
25
+ return nil unless context.is_a?(Stone::TypeContext)
26
+
27
+ context.lookup(@identifier)
28
+ end
29
+
30
+ private def type_from_module(context)
31
+ # Fallback to old module-based lookup for backwards compatibility during migration
32
+ mod = context.is_a?(Stone::TypeContext) ? context.llvm_module : context
33
+ return nil unless mod
34
+
35
+ type_from_record_instance(mod) || type_from_parameter(mod) || type_from_string_constant(mod) || type_from_global(mod)
36
+ end
37
+
38
+ def record_instance?(mod)
39
+ mod.record_instance?(identifier)
40
+ end
41
+
42
+ def to_llir(builder, mod, scope = Stone::Scope.top_level)
43
+ # Check lambda parameters first - they should shadow outer scope definitions
44
+ lookup_parameter(builder, mod) ||
45
+ lookup_in_scope(scope) ||
46
+ lookup_global(builder, mod) ||
47
+ lookup_function(mod) ||
48
+ lookup_record_type(mod) ||
49
+ fail_with_reference_error
50
+ end
51
+
52
+ def to_s
53
+ identifier
54
+ end
55
+
56
+ private def type_from_parameter(mod)
57
+ return unless mod.lambda_param_storage&.key?(identifier)
58
+
59
+ llvm_type_to_stone_type(mod.lambda_param_storage[identifier].allocated_type)
60
+ end
61
+
62
+ private def type_from_string_constant(mod)
63
+ Stone::Type::String if mod.string_constant?(identifier)
64
+ end
65
+
66
+ private def type_from_record_instance(mod)
67
+ return unless mod.record_instance?(identifier)
68
+
69
+ type_name = mod.record_instance_type(identifier)
70
+ Stone::Type::Registry.lookup(type_name)
71
+ end
72
+
73
+ private def type_from_global(mod)
74
+ global = mod.globals[identifier]
75
+ return unless global
76
+
77
+ # Check if the initializer is a null pointer (NULL constant)
78
+ initializer = global.initializer
79
+ return Stone::Type::Null if null_pointer?(initializer)
80
+
81
+ # In LLVM 21+, globals use opaque pointers, so check the initializer's type
82
+ llvm_type = initializer&.type
83
+ llvm_type_to_stone_type(llvm_type) if llvm_type
84
+ end
85
+
86
+ private def null_pointer?(value)
87
+ return false unless value
88
+
89
+ value.type.kind == :pointer && value.null?
90
+ end
91
+
92
+ private def llvm_type_to_stone_type(llvm_type)
93
+ return nil unless llvm_type
94
+
95
+ llvm_kind_to_stone_type(llvm_type)
96
+ end
97
+
98
+ # Maps LLVM type kinds to Stone types.
99
+ # Note: Record instances are handled by type_from_record_instance BEFORE this is called,
100
+ # so :struct here represents Stone strings (which use struct {i64 len, i8* data}).
101
+ private def llvm_kind_to_stone_type(llvm_type)
102
+ case llvm_type.kind
103
+ when :integer then llvm_type.width == 1 ? Stone::Type::Bool : Stone::Type::Int
104
+ when :pointer, :struct then Stone::Type::String
105
+ end
106
+ end
107
+
108
+ private def lookup_parameter(builder, mod)
109
+ param_storage = mod.lambda_param_storage
110
+ return nil unless param_storage && param_storage[identifier]
111
+
112
+ builder.load(param_storage[identifier], identifier)
113
+ end
114
+
115
+ private def lookup_global(builder, mod)
116
+ global = mod.globals[identifier]
117
+ return nil unless global
118
+
119
+ builder.load(global, identifier)
120
+ end
121
+
122
+ private def lookup_function(mod)
123
+ mod.lookup_function(identifier)
124
+ end
125
+
126
+ private def lookup_record_type(mod)
127
+ # Record types aren't really values, but if referenced, return a dummy value
128
+ # This allows code like "Person := Record(...)\nPerson" to not fail
129
+ return LLVM::Int64.from_i(0) if mod.record_type?(identifier)
130
+
131
+ nil
132
+ end
133
+
134
+ private def lookup_in_scope(scope)
135
+ definition = scope&.lookup(identifier)
136
+ definition&.dig(:value)
137
+ end
138
+
139
+ private def fail_with_reference_error
140
+ fail Stone::ReferenceError, "undefined constant, variable, or function: #{identifier}"
141
+ end
142
+
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,79 @@
1
+ require "stone/ast/expression"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class StringLiteral < Stone::AST::Expression
7
+
8
+ def self.parse(text, location)
9
+ # Remove surrounding quotes, but do NOT process escape sequences.
10
+ value = text[1..-2]
11
+ new(value)
12
+ rescue ex
13
+ raise Stone::Error.new(ex.message, location)
14
+ end
15
+
16
+ attr_reader :value
17
+
18
+ def initialize(value)
19
+ @name = :string_literal
20
+ @value = value
21
+ end
22
+
23
+ # Byte count (for system/memory operations)
24
+ def bytesize
25
+ @value.bytesize
26
+ end
27
+
28
+ # Character count (for user-facing string length)
29
+ def length
30
+ @value.length
31
+ end
32
+
33
+ def to_llir(builder, mod, _scope = Stone::Scope.top_level)
34
+ string_global = create_global_string(mod)
35
+
36
+ # Get pointer to the string data (GEP to first element)
37
+ # Array size is bytesize + 1 for null terminator
38
+ builder.gep2(
39
+ LLVM::Type.array(LLVM::Int8, @value.bytesize + 1),
40
+ string_global,
41
+ [LLVM::Int64.from_i(0), LLVM::Int64.from_i(0)],
42
+ "string_ptr"
43
+ )
44
+ # Return pointer directly - no ptr2int (CHERI-safe)
45
+ end
46
+
47
+ private def create_global_string(mod)
48
+ global_name = ".str.#{mod.globals.count}"
49
+ string_constant = create_string_constant
50
+ add_global_constant(mod, global_name, string_constant)
51
+ end
52
+
53
+ # Create string constant from bytes manually to work around ruby-llvm bugs:
54
+ # 1. ConstantArray.string truncates Unicode strings
55
+ # 2. ConstantArray.const with unsigned bytes > 127 produces poison values
56
+ # Solution: convert bytes > 127 to signed i8 equivalent
57
+ # Include null terminator for C string compatibility
58
+ private def create_string_constant
59
+ bytes_with_null = @value.bytes + [0]
60
+ i8_constants = bytes_with_null.map { |byte| LLVM::Int8.from_i(byte > 127 ? byte - 256 : byte) }
61
+ LLVM::ConstantArray.const(LLVM::Int8, i8_constants)
62
+ end
63
+
64
+ private def add_global_constant(mod, name, constant)
65
+ mod.globals.add(constant.type, name).tap do |global|
66
+ global.initializer = constant
67
+ global.linkage = :private
68
+ global.global_constant = true
69
+ global.unnamed_addr = true
70
+ end
71
+ end
72
+
73
+ def type(_context = nil)
74
+ Stone::Type::String
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,36 @@
1
+ require "stone/ast/type_declaration"
2
+ require "stone/type_registry"
3
+
4
+ module Stone
5
+ class AST
6
+ # Mixin for AST nodes that process statements in two phases:
7
+ # 1. Register all type declarations in scope
8
+ # 2. Compile other statements
9
+ #
10
+ # This ensures type declarations are available before definitions that reference them.
11
+ module TwoPhaseProcessing
12
+
13
+ private def register_type_declarations(scope)
14
+ registry = Stone::TypeRegistry.instance
15
+ type_declarations.each do |td|
16
+ stone_type = td.type_annotation.to_type(registry)
17
+ scope.declare_type(td.identifier, type: stone_type, location: td.location)
18
+ end
19
+ end
20
+
21
+ private def type_declarations
22
+ all_statements.select { |s| s.is_a?(Stone::AST::TypeDeclaration) }
23
+ end
24
+
25
+ private def other_statements
26
+ all_statements.reject { |s| s.is_a?(Stone::AST::TypeDeclaration) }
27
+ end
28
+
29
+ # Subclasses must implement this method to return the list of statements to process
30
+ private def all_statements
31
+ fail NotImplementedError, "Subclasses must implement #all_statements"
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ require "stone/ast"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class TypeAnnotation < Stone::AST
7
+
8
+ attr_reader :type_name
9
+
10
+ def initialize(type_name)
11
+ @type_name = type_name
12
+ @name = :type_annotation
13
+ end
14
+
15
+ def to_s
16
+ type_name
17
+ end
18
+
19
+ # Convert to Stone::Type for type checking
20
+ def to_type(registry)
21
+ registry.lookup(type_name)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ require "stone/ast"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class TypeDeclaration < Stone::AST
7
+
8
+ attr_reader :identifier, :type_annotation, :location
9
+
10
+ def initialize(identifier, type_annotation, location: nil)
11
+ @identifier = identifier
12
+ @type_annotation = type_annotation
13
+ @location = location
14
+ @name = :type_declaration
15
+ end
16
+
17
+ def to_s
18
+ "#{identifier} :: #{type_annotation}"
19
+ end
20
+
21
+ # Type declarations don't produce a value, so they have no type
22
+ def type(_context = nil)
23
+ nil
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ require "stone/ast/expression"
2
+ require "stone/type_context"
3
+ require "stone/rtti"
4
+
5
+
6
+ module Stone
7
+ class AST
8
+ class TypeOfExpression < Stone::AST::Expression
9
+
10
+ attr_reader :inner_expression
11
+
12
+ def initialize(inner_expression)
13
+ @inner_expression = inner_expression
14
+ @name = :type_of_expression
15
+ end
16
+
17
+ def type(_context = nil)
18
+ Stone::Type::Type
19
+ end
20
+
21
+ def to_llir(builder, mod, scope = Stone::Scope.top_level)
22
+ # Union field access: extract type tag at runtime
23
+ return @inner_expression.extract_union_type_tag(builder, mod, scope) if union_field_access?(mod)
24
+
25
+ # Default: return compile-time type constant
26
+ context = Stone::TypeContext.new(mod)
27
+ result_type = @inner_expression.type(context)
28
+ Stone::RTTI.type_constant_for(mod, result_type)
29
+ end
30
+
31
+ def to_s
32
+ "Type.of(#{@inner_expression})"
33
+ end
34
+
35
+ private def union_field_access?(mod)
36
+ @inner_expression.is_a?(PropertyAccess) && @inner_expression.union_field_access?(mod)
37
+ end
38
+
39
+ end
40
+ end
41
+ end