docrb-parser 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|