rbs-inline 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 713b2bd4c5ddd833fa1bd1a71505924081013101a8a41200a88fed55751146ba
4
- data.tar.gz: 44ffe7a91c0c0afeb7e163a02de508c50f96a609ae7c64593a6b739109ac11b0
3
+ metadata.gz: 3c6ea86390a9ce51331956c34fb2eedc77ae45c4ac0fc7b39d67f2cacb2a5af0
4
+ data.tar.gz: 9e1446ab0920edec03ed54a1a07c8868e3dac9529813c389ed35f85f0380e7c9
5
5
  SHA512:
6
- metadata.gz: 6507f727be79bcae3890c8b8c3c3865544022058e44d5bfd060efb5df0c91e50f2b5e43a11f284c7ea3de1066bdd0ed7e269a14c20b45cc05cafa7ad60ecffb7
7
- data.tar.gz: aad87cafa19657b4d4b055527924146cb232cd96c56081bcbb24c1e1b0ff2fc24a449bdfe2132a43c2e5f5ac2affca8d615d0946b135676979556eaca4887aec
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 #:: String
20
+ attr_reader :name #: String
18
21
 
19
- attr_reader :addresses #:: Array[String]
22
+ attr_reader :addresses #: Array[String]
20
23
 
21
24
  # @rbs name: String
22
25
  # @rbs addresses: Array[String]
23
- # @rbs returns void
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 #:: String
32
+ def to_s #: String
30
33
  "Person(name = #{name}, addresses = #{addresses.join(", ")})"
31
34
  end
32
35
 
33
- # @rbs yields (String) -> void
36
+ # @rbs &block: (String) -> void
34
37
  def each_address(&block) #:: void
35
38
  addresses.each(&block)
36
39
  end
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