yard2steep 0.1.1 → 0.1.2
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 +4 -4
- data/Gemfile.lock +6 -1
- data/example/sample1/lib/example1.rb +20 -1
- data/example/sample1/sig/example1.rbi +3 -0
- data/lib/yard2steep/ast/p_node.rb +1 -0
- data/lib/yard2steep/gen.rb +2 -0
- data/lib/yard2steep/parser.rb +566 -373
- data/lib/yard2steep/type/parser.rb +6 -2
- data/lib/yard2steep/type.rb +9 -3
- data/lib/yard2steep/util.rb +9 -1
- data/lib/yard2steep/version.rb +1 -1
- data/sig/steep-scaffold/td.rbi +29 -42
- data/sig/yard2steep/yard2steep/parser.rbi +25 -36
- data/sig/yard2steep/yard2steep/type/parser.rbi +3 -3
- data/sig/yard2steep/yard2steep/type.rbi +2 -2
- data/yard2steep.gemspec +1 -0
- metadata +16 -2
data/lib/yard2steep/parser.rb
CHANGED
@@ -1,66 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'ripper'
|
2
|
+
|
2
3
|
require 'yard2steep/type'
|
4
|
+
require 'yard2steep/ast'
|
5
|
+
require 'yard2steep/util'
|
3
6
|
|
4
7
|
module Yard2steep
|
5
8
|
class Parser
|
6
|
-
S_RE
|
7
|
-
S_P_RE = /[\s\t]+/
|
8
|
-
PRE_RE = /^#{S_RE}/
|
9
|
-
POST_RE = /#{S_RE}$/
|
10
|
-
|
11
|
-
CLASS_RE = /
|
12
|
-
#{PRE_RE}
|
13
|
-
(class)
|
14
|
-
#{S_P_RE}
|
15
|
-
(\w+)
|
16
|
-
(
|
17
|
-
#{S_P_RE}
|
18
|
-
<
|
19
|
-
#{S_P_RE}
|
20
|
-
\w+
|
21
|
-
)?
|
22
|
-
#{POST_RE}
|
23
|
-
/x
|
24
|
-
MODULE_RE = /
|
25
|
-
#{PRE_RE}
|
26
|
-
(module)
|
27
|
-
#{S_P_RE}
|
28
|
-
(\w+)
|
29
|
-
#{POST_RE}
|
30
|
-
/x
|
31
|
-
S_CLASS_RE = /#{PRE_RE}class#{S_P_RE}<<#{S_P_RE}\w+#{POST_RE}/
|
32
|
-
END_RE = /#{PRE_RE}end#{POST_RE}/
|
33
|
-
|
34
|
-
CONSTANT_ASSIGN_RE = /
|
35
|
-
#{PRE_RE}
|
36
|
-
(
|
37
|
-
[A-Z\d_]+
|
38
|
-
)
|
39
|
-
#{S_RE}
|
40
|
-
=
|
41
|
-
.*
|
42
|
-
#{POST_RE}
|
43
|
-
/x
|
44
|
-
|
45
|
-
# TODO(south37) `POSTFIX_IF_RE` is wrong. Fix it.
|
46
|
-
POSTFIX_IF_RE = /
|
47
|
-
#{PRE_RE}
|
48
|
-
(?:return|break|next|p|print|raise)
|
49
|
-
#{S_P_RE}
|
50
|
-
.*
|
51
|
-
(?:if|unless)
|
52
|
-
#{S_P_RE}
|
53
|
-
.*
|
54
|
-
$
|
55
|
-
/x
|
56
|
-
|
57
|
-
BEGIN_END_RE = /
|
58
|
-
#{S_P_RE}
|
59
|
-
(if|unless|do|while|until|case|for|begin)
|
60
|
-
(?:#{S_P_RE}.*)?
|
61
|
-
$/x
|
62
|
-
|
63
|
-
COMMENT_RE = /#{PRE_RE}#/
|
9
|
+
S_RE = /[\s\t]*/
|
64
10
|
TYPE_WITH_PAREN_RE = /
|
65
11
|
\[
|
66
12
|
(
|
@@ -69,73 +15,30 @@ module Yard2steep
|
|
69
15
|
)
|
70
16
|
\]
|
71
17
|
/x
|
72
|
-
|
18
|
+
COMMENT_RE = /^
|
19
|
+
\#
|
20
|
+
#{S_RE}
|
21
|
+
@(?:param|return)
|
22
|
+
#{S_RE}
|
23
|
+
#{TYPE_WITH_PAREN_RE}
|
24
|
+
/x
|
73
25
|
PARAM_RE = /
|
74
|
-
|
75
|
-
#{
|
26
|
+
\#
|
27
|
+
#{S_RE}
|
76
28
|
@param
|
77
|
-
#{
|
29
|
+
#{S_RE}
|
78
30
|
#{TYPE_WITH_PAREN_RE}
|
79
|
-
#{
|
31
|
+
#{S_RE}
|
80
32
|
(\w+)
|
81
33
|
/x
|
82
34
|
RETURN_RE = /
|
83
|
-
|
84
|
-
#{S_P_RE}
|
85
|
-
@return
|
86
|
-
#{S_P_RE}
|
87
|
-
#{TYPE_WITH_PAREN_RE}
|
88
|
-
/x
|
89
|
-
|
90
|
-
PAREN_RE = /
|
91
|
-
\(
|
92
|
-
([^)]*)
|
93
|
-
\)
|
94
|
-
/x
|
95
|
-
|
96
|
-
# NOTE: This implementation should be fixed.
|
97
|
-
ARGS_RE = /
|
35
|
+
\#
|
98
36
|
#{S_RE}
|
99
|
-
|
100
|
-
[^)]*
|
101
|
-
\)
|
102
|
-
|
|
103
|
-
#{S_P_RE}
|
104
|
-
.*
|
105
|
-
/x
|
106
|
-
|
107
|
-
METHOD_RE = /
|
108
|
-
#{PRE_RE}
|
109
|
-
def
|
110
|
-
#{S_P_RE}
|
111
|
-
(
|
112
|
-
(?:\w+\.)?
|
113
|
-
\w+
|
114
|
-
(?:\!|\?)?
|
115
|
-
)
|
37
|
+
@return
|
116
38
|
#{S_RE}
|
117
|
-
|
118
|
-
(?:#{ARGS_RE})
|
119
|
-
?
|
120
|
-
)
|
121
|
-
#{POST_RE}
|
122
|
-
/x
|
123
|
-
|
124
|
-
# TODO(south37) Support attr_writer, attr_accessor
|
125
|
-
ATTR_RE = /
|
126
|
-
#{PRE_RE}
|
127
|
-
attr_reader
|
128
|
-
#{S_P_RE}
|
129
|
-
(:\w+.*)
|
130
|
-
#{POST_RE}
|
39
|
+
#{TYPE_WITH_PAREN_RE}
|
131
40
|
/x
|
132
41
|
|
133
|
-
STATES = {
|
134
|
-
class: "STATES.class",
|
135
|
-
s_class: "STATES.s_class", # singleton class
|
136
|
-
method: "STATES.method",
|
137
|
-
}
|
138
|
-
|
139
42
|
ANY_TYPE = 'any'
|
140
43
|
ANY_BLOCK_TYPE = '{ (any) -> any }'
|
141
44
|
|
@@ -146,18 +49,7 @@ module Yard2steep
|
|
146
49
|
# NOTE: set at parse
|
147
50
|
@file = nil
|
148
51
|
|
149
|
-
|
150
|
-
@ast = main
|
151
|
-
|
152
|
-
# Stack of parser state
|
153
|
-
@stack = [STATES[:class]]
|
154
|
-
# Parser state. Being last one of STATES in @stack.
|
155
|
-
@state = STATES[:class]
|
156
|
-
|
157
|
-
# NOTE: reset class context
|
158
|
-
@current_class = main
|
159
|
-
|
160
|
-
reset_method_context!
|
52
|
+
@comments_map = {}
|
161
53
|
end
|
162
54
|
|
163
55
|
# @param [String] file
|
@@ -167,171 +59,170 @@ module Yard2steep
|
|
167
59
|
def parse(file, text, debug: false)
|
168
60
|
@debug = debug
|
169
61
|
|
170
|
-
debug_print!("Start parsing: #{file}")
|
171
|
-
|
172
62
|
@file = file
|
173
|
-
|
174
|
-
parse_line(l)
|
175
|
-
end
|
63
|
+
debug_print!("Start parsing: #{file}")
|
176
64
|
|
177
|
-
@
|
178
|
-
end
|
65
|
+
@comments_map = extract_comments(text)
|
179
66
|
|
180
|
-
|
67
|
+
main = AST::ClassNode.create_main
|
68
|
+
@ast = main
|
181
69
|
|
182
|
-
|
183
|
-
|
184
|
-
# Current method context. Flushed when method definition is parsed.
|
185
|
-
@p_types = {}
|
186
|
-
@r_type = nil
|
187
|
-
@m_name = nil
|
188
|
-
end
|
70
|
+
# NOTE: reset class context
|
71
|
+
@current_class = main
|
189
72
|
|
190
|
-
|
191
|
-
# multiple times before method definition.
|
192
|
-
#
|
193
|
-
# @param [String] l
|
194
|
-
# @return [void]
|
195
|
-
def parse_line(l)
|
196
|
-
# At first, try parsing comment
|
197
|
-
return if try_parse_comment(l)
|
198
|
-
|
199
|
-
return if try_parse_end(l)
|
200
|
-
return if try_parse_postfix_if(l)
|
201
|
-
return if try_parse_begin_end(l)
|
202
|
-
|
203
|
-
case @state
|
204
|
-
when STATES[:class]
|
205
|
-
return if try_parse_constant(l)
|
206
|
-
return if try_parse_class(l)
|
207
|
-
return if try_parse_singleton_class(l)
|
208
|
-
return if try_parse_method(l)
|
209
|
-
return if try_parse_attr(l)
|
210
|
-
when STATES[:s_class]
|
211
|
-
return if try_parse_method_with_no_action(l)
|
212
|
-
when STATES[:method]
|
213
|
-
# Do nothing
|
214
|
-
else
|
215
|
-
raise "invalid state: #{@state}"
|
216
|
-
end
|
73
|
+
parse_program(text)
|
217
74
|
|
218
|
-
|
75
|
+
@ast
|
219
76
|
end
|
220
77
|
|
221
|
-
# @param [String]
|
222
|
-
# @return [
|
223
|
-
def
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
78
|
+
# @param [String] text
|
79
|
+
# @return [void]
|
80
|
+
def parse_program(text)
|
81
|
+
r_ast = Ripper.sexp(text)
|
82
|
+
# NOTE: r_ast is array such as
|
83
|
+
# [:program,
|
84
|
+
# [...]]
|
85
|
+
Util.assert! { r_ast[0] == :program && r_ast[1].is_a?(Array) }
|
86
|
+
parse_stmts(r_ast[1])
|
229
87
|
end
|
230
88
|
|
231
|
-
# @param [
|
232
|
-
# @return [
|
233
|
-
def
|
234
|
-
|
235
|
-
|
236
|
-
return if try_parse_return(l)
|
89
|
+
# @param [Array] r_ast
|
90
|
+
# @return [void]
|
91
|
+
def parse_stmts(r_ast)
|
92
|
+
r_ast.each do |node|
|
93
|
+
parse_stmt(node)
|
237
94
|
end
|
238
95
|
end
|
239
96
|
|
240
|
-
# @param [
|
241
|
-
# @return [
|
242
|
-
def
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
97
|
+
# @param [Array] r_ast
|
98
|
+
# @return [void]
|
99
|
+
def parse_stmt(r_ast)
|
100
|
+
n_type = r_ast[0]
|
101
|
+
|
102
|
+
case n_type
|
103
|
+
when :defs
|
104
|
+
parse_defs(r_ast)
|
105
|
+
when :def
|
106
|
+
parse_def(r_ast)
|
107
|
+
when :class, :module
|
108
|
+
parse_class_or_module(r_ast)
|
109
|
+
when :assign
|
110
|
+
parse_assign(r_ast)
|
111
|
+
when :command
|
112
|
+
parse_command(r_ast)
|
113
|
+
when :method_add_arg
|
114
|
+
parse_method_add_arg(r_ast)
|
250
115
|
end
|
251
116
|
|
252
|
-
|
253
|
-
|
254
|
-
true
|
117
|
+
# Do nothing for other stmt
|
255
118
|
end
|
256
119
|
|
257
|
-
#
|
258
|
-
#
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
120
|
+
# @param [Array] r_ast
|
121
|
+
# @return [void]
|
122
|
+
def parse_defs(r_ast)
|
123
|
+
# NOTE: r_ast is array such as
|
124
|
+
# [:defs,
|
125
|
+
# [:var_ref, [:@kw, "self", [1, 14]]],
|
126
|
+
# [:@period, ".", [1, 18]],
|
127
|
+
# [:@ident, "my_method", [1, 19]],
|
128
|
+
# [:params, nil, nil, nil, nil, nil, nil, nil],
|
129
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]
|
130
|
+
|
131
|
+
Util.assert! {
|
132
|
+
r_ast.size == 6 &&
|
133
|
+
r_ast[0] == :defs &&
|
134
|
+
r_ast[1][0] == :var_ref &&
|
135
|
+
r_ast[1][1][0] == :@kw &&
|
136
|
+
r_ast[1][1][1].is_a?(String) &&
|
137
|
+
r_ast[1][1][2].is_a?(Array) &&
|
138
|
+
r_ast[2][0] == :@period &&
|
139
|
+
r_ast[3][0] == :@ident &&
|
140
|
+
r_ast[3][1].is_a?(String)
|
141
|
+
}
|
142
|
+
|
143
|
+
m_name = "#{r_ast[1][1][1]}.#{r_ast[3][1]}"
|
144
|
+
m_loc = r_ast[1][1][2][0]
|
145
|
+
params = r_ast[4]
|
146
|
+
# NOTE: We also want to check bodystmt
|
147
|
+
# bodystmt = r_ast[5]
|
148
|
+
|
149
|
+
parse_method_impl(m_name, m_loc, params)
|
263
150
|
end
|
264
151
|
|
265
|
-
# @param [
|
266
|
-
# @return [
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
152
|
+
# @param [Array] r_ast
|
153
|
+
# @return [void]
|
154
|
+
def parse_def(r_ast)
|
155
|
+
# NOTE: r_ast is array such as
|
156
|
+
# [:def,
|
157
|
+
# [:@ident, "first", [3, 4]],
|
158
|
+
# [:paren, [:params, ...]],
|
159
|
+
# [:bodystmt, [[:array, nil]], nil, nil, nil]]]
|
160
|
+
|
161
|
+
Util.assert! {
|
162
|
+
r_ast.size == 4 &&
|
163
|
+
r_ast[0] == :def &&
|
164
|
+
r_ast[1][0] == :@ident &&
|
165
|
+
r_ast[1][1].is_a?(String) &&
|
166
|
+
r_ast[1][2].is_a?(Array)
|
167
|
+
}
|
168
|
+
|
169
|
+
m_name = r_ast[1][1]
|
170
|
+
m_loc = r_ast[1][2][0]
|
171
|
+
params = r_ast[2]
|
172
|
+
# NOTE: We also want to check bodystmt
|
173
|
+
# bodystmt = r_ast[3]
|
174
|
+
|
175
|
+
parse_method_impl(m_name, m_loc, params)
|
276
176
|
end
|
277
177
|
|
278
|
-
# @param [String]
|
279
|
-
# @
|
280
|
-
|
281
|
-
|
282
|
-
|
178
|
+
# @param [String] m_name
|
179
|
+
# @param [Integer] m_loc
|
180
|
+
# @param [Array] params
|
181
|
+
# @return [void]
|
182
|
+
def parse_method_impl(m_name, m_loc, params)
|
183
|
+
within_context do
|
184
|
+
extract_p_types!(m_loc)
|
283
185
|
|
284
|
-
|
186
|
+
p_list = parse_params(params)
|
285
187
|
|
286
|
-
|
287
|
-
|
288
|
-
reset_method_context!
|
188
|
+
# NOTE: We also want to check bodystmt
|
189
|
+
# parse_mbody(bodystmt)
|
289
190
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
@current_class = c
|
298
|
-
|
299
|
-
push_state!(STATES[:class])
|
300
|
-
|
301
|
-
true
|
191
|
+
m_node = AST::MethodNode.new(
|
192
|
+
p_list: p_list,
|
193
|
+
r_type: (@r_type || ANY_TYPE),
|
194
|
+
m_name: m_name,
|
195
|
+
)
|
196
|
+
@current_class.append_m(m_node)
|
197
|
+
end
|
302
198
|
end
|
303
199
|
|
304
|
-
# @param [
|
305
|
-
# @return [
|
306
|
-
def
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
200
|
+
# @param [Integer] m_loc represents location of method definition
|
201
|
+
# @return [void]
|
202
|
+
def extract_p_types!(m_loc)
|
203
|
+
Util.assert! { m_loc >= 0 }
|
204
|
+
l = m_loc - 1
|
205
|
+
while l >= 0
|
206
|
+
comment = @comments_map[l]
|
207
|
+
break unless comment # nil when no more comment exist
|
208
|
+
|
209
|
+
parse_comment!(comment)
|
210
|
+
l -= 1
|
211
|
+
end
|
316
212
|
end
|
317
213
|
|
318
|
-
# @param [String]
|
319
|
-
# @return [
|
320
|
-
def
|
321
|
-
|
322
|
-
return
|
323
|
-
|
324
|
-
debug_print!("class <<")
|
325
|
-
|
326
|
-
push_state!(STATES[:s_class])
|
327
|
-
|
328
|
-
true
|
214
|
+
# @param [String] comment
|
215
|
+
# @return [void]
|
216
|
+
def parse_comment!(comment)
|
217
|
+
return if try_param_comment(comment)
|
218
|
+
return if try_return_comment(comment)
|
219
|
+
raise "Must not reach here!"
|
329
220
|
end
|
330
221
|
|
331
|
-
# @param [String]
|
222
|
+
# @param [String] comment
|
332
223
|
# @return [bool]
|
333
|
-
def
|
334
|
-
m =
|
224
|
+
def try_param_comment(comment)
|
225
|
+
m = comment.match(PARAM_RE)
|
335
226
|
return false unless m
|
336
227
|
|
337
228
|
p = AST::PTypeNode.new(
|
@@ -344,10 +235,10 @@ module Yard2steep
|
|
344
235
|
true
|
345
236
|
end
|
346
237
|
|
347
|
-
# @param [String]
|
238
|
+
# @param [String] comment
|
348
239
|
# @return [bool]
|
349
|
-
def
|
350
|
-
m =
|
240
|
+
def try_return_comment(comment)
|
241
|
+
m = comment.match(RETURN_RE)
|
351
242
|
return false unless m
|
352
243
|
|
353
244
|
@r_type = normalize_type(m[1])
|
@@ -355,94 +246,399 @@ module Yard2steep
|
|
355
246
|
true
|
356
247
|
end
|
357
248
|
|
358
|
-
# @param [
|
359
|
-
# @return [
|
360
|
-
def
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
p_list: p_list,
|
373
|
-
r_type: (@r_type || ANY_TYPE),
|
374
|
-
m_name: @m_name,
|
375
|
-
)
|
376
|
-
@current_class.append_m(m_node)
|
377
|
-
reset_method_context!
|
378
|
-
|
379
|
-
push_state!(STATES[:method])
|
380
|
-
|
381
|
-
true
|
249
|
+
# @param [Array] r_ast
|
250
|
+
# @return [Array<AST::PNode>]
|
251
|
+
def parse_params(r_ast)
|
252
|
+
# NOTE: parrams is `paren_params` or `no_paren_params`
|
253
|
+
# paren_params: [:paren, [:params, ...]]
|
254
|
+
# no_paren_params: [:params, ...]
|
255
|
+
case r_ast[0]
|
256
|
+
when :paren
|
257
|
+
parse_paren_params(r_ast)
|
258
|
+
when :params
|
259
|
+
parse_no_paren_params(r_ast)
|
260
|
+
else
|
261
|
+
raise "invalid node"
|
262
|
+
end
|
382
263
|
end
|
383
264
|
|
384
|
-
# @param [
|
385
|
-
# @return [
|
386
|
-
def
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
true
|
265
|
+
# @param [Array] r_ast
|
266
|
+
# @return [Array<AST::PNode>]
|
267
|
+
def parse_paren_params(r_ast)
|
268
|
+
# NOTE: r_ast is array such as
|
269
|
+
# [:paren, [:params, ...]]
|
270
|
+
Util.assert! {
|
271
|
+
r_ast[0] == :paren &&
|
272
|
+
r_ast[1].is_a?(Array) &&
|
273
|
+
r_ast[1][0] == :params
|
274
|
+
}
|
275
|
+
parse_no_paren_params(r_ast[1])
|
397
276
|
end
|
398
277
|
|
399
|
-
# @param [
|
278
|
+
# @param [Array] params
|
400
279
|
# @return [Array<AST::PNode>]
|
401
|
-
def
|
402
|
-
|
280
|
+
def parse_no_paren_params(params)
|
281
|
+
# NOTE: params is array such as
|
282
|
+
# [:params,
|
283
|
+
# nil,
|
284
|
+
# nil,
|
285
|
+
# nil,
|
286
|
+
# nil,
|
287
|
+
# [
|
288
|
+
# [
|
289
|
+
# [:@label, "contents:", [3, 10]],
|
290
|
+
# false
|
291
|
+
# ]
|
292
|
+
# ],
|
293
|
+
# nil,
|
294
|
+
# nil
|
295
|
+
# ]],
|
296
|
+
Util.assert! { params[0] == :params && params.size == 8 }
|
297
|
+
|
298
|
+
r = []
|
299
|
+
n_params = (params[1] || [])
|
300
|
+
# NOTE: n_params is array such as
|
301
|
+
# [[:@ident, "a", [1, 7]], ...]
|
302
|
+
n_params.each do |key|
|
303
|
+
# TODO(south37) Optimize this check
|
304
|
+
Util.assert! { key[0] == :@ident && key[1].is_a?(String) }
|
305
|
+
name = key[1]
|
306
|
+
r.push(
|
307
|
+
AST::PNode.new(
|
308
|
+
type_node: type_node(name),
|
309
|
+
style: AST::PNode::STYLE[:normal],
|
310
|
+
)
|
311
|
+
)
|
312
|
+
end
|
403
313
|
|
404
|
-
|
405
|
-
|
406
|
-
|
314
|
+
v_params = (params[2] || [])
|
315
|
+
# NOTE: v_params is array such as
|
316
|
+
# [
|
317
|
+
# [
|
318
|
+
# [:@ident, "a", [1, 7]],
|
319
|
+
# [:@int, "2", [1, 9]]
|
320
|
+
# ],
|
321
|
+
# ...
|
322
|
+
# ]
|
323
|
+
v_params.each do |v_param|
|
324
|
+
key, default_v = v_param
|
325
|
+
|
326
|
+
# TODO(south37) Optimize this check
|
327
|
+
Util.assert! {
|
328
|
+
key[0] == :@ident &&
|
329
|
+
key[1].is_a?(String) &&
|
330
|
+
default_v.is_a?(Array)
|
331
|
+
}
|
332
|
+
name = key[1]
|
333
|
+
r.push(
|
334
|
+
AST::PNode.new(
|
335
|
+
type_node: type_node(name),
|
336
|
+
style: AST::PNode::STYLE[:normal_with_default],
|
337
|
+
)
|
338
|
+
)
|
407
339
|
end
|
408
340
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
341
|
+
n_params2 = (params[4] || [])
|
342
|
+
# NOTE: n_params2 is array such as
|
343
|
+
# [[:@ident, "a", [1, 7]], ...]
|
344
|
+
n_params2.each do |key|
|
345
|
+
# TODO(south37) Optimize this check
|
346
|
+
Util.assert! { key[0] == :@ident && key[1].is_a?(String) }
|
347
|
+
name = key[1]
|
348
|
+
r.push(
|
349
|
+
AST::PNode.new(
|
350
|
+
type_node: type_node(name),
|
351
|
+
style: AST::PNode::STYLE[:normal],
|
352
|
+
)
|
353
|
+
)
|
414
354
|
end
|
415
355
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
356
|
+
k_params = (params[5] || [])
|
357
|
+
# NOTE: k_params is array such as
|
358
|
+
# [
|
359
|
+
# [
|
360
|
+
# [:@label, "contents:", [3, 10]],
|
361
|
+
# false
|
362
|
+
# ],
|
363
|
+
# ...
|
364
|
+
# ],
|
365
|
+
k_params.each do |v_param|
|
366
|
+
key, default_v = v_param
|
367
|
+
|
368
|
+
# TODO(south37) Optimize this check
|
369
|
+
Util.assert! {
|
370
|
+
key[0] == :@label &&
|
371
|
+
key[1][-1] == ':'
|
372
|
+
}
|
373
|
+
name = key[1][0..-2]
|
374
|
+
if default_v
|
375
|
+
r.push(
|
420
376
|
AST::PNode.new(
|
421
377
|
type_node: type_node(name),
|
422
378
|
style: AST::PNode::STYLE[:keyword_with_default],
|
423
379
|
)
|
424
|
-
|
380
|
+
)
|
381
|
+
else
|
382
|
+
r.push(
|
425
383
|
AST::PNode.new(
|
426
384
|
type_node: type_node(name),
|
427
385
|
style: AST::PNode::STYLE[:keyword],
|
428
386
|
)
|
429
|
-
|
430
|
-
|
387
|
+
)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
b_param = params[7]
|
392
|
+
# NOTE: b_params is array such as
|
393
|
+
# [:blockarg, [:@ident, "block", [1, 8]]]
|
394
|
+
if b_param
|
395
|
+
Util.assert! {
|
396
|
+
b_param[0] == :blockarg &&
|
397
|
+
b_param[1].is_a?(Array) &&
|
398
|
+
b_param[1][0] == :@ident &&
|
399
|
+
b_param[1][1].is_a?(String)
|
400
|
+
}
|
401
|
+
name = b_param[1][1]
|
402
|
+
r.push(
|
431
403
|
AST::PNode.new(
|
432
|
-
type_node:
|
404
|
+
type_node: block_type_node(name),
|
433
405
|
style: AST::PNode::STYLE[:normal],
|
434
406
|
)
|
407
|
+
)
|
408
|
+
end
|
409
|
+
|
410
|
+
# NOTE: params[3] is `*a`, params[6] is `**a`
|
411
|
+
|
412
|
+
r
|
413
|
+
end
|
414
|
+
|
415
|
+
# @return [void]
|
416
|
+
def within_context(&block)
|
417
|
+
# Current method context.
|
418
|
+
@p_types = {}
|
419
|
+
@r_type = nil
|
420
|
+
block.call
|
421
|
+
end
|
422
|
+
|
423
|
+
# @param [Array] r_ast
|
424
|
+
# @return [void]
|
425
|
+
def parse_class_or_module(r_ast)
|
426
|
+
# NOTE: r_ast is array such as
|
427
|
+
# [:class,
|
428
|
+
# [:const_ref, [:@const, "OtherClass", [119, 6]]],
|
429
|
+
# [:var_ref, [:@const, "MyClass", [119, 19]]],
|
430
|
+
# [:bodystmt, [...]]]
|
431
|
+
|
432
|
+
kind = r_ast[0].to_s
|
433
|
+
|
434
|
+
case kind
|
435
|
+
when "class"
|
436
|
+
Util.assert! { r_ast.size == 4 }
|
437
|
+
c_name_node = r_ast[1]
|
438
|
+
super_c_node = r_ast[2]
|
439
|
+
bodystmt_node = r_ast[3]
|
440
|
+
when "module"
|
441
|
+
Util.assert! { r_ast.size == 3 }
|
442
|
+
c_name_node = r_ast[1]
|
443
|
+
super_c_node = nil
|
444
|
+
bodystmt_node = r_ast[2]
|
445
|
+
else
|
446
|
+
raise "invalid kind: #{kind}"
|
447
|
+
end
|
448
|
+
|
449
|
+
Util.assert! {
|
450
|
+
c_name_node[0] == :const_ref &&
|
451
|
+
c_name_node[1].is_a?(Array) &&
|
452
|
+
c_name_node[1][0] == :@const &&
|
453
|
+
c_name_node[1][1].is_a?(String)
|
454
|
+
}
|
455
|
+
c_name = c_name_node[1][1]
|
456
|
+
Util.assert! {
|
457
|
+
!super_c_node || (
|
458
|
+
super_c_node[0] == :var_ref &&
|
459
|
+
super_c_node[1].is_a?(Array) &&
|
460
|
+
super_c_node[1][0] == :@const &&
|
461
|
+
super_c_node[1][1].is_a?(String)
|
462
|
+
)
|
463
|
+
}
|
464
|
+
super_c_name = super_c_node ? super_c_node[1][1] : nil
|
465
|
+
|
466
|
+
c = AST::ClassNode.new(
|
467
|
+
kind: kind,
|
468
|
+
c_name: c_name,
|
469
|
+
super_c: super_c_name,
|
470
|
+
parent: @current_class,
|
471
|
+
)
|
472
|
+
@current_class.append_child(c)
|
473
|
+
@current_class = c
|
474
|
+
|
475
|
+
parse_bodystmt(bodystmt_node)
|
476
|
+
|
477
|
+
@current_class = @current_class.parent
|
478
|
+
end
|
479
|
+
|
480
|
+
# @param [Array] r_ast
|
481
|
+
# @return [void]
|
482
|
+
def parse_bodystmt(r_ast)
|
483
|
+
# NOTE:
|
484
|
+
# [:bodystmt,
|
485
|
+
# [...]]
|
486
|
+
Util.assert! {
|
487
|
+
r_ast[0] == :bodystmt &&
|
488
|
+
r_ast[1].is_a?(Array)
|
489
|
+
}
|
490
|
+
parse_stmts(r_ast[1])
|
491
|
+
end
|
492
|
+
|
493
|
+
# @param [Array] r_ast
|
494
|
+
# @return [void]
|
495
|
+
def parse_assign(r_ast)
|
496
|
+
# NOTE: r_ast is array such as
|
497
|
+
# [:assign,
|
498
|
+
# [:var_field, [:@const, "DNA", [1, 15]]],
|
499
|
+
# [...]]
|
500
|
+
Util.assert! {
|
501
|
+
r_ast[0] == :assign &&
|
502
|
+
r_ast[1].is_a?(Array) &&
|
503
|
+
r_ast[1][0] == :var_field
|
504
|
+
}
|
505
|
+
var_type = r_ast[1][1][0]
|
506
|
+
if var_type == :@const
|
507
|
+
# TODO(south37) Check the value node to predict the type of the const.
|
508
|
+
c = AST::ConstantNode.new(
|
509
|
+
name: r_ast[1][1][1],
|
510
|
+
klass: @current_class,
|
511
|
+
)
|
512
|
+
@current_class.append_constant(c)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
# @param [Array] r_ast
|
517
|
+
# @return [void]
|
518
|
+
def parse_command(r_ast)
|
519
|
+
# NOTE: r_ast is array such as
|
520
|
+
# [:command,
|
521
|
+
# [:@ident, "attr_reader", [1, 15]],
|
522
|
+
# [:args_add_block,
|
523
|
+
# [
|
524
|
+
# [:symbol_literal, [:symbol, [:@ident, "ok", [1, 28]]]]
|
525
|
+
# ],
|
526
|
+
# false
|
527
|
+
# ]
|
528
|
+
# ]
|
529
|
+
Util.assert! {
|
530
|
+
r_ast[0] == :command &&
|
531
|
+
r_ast[1].is_a?(Array)
|
532
|
+
}
|
533
|
+
|
534
|
+
# NOTE: check attr_*
|
535
|
+
if r_ast[1][0] == :@ident
|
536
|
+
case r_ast[1][1]
|
537
|
+
when "attr_reader"
|
538
|
+
parse_attr_reader(parse_command_args_add_block(r_ast[2]))
|
539
|
+
when "attr_writer"
|
540
|
+
# TODO(south37) Impl
|
541
|
+
# parse_attr_writer(parse_command_args_add_block(r_ast[2]))
|
542
|
+
when "attr_accessor"
|
543
|
+
# TODO(south37) Impl
|
544
|
+
# parse_attr_accessor(parse_command_args_add_block(r_ast[2]))
|
435
545
|
end
|
546
|
+
|
547
|
+
# Do nothing for other case
|
436
548
|
end
|
437
549
|
end
|
438
550
|
|
439
|
-
# @param [
|
440
|
-
# @return [
|
441
|
-
def
|
442
|
-
|
443
|
-
|
551
|
+
# @param [Array] r_ast
|
552
|
+
# @return [Array<String>]
|
553
|
+
def parse_command_args_add_block(r_ast)
|
554
|
+
# NOTE: r_ast is array such as
|
555
|
+
#
|
556
|
+
# [:args_add_block,
|
557
|
+
# [
|
558
|
+
# [:symbol_literal, [:symbol, [:@ident, "ok", [1, 28]]]],
|
559
|
+
# [:symbol_literal, [:symbol, [:@ident, "no", [1, 33]]]],
|
560
|
+
# [:string_literal, [:string_content, [:@tstring_content, "b", [1, 38]]]]
|
561
|
+
# ],
|
562
|
+
# false
|
563
|
+
# ]
|
564
|
+
Util.assert! {
|
565
|
+
r_ast[0] == :args_add_block &&
|
566
|
+
r_ast[1].is_a?(Array)
|
567
|
+
}
|
568
|
+
r = []
|
569
|
+
r_ast[1].each do |ast|
|
570
|
+
case ast[0]
|
571
|
+
when :symbol_literal
|
572
|
+
Util.assert! {
|
573
|
+
ast[1].is_a?(Array) &&
|
574
|
+
ast[1][0] == :symbol &&
|
575
|
+
ast[1][1].is_a?(Array) &&
|
576
|
+
ast[1][1][0] == :@ident &&
|
577
|
+
ast[1][1][1].is_a?(String)
|
578
|
+
}
|
579
|
+
r.push(ast[1][1][1])
|
580
|
+
when :string_literal
|
581
|
+
Util.assert! {
|
582
|
+
ast[1].is_a?(Array) &&
|
583
|
+
ast[1][0] == :string_content &&
|
584
|
+
ast[1][1].is_a?(Array) &&
|
585
|
+
ast[1][1][0] == :@tstring_content &&
|
586
|
+
ast[1][1][1].is_a?(String)
|
587
|
+
}
|
588
|
+
r.push(ast[1][1][1])
|
589
|
+
end
|
590
|
+
# Do nothing for other case
|
591
|
+
end
|
592
|
+
r
|
593
|
+
end
|
444
594
|
|
445
|
-
|
595
|
+
# @param [Array] r_ast
|
596
|
+
# @return [void]
|
597
|
+
def parse_method_add_arg(r_ast)
|
598
|
+
# NOTE: r_ast is Array such as
|
599
|
+
# [:method_add_arg,
|
600
|
+
# [:fcall, [:@ident, "attr_reader", [1, 15]]],
|
601
|
+
# [:arg_paren,
|
602
|
+
# [
|
603
|
+
# :args_add_block,
|
604
|
+
# [
|
605
|
+
# [:symbol_literal, [:symbol, [:@ident, "ok", [1, 28]]]]
|
606
|
+
# ],
|
607
|
+
# false
|
608
|
+
# ]
|
609
|
+
# ]
|
610
|
+
# ]
|
611
|
+
Util.assert! {
|
612
|
+
r_ast[0] == :method_add_arg &&
|
613
|
+
r_ast[1].is_a?(Array) &&
|
614
|
+
r_ast[1][0] == :fcall &&
|
615
|
+
r_ast[1][1].is_a?(Array) &&
|
616
|
+
r_ast[2].is_a?(Array) &&
|
617
|
+
r_ast[2][0] == :arg_paren &&
|
618
|
+
r_ast[2][1].is_a?(Array) &&
|
619
|
+
r_ast[2][1][0] == :args_add_block &&
|
620
|
+
r_ast[2][1][1].is_a?(Array)
|
621
|
+
}
|
622
|
+
|
623
|
+
# NOTE: check attr_*
|
624
|
+
if r_ast[1][1][0] == :@ident
|
625
|
+
case r_ast[1][1][1]
|
626
|
+
when "attr_reader"
|
627
|
+
parse_attr_reader(parse_command_args_add_block(r_ast[2][1]))
|
628
|
+
when "attr_writer"
|
629
|
+
# TODO(south37) Impl
|
630
|
+
# parse_attr_writer(parse_command_args_add_block(r_ast[2][1]))
|
631
|
+
when "attr_accessor"
|
632
|
+
# TODO(south37) Impl
|
633
|
+
# parse_attr_accessor(parse_command_args_add_block(r_ast[2][1]))
|
634
|
+
end
|
635
|
+
# Do nothing for other case
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
# @param [Array<String>] vars
|
640
|
+
# @return [void]
|
641
|
+
def parse_attr_reader(ivars)
|
446
642
|
ivars.each do |ivarname|
|
447
643
|
@current_class.append_ivar(
|
448
644
|
AST::IVarNode.new(
|
@@ -459,70 +655,67 @@ module Yard2steep
|
|
459
655
|
)
|
460
656
|
)
|
461
657
|
end
|
462
|
-
|
463
|
-
true
|
464
|
-
end
|
465
|
-
|
466
|
-
# @param [String] state
|
467
|
-
# @return [void]
|
468
|
-
def push_state!(state)
|
469
|
-
if STATES.values.include?(state)
|
470
|
-
@state = state
|
471
|
-
end
|
472
|
-
@stack.push(state)
|
473
658
|
end
|
474
659
|
|
475
|
-
# @
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
660
|
+
# @param [String] text
|
661
|
+
# @return [Hash{ String => String }]
|
662
|
+
def extract_comments(text)
|
663
|
+
# NOTE: `Ripper.lex` returns array of array such as
|
664
|
+
# [
|
665
|
+
# [[1, 0], :on_comment, "# @param [Array] contents\n", EXPR_BEG],
|
666
|
+
# ...
|
667
|
+
# ]
|
668
|
+
r = {}
|
669
|
+
Ripper.lex(text).each do |t|
|
670
|
+
# Check token type
|
671
|
+
type = t[1]
|
672
|
+
next if type != :on_comment
|
673
|
+
# Check comment body
|
674
|
+
comment = t[2]
|
675
|
+
next unless comment.match?(COMMENT_RE)
|
676
|
+
|
677
|
+
line = t[0][0]
|
678
|
+
r[line] = comment
|
487
679
|
end
|
488
|
-
end
|
489
680
|
|
490
|
-
|
491
|
-
def stack_is_empty?
|
492
|
-
@stack.size <= 1
|
681
|
+
r
|
493
682
|
end
|
494
683
|
|
495
684
|
##
|
496
685
|
# Helper
|
497
686
|
|
687
|
+
# @param [String] message
|
688
|
+
# @return [void]
|
689
|
+
def debug_print!(message)
|
690
|
+
print "#{message}\n" if @debug
|
691
|
+
end
|
692
|
+
|
498
693
|
# @param [String] p
|
499
694
|
# @return [AST::PTypeNode]
|
500
695
|
def type_node(p)
|
501
696
|
if @p_types[p]
|
502
697
|
@p_types[p]
|
503
698
|
else
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
kind: AST::PTypeNode::KIND[:block],
|
510
|
-
)
|
511
|
-
else
|
512
|
-
AST::PTypeNode.new(
|
513
|
-
p_type: ANY_TYPE,
|
514
|
-
p_name: p,
|
515
|
-
kind: AST::PTypeNode::KIND[:normal],
|
516
|
-
)
|
517
|
-
end
|
699
|
+
AST::PTypeNode.new(
|
700
|
+
p_type: ANY_TYPE,
|
701
|
+
p_name: p,
|
702
|
+
kind: AST::PTypeNode::KIND[:normal],
|
703
|
+
)
|
518
704
|
end
|
519
705
|
end
|
520
706
|
|
521
|
-
# @param [String]
|
522
|
-
# @
|
523
|
-
|
524
|
-
|
525
|
-
|
707
|
+
# @param [String] p
|
708
|
+
# @return [AST::PTypeNode]
|
709
|
+
def block_type_node(p)
|
710
|
+
if @p_types[p]
|
711
|
+
@p_types[p]
|
712
|
+
else
|
713
|
+
AST::PTypeNode.new(
|
714
|
+
p_type: ANY_BLOCK_TYPE,
|
715
|
+
p_name: p,
|
716
|
+
kind: AST::PTypeNode::KIND[:block],
|
717
|
+
)
|
718
|
+
end
|
526
719
|
end
|
527
720
|
|
528
721
|
# @param [String] type
|