voloko-sdoc 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/rdoc/History.txt +254 -0
  2. data/rdoc/Manifest.txt +126 -0
  3. data/rdoc/README.txt +47 -0
  4. data/rdoc/RI.txt +58 -0
  5. data/rdoc/Rakefile +70 -0
  6. data/rdoc/bin/rdoc +35 -0
  7. data/rdoc/bin/ri +5 -0
  8. data/rdoc/lib/rdoc/alias.rb +54 -0
  9. data/rdoc/lib/rdoc/anon_class.rb +10 -0
  10. data/rdoc/lib/rdoc/any_method.rb +190 -0
  11. data/rdoc/lib/rdoc/attr.rb +79 -0
  12. data/rdoc/lib/rdoc/cache.rb +41 -0
  13. data/rdoc/lib/rdoc/class_module.rb +87 -0
  14. data/rdoc/lib/rdoc/code_object.rb +152 -0
  15. data/rdoc/lib/rdoc/code_objects.rb +23 -0
  16. data/rdoc/lib/rdoc/constant.rb +36 -0
  17. data/rdoc/lib/rdoc/context.rb +712 -0
  18. data/rdoc/lib/rdoc/diagram.rb +340 -0
  19. data/rdoc/lib/rdoc/dot.rb +249 -0
  20. data/rdoc/lib/rdoc/generator/darkfish.rb +455 -0
  21. data/rdoc/lib/rdoc/generator/markup.rb +194 -0
  22. data/rdoc/lib/rdoc/generator/ri.rb +230 -0
  23. data/rdoc/lib/rdoc/generator/template/darkfish/classpage.rhtml +281 -0
  24. data/rdoc/lib/rdoc/generator/template/darkfish/filepage.rhtml +112 -0
  25. data/rdoc/lib/rdoc/generator/template/darkfish/images/brick.png +0 -0
  26. data/rdoc/lib/rdoc/generator/template/darkfish/images/brick_link.png +0 -0
  27. data/rdoc/lib/rdoc/generator/template/darkfish/images/bug.png +0 -0
  28. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_black.png +0 -0
  29. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png +0 -0
  30. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png +0 -0
  31. data/rdoc/lib/rdoc/generator/template/darkfish/images/date.png +0 -0
  32. data/rdoc/lib/rdoc/generator/template/darkfish/images/find.png +0 -0
  33. data/rdoc/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif +0 -0
  34. data/rdoc/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png +0 -0
  35. data/rdoc/lib/rdoc/generator/template/darkfish/images/package.png +0 -0
  36. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_green.png +0 -0
  37. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_white_text.png +0 -0
  38. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_white_width.png +0 -0
  39. data/rdoc/lib/rdoc/generator/template/darkfish/images/plugin.png +0 -0
  40. data/rdoc/lib/rdoc/generator/template/darkfish/images/ruby.png +0 -0
  41. data/rdoc/lib/rdoc/generator/template/darkfish/images/tag_green.png +0 -0
  42. data/rdoc/lib/rdoc/generator/template/darkfish/images/wrench.png +0 -0
  43. data/rdoc/lib/rdoc/generator/template/darkfish/images/wrench_orange.png +0 -0
  44. data/rdoc/lib/rdoc/generator/template/darkfish/images/zoom.png +0 -0
  45. data/rdoc/lib/rdoc/generator/template/darkfish/index.rhtml +64 -0
  46. data/rdoc/lib/rdoc/generator/template/darkfish/js/darkfish.js +116 -0
  47. data/rdoc/lib/rdoc/generator/template/darkfish/js/jquery.js +32 -0
  48. data/rdoc/lib/rdoc/generator/template/darkfish/js/quicksearch.js +114 -0
  49. data/rdoc/lib/rdoc/generator/template/darkfish/js/thickbox-compressed.js +10 -0
  50. data/rdoc/lib/rdoc/generator/template/darkfish/rdoc.css +696 -0
  51. data/rdoc/lib/rdoc/generator.rb +8 -0
  52. data/rdoc/lib/rdoc/ghost_method.rb +8 -0
  53. data/rdoc/lib/rdoc/include.rb +39 -0
  54. data/rdoc/lib/rdoc/known_classes.rb +68 -0
  55. data/rdoc/lib/rdoc/markup/attribute_manager.rb +311 -0
  56. data/rdoc/lib/rdoc/markup/formatter.rb +25 -0
  57. data/rdoc/lib/rdoc/markup/fragments.rb +377 -0
  58. data/rdoc/lib/rdoc/markup/inline.rb +126 -0
  59. data/rdoc/lib/rdoc/markup/lines.rb +156 -0
  60. data/rdoc/lib/rdoc/markup/preprocess.rb +80 -0
  61. data/rdoc/lib/rdoc/markup/to_flow.rb +211 -0
  62. data/rdoc/lib/rdoc/markup/to_html.rb +406 -0
  63. data/rdoc/lib/rdoc/markup/to_html_crossref.rb +140 -0
  64. data/rdoc/lib/rdoc/markup/to_latex.rb +328 -0
  65. data/rdoc/lib/rdoc/markup/to_test.rb +53 -0
  66. data/rdoc/lib/rdoc/markup/to_texinfo.rb +73 -0
  67. data/rdoc/lib/rdoc/markup.rb +378 -0
  68. data/rdoc/lib/rdoc/meta_method.rb +8 -0
  69. data/rdoc/lib/rdoc/normal_class.rb +18 -0
  70. data/rdoc/lib/rdoc/normal_module.rb +34 -0
  71. data/rdoc/lib/rdoc/options.rb +542 -0
  72. data/rdoc/lib/rdoc/parser/c.rb +678 -0
  73. data/rdoc/lib/rdoc/parser/perl.rb +165 -0
  74. data/rdoc/lib/rdoc/parser/ruby.rb +2904 -0
  75. data/rdoc/lib/rdoc/parser/simple.rb +39 -0
  76. data/rdoc/lib/rdoc/parser.rb +138 -0
  77. data/rdoc/lib/rdoc/rdoc.rb +375 -0
  78. data/rdoc/lib/rdoc/require.rb +32 -0
  79. data/rdoc/lib/rdoc/ri/cache.rb +187 -0
  80. data/rdoc/lib/rdoc/ri/descriptions.rb +156 -0
  81. data/rdoc/lib/rdoc/ri/display.rb +340 -0
  82. data/rdoc/lib/rdoc/ri/driver.rb +828 -0
  83. data/rdoc/lib/rdoc/ri/formatter.rb +654 -0
  84. data/rdoc/lib/rdoc/ri/paths.rb +93 -0
  85. data/rdoc/lib/rdoc/ri/reader.rb +106 -0
  86. data/rdoc/lib/rdoc/ri/util.rb +79 -0
  87. data/rdoc/lib/rdoc/ri/writer.rb +68 -0
  88. data/rdoc/lib/rdoc/ri.rb +8 -0
  89. data/rdoc/lib/rdoc/single_class.rb +8 -0
  90. data/rdoc/lib/rdoc/stats.rb +178 -0
  91. data/rdoc/lib/rdoc/task.rb +276 -0
  92. data/rdoc/lib/rdoc/tokenstream.rb +33 -0
  93. data/rdoc/lib/rdoc/top_level.rb +242 -0
  94. data/rdoc/lib/rdoc.rb +398 -0
  95. metadata +1 -1
@@ -0,0 +1,2904 @@
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
+