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

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

Revisão Compiladores – AP1

Apresentações semelhantes


Apresentação em tema: "Revisão Compiladores – AP1"— Transcrição da apresentação:

1 Revisão Compiladores – AP1
Prof. Alexandre Monteiro Baseado em material cedido pelo Prof. Euclides Arcoverde Recife

2 Contatos Prof. Guilherme Alexandre Monteiro Reinaldo
Apelido: Alexandre Cordel /gtalk: Site: Celular: (81)

3 Objetivo Como criar uma linguagem computacional?
O principal objetivo é responder à seguinte pergunta: Como criar uma linguagem computacional? Para responder a pergunta, estudaremos: Como especificar uma linguagem Como construir um compilador para ela

4 História das Linguagens

5 História das Linguagens
Começaram a surgir outras linguagens mais elaboradas Fortran (1957) LISP (1959) COBOL (1960) BASIC (1964) C (1972) etc.

6 Linguagens de Alto Nível

7 Linguagens de Alto Nível
O nome “alto nível” tem o sentido de “alto nível de abstração”, pois essas linguagens abstraem detalhes operacionais pouco relevantes Assim, o programador pode (tenta) focar só no algoritmo Veremos agora dois exemplos para comparar as linguagens de nível alto e baixo

8 Exemplo em Baixo Nível Programa Hello World em assembly x86 DOSSEG
.MODEL SMALL .DATA Msg db "Hello World.",13,10,"$" .CODE Start: mov mov DS, AX lea DX, Msg mov AH, 9 int 21h mov ah, 4ch int 21h END Start

9 Exemplo em Alto Nível Programa Hello World em C
#include <stdio.h> int main() { printf(“Hello World”); return 0; }

10 Linguagens de Baixo Nível
Características gerais Dependentes de arquitetura Oferecem instruções primitivas simples (operações sobre o hardware) Programas extensos e pouco legíveis Visam oferecer mais controle sobre o hardware

11 Linguagens de Alto Nível
Características gerais Especificadas independentemente de qualquer arquitetura Oferecem comandos mais intuitivos Dizem mais “o que” deve ser feito do que “como” deve ser feito Mais fácil de programar e de ler códigos prontos Restringem o uso do hardware para evitar bugs Controle da alocação de memória em Java

12 Introdução a Compiladores

13 Compilação x Interpretação
código fonte compilação código de máquina execução resultados código fonte interpretação resultados

14 O que é um Compilador? Um compilador é um programa que lê um programa escrito em uma linguagem (linguagem fonte) e a traduz em um programa equivalente em outra linguagem (linguagem alvo). Aho, Sethi, Ullman. Compilador Programa Fonte Programa Objeto

15 O que é um Compilador? Nesse processo de tradução, há duas tarefas básicas a serem executadas por um compilador: análise, em que o texto de entrada (na linguagem fonte) é examinado, verificado e compreendido síntese, ou geração de código, em que o texto de saída (na linguagem objeto) é gerado, de forma a corresponder ao texto de entrada

16 O que é um Compilador? A fase de análise normalmente se subdivide em análise léxica, análise sintática e análise semântica É possível representar completamente a sintaxe de uma LP através de uma gramática sensível ao contexto Mas como não existem algoritmos práticos para tratar essas gramáticas, a preferência recai em usar gramáticas livres de contexto Deixa-se para a análise semântica a verificação de todos os aspectos da linguagens que não se consegue exprimir de forma simples usando gramáticas livres de contexto

17 O que é um Compilador? A implementação de reconhecedores de linguagens regulares (autômatos finitos) é mais simples e mais eficiente do que a implementação de reconhecedores de linguagens livres de contexto (autômatos de pilha) Nesse caso, é possível usar expressões regulares para descrever a estrutura de componentes básicos das LP, tais como identificadores, palavras reservadas, literais numéricos, operadores e delimitadores, etc. Essa parte da tarefa de análise (análise léxica) é implementada separadamente, pela simulação de autômatos finitos

18 O que é um Compilador? Um dos modelos possíveis para a construção de compiladores faz a separação total entre o front-end, encarregado da fase de análise, e o back-end, encarregado da geração de código, de forma que: O front-end e back-end se comunicam apenas através de uma representação intermediária O front-end depende exclusivamente da linguagem fonte O back-end depende exclusivamente da linguagem objeto

19 Etapas da Compilação Front-End (Análise) Back-End (Síntese)
Análise Léxica Análise Sintática Analise Semântica Geração de Código Intermediário Final Front-End (Análise) Back-End (Síntese)

20 O que é um Compilador? Um compilador típico consiste de algumas fases onde cada uma passa sua saída para as fases seguintes As principais fases são: análise léxica (ou scanner) análise sintática (ou parser) análise semântica otimização gerador de código otimização (novamente!)

21 Analisador Sintático e
Fases da Compilação Programa Fonte Analisador Léxico Analisador Sintático e Semântico Tabela de Símbolos Manipulador de erros Gerador de Código Intermediário Otimizador de Código Gerador de código Programa Objeto

22 Análise Léxica Também chamada de scanner
Agrupa caracteres em símbolos (ou tokens) Token: <nome-token, valor-atributo> Entrada: fluxo de caracteres Saída: fluxo de símbolos Símbolos são: Palavras reservadas, identificadores de variáveis e procedimentos, operadores, pontuação, etc. Expressões regulares usadas para reconhecimento Scanner é implementado como uma MEF (Método dos Elementos Finitos) Lex/Flex (J) são ferramentas para gerar scanners

23 position = initial + rate * 60
Análise Léxica Por exemplo, os caracteres na instrução de atribuição position = initial + rate * 60 seriam agrupados nos seguintes tokens: O identificador position O símbolo de atribuição = O identificador initial O símbolo de adição + O identificador rate O símbolo de multiplicação * O número 60 <id, 1> <=> <id, 2> <+> <id, 3> <*> <60>

24 Análise Sintática Também chamada de parser
Agrupa símbolos em unidades sintáticas Ex.: os 3 símbolos A+B podem ser agrupados em uma estrutura chamada de expressão Expressões depois podem ser agrupados para formar comandos ou outras unidades Saída: representação de árvore sintática do programa Gramática livre de contexto é usada para definir a estrutura do programa reconhecida por um parser Yacc/Bison (J) são ferramentas para gerar parsers

25 Análise Sintática Regras sintáticas (1)
Qualquer identificador é uma expressão Qualquer número é uma expressão Se expressão1 e expressão2 são expressões, então também são expressões expressão1 + expressão2 expressão1 * expressão2 ( expressão1 )

26 Análise Sintática Regras sintáticas (2)
Se identificador1 é um identificador e expressão2 é uma expressão, então identificador1 = expressão2 é um comando (statement) Se expressão1 é uma expressão e statement2 é um comando (statement), então while ( expressão1 ) { statement2 } if ( expressão1 ) { statement2 } são comandos (statements)

27 position = initial + rate * 60
Análise Sintática position = initial + rate * 60 comando de atribuição identificador = expressão position expressão + expressão identificador expressão * expressão initial identificador número rate 60

28 Gerador de Código Intermediário
Usa as estruturas produzidas pelo analisador sintático e verificadas pelo analisador semântico para criar uma sequência de instruções simples (código intermediário) Está entre a linguagem de alto nível e a linguagem de baixo nível

29 Gerador de Código Intermediário
Considere que temos um único registrador acumulador Considere o comando de atribuição x := a + b * c pode ser traduzido em: t1 := b * c t2 := a + t1 x := t2 Pode-se fazer um gerador de código relativamente simples usando regras como:

30 Gerador de Código Intermediário

31 Gerador de Código Intermediário
O comando de atribuição x := a + b * c Gera o código Load b { t1 := b * c } Mult c Store t1 Load a { t2 := a + t1 } Add t1 Store t2 Load t2 { x := t2 } Store x

32 Otimizador de Código Independente da máquina
Melhora o código intermediário de modo que o programa objeto seja menor (ocupe menos espaço de memória) e/ou mais rápido (tenha tempo de execução menor) A saída do otimizador de código é um novo código intermediário

33 Otimizador de Código Otimizador por sub-expressões comuns

34 Gerador de Código Produz o código objeto final
Toma decisões com relação à: Alocação de espaço para os dados do programa; Seleção da forma de acessá-los Definição de quais registradores serão usados, etc. Projetar um gerador de código que produza programas objeto eficientes é uma das tarefas mais difíceis no projeto de um compilador

35 Gerador de Código Várias considerações têm que ser feitas:
Há vários tipos de instruções correspondendo a vários tipos de dados e a vários modos de endereçamento Há instruções de soma específicas, por exemplo para incrementar/decrementar de 1 Algumas somas não foram especificadas explicitamente Cálculo de endereço de posições em vetores Incremento/decremento registrador de topo pilha Local onde armazenar variáveis Alocação de registradores

36 Gerador de Código temp1 = id3 * 60.0 id1 = id2 + temp1
MOVF id3, R2 MULF #60.0, R2 MOVF id2, R1 ADDF R2, R1 MOVF R1, id1

37 Tabela de Símbolos É uma estrutura de dados usada para guardar informações a respeito de todos os nomes usados pelo programa e registrar informações importantes associadas a cada um, tais como seu tipo (inteiro, real, etc.), tamanho, escopo, etc.

38 Manipulador de Erros É ativado sempre que for detectado um erro no programa fonte Deve avisar o programador da ocorrência do erro emitindo uma mensagem, e ajustar-se novamente à informação sendo passada de fase a fase de modo a poder completar o processo de compilação Mesmo que não seja mais possível gerar código objeto, a análise léxica e sintática deve prosseguir até o fim

39 Bibliografia AHO, A., LAM, M. S., SETHI, R., ULLMAN, J. D., Compiladores: princípios, técnicas e ferramentas. Ed. Addison Wesley. 2a Edição, 2008 (Capítulo 1)

40 Adiantando... Lexema: sequência de caracteres com significado interligado Token: classificação dada ao lexema Geralmente retornado junto com o próprio lexema ou outro atributo, como um ponteiro Padrão: é uma descrição da forma que os lexemas de um token podem tomar. Ex. sequência de caracteres que formam palavra-chave como um token.

41 Exemplos

42 Especificando Tokens Geralmente são especificados com expressões regulares Cada token é associado a uma expressão regular que representa seus lexemas válidos Padrão que representa várias palavras (dizemos que as palavras “casam” com o padrão)

43 Especificando Tokens Expressões Regulares
Formalismo utilizado para definir o conjunto de aceitação de uma linguagem Principais operadores utilizados pelas ERs Expressão Reconhece ε A cadeia de caractedes vazia “” “str” A string “str” A | B Todas as cadeias reconhecidas por A ou B A . B Cadeias formadas pela concatenação das cadeias reconhecidas por A e B A+ Reconhece cadeias formadas pela concatenação de um número finito de cadeias reconhecidas por A

44 Especificando Tokens Operadores derivados Expressão Equivale a ( A )
Agrupamento de operadores A* A+ | ε A? A | ε [A-Z] A | B | ... | Y | Z A{N} A . A . A ... } N vezes A{M,N} A . A . A ... } entre M e N vezes AB A . B abc String “abc”

45 Especificando Tokens Defina expressões para expressar: Exercícios
Número IP: \d{3}.\d{3} .\d{3} .\d{3} Números naturais (e inteiros): \d{n} ou [0-9]{n} Números de telefone (com DDD opcional): \([0-9]{2}\).[0-9]{4}. [0-9]{4} Horas: [012]\d:[0-5]\d s: Placa de Carro: [A-Z]{3}-\d{4} CEP: \d{5}-\d{3} ou \d\d\d\d\d URLs: - Com http - (http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)? - Sem http - ([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

46 Análise Sintática Inclui a análise léxica como subfase
Alguns autores consideram que a análise sintática envolve tudo que diz respeito à verificação do formato do código fonte Inclui a análise léxica como subfase Visão mais coerente, porém o livro texto que usamos tem outra visão...

47 Análise Sintática Entenderemos análise sintática como sendo apenas a segunda fase dessa verificação de formato: “É a fase que analisa os tokens para descobrir a estrutura gramatical do código fonte” Também chamada “Reconhecimento” (Parsing) Porém, um nome melhor seria “Análise Gramatical”

48 Gramáticas São usadas para organizar os tokens em “frases” de sentido lógico Definem regras de formação recursivas expressão → CTE_INT | ID | expressão + expressão

49 Gramáticas Símbolos terminais Símbolos não-terminais Símbolo inicial
As gramáticas livres de contexto possuem quatro elementos: Símbolos terminais Símbolos não-terminais Símbolo inicial Produções! ou Regras de Produção!

50 Gramáticas Símbolos terminais: Símbolos não-terminais:
Elementos das gramáticas livres de contexto: Símbolos terminais: Símbolos assumidos como atômicos, indivisíveis Em compiladores, são os tokens (int, +, -, /, identificador, valores literais, etc) Símbolos não-terminais: Usados para organizar os tokens em “frases” Representam elementos abstratos do programa (expressões, termos, etc)

51 Gramáticas Elementos das gramáticas livres de contexto:
Símbolo inicial: Não-terminal a partir do qual são derivadas “cadeias” de símbolos terminais Geralmente é um não-terminal chamado programa Produções: São as regras de formação Definem as “frases” de terminais válidas na linguagem

52 Derivação Seja a gramática BNF anterior Derivar a cadeia “i+i+x”
Consideremos que esta é uma derivação mais à esquerda <expressao> <termo> + <expressao> i <expressao> i <termo> + <expressao> i i <expressao> i i <termo> i i x

53 Derivação Agora a Derivação mais à direita da cadeia “i+i+x”
Seja a gramática BNF mostrada antes Agora a Derivação mais à direita da cadeia “i+i+x” <expressao> <termo> + <expressao> <termo> + <termo> + <expressao> <termo> + <termo> + <termo> <termo> + <termo> + x <termo> + i x i i x

54 Derivação O processo de derivação consiste em substituir cada ocorrência de um não-terminal pelo lado direito (corpo) de alguma de suas produções Derivação mais à esquerda: substituir sempre o não-terminal mais à esquerda Derivação mais à direita: análogo A derivação pára quando sobrarem apenas terminais e o resultado é a cadeia

55 Árvore de Derivação A árvore dos exemplos anteriores expressao termo +
a cadeia gerada pode ser percebida nas folhas, analisando-as da esquerda para a direita x

56 a seqüência de terminais (cadeia) gerada
Árvore de Derivação A mesma árvore reorganizada expressao termo expressao termo expressao termo i + i + x a seqüência de terminais (cadeia) gerada

57 Gramáticas Ambíguas Uma gramática é dita ambígua se ela puder gerar duas árvores de derivação distintas para uma mesma cadeia Veremos uma gramática equivalente à anterior, porém ambígua...

58 Gramáticas Ambíguas Mostrar duas árvores de derivação para “i+i+x”
Exemplo Mostrar duas árvores de derivação para “i+i+x” <expressao> ::= <expressao> "+" <expressao> | "x" | "i"

59 Gramáticas Ambíguas Dificultam a construção do analisador sintático
Gramáticas ambíguas são, em geral, inadequadas para uso em compiladores Dificultam a construção do analisador sintático Em alguns casos, são usadas gramáticas ambíguas junto com restrições adicionais Exemplo: precedência e associatividade

60 Tratando Ambiguidades
A seguinte de definição de comando apresenta ambigüidade Exemplo: “if (x) if (x) outro else outro” A qual “if” está ligado o “else”? cmd ::= if ( expr ) cmd else cmd | if ( expr ) cmd | outro expr ::= x

61 Tratando Ambiguidades
Solução cmd ::= matched-cmd | open-cmd matched-cmd ::= | if ( expr ) matched-cmd else matched-cmd | outro open-cmd ::= | if ( expr ) cmd | if ( expr ) matched-cmd else open-cmd

62 Análise de Descida Recursiva
É uma análise sintática top-down ou descendente É um parser LL(1) Left-to-right – a ordem de leitura dos tokens é da esquerda para a direita Leftmost derivation – segue a ordem típica de uma derivação mais à esquerda Só olha 1 token por vez

63 Análise de Descida Recursiva
É útil para construir parsers manualmente, por ser fácil de implementar Princípio Básico Se eu quero produzir um não-terminal e sei qual o terminal corrente eu devo saber que regra deve ser aplicada. Porém, requer uma gramática muito bem ajustada Veremos os ajustes necessários Mostraremos como usar a técnica, assumindo que o parser final será uma classe Java

64 Exemplo de Reconhecedor
Gramática: Terminais = {a,b,c} Não-terminais = {S, X} Produções = { S  a X b S  b X a S  c X  b X X  a } Não-terminal inicial = S a b c S a X b b X a X b X Error

65 Conjunto FIRST Aplicado a cadeias α quaisquer
É o conjunto de terminais que podem iniciar a cadeia α dada Se a cadeia derivar vazio, também inclui ε Como calcular?

66 Conjunto FIRST FIRST(ε) = { ε } FIRST(a) = { a }
Se α for a cadeia vazia (α = ε) FIRST(ε) = { ε } Se for um terminal a qualquer FIRST(a) = { a } Se for um não-terminal N com as produções N → α | β FIRST(N) = FIRST(α)  FIRST(β)

67 Conjunto FIRST Se for uma cadeia de símbolos α = X1X2...Xk
Depende de X1 Se X1 não pode gerar vazio FIRST (X1X2...Xk) = FIRST(X1) Se X1 pode gerar vazio FIRST (X1X2...Xk) = FIRST(X1)  FIRST(X2...Xk) Calcula para X1 e continua o cálculo recursivamente para o restante da cadeia

68 Múltiplas Produções comando ::= atribuição | declaração
Calculando o FIRST para as duas produções de comando ... comando ::= atribuição | declaração FIRST(atribuição) = FIRST(IDENTIFICADOR = <expressao> ;) = { IDENTIFICADOR } FIRST(declaração) = FIRST(tipo IDENTIFICADOR ;) = FIRST(tipo) = { int , char, float }

69 Eliminação de Fatoração
Indireta X  a b X  Y c Y  a d Elimina-se a indireção: X  a d c Sem fatoração: X  a Z Z  b Z  d c Direta: X  a b c X  a d e Sem fatoração: X  a Y Y  b c Y  d e


Carregar ppt "Revisão Compiladores – AP1"

Apresentações semelhantes


Anúncios Google