summaryrefslogtreecommitdiff
path: root/lib/rdoc/parsers/parse_f95.rb
blob: 518e421c60f7f8774a14658ba007256b305fd371 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# Parse a Fortran 95 file.

require "rdoc/code_objects"

module RDoc

  # See rdoc/parsers/parse_f95.rb
 
  class Token

    NO_TEXT = "??".freeze
    
    def initialize(line_no, char_no)
      @line_no = line_no
      @char_no = char_no
      @text    = NO_TEXT
    end
    # Because we're used in contexts that expect to return a token,
    # we set the text string and then return ourselves
    def set_text(text)
      @text = text
      self
    end

    attr_reader :line_no, :char_no, :text

  end

  class Fortran95parser

    extend ParserFactory
    parse_files_matching(/\.(f9(0|5)|F)$/)
    
    # prepare to parse a Fortran 95 file
    def initialize(top_level, file_name, body, options, stats)
      @body = body
      @stats = stats
      @options = options
      @top_level = top_level
      @progress = $stderr unless options.quiet
    end
    
    # devine code constructs
    def scan

      # modules and programs
      if @body =~ /^(module|program)\s+(\w+)/i
	progress "m"
	f9x_module = @top_level.add_module NormalClass, $2
	f9x_module.record_location @top_level
	first_comment, second_comment = $`.gsub(/^!\s?/,"").split "\n\s*\n"
	if second_comment
	  @top_level.comment = first_comment if first_comment
	  f9x_module.comment = second_comment
	else
	  f9x_module.comment = first_comment if first_comment
	end
      end

      # use modules
      remaining_code = @body
      while remaining_code =~ /^\s*use\s+(\w+)/i
	remaining_code = $~.post_match
	progress "."
	f9x_module.add_include Include.new($1, "") if f9x_module
      end

      # subroutines
      remaining_code = @body
      while remaining_code =~ /^\s*subroutine\s+(\w+)\s*\((.*?)\)/im
	remaining_code = $~.post_match
        subroutine = AnyMethod.new("Text", $1)
	subroutine.singleton = false

        prematchText = $~.pre_match
        params = $2
        params.gsub!(/&/,'')
	subroutine.params = params
        comment = find_comments prematchText
	subroutine.comment = comment if comment

        subroutine.start_collecting_tokens
        remaining_code =~ /^\s*end\s+subroutine/i
        code = "subroutine #{subroutine.name} (#{subroutine.params})\n"
        code += $~.pre_match
        code += "\nend subroutine\n"
        subroutine.add_token Token.new(1,1).set_text(code)
        
        progress "s"
        f9x_module.add_method subroutine if f9x_module
      end

      @top_level

    end

    def find_comments text
      lines = text.split("\n").reverse
      comment_block = Array.new
      lines.each do |line|
	break if line =~ /^\s*\w/
        comment_block.unshift line.sub(/^!\s?/,"")
      end
      nice_lines = comment_block.join("\n").split "\n\s*\n"
      nice_lines.shift
      nice_lines.shift
      nice_lines.shift
    end

    def progress(char)
      unless @options.quiet
        @progress.print(char)
        @progress.flush
      end
    end

  end # class Fortran95parser

end # module RDoc