Разбор ANTLR MismatchedTokenException

1

Я пытаюсь написать простой парсер для еще более простого языка, который я пишу. Он состоит из постфиксных выражений. На данный момент у меня проблемы с парсером. Когда я запускаю его на входе 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;}
    ;
  • 0
    Он не может обрабатывать 3 4 * 2 3 + / что означает, что он не может принимать выражения в качестве терминов.
  • 0
    Как вы проводите свои тесты? С ANTLRWorks? По какому правилу вы анализировали 2 2 * test >> ? start или program ?
Показать ещё 6 комментариев
Теги:
antlr
antlrworks
parser-generator

2 ответа

4
Лучший ответ

Не все верно:

1

Вы положили токен WS на канал HIDDEN, что делает их недоступными для правил парсера. Таким образом, все теги WS внутри вашего правила body неверны.

2

_ ( ваше последнее редактирование удалило проблему рекурсии left-, но я все равно сделаю это с ней извините, ваш другой вопрос имеет левое рекурсивное правило (expr), поэтому я оставлю эту информацию здесь) _

ANTLR является LL parser -generator, поэтому вы не можете создавать рекурсивные грамматики left-. Остается рекурсивным:

expr
  :  term term operator
  ;

term
  :  INT
  |  ID
  |  expr
  ;

потому что первый term внутри правила expr мог бы соответствовать самому правилу expr. Как и любой парсер LL, созданный парсер ANTLR не может справиться с левой рекурсией.

3

Если вы исправите проблему 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)
    ;

ошибки исчезнут (хотя это все равно не приведет к правильной обработке вашего ввода!).

Я понимаю, что это может все еще казаться неопределенным, но это не тривиально объяснить (или понять, если вы новичок во всем этом).

4

Чтобы правильно учитывать рекурсивный expr внутри expr, вам нужно оставаться в стороне от левой рекурсии, как я объяснил в # 2. Вы можете сделать это вот так:

expr
  :  term (expr operator | term operator)*
  ;

который все еще неоднозначен, но это в случае описания постфиксного выражения с использованием грамматики LL, неизбежного AFAIK. Чтобы решить эту проблему, вы можете включить глобальный откат в разделе options { ... } грамматики:

options {
  language=Python;
  output=AST;
  backtrack=true;
}

Demo

Небольшая демонстрация того, как анализировать рекурсивные выражения, может выглядеть так:

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:

Изображение 174551

  • 0
    в исходной грамматике нет рекурсии
  • 0
    @ Джин, да, ты прав. Я предполагал, что он отредактировал это, но это был другой пост OP, где expr оставлен рекурсивным. Спасибо, я исправлю это в своем ответе.
-1

Проблема заключается в том, что правило вашего тела никогда не заканчивается, потому что оно не позволяет ничего сопоставить. Я не запускал 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());
  • 0
    Это проблема (что она никогда не заканчивается), но не проблема ОП сообщила о. Кроме того, вопрос явно касается ANTLR и Python, а не C ++ и AX.
  • 0
    @ Барт Киерс: это довольно грубо. Я дал как определение проблемы, так и лучший способ ее решения.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню