File Coverage

blib/lib/Fortran/F90Namelist.pm
Criterion Covered Total %
statement 461 542 85.0
branch 126 172 73.2
condition 70 99 70.7
subroutine 46 50 92.0
pod 8 25 32.0
total 711 888 80.0


line stmt bran cond sub pod time code
1             #
2             # F90Namelist.pm
3             # --------------
4             #
5             # Description:
6             # Parse F90 namelist into a hash and export in different formats.
7             # Author: wd (Wolfgang.Dobler@kis.uni-freiburg.de)
8             # $Date: 2006/12/18 23:16:04 $
9             # $Revision: 1.14 $
10             # [Date and CVS revision are now pretty irrelevant, as I keep the code
11             # under Darcs now]
12              
13             package Fortran::F90Namelist;
14              
15             =head1 NAME
16              
17             Fortran::F90Namelist - Parse F90 namelists into hash and export in different formats
18              
19             =head1 SYNOPSIS
20              
21             use Fortran::F90Namelist;
22             my $nl = Fortran::F90Namelist->new() or die "Couldn't get object\n";
23              
24             $nl->parse("&runpars\nx=2,y=3\nvec1=1,2,3\nvec2=3*1.3\n/");
25              
26             # Operate on each namelist in $text (only works with [mutable]
27             # strings, not with files)
28             my $text = "&spars\nx=2,y=3\n/\n&runpars\nvec1=1,2,3\nvec2=3*1.3\n/";
29             while ($nl->parse($text)) {
30             print $nl->name(), "\n";
31             }
32              
33             Dump in arbitrary order:
34              
35             use Data::Dumper;
36             print "F90Namelist ", $nl->name(), " has ", $nl->nslots(), " slots:\n";
37             print Dumper($nl->hash());
38              
39             Retain original order:
40              
41             print "&",$nl->name(),"\n";
42             my $nl_hash = $nl->hash();
43             foreach my $var (@{$nl->slots()}) {
44             print " $var: ", Dumper($nl_hash->{$var});
45             }
46             print "/\n";
47              
48             Read from file:
49              
50             # Read one namelist from file `one_list.nml'
51             $nl->parse(file => 't/files/one_list.nml');
52              
53             # Read one namelist from file handle
54             open(my $fh , "< t/files/one_list.nml") or die "Couldn't get file handle\n";
55             $nl->parse(file => $fh);
56             # or
57             open(NAMELIST , "< t/files/one_list.nml") or die "Couldn't open file\n";
58             $nl->parse(file => \*NAMELIST);
59              
60             Read all namelists from file `start.in' and merge into one namelist
61             called `nlist'
62              
63             $nl->parse(file => 't/files/start.in',
64             all => 1,
65             namelist => 'nlist');
66             print "Merged namelist ", $nl->name, " contains:\n",
67             join(", ", @{$nl->slots}), "\n";
68              
69             Merge two namelists
70              
71             my $nl2 = Fortran::F90Namelist->new() or die "Couldn't get object\n";
72             $nl2->parse(file => 't/files/one_list.nml');
73             $nl->merge($nl2,
74             { dups_ok => 1 } );
75             print $nl->name, " now has ", $nl->nslots, " slots\n";
76              
77              
78             Write namelist:
79              
80             # Write namelist in F90 namelist format
81             print "F90 format:\n", $nl->output();
82              
83             # Write namelist as IDL structure
84             print "IDL format:\n", $nl->output(format => 'idl', name => 'par2');
85              
86              
87             =head1 DESCRIPTION
88              
89             Fortran::F90Namelist is a module for parsing Fortran90 namelists into hashs and
90             re-exporting these hashs in different formats. Currently, the following
91             data types are supported:
92              
93             =over 4
94              
95             =item *
96              
97             integer
98              
99             =item *
100              
101             float/double
102              
103             =item *
104              
105             complex numbers
106              
107             =item *
108              
109             strings [character(LEN=*)], possibly containing all sorts of quotation
110             marks
111              
112             =item *
113              
114             logical
115              
116             =back
117              
118             The following backends exist for re-exporting (or importing into other
119             languages):
120              
121             =over 4
122              
123             =item *
124              
125             F90 namelist
126              
127             =item *
128              
129             IDL struct
130              
131             =back
132              
133             This module is used with the I
134             (L) to import the values of
135             all available input parameters into GDL/IDL or other visualization
136             software.
137              
138             =head2 Methods
139              
140             =over 4
141              
142              
143             =item B<$nl-Enew>()
144              
145             Create a new namelist object
146              
147              
148             =item B<$nl-Eparse>(I)
149              
150             =item B<$nl-Eparse>(text => I)
151              
152             =item B<$nl-Eparse>(file =>(I|I))
153              
154             =item B<$nl-Eparse>(file => (I|I) [, I ])
155              
156             =item B<$nl-Eparse>(\%options)
157              
158             Parse I or the file represented by I or I (a file
159             handle), returns the name of the namelist parsed, or undef if parsing
160             failed.
161              
162             When reading from a mutable text string $text, the string is modified and
163             contains everything following the namelist just parsed.
164              
165             This allows C loops like
166              
167             while ($nl->parse($text)) {
168             print $nl->name(), "\n";
169             }
170              
171             to work.
172             This does however not work for files or immutable strings, so
173              
174             =for test ignore
175              
176             while ($nl->parse(file => "t/files/start.in")) {
177             print $nl->name(), "\n";
178             }
179              
180             =for test
181              
182             and
183              
184             =for test ignore
185              
186             while ($nl->parse("&nl1\nx=5.\n/\n&nl2\n/")) {
187             print $nl->name(), "\n";
188             }
189              
190             =for test
191              
192             will fail.
193              
194             Generally speaking, L
195             is the more appropriate tool for handling several namelists in one file or
196             string.
197              
198              
199             Additional I are:
200              
201             =over 8
202              
203             =item B
204              
205             If true, merge data from namelist with any data that may already be
206             stored in the object.
207             See L for a more
208             flexible framework for dealing with groups of namelists.
209              
210             =item B
211              
212             If true, parse all namelists from string or file and merge them into one
213             namelist object.
214              
215             =item B
216              
217             Set name of resulting namelist (default: name of first namelist read).
218              
219             =item B
220              
221             With B, don't warn if new slots have same names, but different
222             values as existing slots.
223              
224             =item B
225              
226             Try to parse broken namelists as produced by ifc 7.x, where you can get
227             something like
228              
229             =for test ignore
230              
231             COOLING_PROFILE='gaussian ',COOLTYPE='Temp
232             'COOL= 0.0,CS2COOL= 0.0,RCOOL= 1.000000
233              
234             =for test
235              
236             if the closing quote for a string (`Temp ') would end up in column 81.
237              
238             All options can be passed in a hash(-ref):
239              
240             my %options = ( file => 't/files/one_list.nml',
241             name => 'broken_nlist',
242             broken => 1 );
243             $nl->parse(%options);
244             $nl->parse(\%options); # the same
245              
246             =back
247              
248              
249             =item B<$nl-Emerge>($nl2 [, I])
250              
251             Merge namelist object $nl2 into $nl.
252              
253             I are:
254              
255             =over 8
256              
257             =item B
258              
259             Set name of resulting namelist (default: name of $nl).
260              
261             =item B
262              
263             With B, don't warn if new slots have same names, but different
264             values as existing slots.
265              
266             =back
267              
268              
269             =item B<$nl-Ename>()
270              
271             =item B<$nl-Ename>($newname)
272              
273             Return or set name of namelist.
274              
275              
276             =item B<$nl-Enslots>()
277              
278             Return number of slots in namelist
279              
280              
281             =item B<$nl-Eslots>()
282              
283             Return ref to list of variable (slot) names in original order
284              
285              
286             =item B<$nl-Ehash>()
287              
288             Return namelists as Perl hashref.
289             See L below for details of the hash format.
290              
291              
292             =item B<$nl-Eoutput>([options])
293              
294             Write namelist in given I.
295              
296             Options are
297              
298             =over 8
299              
300             =item B=I
301              
302             Set the output format.
303             Currently supported formats are `f90' (default), and `idl'.
304              
305             =item B=I
306              
307             Set the name of the namelist (default: C<$nl-Ename>()).
308              
309             =item B
310              
311             Trim all trailing whitespace from strings.
312              
313             =item B
314              
315             Write all floating point numbers as double precision numbers.
316              
317             =item B
318              
319             Print whole namelist in one line (if compatible with the output format).
320              
321             =item B=I
322              
323             Print only N slots per line.
324             Useful for programs like IDL that have restrictions on the length of lines
325             read from a pipe, so B is dangerous.
326              
327             =back
328              
329             =back
330              
331              
332             =head1 HASH FORMAT
333              
334             The B method returns a hash reference of the following structure:
335              
336             =for test ignore
337              
338             { 'name of var1' => { 'value' => [ value1, value2, ..],
339             'type' => numerical_type,
340             'stype' => "type string"
341             },
342             'name of var2' => { 'value' => [ value1, value2, ..],
343             'type' => numerical_type
344             'stype' => "type string"
345             },
346             ...
347             }
348              
349             =for test
350              
351             Here I is a number identifying each data type, while
352             I is a textual description of the given data type.
353              
354             E.g.
355              
356             =for test ignore
357              
358             { 'xyz0' => { 'value' => [ 0., -3.141593, 0. ],
359             'type' => 6,
360             'stype' => 'single precision float'
361             },
362             'nt' => { 'value' => [ '1000' ],
363             'type' => 4,
364             'stype' => 'integer'
365             }
366             }
367              
368             =for test
369              
370             Note: This is currently just the internal format used to represent
371             namelists and can thus change in the future.
372             In particular the C numbers should not considered to be stable
373             between releases.
374              
375              
376             =head1 TO DO
377              
378             =over 4
379              
380             =item 1.
381              
382             new(), parse(), output(), etc. should check for unknown args and complain,
383             not silently ignore them as is currently the case.
384              
385             =item 2.
386              
387             More output methods:
388              
389             =over 8
390              
391             =item *
392              
393             Octave/matlab , C structs, YAML, XML(?), ...
394              
395             =back
396              
397             =back
398              
399              
400             =head1 BUGS AND LIMITATIONS
401              
402             =over 4
403              
404             =item *
405              
406             No user-defined types (records) are supported, so if you have these LaTeX
407             comment characters in your namelist data, you are out of luck.
408              
409             =back
410              
411              
412             =head1 AUTHOR
413              
414             Wolfgang Dobler
415              
416              
417             =head1 LICENSE AND COPYRIGHT
418              
419             Copyright (c) 2007, Wolfgang Dobler .
420             All rights reserved.
421              
422             This program is free software; you can redistribute it and/or modify it
423             under the same conditions as Perl or under the GNU General Public
424             License, version 2 or later.
425              
426              
427             =head1 DISCLAIMER OF WARRANTY
428              
429             Use completely at your own risk.
430              
431              
432             =head1 SEE ALSO
433              
434             L by Victor Marcello Santillan.
435             That module has a more limited scope (reading a namelist group from file,
436             inserting namelists, and writing the resulting group to another file [my
437             interpretation]), but is way faster on large files.
438              
439             =cut
440              
441              
442 27     27   566992 use strict;
  27         108  
  27         1710  
443 27     27   148 use Carp;
  27         59  
  27         2679  
444 27     27   144 use vars qw($VERSION);
  27         59  
  27         1734  
445              
446             # Cannot use use Perl5.8's constant { x => 1, y=>2 , ..} because 5.6
447             # is very popular still
448             #
449             # Possible states of parser [used at all?]
450 27     27   175 use constant UNDEF => -1;
  27         48  
  27         3160  
451 27     27   138 use constant START => 0; # initial state of parser
  27         50  
  27         1268  
452 27     27   125 use constant VAR => 1; # at beginning of variable name
  27         53  
  27         1316  
453 27     27   129 use constant VALUE => 2; # at beginning of value
  27         53  
  27         1220  
454 27     27   197 use constant SQUOTE => 3; # in string after opening single quote
  27         51  
  27         1277  
455 27     27   126 use constant DQUOTE => 4; # in string after opeing double quote
  27         50  
  27         1278  
456 27     27   375 use constant BRACKET => 5; # after opening bracket (e.g. complex number)
  27         65  
  27         1150  
457 27     27   246 use constant COMMENT => 6; # after exclamation mark (F90 comment)
  27         45  
  27         11177  
458 27     27   276 use constant NL_END => 7; # after closing `/'
  27         52  
  27         3161  
459             #
460             # F90 data types
461 27     27   237 use constant UNKNOWN => 0;
  27         42  
  27         1580  
462 27     27   135 use constant SQ_STRING => 1;
  27         41  
  27         1149  
463 27     27   247 use constant DQ_STRING => 2;
  27         65  
  27         1140  
464 27     27   122 use constant LOGICAL => 3;
  27         49  
  27         1044  
465 27     27   302 use constant INTEGER => 4;
  27         91  
  27         1288  
466 27     27   148 use constant FLOAT => 5; # a float here can be single or double
  27         47  
  27         1106  
467 27     27   122 use constant SINGLE => 6;
  27         45  
  27         1181  
468 27     27   127 use constant DOUBLE => 7;
  27         53  
  27         1117  
469 27     27   205 use constant COMPLEX => 8;
  27         44  
  27         1180  
470 27     27   150 use constant DCOMPLEX => 9;
  27         81  
  27         1526  
471 27     27   179 use constant MULTIPLE => 20;
  27         54  
  27         1164  
472             #
473 27     27   145 use constant ID => 100; # variable name (_not_ a data type)
  27         44  
  27         201911  
474              
475              
476             $VERSION = '0.5.1';
477              
478             ## Regexps for integer and floating-point numbers
479             # general float:
480             my $numeric = qr/(?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[EeDd](?:[+-]?\d+))?/;
481             # float:
482             my $numeric_e = qr/(?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?/;
483             # double:
484             my $numeric_d = qr/(?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Dd](?:[+-]?\d+))?/;
485             # float with decimal point, but w/o exponential part:
486             my $float = qr/(?:[-+]?(?:\d+\.\d*|\d*\.\d+))/;
487              
488             ## Extend floating-point numeric tpes by one- or two-point
489             ## compactification of real numbers (any mathematicians here?), aka IEEE
490             ## denormalized numbers (for engineers):
491             my $NaN = qr/NaN/;
492             my $Inf = qr/(?:[-+]?)Inf/;
493             my $ieee_denorm = qr/(?:$NaN|$Inf)/;
494             #$numeric_e = qr/(?:$numeric_e|$ieee_denorm)/;
495             #$numeric_d = qr/(?:$numeric_d|$ieee_denorm)/;
496             $numeric = qr/(?:$numeric|$ieee_denorm)/;
497             $float = qr/(?:$float|$ieee_denorm)/;
498              
499             ## Regexps for the different data type values. Make sure all brackets are
500             ## marked grouping-but-non-capturing (?:...), or else the parsing
501             ## algorithm will fail.
502             my @regexp;
503             $regexp[SQ_STRING] = qr/'(?:[^']|'')*'/; # even covers 'toto''s quote'
504             $regexp[DQ_STRING] = qr/"(?:[^"]|"")*"/; # ditto for double quotes
505             $regexp[DCOMPLEX] = qr/\(\s*$numeric_d\s*,\s*$numeric_d\s*\)/;
506             $regexp[COMPLEX] = qr/\(\s*$numeric\s*,\s*$numeric\s*\)/;
507             $regexp[LOGICAL] = qr/(?:T|F|\.(?:true|TRUE|false|FALSE)\.)/;
508             $regexp[MULTIPLE] = qr/[0-9]+\*/; # also need special treatment...
509             $regexp[INTEGER] = qr/[+-]?[0-9]+/;
510             $regexp[DOUBLE] = qr/$numeric_d/;
511             $regexp[SINGLE] = qr/$numeric_e/;
512             $regexp[FLOAT] = qr/$float/;
513             $regexp[ID] = qr/[a-zA-Z](?:[a-zA-Z0-9_])*/; # allowed namelist/var. names
514              
515             ## Corresponding regexp for compatible type class (numeric, complex, ...)
516             my @regexp2 = @regexp; # same regexp by default
517             $regexp2[DCOMPLEX] = qr/\(\s*$numeric\s*,\s*$numeric\s*\)/;
518             $regexp2[COMPLEX] = qr/\(\s*$numeric\s*,\s*$numeric\s*\)/;
519             $regexp2[INTEGER] = qr/$numeric/;
520             $regexp2[DOUBLE] = qr/$numeric/;
521             $regexp2[SINGLE] = qr/$numeric/;
522             $regexp2[FLOAT] = qr/$numeric/;
523              
524             # Hash for looking up symbolic names for type constants. The constants are
525             # only expanded as numbers if adding 0.
526             my %stypes = ( UNKNOWN + 0 => 'unknown',
527             SQ_STRING + 0 => 'single-quote string',
528             DQ_STRING + 0 => 'double-quote string',
529             LOGICAL + 0 => 'logical',
530             INTEGER + 0 => 'integer',
531             FLOAT + 0 => 'unspecified float',
532             SINGLE + 0 => 'single precision float',
533             DOUBLE + 0 => 'double precision float',
534             COMPLEX + 0 => 'complex number',
535             DCOMPLEX + 0 => 'double precision complex number',
536             MULTIPLE + 0 => 'multiple data (array)',
537             );
538              
539             # Global variables related to output() method:
540             my ($cmplx_pref,$cmplx_suff) = ('', ''); # default delimiters for complex nums
541              
542             # ---------------------------------------------------------------------- #
543             ##
544             ## Object constructor
545             ##
546             ## Internal structure of Namlist objects (update me):
547             ## DATA -- variable names, values, and types (hashref, see below)
548             ## SLOTS -- ordered list of variable names (array ref)
549             ## NSLOTS -- number of slots
550             ## NAME -- name of namelist
551             ## PARSED_ -- flag indicating that argument has been parsed
552             ## DEBUG_ -- debug flag
553             ##
554             ## Structure of DATA slot: Note: One namelist object holds only one
555             ## namelist -- use {$nl1, $nl2, ..} to group them.
556             ##
557             ## $self->{DATA} = data_hash;
558             ## data_hash = { 'name of var1' => { 'value' => [ value1, value2, ..],
559             ## 'type' => numerical_type,
560             ## 'stype' => "type string"
561             ## }
562             ## 'name of var2' => { 'value' => [ value1, value2, ..],
563             ## 'type' => numerical_type
564             ## 'stype' => "type string"
565             ## }
566             ## ...
567             ## };
568             ##
569             sub new {
570             # Return new F90Namelist object. By default, the object is unparsed and has
571             # no name. Use `empty => 1' and `name => $name' to create a complete empty
572             # namelist with name $name'.
573             #
574             # my $nl = Fortran::F90Namelist::new();
575             # my $nl = Fortran::F90Namelist::new(name => 'toto', empty => 1);
576             # my $nl = Fortran::F90Namelist::new({name => 'toto', empty => 1});
577             # IMPLEMENT US:
578             # my $nl = Fortran::F90Namelist::new({file => $filename);
579             # my $nl = Fortran::F90Namelist::new({text => $text, debug => 1});
580             #
581 98     98 1 1935 my $proto = shift; # either classref or object ref or string
582 98         182 my @argv = @_;
583 98   33     452 my $class = ref($proto) || $proto;
584 98         174 my $self = {};
585              
586 98         201 my %data = ();
587 98         140 my @slots = ();
588 98         136 my $nslots = undef;
589 98         140 my $parsed = 0;
590              
591 98         170 my $short_usage =
592             "Usage:\n" .
593             " Fortran::F90Namelist::new()\n" .
594             " Fortran::F90Namelist::new(name => \$name)\n" .
595             " Fortran::F90Namelist::new({name => \$name})\n" ;
596              
597             # Parse argument(s) (name => ); may be list or hashref
598 98         123 my %args;
599 98 50       251 if (@argv) {
600 0 0       0 if (ref($argv[0]) eq 'HASH') { # parse($hashref)
601 0         0 %args = %{$argv[0]};
  0         0  
602             } else { # parse(%hash) or parse(@list)
603 0         0 %args = @argv;
604             }
605             }
606             #
607 98   50     496 my $name = ($args{name} || '' );
608 98   50     388 my $empty = ($args{empty} || '' );
609 98   50     389 my $debug = ($args{debug} || 0 );
610              
611 98 50       252 if ($empty) { # Valid but empty namelist
612 0         0 $nslots = 0;
613 0         0 $parsed = 1;
614             }
615              
616             ##
617             ## Finish the object
618             ##
619             # public data of object
620 98         268 $self->{DATA} = \%data;
621 98         218 $self->{SLOTS} = \@slots;
622 98         175 $self->{NSLOTS} = $nslots;
623 98         200 $self->{NAME} = $name;
624              
625             # internal data
626 98         195 $self->{PARSED_} = $parsed;
627 98         146 $self->{DEBUG_} = $debug;
628              
629 98         437 bless($self,$class);
630 98         479 return($self);
631             }
632              
633             # ====================================================================== #
634              
635             ##
636             ## Methods
637             ##
638              
639             sub parse {
640             #
641             # $obj->parse($text)
642             # $obj->parse(file => $filename)
643             # $obj->parse(file => $filehandle)
644             # $obj->parse(file => $filename|$filehandle, merge => 1[, name => $name])
645             # $obj->parse({file => $filename|$filehandle, merge => 1[, name => $name]})
646             # $obj->parse({text => $textstring, merge => 1[, name => $name]})
647             #
648             # IMPLEMENT ME:
649             # $obj->parse({text => $textstring, debug => 1})
650             #
651             # Parse text or file containing F90 namelist(s)
652             #
653 125     125 1 25892 my $self = shift;
654 125         329 my @args = @_; # can't use shift() since we change value
655             # of $text
656              
657 125         238 my $state = START;
658 125         278 my $debug = $self->{DEBUG_};
659              
660 125         162 my %args;
661             my $text;
662 125         159 my $textarg = 0;
663              
664             # Parse arguments (file => , etc.); may be single string,
665             # list, hash or hashref
666 125 100       348 if (ref($args[0]) eq 'HASH') { # parse($hashref)
667 1         3 %args = %{$args[0]};
  1         9  
668             } else {
669 124 100       299 if (@_ == 1) { # parse($string)
670 102         122 $textarg = 1;
671 102         169 $text = $args[0];
672             } else { # parse(%hash) or parse(@list)
673 22         90 %args = @args;
674             }
675             }
676 125   100     517 my $file = ($args{file} || '' );
677 125   100     596 my $merge = ($args{merge} || 0 );
678 125   100     474 my $all = ($args{all} || 0 );
679 125   100     487 my $name = ($args{name} || '' );
680 125   100     468 my $dups_ok = ($args{dups_ok} || '' );
681 125   100     470 my $broken = ($args{broken} || 0 );
682              
683             # Get text from file if necessary
684 125   100     464 $text = ($args{text} || $text );
685 125 100       329 if (!defined($text)) {
686 20 50       74 croak "\$nl->parse(): need text or file argument\n"
687             unless ($file ne '');
688 20         92 local $/ = undef;
689 20 100       66 if (ref($file) eq 'GLOB') { # file handle
690 2         105 $text = <$file>;
691             } else { # file name
692 18 50       942 open(FH, "< $file") or croak "Cannot open file <$file> for reading";
693 18         533 $text = ;
694 18         263 close(FH);
695             }
696             }
697              
698 125 100       255 if ($merge) {
699 2   33     12 $name ||= $self->{NAME}; # default to previous name
700             } else { # clear/reset all data
701 123         291 $self->{DATA} = {};
702 123         525 $self->{SLOTS} = [];
703 123         254 $self->{NSLOTS} = 0;
704 123         247 $self->{NAME} = $name;
705             }
706              
707 125         166 my $done = 0;
708              
709 125         169 do {
710 137         169 my ($name1, $nslots1, @slots1);
711 137         454 my $href = parse_namelist(\$text, \$name1, \$nslots1, \@slots1,
712             $broken, $debug);
713 135 100 66     985 if (defined($href)
      66        
714             && defined($name1)
715             && $name1 ne ''
716             ) { # really read a namelist
717              
718 121   66     480 $name ||= $name1; # choose first name if not set yet
719              
720             # Call merge method to do the actual work
721 121         829 $self->merge([\@slots1, $href],
722             { name => $name,
723             dups_ok => $dups_ok }
724             );
725 121 100       954 $done = 1 unless ($all);
726             } else { # read nothing useful --> get out of this loop
727 14         46 $done = 1;
728             }
729             } until ($done);
730              
731             # Is there a way to find out whether $text is mutable (i.e. no
732             # constant)? Until I find one, just use brute force:
733 123         179 eval { $_[0] = $text }; # Return remaining $text
  123         577  
734 123         206 $@ = ''; # We won't use this anyway..
735              
736             # Don't mimic success if we haven't read anything useful
737 123 100       315 return undef if ($name eq '');
738              
739 113 50       338 if ($debug) {
740 0         0 print STDERR
741             "Fortran::F90Namelist->parse: Successfully read namelist <$name>\n";
742 0         0 print STDERR "=================================\n";
743             }
744              
745 113         190 $self->{PARSED_} = 1;
746              
747 113         521 return $self->{NAME};
748             }
749              
750             # ---------------------------------------------------------------------- #
751              
752             sub merge {
753             #
754             # $obj->merge($obj2);
755             # $obj->merge($obj2, name => $name, $dups_ok => 1 );
756             # $obj->merge($obj2, { name => $name, $dups_ok => 1 });
757             #
758             # Merge another namelist into this one
759             #
760 125     125 1 194 my $self = shift();
761 125         162 my $nl2 = shift();
762 125         245 my @args = @_; # remaining argument(s)
763              
764             # Arg $nl2 can be a namelist or just a data hashref
765 125         159 my (@slots2,$hashref2);
766 125 100       485 if (ref($nl2) eq 'Fortran::F90Namelist') {
    50          
767 4         5 @slots2 = @{$nl2->{SLOTS}};
  4         12  
768 4         7 $hashref2 = $nl2->{DATA};
769             } elsif (ref($nl2) eq 'ARRAY') {
770 121         181 @slots2 = @{$$nl2[0]};
  121         424  
771 121         208 $hashref2 = $$nl2[1];
772             } else {
773 0         0 croak "Fortran::F90Namelist->merge(): "
774             . "expected Fortran::F90Namelist object or hashref\n";
775             }
776              
777             # Parse arguments (name => , etc.); may be hash or hashref
778 125         158 my %args;
779 125 100       302 if (ref($args[0]) eq 'HASH') { # parse($hashref)
780 121         163 %args = %{$args[0]};
  121         614  
781             } else {
782 4         7 %args = @args;
783             }
784 125   66     409 my $name = ($args{name} || $self->{NAME} );
785 125   100     487 my $dups_ok = ($args{dups_ok} || '' );
786              
787 125         244 my $nslots = $self->{NSLOTS};
788 125         148 my @slots = @{$self->{SLOTS}};
  125         293  
789 125         201 my $hashref = $self->{DATA};
790 125         181 my $debug = $self->{DEBUG_};
791              
792 125 50       316 if ($debug) {
793 0         0 print STDERR
794             "Fortran::F90Namelist->merge: "
795             , "Merging ", @slots2 + 0,
796             "-slots namelist into $nslots-slots namelist\n";
797             }
798              
799             # Eliminate repeated slots and warn if values don't agree
800 125         234 slot: foreach my $slot (@slots2) {
801 834 100       1450 if (defined($$hashref{$slot})) { # slot already known
802 25         29 my @val1=@{$$hashref{$slot}{'value'}};
  25         65  
803 25         30 my @val2=@{$$hashref2{$slot}{'value'}};
  25         59  
804 25   66     105 while (@val1 and @val2) {
805 25         36 my $v1 = pop(@val1);
806 25         43 my $v2 = pop(@val2);
807 25 50 66     127 if (($v1 ne $v2) && ! $dups_ok) {
808 0         0 carp "WARNING: Conflicting slots" .
809 0         0 " $slot = [@{$$hashref{$slot}{'value'}}]" .
810 0         0 " vs. [@{$$hashref2{$slot}{'value'}}]\n";
811 0         0 next slot;
812             }
813             }
814             } else { # new slot
815 809         1060 push @slots, $slot;
816 809         1429 $$hashref{$slot} = $$hashref2{$slot};
817 809         1094 $nslots++;
818             }
819             }
820              
821             # Wrap it up
822 125         270 $self->{NAME} = $name;
823 125         194 $self->{NSLOTS} = $nslots;
824 125         229 $self->{SLOTS} = \@slots;
825 125         237 $self->{DATA} = $hashref;
826              
827 125 50       328 if ($debug) {
828 0         0 print STDERR
829             "Fortran::F90Namelist->merge: "
830             . "Successfully merged into namelist <$name>\n";
831 0         0 print STDERR "=================================\n";
832             }
833              
834 125         192 $self->{PARSED_} = 1;
835              
836 125         464 return 1; # success
837             }
838              
839             # ---------------------------------------------------------------------- #
840              
841             sub name {
842             # Get or set name of parsed namelists
843 180     180 1 33214 my $self = shift();
844              
845 180 100       464 if (@_) { $self->{NAME} = shift };
  5         12  
846 180         663 return $self->{NAME};
847             }
848              
849             # ---------------------------------------------------------------------- #
850              
851             sub nslots {
852             # Get number of slots in namelist
853 41     41 1 829 my $self = shift();
854 41         220 return $self->{NSLOTS}
855             }
856              
857             # ---------------------------------------------------------------------- #
858              
859             sub slots { # FIXME
860             # Return array ref of variable names in slots
861 17     17 1 41 my $self = shift();
862 17         125 return $self->{SLOTS}
863             }
864              
865             # ---------------------------------------------------------------------- #
866              
867             sub hash {
868             # Return hash with parsed namelist contents
869 63     63 1 118 my $self = shift;
870 63         284 return $self->{DATA};
871             }
872              
873             # ---------------------------------------------------------------------- #
874              
875             sub output {
876             # Write namelist in specified format (defaults to 'f90')
877 14     14 1 59 my $self = shift();
878              
879             # Optional arguments:
880             # format => format ('f90' [default] or 'idl')
881             # name => nl_name (name of nlist/struct [default: get from nlist])
882             # trim => 0/1 (trim trailing whitespace off strings)
883             # double => 0/1 (mark all floats as double precision)
884             # oneline => 0/1 (write all in one line? [only for some formats])
885             # maxslots => N (similar to oneline, but split every N slots)
886 14         32 my @argv = @_;
887              
888             # Parse arguments (file => , etc.); may be list, hash or hashref
889 14         25 my %args;
890 14 50       41 if (ref($argv[0]) eq 'HASH') {
891 0         0 %args = %{$argv[0]};
  0         0  
892             } else {
893 14         44 %args = @argv;
894             }
895 14   100     305 my $format = ($args{format} || 'f90');
896 14   50     73 my $name = ($args{name} || $self->name() || '');
897 14   100     288 my $trim = ($args{trim} || 0);
898 14   100     58 my $double = ($args{double} || 0);
899 14   100     57 my $oneline = ($args{oneline} || 0);
900 14   100     60 my $maxslots = ($args{maxslots} || 0);
901 14 100       32 $oneline = 0 if ($maxslots);
902              
903             # Sanity check
904 14 50       40 unless ($self->{PARSED_}) {
905 0         0 croak "Called method output() on unparsed namelist";
906 0         0 return undef;
907             }
908              
909             # Get name of namelist(s?)
910 14         16 my ($name1,$hashref) = (%{$self->{DATA}}); # hash (name=>valhash) ->
  14         74  
911             # 2-element array; should
912             # possibly be a loop over all
913             # name=>hash pairs?
914             # Format-dependent settings
915             # We are printing the following:
916             # $head_pref
917             #
918             # $head_suff
919             # $slot_pref
920             #
921             # $slot_join
922             #
923             # [...]
924             #
925             # $slot_suff
926             # [...]
927             #
928             # $last_suff
929             # $foot_pref
930             #
931             # $foot_suff
932              
933 14         27 my ($header,$footer);
934 0         0 my ($head_pref,$head_suff);
935 0         0 my ($slot_pref,$slot_join,$slot_suff,$last_suff);
936 0         0 my ($foot_pref,$foot_suff);
937              
938 0         0 my ($newline,$indent); # to play tricks with $oneline
939 14 100       54 if ($oneline) {
940 2         5 $newline = " ";
941 2         4 $indent = "";
942             } else {
943 12         19 $newline = "\n";
944 12         24 $indent = " ";
945             }
946              
947 14 100       57 if (lc($format) eq 'f90') {
    50          
948 7         15 $header = "\&$name";
949 7         9 $footer = "/";
950             #
951 7         11 $head_pref = "";
952 7         13 $head_suff = "$newline";
953 7         13 $slot_pref = "$indent";
954 7         12 $slot_join = ", ";
955 7         13 $slot_suff = ",$newline";
956 7         30 $last_suff = "$newline";
957 7         13 $foot_pref = "";
958 7         14 $foot_suff = "\n";
959             } elsif (lc($format) eq 'idl') {
960 7         14 $header = "$name = {";
961 7         10 $footer = "}";
962             #
963 7         12 $head_pref = "";
964 7         11 $head_suff = " \$$newline";
965 7         167 $slot_pref = "$indent";
966 7         90 $slot_join = ", ";
967 7         11 $slot_suff = ", \$$newline";
968 7         9 $last_suff = " \$$newline";
969 7         12 $foot_pref = "";
970 7         18 $foot_suff = "\n";
971             #
972 7 100       20 if ($oneline) {
973 1         2 $head_suff = "$newline";
974 1         2 $slot_suff = ",$newline";
975 1         2 $last_suff = "$newline";
976             }
977             #
978 7         13 $cmplx_pref = "complex"; # complex number prefix
979             } else {
980 0         0 croak "output(): Format <$format> unknown";
981 0         0 return undef;
982             }
983              
984 14         50 my @slots = format_slots($self,$format,$double,$trim);
985              
986             # Take care of $maxslots
987 14         47 @slots = aggregate_slots(\@slots,$maxslots,$slot_join);
988              
989             # Now construct output string
990 14         25 my $string;
991 14         29 $string .= $head_pref
992             . $header
993             . $head_suff;
994 14 50       34 if (@slots) {
995 14         19 $string .= $slot_pref;
996 14         40 $string .= join($slot_suff . $slot_pref, @slots);
997 14         25 $string .= $last_suff;
998             }
999 14         21 $string .= $foot_pref
1000             . $footer
1001             . $foot_suff;
1002              
1003 14         119 return $string;
1004             }
1005              
1006             sub debug {
1007             #
1008             # $obj->debug(1) # debugging on
1009             # $obj->debug(0) # debugging off
1010             #
1011             # Undocumented: Set/get debug flag
1012 0     0 0 0 my $self = shift();
1013 0 0       0 if (@_) { $self->{DEBUG_} = shift };
  0         0  
1014 0         0 return $self->{DEBUG_}
1015             }
1016              
1017              
1018             # ====================================================================== #
1019              
1020             ## Private utility subroutines:
1021              
1022             sub parse_namelist {
1023             #
1024             # Parse first F90 namelist from text string; return reference to hash
1025             #
1026             # parse_namelist(\$text,\$name,\$nslots,\@slots,$broken,$debug);
1027             #
1028              
1029 137     137 0 195 my $textref = shift;
1030 137         191 my $nameref = shift;
1031 137         152 my $nslotsref = shift;
1032 137         177 my $slotsref = shift;
1033 137         192 my $broken = shift;
1034 137         155 my $debug = shift;
1035              
1036 137         147 my %hash;
1037 137         162 my $nslots = 0;
1038 137         164 my $state = START;
1039 137         208 my $id = $regexp[ID]; # allowed namelist/variable names
1040              
1041 137         169 my ($status,$var,@values,$type);
1042              
1043             ## Reset to reasonable default values
1044 137         206 $$nslotsref = 0;
1045 137         257 @$slotsref = ();
1046 137         197 $$nameref = '';
1047              
1048             ## Get name of nl
1049 137 100       311 $$nameref = extract_nl_name($textref,$debug) or return undef;
1050              
1051 123         193 $status = VAR;
1052             ## Extract variable slots
1053              
1054 123         212 my $text = $$textref;
1055              
1056             ## Apply fix for brokenness
1057 123 100       322 if ($broken) {
1058 2         13 $text =~ s{\n'}{',}g;
1059             }
1060              
1061 123         344 while ($text ne '') {
1062 940 50       1753 print STDERR "--------------------\nTop of while loop..\n" if ($debug);
1063 940         1474 strip_space_and_comment($text);
1064 940 100       8359 if ($text =~ s/^($id)(\([0-9, \t]+\))?\s*=\s*//s) {
    50          
1065             # string starts with or
1066 819         1584 $var = lc($1);
1067             # any array indices following the variable name?
1068 819 100       1730 if (defined($2)) {
1069 15         27 my $indices = $2;
1070 15         58 $indices =~ s/\s+//g; # squeeze out whitespace
1071 15         25 $var = $var . $indices;
1072             }
1073 819         1060 $status = VALUE;
1074 819 50       1453 if ($debug) {
1075 0         0 print STDERR "parse_namelist 1: \$var=<$var>\n";
1076 0         0 print STDERR "parse_namelist 1: \$text=<",
1077             printable_substring($text,50), ">\n";
1078             }
1079              
1080             # Get values and check
1081 819         1708 @values = get_value(\$text,\$type,$var,$debug); # drop $debug here..
1082 819 100       1874 if (@values) {
1083 817         879 $nslots++;
1084 817         1476 push @$slotsref, $var;
1085             } else {
1086 2         8 show_error("Couldn't read value", "", $text, 1);
1087 0         0 return undef;
1088             }
1089              
1090             } elsif ($text =~ s{\s*(/|\$end)\s*}{}) { # string is or <$end>
1091 121         185 $status = NL_END;
1092 121         243 last; # end of namelist
1093              
1094             } else {
1095 0         0 show_error("Expected var=[...] not found ", "", $text, 1);
1096 0         0 return undef;
1097             }
1098              
1099 817 50       1893 print STDERR "[",join(',',@values), "] -> \$hash{$var}\n" if ($debug);
1100 817   50     2153 my $stype = ($stypes{$type} || 'Type inconsistency!');
1101 817         5283 $hash{$var} = { type => $type,
1102             stype => $stype,
1103             value => [@values]
1104             };
1105             }
1106              
1107 121 50       315 unless ($status == NL_END) {
1108 0         0 carp "Aborted parsing at <",
1109             printable_substring($text,50),">\n",
1110             "trying to read slot `$var'\n";
1111 0         0 return undef;
1112             }
1113              
1114 121 50       268 print STDERR
1115             "parse_namelist: Namelist <$$nameref> parsed succesfully\n"
1116             if ($debug);
1117              
1118 121         184 $$textref = $text; # propagate remainder of $text back
1119 121         157 $$nslotsref = $nslots; # propagate number of slots back
1120              
1121 121         385 return \%hash;
1122             }
1123              
1124             # ---------------------------------------------------------------------- #
1125             sub extract_nl_name {
1126             # Extract namelist name (the part starting with `&' or `$')
1127              
1128 137     137 0 170 my $textref = shift;
1129 137         168 my $debug = shift;
1130              
1131 137         218 my $text = $$textref;
1132 137         151 my $name;
1133 137         158 my $id = $regexp[ID]; # allowed namelist/variable names
1134              
1135 137 50       314 print STDERR "extract_nl_name 1: \$text=<",
1136             printable_substring($text,50),">\n" if ($debug);
1137 137         313 strip_space_and_comment($text);
1138              
1139 137 50       371 print STDERR "extract_nl_name 2: \$text=<",
1140             printable_substring($text,50), ">\n" if ($debug);
1141 137 100       2623 if ($text =~ s/^(?:&|\$)($id)//) {
1142 123         403 $name = lc($1);
1143             } else { # empty (comment/whitespace) or erroneous
1144 14 50       34 if ($text eq '') {
1145 14 50       40 print STDERR "Empty text (at most some comments)" if ($debug);
1146 14         24 $$textref = $text; # propagate remainder of $text back
1147 14         75 return undef;
1148             } else {
1149 0         0 show_error("Namelist does not start with &\n","",$text,1);
1150 0         0 return undef; # never got here..
1151             }
1152             }
1153 123         286 strip_space_and_comment($text);
1154              
1155 123 50       357 if ($debug) {
1156 0         0 print STDERR "extract_nl_name 3: \$name=<$name>\n";
1157 0         0 print STDERR "extract_nl_name 3: \$text=<",
1158             printable_substring($text,50), ">\n";
1159             }
1160              
1161 123         204 $$textref = $text; # propagate remainder of $text back
1162 123         776 $name;
1163             }
1164              
1165             # ---------------------------------------------------------------------- #
1166              
1167             sub strip_space_and_comment {
1168             # Strip leading space and anything from possible leading exclamation mark
1169             # til end of line.
1170 2019     2019 0 10397 $_[0] =~ s/^(\s*(![^\n]*)?)*//s;
1171             }
1172              
1173             # ---------------------------------------------------------------------- #
1174              
1175             sub get_value {
1176             # Extract one or several values from string that starts off immediately
1177             # after the equal sign of a slot assignment
1178 819     819 0 958 my $txtptr = shift;
1179 819         859 my $typeptr = shift;
1180 819         889 my $varname = shift;
1181 819         809 my $debug = shift; # Need to somewhow get rid of this argument...
1182              
1183 819         959 my $text = $$txtptr;
1184 819         824 my @values;
1185              
1186 819         1212 strip_space_and_comment($text); # (are comments really allowed here?)
1187 819         1746 my $type = infer_data_type($text);
1188 819 50       1783 if ($debug) { # pretty-printing of type
1189 0         0 print STDERR
1190             "Found data of type $type (",
1191             elucidate_type($type),
1192             ") in <",
1193             printable_substring($text,40), ">\n";
1194             }
1195              
1196 819 50       1503 if ($type == UNKNOWN) {
1197 0         0 show_error("Cannot identify data type","$varname=","$text");
1198 0         0 croak();
1199             }
1200              
1201             # Extract data
1202 819         946 my $multiregexp = $regexp[MULTIPLE]; # qr// wouldn't expand the CONSTANT...
1203 819         3450 my $re_type = qr/$regexp2[$type]/;
1204              
1205 819         37134 while ($text =~ s/^
1206             ($multiregexp)?($re_type)
1207             \s*
1208             (
1209             (?: ,? \s* ! [^\n]* | , | \s+ )
1210             |
1211             (?=\/|\$end)
1212             )
1213             \s*
1214             //sx) {
1215              
1216 1139   100     5945 my $mul = ( $1 || 1);
1217 1139         2441 my ($val,$rest) = ($2,$3);
1218 1139         1694 $mul =~ s/\*//;
1219 1139 50       1969 if ($debug) {
1220 0         0 print STDERR "\$mul=<$mul>, \$val=<$val>\n";
1221 0         0 print STDERR "\$rest=<", printable_substring($rest,2),
1222             ">, \$text=<", printable_substring($text,40), ">\n";
1223             }
1224              
1225             # `Widen' data type if necessary (e.g. integer -> double for
1226             # `var = 1, 2.D0')
1227 1139         2513 my $valtype = infer_data_type($val);
1228 1139 100       2251 $type = $valtype if ($valtype > $type);
1229 1139 50       1853 if ($debug) { # pretty-printing of type
1230 0         0 print STDERR
1231             "Data type is now ($valtype) $type (",
1232             elucidate_type($type),
1233             ")\n";
1234             }
1235              
1236             # Remove quotes around (and doubled in) strings
1237 1139 100       1935 if ($type == SQ_STRING) {
1238 292         1051 $val =~ s/^'(.*)'$/$1/s;
1239 292         481 $val =~ s/''/'/gs;
1240             }
1241 1139 100       1938 if ($type == DQ_STRING) {
1242 12         52 $val =~ s/^"(.*)"$/$1/s;
1243 12         31 $val =~ s/""/"/gs;
1244             }
1245              
1246             # Remove embedded newlines from strings (Anders' strange namelist
1247             # samples from Pencil Code with dust density)
1248 1139 100 100     3788 if (($type == SQ_STRING) || ($type == DQ_STRING)) {
1249 304         406 $val =~ s/\n//g;
1250             }
1251              
1252 1139         2213 push @values, ($val) x $mul;
1253 1139 50       2143 $text =~ s/.*\n// if ($rest eq '!'); # comment
1254 1139 50 0     10624 print STDERR "<<", ($mul||'1'), "x>><<$val>> <<",
1255             printable_substring($text), ">>\n" if ($debug);
1256             }
1257              
1258 819         1233 $$txtptr = $text; # return remaining unparsed string
1259 819         870 $$typeptr = $type; # return type
1260 819         4147 @values;
1261             }
1262              
1263             # ---------------------------------------------------------------------- #
1264              
1265             sub elucidate_type {
1266             # Expand numerical constants into understandable type names
1267 0     0 0 0 my $type = shift;
1268              
1269 0         0 my @tp;
1270 0         0 $tp[UNKNOWN] = 'UNKNOWN';
1271 0         0 $tp[SQ_STRING] = 'SQ_STRING';
1272 0         0 $tp[DQ_STRING] = 'DQ_STRING';
1273 0         0 $tp[LOGICAL ] = 'LOGICAL';
1274 0         0 $tp[INTEGER ] = 'INTEGER';
1275 0         0 $tp[FLOAT ] = 'FLOAT';
1276 0         0 $tp[SINGLE ] = 'SINGLE';
1277 0         0 $tp[DOUBLE ] = 'DOUBLE';
1278 0         0 $tp[COMPLEX ] = 'COMPLEX';
1279 0         0 $tp[DCOMPLEX ] = 'DCOMPLEX';
1280 0         0 $tp[MULTIPLE ] = 'MULTIPLE';
1281              
1282 0         0 return $tp[$type];
1283             }
1284              
1285             # ---------------------------------------------------------------------- #
1286              
1287             sub infer_data_type {
1288             # Determine the F90 data type of first item in string, skipping multiplier
1289             # if present
1290 1958     1958 0 2809 my $text = shift;
1291 1958         3230 $text =~ s/^\s*[0-9]+\*//; # ignore multiplier for finding data type
1292 1958 100       29464 if ($text =~ /^\s*'/) { SQ_STRING;
  395 100       761  
    100          
    100          
    100          
    100          
    100          
    100          
    50          
1293 24         47 } elsif ($text =~ /^\s*"/) { DQ_STRING;
1294 116         255 } elsif ($text =~ /^\s*\(\s*$numeric_e\s*,/) { COMPLEX;
1295 115         242 } elsif ($text =~ /^\s*\(/) { DCOMPLEX;
1296 212         456 } elsif ($text =~ /^\s*(T|F|.(true|false).)/i) { LOGICAL;
1297 369         1029 } elsif ($text =~ /^\s*[+-]?[0-9]+(\s|,|!|$)/) { INTEGER;
1298 409         930 } elsif ($text =~ /^\s*$float(\s|,|!|$)/) { FLOAT;
1299 192         488 } elsif ($text =~ /^\s*$numeric_e(\s|,|!|$)/) { SINGLE;
1300 126         346 } elsif ($text =~ /^\s*$numeric_d(\s|,|!|$)/) { DOUBLE;
1301 0         0 } else { UNKNOWN;
1302             }
1303             }
1304              
1305             # ---------------------------------------------------------------------- #
1306              
1307             sub show_error {
1308             # Print error message and beginning of string with marker of current
1309             # position [Slightly ridiculous, as the marker will always point to
1310             # beginning of line]
1311 2     2 0 6 my $errmsg = shift;
1312 2         5 my $prefix = shift;
1313 2         3 my $text = shift;
1314 2   50     9 my $die = (shift || 0);
1315              
1316 2         7 chomp($errmsg);
1317              
1318             # Escape newlines and only print 75 chars:
1319 2         6 my $subtext = $prefix . $text;
1320 2         15 $subtext =~ s/\n/\\n/g;
1321 2         7 $subtext = substr($subtext,0,75) . "\n";
1322              
1323             # Splice in marker line
1324 2         9 my $marker = " " x length($prefix) . "^------ HERE\n";
1325 2         16 $subtext =~ s/\n/\n$marker/;
1326              
1327             # Prefix error message:
1328 2         8 $subtext = "\e[01m$errmsg:\e[00m\n" . $subtext;
1329              
1330             # Now die
1331 2 50       7 if ($die) {
1332 2         412 croak "$subtext"; # die
1333             } else {
1334 0         0 carp "$subtext"; # warn
1335             }
1336             }
1337              
1338             # ---------------------------------------------------------------------- #
1339              
1340             sub printable_substring {
1341             # Extract substring and quote newlines for diagnostic printing
1342 0     0 0 0 my $string = shift;
1343 0   0     0 my $length = shift || 40;
1344              
1345 0         0 $string =~ s/\n/\\n/g;
1346 0         0 my $oldlen = length($string);
1347 0         0 $string = substr($string,0,$length);
1348 0 0       0 substr($string,-3,3) = '...' if ($length<$oldlen);
1349 0         0 $string;
1350             }
1351              
1352             # ---------------------------------------------------------------------- #
1353              
1354             sub assign_slot_val {
1355             #
1356             # Assignment of value to slot variable for output (format-dependent: `='
1357             # for f90, `:' for IDL records, etc.)
1358             #
1359 48     48 0 74 my $var = shift;
1360 48         50 my @vals = @{shift()};
  48         105  
1361 48         71 my $format = shift;
1362 48         55 my $type = shift;
1363              
1364 48         428 my $assmnt = "$var";
1365              
1366 48 100       119 if ($format eq 'f90') {
    50          
1367 24         41 $assmnt .= "=";
1368             } elsif ($format eq 'idl') {
1369 24         31 $assmnt .= ": "; # structure syntax
1370             } else {
1371 0         0 croak "assign_slot_val: Unknown format <$format>\n";
1372             }
1373              
1374 48         100 encapsulate_values(\@vals,$format,$type); # preprocess values
1375 48 100       119 if (@vals > 1) {
1376 14         57 $assmnt .= add_array_bracket(join(",", @vals), $format);
1377             } else {
1378 34         51 $assmnt .= $vals[0];
1379             }
1380              
1381 48         156 $assmnt;
1382             }
1383              
1384             # ---------------------------------------------------------------------- #
1385              
1386             sub encapsulate_values {
1387             #
1388             # Format-specific preprocessing of data values, e.g. quoting strings,
1389             # mapping logicals to integers for IDL, etc.
1390             #
1391 48     48 0 85 my $valref = shift;
1392 48         60 my $format = shift;
1393 48         62 my $type = shift;
1394 48         127 my @vals = @$valref;
1395              
1396             ## Actions for all formats
1397 48 50 33     452 if ($type==COMPLEX or $type==DCOMPLEX) {
1398              
1399 27     27   37656 use Data::Dumper;
  27         333658  
  27         32761  
1400 0         0 @vals = map { "${cmplx_pref}$_${cmplx_suff}" } @vals;
  0         0  
1401             }
1402              
1403             ## Actions specific for some formats
1404 48 100       121 if ($format eq 'f90') {
    50          
1405             #
1406             # F90 output format:
1407             # - quote strings
1408             #
1409 24 100 100     2111 if ($type==SQ_STRING or $type==DQ_STRING) {
1410 10         21 @vals = map { quote_string_f90($_) } @vals;
  15         43  
1411             }
1412             } elsif ($format eq 'idl') {
1413             #
1414             # IDL output format:
1415             # - convert logicals to integers
1416             # - quote strings
1417             #
1418 24 50 100     111 if ($type==LOGICAL) {
    100          
1419 0         0 @vals = map { encaps_logical_idl($_) } @vals;
  0         0  
1420             } elsif ($type==SQ_STRING or $type==DQ_STRING) {
1421 10         17 @vals = map { quote_string_f90($_) } @vals;
  15         28  
1422             }
1423             } else {
1424             #
1425             # Invalid format
1426             #
1427 0         0 croak "encapsulate_values: Unknown format <$format>\n";
1428             }
1429              
1430 48         175 @$valref = @vals;
1431             }
1432              
1433             # ---------------------------------------------------------------------- #
1434              
1435             sub format_slots {
1436             #
1437             # Format all slots for printing and collect in a list
1438             #
1439 14     14 0 20 my $obj = shift;
1440 14   50     37 my $format = (shift || 0);
1441 14   100     53 my $double = (shift || 0);
1442 14   100     48 my $trim = (shift || 0);
1443              
1444 14 50       44 return () unless ($obj->{NSLOTS});
1445              
1446 14         18 my @slots;
1447             my $slot;
1448 14         17 foreach my $var (@{$obj->{SLOTS}}) {
  14         40  
1449 48         115 my $valhash = $obj->{DATA}->{$var};
1450 48         56 my @vals = @{$$valhash{'value'}};
  48         149  
1451 48         88 my $type = $$valhash{'type'};
1452              
1453             # Trim trailing whitespace
1454 48 100       433 if ($trim) {
1455 8         14 @vals = map { s/\s*$//; $_ } @vals;
  10         40  
  10         33  
1456             }
1457              
1458             # Replace E[0-9]+ by, or append `D0' where necessary
1459 48 100       98 if ($double) {
1460 8 50 66     74 if (($type == FLOAT) ||
      66        
      33        
      33        
1461             ($type == SINGLE) ||
1462             ($type == DOUBLE) ||
1463             ($type == COMPLEX) ||
1464             ($type == DCOMPLEX)) {
1465 2         5 @vals = map { s/[eEdD]/D/; $_ } @vals;
  2         5  
  2         8  
1466 2         8 @vals = map { s/(^|\s|,)($float)($|\s|,)/$1$2D0$3/g; $_ } @vals;
  2         115  
  2         13  
1467 2         6 @vals = map { s/(\(\s*)($float)(\s*,\s*)($float)(\s*\))/$1$2D0$3$4D0$5/g; $_ } @vals;
  2         56  
  2         9  
1468             }
1469             }
1470              
1471 48         117 $slot = assign_slot_val($var,\@vals,$format,$type);
1472 48         146 push @slots, $slot;
1473             }
1474              
1475 14         59 return @slots;
1476             }
1477              
1478             # ---------------------------------------------------------------------- #
1479             sub aggregate_slots {
1480             #
1481             # Take list of formatted slot strings, group every $maxslots strings
1482             # together into one string
1483             #
1484 14     14 0 25 my $slotsref = shift;
1485 14         19 my $maxslots = shift;
1486 14         16 my $slot_join = shift;
1487              
1488 14         36 my @slots = @$slotsref;
1489              
1490             # Short-circuit if nothing to do
1491 14 100       313 return @slots unless ($maxslots>0);
1492              
1493 2         4 my @new_slots;
1494 2         9 while (@slots) {
1495             # Use a loop here, as @slots[1..$maxslots] would generate trailing
1496             # undefs
1497 4         5 my @group_us;
1498 4         11 foreach my $i (1..$maxslots) {
1499 8 50       26 push @group_us, shift @slots if (@slots);
1500             }
1501 4         12 my $aggregated_slot = join($slot_join, @group_us);
1502 4         14 push @new_slots, $aggregated_slot;
1503             }
1504              
1505 2         8 return @new_slots;
1506             }
1507             # ---------------------------------------------------------------------- #
1508              
1509             sub add_array_bracket {
1510             # Add format-specific array delimiters around string
1511 14     14 0 20 my $string = shift;
1512 14         18 my $format = shift;
1513              
1514 14 100       53 if ($format eq 'f90') {
    50          
1515             # No delimiters
1516             } elsif ($format eq 'idl') {
1517 7         18 $string = "[$string]";
1518             } else {
1519             #
1520             # Invalid format
1521             #
1522 0         0 croak "add_array_bracket: Unknown format <$format>\n";
1523             }
1524              
1525 14         33 return $string;
1526             }
1527              
1528             # ---------------------------------------------------------------------- #
1529              
1530             sub encaps_logical_idl {
1531             # Convert logical string to integer for IDL
1532 0     0 0 0 my $val = shift;
1533              
1534 0         0 $val =~ s/(\.false\.|F)/0L/i;
1535 0         0 $val =~ s/(\.true\.|T)/-1L/i;
1536              
1537 0         0 $val;
1538             }
1539              
1540             # ---------------------------------------------------------------------- #
1541              
1542             sub quote_string_f90 {
1543             # Enclose string by quotation marks, doubling any existing quotation marks
1544             # for Fortran and IDL
1545 30     30 0 47 my $val = shift;
1546              
1547 30         705 $val =~ s/'/''/g;
1548              
1549 30         61 return quote_string($val);
1550             }
1551              
1552             # ---------------------------------------------------------------------- #
1553              
1554             sub quote_string {
1555             # Enclose string by quotation marks
1556 30     30 0 150 return "'$_[0]'";
1557             }
1558              
1559             # ---------------------------------------------------------------------- #
1560              
1561             ## Done.
1562              
1563             1;
1564              
1565             # End of file