A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Introdução da Disciplina

Apresentações semelhantes


Apresentação em tema: "Introdução da Disciplina"— Transcrição da apresentação:

1 Introdução da Disciplina
Compiladores João M. P. Cardoso Universidade do Algarve Faro, Portugal ©Universidade do Algarve

2 ©Universidade do Algarve
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 Algarve

3 ©Universidade do Algarve
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 Algarve

4 ©Universidade do Algarve
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? Interpretador Compilador ©Universidade do Algarve

5 Compilador Ponto de Origem
Código fonte descrito numa linguagem de alto-nível (ex.: C, C++, Pascal, etc.) Compilador Programa em Linguagem Máquina Ponto de Destino ©Universidade do Algarve

6 ©Universidade do Algarve
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 Algarve

7 ©Universidade do Algarve
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, 0 Loop: 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 Loop End: jr $ra ©Universidade do Algarve

8 ©Universidade do Algarve
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 Algarve

9 ©Universidade do Algarve
Docentes João M. P. Cardoso Gab. 2.65 ©Universidade do Algarve

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

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

12 ©Universidade do Algarve
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 Algarve

13 ©Universidade do Algarve
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, [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, [existe uma tradução brasileira] Rui Gustavo Crespo. Processadores de Linguagens. IST Press Com tópicos mais avançados: Steven S. Muchnick. Advanced Compiler Design and Implementation, Morgan Kaufmann Publishers, 1997. ©Universidade do Algarve

14 Boa sorte! Página Web da disciplina:
POR EXEMPLO, TENHO QUE SABER COMO FUNCIONA UM COMPILADOR. DEPOIS? DEPOIS ESTAMOS PRONTOS PARA CONSTRUIR UM COMPILADOR! E DEPOIS? Página Web da disciplina: Lista de correio electrónico Para subscrever: ©Universidade do Algarve

15 Do alto-nível ao assembly
Compiladores João M. P. Cardoso ©Universidade do Algarve

16 ©Universidade do Algarve
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 Algarve

17 Do alto-nível ao assembly
Alvo: MIPS R3000 # $a0 armazena o endereço de A[0] # $a1 armazena o valor de N Sum: Addi $t0, $0, 0 # i = 0 Addi $v0, $0, 0 # sum = 0 Loop: beq $t0, $a1, End # if(i == N) goto End; Add $t1, $t0, $t0 # 2*i Add $t1, $t1, $t1 # 2*(2*i) = 4*i Add $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 Int sum(int A[], int N) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum; ©Universidade do Algarve

18 Do alto-nível ao assembly
Variáveis globais: Armazenadas na memória Para cada uso de uma variável global o compilador tem de gerar instruções load/store int a, b, c; ... fun(...) { ... } Memória a b c ©Universidade do Algarve

19 ©Universidade do Algarve
Variáveis Globais .data 0x a: .space 4 b: .space 4 c: .space 4 .text fun: … la $t1, a lw $t1, 0($t1) la $t2, b lw $t2, 0($t2) add $t3, $t2, $t1 la $t4, c sw $t3, 0($t4) Alocação de memória Int a, b, c; void fun() { c = a + b; } Memória a b c 0x 0x 0x ©Universidade do Algarve

20 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 Registos c b a $sp ©Universidade do Algarve

21 ©Universidade do Algarve
Variáveis Locais Reserva espaço na pilha fun: addi $sp, $sp, -12 lw $t1, 0($sp) lw $t2, 4($sp) add $t3, $t2, $t1 sw $t3, 8($sp) addi $sp, $sp, 12 jr $ra Exemplo: Load a Load b a + b store c liberta espaço na pilha void fun() { int a, b, c; ... c = a + b; } Memória c b a $sp ©Universidade do Algarve

22 ©Universidade do Algarve
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 Algarve

23 ©Universidade do Algarve
Variáveis Locais Utilização de registos internos Ficheiro de Registos fun: … add $t3, $t2, $t1 jr $ra void fun() { int a, b, c; ... c = a + b; } $t1 a $t2 b $t3 c ©Universidade do Algarve

24 Do alto-nível ao assembly
Implementar Registos Registos 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 z y x p ©Universidade do Algarve

25 Do alto-nível ao assembly
fun: addi $sp, $sp, -16 lw $t1, 0($sp) addi $t1, $t1, 8 lw $t2, 0($t1) addi $t1, $t1, 12 lw $t3, 0($t1) add $t3, $t2, $t3 addi $t1, $t1, 4 sw $t3, 0($t1) addi $sp, $sp, 16 jr $ra Reserva espaço na pilha Exemplo com estrutura local: Endereço de p address p->y Load p->y Endereço de p Memória z y x p address p->z typedef struct { int x, y, z; } foo; fun() { foo *p; p->x = p->y + p->z; } Load p->z p->y + p->z Endereço de p address p->x store em p->x liberta espaço na pilha ©Universidade do Algarve

26 Do alto-nível ao assembly
Exemplo com estrutura local (optimizado): Reserva espaço na pilha fun: addi $sp, $sp, -16 lw $t2, 8($sp) lw $t3, 12($sp) add $t3, $t2, $t3 sw $t3, 4($sp) addi $sp, $sp, 16 Memória z y x p typedef struct { int x, y, z; } foo; fun() { foo *p; p->x = p->y + p->z; } Load p->y Load p->z p->y + p->z store em p->x liberta espaço na pilha ©Universidade do Algarve

27 Do alto-nível ao assembly
Compiladores João M. P. Cardoso ©Universidade do Algarve

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

29 ©Universidade do Algarve
Alinhamento Organização “ingénua” Organização Empacotada (poupa 4 bytes) Memória typedef struct { int w; char x; int y; char z; } foo; foo *p; Memória z y x y w x, z w p p livre ocupado 32 bits 32 bits ©Universidade do Algarve

30 ©Universidade do Algarve
Arrays Afectação de posições de memória para os elementos do array Elementos são armazenados contiguamente int a[4]; short a[4]; Memória Memória a[3] a[2] a[1] a[2] a[3] a[0] a[0] a[1] 32 bits 32 bits ©Universidade do Algarve

31 ©Universidade do Algarve
Arrays .data A: .space 16 .text Proc: la $t0, A addi $t2, $0, 4 mult $t1, $t2 mflo $t2 add $t3, $t2, $t0 lw $t4, 0($t3) Utilizando registos do processador para armazenar as variáveis i e j: 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 Algarve

32 ©Universidade do Algarve
Expressões a = b * c + d – e; a em $t4; b em $t0; c em $t1; d em $t2; e em $t3 mult $t0, $t1 mflo $t4 sub $t5, $t2, $t3 add $t4, $t4, $t5 mult $t0, $t1 mflo $t4 add $t4, $t4, $t2 sub $t4, $t4, $t3 ©Universidade do Algarve

33 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, 1 bne $t2, $t0, skip_if addi $t1, $0, 2 Skip_if: … addi $t2, $0, 1 bne $t2, $t0, else addi $t1, $0, 2 j skip_if Else: addi $t1, $0, 1 Skip_if: … ©Universidade do Algarve

34 Estruturas condicionais
If(a == 1) b = 2; C = a+1; a em $t0; b em $t1 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 addi $t2, $0, 1 bne $t2, $t0, skip_if addi $t3, $t0, 1 addi $t1, $0, 2 Skip_if: … ©Universidade do Algarve

35 ©Universidade do Algarve
Ciclos Transformar o fluxo de controlo (while, for, do while, etc.) em saltos # $a0 armazena o endereço de A[0] # $a1 armazena o valor de N Sum: Addi $t0, $0, 0 # i = 0 Addi $v0, $0, 0 # sum = 0 Loop: beq $t0, $a1, End # if(i == N) goto End; Add $t1, $t0, $t0 # 2*i Add $t1, $t1, $t1 # 2*(2*i) = 4*i Add $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 Int sum(int A[], int N) { Int i, sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum; ©Universidade do Algarve

36 ©Universidade do Algarve
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 Algarve

37 ©Universidade do Algarve
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 N Sum: Addi $t0, $0, 0 # i = 0 Addi $v0, $0, 0 # sum = 0 Loop: Lw $t2, 0($a0) # load A[i] Add $v0, $v0, $t2 # sum = sum + A[i] addi $a0, $a0, 4 # add 4 to address Addi $t0, $t0, 1 # i++ bne $t0, $a1, Loop # if(i != N) goto Loop; End: jr $ra # return ©Universidade do Algarve

38 ©Universidade do Algarve
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 Algarve

39 ©Universidade do Algarve
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 Algarve

40 Anatomia de um Compilador
Compiladores João M. P. Cardoso ©Universidade do Algarve

41 ©Universidade do Algarve
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 Algarve

42 Análise Lexical Programa (cadeia de caracteres)
Lexical Analyzer (Scanner) Cadeia de Tokens ©Universidade do Algarve

43 Análise Lexical /* uma expressão simples */ y = b*x +c; // atribui a y
Lexical Analyzer (Scanner) ID(y) EQ ID(b) TIMES ID(x) PLUS ID(c) SEMICOLON EOF ©Universidade do Algarve

44 ©Universidade do Algarve
Análise Lexical Tratamento de erros Falta terminar comentário */ /* exemplo Int sum(int A[], int N) { Int i, 5sum = 0; For(i=0; i<N; i++) { sum = sum + A[i]; } return sum; Não é uma palavra reservada nem um identificador ©Universidade do Algarve

45 Análise Sintáctica Programa (cadeia de caracteres)
Lexical Analyzer (Scanner) Cadeia de Tokens Syntax Analyzer (Parser) Syntax Analyzer (Parser) Árvore sintáctica ©Universidade do Algarve

46 ©Universidade do Algarve
Análise Sintáctica Árvore Sintáctica (concreta) <stmt> y = b*x + c = <ident> <expr> y <expr> <op> <ident> + c <ident> <op> <ident> b x * ©Universidade do Algarve

47 ©Universidade do Algarve
Análise Sintáctica Árvore Sintáctica (abstracta): AST y = b*x + c = y + c * b x ©Universidade do Algarve

48 ©Universidade do Algarve
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; Parêntesis a mais Falta ‘;’ “retur” não é palavra reservada: dois identificadores seguidos? Chaveta a mais ©Universidade do Algarve

49 Análise Semântica tmp1 = b*x; tmp2 = tmp1 + c;
Programa (cadeia de caracteres) Lexical Analyzer (Scanner) Cadeia de Tokens Syntax Analyzer (Parser) Syntax Analyzer (Parser) Árvore sintáctica Semantic Analyzer Semantic Analyzer Representação intermédia tmp1 = b*x; tmp2 = tmp1 + c; ©Universidade do Algarve

50 ©Universidade do Algarve
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 Variável não declarada Tipo da variável retornada não confere com a declaração do cabeçalho da função ©Universidade do Algarve

51 Optimização de código Programa (cadeia de caracteres)
Lexical Analyzer (Scanner) Cadeia de Tokens Syntax Analyzer (Parser) Syntax Analyzer (Parser) Árvore sintáctica Semantic Analyzer Semantic Analyzer Representação intermédia Code Optimizer Code Optimizer Representação intermédia optimizada ©Universidade do Algarve

52 Geração de código assembly
Programa (cadeia de caracteres) Lexical Analyzer (Scanner) Cadeia de Tokens Syntax Analyzer (Parser) Syntax Analyzer (Parser) Árvore sintáctica Semantic Analyzer Semantic Analyzer Representação intermédia Code Optimizer Code Optimizer Representação intermédia optimizada Code Generator Code Generator mult $t4, $t1,$t2; Add $t4, $t4, $t3; Código Assembly ©Universidade do Algarve

53 ©Universidade do Algarve
TPC Apresente os resultados de cada etapa de compilação para a expressão: y = a*x*x+b*x+c; ©Universidade do Algarve

54 Compiladores João M. P. Cardoso
Análise Lexical Compiladores João M. P. Cardoso ©Universidade do Algarve

55 ©Universidade do Algarve
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 Algarve

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

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

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

59 ©Universidade do Algarve
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 Algarve

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

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

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

63 ©Universidade do Algarve
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 Algarve

64 ©Universidade do Algarve
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 Algarve

65 Compiladores João M. P. Cardoso
Análise Lexical Compiladores João M. P. Cardoso ©Universidade do Algarve

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

67 ©Universidade do Algarve
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 Algarve

68 ©Universidade do Algarve
Autómatos Finitos Exemplo (0 | 1)*”.”(0 | 1)* 1 1 Estado de início Estado de aceitação ©Universidade do Algarve

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

70 ©Universidade do Algarve
Exemplo 1 1 Estado de início Estado de aceitação 11.0 Símbolo corrente ©Universidade do Algarve

71 ©Universidade do Algarve
Exemplo 1 1 Estado de início Estado de aceitação 11.0 Símbolo corrente ©Universidade do Algarve

72 ©Universidade do Algarve
Exemplo 1 1 Estado de início Estado de aceitação 11.0 Símbolo corrente ©Universidade do Algarve

73 ©Universidade do Algarve
Exemplo 1 1 Estado de início Estado de aceitação 11.0 Símbolo corrente ©Universidade do Algarve

74 ©Universidade do Algarve
Exemplo 1 1 Estado de início Estado de aceitação 11.0 Símbolo corrente ©Universidade do Algarve

75 ©Universidade do Algarve
Exemplo 1 1 Estado de início Estado de aceitação 11.0 Símbolo corrente String aceite! ©Universidade do Algarve

76 ©Universidade do Algarve
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 Algarve

77 ©Universidade do Algarve
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 NOT OK OK b a ©Universidade do Algarve

78 ©Universidade do Algarve
Autómatos Finitos Autómatos Finitos Deterministas (DFAs) Implementações mais rápidas do que para os NFAs, mas Maior complexidade do autómato ©Universidade do Algarve

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

80 Compiladores João M. P. Cardoso
Análise Lexical Compiladores João M. P. Cardoso ©Universidade do Algarve

81 Da Expressão Regular ao Autómato
Tradução da Expressão Regular para um Autómato Implementação do Autómato ©Universidade do Algarve

82 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 ©Universidade do Algarve

83 ©Universidade do Algarve
Construtores Básicos a a ©Universidade do Algarve

84 ©Universidade do Algarve
Sequência r1.r2 r1 r2 ©Universidade do Algarve

85 ©Universidade do Algarve
Selecção r1 | r2 r1 r2 ©Universidade do Algarve

86 ©Universidade do Algarve
Asterisco de Kleene r* r ©Universidade do Algarve

87 ©Universidade do Algarve
Regras de Conversão r1 r2 r1.r2 r1 r2 r1 | r2 a a r r* ©Universidade do Algarve

88 ©Universidade do Algarve
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 Algarve

89 ©Universidade do Algarve
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 Algarve

90 ©Universidade do Algarve
NFA para DFA Exemplo: (0 | 1)*.(0|1)* . 1 1 3 5 11 13 1 2 7 8 9 10 15 16 4 6 12 14 1 1 5,7,2,3,4,8 13,15,10,11,12,16 1 1 1 1 1,2,3,4,8 9,10,11,12,16 6,7,2,3,4,8 14,15,10,11,12,16 ©Universidade do Algarve

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

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

93 Compiladores João M. P. Cardoso
Análise Lexical Compiladores João M. P. Cardoso ©Universidade do Algarve

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

95 ©Universidade do Algarve
Interpretador do DFA Algoritmo do Interpretador State = DFA initial state; inputChar = getchar(); While(inputChar) { State = trans(State, inputChar]); } If(State é um estado de aceitação) realizar acção relativa ao estado State (reconheceu String) Else realizar outra acção ©Universidade do Algarve

96 ©Universidade do Algarve
NFA para DFA Exemplo: (0 | 1)*.(0|1)* . 1 1 3 5 11 13 1 2 7 8 9 10 15 16 4 6 12 14 1 1 5,7,2,3,4,8 13,15,10,11,12,16 1 1 1 1 1,2,3,4,8 9,10,11,12,16 6,7,2,3,4,8 14,15,10,11,12,16 ©Universidade do Algarve

97 ©Universidade do Algarve
DFA Estado Actual Próximo Estado “0” “1” “.” 1 3 2 4 6 5 Tabela de transição de estados (adicionamos o estado 0 – estado morto - para as transições não apresentadas) 1 1 2 5 1 1 1 1 1 4 3 6 ©Universidade do Algarve

98 ©Universidade do Algarve
Implementação do DFA Estado Actual Próximo Estado “0” “1” “.” 1 3 2 4 6 5 Implementar a tabela de transições de estados com um array bidimensional 1 1 2 5 1 1 1 1 1 4 3 6 ©Universidade do Algarve

99 ©Universidade do Algarve
Implementação do DFA Estado Actual Próximo Estado “0” “1” “.” 1 3 2 4 6 5 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 1 2 5 1 1 1 1 1 4 3 6 ©Universidade do Algarve

100 ©Universidade do Algarve
Implementação do DFA Estado Actual Próximo Estado “0” “1” “.” 1 3 2 4 6 5 int Edge[NumStates][256] = { /* “.” ... */ /* 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 ©Universidade do Algarve

101 ©Universidade do Algarve
Implementação do DFA Estado Actual Próximo Estado “0” “1” “.” 1 3 2 4 6 5 Um array unidimensional traduz o número de estado no número da acção a realizar: // estado int final[NumStates] = {0, 0, 0, 0, 1, 1, 1} Exemplo: final[3]  3 (logo deve ser realizada a acção 3) ©Universidade do Algarve

102 ©Universidade do Algarve
Implementação do DFA Programação do Interpretador State = 1; // estado inicial do DFA inputChar = input.read(); While(inputChar) { State = edge[State][inputChar]; } If(final[State] == 1) realizar acção (reconheceu String) Else realizar outra acção ©Universidade do Algarve

103 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” “.” ... */ Int map[256] = { ..., 0, 1, 3, 3, , , ...} Exemplo: Edge[3][(int) ‘.’] é feito assim: Edge[3][map[(int) ‘.’]] ©Universidade do Algarve

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

105 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

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

107 ©Universidade do Algarve
Solução Gramáticas independentes do contexto Reconhecimento realizado por autómatos finitos equipados com uma pilha O que são gramáticas independentes do contexto? ©Universidade do Algarve

108 ©Universidade do Algarve
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 Algarve

109 ©Universidade do Algarve
Gramáticas EBNF, ou BNF estendido Inclui { } para representar 0 ou mais repetições de símbolos e [ ] para representar elementos opcionais ©Universidade do Algarve

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

111 Jogo da produção/derivação
Dada a frase corrente Começar pelo símbolo não-terminal Start Repetir até não haver símbolos não-terminais Seleccionar um símbolo não-terminal Seleccionar uma produção com símbolos não-terminais em LHS Substituir símbolo não-terminal com o RHS da produção Substituir a expressão regular com as Strings correspondentes String gerada está na linguagem Nota: selecções diferentes produzem Strings diferentes ©Universidade do Algarve

112 ©Universidade do Algarve
Produção/Derivação OP = +|-|*|/ INT = [0-9] [0-9]* OPEN = ( CLOSE = ) 1) Start  Expr 2) Expr  Expr OP Expr 3) Expr  INT 4) Expr  OPEN Expr CLOSE 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 ( ) + 1 ©Universidade do Algarve

113 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

114 ©Universidade do Algarve
Á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 Algarve

115 Árvore Sintáctica para (2-1)+1
Start Expr Expr Expr OP + OPEN ( CLOSE ) Expr INT 1 Expr Expr OP - INT 2 INT 1 ©Universidade do Algarve

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

117 Exemplo de ambiguidade
Duas árvores sintácticas para 2-1+1 Árvore correspondente a 2-(1+1) Árvore correspondente a (2-1)+1 Start Start Expr Expr Expr OP - Expr Expr OP + Expr INT 2 INT 1 Expr OP + Expr Expr OP - Expr INT 1 INT 1 INT 2 INT 1 ©Universidade do Algarve

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

119 Árvore sintáctica para a gramática
Apenas uma árvore sintáctica para: 2-1+1 Árvore sintáctica inválida Árvore sintáctica válida Start Start Expr Expr Expr OP - Expr Expr OP + INT 1 INT 2 Expr OP + Expr Expr OP - INT 1 INT 1 INT 1 INT 2 ©Universidade do Algarve

120 Violação de prioridade
Árvore sintáctica para 2-3*4 Todos os operadores associam à esquerda Viola prioridade de * sobre + 2-3*4 associa como (2-3)*4 Start Expr Expr OP * INT 4 Expr OP - INT 3 INT 2 ©Universidade do Algarve

121 ©Universidade do Algarve
Resolver prioridade Gramática Modificada OP1 = + | - 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 Gramática Original OP = + | - | * | / INT = [0-9] [0-9]* OPEN = ( CLOSE = ) Start  Expr Expr  Expr OP INT Expr  INT Expr  OPEN Expr CLOSE ©Universidade do Algarve

122 Modificação nas Árvores Sintácticas
Nova Árvore sintáctica para 2-3*4 Velha Árvore sintáctica para 2-3*4 Start Start Expr Expr Expr OP * Expr OP1 - INT 4 Term Term Expr OP - INT 3 Term OP2 * INT 4 INT 2 INT 2 INT 3 ©Universidade do Algarve

123 ©Universidade do Algarve
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 Algarve

124 ©Universidade do Algarve
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 Algarve

125 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

126 Manuseando estruturas if-then-else
Start  Stat Stat  IF Expr THEN Stat ELSE Stat Stat  IF Expr THEN Stat Stat  ... ©Universidade do Algarve

127 ©Universidade do Algarve
Árvore Sintáctica Considere o enunciado: if e1 then if e2 then s1 else s2 ©Universidade do Algarve

128 ©Universidade do Algarve
Árvore Sintáctica Stat Duas Árvores Sintácticas IF Stat Expr e1 IF Expr THEN Stat ELSE Stat Stat e2 s1 s2 IF Expr THEN Stat ELSE Stat Qual é a correcta? e1 s2 IF Expr s1 THEN ©Universidade do Algarve e2

129 Leituras alternativas
Gramática ambígua Árvore sintáctica 1 if e1 if e2 s1 else s2 Árvore sintáctica 2 ©Universidade do Algarve

130 ©Universidade do Algarve
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 Algarve

131 ©Universidade do Algarve
Gramática modificada Ideia básica: controlar quando um IF sem ELSE pode ocorrer No nível de topo do enunciado Ou como último numa sequência de enunciados if then else if then ... ©Universidade do Algarve

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

133 ©Universidade do Algarve
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 Algarve

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

135 Exemplo Gramática não-ambígua Gramática intuitiva mas ambígua
OP1 = + | - 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 Gramática intuitiva mas ambígua OP = * | / | + | - INT = [0-9] [0-9]* Start  Expr Expr  Expr OP Expr Expr  INT ©Universidade do Algarve

136 ©Universidade do Algarve
Exemplo Start Start Árvore sintáctica abstracta para (2-3)*4 Árvore sintáctica concreta para (2-3)*4 Expr Expr Term OP * Expr Expr OP1 * INT 4 Term Expr Expr INT 4 OP - OPEN Expr CLOSE INT 2 INT 3 Expr OP2 - Term Utiliza gramática intuitiva Elimina terminais supérfluos OPEN, CLOSE, etc. Term INT 3 INT 2 ©Universidade do Algarve

137 ©Universidade do Algarve
Exemplo AST para (2-3)*4 Ainda mais simplificada Árvore sintáctica abstracta para (2-3)*4 Start Start Expr Expr OP * Expr Expr Expr OP * INT 4 Expr Expr INT 4 INT 2 OP - INT 3 OP - INT 2 INT 3 ©Universidade do Algarve

138 ©Universidade do Algarve
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 Algarve

139 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

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

141 ©Universidade do Algarve
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 Algarve

142 ©Universidade do Algarve
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 Algarve

143 ©Universidade do Algarve
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 Algarve

144 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2-2*2 Forma sentencial Start Posição corrente na árvore sintáctica ©Universidade do Algarve

145 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2-2*2 Expr Forma sentencial Expr Produção Aplicada Start  Expr Posição corrente na árvore sintáctica ©Universidade do Algarve

146 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada a faltar Start 2-2*2 Expr Forma sentencial Expr - Term Expr - Term Produção Aplicada Expr  Expr - Term ©Universidade do Algarve

147 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2-2*2 Expr Forma sentencial Term - Term Expr - Term Produção Aplicada Term Expr  Term ©Universidade do Algarve

148 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2-2*2 Expr Forma sentencial INT - Term Expr - Term Produção Aplicada Term Term  INT INT ©Universidade do Algarve

149 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Token corresponde! Entrada que falta Start 2-2*2 Expr Forma sentencial 2 - Term Expr - Term Term INT 2 ©Universidade do Algarve

150 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Token corresponde! Entrada que falta Start -2*2 Expr Forma sentencial 2 - Term Expr - Term Term INT 2 ©Universidade do Algarve

151 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2*2 Expr Forma sentencial 2 - Term Expr - Term Term INT 2 ©Universidade do Algarve

152 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2*2 Expr Forma sentencial 2 – Term*INT Expr - Term Produção Aplicada Term Term Term  Term * INT INT * INT 2 ©Universidade do Algarve

153 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2*2 Expr Forma sentencial 2 – INT*INT Expr - Term Produção Aplicada Term Term Term  INT INT * INT 2 INT ©Universidade do Algarve

154 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Token corresponde! Entrada que falta Start 2*2 Expr Forma sentencial 2 – 2*INT Expr - Term Term Term INT * INT 2 INT 2 ©Universidade do Algarve

155 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Token corresponde! Entrada que falta Start *2 Expr Forma sentencial 2 – 2*INT Expr - Term Term Term INT * INT 2 INT 2 ©Universidade do Algarve

156 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Entrada que falta Start 2 Expr Forma sentencial 2 – 2*INT Expr - Term Term Term INT * INT 2 INT 2 ©Universidade do Algarve

157 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Token corresponde! Entrada que falta Start 2 Expr Forma sentencial 2 – 2*2 Expr - Term Term Term INT 2 * INT 2 INT 2 ©Universidade do Algarve

158 Analisador Sintáctico para a gramática exemplo
Árvore Sintáctica Análise sintáctica terminou Entrada que falta Start Expr Forma sentencial 2 – 2*2 Expr - Term Term Term INT 2 * INT 2 INT 2 ©Universidade do Algarve

159 ©Universidade do Algarve
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 Algarve

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

161 ©Universidade do Algarve
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 Algarve

162 ©Universidade do Algarve
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 Algarve

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

164 Solução: factorizar a gramática à esquerda
Nova gramática factoriza o prefixo comum numa única produção Nt  IF THEN Nt’ Nt’  ELSE Nt’   Nenhuma escolha quando o próximo token é um IF Todas as alternativas foram unificadas numa única produção ©Universidade do Algarve

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

166 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

167 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 Term Term Num Term Num Term * * Term Num ©Universidade do Algarve *

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

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

170 Eliminar recursividade à esquerda
Produções de substituição A A  A   R R é um novo símbolo não-terminal A   R   R R   Nova Árvore A Árvore inicial R A R A R ©Universidade do Algarve

171 ©Universidade do Algarve
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 Algarve

172 Gramática Modificada Pedaço da gramática original Term  Term “*” INT
Pedaço da gramática modificada Term  INT Term’ Term’  “*” INT Term’ Term’  “/” INT Term’ Term’   ©Universidade do Algarve

173 ©Universidade do Algarve
Gramática Modificada INT = [0-9]+ INT = [0-9]+ Start  Expr Expr  Expr “+” Term Expr  Expr “-” Term Expr  Term Term  Term “*” INT Term  Term “/” INT Term  INT Start  Expr Expr  Term Expr’ Expr’  “+” Term Expr’ Expr’  “-” Term Expr’ Expr’   Term  INT Term’ Term’  “*” INT Term’ Term’  “/” INT Term’ Term’   ©Universidade do Algarve

174 Comparando as Árvores Sintácticas
Gramática original Gramática modificada Term Term INT Term’ INT Term * Term’ * INT INT * INT Term’ * INT ©Universidade do Algarve

175 Eliminar Recursividade à esquerda
Necessária na análise sintáctica preditiva Modifica o algoritmo de procura no espaço de produções Elimina recursividade directa infinita Contudo: gramática modificada é menos intuitiva Requer mais transformações para se atingir AST desejada ©Universidade do Algarve

176 Requer +transformações para se alcançar AST desejada
Árvore Sintáctica para: 2*3*4 Construir AST durante a derivação! Árvore Sintáctica Concreta AST desejada Term Term INT 2 Term’ INT 4 Term * Term’ * INT 3 INT 2 * INT 3 Term’ * INT 4 ©Universidade do Algarve

177 ©Universidade do Algarve
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 Algarve

178 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

179 Gramática Modificada Pedaço da gramática original Term  Term “*” INT
Pedaço da gramática modificada Term  INT Term’ Term’  “*” INT Term’ Term’  “/” INT Term’ Term’   ©Universidade do Algarve

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

181 ©Universidade do Algarve
Exemplo Produções para o símbolo não-terminal Term: Term  INT Term’ Procedimento para o símbolo não-terminal Term: Term() { if (token == INT) { token = NextToken(); return TermPrime(); } else return false; } 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 Algarve

182 ©Universidade do Algarve
Exemplo Procedimento para o símbolo não-terminal Term’: TermPrime() { if((token == ‘*’) || (token == ‘/’)) { token = NextToken(); if (token == INT) { return TermPrime(); } else return false; } else return true; } ©Universidade do Algarve

183 ©Universidade do Algarve
Exemplo Pseudo-código para a parte do programa responsável pela análise sintáctica: ... token = NextToken(); Term(); ©Universidade do Algarve

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

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

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

187 Construção da Árvore Sintáctica para o exemplo
Sem 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 error(); } TermPrime() { if ((token == ‘*’) || (token == ‘/’)) { first = token; next = NextToken(); if (next == INT) { return new TermPrimeNode(first, next, TermPrime()); } else return NULL; ©Universidade do Algarve

188 Árvore Sintáctica para 2*3*4
Concreta Term AST desejada INT 2 Term’ Term INT 4 Term Term’ * * INT 3 INT 2 * INT 3 Term’ * INT 4 ©Universidade do Algarve

189 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

190 Geração Directa da AST TermPrime constrói uma árvore incompleta
Falta leftmost child Retorna a raiz e o nó incompleto (root, incomplete) = TermPrime() Chamada com token: * Tokens que faltam: 3 * 4 root Term INT 4 incomplete Term * Falta parte esquerda que será construída pelo procedimento de chamada * INT 3 ©Universidade do Algarve

191 ©Universidade do Algarve
Código para Term Entrada Term() { if (token == INT) { leftmostInt = token; token = NextToken(); (root, incomplete) = TermPrime(); if (root == NULL) return leftmostInt; incomplete.leftChild = leftmostInt; return root; } else throw SyntaxError; } 2*3*4 INT 2 token ©Universidade do Algarve

192 ©Universidade do Algarve
Código para Term Entrada Term() { if (token == INT) { leftmostInt = token; token = NextToken(); (root, incomplete) = TermPrime(); if (root == NULL) return leftmostInt; incomplete.leftChild = leftmostInt; return root; } else throw SyntaxError; } 2*3*4 INT 2 token ©Universidade do Algarve

193 ©Universidade do Algarve
Código para Term Entrada Term() { if (token == INT) { leftmostInt = token; token = NextToken(); (root, incomplete) = TermPrime(); if (root == NULL) return leftmostInt; incomplete.leftChild = leftmostInt; return root; } else throw SyntaxError; } 2*3*4 INT 2 leftmostInt ©Universidade do Algarve

194 ©Universidade do Algarve
Código para Term Entrada Term() { if (token == INT) { leftmostInt = token; token = NextToken(); (root, incomplete) = TermPrime(); if (root == NULL) return leftmostInt; incomplete.leftChild = leftmostInt; return root; } else throw SyntaxError; } 2*3*4 root Term INT 4 incomplete Term * INT 2 * INT 3 leftmostInt ©Universidade do Algarve

195 ©Universidade do Algarve
Código para Term Entrada Term() { if (token == INT) { leftmostInt = token; token = NextToken(); (root, incomplete) = TermPrime(); if (root == NULL) return leftmostInt; incomplete.leftChild = leftmostInt; return root; } else throw SyntaxError; } 2*3*4 Term root INT 4 incomplete Term * INT 2 * INT 3 leftmostInt ©Universidade do Algarve

196 ©Universidade do Algarve
Código para Term Entrada Term() { if (token == INT) { leftmostInt = token; token = NextToken(); (root, incomplete) = TermPrime(); if (root == NULL) return leftmostInt; incomplete.leftChild = leftmostInt; return root; } else throw SyntaxError; } 2*3*4 Term root INT 4 Term incomplete * INT 2 * INT 3 leftmostInt ©Universidade do Algarve

197 Código para TermPrime Filho da esquerda a ser colocado pelo procedimento de chamada 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); ©Universidade do Algarve

198 ©Universidade do Algarve
Sumário Analisador sintáctico descendente (Top-Down Parser) Utilizar Lookahead para evitar Backtracking O parser é um conjunto de procedimentos mutuamente recursivos ©Universidade do Algarve

199 ©Universidade do Algarve
Terminologia Muitas técnicas de análise sintáctica diferentes Cada uma pode manusear algum conjunto de CFGs (gramáticas independentes do contexto) Categorização das técnicas ( ) ©Universidade do Algarve

200 ©Universidade do Algarve
Terminologia Muitas técnicas de análise sintáctica diferentes Cada 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 Algarve

201 ©Universidade do Algarve
Terminologia Muitas técnicas de análise sintáctica diferentes Cada uma pode manusear algum conjunto de CFGs (gramáticas independentes do contexto) Categorização das técnicas L – derivação pela esquerda R – derivação pela direita ( ) ©Universidade do Algarve

202 ©Universidade do Algarve
Terminologia Muitas técnicas de análise sintáctica diferentes Cada uma pode manusear algum conjunto de CFGs (gramáticas independentes do contexto) Categorização das técnicas Número de lookahead ( ) ©Universidade do Algarve

203 ©Universidade do Algarve
Terminologia Muitas técnicas de análise sintáctica diferentes Cada 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 Algarve

204 ©Universidade do Algarve
Terminologia LL(k) Descendente (top-down), preditiva Constrói derivação pela esquerda (leftmost) de cima para baixo LR(k) Ascendente (bottom-up), shift-reduce Constrói derivação pela direita (rightmost) de baixo para cima ©Universidade do Algarve

205 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

206 Analisador Sintáctico Ascendente
Mecanismo Central Autómato Pushdown, que implementa Parser Shift-reduce ©Universidade do Algarve

207 ©Universidade do Algarve
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 Algarve

208 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Pilha String de entrada num * ( num + num ) ©Universidade do Algarve

209 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * num * ( num + num ) ©Universidade do Algarve

210 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * SHIFT num * ( num + num ) ©Universidade do Algarve

211 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * num SHIFT * ( num + num ) ©Universidade do Algarve

212 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * num REDUCE * ( num + num ) ©Universidade do Algarve

213 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr REDUCE num * ( num + num ) ©Universidade do Algarve

214 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr SHIFT num * ( num + num ) ©Universidade do Algarve

215 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * * Expr SHIFT num ( num + num ) ©Universidade do Algarve

216 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Op Expr REDUCE num * ( num + num ) ©Universidade do Algarve

217 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Op Expr SHIFT num * ( num + num ) ©Universidade do Algarve

218 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * ( Op Expr SHIFT num * num + num ) ©Universidade do Algarve

219 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * ( Op Expr SHIFT num * num + num ) ©Universidade do Algarve

220 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * num ( Op Expr SHIFT num * + num ) ©Universidade do Algarve

221 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr ( Op Expr REDUCE SHIFT num * num + num ) ©Universidade do Algarve

222 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr ( Op Expr SHIFT num * num + num ) ©Universidade do Algarve

223 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * + Expr ( Op Expr SHIFT num * num num ) ©Universidade do Algarve

224 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Op Expr ( Op Expr REDUCE SHIFT num * num + num ) ©Universidade do Algarve

225 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Op Expr ( Op Expr SHIFT num * num + num ) ©Universidade do Algarve

226 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce num Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Op Expr ( Op Expr SHIFT num * num + ) ©Universidade do Algarve

227 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Op Expr ( Op Expr REDUCE SHIFT num * num + num ) ©Universidade do Algarve

228 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr Expr Expr ( Op Op Expr Expr REDUCE SHIFT num * num + num ) ©Universidade do Algarve

229 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr Expr ( Op Op Expr Expr SHIFT num * num + num ) ©Universidade do Algarve

230 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * ) Expr Expr ( Op Op Expr Expr SHIFT num * num + num ©Universidade do Algarve

231 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * ) Expr Expr Op Expr Expr ( Op Expr REDUCE num * num + num ©Universidade do Algarve

232 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * ) Expr Expr Op Expr Expr ( Expr Op Expr REDUCE num * num + num ©Universidade do Algarve

233 ©Universidade do Algarve
Exemplo: Parser Shift-Reduce Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * ) Expr Expr Op Expr Expr ( Expr Op Expr ACCEPT! num * num + num ©Universidade do Algarve

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

235 ©Universidade do Algarve
Conflitos Gramática Original Nova Gramática Expr  Expr Op Expr Expr  (Expr) Expr  - Expr Expr  num Op  + Op  - Op  * Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * ©Universidade do Algarve

236 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * num - num ©Universidade do Algarve

237 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * SHIFT num - num ©Universidade do Algarve

238 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * num SHIFT - num ©Universidade do Algarve

239 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * Expr REDUCE SHIFT num - num ©Universidade do Algarve

240 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * Expr SHIFT num - num ©Universidade do Algarve

241 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * - Expr SHIFT num num ©Universidade do Algarve

242 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * Opções: Reduce Shift - Expr num num ©Universidade do Algarve

243 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce - Expr REDUCE num num ©Universidade do Algarve

244 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Expr Expr SHIFT num - num ©Universidade do Algarve

245 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce num Expr Expr SHIFT num - ©Universidade do Algarve

246 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Expr Expr Expr REDUCE num - num ©Universidade do Algarve

247 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Expr Expr Expr FAILS! num - num ©Universidade do Algarve

248 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * Qualquer uma destas opções resulta: Reduce Shift - Expr num num ©Universidade do Algarve

249 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce - Expr num num ©Universidade do Algarve

250 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Op Expr REDUCE num - num ©Universidade do Algarve

251 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce num Op Expr SHIFT num - ©Universidade do Algarve

252 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Expr Op Expr REDUCE num - num ©Universidade do Algarve

253 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Expr Expr Op Expr REDUCE num - num ©Universidade do Algarve

254 ©Universidade do Algarve
Conflito shift/reduce/reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Reduce Expr Expr Op Expr ACCEPT num - num ©Universidade do Algarve

255 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Shift - Expr SHIFT num num ©Universidade do Algarve

256 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Shift num - Expr SHIFT num ©Universidade do Algarve

257 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Shift Expr - Expr REDUCE num num ©Universidade do Algarve

258 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Shift Expr Expr Expr REDUCE num - num ©Universidade do Algarve

259 ©Universidade do Algarve
Conflitos Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * O que acontece ao escolher-se: Shift Expr Expr Expr ACCEPT num - num ©Universidade do Algarve

260 ©Universidade do Algarve
Conflito Shift/Reduce/Reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * Este conflito Shift/Reduce reflecte ambiguidade na gramática - Expr num num ©Universidade do Algarve

261 ©Universidade do Algarve
Conflito Shift/Reduce/Reduce Expr  Expr Op Expr Expr  Expr - Expr Expr  (Expr) Expr  Expr - Expr  num Op  + Op  - Op  * Este conflito Shift/Reduce reflecte ambiguidade na gramática - Expr Eliminar alterando a gramática num num ©Universidade do Algarve

262 ©Universidade do Algarve
Conflito Shift/Reduce/Reduce 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 - Expr num num ©Universidade do Algarve

263 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

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

265 ©Universidade do Algarve
Estados do Parser Sequência de Tokens na entrada ($ para sinalizar o fim da entrada) Estado corrente do autómato de estados finitos Duas Pilhas Pilha de Estados (implementa autómato de estados finitos) Pilha de Símbolos (terminais da entrada e não-terminais das reduções) ©Universidade do Algarve

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

267 ©Universidade do Algarve
Tabela do Parser Implementa controlo dos estados finitos Em cada estado, ver Tabela[topo da pilha de estados][símbolo na entrada] Em seguida, realizar acção ©Universidade do Algarve

268 Exemplo de Tabela do Parser
Pilha de Estados Pilha de Símbolos Entrada Gramática S  X $ (1) X  (X) (2) X  ( ) (3) (()) s0 X ©Universidade do Algarve

269 ©Universidade do Algarve
Tabela do Parser 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 ©Universidade do Algarve

270 ©Universidade do Algarve
Tabela do Parser 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 ©Universidade do Algarve

271 ©Universidade do Algarve
Tabela do Parser Aceitar Parar a análise e reportar sucesso Erro Parar a análise e reportar erro ©Universidade do Algarve

272 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática (())$ S  X $ (1) X  (X) (2) X  ( ) (3) s0 ©Universidade do Algarve

273 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática (())$ S  X $ (1) X  (X) (2) X  ( ) (3) s0 ©Universidade do Algarve

274 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática ())$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 s0 ( ©Universidade do Algarve

275 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática ())$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 ( s0 ©Universidade do Algarve

276 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática ))$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 s2 ( s0 ( ©Universidade do Algarve

277 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática ))$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 s2 ( s0 ( ©Universidade do Algarve

278 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s5 s2 ) s2 ( s0 ( ©Universidade do Algarve

279 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s5 s2 ) s2 ( s0 ( ©Universidade do Algarve

280 ©Universidade do Algarve
Passo 1: pop das pilhas Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s5 s2 ) s2 ( s0 ( ©Universidade do Algarve

281 ©Universidade do Algarve
Passo 1: pop das pilhas Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 s0 ( ©Universidade do Algarve

282 ©Universidade do Algarve
Passo 2: push não-terminal Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 s0 ( ©Universidade do Algarve

283 ©Universidade do Algarve
Passo 2: push não-terminal Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 X s0 ( ©Universidade do Algarve

284 ©Universidade do Algarve
Passo 3: usar Goto, push novo estado Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s2 X s0 ( ©Universidade do Algarve

285 ©Universidade do Algarve
Passo 3: usar Goto, push novo estado Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s3 s2 X s0 ( ©Universidade do Algarve

286 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática )$ S  X $ (1) X  (X) (2) X  ( ) (3) s3 s2 X s0 ( ©Universidade do Algarve

287 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s4 s3 ) s2 X s0 ( ©Universidade do Algarve

288 ©Universidade do Algarve
Tabela do Parser em acção Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s4 s3 ) s2 X s0 ( ©Universidade do Algarve

289 ©Universidade do Algarve
Passo 1: pop pilhas Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s4 s3 ) s2 X s0 ( ©Universidade do Algarve

290 ©Universidade do Algarve
Passo 1: pop pilhas Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s0 ©Universidade do Algarve

291 ©Universidade do Algarve
Passo 2: push não-terminal Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s0 ©Universidade do Algarve

292 ©Universidade do Algarve
Passo 2: push não-terminal Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s0 X ©Universidade do Algarve

293 ©Universidade do Algarve
Passo 3: usar Goto, push novo estado Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s0 X ©Universidade do Algarve

294 ©Universidade do Algarve
Passo 3: usar Goto, push novo estado Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s1 s0 X ©Universidade do Algarve

295 ©Universidade do Algarve
Aceitar a String! Pilha de Estados Pilha de Símbolos Entrada Gramática $ S  X $ (1) X  (X) (2) X  ( ) (3) s1 s0 S ©Universidade do Algarve

296 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

297 ©Universidade do Algarve
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 Algarve

298 ©Universidade do Algarve
Exemplo Estados do DFA baseados nos itens Temos de capturar o que foi já percorrido numa produção X  ( X ) Gramática S  X $ (1) X  (X) (2) X  ( ) (3) Ou aqui? Ou aqui? Aqui? Ou aqui? ©Universidade do Algarve

299 ©Universidade do Algarve
Exemplo Estados do DFA baseados nos itens Temos de capturar o que foi já percorrido numa produção X  ( X ) Gramática S  X $ (1) X  (X) (2) X  ( ) (3) Produção X  (X) gera 4 itens: X  • (X ) X  ( • X ) X  (X • ) X  (X ) • ©Universidade do Algarve

300 ©Universidade do Algarve
Exemplo 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  ( ) • Gramática S  X $ (1) X  (X) (2) X  ( ) (3) ©Universidade do Algarve

301 ©Universidade do Algarve
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   • Reduzirá utilizando: A   Se um estado contém um item: S   • $ e a entrada está vazia O parser aceita a entrada ©Universidade do Algarve

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

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

304 ©Universidade do Algarve
Exemplos de Closure() Itens S  • X $ S  X • $ X  • (X) X  ( • X ) X  (X • ) X  (X) • X  • ( ) X  ( • ) X  ( ) • Closure({X ( • X )}) X  ( • X) X  • (X) X  • ( ) Closure({S  • X $}) S  • X $ X  • (X) X  • ( ) ©Universidade do Algarve

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

306 ©Universidade do Algarve
Exemplos de Goto() Itens S  • X $ S  X • $ X  • (X) X  ( • X ) X  (X • ) X  (X) • X  • ( ) X  ( • ) X  ( ) • Goto({X  ( • X)}, X) X  (X • ) Goto ({X  •(X)}, () X  ( • X) X  • (X) X  • ( ) ©Universidade do Algarve

307 ©Universidade do Algarve
Construir os estados do DFA 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 ©Universidade do Algarve

308 ©Universidade do Algarve
Construir o Parser Construir o DFA DFA para a gramática: Construir a tabela do parser utilizando o DFA s1 X S  X • $ s0 s2 S  • X$ X  • (X) X  • ( ) ( X  ( • X) X  ( • ) X  • (X) X  • ( ) ( s3 X  (X • ) X ) ) s5 s4 X  ( ) • X  (X) • ©Universidade do Algarve

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

310 ©Universidade do Algarve
Criação da tabela sintáctica s1 X S  X • $ s0 s2 S  • X$ X  • (X) X  • ( ) ( X  ( • X) X  ( • ) X  • (X) X  • ( ) ( s3 X  (X • ) X ) ) s5 s4 ©Universidade do Algarve X  ( ) • X  (X) •

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

312 Compiladores João M. P. Cardoso
Análise Sintáctica Compiladores João M. P. Cardoso ©Universidade do Algarve

313 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 (1i 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 Algarve

314 Definições: Regras para First()
1) TFirst(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 Algarve

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

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

317 ©Universidade do Algarve
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 ©Universidade do Algarve

318 ©Universidade do Algarve
Definições: Algoritmo para Follow() for all nonterminals NT Follow(NT) = {} Follow(S) = {$} while Follow sets keep changing for all productions A  B Follow(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 Algarve

319 Definições: Exemplo Follow()
Gramáticas exemplo: S  X $ X  a X  a b Follow(S) = { $ } Follow(X) = { $ } X  “(“ X “)” X   Follow(X) = { “)”, $ } ©Universidade do Algarve

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

321 Parser sem Lookahead: LR(0)
reduce(2) ou shift to s2 s0 X S  X • $ s2 S  • X $ X  • a X  • a b X  a b • s1 X  a • X  a • b b a ©Universidade do Algarve

322 ©Universidade do Algarve
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 Algarve

323 ©Universidade do Algarve
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 Algarve

324 ©Universidade do Algarve
Nova tabela sintáctica bFollow(X) aFollow(X) b nunca segue X nas derivações: resolver conflito shift/reduce com shift s3 X s0 S  X • $ s2 S  • X $ X  • a X  • a b X  a b • s1 X  a • X  a • b b a ©Universidade do Algarve

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

326 ©Universidade do Algarve
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 Algarve

327 ©Universidade do Algarve
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 Algarve

328 ©Universidade do Algarve
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 Algarve

329 ©Universidade do Algarve
Itens LR(1): exemplo Gramática S  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  • $ ] ©Universidade do Algarve

330 ©Universidade do Algarve
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 Algarve

331 ©Universidade do Algarve
Closure para LR(1) Closure(I) repeat for all items [A   • X  c] in I for any production X   for any d  First( c) I = I  { [X  •  d] } until I does not change return I ©Universidade do Algarve

332 ©Universidade do Algarve
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 Algarve

333 ©Universidade do Algarve
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 Algarve

334 ©Universidade do Algarve
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 Algarve

335 ©Universidade do Algarve
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 Algarve

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

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

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

339 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 A gramática não é LL(1) Gramática: Z  “d” Z  X Y Z Y   Y  “c” X  Y X  “a” Não-terminais Terminais “d” “c” “a” Z Z  X Y Z Z  “d” Y Y   Y  “c” X X  Y X  “a” ©Universidade do Algarve

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

341 Classificação de Gramáticas
Context free unambiguous LR(k) LR(1) LALR(1) SLR(1) LR(0) G0 G1 G2 G3 G4 G5 G6 G7 regular LL(0) LL(1) LL(k) ©Universidade do Algarve

342 ©Universidade do Algarve
Geradores de Parsers Geram C, Lex & Yacc flex e bison Geram Java: JLex e CUP JavaCC: Lista com mais geradores de parsers ©Universidade do Algarve

343 Análise Semântica e Representação Intermédia
Compiladores João M. P. Cardoso ©Universidade do Algarve

344 Localização actual nas etapas de compilação
Program (character stream) Lexical Analyzer (Scanner) Token Stream Syntax Analyzer (Parser) Parse Tree Semantic Analyzer Intermediate Code Generator Intermediate Representation + Symbol Table ©Universidade do Algarve

345 O que é a semântica de um programa?
Sintaxe Como o programa é constituído Representação textual ou estrutura Semântica Qual é o significado do programa? ©Universidade do Algarve

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

347 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); Não foi atribuído valor inicial a sum Variável não declarada Tipo da variável devolvida não confere com a declaração do cabeçalho da função Falta um argumento Inconsistência entre tipos no RHS e LHS ©Universidade do Algarve

348 Objectivo das representações intermédias do programa
Permitir análises e transformações Optimizações Estruturar tradução para código máquina Sequência de passos Intermediate Representation (IR) Análise Semântica Representação Intermédia de nível alto Representação Intermédia de nível baixo Árvore Sintáctica Código Máquina ©Universidade do Algarve

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

350 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 baixo Afectação de registos Selecção de instruções ©Universidade do Algarve

351 ©Universidade do Algarve
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 Algarve

352 ©Universidade do Algarve
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 Algarve

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

354 ©Universidade do Algarve
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 Algarve

355 ©Universidade do Algarve
Exemplo void add(int x, int[] v, int N) { int i; i = 0; while (i < N) { v[i] = v[i]+x; i = i+1; } Função add descritor para parâmetro x x descritor para parâmetro v v N descritor para parâmetro N descritor para variável local i i ©Universidade do Algarve

356 ©Universidade do Algarve
Exemplo void add(int x, int[] v, int N) { int i; i = 0; while (i < N) { v[i] = v[i]+x; i = i+1; } Função add Código da função descritor para parâmetro x x descritor para parâmetro v v N descritor para parâmetro N descritor para retorno descritor para variável local i i ©Universidade do Algarve

357 ©Universidade do Algarve
Exemplo void add(int x, int[] v, int N) { int i; i = 0; while (i < N) { v[i] = v[i]+x; i = i+1; } add descritor para parâmetro x x Código da função descritor para parâmetro v v N descritor para parâmetro N descritor para retorno descritor para variável local i i ©Universidade do Algarve

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

359 ©Universidade do Algarve
Lookup i num exemplo TS para as variáveis globais v descritor para a variável global v TS para os parâmetros da função x descritor para parâmetro x TS para as variáveis locais da função i descritor para a variável local i ©Universidade do Algarve

360 ©Universidade do Algarve
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 v descritor para a variável global v x descritor para parâmetro x i descritor para a variável local i ©Universidade do Algarve

361 Análise Semântica e Representação Intermédia
Compiladores João M. P. Cardoso ©Universidade do Algarve

362 ©Universidade do Algarve
Descritores O que contêm? Informação utilizada para geração de código e análise semântica Descritores locais - nome, tipo, offset na pilha Descritores 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 Algarve

363 Parâmetros, Local, e Descritores de Tipos
Parâmetros, Local referem a descritores de tipo Descritor 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 Algarve

364 Exemplo: Tabela de Símbolos para Tipos
int Descritor de int int [] Descritor de array boolean Descritor de boolean boolean [] Descritor de array Descritor de estrutura para vector vector [] Descritor de array ©Universidade do Algarve

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

366 Descritor de função para add
TS para parâmetros N Descritor de parâmetro x Descritor de parâmetro v Descritor de parâmetro Descritor de função para add TS de variáveis locais Descritor de variável local i Código para a função add ©Universidade do Algarve

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

368 Á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 Algarve

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

370 ©Universidade do Algarve
Tabela de Símbolos Dada uma árvore sintáctica (abstracta ou concreta) Atravessar recursivamente a árvore Construir a tabela de símbolos enquanto a travessia da árvore decorre ©Universidade do Algarve

371 ©Universidade do Algarve
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 Algarve

372 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 ... } 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 Algarve

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

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

375 ©Universidade do Algarve
Exemplo x e y são variáveis locais x*x + y*y + * * ldl ldl ldl ldl Descritor de local para x Na tabela de símbolos locais Descritor de local para x Na tabela de símbolos locais ©Universidade do Algarve

376 ©Universidade do Algarve
Exemplo v é 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 ldp ldl Descritor de parâmetro para x na tabela de símbolos de parâmetros da função add Descritor local para I na tabela de símbolos locais da função add Descritor de parâmetro para v na tabela de símbolos de parâmetros da função add ©Universidade do Algarve

377 Representação de enunciados de atribuição
Nós Store stl 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 Para stores em atributos de classes ou campos de estruturas... ©Universidade do Algarve

378 ©Universidade do Algarve
Exemplo sta v[i]=v[i]+x; + ldp ldl lda ldp ldp ldl Descritor do parâmetro para v na tabela de símbolos dos parâmetros da função add Descritor do parâmetro para x 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 ©Universidade do Algarve

379 ©Universidade do Algarve
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 Algarve

380 Análise Semântica e Representação Intermédia
Compiladores João M. P. Cardoso ©Universidade do Algarve

381 Representação do fluxo de controlo
Nós de enunciados Nó if Árvore de expressão para a condição Nó para o corpo do then e nó para o corpo do else Nó while Nó para o corpo Nó return Árvore de expressão para o valor/expressão de retorno ©Universidade do Algarve

382 Exemplo while (i < N) v[i] = v[i]+x; while sta < + ldp ldl ldl
lda ldp ldp ldl Descritor de parâmetros para x Descritor de parâmetros para v Descritor de locais para i ©Universidade do Algarve

383 ©Universidade do Algarve
Exemplo Notação abreviada while (i < N) v[i] = v[i]+x; while sta < + Ldp v Ldl i Ldl i Ldp v lda Ldp x Ldp v Ldl i ©Universidade do Algarve

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

385 ©Universidade do Algarve
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 Algarve

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

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

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

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

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

391 Verificação para a Instrução de Load para arrays
O que tem o compilador? Nome da variável Expressã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 Algarve

392 ©Universidade do Algarve
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 Algarve

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

394 ©Universidade do Algarve
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 Algarve

395 ©Universidade do Algarve
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 Algarve

396 ©Universidade do Algarve
Instrução Store O que tem o compilador? Nome da variável expressã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 Algarve

397 ©Universidade do Algarve
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 Algarve

398 ©Universidade do Algarve
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 programa Tipos dos parâmetros “casam” com tipos dos parâmetros da declaração da função ©Universidade do Algarve

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

400 Análise Semântica e Representação Intermédia
Compiladores João M. P. Cardoso ©Universidade do Algarve

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

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

403 ©Universidade do Algarve
Exemplo: CFG entry while (i < N) v[i] = v[i]+x; cbr < Laços entre Instruções e expressões ldl i ldp N Laços de fluxo de controlo sta exit + ldp v ldl i lda ldp x ©Universidade do Algarve ldp v ldl i

404 ©Universidade do Algarve
Exemplo: CFG entry if (x < y) { a = 0; } else { a = 1; } cbr < ldl x ldl y stl a 0 stl a 1 exit ©Universidade do Algarve

405 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 locais (alguns parâmetros) Stack Heap Arrays Código ©Universidade do Algarve

406 ©Universidade do Algarve
Exemplo: ld address offset MEM[address+offset] No caso de offset=4 relativo ao sp (MEM[$sp+offset]): x*x+y*y ld 4 sp + + * * * * ldl ldl ldl ldl ld 4 ld 4 ld 8 ld 8 sp sp sp sp Descritor local para y (8) Descritor local para x (4) ©Universidade do Algarve

407 ©Universidade do Algarve
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 Algarve

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

409 ©Universidade do Algarve
Exemplo: v[5]+x 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 + + ld 0 ldp 1 lda ldp + 5 ldp 2 ldp * Descritor de parâmetro de x (1) 5 4 Descritor de parâmetro v (2) ©Universidade do Algarve

410 ©Universidade do Algarve
Variáveis Locais Assumir que são alocadas na pilha de chamadas Endereç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 Algarve

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

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

413 ©Universidade do Algarve
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 Outras TS (variáveis globais, por exemplo) TS de parâmetros N Descritor do parâmetro N (2) v Descritor do parâmetro v (1) Descritor da função add TS de variáveis locais x Descritor do parâmetro x (0) i descritor da variável local i (0) Código para a função add ©Universidade do Algarve

414 ©Universidade do Algarve
Exemplo: v[i]+x + + ld 0 lda ldp 0 ldp + ldl ldp Descritor de parâmetro para x (0) ldp 1 * Descritor de parâmetro para v (1) 4 ld 0 Descritor de local para i (0) sp ©Universidade do Algarve

415 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 .... exit 4 ©Universidade do Algarve

416 ©Universidade do Algarve
Exemplo enter 4 st 0 sp cbr st 0 < + ld 0 + ldp 2 ld 0 ldp 0 sp ldp 1 * + 4 st 0 sp ld 0 + 1 ld 0 * ldp 1 sp 4 ld 0 exit 4 sp ©Universidade do Algarve

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

418 ©Universidade do Algarve
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 Algarve

419 Geração de Código Final
Compiladores João M. P. Cardoso ©Universidade do Algarve

420 ©Universidade do Algarve
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 Algarve

421 Geração de código final
Programa (cadeia de caracteres) Lexical Analyzer (Scanner) Cadeia de Tokens Syntax Analyzer (Parser) Syntax Analyzer (Parser) Árvore sintáctica Semantic Analyzer Semantic Analyzer Representação intermédia Code Optimizer Representação intermédia optimizada Code Generator Code Generator Código Assembly ©Universidade do Algarve

422 Geração de código final
Y2=a*x*x+b*x+c; stl y2 IR de alto nível + + ldl c * * * Ldl a Ldl x Ldl b Ldl x Ldl x ©Universidade do Algarve

423 Geração de código final
Y2=a*x*x+b*x+c; Variáveis: A B X C y2 stl y2 IR de alto nível + + ldl c * * * Ldl a Ldl x Ldl b Ldl x Ldl x ©Universidade do Algarve

424 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 st 16 IR de baixo nível sp + + ld 12 * sp * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

425 Geração de código final
Y2=a*x*x+b*x+c; Começar nas folhas: lw $t0, 4($sp) st 16 IR de baixo nível sp + + ld 12 * sp * * Ld 0 <$t0> Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

426 Geração de código final
Y2=a*x*x+b*x+c; lw $t0, 4($sp) lw $t1, 8($sp) st 16 IR de baixo nível sp + + ld 12 * sp * * Ld 0 <$t0> <$t1> Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

427 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 st 16 IR de baixo nível sp + + ld 12 * sp <$t2> * * Ld 0 <$t0> <$t1> Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

428 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) st 16 IR de baixo nível sp + + ld 12 * sp <$t2> * * <$t3> Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

429 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) st 16 IR de baixo nível sp + + ld 12 * sp <$t2> * * <$t4> <$t3> Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

430 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 st 16 IR de baixo nível sp + + ld 12 sp <$t5> * <$t2> * * <$t4> <$t3> Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

431 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) st 16 IR de baixo nível sp + + ld 12 <$t5> * sp <$t2> <$t6> * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

432 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 st 16 IR de baixo nível sp + + ld 12 <$t7> sp <$t5> * <$t2> <$t6> * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

433 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 Add $t8, $t7, $t2 st 16 IR de baixo nível sp + <$t8> + ld 12 <$t7> * sp <$t2> * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

434 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 Add $t8, $t7, $t2 lw $t9, 12($sp) st 16 IR de baixo nível sp + <$t9> <$t8> + ld 12 * sp * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

435 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 Add $t8, $t7, $t2 lw $t9, 12($sp) Add $t10, $t8, $t9 st 16 IR de baixo nível <$t10> sp + <$t9> <$t8> + ld 12 * sp * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

436 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 Add $t8, $t7, $t2 lw $t9, 12($sp) Add $t10, $t8, $t9 Sw $t10, 16($sp) st 16 IR de baixo nível <$t10> sp + + ld 12 * sp * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

437 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 add $t8, $t7, $t2 lw $t9, 12($sp) add $t10, $t8, $t9 sw $t10, 16($sp) st 16 IR de baixo nível 11 registos $t para armazenar valores intermédios! (MIPS tem 10 registos $t) sp + + ld 12 * sp * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

438 Geração de código final
Y2=a*x*x+b*x+c; Solução utilizando menos registos para armazenar resultados intermédios? st 16 IR de baixo nível sp + + ld 12 * sp * * Ld 0 Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

439 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 st 16 IR de baixo nível sp + + ld 12 * sp <$t?> * * Ld 0 <$t0> <$t1> Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

440 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 st 16 IR de baixo nível sp + + ld 12 * sp <$t0> * * Ld 0 <$t0> <$t1> Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

441 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 lw $t2, 8($sp) mult $t1, $t1, $t2 lw $t2, 0($sp) add $t0, $t1, $t0 lw $t1, 12($sp) add $t0, $t0, $t1 sw $t0, 16($sp) st 16 <$t0> IR de baixo nível 3 registos $t para armazenar valores intermédios sp + <$t1> <$t0> + ld 12 <$t1> <$t0> * sp <$t1> <$t2> * * <$t2> Ld 0 <$t0> <$t1> <$t1> Ld 8 Ld 4 Ld 8 Ld 8 sp sp sp sp sp ©Universidade do Algarve

442 ©Universidade do Algarve
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 Algarve

443 ©Universidade do Algarve
Geração de código Utilização de esqueletos (templates) para geração de código assembly para Estruturas If-then ou if-then-else Loops ©Universidade do Algarve

444 ©Universidade do Algarve
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 Algarve

445 ©Universidade do Algarve
Geração de código lab_cont: <do the test> boper …, lab_body j lab_end lab_body: <body> j lab_cont lab_end: while (test) body Esqueleto optimizado: lab_cont: <do the test> boper …, lab_end <body> j lab_cont lab_end: ©Universidade do Algarve

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

447 Linhas mestras para o gerador de código
Começar com geração simples, mesmo que naïf Ok 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 Algarve

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

449 Compiladores João M. P. Cardoso
Optimizações Compiladores João M. P. Cardoso ©Universidade do Algarve

450 ©Universidade do Algarve
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 Algarve

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

452 Tempo de Vida de Variáveis
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) $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 ©Universidade do Algarve

453 Tempo de Vida de Variáveis
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) $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 ©Universidade do Algarve 3 registos

454 ©Universidade do Algarve
Afectação de registos a variáveis 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) $t10 $t7 $t9 $t3 $t8 $t5 $t6 $t4 $t1 $t0 $t2 ©Universidade do Algarve

455 ©Universidade do Algarve
Afectação de registos a variáveis 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) $t10 $t7 $t9 $t3 $t8 $t5 $t6 $t4 $t1 $t0 $t2 ©Universidade do Algarve 4 registos

456 ©Universidade do Algarve
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 Algarve

457 ©Universidade do Algarve
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 Algarve

458 ©Universidade do Algarve
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 Algarve

459 ©Universidade do Algarve
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 Algarve

460 ©Universidade do Algarve
Afectação de registos a variáveis Tempo de vida das variáveis 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) $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 ©Universidade do Algarve

461 ©Universidade do Algarve
Afectação de registos a variáveis Grafo de interferências 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) $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t0 $t1 $t7 $t5 $t2 $t3 $t4 $t8 $t6 $t9 $t10 ©Universidade do Algarve

462 ©Universidade do Algarve
Afectação de registos a variáveis v0 v1 Grafo de interferências Interferência (laço) entre duas variáveis (nós) indica que não podem ser armazenadas no mesmo registo v6 v5 v7 v2 v3 v8 v4 v9 v10 ©Universidade do Algarve

463 ©Universidade do Algarve
Afectação de registos a variáveis v0 v1 Grafo de interferências Depois de colorido: Número de cores indica o número de registos necessário v6 v5 v7 v2 v3 v8 v4 v9 v10 ©Universidade do Algarve

464 ©Universidade do Algarve
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 Algarve

465 ©Universidade do Algarve
Afectação de registos a variáveis Na presença de estruturas condicionais Problema mais complicado Resolução similar ©Universidade do Algarve

466 ©Universidade do Algarve
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 Algarve

467 ©Universidade do Algarve
Fim! Disponível para orientar alunos interessados em realizar projectos sobre estes temas No ensino de certos tópicos: utilizando, por exemplo, Java Applets Na criação de infra-estruturas para apoio à leccionação Em tópicos avançados relacionados com investigação ©Universidade do Algarve


Carregar ppt "Introdução da Disciplina"

Apresentações semelhantes


Anúncios Google