Проблема JavaCC. Сгенерированный код не находит все ошибки анализа.
-
19-09-2019 - |
Вопрос
Только начал с JavaCC.Но у меня с ним странное поведение.Я хочу проверить ввод в виде токенов (букв и цифр), которые объединены знаками (+, -, /) и могут содержать круглые скобки.Надеюсь, это было понятно :)
В основном методе есть строка, которая должна выдавать ошибку, поскольку у нее одна открывающая и две закрывающие скобки, но я не получаю исключения синтаксического анализа --> Почему?
Кто-нибудь знает, почему я не получаю исключение?
При первой попытке я боролся с левой рекурсией и конфликтами выбора, но сумел их преодолеть.Может там я представил проблему?!
Ох - и, возможно, мое решение не очень хорошее - игнорируйте этот факт...а лучше дайте совет ;-)
Файл:КодПарсер.jj
options {
STATIC=false;
}
PARSER_BEGIN(CodeParser)
package com.testing;
import java.io.StringReader;
import java.io.Reader;
public class CodeParser {
public CodeParser(String s)
{
this((Reader)(new StringReader(s)));
}
public static void main(String args[])
{
try
{
/** String has one open, but two closing parenthesis --> should produce parse error */
String s = "A+BC+-(2XXL+A/-B))";
CodeParser parser = new CodeParser(s);
parser.expression();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
PARSER_END(CodeParser)
TOKEN:
{
<code : ("-")?(["A"-"Z", "0"-"9"])+ >
| <op : ("+"|"/") >
| <not : ("-") >
| <lparenthesis : ("(") >
| <rparenthesis : (")") >
}
void expression() :
{
}
{
negated_expression() | parenthesis_expression() | LOOKAHEAD(2) operator_expression() | <code>
}
void negated_expression() :
{
}
{
<not>parenthesis_expression()
}
void parenthesis_expression() :
{
}
{
<lparenthesis>expression()<rparenthesis>
}
void operator_expression() :
{
}
{
<code><op>expression()
}
Редактировать - 16.11.2009
Теперь я попробовал ANTLR.
Я изменил некоторые термины, чтобы они лучше соответствовали моей проблемной области.Я придумал следующий код (используя ответы на этом сайте), который, похоже, теперь работает:
grammar Code;
CODE : ('A'..'Z'|'0'..'9')+;
OP : '+'|'/';
start : terms EOF;
terms : term (OP term)*;
term : '-'? CODE
| '-'? '(' terms ')';
И кстати...ANTLRWORKS — отличный инструмент для отладки/визуализации!Мне очень помогло.
Дополнительная информация
Приведенный выше код соответствует таким вещам, как:
(-Z19+-Z07+((FV+((M005+(M272/M276))/((M278/M273/M642)+-M005)))/(FW+(M005+(M273/M278/M642)))))+(-Z19+-Z07+((FV+((M005+(M272/M276))/((M278/M273/M642/M651)+-M005)))/(FW+(M0))))
Решение
То, что говорит Кгрегори, является правильным ответом.Вы можете увидеть это, если создадите грамматику с опцией DEBUG_PARSER и затем запустите ее:
$ javacc -debug_parser -output_directory=com/testing/ CodeParser.jj && javac com/testing/*.java && java -cp . com.testing.CodeParser Java Compiler Compiler Version 5.0 (Parser Generator) (type "javacc" with no arguments for help) Reading from file CodeParser.jj . . . File "TokenMgrError.java" is being rebuilt. File "ParseException.java" is being rebuilt. File "Token.java" is being rebuilt. File "SimpleCharStream.java" is being rebuilt. Parser generated successfully. Call: expression Call: operator_expression Consumed token: <<code>: "A" at line 1 column 1> Consumed token: <<op>: "+" at line 1 column 2> Call: expression Call: operator_expression Consumed token: <<code>: "BC" at line 1 column 3> Consumed token: <<op>: "+" at line 1 column 5> Call: expression Call: negated_expression Consumed token: <"-" at line 1 column 6> Call: parenthesis_expression Consumed token: <"(" at line 1 column 7> Call: expression Call: operator_expression Consumed token: <<code>: "2XXL" at line 1 column 8> Consumed token: <<op>: "+" at line 1 column 12> Call: expression Call: operator_expression Consumed token: <<code>: "A" at line 1 column 13> Consumed token: <<op>: "/" at line 1 column 14> Call: expression Consumed token: <<code>: "-B" at line 1 column 15> Return: expression Return: operator_expression Return: expression Return: operator_expression Return: expression Consumed token: <")" at line 1 column 17> Return: parenthesis_expression Return: negated_expression Return: expression Return: operator_expression Return: expression Return: operator_expression Return: expression
Видеть, что?Последний использованный токен — это предпоследний символ — предпоследняя правая скобка.
Если вам нужно исключение, опять же, как сказал Кгрегори, вы можете добавить новую продукцию верхнего уровня под названием «файл» или «данные» или что-то в этом роде и завершить ее токеном.Таким образом, любые висячие скобки, подобные этой, вызовут ошибку.Вот грамматика, которая делает это:
options { STATIC=false; } PARSER_BEGIN(CodeParser) package com.testing; import java.io.StringReader; import java.io.Reader; public class CodeParser { public CodeParser(String s) { this((Reader)(new StringReader(s))); } public static void main(String args[]) { try { /** String has one open, but two closing parenthesis --> should produce parse error */ String s = "A+BC+-(2XXL+A/-B))"; CodeParser parser = new CodeParser(s); parser.file(); } catch(Exception e) { e.printStackTrace(); } } } PARSER_END(CodeParser) TOKEN: { <code : ("-")?(["A"-"Z", "0"-"9"])+ > | <op : ("+"|"/") > | <not : ("-") > | <lparenthesis : ("(") > | <rparenthesis : (")") > } void file() : {} { expression() <EOF> } void expression() : { } { negated_expression() | parenthesis_expression() | LOOKAHEAD(2) operator_expression() | <code> } void negated_expression() : { } { <not>parenthesis_expression() } void parenthesis_expression() : { } { <lparenthesis>expression()<rparenthesis> } void operator_expression() : { } { <code><op>expression() }
И пример запуска:
$ javacc -debug_parser -output_directory=com/testing/ CodeParser.jj && javac com/testing/*.java && java -cp . com.testing.CodeParser Java Compiler Compiler Version 5.0 (Parser Generator) (type "javacc" with no arguments for help) Reading from file CodeParser.jj . . . File "TokenMgrError.java" is being rebuilt. File "ParseException.java" is being rebuilt. File "Token.java" is being rebuilt. File "SimpleCharStream.java" is being rebuilt. Parser generated successfully. Call: file Call: expression Call: operator_expression Consumed token: <<code>: "A" at line 1 column 1> Consumed token: <<op>: "+" at line 1 column 2> Call: expression Call: operator_expression Consumed token: <<code>: "BC" at line 1 column 3> Consumed token: <<op>: "+" at line 1 column 5> Call: expression Call: negated_expression Consumed token: <"-" at line 1 column 6> Call: parenthesis_expression Consumed token: <"(" at line 1 column 7> Call: expression Call: operator_expression Consumed token: <<code>: "2XXL" at line 1 column 8> Consumed token: <<op>: "+" at line 1 column 12> Call: expression Call: operator_expression Consumed token: <<code>: "A" at line 1 column 13> Consumed token: <<op>: "/" at line 1 column 14> Call: expression Consumed token: <<code>: "-B" at line 1 column 15> Return: expression Return: operator_expression Return: expression Return: operator_expression Return: expression Consumed token: <")" at line 1 column 17> Return: parenthesis_expression Return: negated_expression Return: expression Return: operator_expression Return: expression Return: operator_expression Return: expression Return: file com.testing.ParseException: Encountered " ")" ") "" at line 1, column 18. Was expecting: <EOF> at com.testing.CodeParser.generateParseException(CodeParser.java:354) at com.testing.CodeParser.jj_consume_token(CodeParser.java:238) at com.testing.CodeParser.file(CodeParser.java:34) at com.testing.CodeParser.main(CodeParser.java:22)
Вуаля!Исключение.
Другие советы
Из Часто задаваемые вопросы по Java CC:
4.7 Я добавил спецификацию LOOKAHEAD, и предупреждение исчезло;значит ли это, что я исправил проблему?
Нет.JavaCC не будет сообщать о предупреждениях о конфликте выбора, если вы используете спецификацию LOOKAHEAD.А отсутствие предупреждения это не значит, что вы правильно решили задачу, это просто означает, что вы добавили спецификацию LOOKAHEAD.
Я бы начал с попытки избавиться от конфликта, не используя сначала просмотр вперед.
Проблема в том, что вы не получаете ошибку при использовании парсера, верно?Не то чтобы генератор синтаксического анализатора утверждал, что грамматика неверна (что, похоже, обсуждается в другом ответе).
Если это так, то я подозреваю, что вы видите проблему, потому что синтаксический анализатор правильно соответствует выражение производство, а затем игнорирует последующий ввод.Я давно не использовал JavaCC, но iirc не выдавал ошибку из-за недостижения конца потока.
Большинство грамматик имеют явную продукцию верхнего уровня, соответствующую всему файлу, и выглядит примерно так (я уверен, что синтаксис неправильный, как я уже сказал, это было давно):
input : ( expression ) *
Или, возможно, вы можете использовать токен EOF, если хотите обработать только одно выражение.