Teoria e Implementao de Linguagens Computacionais Reviso 1

  • Slides: 63
Download presentation
Teoria e Implementação de Linguagens Computacionais Revisão 1º EE André Ricardo Schäffer Lopes –

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 de máquina 0000 0001 0010 controle explícito dos endereços de memória

Níveis de Linguagem de máquina 0000 0001 0010 controle explícito dos endereços de memória dificuldade em ler, escrever, editar Linguagem de Montagem Assembly language Uso de um programa montador (assembler) Instruções têm relação direta com Linguagem de Máquina Linguagem de alto nível Mais próxima da linguagem natural Uso de interpretadores ou compiladores Várias instruções de máquina para cada linha de comando

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

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

Tradutores e Compiladores Tradutor Linguagem fonte -> Linguagem destino Chinês para inglês Java para

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

Interpretadores Recebem um programa fonte escrito em uma linguagem fonte, e o executam imediatamente

Interpretadores Recebem um programa fonte escrito em uma linguagem fonte, e o executam imediatamente Lê, analisa e executa as instruções, uma de cada vez

Compiladores x Interpretadores Compiladores Tempo de espera pela compilação Execução dos programas com máxima

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

Uso de interpretadores Programador trabalha em modo interativo Quer ver o resultado de uma

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

Diagramas Tombstone Programas Programa P expresso na linguagem L P L sort graph Java

Diagramas Tombstone Programas Programa P expresso na linguagem L P L sort graph Java x 86 Basic

Diagramas Tombstone Máquinas Máquina M M x 86 PPC SPARC

Diagramas Tombstone Máquinas Máquina M M x 86 PPC SPARC

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

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

Diagramas Tombstone Tradutores Tradutor de S para T expresso na linguagem L S T

Diagramas Tombstone Tradutores Tradutor de S para T expresso na linguagem L S T L Java x 86 C x 86

Diagramas Tombstone Tradução Um programa fonte P expresso na linguagem S -> um programa

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?

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

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

Compilação em dois estágios sort Java C C sort C x 86 x 86

Compilação em dois estágios sort Java C C sort C x 86 x 86

Cross-Compiler Roda em uma máquina hospedeira, mas gera código para outra máquina Usado quando

Cross-Compiler Roda em uma máquina hospedeira, mas gera código para outra máquina Usado quando a máquina destino tem pouca memória, não tem compilador ou outras ferramentas

Exemplo Cross-Compiler sort Java PPC PPC x 86 PPC

Exemplo Cross-Compiler sort Java PPC PPC x 86 PPC

Diagramas Tombstone Interpretador de S expresso na linguagem L S L Basic SQL shell

Diagramas Tombstone Interpretador de S expresso na linguagem L S L Basic SQL shell x 86 C

Diagramas Tombstone Interpretação Um programa P expresso em uma linguagem S, usando um interpretador

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

Diagramas Tombstone P S S M M

Interpretive Compiler Características de interpretadores e compiladores Compila para uma linguagem intermediária, e interpreta

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

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

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

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

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

Bootstrapping Compilação de uma linguagem utilizando um compilador dessa mesma linguagem Utilizado para melhorar

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 versão 1 Ada-S M C M M

Bootstrapping versão 1 Ada-S M C M M

Bootstrapping versão 2 Inicia desenvolvimento do novo compilador Ada-S M Ada-S Compilador novo Ada-S

Bootstrapping versão 2 Inicia desenvolvimento do novo compilador Ada-S M Ada-S Compilador novo Ada-S M M Compilador antigo

Bootstrapping versão 2 a Inicia desenvolvimento do novo compilador Ada-S M Ada-S Compilador +novo

Bootstrapping versão 2 a Inicia desenvolvimento do novo compilador Ada-S M Ada-S Compilador +novo Ada-S M M Compilador novo

Bootstrapping versão 3 Linguagem estendida Ada M Ada-S MM M

Bootstrapping versão 3 Linguagem estendida Ada M Ada-S MM M

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

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

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

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

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

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

Especificação de Linguagens Sintaxe A forma do programa Semântica estática (Restrições contextuais) Organização das

Especificação de Linguagens Sintaxe A forma do programa Semântica estática (Restrições contextuais) Organização das frases Palavras reservadas Regras de escopo e regras de tipo Semântica Significado do programa

O processo de Compilação begin if x = 5 then. . . output +

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

Fases de Compilação Análise Léxica Tokens e Lexemas Análise Sintática Árvore sintática abstrata Análise

Fases de Compilação Análise Léxica Tokens e Lexemas Análise Sintática Árvore sintática abstrata Análise Semântica AST decorada Geração de Código máquina implementação abstração Código fonte

Análise Léxica (Scanning) Código fonte -> sequência de tokens Símbolos como identificadores, literais, operadores,

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 id "n" assign int. Lit "0" RPAR LCUR return int. Lit "1" comm RCUR else

Análise Sintática Agrupa caracteres ou Tokens em uma estrutura hierárquica com algum significado Responsável

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

Gramáticas – descrevendo Linguagens Gramáticas Livres de Contexto são utilizadas para descrever linguagens de

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

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 : = num

Derivações S S; S S ; id : = E id : = num ; id : = E + E id : = num ; id : = E + (S, E) id : = num ; id : = id + (id : = E, E) id : = num ; id : = id + (id : = E + E, id) id : = num ; id : = id + (id : = num + num, id)

Parse tree S S id : = S E ; num Construída conectando cada

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

Gramática Ambígua A partir dela uma sentença pode dar origem a mais de uma

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

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

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

Refatoração S→S; S S → id : = E S → print (L) E

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

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

Recursive descent parser Algoritmo Funções Simples baseado em previsões mutuamente recursivas implementação Uma função

Recursive descent parser Algoritmo Funções Simples baseado em previsões mutuamente recursivas 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 A : : = a. Bc. C parse. A() { accept(‘a’); parse. B();

Exemplo A : : = a. Bc. C parse. A() { accept(‘a’); parse. B(); accept(‘c’); parse. C(); } parse. B() { case (d): parse. C(); parse. B(); case (c): accept(‘c’); parse. C(); } B : : = CB | c. C C : : = da parse. C() { accept(‘d’); accept(‘a’); }

Recursive descent parser Na prática, constrói uma tabela de produções indexadas por não-terminais e

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. Bc. C A B B: : = CA C C: : = da B : : = CB | CA C : : = da A: : = a. Bc. C

Recursive descent parser Vantagens Fácil de implementar Fácil de entender Desvantagens Performance deficiente Gramática

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 : : = a. Bc. C B : : =

Recursive descent parser A : : = a. Bc. C B : : = CX B : : = CB | CA Gramática LL(k) X : : = B | A C : : = da a A c d A: : = a. Bc. C 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) ->

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ãoterminais 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

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

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.

Parsing LR - de Gramáticas Ambíguas Gramáticas ambíguas ocasionam conflitos em parsers LR Shift-reduce

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

Parsing LR - de Gramáticas Ambíguas Dangling-else if a then { if b then s 1 } else s 2 if a then if b then s 1 else s 2 ? if a then { if b then s 1 else s 2 }

Parsing LR - de Gramáticas Ambíguas Solução: Transformar a gramática Introdução dos conceitos de

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

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 ela: Verifica regras de escopo Verifica regras de tipos Duas fases: Identificação: ocorrências de nomes x definições Verificação de tipos: aplica regras de tipos para cada expressão, inferindo os tipos, e depois compara com os tipos esperados

Identificação Faz a ligação entre uso de nomes e sua definição Tabela de identificação

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

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

Estrutura de blocos de um programa Plana O programa pode ser particionado em vários

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

Estrutura de blocos de um programa Aninhada Blocos podem ser aninhados um dentro do

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

Passos Travessia do programa fonte Um compilador de um passo atravessa o programa uma

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

Compilação em múltiplos passos Módulo principal (driver) Chama cada um dos passos Driver do Compilador 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

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 Analisador Contextual Gerador de Código

Pontos relevantes no Projeto de um Compilador Velocidade – possível vantagem para um passo

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