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,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
|