ANTLR3语法在ICSharpCode.TextEditor中突出显示隐藏通道

问题描述:

我在开发我们的小型DSL方面取得了一些进展,但在尝试突出显示我们正在使用的TextEditorControl中的注释时遇到了问题。 ICSharpCode控件是顺理成章的,并且与ANTLR相结合,它为DSL提供了一个很好的平台。ANTLR3语法在ICSharpCode.TextEditor中突出显示隐藏通道

我有一个工作的语法和词法分析器,并且在文本编辑器中写了一个突出显示策略,它也很好。 DSL拒绝正确着色的唯一因素是我对隐藏通道的“评论”。

Comment 
    : '//' ~('\r' | '\n')* {$channel=Hidden;} 
    | '/*' .* '*/' {$channel=Hidden;}   
    ; 

令人沮丧的事情是,我可以突出显示工作,如果我走了评论lexrule关闭隐藏通道......但是当我做解析器停止的最后一段文字后评估过程中解析以下评论。

作为一个例子;此工程时,评论已被隐藏,但在第一个“ABC”停止解析的时候都没有

//Comment 

    abc=[7,8,9]; 

    return abc[2]; 

我一直在试图单独访问隐藏的通道,这样我可以在默认和隐藏的记号列表可能合并成一个列表按开始索引排序,然后突出显示,但我没有使用CommonReokenStream构造函数的BaseRecognizer.Hidden参数。

我在凸显文本编辑线路电流的尝试看起来像这样

private void MarkUsingParalexTokens(IDocument document, LineSegment line) 
    { 
     var text = document.GetText(line).ToLower(); 
     var input = new ANTLRStringStream(text); 
     _lexer.CharStream = input; 
     _tokens = new CommonTokenStream(_lexer, BaseRecognizer.Hidden); 
     //_tokens.TokenSource =_lexer; 


     var wordStart = 0; 
     if (_tokens.Count > 1) 
     { 
      do 
      { 
       _tokens.Consume(); 

      } while (_tokens.LastToken.Type != ParalexLexer.EOF); 



      var tokenList = _tokens.GetTokens(); 

      var tokenEnum = tokenList.GetEnumerator(); 

      var tokenAvailable = tokenEnum.MoveNext(); 
      if (tokenAvailable) 
      { 
       for (var i = 0; i < text.Length; i++) 
       { 
        var token = tokenEnum.Current; 
        if (token != null) 
        { 
         var c = text[i]; 
         if (c == ' ' || c == '\t') 
         { 
          if (i > wordStart) 
           AddWord(document, line, wordStart, i); 
          line.Words.Add(c == ' ' ? TextWord.Space : TextWord.Tab); 
          wordStart = i + 1; 
         } 
         else 
         { 
          var atStartOfToken = (i == token.StartIndex); 

          if (atStartOfToken) 
          { 
           if (i > wordStart) 
            AddWord(document, line, wordStart, i); 

           var tokenLength = token.StopIndex - token.StartIndex + 1; 

           AddWord(document, line, i, tokenLength, token); 
           tokenEnum.MoveNext(); 
           wordStart = i + tokenLength; 
           i = wordStart - 1; 
          } 
         } 
        } 

       } 

      } 
     } 

     if (wordStart < line.Length) 
       AddWord(document, line, wordStart, line.Length); 
    } 

    void AddWord(IDocument document, LineSegment line, int startOffset, int length, IToken token = null) 
    { 
     if (length==0) return; 

     var hasSpecialColor = token != null; 
     var color = hasSpecialColor ? GetColor(token) : _highlightColors["Default"]; 

     line.Words.Add(new TextWord(document, line, startOffset, length, color, !hasSpecialColor)); 
     if (token != null) Debug.WriteLine("From typing: Text {0}, Type {1}, Color {2}", token.Text, token.Type, color); 
    } 

    private HighlightColor GetColor(IToken token) 
    { 
     var name = token.Type; 
     var groupName = "Default"; 

     var punctuation = new[] 
      {6, 7, 9, 14, 15, 16, 17, 18, 22, 28, 33, 34, 47, 48, 49, 50, 51, 52, 55, 56, 57, 58, 60, 62, 65, 71}; 
     var paralexVerbs = new[] { 8, 13, 23, 26, 27, 31, 32, 38, 39, 40, 54, 64, 68, 73, 75, 76 }; 
     var paralexNouns = new[] {11, 12, 42, 43, 59, 66}; 
     var paralexNumbers = new[] { 53, 61, 41 }; 
     var paralexStrings = new[] {70}; 

     if (Array.IndexOf(punctuation, name) >= 0) 
     { 
      groupName = "Punctuation"; 
     } 
     else if (Array.IndexOf(paralexVerbs, name) >= 0) 
     { 
      groupName = "ParalexVerbs"; 
     } 
     else if (Array.IndexOf(paralexNouns, name) >= 0) 
     { 
      groupName = "ParalexNouns"; 
     } 
     else if (Array.IndexOf(paralexNumbers, name) >= 0) 
     { 
      groupName = "ParalexNumbers"; 
     } 
     else if (Array.IndexOf(paralexStrings, name) >= 0) 
     { 
      groupName = "ParalexStrings"; 
     } 
     else if (name == 19) 
     { 
      groupName = "ParalexComment"; 
     } 

     return _highlightColors[groupName]; 

    } 

的do..while似乎需要得到令牌,否则GetTokens从来没有提供任何名单。在上面的代码中,即使将注释输入到我的测试平台中,也不会生成令牌。

如果我拿掉CommonTokenStream的参数化构造函数的调用并使用基础构造函数,我会得到一个很好的令牌流,我可以对它进行着色,但所有隐藏的令牌都是......好吧......隐藏我猜。

您对这个小问题的集体想法将不胜感激,以及您可能对如何以编程方式维护类型列表的任何想法,而不是每次更改解析器时都需要重新调整它们。

我曾想过为每种需要着色的类型创建独立的通道,但此时我只是递归地添加我的问题!

在此先感谢 伊恩

编辑:

感谢您的伟大的答案山姆它的大加赞赏。它被标记和打分。

我已经使用了覆盖概念,因为它还解决了通过名称跟踪各种令牌类型的问题,从而简化了我添加到语法中的维护。

我创建了一个语法高亮度词法分析器和一个单独的评估词法分析器,并使用了我在原始语法中创建的独立通道。

评论现在看起来是这样,虽然我觉得ALT还没有成型,主要工作得很好

Comment 
: '//' ~('\r' | '\n')* 
| '/*' .* '*/'   
; 

词法成员已经这些添加

 @lexer::members{ 

    public const int StringChannel = 98; 
    public const int NumberChannel = 97; 
    public const int NounChannel = 96; 
    public const int VerbChannel = 95; 
    public const int CommentChannel = 94; 

    } 

和高亮词法分析器使用此覆盖上发射()您的osuggested覆盖也到位和工作

public class HighlightLexer : ParalexLexer 
{ 
    public override IToken Emit() 
    { 
     switch (state.type) 
     { 
      case Strng: 
       state.channel = StringChannel; 
       break; 
      case Nmber: 
      case Null: 
      case Bool: 
      case Instrument: 
      case Price: 
      case PeriodType: 
       state.channel = NumberChannel; 
       break; 
      case BarPeriod: 
      case BarValue: 
      case InstrumentList: 
      case SMA: 
      case Identifier: 
       state.channel = NounChannel; 
       break; 
      case Assert: 
      case Do: 
      case Else: 
      case End: 
      case Fetch: 
      case For: 
      case If: 
      case In: 
      case Return: 
      case Size: 
      case To: 
      case While: 
      case T__77: 
       state.channel = VerbChannel; 
       break; 
      case Comment: 
       state.channel = CommentChannel; 
       break; 
      default: 
       state.channel = DefaultTokenChannel; 
       break; 
     } 

     return base.Emit(); 
    } 
} 

有一件事让我烦恼,那就是显卡无法轻松获得令牌列表。我无法让CommonTokenStream在没有延迟和跳闸的情况下提供它的令牌。我使用BufferedTokenStream为“_tokens”踢了一脚,听起来更像我之后的事情,嘿presto ..令牌!我怀疑我的用户错误?

的标记方法现在看起来是这样

private void MarkUsingParalexTokens(IDocument document, LineSegment line) 
    { 
     var text = document.GetText(line).ToLower(); 
     var input = new ANTLRStringStream(text); 
     _lexer.CharStream = input; 
     _tokens.TokenSource = _lexer; 

     var wordStart = 0; 
     var tokenCounter = 1; 

     for (var i = 0; i < text.Length; i++) 
     { 
      var token = _tokens.LT(tokenCounter); 
      if (token != null) 
      { 
       var c = text[i]; 
       if (c == ' ' || c == '\t') 
       { 
        if (i > wordStart) 
         AddWord(document, line, wordStart, i); 
        line.Words.Add(c == ' ' ? TextWord.Space : TextWord.Tab); 
        wordStart = i + 1; 
       } 
       else 
       { 
        var atStartOfToken = (i == token.StartIndex); 

        if (atStartOfToken) 
        { 
         if (i > wordStart) 
          AddWord(document, line, wordStart, i); 

         var tokenLength = token.StopIndex - token.StartIndex + 1; 

         AddWord(document, line, i, tokenLength, token); 
         tokenCounter++; 
         wordStart = i + tokenLength; 
         i = wordStart - 1; 
        } 
       } 
      } 

     } 

     if (wordStart < line.Length) 
       AddWord(document, line, wordStart, line.Length); 
    } 

    void AddWord(IDocument document, LineSegment line, int startOffset, int length, IToken token = null) 
    { 
     if (length==0) return; 

     var hasSpecialColor = token != null; 
     var color = hasSpecialColor ? GetColor(token) : _highlightColors["Default"]; 

     line.Words.Add(new TextWord(document, line, startOffset, length, color, !hasSpecialColor)); 
     if (token != null) Debug.WriteLine("From typing: Text {0}, Type {1}, Color {2}", token.Text, token.Type, color); 
    } 

    private HighlightColor GetColor(IToken token) 
    { 
     var name = token.Channel; 
     var groupName = "Default"; 

     if (name==0) 
     { 
      groupName = "Punctuation"; 
     } 
     else if (name==95) 
     { 
      groupName = "ParalexVerbs"; 
     } 
     else if (name==96) 
     { 
      groupName = "ParalexNouns"; 
     } 
     else if (name==97) 
     { 
      groupName = "ParalexNumbers"; 
     } 
     else if (name==98) 
     { 
      groupName = "ParalexStrings"; 
     } 
     else if (name == 94) 
     { 
      groupName = "ParalexComment"; 
     } 

     return _highlightColors[groupName]; 

    } 

再次感谢您的帮助。我要关注错误识别和标记... Registers Ian

+0

由于ANTLR 3的'CommonTokenStream.SkipOffTokenChannels(int)'(也存在于参考Java运行时)中的错误,'CommonTokenStream'目前只能与默认令牌通道一起使用。 – 2013-03-19 15:24:16

我总是使用不同的词法分析器来进行语法高亮处理,以用于其他分析任务。用于语法总是突出词法分析器满足以下:

  • 除了NEWLINE没有记号包含\r\n字符。相反,多个词法分析器模式用于块注释和跨越多行的任何其他构造(甚至适用于ANTLR 3词法分析器,但不支持ANTLR 3中的词法分析器模式,它变得复杂得多)。

  • NEWLINE被定义为以下:

    // ANTLR 3-based syntax highlighter: 
    NEWLINE : ('\r' '\n'? | '\n') {skip();}; 
    
    // ANTLR 4-based syntax highlighter: 
    NEWLINE : ('\r' '\n'? | '\n') -> skip; 
    
  • 否标记是在隐藏通道。

如果你不希望走这条路线,你可以从你的Comment规则中删除的操作{$channel=Hidden;},而是从你的基地词法分析器派生一个新类。在派生类中,覆盖Emit()。使用基本实现进行语法高亮显示,并使用派生实现传递给解析器。这在某些情况下更容易,但对于具有多行字符串或注释的语言而言,会导致实质性能限制,我们发现任何产品都无法接受。

public override IToken Emit() 
{ 
    if (state.type == Comment) 
     state.channel = Hidden; 

    return base.Emit(); 
} 
+0

@萨姆哈维尔辉煌山姆,感谢您的帮助。我编辑了我的答案以显示最终的解决方案。伊恩。 – 2013-03-20 08:55:21

我使用C++(QT/SCintilla库),但无论如何,我会建议使用不同的Lexer语法突出显示。矿突出词法分析器从所述解析一个不同:

  • 不需要上下文敏感的词法(“X”是一个关键字,如果任何仅当它后跟“Y”,否则是它的标识符

  • 高亮词法分析器必须永远不会失败

  • 我想要的内置函数加以强调(这是没有必要的解析)

Gui Lexer语法包含附加规则(最后)。

QUOTED_STRING_FRAGMENT 
    : '"' (~('"') | '\"')+ EOF 
    ; 

// Last resort rule matches any character. This lexer should never fail. 
TOKEN_FAILURE : . ; 

规则TOKEN_FAILURE将来自用户的输入匹配任何“无效”字,将 与红色背景显示。否则,该字符将被跳过并且突出显示将被移位。

QUOTED_STRING_FRAGMENT处理用户输入第一个引号并且字符串尚未完成时的情况。