rdoc 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rdoc might be problematic. Click here for more details.

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