Upload
internet
View
118
Download
3
Embed Size (px)
Citation preview
©Universidade do Algarve1
Introdução da Disciplina
Compiladores
João M. P. Cardoso
Universidade do Algarve
8000-117 Faro, Portugal
©Universidade do Algarve2
O que é um compilador?
Software que traduz o texto que representa um programa para código máquina capaz de ser executado pelo computador
Contudo, um compilador pode ser muito mais do que isso...
©Universidade do Algarve3
Importância
Papel importante no desenvolvimento de aplicações complexas em tempo útil
Muitas das matérias abordadas são utilizadas por outras ferramentas: Tradução de linguagens Interpretação de descrições (ex.: html,
postscript, latex, ficheiros word) Procura de palavras ou frases por motores
de busca
©Universidade do Algarve4
Dilema da Programação
Assembly é fastidioso, erróneo, pouco produtivo, etc.Embora possa permitir melhor
desempenho Desenho de linguagens de alto-nível Como implementar a linguagem?
InterpretadorCompilador
©Universidade do Algarve5
Compilador
Programa em Linguagem
Máquina
Código fonte descrito numa linguagem de
alto-nível (ex.: C, C++, Pascal, etc.)
Compilador
Ponto de Origem
Ponto de Destino
©Universidade do Algarve6
Ponto de Origem
Linguagem imperativa (e.g., C/C++, Java, Pascal, etc.) Estado
• Variáveis escalares• Variáveis do tipo array• Registos• Atributos de objectos (linguagens orientadas por
objectos) Computação
• Expressões (aritméticas, lógicas, etc.)• Enunciados de atribuição• Fluxo de controlo (if, switch, etc.)• Procedimentos (métodos nas linguagens
orientadas por objectos)
Int sum(int A[], int N) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum;}
©Universidade do Algarve7
Ponto de Destino
Linguagem máquina que descreve o programa com base no ISA (Instruction-Set Architecture) do processador Estado
• Memória• Registos (de estado, de propósito geral, etc.)
Computação• Instruções do ISA (MIPS):
• Lw $3, 100($2)• Add $3, $2, $1• Bne $2, $3, label• …
Sum: Addi $t0, $0, 0 Addi $v0, $0, 0Loop: beq $t0, $a1, End Add $t1, $t0, $t0 Add $t1, $t1, $t1 Add $t1, $t1, $a0 Lw $t2, 0($t1) Add $v0, $v0, $t2 Addi $t0, $t0, 1 J LoopEnd: jr $ra
©Universidade do Algarve8
Tópicos
Estudo das etapas de um compilador Implementação de algumas dessas
etapas Aulas práticas serão vocacionadas
para implementação e realização de alguns exercícios
Construção de um compilador/interpretador simples
©Universidade do Algarve10
Conteúdo Programático
Aulas Teóricas Introdução Análise lexical Análise Sintáctica Análise Semântica Tradução para código intermédio Geração de código final Optimização de código Tópicos avançados
©Universidade do Algarve11
Conteúdo Programático
Aulas Teórico-Práticas Programação de várias etapas de um
compilador (vamos utilizar Java) Exercícios de papel e lápis sobre
determinados aspectos Trabalhos para Casa
Exercícios levantados nas aulas teóricas Realização dos trabalhos para avaliação
©Universidade do Algarve12
Avaliação
Exame (40 %) + Trabalhos (60%) (1) Exame
de Época Normal de Recurso
Para obter aprovação: Nota da equação (1) >= 9,5 valores, mas Nota do exame >= 6,5 e Nota dos trabalhos >= 10
©Universidade do Algarve13
Bibliografia
Andrew W. Appel. Modern Compiler Implementation in Java, Cambridge University Press, 1998.
Alfred V. Aho, Ravi Sethi, Jeffery D. Ullman. Compiler - Principles, Techniques, and Tools, Addison-Wesley, 1986. [existe uma tradução brasileira]
Dick Grune, Henri E. Bal, Ceriel J. H. Jacobs, and Koen G. Langendoen, “Modern Compiler Design,” John Wiley & Sons, Ltd, 2000. [existe uma tradução brasileira]
Rui Gustavo Crespo. Processadores de Linguagens. IST Press. 2001. http://istpress.ist.utl.pt/lprocess.html
Com tópicos mais avançados: Steven S. Muchnick. Advanced Compiler Design and
Implementation, Morgan Kaufmann Publishers, 1997.
©Universidade do Algarve14
Boa sorte!
POR EXEMPLO, TENHO QUE SABER COMO
FUNCIONA UM COMPILADOR.
E DEPOIS?
DEPOIS? DEPOIS ESTAMOS
PRONTOS PARA CONSTRUIR UM COMPILADOR!
Página Web da disciplina:http://w3.ualg.pt/~jmcardo/ensino/compiladores
Lista de correio electrónicohttps://imap.ualg.pt/wws/info/fct-compiladores
Para subscrever: [email protected]
©Universidade do Algarve15
Do alto-nível ao assembly
Compiladores
João M. P. Cardoso
©Universidade do Algarve16
Viagem
Como são implementadas as estruturas computacionais em assembly? Revisão dos conceitos relacionados com a
programação em assembly para o MIPS R3000 (ver disciplina de Arquitectura de Computadores)
Fizemos o papel de um compilador. Agora vamos aprender, entre outras coisas, a fazer um compilador.
©Universidade do Algarve17
Do alto-nível ao assembly Alvo: MIPS R3000
Int sum(int A[], int N) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum;}
# $a0 armazena o endereço de A[0]# $a1 armazena o valor de NSum: Addi $t0, $0, 0 # i = 0
Addi $v0, $0, 0 # sum = 0Loop: beq $t0, $a1, End # if(i == N) goto End;
Add $t1, $t0, $t0 # 2*iAdd $t1, $t1, $t1 # 2*(2*i) = 4*iAdd $t1, $t1, $a0 # 4*i + base(A)Lw $t2, 0($t1) # load A[i]Add $v0, $v0, $t2 # sum = sum + A[i]Addi $t0, $t0, 1 # i++J Loop # goto Loop;
End: jr $ra # return
©Universidade do Algarve18
Do alto-nível ao assembly
Variáveis globais:Armazenadas na memóriaPara cada uso de uma variável global
o compilador tem de gerar instruções load/store
int a, b, c;
... fun(...) { ...}
Memória
abc
©Universidade do Algarve19
Variáveis Globais
Int a, b, c;
void fun() { c = a + b;}
.data 0x10000000a: .space 4b: .space 4c: .space 4
.textfun: …
la $t1, alw $t1, 0($t1)la $t2, blw $t2, 0($t2)add $t3, $t2, $t1la $t4, csw $t3, 0($t4)…
0x10000008
0x100000040x10000000
Alocação de
memória
Memória
abc
©Universidade do Algarve20
Do alto-nível ao assembly Conceito de chamada a procedimentos
Cada procedimento tem estados• Variáveis locais• Endereço de retorno
Estado é guardado na área de memória designada por pilha de chamadas (é utilizado um registo para apontar para a posição actual da pilha)
Pilha de chamadas A pilha de chamadas encontra-se no topo da memória A pilha cresce para baixo
void fun() { int a, b, c; ... c = a + b; ...}
Memória
cba
Registos
$sp
©Universidade do Algarve21
Variáveis Locais
Exemplo:
void fun() { int a, b, c; ... c = a + b; ...}
fun: addi $sp, $sp, -12…lw $t1, 0($sp)lw $t2, 4($sp)add $t3, $t2, $t1sw $t3, 8($sp)…addi $sp, $sp, 12jr $raMemória
cba $sp
Reserva espaço na pilha
liberta espaço na pilha
Load aLoad b
store ca + b
©Universidade do Algarve22
Variáveis Locais
Acesso aos registos internos do processador é muito mais rápido Mas os registos internos são em número limitado E por isso nem todas as variáveis locais podem ser
armazenadas nesses registos No passado a atribuição de registos internos do
processador a variáveis locais era feita pelo programador: A linguagem C tem uma palavra reservada para
orientar o compilador: register (e.g., register int c;) Hoje os compiladores são muito mais eficientes
Essa atribuição é lhe inteiramente delegada
©Universidade do Algarve23
Variáveis Locais
Utilização de registos internos
void fun() { int a, b, c; ... c = a + b; ...}
fun: …add $t3, $t2, $t1…jr $ra
Ficheiro de
Registos$t1$t2$t3
abc
©Universidade do Algarve24
Do alto-nível ao assembly
Implementar RegistosRegistos contêm
vários campos Cada estrutura é
armazenada em posições contíguas de memória
typedef struct { int x, y, z;} foo;
foo *p;
Memória
zyx
p
©Universidade do Algarve25
Do alto-nível ao assembly
Exemplo com estrutura local:
typedef struct { int x, y, z;} foo;
fun() { foo *p; p->x = p->y + p->z;}
fun: addi $sp, $sp, -16…
lw $t1, 0($sp)addi $t1, $t1, 8lw $t2, 0($t1)lw $t1, 0($sp)addi $t1, $t1, 12lw $t3, 0($t1)add $t3, $t2, $t3lw $t1, 0($sp)addi $t1, $t1, 4sw $t3, 0($t1)…addi $sp, $sp, 16jr $ra
Reserva espaço na pilha
liberta espaço na pilha
Endereço de p
Load p->y
Load p->zp->y + p->z
store em p->x
Memória
zyxp
address p->y
address p->z
address p->x
Endereço de p
Endereço de p
©Universidade do Algarve26
Do alto-nível ao assembly
Exemplo com estrutura local (optimizado):
typedef struct { int x, y, z;} foo;
fun() { foo *p; p->x = p->y + p->z;}
fun: addi $sp, $sp, -16…lw $t2, 8($sp)lw $t3, 12($sp)add $t3, $t2, $t3sw $t3, 4($sp)…addi $sp, $sp, 16…
Reserva espaço na pilha
liberta espaço na pilha
Load p->yLoad p->zp->y + p->zstore em p->x
Memória
zyxp
©Universidade do Algarve27
Do alto-nível ao assembly
Compiladores
João M. P. Cardoso
©Universidade do Algarve28
Alinhamento, empacotamento e enchimento
Requisitos de alinhamento: Inteiros tipo int (4 bytes) a começar em
endereços com os 2 LSBs == “00” Inteiros tipo short (2 bytes) a começar em
endereços com o LSB == ‘0’ Alinhamento requer:
Enchimento entre campos para assegurar o alinhamento
Empacotamento de campos para assegurar a utilização de memória
©Universidade do Algarve29
Alinhamento
typedef struct {
int w; char x; int y; char z;} foo;
foo *p;
Memória
zyx
p
w
Memória
yx, z
p
w
Organização “ingénua”
OrganizaçãoEmpacotada
(poupa 4 bytes)
livre
ocupado 32 bits32 bits
©Universidade do Algarve30
Arrays
Afectação de posições de memória para os elementos do array
Elementos são armazenados contiguamente
Memória
a[0]
int a[4];
a[1]a[2]a[3]
Memória
a[1]
short a[4];
a[0]a[3]a[2]
32 bits 32 bits
©Universidade do Algarve31
Arrays
Utilizando registos do processador para armazenar as variáveis i e j:
.dataA: .space 16
.textProc:
…la $t0, Aaddi $t2, $0,
4mult $t1, $t2mflo $t2add $t3, $t2,
$t0lw $t4,
0($t3)…
int a[4];proc() { int i, j; … i = a[j]; …}
Endereço de a[j] = endereço de a[0] + (4 × j) = a + (4 × j)
©Universidade do Algarve32
Expressões
a = b * c + d – e; a em $t4; b em $t0; c em $t1; d em
$t2; e em $t3
…mult $t0, $t1mflo $t4sub $t5, $t2,
$t3add $t4, $t4,
$t5…
…mult $t0, $t1mflo $t4add $t4, $t4,
$t2sub $t4, $t4,
$t3…
©Universidade do Algarve33
Estruturas condicionais
If(a == 1) b = 2; a em $t0; b em $t1
If(a == 1) b = 2;
else b = 1; a em $t0; b em $t1
…addi $t2, $0, 1bne $t2, $t0,
skip_ifaddi $t1, $0, 2
Skip_if: …
…addi $t2, $0, 1bne $t2, $t0,
elseaddi $t1, $0, 2 j skip_if
Else: addi $t1, $0, 1Skip_if: …
©Universidade do Algarve34
Estruturas condicionais
Branch-delay O processador executa
sempre a instrução a seguir a uma instrução de salto (quer o salto seja realizado ou não)
Quando não é possível deslocar uma instrução para depois da instrução de salto, tem de se introduzir uma instrução nop
If(a == 1) b = 2;
C = a+1; a em $t0; b em $t1
…addi $t2, $0, 1bne $t2, $t0,
skip_ifaddi $t3, $t0, 1addi $t1, $0, 2
Skip_if: …
©Universidade do Algarve35
Ciclos
Transformar o fluxo de controlo (while, for, do while, etc.) em saltos
Int sum(int A[], int N) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum;}
# $a0 armazena o endereço de A[0]# $a1 armazena o valor de NSum: Addi $t0, $0, 0 # i = 0
Addi $v0, $0, 0 # sum = 0Loop: beq $t0, $a1, End # if(i == N) goto End;
Add $t1, $t0, $t0 # 2*iAdd $t1, $t1, $t1 # 2*(2*i) = 4*iAdd $t1, $t1, $a0 # 4*i + base(A)Lw $t2, 0($t1) # load A[i]Add $v0, $v0, $t2 # sum = sum + A[i]Addi $t0, $t0, 1 # i++J Loop # goto Loop;
End: jr $ra # return
©Universidade do Algarve36
Ciclos
Optimizações Manter i e endereço de a[i] em registos Determinar endereço de a[0] antes do corpo
do ciclo, e incrementar de 4 (no caso de serem acessos a palavras de 32 bits) no corpo do ciclo
Caso o ciclo execute pelo menos uma iteração (N > 0) passar salto do ciclo para o fim do corpo
©Universidade do Algarve37
Ciclos
Código após as optimizações
Int sum(int A[], int N) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum;}
# $a0 armazena o endereço de A[0]# $a1 armazena o valor de NSum: Addi $t0, $0, 0 # i = 0
Addi $v0, $0, 0 # sum = 0Loop: Lw $t2, 0($a0) # load A[i]
Add $v0, $v0, $t2 # sum = sum + A[i]addi $a0, $a0, 4 # add 4 to addressAddi $t0, $t0, 1 # i++bne $t0, $a1, Loop # if(i != N) goto Loop;
End: jr $ra # return
©Universidade do Algarve38
Procedimentos
Protocolo entre os procedimentos que invocam e os procedimentos invocados Dependente do processador No MIPS:
• Procedimento espera argumentos nos registos $a0-$a3
• Coloca valores a retornar nos registos $v0-$v1• Outras formas de passagem de parâmetros
utilizam a pilha de chamadas (por exemplo, sempre que o número de argumentos ultrapassa o número de registos para utilizar como argumentos)
©Universidade do Algarve39
Sumário
Quais as responsabilidades do compilador? Esconder do programador conceitos baixo-nível da
máquina Produzir rapidamente código eficiente Afectar variáveis a registos locais ou posições de
memória Cálculo de expressões com constantes Manter funcionalidade inicial Geração de instruções de forma a suportar as
chamadas aos procedimentos utilizadas no programa Optimizações
©Universidade do Algarve40
Anatomia de um Compilador
Compiladores
João M. P. Cardoso
©Universidade do Algarve41
Viagem
Do texto que representa o programa até ao código máquina
Duas fases:Análise
• Reconhecimento dos enunciados no código fonte e armazenamento em estruturas internas
Síntese• Geração do código assembly a partir das
estruturas internas
©Universidade do Algarve42
Análise Lexical
Lexical Analyzer (Scanner)
Cadeia de Tokens
Programa (cadeia de caracteres)
©Universidade do Algarve43
Análise Lexical/* uma expressão simples */y = b*x +c; // atribui a y
ID(y) EQ ID(b) TIMES ID(x) PLUS ID(c) SEMICOLON EOF
Lexical Analyzer (Scanner)
©Universidade do Algarve44
Análise Lexical
/* exemploInt sum(int A[], int N) { Int i, 5sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum;}
Tratamento de erros
Não é uma palavra reservada nem um identificador
Falta terminar comentário */
©Universidade do Algarve45
Análise Sintáctica
Syntax Analyzer (Parser)
Árvore sintáctica
Syntax Analyzer (Parser)
Lexical Analyzer (Scanner)
Cadeia de Tokens
Programa (cadeia de caracteres)
b x
y
=
+
* c
b x
y
=
+
* c
©Universidade do Algarve46
Análise Sintáctica
b x*
=
y
<stmt>
<expr><ident>
<ident>
<expr>
<ident><op>
y = b*x + c
+
<op>
c
<ident>
Árvore Sintáctica (concreta)
©Universidade do Algarve47
Análise Sintáctica
b x
y
=
+
* c
Árvore Sintáctica (abstracta): AST
y = b*x + c
©Universidade do Algarve48
Análise Sintáctica
Tratamento de erros
Int sum(int A[], int N)) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i] } retur sum;}}
Falta ‘;’
“retur” não é palavra reservada: dois identificadores seguidos?
Chaveta a mais
Parêntesis a mais
©Universidade do Algarve49
Análise Semântica
Semantic AnalyzerSemantic Analyzer
Syntax Analyzer (Parser)
Árvore sintáctica
Syntax Analyzer (Parser)
Lexical Analyzer (Scanner)
Cadeia de Tokens
Programa (cadeia de caracteres)
Representação intermédia
tmp1 = b*x;tmp2 = tmp1 + c;
©Universidade do Algarve50
Análise Semântica
Tratamento de erros
boolean sum(int A[], int N) { Int i, sum; For(i=0; i<N; i++) { sum1 = sum + A[i]; } return sum;}
Não foi atribuído valor inicial a sum
Tipo da variável retornada não confere com a declaração do cabeçalho da função
Variável não declarada
©Universidade do Algarve51
Optimização de código
Code OptimizerCode Optimizer
Representação intermédia optimizada
Representação intermédia
Semantic AnalyzerSemantic Analyzer
Syntax Analyzer (Parser)
Árvore sintáctica
Syntax Analyzer (Parser)
Lexical Analyzer (Scanner)
Cadeia de Tokens
Programa (cadeia de caracteres)
©Universidade do Algarve52
Geração de código assembly
Code Generator
Código Assembly
Code Generator
Code OptimizerCode Optimizer
Representação intermédia optimizada
Representação intermédia
Semantic AnalyzerSemantic Analyzer
Syntax Analyzer (Parser)
Árvore sintáctica
Syntax Analyzer (Parser)
Lexical Analyzer (Scanner)
Cadeia de Tokens
Programa (cadeia de caracteres)
mult $t4, $t1,$t2;Add $t4, $t4, $t3;
©Universidade do Algarve53
TPC
Apresente os resultados de cada etapa de compilação para a expressão:y = a*x*x+b*x+c;
©Universidade do Algarve54
Análise Lexical
Compiladores
João M. P. Cardoso
©Universidade do Algarve55
Linguagens Formais
Linguagem natural Ambígua
• problema no processamento das linguagens• dependência do contexto permite mensagens
mais curtas Linguagem (artificial) formal
Obedece a regras apresentadas rigorosamente através do recurso a formalismos apropriados
Regras garantem a não ambiguidade da linguagem
©Universidade do Algarve56
Linguagens formais e definição da linguagem
Necessidade de definir precisamente uma linguagem
Estrutura por camadas na definição da linguagem Começar por um conjunto de símbolos da linguagem Estrutura lexical - identifica “palavras” na
linguagem (cada palavra é uma sequência de símbolos)
Estrutura Sintáctica - identifica “frases” na linguagem (cada frase é uma sequência de palavras)
Semântica – significado do programa (especifica que resultados deverão ser obtidos para as entradas)
©Universidade do Algarve57
Especificação Formal de Linguagens
Expressões regulares (método generativo)Existem casos que não se podem descrever
por expressões regulares Autómatos finitos (método por reconhecimento)
Não deterministas (NFAs)Deterministas (DFAs) Implementam qualquer expressão regular
©Universidade do Algarve58
Especificação de Estruturas Lexicais utilizando Expressões regulares Dado um vocabulário/alfabeto = conjunto
de símbolos Expressões regulares são construídas com:
- string vazia Qualquer símbolo do alfabeto r1r2 – expressão regular r1 seguida de r2
(sequência): concatenação (às vezes utiliza-se o .)
r1| r2 – expressão regular r1 ou r2 (selecção) r* - sequência iterativa ou selecção | r | rr |
… Parêntesis para indicar precedências
• Prioridade: *, ., |
©Universidade do Algarve59
Expressões regulares
Reescrever a expressão regular até se obter apenas uma sequência de letras (string) Exemplo
(0 | 1)*”.”(0 | 1)*(0 | 1)(0 | 1)*”.”(0 | 1)*1(0 | 1)*”.”(0 | 1)*1”.”(0 | 1)*1”.”(0 | 1)(0 | 1)*1”.”(0 | 1)1”.”0
Regras gerais
1) r1| r2 r1
2) r1| r2 r2
3) r* rr*
4) r*
©Universidade do Algarve60
Não determinismo na geração
Diferente aplicação de regras pode conduzir a resultados finais diferentes
Exemplo 1(0 | 1)*”.”(0 | 1)*(0 | 1)(0 | 1)*”.”(0 | 1)*1(0 | 1)*”.”(0 | 1)*1”.”(0 | 1)*1”.”(0 | 1)(0 | 1)*1”.”(0 | 1)1”.”0
Exemplo 2(0 | 1)*”.”(0 | 1)*(0 | 1)(0 | 1)*”.”(0 | 1)*0(0 | 1)*”.”(0 | 1)*0”.”(0 | 1)*0”.”(0 | 1)(0 | 1)*0”.”(0 | 1)0”.”1
©Universidade do Algarve61
Linguagem gerada por expressões regulares
Conjunto de todas as strings geradas pela expressão regular é uma linguagem de expressões regulares
Em geral, uma linguagem pode ser infinita
String na linguagem é chamada de token
©Universidade do Algarve62
Linguagens e Expressões Regulares
Exemplos: = {0, 1, “.” }
(0 | 1)*”.”(0 | 1)* - números binários de vírgula flutuante
(00)* - strings de zeros com comprimento par
(1*01*01*)* - strings com um número par de zeros
= {a, b, c, 0, 1, 2 } (a | b | c)(a | b | c | 0 | 1 | 2)* - identificadores
alfanuméricos (0|1|2)* - números ternários
©Universidade do Algarve63
Expressões Regulares
Outras construções:r+ - uma ou mais ocorrências de r: r |
rr | rrr ...• Equivalente a: r.r*
r? – zero ou uma ocorrência de r: (r | )[ ] – classes de símbolos:
• [ac] o mesmo que: (a | c)• [a-c] o mesmo que: (a | b | c)
©Universidade do Algarve64
Expressões Regulares
Especifique a linguagem de Inteiros Especifique a linguagem de
identificadores (uma letra seguida de sequências de letras e algarismos)
Enumere propriedades algébricas das expressões regulares
Dê exemplos de linguagens que não podem ser especificadas por expressões regulares
©Universidade do Algarve65
Análise Lexical
Compiladores
João M. P. Cardoso
©Universidade do Algarve66
Especificação Formal de Linguagens
Expressões regulares (método generativo)Existem casos que não se podem
descrever por expressões regulares Autómatos finitos (método por
reconhecimento)Não deterministas (NFAs)Deterministas (DFAs)Implementam qualquer expressão regular
©Universidade do Algarve67
Autómatos Finitos
Conjunto de estados 1 estado de Entrada 1 ou mais estados terminais (ou estados de
aceitação) Alfabeto de símbolos: (inclui o símbolo de
string de tamanho zero: ) Transições entre estados despoletadas pela
ocorrência de um determinado símbolo do alfabeto
Transições rotuladas com símbolos
©Universidade do Algarve68
Autómatos Finitos
Exemplo
Estado de início
Estado de aceitação
1
0
1
0
(0 | 1)*”.”(0 | 1)*
©Universidade do Algarve69
Aceitação de string pelo autómato Reconhecimento através da execução do autómato
Começar com o estado de início e com o primeiro símbolo da string
Guardar estado corrente e o símbolo corrente da string
Em cada passo, fazer corresponder símbolo corrente com a transição rotulada com esse símbolo
Continuar até ao fim da string ou até que a correspondência falhe
Se o estado final é estado de aceitação, então o autómato aceita a string
Linguagem do autómato é constituída pelo conjunto de strings que ele aceita
©Universidade do Algarve70
Exemplo
11.0
Símbolo corrente
Estado de início
Estado de aceitação
1
0
1
0
©Universidade do Algarve71
Exemplo
11.0
Símbolo corrente
Estado de início
Estado de aceitação
1
0
1
0
©Universidade do Algarve72
Exemplo
11.0
Símbolo corrente
Estado de início
Estado de aceitação
1
0
1
0
©Universidade do Algarve73
Exemplo
11.0
Símbolo corrente
Estado de início
Estado de aceitação
1
0
1
0
©Universidade do Algarve74
Exemplo
11.0
Símbolo corrente
Estado de início
Estado de aceitação
1
0
1
0
©Universidade do Algarve75
Exemplo
11.0
Símbolo corrente String aceite!
Estado de início
Estado de aceitação
1
0
1
0
©Universidade do Algarve76
Autómatos Finitos
NFA: Autómato Finito não Determinista De um determinado estado, a mesma
ocorrência pode conduzir a estados distintos DFA
O mesmo que NFA com a seguinte ressalva:• De um determinado estado, a ocorrência de um
símbolo não pode ter mais do que um laço, e por isso não pode levar a estados diferentes.
©Universidade do Algarve77
NFA vs DFA
DFA Sem transições No máximo uma transição de cada estado
para cada símbolo
NFA – nenhuma destas restrições
a
a
a
b
OKNOTOK
©Universidade do Algarve78
Autómatos Finitos
Autómatos Finitos Deterministas (DFAs)Implementações mais rápidas do que
para os NFAs, masMaior complexidade do autómato
©Universidade do Algarve79
Generativo vs Reconhecimento
Expressões regulares são um mecanismo para gerar as Strings da linguagem
Autómatos são um mecanismo para reconhecer se uma String específica pertence à linguagem
Abordagem standard Usar expressões regulares aquando da definição da
linguagem Tradução automática para autómatos para a
implementação da analisador lexical
©Universidade do Algarve80
Análise Lexical
Compiladores
João M. P. Cardoso
©Universidade do Algarve81
Da Expressão Regular ao Autómato
Tradução da Expressão Regular para um Autómato
Implementação do Autómato
©Universidade do Algarve82
Da Expressão Regular ao Autómato
Construção por indução estrutural Dada uma expressão regular r arbitrária, Assumir que podemos convertê-la para um
autómato com Um estado início Um estado de aceitação
Mostrar como converter todas as construções de expressões regulares para produzir um autómato com Um estado de início Um estado de aceitação
©Universidade do Algarve83
Construtores Básicos
aa
©Universidade do Algarve84
Sequência
r1.r2
r1 r2
©Universidade do Algarve85
Selecção
r1 | r2
r1
r2
©Universidade do Algarve86
Asterisco de Kleene
r*
r
©Universidade do Algarve87
Regras de Conversão
r1 r2
r1.r2
r1
r2
r1 | r2
r
r*a a
©Universidade do Algarve88
Conversão
A conversão de uma expressão regular para um autómato baseada nas regras apresentadas produz um NFA
Nós queremos ter um DFA para facilitar o algoritmo de reconhecimento
Pode-se converter do NFA para o DFA o DFA pode ser exponencialmente maior do
que o NFA• Teoricamente um NFA com N estados pode
originar um DFA com 2N-1 estados A optimização do DFA resultante envolveria
eliminação de estados equivalentes
©Universidade do Algarve89
Conversão NFA para DFA
O DFA tem um estado para cada subconjunto de estados no NFA O estado início do DFA corresponde ao conjunto de estados
alcançáveis seguindo as transições do estado início no NFA Um estado do DFA é de aceitação se um estado de aceitação do
NFA está no conjunto de estados agrupados Para determinar a transição despoletada pelo símbolo “a” de um dado
estado D do DFA Colocar S como conjunto vazio Encontrar o conjunto N de estados D no NFA
• Para todos os estados do NFA em N• Determinar o conjunto de estados N’ em que o NFA pode
estar depois de reconhecer “a”• Actualizar S com a união de S com N’
Se S não é vazio, há uma transição para “a” de D para o estado no DFA que tem o conjunto de estados S do NFA
Caso contrário, não há nenhuma transição “a” de D
©Universidade do Algarve90
NFA para DFA Exemplo: (0 | 1)*.(0|1)*
1 2
3
4
5
6
1
0
7
8
9 10
11
12
13
14
1
0
15
16
.
1,2,3,4,8
5,7,2,3,4,8
6,7,2,3,4,8
9,10,11,12,16
13,15,10,11,12,16
14,15,10,11,12,16
1
0
1
0
10
10
00
1 1
©Universidade do Algarve91
Estrutura Lexical nas Linguagens
Cada linguagem tem várias categorias de palavras. Numa linguagem de programação: Palavras chave (if, while) Operações aritméticas (+, -, *, /) Números inteiros (1, 2, 45, 67) Números em vírgula flutuante (1.0, .2, 3.337) Identificadores (abc, i, j, ab345)
Tipicamente tem-se uma categoria lexical para cada palavra chave e/ou categoria
Cada categoria lexical é definida por expressões regulares
©Universidade do Algarve92
Exemplo de Categorias Lexicais (exemplo)
Palavra_chave_if = if Palavra_chave_while = while Operador = +|-|*|/ Inteiro = [0-9] [0-9]* Float = [0-9]*. [0-9]* Identificador = [a-z]([a-z]|[0-9])* Na análise sintáctica vamos utilizar
estas categorias
©Universidade do Algarve93
Análise Lexical
Compiladores
João M. P. Cardoso
©Universidade do Algarve94
Da Expressão Regular ao Analisador Lexical
Tradução da Expressão Regular para um NFA
Do NFA para o DFA Implementação em software do
DFA
©Universidade do Algarve95
Interpretador do DFA
State = DFA initial state;inputChar = getchar();While(inputChar) {
State = trans(State, inputChar]);inputChar = getchar();
}If(State é um estado de aceitação) realizar acção relativa ao estado State (reconheceu
String)Else realizar outra acção
Algoritmo do Interpretador
©Universidade do Algarve96
NFA para DFA Exemplo: (0 | 1)*.(0|1)*
1 2
3
4
5
6
1
0
7
8
9 10
11
12
13
14
1
0
15
16
.
1,2,3,4,8
5,7,2,3,4,8
6,7,2,3,4,8
9,10,11,12,16
13,15,10,11,12,16
14,15,10,11,12,16
1
0
1
0
10
10
00
1 1
©Universidade do Algarve97
DFA
Tabela de transição de estados (adicionamos o estado 0 – estado morto - para as transições não apresentadas)
1
2
3
4
5
6
1
0
1
0
10
10
00
1 1
Estado
Actual
Próximo Estado
“0” “1” “.”
0 0 0 0
1 3 2 4
2 3 2 4
3 3 2 4
4 6 5 0
5 6 5 0
6 6 5 0
©Universidade do Algarve98
Implementação do DFA
Implementar a tabela de transições de estados com um array bidimensional
1
2
3
4
5
6
1
0
1
0
10
10
00
1 1
Estado
Actual
Próximo Estado
“0” “1” “.”
0 0 0 0
1 3 2 4
2 3 2 4
3 3 2 4
4 6 5 0
5 6 5 0
6 6 5 0
©Universidade do Algarve99
Implementação do DFA
O array fornecerá uma indexação por número de estado e símbolo
Supondo a possibilidade de 256 símbolos: int Edge[NumStates][256] (NumStates = 7)
1
2
3
4
5
6
1
0
1
0
10
10
00
1 1
Estado
Actual
Próximo Estado
“0” “1” “.”
0 0 0 0
1 3 2 4
2 3 2 4
3 3 2 4
4 6 5 0
5 6 5 0
6 6 5 0
©Universidade do Algarve100
Implementação do DFA
int Edge[NumStates][256] ={ /*... 0 1 2 ... 9 ...
“.” ... *//* estado 0 */ {..., 0, 0, 0, ..., 0, ..., 0, ...},/* estado 1 */ {..., 3, 2, 0, ..., 0, ..., 4, ...},/* estado 2 */ {..., 3, 2, 0, ..., 0, ..., 4, ...}, /* estado 3 */ {..., 3, 2, 0, ..., 0, ..., 4, ...}, /* estado 4 */ {..., 6, 5, 0, ..., 0, ..., 0, ...}, /* estado 5 */ {..., 6, 5, 0, ..., 0, ..., 0, ...}, /* estado 6 */ {..., 6, 5, 0, ..., 0, ..., 0, ...}}
Exemplo:Edge[3][(int) ‘.’] 4
Estado
Actual
Próximo Estado
“0” “1” “.”
0 0 0 0
1 3 2 4
2 3 2 4
3 3 2 4
4 6 5 0
5 6 5 0
6 6 5 0
©Universidade do Algarve101
Implementação do DFA
Um array unidimensional traduz o número de estado no número da acção a realizar:
// estado 0 1 2 3 4 5 6int final[NumStates] = {0, 0, 0, 0, 1, 1, 1}
Exemplo:
final[3] 3 (logo deve ser realizada a acção 3)
Estado
Actual
Próximo Estado
“0” “1” “.”
0 0 0 0
1 3 2 4
2 3 2 4
3 3 2 4
4 6 5 0
5 6 5 0
6 6 5 0
©Universidade do Algarve102
Implementação do DFA
State = 1; // estado inicial do DFAinputChar = input.read();While(inputChar) {
State = edge[State][inputChar];inputChar = input.read();
}If(final[State] == 1) realizar acção (reconheceu String)Else realizar outra acção
Programação do Interpretador
©Universidade do Algarve103
Optimizações na implementação do DFA
Representação da tabela de transições por um array bidimensional pode não ser eficiente em termos de espaço de memória
Mapeamento indirecto:int Edge[NumStates][4] = { /* estado 0 */ {0, 0, 0, 0},/* estado 1 */ {3, 2, 4, 0},/* estado 2 */ {3, 2, 4, 0}, /* estado 3 */ {3, 2, 4, 0}, /* estado 4 */ {6, 5, 0, 0}, /* estado 5 */ {6, 5, 0, 0}, /* estado 6 */ {6, 5, 0, 0}}
/*... “0” “1” “2” “3” ... 9 ... “.” ... */Int map[256] = { ..., 0, 1, 3, 3,... 3, ... 2, ...}Exemplo:
Edge[3][(int) ‘.’] é feito assim:Edge[3][map[(int) ‘.’]]
©Universidade do Algarve104
Optimizações na implementação do DFA
Para o exemplo apresentado:Permitiu passar de um array com 256
7 (1792) elementos para um array com 256 e outro array com 7 4 (28) elementos
De 1792 para 284 elementos Existem outras formas de
optimização...
©Universidade do Algarve105
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve106
Sintaxe em Linguagens de Programação
Linguagens regulares deficientes para especificar a sintaxe das linguagens de programação
Porquê? Construções com sintaxe encadeada (a+(b-c))*(d-(x-(y-z))) if (x < y) if (y < z) a = 5 else a = 6 else a = 7
Linguagens regulares não têm o estado requerido para modelar encadeamento
Nenhuma expressão regular para especificar linguagens com expressões com parêntesis
©Universidade do Algarve107
Solução
Gramáticas independentes do contextoReconhecimento realizado por
autómatos finitos equipados com uma pilha
©Universidade do Algarve108
Gramáticas Notação de Backus Naur Form (BNF) para especificar a
gramática Símbolos terminais: letras maiúsculas (por vezes a
negrito) e Símbolos não-terminais: iniciados por uma letra maiúscula
Ou, símbolos não-terminais delimitados por < e > Numa produção (derivação) os lados esquerdo e direito
são separados pelo símbolo ou ::=• Ex.: Expr Term OP Term
Produções alternativas (p1, p2, p3, ..., pn) são representadas por p1 | p2 | p3 |...| pn
• Ex.: Literal BINARIO | OCTAL | INT | FLOAT Se o lado direito de uma produção não contém nenhum
símbolo, então escreve-se • Ex.: Palavra
©Universidade do Algarve109
Gramáticas
EBNF, ou BNF estendidoInclui { } para representar 0 ou mais
repetições de símbolose [ ] para representar elementos
opcionais
©Universidade do Algarve110
Gramáticas (independentes do contexto)
Conjunto de símbolos terminais{ OP, INT, OPEN, CLOSE }Cada símbolo terminal definido por
expressões regulares Conjunto de símbolos não-terminais
{ Start, Expr } Conjunto de produções
Um único símbolo não-terminal no lado esquerdo da produção (LHS)
Sequência de símbolos terminais e não-terminais no lado direito da produção (RHS)
OP = + | - | * | /
INT = [0-9] [0-9]*
OPEN = (
CLOSE = )
Start Expr
Expr Expr OP Expr
Expr INT
Expr OPEN Expr CLOSE
©Universidade do Algarve111
Jogo da produção/derivação
Dada a frase correnteComeçar pelo símbolo não-terminal StartRepetir até não haver símbolos não-terminais
Seleccionar um símbolo não-terminalSeleccionar uma produção com símbolos não-terminais em LHSSubstituir símbolo não-terminal com o RHS da produção
Substituir a expressão regular com as Strings correspondentesString gerada está na linguagem
Nota: selecções diferentes produzem Strings diferentes
©Universidade do Algarve112
Produção/Derivação
Start
Expr
Expr OP Expr
OPEN Expr CLOSE OP Expr
OPEN Expr OP Expr CLOSE OP Expr
OPEN INT OP Expr CLOSE OP Expr
OPEN INT OP Expr CLOSE OP INT
OPEN INT OP INT CLOSE OP INT
( 2 - 1 ) + 1
OP = +|-|*|/
INT = [0-9] [0-9]*
OPEN = (
CLOSE = )
1) Start Expr
2) Expr Expr OP Expr
3) Expr INT
4) Expr OPEN Expr CLOSE
©Universidade do Algarve113
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve114
Árvore Sintáctica
Nós internos: símbolos não-terminais Folhas: símbolos terminais Laços:
De símbolos não-terminais do LHS da produção
A nós do RHS da produção Captura a derivação da frase (String)
©Universidade do Algarve115
Árvore Sintáctica para (2-1)+1
Start
Expr
ExprExprOP
+OPEN(
CLOSE)
ExprINT1
OP -
Expr
INT2
Expr
INT1
©Universidade do Algarve116
Ambiguidade numa Gramática
Múltiplas derivações (como consequência: múltiplas árvores sintácticas) para a mesma String
Derivação e árvore sintáctica reflecte usualmente a semântica do programa
Ambiguidade na gramática reflecte muitas das vezes ambiguidades na semântica da linguagem (considerada indesejável)
©Universidade do Algarve117
Exemplo de ambiguidade
Duas árvores sintácticas para 2-1+1
Start
Expr
Expr ExprOP+
Expr ExprOP-
INT2
INT1
INT1
Start
Expr
ExprExpr OP-
Expr ExprOP+
INT1
INT1
INT2
Árvore correspondente a (2-1)+1
Árvore correspondente a 2-(1+1)
©Universidade do Algarve118
Eliminação de ambiguidade
Solução: modificar gramática Faz todos os operadores com
associação à esquerda
Gramática Original
Start Expr
Expr Expr OP Expr
Expr INT
Expr OPEN Expr CLOSE
Gramática Modificada
Start Expr
Expr Expr OP INT
Expr INT
Expr OPEN Expr CLOSE
©Universidade do Algarve119
Árvore sintáctica para a gramática
Apenas uma árvore sintáctica para: 2-1+1
Start
Expr
Expr OP+
Expr OP-
INT2
INT1
INT1
Start
Expr
ExprExpr OP-
Expr ExprOP+
INT1
INT1
INT2
Árvore sintáctica válidaÁrvore sintáctica inválida
©Universidade do Algarve120
Violação de prioridade
Todos os operadores associam à esquerda
Viola prioridade de * sobre +2-3*4 associa como (2-
3)*4
Start
Expr
Expr OP*
Expr OP-
INT2
INT3
INT4
Árvore sintáctica para2-3*4
©Universidade do Algarve121
Resolver prioridade
Gramática Original
OP = + | - | * | /
INT = [0-9] [0-9]*
OPEN = (
CLOSE = )
Start Expr
Expr Expr OP INT
Expr INT
Expr OPEN Expr CLOSE
Gramática ModificadaOP1 = + | -
OP2 = * | /
INT = [0-9] [0-9]*
OPEN = (
CLOSE = )
Start Expr
Expr Expr OP1 Term
Expr Term
Expr OPEN Expr CLOSE
Term Term OP2 INT
Term INT
©Universidade do Algarve122
Modificação nas Árvores Sintácticas
Start
Expr
Expr OP*
Expr OP-
INT2
INT3
INT4
Velha Árvore sintáctica para 2-3*4
Start
Expr
Expr OP1-
Term
INT2
Nova Árvore sintáctica para 2-3*4
Term
Term OP2*
INT3
INT4
©Universidade do Algarve123
Ideia Geral
Agrupar operadores por níveis de prioridade * e / estão no nível de topo + e – estão no nível a seguir
Símbolo não-terminal para cada nível de prioridade Term é não-terminal para * e / Expr é não-terminal para + e -
Pode-se fazer associatividade dos operadores à esquerda ou à direita em cada nível
Generalizar para níveis arbitrários de prioridades
©Universidade do Algarve124
Exercícios (TPC) Especificar utilizando a representação BNF
gramáticas correspondentes às expressões regulares: [0-9]+ e [0-9]*
Dada a gramática:
NUM = [0-9]+
ID = [A-Za-Z][0-9A-Za-z]*
Expr Expr “+” Term | Expr “–” Term | Term
Term Term “*” Factor | Term “/” Factor | Factor
Factor Primary “^” Factor | Primary
Primary “-”Primary | Element
Element “(“ Expr “)” | NUM | ID
Quais as árvores sintácticas para:
• 5-2*3• y^3
©Universidade do Algarve125
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve126
Manuseando estruturas if-then-else
Start Stat
Stat IF Expr THEN Stat ELSE Stat
Stat IF Expr THEN Stat
Stat ...
©Universidade do Algarve127
Árvore Sintáctica
Considere o enunciado: if e1 then if e2 then s1 else s2
©Universidade do Algarve128
Árvore Sintáctica Duas Árvores
Sintácticas
Stat
IF Expr Stat
IF Expr Stat ELSEe1
e2
Stat
s1 s2
IF Expr
Stat
IF Expr Stat ELSE
e2
e1
Stat
s1s2
Qual é a correcta?
THEN
THEN
THEN
©Universidade do Algarve129
Leituras alternativas
Gramática ambígua Árvore sintáctica 1
if e1
if e2 s1
else s2
Árvore sintáctica 2
if e1
if e2 s1
else s2
©Universidade do Algarve130
Gramática modificada
Goal Stat
Stat WithElse
Stat LastElse
WithElse IF Expr THEN WithElse ELSE WithElse
WithElse ...
LastElse IF Expr THEN Stat
LastElse IF Expr THEN WithElse ELSE LastElse
©Universidade do Algarve131
Gramática modificada
Ideia básica: controlar quando um IF sem ELSE pode ocorrerNo nível de topo do enunciadoOu como último numa sequência de
enunciados if then else if then ...
©Universidade do Algarve132
Analisador Sintáctico
Converte programas numa árvore sintáctica Pode ser programado do zero! Ou construído automaticamente por um gerador de
“parsers” Aceitam uma gramática como entrada Produzem um analisador sintáctico como resultado
Problema prático A Árvore Sintáctica para a gramática modificada é
complicada Gostaríamos de começar com uma árvore sintáctica
mais intuitiva
©Universidade do Algarve133
Solução
Sintaxe Abstracta versus Concreta Sintaxe abstracta corresponde ao meio
intuitivo de pensar a estrutura do programa• Omite detalhes como palavras-chave supérfluas
que estão lá para tornar a linguagem ambígua• Sintaxe abstracta pode ser ambígua
Sintaxe Concreta corresponde à gramática completa para utilizada para analisar sintacticamente a linguagem
Os analisadores sintácticos são muitas das vezes escritos para produzirem Árvores Sintácticas Abstractas (ASTs)
©Universidade do Algarve134
ASTs (Árvores Sintácticas Abstractas)
Começar com uma gramática intuitiva mas ambígua
Modificar a gramática para a tornar não- ambígua Árvores sintácticas concretas Menos intuitivas
Converter as árvores sintácticas concretas em ASTs Correspondem à gramática intuitiva para a
linguagem Mais simples de manipular pelo programa
©Universidade do Algarve135
Exemplo
Gramática intuitiva mas ambígua
OP = * | / | + | -
INT = [0-9] [0-9]*
Start Expr
Expr Expr OP Expr
Expr INT
Gramática não-ambíguaOP1 = + | -
OP2 = * | /
INT = [0-9] [0-9]*
OPEN = (
CLOSE = )
Start Expr
Expr Expr OP1 Term
Expr Term
Term OPEN Expr CLOSE
Term Term OP2 INT
Term INT
©Universidade do Algarve136
ExemploStart
Expr
Expr
Expr OP2-
INT3
Árvore sintáctica concreta para
(2-3)*4 Term
OP1*
INT4
Term
OPEN CLOSE
Term
INT2
Term
Start
Expr
Expr OP*
ExprOP
-INT2
Expr
INT4
Expr
INT3
Árvore sintáctica abstracta para
(2-3)*4
• Utiliza gramática intuitiva• Elimina terminais supérfluos
• OPEN, CLOSE, etc.
©Universidade do Algarve137
Exemplo
Start
Expr
Expr OP*
OP-
INT2
INT4
INT3
AST para (2-3)*4
Ainda mais
simplificada
Árvore sintáctica abstracta para
(2-3)*4 Start
Expr
Expr OP*
ExprOP
-INT2
Expr
INT4
Expr
INT3
©Universidade do Algarve138
Sumário
Níveis da estrutura lexicais e sintácticos Lexicais – expressões regulares e
autómatos Sintácticos – gramáticas
Ambiguidades na gramática Gramáticas modificadas Árvores Sintácticas Abstractas (ASTs)
Papel generativo versus papel reconhecedor Generativo mais conveniente para
especificação Reconhecedor requerido na implementação
©Universidade do Algarve139
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve140
Vocabulário gramatical
Derivação à esquerda (Leftmost) Expandir sempre o símbolo não terminal
mais à esquerda que resta
Derivação à direita (Rightmost) Expandir sempre o símbolo não terminal
mais à direita que resta
Aplicar primeiro a produção aqui
NT1 T1 T2 T3 NT2 NT3
E só depois aos outros símbolos não-terminais
©Universidade do Algarve141
Ponto Inicial
Assumir que a análise lexical produziu uma sequência de tokens (símbolos terminais) Cada token tem um tipo e um valor Tipos correspondem a símbolos terminais Valores correspondem ao conteúdo do token
lido Exemplo
INT(549) – token que identifica um inteiro de valor lido 549
IF – palavra chave “if” sem necessidade de valor
OP(+) – operador com valor +
©Universidade do Algarve142
Abordagem Básica
Começar pelo símbolo Start ou pela primeira produção
Construir uma derivação leftmost Se o símbolo leftmost é não-terminal,
seleccionar uma produção e aplicá-la Se o símbolo leftmost é terminal, fazer
corresponder com a entrada Se todos os terminais foram correspondidos,
foi encontrada uma derivação que aceita a String!
Chave: encontrar as produções correctas para os símbolos não-terminais
©Universidade do Algarve143
Gramática do Exemplo
INT = [0-9]+
Start Expr
Expr Expr “+” Term
Expr Expr “-” Term
Expr Term
Term Term “*” INT
Term Term “/” INT
Term INT
Conjunto de tokens (símbolos terminais):
{ +, -, *, /, INT }
©Universidade do Algarve144
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Posição corrente na árvore sintáctica
Forma sentencial
Entrada que falta2-2*2
Start
©Universidade do Algarve145
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2-2*2
Expr
Posição corrente na árvore sintáctica
Expr
Produção AplicadaStart Expr
©Universidade do Algarve146
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada a faltar2-2*2
Expr - TermExpr
Produção AplicadaExpr Expr - Term
TermExpr -
©Universidade do Algarve147
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2-2*2
Term - TermExpr
Produção AplicadaExpr Term
TermExpr -
Term
©Universidade do Algarve148
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2-2*2
INT - TermExpr
Produção AplicadaTerm INT
TermExpr -
INT
Term
©Universidade do Algarve149
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2-2*2
2 - TermExpr
TermExpr -
INT 2
Term
Token corresponde!
©Universidade do Algarve150
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta-2*2
2 - TermExpr
TermExpr -
INT 2
Term
Token corresponde!
©Universidade do Algarve151
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2*2
2 - TermExpr
TermExpr -
INT 2
Term
©Universidade do Algarve152
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2*2
2 – Term*INTExpr
TermExpr -
INT 2
Term Term INT*
Produção AplicadaTerm Term * INT
©Universidade do Algarve153
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2*2
2 – INT*INTExpr
TermExpr -
INT 2
Term Term INT*
Produção AplicadaTerm INT
INT
©Universidade do Algarve154
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2*2
2 – 2*INTExpr
TermExpr -
INT 2
Term Term INT*
INT 2
Token corresponde!
©Universidade do Algarve155
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta*2
2 – 2*INTExpr
TermExpr -
INT 2
Term Term INT*
INT 2
Token corresponde!
©Universidade do Algarve156
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2
2 – 2*INTExpr
TermExpr -
INT 2
Term Term INT*
INT 2
©Universidade do Algarve157
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta2
2 – 2*2Expr
TermExpr -
INT 2
Term Term INT 2*
INT 2
Token corresponde!
©Universidade do Algarve158
Analisador Sintáctico para a gramática exemplo
StartÁrvore Sintáctica
Forma sentencial
Entrada que falta
2 – 2*2Expr
TermExpr -
INT 2
Term Term INT 2*
INT 2
Análise sintáctica terminou
©Universidade do Algarve159
Sumário
Três acções (mecanismos) Aplicar produção para expandir o símbolo
não-terminal corrente na árvore sintáctica Casar o símbolo terminal corrente Aceitar a análise sintáctica como correcta
Qual a produção a utilizar por cada símbolo não-terminal?
Uma abordagem: Backtracking Tenta uma alternativa Quando for claro que a alternativa falhou
tentar outra alternativa
©Universidade do Algarve160
Analisador Sintáctico Preditivo
Alternativa ao backtracking Muito útil para linguagens de programação,
que podem ser desenhadas para facilitar a análise
Ideia básica Ver à frente na sequência de tokens Decisão de qual a produção a aplicar
baseada nos tokens seguintes Utilizaremos a profundidade de um token no
mecanismo de ver à frente
©Universidade do Algarve161
Gramática Exemplo
Start Expr
Expr Term Expr’
Expr’ “+” Term Expr’
Expr’ “-” Term Expr’
Expr’ Term INT Term’
Term’ “*” INT Term’
Term’ “/” INT Term’
Term’
INT = [0-9]+
Conjunto de tokens (símbolos terminais):
{ +, -, *, /, INT }
©Universidade do Algarve162
Pontos de Escolha
Assumir que Term’ é a posição corrente na árvore sintáctica
3 produções diferentes a aplicar
Term’ “*” INT Term’
Term’ “/” INT Term’
Term’ Utilizar o próximo token para decidir
Se o próximo token for *, aplicar Term’ * Int Term’ Se o próximo token for /, aplicar Term’ / Int Term’ De outro modo, aplicar Term’
©Universidade do Algarve163
Múltiplas Produções com o mesmo prefixo no RHS
Gramática Exemplo
Nt IF THEN
Nt IF THEN ELSE Assumir que Nt é a posição corrente
na árvore sintáctica e IF é o próximo token
Qual a produção a aplicar?
©Universidade do Algarve164
Solução: factorizar a gramática à esquerda
Nova gramática factoriza o prefixo comum numa única produçãoNt IF THEN Nt’Nt’ ELSENt’
Nenhuma escolha quando o próximo token é um IF
Todas as alternativas foram unificadas numa única produção
©Universidade do Algarve165
Símbolos não-terminais
E as produções com símbolos não-terminais?
Nt Nt1 1
Nt Nt2 2
Tem de se escolher com base nos primeiros terminais possíveis que Nt1 e Nt2
podem gerar E se Nt1 ou Nt2 podem gerar ?
Tem de se escolher com base em 1 e 2
©Universidade do Algarve166
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve167
Analisador sintáctico descendente
A recursividade à esquerda pode levar a ciclos infinitos!
Exemplo de produção: Term Term*Num
Passos potenciais na análise:
Term
Num*Term
Term
Term
Num*Term
Term
Num*
©Universidade do Algarve168
Analisador sintáctico descendente
A recursividade à esquerda pode levar a ciclos infinitos!
Solução: modificar gramática de modo a eliminar recursividade à esquerda
©Universidade do Algarve169
Eliminar recursividade à esquerda
Começar com produções da forma A A A Sequências , de símbolos
terminais e não-terminais que não começam com A
Repetição da aplicação: A A
forma a árvore sintáctica seguinte:
A
A
A
©Universidade do Algarve170
Eliminar recursividade à esquerda
Produções de substituição A A A R R é um novo símbolo não-terminal A R R R
A
A
A
R
R
R
Árvore inicial
Nova Árvore
©Universidade do Algarve171
Gramática do Exemplo
INT = [0-9]+
Start Expr
Expr Expr “+” Term
Expr Expr “-” Term
Expr Term
Term Term “*” INT
Term Term “/” INT
Term INT
Conjunto de tokens (símbolos terminais):
{ +, -, *, /, INT }
©Universidade do Algarve172
Gramática Modificada
Pedaço da gramática original
Term Term “*” INT
Term Term “/” INT
Term INT
Pedaço da gramática modificada
Term INT Term’
Term’ “*” INT Term’
Term’ “/” INT Term’
Term’
©Universidade do Algarve173
Gramática Modificada
Start Expr
Expr Term Expr’
Expr’ “+” Term Expr’
Expr’ “-” Term Expr’
Expr’ Term INT Term’
Term’ “*” INT Term’
Term’ “/” INT Term’
Term’
INT = [0-9]+
INT = [0-9]+
Start Expr
Expr Expr “+” Term
Expr Expr “-” Term
Expr Term
Term Term “*” INT
Term Term “/” INT
Term INT
©Universidade do Algarve174
Comparando as Árvores Sintácticas
Term
INT*
Term
INT*
INT
Term
INT Term’
INT* Term’
INT* Term’
Gramática original Gramática modificada
©Universidade do Algarve175
Eliminar Recursividade à esquerda
Necessária na análise sintáctica preditiva
Modifica o algoritmo de procura no espaço de produçõesElimina recursividade directa infinita Contudo: gramática modificada é
menos intuitiva Requer mais transformações para se
atingir AST desejada
©Universidade do Algarve176
Requer +transformações para se alcançar AST desejada
Árvore Sintáctica para: 2*3*4
Construir AST durante a derivação!
Term
INT2
Term’
INT3
* Term’
INT4
* Term’
Term
INT3
*
Term
INT4
*
INT2
Árvore Sintáctica ConcretaAST desejada
©Universidade do Algarve177
Sumário
Analisador sintáctico descendente Utilizar Lookahead para evitar Backtracking Modificar gramática para evitar
necessidade de inspecção de tokens muito à frente (lookahead): factorização
Modificar gramática para evitar ciclos infinitos
Como implementar o analisador sintáctico descendente?
©Universidade do Algarve178
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve179
Gramática Modificada
Pedaço da gramática original
Term Term “*” INT
Term Term “/” INT
Term INT
Pedaço da gramática modificada
Term INT Term’
Term’ “*” INT Term’
Term’ “/” INT Term’
Term’
©Universidade do Algarve180
Analisador Sintáctico Construído Manualmente Um procedimento por símbolo não-terminal Esse procedimento examina o símbolo
corrente de entrada Chama recursivamente procedimentos para
RHS da produção escolhida Os procedimentos retornam verdadeiro se a
análise sintáctica foi bem sucedida e retornam falso em caso contrário
©Universidade do Algarve181
Exemplo
Procedimento para o símbolo não-terminal Term:
Term() {if (token == INT) {
token = NextToken(); return TermPrime();
} else return false;}
Produções para o símbolo não-terminal Term:
Term INT Term’
A função NextToken() avança um token na sequência de tokens gerada pela análise lexical e retorna o token nessa posição.
©Universidade do Algarve182
Exemplo
Procedimento para o símbolo não-terminal Term’:
TermPrime() {if((token == ‘*’) || (token == ‘/’)) {
token = NextToken();if (token == INT) {
token = NextToken(); return TermPrime();
} else return false;} else return true;
}
©Universidade do Algarve183
Exemplo
Pseudo-código para a parte do programa responsável pela análise sintáctica:
...
token = NextToken();
Term();
...
©Universidade do Algarve184
Construção da Árvore Sintáctica
Cada procedimento retorna a secção da árvore sintáctica para a parte da String que analisou
Utilizar excepções para tornar clara a estrutura do código (outra forma será utilizar uma função de erro)
Em geral, podemos ajustar o algoritmo de análise sintáctica para satisfazer objectivos diferentes Tipicamente, produz AST em vez de árvore
sintáctica concreta
©Universidade do Algarve185
Construção da Árvore Sintáctica para o exemplo
Com geração de excepções:Term() {
if (token == INT) {oldToken = token; token = NextToken();node = TermPrime();if (node == NULL) return oldToken; else return new TermNode(oldToken, node);
} else throw SyntaxError;}
©Universidade do Algarve186
Construção da Árvore Sintáctica para o exemplo
Com geração de excepções:
TermPrime() {if ((token == ‘*’) || (token == ‘/’)) {
first = token; next = NextToken();if (next == INT) {
token = NextToken(); return new TermPrimeNode(first, next, TermPrime());
} else throw SyntaxError;} else return NULL;
}
©Universidade do Algarve187
Construção da Árvore Sintáctica para o exemplo
Sem geração de excepçõesTerm() {
if (token == INT) {oldToken = token; token = NextToken();node = TermPrime();if (node == NULL) return oldToken; else return new TermNode(oldToken, node);
} else error();}TermPrime() {
if ((token == ‘*’) || (token == ‘/’)) {first = token; next = NextToken();if (next == INT) {
token = NextToken(); return new TermPrimeNode(first, next, TermPrime());
} else error();} else return NULL;
}
©Universidade do Algarve188
Árvore Sintáctica para 2*3*4
Term
INT2
Term’
INT3
* Term’
INT4
* Term’
Term
INT3
*
Term
INT4
*
INT2
Árvore Sintáctica Concreta
AST desejada
©Universidade do Algarve189
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve190
Geração Directa da AST
TermPrime constrói uma árvore incompletaFalta leftmost childRetorna a raiz e o nó
incompleto (root, incomplete) =
TermPrime()Chamada com token: *Tokens que faltam: 3 * 4
Term
INT3
*
Term
INT4
*
root
incomplete
Falta parte esquerda que será construída pelo
procedimento de chamada
©Universidade do Algarve191
Código para Term
Term() {
if (token == INT) {
leftmostInt = token;
token = NextToken();
(root, incomplete) = TermPrime();
if (root == NULL) return leftmostInt;
incomplete.leftChild = leftmostInt;
return root;
} else throw SyntaxError;
}
INT2
token
2*3*4
Entrada
©Universidade do Algarve192
Código para Term
INT2
token
2*3*4
EntradaTerm() {
if (token == INT) {
leftmostInt = token;
token = NextToken();
(root, incomplete) = TermPrime();
if (root == NULL) return leftmostInt;
incomplete.leftChild = leftmostInt;
return root;
} else throw SyntaxError;
}
©Universidade do Algarve193
Código para Term
INT2
leftmostInt
2*3*4
EntradaTerm() {
if (token == INT) {
leftmostInt = token;
token = NextToken();
(root, incomplete) = TermPrime();
if (root == NULL) return leftmostInt;
incomplete.leftChild = leftmostInt;
return root;
} else throw SyntaxError;
}
©Universidade do Algarve194
Código para Term
Term
INT3
*
Term
INT4
*
INT2
root
incomplete
leftmostInt
2*3*4
EntradaTerm() {
if (token == INT) {
leftmostInt = token;
token = NextToken();
(root, incomplete) = TermPrime();
if (root == NULL) return leftmostInt;
incomplete.leftChild = leftmostInt;
return root;
} else throw SyntaxError;
}
©Universidade do Algarve195
Código para Term
Term
INT3
*
Term
INT4
*
INT2
root
incomplete
leftmostInt
2*3*4
EntradaTerm() {
if (token == INT) {
leftmostInt = token;
token = NextToken();
(root, incomplete) = TermPrime();
if (root == NULL) return leftmostInt;
incomplete.leftChild = leftmostInt;
return root;
} else throw SyntaxError;
}
©Universidade do Algarve196
Código para Term
Term
INT3
*
Term
INT4
*
INT2
root
incomplete
leftmostInt
2*3*4
EntradaTerm() {
if (token == INT) {
leftmostInt = token;
token = NextToken();
(root, incomplete) = TermPrime();
if (root == NULL) return leftmostInt;
incomplete.leftChild = leftmostInt;
return root;
} else throw SyntaxError;
}
©Universidade do Algarve197
Código para TermPrime
TermPrime() {if((token == ‘*’) || (token == ‘/’)) {
op = token; next = NextToken();if (next == INT) {
token = NextToken();(root, incomplete) = TermPrime();if (root == NULL) {
root = new ExprNode(NULL, op, next);return(root, root);
} else { newChild = new ExprNode(NULL, op, next);incomplete.leftChild = newChild;return(root, newChild);
}} else throw SyntaxError;
} else return(NULL,NULL);}
Filho da esquerda a ser colocado pelo procedimento de
chamada
©Universidade do Algarve198
Sumário
Analisador sintáctico descendente (Top-Down Parser)
Utilizar Lookahead para evitar Backtracking
O parser é um conjunto de procedimentos mutuamente recursivos
©Universidade do Algarve199
Terminologia Muitas técnicas de análise sintáctica
diferentesCada uma pode manusear algum
conjunto de CFGs (gramáticas independentes do contexto)
Categorização das técnicas
( )
©Universidade do Algarve200
Terminologia
Muitas técnicas de análise sintáctica diferentesCada uma pode manusear algum conjunto de
CFGs (gramáticas independentes do contexto)Categorização das técnicas
L – análise da esq. para a drt.R - análise da drt. para a esq.
( )
©Universidade do Algarve201
Terminologia
Muitas técnicas de análise sintáctica diferentesCada uma pode manusear algum conjunto
de CFGs (gramáticas independentes do contexto)
Categorização das técnicas
L – derivação pela esquerdaR – derivação pela direita
( )
©Universidade do Algarve202
Terminologia
Muitas técnicas de análise sintáctica diferentesCada uma pode manusear algum conjunto
de CFGs (gramáticas independentes do contexto)
Categorização das técnicas
Número de lookahead
( )
©Universidade do Algarve203
Terminologia
Muitas técnicas de análise sintáctica diferentesCada uma pode manusear algum conjunto de
CFGs (gramáticas independentes do contexto)Categorização das técnicas
Exemplos: LL(0), LR(1)Até agora: LL(1)Nas próximas aulas
• Análises LR(k)
( )L L k
©Universidade do Algarve204
Terminologia
LL(k)Descendente (top-down), preditivaConstrói derivação pela esquerda (leftmost) de
cima para baixo LR(k)
Ascendente (bottom-up), shift-reduceConstrói derivação pela direita (rightmost) de
baixo para cima
©Universidade do Algarve205
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve206
Analisador Sintáctico Ascendente
Mecanismo CentralAutómato Pushdown, que implementaParser Shift-reduce
©Universidade do Algarve207
Autómato Push-Down
Constituído por Pilha Pushdown (pode conter terminais e não-
terminais) Controlo por um autómato de estados finitos
Pode realizar uma de três acções: Shift:
• Desloca símbolo corrente da entrada para a pilha Reduce:
• Se o(s) símbolo(s) no topo da pilha “casa(m)” RHS de alguma das produções da gramática
• Pop (retirar) esses símbolos da pilha• Push não-terminal do LHS para a pilha
Aceita a entrada como pertencendo à linguagem
©Universidade do Algarve208
* ( + num )numnum
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
Pilha
String de entrada
Exemplo: Parser Shift-Reduce
©Universidade do Algarve209
* ( + num )numnum
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
Exemplo: Parser Shift-Reduce
©Universidade do Algarve210
SH
IFT
* ( + num )numnum
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
Exemplo: Parser Shift-Reduce
©Universidade do Algarve211
SH
IFT
* ( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
Exemplo: Parser Shift-Reduce
©Universidade do Algarve212
* ( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
RE
DU
CE
Exemplo: Parser Shift-Reduce
©Universidade do Algarve213
* ( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
RE
DU
CE
Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve214
* ( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
SH
IFT
Exemplo: Parser Shift-Reduce
©Universidade do Algarve215
( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
SH
IFT
*
Exemplo: Parser Shift-Reduce
©Universidade do Algarve216
( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
RE
DU
CE
*
Exemplo: Parser Shift-Reduce
©Universidade do Algarve217
( + num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
Exemplo: Parser Shift-Reduce
©Universidade do Algarve218
+ num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
Exemplo: Parser Shift-Reduce
©Universidade do Algarve219
+ num )num
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
Exemplo: Parser Shift-Reduce
©Universidade do Algarve220
+ num )
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Exemplo: Parser Shift-Reduce
©Universidade do Algarve221
+ num )
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
RE
DU
CE
Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve222
+ num )
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve223
num )
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
+
Exemplo: Parser Shift-Reduce
©Universidade do Algarve224
num )
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
Op
RE
DU
CE
+
Exemplo: Parser Shift-Reduce
©Universidade do Algarve225
num )
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
Op
+
Exemplo: Parser Shift-Reduce
©Universidade do Algarve226
)
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
num
Op
+
Exemplo: Parser Shift-Reduce
©Universidade do Algarve227
)
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
Op
+
Expr
RE
DU
CE
num
Exemplo: Parser Shift-Reduce
©Universidade do Algarve228
)
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num +
RE
DU
CE
num
Expr
Expr
Op
ExprExpr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve229
)
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
Op
+
Expr
num
Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve230
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*SH
IFT
(
num
Expr
Op
+
Expr
num
Expr
)
Exemplo: Parser Shift-Reduce
©Universidade do Algarve231
RE
DU
CE
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*
(
num
Expr
Op
+
Expr
num
Expr
)Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve232
RE
DU
CE
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*
(
num
Expr
Op
+
Expr
num
Expr
)Expr
Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve233
AC
CE
PT
!
num
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *Expr
Op
*
(
num
Expr
Op
+
Expr
num
Expr
)Expr
Expr
Exemplo: Parser Shift-Reduce
©Universidade do Algarve234
Conflitos que podem Ocorrer
Conflito Reduce/Reduce O topo da pilha pode “casar” com RHS de
produções múltiplas Qual a produção a utilizar na redução?
Conflito Shift/Reduce Pilha pode “casar” com RHS da produção Mas esse pode não ser o “casamento”
correcto Pode ser necessário deslocar a entrada e
encontrar mais tarde uma redução diferente
©Universidade do Algarve235
Expr Expr Op Expr
Expr (Expr)
Expr - Expr
Expr num
Op +
Op -
Op *
Conflitos
Gramática Original Nova Gramática
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr - Expr num
Op +
Op -
Op *
©Universidade do Algarve236
- numnum
ConflitosExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve237
- numnum
SH
IFT
ConflitosExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve238
- num
SH
IFT
num
ConflitosExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve239
- num
SH
IFT
Expr
RE
DU
CE
num
ConflitosExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve240
- num
SH
IFT
Expr
num
ConflitosExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve241
num
SH
IFT
Expr
num
-
ConflitosExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve242
num
Expr
num
-
Opções:Reduce
Reduce
Shift
Conflito shift/reduce/reduceExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve243
num
Expr
num
-
RE
DU
CE
Conflito shift/reduce/reduceExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
O que acontece ao escolher-se: Reduce
©Universidade do Algarve244
num
Expr
num -
SH
IFT
Expr
Conflito shift/reduce/reduceExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
O que acontece ao escolher-se: Reduce
©Universidade do Algarve245
Expr
num -
SH
IFT
Expr
num
Conflito shift/reduce/reduceExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
O que acontece ao escolher-se: Reduce
©Universidade do Algarve246
Expr
num -
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
O que acontece ao escolher-se: Reduce
RE
DU
CE
Expr
Expr
num
Conflito shift/reduce/reduce
©Universidade do Algarve247
Expr
num -
FA
ILS
!
Expr
Expr
num
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
O que acontece ao escolher-se: Reduce
Conflito shift/reduce/reduce
©Universidade do Algarve248
num
Expr
num
-
Qualquer uma destas opções resulta:Reduce
Shift
Conflito shift/reduce/reduceExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve249
num
Expr
num
-
O que acontece ao escolher-se:Reduce
Conflito shift/reduce/reduceExpr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve250
num
Expr
num
Op
-
RE
DU
CE
Conflito shift/reduce/reduce
O que acontece ao escolher-se:Reduce
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve251
Expr
num
Op
-
SH
IFT
num
O que acontece ao escolher-se:Reduce
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
Conflito shift/reduce/reduce
©Universidade do Algarve252
Expr
num
Op
-
RE
DU
CE
Expr
num
O que acontece ao escolher-se:Reduce
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
Conflito shift/reduce/reduce
©Universidade do Algarve253
Expr
num
Op
-
RE
DU
CE
Expr
num
Expr
Conflito shift/reduce/reduce
O que acontece ao escolher-se:Reduce
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve254
Expr
num
Op
-
AC
CE
PT
Expr
num
Expr
Conflito shift/reduce/reduce
O que acontece ao escolher-se:Reduce
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve255
num
Expr
num
-
SH
IFT
Conflitos
O que acontece ao escolher-se:Shift
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve256
Expr
num
-
SH
IFT
num
O que acontece ao escolher-se:Shift
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
Conflitos
©Universidade do Algarve257
Expr
num
-
RE
DU
CE
Expr
num
Conflitos
O que acontece ao escolher-se:Shift
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve258
Expr
num -
RE
DU
CE
Expr
num
Expr
Conflitos
O que acontece ao escolher-se:Shift
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve259
Expr
num -
AC
CE
PT
Expr
num
Expr
Conflitos
O que acontece ao escolher-se:Shift
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve260
num
Expr
num
-
Este conflito Shift/Reduce reflecte ambiguidade na gramática
Conflito Shift/Reduce/Reduce
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve261
num
Expr
num
-
Eliminar alterando a gramática
Conflito Shift/Reduce/Reduce
Este conflito Shift/Reduce reflecte ambiguidade na gramática
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
©Universidade do Algarve262
Conflito Shift/Reduce/Reduce
num
Expr
num
-
Expr Expr Op Expr
Expr Expr - Expr
Expr (Expr)
Expr Expr -
Expr num
Op +
Op -
Op *
Este conflito Shift/Reduce pode ser eliminado com Lookahead de um símbolo
©Universidade do Algarve263
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve264
Construção de um Parser
Vamos construir sem lookahead Decisões chave
Shift ou Reduce Qual a produção a reduzir?
Ideia básica Construir um DFA para controlar acções de shift
e de reduce O mesmo que, converter gramática por
autómato de pilha (pushdown automaton) Codificar controlo de estados finitos numa
tabela de parse
©Universidade do Algarve265
Estados do Parser
Sequência de Tokens na entrada ($ para sinalizar o fim da entrada)
Estado corrente do autómato de estados finitos
Duas PilhasPilha de Estados (implementa autómato
de estados finitos)Pilha de Símbolos (terminais da entrada
e não-terminais das reduções)
©Universidade do Algarve266
Integração de controlo dos estados finitos
Acções Coloca símbolos e estados na pilha Reduzir de acordo com uma dada produção Aceitar Erro
Acção seleccionada é uma função de Símbolo corrente na entrada Estado corrente do controlo de estados finitos
Cada acção especifica o próximo estado Implementar o controlo utilizando a
tabela do parser
©Universidade do Algarve267
Implementa controlo dos estados finitos Em cada estado, ver
Tabela[topo da pilha de estados][símbolo na entrada] Em seguida, realizar acção
Tabela do Parser
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve268
Exemplo de Tabela do Parser
S X $ (1)
X (X) (2)
X ( ) (3)
GramáticaEntradaPilha de Estados
Pilha de Símbolos
(())
Xs0
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve269
Shift para sn Coloca o token na entrada na pilha de
símbolos Coloca sn na pilha de estados Avança para o próximo símbolo na entrada
Tabela do Parser
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve270
Reduce (n) Retira itens das duas pilhas quantas vezes quantos os símbolos no RHS da
regra n Coloca LHS da regra n na pilha de símbolos Ver
• Tabela[topo da pilha de estados][topo da pilha de símbolos] Coloca esse estado (na parte goto da tabela) na pilha de estados
Tabela do Parser
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve271
Aceitar Parar a análise e reportar sucesso
Erro Parar a análise e reportar erro
Tabela do Parser
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve272
S X $(1)
X (X) (2)
X ( ) (3)
(())$
s0
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve273
S X $(1)
X (X) (2)
X ( ) (3)
(())$
s0
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve274
S X $(1)
X (X) (2)
X ( ) (3)
())$
s0 (s2
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve275
S X $(1)
X (X) (2)
X ( ) (3)
())$
s0 (s2
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve276
S X $(1)
X (X) (2)
X ( ) (3)
))$
s0 (s2 (s2
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve277
S X $(1)
X (X) (2)
X ( ) (3)
))$
s0 (s2 (s2
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve278
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2 (s2
s5
)
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve279
)$
s0 (s2 (s2
s5
)
S X $(1)
X (X) (2)
X ( ) (3)
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve280
)$
s0 (s2 (s2
s5
)
S X $(1)
X (X) (2)
X ( ) (3)
Passo 1: pop das pilhas
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve281
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2
Passo 1: pop das pilhas
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve282
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2
Passo 2: push não-terminal
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve283
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2 X
Passo 2: push não-terminal
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve284
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2 X
Passo 3: usar Goto, push novo estado
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve285
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2 Xs3
Passo 3: usar Goto, push novo estado
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve286
S X $(1)
X (X) (2)
X ( ) (3)
)$
s0 (s2 Xs3
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve287
S X $(1)
X (X) (2)
X ( ) (3)s0 (s2 Xs3
s4
)
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve288
S X $(1)
X (X) (2)
X ( ) (3)
$
s0 (s2 Xs3
s4
)
Tabela do Parser em acção
GramáticaEntradaPilha de Estados
Pilha de Símbolos
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve289
S X $(1)
X (X) (2)
X ( ) (3)s0 (s2 Xs3
s4
)
Passo 1: pop pilhas
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve290
S X $(1)
X (X) (2)
X ( ) (3)s0
Passo 1: pop pilhas
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve291
S X $(1)
X (X) (2)
X ( ) (3)s0
Passo 2: push não-terminal
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve292
S X $(1)
X (X) (2)
X ( ) (3)s0 X
Passo 2: push não-terminal
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve293
S X $(1)
X (X) (2)
X ( ) (3)s0 X
Passo 3: usar Goto, push novo estado
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve294
S X $(1)
X (X) (2)
X ( ) (3)s0 Xs1
Passo 3: usar Goto, push novo estado
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve295
S X $(1)
X (X) (2)
X ( ) (3)s0 Ss1
Aceitar a String!
GramáticaEntradaPilha de Estados
Pilha de Símbolos
$
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
©Universidade do Algarve296
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve297
Construção do Parser
Sintetizar um DFA Captura todos os estados possíveis em que o
parser pode estar Transições de estados para terminais e não-
terminais Utilizar o DFA para criar a tabela sintáctica
©Universidade do Algarve298
Exemplo
Gramática
S X $ (1)
X (X) (2)
X ( ) (3)
X ( X )
Aqui? Ou aqui?Ou aqui?
Ou aqui?
Estados do DFA baseados nos itens Temos de capturar o que foi já percorrido numa
produção
©Universidade do Algarve299
Exemplo
Gramática
S X $ (1)
X (X) (2)
X ( ) (3)
X ( X )
Estados do DFA baseados nos itens Temos de capturar o que foi já percorrido numa
produção
Produção X (X) gera 4 itens:X • (X )X ( • X ) X (X • )X (X ) •
©Universidade do Algarve300
Exemplo
Gramática
S X $ (1)
X (X) (2)
X ( ) (3)
Estados do DFA baseados nos itens Temos de capturar o que foi já percorrido numa
produção
Itens para todas as produções da Gramática:S • X $
S X • $
X • (X)
X ( • X )
X (X • )
X (X) •
X • ( )
X ( • )
X ( ) •
©Universidade do Algarve301
Ideia por trás dos itens
Estados correspondem a conjuntos de itens Se um estado contém um item: A • c
O parser espera uma eventual redução utilizando a produção: A c
O parser já analisou Espera que a entrada possa conter c, seguido de
Se um estado contém um item: A • O parser já analisou Reduzirá utilizando: A
Se um estado contém um item: S • $ e a entrada está vazia O parser aceita a entrada
©Universidade do Algarve302
Relação entre itens e acções
Se o estado corrente contém o item: A • c e o símbolo corrente na entrada é c O parser desloca c para a pilha de símbolos O próximo estado conterá A c •
Se o estado corrente contém o item: A • Reduzirá utilizando: A
Se o estado corrente contém o item: S • $ e a entrada está vazia O parser aceita a entrada
©Universidade do Algarve303
Closure() de um conjunto de itens
Closure (fechamento) encontra todos os itens no mesmo estado
Algoritmo de ponto-fixo para Closure(I) Cada item em I é também um item em
Closure(I) Se A • B está em Closure(I) e B •
é um item, então adicionar B • a Closure(I)
Repetir até que não haja adição de novos itens a Closure(I)
©Universidade do Algarve304
Closure({X ( • X )}) Itens
S • X $
S X • $
X • (X)
X ( • X )
X (X • )
X (X) •
X • ( )
X ( • )
X ( ) •
X ( • X) X • (X)X • ( )
Exemplos de Closure()
S • X $
X • (X)
X • ( )
Closure({S • X $})
©Universidade do Algarve305
Goto() de um conjunto de itens
Goto encontra o novo estado depois de ter consumido um símbolo da gramática no presente estado
Algoritmo para Goto(I, X) em que I é um conjunto de itens e X um símbolo terminal ou não-terminal da gramática
Goto(I, X) = Closure({ A X • | A • X em I })
dá-nos o novo conjunto obtido pelo movimento do ponto sobre X
©Universidade do Algarve306
Goto({X ( • X)}, X)
X (X • )
ItensS • X $
S X • $
X • (X)
X ( • X )
X (X • )
X (X) •
X • ( )
X ( • )
X ( ) •
Exemplos de Goto()
Goto ({X •(X)}, ()
X ( • X) X • (X)X • ( )
©Universidade do Algarve307
Começar com o item: S • $ Caso não exista adicionar a primeira produção com término
$ Criar o primeiro estado como sendo Closure({ Goal • S
$}) Escolher um estado I
Para cada item A • X em I• determinar Goto(I, X)• se Goto(I, X) não está no estado, criar um
novo estado• Adicionar um laço X do estado I ao estado
Goto(I, X) Repetir até que não haja mais modificações
possíveis
Construir os estados do DFA
©Universidade do Algarve308
Construir o Parser Construir o DFA
DFA para a gramática:
Construir a tabela do parser utilizando o DFA
S • X$X • (X)X • ( )
s0S X • $
s1X
X ( • X)X ( • )X • (X)X • ( )
s2
(X (X • )
X
s3
(
X ( ) •
)s5
X (X) •
)s4
©Universidade do Algarve309
Criação da tabela sintáctica
Para cada estado Transição para outro estado utilizando um
símbolo terminal é um deslocamento para esse estado (shift to sn)
Transição para outro estado utilizando um símbolo não-terminal é um goto para esse estado (goto sn)
Se existir um item A • no estado fazer uma redução com essa produção para todos os terminais (reduce k)
Se existir um item S X • $ no estado então colocar acção de aceitação para o terminal $
©Universidade do Algarve310
ACTION GotoState ( ) $ Xs0 shift to s2 error error goto s1s1 error error accept s2 shift to s2 shift to s5 error goto s3s3 error shift to s4 error s4 reduce (2) reduce (2) reduce (2) s5 reduce (3) reduce (3) reduce (3)
S • X$X • (X)X • ( )
s0S X • $
s1X
X ( • X)X ( • )X • (X)X • ( )
s2
(X (X • )
X
s3
(
X ( ) •
)s5
X (X) •
)s4
Criação da tabela sintáctica
©Universidade do Algarve311
Problemas que podem ocorrer
Nenhum lookahead Vulnerável a conflitos desnecessários
• Conflitos Shift/Reduce (pode reduzir demasiado cedo em alguns casos)
• Conflitos Reduce/Reduce Solução: Lookahead
Apenas para reduções – reduzir apenas quando o próximo símbolo pode ocorrer depois de não-terminal da produção
Lookahead sistemático, divisão de estados baseada no próximo símbolo, acção é sempre uma função do próximo símbolo
Pode ser generalizado para ver à frente múltiplos símbolos
©Universidade do Algarve312
Análise Sintáctica
Compiladores
João M. P. Cardoso
©Universidade do Algarve313
Definições: Conjuntos First() e Follow()
Conjunto First() Conjunto de símbolos terminais situados mais
à esquerda em todas as possíveis árvores de derivação de
T First( ) se T pode aparecer como primeiro símbolo numa derivação começando em
Começar pelo conceito de NT derivando :• NT implica que NT deriva • NT NT1 ... NTn e se todos os NTi (1i
n) derivam implica que NT deriva
Notação
T é terminal,
NT é não-terminal,
S é terminal ou não-terminal,
e α e representam sequências de terminais e/ou não terminais
©Universidade do Algarve314
Definições: Regras para First()
1) TFirst(T)
2) First(S) First(S )
3) NT deriva implica:
First() First(NT )
4) NT S implica:
First(S ) First(NT)
Notação
T é terminal,
NT é não-terminal,
S é terminal ou não-terminal,
e α e representam sequências de terminais e/ou não terminais
©Universidade do Algarve315
Definições: Exemplo First()
First(Term’)?
Gramática
Term’ * INT Term’
Term’ / INT Term’
Term’
Solução
First(Term’) = {*,/}
First(* INT Term’) = {*}
First(/ INT Term’) = {/}
First(*) = {*}
First(/) = {/}
©Universidade do Algarve316
Definições: Conjunto First()
Se duas ou mais produções diferentes para o mesmo símbolo não-terminal têm conjuntos First com símbolos terminais comuns então: A gramática não pode ser analisada com um
parser preditivo LL(1) sem retrocesso• Exemplo:• S X $• X a• X a b
• First(X a) = { a }• First(X a b) = { a }• Qual a produção a escolher quando perante o
símbolo terminal a?
©Universidade do Algarve317
Definições: Conjunto Follow()
Para o símbolo não-terminal A, Follow(A) é o conjunto dos primeiros terminais que podem vir depois de A em alguma derivação
Regras para Follow() $ Follow(S), em que S é o símbolo início Se A B é uma produção então
First() Follow(B) Se A B é uma produção então
Follow(A) Follow(B) Se A B é uma produção e deriva então
Follow(A) Follow(B)
©Universidade do Algarve318
Definições: Algoritmo para Follow()
for all nonterminals NT
Follow(NT) = {}
Follow(S) = {$}
while Follow sets keep changing
for all productions A BFollow(B) = Follow(B) First()
if ( derives ) Follow(B) = Follow(B)Follow(A)
for all productions A B
Follow(B) = Follow(B)Follow(A)
©Universidade do Algarve319
Definições: Exemplo Follow()
Gramáticas exemplo: S X $
X aX a b
• Follow(S) = { $ }• Follow(X) = { $ }
S X $X “(“ X “)”X
• Follow(S) = { $ }• Follow(X) = { “)”, $ }
©Universidade do Algarve320
Parser com Lookahead apenas nas reduções
Designa-se por parser Simple LR: SLR(1) ou simplesmente SLR
Se um estado contiver: A • Reduzir de A apenas se o próximo
símbolo na entrada pode seguir (follow) A em alguma derivação
Gramática exemplo:S X $ (1)X a (2)X a b (3)
©Universidade do Algarve321
Parser sem Lookahead: LR(0)
S • X $X • aX • a b
S X • $
X a • X a • b
X a b •
ACTION GotoState a b $ Xs0 shift to s1 error error goto s3s1 reduce(2) S/R Conflict reduce(2) s2 reduce(3) reduce(3) reduce(3)s3 error error accept
s0s3
s1
s2X
ab
reduce(2)
ou
shift to s2
©Universidade do Algarve322
Tabela sintáctica com apenas lookahead nas reduções
Para cada estado Transição para outro estado utilizando um símbolo
terminal é um deslocamento para esse estado (shift to sn) (como anteriormente)
Transição para outro estado utilizando um símbolo não-terminal é um goto para esse estado (goto sn) (como anteriormente)
Se existir um item: X • no estado, fazer uma redução com essa produção sempre que o símbolo corrente (T) na entrada possa seguir (follow) X em alguma derivação
Elimina acções de redução inúteis
©Universidade do Algarve323
ACTION GotoState a b $ Xs0 shift to s1 error error goto s3s1 reduce(2) shift s2/r(2) reduce(2) s2 reduce(3)s3 error error accept
Nova tabela sintáctica
•Reduce(2) implica redução com a regra: X a •Como Follow(X) = {$}
b nunca segue X nas derivações: resolver conflito shift/reduce com shift
©Universidade do Algarve324
S • X $X • aX • a b
S X • $
X a • X a • b
X a b •
ACTION GotoState a b $ Xs0 shift to s1 error error goto s3s1 shift to s2 reduce(2) s2 reduce(3)s3 error error accept
s0
s3
s1
s2X
ab
b nunca segue X nas derivações: resolver conflito shift/reduce com shift
Nova tabela sintáctica
bFollow(X)aFollow(X)
©Universidade do Algarve325
Lookahead mais genérico
Itens contêm informação lookahead potencial, resultando em mais estados no controlo de estados finitos Item da forma: [A • X c] diz
• Próximo símbolo na entrada é c• Parser já consumiu , espera analisar X , e
depois reduzir utilizando: A • X Em adição ao estado corrente na tabela sintáctica,
todas as acções do parser são função dos símbolos de lookahead
©Universidade do Algarve326
Sumário
Geradores de Parsers – dada uma gramática, produz um parser
Técnica de análise sintáctica ascendente Construir automaticamente um autómato de
pilha (pushdown automata) Obter um parser shift-reduce
• Controlo de estados finitos + pilha• Implementação baseada em tabela
Conflitos: Shift/Reduce, Reduce/Reduce Uso de lookahead para eliminar conflitos
Parser SLR(1) (elimina acções de redução inúteis)
Parser LR(k) (uso de lookahead genérico)
©Universidade do Algarve327
Ideia básica para LR(1)
Dividir estados em LR(0) DFA baseado em lookahead
Acção de reduzir é baseada no item e no lookahead
©Universidade do Algarve328
Itens LR(1)
Itens mantêm informação em relação a: Produção Posição right-hand-side (o ponto) Símbolo lookahead
Item LR(1) é da forma [A • T] A é uma produção O ponto em A • denota a posição T é um símbolo terminal ou o marcador de término
($) Item [A • T] significa
O parser já analisou Se analisa e o próximo símbolo é T então o parser
deve reduzir com A
©Universidade do Algarve329
GramáticaS X $X (X)X
Símbolos terminais ‘(‘ ‘)’
Marcador de término ‘$’
Itens LR(1)[S • X $ ) ][S • X $ ( ][S • X $ $ ][S X • $ ) ][S X • $ ( ][S X • $ $][X • (X) ) ][X • (X) ( ][X • (X) $ ][X ( • X) ) ][X ( • X) ( ]
[X ( • X) $ ][X (X • ) ][X (X • ) ( ][X (X • ) $ ][X (X) • ) ][X (X) • ( ][X (X) • $ ][X • ) ][X • ( ][X • $ ]
Itens LR(1): exemplo
©Universidade do Algarve330
Criação do parser LR(1)
É necessário definir funções Closure() e Goto() para itens LR(1)
Necessário algoritmo para criar DFA Necessário algoritmo para criar a tabela
do parser
©Universidade do Algarve331
Closure para LR(1)
Closure(I)repeat
for all items [A • X c] in Ifor any production X
for any d First( c)
I = I { [X • d] }until I does not changereturn I
©Universidade do Algarve332
Goto para LR(1)
Goto(I, X)
J = { }
for any item [A • X c] in I
J = J {[A X • c]}
return Closure(J)
©Universidade do Algarve333
Construção do DFA LR(1)
Começar com o item: [Start • S $ ?] ? É irrelevante porque nunca deslocaremos $
Determinar o fechamento (closure) do item e formar o estado
Seleccionar um estado I for each item [A • X c] in I
• find Goto(I, X)• if Goto(I, X) is not already a state, make one• Add an edge X from state I to Goto(I, X)
state Repetir até que não haja mais adições
©Universidade do Algarve334
Criação da tabela do parser
Para cada estado no DFA LR(1) Transição para outro estado usando um símbolo
terminal é um deslocamento para esse estado (shift to sn)
Transição para outro estado usando um símbolo não-terminal é um goto para esse estado (goto sn)
Se existir um item [A • a] num estado,reduzir para o símbolo de entrada a com a produção A (reduce k)
©Universidade do Algarve335
Parser Look-Ahead LR(1) ou LALR(1)
Motivação Parser LR(1) tem um número elevado de
estados Método simples para eliminar estados
Se dois estados LR(1) são idênticos excepto no símbolo lookahead dos itens então juntar estados
Resultado é um DFA LALR(1) Tipicamente tem muito menos estados do que
LR(1) Pode ter mais conflitos reduce/reduce
©Universidade do Algarve336
Classificação de Gramáticas
Dada uma determinada gramática, determinar se pode ter como analisador:LL(0), LL(1), LL(2), ..., LL(k)?LR(0), LR(1), LR(2), ..., LR(k)?SLR(1)?
©Universidade do Algarve337
Classificar uma gramática como LL(1)
Como verificar se uma gramática é LL(1)? Se a tabela sintáctica não tiver mais do que uma
produção em cada célula Tabela sintáctica do analisador preditivo
Uma linha por cada não-terminal Uma coluna por cada Terminal Colocar produção X na linha X, coluna T, para
cada T First() Se pode derivar então colocar produção X
na linha X, coluna T, para cada T Follow(X)
©Universidade do Algarve338
Classificar uma gramática como LL(1)
Colocar produção X na linha X, coluna T, para cada T First()
Se pode derivar então colocar produção X na linha X, coluna T, para cada T Follow(X)
Gramática:
Z “d”
Z X Y Z
Y Y “c”
X Y
X “a” Não-terminais
Terminais“d” “c” “a”
Z
Y
X
©Universidade do Algarve339
Classificar uma gramática como LL(1)
Gramática:
Z “d”
Z X Y Z
Y Y “c”
X Y
X “a”Não-
terminaisTerminais
“d” “c” “a”
Z Z X Y ZZ “d”
Z X Y Z Z X Y Z
Y Y Y Y “c”
Y
X X Y X Y X YX “a”
Como verificar se uma gramática é LL(1)? Se a tabela sintáctica não tiver mais do
que uma produção em cada célula A gramática não é LL(1)
©Universidade do Algarve340
Classificação de Gramáticas
Uma gramática diz-se: LR(0) se existir uma tabela sintáctica LR(0)
sem conflitos (reduce/reduce, ou shift/reduce)
SLR(1) se existir uma tabela sintáctica SLR(1) sem conflitos (reduce/reduce, ou shift/reduce)
LR(k) se existir uma tabela sintáctica LR(k) sem conflitos (reduce/reduce, ou shift/reduce)
LL(k) se puder ser analisada por um analisador sintáctico preditivo com lookahead=k
…
©Universidade do Algarve341
Classificação de Gramáticas
G0regular
LR(0)SLR(1)
LALR(1)LR(1)LR(k)
unambiguousContext free
G1 G2 G3 G4 G5 G6 G7
LL(0)
LL(1)
LL(k)
©Universidade do Algarve342
Geradores de Parsers
Geram C, http://dinosaur.compilertools.net/ Lex & Yacc flex e bison
Geram Java: JLex e CUPhttp://www.cs.princeton.edu/~appel/modern/java/JLex/http://www.cs.princeton.edu/~appel/modern/java/CUP/ JavaCC: http://www.experimentalstuff.com/Technologies/JavaCC/
index.html Lista com mais geradores de parsers
http://catalog.compilertools.net/lexparse.html http://catalog.compilertools.net/java.html
©Universidade do Algarve343
Análise Semântica e Representação Intermédia
Compiladores
João M. P. Cardoso
©Universidade do Algarve344
Localização actual nas etapas de compilação
Lexical Analyzer (Scanner)
Syntax Analyzer (Parser)
Token Stream
Parse Tree
Program (character stream)
Semantic AnalyzerIntermediate Code Generator
Intermediate Representation + Symbol Table
©Universidade do Algarve345
O que é a semântica de um programa?
Sintaxe Como o programa é constituídoRepresentação textual ou estrutura
SemânticaQual é o significado do programa?
©Universidade do Algarve346
Qual o motivo da análise semântica?
Certifica-se de que o programa está de acordo com as definições da linguagem de programação
Reportar, sempre que haja erros semânticos, mensagens de erro que sejam úteis para o utilizador
Não é preciso muito trabalho adicional se for incorporada durante a criação da representação intermédia
©Universidade do Algarve347
Erros na Análise Semântica
boolean sum(int A[], int N) { Int i, sum; For(i=0; i<N; i++) { sum1 = sum + A[i]; } return sum;}...Int s = sum(A);
Falta um argumento
Não foi atribuído valor inicial a sum
Tipo da variável devolvida não confere com a declaração do cabeçalho da função
Variável não declarada
Inconsistência entre tipos no RHS e LHS
©Universidade do Algarve348
Objectivo das representações intermédias do programa
Permitir análises e transformaçõesOptimizações
Estruturar tradução para código máquinaSequência de passos
Árvore Sintáctica
RepresentaçãoIntermédiade nível alto
RepresentaçãoIntermédia denível baixo
CódigoMáquina
Análise Semântica
Intermediate Representation (IR)
©Universidade do Algarve349
Representação Intermédia de Nível Alto
Preserva o fluxo de controlo estruturado Útil para optimizações ao nível do ciclo
Desenrolamento de ciclos, Fusão de ciclos, etc.
Preserva estrutura ao nível dos objectos Útil para optimizações em programas
orientados por objectos
©Universidade do Algarve350
Representação Intermédia de Nível Baixo
Passa do modelo de dados abstracto para o espaço planar de endereçamento
Elimina o fluxo de controlo estruturado
Útil para tarefas de compilação de nível baixoAfectação de registosSelecção de instruções
©Universidade do Algarve351
Alternativas
Há muitas alternativas possíveis Árvores de instruções e de expressões Grafos direccionados acíclicos (DAGs) Código de 3 endereços (C3E) E muitas outras...
Mais ou menos especificas à própria linguagem
Estas lições apresentam uma possibilidade de árvores de instruções e de expressões (mais tarde falaremos também de código de 3 endereços)
©Universidade do Algarve352
Tarefas de Compilação
Determina formato das estruturas na memória Determinar formato de arrays e de objectos na memória Determinar formato da pilha de chamadas na memória
Gerar código para ler valores
• parâmetros, elementos de arrays, campos de objectos para avaliar expressões e computar valores novos para escrever valores para estruturas de controlo
Enumera funções e cria a tabela de funções Invocação de funções acede à entrada correspondente
na tabela de funções Gera código para funções
variáveis locais, e acesso a parâmetros Invocações de funções
©Universidade do Algarve353
Tabelas de símbolos (symbol tables)
Conceito chave na compilação enquanto a declaração de tipos, variáveis e
funções são processadas vamos atribuir significados a esses identificadores utilizando tabelas de símbolos
Compiladores utilizam tabelas de símbolos para produzirem Layout das estruturas na memória Tabelas de funções Código para aceder a campos, variáveis
locais, parâmetros, etc.
©Universidade do Algarve354
Tabelas de Símbolos
Durante a tradução de árvores sintácticas para representação intermédia Tabelas de símbolos mapeiam identificadores
(strings) em descritores (informação acerca dos identificadores)
Operação básica: Lookup• Dada uma string, encontrar o seu descritor• Implementação típica: Hash Table (contentor
associativo) Exemplo
Dado o nome de uma variável, encontrar descritor• Descritor local, descritor de parâmetro, descritor
global
©Universidade do Algarve355
Exemplo
void add(int x, int[] v, int N) {
int i;
i = 0;
while (i < N) {
v[i] = v[i]+x;
i = i+1;
}
}
x
i
Função add
vN
descritor para parâmetro xdescritor para parâmetro vdescritor para parâmetro N
descritor para variável local i
©Universidade do Algarve356
Exemplo
void add(int x, int[] v, int N) {
int i;
i = 0;
while (i < N) {
v[i] = v[i]+x;
i = i+1;
}
}
x
i
Função add
Código da função
vN
descritor para parâmetro xdescritor para parâmetro vdescritor para parâmetro N
descritor para variável local i
descritor para retorno
©Universidade do Algarve357
Exemplo
void add(int x, int[] v, int N) {
int i;
i = 0;
while (i < N) {
v[i] = v[i]+x;
i = i+1;
}
}
x
i
add
Código da função v
N
descritor para parâmetro xdescritor para parâmetro vdescritor para parâmetro N
descritor para variável local i
descritor para retorno
©Universidade do Algarve358
Hierarquia em tabelas de símbolos
Alcance/Escopo (scope) O mesmo nome para uma variável pode ter
significados diferentes em locais diferentes É necessária uma tabela de símbolos por
cada escopo A hierarquia deriva de
Encadeamento de escopos Hierarquia na tabela de símbolos reflecte esta
hierarquia Lookup atravessa de modo ascendente a
hierarquia até que o descritor seja encontrado
©Universidade do Algarve359
Lookup i num exemplo
i descritor para a variável local i
x descritor para parâmetro x
v descritor para a variável global v
TS para as variáveis globais
TS para os parâmetros da função
TS para as variáveis locais da função
©Universidade do Algarve360
Lookup I num exemplo
v[i] = v[i]+x;
1º vai procurar na TS das variáveis locais e só se não encontrar é que sobe na hierarquia das TS i descritor para a variável local i
x descritor para parâmetro x
v descritor para a variável global v
©Universidade do Algarve361
Análise Semântica e Representação Intermédia
Compiladores
João M. P. Cardoso
©Universidade do Algarve362
Descritores
O que contêm? Informação utilizada para geração de
código e análise semânticaDescritores locais - nome, tipo, offset
na pilhaDescritores de funções
• assinatura (tipo do valor retornado, e parâmetros)
• Referência à tabela de símbolos local• Referência ao código para a função
©Universidade do Algarve363
Parâmetros, Local, e Descritores de Tipos
Parâmetros, Local referem a descritores de tipoDescritor de tipo base: int, boolean,
etc.Descritor de tipo de array, que contém
referência ao descritor de tipo para os elementos do array
Descritor de estrutura, etc.
©Universidade do Algarve364
Exemplo: Tabela de Símbolos para Tipos
boolean Descritor de boolean
int Descritor de intint [] Descritor de array
boolean [] Descritor de arrayvector [] Descritor de array Descritor de
estrutura para vector
©Universidade do Algarve365
Descritores de funções
Contêm referência para o código da função
Contêm referência para a tabela de símbolos local (para as variáveis locais da função)
Na hierarquia de tabelas de símbolos, a TS para os parâmetros é mãe da TS para as variáveis locais
©Universidade do Algarve366
Descritor de função para add
v
iDescritor de variável local
TS de variáveis locais
x
TS para parâmetros
Descritor de parâmetro
Código para a função add
Descritor de função para add
N Descritor de parâmetro
Descritor de parâmetro
©Universidade do Algarve367
O que é uma árvore sintáctica?
Árvore sintáctica guarda resultados da análise sintáctica
Nós externos são terminais/tokens Nós internos são não-terminais
©Universidade do Algarve368
Árvores abstractas versus concretas
Relembrar modificações à gramática Factorização à esquerda, eliminação de
ambiguidade, precedências dos operadores Modificações levam a uma árvore que não
reflecte uma interpretação do programa intuitiva e clara
Pode ser mais conveniente trabalhar com a AST (pode ser vista como a árvore sintáctica representativa da gramática sem as modificações modificações)
©Universidade do Algarve369
Construções alternativas para Representações Intermédias
Construir a árvore sintáctica concreta, traduzir para AST, traduzir para representação intermédia
Construir a árvore sintáctica abstracta, traduzir para representação intermédia
Incluir a construção da representação intermédia durante a análise sintáctica Elimina a construção intermédia da árvore
sintáctica – melhora performance do compilador
Menos código a escrever
©Universidade do Algarve370
Tabela de Símbolos
Dada uma árvore sintáctica (abstracta ou concreta)Atravessar recursivamente a árvoreConstruir a tabela de símbolos
enquanto a travessia da árvore decorre
©Universidade do Algarve371
Escopos aninhados
Várias formas de aninhamento TS das funções aninhadas na TS dos
globais TS de locais aninhada dentro da TS da
função Aninhamento resolve ambiguidade em
possíveis conflitos Mesmo nome utilizado para uma variável
global e uma variável local Nome refere uma variável local dentro da
função
©Universidade do Algarve372
Escopos aninhados de código
TS podem ter profundidade arbitrária com base no aninhamento do código:boolean x; int foo(int x) {
double x = 5.0;{ float x = 10.0;
{ int x = 1; ... x ...} ... x ...
}... x ...
}
Nota: Conflitos de nomes com aninhamento podem reflectir erros no programa. Os compiladores geram mensagens de aviso em presença de conflitos deste tipo.
©Universidade do Algarve373
Representação de código em nível alto
Ideia básica Movimento em direcção à linguagem
assembly Preservar a estrutura de nível alto
• Formato de objectos• Fluxo de controlo estruturado• Distinção entre parâmetros, variáveis locais, e
campos Abstracção de nível alto da linguagem
assembly• Nós load e store• Acesso a armazenamento local abstracto,
parâmetros e campos, e não posições de memória directamente
©Universidade do Algarve374
Representação de expressões
Árvores de expressões representam as expressões Nós internos – operações como: +, -, etc. Folhas – Nós Load representam acesso a variáveis
Nós Load ldl para acesso a variáveis locais – descritor de
locais ldp para acessos a parâmetros – descritor de
parâmetros lda para acesso a arrays
• Árvore da expressão para o valor• Árvore de expressão para o índice
Para acesso a atributos de uma classe ou campos de estruturas...
©Universidade do Algarve375
Exemplo
x e y são variáveis locais
x*x + y*y +
ldl
Descritor de local para xNa tabela de símbolos locais
*
ldl ldl
*
ldl
Descritor de local para xNa tabela de símbolos locais
©Universidade do Algarve376
Exemplov é uma array passado como argumento da
função add
i é uma variável local
x é um argumento da função
v[i]+x lda
+
ldp
Descritor local para I na tabela de símbolos locais da função add
ldlldp
Descritor de parâmetro para v na tabela de símbolos de parâmetros da função add
Descritor de parâmetro para x na tabela de símbolos de parâmetros da função add
©Universidade do Algarve377
Representação de enunciados de atribuição
Nós Storestl para stores em variáveis locais
• Descritor local• Árvore da expressão para o valor a guardar
sta para stores em elementos de arrays• Árvore da expressão para o array• Árvore da expressão para o índice• Árvore da expressão para o valor a guardar
Para stores em atributos de classes ou campos de estruturas...
©Universidade do Algarve378
v[i]=v[i]+x;
lda
+
ldp
Descritor do parâmetro para v na tabela de símbolos dos parâmetros da função add
Descritor local para I na tabela de símbolos locais da função add
Descritor do parâmetro para x na tabela de símbolos dos parâmetros da função add
ldl
sta
ldlldp
ldp
Exemplo
©Universidade do Algarve379
Orientação
Representações intermédias Movimento em direcção à linguagem
máquina Suporte para análises do programa e acções
de transformação IR (intermediate representation) de nível
alto Preserva estruturas de objectos e de arrays Tabelas de símbolos Descritores
©Universidade do Algarve380
Análise Semântica e Representação Intermédia
Compiladores
João M. P. Cardoso
©Universidade do Algarve381
Representação do fluxo de controlo
Nós de enunciadosNó if
• Árvore de expressão para a condição• Nó para o corpo do then e nó para o corpo
do elseNó while
• Árvore de expressão para a condição• Nó para o corpo
Nó return • Árvore de expressão para o
valor/expressão de retorno
©Universidade do Algarve382
Exemplo
while (i < N)
v[i] = v[i]+x;
Descritor de locais para i
ldl
Descritor de parâmetros para v
Descritor de parâmetros para x
while
<
ldp lda
+
ldp
ldl
sta
ldl
ldp
ldp
©Universidade do Algarve383
Exemplo
while (i < N)
v[i] = v[i]+x;
Ldl i
while
<
Ldp v lda
+
Ldp x
Ldl i
sta
Ldl i
Ldp v
Ldp v
Notação abreviada
©Universidade do Algarve384
Das árvores sintácticas até à IR
Atravessar recursivamente a árvore sintáctica
Construir representação de modo ascendente (Bottom-Up) Ver identificador da variável na tabela de
símbolos Construir nós Load para aceder a variáveis Construir expressões a partir dos nós de
Load e dos nós de operações Construir nós Store para enunciados de
atribuição Colocar nós while, if, return para as
construções de controlo
©Universidade do Algarve385
Sumário
Representação Intermédia de alto nível Objectivo: representar o programa de um
modo intuitivo para suporte de futuras tarefas de compilação
Representação dos dados do programa Tabelas de Símbolos Organização hierárquica
Representação da computação Árvores de expressões Vários tipos de nós store e load Fluxo de controlo estruturado
©Universidade do Algarve386
Análise semântica: Erros
Assumimos a inexistência de problemas durante a construção da IR
Contudo, é necessário fazer muitas verificações durante a tradução
Chamadas de Análise Semântica Realização da análise semântica ao nível
da árvore sintáctica Para que os erros sejam informativos/claros
é necessário que os nós da árvore sejam anotados com as posições no programa
©Universidade do Algarve387
Objectivo da análise semântica
Assegurar que o programa obedece a um conjunto de verificações de sanidade Todas as variáveis usadas foram definidas Tipos são usados correctamente Chamadas a funções têm o número correcto
e tipos de parâmetros e do valor retornado Verificação aquando da construção da IR
©Universidade do Algarve388
Descritores para identificadores
Quando se constrói o descritor de uma variável local, parâmetro, etc. temos Nome do tipo Nome da variável
O que é a verificação? Verificar se o nome do tipo identifica um tipo válido lookup nome na tabela de símbolos dos
tipos Se não estiver lá, falha na análise semântica
©Universidade do Algarve389
Tabela de símbolos local
Quando se constrói a tabela de símbolos local temos uma lista de descritores locais
Fazer a verificação a quê? Nomes de variáveis em duplicado
Quando se faz a verificação? Quando se insere o descritor na tabela de
símbolos local Similar para os parâmetros, para a TS
global, etc.
©Universidade do Algarve390
Verificação para loads, stores, etc.
O que tem o compilador? Nome da variável. O que faz? Lookup nome da variável:
Se estiver na tabela de símbolos local, referencia descritor local
Se estiver na tabela de símbolos dos parâmetros, referencia descritor do parâmetro
Se estiver na tabela de símbolos global, referencia descritor global
Se não for encontrado um descritor: erro semântico (a variável não foi declarada...)
©Universidade do Algarve391
Verificação para a Instrução de Load para arrays
O que tem o compilador?Nome da variávelExpressão de indexação no array
O que faz?Lookup nome da variável (se não
estiver: erro semântico)Verifica tipo da expressão (se não for
inteiro: erro semântico)
©Universidade do Algarve392
Operação de adição
O que tem o compilador? 2 expressões
O que pode estar errado? Expressões têm o tipo errado Têm de ser os dois inteiros (por exemplo)
Por isso o compilador faz a verificação do tipo das expressões Instruções load guardam o tipo da variável acedida Operações guardam o tipo da expressão produzida Assim, é apenas necessário verificar tipos, e caso
falhe dá erro semântico
©Universidade do Algarve393
Inferência de tipos para operações de adição
Algumas linguagens deixam adicionar floats, ints, doubles
Quais são os problemas? Tipo do resultado da operação Conversão dos operandos da operação
Regras standard podem ser usualmente aplicadas Se adição de um int e de um float
• converter o int para float, adicionar os floats, e o resultado é um float.
Se adição de um float e de um double• converter o float para double, adicionar os
doubles, e o resultado é um double.
©Universidade do Algarve394
Regras para a adição
Princípio básico: Hierarquia de tipos de números (int, depois float, depois double)
Todas as conversões forçadas são feitas de modo ascendente na hierarquia Ex: int para float; float para double;
Resultado tem o tipo do operando no nível mais elevado da hierarquia int + float é float, int + double é double, float + double é double
©Universidade do Algarve395
Inferência de tipos
Inferir tipos sem declaração explicita de tipos
A adição é um caso muito restrito de inferência de tipos
Tópico importante em investigação recente de linguagens de programação Quantas declarações de tipos se podem
omitir? Ligado ao polimorfismo
©Universidade do Algarve396
Instrução Store
O que tem o compilador? Nome da variávelexpressão
O que faz? Lookup nome da variável (se não
estiver: erro semântico)Verifica tipo da variável com o tipo da
expressão• Se o tipo da variável não for compatível
com o tipo da expressão, erro semântico
©Universidade do Algarve397
Instrução Store para arrays O que tem o compilador?
Nome da variável, expressão de indexação expressão
O que faz? Lookup nome da variável (se não estiver:
erro semântico) Verifica se o tipo da expressão de indexação
é inteiro Verifica tipo da variável em relação ao tipo
da expressão• Se o tipo da variável não for compatível com o
tipo da expressão: erro semântico
©Universidade do Algarve398
Invocação de funções
O que tem o compilador?Nome da função, parâmetros
Verificações:Nome da função é identificado na tabela
de funções do programaTipos dos parâmetros “casam” com tipos
dos parâmetros da declaração da função
©Universidade do Algarve399
Sumário de verificações semânticas
Realizar a verificação semântica durante a construção do IR
Muitas verificações são para certificar que se constrói uma IR correcta
Outras verificações correspondem a verificações simples de sanidade
Cada linguagem de programação tem uma lista que deve ser verificada
Pode reportar muitos erros potenciais durante a compilação
©Universidade do Algarve400
Análise Semântica e Representação Intermédia
Compiladores
João M. P. Cardoso
©Universidade do Algarve401
Conversão para a IR de nível baixo
Converte fluxo de controlo estruturado em fluxo de controlo baseado em saltos (não estruturado) Saltos condicionais e incondicionais
Converte modelo de memória estruturado em modelo de memória planar Endereçamento planar para variáveis Endereçamento planar para Arrays
Continua independente da linguagem máquina, mas: Movimento para muito próximo da máquina; para um
modelo standard da máquina (espaço de endereçamento planar, saltos)
©Universidade do Algarve402
Representação do Programa
Control Flow Graph (CFG): grafo de fluxo de controlo Nós do CFG são nós de instruções
• stl, sta, cbr, ldl, lda, ldp são nós de instruções• +, <, ... são nós de expressões
Laços no CFG representam o fluxo de controlo
Forks em instruções de salto condicional• Representam dois ou mais caminhos possíveis
Merges quando o controlo pode alcançar um ponto por caminhos múltiplos
Um nó de entrada (entry) e um nó de saída (exit)
©Universidade do Algarve403
ldl i
<
lda
+
ldp x
ldl i
sta
ldl i
ldp v
ldp v
ldp N
cbr
entry
exit
while (i < N) v[i] = v[i]+x;
Laços de fluxo de controlo
Laços entre Instruções e expressões
Exemplo: CFG
©Universidade do Algarve404
if (x < y) { a = 0;} else { a = 1;}
entry
ldl x ldl y
<
cbr
stl a 0 stl a 1
exit
Exemplo: CFG
©Universidade do Algarve405
Modelo de Memória da Máquina Alvo
Uma memória planar Composta por palavras Endereçável ao byte
Nós modelam instruções Load e Store ld addr,offset – resultado é
o conteúdo de memória no local: addr+offset
st addr, offset, valor – escreve valor no local: addr+offset
Substituir nós: lda e ldl por nós ld
Substituir nós: sta e stl por nós st
Stack
Código
Heap Arrays
locais(alguns parâmetros)
©Universidade do Algarve406
ld 4 ld 8 ld 8ld 4
* *
++
ldl
Descritor local para x (4)
*
ldl ldl
*
ldl
Descritor local para y (8)
Exemplo:
spsp spsp
ld address offset MEM[address+offset] No caso de offset=4 relativo ao sp
(MEM[$sp+offset]):
x*x+y*y
ld 4
sp
©Universidade do Algarve407
Parâmetros
Muitas máquinas têm convenções nas chamadas Primeiro parâmetro no registo 5, segundo
parâmetro no registo 6, ... ver $a0, $a1, … do MIPS
As convenções variam com a máquina Vamos assumir que cada parâmetro é uma
palavra Vamos endereçar os parâmetros pelo
número ldp <número do parâmetro>
©Universidade do Algarve408
Acesso a elementos de um Array
Assumir que a variável aponta para o primeiro elemento do array
Elementos do array em posições contíguas Qual é o endereço: v[5]?
v é um array de inteiros: assumir inteiros de 4 bytes
(endereço em v) + (5*4) Determinar endereço
Base do Array + (index * element size)
©Universidade do Algarve409
Exemplo: v[5]+x
lda
+
ldp
Descritor de parâmetro v (2)
Descritor de parâmetro de x (1)
5ldp ldp 2 *
5 4
+
ld 0
+
ldp 1
Conversão de nós lda para nós ld Determinar endereço
Base + (index * element size) ld do endereço Offset de ld é 0
©Universidade do Algarve410
Variáveis Locais
Assumir que são alocadas na pilha de chamadasEndereçamento realizado usando
offsets a partir do apontador da pilha Relembrar:
pilha cresce para baixo e por isso os offsets são positivos
Símbolo especial sp contém apontador para a pilha
©Universidade do Algarve411
Acções na invocação de funções (relembrar)
Invocadora Definir parâmetros de acordo com a convenção de
invocações Definir endereço de retorno utilizando a convenção de
invocações Saltar para a função invocada
Invocada Alocar stack frame = deslocar para baixo o apontador
da pilha (sp) computar Definir o valor de retorno de acordo com a convenção
de invocações Libertar stack frame = deslocar para cima o apontador
da pilha (sp) Retornar para a função invocadora
©Universidade do Algarve412
Gestão da Pilha (relembrar)
Determinar tamanho da stack frame Alocar quando se entra na função Libertar imediatamente antes do retorno da função Guarda todas as variáveis locais Mais espaço para parâmetros (quando estes ultrapassam
em número o número de registos convencionados como argumentos de funções)
Assume que todas as variáveis locais e os parâmetros têm o comprimento de uma palavra
Determinar offsets das variáveis locais e dos parâmetros Computar offsets das variáveis locais e dos parâmetros
Guardados nas tabelas de símbolos de locais e de parâmetros
Continua a usar nós ldp para aceder aos parâmetros
©Universidade do Algarve413
Eliminação de nós ldl
Uso de offsets na tabela de símbolos locais e sp Substituir nós ldl por nós ld Exemplo de offsets para locais e parâmetros
i descritor da variável local i (0)
Outras TS (variáveis globais, por exemplo)
TS de variáveis locais
x
TS de parâmetros
Descritor do parâmetro x (0)
Código para a função add
Descritor da função add
v Descritor do parâmetro v (1)
N Descritor do parâmetro N (2)
©Universidade do Algarve414
Exemplo: v[i]+x
lda
+
ldp
Descritor de parâmetro para v (1)
Descritor de parâmetro para x (0)
ldpldp 1 *
4
+
ld 0
+
ldp 0
Descritor de local para i (0)
ldl
sp
ld 0
©Universidade do Algarve415
Nós Enter e Exit para a função add
void add(int x, int[] v, int N) {int i; ...
} Qual o espaço na pilha para a função add?
4 bytes (espaço para i)• Assumindo palavras de 4 bytes• Assumindo parâmetros da função em registos
usados para passar argumentos Nós enter e exit são anotados com o valor
do espaço na pilha necessário para a função
enter 4
exit 4
....
©Universidade do Algarve416
ldp 1 *
4
+
ld 0
+
ldp 0
sp
ld 0
ldp 1 *
4
+
sp
ld 0
st 0
sp
ld 0
<
Exemplo
cbr
st 0
sp
ld 0+
1sp
st 0
sp 0
enter 4
exit 4
ldp 2
©Universidade do Algarve417
Sumário da IR de nível baixo
Acessos a arrays traduzidos para nós ld ou st Endereço é o endereço base do array
(apontador) + (index * element size) Acessos locais traduzidos para nós ld ou st
Endereço em sp, offset é o offset local Acesso a parâmetros traduzidos para:
Instruções lpd – especificar número de parâmetro
Nós Enter e Exit de uma função identificam tamanho da pilha utilizado
©Universidade do Algarve418
Sumário
Tradução de árvores sintácticas para IR de nível alto Preserva o fluxo de controlo estruturado Representação eficiente para análise de
nível alto e optimizações Tradução de IR de nível alto para IR de nível
baixo Espaço de endereçamento planar Remoção da estrutura do fluxo de controlo,
substituição por saltos condicionais Movimento em direcção à máquina alvo
©Universidade do Algarve419
Geração de Código Final
Compiladores
João M. P. Cardoso
©Universidade do Algarve420
Problema
Dada a representação intermédia de baixo nível como gerar o código assembly?
Não optimizado: Variáveis locais e parâmetros todos afectados a
posições relativas e distintas na pilha Optimizado:
Partilha de posições relativas da pilha por uma ou mais variáveis
Utilização de registos do banco de registos do uP para armazenar variáveis
...
©Universidade do Algarve421
Geração de código final
Code Generator
Código Assembly
Code Generator
Code Optimizer
Representação intermédia optimizada
Representação intermédia
Semantic AnalyzerSemantic Analyzer
Syntax Analyzer (Parser)
Árvore sintáctica
Syntax Analyzer (Parser)
Lexical Analyzer (Scanner)
Cadeia de Tokens
Programa (cadeia de caracteres)
©Universidade do Algarve422
Geração de código final
Y2=a*x*x+b*x+c;
Ldl x
*
+
+
ldl c
stl y2
Ldl x
*
Ldl a
Ldl x
*
Ldl b
IR de alto nível
©Universidade do Algarve423
Geração de código final
Y2=a*x*x+b*x+c;
Variáveis: A B X C y2 Ldl x
*
+
+
ldl c
stl y2
Ldl x
*
Ldl a
Ldl x
*
Ldl b
IR de alto nível
©Universidade do Algarve424
Geração de código final
Y2=a*x*x+b*x+c;
Variáveis (posição relativa a $sp)
A: 0 B: 4 X: 8 C: 12 Y2: 16 Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
©Universidade do Algarve425
Geração de código final
Y2=a*x*x+b*x+c;
Começar nas folhas:
lw $t0, 4($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t0>
©Universidade do Algarve426
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)
lw $t1, 8($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t0><$t1>
©Universidade do Algarve427
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)
lw $t1, 8($sp)
mult $t2, $t1, $t0
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2>
<$t0><$t1>
©Universidade do Algarve428
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)
lw $t1, 8($sp)
mult $t2, $t1, $t0
lw $t3, 8($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2>
<$t3>
©Universidade do Algarve429
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)
lw $t1, 8($sp)
mult $t2, $t1, $t0
lw $t3, 8($sp)
lw $t4, 8($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2>
<$t3> <$t4>
©Universidade do Algarve430
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)
lw $t1, 8($sp)
mult $t2, $t1, $t0
lw $t3, 8($sp)
lw $t4, 8($sp)
mult $t5, $t3, $t4 Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2>
<$t3> <$t4>
<$t5>
©Universidade do Algarve431
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp) Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2><$t5>
<$t6>
©Universidade do Algarve432
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)
lw $t1, 8($sp)
mult $t2, $t1, $t0
lw $t3, 8($sp)
lw $t4, 8($sp)
mult $t5, $t3, $t4
lw $t6, 0($sp)
mult $t7, $t5, $t6 Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2><$t5>
<$t6>
<$t7>
©Universidade do Algarve433
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6Add $t8, $t7, $t2 Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t2>
<$t7>
<$t8>
©Universidade do Algarve434
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6Add $t8, $t7, $t2lw $t9, 12($sp) Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t8><$t9>
©Universidade do Algarve435
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6Add $t8, $t7, $t2lw $t9, 12($sp)Add $t10, $t8, $t9 Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t8><$t9>
<$t10>
©Universidade do Algarve436
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6Add $t8, $t7, $t2lw $t9, 12($sp)Add $t10, $t8, $t9Sw $t10, 16($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t10>
©Universidade do Algarve437
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6add $t8, $t7, $t2lw $t9, 12($sp)add $t10, $t8, $t9sw $t10, 16($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
11 registos $t para armazenar valores intermédios!(MIPS tem 10 registos $t)
©Universidade do Algarve438
Geração de código final
Y2=a*x*x+b*x+c;
Solução utilizando menos registos para armazenar resultados intermédios?
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
©Universidade do Algarve439
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp) lw $t1, 8($sp) mult $t?, $t1, $t0
Resultado pode ser armazenado em $t1 ou em $t0
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t?>
<$t0><$t1>
©Universidade do Algarve440
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp) lw $t1, 8($sp) mult $t0, $t1, $t0 …
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t0>
<$t0><$t1>
©Universidade do Algarve441
Geração de código final
Y2=a*x*x+b*x+c;
lw $t0, 4($sp)lw $t1, 8($sp)mult $t0, $t1, $t0lw $t1, 8($sp)lw $t2, 8($sp)mult $t1, $t1, $t2lw $t2, 0($sp)mult $t1, $t1, $t2add $t0, $t1, $t0lw $t1, 12($sp)add $t0, $t0, $t1sw $t0, 16($sp)
Ld 8
*
+
+
ld 12
st 16
Ld 8
*
Ld 0
Ld 8
*
Ld 4
IR de baixo nível
sp
sp
spsp
sp
sp sp
<$t0><$t1>
<$t0>
<$t2><$t1>
<$t1><$t2>
<$t1>
<$t0><$t1>
<$t0>3 registos $t para armazenar valores intermédios
©Universidade do Algarve442
Não optimizado
Acessos à pilha requerem mais ciclos do que acessos a registos internos
Utilização da pilha para todas as variáveis requer mais instruções
©Universidade do Algarve443
Geração de código
Utilização de esqueletos (templates) para geração de código assembly paraEstruturas If-then ou if-then-else Loops
©Universidade do Algarve444
Geração de código
if (test)
true_body
else
false_body
<do the test>
boper …, lab_true
<false_body>
j lab_end
lab_true:
<true_body>
lab_end:
©Universidade do Algarve445
Geração de código
while (test)
body
lab_cont:<do the test>boper …, lab_bodyj lab_end
lab_body:<body>j lab_cont
lab_end:
• Esqueleto optimizado: lab_cont:<do the test>boper …, lab_end<body>j lab_cont
lab_end:
©Universidade do Algarve446
Linhas mestras para o gerador de código Descer o nível de abstracção devagar: Utilizar várias
etapas Mesmo que apenas se faça uma coisa por cada
etapa Mais fácil de depurar, mais fácil de lidar com o
problema Manter o nível de abstracção consistente
IR deve manter a semântica correcta sempre! Pode ser necessário realizar optimizações entre
etapas Utilizar assertions (assertividades) deliberadamente
Utilizar uma assertion para verificar um pressuposto Ajudam a encontrar bugs!
©Universidade do Algarve447
Linhas mestras para o gerador de código
Começar com geração simples, mesmo que naïfOk gerar: 0 + 1*x + 0*y
A biblioteca de runtime é nossa amiga!Não tentes gerar código assembly
quando existem rotinas na biblioteca com a mesma funcionalidade
Exemplo: malloc
©Universidade do Algarve448
Linhas mestras para o gerador de código Lembrar que as optimizações vêm depois
O optimizador realiza as optimizações Pensar o que o optimizador necessita e estruturar o
código de acordo com isso Exemplo: alocação de registos, simplificações
algébricas, propagação de constantes Utilizar uma boa infra-estrutura de teste
Teste regressivo• Se um programa cria um bug adicioná-lo ao teste suite
Utilizar makefiles: para executar o compilador sobre o teste suite e verificar automaticamente se os resultados estão correctos (pode implicar a utilização de um simulador da arquitectura)
Ver teste na Engenharia de Software
©Universidade do Algarve449
Optimizações
Compiladores
João M. P. Cardoso
©Universidade do Algarve450
Alocação de registos
Atribuir o maior número de variáveis a registos
Utilizar cada registo para armazenar o maior número possível de variáveis (registos são poucos...)Tempo de vida de variáveis
Uma das optimizações com maior impacto (tamanho do código e desempenho)
©Universidade do Algarve451
Tempo de Vida de Variáveis
Duração numa sequência de instruções entre a definição de uma variável e o seu uso
A = b*c;
D=b*b+e;
E=A+D;
Tempo de vida de A
©Universidade do Algarve452
Tempo de Vida de Variáveis
Y2=a*x*x+b*x+c
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6add $t8, $t7, $t2lw $t9, 12($sp)add $t10, $t8, $t9sw $t10, 16($sp)
$t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10
©Universidade do Algarve453
Tempo de Vida de Variáveis
Y2=a*x*x+b*x+c
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6add $t8, $t7, $t2lw $t9, 12($sp)add $t10, $t8, $t9sw $t10, 16($sp)
$t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10
3 registos
©Universidade do Algarve454
Y2=a*x*x+b*x+c
lw $t0, 4($sp) lw $t1, 8($sp) mult $t2, $t1, $t0 lw $t3, 8($sp) lw $t4, 8($sp) mult $t5, $t3, $t4 lw $t6, 0($sp) mult $t7, $t5, $t6 add $t8, $t7, $t2 lw $t9, 12($sp) add $t10, $t8, $t9 sw $t10, 16($sp)
Afectação de registos a variáveis
$t0$t1 $t2$t3 $t4$t5 $t6$t7 $t8$t9$t10
©Universidade do Algarve455
Y2=a*x*x+b*x+c
lw $t0, 4($sp) lw $t1, 8($sp) mult $t2, $t1, $t0 lw $t3, 8($sp) lw $t4, 8($sp) mult $t5, $t3, $t4 lw $t6, 0($sp) mult $t7, $t5, $t6 add $t8, $t7, $t2 lw $t9, 12($sp) add $t10, $t8, $t9 sw $t10, 16($sp)
Afectação de registos a variáveis
$t0$t1 $t2$t3 $t4$t5 $t6$t7 $t8$t9$t10
4 registos
©Universidade do Algarve456
Afectação de registos a variáveis
Determinar tempo de vida das variáveis
Afectar um registo a uma ou mais variáveis
Como?Por exemplo: Coloração de grafos
(problema NP-complexo)• Heurísticas: Algoritmo left-edge
©Universidade do Algarve457
Afectação de registos a variáveis Algoritmo Left-edge (utilizado no exemplo
em que se obteve 3 registos) Ordenar segmentos (intervalos) pelo valor
do tempo de início Começar pelo primeiro segmento e tentar
adicionar segmentos pela ordem sempre que não haja sobreposição
Quando não for possível adicionar mais segmentos, voltar ao passo anterior considerando o segmento seguinte
Número de registos = número de colunas com segmentos
©Universidade do Algarve458
Afectação de registos a variáveis Algoritmo Left-edge
LEFT_EDGE(I) {Sort elements of I in a list L in ascending order of li ;c = 0;while (some interval has not been colored ) do {
S = ;r = 0;while ( s L such that ls > r) do {∃ ∈
s = First element in the list L with ls > r ;S = S { s } ;∪r = rs ;Delete s from L;
}c = c +1;Label elements of S with color c;
}}
©Universidade do Algarve459
Afectação de registos a variáveis Coloração de grafos
Determinar tempo de vida das variáveis Construir grafo de interferências* (existe interferência
quando duas variáveis têm tempos de vida com sobreposição)
• Laços representam interferência• Nós representam variáveis
Determinar o número mínimo de cores do grafo (pode ser utilizado o algoritmo left-edge)
Cada cor corresponde a um registo (número de registos = número de cores)
* Também chamado de grafo de conflitos
©Universidade do Algarve460
Afectação de registos a variáveis
Tempo de vida das variáveis
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6add $t8, $t7, $t2lw $t9, 12($sp)add $t10, $t8, $t9sw $t10, 16($sp)
$t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10
©Universidade do Algarve461
Afectação de registos a variáveis
Grafo de interferências
lw $t0, 4($sp)lw $t1, 8($sp)mult $t2, $t1, $t0lw $t3, 8($sp)lw $t4, 8($sp)mult $t5, $t3, $t4lw $t6, 0($sp)mult $t7, $t5, $t6add $t8, $t7, $t2lw $t9, 12($sp)add $t10, $t8, $t9sw $t10, 16($sp)
$t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10
$t0 $t1
$t2 $t3
$t4
$t5
$t6
$t7
$t8
$t9
$t10
©Universidade do Algarve462
Afectação de registos a variáveis
Grafo de interferênciasInterferência
(laço) entre duas variáveis (nós) indica que não podem ser armazenadas no mesmo registo
v0 v1
v2 v3
v4
v5
v6
v7
v8
v9 v10
©Universidade do Algarve463
Afectação de registos a variáveis
Grafo de interferências
Depois de colorido:Número de cores
indica o número de registos necessário
v0 v1
v2 v3
v4
v5
v6
v7
v8
v9 v10
©Universidade do Algarve464
Afectação de registos a variáveis: exercício
Considerando o código ao lado, determine o número de registos necessário para armazenar as variáveis utilizando o algoritmo left_edge (y2 é a única variável utilizada posteriormente)
T1=x*x;T2=a*t1;T3=b*x;T4=t3+c;T5=t4+t2;Y2=t5;
©Universidade do Algarve465
Afectação de registos a variáveis
Na presença de estruturas condicionais Problema mais complicado Resolução similar
©Universidade do Algarve466
Optimizações
Existem muitas outras optimizações Loop unrolling (existe uma série de optimizações ao
nível de loops) propagação de constantes redução de força (strength reduction) avaliação de expressões com constantes eliminação de sub-expressões comuns, substituição por escalares (scalar replacement) eliminação de acessos a memória redundantes eliminação de código morto movimento de código
©Universidade do Algarve467
Fim!
Disponível para orientar alunos interessados em realizar projectos sobre estes temasNo ensino de certos tópicos:
utilizando, por exemplo, Java AppletsNa criação de infra-estruturas para
apoio à leccionaçãoEm tópicos avançados relacionados
com investigação