Flowdock
method

parse_program_or_module

Importance_0
v1_9_1_378 - Show latest stable - 0 notes - Class: F95
parse_program_or_module(container, code, visibility=:public, external=nil) private

No documentation

This method has no description. You can help the Ruby community by adding new notes.

Hide source
# File lib/rdoc/parser/f95.rb, line 354
  def parse_program_or_module(container, code,
                              visibility=:public, external=nil)
    return unless container
    return unless code
    remaining_lines = code.split("\n")
    remaining_code = "#{code}"

    #
    # Parse variables before "contains" in module
    #
    level_depth = 0
    before_contains_lines = []
    before_contains_code = nil
    before_contains_flag = nil
    remaining_lines.each{ |line|
      if !before_contains_flag
        if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/
          before_contains_flag = true
        end
      else
        break if line =~ /^\s*?contains\s*?(!.*?)?$/
        level_depth += 1 if block_start?(line)
        level_depth -= 1 if block_end?(line)
        break if level_depth < 0
        before_contains_lines << line
      end
    }
    before_contains_code = before_contains_lines.join("\n")
    if before_contains_code
      before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/m, "")
      before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/m, "")
    end

    #
    # Parse global "use"
    #
    use_check_code = "#{before_contains_code}"
    cascaded_modules_list = []
    while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/
      use_check_code = $~.pre_match
      use_check_code << $~.post_match
      used_mod_name = $1.strip.chomp
      used_list = $2 || ""
      used_trailing = $3 || ""
      next if used_trailing =~ /!:nodoc:/
      if !container.include_includes?(used_mod_name, @options.ignore_case)
        # progress "." # HACK what stats thingy does this correspond to?
        container.add_include Include.new(used_mod_name, "")
      end
      if ! (used_list =~ /\,\s*?only\s*?:/ )
        cascaded_modules_list << "\#" + used_mod_name
      end
    end

    #
    # Parse public and private, and store information.
    # This information is used when "add_method" and
    # "set_visibility_for" are called.
    #
    visibility_default, visibility_info = 
              parse_visibility(remaining_lines.join("\n"), visibility, container)
    @@public_methods.concat visibility_info
    if visibility_default == :public
      if !cascaded_modules_list.empty?
        cascaded_modules = 
          Attr.new("Cascaded Modules",
                   "Imported modules all of whose components are published again",
                   "",
                   cascaded_modules_list.join(", "))
        container.add_attribute(cascaded_modules)
      end
    end

    #
    # Check rename elements
    #
    use_check_code = "#{before_contains_code}"
    while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/
      use_check_code = $~.pre_match
      use_check_code << $~.post_match
      used_mod_name = $1.strip.chomp
      used_elements = $2.sub(/\s*?only\s*?:\s*?/, '')
      used_elements.split(",").each{ |used|
        if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
          local = $1
          org = $2
          @@public_methods.collect!{ |pub_meth|
            if local == pub_meth["name"] ||
                local.upcase == pub_meth["name"].upcase &&
                @options.ignore_case
              pub_meth["name"] = org
              pub_meth["local_name"] = local
            end
            pub_meth
          }
        end
      }
    end

    #
    # Parse private "use"
    #
    use_check_code = remaining_lines.join("\n")
    while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/
      use_check_code = $~.pre_match
      use_check_code << $~.post_match
      used_mod_name = $1.strip.chomp
      used_trailing = $3 || ""
      next if used_trailing =~ /!:nodoc:/
      if !container.include_includes?(used_mod_name, @options.ignore_case)
        # progress "." # HACK what stats thingy does this correspond to?
        container.add_include Include.new(used_mod_name, "")
      end
    end

    container.each_includes{ |inc|
      TopLevel.all_files.each do |name, toplevel|
        indicated_mod = toplevel.find_symbol(inc.name,
                                             nil, @options.ignore_case)
        if indicated_mod
          indicated_name = indicated_mod.parent.file_relative_name
          if !container.include_requires?(indicated_name, @options.ignore_case)
            container.add_require(Require.new(indicated_name, ""))
          end
          break
        end
      end
    }

    #
    # Parse derived-types definitions
    #
    derived_types_comment = ""
    remaining_code = remaining_lines.join("\n")
    while remaining_code =~ /^\s*?
                                  type[\s\,]+(public|private)?\s*?(::)?\s*?
                                  (\w+)\s*?(!.*?)?$
                                  (.*?)
                                  ^\s*?end\s+type.*?$
                            /mx
      remaining_code = $~.pre_match
      remaining_code << $~.post_match
      typename = $3.chomp.strip
      type_elements = $5 || ""
      type_code = remove_empty_head_lines($&)
      type_trailing = find_comments($4)
      next if type_trailing =~ /^:nodoc:/
      type_visibility = $1
      type_comment = COMMENTS_ARE_UPPER ? 
        find_comments($~.pre_match) + "\n" + type_trailing :
          type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/, ''))
      type_element_visibility_public = true
      type_code.split("\n").each{ |line|
        if /^\s*?private\s*?$/ =~ line
          type_element_visibility_public = nil
          break
        end
      } if type_code

      args_comment = ""
      type_args_info = nil

      if @options.show_all
        args_comment = find_arguments(nil, type_code, true)
      else
        type_public_args_list = []
        type_args_info = definition_info(type_code)
        type_args_info.each{ |arg|
          arg_is_public = type_element_visibility_public
          arg_is_public = true if arg.include_attr?("public")
          arg_is_public = nil if arg.include_attr?("private")
          type_public_args_list << arg.varname if arg_is_public
        }
        args_comment = find_arguments(type_public_args_list, type_code)
      end

      type = AnyMethod.new("type #{typename}", typename)
      type.singleton = false
      type.params = ""
      type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
      type.comment << args_comment if args_comment
      type.comment << type_comment if type_comment

      @stats.add_method type

      container.add_method type

      set_visibility(container, typename, visibility_default, @@public_methods)

      if type_visibility
        type_visibility.gsub!(/\s/,'')
        type_visibility.gsub!(/\,/,'')
        type_visibility.gsub!(/:/,'')
        type_visibility.downcase!
        if type_visibility == "public"
          container.set_visibility_for([typename], :public)
        elsif type_visibility == "private"
          container.set_visibility_for([typename], :private)
        end
      end

      check_public_methods(type, container.name)

      if @options.show_all
        derived_types_comment << ", " unless derived_types_comment.empty?
        derived_types_comment << typename
      else
        if type.visibility == :public
        derived_types_comment << ", " unless derived_types_comment.empty?
        derived_types_comment << typename
        end
      end

    end

    if !derived_types_comment.empty?
      derived_types_table = 
        Attr.new("Derived Types", "Derived_Types", "", 
                 derived_types_comment)
      container.add_attribute(derived_types_table)
    end

    #
    # move interface scope
    #
    interface_code = ""
    while remaining_code =~ /^\s*?
                                 interface(
                                            \s+\w+                      |
                                            \s+operator\s*?\(.*?\)       |
                                            \s+assignment\s*?\(\s*?=\s*?\)
                                          )?\s*?$
                                 (.*?)
                                 ^\s*?end\s+interface.*?$
                            /mx
      interface_code << remove_empty_head_lines($&) + "\n"
      remaining_code = $~.pre_match
      remaining_code << $~.post_match
    end

    #
    # Parse global constants or variables in modules
    #
    const_var_defs = definition_info(before_contains_code)
    const_var_defs.each{|defitem|
      next if defitem.nodoc
      const_or_var_type = "Variable"
      const_or_var_progress = "v"
      if defitem.include_attr?("parameter")
        const_or_var_type = "Constant"
        const_or_var_progress = "c"
      end
      const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
      const_or_var.singleton = false
      const_or_var.params = ""
      self_comment = find_arguments([defitem.varname], before_contains_code)
      const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
      const_or_var.comment << self_comment if self_comment

      @stats.add_method const_or_var_progress

      container.add_method const_or_var

      set_visibility(container, defitem.varname, visibility_default, @@public_methods)

      if defitem.include_attr?("public")
        container.set_visibility_for([defitem.varname], :public)
      elsif defitem.include_attr?("private")
        container.set_visibility_for([defitem.varname], :private)
      end

      check_public_methods(const_or_var, container.name)

    } if const_var_defs

    remaining_lines = remaining_code.split("\n")

    # "subroutine" or "function" parts are parsed (new)
    #
    level_depth = 0
    block_searching_flag = nil
    block_searching_lines = []
    pre_comment = []
    procedure_trailing = ""
    procedure_name = ""
    procedure_params = ""
    procedure_prefix = ""
    procedure_result_arg = ""
    procedure_type = ""
    contains_lines = []
    contains_flag = nil
    remaining_lines.collect!{|line|
      if !block_searching_flag
        # subroutine
        if line =~ /^\s*?
                         (recursive|pure|elemental)?\s*?
                         subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
                   /x
          block_searching_flag = :subroutine
          block_searching_lines << line

          procedure_name = $2.chomp.strip
          procedure_params = $3 || ""
          procedure_prefix = $1 || ""
          procedure_trailing = $4 || "!"
          next false

        # function
        elsif line =~ /^\s*?
                       (recursive|pure|elemental)?\s*?
                       (
                           character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | type\s*?\([\w\s]+?\)\s+
                         | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | double\s+precision\s+
                         | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                       )?
                       function\s+(\w+)\s*?
                       (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
                      /x
          block_searching_flag = :function
          block_searching_lines << line

          procedure_prefix = $1 || ""
          procedure_type = $2 ? $2.chomp.strip : nil
          procedure_name = $8.chomp.strip
          procedure_params = $9 || ""
          procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
          procedure_trailing = $12 || "!"
          next false
        elsif line =~ /^\s*?!\s?(.*)/
          pre_comment << line
          next line
        else
          pre_comment = []
          next line
        end
      end
      contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
      block_searching_lines << line
      contains_lines << line if contains_flag

      level_depth += 1 if block_start?(line)
      level_depth -= 1 if block_end?(line)
      if level_depth >= 0
        next false
      end

      # "procedure_code" is formatted.
      # ":nodoc:" flag is checked.
      #
      procedure_code = block_searching_lines.join("\n")
      procedure_code = remove_empty_head_lines(procedure_code)
      if procedure_trailing =~ /^!:nodoc:/
        # next loop to search next block
        level_depth = 0
        block_searching_flag = nil
        block_searching_lines = []
        pre_comment = []
        procedure_trailing = ""
        procedure_name = ""
        procedure_params = ""
        procedure_prefix = ""
        procedure_result_arg = ""
        procedure_type = ""
        contains_lines = []
        contains_flag = nil
        next false
      end

      # AnyMethod is created, and added to container
      #
      subroutine_function = nil
      if block_searching_flag == :subroutine
        subroutine_prefix   = procedure_prefix
        subroutine_name     = procedure_name
        subroutine_params   = procedure_params
        subroutine_trailing = procedure_trailing
        subroutine_code     = procedure_code

        subroutine_comment = COMMENTS_ARE_UPPER ? 
          pre_comment.join("\n") + "\n" + subroutine_trailing : 
            subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/, '')
        subroutine = AnyMethod.new("subroutine", subroutine_name)
        parse_subprogram(subroutine, subroutine_params,
                         subroutine_comment, subroutine_code,
                         before_contains_code, nil, subroutine_prefix)

        @stats.add_method subroutine

        container.add_method subroutine
        subroutine_function = subroutine

      elsif block_searching_flag == :function
        function_prefix     = procedure_prefix
        function_type       = procedure_type
        function_name       = procedure_name
        function_params_org = procedure_params
        function_result_arg = procedure_result_arg
        function_trailing   = procedure_trailing
        function_code_org   = procedure_code

        function_comment = COMMENTS_ARE_UPPER ?
          pre_comment.join("\n") + "\n" + function_trailing :
            function_trailing + "\n " + function_code_org.sub(/^.*$\n/, '')

        function_code = "#{function_code_org}"
        if function_type
          function_code << "\n" + function_type + " :: " + function_result_arg
        end

        function_params =
          function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")

        function = AnyMethod.new("function", function_name)
        parse_subprogram(function, function_params,
                         function_comment, function_code,
                         before_contains_code, true, function_prefix)

        # Specific modification due to function
        function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
        function.params << " result(" + function_result_arg + ")"
        function.start_collecting_tokens
        function.add_token Token.new(1,1).set_text(function_code_org)

        @stats.add_method function

        container.add_method function
        subroutine_function = function

      end

      # The visibility of procedure is specified
      #
      set_visibility(container, procedure_name, 
                     visibility_default, @@public_methods)

      # The alias for this procedure from external modules
      #
      check_external_aliases(procedure_name,
                             subroutine_function.params,
                             subroutine_function.comment, subroutine_function) if external
      check_public_methods(subroutine_function, container.name)


      # contains_lines are parsed as private procedures
      if contains_flag
        parse_program_or_module(container,
                                contains_lines.join("\n"), :private)
      end

      # next loop to search next block
      level_depth = 0
      block_searching_flag = nil
      block_searching_lines = []
      pre_comment = []
      procedure_trailing = ""
      procedure_name = ""
      procedure_params = ""
      procedure_prefix = ""
      procedure_result_arg = ""
      contains_lines = []
      contains_flag = nil
      next false
    } # End of remaining_lines.collect!{|line|

    # Array remains_lines is converted to String remains_code again
    #
    remaining_code = remaining_lines.join("\n")

    #
    # Parse interface
    #
    interface_scope = false
    generic_name = ""
    interface_code.split("\n").each{ |line|
      if /^\s*?
               interface(
                          \s+\w+|
                          \s+operator\s*?\(.*?\)|
                          \s+assignment\s*?\(\s*?=\s*?\)
                        )?
               \s*?(!.*?)?$
         /x =~ line
        generic_name = $1 ? $1.strip.chomp : nil
        interface_trailing = $2 || "!"
        interface_scope = true
        interface_scope = false if interface_trailing =~ /!:nodoc:/
#        if generic_name =~ /operator\s*?\((.*?)\)/i
#          operator_name = $1
#          if operator_name && !operator_name.empty?
#            generic_name = "#{operator_name}"
#          end
#        end
#        if generic_name =~ /assignment\s*?\((.*?)\)/i
#          assignment_name = $1
#          if assignment_name && !assignment_name.empty?
#            generic_name = "#{assignment_name}"
#          end
#        end
      end
      if /^\s*?end\s+interface/ =~ line
        interface_scope = false
        generic_name = nil
      end
      # internal alias
      if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/ =~ line
        procedures = $1.strip.chomp
        procedures_trailing = $2 || "!"
        next if procedures_trailing =~ /!:nodoc:/
        procedures.split(",").each{ |proc|
          proc.strip!
          proc.chomp!
          next if generic_name == proc || !generic_name
          old_meth = container.find_symbol(proc, nil, @options.ignore_case)
          next if !old_meth
          nolink = old_meth.visibility == :private ? true : nil
          nolink = nil if @options.show_all
          new_meth = 
             initialize_external_method(generic_name, proc, 
                                        old_meth.params, nil, 
                                        old_meth.comment, 
                                        old_meth.clone.token_stream[0].text, 
                                        true, nolink)
          new_meth.singleton = old_meth.singleton

          @stats.add_method new_meth

          container.add_method new_meth

          set_visibility(container, generic_name, visibility_default, @@public_methods)

          check_public_methods(new_meth, container.name)

        }
      end

      # external aliases
      if interface_scope
        # subroutine
        proc = nil
        params = nil
        procedures_trailing = nil
        if line =~ /^\s*?
                         (recursive|pure|elemental)?\s*?
                         subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
                   /x
          proc = $2.chomp.strip
          generic_name = proc unless generic_name
          params = $3 || ""
          procedures_trailing = $4 || "!"

        # function
        elsif line =~ /^\s*?
                       (recursive|pure|elemental)?\s*?
                       (
                           character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | type\s*?\([\w\s]+?\)\s+
                         | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | double\s+precision\s+
                         | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                         | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
                       )?
                       function\s+(\w+)\s*?
                       (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
                      /x
          proc = $8.chomp.strip
          generic_name = proc unless generic_name
          params = $9 || ""
          procedures_trailing = $12 || "!"
        else
          next
        end
        next if procedures_trailing =~ /!:nodoc:/
        indicated_method = nil
        indicated_file   = nil
        TopLevel.all_files.each do |name, toplevel|
          indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
          indicated_file = name
          break if indicated_method
        end

        if indicated_method
          external_method = 
            initialize_external_method(generic_name, proc, 
                                       indicated_method.params, 
                                       indicated_file, 
                                       indicated_method.comment)

          @stats.add_method external_method

          container.add_method external_method
          set_visibility(container, generic_name, visibility_default, @@public_methods)
          if !container.include_requires?(indicated_file, @options.ignore_case)
            container.add_require(Require.new(indicated_file, ""))
          end
          check_public_methods(external_method, container.name)

        else
          @@external_aliases << {
            "new_name"  => generic_name,
            "old_name"  => proc,
            "file_or_module" => container,
            "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
          }
        end
      end

    } if interface_code # End of interface_code.split("\n").each ...

    #
    # Already imported methods are removed from @@public_methods.
    # Remainders are assumed to be imported from other modules.
    #
    @@public_methods.delete_if{ |method| method["entity_is_discovered"]}

    @@public_methods.each{ |pub_meth|
      next unless pub_meth["file_or_module"].name == container.name
      pub_meth["used_modules"].each{ |used_mod|
        TopLevel.all_classes_and_modules.each{ |modules|
          if modules.name == used_mod ||
              modules.name.upcase == used_mod.upcase &&
              @options.ignore_case
            modules.method_list.each{ |meth|
              if meth.name == pub_meth["name"] ||
                  meth.name.upcase == pub_meth["name"].upcase &&
                  @options.ignore_case
                new_meth = initialize_public_method(meth,
                                                    modules.name)
                if pub_meth["local_name"]
                  new_meth.name = pub_meth["local_name"]
                end

                @stats.add_method new_meth

                container.add_method new_meth
              end
            }
          end
        }
      }
    }

    container
  end
Register or log in to add new notes.