| 38 | | say "package HTTP::Engine::CGI;"; |
| 39 | | |
| 40 | | # Mouse::Tiny |
| 41 | | sub { |
| 42 | | my $src = join '', read_file($PATH_TO_MOUSE_TINY); |
| 43 | | say $src; |
| 44 | | }->(); |
| 45 | | |
| 46 | | # header |
| 47 | | for (@files) { |
| 48 | | say "\$INC{'$_'} = __FILE__;"; |
| 49 | | } |
| 50 | | |
| 51 | | # http::engine |
| 52 | | for my $file (@files) { |
| 53 | | my $src = join '', read_file("lib/$file"); |
| 54 | | my $doc = PPI::Document->new(\$src); |
| 55 | | $doc->prune('PPI::Token::Pod'); |
| 56 | | $doc->prune('PPI::Token::Comment'); |
| 57 | | $doc->find( |
| 58 | | sub { |
| 59 | | if ($_[1]->isa('PPI::Statement::Include')) { |
| 60 | | if ($_[1]->module =~ /^HTTP::Engine/) { |
| 61 | | eval { |
| 62 | | my $content = $_[1]->content; |
| 63 | | if ($content =~ /^use\s*(HTTP::Engine\S+)\s*(.*?);$/ms) { |
| 64 | | my ($pkg, $args) = ($1, $2); |
| 65 | | if ($pkg->can('import') && $pkg !~ /HTTP::Engine::(Util|Response|Request)/) { |
| 66 | | my $token = PPI::Token::Word->new("BEGIN { ${pkg}::import('${pkg}', $args); }\n"); |
| 67 | | $_[0]->__replace_child($_[1], $token); |
| | 47 | &main; exit; |
| | 48 | |
| | 49 | sub process_accessor { |
| | 50 | my ($name, $klass, $attr) = @_; |
| | 51 | |
| | 52 | my $self = '$_[0]'; |
| | 53 | my $key = $name; |
| | 54 | $name =~ s/^['"]//; |
| | 55 | $name =~ s/['"]$//; |
| | 56 | |
| | 57 | my $accessor = "{# attribute for $name\n"; |
| | 58 | if ($attr->{trigger}) { |
| | 59 | $accessor .= "my \$trigger = $attr->{trigger};\n"; |
| | 60 | } |
| | 61 | my $isa = $attr->{isa}; |
| | 62 | if ($isa) { |
| | 63 | $isa =~ s/^['"]//; |
| | 64 | $isa =~ s/['"]$//; |
| | 65 | } |
| | 66 | if ($isa) { |
| | 67 | if (Mouse::TypeRegistry->optimized_constraints()->{$isa}) { |
| | 68 | $accessor .= "my \$constraint = Mouse::TypeRegistry->optimized_constraints()->{'$isa'};\n"; |
| | 69 | } else { |
| | 70 | $accessor .= "my \$constraint = sub { Mouse::Util::blessed(\$_) && Mouse::Util::blessed(\$_) eq '$isa' };\n"; |
| | 71 | } |
| | 72 | } |
| | 73 | if (my $default = $attr->{default}) { |
| | 74 | $accessor .= "my \$default = $attr->{default};\n"; |
| | 75 | } |
| | 76 | $accessor .= "sub $name {\n"; |
| | 77 | if ($attr->{is} =~ /rw/) { |
| | 78 | $accessor .= 'if (scalar(@_) >= 2) {' . "\n"; |
| | 79 | |
| | 80 | my $value = '$_[1]'; |
| | 81 | |
| | 82 | if ($isa) { |
| | 83 | if ($attr->{coerce}) { |
| | 84 | $accessor .= $value." = Mouse::TypeRegistry->typecast_constraints('$klass', '$isa', $value);"; |
| | 85 | } |
| | 86 | $accessor .= 'local $_ = '.$value.';'; |
| | 87 | my $constraint = sub { }; |
| | 88 | $accessor .= " |
| | 89 | unless (\$constraint->()) { |
| | 90 | my \$display = defined(\$_) ? overload::StrVal(\$_) : \"undef\"; |
| | 91 | Carp::confess(\"Attribute ($name) does not pass the type constraint because: Validation failed for \\'$isa\\' failed with value \$display\"); |
| | 92 | }" . "\n" |
| | 93 | } |
| | 94 | |
| | 95 | # if there's nothing left to do for the attribute we can return during |
| | 96 | # this setter |
| | 97 | $accessor .= 'return ' if !$attr->{weak_ref} && !$attr->{trigger} && !$attr->{auto_deref}; |
| | 98 | |
| | 99 | $accessor .= $self.'->{'.$key.'} = '.$value.';' . "\n"; |
| | 100 | |
| | 101 | if ($attr->{weak_ref}) { |
| | 102 | $accessor .= 'Mouse::Util::weaken('.$self.'->{'.$key.'}) if ref('.$self.'->{'.$key.'});' . "\n"; |
| | 103 | } |
| | 104 | |
| | 105 | die "This module doesn't support trigger" if $attr->{trigger}; |
| | 106 | |
| | 107 | $accessor .= "}\n"; |
| | 108 | } |
| | 109 | else { |
| | 110 | $accessor .= 'Carp::confess "Cannot assign a value to a read-only accessor" if scalar(@_) >= 2;' . "\n"; |
| | 111 | } |
| | 112 | |
| | 113 | if ($attr->{lazy}) { |
| | 114 | $accessor .= $self.'->{'.$key.'} = '; |
| | 115 | |
| | 116 | $accessor .= $attr->{builder} |
| | 117 | ? $self.'->$builder' |
| | 118 | : ref($attr->{default}) eq 'CODE' |
| | 119 | ? '$default->('.$self.')' |
| | 120 | : '$default'; |
| | 121 | $accessor .= ' if !exists '.$self.'->{'.$key.'};' . "\n"; |
| | 122 | } |
| | 123 | |
| | 124 | if ($attr->{auto_deref}) { |
| | 125 | die "THIS MODULE DOESN'T SUPPORT DEREF"; |
| | 126 | } |
| | 127 | |
| | 128 | $accessor .= 'return '.$self.'->{'.$key.'}; |
| | 129 | } |
| | 130 | }'; |
| | 131 | $accessor; |
| | 132 | } |
| | 133 | |
| | 134 | sub generate_constructor_method_inline { |
| | 135 | my ($klass, $attrs) = @_; |
| | 136 | my @attrs = @$attrs; |
| | 137 | |
| | 138 | my $buildargs = _generate_BUILDARGS(); |
| | 139 | my $processattrs = _generate_processattrs($klass, \@attrs); |
| | 140 | |
| | 141 | <<"..."; |
| | 142 | sub new { |
| | 143 | my \$class = shift; |
| | 144 | my \$args = $buildargs; |
| | 145 | my \$instance = bless {}, \$class; |
| | 146 | $processattrs; |
| | 147 | return \$instance; |
| | 148 | } |
| | 149 | ... |
| | 150 | } |
| | 151 | |
| | 152 | sub _generate_processattrs { |
| | 153 | my ($class, $attrs) = @_; |
| | 154 | my @res; |
| | 155 | for my $attr (@$attrs) { |
| | 156 | my $set_value = do { |
| | 157 | my @code; |
| | 158 | |
| | 159 | if ($attr->{coerce}) { |
| | 160 | push @code, "my \$value = Mouse::TypeRegistry->typecast_constraints('$class', $attr->{isa}, \$args->{'$attr->{name}'});"; |
| | 161 | } |
| | 162 | else { |
| | 163 | push @code, "my \$value = \$args->{'$attr->{name}'};"; |
| | 164 | } |
| | 165 | |
| | 166 | # this one is very slow. skip this in cgi mode. |
| | 167 | # if ($attr->{isa}) { |
| | 168 | # push @code, "\$attrs[$index]->verify_type_constraint( \$value );"; |
| | 169 | # } |
| | 170 | |
| | 171 | push @code, "\$instance->{'$attr->{name}'} = \$value;"; |
| | 172 | |
| | 173 | if ($attr->{weak_ref}) { |
| | 174 | push @code, "Mouse::Util::weaken( \$instance->{'$attr->{name}'} ) if ref( \$value );"; |
| | 175 | } |
| | 176 | |
| | 177 | if ( $attr->{trigger} ) { |
| | 178 | die "this module doesn't support trigger"; |
| | 179 | } |
| | 180 | |
| | 181 | join "\n", @code; |
| | 182 | }; |
| | 183 | |
| | 184 | my $make_default_value = do { |
| | 185 | my @code; |
| | 186 | |
| | 187 | if ( $attr->{default} || $attr->{builder} ) { |
| | 188 | unless ( $attr->{lazy} ) { |
| | 189 | push @code, "my \$value = "; |
| | 190 | |
| | 191 | if ($attr->{coerce}) { |
| | 192 | push @code, "Mouse::TypeRegistry->typecast_constraints('$class', $attr->{isa}, "; |
| | 193 | } |
| | 194 | |
| | 195 | if ($attr->{builder}) { |
| | 196 | push @code, "\$instance->$attr->{builder}"; |
| | 197 | } |
| | 198 | elsif (ref($attr->{default}) =~ /^sub /) { |
| | 199 | push @code, "@{[ $attr->{default} ]}->()"; |
| | 200 | } |
| | 201 | else { |
| | 202 | push @code, "$attr->{default}"; |
| | 203 | } |
| | 204 | |
| | 205 | if ($attr->{coerce}) { |
| | 206 | push @code, ");"; |
| | 207 | } |
| | 208 | else { |
| | 209 | push @code, ";"; |
| | 210 | } |
| | 211 | |
| | 212 | if ($attr->{isa}) { |
| | 213 | # "this module doesn't use type constraints"; |
| | 214 | } |
| | 215 | |
| | 216 | push @code, "\$instance->{'$attr->{name}'} = \$value;"; |
| | 217 | |
| | 218 | if ($attr->{weak_ref}) { |
| | 219 | push @code, "weaken( \$instance->{'$attr->{name}'} ) if ref( \$value );"; |
| | 220 | } |
| | 221 | } |
| | 222 | join "\n", @code; |
| | 223 | } |
| | 224 | else { |
| | 225 | if ( $attr->{required} ) { |
| | 226 | qq{Carp::confess("Attribute ($attr->{name}) is required");}; |
| | 227 | } else { |
| | 228 | "" |
| | 229 | } |
| | 230 | } |
| | 231 | }; |
| | 232 | my $code = <<"..."; |
| | 233 | { |
| | 234 | if (exists(\$args->{'$attr->{name}'})) { |
| | 235 | $set_value; |
| | 236 | } else { |
| | 237 | $make_default_value; |
| | 238 | } |
| | 239 | } |
| | 240 | ... |
| | 241 | push @res, $code; |
| | 242 | } |
| | 243 | return join "\n", @res; |
| | 244 | } |
| | 245 | |
| | 246 | sub _generate_BUILDARGS { |
| | 247 | <<'...'; |
| | 248 | do { |
| | 249 | if ( scalar @_ == 1 ) { |
| | 250 | if ( defined $_[0] ) { |
| | 251 | ( ref( $_[0] ) eq 'HASH' ) |
| | 252 | || Carp::confess "Single parameters to new() must be a HASH ref"; |
| | 253 | +{ %{ $_[0] } }; |
| | 254 | } |
| | 255 | else { |
| | 256 | +{}; |
| | 257 | } |
| | 258 | } |
| | 259 | else { |
| | 260 | +{@_}; |
| | 261 | } |
| | 262 | }; |
| | 263 | ... |
| | 264 | } |
| | 265 | |
| | 266 | sub replace_node { |
| | 267 | my ($parent, $child, $src) = @_; |
| | 268 | my $token = PPI::Token::Word->new($src); |
| | 269 | $parent->__replace_child($child, $token); |
| | 270 | } |
| | 271 | |
| | 272 | sub main { |
| | 273 | say "package HTTP::Engine::CGI;"; |
| | 274 | |
| | 275 | # Mouse::Tiny |
| | 276 | sub { |
| | 277 | my $src = join '', read_file($PATH_TO_MOUSE_TINY); |
| | 278 | say $src; |
| | 279 | }->(); |
| | 280 | |
| | 281 | # header |
| | 282 | for (@files) { |
| | 283 | say "\$INC{'$_'} = __FILE__;"; |
| | 284 | } |
| | 285 | |
| | 286 | # http::engine |
| | 287 | for my $file (@files) { |
| | 288 | my $src = join '', read_file("lib/$file"); |
| | 289 | my $doc = PPI::Document->new(\$src); |
| | 290 | $doc->prune('PPI::Token::Pod'); |
| | 291 | $doc->prune('PPI::Token::Comment'); |
| | 292 | # call ->import(); |
| | 293 | $doc->find( |
| | 294 | sub { |
| | 295 | if ($_[1]->isa('PPI::Statement::Include')) { |
| | 296 | if ($_[1]->module =~ /^HTTP::Engine/) { |
| | 297 | eval { |
| | 298 | my $content = $_[1]->content; |
| | 299 | if ($content =~ /^use\s*(HTTP::Engine\S+)\s*(.*?);$/ms) { |
| | 300 | my ($pkg, $args) = ($1, $2); |
| | 301 | if ($pkg->can('import') && $pkg !~ /HTTP::Engine::(Util|Response|Request)/) { |
| | 302 | replace_node($_[0], $_[1], "BEGIN { ${pkg}::import('${pkg}', $args); }\n"); |
| | 303 | } else { |
| | 304 | $_[1]->delete; |
| | 305 | } |
| | 309 | }; |
| | 310 | warn $@ if $@; |
| | 311 | } |
| | 312 | } |
| | 313 | return; |
| | 314 | } |
| | 315 | ); |
| | 316 | (my $klass = $file) =~ s!/!::!g; |
| | 317 | $klass =~ s!\.pm$!!; |
| | 318 | my @attrs; |
| | 319 | $doc->find( |
| | 320 | sub { |
| | 321 | eval { |
| | 322 | if ($_[1]->isa('PPI::Statement') && $_[1] =~ /^has/ && $_[1] !~ /\$attr/) { |
| | 323 | warn "WHY?" unless $_[1]->schild(0) eq 'has'; |
| | 324 | my $name = $_[1]->schild(1)->content; |
| | 325 | my ($args, ) = @{ $_[1]->find('PPI::Statement::Expression') || [] } or die "missing expression"; |
| | 326 | my @args = $args->children; |
| | 327 | my $expect_key = 1; |
| | 328 | my @args_result; |
| | 329 | while (my $elem = shift @args) { |
| | 330 | next if $elem->isa('PPI::Token::Whitespace'); |
| | 331 | next if $elem->isa('PPI::Token::Operator'); |
| | 332 | |
| | 333 | if ($expect_key) { |
| | 334 | push @args_result, "$elem"; |
| | 335 | $expect_key = 0; |
| | 336 | } else { |
| | 337 | if ($elem->isa('PPI::Token::Word') && $elem eq 'sub') { |
| | 338 | my $content; |
| | 339 | while (my $block = shift @args) { |
| | 340 | next if $block->isa('PPI::Token::Whitespace'); |
| | 341 | unless ($block->isa('PPI::Structure::Block')) { |
| | 342 | warn "invalid token: @{[ ref $block ]} $elem, $block ,$_[1]"; |
| | 343 | warn join ' ---- ', @args; |
| | 344 | exit; |
| | 345 | } |
| | 346 | $content = "sub $block"; |
| | 347 | last; |
| | 348 | } |
| | 349 | push @args_result, $content; |
| | 350 | } else { |
| | 351 | push @args_result, "$elem"; |
| | 352 | } |
| | 353 | $expect_key = 1; |
| | 354 | } |
| | 355 | } |
| | 356 | |
| | 357 | $name =~ s/^['"]//; |
| | 358 | $name =~ s/['"]$//; |
| | 359 | my $attr = {@args_result, name => $name}; |
| | 360 | my $src = process_accessor($name, $klass, $attr) . "\n"; |
| | 361 | if (my $handles = $attr->{handles}) { |
| | 362 | my $handles = eval $handles; |
| | 363 | die $@ if $@; |
| | 364 | for my $handle (@$handles) { |
| | 365 | $handle =~ s/^['"]//; |
| | 366 | $handle =~ s/['"]$//; |
| | 367 | $src .= "sub $handle { shift->$name->$handle(\@_) }\n"; |
| | 368 | } |
| | 369 | } |
| | 370 | eval $src; |
| | 371 | if ($@) { |
| | 372 | warn "------------- START"; |
| | 373 | warn $@; |
| | 374 | warn $src; |
| | 375 | warn "------------- END"; |