Como construir um compilador utilizando ferramentas Java Aula 5 – Análise Léxica com JavaCC Prof. Márcio Delamaro delamaro@icmc.usp.br Como construir um compilador utilizando ferramentas Java – p. 1/3 O que é o JavaCC Ambiente ou ferramenta que permite a geração de um analisador sintático completo Como construir um compilador utilizando ferramentas Java – p. 2/3 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 Como construir um compilador utilizando ferramentas Java – p. 2/3 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 construir um compilador utilizando ferramentas Java – p. 2/3 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 Permite também a construção da árvore sintática Como construir um compilador utilizando ferramentas Java – p. 2/3 Como funciona langx.jj JavaCC Como construir um compilador utilizando ferramentas Java – p. 3/3 Como funciona sort.x langx.jj JavaCC sort.jas Como construir um compilador utilizando ferramentas Java – p. 4/3 Como funciona Como construir um compilador utilizando ferramentas Java – p. 5/3 Partes do arquivo jj Opções Declaração da classe principal Declarações do AL Declarações do AS Como construir um compilador utilizando ferramentas Java – p. 6/3 Partes do arquivo jj options { STATIC = false; } PARSER_BEGIN(Test) public class Test { } PARSER_END(Test) SKIP : { " " } JAVACODE void program() { } Como construir um compilador utilizando ferramentas Java – p. 7/3 A classe principal tests2.jj PARSER_BEGIN(Test) public class Test { static public void main(String args[]) { System.out.printf("Hello world!"); } } PARSER_END(Test) Como construir um compilador utilizando ferramentas Java – p. 8/3 A classe principal Como construir um compilador utilizando ferramentas Java – p. 9/3 A classe principal: langx++.jj PARSER_BEGIN(langX) package parser; /* declaração de pacote */ import java.io.*; /* imports necessários */ public class langX { ... } PARSER_END(langX) Como construir um compilador utilizando ferramentas Java – p. 10/3 Variáveis da classe ... public class langX { final static String Version = "X++ Compiler - Version 1.0 - 2004"; boolean Menosshort = false; // saı́da resumida = falso ... } Como construir um compilador utilizando ferramentas Java – p. 11/3 Método principal: variáveis locais ... public class langX { // Define o método "main" da classe langX. public static void main(String args[]) throws ParseException { String filename = ""; // nome do arquivo a ser analisado langX parser; // analisador léxico/sintático int i; boolean ms = false; System.out.println(Version); ... } Como construir um compilador utilizando ferramentas Java – p. 12/3 Método principal: ler argumentos public class langX { public static void main(String args[]) throws ParseException { ... // lê os parâmetros 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); } } ... } Como construir um compilador utilizando ferramentas Java – p. 13/3 Método principal: ler nome arquivo public static void main(String args[]) throws ParseException { ... if (args[i].equals("-")) { // lê da entrada padrão System.out.println("Reading from standard input . . ."); parser = new langX(System.in); } else { // lê 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; } } ... } Como construir um compilador utilizando ferramentas Java – p. 14/3 Criação do AS if (args[i].equals("-")) { // lê da entrada padrão System.out.println("Reading from standard input . . ."); parser = new langX(System.in); } else { // lê 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; } } Como construir um compilador utilizando ferramentas Java – p. 15/3 Execução e finalização public static void main(String args[]) throws ParseException { ... parser.Menosshort = ms; parser.program(); // chama o método que faz a análise // verifica se houve erro léxico 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). Como construir um compilador utilizando ferramentas Java – p. 16/3 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; } Como construir um compilador utilizando ferramentas Java – p. 17/3 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; descrição dos itens léxicos; Como construir um compilador utilizando ferramentas Java – p. 18/3 Código Java TOKEN_MGR_DECLS : { int countLexError = 0; public int foundLexError() { return countLexError; } } Como construir um compilador utilizando ferramentas Java – p. 19/3 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" } Como construir um compilador utilizando ferramentas Java – p. 20/3 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" > } Como construir um compilador utilizando ferramentas Java – p. 21/3 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, então GT será o casamento realizado. Como construir um compilador utilizando ferramentas Java – p. 22/3 TOKEN: operadores TOKEN : { < | | | | | | | | | | | ASSIGN: "=" > < GT: ">" > < LT: "<" > < EQ: "==" > < LE: "<=" > < GE: ">=" > < NEQ: "!=" > < PLUS: "+" > < MINUS: "-" > < STAR: "*" > < SLASH: "/" > < REM: "%" > } Como construir um compilador utilizando ferramentas Java – p. 23/3 TOKEN: outros símbolos TOKEN : { < | | | | | | | | LPAREN: "(" > < RPAREN: ")" > < LBRACE: "{" > < RBRACE: "}" > < LBRACKET: "[" > < RBRACKET: "]" > < SEMICOLON: ";" > < COMMA: "," > < DOT: "." > } Como construir um compilador utilizando ferramentas Java – p. 24/3 TOKEN: constantes inteiras TOKEN : { // números decimais, octais, hexadecimais ou binários < 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"]) ) > } Como construir um compilador utilizando ferramentas Java – p. 25/3 Constantes inteiras 123afhoje Como construir um compilador utilizando ferramentas Java – p. 26/3 Constantes inteiras 123afhoje constante inteira 123afh seguida de um identificador oje; Como construir um compilador utilizando ferramentas Java – p. 26/3 Constantes inteiras 123afhoje constante inteira 123afh seguida de um identificador oje; 0baCh Como construir um compilador utilizando ferramentas Java – p. 26/3 Constantes inteiras 123afhoje constante inteira 123afh seguida de um identificador oje; 0baCh também é uma constante hexadecimal; Como construir um compilador utilizando ferramentas Java – p. 26/3 Constantes inteiras 123afhoje constante inteira 123afh seguida de um identificador oje; 0baCh também é uma constante hexadecimal; 7620OO Como construir um compilador utilizando ferramentas Java – p. 26/3 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; Como construir um compilador utilizando ferramentas Java – p. 26/3 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 Como construir um compilador utilizando ferramentas Java – p. 26/3 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 tb10b. Como construir um compilador utilizando ferramentas Java – p. 26/3 String constante Inicia com aspas Seqüência de quaisquer caracteres (quaisquer ???). Termina com aspas Como definir ? Como construir um compilador utilizando ferramentas Java – p. 27/3 TOKEN: constantes string, null TOKEN : { // constante string como "abcd bcda" < string_constant: "\""( ˜["\"","\n","\r"])* "\"" > | < null_constant: "null" > // constante null } Como construir um compilador utilizando ferramentas Java – p. 28/3 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. Como construir um compilador utilizando ferramentas Java – p. 29/3 Conflitos II for Como construir um compilador utilizando ferramentas Java – p. 30/3 Conflitos II for class Como construir um compilador utilizando ferramentas Java – p. 30/3 Conflitos II for class Definir prioridade Como construir um compilador utilizando ferramentas Java – p. 30/3 Conflitos II for class Definir prioridade A definição que aparecer primeiro tem maior prioridade Como construir um compilador utilizando ferramentas Java – p. 30/3 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; Como construir um compilador utilizando ferramentas Java – p. 31/3 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. Como construir um compilador utilizando ferramentas Java – p. 32/3 Comentários Comentário não é um item léxico; Como construir um compilador utilizando ferramentas Java – p. 33/3 Comentários Comentário não é um item léxico; Pode aparecer em qualquer ponto do programa; Como construir um compilador utilizando ferramentas Java – p. 33/3 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; Como construir um compilador utilizando ferramentas Java – p. 33/3 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; Como construir um compilador utilizando ferramentas Java – p. 33/3 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. Como construir um compilador utilizando ferramentas Java – p. 33/3 Comentários Comentários multilinha /* ... */ Linha única // .... AL simplesmente ignora Usamos conceito de estado, característica do JavaCC Como construir um compilador utilizando ferramentas Java – p. 34/3 Definição de comentários SKIP : { "/*": multilinecomment } Como construir um compilador utilizando ferramentas Java – p. 35/3 Definição de comentários SKIP : { "/*": multilinecomment } <multilinecomment> SKIP: { "*/": DEFAULT | <˜[]> } Como construir um compilador utilizando ferramentas Java – p. 35/3 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. Como construir um compilador utilizando ferramentas Java – p. 35/3 Erro léxico Ao aparecer um item léxico não válido. Como construir um compilador utilizando ferramentas Java – p. 36/3 Erro léxico Ao aparecer um item léxico não válido. AL gerado pelo JavaCC lança um “TokenMgrError ” Como construir um compilador utilizando ferramentas Java – p. 36/3 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. Como construir um compilador utilizando ferramentas Java – p. 36/3 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 Como construir um compilador utilizando ferramentas Java – p. 36/3 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 Como construir um compilador utilizando ferramentas Java – p. 36/3 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. Como construir um compilador utilizando ferramentas Java – p. 36/3 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ê desenvolveu (sort, bintree, etc). Como construir um compilador utilizando ferramentas Java – p. 37/3