voloko-sdoc 0.1.7 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/LICENSE +21 -0
  2. data/VERSION.yml +4 -0
  3. data/lib/sdoc/generator/shtml.rb +1 -4
  4. data/lib/sdoc.rb +0 -1
  5. metadata +32 -101
  6. data/rdoc/History.txt +0 -254
  7. data/rdoc/Manifest.txt +0 -126
  8. data/rdoc/README.txt +0 -47
  9. data/rdoc/RI.txt +0 -58
  10. data/rdoc/Rakefile +0 -70
  11. data/rdoc/bin/rdoc +0 -35
  12. data/rdoc/bin/ri +0 -5
  13. data/rdoc/lib/rdoc/alias.rb +0 -54
  14. data/rdoc/lib/rdoc/anon_class.rb +0 -10
  15. data/rdoc/lib/rdoc/any_method.rb +0 -190
  16. data/rdoc/lib/rdoc/attr.rb +0 -79
  17. data/rdoc/lib/rdoc/cache.rb +0 -41
  18. data/rdoc/lib/rdoc/class_module.rb +0 -87
  19. data/rdoc/lib/rdoc/code_object.rb +0 -152
  20. data/rdoc/lib/rdoc/code_objects.rb +0 -23
  21. data/rdoc/lib/rdoc/constant.rb +0 -36
  22. data/rdoc/lib/rdoc/context.rb +0 -712
  23. data/rdoc/lib/rdoc/diagram.rb +0 -340
  24. data/rdoc/lib/rdoc/dot.rb +0 -249
  25. data/rdoc/lib/rdoc/generator/darkfish.rb +0 -455
  26. data/rdoc/lib/rdoc/generator/markup.rb +0 -194
  27. data/rdoc/lib/rdoc/generator/ri.rb +0 -230
  28. data/rdoc/lib/rdoc/generator/template/darkfish/classpage.rhtml +0 -281
  29. data/rdoc/lib/rdoc/generator/template/darkfish/filepage.rhtml +0 -112
  30. data/rdoc/lib/rdoc/generator/template/darkfish/images/brick.png +0 -0
  31. data/rdoc/lib/rdoc/generator/template/darkfish/images/brick_link.png +0 -0
  32. data/rdoc/lib/rdoc/generator/template/darkfish/images/bug.png +0 -0
  33. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_black.png +0 -0
  34. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png +0 -0
  35. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png +0 -0
  36. data/rdoc/lib/rdoc/generator/template/darkfish/images/date.png +0 -0
  37. data/rdoc/lib/rdoc/generator/template/darkfish/images/find.png +0 -0
  38. data/rdoc/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif +0 -0
  39. data/rdoc/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png +0 -0
  40. data/rdoc/lib/rdoc/generator/template/darkfish/images/package.png +0 -0
  41. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_green.png +0 -0
  42. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_white_text.png +0 -0
  43. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_white_width.png +0 -0
  44. data/rdoc/lib/rdoc/generator/template/darkfish/images/plugin.png +0 -0
  45. data/rdoc/lib/rdoc/generator/template/darkfish/images/ruby.png +0 -0
  46. data/rdoc/lib/rdoc/generator/template/darkfish/images/tag_green.png +0 -0
  47. data/rdoc/lib/rdoc/generator/template/darkfish/images/wrench.png +0 -0
  48. data/rdoc/lib/rdoc/generator/template/darkfish/images/wrench_orange.png +0 -0
  49. data/rdoc/lib/rdoc/generator/template/darkfish/images/zoom.png +0 -0
  50. data/rdoc/lib/rdoc/generator/template/darkfish/index.rhtml +0 -64
  51. data/rdoc/lib/rdoc/generator/template/darkfish/js/darkfish.js +0 -116
  52. data/rdoc/lib/rdoc/generator/template/darkfish/js/jquery.js +0 -32
  53. data/rdoc/lib/rdoc/generator/template/darkfish/js/quicksearch.js +0 -114
  54. data/rdoc/lib/rdoc/generator/template/darkfish/js/thickbox-compressed.js +0 -10
  55. data/rdoc/lib/rdoc/generator/template/darkfish/rdoc.css +0 -696
  56. data/rdoc/lib/rdoc/generator.rb +0 -8
  57. data/rdoc/lib/rdoc/ghost_method.rb +0 -8
  58. data/rdoc/lib/rdoc/include.rb +0 -39
  59. data/rdoc/lib/rdoc/known_classes.rb +0 -68
  60. data/rdoc/lib/rdoc/markup/attribute_manager.rb +0 -311
  61. data/rdoc/lib/rdoc/markup/formatter.rb +0 -25
  62. data/rdoc/lib/rdoc/markup/fragments.rb +0 -377
  63. data/rdoc/lib/rdoc/markup/inline.rb +0 -126
  64. data/rdoc/lib/rdoc/markup/lines.rb +0 -156
  65. data/rdoc/lib/rdoc/markup/preprocess.rb +0 -80
  66. data/rdoc/lib/rdoc/markup/to_flow.rb +0 -211
  67. data/rdoc/lib/rdoc/markup/to_html.rb +0 -406
  68. data/rdoc/lib/rdoc/markup/to_html_crossref.rb +0 -140
  69. data/rdoc/lib/rdoc/markup/to_latex.rb +0 -328
  70. data/rdoc/lib/rdoc/markup/to_test.rb +0 -53
  71. data/rdoc/lib/rdoc/markup/to_texinfo.rb +0 -73
  72. data/rdoc/lib/rdoc/markup.rb +0 -378
  73. data/rdoc/lib/rdoc/meta_method.rb +0 -8
  74. data/rdoc/lib/rdoc/normal_class.rb +0 -18
  75. data/rdoc/lib/rdoc/normal_module.rb +0 -34
  76. data/rdoc/lib/rdoc/options.rb +0 -542
  77. data/rdoc/lib/rdoc/parser/c.rb +0 -678
  78. data/rdoc/lib/rdoc/parser/perl.rb +0 -165
  79. data/rdoc/lib/rdoc/parser/ruby.rb +0 -2904
  80. data/rdoc/lib/rdoc/parser/simple.rb +0 -39
  81. data/rdoc/lib/rdoc/parser.rb +0 -138
  82. data/rdoc/lib/rdoc/rdoc.rb +0 -375
  83. data/rdoc/lib/rdoc/require.rb +0 -32
  84. data/rdoc/lib/rdoc/ri/cache.rb +0 -187
  85. data/rdoc/lib/rdoc/ri/descriptions.rb +0 -156
  86. data/rdoc/lib/rdoc/ri/display.rb +0 -340
  87. data/rdoc/lib/rdoc/ri/driver.rb +0 -828
  88. data/rdoc/lib/rdoc/ri/formatter.rb +0 -654
  89. data/rdoc/lib/rdoc/ri/paths.rb +0 -93
  90. data/rdoc/lib/rdoc/ri/reader.rb +0 -106
  91. data/rdoc/lib/rdoc/ri/util.rb +0 -79
  92. data/rdoc/lib/rdoc/ri/writer.rb +0 -68
  93. data/rdoc/lib/rdoc/ri.rb +0 -8
  94. data/rdoc/lib/rdoc/single_class.rb +0 -8
  95. data/rdoc/lib/rdoc/stats.rb +0 -178
  96. data/rdoc/lib/rdoc/task.rb +0 -276
  97. data/rdoc/lib/rdoc/tokenstream.rb +0 -33
  98. data/rdoc/lib/rdoc/top_level.rb +0 -242
  99. data/rdoc/lib/rdoc.rb +0 -398
@@ -1,2904 +0,0 @@
1
- ##
2
- # This file contains stuff stolen outright from:
3
- #
4
- # rtags.rb -
5
- # ruby-lex.rb - ruby lexcal analyzer
6
- # ruby-token.rb - ruby tokens
7
- # by Keiju ISHITSUKA (Nippon Rational Inc.)
8
- #
9
-
10
- require 'e2mmap'
11
- require 'irb/slex'
12
-
13
- require 'rdoc/code_objects'
14
- require 'rdoc/tokenstream'
15
- require 'rdoc/markup/preprocess'
16
- require 'rdoc/parser'
17
-
18
- $TOKEN_DEBUG ||= nil
19
- #$TOKEN_DEBUG = $DEBUG_RDOC
20
-
21
- ##
22
- # Definitions of all tokens involved in the lexical analysis
23
-
24
- module RDoc::RubyToken
25
-
26
- EXPR_BEG = :EXPR_BEG
27
- EXPR_MID = :EXPR_MID
28
- EXPR_END = :EXPR_END
29
- EXPR_ARG = :EXPR_ARG
30
- EXPR_FNAME = :EXPR_FNAME
31
- EXPR_DOT = :EXPR_DOT
32
- EXPR_CLASS = :EXPR_CLASS
33
-
34
- class Token
35
- NO_TEXT = "??".freeze
36
-
37
- attr_accessor :text
38
- attr_reader :line_no
39
- attr_reader :char_no
40
-
41
- def initialize(line_no, char_no)
42
- @line_no = line_no
43
- @char_no = char_no
44
- @text = NO_TEXT
45
- end
46
-
47
- def ==(other)
48
- self.class == other.class and
49
- other.line_no == @line_no and
50
- other.char_no == @char_no and
51
- other.text == @text
52
- end
53
-
54
- ##
55
- # Because we're used in contexts that expect to return a token, we set the
56
- # text string and then return ourselves
57
-
58
- def set_text(text)
59
- @text = text
60
- self
61
- end
62
-
63
- end
64
-
65
- class TkNode < Token
66
- attr :node
67
- end
68
-
69
- class TkId < Token
70
- def initialize(line_no, char_no, name)
71
- super(line_no, char_no)
72
- @name = name
73
- end
74
- attr :name
75
- end
76
-
77
- class TkKW < TkId
78
- end
79
-
80
- class TkVal < Token
81
- def initialize(line_no, char_no, value = nil)
82
- super(line_no, char_no)
83
- set_text(value)
84
- end
85
- end
86
-
87
- class TkOp < Token
88
- def name
89
- self.class.op_name
90
- end
91
- end
92
-
93
- class TkOPASGN < TkOp
94
- def initialize(line_no, char_no, op)
95
- super(line_no, char_no)
96
- op = TkReading2Token[op] unless Symbol === op
97
- @op = op
98
- end
99
- attr :op
100
- end
101
-
102
- class TkUnknownChar < Token
103
- def initialize(line_no, char_no, id)
104
- super(line_no, char_no)
105
- @name = char_no.chr
106
- end
107
- attr :name
108
- end
109
-
110
- class TkError < Token
111
- end
112
-
113
- def set_token_position(line, char)
114
- @prev_line_no = line
115
- @prev_char_no = char
116
- end
117
-
118
- def Token(token, value = nil)
119
- tk = nil
120
- case token
121
- when String, Symbol
122
- source = String === token ? TkReading2Token : TkSymbol2Token
123
- raise TkReading2TokenNoKey, token if (tk = source[token]).nil?
124
- tk = Token(tk[0], value)
125
- else
126
- tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
127
- token.new(@prev_line_no, @prev_char_no)
128
- else
129
- token.new(@prev_line_no, @prev_char_no, value)
130
- end
131
- end
132
- tk
133
- end
134
-
135
- TokenDefinitions = [
136
- [:TkCLASS, TkKW, "class", EXPR_CLASS],
137
- [:TkMODULE, TkKW, "module", EXPR_CLASS],
138
- [:TkDEF, TkKW, "def", EXPR_FNAME],
139
- [:TkUNDEF, TkKW, "undef", EXPR_FNAME],
140
- [:TkBEGIN, TkKW, "begin", EXPR_BEG],
141
- [:TkRESCUE, TkKW, "rescue", EXPR_MID],
142
- [:TkENSURE, TkKW, "ensure", EXPR_BEG],
143
- [:TkEND, TkKW, "end", EXPR_END],
144
- [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD],
145
- [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD],
146
- [:TkTHEN, TkKW, "then", EXPR_BEG],
147
- [:TkELSIF, TkKW, "elsif", EXPR_BEG],
148
- [:TkELSE, TkKW, "else", EXPR_BEG],
149
- [:TkCASE, TkKW, "case", EXPR_BEG],
150
- [:TkWHEN, TkKW, "when", EXPR_BEG],
151
- [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
152
- [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
153
- [:TkFOR, TkKW, "for", EXPR_BEG],
154
- [:TkBREAK, TkKW, "break", EXPR_END],
155
- [:TkNEXT, TkKW, "next", EXPR_END],
156
- [:TkREDO, TkKW, "redo", EXPR_END],
157
- [:TkRETRY, TkKW, "retry", EXPR_END],
158
- [:TkIN, TkKW, "in", EXPR_BEG],
159
- [:TkDO, TkKW, "do", EXPR_BEG],
160
- [:TkRETURN, TkKW, "return", EXPR_MID],
161
- [:TkYIELD, TkKW, "yield", EXPR_END],
162
- [:TkSUPER, TkKW, "super", EXPR_END],
163
- [:TkSELF, TkKW, "self", EXPR_END],
164
- [:TkNIL, TkKW, "nil", EXPR_END],
165
- [:TkTRUE, TkKW, "true", EXPR_END],
166
- [:TkFALSE, TkKW, "false", EXPR_END],
167
- [:TkAND, TkKW, "and", EXPR_BEG],
168
- [:TkOR, TkKW, "or", EXPR_BEG],
169
- [:TkNOT, TkKW, "not", EXPR_BEG],
170
- [:TkIF_MOD, TkKW],
171
- [:TkUNLESS_MOD, TkKW],
172
- [:TkWHILE_MOD, TkKW],
173
- [:TkUNTIL_MOD, TkKW],
174
- [:TkALIAS, TkKW, "alias", EXPR_FNAME],
175
- [:TkDEFINED, TkKW, "defined?", EXPR_END],
176
- [:TklBEGIN, TkKW, "BEGIN", EXPR_END],
177
- [:TklEND, TkKW, "END", EXPR_END],
178
- [:Tk__LINE__, TkKW, "__LINE__", EXPR_END],
179
- [:Tk__FILE__, TkKW, "__FILE__", EXPR_END],
180
-
181
- [:TkIDENTIFIER, TkId],
182
- [:TkFID, TkId],
183
- [:TkGVAR, TkId],
184
- [:TkIVAR, TkId],
185
- [:TkCONSTANT, TkId],
186
-
187
- [:TkINTEGER, TkVal],
188
- [:TkFLOAT, TkVal],
189
- [:TkSTRING, TkVal],
190
- [:TkXSTRING, TkVal],
191
- [:TkREGEXP, TkVal],
192
- [:TkCOMMENT, TkVal],
193
-
194
- [:TkDSTRING, TkNode],
195
- [:TkDXSTRING, TkNode],
196
- [:TkDREGEXP, TkNode],
197
- [:TkNTH_REF, TkId],
198
- [:TkBACK_REF, TkId],
199
-
200
- [:TkUPLUS, TkOp, "+@"],
201
- [:TkUMINUS, TkOp, "-@"],
202
- [:TkPOW, TkOp, "**"],
203
- [:TkCMP, TkOp, "<=>"],
204
- [:TkEQ, TkOp, "=="],
205
- [:TkEQQ, TkOp, "==="],
206
- [:TkNEQ, TkOp, "!="],
207
- [:TkGEQ, TkOp, ">="],
208
- [:TkLEQ, TkOp, "<="],
209
- [:TkANDOP, TkOp, "&&"],
210
- [:TkOROP, TkOp, "||"],
211
- [:TkMATCH, TkOp, "=~"],
212
- [:TkNMATCH, TkOp, "!~"],
213
- [:TkDOT2, TkOp, ".."],
214
- [:TkDOT3, TkOp, "..."],
215
- [:TkAREF, TkOp, "[]"],
216
- [:TkASET, TkOp, "[]="],
217
- [:TkLSHFT, TkOp, "<<"],
218
- [:TkRSHFT, TkOp, ">>"],
219
- [:TkCOLON2, TkOp],
220
- [:TkCOLON3, TkOp],
221
- # [:OPASGN, TkOp], # +=, -= etc. #
222
- [:TkASSOC, TkOp, "=>"],
223
- [:TkQUESTION, TkOp, "?"], #?
224
- [:TkCOLON, TkOp, ":"], #:
225
-
226
- [:TkfLPAREN], # func( #
227
- [:TkfLBRACK], # func[ #
228
- [:TkfLBRACE], # func{ #
229
- [:TkSTAR], # *arg
230
- [:TkAMPER], # &arg #
231
- [:TkSYMBOL, TkId], # :SYMBOL
232
- [:TkSYMBEG, TkId],
233
- [:TkGT, TkOp, ">"],
234
- [:TkLT, TkOp, "<"],
235
- [:TkPLUS, TkOp, "+"],
236
- [:TkMINUS, TkOp, "-"],
237
- [:TkMULT, TkOp, "*"],
238
- [:TkDIV, TkOp, "/"],
239
- [:TkMOD, TkOp, "%"],
240
- [:TkBITOR, TkOp, "|"],
241
- [:TkBITXOR, TkOp, "^"],
242
- [:TkBITAND, TkOp, "&"],
243
- [:TkBITNOT, TkOp, "~"],
244
- [:TkNOTOP, TkOp, "!"],
245
-
246
- [:TkBACKQUOTE, TkOp, "`"],
247
-
248
- [:TkASSIGN, Token, "="],
249
- [:TkDOT, Token, "."],
250
- [:TkLPAREN, Token, "("], #(exp)
251
- [:TkLBRACK, Token, "["], #[arry]
252
- [:TkLBRACE, Token, "{"], #{hash}
253
- [:TkRPAREN, Token, ")"],
254
- [:TkRBRACK, Token, "]"],
255
- [:TkRBRACE, Token, "}"],
256
- [:TkCOMMA, Token, ","],
257
- [:TkSEMICOLON, Token, ";"],
258
-
259
- [:TkRD_COMMENT],
260
- [:TkSPACE],
261
- [:TkNL],
262
- [:TkEND_OF_SCRIPT],
263
-
264
- [:TkBACKSLASH, TkUnknownChar, "\\"],
265
- [:TkAT, TkUnknownChar, "@"],
266
- [:TkDOLLAR, TkUnknownChar, "\$"], #"
267
- ]
268
-
269
- # {reading => token_class}
270
- # {reading => [token_class, *opt]}
271
- TkReading2Token = {}
272
- TkSymbol2Token = {}
273
-
274
- def self.def_token(token_n, super_token = Token, reading = nil, *opts)
275
- token_n = token_n.id2name unless String === token_n
276
-
277
- fail AlreadyDefinedToken, token_n if const_defined?(token_n)
278
-
279
- token_c = Class.new super_token
280
- const_set token_n, token_c
281
- # token_c.inspect
282
-
283
- if reading
284
- if TkReading2Token[reading]
285
- fail TkReading2TokenDuplicateError, token_n, reading
286
- end
287
- if opts.empty?
288
- TkReading2Token[reading] = [token_c]
289
- else
290
- TkReading2Token[reading] = [token_c].concat(opts)
291
- end
292
- end
293
- TkSymbol2Token[token_n.intern] = token_c
294
-
295
- if token_c <= TkOp
296
- token_c.class_eval %{
297
- def self.op_name; "#{reading}"; end
298
- }
299
- end
300
- end
301
-
302
- for defs in TokenDefinitions
303
- def_token(*defs)
304
- end
305
-
306
- NEWLINE_TOKEN = TkNL.new(0,0)
307
- NEWLINE_TOKEN.set_text("\n")
308
-
309
- end
310
-
311
- ##
312
- # Lexical analyzer for Ruby source
313
-
314
- class RDoc::RubyLex
315
-
316
- ##
317
- # Read an input stream character by character. We allow for unlimited
318
- # ungetting of characters just read.
319
- #
320
- # We simplify the implementation greatly by reading the entire input
321
- # into a buffer initially, and then simply traversing it using
322
- # pointers.
323
- #
324
- # We also have to allow for the <i>here document diversion</i>. This
325
- # little gem comes about when the lexer encounters a here
326
- # document. At this point we effectively need to split the input
327
- # stream into two parts: one to read the body of the here document,
328
- # the other to read the rest of the input line where the here
329
- # document was initially encountered. For example, we might have
330
- #
331
- # do_something(<<-A, <<-B)
332
- # stuff
333
- # for
334
- # A
335
- # stuff
336
- # for
337
- # B
338
- #
339
- # When the lexer encounters the <<A, it reads until the end of the
340
- # line, and keeps it around for later. It then reads the body of the
341
- # here document. Once complete, it needs to read the rest of the
342
- # original line, but then skip the here document body.
343
- #
344
-
345
- class BufferedReader
346
-
347
- attr_reader :line_num
348
-
349
- def initialize(content, options)
350
- @options = options
351
-
352
- if /\t/ =~ content
353
- tab_width = @options.tab_width
354
- content = content.split(/\n/).map do |line|
355
- 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
356
- line
357
- end .join("\n")
358
- end
359
- @content = content
360
- @content << "\n" unless @content[-1,1] == "\n"
361
- @size = @content.size
362
- @offset = 0
363
- @hwm = 0
364
- @line_num = 1
365
- @read_back_offset = 0
366
- @last_newline = 0
367
- @newline_pending = false
368
- end
369
-
370
- def column
371
- @offset - @last_newline
372
- end
373
-
374
- def getc
375
- return nil if @offset >= @size
376
- ch = @content[@offset, 1]
377
-
378
- @offset += 1
379
- @hwm = @offset if @hwm < @offset
380
-
381
- if @newline_pending
382
- @line_num += 1
383
- @last_newline = @offset - 1
384
- @newline_pending = false
385
- end
386
-
387
- if ch == "\n"
388
- @newline_pending = true
389
- end
390
- ch
391
- end
392
-
393
- def getc_already_read
394
- getc
395
- end
396
-
397
- def ungetc(ch)
398
- raise "unget past beginning of file" if @offset <= 0
399
- @offset -= 1
400
- if @content[@offset] == ?\n
401
- @newline_pending = false
402
- end
403
- end
404
-
405
- def get_read
406
- res = @content[@read_back_offset...@offset]
407
- @read_back_offset = @offset
408
- res
409
- end
410
-
411
- def peek(at)
412
- pos = @offset + at
413
- if pos >= @size
414
- nil
415
- else
416
- @content[pos, 1]
417
- end
418
- end
419
-
420
- def peek_equal(str)
421
- @content[@offset, str.length] == str
422
- end
423
-
424
- def divert_read_from(reserve)
425
- @content[@offset, 0] = reserve
426
- @size = @content.size
427
- end
428
- end
429
-
430
- # end of nested class BufferedReader
431
-
432
- extend Exception2MessageMapper
433
- def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
434
- def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
435
- def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
436
- def_exception(:TkReading2TokenDuplicateError,
437
- "key duplicate(token_n='%s', key='%s')")
438
- def_exception(:SyntaxError, "%s")
439
-
440
- include RDoc::RubyToken
441
- include IRB
442
-
443
- attr_reader :continue
444
- attr_reader :lex_state
445
-
446
- def self.debug?
447
- false
448
- end
449
-
450
- def initialize(content, options)
451
- lex_init
452
-
453
- @options = options
454
-
455
- @reader = BufferedReader.new content, @options
456
-
457
- @exp_line_no = @line_no = 1
458
- @base_char_no = 0
459
- @indent = 0
460
-
461
- @ltype = nil
462
- @quoted = nil
463
- @lex_state = EXPR_BEG
464
- @space_seen = false
465
-
466
- @continue = false
467
- @line = ""
468
-
469
- @skip_space = false
470
- @read_auto_clean_up = false
471
- @exception_on_syntax_error = true
472
- end
473
-
474
- attr_accessor :skip_space
475
- attr_accessor :read_auto_clean_up
476
- attr_accessor :exception_on_syntax_error
477
- attr_reader :indent
478
-
479
- # io functions
480
- def line_no
481
- @reader.line_num
482
- end
483
-
484
- def char_no
485
- @reader.column
486
- end
487
-
488
- def get_read
489
- @reader.get_read
490
- end
491
-
492
- def getc
493
- @reader.getc
494
- end
495
-
496
- def getc_of_rests
497
- @reader.getc_already_read
498
- end
499
-
500
- def gets
501
- c = getc or return
502
- l = ""
503
- begin
504
- l.concat c unless c == "\r"
505
- break if c == "\n"
506
- end while c = getc
507
- l
508
- end
509
-
510
-
511
- def ungetc(c = nil)
512
- @reader.ungetc(c)
513
- end
514
-
515
- def peek_equal?(str)
516
- @reader.peek_equal(str)
517
- end
518
-
519
- def peek(i = 0)
520
- @reader.peek(i)
521
- end
522
-
523
- def lex
524
- until (TkNL === (tk = token) or TkEND_OF_SCRIPT === tk) and
525
- not @continue or tk.nil?
526
- end
527
-
528
- line = get_read
529
-
530
- if line == "" and TkEND_OF_SCRIPT === tk or tk.nil? then
531
- nil
532
- else
533
- line
534
- end
535
- end
536
-
537
- def token
538
- set_token_position(line_no, char_no)
539
- begin
540
- begin
541
- tk = @OP.match(self)
542
- @space_seen = TkSPACE === tk
543
- rescue SyntaxError => e
544
- raise RDoc::Error, "syntax error: #{e.message}" if
545
- @exception_on_syntax_error
546
-
547
- tk = TkError.new(line_no, char_no)
548
- end
549
- end while @skip_space and TkSPACE === tk
550
- if @read_auto_clean_up
551
- get_read
552
- end
553
- # throw :eof unless tk
554
- tk
555
- end
556
-
557
- ENINDENT_CLAUSE = [
558
- "case", "class", "def", "do", "for", "if",
559
- "module", "unless", "until", "while", "begin" #, "when"
560
- ]
561
- DEINDENT_CLAUSE = ["end" #, "when"
562
- ]
563
-
564
- PERCENT_LTYPE = {
565
- "q" => "\'",
566
- "Q" => "\"",
567
- "x" => "\`",
568
- "r" => "/",
569
- "w" => "]"
570
- }
571
-
572
- PERCENT_PAREN = {
573
- "{" => "}",
574
- "[" => "]",
575
- "<" => ">",
576
- "(" => ")"
577
- }
578
-
579
- Ltype2Token = {
580
- "\'" => TkSTRING,
581
- "\"" => TkSTRING,
582
- "\`" => TkXSTRING,
583
- "/" => TkREGEXP,
584
- "]" => TkDSTRING
585
- }
586
- Ltype2Token.default = TkSTRING
587
-
588
- DLtype2Token = {
589
- "\"" => TkDSTRING,
590
- "\`" => TkDXSTRING,
591
- "/" => TkDREGEXP,
592
- }
593
-
594
- def lex_init()
595
- @OP = IRB::SLex.new
596
- @OP.def_rules("\0", "\004", "\032") do |chars, io|
597
- Token(TkEND_OF_SCRIPT).set_text(chars)
598
- end
599
-
600
- @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io|
601
- @space_seen = TRUE
602
- while (ch = getc) =~ /[ \t\f\r\13]/
603
- chars << ch
604
- end
605
- ungetc
606
- Token(TkSPACE).set_text(chars)
607
- end
608
-
609
- @OP.def_rule("#") do
610
- |op, io|
611
- identify_comment
612
- end
613
-
614
- @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do
615
- |op, io|
616
- str = op
617
- @ltype = "="
618
-
619
-
620
- begin
621
- line = ""
622
- begin
623
- ch = getc
624
- line << ch
625
- end until ch == "\n"
626
- str << line
627
- end until line =~ /^=end/
628
-
629
- ungetc
630
-
631
- @ltype = nil
632
-
633
- if str =~ /\A=begin\s+rdoc/i
634
- str.sub!(/\A=begin.*\n/, '')
635
- str.sub!(/^=end.*/m, '')
636
- Token(TkCOMMENT).set_text(str)
637
- else
638
- Token(TkRD_COMMENT)#.set_text(str)
639
- end
640
- end
641
-
642
- @OP.def_rule("\n") do
643
- print "\\n\n" if RDoc::RubyLex.debug?
644
- case @lex_state
645
- when EXPR_BEG, EXPR_FNAME, EXPR_DOT
646
- @continue = TRUE
647
- else
648
- @continue = FALSE
649
- @lex_state = EXPR_BEG
650
- end
651
- Token(TkNL).set_text("\n")
652
- end
653
-
654
- @OP.def_rules("*", "**",
655
- "!", "!=", "!~",
656
- "=", "==", "===",
657
- "=~", "<=>",
658
- "<", "<=",
659
- ">", ">=", ">>") do
660
- |op, io|
661
- @lex_state = EXPR_BEG
662
- Token(op).set_text(op)
663
- end
664
-
665
- @OP.def_rules("<<") do
666
- |op, io|
667
- tk = nil
668
- if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
669
- (@lex_state != EXPR_ARG || @space_seen)
670
- c = peek(0)
671
- if /[-\w_\"\'\`]/ =~ c
672
- tk = identify_here_document
673
- end
674
- end
675
- if !tk
676
- @lex_state = EXPR_BEG
677
- tk = Token(op).set_text(op)
678
- end
679
- tk
680
- end
681
-
682
- @OP.def_rules("'", '"') do
683
- |op, io|
684
- identify_string(op)
685
- end
686
-
687
- @OP.def_rules("`") do
688
- |op, io|
689
- if @lex_state == EXPR_FNAME
690
- Token(op).set_text(op)
691
- else
692
- identify_string(op)
693
- end
694
- end
695
-
696
- @OP.def_rules('?') do
697
- |op, io|
698
- if @lex_state == EXPR_END
699
- @lex_state = EXPR_BEG
700
- Token(TkQUESTION).set_text(op)
701
- else
702
- ch = getc
703
- if @lex_state == EXPR_ARG && ch !~ /\s/
704
- ungetc
705
- @lex_state = EXPR_BEG
706
- Token(TkQUESTION).set_text(op)
707
- else
708
- str = op
709
- str << ch
710
- if (ch == '\\') #'
711
- str << read_escape
712
- end
713
- @lex_state = EXPR_END
714
- Token(TkINTEGER).set_text(str)
715
- end
716
- end
717
- end
718
-
719
- @OP.def_rules("&", "&&", "|", "||") do
720
- |op, io|
721
- @lex_state = EXPR_BEG
722
- Token(op).set_text(op)
723
- end
724
-
725
- @OP.def_rules("+=", "-=", "*=", "**=",
726
- "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
727
- |op, io|
728
- @lex_state = EXPR_BEG
729
- op =~ /^(.*)=$/
730
- Token(TkOPASGN, $1).set_text(op)
731
- end
732
-
733
- @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io|
734
- Token(TkUPLUS).set_text(op)
735
- end
736
-
737
- @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io|
738
- Token(TkUMINUS).set_text(op)
739
- end
740
-
741
- @OP.def_rules("+", "-") do
742
- |op, io|
743
- catch(:RET) do
744
- if @lex_state == EXPR_ARG
745
- if @space_seen and peek(0) =~ /[0-9]/
746
- throw :RET, identify_number(op)
747
- else
748
- @lex_state = EXPR_BEG
749
- end
750
- elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
751
- throw :RET, identify_number(op)
752
- else
753
- @lex_state = EXPR_BEG
754
- end
755
- Token(op).set_text(op)
756
- end
757
- end
758
-
759
- @OP.def_rule(".") do
760
- @lex_state = EXPR_BEG
761
- if peek(0) =~ /[0-9]/
762
- ungetc
763
- identify_number("")
764
- else
765
- # for obj.if
766
- @lex_state = EXPR_DOT
767
- Token(TkDOT).set_text(".")
768
- end
769
- end
770
-
771
- @OP.def_rules("..", "...") do
772
- |op, io|
773
- @lex_state = EXPR_BEG
774
- Token(op).set_text(op)
775
- end
776
-
777
- lex_int2
778
- end
779
-
780
- def lex_int2
781
- @OP.def_rules("]", "}", ")") do
782
- |op, io|
783
- @lex_state = EXPR_END
784
- @indent -= 1
785
- Token(op).set_text(op)
786
- end
787
-
788
- @OP.def_rule(":") do
789
- if @lex_state == EXPR_END || peek(0) =~ /\s/
790
- @lex_state = EXPR_BEG
791
- tk = Token(TkCOLON)
792
- else
793
- @lex_state = EXPR_FNAME
794
- tk = Token(TkSYMBEG)
795
- end
796
- tk.set_text(":")
797
- end
798
-
799
- @OP.def_rule("::") do
800
- if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
801
- @lex_state = EXPR_BEG
802
- tk = Token(TkCOLON3)
803
- else
804
- @lex_state = EXPR_DOT
805
- tk = Token(TkCOLON2)
806
- end
807
- tk.set_text("::")
808
- end
809
-
810
- @OP.def_rule("/") do
811
- |op, io|
812
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
813
- identify_string(op)
814
- elsif peek(0) == '='
815
- getc
816
- @lex_state = EXPR_BEG
817
- Token(TkOPASGN, :/).set_text("/=") #")
818
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
819
- identify_string(op)
820
- else
821
- @lex_state = EXPR_BEG
822
- Token("/").set_text(op)
823
- end
824
- end
825
-
826
- @OP.def_rules("^") do
827
- @lex_state = EXPR_BEG
828
- Token("^").set_text("^")
829
- end
830
-
831
- @OP.def_rules(",", ";") do
832
- |op, io|
833
- @lex_state = EXPR_BEG
834
- Token(op).set_text(op)
835
- end
836
-
837
- @OP.def_rule("~") do
838
- @lex_state = EXPR_BEG
839
- Token("~").set_text("~")
840
- end
841
-
842
- @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do
843
- @lex_state = EXPR_BEG
844
- Token("~").set_text("~@")
845
- end
846
-
847
- @OP.def_rule("(") do
848
- @indent += 1
849
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
850
- @lex_state = EXPR_BEG
851
- tk = Token(TkfLPAREN)
852
- else
853
- @lex_state = EXPR_BEG
854
- tk = Token(TkLPAREN)
855
- end
856
- tk.set_text("(")
857
- end
858
-
859
- @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do
860
- Token("[]").set_text("[]")
861
- end
862
-
863
- @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do
864
- Token("[]=").set_text("[]=")
865
- end
866
-
867
- @OP.def_rule("[") do
868
- @indent += 1
869
- if @lex_state == EXPR_FNAME
870
- t = Token(TkfLBRACK)
871
- else
872
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
873
- t = Token(TkLBRACK)
874
- elsif @lex_state == EXPR_ARG && @space_seen
875
- t = Token(TkLBRACK)
876
- else
877
- t = Token(TkfLBRACK)
878
- end
879
- @lex_state = EXPR_BEG
880
- end
881
- t.set_text("[")
882
- end
883
-
884
- @OP.def_rule("{") do
885
- @indent += 1
886
- if @lex_state != EXPR_END && @lex_state != EXPR_ARG
887
- t = Token(TkLBRACE)
888
- else
889
- t = Token(TkfLBRACE)
890
- end
891
- @lex_state = EXPR_BEG
892
- t.set_text("{")
893
- end
894
-
895
- @OP.def_rule('\\') do #'
896
- if getc == "\n"
897
- @space_seen = true
898
- @continue = true
899
- Token(TkSPACE).set_text("\\\n")
900
- else
901
- ungetc
902
- Token("\\").set_text("\\") #"
903
- end
904
- end
905
-
906
- @OP.def_rule('%') do
907
- |op, io|
908
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
909
- identify_quotation('%')
910
- elsif peek(0) == '='
911
- getc
912
- Token(TkOPASGN, "%").set_text("%=")
913
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
914
- identify_quotation('%')
915
- else
916
- @lex_state = EXPR_BEG
917
- Token("%").set_text("%")
918
- end
919
- end
920
-
921
- @OP.def_rule('$') do #'
922
- identify_gvar
923
- end
924
-
925
- @OP.def_rule('@') do
926
- if peek(0) =~ /[@\w_]/
927
- ungetc
928
- identify_identifier
929
- else
930
- Token("@").set_text("@")
931
- end
932
- end
933
-
934
- @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do
935
- throw :eof
936
- end
937
-
938
- @OP.def_rule("") do
939
- |op, io|
940
- printf "MATCH: start %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
941
- if peek(0) =~ /[0-9]/
942
- t = identify_number("")
943
- elsif peek(0) =~ /[\w_]/
944
- t = identify_identifier
945
- end
946
- printf "MATCH: end %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
947
- t
948
- end
949
- end
950
-
951
- def identify_gvar
952
- @lex_state = EXPR_END
953
- str = "$"
954
-
955
- tk = case ch = getc
956
- when /[~_*$?!@\/\\;,=:<>".]/ #"
957
- str << ch
958
- Token(TkGVAR, str)
959
-
960
- when "-"
961
- str << "-" << getc
962
- Token(TkGVAR, str)
963
-
964
- when "&", "`", "'", "+"
965
- str << ch
966
- Token(TkBACK_REF, str)
967
-
968
- when /[1-9]/
969
- str << ch
970
- while (ch = getc) =~ /[0-9]/
971
- str << ch
972
- end
973
- ungetc
974
- Token(TkNTH_REF)
975
- when /\w/
976
- ungetc
977
- ungetc
978
- return identify_identifier
979
- else
980
- ungetc
981
- Token("$")
982
- end
983
- tk.set_text(str)
984
- end
985
-
986
- def identify_identifier
987
- token = ""
988
- token.concat getc if peek(0) =~ /[$@]/
989
- token.concat getc if peek(0) == "@"
990
-
991
- while (ch = getc) =~ /\w|_/
992
- print ":", ch, ":" if RDoc::RubyLex.debug?
993
- token.concat ch
994
- end
995
- ungetc
996
-
997
- if ch == "!" or ch == "?"
998
- token.concat getc
999
- end
1000
- # fix token
1001
-
1002
- # $stderr.puts "identifier - #{token}, state = #@lex_state"
1003
-
1004
- case token
1005
- when /^\$/
1006
- return Token(TkGVAR, token).set_text(token)
1007
- when /^\@/
1008
- @lex_state = EXPR_END
1009
- return Token(TkIVAR, token).set_text(token)
1010
- end
1011
-
1012
- if @lex_state != EXPR_DOT
1013
- print token, "\n" if RDoc::RubyLex.debug?
1014
-
1015
- token_c, *trans = TkReading2Token[token]
1016
- if token_c
1017
- # reserved word?
1018
-
1019
- if (@lex_state != EXPR_BEG &&
1020
- @lex_state != EXPR_FNAME &&
1021
- trans[1])
1022
- # modifiers
1023
- token_c = TkSymbol2Token[trans[1]]
1024
- @lex_state = trans[0]
1025
- else
1026
- if @lex_state != EXPR_FNAME
1027
- if ENINDENT_CLAUSE.include?(token)
1028
- @indent += 1
1029
- elsif DEINDENT_CLAUSE.include?(token)
1030
- @indent -= 1
1031
- end
1032
- @lex_state = trans[0]
1033
- else
1034
- @lex_state = EXPR_END
1035
- end
1036
- end
1037
- return Token(token_c, token).set_text(token)
1038
- end
1039
- end
1040
-
1041
- if @lex_state == EXPR_FNAME
1042
- @lex_state = EXPR_END
1043
- if peek(0) == '='
1044
- token.concat getc
1045
- end
1046
- elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
1047
- @lex_state = EXPR_ARG
1048
- else
1049
- @lex_state = EXPR_END
1050
- end
1051
-
1052
- if token[0, 1] =~ /[A-Z]/
1053
- return Token(TkCONSTANT, token).set_text(token)
1054
- elsif token[token.size - 1, 1] =~ /[!?]/
1055
- return Token(TkFID, token).set_text(token)
1056
- else
1057
- return Token(TkIDENTIFIER, token).set_text(token)
1058
- end
1059
- end
1060
-
1061
- def identify_here_document
1062
- ch = getc
1063
- if ch == "-"
1064
- ch = getc
1065
- indent = true
1066
- end
1067
- if /['"`]/ =~ ch # '
1068
- lt = ch
1069
- quoted = ""
1070
- while (c = getc) && c != lt
1071
- quoted.concat c
1072
- end
1073
- else
1074
- lt = '"'
1075
- quoted = ch.dup
1076
- while (c = getc) && c =~ /\w/
1077
- quoted.concat c
1078
- end
1079
- ungetc
1080
- end
1081
-
1082
- ltback, @ltype = @ltype, lt
1083
- reserve = ""
1084
-
1085
- while ch = getc
1086
- reserve << ch
1087
- if ch == "\\" #"
1088
- ch = getc
1089
- reserve << ch
1090
- elsif ch == "\n"
1091
- break
1092
- end
1093
- end
1094
-
1095
- str = ""
1096
- while (l = gets)
1097
- l.chomp!
1098
- l.strip! if indent
1099
- break if l == quoted
1100
- str << l.chomp << "\n"
1101
- end
1102
-
1103
- @reader.divert_read_from(reserve)
1104
-
1105
- @ltype = ltback
1106
- @lex_state = EXPR_END
1107
- Token(Ltype2Token[lt], str).set_text(str.dump)
1108
- end
1109
-
1110
- def identify_quotation(initial_char)
1111
- ch = getc
1112
- if lt = PERCENT_LTYPE[ch]
1113
- initial_char += ch
1114
- ch = getc
1115
- elsif ch =~ /\W/
1116
- lt = "\""
1117
- else
1118
- fail SyntaxError, "unknown type of %string ('#{ch}')"
1119
- end
1120
- # if ch !~ /\W/
1121
- # ungetc
1122
- # next
1123
- # end
1124
- #@ltype = lt
1125
- @quoted = ch unless @quoted = PERCENT_PAREN[ch]
1126
- identify_string(lt, @quoted, ch, initial_char)
1127
- end
1128
-
1129
- def identify_number(start)
1130
- str = start.dup
1131
-
1132
- if start == "+" or start == "-" or start == ""
1133
- start = getc
1134
- str << start
1135
- end
1136
-
1137
- @lex_state = EXPR_END
1138
-
1139
- if start == "0"
1140
- if peek(0) == "x"
1141
- ch = getc
1142
- str << ch
1143
- match = /[0-9a-f_]/
1144
- else
1145
- match = /[0-7_]/
1146
- end
1147
- while ch = getc
1148
- if ch !~ match
1149
- ungetc
1150
- break
1151
- else
1152
- str << ch
1153
- end
1154
- end
1155
- return Token(TkINTEGER).set_text(str)
1156
- end
1157
-
1158
- type = TkINTEGER
1159
- allow_point = TRUE
1160
- allow_e = TRUE
1161
- while ch = getc
1162
- case ch
1163
- when /[0-9_]/
1164
- str << ch
1165
-
1166
- when allow_point && "."
1167
- type = TkFLOAT
1168
- if peek(0) !~ /[0-9]/
1169
- ungetc
1170
- break
1171
- end
1172
- str << ch
1173
- allow_point = false
1174
-
1175
- when allow_e && "e", allow_e && "E"
1176
- str << ch
1177
- type = TkFLOAT
1178
- if peek(0) =~ /[+-]/
1179
- str << getc
1180
- end
1181
- allow_e = false
1182
- allow_point = false
1183
- else
1184
- ungetc
1185
- break
1186
- end
1187
- end
1188
- Token(type).set_text(str)
1189
- end
1190
-
1191
- def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil)
1192
- @ltype = ltype
1193
- @quoted = quoted
1194
- subtype = nil
1195
-
1196
- str = ""
1197
- str << initial_char if initial_char
1198
- str << (opener||quoted)
1199
-
1200
- nest = 0
1201
- begin
1202
- while ch = getc
1203
- str << ch
1204
- if @quoted == ch
1205
- if nest == 0
1206
- break
1207
- else
1208
- nest -= 1
1209
- end
1210
- elsif opener == ch
1211
- nest += 1
1212
- elsif @ltype != "'" && @ltype != "]" and ch == "#"
1213
- ch = getc
1214
- if ch == "{"
1215
- subtype = true
1216
- str << ch << skip_inner_expression
1217
- else
1218
- ungetc(ch)
1219
- end
1220
- elsif ch == '\\' #'
1221
- str << read_escape
1222
- end
1223
- end
1224
- if @ltype == "/"
1225
- if peek(0) =~ /i|o|n|e|s/
1226
- str << getc
1227
- end
1228
- end
1229
- if subtype
1230
- Token(DLtype2Token[ltype], str)
1231
- else
1232
- Token(Ltype2Token[ltype], str)
1233
- end.set_text(str)
1234
- ensure
1235
- @ltype = nil
1236
- @quoted = nil
1237
- @lex_state = EXPR_END
1238
- end
1239
- end
1240
-
1241
- def skip_inner_expression
1242
- res = ""
1243
- nest = 0
1244
- while (ch = getc)
1245
- res << ch
1246
- if ch == '}'
1247
- break if nest.zero?
1248
- nest -= 1
1249
- elsif ch == '{'
1250
- nest += 1
1251
- end
1252
- end
1253
- res
1254
- end
1255
-
1256
- def identify_comment
1257
- @ltype = "#"
1258
- comment = "#"
1259
- while ch = getc
1260
- if ch == "\\"
1261
- ch = getc
1262
- if ch == "\n"
1263
- ch = " "
1264
- else
1265
- comment << "\\"
1266
- end
1267
- else
1268
- if ch == "\n"
1269
- @ltype = nil
1270
- ungetc
1271
- break
1272
- end
1273
- end
1274
- comment << ch
1275
- end
1276
- return Token(TkCOMMENT).set_text(comment)
1277
- end
1278
-
1279
- def read_escape
1280
- res = ""
1281
- case ch = getc
1282
- when /[0-7]/
1283
- ungetc ch
1284
- 3.times do
1285
- case ch = getc
1286
- when /[0-7]/
1287
- when nil
1288
- break
1289
- else
1290
- ungetc
1291
- break
1292
- end
1293
- res << ch
1294
- end
1295
-
1296
- when "x"
1297
- res << ch
1298
- 2.times do
1299
- case ch = getc
1300
- when /[0-9a-fA-F]/
1301
- when nil
1302
- break
1303
- else
1304
- ungetc
1305
- break
1306
- end
1307
- res << ch
1308
- end
1309
-
1310
- when "M"
1311
- res << ch
1312
- if (ch = getc) != '-'
1313
- ungetc
1314
- else
1315
- res << ch
1316
- if (ch = getc) == "\\" #"
1317
- res << ch
1318
- res << read_escape
1319
- else
1320
- res << ch
1321
- end
1322
- end
1323
-
1324
- when "C", "c" #, "^"
1325
- res << ch
1326
- if ch == "C" and (ch = getc) != "-"
1327
- ungetc
1328
- else
1329
- res << ch
1330
- if (ch = getc) == "\\" #"
1331
- res << ch
1332
- res << read_escape
1333
- else
1334
- res << ch
1335
- end
1336
- end
1337
- else
1338
- res << ch
1339
- end
1340
- res
1341
- end
1342
- end
1343
-
1344
- ##
1345
- # Extracts code elements from a source file returning a TopLevel object
1346
- # containing the constituent file elements.
1347
- #
1348
- # This file is based on rtags
1349
- #
1350
- # RubyParser understands how to document:
1351
- # * classes
1352
- # * modules
1353
- # * methods
1354
- # * constants
1355
- # * aliases
1356
- # * private, public, protected
1357
- # * private_class_function, public_class_function
1358
- # * module_function
1359
- # * attr, attr_reader, attr_writer, attr_accessor
1360
- # * extra accessors given on the command line
1361
- # * metaprogrammed methods
1362
- # * require
1363
- # * include
1364
- #
1365
- # == Method Arguments
1366
- #
1367
- #--
1368
- # NOTE: I don't think this works, needs tests, remove the paragraph following
1369
- # this block when known to work
1370
- #
1371
- # The parser extracts the arguments from the method definition. You can
1372
- # override this with a custom argument definition using the :args: directive:
1373
- #
1374
- # ##
1375
- # # This method tries over and over until it is tired
1376
- #
1377
- # def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
1378
- # puts thing_to_try
1379
- # go_go_go thing_to_try, tries - 1
1380
- # end
1381
- #
1382
- # If you have a more-complex set of overrides you can use the :call-seq:
1383
- # directive:
1384
- #++
1385
- #
1386
- # The parser extracts the arguments from the method definition. You can
1387
- # override this with a custom argument definition using the :call-seq:
1388
- # directive:
1389
- #
1390
- # ##
1391
- # # This method can be called with a range or an offset and length
1392
- # #
1393
- # # :call-seq:
1394
- # # my_method(Range)
1395
- # # my_method(offset, length)
1396
- #
1397
- # def my_method(*args)
1398
- # end
1399
- #
1400
- # The parser extracts +yield+ expressions from method bodies to gather the
1401
- # yielded argument names. If your method manually calls a block instead of
1402
- # yielding or you want to override the discovered argument names use
1403
- # the :yields: directive:
1404
- #
1405
- # ##
1406
- # # My method is awesome
1407
- #
1408
- # def my_method(&block) # :yields: happy, times
1409
- # block.call 1, 2
1410
- # end
1411
- #
1412
- # == Metaprogrammed Methods
1413
- #
1414
- # To pick up a metaprogrammed method, the parser looks for a comment starting
1415
- # with '##' before an identifier:
1416
- #
1417
- # ##
1418
- # # This is a meta-programmed method!
1419
- #
1420
- # add_my_method :meta_method, :arg1, :arg2
1421
- #
1422
- # The parser looks at the token after the identifier to determine the name, in
1423
- # this example, :meta_method. If a name cannot be found, a warning is printed
1424
- # and 'unknown is used.
1425
- #
1426
- # You can force the name of a method using the :method: directive:
1427
- #
1428
- # ##
1429
- # # :method: woo_hoo!
1430
- #
1431
- # By default, meta-methods are instance methods. To indicate that a method is
1432
- # a singleton method instead use the :singleton-method: directive:
1433
- #
1434
- # ##
1435
- # # :singleton-method:
1436
- #
1437
- # You can also use the :singleton-method: directive with a name:
1438
- #
1439
- # ##
1440
- # # :singleton-method: woo_hoo!
1441
- #
1442
- # Additionally you can mark a method as an attribute by
1443
- # using :attr:, :attr_reader:, :attr_writer: or :attr_accessor:. Just like
1444
- # for :method:, the name is optional.
1445
- #
1446
- # ##
1447
- # # :attr_reader: my_attr_name
1448
- #
1449
- # == Hidden methods and attributes
1450
- #
1451
- # You can provide documentation for methods that don't appear using
1452
- # the :method:, :singleton-method: and :attr: directives:
1453
- #
1454
- # ##
1455
- # # :attr_writer: ghost_writer
1456
- # # There is an attribute here, but you can't see it!
1457
- #
1458
- # ##
1459
- # # :method: ghost_method
1460
- # # There is a method here, but you can't see it!
1461
- #
1462
- # ##
1463
- # # this is a comment for a regular method
1464
- #
1465
- # def regular_method() end
1466
- #
1467
- # Note that by default, the :method: directive will be ignored if there is a
1468
- # standard rdocable item following it.
1469
-
1470
- class RDoc::Parser::Ruby < RDoc::Parser
1471
-
1472
- parse_files_matching(/\.rbw?$/)
1473
-
1474
- include RDoc::RubyToken
1475
- include RDoc::TokenStream
1476
-
1477
- NORMAL = "::"
1478
- SINGLE = "<<"
1479
-
1480
- def initialize(top_level, file_name, content, options, stats)
1481
- super
1482
-
1483
- @size = 0
1484
- @token_listeners = nil
1485
- @scanner = RDoc::RubyLex.new content, @options
1486
- @scanner.exception_on_syntax_error = false
1487
-
1488
- reset
1489
- end
1490
-
1491
- def add_token_listener(obj)
1492
- @token_listeners ||= []
1493
- @token_listeners << obj
1494
- end
1495
-
1496
- ##
1497
- # Look for the first comment in a file that isn't a shebang line.
1498
-
1499
- def collect_first_comment
1500
- skip_tkspace
1501
- res = ''
1502
- first_line = true
1503
-
1504
- tk = get_tk
1505
-
1506
- while TkCOMMENT === tk
1507
- if first_line and tk.text =~ /\A#!/ then
1508
- skip_tkspace
1509
- tk = get_tk
1510
- elsif first_line and tk.text =~ /\A#\s*-\*-/ then
1511
- first_line = false
1512
- skip_tkspace
1513
- tk = get_tk
1514
- else
1515
- first_line = false
1516
- res << tk.text << "\n"
1517
- tk = get_tk
1518
-
1519
- if TkNL === tk then
1520
- skip_tkspace false
1521
- tk = get_tk
1522
- end
1523
- end
1524
- end
1525
-
1526
- unget_tk tk
1527
-
1528
- res
1529
- end
1530
-
1531
- def error(msg)
1532
- msg = make_message msg
1533
- $stderr.puts msg
1534
- exit(1)
1535
- end
1536
-
1537
- ##
1538
- # Look for a 'call-seq' in the comment, and override the normal parameter
1539
- # stuff
1540
-
1541
- def extract_call_seq(comment, meth)
1542
- if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') then
1543
- seq = $1
1544
- seq.gsub!(/^\s*\#\s*/, '')
1545
- meth.call_seq = seq
1546
- end
1547
-
1548
- meth
1549
- end
1550
-
1551
- def get_bool
1552
- skip_tkspace
1553
- tk = get_tk
1554
- case tk
1555
- when TkTRUE
1556
- true
1557
- when TkFALSE, TkNIL
1558
- false
1559
- else
1560
- unget_tk tk
1561
- true
1562
- end
1563
- end
1564
-
1565
- ##
1566
- # Look for the name of a class of module (optionally with a leading :: or
1567
- # with :: separated named) and return the ultimate name and container
1568
-
1569
- def get_class_or_module(container)
1570
- skip_tkspace
1571
- name_t = get_tk
1572
-
1573
- # class ::A -> A is in the top level
1574
- if TkCOLON2 === name_t then
1575
- name_t = get_tk
1576
- container = @top_level
1577
- end
1578
-
1579
- skip_tkspace(false)
1580
-
1581
- while TkCOLON2 === peek_tk do
1582
- prev_container = container
1583
- container = container.find_module_named(name_t.name)
1584
- if !container
1585
- # warn("Couldn't find module #{name_t.name}")
1586
- container = prev_container.add_module RDoc::NormalModule, name_t.name
1587
- end
1588
- get_tk
1589
- name_t = get_tk
1590
- end
1591
- skip_tkspace(false)
1592
- return [container, name_t]
1593
- end
1594
-
1595
- ##
1596
- # Return a superclass, which can be either a constant of an expression
1597
-
1598
- def get_class_specification
1599
- tk = get_tk
1600
- return "self" if TkSELF === tk
1601
-
1602
- res = ""
1603
- while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
1604
- res += tk.text
1605
- tk = get_tk
1606
- end
1607
-
1608
- unget_tk(tk)
1609
- skip_tkspace(false)
1610
-
1611
- get_tkread # empty out read buffer
1612
-
1613
- tk = get_tk
1614
-
1615
- case tk
1616
- when TkNL, TkCOMMENT, TkSEMICOLON then
1617
- unget_tk(tk)
1618
- return res
1619
- end
1620
-
1621
- res += parse_call_parameters(tk)
1622
- res
1623
- end
1624
-
1625
- ##
1626
- # Parse a constant, which might be qualified by one or more class or module
1627
- # names
1628
-
1629
- def get_constant
1630
- res = ""
1631
- skip_tkspace(false)
1632
- tk = get_tk
1633
-
1634
- while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
1635
- res += tk.text
1636
- tk = get_tk
1637
- end
1638
-
1639
- # if res.empty?
1640
- # warn("Unexpected token #{tk} in constant")
1641
- # end
1642
- unget_tk(tk)
1643
- res
1644
- end
1645
-
1646
- ##
1647
- # Get a constant that may be surrounded by parens
1648
-
1649
- def get_constant_with_optional_parens
1650
- skip_tkspace(false)
1651
- nest = 0
1652
- while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do
1653
- get_tk
1654
- skip_tkspace(true)
1655
- nest += 1
1656
- end
1657
-
1658
- name = get_constant
1659
-
1660
- while nest > 0
1661
- skip_tkspace(true)
1662
- tk = get_tk
1663
- nest -= 1 if TkRPAREN === tk
1664
- end
1665
- name
1666
- end
1667
-
1668
- def get_symbol_or_name
1669
- tk = get_tk
1670
- case tk
1671
- when TkSYMBOL
1672
- tk.text.sub(/^:/, '')
1673
- when TkId, TkOp
1674
- tk.name
1675
- when TkSTRING
1676
- tk.text
1677
- else
1678
- raise "Name or symbol expected (got #{tk})"
1679
- end
1680
- end
1681
-
1682
- def get_tk
1683
- tk = nil
1684
- if @tokens.empty?
1685
- tk = @scanner.token
1686
- @read.push @scanner.get_read
1687
- puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
1688
- else
1689
- @read.push @unget_read.shift
1690
- tk = @tokens.shift
1691
- puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
1692
- end
1693
-
1694
- if TkSYMBEG === tk then
1695
- set_token_position(tk.line_no, tk.char_no)
1696
- tk1 = get_tk
1697
- if TkId === tk1 or TkOp === tk1 or TkSTRING === tk1 or TkDSTRING === tk1 then
1698
- if tk1.respond_to?(:name)
1699
- tk = Token(TkSYMBOL).set_text(":" + tk1.name)
1700
- else
1701
- tk = Token(TkSYMBOL).set_text(":" + tk1.text)
1702
- end
1703
- # remove the identifier we just read (we're about to
1704
- # replace it with a symbol)
1705
- @token_listeners.each do |obj|
1706
- obj.pop_token
1707
- end if @token_listeners
1708
- else
1709
- warn("':' not followed by identifier or operator")
1710
- tk = tk1
1711
- end
1712
- end
1713
-
1714
- # inform any listeners of our shiny new token
1715
- @token_listeners.each do |obj|
1716
- obj.add_token(tk)
1717
- end if @token_listeners
1718
-
1719
- tk
1720
- end
1721
-
1722
- def get_tkread
1723
- read = @read.join("")
1724
- @read = []
1725
- read
1726
- end
1727
-
1728
- ##
1729
- # Look for directives in a normal comment block:
1730
- #
1731
- # #-- - don't display comment from this point forward
1732
- #
1733
- # This routine modifies it's parameter
1734
-
1735
- def look_for_directives_in(context, comment)
1736
- preprocess = RDoc::Markup::PreProcess.new(@file_name,
1737
- @options.rdoc_include)
1738
-
1739
- preprocess.handle(comment) do |directive, param|
1740
- case directive
1741
- when 'enddoc' then
1742
- throw :enddoc
1743
- when 'main' then
1744
- @options.main_page = param
1745
- ''
1746
- when 'method', 'singleton-method' then
1747
- false # ignore
1748
- when 'section' then
1749
- context.set_current_section(param, comment)
1750
- comment.replace ''
1751
- break
1752
- when 'startdoc' then
1753
- context.start_doc
1754
- context.force_documentation = true
1755
- ''
1756
- when 'stopdoc' then
1757
- context.stop_doc
1758
- ''
1759
- when 'title' then
1760
- @options.title = param
1761
- ''
1762
- else
1763
- warn "Unrecognized directive '#{directive}'"
1764
- false
1765
- end
1766
- end
1767
-
1768
- remove_private_comments(comment)
1769
- end
1770
-
1771
- def make_message(msg)
1772
- prefix = "\n" + @file_name + ":"
1773
- if @scanner
1774
- prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
1775
- end
1776
- return prefix + msg
1777
- end
1778
-
1779
- ##
1780
- # Creates an RDoc::Attr for the name following +tk+, setting the comment to
1781
- # +comment+.
1782
-
1783
- def parse_attr(context, single, tk, comment)
1784
- args = parse_symbol_arg 1
1785
- if args.size > 0
1786
- name = args[0]
1787
- rw = "R"
1788
- skip_tkspace(false)
1789
- tk = get_tk
1790
- if TkCOMMA === tk then
1791
- rw = "RW" if get_bool
1792
- else
1793
- unget_tk tk
1794
- end
1795
- att = RDoc::Attr.new get_tkread, name, rw, comment
1796
- read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
1797
- if att.document_self
1798
- context.add_attribute(att)
1799
- end
1800
- else
1801
- warn("'attr' ignored - looks like a variable")
1802
- end
1803
- end
1804
-
1805
- ##
1806
- # Creates an RDoc::Attr for each attribute listed after +tk+, setting the
1807
- # comment for each to +comment+.
1808
-
1809
- def parse_attr_accessor(context, single, tk, comment)
1810
- args = parse_symbol_arg
1811
- read = get_tkread
1812
- rw = "?"
1813
-
1814
- # TODO If nodoc is given, don't document any of them
1815
-
1816
- tmp = RDoc::CodeObject.new
1817
- read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
1818
- return unless tmp.document_self
1819
-
1820
- case tk.name
1821
- when "attr_reader" then rw = "R"
1822
- when "attr_writer" then rw = "W"
1823
- when "attr_accessor" then rw = "RW"
1824
- else
1825
- rw = '?'
1826
- end
1827
-
1828
- for name in args
1829
- att = RDoc::Attr.new get_tkread, name, rw, comment
1830
- context.add_attribute att
1831
- end
1832
- end
1833
-
1834
- def parse_alias(context, single, tk, comment)
1835
- skip_tkspace
1836
- if TkLPAREN === peek_tk then
1837
- get_tk
1838
- skip_tkspace
1839
- end
1840
- new_name = get_symbol_or_name
1841
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
1842
- skip_tkspace
1843
- if TkCOMMA === peek_tk then
1844
- get_tk
1845
- skip_tkspace
1846
- end
1847
- old_name = get_symbol_or_name
1848
-
1849
- al = RDoc::Alias.new get_tkread, old_name, new_name, comment
1850
- read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
1851
- if al.document_self
1852
- context.add_alias(al)
1853
- end
1854
- end
1855
-
1856
- def parse_call_parameters(tk)
1857
- end_token = case tk
1858
- when TkLPAREN, TkfLPAREN
1859
- TkRPAREN
1860
- when TkRPAREN
1861
- return ""
1862
- else
1863
- TkNL
1864
- end
1865
- nest = 0
1866
-
1867
- loop do
1868
- case tk
1869
- when TkSEMICOLON
1870
- break
1871
- when TkLPAREN, TkfLPAREN
1872
- nest += 1
1873
- when end_token
1874
- if end_token == TkRPAREN
1875
- nest -= 1
1876
- break if @scanner.lex_state == EXPR_END and nest <= 0
1877
- else
1878
- break unless @scanner.continue
1879
- end
1880
- when TkCOMMENT
1881
- unget_tk(tk)
1882
- break
1883
- end
1884
- tk = get_tk
1885
- end
1886
- res = get_tkread.tr("\n", " ").strip
1887
- res = "" if res == ";"
1888
- res
1889
- end
1890
-
1891
- def parse_class(container, single, tk, comment)
1892
- container, name_t = get_class_or_module container
1893
-
1894
- case name_t
1895
- when TkCONSTANT
1896
- name = name_t.name
1897
- superclass = "Object"
1898
-
1899
- if TkLT === peek_tk then
1900
- get_tk
1901
- skip_tkspace(true)
1902
- superclass = get_class_specification
1903
- superclass = "<unknown>" if superclass.empty?
1904
- end
1905
-
1906
- cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
1907
- cls = container.add_class cls_type, name, superclass
1908
-
1909
- @stats.add_class cls
1910
-
1911
- read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
1912
- cls.record_location @top_level
1913
-
1914
- parse_statements cls
1915
- cls.comment = comment
1916
-
1917
- when TkLSHFT
1918
- case name = get_class_specification
1919
- when "self", container.name
1920
- parse_statements(container, SINGLE)
1921
- else
1922
- other = RDoc::TopLevel.find_class_named(name)
1923
- unless other
1924
- # other = @top_level.add_class(NormalClass, name, nil)
1925
- # other.record_location(@top_level)
1926
- # other.comment = comment
1927
- other = RDoc::NormalClass.new "Dummy", nil
1928
- end
1929
-
1930
- @stats.add_class other
1931
-
1932
- read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
1933
- parse_statements(other, SINGLE)
1934
- end
1935
-
1936
- else
1937
- warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
1938
- end
1939
- end
1940
-
1941
- def parse_constant(container, single, tk, comment)
1942
- name = tk.name
1943
- skip_tkspace(false)
1944
- eq_tk = get_tk
1945
-
1946
- unless TkASSIGN === eq_tk then
1947
- unget_tk(eq_tk)
1948
- return
1949
- end
1950
-
1951
- nest = 0
1952
- get_tkread
1953
-
1954
- tk = get_tk
1955
- if TkGT === tk then
1956
- unget_tk(tk)
1957
- unget_tk(eq_tk)
1958
- return
1959
- end
1960
-
1961
- loop do
1962
- case tk
1963
- when TkSEMICOLON then
1964
- break
1965
- when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO, TkIF, TkUNLESS,
1966
- TkCASE then
1967
- nest += 1
1968
- when TkRPAREN, TkRBRACE, TkRBRACK, TkEND then
1969
- nest -= 1
1970
- when TkCOMMENT then
1971
- if nest <= 0 && @scanner.lex_state == EXPR_END
1972
- unget_tk tk
1973
- break
1974
- end
1975
- when TkNL then
1976
- if nest <= 0 &&
1977
- (@scanner.lex_state == EXPR_END || !@scanner.continue) then
1978
- unget_tk tk
1979
- break
1980
- end
1981
- end
1982
- tk = get_tk
1983
- end
1984
-
1985
- res = get_tkread.tr("\n", " ").strip
1986
- res = "" if res == ";"
1987
-
1988
- con = RDoc::Constant.new name, res, comment
1989
- read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
1990
-
1991
- container.add_constant con if con.document_self
1992
- end
1993
-
1994
- ##
1995
- # Generates an RDoc::Method or RDoc::Attr from +comment+ by looking for
1996
- # :method: or :attr: directives in +comment+.
1997
-
1998
- def parse_comment(container, tk, comment)
1999
- line_no = tk.line_no
2000
- column = tk.char_no
2001
-
2002
- singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
2003
-
2004
- # REFACTOR
2005
- if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
2006
- name = $1 unless $1.empty?
2007
-
2008
- meth = RDoc::GhostMethod.new get_tkread, name
2009
- meth.singleton = singleton
2010
-
2011
- @stats.add_method meth
2012
-
2013
- meth.start_collecting_tokens
2014
- indent = TkSPACE.new 1, 1
2015
- indent.set_text " " * column
2016
-
2017
- position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.absolute_name}, line #{line_no}")
2018
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
2019
-
2020
- meth.params = ''
2021
-
2022
- extract_call_seq comment, meth
2023
-
2024
- container.add_method meth if meth.document_self
2025
-
2026
- meth.comment = comment
2027
- elsif comment.sub!(/# +:?(attr(_reader|_writer|_accessor)?:) *(\S*).*?\n/i, '') then
2028
- rw = case $1
2029
- when 'attr_reader' then 'R'
2030
- when 'attr_writer' then 'W'
2031
- else 'RW'
2032
- end
2033
-
2034
- name = $3 unless $3.empty?
2035
-
2036
- att = RDoc::Attr.new get_tkread, name, rw, comment
2037
- container.add_attribute att
2038
- end
2039
- end
2040
-
2041
- def parse_include(context, comment)
2042
- loop do
2043
- skip_tkspace_comment
2044
-
2045
- name = get_constant_with_optional_parens
2046
- context.add_include RDoc::Include.new(name, comment) unless name.empty?
2047
-
2048
- return unless TkCOMMA === peek_tk
2049
- get_tk
2050
- end
2051
- end
2052
-
2053
- def parse_meta_attr(context, single, tk, comment)
2054
- args = parse_symbol_arg
2055
- read = get_tkread
2056
- rw = "?"
2057
-
2058
- # If nodoc is given, don't document any of them
2059
-
2060
- tmp = RDoc::CodeObject.new
2061
- read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
2062
- return unless tmp.document_self
2063
-
2064
- if comment.sub!(/^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '') then
2065
- rw = case $1
2066
- when 'attr_reader' then 'R'
2067
- when 'attr_writer' then 'W'
2068
- else 'RW'
2069
- end
2070
- name = $3 unless $3.empty?
2071
- end
2072
-
2073
- for name in args
2074
- att = RDoc::Attr.new get_tkread, name, rw, comment
2075
- context.add_attribute att
2076
- end
2077
- end
2078
-
2079
- ##
2080
- # Parses a meta-programmed method
2081
-
2082
- def parse_meta_method(container, single, tk, comment)
2083
- line_no = tk.line_no
2084
- column = tk.char_no
2085
-
2086
- start_collecting_tokens
2087
- add_token tk
2088
- add_token_listener self
2089
-
2090
- skip_tkspace false
2091
-
2092
- singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
2093
-
2094
- if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
2095
- name = $1 unless $1.empty?
2096
- end
2097
-
2098
- if name.nil? then
2099
- name_t = get_tk
2100
- case name_t
2101
- when TkSYMBOL then
2102
- name = name_t.text[1..-1]
2103
- when TkSTRING then
2104
- name = name_t.text[1..-2]
2105
- when TkASSIGN then # ignore
2106
- remove_token_listener self
2107
- return
2108
- else
2109
- warn "unknown name token #{name_t.inspect} for meta-method '#{tk.name}'"
2110
- name = 'unknown'
2111
- end
2112
- end
2113
-
2114
- meth = RDoc::MetaMethod.new get_tkread, name
2115
- meth.singleton = singleton
2116
-
2117
- @stats.add_method meth
2118
-
2119
- remove_token_listener self
2120
-
2121
- meth.start_collecting_tokens
2122
- indent = TkSPACE.new 1, 1
2123
- indent.set_text " " * column
2124
-
2125
- position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.absolute_name}, line #{line_no}")
2126
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
2127
- meth.add_tokens @token_stream
2128
-
2129
- add_token_listener meth
2130
-
2131
- meth.params = ''
2132
-
2133
- extract_call_seq comment, meth
2134
-
2135
- container.add_method meth if meth.document_self
2136
-
2137
- last_tk = tk
2138
-
2139
- while tk = get_tk do
2140
- case tk
2141
- when TkSEMICOLON then
2142
- break
2143
- when TkNL then
2144
- break unless last_tk and TkCOMMA === last_tk
2145
- when TkSPACE then
2146
- # expression continues
2147
- else
2148
- last_tk = tk
2149
- end
2150
- end
2151
-
2152
- remove_token_listener meth
2153
-
2154
- meth.comment = comment
2155
- end
2156
-
2157
- ##
2158
- # Parses a method
2159
-
2160
- def parse_method(container, single, tk, comment)
2161
- line_no = tk.line_no
2162
- column = tk.char_no
2163
-
2164
- start_collecting_tokens
2165
- add_token(tk)
2166
- add_token_listener(self)
2167
-
2168
- @scanner.instance_eval do @lex_state = EXPR_FNAME end
2169
-
2170
- skip_tkspace(false)
2171
- name_t = get_tk
2172
- back_tk = skip_tkspace
2173
- meth = nil
2174
- added_container = false
2175
-
2176
- dot = get_tk
2177
- if TkDOT === dot or TkCOLON2 === dot then
2178
- @scanner.instance_eval do @lex_state = EXPR_FNAME end
2179
- skip_tkspace
2180
- name_t2 = get_tk
2181
-
2182
- case name_t
2183
- when TkSELF then
2184
- name = name_t2.name
2185
- when TkCONSTANT then
2186
- name = name_t2.name
2187
- prev_container = container
2188
- container = container.find_module_named(name_t.name)
2189
- unless container then
2190
- added_container = true
2191
- obj = name_t.name.split("::").inject(Object) do |state, item|
2192
- state.const_get(item)
2193
- end rescue nil
2194
-
2195
- type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
2196
-
2197
- unless [Class, Module].include?(obj.class) then
2198
- warn("Couldn't find #{name_t.name}. Assuming it's a module")
2199
- end
2200
-
2201
- if type == RDoc::NormalClass then
2202
- container = prev_container.add_class(type, name_t.name, obj.superclass.name)
2203
- else
2204
- container = prev_container.add_module(type, name_t.name)
2205
- end
2206
-
2207
- container.record_location @top_level
2208
- end
2209
- else
2210
- warn "unexpected method name token #{name_t2.inspect}"
2211
- # break
2212
- skip_method(container)
2213
- return
2214
- end
2215
-
2216
- meth = RDoc::AnyMethod.new(get_tkread, name)
2217
- meth.singleton = true
2218
- else
2219
- unget_tk dot
2220
- back_tk.reverse_each do |token|
2221
- unget_tk token
2222
- end
2223
-
2224
- unless name_t.respond_to? :name then
2225
- warn "unexpected method name token #{name_t.inspect}"
2226
- skip_method container
2227
- return
2228
- end
2229
-
2230
- name = name_t.name
2231
-
2232
- meth = RDoc::AnyMethod.new get_tkread, name
2233
- meth.singleton = (single == SINGLE)
2234
- end
2235
-
2236
- @stats.add_method meth
2237
-
2238
- remove_token_listener self
2239
-
2240
- meth.start_collecting_tokens
2241
- indent = TkSPACE.new 1, 1
2242
- indent.set_text " " * column
2243
-
2244
- token = TkCOMMENT.new(line_no, 1, "# File #{@top_level.absolute_name}, line #{line_no}")
2245
- meth.add_tokens [token, NEWLINE_TOKEN, indent]
2246
- meth.add_tokens @token_stream
2247
-
2248
- add_token_listener meth
2249
-
2250
- @scanner.instance_eval do @continue = false end
2251
- parse_method_parameters meth
2252
-
2253
- if meth.document_self then
2254
- container.add_method meth
2255
- elsif added_container then
2256
- container.document_self = false
2257
- end
2258
-
2259
- # Having now read the method parameters and documentation modifiers, we
2260
- # now know whether we have to rename #initialize to ::new
2261
-
2262
- if name == "initialize" && !meth.singleton then
2263
- if meth.dont_rename_initialize then
2264
- meth.visibility = :protected
2265
- else
2266
- meth.singleton = true
2267
- meth.name = "new"
2268
- meth.visibility = :public
2269
- end
2270
- end
2271
-
2272
- parse_statements(container, single, meth)
2273
-
2274
- remove_token_listener(meth)
2275
-
2276
- extract_call_seq comment, meth
2277
-
2278
- meth.comment = comment
2279
- end
2280
-
2281
- def parse_method_or_yield_parameters(method = nil,
2282
- modifiers = RDoc::METHOD_MODIFIERS)
2283
- skip_tkspace(false)
2284
- tk = get_tk
2285
-
2286
- # Little hack going on here. In the statement
2287
- # f = 2*(1+yield)
2288
- # We see the RPAREN as the next token, so we need
2289
- # to exit early. This still won't catch all cases
2290
- # (such as "a = yield + 1"
2291
- end_token = case tk
2292
- when TkLPAREN, TkfLPAREN
2293
- TkRPAREN
2294
- when TkRPAREN
2295
- return ""
2296
- else
2297
- TkNL
2298
- end
2299
- nest = 0
2300
-
2301
- loop do
2302
- case tk
2303
- when TkSEMICOLON then
2304
- break
2305
- when TkLBRACE then
2306
- nest += 1
2307
- when TkRBRACE then
2308
- # we might have a.each {|i| yield i }
2309
- unget_tk(tk) if nest.zero?
2310
- nest -= 1
2311
- break if nest <= 0
2312
- when TkLPAREN, TkfLPAREN then
2313
- nest += 1
2314
- when end_token then
2315
- if end_token == TkRPAREN
2316
- nest -= 1
2317
- break if @scanner.lex_state == EXPR_END and nest <= 0
2318
- else
2319
- break unless @scanner.continue
2320
- end
2321
- when method && method.block_params.nil? && TkCOMMENT then
2322
- unget_tk tk
2323
- read_documentation_modifiers method, modifiers
2324
- @read.pop
2325
- when TkCOMMENT then
2326
- @read.pop
2327
- end
2328
- tk = get_tk
2329
- end
2330
-
2331
- res = get_tkread.gsub(/\s+/, ' ').strip
2332
- res = '' if res == ';'
2333
- res
2334
- end
2335
-
2336
- ##
2337
- # Capture the method's parameters. Along the way, look for a comment
2338
- # containing:
2339
- #
2340
- # # yields: ....
2341
- #
2342
- # and add this as the block_params for the method
2343
-
2344
- def parse_method_parameters(method)
2345
- res = parse_method_or_yield_parameters method
2346
-
2347
- res = "(#{res})" unless res =~ /\A\(/
2348
- method.params = res unless method.params
2349
-
2350
- if method.block_params.nil? then
2351
- skip_tkspace(false)
2352
- read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
2353
- end
2354
- end
2355
-
2356
- def parse_module(container, single, tk, comment)
2357
- container, name_t = get_class_or_module(container)
2358
-
2359
- name = name_t.name
2360
-
2361
- mod = container.add_module RDoc::NormalModule, name
2362
- mod.record_location @top_level
2363
-
2364
- @stats.add_module mod
2365
-
2366
- read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
2367
- parse_statements(mod)
2368
- mod.comment = comment
2369
- end
2370
-
2371
- def parse_require(context, comment)
2372
- skip_tkspace_comment
2373
- tk = get_tk
2374
- if TkLPAREN === tk then
2375
- skip_tkspace_comment
2376
- tk = get_tk
2377
- end
2378
-
2379
- name = nil
2380
- case tk
2381
- when TkSTRING
2382
- name = tk.text
2383
- # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
2384
- # name = tk.name
2385
- when TkDSTRING
2386
- warn "Skipping require of dynamic string: #{tk.text}"
2387
- # else
2388
- # warn "'require' used as variable"
2389
- end
2390
- if name
2391
- context.add_require RDoc::Require.new(name, comment)
2392
- else
2393
- unget_tk(tk)
2394
- end
2395
- end
2396
-
2397
- def parse_statements(container, single = NORMAL, current_method = nil,
2398
- comment = '')
2399
- nest = 1
2400
- save_visibility = container.visibility
2401
-
2402
- non_comment_seen = true
2403
-
2404
- while tk = get_tk do
2405
- keep_comment = false
2406
-
2407
- non_comment_seen = true unless TkCOMMENT === tk
2408
-
2409
- case tk
2410
- when TkNL then
2411
- skip_tkspace true # Skip blanks and newlines
2412
- tk = get_tk
2413
-
2414
- if TkCOMMENT === tk then
2415
- if non_comment_seen then
2416
- # Look for RDoc in a comment about to be thrown away
2417
- parse_comment container, tk, comment unless comment.empty?
2418
-
2419
- comment = ''
2420
- non_comment_seen = false
2421
- end
2422
-
2423
- while TkCOMMENT === tk do
2424
- comment << tk.text << "\n"
2425
- tk = get_tk # this is the newline
2426
- skip_tkspace(false) # leading spaces
2427
- tk = get_tk
2428
- end
2429
-
2430
- unless comment.empty? then
2431
- look_for_directives_in container, comment
2432
-
2433
- if container.done_documenting then
2434
- container.ongoing_visibility = save_visibility
2435
- end
2436
- end
2437
-
2438
- keep_comment = true
2439
- else
2440
- non_comment_seen = true
2441
- end
2442
-
2443
- unget_tk tk
2444
- keep_comment = true
2445
-
2446
- when TkCLASS then
2447
- if container.document_children then
2448
- parse_class container, single, tk, comment
2449
- else
2450
- nest += 1
2451
- end
2452
-
2453
- when TkMODULE then
2454
- if container.document_children then
2455
- parse_module container, single, tk, comment
2456
- else
2457
- nest += 1
2458
- end
2459
-
2460
- when TkDEF then
2461
- if container.document_self then
2462
- parse_method container, single, tk, comment
2463
- else
2464
- nest += 1
2465
- end
2466
-
2467
- when TkCONSTANT then
2468
- if container.document_self then
2469
- parse_constant container, single, tk, comment
2470
- end
2471
-
2472
- when TkALIAS then
2473
- if container.document_self then
2474
- parse_alias container, single, tk, comment
2475
- end
2476
-
2477
- when TkYIELD then
2478
- if current_method.nil? then
2479
- warn "Warning: yield outside of method" if container.document_self
2480
- else
2481
- parse_yield container, single, tk, current_method
2482
- end
2483
-
2484
- # Until and While can have a 'do', which shouldn't increase the nesting.
2485
- # We can't solve the general case, but we can handle most occurrences by
2486
- # ignoring a do at the end of a line.
2487
-
2488
- when TkUNTIL, TkWHILE then
2489
- nest += 1
2490
- skip_optional_do_after_expression
2491
-
2492
- # 'for' is trickier
2493
- when TkFOR then
2494
- nest += 1
2495
- skip_for_variable
2496
- skip_optional_do_after_expression
2497
-
2498
- when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then
2499
- nest += 1
2500
-
2501
- when TkIDENTIFIER then
2502
- if nest == 1 and current_method.nil? then
2503
- case tk.name
2504
- when 'private', 'protected', 'public', 'private_class_method',
2505
- 'public_class_method', 'module_function' then
2506
- parse_visibility container, single, tk
2507
- keep_comment = true
2508
- when 'attr' then
2509
- parse_attr container, single, tk, comment
2510
- when /^attr_(reader|writer|accessor)$/ then
2511
- parse_attr_accessor container, single, tk, comment
2512
- when 'alias_method' then
2513
- if container.document_self then
2514
- parse_alias container, single, tk, comment
2515
- end
2516
- else
2517
- if container.document_self and comment =~ /\A#\#$/ then
2518
- parse_meta_method container, single, tk, comment
2519
- end
2520
- end
2521
- end
2522
-
2523
- case tk.name
2524
- when "require" then
2525
- parse_require container, comment
2526
- when "include" then
2527
- parse_include container, comment
2528
- end
2529
-
2530
- when TkEND then
2531
- nest -= 1
2532
- if nest == 0 then
2533
- read_documentation_modifiers container, RDoc::CLASS_MODIFIERS
2534
- container.ongoing_visibility = save_visibility
2535
- return
2536
- end
2537
-
2538
- end
2539
-
2540
- comment = '' unless keep_comment
2541
-
2542
- begin
2543
- get_tkread
2544
- skip_tkspace(false)
2545
- end while peek_tk == TkNL
2546
- end
2547
- end
2548
-
2549
- def parse_symbol_arg(no = nil)
2550
- args = []
2551
- skip_tkspace_comment
2552
- case tk = get_tk
2553
- when TkLPAREN
2554
- loop do
2555
- skip_tkspace_comment
2556
- if tk1 = parse_symbol_in_arg
2557
- args.push tk1
2558
- break if no and args.size >= no
2559
- end
2560
-
2561
- skip_tkspace_comment
2562
- case tk2 = get_tk
2563
- when TkRPAREN
2564
- break
2565
- when TkCOMMA
2566
- else
2567
- warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
2568
- break
2569
- end
2570
- end
2571
- else
2572
- unget_tk tk
2573
- if tk = parse_symbol_in_arg
2574
- args.push tk
2575
- return args if no and args.size >= no
2576
- end
2577
-
2578
- loop do
2579
- skip_tkspace(false)
2580
-
2581
- tk1 = get_tk
2582
- unless TkCOMMA === tk1 then
2583
- unget_tk tk1
2584
- break
2585
- end
2586
-
2587
- skip_tkspace_comment
2588
- if tk = parse_symbol_in_arg
2589
- args.push tk
2590
- break if no and args.size >= no
2591
- end
2592
- end
2593
- end
2594
- args
2595
- end
2596
-
2597
- def parse_symbol_in_arg
2598
- case tk = get_tk
2599
- when TkSYMBOL
2600
- tk.text.sub(/^:/, '')
2601
- when TkSTRING
2602
- eval @read[-1]
2603
- else
2604
- warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
2605
- nil
2606
- end
2607
- end
2608
-
2609
- def parse_top_level_statements(container)
2610
- comment = collect_first_comment
2611
- look_for_directives_in(container, comment)
2612
- container.comment = comment unless comment.empty?
2613
- parse_statements container, NORMAL, nil, comment
2614
- end
2615
-
2616
- def parse_visibility(container, single, tk)
2617
- singleton = (single == SINGLE)
2618
-
2619
- vis_type = tk.name
2620
-
2621
- vis = case vis_type
2622
- when 'private' then :private
2623
- when 'protected' then :protected
2624
- when 'public' then :public
2625
- when 'private_class_method' then
2626
- singleton = true
2627
- :private
2628
- when 'public_class_method' then
2629
- singleton = true
2630
- :public
2631
- when 'module_function' then
2632
- singleton = true
2633
- :public
2634
- else
2635
- raise "Invalid visibility: #{tk.name}"
2636
- end
2637
-
2638
- skip_tkspace_comment false
2639
-
2640
- case peek_tk
2641
- # Ryan Davis suggested the extension to ignore modifiers, because he
2642
- # often writes
2643
- #
2644
- # protected unless $TESTING
2645
- #
2646
- when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then
2647
- container.ongoing_visibility = vis
2648
- else
2649
- if vis_type == 'module_function' then
2650
- args = parse_symbol_arg
2651
- container.set_visibility_for args, :private, false
2652
-
2653
- module_functions = []
2654
-
2655
- container.methods_matching args do |m|
2656
- s_m = m.dup
2657
- s_m.singleton = true if RDoc::AnyMethod === s_m
2658
- s_m.visibility = :public
2659
- module_functions << s_m
2660
- end
2661
-
2662
- module_functions.each do |s_m|
2663
- case s_m
2664
- when RDoc::AnyMethod then
2665
- container.add_method s_m
2666
- when RDoc::Attr then
2667
- container.add_attribute s_m
2668
- end
2669
- end
2670
- else
2671
- args = parse_symbol_arg
2672
- container.set_visibility_for args, vis, singleton
2673
- end
2674
- end
2675
- end
2676
-
2677
- def parse_yield_parameters
2678
- parse_method_or_yield_parameters
2679
- end
2680
-
2681
- def parse_yield(context, single, tk, method)
2682
- if method.block_params.nil?
2683
- get_tkread
2684
- @scanner.instance_eval{@continue = false}
2685
- method.block_params = parse_yield_parameters
2686
- end
2687
- end
2688
-
2689
- def peek_read
2690
- @read.join('')
2691
- end
2692
-
2693
- ##
2694
- # Peek at the next token, but don't remove it from the stream
2695
-
2696
- def peek_tk
2697
- unget_tk(tk = get_tk)
2698
- tk
2699
- end
2700
-
2701
- ##
2702
- # Directives are modifier comments that can appear after class, module, or
2703
- # method names. For example:
2704
- #
2705
- # def fred # :yields: a, b
2706
- #
2707
- # or:
2708
- #
2709
- # class MyClass # :nodoc:
2710
- #
2711
- # We return the directive name and any parameters as a two element array
2712
-
2713
- def read_directive(allowed)
2714
- tk = get_tk
2715
- result = nil
2716
-
2717
- if TkCOMMENT === tk then
2718
- if tk.text =~ /\s*:?(\w+):\s*(.*)/ then
2719
- directive = $1.downcase
2720
- if allowed.include? directive then
2721
- result = [directive, $2]
2722
- end
2723
- end
2724
- else
2725
- unget_tk tk
2726
- end
2727
-
2728
- result
2729
- end
2730
-
2731
- def read_documentation_modifiers(context, allow)
2732
- dir = read_directive(allow)
2733
-
2734
- case dir[0]
2735
- when "notnew", "not_new", "not-new" then
2736
- context.dont_rename_initialize = true
2737
-
2738
- when "nodoc" then
2739
- context.document_self = false
2740
- if dir[1].downcase == "all"
2741
- context.document_children = false
2742
- end
2743
-
2744
- when "doc" then
2745
- context.document_self = true
2746
- context.force_documentation = true
2747
-
2748
- when "yield", "yields" then
2749
- unless context.params.nil?
2750
- context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
2751
- end
2752
-
2753
- context.block_params = dir[1]
2754
-
2755
- when "arg", "args" then
2756
- context.params = dir[1]
2757
- end if dir
2758
- end
2759
-
2760
- def remove_private_comments(comment)
2761
- comment.gsub!(/^#--\n.*?^#\+\+/m, '')
2762
- comment.sub!(/^#--\n.*/m, '')
2763
- end
2764
-
2765
- def remove_token_listener(obj)
2766
- @token_listeners.delete(obj)
2767
- end
2768
-
2769
- def reset
2770
- @tokens = []
2771
- @unget_read = []
2772
- @read = []
2773
- end
2774
-
2775
- def scan
2776
- reset
2777
-
2778
- catch(:eof) do
2779
- catch(:enddoc) do
2780
- begin
2781
- parse_top_level_statements(@top_level)
2782
- rescue Exception => e
2783
- $stderr.puts <<-EOF
2784
-
2785
-
2786
- RDoc failure in #{@file_name} at or around line #{@scanner.line_no} column
2787
- #{@scanner.char_no}
2788
-
2789
- Before reporting this, could you check that the file you're documenting
2790
- compiles cleanly--RDoc is not a full Ruby parser, and gets confused easily if
2791
- fed invalid programs.
2792
-
2793
- The internal error was:
2794
-
2795
- EOF
2796
-
2797
- e.set_backtrace(e.backtrace[0,4])
2798
- raise
2799
- end
2800
- end
2801
- end
2802
-
2803
- @top_level
2804
- end
2805
-
2806
- ##
2807
- # while, until, and for have an optional do
2808
-
2809
- def skip_optional_do_after_expression
2810
- skip_tkspace(false)
2811
- tk = get_tk
2812
- case tk
2813
- when TkLPAREN, TkfLPAREN
2814
- end_token = TkRPAREN
2815
- else
2816
- end_token = TkNL
2817
- end
2818
-
2819
- nest = 0
2820
- @scanner.instance_eval{@continue = false}
2821
-
2822
- loop do
2823
- case tk
2824
- when TkSEMICOLON
2825
- break
2826
- when TkLPAREN, TkfLPAREN
2827
- nest += 1
2828
- when TkDO
2829
- break if nest.zero?
2830
- when end_token
2831
- if end_token == TkRPAREN
2832
- nest -= 1
2833
- break if @scanner.lex_state == EXPR_END and nest.zero?
2834
- else
2835
- break unless @scanner.continue
2836
- end
2837
- end
2838
- tk = get_tk
2839
- end
2840
- skip_tkspace(false)
2841
-
2842
- get_tk if TkDO === peek_tk
2843
- end
2844
-
2845
- ##
2846
- # skip the var [in] part of a 'for' statement
2847
-
2848
- def skip_for_variable
2849
- skip_tkspace(false)
2850
- tk = get_tk
2851
- skip_tkspace(false)
2852
- tk = get_tk
2853
- unget_tk(tk) unless TkIN === tk
2854
- end
2855
-
2856
- def skip_method(container)
2857
- meth = RDoc::AnyMethod.new "", "anon"
2858
- parse_method_parameters(meth)
2859
- parse_statements(container, false, meth)
2860
- end
2861
-
2862
- ##
2863
- # Skip spaces
2864
-
2865
- def skip_tkspace(skip_nl = true)
2866
- tokens = []
2867
-
2868
- while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do
2869
- tokens.push tk
2870
- end
2871
-
2872
- unget_tk(tk)
2873
- tokens
2874
- end
2875
-
2876
- ##
2877
- # Skip spaces until a comment is found
2878
-
2879
- def skip_tkspace_comment(skip_nl = true)
2880
- loop do
2881
- skip_tkspace(skip_nl)
2882
- return unless TkCOMMENT === peek_tk
2883
- get_tk
2884
- end
2885
- end
2886
-
2887
- def unget_tk(tk)
2888
- @tokens.unshift tk
2889
- @unget_read.unshift @read.pop
2890
-
2891
- # Remove this token from any listeners
2892
- @token_listeners.each do |obj|
2893
- obj.pop_token
2894
- end if @token_listeners
2895
- end
2896
-
2897
- def warn(msg)
2898
- return if @options.quiet
2899
- msg = make_message msg
2900
- $stderr.puts msg
2901
- end
2902
-
2903
- end
2904
-