Teoria e Implementação de Linguagens Computacionais

Slides:



Advertisements
Apresentações semelhantes
Software Básico Silvio Fernandes
Advertisements

gerador de código intermediário
5.5 – Análise Bottom-Up Tentativa de construir uma árvore sintática para a sentença analisada, começando das folhas, indo em direção à raiz (pós-ordem.
III – Análise sintáctica
Linguagem de Montagem Visão geral.
Deyvisson, Rafael M.P., Renato, Robson C.F., Rodolfo
Análise Sintática Ascendente ­
Software Básico Silvio Fernandes
Compiladores Prof. Yandre Maldonado Compiladores - Prof. Yandre - 1.
Introdução à Compilação Prof. Leandro Magno Slides adaptados a partir do material cedido pelos professores Heloise Manica Paris Teixeira, Yandre M. G.
Prof. Heloise Manica Paris Teixeira
Curso Sistemas de Informação Disciplina: Arquitetura de Software
Softwares.
YACC.
Análise Sintática Ascendente
Relações em uma Gramática
Linguagem de Prog. e Programas
Análise Léxica Supondo o trecho de programa abaixo:
Construção de Compiladores
Construção de Compiladores
Construção de Compiladores
Linguagens de Programação
PROGRAMAÇÃO I UNIDADE 1.
Análise léxica e sintática
Aula prática - análise contextual
Gramáticas Livres de Contexto
Introdução a Programação
O Processo de Compilação
Teoria e Implementação de Linguagens Computacionais – IF688
Sintaxe e Semântica Prof.: Gláucya Carreiro Boechat
Análises léxica e sintática
Análise Léxica Primeira fase de um compilador
Análise Léxica e Sintática
Algoritmo e Programação
02/08/2011 Professor Leomir J. Borba- –
Capítulo II Gramáticas e Linguagens
Analise sintática aula-07-analise-sintática.pdf.
Análise Sintática LR Prof. Alexandre Monteiro
Análises léxica e sintática
Laboratório de Programação I Carlos Oberdan Rolim Ciência da Computação Sistemas de Informação.
Projeto de Linguagens de Programação
Processadores de Linguagens
Projeto de Tradutor Preditivo. Introdução Introdução Esquemas L-atribuidos são reconhecidos por analisadores Top-Down. Entre estes analisadores, se encontra.
Aula 12 1 Análise Sintáctica Compiladores, Aula Nº 12 João M. P. Cardoso.
Compiladores.
Análise Sintática Prof. Alexandre Monteiro
Tradução Dirigida por Sintaxe
Back-End Compilação aula-11-back-end.pdf.
Revisão Compiladores – AP2
Revisão Compiladores – AP1
Faculdade Pernambucana - FAPE Setembro/2007
Aula de SableCC Teoria e Implementação de Linguagens Computacionais (Compiladores) - IF688 – Artur Ribeiro de Aquino – ara Allan Jefferson – ajss.
COMPILADORES 04 Prof. Marcos.
Um Tradutor Dirigido por Sintaxe Simples
Fundamentos de linguagens de programação
Semântica de Linguagens de Programação
Compiladores – IF688 Professor: André Santos
Linguagens de Programação
Sintaxe de uma Linguagem
SISTEMAS DE INFORMAÇÃO
Faculdade Pernambuca - FAPE
PLP – JavaCC Java Compiler Compiler
Teoria e Implementação de Linguagens Computacionais Revisão 1º EE André Ricardo Schäffer Lopes – Rodrigo Diego Melo Amorim–
Analisador sintático: Tipos de análises sintáticas
Compilador Software que traduz o texto (linguagem fonte) que representa um programa para código máquina(linguagem alvo) capaz de ser executado pelo.
Faculdade Pernambuca - FAPE Compiladores Abril/2007 Compiladores Abril/2007.
Análise Contextual Tem o objetivo de verificar se o programa está de acordo com as restrições contextuais da linguagem fonte.
Análise Contextual Mauro Araújo Teoria e Implementação de Linguagens Computacionais - IF688 Centro de Informática – CIn Universidade Federal de Pernambuco.
Transcrição da apresentação:

Teoria e Implementação de Linguagens Computacionais Revisão 1º EE André Ricardo Schäffer Lopes – arsl@cin.ufpe.br Rodrigo Diego Melo Amorim– rdma@cin.ufpe.br

Níveis de Linguagem Linguagem de máquina Linguagem de alto nível Linguagem de montagem Assembly language Controle explícito dos endereços de memória para dados e para o próprio programa; Várias instruções de máquina para cada linha de comando da linguagem de alto-nível Linguagem de alto nível suporta • uso de expressões, usando notação semelhante à matemática; •tipos de dados primitivos e compostos; •estruturas de controle como if-then-else, while, for etc.; •declarações de variáveis, tipos, funções, procedimentos etc.; •abstração: o que é feito x como é feito; •encapsulamento (ou abstração de dados): classes, pacotes, módulos (orientação a objetos). Linguagem de montagem: Uso de um programa montador (assembler) Instruções têm relação direta com Linguagem de Máquina

Processadores de Linguagens Manipulam programas expressos em alguma linguagem de programação: Compiladores Interpretadores

Tradutores e Compiladores Linguagem fonte -> Linguagem destino Chinês para inglês Java para C Compilador Ling. de alto nível -> Ling. de baixo nível

Interpretadores Programa fonte -> executa imediatamente Lê, analisa e executa as instruções, uma de cada vez

Compiladores x Interpretadores Tempo de espera pela compilação Execução dos programas com máxima performance, em linguagem de máquina Interpretadores Resultados imediatos Execução (de linguagem de alto nível) até 100 vezes mais lenta que a versão compilada Interpretadores Sem a tradução do programa para código objeto antes da execução

Uso de interpretadores Programador trabalha em modo interativo Quer ver o resultado de uma instrução antes de entrar na próxima Execução de instruções apenas um vez, ou raramente Programas descartáveis Em que a velocidade de execução não é tão importante Inadequado quando o programa vai ser executado em produção, ou executado frequentemente, ou instruções tem formato complicado, difícil de ser analisado.

Diagramas Tombstone Programas Máquinas P L M

X Diagramas Tombstone Execução Programa P rodando na máquina M P M sort PPC sort PPC X M PPC x86

Diagramas Tombstone Compiladores S L T 

Diagramas Tombstone Tradução Um programa fonte P expresso na linguagem S -> um programa objeto expresso na linguagem T, usando um tradutor de S para T executando na máquina M? Um programa fonte P expresso na linguagem S -> um programa objeto expresso na linguagem T, usando um tradutor de S para T executando na máquina M

Diagramas Tombstone Tradução P S P T S M T  M

Diagramas Tombstone Interpretador S L

Diagramas Tombstone Interpretação Um programa P expresso em uma linguagem S, usando um interpretador de S executando na máquina M?

Diagramas Tombstone P S S M M

Compilação em estágios sort Java sort C sort x86 Java x86 C  C x86  Podemos ter n estágios Por exemplo, usado se vc n tem o compilador de java para x86 x86 x86

Cross-Compiler Roda em uma máquina hospedeira, mas gera código para outra máquina Máquina destino débil em memória, compilador ou outras ferramentas

Exemplo Cross-Compiler sort Java sort PPC sort PPC Java x86 PPC  PPC

Interpretive Compiler Características de interpretadores e compiladores Simples e rápido de traduzir para ela, e simples e rápida de interpretar Exemplo: Java, no JDK Portabilidade Compila para uma linguagem intermediária, e interpreta esta linguagem

Java no JDK Java M JVM  JVM M P JVM P Java P JVM Java M JVM  JVM M M

Máquina Real x Abstrata Projeto de uma nova máquina “Ultima” P Ultima P Ultima  Ultima M Ultima Para testes: usar um interpretador M

Bootstrapping Compilação de uma linguagem utilizando um compilador dessa mesma linguagem Utilizado para melhorar a eficiência de um compilador Precisa de um compilador escrito em outra linguagem que depois será descartado

Bootstrapping - melhorando a eficiência Ada Mfast  Ada Mslow Mfast  Ada Mslow  M

Bootstrapping - melhorando a eficiência Ada P Mfast P Mfast Ada Mslow Mfast  M M

Bootstrapping - melhorando a eficiência Ada Mfast  Ada Mfast  Ada Mslow Mfast  M

Especificação de Linguagens Sintaxe A forma do programa Organização das frases Palavras reservadas Semântica estática (Restrições contextuais) Regras de escopo e regras de tipo Semântica Significado do programa Semântica estática -> Gramáticas de atributos incorporam informações semânticas nas parse trees Determinar que todas as variáveis devem ser declaradas antes de serem referenciadas – O end do subprograma Ada seguido de um nome deve coincidir com o nome do subprograma • Esses dois problemas exemplificam a categoria das regras de linguagem chamada de semântica estática – Leva esse nome pois a análise necessária para verificar essas especificações pode ser feita na Compilação Situações em que a possibilidade de a frase ser bem formada depende do seu contexto Regras de Tipos •Normalmente, valores são classificados em tipos. •Cada operação na linguagem tem uma regra de tipos, que define os tipos esperados para os operandos e o tipo do resultado (se existir). •Qualquer operação utilizando um valor com tipo de errado gera um erro de tipos. Classificação de Linguagens em Relação a Tipos •Estaticamente tipada: todos os erros de tipos podem ser detectados estaticamente, sem executar o programa. •Dinamicamente tipada, se (alguns) erros de tipos só podem ser detectados durante a execução do programa. Regras de escopo •Exemplos: declaração de variáveis, let Ligação estática x Ligação dinâmica •Estática = em tempo de compilação, sem rodar o programa; •Dinâmica: só rodando o programa

O processo de Compilação begin if x = 5 then ... + params output 1100111 0011100011 Programa Código Fonte Compilador

Árvore sintática abstrata Fases de Compilação Código fonte Análise Léxica Tokens e Lexemas implementação abstração Árvore sintática abstrata Análise Sintática Análise Semântica As fases estão associadas às três partes da especificação de uma linguagem: sintaxe, restrições contextuais (semântica estática) e semântica. AST decorada Código máquina Geração de Código

Análise Léxica (Scanning) Código fonte -> sequência de tokens Símbolos como identificadores, literais, operadores, palavras-chave, pontuação etc. if (n == 0) { return 1; } else { ... } if LPAR "n" id assign "0" intLit RPAR LCUR return "1" intLit comm RCUR else

Análise Sintática int y = 0,k = 0; int x = y+++k; Agrupa caracteres ou Tokens em uma estrutura hierárquica com algum significado Determina se uma dada cadeia de entrada pertence ou não à linguagem definida por uma gramática int y = 0,k = 0; int x = y+++k; A seguinte construção é válida?

Gramáticas – descrevendo Linguagens Gramáticas Livres de Contexto são utilizadas para descrever linguagens de programação Produções Símbolos terminais Símbolos não-terminais Símbolo inicial

Exemplo S → S ; S S → id := E S → print (L) E → id E → num E → E + E E → (S , E) L → E L → L , E Terminais: id print , + ; := ( ) num Não terminas: S E L Símbolo inicial: S → é utilizado na notação de produções A cadeia seguinte pertence à gramática? a := 7; b := c + (d := 5 + 6, d)

Derivações S S ; S S ; id := E id := E ; id := E id := num ; id := E id := num ; id := E + E id := num ; id := E + (S, E) id := num ; id := id + (S, E) id := num ; id := id + (id := E, E) id := num ; id := id + (id := E + E, E) id := num ; id := id + (id := E + E, id) id := num ; id := id + (id := num + E, id) id := num ; id := id + (id := num + num, id) Para determinar se uma cadeia pertence à gramática pode ser utilizado o processo de Derivação

Construída conectando cada derivação à sua origem. Parse tree S S S ; id := E E id := num + E E Construída conectando cada derivação à sua origem. ( S , E ) id id id := E E + E num num Obs. Na prática não é implementada pelos compiladores.

Gramática Ambígua A partir dela uma sentença pode dar origem a mais de uma árvore de parsing diferente Indeterminismo Eliminação de ambigüidade Refatoração da gramática

Exemplo x := 1 + 2 + 3; S S E E id := id := E + E E + E num E + E num

Refatoração S → S ; S S → id := E S → print (L) E → id E → num E → E + E E → (S , E) L → E L → L , E S → S ; S S → id := E S → print (L) E → id E → num E → E + T E → T E → (S , E) L → E L → L , E

Parsers Avaliam uma entrada quanto à sintaxe Podem ser Top-down Recursive-descent / LL(k) Bottom-up LR(k)

Recursive descent parser Algoritmo baseado em previsões Funções mutuamente recursivas Simples implementação Uma função para cada não-terminal Uma cláusula para cada produção Verifica o primeiro símbolo terminal para decidir qual função usar

Exemplo parseA() { accept(‘a’); parseB(); accept(‘c’); parseC(); } case (d): parseC(); parseB(); case (c): accept(‘c’); } A ::= aBcC B ::= CB | cC C ::= da parseC() { accept(‘d’); accept(‘a’); }

Recursive descent parser Na prática, constrói uma tabela de produções indexadas por não-terminais e terminais a c d A A::= aBcC B B::= CB B::= CA C C::= da A ::= aBcC B ::= CB | CA C ::= da

Recursive descent parser Vantagens Fácil de implementar Fácil de entender Desvantagens Performance deficiente Gramática reconhecida possui restrições Sem recursão à esquerda Deve estar fatorada

Recursive descent parser A ::= aBcC B ::= CB | CA C ::= da A ::= aBcC B ::= CX X ::= B | A C ::= da Gramática LL(k) a c d A A::= aBcC B B::= CX C C::= da X X::=A X::=B

Parsers LL(k) Gramáticas SEM entradas duplicadas na tabela são conhecidas como LL(k) LL(1) -> Left-to-right, leftmost-derivation, 1-symbol lookahead Left-to-right – direção na qual os símbolos serão examinados Leftmost-derivation – ordem pela qual os símbolos não-terminais serão expandidos 1-symbol lookahead – não mais que um símbolo será avaliado por vez Também existem LL(2), LL(3),... Toda LL(1) é LL(2), toda LL(2) é LL(3),... LL(k)

Recursão à esquerda Gramáticas LL(1) são vulneráveis a entradas duplicadas. Por exemplo, o fragmento a seguir: E → E + T E → T O fato de E aparecer no início do lado direito da produção é a causa do problema. Isso é conhecido como Recursão à Esquerda. Para corrigir, precisamos refatorar a gramática com Recursão à Direita: E → T E´ E´ → +T E´ E´ →

Parsers LR(k) Supera as fraquezas de LL(k) LR(1) -> Left-to-right, rightmost-derivation, 1-symbol lookahead Uso de uma pilha para armazenar símbolos de forma temporária Possui duas operações, shift e reduce shift - move o primeiro símbolo para o topo da pilha reduce - escolhe uma regra da gramática do tipo X→A B C. pop C B A da pilha e push X. Consegue descrever a maioria das linguagens de programação LALR(1) Melhoramento sobre o LR(1) Diminui o tamanho da tabela de parsing

Parsing LR - de Gramáticas Ambíguas Gramáticas ambíguas ocasionam conflitos em parsers LR Shift-reduce conflict O parser não consegue decidir se empilha o próximo símbolo da entrada, ou se reduz para uma regra já disponível Reduce-reduce conflict O parser pode realizar uma redução para duas regras distintas

Parsing LR - de Gramáticas Ambíguas Dangling-else if a then { if b then s1 } else s2 ? if a then if b then s1 else s2 Caso clássico: dangling-else if a then { if b then s1 else s2 }

Parsing LR - de Gramáticas Ambíguas Solução: Transformar a gramática Introdução dos conceitos de matched e unmatched S ::= 'if' E 'then' S 'else' S S ::= 'if' E 'then' S S ::= ... S ::= M | U M ::= 'if' E 'then' M 'else' M | ... U ::= 'if' E 'then' S | 'if' E 'then' M 'else' U

Análise Semântica (Contextual) Verifica se o programa está de acordo com as restrições contextuais da linguagem fonte Em uma linguagem com tipos estáticos e ligação estática: Verifica regras de escopo Verifica regras de tipos Duas fases: Identificação Verificação Identificação: ocorrências de nomes x definições Verificação: aplica regras de tipos para cada expressão, inferindo os tipos, e depois compara com os tipos esperados, etc.

Identificação Faz a ligação entre uso de nomes e sua definição Tabela de identificação (tabela de símbolos), com nome e atributos de cada identificador Cada definição tem o seu escopo – parte do programa sobre a qual ela tem efeito Delimitado por um bloco

Estrutura de blocos de um programa Monolítica: O único bloco é o programa inteiro Declarações estão em um escopo global Regras de escopo: Nenhum identificador pode ser declarado mais de uma vez Nenhum identificador pode ser usado sem ter sido definido Define a organização da tabela de símbolos Monolítica : Basic, Cobol;

Estrutura de blocos de um programa Plana O programa pode ser particionado em vários blocos disjuntos Dois níveis de escopo: Escopo local: ocorrências de identificadores declarados localmente são restritos a um bloco em particular Outras declarações têm escopo global Fortran;

Estrutura de blocos de um programa Aninhada Blocos podem ser aninhados um dentro do outro Vários níveis de escopo: Declarações no nível mais externo têm escopo global (nível 1) Declarações dentro de um bloco interno são locais ao bloco Cada bloco está dentro de outro bloco, com um nível a mais Pascal, Ada, C, Java

Passos Travessia do programa fonte Um compilador de um passo atravessa o programa uma única vez Um compilador de múltiplos passos faz várias travessias O projeto de um compilador é diretamente relacionado ao número de passos

Compilação em múltiplos passos Módulo principal (driver) Chama cada um dos passos Driver do Compilador chama cada um dos passos, responsáveis pela análise sintática, análise contextual e geração de código. Analisador Sintático Analisador Contextual Gerador de Código

Compilação em passo único Analisador sintático Realiza/chama as atividades à medida que lê e reconhece o programa Driver do Compilador Analisador sintático vai realizando/chamando as atividades de análise contextual e geração de código, à medida que lê e reconhece o programa Analisador Sintático Analisador Contextual Gerador de Código

Pontos relevantes no Projeto de um Compilador Velocidade – possível vantagem para um passo Espaço – possível vantagem para um passo (dados x programa) Modularidade – vantagem de múltiplos passos Flexibilidade – vantagem de múltiplos passos Transformações/otimizações de programas – vantagem de múltiplos passos Características da linguagem fonte podem inviabilizar o uso de um passo único – se a linguagem permite o uso de uma função antes da sua definição, por exemplo.