/usr/local/CPAN/Acme-Your/Acme/Your/Filter.pm


package Acme::Your::Filter;
use strict;
use warnings;
use Filter::Simple;
use Parse::RecDescent;

FILTER_ONLY executable => \&_filter_code;

# it's also all on one line because I don't want to disturb the line
# numbers too much

use Data::Dumper;
sub _filter_code {
    s/\b((?:have|your)\b.*?;)/ _transform_statement( $1 ) /ge;
}

my $grammar = q
{
    list        :  identifier
                            |  '(' plist ')'

    plist       : identifier ',' plist
                            | identifier

    identifier  : /[@%\$]\w+/

    keyword     : 'your'
                            | 'have'

    declaration : keyword list ';'
                            | keyword list  '=' /[^;]+/ ';'
                            | <error>
};

my $parse;

sub _transform_statement {
    my $statement = shift;

    $::RD_AUTOACTION = q{ [ @item ] };

    $parse ||= new Parse::RecDescent $grammar;
    my $tree = $parse->declaration($statement);

    my $pattern = $tree->[1][1] eq 'your' ? 'your' : 'have';

    my $assign;
    if ($tree->[-3] eq '=') {
        $assign = $tree->[-2];
    }

    my @ids = _walk_tree($tree);

    my $new_statement = join('', map { _variable_declaration($pattern, $_) } @ids );
    if ($assign) {
        $new_statement .= "(". join(', ', @ids) .") = $assign;";
    }
    #print $new_statement;
    return $new_statement;
}

sub _variable_declaration {
    my $keyword = shift;
    my $name    = shift;

    $name =~ s/^([\$@%])//;
    my $sigil = $1;

    if ($keyword eq 'your') {
        return
          join('',
               qq{ our $sigil$name; },
               qq{ local $sigil$Acme::Your::into\::$name  },
               qq{     = $sigil$Acme::Your::into\::$name; },
               qq{ *$name = \\$sigil$Acme::Your::into\::$name; },
              );
    }

    # have
    return join('',
                qq{ our $sigil$name; },
                qq{ local *$Acme::Your::into\::$name  = \\$sigil$name; },
               );
}

# extract identifiers from the parse tree
sub _walk_tree {
    my $tree = shift;

    my @id;
    push @id, $tree->[1] if $tree->[0] eq 'identifier';
    for (@$tree) {
        push @id, _walk_tree($_) if ref $_;

    }
    return @id;
}

1;
__END__