Sunteți pe pagina 1din 28

Compiladores e

Computabilidade

Unidade I

Todos os direitos reservados. Nenhuma parte desta obra pode ser reproduzida ou transmitida por qualquer forma e/ou
quaisquer meios (eletrnico, incluindo fotocpia e gravao) ou arquivada em qualquer sistema ou banco de dados sem
permisso escrita da Universidade Paulista.

Apresentao

Mesmo compreendendo vrios aspectos funcionais de um sistema computacional, a ideia


de criar um programa que seja capaz de transformar as instrues dadas em cdigofonte em um
programa executvel nos parece uma tarefa desafiadora. Esta disciplina demonstrar que, reunindo
conhecimentos de diversas subreas da computao e adotando uma metodologia apropriada, essa
tarefa pode ser realizada e com sucesso!
Embora possam ser conduzidos de modo prtico, o projeto e a construo de um compilador
propriamente dito so mais adequados quando se busca uma formao mais aprofundada, tpica de
um curso de psgraduao. Aqui, a inteno de que o aluno compreenda: quais so as tarefas
envolvidas no processo de transformao dos algoritmos codificados em programas; a maneira como
as informaes relevantes so extradas e como podem ser compreendidas de maneira adequada; e, por
fim, como criar uma sequncia funcional anloga utilizando o conjunto de instrues de mquinas e
chamadas de sistema, considerando os aspectos da arquitetura escolhida.
Ao trmino do curso, o aluno compreender melhor diversos aspectos fundamentais em uma
linguagem de programao, relacionar estruturas e conjuntos de comandos com seus correlatos em
termos de hardware, alm de ter visto um exemplo consistente de um projeto modular e integrado.
Objetivos gerais
A construo de compiladores um ramo da cincia da computao cujo estudo agrega
conhecimentos e habilidades importantes aos profissionais desta rea, tais como um exemplo
de estruturao apropriada do problema, abordagem metodolgica consistente e necessria ao
desenvolvimento de projetos deste porte, alm de experincias em sistemas que congregam diferentes
conhecimentos especficos de computao.
Dadas as caractersticas de interpretao e traduo de informaes, o estudo dos conceitos
fundamentais envolvidos no projeto e construo de compiladores permite uma aplicao mais ampla
das tcnicas envolvidas, uma vez que torna o aluno familiarizado com os elementos necessrios
escrita de interpretadores de comandos e programas de interface, bem como ao processamento de
dados estruturados e extensveis.
Objetivos especficos
Familiarizar os alunos com as principais tcnicas e conceitos envolvidos na compilao de
programas de computador. Aprimorar seus conhecimentos sobre programao atravs do estudo da
estrutura e caractersticas de uma linguagem de programao e das tarefas realizadas por um
compilador para transformar os programas em seus equivalentes em linguagem de mquina.
Conhecer a organizao e as operaes bsicas de um compilador. Compreender o impacto das
caractersticas e paradigmas das diferentes linguagens de programao. Relacionar os fundamentos de
programao com as tarefas desempenhadas pelo hardware, sendo capaz de compreender melhor a evoluo
3

Unidade I
das novas arquiteturas de computador. Capacitlo no desenvolvimento de ferramentas de gerao de
programas para avaliao e apoio aos processos de engenharia de software, interpretadores e processadores
de dados estruturados e tambm no desenvolvimento de novas linguagens e paradigmas de programao.
Plano de ensino
1. Introduo
1.1. Descrio do processo realizado pelo computador a partir da codificao de um algoritmo pelo
programador at a sua execuo em um computador
1.2. Caracterizao das linguagens quanto ao nvel de abstrao
1.3. Identificao e diferenciao dos elementos do domnio
1.3.1. Linguagemfonte, de implementao e alvo
1.3.2. Compiladores, tradutores, montadores e interpretadores
1.3.3. IDEs, debuggers, editores grficos de interface e demais ferramentas de apoio
1.4. Modelos para a construo de compiladores
1.4.1. As etapas de transformao do cdigo
1.4.2. Compiladores de uma passagem e de mltiplas passagens
1.4.3. O modelo de Anlise e Sntese
2. Anlise Lxica
2.1. A transformao de fluxo de caracteres em lista de tokens
2.2. Autmatos finitos para reconhecimento e classificao de tokens
2.3. Identificao de erros lxicos
3. Anlise Sinttica
3.1. Produes (das Gramticas Livres de Contexto) e rvores de derivao
3.2. Descendente (topdown): Descendente Recursiva e LL
3.3. Ascendente (bottomup): LR e LALR
4

3.4. Abordagens para o tratamento e a recuperao de erros


4. Anlise Semntica
4.1. Tabela de smbolos
4.2. Gramticas de atributos
5. Gerao de cdigo
5.1. Cdigos de trs endereos
5.2. Blocos bsicos
5.3. Diagramas e grafos de dependncia
5.4. Otimizaes
6. Assemblers, linkeditores e carregadores
Bibliografia bsica
AHO, A. V.; LAM, M. S.; SETHI, R.; ULLMANN, J. D. Compiladores: princpios, tcnicas e ferramentas 2
ed. So Paulo: Pearson AddisonWesley, 2008.
LOUDEN, K. C. Compiladores: princpios e prticas 1 ed. So Paulo: Pioneira Thomson Learning, 2004.
TOSCANI, Simo Sirineo; PRICE, Ana Maria de Alencar. Implementao de linguagens de
programao: compiladores 3 ed. Vol. 9 Artmed, 2009.
Bibliografia complementar
RICARTE, I. Introduo compilao 1 ed. Rio de Janeiro: Elsevier, 2008.
GRUNE, D.; BAL, H. E.; JACOBS, C. J. H.; LANGENDOEN, K. G. Projeto moderno de compiladores:
implementao e aplicaes Rio de Janeiro: Campus, 2001.
APPEL, A. W. Modern compiler implementation in Java. London: Cambridge University Press, 1998.
EPSTEIN, Richard L.; CARNIELLI, Walter. Computabilidade, funes computveis, lgica e os
fundamentos da matemtica. UNESP, 2009.
NIES, Andre. Computability and randomness. Oxford, UK, 2009.
5

Unidade I

Compiladores e Computabilidade

Unidade I
Introduo
Da codificao execuo de um programa

Algumas perguntas importantes de serem respondidas j de incio so: o que ocorre desde a
codificao de um algoritmo por um programador at a sua execuo em um computador? Quais
so os elementos envolvidos? O que compete a cada um deles neste processo?
Programador Cdigofonte [Prprocessador] Cdigofonte sem macros
[Compilador] Cdigoobjeto (.obj) [Linker] Cdigo de mquina realocvel (.exe)
[Carregador] Cdigo de mquina absoluto
Podemos descrever esse processo da seguinte maneira:
O programador codifica o algoritmo utilizando uma linguagem de programao (linguagemfonte)
e o salva em um arquivo texto (cdigofonte). Antes de ser compilado, o cdigo submetido a um
prprocessamento para a remoo de macros e, ento, pode ser passado ao compilador para ser
transformado em cdigo de mquina (cdigoobjeto), de acordo com a plataformaalvo.
Neste novo arquivo criado pelo compilador, habitualmente com extenso.obj, temos apenas o
cdigo referente s instrues e funes definidas por este programador, faltando ainda juntlo
com o cdigo de mquina relativo s funes das bibliotecas. Para que isso seja feito, um programa
realiza a ligao entre os diversos arquivosobjetos e acrescenta os cabealhos necessrios ao sistema
operacional para identificlos como um arquivo executvel (cdigo de mquina realocvel).
Quando, ento, desejamos executar o programa, o carregador ou loader do sistema operacional
aloca o cdigo em alguma poro de memria em que caiba e que esteja disponvel, resolvendo
os endereos das variveis estticas e de entrada de funo at o momento sob a forma de
deslocamentos, por isso sendo chamado de cdigo de mquina absoluto.
Caracterizao das linguagens quanto ao nvel de abstrao

certo dizer que qualquer problema pode ser codificado usando qualquer linguagem, afinal
todo e qualquer programa , de fato, um conjunto de instrues dadas em linguagem de mquina;
mas a questo : ser que isso conveniente?
As linguagens de programao podem ser classificadas de acordo com o grau de abstrao
que oferecem, ou seja, o quo permissivas so para que o programador se concentre nos aspectos
essenciais, ignorando caractersticas menos importantes ou mincias operacionais.
7

Unidade I
Desse modo, as linguagens com um alto grau de abstrao, geralmente mais prximo da
linguagem humana, so chamadas de linguagem de alto nvel, enquanto aquelas mais prximas
das caractersticas e cdigo de mquina, em que o conhecimento de detalhes relativos arquitetura
e organizao so fundamentais, so ditas linguagens de baixo nvel.
Identificao e diferenciao dos elementos do domnio

No contexto de compiladores, importante compreender o que os diferentes termos do


domnio significam. Embora cumprindo tarefas parecidas, os compiladores, tradutores, montadores
e interpretadores so elementos diferentes.

Compiladores: traduzem programas codificados em linguagens de alto nvel para linguagem de


baixo nvel (ex.: C .EXE, Java Bytecode ou C# MSIL Microsoft Intermediate Language).
Tradutores: traduzem programas codificados em linguagens de alto nvel para linguagem de
alto nvel (ex.: Portugus Ingls ou C# Java).
Montadores: traduzem programas codificados em linguagens de baixo nvel (simblica) para
linguagem de baixo nvel (ex.: Assembly .EXE).
Interpretadores: diferenciamse dos demais por realizarem a traduo do cdigofonte e
tambm executlo (ex.: Browser HTML, Bash (Linux) Scripts).
Ferramentas de apoio

Tm por objetivo aumentar a produtividade no processo de desenvolvimento. Podemos citar


como exemplos:

Interfaces Integradas de Desenvolvimento (IDEs): costumeiramente oferecem diversos recursos,


mas principalmente auxiliam no gerenciamento de arquivos relativos ao projeto, permitem a
execuo e depurao da aplicao sem que seja necessrio deixar o ambiente de desenvolvimento,
o complemento de comandos e o realce de sintaxe.
Depuradores (debuggers): permitem a execuo passo a passo do cdigo, o acompanhamento
do valor de variveis e contedos de memria durante a execuo, a insero de pontos especficos
de parada, entre outros.
Editores grficos de interface: muito teis em projetos que tenham janelas e outros elementos
grficos; habitualmente permitem a insero e o posicionamento de componentes usando o mouse.
O modelo de Anlise e Sntese para a construo de compiladores

Este modelo se fundamenta em dividir o processo em duas grandes fases: a anlise, em


que o cdigofonte ser verificado e um modelo representativo de sua estrutura e significados
8

Compiladores e Computabilidade
construdo; e a sntese, em que o cdigo correspondente traduo ser efetivamente
produzido de acordo.
A anlise lxica (ou scanner) a primeira etapa do processo de compilao e sua funo varrer
o cdigofonte, caractere por caractere, compondo os elementos que de fato formam o programa
e que so chamados de tokens ou lexemas. Adicionalmente, elementos tais como espaos em
branco, tabulaes, marcas de formatao de texto, comentrios ou quaisquer outros que sejam
irrelevantes ao processo de compilao so eliminados. Desse modo, dizemos que o analisador
lxico responsvel por transformar o fluxo de caracteres em um fluxo de tokens.
Garantida a produo de uma sequncia coerente de lexemas, precisamos ainda verificar a
adequao da estrutura gramatical do programa. A prxima subfase a anlise sinttica, em que se
busca determinar se uma sequncia de smbolos lxicos (cadeia) pode ser gerada pela gramtica que
define a linguagem em questo. Essa tarefa consiste em construir a rvore de derivao correspondente.
Resta ainda verificar o significado ou a semntica do programa, tarefa executada pela ltima
etapa da anlise, a chamada anlise semntica. de sua responsabilidade verificar certos aspectos
que no foram investigados anteriormente por impossibilidade ou inadequao de propsito, tais
como questes relativas a escopo, regras de visibilidade e compatibilidade entre tipos.
Uma vez garantido que o cdigofonte vlido e coerente, podese passar ao processo de
sntese. durante a gerao de cdigo que efetivamente ocorre a produo do cdigo equivalente
ao programa original. Dado o nvel de dificuldade inerente diferena entre os nveis de abstrao,
uma primeira tentativa pode ser realizada e seu produto passa por uma fase de otimizao, cujo
objetivo melhorar o cdigo gerado e eliminar redundncias, aumentando o desempenho.
Leitura recomendada:

Captulo 1, principalmente as sees 1.1 a 1.4 do livro Compiladores: princpios, tcnicas e


ferramentas (Livro do Drago).
Captulo 1 completo do livro Implementao de linguagens de programao: compiladores.
Exerccios resolvidos:

1. Uma discusso tpica entre desenvolvedores apaixonados a de que a linguagem de programao de


sua preferncia a melhor. Um exemplo desses embates ocorreu em certa ocasio, quando um desenvolvedor
Java e um programador C trocavam insultos a respeito da linguagem predileta do outro, dizendo:
C uma porcaria! Se quiser portar uma aplicao, vai ter que arrumar o cdigofonte e
recompilar... isso, se no der problema!
Java lento, pois uma linguagem interpretada. Nunca se compara com o desempenho de
um cdigo compilado!
9

Unidade I
a) Em que aspectos os comentrios podem ser verdadeiros?

Resp.: Compiladores traduzem o cdigofonte para uma linguagemalvo, especfica de uma


plataforma. Desse modo, se quiser mudar para uma plataforma diferente, necessrio recompilar
o cdigofonte considerando a nova arquitetura. Nesse processo, algumas bibliotecas especficas
podem no ter sido portadas ou no haver uma verso similar que a substitua na nova plataforma,
causando aborrecimentos ou at mesmo inviabilizando todo o processo.
A linguagem Java um bom exemplo de tcnica hbrida, pois os cdigosfontes escritos em Java
so compilados tendo como linguagemalvo os bytecodes da mquina virtual. Quando executados,
os programas esto interpretados pela mquina virtual e, dessa forma, realizam um processo de
traduo dos bytecodes do programa para as instrues da plataforma utilizada naquele momento.
certo que o tempo adicional gasto com a interpretao no seria necessrio se o programa fosse
compilado diretamente para a plataforma de destino.
b) De que forma, a linguagem preferida por cada um deles poderia ser considerada como melhor em
relao caracterstica que o interlocutor apontava como uma deficincia na linguagem de seu desafeto?

Resp.: Um programa em Java sempre executado na mquina virtual. A portabilidade se d no pelo


programa em si, mas pela mquina virtual, que responsvel por interpretar os bytecodes nas instrues
especficas daquela plataforma. Portanto, supostamente onde houver uma verso da mquina virtual
implementada possvel executar aquele mesmo programa sem ter de compillo novamente.
O fato de um programa ser compilado uma nica vez permite que seja executado inmeras vezes sem
que nenhum processo de traduo acontea. Mas, se em todas as vezes que for executado tiver de passar
por mais uma etapa, a de interpretao, certo que consumir algum tempo nessa tarefa adicional.
2. Conceitue cada um dos elementos dados a seguir:

a) Linguagemfonte e objeto;

Resp.: Linguagemfonte a linguagem usada para escrever programas que so entrada de


processadores de linguagens. Linguagemobjeto a linguagem usada para escrever programas que
so sada de processadores de linguagens.
b) Linguagem de alto nvel e de baixo nvel;

Resp.: Linguagem de alto nvel a linguagem mais prxima da linguagem natural e apresenta
como principais caractersticas portabilidade, segurana, legibilidade e uso de abstraes. Linguagem
de baixo nvel a linguagem mais prxima do hardware e apresenta como principais caractersticas
dependncia da arquitetura, baixa legibilidade, baixa segurana e pouco ou nenhum suporte para
o uso de abstraes.

10

Compiladores e Computabilidade
c) Linguagem de montagem e de mquina;

Resp.: Linguagem de montagem e linguagem de mquina so ambas linguagens de baixo


nvel e, por isso, compartilham praticamente as mesmas caractersticas. A nica diferena
que a linguagem de montagem utiliza mnemnicos para melhorar um pouco a legibilidade dos
programas, ao passo que a linguagem de mquina utiliza apenas os cdigos numricos, que so
interpretados pela mquinaalvo diretamente.
d) Discorra sobre as relaes que existem entre todos esses tipos de linguagens.

Resp.: Linguagensfonte e linguagensobjeto podem ser tanto de alto quanto de baixo nvel.
Conforme a particular combinao, nomeiase o processador de linguagens como compilador,
tradutor, filtro, montador etc. Linguagens de alto nvel e de baixo nvel podem ser tanto
linguagensfonte quanto linguagensobjeto, dependendo de serem, respectivamente, entrada ou
sada de processadores de linguagens.
Anlise Lxica

Antes de tentar converter o algoritmo em cdigo de mquina, devese analisar o cdigofonte


para que seja possvel identificar os elementos que o compem e assegurar que esteja correto,
tanto em termos estruturais quanto semanticamente.

Cabe ao analisador lxico separar e identificar os elementos vlidos que compem o


programafonte, atravs do processamento individual dos caracteres do arquivo de entrada.
Competelhe ainda a tarefa de eliminar os chamados elementos decorativos do programa, isto ,
caracteres tais como espaos em branco, marcas de formatao de texto e comentrios.
Isso significa que, concluda a anlise lxica, os elementos utilizados para a escrita do programa, tais como
identificadores, operadores, delimitadores e palavras reservadas, tero sido reconhecidos e identificados.
Tarefas que lhe so pertinentes:

Esquadrinhar o cdigofonte, smbolo a smbolo, compondo tokens e classificandoos (segundo


seu significado para a linguagem);
Compor e gerenciar a chamada Lista de Tokens, uma relao de todos os elementos
identificados pelo scanning em uma lista linear;
Eliminar elementos desnecessrios ao processo de compilao, tais como comentrios e
smbolos decorativos;
11

Unidade I
Reconhecer e validar sequncias numricas, quer sejam inteiros, reais ou outra base suportada
pela linguagem (e.g.: hexadecimal);
Reconhecer e validar elementos de definio pelo programador e utilizados como
identificadores;
Prover um mecanismo para controle de erros amigvel, haja vista que o analisador lxico
quem varre o cdigofonte e, portanto, o nico que tem referncia da localidade em que um
determinado token ocorre (e.g.: mensagem de erro relativa ao elemento E da linha N).
Como fazlo?

Os tokens possuem uma estrutura sinttica e, desse modo, podemos descrever cada um dos
itens da linguagem (palavras reservadas, operadores, delimitadores etc.) atravs de regras de
produo, tais como:
identif = letra {letra|dgito}
nmero = dgito {dgito}
if = if
igual = ==
Pensando nisso, inevitavelmente surge a pergunta: por que o analisador lxico no uma parte
do analisador sinttico?
Observe alguns aspectos que justificam essa diviso de fases:
1. Isso deixaria o analisador sinttico mais complicado de ser construdo, a citar a dificuldade
para distinguir palavras reservadas e identificadores. Por exemplo, uma regra sinttica que poderia
ser escrita assim:
Statement ::= ident = Expr ; |
if ( Expr )...
precisaria ser reescrita desta forma, para que fosse possvel tratar as duas tarefas simultaneamente:
Statement ::= i ( f ( Expr )... | notF {letter | digit} = Expr ; ) |
notI {letter | digit} = Expr ;.

12

Compiladores e Computabilidade
2. O scanning deve eliminar brancos, tabulaes, fins de linha e comentrios. Considerando que
esses caracteres podem ocorrer em qualquer lugar do cdigo, teramos de especificar gramticas
mais complexas. Exemplo:
Statement ::= if {Blank} ( {Blank} Expr {Blank} ) {Blank}....
Blank ::= | \r | \n | \t | Comment.
3. Tokens podem ser descritos por gramticas regulares, que so mais simples e mais eficientes
que as gramticas livres de contexto. A maioria das estruturas lxicas so regulares:
Nomes ::= letra { letra | dgito }
Nmeros ::= dgito { dgito }
Strings ::= \ { qqCaractereExcetoAspas } \
Palavras reservadas ::= letra { letra }
Operadores ::= > | = | + |...
4. Gramticas regulares no podem lidar com estruturas aninhadas, pois no so capazes de manipular
recurso central. Esse tipo de construo importante na maioria das linguagens de programao.
Expresses aninhadas Expr ... ( Expr )...
Comandos aninhados Comando do Comando while ( Expr )
Classes aninhadas

Classe class {... Classe... }

Os autmatos finitos podem reconhecer linguagens regulares. Assim, podemos definir o scanner
como um AFD que reconhea os elementos da linguagemfonte.
O processamento de cada caractere obtido ser regido pelas transies previstas pelo autmato,
de modo que o AFD ter reconhecido uma sentena:
Se a entrada tiver sido consumida totalmente; ou
Se no for possvel realizar uma transio com o prximo smbolo da entrada e o autmato
encontrarse em um estado final.
Veja um exemplo no esquema a seguir:
13

Unidade I

Leitura recomendada:

Captulo 1, principalmente as sees 1.1 a 1.4 do livro Compiladores: princpios, tcnicas e


ferramentas (Livro do Drago).
Captulo 1 completo do livro Implementao de linguagens de programao: compiladores.
Exerccio resolvido:

1) Em meio a um cdigofonte, h elementos considerados desnecessrios ao processo de


compilao, tais como espaos em branco e quebras de linha, entre outros. Se os espaos em
branco so necessrios para que se possa separar uma palavra de outra, ento de que modo so
considerados desnecessrios?

Resp.: Apenas um caractere de espao necessrio para separar uma palavra de outra, ento
ocorrncias consecutivas so desnecessrias para esse propsito. Mas at mesmo esse nico espao
perde sua utilidade aps a identificao do trmino do token em formao e, consequentemente,
tambm pode ser descartado aps cumprir com o seu propsito.
Anlise Sinttica (Introduo)
Viso geral do processo

Tendo a funo de verificar se as construes utilizadas no cdigofonte esto na forma


apropriada, a anlise sinttica tem a responsabilidade de:

14

Compiladores e Computabilidade
Dada uma gramtica livre de contexto G e uma sentena s, aqui representada pelo
programafonte, o analisador sinttico tem o propsito de verificar se s pertence linguagem
gerada por G. Em outras palavras, a partir dos tokens fornecidos pelo analisador lxico, tentar
construir a rvore de derivao para s segundo as regras de produo dadas pela gramtica G. Se
esta tarefa for possvel, o programa considerado sintaticamente correto.
Tarefas que lhe so pertinentes:

> Comprovar que a sequncia de tokens cumpre as regras gramaticais; e


> Gerar a rvore gramatical do programa.
Assim, entendemos que so de sua competncia:
A identificao de erros de sintaxe, por exemplo: A * / B;
A correta interpretao da estrutura hierrquica e a evoluo da sentena, mesmo quando sob
aspectos implcitos (ex.: A / B * C ser entendida como (A/B) * C em Fortran e A / (B*C) em APL);
A capacidade de tratamento e/ou recuperao de erros de sintaxe. Vale ressaltar, em relao a esse
aspecto, o projeto de oferecer recursos suficientes; mas tambm tenha o cuidado de no retardar
de forma significativa o processamento de programas corretos.
H claras vantagens na utilizao de gramticas para especificar linguagens de programao e
projetar compiladores, a citar:
Especificaes sintticas precisas de linguagens;
Possibilidade de uso de ferramentas para a gerao automtica do parser;
Possibilidade de identificar ambiguidades durante o processo de especificao/construo;
Facilidades para ampliar ou modificar a linguagem.
Tratamento/Recuperao de erros

Em geral, programas contm erros. Assim, um bom compilador deve, na medida do possvel,
detectar todos os erros ocorridos, avisar o usurio de suas ocorrncias e recuperarse deles, de
modo que seja possvel analisar o restante do cdigofonte.
A implementao de um bom mecanismo de deteco e recuperao de erros muitas vezes
depende de a questo ser considerada desde o incio do projeto da linguagem.
Modo pnico ou desespero
Param imediatamente diante do primeiro erro ou, identificado um erro, o analisador sinttico
descarta smbolos de entrada, at que seja encontrado um token pertencente ao subconjunto de
15

Unidade I
tokens de sincronizao (e.g.: ponto e vrgula, delimitadores etc.).
Recuperao de frases
Ao descobrir um erro, o analisador sinttico pode tentar uma recuperao realizando uma
correo local na entrada restante, substituindoa por alguma cadeia que permita que a anlise
prossiga. Por exemplo, substituir uma vrgula inadequada por um ponto e vrgula, ou ainda remover
um sinal de : excedente.
Produes de erro
Modificar a gramtica incluindo regras de produo estratgicas, de modo a acomodar os erros
mais comuns. Assim, sempre que uma produo de erro for identificada pelo analisador, possvel
conduzir o tratamento mais adequado quela situao em especial.
Correo global
Em geral, emprega algoritmos que escolhem, entre possveis solues, aquela que apresenta
uma sequncia mnima de mudanas para que se obtenha uma correo global do programa, e
no dos erros individualmente.
Alguns dados interessantes que devem ser considerados quando se pensa em quo eficiente
deve ser o mtodo de tratamento e recuperao de erros versus o tempo demandado para que
opere de modo satisfatrio:
Cerca de 60% dos programas so compilados de modo correto sinttica e semanticamente. Entre
os que apresentam erros: cerca de 80% dos enunciados apresentam apenas um erro e 13% apresentam
apenas dois erros. Em aproximadamente 90% dos casos, os erros envolvem um nico token.
Anlise sinttica descendente e ascendente

Os mtodos de anlise sinttica podem ser classificados segundo a maneira pela qual a rvore
de derivao da cadeia analisada x construda:
Nos mtodos descendentes, a rvore de derivao correspondente a x construda de cima para
baixo, ou seja, da raiz (o smbolo inicial S) para as folhas, onde se encontra x.
Nos mtodos ascendentes, a rvore de derivao correspondente a x construda de baixo para
cima, ou seja, das folhas, onde se encontra x, para a raiz, onde se encontra o smbolo inicial S.
Nos mtodos descendentes (topdown), temos de decidir qual a regra A a ser aplicada a
um n rotulado por um no terminal A. A expanso de A feita criando ns filhos rotulados com
os smbolos de .
Nos mtodos ascendentes (bottomup), temos de decidir quando a regra A deve ser aplicada
e devemos encontrar ns vizinhos rotulados com os smbolos de . A reduo pela regra A
consiste em acrescentar rvore um n A, cujos filhos so os ns correspondentes aos smbolos de .
16

Compiladores e Computabilidade
Mtodos descendentes e ascendentes constroem a rvore da esquerda para a direita. A razo para isso
que a escolha das regras deve se basear na cadeia a ser gerada, que lida da esquerda para a direita (seria
muito estranho um compilador que comeasse a partir do fim do programa em direo ao incio).
Exemplo:
Considere a cadeia x = a + a * a e a gramtica:
1.

EE+T

2.

ET

3.

TT*F

4.

TF

5.

F(E)

6.

Fa

Usandose um mtodo descendente, a rvore de derivao de x construda na sequncia


especificada conforme dado na figura abaixo:

17

Unidade I
Note que as regras so consideradas na ordem 1 2 4 6 3 4 6 6, a mesma ordem em que as regras
so usadas na derivao esquerda:
E E+T T+T a+T a+T*F a+F*F a+a*F a+a*a
Porm, se usarmos um mtodo de anlise ascendente, as regras so identificadas na ordem 6 4
2 6 4 6 3 1, e os passos de construo da rvore podem ser vistos na figura a seguir:

Embora a rvore de derivao seja usada para descrever os mtodos de anlise, na prtica ela
nunca efetivamente construda. s vezes, se necessrio, construmos rvores sintticas, que
guardam alguma semelhana com a rvore de derivao, mas ocupam um espao de memria
significativamente menor. A nica estrutura de dados necessria para o processo de anlise uma
pilha, que guarda informao sobre os ns da rvore de derivao relevantes em cada fase do processo.
No caso da anlise descendente, os ns relevantes so aqueles ainda no expandidos; no caso
da anlise ascendente, so as razes das rvores que ainda no foram reunidas em rvores maiores.
Tipos de Analisadores Sintticos:

Mtodos de CockeYoungerKasami e Early


universais: servem para qualquer gramtica (bem eficientes).
Mtodos descendentes (topdown): constroem a rvore sinttica de cima para baixo
Analisadores Descendentes Recursivos
Analisadores LL(k)
18

Compiladores e Computabilidade
Mtodos ascendentes (bottomup): constroem a rvore sinttica de baixo para cima
Analisadores SR
Analisadores LR
Analisadores LALR
Leitura recomendada:

Captulo 4, sees 4.1 e 4.2 do livro Compiladores: princpios, tcnicas e ferramentas (Livro do Drago).
Captulo 3, seo 3.1 do livro Implementao de linguagens de programao: compiladores.
Exerccios resolvidos:

1. Conceitue cada um dos itens dados a seguir:


Anlise sinttica determinstica x no determinstica;
Anlise sinttica descendente x ascendente;
Gramtica e linguagem LL(1) e LR(1).

Resp.: A anlise determinstica aquela em que todas as decises de movimentao do


reconhecedor so tomadas sem arrependimento. Se no houver movimentao possvel, isso
indica a ocorrncia de um erro na cadeia de entrada. A anlise no determinstica, porm, opera
por tentativa e erro. Erros na cadeia de entrada so apontados apenas depois de esgotadas todas
as possibilidades de movimentao.
A anlise descendente, tambm conhecida como topdown, aquela em que o reconhecedor
inicia os seus movimentos na raiz da gramtica e evolui at a cadeia de entrada (em termos
de rvore, aquela em que a rvore montada de cima para baixo). A anlise ascendente, ou
bottomup, aquela em que os movimentos do reconhecedor vo da cadeia entrada em direo
raiz da gramtica (a rvore montada de baixo para cima).
Uma gramtica LL(1) aquela que gera uma linguagem que pode ser reconhecida da esquerda
para a direita, usando derivaes mais esquerda e com lookahead de apenas um smbolo.
Linguagem LL(1) aquela que pode ser gerada por alguma gramtica LL(1). Gramtica LR(1)
aquela que gera uma linguagem que pode ser reconhecida da esquerda para a direita, usando
redues mais esquerda e com lookahead de apenas um smbolo. Linguagem LR(1) aquela que
pode ser gerada por alguma gramtica LR(1).

19

Unidade I
2. Considere a gramtica abaixo, sobre o alfabeto = {(, ), a, ,}: L (S)
S I,S | I
Ia|L
a) Mostre os movimentos de um reconhecedor ascendente na anlise da sentena (a, (a), (a,a));
b) Mostre os movimentos de um reconhecedor descendente na anlise da sentena (a, (a), (a,a));
c) Obtenha o esboo de um reconhecedor recursivo descendente para a linguagem por ela definida.

Resp. item a: Os movimentos de um reconhecedor ascendente sero:


(a,(a),(a,a)) (I,(a),(a,a)) (I,(I),(a,a)) (I,(S),(a,a))
(I,L,(a,a)) (I,I,(a,a)) (I,I,(I,a)) (I,I,(I,I)) (I,

I,(I,S)) (I,I,(S)) (I,I,L) (I,I,I) (I,I,S) (I,S)


(S) L

Resp. item b: Os movimentos para o reconhecedor descendente sero:


L (S) (I,S) (a,S) (a,I,S) (a,L,S) (a,(S),S) (a,(I),S) (a,(a),S) (a,(a),I) (a,(a),L)
(a,(a),(S)) (a,(a),(I,S)) (a,(a),(a,S)) (a,(a),(a,I)) (a,(a),(a,a))
Resp. item c: Um reconhecedor recursivo obtido transcrevendo cada uma das regras de
produo da gramtica como uma subrotina responsvel por consumir os tokens da cadeia gerada.
Sempre que encontrarmos um smbolo no terminal na sentena, realizase a chamada subrotina
correspondente; e, quando encontramos um smbolo terminal na regra, verificamos se corresponde
ao smbolo dado na entrada (posio corrente de anlise de cdigofonte). De acordo com esses
conceitos, poderamos ter o seguinte cdigo para esta gramtica:

20

Compiladores e Computabilidade
Anlise Sinttica I Anlise Descendente

A representao do processo ser feita atravs de configuraes (, y), em que e y representam,


respectivamente, o contedo da pilha e o resto da entrada ainda no analisada. Por conveno, vamos
supor que o topo da pilha fica esquerda, isto , que o primeiro smbolo de o smbolo do topo da pilha.
Existem duas formas de transio de uma configurao para outra:
(1) expanso de um no terminal pela regra A: permite passar da configurao (A, y) para
a configurao (, y).
(2) verificao de um terminal s: permite passar da configurao (s, sy) para a configurao (, y).
O segundo tipo de transio serve para retirar terminais do topo da pilha e nele expor o prximo
no terminal a ser expandido. A configurao inicial para a entrada x (S, x); o processo termina na
configurao (, ), com a pilha vazia e a entrada toda considerada.
Exemplo: Mostramos abaixo as configuraes sucessivas de um analisador descendente, para
a cadeia x. Para acompanhamento, a terceira coluna apresenta os passos correspondentes da
derivao esquerda de x. Note que apenas os passos de expanso tm correspondente na derivao.

Anlise Sinttica LL(1)

Neste mtodo, a escolha da regra a ser usada durante o processo de anlise descendente se d atravs
de duas informaes: o no terminal A a ser expandido e o primeiro smbolo a do resto da entrada.
21

Unidade I
Uma tabela M indexada por estas duas entradas nos fornece a regra a ser utilizada: M[A, a].
Vale ressaltar que essa tcnica s pode ser empregada para uma classe restrita de gramticas, as
chamadas gramticas LL(1).
O nome LL(1) indica que:
A cadeia de entrada examinada da esquerda para a direita (L=lefttoright);
O analisador procura construir uma derivao esquerda (L=leftmost);
Exatamente 1 smbolo do resto da entrada examinado.
Exemplo Suponha a gramtica a seguir:
1. E T E
2. T F T
3. F ( E )
4. F a
5. E + T E
6. E
7. T * F T
8. T
Essa gramtica LL(1) e, assim, a sua tabela de anlise M ser:
(

22

1 1

2 2

3 4

5 6

Compiladores e Computabilidade
Nessa tabela, a entrada M[S, k] correspondente ao no terminal S e ao terminal k tem o nmero
da regra que deve ser usada para expanso de S. As entradas indicadas por correspondem a
erros, ou seja, so combinaes que no podem ocorrer durante a anlise de cadeias da linguagem.
Para analisar a cadeia a + a * a, teremos as seguintes configuraes:
Pilha
E

Entrada
a+a*a

Escolha da regra
M[E, a] = 1

TE
FTE
aTE
TE

a+a*a
a+a*a
a+a*a
+a*a

M[T, a] = 2
M[F, a] = 4

M[T, +] = 8

E
+TE
TE
FTE
aTE
TE
*FTE
FTE
aTE
TE
E

+a*a
+a*a
a*a
a*a
a*a
*a
*a
A
A

M[E, +] = 5

M[T, a] = 2
M[F, a] = 4

M[T, *] = 7

M[F, a] = 4

M[T, $] = 8
M[E, $] = 6

Vamos, agora, mostrar como construir M, a tabela de anlise LL(1). No caso mais simples, o
smbolo a (o primeiro do resto da entrada) o primeiro smbolo derivado do no terminal a ser
expandido A e faz parte de First(A). Nesse caso, a deve pertencer a First(), em que A uma
das alternativas de regra para A. Como ilustrao, podemos ver que, no exemplo acima, a regra F
(E) foi usada com o smbolo (.
Outra possibilidade a de que no seja A o no terminal responsvel pela gerao do smbolo a, mas sim
algum outro no terminal encontrado depois de A. Neste caso, devemos ter a pertencendo ao Follow(A).
Como ilustrao, podemos ver que, no exemplo acima, a regra T foi usada com o smbolo +.
Para construir a tabela M, vamos examinar todas as regras da gramtica:
Para cada regra i: A, temos M[A, a]=i, para cada a em First().
Para cada regra i: A, se *, temos M[A, a]=i, para cada a em Follow(A).
Se a gramtica LL(1), cada entrada de M receber no mximo um valor. As entradas de M que
no receberem nenhum valor devem ser marcadas como entradas de erro. Caso alguma entrada de
23

Unidade I
M seja definida mais de uma vez, dizemos que houve um conflito e que a gramtica no LL(1).
Exemplo Considere a gramtica dada no exemplo anterior. Essa gramtica LL(1), e a tabela
de anlise M pode ser construda como indicado. Temos, para cada regra:
1. E TE
2. T FT
3. F (E)
4. F a
5. E +TE
6. E
7. T *FT
8. T

First(TE) = { (, a }
First(FT) = { (, a }
First((E)) ={ ( }
First(a) = { a }
First(+TE) = { + }
Follow(E) = { $, ) }
First(*FT) = { *}
Follow(T) = { $, +, )}

M[E, (] = 1 e M[E, a] = 1
M[T, (] = 2 e M[T, a] = 2
M[F, (] = 3
M[F, a] = 4
M[E, +] = 5
M[E, $] = 6 e M[E, )] = 6
M[T,* ] = 7
M[T, $] = 8, M[T, +] = 8 e M[T, )] = 8

Exemplo Considere a gramtica dada abaixo, esquerda. Teremos:


1. E E+T
2. E T
3. T T*F
4. T F
5. F (E)
6. F a

First(E+T)= { (, a }
First(T)= { (, a }
First(T?F)= { (, a }
First(F)= { (, a }
First((E))= { ( }
First(a)= { a }

M[E, (] = 1 e M[E, a] = 1
M[E, (] = 2 e M[E, a] = 2
M[T, (] = 3 e M[T, a] = 3
M[T, (] = 4 e M[T, a] = 4
M[F, (] = 5
M[F, a] = 6

Consequentemente, a gramtica no LL(1), por causa dos conflitos (mltiplas definies)


para M[E, (], M[E, a], M[T, (] e M[T, a]. Em alguns casos, como o da gramtica do exemplo anterior,
possvel concluir que a gramtica no LL(1) por inspeo. As duas caractersticas mais bvias so:
a recurso esquerda e a possibilidade de fatorao.
Recurso esquerda:

Se uma gramtica permite uma derivao A * A, para algum no terminal A e para alguma
cadeia no vazia , a gramtica dita recursiva esquerda.
No caso mais simples, existe na gramtica uma regra AA responsvel diretamente pela
derivao mencionada. Para que A no seja um no terminal intil, deve existir na gramtica (pelo
menos) uma regra da forma A, sem recurso esquerda.
A combinao dessas duas regras faz com que First(A) e, portanto, First(A) contenham todos
os smbolos de First(), e isso leva necessariamente a um conflito. A eliminao da recurso
esquerda pode ser tentada, procurando transformar a gramtica em uma gramtica LL(1).

24

Compiladores e Computabilidade
Observar que a combinao:
A A
A
permite a gerao de cadeias da forma e que essas mesmas cadeias podem ser
geradas de outra maneira. Por exemplo:
A A
A A
A
Essa outra forma de gerao usa recurso direita, que no cria problemas.
Prefixos comuns

Tratase do caso em que duas regras comeam com o mesmo smbolo ou conjunto de smbolos
(prefixo), isto , regras como A e A, com First(). Neste caso, existe uma interseo
entre First() e First(), e a gramtica no pode ser LL(1).
Isso acontece porque no possvel decidir, olhando apenas o primeiro smbolo derivado de ,
qual a regra correta. A soluo simples e envolve a fatorao.
Suponha as regras abaixo:
A
A
Podemos reescrever essas regras em equivalentes, de modo a colocar o prefixo em uma regra e
adiar a deciso entre e para quando o primeiro smbolo derivado de ou de estiver visvel.
A A
A
A
Essas duas tcnicas permitem transformar algumas gramticas em gramticas LL(1). Entretanto,
vale ressaltar que algumas gramticas livres de contexto GLC no tm gramticas equivalentes
LL(1). Nesse caso, a aplicao dessas tcnicas ou mesmo de outras poder no ter sucesso.
25

Unidade I
Exemplo Considere a gramtica a seguir:
1. E E + T | T
2. T T * F | F
3. F ( E ) | a
Essa gramtica apresenta duas situaes de recurso esquerda. Fazendo as substituies
indicadas, temos:
1. E T E
2. E + T E |
3. T F T
4. T * F T |
5. F ( E ) | a
E essa gramtica LL(1), como j foi visto anteriormente.
Exemplo Suponha transformar a gramtica abaixo em uma gramtica LL(1) equivalente:
1. L L ; S | S
2. S if E th L el L fi | if E th L fi | s
3. E e
Temos recurso esquerda nas regras de L e possibilidade de fatorao nas regras de S. Uma
transformao dessa gramtica em LL(1) teria o seguinte resultado:
1. L SL
2. L ; S L |
3. S if E th L S | s
4. S el L fi | fi
5. E e
Novamente, podemos verificar que essa uma gramtica LL(1).
26

Compiladores e Computabilidade
Leitura recomendada:

Captulo 4, seo 4.4 do livro Compiladores: princpios, tcnicas e ferramentas (Livro do Drago).
Captulo 3, seo 3.2 do livro Implementao de linguagens de programao: compiladores.
Exerccios resolvidos:
Considere a gramtica abaixo:
S aXb | aYc | aaZd
X bX | bc
Y cY | cb
Z dZ |
a) Essa gramtica LL(1)? Prove a sua resposta.
b) Caso a gramtica acima no seja LL(1), obtenha uma gramtica equivalente que seja LL(1) e
prove que a nova gramtica , de fato, LL(1).

Resp. item a: No, pois os conjuntos First no so disjuntos. Em todas as regras encontramos prefixos
iguais para as possveis derivaes de cada regra, quer de maneira direta ou indireta. Note que isso se
reflete diretamente nos valores includos nos conjuntos First e Follow, conforme dados abaixo:
First (aXb) = {a}, First (aYc) = {a}, First (aaZd) = {a}
First (bX) = {b}, First (bc) = {b}
First (cY) = {c}, First (cb) = {c}
First (dZ) = {d}, follow (Z) = {d}
Resp. item b: Eliminando recurses e fazendo substituies dos smbolos nas construes dadas
por S, teremos: S ab*bcb | ac*cbc | aad*d. Fatorando as trs diferentes possibilidades de derivao
para o smbolo S, ficaramos com a seguinte regra: S a(b*bcb | c*cbc | ad*d)
possvel observar que essa gramtica no apresenta recurses esquerda, uma vez que
deriva diretamente de sentenas compostas apenas de smbolos terminais. Alm disso, tambm
se nota que os prefixos para cada uma das possveis derivaes aps o primeiro smbolo a so
diferentes para os trs casos possveis. Esses aspectos podem ser confirmados quando calculamos
os conjuntos First para cada uma das trs possibilidades de derivao apresentadas para o smbolo
27

Unidade I
S: First (b*bcb) = {b}; First (c*cbc) = {c} e First (aad*d) = {a}
2. Obtenha o esboo de um reconhecedor, atravs do mtodo recursivo descendente, para a
linguagem definida pela expresso:
(+||)(d+(|.|.d*)|.d+)(e(+||)d+|)
So exemplos de sentenas pertencentes a essa linguagem: 123, 45.312, +.76, 5.44e2,
+0.88e35.

Resp.: Um reconhecedor recursivo transcreve a regra de produo como uma subrotina


responsvel por consumir um de seus tokens na ordem em que so dados. Sempre que encontrarmos
um smbolo que pode ou no ocorrer na sentena, devemos ter o cuidado para no reportar um
erro indevidamente. Assim, para a expresso dada, poderamos ter o seguinte cdigo:

28

S-ar putea să vă placă și