1 package WebPAC::Parser;
9 use Data::Dump qw/dump/;
12 use base qw/WebPAC::Common WebPAC::Normalize/;
16 WebPAC::Parser - parse perl normalization configuration files and mungle it
24 our $VERSION = '0.04';
28 This module will parse L<WebPAC::Normalize/lookup> directives and generate source
29 to produce lookups and normalization.
31 It's written using L<PPI>, pure-perl parser for perl and heavily influenced by
32 reading about LISP. It might be a bit over-the board, but at least it removed
33 separate configuration files for lookups.
35 This is experimental code, but it replaces all older formats which where,
36 at one point in time, available in WebPAC.
44 Create new parser object.
46 my $parser = new WebPAC::Parser(
47 config => new WebPAC::Config(),
48 base_path => '/optional/path/to/conf',
58 my $log = $self->_get_logger();
60 $log->logdie("config isn't WebPAC::Config") unless ($self->{config} && $self->{config}->isa('WebPAC::Config'));
62 $log->logdie("can't iterate_inputs over this config object") unless ($self->{config}->can('iterate_inputs'));
66 $self ? return $self : return undef;
71 my $source_files = $parser->read_sources;
80 my $log = $self->_get_logger();
86 $self->{config}->iterate_inputs( sub {
87 my ($input, $database) = @_;
89 $log->debug("database: $database input = ", dump($input));
91 foreach my $normalize (@{ $input->{normalize} }) {
93 my $path = $normalize->{path};
95 my $full = $self->{base_path} ? $self->{base_path} . '/' . $path : $path;
97 $log->logdie("normalization input $full doesn't exist") unless (-e $full);
99 my $s = read_file( $full ) || $log->logdie("can't read $full: $!");
101 my $input_name = _input_name($input) || $log->logdie("can't deduce name of input: ", dump( $input ));
103 $log->debug("$database/$input_name: adding $path");
105 $self->{valid_inputs}->{$database}->{$input_name}++;
108 $self->parse_lookups( $database, $input_name, $full, $s );
115 $log->debug("found $nr source files");
118 $_->() foreach (@lookups);
125 $parser->parse_lookups($database,$input,$path,$source);
127 Called for each normalize source in each input by L</new>
129 It will report invalid databases and inputs in error log after parsing.
135 my ($database, $input, $path, $source) = @_;
137 $input = _input_name($input);
139 my $log = $self->_get_logger();
141 $log->logdie("invalid database $database" ) unless $self->valid_database( $database );
142 $log->logdie("invalid input $input of database $database", ) unless $self->valid_database_input( $database, $input );
144 $log->logdie("no source found for database $database input $input path $path") unless ($source);
146 $log->info("parsing lookups for $database/$input from $path (",length($source)," bytes)");
148 my $Document = PPI::Document->new( \$source ) || $log->logdie("can't parse source:\n", $self->{source});
150 $Document->prune('PPI::Token::Whitespace');
151 #$Document->prune('PPI::Token::Operator');
153 # Find all the named subroutines
155 $self->{_lookup_errors} = ();
160 $self->_get_logger->logconfess("error without message?") unless ($msg);
161 push @{ $self->{_lookup_errors} }, $msg;
165 $Document->find( sub {
166 my ($Document,$Element) = @_;
168 $Element->isa('PPI::Token::Word') or return '';
169 $Element->content eq 'lookup' or return '';
171 $log->debug("expansion: ", $Element->snext_sibling);
173 my $args = $Element->snext_sibling;
175 my @e = $args->child(0)->elements;
176 $log->logdie("hum, expect at least 8 elements, got ", scalar @e, " in $args") if ($#e < 8);
178 if ($log->is_debug) {
179 my $report = "found " . scalar @e . " elements:\n";
181 foreach my $i ( 0 .. $#e ) {
182 $report .= sprintf("# %-2d: %-30s %s\n", $i, ( $e[$i] || 'undef' ), $e[$i]->class );
185 $log->debug($report);
188 my $key_element = $e[8]->clone;
190 $log->logdie("key element must be PPI::Structure::Block") unless $key_element->isa('PPI::Structure::Block');
192 $log->debug("key part: ", $key_element);
196 $key_element->find( sub {
197 my $e = $_[1] || die "no element?";
198 $e->isa('PPI::Token::Word') or return '';
199 $e->content eq 'rec' or return '';
201 my $kf = $e->snext_sibling;
203 $log->debug("key fragment = $kf");
206 $log->logdie("can't eval { $kf }: $@") if ($@);
211 my $key = join('-', @key ) || $log->logdie("no key found!");
213 $log->debug("key = $key");
216 $coderef = ' . $e[7] . $e[8] . ';
217 foreach my $v ($coderef->()) {
218 next unless (defined($v) && $v ne \'\');
219 push @{ $lookup->{\'' . $key . '\'}->{$v} }, $mfn;
223 $log->debug("create: $create");
225 return $self->_lookup_error("invalid database $e[3] in $path" ) unless $self->valid_database( $e[3] );
226 return $self->_lookup_error("invalid input $e[5] of database $e[3] in $path", ) unless $self->valid_database_input( $e[3], $e[5] );
228 # save code to create this lookup
229 $self->{_lookup_create}->{ _q($e[3]) }->{ _q($e[5]) }->{ _q($key) } .= $create;
232 if (defined( $self->{depends}->{ $database }->{ $input }->{ _q($e[3]) }->{ _q($e[5]) }->{ $key } )) {
233 $log->warn("dependency of $database/$input on $e[3]/$e[5] with $key allready recorded");
236 # save this dependency
237 $self->{depends}->{ $database }->{ $input }->{ _q($e[3]) }->{ _q($e[5]) }->{$key}++;
240 $e[8]->insert_after( $e[8]->clone );
241 $e[8]->insert_after( $e[7]->clone );
242 $e[8]->insert_after( $e[6]->clone );
246 $e[8]->insert_before( new PPI::Token::Quote::Single( "'$key'" ) );
250 $log->debug(">>> ", $Element->snext_sibling);
253 my $normalize_source = $Document->serialize;
254 $log->debug("create: ", dump($self->{_lookup_create}) );
255 $log->debug("normalize: $normalize_source");
257 $self->{_normalize_source}->{$database}->{$input} = $normalize_source;
259 if ($self->{debug}) {
260 my $Dumper = PPI::Dumper->new( $Document );
264 $log->error("Parser errors:\n", join("\n",@{ $self->{_lookup_errors} }) ) if ($self->{_lookup_errors});
270 =head2 lookup_create_rules
272 my $source = $parser->lookup_create_rules($database, $input);
276 sub lookup_create_rules {
278 my ($database,$input) = @_;
279 return $self->{_lookup_create}->{ _q($database) }->{ _q($input) };
282 =head2 valid_database
284 my $ok = $parse->valid_database('key');
291 my $database = shift || return;
293 return defined($self->{valid_inputs}->{ _q($database) });
296 =head2 valid_database_input
298 my $ok = $parse->valid_database('database_key','input_name');
302 sub valid_database_input {
305 my ($database,$input) = @_;
306 return defined($self->{valid_inputs}->{ _q($database) }->{ _q($input) });
311 Return all databases and inputs on which specified one depends
313 $depends_on = $parser->depends('database','input');
319 my ($database,$input) = @_;
320 $self->_get_logger->debug("depends($database,$input)");
322 defined( $self->{depends}->{ _q($database) } ) &&
323 defined( $self->{depends}->{ _q($database) }->{ _q($input) } )
325 return $self->{depends}->{ _q($database) }->{ _q($input) };
332 Strip single or double quotes around value
339 my $v = shift || return;
347 Return C<name> value if HASH or arg if scalar
354 my $input = shift || return;
355 if (ref($input) eq 'HASH') {
356 die "can't find 'name' value in ", dump($input) unless defined($input->{name});
357 return $input->{name};
366 Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>
368 =head1 COPYRIGHT & LICENSE
370 Copyright 2006 Dobrica Pavlinusic, All Rights Reserved.
372 This program is free software; you can redistribute it and/or modify it
373 under the same terms as Perl itself.
377 1; # End of WebPAC::Parser