undef $/; $\ = "\n"; $| = 1; binmode STDIN, ":bytes"; binmode STDOUT, ":bytes"; use warnings; use strict; use integer; use feature "switch"; use Encode 'encode'; use Memoize; # Returns the Latin-1 codepoint for a given character. # This handles non-ASCII-compatible encodings used for strings that # are literal to the program (out of these, Perl only supports # EBCDIC). sub l1cp { ord encode('ISO-8859-1', $_[0]); } memoize('l1cp'); # For debug output. sub qquote { my $octet = $_[0] % 256; my $int = $_[0] / 256; if ($octet >= 32 && $octet <= 126) { $octet = chr $octet; } else { $octet = sprintf '\x%X', $octet; } $int ? "${octet}_$int" : $octet; } sub joinrle { scalar @_ == 0 and return ""; my $repcount = 1; while (@_ > 1 && $_[0] eq $_[1]) { $repcount++; shift; } if (@_ == 1) { return $repcount > 1 ? "$_[0]*$repcount" : $_[0]; } my $head = shift; if ($repcount > 1) { return "$head*$repcount " . &joinrle; } else { return "$head " . &joinrle; } } my $program = <>; $program .= ; # append stdin to the program # Our convention for an octet snapshot is to represent it as # (octet value) + (integer * 256). If there's no integer, we use 0 # (which isn't normally valid as an integer). my @program = map ord, split //, $program; my $actdef = []; # Definition for octet x, int y is stored in $defmatrix[x][y-1] my @defmatrix = map [undef, undef, undef], 0 .. 255; my $modstate = undef; my $modstatecounter = 0; # Modification states. sub nop_mod { @_; } sub inc_int { $modstate = \&nop_mod; ($_[0], $_[1] + 1); } sub dec_int { $modstate = \&nop_mod; ($_[0], $_[1] - 1); } sub min_int { $modstate = \&nop_mod; ($_[0], 1); } sub max_int { $modstate = \&nop_mod; ($_[0], scalar @{$defmatrix[$_[0]]}); } sub quote1s { $modstate = \&nop_mod; (($_[0] + 1) % 255, 2); } sub quote_m { if ($_[0] eq l1cp(']')) { if (!$modstatecounter) { $modstate = \&nop_mod; return @_; } $modstatecounter--; } elsif ($_[0] eq l1cp('[')) { $modstatecounter++; } ($_[0], 2); } my %modstatemap = ( l1cp('+') => \&inc_int, l1cp('-') => \&dec_int, l1cp('>') => \&min_int, l1cp('<') => \&max_int, l1cp(',') => \"e1s, l1cp('[') => \"e_m, l1cp(']') => \&nop_mod, ); $modstate = \&nop_mod; while (@program) { if ($ENV{TAKEOVER_DEBUG}) { print STDERR "Debug: program = << ", joinrle(map qquote($_), @program), " >>, actdef = << ", joinrle(map qquote($_), @$actdef), " >>"; } my $cmd = shift @program; my $octet = $cmd % 256; my $int = $cmd / 256; # under "use integer", this is integer division $int ||= scalar @{$defmatrix[$octet]}; ($octet, $int) = $modstate->($octet, $int); if ($int == 1) { push @{$defmatrix[$octet]}, $actdef; $actdef = []; } elsif ($int == 2) { my $defcount = scalar @{$defmatrix[$octet]}; push @$actdef, $octet + ($defcount * 256); } elsif ($int == 3) { $modstate = $modstatemap{$octet}; if (!$modstate) { $modstate = \&nop_mod; unshift @program, l1cp('.') + (4 * 256); unshift @program, ($octet == 0 ? 255 : $octet - 1); } } else { # this crashes if $int is out of range, as reqired by the spec @program = (@{$defmatrix[$octet][$int-1]}, @program); } } print map chr($_ % 256), @$actdef;