Class RDoc::Parser::F95
  1. lib/rdoc/parser/f95.rb
Parent: RDoc::Parser
ClassModule NormalModule AnonClass SingleClass NormalClass AnyMethod GhostMethod MetaMethod CodeObject Context Alias Attr Constant Require Include TopLevel RubyLex IRB RuntimeError Error Error Token TkUnknownChar TkVal TkNode TkOp TkId TkError TkOPASGN TkKW AttributeFormatter HtmlFormatter OverstrikeFormatter AnsiFormatter NamedThing AliasName IncludedModule Constant Attribute MethodSummary DefaultDisplay ClassEntry TopLevelEntry Formatter SimpleFormatter Description MethodDescription ModuleDescription ClassDescription HTML XML HTMLInOne CHM Method Context Class File Generator::MarkUp TEXINFO SimpleElement Port Element Node Subgraph Edge Digraph Stats Parser Options RDoc TemplatePage Markup Diagram NameDescriptor Cache Reader Writer Driver MethodEntry RI TexinfoTemplate AllReferences RubyToken Display Paths RI MarkUp Generator TokenStream DOT RDoc dot/f_5.png

Fortran95 RDoc Parser

Overview

This parser parses Fortran95 files with suffixes “f90”, “F90”, “f95” and “F95”. Fortran95 files are expected to be conformed to Fortran95 standards.

Rules

Fundamental rules are same as that of the Ruby parser. But comment markers are ’!’ not ’#’.

Correspondence between RDoc documentation and Fortran95 programs

F95 parses main programs, modules, subroutines, functions, derived-types, public variables, public constants, defined operators and defined assignments. These components are described in items of RDoc documentation, as follows.

Files :Files (same as Ruby)
Classes:Modules
Methods:Subroutines, functions, variables, constants, derived-types, defined operators, defined assignments
Required files:Files in which imported modules, external subroutines and external functions are defined.
Included Modules:List of imported modules
Attributes:List of derived-types, List of imported modules all of whose components are published again

Components listed in ‘Methods’ (subroutines, functions, …) defined in modules are described in the item of ‘Classes’. On the other hand, components defined in main programs or as external procedures are described in the item of ‘Files’.

Components parsed by default

By default, documentation on public components (subroutines, functions, variables, constants, derived-types, defined operators, defined assignments) are generated.

With “—all” option, documentation on all components are generated (almost same as the Ruby parser).

Information parsed automatically

The following information is automatically parsed.

  • Types of arguments
  • Types of variables and constants
  • Types of variables in the derived types, and initial values
  • NAMELISTs and types of variables in them, and initial values

Aliases by interface statement are described in the item of ‘Methods’.

Components which are imported from other modules and published again are described in the item of ‘Methods’.

Format of comment blocks

Comment blocks should be written as follows.

Comment blocks are considered to be ended when the line without ’!’ appears.

The indentation is not necessary.

  ! (Top of file)
  !
  ! Comment blocks for the files.
  !
  !--
  ! The comment described in the part enclosed by
  ! "!--" and "!++" is ignored.
  !++
  !
  module hogehoge
    !
    ! Comment blocks for the modules (or the programs).
    !

    private

    logical            :: a     ! a private variable
    real, public       :: b     ! a public variable
    integer, parameter :: c = 0 ! a public constant

    public :: c
    public :: MULTI_ARRAY
    public :: hoge, foo

    type MULTI_ARRAY
      !
      ! Comment blocks for the derived-types.
      !
      real, pointer :: var(:) =>null() ! Comments block for the variables.
      integer       :: num = 0
    end type MULTI_ARRAY

  contains

    subroutine hoge( in,   &   ! Comment blocks between continuation lines are ignored.
        &            out )
      !
      ! Comment blocks for the subroutines or functions
      !
      character(*),intent(in):: in ! Comment blocks for the arguments.
      character(*),intent(out),allocatable,target  :: in
                                   ! Comment blocks can be
                                   ! written under Fortran statements.

      character(32) :: file ! This comment parsed as a variable in below NAMELIST.
      integer       :: id

      namelist /varinfo_nml/ file, id
              !
              ! Comment blocks for the NAMELISTs.
              ! Information about variables are described above.
              !

    ....

    end subroutine hoge

    integer function foo( in )
      !
      ! This part is considered as comment block.

      ! Comment blocks under blank lines are ignored.
      !
      integer, intent(in):: inA ! This part is considered as comment block.

                                ! This part is ignored.

    end function foo

    subroutine hide( in,   &
      &              out )      !:nodoc:
      !
      ! If "!:nodoc:" is described at end-of-line in subroutine
      ! statement as above, the subroutine is ignored.
      ! This assignment can be used to modules, subroutines,
      ! functions, variables, constants, derived-types,
      ! defined operators, defined assignments,
      ! list of imported modules ("use" statement).
      !

    ....

    end subroutine hide

  end module hogehoge

Methods

public instance

  1. scan

Constants

COMMENTS_ARE_UPPER = false  
“false”:Comments are below source code
“true” :Comments are upper source code
INTERNAL_ALIAS_MES = "Alias for"   Internal alias message
EXTERNAL_ALIAS_MES = "The entity is"   External alias message

Public instance methods

scan ()

Define code constructs

[show source]
# File lib/rdoc/parser/f95.rb, line 201
  def scan
    # remove private comment
    remaining_code = remove_private_comments(@content)

    # continuation lines are united to one line
    remaining_code = united_to_one_line(remaining_code)

    # semicolons are replaced to line feed
    remaining_code = semicolon_to_linefeed(remaining_code)

    # collect comment for file entity
    whole_comment, remaining_code = collect_first_comment(remaining_code)
    @top_level.comment = whole_comment

    # String "remaining_code" is converted to Array "remaining_lines"
    remaining_lines = remaining_code.split("\n")

    # "module" or "program" parts are parsed (new)
    #
    level_depth = 0
    block_searching_flag = nil
    block_searching_lines = []
    pre_comment = []
    module_program_trailing = ""
    module_program_name = ""
    other_block_level_depth = 0
    other_block_searching_flag = nil
    remaining_lines.collect!{|line|
      if !block_searching_flag && !other_block_searching_flag
        if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
          block_searching_flag = :module
          block_searching_lines << line
          module_program_name = $1
          module_program_trailing = find_comments($2)
          next false
        elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
               line =~ /^\s*?\w/ && !block_start?(line)
          block_searching_flag = :program
          block_searching_lines << line
          module_program_name = $1 || ""
          module_program_trailing = find_comments($2)
          next false

        elsif block_start?(line)
          other_block_searching_flag = true
          next line

        elsif line =~ /^\s*?!\s?(.*)/
          pre_comment << line
          next line
        else
          pre_comment = []
          next line
        end
      elsif other_block_searching_flag
        other_block_level_depth += 1 if block_start?(line)
        other_block_level_depth -= 1 if block_end?(line)
        if other_block_level_depth < 0
          other_block_level_depth = 0
          other_block_searching_flag = nil
        end
        next line
      end

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

      # "module_program_code" is formatted.
      # ":nodoc:" flag is checked.
      #
      module_program_code = block_searching_lines.join("\n")
      module_program_code = remove_empty_head_lines(module_program_code)
      if module_program_trailing =~ /^:nodoc:/
        # next loop to search next block
        level_depth = 0
        block_searching_flag = false
        block_searching_lines = []
        pre_comment = []
        next false
      end

      # NormalClass is created, and added to @top_level
      #
      if block_searching_flag == :module
        module_name = module_program_name
        module_code = module_program_code
        module_trailing = module_program_trailing

        f9x_module = @top_level.add_module NormalClass, module_name
        f9x_module.record_location @top_level

        @stats.add_module f9x_module

        f9x_comment = COMMENTS_ARE_UPPER ? 
          find_comments(pre_comment.join("\n"))  + "\n" + module_trailing :
            module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
        f9x_module.comment = f9x_comment
        parse_program_or_module(f9x_module, module_code)

        TopLevel.all_files.each do |name, toplevel|
          if toplevel.include_includes?(module_name, @options.ignore_case)
            if !toplevel.include_requires?(@file_name, @options.ignore_case)
              toplevel.add_require(Require.new(@file_name, ""))
            end
          end
          toplevel.each_classmodule{|m|
            if m.include_includes?(module_name, @options.ignore_case)
              if !m.include_requires?(@file_name, @options.ignore_case)
                m.add_require(Require.new(@file_name, ""))
              end
            end
          }
        end
      elsif block_searching_flag == :program
        program_name = module_program_name
        program_code = module_program_code
        program_trailing = module_program_trailing
        # progress "p" # HACK what stats thingy does this correspond to?
        program_comment = COMMENTS_ARE_UPPER ? 
          find_comments(pre_comment.join("\n")) + "\n" + program_trailing : 
            program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
        program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
                          + program_comment
        @top_level.comment << program_comment
        parse_program_or_module(@top_level, program_code, :private)
      end

      # next loop to search next block
      level_depth = 0
      block_searching_flag = false
      block_searching_lines = []
      pre_comment = []
      next false
    }

    remaining_lines.delete_if{ |line|
      line == false
    }

    # External subprograms and functions are parsed
    #
    parse_program_or_module(@top_level, remaining_lines.join("\n"),
                            :public, true)

    @top_level
  end