当前位置: 首页 > 知识库问答 >
问题:

访问解析器规则未引用的ANTLR令牌

通安宁
2023-03-14
private class Visitor : JavaBaseVisitor<object>
{
    private readonly string _rawText;
    private readonly SpannableString _text;
    private readonly ISyntaxStyler _styler;

    private int _index;

    internal Visitor(string text, ISyntaxStyler styler)
    {
        _rawText = text;
        _text = new SpannableString(text);
        _styler = styler;
    }

    public override object VisitAnnotation([NotNull] JavaParser.AnnotationContext context)
    {
        Advance("@", SyntaxKind.Annotation);
        VisitAnnotationName(context.annotationName());
        this.VisitChildren(context, 2);
        return null;
    }

    public override object VisitPackageDeclaration([NotNull] JavaParser.PackageDeclarationContext context)
    {
        int index = 0;
        var child = context.GetChild(0);

        while (child is JavaParser.AnnotationContext)
        {
            Visit(child);
            child = context.GetChild(++index);
        }

        Advance("package", SyntaxKind.Keyword);
        this.VisitChildren(context, index + 1);
        return null;
    }

    public override object VisitTerminal(ITerminalNode node)
    {
        Advance(node.Symbol.Text, SyntaxKind.Identifier);
        return null;
    }

    internal SpannableString HighlightText()
    {
        Visit(CreateTree(_rawText));
        return _text;
    }

    private void Advance(int count, SyntaxKind kind)
    {
        var span = _styler.GetSpan(kind);
        _text.SetSpan(span, _index, _index + count, SpanTypes.InclusiveExclusive);
        _index += count;
    }

    private void Advance(string toSkip, SyntaxKind kind)
    {
        int count = toSkip.Length;
        Debug.Assert(string.Compare(_rawText, _index, toSkip, 0, count) == 0);
        Advance(count, kind);
    }

    private static JavaParser.CompilationUnitContext CreateTree(string text)
    {
        var inputStream = new AntlrInputStream(text);
        var lexer = new JavaLexer(inputStream);
        var tokenStream = new CommonTokenStream(lexer);
        var parser = new JavaParser(tokenStream);
        return parser.compilationUnit();
    }
}

需要注意的方法是AdvanceVisitTerminal。实际上,我依赖于这样一个事实,即每个节点最终都分解为终端,因此如果一个节点没有被另一个重写处理(例如visitannotation中的@),那么它的每个终端都将位于visitterminal中。对visitterminal的每次调用都会添加到索引字段中,我假定该字段与AST和原始文本同步。也就是说,_index应该同时表示原始文本中的索引和当前标记的索引。

我最近遇到了一个空白问题。语法规则似乎悄无声息地吃掉了空白,而没有在任何解析器规则中提到它。这会导致空白标记无法通过visitterminal,从而导致所有内容都不对齐。例如,考虑以下Java代码片段:

package a.b.c.d ;

在处理之前,当前令牌的索引与原始文本中的索引相同。但是,当处理时,_index没有增加空格,所以它比应该的要晚1。当处理时,它将比应该的晚2。然后将获得A的颜色,A将获得下一个的颜色。D将获得的颜色;等。

是否有一种方法可以在访问者中使用空白标记,这样索引就不会被搞乱?谢了。

共有1个答案

文喜
2023-03-14

你说得对,语法会吃掉空白。但lexer没有。我处理语法高亮显示--因此也处理显式标记--包括空格,如下所示。主要是重写lexer的emit方法。这是我在这里概述的过程的一部分。

>

  • 一个小类,它允许我记录标记源中的类型、开始和停止长度。

    public class TokenExtent
    {
        public string Name { get; set; }
        public int Start { get; set; }
        public int Length { get; set; }
        public TokenExtent(string name, int start, int stop)
        {
            Name = name;
            Start = start;
            Length = stop - start + 1;
        }
     }
    

    每次解析都会填充的TokenExtents列表。

    public static List<TokenExtent> TokenExtents = new List<TokenExtent>();
    
    public class BailLexer : LISBASICLexer
    {
        public BailLexer(ICharStream input) : base(input) { }
        public override IToken Emit()
        {
            IToken token = base.Emit();
            switch (token.Type)
            {
            case ID :
                {
                TokenExtent extent = new TokenExtent("ID", token.StartIndex, token.StopIndex);
                BasicEnvironment.TokenExtents.Add(extent);
                break;
                }
    
                case COMMENT :
                {
                TokenExtent extent = new TokenExtent("COMMENT", token.StartIndex, token.StopIndex);
                BasicEnvironment.TokenExtents.Add(extent);
                break;
                }
                case WS:
                {
                TokenExtent extent = new TokenExtent("WS", token.StartIndex, token.StopIndex);
                BasicEnvironment.TokenExtents.Add(extent);
                break;
                }
    
    private void Highlight()
    {
        foreach (TokenExtent ext in BasicEnvironment.TokenExtents)
        {
            switch (ext.Name)
            {
                case "ID" :
                {
                    etCode2.Select(ext.Start, ext.Length);
                    etCode2.SelectionColor = Color.Blue;
                    break;
                }
                case "COMMENT" :
                {
                    etCode2.Select(ext.Start, ext.Length);
                    etCode2.SelectionColor = Color.DimGray;
                    break;
                }
                case "WS":
                {
                    etCode2.Select(ext.Start, ext.Length);
                    etCode2.SelectionBackColor = Color.BurlyWood;
                    break;
                }
            }
        }
    }
    

  •  类似资料:
    • 如果没有引用,会导致解析发生更改吗?

    • 现在我得到了:错误。 我知道我的输入被AND和TERM lexer规则匹配,但我希望能够指定TERM是除与AND规则匹配的内容之外的任何内容。

    • ANTLR语法中解析器和词法分析器规则的调用顺序是什么?例如,在以下语法中,输入 223 始终标识为APLHANUMERIC而不是数字

    • 我是ANTLR4的新手,我试图解析此输入 在这个输入中,A应该是一个函数调用,而B应该是一个名为B的变量。但我在lexer中有一个跳过空格的规则。 如何为此输入编写解析器规则,但保留跳过空格的规则 提前感谢

    • CloudGate解析规则可以直接导入使用,不需要任何额外的操作,非常方便! 规则列表 规则名称 下载地址 Surge https://async.be/Rule/Basic/Hosts Shadowrocket https://async.be/Rule/Basic/Hosts 解析规则 简要概述:通过实时同步Hosts信息源达到自动更新,同时使用解析模板进行生成。 无需任何其他操作,导入即可使

    • template.defaults.rules art-template 可以自定义模板解析规则,默认配置了原始语法与标准语法。 修改界定符 // 原始语法的界定符规则 template.defaults.rules[0].test = /<%(#?)((?:==|=#|[=-])?)[ \t]*([\w\W]*?)[ \t]*(-?)%>/; // 标准语法的界定符规则 template.def