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
data/lib/stone/type.rb ADDED
@@ -0,0 +1,323 @@
1
+ module Stone
2
+ # Value object representing a type in the Stone language.
3
+ # Each type (Int, Bool, String, etc.) is a singleton instance registered in TypeRegistry.
4
+ # Access via Stone::Type::Int, Stone::Type::Bool, etc.
5
+ class Type
6
+
7
+ # Size in bytes for primitive types, used for union payload sizing
8
+ PRIMITIVE_SIZES = {
9
+ "Int" => 8,
10
+ "Bool" => 1,
11
+ "String" => 8, # pointer size
12
+ "Null" => 0,
13
+ "Type" => 8, # pointer size
14
+ "FieldList" => 8 # pointer size
15
+ }.freeze
16
+
17
+ # Alignment requirements for primitive types
18
+ PRIMITIVE_ALIGNMENTS = {
19
+ "Int" => 8,
20
+ "Bool" => 1,
21
+ "String" => 8, # pointer alignment
22
+ "Null" => 1,
23
+ "Type" => 8, # pointer alignment
24
+ "FieldList" => 8 # pointer alignment
25
+ }.freeze
26
+
27
+ attr_reader :name, :llvm_type, :fields, :min, :max, :param_types, :return_type
28
+ attr_accessor :property_types
29
+
30
+ def initialize(name:, llvm_type:, property_types: {}, fields: nil, **options)
31
+ @name = name
32
+ @llvm_type = llvm_type
33
+ @property_types = property_types
34
+ @fields = fields
35
+ @primitive = options[:primitive] || false
36
+ @min = options[:min]
37
+ @max = options[:max]
38
+ @param_types = options[:param_types]
39
+ @return_type = options[:return_type]
40
+ end
41
+
42
+ def property_return_type(property_name)
43
+ @property_types[property_name] || field_type(property_name)
44
+ end
45
+
46
+ private def field_type(field_name)
47
+ return nil unless @fields
48
+
49
+ field = @fields.find { |f| f[:name] == field_name }
50
+ return nil unless field
51
+
52
+ Stone::AST::FieldHelpers.resolve_field_type(field)
53
+ end
54
+
55
+ def primitive?
56
+ @primitive
57
+ end
58
+
59
+ def record?
60
+ !@primitive && !@fields.nil?
61
+ end
62
+
63
+ def size_bytes
64
+ return PRIMITIVE_SIZES[@name] if primitive? && PRIMITIVE_SIZES.key?(@name)
65
+ # Records are stored as pointers in unions (to avoid infinite recursion with recursive types)
66
+ return 8 if record? # pointer size
67
+ return 8 if function? # function pointer
68
+
69
+ 8 # default
70
+ end
71
+
72
+ def alignment
73
+ return PRIMITIVE_ALIGNMENTS[@name] if primitive? && PRIMITIVE_ALIGNMENTS.key?(@name)
74
+ # Records are stored as pointers in unions
75
+ return 8 if record? # pointer alignment
76
+
77
+ 8 # default pointer alignment
78
+ end
79
+
80
+ def pointer_type?
81
+ %w[String Null].include?(@name) || record?
82
+ end
83
+
84
+ def payload_llvm_type
85
+ case @name
86
+ when "Null" then LLVM::Type.pointer
87
+ when "Int" then LLVM::Int64.type
88
+ when "Bool" then LLVM::Int1.type
89
+ when "String" then LLVM::Type.pointer
90
+ else LLVM::Type.pointer # records and other pointer types
91
+ end
92
+ end
93
+
94
+ def function?
95
+ @param_types.is_a?(Array)
96
+ end
97
+
98
+ def union?
99
+ false
100
+ end
101
+
102
+ def nullable?
103
+ false
104
+ end
105
+
106
+ def non_null_type
107
+ self
108
+ end
109
+
110
+ def compatible_with?(other)
111
+ return true if self == other
112
+ return other.alternatives.any? { |alt| compatible_with?(alt) } if other.union?
113
+
114
+ function? && function_compatible_with?(other)
115
+ end
116
+
117
+ private def function_compatible_with?(other)
118
+ return false unless other.function?
119
+ return false unless param_types.length == other.param_types.length
120
+
121
+ params_compatible?(other) && return_type.compatible_with?(other.return_type)
122
+ end
123
+
124
+ private def params_compatible?(other)
125
+ param_types.zip(other.param_types).all? { |a, b| a.compatible_with?(b) }
126
+ end
127
+
128
+ def as_String
129
+ name
130
+ end
131
+
132
+ def ==(other)
133
+ other.is_a?(self.class) && other.name == name
134
+ end
135
+ alias eql? ==
136
+
137
+ def hash
138
+ name.hash
139
+ end
140
+
141
+ def to_s
142
+ name
143
+ end
144
+
145
+ def inspect
146
+ "#<Stone::Type:#{name}>"
147
+ end
148
+
149
+ def self.primitive(name:, llvm_type:, property_types: {}, min: nil, max: nil)
150
+ new(name:, llvm_type:, property_types:, primitive: true, min:, max:)
151
+ end
152
+
153
+ def self.record(name:, fields:, llvm_type:)
154
+ new(name:, llvm_type:, fields:, primitive: false)
155
+ end
156
+
157
+ def self.function(param_types:, return_type:)
158
+ param_names = param_types.map(&:name).join(", ")
159
+ return_name = return_type.function? ? "(#{return_type.name})" : return_type.name
160
+ name = "(#{param_names}) -> #{return_name}"
161
+ new(name:, llvm_type: nil, param_types:, return_type:)
162
+ end
163
+
164
+ def self.union(alternatives:)
165
+ union = Union.new(alternatives:)
166
+ union.alternatives.length == 1 ? union.alternatives.first : union
167
+ end
168
+
169
+ # Union type - represents a value that can be one of several types
170
+ class Union < Type
171
+ attr_reader :alternatives
172
+
173
+ def initialize(alternatives:)
174
+ @alternatives = flatten_and_dedupe(alternatives)
175
+ fail ::ArgumentError, "Union type requires at least one alternative" if @alternatives.empty?
176
+
177
+ super(name: generate_name, llvm_type: create_variable_sized_llvm_type)
178
+ end
179
+
180
+ def size_bytes
181
+ 8 + payload_size # tag pointer (8 bytes) + payload
182
+ end
183
+
184
+ def alignment
185
+ # Union alignment is max of pointer alignment and payload alignment
186
+ [8, payload_alignment].max
187
+ end
188
+
189
+ def payload_size
190
+ @alternatives.map(&:size_bytes).max || 0
191
+ end
192
+
193
+ def payload_alignment
194
+ @alternatives.map(&:alignment).max || 1
195
+ end
196
+
197
+ def common_llvm_result_type
198
+ # Determine best common type for phi merge without int2ptr/ptr2int conversions.
199
+ # - If ALL non-null alternatives are pointers, use pointer
200
+ # - If ALL non-null alternatives are integers, use i64
201
+ # - For MIXED types, return nil (caller should not extract, return union as-is)
202
+ non_null_alts = @alternatives.reject { |alt| alt.name == "Null" }
203
+ return LLVM::Int64.type if non_null_alts.empty?
204
+
205
+ all_pointers = non_null_alts.all?(&:pointer_type?)
206
+ all_integers = non_null_alts.all? { |alt| %w[Int Bool].include?(alt.name) }
207
+
208
+ return LLVM::Type.pointer if all_pointers
209
+ return LLVM::Int64.type if all_integers
210
+
211
+ nil # Mixed types - don't extract
212
+ end
213
+
214
+ def homogeneous?
215
+ # Returns true if all non-null alternatives have compatible LLVM types
216
+ !common_llvm_result_type.nil?
217
+ end
218
+
219
+ # Returns true if the union contains types that cannot be distinguished by value alone.
220
+ # For example, Bool | Int - both are integers, and Bool(1) looks like Int(1).
221
+ # Such unions require returning the runtime type tag to Ruby for proper interpretation.
222
+ def needs_runtime_type_tag?
223
+ non_null_names = @alternatives.reject { |alt| alt.name == "Null" }.map(&:name)
224
+ non_null_names.include?("Bool") && non_null_names.include?("Int")
225
+ end
226
+
227
+ private def create_variable_sized_llvm_type
228
+ payload_array = LLVM::Type.array(LLVM::Int8.type, payload_size)
229
+ LLVM::Type.struct([LLVM::Type.pointer, payload_array], false)
230
+ end
231
+
232
+ def union?
233
+ true
234
+ end
235
+
236
+ def primitive?
237
+ false
238
+ end
239
+
240
+ def record?
241
+ false
242
+ end
243
+
244
+ def function?
245
+ false
246
+ end
247
+
248
+ def nullable?
249
+ @alternatives.any? { |t| null_type?(t) }
250
+ end
251
+
252
+ # Look through non-null alternatives for a property.
253
+ # Returns the property type if ALL non-null alternatives have it with the same type.
254
+ # This enables chained access like `o.value.x` where `value` is `Point | Null`.
255
+ def property_return_type(property_name)
256
+ non_null_alts = @alternatives.reject { |alt| null_type?(alt) }
257
+ return nil if non_null_alts.empty?
258
+
259
+ # Get property type from each non-null alternative
260
+ prop_types = non_null_alts.map { |alt| alt.property_return_type(property_name) }
261
+
262
+ # All alternatives must have this property
263
+ return nil if prop_types.any?(&:nil?)
264
+
265
+ # For now, require all alternatives to return the same type
266
+ # (Future: could return a union of the return types)
267
+ return nil unless prop_types.uniq.length == 1
268
+
269
+ prop_types.first
270
+ end
271
+
272
+ def non_null_type
273
+ remaining = @alternatives.reject { |t| null_type?(t) }
274
+ return remaining.first if remaining.length == 1
275
+
276
+ Stone::Type.union(alternatives: remaining)
277
+ end
278
+
279
+ def compatible_with?(other)
280
+ other.union? ? covers_all_alternatives?(other) : includes_compatible_type?(other)
281
+ end
282
+
283
+ private def covers_all_alternatives?(union)
284
+ union.alternatives.all? { |alt| includes_compatible_type?(alt) }
285
+ end
286
+
287
+ private def includes_compatible_type?(type)
288
+ @alternatives.any? { |t| t.compatible_with?(type) }
289
+ end
290
+
291
+ def ==(other)
292
+ return false unless other.is_a?(Union)
293
+
294
+ Set.new(@alternatives) == Set.new(other.alternatives)
295
+ end
296
+ alias eql? ==
297
+
298
+ def hash
299
+ Set.new(@alternatives).hash
300
+ end
301
+
302
+ private def generate_name
303
+ names = @alternatives.map(&:name)
304
+ non_null = names.reject { |n| n == "Null" }.sort
305
+ null = names.select { |n| n == "Null" }
306
+ (non_null + null).join(" | ")
307
+ end
308
+
309
+ private def flatten_and_dedupe(types)
310
+ flattened = types.flat_map { |t| t.union? ? t.alternatives : [t] }
311
+ flattened.uniq
312
+ end
313
+
314
+ private def null_type?(type)
315
+ type.name == "Null"
316
+ end
317
+ end
318
+
319
+ end
320
+
321
+ # Backwards compatibility alias (deprecated - use Stone::Type directly)
322
+ TypeInstance = Type
323
+ end
@@ -0,0 +1,38 @@
1
+ module Stone
2
+ class TypeContext
3
+
4
+ attr_reader :llvm_module
5
+
6
+ def initialize(llvm_module = nil)
7
+ @llvm_module = llvm_module
8
+ @bindings = {}
9
+ end
10
+
11
+ def bind(name, type)
12
+ @bindings[name] = type
13
+ end
14
+
15
+ def lookup(name)
16
+ @bindings[name]
17
+ end
18
+
19
+ def with_llvm_module(mod)
20
+ new_context = self.class.new(mod)
21
+ new_context.instance_variable_set(:@bindings, @bindings.dup)
22
+ new_context
23
+ end
24
+
25
+ def record_type?(name)
26
+ return false unless @llvm_module
27
+
28
+ @llvm_module.record_type?(name)
29
+ end
30
+
31
+ def record_definition(name)
32
+ return nil unless @llvm_module
33
+
34
+ @llvm_module.record_types&.[](name)
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,73 @@
1
+ require "singleton"
2
+
3
+
4
+ module Stone
5
+ class TypeRegistry
6
+ include Singleton
7
+
8
+ def initialize
9
+ @types = {}
10
+ end
11
+
12
+ def register(type)
13
+ @types[type.name] = type
14
+ type
15
+ end
16
+
17
+ def register_as(name, type)
18
+ @types[name] = type
19
+ type
20
+ end
21
+
22
+ def lookup(name)
23
+ @types[name]
24
+ end
25
+ alias [] lookup
26
+
27
+ def registered?(name)
28
+ @types.key?(name)
29
+ end
30
+
31
+ def record?(name)
32
+ type = @types[name]
33
+ return false unless type
34
+
35
+ !type.primitive?
36
+ end
37
+
38
+ def all
39
+ @types.values
40
+ end
41
+
42
+ def primitives
43
+ @types.values.select(&:primitive?)
44
+ end
45
+
46
+ def records
47
+ @types.values.reject(&:primitive?)
48
+ end
49
+
50
+ # Convenience accessors for built-in types
51
+ def int
52
+ @types["Int"]
53
+ end
54
+
55
+ def bool
56
+ @types["Bool"]
57
+ end
58
+
59
+ def string
60
+ @types["String"]
61
+ end
62
+
63
+ def type
64
+ @types["Type"]
65
+ end
66
+
67
+ # Reset registry (useful for testing)
68
+ def reset!
69
+ @types.clear
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,104 @@
1
+ require "llvm/core"
2
+ require "stone/type"
3
+ require "stone/type_registry"
4
+
5
+ # LLVM type alias for convenience
6
+ I64 = LLVM::Int64.type
7
+
8
+
9
+ # Bootstrap the TypeRegistry with primitive types
10
+ module Stone
11
+ module Types
12
+ module_function
13
+
14
+ INT_MIN = -(2**63) # -9_223_372_036_854_775_808
15
+ INT_MAX = 2**63 - 1 # +9_223_372_036_854_775_807
16
+
17
+ def bootstrap_registry!
18
+ types = create_primitive_types
19
+ register_all_types(types)
20
+ setup_property_types(types)
21
+ setup_type_constants(types)
22
+ Stone::TypeRegistry.instance
23
+ end
24
+
25
+ def create_primitive_types
26
+ {
27
+ int: Stone::Type.primitive(name: "Int", llvm_type: LLVM::Int64.type, min: INT_MIN, max: INT_MAX),
28
+ bool: Stone::Type.primitive(name: "Bool", llvm_type: LLVM::Int1.type),
29
+ string: Stone::Type.primitive(name: "String", llvm_type: LLVM::Int64.type),
30
+ type: Stone::Type.primitive(name: "Type", llvm_type: LLVM::Type.pointer),
31
+ null: Stone::Type.primitive(name: "Null", llvm_type: LLVM::Type.ptr),
32
+ # FieldList is a list-like type for record field metadata
33
+ # TODO: Replace with List(Field) once generics are available
34
+ field_list: Stone::Type.primitive(name: "FieldList", llvm_type: LLVM::Type.pointer)
35
+ }
36
+ end
37
+
38
+ def register_all_types(types)
39
+ registry = Stone::TypeRegistry.instance
40
+ types.each_value { |type| registry.register(type) }
41
+ end
42
+
43
+ def setup_property_types(types)
44
+ setup_int_properties(types)
45
+ setup_bool_properties(types)
46
+ setup_string_properties(types)
47
+ setup_type_properties(types)
48
+ setup_field_list_properties(types)
49
+ end
50
+
51
+ def setup_type_properties(types)
52
+ types[:type].property_types.merge!(
53
+ "as_String" => types[:string],
54
+ "record?" => types[:bool],
55
+ "primitive?" => types[:bool],
56
+ "kind" => types[:int],
57
+ "size" => types[:int],
58
+ "fields" => types[:field_list]
59
+ )
60
+ end
61
+
62
+ def setup_field_list_properties(types)
63
+ types[:field_list].property_types.merge!(
64
+ "first" => types[:field_list], # .first returns the FieldList itself (list-like access)
65
+ "name" => types[:string], # Field name
66
+ "type" => types[:type], # Type of the field
67
+ "rest" => types[:field_list] # Next FieldList or NULL
68
+ )
69
+ end
70
+
71
+ def setup_int_properties(types)
72
+ types[:int].property_types.merge!(
73
+ "positive?" => types[:bool], "negative?" => types[:bool],
74
+ "zero?" => types[:bool], "as_String" => types[:string]
75
+ )
76
+ end
77
+
78
+ def setup_bool_properties(types)
79
+ types[:bool].property_types.merge!("not" => types[:bool], "as_String" => types[:string])
80
+ end
81
+
82
+ def setup_string_properties(types)
83
+ types[:string].property_types.merge!(
84
+ "byte_count" => types[:int], "empty?" => types[:bool], "as_String" => types[:string]
85
+ )
86
+ end
87
+
88
+ def setup_type_constants(types)
89
+ # Define type constants on Stone::Type for convenient access
90
+ # Only define if not already defined (avoids warnings during test resets)
91
+ types.each_value do |type|
92
+ define_type_constant(type.name.to_sym, type)
93
+ end
94
+ define_type_constant(:Registry, Stone::TypeRegistry.instance)
95
+ end
96
+
97
+ private def define_type_constant(name, value)
98
+ Stone::Type.const_set(name, value) unless Stone::Type.const_defined?(name, false)
99
+ end
100
+ end
101
+ end
102
+
103
+ # Bootstrap on load
104
+ Stone::Types.bootstrap_registry!
data/lib/stone.rb ADDED
@@ -0,0 +1,70 @@
1
+ # Load extensions
2
+ Dir[File.join(__dir__, "extensions", "*.rb")].each do
3
+ require_relative it
4
+ end
5
+
6
+ require "llvm/core"
7
+ require "llvm/execution_engine"
8
+
9
+ require "extensions/llvm_module"
10
+ require "grammy/scanner"
11
+ require "stone/scope"
12
+ require "stone/error"
13
+ require "stone/error/overflow"
14
+ require "stone/error/reference_error"
15
+ require "stone/error/argument_error"
16
+ require "stone/error/type_error"
17
+ require "stone/grammar"
18
+ require "stone/transform"
19
+ require "stone/ast"
20
+ require "stone/ast/integer_literal"
21
+ require "stone/ast/string_literal"
22
+ require "stone/ast/reference"
23
+ require "stone/ast/function_call"
24
+ require "stone/ast/constant_definition"
25
+ require "stone/ast/computed_property_definition"
26
+ require "stone/ast/program_unit"
27
+
28
+
29
+ module Stone
30
+
31
+ def self.parse(input)
32
+ Stone::Grammar.parse(input)
33
+ end
34
+
35
+ def self.transform(parse_tree)
36
+ transformer = Stone::Transform.new
37
+ transformer.transform(parse_tree)
38
+ end
39
+
40
+ # Returns an AST
41
+ def self.compile(input)
42
+ input_with_prelude = prepend_prelude(input)
43
+ parse_tree = parse(input_with_prelude)
44
+ transform(parse_tree)
45
+ end
46
+
47
+ def self.prepend_prelude(input)
48
+ "#{prelude_code}\n#{input}"
49
+ end
50
+
51
+ def self.prelude_code
52
+ @prelude_code ||= File.read(prelude_path)
53
+ end
54
+
55
+ def self.prelude_path
56
+ File.expand_path("stone/prelude.stone", __dir__)
57
+ end
58
+
59
+ def self.eval(input)
60
+ ast = compile(input)
61
+ ast.eval
62
+ end
63
+
64
+ def self.eval_with_scope(input)
65
+ Stone::Scope.reset_top_level!
66
+ result = self.eval(input)
67
+ [result, Stone::Scope.top_level]
68
+ end
69
+
70
+ end