

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.

