5.2.2 O Montador
O montador transforma a sequência de instruções do controlador para uma máquina em uma lista correspondente de instruções de máquina, cada uma com sua função de execução. No geral, o montador é muito parecido com os avaliadores que estudamos no capítulo 4—existe uma linguagem de entrada (neste caso, a linguagem de máquina de registradores) e devemos executar uma ação apropriada para cada tipo de componente na linguagem.
A técnica de produzir uma função de execução para cada instrução é exatamente o que usamos na seção 4.1.7 para acelerar o avaliador separando a análise da execução em tempo de execução. Como vimos no capítulo 4, muita análise útil de expressões JavaScript pode ser realizada sem saber os valores reais dos nomes. Aqui, analogamente, muita análise útil de expressões da linguagem de máquina de registradores pode ser realizada sem saber o conteúdo real dos registradores da máquina. Por exemplo, podemos substituir referências a registradores por ponteiros para os objetos de registrador, e podemos substituir referências a rótulos por ponteiros para o lugar na sequência de instruções que o rótulo designa.
Antes de poder gerar as funções de execução de instrução, o montador deve saber a que todos os rótulos se referem, então ele começa varrendo a sequência do controlador para separar os rótulos das instruções. À medida que varre o controlador, ele constrói tanto uma lista de instruções quanto uma tabela que associa cada rótulo com um ponteiro para essa lista. Então o montador aumenta a lista de instruções inserindo a função de execução para cada instrução.
A função assemble é o ponto de entrada principal para o montador. Ela recebe a sequência do controlador e o modelo de máquina como argumentos e retorna a sequência de instruções a ser armazenada no modelo. A função assemble chama extract_labels para construir a lista de instruções inicial e a tabela de rótulos a partir da sequência do controlador fornecida. O segundo argumento para extract_labels é uma função a ser chamada para processar esses resultados: Esta função usa update_insts para gerar as funções de execução de instrução e inseri-las na lista de instruções, e retorna a lista modificada.
A função extract_labels recebe uma lista controller e uma função receive como argumentos. A função receive será chamada com dois valores: (1) uma lista insts de estruturas de dados de instrução, cada uma contendo uma instrução de controller; e (2) uma tabela chamada labels, que associa cada rótulo de controller com a posição na lista insts que o rótulo designa.
A função extract_labels funciona varrendo sequencialmente os elementos do controller e acumulando insts e labels. Se um elemento é uma string (e portanto um rótulo), uma entrada apropriada é adicionada à tabela labels. Caso contrário, o elemento é acumulado na lista insts.1
A função update_insts modifica a lista de instruções, que inicialmente contém apenas as instruções do controlador, para incluir as funções de execução correspondentes:
A estrutura de dados de instrução de máquina simplesmente emparelha a instrução do controlador com a função de execução correspondente. A função de execução não está ainda disponível quando extract_labels constrói a instrução, e é inserida mais tarde por update_insts.
A instrução do controlador não é usada por nosso simulador, mas é útil manter por perto para depuração (veja o exercício 5.16).
Elementos da tabela de rótulos são pares:
Entradas serão procuradas na tabela com:
Exercício 5.8
O seguinte código de máquina de registradores é ambíguo, porque o rótulo here é definido mais de uma vez:
"start",
go_to(label("here")),
"here",
assign("a", constant(3)),
go_to(label("there")),
"here",
assign("a", constant(4)),
go_to(label("there")),
"there",
Com o simulador como escrito, qual será o conteúdo do registrador a quando o controle chegar a there? Modifique a função extract_labels de modo que o montador sinalize um erro se o mesmo nome de rótulo for usado para indicar dois locais diferentes.