gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 05:06:21 30 4
gpt4 key购买 nike

我在开发我们的小 DSL 方面取得了一些进展,但在尝试突出显示我们正在使用的 TextEditorControl 中的注释时遇到了问题。顺便说一下,ICSharpCode 控件非常棒,它与 ANTLR 相结合,为 DSL 提供了一个很好的平台。

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

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

令人沮丧的是,如果我将 Comment lexrule 从隐藏 channel 中移除,我可以使突出显示工作......但是当我这样做时,解析器在评论后的最后一段文本之后的评估期间停止解析。

举个例子;这在注释被隐藏但在第一个“abc”处停止解析时有效
   //Comment

abc=[7,8,9];

return abc[2];

我一直在尝试单独访问隐藏 channel ,以便我可以将默认和隐藏 token 列表合并到一个按开始索引排序的列表中,然后从那里突出显示,但我没有运气使用 CommonTokenStream 的 BaseRecognizer.Hidden 参数构造函数。

我当前突出显示 TextEditor 行的尝试如下所示
    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 似乎需要将 token 放入列表中,否则 GetTokens 永远不会提供任何东西。在上面的代码形式中,即使在我的测试设备中输入评论时也不会产生任何标记。

如果我取消对 CommonTokenStream 的参数化构造函数的调用并使用基本构造函数,我会得到一个很好的 token 流,我可以为它着色,但所有隐藏的 token 都是......好吧......我猜是隐藏的。

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

我曾想过为每种需要着色的类型创建独立的 channel ,但目前我只是递归地添加到我的问题中!

提前致谢
伊恩

编辑:

感谢您的精彩回答山姆,非常感谢。它被标记和得分。

我采用了覆盖概念,因为它还解决了按名称跟踪各种 Token 类型的问题,从而在我添加到语法中时简化了我的维护。

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

评论现在看起来像这样虽然我认为 alt 还没有工作,主要工作很好
Comment
: '//' ~('\r' | '\n')*
| '/*' .* '*/'
;

Lexer 成员添加了这些
        @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;

}

并且突出显示词法分析器在 Emit() 上使用此覆盖,您建议的覆盖也已就位并有效
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();
}
}

困扰我的一件事是明显无法轻松获取 token 列表。我无法让 CommonTokenStream 在没有延迟和绊倒的情况下交付其 token 。我尝试将 BufferedTokenStream 用于“_tokens”,因为这听起来更像是我所追求的,嘿,presto .. token !我怀疑是我的用户错误?

标记方法现在看起来像这样
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];

}

再次感谢您的帮助。我要去看看错误识别和标记......
问候
伊恩

最佳答案

我总是使用与其他解析任务不同的词法分析器来突出显示语法。用于语法高亮的词法分析器始终满足以下条件:

  • 除了 NEWLINE 之外没有 token 包含 \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;
  • 隐藏 channel 上没有 token 。

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

    return base.Emit();
    }

    关于ANTLR3 语法在 ICSharpCode.TextEditor 中突出显示隐藏 channel ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15502426/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com