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