CES41 COMPILADORES Captulo I Introduo Captulo I Introduo
- Slides: 76
CES-41 COMPILADORES Capítulo I Introdução
Capítulo I - Introdução 1. 1 – Compiladores 1. 2 – Estrutura de um compilador 1. 3 – Interpretadores 1. 4 – Automação da construção de compiladores
1. 1 – Compiladores 1. 1. 1 – Definição n Genericamente falando, compilador é um software que - Lê um programa escrito numa linguagem: a linguagem-fonte (é o programa-fonte) - Traduz em um programa equivalente, escrito noutra linguagem : a linguagem-objeto (é o programaobjeto)
Esquematicamente: Programa fonte Compilador Programa objeto Mensagens de erro
Possíveis linguagens-fontes: n Mais comuns: linguagens tradicionais de programação: Fortran, Pascal, C, Modula-2, C++, Java, C#, Python, etc. n Linguagens especializadas: simulação, computação gráfica, banco de dados, experimentos Possíveis linguagens-objetos: n Mais comuns: linguagem assembly ou linguagem de máquina de vários computadores n Outras linguagens de programação
Exemplos de linguagens de programação como linguagens-objetos: n Transformação de programas sequenciais escritos em C ou Fortran, em programas paralelos escritos em HPF ou HPC (High Performance Fortran ou C) for (i = 1; i <= n; i++) C[i] = A[i] + B[i] for (i = 1; i <= n; i++) C[i] = A[i] + C[i-1] do parallel i = 1, n C[i] = A[i] + B[i] do parallel i = 1, n C[i] = A[i] + C[i-1]
n n Tradução de programas realizadores de certos experimentos: - Escritos numa linguagem especializada - Traduzidos (compilados) para uma linguagem de programação como C - Compilados depois por um compilador similar ao de C Programas em Lex e Yacc são traduzidos para C - Lex e Yacc são linguagens especializadas para construir componentes de um compilador
Há ainda o “compilador” Assembler: n Linguagem-fonte: Assembly n Linguagem-objeto: linguagem de máquina Enfoque desta disciplina: n A linguagem-fonte é uma linguagem tradicional e a linguagem-objeto é o Assembly de uma máquina n Para a implementação, a linguagem-fonte é Lex e Yacc e a linguagem-objeto é C
1. 1. 2 – O contexto de um compilador n Além do compilador, outros softwares são exigidos para criar um programa executável nalguma máquina
Programa fonte com diretivas de préprocessamento Pré-processador Programa fonte puro Compilador Programa objeto em Assembly Montador Código de máquina com endereçamento deslocado Bibliotecas e outros Editor de Ligações arquivos com endereçamento deslocado Código de máquina executável com endereçamento deslocado Código de máquina Carregador executável com endereçamento correto
a) Pré-processador – realiza várias tarefas antes da compilação: n Inclusão de arquivos ao programa-fonte – Por exemplo, na Linguagem C: - #include <math. h>: inclui protótipos de funções matemáticas pertencentes à biblioteca da linguagem; essas funções já estão em linguagem de máquina - #include “sistemas. c”: inclui arquivo pertencente ao acervo do programador; contém código fonte
Processamento de macros – para abreviar construções longas n Exemplo, em C, com as macros: #define EHPAR(x) (((x)%2)? 0: 1) #define ERRO(msg) printf (“ERRO: % s/n”, msg) pode-se escrever comandos tipos: O pré-processador O substitui a primeira parte do #define if (EHPAR(a+b)) -------; pela segunda, if (valor max) ERRO(“valor muito grande”); realizando inclusive passagem de resultado do pré-processamento é: argumentos if ((((a+b)%2)? 0: 1)). . . . ; if (valor > max) printf(“ERRO: %sn”, “valor muito grande”);
Processamento de extensões de linguagens: n Algumas linguagens são acrescidas de certos artifícios para propósitos específicos de certas aplicações n Exemplos: comandos para manipular banco de dados, para computação gráfica, processamento paralelo, etc. n Muitas linguagens são, na realidade, extensões da Linguagem C n Diretivas iniciadas pelos caracteres “##” são substituídas pelo pré-processador por chamadas de
b) Montador (Assembler): n Transforma o código Assembly, produzido pelo compilador, em código de máquina relocável n Exemplo: programa em C para o cálculo do fatorial de um número digitado e seus correspondentes em Assembly e em linguagem de máquina n Supõe-se uma CPU bem simples, com apenas um registrador de propósitos gerais (AC - acumulador) Para fazer C=A+B Sendo A, B e C endereços de memória Instrução Significado LD A AC Mem(A) ADD B AC + Mem(B) ST C Mem(C) AC
C 1: C 2: #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } Primeiramente, reserva de espaço para as constantes 1 e 2 CONST 1 2
#include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } Em seguida, reserva de espaço para as variáveis n, i, fat C 1: C 2: n: fat: i: CONST CONST 1 2 0 0 0
#include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } Agora a tradução dos comandos C 1: C 2: n: fat: i: CONST CONST 1 2 0 0 0
Rótulo da 1ª instrução executável: inic #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } Na realidade, a tradução de scanf é algo mais complexo: É uma chamada de C 1: C 2: n: fat: i: inic: CONST CONST READ 1 2 0 0 0 n
Rótulo da 1ª instrução executável: inic #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST 1 2 0 0 0 n C 1 fat C 2 i
Rótulo da 1ª instrução executável: inic #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } C 1: C 2: n: fat: i: inic: loop: CONST CONST READ LD ST SUB JP 1 2 0 0 0 n C 1 fat C 2 i n escrever JUMP loop “escrever” é o rótulo da instrução logo após
Rótulo da 1ª instrução executável: inic #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } C 1: C 2: n: fat: i: inic: loop: CONST CONST READ LD ST SUB JP LD MULT ST LD ADD ST JUMP 1 2 0 0 0 n C 1 fat C 2 i n escrever fat i C 1 i loop
Rótulo da 1ª instrução executável: inic #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } Na realidade, a tradução de printf é algo mais complexo: É uma chamada de C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP LD MULT ST LD ADD ST JUMP escrever: WRITE 1 2 0 0 0 n C 1 fat C 2 i n escrever fat i C 1 i loop fat
Rótulo da 1ª instrução executável: inic #include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP LD MULT ST LD ADD ST JUMP escrever: WRITE STOP END 1 2 0 0 0 n C 1 fat C 2 i n escrever fat i C 1 i loop fat inic
#include <stdio. h> void main ( ) { int n, fat, i; scanf (“%d”, &n); fat = 1; i = 2; while (i <= n) { fat = fat * i; i = i + 1; } printf (“%d”, fat); } Final da compilação Agora vem a montagem C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP LD MULT ST LD ADD ST JUMP escrever: WRITE STOP END 1 2 0 0 0 n C 1 fat C 2 i n escrever fat i C 1 i loop fat inic
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n rótulo endereço 0 1 2 3 4 5 6 7 8 9 10 11 fat i C 1 i loop 12 O Assembler monta uma tabela de rótulos para ajudar a preencher o programa em linguagem de máquina 13 14 15 16 17 18 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço 0 1 2 3 4 5 6 7 8 9 Mnemônico Cod Op LD 1 ST 2 ADD 4 SUB 5 MULT 6 JUMP 11 JP 14 READ 15 WRITE 16 STOP 17 10 11 12 13 14 15 16 17 18 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 6 7 8 9 Mnemônico Cod Op LD 1 ST 2 ADD 4 SUB 5 MULT 6 JUMP 11 JP 14 READ 15 WRITE 16 STOP 17 10 11 12 13 14 15 16 17 18 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 inic 5 6 7 8 9 Mnemônico Cod Op LD 1 ST 2 ADD 4 SUB 5 MULT 6 JUMP 11 JP 14 READ 15 WRITE 16 STOP 17 10 11 12 13 14 15 16 17 18 19 20 codop 15 ender 2
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 7 2 3 8 1 1 9 2 4 Mnemônico Cod Op LD 1 ST 2 ADD 4 SUB 5 MULT 6 JUMP 11 JP 14 READ 15 WRITE 16 STOP 17 10 11 12 13 14 15 16 17 18 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 loop 10 7 2 3 8 1 1 9 2 4 10 5 2 Mnemônico Cod Op LD 1 ST 2 ADD 4 SUB 5 MULT 6 JUMP 11 JP 14 READ 15 WRITE 16 STOP 17 11 12 13 14 15 16 17 18 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 loop 10 7 2 3 8 1 1 escrever ? ? ? 9 2 4 Mnemônico Cod Op 10 5 2 LD 1 11 14 ? ? ? ST 2 ADD 4 SUB 5 MULT 6 JUMP 11 JP 14 READ 15 WRITE 16 STOP 17 12 13 14 15 16 17 18 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 loop 10 7 2 3 8 1 1 escrever ? ? ? 9 2 4 Mnemônico Cod Op 10 5 2 LD 1 11 14 ? ? ? ST 2 12 1 3 ADD 4 13 6 4 SUB 5 14 2 3 MULT 6 15 1 4 JUMP 11 16 4 0 JP 14 17 2 4 READ 15 18 11 10 WRITE 16 STOP 17 19 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 loop 10 7 2 3 8 1 1 escrever 19 9 2 4 Mnemônico Cod Op 10 5 2 LD 1 11 14 19 ST 2 12 1 3 ADD 4 13 6 4 SUB 5 14 2 3 MULT 6 15 1 4 JUMP 11 16 4 0 JP 14 17 2 4 READ 15 18 11 10 WRITE 16 19 16 3 STOP 17 20 codop ender
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço codop ender C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 loop 10 7 2 3 8 1 1 escrever 19 9 2 4 Mnemônico Cod Op 10 5 2 LD 1 11 14 19 ST 2 12 1 3 ADD 4 13 6 4 SUB 5 14 2 3 MULT 6 15 1 4 JUMP 11 16 4 0 JP 14 17 2 4 READ 15 18 11 10 WRITE 16 19 16 3 STOP 17 20 17 0
C 1: C 2: n: fat: i: inic: CONST CONST READ LD ST loop: SUB JP escrever LD MULT ST LD ADD ST JUMP escrever: WRITE fat 1 2 0 0 0 n C 1 fat C 2 i n fat i C 1 i loop rótulo endereço C 1 0 0 1 C 2 1 1 2 n 2 2 0 3 0 fat 3 4 0 i 4 5 15 2 inic 5 6 1 0 loop 10 7 2 3 8 1 1 escrever 19 9 2 4 10 5 2 11 14 19 12 1 3 13 6 4 14 2 3 15 1 4 16 4 0 17 2 4 18 11 10 19 16 3 20 17 0 Endereço inicial da execução: 5 Essa informação deve acompanhar o programa em linguagem de codop ender
n n O programa em linguagem C é o programa-fonte O programa gerado pelo Assembler é o programaobjeto O programa-objeto foi montado a partir do endereço zero da RAM Esse programa é guardado num arquivo (extensão obj) Endereço inicial da execução: 5 endereço codop ender 0 1 1 2 2 0 3 0 4 0 5 15 2 6 1 0 7 2 3 8 1 1 9 2 4 10 5 2 11 14 19 12 1 3 13 6 4 14 2 3 15 1 4 16 4 0 17 2 4 18 11 10 19 16 3 20 17 0
n n n O local para execução é estabelecido pelo sistema operacional do computador Esse local depende da disponibilidade da RAM E se o local não for o endereço zero (por exemplo, endereço 3000)? Endereço inicial da execução: 5 endereço codop ender 0 1 1 2 2 0 3 0 4 0 5 15 2 6 1 0 7 2 3 8 1 1 9 2 4 10 5 2 11 14 19 12 1 3 13 6 4 14 2 3 15 1 4 16 4 0 17 2 4 18 11 10 19 16 3 20 17 0
n n Os locais para C 1, C 2, n, fat e i não são mais 0, 1, 2, 3 e 4, mas sim 3000, 3001, 3002, 3003 e 3004 Os rótulos inic, loop e escrever mudarão para os endereços 3005, 3010 e 3019 Então todos os endereços das instruções estarão com um erro (deslocamento de 3000 posições) Endereçoantes inicial Isso tem de ser corrigido da execução: da execução endereço codop ender 3000 1 3001 2 3002 0 3003 0 3004 0 3005 15 2 3006 1 0 3007 2 3 3008 1 1 3009 2 4 3010 5 2 3011 14 19 3012 1 3 3013 6 4 3014 2 3 3015 1 4 3016 4 0 3017 2 4 3018 11 10 3019 16 3 3020 17 0
c) Editor de ligações n Antes de corrigir os endereços do programa-objeto, é necessário juntar a ele todos os subprogramas auxiliares pertencentes à biblioteca da linguagem n Exemplos: funções para entrada e saída (scanf, printf, etc. ), funções matemáticas (sqr, pow, sqrt, log, sin, cos, etc. ) n Esse trabalho de juntar o programa-objeto com tais subprogramas é feito por um software denominado editor de ligações (linkage-editor) n O produto do editor de ligações é um arquivo denominado programa-executável (extensão exe)
d) Carregador n A região de memória onde um programa será alocado para execução só será conhecida quando ele for chamado para isso n Então o endereçamento do arquivo executável precisa ser corrigido, quando sua execução for solicitada n Esse trabalho é feito pelo carregador (loader), que produz a versão final do programa, pronto para rodar
1. 2 – Estrutura de um Compilador 1. 2. 1 – Componentes de um compilador n O trabalho de compilação é dividido em 2 fases: Fase de análise e fase de síntese n Além disso, existem atividades que fazem parte das duas fases
O processo não precisa ser sequencial
Programawhile (i < nn) i = i + j; fonte (caracteres) while ( i < nn ) Sequência de átomos Analisador léxico Analisador sintático Analisador semântico Gerador de código intermediário Otimizador de código intermediário Gerador de código objeto i = i int --- nn int --- j int --- Tabela de símbolos i + j ; Árvore sintátic a while < i = nn i Código + objeto load i R 1: T 1 = i < nn i nnj R 1: sub JF T 1 R 1: T 1 = i. R 2 < nn JZ R 2 T 2 + j JF = T 1 i. R 2 Código JP R 2 T 2+ j i = i load i intermediári JUMP R 1 add j R 2: - - o st i Exemplo J R 1 R 2: - - -
1. 2. 2 – A fase de análise n São realizados três tipos de análise: - Análise linear ou léxica Análise hierárquica ou sintática Análise semântica
a) Análise léxica n Os caracteres do texto são agrupados em átomos (tokens) n A validade dos átomos é verificada n Os átomos recebem uma classificação
n Exemplo: frase da Língua Portuguesa: ajbxswn o homem alto apanhou a laranja madura na laranjeira tdhf
n Exemplo: um comando while em Pascal: while num 50 do num : = num * 2
b) Análise sintática n Os átomos são agrupados em frases, em estrutura de árvore (árvore sintática) n A validade da posição dos átomos é verificada
n Exemplo: frase: o homem alto apanhou a laranja madura na laranjeira
n Exemplo: comando while de Pascal: while num 50 do num : = num * 2
c) Análise semântica n Verifica se a árvore sintática tem sentido n Coleta informações de tipos para a fase de síntese
n Exemplo: frase sem sentido: a laranja apanhou o homem Erro: o verbo apanhar não admite sujeito do tipo vegetal
Exemplo: comando while de Pascal: while n+3 do n*5 (com integer n) Erro: A expressão de um comando while deve ser do tipo lógico Erro: O operador de uma atribuição
1. 2. 3 – A fase de síntese n Depois da análise, a fase de síntese constrói o programa objeto n São realizadas três tarefas: - Geração do código intermediário - Otimização do código intermediário - Geração do código objeto
a) Geração do código intermediário: n A estrutura do programa-fonte costuma ser bem diferente da estrutura do programa-objeto n A transformação da primeira estrutura para a segunda deve ser aliviada passando por uma fase intermediária n Fazendo analogia com metamorfose na vida animal: Ovo Lagarta Borboleta Ovo Girino Sapo
n Exemplo: while n 50 do n : = n * 2 Código de três endereços: 2 endereços p/operandos 1 endereço p/resultado Quádruplas: 1º elemento: operador 2º e 3º elementos: operandos 4º elemento: resultado
n O código intermediário deve ser fácil de: - Ser produzido - Ser transformado em código objeto (Assembly da máquina)
b) Otimização do código intermediário: n Elimina operações desnecessárias e repetidas n Visa simplificar o código intermediário n Visa tornar o programa executável mais rápido n Visa economia de memória
n Exemplo: sejam os comandos x y : = a + b + c; a + b + c + d;
n n Outros casos para otimização: - Detecção de atribuições e operações cujos valores não são usados no programa - Detecção de código morto, ou seja, de código que não é executado, qualquer que seja a entrada de dados Otimização faz análise de fluxo de dados e de controle
c) Geração do código objeto: n Transformação do código intermediário otimizado no código objeto (normalmente o Assembly da máquina alvo) n O código objeto pode também receber otimizações n Exemplo: seja o comando while n < 50 do n : = n * 2
1. 2. 4 – Atividades em ambas as fases n n Manipulação da tabela de símbolos Tratamento de erros a) Tabela de símbolos n Guarda informações sobre todos os identificadores usados em um programa
Informações sobre os identificadores: n Tipo do identificador: Variável - Constante - Definição de tipo - Nome de subprograma - Rótulo - n Escopo: em que trecho ele é válido
n Se for nome de variável: Tipo Se for variável indexada Se é ou não indexada Número de dimensões Se é ou não estrutura Número de elementos em cada dimensão Se é ou não ponteiro Endereço de memória Espaço a ser alocado Se for estrutura: Nome e tipo de cada campo Se for ponteiro: Tipo dos locais apontados por ela
n Exemplos em C: int a; a var int não nome tipova eharra r y float X[7][5][4]; X var real sim 3 nome tipova eharra ndim r y 0 7 1 5 dims 2 4
n Se for nome de subprograma: - Tipo de subprograma (função ou procedimento) - Número de parâmetros - Lista de parâmetros por valor - Lista de parâmetros por referência - Tipo a ser retornado - Lista de variáveis locais
n Exemplo em C: int fff (int m, int n) {float a, b; - - - } n nome var tipo m nome fff nome func tipo int b tipovar nome var tipo int tipovar int tipofun var real tipovar a var real tipovar nome 2 nparam List Var Loc
A tabela de símbolos é manipulada pelos vários componentes das fases de análise e síntese: n Análise léxica detecta e pode armazenar o identificador n Análise sintática pode armazenar seu tipo e outras informações n Análise semântica faz consultas à tabela n Geração de código introduz e usa informações sobre espaço alocado
b) Detecção e tratamento de erros n Alguns componentes do compilador podem encontrar erros n Encontrado um erro, um componente deve tratá-lo de forma a permitir que outros erros sejam detectados n Um compilador que para, quando encontra o primeiro erro, não pode ajudar muito n Existem erros léxicos, sintáticos e semânticos
n n Exemplos: - Erro léxico: Conjunto de caracteres que não corresponde a nenhum átomo - Erro sintático: Sequência de átomos que viola construções sintáticas - Erro semântico: Construções sintáticas corretas porém sentido Cada componente da estrutura do compilador será estudado detalhadamente em capítulo específico
1. 2. 5 – Outra decomposição de um compilador n Compilador é uma interface entre: linguagem-fonte e máquina-alvo n Frente ou front-end : parte do compilador dependente da linguagem-fonte n Retaguarda ou back-end: parte do compilador dependente da máquina-alvo
Frente (front-end) compreende: n Análises léxica, sintática e semântica n Criação e consultas à tabela de símbolos n Geração do código intermediário n Muita otimização do código intermediário n Tratamento de erros léxicos, sintáticos e semânticos
Retaguarda (back-end) compreende: n Alguma otimização do código intermediário n Geração de código objeto n Otimização do código objeto n Operações na tabela de símbolos n Tratamento de alguns erros
n Essa decomposição facilita a criação de compiladores de: - Mesma linguagem-fonte para várias máquinasalvos - Várias linguagens-fontes para mesma máquinaalvo L L 1 L 2 Lj M 1 M 2 Mi M Uma única linguagem para código intermediário
No primeiro caso: sucesso absoluto n Pode-se projetar um código intermediário que se aproxime bem da linguagem de máquina de uma variedade de arquiteturas L M 1 M 2 L 1 Mi L 2 Lj M
No segundo caso: sucesso limitado n Diferenças sutis entre algumas linguagens dificultam a obtenção de um código intermediário comum entre elas L M 1 M 2 L 1 Mi L 2 Lj M