Durante esta fase pasamos a asignar un tipo a los nodos del AST. Pueden producirse errores de tipo y es importante que los mensajes de diagnóstico sea precisos. Por ejemplo, dado el programa:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/script> cat -n prueba17.c 1 int c[20][30], d; 2 3 int f(int a, int b) { 4 return 5 (a+b)* 6 d* 7 c[5]; 8 }queremos producir un mensaje de error adecuado como:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/script> usetypes.pl prueba17.c Type Error at line 6: Incompatible types with operator '*'indicando asi que el error se produce en la multiplicación de la línea 6, ya que
c[5]
es de tipo array.
El problema que tenemos a estas alturas de la construcción de nuestro compilador
es que el número de línea asociado con la multiplicación de la línea 6
no figura como atributo del nodo TIMES
: fue obviado por la directiva %tree
ya que el terminal *
fué tratado como un syntactic token.
Afortunadamente Parse::Eyapp
provee un mecanismo para permitir
guardar la información
residente en un terminal sintáctico
en el nodo padre del mismo. Si el programador
provee a la clase TERMINAL de un método
save_attributes dicho método será llamado
durante la construcción del AST en el momento de la
eliminación del terminal:
TERMINAL::save_attributes($terminal, $lhs) |
El primer argumento es la referencia al nodo TERMINAL
y el segundo al node padre del terminal.
Por tanto, para resolver el problema de conocer
el número de línea en el que ocurre un operador,
proveeremos a los nodos TERMINAL
del siguiente método save_attributes
:
620 sub TERMINAL::save_attributes { 621 # $_[0] is a syntactic terminal 622 # $_[1] is the father. 623 push @{$_[1]->{lines}}, $_[0]->[1]; # save the line! 624 }
La subrutina de mensajes de error de tipo type_error
la llamaremos proporcionando el mensaje y el número de línea.
Sigue un ejemplo de llamada:
type_error("Incompatible types with operator '".($_[0]->lexeme)."'", $_[0]->line);El método
line
para este tipo de nodos tiene la forma:
pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \ sed -ne '/sub P.*ne/,/^)/p' Types.eyp | cat -n 1 sub PLUS::line { 2 $_[0]->{lines}[0] 3 } 4 5 insert_method( 6 qw{TIMES DIV MINUS ASSIGN GT IF RETURN}, 7 'line', 8 \&PLUS::line 9 );
La función insert_method es proveída por Parse::Eyapp::Base
(versiones de Parse::Eyapp posteriores a la 1.099).
Recibe como argumentos una lista de clases, el nombre del método ('line'
en el ejemplo) y la referencia a
la función que la implementa. Dotará a
cada una de las clases especificadas
en la lista (TIMES
, DIV
etc.) con métodos
line
implantados a través de la función especificada (\&PLUS::line
).
pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \ sed -ne '/use/,/^$/p' Types.eyp | cat -n 1 use strict; 2 use Carp; 3 use warnings; 4 use Data::Dumper; 5 use List::MoreUtils qw(firstval); 6 use Simple::Trans; 7 use Parse::Eyapp::Scope qw(:all); 8 use Parse::Eyapp::Base qw(insert_function insert_method); 9 our $VERSION = "0.4"; 10
El método lexeme
que aparece
en la llamada a type_error
devuelve
para un tipo de nodo dado
el lexema asociado con ese nodo.
Por ejemplo, para los nodos TIMES
retorna la cadena *
.
Para implantarlo se usan los hashes lexeme
y
rlexeme
:
pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \ sed -ne '31,64p' Types.eyp | cat -n 1 my %lexeme = ( 2 '=' => "ASSIGN", 3 '+' => "PLUS", 4 '-' => "MINUS", 5 '*' => "TIMES", 6 '/' => "DIV", 7 '%' => "MOD", 8 '|' => "OR", 9 '&' => "AND", 10 '{' => "LEFTKEY", 11 '}' => "RIGHTKEY", 12 ',' => "COMMA", 13 ';' => "SEMICOLON", 14 '(' => "LEFTPARENTHESIS", 15 ')' => "RIGHTPARENTHESIS", 16 '[' => "LEFTBRACKET", 17 ']' => "RIGHTBRACKET", 18 '==' => "EQ", 19 '+=' => "PLUSASSIGN", 20 '-=' => "MINUSASSIGN", 21 '*=' => "TIMESASSIGN", 22 '/=' => "DIVASSIGN", 23 '%=' => "MODASSIGN", 24 '!=' => "NE", 25 '<' => "LT", 26 '>' => "GT", 27 '<=' => "LE", 28 '>=' => "GE", 29 '++' => "INC", 30 '--' => "DEC", 31 '**' => "EXP" 32 ); 33 34 my %rlexeme = reverse %lexeme;Cada una de las clases implicadas es dotada de un método
lexeme
mediante una llamada a insert_method
:
pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \ sed -ne '/sub.*xeme/,/^)/p' Types.eyp | cat -n 1 sub lexeme { 2 return $rlexeme{ref($_[0])} if defined($rlexeme{ref($_[0])}); 3 return ref($_[0]); 4 } 5 6 insert_method( 7 # Gives the lexeme for a given operator 8 qw{ 9 PLUS TIMES DIV MINUS 10 GT EQ NE 11 IF 12 IFELSE 13 WHILE 14 VARARRAY VAR ASSIGN 15 FUNCTIONCALL 16 }, 17 'lexeme', 18 \&lexeme 19 );
El método save_attributes
no se ejecuta automáticamente en las reglas que usan
los operadores de listas +
, *
y ?
. En tales casos el programador
deberá proveer código explícito que salve el número de línea:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple> \ sed -ne '353,363p' Types.eyp | cat -n 1 Variable: 2 %name VAR 3 ID 4 | %name VARARRAY 5 $ID ('[' binary ']') <%name INDEXSPEC +> 6 { 7 my $self = shift; 8 my $node = $self->YYBuildAST(@_); 9 $node->{line} = $ID->[1]; 10 return $node; 11 }En el ejemplo se pone explícitamente como atributo
line
del
nodo VARARRAY
el número de línea del terminal ID
.