Projeto do compilador - fases do compilador
O processo de compilação é uma sequência de várias fases. Cada fase recebe entrada de seu estágio anterior, tem sua própria representação do programa de origem e alimenta sua saída para a próxima fase do compilador. Vamos entender as fases de um compilador.
Análise Lexical
A primeira fase do scanner funciona como um scanner de texto. Esta fase verifica o código-fonte como um fluxo de caracteres e o converte em lexemas significativos. O analisador léxico representa esses lexemas na forma de tokens como:
<token-name, attribute-value>
Análise de sintaxe
A próxima fase é chamada de análise de sintaxe ou parsing. Ele pega o token produzido pela análise lexical como entrada e gera uma árvore de análise (ou árvore de sintaxe). Nesta fase, os arranjos de tokens são verificados em relação à gramática do código-fonte, ou seja, o analisador verifica se a expressão feita pelos tokens está sintaticamente correta.
Análise Semântica
A análise semântica verifica se a árvore de análise construída segue as regras da linguagem. Por exemplo, a atribuição de valores é entre tipos de dados compatíveis e adição de string a um inteiro. Além disso, o analisador semântico controla os identificadores, seus tipos e expressões; se os identificadores são declarados antes do uso ou não etc. O analisador semântico produz uma árvore de sintaxe anotada como uma saída.
Geração de código intermediário
Após a análise semântica, o compilador gera um código intermediário do código-fonte para a máquina de destino. Ele representa um programa para alguma máquina abstrata. Está entre a linguagem de alto nível e a linguagem de máquina. Este código intermediário deve ser gerado de forma que seja mais fácil de ser traduzido para o código da máquina alvo.
Otimização de Código
A próxima fase faz a otimização do código intermediário. A otimização pode ser assumida como algo que remove linhas de código desnecessárias e organiza a sequência de instruções para acelerar a execução do programa sem desperdiçar recursos (CPU, memória).
Geração de Código
Nesta fase, o gerador de código pega a representação otimizada do código intermediário e mapeia para a linguagem de máquina de destino. O gerador de código traduz o código intermediário em uma sequência de código de máquina (geralmente) realocável. A sequência de instruções do código de máquina executa a tarefa como o código intermediário faria.
Tabela de Símbolos
É uma estrutura de dados mantida ao longo de todas as fases de um compilador. Todos os nomes do identificador junto com seus tipos são armazenados aqui. A tabela de símbolos torna mais fácil para o compilador pesquisar rapidamente o registro do identificador e recuperá-lo. A tabela de símbolos também é usada para gerenciamento de escopo.