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. =head1 SYNOPSIS use Acme::ComeFrom; sub func { print "@_" }; func("Start\n"); print "This won't happen\n"; comefrom &func; print "Branch 1\n"; exit; comefrom &func; print "Branch 2\n"; label: print "This won't happen either\n"; comefrom label; print "Branch 2.1\n"; exit; comefrom label; print "Branch 2.2\n"; EXPR0: print "To be\n"; exit; comefrom "EXPR".int(rand(2)); print "Not to be\n"; =head1 DESCRIPTION B programmers have for a long time monopolized the enormously powerful construct C, both as a flow-control replacement to C, and as a simple way to mark parallel execution branches in the multi-thread variant. But now, with B, we Perl hackers can finally be on par with them in terms of wackiness, if not in obfuscation. Just like C, C comes in three different flavors: =over 4 =item comefrom LABEL The C form finds the statement labeled with C