this ANTLR v4 grammar fileに基づいて、C#でJavaの構文ハイライターを作成しようとしています。これを行うために、私は現在、ノードを渡すたびに追加するビジターの_index
フィールドを追跡しようとしています。パーサー規則で参照されていないANTLRトークンを訪問する
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();
}
}
Advance
とVisitTerminal
ているに注意を払う方法:ここでは、コードです。基本的には、すべてのノードが最終的に端末に分解されるという事実に頼っているので、ノードが別のオーバーライド(@
、VisitAnnotation
など)によって処理されない場合、その端末はそれぞれVisitTerminal
になります。 VisitTerminal
への各呼び出しは、インデックスフィールドに追加されます。インデックスフィールドは、ASTと生のテキストの両方と同期していると想定しています。つまり、_index
は、生のテキストのインデックスと現在のトークンのインデックスの両方を表す必要があります。
最近空白で問題が発生しました。文法の規則は、どのパーサー規則にも言及せずにeat up whitespace silentlyと思われます。これにより、空白トークンがVisitTerminal
を通過せず、すべてが整列しなくなります。たとえば、このJavaコードスニペットを考えてみます。package
後に処理されるまで
package a.b.c.d ;
は最大、現在のトークンの指標は、生のテキストのインデックスと同じです。しかし、a
が処理されるとき、_index
は空白のためにインクリメントされていないので、それは1でなければなりません。 ;
が処理されると、それは2倍になります。その後は、次の
.
の着色を取得しますa
、a
の着色を取得します、d
は訪問者に空白トークンにピックアップする方法はありますなど;
の着色、
を取得するようにインデックスうんざりすることはありませんか?ありがとう。