class RDoc::Parser::C

RDoc::Parser::C attempts to parse C extension files. It looks for the standard patterns that you find in extensions: rb_define_class, rb_define_method and so on. It tries to find the corresponding C source for the methods and extract comments, but if we fail we don't worry too much.

The comments associated with a Ruby method are extracted from the C comment block associated with the routine that implements that method, that is to say the method whose name is given in the rb_define_method call. For example, you might write:

 Returns a new array that is a one-dimensional flattening of this
 array (recursively). That is, for every element that is an array,
 extract its elements into the new array.
 s = [ 1, 2, 3 ] #=> [1, 2, 3]
 t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
 a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
 a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
static VALUE
rb_ary_flatten(ary)
 VALUE ary;
{
 ary = rb_obj_dup(ary);
 rb_ary_flatten_bang(ary);
 return ary;
}
...
void
Init_Array()
{
 ...
 rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);

Here RDoc will determine from the rb_define_method line that there's a method called "flatten" in class Array, and will look for the implementation in the method rb_ary_flatten. It will then use the comment from that method in the HTML output. This method must be in the same source file as the rb_define_method.

The comment blocks may include special directives:

Document-class: name

Documentation for the named class.

Document-module: name

Documentation for the named module.

Document-const: name

Documentation for the named rb_define_const.

Constant values can be supplied on the first line of the comment like so:

 300: The highest possible score in bowling 
rb_define_const(cFoo, "PERFECT", INT2FIX(300));

The value can contain internal colons so long as they are escaped with a \

Document-global: name

Documentation for the named rb_define_global_const

Document-variable: name

Documentation for the named rb_define_variable

Document-method: method_name

Documentation for the named method. Use this when the method name is unambiguous.

Document-method: <tt>ClassName::method_name<tt>

Documentation for a singleton method in the given class. Use this when the method name alone is ambiguous.

Document-method: <tt>ClassName#method_name<tt>

Documentation for a instance method in the given class. Use this when the method name alone is ambiguous.

Document-attr: name

Documentation for the named attribute.

call-seq: text up to an empty line

Because C source doesn't give descriptive names to Ruby-level parameters, you need to document the calling sequence explicitly

In addition, RDoc assumes by default that the C method implementing a Ruby function is in the same source file as the rb_define_method call. If this isn't the case, add the comment:

rb_define_method(....); // in filename

As an example, we might have an extension that defines multiple classes in its Init_xxx method. We could document them using

/*
 Document-class: MyClass
 Encapsulate the writing and reading of the configuration
 file. ...
 /
/*
 call-seq:
 cfg.read_value(key) -> value
 cfg.read_value(key} { |key| } -> value
 Return the value corresponding to +key+ from the configuration.
 In the second form, if the key isn't found, invoke the
 block and return its value.
 /

Attributes

classes[R]

Maps C variable names to names of Ruby classes or modules

content[RW]

C file the parser is parsing

enclosure_dependencies[R]

Dependencies from a missing enclosing class to the classes in #missing_dependencies that depend upon it.

known_classes[R]

Maps C variable names to names of Ruby classes (and singleton classes)

missing_dependencies[R]

Classes found while parsing the C file that were not yet registered due to a missing enclosing class. These are processed by #do_missing

singleton_classes[R]

Maps C variable names to names of Ruby singleton classes

top_level[R]

The TopLevel items in the parsed file belong to

Public Class Methods

new(top_level, file_name, content, options, stats) click to toggle source

Prepares for parsing a C file. See RDoc::Parser#initialize for details on the arguments.

Calls superclass method RDoc::Parser.new
# File lib/rdoc/parser/c.rb, line 166
def initialize top_level, file_name, content, options, stats
 super
 @known_classes = RDoc::KNOWN_CLASSES.dup
 @content = handle_tab_width handle_ifdefs_in @content
 @file_dir = File.dirname @file_name
 @classes = load_variable_map :c_class_variables
 @singleton_classes = load_variable_map :c_singleton_class_variables
 # class_variable => { function => [method, ...] }
 @methods = Hash.new { |h, f| h[f] = Hash.new { |i, m| i[m] = [] } }
 # missing variable => [handle_class_module arguments]
 @missing_dependencies = {}
 # missing enclosure variable => [dependent handle_class_module arguments]
 @enclosure_dependencies = Hash.new { |h, k| h[k] = [] }
 @enclosure_dependencies.instance_variable_set :@missing_dependencies,
 @missing_dependencies
 @enclosure_dependencies.extend TSort
 def @enclosure_dependencies.tsort_each_node &block
 each_key(&block)
 rescue TSort::Cyclic => e
 cycle_vars = e.message.scan(/"(.*?)"/).flatten
 cycle = cycle_vars.sort.map do |var_name|
 delete var_name
 var_name, type, mod_name, = @missing_dependencies[var_name]
 "#{type} #{mod_name} (#{var_name})"
 end.join ', '
 warn "Unable to create #{cycle} due to a cyclic class or module creation"
 retry
 end
 def @enclosure_dependencies.tsort_each_child node, &block
 fetch(node, []).each(&block)
 end
end

Public Instance Methods

deduplicate_call_seq() click to toggle source

Removes duplicate call-seq entries for methods using the same implementation.

# File lib/rdoc/parser/c.rb, line 216
def deduplicate_call_seq
 @methods.each do |var_name, functions|
 class_name = @known_classes[var_name]
 class_obj = find_class var_name, class_name
 functions.each_value do |method_names|
 next if method_names.length == 1
 method_names.each do |method_name|
 deduplicate_method_name class_obj, method_name
 end
 end
 end
end
do_aliases() click to toggle source

Scans content for rb_define_alias

# File lib/rdoc/parser/c.rb, line 256
def do_aliases
 @content.scan(/rb_define_alias\s*\(
 \s*(\w+),
 \s*"(.+?)",
 \s*"(.+?)"
 \s*\)/xm) do |var_name, new_name, old_name|
 class_name = @known_classes[var_name]
 unless class_name then
 @options.warn "Enclosing class or module %p for alias %s %s is not known" % [
 var_name, new_name, old_name]
 next
 end
 class_obj = find_class var_name, class_name
 al = RDoc::Alias.new '', old_name, new_name, ''
 al.singleton = @singleton_classes.key? var_name
 comment = find_alias_comment var_name, new_name, old_name
 comment.normalize
 al.comment = comment
 al.record_location @top_level
 class_obj.add_alias al
 @stats.add_alias al
 end
end
do_attrs() click to toggle source

Scans content for rb_attr and rb_define_attr

# File lib/rdoc/parser/c.rb, line 291
def do_attrs
 @content.scan(/rb_attr\s*\(
 \s*(\w+),
 \s*([\w"()]+),
 \s*([01]),
 \s*([01]),
 \s*\w+\);/xm) do |var_name, attr_name, read, write|
 handle_attr var_name, attr_name, read, write
 end
 @content.scan(%r%rb_define_attr\(
 \s*([\w\.]+),
 \s*"([^"]+)",
 \s*(\d+),
 \s*(\d+)\s*\);
 %xm) do |var_name, attr_name, read, write|
 handle_attr var_name, attr_name, read, write
 end
end
do_boot_defclass() click to toggle source

Scans content for boot_defclass

# File lib/rdoc/parser/c.rb, line 314
def do_boot_defclass
 @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
 |var_name, class_name, parent|
 parent = nil if parent == "0"
 handle_class_module(var_name, :class, class_name, parent, nil)
 end
end
do_classes() click to toggle source

Scans content for rb_define_class, boot_defclass, rb_define_class_under and rb_singleton_class

# File lib/rdoc/parser/c.rb, line 326
def do_classes
 do_boot_defclass
 do_define_class
 do_define_class_under
 do_singleton_class
 do_struct_define_without_accessor
end
do_constants() click to toggle source

Scans content for rb_define_variable, rb_define_readonly_variable, rb_define_const and rb_define_global_const

# File lib/rdoc/parser/c.rb, line 338
def do_constants
 @content.scan(%r%\Wrb_define_
 ( variable |
 readonly_variable |
 const |
 global_const )
 \s*\(
 (?:\s*(\w+),)?
 \s*"(\w+)",
 \s*(.*?)\s*\)\s*;
 %xm) do |type, var_name, const_name, definition|
 var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
 handle_constants type, var_name, const_name, definition
 end
 @content.scan(%r%
 \Wrb_curses_define_const
 \s*\(
 \s*
 (\w+)
 \s*
 \)
 \s*;%xm) do |consts|
 const = consts.first
 handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})"
 end
 @content.scan(%r%
 \Wrb_file_const
 \s*\(
 \s*
 "([^"]+)",
 \s*
 (.*?)
 \s*
 \)
 \s*;%xm) do |name, value|
 handle_constants 'const', 'rb_mFConst', name, value
 end
end
do_define_class() click to toggle source

Scans content for rb_define_class

# File lib/rdoc/parser/c.rb, line 383
def do_define_class
 # The '.' lets us handle SWIG-generated files
 @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
 \(
 \s*"(\w+)",
 \s*(\w+)\s*
 \)/mx) do |var_name, class_name, parent|
 handle_class_module(var_name, :class, class_name, parent, nil)
 end
end
do_define_class_under() click to toggle source

Scans content for rb_define_class_under

# File lib/rdoc/parser/c.rb, line 397
def do_define_class_under
 @content.scan(/([\w\.]+)\s* = # var_name
 \s*rb_define_class_under\s*
 \(
 \s* (\w+), # under
 \s* "(\w+)", # class_name
 \s*
 (?:
 ([\w\*\s\(\)\.\->]+) | # parent_name
 rb_path2class\("([\w:]+)"\) # path
 )
 \s*
 \)
 /mx) do |var_name, under, class_name, parent_name, path|
 parent = path || parent_name
 handle_class_module var_name, :class, class_name, parent, under
 end
end
do_define_module() click to toggle source

Scans content for rb_define_module

# File lib/rdoc/parser/c.rb, line 420
def do_define_module
 @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
 |var_name, class_name|
 handle_class_module(var_name, :module, class_name, nil, nil)
 end
end
do_define_module_under() click to toggle source

Scans content for rb_define_module_under

# File lib/rdoc/parser/c.rb, line 430
def do_define_module_under
 @content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
 \(
 \s*(\w+),
 \s*"(\w+)"
 \s*\)/mx) do |var_name, in_module, class_name|
 handle_class_module(var_name, :module, class_name, nil, in_module)
 end
end
do_includes() click to toggle source

Scans content for rb_include_module

# File lib/rdoc/parser/c.rb, line 443
def do_includes
 @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
 next unless cls = @classes[c]
 m = @known_classes[m] || m
 comment = RDoc::Comment.new '', @top_level
 incl = cls.add_include RDoc::Include.new(m, comment)
 incl.record_location @top_level
 end
end
do_methods() click to toggle source

Scans content for rb_define_method, rb_define_singleton_method, rb_define_module_function, rb_define_private_method, rb_define_global_function and define_filetest_function

# File lib/rdoc/parser/c.rb, line 459
def do_methods
 @content.scan(%r%rb_define_
 (
 singleton_method |
 method |
 module_function |
 private_method
 )
 \s*\(\s*([\w\.]+),
 \s*"([^"]+)",
 \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\(|\(METHOD\))?(\w+)\)?,
 \s*(-?\w+)\s*\)
 (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))?
 %xm) do |type, var_name, meth_name, function, param_count, source_file|
 # Ignore top-object and weird struct.c dynamic stuff
 next if var_name == "ruby_top_self"
 next if var_name == "nstr"
 var_name = "rb_cObject" if var_name == "rb_mKernel"
 handle_method(type, var_name, meth_name, function, param_count,
 source_file)
 end
 @content.scan(%r%rb_define_global_function\s*\(
 \s*"([^"]+)",
 \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
 \s*(-?\w+)\s*\)
 (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
 %xm) do |meth_name, function, param_count, source_file|
 handle_method("method", "rb_mKernel", meth_name, function, param_count,
 source_file)
 end
 @content.scan(/define_filetest_function\s*\(
 \s*"([^"]+)",
 \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
 \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count|
 handle_method("method", "rb_mFileTest", meth_name, function, param_count)
 handle_method("singleton_method", "rb_cFile", meth_name, function,
 param_count)
 end
end
do_missing() click to toggle source

Creates classes and module that were missing were defined due to the file order being different than the declaration order.

# File lib/rdoc/parser/c.rb, line 508
def do_missing
 return if @missing_dependencies.empty?
 @enclosure_dependencies.tsort.each do |in_module|
 arguments = @missing_dependencies.delete in_module
 next unless arguments # dependency on existing class
 handle_class_module(*arguments)
 end
end
do_modules() click to toggle source

Scans content for rb_define_module and rb_define_module_under

# File lib/rdoc/parser/c.rb, line 523
def do_modules
 do_define_module
 do_define_module_under
end
do_singleton_class() click to toggle source

Scans content for rb_singleton_class

# File lib/rdoc/parser/c.rb, line 531
def do_singleton_class
 @content.scan(/([\w\.]+)\s* = \s*rb_singleton_class\s*
 \(
 \s*(\w+)
 \s*\)/mx) do |sclass_var, class_var|
 handle_singleton sclass_var, class_var
 end
end
do_struct_define_without_accessor() click to toggle source

Scans content for struct_define_without_accessor

# File lib/rdoc/parser/c.rb, line 543
def do_struct_define_without_accessor
 @content.scan(/([\w\.]+)\s* = \s*rb_struct_define_without_accessor\s*
 \(
 \s*"(\w+)", # Class name
 \s*(\w+), # Parent class
 \s*\w+, # Allocation function
 (\s*"\w+",)* # Attributes
 \s*NULL
 \)/mx) do |var_name, class_name, parent|
 handle_class_module(var_name, :class, class_name, parent, nil)
 end
end
find_alias_comment(class_name, new_name, old_name) click to toggle source

Finds the comment for an alias on class_name from new_name to old_name

# File lib/rdoc/parser/c.rb, line 560
def find_alias_comment class_name, new_name, old_name
 content =~ %r%((?>/\*.*?\*/\s+))
 rb_define_alias\(\s*#{Regexp.escape class_name}\s*,
 \s*"#{Regexp.escape new_name}"\s*,
 \s*"#{Regexp.escape old_name}"\s*\);%xm
 RDoc::Comment.new(1ドル || '', @top_level)
end
find_attr_comment(var_name, attr_name, read = nil, write = nil) click to toggle source

Finds a comment for rb_define_attr, rb_attr or Document-attr.

var_name is the C class variable the attribute is defined on. attr_name is the attribute's name.

read and write are the read/write flags ('1' or '0'). Either both or neither must be provided.

# File lib/rdoc/parser/c.rb, line 578
def find_attr_comment var_name, attr_name, read = nil, write = nil
 attr_name = Regexp.escape attr_name
 rw = if read and write then
 /\s*#{read}\s*,\s*#{write}\s*/xm
 else
 /.*?/m
 end
 comment = if @content =~ %r%((?>/\*.*?\*/\s+))
 rb_define_attr\((?:\s*#{var_name},)?\s*
 "#{attr_name}"\s*,
 #{rw}\)\s*;%xm then
 1ドル
 elsif @content =~ %r%((?>/\*.*?\*/\s+))
 rb_attr\(\s*#{var_name}\s*,
 \s*#{attr_name}\s*,
 #{rw},.*?\)\s*;%xm then
 1ドル
 elsif @content =~ %r%Document-attr:\s#{attr_name}\s*?\n
 ((?>.*?\*/))%xm then
 1ドル
 else
 ''
 end
 RDoc::Comment.new comment, @top_level
end
find_body(class_name, meth_name, meth_obj, file_content, quiet = false) click to toggle source

Find the C code corresponding to a Ruby method

# File lib/rdoc/parser/c.rb, line 610
def find_body class_name, meth_name, meth_obj, file_content, quiet = false
 case file_content
 when %r%((?>/\*.*?\*/\s*)?)
 ((?:(?:static|SWIGINTERN)\s+)?
 (?:intern\s+)?VALUE\s+#{meth_name}
 \s*(\([^)]*\))([^;]|$))%xm then
 comment = RDoc::Comment.new 1ドル, @top_level
 body = 2ドル
 offset, = $~.offset(2)
 comment.remove_private if comment
 # try to find the whole body
 body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content
 # The comment block may have been overridden with a 'Document-method'
 # block. This happens in the interpreter when multiple methods are
 # vectored through to the same C method but those methods are logically
 # distinct (for example Kernel.hash and Kernel.object_id share the same
 # implementation
 override_comment = find_override_comment class_name, meth_obj
 comment = override_comment if override_comment
 comment.normalize
 find_modifiers comment, meth_obj if comment
 #meth_obj.params = params
 meth_obj.start_collecting_tokens
 tk = RDoc::RubyToken::Token.new nil, 1, 1
 tk.set_text body
 meth_obj.add_token tk
 meth_obj.comment = comment
 meth_obj.offset = offset
 meth_obj.line = file_content[0, offset].count("\n") + 1
 body
 when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m then
 comment = RDoc::Comment.new 1ドル, @top_level
 body = 2ドル
 offset = $~.offset(2).first
 find_body class_name, 3ドル, meth_obj, file_content, true
 comment.normalize
 find_modifiers comment, meth_obj
 meth_obj.start_collecting_tokens
 tk = RDoc::RubyToken::Token.new nil, 1, 1
 tk.set_text body
 meth_obj.add_token tk
 meth_obj.comment = comment
 meth_obj.offset = offset
 meth_obj.line = file_content[0, offset].count("\n") + 1
 body
 when %r%^\s*\#\s*define\s+#{meth_name}\s+(\w+)%m then
 # with no comment we hope the aliased definition has it and use it's
 # definition
 body = find_body(class_name, 1ドル, meth_obj, file_content, true)
 return body if body
 @options.warn "No definition for #{meth_name}"
 false
 else # No body, but might still have an override comment
 comment = find_override_comment class_name, meth_obj
 if comment then
 comment.normalize
 find_modifiers comment, meth_obj
 meth_obj.comment = comment
 ''
 else
 @options.warn "No definition for #{meth_name}"
 false
 end
 end
end
find_class(raw_name, name) click to toggle source

Finds a RDoc::NormalClass or RDoc::NormalModule for raw_name

# File lib/rdoc/parser/c.rb, line 695
def find_class(raw_name, name)
 unless @classes[raw_name]
 if raw_name =~ /^rb_m/
 container = @top_level.add_module RDoc::NormalModule, name
 else
 container = @top_level.add_class RDoc::NormalClass, name
 end
 container.record_location @top_level
 @classes[raw_name] = container
 end
 @classes[raw_name]
end
find_class_comment(class_name, class_mod) click to toggle source

Look for class or module documentation above Init_+class_name+(void), in a Document-class class_name (or module) comment or above an rb_define_class (or module). If a comment is supplied above a matching Init_ and a rb_define_class the Init_ comment is used.

 This is a comment for Foo
Init_Foo(void) {
 VALUE cFoo = rb_define_class("Foo", rb_cObject);
}
 Document-class: Foo
 This is a comment for Foo
Init_foo(void) {
 VALUE cFoo = rb_define_class("Foo", rb_cObject);
}
/*
 This is a comment for Foo
 /
VALUE cFoo = rb_define_class("Foo", rb_cObject);
# File lib/rdoc/parser/c.rb, line 735
def find_class_comment class_name, class_mod
 comment = nil
 if @content =~ %r%
 ((?>/\*.*?\*/\s+))
 (static\s+)?
 void\s+
 Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xmi then
 comment = 1ドル.sub(%r%Document-(?:class|module):\s+#{class_name}%, '')
 elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*?
 (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm then
 comment = "/*\n#{1ドル}"
 elsif @content =~ %r%.*((?>/\*.*?\*/\s+))
 ([\w\.\s]+\s* = \s+)?rb_define_(class|module).*?"(#{class_name})"%xm then
 comment = 1ドル
 elsif @content =~ %r%.*((?>/\*.*?\*/\s+))
 ([\w\.\s]+\s* = \s+)?rb_define_(class|module)_under.*?"(#{class_name.split('::').last})"%xm then
 comment = 1ドル
 else
 comment = ''
 end
 comment = RDoc::Comment.new comment, @top_level
 comment.normalize
 look_for_directives_in class_mod, comment
 class_mod.add_comment comment, @top_level
end
find_const_comment(type, const_name, class_name = nil) click to toggle source

Finds a comment matching type and const_name either above the comment or in the matching Document- section.

# File lib/rdoc/parser/c.rb, line 769
def find_const_comment(type, const_name, class_name = nil)
 comment = if @content =~ %r%((?>^\s*/\*.*?\*/\s+))
 rb_define_#{type}\((?:\s*(\w+),)?\s*
 "#{const_name}"\s*,
 .*?\)\s*;%xmi then
 1ドル
 elsif class_name and
 @content =~ %r%Document-(?:const|global|variable):\s
 #{class_name}::#{const_name}
 \s*?\n((?>.*?\*/))%xm then
 "/*\n#{1ドル}"
 elsif @content =~ %r%Document-(?:const|global|variable):
 \s#{const_name}
 \s*?\n((?>.*?\*/))%xm then
 "/*\n#{1ドル}"
 else
 ''
 end
 RDoc::Comment.new comment, @top_level
end
find_modifiers(comment, meth_obj) click to toggle source

Handles modifiers in comment and updates meth_obj as appropriate.

# File lib/rdoc/parser/c.rb, line 794
def find_modifiers comment, meth_obj
 comment.normalize
 comment.extract_call_seq meth_obj
 look_for_directives_in meth_obj, comment
end
find_override_comment(class_name, meth_obj) click to toggle source

Finds a Document-method override for meth_obj on class_name

# File lib/rdoc/parser/c.rb, line 804
def find_override_comment class_name, meth_obj
 name = Regexp.escape meth_obj.name
 prefix = Regexp.escape meth_obj.name_prefix
 comment = if @content =~ %r%Document-method:
 \s+#{class_name}#{prefix}#{name}
 \s*?\n((?>.*?\*/))%xm then
 "/*#{1ドル}"
 elsif @content =~ %r%Document-method:
 \s#{name}\s*?\n((?>.*?\*/))%xm then
 "/*#{1ドル}"
 end
 return unless comment
 RDoc::Comment.new comment, @top_level
end
handle_attr(var_name, attr_name, read, write) click to toggle source

Creates a new RDoc::Attr attr_name on class var_name that is either read, write or both

# File lib/rdoc/parser/c.rb, line 826
def handle_attr(var_name, attr_name, read, write)
 rw = ''
 rw << 'R' if '1' == read
 rw << 'W' if '1' == write
 class_name = @known_classes[var_name]
 return unless class_name
 class_obj = find_class var_name, class_name
 return unless class_obj
 comment = find_attr_comment var_name, attr_name
 comment.normalize
 name = attr_name.gsub(/rb_intern\("([^"]+)"\)/, '1円')
 attr = RDoc::Attr.new '', name, rw, comment
 attr.record_location @top_level
 class_obj.add_attribute attr
 @stats.add_attribute attr
end
handle_class_module(var_name, type, class_name, parent, in_module) click to toggle source

Creates a new RDoc::NormalClass or RDoc::NormalModule based on type named class_name in parent which was assigned to the C var_name.

# File lib/rdoc/parser/c.rb, line 855
def handle_class_module(var_name, type, class_name, parent, in_module)
 parent_name = @known_classes[parent] || parent
 if in_module then
 enclosure = @classes[in_module] || @store.find_c_enclosure(in_module)
 if enclosure.nil? and enclosure = @known_classes[in_module] then
 enc_type = /^rb_m/ =~ in_module ? :module : :class
 handle_class_module in_module, enc_type, enclosure, nil, nil
 enclosure = @classes[in_module]
 end
 unless enclosure then
 @enclosure_dependencies[in_module] << var_name
 @missing_dependencies[var_name] =
 [var_name, type, class_name, parent, in_module]
 return
 end
 else
 enclosure = @top_level
 end
 if type == :class then
 full_name = if RDoc::ClassModule === enclosure then
 enclosure.full_name + "::#{class_name}"
 else
 class_name
 end
 if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)% then
 parent_name = 1ドル
 end
 cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
 else
 cm = enclosure.add_module RDoc::NormalModule, class_name
 end
 cm.record_location enclosure.top_level
 find_class_comment cm.full_name, cm
 case cm
 when RDoc::NormalClass
 @stats.add_class cm
 when RDoc::NormalModule
 @stats.add_module cm
 end
 @classes[var_name] = cm
 @known_classes[var_name] = cm.full_name
 @store.add_c_enclosure var_name, cm
end
handle_constants(type, var_name, const_name, definition) click to toggle source

Adds constants. By providing some_value: at the start of the comment you can override the C value of the comment to give a friendly definition.

 300: The perfect score in bowling 
rb_define_const(cFoo, "PERFECT", INT2FIX(300);

Will override INT2FIX(300) with the value 300 in the output RDoc. Values may include quotes and escaped colons (:).

# File lib/rdoc/parser/c.rb, line 920
def handle_constants(type, var_name, const_name, definition)
 class_name = @known_classes[var_name]
 return unless class_name
 class_obj = find_class var_name, class_name
 unless class_obj then
 @options.warn 'Enclosing class or module %p is not known' % [const_name]
 return
 end
 comment = find_const_comment type, const_name, class_name
 comment.normalize
 # In the case of rb_define_const, the definition and comment are in
 # "/* definition: comment */" form. The literal ':' and '\' characters
 # can be escaped with a backslash.
 if type.downcase == 'const' then
 no_match, new_definition, new_comment = comment.text.split(/(\A.*):/)
 if no_match and no_match.empty? then
 if new_definition.empty? then # Default to literal C definition
 new_definition = definition
 else
 new_definition.gsub!("\:", ":")
 new_definition.gsub!("\\", '\')
 end
 new_definition.sub!(/\A(\s+)/, '')
 new_comment = "#{1ドル}#{new_comment.lstrip}"
 new_comment = RDoc::Comment.new new_comment, @top_level
 con = RDoc::Constant.new const_name, new_definition, new_comment
 else
 con = RDoc::Constant.new const_name, definition, comment
 end
 else
 con = RDoc::Constant.new const_name, definition, comment
 end
 con.record_location @top_level
 @stats.add_constant con
 class_obj.add_constant con
end
handle_ifdefs_in(body) click to toggle source

Removes ifdefs that would otherwise confuse us

# File lib/rdoc/parser/c.rb, line 971
def handle_ifdefs_in(body)
 body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '1円')
end
handle_method(type, var_name, meth_name, function, param_count, source_file = nil) click to toggle source

Adds an RDoc::AnyMethod meth_name defined on a class or module assigned to var_name. type is the type of method definition function used. singleton_method and module_function create a singleton method.

# File lib/rdoc/parser/c.rb, line 980
def handle_method(type, var_name, meth_name, function, param_count,
 source_file = nil)
 class_name = @known_classes[var_name]
 singleton = @singleton_classes.key? var_name
 @methods[var_name][function] << meth_name
 return unless class_name
 class_obj = find_class var_name, class_name
 if class_obj then
 if meth_name == 'initialize' then
 meth_name = 'new'
 singleton = true
 type = 'method' # force public
 end
 meth_obj = RDoc::AnyMethod.new '', meth_name
 meth_obj.c_function = function
 meth_obj.singleton =
 singleton || %w[singleton_method module_function].include?(type)
 p_count = Integer(param_count) rescue -1
 if source_file then
 file_name = File.join @file_dir, source_file
 if File.exist? file_name then
 file_content = File.read file_name
 else
 @options.warn "unknown source #{source_file} for #{meth_name} in #{@file_name}"
 end
 else
 file_content = @content
 end
 body = find_body class_name, function, meth_obj, file_content
 if body and meth_obj.document_self then
 meth_obj.params = if p_count < -1 then # -2 is Array
 '(*args)'
 elsif p_count == -1 then # argc, argv
 rb_scan_args body
 else
 "(#{(1..p_count).map { |i| "p#{i}" }.join ', '})"
 end
 meth_obj.record_location @top_level
 class_obj.add_method meth_obj
 @stats.add_method meth_obj
 meth_obj.visibility = :private if 'private_method' == type
 end
 end
end
handle_singleton(sclass_var, class_var) click to toggle source

Registers a singleton class sclass_var as a singleton of class_var

# File lib/rdoc/parser/c.rb, line 1040
def handle_singleton sclass_var, class_var
 class_name = @known_classes[class_var]
 @known_classes[sclass_var] = class_name
 @singleton_classes[sclass_var] = class_name
end
handle_tab_width(body) click to toggle source

Normalizes tabs in body

# File lib/rdoc/parser/c.rb, line 1050
def handle_tab_width(body)
 if /\t/ =~ body
 tab_width = @options.tab_width
 body.split(/\n/).map do |line|
 1 while line.gsub!(/\t+/) do
 ' ' * (tab_width * $&.length - $`.length % tab_width)
 end && $~
 line
 end.join "\n"
 else
 body
 end
end
load_variable_map(map_name) click to toggle source

Loads the variable map with the given name from the RDoc::Store, if present.

# File lib/rdoc/parser/c.rb, line 1068
def load_variable_map map_name
 return {} unless files = @store.cache[map_name]
 return {} unless name_map = files[@file_name]
 class_map = {}
 name_map.each do |variable, name|
 next unless mod = @store.find_class_or_module(name)
 class_map[variable] = if map_name == :c_class_variables then
 mod
 else
 name
 end
 @known_classes[variable] = name
 end
 class_map
end
look_for_directives_in(context, comment) click to toggle source

Look for directives in a normal comment block:

* :title: My Awesome Project

This method modifies the comment

# File lib/rdoc/parser/c.rb, line 1097
def look_for_directives_in context, comment
 @preprocess.handle comment, context do |directive, param|
 case directive
 when 'main' then
 @options.main_page = param
 ''
 when 'title' then
 @options.default_title = param if @options.respond_to? :default_title=
 ''
 end
 end
 comment
end
rb_scan_args(method_body) click to toggle source

Extracts parameters from the method_body and returns a method parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT

# File lib/rdoc/parser/c.rb, line 1116
def rb_scan_args method_body
 method_body =~ /rb_scan_args\((.*?)\)/m
 return '(*args)' unless 1ドル
 1ドル.split(/,/)[2] =~ /"(.*?)"/ # format argument
 format = 1ドル.split(//)
 lead = opt = trail = 0
 if format.first =~ /\d/ then
 lead = $&.to_i
 format.shift
 if format.first =~ /\d/ then
 opt = $&.to_i
 format.shift
 if format.first =~ /\d/ then
 trail = $&.to_i
 format.shift
 block_arg = true
 end
 end
 end
 if format.first == '*' and not block_arg then
 var = true
 format.shift
 if format.first =~ /\d/ then
 trail = $&.to_i
 format.shift
 end
 end
 if format.first == ':' then
 hash = true
 format.shift
 end
 if format.first == '&' then
 block = true
 format.shift
 end
 # if the format string is not empty there's a bug in the C code, ignore it
 args = []
 position = 1
 (1...(position + lead)).each do |index|
 args << "p#{index}"
 end
 position += lead
 (position...(position + opt)).each do |index|
 args << "p#{index} = v#{index}"
 end
 position += opt
 if var then
 args << '*args'
 position += 1
 end
 (position...(position + trail)).each do |index|
 args << "p#{index}"
 end
 position += trail
 if hash then
 args << "p#{position} = {}"
 position += 1
 end
 args << '&block' if block
 "(#{args.join ', '})"
end
remove_commented_out_lines() click to toggle source

Removes lines that are commented out that might otherwise get picked up when scanning for classes and methods

# File lib/rdoc/parser/c.rb, line 1200
def remove_commented_out_lines
 @content.gsub!(%r%//.*rb_define_%, '//')
end
scan() click to toggle source

Extracts the classes, modules, methods, attributes, constants and aliases from a C file and returns an RDoc::TopLevel for this file

# File lib/rdoc/parser/c.rb, line 1208
def scan
 remove_commented_out_lines
 do_modules
 do_classes
 do_missing
 do_constants
 do_methods
 do_includes
 do_aliases
 do_attrs
 deduplicate_call_seq
 @store.add_c_variables self
 @top_level
end