t-ruby 0.0.42 → 0.0.43

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/lib/t_ruby/ast_type_inferrer.rb +2 -0
  3. data/lib/t_ruby/cache.rb +40 -10
  4. data/lib/t_ruby/cli.rb +13 -8
  5. data/lib/t_ruby/compiler.rb +168 -0
  6. data/lib/t_ruby/diagnostic.rb +115 -0
  7. data/lib/t_ruby/diagnostic_formatter.rb +162 -0
  8. data/lib/t_ruby/error_handler.rb +201 -35
  9. data/lib/t_ruby/error_reporter.rb +57 -0
  10. data/lib/t_ruby/ir.rb +39 -1
  11. data/lib/t_ruby/lsp_server.rb +40 -97
  12. data/lib/t_ruby/parser.rb +18 -4
  13. data/lib/t_ruby/parser_combinator/combinators/alternative.rb +20 -0
  14. data/lib/t_ruby/parser_combinator/combinators/chain_left.rb +34 -0
  15. data/lib/t_ruby/parser_combinator/combinators/choice.rb +29 -0
  16. data/lib/t_ruby/parser_combinator/combinators/flat_map.rb +21 -0
  17. data/lib/t_ruby/parser_combinator/combinators/label.rb +22 -0
  18. data/lib/t_ruby/parser_combinator/combinators/lookahead.rb +21 -0
  19. data/lib/t_ruby/parser_combinator/combinators/many.rb +29 -0
  20. data/lib/t_ruby/parser_combinator/combinators/many1.rb +32 -0
  21. data/lib/t_ruby/parser_combinator/combinators/map.rb +17 -0
  22. data/lib/t_ruby/parser_combinator/combinators/not_followed_by.rb +21 -0
  23. data/lib/t_ruby/parser_combinator/combinators/optional.rb +21 -0
  24. data/lib/t_ruby/parser_combinator/combinators/sep_by.rb +34 -0
  25. data/lib/t_ruby/parser_combinator/combinators/sep_by1.rb +34 -0
  26. data/lib/t_ruby/parser_combinator/combinators/sequence.rb +23 -0
  27. data/lib/t_ruby/parser_combinator/combinators/skip_right.rb +23 -0
  28. data/lib/t_ruby/parser_combinator/declaration_parser.rb +147 -0
  29. data/lib/t_ruby/parser_combinator/dsl.rb +115 -0
  30. data/lib/t_ruby/parser_combinator/parse_error.rb +48 -0
  31. data/lib/t_ruby/parser_combinator/parse_result.rb +46 -0
  32. data/lib/t_ruby/parser_combinator/parser.rb +84 -0
  33. data/lib/t_ruby/parser_combinator/primitives/end_of_input.rb +16 -0
  34. data/lib/t_ruby/parser_combinator/primitives/fail.rb +16 -0
  35. data/lib/t_ruby/parser_combinator/primitives/lazy.rb +18 -0
  36. data/lib/t_ruby/parser_combinator/primitives/literal.rb +21 -0
  37. data/lib/t_ruby/parser_combinator/primitives/pure.rb +16 -0
  38. data/lib/t_ruby/parser_combinator/primitives/regex.rb +25 -0
  39. data/lib/t_ruby/parser_combinator/primitives/satisfy.rb +21 -0
  40. data/lib/t_ruby/parser_combinator/token/expression_parser.rb +541 -0
  41. data/lib/t_ruby/parser_combinator/token/statement_parser.rb +644 -0
  42. data/lib/t_ruby/parser_combinator/token/token_alternative.rb +20 -0
  43. data/lib/t_ruby/parser_combinator/token/token_body_parser.rb +54 -0
  44. data/lib/t_ruby/parser_combinator/token/token_declaration_parser.rb +920 -0
  45. data/lib/t_ruby/parser_combinator/token/token_dsl.rb +16 -0
  46. data/lib/t_ruby/parser_combinator/token/token_label.rb +22 -0
  47. data/lib/t_ruby/parser_combinator/token/token_many.rb +29 -0
  48. data/lib/t_ruby/parser_combinator/token/token_many1.rb +32 -0
  49. data/lib/t_ruby/parser_combinator/token/token_map.rb +17 -0
  50. data/lib/t_ruby/parser_combinator/token/token_matcher.rb +29 -0
  51. data/lib/t_ruby/parser_combinator/token/token_optional.rb +21 -0
  52. data/lib/t_ruby/parser_combinator/token/token_parse_result.rb +40 -0
  53. data/lib/t_ruby/parser_combinator/token/token_parser.rb +62 -0
  54. data/lib/t_ruby/parser_combinator/token/token_sep_by.rb +34 -0
  55. data/lib/t_ruby/parser_combinator/token/token_sep_by1.rb +34 -0
  56. data/lib/t_ruby/parser_combinator/token/token_sequence.rb +23 -0
  57. data/lib/t_ruby/parser_combinator/token/token_skip_right.rb +23 -0
  58. data/lib/t_ruby/parser_combinator/type_parser.rb +103 -0
  59. data/lib/t_ruby/parser_combinator.rb +64 -936
  60. data/lib/t_ruby/scanner.rb +883 -0
  61. data/lib/t_ruby/version.rb +1 -1
  62. data/lib/t_ruby/watcher.rb +67 -75
  63. data/lib/t_ruby.rb +15 -1
  64. metadata +51 -2
  65. data/lib/t_ruby/body_parser.rb +0 -561
@@ -1,942 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Parser Combinator module for T-Ruby
4
+ # Provides both string-based and token-based parsing capabilities
5
+
3
6
  module TRuby
4
7
  module ParserCombinator
5
- # Parse result - either success or failure
6
- class ParseResult
7
- attr_reader :value, :remaining, :position, :error
8
-
9
- def initialize(success:, value: nil, remaining: "", position: 0, error: nil)
10
- @success = success
11
- @value = value
12
- @remaining = remaining
13
- @position = position
14
- @error = error
15
- end
16
-
17
- def success?
18
- @success
19
- end
20
-
21
- def failure?
22
- !@success
23
- end
24
-
25
- def self.success(value, remaining, position)
26
- new(success: true, value: value, remaining: remaining, position: position)
27
- end
28
-
29
- def self.failure(error, remaining, position)
30
- new(success: false, error: error, remaining: remaining, position: position)
31
- end
32
-
33
- def map
34
- return self if failure?
35
-
36
- ParseResult.success(yield(value), remaining, position)
37
- end
38
-
39
- def flat_map
40
- return self if failure?
41
-
42
- yield(value, remaining, position)
43
- end
44
- end
45
-
46
- # Base parser class
47
- class Parser
48
- def parse(input, position = 0)
49
- raise NotImplementedError
50
- end
51
-
52
- # Combinators as methods
53
-
54
- # Sequence: run this parser, then the other
55
- def >>(other)
56
- Sequence.new(self, other)
57
- end
58
-
59
- # Alternative: try this parser, if it fails try the other
60
- def |(other)
61
- Alternative.new(self, other)
62
- end
63
-
64
- # Map: transform the result
65
- def map(&block)
66
- Map.new(self, block)
67
- end
68
-
69
- # FlatMap: transform with another parser
70
- def flat_map(&block)
71
- FlatMap.new(self, block)
72
- end
73
-
74
- # Many: zero or more repetitions
75
- def many
76
- Many.new(self)
77
- end
78
-
79
- # Many1: one or more repetitions
80
- def many1
81
- Many1.new(self)
82
- end
83
-
84
- # Optional: zero or one
85
- def optional
86
- Optional.new(self)
87
- end
88
-
89
- # Separated by: parse items separated by delimiter
90
- def sep_by(delimiter)
91
- SepBy.new(self, delimiter)
92
- end
93
-
94
- # Separated by 1: at least one item
95
- def sep_by1(delimiter)
96
- SepBy1.new(self, delimiter)
97
- end
98
-
99
- # Between: parse between left and right delimiters
100
- def between(left, right)
101
- (left >> self << right).map { |(_, val)| val }
102
- end
103
-
104
- # Skip right: parse both, keep left result
105
- def <<(other)
106
- SkipRight.new(self, other)
107
- end
108
-
109
- # Label: add a descriptive label for error messages
110
- def label(name)
111
- Label.new(self, name)
112
- end
113
-
114
- # Lookahead: check without consuming
115
- def lookahead
116
- Lookahead.new(self)
117
- end
118
-
119
- # Not: succeed only if parser fails
120
- def not_followed_by
121
- NotFollowedBy.new(self)
122
- end
123
- end
124
-
125
- #==========================================================================
126
- # Primitive Parsers
127
- #==========================================================================
128
-
129
- # Parse a literal string
130
- class Literal < Parser
131
- def initialize(string)
132
- @string = string
133
- end
134
-
135
- def parse(input, position = 0)
136
- remaining = input[position..]
137
- if remaining&.start_with?(@string)
138
- ParseResult.success(@string, input, position + @string.length)
139
- else
140
- ParseResult.failure("Expected '#{@string}'", input, position)
141
- end
142
- end
143
- end
144
-
145
- # Parse a single character matching predicate
146
- class Satisfy < Parser
147
- def initialize(predicate, description = "character")
148
- @predicate = predicate
149
- @description = description
150
- end
151
-
152
- def parse(input, position = 0)
153
- if position < input.length && @predicate.call(input[position])
154
- ParseResult.success(input[position], input, position + 1)
155
- else
156
- ParseResult.failure("Expected #{@description}", input, position)
157
- end
158
- end
159
- end
160
-
161
- # Parse using regex
162
- class Regex < Parser
163
- def initialize(pattern, description = nil)
164
- @pattern = pattern.is_a?(Regexp) ? pattern : Regexp.new("^#{pattern}")
165
- @description = description || @pattern.inspect
166
- end
167
-
168
- def parse(input, position = 0)
169
- remaining = input[position..]
170
- match = @pattern.match(remaining)
171
-
172
- if match&.begin(0)&.zero?
173
- matched = match[0]
174
- ParseResult.success(matched, input, position + matched.length)
175
- else
176
- ParseResult.failure("Expected #{@description}", input, position)
177
- end
178
- end
179
- end
180
-
181
- # Parse end of input
182
- class EndOfInput < Parser
183
- def parse(input, position = 0)
184
- if position >= input.length
185
- ParseResult.success(nil, input, position)
186
- else
187
- ParseResult.failure("Expected end of input", input, position)
188
- end
189
- end
190
- end
191
-
192
- # Always succeed with a value
193
- class Pure < Parser
194
- def initialize(value)
195
- @value = value
196
- end
197
-
198
- def parse(input, position = 0)
199
- ParseResult.success(@value, input, position)
200
- end
201
- end
202
-
203
- # Always fail
204
- class Fail < Parser
205
- def initialize(message)
206
- @message = message
207
- end
208
-
209
- def parse(input, position = 0)
210
- ParseResult.failure(@message, input, position)
211
- end
212
- end
213
-
214
- # Lazy parser (for recursive grammars)
215
- class Lazy < Parser
216
- def initialize(&block)
217
- @block = block
218
- @parser = nil
219
- end
220
-
221
- def parse(input, position = 0)
222
- @parser ||= @block.call
223
- @parser.parse(input, position)
224
- end
225
- end
226
-
227
- #==========================================================================
228
- # Combinator Parsers
229
- #==========================================================================
230
-
231
- # Sequence two parsers
232
- class Sequence < Parser
233
- def initialize(left, right)
234
- @left = left
235
- @right = right
236
- end
237
-
238
- def parse(input, position = 0)
239
- result1 = @left.parse(input, position)
240
- return result1 if result1.failure?
241
-
242
- result2 = @right.parse(input, result1.position)
243
- return result2 if result2.failure?
244
-
245
- ParseResult.success([result1.value, result2.value], input, result2.position)
246
- end
247
- end
248
-
249
- # Alternative: try first, if fails try second
250
- class Alternative < Parser
251
- def initialize(left, right)
252
- @left = left
253
- @right = right
254
- end
255
-
256
- def parse(input, position = 0)
257
- result = @left.parse(input, position)
258
- return result if result.success?
259
-
260
- @right.parse(input, position)
261
- end
262
- end
263
-
264
- # Map result
265
- class Map < Parser
266
- def initialize(parser, func)
267
- @parser = parser
268
- @func = func
269
- end
270
-
271
- def parse(input, position = 0)
272
- @parser.parse(input, position).map(&@func)
273
- end
274
- end
275
-
276
- # FlatMap (bind)
277
- class FlatMap < Parser
278
- def initialize(parser, func)
279
- @parser = parser
280
- @func = func
281
- end
282
-
283
- def parse(input, position = 0)
284
- result = @parser.parse(input, position)
285
- return result if result.failure?
286
-
287
- next_parser = @func.call(result.value)
288
- next_parser.parse(input, result.position)
289
- end
290
- end
291
-
292
- # Many: zero or more
293
- class Many < Parser
294
- def initialize(parser)
295
- @parser = parser
296
- end
297
-
298
- def parse(input, position = 0)
299
- results = []
300
- current_pos = position
301
-
302
- loop do
303
- result = @parser.parse(input, current_pos)
304
- break if result.failure?
305
-
306
- results << result.value
307
- break if result.position == current_pos # Prevent infinite loop
308
-
309
- current_pos = result.position
310
- end
311
-
312
- ParseResult.success(results, input, current_pos)
313
- end
314
- end
315
-
316
- # Many1: one or more
317
- class Many1 < Parser
318
- def initialize(parser)
319
- @parser = parser
320
- end
321
-
322
- def parse(input, position = 0)
323
- first = @parser.parse(input, position)
324
- return first if first.failure?
325
-
326
- results = [first.value]
327
- current_pos = first.position
328
-
329
- loop do
330
- result = @parser.parse(input, current_pos)
331
- break if result.failure?
332
-
333
- results << result.value
334
- break if result.position == current_pos
335
-
336
- current_pos = result.position
337
- end
338
-
339
- ParseResult.success(results, input, current_pos)
340
- end
341
- end
342
-
343
- # Optional: zero or one
344
- class Optional < Parser
345
- def initialize(parser)
346
- @parser = parser
347
- end
348
-
349
- def parse(input, position = 0)
350
- result = @parser.parse(input, position)
351
- if result.success?
352
- result
353
- else
354
- ParseResult.success(nil, input, position)
355
- end
356
- end
357
- end
358
-
359
- # Separated by delimiter
360
- class SepBy < Parser
361
- def initialize(parser, delimiter)
362
- @parser = parser
363
- @delimiter = delimiter
364
- end
365
-
366
- def parse(input, position = 0)
367
- first = @parser.parse(input, position)
368
- return ParseResult.success([], input, position) if first.failure?
369
-
370
- results = [first.value]
371
- current_pos = first.position
372
-
373
- loop do
374
- delim_result = @delimiter.parse(input, current_pos)
375
- break if delim_result.failure?
376
-
377
- item_result = @parser.parse(input, delim_result.position)
378
- break if item_result.failure?
379
-
380
- results << item_result.value
381
- current_pos = item_result.position
382
- end
383
-
384
- ParseResult.success(results, input, current_pos)
385
- end
386
- end
387
-
388
- # Separated by 1 (at least one)
389
- class SepBy1 < Parser
390
- def initialize(parser, delimiter)
391
- @parser = parser
392
- @delimiter = delimiter
393
- end
394
-
395
- def parse(input, position = 0)
396
- first = @parser.parse(input, position)
397
- return first if first.failure?
398
-
399
- results = [first.value]
400
- current_pos = first.position
401
-
402
- loop do
403
- delim_result = @delimiter.parse(input, current_pos)
404
- break if delim_result.failure?
405
-
406
- item_result = @parser.parse(input, delim_result.position)
407
- break if item_result.failure?
408
-
409
- results << item_result.value
410
- current_pos = item_result.position
411
- end
412
-
413
- ParseResult.success(results, input, current_pos)
414
- end
415
- end
416
-
417
- # Skip right: parse both, return left
418
- class SkipRight < Parser
419
- def initialize(left, right)
420
- @left = left
421
- @right = right
422
- end
423
-
424
- def parse(input, position = 0)
425
- result1 = @left.parse(input, position)
426
- return result1 if result1.failure?
427
-
428
- result2 = @right.parse(input, result1.position)
429
- return result2 if result2.failure?
430
-
431
- ParseResult.success(result1.value, input, result2.position)
432
- end
433
- end
434
-
435
- # Label for error messages
436
- class Label < Parser
437
- def initialize(parser, name)
438
- @parser = parser
439
- @name = name
440
- end
441
-
442
- def parse(input, position = 0)
443
- result = @parser.parse(input, position)
444
- if result.failure?
445
- ParseResult.failure("Expected #{@name}", input, position)
446
- else
447
- result
448
- end
449
- end
450
- end
451
-
452
- # Lookahead: check without consuming
453
- class Lookahead < Parser
454
- def initialize(parser)
455
- @parser = parser
456
- end
457
-
458
- def parse(input, position = 0)
459
- result = @parser.parse(input, position)
460
- if result.success?
461
- ParseResult.success(result.value, input, position)
462
- else
463
- result
464
- end
465
- end
466
- end
467
-
468
- # Not followed by
469
- class NotFollowedBy < Parser
470
- def initialize(parser)
471
- @parser = parser
472
- end
473
-
474
- def parse(input, position = 0)
475
- result = @parser.parse(input, position)
476
- if result.failure?
477
- ParseResult.success(nil, input, position)
478
- else
479
- ParseResult.failure("Unexpected match", input, position)
480
- end
481
- end
482
- end
483
-
484
- # Choice: try multiple parsers in order
485
- class Choice < Parser
486
- def initialize(*parsers)
487
- @parsers = parsers
488
- end
489
-
490
- def parse(input, position = 0)
491
- best_error = nil
492
- best_position = position
493
-
494
- @parsers.each do |parser|
495
- result = parser.parse(input, position)
496
- return result if result.success?
497
-
498
- if result.position >= best_position
499
- best_error = result.error
500
- best_position = result.position
501
- end
502
- end
503
-
504
- ParseResult.failure(best_error || "No alternative matched", input, best_position)
505
- end
506
- end
507
-
508
- # Chainl: left-associative chain
509
- class ChainLeft < Parser
510
- def initialize(term, op)
511
- @term = term
512
- @op = op
513
- end
514
-
515
- def parse(input, position = 0)
516
- first = @term.parse(input, position)
517
- return first if first.failure?
518
-
519
- result = first.value
520
- current_pos = first.position
521
-
522
- loop do
523
- op_result = @op.parse(input, current_pos)
524
- break if op_result.failure?
525
-
526
- term_result = @term.parse(input, op_result.position)
527
- break if term_result.failure?
528
-
529
- result = op_result.value.call(result, term_result.value)
530
- current_pos = term_result.position
531
- end
532
-
533
- ParseResult.success(result, input, current_pos)
534
- end
535
- end
536
-
537
- #==========================================================================
538
- # DSL Module - Convenience methods
539
- #==========================================================================
540
-
541
- module DSL
542
- def literal(str)
543
- Literal.new(str)
544
- end
545
-
546
- def regex(pattern, description = nil)
547
- Regex.new(pattern, description)
548
- end
549
-
550
- def satisfy(description = "character", &predicate)
551
- Satisfy.new(predicate, description)
552
- end
553
-
554
- def char(c)
555
- Literal.new(c)
556
- end
557
-
558
- def string(str)
559
- Literal.new(str)
560
- end
561
-
562
- def eof
563
- EndOfInput.new
564
- end
565
-
566
- def pure(value)
567
- Pure.new(value)
568
- end
569
-
570
- def fail(message)
571
- Fail.new(message)
572
- end
573
-
574
- def lazy(&)
575
- Lazy.new(&)
576
- end
577
-
578
- def choice(*parsers)
579
- Choice.new(*parsers)
580
- end
581
-
582
- def sequence(*parsers)
583
- parsers.reduce { |acc, p| acc >> p }
584
- end
585
-
586
- # Common character parsers
587
- def digit
588
- satisfy("digit") { |c| c =~ /[0-9]/ }
589
- end
590
-
591
- def letter
592
- satisfy("letter") { |c| c =~ /[a-zA-Z]/ }
593
- end
594
-
595
- def alphanumeric
596
- satisfy("alphanumeric") { |c| c =~ /[a-zA-Z0-9]/ }
597
- end
598
-
599
- def whitespace
600
- satisfy("whitespace") { |c| c =~ /\s/ }
601
- end
602
-
603
- def spaces
604
- whitespace.many.map(&:join)
605
- end
606
-
607
- def spaces1
608
- whitespace.many1.map(&:join)
609
- end
610
-
611
- def newline
612
- char("\n") | string("\r\n")
613
- end
614
-
615
- def identifier
616
- (letter >> (alphanumeric | char("_")).many).map do |(first, rest)|
617
- first + rest.join
618
- end
619
- end
620
-
621
- def integer
622
- (char("-").optional >> digit.many1).map do |(sign, digits)|
623
- num = digits.join.to_i
624
- sign ? -num : num
625
- end
626
- end
627
-
628
- def float
629
- regex(/-?\d+\.\d+/, "float").map(&:to_f)
630
- end
631
-
632
- def quoted_string(quote = '"')
633
- content = satisfy("string character") { |c| c != quote && c != "\\" }
634
- escape = (char("\\") >> satisfy("escape char")).map { |(_bs, c)| c }
635
-
636
- (char(quote) >> (content | escape).many.map(&:join) << char(quote)).map { |(_, str)| str }
637
- end
638
-
639
- # Skip whitespace around parser
640
- def lexeme(parser)
641
- (spaces >> parser << spaces).map { |(_, val)| val }
642
- end
643
-
644
- # Chain for left-associative operators
645
- def chainl(term, op)
646
- ChainLeft.new(term, op)
647
- end
648
- end
649
-
650
- #==========================================================================
651
- # Type Parser - Parse T-Ruby type expressions
652
- #==========================================================================
653
-
654
- class TypeParser
655
- include DSL
656
-
657
- def initialize
658
- build_parsers
659
- end
660
-
661
- def parse(input)
662
- result = @type_expr.parse(input.strip)
663
- if result.success?
664
- { success: true, type: result.value, remaining: input[result.position..] }
665
- else
666
- { success: false, error: result.error, position: result.position }
667
- end
668
- end
669
-
670
- private
671
-
672
- def build_parsers
673
- # Identifier (type name)
674
- type_name = identifier.label("type name")
675
-
676
- # Simple type
677
- type_name.map { |name| IR::SimpleType.new(name: name) }
678
-
679
- # Lazy reference for recursive types
680
- type_expr = lazy { @type_expr }
681
-
682
- # Generic type arguments: <Type, Type, ...>
683
- generic_args = (
684
- lexeme(char("<")) >>
685
- type_expr.sep_by1(lexeme(char(","))) <<
686
- lexeme(char(">"))
687
- ).map { |(_, types)| types }
688
-
689
- # Generic type: Base<Args>
690
- generic_type = (type_name >> generic_args.optional).map do |(name, args)|
691
- if args && !args.empty?
692
- IR::GenericType.new(base: name, type_args: args)
693
- else
694
- IR::SimpleType.new(name: name)
695
- end
696
- end
697
-
698
- # Nullable type: Type?
699
- nullable_suffix = char("?")
700
-
701
- # Parenthesized type
702
- paren_type = (lexeme(char("(")) >> type_expr << lexeme(char(")"))).map { |(_, t)| t }
703
-
704
- # Function type: (Params) -> ReturnType
705
- param_list = (
706
- lexeme(char("(")) >>
707
- type_expr.sep_by(lexeme(char(","))) <<
708
- lexeme(char(")"))
709
- ).map { |(_, params)| params }
710
-
711
- arrow = lexeme(string("->"))
712
-
713
- function_type = (param_list >> arrow >> type_expr).map do |((params, _arrow), ret)|
714
- IR::FunctionType.new(param_types: params, return_type: ret)
715
- end
716
-
717
- # Tuple type: [Type, Type, ...]
718
- tuple_type = (
719
- lexeme(char("[")) >>
720
- type_expr.sep_by1(lexeme(char(","))) <<
721
- lexeme(char("]"))
722
- ).map { |(_, types)| IR::TupleType.new(element_types: types) }
723
-
724
- # Primary type (before operators)
725
- primary_type = choice(
726
- function_type,
727
- tuple_type,
728
- paren_type,
729
- generic_type
730
- )
731
-
732
- # With optional nullable suffix
733
- base_type = (primary_type >> nullable_suffix.optional).map do |(type, nullable)|
734
- nullable ? IR::NullableType.new(inner_type: type) : type
735
- end
736
-
737
- # Union type: Type | Type | ...
738
- union_op = lexeme(char("|"))
739
- union_type = base_type.sep_by1(union_op).map do |types|
740
- types.length == 1 ? types.first : IR::UnionType.new(types: types)
741
- end
742
-
743
- # Intersection type: Type & Type & ...
744
- intersection_op = lexeme(char("&"))
745
- @type_expr = union_type.sep_by1(intersection_op).map do |types|
746
- types.length == 1 ? types.first : IR::IntersectionType.new(types: types)
747
- end
748
- end
749
- end
750
-
751
- #==========================================================================
752
- # Declaration Parser - Parse T-Ruby declarations
753
- #==========================================================================
754
-
755
- class DeclarationParser
756
- include DSL
757
-
758
- def initialize
759
- @type_parser = TypeParser.new
760
- build_parsers
761
- end
762
-
763
- def parse(input)
764
- result = @declaration.parse(input.strip)
765
- if result.success?
766
- { success: true, declarations: result.value }
767
- else
768
- { success: false, error: result.error, position: result.position }
769
- end
770
- end
771
-
772
- def parse_file(input)
773
- result = @program.parse(input)
774
- if result.success?
775
- { success: true, declarations: result.value.compact }
776
- else
777
- { success: false, error: result.error, position: result.position }
778
- end
779
- end
780
-
781
- private
782
-
783
- def build_parsers
784
- # Type expression (delegate to TypeParser)
785
- lazy { parse_type_inline }
786
-
787
- # Keywords
788
- kw_type = lexeme(string("type"))
789
- kw_interface = lexeme(string("interface"))
790
- kw_def = lexeme(string("def"))
791
- kw_end = lexeme(string("end"))
792
- lexeme(string("class"))
793
- lexeme(string("module"))
794
-
795
- # Type alias: type Name = Definition
796
- type_alias = (
797
- kw_type >>
798
- lexeme(identifier) <<
799
- lexeme(char("=")) >>
800
- regex(/[^\n]+/).map(&:strip)
801
- ).map do |((_, name), definition)|
802
- type_result = @type_parser.parse(definition)
803
- if type_result[:success]
804
- IR::TypeAlias.new(name: name, definition: type_result[:type])
805
- end
806
- end
807
-
808
- # Interface member: name: Type
809
- interface_member = (
810
- lexeme(identifier) <<
811
- lexeme(char(":")) >>
812
- regex(/[^\n]+/).map(&:strip)
813
- ).map do |(name, type_str)|
814
- type_result = @type_parser.parse(type_str)
815
- if type_result[:success]
816
- IR::InterfaceMember.new(name: name, type_signature: type_result[:type])
817
- end
818
- end
819
-
820
- # Interface: interface Name ... end
821
- interface_body = (interface_member << (newline | spaces)).many
822
-
823
- interface_decl = (
824
- kw_interface >>
825
- lexeme(identifier) <<
826
- (newline | spaces) >>
827
- interface_body <<
828
- kw_end
829
- ).map do |((_, name), members)|
830
- IR::Interface.new(name: name, members: members.compact)
831
- end
832
-
833
- # Parameter: name: Type or name
834
- param = (
835
- identifier >>
836
- (lexeme(char(":")) >> regex(/[^,)]+/).map(&:strip)).optional
837
- ).map do |(name, type_str)|
838
- type_node = if type_str
839
- type_str_val = type_str.is_a?(Array) ? type_str.last : type_str
840
- result = @type_parser.parse(type_str_val)
841
- result[:success] ? result[:type] : nil
842
- end
843
- IR::Parameter.new(name: name, type_annotation: type_node)
844
- end
845
-
846
- # Parameters list
847
- params_list = (
848
- lexeme(char("(")) >>
849
- param.sep_by(lexeme(char(","))) <<
850
- lexeme(char(")"))
851
- ).map { |(_, params)| params }
852
-
853
- # Return type annotation
854
- return_type = (
855
- lexeme(char(":")) >>
856
- regex(/[^\n]+/).map(&:strip)
857
- ).map { |(_, type_str)| type_str }.optional
858
-
859
- # Method definition: def name(params): ReturnType
860
- method_def = (
861
- kw_def >>
862
- identifier >>
863
- params_list.optional >>
864
- return_type
865
- ).map do |(((_, name), params), ret_str)|
866
- ret_type = if ret_str
867
- result = @type_parser.parse(ret_str)
868
- result[:success] ? result[:type] : nil
869
- end
870
- IR::MethodDef.new(
871
- name: name,
872
- params: params || [],
873
- return_type: ret_type
874
- )
875
- end
876
-
877
- # Any declaration
878
- @declaration = choice(
879
- type_alias,
880
- interface_decl,
881
- method_def
882
- )
883
-
884
- # Line (declaration or empty)
885
- line = (@declaration << (newline | eof)) | (spaces >> newline).map { nil }
886
-
887
- # Program (multiple declarations)
888
- @program = line.many
889
- end
890
-
891
- def parse_type_inline
892
- Lazy.new { @type_parser.instance_variable_get(:@type_expr) }
893
- end
894
- end
895
-
896
- #==========================================================================
897
- # Error Reporting
898
- #==========================================================================
899
-
900
- class ParseError
901
- attr_reader :message, :position, :line, :column, :input
902
-
903
- def initialize(message:, position:, input:)
904
- @message = message
905
- @position = position
906
- @input = input
907
- @line, @column = calculate_line_column
908
- end
909
-
910
- def to_s
911
- "Parse error at line #{@line}, column #{@column}: #{@message}"
912
- end
913
-
914
- def context(lines_before: 2, lines_after: 1)
915
- input_lines = @input.split("\n")
916
- start_line = [@line - lines_before - 1, 0].max
917
- end_line = [@line + lines_after - 1, input_lines.length - 1].min
918
-
919
- result = []
920
- (start_line..end_line).each do |i|
921
- prefix = i == @line - 1 ? ">>> " : " "
922
- result << "#{prefix}#{i + 1}: #{input_lines[i]}"
923
-
924
- if i == @line - 1
925
- result << " #{" " * (@column + @line.to_s.length + 1)}^"
926
- end
927
- end
928
-
929
- result.join("\n")
930
- end
931
-
932
- private
933
-
934
- def calculate_line_column
935
- lines = @input[0...@position].split("\n", -1)
936
- line = lines.length
937
- column = lines.last&.length || 0
938
- [line, column + 1]
939
- end
940
- end
8
+ # Base classes
9
+ require_relative "parser_combinator/parse_result"
10
+ require_relative "parser_combinator/parser"
11
+
12
+ # Primitive parsers
13
+ require_relative "parser_combinator/primitives/literal"
14
+ require_relative "parser_combinator/primitives/satisfy"
15
+ require_relative "parser_combinator/primitives/regex"
16
+ require_relative "parser_combinator/primitives/end_of_input"
17
+ require_relative "parser_combinator/primitives/pure"
18
+ require_relative "parser_combinator/primitives/fail"
19
+ require_relative "parser_combinator/primitives/lazy"
20
+
21
+ # Combinator parsers
22
+ require_relative "parser_combinator/combinators/sequence"
23
+ require_relative "parser_combinator/combinators/alternative"
24
+ require_relative "parser_combinator/combinators/map"
25
+ require_relative "parser_combinator/combinators/flat_map"
26
+ require_relative "parser_combinator/combinators/many"
27
+ require_relative "parser_combinator/combinators/many1"
28
+ require_relative "parser_combinator/combinators/optional"
29
+ require_relative "parser_combinator/combinators/sep_by"
30
+ require_relative "parser_combinator/combinators/sep_by1"
31
+ require_relative "parser_combinator/combinators/skip_right"
32
+ require_relative "parser_combinator/combinators/label"
33
+ require_relative "parser_combinator/combinators/lookahead"
34
+ require_relative "parser_combinator/combinators/not_followed_by"
35
+ require_relative "parser_combinator/combinators/choice"
36
+ require_relative "parser_combinator/combinators/chain_left"
37
+
38
+ # DSL module
39
+ require_relative "parser_combinator/dsl"
40
+
41
+ # Token-based parsers
42
+ require_relative "parser_combinator/token/token_parse_result"
43
+ require_relative "parser_combinator/token/token_parser"
44
+ require_relative "parser_combinator/token/token_matcher"
45
+ require_relative "parser_combinator/token/token_sequence"
46
+ require_relative "parser_combinator/token/token_alternative"
47
+ require_relative "parser_combinator/token/token_map"
48
+ require_relative "parser_combinator/token/token_many"
49
+ require_relative "parser_combinator/token/token_many1"
50
+ require_relative "parser_combinator/token/token_optional"
51
+ require_relative "parser_combinator/token/token_sep_by"
52
+ require_relative "parser_combinator/token/token_sep_by1"
53
+ require_relative "parser_combinator/token/token_skip_right"
54
+ require_relative "parser_combinator/token/token_label"
55
+ require_relative "parser_combinator/token/token_dsl"
56
+
57
+ # High-level parsers
58
+ require_relative "parser_combinator/token/expression_parser"
59
+ require_relative "parser_combinator/token/statement_parser"
60
+ require_relative "parser_combinator/token/token_declaration_parser"
61
+ require_relative "parser_combinator/token/token_body_parser"
62
+
63
+ # Type and declaration parsers (string-based)
64
+ require_relative "parser_combinator/type_parser"
65
+ require_relative "parser_combinator/declaration_parser"
66
+
67
+ # Error reporting
68
+ require_relative "parser_combinator/parse_error"
941
69
  end
942
70
  end