【问题标题】:ANTLR: Syntax Errors are ignored when running parser programmaticallyANTLR:以编程方式运行解析器时忽略语法错误
【发布时间】:2011-07-13 11:39:15
【问题描述】:

我目前正在使用 ANTLR 创建一个或多或少简单的表达式评估器。

我的语法很简单(至少我希望如此),看起来像这样:

grammar SXLGrammar;

options {
  language = Java;
  output   = AST;
}

tokens {
  OR  = 'OR';
  AND = 'AND';
  NOT = 'NOT';
  GT  = '>'; //greater then
  GE  = '>='; //greater then or equal
  LT  = '<'; //lower then
  LE  = '<='; //lower then or equal
  EQ  = '=';
  NEQ = '!='; //Not equal
  PLUS = '+';
  MINUS = '-';
  MULTIPLY = '*';
  DIVISION = '/';
  CALL;
}

@header {
package somepackage;
}

@members {

}


@lexer::header {
package rise.spics.sxl;
}

rule
  :  ('='|':')! expression 
  ;

expression
    : booleanOrExpression
    ;

booleanOrExpression
    :
    booleanAndExpression ('OR'^ booleanAndExpression)*
    ;

booleanAndExpression
  :
  booleanNotExpression ('AND'^ booleanNotExpression)*
  ;

booleanNotExpression
  :
  ('NOT'^)? booleanAtom
  ;

booleanAtom
  :
  | compareExpression
  ;

compareExpression
    :
    commonExpression (('<' | '>' | '=' | '<=' | '>=' | '!=' )^ commonExpression)?
    ;

commonExpression
  :
  multExpr
  (
    (
      '+'^
      | '-'^
    )
    multExpr
  )*
  | DATE
  ;

multExpr
  :
  atom (('*'|'/')^ atom)*
  | '-'^ atom
  ;

atom
  :
  INTEGER
  | DECIMAL
  | BOOLEAN
  | ID
  | '(' expression ')' -> expression
  | functionCall
  ;

functionCall
  :
  ID '(' arguments ')' -> ^(CALL ID arguments?)
  ;

arguments
  :
  (expression) (','! expression)*
  |  WS
  ;

BOOLEAN
  :
  'true'
  | 'false'
  ;

ID
  :
  (
    'a'..'z'
    | 'A'..'Z'
  )+
  ;

INTEGER
  :
  ('0'..'9')+ 
  ;

DECIMAL
  :
  ('0'..'9')+ ('.' ('0'..'9')*)?
  ;

DATE
  :
  '!' '0'..'9' '0'..'9' '0'..'9' '0'..'9' '-' '0'..'9' '0'..'9' '-' '0'..'9' '0'..'9' (' ' '0'..'9' '0'..'9' ':''0'..'9' '0'..'9' (':''0'..'9' '0'..'9')?)?
  ;

WS
  :  (' '|'\t' | '\n' | '\r' | '\f')+ { $channel = HIDDEN; };

现在,如果我尝试解析像“= true NOT true”这样的无效表达式,eclipse 插件的图形测试工具会抛出 NoViableAltException:第 1:6 行在输入“NOT”时没有可行的替代方案,这是正确的并且应该的。

现在,如果我尝试解析 Java 程序中的表达式,则什么也不会发生。程序

    String expression = "=true NOT false";

    CharStream input = new ANTLRStringStream(expression);
    SXLGrammarLexer lexer = new SXLGrammarLexer(input);
    TokenStream tokenStream = new CommonTokenStream(lexer);
    SXLGrammarParser parser = new SXLGrammarParser(tokenStream);
    CommonTree tree = (CommonTree) parser.rule().getTree();
    System.out.println(tree.toStringTree());
    System.out.println(parser.getNumberOfSyntaxErrors());

会输出:

true
0

这意味着,解析器创建的 AST 存在一个节点并忽略其余节点。我想在我的应用程序中处理语法错误,但如果生成的解析器没有发现任何错误,这是不可能的。

我还尝试通过使用以下内容覆盖 displayRecognitionError() 方法来更改解析器:

public void displayRecognitionError(String[] tokenNames,
                                    RecognitionException e) {
    String msg = getErrorMessage(e, tokenNames);
    throw new RuntimeException("Error at position "+e.index+" " + msg);
} 

但是 displayRecognitionError 永远不会被调用。

如果我尝试“=1+”之类的操作,则会显示错误。我猜我的语法有问题,但是为什么eclipse插件会抛出该错误而生成的解析器没有?

【问题讨论】:

    标签: java eclipse-plugin antlr antlr3


    【解决方案1】:

    如果您希望rule 使用整个令牌流,您必须指定您希望输入结束的位置。像这样:

    rule
      :  ('='|':')! expression EOF
      ;
    

    如果没有 EOF,您的解析器会将 true 读取为布尔值并忽略其余部分。

    【讨论】:

    • 谢谢,就是这样。想知道为什么我在“权威的 ANTLR 参考”中找不到这些信息...
    • @huzi,错了,EOF 令牌在权威的 ANTLR 参考中已多次解释。如果你没有指定你的令牌应该一直消耗到EOF,解析器将简单地尝试消耗一个expression(前面有一个=:),你的输入"=true"。在解析器上调用 rule() 方法后,您可以调用另一种方法来使用剩余的令牌(输入:" NOT false"):这就是您看不到错误的原因(您的 Eclipse 插件所做的是不相关,因为它只是一个围绕 ANTLR 的工具,它本身不是 ANTLR)。
    • @Arne,我通过删除您谦虚的“我认为”来稍微编辑您的答案:) 并稍微改写一下。如果您不满意,请随时恢复,我不会干预它。
    猜你喜欢
    • 1970-01-01
    • 2019-11-29
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    相关资源
    最近更新 更多