/[webpac2]/trunk/lib/WebPAC/Parser.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/WebPAC/Parser.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 712 - (show annotations)
Tue Sep 26 10:23:04 2006 UTC (17 years, 6 months ago) by dpavlin
File size: 9239 byte(s)
 r1018@llin:  dpavlin | 2006-09-26 12:20:52 +0200
 correct creation of lookups (by database and input)

1 package WebPAC::Parser;
2
3 use warnings;
4 use strict;
5
6
7 use PPI;
8 use PPI::Dumper;
9 use Data::Dump qw/dump/;
10 use File::Slurp;
11
12 use base qw/WebPAC::Common/;
13
14 =head1 NAME
15
16 WebPAC::Parser - parse perl normalization configuration files and mungle it
17
18 =head1 VERSION
19
20 Version 0.05
21
22 =cut
23
24 our $VERSION = '0.05';
25
26 =head1 SYNOPSIS
27
28 This module will parse L<WebPAC::Normalize/lookup> directives and generate source
29 to produce lookups and normalization.
30
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.
34
35 This is experimental code, but it replaces all older formats which where,
36 at one point in time, available in WebPAC.
37
38 FIXME
39
40 =head1 FUNCTIONS
41
42 =head2 new
43
44 Create new parser object.
45
46 my $parser = new WebPAC::Parser(
47 config => new WebPAC::Config(),
48 base_path => '/optional/path/to/conf',
49 );
50
51 =cut
52
53 sub new {
54 my $class = shift;
55 my $self = {@_};
56 bless($self, $class);
57
58 my $log = $self->_get_logger();
59
60 $log->logdie("config isn't WebPAC::Config") unless ($self->{config} && $self->{config}->isa('WebPAC::Config'));
61
62 $log->logdie("can't iterate_inputs over this config object") unless ($self->{config}->can('iterate_inputs'));
63
64 $self->_read_sources;
65
66 $self ? return $self : return undef;
67 }
68
69 =head2 valid_database
70
71 my $ok = $parse->valid_database('key');
72
73 =cut
74
75 sub valid_database {
76 my $self = shift;
77
78 my $database = shift || return;
79
80 return defined($self->{valid_inputs}->{ _q($database) });
81 }
82
83 =head2 valid_database_input
84
85 my $ok = $parse->valid_database('database_key','input_name');
86
87 =cut
88
89 sub valid_database_input {
90 my $self = shift;
91 my ($database,$input) = @_;
92 $input = _input_name($input);
93 return defined($self->{valid_inputs}->{ _q($database) }->{ _q($input) });
94 }
95
96 =head2 depends
97
98 Return all databases and inputs on which specified one depends
99
100 $depends_on = $parser->depends('database','input');
101
102 =cut
103
104 sub depends {
105 my $self = shift;
106 my ($database,$input) = @_;
107 $input = _input_name($input);
108 $self->_get_logger->debug("depends($database,$input)");
109 return unless (
110 defined( $self->{depends}->{ _q($database) } ) &&
111 defined( $self->{depends}->{ _q($database) }->{ _q($input) } )
112 );
113 return $self->{depends}->{ _q($database) }->{ _q($input) };
114 }
115
116 =head2 have_lookup_create
117
118 my @keys = $parser->have_lookup_create($database, $input);
119
120 =cut
121
122 sub have_lookup_create {
123 my $self = shift;
124 my ($database,$input) = @_;
125 $input = _input_name($input);
126 return unless (
127 defined( $self->{_lookup_create_key}->{ _q($database) } ) &&
128 defined( $self->{_lookup_create_key}->{ _q($database) }->{ _q($input) } )
129 );
130 return keys %{ $self->{_lookup_create_key}->{ _q($database) }->{ _q($input) } };
131 }
132
133
134 =head2 lookup_create_rules
135
136 my $source = $parser->lookup_create_rules($database, $input);
137
138 =cut
139
140 sub lookup_create_rules {
141 my $self = shift;
142 my ($database,$input) = @_;
143 $input = _input_name($input);
144 return unless (
145 defined( $self->{_lookup_create}->{ _q($database) } ) &&
146 defined( $self->{_lookup_create}->{ _q($database) }->{ _q($input) } )
147 );
148 return $self->{_lookup_create}->{ _q($database) }->{ _q($input) };
149 }
150
151 =head1 PRIVATE
152
153 =head2 _read_sources
154
155 my $source_files = $parser->_read_sources;
156
157 Called by L</new>.
158
159 =cut
160
161 sub _read_sources {
162 my $self = shift;
163
164 my $log = $self->_get_logger();
165
166 my $nr = 0;
167
168 my @lookups;
169
170 $self->{config}->iterate_inputs( sub {
171 my ($input, $database) = @_;
172
173 $log->debug("database: $database input = ", dump($input));
174
175 foreach my $normalize (@{ $input->{normalize} }) {
176
177 my $path = $normalize->{path};
178 return unless($path);
179 my $full = $self->{base_path} ? $self->{base_path} . '/' . $path : $path;
180
181 $log->logdie("normalization input $full doesn't exist") unless (-e $full);
182
183 my $s = read_file( $full ) || $log->logdie("can't read $full: $!");
184
185 my $input_name = _input_name($input) || $log->logdie("can't deduce name of input: ", dump( $input ));
186
187 $log->debug("$database/$input_name: adding $path");
188
189 $self->{valid_inputs}->{$database}->{$input_name}++;
190
191 push @lookups, sub {
192 $self->_parse_lookups( $database, $input_name, $full, $s );
193 };
194
195 $nr++;
196 }
197 } );
198
199 $log->debug("found $nr source files");
200
201 # parse all lookups
202 $_->() foreach (@lookups);
203
204 return $nr;
205 }
206
207 =head2 _parse_lookups
208
209 $parser->_parse_lookups($database,$input,$path,$source);
210
211 Called for each normalize source (rules) in each input by L</_read_sources>
212
213 It will report invalid databases and inputs in error log after parsing.
214
215 =cut
216
217 sub _parse_lookups {
218 my $self = shift;
219 my ($database, $input, $path, $source) = @_;
220
221 $input = _input_name($input);
222
223 my $log = $self->_get_logger();
224
225 $log->logdie("invalid database $database" ) unless $self->valid_database( $database );
226 $log->logdie("invalid input $input of database $database", ) unless $self->valid_database_input( $database, $input );
227
228 $log->logdie("no source found for database $database input $input path $path") unless ($source);
229
230 $log->info("parsing lookups for $database/$input from $path (",length($source)," bytes)");
231
232 my $Document = PPI::Document->new( \$source ) || $log->logdie("can't parse source:\n", $self->{source});
233
234 $Document->prune('PPI::Token::Whitespace');
235 #$Document->prune('PPI::Token::Operator');
236
237 # Find all the named subroutines
238
239 $self->{_lookup_errors} = ();
240
241 sub _lookup_error {
242 my $self = shift;
243 my $msg = shift;
244 $self->_get_logger->logconfess("error without message?") unless ($msg);
245 push @{ $self->{_lookup_errors} }, $msg;
246 return '';
247 }
248
249 $Document->find( sub {
250 my ($Document,$Element) = @_;
251
252 $Element->isa('PPI::Token::Word') or return '';
253 $Element->content eq 'lookup' or return '';
254
255 $log->debug("expansion: ", $Element->snext_sibling);
256
257 my $args = $Element->snext_sibling;
258
259 my @e = $args->child(0)->elements;
260 $log->logdie("hum, expect at least 8 elements, got ", scalar @e, " in $args") if ($#e < 8);
261
262 if ($log->is_debug) {
263 my $report = "found " . scalar @e . " elements:\n";
264
265 foreach my $i ( 0 .. $#e ) {
266 $report .= sprintf("# %-2d: %-30s %s\n", $i, ( $e[$i] || 'undef' ), $e[$i]->class );
267 }
268
269 $log->debug($report);
270 }
271
272 my $key_element = $e[8]->clone;
273
274 $log->logdie("key element must be PPI::Structure::Block") unless $key_element->isa('PPI::Structure::Block');
275
276 $log->debug("key part: ", $key_element);
277
278 my @key;
279
280 $key_element->find( sub {
281 my $e = $_[1] || die "no element?";
282 $e->isa('PPI::Token::Word') or return '';
283 $e->content eq 'rec' or return '';
284
285 my $kf = $e->snext_sibling;
286
287 $log->debug("key fragment = $kf");
288
289 push @key, eval $kf;
290 $log->logdie("can't eval { $kf }: $@") if ($@);
291
292 return 1;
293 });
294
295 my $key = join('-', @key ) || $log->logdie("no key found!");
296
297 $log->debug("key = $key");
298
299 return $self->_lookup_error("invalid database $e[3] in $path" ) unless $self->valid_database( $e[3] );
300 return $self->_lookup_error("invalid input $e[5] of database $e[3] in $path", ) unless $self->valid_database_input( $e[3], $e[5] );
301
302 my $create = qq{
303 save_into_lookup($e[3],$e[5],'$key', $e[7] $e[8] );
304 };
305
306 $log->debug("create: $create");
307
308 # save code to create this lookup
309 $self->{_lookup_create}->{ _q($e[3]) }->{ _q($e[5]) } .= $create;
310 $self->{_lookup_create_key}->{ _q($e[3]) }->{ _q($e[5]) }->{ _q($key) }++;
311
312
313 if (defined( $self->{depends}->{ $database }->{ $input }->{ _q($e[3]) }->{ _q($e[5]) }->{ $key } )) {
314 $log->warn("dependency of $database/$input on $e[3]/$e[5] with $key allready recorded");
315 }
316
317 # save this dependency
318 $self->{depends}->{ $database }->{ $input }->{ _q($e[3]) }->{ _q($e[5]) }->{$key}++;
319
320 if ($#e < 10) {
321 $e[8]->insert_after( $e[8]->clone );
322 $e[8]->insert_after( $e[7]->clone );
323 $e[8]->insert_after( $e[6]->clone );
324 }
325
326 $e[7]->remove;
327 $e[8]->insert_before( new PPI::Token::Quote::Single( "'$key'" ) );
328 $e[8]->remove;
329
330
331 $log->debug(">>> ", $Element->snext_sibling);
332 });
333
334 my $normalize_source = $Document->serialize;
335 $log->debug("create: ", dump($self->{_lookup_create}) );
336 $log->debug("normalize: $normalize_source");
337
338 $self->{_normalize_source}->{$database}->{$input} = $normalize_source;
339
340 if ($self->{debug}) {
341 my $Dumper = PPI::Dumper->new( $Document );
342 $Dumper->print;
343 }
344
345 $log->error("Parser errors:\n", join("\n",@{ $self->{_lookup_errors} }) ) if ($self->{_lookup_errors});
346
347 return 1;
348 }
349
350
351 =head2 _q
352
353 Strip single or double quotes around value
354
355 _q(qq/'foo'/) -> foo
356
357 =cut
358
359 sub _q {
360 my $v = shift || return;
361 $v =~ s/^['"]*//g;
362 $v =~ s/['"]*$//g;
363 return $v;
364 }
365
366 =head2 _input_name
367
368 Return C<name> value if HASH or arg if scalar
369
370 _input_name($input)
371
372 =cut
373
374 sub _input_name {
375 my $input = shift || return;
376 if (ref($input) eq 'HASH') {
377 die "can't find 'name' value in ", dump($input) unless defined($input->{name});
378 return $input->{name};
379 } else {
380 return $input;
381 }
382 }
383
384
385 =head1 AUTHOR
386
387 Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>
388
389 =head1 COPYRIGHT & LICENSE
390
391 Copyright 2006 Dobrica Pavlinusic, All Rights Reserved.
392
393 This program is free software; you can redistribute it and/or modify it
394 under the same terms as Perl itself.
395
396 =cut
397
398 1; # End of WebPAC::Parser

  ViewVC Help
Powered by ViewVC 1.1.26