La asignación de direcciones depende de la máquina objetivo en cuanto conlleva consideraciones sobre la longitud de palabra, las unidades de memoria direccionables, la compactación de objetos pequeños (por ejemplo, valores lógicos), el alineamiento a fronteras de palabra, etc.
En nuestro caso debemos distinguir entre las cadenas y las variables enteras.
Las constantes literales (como "hola"
) se almacenan
concatenadas en orden de aparición textual.
Una variable de tipo cadena ocupa dos palabras, una dando su dirección
y otra dando su longitud.
sub factor() { my ($e, $id, $str, $num); if ($lookahead eq 'NUM') { ... } elsif ($lookahead eq 'ID') { ... } elsif ($lookahead eq 'STR') { $str = $value; my ($offset, $length) = Address::Assignment::str($str); match('STR'); return STR->new(OFFSET => $offset, LENGTH => $length, TYPE => $string_type); } elsif ($lookahead eq '(') { ... } else { Error::fatal("Se esperaba (, NUM o ID"); } }
El código de la subrutina Address::Assignment::str
es:
sub str { my $str = shift; my $len = length($str); my $offset = length($data); $data .= $str; return ($offset, $len); }Una posible mejora es la que se describe en el punto 2 de la práctica 33.13.1.
Hemos supuesto que las variables enteras y las referencias ocupan una palabra. Cada vez que una variable es declarada, se le computa su dirección relativa:
# declaration -> type idlist # type -> INT | STRING sub declaration() { my ($t, $decl, @il); if (($lookahead eq 'INT') or ($lookahead eq 'STRING')) { $t = &type(); @il = &idlist(); &Semantic::Analysis::set_types($t, @il); &Address::Assignment::compute_address($t, @il); $decl = [$t, \@il]; return bless $decl, 'DECL'; } else { Error::fatal('Se esperaba un tipo'); } }
Se usa una variable $global_address
para llevar la cuenta de la última
dirección utilizada y se introduce un atributo ADDRESS
en
la tabla de símbolos:
sub compute_address { my $type = shift; my @id_list = @_; for my $i (@id_list) { $symbol_table{$i}->{ADDRESS} = $global_address; $global_address += $type->LENGTH; } }
Por último situamos todas las cadenas después de las variables del programa fuente:
... ##### En compile, despues de haber calculado las direcciones Tree::Transform::match_and_transform_list( NODES => $tree->{STS}, PATTERN => sub { $_[0]->isa('STR') }, ACTION => sub { $_[0]->{OFFSET} += $global_address; } );
Esta aproximación es bastante simplista en cuanto que usa una palabra por carácter. El punto 3 de la práctica 33.13.1 propone una mejora.
$global_address
disminuye al final de cada bloque.
Address::Assignment::str
sub str { my $str = shift; my $len = length($str); my $offset = length($data); $data .= $str; return ($offset, $len); }
no se comprueba si la cadena $str
ya está en $data
.
Es natural que si ya está no la insertemos de nuevo. Introduzca esa
mejora. Para ello use la función pos
,
la cual devuelve el desplazamiento en $data
donde
quedó la última búsqueda $data =~ m/regexp/g
.
El siguiente ejemplo con
el depurador le muestra como trabaja. Escriba perldoc -f pos
para obtener mas información sobre la función pos
.
Vea un ejemplo de uso:
DB<1> $str = "world" # 012345678901234567890123456789012 DB<2> $data = "hello worldjust another statement" DB<3> $res = $data =~ m{$str}g DB<4> x pos($data) # la siguiente busqueda comienza en la 11 0 11 DB<5> $s = "hello" DB<6> $res = $data =~ m{$s}g DB<7> x pos($data) # No hay "hello" despues de world en $data 0 undef DB<8> $res = $data =~ m{$str}g # Repetimos ... DB<9> x pos($data) 0 11 DB<10> pos($data) = 0 # Podemos reiniciar pos! DB<11> $res = $data =~ m{$s}g DB<12> x pos($data) # Ahora si se encuentra "hello" 0 5
0 | 1 | 2 0 1 2 3 | 4 5 6 7 | 8 9 10 11 p r o c | e s a d | o r \0 \0
Existen dos posibles formas de hacer esta empaquetado.
Por ejemplo, si tenemos las cadenas
lucas
y pedro
las podemos introducir en
la cadena $data
asi:
0 | 1 | 2 | 3 0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15 l u c a | s \0 \0 \0 | p e d r | o \0 \0 \0
o bien:
0 | 1 | 2 0 1 2 3 | 4 5 6 7 | 8 9 10 11 l u c a | s p e d | r o \0 \0
La segunda forma aunque compacta más tiene la desventaja de hacer luego mas lento el código para el direccionamiento de las cadenas, ya que no empiezan en frontera de palabra. Utilice el primer modo.
Observe que este empaquetado introduce un efecto en lo que
se considera un éxito en la búsqueda de una cadena
$str
en $data
. ¿Que ocurre si $str
aparece en $data
como subcadena de una cadena
previamente empaquetada ocupando una posición que no es frontera de palabra?
Cuando rellene con \0
la cadena use el operador x
(letra equis)
para multiplicar número por cadena:
DB<1> $x = "hola"x4 DB<2> p $x holaholaholahola
esto le evitará escribir un bucle.
STR
para el desplazamiento y longitud
de $str
en $data
(en caracteres)
y su dirección y tamaño final en memoria (en palabras).
Casiano Rodríguez León