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
# 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
# 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.
# 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
# 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
# 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 |
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# File parsers/parse_rb.rb, line 2375 2375: def parse_yield_parameters 2376: parse_method_or_yield_parameters 2377: end
# File parsers/parse_rb.rb, line 1494 1494: def peek_tk 1495: unget_tk(tk = get_tk) 1496: tk 1497: end
# 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
# 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
# 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
# File parsers/parse_rb.rb, line 2334 2334: def remove_private_comments(comment) 2335: comment.gsub!(/^#--.*?^#\+\+/m, '') 2336: comment.sub!(/^#--.*/m, '') 2337: end
# File parsers/parse_rb.rb, line 1450 1450: def remove_token_listener(obj) 1451: @token_listeners.delete(obj) 1452: end
# 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
# 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
# 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
# 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
# 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
# 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