rbs-inline 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -7
- data/Rakefile +12 -0
- data/lib/rbs/inline/annotation_parser/tokenizer.rb +361 -0
- data/lib/rbs/inline/annotation_parser.rb +548 -326
- data/lib/rbs/inline/ast/annotations.rb +446 -136
- data/lib/rbs/inline/ast/comment_lines.rb +32 -18
- data/lib/rbs/inline/ast/declarations.rb +67 -28
- data/lib/rbs/inline/ast/members.rb +137 -140
- data/lib/rbs/inline/ast/tree.rb +104 -5
- data/lib/rbs/inline/cli.rb +12 -12
- data/lib/rbs/inline/node_utils.rb +4 -0
- data/lib/rbs/inline/parser.rb +140 -59
- data/lib/rbs/inline/version.rb +1 -1
- data/lib/rbs/inline/writer.rb +243 -94
- data/lib/rbs/inline.rb +4 -0
- data/rbs_collection.lock.yaml +3 -7
- data/rbs_collection.yaml +2 -0
- data/sig/generated/rbs/inline/annotation_parser/tokenizer.rbs +221 -0
- data/sig/generated/rbs/inline/annotation_parser.rbs +148 -92
- data/sig/generated/rbs/inline/ast/annotations.rbs +142 -36
- data/sig/generated/rbs/inline/ast/comment_lines.rbs +35 -0
- data/sig/generated/rbs/inline/ast/declarations.rbs +29 -10
- data/sig/generated/rbs/inline/ast/members.rbs +33 -24
- data/sig/generated/rbs/inline/ast/tree.rbs +132 -0
- data/sig/generated/rbs/inline/cli.rbs +3 -3
- data/sig/generated/rbs/inline/node_utils.rbs +11 -0
- data/sig/generated/rbs/inline/parser.rbs +38 -18
- data/sig/generated/rbs/inline/version.rbs +7 -0
- data/sig/generated/rbs/inline/writer.rbs +104 -0
- data/sig/generated/rbs/inline.rbs +7 -0
- metadata +14 -14
- data/sig/rbs/inline/annotation_parser.rbs +0 -0
- data/sig/rbs/inline/ast/comment_lines.rbs +0 -27
- data/sig/rbs/inline/ast/tree.rbs +0 -98
- data/sig/rbs/inline/node_utils.rbs +0 -7
- data/sig/rbs/inline/writer.rbs +0 -27
- data/sig/rbs/inline.rbs +0 -41
- data/yard-samples/hello.rb +0 -6
- data/yard-samples/sample1.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c6ea86390a9ce51331956c34fb2eedc77ae45c4ac0fc7b39d67f2cacb2a5af0
|
4
|
+
data.tar.gz: 9e1446ab0920edec03ed54a1a07c8868e3dac9529813c389ed35f85f0380e7c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 793f9e99431ac027e1ccf6d3b2b31ead740b9227584aa5be5f751cd1eafea85ce8db37e070b5dfcde28139f629d2c2a499aa5e2d507fcbe53c7fd8a3e9cf2377
|
7
|
+
data.tar.gz: 3f74d9a9a96189fcd21c470aa7b97949fa625dbb46b218c6ae993b049993947d7a27840755af43626702aa6b55cebc1a25d70830343c6f954bcb2694f30fc34b
|
data/README.md
CHANGED
@@ -8,29 +8,32 @@ RBS::Inline allows embedding RBS type declarations into Ruby code as comments. Y
|
|
8
8
|
> [!IMPORTANT]
|
9
9
|
> This gem is a prototype for testing. We plan to merge this feature to rbs-gem and deprecate rbs-inline gem after that.
|
10
10
|
|
11
|
+
> [!NOTE]
|
12
|
+
> Use Steep >= `1.8.0.dev` to avoid the conflicts on `#:` syntax.
|
13
|
+
|
11
14
|
Here is a quick example of embedded declarations.
|
12
15
|
|
13
16
|
```rb
|
14
17
|
# rbs_inline: enabled
|
15
18
|
|
16
19
|
class Person
|
17
|
-
attr_reader :name
|
20
|
+
attr_reader :name #: String
|
18
21
|
|
19
|
-
attr_reader :addresses
|
22
|
+
attr_reader :addresses #: Array[String]
|
20
23
|
|
21
24
|
# @rbs name: String
|
22
25
|
# @rbs addresses: Array[String]
|
23
|
-
# @rbs
|
26
|
+
# @rbs return: void
|
24
27
|
def initialize(name:, addresses:)
|
25
28
|
@name = name
|
26
29
|
@addresses = addresses
|
27
30
|
end
|
28
31
|
|
29
|
-
def to_s
|
32
|
+
def to_s #: String
|
30
33
|
"Person(name = #{name}, addresses = #{addresses.join(", ")})"
|
31
34
|
end
|
32
35
|
|
33
|
-
# @rbs
|
36
|
+
# @rbs &block: (String) -> void
|
34
37
|
def each_address(&block) #:: void
|
35
38
|
addresses.each(&block)
|
36
39
|
end
|
@@ -80,12 +83,12 @@ The gem works as a transpiler from annotated Ruby code to RBS files. Run `rbs-in
|
|
80
83
|
$ bundle exec rbs-inline lib
|
81
84
|
|
82
85
|
# Save generated RBS files under sig/generated
|
83
|
-
$ bundle exec rbs-inline --output
|
86
|
+
$ bundle exec rbs-inline --output lib
|
84
87
|
```
|
85
88
|
|
86
89
|
You may want to use `fswatch` or likes to automatically generate RBS files when you edit the Ruby code.
|
87
90
|
|
88
|
-
$ fswatch -0 lib | xargs -0 -n1 bundle exec rbs-inline --output
|
91
|
+
$ fswatch -0 lib | xargs -0 -n1 bundle exec rbs-inline --output
|
89
92
|
|
90
93
|
## More materials
|
91
94
|
|
data/Rakefile
CHANGED
@@ -10,3 +10,15 @@ Rake::TestTask.new(:test) do |t|
|
|
10
10
|
end
|
11
11
|
|
12
12
|
task default: :test
|
13
|
+
|
14
|
+
namespace :rbs do
|
15
|
+
task :generate do
|
16
|
+
sh "rbs-inline --opt-out --output lib"
|
17
|
+
end
|
18
|
+
|
19
|
+
task :watch do
|
20
|
+
sh "fswatch -0 lib | xargs -n1 -0 rbs-inline --opt-out --output lib"
|
21
|
+
rescue Interrupt
|
22
|
+
# nop
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,361 @@
|
|
1
|
+
module RBS
|
2
|
+
module Inline
|
3
|
+
class AnnotationParser
|
4
|
+
module Tokens
|
5
|
+
K_RETURN = :kRETURN
|
6
|
+
K_INHERITS = :kINHERITS
|
7
|
+
K_AS = :kAS
|
8
|
+
K_OVERRIDE = :kOVERRIDE
|
9
|
+
K_USE = :kUSE
|
10
|
+
K_MODULE_SELF = :kMODULESELF
|
11
|
+
K_GENERIC = :kGENERIC
|
12
|
+
K_IN = :kIN
|
13
|
+
K_OUT = :kOUT
|
14
|
+
K_UNCHECKED = :kUNCHECKED
|
15
|
+
K_SELF = :kSELF
|
16
|
+
K_SKIP = :kSKIP
|
17
|
+
K_YIELDS = :kYIELDS
|
18
|
+
K_MODULE = :kMODULE
|
19
|
+
K_CLASS = :kCLASS
|
20
|
+
K_COLON2 = :kCOLON2
|
21
|
+
K_COLON = :kCOLON
|
22
|
+
K_LBRACKET = :kLBRACKET
|
23
|
+
K_RBRACKET = :kRBRACKET
|
24
|
+
K_COMMA = :kCOMMA
|
25
|
+
K_STAR2 = :kSTAR2
|
26
|
+
K_STAR = :kSTAR
|
27
|
+
K_MINUS2 = :kMINUS2
|
28
|
+
K_LT = :kLT
|
29
|
+
K_DOT3 = :kDOT3
|
30
|
+
K_DOT = :kDOT
|
31
|
+
K_ARROW = :kARROW
|
32
|
+
K_LBRACE = :kLBRACE
|
33
|
+
K_LPAREN = :kLPAREN
|
34
|
+
K_AMP = :kAMP
|
35
|
+
K_QUESTION = :kQUESTION
|
36
|
+
K_VBAR = :kVBAR
|
37
|
+
|
38
|
+
K_EOF = :kEOF
|
39
|
+
|
40
|
+
# `@rbs!`
|
41
|
+
K_RBSE = :kRBSE
|
42
|
+
|
43
|
+
# `@rbs`
|
44
|
+
K_RBS = :kRBS
|
45
|
+
|
46
|
+
T_UIDENT = :tUIDENT
|
47
|
+
T_IFIDENT = :tIFIDENT
|
48
|
+
T_LVAR = :tLVAR
|
49
|
+
|
50
|
+
# The body of comment string following `--`
|
51
|
+
T_COMMENT = :tCOMMENT
|
52
|
+
|
53
|
+
# Type/method type source
|
54
|
+
T_SOURCE = :tSOURCE
|
55
|
+
|
56
|
+
# Block type source
|
57
|
+
T_BLOCKSTR = :tBLOCKSTR
|
58
|
+
|
59
|
+
# `!` local variable
|
60
|
+
T_ELVAR = :tELVAR
|
61
|
+
|
62
|
+
T_ATIDENT = :tATIDENT
|
63
|
+
T_ANNOTATION = :tANNOTATION
|
64
|
+
T_WHITESPACE = :tWHITESPACE
|
65
|
+
end
|
66
|
+
|
67
|
+
class Tokenizer
|
68
|
+
include Tokens
|
69
|
+
|
70
|
+
KEYWORDS = {
|
71
|
+
"return" => K_RETURN,
|
72
|
+
"inherits" => K_INHERITS,
|
73
|
+
"as" => K_AS,
|
74
|
+
"override" => K_OVERRIDE,
|
75
|
+
"use" => K_USE,
|
76
|
+
"module-self" => K_MODULE_SELF,
|
77
|
+
"generic" => K_GENERIC,
|
78
|
+
"in" => K_IN,
|
79
|
+
"out" => K_OUT,
|
80
|
+
"unchecked" => K_UNCHECKED,
|
81
|
+
"self" => K_SELF,
|
82
|
+
"skip" => K_SKIP,
|
83
|
+
"yields" => K_YIELDS,
|
84
|
+
"module" => K_MODULE,
|
85
|
+
"class" => K_CLASS,
|
86
|
+
} #: Hash[String, Symbol]
|
87
|
+
KW_RE = /#{Regexp.union(KEYWORDS.keys)}\b/
|
88
|
+
|
89
|
+
PUNCTS = {
|
90
|
+
"::" => K_COLON2,
|
91
|
+
":" => K_COLON,
|
92
|
+
"[" => K_LBRACKET,
|
93
|
+
"]" => K_RBRACKET,
|
94
|
+
"," => K_COMMA,
|
95
|
+
"**" => K_STAR2,
|
96
|
+
"*" => K_STAR,
|
97
|
+
"--" => K_MINUS2,
|
98
|
+
"<" => K_LT,
|
99
|
+
"..." => K_DOT3,
|
100
|
+
"." => K_DOT,
|
101
|
+
"->" => K_ARROW,
|
102
|
+
"{" => K_LBRACE,
|
103
|
+
"(" => K_LPAREN,
|
104
|
+
"&" => K_AMP,
|
105
|
+
"?" => K_QUESTION,
|
106
|
+
"|" => K_VBAR,
|
107
|
+
} #: Hash[String, Symbol]
|
108
|
+
PUNCTS_RE = Regexp.union(PUNCTS.keys) #: Regexp
|
109
|
+
|
110
|
+
attr_reader :scanner #: StringScanner
|
111
|
+
|
112
|
+
# Tokens that comes after the current position
|
113
|
+
#
|
114
|
+
# This is a four tuple of tokens.
|
115
|
+
#
|
116
|
+
# 1. The first array is a trivia tokens before current position
|
117
|
+
# 2. The second token is the first lookahead token after the current position
|
118
|
+
# 3. The third array is a trivia tokens between the first lookahead and the second lookahead
|
119
|
+
# 4. The fourth token is the second lookahead token
|
120
|
+
#
|
121
|
+
attr_reader :lookahead_tokens #: [Array[token], token?, Array[token], token?]
|
122
|
+
|
123
|
+
# Token that comes after the current position
|
124
|
+
# @rbs %a{pure}
|
125
|
+
def lookahead1 #: token?
|
126
|
+
lookahead_tokens[1]
|
127
|
+
end
|
128
|
+
|
129
|
+
# Token that comes after `lookahead1`
|
130
|
+
# @rbs %a{pure}
|
131
|
+
def lookahead2 #: token?
|
132
|
+
lookahead_tokens[3]
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the current char position of the first lookahead token
|
136
|
+
#
|
137
|
+
# ```
|
138
|
+
# __ foo ___ bar baz
|
139
|
+
# ^^ Trivia tokens before lookahead1
|
140
|
+
# ^ #current_position
|
141
|
+
# ^^^ lookahead1
|
142
|
+
# ^^^ Trivia tokens between lookahead1 and lookahead2
|
143
|
+
# ^^^ lookahead2
|
144
|
+
# ^ <= scanner.charpos
|
145
|
+
# ```
|
146
|
+
#
|
147
|
+
def current_position #: Integer
|
148
|
+
start = scanner.charpos
|
149
|
+
start -= lookahead1[1].size if lookahead1
|
150
|
+
lookahead_tokens[2].each {|_, s| start -= s.size }
|
151
|
+
start -= lookahead2[1].size if lookahead2
|
152
|
+
start
|
153
|
+
end
|
154
|
+
|
155
|
+
def lookaheads #: Array[Symbol?]
|
156
|
+
[lookahead1&.[](0), lookahead2&.[](0)]
|
157
|
+
end
|
158
|
+
|
159
|
+
# @rbs scanner: StringScanner
|
160
|
+
# @rbs return: void
|
161
|
+
def initialize(scanner)
|
162
|
+
@scanner = scanner
|
163
|
+
|
164
|
+
@lookahead_tokens = [[], nil, [], nil]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Advances the scanner
|
168
|
+
#
|
169
|
+
# @rbs tree: AST::Tree -- Tree to insert trivia tokens
|
170
|
+
# @rbs eat: bool -- true to add the current lookahead token into the tree
|
171
|
+
# @rbs return: void
|
172
|
+
def advance(tree, eat: false)
|
173
|
+
consume_trivias(tree)
|
174
|
+
last = lookahead_tokens[1]
|
175
|
+
tree << last if eat
|
176
|
+
|
177
|
+
lookahead_tokens[0].replace(lookahead_tokens[2])
|
178
|
+
lookahead_tokens[1] = lookahead_tokens[3]
|
179
|
+
lookahead_tokens[2].clear
|
180
|
+
|
181
|
+
while s = scanner.scan(/\s+/)
|
182
|
+
lookahead_tokens[2] << [T_WHITESPACE, s]
|
183
|
+
end
|
184
|
+
|
185
|
+
lookahead =
|
186
|
+
case
|
187
|
+
when scanner.eos?
|
188
|
+
[K_EOF, ""]
|
189
|
+
when s = scanner.scan(/@rbs!/)
|
190
|
+
[K_RBSE, s]
|
191
|
+
when s = scanner.scan(/@rbs\b/)
|
192
|
+
[K_RBS, s]
|
193
|
+
when s = scanner.scan(PUNCTS_RE)
|
194
|
+
[PUNCTS.fetch(s), s]
|
195
|
+
when s = scanner.scan(KW_RE)
|
196
|
+
[KEYWORDS.fetch(s), s]
|
197
|
+
when s = scanner.scan(/[A-Z]\w*/)
|
198
|
+
[T_UIDENT, s]
|
199
|
+
when s = scanner.scan(/_[A-Z]\w*/)
|
200
|
+
[T_IFIDENT, s]
|
201
|
+
when s = scanner.scan(/[a-z]\w*/)
|
202
|
+
[T_LVAR, s]
|
203
|
+
when s = scanner.scan(/![a-z]\w*/)
|
204
|
+
[T_ELVAR, s]
|
205
|
+
when s = scanner.scan(/@\w+/)
|
206
|
+
[T_ATIDENT, s]
|
207
|
+
when s = scanner.scan(/%a\{[^}]+\}/)
|
208
|
+
[T_ANNOTATION, s]
|
209
|
+
when s = scanner.scan(/%a\[[^\]]+\]/)
|
210
|
+
[T_ANNOTATION, s]
|
211
|
+
when s = scanner.scan(/%a\([^)]+\)/)
|
212
|
+
[T_ANNOTATION, s]
|
213
|
+
end #: token?
|
214
|
+
|
215
|
+
lookahead_tokens[3] = lookahead
|
216
|
+
|
217
|
+
last
|
218
|
+
end
|
219
|
+
|
220
|
+
# @rbs (AST::Tree?) -> String
|
221
|
+
def consume_trivias(tree)
|
222
|
+
buf = +""
|
223
|
+
|
224
|
+
lookahead_tokens[0].each do |tok|
|
225
|
+
tree << tok if tree
|
226
|
+
buf << tok[1]
|
227
|
+
end
|
228
|
+
lookahead_tokens[0].clear
|
229
|
+
|
230
|
+
buf
|
231
|
+
end
|
232
|
+
|
233
|
+
# Returns true if the scanner cannot consume next token
|
234
|
+
def stuck? #: bool
|
235
|
+
lookahead1.nil? && lookahead2.nil?
|
236
|
+
end
|
237
|
+
|
238
|
+
# Skips characters
|
239
|
+
#
|
240
|
+
# This method ensures the `current_position` will be the given `position`.
|
241
|
+
#
|
242
|
+
# @rbs position: Integer -- The new position
|
243
|
+
# @rbs tree: AST::Tree -- Tree to insert trivia tokens
|
244
|
+
# @rbs return: void
|
245
|
+
def reset(position, tree)
|
246
|
+
if scanner.charpos > position
|
247
|
+
scanner.reset()
|
248
|
+
end
|
249
|
+
|
250
|
+
skips = position - scanner.charpos
|
251
|
+
|
252
|
+
if scanner.rest_size < skips
|
253
|
+
raise "The position is bigger than the size of the rest of the input: input size=#{scanner.string.size}, position=#{position}"
|
254
|
+
end
|
255
|
+
|
256
|
+
scanner.skip(/.{#{skips}}/)
|
257
|
+
|
258
|
+
@lookahead_tokens = [[], nil, [], nil]
|
259
|
+
|
260
|
+
advance(tree)
|
261
|
+
advance(tree)
|
262
|
+
end
|
263
|
+
|
264
|
+
def rest #: String
|
265
|
+
buf = +""
|
266
|
+
lookahead_tokens[0].each {|_, s| buf << s }
|
267
|
+
buf << lookahead1[1] if lookahead1
|
268
|
+
lookahead_tokens[2].each {|_, s| buf << s }
|
269
|
+
buf << lookahead2[1] if lookahead2
|
270
|
+
buf << scanner.rest
|
271
|
+
buf
|
272
|
+
end
|
273
|
+
|
274
|
+
# Consume given token type and inserts the token to the tree or `nil`
|
275
|
+
#
|
276
|
+
# @rbs *types: Symbol
|
277
|
+
# @rbs tree: AST::Tree
|
278
|
+
# @rbs return: void
|
279
|
+
def consume_token(*types, tree:)
|
280
|
+
if type?(*types)
|
281
|
+
advance(tree, eat: true)
|
282
|
+
else
|
283
|
+
tree << nil
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Consume given token type and inserts the token to the tree or raise
|
288
|
+
#
|
289
|
+
# @rbs *types: Symbol
|
290
|
+
# @rbs tree: AST::Tree
|
291
|
+
# @rbs return: void
|
292
|
+
def consume_token!(*types, tree:)
|
293
|
+
type!(*types)
|
294
|
+
advance(tree, eat: true)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Test if current token has specified `type`
|
298
|
+
#
|
299
|
+
# @rbs *types: Symbol
|
300
|
+
# @rbs return: bool
|
301
|
+
def type?(*types)
|
302
|
+
types.any? { lookahead1 && lookahead1[0] == _1 }
|
303
|
+
end
|
304
|
+
|
305
|
+
# Test if lookahead2 token have specified `type`
|
306
|
+
#
|
307
|
+
# @rbs *types: Symbol -- The type of the lookahead2 token
|
308
|
+
# @rbs return: bool
|
309
|
+
def type2?(*types)
|
310
|
+
types.any? { lookahead2 && lookahead2[0] == _1 }
|
311
|
+
end
|
312
|
+
|
313
|
+
# Ensure current token is one of the specified in types
|
314
|
+
#
|
315
|
+
# @rbs *types: Symbol
|
316
|
+
# @rbs return: void
|
317
|
+
def type!(*types)
|
318
|
+
raise "Unexpected token: #{lookahead1&.[](0)}, where expected token: #{types.join(",")}" unless type?(*types)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Reset the current_token to incoming comment `--`
|
322
|
+
#
|
323
|
+
# Reset to the end of the input if `--` token cannot be found.
|
324
|
+
#
|
325
|
+
# @rbs return: String -- String that is skipped
|
326
|
+
def skip_to_comment
|
327
|
+
prefix = +""
|
328
|
+
|
329
|
+
lookahead_tokens[0].each { prefix << _1[1] }
|
330
|
+
lookahead_tokens[0].clear
|
331
|
+
|
332
|
+
if type?(K_MINUS2)
|
333
|
+
return prefix
|
334
|
+
end
|
335
|
+
|
336
|
+
prefix << lookahead1[1] if lookahead1
|
337
|
+
lookahead_tokens[2].each { prefix << _1[1] }
|
338
|
+
lookahead_tokens[2].clear
|
339
|
+
|
340
|
+
if type2?(K_MINUS2)
|
341
|
+
advance(_ = nil) # The tree is unused because no trivia tokens are left
|
342
|
+
return prefix
|
343
|
+
end
|
344
|
+
|
345
|
+
prefix << lookahead2[1] if lookahead2
|
346
|
+
|
347
|
+
if string = scanner.scan_until(/--/)
|
348
|
+
@lookahead_tokens = [[], nil, [], [K_MINUS2, "--"]]
|
349
|
+
advance(_ = nil) # The tree is unused because no trivia tokens are left
|
350
|
+
prefix + string.delete_suffix("--")
|
351
|
+
else
|
352
|
+
s = scanner.rest
|
353
|
+
@lookahead_tokens = [[], [K_EOF, ""], [], nil]
|
354
|
+
scanner.terminate
|
355
|
+
prefix + s
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|