| Class | RDoc::RubyParser |
| In: |
lib/rdoc/parsers/parse_rb.rb
|
| Parent: | Object |
| NORMAL | = | "::" |
| SINGLE | = | "<<" |
# 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
# 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
# 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.
# 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 && /\A#!/ =~ tk.text
1550: skip_tkspace
1551: tk = get_tk
1552: elsif first_line && /\A#\s*-\*-/ =~ tk.text
1553: first_line = false
1554: skip_tkspace
1555: tk = get_tk
1556: else
1557: first_line = false
1558: res << tk.text << "\n"
1559: tk = get_tk
1560: if tk.kind_of? TkNL
1561: skip_tkspace(false)
1562: tk = get_tk
1563: end
1564: end
1565: end
1566: unget_tk(tk)
1567: res
1568: end
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2445
2445: def get_bool
2446: skip_tkspace
2447: tk = get_tk
2448: case tk
2449: when TkTRUE
2450: true
2451: when TkFALSE, TkNIL
2452: false
2453: else
2454: unget_tk tk
2455: true
2456: end
2457: 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 |
# File lib/rdoc/parsers/parse_rb.rb, line 1801
1801: def get_class_or_module(container)
1802: skip_tkspace
1803: name_t = get_tk
1804:
1805: # class ::A -> A is in the top level
1806: if name_t.kind_of?(TkCOLON2)
1807: name_t = get_tk
1808: container = @top_level
1809: end
1810:
1811: skip_tkspace(false)
1812:
1813: while peek_tk.kind_of?(TkCOLON2)
1814: prev_container = container
1815: container = container.find_module_named(name_t.name)
1816: if !container
1817: # warn("Couldn't find module #{name_t.name}")
1818: container = prev_container.add_module(NormalModule, name_t.name)
1819: end
1820: get_tk
1821: name_t = get_tk
1822: end
1823: skip_tkspace(false)
1824: return [container, name_t]
1825: end
Return a superclass, which can be either a constant of an expression
# File lib/rdoc/parsers/parse_rb.rb, line 2130
2130: def get_class_specification
2131: tk = get_tk
2132: return "self" if tk.kind_of?(TkSELF)
2133:
2134: res = ""
2135: while tk.kind_of?(TkCOLON2) ||
2136: tk.kind_of?(TkCOLON3) ||
2137: tk.kind_of?(TkCONSTANT)
2138:
2139: res += tk.text
2140: tk = get_tk
2141: end
2142:
2143: unget_tk(tk)
2144: skip_tkspace(false)
2145:
2146: get_tkread # empty out read buffer
2147:
2148: tk = get_tk
2149:
2150: case tk
2151: when TkNL, TkCOMMENT, TkSEMICOLON
2152: unget_tk(tk)
2153: return res
2154: end
2155:
2156: res += parse_call_parameters(tk)
2157: res
2158: end
Parse a constant, which might be qualified by one or more class or module names
# File lib/rdoc/parsers/parse_rb.rb, line 2202
2202: def get_constant
2203: res = ""
2204: skip_tkspace(false)
2205: tk = get_tk
2206:
2207: while tk.kind_of?(TkCOLON2) ||
2208: tk.kind_of?(TkCOLON3) ||
2209: tk.kind_of?(TkCONSTANT)
2210:
2211: res += tk.text
2212: tk = get_tk
2213: end
2214:
2215: # if res.empty?
2216: # warn("Unexpected token #{tk} in constant")
2217: # end
2218: unget_tk(tk)
2219: res
2220: end
Get a constant that may be surrounded by parens
# File lib/rdoc/parsers/parse_rb.rb, line 2224
2224: def get_constant_with_optional_parens
2225: skip_tkspace(false)
2226: nest = 0
2227: while (tk = peek_tk).kind_of?(TkLPAREN) || tk.kind_of?(TkfLPAREN)
2228: get_tk
2229: skip_tkspace(true)
2230: nest += 1
2231: end
2232:
2233: name = get_constant
2234:
2235: while nest > 0
2236: skip_tkspace(true)
2237: tk = get_tk
2238: nest -= 1 if tk.kind_of?(TkRPAREN)
2239: end
2240: name
2241: end
# File lib/rdoc/parsers/parse_rb.rb, line 2359
2359: def get_symbol_or_name
2360: tk = get_tk
2361: case tk
2362: when TkSYMBOL
2363: tk.text.sub(/^:/, '')
2364: when TkId, TkOp
2365: tk.name
2366: when TkSTRING
2367: tk.text
2368: else
2369: raise "Name or symbol expected (got #{tk})"
2370: end
2371: end
# 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
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2309
2309: def look_for_directives_in(context, comment)
2310:
2311: preprocess = SM::PreProcess.new(@input_file_name,
2312: @options.rdoc_include)
2313:
2314: preprocess.handle(comment) do |directive, param|
2315: case directive
2316: when "stopdoc"
2317: context.stop_doc
2318: ""
2319: when "startdoc"
2320: context.start_doc
2321: context.force_documentation = true
2322: ""
2323:
2324: when "enddoc"
2325: #context.done_documenting = true
2326: #""
2327: throw :enddoc
2328:
2329: when "main"
2330: options = Options.instance
2331: options.main_page = param
2332: ""
2333:
2334: when "title"
2335: options = Options.instance
2336: options.title = param
2337: ""
2338:
2339: when "section"
2340: context.set_current_section(param, comment)
2341: comment.replace("") # 1.8 doesn't support #clear
2342: break
2343: else
2344: warn "Unrecognized directive '#{directive}'"
2345: break
2346: end
2347: end
2348:
2349: remove_private_comments(comment)
2350: end
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2373
2373: def parse_alias(context, single, tk, comment)
2374: skip_tkspace
2375: if (peek_tk.kind_of? TkLPAREN)
2376: get_tk
2377: skip_tkspace
2378: end
2379: new_name = get_symbol_or_name
2380: @scanner.instance_eval{@lex_state = EXPR_FNAME}
2381: skip_tkspace
2382: if (peek_tk.kind_of? TkCOMMA)
2383: get_tk
2384: skip_tkspace
2385: end
2386: old_name = get_symbol_or_name
2387:
2388: al = Alias.new(get_tkread, old_name, new_name, comment)
2389: read_documentation_modifiers(al, ATTR_MODIFIERS)
2390: if al.document_self
2391: context.add_alias(al)
2392: end
2393: end
# File lib/rdoc/parsers/parse_rb.rb, line 2459
2459: def parse_attr(context, single, tk, comment)
2460: args = parse_symbol_arg(1)
2461: if args.size > 0
2462: name = args[0]
2463: rw = "R"
2464: skip_tkspace(false)
2465: tk = get_tk
2466: if tk.kind_of? TkCOMMA
2467: rw = "RW" if get_bool
2468: else
2469: unget_tk tk
2470: end
2471: att = Attr.new(get_tkread, name, rw, comment)
2472: read_documentation_modifiers(att, ATTR_MODIFIERS)
2473: if att.document_self
2474: context.add_attribute(att)
2475: end
2476: else
2477: warn("'attr' ignored - looks like a variable")
2478: end
2479:
2480: end
# File lib/rdoc/parsers/parse_rb.rb, line 2513
2513: def parse_attr_accessor(context, single, tk, comment)
2514: args = parse_symbol_arg
2515: read = get_tkread
2516: rw = "?"
2517:
2518: # If nodoc is given, don't document any of them
2519:
2520: tmp = CodeObject.new
2521: read_documentation_modifiers(tmp, ATTR_MODIFIERS)
2522: return unless tmp.document_self
2523:
2524: case tk.name
2525: when "attr_reader" then rw = "R"
2526: when "attr_writer" then rw = "W"
2527: when "attr_accessor" then rw = "RW"
2528: else
2529: rw = @options.extra_accessor_flags[tk.name]
2530: end
2531:
2532: for name in args
2533: att = Attr.new(get_tkread, name, rw, comment)
2534: context.add_attribute(att)
2535: end
2536: end
# File lib/rdoc/parsers/parse_rb.rb, line 2160
2160: def parse_call_parameters(tk)
2161:
2162: end_token = case tk
2163: when TkLPAREN, TkfLPAREN
2164: TkRPAREN
2165: when TkRPAREN
2166: return ""
2167: else
2168: TkNL
2169: end
2170: nest = 0
2171:
2172: loop do
2173: puts("Call param: #{tk}, #{@scanner.continue} " +
2174: "#{@scanner.lex_state} #{nest}") if $DEBUG
2175: case tk
2176: when TkSEMICOLON
2177: break
2178: when TkLPAREN, TkfLPAREN
2179: nest += 1
2180: when end_token
2181: if end_token == TkRPAREN
2182: nest -= 1
2183: break if @scanner.lex_state == EXPR_END and nest <= 0
2184: else
2185: break unless @scanner.continue
2186: end
2187: when TkCOMMENT
2188: unget_tk(tk)
2189: break
2190: end
2191: tk = get_tk
2192: end
2193: res = get_tkread.tr("\n", " ").strip
2194: res = "" if res == ";"
2195: res
2196: end
# File lib/rdoc/parsers/parse_rb.rb, line 1733
1733: def parse_class(container, single, tk, comment, &block)
1734: progress("c")
1735:
1736: @stats.num_classes += 1
1737:
1738: container, name_t = get_class_or_module(container)
1739:
1740: case name_t
1741: when TkCONSTANT
1742: name = name_t.name
1743: superclass = "Object"
1744:
1745: if peek_tk.kind_of?(TkLT)
1746: get_tk
1747: skip_tkspace(true)
1748: superclass = get_class_specification
1749: superclass = "<unknown>" if superclass.empty?
1750: end
1751:
1752: if single == SINGLE
1753: cls_type = SingleClass
1754: else
1755: cls_type = NormalClass
1756: end
1757:
1758: cls = container.add_class(cls_type, name, superclass)
1759: read_documentation_modifiers(cls, CLASS_MODIFIERS)
1760: cls.record_location(@top_level)
1761: parse_statements(cls)
1762: cls.comment = comment
1763:
1764: when TkLSHFT
1765: case name = get_class_specification
1766: when "self", container.name
1767: parse_statements(container, SINGLE, &block)
1768: else
1769: other = TopLevel.find_class_named(name)
1770: unless other
1771: # other = @top_level.add_class(NormalClass, name, nil)
1772: # other.record_location(@top_level)
1773: # other.comment = comment
1774: other = NormalClass.new("Dummy", nil)
1775: end
1776: read_documentation_modifiers(other, CLASS_MODIFIERS)
1777: parse_statements(other, SINGLE, &block)
1778: end
1779:
1780: else
1781: warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
1782: end
1783: end
# File lib/rdoc/parsers/parse_rb.rb, line 1827
1827: def parse_constant(container, single, tk, comment)
1828: name = tk.name
1829: skip_tkspace(false)
1830: eq_tk = get_tk
1831:
1832: unless eq_tk.kind_of?(TkASSIGN)
1833: unget_tk(eq_tk)
1834: return
1835: end
1836:
1837:
1838: nest = 0
1839: get_tkread
1840:
1841: tk = get_tk
1842: if tk.kind_of? TkGT
1843: unget_tk(tk)
1844: unget_tk(eq_tk)
1845: return
1846: end
1847:
1848: loop do
1849: puts("Param: #{tk}, #{@scanner.continue} " +
1850: "#{@scanner.lex_state} #{nest}") if $DEBUG
1851:
1852: case tk
1853: when TkSEMICOLON
1854: break
1855: when TkLPAREN, TkfLPAREN
1856: nest += 1
1857: when TkRPAREN
1858: nest -= 1
1859: when TkCOMMENT
1860: if nest <= 0 && @scanner.lex_state == EXPR_END
1861: unget_tk(tk)
1862: break
1863: end
1864: when TkNL
1865: if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue
1866: unget_tk(tk)
1867: break
1868: end
1869: end
1870: tk = get_tk
1871: end
1872:
1873: res = get_tkread.tr("\n", " ").strip
1874: res = "" if res == ";"
1875: con = Constant.new(name, res, comment)
1876: read_documentation_modifiers(con, CONSTANT_MODIFIERS)
1877: if con.document_self
1878: container.add_constant(con)
1879: end
1880: end
# File lib/rdoc/parsers/parse_rb.rb, line 2433
2433: def parse_include(context, comment)
2434: loop do
2435: skip_tkspace_comment
2436: name = get_constant_with_optional_parens
2437: unless name.empty?
2438: context.add_include(Include.new(name, comment))
2439: end
2440: return unless peek_tk.kind_of?(TkCOMMA)
2441: get_tk
2442: end
2443: end
# File lib/rdoc/parsers/parse_rb.rb, line 1882
1882: def parse_method(container, single, tk, comment)
1883: progress(".")
1884: @stats.num_methods += 1
1885: line_no = tk.line_no
1886: column = tk.char_no
1887:
1888: start_collecting_tokens
1889: add_token(tk)
1890: add_token_listener(self)
1891:
1892: @scanner.instance_eval{@lex_state = EXPR_FNAME}
1893: skip_tkspace(false)
1894: name_t = get_tk
1895: back_tk = skip_tkspace
1896: meth = nil
1897: added_container = false
1898:
1899: dot = get_tk
1900: if dot.kind_of?(TkDOT) or dot.kind_of?(TkCOLON2)
1901: @scanner.instance_eval{@lex_state = EXPR_FNAME}
1902: skip_tkspace
1903: name_t2 = get_tk
1904: case name_t
1905: when TkSELF
1906: name = name_t2.name
1907: when TkCONSTANT
1908: name = name_t2.name
1909: prev_container = container
1910: container = container.find_module_named(name_t.name)
1911: if !container
1912: added_container = true
1913: obj = name_t.name.split("::").inject(Object) do |state, item|
1914: state.const_get(item)
1915: end rescue nil
1916:
1917: type = obj.class == Class ? NormalClass : NormalModule
1918: if not [Class, Module].include?(obj.class)
1919: warn("Couldn't find #{name_t.name}. Assuming it's a module")
1920: end
1921:
1922: if type == NormalClass then
1923: container = prev_container.add_class(type, name_t.name, obj.superclass.name)
1924: else
1925: container = prev_container.add_module(type, name_t.name)
1926: end
1927: end
1928: else
1929: # warn("Unexpected token '#{name_t2.inspect}'")
1930: # break
1931: skip_method(container)
1932: return
1933: end
1934: meth = AnyMethod.new(get_tkread, name)
1935: meth.singleton = true
1936: else
1937: unget_tk dot
1938: back_tk.reverse_each do
1939: |tk|
1940: unget_tk tk
1941: end
1942: name = name_t.name
1943:
1944: meth = AnyMethod.new(get_tkread, name)
1945: meth.singleton = (single == SINGLE)
1946: end
1947:
1948: remove_token_listener(self)
1949:
1950: meth.start_collecting_tokens
1951: indent = TkSPACE.new(1,1)
1952: indent.set_text(" " * column)
1953:
1954: meth.add_tokens([TkCOMMENT.new(line_no,
1955: 1,
1956: "# File #{@top_level.file_absolute_name}, line #{line_no}"),
1957: NEWLINE_TOKEN,
1958: indent])
1959:
1960: meth.add_tokens(@token_stream)
1961:
1962: add_token_listener(meth)
1963:
1964: @scanner.instance_eval{@continue = false}
1965: parse_method_parameters(meth)
1966:
1967: if meth.document_self
1968: container.add_method(meth)
1969: elsif added_container
1970: container.document_self = false
1971: end
1972:
1973: # Having now read the method parameters and documentation modifiers, we
1974: # now know whether we have to rename #initialize to ::new
1975:
1976: if name == "initialize" && !meth.singleton
1977: if meth.dont_rename_initialize
1978: meth.visibility = :protected
1979: else
1980: meth.singleton = true
1981: meth.name = "new"
1982: meth.visibility = :public
1983: end
1984: end
1985:
1986: parse_statements(container, single, meth)
1987:
1988: remove_token_listener(meth)
1989:
1990: # Look for a 'call-seq' in the comment, and override the
1991: # normal parameter stuff
1992:
1993: if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '')
1994: seq = $1
1995: seq.gsub!(/^\s*\#\s*/, '')
1996: meth.call_seq = seq
1997: end
1998:
1999: meth.comment = comment
2000:
2001: end
# File lib/rdoc/parsers/parse_rb.rb, line 2026
2026: def parse_method_or_yield_parameters(method=nil, modifiers=METHOD_MODIFIERS)
2027: skip_tkspace(false)
2028: tk = get_tk
2029:
2030: # Little hack going on here. In the statement
2031: # f = 2*(1+yield)
2032: # We see the RPAREN as the next token, so we need
2033: # to exit early. This still won't catch all cases
2034: # (such as "a = yield + 1"
2035: end_token = case tk
2036: when TkLPAREN, TkfLPAREN
2037: TkRPAREN
2038: when TkRPAREN
2039: return ""
2040: else
2041: TkNL
2042: end
2043: nest = 0
2044:
2045: loop do
2046: puts("Param: #{tk.inspect}, #{@scanner.continue} " +
2047: "#{@scanner.lex_state} #{nest}") if $DEBUG
2048: case tk
2049: when TkSEMICOLON
2050: break
2051: when TkLBRACE
2052: nest += 1
2053: when TkRBRACE
2054: # we might have a.each {|i| yield i }
2055: unget_tk(tk) if nest.zero?
2056: nest -= 1
2057: break if nest <= 0
2058: when TkLPAREN, TkfLPAREN
2059: nest += 1
2060: when end_token
2061: if end_token == TkRPAREN
2062: nest -= 1
2063: break if @scanner.lex_state == EXPR_END and nest <= 0
2064: else
2065: break unless @scanner.continue
2066: end
2067: when method && method.block_params.nil? && TkCOMMENT
2068: unget_tk(tk)
2069: read_documentation_modifiers(method, modifiers)
2070: end
2071: tk = get_tk
2072: end
2073: res = get_tkread.tr("\n", " ").strip
2074: res = "" if res == ";"
2075: res
2076: 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
# File lib/rdoc/parsers/parse_rb.rb, line 2016
2016: def parse_method_parameters(method)
2017: res = parse_method_or_yield_parameters(method)
2018: res = "(" + res + ")" unless res[0] == ?(
2019: method.params = res unless method.params
2020: if method.block_params.nil?
2021: skip_tkspace(false)
2022: read_documentation_modifiers(method, METHOD_MODIFIERS)
2023: end
2024: end
# File lib/rdoc/parsers/parse_rb.rb, line 1785
1785: def parse_module(container, single, tk, comment)
1786: progress("m")
1787: @stats.num_modules += 1
1788: container, name_t = get_class_or_module(container)
1789: # skip_tkspace
1790: name = name_t.name
1791: mod = container.add_module(NormalModule, name)
1792: mod.record_location(@top_level)
1793: read_documentation_modifiers(mod, CLASS_MODIFIERS)
1794: parse_statements(mod)
1795: mod.comment = comment
1796: end
# File lib/rdoc/parsers/parse_rb.rb, line 2407
2407: def parse_require(context, comment)
2408: skip_tkspace_comment
2409: tk = get_tk
2410: if tk.kind_of? TkLPAREN
2411: skip_tkspace_comment
2412: tk = get_tk
2413: end
2414:
2415: name = nil
2416: case tk
2417: when TkSTRING
2418: name = tk.text
2419: # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
2420: # name = tk.name
2421: when TkDSTRING
2422: warn "Skipping require of dynamic string: #{tk.text}"
2423: # else
2424: # warn "'require' used as variable"
2425: end
2426: if name
2427: context.add_require(Require.new(name, comment))
2428: else
2429: unget_tk(tk)
2430: end
2431: end
# File lib/rdoc/parsers/parse_rb.rb, line 1577
1577: def parse_statements(container, single=NORMAL, current_method=nil, comment='')
1578: nest = 1
1579: save_visibility = container.visibility
1580:
1581: # if container.kind_of?(TopLevel)
1582: # else
1583: # comment = ''
1584: # end
1585:
1586: non_comment_seen = true
1587:
1588: while tk = get_tk
1589:
1590: keep_comment = false
1591:
1592: non_comment_seen = true unless tk.kind_of?(TkCOMMENT)
1593:
1594: case tk
1595:
1596: when TkNL
1597: skip_tkspace(true) # Skip blanks and newlines
1598: tk = get_tk
1599: if tk.kind_of?(TkCOMMENT)
1600: if non_comment_seen
1601: comment = ''
1602: non_comment_seen = false
1603: end
1604: while tk.kind_of?(TkCOMMENT)
1605: comment << tk.text << "\n"
1606: tk = get_tk # this is the newline
1607: skip_tkspace(false) # leading spaces
1608: tk = get_tk
1609: end
1610: unless comment.empty?
1611: look_for_directives_in(container, comment)
1612: if container.done_documenting
1613: container.ongoing_visibility = save_visibility
1614: # return
1615: end
1616: end
1617: keep_comment = true
1618: else
1619: non_comment_seen = true
1620: end
1621: unget_tk(tk)
1622: keep_comment = true
1623:
1624:
1625: when TkCLASS
1626: if container.document_children
1627: parse_class(container, single, tk, comment)
1628: else
1629: nest += 1
1630: end
1631:
1632: when TkMODULE
1633: if container.document_children
1634: parse_module(container, single, tk, comment)
1635: else
1636: nest += 1
1637: end
1638:
1639: when TkDEF
1640: if container.document_self
1641: parse_method(container, single, tk, comment)
1642: else
1643: nest += 1
1644: end
1645:
1646: when TkCONSTANT
1647: if container.document_self
1648: parse_constant(container, single, tk, comment)
1649: end
1650:
1651: when TkALIAS
1652: if container.document_self
1653: parse_alias(container, single, tk, comment)
1654: end
1655:
1656: when TkYIELD
1657: if current_method.nil?
1658: warn("Warning: yield outside of method") if container.document_self
1659: else
1660: parse_yield(container, single, tk, current_method)
1661: end
1662:
1663: # Until and While can have a 'do', which shouldn't increas
1664: # the nesting. We can't solve the general case, but we can
1665: # handle most occurrences by ignoring a do at the end of a line
1666:
1667: when TkUNTIL, TkWHILE
1668: nest += 1
1669: puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
1670: "line #{tk.line_no}" if $DEBUG
1671: skip_optional_do_after_expression
1672:
1673: # 'for' is trickier
1674: when TkFOR
1675: nest += 1
1676: puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
1677: "line #{tk.line_no}" if $DEBUG
1678: skip_for_variable
1679: skip_optional_do_after_expression
1680:
1681: when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN
1682: nest += 1
1683: puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
1684: "line #{tk.line_no}" if $DEBUG
1685:
1686: when TkIDENTIFIER
1687: if nest == 1 and current_method.nil?
1688: case tk.name
1689: when "private", "protected", "public",
1690: "private_class_method", "public_class_method"
1691: parse_visibility(container, single, tk)
1692: keep_comment = true
1693: when "attr"
1694: parse_attr(container, single, tk, comment)
1695: when /^attr_(reader|writer|accessor)$/, @options.extra_accessors
1696: parse_attr_accessor(container, single, tk, comment)
1697: when "alias_method"
1698: if container.document_self
1699: parse_alias(container, single, tk, comment)
1700: end
1701: end
1702: end
1703:
1704: case tk.name
1705: when "require"
1706: parse_require(container, comment)
1707: when "include"
1708: parse_include(container, comment)
1709: end
1710:
1711:
1712: when TkEND
1713: nest -= 1
1714: puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG
1715: puts "Method = #{current_method.name}" if $DEBUG and current_method
1716: if nest == 0
1717: read_documentation_modifiers(container, CLASS_MODIFIERS)
1718: container.ongoing_visibility = save_visibility
1719: return
1720: end
1721:
1722: end
1723:
1724: comment = '' unless keep_comment
1725: begin
1726: get_tkread
1727: skip_tkspace(false)
1728: end while peek_tk == TkNL
1729:
1730: end
1731: end
# File lib/rdoc/parsers/parse_rb.rb, line 2546
2546: def parse_symbol_arg(no = nil)
2547:
2548: args = []
2549: skip_tkspace_comment
2550: case tk = get_tk
2551: when TkLPAREN
2552: loop do
2553: skip_tkspace_comment
2554: if tk1 = parse_symbol_in_arg
2555: args.push tk1
2556: break if no and args.size >= no
2557: end
2558:
2559: skip_tkspace_comment
2560: case tk2 = get_tk
2561: when TkRPAREN
2562: break
2563: when TkCOMMA
2564: else
2565: warn("unexpected token: '#{tk2.inspect}'") if $DEBUG
2566: break
2567: end
2568: end
2569: else
2570: unget_tk tk
2571: if tk = parse_symbol_in_arg
2572: args.push tk
2573: return args if no and args.size >= no
2574: end
2575:
2576: loop do
2577: # skip_tkspace_comment(false)
2578: skip_tkspace(false)
2579:
2580: tk1 = get_tk
2581: unless tk1.kind_of?(TkCOMMA)
2582: unget_tk tk1
2583: break
2584: end
2585:
2586: skip_tkspace_comment
2587: if tk = parse_symbol_in_arg
2588: args.push tk
2589: break if no and args.size >= no
2590: end
2591: end
2592: end
2593: args
2594: end
# File lib/rdoc/parsers/parse_rb.rb, line 2596
2596: def parse_symbol_in_arg
2597: case tk = get_tk
2598: when TkSYMBOL
2599: tk.text.sub(/^:/, '')
2600: when TkSTRING
2601: eval @read[-1]
2602: else
2603: warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG
2604: nil
2605: end
2606: end
# File lib/rdoc/parsers/parse_rb.rb, line 1570
1570: def parse_toplevel_statements(container)
1571: comment = collect_first_comment
1572: look_for_directives_in(container, comment)
1573: container.comment = comment unless comment.empty?
1574: parse_statements(container, NORMAL, nil, comment)
1575: end
# File lib/rdoc/parsers/parse_rb.rb, line 2482
2482: def parse_visibility(container, single, tk)
2483: singleton = (single == SINGLE)
2484: vis = case tk.name
2485: when "private" then :private
2486: when "protected" then :protected
2487: when "public" then :public
2488: when "private_class_method"
2489: singleton = true
2490: :private
2491: when "public_class_method"
2492: singleton = true
2493: :public
2494: else raise "Invalid visibility: #{tk.name}"
2495: end
2496:
2497: skip_tkspace_comment(false)
2498: case peek_tk
2499: # Ryan Davis suggested the extension to ignore modifiers, because he
2500: # often writes
2501: #
2502: # protected unless $TESTING
2503: #
2504: when TkNL, TkUNLESS_MOD, TkIF_MOD
2505: # error("Missing argument") if singleton
2506: container.ongoing_visibility = vis
2507: else
2508: args = parse_symbol_arg
2509: container.set_visibility_for(args, vis, singleton)
2510: end
2511: end
# File lib/rdoc/parsers/parse_rb.rb, line 2399
2399: def parse_yield(context, single, tk, method)
2400: if method.block_params.nil?
2401: get_tkread
2402: @scanner.instance_eval{@continue = false}
2403: method.block_params = parse_yield_parameters
2404: end
2405: end
# File lib/rdoc/parsers/parse_rb.rb, line 2395
2395: def parse_yield_parameters
2396: parse_method_or_yield_parameters
2397: end
# File lib/rdoc/parsers/parse_rb.rb, line 1501
1501: def peek_tk
1502: unget_tk(tk = get_tk)
1503: tk
1504: end
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2254
2254: def read_directive(allowed)
2255: tk = get_tk
2256: puts "directive: #{tk.inspect}" if $DEBUG
2257: result = nil
2258: if tk.kind_of?(TkCOMMENT)
2259: if tk.text =~ /\s*:?(\w+):\s*(.*)/
2260: directive = $1.downcase
2261: if allowed.include?(directive)
2262: result = [directive, $2]
2263: end
2264: end
2265: else
2266: unget_tk(tk)
2267: end
2268: result
2269: end
# File lib/rdoc/parsers/parse_rb.rb, line 2272
2272: def read_documentation_modifiers(context, allow)
2273: dir = read_directive(allow)
2274:
2275: case dir[0]
2276:
2277: when "notnew", "not_new", "not-new"
2278: context.dont_rename_initialize = true
2279:
2280: when "nodoc"
2281: context.document_self = false
2282: if dir[1].downcase == "all"
2283: context.document_children = false
2284: end
2285:
2286: when "doc"
2287: context.document_self = true
2288: context.force_documentation = true
2289:
2290: when "yield", "yields"
2291: unless context.params.nil?
2292: context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
2293: end
2294: context.block_params = dir[1]
2295:
2296: when "arg", "args"
2297: context.params = dir[1]
2298: end if dir
2299: end
# File lib/rdoc/parsers/parse_rb.rb, line 2352
2352: def remove_private_comments(comment)
2353: comment.gsub!(/^#--.*?^#\+\+/m, '')
2354: comment.sub!(/^#--.*/m, '')
2355: end
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2079
2079: def skip_for_variable
2080: skip_tkspace(false)
2081: tk = get_tk
2082: skip_tkspace(false)
2083: tk = get_tk
2084: unget_tk(tk) unless tk.kind_of?(TkIN)
2085: end
# File lib/rdoc/parsers/parse_rb.rb, line 2003
2003: def skip_method(container)
2004: meth = AnyMethod.new("", "anon")
2005: parse_method_parameters(meth)
2006: parse_statements(container, false, meth)
2007: end
while, until, and for have an optional
# File lib/rdoc/parsers/parse_rb.rb, line 2088
2088: def skip_optional_do_after_expression
2089: skip_tkspace(false)
2090: tk = get_tk
2091: case tk
2092: when TkLPAREN, TkfLPAREN
2093: end_token = TkRPAREN
2094: else
2095: end_token = TkNL
2096: end
2097:
2098: nest = 0
2099: @scanner.instance_eval{@continue = false}
2100:
2101: loop do
2102: puts("\nWhile: #{tk}, #{@scanner.continue} " +
2103: "#{@scanner.lex_state} #{nest}") if $DEBUG
2104: case tk
2105: when TkSEMICOLON
2106: break
2107: when TkLPAREN, TkfLPAREN
2108: nest += 1
2109: when TkDO
2110: break if nest.zero?
2111: when end_token
2112: if end_token == TkRPAREN
2113: nest -= 1
2114: break if @scanner.lex_state == EXPR_END and nest.zero?
2115: else
2116: break unless @scanner.continue
2117: end
2118: end
2119: tk = get_tk
2120: end
2121: skip_tkspace(false)
2122: if peek_tk.kind_of? TkDO
2123: get_tk
2124: end
2125: end
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2538
2538: def skip_tkspace_comment(skip_nl = true)
2539: loop do
2540: skip_tkspace(skip_nl)
2541: return unless peek_tk.kind_of? TkCOMMENT
2542: get_tk
2543: end
2544: end