| 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