0

カスタム言語 ( C 構文とAllman スタイルの書式設定にわずかに基づく) でスクリプトを解析するアプリケーションを作成しており、スクリプト コードのブロックを文字列配列に解析するより良い (読み取り: 高速) 方法を探しています私が現在行っている方法です(現在の方法で十分ですが、何よりもデバッグ用でした)。

スクリプトの内容は現在、ファイルから文字列配列に読み取られ、メソッドに渡されます。

スクリプト ブロックのテンプレートは次のとおりです。

loop [/* some conditional */ ]
{
  /* a whole bunch of commands that are to be read into
   * a List<string>, then converted to a string[] and
   * passed to the next step for execution */

   /* some command that has a bracket delimited set of
    * properties or attributes */
   {
     /* some more commands to be acted on */
   }
}

基本的に、中かっこブロックは (他の C ベースの言語と同様に) 入れ子にすることができます。私は、このような個々のブロックを見つける最善の方法を探しています。

中括弧で区切られたブロックは常にこのようにフォーマットされます - 括弧の内容は開き括弧の後の行で始まり、最後の属性/コマンド/コメント/その他の後の行に括弧が続きます。

例は次のとおりです。

loop [ someVar <= 10 ]
{
  informUser "Get ready to do something"
  readValue
  {
    valueToLookFor = 0x54
    timeout = 10 /* in seconds */
  }
}

これにより、 someVar が 10 未満である間、アプリがループするように指示されます (卵を吸うコメントで申し訳ありません)。毎回、ユーザーにメッセージを渡し、どこかから特定の値を探します (タイムアウトは 10 秒)。

現時点での方法は次のとおりです(注:これを呼び出すメソッドは、現在のスクリプトを含むstring []全体を、読み取り元のインデックスとともに渡します):

private string[] findEntireBlock(string[] scriptContents, int indexToReadFrom,
                                out int newIndex)
{
    newIndex = 0;
    int openBraceCount = 0;     // for '{' char count
    int closeBraceCount = 0;    // for '}' char count
    int openSquareCount = 0;    // for '[' char count
    int closeSquareCount = 0;   // for ']' char count

    List<string> fullblock = new List<string>();

    for (int i = indexToReadFrom; i < scriptContents.Length; i++)
    {
        if (scriptContents[i].Contains('}'))
        {
            if (scriptContents[i].Contains("[") && fullblock.Count > 0)
            {
                //throw new exception, as we shouldn't expect to
                //to find a line which starts with [ when we've already
            }
            else
            {
                if (scriptContents[i].Contains('{')) openBraceCount++;
                if (scriptContents[i].Contains('}')) closeBraceCount++;
                if (scriptContents[i].Contains('[')) openSquareCount++;
                if (scriptContents[i].Contains(']')) closeBraceCount++;
                newIndex = i;
                fullblock.Add(scriptContents[i]);
                break;
            }
        }
        else
        {
            if (scriptContents[i].Contains("[") && fullblock.Count > 0)
            {
                //throw new exception, as we shouldn't expect to
                //to find a line which starts with [ when we've already
            }
            else
            {
                if (scriptContents[i].Contains('{')) openBraceCount++;
                if (scriptContents[i].Contains('}')) closeBraceCount++;
                if (scriptContents[i].Contains('[')) openSquareCount++;
                if (scriptContents[i].Contains(']')) closeBraceCount++;
                fullblock.Add(scriptContents[i]);
            }
        }
    }
    if (openBraceCount == closeBraceCount &&
        openSquareCount == closeSquareCount)
          return fullblock.ToArray();
    else
        //throw new exception, the number of open brackets doesn't match
        //the number of close brackets
}

これはやや鈍感で遅い方法である可能性があることに同意します。そのため、速度と明確さのためにこれを再実装する方法についてアイデアを求めています (バランスがとれる場合)。

ブラケットカウントを維持するために使用することができず、再帰的に動作できる RegEx ステートメント (正しい用語ですか?) を記述できるかどうかわからないため、RegEx には近づかないようにしています。内側から外側に向かって作業することを考えていましたが、それはかなり遅いと確信しています。

私のために書き直す人を探しているわけではありませんが、私の方法を改善するために使用できるアルゴリズムまたはテクニック/ライブラリに関する一般的なアイデアです。

副次的な質問として、コンパイラはソース コード内の複数のネストされたブラケットをどのように処理しますか?

4

1 に答える 1

3

Jack Crenshaw によるLet's Build a Compilerは、基本的なコンパイラを構築するための素晴らしい、読みやすい入門書です。ここで説明する手法は、ここでやろうとしていることの助けになるはずです。

于 2013-03-20T10:45:39.237 に答える