~ubuntu-branches/ubuntu/jaunty/horae/jaunty

« back to all changes in this revision

Viewing changes to 0CPAN/STAR-Parser-0.59/lib/STAR/Parser.pm

  • Committer: Bazaar Package Importer
  • Author(s): Carlo Segre
  • Date: 2008-02-23 23:13:02 UTC
  • mfrom: (2.1.2 hardy)
  • Revision ID: james.westby@ubuntu.com-20080223231302-mnyyxs3icvrus4ke
Tags: 066-3
Apply patch to athena_parts/misc.pl for compatibility with 
perl-tk 804.28.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package STAR::Parser;
2
 
 
3
 
use STAR::DataBlock;
4
 
use STAR::Dictionary;
5
 
 
6
 
use strict;
7
 
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
8
 
 
9
 
$VERSION = '0.59';
10
 
 
11
 
# $Id: Parser.pm,v 1.6 2004/04/08 17:03:43 wbluhm Exp $  RCS identification
12
 
 
13
 
 
14
 
####################
15
 
# Constructor: new #
16
 
####################
17
 
 
18
 
sub new {
19
 
    my $proto = shift;
20
 
    my $class = ref($proto) || $proto;
21
 
    my $self = {};
22
 
    bless ($self,$class);
23
 
    return $self;
24
 
}
25
 
 
26
 
 
27
 
#######################
28
 
# Class method: parse #
29
 
#######################
30
 
 
31
 
sub parse {
32
 
 
33
 
    my ($self,@parameters) = @_;      
34
 
    my ($file,$dict,$options);
35
 
    $options = '';
36
 
        
37
 
    $file = shift @parameters unless $#parameters;
38
 
    while ($_ = shift @parameters) {
39
 
       $file = shift @parameters if /-file/;
40
 
       $dict = shift @parameters if /-dict/;
41
 
       $options = shift @parameters if /-options/;
42
 
    }
43
 
    
44
 
    my ($d, $s, $c, $i);  # data and save blocks
45
 
                          # category, item
46
 
    my ($n, $m);          # loop counters
47
 
    my ($flag);
48
 
    my ($debug, $log);
49
 
    my (@entries, $entry);
50
 
    my (@cats_in_loop, @items_in_loop);
51
 
    my ($line_nums_ref, $flags_ref, $tokens_ref);
52
 
 
53
 
    my $token;      # Here, "token" shall mean an item name (e.g. _atom.id),
54
 
                    # or an item value, (5 examples: 1 value 'a value' . ? )
55
 
                    # or a value over several lines delimited by semicolons.
56
 
 
57
 
    $d = 'untitled';  # default (if no data block)
58
 
    $s = '-';         # default (if not in save block)
59
 
        
60
 
    $debug = 1      if ( $options =~ /d/ );
61
 
    $log =1         if ( $options =~ /l/ );
62
 
 
63
 
 
64
 
    ##################
65
 
    ### tokenizing ###
66
 
    ##################
67
 
 
68
 
    print STDERR "tokenizing complete file\n" if ( $log );
69
 
 
70
 
    ($line_nums_ref, $flags_ref, $tokens_ref) = STAR::Parser->_all_tokens(-file=>$file);          
71
 
 
72
 
    ### check integrity of token list -- pre-parsing check ###
73
 
    # this had not been implemented yet, but
74
 
    # would now have to be a class method in STAR::Parser
75
 
 
76
 
    if ($debug) {
77
 
        print STDERR "Start of tokens\n";
78
 
        foreach $n (0.. $#$tokens_ref) {
79
 
            print STDERR "next token: ",$$flags_ref[$n],
80
 
                        " ",$$tokens_ref[$n],"\n";
81
 
        }
82
 
        print STDERR "End of tokens\n";
83
 
    }        
84
 
 
85
 
    # default data block (if no data_ in file, e.g. for ERF files)
86
 
 
87
 
    $entry = STAR::DataBlock->new;
88
 
    $entry->file_name($file);
89
 
    $entry->type('data');
90
 
    $entry->title('untitled');
91
 
    $entry->starting_line(1);
92
 
    push @entries, $entry;
93
 
 
94
 
 
95
 
    ###############
96
 
    ### parsing ###
97
 
    ###############
98
 
 
99
 
    until ( (shift @$flags_ref) eq 'eof' ) {                
100
 
    
101
 
        $token = shift @$tokens_ref;
102
 
        print STDERR "next token: $token\n" if ($debug);
103
 
                            
104
 
        if ( $token =~ /^data_(.*)/ ) {        #data block 
105
 
 
106
 
            $d = $1;
107
 
            $s = '-';    # default (if not in save block)
108
 
            print STDERR "New data block: $token\n"  if ($debug);
109
 
 
110
 
            # create new "entry object" (DataBlock or Dictionary)
111
 
            # ---------------------------------------------------
112
 
 
113
 
            if ( $dict ) {
114
 
                $entry = STAR::Dictionary->new;
115
 
                $entry->type('dictionary');
116
 
            }
117
 
            else {
118
 
                $entry = STAR::DataBlock->new;
119
 
                $entry->type('data');
120
 
            }
121
 
            $entry->file_name($file);
122
 
            $entry->title($1);
123
 
            $entry->starting_line( shift @$line_nums_ref );   # next data block line number
124
 
            push @entries, $entry;
125
 
 
126
 
            print STDERR "parsing ",$entry->{TITLE},"\n" if ( $log );
127
 
 
128
 
            next;
129
 
        }
130
 
 
131
 
        if ( $token =~ /^save_(\S+)/ ) {       #save block
132
 
            $s = $1;
133
 
            print STDERR "save block: $s\n" if ($debug);
134
 
        }
135
 
        elsif ( $token =~ /^save_$/ ) {        #end of save block
136
 
            $s = '-';
137
 
        } 
138
 
 
139
 
        if ( $token =~ /^loop_/ ) {            #loop block
140
 
      
141
 
            print STDERR "started loop\n" if ($debug);
142
 
            $flag = shift @$flags_ref;
143
 
            $token = shift @$tokens_ref;
144
 
            @cats_in_loop = ();
145
 
            @items_in_loop = ();
146
 
            
147
 
            while ( $flag eq 'i' ) {  # need to check for $flag since _something could have 
148
 
                                      # also been a value (in quotes)
149
 
 
150
 
                if ( $token =~ /^(_\S+?)\.\S+/ ) {     # DDL2: _category.item
151
 
                    $c = $1;
152
 
                }
153
 
                else {                                 # DDL1: no notion of category
154
 
                    $c = '-';      
155
 
                }
156
 
                print STDERR "token (item) in loop: ", "$token\n" if ($debug);
157
 
                push @cats_in_loop, $c;
158
 
                push @items_in_loop, $token;
159
 
                $flag = shift @$flags_ref;
160
 
                $token = shift @$tokens_ref;
161
 
            }
162
 
            
163
 
            $m=0;                 
164
 
            until ( $flag ) {   #if it's NOT a value, it's got a flag
165
 
 
166
 
                foreach $n (0..$#items_in_loop) {
167
 
                    print STDERR "token (value) in loop: ",
168
 
                                 "$token\n" if ($debug);
169
 
                    $entry->{DATA}{$d}{$s}{$cats_in_loop[$n]}
170
 
                                          {$items_in_loop[$n]}[$m]
171
 
                          = $token;
172
 
                    $flag = shift @$flags_ref;     
173
 
                    if ( $flag && ( $n < $#items_in_loop ) ) {
174
 
                        die "fatal parsing error in category $cats_in_loop[$n]\n";
175
 
                    }
176
 
                    $token = shift @$tokens_ref;
177
 
                }
178
 
                $m++;
179
 
            }
180
 
            
181
 
            print STDERR "finished loop\n" if ($debug);
182
 
            print STDERR "last token (to be recycled): ",
183
 
                         "$token\n" if ($debug);
184
 
                        
185
 
            # the last token was out of 'loop_' 
186
 
            # and needs to be recycled at the top
187
 
            unshift @$flags_ref, $flag;
188
 
            unshift @$tokens_ref,$token;
189
 
        }
190
 
         
191
 
        elsif ( $token =~ /^_\S+/ ) {
192
 
            $i = $token;
193
 
            if ( $token =~ /^(_\S+?)\.\S+/ ) {     # DDL2: _category.item
194
 
                $c = $1;
195
 
            }
196
 
            else {                                 # DDL1: no notion of category
197
 
                $c = '-';      
198
 
            }
199
 
            $flag = shift @$flags_ref;
200
 
            if ( $flag ) {
201
 
                die "fatal parsing error in category $c\n";
202
 
            }
203
 
            $token = shift @$tokens_ref;     #this one must be a value!
204
 
            print STDERR "next token (value): ",
205
 
                         "$token\n" if ($debug);
206
 
            $entry->{DATA}{$d}{$s}{$c}{$i}[0] = $token;
207
 
        }
208
 
    }
209
 
    
210
 
    if ($#entries > 0) {   # if there is more than one entry
211
 
        shift @entries;     # discard the default "untitled" entry
212
 
    }
213
 
 
214
 
    # add ending line number attributes
215
 
 
216
 
    my @ending_lines;
217
 
 
218
 
    foreach $entry ( @entries ) {
219
 
        push @ending_lines, ( $entry->starting_line() - 1 );
220
 
    }
221
 
 
222
 
    shift @ending_lines;                              # first one didn't make sense
223
 
    push @ending_lines, ( shift @$line_nums_ref );    # last one is last line number
224
 
 
225
 
    foreach $entry ( @entries ) {
226
 
        $entry->ending_line( shift @ending_lines );
227
 
    }
228
 
 
229
 
    if ( $log ) {
230
 
        foreach $entry ( @entries ) {
231
 
            print STDERR $entry->get_attributes;
232
 
        }
233
 
    }
234
 
 
235
 
    return @entries;
236
 
}
237
 
 
238
 
 
239
 
#####################################
240
 
# Private class method: _all_tokens #
241
 
#####################################
242
 
 
243
 
# This method was moved from DataBlock to Parser in version 0.58
244
 
 
245
 
sub _all_tokens {
246
 
 
247
 
    my ($self, @parameters) = @_;
248
 
    my ($file);
249
 
 
250
 
    $file = shift @parameters unless $#parameters;
251
 
    while ($_ = shift @parameters) {
252
 
       $file = shift @parameters if /-file/;
253
 
    }
254
 
 
255
 
    my $multi_flag=0;
256
 
    my ($lines, $token, $rest);
257
 
    my (@line_nums, @flags, @tokens);
258
 
 
259
 
    open (IN, "<$file") or die "Can't open file $file";
260
 
 
261
 
    while (<IN>) {
262
 
        if ($multi_flag == 1) {
263
 
            if (  /^;\s(.*)/s ) {
264
 
                $multi_flag=0;          #one value (w/o semicolons)
265
 
                push @flags, '';
266
 
                push @tokens, $lines;   #no flag
267
 
                $_ = $1;                # continue with rest of line
268
 
                                        # closing semicolon does not have to be on line by itself
269
 
            }
270
 
            elsif ( /^;/ ) {
271
 
                $multi_flag=0;          #one value (w/o semicolons)
272
 
                push @flags, '';
273
 
                push @tokens, $lines;   #no flag
274
 
                next;
275
 
            }
276
 
            else {
277
 
                $lines .= $_;           #append
278
 
                next;
279
 
            }
280
 
        }
281
 
        elsif (  /^;(.*)/s ) {
282
 
            $multi_flag=1;              #start
283
 
            $lines = $1;                #newline still on
284
 
            next;
285
 
        }
286
 
        while ( /\S/ ) {
287
 
            last if ( /^\s*#/ );
288
 
 
289
 
            if ( /^\s*["']/s ) {
290
 
 
291
 
                /^\s*(["'])(.*?)\1\s(.*)/s;     #stuff in quotes is one token
292
 
                push @flags, '';                #it's a value, so no flag
293
 
                push @tokens, $2;
294
 
                $_ = $3;
295
 
            }
296
 
            elsif ( /^\s*(\S+)(.*)/s ) {        #one token
297
 
 
298
 
                $token = $1;
299
 
                push @tokens, $token;
300
 
                $_ = $2;
301
 
 
302
 
                unless ( $token =~ /_/ ) {
303
 
                    push @flags, '';            #without '_' certainly a value
304
 
                    next;
305
 
                }
306
 
                if ( $token =~ /^_/ ) {
307
 
                    push @flags, 'i';           #item
308
 
                }
309
 
                elsif ( $token =~ /^loop_/ ) {  #loop
310
 
                    push @flags, 'l';
311
 
                }
312
 
                elsif ( $token =~ /^save_/ ) {  #save
313
 
                    push @flags, 's';
314
 
                }
315
 
                elsif ( $token =~ /^data_/ ) {  #data
316
 
                    push @flags, 'd';
317
 
                    push @line_nums, $.;        # next data block line number
318
 
                }
319
 
                else {
320
 
                    push @flags, '';            #an unquoted value with '_'
321
 
                }
322
 
            }
323
 
        }
324
 
    }
325
 
 
326
 
    push @flags, 'eof';     # 'eof' added as last flag
327
 
                            # thus there should always be one more flag
328
 
    push @line_nums, $. ;   # last line number
329
 
 
330
 
    close (IN);
331
 
 
332
 
    return (\@line_nums, \@flags, \@tokens);
333
 
}
334
 
 
335
 
 
336
 
#######################################
337
 
# Private class method: _find_entries #
338
 
#######################################
339
 
 
340
 
# This method has been obsoleted in version 0.58.
341
 
# Since 0.58, files are no longer pre-parsed
342
 
# for data blocks, since it does not allow
343
 
# for proper functional assignment of all
344
 
# 'data' strings.      
345
 
 
346
 
 
347
 
1;
348
 
__END__
349
 
 
350
 
 
351
 
=head1 NAME
352
 
 
353
 
STAR::Parser - Perl extension for parsing STAR compliant files (with no 
354
 
nested loops).
355
 
 
356
 
=head2 Version
357
 
 
358
 
This documentation refers to version 0.59 of this module.
359
 
 
360
 
=head1 SYNOPSIS
361
 
 
362
 
  use STAR::Parser;
363
 
  
364
 
  ($data) = STAR::Parser->parse('1fbm.cif');
365
 
 
366
 
  ($dict) = STAR::Parser->parse(-file=>'mmcif_dict',
367
 
                                -dict=>1,
368
 
                                -options=>'l');  #logs activity
369
 
 
370
 
=head1 DESCRIPTION
371
 
 
372
 
STAR::Parser is one of several related Perl modules for parsing
373
 
STAR compliant files (such as CIF and mmCIF files). Currently, 
374
 
these modules include STAR::Parser, STAR::DataBlock, STAR::Dictionary,
375
 
STAR::Writer, STAR::Checker, and STAR::Filter.
376
 
 
377
 
STAR::Parser is the parsing module, with the class method parse 
378
 
for parsing any STAR compliant files or dictionaries, as long 
379
 
as they do B<not> contain nested loops (i.e., only B<one> level of 
380
 
loop is supported). 
381
 
Upon parsing of a file, an array of DataBlock objects is returned (one 
382
 
for each data_ entry in the file).  
383
 
The class 
384
 
STAR::DataBlock contains object methods for these objects.
385
 
STAR::DataBlock is automatically accessible through STAR::Parser.
386
 
Upon parsing of a dictionary (indicated with the C<-dict=E<gt>1> parameter), 
387
 
an array of Dictionary objects is returned. STAR::Dictionary is a sub-class 
388
 
of STAR::DataBlock.
389
 
 
390
 
The methods of this module and the accompanying modules 
391
 
(STAR::DataBlock, STAR::Checker, etc.) support 
392
 
"named parameters" style for passing arguments. If 
393
 
only one argument is mandatory, then it may be passed in either a 
394
 
"named parameters" or "unnamed parameters" style, for example:
395
 
 
396
 
       @objs = STAR::Parser->parse( -file=>$file, -options=>'d' );  #debugging
397
 
 
398
 
       @objs = STAR::Parser->parse( -file=>$file );  #no options
399
 
   or: @objs = STAR::Parser->parse( $file );
400
 
 
401
 
=head1 CLASS METHODS
402
 
 
403
 
=head2 parse
404
 
 
405
 
  Usage:  @objs = STAR::Parser->parse(-file=>$file[,
406
 
                                      -dict=>1,
407
 
                                      -options=>$options]);
408
 
 
409
 
     or:  @objs = STAR::Parser->parse($file);
410
 
                                    
411
 
  Examples: 
412
 
  
413
 
  1)  @objs = STAR::Parser->parse('1fbm.cif');
414
 
      $data = $objs[0];
415
 
 
416
 
      OR:
417
 
 
418
 
      ($data) = STAR::Parser->parse('1fbm.cif');
419
 
 
420
 
  2)  @objs = STAR::Parser->parse('7files.txt');
421
 
      foreach $obj (@objs) {
422
 
          # do something, see STAR::DataBlock
423
 
      }
424
 
 
425
 
  3)  @objs = STAR::Parser->parse(-file=>'mmcif_dict',
426
 
                                  -dict=>1,
427
 
                                  -options=>'l'); #logs activity
428
 
      $dict = @objs[0];
429
 
 
430
 
This method first searches the file and creates a DataBlock object 
431
 
for each data_ identifier found in the file. If no data_ identifier 
432
 
is found, then only one DataBlock object 
433
 
will be created (with C<$d='untitled'>, 
434
 
see below). If parse is invoked with the C<-dict=E<gt>1> option,
435
 
then a Dictionary object is created for each data_ identifier found.
436
 
 
437
 
Next, the method populates 
438
 
the data structure of each DataBlock or Dictionary object. 
439
 
The parsed data may be queried or accessed by 
440
 
object methods of the STAR::DataBlock and STAR::Dictionary modules. 
441
 
See the documentation for STAR::DataBlock and STAR::Dictionary.
442
 
 
443
 
The method always returns an array of objects, even if it contains only 
444
 
one object (if there is only one data_ block in the file). 
445
 
 
446
 
Internally, the parsed data is stored in a multidimensional 
447
 
hash with keys for data blocks (C<$d>), save blocks (C<$s>),
448
 
categories (C<$c>), and items (C<$i>). 
449
 
For a file, C<$s> will always be C<'-'>, since there are no 
450
 
save blocks in files. 
451
 
For a dictionary, C<$s> will be C<'-'> outside of save_ blocks, 
452
 
and C<'CATEGORY'> or C<'_item'> inside save_CATEGORY or save__item blocks 
453
 
(capitalization depends on the user's dictionary.)
454
 
If a file is parsed that contains no data_ identifier, then C<$d> becomes 
455
 
C<'untitled'>. C<$c> refers to a category, such as _atom_site and 
456
 
C<$i> refers to an item, such as _atom_site.id.
457
 
 
458
 
The method may be invoked with an $options string. These options 
459
 
are the following letters which may be concatenated in any order:
460
 
 
461
 
  d  writes debugging output to STDERR 
462
 
  l  writes program activity log to STDERR
463
 
 
464
 
=head1 COMMENTS
465
 
 
466
 
This module provides no error checking of files or objects, 
467
 
either against the dictionary, or otherwise. While 
468
 
the module is applicable to parsing either a 
469
 
file or a dictionary, dictionary 
470
 
information is not currently used in the parsing 
471
 
of files. So, for example, information about 
472
 
parent-child relationships between items is not 
473
 
present in a DataBlock object. Functionality related to these 
474
 
issues is being provided in additional modules such as STAR::Checker, 
475
 
and STAR::Filter.
476
 
 
477
 
=head1 AUTHOR
478
 
 
479
 
Wolfgang Bluhm, mail@wbluhm.com
480
 
 
481
 
=head2 Acknowledgments
482
 
 
483
 
Thanks to Phil Bourne, Helge Weissig, Anne Kuller, Doug Greer, 
484
 
Michele Bluhm, and others for support, help, and comments.
485
 
 
486
 
=head1 COPYRIGHT
487
 
 
488
 
A full copyright statement is provided with the distribution
489
 
Copyright (c) 2000 University of California, San Diego
490
 
 
491
 
=head1 SEE ALSO
492
 
 
493
 
STAR::DataBlock, STAR::Dictionary.
494
 
 
495
 
=cut