rbs-inline 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/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +109 -0
- data/Rakefile +12 -0
- data/Steepfile +27 -0
- data/exe/rbs-inline +6 -0
- data/lib/rbs/inline/annotation_parser.rb +765 -0
- data/lib/rbs/inline/ast/annotations.rb +454 -0
- data/lib/rbs/inline/ast/comment_lines.rb +42 -0
- data/lib/rbs/inline/ast/declarations.rb +243 -0
- data/lib/rbs/inline/ast/members.rb +599 -0
- data/lib/rbs/inline/ast/tree.rb +141 -0
- data/lib/rbs/inline/cli.rb +106 -0
- data/lib/rbs/inline/node_utils.rb +12 -0
- data/lib/rbs/inline/parser.rb +360 -0
- data/lib/rbs/inline/version.rb +7 -0
- data/lib/rbs/inline/writer.rb +210 -0
- data/lib/rbs/inline.rb +22 -0
- data/rbs_collection.lock.yaml +68 -0
- data/rbs_collection.yaml +17 -0
- data/sig/generated/rbs/inline/annotation_parser.rbs +222 -0
- data/sig/generated/rbs/inline/ast/annotations.rbs +185 -0
- data/sig/generated/rbs/inline/ast/declarations.rbs +116 -0
- data/sig/generated/rbs/inline/ast/members.rbs +179 -0
- data/sig/generated/rbs/inline/cli.rbs +21 -0
- data/sig/generated/rbs/inline/parser.rbs +116 -0
- data/sig/rbs/inline/annotation_parser.rbs +0 -0
- data/sig/rbs/inline/ast/comment_lines.rbs +27 -0
- data/sig/rbs/inline/ast/members.rbs +24 -0
- data/sig/rbs/inline/ast/tree.rbs +98 -0
- data/sig/rbs/inline/node_utils.rbs +7 -0
- data/sig/rbs/inline/writer.rbs +27 -0
- data/sig/rbs/inline.rbs +41 -0
- data/yard-samples/hello.rb +6 -0
- data/yard-samples/sample1.rb +26 -0
- metadata +111 -0
@@ -0,0 +1,765 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
|
3
|
+
module RBS
|
4
|
+
module Inline
|
5
|
+
class AnnotationParser
|
6
|
+
class ParsingResult
|
7
|
+
attr_reader :comments #:: Array[Prism::Comment]
|
8
|
+
attr_reader :annotations #:: Array[AST::Annotations::t]
|
9
|
+
attr_reader :first_comment_offset #:: Integer
|
10
|
+
|
11
|
+
# @rbs first_comment: Prism::Comment
|
12
|
+
def initialize(first_comment)
|
13
|
+
@comments = [first_comment]
|
14
|
+
@annotations = []
|
15
|
+
content = first_comment.location.slice
|
16
|
+
index = content.index(/[^#\s]/) || content.size
|
17
|
+
@first_comment_offset = index
|
18
|
+
end
|
19
|
+
|
20
|
+
# @rbs returns Range[Integer]
|
21
|
+
def line_range
|
22
|
+
first = comments.first or raise
|
23
|
+
last = comments.last or raise
|
24
|
+
|
25
|
+
first.location.start_line .. last.location.end_line
|
26
|
+
end
|
27
|
+
|
28
|
+
# @rbs returns Prism::Comment
|
29
|
+
def last_comment
|
30
|
+
comments.last or raise
|
31
|
+
end
|
32
|
+
|
33
|
+
# @rbs comment: Prism::Comment
|
34
|
+
# @rbs returns self?
|
35
|
+
def add_comment(comment)
|
36
|
+
if last_comment.location.end_line + 1 == comment.location.start_line
|
37
|
+
if last_comment.location.start_column == comment.location.start_column
|
38
|
+
if prefix = comment.location.start_line_slice[..comment.location.start_column]
|
39
|
+
prefix.strip!
|
40
|
+
if prefix.empty?
|
41
|
+
comments << comment
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @rbs returns Array[[String, Prism::Comment]]
|
50
|
+
def lines
|
51
|
+
comments.map do |comment|
|
52
|
+
slice = comment.location.slice
|
53
|
+
index = slice.index(/[^#\s]/) || slice.size
|
54
|
+
string = if index > first_comment_offset
|
55
|
+
slice[first_comment_offset..] || ""
|
56
|
+
else
|
57
|
+
slice[index..] || ""
|
58
|
+
end
|
59
|
+
[string, comment]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @rbs returns String
|
64
|
+
def content
|
65
|
+
lines.map(&:first).join("\n")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :input #:: Array[Prism::Comment]
|
70
|
+
|
71
|
+
# @rbs input: Array[Prism::Comment]
|
72
|
+
def initialize(input)
|
73
|
+
@input = input
|
74
|
+
end
|
75
|
+
|
76
|
+
# @rbs input: Array[Prism::Comment]
|
77
|
+
# @rbs returns Array[ParsingResult]
|
78
|
+
def self.parse(input)
|
79
|
+
new(input).parse
|
80
|
+
end
|
81
|
+
|
82
|
+
# @rbs returns Array[ParsingResult]
|
83
|
+
def parse
|
84
|
+
results = [] #: Array[ParsingResult]
|
85
|
+
|
86
|
+
first_comment, *rest = input
|
87
|
+
first_comment or return results
|
88
|
+
|
89
|
+
result = ParsingResult.new(first_comment)
|
90
|
+
results << result
|
91
|
+
|
92
|
+
rest.each do |comment|
|
93
|
+
unless result.add_comment(comment)
|
94
|
+
result = ParsingResult.new(comment)
|
95
|
+
results << result
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
results.each do |result|
|
100
|
+
each_annotation_paragraph(result) do |comments|
|
101
|
+
if annot = parse_annotation(AST::CommentLines.new(comments))
|
102
|
+
result.annotations << annot
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
results
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# @rbs result: ParsingResult
|
113
|
+
# @rbs block: ^(Array[Prism::Comment]) -> void
|
114
|
+
# @rbs returns void
|
115
|
+
def each_annotation_paragraph(result, &block)
|
116
|
+
lines = result.lines
|
117
|
+
|
118
|
+
while true
|
119
|
+
line, comment = lines.shift
|
120
|
+
break unless line && comment
|
121
|
+
|
122
|
+
next_line, next_comment = lines.first
|
123
|
+
|
124
|
+
possible_annotation = false
|
125
|
+
possible_annotation ||= line.start_with?('@rbs', '@rbs!')
|
126
|
+
possible_annotation ||= comment.location.slice.start_with?("#::", "#[") # No leading whitespace is allowed
|
127
|
+
|
128
|
+
if possible_annotation
|
129
|
+
line_offset = line.index(/\S/) || raise
|
130
|
+
|
131
|
+
comments = [comment]
|
132
|
+
|
133
|
+
while true
|
134
|
+
break unless next_line && next_comment
|
135
|
+
next_offset = next_line.index(/\S/) || 0
|
136
|
+
break unless next_offset > line_offset
|
137
|
+
|
138
|
+
comments << next_comment
|
139
|
+
lines.shift
|
140
|
+
|
141
|
+
next_line, next_comment = lines.first
|
142
|
+
end
|
143
|
+
|
144
|
+
yield comments
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Tokenizer
|
150
|
+
attr_reader :scanner #:: StringScanner
|
151
|
+
attr_reader :current_token #:: token?
|
152
|
+
|
153
|
+
KEYWORDS = {
|
154
|
+
"returns" => :kRETURNS,
|
155
|
+
"inherits" => :kINHERITS,
|
156
|
+
"as" => :kAS,
|
157
|
+
"override" => :kOVERRIDE,
|
158
|
+
"use" => :kUSE,
|
159
|
+
"module-self" => :kMODULESELF,
|
160
|
+
"generic" => :kGENERIC,
|
161
|
+
"in" => :kIN,
|
162
|
+
"out" => :kOUT,
|
163
|
+
"unchecked" => :kUNCHECKED,
|
164
|
+
"self" => :kSELF,
|
165
|
+
"skip" => :kSKIP,
|
166
|
+
"yields" => :kYIELDS,
|
167
|
+
} #:: Hash[String, Symbol]
|
168
|
+
KW_RE = /#{Regexp.union(KEYWORDS.keys)}\b/
|
169
|
+
|
170
|
+
PUNCTS = {
|
171
|
+
"[optional]" => :kOPTIONAL,
|
172
|
+
"::" => :kCOLON2,
|
173
|
+
":" => :kCOLON,
|
174
|
+
"[" => :kLBRACKET,
|
175
|
+
"]" => :kRBRACKET,
|
176
|
+
"," => :kCOMMA,
|
177
|
+
"*" => :kSTAR,
|
178
|
+
"--" => :kMINUS2,
|
179
|
+
"<" => :kLT,
|
180
|
+
"." => :kDOT,
|
181
|
+
} #:: Hash[String, Symbol]
|
182
|
+
PUNCTS_RE = Regexp.union(PUNCTS.keys) #:: Regexp
|
183
|
+
|
184
|
+
# @rbs scanner: StringScanner
|
185
|
+
# @rbs returns void
|
186
|
+
def initialize(scanner)
|
187
|
+
@scanner = scanner
|
188
|
+
@current_token = nil
|
189
|
+
end
|
190
|
+
|
191
|
+
# @rbs tree: AST::Tree
|
192
|
+
# @rbs returns token?
|
193
|
+
def advance(tree)
|
194
|
+
last = current_token
|
195
|
+
|
196
|
+
case
|
197
|
+
when s = scanner.scan(/\s+/)
|
198
|
+
tree << [:tWHITESPACE, s] if tree
|
199
|
+
advance(tree)
|
200
|
+
when s = scanner.scan(/@rbs!/)
|
201
|
+
@current_token = [:kRBSE, s]
|
202
|
+
when s = scanner.scan(/@rbs\b/)
|
203
|
+
@current_token = [:kRBS, s]
|
204
|
+
when s = scanner.scan(PUNCTS_RE)
|
205
|
+
@current_token = [PUNCTS.fetch(s), s]
|
206
|
+
when s = scanner.scan(KW_RE)
|
207
|
+
@current_token = [KEYWORDS.fetch(s), s]
|
208
|
+
when s = scanner.scan(/[A-Z]\w*/)
|
209
|
+
@current_token = [:tUIDENT, s]
|
210
|
+
when s = scanner.scan(/_[A-Z]\w*/)
|
211
|
+
@current_token = [:tIFIDENT, s]
|
212
|
+
when s = scanner.scan(/[a-z]\w*/)
|
213
|
+
@current_token = [:tLVAR, s]
|
214
|
+
when s = scanner.scan(/![a-z]\w*/)
|
215
|
+
@current_token = [:tELVAR, s]
|
216
|
+
when s = scanner.scan(/@\w+/)
|
217
|
+
@current_token = [:tATIDENT, s]
|
218
|
+
when s = scanner.scan(/%a\{[^}]+\}/)
|
219
|
+
@current_token = [:tANNOTATION, s]
|
220
|
+
when s = scanner.scan(/%a\[[^\]]+\]/)
|
221
|
+
@current_token = [:tANNOTATION, s]
|
222
|
+
when s = scanner.scan(/%a\([^)]+\)/)
|
223
|
+
@current_token = [:tANNOTATION, s]
|
224
|
+
else
|
225
|
+
@current_token = nil
|
226
|
+
end
|
227
|
+
|
228
|
+
last
|
229
|
+
end
|
230
|
+
|
231
|
+
# Consume given token type and inserts the token to the tree or `nil`
|
232
|
+
#
|
233
|
+
# @rbs type: Array[Symbol]
|
234
|
+
# @rbs tree: AST::Tree
|
235
|
+
# @rbs returns void
|
236
|
+
def consume_token(*types, tree:)
|
237
|
+
if type?(*types)
|
238
|
+
tree << advance(tree)
|
239
|
+
else
|
240
|
+
tree << nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Consume given token type and inserts the token to the tree or raise
|
245
|
+
#
|
246
|
+
# @rbs type: Array[Symbol]
|
247
|
+
# @rbs tree: AST::Tree
|
248
|
+
# @rbs returns void
|
249
|
+
def consume_token!(*types, tree:)
|
250
|
+
type!(*types)
|
251
|
+
tree << advance(tree)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Test if current token has specified `type`
|
255
|
+
#
|
256
|
+
# @rbs type: Array[Symbol]
|
257
|
+
# @rbs returns bool
|
258
|
+
def type?(*type)
|
259
|
+
type.any? { current_token && current_token[0] == _1 }
|
260
|
+
end
|
261
|
+
|
262
|
+
# Ensure current token is one of the specified in types
|
263
|
+
#
|
264
|
+
# @rbs types: Array[Symbol]
|
265
|
+
# @rbs returns void
|
266
|
+
def type!(*types)
|
267
|
+
raise "Unexpected token: #{current_token&.[](0)}, where expected token: #{types.join(",")}" unless type?(*types)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Reset the current_token to incoming comment `--`
|
271
|
+
#
|
272
|
+
# Reset to the end of the input if `--` token cannot be found.
|
273
|
+
#
|
274
|
+
# @rbs returns String -- String that is skipped
|
275
|
+
def skip_to_comment
|
276
|
+
return "" if type?(:kMINUS2)
|
277
|
+
|
278
|
+
if string = scanner.scan_until(/--/)
|
279
|
+
@current_token = [:kMINUS2, "--"]
|
280
|
+
string.delete_suffix("--")
|
281
|
+
else
|
282
|
+
s = scanner.rest
|
283
|
+
@current_token = [:kEOF, ""]
|
284
|
+
scanner.terminate
|
285
|
+
s
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# @rbs comments: AST::CommentLines
|
291
|
+
# @rbs returns AST::Annotations::t?
|
292
|
+
def parse_annotation(comments)
|
293
|
+
scanner = StringScanner.new(comments.string)
|
294
|
+
tokenizer = Tokenizer.new(scanner)
|
295
|
+
|
296
|
+
tree = AST::Tree.new(:rbs_annotation)
|
297
|
+
tokenizer.advance(tree)
|
298
|
+
|
299
|
+
case
|
300
|
+
when tokenizer.type?(:kRBSE)
|
301
|
+
tree << tokenizer.current_token
|
302
|
+
tree << [:EMBEDDED_RBS, tokenizer.scanner.rest]
|
303
|
+
tokenizer.scanner.terminate
|
304
|
+
AST::Annotations::Embedded.new(tree, comments)
|
305
|
+
when tokenizer.type?(:kRBS)
|
306
|
+
tree << tokenizer.current_token
|
307
|
+
|
308
|
+
tokenizer.advance(tree)
|
309
|
+
|
310
|
+
case
|
311
|
+
when tokenizer.type?(:tLVAR, :tELVAR)
|
312
|
+
tree << parse_var_decl(tokenizer)
|
313
|
+
AST::Annotations::VarType.new(tree, comments)
|
314
|
+
when tokenizer.type?(:kSKIP)
|
315
|
+
AST::Annotations::Skip.new(tree, comments)
|
316
|
+
when tokenizer.type?(:kRETURNS)
|
317
|
+
tree << parse_return_type_decl(tokenizer)
|
318
|
+
AST::Annotations::ReturnType.new(tree, comments)
|
319
|
+
when tokenizer.type?(:tANNOTATION)
|
320
|
+
tree << parse_rbs_annotation(tokenizer)
|
321
|
+
AST::Annotations::RBSAnnotation.new(tree, comments)
|
322
|
+
when tokenizer.type?(:kINHERITS)
|
323
|
+
tree << parse_inherits(tokenizer)
|
324
|
+
AST::Annotations::Inherits.new(tree, comments)
|
325
|
+
when tokenizer.type?(:kOVERRIDE)
|
326
|
+
tree << parse_override(tokenizer)
|
327
|
+
AST::Annotations::Override.new(tree, comments)
|
328
|
+
when tokenizer.type?(:kUSE)
|
329
|
+
tree << parse_use(tokenizer)
|
330
|
+
AST::Annotations::Use.new(tree, comments)
|
331
|
+
when tokenizer.type?(:kMODULESELF)
|
332
|
+
tree << parse_module_self(tokenizer)
|
333
|
+
AST::Annotations::ModuleSelf.new(tree, comments)
|
334
|
+
when tokenizer.type?(:kGENERIC)
|
335
|
+
tree << parse_generic(tokenizer)
|
336
|
+
AST::Annotations::Generic.new(tree, comments)
|
337
|
+
when tokenizer.type?(:kSELF, :tATIDENT)
|
338
|
+
tree << parse_ivar_type(tokenizer)
|
339
|
+
AST::Annotations::IvarType.new(tree, comments)
|
340
|
+
when tokenizer.type?(:kYIELDS)
|
341
|
+
tree << parse_yields(tokenizer)
|
342
|
+
AST::Annotations::Yields.new(tree, comments)
|
343
|
+
end
|
344
|
+
when tokenizer.type?(:kCOLON2)
|
345
|
+
tree << tokenizer.current_token
|
346
|
+
tokenizer.advance(tree)
|
347
|
+
tree << parse_type_method_type(tokenizer, tree)
|
348
|
+
AST::Annotations::Assertion.new(tree, comments)
|
349
|
+
when tokenizer.type?(:kLBRACKET)
|
350
|
+
tree << parse_type_app(tokenizer)
|
351
|
+
AST::Annotations::Application.new(tree, comments)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# @rbs tokenizer: Tokenizer
|
356
|
+
# @rbs returns AST::Tree
|
357
|
+
def parse_var_decl(tokenizer)
|
358
|
+
tree = AST::Tree.new(:var_decl)
|
359
|
+
|
360
|
+
tokenizer.consume_token!(:tLVAR, :tELVAR, tree: tree)
|
361
|
+
|
362
|
+
if tokenizer.type?(:kCOLON)
|
363
|
+
tree << tokenizer.current_token
|
364
|
+
tokenizer.advance(tree)
|
365
|
+
else
|
366
|
+
tree << nil
|
367
|
+
end
|
368
|
+
|
369
|
+
tree << parse_type(tokenizer, tree)
|
370
|
+
|
371
|
+
if tokenizer.type?(:kMINUS2)
|
372
|
+
tree << parse_comment(tokenizer)
|
373
|
+
else
|
374
|
+
tree << nil
|
375
|
+
end
|
376
|
+
|
377
|
+
tree
|
378
|
+
end
|
379
|
+
|
380
|
+
# @rbs tokenizer: Tokenizer
|
381
|
+
# @rbs returns AST::Tree
|
382
|
+
def parse_return_type_decl(tokenizer)
|
383
|
+
tree = AST::Tree.new(:return_type_decl)
|
384
|
+
|
385
|
+
tokenizer.consume_token!(:kRETURNS, tree: tree)
|
386
|
+
tree << parse_type(tokenizer, tree)
|
387
|
+
tree << parse_optional(tokenizer, :kMINUS2) { parse_comment(tokenizer) }
|
388
|
+
|
389
|
+
tree
|
390
|
+
end
|
391
|
+
|
392
|
+
# @rbs tokenizer: Tokenizer
|
393
|
+
# @rbs returns AST::Tree
|
394
|
+
def parse_comment(tokenizer)
|
395
|
+
tree = AST::Tree.new(:comment)
|
396
|
+
|
397
|
+
tokenizer.type!(:kMINUS2)
|
398
|
+
|
399
|
+
tree << tokenizer.current_token
|
400
|
+
rest = tokenizer.scanner.rest || ""
|
401
|
+
tokenizer.scanner.terminate
|
402
|
+
tree << [:tCOMMENT, rest]
|
403
|
+
|
404
|
+
tree
|
405
|
+
end
|
406
|
+
|
407
|
+
# @rbs tokenizer: Tokenizer
|
408
|
+
# @rbs returns AST::Tree
|
409
|
+
def parse_type_app(tokenizer)
|
410
|
+
tree = AST::Tree.new(:tapp)
|
411
|
+
|
412
|
+
if tokenizer.type?(:kLBRACKET)
|
413
|
+
tree << tokenizer.current_token
|
414
|
+
tokenizer.advance(tree)
|
415
|
+
end
|
416
|
+
|
417
|
+
types = AST::Tree.new(:types)
|
418
|
+
while true
|
419
|
+
type = parse_type(tokenizer, types)
|
420
|
+
types << type
|
421
|
+
|
422
|
+
break unless type
|
423
|
+
break if type.is_a?(AST::Tree)
|
424
|
+
|
425
|
+
if tokenizer.type?(:kCOMMA)
|
426
|
+
types << tokenizer.current_token
|
427
|
+
tokenizer.advance(types)
|
428
|
+
end
|
429
|
+
|
430
|
+
if tokenizer.type?(:kRBRACKET)
|
431
|
+
break
|
432
|
+
end
|
433
|
+
end
|
434
|
+
tree << types
|
435
|
+
|
436
|
+
if tokenizer.type?(:kRBRACKET)
|
437
|
+
tree << tokenizer.current_token
|
438
|
+
tokenizer.advance(tree)
|
439
|
+
end
|
440
|
+
|
441
|
+
tree
|
442
|
+
end
|
443
|
+
|
444
|
+
# Parse a RBS method type or type and returns it
|
445
|
+
#
|
446
|
+
# It tries parsing a method type, and then parsing a type if failed.
|
447
|
+
#
|
448
|
+
# If both parsing failed, it returns a Tree(`:type_syntax_error), consuming all of the remaining input.
|
449
|
+
#
|
450
|
+
# Note that this doesn't recognize `--` comment unlike `parse_type`.
|
451
|
+
#
|
452
|
+
# @rbs tokenizer: Tokenizer
|
453
|
+
# @rbs parent_tree: AST::Tree
|
454
|
+
# @rbs returns MethodType | AST::Tree | Types::t | nil
|
455
|
+
def parse_type_method_type(tokenizer, parent_tree)
|
456
|
+
buffer = RBS::Buffer.new(name: "", content: tokenizer.scanner.string)
|
457
|
+
range = (tokenizer.scanner.charpos - (tokenizer.scanner.matched_size || 0) ..)
|
458
|
+
begin
|
459
|
+
if type = RBS::Parser.parse_method_type(buffer, range: range, require_eof: false)
|
460
|
+
loc = type.location or raise
|
461
|
+
size = loc.end_pos - loc.start_pos
|
462
|
+
(size - (tokenizer.scanner.matched_size || 0)).times do
|
463
|
+
tokenizer.scanner.skip(/./)
|
464
|
+
end
|
465
|
+
tokenizer.advance(parent_tree)
|
466
|
+
type
|
467
|
+
else
|
468
|
+
tokenizer.advance(parent_tree)
|
469
|
+
nil
|
470
|
+
end
|
471
|
+
rescue RBS::ParsingError
|
472
|
+
begin
|
473
|
+
if type = RBS::Parser.parse_type(buffer, range: range, require_eof: false)
|
474
|
+
loc = type.location or raise
|
475
|
+
size = loc.end_pos - loc.start_pos
|
476
|
+
(size - (tokenizer.scanner.matched_size || 0)).times do
|
477
|
+
tokenizer.scanner.skip(/./)
|
478
|
+
end
|
479
|
+
tokenizer.advance(parent_tree)
|
480
|
+
type
|
481
|
+
else
|
482
|
+
tokenizer.advance(parent_tree)
|
483
|
+
nil
|
484
|
+
end
|
485
|
+
rescue RBS::ParsingError
|
486
|
+
content = (tokenizer.scanner.matched || "") + (tokenizer.scanner.rest || "")
|
487
|
+
tree = AST::Tree.new(:type_syntax_error)
|
488
|
+
tree << [:tSOURCE, content]
|
489
|
+
tokenizer.scanner.terminate
|
490
|
+
tree
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
# Parse a RBS type and returns it
|
496
|
+
#
|
497
|
+
# If parsing failed, it returns a Tree(`:type_syntax_error), consuming
|
498
|
+
#
|
499
|
+
# 1. All of the input with `--` token if exists (for comments)
|
500
|
+
# 2. All of the input (for anything else)
|
501
|
+
#
|
502
|
+
# ```
|
503
|
+
# Integer -- Foo # => Returns `Integer`, tokenizer has `--` as its current token
|
504
|
+
# Integer[ -- Foo # => Returns a tree for `Integer[`, tokenizer has `--` as its curren token
|
505
|
+
# Integer[ Foo # => Returns a tree for `Integer[ Foo`, tokenizer is at the end of the input
|
506
|
+
# ```
|
507
|
+
#
|
508
|
+
# @rbs tokenizer: Tokenizer
|
509
|
+
# @rbs parent_tree: AST::Tree
|
510
|
+
# @rbs returns Types::t | AST::Tree | nil
|
511
|
+
def parse_type(tokenizer, parent_tree)
|
512
|
+
buffer = RBS::Buffer.new(name: "", content: tokenizer.scanner.string)
|
513
|
+
range = (tokenizer.scanner.charpos - (tokenizer.scanner.matched_size || 0) ..)
|
514
|
+
if type = RBS::Parser.parse_type(buffer, range: range, require_eof: false)
|
515
|
+
loc = type.location or raise
|
516
|
+
size = loc.end_pos - loc.start_pos
|
517
|
+
(size - (tokenizer.scanner.matched_size || 0)).times do
|
518
|
+
tokenizer.scanner.skip(/./)
|
519
|
+
end
|
520
|
+
tokenizer.advance(parent_tree)
|
521
|
+
type
|
522
|
+
else
|
523
|
+
tokenizer.advance(parent_tree)
|
524
|
+
nil
|
525
|
+
end
|
526
|
+
rescue RBS::ParsingError
|
527
|
+
content = tokenizer.skip_to_comment
|
528
|
+
tree = AST::Tree.new(:type_syntax_error)
|
529
|
+
tree << [:tSOURCE, content]
|
530
|
+
tree
|
531
|
+
end
|
532
|
+
|
533
|
+
# @rbs tokenizer: Tokenizer
|
534
|
+
# @rbs returns AST::Tree
|
535
|
+
def parse_rbs_annotation(tokenizer)
|
536
|
+
tree = AST::Tree.new(:rbs_annotation)
|
537
|
+
|
538
|
+
while tokenizer.type?(:tANNOTATION)
|
539
|
+
tree << tokenizer.current_token
|
540
|
+
tokenizer.advance(tree)
|
541
|
+
end
|
542
|
+
|
543
|
+
tree
|
544
|
+
end
|
545
|
+
|
546
|
+
# @rbs tokznier: Tokenizer
|
547
|
+
# @rbs returns AST::Tree
|
548
|
+
def parse_inherits(tokenizer)
|
549
|
+
tree = AST::Tree.new(:rbs_inherits)
|
550
|
+
|
551
|
+
if tokenizer.type?(:kINHERITS)
|
552
|
+
tree << tokenizer.current_token
|
553
|
+
tokenizer.advance(tree)
|
554
|
+
end
|
555
|
+
|
556
|
+
tree << parse_type(tokenizer, tree)
|
557
|
+
|
558
|
+
tree
|
559
|
+
end
|
560
|
+
|
561
|
+
# Parse `@rbs override` annotation
|
562
|
+
#
|
563
|
+
# @rbs tokenizer: Tokenizer
|
564
|
+
# @rbs returns AST::Tree
|
565
|
+
def parse_override(tokenizer)
|
566
|
+
tree = AST::Tree.new(:override)
|
567
|
+
|
568
|
+
if tokenizer.type?(:kOVERRIDE)
|
569
|
+
tree << tokenizer.current_token
|
570
|
+
tokenizer.advance(tree)
|
571
|
+
end
|
572
|
+
|
573
|
+
tree
|
574
|
+
end
|
575
|
+
|
576
|
+
# Parse `@rbs use [CLAUSES]` annotation
|
577
|
+
#
|
578
|
+
# @rbs tokenizer: Tokenizer
|
579
|
+
# @rbs returns AST::Tree
|
580
|
+
def parse_use(tokenizer)
|
581
|
+
tree = AST::Tree.new(:use)
|
582
|
+
|
583
|
+
if tokenizer.type?(:kUSE)
|
584
|
+
tree << tokenizer.current_token
|
585
|
+
tokenizer.advance(tree)
|
586
|
+
end
|
587
|
+
|
588
|
+
while tokenizer.type?(:kCOLON2, :tUIDENT, :tIFIDENT, :tLVAR)
|
589
|
+
tree << parse_use_clause(tokenizer)
|
590
|
+
|
591
|
+
if tokenizer.type?(:kCOMMA)
|
592
|
+
tree << tokenizer.advance(tree)
|
593
|
+
else
|
594
|
+
tree << nil
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
tree
|
599
|
+
end
|
600
|
+
|
601
|
+
# Parses use clause
|
602
|
+
#
|
603
|
+
# Returns one of the following form:
|
604
|
+
#
|
605
|
+
# * [`::`?, [UIDENT, `::`]*, LIDENT, [`as` LIDENT]?]
|
606
|
+
# * [`::`?, [UIDENT, `::`]*, UIDENT, [`as` UIDENT]?]
|
607
|
+
# * [`::`?, [UIDENT, `::`]*, IFIDENT, [`as`, IFIDENT]?]
|
608
|
+
# * [`::`?, [UIDENT) `::`]*, `*`]
|
609
|
+
#
|
610
|
+
# @rbs tokenizer: Tokenizer
|
611
|
+
# @rbs returns AST::Tree
|
612
|
+
def parse_use_clause(tokenizer)
|
613
|
+
tree = AST::Tree.new(:use_clause)
|
614
|
+
|
615
|
+
if tokenizer.type?(:kCOLON2)
|
616
|
+
tree << tokenizer.current_token
|
617
|
+
tokenizer.advance(tree)
|
618
|
+
end
|
619
|
+
|
620
|
+
while true
|
621
|
+
case
|
622
|
+
when tokenizer.type?(:tUIDENT)
|
623
|
+
tree << tokenizer.advance(tree)
|
624
|
+
|
625
|
+
case
|
626
|
+
when tokenizer.type?(:kCOLON2)
|
627
|
+
tree << tokenizer.advance(tree)
|
628
|
+
else
|
629
|
+
break
|
630
|
+
end
|
631
|
+
else
|
632
|
+
break
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
case
|
637
|
+
when tokenizer.type?(:tLVAR)
|
638
|
+
tree << tokenizer.advance(tree)
|
639
|
+
when tokenizer.type?(:tIFIDENT)
|
640
|
+
tree << tokenizer.advance(tree)
|
641
|
+
when tokenizer.type?(:kSTAR)
|
642
|
+
tree << tokenizer.advance(tree)
|
643
|
+
return tree
|
644
|
+
end
|
645
|
+
|
646
|
+
if tokenizer.type?(:kAS)
|
647
|
+
as_tree = AST::Tree.new(:as)
|
648
|
+
|
649
|
+
tokenizer.consume_token!(:kAS, tree: as_tree)
|
650
|
+
tokenizer.consume_token(:tLVAR, :tIFIDENT, :tUIDENT, tree: as_tree)
|
651
|
+
|
652
|
+
tree << as_tree
|
653
|
+
else
|
654
|
+
tree << nil
|
655
|
+
end
|
656
|
+
|
657
|
+
tree
|
658
|
+
end
|
659
|
+
|
660
|
+
# @rbs tokenizer: Tokenizer
|
661
|
+
# @rbs returns AST::Tree
|
662
|
+
def parse_module_self(tokenizer)
|
663
|
+
tree = AST::Tree.new(:module_self)
|
664
|
+
|
665
|
+
tokenizer.consume_token!(:kMODULESELF, tree: tree)
|
666
|
+
tree << parse_type(tokenizer, tree)
|
667
|
+
|
668
|
+
if tokenizer.type?(:kMINUS2)
|
669
|
+
tree << parse_comment(tokenizer)
|
670
|
+
else
|
671
|
+
tree << nil
|
672
|
+
end
|
673
|
+
|
674
|
+
tree
|
675
|
+
end
|
676
|
+
|
677
|
+
# Yield the block and return the resulting tree if tokenizer has current token of `types`
|
678
|
+
#
|
679
|
+
# ```rb
|
680
|
+
# # Test if tokenize has `--` token, then parse comment or insert `nil` to tree
|
681
|
+
#
|
682
|
+
# tree << parse_optional(tokenizer, :kMINUS2) do
|
683
|
+
# parse_comment(tokenizer)
|
684
|
+
# end
|
685
|
+
# ```
|
686
|
+
#
|
687
|
+
# @rbs tokenizer: Tokenizer
|
688
|
+
# @rbs types: Array[Symbol]
|
689
|
+
# @rbs block: ^() -> AST::Tree
|
690
|
+
# @rbs returns AST::Tree?
|
691
|
+
def parse_optional(tokenizer, *types, &block)
|
692
|
+
if tokenizer.type?(*types)
|
693
|
+
yield
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
# @rbs tokenizer: Tokenizer
|
698
|
+
# @rbs returns AST::Tree
|
699
|
+
def parse_generic(tokenizer)
|
700
|
+
tree = AST::Tree.new(:generic)
|
701
|
+
|
702
|
+
tokenizer.consume_token!(:kGENERIC, tree: tree)
|
703
|
+
|
704
|
+
tokenizer.consume_token(:kUNCHECKED, tree: tree)
|
705
|
+
tokenizer.consume_token(:kIN, :kOUT, tree: tree)
|
706
|
+
|
707
|
+
tokenizer.consume_token(:tUIDENT, tree: tree)
|
708
|
+
|
709
|
+
tree << parse_optional(tokenizer, :kLT) do
|
710
|
+
bound = AST::Tree.new(:upper_bound)
|
711
|
+
|
712
|
+
tokenizer.consume_token!(:kLT, tree: bound)
|
713
|
+
bound << parse_type(tokenizer, bound)
|
714
|
+
|
715
|
+
bound
|
716
|
+
end
|
717
|
+
|
718
|
+
tree << parse_optional(tokenizer, :kMINUS2) do
|
719
|
+
parse_comment(tokenizer)
|
720
|
+
end
|
721
|
+
|
722
|
+
tree
|
723
|
+
end
|
724
|
+
|
725
|
+
#:: (Tokenizer) -> AST::Tree
|
726
|
+
def parse_ivar_type(tokenizer)
|
727
|
+
tree = AST::Tree.new(:ivar_type)
|
728
|
+
|
729
|
+
tokenizer.consume_token(:kSELF, tree: tree)
|
730
|
+
tokenizer.consume_token(:kDOT, tree: tree)
|
731
|
+
|
732
|
+
tokenizer.consume_token(:tATIDENT, tree: tree)
|
733
|
+
tokenizer.consume_token(:kCOLON, tree: tree)
|
734
|
+
|
735
|
+
tree << parse_type(tokenizer, tree)
|
736
|
+
|
737
|
+
tree << parse_optional(tokenizer, :kMINUS2) do
|
738
|
+
parse_comment(tokenizer)
|
739
|
+
end
|
740
|
+
|
741
|
+
tree
|
742
|
+
end
|
743
|
+
|
744
|
+
#:: (Tokenizer) -> AST::Tree
|
745
|
+
def parse_yields(tokenizer)
|
746
|
+
tree = AST::Tree.new(:yields)
|
747
|
+
|
748
|
+
tokenizer.consume_token!(:kYIELDS, tree: tree)
|
749
|
+
tokenizer.consume_token(:kOPTIONAL, tree: tree)
|
750
|
+
|
751
|
+
unless (string = tokenizer.skip_to_comment()).empty?
|
752
|
+
tree << [:tBLOCKSTR, string]
|
753
|
+
else
|
754
|
+
tree << nil
|
755
|
+
end
|
756
|
+
|
757
|
+
tree << parse_optional(tokenizer, :kMINUS2) do
|
758
|
+
parse_comment(tokenizer)
|
759
|
+
end
|
760
|
+
|
761
|
+
tree
|
762
|
+
end
|
763
|
+
end
|
764
|
+
end
|
765
|
+
end
|