compile
:
1 package PL::Tutu; 2 use 5.008004; # Versión mínima de Perl 5.84 3 use strict; # Variables deben ser declaradas, etc. 4 use warnings; # Enviar warnings 5 use IO::File; 6 use Carp; # Provee alternativas a "die" and "warn" 7 8 require Exporter; 9 10 our @ISA = qw(Exporter); # Heredamos los métodos de la clase Exporter 11 our @EXPORT = qw( compile compile_from_file); # Estas funciones serán exportadas 12 our $VERSION = '0.01'; # Variable que define la versión del módulo 13 14 our %symbol_table; # La tabla de símbolos $symbol_table{x} contiene 15 our $data; # la información asociada con el objeto 'x' 16 our $target; # tipo, dirección, etc. 17 our @tokens; # La lista de terminales 18 our $errorflag; 19 our ($lookahead, $value); # Token actual y su atributo 20 our $tree; # referencia al objeto que contiene 21 our $global_address; # el árbol sintáctico 22 23 # Lexical analyzer 24 package Lexical::Analysis; 25 sub scanner { 26 } 27 28 package Syntax::Analysis; 29 sub parser { 30 } 31 32 package Machine::Independent::Optimization; 33 sub Optimize { 34 } 35 36 package Code::Generation; 37 sub code_generator { 38 } 39 40 package Peephole::Optimization; 41 sub transform { 42 } 43 44 package PL::Tutu; 45 sub compile { 46 my ($input) = @_; # Observe el contexto! 47 local %symbol_table = (); 48 local $data = ""; # Contiene todas las cadenas en el programa fuente 49 local $target = ""; # target code 50 local @tokens =(); # "local" salva el valor que será recuperado al finalizar 51 local $errorflag = 0; # el ámbito 52 local ($lookahead, $value) = (); 53 local $tree = undef; # Referencia al árbol sintÃctico abstracto 54 local $global_address = 0; # Usado para guardar la Ãltima direcciÃn ocupada 55 56 ########lexical analysis 57 &Lexical::Analysis::scanner($input); 58 59 ########syntax (and semantic) analysis 60 $tree = &Syntax::Analysis::parser; 61 62 ########machine independent optimizations 63 &Machine::Independent::Optimization::Optimize; 64 65 ########code generation 66 &Code::Generation::code_generator; 67 68 ########peephole optimization 69 &Peephole::Optimization::transform($target); 70 71 return \$target; #retornamos una referencia a $target 72 } 73 74 sub compile_from_file { 75 my ($input_name, $output_name) = @_; # Nombres de ficheros 76 my $fhi; # de entrada y de salida 77 my $targetref; 78 79 if (defined($input_name) and (-r $input_name)) { 80 $fhi = IO::File->new("< $input_name"); 81 } 82 else { $fhi = 'STDIN'; } 83 my $input; 84 { # leer todo el fichero 85 local $/ = undef; # localizamos para evitar efectos laterales 86 $input = <$fhi>; 87 } 88 $targetref = compile($input); 89 90 ########code output 91 my $fh = defined($output_name)? IO::File->new("> $output_name") : 'STDOUT'; 92 $fh->print($$targetref); 93 $fh->close; 94 1; # El último valor evaluado es el valor retornado 95 } 96 97 1; # El 1 indica que la fase de carga termina con éxito 98 # Sigue la documentación ...
Vamos a añadir un script que use el módulo
PL::Tutu
para asi poder ejecutar nuestro compilador:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/$ mkdir scripts lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ cd scripts/A continuación creamos dos versiones del compilador
tutu.pl
y tutu
y un programa de prueba test01.tutu
:
... # despues de crear los ficheros lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ ls test01.tutu tutu tutu.pl lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ cat tutu.pl #!/usr/bin/perl -w -I../lib/ use PL::Tutu; PL::Tutu::compile_from_file(@ARGV);
El programa tutu
ilustra otra forma de conseguir que el intérprete
Perl busque por la librería que está siendo desarrollada, mediante el
uso de use lib
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ cat tutu #!/usr/bin/perl -w use lib ('../lib'); use PL::Tutu; &PL::Tutu::compile_from_file(@ARGV);
Una tercera forma (la que recomiendo):
$ export PERL5LIB=~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/lib $ perl -MPL::Tutu -e 'PL::Tutu::compile_from_file("test01.tutu")'
Ahora tenemos que añadir estos ficheros en MANIFEST
para que formen
parte del proyecto. En vez de eso lo que podemos hacer es crear un fichero
MANIFEST.SKIP
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ cat MANIFEST.SKIP \.o$ ^\.cvsignore$ /\.cvsignore$ \.cvsignore$ CVS/[^/]+$ \.svn\b ^Makefile$ /Makefile$ ^blib/ \.swp$ \.bak$ \.pdf$ \.ps$ \.sal$ pm_to_blib \.pdf$ \.tar.gz$ \.tgz$ ^META.yml$Ahora al hacer
make manifestse crea un fichero
MANIFEST
que contiene los caminos
relativos de todos los ficheros
en la jerarquía cuyos nombres no casan con una de las expresiones
regulares en MANIFEST.SKIP
.
Para saber mas sobre MANIFEST
léa
[10].
No recomiendo el uso de MANIFEST.SKIP
. Prefiero un control manual de los
ficheros que integran la aplicacion.
Es necesario indicarle a Perl que los ficheros añadidos son ejecutables.
Esto se hace mediante el parámetro EXE_FILES
de WriteMakefile
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ cat Makefile.PL use 5.008004; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'PL::Tutu', VERSION_FROM => 'lib/PL/Tutu.pm', # finds $VERSION PREREQ_PM => {}, # e.g., Module::Name => 1.1 EXE_FILES => [ 'scripts/tutu.pl', 'scripts/tutu' ], ($] >= 5.005 ? ## Add these new keywords supported since 5.005 (ABSTRACT_FROM => 'lib/PL/Tutu.pm', # retrieve abstract from module AUTHOR => 'Lenguajes y Herramientas de Programacion <lhp@>') : ()), );Perl utilizará esa información durante la fase de instalación para instalar los ejecutables en el path de búsqueda.
A continuación hay que rehacer el Makefile
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ perl Makefile.PL Writing Makefile for PL::Tutu
Para crear una versión funcional hacemos make
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ make cp scripts/tutu blib/script/tutu /usr/bin/perl "-MExtUtils::MY" -e "MY->fixin(shift)" blib/script/tutu cp scripts/tutu.pl blib/script/tutu.pl /usr/bin/perl "-MExtUtils::MY" -e "MY->fixin(shift)" blib/script/tutu.pl Manifying blib/man3/PL::Tutu.3pm
Para crear el MANIFEST
hacemos make manifest
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ make manifest /usr/bin/perl "-MExtUtils::Manifest=mkmanifest" -e mkmanifest Added to MANIFEST: scripts/tutu
Comprobemos que el test de prueba generado automáticamente por
h2xs
se pasa correctamente:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/PL-Tutu....ok All tests successful. Files=1, Tests=1, 0 wallclock secs ( 0.08 cusr + 0.00 csys = 0.08 CPU)
Podemos ahora ejecutar los guiones:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ cd scripts/ lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ ls -l total 12 -rw-r--r-- 1 lhp lhp 15 2005-09-29 12:56 test01.tutu -rwxr-xr-x 1 lhp lhp 92 2005-09-29 13:29 tutu -rwxr-xr-x 1 lhp lhp 80 2005-09-29 12:58 tutu.pl lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ tutu test01.tutu test01.sal lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ ls -l total 12 -rw-r--r-- 1 lhp lhp 0 2005-09-29 13:53 test01.sal -rw-r--r-- 1 lhp lhp 15 2005-09-29 12:56 test01.tutu -rwxr-xr-x 1 lhp lhp 92 2005-09-29 13:29 tutu -rwxr-xr-x 1 lhp lhp 80 2005-09-29 12:58 tutu.pl
Veamos los contenidos del programa fuente test01.tutu
que usaremos para hacer una prueba:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu/scripts$ cat test01.tutu int a,b; a = 4
Para hacer una distribución instalable hacemos make dist
:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ make dist rm -rf PL-Tutu-0.01 /usr/bin/perl "-MExtUtils::Manifest=manicopy,maniread" \ -e "manicopy(maniread(),'PL-Tutu-0.01', 'best');" mkdir PL-Tutu-0.01 mkdir PL-Tutu-0.01/scripts mkdir PL-Tutu-0.01/lib mkdir PL-Tutu-0.01/lib/PL mkdir PL-Tutu-0.01/t tar cvf PL-Tutu-0.01.tar PL-Tutu-0.01 PL-Tutu-0.01/ PL-Tutu-0.01/scripts/ PL-Tutu-0.01/scripts/test01.tutu PL-Tutu-0.01/scripts/tutu PL-Tutu-0.01/scripts/tutu.pl PL-Tutu-0.01/META.yml PL-Tutu-0.01/Changes PL-Tutu-0.01/MANIFEST PL-Tutu-0.01/lib/ PL-Tutu-0.01/lib/PL/ PL-Tutu-0.01/lib/PL/Tutu.pm PL-Tutu-0.01/MANIFEST.SKIP PL-Tutu-0.01/t/ PL-Tutu-0.01/t/PL-Tutu.t PL-Tutu-0.01/Makefile.PL PL-Tutu-0.01/README rm -rf PL-Tutu-0.01 gzip --best PL-Tutu-0.01.tarDespués de esto tenemos en el directorio de trabajo el fichero
PL-Tutu-0.01.tar.gz
con la distribución:
lhp@nereida:~/Lperl/src/topdown/PL0506/02fases/PL-Tutu$ ls -ltr total 72 drwxr-xr-x 2 lhp lhp 4096 2005-09-29 12:01 t -rw-r--r-- 1 lhp lhp 1196 2005-09-29 12:01 README drwxr-xr-x 3 lhp lhp 4096 2005-09-29 12:01 lib -rw-r--r-- 1 lhp lhp 152 2005-09-29 12:01 Changes -rw-r--r-- 1 lhp lhp 167 2005-09-29 13:23 MANIFEST.SKIP -rw-r--r-- 1 lhp lhp 0 2005-09-29 13:23 pm_to_blib drwxr-xr-x 6 lhp lhp 4096 2005-09-29 13:23 blib -rw-r--r-- 1 lhp lhp 113 2005-09-29 13:23 MANIFEST.bak drwxr-xr-x 2 lhp lhp 4096 2005-09-29 13:29 scripts -rw-r--r-- 1 lhp lhp 616 2005-09-29 13:49 Makefile.PL -rw-r--r-- 1 lhp lhp 20509 2005-09-29 13:51 Makefile -rw-r--r-- 1 lhp lhp 3654 2005-09-29 16:34 PL-Tutu-0.01.tar.gz -rw-r--r-- 1 lhp lhp 298 2005-09-29 16:34 META.yml -rw-r--r-- 1 lhp lhp 205 2005-09-29 16:34 MANIFEST
package nombredepaquete
?
use 5.008004
?
use strict
?
use warnings
?
use warnings
y perl -w
?
use Carp
?
¿Que diferencia hay entre croak
y die
?
our
?
local
?
%h
de clave k
?
@a
de índice 7
?
¿Que lugar ocupa ese elemento en el array?
undef
?
my ($input) = @_;y
my $input = @_;Repase [10].
use
y require
?
¿La línea require Exporter
se ejecuta en tiempo de compilación
o en tiempo de ejecución?
our @ISA = qw(Exporter)
?. Repáse
[10].
our @EXPORT = qw( compile compile_from_file)
?
EXPORT
y EXPORT_OK
?.
Repase
[10].
our $VERSION = '0.01
?
=>
?.
Repase
[10].
MANIFEST.SKIP
?
¿Que hace make manifest
?
-I
?
¿Porqué la primera línea de tutu.pl
comienza:
#!/usr/bin/perl -w -I../lib/
?
lib
?
¿Qué hace la línea use lib ('../lib')
en el programa tutu
?
PERL5LIB
?
-r $input_name
en la línea 79?
Repase
[10].
$/
?
¿Que se leerá en la línea 86
85 local $/ = undef; 86 my $input = <$fhi>;
\
? ¿Que relación hay entre \$target
y $target
?.
$targetref
es una referencia a la cadena que va a contener el
código objeto, ¿Cómo se denota a la cadena referenciada por $targetref
?
Explique la línea
92 $fh->print($$targetref);
PL::Tutu
con las funciones de compilación y los correspondientes
guiones de compilación.
Mejore el script tutu
para que acepte opciones desde la línea de comandos.
Debera soportar al menos las siguientes opciones:
--usage
Muestra de forma concisa el comando de uso
--help
Un resumen de cada opción disponible
--version
Muestra la versión del programa
--man
Muestra la documentación
Use para ello el módulo Getopt::Long .
Este módulo provee la función GetOptions la cual
se atiene a los estándares de especificación
de opciones en la línea de comandos POSIX y GNU.
Esta función soporta el uso del guión doble --
y el simple así como admitir el prefijo mas corto
que deshace la ambiguedad entre las diferentes opciones.
La llamada a
GetOptions analiza la línea de comandos en
ARGV
inicializa la variable asociada de manera adecuada. Retorna un valor verdadero
si la línea de comandos pudo ser procesada con
En caso contrario emitirá
un mensaje de error y devolverá falso. Recuerde hacer
perldoc Getopt::Long
para obtener información mas detallada
El siguiente ejemplo ilustra el uso de Getopt::Long
.
Se hace uso también del módulo (función pod2usage en la línea 63)
Pod::Usage
el cual permite la documentación empotrada.
nereida:~/LEyapp/examples> cat -n treereg 1 #!/usr/bin/perl -w 2 use strict; 3 use Parse::Eyapp::YATW; 4 use Parse::Eyapp::Node; 5 use Parse::Eyapp::Treeregexp; 6 use Carp; 7 use Getopt::Long; 8 use Pod::Usage; 9 10 my $infile; 11 my $outfile; 12 my $packagename; 13 my $prefix = ''; 14 my $syntax = 1; 15 my $numbers = 1; 16 my $severity = 0; # 0 = Don't check arity. 1 = Check arity. 17 # 2 = Check arity and give a warning 3 = ... and croak 18 GetOptions( 19 'in=s' => \$infile, 20 'out=s' => \$outfile, 21 'mod=s' => \$packagename, 22 'prefix=s' => \$prefix, 23 'severity=i' => \$severity, 24 'syntax!' => \$syntax, 25 'numbers!' => \$numbers, 26 'version' => \&version, 27 'usage' => \&usage, 28 'help' => \&man, 29 ) or croak usage(); 30 31 # If an argument remains is the inputfile 32 ($infile) = @ARGV unless defined($infile); 33 die usage() unless defined($infile); 34 35 my $treeparser = Parse::Eyapp::Treeregexp->new( 36 INFILE => $infile, 37 OUTFILE => $outfile, 38 PACKAGE => $packagename, 39 PREFIX => $prefix, 40 SYNTAX => $syntax, 41 NUMBERS => $numbers, 42 SEVERITY => $severity 43 ); 44 45 $treeparser->generate(); 46 47 sub version { 48 print "Version $Parse::Eyapp::Treeregexp::VERSION\n"; 49 exit; 50 } 51 52 sub usage { 53 print <<"END_ERR"; 54 Supply the name of a file containing a tree grammar (.trg) 55 Usage is: 56 treereg [-m packagename] [[no]syntax] [[no]numbers] [-severity 0|1|2|3] \ 57 [-p treeprefix] [-o outputfile] -i filename[.trg] 58 END_ERR 59 exit; 60 } 61 62 sub man { 63 pod2usage( 64 -exitval => 1, 65 -verbose => 2 66 ); 67 } 68 __END__ 69 70 =head1 SYNOPSIS 71 72 treereg [-m packagename] [[no]syntax] [[no]numbers] [-severity 0|1|2|3] \ 73 [-p treeprefix] [-o outputfile] -i filename[.trg] 74 treereg [-m packagename] [[no]syntax] [[no]numbers] [-severity 0|1|2|3] \ 75 [-p treeprefix] [-o outputfile] filename[.trg] 76 ... # Follows the documentation bla, bla, bla
Ahora podemos ejecutar el guión de múltiples formas:
nereida:~/LEyapp/examples> treereg -nos -nonu -se 3 -m Tutu Foldonly1.trg nereida:~/LEyapp/examples> treereg -nos -nonu -s 3 -m Tutu Foldonly1.trg Option s is ambiguous (severity, syntax) nereida:~/LEyapp/examples> treereg -nos -bla -nonu -m Tutu Foldonly1.trg Unknown option: bla nereida:~/LEyapp/examples>
La librería estandar de Perl incluye el módulo Getopt::Long
.
No es el caso de Pod::Usage
. Descarge el módulo e instalelo
en un directorio local en el que tenga permisos. Si es preciso repase las secciones
[10] y
[10] de los apuntes de
introducción a Perl.
Casiano Rodríguez León