Acme-AsciiArtinator

 view release on metacpan or  search on metacpan

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

#
# replace darkspace on the pic with characters from the code
#
sub print_code_to_pic {
  my ($pic, @tokens) = @_;
  local $" = '';
  my $code = "@tokens";
  my @code = split //, $code;

  $pic =~ s/(\S)/@code==0?"#":shift @code/ge;

  print $pic;
}


#
# find misalignment between multi-character tokens and blocks
# and report position where additional padding is needed for
# alignment
#
sub padding_needed {
  my @tokens = @{$_[0]};
  my @contexts = @{$_[1]};
  my @blocks = @{$_[2]};
  my $ib = 0;
  my $tc = 0;
  my $bc = $blocks[$ib++];
  my $it = 0;
  while ($bc == 0) {
    $bc = $blocks[$ib++];
    if ($ib > @blocks) {
      print "Error: picture is not large enough to contain code!\n";

      print map {(" ",length $_)} @tokens;
      print "\n\n@blocks\n";

      return [-1,-1];
    }
  }
  foreach my $t (@tokens) {
    my $tt = length $t;
    defined $tt or print "! \$tt is not defined! \$it=$it \$ib=$ib\n";
    defined $bc or print "! \$bc is not defined! \$it=$it \$ib=$ib \$tt=$tt\n";
    if ($tt > $bc) {
      if ($DEBUG) {
	print "Need to pad by $bc spaces at or before position $tc\n";
      } else {
	print "\rNeed to pad by $bc spaces at or before position $tc            ";
      }
      return [$it, $bc];
    }

    $bc -= $tt;

    #
    # for regular Perl variables ( "$x", "@bob" ), it is OK to split
    # the sigil and the var name with any whitespace ("$ x", "@\n\tbob").
    # For special Perl vars ( '$"', "$/", "$$" ), it is OK to split
    # with spaces and tabs but not with newlines.
    # 
    # Check for this condition here and say that padding is needed if
    # a special var is currently aligned on a newline.
    #
    if ($bc == 0 && $blocks[$ib] == 0 && $tokens[$it] eq "\$"
	&& $contexts[$it] eq "SIGIL" && $contexts[$it+1] eq "name"
	&& length($tokens[$it+1]) == 1 && $tokens[$it+1] =~ /\W/) {

      warn "\$tt > \$bc but padding still needed: \n",
	(join " : ", @tokens[0 .. $it+1]), "\n",
	  (join " : ", @contexts[0 .. $it+1]), "\n",
	    (join " : ", @blocks[0 .. $ib+1]), "\n";

      return [$it, 1] if 1;
    }


    while ($bc == 0) {
      $bc = $blocks[$ib++];
      if ($ib > @blocks) {
	print "Error: picture is not large enough to contain code!\n";

	print map {(" ",length $_)} @tokens;
	print "\n\n@blocks\n";

	return [-1,-1];
      }
    }
    $tc += length $t;
    $it++;
  }
  return;
}

#
# choose a random number between 0 and n-1,
# with the distribution heavily weighted toward
# the high end of the range
#
sub hi_weighted_rand {
  my $n = shift;
  my (@p, $r, $p);
  for ($r = 1; $r <= $n; $r++) {
    push @p, $p += $r * $r * $r;
  }
  $p = int(rand() * $p);
  for ($r = 1; $r <= @p; $r++) {
    return $r if $p[$r-1] >= $p;
  }
  return $n;
}

#
# look for opportunity to insert padding into the
# code at the specified location
#
sub try_to_pad {
  my ($pos, $npad, $tref, $cref) = @_;

    #      padding techniques:
    # X        SIGIL name --->   SIGIL { name }
    #          XXX       --->    ( XXX )

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

    $a=$b+$c;;;;;;$d=4;;;;;;
    ;;;;;;;;;$a=$b+$c;;;;;;;;$d=4;

=item 2. Putting braces around a variable name.

In general, we can replace C<$name> with C<${name}> and the code
will run the same.

=back

There are several other interesting ways to pad code (putting parentheses
around expressions, adding and or-ing zeros to expressions, using quoted
strings in a void context) that may be put to use in future versions
of this module.

When all tokens from the code are successfully aligned with the
blocks of darkspace from the artwork, we can paste the code on top
of the art and write the output file. 

Sometimes we insert too many characters without successfully
aligning the tokens and darkspace blocks (and actually in the
spider example, this happens about 90% of the time). If this
happens, we will start over and retry up to 100 times.

=head1 BEST PRACTICES

Certain coding practices will increase the chance that
C<Acme::AsciiArtinator> will be able to embed your code
in the artwork of your choice. In no particular order,
here are some suggestions:

=over 4

=item * Make sure the original code works

Make sure the code compiles and test it to see if it
works like you expect it to
before running the ASCII Artinator. It would be frustrating to
try to debug an artinated script only to later realize that
there was some bug in the original input.

=item * Get rid of comments

This module won't handle comments very well. There's no way
to stop the ASCII Artinator from splitting your comment across
two lines and breaking the code.

=item * Reduce whitespace

In addition to making the code longer and thus more difficult
to align, any whitespace in your code will be printed out as
space over a darkspace in the art and put a "hole" in your
picture. It would be nice if there was a way to align the
whitespace in the code with the whitespace in the art, but that
is probably something for a far future version.

=item * Avoid significant newlines

Newlines are stripped from the code before the code is tokenized.
If there are any significant newlines (I mean the literal 0x0a char.
It should still be OK to say  C<print"\n">), then the artinated
code will run differently.

=item * Consider workarounds for quoted strings

Quoted strings are parsed as a single token. Consider ways to break
them up so that can be split into multiple tokens. For example, instead
of saying C<$h="Hello, world!";>, we could actually say something like:

    &I;($c,$e)=qw(, !);$h=H.e.l.l.o.$c.$".W.o.r.l.d.$e;

The modified code is a lot longer, but this code can be split at any
point except in the middle of C<qw>, so it is much more flexible code
from the perspective of the Artinator.

=item * Perform some smart reordering

In the spider example, we see that the largest contiguous blocks of
darkspace are in the center of the spider, and at the beginning and
end of the spider art, there are many smaller blocks of darkspace.
In this case, code that has large tokens in the middle or near the
end of the code will be more flexible than code with large tokens in
the beginning of the code. So for example, we are better off
writing

    @o=(map ... );print@o

than

    print@o=(map ... )

even through the latter code is a little shorter.

=back

=head1 OPTIONS

The C<asciiartinate> method supports the following options:

=over 4

=item art_file => filename

=item art_string => string

=item art => string

Specifies the ASCII artwork that we'll try to embed code into.
At least one of C<art>, C<art_string>, C<art_file> must be
specified.

=item code_file => filename

=item code_string => string

=item code => string

Specifies the Perl code that we will try to embed into the
art. At least one of C<code>, C<code_string>, C<code_file>
must be specified.

=item output => filename

Specifies the output file for the embedded code. If omitted,
output is written to the file "ascii-art.pl" in the current
directory.

=item compile_check => 0 | 1



( run in 1.021 second using v1.01-cache-2.11-cpan-007c89162af )