File Coverage

blib/lib/Fortran/F90Namelist/Group.pm
Criterion Covered Total %
statement 136 148 91.8
branch 26 40 65.0
condition 12 18 66.6
subroutine 16 17 94.1
pod 12 13 92.3
total 202 236 85.5


line stmt bran cond sub pod time code
1             #
2             # F90Namelist/Group.pm
3             # --------------------
4             #
5             # Description:
6             # Parse a group of F90 namelists into F90Namelist.pm objects and export in
7             # different formats.
8             # Author: wd (Wolfgang.Dobler@kis.uni-freiburg.de)
9             # $Date: 2006/12/18 23:16:04 $
10             # $Revision: 1.2 $
11             # [Date and CVS revision are now pretty irrelevant, as I keep the code
12             # under Darcs now]
13              
14             package Fortran::F90Namelist::Group;
15              
16             =head1 NAME
17              
18             Fortran::F90Namelist::Group - Parse F90 namelist groups and export in different formats
19              
20             =head1 SYNOPSIS
21              
22             use Fortran::F90Namelist::Group;
23             my $nlgrp = Fortran::F90Namelist::Group->new() or die "Couldn't get object\n";
24              
25             $nlgrp->parse(<<' HERE');
26             &runpars
27             x=2,y=3
28             vec1=1,2,3
29             vec2=3*1.3
30             /
31             &more_runpars
32             z=7
33             vec1=0,1,2,3
34             /
35             HERE
36              
37             Read from file:
38              
39             # Read namelist group from file `some_lists.nml':
40             $nlgrp->parse(file => 't/files/some_lists.nml');
41              
42             # Read namelist group from file handle
43             open(my $fh , "< t/files/some_lists.nml") or die "Couldn't get file handle\n";
44             $nlgrp->parse(file => $fh);
45             # or
46             open(NLGROUP , "< t/files/some_lists.nml") or die "Couldn't get file handle\n";
47             $nlgrp->parse(file => \*NLGROUP);
48              
49             # Print names of all namelists in file `start.in'
50             $nlgrp->parse(file => 't/files/start.in') or die "Couldn't parse\n";
51             print join(" ", $nlgrp->names), "\n";
52              
53             Extract or merge namelists from group and return I object:
54              
55             my $nl_1 = $nlgrp->first(); # Extract first namelist from group
56             my $nl_3 = $nlgrp->nth(3); # Extract 4th namelist from group
57             my $nl_all = $nlgrp->flatten(); # Flatten all namelists into one
58              
59             Write namelist group:
60              
61             # Write namelist group in F90 namelist format
62             print $nlgrp->output();
63              
64             # Write namelist as IDL structure
65             print $nlgrp->output(format => 'idl');
66              
67              
68             =head1 DESCRIPTION
69              
70             I is a module for parsing Fortran90 namelist
71             groups into an internal format based on
72             L, and re-exporting in different
73             formats.
74             Parsing is done by L, see the
75             documentation of that module for more details.
76              
77              
78             =head2 Methods
79              
80             =over 4
81              
82              
83             =item B<$nlgrp-Enew>()
84              
85             Create a new namelist group object
86              
87             =item B<$nlgrp-Eparse>(I)
88              
89             =item B<$nlgrp-Eparse>(text => I)
90              
91             =item B<$nlgrp-Eparse>(file => I|I)
92              
93             =item B<$nlgrp-Eparse>(file => I|I [, ])
94              
95             Parse I or the file represented by I or i (a file
96             handle or L object [not yet implemeted]);
97             returns number of parsed namelists, or undef if parsing failed.
98              
99             Additional I are:
100              
101             =over 8
102              
103             =item B
104              
105             If true, append newly parsed namelists to already existing data in the
106             object.
107              
108             =back
109              
110              
111             =item B<$nlgrp-Enlists>()
112              
113             Return number of namelists in group.
114              
115              
116             =item B<$nlgrp-Enames>()
117              
118             Return list of namelist names in group (in original order).
119              
120              
121             =item B<$nlgrp-Einsert>(nl [, pos])
122              
123             Insert namelist into namelist group at position POS (defaults to appending
124             nl at end of group.
125             Returns 1 if successfull, 0 or undef otherwise.
126              
127              
128             =item B<$nlgrp-Edelete>(nl)
129              
130             =item B<$nlgrp-Edelete>(name)
131              
132             =item B<$nlgrp-Edelete>(num)
133              
134             Delete namelist (identified by namelist object, name, or position in
135             $nlgrp->names) from namelist group.
136             Returns 1 if successfull, 0 otherwise.
137              
138              
139             =item B<$nlgrp-Efirst>()
140              
141             Return the first namelist in the group as
142             L object.
143              
144              
145             =item B<$nlgrp-Enth>(n)
146              
147             Return the namelist with index n from the group as
148             L object.
149             Indices count from 0, so this returns the (n+1)st namelist.
150              
151              
152             =item B<$nlgrp-Epop>(n)
153              
154             Return the first namelist in the group as
155             L object and remove it from the
156             group.
157             Returns C for an empty group.
158             This allows to write:
159              
160             while (my $nl = $nlgrp->pop()) {
161             print $nl->name(), " has ", $nl->nslots(), "slots\n";
162             }
163              
164              
165             =item B<$nlgrp-Eflatten([options])>
166              
167             Merge all namelist data in the group into one
168             L object.
169             Thus,
170              
171             $nlgrp->parse(file => 't/files/some_lists.nml');
172             my $nl = $nlgrp->flatten();
173              
174             is another way of doing
175              
176             my $nl = Fortran::F90Namelist->new();
177             $nl->parse(file => 't/files/some_lists.nml',
178             all => 1 );
179              
180             I are:
181              
182             =over 8
183              
184             =item B
185              
186             Set name of resulting namelist (default: name of first namelist read).
187              
188             =item B
189              
190             Don't warn if new slots have same names as existing slots.
191              
192             =back
193              
194              
195             =item B<$nlgrp-Ehash>()
196              
197             Return namelist group as Perl hash.
198             See L below for details of the hash format.
199              
200              
201             =item B<$nlgrp-Eoutput>(format => I)
202              
203             Write namelist group in given I.
204             Currently supported formats are `f90' (default), and `idl'
205              
206              
207             =back
208              
209              
210             =head1 HASH FORMAT
211              
212             The B method returns a hash reference of the following structure:
213              
214             =for test ignore
215              
216             { namelist1 => { var1 => { 'value' => [ value1, value2, ..],
217             'type' => numerical_type,
218             'stype' => "type string"
219             },
220             var2 => { 'value' => [ value1, value2, ..],
221             'type' => numerical_type
222             'stype' => "type string"
223             },
224             ...
225             },
226             namelist2 => { var1 => { 'value' => [ value1, value2, ..],
227             'type' => numerical_type,
228             'stype' => "type string"
229             },
230             ...
231             },
232             ...
233             }
234              
235             =for test
236              
237             Here I is a number identifying each data type, while
238             I is a textual description of the given data type.
239              
240             E.g.
241              
242             =for test ignore
243              
244             { 'hydro_init_pars' => { 'u0' => { 'value' => [ 0., -3.141593, 0. ],
245             'type' => 6,
246             'stype' => 'single precision float'
247             },
248             'u1' => { 'value' => [ 0., 0., 0.],
249             'type' => 6,
250             'stype' => 'single precision float'
251             },
252             },
253             'density_init_pars' => { 'rho0' => { 'value' => [ -2.78 ],
254             'type' => 6,
255             'stype' => 'single precision float'
256             },
257             'ilnrho' => { 'value' => [ 3 ],
258             'type' => 4,
259             'stype' => 'integer'
260             },
261             },
262             }
263              
264             =for test
265              
266             Note: This is currently just the internal format used to represent
267             namelists and can thus change in the future.
268             In particular the C numbers should not be considered to be stable
269             between releases.
270              
271              
272             =head1 TO DO
273              
274             =over 4
275              
276             =item 1.
277              
278             More output methods:
279              
280             =over 8
281              
282             =item *
283              
284             Octave/Matlab , C struct, YAML, XML(?), ...
285              
286             =back
287              
288             =back
289              
290              
291             =head1 BUGS AND LIMITATIONS
292              
293             =over 4
294              
295             =item *
296              
297             No user-defined types (records) are supported, so if you have these LaTeX
298             comment characters in your namelist data, you are out of luck.
299              
300             =back
301              
302              
303             =head1 AUTHOR
304              
305             Wolfgang Dobler
306              
307              
308             =head1 LICENSE AND COPYRIGHT
309              
310             Copyright (c) 2007, Wolfgang Dobler .
311             All rights reserved.
312              
313             This program is free software; you can redistribute it and/or modify it
314             under the same conditions as Perl or under the GNU General Public
315             License, version 2 or later.
316              
317              
318             =head1 DISCLAIMER OF WARRANTY
319              
320             Use completely at your own risk.
321              
322              
323             =cut
324              
325              
326 8     8   234607 use strict;
  8         20  
  8         305  
327 8     8   53 use Carp;
  8         15  
  8         812  
328 8     8   44 use vars qw($VERSION);
  8         15  
  8         459  
329 8     8   7776 use Fortran::F90Namelist;
  8         26  
  8         13224  
330              
331             $VERSION = '0.5.1';
332              
333             # ---------------------------------------------------------------------- #
334             ##
335             ## Object constructor
336             ##
337             ## Internal structure of Namlist::Group objects:
338             ## DATA -- variable names, values, and types (hashref, see below)
339             ## NAMES -- ordered list of variable names (array ref)
340             ## NLISTS -- number of namelists
341             ## PARSED_ -- flag indicating that argument has been parsed
342             ## DEBUG_ -- debug flag
343             ##
344             ## {
345             ## 'NAMES' => [ 'nlname1', 'nlname2', ..., 'nlnameN' ],
346             ## 'DATA' => { 'nlname1' => $nlobj1,
347             ## 'nlname2' => $nlobj2,
348             ## ...
349             ## 'nlnameN' => $nlobjN },
350             ## 'NLISTS' => N,
351             ## 'PARSED_' => 0/1,
352             ## 'DEBUG_' => 0/1
353             ## }
354             ##
355             sub new {
356 8     8 1 157 my $proto = shift; # either classref or object ref or string
357 8         22 my @argv = @_;
358 8   33     65 my $class = ref($proto) || $proto;
359 8         22 my $self = {};
360              
361 8         24 my %data = ();
362 8         18 my @names = ();
363 8         20 my $nlists = undef;
364 8         20 my $parsed = 0;
365              
366 8         20 my $short_usage =
367             "Usage:\n" .
368             " Fortran::F90Namelist::Group->new()\n" ;
369             # unless($file) {
370             # croak $short_usage;
371             # return undef;
372             # }
373              
374             # Parse argument(s) (name => ); may be list or hashref
375 8         18 my %args;
376 8 50       32 if (@argv) {
377 0 0       0 if (ref($argv[0]) eq 'HASH') { # parse($hashref)
378 0         0 %args = %{$argv[0]};
  0         0  
379             } else { # parse(%hash) or parse(@list)
380 0         0 %args = @argv;
381             }
382             }
383             #
384 8   50     69 my $debug = ($args{debug} || 0);
385              
386             ##
387             ## Finish the object
388             ##
389             # public data of object
390 8         28 $self->{DATA} = \%data;
391 8         27 $self->{NAMES} = \@names;
392 8         19 $self->{NLISTS} = $nlists;
393              
394             # internal data
395 8         22 $self->{PARSED_} = $parsed;
396 8         21 $self->{DEBUG_} = $debug;
397              
398 8         25 bless($self,$class);
399 8         35 return $self;
400             }
401              
402             # ====================================================================== #
403              
404             ##
405             ## Methods
406             ##
407              
408             sub parse {
409             #
410             # $obj->parse($text)
411             # $obj->parse(file => $filename|$filehandle)
412             # $obj->parse({text => $textstring)
413             # $obj->parse(... , append => 1)
414             #
415             # Parse text or file containing F90 namelist(s)
416             #
417 20     20 1 14048 my $self = shift;
418 20         61 my @args = @_; # can't use shift() since we change value
419             # of $text
420 20         34 my %args;
421 20         44 my $text = '';
422              
423             # Parse arguments (file => , etc.); may be single string,
424             # list, hash or hashref
425 20 50       68 if (ref($args[0]) eq 'HASH') { # parse($hashref)
426 0         0 %args = %{$args[0]};
  0         0  
427             } else {
428 20 100       67 if (@_ == 1) { # parse($string)
429 4         8 $text = $args[0];
430             } else { # parse(%hash) or parse(@list)
431 16         56 %args = @args;
432             }
433             }
434 20   100     96 my $file = ($args{file} || '' );
435 20   100     107 $text = ($args{text} || $text );
436 20   100     95 my $append = ($args{append} || 0 );
437              
438 20         40 my $new_nlists = 0; # counter
439              
440 20 100       56 if (! $append) { # clear all data
441 19         81 $self->{DATA} = {};
442 19         275 $self->{NAMES} = [];
443 19         44 $self->{NLISTS} = 0;
444 19         31 $self->{PARSED_} = 0;
445             }
446              
447             # Get text from file if necessary
448 20 100       66 if ($text eq '') {
449 15 50       52 croak "Fortran::F90Namelist::Group->parse(): need text or file argument\n"
450             unless ($file ne '');
451 15         57 local $/ = undef;
452 15 100       51 if (ref($file) eq 'GLOB') { # file handle
453 2         101 $text = <$file>;
454             } else { # file name
455 13 50       607 open(FH, "< $file") or croak "Cannot open file <$file> for reading";
456 13         416 $text = ;
457 13         188 close(FH);
458             }
459             }
460              
461 20         67 while (length($text) > 0) {
462             # Note: shouldn't be difficult to reuse F90Namelist object here
463 78 50       290 my $nl = Fortran::F90Namelist->new()
464             or croak "Couldn't get F90Namelist object\n";
465 78 50       206 $nl->debug(1) if $self->{DEBUG_}; # hand down debugging flag
466 78         217 $nl->parse($text);
467 78         232 my $name = $nl->name;
468 78 100 66     421 if (defined($name) and $name ne '') {
469 69         165 $self->{DATA}->{$name} = $nl;
470 69         76 push @{$self->{NAMES}}, $name;
  69         135  
471 69         105 $self->{NLISTS}++;
472 69         183 $new_nlists++;
473             }
474             }
475              
476 20         41 $self->{PARSED_} = 1;
477              
478 20         93 return $new_nlists;
479             }
480              
481             # ---------------------------------------------------------------------- #
482              
483             sub nlists {
484             #
485             # Get number of namelist in group
486             #
487 33     33 1 15704 my $self = shift();
488 33         139 return $self->{NLISTS};
489             }
490              
491             # ---------------------------------------------------------------------- #
492              
493             sub names {
494             #
495             # Return array of namelist names in order
496             #
497 28     28 1 60 my $self = shift();
498 28         173 return $self->{NAMES};
499             }
500              
501             # ---------------------------------------------------------------------- #
502              
503             sub insert {
504             #
505             # Insert namelist at given position
506             #
507             # $nlgrp->insert($nlobj)
508             # $nlgrp->insert($nlobj, $pos)
509             #
510 3     3 1 5 my $self = shift();
511 3         4 my ($nlobj,$pos) = @_;
512              
513 3 100       9 $pos = $self->nlists() unless defined($pos);
514              
515 3 50       11 croak "Usage: Fortran::F90Namelist::Group::insert(\$nlobj,\$pos)\n"
516             unless (ref($nlobj) eq "Fortran::F90Namelist");
517              
518 3         8 my $nlname = $nlobj->name();
519              
520             # Insert namelist data
521 3         11 $self->{DATA}->{$nlname} = $nlobj;
522             # Insert name in order list
523 3         5 my @names = @{$self->{NAMES}};
  3         8  
524 3         8 splice @names, $pos, 0, $nlname;
525 3         6 $self->{NAMES} = \@names;
526             # Increment counter
527 3         6 $self->{NLISTS}++;
528              
529 3         12 return 1;
530             }
531              
532             # ---------------------------------------------------------------------- #
533              
534             sub delete {
535             #
536             # Delete namelist from group
537             #
538             # $nlgrp->delete($nlobj)
539             # $nlgrp->delete($nlname)
540             # $nlgrp->delete($pos)
541             #
542              
543 4     4 1 9 my $self = shift();
544 4         6 my ($arg1) = @_;
545              
546             # Extract nl name from whatever argument we have
547 4         6 my $nlname;
548 4 100       24 if (ref($arg1) eq 'Fortran::F90Namelist') { # F::NL object
    100          
549 1         4 $nlname = $arg1->name();
550             } elsif ($arg1 =~ /^[0-9]+$/) { # number (=position)
551 2         5 $nlname = $self->{NAMES}[$arg1];
552             } else { # name
553 1         2 $nlname = $arg1;
554             }
555              
556             # Delete namelist data
557 4 50       16 delete $self->{DATA}->{$nlname} or return 0;
558              
559             # Remove name from list
560 4         28 my @names = grep { !/^${nlname}$/ } @{$self->{NAMES}};
  18         99  
  4         7  
561 4         11 $self->{NAMES} = \@names;
562              
563             # Decrement counter
564 4         10 $self->{NLISTS}--;
565              
566 4         88 return 1;
567             }
568              
569             # ---------------------------------------------------------------------- #
570              
571             sub first {
572             #
573             # Return the first namelist in the group as Fortran::F90Namelist object.
574             #
575 4     4 1 7 my $self = shift();
576              
577 4         14 return $self->nth(0);
578             }
579              
580             # ---------------------------------------------------------------------- #
581              
582             sub nth {
583             #
584             # Return the n-th namelist in the group as Fortran::F90Namelist object.
585             # Counts from 0.
586             #
587 9     9 1 12 my $self = shift();
588 9         10 my $n = shift();
589              
590 9         20 my $nlists = $self->nlists();
591 9 50 33     47 if (($n < 0) || ($n >= $nlists)) {
592 0         0 croak "Fortran::F90Namelist::Group->nth(): "
593             . "Need 0 <= n < $nlists, but n=$n";
594             }
595 9         15 my $nlname = $self->{NAMES}[$n];
596 9         18 my $nl = $self->{DATA}->{$nlname};
597              
598 9         17 return $nl;
599             }
600              
601             # ---------------------------------------------------------------------- #
602              
603             sub pop {
604             #
605             # Return the first namelist in the group as Fortran::F90Namelist object (or
606             # undef if there is no first namelist) and remove it from the group,
607             # pretty much like pop(@arr) does for arrays.
608             #
609 1     1 1 2 my $self = shift();
610              
611 1 50       4 if ($self->nlists() < 1) {
612 0         0 return undef;
613             } else {
614 1         4 my $nl = $self->first();
615 1         4 $self->delete(0);
616 1         4 return $nl;
617             }
618             }
619              
620             # ---------------------------------------------------------------------- #
621              
622             sub flatten {
623             #
624             # Merge all namelist data in the group into one Fortran::F90Namelist object.
625             #
626 2     2 1 4 my $self = shift();
627 2         6 my @argv = @_; # will just be handed over to F90Namelist::merge()
628              
629 2         7 my $nl = $self->first();
630              
631 2         10 foreach my $i (1..$self->{NLISTS}-1) {
632 4         8 my $nl2 = $self->nth($i);
633 4         13 $nl->merge($nl2, @argv);
634             }
635              
636 2         8 return $nl;
637             }
638              
639             # ---------------------------------------------------------------------- #
640              
641             sub hash {
642             #
643             # Return hash with parsed namelist contents
644             #
645 8     8 1 22 my $self = shift;
646              
647             # Collect the $nl->hash() values of the individual namelists
648 8         20 my $hashref = {};
649 8         19 foreach my $name (@{$self->{NAMES}}) {
  8         25  
650 37         82 my $nl = $self->{DATA}->{$name};
651 37         97 my $name = $nl->name();
652              
653 37         104 $hashref->{$name} = $nl->hash;
654             }
655              
656 8         35 return $hashref;
657             }
658              
659             # ---------------------------------------------------------------------- #
660              
661             sub output {
662             #
663             # Write namelist in specified format (defaults to 'f90')
664             # Optional arguments (see Fortran::F90Namelist.pm):
665             # format => format ('f90' [default] or 'idl')
666             # trim => 0/1 (trim strings?)
667             # double => 0/1 (mark all floats as double precision?)
668             # oneline => 0/1 (write all in one line? [only for some formats])
669             # maxslots => N (similar to oneline, but split every N slots)
670             #
671 2     2 1 4 my $self = shift();
672 2         4 my @argv = @_; # will just be handed over to F90Namelist::output()
673              
674 2         3 my $string='';
675              
676 2         13 foreach my $nlname (@{$self->{NAMES}}) {
  2         6  
677 4         8 my $nl = $self->{DATA}->{$nlname};
678 4         16 $string .= $nl->output(@argv);
679             }
680              
681 2         7 return $string;
682              
683             }
684              
685             # ---------------------------------------------------------------------- #
686              
687             sub debug{
688             #
689             # $obj->debug(1) # debugging on
690             # $obj->debug(0) # debugging off
691             #
692             # Purposefully undocumented: Set/get debug flag
693             #
694 0     0 0   my $self = shift();
695 0 0         if (@_) { $self->{DEBUG_} = shift };
  0            
696 0           $self->{DEBUG_}
697             }
698              
699              
700             # ====================================================================== #
701              
702             ## Private utility subroutines:
703              
704             # ---------------------------------------------------------------------- #
705              
706             ## Done.
707              
708             1;
709              
710             # End of file