Practice 3: 標準入力を受け取る構文解析プログラム

解説「段階的詳細化からコードへの変換」と解答例(前半後半)を用意しています.まず,解説を読んで考えみましょう.解答例を見るのは,慎重に.

練習3-1 プログラムの詳細化 (1/3)

解説「段階的詳細化からコードへの変換」を読み,段階的詳細化の理解を深めよ.

その実践として,以下の処理について,もう一段階詳細化せよ.

  • 2文字目のコマンド文字と4文字目以降の引数を取り出す;

(クリックすると,練習3-1の解答例を示します)

「2文字目のコマンド文字と4文字目以降の引数を取り出す;」についての詳細化

読み込んだ行をchar line[]とすると,

  • コマンド文字 →line[1](型はchar

  • コマンド引数 →&line[3](型はchar*

として取り出せる.

この時点で,ほぼコードが出来上がっており,もう一段の詳細化は不要であると思える.

具体例を考えるとわかりやすい.line"%P 15"である場合,以下の様にメモリ上にデータが配置されているはずである.

line[0]

line[1]

line[2]

line[3]

line[4]

line[5]

'%'

'P'

' '

'1'

'5'

'\0'

練習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()関数

  • 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-2)

練習3-3 構文解析プログラムの簡単な動作テスト

ここまでの実装を踏まえて,標準入力を受け取る構文解析プログラムを完成せよ.そして,簡単な動作テストをせよ.

ヒント1:レポートAでは,練習3-3(あるいは,できている人は練習3-5)で完成させたプログラムをeop1a_09Bxxxxx.cというファイル名で提出してもらう.(09Bxxxxxは各自の学生番号)

ヒント2:動作テストにより,“目標”を達成したことを示そう.当面の目標は,すでに考えてきたはずである.

» 解答例(練習3-3)

練習3-4 プログラムの詳細化 (2/3)

2025年度注:レポートA執筆の段階では,練習3-4は飛ばして構いません.第5回の講義で扱います.

段階的詳細化のさらなる実践として,以下の処理について,もう一段階詳細化せよ.

  • コマンド文字と引数で示された処理を実行;

そして,この詳細化の結果を踏まえて「名簿管理プログラムの流れ ver.2」を検討してみよ.

» 解答例(練習3-4)

練習3-5 exec_command() 関数の実装

2025年度注:レポートA執筆の段階では,練習3-5は飛ばして構いません.第5回の講義で扱います.

練習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);
}

» 解答例(練習3-5)