ANTLR PopMode:InvalidOperationException

ANTLR PopMode: InvalidOperationException

我一直在尝试调试此错误,但不知道是什么原因造成的。从我通过堆栈跟踪可以看出的情况来看,它是在 PopMode 操作发生时发生的。当我开始走树时发生异常。如果我删除 PopMode 操作,则没有异常,但标记化未正确完成。弹出默认模式时我做错了什么?

词法分析器语法:

lexer grammar BlockLexer;

P_START                 : '!(:' -> pushMode(P_BLOCK);
LINE_START              : '!';
SEPARATOR               : ',' WS* | WS+;
WS                      : ' '   -> channel(HIDDEN);
NEWLINE                 : ((RETURN_CHAR? NEWLINE_CHAR) | RETURN_CHAR) -> popMode;

mode P_BLOCK;
P_CONTENTS              : (LETTER | DIGIT)+ SEPARATOR (LETTER | DIGIT)+ SEPARATOR;

fragment RETURN_CHAR    : '\r' | '\r';
fragment NEWLINE_CHAR   : '\n' | '\n';
fragment DIGIT          : [0-9];
fragment LETTER         : [a-zA-Z];

解析器语法:

grammar BlockParser;
options { tokenVocab = BlockLexer; }

block           : comment_line* unit_info_line? comment_line* date_line comment_line*;
p_line          : P_START P_CONTENTS NEWLINE;
comment_line    : LINE_START .*? NEWLINE;

这是我用来进行解析的 C# 代码:

var lexer = new BlockLexer(new AntlrInputStream(text));
var parser = new BlockParser(new CommonTokenStream(lexer));
var listener = new BlockListener();
new ParseTreeWalker().Walk(listener, parser.block());

堆栈跟踪:

System.InvalidOperationException
    HResult=0x80131509
    Message=Operation is not valid due to the current state of the object.
    Source=Antlr4.Runtime
    StackTrace:
        at Antlr4.Runtime.Lexer.PopMode()
        at Antlr4.Runtime.Atn.LexerPopModeAction.Execute(Lexer lexer)
        at Antlr4.Runtime.Atn.LexerActionExecutor.Execute(Lexer lexer, ICharStream input, Int32 startIndex)
        at Antlr4.Runtime.Atn.LexerATNSimulator.Accept(ICharStream input, LexerActionExecutor lexerActionExecutor, Int32 startIndex, Int32 index, Int32 line, Int32 charPos)
        at Antlr4.Runtime.Atn.LexerATNSimulator.FailOrAccept(SimState prevAccept, ICharStream input, ATNConfigSet reach, Int32 t)
        at Antlr4.Runtime.Atn.LexerATNSimulator.ExecATN(ICharStream input, DFAState ds0)
        at Antlr4.Runtime.Atn.LexerATNSimulator.Match(ICharStream input, Int32 mode)
        at Antlr4.Runtime.Lexer.NextToken()
        at Antlr4.Runtime.BufferedTokenStream.Fetch(Int32 n)
        at Antlr4.Runtime.BufferedTokenStream.Sync(Int32 i)
        at Antlr4.Runtime.BufferedTokenStream.Consume()
        at Antlr4.Runtime.Atn.ParserATNSimulator.ExecATN(DFA dfa, ITokenStream input, Int32 startIndex, SimulatorState initialState)
        at Antlr4.Runtime.Atn.ParserATNSimulator.ExecDFA(DFA dfa, ITokenStream input, Int32 startIndex, SimulatorState state)
        at Antlr4.Runtime.Atn.ParserATNSimulator.AdaptivePredict(ITokenStream input, Int32 decision, ParserRuleContext outerContext, Boolean useContext)
        at Antlr4.Runtime.Atn.ParserATNSimulator.AdaptivePredict(ITokenStream input, Int32 decision, ParserRuleContext outerContext)
        at MyLibrary.Parser.BlockParser.block() in C:\Projects\mylibrary\MyLibrary.Parser\obj\Debug\BlockParser.cs:line 143

您不能弹出空堆栈,因此只有在您之前使用过 pushMode 时才允许调用 popMode(并且只能按您推送的次数调用)。

在您的代码中,当您在默认模式下看到换行符时调用 popMode(当您处于块模式时从不调用,因此您永远无法真正离开块模式),这可以只有当堆栈为空时才会发生(因为你永远不会推送默认模式,所以如果还没有推送任何内容,你只能处于默认模式),所以在默认模式下遇到换行符总是会导致异常,因为你是弹出一个空堆栈。