syntax_tree-rbs 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,697 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ module RBS
5
+ class Format < Visitor
6
+ attr_reader :q
7
+
8
+ def initialize(q)
9
+ @q = q
10
+ end
11
+
12
+ def visit_base_type(node)
13
+ q.text(node.to_s)
14
+ end
15
+
16
+ # Visit a RBS::AST::Declarations::Alias node.
17
+ def visit_alias_declaration(node)
18
+ print_comment(node)
19
+ print_annotations(node)
20
+
21
+ q.group do
22
+ q.text("type ")
23
+ visit(node.name)
24
+ q.text(" =")
25
+ q.group do
26
+ q.indent do
27
+ q.breakable
28
+ visit(node.type)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ # Visit a RBS::AST::Members::Alias node.
35
+ def visit_alias_member(node)
36
+ print_comment(node)
37
+ print_annotations(node)
38
+
39
+ if node.kind == :singleton
40
+ q.text("alias self.")
41
+ q.text(node.new_name)
42
+ q.text(" self.")
43
+ q.text(node.old_name)
44
+ else
45
+ q.text("alias ")
46
+ q.text(node.new_name)
47
+ q.text(" ")
48
+ q.text(node.old_name)
49
+ end
50
+ end
51
+
52
+ # Visit a RBS::Types::Alias node.
53
+ def visit_alias_type(node)
54
+ visit(node.name)
55
+ end
56
+
57
+ # Visit a RBS::Types::Bases::Any node.
58
+ alias visit_any_type visit_base_type
59
+
60
+ # Visit a RBS::AST::Members::AttrAccessor node.
61
+ def visit_attr_accessor_member(node)
62
+ print_comment(node)
63
+ print_annotations(node)
64
+ print_attribute(:accessor, node)
65
+ end
66
+
67
+ # Visit a RBS::AST::Members::AttrReader node.
68
+ def visit_attr_reader_member(node)
69
+ print_comment(node)
70
+ print_annotations(node)
71
+ print_attribute(:reader, node)
72
+ end
73
+
74
+ # Visit a RBS::AST::Members::AttrWriter node.
75
+ def visit_attr_writer_member(node)
76
+ print_comment(node)
77
+ print_annotations(node)
78
+ print_attribute(:writer, node)
79
+ end
80
+
81
+ # Visit a RBS::Types::Bases::Bool node.
82
+ alias visit_bool_type visit_base_type
83
+
84
+ # Visit a RBS::Types::Bases::Bottom node.
85
+ alias visit_bottom_type visit_base_type
86
+
87
+ # Visit a RBS::AST::Declarations::Class node.
88
+ def visit_class_declaration(node)
89
+ print_comment(node)
90
+ print_annotations(node)
91
+
92
+ q.group do
93
+ q.text("class ")
94
+ print_name_and_type_params(node)
95
+
96
+ if node.super_class
97
+ q.text(" < ")
98
+ print_name_and_args(node.super_class)
99
+ end
100
+
101
+ q.indent { print_members(node) }
102
+ q.breakable(force: true)
103
+ q.text("end")
104
+ end
105
+ end
106
+
107
+ # Visit a RBS::Types::ClassInstance node.
108
+ def visit_class_instance_type(node)
109
+ print_name_and_args(node)
110
+ end
111
+
112
+ # Visit a RBS::AST::Members::ClassInstanceVariable node.
113
+ def visit_class_instance_variable_member(node)
114
+ print_comment(node)
115
+
116
+ q.group do
117
+ q.text("self.")
118
+ q.text(node.name)
119
+ q.text(": ")
120
+ visit(node.type)
121
+ end
122
+ end
123
+
124
+ # Visit a RBS::Types::ClassSingleton node.
125
+ def visit_class_singleton_type(node)
126
+ q.text("singleton(")
127
+ visit(node.name)
128
+ q.text(")")
129
+ end
130
+
131
+ # Visit a RBS::Types::Bases::Class node.
132
+ alias visit_class_type visit_base_type
133
+
134
+ # Visit a RBS::AST::Members::ClassVariable node.
135
+ def visit_class_variable_member(node)
136
+ print_comment(node)
137
+
138
+ q.group do
139
+ q.text(node.name)
140
+ q.text(": ")
141
+ visit(node.type)
142
+ end
143
+ end
144
+
145
+ # Visit a RBS::AST::Declarations::Constant node.
146
+ def visit_constant_declaration(node)
147
+ print_comment(node)
148
+
149
+ q.group do
150
+ visit(node.name)
151
+ q.text(": ")
152
+ visit(node.type)
153
+ end
154
+ end
155
+
156
+ # Visit a RBS::AST::Members::Extend node.
157
+ def visit_extend_member(node)
158
+ print_comment(node)
159
+ print_annotations(node)
160
+
161
+ q.group do
162
+ q.text("extend ")
163
+ print_name_and_args(node)
164
+ end
165
+ end
166
+
167
+ # Visit a RBS::Types::Function::Param node.
168
+ def visit_function_param_type(node)
169
+ visit(node.type)
170
+
171
+ if node.name
172
+ q.text(" ")
173
+
174
+ if ::RBS::Parser::KEYWORDS.include?(node.name.to_s)
175
+ q.text("`#{node.name}`")
176
+ else
177
+ q.text(node.name)
178
+ end
179
+ end
180
+ end
181
+
182
+ # Visit a RBS::AST::Declarations::Global node.
183
+ def visit_global_declaration(node)
184
+ print_comment(node)
185
+
186
+ q.group do
187
+ q.text(node.name)
188
+ q.text(": ")
189
+ visit(node.type)
190
+ end
191
+ end
192
+
193
+ # Visit a RBS::AST::Members::Include node.
194
+ def visit_include_member(node)
195
+ print_comment(node)
196
+ print_annotations(node)
197
+
198
+ q.group do
199
+ q.text("include ")
200
+ print_name_and_args(node)
201
+ end
202
+ end
203
+
204
+ # Visit a RBS::Types::Bases::Instance node.
205
+ alias visit_instance_type visit_base_type
206
+
207
+ # Visit a RBS::AST::Members::InstanceVariable node.
208
+ def visit_instance_variable_member(node)
209
+ print_comment(node)
210
+
211
+ q.group do
212
+ q.text(node.name)
213
+ q.text(": ")
214
+ visit(node.type)
215
+ end
216
+ end
217
+
218
+ # Visit a RBS::AST::Declarations::Interface node.
219
+ def visit_interface_declaration(node)
220
+ print_comment(node)
221
+ print_annotations(node)
222
+
223
+ q.group do
224
+ q.text("interface ")
225
+ print_name_and_type_params(node)
226
+ q.indent { print_members(node) }
227
+ q.breakable(force: true)
228
+ q.text("end")
229
+ end
230
+ end
231
+
232
+ # Visit a RBS::Types::Interface node.
233
+ def visit_interface_type(node)
234
+ print_name_and_args(node)
235
+ end
236
+
237
+ # Visit a RBS::Types::Intersection node.
238
+ def visit_intersection_type(node)
239
+ separator =
240
+ lambda do
241
+ q.breakable
242
+ q.text("& ")
243
+ end
244
+
245
+ q.text("(") if q.force_parens?
246
+ q.group do
247
+ q.force_parens do
248
+ q.seplist(node.types, separator) { |type| visit(type) }
249
+ end
250
+ end
251
+ q.text(")") if q.force_parens?
252
+ end
253
+
254
+ # Visit a RBS::Types::Literal node.
255
+ def visit_literal_type(node)
256
+ unless node.literal.is_a?(String)
257
+ q.text(node.literal.inspect)
258
+ return
259
+ end
260
+
261
+ # We're going to go straight to the source here, as if we don't then
262
+ # we're going to end up with the result of String#inspect, which does
263
+ # weird things to escape sequences.
264
+ source = q.source[node.location.range]
265
+ quote = source.include?("\\") ? source[0] : "\""
266
+ source = SyntaxTree::Quotes.normalize(source[1..-2], quote)
267
+
268
+ q.text(quote)
269
+ q.seplist(
270
+ source.split(/\r?\n/),
271
+ -> { q.breakable(force: true) }
272
+ ) { |line| q.text(line) }
273
+ q.text(quote)
274
+ end
275
+
276
+ # Visit a RBS::AST::Members::MethodDefinition node.
277
+ def visit_method_definition_member(node)
278
+ print_comment(node)
279
+ print_annotations(node)
280
+
281
+ q.group do
282
+ q.text("#{node.visibility} ") if node.visibility
283
+ q.text("def ")
284
+
285
+ if node.kind == :singleton
286
+ q.text("self.")
287
+ elsif node.kind == :singleton_instance
288
+ q.text("self?.")
289
+ end
290
+
291
+ q.text(
292
+ (
293
+ if ::RBS::Parser::KEYWORDS.include?(node.name.to_s)
294
+ "`#{node.name}`"
295
+ else
296
+ node.name
297
+ end
298
+ )
299
+ )
300
+ q.text(":")
301
+
302
+ if node.types.length == 1 && !node.overload?
303
+ q.text(" ")
304
+ print_method_signature(node.types.first)
305
+ else
306
+ separator =
307
+ lambda do
308
+ q.breakable
309
+ q.text("| ")
310
+ end
311
+
312
+ q.group do
313
+ q.indent do
314
+ q.breakable
315
+ q.seplist(node.types, separator) do |type|
316
+ print_method_signature(type)
317
+ end
318
+
319
+ if node.overload?
320
+ separator.call
321
+ q.text("...")
322
+ end
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ # Visit a RBS::AST::Declarations::Module node.
330
+ def visit_module_declaration(node)
331
+ print_comment(node)
332
+ print_annotations(node)
333
+
334
+ q.group do
335
+ q.text("module ")
336
+ print_name_and_type_params(node)
337
+
338
+ if node.self_types.any?
339
+ q.text(" : ")
340
+ q.seplist(node.self_types, -> { q.text(", ") }) do |self_type|
341
+ print_name_and_args(self_type)
342
+ end
343
+ end
344
+
345
+ q.indent { print_members(node) }
346
+ q.breakable(force: true)
347
+ q.text("end")
348
+ end
349
+ end
350
+
351
+ # Visit a RBS::Types::Bases::Nil node.
352
+ alias visit_nil_type visit_base_type
353
+
354
+ # Visit a RBS::Types::Optional node.
355
+ def visit_optional_type(node)
356
+ q.force_parens { visit(node.type) }
357
+ q.text("?")
358
+ end
359
+
360
+ # Visit a RBS::AST::Members::Prepend node.
361
+ def visit_prepend_member(node)
362
+ print_comment(node)
363
+ print_annotations(node)
364
+
365
+ q.group do
366
+ q.text("prepend ")
367
+ print_name_and_args(node)
368
+ end
369
+ end
370
+
371
+ # Visit a RBS::AST::Members::Private node.
372
+ def visit_private_member(node)
373
+ q.text("private")
374
+ end
375
+
376
+ # Visit a RBS::Types::Proc node.
377
+ def visit_proc_type(node)
378
+ q.group do
379
+ q.text("(") if q.force_parens?
380
+ q.text("^")
381
+ print_method_signature(node)
382
+ q.text(")") if q.force_parens?
383
+ end
384
+ end
385
+
386
+ # Visit a RBS::AST::Members::Public node.
387
+ def visit_public_member(node)
388
+ q.text("public")
389
+ end
390
+
391
+ # Visit a RBS::Types::Record node.
392
+ def visit_record_type(node)
393
+ separator =
394
+ lambda do
395
+ q.text(",")
396
+ q.breakable
397
+ end
398
+
399
+ q.group do
400
+ q.text("{")
401
+ q.indent do
402
+ q.breakable
403
+ q.seplist(node.fields, separator, :each_pair) do |key, type|
404
+ if key.is_a?(Symbol) && key.match?(/\A[A-Za-z_][A-Za-z_]*\z/)
405
+ q.text("#{key}: ")
406
+ else
407
+ q.text("#{key.inspect} => ")
408
+ end
409
+
410
+ visit(type)
411
+ end
412
+ end
413
+ q.breakable
414
+ q.text("}")
415
+ end
416
+ end
417
+
418
+ # Visit a SyntaxTree::RBS::Root node.
419
+ def visit_root(node)
420
+ separator =
421
+ lambda do
422
+ q.breakable(force: true)
423
+ q.breakable(force: true)
424
+ end
425
+
426
+ q.seplist(node.declarations, separator) do |declaration|
427
+ visit(declaration)
428
+ end
429
+
430
+ q.breakable(force: true)
431
+ end
432
+
433
+ # Visit a RBS::Types::Self node.
434
+ alias visit_self_type visit_base_type
435
+
436
+ # Visit a RBS::Types::Top node.
437
+ alias visit_top_type visit_base_type
438
+
439
+ # Visit a RBS::Types::Tuple node.
440
+ def visit_tuple_type(node)
441
+ # If we don't have any sub types, we explicitly need the space in
442
+ # between the brackets to not confuse the parser.
443
+ if node.types.empty?
444
+ q.text("[ ]")
445
+ return
446
+ end
447
+
448
+ q.group do
449
+ q.text("[")
450
+ q.seplist(node.types, -> { q.text(", ") }) { |type| visit(type) }
451
+ q.text("]")
452
+ end
453
+ end
454
+
455
+ # Visit a RBS::TypeName node.
456
+ def visit_type_name(node)
457
+ q.text(node.to_s)
458
+ end
459
+
460
+ # Visit a RBS::Types::Union node.
461
+ def visit_union_type(node)
462
+ separator =
463
+ lambda do
464
+ q.breakable
465
+ q.text("| ")
466
+ end
467
+
468
+ q.text("(") if q.force_parens?
469
+ q.group { q.seplist(node.types, separator) { |type| visit(type) } }
470
+ q.text(")") if q.force_parens?
471
+ end
472
+
473
+ # Visit a RBS::Types::Variable node.
474
+ def visit_variable_type(node)
475
+ q.text(node.name)
476
+ end
477
+
478
+ # Visit a RBS::Types::Bases::Void node.
479
+ alias visit_void_type visit_base_type
480
+
481
+ private
482
+
483
+ # An annotation can be attached to many kinds of nodes, and should be
484
+ # printed using %a{}.
485
+ def print_annotations(node)
486
+ annotations = node.annotations
487
+ return if annotations.empty?
488
+
489
+ q.seplist(annotations, -> { q.breakable(force: true) }) do |annotation|
490
+ if annotation.string.match?(/[{}]/)
491
+ # Bail out and just print the source string if there are any braces
492
+ # because we don't want to mess with escaping them.
493
+ q.text(q.source[annotation.location.range])
494
+ else
495
+ q.text("%a{")
496
+ q.text(annotation.string)
497
+ q.text("}")
498
+ end
499
+ end
500
+ q.breakable(force: true)
501
+ end
502
+
503
+ def print_attribute(type, node)
504
+ q.group do
505
+ q.text("#{node.visibility} ") if node.visibility
506
+ q.text("attr_#{type} ")
507
+ q.text("self.") if node.kind == :singleton
508
+ q.text(node.name)
509
+
510
+ if node.ivar_name == false
511
+ q.text("()")
512
+ elsif node.ivar_name
513
+ q.text("(")
514
+ q.text(node.ivar_name)
515
+ q.text(")")
516
+ end
517
+
518
+ q.text(": ")
519
+ visit(node.type)
520
+ end
521
+ end
522
+
523
+ # Comments come in as one whole string, so here we split it up into
524
+ # multiple lines and then prefix it with the pound sign.
525
+ def print_comment(node)
526
+ comment = node.comment
527
+ return unless comment
528
+
529
+ q.seplist(
530
+ comment.string.split(/\r?\n/),
531
+ -> { q.breakable(force: true) }
532
+ ) { |line| q.text("# #{line}") }
533
+ q.breakable(force: true)
534
+ end
535
+
536
+ # Nodes which have members will all flow their printing through this
537
+ # class, which keeps track of
538
+ def print_members(node)
539
+ last_line = nil
540
+
541
+ node.members.each do |member|
542
+ q.breakable(force: true)
543
+
544
+ if last_line && (member.location.start_line - last_line >= 2)
545
+ q.breakable(force: true)
546
+ end
547
+
548
+ visit(member)
549
+ last_line = member.location.end_line
550
+ end
551
+ end
552
+
553
+ # (T t) -> void
554
+ def print_method_signature(node)
555
+ q.group do
556
+ # We won't have a type_params key if we're printing a block
557
+ if node.respond_to?(:type_params) && node.type_params.any?
558
+ q.text("[")
559
+ q.seplist(node.type_params, -> { q.text(", ") }) do |param|
560
+ # We need to do a type check here to support RBS 1.0
561
+ q.text(param.is_a?(Symbol) ? param.to_s : param.name)
562
+ end
563
+ q.text("] ")
564
+ end
565
+
566
+ params = []
567
+
568
+ # Directly visit each of the required positional parameters.
569
+ node.type.required_positionals.each do |param|
570
+ params << -> { visit(param) }
571
+ end
572
+
573
+ # Prefix each of the optional positional parameters with a ?.
574
+ node.type.optional_positionals.each do |param|
575
+ params << -> do
576
+ q.text("?")
577
+ visit(param)
578
+ end
579
+ end
580
+
581
+ # If a rest positional is present, print it and prefix it with a *.
582
+ if node.type.rest_positionals
583
+ params << -> do
584
+ q.text("*")
585
+ visit(node.type.rest_positionals)
586
+ end
587
+ end
588
+
589
+ # Directly visit any required positional parameters that occur after
590
+ # the rest positional.
591
+ node.type.trailing_positionals.each do |param|
592
+ params << -> { visit(param) }
593
+ end
594
+
595
+ # Print all of the required keyword parameters with their name and
596
+ # parameter separated by a colon.
597
+ node.type.required_keywords.each do |name, param|
598
+ params << -> do
599
+ q.text(name)
600
+ q.text(": ")
601
+ visit(param)
602
+ end
603
+ end
604
+
605
+ # Print all of the required keyword parameters with their name and
606
+ # parameter separated by a colon, prefixed by a ?.
607
+ node.type.optional_keywords.each do |name, param|
608
+ params << -> do
609
+ q.text("?")
610
+ q.text(name)
611
+ q.text(": ")
612
+ visit(param)
613
+ end
614
+ end
615
+
616
+ # Print the rest keyword parameter if it exists by prefixing it with
617
+ # a ** operator.
618
+ if node.type.rest_keywords
619
+ params << -> do
620
+ q.text("**")
621
+ visit(node.type.rest_keywords)
622
+ end
623
+ end
624
+
625
+ if params.any?
626
+ q.text("(")
627
+ q.indent do
628
+ q.breakable("")
629
+ q.seplist(params, &:call)
630
+ end
631
+ q.breakable("")
632
+ q.text(") ")
633
+ end
634
+
635
+ if node.respond_to?(:block) && node.block
636
+ q.text("?") unless node.block.required
637
+ q.text("{")
638
+ q.indent do
639
+ q.breakable
640
+ print_method_signature(node.block)
641
+ end
642
+ q.breakable
643
+ q.text("} ")
644
+ end
645
+
646
+ q.text("-> ")
647
+ q.force_parens { visit(node.type.return_type) }
648
+ end
649
+ end
650
+
651
+ # Certain nodes are names with optional arguments attached, as in
652
+ # Array[A]. We handle all of that printing centralized here.
653
+ def print_name_and_args(node)
654
+ q.group do
655
+ visit(node.name)
656
+
657
+ if node.args.any?
658
+ q.text("[")
659
+ q.seplist(node.args, -> { q.text(", ") }) { |arg| visit(arg) }
660
+ q.text("]")
661
+ end
662
+ end
663
+ end
664
+
665
+ # Prints out the name of a class, interface, or module declaration.
666
+ # Additionally loops through each type parameter if there are any and
667
+ # print them out joined by commas. Checks for validation and variance.
668
+ def print_name_and_type_params(node)
669
+ visit(node.name)
670
+ return if node.type_params.empty?
671
+
672
+ q.text("[")
673
+ q.seplist(node.type_params, -> { q.text(", ") }) do |param|
674
+ parts = []
675
+
676
+ parts << "unchecked" if param.unchecked?
677
+
678
+ if param.variance == :covariant
679
+ parts << "out"
680
+ elsif param.variance == :contravariant
681
+ parts << "in"
682
+ end
683
+
684
+ parts << param.name
685
+ q.text(parts.join(" "))
686
+
687
+ if param.upper_bound
688
+ q.text(" < ")
689
+ visit(param.upper_bound)
690
+ end
691
+ end
692
+
693
+ q.text("]")
694
+ end
695
+ end
696
+ end
697
+ end