Removed llvm and step numbers
[rbx.git] / lib / rdoc / parsers / parse_c.rb
blob5771250cb9daab0f974f46b83207f72205d152ed
1 # Classes and modules built in to the interpreter. We need
2 # these to define superclasses of user objects
4 require "rdoc/code_objects"
5 require "rdoc/parsers/parserfactory"
6 require "rdoc/rdoc"
8 module RDoc
10   ##
11   # Ruby's built-in classes.
13   KNOWN_CLASSES = {
14     "rb_cObject"           => "Object",
15     "rb_cArray"            => "Array",
16     "rb_cBignum"           => "Bignum",
17     "rb_cClass"            => "Class",
18     "rb_cDir"              => "Dir",
19     "rb_cData"             => "Data",
20     "rb_cFalseClass"       => "FalseClass",
21     "rb_cFile"             => "File",
22     "rb_cFixnum"           => "Fixnum",
23     "rb_cFloat"            => "Float",
24     "rb_cHash"             => "Hash",
25     "rb_cInteger"          => "Integer",
26     "rb_cIO"               => "IO",
27     "rb_cModule"           => "Module",
28     "rb_cNilClass"         => "NilClass",
29     "rb_cNumeric"          => "Numeric",
30     "rb_cProc"             => "Proc",
31     "rb_cRange"            => "Range",
32     "rb_cRegexp"           => "Regexp",
33     "rb_cString"           => "String",
34     "rb_cSymbol"           => "Symbol",
35     "rb_cThread"           => "Thread",
36     "rb_cTime"             => "Time",
37     "rb_cTrueClass"        => "TrueClass",
38     "rb_cStruct"           => "Struct",
39     "rb_cVM"               => "VM",
40     "rb_eException"        => "Exception",
41     "rb_eStandardError"    => "StandardError",
42     "rb_eSystemExit"       => "SystemExit",
43     "rb_eInterrupt"        => "Interrupt",
44     "rb_eSignal"           => "Signal",
45     "rb_eFatal"            => "Fatal",
46     "rb_eArgError"         => "ArgError",
47     "rb_eEOFError"         => "EOFError",
48     "rb_eIndexError"       => "IndexError",
49     "rb_eRangeError"       => "RangeError",
50     "rb_eIOError"          => "IOError",
51     "rb_eRuntimeError"     => "RuntimeError",
52     "rb_eSecurityError"    => "SecurityError",
53     "rb_eSystemCallError"  => "SystemCallError",
54     "rb_eTypeError"        => "TypeError",
55     "rb_eZeroDivError"     => "ZeroDivError",
56     "rb_eNotImpError"      => "NotImpError",
57     "rb_eNoMemError"       => "NoMemError",
58     "rb_eFloatDomainError" => "FloatDomainError",
59     "rb_eScriptError"      => "ScriptError",
60     "rb_eNameError"        => "NameError",
61     "rb_eSyntaxError"      => "SyntaxError",
62     "rb_eLoadError"        => "LoadError",
64     "rb_mKernel"           => "Kernel",
65     "rb_mComparable"       => "Comparable",
66     "rb_mEnumerable"       => "Enumerable",
67     "rb_mPrecision"        => "Precision",
68     "rb_mErrno"            => "Errno",
69     "rb_mFileTest"         => "FileTest",
70     "rb_mGC"               => "GC",
71     "rb_mMath"             => "Math",
72     "rb_mProcess"          => "Process"
73   }
75   ##
76   # We attempt to parse C extension files. Basically we look for
77   # the standard patterns that you find in extensions: <tt>rb_define_class,
78   # rb_define_method</tt> and so on. We also try to find the corresponding
79   # C source for the methods and extract comments, but if we fail
80   # we don't worry too much.
81   #
82   # The comments associated with a Ruby method are extracted from the C
83   # comment block associated with the routine that _implements_ that
84   # method, that is to say the method whose name is given in the
85   # <tt>rb_define_method</tt> call. For example, you might write:
86   #
87   #  /*
88   #   * Returns a new array that is a one-dimensional flattening of this
89   #   * array (recursively). That is, for every element that is an array,
90   #   * extract its elements into the new array.
91   #   *
92   #   *    s = [ 1, 2, 3 ]           #=> [1, 2, 3]
93   #   *    t = [ 4, 5, 6, [7, 8] ]   #=> [4, 5, 6, [7, 8]]
94   #   *    a = [ s, t, 9, 10 ]       #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
95   #   *    a.flatten                 #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
96   #   */
97   #   static VALUE
98   #   rb_ary_flatten(ary)
99   #       VALUE ary;
100   #   {
101   #       ary = rb_obj_dup(ary);
102   #       rb_ary_flatten_bang(ary);
103   #       return ary;
104   #   }
105   #
106   #   ...
107   #
108   #   void
109   #   Init_Array()
110   #   {
111   #     ...
112   #     rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
113   #
114   # Here RDoc will determine from the rb_define_method line that there's a
115   # method called "flatten" in class Array, and will look for the implementation
116   # in the method rb_ary_flatten. It will then use the comment from that
117   # method in the HTML output. This method must be in the same source file
118   # as the rb_define_method.
119   #
120   # C classes can be diagramed (see /tc/dl/ruby/ruby/error.c), and RDoc
121   # integrates C and Ruby source into one tree
122   #
123   # The comment blocks may include special direcives:
124   #
125   # [Document-class: <i>name</i>]
126   #   This comment block is documentation for the given class. Use this
127   #   when the <tt>Init_xxx</tt> method is not named after the class.
128   #
129   # [Document-method: <i>name</i>]
130   #   This comment documents the named method. Use when RDoc cannot
131   #   automatically find the method from it's declaration
132   #
133   # [call-seq:  <i>text up to an empty line</i>]
134   #   Because C source doesn't give descripive names to Ruby-level parameters,
135   #   you need to document the calling sequence explicitly
136   #
137   # In additon, RDoc assumes by default that the C method implementing a 
138   # Ruby function is in the same source file as the rb_define_method call.
139   # If this isn't the case, add the comment 
140   #
141   #    rb_define_method(....);  // in: filename
142   #
143   # As an example, we might have an extension that defines multiple classes
144   # in its Init_xxx method. We could document them using
145   #
146   #  
147   #  /*
148   #   * Document-class:  MyClass
149   #   *
150   #   * Encapsulate the writing and reading of the configuration
151   #   * file. ...
152   #   */
153   #  
154   #  /*
155   #   * Document-method: read_value
156   #   *
157   #   * call-seq:
158   #   *   cfg.read_value(key)            -> value
159   #   *   cfg.read_value(key} { |key| }  -> value
160   #   *
161   #   * Return the value corresponding to +key+ from the configuration.
162   #   * In the second form, if the key isn't found, invoke the
163   #   * block and return its value.
164   #   */
165   #
167   class C_Parser
169     attr_writer :progress
171     extend ParserFactory
172     parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
174     @@enclosure_classes = {}
175     @@known_bodies = {}
177     # prepare to parse a C file
178     def initialize(top_level, file_name, body, options, stats)
179       @known_classes = KNOWN_CLASSES.dup
180       @options = options
181       @body = handle_tab_width(handle_ifdefs_in(body))
182       @stats   = stats
183       @top_level = top_level
184       @classes = Hash.new
185       @file_dir = File.dirname(file_name)
186       @progress = $stderr unless @options.quiet
187     end
189     # Extract the classes/modules and methods from a C file
190     # and return the corresponding top-level object
191     def scan
192       remove_commented_out_lines
193       do_classes
194       do_constants
195       do_methods
196       do_includes
197       do_aliases
198       @top_level
199     end
201     #######
202     private
203     #######
205     def progress(char)
206       unless @options.quiet
207         @progress.print(char)
208         @progress.flush
209       end
210     end
212     def warn(msg)
213       $stderr.puts
214       $stderr.puts msg
215       $stderr.flush
216     end
218     def remove_private_comments(comment)
219        comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '')
220        comment.sub!(/\/?\*--.*/m, '')
221     end
223     ##
224     # removes lines that are commented out that might otherwise get picked up
225     # when scanning for classes and methods
227     def remove_commented_out_lines
228       @body.gsub!(%r{//.*rb_define_}, '//')
229     end
230     
231     def handle_class_module(var_name, class_mod, class_name, parent, in_module)
232       progress(class_mod[0, 1])
234       parent_name = @known_classes[parent] || parent
236       if in_module
237         enclosure = @classes[in_module] || @@enclosure_classes[in_module]
238         unless enclosure
239           if enclosure = @known_classes[in_module]
240             handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
241                                 enclosure, nil, nil)
242             enclosure = @classes[in_module]
243           end
244         end
245         unless enclosure
246           warn("Enclosing class/module '#{in_module}' for " +
247                 "#{class_mod} #{class_name} not known")
248           return
249         end
250       else
251         enclosure = @top_level
252       end
254       if class_mod == "class" 
255         cm = enclosure.add_class(NormalClass, class_name, parent_name)
256         @stats.num_classes += 1
257       else
258         cm = enclosure.add_module(NormalModule, class_name)
259         @stats.num_modules += 1
260       end
261       cm.record_location(enclosure.toplevel)
263       find_class_comment(cm.full_name, cm)
264       @classes[var_name] = cm
265       @@enclosure_classes[var_name] = cm
266       @known_classes[var_name] = cm.full_name
267     end
269     ##
270     # Look for class or module documentation above Init_+class_name+(void),
271     # in a Document-class +class_name+ (or module) comment or above an
272     # rb_define_class (or module).  If a comment is supplied above a matching
273     # Init_ and a rb_define_class the Init_ comment is used.
274     #
275     #   /*
276     #    * This is a comment for Foo
277     #    */
278     #   Init_Foo(void) {
279     #       VALUE cFoo = rb_define_class("Foo", rb_cObject);
280     #   }
281     #
282     #   /*
283     #    * Document-class: Foo
284     #    * This is a comment for Foo
285     #    */
286     #   Init_foo(void) {
287     #       VALUE cFoo = rb_define_class("Foo", rb_cObject);
288     #   }
289     #
290     #   /*
291     #    * This is a comment for Foo
292     #    */
293     #   VALUE cFoo = rb_define_class("Foo", rb_cObject);
295     def find_class_comment(class_name, class_meth)
296       comment = nil
297       if @body =~ %r{((?>/\*.*?\*/\s+))
298                      (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi
299         comment = $1
300       elsif @body =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m
301         comment = $2
302       else
303         if @body =~ /rb_define_(class|module)/m then
304           class_name = class_name.split("::").last
305           comments = []
306           @body.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
307             comments[index] = chunk
308             if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then
309               comment = comments[index-1]
310               break
311             end
312           end
313         end
314       end
315       class_meth.comment = mangle_comment(comment) if comment
316     end
317     
318     ############################################################
320     def do_classes
321       @body.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do 
322         |var_name, class_name|
323         handle_class_module(var_name, "module", class_name, nil, nil)
324       end
325       
326       # The '.' lets us handle SWIG-generated files
327       @body.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
328                 \( 
329                    \s*"(\w+)",
330                    \s*(\w+)\s*
331                 \)/mx) do 
332         
333         |var_name, class_name, parent|
334         handle_class_module(var_name, "class", class_name, parent, nil)
335       end
336       
337       @body.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
338         |var_name, class_name, parent|
339         parent = nil if parent == "0"
340         handle_class_module(var_name, "class", class_name, parent, nil)
341       end
343       @body.scan(/(\w+)\s* = \s*rb_define_module_under\s*
344                 \( 
345                    \s*(\w+),
346                    \s*"(\w+)"
347                 \s*\)/mx) do 
348         
349         |var_name, in_module, class_name|
350         handle_class_module(var_name, "module", class_name, nil, in_module)
351       end
352       
353       @body.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
354                 \( 
355                    \s*(\w+),
356                    \s*"(\w+)",
357                    \s*(\w+)\s*
358                 \s*\)/mx) do 
359         
360         |var_name, in_module, class_name, parent|
361         handle_class_module(var_name, "class", class_name, parent, in_module)
362       end
363       
364     end
366                 ###########################################################
368     def do_constants
369       @body.scan(%r{\Wrb_define_
370                      (
371                         variable |
372                         readonly_variable |
373                         const |
374                         global_const |
375                       )
376                  \s*\( 
377                    (?:\s*(\w+),)?
378                    \s*"(\w+)",
379                    \s*(.*?)\s*\)\s*;
380                    }xm) do
381         
382         |type, var_name, const_name, definition|
383         var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
384                                 handle_constants(type, var_name, const_name, definition)
385       end
386     end
387     
388     ############################################################
389     
390     def do_methods
392       @body.scan(%r{rb_define_
393                      (
394                         singleton_method |
395                         method           |
396                         module_function  |
397                         private_method
398                      )
399                      \s*\(\s*([\w\.]+),
400                        \s*"([^"]+)",
401                        \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
402                        \s*(-?\w+)\s*\)
403                      (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
404                    }xm) do
405         |type, var_name, meth_name, meth_body, param_count, source_file|
406        #" 
408         # Ignore top-object and weird struct.c dynamic stuff
409         next if var_name == "ruby_top_self" 
410         next if var_name == "nstr"
411         next if var_name == "envtbl"
412         next if var_name == "argf"   # it'd be nice to handle this one
414         var_name = "rb_cObject" if var_name == "rb_mKernel"
415         handle_method(type, var_name, meth_name, 
416                       meth_body, param_count, source_file)
417       end
419       @body.scan(%r{rb_define_attr\(
420                                \s*([\w\.]+),
421                                \s*"([^"]+)",
422                                \s*(\d+),
423                                \s*(\d+)\s*\);
424                   }xm) do  #"
425         |var_name, attr_name, attr_reader, attr_writer|
426         
427         #var_name = "rb_cObject" if var_name == "rb_mKernel"
428         handle_attr(var_name, attr_name,
429                     attr_reader.to_i != 0,
430                     attr_writer.to_i != 0)
431       end
433       @body.scan(%r{rb_define_global_function\s*\(
434                                \s*"([^"]+)",
435                                \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
436                                \s*(-?\w+)\s*\)
437                   (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
438                   }xm) do  #"
439         |meth_name, meth_body, param_count, source_file|
440         handle_method("method", "rb_mKernel", meth_name, 
441                       meth_body, param_count, source_file)
442       end
443   
444       @body.scan(/define_filetest_function\s*\(
445                                \s*"([^"]+)",
446                                \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
447                                \s*(-?\w+)\s*\)/xm) do  #"
448         |meth_name, meth_body, param_count|
449         
450         handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
451         handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
452       end
453    end
455     ############################################################
456     
457     def do_aliases
458       @body.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
459         |var_name, new_name, old_name|
460         @stats.num_methods += 1
461         class_name = @known_classes[var_name] || var_name
462         class_obj  = find_class(var_name, class_name)
464         class_obj.add_alias(Alias.new("", old_name, new_name, ""))
465       end
466    end
468     ##
469     # Adds constant comments.  By providing some_value: at the start ofthe
470     # comment you can override the C value of the comment to give a friendly
471     # definition.
472     #
473     #   /* 300: The perfect score in bowling */
474     #   rb_define_const(cFoo, "PERFECT", INT2FIX(300);
475     #
476     # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc.
477     # Values may include quotes and escaped colons (\:).
479     def handle_constants(type, var_name, const_name, definition)
480       #@stats.num_constants += 1
481       class_name = @known_classes[var_name]
482       
483       return unless class_name
485       class_obj  = find_class(var_name, class_name)
487       unless class_obj
488         warn("Enclosing class/module '#{const_name}' for not known")
489         return
490       end
491       
492       comment = find_const_comment(type, const_name)
494       # In the case of rb_define_const, the definition and comment are in
495       # "/* definition: comment */" form.  The literal ':' and '\' characters
496       # can be escaped with a backslash.
497       if type.downcase == 'const' then
498          elements = mangle_comment(comment).split(':')
499          if elements.nil? or elements.empty? then
500             con = Constant.new(const_name, definition, mangle_comment(comment))
501          else
502             new_definition = elements[0..-2].join(':')
503             if new_definition.empty? then # Default to literal C definition
504                new_definition = definition
505             else
506                new_definition.gsub!("\:", ":")
507                new_definition.gsub!("\\", '\\')
508             end
509             new_definition.sub!(/\A(\s+)/, '')
510             new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
511             con = Constant.new(const_name, new_definition,
512                                mangle_comment(new_comment))
513          end
514       else
515          con = Constant.new(const_name, definition, mangle_comment(comment))
516       end
518       class_obj.add_constant(con)
519     end
521     ##
522     # Finds a comment matching +type+ and +const_name+ either above the
523     # comment or in the matching Document- section.
525     def find_const_comment(type, const_name)
526       if @body =~ %r{((?>^\s*/\*.*?\*/\s+))
527                      rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
528         $1
529       elsif @body =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
530         $1
531       else
532         ''
533       end
534     end
536     ###########################################################
538     def handle_attr(var_name, attr_name, reader, writer)
539       rw = ''
540       if reader 
541         #@stats.num_methods += 1
542         rw << 'R'
543       end
544       if writer
545         #@stats.num_methods += 1
546         rw << 'W'
547       end
549       class_name = @known_classes[var_name]
551       return unless class_name
552       
553       class_obj  = find_class(var_name, class_name)
555       if class_obj
556         comment = find_attr_comment(attr_name)
557         unless comment.empty?
558           comment = mangle_comment(comment)
559         end
560         att = Attr.new('', attr_name, rw, comment)
561         class_obj.add_attribute(att)
562       end
564     end
566     ###########################################################
568     def find_attr_comment(attr_name)
569       if @body =~ %r{((?>/\*.*?\*/\s+))
570                      rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
571         $1
572       elsif @body =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
573         $1
574       else
575         ''
576       end
577     end
579     ###########################################################
581     def handle_method(type, var_name, meth_name, 
582                       meth_body, param_count, source_file = nil)
583       progress(".")
585       @stats.num_methods += 1
586       class_name = @known_classes[var_name]
588       return unless class_name
590       class_obj  = find_class(var_name, class_name)
591       
592       if class_obj
593         if meth_name == "initialize"
594           meth_name = "new"
595           type = "singleton_method"
596         end
597         meth_obj = AnyMethod.new("", meth_name)
598         meth_obj.singleton =
599           %w{singleton_method module_function}.include?(type) 
600         
601         p_count = (Integer(param_count) rescue -1)
602         
603         if p_count < 0
604           meth_obj.params = "(...)"
605         elsif p_count == 0
606           meth_obj.params = "()"
607         else
608           meth_obj.params = "(" +
609                             (1..p_count).map{|i| "p#{i}"}.join(", ") + 
610                                                 ")"
611         end
613         if source_file
614           file_name = File.join(@file_dir, source_file)
615           body = (@@known_bodies[source_file] ||= File.read(file_name))
616         else
617           body = @body
618         end
619         if find_body(meth_body, meth_obj, body) and meth_obj.document_self
620           class_obj.add_method(meth_obj)
621         end
622       end
623     end
624     
625     ############################################################
627     # Find the C code corresponding to a Ruby method
628     def find_body(meth_name, meth_obj, body, quiet = false)
629       case body
630       when %r"((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name}
631               \s*(\([^)]*\))\s*\{.*?^\}"xm
632         comment, params = $1, $2
633         body_text = $&
635         remove_private_comments(comment) if comment
637         # see if we can find the whole body
638         
639         re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
640         if Regexp.new(re, Regexp::MULTILINE).match(body)
641           body_text = $&
642         end
644         # The comment block may have been overridden with a
645         # 'Document-method' block. This happens in the interpreter
646         # when multiple methods are vectored through to the same
647         # C method but those methods are logically distinct (for
648         # example Kernel.hash and Kernel.object_id share the same
649         # implementation
651         override_comment = find_override_comment(meth_obj.name)
652         comment = override_comment if override_comment
654         find_modifiers(comment, meth_obj) if comment
655         
656 #        meth_obj.params = params
657         meth_obj.start_collecting_tokens
658         meth_obj.add_token(RubyToken::Token.new(1,1).set_text(body_text))
659         meth_obj.comment = mangle_comment(comment)
660       when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
661         comment = $1
662         find_body($2, meth_obj, body, true)
663         find_modifiers(comment, meth_obj)
664         meth_obj.comment = mangle_comment(comment) + meth_obj.comment
665       when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
666         unless find_body($1, meth_obj, body, true)
667           warn "No definition for #{meth_name}" unless quiet
668           return false
669         end
670       else
672         # No body, but might still have an override comment
673         comment = find_override_comment(meth_obj.name)
675         if comment
676           find_modifiers(comment, meth_obj)
677           meth_obj.comment = mangle_comment(comment)
678         else
679           warn "No definition for #{meth_name}" unless quiet
680           return false
681         end
682       end
683       true
684     end
687     ##
688     # If the comment block contains a section that looks like:
689     #
690     #    call-seq:
691     #        Array.new
692     #        Array.new(10)
693     #
694     # use it for the parameters.
696     def find_modifiers(comment, meth_obj)
697       if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
698          comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
699         meth_obj.document_self = false
700       end
701       if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
702          comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
703         seq = $1
704         seq.gsub!(/^\s*\*\s*/, '')
705         meth_obj.call_seq = seq
706       end
707     end
709     ############################################################
711     def find_override_comment(meth_name)
712       name = Regexp.escape(meth_name)
713       if @body =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m
714         $1
715       end
716     end
718     ##
719     # Look for includes of the form:
720     #
721     #     rb_include_module(rb_cArray, rb_mEnumerable);
723     def do_includes
724       @body.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
725         if cls = @classes[c]
726           m = @known_classes[m] || m
727           cls.add_include(Include.new(m, ""))
728         end
729       end
730     end
732     ##
733     # Remove the /*'s and leading asterisks from C comments
734     
735     def mangle_comment(comment)
736       comment.sub!(%r{/\*+}) { " " * $&.length }
737       comment.sub!(%r{\*+/}) { " " * $&.length }
738       comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
739       comment
740     end
742     def find_class(raw_name, name)
743       unless @classes[raw_name]
744         if raw_name =~ /^rb_m/ 
745           @classes[raw_name] = @top_level.add_module(NormalModule, name)
746         else
747           @classes[raw_name] = @top_level.add_class(NormalClass, name, nil)
748         end
749       end
750       @classes[raw_name]
751     end
753     def handle_tab_width(body)
754       if /\t/ =~ body
755         tab_width = @options.tab_width
756         body.split(/\n/).map do |line|
757           1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)}  && $~ #`
758           line
759         end .join("\n")
760       else
761         body
762       end
763     end
765     ##
766     # Removes #ifdefs that would otherwise confuse us
767     
768     def handle_ifdefs_in(body)
769       body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
770     end
771     
772   end