Construindo o primeiro analisador léxico com JFlex
JFlex é um gerador de analisador léxico escrito em Java. Ele recebe como entrada uma especificação baseada em um conjunto de expressões regulares e gera um algoritmo que lê os caracteres descritos no arquivo arquivo fonte combinando com os padrões definidos na especificação.
Instalando o JFlex
Faça o download do JFlex nesse link: http://jflex.de/download.html
Nesse primeiro exemplo nós vamos usar o JFlex integrado com a IDE Eclipse.
Você deve fazer a instalação da biblioteca no seu ambiente de desenvolvimento, eu sugiro uma rápida leitura nas instruções de instalação nesse link.
Você pode adicionar a biblioteca jflex-1.6.1.jar na configuração de Build Path do seu projeto no Eclipse.
Criado uma analisador léxico
Crie um novo projeto no Eclipse através do menu File -> New -> Java Project
chamado lexicalanalyzer
e adicione a biblioteca do JFlex - jflex-1.6.1.jar - nas configurações de Build Path do projeto. Você também deve crair uma pacote chamado br.com.johnidouglas
.
Se preferir você pode criar um projeto usando o Maven e adicionar a seguinte dependência.
No campo Group Id
informe br.com.johnidouglas
e no campo Artifact Id
informe lexicalanalyzer
<dependency>
<groupId>de.jflex</groupId>
<artifactId>jflex</artifactId>
<version>1.6.1</version>
</dependency>
Essa imagem mostra a estrutura do projeto no Eclipse.
Em seguida vamos criar a classe que ira gerar o analisador léxico a cada nova alteração no arquivo de especificação. Isso evita o uso da linha de comando para gerar a classe Java responsável por implementar o algoritmo de reconhecimento de tokens.
Vamos chamar essa classe de Generator
.
package br.com.johnidouglas.lexicalanalyzer;
import java.io.File;
import java.nio.file.Paths;
public class Generator {
public static void main(String[] args) {
String rootPath = Paths.get("").toAbsolutePath(). toString();
String subPath = "/src/main/java/br/com/johnidouglas/lexicalanalyzer/";
String file = rootPath + subPath + "language.lex";
File sourceCode = new File(file);
jflex.Main.generate(sourceCode);
}
}
O arquivo especificação possui as definições da nossa linguagem de programação. Para criar esse arquivo na IDE do Eclipse vá em File -> New -> Other -> File
salve esse arquivo com o nome de language.lex
na raiz do projeto.
No arquivo digite o seguinte conteúdo.
package br.com.johnidouglas.lexicalanalyzer;
%%
%{
private void imprimir(String descricao, String lexema) {
System.out.println(lexema + " - " + descricao);
}
%}
%class LexicalAnalyzer
%type void
BRANCO = [\n| |\t|\r]
ID = [_|a-z|A-Z][a-z|A-Z|0-9|_]*
SOMA = "+"
INTEIRO = 0|[1-9][0-9]*
%%
"if" { imprimir("Palavra reservada if", yytext()); }
"then" { imprimir("Palavra reservada then", yytext()); }
{BRANCO} { imprimir("Espaço em branco", yytext()); }
{ID} { imprimir("Identificador", yytext()); }
{SOMA} { imprimir("Operador de soma", yytext()); }
{INTEIRO} { imprimir("Número Inteiro", yytext()); }
. { throw new RuntimeException("Caractere inválido " + yytext()); }
Pronto, nós já temos um analisador léxico onde é possível reconhecer um conjunto de lexemas baseado em padrões definidos no arquivo de especificação - language.lex
, agora nos precisamos testar o analisador léxico.
Para isso crie uma nova classe chamada LanguageSextaFase
e inclua o seguinte código nela.
package br.com.johnidouglas.lexicalanalyzer;
import java.io.IOException;
import java.io.StringReader;
public class LanguageSextaFase {
public static void main(String[] args) throws IOException {
String expr = "if 2 + 3+a then";
LexicalAnalyzer lexical = new LexicalAnalyzer(new StringReader(expr));
lexical.yylex();
}
}
Para executar o analisador léxico siga os seguintes passos:
Execute a classe Generator: Observe que no projeto foi criado a classe
LexicalAnalyzer
. Essa classe possui a implementação das regras definidas no arquivo de especificaçãolanguage.lex
é representa o algoritmo do analisador léxico.Execute a classe
LanguageSextaFase
: Essa classe vai executar o analisador léxico passando como parametro uma expressão de uma lingaugem de programação qualquer.
Observe que a saída será do analisador léxico será a seguinte.
if - Palavra reservada if
- Espaço em branco
2 - Número Inteiro
- Espaço em branco
+ - Operador de soma
- Espaço em branco
3 - Número Inteiro
+ - Operador de soma
a - Identificador
- Espaço em branco
then - Palavra reservada then
Baseado nesse primeiro exercício nos implementamos a primeira etapa do processo de compilação utilizado um gerando de analisador léxico.
Construindo uma analisador léxico para a linguagem Pascal
Vamos aprimorar o nosso analisador léxico para que ele reconheça os tokens pertencentes a linguagem de programação Pascal.
Essa imagem mostra a estrutura do projeto no Eclipse.
Crie uma classe chamada GeneratorPascal
, para isso você deve criar um novo pacote Java dentro do projeto lexicalanalyzer
, vamos chamar esse pacote de pascal
- br.com.johnidouglas.lexicalanalyzer.pascal
.
A classe GeneratorPascal
deve ter a seguinte implementação.
package br.com.johnidouglas.lexicalanalyzer.pascal;
import java.io.File;
import java.nio.file.Paths;
public class GeneratorPascal {
public static void main(String[] args) {
String rootPath = Paths.get("").toAbsolutePath(). toString();
String subPath = "/src/main/java/br/com/johnidouglas/lexicalanalyzer/pascal/";
String file = rootPath + subPath + "pascal.lex";
File sourceCode = new File(file);
jflex.Main.generate(sourceCode);
}
}
Agora vamos criar um arquivo de código chamado program.pas
esse arquivo vai conter o código fonte do programa escrito em Pascal.
// Programa que cálcula o salário de funcionários
Program CalcularSalario;
Var TempoEmAnos : Integer;
Var ValorSalario: Real;
Begin
If (TempoEmAnos > 10) Then
ValorSalario := 100.00;
Else
ValorSalario := ValorSalario * 2;
End;
Imprimir("Processamento feito com sucesso")
End.
Vamos criar uma classe chamada PascalToken
essa classe vai representar um token reconhecido na linguagem e deve ter duas propriedades, name
, value
, line
e column
.
package br.com.johnidouglas.lexicalanalyzer.pascal;
public class PascalToken {
public String name;
public String value;
public Integer line;
public Integer column;
public PascalToken(String name, String value, Integer line, Integer column) {
this.name = name;
this.value = value;
this.line = line;
this.column = column;
}
}
Crie uma classe chamada PascaLexicalAnalyzer
com o seguinte código.
package br.com.johnidouglas.lexicalanalyzer.pascal;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;
public class PascaLexicalAnalyzer {
public static void main(String[] args) throws IOException {
String rootPath = Paths.get("").toAbsolutePath(). toString();
String subPath = "/src/main/java/br/com/johnidouglas/lexicalanalyzer/pascal";
String sourceCode = rootPath + subPath + "/program.pas";
LexicalAnalyzer lexical = new LexicalAnalyzer(new FileReader(sourceCode));
PascalToken token;
while ((token = lexical.yylex()) != null) {
System.out.println("<" + token.name + ", " + token.value + "> (" + token.line + " - " + token.column + ")");
}
}
}
Essa classe é responsável por executar o analisador léxico.
Nos precisamos criar o arquivo que vai conter as especificações da linguagem de programação Pascal para que os tokens sejam reconhecidos.
Crie o arquivo pascal.lex
esse arquivo deve conter a seguinte especificação.
package br.com.johnidouglas.lexicalanalyzer.pascal;
import java_cup.runtime.*;
%%
%{
private PascalToken createToken(String name, String value) {
return new PascalToken( name, value, yyline, yycolumn);
}
%}
%public
%class LexicalAnalyzer
%type PascalToken
%line
%column
inteiro = 0|[1-9][0-9]*
brancos = [\n| |\t]
program = "program"
%%
{inteiro} { return createToken("inteiro", yytext()); }
{program} { return createToken(yytext(), "");}
{brancos} { /**/ }
. { throw new RuntimeException("Caractere inválido " + yytext() + " na linha " + yyline + ", coluna " +yycolumn); }
O processo para executar o analisador léxico e o mesmo já utilizado no primeiro exemplo.
Execute a classe
GeneratorPascal
essa classe é responsável por gerar o algoritmo do analisador léxico.Execute a classe
PascaLexicalAnalyzer
essa classe é responsável por executar o analisador léxico.
Você deve ter percebido que alguns erros ocorreram, isso por que nosso arquivo de especificação esta incompleto, ou seja, não possui todos os tokens da linguagem Pascal.
Você deve implementar o restante da especificação do Pascal através de expressões regulares.