#!/usr/bin/perl
# --------------------------------------------------
# This script converts p5 source to p6. Currently,
# only regexp matching and substitution are used, and
# this brute-force, simplistic, and extremely foolish
# script, ignoring deeper context, needs lots of
# improvement. Due to regexp's limits, it is only
# expected to serve as a tool to somewhat speed up
# the rewrite of existing p5 programs instead of doing
# full conversions. :-/
#
# c.f. Mini HowTo: How to port Perl 5 modules to Perl 6:
# http://perlmonks.org/index.pl?node_id=442402
#
# --------------------------------------------------
# Author: Yung-chung Lin (xern@cpan.org)
# --------------------------------------------------
use strict;
use IO::All;
use Regexp::Common;
use Data::Dumper;
# --------------------------------------------------
# Regexps
# --------------------------------------------------
our $re_sigil = '[\$\@\%]';
our $re_symbol = '\w[\w\d]*';
our $re_condition = '(?:if|elsif|else|unless|while|unless)';
our $re_reserved = "(?:$re_condition)";
our $re_varname = $re_sigil.$re_symbol;
our $re_comma = '(?:,|=>)';
our $re_scopeop = '(?:my|our)';
# conv_foreach is moved out of CONVERSION_SUBS section
# temporarily for the use of $RE{balanced}{-parens=>'{}'};
sub conv_foreach {
$_ = shift;
my $foreach_kw = 'for(?:each)?';
s/${foreach_kw}\s*${re_scopeop}?\s+(${re_varname})\s*\(((?:\@${re_symbol}\s*,\s*)*\@${re_symbol})\)\s*\{/for $2 -> $1 {/g;
s/${foreach_kw}\s*\(((?:\@${re_symbol}\s*,\s*)*\@${re_symbol})\)\s*\{/for $1 {/g;
$_
}
my $conv = <<'CONVERSION_SUBS';
conv_array {
s/\$(${re_symbol}\[.+])/\@$1/g;
s/\$#(${re_symbol})/\@$1.last/g; # last_index
}
conv_hash {
s/\$(${re_symbol}\{.+?\})/%$1/g;
}
conv_reference {
s/\$(${re_symbol})->(\[.+?])/\$$1$2/g;
s/\$(${re_symbol})->(\{.+?\})/\$$1$2/g;
s/\$(${re_symbol})->(\(.+?\))/\$$1$2/g;
s/(${re_varname})\s*=\s*\\(${re_varname})/$1 = $2/g;
}
conv_self {
my $sub_context;
# Check if it's now in 'sub' context
if(/\bsub\s*(${re_symbol}\s*)?($RE{balanced}{-parens=>'{}'})/){
my ($pre, $post) = ($`, $');
my ($n, $c) = ($1, $2);
my (@s, @e) = (@-, @+);
# print Dumper \@s, \@e;
# foreach my $i (0..$#s){
# print "$i >> ", substr($_, $n[$i], $e[$i] - $s[$i]),$/;
# }
if($n !~ /${re_reserved}/){
$c =~ s/\$self->{(${re_symbol})}/\$.$1/g;
$c =~ s/\$self->/\$./g;
}
$_ = $` . 'sub ' . $n . $c . $';
}
}
conv_package {
s/package\s+((?:${re_symbol}::)*${re_symbol})\s*;/class $1;/g;
}
conv_open {
s/open (${re_scopeop}?\s*${re_varname})\s*${re_comma}\s*($RE{quoted})/$1 = open $2/g;
# s/open (\${re_varname})\s*${re_comma}\s*($RE{quoted})/$1 = open $2/g;
}
conv_condition {
if(/(${re_condition})\s*($RE{balanced}{-parens=>'()'})\s*($RE{balanced}{-parens=>'{}'})/){
my ($c, $t, $p) = ($1, $2, $3);
$t =~ s/^\((.+)\)$/$1/;
$_ = "$c $t $p";
}
}
CONVERSION_SUBS
my $newconv;
$conv =~ s/^\s*#.+$//g;
while($conv =~ /(conv_\w+)\s*($RE{balanced}{-parens=>'{}'})\n/sg){
my ($n, $c) = ($1, $2);
$c =~ s/^\{[\s\n]*(.+)\}/$1/s;
$newconv .= <<".";
sub $n {
\$_ = shift;
$c;
\$_;
}
.
}
$conv = $newconv;
eval $conv;
die $@ if $@;
my $src;
my $srcfile;
my $DEBUG;
if(!@ARGV || $ARGV[0] eq '-h'){
print( <<'HELP') and exit;
<< USAGE >>
% p526.pl # Convert from p5 code to p6 code
-c # Show conversion rules
-e # Convert one-liner
-s # List supported features
-d # Dump debugging information
p5_source.pl # Convert the file. The script appends
# '.p6' to the original name
HELP
}
while(my $arg = shift @ARGV){
if($arg eq '-e'){
$src = shift(@ARGV) or die "Enter a one-liner";
}
elsif($arg eq '-d'){
$DEBUG = 1;
}
elsif ($arg eq '-c'){
no strict;
print "---- Regexps ----\n";
print map{ "$_ => ${$_}\n" } sort grep { /patt/ } keys %main::;
print "\n---- Conversion subs ----\n";
print $conv;
}
elsif ($arg eq '-s'){
print <<'SUPPORTED_FEATURES' and exit;
<< SUPPORTED FEATURES >>
$v[0] --> @v[0];
$v{0} --> %v{0};
$#v --> @v.last_index;
$r = \@v --> $r = @v;
$r = \%v --> $r = %v;
$r->[0] --> $r[0];
$r->{0} --> $r{0};
$r->(0) --> $r(0);
foreach (@a) --> for @a
foreach my $r (@a) --> for @a -> $r
package MY::Class; --> class MY::Class;
open $f, 'file'; --> $f = open 'file';
open my $f, 'file'; --> my $f = open 'file';
open our $f, 'file'; --> our $f = open 'file';
sub { $self->blah } --> sub { $.blah };
sub my_sub { $self->blah } --> sub my_sub { $.blah };
SUPPORTED_FEATURES
}
else {
$srcfile = $arg;
local $/;
open my $f, $srcfile or die "Could'nt open file $srcfile";
$src = <$f>;
}
}
for my $c (qw(
conv_self conv_array conv_hash conv_reference
conv_foreach conv_package conv_open conv_condition
)){
no strict;
if(main->can($c)){
$src = &{$c}($src);
print "---- ( $c ) ----\n$src\n" if $DEBUG;
}
else {
print "Skipping conversion rule: $c\n";
}
}
if($srcfile){
open my $f, '>', "${srcfile}.p6"
or "Couldn't open file ${srcfile}.p6 for writing";
print {$f} $src;
}
else {
print "$src\n";
}
__END__