| 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 && 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
# 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 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 |
# 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
# 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
# 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
# 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
# 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
# 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 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
# 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 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# File lib/rdoc/parsers/parse_rb.rb, line 2391
2391: def parse_yield_parameters
2392: parse_method_or_yield_parameters
2393: 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 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
# 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
# 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
# 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 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
# 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
# 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
# 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 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