Class RDoc::RubyParser
In: lib/rdoc/parsers/parse_rb.rb
Parent: Object

Methods

Included Modules

RubyToken TokenStream

Constants

NORMAL = "::"
SINGLE = "<<"

Public Class methods

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1387
1387:     def initialize(top_level, file_name, content, options, stats)
1388:       @options = options
1389:       @stats   = stats
1390:       @size = 0
1391:       @token_listeners = nil
1392:       @input_file_name = file_name
1393:       @scanner = RubyLex.new(content)
1394:       @scanner.exception_on_syntax_error = false
1395:       @top_level = top_level
1396:       @progress = $stderr unless options.quiet
1397:     end

Public Instance methods

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1399
1399:     def scan
1400:       @tokens = []
1401:       @unget_read = []
1402:       @read = []
1403:       catch(:eof) do
1404:         catch(:enddoc) do
1405:           begin
1406:             parse_toplevel_statements(@top_level)
1407:           rescue Exception => e
1408:             $stderr.puts "\n\n"
1409:             $stderr.puts "RDoc failure in #@input_file_name at or around " +
1410:                          "line #{@scanner.line_no} column #{@scanner.char_no}"
1411:             $stderr.puts 
1412:             $stderr.puts "Before reporting this, could you check that the file"
1413:             $stderr.puts "you're documenting compiles cleanly--RDoc is not a"
1414:             $stderr.puts "full Ruby parser, and gets confused easily if fed"
1415:             $stderr.puts "invalid programs."
1416:             $stderr.puts
1417:             $stderr.puts "The internal error was:\n\n"
1418:             
1419:             e.set_backtrace(e.backtrace[0,4])
1420:             raise
1421:           end
1422:         end
1423:       end
1424:       @top_level
1425:     end

Private Instance methods

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1456
1456:     def add_token_listener(obj)
1457:       @token_listeners ||= []
1458:       @token_listeners << obj
1459:     end

Look for the first comment in a file that isn‘t a shebang line.

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1542
1542:     def collect_first_comment
1543:       skip_tkspace
1544:       res = ''
1545:       first_line = true
1546: 
1547:       tk = get_tk
1548:       while tk.kind_of?(TkCOMMENT)
1549:         if first_line && tk.text[0,2] == "#!"
1550:           skip_tkspace
1551:           tk = get_tk
1552:         else
1553:           res << tk.text << "\n"
1554:           tk = get_tk
1555:           if tk.kind_of? TkNL
1556:             skip_tkspace(false)
1557:             tk = get_tk
1558:           end
1559:         end
1560:         first_line = false
1561:       end
1562:       unget_tk(tk)
1563:       res
1564:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1443
1443:     def error(msg)
1444:       msg = make_message msg
1445:       $stderr.puts msg
1446:       exit(1)
1447:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2441
2441:     def get_bool
2442:       skip_tkspace
2443:       tk = get_tk
2444:       case tk
2445:       when TkTRUE
2446:         true
2447:       when TkFALSE, TkNIL
2448:         false
2449:       else
2450:         unget_tk tk
2451:         true
2452:       end
2453:     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

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1797
1797:     def get_class_or_module(container)
1798:       skip_tkspace
1799:       name_t = get_tk
1800: 
1801:       # class ::A -> A is in the top level
1802:       if name_t.kind_of?(TkCOLON2)
1803:         name_t = get_tk
1804:         container = @top_level
1805:       end
1806: 
1807:       skip_tkspace(false)
1808: 
1809:       while peek_tk.kind_of?(TkCOLON2)
1810:         prev_container = container
1811:         container = container.find_module_named(name_t.name)
1812:         if !container
1813: #          warn("Couldn't find module #{name_t.name}")
1814:           container = prev_container.add_module(NormalModule, name_t.name)
1815:         end
1816:         get_tk
1817:         name_t = get_tk
1818:       end
1819:       skip_tkspace(false)
1820:       return [container, name_t]
1821:     end

Return a superclass, which can be either a constant of an expression

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2126
2126:     def get_class_specification
2127:       tk = get_tk
2128:       return "self" if tk.kind_of?(TkSELF)
2129:         
2130:       res = ""
2131:       while tk.kind_of?(TkCOLON2) ||
2132:           tk.kind_of?(TkCOLON3)   ||
2133:           tk.kind_of?(TkCONSTANT)   
2134:         
2135:         res += tk.text
2136:         tk = get_tk
2137:       end
2138: 
2139:       unget_tk(tk)
2140:       skip_tkspace(false)
2141: 
2142:       get_tkread # empty out read buffer
2143: 
2144:       tk = get_tk
2145: 
2146:       case tk
2147:       when TkNL, TkCOMMENT, TkSEMICOLON
2148:         unget_tk(tk)
2149:         return res
2150:       end
2151: 
2152:       res += parse_call_parameters(tk)
2153:       res
2154:     end

Parse a constant, which might be qualified by one or more class or module names

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2198
2198:     def get_constant
2199:       res = ""
2200:       skip_tkspace(false)
2201:       tk = get_tk
2202: 
2203:       while tk.kind_of?(TkCOLON2) ||
2204:           tk.kind_of?(TkCOLON3)   ||
2205:           tk.kind_of?(TkCONSTANT)          
2206:         
2207:         res += tk.text
2208:         tk = get_tk
2209:       end
2210: 
2211: #      if res.empty?
2212: #        warn("Unexpected token #{tk} in constant")
2213: #      end 
2214:       unget_tk(tk)
2215:       res
2216:     end

Get a constant that may be surrounded by parens

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2220
2220:     def get_constant_with_optional_parens
2221:       skip_tkspace(false)
2222:       nest = 0
2223:       while (tk = peek_tk).kind_of?(TkLPAREN)  || tk.kind_of?(TkfLPAREN)
2224:         get_tk
2225:         skip_tkspace(true)
2226:         nest += 1
2227:       end
2228: 
2229:       name = get_constant
2230: 
2231:       while nest > 0
2232:         skip_tkspace(true)
2233:         tk = get_tk
2234:         nest -= 1 if tk.kind_of?(TkRPAREN)
2235:       end
2236:       name
2237:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2355
2355:     def get_symbol_or_name
2356:       tk = get_tk
2357:       case tk
2358:       when  TkSYMBOL
2359:         tk.text.sub(/^:/, '')
2360:       when TkId, TkOp
2361:         tk.name
2362:       when TkSTRING
2363:         tk.text
2364:       else
2365:         raise "Name or symbol expected (got #{tk})"
2366:       end
2367:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1465
1465:     def get_tk
1466:       tk = nil
1467:       if @tokens.empty?
1468:         tk = @scanner.token
1469:         @read.push @scanner.get_read
1470:         puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
1471:       else
1472:         @read.push @unget_read.shift
1473:         tk = @tokens.shift
1474:         puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
1475:       end
1476: 
1477:       if tk.kind_of?(TkSYMBEG)
1478:         set_token_position(tk.line_no, tk.char_no)
1479:         tk1 = get_tk
1480:         if tk1.kind_of?(TkId) || tk1.kind_of?(TkOp)
1481:           tk = Token(TkSYMBOL).set_text(":" + tk1.name)
1482:           # remove the identifier we just read (we're about to
1483:           # replace it with a symbol)
1484:           @token_listeners.each do |obj|
1485:             obj.pop_token
1486:           end if @token_listeners
1487:         else
1488:           warn("':' not followed by identifier or operator")
1489:           tk = tk1
1490:         end
1491:       end
1492: 
1493:       # inform any listeners of our shiny new token
1494:       @token_listeners.each do |obj|
1495:         obj.add_token(tk)
1496:       end if @token_listeners
1497: 
1498:       tk
1499:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1526
1526:     def get_tkread
1527:       read = @read.join("")
1528:       @read = []
1529:       read
1530:     end

Look for directives in a normal comment block:

  #--       - don't display comment from this point forward

This routine modifies it‘s parameter

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2305
2305:     def look_for_directives_in(context, comment)
2306: 
2307:       preprocess = SM::PreProcess.new(@input_file_name,
2308:                                       @options.rdoc_include)
2309: 
2310:       preprocess.handle(comment) do |directive, param|
2311:         case directive
2312:         when "stopdoc"
2313:           context.stop_doc
2314:           ""
2315:         when "startdoc"
2316:           context.start_doc
2317:           context.force_documentation = true
2318:           ""
2319: 
2320:         when "enddoc"
2321:           #context.done_documenting = true
2322:           #""
2323:           throw :enddoc
2324: 
2325:         when "main"
2326:           options = Options.instance
2327:           options.main_page = param
2328:           ""
2329: 
2330:         when "title"
2331:           options = Options.instance
2332:           options.title = param
2333:           ""
2334: 
2335:         when "section"
2336:           context.set_current_section(param, comment)
2337:           comment.replace("") # 1.8 doesn't support #clear
2338:           break 
2339:         else
2340:           warn "Unrecognized directive '#{directive}'"
2341:           break
2342:         end
2343:       end
2344: 
2345:       remove_private_comments(comment)
2346:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1429
1429:     def make_message(msg)
1430:       prefix = "\n" + @input_file_name + ":"
1431:       if @scanner
1432:         prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
1433:       end
1434:       return prefix + msg
1435:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2369
2369:     def parse_alias(context, single, tk, comment)
2370:       skip_tkspace
2371:       if (peek_tk.kind_of? TkLPAREN)
2372:         get_tk
2373:         skip_tkspace
2374:       end
2375:       new_name = get_symbol_or_name
2376:       @scanner.instance_eval{@lex_state = EXPR_FNAME}
2377:       skip_tkspace
2378:       if (peek_tk.kind_of? TkCOMMA)
2379:         get_tk
2380:         skip_tkspace
2381:       end
2382:       old_name = get_symbol_or_name
2383: 
2384:       al = Alias.new(get_tkread, old_name, new_name, comment)
2385:       read_documentation_modifiers(al, ATTR_MODIFIERS)
2386:       if al.document_self
2387:         context.add_alias(al)
2388:       end
2389:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2455
2455:     def parse_attr(context, single, tk, comment)
2456:       args = parse_symbol_arg(1)
2457:       if args.size > 0
2458:         name = args[0]
2459:         rw = "R"
2460:         skip_tkspace(false)
2461:         tk = get_tk
2462:         if tk.kind_of? TkCOMMA
2463:           rw = "RW" if get_bool
2464:         else
2465:           unget_tk tk
2466:         end
2467:         att = Attr.new(get_tkread, name, rw, comment)
2468:         read_documentation_modifiers(att, ATTR_MODIFIERS)
2469:         if att.document_self
2470:           context.add_attribute(att)
2471:         end
2472:       else
2473:         warn("'attr' ignored - looks like a variable")
2474:       end    
2475: 
2476:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2509
2509:     def parse_attr_accessor(context, single, tk, comment)
2510:       args = parse_symbol_arg
2511:       read = get_tkread
2512:       rw = "?"
2513: 
2514:       # If nodoc is given, don't document any of them
2515: 
2516:       tmp = CodeObject.new
2517:       read_documentation_modifiers(tmp, ATTR_MODIFIERS)
2518:       return unless tmp.document_self
2519: 
2520:       case tk.name
2521:       when "attr_reader"   then rw = "R"
2522:       when "attr_writer"   then rw = "W"
2523:       when "attr_accessor" then rw = "RW"
2524:       else
2525:         rw = @options.extra_accessor_flags[tk.name]
2526:       end
2527:       
2528:       for name in args
2529:         att = Attr.new(get_tkread, name, rw, comment)
2530:         context.add_attribute(att)
2531:       end    
2532:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2156
2156:     def parse_call_parameters(tk)
2157: 
2158:       end_token = case tk
2159:                   when TkLPAREN, TkfLPAREN
2160:                     TkRPAREN
2161:                   when TkRPAREN
2162:                     return ""
2163:                   else
2164:                     TkNL
2165:                   end
2166:       nest = 0
2167: 
2168:       loop do
2169:         puts("Call param: #{tk}, #{@scanner.continue} " +
2170:           "#{@scanner.lex_state} #{nest}") if $DEBUG
2171:         case tk
2172:         when TkSEMICOLON
2173:           break
2174:         when TkLPAREN, TkfLPAREN
2175:           nest += 1
2176:         when end_token
2177:           if end_token == TkRPAREN
2178:             nest -= 1
2179:             break if @scanner.lex_state == EXPR_END and nest <= 0
2180:           else
2181:             break unless @scanner.continue
2182:           end
2183:         when TkCOMMENT
2184:           unget_tk(tk)
2185:           break
2186:         end
2187:         tk = get_tk
2188:       end
2189:       res = get_tkread.tr("\n", " ").strip
2190:       res = "" if res == ";"
2191:       res
2192:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1729
1729:     def parse_class(container, single, tk, comment, &block)
1730:       progress("c")
1731: 
1732:       @stats.num_classes += 1
1733: 
1734:       container, name_t = get_class_or_module(container)
1735: 
1736:       case name_t
1737:       when TkCONSTANT
1738:         name = name_t.name
1739:         superclass = "Object"
1740: 
1741:         if peek_tk.kind_of?(TkLT)
1742:           get_tk
1743:           skip_tkspace(true)
1744:           superclass = get_class_specification
1745:           superclass = "<unknown>" if superclass.empty?
1746:         end
1747: 
1748:         if single == SINGLE
1749:           cls_type = SingleClass
1750:         else
1751:           cls_type = NormalClass
1752:         end
1753: 
1754:         cls = container.add_class(cls_type, name, superclass)
1755:         read_documentation_modifiers(cls, CLASS_MODIFIERS)
1756:         cls.record_location(@top_level)
1757:         parse_statements(cls)
1758:         cls.comment = comment
1759: 
1760:       when TkLSHFT
1761:         case name = get_class_specification
1762:         when "self", container.name
1763:           parse_statements(container, SINGLE, &block)
1764:         else
1765:           other = TopLevel.find_class_named(name)
1766:           unless other
1767: #            other = @top_level.add_class(NormalClass, name, nil)
1768: #            other.record_location(@top_level)
1769: #            other.comment = comment
1770:             other = NormalClass.new("Dummy", nil)
1771:           end
1772:           read_documentation_modifiers(other, CLASS_MODIFIERS)
1773:           parse_statements(other, SINGLE, &block)
1774:         end
1775: 
1776:       else
1777:         warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
1778:       end
1779:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1823
1823:     def parse_constant(container, single, tk, comment)
1824:       name = tk.name
1825:       skip_tkspace(false)
1826:       eq_tk = get_tk
1827: 
1828:       unless eq_tk.kind_of?(TkASSIGN)
1829:         unget_tk(eq_tk)
1830:         return
1831:       end
1832: 
1833: 
1834:       nest = 0
1835:       get_tkread
1836: 
1837:       tk = get_tk
1838:       if tk.kind_of? TkGT
1839:         unget_tk(tk)
1840:         unget_tk(eq_tk)
1841:         return
1842:       end
1843: 
1844:       loop do
1845:         puts("Param: #{tk}, #{@scanner.continue} " +
1846:           "#{@scanner.lex_state} #{nest}")  if $DEBUG
1847: 
1848:         case tk
1849:         when TkSEMICOLON
1850:           break
1851:         when TkLPAREN, TkfLPAREN
1852:           nest += 1
1853:         when TkRPAREN
1854:           nest -= 1
1855:         when TkCOMMENT
1856:           if nest <= 0 && @scanner.lex_state == EXPR_END
1857:             unget_tk(tk)
1858:             break
1859:           end
1860:         when TkNL
1861:           if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue
1862:             unget_tk(tk)
1863:             break
1864:           end
1865:         end
1866:         tk = get_tk
1867:       end
1868: 
1869:       res = get_tkread.tr("\n", " ").strip
1870:       res = "" if res == ";"
1871:       con = Constant.new(name, res, comment)
1872:       read_documentation_modifiers(con, CONSTANT_MODIFIERS)
1873:       if con.document_self
1874:         container.add_constant(con)
1875:       end
1876:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2429
2429:   def parse_include(context, comment)
2430:     loop do
2431:       skip_tkspace_comment
2432:       name = get_constant_with_optional_parens
2433:       unless name.empty?
2434:         context.add_include(Include.new(name, comment))
2435:       end
2436:       return unless peek_tk.kind_of?(TkCOMMA)
2437:       get_tk
2438:     end
2439:   end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1878
1878:     def parse_method(container, single, tk, comment)
1879:       progress(".")
1880:       @stats.num_methods += 1
1881:       line_no = tk.line_no
1882:       column  = tk.char_no
1883:       
1884:       start_collecting_tokens
1885:       add_token(tk)
1886:       add_token_listener(self)
1887:       
1888:       @scanner.instance_eval{@lex_state = EXPR_FNAME}
1889:       skip_tkspace(false)
1890:       name_t = get_tk
1891:       back_tk = skip_tkspace
1892:       meth = nil
1893:       added_container = false
1894: 
1895:       dot = get_tk
1896:       if dot.kind_of?(TkDOT) or dot.kind_of?(TkCOLON2)
1897:         @scanner.instance_eval{@lex_state = EXPR_FNAME}
1898:         skip_tkspace
1899:         name_t2 = get_tk
1900:         case name_t
1901:         when TkSELF
1902:           name = name_t2.name
1903:         when TkCONSTANT
1904:           name = name_t2.name
1905:           prev_container = container
1906:           container = container.find_module_named(name_t.name)
1907:           if !container
1908:             added_container = true
1909:             obj = name_t.name.split("::").inject(Object) do |state, item|
1910:               state.const_get(item)
1911:             end rescue nil
1912: 
1913:             type = obj.class == Class ? NormalClass : NormalModule
1914:             if not [Class, Module].include?(obj.class)
1915:               warn("Couldn't find #{name_t.name}. Assuming it's a module")
1916:             end
1917: 
1918:             if type == NormalClass then
1919:               container = prev_container.add_class(type, name_t.name, obj.superclass.name)
1920:             else
1921:               container = prev_container.add_module(type, name_t.name)
1922:             end
1923:           end
1924:         else
1925:           # warn("Unexpected token '#{name_t2.inspect}'")
1926:           # break
1927:           skip_method(container)
1928:           return
1929:         end
1930:         meth =  AnyMethod.new(get_tkread, name)
1931:         meth.singleton = true
1932:       else
1933:         unget_tk dot
1934:         back_tk.reverse_each do
1935:           |tk|
1936:           unget_tk tk
1937:         end
1938:         name = name_t.name
1939: 
1940:         meth =  AnyMethod.new(get_tkread, name)
1941:         meth.singleton = (single == SINGLE)
1942:       end
1943: 
1944:       remove_token_listener(self)
1945: 
1946:       meth.start_collecting_tokens
1947:       indent = TkSPACE.new(1,1)
1948:       indent.set_text(" " * column)
1949: 
1950:       meth.add_tokens([TkCOMMENT.new(line_no,
1951:                                      1,
1952:                                      "# File #{@top_level.file_absolute_name}, line #{line_no}"),
1953:                         NEWLINE_TOKEN,
1954:                         indent])
1955: 
1956:       meth.add_tokens(@token_stream)
1957: 
1958:       add_token_listener(meth)
1959: 
1960:       @scanner.instance_eval{@continue = false}
1961:       parse_method_parameters(meth)
1962: 
1963:       if meth.document_self
1964:         container.add_method(meth)
1965:       elsif added_container
1966:         container.document_self = false
1967:       end
1968: 
1969:       # Having now read the method parameters and documentation modifiers, we
1970:       # now know whether we have to rename #initialize to ::new
1971: 
1972:       if name == "initialize" && !meth.singleton
1973:         if meth.dont_rename_initialize
1974:           meth.visibility = :protected
1975:         else
1976:           meth.singleton = true
1977:           meth.name = "new"
1978:           meth.visibility = :public
1979:         end
1980:       end
1981:       
1982:       parse_statements(container, single, meth)
1983:       
1984:       remove_token_listener(meth)
1985: 
1986:       # Look for a 'call-seq' in the comment, and override the
1987:       # normal parameter stuff
1988: 
1989:       if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '')
1990:         seq = $1
1991:         seq.gsub!(/^\s*\#\s*/, '')
1992:         meth.call_seq = seq
1993:       end
1994:       
1995:       meth.comment = comment
1996: 
1997:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2022
2022:     def parse_method_or_yield_parameters(method=nil, modifiers=METHOD_MODIFIERS)
2023:       skip_tkspace(false)
2024:       tk = get_tk
2025: 
2026:       # Little hack going on here. In the statement
2027:       #  f = 2*(1+yield)
2028:       # We see the RPAREN as the next token, so we need
2029:       # to exit early. This still won't catch all cases
2030:       # (such as "a = yield + 1"
2031:       end_token = case tk
2032:                   when TkLPAREN, TkfLPAREN
2033:                     TkRPAREN
2034:                   when TkRPAREN
2035:                     return ""
2036:                   else
2037:                     TkNL
2038:                   end
2039:       nest = 0
2040: 
2041:       loop do
2042:         puts("Param: #{tk.inspect}, #{@scanner.continue} " +
2043:           "#{@scanner.lex_state} #{nest}")  if $DEBUG
2044:         case tk
2045:         when TkSEMICOLON
2046:           break
2047:         when TkLBRACE
2048:           nest += 1
2049:         when TkRBRACE
2050:           # we might have a.each {|i| yield i }
2051:           unget_tk(tk) if nest.zero?
2052:           nest -= 1
2053:           break if nest <= 0
2054:         when TkLPAREN, TkfLPAREN
2055:           nest += 1
2056:         when end_token
2057:           if end_token == TkRPAREN
2058:             nest -= 1
2059:             break if @scanner.lex_state == EXPR_END and nest <= 0
2060:           else
2061:             break unless @scanner.continue
2062:           end
2063:         when method && method.block_params.nil? && TkCOMMENT
2064:           unget_tk(tk)
2065:           read_documentation_modifiers(method, modifiers)
2066:         end
2067:         tk = get_tk
2068:       end
2069:       res = get_tkread.tr("\n", " ").strip
2070:       res = "" if res == ";"
2071:       res
2072:     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

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2012
2012:     def parse_method_parameters(method)
2013:       res = parse_method_or_yield_parameters(method)
2014:       res = "(" + res + ")" unless res[0] == ?(
2015:       method.params = res unless method.params
2016:       if method.block_params.nil?
2017:           skip_tkspace(false)
2018:           read_documentation_modifiers(method, METHOD_MODIFIERS)
2019:       end
2020:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1781
1781:     def parse_module(container, single, tk, comment)
1782:       progress("m")
1783:       @stats.num_modules += 1
1784:       container, name_t  = get_class_or_module(container)
1785: #      skip_tkspace
1786:       name = name_t.name
1787:       mod = container.add_module(NormalModule, name)
1788:       mod.record_location(@top_level)
1789:       read_documentation_modifiers(mod, CLASS_MODIFIERS)
1790:       parse_statements(mod)
1791:       mod.comment = comment
1792:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2403
2403:   def parse_require(context, comment)
2404:     skip_tkspace_comment
2405:     tk = get_tk
2406:     if tk.kind_of? TkLPAREN
2407:       skip_tkspace_comment
2408:       tk = get_tk
2409:     end
2410: 
2411:     name = nil
2412:     case tk
2413:     when TkSTRING
2414:       name = tk.text
2415: #    when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
2416: #      name = tk.name
2417:     when TkDSTRING
2418:       warn "Skipping require of dynamic string: #{tk.text}"
2419:  #   else
2420:  #     warn "'require' used as variable"
2421:     end
2422:     if name
2423:       context.add_require(Require.new(name, comment))
2424:     else
2425:       unget_tk(tk)
2426:     end
2427:   end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1573
1573:     def parse_statements(container, single=NORMAL, current_method=nil, comment='')
1574:       nest = 1
1575:       save_visibility = container.visibility
1576:       
1577: #      if container.kind_of?(TopLevel)
1578: #      else
1579: #        comment = ''
1580: #      end
1581: 
1582:       non_comment_seen = true
1583:       
1584:       while tk = get_tk
1585:         
1586:         keep_comment = false
1587:         
1588:         non_comment_seen = true unless tk.kind_of?(TkCOMMENT)
1589:         
1590:         case tk
1591: 
1592:         when TkNL
1593:           skip_tkspace(true)   # Skip blanks and newlines
1594:           tk = get_tk
1595:           if tk.kind_of?(TkCOMMENT)
1596:             if non_comment_seen
1597:               comment = ''
1598:               non_comment_seen = false
1599:             end
1600:             while tk.kind_of?(TkCOMMENT)
1601:               comment << tk.text << "\n"
1602:               tk = get_tk          # this is the newline 
1603:               skip_tkspace(false)  # leading spaces
1604:               tk = get_tk
1605:             end
1606:             unless comment.empty?
1607:               look_for_directives_in(container, comment) 
1608:               if container.done_documenting
1609:                 container.ongoing_visibility = save_visibility
1610: #                return
1611:               end
1612:             end
1613:             keep_comment = true
1614:           else
1615:             non_comment_seen = true
1616:           end
1617:           unget_tk(tk)
1618:           keep_comment = true
1619: 
1620: 
1621:         when TkCLASS
1622:           if container.document_children
1623:             parse_class(container, single, tk, comment)
1624:           else
1625:             nest += 1
1626:           end
1627: 
1628:         when TkMODULE
1629:           if container.document_children
1630:             parse_module(container, single, tk, comment)
1631:           else
1632:             nest += 1
1633:           end
1634: 
1635:         when TkDEF
1636:           if container.document_self
1637:             parse_method(container, single, tk, comment)
1638:           else
1639:             nest += 1
1640:           end
1641: 
1642:         when TkCONSTANT
1643:           if container.document_self
1644:             parse_constant(container, single, tk, comment)
1645:           end
1646: 
1647:         when TkALIAS
1648:           if container.document_self
1649:             parse_alias(container, single, tk, comment)
1650:           end
1651: 
1652:         when TkYIELD
1653:           if current_method.nil?
1654:             warn("Warning: yield outside of method") if container.document_self
1655:           else
1656:             parse_yield(container, single, tk, current_method)
1657:           end
1658: 
1659:           # Until and While can have a 'do', which shouldn't increas
1660:           # the nesting. We can't solve the general case, but we can
1661:           # handle most occurrences by ignoring a do at the end of a line
1662: 
1663:         when  TkUNTIL, TkWHILE
1664:           nest += 1
1665:           puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
1666:             "line #{tk.line_no}" if $DEBUG
1667:           skip_optional_do_after_expression
1668: 
1669:           # 'for' is trickier
1670:         when TkFOR
1671:           nest += 1
1672:           puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
1673:             "line #{tk.line_no}" if $DEBUG
1674:           skip_for_variable
1675:           skip_optional_do_after_expression
1676: 
1677:         when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN
1678:           nest += 1
1679:           puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
1680:             "line #{tk.line_no}" if $DEBUG
1681: 
1682:         when TkIDENTIFIER
1683:           if nest == 1 and current_method.nil?
1684:             case tk.name
1685:             when "private", "protected", "public",
1686:                  "private_class_method", "public_class_method"
1687:               parse_visibility(container, single, tk)
1688:               keep_comment = true
1689:             when "attr"
1690:               parse_attr(container, single, tk, comment)
1691:             when /^attr_(reader|writer|accessor)$/, @options.extra_accessors
1692:               parse_attr_accessor(container, single, tk, comment)
1693:             when "alias_method"
1694:               if container.document_self
1695:                 parse_alias(container, single, tk, comment)
1696:               end
1697:             end
1698:           end
1699:           
1700:           case tk.name
1701:           when "require"
1702:             parse_require(container, comment)
1703:           when "include"
1704:             parse_include(container, comment)
1705:           end
1706: 
1707: 
1708:         when TkEND
1709:           nest -= 1
1710:           puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG
1711:           puts "Method = #{current_method.name}" if $DEBUG and current_method
1712:           if nest == 0
1713:             read_documentation_modifiers(container, CLASS_MODIFIERS)
1714:             container.ongoing_visibility = save_visibility
1715:             return
1716:           end
1717: 
1718:         end
1719: 
1720:         comment = '' unless keep_comment
1721:         begin
1722:           get_tkread
1723:           skip_tkspace(false)
1724:         end while peek_tk == TkNL
1725: 
1726:       end
1727:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2542
2542:     def parse_symbol_arg(no = nil)
2543: 
2544:       args = []
2545:       skip_tkspace_comment
2546:       case tk = get_tk
2547:       when TkLPAREN
2548:         loop do
2549:           skip_tkspace_comment
2550:           if tk1 = parse_symbol_in_arg
2551:             args.push tk1
2552:             break if no and args.size >= no
2553:           end
2554:           
2555:           skip_tkspace_comment
2556:           case tk2 = get_tk
2557:           when TkRPAREN
2558:             break
2559:           when TkCOMMA
2560:           else
2561:            warn("unexpected token: '#{tk2.inspect}'") if $DEBUG
2562:             break
2563:           end
2564:         end
2565:       else
2566:         unget_tk tk
2567:         if tk = parse_symbol_in_arg
2568:           args.push tk
2569:           return args if no and args.size >= no
2570:         end
2571: 
2572:         loop do
2573: #         skip_tkspace_comment(false)
2574:           skip_tkspace(false)
2575: 
2576:           tk1 = get_tk
2577:           unless tk1.kind_of?(TkCOMMA) 
2578:             unget_tk tk1
2579:             break
2580:           end
2581:           
2582:           skip_tkspace_comment
2583:           if tk = parse_symbol_in_arg
2584:             args.push tk
2585:             break if no and args.size >= no
2586:           end
2587:         end
2588:       end
2589:       args
2590:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2592
2592:     def parse_symbol_in_arg
2593:       case tk = get_tk
2594:       when TkSYMBOL
2595:         tk.text.sub(/^:/, '')
2596:       when TkSTRING
2597:         eval @read[-1]
2598:       else
2599:         warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG
2600:         nil
2601:       end
2602:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1566
1566:     def parse_toplevel_statements(container)
1567:       comment = collect_first_comment
1568:       look_for_directives_in(container, comment)
1569:       container.comment = comment unless comment.empty?
1570:       parse_statements(container, NORMAL, nil, comment)
1571:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2478
2478:     def parse_visibility(container, single, tk)
2479:       singleton = (single == SINGLE)
2480:       vis = case tk.name
2481:             when "private"   then :private
2482:             when "protected" then :protected
2483:             when "public"    then :public
2484:             when "private_class_method"
2485:               singleton = true
2486:               :private
2487:             when "public_class_method"
2488:               singleton = true
2489:               :public
2490:             else raise "Invalid visibility: #{tk.name}"
2491:             end
2492:             
2493:       skip_tkspace_comment(false)
2494:       case peek_tk
2495:         # Ryan Davis suggested the extension to ignore modifiers, because he
2496:         # often writes
2497:         #
2498:         #   protected unless $TESTING
2499:         #
2500:       when TkNL, TkUNLESS_MOD, TkIF_MOD
2501: #        error("Missing argument") if singleton        
2502:         container.ongoing_visibility = vis
2503:       else
2504:         args = parse_symbol_arg
2505:         container.set_visibility_for(args, vis, singleton)
2506:       end
2507:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2395
2395:   def parse_yield(context, single, tk, method)
2396:     if method.block_params.nil?
2397:       get_tkread
2398:       @scanner.instance_eval{@continue = false}
2399:       method.block_params = parse_yield_parameters
2400:     end
2401:   end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2391
2391:     def parse_yield_parameters
2392:       parse_method_or_yield_parameters
2393:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1532
1532:     def peek_read
1533:       @read.join('')
1534:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1501
1501:     def peek_tk
1502:       unget_tk(tk = get_tk)
1503:       tk
1504:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1449
1449:     def progress(char)
1450:       unless @options.quiet
1451:         @progress.print(char)
1452:         @progress.flush
1453:       end
1454:     end

Directives are modifier comments that can appear after class, module, or method names. For example

  def fred    # :yields:  a, b

or

  class SM  # :nodoc:

we return the directive name and any parameters as a two element array

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2250
2250:     def read_directive(allowed)
2251:       tk = get_tk
2252:       puts "directive: #{tk.inspect}" if $DEBUG
2253:       result = nil
2254:       if tk.kind_of?(TkCOMMENT) 
2255:         if tk.text =~ /\s*:?(\w+):\s*(.*)/
2256:           directive = $1.downcase
2257:           if allowed.include?(directive)
2258:             result = [directive, $2]
2259:           end
2260:         end
2261:       else
2262:         unget_tk(tk)
2263:       end
2264:       result
2265:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2268
2268:     def read_documentation_modifiers(context, allow)
2269:       dir = read_directive(allow)
2270: 
2271:       case dir[0]
2272: 
2273:       when "notnew", "not_new", "not-new"
2274:         context.dont_rename_initialize = true
2275: 
2276:       when "nodoc"
2277:         context.document_self = false
2278:         if dir[1].downcase == "all"
2279:           context.document_children = false
2280:         end
2281: 
2282:       when "doc"
2283:         context.document_self = true
2284:         context.force_documentation = true
2285: 
2286:       when "yield", "yields"
2287:         unless context.params.nil?
2288:           context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
2289:         end
2290:         context.block_params = dir[1]
2291: 
2292:       when "arg", "args"
2293:         context.params = dir[1]
2294:       end if dir
2295:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2348
2348:     def remove_private_comments(comment)
2349:       comment.gsub!(/^#--.*?^#\+\+/m, '')
2350:       comment.sub!(/^#--.*/m, '')
2351:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1461
1461:     def remove_token_listener(obj)
1462:       @token_listeners.delete(obj)
1463:     end

skip the var [in] part of a ‘for’ statement

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2075
2075:     def skip_for_variable
2076:       skip_tkspace(false)
2077:       tk = get_tk
2078:       skip_tkspace(false)
2079:       tk = get_tk
2080:       unget_tk(tk) unless tk.kind_of?(TkIN)
2081:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1999
1999:     def skip_method(container)
2000:       meth =  AnyMethod.new("", "anon")
2001:       parse_method_parameters(meth)
2002:       parse_statements(container, false, meth)
2003:     end

while, until, and for have an optional

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2084
2084:     def skip_optional_do_after_expression
2085:       skip_tkspace(false)
2086:       tk = get_tk
2087:       case tk
2088:       when TkLPAREN, TkfLPAREN
2089:         end_token = TkRPAREN
2090:       else
2091:         end_token = TkNL
2092:       end
2093: 
2094:       nest = 0
2095:       @scanner.instance_eval{@continue = false}
2096: 
2097:       loop do
2098:         puts("\nWhile: #{tk}, #{@scanner.continue} " +
2099:           "#{@scanner.lex_state} #{nest}") if $DEBUG
2100:         case tk
2101:         when TkSEMICOLON
2102:           break
2103:         when TkLPAREN, TkfLPAREN
2104:           nest += 1
2105:         when TkDO
2106:           break if nest.zero?
2107:         when end_token
2108:           if end_token == TkRPAREN
2109:             nest -= 1
2110:             break if @scanner.lex_state == EXPR_END and nest.zero?
2111:           else
2112:             break unless @scanner.continue
2113:           end
2114:         end
2115:         tk = get_tk
2116:       end
2117:       skip_tkspace(false)
2118:       if peek_tk.kind_of? TkDO
2119:         get_tk
2120:       end
2121:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1516
1516:     def skip_tkspace(skip_nl = true)
1517:       tokens = []
1518:       while ((tk = get_tk).kind_of?(TkSPACE) ||
1519:              (skip_nl && tk.kind_of?(TkNL)))
1520:         tokens.push tk
1521:       end
1522:       unget_tk(tk)
1523:       tokens
1524:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 2534
2534:     def skip_tkspace_comment(skip_nl = true)
2535:       loop do
2536:         skip_tkspace(skip_nl)
2537:         return unless peek_tk.kind_of? TkCOMMENT
2538:         get_tk
2539:       end
2540:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1506
1506:     def unget_tk(tk)
1507:       @tokens.unshift tk
1508:       @unget_read.unshift @read.pop
1509: 
1510:       # Remove this token from any listeners
1511:       @token_listeners.each do |obj|
1512:         obj.pop_token
1513:       end if @token_listeners
1514:     end

[Source]

      # File lib/rdoc/parsers/parse_rb.rb, line 1437
1437:     def warn(msg)
1438:       return if @options.quiet
1439:       msg = make_message msg
1440:       $stderr.puts msg
1441:     end

[Validate]