コメントを抜き出すプログラム

Cのソースファイルの中からコメント(/* ・・・ */)だけを別ファイルに書き出すプログラムを作ろう.

新入社員への課題等でありそうな内容.「ソースファイルを1文字づつ読んで,コメントを見つけたかどうかのフラグを持って・・・」という作り方とは違うやり方を考えてみた.以下ソース.

/* 1: comment.c */
#include <stdio.h>

main(int argc,char *argv[])
{
  FILE *comment=fopen(argv[1],"w");
  FILE *source=fopen(argv[2],"r");
  scan_comment(comment,source);
  fclose(comment);
  fclose(source);
}

int scan_comment(FILE *comment,FILE *source)
{
  return in_function(comment,source);
}

int in_function(FILE *comment,FILE *source)
{
  int c;

  while((c=getc(source))!='/'&&c!='"'&&c!='\''&&c!=EOF){
  }
  switch(c){
  case '/': /* 2: コメントが始まるのかも */
    return begin_comment(comment,source);
    break;
  case '"': /* 3: 文字列.文字列の中では"/*"が出てきてもコメントではない */
    return in_string(comment,source);
    break;
  case '\'': /* 4: 文字 */
    return in_charactor(comment,source);
    break;
  default: /* 5: ファイルが終った */
    return normal_end();
    break;
  }
}

int begin_comment(FILE *comment,FILE *source)
{
  switch(getc(source)){
  case '*': /* 6: '/'の次の文字が'*'だったらコメントの始まり */
    return in_comment(comment,source);
    break;
  case '"': /* 7: '/'の次の文字が'"'だったら文字列.コメントではない */
    return in_string(comment,source);
    break;
  case '\'': /* 8: '/'の次の文字が'\''だったら文字.コメントではない */
    return in_charactor(comment,source);
    break;
  case EOF:
    return normal_end();
    break;
  default:
    return in_function(comment,source);
    break;
  }
}

int in_comment(FILE *comment,FILE *source)
{
  int c;

  while((c=getc(source))!='*'&&c!=EOF){
    putc(c,comment);
  }
  return (c=='*')? end_comment(comment,source): abnormal_end();
  /* 9: "/*"の後に続く文字列に'*'が出てきたらコメントが終るのかも
        コメントが終らないでファイルが終ってはまずい */
}

int end_comment(FILE *comment,FILE *source)
{
  int c=getc(source);

  switch(c){
  case '/': /* 10: コメント終了 */
    return in_function(comment,source);
    break;
  case EOF:
    return abnormal_end();
    break;
  default: /* 11: コメント継続 */
    putc('*',comment);
    putc(c,comment);
    return in_comment(comment,source);
    break;
  }
}

int in_string(FILE *comment,FILE *source)
{
  return not_comment_until_find_this('"',comment,source);
}

int in_charactor(FILE *comment,FILE *source)
{
  return not_comment_until_find_this('\'',comment,source);
}

int not_comment_until_find_this(int mark,FILE *comment,FILE *source)
{
  int c;

  while((c=getc(source))!=mark&&c!=EOF){
    if(c=='\\'){ /* 12: エスケープしていたら次の文字が'だろうと"だろうと無視して次へ */
      getc(source);
    }
  }
  return (c==mark)? in_function(comment,source): abnormal_end();
}

int normal_end()
{
  return 1;
}

int abnormal_end()
{
  return 0;
}

cygwinで動くgcc 3.4.4でコンパイルして実行してみた.

$ gcc comment.c
$ a comment.txt comment.c

comment.txtの内容は以下の通り.適当に改行を入れている.

1: comment.c  2: コメントが始まるのかも  3: 文字列.文字列の中では"/*"が出てきてもコメント
ではない  4: 文字  5: ファイルが終った  6: '/'の次の文字が'*'だったらコメントの始まり  7: 
'/'の次の文字が'"'だったら文字列.コメントではない  8: '/'の次の文字が'"'だったら文字.コメ
ントではない  9: "/*"の後に続く文字列に'*'が出てきたらコメントが終るのかも
コメントが終らないでファイルが終ってはまずい  10: コメント終了  11: コメント継続  12: エス
ケープしていたら1文字無視して次へ