diff options
Diffstat (limited to 'lib/rdoc/parser')
| -rw-r--r-- | lib/rdoc/parser/c.rb | 705 | ||||
| -rw-r--r-- | lib/rdoc/parser/perl.rb | 165 | ||||
| -rw-r--r-- | lib/rdoc/parser/ruby.rb | 1599 | ||||
| -rw-r--r-- | lib/rdoc/parser/ruby_tools.rb | 157 | ||||
| -rw-r--r-- | lib/rdoc/parser/simple.rb | 44 |
5 files changed, 0 insertions, 2670 deletions
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb deleted file mode 100644 index f30167aafc..0000000000 --- a/lib/rdoc/parser/c.rb +++ /dev/null @@ -1,705 +0,0 @@ -require 'rdoc/parser' -require 'rdoc/parser/ruby' -require 'rdoc/known_classes' - -## -# We attempt to parse C extension files. Basically we look for -# the standard patterns that you find in extensions: <tt>rb_define_class, -# rb_define_method</tt> and so on. We also try 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 -# <tt>rb_define_method</tt> 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: <i>name</i>] -# This comment block is documentation for the given class. Use this -# when the <tt>Init_xxx</tt> method is not named after the class. -# -# [Document-method: <i>name</i>] -# This comment documents the named method. Use when RDoc cannot -# automatically find the method from it's declaration -# -# [call-seq: <i>text up to an empty line</i>] -# Because C source doesn't give descripive 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. ... -# */ -# -# /* -# * Document-method: read_value -# * -# * 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. -# */ - -class RDoc::Parser::C < RDoc::Parser - - parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/) - - include RDoc::Text - - ## - # C file the parser is parsing - - attr_accessor :content - - ## - # Resets cross-file state. Call when parsing different projects that need - # separate documentation. - - def self.reset - @@enclosure_classes = {} - @@known_bodies = {} - end - - reset - - ## - # Prepare to parse a C file - - def initialize(top_level, file_name, content, options, stats) - super - - @known_classes = RDoc::KNOWN_CLASSES.dup - @content = handle_tab_width handle_ifdefs_in(@content) - @classes = Hash.new - @file_dir = File.dirname(@file_name) - end - - def do_aliases - @content.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do - |var_name, new_name, old_name| - class_name = @known_classes[var_name] || var_name - class_obj = find_class(var_name, class_name) - - as = class_obj.add_alias RDoc::Alias.new("", old_name, new_name, "") - - @stats.add_alias as - end - end - - def do_classes - @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 - - # 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 - - @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 - - @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 - - @content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s* - \( - \s*(\w+), - \s*"(\w+)", - \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG - \s*\)/mx) do |var_name, in_module, class_name, parent| - handle_class_module(var_name, "class", class_name, parent, in_module) - end - end - - 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 - end - - ## - # Look for includes of the form: - # - # rb_include_module(rb_cArray, rb_mEnumerable); - - def do_includes - @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| - if cls = @classes[c] - m = @known_classes[m] || m - cls.add_include RDoc::Include.new(m, "") - end - end - end - - def do_methods - @content.scan(%r{rb_define_ - ( - singleton_method | - method | - module_function | - private_method - ) - \s*\(\s*([\w\.]+), - \s*"([^"]+)", - \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, - \s*(-?\w+)\s*\) - (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? - }xm) do - |type, var_name, meth_name, meth_body, 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" - next if var_name == "envtbl" - next if var_name == "argf" # it'd be nice to handle this one - - var_name = "rb_cObject" if var_name == "rb_mKernel" - handle_method(type, var_name, meth_name, meth_body, param_count, - source_file) - end - - @content.scan(%r{rb_define_attr\( - \s*([\w\.]+), - \s*"([^"]+)", - \s*(\d+), - \s*(\d+)\s*\); - }xm) do |var_name, attr_name, attr_reader, attr_writer| - #var_name = "rb_cObject" if var_name == "rb_mKernel" - handle_attr(var_name, attr_name, - attr_reader.to_i != 0, - attr_writer.to_i != 0) - 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, meth_body, param_count, source_file| - handle_method("method", "rb_mKernel", meth_name, - meth_body, 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, meth_body, param_count| - - handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count) - handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count) - end - end - - def find_attr_comment(attr_name) - if @content =~ %r{((?>/\*.*?\*/\s+)) - rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi - $1 - elsif @content =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m - $1 - else - '' - end - end - - ## - # Find the C code corresponding to a Ruby method - - def find_body(class_name, meth_name, meth_obj, body, quiet = false) - case body - when %r"((?>/\*.*?\*/\s*))((?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name} - \s*(\([^)]*\))([^;]|$))"xm - comment = $1 - body_text = $2 - params = $3 - - remove_private_comments comment if comment - - # see if we can find the whole body - - re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' - body_text = $& if /#{re}/m =~ body - - # 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.name - comment = override_comment if override_comment - - 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_text - meth_obj.add_token tk - meth_obj.comment = strip_stars comment - when %r{((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))}m - comment = $1 - body_text = $2 - find_body class_name, $3, meth_obj, body, true - find_modifiers comment, meth_obj - - meth_obj.start_collecting_tokens - tk = RDoc::RubyToken::Token.new nil, 1, 1 - tk.set_text body_text - meth_obj.add_token tk - meth_obj.comment = strip_stars(comment) + meth_obj.comment.to_s - when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m - unless find_body(class_name, $1, meth_obj, body, true) - warn "No definition for #{meth_name}" unless @options.quiet - return false - end - else - # No body, but might still have an override comment - comment = find_override_comment(class_name, meth_obj.name) - - if comment - find_modifiers(comment, meth_obj) - meth_obj.comment = strip_stars comment - else - warn "No definition for #{meth_name}" unless @options.quiet - return false - end - end - true - end - - 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 - - ## - # 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); - - 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 - elsif @content =~ %r{Document-(?:class|module):\s+#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m then - comment = $1 - elsif @content =~ %r{((?>/\*.*?\*/\s+)) - ([\w\.\s]+\s* = \s+)?rb_define_(class|module).*?"(#{class_name})"}xm then # " - comment = $1 - end - - return unless comment - - comment = strip_stars comment - - comment = look_for_directives_in class_mod, comment - - class_mod.comment = comment - end - - ## - # Finds a comment matching +type+ and +const_name+ either above the - # comment or in the matching Document- section. - - def find_const_comment(type, const_name) - if @content =~ %r{((?>^\s*/\*.*?\*/\s+)) - rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi - $1 - elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m - $1 - else - '' - end - end - - ## - # If the comment block contains a section that looks like: - # - # call-seq: - # Array.new - # Array.new(10) - # - # use it for the parameters. - - def find_modifiers(comment, meth_obj) - if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or - comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '') - meth_obj.document_self = false - end - if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or - comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '') - seq = $1 - seq.gsub!(/^\s*\*\s*/, '') - meth_obj.call_seq = seq - end - end - - def find_override_comment(class_name, meth_name) - name = Regexp.escape(meth_name) - if @content =~ %r{Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))}m then - $1 - elsif @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m then - $1 - end - end - - def handle_attr(var_name, attr_name, reader, writer) - rw = '' - rw << 'R' if reader - rw << 'W' if writer - - class_name = @known_classes[var_name] - - return unless class_name - - class_obj = find_class(var_name, class_name) - - if class_obj - comment = find_attr_comment(attr_name) - comment = strip_stars comment - att = RDoc::Attr.new '', attr_name, rw, comment - @stats.add_method att - class_obj.add_attribute(att) - end - end - - 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] || @@enclosure_classes[in_module] - - if enclosure.nil? and enclosure = @known_classes[in_module] then - type = /^rb_m/ =~ in_module ? "module" : "class" - handle_class_module in_module, type, enclosure, nil, nil - enclosure = @classes[in_module] - end - - unless enclosure then - warn "Enclosing class/module '#{in_module}' for #{type} #{class_name} not known" - 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 - - @stats.add_class cm - else - cm = enclosure.add_module RDoc::NormalModule, class_name - @stats.add_module cm - end - - cm.record_location enclosure.top_level - - find_class_comment cm.full_name, cm - - @classes[var_name] = cm - @@enclosure_classes[var_name] = cm - @known_classes[var_name] = cm.full_name - end - - ## - # Adds constant comments. By providing some_value: at the start ofthe - # 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 (\:). - - 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 - warn "Enclosing class/module #{const_name.inspect} not known" - return - end - - comment = find_const_comment type, const_name - comment = strip_stars comment - comment = normalize_comment comment - - # 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 - elements = comment.split ':' - - if elements.nil? or elements.empty? then - con = RDoc::Constant.new const_name, definition, comment - else - new_definition = elements[0..-2].join(':') - - 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 = if $1.nil? then - elements.last.lstrip - else - "#{$1}#{elements.last.lstrip}" - end - - con = RDoc::Constant.new const_name, new_definition, new_comment - end - else - con = RDoc::Constant.new const_name, definition, comment - end - - @stats.add_constant con - class_obj.add_constant con - end - - ## - # Removes #ifdefs that would otherwise confuse us - - def handle_ifdefs_in(body) - body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1') - end - - def handle_method(type, var_name, meth_name, meth_body, param_count, - source_file = nil) - class_name = @known_classes[var_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" - type = "singleton_method" - end - - meth_obj = RDoc::AnyMethod.new '', meth_name - meth_obj.singleton = %w[singleton_method module_function].include? type - - p_count = Integer(param_count) rescue -1 - - if p_count < 0 then - meth_obj.params = "(...)" - elsif p_count == 0 - meth_obj.params = "()" - else - meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")" - end - - if source_file then - file_name = File.join @file_dir, source_file - - if File.exist? file_name then - body = (@@known_bodies[file_name] ||= File.read(file_name)) - else - warn "unknown source #{source_file} for #{meth_name} in #{@file_name}" - end - else - body = @content - end - - if find_body(class_name, meth_body, meth_obj, body) and meth_obj.document_self then - class_obj.add_method meth_obj - @stats.add_method meth_obj - meth_obj.visibility = :private if 'private_method' == type - end - end - end - - def handle_tab_width(body) - if /\t/ =~ body - tab_width = @options.tab_width - body.split(/\n/).map do |line| - 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #` - line - end .join("\n") - else - body - end - end - - ## - # Look for directives in a normal comment block: - # - # /* - # * :title: My Awesome Project - # */ - # - # This routine modifies it's parameter - - def look_for_directives_in(context, comment) - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include - - preprocess.handle comment, context do |directive, param| - case directive - when 'main' then - @options.main_page = param - '' - when 'title' then - @options.title = param - '' - end - end - - comment - end - ## - # Removes lines that are commented out that might otherwise get picked up - # when scanning for classes and methods - - def remove_commented_out_lines - @content.gsub!(%r{//.*rb_define_}, '//') - end - - def remove_private_comments(comment) - comment.gsub!(/\/?\*--\n(.*?)\/?\*\+\+/m, '') - comment.sub!(/\/?\*--\n.*/m, '') - end - - ## - # Extract the classes/modules and methods from a C file and return the - # corresponding top-level object - - def scan - remove_commented_out_lines - do_classes - do_constants - do_methods - do_includes - do_aliases - @top_level - end - -end - diff --git a/lib/rdoc/parser/perl.rb b/lib/rdoc/parser/perl.rb deleted file mode 100644 index 0023a013a6..0000000000 --- a/lib/rdoc/parser/perl.rb +++ /dev/null @@ -1,165 +0,0 @@ -require 'rdoc/parser' - -## -# -# This is an attamept to write a basic parser for Perl's -# POD (Plain old Documentation) format. Ruby code must -# co-exist with Perl, and some tasks are easier in Perl -# than Ruby because of existing libraries. -# -# One difficult is that Perl POD has no means of identifying -# the classes (packages) and methods (subs) with which it -# is associated, it is more like literate programming in so -# far as it just happens to be in the same place as the code, -# but need not be. -# -# We would like to support all the markup the POD provides -# so that it will convert happily to HTML. At the moment -# I don't think I can do that: time constraints. -# - -class RDoc::Parser::PerlPOD < RDoc::Parser - - parse_files_matching(/.p[lm]$/) - - ## - # Prepare to parse a perl file - - def initialize(top_level, file_name, content, options, stats) - super - - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include - - preprocess.handle @content do |directive, param| - warn "Unrecognized directive '#{directive}' in #{@file_name}" - end - end - - ## - # Extract the Pod(-like) comments from the code. - # At its most basic there will ne no need to distinguish - # between the different types of header, etc. - # - # This uses a simple finite state machine, in a very - # procedural pattern. I could "replace case with polymorphism" - # but I think it would obscure the intent, scatter the - # code all over tha place. This machine is necessary - # because POD requires that directives be preceded by - # blank lines, so reading line by line is necessary, - # and preserving state about what is seen is necesary. - - def scan - - @top_level.comment ||= "" - state=:code_blank - line_number = 0 - line = nil - - # This started out as a really long nested case statement, - # which also led to repetitive code. I'd like to avoid that - # so I'm using a "table" instead. - - # Firstly we need some procs to do the transition and processing - # work. Because these are procs they are closures, and they can - # use variables in the local scope. - # - # First, the "nothing to see here" stuff. - code_noop = lambda do - if line =~ /^\s+$/ - state = :code_blank - end - end - - pod_noop = lambda do - if line =~ /^\s+$/ - state = :pod_blank - end - @top_level.comment += filter(line) - end - - begin_noop = lambda do - if line =~ /^\s+$/ - state = :begin_blank - end - @top_level.comment += filter(line) - end - - # Now for the blocks that process code and comments... - - transit_to_pod = lambda do - case line - when /^=(?:pod|head\d+)/ - state = :pod_no_blank - @top_level.comment += filter(line) - when /^=over/ - state = :over_no_blank - @top_level.comment += filter(line) - when /^=(?:begin|for)/ - state = :begin_no_blank - end - end - - process_pod = lambda do - case line - when /^\s*$/ - state = :pod_blank - @top_level.comment += filter(line) - when /^=cut/ - state = :code_no_blank - when /^=end/ - $stderr.puts "'=end' unexpected at #{line_number} in #{@file_name}" - else - @top_level.comment += filter(line) - end - end - - - process_begin = lambda do - case line - when /^\s*$/ - state = :begin_blank - @top_level.comment += filter(line) - when /^=end/ - state = :code_no_blank - when /^=cut/ - $stderr.puts "'=cut' unexpected at #{line_number} in #{@file_name}" - else - @top_level.comment += filter(line) - end - - end - - - transitions = { :code_no_blank => code_noop, - :code_blank => transit_to_pod, - :pod_no_blank => pod_noop, - :pod_blank => process_pod, - :begin_no_blank => begin_noop, - :begin_blank => process_begin} - @content.each_line do |l| - line = l - line_number += 1 - transitions[state].call - end # each line - - @top_level - end - - # Filter the perl markup that does the same as the rdoc - # filtering. Only basic for now. Will probably need a - # proper parser to cope with C<<...>> etc - def filter(comment) - return '' if comment =~ /^=pod\s*$/ - comment.gsub!(/^=pod/, '==') - comment.gsub!(/^=head(\d+)/) do - "=" * $1.to_i - end - comment.gsub!(/=item/, ''); - comment.gsub!(/C<(.*?)>/, '<tt>\1</tt>'); - comment.gsub!(/I<(.*?)>/, '<i>\1</i>'); - comment.gsub!(/B<(.*?)>/, '<b>\1</b>'); - comment - end - -end - diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb deleted file mode 100644 index 1876c339fa..0000000000 --- a/lib/rdoc/parser/ruby.rb +++ /dev/null @@ -1,1599 +0,0 @@ -## -# This file contains stuff stolen outright from: -# -# rtags.rb - -# ruby-lex.rb - ruby lexcal analyzer -# ruby-token.rb - ruby tokens -# by Keiju ISHITSUKA (Nippon Rational Inc.) -# - -require 'rdoc/ruby_token' -require 'rdoc/ruby_lex' - -require 'rdoc/code_objects' -require 'rdoc/tokenstream' -require 'rdoc/markup/preprocess' -require 'rdoc/parser' -require 'rdoc/parser/ruby_tools' - -$TOKEN_DEBUG ||= nil - -## -# Extracts code elements from a source file returning a TopLevel object -# containing the constituent file elements. -# -# This file is based on rtags -# -# RubyParser understands how to document: -# * classes -# * modules -# * methods -# * constants -# * aliases -# * private, public, protected -# * private_class_function, public_class_function -# * module_function -# * attr, attr_reader, attr_writer, attr_accessor -# * extra accessors given on the command line -# * metaprogrammed methods -# * require -# * include -# -# == Method Arguments -# -#-- -# NOTE: I don't think this works, needs tests, remove the paragraph following -# this block when known to work -# -# The parser extracts the arguments from the method definition. You can -# override this with a custom argument definition using the :args: directive: -# -# ## -# # This method tries over and over until it is tired -# -# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try -# puts thing_to_try -# go_go_go thing_to_try, tries - 1 -# end -# -# If you have a more-complex set of overrides you can use the :call-seq: -# directive: -#++ -# -# The parser extracts the arguments from the method definition. You can -# override this with a custom argument definition using the :call-seq: -# directive: -# -# ## -# # This method can be called with a range or an offset and length -# # -# # :call-seq: -# # my_method(Range) -# # my_method(offset, length) -# -# def my_method(*args) -# end -# -# The parser extracts +yield+ expressions from method bodies to gather the -# yielded argument names. If your method manually calls a block instead of -# yielding or you want to override the discovered argument names use -# the :yields: directive: -# -# ## -# # My method is awesome -# -# def my_method(&block) # :yields: happy, times -# block.call 1, 2 -# end -# -# == Metaprogrammed Methods -# -# To pick up a metaprogrammed method, the parser looks for a comment starting -# with '##' before an identifier: -# -# ## -# # This is a meta-programmed method! -# -# add_my_method :meta_method, :arg1, :arg2 -# -# The parser looks at the token after the identifier to determine the name, in -# this example, :meta_method. If a name cannot be found, a warning is printed -# and 'unknown is used. -# -# You can force the name of a method using the :method: directive: -# -# ## -# # :method: woo_hoo! -# -# By default, meta-methods are instance methods. To indicate that a method is -# a singleton method instead use the :singleton-method: directive: -# -# ## -# # :singleton-method: -# -# You can also use the :singleton-method: directive with a name: -# -# ## -# # :singleton-method: woo_hoo! -# -# Additionally you can mark a method as an attribute by -# using :attr:, :attr_reader:, :attr_writer: or :attr_accessor:. Just like -# for :method:, the name is optional. -# -# ## -# # :attr_reader: my_attr_name -# -# == Hidden methods and attributes -# -# You can provide documentation for methods that don't appear using -# the :method:, :singleton-method: and :attr: directives: -# -# ## -# # :attr_writer: ghost_writer -# # There is an attribute here, but you can't see it! -# -# ## -# # :method: ghost_method -# # There is a method here, but you can't see it! -# -# ## -# # this is a comment for a regular method -# -# def regular_method() end -# -# Note that by default, the :method: directive will be ignored if there is a -# standard rdocable item following it. - -class RDoc::Parser::Ruby < RDoc::Parser - - parse_files_matching(/\.rbw?$/) - - include RDoc::RubyToken - include RDoc::TokenStream - include RDoc::Parser::RubyTools - - ## - # RDoc::NormalClass type - - NORMAL = "::" - - ## - # RDoc::SingleClass type - - SINGLE = "<<" - - def initialize(top_level, file_name, content, options, stats) - super - - @size = 0 - @token_listeners = nil - @scanner = RDoc::RubyLex.new content, @options - @scanner.exception_on_syntax_error = false - @prev_seek = nil - - reset - end - - ## - # Look for the first comment in a file that isn't a shebang line. - - def collect_first_comment - skip_tkspace - comment = '' - first_line = true - - tk = get_tk - - while TkCOMMENT === tk - if first_line and tk.text =~ /\A#!/ then - skip_tkspace - tk = get_tk - elsif first_line and tk.text =~ /\A#\s*-\*-/ then - first_line = false - skip_tkspace - tk = get_tk - else - first_line = false - comment << tk.text << "\n" - tk = get_tk - - if TkNL === tk then - skip_tkspace false - tk = get_tk - end - end - end - - unget_tk tk - - comment - end - - def error(msg) - msg = make_message msg - $stderr.puts msg - exit false - end - - ## - # Look for a 'call-seq' in the comment, and override the normal parameter - # stuff - - def extract_call_seq(comment, meth) - if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') then - seq = $1 - seq.gsub!(/^\s*\#\s*/, '') - meth.call_seq = seq - end - - meth - end - - def get_bool - skip_tkspace - tk = get_tk - case tk - when TkTRUE - true - when TkFALSE, TkNIL - false - else - unget_tk tk - true - end - end - - ## - # Look for the name of a class of module (optionally with a leading :: or - # with :: separated named) and return the ultimate name and container - - def get_class_or_module(container) - skip_tkspace - name_t = get_tk - - # class ::A -> A is in the top level - case name_t - when TkCOLON2, TkCOLON3 then # bug - name_t = get_tk - container = @top_level - end - - skip_tkspace false - - while TkCOLON2 === peek_tk do - prev_container = container - container = container.find_module_named name_t.name - unless container then - container = prev_container.add_module RDoc::NormalModule, name_t.name - end - get_tk - name_t = get_tk - end - skip_tkspace false - return [container, name_t] - end - - ## - # Return a superclass, which can be either a constant of an expression - - def get_class_specification - tk = get_tk - return "self" if TkSELF === tk - - res = "" - while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do - res += tk.name - tk = get_tk - end - - unget_tk(tk) - skip_tkspace false - - get_tkread # empty out read buffer - - tk = get_tk - - case tk - when TkNL, TkCOMMENT, TkSEMICOLON then - unget_tk(tk) - return res - end - - res += parse_call_parameters(tk) - res - end - - ## - # Parse a constant, which might be qualified by one or more class or module - # names - - def get_constant - res = "" - skip_tkspace false - tk = get_tk - - while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do - res += tk.name - tk = get_tk - end - -# if res.empty? -# warn("Unexpected token #{tk} in constant") -# end - unget_tk(tk) - res - end - - ## - # Get a constant that may be surrounded by parens - - def get_constant_with_optional_parens - skip_tkspace false - nest = 0 - while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do - get_tk - skip_tkspace - nest += 1 - end - - name = get_constant - - while nest > 0 - skip_tkspace - tk = get_tk - nest -= 1 if TkRPAREN === tk - end - - name - end - - def get_symbol_or_name - tk = get_tk - case tk - when TkSYMBOL then - text = tk.text.sub(/^:/, '') - - if TkASSIGN === peek_tk then - get_tk - text << '=' - end - - text - when TkId, TkOp then - tk.name - when TkSTRING, TkDSTRING then - tk.text - else - raise RDoc::Error, "Name or symbol expected (got #{tk})" - end - end - - ## - # Look for directives in a normal comment block: - # - # # :stopdoc: - # # Don't display comment from this point forward - # - # This routine modifies it's parameter - - def look_for_directives_in(context, comment) - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include - - preprocess.handle comment, context do |directive, param| - case directive - when 'enddoc' then - throw :enddoc - when 'main' then - @options.main_page = param - '' - when 'method', 'singleton-method', - 'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then - false # handled elsewhere - when 'section' then - context.set_current_section param, comment - comment.replace '' - break - when 'startdoc' then - context.start_doc - context.force_documentation = true - '' - when 'stopdoc' then - context.stop_doc - '' - when 'title' then - @options.title = param - '' - end - end - - remove_private_comments comment - end - - ## - # Adds useful info about the parser to +message+ - - def make_message message - prefix = "#{@file_name}:" - - prefix << "#{@scanner.line_no}:#{@scanner.char_no}:" if @scanner - - "#{prefix} #{message}" - end - - ## - # Creates an RDoc::Attr for the name following +tk+, setting the comment to - # +comment+. - - def parse_attr(context, single, tk, comment) - args = parse_symbol_arg 1 - if args.size > 0 - name = args[0] - rw = "R" - skip_tkspace false - tk = get_tk - if TkCOMMA === tk then - rw = "RW" if get_bool - else - unget_tk tk - end - att = RDoc::Attr.new get_tkread, name, rw, comment - read_documentation_modifiers att, RDoc::ATTR_MODIFIERS - if att.document_self - context.add_attribute(att) - end - else - warn("'attr' ignored - looks like a variable") - end - end - - ## - # Creates an RDoc::Attr for each attribute listed after +tk+, setting the - # comment for each to +comment+. - - def parse_attr_accessor(context, single, tk, comment) - args = parse_symbol_arg - read = get_tkread - rw = "?" - - # TODO If nodoc is given, don't document any of them - - tmp = RDoc::CodeObject.new - read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS - return unless tmp.document_self - - case tk.name - when "attr_reader" then rw = "R" - when "attr_writer" then rw = "W" - when "attr_accessor" then rw = "RW" - else - rw = '?' - end - - for name in args - att = RDoc::Attr.new get_tkread, name, rw, comment - context.add_attribute att - end - end - - def parse_alias(context, single, tk, comment) - skip_tkspace - if TkLPAREN === peek_tk then - get_tk - skip_tkspace - end - new_name = get_symbol_or_name - - @scanner.instance_eval { @lex_state = EXPR_FNAME } - - skip_tkspace - if TkCOMMA === peek_tk then - get_tk - skip_tkspace - end - - begin - old_name = get_symbol_or_name - rescue RDoc::Error - return - end - - al = RDoc::Alias.new get_tkread, old_name, new_name, comment - read_documentation_modifiers al, RDoc::ATTR_MODIFIERS - context.add_alias al if al.document_self - end - - def parse_call_parameters(tk) - end_token = case tk - when TkLPAREN, TkfLPAREN - TkRPAREN - when TkRPAREN - return "" - else - TkNL - end - nest = 0 - - loop do - case tk - when TkSEMICOLON - break - when TkLPAREN, TkfLPAREN - nest += 1 - when end_token - if end_token == TkRPAREN - nest -= 1 - break if @scanner.lex_state == EXPR_END and nest <= 0 - else - break unless @scanner.continue - end - when TkCOMMENT - unget_tk(tk) - break - when nil then - break - end - tk = get_tk - end - res = get_tkread.tr("\n", " ").strip - res = "" if res == ";" - res - end - - def parse_class(container, single, tk, comment) - container, name_t = get_class_or_module container - - case name_t - when TkCONSTANT - name = name_t.name - superclass = "Object" - - if TkLT === peek_tk then - get_tk - skip_tkspace - superclass = get_class_specification - superclass = "<unknown>" if superclass.empty? - end - - cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass - cls = container.add_class cls_type, name, superclass - - read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS - cls.record_location @top_level - cls.comment = comment - - @stats.add_class cls - - parse_statements cls - when TkLSHFT - case name = get_class_specification - when "self", container.name - parse_statements container, SINGLE - else - other = RDoc::TopLevel.find_class_named name - - unless other then - other = container.add_module RDoc::NormalModule, name - other.record_location @top_level - other.comment = comment - end - - @stats.add_class other - - read_documentation_modifiers other, RDoc::CLASS_MODIFIERS - parse_statements(other, SINGLE) - end - - else - warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}") - end - end - - def parse_constant(container, tk, comment) - name = tk.name - skip_tkspace false - eq_tk = get_tk - - unless TkASSIGN === eq_tk then - unget_tk eq_tk - return - end - - nest = 0 - get_tkread - - tk = get_tk - - if TkGT === tk then - unget_tk tk - unget_tk eq_tk - return - end - - rhs_name = '' - - loop do - case tk - when TkSEMICOLON then - break - when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO, TkIF, TkUNLESS, - TkCASE then - nest += 1 - when TkRPAREN, TkRBRACE, TkRBRACK, TkEND then - nest -= 1 - when TkCOMMENT then - if nest <= 0 && @scanner.lex_state == EXPR_END - unget_tk tk - break - end - when TkCONSTANT then - rhs_name << tk.name - - if nest <= 0 and TkNL === peek_tk then - mod = if rhs_name =~ /^::/ then - RDoc::TopLevel.find_class_or_module rhs_name - else - container.find_module_named rhs_name - end - - container.add_module_alias mod, name if mod - get_tk # TkNL - break - end - when TkNL then - if nest <= 0 && - (@scanner.lex_state == EXPR_END || !@scanner.continue) then - unget_tk tk - break - end - when TkCOLON2, TkCOLON3 then - rhs_name << '::' - when nil then - break - end - tk = get_tk - end - - res = get_tkread.tr("\n", " ").strip - res = "" if res == ";" - - con = RDoc::Constant.new name, res, comment - read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS - - @stats.add_constant con - container.add_constant con if con.document_self - end - - ## - # Generates an RDoc::Method or RDoc::Attr from +comment+ by looking for - # :method: or :attr: directives in +comment+. - - def parse_comment(container, tk, comment) - line_no = tk.line_no - column = tk.char_no - - singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3') - - # REFACTOR - if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then - name = $1 unless $1.empty? - - meth = RDoc::GhostMethod.new get_tkread, name - meth.singleton = singleton - - meth.start_collecting_tokens - indent = TkSPACE.new nil, 1, 1 - indent.set_text " " * column - - position_comment = TkCOMMENT.new nil, line_no, 1 - position_comment.set_text "# File #{@top_level.absolute_name}, line #{line_no}" - meth.add_tokens [position_comment, NEWLINE_TOKEN, indent] - - meth.params = '' - - extract_call_seq comment, meth - - return unless meth.name - - container.add_method meth if meth.document_self - - meth.comment = comment - - @stats.add_method meth - elsif comment.sub!(/# +:?(attr(_reader|_writer|_accessor)?:) *(\S*).*?\n/i, '') then - rw = case $1 - when 'attr_reader' then 'R' - when 'attr_writer' then 'W' - else 'RW' - end - - name = $3 unless $3.empty? - - att = RDoc::Attr.new get_tkread, name, rw, comment - container.add_attribute att - - @stats.add_method att - end - end - - def parse_include(context, comment) - loop do - skip_tkspace_comment - - name = get_constant_with_optional_parens - context.add_include RDoc::Include.new(name, comment) unless name.empty? - - return unless TkCOMMA === peek_tk - get_tk - end - end - - ## - # Parses a meta-programmed attribute and creates an RDoc::Attr. - # - # To create foo and bar attributes on class C with comment "My attributes": - # - # class C - # - # ## - # # :attr: - # # - # # My attributes - # - # my_attr :foo, :bar - # - # end - # - # To create a foo attribute on class C with comment "My attribute": - # - # class C - # - # ## - # # :attr: foo - # # - # # My attribute - # - # my_attr :foo, :bar - # - # end - - def parse_meta_attr(context, single, tk, comment) - args = parse_symbol_arg - read = get_tkread - rw = "?" - - # If nodoc is given, don't document any of them - - tmp = RDoc::CodeObject.new - read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS - return unless tmp.document_self - - if comment.sub!(/^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '') then - rw = case $1 - when 'attr_reader' then 'R' - when 'attr_writer' then 'W' - else 'RW' - end - name = $3 unless $3.empty? - end - - if name then - att = RDoc::Attr.new get_tkread, name, rw, comment - context.add_attribute att - else - args.each do |attr_name| - att = RDoc::Attr.new get_tkread, attr_name, rw, comment - context.add_attribute att - end - end - end - - ## - # Parses a meta-programmed method - - def parse_meta_method(container, single, tk, comment) - line_no = tk.line_no - column = tk.char_no - - start_collecting_tokens - add_token tk - add_token_listener self - - skip_tkspace false - - singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3') - - if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then - name = $1 unless $1.empty? - end - - if name.nil? then - name_t = get_tk - case name_t - when TkSYMBOL then - name = name_t.text[1..-1] - when TkSTRING then - name = name_t.value[1..-2] - when TkASSIGN then # ignore - remove_token_listener self - return - else - warn "unknown name token #{name_t.inspect} for meta-method '#{tk.name}'" - name = 'unknown' - end - end - - meth = RDoc::MetaMethod.new get_tkread, name - meth.singleton = singleton - - remove_token_listener self - - meth.start_collecting_tokens - indent = TkSPACE.new nil, 1, 1 - indent.set_text " " * column - - position_comment = TkCOMMENT.new nil, line_no, 1 - position_comment.value = "# File #{@top_level.absolute_name}, line #{line_no}" - meth.add_tokens [position_comment, NEWLINE_TOKEN, indent] - meth.add_tokens @token_stream - - token_listener meth do - meth.params = '' - - extract_call_seq comment, meth - - container.add_method meth if meth.document_self - - last_tk = tk - - while tk = get_tk do - case tk - when TkSEMICOLON then - break - when TkNL then - break unless last_tk and TkCOMMA === last_tk - when TkSPACE then - # expression continues - else - last_tk = tk - end - end - end - - meth.comment = comment - - @stats.add_method meth - end - - ## - # Parses a normal method defined by +def+ - - def parse_method(container, single, tk, comment) - added_container = nil - meth = nil - name = nil - line_no = tk.line_no - column = tk.char_no - - start_collecting_tokens - add_token tk - - token_listener self do - @scanner.instance_eval do @lex_state = EXPR_FNAME end - - skip_tkspace false - name_t = get_tk - back_tk = skip_tkspace - meth = nil - added_container = false - - dot = get_tk - if TkDOT === dot or TkCOLON2 === dot then - @scanner.instance_eval do @lex_state = EXPR_FNAME end - skip_tkspace - name_t2 = get_tk - - case name_t - when TkSELF, TkMOD then - name = name_t2.name - when TkCONSTANT then - name = name_t2.name - prev_container = container - container = container.find_module_named(name_t.name) - unless container then - added_container = true - obj = name_t.name.split("::").inject(Object) do |state, item| - state.const_get(item) - end rescue nil - - type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule - - unless [Class, Module].include?(obj.class) then - warn("Couldn't find #{name_t.name}. Assuming it's a module") - end - - if type == RDoc::NormalClass then - sclass = obj.superclass ? obj.superclass.name : nil - container = prev_container.add_class type, name_t.name, sclass - else - container = prev_container.add_module type, name_t.name - end - - container.record_location @top_level - end - when TkIDENTIFIER, TkIVAR then - dummy = RDoc::Context.new - dummy.parent = container - skip_method dummy - return - else - warn "unexpected method name token #{name_t.inspect}" - # break - skip_method container - return - end - - meth = RDoc::AnyMethod.new(get_tkread, name) - meth.singleton = true - else - unget_tk dot - back_tk.reverse_each do |token| - unget_tk token - end - - name = case name_t - when TkSTAR, TkAMPER then - name_t.text - else - unless name_t.respond_to? :name then - warn "expected method name token, . or ::, got #{name_t.inspect}" - skip_method container - return - end - name_t.name - end - - meth = RDoc::AnyMethod.new get_tkread, name - meth.singleton = (single == SINGLE) - end - end - - meth.start_collecting_tokens - indent = TkSPACE.new nil, 1, 1 - indent.set_text " " * column - - token = TkCOMMENT.new nil, line_no, 1 - token.set_text "# File #{@top_level.absolute_name}, line #{line_no}" - meth.add_tokens [token, NEWLINE_TOKEN, indent] - meth.add_tokens @token_stream - - token_listener meth do - @scanner.instance_eval do @continue = false end - parse_method_parameters meth - - if meth.document_self then - container.add_method meth - elsif added_container then - container.document_self = false - end - - # Having now read the method parameters and documentation modifiers, we - # now know whether we have to rename #initialize to ::new - - if name == "initialize" && !meth.singleton then - if meth.dont_rename_initialize then - meth.visibility = :protected - else - meth.singleton = true - meth.name = "new" - meth.visibility = :public - end - end - - parse_statements container, single, meth - end - - extract_call_seq comment, meth - - meth.comment = comment - - @stats.add_method meth - end - - def parse_method_or_yield_parameters(method = nil, - modifiers = RDoc::METHOD_MODIFIERS) - skip_tkspace false - tk = get_tk - - # Little hack going on here. In the statement - # f = 2*(1+yield) - # We see the RPAREN as the next token, so we need - # to exit early. This still won't catch all cases - # (such as "a = yield + 1" - end_token = case tk - when TkLPAREN, TkfLPAREN - TkRPAREN - when TkRPAREN - return "" - else - TkNL - end - nest = 0 - - loop do - case tk - when TkSEMICOLON then - break - when TkLBRACE then - nest += 1 - when TkRBRACE then - # we might have a.each {|i| yield i } - unget_tk(tk) if nest.zero? - nest -= 1 - break if nest <= 0 - when TkLPAREN, TkfLPAREN then - nest += 1 - when end_token then - if end_token == TkRPAREN - nest -= 1 - break if @scanner.lex_state == EXPR_END and nest <= 0 - else - break unless @scanner.continue - end - when method && method.block_params.nil? && TkCOMMENT then - unget_tk tk - read_documentation_modifiers method, modifiers - @read.pop - when TkCOMMENT then - @read.pop - when nil then - break - end - tk = get_tk - end - - res = get_tkread.gsub(/\s+/, ' ').strip - res = '' if res == ';' - res - end - - ## - # Capture the method's parameters. Along the way, look for a comment - # containing: - # - # # yields: .... - # - # and add this as the block_params for the method - - def parse_method_parameters(method) - res = parse_method_or_yield_parameters method - - res = "(#{res})" unless res =~ /\A\(/ - method.params = res unless method.params - - if method.block_params.nil? then - skip_tkspace false - read_documentation_modifiers method, RDoc::METHOD_MODIFIERS - end - end - - def parse_module(container, single, tk, comment) - container, name_t = get_class_or_module container - - name = name_t.name - - mod = container.add_module RDoc::NormalModule, name - mod.record_location @top_level - - read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS - parse_statements(mod) - mod.comment = comment - - @stats.add_module mod - end - - def parse_require(context, comment) - skip_tkspace_comment - tk = get_tk - - if TkLPAREN === tk then - skip_tkspace_comment - tk = get_tk - end - - name = tk.text if TkSTRING === tk - - if name then - context.add_require RDoc::Require.new(name, comment) - else - unget_tk tk - end - end - - ## - # The core of the ruby parser. - - def parse_statements(container, single = NORMAL, current_method = nil, - comment = '') - nest = 1 - save_visibility = container.visibility - - non_comment_seen = true - - while tk = get_tk do - keep_comment = false - - non_comment_seen = true unless TkCOMMENT === tk - - case tk - when TkNL then - skip_tkspace - tk = get_tk - - if TkCOMMENT === tk then - if non_comment_seen then - # Look for RDoc in a comment about to be thrown away - parse_comment container, tk, comment unless comment.empty? - - comment = '' - non_comment_seen = false - end - - while TkCOMMENT === tk do - comment << tk.text << "\n" - - tk = get_tk # this is the newline - skip_tkspace false # leading spaces - tk = get_tk - end - - unless comment.empty? then - look_for_directives_in container, comment - - if container.done_documenting then - container.ongoing_visibility = save_visibility - end - end - - keep_comment = true - else - non_comment_seen = true - end - - unget_tk tk - keep_comment = true - - when TkCLASS then - if container.document_children then - parse_class container, single, tk, comment - else - nest += 1 - end - - when TkMODULE then - if container.document_children then - parse_module container, single, tk, comment - else - nest += 1 - end - - when TkDEF then - if container.document_self then - parse_method container, single, tk, comment - else - nest += 1 - end - - when TkCONSTANT then - if container.document_self then - parse_constant container, tk, comment - end - - when TkALIAS then - if container.document_self and not current_method then - parse_alias container, single, tk, comment - end - - when TkYIELD then - if current_method.nil? then - warn "Warning: yield outside of method" if container.document_self - else - parse_yield container, single, tk, current_method - end - - # Until and While can have a 'do', which shouldn't increase the nesting. - # We can't solve the general case, but we can handle most occurrences by - # ignoring a do at the end of a line. - - when TkUNTIL, TkWHILE then - nest += 1 - skip_optional_do_after_expression - - # 'for' is trickier - when TkFOR then - nest += 1 - skip_for_variable - skip_optional_do_after_expression - - when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then - nest += 1 - - when TkIDENTIFIER then - if nest == 1 and current_method.nil? then - case tk.name - when 'private', 'protected', 'public', 'private_class_method', - 'public_class_method', 'module_function' then - parse_visibility container, single, tk - keep_comment = true - when 'attr' then - parse_attr container, single, tk, comment - when /^attr_(reader|writer|accessor)$/ then - parse_attr_accessor container, single, tk, comment - when 'alias_method' then - parse_alias container, single, tk, comment if - container.document_self - when 'require', 'include' then - # ignore - else - if container.document_self and comment =~ /\A#\#$/ then - case comment - when /^# +:?attr(_reader|_writer|_accessor)?:/ then - parse_meta_attr container, single, tk, comment - else - parse_meta_method container, single, tk, comment - end - end - end - end - - case tk.name - when "require" then - parse_require container, comment - when "include" then - parse_include container, comment - end - - when TkEND then - nest -= 1 - if nest == 0 then - read_documentation_modifiers container, RDoc::CLASS_MODIFIERS - container.ongoing_visibility = save_visibility - - parse_comment container, tk, comment unless comment.empty? - - return - end - end - - comment = '' unless keep_comment - - begin - get_tkread - skip_tkspace false - end while peek_tk == TkNL - end - end - - def parse_symbol_arg(no = nil) - args = [] - skip_tkspace_comment - case tk = get_tk - when TkLPAREN - loop do - skip_tkspace_comment - if tk1 = parse_symbol_in_arg - args.push tk1 - break if no and args.size >= no - end - - skip_tkspace_comment - case tk2 = get_tk - when TkRPAREN - break - when TkCOMMA - else - warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC - break - end - end - else - unget_tk tk - if tk = parse_symbol_in_arg - args.push tk - return args if no and args.size >= no - end - - loop do - skip_tkspace false - - tk1 = get_tk - unless TkCOMMA === tk1 then - unget_tk tk1 - break - end - - skip_tkspace_comment - if tk = parse_symbol_in_arg - args.push tk - break if no and args.size >= no - end - end - end - args - end - - def parse_symbol_in_arg - case tk = get_tk - when TkSYMBOL - tk.text.sub(/^:/, '') - when TkSTRING - eval @read[-1] - else - warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC - nil - end - end - - def parse_top_level_statements(container) - comment = collect_first_comment - look_for_directives_in(container, comment) - container.comment = comment unless comment.empty? - parse_statements container, NORMAL, nil, comment - end - - def parse_visibility(container, single, tk) - singleton = (single == SINGLE) - - vis_type = tk.name - - vis = case vis_type - when 'private' then :private - when 'protected' then :protected - when 'public' then :public - when 'private_class_method' then - singleton = true - :private - when 'public_class_method' then - singleton = true - :public - when 'module_function' then - singleton = true - :public - else - raise RDoc::Error, "Invalid visibility: #{tk.name}" - end - - skip_tkspace_comment false - - case peek_tk - # Ryan Davis suggested the extension to ignore modifiers, because he - # often writes - # - # protected unless $TESTING - # - when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then - container.ongoing_visibility = vis - else - if vis_type == 'module_function' then - args = parse_symbol_arg - container.set_visibility_for args, :private, false - - module_functions = [] - - container.methods_matching args do |m| - s_m = m.dup - s_m.singleton = true if RDoc::AnyMethod === s_m - s_m.visibility = :public - module_functions << s_m - end - - module_functions.each do |s_m| - case s_m - when RDoc::AnyMethod then - container.add_method s_m - when RDoc::Attr then - container.add_attribute s_m - end - end - else - args = parse_symbol_arg - container.set_visibility_for args, vis, singleton - end - end - end - - def parse_yield(context, single, tk, method) - return if method.block_params - - get_tkread - @scanner.instance_eval { @continue = false } - method.block_params = parse_method_or_yield_parameters - end - - ## - # Directives are modifier comments that can appear after class, module, or - # method names. For example: - # - # def fred # :yields: a, b - # - # or: - # - # class MyClass # :nodoc: - # - # We return the directive name and any parameters as a two element array - - def read_directive(allowed) - tk = get_tk - result = nil - - if TkCOMMENT === tk then - if tk.text =~ /\s*:?(\w+):\s*(.*)/ then - directive = $1.downcase - if allowed.include? directive then - result = [directive, $2] - end - end - else - unget_tk tk - end - - result - end - - def read_documentation_modifiers(context, allow) - dir = read_directive(allow) - - case dir[0] - when "notnew", "not_new", "not-new" then - context.dont_rename_initialize = true - - when "nodoc" then - context.document_self = false - if dir[1].downcase == "all" - context.document_children = false - end - - when "doc" then - context.document_self = true - context.force_documentation = true - - when "yield", "yields" then - unless context.params.nil? - context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc - end - - context.block_params = dir[1] - - when "arg", "args" then - context.params = dir[1] - end if dir - end - - def remove_private_comments(comment) - comment.gsub!(/^#--\n.*?^#\+\+/m, '') - comment.sub!(/^#--\n.*/m, '') - end - - def scan - reset - - catch :eof do - catch :enddoc do - begin - parse_top_level_statements @top_level - rescue StandardError => e - bytes = '' - - 20.times do @scanner.ungetc end - count = 0 - 60.times do |i| - count = i - byte = @scanner.getc - break unless byte - bytes << byte - end - count -= 20 - count.times do @scanner.ungetc end - - $stderr.puts <<-EOF - -#{self.class} failure around line #{@scanner.line_no} of -#{@file_name} - - EOF - - unless bytes.empty? then - $stderr.puts - $stderr.puts bytes.inspect - end - - raise e - end - end - end - - @top_level - end - - ## - # while, until, and for have an optional do - - def skip_optional_do_after_expression - skip_tkspace false - tk = get_tk - case tk - when TkLPAREN, TkfLPAREN then - end_token = TkRPAREN - else - end_token = TkNL - end - - b_nest = 0 - nest = 0 - @scanner.instance_eval { @continue = false } - - loop do - case tk - when TkSEMICOLON then - break if b_nest.zero? - when TkLPAREN, TkfLPAREN then - nest += 1 - when TkBEGIN then - b_nest += 1 - when TkEND then - b_nest -= 1 - when TkDO - break if nest.zero? - when end_token then - if end_token == TkRPAREN - nest -= 1 - break if @scanner.lex_state == EXPR_END and nest.zero? - else - break unless @scanner.continue - end - when nil then - break - end - tk = get_tk - end - - skip_tkspace false - - get_tk if TkDO === peek_tk - end - - ## - # skip the var [in] part of a 'for' statement - - def skip_for_variable - skip_tkspace false - tk = get_tk - skip_tkspace false - tk = get_tk - unget_tk(tk) unless TkIN === tk - end - - def skip_method container - meth = RDoc::AnyMethod.new "", "anon" - parse_method_parameters meth - parse_statements container, false, meth - end - - ## - # Skip spaces until a comment is found - - def skip_tkspace_comment(skip_nl = true) - loop do - skip_tkspace skip_nl - return unless TkCOMMENT === peek_tk - get_tk - end - end - - def warn(msg) - return if @options.quiet - msg = make_message msg - $stderr.puts msg - end - -end - diff --git a/lib/rdoc/parser/ruby_tools.rb b/lib/rdoc/parser/ruby_tools.rb deleted file mode 100644 index 90c03307b4..0000000000 --- a/lib/rdoc/parser/ruby_tools.rb +++ /dev/null @@ -1,157 +0,0 @@ -## -# Collection of methods for writing parsers against RDoc::RubyLex and -# RDoc::RubyToken - -module RDoc::Parser::RubyTools - - include RDoc::RubyToken - - ## - # Adds a token listener +obj+, but you should probably use token_listener - - def add_token_listener(obj) - @token_listeners ||= [] - @token_listeners << obj - end - - ## - # Fetches the next token from the scanner - - def get_tk - tk = nil - - if @tokens.empty? then - tk = @scanner.token - @read.push @scanner.get_readed - puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG - else - @read.push @unget_read.shift - tk = @tokens.shift - puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG - end - - tk = nil if TkEND_OF_SCRIPT === tk - - if TkSYMBEG === tk then - set_token_position tk.line_no, tk.char_no - - case tk1 = get_tk - when TkId, TkOp, TkSTRING, TkDSTRING, TkSTAR, TkAMPER then - if tk1.respond_to?(:name) then - tk = Token(TkSYMBOL).set_text(":" + tk1.name) - else - tk = Token(TkSYMBOL).set_text(":" + tk1.text) - end - - # remove the identifier we just read (we're about to replace it with a - # symbol) - @token_listeners.each do |obj| - obj.pop_token - end if @token_listeners - else - warn("':' not followed by identifier or operator") - tk = tk1 - end - end - - # inform any listeners of our shiny new token - @token_listeners.each do |obj| - obj.add_token(tk) - end if @token_listeners - - tk - end - - def get_tk_until(*tokens) - read = [] - - loop do - tk = get_tk - case tk when *tokens then unget_tk tk; break end - read << tk - end - - read - end - - ## - # Retrieves a String representation of the read tokens - - def get_tkread - read = @read.join("") - @read = [] - read - end - - ## - # Peek equivalent for get_tkread - - def peek_read - @read.join('') - end - - ## - # Peek at the next token, but don't remove it from the stream - - def peek_tk - unget_tk(tk = get_tk) - tk - end - - ## - # Removes the token listener +obj+ - - def remove_token_listener(obj) - @token_listeners.delete(obj) - end - - ## - # Resets the tools - - def reset - @read = [] - @tokens = [] - @unget_read = [] - @nest = 0 - end - - ## - # Skips whitespace tokens including newlines if +skip_nl+ is true - - def skip_tkspace(skip_nl = true) # HACK dup - tokens = [] - - while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do - tokens.push tk - end - - unget_tk tk - tokens - end - - ## - # Has +obj+ listen to tokens - - def token_listener(obj) - add_token_listener obj - yield - ensure - remove_token_listener obj - end - - ## - # Returns +tk+ to the scanner - - def unget_tk(tk) - @tokens.unshift tk - @unget_read.unshift @read.pop - - # Remove this token from any listeners - @token_listeners.each do |obj| - obj.pop_token - end if @token_listeners - end - -end - - diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb deleted file mode 100644 index e99d2d4319..0000000000 --- a/lib/rdoc/parser/simple.rb +++ /dev/null @@ -1,44 +0,0 @@ -## -# Parse a non-source file. We basically take the whole thing as one big -# comment. If the first character in the file is '#', we strip leading pound -# signs. - -class RDoc::Parser::Simple < RDoc::Parser - - parse_files_matching(//) - - attr_reader :content # :nodoc: - - ## - # Prepare to parse a plain file - - def initialize(top_level, file_name, content, options, stats) - super - - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include - - preprocess.handle @content, @top_level - end - - ## - # Extract the file contents and attach them to the TopLevel as a comment - - def scan - comment = remove_coding_comment @content - comment = remove_private_comments comment - - @top_level.comment = comment - @top_level.parser = self.class - @top_level - end - - def remove_private_comments(comment) - comment.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '') - end - - def remove_coding_comment text - text.sub(/\A# .*coding[=:].*$/, '') - end - -end - |
