Como construir um compilador utilizando
ferramentas Java
Aula 5 – Análise Léxica com JavaCC
Prof. M ´arcio Delamaro
O que é o JavaCC
Ambiente ou ferramenta que permite a geração de um analisador sintático completo
O que é o JavaCC
Ambiente ou ferramenta que permite a geração de um analisador sintático completo
O que é o JavaCC
Ambiente ou ferramenta que permite a geração de um analisador sintático completo
A partir de uma descrição de alto nível gera código Java Dentro de um só arquivo permite definir AL e AS
O que é o JavaCC
Ambiente ou ferramenta que permite a geração de um analisador sintático completo
A partir de uma descrição de alto nível gera código Java Dentro de um só arquivo permite definir AL e AS
Como funciona
Como funciona
langx.jj JavaCC sort.x
Partes do arquivo jj
OpçõesDeclaração da classe principal
Declarações do AL
Partes do arquivo jj
options {
STATIC = false; }
PARSER_BEGIN(Test)
public class Test { } PARSER_END(Test) SKIP : { " " }
JAVACODE void program() {
A classe principal
tests2.jjPARSER_BEGIN(Test)
public class Test {
static public void main(String args[]) { System.out.printf("Hello world!"); }
}
A classe principal: langx++.jj
PARSER_BEGIN(langX)
package parser; /* declarac¸˜ao de pacote */ import java.io.*; /* imports necess´arios */
public class langX { ...
}
Variáveis da classe
...
public class langX {
final static String Version = "X++ Compiler - Version 1.0 - 2004"; boolean Menosshort = false; // sa´ıda resumida = falso
... }
Método principal: variáveis locais
...
public class langX {
// Define o m´etodo "main" da classe langX.
public static void main(String args[]) throws ParseException {
String filename = ""; // nome do arquivo a ser analisado langX parser; // analisador l´exico/sint´atico
int i;
boolean ms = false;
System.out.println(Version); ...
Método principal: ler argumentos
public class langX {
public static void main(String args[]) throws ParseException {
...
// lˆe os parˆametros passados para o compilador for (i = 0; i < args.length - 1; i++)
{
if ( args[i].toLowerCase().equals("-short") ) ms = true;
else {
System.out.println("Usage is: java langX [-short] " + "inputfile");
System.exit(0); }
}
Método principal: ler nome arquivo
public static void main(String args[]) throws ParseException {
...
if (args[i].equals("-")) { // lˆe da entrada padr˜ao
System.out.println("Reading from standard input . . ."); parser = new langX(System.in);
}
else { // lˆe do arquivo
filename = args[args.length-1];
System.out.println("Reading from file " + filename + " . . ."); try {
parser = new langX(new java.io.FileInputStream(filename)); }
catch (java.io.FileNotFoundException e) {
System.out.println("File " + filename + " not found."); return;
} }
... }
Criação do AS
if (args[i].equals("-")) { // lˆe da entrada padr˜ao
System.out.println("Reading from standard input . . ."); parser = new langX(System.in);
}
else { // lˆe do arquivo
filename = args[args.length-1];
System.out.println("Reading from file " + filename + " . . ."); try {
parser = new langX(new java.io.FileInputStream(filename)); }
catch (java.io.FileNotFoundException e) {
System.out.println("File " + filename + " not found."); return;
} }
Execução e finalização
public static void main(String args[]) throws ParseException {
...
parser.Menosshort = ms;
parser.program(); // chama o m´etodo que faz a an´alise // verifica se houve erro l´exico
if ( parser.token_source.foundLexError() != 0 )
System.out.println(parser.token_source.foundLexError() + " Lexical Errors found");
else
System.out.println("Program successfully analyzed."); }
O AS “possui” um AL. O AL é definido pelo usuário
(algumas coisas).
Classe principal: outros métodos
public class langX {
public static void main(String args[]) throws ParseException {
... }
static public String im(int x) { int k; String s; s = tokenImage[x]; k = s.lastIndexOf("\""); try {s = s.substring(1,k);} catch (StringIndexOutOfBoundsException e) {} return s; }
Descrição do AL
A descrição do analisador léxico é dividida em duas partes: código Java a ser inserido na classe do AL;
Código Java
TOKEN_MGR_DECLS : {
int countLexError = 0;
public int foundLexError() {
return countLexError; }
SKIP
Todas as definições nesta seção utilizam a representação de expressões regulares. A palavra SKIP indica ao JavaCC que desejamos definir quais são as cadeias que devem ser ignoradas. SKIP : { " " | "\t" | "\n" | "\r" | "\f" }
TOKEN: palavras reservadas
TOKEN é utilizada para definir, por meio de expressões
regulares, quais as cadeias a serem reconhecidas e quais os tipos de tokens que a elas correspondem.
TOKEN : { < BREAK: "break" > | < CLASS: "class" > | < CONSTRUCTOR: "constructor" > | < ELSE: "else" > | < EXTENDS: "extends" > | < FOR: "for" > ... | < PRINT: "print" > | < READ: "read" > | < RETURN: "return" > | < STRING: "string" > | < SUPER: "super" >
Conflitos
AL é construído de modo que a maior cadeia possível seja reconhecida. Por isso não há nenhum problema quanto a uma Expressão Regular poder gerar subcadeias de outra Expressão Regular. Sempre será considerada a maior
cadeia da entrada que casar com alguma expressão
regular. Isso acontece, por exemplo, a seguir com os tokens
GT e GE. Se a entrada possuir um string >=, este será
identificado como um GE, mas se possuir um > apenas,
TOKEN: operadores
TOKEN : { < ASSIGN: "=" > | < GT: ">" > | < LT: "<" > | < EQ: "==" > | < LE: "<=" > | < GE: ">=" > | < NEQ: "!=" > | < PLUS: "+" > | < MINUS: "-" > | < STAR: "*" > | < SLASH: "/" > | < REM: "%" > }TOKEN: outros símbolos
TOKEN : { < LPAREN: "(" > | < RPAREN: ")" > | < LBRACE: "{" > | < RBRACE: "}" > | < LBRACKET: "[" > | < RBRACKET: "]" > | < SEMICOLON: ";" > | < COMMA: "," > | < DOT: "." > }TOKEN: constantes inteiras
TOKEN :
{ // n´umeros decimais, octais, hexadecimais ou bin´arios < int_constant:( (["0"-"9"] (["0"-"9"])* ) | (["0"-"7"] (["0"-"7"])* ["o", "O"] ) | (["0"-"9"] (["0"-"7","A"-"F","a"-"f"])* ["h", "H"] ) | (["0"-"1"] (["0"-"1"])* ["b", "B"]) ) > }
Constantes inteiras
Constantes inteiras
123afhoje
Constantes inteiras
123afhoje
constante inteira 123afh seguida de um identificador oje;
Constantes inteiras
123afhoje
constante inteira 123afh seguida de um identificador oje;
0baCh
Constantes inteiras
123afhoje
constante inteira 123afh seguida de um identificador oje;
0baCh
também é uma constante hexadecimal;
Constantes inteiras
123afhoje
constante inteira 123afh seguida de um identificador oje;
0baCh
também é uma constante hexadecimal;
7620OO
Constantes inteiras
123afhoje
constante inteira 123afh seguida de um identificador oje;
0baCh
também é uma constante hexadecimal;
7620OO
é uma constante octal seguida por um identificador O;
Constantes inteiras
123afhoje
constante inteira 123afh seguida de um identificador oje;
0baCh
também é uma constante hexadecimal;
7620OO
é uma constante octal seguida por um identificador O;
1011tb10b
constante decimal 1011 seguida pelo identificador
String constante
Inicia com aspasSeqüência de quaisquer caracteres (quaisquer ???). Termina com aspas
TOKEN: constantes string, null
TOKEN :
{ // constante string como "abcd bcda" < string_constant:
"\""( ˜["\"","\n","\r"])* "\"" > |
< null_constant: "null" > // constante null }
TOKEN: identificadores
Os identificadores são definido como sendo iniciados por uma letra, seguida por letras ou dígitos.
TOKEN : {
< IDENT: <LETTER> (<LETTER>|<DIGIT>)* > |
< #LETTER:["A"-"Z","a"-"z"] > |
< #DIGIT:["0"-"9"] > }
Foram utilizados dois tokens
#LETTER
e
#DIGIT
para
definir
IDENT
. Esses tokens não são utilizados na
gramática da linguagem
X
++, mas servem como
auxiliares na definição do próprio AL.
Conflitos II
forConflitos II
forConflitos II
forclass
Conflitos II
forclass
Definir prioridade
O que o AL produz: Token
int kind; Contém o tipo do token reconhecido. Cada
um dos tokens descritos no arquivo .jj como IF ou
IDENT é definido na classe langXConstants como
sendo uma constante inteira. Assim, supondo que
langXConstants.IDENT foi definido com o valor 9,
então ao reconhecer um identificador, o AL irá produzir um objeto Token cuja variável kind tem o valor 9;
int beginLine, beginColumn, endLine,
endColumn; Essas variáveis indicam, respectivamente,
a linha e a coluna dentro do arquivo de entrada, onde se inicia e onde termina o token reconhecido;
O que o AL produz: Token
String image; É a cadeia que foi lida e reconhecida
como token. Por exemplo, se a cadeia func10 foi lida na entrada e reconhecida como um IDENT, então essa
variável contém a cadeia lida, ou seja, func10;
Token next; Uma referência para o próximo token
reconhecido após ele. Se o AL ainda não leu nenhum outro token ou se esse é o último token da entrada, então seu valor é null;
Token specialToken; É um apontador para o último
token especial reconhecido antes deste. Veja mais adiante os comentários sobre o que são os tokens especiais.
Comentários
Comentários
Comentário não é um item léxico;
Comentários
Comentário não é um item léxico;
Pode aparecer em qualquer ponto do programa;
Comentários
Comentário não é um item léxico;
Pode aparecer em qualquer ponto do programa;
a = b.myMethod(10, /* comentário */ c) + 2;
Comentários
Comentário não é um item léxico;
Pode aparecer em qualquer ponto do programa;
a = b.myMethod(10, /* comentário */ c) + 2;
Deve ser tratado pelo AL; Por exemplo, ignorar.
Comentários
Comentários multilinha /* ... */ Linha única // ....
AL simplesmente ignora
Definição de comentários
SKIP : {
"/*": multilinecomment }
Definição de comentários
SKIP : { "/*": multilinecomment } <multilinecomment> SKIP: { "*/": DEFAULT | <˜[]> }Definição de comentários
SKIP : { "/*": multilinecomment } <multilinecomment> SKIP: { "*/": DEFAULT | <˜[]> }Vale a regra de que sempre a maior cadeia possível é
utilizada no casamento. Como o segundo padrão tem
apenas um caractere, então ao aparecer a cadeia */, o
casamento é sempre feito no primeiro padrão.
Erro léxico
Erro léxico
Ao aparecer um item léxico não válido.
Erro léxico
Ao aparecer um item léxico não válido.
AL gerado pelo JavaCC lança um “TokenMgrError ” Isso faz com que a execução do AS termine.
Erro léxico
Ao aparecer um item léxico não válido.
AL gerado pelo JavaCC lança um “TokenMgrError ” Isso faz com que a execução do AS termine.
Erro léxico
Ao aparecer um item léxico não válido.
AL gerado pelo JavaCC lança um “TokenMgrError ” Isso faz com que a execução do AS termine.
Gostaríamos de ignorar e continuar a análise
Erro léxico
Ao aparecer um item léxico não válido.
AL gerado pelo JavaCC lança um “TokenMgrError ” Isso faz com que a execução do AS termine.
Gostaríamos de ignorar e continuar a análise
Para isso devemos evitar que o AL identifique o erro
Ou seja, qualquer outro símbolo que aparecer, deve ser tratado.
Exercícios
Estudar no livro a implementação do comentário de linha única.
Estudar no livro a implementação da recuperação de erro léxico.
Baixar o arquivo do Capítulo 3. Gerar o “compilador” e executar com os programas exemplos que você