Generación de Código: Máquina Pila

El generador de código emite el código para el lenguaje objeto, utilizando las direcciones calculadas en la fase anterior. Hemos simplificado esta fase considerando una máquina orientada a pila: una memoria de pila en la que se computan las expresiones, un segmento de datos en el que se guardan las variables y un segmento para guardar las cadenas. La estrategia utilizada es similar a la que vimos en el plegado de las constantes. Definimos un método para la traducción de cada clase de nodo del AAA. Entonces, para traducir el programa basta con llamar al correspondiente método:

  $tree = Syntax::Analysis::parser;
  ... # otras fases
  ########code generation
  local $target = ""; # target code
  $tree->translate;
  ...

en la cadena $target dejamos el código emitido. Cada método visita los hijos, traduciéndolos y añadiendo el código que fuera necesario para la traducción del nodo.

sub PROGRAM::translate {
  my $tree = shift;

  $target .= "DATA ". $data."\n" if $data;
  $tree->STS->translate;
}

Traducir la lista de sentencias es concatenar la traducción de cada una de las sentencias en la lista:

sub STATEMENTS::translate {
  my $statements = shift;
  my @statements = @{$statements};

  for my $s (@statements) {
    $s->translate;
  }
}

Si suponemos que disponemos en el ensamblador de nuestra máquina objeto de instrucciones PRINT_INT y PRINT_STR que imprimen el contenido de la expresión que esta en la cima de la pila, la traducción será:

sub PRINT::translate {
  my $self = shift;

  $self->EXPRESSION->translate;
  if ($self->EXPRESSION->TYPE == $int_type) { emit "PRINT_INT\n"; }
  else {emit "PRINT_STR\n"; }
}
Asi, si la sentencia era P c, donde c es del tipo cadena, se debería eventualmente llamar al método de traducción del identificador:

sub ID::translate {
  my $self = shift;

  my $id = $self->VAL;
  my $type =  Semantic::Analysis::get_type($id);
  if ($type == $int_type) {
    emit "LOAD ".$symbol_table{$id}->{ADDRESS}."\n";
  }
  else {
    emit "LOAD_STRING ".$symbol_table{$id}->{ADDRESS}."\n";
  }
}
la función emit simplemente concatena el código producido a la salida:

sub emit { $target .= shift; }

Para la traducción de una sentencia de asignación supondremos de la existencia de isntrucciones STORE_INT y STORE_STRING. La instrucción STORE_STRING asume que en la cima de la pila están la dirección y la cadena a almacenar.

sub ASSIGN::translate {
  my $self = shift;

  $self->RIGHT->translate;
  my $id = $self->LEFT;
  $id->translate;
  my $type =  Semantic::Analysis::get_type($id->VAL);

  if ($type == $int_type) {
    emit "STORE_INT\n";
  }
  else {
    emit "STORE_STRING\n";
  }
}
Si se está traduciendo una sentencia como a = "hola", se acabará llamando al método de traducción asociado con la clase STR el cual actúa empujando la dirección y la longitud de la cadena en la pila:
sub STR::translate {
  my $self = shift;

  emit "PUSHSTR ".$self->OFFSET." ".$self->LENGTH."\n";
}
Así la traducción de este fuente:

$ cat test06.tutu
string a;
a = "hola";
p a

es:

$ ./main.pl test06.tutu test06.ok
$ cat test06.ok
DATA 2, hola
PUSHSTR 2 4
PUSHADDR 0
STORE_STRING
LOAD_STRING 0
PRINT_STR

El resto de los métodos de traducción es similar:

sub LEFTVALUE::translate {
  my $id = shift ->VAL;

  emit "PUSHADDR ".$symbol_table{$id}->{ADDRESS}."\n";
}

sub NUM::translate {
  my $self = shift;

  emit "PUSH ".$self->VAL."\n";
}

sub PLUS::translate {
  my $self = shift;

  $self->LEFT->translate;
  $self->RIGHT->translate;
  emit "PLUS\n";
}

sub TIMES::translate {
  my $self = shift;

  $self->LEFT->translate;
  $self->RIGHT->translate;
  emit "MULT\n";
}

Veamos un ejemplo. Dado el código fuente:

$ cat test02.tutu
int a,b; string c; 
a = 2+3; b = 3*4; c = "hola"; p c; 
c = "mundo"; p c; p 9+2; p a+1; p b+1
el código generado es:
$ ./main.pl test02.tutu test02.ok
$ cat test02.ok
DATA 4, holamundo
PUSH 5
PUSHADDR 0
STORE_INT
PUSH 12
PUSHADDR 1
STORE_INT
PUSHSTR 4 4
PUSHADDR 2
STORE_STRING
LOAD_STRING 2
PRINT_STR
PUSHSTR 8 5
PUSHADDR 2
STORE_STRING
LOAD_STRING 2
PRINT_STR
PUSH 11
PRINT_INT
LOAD 0
INC
PRINT_INT
LOAD 1
INC
PRINT_INT

Casiano Rodríguez León
2013-04-23