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
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
|
data/lib/stone/types.rb
ADDED
|
@@ -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
|