docrb-parser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +75 -0
- data/Rakefile +12 -0
- data/docrb-parser.gemspec +38 -0
- data/lib/docrb/core_extensions.rb +60 -0
- data/lib/docrb/parser/attribute.rb +25 -0
- data/lib/docrb/parser/call.rb +27 -0
- data/lib/docrb/parser/class.rb +94 -0
- data/lib/docrb/parser/comment.rb +40 -0
- data/lib/docrb/parser/comment_parser.rb +290 -0
- data/lib/docrb/parser/computations.rb +471 -0
- data/lib/docrb/parser/constant.rb +19 -0
- data/lib/docrb/parser/container.rb +305 -0
- data/lib/docrb/parser/deferred_singleton_class.rb +17 -0
- data/lib/docrb/parser/location.rb +43 -0
- data/lib/docrb/parser/method.rb +62 -0
- data/lib/docrb/parser/method_parameters.rb +85 -0
- data/lib/docrb/parser/module.rb +50 -0
- data/lib/docrb/parser/node_array.rb +24 -0
- data/lib/docrb/parser/reference.rb +25 -0
- data/lib/docrb/parser/reloader.rb +19 -0
- data/lib/docrb/parser/resolved_reference.rb +26 -0
- data/lib/docrb/parser/version.rb +7 -0
- data/lib/docrb/parser/virtual_container.rb +21 -0
- data/lib/docrb/parser/virtual_location.rb +9 -0
- data/lib/docrb/parser/virtual_method.rb +19 -0
- data/lib/docrb/parser.rb +139 -0
- data/lib/docrb-parser.rb +3 -0
- data/sig/docrb/core_extensions.rbs +24 -0
- data/sig/docrb/parser/attribute.rbs +18 -0
- data/sig/docrb/parser/call.rbs +17 -0
- data/sig/docrb/parser/class.rbs +34 -0
- data/sig/docrb/parser/comment.rbs +14 -0
- data/sig/docrb/parser/comment_parser.rbs +79 -0
- data/sig/docrb/parser/constant.rbs +15 -0
- data/sig/docrb/parser/container.rbs +91 -0
- data/sig/docrb/parser/deferred_singleton_class.rbs +12 -0
- data/sig/docrb/parser/location.rbs +24 -0
- data/sig/docrb/parser/method.rbs +34 -0
- data/sig/docrb/parser/method_parameters.rbs +34 -0
- data/sig/docrb/parser/module.rbs +14 -0
- data/sig/docrb/parser/node_array.rbs +12 -0
- data/sig/docrb/parser/reference.rbs +19 -0
- data/sig/docrb/parser/reloader.rbs +7 -0
- data/sig/docrb/parser/resolved_reference.rbs +22 -0
- data/sig/docrb/parser/virtual_method.rbs +17 -0
- data/sig/docrb/parser.rbs +5 -0
- metadata +109 -0
@@ -0,0 +1,305 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class Container
|
6
|
+
visible_attr_accessor :classes, :modules, :defined_by, :instance_methods,
|
7
|
+
:class_methods, :extends, :includes, :name, :path_segments,
|
8
|
+
:instance_attributes, :class_attributes, :current_visibility_modifier,
|
9
|
+
:constants, :location
|
10
|
+
attr_accessor :parent, :parser, :doc
|
11
|
+
|
12
|
+
SINGLETON_CLASS_TYPES = %i[
|
13
|
+
singleton_class_node
|
14
|
+
deferred_singleton_class_node
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
def initialize(parser, parent, node)
|
18
|
+
@object_id = parser.make_id(self)
|
19
|
+
@node = node
|
20
|
+
@parser = parser
|
21
|
+
@explicit_instance_visibility = {}
|
22
|
+
@explicit_class_visibility = {}
|
23
|
+
@constants = NodeArray.new
|
24
|
+
@classes = NodeArray.new
|
25
|
+
@modules = NodeArray.new
|
26
|
+
@includes = NodeArray.new
|
27
|
+
@extends = NodeArray.new
|
28
|
+
@defined_by = []
|
29
|
+
@instance_methods = NodeArray.new
|
30
|
+
@class_methods = NodeArray.new
|
31
|
+
@class_attributes = NodeArray.new
|
32
|
+
@instance_attributes = NodeArray.new
|
33
|
+
@inside_module_function = false
|
34
|
+
@location = parser.location(node.location)
|
35
|
+
@parent = parent
|
36
|
+
|
37
|
+
@name = if SINGLETON_CLASS_TYPES.include? node&.type
|
38
|
+
nil
|
39
|
+
elsif node
|
40
|
+
name = parser.unfurl_constant_path(node.constant_path)
|
41
|
+
@path_segments = name
|
42
|
+
name.pop
|
43
|
+
end
|
44
|
+
|
45
|
+
node&.body&.body&.each { handle_node(_1) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def full_path(relative_to: nil)
|
49
|
+
path = [self] + (parent&.full_path || [])
|
50
|
+
return path unless relative_to
|
51
|
+
|
52
|
+
rel_path = relative_to.full_path
|
53
|
+
while rel_path.last == path.last
|
54
|
+
rel_path.pop
|
55
|
+
path.pop
|
56
|
+
end
|
57
|
+
|
58
|
+
path
|
59
|
+
end
|
60
|
+
|
61
|
+
def extract_references(from, attr)
|
62
|
+
NodeArray.new(from
|
63
|
+
.filter(&:fulfilled?)
|
64
|
+
.map { _1.resolved.id }
|
65
|
+
.map { parser.object_by_id(_1) }
|
66
|
+
.map { _1.send(attr) }
|
67
|
+
.flatten)
|
68
|
+
end
|
69
|
+
|
70
|
+
def unowned_classes = extract_references(@includes, :all_classes)
|
71
|
+
|
72
|
+
def all_classes = NodeArray.new(@classes).tap { _1.merge_unowned(*unowned_classes) }
|
73
|
+
|
74
|
+
def unowned_modules = extract_references(@includes, :all_modules)
|
75
|
+
|
76
|
+
def all_modules = NodeArray.new(@modules).tap { _1.merge_unowned(*unowned_modules) }
|
77
|
+
|
78
|
+
def unowned_instance_methods = extract_references(@includes, :all_instance_methods)
|
79
|
+
|
80
|
+
def all_instance_methods = NodeArray.new(@instance_methods).tap { _1.merge_unowned(*unowned_instance_methods) }
|
81
|
+
|
82
|
+
def unowned_class_methods = extract_references(@extends, :all_instance_methods)
|
83
|
+
|
84
|
+
def all_class_methods = NodeArray.new(@class_methods).tap { _1.merge_unowned(*unowned_class_methods) }
|
85
|
+
|
86
|
+
def unowned_class_attributes = extract_references(@extends, :all_instance_attributes)
|
87
|
+
|
88
|
+
def all_class_attributes = NodeArray.new(@class_attributes).tap { _1.merge_unowned(*unowned_class_attributes) }
|
89
|
+
|
90
|
+
def unowned_instance_attributes = extract_references(@includes, :all_instance_attributes)
|
91
|
+
|
92
|
+
def all_instance_attributes
|
93
|
+
NodeArray.new(@instance_attributes)
|
94
|
+
.tap { _1.merge_unowned(*unowned_instance_attributes) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def all_objects
|
98
|
+
NodeArray.new(
|
99
|
+
all_classes + all_modules + all_instance_methods + all_classes +
|
100
|
+
all_class_attributes + all_instance_attributes
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
def id = @object_id
|
105
|
+
|
106
|
+
def source_of(obj)
|
107
|
+
parent = obj.parent.id
|
108
|
+
case
|
109
|
+
when parent == id
|
110
|
+
:self
|
111
|
+
when @includes.filter(&:fulfilled?).any? { _1.resolved.id == id }
|
112
|
+
:included
|
113
|
+
when @extends.filter(&:fulfilled?).any? { _1.resolved.id == id }
|
114
|
+
:extended
|
115
|
+
else
|
116
|
+
:unknown
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def handle_parsed_node(_parser, _node) = nil
|
121
|
+
|
122
|
+
def instance_method_added(_parser, _node, _method) = nil
|
123
|
+
|
124
|
+
def class_method_added(_parser, _node, _method) = nil
|
125
|
+
|
126
|
+
def class_added(_parser, _node, _method) = nil
|
127
|
+
|
128
|
+
def module_added(_parser, _node, _method) = nil
|
129
|
+
|
130
|
+
def handle_call(call)
|
131
|
+
case call.name
|
132
|
+
when :extend
|
133
|
+
extends.append(*call.arguments.map { reference(_1) })
|
134
|
+
true
|
135
|
+
when :include
|
136
|
+
includes.append(*call.arguments.map { reference(_1) })
|
137
|
+
true
|
138
|
+
when :attr, :attr_reader
|
139
|
+
add_attribute(parser, call, :reader)
|
140
|
+
true
|
141
|
+
when :attr_accessor
|
142
|
+
add_attribute(parser, call, :accessor)
|
143
|
+
true
|
144
|
+
when :attr_writer
|
145
|
+
add_attribute(parser, call, :writer)
|
146
|
+
true
|
147
|
+
when :private, :public, :protected
|
148
|
+
handle_visibility_modifier(parser, call)
|
149
|
+
true
|
150
|
+
when :private_class_method, :public_class_method
|
151
|
+
handle_singleton_visibility_modifier(parser, call)
|
152
|
+
true
|
153
|
+
else
|
154
|
+
false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def handle_node(node)
|
159
|
+
case v = parse_node(node)
|
160
|
+
when nil
|
161
|
+
# Pass
|
162
|
+
when Method
|
163
|
+
if v.instance?
|
164
|
+
@instance_methods << v
|
165
|
+
instance_method_added(parser, node, v)
|
166
|
+
else
|
167
|
+
@class_methods << v
|
168
|
+
class_method_added(parser, node, v)
|
169
|
+
end
|
170
|
+
when Class
|
171
|
+
if v.singleton? && kind == :class
|
172
|
+
merge_singleton_class(v)
|
173
|
+
else
|
174
|
+
@classes << v
|
175
|
+
class_added(parser, node, v)
|
176
|
+
end
|
177
|
+
when Module
|
178
|
+
@modules << v
|
179
|
+
module_added(parser, node, v)
|
180
|
+
when Call
|
181
|
+
handle_parsed_node(parser, v) unless handle_call(v)
|
182
|
+
when Constant
|
183
|
+
@constants << v
|
184
|
+
else
|
185
|
+
handle_parsed_node(parser, v)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def parse_node(node) = parser.handle_node(node, self)
|
190
|
+
|
191
|
+
def reference(path) = parser.reference(self, path)
|
192
|
+
|
193
|
+
def add_attribute(parser, node, type)
|
194
|
+
node.arguments.each do |arg|
|
195
|
+
next unless arg.try(:type) == :symbol_node # TODO: Support extra types?
|
196
|
+
|
197
|
+
@instance_attributes << Attribute.new(parser, self, node, arg.value.to_sym, type)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def adjust_split_attributes!(scope)
|
202
|
+
attrs = instance_variable_get("@#{scope}_attributes")
|
203
|
+
methods = instance_variable_get("@#{scope}_methods")
|
204
|
+
exp = instance_variable_get("@explicit_#{scope}_visibility")
|
205
|
+
|
206
|
+
loop do
|
207
|
+
changed = false
|
208
|
+
attrs.each do |attr|
|
209
|
+
setter = "#{attr.name}=".to_sym
|
210
|
+
|
211
|
+
# Do we have a writer defined explicitly?
|
212
|
+
if (method = methods.named(setter).first)
|
213
|
+
changed = true
|
214
|
+
methods.delete(method)
|
215
|
+
attr.writer_visibility = exp.fetch(setter, method.visibility)
|
216
|
+
|
217
|
+
# Promote attribute to accessor in case it was only a reader.
|
218
|
+
attr.type = :accessor if attr.type == :reader
|
219
|
+
end
|
220
|
+
|
221
|
+
next unless (method = methods.named(attr.name).first)
|
222
|
+
|
223
|
+
changed = true
|
224
|
+
methods.delete(method)
|
225
|
+
attr.reader_visibility = exp.fetch(setter, method.visibility)
|
226
|
+
|
227
|
+
# Promote attribute to accessor in case it was only a writer.
|
228
|
+
attr.type = :accessor if attr.type == :writer
|
229
|
+
end
|
230
|
+
|
231
|
+
# Finally, move all methods ending in `=` that are left to write-only
|
232
|
+
# attributes, and re-run this loop if required.
|
233
|
+
methods.each do |met|
|
234
|
+
name = met.name.to_s
|
235
|
+
next unless name.end_with? "="
|
236
|
+
|
237
|
+
changed = true
|
238
|
+
attr_name = name.gsub("=", "").to_sym
|
239
|
+
attrs << Attribute.new(@parser, self, met.node, attr_name, :writer).tap do |attr|
|
240
|
+
attr.writer_visibility = met.visibility
|
241
|
+
end
|
242
|
+
methods.delete(met)
|
243
|
+
end
|
244
|
+
|
245
|
+
break unless changed
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def handle_visibility_modifier(_parser, node)
|
250
|
+
@inside_module_function = false
|
251
|
+
old_visibility = @current_visibility_modifier
|
252
|
+
@current_visibility_modifier = node.name
|
253
|
+
node.arguments&.each do |arg|
|
254
|
+
case arg.type
|
255
|
+
when :symbol_node
|
256
|
+
@explicit_instance_visibility[arg.value.to_sym] = @current_visibility_modifier
|
257
|
+
if (method = @instance_methods.named(arg.value.to_sym).first)
|
258
|
+
method.visibility = @current_visibility_modifier
|
259
|
+
next
|
260
|
+
end
|
261
|
+
|
262
|
+
if (attr = @instance_attributes.named(arg.value.gsub("=", "").to_sym).first)
|
263
|
+
if arg.value.end_with? "="
|
264
|
+
attr.writer_visibility = @current_visibility_modifier
|
265
|
+
else
|
266
|
+
attr.reader_visibility = @current_visibility_modifier
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
when :def_node, :call_node
|
271
|
+
@explicit_instance_visibility[arg.name.to_sym] = @current_visibility_modifier
|
272
|
+
known_methods = @instance_methods.map(&:name)
|
273
|
+
handle_node(arg)
|
274
|
+
(@instance_methods.map(&:name) - known_methods).each do |n|
|
275
|
+
@explicit_instance_visibility[n] = @current_visibility_modifier
|
276
|
+
end
|
277
|
+
else
|
278
|
+
parser.unhandled_node! node
|
279
|
+
end
|
280
|
+
end
|
281
|
+
ensure
|
282
|
+
@current_visibility_modifier = old_visibility unless node.arguments && node.arguments.empty?
|
283
|
+
end
|
284
|
+
|
285
|
+
def handle_singleton_visibility_modifier(_parser, node)
|
286
|
+
visibility = node.name.to_s.split("_", 2).first.to_sym
|
287
|
+
node.arguments.each do |arg|
|
288
|
+
if arg.value == "new"
|
289
|
+
@default_constructor_visibility = visibility
|
290
|
+
next
|
291
|
+
end
|
292
|
+
@class_methods.named(arg.value.to_sym).first&.visibility = visibility
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def update_constructor_visibility!
|
297
|
+
return if @default_constructor_visibility == :public
|
298
|
+
|
299
|
+
# Create a new "new" singleton
|
300
|
+
handle_node(VirtualMethod.new(:new, :self_node))
|
301
|
+
@class_methods.named(:new).first!.visibility = @default_constructor_visibility
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class DeferredSingletonClass < Class
|
6
|
+
visible_attr_reader :target
|
7
|
+
|
8
|
+
def kind = :deferred_singleton_class
|
9
|
+
|
10
|
+
def initialize(parser, parent, node)
|
11
|
+
super
|
12
|
+
@target = parser.unfurl_constant_path(node.expression)
|
13
|
+
singleton!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class Location
|
6
|
+
visible_attr_reader :line_start, :line_end, :offset_start, :offset_end
|
7
|
+
attr_reader :parser, :file_path, :ast, :source
|
8
|
+
|
9
|
+
def initialize(parser, ast, loc)
|
10
|
+
@parser = parser
|
11
|
+
@ast = ast
|
12
|
+
if loc.try(:virtual?)
|
13
|
+
@virtual = true
|
14
|
+
else
|
15
|
+
@line_start = loc.start_line
|
16
|
+
@line_end = loc.end_line
|
17
|
+
@offset_start = loc.start_offset
|
18
|
+
@offset_end = loc.end_offset
|
19
|
+
@file_path = parser.current_file
|
20
|
+
end
|
21
|
+
|
22
|
+
@source = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def virtual? = @virtual || false
|
26
|
+
|
27
|
+
def load_source!
|
28
|
+
return if @virtual
|
29
|
+
|
30
|
+
@source = @ast.source.source[@offset_start...@offset_end]
|
31
|
+
.then do |src|
|
32
|
+
lines = src.lines
|
33
|
+
next src unless lines.length > 1
|
34
|
+
|
35
|
+
match = /^(\s+)/.match(lines.last)
|
36
|
+
next src unless match
|
37
|
+
|
38
|
+
src.gsub(/^\s{#{match[1].length}}/, "")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class Method
|
6
|
+
visible_attr_accessor :name, :parameters, :visibility, :overriding,
|
7
|
+
:overridden_by, :type, :external_receiver, :location
|
8
|
+
attr_accessor :parent, :node, :doc
|
9
|
+
|
10
|
+
def kind = :method
|
11
|
+
|
12
|
+
def initialize(parser, parent, node)
|
13
|
+
@object_id = parser.make_id(self)
|
14
|
+
@parser = parser
|
15
|
+
@parent = parent
|
16
|
+
@node = node
|
17
|
+
@name = node.name
|
18
|
+
@visibility = parent.try(:current_visibility_modifier) || :public
|
19
|
+
@location = parser.location(node.location) unless node.is_a? VirtualMethod
|
20
|
+
@parameters = MethodParameters.new(parser, self, node.parameters)
|
21
|
+
@overridden_by = []
|
22
|
+
case node.receiver&.type
|
23
|
+
when nil
|
24
|
+
@type = :instance
|
25
|
+
when :self_node
|
26
|
+
@type = :class
|
27
|
+
else
|
28
|
+
@type = :class
|
29
|
+
@external_receiver = parser.reference(self, parser.unfurl_constant_path(node.receiver))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def id = @object_id
|
34
|
+
|
35
|
+
def class? = @type == :class
|
36
|
+
|
37
|
+
def instance? = @type == :instance
|
38
|
+
|
39
|
+
def module_function? = @visibility == :module_function
|
40
|
+
|
41
|
+
def external? = !@external_receiver.nil?
|
42
|
+
|
43
|
+
def override_target = (@parser.object_by_id(@overriding) if @overriding)
|
44
|
+
def overriders = @overridden_by.map { @parser.object_by_id(_1) }.map(&:parent).compact
|
45
|
+
|
46
|
+
def full_path(relative_to: nil)
|
47
|
+
path = (parent&.full_path || []).reverse + [self]
|
48
|
+
return path unless relative_to
|
49
|
+
|
50
|
+
full_rel = relative_to.full_path.reverse
|
51
|
+
while full_rel.first == path.first
|
52
|
+
full_rel.shift
|
53
|
+
path.shift
|
54
|
+
end
|
55
|
+
|
56
|
+
path
|
57
|
+
end
|
58
|
+
|
59
|
+
docrb_inspect { "name=#{name}" }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class MethodParameters < Array
|
6
|
+
def initialize(parser, parent, node)
|
7
|
+
@object_id = parser.make_id(self)
|
8
|
+
super()
|
9
|
+
@parent = parent
|
10
|
+
@node = node
|
11
|
+
node&.compact_child_nodes&.sort_by { _1.location.start_offset }&.each do |n|
|
12
|
+
kind = case n
|
13
|
+
when Prism::RequiredParameterNode then :arg
|
14
|
+
when Prism::OptionalParameterNode then :optarg
|
15
|
+
when Prism::RestParameterNode then :rest
|
16
|
+
when Prism::KeywordParameterNode then n.value ? :optkw : :kw
|
17
|
+
when Prism::KeywordRestParameterNode then :kwrest
|
18
|
+
when Prism::BlockParameterNode then :block
|
19
|
+
else raise NotImplementedError, "Unsupported parameter kind #{n.class}"
|
20
|
+
end
|
21
|
+
append Parameter.new(parser, kind, n.name, n.try(:value))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def id = @object_id
|
26
|
+
|
27
|
+
docrb_inspect { to_a }
|
28
|
+
|
29
|
+
class Parameter
|
30
|
+
visible_attr_reader :kind, :name, :value, :value_type
|
31
|
+
attr_reader :parser
|
32
|
+
|
33
|
+
def initialize(parser, kind, name, value = nil)
|
34
|
+
@object_id = parser.make_id(self)
|
35
|
+
@kind = kind
|
36
|
+
@name = name || name_by_type
|
37
|
+
@value_type = type_for_value(value)
|
38
|
+
@value = value.then! { parser.location(_1.location) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def id = @object_id
|
42
|
+
|
43
|
+
def has_value? = !value.nil?
|
44
|
+
|
45
|
+
def optional? = kind == :optarg || kind == :optkw
|
46
|
+
|
47
|
+
def positional? = kind == :optarg || kind == :arg || kind == :rest
|
48
|
+
|
49
|
+
def keyword? = kind == :kw || kind == :optkw || kind == :kwrest
|
50
|
+
|
51
|
+
def block? = kind == :block
|
52
|
+
|
53
|
+
def rest? = kind == :kwrest || kind == :rest
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def name_by_type
|
58
|
+
case kind
|
59
|
+
when :rest then :*
|
60
|
+
when :kwrest then :**
|
61
|
+
when :block then :&
|
62
|
+
else
|
63
|
+
raise "Invalid call to name_by_type for a non-anonymous parameter"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def type_for_value(value)
|
68
|
+
return unless value
|
69
|
+
|
70
|
+
case value
|
71
|
+
when Prism::SymbolNode then :symbol
|
72
|
+
when Prism::NilNode then :nil
|
73
|
+
when Prism::FalseNode, Prism::TrueNode then :bool
|
74
|
+
when Prism::CallNode then :call
|
75
|
+
when Prism::StringNode then :string
|
76
|
+
when Prism::IntegerNode, Prism::FloatNode then :number
|
77
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode then :const
|
78
|
+
else
|
79
|
+
puts "Unhandled parameter value type #{value.class.name}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class Module < Container
|
6
|
+
def kind = :module
|
7
|
+
|
8
|
+
def initialize(parser, parent, node)
|
9
|
+
super
|
10
|
+
adjust_split_attributes! :class
|
11
|
+
adjust_split_attributes! :instance
|
12
|
+
end
|
13
|
+
|
14
|
+
def instance_method_added(_parser, _node, method)
|
15
|
+
return unless @inside_module_function
|
16
|
+
return unless method.instance?
|
17
|
+
|
18
|
+
method.type = :module_function
|
19
|
+
@class_methods.append(method)
|
20
|
+
@instance_methods.delete(method)
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_parsed_node(parser, node)
|
24
|
+
return unless node.is_a? Call
|
25
|
+
return unless node.name == :module_function
|
26
|
+
|
27
|
+
old_module_function = @inside_module_function
|
28
|
+
@inside_module_function = true
|
29
|
+
|
30
|
+
node.arguments&.each do |arg|
|
31
|
+
case arg.type
|
32
|
+
when :symbol_node
|
33
|
+
if (method = @instance_methods.named(arg.value.to_sym).first)
|
34
|
+
method.type = :module_function
|
35
|
+
@class_methods.append(method)
|
36
|
+
@instance_methods.delete(method)
|
37
|
+
end
|
38
|
+
|
39
|
+
when :def_node, :call_node
|
40
|
+
handle_node(arg)
|
41
|
+
else
|
42
|
+
parser.unhandled_node! node
|
43
|
+
end
|
44
|
+
ensure
|
45
|
+
@inside_module_function = old_module_function unless node.arguments && node.arguments.empty?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class NodeArray < Array
|
6
|
+
def by_kind(*kind) = filter { _1.respond_to?(:kind) && kind.include?(_1.kind) }
|
7
|
+
|
8
|
+
def named(name)
|
9
|
+
name = name.to_s
|
10
|
+
NodeArray.new(filter { _1.respond_to?(:name) && _1.name.to_s == name })
|
11
|
+
end
|
12
|
+
|
13
|
+
def named!(name) = named(name).first!
|
14
|
+
|
15
|
+
def typed(*classes) = filter { |node| classes.any? { node.is_a? _1 } }
|
16
|
+
|
17
|
+
def merge_unowned(*other)
|
18
|
+
other
|
19
|
+
.reject { |v| find { _1.name == v.name } }
|
20
|
+
.then { append(*_1) unless _1.empty? }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class Reference
|
6
|
+
visible_attr_accessor :path, :resolved
|
7
|
+
attr_accessor :parent
|
8
|
+
|
9
|
+
def initialize(parser, parent, path)
|
10
|
+
@parent = parent
|
11
|
+
@path = path
|
12
|
+
@parser = parser
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolved? = !resolved.nil?
|
16
|
+
def fulfilled? = resolved? && resolved.valid?
|
17
|
+
|
18
|
+
def dereference!
|
19
|
+
raise "Dereference of unfulfilled reference" unless fulfilled?
|
20
|
+
|
21
|
+
resolved.dereference!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class Reloader
|
6
|
+
def self.reload!
|
7
|
+
Object.send(:remove_const, :Docrb) if defined? Docrb
|
8
|
+
|
9
|
+
root_dir = File.expand_path("../..", __dir__)
|
10
|
+
$LOADED_FEATURES
|
11
|
+
.select { _1.start_with? root_dir }
|
12
|
+
.each { $LOADED_FEATURES.delete _1 }
|
13
|
+
|
14
|
+
require "docrb/parser"
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class ResolvedReference
|
6
|
+
visible_attr_accessor :status
|
7
|
+
|
8
|
+
def initialize(parser, status, id)
|
9
|
+
@status = status
|
10
|
+
@object_id = id
|
11
|
+
@parser = parser
|
12
|
+
end
|
13
|
+
|
14
|
+
def id = @object_id
|
15
|
+
|
16
|
+
def broken? = status == :broken
|
17
|
+
def valid? = status == :valid
|
18
|
+
|
19
|
+
def dereference!
|
20
|
+
raise "Cannot dereference broken reference" if broken?
|
21
|
+
|
22
|
+
@parser.object_by_id(id)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class Parser
|
5
|
+
class VirtualContainer
|
6
|
+
Body = Struct.new(:body)
|
7
|
+
ConstantName = Struct.new(:name) do
|
8
|
+
def type = :constant_read_node
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :location, :body, :constant_path, :type, :superclass
|
12
|
+
|
13
|
+
def initialize(type, name)
|
14
|
+
@type = type
|
15
|
+
@constant_path = ConstantName.new(name:)
|
16
|
+
@location = VirtualLocation.new
|
17
|
+
@body = Body.new(body: [])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|