t-ruby 0.0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/bin/trc +6 -0
- data/lib/t_ruby/benchmark.rb +592 -0
- data/lib/t_ruby/bundler_integration.rb +569 -0
- data/lib/t_ruby/cache.rb +774 -0
- data/lib/t_ruby/cli.rb +106 -0
- data/lib/t_ruby/compiler.rb +299 -0
- data/lib/t_ruby/config.rb +53 -0
- data/lib/t_ruby/constraint_checker.rb +441 -0
- data/lib/t_ruby/declaration_generator.rb +298 -0
- data/lib/t_ruby/doc_generator.rb +474 -0
- data/lib/t_ruby/error_handler.rb +132 -0
- data/lib/t_ruby/generic_type_parser.rb +68 -0
- data/lib/t_ruby/intersection_type_parser.rb +30 -0
- data/lib/t_ruby/ir.rb +1301 -0
- data/lib/t_ruby/lsp_server.rb +994 -0
- data/lib/t_ruby/package_manager.rb +735 -0
- data/lib/t_ruby/parser.rb +245 -0
- data/lib/t_ruby/parser_combinator.rb +942 -0
- data/lib/t_ruby/rbs_generator.rb +71 -0
- data/lib/t_ruby/runtime_validator.rb +367 -0
- data/lib/t_ruby/smt_solver.rb +1076 -0
- data/lib/t_ruby/type_alias_registry.rb +102 -0
- data/lib/t_ruby/type_checker.rb +770 -0
- data/lib/t_ruby/type_erasure.rb +26 -0
- data/lib/t_ruby/type_inferencer.rb +580 -0
- data/lib/t_ruby/union_type_parser.rb +38 -0
- data/lib/t_ruby/version.rb +5 -0
- data/lib/t_ruby/watcher.rb +320 -0
- data/lib/t_ruby.rb +42 -0
- metadata +87 -0
data/lib/t_ruby/ir.rb
ADDED
|
@@ -0,0 +1,1301 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TRuby
|
|
4
|
+
module IR
|
|
5
|
+
# Base class for all IR nodes
|
|
6
|
+
class Node
|
|
7
|
+
attr_accessor :location, :type_info, :metadata
|
|
8
|
+
|
|
9
|
+
def initialize(location: nil)
|
|
10
|
+
@location = location
|
|
11
|
+
@type_info = nil
|
|
12
|
+
@metadata = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def accept(visitor)
|
|
16
|
+
visitor.visit(self)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def children
|
|
20
|
+
[]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def transform(&block)
|
|
24
|
+
block.call(self)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Program - root node containing all top-level declarations
|
|
29
|
+
class Program < Node
|
|
30
|
+
attr_accessor :declarations, :source_file
|
|
31
|
+
|
|
32
|
+
def initialize(declarations: [], source_file: nil, **opts)
|
|
33
|
+
super(**opts)
|
|
34
|
+
@declarations = declarations
|
|
35
|
+
@source_file = source_file
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def children
|
|
39
|
+
@declarations
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Type alias declaration: type Name = Definition
|
|
44
|
+
class TypeAlias < Node
|
|
45
|
+
attr_accessor :name, :definition, :type_params
|
|
46
|
+
|
|
47
|
+
def initialize(name:, definition:, type_params: [], **opts)
|
|
48
|
+
super(**opts)
|
|
49
|
+
@name = name
|
|
50
|
+
@definition = definition
|
|
51
|
+
@type_params = type_params
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Interface declaration
|
|
56
|
+
class Interface < Node
|
|
57
|
+
attr_accessor :name, :members, :extends, :type_params
|
|
58
|
+
|
|
59
|
+
def initialize(name:, members: [], extends: [], type_params: [], **opts)
|
|
60
|
+
super(**opts)
|
|
61
|
+
@name = name
|
|
62
|
+
@members = members
|
|
63
|
+
@extends = extends
|
|
64
|
+
@type_params = type_params
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def children
|
|
68
|
+
@members
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Interface member (method signature)
|
|
73
|
+
class InterfaceMember < Node
|
|
74
|
+
attr_accessor :name, :type_signature, :optional
|
|
75
|
+
|
|
76
|
+
def initialize(name:, type_signature:, optional: false, **opts)
|
|
77
|
+
super(**opts)
|
|
78
|
+
@name = name
|
|
79
|
+
@type_signature = type_signature
|
|
80
|
+
@optional = optional
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Class declaration
|
|
85
|
+
class ClassDecl < Node
|
|
86
|
+
attr_accessor :name, :superclass, :implements, :type_params, :body
|
|
87
|
+
|
|
88
|
+
def initialize(name:, superclass: nil, implements: [], type_params: [], body: [], **opts)
|
|
89
|
+
super(**opts)
|
|
90
|
+
@name = name
|
|
91
|
+
@superclass = superclass
|
|
92
|
+
@implements = implements
|
|
93
|
+
@type_params = type_params
|
|
94
|
+
@body = body
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def children
|
|
98
|
+
@body
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Module declaration
|
|
103
|
+
class ModuleDecl < Node
|
|
104
|
+
attr_accessor :name, :body
|
|
105
|
+
|
|
106
|
+
def initialize(name:, body: [], **opts)
|
|
107
|
+
super(**opts)
|
|
108
|
+
@name = name
|
|
109
|
+
@body = body
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def children
|
|
113
|
+
@body
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Method definition
|
|
118
|
+
class MethodDef < Node
|
|
119
|
+
attr_accessor :name, :params, :return_type, :body, :visibility, :type_params
|
|
120
|
+
|
|
121
|
+
def initialize(name:, params: [], return_type: nil, body: nil, visibility: :public, type_params: [], **opts)
|
|
122
|
+
super(**opts)
|
|
123
|
+
@name = name
|
|
124
|
+
@params = params
|
|
125
|
+
@return_type = return_type
|
|
126
|
+
@body = body
|
|
127
|
+
@visibility = visibility
|
|
128
|
+
@type_params = type_params
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def children
|
|
132
|
+
[@body].compact
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Method parameter
|
|
137
|
+
class Parameter < Node
|
|
138
|
+
attr_accessor :name, :type_annotation, :default_value, :kind
|
|
139
|
+
|
|
140
|
+
# kind: :required, :optional, :rest, :keyrest, :block
|
|
141
|
+
def initialize(name:, type_annotation: nil, default_value: nil, kind: :required, **opts)
|
|
142
|
+
super(**opts)
|
|
143
|
+
@name = name
|
|
144
|
+
@type_annotation = type_annotation
|
|
145
|
+
@default_value = default_value
|
|
146
|
+
@kind = kind
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Block of statements
|
|
151
|
+
class Block < Node
|
|
152
|
+
attr_accessor :statements
|
|
153
|
+
|
|
154
|
+
def initialize(statements: [], **opts)
|
|
155
|
+
super(**opts)
|
|
156
|
+
@statements = statements
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def children
|
|
160
|
+
@statements
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Variable assignment
|
|
165
|
+
class Assignment < Node
|
|
166
|
+
attr_accessor :target, :value, :type_annotation
|
|
167
|
+
|
|
168
|
+
def initialize(target:, value:, type_annotation: nil, **opts)
|
|
169
|
+
super(**opts)
|
|
170
|
+
@target = target
|
|
171
|
+
@value = value
|
|
172
|
+
@type_annotation = type_annotation
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def children
|
|
176
|
+
[@value]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Variable reference
|
|
181
|
+
class VariableRef < Node
|
|
182
|
+
attr_accessor :name, :scope
|
|
183
|
+
|
|
184
|
+
# scope: :local, :instance, :class, :global
|
|
185
|
+
def initialize(name:, scope: :local, **opts)
|
|
186
|
+
super(**opts)
|
|
187
|
+
@name = name
|
|
188
|
+
@scope = scope
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Method call
|
|
193
|
+
class MethodCall < Node
|
|
194
|
+
attr_accessor :receiver, :method_name, :arguments, :block, :type_args
|
|
195
|
+
|
|
196
|
+
def initialize(method_name:, receiver: nil, arguments: [], block: nil, type_args: [], **opts)
|
|
197
|
+
super(**opts)
|
|
198
|
+
@receiver = receiver
|
|
199
|
+
@method_name = method_name
|
|
200
|
+
@arguments = arguments
|
|
201
|
+
@block = block
|
|
202
|
+
@type_args = type_args
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def children
|
|
206
|
+
([@receiver, @block] + @arguments).compact
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Literal values
|
|
211
|
+
class Literal < Node
|
|
212
|
+
attr_accessor :value, :literal_type
|
|
213
|
+
|
|
214
|
+
def initialize(value:, literal_type:, **opts)
|
|
215
|
+
super(**opts)
|
|
216
|
+
@value = value
|
|
217
|
+
@literal_type = literal_type
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Array literal
|
|
222
|
+
class ArrayLiteral < Node
|
|
223
|
+
attr_accessor :elements, :element_type
|
|
224
|
+
|
|
225
|
+
def initialize(elements: [], element_type: nil, **opts)
|
|
226
|
+
super(**opts)
|
|
227
|
+
@elements = elements
|
|
228
|
+
@element_type = element_type
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def children
|
|
232
|
+
@elements
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Hash literal
|
|
237
|
+
class HashLiteral < Node
|
|
238
|
+
attr_accessor :pairs, :key_type, :value_type
|
|
239
|
+
|
|
240
|
+
def initialize(pairs: [], key_type: nil, value_type: nil, **opts)
|
|
241
|
+
super(**opts)
|
|
242
|
+
@pairs = pairs
|
|
243
|
+
@key_type = key_type
|
|
244
|
+
@value_type = value_type
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Hash pair (key => value)
|
|
249
|
+
class HashPair < Node
|
|
250
|
+
attr_accessor :key, :value
|
|
251
|
+
|
|
252
|
+
def initialize(key:, value:, **opts)
|
|
253
|
+
super(**opts)
|
|
254
|
+
@key = key
|
|
255
|
+
@value = value
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def children
|
|
259
|
+
[@key, @value]
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Conditional (if/unless)
|
|
264
|
+
class Conditional < Node
|
|
265
|
+
attr_accessor :condition, :then_branch, :else_branch, :kind
|
|
266
|
+
|
|
267
|
+
# kind: :if, :unless, :ternary
|
|
268
|
+
def initialize(condition:, then_branch:, else_branch: nil, kind: :if, **opts)
|
|
269
|
+
super(**opts)
|
|
270
|
+
@condition = condition
|
|
271
|
+
@then_branch = then_branch
|
|
272
|
+
@else_branch = else_branch
|
|
273
|
+
@kind = kind
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def children
|
|
277
|
+
[@condition, @then_branch, @else_branch].compact
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Case/when expression
|
|
282
|
+
class CaseExpr < Node
|
|
283
|
+
attr_accessor :subject, :when_clauses, :else_clause
|
|
284
|
+
|
|
285
|
+
def initialize(subject: nil, when_clauses: [], else_clause: nil, **opts)
|
|
286
|
+
super(**opts)
|
|
287
|
+
@subject = subject
|
|
288
|
+
@when_clauses = when_clauses
|
|
289
|
+
@else_clause = else_clause
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def children
|
|
293
|
+
([@subject, @else_clause] + @when_clauses).compact
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# When clause
|
|
298
|
+
class WhenClause < Node
|
|
299
|
+
attr_accessor :patterns, :body
|
|
300
|
+
|
|
301
|
+
def initialize(patterns:, body:, **opts)
|
|
302
|
+
super(**opts)
|
|
303
|
+
@patterns = patterns
|
|
304
|
+
@body = body
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def children
|
|
308
|
+
[@body] + @patterns
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Loop constructs
|
|
313
|
+
class Loop < Node
|
|
314
|
+
attr_accessor :kind, :condition, :body
|
|
315
|
+
|
|
316
|
+
# kind: :while, :until, :loop
|
|
317
|
+
def initialize(kind:, condition: nil, body:, **opts)
|
|
318
|
+
super(**opts)
|
|
319
|
+
@kind = kind
|
|
320
|
+
@condition = condition
|
|
321
|
+
@body = body
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def children
|
|
325
|
+
[@condition, @body].compact
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# For loop / each iteration
|
|
330
|
+
class ForLoop < Node
|
|
331
|
+
attr_accessor :variable, :iterable, :body
|
|
332
|
+
|
|
333
|
+
def initialize(variable:, iterable:, body:, **opts)
|
|
334
|
+
super(**opts)
|
|
335
|
+
@variable = variable
|
|
336
|
+
@iterable = iterable
|
|
337
|
+
@body = body
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def children
|
|
341
|
+
[@iterable, @body]
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Return statement
|
|
346
|
+
class Return < Node
|
|
347
|
+
attr_accessor :value
|
|
348
|
+
|
|
349
|
+
def initialize(value: nil, **opts)
|
|
350
|
+
super(**opts)
|
|
351
|
+
@value = value
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def children
|
|
355
|
+
[@value].compact
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Binary operation
|
|
360
|
+
class BinaryOp < Node
|
|
361
|
+
attr_accessor :operator, :left, :right
|
|
362
|
+
|
|
363
|
+
def initialize(operator:, left:, right:, **opts)
|
|
364
|
+
super(**opts)
|
|
365
|
+
@operator = operator
|
|
366
|
+
@left = left
|
|
367
|
+
@right = right
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def children
|
|
371
|
+
[@left, @right]
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Unary operation
|
|
376
|
+
class UnaryOp < Node
|
|
377
|
+
attr_accessor :operator, :operand
|
|
378
|
+
|
|
379
|
+
def initialize(operator:, operand:, **opts)
|
|
380
|
+
super(**opts)
|
|
381
|
+
@operator = operator
|
|
382
|
+
@operand = operand
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def children
|
|
386
|
+
[@operand]
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Type cast / assertion
|
|
391
|
+
class TypeCast < Node
|
|
392
|
+
attr_accessor :expression, :target_type, :kind
|
|
393
|
+
|
|
394
|
+
# kind: :as, :assert
|
|
395
|
+
def initialize(expression:, target_type:, kind: :as, **opts)
|
|
396
|
+
super(**opts)
|
|
397
|
+
@expression = expression
|
|
398
|
+
@target_type = target_type
|
|
399
|
+
@kind = kind
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def children
|
|
403
|
+
[@expression]
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Type guard (is_a?, respond_to?)
|
|
408
|
+
class TypeGuard < Node
|
|
409
|
+
attr_accessor :expression, :type_check, :narrowed_type
|
|
410
|
+
|
|
411
|
+
def initialize(expression:, type_check:, narrowed_type: nil, **opts)
|
|
412
|
+
super(**opts)
|
|
413
|
+
@expression = expression
|
|
414
|
+
@type_check = type_check
|
|
415
|
+
@narrowed_type = narrowed_type
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def children
|
|
419
|
+
[@expression]
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Lambda/Proc definition
|
|
424
|
+
class Lambda < Node
|
|
425
|
+
attr_accessor :params, :body, :return_type
|
|
426
|
+
|
|
427
|
+
def initialize(params: [], body:, return_type: nil, **opts)
|
|
428
|
+
super(**opts)
|
|
429
|
+
@params = params
|
|
430
|
+
@body = body
|
|
431
|
+
@return_type = return_type
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def children
|
|
435
|
+
[@body]
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# Begin/rescue/ensure block
|
|
440
|
+
class BeginBlock < Node
|
|
441
|
+
attr_accessor :body, :rescue_clauses, :else_clause, :ensure_clause
|
|
442
|
+
|
|
443
|
+
def initialize(body:, rescue_clauses: [], else_clause: nil, ensure_clause: nil, **opts)
|
|
444
|
+
super(**opts)
|
|
445
|
+
@body = body
|
|
446
|
+
@rescue_clauses = rescue_clauses
|
|
447
|
+
@else_clause = else_clause
|
|
448
|
+
@ensure_clause = ensure_clause
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def children
|
|
452
|
+
[@body, @else_clause, @ensure_clause].compact + @rescue_clauses
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
# Rescue clause
|
|
457
|
+
class RescueClause < Node
|
|
458
|
+
attr_accessor :exception_types, :variable, :body
|
|
459
|
+
|
|
460
|
+
def initialize(exception_types: [], variable: nil, body:, **opts)
|
|
461
|
+
super(**opts)
|
|
462
|
+
@exception_types = exception_types
|
|
463
|
+
@variable = variable
|
|
464
|
+
@body = body
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def children
|
|
468
|
+
[@body]
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Raw Ruby code (for passthrough)
|
|
473
|
+
class RawCode < Node
|
|
474
|
+
attr_accessor :code
|
|
475
|
+
|
|
476
|
+
def initialize(code:, **opts)
|
|
477
|
+
super(**opts)
|
|
478
|
+
@code = code
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
#==========================================================================
|
|
483
|
+
# Type Representation Nodes
|
|
484
|
+
#==========================================================================
|
|
485
|
+
|
|
486
|
+
# Base type node
|
|
487
|
+
class TypeNode < Node
|
|
488
|
+
def to_rbs
|
|
489
|
+
raise NotImplementedError
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def to_trb
|
|
493
|
+
raise NotImplementedError
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Simple type (String, Integer, etc.)
|
|
498
|
+
class SimpleType < TypeNode
|
|
499
|
+
attr_accessor :name
|
|
500
|
+
|
|
501
|
+
def initialize(name:, **opts)
|
|
502
|
+
super(**opts)
|
|
503
|
+
@name = name
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def to_rbs
|
|
507
|
+
@name
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def to_trb
|
|
511
|
+
@name
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# Generic type (Array<String>, Map<K, V>)
|
|
516
|
+
class GenericType < TypeNode
|
|
517
|
+
attr_accessor :base, :type_args
|
|
518
|
+
|
|
519
|
+
def initialize(base:, type_args: [], **opts)
|
|
520
|
+
super(**opts)
|
|
521
|
+
@base = base
|
|
522
|
+
@type_args = type_args
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def to_rbs
|
|
526
|
+
"#{@base}[#{@type_args.map(&:to_rbs).join(', ')}]"
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def to_trb
|
|
530
|
+
"#{@base}<#{@type_args.map(&:to_trb).join(', ')}>"
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
# Union type (String | Integer | nil)
|
|
535
|
+
class UnionType < TypeNode
|
|
536
|
+
attr_accessor :types
|
|
537
|
+
|
|
538
|
+
def initialize(types: [], **opts)
|
|
539
|
+
super(**opts)
|
|
540
|
+
@types = types
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def to_rbs
|
|
544
|
+
@types.map(&:to_rbs).join(" | ")
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def to_trb
|
|
548
|
+
@types.map(&:to_trb).join(" | ")
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
# Intersection type (Readable & Writable)
|
|
553
|
+
class IntersectionType < TypeNode
|
|
554
|
+
attr_accessor :types
|
|
555
|
+
|
|
556
|
+
def initialize(types: [], **opts)
|
|
557
|
+
super(**opts)
|
|
558
|
+
@types = types
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def to_rbs
|
|
562
|
+
@types.map(&:to_rbs).join(" & ")
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def to_trb
|
|
566
|
+
@types.map(&:to_trb).join(" & ")
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
# Function/Proc type ((String, Integer) -> Boolean)
|
|
571
|
+
class FunctionType < TypeNode
|
|
572
|
+
attr_accessor :param_types, :return_type
|
|
573
|
+
|
|
574
|
+
def initialize(param_types: [], return_type:, **opts)
|
|
575
|
+
super(**opts)
|
|
576
|
+
@param_types = param_types
|
|
577
|
+
@return_type = return_type
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def to_rbs
|
|
581
|
+
params = @param_types.map(&:to_rbs).join(", ")
|
|
582
|
+
"^(#{params}) -> #{@return_type.to_rbs}"
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def to_trb
|
|
586
|
+
params = @param_types.map(&:to_trb).join(", ")
|
|
587
|
+
"(#{params}) -> #{@return_type.to_trb}"
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# Tuple type ([String, Integer, Boolean])
|
|
592
|
+
class TupleType < TypeNode
|
|
593
|
+
attr_accessor :element_types
|
|
594
|
+
|
|
595
|
+
def initialize(element_types: [], **opts)
|
|
596
|
+
super(**opts)
|
|
597
|
+
@element_types = element_types
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def to_rbs
|
|
601
|
+
"[#{@element_types.map(&:to_rbs).join(', ')}]"
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def to_trb
|
|
605
|
+
"[#{@element_types.map(&:to_trb).join(', ')}]"
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
# Nullable type (String?)
|
|
610
|
+
class NullableType < TypeNode
|
|
611
|
+
attr_accessor :inner_type
|
|
612
|
+
|
|
613
|
+
def initialize(inner_type:, **opts)
|
|
614
|
+
super(**opts)
|
|
615
|
+
@inner_type = inner_type
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def to_rbs
|
|
619
|
+
"#{@inner_type.to_rbs}?"
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def to_trb
|
|
623
|
+
"#{@inner_type.to_trb}?"
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# Literal type (literal value as type)
|
|
628
|
+
class LiteralType < TypeNode
|
|
629
|
+
attr_accessor :value
|
|
630
|
+
|
|
631
|
+
def initialize(value:, **opts)
|
|
632
|
+
super(**opts)
|
|
633
|
+
@value = value
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def to_rbs
|
|
637
|
+
@value.inspect
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
def to_trb
|
|
641
|
+
@value.inspect
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
#==========================================================================
|
|
646
|
+
# Visitor Pattern
|
|
647
|
+
#==========================================================================
|
|
648
|
+
|
|
649
|
+
class Visitor
|
|
650
|
+
def visit(node)
|
|
651
|
+
method_name = "visit_#{node.class.name.split('::').last.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '')}"
|
|
652
|
+
if respond_to?(method_name)
|
|
653
|
+
send(method_name, node)
|
|
654
|
+
else
|
|
655
|
+
visit_default(node)
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def visit_default(node)
|
|
660
|
+
node.children.each { |child| visit(child) }
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def visit_children(node)
|
|
664
|
+
node.children.each { |child| visit(child) }
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
#==========================================================================
|
|
669
|
+
# IR Builder - Converts parsed AST to IR
|
|
670
|
+
#==========================================================================
|
|
671
|
+
|
|
672
|
+
class Builder
|
|
673
|
+
def initialize
|
|
674
|
+
@type_registry = {}
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
# Build IR from parser output
|
|
678
|
+
def build(parse_result, source: nil)
|
|
679
|
+
declarations = []
|
|
680
|
+
|
|
681
|
+
# Build type aliases
|
|
682
|
+
(parse_result[:type_aliases] || []).each do |alias_info|
|
|
683
|
+
declarations << build_type_alias(alias_info)
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# Build interfaces
|
|
687
|
+
(parse_result[:interfaces] || []).each do |interface_info|
|
|
688
|
+
declarations << build_interface(interface_info)
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
# Build functions/methods
|
|
692
|
+
(parse_result[:functions] || []).each do |func_info|
|
|
693
|
+
declarations << build_method(func_info)
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
Program.new(declarations: declarations, source_file: source)
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
# Build from source code
|
|
700
|
+
def build_from_source(source)
|
|
701
|
+
parser = Parser.new(source)
|
|
702
|
+
result = parser.parse
|
|
703
|
+
build(result, source: source)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
private
|
|
707
|
+
|
|
708
|
+
def build_type_alias(info)
|
|
709
|
+
TypeAlias.new(
|
|
710
|
+
name: info[:name],
|
|
711
|
+
definition: parse_type(info[:definition])
|
|
712
|
+
)
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
def build_interface(info)
|
|
716
|
+
members = (info[:members] || []).map do |member|
|
|
717
|
+
InterfaceMember.new(
|
|
718
|
+
name: member[:name],
|
|
719
|
+
type_signature: parse_type(member[:type])
|
|
720
|
+
)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
Interface.new(
|
|
724
|
+
name: info[:name],
|
|
725
|
+
members: members
|
|
726
|
+
)
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def build_method(info)
|
|
730
|
+
params = (info[:params] || []).map do |param|
|
|
731
|
+
Parameter.new(
|
|
732
|
+
name: param[:name],
|
|
733
|
+
type_annotation: param[:type] ? parse_type(param[:type]) : nil
|
|
734
|
+
)
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
MethodDef.new(
|
|
738
|
+
name: info[:name],
|
|
739
|
+
params: params,
|
|
740
|
+
return_type: info[:return_type] ? parse_type(info[:return_type]) : nil
|
|
741
|
+
)
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def parse_type(type_str)
|
|
745
|
+
return nil unless type_str
|
|
746
|
+
|
|
747
|
+
type_str = type_str.strip
|
|
748
|
+
|
|
749
|
+
# Union type
|
|
750
|
+
if type_str.include?("|")
|
|
751
|
+
types = type_str.split("|").map { |t| parse_type(t.strip) }
|
|
752
|
+
return UnionType.new(types: types)
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# Intersection type
|
|
756
|
+
if type_str.include?("&")
|
|
757
|
+
types = type_str.split("&").map { |t| parse_type(t.strip) }
|
|
758
|
+
return IntersectionType.new(types: types)
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
# Nullable type
|
|
762
|
+
if type_str.end_with?("?")
|
|
763
|
+
inner = parse_type(type_str[0..-2])
|
|
764
|
+
return NullableType.new(inner_type: inner)
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# Generic type
|
|
768
|
+
if type_str.include?("<") && type_str.include?(">")
|
|
769
|
+
match = type_str.match(/^(\w+)<(.+)>$/)
|
|
770
|
+
if match
|
|
771
|
+
base = match[1]
|
|
772
|
+
args = parse_generic_args(match[2])
|
|
773
|
+
return GenericType.new(base: base, type_args: args)
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
# Function type
|
|
778
|
+
if type_str.include?("->")
|
|
779
|
+
match = type_str.match(/^\((.*)?\)\s*->\s*(.+)$/)
|
|
780
|
+
if match
|
|
781
|
+
param_types = match[1] ? match[1].split(",").map { |t| parse_type(t.strip) } : []
|
|
782
|
+
return_type = parse_type(match[2])
|
|
783
|
+
return FunctionType.new(param_types: param_types, return_type: return_type)
|
|
784
|
+
end
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
# Simple type
|
|
788
|
+
SimpleType.new(name: type_str)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def parse_generic_args(args_str)
|
|
792
|
+
args = []
|
|
793
|
+
current = ""
|
|
794
|
+
depth = 0
|
|
795
|
+
|
|
796
|
+
args_str.each_char do |char|
|
|
797
|
+
case char
|
|
798
|
+
when "<"
|
|
799
|
+
depth += 1
|
|
800
|
+
current += char
|
|
801
|
+
when ">"
|
|
802
|
+
depth -= 1
|
|
803
|
+
current += char
|
|
804
|
+
when ","
|
|
805
|
+
if depth == 0
|
|
806
|
+
args << parse_type(current.strip)
|
|
807
|
+
current = ""
|
|
808
|
+
else
|
|
809
|
+
current += char
|
|
810
|
+
end
|
|
811
|
+
else
|
|
812
|
+
current += char
|
|
813
|
+
end
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
args << parse_type(current.strip) unless current.empty?
|
|
817
|
+
args
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
#==========================================================================
|
|
822
|
+
# Code Generator - Converts IR to Ruby code
|
|
823
|
+
#==========================================================================
|
|
824
|
+
|
|
825
|
+
class CodeGenerator < Visitor
|
|
826
|
+
attr_reader :output
|
|
827
|
+
|
|
828
|
+
def initialize
|
|
829
|
+
@output = []
|
|
830
|
+
@indent = 0
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
def generate(program)
|
|
834
|
+
@output = []
|
|
835
|
+
visit(program)
|
|
836
|
+
@output.join("\n")
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
def visit_program(node)
|
|
840
|
+
node.declarations.each do |decl|
|
|
841
|
+
visit(decl)
|
|
842
|
+
@output << ""
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
def visit_type_alias(node)
|
|
847
|
+
# Type aliases are erased in Ruby output
|
|
848
|
+
emit_comment("type #{node.name} = #{node.definition.to_trb}")
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
def visit_interface(node)
|
|
852
|
+
# Interfaces are erased in Ruby output
|
|
853
|
+
emit_comment("interface #{node.name}")
|
|
854
|
+
node.members.each do |member|
|
|
855
|
+
emit_comment(" #{member.name}: #{member.type_signature.to_trb}")
|
|
856
|
+
end
|
|
857
|
+
emit_comment("end")
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
def visit_method_def(node)
|
|
861
|
+
params_str = node.params.map(&:name).join(", ")
|
|
862
|
+
emit("def #{node.name}(#{params_str})")
|
|
863
|
+
@indent += 1
|
|
864
|
+
|
|
865
|
+
if node.body
|
|
866
|
+
visit(node.body)
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
@indent -= 1
|
|
870
|
+
emit("end")
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def visit_block(node)
|
|
874
|
+
node.statements.each { |stmt| visit(stmt) }
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def visit_assignment(node)
|
|
878
|
+
emit("#{node.target} = #{generate_expression(node.value)}")
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def visit_return(node)
|
|
882
|
+
if node.value
|
|
883
|
+
emit("return #{generate_expression(node.value)}")
|
|
884
|
+
else
|
|
885
|
+
emit("return")
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def visit_conditional(node)
|
|
890
|
+
keyword = node.kind == :unless ? "unless" : "if"
|
|
891
|
+
emit("#{keyword} #{generate_expression(node.condition)}")
|
|
892
|
+
@indent += 1
|
|
893
|
+
visit(node.then_branch) if node.then_branch
|
|
894
|
+
@indent -= 1
|
|
895
|
+
|
|
896
|
+
if node.else_branch
|
|
897
|
+
emit("else")
|
|
898
|
+
@indent += 1
|
|
899
|
+
visit(node.else_branch)
|
|
900
|
+
@indent -= 1
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
emit("end")
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
def visit_raw_code(node)
|
|
907
|
+
node.code.each_line do |line|
|
|
908
|
+
emit(line.rstrip)
|
|
909
|
+
end
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
private
|
|
913
|
+
|
|
914
|
+
def emit(text)
|
|
915
|
+
@output << (" " * @indent + text)
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
def emit_comment(text)
|
|
919
|
+
emit("# #{text}")
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def generate_expression(node)
|
|
923
|
+
case node
|
|
924
|
+
when Literal
|
|
925
|
+
node.value.inspect
|
|
926
|
+
when VariableRef
|
|
927
|
+
node.name
|
|
928
|
+
when MethodCall
|
|
929
|
+
args = node.arguments.map { |a| generate_expression(a) }.join(", ")
|
|
930
|
+
if node.receiver
|
|
931
|
+
"#{generate_expression(node.receiver)}.#{node.method_name}(#{args})"
|
|
932
|
+
else
|
|
933
|
+
"#{node.method_name}(#{args})"
|
|
934
|
+
end
|
|
935
|
+
when BinaryOp
|
|
936
|
+
"(#{generate_expression(node.left)} #{node.operator} #{generate_expression(node.right)})"
|
|
937
|
+
when UnaryOp
|
|
938
|
+
"#{node.operator}#{generate_expression(node.operand)}"
|
|
939
|
+
else
|
|
940
|
+
node.to_s
|
|
941
|
+
end
|
|
942
|
+
end
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
#==========================================================================
|
|
946
|
+
# RBS Generator - Converts IR to RBS type definitions
|
|
947
|
+
#==========================================================================
|
|
948
|
+
|
|
949
|
+
class RBSGenerator < Visitor
|
|
950
|
+
attr_reader :output
|
|
951
|
+
|
|
952
|
+
def initialize
|
|
953
|
+
@output = []
|
|
954
|
+
@indent = 0
|
|
955
|
+
end
|
|
956
|
+
|
|
957
|
+
def generate(program)
|
|
958
|
+
@output = []
|
|
959
|
+
visit(program)
|
|
960
|
+
@output.join("\n")
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
def visit_program(node)
|
|
964
|
+
node.declarations.each do |decl|
|
|
965
|
+
visit(decl)
|
|
966
|
+
@output << ""
|
|
967
|
+
end
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
def visit_type_alias(node)
|
|
971
|
+
emit("type #{node.name} = #{node.definition.to_rbs}")
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
def visit_interface(node)
|
|
975
|
+
emit("interface _#{node.name}")
|
|
976
|
+
@indent += 1
|
|
977
|
+
|
|
978
|
+
node.members.each do |member|
|
|
979
|
+
visit(member)
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
@indent -= 1
|
|
983
|
+
emit("end")
|
|
984
|
+
end
|
|
985
|
+
|
|
986
|
+
def visit_interface_member(node)
|
|
987
|
+
emit("def #{node.name}: #{node.type_signature.to_rbs}")
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
def visit_method_def(node)
|
|
991
|
+
params = node.params.map do |param|
|
|
992
|
+
type = param.type_annotation&.to_rbs || "untyped"
|
|
993
|
+
"#{type} #{param.name}"
|
|
994
|
+
end.join(", ")
|
|
995
|
+
|
|
996
|
+
return_type = node.return_type&.to_rbs || "untyped"
|
|
997
|
+
emit("def #{node.name}: (#{params}) -> #{return_type}")
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
def visit_class_decl(node)
|
|
1001
|
+
emit("class #{node.name}")
|
|
1002
|
+
@indent += 1
|
|
1003
|
+
node.body.each { |member| visit(member) }
|
|
1004
|
+
@indent -= 1
|
|
1005
|
+
emit("end")
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
private
|
|
1009
|
+
|
|
1010
|
+
def emit(text)
|
|
1011
|
+
@output << (" " * @indent + text)
|
|
1012
|
+
end
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
#==========================================================================
|
|
1016
|
+
# Optimization Passes
|
|
1017
|
+
#==========================================================================
|
|
1018
|
+
|
|
1019
|
+
module Passes
|
|
1020
|
+
# Base class for optimization passes
|
|
1021
|
+
class Pass
|
|
1022
|
+
attr_reader :name, :changes_made
|
|
1023
|
+
|
|
1024
|
+
def initialize(name)
|
|
1025
|
+
@name = name
|
|
1026
|
+
@changes_made = 0
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
def run(program)
|
|
1030
|
+
@changes_made = 0
|
|
1031
|
+
transform(program)
|
|
1032
|
+
{ program: program, changes: @changes_made }
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
def transform(node)
|
|
1036
|
+
raise NotImplementedError
|
|
1037
|
+
end
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
# Dead code elimination
|
|
1041
|
+
class DeadCodeElimination < Pass
|
|
1042
|
+
def initialize
|
|
1043
|
+
super("dead_code_elimination")
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
def transform(node)
|
|
1047
|
+
case node
|
|
1048
|
+
when Program
|
|
1049
|
+
node.declarations = node.declarations.map { |d| transform(d) }.compact
|
|
1050
|
+
when Block
|
|
1051
|
+
node.statements = eliminate_dead_statements(node.statements)
|
|
1052
|
+
node.statements.each { |stmt| transform(stmt) }
|
|
1053
|
+
when MethodDef
|
|
1054
|
+
transform(node.body) if node.body
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
node
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
private
|
|
1061
|
+
|
|
1062
|
+
def eliminate_dead_statements(statements)
|
|
1063
|
+
result = []
|
|
1064
|
+
found_return = false
|
|
1065
|
+
|
|
1066
|
+
statements.each do |stmt|
|
|
1067
|
+
if found_return
|
|
1068
|
+
@changes_made += 1
|
|
1069
|
+
next
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
result << stmt
|
|
1073
|
+
found_return = true if stmt.is_a?(Return)
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
result
|
|
1077
|
+
end
|
|
1078
|
+
end
|
|
1079
|
+
|
|
1080
|
+
# Constant folding
|
|
1081
|
+
class ConstantFolding < Pass
|
|
1082
|
+
def initialize
|
|
1083
|
+
super("constant_folding")
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
def transform(node)
|
|
1087
|
+
case node
|
|
1088
|
+
when Program
|
|
1089
|
+
node.declarations.each { |d| transform(d) }
|
|
1090
|
+
when MethodDef
|
|
1091
|
+
transform(node.body) if node.body
|
|
1092
|
+
when Block
|
|
1093
|
+
node.statements = node.statements.map { |s| fold_constants(s) }
|
|
1094
|
+
when BinaryOp
|
|
1095
|
+
fold_binary_op(node)
|
|
1096
|
+
end
|
|
1097
|
+
|
|
1098
|
+
node
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
private
|
|
1102
|
+
|
|
1103
|
+
def fold_constants(node)
|
|
1104
|
+
case node
|
|
1105
|
+
when BinaryOp
|
|
1106
|
+
fold_binary_op(node)
|
|
1107
|
+
when Assignment
|
|
1108
|
+
node.value = fold_constants(node.value)
|
|
1109
|
+
node
|
|
1110
|
+
when Return
|
|
1111
|
+
node.value = fold_constants(node.value) if node.value
|
|
1112
|
+
node
|
|
1113
|
+
else
|
|
1114
|
+
node
|
|
1115
|
+
end
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
def fold_binary_op(node)
|
|
1119
|
+
return node unless node.is_a?(BinaryOp)
|
|
1120
|
+
|
|
1121
|
+
left = fold_constants(node.left)
|
|
1122
|
+
right = fold_constants(node.right)
|
|
1123
|
+
|
|
1124
|
+
if left.is_a?(Literal) && right.is_a?(Literal)
|
|
1125
|
+
result = evaluate_op(node.operator, left.value, right.value)
|
|
1126
|
+
if result
|
|
1127
|
+
@changes_made += 1
|
|
1128
|
+
return Literal.new(value: result, literal_type: result.class.to_s.downcase.to_sym)
|
|
1129
|
+
end
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
node.left = left
|
|
1133
|
+
node.right = right
|
|
1134
|
+
node
|
|
1135
|
+
end
|
|
1136
|
+
|
|
1137
|
+
def evaluate_op(op, left, right)
|
|
1138
|
+
return nil unless left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
1139
|
+
|
|
1140
|
+
case op
|
|
1141
|
+
when "+" then left + right
|
|
1142
|
+
when "-" then left - right
|
|
1143
|
+
when "*" then left * right
|
|
1144
|
+
when "/" then right != 0 ? left / right : nil
|
|
1145
|
+
when "%" then right != 0 ? left % right : nil
|
|
1146
|
+
when "**" then left ** right
|
|
1147
|
+
else nil
|
|
1148
|
+
end
|
|
1149
|
+
rescue
|
|
1150
|
+
nil
|
|
1151
|
+
end
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
# Type annotation cleanup
|
|
1155
|
+
class TypeAnnotationCleanup < Pass
|
|
1156
|
+
def initialize
|
|
1157
|
+
super("type_annotation_cleanup")
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
def transform(node)
|
|
1161
|
+
case node
|
|
1162
|
+
when Program
|
|
1163
|
+
node.declarations.each { |d| transform(d) }
|
|
1164
|
+
when MethodDef
|
|
1165
|
+
# Remove redundant type annotations
|
|
1166
|
+
node.params.each do |param|
|
|
1167
|
+
if param.type_annotation && redundant_annotation?(param)
|
|
1168
|
+
param.type_annotation = nil
|
|
1169
|
+
@changes_made += 1
|
|
1170
|
+
end
|
|
1171
|
+
end
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
node
|
|
1175
|
+
end
|
|
1176
|
+
|
|
1177
|
+
private
|
|
1178
|
+
|
|
1179
|
+
def redundant_annotation?(param)
|
|
1180
|
+
# Consider annotation redundant if it matches the default/inferred type
|
|
1181
|
+
false
|
|
1182
|
+
end
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
# Unused declaration removal
|
|
1186
|
+
class UnusedDeclarationRemoval < Pass
|
|
1187
|
+
def initialize
|
|
1188
|
+
super("unused_declaration_removal")
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
def transform(node)
|
|
1192
|
+
return node unless node.is_a?(Program)
|
|
1193
|
+
|
|
1194
|
+
used_types = collect_used_types(node)
|
|
1195
|
+
|
|
1196
|
+
node.declarations = node.declarations.select do |decl|
|
|
1197
|
+
case decl
|
|
1198
|
+
when TypeAlias
|
|
1199
|
+
if used_types.include?(decl.name)
|
|
1200
|
+
true
|
|
1201
|
+
else
|
|
1202
|
+
@changes_made += 1
|
|
1203
|
+
false
|
|
1204
|
+
end
|
|
1205
|
+
else
|
|
1206
|
+
true
|
|
1207
|
+
end
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1210
|
+
node
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
private
|
|
1214
|
+
|
|
1215
|
+
def collect_used_types(program)
|
|
1216
|
+
used = Set.new
|
|
1217
|
+
|
|
1218
|
+
program.declarations.each do |decl|
|
|
1219
|
+
case decl
|
|
1220
|
+
when MethodDef
|
|
1221
|
+
collect_types_from_method(decl, used)
|
|
1222
|
+
when Interface
|
|
1223
|
+
decl.members.each do |member|
|
|
1224
|
+
collect_types_from_type(member.type_signature, used)
|
|
1225
|
+
end
|
|
1226
|
+
end
|
|
1227
|
+
end
|
|
1228
|
+
|
|
1229
|
+
used
|
|
1230
|
+
end
|
|
1231
|
+
|
|
1232
|
+
def collect_types_from_method(method, used)
|
|
1233
|
+
method.params.each do |param|
|
|
1234
|
+
collect_types_from_type(param.type_annotation, used) if param.type_annotation
|
|
1235
|
+
end
|
|
1236
|
+
collect_types_from_type(method.return_type, used) if method.return_type
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1239
|
+
def collect_types_from_type(type_node, used)
|
|
1240
|
+
case type_node
|
|
1241
|
+
when SimpleType
|
|
1242
|
+
used.add(type_node.name)
|
|
1243
|
+
when GenericType
|
|
1244
|
+
used.add(type_node.base)
|
|
1245
|
+
type_node.type_args.each { |arg| collect_types_from_type(arg, used) }
|
|
1246
|
+
when UnionType, IntersectionType
|
|
1247
|
+
type_node.types.each { |t| collect_types_from_type(t, used) }
|
|
1248
|
+
when NullableType
|
|
1249
|
+
collect_types_from_type(type_node.inner_type, used)
|
|
1250
|
+
when FunctionType
|
|
1251
|
+
type_node.param_types.each { |t| collect_types_from_type(t, used) }
|
|
1252
|
+
collect_types_from_type(type_node.return_type, used)
|
|
1253
|
+
end
|
|
1254
|
+
end
|
|
1255
|
+
end
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
#==========================================================================
|
|
1259
|
+
# Optimizer - Runs optimization passes
|
|
1260
|
+
#==========================================================================
|
|
1261
|
+
|
|
1262
|
+
class Optimizer
|
|
1263
|
+
DEFAULT_PASSES = [
|
|
1264
|
+
Passes::DeadCodeElimination,
|
|
1265
|
+
Passes::ConstantFolding,
|
|
1266
|
+
Passes::TypeAnnotationCleanup,
|
|
1267
|
+
Passes::UnusedDeclarationRemoval
|
|
1268
|
+
].freeze
|
|
1269
|
+
|
|
1270
|
+
attr_reader :passes, :stats
|
|
1271
|
+
|
|
1272
|
+
def initialize(passes: DEFAULT_PASSES)
|
|
1273
|
+
@passes = passes.map(&:new)
|
|
1274
|
+
@stats = {}
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
def optimize(program, max_iterations: 10)
|
|
1278
|
+
@stats = { iterations: 0, total_changes: 0, pass_stats: {} }
|
|
1279
|
+
|
|
1280
|
+
max_iterations.times do |i|
|
|
1281
|
+
@stats[:iterations] = i + 1
|
|
1282
|
+
changes_this_iteration = 0
|
|
1283
|
+
|
|
1284
|
+
@passes.each do |pass|
|
|
1285
|
+
result = pass.run(program)
|
|
1286
|
+
program = result[:program]
|
|
1287
|
+
changes_this_iteration += result[:changes]
|
|
1288
|
+
|
|
1289
|
+
@stats[:pass_stats][pass.name] ||= 0
|
|
1290
|
+
@stats[:pass_stats][pass.name] += result[:changes]
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
@stats[:total_changes] += changes_this_iteration
|
|
1294
|
+
break if changes_this_iteration == 0
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1297
|
+
{ program: program, stats: @stats }
|
|
1298
|
+
end
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1301
|
+
end
|