如果输入无效,是否可以抛出异常?

时间:2015-04-23 21:17:32

标签: c# exception antlr4

我有一个简单的ANLTR语法和随附的访问者。除非输入无效,否则一切都很好。如果输入无效,则吞下错误并且我的计算器输出错误。

我已经尝试过实施一个错误监听器,而不是使用词法分析器的Recover方法,而且......好吧......今天还有其他几十件事。有人可以告诉我如何简单地抛出错误而不是吞下坏的“令牌”吗? (我使用引号,因为它们根本不是令牌。我的语法中的字符未定义。)

有效输入:

  

1 + 2 * 3 - 4

输入无效:

  

1 + 2 + 3(4)

如果解析器/词法分析器遇到括号(或任何其他未定义的字符),我想抛出ArgumentException。目前,无效字符似乎只是消失在以太中,解析器就像没有任何错误一样。

如果我使用grun命令在控制台中运行它,我会得到以下输出,因此它在某个级别识别出无效的标记。

  

第1行:9令牌识别错误:'('

     

第1:11行令牌识别错误:')'

和生成的解析树。

enter image description here

BasicMath.g4

grammar BasicMath;

/*
 * Parser Rules
 */

compileUnit : expression+ EOF;

expression :
    expression MULTIPLY expression #Multiplication
    | expression DIVIDE expression #Division
    | expression ADD expression #Addition
    | expression SUBTRACT expression #Subtraction
    | NUMBER #Number
    ; 

/*
 * Lexer Rules
 */

NUMBER : INT; //Leave room to extend what kind of math we can do.

INT : ('0'..'9')+;
MULTIPLY : '*';
DIVIDE : '/';
SUBTRACT : '-';
ADD : '+';

WS : [ \t\r\n] -> channel(HIDDEN);

计算器:

public static class Calculator
{
    public static int Evaluate(string expression)
    {
        var lexer = new BasicMathLexer(new AntlrInputStream(expression));
        var tokens = new CommonTokenStream(lexer);
        var parser = new BasicMathParser(tokens);

        var tree = parser.compileUnit();

        var visitor = new IntegerMathVisitor();

        return visitor.Visit(tree);
    }
}

3 个答案:

答案 0 :(得分:10)

实际上每条错误消息都是由异常引起的。捕获此异常并且解析器尝试恢复。解析树是恢复的结果。

由于词法分析器中发生错误(词法分析器只是不知道字符()),因此必须将错误处理附加到词法分析器。在Java中,这看起来像:

    lexer.addErrorListener(new BaseErrorListener()  {
        @Override
        public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
            throw new RuntimeException(e);
        }
    });

C#语法应该不远。但我建议不要抛出异常。更好地将错误收集到列表中并在词法分析器完成后报告它们,如果错误列表不为空,则不要开始解析。

答案 1 :(得分:5)

@CoronA was right. The error happens in the lexer.。所以,虽然我仍然认为创建一个ErrorStrategy将是更好,这实际上对我有用,我的目标是为未定义的输入抛出异常。

首先,我创建了一个继承自BaseErrorListener 实现IAntlrErrorListener<T>的派生类。第二部分似乎是我的问题。因为我的访问者继承自FooBarBaseVistor<int>,我的错误监听器也需要是类型来向我的词法分析器注册它。

class ThrowExceptionErrorListener : BaseErrorListener, IAntlrErrorListener<int>
{
    //BaseErrorListener implementation; not called in my test, but left it just in case

    public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
    {
        throw new ArgumentException("Invalid Expression: {0}", msg, e);
    }

    //IAntlrErrorListener<int> implementation; this one actually gets called.

    public void SyntaxError(IRecognizer recognizer, int offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
    {
        throw new ArgumentException("Invalid Expression: {0}", msg, e);
    }
}

并更改了我的Calculator类,将我的自定义错误侦听器附加到词法分析器。请注意,您不必像我为实际抛出的错误那样删除ConsoleListener。由于我并没有真正使用它,我认为最好继续这样做。

public static class Calculator
{
    public static int Evaluate(string expression)
    {
        var lexer = new BasicMathLexer(new AntlrInputStream(expression));
        lexer.RemoveErrorListeners(); //removes the default console listener
        lexer.AddErrorListener(new ThrowExceptionErrorListener());

        var tokens = new CommonTokenStream(lexer);
        var parser = new BasicMathParser(tokens);

        var tree = parser.compileUnit();

        var visitor = new IntegerMathVisitor();

        return visitor.Visit(tree);
    }
}

就是这样。抛出参数异常,此测试现在通过。

    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void BadInput()
    {
        var expr = "1 + 5 + 2(3)";
        int value = Calculator.Evaluate(expr);
    }

最后一点。如果你在这里抛出一个RecognitionException,它将再次被吞没。建议使用ParseCancelationException,因为它不是来自RecognitionException,而是选择ArgumentException,因为我觉得这对客户端C#代码最有意义。

答案 2 :(得分:0)

从 ANTLR 4.6 升级到 4.9.2 时,我们注意到解析器的行为发生了变化,一些以前不匹配的文本在语法上没有变化。

一些负输入案例正在使用词法分析器 例如

title eq "Employee" 1234

我使用

覆盖了syntaxError
lexer.addErrorListener(new BaseErrorListener()  {
    @Override
    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
        throw new RuntimeException(e);
    }
});

在调试时发现 Lexer 没有因错误输入的运行时异常而失败。

我们使用 Java 来实现。