File Coverage

blib/script/sqlpp
Criterion Covered Total %
statement 312 447 69.8
branch 157 318 49.3
condition 60 140 42.8
subroutine 27 32 84.3
pod n/a
total 556 937 59.3


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl -w
2              
3 1         2 eval 'exec /usr/local/bin/perl -w -S $0 ${1+"$@"}'
4             if 0; # not running under some shell
5            
6             # $Id: sqlpp,v 1.15 2007/03/24 12:22:30 dk Exp $
7            
8 1     1   8 use strict;
  1         2  
  1         46  
9 1     1   5 use vars qw($input $output @inc @context $context $sigdie %defines %macros $debug $VERSION);
  1         1  
  1         239  
10 1     1   5 use vars qw(%global); # for perldef
  1         4  
  1         436  
11            
12 1         3 $VERSION = '0.06';
13            
14             # special predefined macros
15             %defines = (
16             __LINE__ => {
17 0     0   0 code => sub { $context->{line} },
18             },
19             __FILE__ => {
20 0     0   0 code => sub { $context->{file} },
21             },
22             __VERSION__ => {
23 0     0   0 code => sub { $VERSION },
24             },
25             '#' => {
26             num => 1,
27             name => '#',
28             code => sub {
29 1     1   3 my $x = $_[0];
30 1         4 $x =~ s/([\\'])/\\$1/gs;
31 1         5 "'$x'";
32             },
33             },
34 1         23 );
35            
36 1     1   7 use constant MACRO_OFF => 0; # none
  1         2  
  1         289  
37 1     1   6 use constant MACRO_SIMPLE => 1; # #defines with no-parameters only
  1         2  
  1         42  
38 1     1   5 use constant MACRO_COMPLEX => 2; # #defines with parameters only
  1         1  
  1         40  
39 1     1   6 use constant MACRO_ALL => 3; # all #defines
  1         2  
  1         33989  
40            
41             # run
42 1         3 $debug = 0;
43            
44 1         5 $context = new_context( file => 'command line', macro => MACRO_OFF );
45 1         8 parse_argv();
46            
47 1         3 $context = new_context();
48 1         7 parse_input();
49            
50            
51             # used for serving 'defined' call from #if, which is basically perl code
52 0 0   0   0 sub is_defined { exists ($defines{$_[0]}) ? 1 : 0 }
53            
54             $SIG{__DIE__} = sub {
55             # avoid multiple wrappings by perl's "use" - careful when recovering from an eval!
56 1 50   1   5 die @_ if $sigdie++;
57            
58 1         0 die "error in `$context->{file}', line #$context->{line}: ", @_, "\n";
59 1         20 };
60 1         4 parse_file(1);
61 0         0 exit;
62            
63             # a context defines state of parser in a file
64             sub new_context
65             {
66             {
67 2     2   25 line => 0,
68             buf => '',
69             in_comment => 0,
70             ifdef => [{state => 1,passive=>[]}],
71             in_sql => 0,
72             macro => MACRO_ALL,
73             strip => 1,
74             @_
75             }
76             }
77            
78             # does buffered input
79             sub getline
80             {
81 131     131   165 my $undef_if_eof = $_[0];
82 131 100       274 if ( length $context->{buf}) {
83 17         29 my $ret = $context->{buf};
84 17         26 $context->{buf} = '';
85 17         42 return $ret;
86             }
87 114         129 my $ret;
88 114 50       349 unless ( defined ($ret = <$input>)) {
89 0 0       0 die "Unexpected end of input\n" unless $undef_if_eof;
90             } else {
91 114         161 $context->{line}++;
92             }
93 114         371 $ret;
94             }
95            
96             # skips input until the EOL
97 43     43   134 sub eatline { $context->{buf} = '' }
98            
99             # returns next token from input stream
100             sub gettok
101             {
102 28     28   36 while ( 1) {
103 28 50       64 unless ( length $context->{buf}) {
104 0 0       0 unless ( defined ($context->{buf} = <$input>)) {
105 0         0 die "Unexpected end of input\n";
106             }
107 0         0 chomp $context->{buf};
108 0         0 $context->{line}++;
109             }
110            
111 28         64 $context->{buf} =~ s/^\s+//;
112            
113 28 50       157 return $1
114             if $context-> {buf} =~ s/^(\w+|\S)//;
115             }
116             }
117            
118             # returns ID from input stream
119             sub getid
120             {
121 28     28   77 my $tok = gettok;
122 28 50       109 die "Identificator expected\n" unless $tok =~ /^\w+$/;
123 28         51 $tok;
124             }
125            
126             # Line handle is a state of the parser as it progresses through input .
127             # The idea is to avoid accumultaion of input until the end of input, and
128             # spew processed data as soon as possible. The calling routing thus is
129             # begin_line / while( not parse_line) / print end_line, with different
130             # variations.
131             #
132             # Currently, parse_line returns 0 ( a signal to call end_line ) when
133             # bracket balance is achieved - but there's a bug with macro
134             # call MACRO\n() where MACRO and () are on different lines.
135            
136 2     2   8 sub new_line_handle { {} }
137            
138             sub begin_line
139             {
140 47   66 47   112 my $k = $_[0] || new_line_handle;
141 47         80 $k-> {var} = ''; # text to parse
142 47         103 $k-> {ids} = []; # stack of IDs met, f.ex. if var="A(b,C(d,", then ids=(A,C)
143 47         73 $k-> {last_id} = ''; # a candidate to ids
144 47         68 $k-> {last_pos} = 0; # stores pos(var) between calls
145 47         104 $k-> {storage} = [ 'copy', 0 ]; # accululates parsed info, to be run throung substitute_parameters later
146 47         111 $k-> {run_stack}= []; # stack of macro calls
147 47         72 $k-> {run} = $k-> {storage};# current macro call context
148 47         164 $k;
149             }
150            
151             sub parse_line
152             {
153 52     52   68 my $k = $_[0];
154 52   100     227 $k-> {last_pos} = pos( $k-> {var}) || 0;
155 52         114 $k-> {var} .= $_[1];
156 52         76 my $full = $context-> {macro} & MACRO_COMPLEX;
157 52         71 my $simple = $context-> {macro} & MACRO_SIMPLE;
158 52         131 pos( $k-> {var}) = $k-> {last_pos};
159             {
160             # do comments
161 52 50 33     98 $context->{multiline_comment} and $k-> {var} =~ m/\G.*?(\*\/)?/gcs and do {
  271         674  
162 0 0       0 $context-> {multiline_comment} = 0 if $1;
163 0         0 redo;
164             };
165             ( $k-> {var} =~ m/\G--/ or (
166             not $k-> {macro} and $k-> {var} =~ m/\G#/
167 271 50 66     1672 )) and do {
      33        
168 0 0       0 if ( $context->{strip}) {
    0          
169 0         0 my $savepos = pos( $k-> {var});
170 0         0 $k-> {var} =~ s/\G.*$//g;
171 0         0 pos( $k-> {var}) = $savepos;
172             } elsif ( $k-> {macro}) {
173 0         0 $k-> {var} =~ m/\G--/gc;
174             } else {
175 0         0 $k-> {var} =~ m/\G(--|#)/gc;
176             }
177 0         0 redo;
178             };
179 271 50       586 $k-> {var} =~ m/\G\/\*/gcs and do {
180 0         0 $context-> {multiline_comment} = 1;
181 0         0 redo;
182             };
183 271 50       501 $k-> {var} =~ m/\G-+/gc and redo;
184            
185             # do identifiers
186 271 100       714 $k-> {var} =~ m/\G(\w+)/gcs and do {
187 82 100 100     509 if ( $k->{parameters} and exists $k->{parameters}->{$1}) {
    100 66        
188 10         18 $k-> {last_id} = '';
189 10         11 push @{$k->{run}},
  10         64  
190             pos( $k->{var}) - length($1),
191             'parameter', $k->{parameters}->{$1},
192             'copy', pos( $k->{var});
193             } elsif ( $simple and exists $defines{$1}) {
194 8         24 my ( $l1, $d, $p) = ( length( $1), $defines{$1}, pos($k->{var}));
195 8         13 $k-> {last_id} = '';
196 8         11 push @{$k->{run}},
  8         37  
197             $p - $l1,
198             'define', $defines{$1},
199             'copy', $p;
200             } else {
201 64         121 $k-> {last_id} = $1;
202 64         150 $k-> {last_id_pos_start} = pos($k-> {var}) - length($1);
203             }
204 82 50       221 print "- id $k->{last_id}\n" if $debug;
205 82         90 redo;
206             };
207            
208             # do opening bracket
209 189 100 66     821 $full and $k-> {var} =~ m/\G\(\s*/gcs and do {
210 24         25 push @{$k-> {ids}}, [ $k-> {last_id}, $context->{line}];
  24         100  
211 24 100 66     133 if ( length $k->{last_id} and $macros{$k->{last_id}}) {
212 22         23 push @{$k->{run_stack}}, $k->{run};
  22         55  
213 22         27 push @{$k->{run}},
  22         121  
214             $k-> {last_id_pos_start},
215             'macro', $macros{$k->{last_id}},
216             [
217             'copy', pos($k->{var}),
218             ];
219 22         50 $k->{run} = $k->{run}->[-1];
220             }
221 24         28 $k-> {last_id} = '';
222 24 50       56 print "- open\n" if $debug;
223 24         28 redo;
224             };
225            
226             # nulling ID after right after comments and IDs are processed is basically
227             # a grammar rule that states that in a macro call nothing except a comment
228             # and whitespace can be present between a macro ID and an opening bracket
229 165 100       408 $k-> {var} =~ m/\G\s+/gcs and redo;
230 112         218 $k-> {last_id} = '';
231            
232             # do closing bracket
233 112 100 66     513 $full and $k-> {var} =~ m/\G(\s*\))/gcs and do {
234 24         62 die "Brackets mismatch at character ", pos($k-> {var})-$k-> {last_pos}, "\n"
235 24 50       27 unless @{$k-> {ids}};
236 24         27 my $id = (pop @{$k->{ids}})->[0];
  24         75  
237 24 50       63 print "- close [$id]\n" if $debug;
238            
239 24 100 66     97 if ( length $id and $macros{$id}) {
240 22         24 push @{$k->{run}}, pos($k->{var}) - length($1);
  22         65  
241 22         27 $k->{run} = pop @{$k->{run_stack}};
  22         40  
242 22         29 push @{$k->{run}}, 'copy', pos($k->{var});
  22         59  
243             }
244 24         32 redo;
245             };
246            
247             # do next param
248 88 100 66     354 $full and $k-> {var} =~ m/\G(\s*,\s*)/gcs and do {
249 16 50       20 redo unless @{$k->{ids}};
  16         36  
250            
251 16 50 66     86 if ( length($k->{ids}->[-1]->[0]) and
  14   66     64  
252             $macros{$k->{ids}->[-1]->[0]} and @{$k->{run_stack}}) {
253 14         18 push @{$k->{run}},
  14         71  
254             pos($k-> {var}) - length($1),
255             'next',
256             'copy', pos($k-> {var})
257             }
258 16         22 redo;
259             };
260            
261             # special # and ## operators
262 72 100 100     211 $k->{macro} and $k->{var} =~ /\G\#(?:(\#\s*)|(\s*)(\w+)|(.*))/gcs and do {
263 5 100 66     24 if ( defined $1) {
    100          
264             # concatenation
265 3         6 my $minus = 1 + length($1);
266 3   66     34 $minus++ while
267             $minus < pos($k->{var}) and
268             substr( $k->{var}, pos($k->{var}) - $minus - 1, 1) eq ' ';
269 3         5 push @{$k->{run}},
  3         22  
270             pos($k->{var}) - $minus,
271             'copy', pos($k->{var});
272             } elsif ( defined $3 and exists $k->{parameters}->{$3}) {
273             # stringification
274 1         2 push @{$k->{run}},
  1         10  
275             pos($k->{var}) - 1 - length($2) - length($3),
276             'macro', $defines{'#'},
277             [ 'parameter', $k->{parameters}->{$3} ],
278             'copy', pos($k->{var});
279             } else {
280 1 50       20 die "'#' is not followed by a macro parameter (",
281             (( defined $3) ? $3 : $4),
282             ")\n";
283             }
284 4         6 redo;
285             };
286            
287             # we do ''-only strings
288 67 100 66     293 $full and $k-> {var} =~ m/\G'[^']*'/gcs and redo;
289            
290             # everything else
291 66 50       145 if ( $full) {
292 66 100       167 $k-> {var} =~ m/\G[^\w\(\)\'\-\,\#]+/gcs and redo;
293             } else {
294 0 0       0 $k-> {var} =~ m/\G[^\w\-\#]+/gcs and redo;
295             }
296 51 50 33     116 !$full and $k-> {var} =~ m/\G[\(\)\']+/gcs and redo;
297            
298 51 50       99 print "? stop at ", pos($k-> {var}), "\n" if $debug;
299 51 50       84 print +('.' x (pos($k-> {var})-1)), "v\n$k->{var}\n" if $debug;
300             }
301            
302 51 50       48 return scalar(@{$k-> {ids}}) ? 0 : 1;
  51         247  
303             }
304            
305             sub end_parse_line
306             {
307 45     45   50 my $k = $_[0];
308 45         109 die "Brackets don't match at character ", pos($k->{var}) - $k-> {last_pos}, ", line $k->{ids}->[-1]->[1]\n"
309 45 50       43 if @{$k-> {ids}};
310 45         51 push @{$k->{run}}, length($k->{var});
  45         151  
311 45         185 delete @$k{qw(run run_stack last_id last_pos last_id_pos_start ids)};
312             }
313            
314             # input:
315             # k - text reference object
316             # v - set of commands, where 'copy' referes to text chunks in k
317             # p - set of actual parameters to be substututed
318             # output:
319             # text with parameters substituted
320             sub substitute_parameters
321             {
322 70     70   103 my ( $k, $v, $parameters) = @_;
323            
324 70         177 my @output = ('');
325            
326 70         826 for ( my $i = 0; $i < @$v; ) {
327 182         280 my $cmd = $v->[$i++];
328 182 100       391 if ( $cmd eq 'copy') {
    100          
    100          
    100          
    50          
329 126         341 $output[-1] .= substr( $k->{var}, $v->[$i], $v->[$i+1] - $v->[$i]);
330 126         299 $i += 2;
331             } elsif ( $cmd eq 'parameter') {
332 13         35 $output[-1] .= $parameters->[ $v->[$i++] ];
333             } elsif ( $cmd eq 'next') {
334 13         174 push @output, '';
335             } elsif ( $cmd eq 'macro') {
336 23         78 $output[-1] .= execute_macro(
337             $v->[$i],
338             substitute_parameters( $k, $v->[$i+1], $parameters)
339             );
340 23         76 $i += 2;
341             } elsif ( $cmd eq 'define') {
342 7         16 $output[-1] .= execute_macro( $v->[$i++]);
343             } else {
344 0         0 die "Internal error: unknown directive `$cmd' (i=$i, stack=@$v)\n";
345             }
346             }
347            
348             # XXX special case - zero parameters
349 70 100 100     354 return if 1 == @output and $output[0] eq '';
350            
351 67         311 return @output;
352             }
353            
354             sub execute_macro
355             {
356 30     30   55 my ( $handle, @params) = @_;
357            
358 30 0 66     257 die sprintf "Macro `%s' requires %d argument%s, %d %s passed\n",
    0 66        
    50          
359             $handle->{name},
360             $handle->{num}, ( $handle->{num} == 1) ? '' : 's',
361             scalar(@params), (scalar(@params) == 1) ? 'was' : 'were'
362             unless $handle->{ellipsis} or
363             !defined($handle->{num}) or
364             $handle->{num} == scalar(@params);
365            
366 30 100       511 return join($", $handle->{code}->(@params)) if $handle-> {code};
367            
368 13         30 return join('', substitute_parameters(
369             $handle,
370             $handle->{storage},
371             \@params
372             ));
373             }
374            
375             sub end_line
376             {
377 34     34   50 my $k = $_[0];
378 34         64 end_parse_line($k);
379 34         126 return join('', substitute_parameters( $k, $k->{storage}, [] ));
380             }
381            
382             # begin_macro/end_macro pairs are same as begin_line/end_line, but for macro declaration purposes
383             sub begin_macro
384             {
385 12     12   24 my ( $name, $parametric, @params ) = @_;
386            
387 12         16 my %p;
388 12         14 my $pno = 0;
389 12         20 for my $p ( @params) {
390 11 50       23 die "Error in macros `$name' definition: argument `$p' is used twice\n"
391             if $p{$p};
392 11 50       74 die "Error in macros `$name' definition: argument name `$p' is not a valid identifier\n"
393             if $p =~ /\'\(\)\#/;
394 11         36 $p{$p} = $pno++;
395             }
396            
397 12         133 return begin_line {
398             parametric => $parametric,
399             parameters => \%p,
400             name => $name,
401             macro => 1,
402             line => $context->{line},
403             file => $context->{file},
404             };
405             }
406            
407             sub end_macro
408             {
409 11     11   17 my $handle = $_[0];
410 11         14 end_parse_line( $handle);
411            
412 11 100       26 if ( $handle->{parametric}) {
413 9         20 $macros{ $handle->{name} } = $handle;
414 9         12 $handle->{num} = scalar keys %{$handle->{parameters}};
  9         27  
415             } else {
416 2         6 $defines{ $handle->{name} } = $handle;
417 2         5 $handle->{num} = 0;
418             }
419 11         38 delete @$handle{qw(parametric macro)};
420             }
421            
422             sub parse_pragma
423             {
424 0     0   0 my ( $pragma, $param) = @_;
425 0 0       0 if ( $pragma eq 'macro') {
    0          
    0          
426 0 0       0 if ( $param eq 'simple') {
    0          
    0          
427 0         0 $context->{macro} = MACRO_SIMPLE;
428             } elsif ( $param eq 'all') {
429 0         0 $context->{macro} = MACRO_ALL;
430             } elsif ( $param eq 'off') {
431 0         0 $context->{macro} = MACRO_OFF;
432             } else {
433 0         0 die "Invalid '#pragma macro($param)': should be 'all', 'simple', or 'off'\n";
434             }
435             } elsif ( $pragma eq 'comment') {
436 0 0       0 if ( $param eq 'strip') {
    0          
437 0         0 $context->{strip} = 1;
438             } elsif ( $param eq 'leave') {
439 0         0 $context->{strip} = 0;
440             } else {
441 0         0 die "Invalid '#pragma comments($param)': should be 'strip' or 'leave'\n";
442             }
443             } elsif ( $pragma eq 'lang') {
444 0 0       0 if ( $param eq 'sql') {
    0          
445 0         0 parse_pragma(qw(macro all));
446 0         0 parse_pragma(qw(comment strip));
447             } elsif ( $param eq 'perl') {
448 0         0 parse_pragma(qw(macro simple));
449 0         0 parse_pragma(qw(comment leave));
450             } else {
451 0         0 die "Invalid '#pragma lang($param)': should be 'sql' or 'perl'\n";
452             }
453             } else {
454 0         0 die "Unknown #pragma $pragma\n";
455             }
456             }
457            
458             # if a line begins with #, then parse_comment checks it first
459             sub parse_comment
460             {
461 63     63   70 my $eatline = 1;
462 63         64 my $what;
463            
464 63 100       249 if ( $context->{buf} !~ s/^(\w+)\s+//) {
465             # a comment
466 21         56 eatline;
467 21         54 return;
468             } else {
469 42         78 $what = $1;
470             }
471            
472             # parse if/else/elif/endif in the passive code less heavily
473 42 100       127 unless ( $context->{ifdef}->[-1]->{state}) {
474 6 50       27 if ( $what =~ /^if(n?def)?$/) {
    100          
    50          
    50          
475 0         0 push @{$context->{ifdef}->[-1]->{passive}}, 1; # flipsleft
  0         0  
476             } elsif ( $what eq 'else') {
477 3 50       4 goto NORMAL unless @{$context->{ifdef}->[-1]->{passive}};
  3         31  
478 0 0       0 die "Too many #else\n" unless $context->{ifdef}->[-1]->{passive}->[-1]--;
479             } elsif ( $what eq 'elif') {
480 0 0       0 goto NORMAL unless @{$context->{ifdef}->[-1]->{passive}};
  0         0  
481             } elsif ( $what eq 'endif') {
482 3 50       4 goto NORMAL unless @{$context->{ifdef}->[-1]->{passive}};
  3         36  
483 0         0 pop @{$context->{ifdef}->[-1]->{passive}};
  0         0  
484             }
485 0         0 eatline;
486 0         0 return;
487             }
488            
489             # normal '#' pragmas
490             NORMAL:
491 42 100       193 if ( $what eq 'define') {
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
    50          
    50          
    0          
492 14         32 my $heredoc = $context->{buf} =~ s/^<
493 14         25 my $def = getid();
494            
495 14         18 my @params;
496 14         15 my $parametric = 0;
497 14 100       63 if ( $context->{buf} =~ s/^\(([^\)]*)\)//) {
498 11         35 @params = map {
499 10         487 s/^\s*//;
500 11         34 s/\s*$//;
501 11 50       31 die "`$1' may not appear in macro parameter list\n"
502             if m/(\W)/;
503 11         36 $_
504             } split ',', $1;
505 10         18 $parametric = 1;
506             }
507 14         44 $context->{buf} =~ s/^\s*//;
508            
509 14         20 $eatline = 0;
510 14 100 100     64 if ( $heredoc or length $context->{buf}) {
    100          
511 12         28 my $v = begin_macro( $def, $parametric, @params);
512            
513 12         17 my $do_ml = 1;
514 12         24 while ( $do_ml) {
515 18         28 my $l = getline;
516 18         31 chomp $l;
517 18 100       28 if ( $heredoc) {
518 7 50       17 last if $l eq $def;
519             } else {
520 11         29 $do_ml = $l =~ s/\\\s*$//;
521             }
522 18 100       59 parse_line( $v, $l . ( $do_ml ? "\n" : ''));
523             }
524            
525             # check if macro already exists by comparing with the macro body
526 11 100       27 my $ref = $parametric ? $macros{$def} : $defines{$def};
527 11 50       23 if ( defined $ref) {
528 0         0 my $fail;
529 0 0       0 if ( !defined $ref->{var}) {
530 0         0 $fail = 1;
531             } else {
532             $fail = (
533 0   0     0 join(':', keys %{$ref->{parameters}})
534             ne
535             join(':', @params)
536             ) || (
537             $ref->{var}
538             ne
539             $v->{var}
540             );
541             }
542 0 0       0 warn "`$def' redefined, previous declaration in $ref->{file}:$ref->{line}\n"
543             if $fail;
544             }
545            
546             # register the macro
547 11         21 end_macro( $v);
548             } elsif ( $parametric) { # special macro
549 1 50 33     6 warn "`$def' redefined, previous declaration in $macros{$def}->{file}:$macros{$def}->{line}\n"
550             if exists $macros{$def} and defined $macros{$def}->{var};
551 1         8 $macros{$def} = {
552             name => $def,
553             num => scalar(@params),
554             storage => [],
555             line => $context->{line},
556             file => $context->{file},
557             }
558             } else { # special define
559 1 50 33     46 warn "`$def' redefined, previous declaration in $defines{$def}->{file}:$defines{$def}->{line}\n"
560             if exists $defines{$def} and defined $defines{$def}->{var};
561 1         7 $defines{$def} = {
562             name => $def,
563             num => 0,
564             storage => [],
565             line => $context->{line},
566             file => $context->{file},
567             }
568             }
569             } elsif ( $what eq 'undef') {
570 3         7 my $def = getid();
571 3         12 delete $defines{$def};
572 3         6 delete $macros{$def};
573             } elsif ( $what =~ /if(n?)def/) {
574 6         12 my $def = getid();
575 6 100       16 my $notdef = length($1) ? 1 : 0;
576 6 100       7 push @{$context->{ifdef}}, {
  6 100       43  
577             state => exists($defines{$def}) ? !$notdef : $notdef,
578             flipsleft => 1,
579             passive => [],
580             do_else => exists($defines{$def}) ? $notdef : !$notdef,
581             };
582             } elsif ( $what eq 'if') {
583 1         2 my $do_ml = 1;
584 1         2 my $v = begin_line;
585 1         3 while ( $do_ml) {
586 1         3 my $l = getline;
587 1         3 chomp $l;
588 1         3 $do_ml = $l =~ s/\\\s*$//;
589 1         2 $l =~ s/defined\s*\(([^\)\s]+)\s*\)\s*/is_defined($1)/ge; # XXX a hack
  0         0  
590 1 50       4 parse_line( $v, $l . ( $do_ml ? "\n" : ''));
591             }
592 1         4 my $if = end_line($v);
593 1         73 my $ret = eval $if;
594 1 50       5 die $@ if $@;
595 1 50       1 push @{$context->{ifdef}}, {
  1 50       8  
596             state => $ret ? 1 : 0,
597             flipsleft => 1,
598             passive => [],
599             do_else => ( $ret ? 0 : 1),
600             };
601 1         4 $eatline = 0;
602             } elsif ( $what eq 'elif') {
603 0         0 die "Runaway #elif\n" if
604 0         0 0 == $#{$context->{ifdef}} or
605 0 0 0     0 @{$context->{ifdef}->[-1]->{passive}};
606 0         0 my $do_ml = 1;
607 0         0 my $v = begin_line;
608 0         0 while ( $do_ml) {
609 0         0 my $l = getline;
610 0         0 chomp $l;
611 0         0 $do_ml = $l =~ s/\\\s*$//;
612 0         0 $l =~ s/defined\s*\(([^\)\s]+)\s*\)\s*/is_defined($1)/ge; # XXX a hack
  0         0  
613 0 0       0 parse_line( $v, $l . ( $do_ml ? "\n" : ''));
614             }
615 0         0 my $if = end_line($v);
616 0 0       0 if ( $context->{ifdef}->[-1]->{do_else}) {
617 0         0 my $ret = eval $if;
618 0 0       0 die $@ if $@;
619 0 0       0 $context->{ifdef}->[-1]->{state} = ($ret ? 1 : 0);
620 0 0       0 $context->{ifdef}->[-1]->{do_else} = 0 if $ret;
621             } else {
622 0         0 $context->{ifdef}->[-1]->{state} = 0;
623             }
624 0         0 $eatline = 0;
625             } elsif ( $what eq 'else') {
626 6         18 die "Runaway #else\n" if
627 6         22 0 == $#{$context->{ifdef}} or
628 6 50 33     8 @{$context->{ifdef}->[-1]->{passive}};
629 6 50       16 die "Too many #else\n" unless $context->{ifdef}->[-1]->{flipsleft}--;
630 6 100       19 $context->{ifdef}->[-1]->{state} = $context->{ifdef}->[-1]->{state} ?
631             0 :
632             $context->{ifdef}->[-1]->{do_else};
633             } elsif ( $what eq 'endif') {
634 7         52 die "Runaway #endif\n" if
635 7         29 0 == $#{$context->{ifdef}} or
636 7 50 33     7 @{$context->{ifdef}->[-1]->{passive}};
637 7         9 pop @{$context->{ifdef}};
  7         11  
638             } elsif ( $what eq 'error') {
639 0         0 die getline;
640             } elsif ( $what eq 'include') {
641 0         0 my $bracket = gettok();
642 0 0       0 die "format #include or #include \"file\"\n"
643             unless $bracket =~ /^["<]$/;
644 0         0 my $file;
645             my @local_inc;
646 0 0       0 if ( $bracket eq '<') {
647 0         0 @local_inc = ( @inc, '.');
648 0 0       0 die "format #include \n" unless $context->{buf} =~ s/([^>]*)>//;
649 0         0 $file = $1;
650             } else {
651 0         0 @local_inc = ( '.');
652 0 0       0 die "format #include \"file\"\n" unless $context->{buf} =~ s/([^"]*)"//;
653 0         0 $file = $1;
654             }
655 0         0 my $found;
656 0         0 for my $inc ( @local_inc) {
657 0 0       0 next unless -f "$inc/$file";
658 0         0 $found = "$inc/$file";
659 0         0 last;
660             }
661 0 0       0 die "Cannot find file `$file' in path [@local_inc]\n" unless $found;
662 0         0 $file = $found;
663            
664 0         0 local $input;
665 0 0       0 open $input, "< $file" or die "Cannot open $file\n";
666 0         0 push @context, $context;
667 0         0 $context = new_context( file => $file);
668 0         0 parse_file(1);
669 0         0 $context = pop @context;
670 0         0 close $input;
671             } elsif ( $what eq 'pragma') {
672 0         0 my $pragma = gettok();
673 0 0       0 my $param = length($context->{buf}) ? getline() : '';
674 0         0 $param =~ s/^[\s\(]*(\w+)[\s\)\#]*$/$1/m;
675            
676 0         0 parse_pragma( $pragma, $param);
677             } elsif ( $what eq 'perldef') {
678 5         5 $eatline = 0;
679 5         13 my $heredoc = $context->{buf} =~ s/^<
680 5         11 my $def = getid();
681            
682 5         7 my ( $ellipsis, @params);
683 5         7 my $parametric = 0;
684 5 100       20 if ( $context->{buf} =~ s/^\(([^\)]*)\)//) {
685 2 100       7 if ( $1 eq '...') {
686 1         2 $ellipsis = 1;
687             } else {
688 2         6 @params = map {
689 1         5 s/^\s*//;
690 2         6 s/\s*$//;
691 2 50       7 die "`$_' is not a valid Perl scalar declaration (must begin with \$)\n"
692             unless m/^\$\w+$/;
693 2         5 $_
694             } split ',', $1;
695             }
696 2         4 $parametric = 1;
697             }
698 5         18 $context->{buf} =~ s/^\s*//;
699 5 50 33     30 die "Empty #perldef declaration `$def'\n"
700             unless $heredoc or length $context->{buf};
701            
702 5         9 my $perlcode = "sub {\n";
703 5 100 100     21 $perlcode .= "my (" . join( ', ', @params) . ") = \@_;\n"
704             if !$ellipsis and @params;
705            
706 5         8 my $do_ml = 1;
707 5         13 while ( $do_ml) {
708 10         14 my $l = getline;
709 10         13 chomp $l;
710 10 50       15 if ( $heredoc) {
711 0 0       0 last if $l eq $def;
712             } else {
713 10         38 $do_ml = $l =~ s/\\\s*$//;
714             }
715 10 100       31 $perlcode .= $l . ( $do_ml ? "\n" : '');
716             }
717 5         6 $perlcode .= "\n}";
718 5         540 my $p = eval $perlcode;
719 5 50       17 unless ( defined $p) {
720 0         0 $@ =~ s/at \(eval \d+\) line (\d+), //gs;
721 0         0 $@ =~ s/<\$ih>\s+//gs;
722 0         0 die "$@\n";
723             }
724 5 100       40 ( $parametric ? $macros{$def} : $defines{$def} ) = {
725             name => $def,
726             num => scalar(@params),
727             storage => [],
728             ellipsis => $ellipsis,
729             code => $p,
730             };
731             } elsif ( $what =~ /^([\w\d_]+)/) {
732 0         0 die "Invalid preprocessor directive '$1'\n";
733             } else {
734             # a true comment
735             }
736            
737 41 100       139 eatline if $eatline;
738             }
739            
740             sub parse_file
741             {
742 1     1   3 my $do_output = $_[0];
743 1         1 my $l;
744 1         6 my $h = begin_line;
745 1         5 while ( defined ( $l = getline(1))) {
746 102 100 66     725 if ( !$context->{multiline_comment} and $l =~ s/^#//) {
    100 66        
747 63         105 $context->{buf} = $l;
748 63         142 parse_comment( $l);
749             } elsif ( $context->{ifdef}->[-1]->{state} and parse_line( $h, $l)) {
750 33         70 $l = end_line($h);
751 33 50       102 print $l if $do_output;
752 33         56 begin_line($h);
753             }
754             }
755 0         0 end_line($h);
756 0 0       0 die "Runaway #ifdef\n" if $#{$context->{ifdef}};
  0         0  
757             }
758            
759             sub parse_input
760             {
761 1     1   1 my $ih;
762            
763 1 50       57 if ( $input eq '-') {
    50          
764 0         0 $input = \*STDIN;
765 0         0 $context->{file} = 'stdin';
766             } elsif ( ! open $ih, "< $input") {
767 0         0 die "Cannot open $input:$!\n";
768             } else {
769 1         3 $context->{file} = $input;
770 1         3 $input = $ih;
771             }
772            
773 1 50       5 if ( defined $output) {
774 0 0       0 open OUT, "> $output" or die "Cannot open $output:$!\n";
775 0         0 select OUT;
776             }
777             }
778            
779             sub parse_argv
780             {
781 1     1   2 my $dominus = 1;
782 1         7 for ( my $i = 0; $i < @ARGV; $i++) {
783            
784 1 50       3 die "Too many arguments\n" if $input;
785            
786 1         3 my $d = $ARGV[$i];
787 1 50 33     11 if ( $dominus and $d =~ s/^-//) {
788 0 0 0     0 if ( $d =~ /^I(.+)/ or
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
789             ( $d eq 'I' and
790             ( defined $ARGV[$i+1] or die "Argument required\n") and
791             $ARGV[++$i] =~ /(.*)/
792             )) {
793 0         0 push @inc, $1;
794             } elsif ( $d =~ /^D(.+)/ or
795             ( $d eq 'D' and
796             ( defined $ARGV[$i+1] or die "Argument required\n") and
797             $ARGV[++$i] =~ /(.*)/
798             )) {
799 0         0 $d = $1;
800 0 0       0 die "Invalid define $d\n" unless $d =~ m/^([^\=]+)(?:\=(.*))?$/;
801 0         0 my ( $def, $body) = ( $1, $2);
802 0         0 my $v = begin_macro( $def );
803 0 0       0 parse_line( $v, defined($2) ? $2 : '');
804 0         0 end_macro( $v);
805             } elsif ( $d =~ /^o(.+)/ or
806             ( $d eq 'o' and
807             ( defined $ARGV[$i+1] or die "Argument required\n") and
808             $ARGV[++$i] =~ /(.*)/
809             )) {
810 0 0       0 die "Output is already defined\n" if defined $output;
811 0         0 $output = $1;
812             } elsif ( $d eq '?' or $d eq 'h' or $d eq '-help') {
813 0         0 print <
814             sqlpp - simple SQL preprocessor v$VERSION
815            
816             sqlpp [options] filename
817            
818             options:
819            
820             -I path - include path
821             -D var[=value] - define variable
822             -o output - output to file ( default to stdout )
823             -h,--help - display this text
824             -hh - display man page
825            
826             USAGE
827 0         0 exit;
828             } elsif ( $d eq 'hh') {
829 0         0 system 'perldoc', $0;
830 0         0 exit;
831             } elsif ( $d eq '-') {
832 0         0 $dominus = 0;
833             } elsif ( $d eq '') {
834 0         0 $input = '-';
835             } else {
836 0         0 die "Unknown or invalid argument -$d\n";
837             }
838             } else {
839 1         5 $input = $d;
840             }
841             }
842            
843 1 50       6 die "No input file\n" unless defined $input;
844             }
845            
846             __DATA__