| 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__ |