aboutsummaryrefslogtreecommitdiffstats
path: root/lib/rdoc/parser/ruby.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rdoc/parser/ruby.rb')
-rw-r--r--lib/rdoc/parser/ruby.rb333
1 files changed, 222 insertions, 111 deletions
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
index 2874c47a20..e6f07d66da 100644
--- a/lib/rdoc/parser/ruby.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -11,8 +11,8 @@ require 'rdoc/ruby_token'
require 'rdoc/ruby_lex'
require 'rdoc/code_objects'
-require 'rdoc/tokenstream'
-require 'rdoc/markup/preprocess'
+require 'rdoc/token_stream'
+require 'rdoc/markup/pre_process'
require 'rdoc/parser'
require 'rdoc/parser/ruby_tools'
@@ -162,6 +162,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
SINGLE = "<<"
+ ##
+ # Creates a new Ruby parser.
+
def initialize(top_level, file_name, content, options, stats)
super
@@ -209,10 +212,13 @@ class RDoc::Parser::Ruby < RDoc::Parser
comment
end
+ ##
+ # Aborts with +msg+
+
def error(msg)
msg = make_message msg
- $stderr.puts msg
- exit false
+
+ abort msg
end
##
@@ -229,6 +235,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
meth
end
+ ##
+ # Looks for a true or false token. Returns false if TkFALSE or TkNIL are
+ # found.
+
def get_bool
skip_tkspace
tk = get_tk
@@ -245,20 +255,24 @@ class RDoc::Parser::Ruby < RDoc::Parser
##
# Look for the name of a class of module (optionally with a leading :: or
- # with :: separated named) and return the ultimate name and container
+ # with :: separated named) and return the ultimate name, the associated
+ # container, and the given name (with the ::).
def get_class_or_module(container)
skip_tkspace
name_t = get_tk
+ given_name = ''
# class ::A -> A is in the top level
case name_t
when TkCOLON2, TkCOLON3 then # bug
name_t = get_tk
container = @top_level
+ given_name << '::'
end
skip_tkspace false
+ given_name << name_t.name
while TkCOLON2 === peek_tk do
prev_container = container
@@ -268,9 +282,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
get_tk
name_t = get_tk
+ given_name << '::' << name_t.name
end
skip_tkspace false
- return [container, name_t]
+ return [container, name_t, given_name]
end
##
@@ -347,6 +362,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
name
end
+ ##
+ # Extracts a name or symbol from the token stream.
+
def get_symbol_or_name
tk = get_tk
case tk
@@ -361,7 +379,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
text
when TkId, TkOp then
tk.name
- when TkSTRING, TkDSTRING then
+ when TkAMPER,
+ TkDSTRING,
+ TkSTAR,
+ TkSTRING then
tk.text
else
raise RDoc::Error, "Name or symbol expected (got #{tk})"
@@ -374,7 +395,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# # :stopdoc:
# # Don't display comment from this point forward
#
- # This routine modifies it's parameter
+ # This routine modifies its +comment+ parameter.
def look_for_directives_in(context, comment)
preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
@@ -382,9 +403,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
preprocess.handle comment, context do |directive, param|
case directive
when 'enddoc' then
- throw :enddoc
+ context.done_documenting = true
+ ''
when 'main' then
- @options.main_page = param
+ @options.main_page = param if @options.respond_to? :main_page
''
when 'method', 'singleton-method',
'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then
@@ -401,7 +423,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
context.stop_doc
''
when 'title' then
- @options.title = param
+ @options.default_title = param if @options.respond_to? :default_title=
''
end
end
@@ -426,23 +448,28 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_attr(context, single, tk, comment)
args = parse_symbol_arg 1
- if args.size > 0
+ if args.size > 0 then
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
+
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ att.record_location @top_level
+
read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
- if att.document_self
- context.add_attribute(att)
- end
+
+ context.add_attribute att if att.document_self
+
+ @stats.add_attribute att
else
- warn("'attr' ignored - looks like a variable")
+ warn "'attr' ignored - looks like a variable"
end
end
@@ -452,12 +479,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_attr_accessor(context, single, tk, comment)
args = parse_symbol_arg
- 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
@@ -471,17 +494,25 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
for name in args
- att = RDoc::Attr.new get_tkread, name, rw, comment
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ att.record_location @top_level
+
context.add_attribute att
+ @stats.add_attribute att
end
end
+ ##
+ # Parses an +alias+ in +context+ with +comment+
+
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 }
@@ -498,11 +529,20 @@ class RDoc::Parser::Ruby < RDoc::Parser
return
end
- al = RDoc::Alias.new get_tkread, old_name, new_name, comment
+ al = RDoc::Alias.new(get_tkread, old_name, new_name, comment,
+ single == SINGLE)
+ al.record_location @top_level
+
read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
context.add_alias al if al.document_self
+ @stats.add_alias al
+
+ al
end
+ ##
+ # Extracts call parameters from the token stream.
+
def parse_call_parameters(tk)
end_token = case tk
when TkLPAREN, TkfLPAREN
@@ -540,28 +580,33 @@ class RDoc::Parser::Ruby < RDoc::Parser
res
end
+ ##
+ # Parses a class in +context+ with +comment+
+
def parse_class(container, single, tk, comment)
- container, name_t = get_class_or_module container
+ declaration_context = container
+ container, name_t, given_name = get_class_or_module container
case name_t
when TkCONSTANT
name = name_t.name
- superclass = "Object"
+ superclass = '::Object'
if TkLT === peek_tk then
get_tk
skip_tkspace
superclass = get_class_specification
- superclass = "<unknown>" if superclass.empty?
+ superclass = '(unknown)' if superclass.empty?
end
cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
- cls = container.add_class cls_type, name, superclass
+ cls = declaration_context.add_class cls_type, given_name, superclass
read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
cls.record_location @top_level
- cls.comment = comment
+ cls.comment = comment if cls.document_self
+ @top_level.add_to_classes_or_modules cls
@stats.add_class cls
parse_statements cls
@@ -569,7 +614,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
case name = get_class_specification
when "self", container.name
parse_statements container, SINGLE
- when /\A[A-Z]/
+ else
other = RDoc::TopLevel.find_class_named name
unless other then
@@ -578,6 +623,15 @@ class RDoc::Parser::Ruby < RDoc::Parser
other.comment = comment
end
+ # notify :nodoc: all if not a constant-named class/module
+ # (and remove any comment)
+ unless name =~ /\A(::)?[A-Z]/
+ other.document_self = nil
+ other.document_children = false
+ other.clear_comment
+ end
+
+ @top_level.add_to_classes_or_modules other
@stats.add_class other
read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
@@ -589,9 +643,15 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Parses a constant in +context+ with +comment+
+
def parse_constant(container, tk, comment)
name = tk.name
skip_tkspace false
+
+ return unless name =~ /^\w+$/
+
eq_tk = get_tk
unless TkASSIGN === eq_tk then
@@ -615,9 +675,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
loop do
case tk
when TkSEMICOLON then
- break
- when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO, TkIF, TkUNLESS,
- TkCASE then
+ break if nest <= 0
+ when TkLPAREN, TkfLPAREN, TkLBRACE, TkfLBRACE, TkLBRACK, TkfLBRACK,
+ TkDO, TkIF, TkUNLESS, TkCASE, TkDEF, TkBEGIN then
nest += 1
when TkRPAREN, TkRBRACE, TkRBRACK, TkEND then
nest -= 1
@@ -654,10 +714,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
tk = get_tk
end
- res = get_tkread.tr("\n", " ").strip
+ res = get_tkread.gsub(/^[ \t]+/, '').strip
res = "" if res == ";"
con = RDoc::Constant.new name, res, comment
+ con.record_location @top_level
read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
@stats.add_constant con
@@ -679,6 +740,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
name = $1 unless $1.empty?
meth = RDoc::GhostMethod.new get_tkread, name
+ meth.record_location @top_level
meth.singleton = singleton
meth.start_collecting_tokens
@@ -709,13 +771,19 @@ class RDoc::Parser::Ruby < RDoc::Parser
name = $3 unless $3.empty?
+ # TODO authorize 'singleton-attr...'?
att = RDoc::Attr.new get_tkread, name, rw, comment
+ att.record_location @top_level
+
container.add_attribute att
- @stats.add_method att
+ @stats.add_attribute att
end
end
+ ##
+ # Parses an +include+ in +context+ with +comment+
+
def parse_include(context, comment)
loop do
skip_tkspace_comment
@@ -759,8 +827,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_meta_attr(context, single, tk, comment)
args = parse_symbol_arg
- get_tkread
-
rw = "?"
# If nodoc is given, don't document any of them
@@ -779,12 +845,19 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
if name then
- att = RDoc::Attr.new get_tkread, name, rw, comment
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ att.record_location @top_level
+
context.add_attribute att
+ @stats.add_attribute att
else
args.each do |attr_name|
- att = RDoc::Attr.new get_tkread, attr_name, rw, comment
+ att = RDoc::Attr.new(get_tkread, attr_name, rw, comment,
+ single == SINGLE)
+ att.record_location @top_level
+
context.add_attribute att
+ @stats.add_attribute att
end
end
end
@@ -825,6 +898,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
meth = RDoc::MetaMethod.new get_tkread, name
+ meth.record_location @top_level
meth.singleton = singleton
remove_token_listener self
@@ -882,7 +956,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
token_listener self do
@scanner.instance_eval do @lex_state = EXPR_FNAME end
- skip_tkspace false
+ skip_tkspace
name_t = get_tk
back_tk = skip_tkspace
meth = nil
@@ -922,11 +996,17 @@ class RDoc::Parser::Ruby < RDoc::Parser
container.record_location @top_level
end
- when TkIDENTIFIER, TkIVAR then
+ when TkIDENTIFIER, TkIVAR, TkGVAR then
dummy = RDoc::Context.new
dummy.parent = container
skip_method dummy
return
+ when TkTRUE, TkFALSE, TkNIL then
+ klass_name = "#{name_t.name.capitalize}Class"
+ container = RDoc::TopLevel.find_class_named klass_name
+ container ||= @top_level.add_class RDoc::NormalClass, klass_name
+
+ name = name_t2.name
else
warn "unexpected method name token #{name_t.inspect}"
# break
@@ -959,6 +1039,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ meth.record_location @top_level
+
meth.start_collecting_tokens
indent = TkSPACE.new nil, 1, 1
indent.set_text " " * column
@@ -1001,6 +1083,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
@stats.add_method meth
end
+ ##
+ # Extracts +yield+ parameters from +method+
+
def parse_method_or_yield_parameters(method = nil,
modifiers = RDoc::METHOD_MODIFIERS)
skip_tkspace false
@@ -1024,14 +1109,16 @@ class RDoc::Parser::Ruby < RDoc::Parser
loop do
case tk
when TkSEMICOLON then
- break
- when TkLBRACE then
+ break if nest == 0
+ when TkLBRACE, TkfLBRACE 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
+ if nest <= 0
+ # we might have a.each { |i| yield i }
+ unget_tk(tk) if nest < 0
+ break
+ end
when TkLPAREN, TkfLPAREN then
nest += 1
when end_token then
@@ -1041,6 +1128,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
else
break unless @scanner.continue
end
+ when TkRPAREN then
+ nest -= 1
when method && method.block_params.nil? && TkCOMMENT then
unget_tk tk
read_documentation_modifiers method, modifiers
@@ -1078,8 +1167,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Parses an RDoc::NormalModule in +container+ with +comment+
+
def parse_module(container, single, tk, comment)
- container, name_t = get_class_or_module container
+ container, name_t, = get_class_or_module container
name = name_t.name
@@ -1087,12 +1179,16 @@ class RDoc::Parser::Ruby < RDoc::Parser
mod.record_location @top_level
read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+ mod.comment = comment if mod.document_self
parse_statements(mod)
- mod.comment = comment
+ @top_level.add_to_classes_or_modules mod
@stats.add_module mod
end
+ ##
+ # Parses an RDoc::Require in +context+ containing +comment+
+
def parse_require(context, comment)
skip_tkspace_comment
tk = get_tk
@@ -1105,7 +1201,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
name = tk.text if TkSTRING === tk
if name then
- context.add_require RDoc::Require.new(name, comment)
+ @top_level.add_require RDoc::Require.new(name, comment)
else
unget_tk tk
end
@@ -1206,7 +1302,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# 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
+ when TkUNTIL, TkWHILE then
nest += 1
skip_optional_do_after_expression
@@ -1275,9 +1371,14 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Parse up to +no+ symbol arguments
+
def parse_symbol_arg(no = nil)
args = []
+
skip_tkspace_comment
+
case tk = get_tk
when TkLPAREN
loop do
@@ -1320,28 +1421,40 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
end
+
args
end
+ ##
+ # Returns symbol text from the next token
+
def parse_symbol_in_arg
case tk = get_tk
when TkSYMBOL
tk.text.sub(/^:/, '')
when TkSTRING
eval @read[-1]
+ when TkDSTRING, TkIDENTIFIER then
+ nil # ignore
else
warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
nil
end
end
+ ##
+ # Parses statements at the toplevel in +container+
+
def parse_top_level_statements(container)
comment = collect_first_comment
look_for_directives_in(container, comment)
- container.comment = comment unless comment.empty?
+ container.comment = comment if container.document_self unless comment.empty?
parse_statements container, NORMAL, nil, comment
end
+ ##
+ # Determines the visibility in +container+ from +tk+
+
def parse_visibility(container, single, tk)
singleton = (single == SINGLE)
@@ -1383,7 +1496,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
container.methods_matching args do |m|
s_m = m.dup
- s_m.singleton = true if RDoc::AnyMethod === s_m
+ s_m.record_location @top_level
+ s_m.singleton = true
s_m.visibility = :public
module_functions << s_m
end
@@ -1403,6 +1517,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Determines the block parameter for +context+
+
def parse_yield(context, single, tk, method)
return if method.block_params
@@ -1423,93 +1540,81 @@ class RDoc::Parser::Ruby < RDoc::Parser
#
# We return the directive name and any parameters as a two element array
- def read_directive(allowed)
+ 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
+ return unless tk.text =~ /\s*:?(\w+):\s*(.*)/
+
+ directive = $1.downcase
+
+ return [directive, $2] if allowed.include? directive
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
+ ##
+ # Handles the directive for +context+ if the directive is listed in +allow+.
+ # This method is called for directives following a definition.
- when "yield", "yields" then
- unless context.params.nil?
- context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
- end
+ def read_documentation_modifiers(context, allow)
+ directive, value = read_directive allow
- context.block_params = dir[1]
+ return unless directive
- when "arg", "args" then
- context.params = dir[1]
- end if dir
+ case directive
+ when 'notnew', 'not_new', 'not-new' then
+ context.dont_rename_initialize = true
+ else
+ RDoc::Parser.process_directive context, directive, value
+ end
end
+ ##
+ # Removes private comments from +comment+
+
def remove_private_comments(comment)
- comment.gsub!(/^#--\n.*?^#\+\+/m, '')
- comment.sub!(/^#--\n.*/m, '')
+ comment.gsub!(/^#--\n.*?^#\+\+\n?/m, '')
+ comment.sub!(/^#--\n.*\n?/m, '')
end
+ ##
+ # Scans this ruby file for ruby constructs
+
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
+ 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
+ $stderr.puts <<-EOF
#{self.class} failure around line #{@scanner.line_no} of
#{@file_name}
- EOF
+ EOF
- unless bytes.empty? then
- $stderr.puts
- $stderr.puts bytes.inspect
- end
-
- raise e
+ unless bytes.empty? then
+ $stderr.puts
+ $stderr.puts bytes.inspect
end
+
+ raise e
end
end
@@ -1574,6 +1679,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
unget_tk(tk) unless TkIN === tk
end
+ ##
+ # Skips the next method in +container+
+
def skip_method container
meth = RDoc::AnyMethod.new "", "anon"
parse_method_parameters meth
@@ -1591,6 +1699,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Prints +msg+ to +$stderr+ unless we're being quiet
+
def warn(msg)
return if @options.quiet
msg = make_message msg