/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
/*---------------------------------------------------------------------------*/
static const char* g_source;		//%{～%}で指定されたソースを連結する。
static ght_hash_table_t* ht_map;	//状態マップの名前⇒(ST_Map*)
static ght_hash_table_t* ht_stateName;	//状態の名前⇒状態の名前	同じ名前の状態が複数存在する可能性が有るので、名前と(ST_State*)は1:1ではない。
static ght_hash_table_t* ht_transName;	//遷移の名前⇒状態の名前	同じ名前の遷移が複数存在する可能性が有るので、名前と(ST_Trans*)は1:1ではない。
static ght_hash_table_t* ht_func;	//関数の名前⇒(ST_Func*)	op: 1=(void(*)(ST_FsmObj*)),2=(int(*)(ST_FsmObj*,int,void*)),3=(void(*)(ST_FsmObj*,int,void*))	1はEnter/Leave関数,2はGuard関数,3はAct関数に相当する。
//{{進捗表示用
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
void yyerror(const char* fmt, ...) {	//←{{2015/03/06:'-e'オプション対応のためにwinapp.cから移動しました。}}
	//'-e'オプションが指定されていて、最初のエラーログを記録する時ならば…
	if(*err_path && !fp_err) {
		//エラーログファイルを作成する。
		fp_err = fopen(err_path, "w");
		if(!fp_err) { die("%s が作成できません。\n", err_path); }
		//エラーログファイルの出力ストリームは、バッファリング無しとする。
		// - die()で終了した時(abort()時)に、バッファ分がフラッシュされないようなので、こうしておく必要が有る。
		//   別の案として、この下の処理で毎回fflush()する方法も有るが、バッファリング無しとする方が簡単だろう。
		setbuf(fp_err, NULL);
	}
	//エラーメッセージ(,及び,エラーログ)を出力する。
	             fprintf(stderr, "### error at line %d : ", yylineno);	//エラーメッセージ
	if(fp_err) { fprintf(fp_err, "### error at line %d : ", yylineno); }	//エラーログ
	{
		va_list ap;
		va_start(ap, fmt);
		             vfprintf(stderr, fmt, ap);		//エラーメッセージ
		if(fp_err) { vfprintf(fp_err, fmt, ap); }	//エラーログ
		va_end(ap);
	}
	             fputc('\n', stderr);	//エラーメッセージ
	if(fp_err) { fputc('\n', fp_err); }	//エラーログ
}
/*---------------------------------------------------------------------------*/
/* ローカル関数宣言 */
__declspec(noreturn) static void usage();
static void pass2(int bOptimize);
static void output(char* sRegOut, char* sTblOut);
/*---------------------------------------------------------------------------*/
static ght_hash_table_t* Table_new() {
	ght_hash_table_t* p_ht = ght_create(0);
	ght_set_rehash(p_ht, 1);
	return p_ht;
}
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int opt, bOptimize = 1;	//'-d'オプションが指定されなければ最適化を行う。
	char* sRegOut = NULL;	//'-r'オプションが指定されたらレジストリスクリプトファイル名を設定する。
	char* sTblOut = NULL;	//'-t'オプションが指定されたらC言語ソースファイル名を設定する。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "dr:t:eh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;		//最適化を行わない。
			break;
		case 'r':
			sRegOut = optarg;	//レジストリスクリプトファイル名を設定する。
			break;
		case 't':
			sTblOut = optarg;	//C言語ソースファイル名を設定する。
			break;
		case 'e':
			{
				//エラーログファイル名を作成する。
				// - カレントフォルダに'実行ファイル名.err'という名前で生成する事にした。この動作は、'lk33.exe'の仕様を参考にした。
				char fname[_MAX_FNAME];
				_splitpath(argv[0], NULL, NULL, fname, NULL);
				_makepath(err_path, NULL, NULL, fname, ".err");
				//'-e'オプションが指定されたら、最初に確実に、既存のエラーログファイルを削除しておく。
				//前回に生成したエラーログが残っていて、今回生成したエラーログと混同する危険を避けるためである。
				//尚、'-e'オプションが指定されなかった場合は、既存のエラーログファイルを削除する処理は行わない。
				remove(err_path);
			}
			break;
		case 'h':
		default:
			usage();
		}
	}
	//ハッシュテーブルを作成する。
	ht_map		= Table_new();
	ht_stateName	= Table_new();
	ht_transName	= Table_new();
	ht_func		= Table_new();
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		yylex_init(stdin);
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { die("エラーが発生しました。\n"); }
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { die("%s が開けません。\n", argv[i]); }
			yylex_init(fp);
			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { die("%s: エラーが発生しました。\n", argv[i]); }
			fclose(fp);
		}
	}
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass1 ok");
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass2 ok");
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
    //}}進捗表示用
	if(yynerrs != 0) { die("エラーが発生しました。\n"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。
	//--- 出力処理 ---
	//出力処理を行う。
	output(sRegOut, sTblOut);
	if(yynerrs != 0) { die("エラーが発生しました。\n"); }
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dFsmMapC - 有限状態機械マップコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dFsmMapC [option ...] [filename ...] \n");
	fprintf(stderr, "  ・filenameを指定しなければ、標準入力から読み込みます。\n");
	fprintf(stderr, "  ・filenameは、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -d           最適化を行いません。\n");
	fprintf(stderr, "               このオプションを指定しなければ、最適化を行います。\n");
	fprintf(stderr, "  -rファイル名 有限状態機械マップのレジストリスクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "  -tファイル名 関数テーブルのC言語ソースを、ファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "  -e           エラーログファイルを生成します。\n");
	fprintf(stderr, "               このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -h           使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①dFsmMapC.exe FsmMap.txt > FsmMap.out\n");
	fprintf(stderr, "  　FsmMap.txtをコンパイルして、FsmMap.outへ出力します。\n");
	fprintf(stderr, "  ②dFsmMapC.exe -rRegFsmMap.txt -tFsmMap.c FsmMap.txt > FsmMap.h\n");
	fprintf(stderr, "  　FsmMap.txtをコンパイルして、ラベルをFsmMap.hへ出力します。\n");
	fprintf(stderr, "  　有限状態機械マップのレジストリスクリプトを、RegFsmMap.txtへ出力します。\n");
	fprintf(stderr, "  　関数テーブルのC言語ソースを、FsmMap.hへ出力します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
static char* GetFuncDesc(ST_Func* func) {
	//コード指定の関数ならば…
	if(func->code) {
		char** pp = strsplit(func->code, "\n", 0);		//コードを行分割する。
		char** qq = pp;
		while(*qq) { strstrip(*qq++); }				//各行の先頭と末尾の空白文字を削除する。
		return strdup_printf("`%s`", strjoinv(" ", pp));	//各行をスペースを挟んで連結し、`～`で囲んで返す。
	//名前指定の関数ならば…
	} else {
		return func->name;
	}
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void add_source(char* source) {
	//ソースを追加する。
	g_source = strconcat((g_source ? g_source : ""), source, NULL);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void add_map(ST_Map* map) {
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass1 %s", map->name);
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
    //}}進捗表示用
	//--- 状態マップ登録処理 ---
	if(ght_insert(ht_map,
		       map,
		strlen(map->name) + 1/*nul*/,
		       map->name)) {
		yyerror("状態マップの名前が重複しています。(%s)", map->name);
		yynerrs++;		//呼び出し元(main)に知らせるために
		return;			//全てのセマンティクスエラーを報告するために継続する。
	}
	//--- 関数登録処理 ---
	{
		ght_iterator_t it_transSet, it_trans, it_state;
		char* name;
		ST_Func* func;
		ST_TransSet* transSet;
		ST_Trans* trans;
		ST_State* state;
		ST_MapBody* mapBody = map->mapBody;
		//状態マップのEnter関数
		if(mapBody->enter) {
			if(!*mapBody->enter->name) { mapBody->enter->name = strjoin("_", map->name, "enter", NULL); }
			func = ght_get(ht_func,
				strlen(mapBody->enter->name) + 1/*nul*/,
				       mapBody->enter->name);
			if(func && (func->op != mapBody->enter->op)) {
				yyerror("Enter関数の種類が相違しています。(%s)", GetFuncDesc(mapBody->enter));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       mapBody->enter,
				strlen(mapBody->enter->name) + 1/*nul*/,
				       mapBody->enter->name);	//既に登録されている場合はダミー処理になる。
		}
		//状態マップのLeave関数
		if(mapBody->leave) {
			if(!*mapBody->leave->name) { mapBody->leave->name = strjoin("_", map->name, "leave", NULL); }
			func = ght_get(ht_func,
				strlen(mapBody->leave->name) + 1/*nul*/,
				       mapBody->leave->name);
			if(func && (func->op != mapBody->leave->op)) {
				yyerror("Leave関数の種類が相違しています。(%s)", GetFuncDesc(mapBody->leave));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       mapBody->leave,
				strlen(mapBody->leave->name) + 1/*nul*/,
				       mapBody->leave->name);	//既に登録されている場合はダミー処理になる。
		}
		//～～状態マップの遷移セット～～
		for(transSet = ght_first(mapBody->ht_transSet, &it_transSet, &name);
		    transSet;
		    transSet = ght_next( mapBody->ht_transSet, &it_transSet, &name)) {
			//～～状態マップの遷移セットの遷移～～
			for(trans = ght_first(transSet->ht_trans, &it_trans, &name);	//ガード名をキーとする。ガードが無い遷移は長さ(0)のキーを持つ。ガード名が空文字の遷移は終端文字のみの長さ(1)のキーを持つので区別できる。
			    trans;
			    trans = ght_next( transSet->ht_trans, &it_trans, &name)) {
				//状態マップの遷移セットの遷移のGuard関数
				if(trans->transBody->guard) {
					if(!*trans->transBody->guard->name) { trans->transBody->guard->name = strjoin("_", map->name, trans->name, "guard", NULL); }
					func = ght_get(ht_func,
						strlen(trans->transBody->guard->name) + 1/*nul*/,
						       trans->transBody->guard->name);
					if(func && (func->op != trans->transBody->guard->op)) {
						yyerror("Guard関数の種類が相違しています。(%s)", GetFuncDesc(trans->transBody->guard));
						yynerrs++;		//呼び出し元(main)に知らせるために
						//全てのセマンティクスエラーを報告するために継続する。
					}
					ght_insert(ht_func,
						       trans->transBody->guard,
						strlen(trans->transBody->guard->name) + 1/*nul*/,
						       trans->transBody->guard->name);	//既に登録されている場合はダミー処理になる。
				}
				//状態マップの遷移セットの遷移のAct関数
				if(trans->transBody->act) {
					if(!*trans->transBody->act->name) { trans->transBody->act->name = strjoin("_", map->name, trans->name, "act", NULL); }
					func = ght_get(ht_func,
						strlen(trans->transBody->act->name) + 1/*nul*/,
						       trans->transBody->act->name);
					if(func && (func->op != trans->transBody->act->op)) {
						yyerror("Act関数の種類が相違しています。(%s)", GetFuncDesc(trans->transBody->act));
						yynerrs++;		//呼び出し元(main)に知らせるために
						//全てのセマンティクスエラーを報告するために継続する。
					}
					ght_insert(ht_func,
						       trans->transBody->act,
						strlen(trans->transBody->act->name) + 1/*nul*/,
						       trans->transBody->act->name);	//既に登録されている場合はダミー処理になる。
				}
			}
		}
		//～～状態マップの状態～～
		for(state = ght_first(mapBody->ht_state, &it_state, &name);
		    state;
		    state = ght_next( mapBody->ht_state, &it_state, &name)) {
			ST_StateBody* stateBody = state->stateBody;
			//状態マップの状態のEnter関数
			if(stateBody->enter) {
				if(!*stateBody->enter->name) { stateBody->enter->name = strjoin("_", map->name, state->name, "enter", NULL); }
				func = ght_get(ht_func,
					strlen(stateBody->enter->name) + 1/*nul*/,
					       stateBody->enter->name);
				if(func && (func->op != stateBody->enter->op)) {
					yyerror("関数の種類が相違しています。(%s)", GetFuncDesc(stateBody->enter));
					yynerrs++;		//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する。
				}
				ght_insert(ht_func,
					       stateBody->enter,
					strlen(stateBody->enter->name) + 1/*nul*/,
					       stateBody->enter->name);	//既に登録されている場合はダミー処理になる。
			}
			//状態マップの状態のLeave関数
			if(stateBody->leave) {
				if(!*stateBody->leave->name) { stateBody->leave->name = strjoin("_", map->name, state->name, "leave", NULL); }
				func = ght_get(ht_func,
					strlen(stateBody->leave->name) + 1/*nul*/,
					       stateBody->leave->name);
				if(func && (func->op != stateBody->leave->op)) {
					yyerror("関数の種類が相違しています。(%s)", GetFuncDesc(stateBody->leave));
					yynerrs++;		//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する。
				}
				ght_insert(ht_func,
					       stateBody->leave,
					strlen(stateBody->leave->name) + 1/*nul*/,
					       stateBody->leave->name);	//既に登録されている場合はダミー処理になる。
			}
			//～～状態マップの状態の遷移セット～～
			for(transSet = ght_first(stateBody->ht_transSet, &it_transSet, &name);
			    transSet;
			    transSet = ght_next( stateBody->ht_transSet, &it_transSet, &name)) {
				//～～状態マップの状態の遷移セットの遷移～～
				for(trans = ght_first(transSet->ht_trans, &it_trans, &name);	//ガード名をキーとする。ガードが無い遷移は長さ(0)のキーを持つ。ガード名が空文字の遷移は終端文字のみの長さ(1)のキーを持つので区別できる。
				    trans;
				    trans = ght_next( transSet->ht_trans, &it_trans, &name)) {
					//状態マップの状態の遷移セットの遷移のGuard関数
					if(trans->transBody->guard) {
						if(!*trans->transBody->guard->name) { trans->transBody->guard->name = strjoin("_", map->name, state->name, trans->name, "guard", NULL); }
						func = ght_get(ht_func,
							strlen(trans->transBody->guard->name) + 1/*nul*/,
							       trans->transBody->guard->name);
						if(func && (func->op != trans->transBody->guard->op)) {
							yyerror("Guard関数の種類が相違しています。(%s)", GetFuncDesc(trans->transBody->guard));
							yynerrs++;		//呼び出し元(main)に知らせるために
							//全てのセマンティクスエラーを報告するために継続する。
						}
						ght_insert(ht_func,
							       trans->transBody->guard,
							strlen(trans->transBody->guard->name) + 1/*nul*/,
							       trans->transBody->guard->name);	//既に登録されている場合はダミー処理になる。
					}
					//状態マップの状態の遷移セットの遷移のAct関数
					if(trans->transBody->act) {
						if(!*trans->transBody->act->name) { trans->transBody->act->name = strjoin("_", map->name, state->name, trans->name, "act", NULL); }
						func = ght_get(ht_func,
							strlen(trans->transBody->act->name) + 1/*nul*/,
							       trans->transBody->act->name);
						if(func && (func->op != trans->transBody->act->op)) {
							yyerror("Act関数の種類が相違しています。(%s)", GetFuncDesc(trans->transBody->act));
							yynerrs++;		//呼び出し元(main)に知らせるために
							//全てのセマンティクスエラーを報告するために継続する。
						}
						ght_insert(ht_func,
							       trans->transBody->act,
							strlen(trans->transBody->act->name) + 1/*nul*/,
							       trans->transBody->act->name);	//既に登録されている場合はダミー処理になる。
					}
				}
			}
		}
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Map* Map_new(char* name, ST_MapBody* mapBody) {
	ST_Map* map = calloc(1, sizeof(ST_Map));
	map->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	map->name		= name;
	map->mapBody		= mapBody;
	//状態マップの構造が正しい事を確認する。
	{
		if(!ght_size(map->mapBody->ht_state)) {
			yyerror("状態が無いマップは作成できません。(%s)", map->name);
			yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
		}
	}
	{
		ght_iterator_t it_transSet, it_trans, it_state;
		ST_TransSet* transSet;
		ST_Trans* trans;
		ST_State* state;
		//～～状態マップの遷移セット～～
		for(transSet = ght_first(mapBody->ht_transSet, &it_transSet, &name);
		    transSet;
		    transSet = ght_next( mapBody->ht_transSet, &it_transSet, &name)) {
			//～～状態マップの遷移セットの遷移～～
			for(trans = ght_first(transSet->ht_trans, &it_trans, &name);	//ガード名をキーとする。ガードが無い遷移は長さ(0)のキーを持つ。ガード名が空文字の遷移は終端文字のみの長さ(1)のキーを持つので区別できる。
			    trans;
			    trans = ght_next( transSet->ht_trans, &it_trans, &name)) {
				if(trans->transBody->nextState && trans->transBody->nextState->state) {
					//この状態マップに、遷移先状態が無ければ、エラーとする。
					if(!ght_get(mapBody->ht_state,
						strlen(trans->transBody->nextState->state) + 1/*nul*/,
						       trans->transBody->nextState->state)) {
						yyerror("遷移先状態が存在しません。(%s)", trans->transBody->nextState->state);
						yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
						return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
					}
				}
			}
		}
		//～～状態マップの状態～～
		for(state = ght_first(mapBody->ht_state, &it_state, &name);
		    state;
		    state = ght_next( mapBody->ht_state, &it_state, &name)) {
			ST_StateBody* stateBody = state->stateBody;
			//～～状態マップの状態の遷移セット～～
			for(transSet = ght_first(stateBody->ht_transSet, &it_transSet, &name);
			    transSet;
			    transSet = ght_next( stateBody->ht_transSet, &it_transSet, &name)) {
				//～～状態マップの状態の遷移～～
				for(trans = ght_first(transSet->ht_trans, &it_trans, &name);	//ガード名をキーとする。ガードが無い遷移は長さ(0)のキーを持つ。ガード名が空文字の遷移は終端文字のみの長さ(1)のキーを持つので区別できる。
				    trans;
				    trans = ght_next( transSet->ht_trans, &it_trans, &name)) {
					if(trans->transBody->nextState && trans->transBody->nextState->state) {
						//この状態マップに、遷移先状態が無ければ、エラーとする。
						if(!ght_get(mapBody->ht_state,
							strlen(trans->transBody->nextState->state) + 1/*nul*/,
							       trans->transBody->nextState->state)) {
							yyerror("遷移先状態が存在しません。(%s)", trans->transBody->nextState->state);
							yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
							return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
						}
					}
				}
			}
		}
	}
	return map;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_new() {
	ST_MapBody* mapBody = calloc(1, sizeof(ST_MapBody));
	mapBody->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	mapBody->ht_transSet	= Table_new();
	mapBody->ht_state	= Table_new();
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addEnter(ST_MapBody* mapBody, ST_Func* enter) {
	if(mapBody->enter) {
		yyerror("Enter関数が重複しています。(%s)", GetFuncDesc(enter));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	mapBody->enter		= enter;
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addLeave(ST_MapBody* mapBody, ST_Func* leave) {
	if(mapBody->leave) {
		yyerror("Leave関数が重複しています。(%s)", GetFuncDesc(leave));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	mapBody->leave		= leave;
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addTrans(ST_MapBody* mapBody, ST_Trans* trans) {
	ST_TransSet* transSet = ght_get(mapBody->ht_transSet,
		strlen(trans->name) + 1/*nul*/,
		       trans->name);
	if(!transSet) {
		transSet = calloc(1, sizeof(ST_TransSet));
		transSet->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
		transSet->name		= trans->name;
		transSet->ht_trans	= Table_new();
		if(ght_insert(mapBody->ht_transSet,
			transSet,
			strlen(transSet->name) + 1/*nul*/,
			       transSet->name)) { DIE(); }		//ここで停止したらバグ
	}
	if(ght_insert(transSet->ht_trans,
		trans,
		        trans->transBody->guard ?
		(strlen(trans->transBody->guard->name) + 1/*nul*/) : 0,	//ガード名をキーとする。ガードが無い遷移は長さ(0)のキーを持つ。ガード名が空文字の遷移は終端文字のみの長さ(1)のキーを持つので区別できる。
		        trans->transBody->guard ?
		        trans->transBody->guard->name              : NULL)) {
		if(trans->transBody->guard) {
			yyerror("遷移が重複しています。(%s[%s])", trans->name, GetFuncDesc(trans->transBody->guard));
		} else {
			yyerror("遷移が重複しています。(%s)",     trans->name);
		}
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addState(ST_MapBody* mapBody, ST_State* state) {
	if(ght_insert(mapBody->ht_state,
		state,
		strlen(state->name) + 1/*nul*/,
		       state->name)) {
		yyerror("状態が重複しています。(%s)", state->name);
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_State* State_new(char* name, ST_StateBody* stateBody) {
	ST_State* state = calloc(1, sizeof(ST_State));
	state->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	state->name		= name;
	state->stateBody	= stateBody;
	//状態の構造が正しい事を確認する。
	if(!state->stateBody->enter &&
	   !state->stateBody->leave &&
	   !ght_size(state->stateBody->ht_transSet)) {
		yyerror("空の状態は作成できません。(%s)", state->name);
		yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;		//y.tab.yのstateの処理でYYERRORを行う。
	}
	//--- 状態名登録処理 ---
	{
		ght_insert(ht_stateName,
			       state->name,
			strlen(state->name) + 1/*nul*/,
			       state->name);	//既に登録されている場合はダミー処理になる。
	}
	return state;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_new() {
	ST_StateBody* stateBody = calloc(1, sizeof(ST_StateBody));
	stateBody->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	stateBody->ht_transSet	= Table_new();
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_addEnter(ST_StateBody* stateBody, ST_Func* enter) {
	if(stateBody->enter) {
		yyerror("Enter関数が重複しています。(%s)", GetFuncDesc(enter));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	stateBody->enter	= enter;
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_addLeave(ST_StateBody* stateBody, ST_Func* leave) {
	if(stateBody->leave) {
		yyerror("Leave関数が重複しています。(%s)", GetFuncDesc(leave));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	stateBody->leave	= leave;
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_addTrans(ST_StateBody* stateBody, ST_Trans* trans) {
	ST_TransSet* transSet = ght_get(stateBody->ht_transSet,
		strlen(trans->name) + 1/*nul*/,
		       trans->name);
	if(!transSet) {
		transSet = calloc(1, sizeof(ST_TransSet));
		transSet->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
		transSet->name		= trans->name;
		transSet->ht_trans	= Table_new();
		if(ght_insert(stateBody->ht_transSet,
			transSet,
			strlen(transSet->name) + 1/*nul*/,
			       transSet->name)) { DIE(); }		//ここで停止したらバグ
	}
	if(ght_insert(transSet->ht_trans,
		trans,
		        trans->transBody->guard ?
		(strlen(trans->transBody->guard->name) + 1/*nul*/) : 0,	//ガード名をキーとする。ガードが無い遷移は長さ(0)のキーを持つ。ガード名が空文字の遷移は終端文字のみの長さ(1)のキーを持つので区別できる。
		        trans->transBody->guard ?
		        trans->transBody->guard->name              : NULL)) {
		if(trans->transBody->guard) {
			yyerror("遷移が重複しています。(%s[%s])", trans->name, GetFuncDesc(trans->transBody->guard));
		} else {
			yyerror("遷移が重複しています。(%s)",     trans->name);
		}
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Trans* Trans_new(char* name, ST_TransBody* transBody) {
	ST_Trans* trans = calloc(1, sizeof(ST_Trans));
	trans->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	trans->name		= name;
	trans->transBody	= transBody;
	//遷移の構造が正しい事を確認する。
	if(!trans->transBody->act &&
	   !trans->transBody->nextState) {
		yyerror("Act関数も遷移先状態も無い遷移は作成できません。(%s)", trans->name);
		yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;		//y.tab.yのtransの処理でYYERRORを行う。
	}
	//--- 遷移名登録処理 ---
	{
		ght_insert(ht_transName,
			       trans->name,
			strlen(trans->name) + 1/*nul*/,
			       trans->name);	//既に登録されている場合はダミー処理になる。
	}
	return trans;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TransBody* TransBody_newGuard(ST_Func* guard) {
	ST_TransBody* transBody = calloc(1, sizeof(ST_TransBody));
	transBody->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	transBody->guard	= guard;
	return transBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TransBody* TransBody_newAct(ST_Func* act) {
	ST_TransBody* transBody = calloc(1, sizeof(ST_TransBody));
	transBody->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	transBody->act		= act;
	return transBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TransBody* TransBody_newState(ST_NextState* nextState) {
	ST_TransBody* transBody = calloc(1, sizeof(ST_TransBody));
	transBody->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	transBody->nextState	= nextState;
	return transBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TransBody* TransBody_addGuard(ST_TransBody* transBody, ST_Func* guard) {
	if(transBody->guard) {
		yyerror("Guard関数が重複しています。(%s)", GetFuncDesc(guard));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return transBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	transBody->guard	= guard;
	return transBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TransBody* TransBody_addAct(ST_TransBody* transBody, ST_Func* act) {
	if(transBody->act) {
		yyerror("Act関数が重複しています。(%s)", GetFuncDesc(act));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return transBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	transBody->act		= act;
	return transBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TransBody* TransBody_addState(ST_TransBody* transBody, ST_NextState* nextState) {
	if(transBody->nextState) {
		switch(transBody->nextState->op) {
		default:DIE();
		case 1/*set*/:
			yyerror("遷移先状態が重複しています。(%s)", nextState->state);
			break;
		case 2/*push*/:
			yyerror("遷移先状態が重複しています。(push(%s))", nextState->state);
			break;
		case 3/*pop*/:
			yyerror("遷移先状態が重複しています。(pop)");
			break;
		}
		yynerrs++;		//呼び出し元(main)に知らせるために
		return transBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	transBody->nextState	= nextState;
	return transBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Func* Func_new(int op, char* name, char* code) {
	ST_Func* func = calloc(1, sizeof(ST_Func));
	func->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	func->op		= op;
	func->name		= name;
	func->code		= code;
    //{{コード指定([`～`],{`～`})だったならば、関数の名前を作成する。
	if((name &&  code) || (!name && !code)) { DIE(); }	//nameとcodeの片方のみ指定されなければならない。そうなっていなければy.tab.yのバグである。
	if(!name) {
	    //}}static
		static ght_hash_table_t* ht[3+1];		//┬op: 1=(void(*)(ST_FsmObj*)),2=(int(*)(ST_FsmObj*,int,void*)),3=(void(*)(ST_FsmObj*,int,void*))	1はEnter/Leave関数,2はGuard関数,3はAct関数に相当する。
		static int              cnt[3+1];		//┘
	    //{{static
		ST_Func* tmp;
		if((op < 1) || (op > 3)) { DIE(); }						//y.tab.yのバグ
		if(!ht[op]) { ht[op] = Table_new(); }
		if((tmp = ght_get(ht[op], strlen(code) + 1/*nul*/, code))) {			//同じ関数種類で同じコードの関数が既に有れば…
			if(!tmp->name || !*tmp->name) { DIE(); }				//バグ。コード指定の関数が既に登録されているならば、名前が作成されているはずである。
			func->name = tmp->name;							//同じ名前を使う。func自体を同じにしても良いのだが、lineが異なるので。(動作上は問題ないが)
		} else {
			func->name = strdup_printf("%d_%05d", op, ++cnt[op]);			//名前を作成する。	ユーザー指定の関数名と被らなそうな名前で、'FsmFunc_?_?????'とする事にした。変更可。
			if(ght_insert(ht[op], func, strlen(code) + 1/*nul*/, code)) { DIE(); }	//バグ
		}
	}
    //}}コード指定([`～`],{`～`})だったならば、関数の名前を作成する。
	return func;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_NextState* NextState_new(int op, char* state) {
	ST_NextState* nextState = calloc(1, sizeof(ST_NextState));
	nextState->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	nextState->op		= op;
	nextState->state	= state;
	return nextState;
}
/*---------------------------------------------------------------------------*/
static char** TBL_Map;
static int       nMap;
static ght_hash_table_t* ht_mapNo;	//状態マップの名前⇒状態マップの番号
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static char** TBL_State;
static int       nState;
static ght_hash_table_t* ht_stateNo;	//状態の名前⇒関数の番号
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static char** TBL_Trans;
static int       nTrans;
static ght_hash_table_t* ht_transNo;	//遷移の名前⇒関数の番号
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Name_compar(const void* _x, const void* _y) {
	const char** x = _x;
	const char** y = _y;
	return strcmp(*x, *y);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static ST_Func** TBL_Func;
static int          nFunc;
static ght_hash_table_t* ht_funcNo;	//関数の名前⇒関数の番号
static int Func_compar(const void* _x, const void* _y) {
	const ST_Func* x = *(const ST_Func**)_x;
	const ST_Func* y = *(const ST_Func**)_y;
    //{{コード指定に対して関数の名前を自動生成した関数を後ろにするための判断。関数の種類によるソートよりも優先である。
	if( x->code && !y->code) { return  1; }
	if(!x->code &&  y->code) { return -1; }
    //}}コード指定に対して関数の名前を自動生成した関数を後ろにするための判断。関数の種類によるソートよりも優先である。
	//関数の種類によるソート。
	if(x->op < y->op) { return -1; }
	if(x->op > y->op) { return  1; }
	//関数の名前によるソート。
	return strcmp(x->name, y->name);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//2パス目の処理
static void pass2(int bOptimize) {
	char* name;
	ght_iterator_t it;
	//～～状態マップ～～
	{
		ht_mapNo	= Table_new();
		//状態マップを名前の昇順にソートする。
		{
			ST_Map* map;
			TBL_Map = calloc(ght_size(ht_map), sizeof(char*));
			for(map = ght_first(ht_map, &it, &name);
			    map;
			    map = ght_next( ht_map, &it, &name)) {
				TBL_Map[nMap]	= name;
				        nMap++;
			}
			qsort(TBL_Map, nMap, sizeof(char*), Name_compar);
		}
		//状態マップ番号のハッシュテーブルを登録する。
		{
			int iMap;
			for(iMap = 0; iMap < nMap; iMap++) {
				if(ght_insert(ht_mapNo,
					(void*)(iMap + 1),				//状態マップ番号は1ベース
					strlen(TBL_Map[iMap]) + 1/*nul*/,
					       TBL_Map[iMap])) { DIE(); }		//ここで停止したらバグ
			}
		}
	}
	//～～状態～～
	{
		ht_stateNo	= Table_new();
		//状態を名前の昇順にソートする。
		{
			TBL_State = calloc(ght_size(ht_stateName), sizeof(char*));
			for(name = ght_first(ht_stateName, &it, &name);
			    name;
			    name = ght_next( ht_stateName, &it, &name)) {
				TBL_State[nState]	= name;
				          nState++;
			}
			qsort(TBL_State, nState, sizeof(char*), Name_compar);
		}
		//状態番号のハッシュテーブルを登録する。
		{
			int iState;
			for(iState = 0; iState < nState; iState++) {
				if(ght_insert(ht_stateNo,
					(void*)(iState + 1),				//状態番号は1ベース
					strlen(TBL_State[iState]) + 1/*nul*/,
					       TBL_State[iState])) { DIE(); }		//ここで停止したらバグ
			}
		}
	}
	//～～遷移～～
	{
		ht_transNo	= Table_new();
		//遷移を名前の昇順にソートする。
		{
			TBL_Trans = calloc(ght_size(ht_transName), sizeof(char*));
			for(name = ght_first(ht_transName, &it, &name);
			    name;
			    name = ght_next( ht_transName, &it, &name)) {
				if(!strcmp(name, "default")) { continue; }		//default遷移の遷移番号は(0)固定
				TBL_Trans[nTrans]	= name;
				          nTrans++;
			}
			qsort(TBL_Trans, nTrans, sizeof(char*), Name_compar);
		}
		//遷移番号のハッシュテーブルを登録する。
		{
			int iTrans;
			for(iTrans = 0; iTrans < nTrans; iTrans++) {
				if(ght_insert(ht_transNo,
					(void*)(iTrans + 1),				//遷移番号は1ベース
					strlen(TBL_Trans[iTrans]) + 1/*nul*/,
					       TBL_Trans[iTrans])) { DIE(); }		//ここで停止したらバグ
			}
		}
	}
	//～～関数～～
	{
		ht_funcNo	= Table_new();
		//関数を{関数の種類,関数の名前}の昇順にソートする。
		{
			ST_Func* func;
			TBL_Func = calloc(ght_size(ht_func), sizeof(ST_Func*));
			for(func = ght_first(ht_func, &it, &name);
			    func;
			    func = ght_next( ht_func, &it, &name)) {
				TBL_Func[nFunc++] = func;
			}
			qsort(TBL_Func, nFunc, sizeof(ST_Func*), Func_compar);
		}
		//関数番号のハッシュテーブルを登録する。
		{
			int iFunc;
			for(iFunc = 0; iFunc < nFunc; iFunc++) {
				if(ght_insert(ht_funcNo,
					(void*)(iFunc + 1),				//関数番号は1ベース
					strlen(TBL_Func[iFunc]->name) + 1/*nul*/,
					       TBL_Func[iFunc]->name)) { DIE(); }	//ここで停止したらバグ
			}
		}
	}
	//2015/06/21現在、最適化処理は有りません。
	//今後、最適化機能を追加する場合のためのプレースホルダとして、関数のみ用意して有ります。
	//現在のところは、コマンドラインオプションに'-d'を指定してもしなくても違いは有りません。
	if(bOptimize) { /** no job **/ }
}
/*---------------------------------------------------------------------------*/
//出力処理
static int Label_compar(const void* x, const void* y) {
	return strcmp(*(char**)x, *(char**)y);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//状態マップを、状態マップ名の昇順にソートする。
static int Map_compar(const void* _x, const void* _y) {
	const char* x = (*(ST_Map**)_x)->name;
	const char* y = (*(ST_Map**)_y)->name;
	return strcmp(x, y);
}
static ST_Map** getSortedMap(ght_hash_table_t* p_ht) {
	ght_iterator_t it;
	const void* key;
	int keySize, i = 0, n = ght_size(p_ht);
	ST_Map** pp_map = calloc(n + 1/*NULL*/, sizeof(ST_Map*));
	ST_Map* map;
	for(map = ght_first_keysize(p_ht, &it, &key, &keySize);
	    map;
	    map = ght_next_keysize( p_ht, &it, &key, &keySize)) {
		pp_map[i++] = map;
	}
	qsort(pp_map, n, sizeof(ST_Map*), Map_compar);
	return pp_map;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//状態を、状態名の昇順にソートする。
static int State_compar(const void* _x, const void* _y) {
	const char* x = (*(ST_State**)_x)->name;
	const char* y = (*(ST_State**)_y)->name;
	return strcmp(x, y);
}
static ST_State** getSortedState(ght_hash_table_t* p_ht) {
	ght_iterator_t it;
	const void* key;
	int keySize, i = 0, n = ght_size(p_ht);
	ST_State** pp_state = calloc(n + 1/*NULL*/, sizeof(ST_State*));
	ST_State* state;
	for(state = ght_first_keysize(p_ht, &it, &key, &keySize);
	    state;
	    state = ght_next_keysize( p_ht, &it, &key, &keySize)) {
		pp_state[i++] = state;
	}
	qsort(pp_state, n, sizeof(ST_State*), State_compar);
	return pp_state;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//遷移セットを、遷移名の昇順にソートする。
static int TransSet_compar(const void* _x, const void* _y) {
	const char* x = (*(ST_TransSet**)_x)->name;
	const char* y = (*(ST_TransSet**)_y)->name;
	return strcmp(x, y);
}
static ST_TransSet** getSortedTransSet(ght_hash_table_t* p_ht) {
	ght_iterator_t it;
	const void* key;
	int keySize, i = 0, n = ght_size(p_ht);
	ST_TransSet** pp_transSet = calloc(n + 1/*NULL*/, sizeof(ST_TransSet*));
	ST_TransSet* transSet;
	for(transSet = ght_first_keysize(p_ht, &it, &key, &keySize);
	    transSet;
	    transSet = ght_next_keysize( p_ht, &it, &key, &keySize)) {
		pp_transSet[i++] = transSet;
	}
	qsort(pp_transSet, n, sizeof(ST_TransSet*), TransSet_compar);
	return pp_transSet;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//遷移を、ガード名の昇順,ただし,ガードが無い遷移(有れば)を末尾にソートする。
static int Trans_compar(const void* _x, const void* _y) {
	const ST_Func* x = (*(ST_Trans**)_x)->transBody->guard;
	const ST_Func* y = (*(ST_Trans**)_y)->transBody->guard;
	if(x) {
		if(y) {
			return strcmp(x->name, y->name);
		} else {
			return -1;	//(x)はガードが有る,(y)はガードが無い⇒(y)を後にする
		}
	} else {
		if(y) {
			return  1;	//(x)はガードが無い,(y)はガードが有る⇒(x)を後にする
		} else {
			DIE();		//(x)はガードが無い,(y)はガードが無い⇒バグ。ガードが無い遷移が重複して登録される事は無いはず。登録処理での検出処理にバグが有る可能性有り。
		}
	}
}
static ST_Trans** getSortedTrans(ght_hash_table_t* p_ht) {
	ght_iterator_t it;
	const void* key;
	int keySize, i = 0, n = ght_size(p_ht);
	ST_Trans** pp_trans = calloc(n + 1/*NULL*/, sizeof(ST_Trans*));
	ST_Trans* trans;
	for(trans = ght_first_keysize(p_ht, &it, &key, &keySize);
	    trans;
	    trans = ght_next_keysize( p_ht, &it, &key, &keySize)) {
		pp_trans[i++] = trans;
	}
	qsort(pp_trans, n, sizeof(ST_Trans*), Trans_compar);
	return pp_trans;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void output(char* sRegOut, char* sTblOut) {
	int iMap, iTrans, iState, iFunc, iValNm;
	//標準出力
	{
		FILE* fp = stdout;
		//状態マップ
		fprintf(fp, "//状態マップ\n");
		for(iMap = 0; iMap < nMap; iMap++) {
			fprintf(fp, "#define FsmMap_%s %d\n", TBL_Map[iMap], iMap + 1);		//状態マップ番号は1ベース
		}
		fprintf(fp, "#define FsmMap_Max %d\n", nMap);
		//状態
		fprintf(fp, "//状態\n");
		for(iState = 0; iState < nState; iState++) {
			fprintf(fp, "#define FsmState_%s %d\n", TBL_State[iState], iState + 1);	//状態番号は1ベース
		}
		fprintf(fp, "#define FsmState_Max %d\n", nState);
		//遷移
		fprintf(fp, "//遷移\n");
		for(iTrans = 0; iTrans < nTrans; iTrans++) {
			fprintf(fp, "#define FsmTrans_%s %d\n", TBL_Trans[iTrans], iTrans + 1);	//遷移番号は1ベース		default遷移(=0)の定義は有無にかかわらず出力しない。アプリケーションが明示的にdefault遷移を指定する必要は無いはずである。
		}
		fprintf(fp, "#define FsmTrans_Max %d\n", nTrans);
		//C言語ソースファイル名が指定されていたら…	C言語ソースファイル名が指定されていなければ、以下の'標準出力,又は,C言語ソースファイル'も標準出力に出力するので、ここでグローバル関数宣言を出力する事は不要です。
		if(sTblOut) {
			int mode = 0;
			//グローバル関数宣言
			for(iFunc = 0; iFunc < nFunc; iFunc++) {
				ST_Func* func = TBL_Func[iFunc];
				if(!func->code) {
					if(mode == 0) { mode = 1; fprintf(fp, "struct _ST_FsmObj;\n"); fprintf(fp, "//グローバル関数宣言\n"); }
					switch(func->op) {
					default:DIE();
					case 1/*Enter/Leave*/:
						fprintf(fp, "extern void FsmFunc_%s(struct _ST_FsmObj* pFsmObj);\n", func->name);
						break;
					case 2/*Guard*/:
						fprintf(fp, "extern int FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam);\n", func->name);
						break;
					case 3/*Act*/:
						fprintf(fp, "extern void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam);\n", func->name);
						break;
					}
				}
			}
		}
	}
	//標準出力,又は,C言語ソースファイル
	{
		FILE* fp = stdout;
		int mode = 0;
		//C言語ソースファイル名が指定されていたら…
		if(sTblOut) {
			//C言語ソースファイルを作成する。
			fp = fopen(sTblOut, "w");
			if(!fp) { die("%s が作成できません。\n", sTblOut); }
		}
		//%{～%}で指定されたソース
		if(g_source) { fprintf(fp, "%s\n", g_source); }
		//グローバル関数宣言,及び,ローカル関数定義
		for(iFunc = 0; iFunc < nFunc; iFunc++) {
			ST_Func* func = TBL_Func[iFunc];
			if(mode == 0) { fprintf(fp, "struct _ST_FsmObj;\n"); }
			if(!func->code) {
				if(mode != 1) { mode = 1; fprintf(fp, "//グローバル関数宣言\n"); }
				switch(func->op) {
				default:DIE();
				case 1/*Enter/Leave*/:
					fprintf(fp, "extern void FsmFunc_%s(struct _ST_FsmObj* pFsmObj);\n", func->name);
					break;
				case 2/*Guard*/:
					fprintf(fp, "extern int FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam);\n", func->name);
					break;
				case 3/*Act*/:
					fprintf(fp, "extern void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam);\n", func->name);
					break;
				}
			} else {
				if(mode != 2) { mode = 2; fprintf(fp, "//ローカル関数定義\n"); }
				switch(func->op) {
				default:DIE();
				case 1/*Enter/Leave*/:
					fprintf(fp, "static void FsmFunc_%s(struct _ST_FsmObj* pFsmObj) { %s; }\n", func->name, func->code);
					break;
				case 2/*Guard*/:
					fprintf(fp, "static int FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam) { return %s; }\n", func->name, func->code);
					break;
				case 3/*Act*/:
					fprintf(fp, "static void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam) { %s; }\n", func->name, func->code);
					break;
				}
			}
		}
		//関数テーブル
		fprintf(fp, "//関数テーブル\n");
		fprintf(fp, "const void* const TBL_FsmFunc[]={\n");
		fprintf(fp, "(void*)%d,\n", nFunc + 1);						//先頭要素に関数配列の要素数を格納する
		for(iFunc = 0; iFunc < nFunc; iFunc++) {
			fprintf(fp, "FsmFunc_%s,//%d\n", TBL_Func[iFunc]->name, iFunc + 1);	//関数番号は1ベース
		}
		fprintf(fp, "};\n");
		//C言語ソースファイル名が指定されていたら…
		if(sTblOut) {
			//C言語ソースファイルを閉じる。
			fclose(fp);
		}
	}
	//標準出力,又は,レジストリスクリプトファイル
	{
		ST_Map		**pp_map,	*map;
		ST_TransSet	**pp_transSet,	*transSet;
		ST_Trans	**pp_trans,	*trans;
		ST_State	**pp_state,	*state;
		FILE* fp = stdout;
		//レジストリスクリプトファイル名が指定されていたら…
		if(sRegOut) {
			//レジストリスクリプトファイルを作成する。
			fp = fopen(sRegOut, "w");
			if(!fp) { die("%s が作成できません。\n", sRegOut); }
		}
		fprintf(fp, "FsmMap" "\n");
		//～～状態マップ～～
		pp_map = getSortedMap(ht_map);					//状態マップを、状態マップ名の昇順にソートする。						状態マップ名の昇順にソートする事は読み易さのためだけであり必須ではない。
		while((map = *pp_map++)) {					//NULLポインタで終端されている。
			ST_MapBody* mapBody = map->mapBody;
			iMap = (int)ght_get(ht_mapNo,
				strlen(map->name) + 1/*nul*/,
				       map->name);
			if(!iMap) { DIE(); }	//ここで停止したらバグ
			fprintf(fp, "\t%d\t\t\t\t\t\t\t#//%s\n", iMap, map->name);
			if(mapBody->enter) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapBody->enter->name) + 1/*nul*/,
					       mapBody->enter->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t0\t=\t%d\t\t\t\t#//\tenter\t{%s}\n", iFunc, GetFuncDesc(mapBody->enter));
			}
			if(mapBody->leave) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapBody->leave->name) + 1/*nul*/,
					       mapBody->leave->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t1\t=\t%d\t\t\t\t#//\tleave\t{%s}\n", iFunc, GetFuncDesc(mapBody->leave));
			}
			//～～状態マップの遷移セット～～
			pp_transSet = getSortedTransSet(mapBody->ht_transSet);		//遷移セットを、遷移名の昇順にソートする。						遷移名の昇順にソートする事は読み易さのためだけであり必須ではない。
			while((transSet = *pp_transSet++)) {				//NULLポインタで終端されている。
				iTrans = (int)ght_get(ht_transNo,
					strlen(transSet->name) + 1/*nul*/,
					       transSet->name);
				if((!iTrans &&  strcmp(transSet->name, "default")) ||
				   ( iTrans && !strcmp(transSet->name, "default"))) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t%d\t\t\t\t\t\t#//\t%s\n", iTrans, transSet->name);
				//～～状態マップの遷移セットの遷移～～
				iValNm = 0;
				pp_trans = getSortedTrans(transSet->ht_trans);		//遷移を、ガード名の昇順,ただし,ガードが無い遷移(有れば)を末尾にソートする。		ガード名の昇順にソートする事は読み易さのためだけであり必須ではない。ガードが無い遷移(有れば)を末尾にソートする事は必須である。
				while((trans = *pp_trans++)) {				//NULLポインタで終端されている。
					if(trans->transBody->guard) {
						iFunc = (int)ght_get(ht_funcNo,
							strlen(trans->transBody->guard->name) + 1/*nul*/,
							       trans->transBody->guard->name);
						if(!iFunc) { DIE(); }	//ここで停止したらバグ
						fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t[%s]\n", iValNm, iFunc, GetFuncDesc(trans->transBody->guard));
					}
					iValNm++;
					if(trans->transBody->act) {
						iFunc = (int)ght_get(ht_funcNo,
							strlen(trans->transBody->act->name) + 1/*nul*/,
							       trans->transBody->act->name);
						if(!iFunc) { DIE(); }	//ここで停止したらバグ
						fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t{%s}\n", iValNm, iFunc, GetFuncDesc(trans->transBody->act));
					}
					iValNm++;
					if(trans->transBody->nextState) {
						switch(trans->transBody->nextState->op) {
						default:DIE();
						case 1/*set*/:
							iState = (int)ght_get(ht_stateNo,
								strlen(trans->transBody->nextState->state) + 1/*nul*/,
								       trans->transBody->nextState->state);
							if(!iState) { DIE(); }	//ここで停止したらバグ
							fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\n", iValNm, iState, trans->transBody->nextState->state);
							break;
						case 2/*push*/:
							iState = (int)ght_get(ht_stateNo,
								strlen(trans->transBody->nextState->state) + 1/*nul*/,
								       trans->transBody->nextState->state);
							if(!iState) { DIE(); }	//ここで停止したらバグ
							fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\tpush(%s)\n", iValNm, (iState|0x8000), trans->transBody->nextState->state);	//遷移先状態番号が(0x8000～0xFFFE)ならば、下位15ビットを状態番号と見なす、プッシュ遷移である。
							break;
						case 3/*pop*/:
							fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\tpop\n", iValNm, 0xFFFF);
							break;
						}
					}
					iValNm++;
					if(*pp_trans) { fprintf(fp, "\n"); }	//読み易さのために一行空ける。
				}
				fprintf(fp, "\t\t" "end" "\n");
			}
			//～～状態マップの状態～～
			pp_state = getSortedState(mapBody->ht_state);				//状態を、状態名の昇順にソートする。							状態名の昇順にソートする事は読み易さのためだけであり必須ではない。
			while((state = *pp_state++)) {						//NULLポインタで終端されている。
				ST_StateBody* stateBody = state->stateBody;
				iState = (int)ght_get(ht_stateNo,
					strlen(state->name) + 1/*nul*/,
					       state->name);
				if(!iState) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t%d\t\t\t\t\t\t#//\t%s\n", (iState|0x8000), state->name);	//状態のキーの名前は最上位ビットをセットする。状態マップのキーの直下に状態と遷移のキーが並存するので、状態と遷移のキーの名前が重複する事を避けるためです。
				if(stateBody->enter) {
					iFunc = (int)ght_get(ht_funcNo,
						strlen(stateBody->enter->name) + 1/*nul*/,
						       stateBody->enter->name);
					if(!iFunc) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t0\t=\t%d\t\t\t#//\t\tenter\t{%s}\n", iFunc, GetFuncDesc(stateBody->enter));
				}
				if(stateBody->leave) {
					iFunc = (int)ght_get(ht_funcNo,
						strlen(stateBody->leave->name) + 1/*nul*/,
						       stateBody->leave->name);
					if(!iFunc) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t1\t=\t%d\t\t\t#//\t\tleave\t{%s}\n", iFunc, GetFuncDesc(stateBody->leave));
				}
				//～～状態マップの状態の遷移セット～～
				pp_transSet = getSortedTransSet(stateBody->ht_transSet);	//遷移セットを、遷移名の昇順にソートする。						遷移名の昇順にソートする事は読み易さのためだけであり必須ではない。
				while((transSet = *pp_transSet++)) {				//NULLポインタで終端されている。
					iTrans = (int)ght_get(ht_transNo,
						strlen(transSet->name) + 1/*nul*/,
						       transSet->name);
					if((!iTrans &&  strcmp(transSet->name, "default")) ||
					   ( iTrans && !strcmp(transSet->name, "default"))) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t%d\t\t\t\t\t#//\t\t%s\n", iTrans, transSet->name);
					//～～状態マップの状態の遷移セットの遷移～～
					iValNm = 0;
					pp_trans = getSortedTrans(transSet->ht_trans);		//遷移を、ガード名の昇順,ただし,ガードが無い遷移(有れば)を末尾にソートする。		ガード名の昇順にソートする事は読み易さのためだけであり必須ではない。ガードが無い遷移(有れば)を末尾にソートする事は必須である。
					while((trans = *pp_trans++)) {				//NULLポインタで終端されている。
						if(trans->transBody->guard) {
							iFunc = (int)ght_get(ht_funcNo,
								strlen(trans->transBody->guard->name) + 1/*nul*/,
								       trans->transBody->guard->name);
							if(!iFunc) { DIE(); }	//ここで停止したらバグ
							fprintf(fp, "\t\t\t\t%d\t=\t%d\t\t#//\t\t\t[%s]\n", iValNm, iFunc, GetFuncDesc(trans->transBody->guard));
						}
						iValNm++;
						if(trans->transBody->act) {
							iFunc = (int)ght_get(ht_funcNo,
								strlen(trans->transBody->act->name) + 1/*nul*/,
								       trans->transBody->act->name);
							if(!iFunc) { DIE(); }	//ここで停止したらバグ
							fprintf(fp, "\t\t\t\t%d\t=\t%d\t\t#//\t\t\t{%s}\n", iValNm, iFunc, GetFuncDesc(trans->transBody->act));
						}
						iValNm++;
						if(trans->transBody->nextState) {
							switch(trans->transBody->nextState->op) {
							default:DIE();
							case 1/*set*/:
								iState = (int)ght_get(ht_stateNo,
									strlen(trans->transBody->nextState->state) + 1/*nul*/,
									       trans->transBody->nextState->state);
								if(!iState) { DIE(); }	//ここで停止したらバグ
								fprintf(fp, "\t\t\t\t%d\t=\t%d\t\t#//\t\t\t%s\n", iValNm, iState, trans->transBody->nextState->state);
								break;
							case 2/*push*/:
								iState = (int)ght_get(ht_stateNo,
									strlen(trans->transBody->nextState->state) + 1/*nul*/,
									       trans->transBody->nextState->state);
								if(!iState) { DIE(); }	//ここで停止したらバグ
								fprintf(fp, "\t\t\t\t%d\t=\t%d\t\t#//\t\t\tpush(%s)\n", iValNm, (iState|0x8000), trans->transBody->nextState->state);	//遷移先状態番号が(0x8000～0xFFFE)ならば、下位15ビットを状態番号と見なす、プッシュ遷移である。
								break;
							case 3/*pop*/:
								fprintf(fp, "\t\t\t\t%d\t=\t%d\t\t#//\t\t\tpop\n", iValNm, 0xFFFF);
								break;
							}
						}
						iValNm++;
						if(*pp_trans) { fprintf(fp, "\n"); }	//読み易さのために一行空ける。
					}
					fprintf(fp, "\t\t\t" "end" "\n");
				}
				fprintf(fp, "\t\t" "end" "\n");
			}
			fprintf(fp, "\t" "end" "\n");
		}
		fprintf(fp, "end" "\n");
		//レジストリスクリプトファイル名が指定されていたら…
		if(sRegOut) {
			//レジストリスクリプトファイルを閉じる。
			fclose(fp);
		}
	}
}
