2017-06-11 13 views
0

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(); 
    } 
} 

AdvanceVisitTerminalているに注意を払う方法:ここでは、コードです。基本的には、すべてのノードが最終的に端末に分解されるという事実に頼っているので、ノードが別のオーバーライド(@VisitAnnotationなど)によって処理されない場合、その端末はそれぞれVisitTerminalになります。 VisitTerminalへの各呼び出しは、インデックスフィールドに追加されます。インデックスフィールドは、ASTと生のテキストの両方と同期していると想定しています。つまり、_indexは、生のテキストのインデックスと現在のトークンのインデックスの両方を表す必要があります。

最近空白で問題が発生しました。文法の規則は、どのパーサー規則にも言及せずにeat up whitespace silentlyと思われます。これにより、空白トークンがVisitTerminalを通過せず、すべてが整列しなくなります。たとえば、このJavaコードスニペットを考えてみます。package後に処理されるまで

package a.b.c.d ; 

は最大、現在のトークンの指標は、生のテキストのインデックスと同じです。しかし、aが処理されるとき、_indexは空白のためにインクリメントされていないので、それは1でなければなりません。 ;が処理されると、それは2倍になります。その後は、次の.の着色を取得しますaaの着色を取得します、dは訪問者に空白トークンにピックアップする方法はありますなど;の着色、

を取得するようにインデックスうんざりすることはありませんか?ありがとう。

答えて

0

あなたはそうです、文法は空白を食べます。しかし、レクサーはそうではありません。私はシンタックスハイライトを扱います。それゆえ明示的なトークン処理は以下のような空白を含みます。主なことは、レクサーのEmitメソッドをオーバーライドすることです。それは私がここで概説しているプロセスの一部です。

  1. トークンのソースにタイプ、開始、停止の長さを記録できる小さなクラスです。

    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; 
        } 
    } 
    
  2. すべての解析に記入するTokenExtentsのリスト。

    public static List<TokenExtent> TokenExtents = new List<TokenExtent>(); 
    
  3. 私はWHITESPACE

    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; 
          } 
    
  4. 私はスタイルを適用、私の形でHiglight()方法を含め、トークンを分類するEmit()メソッドをオーバーライドするカスタムレクサー:

    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; 
          } 
         } 
        } 
    } 
    

は、私はそれがより顕著にするために空白の背景色を変更するので、ここでは次のようになります。あなたが見ることができるように enter image description here

、あなたはレクサーとそこに取得する場合、あなたはこれらの空白が同じトークン扱うことができますレクサールールによって命名された他のトークン。

関連する問題