プリプロセッサ命令の後ろに余分なトークンがあったらエラーにするパッチ

http://hsp.tv/play/pforum.php?mode=all&num=22946#22959 を見て作ってみたんだけど微妙な感じ。

  • #if, #ifdef, #ifndef で実行されないブロック内ではチェックされない
  • #enum は 「if ( GetToken() == '=' ) {」ってやってるので余分なトークンがひとつだけではエラーにならない
  • #uselib などいくつかのプリプロセッサ命令は token.cpp では処理しないので余分なトークンがあってもエラーにならない
  • #func 系の命令や #usecom は token.cpp では途中までしか処理しないので余分なトークンがあることをチェックできない
  • #ahtmes, #pack, #packopt なんかは条件が揃わないとトークンを処理しないんだけど、これらは条件が揃わなくてもトークンを処理するように簡単に修正できるのでそうした
  • ほかにも最後までトークンを処理しないようなプリプロセッサ命令を見逃しているかも
  • エラーを表す int 型の戻り値のマジックナンバーだけど何か意味があるのだろうか。何も考えずに 1 をつかったけど。
  • hsp31/sample 内のすべてのソースで同じオブジェクトファイル、プリプロセス済みソース、コンパイルメッセージが出力されることを確認しています。(調べるのに使ったスクリプト:hspcmp_result_compare.hsp)
Index: token.cpp
===================================================================
--- token.cpp	(revision 189)
+++ token.cpp	(working copy)
@@ -2406,6 +2406,7 @@
 	//
 	wrtbuf->PutStrf( "#%s %s%s",name, word, (char *)wp );
 	wrtbuf->PutCR();
+	GetToken();
 	//
 	return -1;
 }
@@ -2564,11 +2565,10 @@
 	//		#ahtmes解析
 	//
 	int i;
+	bool execute = ahtmodel != NULL &&
+	               ahtbuf != NULL &&
+	               wp != NULL;
 
-	if ( ahtmodel == NULL ) return 0;
-	if ( ahtbuf == NULL ) return 0;
-	if ( wp == NULL ) return 0;
-
 	while(1) {
 
 		if ( wp == NULL ) break;
@@ -2577,7 +2577,7 @@
 		if (( i != TK_OBJ )&&( i != TK_NUM )&&( i != TK_STRING )) {
 			SetError("illegal ahtmes parameter"); return 1;
 		}
-		ahtbuf->PutStr( (char *)s3 );
+		if ( execute ) ahtbuf->PutStr( (char *)s3 );
 
 		if ( wp == NULL ) break;
 
@@ -2585,7 +2585,7 @@
 		if ( i != '+' ) { SetError("invalid ahtmes format"); return 1; }
 
 	}
-	ahtbuf->PutCR();
+	if ( execute ) ahtbuf->PutCR();
 	return 0;
 }
 
@@ -2595,11 +2595,11 @@
 	//		#pack,#epack解析
 	//			(mode:0=normal/1=encrypt)
 	int i;
+	i = GetToken();
+	if ( i != TK_STRING ) {
+		SetError("invalid pack name"); return 1;
+	}
 	if ( packbuf!=NULL ) {
-		i = GetToken();
-		if ( i != TK_STRING ) {
-			SetError("invalid pack name"); return 1;
-		}
 		AddPackfile( (char *)s3, mode );
 	}
 	return 0;
@@ -2613,16 +2613,16 @@
 	int i;
 	char tmp[1024];
 	char optname[1024];
+	i = GetToken();
+	if ( i != TK_OBJ ) {
+		SetError("illegal option name"); return 1;
+	}
+	strncpy( optname, (char *)s3, 128 );
+	i = GetToken();
+	if (( i != TK_OBJ )&&( i != TK_NUM )&&( i != TK_STRING )) {
+		SetError("illegal option parameter"); return 1;
+	}
 	if ( packbuf!=NULL ) {
-		i = GetToken();
-		if ( i != TK_OBJ ) {
-			SetError("illegal option name"); return 1;
-		}
-		strncpy( optname, (char *)s3, 128 );
-		i = GetToken();
-		if (( i != TK_OBJ )&&( i != TK_NUM )&&( i != TK_STRING )) {
-			SetError("illegal option parameter"); return 1;
-		}
 		sprintf( tmp, ";!%s=%s", optname, (char *)s3 );
 		AddPackfile( tmp, 2 );
 	}
@@ -2752,10 +2752,11 @@
 	//		ソース生成コントロール
 	//
 	if (tstrcmp(word,"ifdef")) {		// generate control
+		type = GetToken();
 		if ( mulstr == LMODE_OFF ) {
 			res = PP_SwitchStart( 0 );
 		} else {
-			res = 1; type = GetToken();
+			res = 1;
 			if ( type == TK_OBJ ) {
 				strcase2( word, fixname );
 				AddModuleName( fixname );
@@ -2766,13 +2767,14 @@
 			}
 		}
 		if (res) { SetError("bad ifdef syntax"); return 6; }
-		return 0;
+		goto leave;
 	}
 	if (tstrcmp(word,"ifndef")) {		// generate control
+		type = GetToken();
 		if ( mulstr == LMODE_OFF ) {
 			res = PP_SwitchStart( 0 );
 		} else {
-			res = 1; type = GetToken();
+			res = 1;
 			if ( type == TK_OBJ ) {
 				strcase2( word, fixname );
 				AddModuleName( fixname );
@@ -2783,21 +2785,21 @@
 			}
 		}
 		if (res) { SetError("bad ifdef syntax"); return 6; }
-		return 0;
+		goto leave;
 	}
 	if (tstrcmp(word,"else")) {			// generate control
 		if ( PP_SwitchReverse() ) {
 			SetError("bad else syntax");
 			return 6;
 		}
-		return 0;
+		goto leave;
 	}
 	if (tstrcmp(word,"endif")) {		// generate control
 		if ( PP_SwitchEnd() ) {
 			SetError("bad endif syntax");
 			return 6;
 		}
-		return 0;
+		goto leave;
 	}
 
 	//		これ以降は#off時に実行しません
@@ -2818,12 +2820,14 @@
 			res = lb->SearchLocal( word, fixname );
 
 		//res = lb->Search( word );
-		if ( res < 0 ) return 0;
+		if ( res < 0 ) goto leave;
 		lb->SetFlag( res, -1 );
-		return 0;
+		goto leave;
 	}
 
 	return -1;
+leave:
+	return CheckExtraTokensAtDirective();
 }
 
 
@@ -2854,7 +2858,7 @@
 			} else res=1;
 		}
 		if (res) { SetError("bad if syntax"); return 6; }
-		return 0;
+		goto leave;
 	}
 
 	//		これ以降は#off時に実行しません
@@ -2865,6 +2869,7 @@
 	//
 	if (tstrcmp(word,"include")) {		// text include
 		if ( GetToken() != TK_STRING ) { SetError("invalid include suffix"); return 1; }
+		if ( CheckExtraTokensAtDirective() ) return 1;
 		incinf++;
 		if (incinf>32) { SetError("too many include level"); return 2; }
 		strcpy( tmp_spath, search_path );
@@ -2877,6 +2882,7 @@
 	if (tstrcmp(word,"addition")) {		// text include
 		int add_bak;
 		if ( GetToken() != TK_STRING ) { SetError("invalid addition suffix"); return 1; }
+		if ( CheckExtraTokensAtDirective() ) return 1;
 		incinf++;
 		if (incinf>32) { SetError("too many include level"); return 2; }
 		strcpy( tmp_spath, search_path );
@@ -2893,11 +2899,11 @@
 	}
 	if (tstrcmp(word,"const")) {		// constant define
 		res = PP_Const();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"enum")) {			// constant enum define
 		res = PP_Enum();
-		return res;
+		goto leave;
 	}
 /*
 	if (tstrcmp(word,"define")) {		// keyword define
@@ -2908,35 +2914,35 @@
 */
 	if (tstrcmp(word,"module")) {		// module define
 		res = PP_Module();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"global")) {		// module exit
 		res = PP_Global();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"deffunc")) {		// module function
 		res = PP_Deffunc(0);
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"defcfunc")) {		// module function (2)
 		res = PP_Defcfunc(0);
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"modfunc")) {		// module function (2)
 		res = PP_Deffunc(1);
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"modinit")) {		// module function (3)
 		res = PP_Deffunc(2);
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"modterm")) {		// module function (4)
 		res = PP_Deffunc(3);
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"struct")) {		// struct define
 		res = PP_Struct();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"func")) {			// DLL function
 		res = PP_Func( "func" );
@@ -2948,7 +2954,7 @@
 	}
 	if (tstrcmp(word,"cmd")) {			// DLL function (3.0)
 		res = PP_Cmd( "cmd" );
-		return res;
+		goto leave;
 	}
 /*
 	if (tstrcmp(word,"func2")) {		// DLL function (2)
@@ -2962,7 +2968,7 @@
 	}
 	if (tstrcmp(word,"aht")) {			// AHT definition
 		res = PP_Aht();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"ahtout")) {		// AHT command line output
 		res = PP_Ahtout();
@@ -2970,27 +2976,27 @@
 	}
 	if (tstrcmp(word,"ahtmes")) {		// AHT command line output (mes)
 		res = PP_Ahtmes();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"pack")) {			// packfile process
 		res = PP_Pack( 0 );
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"epack")) {		// packfile process
 		res = PP_Pack( 1 );
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"packopt")) {		// packfile process
 		res = PP_PackOpt();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"runtime")) {		// runtime process
 		res = PP_RuntimeOpt();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"cmpopt")) {		// compile option process
 		res = PP_CmpOpt();
-		return res;
+		goto leave;
 	}
 	if (tstrcmp(word,"usecom")) {		// COM definition
 		res = PP_Usecom();
@@ -3004,8 +3010,19 @@
 	wrtbuf->PutCR();
 	//wrtbuf->PutStr( (char *)s3 );
 	return -1;
+leave:
+	if( res && !(res & 0x1000) ) return res;
+	if( CheckExtraTokensAtDirective() ) return 1;
+	return res;
 }
 
+int CToken::CheckExtraTokensAtDirective() {
+	if ( GetToken() != TK_NONE ) {
+		SetError("extra tokens at end of preprocessing directive");
+		return 1;
+	}
+	return 0;
+}
 
 int CToken::ExpandLine( CMemBuf *buf, CMemBuf *src )
 {
Index: token.h
===================================================================
--- token.h	(revision 189)
+++ token.h	(working copy)
@@ -238,6 +238,8 @@
 	char *SendLineBuf( char *str );
 	char *SendLineBufPP( char *str, int *lines );
 	int ReplaceLineBuf( char *str1, char *str2, char *repl, int macopt, MACDEF *macdef );
+	
+	int CheckExtraTokensAtDirective();
 
 
 	//		For Code Generate

追記(2009-02-03T12:50:09+09:00)

PreprocessNM() で処理されるプリプロセッサ命令(#ifdef, #ifndef, #else, #endif, #undef)でコメントをつけていたときまでエラーになることに気づきました。(ex. #undef foo ; comment)
これではだめですね。ちなみに Preprocess() で処理されるプリプロセッサ命令でコメントがあっても大丈夫なのは ExpandToken 中で linebuf 内にコメントがあった場所に '\0' を書き込むからです。