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,88 @@
1
+ require "stone/ast"
2
+ require "stone/ast/two_phase_processing"
3
+ require "stone/types"
4
+
5
+
6
+ module Stone
7
+ class AST
8
+ class Block < Stone::AST::Expression
9
+ include TwoPhaseProcessing
10
+
11
+ attr_reader :statements
12
+
13
+ class << self
14
+ attr_accessor :block_count
15
+ end
16
+
17
+ @block_count = 0
18
+
19
+ def initialize(statements)
20
+ @name = :block
21
+ @statements = statements
22
+ @block_id = next_block_id
23
+ end
24
+
25
+ def to_llir(_builder, mod, scope = Stone::Scope.top_level)
26
+ # Check if function already exists (happens if to_llir called multiple times on same block).
27
+ mod.functions[function_name] || create_function(mod, function_name, function_type, scope)
28
+ end
29
+
30
+ def to_s
31
+ "{ #{statements.join("\n")} }"
32
+ end
33
+
34
+ def type(context = nil)
35
+ last_expression = statements.reverse.find { |stmt| stmt.type(context) }
36
+ last_expression&.type(context)
37
+ end
38
+
39
+ private def next_block_id
40
+ self.class.block_count += 1
41
+ self.class.block_count
42
+ end
43
+
44
+ private def function_type
45
+ # Blocks take no parameters and return an i64.
46
+ LLVM::Type.function([], I64)
47
+ end
48
+
49
+ private def function_name
50
+ "__#{function_prefix}_#{@block_id}__"
51
+ end
52
+
53
+ private def function_prefix
54
+ "block"
55
+ end
56
+
57
+ private def create_function(mod, function_name, function_type, scope)
58
+ mod.functions.add(function_name, function_type).tap do |func|
59
+ build_function_body(func, mod, scope)
60
+ end
61
+ end
62
+
63
+ private def build_function_body(func, mod, scope)
64
+ func.basic_blocks.append("entry").build do |block_builder|
65
+ evaluate_body_and_return(block_builder, mod, scope)
66
+ end
67
+ end
68
+
69
+ # Evaluate all statements in block and return value of last statement.
70
+ def evaluate_body_and_return(builder, mod, scope)
71
+ child_scope = scope.child
72
+ register_type_declarations(child_scope)
73
+ last_result = compile_statements(builder, mod, child_scope)
74
+ builder.ret(last_result)
75
+ end
76
+
77
+ private def compile_statements(builder, mod, scope)
78
+ results = other_statements.map { |stmt| stmt.to_llir(builder, mod, scope) }
79
+ results.compact.last || LLVM::Int64.from_i(0)
80
+ end
81
+
82
+ private def all_statements
83
+ @all_statements ||= Array(statements).compact
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,43 @@
1
+ require "stone/ast/expression"
2
+
3
+ # TODO: Eventually, TRUE and FALSE should be top-level constants defined as
4
+ # __BUILTIN__.Boolean.TRUE and __BUILTIN__.Boolean.FALSE rather than literals.
5
+
6
+ module Stone
7
+ class AST
8
+ class BooleanLiteral < Stone::AST::Expression
9
+
10
+ TRUE = 1
11
+ FALSE = 0
12
+
13
+ def self.parse(text, location)
14
+ new(text)
15
+ rescue ex
16
+ raise Stone::Error(ex.message, location) # TODO: Make a more specific error?
17
+ end
18
+
19
+ attr_reader :value
20
+
21
+ def initialize(value)
22
+ @name = :boolean_literal
23
+ case value
24
+ when "TRUE"
25
+ @value = TRUE
26
+ when "FALSE"
27
+ @value = FALSE
28
+ else
29
+ fail "expected TRUE or FALSE"
30
+ end
31
+ end
32
+
33
+ def to_llir(_builder, _mod, _scope = Stone::Scope.top_level)
34
+ @value == TRUE ? LLVM::TRUE : LLVM::FALSE
35
+ end
36
+
37
+ def type(_context = nil)
38
+ Stone::Type::Bool
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ require "stone/ast"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class ComputedPropertyDefinition < Stone::AST
7
+
8
+ attr_reader :type_name, :property_name, :lambda
9
+
10
+ def initialize(type_name, property_name, lambda)
11
+ @name = :computed_property_definition
12
+ @type_name = type_name
13
+ @property_name = property_name
14
+ @lambda = lambda
15
+ end
16
+
17
+ def to_llir(builder, mod, scope = Stone::Scope.top_level)
18
+ # Generate the lambda function
19
+ func = @lambda.to_llir(builder, mod, scope)
20
+
21
+ # Register as a function alias with the name "Type@property"
22
+ # This allows lookup via mod.lookup_function("Type@property")
23
+ function_name = "#{@type_name}@#{@property_name}"
24
+ mod.register_function_alias(function_name, func)
25
+
26
+ # Return nil (computed properties are definitions, not expressions)
27
+ nil
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,124 @@
1
+ require "stone/ast/expression"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class ConstantDefinition < Stone::AST::Expression
7
+
8
+ attr_reader :identifier, :value_expression
9
+
10
+ def initialize(identifier, value_expression)
11
+ @identifier = identifier
12
+ @value_expression = value_expression
13
+ @name = :constant_definition
14
+ end
15
+
16
+ def to_llir(builder, mod, scope = Stone::Scope.top_level)
17
+ return register_record_type(mod, builder, scope) if value_expression.is_a?(Stone::AST::RecordDefinition)
18
+
19
+ llvm_value = value_expression.to_llir(builder, mod, scope)
20
+
21
+ return handle_record_definition(mod, llvm_value) if value_expression.is_a?(Stone::AST::RecordDefinition)
22
+ return register_function_alias(mod, llvm_value, scope) if llvm_value.is_a?(LLVM::Function)
23
+
24
+ handle_value_expression(mod, scope)
25
+ create_global(mod, builder, llvm_value, scope)
26
+ end
27
+
28
+ def type(_context = nil)
29
+ # ConstantDefinition doesn't have a value itself, it defines a binding
30
+ # Return nil to indicate this isn't an expression
31
+ nil
32
+ end
33
+
34
+ private def create_global(mod, builder, llvm_value, scope)
35
+ global = mod.globals.add(llvm_value.type, identifier)
36
+ global.linkage = :internal
37
+
38
+ if literal_constant?
39
+ initialize_as_constant(global, llvm_value)
40
+ else
41
+ initialize_at_runtime(global, builder, llvm_value)
42
+ end
43
+
44
+ # Register in scope for lexical lookup
45
+ scope.define(identifier, value: llvm_value)
46
+
47
+ nil
48
+ end
49
+
50
+ private def handle_record_definition(mod, llvm_value)
51
+ mod.register_record_type(identifier, value_expression)
52
+ mod.register_function_alias(identifier, llvm_value)
53
+ nil
54
+ end
55
+
56
+ # Register type information for expressions whose types cannot be inferred from LLVM types.
57
+ # - Strings: LLVM value is ptr-as-i64, need explicit String type tracking
58
+ # - Records: User-defined types, need explicit type name tracking
59
+ # - Integers/Booleans: Types inferred from LLVM types (i64, i1), no special handling needed
60
+ private def handle_value_expression(mod, scope)
61
+ if value_expression.is_a?(Stone::AST::StringLiteral)
62
+ mod.register_string_constant(identifier, value_expression)
63
+ scope.declare_type(identifier, type: Stone::Type::String)
64
+ end
65
+ register_record_instance(mod, scope)
66
+ end
67
+
68
+ # Register a record type definition and generate its type constant
69
+ private def register_record_type(mod, builder = nil, scope = Stone::Scope.top_level)
70
+ mod.register_record_type(identifier, value_expression)
71
+ # Generate constructor function and type constant
72
+ value_expression.to_llir(builder, mod, scope) if builder
73
+ nil
74
+ end
75
+
76
+ # Register a function (lambda) as an alias so it can be called by the constant name
77
+ private def register_function_alias(mod, function, scope)
78
+ mod.register_function_alias(identifier, function)
79
+ scope.define(identifier, value: function)
80
+ nil
81
+ end
82
+
83
+ # Use true LLVM constants, when we can
84
+ private def initialize_as_constant(global, llvm_value)
85
+ global.global_constant = true
86
+ global.initializer = llvm_value
87
+ end
88
+
89
+ # Runtime initialization for non-constant expressions
90
+ private def initialize_at_runtime(global, builder, llvm_value)
91
+ global.global_constant = false # Allow runtime update
92
+ global.initializer = llvm_value.type.null
93
+ builder.store(llvm_value, global)
94
+ end
95
+
96
+ private def literal_constant?
97
+ value_expression.is_a?(Stone::AST::IntegerLiteral) ||
98
+ value_expression.is_a?(Stone::AST::BooleanLiteral) ||
99
+ value_expression.is_a?(Stone::AST::StringLiteral)
100
+ end
101
+
102
+ private def register_record_instance(mod, scope)
103
+ type_name = record_type_name(mod)
104
+ return unless type_name
105
+
106
+ mod.register_record_instance(identifier, type_name)
107
+ record_type = Stone::TypeRegistry.instance.lookup(type_name)
108
+ scope.declare_type(identifier, type: record_type) if record_type
109
+ end
110
+
111
+ private def record_type_name(mod)
112
+ return value_expression.record_type_name if value_expression.is_a?(Stone::AST::RecordInstantiation)
113
+ return value_expression.function_name if record_constructor_call?(mod)
114
+
115
+ nil
116
+ end
117
+
118
+ private def record_constructor_call?(mod)
119
+ value_expression.is_a?(Stone::AST::FunctionCall) && mod.record_type?(value_expression.function_name)
120
+ end
121
+
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,12 @@
1
+ require "stone/ast"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class Expression < Stone::AST
7
+
8
+ def type(_context = nil) = fail NotImplementedError, "#{self.class} must implement #type"
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,291 @@
1
+ require "stone/ast/expression"
2
+ require "stone/libc"
3
+ require "stone/error/argument_error"
4
+
5
+
6
+ module Stone
7
+ class AST
8
+ class FunctionCall < Stone::AST::Expression
9
+
10
+ attr_reader :function_name, :arguments
11
+
12
+ def initialize(function_name, arguments)
13
+ @function_name = function_name
14
+ @arguments = arguments
15
+ @name = :function_call
16
+ end
17
+
18
+ def to_llir(builder, mod, scope = Stone::Scope.top_level)
19
+ # NULL comparisons with non-NULL values are always unequal (different types)
20
+ return null_comparison_result if mixed_null_comparison?
21
+
22
+ # TODO: Record equality should be delegated to the type system.
23
+ # When the type system is refactored, equality should be polymorphic:
24
+ # - Global `==` checks that both args are the same type
25
+ # - If same type, delegate to type-specific comparison
26
+ # - This special case should be removed
27
+ return generate_record_equality(builder, mod, scope) if record_equality_comparison?(mod)
28
+ # TODO: Record instantiation should not be special-cased here.
29
+ # When the type system is refactored, record constructors should be
30
+ # regular functions, and this check should be removed.
31
+ return instantiate_record(builder, mod, scope) if mod.record_type?(function_name)
32
+
33
+ # For chained comparisons, generate inline comparison logic
34
+ return generate_chained_comparison(builder, mod, scope) if chained_comparison?
35
+
36
+ # For chained boolean operations, generate inline logic
37
+ return generate_chained_boolean(builder, mod, scope) if chained_boolean?
38
+
39
+ # Regular function call
40
+ generate_function_call(builder, mod, scope)
41
+ end
42
+
43
+ def to_s
44
+ "#{function_name}(#{arguments.join(', ')})"
45
+ end
46
+
47
+ def type(context = nil)
48
+ return Stone::Type::Bool if comparison_operator? || boolean_operator?
49
+ return record_constructor_type if context&.record_type?(function_name)
50
+
51
+ function_return_type
52
+ end
53
+
54
+ private def record_constructor_type
55
+ Stone::Type::Registry.lookup(function_name)
56
+ end
57
+
58
+ private def function_return_type
59
+ Stone::Type::Registry.lookup(function_name)&.return_type
60
+ end
61
+
62
+ private def generate_function_call(builder, mod, scope)
63
+ func = mod.lookup_function(function_name)
64
+ fail Stone::ReferenceError, "undefined function: #{function_name}" unless func
65
+
66
+ validate_argument_count(func.function_type.argument_types.size)
67
+ args = evaluate_arguments(builder, mod, scope)
68
+ builder.call(func, *args, "#{function_name}_result")
69
+ end
70
+
71
+ private def chained_comparison?
72
+ comparison_operator? && arguments.length > 2
73
+ end
74
+
75
+ private def comparison_operator?
76
+ %w[== != ≠ < <= ≤ > >= ≥].include?(function_name)
77
+ end
78
+
79
+ private def boolean_operator?
80
+ %w[∧ ∨ ⊻ ¬].include?(function_name)
81
+ end
82
+
83
+ private def chained_boolean?
84
+ boolean_operator? && arguments.length > 2
85
+ end
86
+
87
+ private def generate_chained_boolean(builder, mod, scope)
88
+ # Generate inline code for chained boolean operations
89
+ # ∧(a, b, c) generates: (a && b) && c
90
+ func = lookup_and_validate_function(mod)
91
+ args = evaluate_arguments(builder, mod, scope)
92
+ build_chained_boolean_op(builder, func, args)
93
+ end
94
+
95
+ private def lookup_and_validate_function(mod)
96
+ func = mod.lookup_function(function_name)
97
+ fail Stone::ReferenceError, "undefined function: #{function_name}" unless func
98
+
99
+ func
100
+ end
101
+
102
+ private def build_chained_boolean_op(builder, func, args)
103
+ # Compute first pair using the function
104
+ result = builder.call(func, args[0], args[1], "bool_0")
105
+
106
+ # Chain remaining operands left-to-right: (a ⊻ b) ⊻ c
107
+ # This produces: ((a op b) op c) op d ...
108
+ # NOT overlapping pairs like: (a op b) and (b op c)
109
+ remaining_args = args[2..]
110
+ remaining_args.each_with_index do |arg, i|
111
+ result = chain_with_next_operand(builder, result, arg, i + 1)
112
+ end
113
+
114
+ result
115
+ end
116
+
117
+ private def chain_with_next_operand(builder, current_result, next_arg, index)
118
+ # NOTE: NOT (¬) is unary and never reaches this code path
119
+ # because chained_boolean? requires arguments.length > 2
120
+ case function_name
121
+ when "∧"
122
+ builder.and(current_result, next_arg, "and_#{index}")
123
+ when "∨"
124
+ builder.or(current_result, next_arg, "or_#{index}")
125
+ when "⊻"
126
+ builder.xor(current_result, next_arg, "xor_#{index}")
127
+ else
128
+ fail "Unsupported boolean operator for chaining: #{function_name}"
129
+ end
130
+ end
131
+
132
+ private def generate_chained_comparison(builder, mod, scope)
133
+ # Generate inline code for chained comparisons
134
+ # <(a, b, c) generates: (a < b) && (b < c)
135
+ func = lookup_and_validate_function(mod)
136
+ args = evaluate_arguments(builder, mod, scope)
137
+ build_comparison_conjunction(builder, func, args)
138
+ end
139
+
140
+
141
+ private def build_comparison_conjunction(builder, func, args)
142
+ result = builder.call(func, args[0], args[1], "cmp_0")
143
+
144
+ (1...args.length - 1).each do |i|
145
+ pair_result = builder.call(func, args[i], args[i + 1], "cmp_#{i}")
146
+ result = builder.and(result, pair_result, "and_#{i}")
147
+ end
148
+
149
+ result
150
+ end
151
+
152
+ private def validate_argument_count(expected)
153
+ return if arguments.length == expected
154
+
155
+ fail Stone::ArgumentError, "wrong number of arguments for #{function_name} (given #{arguments.length}, expected #{expected})"
156
+ end
157
+
158
+ private def evaluate_arguments(builder, mod, scope)
159
+ arguments.map { |arg| arg.to_llir(builder, mod, scope) }
160
+ end
161
+
162
+ private def record_equality_comparison?(mod)
163
+ return false unless equality_operator?
164
+ return false unless arguments.size == 2
165
+
166
+ both_arguments_are_records?(mod)
167
+ end
168
+
169
+ private def both_arguments_are_records?(mod)
170
+ Stone::AST::RecordHelpers.record_instance?(arguments[0], mod) &&
171
+ Stone::AST::RecordHelpers.record_instance?(arguments[1], mod)
172
+ end
173
+
174
+ private def instantiate_record(builder, mod, scope)
175
+ record_instantiation = Stone::AST::RecordInstantiation.new(function_name, arguments)
176
+ record_instantiation.to_llir(builder, mod, scope)
177
+ end
178
+
179
+ private def equality_operator?
180
+ %w[== != ≠].include?(function_name)
181
+ end
182
+
183
+ private def mixed_null_comparison?
184
+ return false unless equality_operator?
185
+ return false unless arguments.size == 2
186
+
187
+ null_args = arguments.count { |arg| arg.is_a?(Stone::AST::NullLiteral) }
188
+ return false unless null_args == 1 # Exactly one NULL
189
+
190
+ # If the non-NULL operand returns a pointer, it's a valid pointer comparison
191
+ non_null_arg = arguments.find { |arg| !arg.is_a?(Stone::AST::NullLiteral) }
192
+ !returns_pointer?(non_null_arg)
193
+ end
194
+
195
+ private def returns_pointer?(node)
196
+ # PropertyAccess to a recursive field returns a pointer
197
+ return true if node.is_a?(Stone::AST::PropertyAccess)
198
+
199
+ false
200
+ end
201
+
202
+ private def null_comparison_result
203
+ # For ==: different types are not equal, return false
204
+ # For != or ≠: different types are not equal, return true
205
+ function_name == "==" ? LLVM::FALSE : LLVM::TRUE
206
+ end
207
+
208
+ private def generate_record_equality(builder, mod, scope)
209
+ records = evaluate_record_arguments(builder, mod, scope)
210
+ type1, type2 = get_record_types(mod)
211
+
212
+ return different_types_result if type1 != type2
213
+
214
+ result = compare_all_fields(builder, records, mod.record_types[type1], mod)
215
+ apply_not_operator(builder, result)
216
+ end
217
+
218
+ private def evaluate_record_arguments(builder, mod, scope)
219
+ [arguments[0].to_llir(builder, mod, scope), arguments[1].to_llir(builder, mod, scope)]
220
+ end
221
+
222
+ private def get_record_types(mod)
223
+ [mod.record_instance_type(arguments[0].identifier), mod.record_instance_type(arguments[1].identifier)]
224
+ end
225
+
226
+ private def different_types_result
227
+ function_name == "==" ? LLVM::Int1.from_i(0) : LLVM::Int1.from_i(1)
228
+ end
229
+
230
+ private def apply_not_operator(builder, result)
231
+ function_name == "==" ? result : builder.not(result, "not_equal")
232
+ end
233
+
234
+ private def compare_all_fields(builder, records, record_def, mod)
235
+ fields = record_def.fields
236
+ result = compare_fields_at_index(builder, records, fields, mod, 0)
237
+
238
+ (1...fields.size).each do |i|
239
+ field_equal = compare_fields_at_index(builder, records, fields, mod, i)
240
+ result = builder.and(result, field_equal, "and_#{i}")
241
+ end
242
+
243
+ result
244
+ end
245
+
246
+ private def compare_fields_at_index(builder, records, fields, mod, index)
247
+ field = fields[index]
248
+ val1 = builder.extract_value(records[0], index, "field1_#{field[:name]}")
249
+ val2 = builder.extract_value(records[1], index, "field2_#{field[:name]}")
250
+
251
+ compare_field_values(builder, val1, val2, field[:type_name], mod)
252
+ end
253
+
254
+ private def compare_field_values(builder, val1, val2, type_name, mod)
255
+ case type_name
256
+ when "Int", "Bool"
257
+ builder.icmp(:eq, val1, val2, "field_eq")
258
+ when "String"
259
+ # Stone represents strings as i64 pointers to null-terminated C strings
260
+ # We need to compare the string contents, not just the pointers
261
+ compare_strings(builder, val1, val2, mod)
262
+ else
263
+ fail "Unknown type for comparison: #{type_name}"
264
+ end
265
+ end
266
+
267
+ private def compare_strings(builder, str_ptr1, str_ptr2, mod)
268
+ # Optimization: check if pointers are equal first
269
+ # If pointers differ, we still need strcmp for content comparison
270
+ ptrs_equal = builder.icmp(:eq, str_ptr1, str_ptr2, "ptrs_eq")
271
+
272
+ # Convert i64 pointers back to i8* for strcmp
273
+ ptr1 = builder.int2ptr(str_ptr1, LLVM::Type.pointer(LLVM::Int8), "ptr1")
274
+ ptr2 = builder.int2ptr(str_ptr2, LLVM::Type.pointer(LLVM::Int8), "ptr2")
275
+
276
+ # Declare or get strcmp function
277
+ strcmp_func = Stone::LibC.get_or_declare_strcmp(mod)
278
+ strcmp_result = builder.call(strcmp_func, ptr1, ptr2, "strcmp_result")
279
+
280
+ # strcmp returns 0 if strings are equal
281
+ strings_equal = builder.icmp(:eq, strcmp_result, LLVM::Int32.from_i(0), "strings_eq")
282
+
283
+ # Return true if pointers are equal OR string contents are equal
284
+ # Note: This always calls strcmp, but LLVM's optimizer will likely eliminate
285
+ # the strcmp call when ptrs_equal is true at compile time
286
+ builder.or(ptrs_equal, strings_equal, "str_cmp_result")
287
+ end
288
+
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,31 @@
1
+ require "stone/ast"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class FunctionTypeAnnotation < Stone::AST
7
+
8
+ attr_reader :param_types, :return_type
9
+
10
+ def initialize(param_types, return_type)
11
+ @param_types = param_types
12
+ @return_type = return_type
13
+ @name = :function_type_annotation
14
+ end
15
+
16
+ def to_s
17
+ params = @param_types.map(&:to_s).join(", ")
18
+ return_str = @return_type.is_a?(FunctionTypeAnnotation) ? "(#{@return_type})" : @return_type.to_s
19
+ "(#{params}) -> #{return_str}"
20
+ end
21
+
22
+ # Convert to Stone::Type for type checking
23
+ def to_type(registry)
24
+ param_stone_types = @param_types.map { |t| t.to_type(registry) }
25
+ return_stone_type = @return_type.to_type(registry)
26
+ Stone::Type.function(param_types: param_stone_types, return_type: return_stone_type)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,46 @@
1
+ require "stone/ast/expression"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class IntegerLiteral < Stone::AST::Expression
7
+
8
+ BASES = {
9
+ "0b" => 2,
10
+ "0o" => 8,
11
+ "0x" => 16
12
+ }.freeze
13
+
14
+ # TODO: This looks like a good place to set a good example: refactor into a Method Object.
15
+ def self.parse(text, location)
16
+ sign = text.start_with?("-") ? -1 : 1
17
+ unsigned_text = text.sub(/^[+-]/, "")
18
+ base = BASES.fetch(unsigned_text[0..1], 10)
19
+ digits = base == 10 ? unsigned_text : unsigned_text[2..]
20
+ value = sign * Integer(digits, base)
21
+ fail Stone::Error::Overflow.new(location:, literal: text) unless in_range?(value)
22
+ new(value)
23
+ end
24
+
25
+ def self.in_range?(value)
26
+ value >= Stone::Type::Int.min && value <= Stone::Type::Int.max
27
+ end
28
+
29
+ attr_reader :value
30
+
31
+ def initialize(value)
32
+ @value = value
33
+ @name = :integer_literal
34
+ end
35
+
36
+ def to_llir(_builder, _mod, _scope = Stone::Scope.top_level)
37
+ LLVM::Int64.from_i(@value)
38
+ end
39
+
40
+ def type(_context = nil)
41
+ Stone::Type::Int
42
+ end
43
+
44
+ end
45
+ end
46
+ end