#java #antlr #antlr4 #lexer
#java #antlr #antlr4 #лексер
Вопрос:
Я написал следующую грамматику для вычисления комбинации логических и реляционных операторов в Java.
В грамматике я могу использовать Equals opertor( =
) только для строкового типа, а не для типов INT и BOOLEAN . Может кто-нибудь, пожалуйста, помочь мне определить, что не так?
Я могу ("a" == "b")
и не могу ("a" == 567)
. Левый операнд является переменным, и я буду заменять значения во время выполнения.
grammar testGrammar;
/*
* Parser rules
*/
conditionalExpression: leftOperand=conditionalExpression operator=LOGICAL_OPERATORS rightOperand=conditionalExpression #LogicalOperators
| '(' conditionalExpression ')' #ParenthesisExpression
| leftOperand=STRING operator=BOOLEAN_RELATIONAL_OPERATORS rightOperand=BOOLEAN #RelationalBooleanOperators
| leftOperand=STRING operator=STRING_RELATIONAL_OPERATORS rightOperand=STRING #RelationalStringOperators
| leftOperand=STRING operator=INT_RELATIONAL_OPERATORS rightOperand=INT #RelationalIntOperators
;
/*
* Lexer rules
*/
STRING: '"'CHAR(CHAR)*'"';
INT:DIGIT ;
BOOLEAN: BOOLEAN_TRUE | BOOLEAN_FALSE;
LOGICAL_OPERATORS: LOGICAL_OR | LOGICAL_AND | LOGICAL_NOT;
STRING_RELATIONAL_OPERATORS: RELATIONAL_EQUALS | RELATIONAL_NOT_EQUAL;
INT_RELATIONAL_OPERATORS: RELATIONAL_EQUALS | RELATIONAL_NOT_EQUAL | RELATIONAL_GREATER_THEN
| RELATIONAL_GREATER_THEN_OR_EQUAL | RELATIONAL_LESS_THEN | RELATIONAL_LESS_THEN_OR_EQUAL;
BOOLEAN_RELATIONAL_OPERATORS: RELATIONAL_EQUALS | RELATIONAL_NOT_EQUAL;
fragment RELATIONAL_EQUALS: '==';
fragment RELATIONAL_NOT_EQUAL: '!=';
fragment RELATIONAL_GREATER_THEN: '>';
fragment RELATIONAL_LESS_THEN: '<';
fragment RELATIONAL_GREATER_THEN_OR_EQUAL: '>=';
fragment RELATIONAL_LESS_THEN_OR_EQUAL: '<=';
fragment LOGICAL_AND: 'amp;amp;';
fragment LOGICAL_OR: '||';
fragment LOGICAL_NOT: '!';
fragment CHAR: [a-zA-Z_];
fragment DIGIT: [0-9];
fragment BOOLEAN_TRUE: 'true';
fragment BOOLEAN_FALSE: 'false';
Ответ №1:
У вас есть несколько правил лексера, которые будут соответствовать вводу ==
или !=
. ANTLR (как и большинство генераторов лексеров) устраняет неоднозначности в правилах лексеров, сначала выбирая тот (ы), который дает самое длинное совпадение (в этом случае все правила приведут к совпадению длиной 2), а затем разрешая связи, выбирая тот, который идет первым в грамматике. Поэтому, когда лексер видит ==
or !=
, он всегда будет генерировать токен типа STRING_RELATIONAL_OPERATORS
.
Обратите внимание, что лексеру все равно, какие токены нужны синтаксическому анализатору прямо сейчас — лексер функционирует независимо от синтаксического анализатора. Он рассматривает только текущий ввод и определенные правила лексера, чтобы решить, какой тип токена создавать. Таким образом, одна и та же последовательность символов всегда будет создавать токен одного и того же типа.
Чтобы исправить вашу грамматику, вы должны определить свои правила лексера, чтобы они не перекрывались, а затем сгруппировать их по своему усмотрению в правилах синтаксического анализатора. Таким образом, вы могли бы иметь одно правило лексера для каждого оператора (возможно, неявно, используя строковые литералы в правилах синтаксического анализатора), а затем просто использовать ('==' | '!=' | ...)
в анализаторе.
Я бы также рекомендовал иметь только одно правило синтаксического анализа для реляционных выражений. Прямо сейчас у вас есть по одному для каждого типа, чтобы запретить сравнение выражений разных типов, но этот подход не масштабируется (что вы собираетесь делать, например, когда вводите переменные?). Вместо этого вы должны просто разрешить неверно типизированное выражение в анализаторе, а затем отклонить их в программе проверки типов, которую вы пишете отдельно.
PS: Чтобы найти эти типы проблем в лексере, это помогает распечатать поток токенов, который был сгенерирован для данного ввода. Вы можете добиться этого, выполнив итерацию по потоку токенов в вашем Java-коде или запустив grun YourGrammarName tokens -tokens yourInputFile
в командной строке.
Комментарии:
1. Спасибо за ваш ответ 🙂 В моем случае я не знаю тип данных переменной и поэтому не смог найти правильный оператор. Нет ли способа повторно использовать один и тот же оператор для разных типов данных?
2. У @KrishnaM есть только одно правило
operand relationalOperator operand
, в которомrelationalOperator
сопоставляются все операторы отношений иoperand
сопоставляются все возможные операнды независимо от типа. Тогда пусть проверка типов позаботится об остальном.3. Если мы введем средство проверки типов, то нам нужно поддерживать отображение типа переменной, верно? В моем случае левый операнд будет переменной.
4. @KrishnaM Как только вы вводите переменные в свой язык, вашей программе проверки типов требуется сопоставление имен переменных с типами, да.