gloss 0.0.1 → 0.0.6

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +3 -0
  3. data/.github/workflows/crystal_specs.yml +26 -0
  4. data/.github/workflows/ruby_specs.yml +33 -0
  5. data/.gitignore +2 -1
  6. data/.rspec +1 -0
  7. data/Gemfile +0 -1
  8. data/Gemfile.lock +26 -34
  9. data/LICENSE +21 -0
  10. data/README.md +59 -7
  11. data/Rakefile +4 -0
  12. data/exe/gloss +15 -1
  13. data/ext/gloss/Makefile +27 -5
  14. data/ext/gloss/extconf.rb +3 -25
  15. data/ext/gloss/{src/lib → lib}/cr_ruby.cr +0 -0
  16. data/ext/gloss/lib/rbs_types.cr +3 -0
  17. data/ext/gloss/spec/parser_spec.cr +124 -0
  18. data/ext/gloss/spec/spec_helper.cr +2 -0
  19. data/ext/gloss/src/cr_ast.cr +129 -31
  20. data/ext/gloss/src/gloss.cr +12 -18
  21. data/ext/gloss/src/lexer.cr +59 -1
  22. data/ext/gloss/src/parser.cr +548 -254
  23. data/ext/gloss/src/rb_ast.cr +153 -27
  24. data/gloss.gemspec +1 -0
  25. data/lib/gloss.rb +4 -1
  26. data/lib/gloss/builder.rb +607 -409
  27. data/lib/gloss/cli.rb +64 -24
  28. data/lib/gloss/config.rb +16 -10
  29. data/lib/gloss/errors.rb +13 -7
  30. data/lib/gloss/initializer.rb +11 -6
  31. data/lib/gloss/logger.rb +34 -0
  32. data/lib/gloss/parser.rb +35 -0
  33. data/lib/gloss/scope.rb +8 -3
  34. data/lib/gloss/source.rb +18 -15
  35. data/lib/gloss/type_checker.rb +96 -0
  36. data/lib/gloss/version.rb +6 -1
  37. data/lib/gloss/watcher.rb +63 -19
  38. data/lib/gloss/writer.rb +18 -12
  39. data/sig/gloss.rbs +3 -0
  40. data/sig/listen.rbs +1 -0
  41. data/src/lib/gloss/builder.gl +546 -0
  42. data/src/lib/gloss/cli.gl +55 -0
  43. data/src/lib/gloss/config.gl +21 -0
  44. data/src/lib/gloss/errors.gl +11 -0
  45. data/src/lib/gloss/initializer.gl +20 -0
  46. data/src/lib/gloss/logger.gl +26 -0
  47. data/src/lib/gloss/parser.gl +31 -0
  48. data/src/lib/gloss/scope.gl +9 -0
  49. data/src/lib/gloss/source.gl +32 -0
  50. data/src/lib/gloss/type_checker.gl +101 -0
  51. data/src/lib/gloss/version.gl +3 -0
  52. data/src/lib/gloss/watcher.gl +67 -0
  53. data/src/lib/gloss/writer.gl +33 -0
  54. metadata +42 -6
  55. data/lib/gloss.bundle.dwarf +0 -0
  56. data/src/lib/hrb/initializer.gl +0 -22
  57. data/src/lib/hrb/watcher.gl +0 -32
@@ -43,13 +43,14 @@ module Rb
43
43
  end
44
44
 
45
45
  class Block < Node
46
- @info : NamedTuple(type: String, args: Array(Var), body: Node)
46
+ @info : NamedTuple(type: String, positional_args: Array(Var), body: Node, rest_p_args: Node?)
47
47
 
48
- def initialize(args, body)
48
+ def initialize(args, splat, body)
49
49
  @info = {
50
50
  type: self.class.name.split("::").last,
51
51
  body: body,
52
- args: args,
52
+ positional_args: args,
53
+ rest_p_args: splat
53
54
  }
54
55
  end
55
56
 
@@ -94,18 +95,31 @@ module Rb
94
95
  end
95
96
 
96
97
  class DefNode < Node
97
- @info : NamedTuple(type: String, name: String, body: Node, rp_args: Array(Arg), receiver: Node?,
98
- return_type: Node?, rest_kw_args: Arg?)
99
-
100
- def initialize(name : String, rp_args : Array(Arg), body : Node, receiver : Node?, return_type : Node?, rest_kw_args)
98
+ @info : NamedTuple(type: String, name: String, body: Node, positional_args: Array(Arg), receiver: Node?,
99
+ return_type: Node?, rest_kw_args: Arg?, rest_p_args: Arg?, block_arg: Node?, yield_arg_count: Int32?)
100
+
101
+ def initialize(
102
+ receiver : Node?,
103
+ name : String,
104
+ positional_args : Array(Arg),
105
+ splat,
106
+ rest_kw_args,
107
+ body : Node,
108
+ return_type : Node?,
109
+ yields : Int32?,
110
+ block_arg : Node?
111
+ )
101
112
  @info = {
102
- type: self.class.name.split("::").last,
103
- name: name,
104
- body: body,
105
- rp_args: rp_args,
106
- rest_kw_args: rest_kw_args,
107
- receiver: receiver,
108
- return_type: return_type,
113
+ type: self.class.name.split("::").last,
114
+ name: name,
115
+ body: body,
116
+ positional_args: positional_args,
117
+ rest_kw_args: rest_kw_args,
118
+ receiver: receiver,
119
+ return_type: return_type,
120
+ rest_p_args: splat,
121
+ yield_arg_count: yields,
122
+ block_arg: block_arg
109
123
  }
110
124
  end
111
125
 
@@ -113,16 +127,17 @@ module Rb
113
127
  end
114
128
 
115
129
  class Arg < Node
116
- @info : NamedTuple(type: String, name: String, external_name: String, default_value: Node?,
117
- restriction: Node?)
130
+ @info : NamedTuple(type: String, name: String, external_name: String, value: Node?,
131
+ restriction: Node?, keyword_arg: Bool)
118
132
 
119
- def initialize(name : String, external_name : String, restriction : Node?, default_value : Node?)
133
+ def initialize(name : String, external_name : String, restriction : Node?, value : Node?, keyword_arg)
120
134
  @info = {
121
135
  type: self.class.name.split("::").last,
122
136
  name: name,
123
137
  restriction: restriction,
124
- default_value: default_value,
138
+ value: value,
125
139
  external_name: external_name,
140
+ keyword_arg: keyword_arg,
126
141
  }
127
142
  end
128
143
 
@@ -198,7 +213,17 @@ module Rb
198
213
  delegate :to_json, to: @info
199
214
  end
200
215
 
201
- class RegexLiteral < NodeWithValue
216
+ class RegexLiteral < Node
217
+ @info : NamedTuple(type: String, value: Node)
218
+
219
+ def initialize(value)
220
+ @info = {
221
+ type: self.class.name.split("::").last,
222
+ value: value,
223
+ }
224
+ end
225
+
226
+ delegate :to_json, to: @info
202
227
  end
203
228
 
204
229
  class Nop < Node
@@ -236,6 +261,9 @@ module Rb
236
261
  class InstanceVar < Var
237
262
  end
238
263
 
264
+ class GlobalVar < Var
265
+ end
266
+
239
267
  abstract class Conditional < Node
240
268
  @info : NamedTuple(type: String, condition: Node, then: Node, else: Node)
241
269
 
@@ -303,16 +331,19 @@ module Rb
303
331
  end
304
332
 
305
333
  class Call < Node
306
- @info : NamedTuple(type: String, name: String, args: Array(Node), object: Node?, block: Block?, block_arg: Node?)
334
+ @info : NamedTuple(type: String, name: String, args: Array(Node), object: Node?, block: Block?, block_arg: Node?, named_args: Array(Arg)?, has_parentheses: Bool)
307
335
 
308
- def initialize(object : Node?, name : String, args : Array(Node), block, block_arg)
336
+ def initialize(object : Node?, name : String, args : Array(Node), named_args, block,
337
+ block_arg, has_parentheses)
309
338
  @info = {
310
- type: self.class.name.split("::").last,
311
- name: name,
312
- args: args,
313
- object: object,
314
- block: block,
315
- block_arg: block_arg,
339
+ type: self.class.name.split("::").last,
340
+ name: name,
341
+ args: args,
342
+ object: object,
343
+ block: block,
344
+ block_arg: block_arg,
345
+ named_args: named_args,
346
+ has_parentheses: has_parentheses || false,
316
347
  }
317
348
  end
318
349
 
@@ -383,6 +414,20 @@ module Rb
383
414
  delegate :to_json, to: @info
384
415
  end
385
416
 
417
+ class MultiAssign < Node
418
+ @info : NamedTuple(type: String, targets: Array(Node), values: Array(Node))
419
+
420
+ def initialize(targets, values)
421
+ @info = {
422
+ type: self.class.name.split("::").last,
423
+ targets: targets,
424
+ values: values,
425
+ }
426
+ end
427
+
428
+ delegate :to_json, to: @info
429
+ end
430
+
386
431
  class TypeDeclaration < Node
387
432
  @info : NamedTuple(type: String, var: Node, declared_type: Node, value: Node?, var_type: String)
388
433
 
@@ -487,5 +532,86 @@ module Rb
487
532
 
488
533
  delegate :to_json, to: @info
489
534
  end
535
+
536
+ class Union < Node
537
+ @info : NamedTuple(type: String, types: Array(Node))
538
+
539
+ def initialize(types)
540
+ @info = {
541
+ type: self.class.name.split("::").last,
542
+ types: types,
543
+ }
544
+ end
545
+
546
+ delegate :to_json, to: @info
547
+ end
548
+
549
+ class Generic < Node
550
+ @info : NamedTuple(type: String, name: Node, args: Array(Node))
551
+
552
+ def initialize(name, args)
553
+ @info = {
554
+ type: self.class.name.split("::").last,
555
+ name: name,
556
+ args: args,
557
+ }
558
+ end
559
+
560
+ delegate :to_json, to: @info
561
+ end
562
+
563
+ class Proc < Node
564
+ @info : NamedTuple(type: String, function: DefNode)
565
+
566
+ def initialize(function)
567
+ @info = {
568
+ type: self.class.name.split("::").last,
569
+ function: function,
570
+ }
571
+ end
572
+
573
+ delegate :to_json, to: @info
574
+ end
575
+
576
+ class NodeWithNameNode < Node
577
+ @info : NamedTuple(type: String, name: Node)
578
+
579
+ def initialize(name)
580
+ @info = {
581
+ type: self.class.name.split("::").last,
582
+ name: name,
583
+ }
584
+ end
585
+
586
+ delegate :to_json, to: @info
587
+ end
588
+
589
+ class Extend < NodeWithNameNode
590
+ end
591
+
592
+ class Include < NodeWithNameNode
593
+ end
594
+
595
+ class VisibilityModifier < Node
596
+ @info : NamedTuple(type: String, visibility: String, exp: Node)
597
+
598
+ def initialize(visibility : Crystal::Visibility, exp : Node)
599
+ vis = case visibility
600
+ when Crystal::Visibility::Public
601
+ "public"
602
+ when Crystal::Visibility::Protected
603
+ "protected"
604
+ when Crystal::Visibility::Private
605
+ "private"
606
+ end
607
+ @info = {
608
+ type: self.class.name.split("::").last,
609
+ visibility: vis.as(String),
610
+ exp: exp,
611
+ }
612
+ end
613
+
614
+ delegate :to_json, to: @info
615
+ end
490
616
  end
491
617
  end
data/gloss.gemspec CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_runtime_dependency "fast_blank"
19
19
  s.add_runtime_dependency "listen"
20
+ s.add_runtime_dependency "rbs"
20
21
  s.add_runtime_dependency "steep"
21
22
 
22
23
  s.add_development_dependency "rake-compiler"
data/lib/gloss.rb CHANGED
@@ -8,6 +8,8 @@ require "fast_blank"
8
8
  require "gloss/version"
9
9
  require "gloss/cli"
10
10
  require "gloss/watcher"
11
+ require "gloss/type_checker"
12
+ require "gloss/parser"
11
13
  require "gloss/initializer"
12
14
  require "gloss/config"
13
15
  require "gloss/writer"
@@ -15,8 +17,9 @@ require "gloss/source"
15
17
  require "gloss/scope"
16
18
  require "gloss/builder"
17
19
  require "gloss/errors"
20
+ require "gloss/logger"
18
21
 
19
- require "gloss.bundle"
22
+ require "gls" unless ENV["CI"] # a bit of a hack for now
20
23
 
21
24
  EMPTY_ARRAY = [].freeze
22
25
  EMPTY_HASH = {}.freeze
data/lib/gloss/builder.rb CHANGED
@@ -1,442 +1,640 @@
1
- # frozen_string_literal: true
1
+ # frozen_string_literal: true
2
+
3
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
4
+ ##### See src/ to make changes
2
5
 
3
6
  module Gloss
7
+ module Utils
8
+ module_function
9
+ def with_file_header(str)
10
+ "#{Builder::FILE_HEADER}\n\n#{str}"
11
+ end
12
+ end
4
13
  class Builder
5
- attr_reader :tree
6
-
7
- def initialize(str)
14
+ FILE_HEADER = " #{(if Config.frozen_string_literals
15
+ "# frozen_string_literal: true\n"
16
+ end)}\n ##### This file was generated by Gloss; any changes made here will be overwritten.\n ##### See #{Config.src_dir}/ to make changes"
17
+ include Utils
18
+ attr_reader(:"tree")
19
+ def initialize(tree_hash, type_checker = nil)
8
20
  @indent_level = 0
9
21
  @inside_macro = false
10
22
  @eval_vars = false
11
- tree_json = Gloss.parse_buffer str
12
- begin
13
- @tree = JSON.parse tree_json, symbolize_names: true
14
- rescue JSON::ParserError
15
- raise Errors::ParserError, tree_json
16
- end
17
23
  @current_scope = nil
18
- @steep_target = Steep::Project::Target.new(
19
- name: "gloss",
20
- options: Steep::Project::Options.new,
21
- source_patterns: ["gloss"],
22
- ignore_patterns: [],
23
- signature_patterns: []
24
- )
25
- Dir.glob("sig/**/*.rbs").each do |fp|
26
- next if !@steep_target.possible_signature_file?(fp) || @steep_target.signature_file?(fp)
27
-
28
- Steep.logger.info { "Adding signature file: #{fp}" }
29
- @steep_target.add_signature path, (Pathname(".") + fp).cleanpath.read
30
- end
31
- @top_level_decls = {}
24
+ @tree = tree_hash
25
+ @type_checker = type_checker
32
26
  end
33
-
34
- def run
27
+ def run()
35
28
  rb_output = visit_node(@tree)
36
- rb_output = "# frozen_string_literal: true\n#{rb_output}" if Config.frozen_string_literals
37
-
38
- unless check_types(rb_output)
39
- raise Errors::TypeError,
40
- @steep_target.errors.map { |e|
41
- case e
42
- when Steep::Errors::NoMethod
43
- "Unknown method :#{e.method}, location: #{e.type.location.inspect}"
44
- when Steep::Errors::MethodBodyTypeMismatch
45
- "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
46
- when Steep::Errors::IncompatibleArguments
47
- "Invalid argmuents - method type: #{e.method_type}, receiver type: #{e.receiver_type}"
48
- when Steep::Errors::ReturnTypeMismatch
49
- "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
50
- when Steep::Errors::IncompatibleAssignment
51
- "Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
52
- else
53
- e.inspect
54
- end
55
- }.join("\n")
56
- end
57
-
58
- rb_output
29
+ with_file_header(rb_output)
59
30
  end
60
-
61
- def check_types(rb_str)
62
- env_loader = RBS::EnvironmentLoader.new
63
- env = RBS::Environment.from_loader(env_loader)
64
-
65
- @top_level_decls.each do |_, decl|
66
- env << decl
67
- end
68
- env = env.resolve_type_names
69
-
70
- @steep_target.instance_variable_set("@environment", env)
71
- @steep_target.add_source("gloss", rb_str)
72
-
73
- definition_builder = RBS::DefinitionBuilder.new(env: env)
74
- factory = Steep::AST::Types::Factory.new(builder: definition_builder)
75
- check = Steep::Subtyping::Check.new(factory: factory)
76
- validator = Steep::Signature::Validator.new(checker: check)
77
- validator.validate
78
-
79
- raise Errors::TypeValidationError, validator.each_error.to_a.join("\n") unless validator.no_error?
80
-
81
- @steep_target.run_type_check(env, check, Time.now)
82
-
83
- @steep_target.status.is_a?(Steep::Project::Target::TypeCheckStatus) &&
84
- @steep_target.no_error? &&
85
- @steep_target.errors.empty?
86
- end
87
-
88
31
  def visit_node(node, scope = Scope.new)
89
32
  src = Source.new(@indent_level)
90
- case node[:type]
91
- when "ClassNode"
92
- class_name = visit_node(node[:name])
93
- superclass = if node[:superclass]
94
- @eval_vars = true
95
- visit_node(node[:superclass])
96
- @eval_vars = false
97
- else
98
- nil
99
- end
100
-
101
- src.write_ln "class #{class_name}#{" < #{superclass}" if superclass}"
102
-
103
- current_namespace = @current_scope ? @current_scope.name.to_namespace : RBS::Namespace.root
104
- class_type = RBS::AST::Declarations::Class.new(
105
- name: RBS::TypeName.new(
106
- namespace: current_namespace,
107
- name: class_name.to_sym
108
- ),
109
- type_params: RBS::AST::Declarations::ModuleTypeParams.new, # responds to #add to add params
110
- super_class: superclass ? RBS::AST::Declarations::Class::Super.new(name: RBS::Typename.new(name: super_class.to_sym, namespace: RBS::Namespace.root), args: [], location: nil) : nil,
111
- members: [],
112
- annotations: [],
113
- location: node[:location],
114
- comment: node[:comment]
115
- )
116
- old_parent_scope = @current_scope
117
- @current_scope = class_type
118
-
119
- indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
120
-
121
- src.write_ln "end"
122
-
123
- @current_scope = old_parent_scope
124
- @top_level_decls[class_type.name.name] = class_type unless @current_scope
125
- when "ModuleNode"
126
- module_name = visit_node node[:name]
127
- src.write_ln "module #{module_name}"
128
-
129
- current_namespace = RBS::Namespace.root # RBS::Namespace.new(path: [module_name.to_sym], absolute: false)
130
-
131
- module_type = RBS::AST::Declarations::Module.new(
132
- name: RBS::TypeName.new(
133
- namespace: current_namespace,
134
- name: module_name.to_sym
135
- ),
136
- type_params: RBS::AST::Declarations::ModuleTypeParams.new, # responds to #add to add params
137
- self_types: [],
138
- members: [],
139
- annotations: [],
140
- location: node[:location],
141
- comment: node[:comment]
142
- )
143
- old_parent_scope = @current_scope
144
- @current_scope = module_type
145
-
146
- indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
147
-
148
- @current_scope = old_parent_scope
149
- @top_level_decls[module_type.name.name] = module_type unless @current_scope
150
- src.write_ln "end"
151
- when "DefNode"
152
- args = render_args(node)
153
- src.write_ln "def #{node[:name]}#{args}"
154
-
155
- return_type = if node[:return_type]
156
- RBS::Types::ClassInstance.new(
157
- name: RBS::TypeName.new(
158
- name: eval(visit_node(node[:return_type])).to_s.to_sym,
159
- namespace: RBS::Namespace.root
160
- ),
161
- args: [],
162
- location: nil
163
- )
164
- else
165
- RBS::Types::Bases::Any.new(location: nil)
166
- end
167
-
168
- method_types = [
169
- RBS::MethodType.new(
170
- type_params: [],
171
- type: RBS::Types::Function.new(
172
- required_positionals: [],
173
- optional_positionals: [],
174
- rest_positionals: nil,
175
- trailing_positionals: [],
176
- required_keywords: {},
177
- optional_keywords: {},
178
- rest_keywords: node[:rest_kw_args] ?
179
- RBS::Types::Function::Param.new(
180
- name: visit_node(node[:rest_kw_args]).to_sym,
181
- type: RBS::Types::Bases::Any.new(location: nil)
182
- ) : nil,
183
- return_type: return_type
184
- ),
185
- block: nil,
186
- location: nil
187
- )
188
- ]
189
- method_definition = RBS::AST::Members::MethodDefinition.new(
190
- name: node[:name].to_sym,
191
- kind: :instance,
192
- types: method_types,
193
- annotations: [],
194
- location: node[:location],
195
- comment: node[:comment],
196
- overload: false
197
- )
198
-
199
- if @current_scope
200
- @current_scope.members << method_definition
201
- else
202
- @type_env << method_definition # should be new class declaration for Object with method_definition as private method
203
- end
204
-
205
- indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
206
-
207
- src.write_ln "end"
208
- when "CollectionNode"
209
- src.write(*node[:children].map { |a| visit_node(a, scope) })
210
- when "Call"
211
- obj = node[:object] ? "#{visit_node(node[:object], scope)}." : ""
212
- args = node[:args] || EMPTY_ARRAY
213
- args = if !args.empty? || node[:block_arg]
214
- "(#{args.map { |a| visit_node(a, scope).strip }.reject(&:blank?).join(", ")}#{"&#{visit_node(node[:block_arg]).strip}" if node[:block_arg]})"
215
- else
216
- nil
217
- end
218
- block = node[:block] ? " #{visit_node(node[:block])}" : nil
219
- src.write_ln "#{obj}#{node[:name]}#{args}#{block}"
220
-
221
- when "Block"
222
-
223
- src.write "{ |#{node[:args].map { |a| visit_node a }.join(", ")}|\n"
224
-
225
- indented(src) { src.write visit_node(node[:body]) }
226
-
227
- src.write_ln "}"
228
-
229
- when "RangeLiteral"
230
- dots = node[:exclusive] ? "..." : ".."
231
-
232
- # parentheses help the compatibility with precendence of operators in some situations
233
- # eg. (1..3).cover? 2 vs. 1..3.cover? 2
234
- src.write "(", visit_node(node[:from]), dots, visit_node(node[:to]), ")"
235
-
236
- when "LiteralNode"
237
-
238
- src.write node[:value]
239
-
240
- when "ArrayLiteral"
241
-
242
- src.write("[", *node[:elements].map { |e| visit_node e }.join(", "), "]")
243
- src.write ".freeze" if node[:frozen]
244
-
245
- when "StringInterpolation"
246
-
247
- contents = node[:contents].inject(String.new) do |str, c|
248
- str << case c[:type]
249
- when "LiteralNode"
250
- c[:value][1...-1]
251
- else
252
- "\#{#{visit_node(c).strip}}"
253
- end
254
- end
255
- src.write '"', contents, '"'
256
-
257
- when "Path"
258
-
259
- src.write node[:value]
260
-
261
- when "Require"
262
-
263
- src.write_ln %(require "#{node[:value]}")
264
-
265
- when "Assign", "OpAssign"
266
-
267
- src.write_ln "#{visit_node(node[:target])} #{node[:op]}= #{visit_node(node[:value]).strip}"
268
-
269
- when "Var"
270
-
271
- if @eval_vars
272
- src.write scope[node[:name]]
273
- else
274
- src.write node[:name]
275
- end
276
-
277
- when "InstanceVar"
278
-
279
- src.write node[:name]
280
-
281
- when "Arg"
282
-
283
- src.write node[:external_name]
284
-
285
- when "UnaryExpr"
286
-
287
- src.write "#{node[:op]}#{visit_node(node[:value]).strip}"
288
-
289
- when "BinaryOp"
290
-
291
- src.write visit_node(node[:left]).strip, " #{node[:op]} ", visit_node(node[:right]).strip
292
-
293
- when "HashLiteral"
294
-
295
- contents = node[:elements].map do |k, v|
296
- key = case k
297
- when String
298
- k.to_sym
299
- else
300
- visit_node k
301
- end
302
- value = visit_node v
303
- "#{key.inspect} => #{value}"
304
- end
305
-
306
- src.write "{#{contents.join(",\n")}}"
307
- src.write ".freeze" if node[:frozen]
308
-
309
- when "Enum"
310
- src.write_ln "module #{node[:name]}"
311
- node[:members].each_with_index do |m, i|
312
- indented(src) { src.write_ln(visit_node(m) + (!m[:value] ? " = #{i}" : "")) }
313
- end
314
- src.write_ln "end"
315
- when "If"
316
- src.write_ln "(if #{visit_node(node[:condition]).strip}"
317
-
318
- indented(src) { src.write_ln visit_node(node[:then]) }
319
-
320
- if node[:else]
321
- src.write_ln "else"
322
- indented(src) { src.write_ln visit_node(node[:else]) }
323
- end
324
-
325
- src.write_ln "end)"
326
- when "Case"
327
- src.write "case"
328
- src.write " #{visit_node(node[:condition]).strip}\n" if node[:condition]
329
- indented(src) do
330
- node[:whens].each do |w|
331
- src.write_ln visit_node(w)
33
+ case node.[](:"type")
34
+ when "ClassNode"
35
+ class_name = visit_node(node.[](:"name"))
36
+ current_namespace = (if @current_scope
37
+ @current_scope.name
38
+ .to_namespace
39
+ else
40
+ RBS::Namespace.root
41
+ end)
42
+ superclass_type = nil
43
+ superclass_output = nil
44
+ (if node.[](:"superclass")
45
+ @eval_vars = true
46
+ superclass_output = visit_node(node.[](:"superclass"))
47
+ @eval_vars = false
48
+ superclass_type = RBS::Parser.parse_type(superclass_output)
49
+ (if node.dig(:"superclass", :"type")
50
+ .==("Generic")
51
+ superclass_output = superclass_output.[](/^[^\[]+/)
52
+ end)
53
+ end)
54
+ src.write_ln("class #{class_name}#{(if superclass_output
55
+ " < #{superclass_output}"
56
+ end)}")
57
+ class_type = RBS::AST::Declarations::Class.new(name: RBS::TypeName.new(namespace: current_namespace, name: class_name.to_sym), type_params: RBS::AST::Declarations::ModuleTypeParams.new, super_class: superclass_type, members: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
58
+ old_parent_scope = @current_scope
59
+ @current_scope = class_type
60
+ indented(src) { ||
61
+ (if node.[](:"body")
62
+ src.write_ln(visit_node(node.[](:"body")))
63
+ end)
64
+ }
65
+ src.write_ln("end")
66
+ @current_scope = old_parent_scope
67
+ (if @current_scope
68
+ @current_scope.members
69
+ .<<(class_type)
70
+ end)
71
+ (if @type_checker
72
+ unless @current_scope
73
+ @type_checker.top_level_decls
74
+ .[]=(class_type.name
75
+ .name, class_type)
76
+ end
77
+ end)
78
+ when "ModuleNode"
79
+ module_name = visit_node(node.[](:"name"))
80
+ src.write_ln("module #{module_name}")
81
+ current_namespace = (if @current_scope
82
+ @current_scope.name
83
+ .to_namespace
84
+ else
85
+ RBS::Namespace.root
86
+ end)
87
+ module_type = RBS::AST::Declarations::Module.new(name: RBS::TypeName.new(namespace: current_namespace, name: module_name.to_sym), type_params: RBS::AST::Declarations::ModuleTypeParams.new, self_types: Array.new, members: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
88
+ old_parent_scope = @current_scope
89
+ @current_scope = module_type
90
+ indented(src) { ||
91
+ (if node.[](:"body")
92
+ src.write_ln(visit_node(node.[](:"body")))
93
+ end)
94
+ }
95
+ @current_scope = old_parent_scope
96
+ (if @current_scope
97
+ @current_scope.members
98
+ .<<(module_type)
99
+ end)
100
+ (if @type_checker
101
+ unless @current_scope
102
+ @type_checker.top_level_decls
103
+ .[]=(module_type.name
104
+ .name, module_type)
105
+ end
106
+ end)
107
+ src.write_ln("end")
108
+ when "DefNode"
109
+ args = render_args(node)
110
+ receiver = (if node.[](:"receiver")
111
+ visit_node(node.[](:"receiver"))
112
+ else
113
+ nil
114
+ end)
115
+ src.write_ln("def #{(if receiver
116
+ "#{receiver}."
117
+ end)}#{node.[](:"name")}#{args.[](:"representation")}")
118
+ return_type = (if node.[](:"return_type")
119
+ RBS::Types::ClassInstance.new(name: RBS::TypeName.new(name: eval(visit_node(node.[](:"return_type")))
120
+ .to_s
121
+ .to_sym, namespace: RBS::Namespace.root), args: EMPTY_ARRAY, location: node.[](:"location"))
122
+ else
123
+ RBS::Types::Bases::Any.new(location: node.[](:"location"))
124
+ end)
125
+ method_types = [RBS::MethodType.new(type_params: EMPTY_ARRAY, type: RBS::Types::Function.new(required_positionals: args.dig(:"types", :"required_positionals"), optional_positionals: args.dig(:"types", :"optional_positionals"), rest_positionals: args.dig(:"types", :"rest_positionals"), trailing_positionals: args.dig(:"types", :"trailing_positionals"), required_keywords: args.dig(:"types", :"required_keywords"), optional_keywords: args.dig(:"types", :"optional_keywords"), rest_keywords: args.dig(:"types", :"rest_keywords"), return_type: return_type), block: (if node.[](:"yield_arg_count")
126
+ RBS::Types::Block.new(type: RBS::Types::Function.new(required_positionals: Array.new, optional_positionals: Array.new, rest_positionals: nil, trailing_positionals: Array.new, required_keywords: Hash.new, optional_keywords: Hash.new, rest_keywords: nil, return_type: RBS::Types::Bases::Any.new(location: node.[](:"location"))), required: !!node.[](:"block_arg") || node.[](:"yield_arg_count"))
127
+ else
128
+ nil
129
+ end), location: node.[](:"location"))]
130
+ method_definition = RBS::AST::Members::MethodDefinition.new(name: node.[](:"name")
131
+ .to_sym, kind: (if receiver
132
+ :"class"
133
+ else
134
+ :"instance"
135
+ end), types: method_types, annotations: EMPTY_ARRAY, location: node.[](:"location"), comment: node.[](:"comment"), overload: false)
136
+ (if @current_scope
137
+ @current_scope.members
138
+ .<<(method_definition)
139
+ else
140
+ (if @type_checker
141
+ @type_checker.type_env
142
+ .<<(method_definition)
143
+ end)
144
+ end)
145
+ indented(src) { ||
146
+ (if node.[](:"body")
147
+ src.write_ln(visit_node(node.[](:"body")))
148
+ end)
149
+ }
150
+ src.write_ln("end")
151
+ when "VisibilityModifier"
152
+ src.write_ln("#{node.[](:"visibility")} #{visit_node(node.[](:"exp"))}")
153
+ when "CollectionNode"
154
+ node.[](:"children")
155
+ .each() { |a|
156
+ src.write(visit_node(a, scope))
157
+ }
158
+ when "Call"
159
+ obj = (if node.[](:"object")
160
+ "#{visit_node(node.[](:"object"), scope)}."
161
+ else
162
+ ""
163
+ end)
164
+ arg_arr = node.fetch(:"args") { ||
165
+ EMPTY_ARRAY }
166
+ (if node.[](:"named_args")
167
+ arg_arr += node.[](:"named_args")
168
+ end)
169
+ args = (if !arg_arr.empty? || node.[](:"block_arg")
170
+ "#{arg_arr.map() { |a|
171
+ visit_node(a, scope)
172
+ .strip
173
+ }
174
+ .reject(&:"blank?")
175
+ .join(", ")}#{(if node.[](:"block_arg")
176
+ "&#{visit_node(node.[](:"block_arg"))
177
+ .strip}"
178
+ end)}"
179
+ else
180
+ nil
181
+ end)
182
+ block = (if node.[](:"block")
183
+ " #{visit_node(node.[](:"block"))}"
184
+ else
185
+ nil
186
+ end)
187
+ has_parens = !!node.[](:"has_parentheses") || args || block
188
+ opening_delimiter = (if has_parens
189
+ "("
190
+ else
191
+ nil
192
+ end)
193
+ call = "#{obj}#{node.[](:"name")}#{opening_delimiter}#{args}#{(if has_parens
194
+ ")"
195
+ end)}#{block}"
196
+ src.write_ln(call)
197
+ when "Block"
198
+ args = render_args(node)
199
+ src.write("{ #{args.[](:"representation")
200
+ .gsub(/(\A\(|\)\z)/, "|")}\n")
201
+ indented(src) { ||
202
+ src.write(visit_node(node.[](:"body")))
203
+ }
204
+ src.write_ln("}")
205
+ when "RangeLiteral"
206
+ dots = (if node.[](:"exclusive")
207
+ "..."
208
+ else
209
+ ".."
210
+ end)
211
+ src.write("(", "(", visit_node(node.[](:"from")), ")", dots, "(", visit_node(node.[](:"to")), ")", ")")
212
+ when "LiteralNode"
213
+ src.write(node.[](:"value"))
214
+ when "ArrayLiteral"
215
+ src.write("[", node.[](:"elements")
216
+ .map() { |e|
217
+ visit_node(e)
218
+ .strip
219
+ }
220
+ .join(", "), "]")
221
+ (if node.[](:"frozen")
222
+ src.write(".freeze")
223
+ end)
224
+ when "StringInterpolation"
225
+ contents = node.[](:"contents")
226
+ .inject(String.new) { |str, c|
227
+ str.<<(case c.[](:"type")
228
+ when "LiteralNode"
229
+ c.[](:"value")
230
+ .[](((1)...(-1)))
231
+ else
232
+ ["\#{", visit_node(c)
233
+ .strip, "}"].join
234
+ end)
235
+ }
236
+ src.write("\"", contents, "\"")
237
+ when "Path"
238
+ src.write(node.[](:"value"))
239
+ when "Require"
240
+ src.write_ln("require \"#{node.[](:"value")}\"")
241
+ when "Assign", "OpAssign"
242
+ src.write_ln("#{visit_node(node.[](:"target"))} #{node.[](:"op")}= #{visit_node(node.[](:"value"))
243
+ .strip}")
244
+ when "MultiAssign"
245
+ src.write_ln("#{node.[](:"targets")
246
+ .map() { |t|
247
+ visit_node(t)
248
+ .strip
249
+ }
250
+ .join(", ")} = #{node.[](:"values")
251
+ .map() { |v|
252
+ visit_node(v)
253
+ .strip
254
+ }
255
+ .join(", ")}")
256
+ when "Var"
257
+ (if @eval_vars
258
+ src.write(scope.[](node.[](:"name")))
259
+ else
260
+ src.write(node.[](:"name"))
261
+ end)
262
+ when "InstanceVar"
263
+ src.write(node.[](:"name"))
264
+ when "GlobalVar"
265
+ src.write(node.[](:"name"))
266
+ when "Arg"
267
+ val = node.[](:"external_name")
268
+ (if node.[](:"keyword_arg")
269
+ val += ":"
270
+ (if node.[](:"value")
271
+ val += " #{visit_node(node.[](:"value"))}"
272
+ end)
273
+ else
274
+ (if node.[](:"value")
275
+ val += " = #{visit_node(node.[](:"value"))}"
276
+ end)
277
+ end)
278
+ src.write(val)
279
+ when "UnaryExpr"
280
+ src.write("#{node.[](:"op")}#{visit_node(node.[](:"value"))
281
+ .strip}")
282
+ when "BinaryOp"
283
+ src.write(visit_node(node.[](:"left"))
284
+ .strip, " #{node.[](:"op")} ", visit_node(node.[](:"right"))
285
+ .strip)
286
+ when "HashLiteral"
287
+ contents = node.[](:"elements")
288
+ .map() { |k, v|
289
+ key = case k
290
+ when String
291
+ k.to_sym
292
+ .inspect
293
+ else
294
+ visit_node(k)
295
+ end
296
+ value = visit_node(v)
297
+ "#{key} => #{value}" }
298
+ src.write("{#{contents.join(",\n")}}")
299
+ (if node.[](:"frozen")
300
+ src.write(".freeze")
301
+ end)
302
+ when "Enum"
303
+ src.write_ln("module #{node.[](:"name")}")
304
+ node.[](:"members")
305
+ .each_with_index() { |m, i|
306
+ indented(src) { ||
307
+ src.write_ln(visit_node(m)
308
+ .+((if !m.[](:"value")
309
+ " = #{i}"
310
+ else
311
+ ""
312
+ end)))
313
+ }
314
+ }
315
+ src.write_ln("end")
316
+ when "If"
317
+ src.write_ln("(if #{visit_node(node.[](:"condition"))
318
+ .strip}")
319
+ indented(src) { ||
320
+ src.write_ln(visit_node(node.[](:"then")))
321
+ }
322
+ (if node.[](:"else")
323
+ src.write_ln("else")
324
+ indented(src) { ||
325
+ src.write_ln(visit_node(node.[](:"else")))
326
+ }
327
+ end)
328
+ src.write_ln("end)")
329
+ when "Unless"
330
+ src.write_ln("unless #{visit_node(node.[](:"condition"))}")
331
+ indented(src) { ||
332
+ src.write_ln(visit_node(node.[](:"then")))
333
+ }
334
+ (if node.[](:"else")
335
+ src.write_ln("else")
336
+ indented(src) { ||
337
+ src.write_ln(visit_node(node.[](:"else")))
338
+ }
339
+ end)
340
+ src.write_ln("end")
341
+ when "Case"
342
+ src.write("case")
343
+ (if node.[](:"condition")
344
+ src.write(" #{visit_node(node.[](:"condition"))
345
+ .strip}\n")
346
+ end)
347
+ indented(src) { ||
348
+ node.[](:"whens")
349
+ .each() { |w|
350
+ src.write_ln(visit_node(w))
351
+ }
352
+ (if node.[](:"else")
353
+ src.write_ln("else")
354
+ indented(src) { ||
355
+ src.write_ln(visit_node(node.[](:"else")))
356
+ }
357
+ end)
358
+ }
359
+ src.write_ln("end")
360
+ when "When"
361
+ src.write_ln("when #{node.[](:"conditions")
362
+ .map() { |n|
363
+ visit_node(n)
364
+ }
365
+ .join(", ")}")
366
+ indented(src) { ||
367
+ src.write_ln((if node.[](:"body")
368
+ visit_node(node.[](:"body"))
369
+ else
370
+ "# no op"
371
+ end))
372
+ }
373
+ when "MacroFor"
374
+ vars, expr, body = node.[](:"vars"), node.[](:"expr"), node.[](:"body")
375
+ var_names = vars.map() { |v|
376
+ visit_node(v)
377
+ }
378
+ @inside_macro = true
379
+ indent_level = @indent_level
380
+ unless indent_level.zero?
381
+ @indent_level -= 1
332
382
  end
333
- end
334
- src.write_ln "end"
335
- when "When"
336
- src.write_ln "when #{node[:conditions].map { |n| visit_node(n) }.join(", ")}"
337
-
338
- indented(src) { src.write_ln visit_node(node[:body]) }
339
- when "MacroFor"
340
- vars, expr, body = node[:vars], node[:expr], node[:body]
341
- var_names = vars.map { |v| visit_node v }
342
- @inside_macro = true
343
- indent_level = @indent_level
344
- @indent_level -= 1 unless indent_level.zero?
345
- expanded = eval(visit_node(expr)).map do |*a|
346
- locals = Hash[[var_names.join(%(", "))].zip(a)]
347
- locals.merge!(scope) if @inside_macro
348
- visit_node(body, locals)
349
- end.flatten
350
- @indent_level += 1 unless indent_level.zero?
351
- src.write(*expanded)
352
- @inside_macro = false
353
- when "MacroLiteral"
354
- src.write node[:value]
355
- when "MacroExpression"
356
- if node[:output]
357
- expr = visit_node node[:expr], scope
358
- val = scope[expr]
359
- src.write val
360
- end
361
- when "MacroIf"
362
- if evaluate_macro_condition(node[:condition], scope)
363
- src.write_ln visit_node(node[:then], scope) if node[:then]
383
+ # @type var expanded: Array[String]
384
+ expanded = eval(visit_node(expr))
385
+ .map() { |*a|
386
+ locals = [var_names.join("\", \"")].zip(a)
387
+ .to_h
388
+ (if @inside_macro
389
+ locals.merge!(scope)
390
+ end)
391
+ visit_node(body, locals)
392
+ }
393
+ .flatten
394
+ unless indent_level.zero?
395
+ @indent_level += 1
396
+ end
397
+ expanded.each() { |e|
398
+ src.write(e)
399
+ }
400
+ @inside_macro = false
401
+ when "MacroLiteral"
402
+ src.write(node.[](:"value"))
403
+ when "MacroExpression"
404
+ (if node.[](:"output")
405
+ expr = visit_node(node.[](:"expr"), scope)
406
+ val = scope.[](expr)
407
+ src.write(val)
408
+ end)
409
+ when "MacroIf"
410
+ (if evaluate_macro_condition(node.[](:"condition"), scope)
411
+ (if node.[](:"then")
412
+ src.write_ln(visit_node(node.[](:"then"), scope))
413
+ end)
414
+ else
415
+ (if node.[](:"else")
416
+ src.write_ln(visit_node(node.[](:"else"), scope))
417
+ end)
418
+ end)
419
+ when "Return"
420
+ val = (if node.[](:"value")
421
+ " #{visit_node(node.[](:"value"))
422
+ .strip}"
423
+ else
424
+ nil
425
+ end)
426
+ src.write("return#{val}")
427
+ when "TypeDeclaration"
428
+ src.write_ln("# @type var #{visit_node(node.[](:"var"))}: #{visit_node(node.[](:"declared_type"))}")
429
+ src.write_ln("#{visit_node(node.[](:"var"))} = #{visit_node(node.[](:"value"))}")
430
+ when "ExceptionHandler"
431
+ src.write_ln("begin")
432
+ indented(src) { ||
433
+ src.write_ln(visit_node(node.[](:"body")))
434
+ }
435
+ (if node.[](:"rescues")
436
+ node.[](:"rescues")
437
+ .each() { |r|
438
+ src.write_ln("rescue #{(if r.[](:"types")
439
+ r.[](:"types")
440
+ .map() { |n|
441
+ visit_node(n)
442
+ }
443
+ .join(", ")
444
+ end)}#{(if r.[](:"name")
445
+ " => #{r.[](:"name")}"
446
+ end)}")
447
+ (if r.[](:"body")
448
+ indented(src) { ||
449
+ src.write_ln(visit_node(r.[](:"body")))
450
+ }
451
+ end)
452
+ }
453
+ end)
454
+ (if node.[](:"else")
455
+ src.write_ln("else")
456
+ indented(src) { ||
457
+ src.write_ln(visit_node(node.[](:"else")))
458
+ }
459
+ end)
460
+ (if node.[](:"ensure")
461
+ src.write_ln("ensure")
462
+ indented(src) { ||
463
+ src.write_ln(visit_node(node.[](:"ensure")))
464
+ }
465
+ end)
466
+ src.write_ln("end")
467
+ when "Generic"
468
+ src.write("#{visit_node(node.[](:"name"))}[#{node.[](:"args")
469
+ .map() { |a|
470
+ visit_node(a)
471
+ }
472
+ .join(", ")}]")
473
+ when "Proc"
474
+ fn = node.[](:"function")
475
+ src.write("->#{render_args(fn)} { #{visit_node(fn.[](:"body"))} }")
476
+ when "Include"
477
+ current_namespace = (if @current_scope
478
+ @current_scope.name
479
+ .to_namespace
480
+ else
481
+ RBS::Namespace.root
482
+ end)
483
+ name = visit_node(node.[](:"name"))
484
+ src.write_ln("include #{name}")
485
+ type = RBS::AST::Members::Include.new(name: method(:"TypeName")
486
+ .call(name), args: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
487
+ (if @current_scope
488
+ @current_scope.members
489
+ .<<(type)
490
+ else
491
+ @type_checker.type_env
492
+ .<<(type)
493
+ end)
494
+ when "Extend"
495
+ current_namespace = (if @current_scope
496
+ @current_scope.name
497
+ .to_namespace
498
+ else
499
+ RBS::Namespace.root
500
+ end)
501
+ name = visit_node(node.[](:"name"))
502
+ src.write_ln("extend #{name}")
503
+ type = RBS::AST::Members::Extend.new(name: method(:"TypeName")
504
+ .call(name), args: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
505
+ (if @current_scope
506
+ @current_scope.members
507
+ .<<(type)
508
+ else
509
+ @type_checker.type_env
510
+ .<<(type)
511
+ end)
512
+ when "RegexLiteral"
513
+ contents = visit_node(node.[](:"value"))
514
+ src.write(Regexp.new(contents.undump)
515
+ .inspect)
516
+ when "Union"
517
+ types = node.[](:"types")
518
+ output = (if types.length
519
+ .==(2) && types.[](1)
520
+ .[](:"type")
521
+ .==("Path") && types.[](1)
522
+ .[]("value")
523
+ .==(nil)
524
+ "#{visit_node(types.[](0))}?"
525
+ else
526
+ types.map() { |t|
527
+ visit_node(t)
528
+ }
529
+ .join(" | ")
530
+ end)
531
+ src.write(output)
532
+ when "Next"
533
+ (if node.[](:"value")
534
+ val = " #{node.[](:"value")}"
535
+ end)
536
+ src.write("next#{val}")
537
+ when "EmptyNode"
538
+ # no op
364
539
  else
365
- src.write_ln visit_node(node[:else], scope) if node[:else]
366
- end
367
- when "Return"
368
- val = node[:value] ? " #{visit_node(node[:value]).strip}" : nil
369
- src.write "return#{val}"
370
- when "TypeDeclaration"
371
- src.write_ln "# @type var #{visit_node(node[:var])}: #{visit_node(node[:declared_type])}"
372
- src.write_ln "#{visit_node(node[:var])} = #{visit_node(node[:value])}"
373
- when "ExceptionHandler"
374
- src.write_ln "begin"
375
- src.write_ln visit_node(node[:body])
376
- node[:rescues]&.each do |r|
377
- src.write_ln "rescue #{r[:types].map { |n| visit_node n }.join(", ") if r[:types]}#{" => #{r[:name]}" if r[:name]}"
378
- src.write_ln visit_node(r[:body]) if r[:body]
379
- end
380
- if node[:else]
381
- src.write_ln "else"
382
- src.write_ln visit_node(node[:else])
383
- end
384
- if node[:ensure]
385
- src.write_ln "ensure"
386
- src.write_ln visit_node(node[:ensure])
387
- end
388
- src.write_ln "end"
389
- when "EmptyNode"
390
- # pass
391
- else
392
- raise "Not implemented: #{node[:type]}"
540
+ raise("Not implemented: #{node.[](:"type")}")
393
541
  end
394
-
395
- src
542
+ src
396
543
  end
397
-
398
- private
399
-
400
- def evaluate_macro_condition(condition_node, scope)
544
+ private def evaluate_macro_condition(condition_node, scope)
401
545
  @eval_vars = true
402
546
  eval(visit_node(condition_node, scope))
403
547
  @eval_vars = false
404
548
  end
405
-
406
- def indented(src)
549
+ private def indented(src)
407
550
  increment_indent(src)
408
551
  yield
409
552
  decrement_indent(src)
410
553
  end
411
-
412
- def increment_indent(src)
554
+ private def increment_indent(src)
413
555
  @indent_level += 1
414
556
  src.increment_indent
415
557
  end
416
-
417
- def decrement_indent(src)
558
+ private def decrement_indent(src)
418
559
  @indent_level -= 1
419
560
  src.decrement_indent
420
561
  end
421
-
422
- def render_args(node)
423
- rp = node[:rp_args] || EMPTY_ARRAY
424
- op = node[:op_args] || EMPTY_ARRAY
425
- rkw = node[:req_kw_args] || EMPTY_HASH
426
- okw = node[:opt_kw_args] || EMPTY_HASH
427
- rest_p = node[:rest_p_args]
428
- rest_kw = node[:rest_kw_args]
429
- return nil unless [rp, op, rkw, okw, rest_p, rest_kw].any? { |a| !a.nil? || !a.empty? }
430
-
431
- contents = [
432
- rp.map { |a| visit_node(a) },
433
- op.map { |pos| "#{pos.name} = #{value}" },
434
- rkw.map { |name, _| "#{name}:" },
435
- okw.map { |name, _| "#{name}: #{value}" },
436
- rest_p ? "*#{rest_p}" : "",
437
- rest_kw ? "**#{visit_node(rest_kw)}" : ""
438
- ].reject(&:empty?).flatten.join(", ")
439
- "(#{contents})"
562
+ private def render_args(node)
563
+ # @type var rp: Array[Hash[Symbol, Any]]
564
+ rp = node.fetch(:"positional_args") { ||
565
+ EMPTY_ARRAY }
566
+ .filter() { |a|
567
+ !a.[](:"value") }
568
+ # @type var op: Array[Hash[Symbol, Any]]
569
+ op = node.fetch(:"positional_args") { ||
570
+ EMPTY_ARRAY }
571
+ .filter() { |a|
572
+ a.[](:"value")
573
+ }
574
+ # @type var rkw: Hash[Symbol, Any]
575
+ rkw = node.fetch(:"req_kw_args") { ||
576
+ EMPTY_HASH }
577
+ # @type var okw: Hash[Symbol, Any]
578
+ okw = node.fetch(:"opt_kw_args") { ||
579
+ EMPTY_HASH }
580
+ # @type var rest_p: String?
581
+ rest_p = (if node.[](:"rest_p_args")
582
+ visit_node(node.[](:"rest_p_args"))
583
+ else
584
+ nil
585
+ end)
586
+ # @type var rest_kw: Hash[Symbol, Any]?
587
+ rest_kw = node.[](:"rest_kw_args")
588
+ (if [rp, op, rkw, okw, rest_p, rest_kw].all?() { |a|
589
+ a && a.empty? }
590
+ return nil
591
+ end)
592
+ contents = [rp.map() { |a|
593
+ visit_node(a)
594
+ }, op.map() { |a|
595
+ "#{a.[](:"name")} = #{visit_node(a.[](:"value"))
596
+ .strip}" }, rkw.map() { |name, _|
597
+ "#{name}:" }, okw.map() { |name, value|
598
+ "#{name}: #{value}" }, (if rest_p
599
+ "*#{rest_p}"
600
+ else
601
+ ""
602
+ end), (if rest_kw
603
+ "**#{visit_node(rest_kw)}"
604
+ else
605
+ ""
606
+ end)].reject(&:"empty?")
607
+ .flatten
608
+ .join(", ")
609
+ representation = "(#{contents})"
610
+ rp.map!() { |a|
611
+ RBS::Types::Function::Param.new(name: visit_node(a)
612
+ .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
613
+ }
614
+ op.map!() { |a|
615
+ RBS::Types::Function::Param.new(name: visit_node(a)
616
+ .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
617
+ }
618
+ rest_p = (if rpa = node.[](:"rest_p_args")
619
+ RBS::Types::Function::Param.new(name: visit_node(rpa)
620
+ .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
621
+ else
622
+ nil
623
+ end)
624
+ {:representation => representation,
625
+ :types => {:required_positionals => rp,
626
+ :optional_positionals => op,
627
+ :rest_positionals => rest_p,
628
+ :trailing_positionals => EMPTY_ARRAY,
629
+ :required_keywords => node.[](:"req_kw_args") || EMPTY_HASH,
630
+ :optional_keywords => node.[](:"opt_kw_args") || EMPTY_HASH,
631
+ :rest_keywords => (if node.[](:"rest_kw_args")
632
+ RBS::Types::Function::Param.new(name: visit_node(node.[](:"rest_kw_args"))
633
+ .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
634
+ else
635
+ nil
636
+ end)
637
+ }.freeze}.freeze
440
638
  end
441
639
  end
442
640
  end