Como construir um compilador utilizando ferramentas Java Aula 8 – Recuperação de Erros Sintáticos Prof. Márcio Delamaro [email protected] Como construir um compilador utilizando ferramentas Java – p. 1/2 O que temos até agora A.S. lê entrada. Se entrada estiver de acordo com a gramática, execução termina OK. Se existir algum erro sintático, uma execeção é lançada. ParseException é tratada no método main. Uma mensagem de erro é dada. Como construir um compilador utilizando ferramentas Java – p. 2/2 Tratamento da exceção try { parser.program(); } catch (ParseException e) { System.err.println(e.getMessage()); parser.contParseError = 1; } finally { System.out.println(parser.token_source.foundLexError() + " Lexical Errors found"); System.out.println(parser.contParseError + " Syntactic Errors found"); } Como construir um compilador utilizando ferramentas Java – p. 3/2 A saída é... X++ Compiler - Version 1.0 - 2004 Reading from file bintree-erro-sintatico.x . . . Encountered "mes" at line 21, column 4. Was expecting one of: "[" ... ";" ... "." ... ">" ... "<" ... "==" ... "<=" ... ">=" ... "!=" ... 0 Lexical Errors found 1 Syntactic Errors found Como construir um compilador utilizando ferramentas Java – p. 4/2 Recuperação de erros Encountered "mes" at line 21, column 4. Was expecting one of: "[" ... ";" ... "." ... ">" ... "<" ... "==" ... "<=" ... ">=" ... "!=" ... Encountered "<" at line 27, column 14. Was expecting one of: <int_constant> ... <string_constant> ... "null" ... <IDENT> ... "(" ... "+" ... "-" ... Como construir um compilador utilizando ferramentas Java – p. 5/2 Recuperação de erros Encountered "right" at line 43, column 14. Was expecting one of: "(" ... "[" ... "," ... ";" ... Encountered "1" at line 69, column 14. Was expecting one of: "[" ... "." ... "=" ... <IDENT> ... 0 Lexical Errors found 4 Syntactic Errors found Como construir um compilador utilizando ferramentas Java – p. 6/2 Método de resincronização (do pânico) S → aAcd A → gh Como construir um compilador utilizando ferramentas Java – p. 7/2 Método de resincronização (do pânico) S → aAcd A → gh Analisar: agabcd Como construir um compilador utilizando ferramentas Java – p. 7/2 Método de resincronização (do pânico) S → aAcd A → gh Analisar: agabcd Método S não deve ser afetado por esse erro sintático. Como construir um compilador utilizando ferramentas Java – p. 7/2 Método de resincronização (do pânico) S → aAcd A → gh Analisar: agabcd Método S não deve ser afetado por esse erro sintático. O método A deve ressincronizar a entrada com o não-terminal que se espera reconhecer (S ). Como construir um compilador utilizando ferramentas Java – p. 7/2 Método de resincronização (do pânico) S → aAcd A → gh Analisar: agabcd Método S não deve ser afetado por esse erro sintático. O método A deve ressincronizar a entrada com o não-terminal que se espera reconhecer (S ). Consumir tokens até que apareça algum que possibilite a continuidade da análise. Como construir um compilador utilizando ferramentas Java – p. 7/2 Método de resincronização (do pânico) S → aAcd A → gh Analisar: agabcd Método S não deve ser afetado por esse erro sintático. O método A deve ressincronizar a entrada com o não-terminal que se espera reconhecer (S ). Consumir tokens até que apareça algum que possibilite a continuidade da análise. Método A precisa saber até onde ler. Como construir um compilador utilizando ferramentas Java – p. 7/2 Método de resincronização (do pânico) S → aAcd A → gh Analisar: agabcd Método S não deve ser afetado por esse erro sintático. O método A deve ressincronizar a entrada com o não-terminal que se espera reconhecer (S ). Consumir tokens até que apareça algum que possibilite a continuidade da análise. Método A precisa saber até onde ler. Usar FOLLOW de A. Como construir um compilador utilizando ferramentas Java – p. 7/2 Um pouco melhor S → aAcd | bAef A → gh Como construir um compilador utilizando ferramentas Java – p. 8/2 Um pouco melhor S → aAcd | bAef A → gh FOLLOW(A) = {c, e} Como construir um compilador utilizando ferramentas Java – p. 8/2 Um pouco melhor S → aAcd | bAef A → gh FOLLOW(A) = {c, e} Se estivermos utilizando a primeira produção de S, desejaremos utilizar o c para fazer a recuperação de erros, e não o e. Como construir um compilador utilizando ferramentas Java – p. 8/2 Um pouco melhor S → aAcd | bAef A → gh FOLLOW(A) = {c, e} Se estivermos utilizando a primeira produção de S, desejaremos utilizar o c para fazer a recuperação de erros, e não o e. Devemos permitir que a cada chamada de A seja informado, por meio de um parâmetro, qual é o token de sincronização a ser utilizado. Como construir um compilador utilizando ferramentas Java – p. 8/2 No JavaCC Construções try/catch Dessa forma, caso não haja um casamento esperado, o método pode fazer a ressincronização. Um erro de sintaxe é sinalizado com um ParseException Como construir um compilador utilizando ferramentas Java – p. 9/2 Tratamento de erro void A() { } { try { "g" "h" } catch (ParseException e) { // tratamento de erro } } Como construir um compilador utilizando ferramentas Java – p. 10/2 Chamada a não-terminais void A() { } { try { "g" B() "h" } catch (ParseException e) { // tratamento de erros de A ou de B } } Como construir um compilador utilizando ferramentas Java – p. 11/2 Chamada a não-terminais Nesse último caso, o não-terminal B pode ou não implementar o tratamento de erro. Se tratar, o método A não muda. Se não tratar, um erro pode ser propagado e tratado em A. Para cada chamada de não-terminal que trata dos erros deve ser passado um conjunto de ressincronização. Como construir um compilador utilizando ferramentas Java – p. 12/2 Esquema geral void S() { RecoverySet x = y = } { ( <a> A(x) <c> <b> A(y) <e> ) } new RecoverySet(<c>), new RecoverySet(<e>); <d> | <f> Como construir um compilador utilizando ferramentas Java – p. 13/2 Esquema geral void A(RecoverySet r) { } { try { <g> <h> } catch (ParseException e) { consumeUntil(r, e, "A"); } } Como construir um compilador utilizando ferramentas Java – p. 14/2 Método consumeUntil Dá mensagem sobre recuperação de erros (ver main); Como construir um compilador utilizando ferramentas Java – p. 15/2 Método consumeUntil Dá mensagem sobre recuperação de erros (ver main); Se conjunto de sincronização não null, vai consumindo os tokens da entrada até achar algum no conjunto. Como construir um compilador utilizando ferramentas Java – p. 15/2 Método consumeUntil Dá mensagem sobre recuperação de erros (ver main); Se conjunto de sincronização não null, vai consumindo os tokens da entrada até achar algum no conjunto. Se um <EOF> for achado, lança outro tipo de exceção ParseEOFException Como construir um compilador utilizando ferramentas Java – p. 15/2 Método consumeUntil Dá mensagem sobre recuperação de erros (ver main); Se conjunto de sincronização não null, vai consumindo os tokens da entrada até achar algum no conjunto. Se um <EOF> for achado, lança outro tipo de exceção ParseEOFException Por isso, todos os métodos devem declarar esse tipo de exceção. Como construir um compilador utilizando ferramentas Java – p. 15/2 Método consumeUntil Dá mensagem sobre recuperação de erros (ver main); Se conjunto de sincronização não null, vai consumindo os tokens da entrada até achar algum no conjunto. Se um <EOF> for achado, lança outro tipo de exceção ParseEOFException Por isso, todos os métodos devem declarar esse tipo de exceção. Incremnta número de erros encontrados. Como construir um compilador utilizando ferramentas Java – p. 15/2 Do começo void program() throws ParseEOFException : { RecoverySet g = new RecoverySet(EOF); } { try { [ classlist(g) ] <EOF> } catch (ParseException e) { consumeUntil(g, e, "program"); } } Como construir um compilador utilizando ferramentas Java – p. 16/2 RecoverySet Implementa um conjunto de inteiros, com algumas operações específicas Construtor RecoverySet(int) boolean contains(int) RecoverySet union(RecoverySet) RecoverySet add(in) RecoverySet remove(in) String toString() Como construir um compilador utilizando ferramentas Java – p. 17/2 classlist void classlist(RecoverySet g) throws ParseEOFException : { RecoverySet f = First.classlist.union(g); } { classdecl(f) [ classlist(g) ] } Não há recuperação de erros. Não há possibilidade de erros sintático. Uso dos conjuntos da classe First Como construir um compilador utilizando ferramentas Java – p. 18/2 classbody void classbody(RecoverySet g) throws ParseEOFException : { RecoverySet f1 = new RecoverySet(RBRACE), f2 = new RecoverySet(SEMICOLON), f3 = First.methoddecl.union(f1), f4 = First.constructdecl.union(f3), f5 = First.vardecl.union(f4); } { try { <LBRACE> [classlist(f5)] (LOOKAHEAD(3) vardecl(f2) <SEMICOLON>)* (constructdecl(f4))* (methoddecl(f3))* <RBRACE> } catch (ParseException e) { consumeUntil(g, e, "classbody"); } } Como construir um compilador utilizando ferramentas Java – p. 19/2 Algumas exceções void lvalue(RecoverySet g) throws ParseEOFException : {} { try { <IDENT> ( <LBRACKET> expression(null) <RBRACKET> | <DOT> <IDENT> [<LPAREN> arglist(null) <RPAREN>] )* } catch (ParseException e) { consumeUntil(g, e, "lvalue"); } } É o ponto “mais baixo” onde se realiza recuperação de erros. a[i + * 3].x = 123; Como construir um compilador utilizando ferramentas Java – p. 20/2 Expression void expression(RecoverySet g) throws ParseEOFException : {} { try { numexpr() [(<LT>|<GT>|<LE>|<GE>|<EQ>|<NEQ>) numexpr()] } catch (ParseException e) { consumeUntil(g, e, "expression"); } } Como construir um compilador utilizando ferramentas Java – p. 21/2 Algumas melhorias A forma de cálculo do conjunto de sincronização é bastante simples. Esse conjunto é muito importante no sucesso da recuperação. Nem sempre é eficiente. Uma análise caso-a-caso pode melhorar a eficiência da estratégia. Ver descrição no capítulo 5. Como construir um compilador utilizando ferramentas Java – p. 22/2