rdoc-f95 0.0.1

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