如果输入无效,是否可以抛出异常?
Is it possible to throw an exception if the input isn't valid?
我有一个简单的 ANLTR 语法和附带的 Visitor。一切正常,除非输入无效。如果输入无效,错误会被吞没,我的计算器会输出错误的结果。
我已经尝试实现一个错误侦听器,覆盖词法分析器的 Recover
方法,以及......好吧......今天有六件其他事情。有人可以告诉我如何简单地抛出错误而不是吞下坏的“令牌”吗? (我使用引号是因为它们 根本不是 标记。字符在我的语法中未定义。)
有效输入:
1 + 2 * 3 - 4
无效输入:
1 + 2 + 3(4)
如果 parser/lexer 遇到括号(或任何其他未定义的字符),我想抛出一个 ArgumentException
。目前,无效字符似乎只是消失在以太中,解析器只是缓慢地前进,就像没有错一样。
如果我在控制台中使用 grun
命令 运行 它,我会得到以下输出,因此它在某种程度上识别了无效标记。
line 1:9 token recognition error at: '('
line 1:11 token recognition error at: ')'
和这个生成的解析树。
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);
}
}
其实每条错误信息都是由异常引起的。此异常被捕获并且解析器尝试恢复。分析树是恢复的结果。
由于错误发生在词法分析器中(词法分析器只是不知道字符(
或)
),所以必须将错误处理附加到词法分析器中。在 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# 语法应该与此相去不远。但我建议不要抛出异常。最好将错误收集到列表中并在词法分析器完成后报告它们,如果错误列表不为空则不要开始解析。
。因此,虽然我仍然认为创建一个 ErrorStrategy 会 更好 ,但这对我来说确实有效,我的目标是为未定义的输入抛出异常。
首先,我创建了一个继承自 BaseErrorListener
和 的派生 class 实现 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
class 以将我的自定义错误侦听器附加到 lexer。请注意,您不必像我为实际抛出错误所做的那样删除 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# 代码最有意义。
从 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 进行此实施。
我有一个简单的 ANLTR 语法和附带的 Visitor。一切正常,除非输入无效。如果输入无效,错误会被吞没,我的计算器会输出错误的结果。
我已经尝试实现一个错误侦听器,覆盖词法分析器的 Recover
方法,以及......好吧......今天有六件其他事情。有人可以告诉我如何简单地抛出错误而不是吞下坏的“令牌”吗? (我使用引号是因为它们 根本不是 标记。字符在我的语法中未定义。)
有效输入:
1 + 2 * 3 - 4
无效输入:
1 + 2 + 3(4)
如果 parser/lexer 遇到括号(或任何其他未定义的字符),我想抛出一个 ArgumentException
。目前,无效字符似乎只是消失在以太中,解析器只是缓慢地前进,就像没有错一样。
如果我在控制台中使用 grun
命令 运行 它,我会得到以下输出,因此它在某种程度上识别了无效标记。
line 1:9 token recognition error at: '('
line 1:11 token recognition error at: ')'
和这个生成的解析树。
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);
}
}
其实每条错误信息都是由异常引起的。此异常被捕获并且解析器尝试恢复。分析树是恢复的结果。
由于错误发生在词法分析器中(词法分析器只是不知道字符(
或)
),所以必须将错误处理附加到词法分析器中。在 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# 语法应该与此相去不远。但我建议不要抛出异常。最好将错误收集到列表中并在词法分析器完成后报告它们,如果错误列表不为空则不要开始解析。
首先,我创建了一个继承自 BaseErrorListener
和 的派生 class 实现 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
class 以将我的自定义错误侦听器附加到 lexer。请注意,您不必像我为实际抛出错误所做的那样删除 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# 代码最有意义。
从 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 进行此实施。