File Coverage

blib/lib/Fortran/Namelist.pm
Criterion Covered Total %
statement 6 363 1.6
branch 0 100 0.0
condition 0 52 0.0
subroutine 2 23 8.7
pod 0 21 0.0
total 8 559 1.4


line stmt bran cond sub pod time code
1             package Fortran::Namelist;
2              
3             our $VERSION = '0.10';
4              
5 1     1   5740 use Carp;
  1         1  
  1         55  
6 1     1   5 use Scalar::Util qw(looks_like_number);
  1         1  
  1         4309  
7              
8             our @nml_groups=();
9              
10             sub new {
11 0     0 0   my $class = shift;
12 0           my %opt = @_;
13 0           my $self = {};
14 0           $self->{nml} = {};
15 0           $self->{groups} = [];
16 0           bless $self,$class;
17 0           return $self->init(%opt);
18             }
19              
20             sub init {
21 0     0 0   my $self = shift;
22 0           my %opt = (
23             file => '',
24             fh => '',
25             nml => '',
26             @_,
27             );
28 0 0 0       if ( $opt{nml} && ref $opt{nml} eq 'HASH' ) {
29 0           $self->set( %{$opt{nml}} );
  0            
30             }
31 0 0 0       if ( $opt{file} || $opt{fh} ) {
32 0 0         $self->load( %opt ) or croak "Error loading namelist\n";
33             }
34 0           return $self
35             }
36              
37             sub nml {
38 0     0 0   my $self = shift;
39 0   0       my $grp_name = shift ||'';
40 0   0       my $var_name = shift ||'';
41 0           ( $grp_name ) = grep { lc $grp_name eq lc $_ } %{$self->{nml}};
  0            
  0            
42 0           ( $var_name ) = grep { lc $var_name eq lc $_ } %{$self->{nml}{$grp_name}};
  0            
  0            
43 0 0 0       my $nml = $grp_name && $var_name ? $self->{nml}{$grp_name}{$var_name} :
    0 0        
44             $grp_name && ! $var_name ? $self->{nml}{$grp_name} :
45             $self->{nml};
46 0           return $nml
47             }
48             sub set {
49 0     0 0   my $self = shift;
50 0           my %opt = (
51             @_,
52             );
53 0 0         if ( ! keys %opt ) {
54 0           return;
55             }
56 0           my $new = $self->read_group(%opt);
57 0           my %seen;
58 0           my @grp_names = keys %{$self->{nml}};
  0            
59 0           my (%hg,%hv,$grp,$gname);
60 0           @hg{ map { lc } @grp_names }= @grp_names;
  0            
61 0           foreach my $g ( keys %{$new} ){
  0            
62             # create the group if doesn't exist
63 0 0         if ( ! exists $hg{ lc $g } ) {
64 0           $self->new_group( $g ,$new->{$g});
65            
66             }
67             else {
68 0           $gname = $hg{ lc $g};
69 0           my $hlines = $self->parse_group($gname);
70 0           my $grp = $self->group($gname);
71 0           my @var_names = keys %{$self->{nml}{ $gname }};
  0            
72 0           @hv{ map { lc } @var_names }= @var_names;
  0            
73 0           my $vars = [];
74 0           %seen=();
75 0           foreach my $h ( @$hlines ) {
76 0           ($vname) = keys %$h;
77 0           my ($v) = grep { lc $_ eq lc $vname } keys %{$new->{$g}};
  0            
  0            
78             #$self->assign_hvar($gname,$vname,$h);
79             #my $vval = $self->{nml}{$gname}{$vname};
80             #push @{$vars},@{$self->new_vars($gname,$self->{nml}{$gname} ) };
81 0 0         if ( $v ) {
82 0 0         next if $seen{$v}++;
83 0           my $hh = { $vname => {} };
84 0           $hh->{$vname} = $new->{$g}{$v};
85 0           $self->assign_hvar($gname,$vname,$hh );
86             }
87             #next if $seen{$vname}++;
88 0           my ($idx,$val) = $self->unassign($self->{nml}{$gname}{$vname});
89 0           my $j = 0;
90 0           foreach my $i ( @$idx ) {
91 0           my $line = $self->write_var("$vname$i",$val->[$j++]);
92 0           push @{$vars},$line;
  0            
93             }
94             }
95 0           foreach my $v ( grep { ! exists $hv{lc $_} } keys %{$new->{$g}} ) {
  0            
  0            
96 0           my $h = { $v => $new->{$g}{$v} };
97 0           push @$vars, @{$self->new_vars($gname,$h)};
  0            
98             }
99 0           $grp->{vars}=$vars;
100             }
101             }
102             return
103 0           }
104              
105             sub assign_hvar {
106 0     0 0   my $self = shift;
107 0           my $gname = shift;
108 0           my $vname = shift;
109 0           my $h = shift;
110 0           my ($nidx,$nval) = ([],[]);
111 0           ($nidx,$nval) = $self->unassign($h->{$vname});
112 0           my $j=0;
113 0 0         $self->{nml}{$gname}{$vname} = {} if ! exists $self->{nml}{$gname}{$vname};
114 0           foreach my $ni ( @$nidx ) {
115 0           $ni =~ s/^\(//;
116 0           $ni =~ s/\)$//;
117 0           my $ii = [split /,/,$ni];
118 0           $self->assign($self->{nml}{$gname}{$vname},$ii,$nval->[$j++]);
119             }
120 0           return $self->{nml}{$gname}{$vname}
121             }
122              
123              
124             sub read_group {
125 0     0 0   my $self = shift;
126 0           my %grp = @_;
127 0           foreach my $v ( keys %grp ) {
128 0           $grp{$v}= $self->fix_val( $grp{$v} );
129             }
130 0           return \%grp
131             }
132              
133             sub fix_val {
134 0     0 0   my $self = shift;
135 0           my $val = shift;
136 0 0         if ( ref ($val) eq 'HASH' ) {
    0          
137 0           foreach my $k ( keys %$val ) {
138 0           $val->{$k} = $self->fix_val( $val->{$k} );
139             }
140             }
141             elsif ( ref $val eq 'ARRAY' ) {
142 0           foreach my $v ( @$val ) {
143 0           $v = trim($v);
144 0           $v =~ s/^'//;
145 0           $v =~ s/'$//;
146 0 0         if ( ! looks_like_number($v) ) {
147 0           $v = "'$v'" ;
148             }
149             }
150             }
151 0           return $val
152             }
153             sub print {
154 0     0 0   my $self = shift;
155 0           my %opt = (
156             group => '',
157             file => '',
158             fh => \*STDOUT,
159             @_,
160             );
161 0           my $fh = $opt{fh};
162 0 0         if ( $opt{file} ) {
163 0           $fh = \*FILE;
164 0 0         open( $fh ,'>', $opt{file} ) or
165             croak "couldn't open file: $opt{file} $!\n"
166             }
167 0           my $groups = $self->{groups};
168 0           foreach my $g ( @$groups ) {
169 0 0         if ( exists $g->{comment} ) {
170 0           print $fh "$g->{comment}\n";
171 0           next;
172             }
173 0 0 0       next if $opt{group} && lc $opt{group} ne lc $g->{name};
174 0           print $fh "$g->{ch_start}$g->{name}\n\n";
175 0           foreach my $v ( @{$g->{vars}} ) {
  0            
176 0           print $fh "$v\n";
177             }
178 0           print $fh "\n$g->{ch_end}\n";
179             }
180             }
181              
182              
183             sub write_var {
184 0     0 0   my $self = shift;
185 0           my $lhs = shift;
186 0           my $rhs = shift;
187 0   0       my $nl = shift || 80;
188 0           my @l;
189 0 0         my $eq = trim($lhs) ? '=' : ' ' ;
190 0           $l[0] = " $lhs $eq ";
191 0           my $line = '';
192 0           my $lpad = ' 'x(length $l[0]);
193 0 0         my $sep = @$rhs > 1 ? ', ': '' ;
194 0           my @r = @$rhs;
195 0           while ( @r ) {
196 0           my $v = shift @r;
197 0           my $lvs=length( "$l[-1]$sep$v$sep");
198 0 0         if ( @r ) {
199 0 0         if ( $lvs < $nl ) {
200 0           $l[-1] .= "$v$sep";
201             }
202             else {
203 0           $l[-1] .= "\n";
204 0           push @l, "$lpad$v$sep";
205             }
206             }
207             else {
208 0 0         if ( $lvs < $nl ) {
209 0           $l[-1].= $v;
210             }
211             else {
212 0           $l[-1] .= "\n";
213 0           push @l, "$lpad$v";
214             }
215             }
216             }
217 0           $line = join '',@l;
218 0           return $line
219             }
220              
221             sub load {
222 0     0 0   my $self = shift;
223 0           my %opt = (
224             fh => '',
225             file => '',
226             nml => '',
227             @_,
228             );
229            
230 0 0 0       $self->{nml} = $opt{nml} && ref $opt{nml} eq 'HASH' ? $opt{nml} : {};
231 0           my ($gname,$var,$val,$type,$ch_start,$ch_end);
232 0           my $fh = $opt{fh};
233 0 0         if ( $opt{file} ) {
234 0 0         open( $fh ,"<",$opt{file} ) or
235             croak "Couldn't open $opt{file} for reading $!\n";
236             }
237 0           my $lines=[''];
238 0           my $comment='';
239 0           $ch_start='';
240 0           $ch_end='';
241 0           my $l='';
242 0           while ( <$fh> ) {
243 0           $l = $_;
244 0           chomp;
245 0           chomp $l;
246 0 0         if ( /^(\s*\!.*)$/ ) {
247 0           $comment = $l;
248 0 0         if ( ! $ch_start ) {
249 0           push @{$self->{groups}},{ comment => $comment };
  0            
250             }
251             else {
252 0           push @{$lines},$comment;
  0            
253             }
254 0           $comment='';
255             }
256 0           s/\!.*$//;
257 0           s/^\s+//;
258 0           s/\s+$//;
259 0 0         next unless length;
260 0 0         if ( /(\$|\&)(\w+)(.*)(\$end|\/)/ixmsg ) {
261 0           $ch_start = $1;
262 0           $gname = $2;
263 0           $ch_end = $4;
264 0           $lines = [ trim($3) ];
265 0           $self->{nml}{$gname}={};
266 0           push @{$self->{groups}},{ name => $gname,
  0            
267             ch_start => $ch_start ,
268             ch_end => $ch_end,
269             vars => $lines,
270             };
271 0           $lines = [];
272 0           $ch_start='';
273 0           $ch_end='';
274 0           next;
275             }
276              
277 0 0         if ( /(\$end|\/)$/i ) {
278 0           $ch_end = $1;
279 0           push @{$self->{groups}},{ name => $gname,
  0            
280             ch_start => $ch_start ,
281             ch_end => $ch_end,
282             vars => $lines,
283             };
284             #print join "\n",@lines,"\n";
285             #if ( ! $opt{nml} || $group eq $opt{nml} )
286             #$self->parse_group( $group, @lines );
287 0           $ch_start='';
288 0           $ch_end='';
289 0           next;
290             }
291 0 0         if ( /^(\$|\&)([A-Za-z]\w+)( |$)/ ) {
292 0           $ch_start = $1;
293 0           $gname = $2;
294 0           $self->{nml}{$gname}={};
295             #print "$group $_\n";
296 0           $lines = [];
297             #$nml->{$group} = [];
298             #$nml->{$group}{comment} = [$comment] if $comment;
299             #$comment = '';
300 0           next;
301             }
302 0           push @$lines,$l;
303             }
304 0 0 0       my $ok = $ch_start || $ch_end ? 0 : 1;
305            
306 0           return $ok
307             }
308             sub new_group {
309 0     0 0   my $self = shift;
310 0           my $name = shift;
311 0           my $h = shift ;
312 0           my $g = { name => $name,
313             ch_start => '$' ,
314             ch_end => '$end',
315             vars => $self->new_vars($name,$h),
316             };
317 0 0         $self->{nml}{$name}= {} if ! exists $self->{nml}{$name};
318 0           foreach my $v ( keys %$h ) {
319 0           $self->{nml}{$name}{$v}={};
320 0           $self->{nml}{$name}{$v}=$self->assign_hvar($name,$v,$h);
321             }
322 0           push @{$self->{groups}},$g;
  0            
323 0           return $g
324             }
325             sub new_vars {
326 0     0 0   my $self = shift;
327 0           my $gname = shift;
328 0           my $h = shift;
329 0           my ($idx,$val,$j);
330 0           my $vars = [];
331 0           foreach my $var ( keys %{$h} ) {
  0            
332 0           ($idx,$val) = $self->unassign($h->{$var});
333 0           $j=0; #print $var,"\n";
334 0           foreach my $i ( @$idx ) {
335 0           my $line = $self->write_var("$var$i",$val->[$j++]);
336 0           push @{$vars},$line;
  0            
337             }
338             }
339 0           return $vars
340             }
341             sub group {
342 0     0 0   my $self = shift;
343 0   0       my $gname = shift || croak "No group name given!\n";
344 0           my $g = {};
345 0           ($g) = grep { lc $_->{name} eq lc $gname } @{$self->{groups}};
  0            
  0            
346 0 0         if ( ! $g ) {
347 0           carp "Group $gname not found\n";
348             }
349 0           return $g
350             }
351             sub group_lines {
352 0     0 0   my $self = shift;
353 0   0       my $gname = shift || croak "No group name given!\n";
354 0   0       my $var = shift || '';
355 0           my @lines = ('');
356 0           my $g = $self->group($gname);
357 0           foreach my $l ( @{$g->{vars}} ) {
  0            
358 0           chomp $l;
359 0           $l=~ s/\!.*$//;
360 0 0         if ( $l !~ /=/ ) {
361 0           $lines[-1] .= " $l";
362 0           next;
363             }
364             else {
365 0           push @lines,$l;
366             }
367             }
368             return \@lines
369 0           }
370              
371              
372             sub parse_group {
373 0     0 0   my $self = shift;
374 0           my $gname = shift;
375 0   0       my $vname = shift || '';
376 0           my $group_lines = $self->group_lines($gname);
377 0           my $group = $self->group($gname);
378 0           my $vars = [];
379 0           my $vlines =[];
380 0           my $comment = '';
381 0           foreach my $l ( @$group_lines ) {
382             #print "$l\n";
383 0 0         if ( $l !~ /^\s*\!/ ) {
384 0           my $hl = $self->parse_vars($gname,$l);
385             #foreach my $h ( @$hl ) {
386             # my ($vname) = keys %$h;
387             # $self->assign_hvar($gname,$vname,$h);
388             # push @$vars,@{$self->new_vars($gname,$h) };
389             #}
390 0           push @$vlines,@$hl;
391             }
392             else {
393 0           push @$vars,$l;
394             }
395             }
396 0           $group->{vars} = $vars;
397 0           return $vlines;
398             }
399              
400              
401             sub parse_vars {
402 0     0 0   my $self = shift;
403 0           my $gname = shift;
404 0           my $line = shift;
405 0           my $v=[];
406             # [A-Za-z_0-9\(\),\s\#\-\:]+
407             # \( [\s0-9\.DdEe\-\+]+ , [\s0-9\.DdEe\-\+]+ \)
408 0           while ( $line =~ / (
409             [A-Za-z_0-9]+
410             (\s*|
411             \( \s*
412             (
413             \s*\d\s*(,|\s*|)
414             )+
415             \s* \)
416             )\s*
417             =
418             (
419             (\s*\d+\s*\*|)
420             (
421             \s*\'.*\'\s* |
422             [\s\-\+A-Za-z_0-9\.'\:,]+ |
423             \s*\(
424             [\s0-9\.DdEeIi\-\+]+
425             \s*,\s*
426             [\s0-9\.DdEeIi\-\+]+
427             \)\s*
428             )
429             (,|,\s*|$)
430             |
431             )+
432            
433             )+
434             /xmsg ) {
435             #print "line : $line\n";
436 0           ($var,$val) = split /=/,$1;
437             #print "parsing $var = $val\n";
438 0           my $index=[];
439 0           ($var,$index) = $self->var_index($var);
440 0           my @i= @$index;
441            
442             #print "index = @$index\n";
443 0           $val = trim($val);
444 0           $val = $self->parse_val($val,$var);
445 0           my $h = { $var => {} };
446 0 0         if ( ! exists $self->{nml}{$gname}{$var} ) {
447 0           $self->{nml}{$gname}{$var} = {};
448             }
449 0           $self->assign($self->{nml}{$gname}{$var},$index,$val);
450 0           $self->assign($h->{$var},$index,$val);
451             #$h->{$var} = $self->{nml}{$gname}{$var};
452             #print "$var@{$index}= @{$h->{$var}} \n";
453             #print "var $var @{$i} = @{$val}\n";
454 0           push @$v , $h;
455             }
456 0           return $v
457             }
458             sub unassign {
459 0     0 0   my $self = shift;
460 0           my $h = shift;
461 0   0       my $index = shift || [''];
462 0   0       my $idx = shift || [];
463 0   0       my $vals = shift || [];
464 0           my $v = '';
465 0           my $l = [];
466 0 0         my @k = ref $h eq 'HASH' ? sort { $a <=> $b } keys %$h : ();
  0            
467             #print "keys @k\n";
468             #print "$index->[-1]\n";
469 0 0         if ( @k ) {
470 0           my $ii = $index->[-1];
471 0           my $i=$k[0];
472 0           while ( @k ) {
473 0           $i=shift @k;
474 0           push @{$index},$ii ;
  0            
475 0           $index->[-1] .= " $i";
476             #print "$index->[-1]\n";
477             #push @$vals,$h->{$i} if ref $h->{$i} eq 'ARRAY';
478 0           $self->unassign($h->{$i},$index,$idx,$vals);
479             }
480             }
481             else {
482 0           my @ii = split ' ',$index->[-1];
483 0   0       my $j = (join ',', @ii) || '';
484 0 0         $index->[-1] = $j ? "($j)" : '';
485 0           push @$idx,$index->[-1];
486 0           push @$vals,$h;
487             }
488              
489 0           return ($idx,$vals)
490             }
491              
492             sub assign {
493 0     0 0   my $self = shift;
494 0           my $h = shift;
495 0           my $index = shift;
496 0           my $val = shift;
497 0 0         if ( @$index ) {
    0          
498 0           my $i = shift @$index;
499 0 0         $h->{$i}= ! @$index ? $val :
    0          
500             exists $h->{$i} ? $h->{$i} :
501             {} ;
502 0           $self->assign( $h->{$i},$index,$val );
503             }
504             elsif ( ref $h eq 'HASH' ) {
505 0           my ($i) = keys %$h;
506 0           $h->{$i}=$val;
507             }
508             else {
509 0           $h = $val;
510             }
511              
512 0           return $h
513             }
514              
515             sub var_index {
516 0     0 0   my $self = shift;
517 0           my $var = shift;
518 0           $var =~ s/\s+//g;
519 0           my $index = [];
520 0 0         if ( $var =~ s/(
521             \(.*\) # match indexes between parenthesis
522             )
523             (| # nothing or,
524             \(\d*:\d*\) # substring indexes, but we will not use them
525             # for now
526             )
527             //x ) {
528 0           my $i= $1;
529 0           $i =~ s/(\(|\))//g;
530 0           $index = [ split /,/,$i ];
531             #print "index @{$index}\n";
532             }
533             #$index =~ s/\s+//g if $index ;
534 0           return (trim($var),$index);
535             }
536              
537             sub parse_val {
538 0     0 0   my $self = shift;
539 0           my $val = shift;
540 0   0       my $var = shift ||'';
541 0           my $values = [];
542 0           my $all = $val ;
543 0           my $ok = 1;
544 0 0         return [$val] if $val =~ /\.(true|false)\./i ;
545 0           while ( $val =~ / (\s*,\s*|\s*) # match starting null value
546             ((\s*\d+\s*)\*|) # match multiplier
547             ( # begin matching values
548             \s*\'.*?\'\s* | # quoted string
549             [DdEe_0-9\.\-\+\:]+ | # numeric variable
550             \s*\( # start complex number
551             [\s0-9\.DdEeIi\-\+]+ # real part
552             \s* , \s* # comma
553             [\s0-9\.DdEeIi\-\+]+ # imaginary part
554             \)\s* | # end complex number
555             \s*,\s* # separator
556             ) # end matching values
557             ( # begin separators:
558             \s*,\s* | # match null value ',,'
559             \s* | # blanks spaces,tabs,etc
560             $ # end of string or new line
561             ) # end separators
562             /xmsg ) {
563 0           my $nv = $1;
564 0           my $ntimes = $2;
565 0   0       my $n = $3 || 1;
566 0           my $c = $4 ;
567 0           my $sep = $5;
568 0           my $pv = $c;
569 0           $nv = trim($nv);
570 0 0         push @$values,$nv if $nv;
571            
572             #$c = ! looks_like_number($c) ? "'$c'" : $c;
573             #print "! sep= |$sep| bnv = |$nv| rep = |$n| |$ntimes| ";
574             #print "! parsing : $var = |$c|\n";
575 0           $pv =~ s/(\+|\(|\)|\.)/\\$1/g;
576 0           $ntimes =~ s/(\*)/\\$1/g;
577 0           $all =~ s/($nv$ntimes$pv$sep)?//;
578 0 0 0       $ok = ! $sep && $c eq ',' ? 0 : 1;
579             #push @$values,split(/,/,$c);
580 0 0         push @$values,(trim($c))x($n) if $ok ;
581             #my $vv = [ split /$sep/,$val ];
582             #foreach my $c ( @$vv ) {
583             # print "var $var| val $val|c $c \n";
584             #}
585             }
586 0 0         if ( trim($all) ) { print "left overs |$all|\n";}
  0            
587             #print "@$values ",scalar @$values,"\n";
588             #return scalar @$values > 1 ? $values : $values->[0];
589 0           return $values
590             }
591              
592             sub trim {
593 0     0 0   my $s = shift;
594 0           $s =~ s/^\s+//;
595 0           $s =~ s/\s+$//;
596 0           return $s
597             }
598             1;
599              
600             __END__