Você está na página 1de 21

COMPILADORES

AULA 05

Professor: ADRIANO DOIMO


E-Mail: adriano.prof.cps@ipep.edu.br
• O código
sugerido para
um analisador /***********
léxico simples é global.h
dividido em três ***********/
partes
(arquivos): #define NONE -1
– Variáveis e #define NUM 256
constantes
globais int clinha;
(global.h)
– Analisador int tokenval;
Léxico (lexan.c) int lookahead;
– Programa de
Teste
(lexantest.c) Análise Léxica
Construção de um Analisador Léxico
Simples
/*********** if(t==' '||t=='\t') ; //
lexan.c else if(t=='\n')
***********/ clinha++;
#include <stdio.h> else if(isdigit(t)) {
#include <ctype.h> ungetc(t, stdin);
#include "global.h" scanf("%d", &tokenval);
int clinha=1; return NUM;
int tokenval=NONE; } else {
tokenval=NONE;
int lexan() { return t;
int t; }
while(1) { }
t=getchar(); }
Análise Léxica
Construção de um Analisador Léxico
Simples
/***********
lexantest.c
***********/ printf("%c\n",lookahead)
;
#include <stdio.h>
#include "global.h” }
lookahead=lexan();
main() { }
lookahead=lexan(); }
while(lookahead!=EOF)
{
if(lookahead==NUM) {

printf("%d\n",tokenval);
} else {
4
CONSTRUÇÃO DE UM
ANALISADOR LÉXICO SIMPLES
• Observando o código podemos notar que:
» A função lexan retorna os tokens como
inteiros.
» Caso o token sejam um número (NUM) a
variável global tokenval contem seu valor.
» A constante NUM é definida como 256 pois
tal valor não corresponde a qualquer outro
caractere válido (0 - 255 no ASCII).
» A variável global clinha mantêm uma
contagem do número de linha que está sendo
correntemente processada.
Análise Léxica
CONSTRUÇÃO DE UM
ANALISADOR LÉXICO SIMPLES
• Para compilar os arquivos
separadamente:
» gcc -c lexan.c
» gcc -c lexantest.c
» gcc lexan.o lexantest.o -o lexantest
• Para compilar todos os arquivos
simultaneamente:
» gcc lexan.c lexantest.c -o lexantest
Análise Léxica
BUFERIZAÇÃO DE ENTRADA
• Como a leitura da entrada pode
consumir quantidade significativa de
tempo da compilação, é interessante
otimizar tal operação.
• Duas técnicas simples podem ser
particularmente úteis:
» Uso de pares de buffers e
» Uso de sentinelas.

Análise Léxica
PARES DE BUFFERS
• A leitura de caracteres um a um é
ineficiente em termos de tempo.
• Como também existe a necessidade da
leitura de vários caracteres à frente do
lexema para sua identificação, o retorno
dos caracteres não usados significa
desperdício.
• Propõe-se o uso de um buffer de
tamanho 2N dividido em duas metades (N
é número de caracteres em um bloco de
disco).
Análise Léxica
PARES DE BUFFERS
• Princípios de funcionamento:
» Cada metade do buffer pode ser lida através de uma única operação de leitura.
Um eof é inserido caso a metade não seja preenchida.
» Mantêm-se dois apontadores: um para o início do lexema e outro para leitura
adiante.
» Se um apontador avança além do limite do buffer, a outra metade é lida e o
apontador transferido para tal metade.
• Manipulando-se tais apontadores reduz-se ao máximo as operações de leitura e o
desperdício de tempo devido a “devolução” de caracteres.

1a. Metade do Buffer 2a. Metade do Buffer

: ; : \n : \r : \t : E : : = : : M : * : C : * : * : 2 : ; : eof

Apontador Apontador
de Início do de Leitura
Lexema Adiante
Análise Léxica
SENTINELAS
• O esquema dos pares de buffers apresenta o inconveniente de um
duplo teste quando do avanço do ponteiro de leitura adiante.
• Adicionando um caractere especial ao final de cada metade do buffer
(cujo tamanho agora é 2N+2) reduz-se o teste duplo para outro simples.
• Tal caractere é denominado sentinela, devendo ser distinto dos
caracteres possíveis do alfabeto de entrada.
1a. Metade do Buffer 2a. Metade do Buffer

: \n : \r : \t : E : : = : : M : *: eof : C : * : * : 2 : ; : eof eof

Apontador Apontador
de Início do de Leitura
Lexema Adiante

• A escolha natural para o caractere de sentinela é eof.


• O funcionamento é o mesmo dos pares de buffers com ganho de
eficiência.
Análise Léxica
GRAMÁTICAS
• Para definir-se uma gramática é necessário:
» Um conjunto de símbolos que constituirão as cadeias desta
gramática, denominado alfabeto ou alfabeto terminal.
» Um conjunto de regras que permita distinguir quais cadeias
pertencem a gramática e quais não, onde são necessárias:
 regras de transformação das cadeias (produções)
 um conjunto de símbolos de não-terminais
 um símbolo de partida
• Portanto uma gramática é uma quádrupla:
G = {T, N, P, S}
onde:
» T : alfabeto de símbolos terminais
» N: conjunto de símbolos não-terminais
» P: conjunto de produções
» S: símbolo de partida ou axioma
Análise Léxica
EXPRESSÕES REGULARES
• Notação que permite exprimir
precisamente o formato de um conjunto
de cadeias.
• Se r é uma expressão regular então L(r) é
a linguagem produzida por tal expressão.
• A linguagem formada por um conjunto de
cadeia produzidas por uma expressão
regular é dita conjunto regular.
• Uma expressão regular pode ser
composta de expressões regulares mais
simples.
Análise Léxica
DIAGRAMAS DE CONWAY
• Os diagramas de Conway ou de transição, permitem especificar as ações de um
analisador léxico tal como num fluxograma estilizado.
• Permitem controlar as informações a medida que a entrada é lida pelo analisador
léxico.
• São determinísticos, ou seja, em qualquer ponto representado cada caminho é
único.
início
1

0 S0 S1 1
0

• Os círculos representam estados distintos.


• Os arcos representam as transições ou lados entre estados provocadas pelo
caractere indicado.
• Existe um estado inicial denotado pelo arco início.
• Existe um ou mais estados especiais denominados estados finais (círculo de borda
dupla).
Análise Léxica
DIAGRAMAS DE CONWAY
início
1

0 S0 S1 1
0

• Dada as cadeias c1 = 0010 e c2 = 1001 temos:


» Para c1:
S0  0  S0  0  S0  1  S1  0  S0
Esta cadeia não “chega” num estado final.
» Para c2:
S0  1  S1  0  S0  0  S0  1  S1
Esta cadeia “chega” num estado final.

Análise Léxica
AUTÔMATOS DE ESTADOS FINITOS
• Um diagrama de Conway é uma forma conveniente de representarmos
uma máquina abstrata denominada “Autômatos de Estados Finitos” ou
abreviadamente AF.
• Dizemos que um AF aceita ou reconhece uma certa cadeia c contendo n
símbolos (n>=0) quando é conduzida a um estado final por meio das
transições provocadas por todos os caracteres contidos na cadeia c.

início 1

0 S0 S1 1
0

• O AF acima descrito trata cadeias contendo apenas os símbolos 0 e 1,


aceitando aquelas que:
» sejam não vazias |c|>0 e
» representem números binários (terminados pelo dígito 1).
Análise Léxica
AUTÔMATOS DE ESTADOS FINITOS

início
a

b S0 S1 b

• O outro AF acima trata cadeias contendo apenas os


símbolos a e b, aceitando aquelas que:
» sejam vazias |c|>0 ou
» representem sequências contendo um número par de
símbolos a (e um número qualquer de símbolos b).

Análise Léxica
AUTÔMATOS DE ESTADOS FINITOS
• TIPOS
» Existem os AF Não-Determinísticos (AFN) e os AF Determinísticos
(AFD):
 Num AFN um mesmo símbolo pode rotular duas diferentes transições
para fora de um mesmo estado assim como  pode rotular uma
transição.
 Num AFD só pode existir uma transição rotulada com um dado símbolo
para fora de um mesmo estado e não se aceitam transições rotuladas por
.

• IMPLEMENTAÇÃO DE AF’s
» Um AF Determinístico pode ser facilmente implementado utilizando-se
comandos de seleção tipo switch (C ou Java).
» Também podem ser usados vetores mapeando a aplicação de
transição.
» Um AFD genérico pode ser construído de forma que sua
especificação seja obtida de um arquivo de definição.
Análise Léxica
Implementação
EXEMPLO de AFs
// Fonte Java
public class BasicAF { case 1:
public static void main(String a[]) { switch(a[i].charAt(p)) {
int p=0; case '0':
int state=0; state=0; break;
case '1':
while (p<a[i].length()) { state=1; break;
switch(state) { }
case 0: break;
switch(a[i].charAt(p)) { }
case '0': p++;
state=0; break; }
case '1': if (state==1)
state=1; break; System.out.println(”Aceita");
} else
System.out.println("Nao
break; aceita");
}}

Análise Léxica
ANÁLISE SINTÁTICA
• Conceito
» Estuda os métodos usados para decidir se uma cadeia pertence
ou não a uma linguagem definida por uma gramática.
» O objetivo é, se a cadeia pertencer á linguagem, obter a estrutura
sintática correspondente a mesma, em forma de árvore de
derivação.
» Corresponde a segunda fase da compilação e o Analisador
Sintático também é conhecido como Parser.
» A ligação entre o Analisador Sintático e o Léxico se ocorre da
seguinte forma: o A.L. após identificar os tokens existentes na
cadeia, os repassa ao A.S para a criação da árvore de derivação.
» A árvore de derivação pode ser construída explicitamente, ou
apenas realizar as ações conceitualmente como se baseadas na
árvore
» É desejável que os Analisadores Sintáticos possam “tentar
corrigir” erros, de modo a efetuar a análise até o final do código
fonte, retornando de uma só vez a maior quantidade possível de
erros. Análise Sintática
ANÁLISE SINTÁTICA
• Estratégias para Análise Sintática
» São dois os métodos mais usados para
Análise Sintática:
 Método Descendente (Top-down)
• Começam pela raiz (regra de partida da gramática)
• Em cada passo, caminha-se em direção as folhas,
substituindo-se símbolos não-terminais por símbolos
não terminais ou terminais.
 Método Ascendente (Bottom-up)
• Começam pelas folhas das árvores (tokens do código
fonte)
• Em cada passo, caminha-se em direção à raiz,
substituindo-se símbolos terminais por não-terminais
Análise Sintática
ATIVIDADES
• http://www.linhadecodigo.com.br/artigo/1643/compilad
ores-revisao-dos-principios-tecnicas-e-
ferramentas.aspx

Análise Sintática

Você também pode gostar