Вы находитесь на странице: 1из 30

Construo de Compiladores

Jos Romildo Malaquias 6 de abril de 2011

Sumrio

Representando programas como dados 1.1 Introduo . . . . . . . . . . . . . . . . . . . . 1.2 Uma linguagem de programao muito simples 1.3 Estruturas de dados . . . . . . . . . . . . . . . 1.4 Convertendo para rvore de strings . . . . . . . 1.5 Projeto . . . . . . . . . . . . . . . . . . . . . . 1.6 Processando programas . . . . . . . . . . . . . 1.7 Interpretando programas . . . . . . . . . . . . 1.8 Expandindo a linguagem . . . . . . . . . . . . 1.8.1 Comando condicional . . . . . . . . . 1.8.2 Comando de repetio . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

1-1 1-1 1-1 1-3 1-7 1-9 1-10 1-10 1-10 1-11 1-11 2-1 2-1 2-1 2-2 2-2 2-2 2-3 2-4 2-7 2-9 2-10 2-11

Anlise Lxica 2.1 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Analisador lxico ad hoc . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Geradores de analisadores lxicos . . . . . . . . . . . . . . . . . . . . 2.4 JFlex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Instalao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Executando o JFlex . . . . . . . . . . . . . . . . . . . . . . . . 2.4.3 Exemplo: analisador simples . . . . . . . . . . . . . . . . . . . 2.4.4 Exemplo: usando uma classe para representar os smbols lxicos 2.4.5 Exemplo: denio de expresso regular . . . . . . . . . . . . 2.4.6 Exemplo: estados . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Analisador lxico para a linguagem Tiger . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

Anlise sinttica descendente recursiva 3-1 3.1 Expresses aritmticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-1 Anlise sinttica de Tiger 4-1

1
Representando programas como dados

1.1

Introduo

Os processadores de linguagem sempre fazem alguma manipulao com programas. Normalmente a entrada para o processador um programa apresentado na forma de uma sequncia de caracteres (texto). Esta sequncia de caracteres deve ser convertida para uma representao intermediria que reete a estrutura do programa, tornando o seu processamento mais fcil. Frequentemente a representao intermediria do programa feita usando rvores, com vrios tipos de ns, cada um com diferentes atributos. Neste captulo vamos nos familiarizar com esta representao.

1.2

Uma linguagem de programao muito simples

Vamos trabalhar com uma variao da micro linguagem straight-line1 que chamaremos de Lang. Todos os valores so numricos (inteiros), e a linguagem possui um conjunto reduzido de formas de comandos expresses. A sintaxe de Lang mostrada na gramtica da tabela 1.1. As regras de produo so apresentadas na primeira coluna. A segunda coluna ser discutida na seo 1.3. Esta gramtica ambgua, uma vez que permite mais de uma representao para um programa. No entanto ela ser adotada por ser simples e por no estarmos interessados nos detalhes das anlises lxica e sinttica. A ambiguidade est nas regras de produo que denem expresses com operadores binrios, e pode ser resolvida pela denio de precedncia e associatividade dos operadores, como usual na matemtica. Os operadores relacionais =, !=, >, <, >= e <= tem precedncia menor do que os demais operadores, e no so associativos. Os operadores aritmticos * e / tem a maior precedncia, enquanto que + e - tem precedncia intermediria, e todos eles tem associatividade esquerda. Um exemplo de programa nesta linguagem apresentado na listagem 1.1.
Listagem 1.1: Exemplo de programa em Lang.

begin a := 5+3; b := { print(a, a-1), 10 * a } ; print(b) end A seguir apresentada uma semntica informal da linguagem. Cada Stm um comando que pode ser executado para produzir algum efeito colateral (mudar o valor de alguma varivel ou exibir alguma informao), e cada Exp uma expresso que pode ser avaliada para produzir um valor, e possivelmente algum efeito colateral. Um programa um comando.
1A

linguagem straight-line descrita por Appel ([1]) na gramtica 1.3 no nal do captulo 1.

1-1

1-2

CAPTULO 1. REPRESENTANDO PROGRAMAS COMO DADOS


Tabela 1.1: Gramtica da linguagem de programao Lang.

Regra de produo Stm id := Exp Stm print ( ExpList ) Stm begin StmList end StmList Stm StmListRest StmList StmListRest ; Stm StmListRest StmListRest Exp id Exp num Exp read ( ) Exp Exp Binop Exp Exp { Stm , Exp } Exp ( Exp ) ExpList Exp ExpListRest ExpList ExpListRest , Exp ExpListRest ExpListRest Binop + Binop Binop * Binop / Binop = Binop != Binop > Binop < Binop >= Binop <= Comando de atribuio i := e

Tipo AssignStm PrintStm CompoundStm List<Stm> List<Stm> List<Stm> List<Stm> IdExp NumExp ReadExp OpExp EseqExp Exp List<Exp> List<Exp> List<Exp> List<Exp> OpExp.Op.PLUS OpExp.Op.MINUS OpExp.Op.TIMES OpExp.Op.DIV OpExp.Op.EQ OpExp.Op.NE OpExp.Op.GT OpExp.Op.LT OpExp.Op.GE OpExp.Op.LE

Avalia-se a expresso e, armazenando o seu resultado na varivel i. Comando de impresso print(e1 , . . ., en ) Avaliam-se as expresses e1 , . . ., en da esquerda para a direita, exibindo os resultados, separandoos por espao e terminando com uma mudana de linha. Comando composto begin s1 ; . . .; sn end Executam-se os comandos s1 , . . ., sn em sequncia. Expresso identicador i O valor da expresso o contedo armazenado na varivel i.

1.3. ESTRUTURAS DE DADOS Expresso constante num O valor da expresso a prpria constante numrica num. Expresso de leitura read()

1-3

A avaliao da expresso de leitura feita pela leitura de um nmero inteiro da entrada padro. O nmero lido o resultado da expresso. Expresso operao e1 op e2 Avalia-se e1 , e ento e2 , e o valor da expresso dado pela aplicao da operao op aos resultados. op um dos operadores aritmticos +, -, * e /, ou um dos operadores relacionais =, !=, >, <, >= e <=. No resultado de uma operao relacional, falso indicado pelo valor 0, e verdadeiro indicado pelo valor 1. Expresso sequncia { s, e } Executa-se o comando s, para produo de algum efeito colateral, e em seguida avalia-se a expresso e, para produzir o resultado nal. O pograma da listagem 1.1 produz a seguinte sada quando executado: 8 7 80 Exerccio 1.1. Determine a sada produzida pelo programa da listagem 1.2, quando executado.
Listagem 1.2: Outro exemplo de programa em Lang.

begin lado := 4; area := lado*lado; print(lado, area); volume := { lado := lado + 1, lado*lado*lado }; print(lado,volume) end

1.3

Estruturas de dados

A estrutura de dados mais conveniente para representar o programa uma rvore, com um n para cada comando e expresso. Cada categoria sinttica (correspondente a um smbolo no terminal) representada por um tipo de n especco. Assim cada smbolo no terminal corresponde a uma classe. Se h mais de uma forma para a construo representada pelo smbolo no terminal, esta classe ser abstrata, com subclasses concretas para representar cada forma especca da construo. Para cada regra de produo h um construtor na classe (ou alguma subclasse) correspondente ao smbolo no terminal no lado esquerdo da regra. As categorias sintticas para a linguagem Lang sero representadas como segue. Para representar comandos (classe sinttica Stm) ser utilizada a classe abstrata Stm. Assim todo e qualquer comando ser representado por uma instncia de Stm. Para cada forma especca de comando, denida por uma regra de produo especca, ser utilizada uma subclasse de Stm.

1-4

CAPTULO 1. REPRESENTANDO PROGRAMAS COMO DADOS Para representar expresses (classe sinttica Exp) ser utilizada a classe abstrata Exp. Toda e qualquer expresso ser uma instncia de Exp. Para cada forma especca de expresso ser utilizada uma subclasse de Exp, de acordo com a regra de produo correspondente. Listas de comandos e de expresses sero representados pelas classes List<Stm> e List<Exp>, respectivamente, utilizando a classe genrica List<T> do pacote java.util de Java. Operadores binrios sero representados por instncias da enumerao OpExp.Op.

A tabela 1.2 sintetiza as classes usadas na representao de programas em Lang. Observe tambm que a gramtica apresentada na tabela 1.1 est anotada com os tipos que sero utilizados para representar as construes da linguagem.
Tabela 1.2: Classes utilizadas para representar os programa na linguagem Lang.

Smbolo no terminal Classes Stm Stm AssignStm PrintStm CompoundStm Exp Exp NumExp IdExp ReadExp OpExp EseqExp StmList List<Stm> StmListRest List<Stm> ExpList List<Exp> ExpListRest List<Exp>

Comando de atribuio Comando de impresso Comando composto Expresso constante Expresso identicador Expresso de leitura Expresso operao binria Expresso sequncia

Os componentes que aparecem no lado direito de cada regra de produo e carregam alguma informao so representados como campos (variveis de instncia) na classe correspondente. Por exemplo, a classe AssignStm dever ter um campo (do tipo String) para representar o identicador e outro campo (do tipo Exp) para representar a expresso da atribuio. A gura 1.1 mostra gracamente a rvore que representa o programa da listagem 1.1. A listagem 1.3 apresenta uma implementao das classes necessrias para representar os programas de Lang.
Listagem 1.3: Denies das classes para representar programas em Lang.

import java.util.List; public abstract class Stm { } public class AssignStm extends Stm { public String id; public Exp exp; public AssignStm(String id, Exp exp) { this.id = id;

1.3. ESTRUTURAS DE DADOS


Figura 1.1: Representao de rvore para o programa da listagem 1.1.
CompoundStm

1-5

AssignStm

AssignStm

PrintStm

OpExp

EseqExp

IdExp b

NumExp 5

PLUS

NumExp 3

PrintStm

OpExp

IdExp a

OpExp

NumExp 10

TIMES

IdExp a

IdExp a

MINUS

NumExp 1

this.exp = exp; } } public class PrintStm extends Stm { public List<Exp> expList; public PrintStm(List<Exp> expList) { this.expList = expList; } } public class CompoundStm extends Stm { public List<Stm> stmList; public CompoundStm(List<Stm> stmList) { this.stmList = stmList; } } public abstract class Exp { } public class NumExp extends Exp { public Integer num;

1-6

CAPTULO 1. REPRESENTANDO PROGRAMAS COMO DADOS

public NumExp(Integer num) { this.num = num; } } public class IdExp extends Exp { public String id; public IdExp(String id) { this.id = id; } } public class ReadExp extends Exp { } public class OpExp extends Exp { public enum Op { EQ, NE, GT, GE, LT, LE, PLUS, MINUS, TIMES, DIV; }; public Exp left; public Op oper; public Exp right; public OpExp(Exp left, Op oper, Exp right) { this.left = left; this.oper = oper; this.right = right; } } public class EseqExp extends Exp { public Stm stm; public Exp exp; public EseqExp(Stm stm, Exp exp) { this.stm = stm; this.exp = exp; } } Exerccio 1.2. Represente o programa da listagem 1.4 como uma rvore utilizando as classes apre-

1.4. CONVERTENDO PARA RVORE DE STRINGS sentadas.


Listagem 1.4: Programa em Lang.

1-7

begin k := 2 + 3 * 4 - 1; print(k - 1, k, k + 1) end

1.4

Convertendo para rvore de strings

Durante o desenvolvimento do processador da linguagem desejvel poder visualizar gracamente a rvore que representa o programa. Para tornar isto possvel vamos convert-la para uma rvore de strings, onde os ns da rvore identicam a classe sinttica da estrutura, e as folhas so os componentes da estrutura, como demonstra a gura 1.1. A gura 1.5 exibe a mesma informao formatada no modo texto.
Listagem 1.5: Visualizao da estrutura do programa da listagem 1.1 no modo texto.

CompoundStm +--AssignStm | +--a | +--OpExp | +--NumExp 5 | +--PLUS | +--NumExp 3 +--AssignStm | +--b | +--EseqExp | +--PrintStm | | +--IdExp a | | +--OpExp | | +--IdExp a | | +--MINUS | | +--NumExp 1 | +--OpExp | +--NumExp 10 | +--TIMES | +--IdExp a +--PrintStm +--IdExp b A partir da rvore de strings pode-se obter facilmente uma representao visual da rvore de fcil leitura, tanto em modo texto como em modo grco. A classe Tree apresentada na listagem 1.62 uma classe genrica que permite fazer esta representao. Uma instncia de Tree<E> uma rvore contendo uma informao do tipo E na raiz, e uma lista de sub-rvores do tipo Tree<E>. O mtodo String prettyPrint() retorna uma string que representa a rvore formatada para fcil leitura no modo texto.
listagem est incompleta. Veja a denio completa desta e de outras classes nos arquivos disponibilizados na pgina do curso na internet (http://www.decom.ufop.br/romildo/bcc328.2011-1/).
2A

1-8

CAPTULO 1. REPRESENTANDO PROGRAMAS COMO DADOS O mtodo void graph(Formatter out) insere no objeto de formatao out o texto de um programa para a ferramenta asymptote3 representando a rvore formatada para fcil leitura no modo grco. Pode-se gravar este programa em asymptote em um arquivo texto que, quando compilado, produz uma imagem grca da rvore.
Listagem 1.6: Classe para representao genrica de rvores.

import java.util.Formatter; import java.util.List; public class Tree<E> { public E info; public List<Tree<E>> children; public Tree(E info, List<Tree<E>> children) { this.info = info; this.children = children; } public String prettyPrint() { // implementation details omitted } public void graph(Formatter out) { // implementation details omitted } // private methods omitted } As classes que representam a estrutura do programa (Stm, Exp e demais) devem ter um mtodo Tree<String> toTree() que produz uma representao da estrutura como uma rvore de strings, permitindo assim a sua fcil visualizao. Como exemplo, a listagem 1.7 mostra a denio deste mtodo para a expresso sequncia e para o comando de impresso.
Listagem 1.7: Converso de expresso sequncia e comando de impresso em rvores de strings.

class EseqExp extends Exp { // instance variables and other methods omitted protected Tree<String> toTree() { List<Tree<String>> l = new LinkedList<Tree<String>>(); l.add(stm.toTree()); l.add(exp.toTree()); return new Tree<String>("EseqExp", l); } }
3 Veja

http://www.asymptote.org para maiores detalhes.

1.5. PROJETO public class PrintStm extends Stm { // instance variables and other methods omitted protected Tree<String> toTree() { List<Tree<String>> l = new LinkedList<Tree<String>>(); for (Exp exp : expList) l.add(exp.toTree()); return new Tree<String>("PrintStm", l); } }

1-9

1.5

Projeto

Exerccio 1.3. Utilize os arquivos disponveis em http://www.decom.ufop.br/romildo/bcc328. 2011-1/praticas/ulang-0.1.zip para criar uma aplicao em Java para processamento de programas na linguagem Lang. Esto includas todas as classes mencionadas at o momento neste captulo, alm de outras classes complementares: Classes Stm, AssignStm, PrintStm e CompoundStm para representao de comandos. Classes Exp, NumExp, IdExp, ReadExp, OpExp e EseqExp para representao de expresses. Classe Tree<E> para representao de rvores genricas. Classes Token e Lexer para anlise lxica. Classe Parser para anlise sinttica. Classe Main, onde se encontra a denio do mtodo main. O mtodo main basicamente faz o seguinte: 1. Determina qual ser a programa (entrada) a ser processado. Basicamente verica-se se o usurio forneceu algum argumento na linha de comando. Em caso armativo, este argumento o nome do arquivo contendo o texto do programa em Lang a ser processado, e este arquivo aberto para leitura. Caso contrrio, a entrada feita atravs do dispositivo de entrada padro, dando oportunidade ao usurio de digitar o texto do programa. Neste caso a entrada deve ser encerrada com o caracter que marca m de arquivo (Control-Z no Windows e Control-D no Linux e Unix). 2. Constri uma instncia de Lexer, para fazer anlise lxica utilizando a entrada j denida. 3. Constri uma instncia de Parser, para fazer anlise sinttica, utilizando o analisador lxico para obter os smbolos lxicos. 4. Obtm a representao do programa como uma rvore do tipo Stm, utilizando o mtodo parse do analisador sinttio. 5. Obtm e exibe uma representao da rvore em modo texto usando o mtodo toTree. 6. Grava um arquivo contendo o cdigo em asymptote capaz de gerar uma imagem grca da rvore. Exerccio 1.4. Teste a aplicao com as entradas apresentadas no decorrer do texto.

1-10

CAPTULO 1. REPRESENTANDO PROGRAMAS COMO DADOS

1.6

Processando programas

Como primeiro exemplo de processamento de programas, vamos acrescentar um mtodo para analisar um determinado programa em Lang, procurando por todas as ocorrncias do comando print e contando o nmero de argumentos do mesmo, com o objetivo de determinar o maior nmero de argumentos com que o comando print usado. No podemos esquecer que comandos print podem ocorrer em expresses, pois a expresso sequncia formada por um comando e uma expresso. Exerccio 1.5. Modique a aplicao: 1. Acrescente os mtodos abstratos int maxargs() nas classes Stm e Exp. 2. Implemente estes mtodos em todas as subclasses concretas de Stm e Exp, de acordo com exposto anteriormente. 3. Modique o mtodo Main.main para tambm exibir o resultado de maxargs sobre o programa sendo processado. 4. Testar o mtodo maxargs com os programas dados anteriormente.

1.7

Interpretando programas

Como um exemplo mais til de processamento de programas vamos implementar um interpretador para a linguagem Lang. O interpretador ir executar os comandos e quando necessrio avaliar as expresses que ocorrem nos comandos. A memria ser representada por um mapeamento (da biblioteca padro de Java) de String em Integer. A chave do mapeamento ser o nome da varivel, e o valor associado chave ser o valor da varivel. Exerccio 1.6. Modique a aplicao: 1. Acrescente o mtodo void interp(Map<String,Integer>) classe Stm para interpretar (executar) o comando, e o mtodo Integer eval(Map<String,Integer>) classe Exp, para avaliar a expresso. 2. Implemente os mtodos interp e eval em todas as subclasses concretas de Stm e Exp, respectivamente, de acordo com a semntica da linguagem Lang. Lembre-se que um comando executado para produzir algum efeito colateral (exibir algum valor ou modicar a memria), e uma expresso avaliada para determinar o seu valor (e possivelmente produzir algum efeito colateral, quando em em sua estrutura houver um comando). 3. Modique o mtodo Main.main para tambm interpretar o programa de entrada. 4. Testar o interpretador com os programas dados anteriormente no texto.

1.8

Expandindo a linguagem

Agora vamos introduzir novas construes na linguagem Lang.

1.8. EXPANDINDO A LINGUAGEM

1-11

1.8.1

Comando condicional
Stm if Exp then Stm else Stm

Vamo introduzir o comando condicional na linguagem:

Quando executado, o comando if e then c1 else c2 avalia a expresso e e analisa o resultado. Se o valor de e for verdadeiro, executa o comando c1 e ignora o comando c2 . Caso contrrio, executa o comando c2 e ignora o comando c1 . Um valor considerado verdadeiro se for diferente de zero. Zero considerado falso. Exerccio 1.7. Modique a aplicao para dar suporte ao comando condicional: 1. Acrescente a classe IfStm como uma subclasse concreta de Stm, para representar um comando condicional. A classe IfStm dever ter trs variveis de instncia, correspondente condio e s duas alternativas do comando. O construtor da classe deve inicializar estas variveis de instncia. 2. Na classe Parser remova o comentrio na linha que chama o construtor da classe IfStm e comente a linha seguinte, onde levantada uma exceo. 3. Implemente o mtodo maxargs na classe IfStm. 4. Implemente o mtodo interp na classe IfStm. 5. Implemente o mtodo toTree na classe IfStm. 6. Escreva alguns programas em Lang usando o comando condicional e use-os para testar a aplicao.

1.8.2

Comando de repetio
Stm while Exp do Stm

Vamo introduzir o comando de repetio na linguagem:

Quando executado, o comando while e do c avalia a expresso e e analisa o resultado. Se o valor de e for verdadeiro, executa o comando c e repete a execuo do comando de repetio. Caso contrrio, a execuo do comando de repetio termina. Um valor considerado verdadeiro se for diferente de zero. Zero considerado falso. Exerccio 1.8. Modique a aplicao para dar suporte ao comando de repetio: 1. Acrescente a classe WhileStm como uma subclasse concreta de Stm, para representar um comando de repetio. A classe WhileStm dever ter duas variveis de instncia, correspondente condio e ao corpo da repetio. O construtor da classe deve inicializar estas variveis de instncia. 2. Na classe Parser remova o comentrio na linha que chama o construtor da classe WhileStm e comente a linha seguinte, onde levantada uma exceo. 3. Implemente o mtodo maxargs na classe WhileStm. 4. Implemente o mtodo interp na classe WhileStm. 5. Implemente o mtodo toTree na classe WhileStm.

1-12

CAPTULO 1. REPRESENTANDO PROGRAMAS COMO DADOS

6. Escreva alguns programas em Lang usando o comando de repetio e use-os para testar a aplicao. Exerccio 1.9. Escreva um programa em Lang para calcular o fatorial de um nmero inteiro a ser informado pelo usurio, exibindo o resultado.

2
Anlise Lxica

2.1

Introduo

A anlise lxica a primeira etapa do processo de compilao e seu objetivo dividir o cdigo fonte em smbolos, preparado-o para a anlise sinttica. Neste processo pode-se destacar trs atividades como fundamentais: extrao e classicao dos smbolos lxicos que compem o programa fonte, eliminao de brancos (espaos em branco, tabulao, mudanas de linha) e comentrios, e recuperao de erros lxicos, gerados por sequncias de caracteres que no formam smbolos lxicos. Smbolos lxicos, ou tokens, so as palavras e sinais de pontuao utilizados para expressar a estrutura de um programa em um texto. O analisador lxico, ou scanner, um mdulo do compilador que tem como entrada uma sequncia de caracteres (texto do programa), produzindo na sada uma sequncia de smbolos lxicos. O analisador lxico atua como uma interface entre o texto de entrada e o analisador sinttico.
Figura 2.1: Analisador lxico.

sequncia de caracteres

analisador lxico

sequncia de smbolos lxicos

As formas mais comuns de smbolos lxicos so: identicadores palavras utilizadas para nomear entidades do programa, como variveis, funes, mtodos, classes, mdulos, etc. literais sequncia de caracteres que representa uma constante, como um nmero inteiro, um nmero em ponto utuante, um caracter, uma string, um valor verdade (verdadeiro ou falso), etc. palavras chaves palavras usados para expressar estruturas da linguagem, como comandos condicionais, comandos de repetio, etc. Geralmente so reservadas, no podendo ser utilizadas como identicadores. sinais de pontuao sequncias de caracteres que auxiliam na construo das estruturas do programa, como por exemplo servindo de separador de expresses em uma lista de expresses.

2.2

Analisador lxico ad hoc

Quando a estrutura lxica de uma linguagem no muito complexa, o analisador lxico pode ser facilmente escrito mo. O programa deve analisar a sequncia de caracteres da entrada, agrupandoos para formar os tokens de acordo com a linguagem sendo implementada. 2-1

2-2

CAPTULO 2. ANLISE LXICA

2.3

Geradores de analisadores lxicos

Os geradores de analisadores lxicos so ferramentas que tem como entrada uma especicao da estrutura lxica de uma linguagem (na forma de um arquivo texto), e produzem um analisador lxico correspondente especicao.

2.4

JFlex

JFLex (http://jflex.de/) um gerador de analisador lxico escrito em Java que gera cdigo em Java. Ele distribudo usando o licena GPL e est disponvel em http://jflex.de/download. html. O seu manual pode ser obtido em http://jflex.de/manual.pdf.

2.4.1

Instalao

Sendo uma aplicao Java, JFlex necessita de uma mquina virtual Java para ser executado. Assim certique-se primeiro de que uma mquina virtual de Java j est instalada. Assumiremos que a sua verso 1.2 ou superior. Instalao no Windows Para instalar JFlex no Windows, siga os passos seguintes: 1. Descompacte o arquivo zip disponibilizado em http://jflex.de/download.html em um diretrio de sua preferncia. Vamos assumir que este diretrio C:\, e que a verso do JFlex a ltima disponvel (1.4.3, neste momento). Assim o JFlex estar disponvel em C:\jflex-1.4.3. 2. Edite o arquivo bin\jflex.bat de forma que ele que com o seguinte contedo: set JFLEX_HOME=C:\jflex-1.4.3 java -Xmx128m -jar %JFLEX_HOME%\lib\JFlex.jar %1 %2 %3 %4 %5 %6 %7 %8 %9 Se necessro, modique a varivel de ambiente JFLEX_HOME de acordo com o diretrio onde o JFlex foi instalado. 3. Inclua o diretrio bin\ do JFlex (no exemplo, C:\jflex-1.4.3\bin) na varivel de ambiente PATH. Instalao no Linux Sugiro que se utilize o programa de gerenciamento de pacotes de sua distribuio Linux para instalar o JFlex. Caso no haja um pacote disponvel para a sua distribuio, siga as instrues em http://jflex.de/installing.html. Instalao no Eclipse O JFlex pode ser integrado no Eclipse, um ambiente de desenvolvimento integrado largamente utilizado para o desenvolvimento de aplicaes Java. Para tanto siga os passos segintes. Assumiremos que a verso do Eclipse a verso 3.5.2. 1. Abra a janela de congurao de ferramentas externas acessando o menu Run -> External Tools -> External Tools Congurations... 2. Na janela External Tools Conguration Clique no boto New Launch Conguration para criar uma nova congurao. Veja a gura 2.2.

2.4. JFLEX
Figura 2.2: Criando uma nova congurao de ferramenta externa.

2-3

3. Modique o nome da congurao para JFlex. 4. Na aba Main preencha os seguintes campos (veja a gura 2.3): Coloque o caminho para o arquivo de execuo do JFlex no campo Location. No exemplo do Windows o caminho C:\jflex-1.4.3\bin\jflex.bat. No linux o caminho provavelmente seja /usr/bin/jflex. Em Working Directory coloque ${container_loc}. Em Arguments coloque ${resource_name}. 5. Na aba Refresh marque as opes seguintes (veja a gura 2.4): Marque a opo Refresh resources upon completion. Marque a opo The project containing the selected resource. 6. Clique no boto Apply e depois no boto Close.

2.4.2

Executando o JFlex

Linha de comando O JFlex pode ser executado na linha de comando com: jflex <opes> <arquivos de entrada> Consulte o manual para conhecer as opes que o JFlex aceita.

2-4

CAPTULO 2. ANLISE LXICA


Figura 2.3: Aba Main da congurao do JFlex no Eclipse.

No Eclipse Para compilar uma especicao, abra o arquivo contendo a especicao, v janela de congurao de ferramentas externas acessando o menu Run -> External Tools -> External Tools Congurations..., selecione a ferramenta JFlex, e clique no boto Run. Da prxima vez no ser mais necessrio abrir esta janela, pois uma entrada do JFlex adicionada ao menu: Run -> External Tools -> JFlex. Exerccio 2.1. Verique a congurao do JFlex e do Eclipse em seu computador.

2.4.3

Exemplo: analisador simples

Uma especicao lxica formada por trs sees, separadas por %%: 1. Cdigo do usurio. inserido no incio do arquivo gerado pelo JFlex. Tipicamente colocamos declarao de pacote e declarao de importao nesta seo. 2. Opes e declaraes. um conjunto de opes que permitem congurar como o analisador lxico ser gerado, declaraes de estados, e declaraes de macros Cada opo deve comear com o caracter %, colocado na primeira coluna da linha. 3. Regras lxicas. um conjunto de regras, onde cada regra formada por uma expresso regular e uma aoo. A expresso regular identica uma classe de smbolos lxicos, e a ao um trecho de cdigo Java que executado quando um smbolo formado durante a anlise lxica utilizando a expresso regular correspondente. Caso haja conito entre as regras, decidie-se pela regra que produzir a maior cadeia. Se ainda assim persistir o conito, d-se preferncia regra que aparece primeiro na especicao. O texto da entrada que casa com a expresso regular pode ser acessado pelo mtodo yytext, que includo na classe gerada.

2.4. JFLEX
Figura 2.4: Aba Refresh da congurao do JFlex no Eclipse.

2-5

A listagem 2.1 contm uma especicao lxica bem simples.


Listagem 2.1: Exemplo de especicao lxica.

%% %integer %% [a-z][a-z0-9]* { return 1; } [0-9]+ { return 2; } [ \t\n\r]+ { /* do nothing */ } . { System.err.printf("error: unexpected char |%s|\n", yytext()); } 1. Identicadores so formados por uma sequncia de letras minsculas e dgitos decimais, comeando por uma letra. O tipo do smbolo 1. 2. Nmeros naturais so formados por uma sequncia de um ou mais dgitos decimais. O tipo do smbolo 2. 3. Brancos (espaos, tabulao horizontal, mudana de linha) so ignorados. 4. Qualquer caracter da entrada que no formar smbolo lxico (identicador ou nmero inteiro) ou for ignorado (brancos) gera uma mensagem de erro. importante que esta seja a ltima regra, pois ela casa com qualquer caracter da entrada (exceto mudana de linha). Por default, JFlex gera uma classe chamada Yylex. A opo %class permite especicar o nome da classe desejada. O construtor desta classe tem um argumento representando a entrada a ser analisada.

2-6

CAPTULO 2. ANLISE LXICA

A classe gerada tem um mtodo pblico, sem argumentos, que deve ser utilizado no restante da aplicao para obter o prximo smbolo lxico da entrada. O nome default deste mtodo yylex, mas pode-se especicar um outro nome utilizando a opo %function. O tipo de retorno deste mtodo de anlise lxica , por default, YYToken, que deve ser denido na aplicao. A opo %type permite usar um outro tipo, no entanto. Existe tambm a opo %integer que especca o tipo de retorno int. Quando o nal de arquivo atingido, o mtodo de anlise lxica retorna um valor especco para indicar o m da entrada. Quando o tipo de retorno for uma subclasse de java.lang.Object, o valor retornado null, por default. Com %integer o valor de retorno default a constante YYEOF, denida como uma varivel de instncia public static final int na classe gerada. Existem diferentes maneiras de se especicar um valor de retorno diferente do default quando se atinge o m de arquivo. A listagem 2.2 dene a classe Test com o mtodo main que pode ser utilizado para testar o analisador gerado.
Listagem 2.2: Classe para testar o analisador gerado pelo JFlex.

import java.io.FileReader; import java.io.Reader; public class Test { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java Test <input file>"); System.exit(1); } try { Reader input = new FileReader(args[0]); Yylex scanner = new Yylex(input); int token; do { token = scanner.yylex(); System.out.println(token); } while (token != Yylex.YYEOF); } catch (Exception e) { e.printStackTrace(); } } } Basicamente toma-se as seguintes aes: 1. O arquivo especicado na linha de comando aberto para leitura 2. Uma instncia da classe de anlise lxica criada, usando este arquivo como fonte 3. Em um comando de repetio, cada smbolo lxico obtido e exibido na sada padro, at que se chegue no nal da entrada.

2.4. JFLEX

2-7

Exerccio 2.2. Cria um aplicativo Java para testar o JFlex utilizando a especicao e a classe Test, dadas anteriormente. Crie um programa de entrada e teste a aplicao determinando a sada para esta entrada.

2.4.4

Exemplo: usando uma classe para representar os smbols lxicos


Listagem 2.3: Classe para representar um smbolo lxico.

A classe Token, denida na listagem 2.3, ser utilizada para representar os smbolos lxicos. import java.util.Formatter; public class Token { public enum T { IF { public ID { public INT { public FLOAT { public STR { public EOF { public } public public public public T type; Object val; int line; int col;

String String String String String String

toString() toString() toString() toString() toString() toString()

{ { { { { {

return return return return return return

"IF"; } }, "ID"; } }, "INT"; } }, "FLOAT"; } }, "STR"; } }, "EOF"; } }

public Token(T type, int line, int col) { this.type = type; this.line = line; this.col = col; } public Token(T type, Object val, int line, int col) { this.type = type; this.val = val; this.line = line; this.col = col; } public String toString() { Formatter out = new Formatter(); out.format("(%4d,%4d) %s", line, col, type); if (val != null) out.format(" [%s]", val); return out.toString(); } }

2-8 Cada smbolo lxico tem as seguintes informaes:

CAPTULO 2. ANLISE LXICA

o tipo do smbolo lxico, representando a sua categoria, como por exemplo identicador, literal inteiro, literal em ponto utuante, etc. valor semntico, que alguma informao adicional sobre o smbolo lxico e que ser necessria em etapas posteriores da compilao; exemplo: nome do identicador, valor de um literal inteiro, etc. posio, dada pelo nmero da linha e da coluna, em que o smbolo aparece na entrada. Os tipos possveis para um smbolo lxico so representados pelos valores enumerados do tipo Token.T. A listagem 2.4 mostra uma especicao lxica simples.
Listagem 2.4: Exemplo de especicao lxica.

%% %class Lexer %type Token %line %column %{ private Token token(Token.T type) { return new Token(type, yyline, yycolumn); } private Token token(Token.T type, Object val) { return new Token(type, val, yyline, yycolumn); } %} %% if { return token(Token.T.IF); } [a-z][a-z0-9]* { return token(Token.T.ID, yytext()); } [0-9]+ { return token(Token.T.INT, new Integer(yytext())); } [0-9]+"."[0-9]*|[0-9]*"."[0-9]+ { return token(Token.T.FLOAT, new Double(yytext())); } [ \t\n\r]+ { /* do nothing */ } EOF { return token(Token.T.EOF); } . { System.err.printf("error: unexpected char |%s|\n", yytext()); } Observe que: O nome da classe gerada Lexer. O tipo do resultado do mtodo que faz a anlise lxica Token. As opes %line, %column e %char habilitam a contagem de linhas, colunas e caracteres durante a anlise lxica. As variveis de instncia yyline, yycolumn e yychar podem ser utilizadas para se obter a contagem de linha, de coluna e de caracter atual.

2.4. JFLEX

2-9

As opes %{ e %} delimitam um cdigo que inserido na classe gerado. Neste exemplo denimos dois mtodos adicionais na classe gerada que facilitam a construo dos smbolos lxicos. A expresso regular <<EOF>> utilizada quando o m da entrada atingido. Exerccio 2.3. Explique porque a regra da palavra-chave if precisa ser colocada antes da regra de identicadores. Exerccio 2.4. Utilize esta especicao lxica para gerar um analisador usando o JFlex. Crie uma nova classe Test2 baseada na classe Test para testar o novo analisador. Teste o analisador.

2.4.5

Exemplo: denio de expresso regular


Listagem 2.5: Exemplo de especicao lxica.

A listagem 2.5 mostra uma especicao lxica simples. %% %class Lexer %type Token %line %column %{ private Token token(Token.T type) { return new Token(type, yyline, yycolumn); } private Token token(Token.T type, Object val) { return new Token(type, val, yyline, yycolumn); } %} alpha dig id int float %% if { return token(Token.T.IF); } {id} { return token(Token.T.ID, yytext()); } {int} { return token(Token.T.INT, new Integer(yytext())); } {float} { return token(Token.T.FLOAT, new Double(yytext())); } [ \t\n\r]+ { /* do nothing */ } EOF { return token(Token.T.EOF); } . { System.err.printf("error: unexpected char |%s|\n", yytext()); } Observe que segunda seo da especicao dene cinco expresses regulares, {alpha}, {dig}, {id}, {int} e {float}, que so utilizadas nas prprias denies e nas regras lxicas. Exerccio 2.5. Gere um analisador lxico usando esta nova especicao, e teste-o. = = = = = [a-zA-Z] [0-9] {alpha} ({alpha} | {dig})* {dig}+ {dig}+ "." {dig}* | {dig}* "." {dig}+

2-10

CAPTULO 2. ANLISE LXICA

2.4.6

Exemplo: estados

A listagem 2.6 mostra uma especicao lxica que dene novos estados.
Listagem 2.6: Exemplo de especicao lxica usando estados.

%% %class Lexer %type Token %line %column %{ private StringBuilder str = new StringBuilder(); private Token token(Token.T type) { return new Token(type, yyline, yycolumn); } private Token token(Token.T type, Object val) { return new Token(type, val, yyline, yycolumn); } %} %state STR alpha dig id int float %% <YYINITIAL> { if { return token(Token.T.IF); } {id} { return token(Token.T.ID, yytext()); } {int} { return token(Token.T.INT, new Integer(yytext())); } {float} { return token(Token.T.FLOAT, new Double(yytext())); } \" { str.setLength(0); yybegin(STR); } [ \t\n\r]+ { /* do nothing */ } EOF { return token(Token.T.EOF); } } <STR> \" } <STR> \\t <STR> \\n { str.append(\t); } { str.append(\n); } { yybegin(YYINITIAL); return token(Token.T.STR, str.toString()); = = = = = [a-zA-Z] [0-9] {alpha} ({alpha} | {dig})* {dig}+ {dig}+ "." {dig}* | {dig}* "." {dig}+

2.5. ANALISADOR LXICO PARA A LINGUAGEM TIGER <STR> <STR> <STR> <STR> \\\" { str.append("); } \\\\ { str.append(\\); } [^\n\r\\]+ { str.append(yytext()); } EOF { yybegin(YYINITIAL); System.err.println("error: unclosed string literal"); }

2-11

.|\n

{ System.err.printf("error: unexpected char |%s|\n", yytext()); }

Este analisador utiliza um estado especial para analisar literais caracteres. Observe que: 1. O estado inicial YYINITIAL. 2. Outros estados podem ser declarados na segunda seo usando a opo %state. 3. O mtodo yybegin, includo pelo JFlex na classe gerada, permite mudar de estado. 4. Uma regra lxica pode ser prexada com uma lista de estados, indicando que a regra s usada se o analisador estiver em um dos etados listados. 5. Quanda a lista de estados omitida de uma regra, a regra pode ser utilizada em qualquer estado. Exerccio 2.6. Gere um analisador lxico usando esta nova especicao, e teste-o.

2.5

Analisador lxico para a linguagem Tiger

Exerccio 2.7. Utilizando o JFLex, implementar um analisador lxico para a linguagem Tiger, denida no apndice do livro do Appel [1].

3
Anlise sinttica descendente recursiva

3.1

Expresses aritmticas
Tabela 3.1: Gramtica de uma linguagem de expresses aritmticas: verso 1.

Considere a gramtica na tabela 3.1 para expresses aritmticas.

Exp Exp Exp Exp Exp Exp Exp Exp

num id id := Exp Exp + Exp Exp - Exp Exp * Exp Exp / Exp ( Exp )

Esta gramtica ambgua, uma vez que possvel construir mais de uma rvore de derivao para algumas sentenas da linguagem, como mostra a gura 3.1.
Figura 3.1: rvores de derivao para a cadeia 1 + 2 3.

Exp

Exp

Exp

Exp

Por este motivo esta gramtica no adequada para implementao a linguagem de expresses. Vamos escrever uma nova gramtica para esta linguagem para remover a ambiguidade. Para tanto vamos denir a prioridade usual dos operadores aritmticos, e designar para a atribuio uma prioridade menor do que todas as demais. O resultado a gramtica da tabela 3.2. Com esta gramtica, a nica rvore de derivao possvel para a cadeia 1 + 2 3 mostrada na gura 3.2. Mas esta gramtica ainda ainda no adequada para anlise sinttica descendente recursiva (ou preditiva), pois existem regras com recursividade esquerda. No entanto a recursividade esquerda pode ser facilmente eliminada, levando gramtica da tabela 3.3. Com esta gramtica a rvore de derivao para a cadeia 1 + 2 + 3 exibida na gura 3.3.

3-1

3-2

CAPTULO 3. ANLISE SINTTICA DESCENDENTE RECURSIVA

Tabela 3.2: Gramtica de uma linguagem de expresses aritmticas: verso 2.

Exp Exp Exp1 Exp1 Exp1 Term Term Term Fator Fator Fator

id := Exp1 Exp1 Exp1 + Term Exp1 - Term Term Term * Fator Term / Fator Fator num id ( Exp )

Figura 3.2: rvore de derivao para a cadeia 1 + 2 3.

Exp

Exp1

Exp1

Term

Term

Term

Fator

Fator

Fator

Tabela 3.3: Gramtica de uma linguagem de expresses aritmticas: verso 3.

Exp Exp Exp1 RestoExp RestoExp RestoExp Term RestoTermo RestoTermo RestoTermo Fator Fator Fator

id := Exp1 Exp1 Term RestoExp + Term RestoExp - Term RestoExp Fator RestoTermo * Fator RestoTermo / Fator RestoTermo num id ( Exp )

3.1. EXPRESSES ARITMTICAS

3-3

Figura 3.3: rvore de derivao para a cadeia 1 + 2 3.

Exp Exp1 Term Fator 1 RestoTermo + Fator 2 * Termo RestoTermo Fator 3 RestoTermo RestoExp RestoExp

4
Anlise sinttica de Tiger
Exerccio 4.1. Utilizando o CUP, implementar um analisador sinttico para a linguagem Tiger, denida no apndice do livro do Appel [1]. Utilize os arquivos disponibilizados em http://www.iceb. ufop.br/decom/prof/romildo/cic220/praticas/tiger-parser-0.1.rar para criar uma aplicao em Java para fazer a anlise sinttica de programas em Tiger. Esto includas todas as classes necessrias. Falta apenas completar a gramtica livre de contexto para a linguagem Tiger, que ser processada pelo CUP.

4-1

Referncias Bibliogrcas

[1] Andrew W Appel. Modern Compiler Implementation in Java. Cambridge University Press, 1998.

4-2