これは宿題/学習演習のように聞こえるので、このようなプログラムを計画/設計するプロセスを説明します。次に、それを実装する 1 つの方法のサンプルを示します。
この種のプログラムを作成する 1 つの方法は、一対のステート マシンを構築することです。
- 初期状態/空白状態
- 状態 1 から:
/*
シーケンスを見た。までこの状態を保ち*/
ます。1に戻る
"
状態 1 から:キャラクターを見たことがある。\
またはまでこの状態にとどまり"
ます。1に戻る
- 状態 1 から:
//
シーケンスを見た。改行までこの状態を維持します。1に戻る
- 状態 1 から: 手紙を見たことがある。非文字および非数字になるまでキャプチャするこの状態にとどまる
- 状態 1 から: 他に何かを見た場合は、改行までこの状態にとどまります。
- 状態 3 から: 次の文字の抽出を見
\
た後、状態 3 に戻る
状態 5 を終了すると、次のようになります。
- 単語が「ある」場合は
switch
、別のステート マシン (以下) に入ります。
- 表示されなかった場合は
:
、状態 1 に戻ります
- そうでなければ、私たちの言葉はラベルです。好きなことをして、状態 1 に戻ります。
ただし、 が表示switch
されると、すぐ後に式が続くため、別の状態テーブルが必要になります。
- 初期状態/空白
- 状態 1 から:
(
開始時switch
- 状態 1 から: その他のすべての文字はエラーを生成します。
- 状態 2 から:状態マシン 1 (上記) のよう
/*
に"
扱われる
- 状態 2 から:
)
見た
- 状態 5 から:
{
- ネスティング レベルをインクリメントし、すぐに状態 7 に進みます。
- 空白/状態
- 状態 7 から:
}
- この状態への入り口でネスティング レベルをデクリメントします。ネスティング レベル 0 の場合は、最初のステート マシンの状態 1 に戻ります。それ以外の場合は、状態 7 のままです。
- 状態 7 から: 手紙を見たことがある。非文字または
:
- 状態 7 から: 他に何かを見たことがあり、状態 7 と 8 にそれぞれ移動するまで
;
、または状態 7 と 8 に移動するまで、この状態にとどまり}
ます。
状態 9 を終了したら:
- 表示されなかった場合は
:
、状態 10 に進みます
- それ以外の場合、単語 as
default
then が状態 10 に進む
- それ以外の場合、これはラベルです。好きなことをして、状態 10 に移動します。
次のステップは、各状態の関数を作成することです。状態に番号を付け、関数に番号を付けると役立つ場合があります。彼らが「文字」または「文字シーケンス」を操作する方法は、一般的に、私が初期に書いたいくつかの低レベルのものです。
int c;
int next() { c=getchar(); return c; }
typedef int (*state)();
たとえば、上記の状態 2 と 3 は次のように記述できます。
state state2() { if(c == '*' && next() == '/') return state1; return state2; }
state state3() { if(c == '"') return state1; if (c == '\\') return state7; }
残りの状態を解決するのはかなり簡単なはずです。状態 5 には、単語を「読み取る」ために満たすバッファがあります。
char word[600];
int ptr;
state state5() {
ptr = 0;
while(isdigit(c) || isalpha(c)) {
word[ptr] = c;
ptr++;
if(ptr==600)abort();
next();
}
/* now leaving state 5 */
}
これが完了したら、ドライバーを作成できます。
void statemachine1() {
state x = state1;
while(c != -1) x = x();
}
間違いを犯した場合は、デバッグ ツールがあると便利です。1 つの良い方法は、配列内の各状態にラベルを付けることです。
state statelist1[] = { state1, state2, state3, state4, state5, state6, state7 };
int statenumber(state x) {
int i, n = sizeof(statelist1) / sizeof(state);
while(n-->0) if(x == statelist1[n]) return n;
abort();
}
これはデバッグ時に役立ちます。私は挿入するかもしれません:
printf("state = %d, char = %02x (%c)\n", statenumber(x), c,c);
ドライバーループに、そして次のようなものに:
printf("char = %02x (%c)\n", c,c);
マシンをなぞるといろいろな部分に。これにより、ステート マシンのマップがよりしっかりと頭の中で構築され、テスト プログラムに沿ってマップをたどると、すべてのステートの検証が容易になります。
状況が悪化し始めたらnext()
、現在の行を追跡するように変更します。
int line = 1;
int next() { c=getchar(); if(c == '\n') line++; return c; }
そうすれば、私のprintf()
ステートメントでもそれを使用できます。
動作していることに満足したら、デバッグ コードを削除します。
幸運を!