Skip to the content.

This project is an exploration of program translation using compiler design techniques. Given a syntactically correct code written in Object Oriented Perl the tool developed here programmatically generates an equivalent code in Python. The Python code generated is logically identical to the input Perl code and given any input, both codes produce identical outputs. The project is implemented in python with the help of the python lex and yacc library (PLY).

Perl concepts covered

To understand the difficulty in translation, we must first take into consideration the peculiarities of perl. Namely, the concepts of blessing , special variables, confusing aspects of scoping (local, global and my) proved to be challenging to translate as there is no one to one equivalent of aforementioned concepts in python. However with intelligent parsing, perfect translation of these concepts were achieved.
The following have been covered in the scope of this project.
  1. Packages
  2. Arithmetic, relational and logical expressions
  3. Loops
  4. Hashes
  5. Subroutines
  6. Constructors, Blessing
  7. Scoping (local, global and my)
  8. Inheritance
Perl code using / implementing these concepts could successfully be translated to equivalent python code.

Results

The following are the results of a few input programs the tool was tested on.

Example 1

Printing the fibonacci series - demonstrating loops.

Input: Perl Code

fibonacci.pl
  1. print "12 terms of the fibonacci series\n";
  2. $a = 0 ;
  3. $b = 1 ;
  4. print "$a $b ";
  5. for ( $i = 2; $i <= 12; $i++)
  6. {
  7.    $c = $a + $b;
  8.    print "$c ";
  9.    $a = $b ;
  10.    $b = $c ;
  11. }
  12. print "\n";

Output: Python Code

fibonacci.py
  1. str_ = lambda x: '' if x == None else str(x)
  2. a = None
  3. b = None
  4. c = None
  5. print( "12 terms of the fibonacci series\n", end='', sep='' )
  6. a = 0
  7. b = 1
  8. print( str_(a) + " " + str_(b) + " ", end='', sep='' )
  9. for i in range (2, 13, 1):
  10.         c = a + b
  11.         print( str_(c) + " ", end='', sep='' )
  12.         a = b
  13.         b = c
  14. print( "\n", end='', sep='' )

Perl Code Output

12 terms of the fibonacci series
0 1 1 2 3 5 8 13 21 34 55 89 144

Python Code Output

12 terms of the fibonacci series
0 1 1 2 3 5 8 13 21 34 55 89 144

Example 2

Getting the area of a rectangle. Demonstrates translation packages, expressions, blessing and @INC.

Server


Input: Perl Code

server.pm
  1. package rect;
  2. sub new
  3. {
  4.     my $class = shift;
  5.     my $self =
  6.     {
  7.         length => shift,
  8.         breadth => shift
  9.     };
  10.     bless($self, $class);
  11.     return $self;
  12. }
  13. sub area
  14. {
  15.     my $self = shift;
  16.     return $self->{length} * $self->{breadth};
  17. }
  18. sub perimeter
  19. {
  20.     my $self = shift;
  21.     return $self->{length} * 2 + $self->{breadth} * 2;
  22. }
  23. 1;

Output: Python Code

server.py
  1. class rect :
  2.     
  3.     def __init__(self,*argv) :
  4.         arg_list = list(argv)[::-1]
  5.         self.__dict__.update({'length':arg_list.pop(),'breadth':arg_list.pop()})
  6.     
  7.     def area (self,*argv) :
  8.         arg_list = list(argv)[::-1]
  9.         return(self.length * self.breadth)
  10.         
  11.     
  12.     def perimeter (self,*argv) :
  13.         arg_list = list(argv)[::-1]
  14.         return(self.length * 2 + self.breadth * 2)
  15.         

Client


Input: Perl Code

client.pl
  1. # !/usr/local/bin/perl -w -I E:\Rachana\extra\Refactoring_oo_perl\testing\input
  2. use lib 'E:\\Rachana\\extra\\Refactoring_oo_perl\\testing\\input\\';
  3. use server::rect;
  4.  
  5. $x=10;
  6. $y=15;
  7. print "Constructing a rectangle of length $x and breadth $y \n";
  8.  
  9. #Constructing a rectangle of length $x and breadth $y
  10. $d = rect->new($x,$y);
  11. $d->area();
  12.  
  13. #area and perimeter
  14. $area = $d->area();
  15. $perimeter = $d->perimeter();
  16.  
  17. print "A rectangle of length $x and breadth $y \nHas area = $area \t perimeter = $perimeter \n";
  18.  
  19.  

Output: Python Code

client.py
  1. str_ = lambda x: '' if x == None else str(x)
  2. area = None
  3. perimeter = None
  4. x = None
  5. y = None
  6. # !/usr/local/bin/perl -w -I E:\Rachana\extra\Refactoring_oo_perl\testing\input
  7. import sys
  8. sys.path.insert(0, 'E:\\Rachana\\extra\\Refactoring_oo_perl\\testing\\input\\')
  9. import server.rect as rect
  10. x = 10
  11. y = 15
  12. print( "Constructing a rectangle of length " + str_(x) + " and breadth " + str_(y) + " \n", end='', sep='' )
  13. #Constructing a rectangle of length $x and breadth $y
  14. d = rect.rect(x,y)
  15. d.area()
  16. #area and perimeter
  17. area = d.area()
  18. perimeter = d.perimeter()
  19. print( "A rectangle of length " + str_(x) + " and breadth " + str_(y) + " \nHas area = " + str_(area) + " \t perimeter = " + str_(perimeter) + " \n", end='', sep='' )

Perl Code Output

Constructing a rectangle of length 10 and breadth 15
A rectangle of length 10 and breadth 15
Has area = 150 perimeter = 50

Python Code Output

Constructing a rectangle of length 10 and breadth 15
A rectangle of length 10 and breadth 15
Has area = 150 perimeter = 50

Example 3

Example demonstrating the global, local and my concept in perl.

Input: Perl Code

scope.pl
  1.  
  2. sub foo
  3. {
  4. print "deleted";
  5. }
  6. sub foo
  7. {
  8. $z=shift;
  9. my $y = 20;
  10. local $z = 30;
  11. print "foo before g : \n x : $x y : $y z : $z \n"; # x: 10 y: 20 z: 30
  12. g();
  13. print "foo after g : \n x : $x y : $y z : $z \n"; #x: 2 y: 20 z: 3
  14. print "foo global : \n y : ", $main::y,"\n"; # y : 2
  15.  
  16. }
  17. sub g
  18. {
  19. print "g : \n x : $x y : $y z : $z \n"; # x: 10 y : z: 30
  20. $x = 2; $y = 2; $z = 3;
  21. }
  22. $x = 10;
  23. print "main before foo : \n x : $x y : $y z : $z \n"; # x: 10 y: z:
  24. foo($x);
  25. print "main after foo : \n x : $x y : $y z : $z \n"; # x : 2 y:2 z :10

Output: Python Code

scope.py
  1. str_ = lambda x: '' if x == None else str(x)
  2. x = None
  3. y = None
  4. z = None
  5. def foo (x , y , z , *argv) :
  6.     arg_list = list(argv)[::-1]
  7.     z = arg_list.pop()
  8.     my_y = 20
  9.     local_z = 30
  10.     print( "foo before g : \n x : " + str_(x) + " y : " + str_(my_y) + " z : " + str_(local_z) + " \n",end='',sep='' )
  11.     # x: 10 y: 20 z: 30
  12.     (x , y , local_z)=g( x , y , local_z )
  13.     print( "foo after g : \n x : " + str_(x) + " y : " + str_(my_y) + " z : " + str_(local_z) + " \n",end='',sep='' )
  14.     #x: 2 y: 20 z: 3
  15.     print( "foo global : \n y : ",y,"\n",end='',sep='' )
  16.     # y : 2
  17.     return( x,y,z )
  18. def g (x , y , z , *argv) :
  19.     arg_list = list(argv)[::-1]
  20.     print( "g : \n x : " + str_(x) + " y : " + str_(y) + " z : " + str_(z) + " \n",end='',sep='' )
  21.     # x: 10 y : z: 30
  22.     x = 2
  23.     y = 2
  24.     z = 3
  25.     return( x,y,z )
  26. x = 10
  27. print( "main before foo : \n x : " + str_(x) + " y : " + str_(y) + " z : " + str_(z) + " \n",end='',sep='' )
  28. # x: 10 y: z:
  29. (x , y , z)=foo( x , y , z , x )
  30. print( "main after foo : \n x : " + str_(x) + " y : " + str_(y) + " z : " + str_(z) + " \n",end='',sep='' )
  31. # x : 2 y:2 z :10

Perl Code Output

main before foo :
x : 10 y : z :
foo before g :
x : 10 y : 20 z : 30
g :
x : 10 y : z : 30
foo after g :
x : 2 y : 20 z : 3
foo global :
y : 2
main after foo :
x : 2 y : 2 z : 10

Python Code Output

main before foo :
x : 10 y : z :
foo before g :
x : 10 y : 20 z : 30
g :
x : 10 y : z : 30
foo after g :
x : 2 y : 20 z : 3
foo global :
y : 2
main after foo :
x : 2 y : 2 z : 10

System Architecture

The translation consists of 3 main stages :

1. Lexing

Perl regex rules are used to tokenize the input code.

2. Parsing

The sequence of tokens passed is matched to Perl grammar in the parsing stage. The parser built works in two stages.
  1. In the first stage of parsing a type of look-up table is constructed. This look up table is uniquely designed to accommodate the intricacies of variable scope in Perl which was mentioned earlier.
  2. In the second stage the parser utilizes the look up table generated previously along with production rules and mapping functions to map the tokens of each Perl statement to its equivalent Python statement. The generated python statement is subsequently added to an intermediate representation which is not unlike an Abstract Syntax Tree (AST).

3. Tree Traversal and Indentation

In the final step the generated Abstract Syntax tree is traversed using depth first traversal. Proper indentation is crucial for python; indentations are set to match the depth of the node containing the statement in the Abstract Syntax Tree.

Applications

The Perl Developer community is shrinking rapidly. The peculiarities of Object Oriented Perl deters new programmers from picking it up while scripting languages like Python are gaining popularity. Despite this, many companies still have a large amount of legacy Perl code in their code base whose maintenance is becoming tedious. One of the possible applications of this tool could be to translate existing perl code to more maintainable python.

User Interface