~ubuntu-branches/ubuntu/intrepid/ruby1.8/intrepid-updates

« back to all changes in this revision

Viewing changes to lib/rdoc/parsers/parse_f95.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#= parse_f95.rb - Fortran95 Parser
 
2
#
 
3
#== Overview
 
4
#
 
5
#"parse_f95.rb" parses Fortran95 files with suffixes "f90", "F90", "f95"
 
6
#and "F95". Fortran95 files are expected to be conformed to Fortran95
 
7
#standards.
 
8
#
 
9
#== Rules
 
10
#
 
11
#Fundamental rules are same as that of the Ruby parser.
 
12
#But comment markers are '!' not '#'.
 
13
#
 
14
#=== Correspondence between RDoc documentation and Fortran95 programs
 
15
#
 
16
#"parse_f95.rb" parses main programs, modules, subroutines, functions,
 
17
#derived-types, public variables, public constants,
 
18
#defined operators and defined assignments.
 
19
#These components are described in items of RDoc documentation, as follows.
 
20
#
 
21
#Files :: Files (same as Ruby)
 
22
#Classes :: Modules
 
23
#Methods :: Subroutines, functions, variables, constants, derived-types, defined operators, defined assignments
 
24
#Required files :: Files in which imported modules, external subroutines and external functions are defined.
 
25
#Included Modules :: List of imported modules
 
26
#Attributes :: List of derived-types, List of imported modules all of whose components are published again
 
27
#
 
28
#Components listed in 'Methods' (subroutines, functions, ...)
 
29
#defined in modules are described in the item of 'Classes'.
 
30
#On the other hand, components defined in main programs or
 
31
#as external procedures are described in the item of 'Files'.
 
32
#
 
33
#=== Components parsed by default
 
34
#
 
35
#By default, documentation on public components (subroutines, functions, 
 
36
#variables, constants, derived-types, defined operators, 
 
37
#defined assignments) are generated. 
 
38
#With "--all" option, documentation on all components
 
39
#are generated (almost same as the Ruby parser).
 
40
#
 
41
#=== Information parsed automatically
 
42
#
 
43
#The following information is automatically parsed.
 
44
#
 
45
#* Types of arguments
 
46
#* Types of variables and constants
 
47
#* Types of variables in the derived types, and initial values
 
48
#* NAMELISTs and types of variables in them, and initial values
 
49
#
 
50
#Aliases by interface statement are described in the item of 'Methods'.
 
51
#
 
52
#Components which are imported from other modules and published again 
 
53
#are described in the item of 'Methods'.
 
54
#
 
55
#=== Format of comment blocks
 
56
#
 
57
#Comment blocks should be written as follows.
 
58
#Comment blocks are considered to be ended when the line without '!'
 
59
#appears.
 
60
#The indentation is not necessary.
 
61
#
 
62
#     ! (Top of file)
 
63
#     !
 
64
#     ! Comment blocks for the files.
 
65
#     !
 
66
#     !--
 
67
#     ! The comment described in the part enclosed by
 
68
#     ! "!--" and "!++" is ignored.
 
69
#     !++
 
70
#     !
 
71
#     module hogehoge
 
72
#       !
 
73
#       ! Comment blocks for the modules (or the programs).
 
74
#       !
 
75
#
 
76
#       private
 
77
#
 
78
#       logical            :: a     ! a private variable
 
79
#       real, public       :: b     ! a public variable
 
80
#       integer, parameter :: c = 0 ! a public constant
 
81
#
 
82
#       public :: c
 
83
#       public :: MULTI_ARRAY
 
84
#       public :: hoge, foo
 
85
#
 
86
#       type MULTI_ARRAY
 
87
#         !
 
88
#         ! Comment blocks for the derived-types.
 
89
#         !
 
90
#         real, pointer :: var(:) =>null() ! Comments block for the variables.
 
91
#         integer       :: num = 0
 
92
#       end type MULTI_ARRAY
 
93
#
 
94
#     contains
 
95
#
 
96
#       subroutine hoge( in,   &   ! Comment blocks between continuation lines are ignored.
 
97
#           &            out )
 
98
#         !
 
99
#         ! Comment blocks for the subroutines or functions
 
100
#         !
 
101
#         character(*),intent(in):: in ! Comment blocks for the arguments.
 
102
#         character(*),intent(out),allocatable,target  :: in
 
103
#                                      ! Comment blocks can be
 
104
#                                      ! written under Fortran statements.
 
105
#
 
106
#         character(32) :: file ! This comment parsed as a variable in below NAMELIST.
 
107
#         integer       :: id
 
108
#
 
109
#         namelist /varinfo_nml/ file, id
 
110
#                 !
 
111
#                 ! Comment blocks for the NAMELISTs.
 
112
#                 ! Information about variables are described above.
 
113
#                 !
 
114
#
 
115
#       ....
 
116
#
 
117
#       end subroutine hoge
 
118
#
 
119
#       integer function foo( in )
 
120
#         !
 
121
#         ! This part is considered as comment block.
 
122
#
 
123
#         ! Comment blocks under blank lines are ignored.
 
124
#         !
 
125
#         integer, intent(in):: inA ! This part is considered as comment block.
 
126
#
 
127
#                                   ! This part is ignored.
 
128
#
 
129
#       end function foo
 
130
#
 
131
#       subroutine hide( in,   &
 
132
#         &              out )      !:nodoc:
 
133
#         !
 
134
#         ! If "!:nodoc:" is described at end-of-line in subroutine
 
135
#         ! statement as above, the subroutine is ignored.
 
136
#         ! This assignment can be used to modules, subroutines,
 
137
#         ! functions, variables, constants, derived-types,
 
138
#         ! defined operators, defined assignments,
 
139
#         ! list of imported modules ("use" statement).
 
140
#         !
 
141
#
 
142
#       ....
 
143
#
 
144
#       end subroutine hide
 
145
#
 
146
#     end module hogehoge
 
147
#
 
148
 
 
149
 
 
150
require "rdoc/code_objects"
 
151
 
 
152
module RDoc
 
153
 
 
154
  class Token
 
155
 
 
156
    NO_TEXT = "??".freeze
 
157
 
 
158
    def initialize(line_no, char_no)
 
159
      @line_no = line_no
 
160
      @char_no = char_no
 
161
      @text    = NO_TEXT
 
162
    end
 
163
    # Because we're used in contexts that expect to return a token,
 
164
    # we set the text string and then return ourselves
 
165
    def set_text(text)
 
166
      @text = text
 
167
      self
 
168
    end
 
169
 
 
170
    attr_reader :line_no, :char_no, :text
 
171
 
 
172
  end
 
173
 
 
174
  # See rdoc/parsers/parse_f95.rb
 
175
 
 
176
  class Fortran95parser
 
177
 
 
178
    extend ParserFactory
 
179
    parse_files_matching(/\.((f|F)9(0|5)|F)$/)
 
180
 
 
181
    @@external_aliases = []
 
182
    @@public_methods   = []
 
183
 
 
184
    # "false":: Comments are below source code
 
185
    # "true" :: Comments are upper source code
 
186
    COMMENTS_ARE_UPPER  = false
 
187
 
 
188
    # Internal alias message
 
189
    INTERNAL_ALIAS_MES = "Alias for"
 
190
 
 
191
    # External alias message
 
192
    EXTERNAL_ALIAS_MES = "The entity is"
 
193
 
 
194
    # prepare to parse a Fortran 95 file
 
195
    def initialize(top_level, file_name, body, options, stats)
 
196
      @body = body
 
197
      @stats = stats
 
198
      @file_name  = file_name
 
199
      @options = options
 
200
      @top_level = top_level
 
201
      @progress = $stderr unless options.quiet
 
202
    end
 
203
 
 
204
    # devine code constructs
 
205
    def scan
 
206
 
 
207
      # remove private comment
 
208
      remaining_code = remove_private_comments(@body)
 
209
 
 
210
      # continuation lines are united to one line
 
211
      remaining_code = united_to_one_line(remaining_code)
 
212
 
 
213
      # semicolons are replaced to line feed
 
214
      remaining_code = semicolon_to_linefeed(remaining_code)
 
215
 
 
216
      # collect comment for file entity
 
217
      whole_comment, remaining_code = collect_first_comment(remaining_code)
 
218
      @top_level.comment = whole_comment
 
219
 
 
220
      # String "remaining_code" is converted to Array "remaining_lines"
 
221
      remaining_lines = remaining_code.split("\n")
 
222
 
 
223
      # "module" or "program" parts are parsed (new)
 
224
      #
 
225
      level_depth = 0
 
226
      block_searching_flag = nil
 
227
      block_searching_lines = []
 
228
      pre_comment = []
 
229
      module_program_trailing = ""
 
230
      module_program_name = ""
 
231
      other_block_level_depth = 0
 
232
      other_block_searching_flag = nil
 
233
      remaining_lines.collect!{|line|
 
234
        if !block_searching_flag && !other_block_searching_flag
 
235
          if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
 
236
            block_searching_flag = :module
 
237
            block_searching_lines << line
 
238
            module_program_name = $1
 
239
            module_program_trailing = find_comments($2)
 
240
            next false
 
241
          elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
 
242
                 line =~ /^\s*?\w/ && !block_start?(line)
 
243
            block_searching_flag = :program
 
244
            block_searching_lines << line
 
245
            module_program_name = $1 || ""
 
246
            module_program_trailing = find_comments($2)
 
247
            next false
 
248
 
 
249
          elsif block_start?(line)
 
250
            other_block_searching_flag = true
 
251
            next line
 
252
 
 
253
          elsif line =~ /^\s*?!\s?(.*)/
 
254
            pre_comment << line
 
255
            next line
 
256
          else
 
257
            pre_comment = []
 
258
            next line
 
259
          end
 
260
        elsif other_block_searching_flag
 
261
          other_block_level_depth += 1 if block_start?(line)
 
262
          other_block_level_depth -= 1 if block_end?(line)
 
263
          if other_block_level_depth < 0
 
264
            other_block_level_depth = 0
 
265
            other_block_searching_flag = nil
 
266
          end
 
267
          next line
 
268
        end
 
269
 
 
270
        block_searching_lines << line
 
271
        level_depth += 1 if block_start?(line)
 
272
        level_depth -= 1 if block_end?(line)
 
273
        if level_depth >= 0
 
274
          next false
 
275
        end
 
276
 
 
277
        # "module_program_code" is formatted.
 
278
        # ":nodoc:" flag is checked.
 
279
        #
 
280
        module_program_code = block_searching_lines.join("\n")
 
281
        module_program_code = remove_empty_head_lines(module_program_code)
 
282
        if module_program_trailing =~ /^:nodoc:/
 
283
          # next loop to search next block
 
284
          level_depth = 0
 
285
          block_searching_flag = false
 
286
          block_searching_lines = []
 
287
          pre_comment = []
 
288
          next false
 
289
        end
 
290
 
 
291
        # NormalClass is created, and added to @top_level
 
292
        #
 
293
        if block_searching_flag == :module
 
294
          module_name = module_program_name
 
295
          module_code = module_program_code
 
296
          module_trailing = module_program_trailing
 
297
          progress "m"
 
298
          @stats.num_modules += 1
 
299
          f9x_module = @top_level.add_module NormalClass, module_name
 
300
          f9x_module.record_location @top_level
 
301
 
 
302
          f9x_comment = COMMENTS_ARE_UPPER ? 
 
303
            find_comments(pre_comment.join("\n"))  + "\n" + module_trailing :
 
304
              module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
 
305
          f9x_module.comment = f9x_comment
 
306
          parse_program_or_module(f9x_module, module_code)
 
307
 
 
308
          TopLevel.all_files.each do |name, toplevel|
 
309
            if toplevel.include_includes?(module_name, @options.ignore_case)
 
310
              if !toplevel.include_requires?(@file_name, @options.ignore_case)
 
311
                toplevel.add_require(Require.new(@file_name, ""))
 
312
              end
 
313
            end
 
314
            toplevel.each_classmodule{|m|
 
315
              if m.include_includes?(module_name, @options.ignore_case)
 
316
                if !m.include_requires?(@file_name, @options.ignore_case)
 
317
                  m.add_require(Require.new(@file_name, ""))
 
318
                end
 
319
              end
 
320
            }
 
321
          end
 
322
        elsif block_searching_flag == :program
 
323
          program_name = module_program_name
 
324
          program_code = module_program_code
 
325
          program_trailing = module_program_trailing
 
326
          progress "p"
 
327
          program_comment = COMMENTS_ARE_UPPER ? 
 
328
            find_comments(pre_comment.join("\n")) + "\n" + program_trailing : 
 
329
              program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
 
330
          program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
 
331
                            + program_comment
 
332
          @top_level.comment << program_comment
 
333
          parse_program_or_module(@top_level, program_code, :private)
 
334
        end
 
335
 
 
336
        # next loop to search next block
 
337
        level_depth = 0
 
338
        block_searching_flag = false
 
339
        block_searching_lines = []
 
340
        pre_comment = []
 
341
        next false
 
342
      }
 
343
 
 
344
      remaining_lines.delete_if{ |line|
 
345
        line == false
 
346
      }
 
347
 
 
348
      # External subprograms and functions are parsed
 
349
      #
 
350
      parse_program_or_module(@top_level, remaining_lines.join("\n"),
 
351
                              :public, true)
 
352
 
 
353
      @top_level
 
354
    end  # End of scan
 
355
 
 
356
    private
 
357
 
 
358
    def parse_program_or_module(container, code,
 
359
                                visibility=:public, external=nil)
 
360
      return unless container
 
361
      return unless code
 
362
      remaining_lines = code.split("\n")
 
363
      remaining_code = "#{code}"
 
364
 
 
365
      #
 
366
      # Parse variables before "contains" in module
 
367
      #
 
368
      level_depth = 0
 
369
      before_contains_lines = []
 
370
      before_contains_code = nil
 
371
      before_contains_flag = nil
 
372
      remaining_lines.each{ |line|
 
373
        if !before_contains_flag
 
374
          if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
 
375
            before_contains_flag = true
 
376
          end
 
377
        else
 
378
          break if line =~ /^\s*?contains\s*?(!.*?)?$/i
 
379
          level_depth += 1 if block_start?(line)
 
380
          level_depth -= 1 if block_end?(line)
 
381
          break if level_depth < 0
 
382
          before_contains_lines << line
 
383
        end
 
384
      }
 
385
      before_contains_code = before_contains_lines.join("\n")
 
386
      if before_contains_code
 
387
        before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
 
388
        before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
 
389
      end
 
390
 
 
391
      #
 
392
      # Parse global "use"
 
393
      #
 
394
      use_check_code = "#{before_contains_code}"
 
395
      cascaded_modules_list = []
 
396
      while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
 
397
        use_check_code = $~.pre_match
 
398
        use_check_code << $~.post_match
 
399
        used_mod_name = $1.strip.chomp
 
400
        used_list = $2 || ""
 
401
        used_trailing = $3 || ""
 
402
        next if used_trailing =~ /!:nodoc:/
 
403
        if !container.include_includes?(used_mod_name, @options.ignore_case)
 
404
          progress "."
 
405
          container.add_include Include.new(used_mod_name, "")
 
406
        end
 
407
        if ! (used_list =~ /\,\s*?only\s*?:/i )
 
408
          cascaded_modules_list << "\#" + used_mod_name
 
409
        end
 
410
      end
 
411
 
 
412
      #
 
413
      # Parse public and private, and store information.
 
414
      # This information is used when "add_method" and
 
415
      # "set_visibility_for" are called.
 
416
      #
 
417
      visibility_default, visibility_info = 
 
418
                parse_visibility(remaining_lines.join("\n"), visibility, container)
 
419
      @@public_methods.concat visibility_info
 
420
      if visibility_default == :public
 
421
        if !cascaded_modules_list.empty?
 
422
          cascaded_modules = 
 
423
            Attr.new("Cascaded Modules",
 
424
                     "Imported modules all of whose components are published again",
 
425
                     "",
 
426
                     cascaded_modules_list.join(", "))
 
427
          container.add_attribute(cascaded_modules)
 
428
        end
 
429
      end
 
430
 
 
431
      #
 
432
      # Check rename elements
 
433
      #
 
434
      use_check_code = "#{before_contains_code}"
 
435
      while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
 
436
        use_check_code = $~.pre_match
 
437
        use_check_code << $~.post_match
 
438
        used_mod_name = $1.strip.chomp
 
439
        used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
 
440
        used_elements.split(",").each{ |used|
 
441
          if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
 
442
            local = $1
 
443
            org = $2
 
444
            @@public_methods.collect!{ |pub_meth|
 
445
              if local == pub_meth["name"] ||
 
446
                  local.upcase == pub_meth["name"].upcase &&
 
447
                  @options.ignore_case
 
448
                pub_meth["name"] = org
 
449
                pub_meth["local_name"] = local
 
450
              end
 
451
              pub_meth
 
452
            }
 
453
          end
 
454
        }
 
455
      end
 
456
 
 
457
      #
 
458
      # Parse private "use"
 
459
      #
 
460
      use_check_code = remaining_lines.join("\n")
 
461
      while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
 
462
        use_check_code = $~.pre_match
 
463
        use_check_code << $~.post_match
 
464
        used_mod_name = $1.strip.chomp
 
465
        used_trailing = $3 || ""
 
466
        next if used_trailing =~ /!:nodoc:/
 
467
        if !container.include_includes?(used_mod_name, @options.ignore_case)
 
468
          progress "."
 
469
          container.add_include Include.new(used_mod_name, "")
 
470
        end
 
471
      end
 
472
 
 
473
      container.each_includes{ |inc|
 
474
        TopLevel.all_files.each do |name, toplevel|
 
475
          indicated_mod = toplevel.find_symbol(inc.name,
 
476
                                               nil, @options.ignore_case)
 
477
          if indicated_mod
 
478
            indicated_name = indicated_mod.parent.file_relative_name
 
479
            if !container.include_requires?(indicated_name, @options.ignore_case)
 
480
              container.add_require(Require.new(indicated_name, ""))
 
481
            end
 
482
            break
 
483
          end
 
484
        end
 
485
      }
 
486
 
 
487
      #
 
488
      # Parse derived-types definitions
 
489
      #
 
490
      derived_types_comment = ""
 
491
      remaining_code = remaining_lines.join("\n")
 
492
      while remaining_code =~ /^\s*?
 
493
                                    type[\s\,]+(public|private)?\s*?(::)?\s*?
 
494
                                    (\w+)\s*?(!.*?)?$
 
495
                                    (.*?)
 
496
                                    ^\s*?end\s+type.*?$
 
497
                              /imx
 
498
        remaining_code = $~.pre_match
 
499
        remaining_code << $~.post_match
 
500
        typename = $3.chomp.strip
 
501
        type_elements = $5 || ""
 
502
        type_code = remove_empty_head_lines($&)
 
503
        type_trailing = find_comments($4)
 
504
        next if type_trailing =~ /^:nodoc:/
 
505
        type_visibility = $1
 
506
        type_comment = COMMENTS_ARE_UPPER ? 
 
507
          find_comments($~.pre_match) + "\n" + type_trailing :
 
508
            type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
 
509
        type_element_visibility_public = true
 
510
        type_code.split("\n").each{ |line|
 
511
          if /^\s*?private\s*?$/ =~ line
 
512
            type_element_visibility_public = nil
 
513
            break
 
514
          end
 
515
        } if type_code
 
516
 
 
517
        args_comment = ""
 
518
        type_args_info = nil
 
519
 
 
520
        if @options.show_all
 
521
          args_comment = find_arguments(nil, type_code, true)
 
522
        else
 
523
          type_public_args_list = []
 
524
          type_args_info = definition_info(type_code)
 
525
          type_args_info.each{ |arg|
 
526
            arg_is_public = type_element_visibility_public
 
527
            arg_is_public = true if arg.include_attr?("public")
 
528
            arg_is_public = nil if arg.include_attr?("private")
 
529
            type_public_args_list << arg.varname if arg_is_public
 
530
          }
 
531
          args_comment = find_arguments(type_public_args_list, type_code)
 
532
        end
 
533
 
 
534
        type = AnyMethod.new("type #{typename}", typename)
 
535
        type.singleton = false
 
536
        type.params = ""
 
537
        type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
 
538
        type.comment << args_comment if args_comment
 
539
        type.comment << type_comment if type_comment
 
540
        progress "t"
 
541
        @stats.num_methods += 1
 
542
        container.add_method type
 
543
 
 
544
        set_visibility(container, typename, visibility_default, @@public_methods)
 
545
 
 
546
        if type_visibility
 
547
          type_visibility.gsub!(/\s/,'')
 
548
          type_visibility.gsub!(/\,/,'')
 
549
          type_visibility.gsub!(/:/,'')
 
550
          type_visibility.downcase!
 
551
          if type_visibility == "public"
 
552
            container.set_visibility_for([typename], :public)
 
553
          elsif type_visibility == "private"
 
554
            container.set_visibility_for([typename], :private)
 
555
          end
 
556
        end
 
557
 
 
558
        check_public_methods(type, container.name)
 
559
 
 
560
        if @options.show_all
 
561
          derived_types_comment << ", " unless derived_types_comment.empty?
 
562
          derived_types_comment << typename
 
563
        else
 
564
          if type.visibility == :public
 
565
          derived_types_comment << ", " unless derived_types_comment.empty?
 
566
          derived_types_comment << typename
 
567
          end
 
568
        end
 
569
 
 
570
      end
 
571
 
 
572
      if !derived_types_comment.empty?
 
573
        derived_types_table = 
 
574
          Attr.new("Derived Types", "Derived_Types", "", 
 
575
                   derived_types_comment)
 
576
        container.add_attribute(derived_types_table)
 
577
      end
 
578
 
 
579
      #
 
580
      # move interface scope
 
581
      #
 
582
      interface_code = ""
 
583
      while remaining_code =~ /^\s*?
 
584
                                   interface(
 
585
                                              \s+\w+                      |
 
586
                                              \s+operator\s*?\(.*?\)       |
 
587
                                              \s+assignment\s*?\(\s*?=\s*?\)
 
588
                                            )?\s*?$
 
589
                                   (.*?)
 
590
                                   ^\s*?end\s+interface.*?$
 
591
                              /imx
 
592
        interface_code << remove_empty_head_lines($&) + "\n"
 
593
        remaining_code = $~.pre_match
 
594
        remaining_code << $~.post_match
 
595
      end
 
596
 
 
597
      #
 
598
      # Parse global constants or variables in modules
 
599
      #
 
600
      const_var_defs = definition_info(before_contains_code)
 
601
      const_var_defs.each{|defitem|
 
602
        next if defitem.nodoc
 
603
        const_or_var_type = "Variable"
 
604
        const_or_var_progress = "v"
 
605
        if defitem.include_attr?("parameter")
 
606
          const_or_var_type = "Constant"
 
607
          const_or_var_progress = "c"
 
608
        end
 
609
        const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
 
610
        const_or_var.singleton = false
 
611
        const_or_var.params = ""
 
612
        self_comment = find_arguments([defitem.varname], before_contains_code)
 
613
        const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
 
614
        const_or_var.comment << self_comment if self_comment
 
615
        progress const_or_var_progress
 
616
        @stats.num_methods += 1
 
617
        container.add_method const_or_var
 
618
 
 
619
        set_visibility(container, defitem.varname, visibility_default, @@public_methods)
 
620
 
 
621
        if defitem.include_attr?("public")
 
622
          container.set_visibility_for([defitem.varname], :public)
 
623
        elsif defitem.include_attr?("private")
 
624
          container.set_visibility_for([defitem.varname], :private)
 
625
        end
 
626
 
 
627
        check_public_methods(const_or_var, container.name)
 
628
 
 
629
      } if const_var_defs
 
630
 
 
631
      remaining_lines = remaining_code.split("\n")
 
632
 
 
633
      # "subroutine" or "function" parts are parsed (new)
 
634
      #
 
635
      level_depth = 0
 
636
      block_searching_flag = nil
 
637
      block_searching_lines = []
 
638
      pre_comment = []
 
639
      procedure_trailing = ""
 
640
      procedure_name = ""
 
641
      procedure_params = ""
 
642
      procedure_prefix = ""
 
643
      procedure_result_arg = ""
 
644
      procedure_type = ""
 
645
      contains_lines = []
 
646
      contains_flag = nil
 
647
      remaining_lines.collect!{|line|
 
648
        if !block_searching_flag
 
649
          # subroutine
 
650
          if line =~ /^\s*?
 
651
                           (recursive|pure|elemental)?\s*?
 
652
                           subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
 
653
                     /ix
 
654
            block_searching_flag = :subroutine
 
655
            block_searching_lines << line
 
656
 
 
657
            procedure_name = $2.chomp.strip
 
658
            procedure_params = $3 || ""
 
659
            procedure_prefix = $1 || ""
 
660
            procedure_trailing = $4 || "!"
 
661
            next false
 
662
 
 
663
          # function
 
664
          elsif line =~ /^\s*?
 
665
                         (recursive|pure|elemental)?\s*?
 
666
                         (
 
667
                             character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
668
                           | type\s*?\([\w\s]+?\)\s+
 
669
                           | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
670
                           | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
671
                           | double\s+precision\s+
 
672
                           | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
673
                           | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
674
                         )?
 
675
                         function\s+(\w+)\s*?
 
676
                         (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
 
677
                        /ix
 
678
            block_searching_flag = :function
 
679
            block_searching_lines << line
 
680
 
 
681
            procedure_prefix = $1 || ""
 
682
            procedure_type = $2 ? $2.chomp.strip : nil
 
683
            procedure_name = $8.chomp.strip
 
684
            procedure_params = $9 || ""
 
685
            procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
 
686
            procedure_trailing = $12 || "!"
 
687
            next false
 
688
          elsif line =~ /^\s*?!\s?(.*)/
 
689
            pre_comment << line
 
690
            next line
 
691
          else
 
692
            pre_comment = []
 
693
            next line
 
694
          end
 
695
        end
 
696
        contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
 
697
        block_searching_lines << line
 
698
        contains_lines << line if contains_flag
 
699
 
 
700
        level_depth += 1 if block_start?(line)
 
701
        level_depth -= 1 if block_end?(line)
 
702
        if level_depth >= 0
 
703
          next false
 
704
        end
 
705
 
 
706
        # "procedure_code" is formatted.
 
707
        # ":nodoc:" flag is checked.
 
708
        #
 
709
        procedure_code = block_searching_lines.join("\n")
 
710
        procedure_code = remove_empty_head_lines(procedure_code)
 
711
        if procedure_trailing =~ /^!:nodoc:/
 
712
          # next loop to search next block
 
713
          level_depth = 0
 
714
          block_searching_flag = nil
 
715
          block_searching_lines = []
 
716
          pre_comment = []
 
717
          procedure_trailing = ""
 
718
          procedure_name = ""
 
719
          procedure_params = ""
 
720
          procedure_prefix = ""
 
721
          procedure_result_arg = ""
 
722
          procedure_type = ""
 
723
          contains_lines = []
 
724
          contains_flag = nil
 
725
          next false
 
726
        end
 
727
 
 
728
        # AnyMethod is created, and added to container
 
729
        #
 
730
        subroutine_function = nil
 
731
        if block_searching_flag == :subroutine
 
732
          subroutine_prefix   = procedure_prefix
 
733
          subroutine_name     = procedure_name
 
734
          subroutine_params   = procedure_params
 
735
          subroutine_trailing = procedure_trailing
 
736
          subroutine_code     = procedure_code
 
737
 
 
738
          subroutine_comment = COMMENTS_ARE_UPPER ? 
 
739
            pre_comment.join("\n") + "\n" + subroutine_trailing : 
 
740
              subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
 
741
          subroutine = AnyMethod.new("subroutine", subroutine_name)
 
742
          parse_subprogram(subroutine, subroutine_params,
 
743
                           subroutine_comment, subroutine_code,
 
744
                           before_contains_code, nil, subroutine_prefix)
 
745
          progress "s"
 
746
          @stats.num_methods += 1
 
747
          container.add_method subroutine
 
748
          subroutine_function = subroutine
 
749
 
 
750
        elsif block_searching_flag == :function
 
751
          function_prefix     = procedure_prefix
 
752
          function_type       = procedure_type
 
753
          function_name       = procedure_name
 
754
          function_params_org = procedure_params
 
755
          function_result_arg = procedure_result_arg
 
756
          function_trailing   = procedure_trailing
 
757
          function_code_org   = procedure_code
 
758
 
 
759
          function_comment = COMMENTS_ARE_UPPER ?
 
760
            pre_comment.join("\n") + "\n" + function_trailing :
 
761
              function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
 
762
 
 
763
          function_code = "#{function_code_org}"
 
764
          if function_type
 
765
            function_code << "\n" + function_type + " :: " + function_result_arg
 
766
          end
 
767
 
 
768
          function_params =
 
769
            function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
 
770
 
 
771
          function = AnyMethod.new("function", function_name)
 
772
          parse_subprogram(function, function_params,
 
773
                           function_comment, function_code,
 
774
                           before_contains_code, true, function_prefix)
 
775
 
 
776
          # Specific modification due to function
 
777
          function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
 
778
          function.params << " result(" + function_result_arg + ")"
 
779
          function.start_collecting_tokens
 
780
          function.add_token Token.new(1,1).set_text(function_code_org)
 
781
 
 
782
          progress "f"
 
783
          @stats.num_methods += 1
 
784
          container.add_method function
 
785
          subroutine_function = function
 
786
 
 
787
        end
 
788
 
 
789
        # The visibility of procedure is specified
 
790
        #
 
791
        set_visibility(container, procedure_name, 
 
792
                       visibility_default, @@public_methods)
 
793
 
 
794
        # The alias for this procedure from external modules
 
795
        #
 
796
        check_external_aliases(procedure_name,
 
797
                               subroutine_function.params,
 
798
                               subroutine_function.comment, subroutine_function) if external
 
799
        check_public_methods(subroutine_function, container.name)
 
800
 
 
801
 
 
802
        # contains_lines are parsed as private procedures
 
803
        if contains_flag
 
804
          parse_program_or_module(container,
 
805
                                  contains_lines.join("\n"), :private)
 
806
        end
 
807
 
 
808
        # next loop to search next block
 
809
        level_depth = 0
 
810
        block_searching_flag = nil
 
811
        block_searching_lines = []
 
812
        pre_comment = []
 
813
        procedure_trailing = ""
 
814
        procedure_name = ""
 
815
        procedure_params = ""
 
816
        procedure_prefix = ""
 
817
        procedure_result_arg = ""
 
818
        contains_lines = []
 
819
        contains_flag = nil
 
820
        next false
 
821
      } # End of remaining_lines.collect!{|line|
 
822
 
 
823
      # Array remains_lines is converted to String remains_code again
 
824
      #
 
825
      remaining_code = remaining_lines.join("\n")
 
826
 
 
827
      #
 
828
      # Parse interface
 
829
      #
 
830
      interface_scope = false
 
831
      generic_name = ""
 
832
      interface_code.split("\n").each{ |line|
 
833
        if /^\s*?
 
834
                 interface(
 
835
                            \s+\w+|
 
836
                            \s+operator\s*?\(.*?\)|
 
837
                            \s+assignment\s*?\(\s*?=\s*?\)
 
838
                          )?
 
839
                 \s*?(!.*?)?$
 
840
           /ix =~ line
 
841
          generic_name = $1 ? $1.strip.chomp : nil
 
842
          interface_trailing = $2 || "!"
 
843
          interface_scope = true
 
844
          interface_scope = false if interface_trailing =~ /!:nodoc:/
 
845
#          if generic_name =~ /operator\s*?\((.*?)\)/i
 
846
#            operator_name = $1
 
847
#            if operator_name && !operator_name.empty?
 
848
#              generic_name = "#{operator_name}"
 
849
#            end
 
850
#          end
 
851
#          if generic_name =~ /assignment\s*?\((.*?)\)/i
 
852
#            assignment_name = $1
 
853
#            if assignment_name && !assignment_name.empty?
 
854
#              generic_name = "#{assignment_name}"
 
855
#            end
 
856
#          end
 
857
        end
 
858
        if /^\s*?end\s+interface/i =~ line
 
859
          interface_scope = false
 
860
          generic_name = nil
 
861
        end
 
862
        # internal alias
 
863
        if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
 
864
          procedures = $1.strip.chomp
 
865
          procedures_trailing = $2 || "!"
 
866
          next if procedures_trailing =~ /!:nodoc:/
 
867
          procedures.split(",").each{ |proc|
 
868
            proc.strip!
 
869
            proc.chomp!
 
870
            next if generic_name == proc || !generic_name
 
871
            old_meth = container.find_symbol(proc, nil, @options.ignore_case)
 
872
            next if !old_meth
 
873
            nolink = old_meth.visibility == :private ? true : nil
 
874
            nolink = nil if @options.show_all
 
875
            new_meth = 
 
876
               initialize_external_method(generic_name, proc, 
 
877
                                          old_meth.params, nil, 
 
878
                                          old_meth.comment, 
 
879
                                          old_meth.clone.token_stream[0].text, 
 
880
                                          true, nolink)
 
881
            new_meth.singleton = old_meth.singleton
 
882
 
 
883
            progress "i"
 
884
            @stats.num_methods += 1
 
885
            container.add_method new_meth
 
886
 
 
887
            set_visibility(container, generic_name, visibility_default, @@public_methods)
 
888
 
 
889
            check_public_methods(new_meth, container.name)
 
890
 
 
891
          }
 
892
        end
 
893
 
 
894
        # external aliases
 
895
        if interface_scope
 
896
          # subroutine
 
897
          proc = nil
 
898
          params = nil
 
899
          procedures_trailing = nil
 
900
          if line =~ /^\s*?
 
901
                           (recursive|pure|elemental)?\s*?
 
902
                           subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
 
903
                     /ix
 
904
            proc = $2.chomp.strip
 
905
            generic_name = proc unless generic_name
 
906
            params = $3 || ""
 
907
            procedures_trailing = $4 || "!"
 
908
 
 
909
          # function
 
910
          elsif line =~ /^\s*?
 
911
                         (recursive|pure|elemental)?\s*?
 
912
                         (
 
913
                             character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
914
                           | type\s*?\([\w\s]+?\)\s+
 
915
                           | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
916
                           | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
917
                           | double\s+precision\s+
 
918
                           | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
919
                           | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
920
                         )?
 
921
                         function\s+(\w+)\s*?
 
922
                         (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
 
923
                        /ix
 
924
            proc = $8.chomp.strip
 
925
            generic_name = proc unless generic_name
 
926
            params = $9 || ""
 
927
            procedures_trailing = $12 || "!"
 
928
          else
 
929
            next
 
930
          end
 
931
          next if procedures_trailing =~ /!:nodoc:/
 
932
          indicated_method = nil
 
933
          indicated_file   = nil
 
934
          TopLevel.all_files.each do |name, toplevel|
 
935
            indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
 
936
            indicated_file = name
 
937
            break if indicated_method
 
938
          end
 
939
 
 
940
          if indicated_method
 
941
            external_method = 
 
942
              initialize_external_method(generic_name, proc, 
 
943
                                         indicated_method.params, 
 
944
                                         indicated_file, 
 
945
                                         indicated_method.comment)
 
946
 
 
947
            progress "e"
 
948
            @stats.num_methods += 1
 
949
            container.add_method external_method
 
950
            set_visibility(container, generic_name, visibility_default, @@public_methods)
 
951
            if !container.include_requires?(indicated_file, @options.ignore_case)
 
952
              container.add_require(Require.new(indicated_file, ""))
 
953
            end
 
954
            check_public_methods(external_method, container.name)
 
955
 
 
956
          else
 
957
            @@external_aliases << {
 
958
              "new_name"  => generic_name,
 
959
              "old_name"  => proc,
 
960
              "file_or_module" => container,
 
961
              "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
 
962
            }
 
963
          end
 
964
        end
 
965
 
 
966
      } if interface_code # End of interface_code.split("\n").each ...
 
967
 
 
968
      #
 
969
      # Already imported methods are removed from @@public_methods.
 
970
      # Remainders are assumed to be imported from other modules.
 
971
      #
 
972
      @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
 
973
 
 
974
      @@public_methods.each{ |pub_meth|
 
975
        next unless pub_meth["file_or_module"].name == container.name
 
976
        pub_meth["used_modules"].each{ |used_mod|
 
977
          TopLevel.all_classes_and_modules.each{ |modules|
 
978
            if modules.name == used_mod ||
 
979
                modules.name.upcase == used_mod.upcase &&
 
980
                @options.ignore_case
 
981
              modules.method_list.each{ |meth|
 
982
                if meth.name == pub_meth["name"] ||
 
983
                    meth.name.upcase == pub_meth["name"].upcase &&
 
984
                    @options.ignore_case
 
985
                  new_meth = initialize_public_method(meth,
 
986
                                                      modules.name)
 
987
                  if pub_meth["local_name"]
 
988
                    new_meth.name = pub_meth["local_name"]
 
989
                  end
 
990
                  progress "e"
 
991
                  @stats.num_methods += 1
 
992
                  container.add_method new_meth
 
993
                end
 
994
              }
 
995
            end
 
996
          }
 
997
        }
 
998
      }
 
999
 
 
1000
      container
 
1001
    end  # End of parse_program_or_module
 
1002
 
 
1003
    #
 
1004
    # Parse arguments, comment, code of subroutine and function.
 
1005
    # Return AnyMethod object.
 
1006
    #
 
1007
    def parse_subprogram(subprogram, params, comment, code, 
 
1008
                         before_contains=nil, function=nil, prefix=nil)
 
1009
      subprogram.singleton = false
 
1010
      prefix = "" if !prefix
 
1011
      arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
 
1012
      args_comment, params_opt = 
 
1013
        find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
 
1014
                       nil, nil, true)
 
1015
      params_opt = "( " + params_opt + " ) " if params_opt
 
1016
      subprogram.params = params_opt || ""
 
1017
      namelist_comment = find_namelists(code, before_contains)
 
1018
 
 
1019
      block_comment = find_comments comment
 
1020
      if function
 
1021
        subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
 
1022
      else
 
1023
        subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
 
1024
      end
 
1025
      subprogram.comment << args_comment if args_comment
 
1026
      subprogram.comment << block_comment if block_comment
 
1027
      subprogram.comment << namelist_comment if namelist_comment
 
1028
 
 
1029
      # For output source code
 
1030
      subprogram.start_collecting_tokens
 
1031
      subprogram.add_token Token.new(1,1).set_text(code)
 
1032
 
 
1033
      subprogram
 
1034
    end
 
1035
 
 
1036
    #
 
1037
    # Collect comment for file entity
 
1038
    #
 
1039
    def collect_first_comment(body)
 
1040
      comment = ""
 
1041
      not_comment = ""
 
1042
      comment_start = false
 
1043
      comment_end   = false
 
1044
      body.split("\n").each{ |line|
 
1045
        if comment_end
 
1046
          not_comment << line
 
1047
          not_comment << "\n"
 
1048
        elsif /^\s*?!\s?(.*)$/i =~ line
 
1049
          comment_start = true
 
1050
          comment << $1
 
1051
          comment << "\n"
 
1052
        elsif /^\s*?$/i =~ line
 
1053
          comment_end = true if comment_start && COMMENTS_ARE_UPPER
 
1054
        else
 
1055
          comment_end = true
 
1056
          not_comment << line
 
1057
          not_comment << "\n"
 
1058
        end
 
1059
      }
 
1060
      return comment, not_comment
 
1061
    end
 
1062
 
 
1063
 
 
1064
    # Return comments of definitions of arguments
 
1065
    #
 
1066
    # If "all" argument is true, information of all arguments are returned.
 
1067
    # If "modified_params" is true, list of arguments are decorated,
 
1068
    # for exameple, optional arguments are parenthetic as "[arg]".
 
1069
    #
 
1070
    def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
 
1071
      return unless args || all
 
1072
      indent = "" unless indent
 
1073
      args = ["all"] if all
 
1074
      params = "" if modified_params
 
1075
      comma = ""
 
1076
      return unless text
 
1077
      args_rdocforms = "\n"
 
1078
      remaining_lines = "#{text}"
 
1079
      definitions = definition_info(remaining_lines)
 
1080
      args.each{ |arg|
 
1081
        arg.strip!
 
1082
        arg.chomp!
 
1083
        definitions.each { |defitem|
 
1084
          if arg == defitem.varname.strip.chomp || all
 
1085
            args_rdocforms << <<-"EOF"
 
1086
 
 
1087
#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> :: 
 
1088
#{indent}   <tt>#{defitem.types.chomp.strip}</tt>
 
1089
EOF
 
1090
            if !defitem.comment.chomp.strip.empty?
 
1091
              comment = ""
 
1092
              defitem.comment.split("\n").each{ |line|
 
1093
                comment << "       " + line + "\n"
 
1094
              }
 
1095
              args_rdocforms << <<-"EOF"
 
1096
 
 
1097
#{indent}   <tt></tt> :: 
 
1098
#{indent}       <tt></tt>
 
1099
#{indent}       #{comment.chomp.strip}
 
1100
EOF
 
1101
            end
 
1102
 
 
1103
            if modified_params
 
1104
              if defitem.include_attr?("optional")
 
1105
                params << "#{comma}[#{arg}]"
 
1106
              else
 
1107
                params << "#{comma}#{arg}"
 
1108
              end
 
1109
              comma = ", "
 
1110
            end
 
1111
          end
 
1112
        }
 
1113
      }
 
1114
      if modified_params
 
1115
        return args_rdocforms, params
 
1116
      else
 
1117
        return args_rdocforms
 
1118
      end
 
1119
    end
 
1120
 
 
1121
    # Return comments of definitions of namelists
 
1122
    #
 
1123
    def find_namelists(text, before_contains=nil)
 
1124
      return nil if !text
 
1125
      result = ""
 
1126
      lines = "#{text}"
 
1127
      before_contains = "" if !before_contains
 
1128
      while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
 
1129
        lines = $~.post_match
 
1130
        nml_comment = COMMENTS_ARE_UPPER ? 
 
1131
            find_comments($~.pre_match) : find_comments($~.post_match)
 
1132
        nml_name = $1
 
1133
        nml_args = $2.split(",")
 
1134
        result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
 
1135
        result << nml_comment + "\n" if nml_comment
 
1136
        if lines.split("\n")[0] =~ /^\//i
 
1137
          lines = "namelist " + lines
 
1138
        end
 
1139
        result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
 
1140
      end
 
1141
      return result
 
1142
    end
 
1143
 
 
1144
    #
 
1145
    # Comments just after module or subprogram, or arguments are
 
1146
    # returnd. If "COMMENTS_ARE_UPPER" is true, comments just before
 
1147
    # modules or subprograms are returnd
 
1148
    #
 
1149
    def find_comments text
 
1150
      return "" unless text
 
1151
      lines = text.split("\n")
 
1152
      lines.reverse! if COMMENTS_ARE_UPPER
 
1153
      comment_block = Array.new
 
1154
      lines.each do |line|
 
1155
        break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
 
1156
        if COMMENTS_ARE_UPPER
 
1157
          comment_block.unshift line.sub(/^\s*?!\s?/,"")
 
1158
        else
 
1159
          comment_block.push line.sub(/^\s*?!\s?/,"")
 
1160
        end
 
1161
      end
 
1162
      nice_lines = comment_block.join("\n").split "\n\s*?\n"
 
1163
      nice_lines[0] ||= ""
 
1164
      nice_lines.shift
 
1165
    end
 
1166
 
 
1167
    def progress(char)
 
1168
      unless @options.quiet
 
1169
        @progress.print(char)
 
1170
        @progress.flush
 
1171
      end
 
1172
    end
 
1173
 
 
1174
    #
 
1175
    # Create method for internal alias
 
1176
    #
 
1177
    def initialize_public_method(method, parent)
 
1178
      return if !method || !parent
 
1179
 
 
1180
      new_meth = AnyMethod.new("External Alias for module", method.name)
 
1181
      new_meth.singleton    = method.singleton
 
1182
      new_meth.params       = method.params.clone
 
1183
      new_meth.comment      = remove_trailing_alias(method.comment.clone)
 
1184
      new_meth.comment      << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
 
1185
 
 
1186
      return new_meth
 
1187
    end
 
1188
 
 
1189
    #
 
1190
    # Create method for external alias
 
1191
    #
 
1192
    # If argument "internal" is true, file is ignored.
 
1193
    #
 
1194
    def initialize_external_method(new, old, params, file, comment, token=nil,
 
1195
                                   internal=nil, nolink=nil)
 
1196
      return nil unless new || old
 
1197
 
 
1198
      if internal
 
1199
        external_alias_header = "#{INTERNAL_ALIAS_MES} "
 
1200
        external_alias_text   = external_alias_header + old 
 
1201
      elsif file
 
1202
        external_alias_header = "#{EXTERNAL_ALIAS_MES} "
 
1203
        external_alias_text   = external_alias_header + file + "#" + old
 
1204
      else
 
1205
        return nil
 
1206
      end
 
1207
      external_meth = AnyMethod.new(external_alias_text, new)
 
1208
      external_meth.singleton    = false
 
1209
      external_meth.params       = params
 
1210
      external_comment = remove_trailing_alias(comment) + "\n\n" if comment
 
1211
      external_meth.comment = external_comment || ""
 
1212
      if nolink && token
 
1213
        external_meth.start_collecting_tokens
 
1214
        external_meth.add_token Token.new(1,1).set_text(token)
 
1215
      else
 
1216
        external_meth.comment << external_alias_text
 
1217
      end
 
1218
 
 
1219
      return external_meth
 
1220
    end
 
1221
 
 
1222
 
 
1223
 
 
1224
    #
 
1225
    # Parse visibility
 
1226
    #
 
1227
    def parse_visibility(code, default, container)
 
1228
      result = []
 
1229
      visibility_default = default || :public
 
1230
 
 
1231
      used_modules = []
 
1232
      container.includes.each{|i| used_modules << i.name} if container
 
1233
 
 
1234
      remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
 
1235
      remaining_code.split("\n").each{ |line|
 
1236
        if /^\s*?private\s*?$/ =~ line
 
1237
          visibility_default = :private
 
1238
          break
 
1239
        end
 
1240
      } if remaining_code
 
1241
 
 
1242
      remaining_code.split("\n").each{ |line|
 
1243
        if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
 
1244
          methods = $2.sub(/!.*$/, '')
 
1245
          methods.split(",").each{ |meth|
 
1246
            meth.sub!(/!.*$/, '')
 
1247
            meth.gsub!(/:/, '')
 
1248
            result << {
 
1249
              "name" => meth.chomp.strip,
 
1250
              "visibility" => :private,
 
1251
              "used_modules" => used_modules.clone,
 
1252
              "file_or_module" => container,
 
1253
              "entity_is_discovered" => nil,
 
1254
              "local_name" => nil
 
1255
            }
 
1256
          }
 
1257
        elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
 
1258
          methods = $2.sub(/!.*$/, '')
 
1259
          methods.split(",").each{ |meth|
 
1260
            meth.sub!(/!.*$/, '')
 
1261
            meth.gsub!(/:/, '')
 
1262
            result << {
 
1263
              "name" => meth.chomp.strip,
 
1264
              "visibility" => :public,
 
1265
              "used_modules" => used_modules.clone,
 
1266
              "file_or_module" => container,
 
1267
              "entity_is_discovered" => nil,
 
1268
              "local_name" => nil
 
1269
            }
 
1270
          }
 
1271
        end
 
1272
      } if remaining_code
 
1273
 
 
1274
      if container
 
1275
        result.each{ |vis_info|
 
1276
          vis_info["parent"] = container.name
 
1277
        }
 
1278
      end
 
1279
 
 
1280
      return visibility_default, result
 
1281
    end
 
1282
 
 
1283
    #
 
1284
    # Set visibility
 
1285
    #
 
1286
    # "subname" element of "visibility_info" is deleted.
 
1287
    #
 
1288
    def set_visibility(container, subname, visibility_default, visibility_info)
 
1289
      return unless container || subname || visibility_default || visibility_info
 
1290
      not_found = true
 
1291
      visibility_info.collect!{ |info|
 
1292
        if info["name"] == subname ||
 
1293
            @options.ignore_case && info["name"].upcase == subname.upcase
 
1294
          if info["file_or_module"].name == container.name
 
1295
            container.set_visibility_for([subname], info["visibility"])
 
1296
            info["entity_is_discovered"] = true
 
1297
            not_found = false
 
1298
          end
 
1299
        end
 
1300
        info
 
1301
      }
 
1302
      if not_found
 
1303
        return container.set_visibility_for([subname], visibility_default)
 
1304
      else
 
1305
        return container
 
1306
      end
 
1307
    end
 
1308
 
 
1309
    #
 
1310
    # Find visibility
 
1311
    #
 
1312
    def find_visibility(container, subname, visibility_info)
 
1313
      return nil if !subname || !visibility_info
 
1314
      visibility_info.each{ |info|
 
1315
        if info["name"] == subname ||
 
1316
            @options.ignore_case && info["name"].upcase == subname.upcase
 
1317
          if info["parent"] == container.name
 
1318
            return info["visibility"]
 
1319
          end
 
1320
        end
 
1321
      }
 
1322
      return nil
 
1323
    end
 
1324
 
 
1325
    #
 
1326
    # Check external aliases
 
1327
    #
 
1328
    def check_external_aliases(subname, params, comment, test=nil)
 
1329
      @@external_aliases.each{ |alias_item|
 
1330
        if subname == alias_item["old_name"] ||
 
1331
                    subname.upcase == alias_item["old_name"].upcase &&
 
1332
                            @options.ignore_case
 
1333
 
 
1334
          new_meth = initialize_external_method(alias_item["new_name"], 
 
1335
                                                subname, params, @file_name, 
 
1336
                                                comment)
 
1337
          new_meth.visibility = alias_item["visibility"]
 
1338
 
 
1339
          progress "e"
 
1340
          @stats.num_methods += 1
 
1341
          alias_item["file_or_module"].add_method(new_meth)
 
1342
 
 
1343
          if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
 
1344
            alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
 
1345
          end
 
1346
        end
 
1347
      }
 
1348
    end
 
1349
 
 
1350
    #
 
1351
    # Check public_methods
 
1352
    #
 
1353
    def check_public_methods(method, parent)
 
1354
      return if !method || !parent
 
1355
      @@public_methods.each{ |alias_item|
 
1356
        parent_is_used_module = nil
 
1357
        alias_item["used_modules"].each{ |used_module|
 
1358
          if used_module == parent ||
 
1359
              used_module.upcase == parent.upcase &&
 
1360
              @options.ignore_case
 
1361
            parent_is_used_module = true
 
1362
          end
 
1363
        }
 
1364
        next if !parent_is_used_module
 
1365
 
 
1366
        if method.name == alias_item["name"] ||
 
1367
            method.name.upcase == alias_item["name"].upcase &&
 
1368
            @options.ignore_case
 
1369
 
 
1370
          new_meth = initialize_public_method(method, parent)
 
1371
          if alias_item["local_name"]
 
1372
            new_meth.name = alias_item["local_name"]
 
1373
          end
 
1374
 
 
1375
          progress "e"
 
1376
          @stats.num_methods += 1
 
1377
          alias_item["file_or_module"].add_method new_meth
 
1378
        end
 
1379
      }
 
1380
    end
 
1381
 
 
1382
    #
 
1383
    # Continuous lines are united.
 
1384
    #
 
1385
    # Comments in continuous lines are removed.
 
1386
    #
 
1387
    def united_to_one_line(f90src)
 
1388
      return "" unless f90src
 
1389
      lines = f90src.split("\n")
 
1390
      previous_continuing = false
 
1391
      now_continuing = false
 
1392
      body = ""
 
1393
      lines.each{ |line|
 
1394
        words = line.split("")
 
1395
        next if words.empty? && previous_continuing
 
1396
        commentout = false
 
1397
        brank_flag = true ; brank_char = ""
 
1398
        squote = false    ; dquote = false
 
1399
        ignore = false
 
1400
        words.collect! { |char|
 
1401
          if previous_continuing && brank_flag
 
1402
            now_continuing = true
 
1403
            ignore         = true
 
1404
            case char
 
1405
            when "!"                       ; break
 
1406
            when " " ; brank_char << char  ; next ""
 
1407
            when "&"
 
1408
              brank_flag = false
 
1409
              now_continuing = false
 
1410
              next ""
 
1411
            else 
 
1412
              brank_flag     = false
 
1413
              now_continuing = false
 
1414
              ignore         = false
 
1415
              next brank_char + char
 
1416
            end
 
1417
          end
 
1418
          ignore = false
 
1419
 
 
1420
          if now_continuing
 
1421
            next ""
 
1422
          elsif !(squote) && !(dquote) && !(commentout)
 
1423
            case char
 
1424
            when "!" ; commentout = true     ; next char
 
1425
            when "\""; dquote = true         ; next char
 
1426
            when "\'"; squote = true         ; next char
 
1427
            when "&" ; now_continuing = true ; next ""
 
1428
            else next char
 
1429
            end
 
1430
          elsif commentout
 
1431
            next char
 
1432
          elsif squote
 
1433
            case char
 
1434
            when "\'"; squote = false ; next char
 
1435
            else next char
 
1436
            end
 
1437
          elsif dquote
 
1438
            case char
 
1439
            when "\""; dquote = false ; next char
 
1440
            else next char
 
1441
            end
 
1442
          end
 
1443
        }
 
1444
        if !ignore && !previous_continuing || !brank_flag
 
1445
          if previous_continuing
 
1446
            body << words.join("")
 
1447
          else
 
1448
            body << "\n" + words.join("")
 
1449
          end
 
1450
        end
 
1451
        previous_continuing = now_continuing ? true : nil
 
1452
        now_continuing = nil
 
1453
      }
 
1454
      return body
 
1455
    end
 
1456
 
 
1457
 
 
1458
    #
 
1459
    # Continuous line checker
 
1460
    #
 
1461
    def continuous_line?(line)
 
1462
      continuous = false
 
1463
      if /&\s*?(!.*)?$/ =~ line
 
1464
        continuous = true
 
1465
        if comment_out?($~.pre_match)
 
1466
          continuous = false
 
1467
        end
 
1468
      end
 
1469
      return continuous
 
1470
    end
 
1471
 
 
1472
    #
 
1473
    # Comment out checker
 
1474
    #
 
1475
    def comment_out?(line)
 
1476
      return nil unless line
 
1477
      commentout = false
 
1478
      squote = false ; dquote = false
 
1479
      line.split("").each { |char|
 
1480
        if !(squote) && !(dquote)
 
1481
          case char
 
1482
          when "!" ; commentout = true ; break
 
1483
          when "\""; dquote = true
 
1484
          when "\'"; squote = true
 
1485
          else next
 
1486
          end
 
1487
        elsif squote
 
1488
          case char
 
1489
          when "\'"; squote = false
 
1490
          else next
 
1491
          end
 
1492
        elsif dquote
 
1493
          case char
 
1494
          when "\""; dquote = false
 
1495
          else next
 
1496
          end
 
1497
        end
 
1498
      }
 
1499
      return commentout
 
1500
    end
 
1501
 
 
1502
    #
 
1503
    # Semicolons are replaced to line feed.
 
1504
    #
 
1505
    def semicolon_to_linefeed(text)
 
1506
      return "" unless text
 
1507
      lines = text.split("\n")
 
1508
      lines.collect!{ |line|
 
1509
        words = line.split("")
 
1510
        commentout = false
 
1511
        squote = false ; dquote = false
 
1512
        words.collect! { |char|
 
1513
          if !(squote) && !(dquote) && !(commentout)
 
1514
            case char
 
1515
            when "!" ; commentout = true ; next char
 
1516
            when "\""; dquote = true     ; next char
 
1517
            when "\'"; squote = true     ; next char
 
1518
            when ";" ;                     "\n"
 
1519
            else next char
 
1520
            end
 
1521
          elsif commentout
 
1522
            next char
 
1523
          elsif squote
 
1524
            case char
 
1525
            when "\'"; squote = false ; next char
 
1526
            else next char
 
1527
            end
 
1528
          elsif dquote
 
1529
            case char
 
1530
            when "\""; dquote = false ; next char
 
1531
            else next char
 
1532
            end
 
1533
          end
 
1534
        }
 
1535
        words.join("")
 
1536
      }
 
1537
      return lines.join("\n")
 
1538
    end
 
1539
 
 
1540
    #
 
1541
    # Which "line" is start of block (module, program, block data,
 
1542
    # subroutine, function) statement ?
 
1543
    #
 
1544
    def block_start?(line)
 
1545
      return nil if !line
 
1546
 
 
1547
      if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i    ||
 
1548
          line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i  ||
 
1549
          line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i     ||
 
1550
          line =~ \
 
1551
                  /^\s*?
 
1552
                   (recursive|pure|elemental)?\s*?
 
1553
                   subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
 
1554
                  /ix ||
 
1555
          line =~ \
 
1556
                  /^\s*?
 
1557
                   (recursive|pure|elemental)?\s*?
 
1558
                   (
 
1559
                       character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
1560
                     | type\s*?\([\w\s]+?\)\s+
 
1561
                     | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
1562
                     | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
1563
                     | double\s+precision\s+
 
1564
                     | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
1565
                     | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
 
1566
                   )?
 
1567
                   function\s+(\w+)\s*?
 
1568
                   (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
 
1569
                  /ix
 
1570
        return true
 
1571
      end
 
1572
 
 
1573
      return nil
 
1574
    end
 
1575
 
 
1576
    #
 
1577
    # Which "line" is end of block (module, program, block data,
 
1578
    # subroutine, function) statement ?
 
1579
    #
 
1580
    def block_end?(line)
 
1581
      return nil if !line
 
1582
 
 
1583
      if line =~ /^\s*?end\s*?(!.*?)?$/i                 ||
 
1584
          line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i       ||
 
1585
          line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i      ||
 
1586
          line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i  ||
 
1587
          line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i   ||
 
1588
          line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
 
1589
        return true
 
1590
      end
 
1591
 
 
1592
      return nil
 
1593
    end
 
1594
 
 
1595
    #
 
1596
    # Remove "Alias for" in end of comments
 
1597
    #
 
1598
    def remove_trailing_alias(text)
 
1599
      return "" if !text
 
1600
      lines = text.split("\n").reverse
 
1601
      comment_block = Array.new
 
1602
      checked = false
 
1603
      lines.each do |line|
 
1604
        if !checked 
 
1605
          if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
 
1606
              /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
 
1607
            checked = true
 
1608
            next
 
1609
          end
 
1610
        end
 
1611
        comment_block.unshift line
 
1612
      end
 
1613
      nice_lines = comment_block.join("\n")
 
1614
      nice_lines ||= ""
 
1615
      return nice_lines
 
1616
    end
 
1617
 
 
1618
    # Empty lines in header are removed
 
1619
    def remove_empty_head_lines(text)
 
1620
      return "" unless text
 
1621
      lines = text.split("\n")
 
1622
      header = true
 
1623
      lines.delete_if{ |line|
 
1624
        header = false if /\S/ =~ line
 
1625
        header && /^\s*?$/ =~ line
 
1626
      }
 
1627
      lines.join("\n")
 
1628
    end
 
1629
 
 
1630
 
 
1631
    # header marker "=", "==", ... are removed
 
1632
    def remove_header_marker(text)
 
1633
      return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
 
1634
    end
 
1635
 
 
1636
    def remove_private_comments(body)
 
1637
      body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
 
1638
      return body
 
1639
    end
 
1640
 
 
1641
 
 
1642
    #
 
1643
    # Information of arguments of subroutines and functions in Fortran95
 
1644
    #
 
1645
    class Fortran95Definition
 
1646
 
 
1647
      # Name of variable
 
1648
      #
 
1649
      attr_reader   :varname
 
1650
 
 
1651
      # Types of variable
 
1652
      #
 
1653
      attr_reader   :types
 
1654
 
 
1655
      # Initial Value
 
1656
      #
 
1657
      attr_reader   :inivalue
 
1658
 
 
1659
      # Suffix of array
 
1660
      #
 
1661
      attr_reader   :arraysuffix
 
1662
 
 
1663
      # Comments
 
1664
      #
 
1665
      attr_accessor   :comment
 
1666
 
 
1667
      # Flag of non documentation
 
1668
      #
 
1669
      attr_accessor   :nodoc
 
1670
 
 
1671
      def initialize(varname, types, inivalue, arraysuffix, comment,
 
1672
                     nodoc=false)
 
1673
        @varname = varname
 
1674
        @types = types
 
1675
        @inivalue = inivalue
 
1676
        @arraysuffix = arraysuffix
 
1677
        @comment = comment
 
1678
        @nodoc = nodoc
 
1679
      end
 
1680
 
 
1681
      def to_s
 
1682
        return <<-EOF
 
1683
<Fortran95Definition: 
 
1684
  varname=#{@varname}, types=#{types},
 
1685
  inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc}, 
 
1686
  comment=
 
1687
#{@comment}
 
1688
>
 
1689
EOF
 
1690
      end
 
1691
 
 
1692
      #
 
1693
      # If attr is included, true is returned
 
1694
      #
 
1695
      def include_attr?(attr)
 
1696
        return if !attr
 
1697
        @types.split(",").each{ |type|
 
1698
          return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
 
1699
        }
 
1700
        return nil
 
1701
      end
 
1702
 
 
1703
    end # End of Fortran95Definition
 
1704
 
 
1705
    #
 
1706
    # Parse string argument "text", and Return Array of
 
1707
    # Fortran95Definition object
 
1708
    #
 
1709
    def definition_info(text)
 
1710
      return nil unless text
 
1711
      lines = "#{text}"
 
1712
      defs = Array.new
 
1713
      comment = ""
 
1714
      trailing_comment = ""
 
1715
      under_comment_valid = false
 
1716
      lines.split("\n").each{ |line|
 
1717
        if /^\s*?!\s?(.*)/ =~ line
 
1718
          if COMMENTS_ARE_UPPER
 
1719
            comment << remove_header_marker($1)
 
1720
            comment << "\n"
 
1721
          elsif defs[-1] && under_comment_valid
 
1722
            defs[-1].comment << "\n"
 
1723
            defs[-1].comment << remove_header_marker($1)
 
1724
          end
 
1725
          next
 
1726
        elsif /^\s*?$/ =~ line
 
1727
          comment = ""
 
1728
          under_comment_valid = false
 
1729
          next
 
1730
        end
 
1731
        type = ""
 
1732
        characters = ""
 
1733
        if line =~ /^\s*?
 
1734
                    (
 
1735
                        character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
 
1736
                      | type\s*?\([\w\s]+?\)[\s\,]*
 
1737
                      | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
 
1738
                      | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
 
1739
                      | double\s+precision[\s\,]*
 
1740
                      | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
 
1741
                      | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
 
1742
                    )
 
1743
                    (.*?::)?
 
1744
                    (.+)$
 
1745
                   /ix
 
1746
          characters = $8
 
1747
          type = $1
 
1748
          type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
 
1749
        else
 
1750
          under_comment_valid = false
 
1751
          next
 
1752
        end
 
1753
        squote = false ; dquote = false ; bracket = 0
 
1754
        iniflag = false; commentflag = false
 
1755
        varname = "" ; arraysuffix = "" ; inivalue = ""
 
1756
        start_pos = defs.size
 
1757
        characters.split("").each { |char|
 
1758
          if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
 
1759
            case char
 
1760
            when "!" ; commentflag = true
 
1761
            when "(" ; bracket += 1       ; arraysuffix = char
 
1762
            when "\""; dquote = true
 
1763
            when "\'"; squote = true
 
1764
            when "=" ; iniflag = true     ; inivalue << char
 
1765
            when ","
 
1766
              defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
 
1767
              varname = "" ; arraysuffix = "" ; inivalue = ""
 
1768
              under_comment_valid = true
 
1769
            when " " ; next
 
1770
            else     ; varname << char
 
1771
            end
 
1772
          elsif commentflag
 
1773
            comment << remove_header_marker(char)
 
1774
            trailing_comment << remove_header_marker(char)
 
1775
          elsif iniflag
 
1776
            if dquote
 
1777
              case char
 
1778
              when "\"" ; dquote = false ; inivalue << char
 
1779
              else      ; inivalue << char
 
1780
              end
 
1781
            elsif squote
 
1782
              case char
 
1783
              when "\'" ; squote = false ; inivalue << char
 
1784
              else      ; inivalue << char
 
1785
              end
 
1786
            elsif bracket > 0
 
1787
              case char
 
1788
              when "(" ; bracket += 1 ; inivalue << char
 
1789
              when ")" ; bracket -= 1 ; inivalue << char
 
1790
              else     ; inivalue << char
 
1791
              end
 
1792
            else
 
1793
              case char
 
1794
              when ","
 
1795
                defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
 
1796
                varname = "" ; arraysuffix = "" ; inivalue = ""
 
1797
                iniflag = false
 
1798
                under_comment_valid = true
 
1799
              when "(" ; bracket += 1 ; inivalue << char
 
1800
              when "\""; dquote = true  ; inivalue << char
 
1801
              when "\'"; squote = true  ; inivalue << char
 
1802
              when "!" ; commentflag = true
 
1803
              else     ; inivalue << char
 
1804
              end
 
1805
            end
 
1806
          elsif !(squote) && !(dquote) && bracket > 0
 
1807
            case char
 
1808
            when "(" ; bracket += 1 ; arraysuffix << char
 
1809
            when ")" ; bracket -= 1 ; arraysuffix << char
 
1810
            else     ; arraysuffix << char
 
1811
            end
 
1812
          elsif squote
 
1813
            case char
 
1814
            when "\'"; squote = false ; inivalue << char
 
1815
            else     ; inivalue << char
 
1816
            end
 
1817
          elsif dquote
 
1818
            case char
 
1819
            when "\""; dquote = false ; inivalue << char
 
1820
            else     ; inivalue << char
 
1821
            end
 
1822
          end
 
1823
        }
 
1824
        defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
 
1825
        if trailing_comment =~ /^:nodoc:/
 
1826
          defs[start_pos..-1].collect!{ |defitem|
 
1827
            defitem.nodoc = true
 
1828
          }
 
1829
        end
 
1830
        varname = "" ; arraysuffix = "" ; inivalue = ""
 
1831
        comment = ""
 
1832
        under_comment_valid = true
 
1833
        trailing_comment = ""
 
1834
      }
 
1835
      return defs
 
1836
    end
 
1837
 
 
1838
 
 
1839
  end # class Fortran95parser
 
1840
 
 
1841
end # module RDoc