コンパイラの専門家の皆さん、私は再帰降下パーサーを書きたいと思っています。それをコードだけでやりたいのです。他の文法からレクサーとパーサーを生成せず、ドラゴンブックを読むように言わないでください。最終的にはそれに近づきます。
CSS などの合理的な単純な言語のレクサーとパーサーの実装について、ザラザラした詳細に入りたいと思います。そして、私はこれを正しく行いたいです。
これはおそらく一連の質問になるでしょうが、今はレクサーから始めています。CSS のトークン化ルールについては、こちらを参照してください。
私は次のような自己記述コードを見つけました (うまくいけば、このスニペットから残りを推測できます):
public CssToken ReadNext()
{
int val;
while ((val = _reader.Read()) != -1)
{
var c = (char)val;
switch (_stack.Top)
{
case ParserState.Init:
if (c == ' ')
{
continue; // ignore
}
else if (c == '.')
{
_stack.Transition(ParserState.SubIdent, ParserState.Init);
}
break;
case ParserState.SubIdent:
if (c == '-')
{
_token.Append(c);
}
_stack.Transition(ParserState.SubNMBegin);
break;
これは何と呼ばれていますか?そして、私は合理的によく理解されているものからどのくらい離れていますか? スタックを使用してある種のステートマシンを実装することは非常にうまく機能していますが、このように続ける方法がわかりません。
私が持っているのは、一度に 1 文字を読み取ることができる入力ストリームです。私は今のところ頭を見ていません。ただキャラクターを読んで、現在の状態に応じてそれで何かをしようとします.
再利用可能なコードのスニペットを作成するというマインド セットに取り組みたいと思っています。このTransition
メソッドは現在、それを行うための手段であり、スタックの現在の状態をポップしてから、逆の順序で引数をプッシュします。そうすれば、私が書くTransition(ParserState.SubIdent, ParserState.Init)
と、サブルーチンが「呼び出され」、SubIdent
完了すると状態に戻りInit
ます。
パーサーはほぼ同じ方法で実装されます。現在、このようにすべてを単一の大きなメソッドに含めると、トークンを見つけたときにトークンを簡単に返すことができますが、すべてを単一の大きなメソッドに保持する必要があります。これらのトークン化ルールを別々の方法に分割する良い方法はありますか?