source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rdoc/parsers/parse_f95.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 59.2 KB
Line 
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
150require "rdoc/code_objects"
151
152module 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>
1089EOF
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}
1100EOF
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>
1689EOF
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
1841end # module RDoc
Note: See TracBrowser for help on using the repository browser.