Practice 3: 標準入力を受け取る構文解析プログラム¶
解説「段階的詳細化からコードへの変換」と解答例(前半・後半)を用意しています.まず,解説を読んで考えみましょう.解答例を見るのは,慎重に.
練習3-1 プログラムの詳細化 (1/3)¶
解説「段階的詳細化からコードへの変換」を読み,段階的詳細化の理解を深めよ.
その実践として,以下の処理について,もう一段階詳細化せよ.
2文字目のコマンド文字と4文字目以降の引数を取り出す;
(クリックすると,練習3-1の解答例を示します)
「2文字目のコマンド文字と4文字目以降の引数を取り出す;」についての詳細化¶
読み込んだ行をchar line[]とすると,
コマンド文字 →
line[1](型はchar)コマンド引数 →
&line[3](型はchar*)
として取り出せる.
この時点で,ほぼコードが出来上がっており,もう一段の詳細化は不要であると思える.
具体例を考えるとわかりやすい.lineが"%P 15"である場合,以下の様にメモリ上にデータが配置されているはずである.
|
|
|
|
|
|
|
|
|
|
|
|
練習3-2 parse_line() 関数の実装¶
段階的詳細化からコードへの変換を読んで,これまで作成したsplit(),get_line(),subst()を利用して,以下のmain()関数とともに,parse_line()関数を作成せよ.
なお,以下に掲載するプログラム例は大幅に省略して書かれている.例えば,関数定義や,関数利用時の引数や戻り値などを省略しており,各自で適切に修正する必要がある.加えて,必要な関数があれば,それらも適切な箇所で実装を記述する必要があるだろう.
正しく動作するC言語のプログラムとして実装するため,よく考えて実装を進めること.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h>
#define MAX_LINE_LEN 1024
parse_line()
{
if (行頭が '%' ではじまっているなら) {
2文字目のコマンド文字(=cmd)と4文字目以降の引数(=param)を取り出す;
exec_command(cmd, param);
} else {
// new_profile(); // will be implemented at Practice 4
split_test_csv(...); // ... には,各自で適切な引数を与える
}
}
int main()
{
char line[MAX_LINE_LEN + 1];
while (get_line(line)) {
parse_line(line);
}
return 0;
}
|
parse_line()から呼び出される各関数は,以下のように仮実装せよ.
new_profile()関数get_line()とsplit()のテスト(練習2-3 split() と get_line() の結合)で利用したsplit_test_csv()で代用せよ.
exec_command()関数単に2文字目のコマンド名と4文字目以降の引数を受け取って,表示するだけの関数とせよ.
例えば,以下の通り:
void exec_command(char cmd, char *param) { fprintf(stderr, "%%%c command is invoked with arg: '%s'\n", cmd, param); }
参考:FAQ "fprintf() と printf() はどう違うのですか"
注釈
Linuxの端末画面では,Ctrl + dにより『キーボード入力は終了する』という信号を送ることができる.
練習3-3 構文解析プログラムの簡単な動作テスト¶
ここまでの実装を踏まえて,標準入力を受け取る構文解析プログラムを完成せよ.そして,簡単な動作テストをせよ.
ヒント1:レポートAでは,練習3-3(あるいは,できている人は練習3-5)で完成させたプログラムをeop1a_09Bxxxxx.cというファイル名で提出してもらう.(09Bxxxxxは各自の学生番号)
ヒント2:動作テストにより,“目標”を達成したことを示そう.当面の目標は,すでに考えてきたはずである.
練習3-4 プログラムの詳細化 (2/3)¶
段階的詳細化のさらなる実践として,以下の処理について,もう一段階詳細化せよ.
コマンド文字と引数で示された処理を実行;
そして,この詳細化の結果を踏まえて「名簿管理プログラムの流れ ver.2」を検討してみよ.
練習3-5 exec_command() 関数の実装¶
練習3-2 で仮実装としているexec_command()について,練習3-4で検討した詳細化を踏まえて,さらに実装を進めよ.テストせよ.
exec_command()から呼び出される各コマンドのための処理(例えば,cmd_xxx()のような関数)については,練習3-2 と同じ要領で仮実装でよい.ただし,演習課題説明資料 (p2-theme-all.pdf)の付録Aを参照して,引数を必要とするコマンドへの手当ても考えておくこと.
例えば,cmd_quit()やcmd_print()であれば,以下のような実装案となる.
void cmd_quit()
{
fprintf(stderr, "Quit (%%Q) command is invoked with no arg\n");
}
void cmd_print(int nitems)
{
fprintf(stderr, "Print (%%P) command is invoked with arg: '%d'\n", nitems);
}
参考:FAQ -fprintf() と printf() はどう違うのですか