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

Extract code elements from a source file, returning a TopLevel object containing the constituent file elements.

This file is based on rtags

Methods

Included Modules

RubyToken RDoc::TokenStream

Public Class methods

[Source]

      # File parsers/parse_rb.rb, line 1376
1376:   def initialize(top_level, file_name, content, options, stats)
1377:     @options = options
1378:     @stats   = stats
1379:     @size = 0
1380:     @token_listeners = nil
1381:     @input_file_name = file_name
1382:     @scanner = RubyLex.new content, @options
1383:     @scanner.exception_on_syntax_error = false
1384:     @top_level = top_level
1385:     @progress = $stderr unless options.quiet
1386:   end

Public Instance methods

[Source]

      # File parsers/parse_rb.rb, line 1445
1445:   def add_token_listener(obj)
1446:     @token_listeners ||= []
1447:     @token_listeners << obj
1448:   end

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

[Source]

      # File parsers/parse_rb.rb, line 1535
1535:   def collect_first_comment
1536:     skip_tkspace
1537:     res = ''
1538:     first_line = true
1539: 
1540:     tk = get_tk
1541:     while tk.kind_of?(TkCOMMENT)
1542:       if first_line && tk.text[0,2] == "#!"
1543:         skip_tkspace
1544:         tk = get_tk
1545:       else
1546:         res << tk.text << "\n"
1547:         tk = get_tk
1548:         if tk.kind_of? TkNL
1549:           skip_tkspace(false)
1550:           tk = get_tk
1551:         end
1552:       end
1553:       first_line = false
1554:     end
1555:     unget_tk(tk)
1556:     res
1557:   end

[Source]

      # File parsers/parse_rb.rb, line 1432
1432:   def error(msg)
1433:     msg = make_message msg
1434:     $stderr.puts msg
1435:     exit(1)
1436:   end

[Source]

      # File parsers/parse_rb.rb, line 2425
2425:   def get_bool
2426:     skip_tkspace
2427:     tk = get_tk
2428:     case tk
2429:     when TkTRUE
2430:       true
2431:     when TkFALSE, TkNIL
2432:       false
2433:     else
2434:       unget_tk tk
2435:       true
2436:     end
2437:   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 parsers/parse_rb.rb, line 1787
1787:   def get_class_or_module(container)
1788:     skip_tkspace
1789:     name_t = get_tk
1790: 
1791:     # class ::A -> A is in the top level
1792:     if name_t.kind_of?(TkCOLON2)
1793:       name_t = get_tk
1794:       container = @top_level
1795:     end
1796: 
1797:     skip_tkspace(false)
1798: 
1799:     while peek_tk.kind_of?(TkCOLON2)
1800:       prev_container = container
1801:       container = container.find_module_named(name_t.name)
1802:       if !container
1803: #          warn("Couldn't find module #{name_t.name}")
1804:         container = prev_container.add_module RDoc::NormalModule, name_t.name
1805:       end
1806:       get_tk
1807:       name_t = get_tk
1808:     end
1809:     skip_tkspace(false)
1810:     return [container, name_t]
1811:   end

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

[Source]

      # File parsers/parse_rb.rb, line 2117
2117:   def get_class_specification
2118:     tk = get_tk
2119:     return "self" if tk.kind_of?(TkSELF)
2120: 
2121:     res = ""
2122:     while tk.kind_of?(TkCOLON2) ||
2123:         tk.kind_of?(TkCOLON3)   ||
2124:         tk.kind_of?(TkCONSTANT)
2125: 
2126:       res += tk.text
2127:       tk = get_tk
2128:     end
2129: 
2130:     unget_tk(tk)
2131:     skip_tkspace(false)
2132: 
2133:     get_tkread # empty out read buffer
2134: 
2135:     tk = get_tk
2136: 
2137:     case tk
2138:     when TkNL, TkCOMMENT, TkSEMICOLON
2139:       unget_tk(tk)
2140:       return res
2141:     end
2142: 
2143:     res += parse_call_parameters(tk)
2144:     res
2145:   end

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

[Source]

      # File parsers/parse_rb.rb, line 2188
2188:   def get_constant
2189:     res = ""
2190:     skip_tkspace(false)
2191:     tk = get_tk
2192: 
2193:     while tk.kind_of?(TkCOLON2) ||
2194:         tk.kind_of?(TkCOLON3)   ||
2195:         tk.kind_of?(TkCONSTANT)
2196: 
2197:       res += tk.text
2198:       tk = get_tk
2199:     end
2200: 
2201: #      if res.empty?
2202: #        warn("Unexpected token #{tk} in constant")
2203: #      end
2204:     unget_tk(tk)
2205:     res
2206:   end

Get a constant that may be surrounded by parens

[Source]

      # File parsers/parse_rb.rb, line 2210
2210:   def get_constant_with_optional_parens
2211:     skip_tkspace(false)
2212:     nest = 0
2213:     while (tk = peek_tk).kind_of?(TkLPAREN)  || tk.kind_of?(TkfLPAREN)
2214:       get_tk
2215:       skip_tkspace(true)
2216:       nest += 1
2217:     end
2218: 
2219:     name = get_constant
2220: 
2221:     while nest > 0
2222:       skip_tkspace(true)
2223:       tk = get_tk
2224:       nest -= 1 if tk.kind_of?(TkRPAREN)
2225:     end
2226:     name
2227:   end

[Source]

      # File parsers/parse_rb.rb, line 2339
2339:   def get_symbol_or_name
2340:     tk = get_tk
2341:     case tk
2342:     when  TkSYMBOL
2343:       tk.text.sub(/^:/, '')
2344:     when TkId, TkOp
2345:       tk.name
2346:     when TkSTRING
2347:       tk.text
2348:     else
2349:       raise "Name or symbol expected (got #{tk})"
2350:     end
2351:   end

[Source]

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

[Source]

      # File parsers/parse_rb.rb, line 1519
1519:   def get_tkread
1520:     read = @read.join("")
1521:     @read = []
1522:     read
1523:   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 parsers/parse_rb.rb, line 2293
2293:   def look_for_directives_in(context, comment)
2294:     preprocess = RDoc::Markup::PreProcess.new(@input_file_name,
2295:                                               @options.rdoc_include)
2296: 
2297:     preprocess.handle(comment) do |directive, param|
2298:       case directive
2299:       when "stopdoc"
2300:         context.stop_doc
2301:         ""
2302:       when "startdoc"
2303:         context.start_doc
2304:         context.force_documentation = true
2305:         ""
2306: 
2307:       when "enddoc"
2308:         #context.done_documenting = true
2309:         #""
2310:         throw :enddoc
2311: 
2312:       when "main"
2313:         @options.main_page = param
2314:         ""
2315: 
2316:       when "title"
2317:         @options.title = param
2318:         ""
2319: 
2320:       when "section"
2321:         context.set_current_section(param, comment)
2322:         comment = ''
2323:         break
2324: 
2325:       else
2326:         warn "Unrecognized directive '#{directive}'"
2327:         break
2328:       end
2329:     end
2330: 
2331:     remove_private_comments(comment)
2332:   end

[Source]

      # File parsers/parse_rb.rb, line 1418
1418:   def make_message(msg)
1419:     prefix = "\n" + @input_file_name + ":"
1420:     if @scanner
1421:       prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
1422:     end
1423:     return prefix + msg
1424:   end

[Source]

      # File parsers/parse_rb.rb, line 2353
2353:   def parse_alias(context, single, tk, comment)
2354:     skip_tkspace
2355:     if (peek_tk.kind_of? TkLPAREN)
2356:       get_tk
2357:       skip_tkspace
2358:     end
2359:     new_name = get_symbol_or_name
2360:     @scanner.instance_eval{@lex_state = EXPR_FNAME}
2361:     skip_tkspace
2362:     if (peek_tk.kind_of? TkCOMMA)
2363:       get_tk
2364:       skip_tkspace
2365:     end
2366:     old_name = get_symbol_or_name
2367: 
2368:     al = RDoc::Alias.new get_tkread, old_name, new_name, comment
2369:     read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
2370:     if al.document_self
2371:       context.add_alias(al)
2372:     end
2373:   end

[Source]

      # File parsers/parse_rb.rb, line 2439
2439:   def parse_attr(context, single, tk, comment)
2440:     args = parse_symbol_arg(1)
2441:     if args.size > 0
2442:       name = args[0]
2443:       rw = "R"
2444:       skip_tkspace(false)
2445:       tk = get_tk
2446:       if tk.kind_of? TkCOMMA
2447:         rw = "RW" if get_bool
2448:       else
2449:         unget_tk tk
2450:       end
2451:       att = RDoc::Attr.new get_tkread, name, rw, comment
2452:       read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
2453:       if att.document_self
2454:         context.add_attribute(att)
2455:       end
2456:     else
2457:       warn("'attr' ignored - looks like a variable")
2458:     end
2459:   end

[Source]

      # File parsers/parse_rb.rb, line 2492
2492:   def parse_attr_accessor(context, single, tk, comment)
2493:     args = parse_symbol_arg
2494:     read = get_tkread
2495:     rw = "?"
2496: 
2497:     # If nodoc is given, don't document any of them
2498: 
2499:     tmp = RDoc::CodeObject.new
2500:     read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
2501:     return unless tmp.document_self
2502: 
2503:     case tk.name
2504:     when "attr_reader"   then rw = "R"
2505:     when "attr_writer"   then rw = "W"
2506:     when "attr_accessor" then rw = "RW"
2507:     else
2508:       rw = @options.extra_accessor_flags[tk.name]
2509:     end
2510: 
2511:     for name in args
2512:       att = RDoc::Attr.new get_tkread, name, rw, comment
2513:       context.add_attribute att
2514:     end
2515:   end

[Source]

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

[Source]

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

[Source]

      # File parsers/parse_rb.rb, line 1813
1813:   def parse_constant(container, single, tk, comment)
1814:     name = tk.name
1815:     skip_tkspace(false)
1816:     eq_tk = get_tk
1817: 
1818:     unless eq_tk.kind_of?(TkASSIGN)
1819:       unget_tk(eq_tk)
1820:       return
1821:     end
1822: 
1823: 
1824:     nest = 0
1825:     get_tkread
1826: 
1827:     tk = get_tk
1828:     if tk.kind_of? TkGT
1829:       unget_tk(tk)
1830:       unget_tk(eq_tk)
1831:       return
1832:     end
1833: 
1834:     loop do
1835:       puts "Param: %p, %s %s %s" %
1836:         [tk.text, @scanner.continue, @scanner.lex_state, nest] if $DEBUG_RDOC
1837: 
1838:         case tk
1839:         when TkSEMICOLON
1840:           break
1841:         when TkLPAREN, TkfLPAREN
1842:           nest += 1
1843:         when TkRPAREN
1844:           nest -= 1
1845:         when TkCOMMENT
1846:           if nest <= 0 && @scanner.lex_state == EXPR_END
1847:             unget_tk(tk)
1848:             break
1849:           end
1850:         when TkNL
1851:           if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue
1852:             unget_tk(tk)
1853:             break
1854:           end
1855:         end
1856:         tk = get_tk
1857:     end
1858: 
1859:     res = get_tkread.tr("\n", " ").strip
1860:     res = "" if res == ";"
1861: 
1862:     con = RDoc::Constant.new name, res, comment
1863:     read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
1864: 
1865:     if con.document_self
1866:       container.add_constant(con)
1867:     end
1868:   end

[Source]

      # File parsers/parse_rb.rb, line 2413
2413:   def parse_include(context, comment)
2414:     loop do
2415:       skip_tkspace_comment
2416:       name = get_constant_with_optional_parens
2417:       unless name.empty?
2418:         context.add_include RDoc::Include.new(name, comment)
2419:       end
2420:       return unless peek_tk.kind_of?(TkCOMMA)
2421:       get_tk
2422:     end
2423:   end

[Source]

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

[Source]

      # File parsers/parse_rb.rb, line 2012
2012:   def parse_method_or_yield_parameters(method = nil,
2013:                                        modifiers = RDoc::METHOD_MODIFIERS)
2014:     skip_tkspace(false)
2015:     tk = get_tk
2016: 
2017:     # Little hack going on here. In the statement
2018:     #  f = 2*(1+yield)
2019:     # We see the RPAREN as the next token, so we need
2020:     # to exit early. This still won't catch all cases
2021:     # (such as "a = yield + 1"
2022:     end_token = case tk
2023:                 when TkLPAREN, TkfLPAREN
2024:                   TkRPAREN
2025:                 when TkRPAREN
2026:                   return ""
2027:                 else
2028:                   TkNL
2029:                 end
2030:     nest = 0
2031: 
2032:     loop do
2033:       puts "Param: %p, %s %s %s" %
2034:         [tk.text, @scanner.continue, @scanner.lex_state, nest] if $DEBUG_RDOC
2035:         case tk
2036:         when TkSEMICOLON
2037:           break
2038:         when TkLBRACE
2039:           nest += 1
2040:         when TkRBRACE
2041:           # we might have a.each {|i| yield i }
2042:           unget_tk(tk) if nest.zero?
2043:           nest -= 1
2044:           break if nest <= 0
2045:         when TkLPAREN, TkfLPAREN
2046:           nest += 1
2047:         when end_token
2048:           if end_token == TkRPAREN
2049:             nest -= 1
2050:             break if @scanner.lex_state == EXPR_END and nest <= 0
2051:           else
2052:             break unless @scanner.continue
2053:           end
2054:         when method && method.block_params.nil? && TkCOMMENT
2055:           unget_tk(tk)
2056:           read_documentation_modifiers(method, modifiers)
2057:         end
2058:       tk = get_tk
2059:     end
2060:     res = get_tkread.tr("\n", " ").strip
2061:     res = "" if res == ";"
2062:     res
2063:   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 parsers/parse_rb.rb, line 2002
2002:   def parse_method_parameters(method)
2003:     res = parse_method_or_yield_parameters(method)
2004:     res = "(" + res + ")" unless res[0] == ?(
2005:     method.params = res unless method.params
2006:     if method.block_params.nil?
2007:       skip_tkspace(false)
2008:       read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
2009:     end
2010:   end

[Source]

      # File parsers/parse_rb.rb, line 1771
1771:   def parse_module(container, single, tk, comment)
1772:     progress("m")
1773:     @stats.num_modules += 1
1774:     container, name_t  = get_class_or_module(container)
1775: #      skip_tkspace
1776:     name = name_t.name
1777:     mod = container.add_module RDoc::NormalModule, name
1778:     mod.record_location @top_level
1779:     read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
1780:     parse_statements(mod)
1781:     mod.comment = comment
1782:   end

[Source]

      # File parsers/parse_rb.rb, line 2387
2387:   def parse_require(context, comment)
2388:     skip_tkspace_comment
2389:     tk = get_tk
2390:     if tk.kind_of? TkLPAREN
2391:       skip_tkspace_comment
2392:       tk = get_tk
2393:     end
2394: 
2395:     name = nil
2396:     case tk
2397:     when TkSTRING
2398:       name = tk.text
2399:       #    when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
2400:       #      name = tk.name
2401:     when TkDSTRING
2402:       warn "Skipping require of dynamic string: #{tk.text}"
2403:       #   else
2404:       #     warn "'require' used as variable"
2405:     end
2406:     if name
2407:       context.add_require(RDoc::Require.new(name, comment))
2408:     else
2409:       unget_tk(tk)
2410:     end
2411:   end

[Source]

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

[Source]

      # File parsers/parse_rb.rb, line 2525
2525:   def parse_symbol_arg(no = nil)
2526:     args = []
2527:     skip_tkspace_comment
2528:     case tk = get_tk
2529:     when TkLPAREN
2530:       loop do
2531:         skip_tkspace_comment
2532:         if tk1 = parse_symbol_in_arg
2533:           args.push tk1
2534:           break if no and args.size >= no
2535:         end
2536: 
2537:         skip_tkspace_comment
2538:         case tk2 = get_tk
2539:         when TkRPAREN
2540:           break
2541:         when TkCOMMA
2542:         else
2543:           warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
2544:           break
2545:         end
2546:       end
2547:     else
2548:       unget_tk tk
2549:       if tk = parse_symbol_in_arg
2550:         args.push tk
2551:         return args if no and args.size >= no
2552:       end
2553: 
2554:       loop do
2555:         #         skip_tkspace_comment(false)
2556:         skip_tkspace(false)
2557: 
2558:         tk1 = get_tk
2559:         unless tk1.kind_of?(TkCOMMA)
2560:           unget_tk tk1
2561:           break
2562:         end
2563: 
2564:         skip_tkspace_comment
2565:         if tk = parse_symbol_in_arg
2566:           args.push tk
2567:           break if no and args.size >= no
2568:         end
2569:       end
2570:     end
2571:     args
2572:   end

[Source]

      # File parsers/parse_rb.rb, line 2574
2574:   def parse_symbol_in_arg
2575:     case tk = get_tk
2576:     when TkSYMBOL
2577:       tk.text.sub(/^:/, '')
2578:     when TkSTRING
2579:       eval @read[-1]
2580:     else
2581:       warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
2582:       nil
2583:     end
2584:   end

[Source]

      # File parsers/parse_rb.rb, line 1559
1559:   def parse_toplevel_statements(container)
1560:     comment = collect_first_comment
1561:     look_for_directives_in(container, comment)
1562:     container.comment = comment unless comment.empty?
1563:     parse_statements(container, NORMAL, nil, comment)
1564:   end

[Source]

      # File parsers/parse_rb.rb, line 2461
2461:   def parse_visibility(container, single, tk)
2462:     singleton = (single == SINGLE)
2463:     vis = case tk.name
2464:           when "private"   then :private
2465:           when "protected" then :protected
2466:           when "public"    then :public
2467:           when "private_class_method"
2468:             singleton = true
2469:             :private
2470:           when "public_class_method"
2471:             singleton = true
2472:             :public
2473:           else raise "Invalid visibility: #{tk.name}"
2474:           end
2475: 
2476:     skip_tkspace_comment(false)
2477:     case peek_tk
2478:       # Ryan Davis suggested the extension to ignore modifiers, because he
2479:       # often writes
2480:       #
2481:       #   protected unless $TESTING
2482:       #
2483:     when TkNL, TkUNLESS_MOD, TkIF_MOD
2484:       #        error("Missing argument") if singleton
2485:       container.ongoing_visibility = vis
2486:     else
2487:       args = parse_symbol_arg
2488:       container.set_visibility_for(args, vis, singleton)
2489:     end
2490:   end

[Source]

      # File parsers/parse_rb.rb, line 2379
2379:   def parse_yield(context, single, tk, method)
2380:     if method.block_params.nil?
2381:       get_tkread
2382:       @scanner.instance_eval{@continue = false}
2383:       method.block_params = parse_yield_parameters
2384:     end
2385:   end

[Source]

      # File parsers/parse_rb.rb, line 2375
2375:   def parse_yield_parameters
2376:     parse_method_or_yield_parameters
2377:   end

[Source]

      # File parsers/parse_rb.rb, line 1525
1525:   def peek_read
1526:     @read.join('')
1527:   end

[Source]

      # File parsers/parse_rb.rb, line 1494
1494:   def peek_tk
1495:     unget_tk(tk = get_tk)
1496:     tk
1497:   end

[Source]

      # File parsers/parse_rb.rb, line 1438
1438:   def progress(char)
1439:     unless @options.quiet
1440:       @progress.print(char)
1441:       @progress.flush
1442:     end
1443:   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

[Source]

      # File parsers/parse_rb.rb, line 2240
2240:   def read_directive(allowed)
2241:     tk = get_tk
2242:     puts "directive: #{tk.text.inspect}" if $DEBUG_RDOC
2243:     result = nil
2244:     if tk.kind_of?(TkCOMMENT)
2245:       if tk.text =~ /\s*:?(\w+):\s*(.*)/
2246:         directive = $1.downcase
2247:         if allowed.include?(directive)
2248:           result = [directive, $2]
2249:         end
2250:       end
2251:     else
2252:       unget_tk(tk)
2253:     end
2254:     result
2255:   end

[Source]

      # File parsers/parse_rb.rb, line 2257
2257:   def read_documentation_modifiers(context, allow)
2258:     dir = read_directive(allow)
2259: 
2260:     case dir[0]
2261: 
2262:     when "notnew", "not_new", "not-new"
2263:       context.dont_rename_initialize = true
2264: 
2265:     when "nodoc"
2266:       context.document_self = false
2267:       if dir[1].downcase == "all"
2268:         context.document_children = false
2269:       end
2270: 
2271:     when "doc"
2272:       context.document_self = true
2273:       context.force_documentation = true
2274: 
2275:     when "yield", "yields"
2276:       unless context.params.nil?
2277:         context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
2278:       end
2279:     context.block_params = dir[1]
2280: 
2281:     when "arg", "args"
2282:       context.params = dir[1]
2283:     end if dir
2284:   end

[Source]

      # File parsers/parse_rb.rb, line 2334
2334:   def remove_private_comments(comment)
2335:     comment.gsub!(/^#--.*?^#\+\+/m, '')
2336:     comment.sub!(/^#--.*/m, '')
2337:   end

[Source]

      # File parsers/parse_rb.rb, line 1450
1450:   def remove_token_listener(obj)
1451:     @token_listeners.delete(obj)
1452:   end

[Source]

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

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

[Source]

      # File parsers/parse_rb.rb, line 2066
2066:   def skip_for_variable
2067:     skip_tkspace(false)
2068:     tk = get_tk
2069:     skip_tkspace(false)
2070:     tk = get_tk
2071:     unget_tk(tk) unless tk.kind_of?(TkIN)
2072:   end

[Source]

      # File parsers/parse_rb.rb, line 1989
1989:   def skip_method(container)
1990:     meth = RDoc::AnyMethod.new "", "anon"
1991:     parse_method_parameters(meth)
1992:     parse_statements(container, false, meth)
1993:   end

while, until, and for have an optional

[Source]

      # File parsers/parse_rb.rb, line 2075
2075:   def skip_optional_do_after_expression
2076:     skip_tkspace(false)
2077:     tk = get_tk
2078:     case tk
2079:     when TkLPAREN, TkfLPAREN
2080:       end_token = TkRPAREN
2081:     else
2082:       end_token = TkNL
2083:     end
2084: 
2085:     nest = 0
2086:     @scanner.instance_eval{@continue = false}
2087: 
2088:     loop do
2089:       puts("\nWhile: #{tk.text.inspect}, #{@scanner.continue} " \
2090:            "#{@scanner.lex_state} #{nest}") if $DEBUG_RDOC
2091:       case tk
2092:       when TkSEMICOLON
2093:         break
2094:       when TkLPAREN, TkfLPAREN
2095:         nest += 1
2096:       when TkDO
2097:         break if nest.zero?
2098:       when end_token
2099:         if end_token == TkRPAREN
2100:           nest -= 1
2101:           break if @scanner.lex_state == EXPR_END and nest.zero?
2102:         else
2103:           break unless @scanner.continue
2104:         end
2105:       end
2106:       tk = get_tk
2107:     end
2108:     skip_tkspace(false)
2109:     if peek_tk.kind_of? TkDO
2110:       get_tk
2111:     end
2112:   end

[Source]

      # File parsers/parse_rb.rb, line 1509
1509:   def skip_tkspace(skip_nl = true)
1510:     tokens = []
1511:     while ((tk = get_tk).kind_of?(TkSPACE) ||
1512:      (skip_nl && tk.kind_of?(TkNL)))
1513:       tokens.push tk
1514:     end
1515:     unget_tk(tk)
1516:     tokens
1517:   end

[Source]

      # File parsers/parse_rb.rb, line 2517
2517:   def skip_tkspace_comment(skip_nl = true)
2518:     loop do
2519:       skip_tkspace(skip_nl)
2520:       return unless peek_tk.kind_of? TkCOMMENT
2521:       get_tk
2522:     end
2523:   end

[Source]

      # File parsers/parse_rb.rb, line 1499
1499:   def unget_tk(tk)
1500:     @tokens.unshift tk
1501:     @unget_read.unshift @read.pop
1502: 
1503:     # Remove this token from any listeners
1504:     @token_listeners.each do |obj|
1505:       obj.pop_token
1506:     end if @token_listeners
1507:   end

[Source]

      # File parsers/parse_rb.rb, line 1426
1426:   def warn(msg)
1427:     return if @options.quiet
1428:     msg = make_message msg
1429:     $stderr.puts msg
1430:   end

[Validate]