【问题标题】:ANTLR parsing MismatchedTokenExceptionANTLR 解析 MismatchedTokenException
【发布时间】:2011-06-15 15:04:12
【问题描述】:

我正在尝试为我正在编写的更简单的语言编写一个简单的解析器。它由后缀表达式组成。截至目前,我遇到了解析器的问题。当我在输入 2 2 * test >> 上运行它时,我得到了 MismatchedTokenException。

另外,我将如何实现递归后缀解析器?

这是我的代码:

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;}
    ;

【问题讨论】:

  • 它无法处理3 4 * 2 3 + /,这意味着它不能将表达式作为术语。
  • 您的测试运行情况如何?使用 ANTLRWorks?你用什么规则解析2 2 * test &gt;&gt;startprogram?
  • 我在 ANTLRWorks 解释器中运行它,是的,我用 program 解析它
  • 我也收到了一些关于multiple alternatives的警告
  • 您发布的语法有很多问题。我会在今晚晚些时候尝试解释(如果其他人在我之前没有这样做的话)。

标签: python antlr parser-generator antlrworks


【解决方案1】:

有几点不正确:

1

您已将WS 令牌放在HIDDEN 通道上,这使得它们无法用于解析器规则。因此,body 规则中的所有 WS 标记都不正确。

2

_(您的最新编辑删除了左递归问题,但我仍然会指出这一点抱歉,您的 other question 有左递归规则(expr),所以我会把这个信息留在这里)_

ANTLR 是一个LL parser 生成器,因此您可以创建左递归语法。以下是左递归的:

expr
  :  term term operator
  ;

term
  :  INT
  |  ID
  |  expr
  ;

因为expr 规则中的第一个term 可能匹配expr 规则本身。与任何 LL 解析器一样,ANTLR 生成的解析器无法处理左递归。

3

如果您修复了WS 问题,您的body 规则将产生以下错误消息:

(1/7) 决策可以使用多种选择匹配输入,例如“INT”

这意味着解析器无法“看到”INT 标记属于哪个规则。这是因为您的所有body 替代项都可以重复零次或多次,并且exprnested 也会重复。而且它们都可以匹配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;
}

演示

如何解析递归表达式的小演示如下所示:

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;}
  ;

测试脚本:

#!/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:

【讨论】:

  • 原语法中没有左递归
  • @Gene,是的,你是对的。我以为他把它编辑了,但它是 OP 的 other post,其中 expr 是递归的。谢谢,我会在我的回答中解决这个问题。
【解决方案2】:

问题是您的主体规则永远不会终止,因为它不允许匹配任何内容。我没有启动 ANTLR,我真的不喜欢弄乱它,而是我用 C++ 重写了你的语法(使用 AX 解析器生成器),添加了打印语句来跟踪匹配并通过解析 "2 2 * test &gt;&gt;" 得到以下结果:

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 语法如下所示,在 prints 处设置断点以单步执行解析器:

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());

【讨论】:

  • 这是 a 问题(它永远不会终止),但不是 OP 报告的问题。另外,这个问题显然是关于 ANTLR 和 Python,而不是关于 C++ 和 AXE。
  • @Bart Kiers:这很粗鲁。我给出了问题的识别以及解决问题的更好方法。
  • 对不起,我不是故意粗鲁的(顺便说一句,我们对“粗鲁”也有不同的定义)。但是您错了:OP 指出的问题(MismatchedTokenException)并不是因为 body 规则永远不会终止。正如我已经提到的:OP 明确询问 Python 和 ANTLR,而不是 C++ 和 AXE。
  • 如果 OP 由于主体规则永远不会终止而出现问题,则会引发某种溢出异常,而不是 MismatchedTokenException。就像我说的:body 匹配零个或多个空字符串的事实确实是一个问题,但不是 OP 报告的 问题。
猜你喜欢
  • 2016-11-09
  • 2012-05-31
  • 2016-08-12
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 2012-09-13
相关资源
最近更新 更多