Acme-ComeFrom

 view release on metacpan or  search on metacpan

lib/Acme/ComeFrom.pm  view on Meta::CPAN

package Acme::ComeFrom;
$Acme::ComeFrom::VERSION = '0.11';

use 5.005;
use strict;
use vars qw/$CacheEXPR/;
use Filter::Simple 0.70;

my $Mark  = '__COME_FROM';
my $count = '0000';

FILTER_ONLY code => sub {
    my ( %subs, %labels, @tokens, @counts );
    my $source = $_;

    $_ = $source and return unless $source =~ /comefrom/;

    while ( $source =~
        s/\bcomefrom\b(\s*)\(?(&?)?([\w\:]+|[^\;]+)(?:\(\))?\)?/$Mark$count:$1/ )
    {
        my $token = $3;

        push @{ $subs{$token} }, $count++ and next if $2;
        push @{ $labels{$token} }, $count++ and next if $token =~ /^[\w\:]+$/;
        push @tokens, $token;
        push @counts, $count++;
    }

    $_ = $source and return unless %subs or %labels or @tokens;

    my $code = '';

    if (%subs) {
        require Hook::LexWrap;
        $code .= 'require Hook::LexWrap;';
    }

    while ( my ( $k, $v ) = each %subs ) {
        my $chunk = make_chunk($v);
        $code .= "Hook::LexWrap::wrap($k, post => sub { $chunk });";
    }

    if (@tokens) {
        $source =~ s!(\n\s*)([a-zA-Z_]\w+):!
            my $label = $2;
            my $chunk = make_chunk(
                [ @counts, exists $labels{$label} ? @{$labels{$label}} : ()],
                $label, \@tokens
            ) unless substr($label, 0, length($Mark)) eq $Mark;

            "$1$label:" . ($chunk ? "do {$chunk};" : '');
        !eg;
    }
    else {
        while ( my ( $k, $v ) = each %labels ) {
            my $chunk = make_chunk($v);
            $source =~ s!\Q$k\E:!$k: do {$chunk};!g;
        }
    }

    $_ = ( $code ? "CHECK { $code; 1 };" : '' ) . $source;
};

sub make_chunk {
    my $pkg = '$' . __PACKAGE__;
    my ( $v, $label, $cond ) = @_;
    my $chunk = '';

    foreach my $iter ( 0 .. $#{$v} ) {
        my $fork = ( $iter != $#{$v} );

        if ( defined $cond->[$iter] ) {
            my $forktext = ( $fork ? ' or fork' : '' );

            $chunk .= "
                if (\$Acme::ComeFrom::CacheEXPR) {
                    $pkg\::CACHE[$v->[$iter]] = eval q;$cond->[$iter];
                        unless exists $pkg\::CACHE[$v->[$iter]];

                    goto $Mark$v->[$iter] unless
                        ('$label' ne $pkg\::CACHE[$v->[$iter]])$forktext;
                }
                else {
                    goto $Mark$v->[$iter] unless
                        ('$label' ne eval q;$cond->[$iter];)$forktext;
                }
            ";
        }
        else {
            $chunk .= "goto $Mark$v->[$iter]" . ( $fork ? " unless fork();" : ';' );
        }
    }

    $chunk =~ s/\n */ /g;
    return $chunk;
}

1;

__END__

=head1 NAME

Acme::ComeFrom - Parallel Goto-in-reverse

=head1 VERSION

This document describes version 0.11 of Acme::ComeFrom, released
October 15, 2007.



( run in 0.946 second using v1.01-cache-2.11-cpan-140bd7fdf52 )