2

私は小さなDSLの開発をある程度進めてきましたが、使用しているTextEditorControlのコメントを強調表示しようとすると問題が発生しました。ちなみに、ICSharpCodeコントロールは優れており、ANTLRと組み合わせると、DSL用の優れたプラットフォームになります。

私は機能するグラマーとレクサーを持っており、テキストエディターでハイライト戦略を作成しました。これもうまく機能します。正しく色付けすることを拒否しているDSLの唯一の要素は、私が隠しチャネルに持っている「コメント」です。

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

イライラするのは、コメントlexruleを非表示のチャネルから外すと、強調表示が機能するようになることです...しかし、そうすると、コメントに続く最後のテキストの後、評価中にパーサーが解析を停止します。

例として; これは、コメントが非表示の場合は機能しますが、非表示の場合は最初の「abc」で解析を停止します

   //Comment

   abc=[7,8,9];

   return abc[2];

デフォルトと非表示のトークンリストを開始インデックス順に並べられた1つのリストに結合し、そこから強調表示できるように、非表示チャネルに個別にアクセスしようとしていますが、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が必要なようです。そうでない場合、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;

    }

ハイライトレクサーは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();
    }
}

私を悩ませていたのは、トークンのリストを簡単に取得できないことでした。CommonTokenStreamに、遅延とトリップアップなしでトークンを配信させることができませんでした。「_tokens」にBufferedTokenStreamを使用してパントをしました。これは、私が求めていたもののように聞こえたためです。私の側でユーザーエラーが疑われますか?

マークアップメソッドは次のようになります

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

    }

よろしくお願いします。エラー認識とマークアップを見ていきます...よろしくイアン

4

2 に答える 2

3

構文の強調表示には、他の構文解析タスクで使用されるものとは常に異なるレクサーを使用します。構文の強調表示に使用されるレクサーは、常に次の条件を満たす。

  • または文字をNEWLINE含む以外のトークンはありません。代わりに、複数のレクサーモードが、ブロックコメントや複数行にまたがるその他の構造などに使用されます(これはANTLR 3レクサーにも適用されますが、ANTLR 3自体でレクサーモードがサポートされていないと、複雑になります)。\r\n

  • NEWLINE次のように定義されます。

    // ANTLR 3-based syntax highlighter:
    NEWLINE : ('\r' '\n'? | '\n') {skip();};
    
    // ANTLR 4-based syntax highlighter:
    NEWLINE : ('\r' '\n'? | '\n') -> skip;
    
  • 非表示のチャネルにトークンはありません。

このルートを使用したくない場合は{$channel=Hidden;}、ルールからアクションを削除しComment、代わりにベースレクサーから新しいクラスを派生させることができます。派生クラスで、をオーバーライドしますEmit()。構文の強調表示には基本実装を使用し、パーサーに渡すには派生実装を使用します。これは場合によっては簡単ですが、複数行の文字列またはコメントを含む言語では、パフォーマンスが大幅に制限され、どの製品でも受け入れられないことがわかります。

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

    return base.Emit();
}
于 2013-03-19T15:06:58.680 に答える
0

私は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は、ユーザーが最初の引用符を入力し、文字列がまだ終了していない状況を処理します。

于 2013-03-20T11:12:18.840 に答える