| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Acme::Sub::Parms; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 4 |  |  | 4 |  | 7530 | use strict; | 
|  | 4 |  |  |  |  | 23 |  | 
|  | 4 |  |  |  |  | 114 |  | 
| 4 | 4 |  |  | 4 |  | 18 | use warnings; | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 106 |  | 
| 5 | 4 |  |  | 4 |  | 2222 | use Filter::Util::Call; | 
|  | 4 |  |  |  |  | 4066 |  | 
|  | 4 |  |  |  |  | 404 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | BEGIN { | 
| 8 | 4 |  |  | 4 |  | 15 | $Acme::Sub::Parms::VERSION  = '1.03'; | 
| 9 | 4 |  |  |  |  | 7 | %Acme::Sub::Parms::args     = (); | 
| 10 | 4 |  |  |  |  | 15 | %Acme::Sub::Parms::raw_args = (); | 
| 11 | 4 |  |  |  |  | 12179 | $Acme::Sub::Parms::line_counter   = 0; | 
| 12 |  |  |  |  |  |  | } | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 445 |  |  | 445 |  | 644 | sub _NORMALIZE    ()   { return ':normalize';    }; | 
| 15 | 473 |  |  | 473 |  | 582 | sub _NO_VALIDATION  () { return ':no_validation';  }; | 
| 16 | 445 |  |  | 445 |  | 834 | sub _DUMP           () { return ':dump_to_stdout'; }; | 
| 17 |  |  |  |  |  |  | sub _DEBUG          () { 0; }; | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | sub _legal_option { | 
| 20 |  |  |  |  |  |  | return { | 
| 21 |  |  |  |  |  |  | _NORMALIZE()     => 1, | 
| 22 |  |  |  |  |  |  | _NO_VALIDATION() => 1, | 
| 23 |  |  |  |  |  |  | _DUMP()          => 1, | 
| 24 | 5 |  |  | 5 |  | 7 | }->{$_[0]}; | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | #### | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | sub import { | 
| 30 | 4 |  |  | 4 |  | 41 | my $class = shift; | 
| 31 | 4 |  |  |  |  | 13 | my $options = { | 
| 32 |  |  |  |  |  |  | _NORMALIZE()      => 0, | 
| 33 |  |  |  |  |  |  | _NO_VALIDATION()  => 0, | 
| 34 |  |  |  |  |  |  | _DUMP()           => 0, | 
| 35 |  |  |  |  |  |  | }; | 
| 36 | 4 |  |  |  |  | 15 | foreach my $item (@_) { | 
| 37 | 5 | 50 |  |  |  | 11 | if (not _legal_option($item)) { | 
| 38 | 0 |  |  |  |  | 0 | my $package = __PACKAGE__; | 
| 39 | 0 |  |  |  |  | 0 | require Carp; | 
| 40 | 0 |  |  |  |  | 0 | Carp::croak("'$item' not a valid option for 'use $package'\n"); | 
| 41 |  |  |  |  |  |  | } | 
| 42 | 5 |  |  |  |  | 13 | $options->{$item} = 1; | 
| 43 |  |  |  |  |  |  | } | 
| 44 | 4 |  |  |  |  | 10 | $Acme::Sub::Parms::line_counter = 0; | 
| 45 | 4 |  |  |  |  | 9 | my $ref   = {'options' => $options, 'bind_block' => 0 }; | 
| 46 | 4 |  |  |  |  | 13 | filter_add(bless $ref); # imported from Filter::Util::Call | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | #### | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | sub _parse_bind_spec { | 
| 52 | 28 |  |  | 28 |  | 40 | my ($self, $raw_spec) = @_; | 
| 53 |  |  |  |  |  |  |  | 
| 54 | 28 |  |  |  |  | 36 | my $spec = $raw_spec; | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 28 |  |  |  |  | 62 | my $spec_tokens = { | 
| 57 |  |  |  |  |  |  | 'is_defined' => 0, | 
| 58 |  |  |  |  |  |  | 'required'   => 1, | 
| 59 |  |  |  |  |  |  | 'optional'   => 0, | 
| 60 |  |  |  |  |  |  | }; | 
| 61 | 28 |  |  |  |  | 66 | while ($spec ne '') { | 
| 62 | 56 | 100 |  |  |  | 291 | if ($spec =~ s/^required(\s*,\s*|$)//) { # 'required' flag | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 63 | 8 |  |  |  |  | 13 | $spec_tokens->{'required'} = 1; | 
| 64 | 8 |  |  |  |  | 16 | $spec_tokens->{'optional'} = 0; | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | } elsif ($spec =~ s/^optional(\s*,\s*|$)//) { # 'optional' flag | 
| 67 | 16 |  |  |  |  | 36 | $spec_tokens->{'required'} = 0; | 
| 68 | 16 |  |  |  |  | 32 | $spec_tokens->{'optional'} = 1; | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | } elsif ($spec =~ s/^is_defined(\s*,\s*|$)//) { # 'is_defined' flag | 
| 71 | 8 |  |  |  |  | 18 | $spec_tokens->{'is_defined'} = 1; | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | } elsif ($spec =~ s/^(can|isa|type|callback|default)\s*=\s*//) { # 'something="somevalue"' | 
| 74 | 24 |  |  |  |  | 53 | my $spec_key = $1; | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | # Simple unquoted text with no embedded ws | 
| 77 | 24 | 100 |  |  |  | 128 | if ($spec =~ s/^([^\s"',]+)(\s*,\s*|$)//) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 78 | 20 |  |  |  |  | 67 | $spec_tokens->{$spec_key} = $1; | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | # Single quoted text with no embedded quotes | 
| 81 |  |  |  |  |  |  | } elsif ($spec =~ s/^'([^'\/]+)'\s*,\s*//) { | 
| 82 | 0 |  |  |  |  | 0 | $spec_tokens->{$spec_key} = "'$1'"; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | # Double quoted text with no embedded quotes or escapes | 
| 85 |  |  |  |  |  |  | } elsif ($spec =~ s/^"([^"\/]+)"\s*,\s*//) { | 
| 86 | 0 |  |  |  |  | 0 | $spec_tokens->{$spec_key} = '"' . $1 . '"'; | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | # It is a tricky case with quoted characters. One character at a time it is. | 
| 89 |  |  |  |  |  |  | } elsif ($spec =~ s/^(['"])//) { | 
| 90 | 4 |  |  |  |  | 10 | my $quote = $1; | 
| 91 | 4 |  |  |  |  | 8 | my $upend_spec  = reverse $spec; | 
| 92 | 4 |  |  |  |  | 8 | my $block_done  = 0; | 
| 93 | 4 |  |  |  |  | 4 | my $escape_next = 0; | 
| 94 | 4 |  |  |  |  | 8 | my $token       = $quote; | 
| 95 | 4 |  | 66 |  |  | 50 | until ($block_done || ($upend_spec eq '')) { | 
| 96 | 32 |  |  |  |  | 61 | my $ch = chop $upend_spec; | 
| 97 | 32 | 50 | 33 |  |  | 88 | if ($escape_next) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 98 | 0 |  |  |  |  | 0 | $token      .= $ch; | 
| 99 | 0 |  |  |  |  | 0 | $escape_next = 0; | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | } elsif (($ch eq "\\") && (not $escape_next)) { | 
| 102 | 0 |  |  |  |  | 0 | $token      .= $ch; | 
| 103 | 0 |  |  |  |  | 0 | $escape_next = 1; | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | } elsif ($ch eq $quote) { | 
| 106 | 4 |  |  |  |  | 12 | $block_done = 1; | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | } else { | 
| 109 | 28 |  |  |  |  | 74 | $token .= $ch; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 | 4 | 50 |  |  |  | 10 | if ($escape_next) { | 
| 113 | 0 |  |  |  |  | 0 | die("Syntax error in BindParms spec: $raw_spec\n"); | 
| 114 |  |  |  |  |  |  | } | 
| 115 | 4 |  |  |  |  | 8 | $spec = reverse $upend_spec; | 
| 116 | 4 |  |  |  |  | 25 | $spec_tokens->{$spec_key} = $token . $quote; | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | } else { | 
| 119 | 0 |  |  |  |  | 0 | die("Syntax error in BindParms spec: $raw_spec\n"); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | } else { | 
| 122 | 0 |  |  |  |  | 0 | die("Syntax error in BindParms spec: $raw_spec\n"); | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  | } | 
| 125 | 28 |  |  |  |  | 57 | return $spec_tokens; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | ############################################################################### | 
| 129 |  |  |  |  |  |  | # bind_spec is intentionally a a non-POD documented'public' method. It can be overridden in a sub-class | 
| 130 |  |  |  |  |  |  | # to provide alternative features. | 
| 131 |  |  |  |  |  |  | # | 
| 132 |  |  |  |  |  |  | # It takes two parameters: | 
| 133 |  |  |  |  |  |  | # | 
| 134 |  |  |  |  |  |  | #  $raw_spec             - this is the content of the [....] block (not including the '[' and ']' block delimitters) | 
| 135 |  |  |  |  |  |  | #  $field_name           - the hash key for the field being processed | 
| 136 |  |  |  |  |  |  | # | 
| 137 |  |  |  |  |  |  | # As each line of the BindParms block is processed the two parameters for each line are passed to the bind_spec | 
| 138 |  |  |  |  |  |  | # method for evaluation. bind_spec should return a string containing any Perl code generated as a result of | 
| 139 |  |  |  |  |  |  | # the bind specification. | 
| 140 |  |  |  |  |  |  | # | 
| 141 |  |  |  |  |  |  | # Good style dictates that the returned output should be *ONE* line (it could be a very *long* line) | 
| 142 |  |  |  |  |  |  | # so that line numbering in the source file is preserved for any error messages. | 
| 143 |  |  |  |  |  |  | # | 
| 144 |  |  |  |  |  |  | sub bind_spec { | 
| 145 | 28 |  |  | 28 | 0 | 34 | my $self = shift; | 
| 146 | 28 |  |  |  |  | 43 | my ($raw_spec, $field_name) = @_; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 28 |  |  |  |  | 36 | my $options        = $self->{'options'}; | 
| 149 | 28 |  |  |  |  | 42 | my $no_validation  = $options->{_NO_VALIDATION()}; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 28 |  |  |  |  | 187 | my $spec_tokens = $self->_parse_bind_spec($raw_spec); | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 28 |  |  |  |  | 34 | my $has_side_effects = 0; | 
| 154 | 28 |  |  |  |  | 32 | my $output = ''; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 28 |  |  |  |  | 93 | my @spec_tokens_list = keys %$spec_tokens; | 
| 157 | 28 | 0 | 33 |  |  | 89 | if ((0 == @spec_tokens_list) || ((1 == @spec_tokens_list) && ($spec_tokens->{'optional'}))) { | 
|  |  |  | 33 |  |  |  |  | 
| 158 | 0 |  |  |  |  | 0 | return; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | ###################### | 
| 162 |  |  |  |  |  |  | # default="some value" | 
| 163 | 28 | 100 |  |  |  | 56 | if (defined $spec_tokens->{'default'}) { | 
| 164 | 4 | 50 |  |  |  | 12 | if ($spec_tokens->{'optional'}) { | 
| 165 | 4 |  |  |  |  | 30 | $output .= "unless (exists (\$Acme::Sub::Parms::args\{'$field_name'\})) \{ \$Acme::Sub::Parms::args\{'$field_name'\} = " . $spec_tokens->{'default'} . ";\} "; | 
| 166 |  |  |  |  |  |  | } else { # required | 
| 167 | 0 |  |  |  |  | 0 | $output .= "unless (defined (\$Acme::Sub::Parms::args\{'$field_name'\})) \{ \$Acme::Sub::Parms::args\{'$field_name'\} = " . $spec_tokens->{'default'} . ";\} "; | 
| 168 |  |  |  |  |  |  | } | 
| 169 | 4 |  |  |  |  | 8 | $has_side_effects = 1; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | ###################### | 
| 173 |  |  |  |  |  |  | # callback="some_subroutine" | 
| 174 | 28 | 100 |  |  |  | 45 | if ($spec_tokens->{'callback'}) { | 
| 175 |  |  |  |  |  |  | $output .= "\{ my (\$callback_is_valid, \$callback_error) = " | 
| 176 | 8 |  |  |  |  | 59 | . $spec_tokens->{'callback'} | 
| 177 |  |  |  |  |  |  | . "(\'$field_name\', \$Acme::Sub::Parms::args\{\'$field_name\'\}, \\\%Acme::Sub::Parms::args);" | 
| 178 |  |  |  |  |  |  | . "unless (\$callback_is_valid) { require Carp; Carp::croak(\"$field_name error: \$callback_error\"); }} "; | 
| 179 | 8 |  |  |  |  | 14 | $has_side_effects = 1; | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | ###################### | 
| 183 |  |  |  |  |  |  | # required | 
| 184 | 28 | 100 | 100 |  |  | 89 | if ((! $no_validation) && $spec_tokens->{'required'}) { | 
| 185 | 4 |  |  |  |  | 20 | $output .= "unless (exists (\$Acme::Sub::Parms::args\{\'$field_name\'\})) { require Carp; Carp::croak(\"Missing required parameter \'$field_name\'\"); } "; | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | ###################### | 
| 189 |  |  |  |  |  |  | # is_defined | 
| 190 | 28 | 100 |  |  |  | 47 | if ($spec_tokens->{'is_defined'}) { | 
| 191 | 8 |  |  |  |  | 29 | $output .= "if (exists (\$Acme::Sub::Parms::args\{\'$field_name\'\}) and (! defined (\$Acme::Sub::Parms::args\{\'$field_name\'\}))) { require Carp; Carp::croak(\"parameter \'$field_name\' cannot be undef\"); } "; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 28 |  |  |  |  | 36 | my $type_requirements = $spec_tokens->{'type'}; | 
| 195 | 28 |  |  |  |  | 37 | my $isa_requirements  = $spec_tokens->{'isa'}; | 
| 196 | 28 |  |  |  |  | 30 | my $can_requirements  = $spec_tokens->{'can'}; | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 28 | 100 | 100 |  |  | 118 | if (defined ($type_requirements ) || defined($isa_requirements) || defined($can_requirements)) { | 
|  |  |  | 100 |  |  |  |  | 
| 199 | 12 |  |  |  |  | 37 | $output .=  "if (exists (\$Acme::Sub::Parms::args\{\'$field_name\'\})) \{"; | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | ##################### | 
| 202 |  |  |  |  |  |  | # type="SomeRefType" or type="SomeRefType, SomeOtherRefType, ..." | 
| 203 | 12 | 100 |  |  |  | 25 | if (defined $type_requirements) { | 
| 204 | 4 |  |  |  |  | 9 | $type_requirements =~ s/^['"]//; | 
| 205 | 4 |  |  |  |  | 8 | $type_requirements =~ s/['"]$//; | 
| 206 | 4 |  |  |  |  | 11 | my @type_classes = split(/[,\s]+/, $type_requirements); | 
| 207 | 4 |  |  |  |  | 15 | $output .= "unless ("; | 
| 208 | 4 |  |  |  |  | 8 | my @type_tests = (); | 
| 209 | 4 |  |  |  |  | 6 | foreach my $class_name (@type_classes) { | 
| 210 | 4 |  |  |  |  | 14 | push (@type_tests, "ref(\$Acme::Sub::Parms::args\{'$field_name'\}) eq '$class_name')"); | 
| 211 |  |  |  |  |  |  | } | 
| 212 | 4 |  |  |  |  | 19 | $output .= join(' || ',@type_tests) . " \{ require Carp; Carp::croak(\'parameter \\\'$field_name\\\' must be a " . join(' or ',@type_classes) . "\'); \}"; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | ##################### | 
| 216 |  |  |  |  |  |  | # isa="SomeRefType" or isa="SomeRefType, SomeOtherRefType, ..." | 
| 217 | 12 | 100 |  |  |  | 34 | if (defined $isa_requirements) { | 
| 218 | 4 |  |  |  |  | 17 | $isa_requirements =~ s/^['"]//; | 
| 219 | 4 |  |  |  |  | 9 | $isa_requirements =~ s/['"]$//; | 
| 220 | 4 |  |  |  |  | 15 | my @isa_classes = split(/[,\s]+/, $isa_requirements); | 
| 221 | 4 |  |  |  |  | 8 | $output .= "unless ("; | 
| 222 | 4 |  |  |  |  | 8 | my @isa_tests = (); | 
| 223 | 4 |  |  |  |  | 7 | foreach my $class_name (@isa_classes) { | 
| 224 | 4 |  |  |  |  | 23 | push (@isa_tests, "\$Acme::Sub::Parms::args\{'$field_name'\}->isa('$class_name')"); | 
| 225 |  |  |  |  |  |  | } | 
| 226 | 4 |  |  |  |  | 21 | $output .= join(' || ',@isa_tests) . ") \{ require Carp; Carp::croak(\'parameter \\\'$field_name\\\' must be a " . join(' or ',@isa_classes) . " instance or subclass\'); \}"; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | ##################### | 
| 230 |  |  |  |  |  |  | # can="somemethod" or can="somemethod, someothermethod, ..." | 
| 231 | 12 | 100 |  |  |  | 39 | if (defined $can_requirements) { | 
| 232 | 4 |  |  |  |  | 13 | $can_requirements =~ s/^['"]//; | 
| 233 | 4 |  |  |  |  | 18 | $can_requirements =~ s/['"]$//; | 
| 234 | 4 |  |  |  |  | 22 | my @can_methods = split(/[,\s]+/, $can_requirements); | 
| 235 | 4 |  |  |  |  | 35 | $output .= "unless ("; | 
| 236 | 4 |  |  |  |  | 17 | my @can_tests = (); | 
| 237 | 4 |  |  |  |  | 8 | foreach my $method_name (@can_methods) { | 
| 238 | 4 |  |  |  |  | 15 | push (@can_tests, "\$Acme::Sub::Parms::args\{'$field_name'\}->can('$method_name')"); | 
| 239 |  |  |  |  |  |  | } | 
| 240 | 4 |  |  |  |  | 28 | $output .= join(' && ',@can_tests) . ") \{ require Carp; Carp::croak(\'parameter \\\'$field_name\\\' must be an object with a " . join(' and a ',@can_methods) . " method\'); \}"; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 | 12 |  |  |  |  | 19 | $output .= "\}"; | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 28 |  |  |  |  | 132 | return ($has_side_effects,$output); | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | #### | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | sub filter { | 
| 252 | 436 |  |  | 436 | 0 | 803 | my $self = shift; | 
| 253 |  |  |  |  |  |  |  | 
| 254 | 436 |  |  |  |  | 518 | my $options        = $self->{'options'}; | 
| 255 | 436 |  |  |  |  | 597 | my $dump_to_stdout = $options->{_DUMP()}; | 
| 256 | 436 |  |  |  |  | 570 | my $normalize      = $options->{_NORMALIZE()}; | 
| 257 | 436 |  |  |  |  | 522 | my $no_validation  = $options->{_NO_VALIDATION()}; | 
| 258 | 436 |  |  |  |  | 464 | my $bind_block     = $self->{'bind_block'}; | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 436 |  |  |  |  | 422 | my $status; | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 436 | 100 |  |  |  | 1430 | if ($status = filter_read() > 0) { # imported from Filter::Util::Call | 
| 263 | 432 |  |  |  |  | 485 | $Acme::Sub::Parms::line_counter++; | 
| 264 |  |  |  |  |  |  |  | 
| 265 | 432 |  |  |  |  | 390 | if (_DEBUG) { | 
| 266 |  |  |  |  |  |  | print STDERR "input line $Acme::Sub::Parms::line_counter: $_"; | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | ############################################# | 
| 270 |  |  |  |  |  |  | # If we are in a bind block, handle it | 
| 271 | 432 | 100 |  |  |  | 539 | if ($bind_block) { | 
| 272 | 36 |  |  |  |  | 46 | my $bind_entries = $self->{'bind_entries'}; | 
| 273 | 36 |  |  |  |  | 37 | my $simple_bind  = $self->{'simple_bind'}; | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | ############################## | 
| 276 |  |  |  |  |  |  | # Last line of the bind block? Generate the working code. | 
| 277 | 36 | 100 |  |  |  | 373 | if (m/^\s*\)(\s*$|\s*#.*$)/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 4 |  |  |  |  | 10 | my $block_trailing_comment = $2; | 
| 280 | 4 | 50 |  |  |  | 13 | $block_trailing_comment = defined($block_trailing_comment) ? $block_trailing_comment : ''; | 
| 281 | 4 |  |  |  |  | 8 | $block_trailing_comment =~ s/[\r\n]+$//s; | 
| 282 | 4 |  |  |  |  | 22 | my $side_effects = 0; | 
| 283 | 4 |  |  |  |  | 7 | my $args = 'local %Acme::Sub::Parms::args; '; # needed? | 
| 284 | 4 | 100 |  |  |  | 10 | if ($normalize) { | 
| 285 | 2 |  |  |  |  | 6 | $args .= '{ local $_; local %Acme::Sub::Parms::raw_args = @_; %Acme::Sub::Parms::args = map { lc($_) => $Acme::Sub::Parms::raw_args{$_} } keys %Acme::Sub::Parms::raw_args; }' . "\n"; | 
| 286 |  |  |  |  |  |  | } else { | 
| 287 | 2 |  |  |  |  | 7 | $args .= '%Acme::Sub::Parms::args = @_;' . "\n"; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  | # If we have validation or defaults, handle them | 
| 290 | 4 |  |  |  |  | 4 | my $padding_lines = 0; | 
| 291 | 4 | 50 |  |  |  | 12 | if (! $simple_bind) { | 
| 292 | 4 |  |  |  |  | 9 | my @parm_declarations = (); | 
| 293 | 4 |  |  |  |  | 9 | foreach my $entry (@$bind_entries) { | 
| 294 | 32 |  |  |  |  | 49 | my $variable_decl    = $entry->{'variable'}; | 
| 295 | 32 |  |  |  |  | 41 | my $field_name       = $entry->{'field'}; | 
| 296 | 32 |  |  |  |  | 34 | my $spec             = $entry->{'spec'}; | 
| 297 | 32 |  |  |  |  | 39 | my $trailing_comment = $entry->{'trailing_comment'}; | 
| 298 | 32 | 100 | 66 |  |  | 129 | if ( (! defined($spec)) || ($spec eq '')) { | 
| 299 |  |  |  |  |  |  | # push(@parm_declarations, $trailing_comment); | 
| 300 | 4 |  |  |  |  | 11 | next; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  | # The hard case. We have validation requirements. | 
| 303 | 28 |  |  |  |  | 61 | my ($has_side_effects, $bind_spec_output) = $self->bind_spec($spec, $field_name); | 
| 304 | 28 |  |  |  |  | 40 | $side_effects += $has_side_effects; | 
| 305 | 28 |  |  |  |  | 90 | push (@parm_declarations, "$bind_spec_output$trailing_comment"); | 
| 306 |  |  |  |  |  |  | } | 
| 307 | 4 |  |  |  |  | 58 | $args .=  join("\n",@parm_declarations,''); | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | # Generate the actual parameter data binding | 
| 311 | 4 |  |  |  |  | 12 | my @var_declarations      = (); | 
| 312 | 4 |  |  |  |  | 6 | my @hard_var_declarations = (); | 
| 313 | 4 |  |  |  |  | 8 | my @field_declarations    = (); | 
| 314 | 4 |  |  |  |  | 5 | my @fields_list           = (); | 
| 315 | 4 |  |  |  |  | 8 | foreach my $entry (@$bind_entries) { | 
| 316 | 32 |  |  |  |  | 43 | my $spec       = $entry->{'spec'}; | 
| 317 | 32 | 100 | 66 |  |  | 122 | next if ((not defined $spec) || ($spec eq '')); | 
| 318 | 28 |  |  |  |  | 35 | my $raw_var    = $entry->{'variable'}; | 
| 319 | 28 |  |  |  |  | 34 | my $field_name = $entry->{'field'}; | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 28 |  |  |  |  | 57 | push (@fields_list, "'$field_name'"); | 
| 322 | 28 |  |  |  |  | 110 | my ($variable_name) = $raw_var =~ m/^my\s+(\S+)$/; | 
| 323 | 28 | 50 |  |  |  | 53 | if (defined $variable_name) { # simple 'my $variable :' entries are special-cased for performance | 
| 324 | 28 |  |  |  |  | 33 | push (@var_declarations,   $variable_name); | 
| 325 | 28 |  |  |  |  | 61 | push (@field_declarations, "'$field_name'"); | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | } else { # Otherwise make a seperate entry for this binding | 
| 328 | 0 |  |  |  |  | 0 | push (@hard_var_declarations, "$raw_var = \$Acme::Sub::Parms::args\{$field_name\};"); | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  | } | 
| 331 | 4 |  |  |  |  | 17 | my $hard_args = join(' ',@hard_var_declarations); | 
| 332 | 4 |  |  |  |  | 6 | my $arg_line  = ''; | 
| 333 | 4 | 50 |  |  |  | 44 | if (0 < @var_declarations) { | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 4 | 50 | 33 |  |  | 60 | if ($simple_bind && (! $normalize) && $no_validation && (0 == $side_effects) && (0 == @hard_var_declarations)) { | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 336 | 0 |  |  |  |  | 0 | $args = "\n    my (" . join(",", @var_declarations) . ') = @{{@_}}{' . join(',',@field_declarations) . '}; '; | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | } else { | 
| 339 |  |  |  |  |  |  |  | 
| 340 | 4 |  |  |  |  | 52 | $arg_line  = 'my (' . join(",", @var_declarations) . ') = @Acme::Sub::Parms::args{' . join(',',@field_declarations) . '}; '; | 
| 341 |  |  |  |  |  |  | } | 
| 342 |  |  |  |  |  |  | } | 
| 343 | 4 |  |  |  |  | 11 | my $unknown_parms_check = ''; | 
| 344 | 4 | 100 |  |  |  | 13 | unless ($no_validation) { | 
| 345 | 2 |  |  |  |  | 13 | $unknown_parms_check = 'delete @Acme::Sub::Parms::args{' . join(',',@fields_list) . '}; if (0 <  @Acme::Sub::Parms::args) { require Carp; Carp::croak(\'Unexpected parameters passed: \' . join(\', \',@Acme::Sub::Parms::args)); } '; | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | } | 
| 348 | 4 |  |  |  |  | 10 | $self->{'bind_block'} = 0; | 
| 349 | 4 |  |  |  |  | 8 | my $original_block_length = $Acme::Sub::Parms::line_counter - $self->{'line_block_start'}; | 
| 350 | 4 |  |  |  |  | 43 | my $new_block = $args . join(' ',$arg_line, $hard_args, $unknown_parms_check) . "$block_trailing_comment\n"; | 
| 351 | 4 |  |  |  |  | 65 | $new_block =~ s/\n+/\n/gs; | 
| 352 | 4 |  |  |  |  | 19 | my $new_block_lines = $new_block =~ m/\n/gs; | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 4 |  |  |  |  | 8 | my $additional_lines = $original_block_length - $new_block_lines; | 
| 355 |  |  |  |  |  |  | #warn("Need $additional_lines extra lines\n---\n$new_block---\n"); | 
| 356 | 4 | 50 |  |  |  | 23 | if ($additional_lines > 0) { | 
| 357 | 4 |  |  |  |  | 65 | $_ = $new_block . ("\n" x $additional_lines); | 
| 358 |  |  |  |  |  |  | } else { | 
| 359 | 0 |  |  |  |  | 0 | $_ = $new_block; | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | ######################## | 
| 363 |  |  |  |  |  |  | # Bind block parameter line | 
| 364 |  |  |  |  |  |  | } elsif (my($bind_var, $bind_field,$trailing_comment) = m/^\s*(\S.*?)\s+:\s+([^'"\s\[]+.*?)\s*(;\s*|;\s*#.*)$/) { | 
| 365 | 28 | 50 |  |  |  | 72 | $trailing_comment = defined($trailing_comment) ? $trailing_comment : ''; | 
| 366 | 28 |  |  |  |  | 90 | $trailing_comment =~ s/[\r\n]+$//s; | 
| 367 | 28 |  |  |  |  | 64 | $trailing_comment =~ s/^;//; | 
| 368 | 28 |  |  |  |  | 110 | my $bind_entry = { 'variable' => $bind_var, 'field' => $bind_field, trailing_comment => $trailing_comment }; | 
| 369 | 28 |  |  |  |  | 50 | push (@$bind_entries, $bind_entry); | 
| 370 | 28 | 50 |  |  |  | 88 | if ($bind_var !~ m/^my \$\S+$/) { | 
| 371 | 0 |  |  |  |  | 0 | $self->{'simple_bind'} = 0; | 
| 372 |  |  |  |  |  |  | } | 
| 373 | 28 | 100 |  |  |  | 97 | if ($bind_field =~ m/^(\S+)\s*\[(.*)\]$/) { # Complex spec | 
|  |  | 50 |  |  |  |  |  | 
| 374 | 26 |  |  |  |  | 62 | $bind_entry->{'field'} = $1; | 
| 375 | 26 |  |  |  |  | 68 | $bind_entry->{'spec'}  = $2; | 
| 376 | 26 | 100 | 100 |  |  | 119 | unless ($no_validation && ($bind_field !~ m/[\s\[,](default|callback)\s*=\s*/)) { | 
| 377 | 18 |  |  |  |  | 26 | $self->{'simple_bind'} = 0; | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  | } elsif ($bind_field =~ m/^\w+$/) { # my $thing : something; | 
| 380 | 2 |  |  |  |  | 4 | $bind_entry->{'spec'}  = 'required'; | 
| 381 | 2 | 50 |  |  |  | 7 | unless ($no_validation) { | 
| 382 | 0 |  |  |  |  | 0 | $self->{'simple_bind'} = 0; | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  | } else { | 
| 385 | 0 |  |  |  |  | 0 | die("Failed to parse BindParms block line $Acme::Sub::Parms::line_counter: $_"); | 
| 386 |  |  |  |  |  |  | } | 
| 387 | 28 |  |  |  |  | 53 | undef $trailing_comment; | 
| 388 | 28 |  |  |  |  | 33 | undef $bind_var; | 
| 389 | 28 |  |  |  |  | 64 | undef $bind_field; | 
| 390 | 28 |  |  |  |  | 60 | $_ = ''; | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | ############################ | 
| 393 |  |  |  |  |  |  | # Blank and comment only lines | 
| 394 |  |  |  |  |  |  | } elsif (m/^(\s*|\s*#.*)$/) { | 
| 395 | 4 |  |  |  |  | 22 | my $trailing_comment = $1; | 
| 396 | 4 | 50 |  |  |  | 14 | $trailing_comment = defined ($trailing_comment) ? $trailing_comment : ''; | 
| 397 | 4 |  |  |  |  | 15 | $trailing_comment =~ s/[\r\n]+$//s; | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 4 |  |  |  |  | 12 | my $bind_entry = { spec => '', trailing_comment => $trailing_comment}; | 
| 400 | 4 |  |  |  |  | 8 | push (@$bind_entries, $bind_entry); | 
| 401 | 4 |  |  |  |  | 8 | $_ = ''; | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | } else { | 
| 404 | 0 |  |  |  |  | 0 | die("Failed to parse BindParms block line $Acme::Sub::Parms::line_counter: $_"); | 
| 405 |  |  |  |  |  |  | } | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | } else { # Start of a bind block | 
| 408 | 396 | 100 |  |  |  | 741 | if (m/^\s*BindParms\s+:\s+\((\s*#.*$|\s*$)/) { | 
| 409 | 4 |  |  |  |  | 12 | $self->{'simple_bind'}  = 1; | 
| 410 | 4 |  |  |  |  | 10 | $self->{'bind_entries'} = []; | 
| 411 | 4 |  |  |  |  | 8 | $self->{'bind_block'}   = 1; | 
| 412 | 4 |  |  |  |  | 15 | $self->{'line_block_start'} = $Acme::Sub::Parms::line_counter; | 
| 413 | 4 |  |  |  |  | 23 | my $block_head_comment = $2; | 
| 414 | 4 | 50 |  |  |  | 26 | $block_head_comment = defined ($block_head_comment) ? $block_head_comment : ''; | 
| 415 | 4 |  |  |  |  | 8 | $block_head_comment =~ s/[\r\n]+$//s; | 
| 416 | 4 |  |  |  |  | 9 | $_ = $block_head_comment; | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | ####### | 
| 419 |  |  |  |  |  |  | #            ################################ | 
| 420 |  |  |  |  |  |  | #            # Invokation : $self; | 
| 421 |  |  |  |  |  |  | #            } elsif (my ($ihead,$ivar,$itail) = m/^(\s*)Invokation\s+:\s+(\S+.*?)\s*;(.*)$/) { | 
| 422 |  |  |  |  |  |  | #                $_ = $ihead . " my $ivar = shift @_;$itail\n"; | 
| 423 |  |  |  |  |  |  | # | 
| 424 |  |  |  |  |  |  | #            ################################ | 
| 425 |  |  |  |  |  |  | #            # ParmsHash : %args; | 
| 426 |  |  |  |  |  |  | #            } elsif (my ($fhead,$func_hash_ident,$ftail) = m/^(\s*)ParmsHash\s+:\s+(\S+.*?)\s*;(.*)$/) { | 
| 427 |  |  |  |  |  |  | #                if ($normalize) { | 
| 428 |  |  |  |  |  |  | #                    $_ = "${fhead}my $func_hash_ident; { local \%Acme::Sub::Parms::raw_args = \@\_; $func_hash_ident = map \{ lc(\$\_\) \=\> \$Acme::Sub::Parms::raw_args\{\$\_\} \} keys \%Acme::Sub::Parms::raw_args; } $ftail\n"; | 
| 429 |  |  |  |  |  |  | #                } else { | 
| 430 |  |  |  |  |  |  | #                    $_ = "${fhead}my $func_hash_ident = \@\_;$ftail\n"; | 
| 431 |  |  |  |  |  |  | #                } | 
| 432 |  |  |  |  |  |  | # | 
| 433 |  |  |  |  |  |  | #            ################################ | 
| 434 |  |  |  |  |  |  | #            # MethodParms : $self, %args; | 
| 435 |  |  |  |  |  |  | #            } elsif (my ($mhead,$method_invokation,$method_hash_ident,$mtail) = m/^(\s*)MethodParms\s+:\s+(\S+.*?)\s*,\s*(\S+.*?)\s*;(.*)$/) { | 
| 436 |  |  |  |  |  |  | #                if ($normalize) { | 
| 437 |  |  |  |  |  |  | #                    $_ = "${mhead}my $method_invokation = shift; my $method_hash_ident; { local \$_; local \%Acme::Sub::Parms::raw_args = \@\_; $method_hash_ident = map \{ lc(\$\_\) \=\> \$Acme::Sub::Parms::raw_args\{\$\_\} \} keys \%Acme::Sub::Parms::raw_args; } $mtail\n"; | 
| 438 |  |  |  |  |  |  | #                } else { | 
| 439 |  |  |  |  |  |  | #                    $_ = "${mhead}my $method_invokation = shift; my $method_hash_ident = \@\_; $mtail\n"; | 
| 440 |  |  |  |  |  |  | #                } | 
| 441 |  |  |  |  |  |  | ####### | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  | } | 
| 444 |  |  |  |  |  |  | } | 
| 445 | 436 |  |  |  |  | 413 | if (_DEBUG) { | 
| 446 |  |  |  |  |  |  | print STDERR "output as: $_"; | 
| 447 |  |  |  |  |  |  | } | 
| 448 | 436 | 100 |  |  |  | 562 | if ($dump_to_stdout) { print $_; } | 
|  | 110 |  |  |  |  | 154 |  | 
| 449 |  |  |  |  |  |  |  | 
| 450 | 436 |  |  |  |  | 11578 | return $status; | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | #### | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | 1; | 
| 456 |  |  |  |  |  |  |  |