next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Comprobando el Analizador Léxico Sup: La Estructura de los Ant: Práctica: Números de Línea, Err: Si hallas una errata ...


Pruebas para el Analizador Léxico

Queremos separar/aislar las diferentes fases del compilador en diferentes módulos.

Módulo PL::Error

Para ello comenzamos creando un módulo conteniendo las rutinas de tratamiento de errores:

lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ pwd
/home/lhp/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ cat -n Error.pm
   1  package Error;
   2  use strict;
   3  use warnings;
   4  use Carp;
   5
   6  require Exporter;
   7
   8  our @ISA = qw(Exporter);
   9  our @EXPORT = qw( error fatal);
  10  our $VERSION = '0.01';
  11
  12  sub error {
  13    my $msg = join " ", @_;
  14    if (!$PL::Tutu::errorflag) {
  15      carp("Error: $msg\n");
  16      $PL::Tutu::errorflag = 1;
  17    }
  18  }
  19
  20  sub fatal {
  21    my $msg = join " ", @_;
  22    croak("Error: $msg\n");
  23  }
Observa como accedemos a la variable errorflag del paquete PL::Tutu. Para usar este módulo desde PL::Tutu, tenemos que declarar su uso:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ cat -n Tutu.pm | head -8
     1  package PL::Tutu;
     2
     3  use 5.008004;
     4  use strict;
     5  use warnings;
     6  use IO::File;
     7  use Carp;
     8  use PL::Error;
En la línea 8 hacemos use PL::Error y no use Error ya que el módulo lo hemos puesto en el directorio PL. No olvides hacer make manifest para actualizar el fichero MANIFEST.

Módulo PL::Lexical::Analysis

Supongamos que además de modularizar el grupo de rutinas de tratamiento de errores queremos hacer lo mismo con la parte del análisis léxico. Parece lógico que el fichero lo pongamos en un subdirectorio de PL/ por lo que cambiamos el nombre del módulo a PL::Lexical::Analysis quedando la jerarquía de ficheros asi:

lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ tree
.
|-- Error.pm
|-- Lexical
|   `-- Analysis.pm
`-- Tutu.pm

Por supuesto debemos modificar las correspondientes líneas en Tutu.pm:

 1 package PL::Tutu;
 2 
 3 use 5.008004;
 4 use strict;
 5 use warnings;
 6 use IO::File;
 7 use Carp;
 8 use PL::Error;
 9 use PL::Lexical::Analysis;
10 ...
11 
12 sub compile {
13   my ($input) = @_;
14   local %symbol_table = ();
15   local $data = ""; # Contiene todas las cadenas en el programa fuente
16   local $target = ""; # target code
17   my @tokens = ();
18   local $errorflag = 0;
19   local ($lookahead, $value) = ();
20   local $tree = undef; # abstract syntax tree
21   local $global_address = 0;
22 
23   
24   ########lexical analysis
25   @tokens = &PL::Lexical::Analysis::scanner($input);
26   print "@tokens\n";
27 
28   ...
29 
30   return \$target;
31 }
Observe que ahora PL::Lexical::Analysis::scanner devuelve ahora la lista con los terminales y que @tokens se ha ocultado en compile como una variable léxica (línea 17). En la línea 26 mostramos el contenido de la lista de terminales.

Sigue el listado del módulo conteniendo el analizador léxico. Obsérve las líneas 6, 16 y 44.

lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL/Lexical$ cat -n Analysis.pm
     1  # Lexical analyzer
     2  package PL::Lexical::Analysis;
     3  use strict;
     4  use warnings;
     5  use Carp;
     6  use PL::Error;
     7
     8  require Exporter;
     9
    10  our @ISA = qw(Exporter);
    11  our @EXPORT = qw( scanner );
    12  our $VERSION = '0.01';
    13
    14  sub scanner {
    15    local $_ = shift;
    16    my @tokens;
    17
    18    { # Con el redo del final hacemos un bucle "infinito"
    19      if (m{\G\s*(\d+)}gc) {
    20        push @tokens, 'NUM', $1;
    21      }
    ..      ...
    37      elsif (m{\G\s*([+*()=;,])}gc) {
    38        push @tokens, 'PUN', $1;
    39      }
    40      elsif (m{\G\s*(\S)}gc) {
    41        Error::fatal "Caracter invalido: $1\n";
    42      }
    43      else {
    44        return @tokens;
    45      }
    46      redo;
    47    }
    48  }

El Programa Cliente

Puesto que en el paquete PL::Lexical::Analysis exportamos scanner no es necesario llamar la rutina por el nombre completo desde compile. Podemos simplificar la línea en la que se llama a scanner que queda así:

########lexical analysis
@tokens = &scanner($input);
print "@tokens\n";
De la misma forma, dado que PL::Tutu exporta la función compile_from_file, no es necesario llamarla por su nombre completo desde el guión tutu. Reescribimos la línea de llamada:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/scripts$ cat tutu
#!/usr/bin/perl -w
use lib ('../lib');
use PL::Tutu;

&compile_from_file(@ARGV);

Actualización del MANIFEST

Como siempre que se añaden o suprimen archivos es necesario actualizar MANIFEST:

lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ make manifest
/usr/bin/perl "-MExtUtils::Manifest=mkmanifest" -e mkmanifest
Added to MANIFEST: lib/PL/Lexical/Analysis.pm
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ cat -n MANIFEST
     1  Changes
     2  lib/PL/Error.pm
     3  lib/PL/Lexical/Analysis.pm
     4  lib/PL/Tutu.pm
     5  Makefile.PL
     6  MANIFEST
     7  MANIFEST.SKIP
     8  README
     9  scripts/test01.tutu
    10  scripts/tutu
    11  scripts/tutu.pl
    12  t/PL-Tutu.t



Subsecciones
next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Comprobando el Analizador Léxico Sup: La Estructura de los Ant: Práctica: Números de Línea, Err: Si hallas una errata ...
Casiano Rodríguez León
2012-05-22