package Acme::PlayCode; use Moose; use PPI; use Path::Class (); our $VERSION = '0.12'; our $AUTHORITY = 'cpan:FAYLAND'; with 'MooseX::Object::Pluggable'; has 'tokens' => ( is => 'rw', isa => 'ArrayRef', auto_deref => 1, default => sub { [] }, ); has 'token_flag' => ( is => 'rw', isa => 'Num', default => 0 ); has 'output' => ( is => 'rw', isa => 'ArrayRef', default => sub { [] } ); sub play { my ( $self, $code, $opts ) = @_; my $file; if ( $code !~ /\s/ and -e $code ) { $file = Path::Class::File->new($code); $code = $file->slurp(); } # clear to multi-run $self->output( [] ); $self->token_flag( 0 ); my $doc = PPI::Document->new( \$code ); $self->tokens( $doc->find('PPI::Token') ); $self->do_with_tokens(); my @output = @{ $self->output }; # check Acme::PlayCode::Plugin::PrintComma @output = grep { $_ ne 'Acme::PlayCode::!@#$%^&*()_+' } @output; my $output = join('', @output); if ( $opts->{rewrite_file} and $file ) { my $fh = $file->openw(); print $fh $output; $fh->close(); } return $output; } sub do_with_tokens { my ( $self ) = @_; while ( $self->token_flag < scalar @{$self->tokens}) { my $orginal_flag = $self->token_flag; my $content = $self->do_with_token_flag( $self->token_flag ); push @{ $self->output }, $content if ( defined $content ); # if we don't move token_flag, ++ if ( $self->token_flag == $orginal_flag ) { $self->token_flag( $self->token_flag + 1 ); } } } sub do_with_token_flag { my ( $self, $token_flag ) = @_; my @tokens = $self->tokens; my $token = $tokens[$token_flag]; return $self->do_with_token( $token ); } sub do_with_token { my ( $self, $token ) = @_; my $token_flag = $self->token_flag; my @tokens = $self->tokens; if ( $token->isa('PPI::Token::HereDoc') ) { my @output = @{ $self->output }; my @next_tokens; my $old_flag = $token_flag; while ( $old_flag++ ) { push @next_tokens, $tokens[$old_flag]; last if ( $tokens[$old_flag]->content eq ';' ); } push @output, $token->content, join('', map { $_->content } @next_tokens ), "\n", join('', $token->heredoc), $token->terminator; # skip next itself and next ';' $self->token_flag( $token_flag + 1 + scalar @next_tokens ); $self->output( \@output ); return; } else { return $token->content; } } no Moose; __PACKAGE__->meta->make_immutable; 1; __END__ =head1 NAME Acme::PlayCode - Code transforming to avoid typical typing mistakes =head1 SYNOPSIS use Acme::PlayCode; my $app = new Acme::PlayCode; $app->load_plugin('DoubleToSingle'); $app->load_plugin('ExchangeCondition'); my $played_code = $app->play( $code ); # or my $played_code = $app->play( $filename ); # or $app->play( $filename, { rewrite_file => 1 } ); # override $filename with played code =head1 ALPHA WARNING L is still in its infancy. No fundamental changes are expected, but nevertheless backwards compatibility is not yet guaranteed. =head1 DESCRIPTION It aims to change the code to be better (to be worse if you want). More description and API detais will come later. =head1 PLUGINS =over 4 =item L load all plugins we found. =item L Play code with Single and Double =item L Play code with exchanging condition =item L Play code with printing comma =item L Play code with plus number =back =head1 SEE ALSO L, L, L =head1 AUTHOR Fayland Lam, C<< >> =head1 ACKNOWLEDGEMENTS The L Team. Jens Rehsack, for the description (RT 53680) =head1 COPYRIGHT & LICENSE Copyright 2008 Fayland Lam, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut