gpt4 book ai didi

java - 为什么 ANTLR 省略了最后的标记*并且*没有产生错误?

转载 作者:行者123 更新时间:2023-12-02 02:43:35 28 4
gpt4 key购买 nike

我有一个像这样的语法(任何看起来复杂的东西都是因为它是实际语法的子集,其中包含更多红鲱鱼):

grammar Query;

startExpression
: WS? expression WS? EOF
;

expression
| maybeDefaultBooleanExpression
;

maybeDefaultBooleanExpression
: defaultBooleanExpression
| queryFragment
;

defaultBooleanExpression
: nested += queryFragment (WS nested += queryFragment)+
;

queryFragment
: unquotedQuery
| quotedQuery
;

unquotedQuery
: UNQUOTED
;

quotedQuery
: QUOTED
;

UNQUOTED
: UnquotedStartChar
UnquotedChar*
;

fragment
UnquotedStartChar
: EscapeSequence
| ~( ' ' | '\r' | '\t' | '\u000C' | '\n' | '\\' | ':'
| '"' | '\u201C' | '\u201D' // DoubleQuote
| '\'' | '\u2018' | '\u2019' // SingleQuote
| '(' | ')' | '[' | ']' | '{' | '}' | '~'
| '&' | '|' | '!' | '^' | '?' | '*' | '/' | '+' | '-' | '$' )
;

fragment
UnquotedChar
: EscapeSequence
| ~( ' ' | '\r' | '\t' | '\u000C' | '\n' | '\\' | ':'
| '"' | '\u201C' | '\u201D' // DoubleQuote
| '\'' | '\u2018' | '\u2019' // SingleQuote
| '(' | ')' | '[' | ']' | '{' | '}' | '~'
| '&' | '|' | '!' | '^' | '?' | '*' )
;

QUOTED
: '"'
QuotedChar*
'"'
;

fragment
QuotedChar
: ~( '\\'
| | '\u201C' | '\u201D' // DoubleQuote
| '\r' | '\n' | '?' | '*' )
;


WS : ( ' ' | '\r' | '\t' | '\u000C' | '\n' )+;

如果我直接调用词法分析器:

    CharStream input = CharStreams.fromString("A \"");
QueryLexer lexer = new QueryLexer(input);
lexer.removeErrorListeners();
CommonTokenStream tokens = new CommonTokenStream(lexer);
System.out.println(tokens.LT(0));
System.out.println(tokens.LT(1));
System.out.println(tokens.LT(2));
System.out.println(tokens.LT(3));

我得到:

java.lang.StringIndexOutOfBoundsException: String index out of range: 4

at java.lang.String.checkBounds(String.java:385)
at java.lang.String.<init>(String.java:462)
at org.antlr.v4.runtime.CodePointCharStream$CodePoint8BitCharStream.getText(CodePointCharStream.java:160)
at org.antlr.v4.runtime.Lexer.notifyListeners(Lexer.java:360)
at org.antlr.v4.runtime.Lexer.nextToken(Lexer.java:144)
at org.antlr.v4.runtime.BufferedTokenStream.fetch(BufferedTokenStream.java:169)
at org.antlr.v4.runtime.BufferedTokenStream.sync(BufferedTokenStream.java:152)
at org.antlr.v4.runtime.CommonTokenStream.LT(CommonTokenStream.java:100)

这在某种程度上是有道理的,尽管我认为适当的 ANTLR 异常(exception)可能会更好。

不过,我真正不明白的是,当我通过完整的解析器提供它时:

        QueryParser parser = new QueryParser(tokens);
parser.removeErrorListeners();
parser.addErrorListener(LoggingErrorListener.get());
parser.setErrorHandler(new BailErrorStrategy());

// Performance hack as per the ANTLR v4 FAQ
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
ParseTree expression;
try
{
expression = parser.startExpression();
}
catch (Exception e)
{
// It catches a StringIndexOutOfBoundsException here.

parser.reset();
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
expression = parser.startExpression();
}

我得到:

tokens = {org.antlr.v4.runtime.CommonTokenStream@1811} 
channel = 0
tokenSource = {MyQueryLexer@1810}
tokens = {java.util.ArrayList@1816} size = 3
0 = {org.antlr.v4.runtime.CommonToken@1818} "[@0,0:0='A',<13>,1:0]"
1 = {org.antlr.v4.runtime.CommonToken@1819} "[@1,1:1=' ',<32>,1:1]"
2 = {org.antlr.v4.runtime.CommonToken@1820} "[@2,3:2='<EOF>',<-1>,1:3]"
p = 2
fetchedEOF = true

expression = {MyQueryParser$StartExpressionContext@1813} "[]"
children = {java.util.ArrayList@1827} size = 3
0 = {MyQueryParser$ExpressionContext@1831} "[87]"
1 = {org.antlr.v4.runtime.tree.TerminalNodeImpl@1832} " "
2 = {org.antlr.v4.runtime.tree.TerminalNodeImpl@1833} "<EOF>"

在这里,我本希望得到一个RecognitionException,但不知何故解析成功了,并且最后丢失了 token 数据的无效位。

问题是:

(1) 这是设计使然吗?

(2) 如果是这样,我如何检测到它并将其视为语法错误?

进一步调查

当我寻找捕获 StringIndexOutOfBoudsException 并吃掉它的罪魁祸首时,结果发现它一直到我们的 catch block 中。所以我猜 ANTLR 永远没有机会完成最后一个无效 token 的构建..?

我不完全确定会发生什么,但我想我预计 ANTLR 会捕获它,创建一个无效 token 并继续。

然后,我进一步深入研究,发现 Token#nextToken() 抛出了异常,而文档让这看起来不应该发生,所以我最终提交了有关该问题的票证。

最佳答案

直到最近的版本,ANTLR4 的自适应机制具有以下“功能”:如果 token 流的该部分中只有一种可行的替代方案,则能够从单个 token 缺失和单个额外 token 解析中恢复。现在最近,apparently that behavior has changed.因此,如果您像我一样使用较旧的版本,您仍然会看到自适应解析。也许帕尔和哈威尔会解决这个问题。

和您一样,我认识到需要完美的输入流和零解析错误,无论是否“被忽视”。要创建“严格解析器”,请按照下列步骤操作:

  1. 创建一个名为“StrictErrorStrategy”的类,该类继承/扩展 DefaultErrorStrategy。您需要重写 Recover、RecoverInline 和 Sync 方法。底线是我们对任何出错的情况抛出异常,并且不进行任何尝试在额外/丢失 token 后重新同步代码。这是我的 C# 代码,您的 java 看起来非常相似:

    public class StrictErrorStrategy : DefaultErrorStrategy
    {
    public override void Recover(Parser recognizer, RecognitionException e)
    {
    IToken token = recognizer.CurrentToken;
    string message = string.Format("parse error at line {0}, position {1} right before {2} ", token.Line, token.Column, GetTokenErrorDisplay(token));
    throw new Exception(message, e);
    }

    public override IToken RecoverInline(Parser recognizer)
    {
    IToken token = recognizer.CurrentToken;
    string message = string.Format("parse error at line {0}, position {1} right before {2} ", token.Line, token.Column, GetTokenErrorDisplay(token));
    throw new Exception(message, new InputMismatchException(recognizer));
    }

    public override void Sync(Parser recognizer) { /* do nothing to resync */}
    }
  2. 创建一个实现单个方法的新词法分析器:

    public class StrictLexer : <YourGeneratedLexerNameHere>
    {
    public override void Recover(LexerNoViableAltException e)
    {
    string message = string.Format("lex error after token {0} at position {1}", _lasttoken.Text, e.StartIndex);
    throw new ParseCanceledException(BasicEnvironment.SyntaxError);
    }
    }
  3. 使用您的词法分析器和策略:

      AntlrInputStream inputStream = new AntlrInputStream(stream);
    StrictLexer lexer = new BailLexer(inputStream);
    CommonTokenStream tokenStream = new CommonTokenStream(lexer);
    LISBASICParser parser = new LISBASICParser(tokenStream);
    parser.RemoveErrorListeners();
    parser.ErrorHandler = new StrictErrorStrategy();

这非常有效,来 self 的一个项目的实际代码,该项目对语法错误有“零容忍规则”。我从 Terence Parr 关于 ANTLR4 的伟大著作中获得了代码和想法。

关于java - 为什么 ANTLR 省略了最后的标记*并且*没有产生错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45025556/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com