1 3 Interpretadores 1 3 1 Compiladores versus

  • Slides: 49
Download presentation
1. 3 – Interpretadores 1. 3. 1 – Compiladores versus Interpretadores n Execução de

1. 3 – Interpretadores 1. 3. 1 – Compiladores versus Interpretadores n Execução de um programa gerado por compilador: Dados Programa fonte Compilador + Montador Programa em linguagem de máquina Em tempo de compilação Em tempo de execução Resultado s

n Execução de um programa interpretado: - O interpretador faz análise léxica, sintática e

n Execução de um programa interpretado: - O interpretador faz análise léxica, sintática e semântica do programa-fonte, armazenando-o numa estrutura interna - Em seguida, ele percorre a estrutura interna, executando as operações ali especificadas, Dados consumindo os dados por elas pedidos Interpretado r Programa fonte Módulo de análise e armazenamento Programa numa estrutura interna Módulo de execuçã o Em Tempo de execução Resultado s

n Execução de um programa interpretado: - Não há criação de um programa em

n Execução de um programa interpretado: - Não há criação de um programa em linguagem de máquina equivalente ao programa-fonte - O único programa executado é o interpretador Dados Interpretado r Programa fonte Módulo de análise e armazenamento Programa numa estrutura interna Módulo de execuçã o Em Tempo de execução Resultado s

Exemplo: seja o comando de atribuição v 1 = v 2 + v 3

Exemplo: seja o comando de atribuição v 1 = v 2 + v 3 * v 4; n Um interpretador pode construir-lhe uma árvore de execução (estrutura interna): = Depois ele caminha pela árvore, executando as operações

n Na raiz, ele faz uma atribuição da expressão do lado direito à variável

n Na raiz, ele faz uma atribuição da expressão do lado direito à variável do lado esquerdo n No cálculo dessa expressão, ele soma a variável do lado esquerdo com a expressão do lado direito (chamada recursiva do cálculo de expressão) =

n Há interpretadores de código fonte e de código intermediário n Interpretador de código

n Há interpretadores de código fonte e de código intermediário n Interpretador de código fonte: Deve haver uma forma de armazenamento do programa fonte n Interpretador de código intermediário: o código intermediário pode ser gerado pelo mesmo processo usado nos compiladores

1. 3. 2 – Interpretadores de código-fonte n Durante a análise sintática, o programa-fonte

1. 3. 2 – Interpretadores de código-fonte n Durante a análise sintática, o programa-fonte é armazenado numa estrutura denominada árvore do programa n Depois, essa estrutura é percorrida, para que os comandos do programa sejam executados n Exemplo: programa para o Bubble-Sort

void main () { int n, i, p, aux, vetor[50]; char trocou; read (n);

void main () { int n, i, p, aux, vetor[50]; char trocou; read (n); for (i=0; i<n; i++) read (vetor[i]); trocou = 1; for (p = n-2; p>=0 && trocou == 1; p--) { trocou = 0; for (i = 0; i<=p; i++) if (vetor[i] > vetor[i+1]) { aux = vetor[i]; vetor[i] = vetor[i+1]; vetor[i+1] = aux; trocou = 1; } } for (i=0; i<n; i++) write (vetor[i]); } Sua árvore de armazenamen to

As declarações podem ser eliminadas

As declarações podem ser eliminadas

Cada nó retangular pode ser mais detalhado

Cada nó retangular pode ser mais detalhado

Cada nó retangular pode ser mais detalhado Ver detalhes

Cada nó retangular pode ser mais detalhado Ver detalhes

Cada nome de variável deve ser substituído por um ponteiro para sua posição na

Cada nome de variável deve ser substituído por um ponteiro para sua posição na tabela de símbolos Lá deve haver um campo para guardar seu valor

Interpretação de código fonte é muito ineficiente Os programas costumam ter muitos aninhamentos Cada

Interpretação de código fonte é muito ineficiente Os programas costumam ter muitos aninhamentos Cada tipo de comando é executado por um módulo específico

O módulo de um comando repetitivo irá chamar os módulos dos comandos de seu

O módulo de um comando repetitivo irá chamar os módulos dos comandos de seu escopo Se um deles for um comando condicional, ocorrerá com ele o mesmo Num dado momento, poderão estar ativas várias versões de diversos módulos

n Há consumo de muita memória e sobrecarga de trabalho para gerenciá-la n Os

n Há consumo de muita memória e sobrecarga de trabalho para gerenciá-la n Os interpretadores de código intermediário são os preferidos n No entanto, o conceito de árvore de programa é muito útil em softwares que fazem análise do programa-fonte n Por exemplo, em compiladores paralelos, a detecção de paralelismo exige análise de dependências que só pode ser feita se o programafonte estiver apropriadamente armazenado

1. 3. 3 – Interpretadores de código intermediário n Podem ter os mesmos componentes

1. 3. 3 – Interpretadores de código intermediário n Podem ter os mesmos componentes da frente de um compilador convencional: Analisadores léxico, sintático e semântico - Gerador de código intermediário - Otimizador de código intermediário - n Além desses, devem ter o componente que vai fazer a interpretação propriamente dita do código intermediário n Esse componente percorre o código, executando as

Exemplo: laço do programa anterior: for (i = 0; i<=p; i++) if (vetor[i] >

Exemplo: laço do programa anterior: for (i = 0; i<=p; i++) if (vetor[i] > vetor[i+1]) { aux = vetor[i]; vetor[i] = vetor[i+1]; vetor[i+1] = aux; trocou = 1; } n Suas quádruplas com alguma otimização:

A propósito, quádruplas para indexação: n Sejam as seguintes declarações e comandos: int i,

A propósito, quádruplas para indexação: n Sejam as seguintes declarações e comandos: int i, j, k, A[6][5]; i = 4; j = 3; k = A[i][j-2] + 5; A[8 -i][2*j-3] = i + j * k; n Acesso a um elemento genérico A[i][j]: - n Uma vez conhecido o endereço inicial da matriz A, é necessário localizar o elemento A[i][j] Seja a seguir o mapa de A[6][5] na memória:

n Seja m o número de linhas e n o número de colunas da

n Seja m o número de linhas e n o número de colunas da matriz A n O endereço do elemento A[i][j] é dado pela fórmula: Ender (A) + i * n + j n Para m = 6, n = 5, o endereço de A[4][3] é Ender (A) + 23

n No programa, cada índice pode ser uma expressão inteira n Calcula-se o valor

n No programa, cada índice pode ser uma expressão inteira n Calcula-se o valor de cada índice, empilhando-o numa pilha de índices n Isso pode ser feito pela execução de uma quádrupla de operador IND: IND, i , ---- , --- IND, j , ----

n Calcula-se o endereço de A[i][j], usando uma quádrupla de operador INDEX: INDEX ,

n Calcula-se o endereço de A[i][j], usando uma quádrupla de operador INDEX: INDEX , A , 2 , temp 1 n Sua execução consiste em: - Pegar as dimensões e o endereço de A na tabela de símbolos - Desempilhar dois índices - Calcular o endereço, colocando-o na variável temp 1 n A variável temp 1 é portanto

n Formação do código intermediário: int i, j, k, A[6][5]; i = 4; j

n Formação do código intermediário: int i, j, k, A[6][5]; i = 4; j = 3; k = A[i][j-2] + 5; A[8 -i][2*j-3] = i+j*k; temp 2 tem o endereço do elemento A[i][j-2] É necessário saber o valor guardado nesse elemento A quádrupla @, temp 2, ---, temp 3 atribui a temp 3 o valor do local apontado por temp 2 : =, 4, ---, i : =, 3, ---, j IND, i, ---, j, 2, temp 1 IND, temp 1, ---, --INDEX, A, 2, temp 2 @, temp 2, ---, temp 3 +, temp 3, 5, k

n Formação do código intermediário: int i, j, k, A[6][5]; i = 4; j

n Formação do código intermediário: int i, j, k, A[6][5]; i = 4; j = 3; k = A[i][j-2] + 5; A[8 -i][2*j-3] = i+j*k; : =, 4, ---, i : =, 3, ---, j IND, i, ---, j, 2, temp 1 IND, temp 1, ---, --INDEX, A, 2, temp 2 temp 7 tem o endereço do elemento A[8 -i][2*j-3] @, temp 2, ---, temp 3 +, temp 3, 5, k O valor de temp 9 deve ser atribuído ao local apontado por temp 7 -, 8, i, temp 4 IND, temp 4, ---, --*, 2, j, temp 5 -, temp 5, 3, temp 6 IND, temp 6, ---, --INDEX, A, 2, temp 7 A quádrupla #, temp 9, ---, temp 7 atribui o valor de temp 9 ao local apontado por temp 7 *, j, k, temp 8 +, i, temp 8, temp 9 #, temp 9, ---, temp 7

Voltando ao laço do programa anterior: for (i = 0; i<=p; i++) if (vetor[i]

Voltando ao laço do programa anterior: for (i = 0; i<=p; i++) if (vetor[i] > vetor[i+1]) { aux = vetor[i]; vetor[i] = vetor[i+1]; vetor[i+1] = aux; trocou = 1; } n Suas quádruplas:

Estrutura de dados São omitidos vários ponteiros As quádrupla s poderiam ser guardada s

Estrutura de dados São omitidos vários ponteiros As quádrupla s poderiam ser guardada s numa lista encadead a em vez de num

Desvantagens da interpretação: n A execução de programas compilados é muito mais rápida que

Desvantagens da interpretação: n A execução de programas compilados é muito mais rápida que a de programas interpretados n Apesar do código intermediário ser aperfeiçoado antes da interpretação propriamente dita, o código objeto ainda pode ser otimizado - n Isso não ocorre em programas interpretados Um interpretador faz por software a interpretação do código da operação; num programa compilado isso ocorre por hardware, o que é muito mais rápido

Vantagens da interpretação: n O código intermediário é independente de máquina, o que lhe

Vantagens da interpretação: n O código intermediário é independente de máquina, o que lhe confere portabilidade n Um programa compilado só pode rodar em máquinas compatíveis com sua máquina alvo n Interpretadores se mostram adequados para as redes de computadores heterogêneos, que é o caso da Internet n A elaboração de um módulo interpretador propriamente dito é muito mais simples que a de um back-end de compilador

n Os detalhes da arquitetura da máquina-alvo complicam muito o projeto do back-end n

n Os detalhes da arquitetura da máquina-alvo complicam muito o projeto do back-end n Para casos em que a rapidez de execução não é fundamental, os interpretadores são preferidos, em comparação com os compiladores A Linguagem Java: n Seu compilador gera código denominado bytecode, independente de máquina n Bytecode é traduzido para linguagem de máquina por uma máquina virtual residente em qualquer ambiente Java n Bytecode é portável, porém, para ser executado,

1. 4 – Automação da Construção de Compiladores 1. 4. 1 – Bootstrapping e

1. 4 – Automação da Construção de Compiladores 1. 4. 1 – Bootstrapping e compiladores cruzados n O compilador para a primeira linguagem de programação só poderia ter sido escrito em Assembly n Assim foi com Fortran e Cobol n Seus projetos demandaram esforço de programação descomunal

No princípio: Assembler Máquina M 1

No princípio: Assembler Máquina M 1

Primeiro compilador Fortran, escrito em Assembly: Fortran em Assembly Assembler Fortran em M 1

Primeiro compilador Fortran, escrito em Assembly: Fortran em Assembly Assembler Fortran em M 1 Máquina M 1

Compilador Algol, escrito em Fortran: Fortran em Assembly Assembler Algol em Fortran em M

Compilador Algol, escrito em Fortran: Fortran em Assembly Assembler Algol em Fortran em M 1 Máquina M 1 Algol em M 1

Compilador Pascal, escrito em Algol: Fortran Pascal em Assembly em Algol Assembler Algol em

Compilador Pascal, escrito em Algol: Fortran Pascal em Assembly em Algol Assembler Algol em Fortran em M 1 Máquina M 1 Algol em M 1 Pascal em M 1

Compilador C, escrito em Pascal: Fortran Pascal em Assembly em Algol C em Pascal

Compilador C, escrito em Pascal: Fortran Pascal em Assembly em Algol C em Pascal Assembler Algol em Fortran em M 1 Algol em M 1 C em M 1 Máquina M 1 Pascal em M 1

Em UNIX, compiladores L 1, L 2, . . . , escritos em C:

Em UNIX, compiladores L 1, L 2, . . . , escritos em C: Fortran Pascal em Assembly em Algol C em Pascal Assembler Algol em Fortran em M 1 Algol em M 1 L 1 em C C L 2 em M 1 C Máquina M 1 Pascal em M 1 L 1 em M 1 L 2 em M 1

Em UNIX, compiladores C eram escritos em C: Fortran Pascal em Assembly em Algol

Em UNIX, compiladores C eram escritos em C: Fortran Pascal em Assembly em Algol C em Pascal Assembler Algol em Fortran em M 1 Algol em M 1 C 1 em C C C 2 em C em M 1 Máquina M 1 Pascal em M 1 C 1 em M 1 C 2 em M 1

n Bootstrapping: propriedade de uma linguagem compilar a si mesma n Compilador cruzado: compilador

n Bootstrapping: propriedade de uma linguagem compilar a si mesma n Compilador cruzado: compilador que roda numa máquina e gera código para outra n Para uma máquina M 2, seria necessário escrever um programa em Assembly para um primeiro compilador? n Com bootstrapping e compiladores cruzados pode -se evitar isso (visto a seguir)

n Um compilador é caracterizado por 3 linguagens: - A linguagem-fonte (F) que ele

n Um compilador é caracterizado por 3 linguagens: - A linguagem-fonte (F) que ele compila - A linguagem-objeto (O) para a qual ele gera código - A linguagem de implementação (I) na qual ele está escrito n Simbolicamente, FIO n Ou, usando diagrama T: F O I

n Seja M usado para denotar a linguagem de máquina de um computador ou

n Seja M usado para denotar a linguagem de máquina de um computador ou máquina M n Para um compilador rodar na máquina M, sua linguagem de implementação deve ser M F O M

n Seja um compilador L 1 MM residindo na máquina M n Deseja-se em

n Seja um compilador L 1 MM residindo na máquina M n Deseja-se em M um compilador para uma nova linguagem L 2, ou seja, L 2 MM n Primeiramente escreve-se um programa L 2 L 1 L 2 M n Depois roda-se na máquina M: Entrada Programa em execução M L 1 L 2 Saída L 1 M M O compilador de M M L 2 está pronto para rodar em M

n Esquema para se obter um compilador cruzado: Entrada Compilador residindo em M 1,

n Esquema para se obter um compilador cruzado: Entrada Compilador residindo em M 1, em execução L 2 M 2 L 1 M 2 L 1 M 1 Saída n Agora em M 1, um compilador de L 2 para a máquina M 2 n Os programas escritos em L 2 são compilados em M 1 e o código objeto é transportado para M 2

n Seja então L a primeira linguagem a ser instalada na máquina M 2,

n Seja então L a primeira linguagem a ser instalada na máquina M 2, mas já instalada na máquina M 1 (LM 1 M 1) L M 2 L n Primeiramente, bootstrapping: LLM 2 n Depois, produz-se em M 1 um compilador cruzado n Finalmente, usando em M 1 o compilador cruzado produzido n É só transportar LM 2 M 2 para a máquina M 2 L Saída Entrada L M 2 Saída M 2 Programa em L M 2 Compilador execução M 1 Programa em M 1 desejado execução M 1

1. 4. 2 – Compiladores de compiladores n As ferramentas de automação, logo que

1. 4. 2 – Compiladores de compiladores n As ferramentas de automação, logo que começaram a surgir, receberam alguns nomes um tanto ambiciosos: - Geradores de compiladores - Compiladores de compiladores - Sistemas de construção de tradutores

1. 4. 2 – Compiladores de compiladores n Eram ferramentas de uso limitado, pois

1. 4. 2 – Compiladores de compiladores n Eram ferramentas de uso limitado, pois eram orientadas em torno de modelos particulares de linguagens n Devido à grande heterogeneidade das linguagens e das arquiteturas, é muito difícil a elaboração de um gerador de propósitos gerais eficiente n O que existe hoje são ferramentas automáticas para o projeto de componentes específicos, relacionados a seguir

1. 4. 3 – Ferramentas para cada componente Utilizam linguagens especializadas para a especificação

1. 4. 3 – Ferramentas para cada componente Utilizam linguagens especializadas para a especificação e implementação do componente e algoritmos bem sofisticados Geradores de analisadores léxicos: n Têm como entrada expressões regulares e implementam um autômato finito reconhecedor e classificador dos átomos dos programas a serem compilados n A mais conhecida entre elas é o Lex do sistema Unix, que possui também versões para o sistema DOS n O programa gerado é escrito em Linguagem C

1. 4. 3 – Ferramentas para cada componente Geradores de analisadores sintáticos: n Têm

1. 4. 3 – Ferramentas para cada componente Geradores de analisadores sintáticos: n Têm como entrada a gramática livre de contexto da linguagem-fonte do compilador n Nos compiladores primitivos, a análise sintática consumia grande fração do tempo de compilação e do esforço intelectual para escrever um compilador n Hoje é considerada uma das fases mais fáceis de serem implementadas

1. 4. 3 – Ferramentas para cada componente Geradores de analisadores sintáticos: n Utilizam

1. 4. 3 – Ferramentas para cada componente Geradores de analisadores sintáticos: n Utilizam algoritmos de analise muito eficientes, porém muito complexos para serem implementados à mão n A mais conhecida: Yacc (Yet Another Compiler) do sistema UNIX que também possui diversas versões para o sistema DOS n O programa gerado também é escrito em Linguagem C

1. 4. 3 – Ferramentas para cada componente Geradores de código intermediário: n Produzem

1. 4. 3 – Ferramentas para cada componente Geradores de código intermediário: n Produzem uma coleção de rotinas que, ao caminhar pela árvore sintática do programa, já com atributos calculados pelo analisador semântico, produzem o código intermediário Analisadores de fluxo de dados: n Importante ferramenta para a otimização do código intermediário

1. 4. 3 – Ferramentas para cada componente Geradores de código objeto: n Recebem

1. 4. 3 – Ferramentas para cada componente Geradores de código objeto: n Recebem como entrada uma coleção de regras que definem a tradução de cada tipo de comando do código intermediário em código de máquina ou Assembly n Essas regras devem incluir detalhes suficientes para se escolher os locais adequados para alocação de variáveis (registradores, memória, pilha, etc)