next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Condiciones de arranque Sup: Construcción de Analizadores Léxicos Ant: Construcción usando la opción Err: Si hallas una errata ...

La clase Parse::Lex

La clase Parse::Lex nos permite crear un analizador léxico. La estrategia seguida es mover el puntero de búsqueda dentro de la cadena a analizar utilizando conjuntamente el operador pos() y el ancla \G.

> cat -n tokenizer.pl
 1  #!/usr/local/bin/perl
 2
 3  require 5.004;
 4  #BEGIN {  unshift @INC, "../lib"; }
 5
 6  $^W = 0;
 7  use Parse::Lex;
 8  print STDERR "Version $Parse::ALex::VERSION\n";
 9
10  @token = (
11            qw(
12               ADDOP    [-+]
13               LEFTP    [\(]
14               RIGHTP   [\)]
15               INTEGER  [1-9][0-9]*
16               NEWLINE  \n
17              ),
18            qw(STRING),   [qw(" (?:[^"]+|"")* ")],
19            qw(ERROR  .*), sub {
20              die qq!can\'t analyze: "$_[1]"\n!;
21            }
22           );
23
24  Parse::Lex->trace;
25  $lexer = Parse::Lex->new(@token);
26
27  $lexer->from(\*DATA);
28  print "Tokenization of DATA:\n";
29
30  TOKEN:while (1) {
31    $token = $lexer->next;
32    if (not $lexer->eoi) {
33      print "Record number: ", $lexer->line, "\n";
34      print "Type: ", $token->name, "\t";
35      print "Content:->", $token->text, "<-\n";
36    } else {
37      last TOKEN;
38    }
39  }
40
41  __END__
42  1+2-5
43  "This is a multiline
44  string with an embedded "" in it"
45  this is an invalid string with a "" in it"
La línea 3 usa la palabra reservada require la cual normalmente indica la existencia de una dependencia. Cuando se usa como:

require "file.pl";
simplemente carga el fichero en nuestro programa. Si se omite el sufijo, se asume .pm. Si el argumento, como es el caso, es un número o un número de versión, se compara el número de versión de perl (como se guarda en $]) y se compara con el argumento. si es mas pequeño, se aborta la ejecución.

La línea 4, aunque comentada, contiene una definición del incializador BEGIN. Si aparece, este código será ejecutado tan pronto como sea posible. el intérprete Perl busca los módulos en uno de varios directorios estándar contenidos en la variable de entorno PERL5LIB. Esa lista esta disponible en un programa Perl a través de la variable @INC. Recuerda que el compilador sustituye cada :: por el separador de caminos. Asi la orden: unshift @INC, "../lib"; coloca este directorio entre los que Perl realiza la búsqueda por módulos. En mi máquina los mósulos de análisis léxico están en:

> locate Lex.pm
/usr/local/lib/perl5/site_perl/5.8.0/Parse/ALex.pm
/usr/local/lib/perl5/site_perl/5.8.0/Parse/CLex.pm
/usr/local/lib/perl5/site_perl/5.8.0/Parse/Lex.pm
/usr/local/lib/perl5/site_perl/5.8.0/Parse/YYLex.pm
La línea 6 establece a 0 la variable $^W o $WARNING la cual guarda el valora actual de la opción de warning, si es cierto o falso.

La línea 7 indica que hacemos uso de la clase Parse::Lex y la línea 8 muestra el número de versión. La clase ALex es la clase abstracta desde la que heredan las otras. Si editas el módulo ALex.pm encontrarás el siguiente comentario que aclara la jerarquía de clases:

# Architecture:
#           Parse::ALex - Abstract Lexer
#                |
#           +----------+
#           |          |
#           |     Parse::Tokenizer
#           |        |     |
#        LexEvent   Lex  CLex  ...       - Concrete lexers
en ese mismo fichero encontrarás la declaración de la variable:
$Parse::ALex::VERSION = '2.15';
Las líneas de la 10 a la 22 definen el array @token:
10  @token = (
11            qw(
12               ADDOP    [-+]
13               LEFTP    [\(]
14               RIGHTP   [\)]
15               INTEGER  [1-9][0-9]*
16               NEWLINE  \n
17              ),
18            qw(STRING),   [qw(" (?:[^"]+|"")* ")],
19            qw(ERROR  .*), sub {
20              die qq!can\'t analyze: "$_[1]"\n!;
21            }
22           );
Esto da lugar a un array de la forma:
"ADDOP", "[-+]", 
"LEFTP", "[(]", 
"RIGHTP", "[)]", 
"INTEGER", "[1-9][0-9]*", 
"NEWLINE", "\n", 
"STRING", ARRAY(0x811f55c),
"ERROR", ".*", CODE(0x82534b8)
Observa que los elementos 11 y 14 son referencias. El primero a un array anónimo conteniendo la expresión regular "(?:[^"]+"")*"| para las STRING y el segundo es una referencia a la subrutina anónima que muestra el mensaje de error.

La línea 24 llama al método trace como método de la clase, el cual activa el modo ``traza''. La activación del modo traza debe establecerse antes de la creación del analizador léxico. Es por esto que precede a la llamada $lexer = Parse::Lex->new(@token) en la línea 25. Se puede desactivar el modo traza sin mas que hacer una segunda llamada a este método. El método admite la forma trace OUTPUT, siendo OUTPUT un nombre de fichero o un manipulador ed ficheros. En este caso la traza se redirije a dicho fichero.

La llamada al método from en la línea 27 establece la fuente de los datos a ser analizados. el argumento de este método puede ser una cadena o una lista de cadenas o una referencia a un manipulador de fichero. En este caso la llamada $lexer->from(\*DATA) establece que la entrada se produce dede el manipulador de ficheros especial DATA, el cual se refiere a todo el texto que sigue al token __END__ en el fichero que contiene el guión Perl.

La llamada al método next en la línea 31 fuerza la búsqueda por el siguiente token. Devuelve un objeto de la clase Parse::Token. Existe un token especial, Token::EOI| que indica el final de los datos. En el ejemplo, el final se detecta a través del método eoi.

El método line es usado para devolver el número de línea del registro actual.

Los objetos token son definidos mediante la clase Parse::Token, la cual viene con Parse::Lex. la definición de un token normalmente conlleva un nombre simbólico (por ejemplo, INTEGER), seguido de una expresión regular. Se puede dar como tercer argumento una referencia a una subrutina anónima, en cuyo caso la subrutina es llamada cada vez que se reconoce el token. Los argumentos que recibe la subrutina son la referencia al objeto token y la cadena reconocida por la expresión regular. El valor retornado por la subrutina es usado como los nuevos contenidos de la cadena asociada con el objeto token en cuestión. Un objeto token tiene diversos métodos asociados. Asi el método name devuelve el nombre del token. El método text devuelve lac adena de caracteres reconocida por medio del token. Se le puede pasar un parámetro text EXPR en cuyo caso EXPR define la cadena de caracteres asociada con el lexema.

> tokenizer.pl
Version 2.15
Trace is ON in class Parse::Lex
Tokenization of DATA:
[main::lexer|Parse::Lex] Token read (INTEGER, [1-9][0-9]*): 1
Record number: 1
Type: INTEGER   Content:->1<-
[main::lexer|Parse::Lex] Token read (ADDOP, [-+]): +
Record number: 1
Type: ADDOP     Content:->+<-
[main::lexer|Parse::Lex] Token read (INTEGER, [1-9][0-9]*): 2
Record number: 1
Type: INTEGER   Content:->2<-
[main::lexer|Parse::Lex] Token read (ADDOP, [-+]): -
Record number: 1
Type: ADDOP     Content:->-<-
[main::lexer|Parse::Lex] Token read (INTEGER, [1-9][0-9]*): 5
Record number: 1
Type: INTEGER   Content:->5<-
[main::lexer|Parse::Lex] Token read (NEWLINE, \n):

Record number: 1
Type: NEWLINE   Content:->
<-
[main::lexer|Parse::Lex] Token read (STRING, \"(?:[^\"]+|\"\")*\"): "This is a multiline
string with an embedded "" in it"
Record number: 3
Type: STRING    Content:->"This is a multiline
string with an embedded "" in it"<-
[main::lexer|Parse::Lex] Token read (NEWLINE, \n):

Record number: 3
Type: NEWLINE   Content:->
<-
[main::lexer|Parse::Lex] Token read (ERROR, .*): this is an invalid string with a "" in it"
can't analyze: "this is an invalid string with a "" in it""



Subsecciones
next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Condiciones de arranque Sup: Construcción de Analizadores Léxicos Ant: Construcción usando la opción Err: Si hallas una errata ...
Casiano Rodríguez León
2012-05-22