The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Acme::EyeDrops - Visual Programming in Perl

SYNOPSIS

    use Acme::EyeDrops qw(sightly);
    
    print sightly( { Shape       => 'camel',
                     SourceFile  => 'myprog.pl' } );

DESCRIPTION

Acme::EyeDrops converts a Perl program into an equivalent one, but without all those unsightly letters and numbers.

In a Visual Programming breakthrough, EyeDrops allows you to pour the generated program into various shapes, such as UML diagrams, enabling you to instantly understand how the program works just by glancing at its new and improved visual representation.

Like Acme::Smirch, but unlike Acme::Bleach and Acme::Buffy, the generated program runs without requiring that Acme::EyeDrops be installed on the target system.

EXAMPLES

Suppose you have a program, helloworld.pl, consisting of:

    print "hello world\n";

You can make this program look like a camel with:

    print sightly( { Shape       => 'camel',
                     SourceFile  => 'helloworld.pl',
                     Regex       => 1 } );

Instead of using the API above, you may find it more convenient to use the sightly.pl command in the demo directory:

    sightly.pl -h           (for help)
    sightly.pl -s camel -f helloworld.pl -r >new.pl
    cat new.pl
    perl new.pl             (should print "hello world" as before)

Notice that the shape 'camel' is just the file 'camel.eye' in the same directory as EyeDrops.pm, so you are free to add your own new shapes as required.

If your boss demands a UML diagram describing the program, you can give him this:

    print sightly( { Shape       => 'uml',
                     SourceFile  => 'helloworld.pl',
                     Regex       => 1 } );

If it is a Windows program, you can indicate that too, by combining shapes:

    print sightly( { Shape       => 'uml,window',
                     SourceFile  => 'helloworld.pl',
                     Regex       => 1 } );

producing this improved visual representation:

                ''=~('('.'?'.'{'.('`'|'%').('['^'-').(
                (                                    (
                (                                    (
                (                                    (
                (                                    (
                (                                    (
                '`'))))))))))|'!').('`'|',').'"'.('['^
                                  (
                                 ( (
                                (   (
                               '+'))))
                                  )
                                  )
                .('['^')').('`'|')').('`'|'.').(('[')^
                (                                    (
                (                                    (
 '/'))))).('{'^'[').'\\'.('"').(      '`'|'(').('`'|'%').('`'|"\,").(
 (                             (      (                             (
 (                             (      (                             (
 (                             (      (                             (
 (                             (      (                             (
 (                             (      (                             (
 '`'))))))))))))))))))))|"\,").(      '`'|'/').('{'^'[').('['^"\,").(


 '`'|'/').('['^')').('`'|',').('`'|'$').('\\').
 '\\'.('`'|'.').'\\'.'"'.';'.('!'^'+').'"'.'}'.
 ')');$:='"'|'#';$:='?'&'!';$:='*'|'~';$:="\%"&
 '$';                  $:                  ='"'
 |'#'                  ;(                  $:)=
 '?'&                  ((                  '!')
 );$:                  =(                  '*')
 |'~'                  ;(                  $:)=
 '%'&                  ((                  '$')
 );$:                  =(                  '"')
 |'#'                  ;(                  $:)=
 '?'&                  ((                  '!')
 );$:                  =(                  '*')
 |'~'                  ;(                  $:)=
 '%'&'$';$:='"'|'#';$:='?'&'!';$:='*'|('~');$:=
 '%'&                  ((                  '$')
 );$:                  =(                  '"')
 |'#'                  ;(                  $:)=
 '?'&                  ((                  '!')
 );$:                  =(                  '*')
 |'~'                  ;(                  $:)=
 '%'&                  ((                  '$')
 );$:                  =(                  '"')
 |'#'                  ;(                  $:)=
 '?'&                  ((                  '!')
 );$:                  =(                  '*')
 |'~';$:='%'&'$';$:='"'|'#';$:='?'&'!';$:="\*"|
 '~';$:='%'&'$';$:='"'|'#';$:='?'&'!';$:=('*')|
 '~';$:='%'&'$';$:='"'|'#';$:='?'&'!';$:=('*');

This is a Visual Programming breakthrough in that you can tell that it is a Windows program and see its UML structure too, just by glancing at the code.

For Linux-only, you can apply its /usr/games/banner command to the program's source text like this:

    print sightly( { Shape       => 'srcbanner',
                     Width       => 70,
                     SourceFile  => 'helloworld.pl',
                     Regex       => 1 } );

The generated program, shown below, is easier to understand than the original because its characters are bigger and easier to read:

 ''                                        =~ 
 +(                                        (( 
 '(')).'?'.'{'.('`'|'%').('['^'-').('`'|'!'). 
 ('`'|',').'"'.('['^'+').('['^')').('`'|')'). 
 ('`'|'.').('['^'/').('{'^'[').'\\'.'"'.('`'| 
 '(').('`'|'%').('`'|',').('`'|',').('`'|'/') 
                    .+(                (( 
                  '{'                    )) 
                 ^((                      '['
                 ))                       ).(
                '['                       ^(( 
                ',')                      )). 
                 ('`'                   |'/')
                 .(('[')^           (')')).( 
                   '`'|',').('`'|'$').'\\'.
                    '\\'.('`'|"\.").'\\'. 
                       '"'.';'.('!'^'+'
                                     
                ).+                        (( 
                '"'                        )) 
                .'}'.')');$:='"'|'#';$:="\?"& 
                '!';$:='*'|'~';$:='%'&'$';$:= 
                '"'|'#';$:='?'&'!';$:='*'|'~' 
                ;$:='%'&'$';$:='"'|'#';$:='?' 
                &((                   '!'
                                       )); 
                                        $:= 
                                        '*'| 
                                        "\~";
                                    $:=('%')& 
                                   '$';$:='"' 
                                   |('#');$:=
                                   '?'&"\!"; 
                                     ($:) 

                =((                        (( 
                '*'                        ))         ))|
                '~';$:='%'&'$';$:='"'|'#';$:=       ('?')& 
                '!';$:='*'|'~';$:='%'&'$';$:=      '"'|'#';
                $:='?'&'!';$:='*'|'~';$:='%'&       '$';$: 
                ='"'|'#';$:='?'&'!';$:=('*')|         '~'
                ;$:

                =((                        (( 
                '%'                        )) 
                ))&'$';$:='"'|'#';$:='?'&'!'; 
                $:='*'|'~';$:='%'&'$';$:='"'| 
                '#';$:='?'&'!';$:='*'|'~';$:= 
                '%'&'$';$:='"'|'#';$:='?'&'!' 
                ;$:                     =( 
                                         (( 
                                         '*')
                                         ))|+
                                         '~'; 
                $:=                     "\%"& 
                '$';$:='"'|'#';$:='?'&'!';$:= 
                '*'|'~';$:='%'&'$';$:='"'|'#'
                ;$:='?'&'!';$:='*'|"\~";$:= 
                '%'&'$';$:='"'|'#';$:='?'
                &((
                   
                                           (( 
                                           (( 
                      '!'))))));$:='*'|'~';$:=('%')&
                   '$';$:='"'|'#';$:='?'&'!';$:='*'|
                 '~';$:='%'&'$';$:='"'|'#';$:=('?')&
                '!';$:='*'|'~';$:='%'&'$';$:='"'|'#'
                ;($:)=                     (( 
                '?'))                      &+ 
                 '!' 
                 ;$: 
                   =( 
  
  
  
  
  
  
  
  
  
  
  
  
  
 
 
 
                                                     '*')|
                                                    '~';$:= 
                                                   '%'&"\$";$:=
                                                   '"'|'#' 
                                                    ;($:)
                                                         
                =((                                            ((
                '?'                                            ))
                ))&'!';$:='*'|'~';$:='%'&'$';$:='"'|'#';$:=('?')&
                '!';$:='*'|'~';$:='%'&'$';$:='"'|'#';$:='?'&"\!";
                $:='*'|'~';$:='%'&'$';$:='"'|'#';$:='?'&('!');$:=
                '*'|'~';$:='%'&'$';$:='"'|'#';$:='?'&'!';$:="\*"|
                '~'                     ;( 
                                         $: 
                                         )=((
                                         '%')
                                         )&(( 
                '$'                     ));$: 
                ='"'|'#';$:='?'&'!';$:=('*')| 
                '~';$:='%'&'$';$:='"'|'#';$:=
                '?'&'!';$:='*'|'~';$:="\%"& 
                '$';$:='"'|'#';$:='?'&'!'
                ;$:
                   
                          ='*'|'~'; 
                       $:='%'&"\$";$:= 
                     '"'|'#';$:='?'&'!'; 
                   $:='*'|'~';$:='%'&"\$"; 
                  $:='"'|     ((     '#')); 
                 ($:)         =(        '?') 
                 &((          ((          '!'
                )))           );           $: 
                =((           ((           (( 
                '*'           ))           )) 
                 ))           |+          '~'
                 ;(           $:        )=(( 
                  ((          ((     '%'))) 
                   )))        &'$';$:='"'| 
                     ((       "\#"));$:= 
                              '?'&'!'; 
                                    
                $:=                                            ((
                '*'                                            ))
                |'~';$:='%'&'$';$:='"'|'#';$:='?'&'!';$:='*'|'~';
                $:='%'&'$';$:='"'|'#';$:='?'&'!';$:='*'|('~');$:=
                '%'&'$';$:='"'|'#';$:='?'&'!';$:='*'|'~';$:="\%"&
                '$';$:='"'|'#';$:='?'&'!';$:='*'|'~';$:='%'&"\$";
                $:=

                '"'                                            |+
                '#'                                            ;(
                $:)='?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|"\#";$:=
                '?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|'#';$:="\?"&
                '!';$:='*'|'~';$:='%'&'$';$:='"'|'#';$:='?'&"\!";
                $:='*'|'~';$:='%'&'$';$:='"'|'#';$:='?'&('!');$:=
                '*'

                          |"\~";$:= 
                       '%'&'$';$:='"'| 
                     '#';$:='?'&"\!";$:= 
                   '*'|'~';$:='%'&"\$";$:= 
                  '"'|'#'            ;($:)= 
                 '?'&                   '!'; 
                 $:=                      '*'
                |((                        (( 
                '~'                        )) 
                ));                        $: 
                =((                       '%' 
                 ))&                     '$';
                 ($:)=                 "\""| 
                   '#';$:='?'&'!';$:=('*')|
                    '~';$:='%'&'$';$:='"' 
                      |'#';$:='?'&"\!"; 
                          $:=('*')| 
  
  
  
  
  
  
  
  
  
  
  
 
 
 
 
 
 
                                           (( 
                                           (( 
                                        '~')) 
                                   ));$:='%'& 
                                '$';$:=('"')| 
                            '#';$:='?'&'!';$: 
                        ='*'|'~';$:="\%"&  (( 
                    '$'));$:='"'|'#';
                $:='?'&'!';$:='*'
                  |'~';$:="\%"&
                      '$';$: 
                         =('"')| 
                      '#';$:='?'&'!';
                 $:='*'|'~';$:='%' 
                  &'$';$:="\""|
                      '#';$: 
                          ="\?"& 
                              '!';$:=
                                '*'|'~'    ;( 
                                    $:)="\%"& 
                                        "\$"; 
                                           $: 
                                           =( 
                          '"')|'#'; 
                       $:='?'&"\!";$:= 
                     '*'|'~';$:='%'&'$'; 
                   $:='"'|'#';$:='?'&"\!"; 
                  $:='*'|            '~';$: 
                 ='%'                   &'$' 
                 ;$:                      =((
                '"'                        )) 
                |((                        (( 
                '#'                        )) 
                ));                       $:= 
                 '?'                     &'!'
                 ;($:)                 ='*'| 
                   '~';$:='%'&'$';$:=('"')|
                    '#';$:='?'&'!';$:='*' 
                      |'~';$:='%'&"\$"; 
                          $:=('"')| 
                '#'                        ;( 
                $:)                        =( 
                '?')&'!';$:='*'|'~';$:=('%')& 
                '$';$:='"'|'#';$:='?'&'!';$:= 
                '*'|'~';$:='%'&'$';$:='"'|'#' 
                ;$:='?'&'!';$:='*'|'~';$:='%' 
                &((                   '$'
                                       )); 
                                        $:= 
                                        '"'| 
                                        "\#";
                                    $:=('?')& 
                                   '!';$:='*' 
                                   |('~');$:=
                                   '%'&"\$"; 
                                     ($:) 

                =((                                            ((
                '"'                                            ))
                ))|'#';$:='?'&'!';$:='*'|'~';$:='%'&'$';$:=('"')|
                '#';$:='?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|"\#";
                $:='?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|('#');$:=
                '?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|'#';$:="\?"&
                '!'

                         ;$:='*'|'~';
                       $:='%'&('$');$:=
                    '"'|'#';$:='?'&'!';$: 
                   ='*'|'~';$:='%'&"\$";$:=
                 '"'|'#';           $:="\?"& 
                 '!';                   ($:)=
                '*'|                      '~' 
                ;$:                       =(( 
                 ((                       '%'
                 )))                      )&+
                  '$'                    ;( 
                    $:)                =(                      ((
                '"')))|'#';$:='?'&'!';$:='*'|'~';$:='%'&('$');$:=
                '"'|'#';$:='?'&'!';$:='*'|'~';$:='%'&'$';$:="\""|
                '#';$:='?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|"\#";
                $:='?'&'!';$:='*'|'~';$:='%'&'$';$:='"'|('#');$:=
                '?'
                &((

                '!') 
                  );$:= 
                     "\*"| 
                        '~';$:
                            ='%'&
                               "\$"; 
                                  ($:)= 
                                     "\""| 
                                       "\#"; 
                                          ($:)= 
                                             "\?"& 
                                                '!';$:
                                                   ="\*"|
                                                       "\~";
                                                          ($:)= 
                                                             '%'&
                                                                 
                '$'                        ;( 
                $:)                        =( 
                '"')|'#';$:='?'&'!';$:=('*')| 
                '~';$:='%'&'$';$:='"'|'#';$:= 
                '?'&'!';$:='*'|'~';$:='%'&'$' 
                ;$:='"'|'#';$:='?'&'!';$:='*' 
                |((                     (( 
                                         (( 
                                         '~')
                                         ))))
                                         );$: 
                =((                     '%')) 
                &'$';$:='"'|'#';$:='?'&'!';$: 
                ='*'|'~';$:='%'&'$';$:=('"')|
                '#';$:='?'&'!';$:='*'|"\~"; 
                $:='%'&'$';$:='"'|'#';$:=
                '?'
                   
                                                     &'!';
                                                    $:='*'| 
                                                   '~';$:="\%"&
                                                   '$';$:= 
                                                    "\""|

                                                         
                   '#'                  ;$:
                 ="\?"&                "\!"; 
                $:="\*"|              '~';$:= 
            '%'&"\$";$:=              '"'|'#' 
               ;$:='?'&                "\!";

Let's get more ambitious and create a big JAPH.

    my $src = <<'PROG';
    open 0;
    $/ = undef;
    $x = <0>;
    close 0;
    $x =~ tr/!-~/#/;
    print $x;
    PROG
    print sightly({ Shape         => 'japh',
                    SourceString  => $src,
                    Regex         => 1 } );

This works. However, if we were to change:

    $x =~ tr/!-~/#/;

to:

    $x =~ s/\S/#/g;

the generated program would malfunction in strange ways because it is running inside a regular expression and Perl's regex engine is not reentrant. In this case, we must resort to:

    print sightly({ Shape         => 'japh',
                    SourceString  => $src,
                    Regex         => 0 } );

which runs the generated sightly program via eval instead.

EyeDrops can also convert plain text:

    print sightly({ Shape         => 'window',
                    SourceString  => "Bill Gates is a pest!\n",
                    Regex         => 1,
                    Print         => 1 } );

In this example, the generated program will print the SourceString above.

But wait, there's more. You can encode binary files too.

    print sightly({ Shape       => 'camel,japh,camel',
                    SourceFile  => 'some_binary_file',
                    Binary      => 1,
                    Print       => 1,
                    Gap         => 5 } );

This is prettier than uuencode/uudecode. Here is how you encode/decode binary files with sightly.pl.

To encode:

    sightly.pl -g5 -bps camel,japh,camel -f some_binary_file >fred

To decode:

    perl fred >f.tmp

To verify it worked:

    cmp f.tmp some_binary_file

REFERENCE

Sightly Encoding

There are 32 characters in the sightly character set:

    ! " # $ % & ' ( ) * + , - . /            (33-47)
    : ; < = > ? @                            (58-64)
    [ \ ] ^ _ `                              (91-96)
    { | } ~                                  (123-126)

A sightly string consists only of characters drawn from this set.

The ascii_to_sightly function converts an ASCII string (0-255) to a sightly string; the sightly_to_ascii function does the reverse.

Function Reference

ascii_to_sightly STRING

Given an ascii string STRING, returns a sightly string.

sightly_to_ascii STRING

Given a sightly string STRING, returns an ascii string.

regex_print_sightly STRING

Given an ascii string STRING, returns a sightly-encoded Perl program with a print statement embedded in a regular expression. When run, the program will print STRING.

regex_eval_sightly STRING

Given a Perl program in ascii string STRING, returns an equivalent sightly-encoded Perl program using an eval statement embedded in a regular expression.

clean_print_sightly STRING

Given an ascii string STRING, returns a sightly-encoded Perl program with a print statement executed via eval. When run, the program will print STRING.

clean_eval_sightly STRING

Given a Perl program in ascii string STRING, returns an equivalent sightly-encoded Perl program using an eval statement executed via eval.

regex_binmode_print_sightly STRING

Given an ascii string STRING, returns a sightly-encoded Perl program with a binmode(STDOUT) and a print statement embedded in a regular expression. When run, the program will print STRING. Note that STRING may contain any character in the range 0-255. This function is used to sightly-encode binary files. This function is dodgy because regexs don't seem to like binary zeros; use clean_binmode_print_sightly instead.

clean_binmode_print_sightly STRING

Given an ascii string STRING, returns a sightly-encoded Perl program with a binmode(STDOUT) and a print statement executed via eval. When run, the program will print STRING. Note that STRING may contain any character in the range 0-255. This function is used to sightly-encode binary files.

get_builtin_shapes

Returns a list of the built-in shape names.

get_eye_shapes

Returns a list of the eye shapes. An eye shape is just a file with a .eye extension residing in the same directory as EyeDrops.pm.

pour_sightly SHAPESTRING PROGSTRING GAP

Given a shape string SHAPESTRING, a sightly-encoded program string PROGSTRING, and a GAP between successive shapes, returns a properly shaped program string.

sightly HASHREF

Given a hash reference, HASHREF, describing various attributes, returns a properly shaped program string.

The attributes that HASHREF may contain are:

    Shape         Describes the shape you want.
                  First, a built-in shape is looked for. Next a
                  I<eye> shape (.eye file in the same directory
                  as EyeDrops.pm) is looked for. Finally a file
                  name is looked for.

    ShapeString   Describes the shape you want.
                  This time you specify a shape string.

    SourceFile    The source file name to convert.

    SourceString  Specify a string instead of a file name.

    BannerString  String to use with built-in Shape 'banner'.

    Regex         Boolean. If set, try to embed source program
                  in a regular expression. Do not set this flag
                  when converting complex programs.

    Print         Boolean. If set, use a print statement instead
                  of the default eval statement.

    Binary        Boolean. Set if encoding a binary file.

    Gap           The number of lines between successive shapes.

    Width         Ignored for .eye file shapes. For built-in shapes,
                  specifies the shape width in characters.

MIGRATION

To aid those migrating from Acme::Bleach and Acme::Buffy, the 'bleach' and 'buffy' shapes are provided.

BUGS

A really diabolical shape with lots of single character lines will defeat the shape-pouring algorithm.

You can eliminate all alphanumerics (via Regex => 1) only for small programs with simple I/O and no regular expressions. To convert complex programs, you must use Regex => 0, which emits a leading unsightly eval.

AUTHOR

Andrew Savige <andrew.savige@ir.com>

SEE ALSO

Acme::Bleach Acme::Smirch Acme::Buffy

CREDITS

I blame Japhy and Ronald J Kimball and others on the fwp mailing list for exposing the ''=~ trick and Jas Nagra for explaining his Acme::Smirch module.

COPYRIGHT

Copyright (c) 2001 Andrew Savige. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.