rdoc-f95 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+