Class RDoc::Fortran95parser
In: parsers/parse_f95.rb
Parent: Object

See rdoc/parsers/parse_f95.rb

Methods

Public Class methods

prepare to parse a Fortran 95 file

[Source]

     # File parsers/parse_f95.rb, line 912
912:     def initialize(top_level, file_name, body, options, stats)
913:       @body = body
914:       @stats = stats
915:       @file_name  = file_name
916:       @options = options
917:       @top_level = top_level
918:       @progress = $stderr unless options.quiet
919: 
920:       begin
921:         @options_ignore_case = options.ignore_case
922:       rescue
923:         @options_ignore_case = true
924:       end
925: 
926:     end

Public Instance methods

Return lines before "contains" statement in modules. "interface", "type" statements are removed.

[Source]

      # File parsers/parse_f95.rb, line 1865
1865:     def before_contains(code)
1866:       level_depth = 0
1867:       before_contains_lines = []
1868:       before_contains_code = nil
1869:       before_contains_flag = nil
1870:       code.split("\n").each{ |line|
1871:         if !before_contains_flag
1872:           if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
1873:             before_contains_flag = true
1874:           end
1875:         else
1876:           break if line =~ /^\s*?contains\s*?(!.*?)?$/i
1877:           level_depth += 1 if block_start?(line)
1878:           level_depth -= 1 if block_end?(line)
1879:           break if level_depth < 0
1880:           before_contains_lines << line
1881:         end
1882: 
1883:       }
1884:       before_contains_code = before_contains_lines.join("\n")
1885:       if before_contains_code
1886:         before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
1887:         before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
1888:       end
1889: 
1890:       before_contains_code
1891:     end

Which "line" is end of block (module, program, block data, subroutine, function) statement ?

[Source]

      # File parsers/parse_f95.rb, line 2533
2533:     def block_end?(line)
2534:       return nil if !line
2535: 
2536:       if line =~ /^\s*?end\s*?(!.*?)?$/i                 ||
2537:           line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i       ||
2538:           line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i      ||
2539:           line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i  ||
2540:           line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i   ||
2541:           line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
2542:         return true
2543:       end
2544: 
2545:       return nil
2546:     end

Which "line" is start of block (module, program, block data, subroutine, function) statement ?

[Source]

      # File parsers/parse_f95.rb, line 2497
2497:     def block_start?(line)
2498:       return nil if !line
2499: 
2500:       if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i    ||
2501:           line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i  ||
2502:           line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i     ||
2503:           line =~ \
2504:                   /^\s*?
2505:                    (recursive|pure|elemental)?\s*?
2506:                    subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
2507:                   /ix ||
2508:           line =~ \
2509:                   /^\s*?
2510:                    (recursive|pure|elemental)?\s*?
2511:                    (
2512:                        character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
2513:                      | type\s*?\([\w\s]+?\)\s+
2514:                      | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
2515:                      | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
2516:                      | double\s+precision\s+
2517:                      | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
2518:                      | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
2519:                    )?
2520:                    function\s+(\w+)\s*?
2521:                    (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
2522:                   /ix
2523:         return true
2524:       end
2525: 
2526:       return nil
2527:     end

Check external aliases

subname というサブルーチン名, または関数名を持つファイルを 探査し, 存在する場合にはそのファイル内へメソッドを追加する.

[Source]

      # File parsers/parse_f95.rb, line 2252
2252:     def check_external_aliases(subname, params, comment, test=nil)
2253:       @@external_aliases.each{ |alias_item|
2254:         if subname == alias_item["old_name"] ||
2255:                     subname.upcase == alias_item["old_name"].upcase &&
2256:                             @options_ignore_case
2257: 
2258:           new_meth = initialize_external_method(alias_item["new_name"], 
2259:                                                 subname, params, @file_name, 
2260:                                                 comment)
2261:           new_meth.visibility = alias_item["visibility"]
2262:           new_meth.set_priority(alias_item["doc_priority"]) if alias_item["doc_priority"]
2263: 
2264:           progress "e"
2265:           @stats.num_methods += 1
2266:           alias_item["file_or_module"].add_method(new_meth)
2267: 
2268:           if !alias_item["file_or_module"].include_requires?(@file_name, @options_ignore_case)
2269:             alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
2270:           end
2271:         end
2272:       }
2273:     end

Check public_methods

use したモジュールからそのまま引き継いで public として 公開する場合のチェックを行う. subname というサブルーチン名, または関数名を持つファイルを 探査し, 存在する場合にはそのファイル内へメソッドを追加する.

[Source]

      # File parsers/parse_f95.rb, line 2283
2283:     def check_public_methods(method, parent)
2284:       return if !method || !parent
2285:       @@public_methods.each{ |alias_item|
2286:         parent_is_used_module = nil
2287:         alias_item["used_modules"].each{ |used_module|
2288:           if used_module == parent ||
2289:               used_module.upcase == parent.upcase &&
2290:               @options_ignore_case
2291:             parent_is_used_module = true
2292:           end
2293:         }
2294:         next if !parent_is_used_module
2295: 
2296:         if method.name == alias_item["name"] ||
2297:             method.name.upcase == alias_item["name"].upcase &&
2298:             @options_ignore_case
2299: 
2300:           new_meth = initialize_public_method(method, parent)
2301:           if alias_item["local_name"]
2302:             new_meth.name = alias_item["local_name"]
2303:           end
2304: 
2305:           progress "e"
2306:           @stats.num_methods += 1
2307:           alias_item["file_or_module"].add_method new_meth
2308:         end
2309:       }
2310:     end

Collect comment for file entity

[Source]

      # File parsers/parse_f95.rb, line 1896
1896:     def collect_first_comment(body)
1897:       comment = ""
1898:       not_comment = ""
1899:       comment_start = false
1900:       comment_end   = false
1901:       body.split("\n").each{ |line|
1902:         if comment_end
1903:           not_comment << line
1904:           not_comment << "\n"
1905:         elsif /^\s*?!\s?(.*)$/i =~ line
1906:           comment_start = true
1907:           comment << $1
1908:           comment << "\n"
1909:         elsif /^\s*?$/i =~ line
1910:           comment_end = true if comment_start && COMMENTS_ARE_UPPER
1911:         else
1912:           comment_end = true
1913:           not_comment << line
1914:           not_comment << "\n"
1915:         end
1916:       }
1917:       return comment, not_comment
1918:     end

Comment out checker

[Source]

      # File parsers/parse_f95.rb, line 2424
2424:     def comment_out?(line)
2425:       return nil unless line
2426:       commentout = false
2427:       squote = false ; dquote = false
2428:       line.split("").each { |char|
2429:         if !(squote) && !(dquote)
2430:           case char
2431:           when "!" ; commentout = true ; break
2432:           when "\""; dquote = true
2433:           when "\'"; squote = true
2434:           else next
2435:           end
2436:         elsif squote
2437:           case char
2438:           when "\'"; squote = false
2439:           else next
2440:           end
2441:         elsif dquote
2442:           case char
2443:           when "\""; dquote = false
2444:           else next
2445:           end
2446:         end
2447:       }
2448:       return commentout
2449:     end

Continuous line checker

[Source]

      # File parsers/parse_f95.rb, line 2410
2410:     def continuous_line?(line)
2411:       continuous = false
2412:       if /&\s*?(!.*)?$/ =~ line
2413:         continuous = true
2414:         if comment_out?($~.pre_match)
2415:           continuous = false
2416:         end
2417:       end
2418:       return continuous
2419:     end

Parse string argument "text", and Return Array of Fortran95Definition object

[Source]

      # File parsers/parse_f95.rb, line 2675
2675:     def definition_info(text)
2676:       return nil unless text
2677:       lines = "#{text}"
2678:       defs = Array.new
2679:       comment = ""
2680:       trailing_comment = ""
2681:       under_comment_valid = false
2682:       lines.split("\n").each{ |line|
2683:         if /^\s*?!\s?(.*)/ =~ line
2684:           if COMMENTS_ARE_UPPER
2685:             comment << remove_header_marker($1)
2686:             comment << "\n"
2687:           elsif defs[-1] && under_comment_valid
2688:             defs[-1].comment << "\n"
2689:             defs[-1].comment << remove_header_marker($1)
2690:           end
2691:           next
2692:         elsif /^\s*?$/ =~ line
2693:           comment = ""
2694:           under_comment_valid = false
2695:           next
2696:         end
2697:         type = ""
2698:         characters = ""
2699:         if line =~ /^\s*?
2700:                     (
2701:                         character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
2702:                       | type\s*?\([\w\s]+?\)[\s\,]*
2703:                       | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
2704:                       | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
2705:                       | double\s+precision[\s\,]*
2706:                       | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
2707:                       | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
2708:                     )
2709:                     (.*?::)?
2710:                     (.+)$
2711:                    /ix
2712:           characters = $8
2713:           type = $1
2714:           type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
2715:         else
2716:           under_comment_valid = false
2717:           next
2718:         end
2719:         squote = false ; dquote = false ; bracket = 0
2720:         iniflag = false; commentflag = false
2721:         varname = "" ; arraysuffix = "" ; inivalue = ""
2722:         start_pos = defs.size
2723:         characters.split("").each { |char|
2724:           if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
2725:             case char
2726:             when "!" ; commentflag = true
2727:             when "(" ; bracket += 1       ; arraysuffix = char
2728:             when "\""; dquote = true
2729:             when "\'"; squote = true
2730:             when "=" ; iniflag = true     ; inivalue << char
2731:             when ","
2732:               defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
2733:               varname = "" ; arraysuffix = "" ; inivalue = ""
2734:               under_comment_valid = true
2735:             when " " ; next
2736:             else     ; varname << char
2737:             end
2738:           elsif commentflag
2739:             comment << remove_header_marker(char)
2740:             trailing_comment << remove_header_marker(char)
2741:           elsif iniflag
2742:             if dquote
2743:               case char
2744:               when "\"" ; dquote = false ; inivalue << char
2745:               else      ; inivalue << char
2746:               end
2747:             elsif squote
2748:               case char
2749:               when "\'" ; squote = false ; inivalue << char
2750:               else      ; inivalue << char
2751:               end
2752:             elsif bracket > 0
2753:               case char
2754:               when "(" ; bracket += 1 ; inivalue << char
2755:               when ")" ; bracket -= 1 ; inivalue << char
2756:               else     ; inivalue << char
2757:               end
2758:             else
2759:               case char
2760:               when ","
2761:                 defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
2762:                 varname = "" ; arraysuffix = "" ; inivalue = ""
2763:                 iniflag = false
2764:                 under_comment_valid = true
2765:               when "(" ; bracket += 1 ; inivalue << char
2766:               when "\""; dquote = true  ; inivalue << char
2767:               when "\'"; squote = true  ; inivalue << char
2768:               when "!" ; commentflag = true
2769:               else     ; inivalue << char
2770:               end
2771:             end
2772:           elsif !(squote) && !(dquote) && bracket > 0
2773:             case char
2774:             when "(" ; bracket += 1 ; arraysuffix << char
2775:             when ")" ; bracket -= 1 ; arraysuffix << char
2776:             else     ; arraysuffix << char
2777:             end
2778:           elsif squote
2779:             case char
2780:             when "\'"; squote = false ; inivalue << char
2781:             else     ; inivalue << char
2782:             end
2783:           elsif dquote
2784:             case char
2785:             when "\""; dquote = false ; inivalue << char
2786:             else     ; inivalue << char
2787:             end
2788:           end
2789:         }
2790:         defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
2791:         if trailing_comment =~ IGNORED_MARKER_REGEXP
2792:           defs[start_pos..-1].collect!{ |defitem|
2793:             defitem.nodoc = true
2794:           }
2795:         end
2796:         if trailing_comment =~ DOC_PRIORITY_REGEXP
2797:           doc_priority = $1.to_i
2798:           defs[start_pos..-1].collect!{ |defitem|
2799:             defitem.doc_priority = doc_priority
2800:             defitem.comment.sub!(DOC_PRIORITY_REGEXP, '')
2801:           }
2802:         end
2803:         varname = "" ; arraysuffix = "" ; inivalue = ""
2804:         comment = ""
2805:         under_comment_valid = true
2806:         trailing_comment = ""
2807:       }
2808:       return defs
2809:     end

[Source]

      # File parsers/parse_f95.rb, line 1845
1845:     def doc_priority_from_trailing(trailing)
1846:       prefix = ''
1847:       if trailing =~ /^(\s*!)(.*)$/
1848:         prefix = $1
1849:         trailing = $2
1850:       end
1851:       if trailing =~ DOC_PRIORITY_REGEXP
1852:         priority = $1.to_i
1853:         trailing.sub!(DOC_PRIORITY_REGEXP, '')
1854:       else
1855:         priority = false
1856:       end
1857:       trailing = prefix + trailing
1858:       return priority, trailing
1859:     end

Return comments of definitions of arguments

If "all" argument is true, information of all arguments are returned. If "modified_params" is true, list of arguments are decorated, for exameple, optional arguments are parenthetic as "[arg]".

[Source]

      # File parsers/parse_f95.rb, line 1927
1927:     def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
1928:       return unless args || all
1929:       indent = "" unless indent
1930:       args = ["all"] if all
1931:       params = "" if modified_params
1932:       comma = ""
1933:       return unless text
1934:       args_rdocforms = "\n"
1935:       remaining_lines = "#{text}"
1936:       definitions = definition_info(remaining_lines)
1937:       args.each{ |arg|
1938:         arg.strip!
1939:         arg.chomp!
1940:         definitions.each { |defitem|
1941:           if arg == defitem.varname.strip.chomp || all
1942:             args_rdocforms << "\n\#{indent}<b><tt>\#{defitem.varname.chomp.strip}\#{defitem.arraysuffix}  </tt></b>  <tt> \#{defitem.inivalue}</tt> ::\n\#{indent}   <tt>\#{defitem.types.chomp.strip}</tt>\n"
1943:             if !defitem.comment.chomp.strip.empty?
1944:               comment = ""
1945:               defitem.comment.split("\n").each{ |line|
1946:                 comment << "       " + line + "\n"
1947:               }
1948:               args_rdocforms << "\n\#{indent}   <tt></tt> ::\n\#{indent}       <tt></tt>\n\#{indent}       \#{comment.chomp.strip}\n"
1949:             end
1950: 
1951:             if modified_params
1952:               if defitem.include_attr?("optional")
1953:                 params << "#{comma}[#{arg}]"
1954:               else
1955:                 params << "#{comma}#{arg}"
1956:               end
1957:               comma = ", "
1958:             end
1959:           end
1960:         }
1961:       }
1962:       if modified_params
1963:         return args_rdocforms, params
1964:       else
1965:         return args_rdocforms
1966:       end
1967:     end

Comments just after module or subprogram, or arguments are returnd. If "COMMENTS_ARE_UPPER" is true, comments just before modules or subprograms are returnd

[Source]

      # File parsers/parse_f95.rb, line 2070
2070:     def find_comments text
2071:       return "" unless text
2072:       lines = text.split("\n")
2073:       lines.reverse! if COMMENTS_ARE_UPPER
2074:       comment_block = Array.new
2075:       lines.each do |line|
2076:         break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
2077:         if COMMENTS_ARE_UPPER
2078:           comment_block.unshift line.sub(/^\s*?!\s?/,"")
2079:         else
2080:           comment_block.push line.sub(/^\s*?!\s?/,"")
2081:         end
2082:       end
2083:       nice_lines = comment_block.join("\n").split "\n\s*?\n"
2084:       nice_lines[0] ||= ""
2085:       nice_lines.shift
2086:     end

Add namelist information to Repository (dummy module of each @top_level) of NAMELIST statements. And return comments about namelist group names

[Source]

      # File parsers/parse_f95.rb, line 1985
1985:     def find_namelists(container, text, before_contains=nil)
1986:       return nil if !text
1987:       top_level = find_toplevel(container)
1988: 
1989:       if text =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)(!.*?)?$/i
1990:         if top_level.include_includes?(NAMELIST_REPOSITORY_NAME)
1991:           namelist_module = 
1992:             top_level.find_module_named(NAMELIST_REPOSITORY_NAME)
1993:         else
1994:           namelist_module = 
1995:             top_level.add_module NormalClass, NAMELIST_REPOSITORY_NAME
1996:           namelist_module.record_location top_level
1997:           namelist_module.comment = "This is not a module but a repository of NAMELIST group names declared\nin all Fortran 90/95 files\n"
1998:         end
1999:       else
2000:         return ""
2001:       end
2002: 
2003:       nml_group_name_lists = []
2004:       lines = "#{text}"
2005:       before_contains = "" if !before_contains
2006:       while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)(!.*?)?$/i
2007:         lines = $~.post_match
2008:         pre_match = $~.pre_match ; post_match = $~.post_match
2009:         nml_group_name = $1
2010:         trailing_comment = $3 || ""
2011:         nml_vars_list  = $2.split(",")
2012:         nml_comment = COMMENTS_ARE_UPPER ? 
2013:             find_comments(pre_match.sub(/\n$/, '')) :
2014:             find_comments(trailing_comment + post_match)
2015:         if lines.split("\n")[0] =~ /^\//i
2016:           lines = "namelist " + lines
2017:         end
2018: 
2019:         nml_meth = AnyMethod.new("NAMELIST", nml_group_name)
2020:         nml_meth.singleton = false
2021:         nml_meth.params    = "( " + nml_vars_list.join(", ") + " )"
2022:         nml_meth.comment   = "<b><em> NAMELIST </em></b> :: <tt></tt>\n"
2023:         nml_meth.comment   << find_arguments(nml_vars_list, "#{text}" + "\n" + before_contains)
2024:         nml_meth.comment   << "\n" + nml_comment if nml_comment
2025:         if container.parent.parent
2026:           parent_object = container.parent.name
2027:         else
2028:           parent_object = container.parent.file_relative_name
2029:         end
2030:         nml_meth.comment   << "\n\nThis namelist group name is input/output in "
2031:         nml_meth.comment   << parent_object + "#" + container.name
2032: 
2033:         progress "n"
2034:         @stats.num_methods += 1
2035:         namelist_module.add_method nml_meth
2036: 
2037:         nml_group_name_lists << NAMELIST_REPOSITORY_NAME + "#" + nml_group_name
2038:       end
2039: 
2040:       if !nml_group_name_lists.empty?
2041:         comments_in_procedures = "\n\nThis procedure input/output "
2042:         comments_in_procedures << nml_group_name_lists.join(", ") + " . "
2043:       else
2044:         comments_in_procedures = ""
2045:       end
2046: 
2047:       comments_in_procedures
2048:     end

Return toplevel class of container

[Source]

      # File parsers/parse_f95.rb, line 2057
2057:     def find_toplevel(container)
2058:       top_level = container
2059:       while top_level.parent
2060:         top_level = top_level.parent
2061:       end
2062:       top_level
2063:     end

Find visibility

[Source]

      # File parsers/parse_f95.rb, line 2233
2233:     def find_visibility(container, subname, visibility_info)
2234:       return nil if !subname || !visibility_info
2235:       visibility_info.each{ |info|
2236:         if info["name"] == subname ||
2237:             @options_ignore_case && info["name"].upcase == subname.upcase
2238:           if info["parent"] == container.name
2239:             return info["visibility"]
2240:           end
2241:         end
2242:       }
2243:       return nil
2244:     end

Create method for external alias

If argument "internal" is true, file is ignored.

[Source]

      # File parsers/parse_f95.rb, line 2115
2115:     def initialize_external_method(new, old, params, file, comment, token=nil,
2116:                                    internal=nil, nolink=nil)
2117:       return nil unless new || old
2118: 
2119:       if internal
2120:         external_alias_header = "#{INTERNAL_ALIAS_MES} "
2121:         external_alias_text   = external_alias_header + old 
2122:       elsif file
2123:         external_alias_header = "#{EXTERNAL_ALIAS_MES} "
2124:         external_alias_text   = external_alias_header + file + "#" + old
2125:       else
2126:         return nil
2127:       end
2128:       external_meth = AnyMethod.new(external_alias_text, new)
2129:       external_meth.singleton    = false
2130:       external_meth.params       = params
2131:       external_comment = remove_trailing_alias(comment) + "\n\n" if comment
2132:       external_meth.comment = external_comment || ""
2133:       if nolink && token
2134:         external_meth.start_collecting_tokens
2135:         external_meth.add_token Token.new(1,1).set_text(token)
2136:       else
2137:         external_meth.comment << external_alias_text
2138:       end
2139: 
2140:       return external_meth
2141:     end

Create method for internal alias

[Source]

      # File parsers/parse_f95.rb, line 2098
2098:     def initialize_public_method(method, parent)
2099:       return if !method || !parent
2100: 
2101:       new_meth = AnyMethod.new("External Alias for module", method.name)
2102:       new_meth.singleton    = method.singleton
2103:       new_meth.params       = method.params.clone
2104:       new_meth.comment      = remove_trailing_alias(method.comment.clone)
2105:       new_meth.comment      << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
2106: 
2107:       return new_meth
2108:     end

[Source]

      # File parsers/parse_f95.rb, line 1156
1156:     def parse_program_or_module(container, code,
1157:                                 visibility=:public, external=nil)
1158:       return unless container
1159:       return unless code
1160:       remaining_lines = code.split("\n")
1161:       remaining_code = "#{code}"
1162: 
1163:       #
1164:       # Parse variables before "contains" in module
1165:       #
1166:       # namelist 変数の定義に使われたり, これ自体が定数, 変数
1167:       # 提供されるのに利用される. (変数や定数として利用される場合,
1168:       # これもメソッドとして提供する.
1169:       #
1170:       before_contains_code = before_contains(remaining_code)
1171: 
1172:       #
1173:       # Parse global "use"
1174:       #
1175:       use_check_code = "#{before_contains_code}"
1176:       cascaded_modules_list = []
1177:       while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
1178:         use_check_code = $~.pre_match
1179:         use_check_code << $~.post_match
1180:         used_mod_name = $1.strip.chomp
1181:         used_list = $2 || ""
1182:         used_trailing = $3 || ""
1183:         next if used_trailing.sub(/^!/, '') =~ IGNORED_MARKER_REGEXP
1184:         if !container.include_includes?(used_mod_name, @options_ignore_case)
1185:           progress "."
1186:           container.add_include Include.new(used_mod_name, "")
1187:         end
1188:         if ! (used_list =~ /\,\s*?only\s*?:/i )
1189:           cascaded_modules_list << "\#" + used_mod_name
1190:         end
1191:       end
1192: 
1193:       #
1194:       # Parse public and private, and store information.
1195:       # This information is used when "add_method" and
1196:       # "set_visibility_for" are called.
1197:       #
1198:       visibility_default, visibility_info = 
1199:                 parse_visibility(remaining_lines.join("\n"), visibility, container)
1200:       @@public_methods.concat visibility_info
1201:       if visibility_default == :public
1202:         if !cascaded_modules_list.empty?
1203:           cascaded_modules = 
1204:             Attr.new("Cascaded Modules",
1205:                      "Imported modules all of whose components are published again",
1206:                      "",
1207:                      cascaded_modules_list.join(", "))
1208:           container.add_attribute(cascaded_modules)
1209:         end
1210:       end
1211: 
1212:       #
1213:       # Check rename elements
1214:       #
1215:       use_check_code = "#{before_contains_code}"
1216:       while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
1217:         use_check_code = $~.pre_match
1218:         use_check_code << $~.post_match
1219:         used_mod_name = $1.strip.chomp
1220:         used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
1221:         used_elements.split(",").each{ |used|
1222:           if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
1223:             local = $1
1224:             org = $2
1225:             @@public_methods.collect!{ |pub_meth|
1226:               if local == pub_meth["name"] ||
1227:                   local.upcase == pub_meth["name"].upcase &&
1228:                   @options_ignore_case
1229:                 pub_meth["name"] = org
1230:                 pub_meth["local_name"] = local
1231:               end
1232:               pub_meth
1233:             }
1234:           end
1235:         }
1236:       end
1237: 
1238:       #
1239:       # Parse private "use"
1240:       #
1241:       use_check_code = remaining_lines.join("\n")
1242:       while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
1243:         use_check_code = $~.pre_match
1244:         use_check_code << $~.post_match
1245:         used_mod_name = $1.strip.chomp
1246:         used_trailing = $3 || ""
1247:         next if used_trailing.sub(/^!/, '') =~ IGNORED_MARKER_REGEXP
1248:         if !container.include_includes?(used_mod_name, @options_ignore_case)
1249:           progress "."
1250:           container.add_include Include.new(used_mod_name, "")
1251:         end
1252:       end
1253: 
1254:       container.each_includes{ |inc|
1255:         TopLevel.all_files.each do |name, toplevel|
1256:           indicated_mod = toplevel.find_symbol(inc.name,
1257:                                                nil, @options_ignore_case)
1258:           if indicated_mod
1259:             indicated_name = indicated_mod.parent.file_relative_name
1260:             if !container.include_requires?(indicated_name, @options_ignore_case)
1261:               container.add_require(Require.new(indicated_name, ""))
1262:             end
1263:             break
1264:           end
1265:         end
1266:       }
1267: 
1268:       #
1269:       # Parse derived types definitions
1270:       #
1271:       derived_types_comment = ""
1272:       remaining_code = remaining_lines.join("\n")
1273:       while remaining_code =~ /^\s*?
1274:                                     type[\s\,]+(public|private)?\s*?(::)?\s*?
1275:                                     (\w+)\s*?(!.*?)?$
1276:                                     (.*?)
1277:                                     ^\s*?end\s+type.*?$
1278:                               /imx
1279:         remaining_code = $~.pre_match
1280:         remaining_code << $~.post_match
1281:         typename = $3.chomp.strip
1282:         type_elements = $5 || ""
1283:         type_code = remove_empty_head_lines($&)
1284:         type_trailing = find_comments($4)
1285:         type_visibility = $1
1286:         pre_match = $~.pre_match
1287:         next if type_trailing =~ IGNORED_MARKER_REGEXP
1288:         type_priority, type_trailing = doc_priority_from_trailing(type_trailing)
1289:         type_comment = COMMENTS_ARE_UPPER ? 
1290:           find_comments(pre_match) + "\n" + type_trailing :
1291:             type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
1292:         type_element_visibility_public = true
1293:         type_code.split("\n").each{ |line|
1294:           if /^\s*?private\s*?$/ =~ line
1295:             type_element_visibility_public = nil
1296:             break
1297:           end
1298:         } if type_code
1299: 
1300:         args_comment = ""
1301:         type_args_info = nil
1302: 
1303:         if @options.show_all
1304:           args_comment = find_arguments(nil, type_code, true)
1305:         else
1306:           type_public_args_list = []
1307:           type_args_info = definition_info(type_code)
1308:           type_args_info.each{ |arg|
1309:             arg_is_public = type_element_visibility_public
1310:             arg_is_public = true if arg.include_attr?("public")
1311:             arg_is_public = nil if arg.include_attr?("private")
1312:             type_public_args_list << arg.varname if arg_is_public
1313:           }
1314:           args_comment = find_arguments(type_public_args_list, type_code)
1315:         end
1316: 
1317:         type = AnyMethod.new("type #{typename}", typename)
1318:         type.singleton = false
1319:         type.params = ""
1320:         type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
1321:         type.comment << args_comment if args_comment
1322:         type.comment << type_comment if type_comment
1323:         type.set_priority(type_priority) if type_priority
1324:         progress "t"
1325:         @stats.num_methods += 1
1326:         container.add_method type
1327: 
1328:         set_visibility(container, typename, visibility_default, @@public_methods)
1329: 
1330:         if type_visibility
1331:           type_visibility.gsub!(/\s/,'')
1332:           type_visibility.gsub!(/\,/,'')
1333:           type_visibility.gsub!(/:/,'')
1334:           type_visibility.downcase!
1335:           if type_visibility == "public"
1336:             container.set_visibility_for([typename], :public)
1337:           elsif type_visibility == "private"
1338:             container.set_visibility_for([typename], :private)
1339:           end
1340:         end
1341: 
1342:         check_public_methods(type, container.name)
1343: 
1344:         if @options.show_all
1345:           derived_types_comment << ", " unless derived_types_comment.empty?
1346:           derived_types_comment << typename
1347:         else
1348:           if type.visibility == :public
1349:           derived_types_comment << ", " unless derived_types_comment.empty?
1350:           derived_types_comment << typename
1351:           end
1352:         end
1353: 
1354:       end
1355: 
1356:       if !derived_types_comment.empty?
1357:         derived_types_table = 
1358:           Attr.new("Derived Types", "Derived_Types", "", 
1359:                    derived_types_comment)
1360:         container.add_attribute(derived_types_table)
1361:       end
1362: 
1363:       #
1364:       # move interface scope
1365:       #
1366:       interface_code = ""
1367:       while remaining_code =~ /^\s*?
1368:                                    interface(
1369:                                               \s+\w+                      |
1370:                                               \s+operator\s*?\(.*?\)       |
1371:                                               \s+assignment\s*?\(\s*?=\s*?\)
1372:                                             )?\s*?$
1373:                                    (.*?)
1374:                                    ^\s*?end\s+interface.*?$
1375:                               /imx
1376:         interface_code << remove_empty_head_lines($&) + "\n"
1377:         remaining_code = $~.pre_match
1378:         remaining_code << $~.post_match
1379:       end
1380: 
1381:       #
1382:       # Parse global constants or variables in modules
1383:       #
1384:       const_var_defs = definition_info(before_contains_code)
1385:       const_var_defs.each{|defitem|
1386:         next if defitem.nodoc
1387:         const_or_var_type = "Variable"
1388:         const_or_var_progress = "v"
1389:         if defitem.include_attr?("parameter")
1390:           const_or_var_type = "Constant"
1391:           const_or_var_progress = "c"
1392:         end
1393:         const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
1394:         const_or_var.set_priority(defitem.doc_priority) if defitem.doc_priority
1395:         const_or_var.singleton = false
1396:         const_or_var.params = ""
1397:         self_comment = find_arguments([defitem.varname], before_contains_code)
1398:         const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
1399:         const_or_var.comment << self_comment if self_comment
1400:         progress const_or_var_progress
1401:         @stats.num_methods += 1
1402:         container.add_method const_or_var
1403: 
1404:         set_visibility(container, defitem.varname, visibility_default, @@public_methods)
1405: 
1406:         if defitem.include_attr?("public")
1407:           container.set_visibility_for([defitem.varname], :public)
1408:         elsif defitem.include_attr?("private")
1409:           container.set_visibility_for([defitem.varname], :private)
1410:         end
1411: 
1412:         check_public_methods(const_or_var, container.name)
1413: 
1414:       } if const_var_defs
1415: 
1416:       remaining_lines = remaining_code.split("\n")
1417: 
1418:       # "subroutine" or "function" parts are parsed (new)
1419:       #
1420:       level_depth = 0
1421:       block_searching_flag = nil
1422:       block_searching_lines = []
1423:       pre_comment = []
1424:       procedure_trailing = ""
1425:       procedure_name = ""
1426:       procedure_params = ""
1427:       procedure_prefix = ""
1428:       procedure_result_arg = ""
1429:       procedure_type = ""
1430:       contains_lines = []
1431:       contains_flag = nil
1432:       remaining_lines.collect!{|line|
1433:         if !block_searching_flag
1434:           # subroutine
1435:           if line =~ /^\s*?
1436:                            (recursive|pure|elemental)?\s*?
1437:                            subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
1438:                      /ix
1439:             block_searching_flag = :subroutine
1440:             block_searching_lines << line
1441: 
1442:             procedure_name = $2.chomp.strip
1443:             procedure_params = $3 || ""
1444:             procedure_prefix = $1 || ""
1445:             procedure_trailing = $4 || "!"
1446:             next false
1447: 
1448:           # function
1449:           elsif line =~ /^\s*?
1450:                          (recursive|pure|elemental)?\s*?
1451:                          (
1452:                              character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1453:                            | type\s*?\([\w\s]+?\)\s+
1454:                            | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1455:                            | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1456:                            | double\s+precision\s+
1457:                            | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1458:                            | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1459:                          )?
1460:                          function\s+(\w+)\s*?
1461:                          (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
1462:                         /ix
1463:             block_searching_flag = :function
1464:             block_searching_lines << line
1465: 
1466:             procedure_prefix = $1 || ""
1467:             procedure_type = $2 ? $2.chomp.strip : nil
1468:             procedure_name = $8.chomp.strip
1469:             procedure_params = $9 || ""
1470:             procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
1471:             procedure_trailing = $12 || "!"
1472:             next false
1473:           elsif line =~ /^\s*?!\s?(.*)/
1474:             pre_comment << line
1475:             next line
1476:           else
1477:             pre_comment = []
1478:             next line
1479:           end
1480:         end
1481:         contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
1482:         block_searching_lines << line
1483:         contains_lines << line if contains_flag
1484: 
1485:         level_depth += 1 if block_start?(line)
1486:         level_depth -= 1 if block_end?(line)
1487:         if level_depth >= 0
1488:           next false
1489:         end
1490: 
1491:         # "procedure_code" is formatted.
1492:         # ":nodoc:" flag is checked.
1493:         #
1494:         procedure_code = block_searching_lines.join("\n")
1495:         procedure_code = remove_empty_head_lines(procedure_code)
1496:         if procedure_trailing.sub(/^!/, '') =~ IGNORED_MARKER_REGEXP
1497:           # next loop to search next block
1498:           level_depth = 0
1499:           block_searching_flag = nil
1500:           block_searching_lines = []
1501:           pre_comment = []
1502:           procedure_trailing = ""
1503:           procedure_name = ""
1504:           procedure_params = ""
1505:           procedure_prefix = ""
1506:           procedure_result_arg = ""
1507:           procedure_type = ""
1508:           contains_lines = []
1509:           contains_flag = nil
1510:           next false
1511:         end
1512: 
1513:         # AnyMethod is created, and added to container
1514:         #
1515:         subroutine_function = nil
1516:         if block_searching_flag == :subroutine
1517:           subroutine_prefix   = procedure_prefix
1518:           subroutine_name     = procedure_name
1519:           subroutine_params   = procedure_params
1520:           subroutine_trailing = procedure_trailing
1521:           subroutine_code     = procedure_code
1522:           subroutine_priority, subroutine_trailing = doc_priority_from_trailing(subroutine_trailing)
1523: 
1524:           subroutine_comment = COMMENTS_ARE_UPPER ? 
1525:             pre_comment.join("\n") + "\n" + subroutine_trailing : 
1526:               subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
1527:           subroutine = AnyMethod.new("subroutine", subroutine_name)
1528:           parse_subprogram(subroutine, subroutine_params,
1529:                            subroutine_comment, subroutine_code,
1530:                            before_contains_code, nil, subroutine_prefix)
1531:           subroutine.set_priority(subroutine_priority) if subroutine_priority
1532:           progress "s"
1533:           @stats.num_methods += 1
1534:           container.add_method subroutine
1535:           subroutine_function = subroutine
1536: 
1537:           namelist_comment = 
1538:             find_namelists(subroutine, subroutine_code, before_contains_code)
1539:           subroutine.comment << namelist_comment if namelist_comment
1540: 
1541:         elsif block_searching_flag == :function
1542:           function_prefix     = procedure_prefix
1543:           function_type       = procedure_type
1544:           function_name       = procedure_name
1545:           function_params_org = procedure_params
1546:           function_result_arg = procedure_result_arg
1547:           function_trailing   = procedure_trailing
1548:           function_code_org   = procedure_code
1549:           function_priority, function_trailing = doc_priority_from_trailing(function_trailing)
1550: 
1551:           function_comment = COMMENTS_ARE_UPPER ?
1552:             pre_comment.join("\n") + "\n" + function_trailing :
1553:               function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
1554: 
1555:           function_code = "#{function_code_org}"
1556:           if function_type
1557:             function_code << "\n" + function_type + " :: " + function_result_arg
1558:           end
1559: 
1560:           if function_params_org =~ /^\s*\(\s*\)\s*$/
1561:             function_params =
1562:               function_params_org.sub(/^\(/, "\(#{function_result_arg}")
1563:           else
1564:             function_params =
1565:               function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
1566:           end
1567: 
1568:           function = AnyMethod.new("function", function_name)
1569:           parse_subprogram(function, function_params,
1570:                            function_comment, function_code,
1571:                            before_contains_code, true, function_prefix)
1572:           function.set_priority(function_priority) if function_priority
1573: 
1574:           # Specific modification due to function
1575:           function.params.sub!(/\(\s*?#{function_result_arg}\s*?,?\s*?/, "\( ")
1576:           function.params << " result(" + function_result_arg + ")"
1577:           function.start_collecting_tokens
1578:           function.add_token Token.new(1,1).set_text(function_code_org)
1579: 
1580:           progress "f"
1581:           @stats.num_methods += 1
1582:           container.add_method function
1583:           subroutine_function = function
1584: 
1585:           namelist_comment = 
1586:             find_namelists(function, function_code, before_contains_code)
1587:           function.comment << namelist_comment if namelist_comment
1588: 
1589:         end
1590: 
1591:         # The visibility of procedure is specified
1592:         #
1593:         set_visibility(container, procedure_name, 
1594:                        visibility_default, @@public_methods)
1595: 
1596:         # The alias for this procedure from external modules
1597:         #
1598:         check_external_aliases(procedure_name,
1599:                                subroutine_function.params,
1600:                                subroutine_function.comment, subroutine_function) if external
1601:         check_public_methods(subroutine_function, container.name)
1602: 
1603: 
1604:         # contains_lines are parsed as private procedures
1605:         if contains_flag
1606:           parse_program_or_module(container,
1607:                                   contains_lines.join("\n"), :private)
1608:         end
1609: 
1610:         # next loop to search next block
1611:         level_depth = 0
1612:         block_searching_flag = nil
1613:         block_searching_lines = []
1614:         pre_comment = []
1615:         procedure_trailing = ""
1616:         procedure_name = ""
1617:         procedure_params = ""
1618:         procedure_prefix = ""
1619:         procedure_result_arg = ""
1620:         contains_lines = []
1621:         contains_flag = nil
1622:         next false
1623:       } # End of remaining_lines.collect!{|line|
1624: 
1625:       # Array remains_lines is converted to String remains_code again
1626:       #
1627:       remaining_code = remaining_lines.join("\n")
1628: 
1629:       #
1630:       # Parse interface
1631:       #
1632:       interface_scope = false
1633:       generic_name = ""
1634:       interface_code.split("\n").each{ |line|
1635:         if /^\s*?
1636:                  interface(
1637:                             \s+\w+|
1638:                             \s+operator\s*?\(.*?\)|
1639:                             \s+assignment\s*?\(\s*?=\s*?\)
1640:                           )?
1641:                  \s*?(!.*?)?$
1642:            /ix =~ line
1643:           generic_name = $1 ? $1.strip.chomp : nil
1644:           interface_trailing = $2 || "!"
1645:           interface_scope = true
1646:           interface_scope = false if interface_trailing.sub(/^!/, '') =~ IGNORED_MARKER_REGEXP
1647: #          if generic_name =~ /operator\s*?\((.*?)\)/i
1648: #            operator_name = $1
1649: #            if operator_name && !operator_name.empty?
1650: #              generic_name = "#{operator_name}"
1651: #            end
1652: #          end
1653: #          if generic_name =~ /assignment\s*?\((.*?)\)/i
1654: #            assignment_name = $1
1655: #            if assignment_name && !assignment_name.empty?
1656: #              generic_name = "#{assignment_name}"
1657: #            end
1658: #          end
1659:         end
1660:         if /^\s*?end\s+interface/i =~ line
1661:           interface_scope = false
1662:           generic_name = nil
1663:         end
1664:         # internal alias
1665:         if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
1666:           procedures = $1.strip.chomp
1667:           procedures_trailing = $2 || "!"
1668:           next if procedures_trailing.sub(/^!/, '') =~ IGNORED_MARKER_REGEXP
1669:           procedures_priority, procedures_trailing = doc_priority_from_trailing(procedures_trailing)
1670: 
1671:           procedures.split(",").each{ |proc|
1672:             proc.strip!
1673:             proc.chomp!
1674:             next if generic_name == proc || !generic_name
1675:             old_meth = container.find_symbol(proc, nil, @options_ignore_case)
1676:             next if !old_meth
1677:             nolink = old_meth.visibility == :private ? true : nil
1678:             nolink = nil if @options.show_all
1679:             new_meth = 
1680:                initialize_external_method(generic_name, proc, 
1681:                                           old_meth.params, nil, 
1682:                                           old_meth.comment, 
1683:                                           old_meth.clone.token_stream[0].text, 
1684:                                           true, nolink)
1685:             new_meth.singleton = old_meth.singleton
1686:             new_meth.set_priority(procedures_priority) if procedures_priority
1687: 
1688:             progress "i"
1689:             @stats.num_methods += 1
1690:             container.add_method new_meth
1691: 
1692:             set_visibility(container, generic_name, visibility_default, @@public_methods)
1693: 
1694:             check_public_methods(new_meth, container.name)
1695: 
1696:           }
1697:         end
1698: 
1699:         # external aliases
1700:         if interface_scope
1701:           # subroutine
1702:           proc = nil
1703:           params = nil
1704:           procedures_trailing = nil
1705:           if line =~ /^\s*?
1706:                            (recursive|pure|elemental)?\s*?
1707:                            subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
1708:                      /ix
1709:             proc = $2.chomp.strip
1710:             proc_name = generic_name || proc
1711:             params = $3 || ""
1712:             procedures_trailing = $4 || "!"
1713: 
1714:           # function
1715:           elsif line =~ /^\s*?
1716:                          (recursive|pure|elemental)?\s*?
1717:                          (
1718:                              character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1719:                            | type\s*?\([\w\s]+?\)\s+
1720:                            | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1721:                            | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1722:                            | double\s+precision\s+
1723:                            | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1724:                            | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
1725:                          )?
1726:                          function\s+(\w+)\s*?
1727:                          (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
1728:                         /ix
1729:             proc = $8.chomp.strip
1730:             proc_name = generic_name || proc
1731:             params = $9 || ""
1732:             procedures_trailing = $12 || "!"
1733:           else
1734:             next
1735:           end
1736:           next if procedures_trailing.sub(/^!/, '') =~ IGNORED_MARKER_REGEXP
1737:           procedures_priority, procedures_trailing = doc_priority_from_trailing(procedures_trailing)
1738:           indicated_method = nil
1739:           indicated_file   = nil
1740:           TopLevel.all_files.each do |name, toplevel|
1741:             indicated_method = toplevel.find_local_symbol(proc, @options_ignore_case)
1742:             indicated_file = name
1743:             break if indicated_method
1744:           end
1745: 
1746:           if indicated_method
1747:             external_method = 
1748:               initialize_external_method(proc_name, proc, 
1749:                                          indicated_method.params, 
1750:                                          indicated_file, 
1751:                                          indicated_method.comment)
1752:             external_method.set_priority(procedures_priority) if procedures_priority
1753: 
1754:             progress "e"
1755:             @stats.num_methods += 1
1756:             container.add_method external_method
1757:             set_visibility(container, proc_name, visibility_default, @@public_methods)
1758:             if !container.include_requires?(indicated_file, @options_ignore_case)
1759:               container.add_require(Require.new(indicated_file, ""))
1760:             end
1761:             check_public_methods(external_method, container.name)
1762: 
1763:           else
1764:             @@external_aliases << {
1765:               "new_name"  => proc_name,
1766:               "old_name"  => proc,
1767:               "file_or_module" => container,
1768:               "visibility" => find_visibility(container, proc_name, @@public_methods) || visibility_default,
1769:               "doc_priority" => procedures_priority
1770:             }
1771:           end
1772:         end
1773: 
1774:       } if interface_code # End of interface_code.split("\n").each ...
1775: 
1776:       #
1777:       # Already imported methods are removed from @@public_methods.
1778:       # Remainders are assumed to be imported from other modules.
1779:       #
1780:       # 既に参照済みのメソッドは @@public_methods から取り除く.
1781:       # 残りは外部モジュールからの参照と仮定する.
1782:       #
1783:       @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
1784: 
1785:       @@public_methods.each{ |pub_meth|
1786:         next unless pub_meth["file_or_module"].name == container.name
1787:         pub_meth["used_modules"].each{ |used_mod|
1788:           TopLevel.all_classes_and_modules.each{ |modules|
1789:             if modules.name == used_mod ||
1790:                 modules.name.upcase == used_mod.upcase &&
1791:                 @options_ignore_case
1792:               modules.method_list.each{ |meth|
1793:                 if meth.name == pub_meth["name"] ||
1794:                     meth.name.upcase == pub_meth["name"].upcase &&
1795:                     @options_ignore_case
1796:                   new_meth = initialize_public_method(meth,
1797:                                                       modules.name)
1798:                   if pub_meth["local_name"]
1799:                     new_meth.name = pub_meth["local_name"]
1800:                   end
1801:                   progress "e"
1802:                   @stats.num_methods += 1
1803:                   container.add_method new_meth
1804:                 end
1805:               }
1806:             end
1807:           }
1808:         }
1809:       }
1810: 
1811:       container
1812:     end

Parse arguments, comment, code of subroutine and function. Return AnyMethod object.

[Source]

      # File parsers/parse_f95.rb, line 1818
1818:     def parse_subprogram(subprogram, params, comment, code, 
1819:                          before_contains=nil, function=nil, prefix=nil)
1820:       subprogram.singleton = false
1821:       prefix = "" if !prefix
1822:       arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
1823:       args_comment, params_opt = 
1824:         find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
1825:                        nil, nil, true)
1826:       params_opt = "( " + params_opt + " ) " if params_opt
1827:       subprogram.params = params_opt || ""
1828: 
1829:       block_comment = find_comments comment
1830:       if function
1831:         subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
1832:       else
1833:         subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
1834:       end
1835:       subprogram.comment << args_comment if args_comment
1836:       subprogram.comment << block_comment if block_comment
1837: 
1838:       # For output source code
1839:       subprogram.start_collecting_tokens
1840:       subprogram.add_token Token.new(1,1).set_text(code)
1841: 
1842:       subprogram
1843:     end

Parse visibility

[Source]

      # File parsers/parse_f95.rb, line 2148
2148:     def parse_visibility(code, default, container)
2149:       result = []
2150:       visibility_default = default || :public
2151: 
2152:       used_modules = []
2153:       container.includes.each{|i| used_modules << i.name} if container
2154: 
2155:       remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
2156:       remaining_code.split("\n").each{ |line|
2157:         if /^\s*?private\s*?$/ =~ line
2158:           visibility_default = :private
2159:           break
2160:         end
2161:       } if remaining_code
2162: 
2163:       remaining_code.split("\n").each{ |line|
2164:         if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
2165:           methods = $2.sub(/!.*$/, '')
2166:           methods.split(",").each{ |meth|
2167:             meth.sub!(/!.*$/, '')
2168:             meth.gsub!(/:/, '')
2169:             result << {
2170:               "name" => meth.chomp.strip,
2171:               "visibility" => :private,
2172:               "used_modules" => used_modules.clone,
2173:               "file_or_module" => container,
2174:               "entity_is_discovered" => nil,
2175:               "local_name" => nil
2176:             }
2177:           }
2178:         elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
2179:           methods = $2.sub(/!.*$/, '')
2180:           methods.split(",").each{ |meth|
2181:             meth.sub!(/!.*$/, '')
2182:             meth.gsub!(/:/, '')
2183:             result << {
2184:               "name" => meth.chomp.strip,
2185:               "visibility" => :public,
2186:               "used_modules" => used_modules.clone,
2187:               "file_or_module" => container,
2188:               "entity_is_discovered" => nil,
2189:               "local_name" => nil
2190:             }
2191:           }
2192:         end
2193:       } if remaining_code
2194: 
2195:       if container
2196:         result.each{ |vis_info|
2197:           vis_info["parent"] = container.name
2198:         }
2199:       end
2200: 
2201:       return visibility_default, result
2202:     end

[Source]

      # File parsers/parse_f95.rb, line 2088
2088:     def progress(char)
2089:       unless @options.quiet
2090:         @progress.print(char)
2091:         @progress.flush
2092:       end
2093:     end

Empty lines in header are removed

[Source]

      # File parsers/parse_f95.rb, line 2572
2572:     def remove_empty_head_lines(text)
2573:       return "" unless text
2574:       lines = text.split("\n")
2575:       header = true
2576:       lines.delete_if{ |line|
2577:         header = false if /\S/ =~ line
2578:         header && /^\s*?$/ =~ line
2579:       }
2580:       lines.join("\n")
2581:     end

header marker "=", "==", … are removed

[Source]

      # File parsers/parse_f95.rb, line 2585
2585:     def remove_header_marker(text)
2586:       return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
2587:     end

[Source]

      # File parsers/parse_f95.rb, line 2589
2589:     def remove_private_comments(body)
2590:       body.gsub!(/^(\s*)!--\s*?$.*?^\s*!\+\+\s*?$/m, '\\1!')
2591:       return body
2592:     end

Remove "Alias for" in end of comments

[Source]

      # File parsers/parse_f95.rb, line 2551
2551:     def remove_trailing_alias(text)
2552:       return "" if !text
2553:       lines = text.split("\n").reverse
2554:       comment_block = Array.new
2555:       checked = false
2556:       lines.each do |line|
2557:         if !checked 
2558:           if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
2559:               /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
2560:             checked = true
2561:             next
2562:           end
2563:         end
2564:         comment_block.unshift line
2565:       end
2566:       nice_lines = comment_block.join("\n")
2567:       nice_lines ||= ""
2568:       return nice_lines
2569:     end

devine code constructs

[Source]

      # File parsers/parse_f95.rb, line 929
 929:     def scan
 930: 
 931:       # remove private comment
 932:       remaining_code = remove_private_comments(@body)
 933: 
 934:       # continuation lines are united to one line
 935:       remaining_code = united_to_one_line(remaining_code)
 936: 
 937:       # semicolons are replaced to line feed
 938:       remaining_code = semicolon_to_linefeed(remaining_code)
 939: 
 940:       # collect comment for file entity
 941:       whole_comment, remaining_code = collect_first_comment(remaining_code)
 942:       @top_level.comment = whole_comment
 943: 
 944:       # String "remaining_code" is converted to Array "remaining_lines"
 945:       remaining_lines = remaining_code.split("\n")
 946: 
 947:       # "module" or "program" parts are parsed (new)
 948:       #
 949:       level_depth = 0
 950:       block_searching_flag = nil
 951:       block_searching_lines = []
 952:       pre_comment = []
 953:       module_program_trailing = ""
 954:       module_program_name = ""
 955:       other_block_level_depth = 0
 956:       other_block_searching_flag = nil
 957:       remaining_lines.collect!{|line|
 958:         if !block_searching_flag && !other_block_searching_flag
 959:           if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
 960:             block_searching_flag = :module
 961:             block_searching_lines << line
 962:             module_program_name = $1
 963:             module_program_trailing = find_comments($2)
 964:             next false
 965:           elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
 966:                  line =~ /^\s*?\w/ && !block_start?(line)
 967:             block_searching_flag = :program
 968:             block_searching_lines << line
 969:             module_program_name = $1 || ""
 970:             module_program_trailing = find_comments($2)
 971:             next false
 972: 
 973:           elsif block_start?(line)
 974:             other_block_searching_flag = true
 975:             next line
 976: 
 977:           elsif line =~ /^\s*?!\s?(.*)/
 978:             pre_comment << line
 979:             next line
 980:           else
 981:             pre_comment = []
 982:             next line
 983:           end
 984:         elsif other_block_searching_flag
 985:           other_block_level_depth += 1 if block_start?(line)
 986:           other_block_level_depth -= 1 if block_end?(line)
 987:           if other_block_level_depth < 0
 988:             other_block_level_depth = 0
 989:             other_block_searching_flag = nil
 990:           end
 991:           next line
 992:         end
 993: 
 994:         block_searching_lines << line
 995:         level_depth += 1 if block_start?(line)
 996:         level_depth -= 1 if block_end?(line)
 997:         if level_depth >= 0
 998:           next false
 999:         end
1000: 
1001:         # "module_program_code" is formatted.
1002:         # ":nodoc:" flag is checked.
1003:         #
1004:         module_program_code = block_searching_lines.join("\n")
1005:         module_program_code = remove_empty_head_lines(module_program_code)
1006:         if module_program_trailing =~ IGNORED_MARKER_REGEXP
1007:           # next loop to search next block
1008:           level_depth = 0
1009:           block_searching_flag = false
1010:           block_searching_lines = []
1011:           pre_comment = []
1012:           next false
1013:         end
1014: 
1015:         # NormalClass is created, and added to @top_level
1016:         #
1017:         if block_searching_flag == :module
1018:           module_name = module_program_name
1019:           module_code = module_program_code
1020:           module_trailing = module_program_trailing
1021:           progress "m"
1022:           @stats.num_modules += 1
1023:           f9x_module = @top_level.add_module NormalClass, module_name
1024:           f9x_module.record_location @top_level
1025: 
1026:           #
1027:           # Add provided modules information to @top_level comment
1028:           #
1029:           provided_modules = []
1030:           provided_mes_line_num = nil
1031:           top_level_comment_lines = []
1032:           line_num = 0
1033:           @top_level.comment.split("\n").each{|line|
1034:             top_level_comment_lines << line
1035:             line_num += 1
1036:             next if line.empty?
1037:             if !provided_mes_line_num && /^\s?#{PROVIDED_MODULES_MES}/ =~ line
1038:               provided_mes_line_num = line_num
1039:               next
1040:             end
1041:             if provided_mes_line_num
1042:               if /^\s?\*\s+<b>(\w+)<\/b>/ =~ line
1043:                 provided_modules << $1
1044:               else
1045:                 provided_mes_line_num = nil
1046:               end
1047:             end
1048:           }
1049:           line_num = 0
1050:           if provided_mes_line_num
1051:             top_level_comment_lines.collect!{ |line|
1052:               line_num += 1
1053:               if line_num < provided_mes_line_num
1054:                 line
1055:               else
1056:                 nil
1057:               end
1058:             }
1059:             top_level_comment_lines.delete_if{|line| !line }
1060:           end
1061:           top_level_comment_lines << "\n" + PROVIDED_MODULES_MES + "."
1062:           if provided_mes_line_num
1063:             top_level_comment_lines[-1].sub!(/\.$/, '')
1064:             top_level_comment_lines[-1] << "s."
1065:           end
1066:           provided_modules.each{ |mod|
1067:             top_level_comment_lines << "* <b>" + mod + "</b>"
1068:           }
1069:           top_level_comment_lines << "* <b>" + module_name + "</b>"
1070:           @top_level.comment = top_level_comment_lines.join("\n")
1071: 
1072:           #
1073:           # Information about the module is parsed
1074:           #
1075:           f9x_comment = COMMENTS_ARE_UPPER ? find_comments(pre_comment.join("\n")) +
1076:             "\n" + module_trailing : module_trailing + "\n" +
1077:             find_comments(module_code.sub(/^.*$\n/i, ''))
1078:           f9x_module.comment = f9x_comment
1079:           parse_program_or_module(f9x_module, module_code)
1080: 
1081:           TopLevel.all_files.each do |name, toplevel|
1082:             if toplevel.include_includes?(module_name, @options_ignore_case)
1083:               if !toplevel.include_requires?(@file_name, @options_ignore_case)
1084:                 toplevel.add_require(Require.new(@file_name, ""))
1085:               end
1086:             end
1087:             toplevel.each_classmodule{|m|
1088:               if m.include_includes?(module_name, @options_ignore_case)
1089:                 if !m.include_requires?(@file_name, @options_ignore_case)
1090:                   m.add_require(Require.new(@file_name, ""))
1091:                 end
1092:               end
1093:             }
1094:           end
1095: 
1096:           namelist_comment = 
1097:             find_namelists(f9x_module, before_contains(module_code))
1098:           f9x_module.comment << namelist_comment if namelist_comment
1099: 
1100:         elsif block_searching_flag == :program
1101:           program_name = module_program_name
1102:           program_name = "main_program" if program_name.empty?
1103:           program_code = module_program_code
1104:           program_trailing = module_program_trailing
1105:           program_priority, program_trailing = doc_priority_from_trailing(program_trailing)
1106: 
1107:           program_comment = COMMENTS_ARE_UPPER ? find_comments(pre_comment.join("\n")) + 
1108:             "\n" + program_trailing : program_trailing + "\n" + 
1109:             find_comments(program_code.sub(/^.*$\n/i, ''))
1110: 
1111:           progress "p"
1112:           @stats.num_methods += 1
1113:           f9x_mainprogram = AnyMethod.new("main_program", program_name)
1114:           f9x_mainprogram.singleton = false
1115:           f9x_mainprogram.comment = "<b><em> Main Program </em></b> :: <tt></tt>\n"
1116:           f9x_mainprogram.comment << program_comment
1117:           f9x_mainprogram.params = ""
1118:           f9x_mainprogram.set_priority(program_priority) if program_priority
1119: 
1120:           # For output source code
1121:           f9x_mainprogram.start_collecting_tokens
1122:           f9x_mainprogram.add_token Token.new(1,1).set_text(program_code)
1123: 
1124:           @top_level.add_method f9x_mainprogram
1125:           parse_program_or_module(@top_level, program_code, :private)
1126: 
1127:           namelist_comment = find_namelists(f9x_mainprogram, program_code)
1128:           f9x_mainprogram.comment << namelist_comment if namelist_comment
1129:         end
1130: 
1131:         # next loop to search next block
1132:         level_depth = 0
1133:         block_searching_flag = false
1134:         block_searching_lines = []
1135:         pre_comment = []
1136:         next false
1137:       }
1138: 
1139:       remaining_lines.delete_if{ |line|
1140:         line == false
1141:       }
1142: 
1143:       # External subprograms and functions are parsed
1144:       #
1145:       # 単一のファイル内において program や module に格納されない,
1146:       # 外部サブルーチン, 外部関数部分の解析.
1147:       #
1148:       parse_program_or_module(@top_level, remaining_lines.join("\n"),
1149:                               :public, true)
1150: 
1151:       @top_level
1152:     end

Semicolons are replaced to line feed.

[Source]

      # File parsers/parse_f95.rb, line 2454
2454:     def semicolon_to_linefeed(text)
2455:       return "" unless text
2456:       lines = text.split("\n")
2457:       lines.collect!{ |line|
2458:         indent_space = ""
2459:         if line =~ /^(\s+)/
2460:           indent_space = $1
2461:         end
2462:         words = line.split("")
2463:         commentout = false
2464:         squote = false ; dquote = false
2465:         words.collect! { |char|
2466:           if !(squote) && !(dquote) && !(commentout)
2467:             case char
2468:             when "!" ; commentout = true ; next char
2469:             when "\""; dquote = true     ; next char
2470:             when "\'"; squote = true     ; next char
2471:             when ";" ;                     "\n"+indent_space
2472:             else next char
2473:             end
2474:           elsif commentout
2475:             next char
2476:           elsif squote
2477:             case char
2478:             when "\'"; squote = false ; next char
2479:             else next char
2480:             end
2481:           elsif dquote
2482:             case char
2483:             when "\""; dquote = false ; next char
2484:             else next char
2485:             end
2486:           end
2487:         }
2488:         words.join("")
2489:       }
2490:       return lines.join("\n")
2491:     end

Set visibility

"subname" element of "visibility_info" is deleted.

[Source]

      # File parsers/parse_f95.rb, line 2209
2209:     def set_visibility(container, subname, visibility_default, visibility_info)
2210:       return unless container || subname || visibility_default || visibility_info
2211:       not_found = true
2212:       visibility_info.collect!{ |info|
2213:         if info["name"] == subname ||
2214:             @options_ignore_case && info["name"].upcase == subname.upcase
2215:           if info["file_or_module"].name == container.name
2216:             container.set_visibility_for([subname], info["visibility"])
2217:             info["entity_is_discovered"] = true
2218:             not_found = false
2219:           end
2220:         end
2221:         info
2222:       }
2223:       if not_found
2224:         return container.set_visibility_for([subname], visibility_default)
2225:       else
2226:         return container
2227:       end
2228:     end

Continuous lines are united.

Comments in continuous lines are removed. If delete_space=false, spaces around "&" are not deleted.

Example

before

   subroutine func(a, b, c, d, e, & ! ignored comments
     &             f, g, h)         ! valid comments

after

   subroutine func(a, b, c, d, e, f, g, h)         ! valid comments

[Source]

      # File parsers/parse_f95.rb, line 2329
2329:     def united_to_one_line(f90src, delete_space=true)
2330:       return "" unless f90src
2331:       lines = f90src.split("\n")
2332:       previous_continuing = false
2333:       now_continuing = false
2334:       body = ""
2335:       squote = false ; dquote = false
2336:       lines.each{ |line|
2337:         words = line.split("")
2338:         next if words.empty? && previous_continuing
2339:         commentout = false
2340:         brank_flag = true ; brank_char = ""
2341:         ignore = false
2342:         words.collect! { |char|
2343:           if previous_continuing && brank_flag
2344:             now_continuing = true
2345:             ignore         = true
2346:             case char
2347:             when "!"                       ; break
2348:             when " " ; brank_char << char  ; next ""
2349:             when "&"
2350:               brank_flag = false
2351:               now_continuing = false
2352:               next ""
2353:             else 
2354:               brank_flag     = false
2355:               now_continuing = false
2356:               ignore         = false
2357:               next brank_char + char
2358:             end
2359:           end
2360:           ignore = false
2361: 
2362:           if now_continuing && !(squote) && !(dquote)
2363:             next ""
2364:           elsif !(squote) && !(dquote) && !(commentout)
2365:             case char
2366:             when "!" ; commentout = true     ; next char
2367:             when "\""; dquote = true         ; next char
2368:             when "\'"; squote = true         ; next char
2369:             when "&" ; now_continuing = true ; next ""
2370:             else next char
2371:             end
2372:           elsif commentout
2373:             next char
2374:           elsif squote
2375:             case char
2376:             when "\'"; squote = false ; now_continuing = false ; next char
2377:             when "&" ; now_continuing = true ; next ""
2378:             else next char
2379:             end
2380:           elsif dquote
2381:             case char
2382:             when "\""; dquote = false ; now_continuing = false ; next char
2383:             when "&" ; now_continuing = true ; next ""
2384:             else next char
2385:             end
2386:           end
2387:         }
2388:         if !ignore && !previous_continuing || !brank_flag
2389:           if previous_continuing
2390:             if delete_space
2391:               joined_words = words.join("")
2392:               body = body.rstrip + " " + joined_words.lstrip
2393:             else
2394:               body << words.join("")
2395:             end
2396:           else
2397:             body << "\n" + words.join("")
2398:           end
2399:         end
2400:         previous_continuing = now_continuing ? true : false
2401:         now_continuing = false
2402:       }
2403:       return body
2404:     end

[Validate]