Subsecciones

Asignación de Direcciones

Esta suele ser considerada la primera de las fases de síntesis. Las anteriores lo eran de análisis. La fase de análisis es una transformación $ texto fuente \rightarrow arbol$, mientras que la fase síntesis es una transformación inversa $ arbol \rightarrow texto objeto$ que produce una ``linealización'' del árbol. En general, se ubican en la fase de síntesis todas las tareas que dependan del lenguaje objeto.

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.


Práctica: Cálculo de las Direcciones

Modifique el cálculo de las direcciones para la gramática de Tutu extendida con sentencias compuestas que fué presentada en la sección 33.10.2.

  1. Resuelva el problema de calcular las direcciones de las variables declaradas en los bloques. La memoria ocupada por dichas variables se libera al terminar el bloque. Por tanto la variable $global_address disminuye al final de cada bloque.
  2. En el código de la subrutina 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
    

  3. Empaquete las cadenas asumiendo que cada carácter ocupa un octeto o byte. Una posible solución al problema de alineamiento que se produce es rellenar con ceros los bytes que faltan hasta completar la última palabra ocupada por la cadena. Por ejemplo, si la longitud de palabra de la máquina objeto es de 4 bytes, la palabra procesador se cargaría asi:

       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.

  4. Mantega atributos distintos en el nodo 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
2013-04-23