Introdução da Disciplina

Slides:



Advertisements
Apresentações semelhantes
Estruturação de Linguagens (Pascal e C++)
Advertisements

ESTRUTURA DE DADOS Professor: Marcelo Mendes Turma: MBI-1
Parte 1: Organização de Computadores
PIPELINE (continuação).
1.3.2 – Linguagem Assembly Um programa em linguagem de máquina sofre de total falta de clareza O programa é uma sequência de linhas numeradas Cada linha.
CPU: Controle e processamento
Arquitetura de Computadores
WebDesign Redes de Computadores Aula 05
Introdução à Ciência da Computação Linguagens de Programação.
Compiladores Claudio Benossi
II – Análise léxica DEI Papel do analisador léxico.
I - Noções dum compilador Partes dum compilador Ferramentas de construção de compiladores Bibliografia aconselhada: –Aho, Sethi e Ullman – Cap. 1 LFA 1999/2000.
VI – Optimização de código Blocos básicos Optimização de ciclos Bibliografia aconselhada: –Apontamentos LFA 1999/ Jorge Morais.
Linguagens Formais e Autómatos
Compiladores I Cristiano Damiani Vasconcellos
03/08/2011 Professor Leomir J. Borba- –
Paradigmas de Programação
1 Arquitectura de Computadores II Ano lectivo 2008/09
Exemplo de arquitetura registrador-registrador- MIPS
MIPS PIPELINE.
Processador Fluxo de Dados e Controle
Aula-02 Arquitetura do conjunto de instruções – ISA (Instruction Set Architecture)
Introdução à Compilação Prof. Leandro Magno Slides adaptados a partir do material cedido pelos professores Heloise Manica Paris Teixeira, Yandre M. G.
Teoria dos Compiladores
Programação Básica em Java
1 Tipos definidos O programador pode definir seus próprios tipos de dados tipos complexos usados da mesma forma que os simples declaram-se variáveis utilizando-se.
Árvores.
Rganização de Computadores Melhorias de Desempenho com Pipelines - Parte B - Capítulo 6 – Patterson & Hennessy Organização de Computadores Melhorias de.
Rganização de Computadores Melhorias de Desempenho com Pipelines Capítulo 6 – Patterson & Hennessy Organização de Computadores Melhorias de Desempenho.
Curso Sistemas de Informação Disciplina: Arquitetura de Software
UNIDADE DE CONTROLE MICROPROGRAMADA PARA O FEMTOJAVA
Arquitetura de Computadores I
Linguagens Formais e Tradutores
Arquitetura de Computadores
Aula 4 Nomes, Vinculações, Tipos e Escopos
Linguagens de Programação
Autor: Fernando de Mesentier Silva
Escola Secundária c/ 3º CEB da Batalha
Arquitecturas RISC Arquitectura e Conjunto de Instruções
Arquitecturas RISC Programação em assembly
Listas Encadeadas.
Revisão da Linguagem C.
Classes e objetos P. O. O. Prof. Grace.
Instruções: A Linguagem de Máquina – Aula 01
Alocação de Registos (exercício) Compiladores João M. P. Cardoso.
Análise Semântica e Representação Intermédia
PROGRAMAÇÃO I UNIDADE 1.
Object Oriented Software Construction (MEYER, Bertrand)
Funcionamento básico de um computador
Do alto-nível ao assembly
Aula 3©Universidade do Algarve 1 Do alto-nível ao assembly Compiladores, Aula Nº 3 João M. P. Cardoso.
Classes de Arquiteturas Tipos de operação Codificação das operações
Linguagens de programação
Organização do Ambiente de Execução (Runtime System)
DESIGN E COMUNICAÇÃO MULTIMÉDIA 2011 / 2012 DESIGN E COMUNICAÇÃO MULTIMÉDIA 2011 / 2012 FORMADOR Gonçalo Miguel MÓDULO 25. Introdução ao ActionScript 3.0.
Organização e Arquitetura de Computadores
Agenda - Aula 2 Introdução (Computador Digital) Processadores
Java Bytecode Software Básico Mitsuo Takaki.
Geração de Código Intermediário
FORMATANDO O TRABALHO NO WORD 2007
Laboratório de Programação I Carlos Oberdan Rolim Ciência da Computação Sistemas de Informação.
Lógica para Computação Prof. Celso Antônio Alves Kaestner, Dr. Eng. celsokaestner (at) utfpr (dot) edu (dot) br.
Prof. Fábio Carneiro Mokarzel 2014
A Linguagem de Máquina – Desvios e Laços
A Linguagem de Máquina – Funções e recursividade Prof. André Luis M. Silva orgearq wordpress.com.
Arquitetura de Computadores 2009 Infra-estrutura Hardware
Análise Semântica e Representação Intermédia
A linguagem de Máquina – Instruções Básicas
COMPILADORES 07 Prof. Marcos.
Programação em assembly
Transcrição da apresentação:

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

©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

©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

©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

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

©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

©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

©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

©Universidade do Algarve Docentes João M. P. Cardoso jmcardo@ualg.pt Gab. 2.65 ©Universidade do Algarve

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

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

©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

©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, 1986. [existe uma tradução brasileira] Dick Grune, Henri E. Bal, Ceriel J. H. Jacobs, and Koen G. Langendoen, “Modern Compiler Design,” John Wiley & Sons, Ltd, 2000. [existe uma tradução brasileira] Rui Gustavo Crespo. Processadores de Linguagens. IST Press. 2001. http://istpress.ist.utl.pt/lprocess.html Com tópicos mais avançados: Steven S. Muchnick. Advanced Compiler Design and Implementation, Morgan Kaufmann Publishers, 1997. ©Universidade do Algarve

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: http://w3.ualg.pt/~jmcardo/ensino/compiladores Lista de correio electrónico https://imap.ualg.pt/wws/info/fct-compiladores Para subscrever: fct-compiladores@ualg.pt ©Universidade do Algarve

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

©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

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

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

©Universidade do Algarve Variáveis Globais .data 0x10000000 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 0x10000008 0x10000004 0x10000000 ©Universidade do Algarve

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

©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

©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

©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

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

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

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

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

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

©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

©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

©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

©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

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

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

©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

©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

©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

©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

©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

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

©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

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

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

©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

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

©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

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

©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

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

©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

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

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

©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

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

©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

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

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

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

©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

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

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

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

©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

©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

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

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

©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

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

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

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

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

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

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

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

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

©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

©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

©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

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

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

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

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

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

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

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

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

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

©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

©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

©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

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

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

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

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

©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

©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

©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

©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

©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

©Universidade do Algarve Implementação do DFA Estado Actual Próximo Estado “0” “1” “.” 1 3 2 4 6 5 int Edge[NumStates][256] = { /*... 0 1 2 ... 9 ... “.” ... */ /* estado 0 */ {..., 0, 0, 0, ..., 0, ..., 0, ...}, /* estado 1 */ {..., 3, 2, 0, ..., 0, ..., 4, ...}, /* estado 2 */ {..., 3, 2, 0, ..., 0, ..., 4, ...}, /* estado 3 */ {..., 3, 2, 0, ..., 0, ..., 4, ...}, /* estado 4 */ {..., 6, 5, 0, ..., 0, ..., 0, ...}, /* estado 5 */ {..., 6, 5, 0, ..., 0, ..., 0, ...}, /* estado 6 */ {..., 6, 5, 0, ..., 0, ..., 0, ...} } Exemplo: Edge[3][(int) ‘.’]  4 ©Universidade do Algarve

©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 0 1 2 3 4 5 6 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

©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

Optimizações na implementação do DFA Representação da tabela de transições por um array bidimensional pode não ser eficiente em termos de espaço de memória Mapeamento indirecto: int Edge[NumStates][4] = { /* estado 0 */ {0, 0, 0, 0}, /* estado 1 */ {3, 2, 4, 0}, /* estado 2 */ {3, 2, 4, 0}, /* estado 3 */ {3, 2, 4, 0}, /* estado 4 */ {6, 5, 0, 0}, /* estado 5 */ {6, 5, 0, 0}, /* estado 6 */ {6, 5, 0, 0} } /*... “0” “1” “2” “3” ... 9 ... “.” ... */ Int map[256] = { ..., 0, 1, 3, 3,... 3, ... 2, ...} Exemplo: Edge[3][(int) ‘.’] é feito assim: Edge[3][map[(int) ‘.’]] ©Universidade do Algarve

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

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

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

©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

©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

©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

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

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

©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 ( 2 - 1 ) + 1 ©Universidade do Algarve

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

©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

Á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

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

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

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

Á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

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

©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

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

©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

©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

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

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

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

©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

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

©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

©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

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

©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

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

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

©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

©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

©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

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

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

©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

©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

©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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

©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

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

©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

©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

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

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

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

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

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 *

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

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

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 

©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

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

©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

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

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

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

©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

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

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

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

©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

©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

©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

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

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

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

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

Á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

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

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

©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

©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

©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

©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

©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

©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

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

©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

©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

©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

©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

©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

©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

©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

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

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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

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

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

©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

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

©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

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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

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

©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

©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

©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

©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

©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

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

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

©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

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

©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

©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

©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

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

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

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

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

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

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

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

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

©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

©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

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

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

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

©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

©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

©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

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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

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

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

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

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

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

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

©Universidade do Algarve Geradores de Parsers Geram C, http://dinosaur.compilertools.net/ Lex & Yacc flex e bison Geram Java: JLex e CUP http://www.cs.princeton.edu/~appel/modern/java/JLex/ http://www.cs.princeton.edu/~appel/modern/java/CUP/ JavaCC: http://www.experimentalstuff.com/Technologies/JavaCC/index.html Lista com mais geradores de parsers http://catalog.compilertools.net/lexparse.html http://catalog.compilertools.net/java.html ©Universidade do Algarve

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

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

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

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

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

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

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

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

©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

©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

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

©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

©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

©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

©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

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

©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

©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

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

©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

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

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

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

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

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

Á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

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

©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

©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

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

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

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

©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

©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

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

©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

©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

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

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

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

©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

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

©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

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

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

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

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

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

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

©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

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

©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

©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

©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

©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

©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

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

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

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

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

©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

©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

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

©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

©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

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

©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

©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

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

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

©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

©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

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

©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

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

©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

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

©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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

©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

©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

©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

©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

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

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

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

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

©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

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

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

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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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

©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