Class | RDoc::RubyParser |
In: |
parsers/parse_rb.rb
|
Parent: | Object |
NORMAL | = | "::" |
SINGLE | = | "<<" |
# File 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 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 parsers/parse_rb.rb, line 1546 1546: def collect_first_comment 1547: skip_tkspace 1548: res = '' 1549: first_line = true 1550: 1551: tk = get_tk 1552: while tk.kind_of?(TkCOMMENT) 1553: if first_line && tk.text[0,2] == "#!" 1554: skip_tkspace 1555: tk = get_tk 1556: else 1557: res << tk.text << "\n" 1558: tk = get_tk 1559: if tk.kind_of? TkNL 1560: skip_tkspace(false) 1561: tk = get_tk 1562: end 1563: end 1564: first_line = false 1565: end 1566: unget_tk(tk) 1567: res 1568: end
# File 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 parsers/parse_rb.rb, line 2446 2446: def get_bool 2447: skip_tkspace 2448: tk = get_tk 2449: case tk 2450: when TkTRUE 2451: true 2452: when TkFALSE, TkNIL 2453: false 2454: else 2455: unget_tk tk 2456: true 2457: end 2458: 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 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 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 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 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 parsers/parse_rb.rb, line 2360 2360: def get_symbol_or_name 2361: tk = get_tk 2362: case tk 2363: when TkSYMBOL 2364: tk.text.sub(/^:/, '') 2365: when TkId, TkOp 2366: tk.name 2367: when TkSTRING 2368: tk.text 2369: else 2370: raise "Name or symbol expected (got #{tk})" 2371: end 2372: end
# File 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) || tk1.kind_of?(TkSTRING) 1481: if tk1.respond_to?(:name) 1482: tk = Token(TkSYMBOL).set_text(":" + tk1.name) 1483: else 1484: tk = Token(TkSYMBOL).set_text(":" + tk1.text) 1485: end 1486: # remove the identifier we just read (we're about to 1487: # replace it with a symbol) 1488: @token_listeners.each do |obj| 1489: obj.pop_token 1490: end if @token_listeners 1491: else 1492: warn("':' not followed by identifier or operator") 1493: tk = tk1 1494: end 1495: end 1496: 1497: # inform any listeners of our shiny new token 1498: @token_listeners.each do |obj| 1499: obj.add_token(tk) 1500: end if @token_listeners 1501: 1502: tk 1503: end
# File parsers/parse_rb.rb, line 1530 1530: def get_tkread 1531: read = @read.join("") 1532: @read = [] 1533: read 1534: 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 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.clear # ruby 1.9 feature 2342: comment.replace("") # 1.8 doesn't support #clear 2343: break 2344: else 2345: warn "Unrecognized directive '#{directive}'" 2346: break 2347: end 2348: end 2349: 2350: remove_private_comments(comment) 2351: end
# File 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 parsers/parse_rb.rb, line 2374 2374: def parse_alias(context, single, tk, comment) 2375: skip_tkspace 2376: if (peek_tk.kind_of? TkLPAREN) 2377: get_tk 2378: skip_tkspace 2379: end 2380: new_name = get_symbol_or_name 2381: @scanner.instance_eval{@lex_state = EXPR_FNAME} 2382: skip_tkspace 2383: if (peek_tk.kind_of? TkCOMMA) 2384: get_tk 2385: skip_tkspace 2386: end 2387: old_name = get_symbol_or_name 2388: 2389: al = Alias.new(get_tkread, old_name, new_name, comment) 2390: read_documentation_modifiers(al, ATTR_MODIFIERS) 2391: if al.document_self 2392: context.add_alias(al) 2393: end 2394: end
# File parsers/parse_rb.rb, line 2460 2460: def parse_attr(context, single, tk, comment) 2461: args = parse_symbol_arg(1) 2462: if args.size > 0 2463: name = args[0] 2464: rw = "R" 2465: skip_tkspace(false) 2466: tk = get_tk 2467: if tk.kind_of? TkCOMMA 2468: rw = "RW" if get_bool 2469: else 2470: unget_tk tk 2471: end 2472: att = Attr.new(get_tkread, name, rw, comment) 2473: read_documentation_modifiers(att, ATTR_MODIFIERS) 2474: if att.document_self 2475: context.add_attribute(att) 2476: end 2477: else 2478: warn("'attr' ignored - looks like a variable") 2479: end 2480: 2481: end
# File parsers/parse_rb.rb, line 2514 2514: def parse_attr_accessor(context, single, tk, comment) 2515: args = parse_symbol_arg 2516: read = get_tkread 2517: rw = "?" 2518: 2519: # If nodoc is given, don't document any of them 2520: 2521: tmp = CodeObject.new 2522: read_documentation_modifiers(tmp, ATTR_MODIFIERS) 2523: return unless tmp.document_self 2524: 2525: case tk.name 2526: when "attr_reader" then rw = "R" 2527: when "attr_writer" then rw = "W" 2528: when "attr_accessor" then rw = "RW" 2529: else 2530: rw = @options.extra_accessor_flags[tk.name] 2531: end 2532: 2533: for name in args 2534: att = Attr.new(get_tkread, name, rw, comment) 2535: context.add_attribute(att) 2536: end 2537: end
# File 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 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 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 parsers/parse_rb.rb, line 2434 2434: def parse_include(context, comment) 2435: loop do 2436: skip_tkspace_comment 2437: name = get_constant_with_optional_parens 2438: unless name.empty? 2439: context.add_include(Include.new(name, comment)) 2440: end 2441: return unless peek_tk.kind_of?(TkCOMMA) 2442: get_tk 2443: end 2444: end
# File 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 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 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 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 parsers/parse_rb.rb, line 2408 2408: def parse_require(context, comment) 2409: skip_tkspace_comment 2410: tk = get_tk 2411: if tk.kind_of? TkLPAREN 2412: skip_tkspace_comment 2413: tk = get_tk 2414: end 2415: 2416: name = nil 2417: case tk 2418: when TkSTRING 2419: name = tk.text 2420: # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR 2421: # name = tk.name 2422: when TkDSTRING 2423: warn "Skipping require of dynamic string: #{tk.text}" 2424: # else 2425: # warn "'require' used as variable" 2426: end 2427: if name 2428: context.add_require(Require.new(name, comment)) 2429: else 2430: unget_tk(tk) 2431: end 2432: end
# File 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 parsers/parse_rb.rb, line 2547 2547: def parse_symbol_arg(no = nil) 2548: 2549: args = [] 2550: skip_tkspace_comment 2551: case tk = get_tk 2552: when TkLPAREN 2553: loop do 2554: skip_tkspace_comment 2555: if tk1 = parse_symbol_in_arg 2556: args.push tk1 2557: break if no and args.size >= no 2558: end 2559: 2560: skip_tkspace_comment 2561: case tk2 = get_tk 2562: when TkRPAREN 2563: break 2564: when TkCOMMA 2565: else 2566: warn("unexpected token: '#{tk2.inspect}'") if $DEBUG 2567: break 2568: end 2569: end 2570: else 2571: unget_tk tk 2572: if tk = parse_symbol_in_arg 2573: args.push tk 2574: return args if no and args.size >= no 2575: end 2576: 2577: loop do 2578: # skip_tkspace_comment(false) 2579: skip_tkspace(false) 2580: 2581: tk1 = get_tk 2582: unless tk1.kind_of?(TkCOMMA) 2583: unget_tk tk1 2584: break 2585: end 2586: 2587: skip_tkspace_comment 2588: if tk = parse_symbol_in_arg 2589: args.push tk 2590: break if no and args.size >= no 2591: end 2592: end 2593: end 2594: args 2595: end
# File parsers/parse_rb.rb, line 2597 2597: def parse_symbol_in_arg 2598: case tk = get_tk 2599: when TkSYMBOL 2600: tk.text.sub(/^:/, '') 2601: when TkSTRING 2602: eval @read[-1] 2603: else 2604: warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG 2605: nil 2606: end 2607: end
# File 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 parsers/parse_rb.rb, line 2483 2483: def parse_visibility(container, single, tk) 2484: singleton = (single == SINGLE) 2485: vis = case tk.name 2486: when "private" then :private 2487: when "protected" then :protected 2488: when "public" then :public 2489: when "private_class_method" 2490: singleton = true 2491: :private 2492: when "public_class_method" 2493: singleton = true 2494: :public 2495: else raise "Invalid visibility: #{tk.name}" 2496: end 2497: 2498: skip_tkspace_comment(false) 2499: case peek_tk 2500: # Ryan Davis suggested the extension to ignore modifiers, because he 2501: # often writes 2502: # 2503: # protected unless $TESTING 2504: # 2505: when TkNL, TkUNLESS_MOD, TkIF_MOD 2506: # error("Missing argument") if singleton 2507: container.ongoing_visibility = vis 2508: else 2509: args = parse_symbol_arg 2510: container.set_visibility_for(args, vis, singleton) 2511: end 2512: end
# File parsers/parse_rb.rb, line 2400 2400: def parse_yield(context, single, tk, method) 2401: if method.block_params.nil? 2402: get_tkread 2403: @scanner.instance_eval{@continue = false} 2404: method.block_params = parse_yield_parameters 2405: end 2406: end
# File parsers/parse_rb.rb, line 2396 2396: def parse_yield_parameters 2397: parse_method_or_yield_parameters 2398: end
# File parsers/parse_rb.rb, line 1505 1505: def peek_tk 1506: unget_tk(tk = get_tk) 1507: tk 1508: end
# File 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 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 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 parsers/parse_rb.rb, line 2353 2353: def remove_private_comments(comment) 2354: comment.gsub!(/^#--.*?^#\+\+/m, '') 2355: comment.sub!(/^#--.*/m, '') 2356: end
# File parsers/parse_rb.rb, line 1461 1461: def remove_token_listener(obj) 1462: @token_listeners.delete(obj) 1463: end
# File 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
skip the var [in] part of a ‘for’ statement
# File 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 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 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 parsers/parse_rb.rb, line 1520 1520: def skip_tkspace(skip_nl = true) 1521: tokens = [] 1522: while ((tk = get_tk).kind_of?(TkSPACE) || 1523: (skip_nl && tk.kind_of?(TkNL))) 1524: tokens.push tk 1525: end 1526: unget_tk(tk) 1527: tokens 1528: end
# File parsers/parse_rb.rb, line 2539 2539: def skip_tkspace_comment(skip_nl = true) 2540: loop do 2541: skip_tkspace(skip_nl) 2542: return unless peek_tk.kind_of? TkCOMMENT 2543: get_tk 2544: end 2545: end