33

C# で非常に単純なパーサーを作成しようとしています。

正規表現をトークンに関連付けることができるレクサーが必要です。これにより、正規表現を読み取ってシンボルを返すことができます。

正規表現を使用して実際の重い作業を行うことができるように思われますが、それを行う簡単な方法がわかりません。一つには、Regex はストリームではなく文字列でのみ動作するようです (なぜですか!?!?)。

基本的に、次のインターフェースの実装が必要です。

interface ILexer : IDisposable
{
    /// <summary>
    /// Return true if there are more tokens to read
    /// </summary>
    bool HasMoreTokens { get; }
    /// <summary>
    /// The actual contents that matched the token
    /// </summary>
    string TokenContents { get; }
    /// <summary>
    /// The particular token in "tokenDefinitions" that was matched (e.g. "STRING", "NUMBER", "OPEN PARENS", "CLOSE PARENS"
    /// </summary>
    object Token { get; }
    /// <summary>
    /// Move to the next token
    /// </summary>
    void Next();
}

interface ILexerFactory
{
    /// <summary>
    /// Create a Lexer for converting a stream of characters into tokens
    /// </summary>
    /// <param name="reader">TextReader that supplies the underlying stream</param>
    /// <param name="tokenDefinitions">A dictionary from regular expressions to their "token identifers"</param>
    /// <returns>The lexer</returns>
    ILexer CreateLexer(TextReader reader, IDictionary<string, object> tokenDefinitions);
}

だから、codzを送ってください...
いいえ、真剣に、私は上記のインターフェースの実装を書き始めようとしていますが、.NET (2.0) でこれを行う簡単な方法が既にないことを信じるのは難しいと思います. .

それで、上記を行う簡単な方法についての提案はありますか?(また、「コード ジェネレーター」も必要ありません。パフォーマンスは重要ではなく、ビルド プロセスを複雑にしたくありません。)

4

7 に答える 7

6

やり過ぎかもしれませんが、CodePlexのIronyを見てください。

Irony は、.NET プラットフォームに言語を実装するための開発キットです。C# 言語と .NET Framework 3.5 の柔軟性と能力を利用して、完全に新しい合理化されたコンパイラ構築テクノロジを実装します。ほとんどの既存の yacc/lex スタイルのソリューションとは異なり、Irony は特殊なメタ言語で記述された文法仕様からスキャナーまたはパーサー コードを生成しません。Irony では、ターゲット言語の文法は、演算子のオーバーロードを使用して c# で直接コーディングされ、文法構造を表現します。Irony のスキャナーおよびパーサー モジュールは、c# クラスとしてエンコードされた文法を使用して、解析プロセスを制御します。C# クラスでの文法定義の例と、動作中のパーサーでの使用については、式文法のサンプルを参照してください。

于 2009-03-23T15:03:53.367 に答える
5

非常に型破りな文法を持っていない限り、独自のレクサー/パーサーを作成しないことを強くお勧めします。

私は通常、C# のレクサー/パーサーが本当に不足していることに気付きます。ただし、F# には fslex と fsyacc が付属しており、このチュートリアルで使用方法を学習できます。私は F# でいくつかのレクサー/パーサーを作成し、それらを C# で使用しましたが、非常に簡単に実行できます。

始めるにはまったく新しい言語を学ばなければならないことを考えると、それは本当に貧乏人のレクサー/パーサーではないと思いますが、それは始まりです。

于 2009-03-23T12:30:27.620 に答える
2

私の元の答えを変更します。

さまざまな構文タイプのパーサーを備えたSharpTemplateを見てください。

#foreach ($product in $Products)
   <tr><td>$product.Name</td>
   #if ($product.Stock > 0)
      <td>In stock</td>
   #else
     <td>Backordered</td>
   #end
  </tr>
#end

トークンのタイプごとに正規表現を使用します。

public class Velocity : SharpTemplateConfig
{
    public Velocity()
    {
        AddToken(TemplateTokenType.ForEach, @"#(foreach|{foreach})\s+\(\s*(?<iterator>[a-z_][a-z0-9_]*)\s+in\s+(?<expr>.*?)\s*\)", true);
        AddToken(TemplateTokenType.EndBlock, @"#(end|{end})", true);
        AddToken(TemplateTokenType.If, @"#(if|{if})\s+\((?<expr>.*?)\s*\)", true);
        AddToken(TemplateTokenType.ElseIf, @"#(elseif|{elseif})\s+\((?<expr>.*?)\s*\)", true);
        AddToken(TemplateTokenType.Else, @"#(else|{else})", true);
        AddToken(TemplateTokenType.Expression, @"\${(?<expr>.*?)}", false);
        AddToken(TemplateTokenType.Expression, @"\$(?<expr>[a-zA-Z_][a-zA-Z0-9_\.@]*?)(?![a-zA-Z0-9_\.@])", false);
    }
}

このように使用されます

foreach (Match match in regex.Matches(inputString))
{
    ...

    switch (tokenMatch.TokenType)
    {
        case TemplateTokenType.Expression:
            {
                currentNode.Add(new ExpressionNode(tokenMatch));
            }
            break;

        case TemplateTokenType.ForEach:
            {
                nodeStack.Push(currentNode);

                currentNode = currentNode.Add(new ForEachNode(tokenMatch));
            }
            break;
        ....
    }

    ....
}

スタックからプッシュおよびポップして状態を維持します。

于 2009-03-23T12:41:23.367 に答える
2

Malcolm Crowe は、C# 用の優れた LEX/YACC 実装をここで公開しています。LEX の正規表現を作成することで機能します...

直接ダウンロード

于 2009-03-23T13:07:53.507 に答える
0

C# には Flex と Bison を使用できます。

アイルランド大学の研究者は、次のリンクで見つけることができる部分的な実装を開発しました: Flex/Bison for C#

プリプロセッサがない、「ぶら下がっているelse」の問題など、彼の実装にはまだいくつかの問題があるように見えるため、これは間違いなく「貧乏人の字句解析器」と見なすことができます。

于 2009-03-23T12:57:39.663 に答える