CES41 COMPILADORES Captulo I Introduo Captulo I Introduo

  • Slides: 76
Download presentation
CES-41 COMPILADORES Capítulo I Introdução

CES-41 COMPILADORES Capítulo I Introdução

Capítulo I - Introdução 1. 1 – Compiladores 1. 2 – Estrutura de um

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 é

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

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++,

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

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

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

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

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

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

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

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

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

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,

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”,

#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”,

#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 ( ) {

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 ( ) {

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 ( ) {

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 ( ) {

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 ( ) {

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 ( ) {

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”,

#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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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

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

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

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

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

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

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

O processo não precisa ser sequencial

Programawhile (i < nn) i = i + j; fonte (caracteres) while ( i

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

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) 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

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 : =

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

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: frase: o homem alto apanhou a laranja madura na laranjeira

n Exemplo: comando while de Pascal: while num 50 do num : = num

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

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 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

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

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

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

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

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

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;

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 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

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

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

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 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

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 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 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

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

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

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

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 à

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

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

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

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

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