Я пытаюсь написать простой парсер для еще более простого языка, который я пишу. Он состоит из постфиксных выражений. На данный момент у меня проблемы с парсером. Когда я запускаю его на входе 2 2 * test >>
, я получаю исключение MismatchedTokenException.
Кроме того, как я могу реализовать рекурсивный синтаксический анализатор postfix?
Здесь мой код:
grammar star;
options {
language=Python;
output=AST;
ASTLabelType=CommonTree;
}
tokens {DECL;}
//start
// : decl ;
//decl
// : type ID -> ^(DECL type ID)
// ;
program
: (body)+
;
body : (nested WS)*
| (var WS)*
| (get WS)*
;
var
: nested ID '>>'
;
get
: ID '<<'
;
//expressions
term
: INT
;
expr
: term (term operator)*
;
nested
: expr (expr operator)*
;
operator
: ('*' | '+' | '/' | '%' | '-')
;
ID
: ('a'..'z' | 'A'..'Z') ('a..z' | '0'..'9' | 'A'..'Z')*
;
INT
: '0'..'9'+
;
WS
: (' ' | '\n' | '\t' | '\r') {$channel=HIDDEN;}
;
Не все верно:
Вы положили токен WS
на канал HIDDEN
, что делает их недоступными для правил парсера. Таким образом, все теги WS
внутри вашего правила body
неверны.
_ ( ваше последнее редактирование удалило проблему рекурсии left-, но я все равно сделаю это с ней извините, ваш другой вопрос имеет левое рекурсивное правило (expr
), поэтому я оставлю эту информацию здесь) _
ANTLR является LL parser -generator, поэтому вы не можете создавать рекурсивные грамматики left-. Остается рекурсивным:
expr
: term term operator
;
term
: INT
| ID
| expr
;
потому что первый term
внутри правила expr
мог бы соответствовать самому правилу expr
. Как и любой парсер LL, созданный парсер ANTLR не может справиться с левой рекурсией.
Если вы исправите проблему WS
, в правиле body
появится следующее сообщение об ошибке:
(1/7) Decision can match input such as "INT" using multiple alternatives
Это означает, что синтаксический анализатор не может "видеть", к какому правилу принадлежит токен INT
. Это связано с тем, что все ваши альтернативы body
можно повторить нулем или более раз, а также expr
и nested
. И все они могут соответствовать INT
, о чем жалуется ANTLR. Если вы удалите *
следующим образом:
body
: nested
| var
| get
;
// ...
expr
: term (term operator)
;
nested
: expr (expr operator)
;
ошибки исчезнут (хотя это все равно не приведет к правильной обработке вашего ввода!).
Я понимаю, что это может все еще казаться неопределенным, но это не тривиально объяснить (или понять, если вы новичок во всем этом).
Чтобы правильно учитывать рекурсивный expr
внутри expr
, вам нужно оставаться в стороне от левой рекурсии, как я объяснил в # 2. Вы можете сделать это вот так:
expr
: term (expr operator | term operator)*
;
который все еще неоднозначен, но это в случае описания постфиксного выражения с использованием грамматики LL, неизбежного AFAIK. Чтобы решить эту проблему, вы можете включить глобальный откат в разделе options { ... }
грамматики:
options {
language=Python;
output=AST;
backtrack=true;
}
Небольшая демонстрация того, как анализировать рекурсивные выражения, может выглядеть так:
grammar star;
options {
language=Python;
output=AST;
backtrack=true;
}
parse
: expr EOF -> expr
;
expr
: (term -> term) ( expr2 operator -> ^(operator $expr expr2)
| term operator -> ^(operator term term)
)*
;
expr2
: expr
;
term
: INT
| ID
;
operator
: ('*' | '+' | '/' | '%' | '-')
;
ID
: ('a'..'z' | 'A'..'Z') ('a..z' | '0'..'9' | 'A'..'Z')*
;
INT
: '0'..'9'+
;
WS
: (' ' | '\n' | '\t' | '\r') {$channel=HIDDEN;}
;
Тест script:
#!/usr/bin/env python
import antlr3
from antlr3 import *
from antlr3.tree import *
from starLexer import *
from starParser import *
def print_level_order(tree, indent):
print '{0}{1}'.format(' '*indent, tree.text)
for child in tree.getChildren():
print_level_order(child, indent+1)
input = "5 1 2 + 4 * + 3 -"
char_stream = antlr3.ANTLRStringStream(input)
lexer = starLexer(char_stream)
tokens = antlr3.CommonTokenStream(lexer)
parser = starParser(tokens)
tree = parser.parse().tree
print_level_order(tree, 0)
выводит следующий результат:
- + 5 * + 1 2 4 3
что соответствует следующему AST:
expr
оставлен рекурсивным. Спасибо, я исправлю это в своем ответе.
Проблема заключается в том, что правило вашего тела никогда не заканчивается, потому что оно не позволяет ничего сопоставить. Я не запускал ANTLR, мне действительно не нравится возиться с ним, вместо этого я переписал вашу грамматику в C++ (используя генератор парсеров AX), добавил инструкции печати для отслеживания совпадений и получил следующий результат от разбора "2 2 * test >>"
:
parsed term: 2
parsed expr: 2
parsed nested: 2
parsed term: 2
parsed expr: 2
parsed nested: 2
parsed body: 2 2
parsed body:
parsed body: ... here goes your infinite loop
Если вы заинтересованы в отладке этого тестового примера, грамматика AX показана ниже, установите точки останова при печати для прохождения через парсер:
using namespace axe;
typedef std::string::iterator It;
auto space = r_any(" \t\n\r");
auto int_rule = r_numstr();
auto id = r_ident();
auto op = r_any("*+/%-");
auto term = int_rule
>> e_ref([](It i1, It i2)
{
std::cout << "\nparsed term: " << std::string(i1, i2);
});
auto expr = (term & *(term & op))
>> e_ref([](It i1, It i2)
{
std::cout << "\nparsed expr: " << std::string(i1, i2);
});
auto nested = (expr & *(expr & op))
>> e_ref([](It i1, It i2)
{
std::cout << "\nparsed nested: " << std::string(i1, i2);
});
auto get = (id & "<<")
>> e_ref([](It i1, It i2)
{
std::cout << "\nparsed get: " << std::string(i1, i2);
});
auto var = (nested & id & ">>")
>> e_ref([](It i1, It i2)
{
std::cout << "\nparsed var: " << std::string(i1, i2);
});
auto body = (*(nested & space) | *(var & space) | *(get & space))
>> e_ref([](It i1, It i2)
{
std::cout << "\nparsed body: " << std::string(i1, i2);
});
auto program = +(body)
| r_fail([](It i1, It i2)
{
std::cout << "\nparsing failed, parsed portion: "
<< std::string(i1, i2);
});
// test parser
std::ostringstream text;
text << "2 2 * test >>";
std::string str = text.str();
program(str.begin(), str.end());
3 4 * 2 3 + /
что означает, что он не может принимать выражения в качестве терминов.2 2 * test >>
?start
илиprogram
?