ANTLR4将所有令牌存储在特定通道中

ANTLR4将所有令牌存储在特定通道中,antlr,antlr4,Antlr,Antlr4,我有一个lexer,它将解析器感兴趣的每个标记放入默认通道,并将所有注释标记放入通道1 默认通道用于创建实际的树,而注释通道用于分离标记和存储所有注释 看看这个涂鸦: 在第12.1章p。206-208在最终ANTLR4参考文献中,存在注释标记在标记流内移位的类似情况。表示的方法是在解析器内的exit方法中读取注释通道 在我看来,对于我的问题来说,这是一个非常粗糙的选择,因为我不想让我的听众被回过头来的操作压垮。是否有可能重写将标记放入注释通道的方法 看来您误解了ANTLR中频道的工作方式。发生

我有一个lexer,它将解析器感兴趣的每个标记放入默认通道,并将所有注释标记放入通道1

默认通道用于创建实际的树,而注释通道用于分离标记和存储所有注释

看看这个涂鸦:

在第12.1章p。206-208在最终ANTLR4参考文献中,存在注释标记在标记流内移位的类似情况。表示的方法是在解析器内的exit方法中读取注释通道


在我看来,对于我的问题来说,这是一个非常粗糙的选择,因为我不想让我的听众被回过头来的操作压垮。是否有可能重写将标记放入注释通道的方法

看来您误解了ANTLR中频道的工作方式。发生的情况是,lexer在令牌初始化期间分配默认通道(只是一个数字)。只有当lexer找到
->channel()
命令或您在lexer操作中明确更改它时,该值才会更改。因此,在侦听器中没有什么事情可以做,也没有什么事情可以过滤掉这些标记


稍后,当您希望获取给定通道中的所有令牌(即分配了特定通道号的所有令牌)时,您可以迭代令牌流返回的所有令牌,并比较通道值。或者,您可以创建一个新的
CommonTokenStream
实例,并将其传递给您感兴趣的频道。然后,它将只从该通道向您提供这些令牌(它使用令牌源,例如lexer,获取实际令牌并缓存它们)。

我发现,有一种简单的方法可以覆盖令牌的创建方式。为此,可以重写CommonTokenFactory中的一个方法,并将其交给Lexer。在这一点上,我可以检查通道,并且我能够在单独的集合中推送令牌

在我看来,这有点不成熟,但我以后不需要迭代整个commonTokenStream

这段代码只是为了演示背后的思想(用C#)

内部类头分析器
{
#区域方法
内部空隙分析集管(流头SourceStream)
{
var AntlStream=
新AntlInputStream(headerSourceStream);
var mcrLexer=新的mcrLexer(antlrFileStream);
var commentSaverTokenFactory=new MyTokenFactory();
mcrLexer.TokenFactory=commentSaverTokenFactory;
var commonTokenStream=新的commonTokenStream(mcrLexer);
var mcrParser=新的mcrParser(commonTokenStream);
mcrParser.AddErrorListener(新的DefaultErrorListener());
MCRParser.ProgramContext树;
尝试
{
tree=mcrParser.program();//创建树
}
捕获(SyntaxErrorException SyntaxErrorException)
{
抛出新的NotImplementedException();
}
var headerContext=新的headerContext();
var headListener=新的headListener(headerContext);
ParseTreeWalker.Default.Walk(headListener,tree);
var comments=commentSaverTokenFactory.CommentTokens;//包含所有注释:)
}
#端区
}
内部类MyTokenFactory:CommonTokenFactory
{
内部只读列表注释标记=新列表();
公共重写CommonToken创建(元组源、int类型、字符串文本、int通道、int开始、int停止、int行、int位置内联)
{
var token=base.Create(源、类型、文本、通道、开始、停止、行、charPositionInLine);
if(token.Channel==1)
{
添加(令牌);
}
返回令牌;
}
}

也许有更好的方法。对于我的用例,它的工作原理与预期一样。

您尝试了什么?词汇语法?您寻求的确切结果是什么?感谢您的详细回复。您是对的,没有真正的物理通道,但是标记上有数字。有一种可能性,你也可以在你的词汇量别名。这就是我真正做的。因此,考虑通道0作为默认值,通道1作为注释。这很好:var tokens=commonTokenStream.GetTokens();var commentTokens=tokens.Where(token=>token.Channel==1);但我有点担心这条流是一条流。它永远不会被冲走吗?线性搜索的性能很差,不是吗?令牌流不是操作系统流。它基本上是lexer生成的所有令牌的缓存。lexer始终只有一个令牌(其上的当前正在使用)。公共令牌流在内存中保留一个列表,以便轻松查找。该列表的迭代几乎是瞬时的。不需要为特定通道上的令牌保留另一个列表。
internal class HeadAnalyzer
{
    #region Methods

    internal void AnalyzeHeader(Stream headerSourceStream)
    {
        var antlrFileStream =
            new AntlrInputStream(headerSourceStream);
        var mcrLexer = new MCRLexer(antlrFileStream);

        var commentSaverTokenFactory = new MyTokenFactory();
        mcrLexer.TokenFactory = commentSaverTokenFactory;
        var commonTokenStream = new CommonTokenStream(mcrLexer);

        var mcrParser = new MCRParser(commonTokenStream);
        mcrParser.AddErrorListener(new DefaultErrorListener());

        MCRParser.ProgramContext tree;
        try
        {
            tree = mcrParser.program(); // create the tree
        }
        catch (SyntaxErrorException syntaxErrorException)
        {
            throw new NotImplementedException();
        }
        var headerContext = new HeaderContext();
        var headListener = new HeadListener(headerContext);
        ParseTreeWalker.Default.Walk(headListener, tree);

        var comments = commentSaverTokenFactory.CommentTokens; // contains all comments :)
    }

    #endregion
}

internal class MyTokenFactory : CommonTokenFactory
{
    internal readonly List<CommonToken> CommentTokens = new List<CommonToken>();

    public override CommonToken Create(Tuple<ITokenSource, ICharStream> source, int type, string text, int channel, int start, int stop, int line, int charPositionInLine)
    {
        var token =  base.Create(source, type, text, channel, start, stop, line, charPositionInLine);
        if (token.Channel == 1)
        {
            CommentTokens.Add(token);
        }
        return token;
    }
}