/*****************************************************************************
 * 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 const char* g_property;		//状態マップの外で@{～@}で指定された、プロパティテーブルスクリプトを連結する。
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*,int)),2=(int(*)(ST_FsmObj*,int,void*)),3=(void(*)(ST_FsmObj*,int,void*))	1はEntry/Exit/Suspend/Resume関数,2はGuard関数,3はAct関数に相当する。
//ラベル名のソート方法
int (*fnLabCmp)(const void*, const void*) = strcmp;	//'n'オプションが指定されたらstrnatcmpに変更する。
//{{進捗表示用
static int opt_q;
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
typedef struct _ST_Location {
	const char*	path;					//push_location()が呼び出された直前のファイル名
	int		line;					//push_location()が呼び出された直前の行番号
} ST_Location;
static ST_Location TBL_Location[16/*調整可*/];			//ロケーションスタック
static nLocation;						//ロケーションスタックの格納数
static const char* filename = "-";				//現在のファイル名(popen時はコマンド名)。現在の行番号はyylinenoを参照して下さい。
static void init_location(const char* path) {
	nLocation = 0;						//ロケーションスタックを空にする。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。	yylex_init()でも初期化しているのでこの行は無くても構わないのだが、判り易さのためにここでも初期化する事にした。
}
void push_location(const char* path) {
	if(nLocation >= ARRAY_SIZE(TBL_Location)) { DIE(); }	//ロケーションスタックがフルでない事を確認する。
	TBL_Location[nLocation].path = filename;		//現在のファイル名をロケーションスタックに退避する。
	TBL_Location[nLocation].line = yylineno;		//現在の行番号をロケーションスタックに退避する。
	nLocation++;						//ロケーションスタックの格納数を増やす。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。
}
void pop_location() {
	if(!nLocation) { DIE(); }				//ロケーションスタックが空でない事を確認する。
	nLocation--;						//ロケーションスタックの格納数を減らす。
	filename = TBL_Location[nLocation].path;		//現在のファイル名をロケーションスタックから復元する。
	yylineno = TBL_Location[nLocation].line;		//現在の行番号をロケーションスタックから復元する。
}
//}}2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
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) { error(EXIT_FAILURE, errno, "%s が作成できません。", err_path); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		//エラーログファイルの出力ストリームは、バッファリング無しとする。
		// - die()で終了した時(abort()時)に、バッファ分がフラッシュされないようなので、こうしておく必要が有る。
		//   別の案として、この下の処理で毎回fflush()する方法も有るが、バッファリング無しとする方が簡単だろう。
		setbuf(fp_err, NULL);
	}
	//エラーメッセージ(,及び,エラーログ)を出力する。
//{{2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
//	             fprintf(stderr, "### error at line %d : ", yylineno);	//エラーメッセージ
//	if(fp_err) { fprintf(fp_err, "### error at line %d : ", yylineno); }	//エラーログ
//↓2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	             fprintf(stderr, "%s(%d) : ", filename, yylineno);		//エラーメッセージ
	if(fp_err) { fprintf(fp_err, "%s(%d) : ", filename, yylineno); }	//エラーログ
//}}2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	{
		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 int subr(int argc, char* argv[]);
static void pass2(int bOptimize);
static void output(char* sRegOut, char* sTblOut, char* sEnmOut, char* sPrpOut);
/*---------------------------------------------------------------------------*/
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 exitCode;
	SEH_init();
	exitCode = subr(argc, argv);
	return exitCode;
}
/*---------------------------------------------------------------------------*/
static int subr(int argc, char* argv[]) {
	int opt, bOptimize = 1;	//'-d'オプションが指定されなければ最適化を行う。
	char* sRegOut = NULL;	//'-r'オプションが指定されたらレジストリスクリプトファイル名を設定する。
	char* sTblOut = NULL;	//'-t'オプションが指定されたらC言語ソースファイル名を設定する。
	char* sEnmOut = NULL;	//'-u'オプションが指定されたら列挙定義スクリプトファイル名を設定する。
	char* sPrpOut = NULL;	//'-p'オプションが指定されたらプロパティテーブルスクリプトファイル名を設定する。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "dr:t:u:p:enqh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;		//最適化を行わない。
			break;
		case 'r':
			sRegOut = optarg;	//レジストリスクリプトファイル名を設定する。
			break;
		case 't':
			sTblOut = optarg;	//C言語ソースファイル名を設定する。
			break;
		case 'u':
			sEnmOut = optarg;	//列挙定義スクリプトファイル名を設定する。
			break;
		case 'p':
			sPrpOut = optarg;	//プロパティテーブルスクリプトファイル名を設定する。
			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 'n':
			fnLabCmp = strnatcmp;	//ラベル名のソートにstrnatcmp()を使用する。
			break;
		case 'q':
			opt_q = 1;
			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) {
		init_location("-");	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
		yylex_init(stdin);
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			init_location(argv[i]);	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
			yylex_init(fp);
			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			fclose(fp);
		}
	}
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass2 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//--- 出力処理 ---
	//出力処理を行う。
	output(sRegOut, sTblOut, sEnmOut, sPrpOut);
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dFsmMapC - 有限状態機械マップコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015-2017 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, "  -uファイル名 状態マップ番号,状態番号,遷移番号の列挙定義スクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、#define形式で標準出力へ出力します。\n");
	fprintf(stderr, "  -pファイル名 プロパティテーブルスクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "  -e           エラーログファイルを生成します。\n");
	fprintf(stderr, "               このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -n           ラベル名のソートに、strnatcmp()を使用します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、strcmp()を使用します。\n");
	fprintf(stderr, "  -q           進捗表示を抑制します。\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をコンパイルして、\n");
	fprintf(stderr, "  　グローバル関数宣言,及び,状態マップ番号,状態番号,遷移番号を、FsmMap.hへ出力します。\n");
	fprintf(stderr, "  　有限状態機械マップのレジストリスクリプトを、RegFsmMap.txtへ出力します。\n");
	fprintf(stderr, "  　関数テーブルのC言語ソースを、FsmMap.cへ出力します。\n");
	fprintf(stderr, "  ③dFsmMapC.exe -rRegFsmMap.txt -tFsmMap.c -uEnmFsmMap.txt FsmMap.txt > FsmMap.h\n");
	fprintf(stderr, "  　FsmMap.txtをコンパイルして、\n");
	fprintf(stderr, "  　グローバル関数宣言を、FsmMap.hへ出力します。\n");
	fprintf(stderr, "  　状態マップ番号,状態番号,遷移番号の列挙定義スクリプトを、EnmFsmMap.txtへ出力します。\n");
	fprintf(stderr, "  　有限状態機械マップのレジストリスクリプトを、RegFsmMap.txtへ出力します。\n");
	fprintf(stderr, "  　関数テーブルのC言語ソースを、FsmMap.cへ出力します。\n");
	fprintf(stderr, "  ④dFsmMapC.exe -rRegFsmMap.txt -tFsmMap.c -uEnmFsmMap.txt -pPrpFsmMap.txt FsmMap.txt > FsmMap.h\n");
	fprintf(stderr, "  　FsmMap.txtをコンパイルして、\n");
	fprintf(stderr, "  　グローバル関数宣言を、FsmMap.hへ出力します。\n");
	fprintf(stderr, "  　状態マップ番号,状態番号,遷移番号の列挙定義スクリプトを、EnmFsmMap.txtへ出力します。\n");
	fprintf(stderr, "  　有限状態機械マップのレジストリスクリプトを、RegFsmMap.txtへ出力します。\n");
	fprintf(stderr, "  　関数テーブルのC言語ソースを、FsmMap.cへ出力します。\n");
	fprintf(stderr, "  　プロパティテーブルスクリプトを、PrpFsmMap.txtへ出力します。\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);
	if(!str_has_suffix(g_source, "\n")) { g_source = strconcat(g_source, "\n", NULL); }				//末尾に確実に改行を補う。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void add_property(char* property) {
	//状態マップの外で@{～@}で指定された、プロパティテーブルスクリプトを追加する。
	g_property = strconcat((g_property ? g_property : ""), property, NULL);
	if(!str_has_suffix(g_property, "\n")) { g_property = strconcat(g_property, "\n", NULL); }			//末尾に確実に改行を補う。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void add_map(ST_Map* map) {
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", map->mapHdr->name);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//--- 状態マップ登録処理 ---
	if(ght_insert(ht_map,
		       map,
		strlen(map->mapHdr->name) + 1/*nul*/,
		       map->mapHdr->name)) {
		yyerror("状態マップの名前が重複しています。(%s)", map->mapHdr->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;
//{{2017/01/06追加:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
		//状態マップの初期化Act関数
		if(map->mapHdr->initAct) {
			if(!*map->mapHdr->initAct->name) { map->mapHdr->initAct->name = strjoin("_", map->mapHdr->name, "init", "act", NULL); }
			func = ght_get(ht_func,
				strlen(map->mapHdr->initAct->name) + 1/*nul*/,
				       map->mapHdr->initAct->name);
			if(func && (func->op != map->mapHdr->initAct->op)) {
				yyerror("Act関数の種類が相違しています。(%s)", GetFuncDesc(map->mapHdr->initAct));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       map->mapHdr->initAct,
				strlen(map->mapHdr->initAct->name) + 1/*nul*/,
				       map->mapHdr->initAct->name);	//既に登録されている場合はダミー処理になる。
		}
//}}2017/01/06追加:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
		//状態マップのEntry関数
		if(mapBody->entry) {
			if(!*mapBody->entry->name) { mapBody->entry->name = strjoin("_", map->mapHdr->name, "entry", NULL); }
			func = ght_get(ht_func,
				strlen(mapBody->entry->name) + 1/*nul*/,
				       mapBody->entry->name);
			if(func && (func->op != mapBody->entry->op)) {
				yyerror("Entry関数の種類が相違しています。(%s)", GetFuncDesc(mapBody->entry));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       mapBody->entry,
				strlen(mapBody->entry->name) + 1/*nul*/,
				       mapBody->entry->name);	//既に登録されている場合はダミー処理になる。
		}
		//状態マップのExit関数
		if(mapBody->exit) {
			if(!*mapBody->exit->name) { mapBody->exit->name = strjoin("_", map->mapHdr->name, "exit", NULL); }
			func = ght_get(ht_func,
				strlen(mapBody->exit->name) + 1/*nul*/,
				       mapBody->exit->name);
			if(func && (func->op != mapBody->exit->op)) {
				yyerror("Exit関数の種類が相違しています。(%s)", GetFuncDesc(mapBody->exit));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       mapBody->exit,
				strlen(mapBody->exit->name) + 1/*nul*/,
				       mapBody->exit->name);	//既に登録されている場合はダミー処理になる。
		}
		//状態マップのSuspend関数
		if(mapBody->suspend) {
			if(!*mapBody->suspend->name) { mapBody->suspend->name = strjoin("_", map->mapHdr->name, "suspend", NULL); }
			func = ght_get(ht_func,
				strlen(mapBody->suspend->name) + 1/*nul*/,
				       mapBody->suspend->name);
			if(func && (func->op != mapBody->suspend->op)) {
				yyerror("Suspend関数の種類が相違しています。(%s)", GetFuncDesc(mapBody->suspend));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       mapBody->suspend,
				strlen(mapBody->suspend->name) + 1/*nul*/,
				       mapBody->suspend->name);	//既に登録されている場合はダミー処理になる。
		}
		//状態マップのResume関数
		if(mapBody->resume) {
			if(!*mapBody->resume->name) { mapBody->resume->name = strjoin("_", map->mapHdr->name, "resume", NULL); }
			func = ght_get(ht_func,
				strlen(mapBody->resume->name) + 1/*nul*/,
				       mapBody->resume->name);
			if(func && (func->op != mapBody->resume->op)) {
				yyerror("Resume関数の種類が相違しています。(%s)", GetFuncDesc(mapBody->resume));
				yynerrs++;		//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する。
			}
			ght_insert(ht_func,
				       mapBody->resume,
				strlen(mapBody->resume->name) + 1/*nul*/,
				       mapBody->resume->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->mapHdr->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->mapHdr->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;
			//状態マップの状態のEntry関数
			if(stateBody->entry) {
				if(!*stateBody->entry->name) { stateBody->entry->name = strjoin("_", map->mapHdr->name, state->name, "entry", NULL); }
				func = ght_get(ht_func,
					strlen(stateBody->entry->name) + 1/*nul*/,
					       stateBody->entry->name);
				if(func && (func->op != stateBody->entry->op)) {
					yyerror("関数の種類が相違しています。(%s)", GetFuncDesc(stateBody->entry));
					yynerrs++;		//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する。
				}
				ght_insert(ht_func,
					       stateBody->entry,
					strlen(stateBody->entry->name) + 1/*nul*/,
					       stateBody->entry->name);		//既に登録されている場合はダミー処理になる。
			}
			//状態マップの状態のExit関数
			if(stateBody->exit) {
				if(!*stateBody->exit->name) { stateBody->exit->name = strjoin("_", map->mapHdr->name, state->name, "exit", NULL); }
				func = ght_get(ht_func,
					strlen(stateBody->exit->name) + 1/*nul*/,
					       stateBody->exit->name);
				if(func && (func->op != stateBody->exit->op)) {
					yyerror("関数の種類が相違しています。(%s)", GetFuncDesc(stateBody->exit));
					yynerrs++;		//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する。
				}
				ght_insert(ht_func,
					       stateBody->exit,
					strlen(stateBody->exit->name) + 1/*nul*/,
					       stateBody->exit->name);		//既に登録されている場合はダミー処理になる。
			}
			//状態マップの状態のSuspend関数
			if(stateBody->suspend) {
				if(!*stateBody->suspend->name) { stateBody->suspend->name = strjoin("_", map->mapHdr->name, state->name, "suspend", NULL); }
				func = ght_get(ht_func,
					strlen(stateBody->suspend->name) + 1/*nul*/,
					       stateBody->suspend->name);
				if(func && (func->op != stateBody->suspend->op)) {
					yyerror("関数の種類が相違しています。(%s)", GetFuncDesc(stateBody->suspend));
					yynerrs++;		//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する。
				}
				ght_insert(ht_func,
					       stateBody->suspend,
					strlen(stateBody->suspend->name) + 1/*nul*/,
					       stateBody->suspend->name);	//既に登録されている場合はダミー処理になる。
			}
			//状態マップの状態のResume関数
			if(stateBody->resume) {
				if(!*stateBody->resume->name) { stateBody->resume->name = strjoin("_", map->mapHdr->name, state->name, "resume", NULL); }
				func = ght_get(ht_func,
					strlen(stateBody->resume->name) + 1/*nul*/,
					       stateBody->resume->name);
				if(func && (func->op != stateBody->resume->op)) {
					yyerror("関数の種類が相違しています。(%s)", GetFuncDesc(stateBody->resume));
					yynerrs++;		//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する。
				}
				ght_insert(ht_func,
					       stateBody->resume,
					strlen(stateBody->resume->name) + 1/*nul*/,
					       stateBody->resume->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->mapHdr->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->mapHdr->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(ST_MapHdr* mapHdr, ST_MapBody* mapBody) {
	ST_Map* map = calloc(1, sizeof(ST_Map));
	map->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	map->mapHdr		= mapHdr;
	map->mapBody		= mapBody;
	//状態マップの構造が正しい事を確認する。
	{
		if(!ght_size(mapBody->ht_state)) {
			yyerror("状態が無いマップは作成できません。(%s)", mapHdr->name);
			yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
		}
		if(!ght_get(mapBody->ht_state,
			strlen(mapHdr->initState) + 1/*nul*/,
			       mapHdr->initState)) {
			yyerror("初期状態が存在しません。(%s)", mapHdr->initState);
			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;
		char* 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)) {
				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;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/01/06変更:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
//ST_MapHdr* MapHdr_new(char* name, char* initState) {
//	ST_MapHdr* mapHdr = calloc(1, sizeof(ST_MapHdr));
//	mapHdr->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
//	mapHdr->name		= name;
//	mapHdr->initState	= initState;
//	return mapHdr;
//}
//↓2017/01/06変更:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
ST_MapHdr* MapHdr_new(char* name, ST_Func* initAct, char* initState) {
	ST_MapHdr* mapHdr = calloc(1, sizeof(ST_MapHdr));
	mapHdr->line		= yylineno;			//行番号。エラーメッセージのためだけに使用する。
	mapHdr->name		= name;
	mapHdr->initAct		= initAct;
	mapHdr->initState	= initState;
	return mapHdr;
}
//}}2017/01/06変更:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
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_addEntry(ST_MapBody* mapBody, ST_Func* entry) {
	if(mapBody->entry) {
		yyerror("Entry関数が重複しています。(%s)", GetFuncDesc(entry));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	mapBody->entry		= entry;
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addExit(ST_MapBody* mapBody, ST_Func* exit) {
	if(mapBody->exit) {
		yyerror("Exit関数が重複しています。(%s)", GetFuncDesc(exit));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	mapBody->exit		= exit;
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addSuspend(ST_MapBody* mapBody, ST_Func* suspend) {
	if(mapBody->suspend) {
		yyerror("Suspend関数が重複しています。(%s)", GetFuncDesc(suspend));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	mapBody->suspend	= suspend;
	return mapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_MapBody* MapBody_addResume(ST_MapBody* mapBody, ST_Func* resume) {
	if(mapBody->resume) {
		yyerror("Resume関数が重複しています。(%s)", GetFuncDesc(resume));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
	}
	mapBody->resume		= resume;
	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_MapBody* MapBody_addProperty(ST_MapBody* mapBody, char* property) {
	//状態マップの中で@{～@}で指定された、プロパティテーブルスクリプトを追加する。
	mapBody->property = strconcat((mapBody->property ? mapBody->property : ""), property, NULL);
	if(!str_has_suffix(mapBody->property, "\n")) { mapBody->property = strconcat(mapBody->property, "\n", NULL); }	//末尾に確実に改行を補う。
	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->entry &&
	   !state->stateBody->exit &&
	   !state->stateBody->suspend &&
	   !state->stateBody->resume &&
	   !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_addEntry(ST_StateBody* stateBody, ST_Func* entry) {
	if(stateBody->entry) {
		yyerror("Entry関数が重複しています。(%s)", GetFuncDesc(entry));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	stateBody->entry	= entry;
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_addExit(ST_StateBody* stateBody, ST_Func* exit) {
	if(stateBody->exit) {
		yyerror("Exit関数が重複しています。(%s)", GetFuncDesc(exit));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	stateBody->exit		= exit;
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_addSuspend(ST_StateBody* stateBody, ST_Func* suspend) {
	if(stateBody->suspend) {
		yyerror("Suspend関数が重複しています。(%s)", GetFuncDesc(suspend));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	stateBody->suspend	= suspend;
	return stateBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StateBody* StateBody_addResume(ST_StateBody* stateBody, ST_Func* resume) {
	if(stateBody->resume) {
		yyerror("Resume関数が重複しています。(%s)", GetFuncDesc(resume));
		yynerrs++;		//呼び出し元(main)に知らせるために
		return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
	}
	stateBody->resume	= resume;
	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を行う。
	}
//{{2017/01/15追加:自己遷移を抑止するGuard関数の省略記法を追加しました。
	//Guard関数が有れば…
	if(transBody->guard) {
		//自己遷移を抑止するGuard関数の省略記法ならば…
		if(!strcmp(transBody->guard->name, "no-self-transition")) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			//自己遷移を抑止するGuard関数の省略記法を指定した場合は、遷移先状態としてset遷移,又は,push遷移が必須です。
			//遷移先状態が無かったり、遷移先状態がpop遷移である遷移には、自己遷移を抑止するGuard関数の省略記法を指定する事は出来ません。
			//遷移先状態が無かったり、遷移先状態がpop遷移である遷移では、自己遷移かどうかを判定するコードが生成出来ないからです。
			//<例>
			//│Trans1 [no-self-transition] {} State2	#//ok
			//│Trans1 [no-self-transition] {} push(State2)	#//ok
			//│Trans1 [no-self-transition] {} 		#//NG
			//│Trans1 [no-self-transition] {} pop		#//NG
			if(!transBody->nextState || !transBody->nextState->state) {
				yyerror("遷移先状態が無かったり、遷移先状態がpop遷移である遷移には、自己遷移を抑止するGuard関数の省略記法を指定する事は出来ません。(%s)", trans->name);
				yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
				return NULL;		//y.tab.yのtransの処理でYYERRORを行う。
			}
			//自己遷移を抑止するGuard関数の省略記法をコードに展開して、Guard関数の関数オブジェクトを作り直して置き換える。
			//元の関数オブジェクトはもう不要になり、ポインタ参照されなくなるので、いずれガーベージコレクタに回収されます。
			transBody->guard = Func_new(transBody->guard->op, NULL, strdup_printf("FsmObj_GetState(pFsmObj) != FsmState_%s", transBody->nextState->state));
		}
	}
//}}2017/01/15追加:自己遷移を抑止するGuard関数の省略記法を追加しました。
	//--- 遷移名登録処理 ---
	{
		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*,int)),2=(int(*)(ST_FsmObj*,int,void*)),3=(void(*)(ST_FsmObj*,int,void*))	1はEntry/Exit/Suspend/Resume関数,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 (*fnLabCmp)(*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;
    //{{コード指定に対して関数の名前を自動生成した関数を後ろにするための判断。output()においてグローバル関数宣言とローカル関数定義をグループ分けするためにこのソートが最優先である。
	if( x->code && !y->code) { return  1; }
	if(!x->code &&  y->code) { return -1; }
    //}}コード指定に対して関数の名前を自動生成した関数を後ろにするための判断。output()においてグローバル関数宣言とローカル関数定義をグループ分けするためにこのソートが最優先である。
    //{{2016/01/16削除:Func_compar()において関数の種類によるソートを削除しました。
    //	//関数の種類によるソート。
    //	if(x->op < y->op) { return -1; }
    //	if(x->op > y->op) { return  1; }
    //}}2016/01/16削除:Func_compar()において関数の種類によるソートを削除しました。
	//関数の名前によるソート。
	return (*fnLabCmp)(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)固定	//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
				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 Map_compar(const void* _x, const void* _y) {
	const char* x = (*(ST_Map**)_x)->mapHdr->name;
	const char* y = (*(ST_Map**)_y)->mapHdr->name;
	return (*fnLabCmp)(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 (*fnLabCmp)(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 (*fnLabCmp)(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 (*fnLabCmp)(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, char* sEnmOut, char* sPrpOut) {
	int iMap, iTrans, iState, iFunc, iValNm;
	//標準出力,又は,列挙定義スクリプト
	{
		FILE* fp = stdout;
		//列挙定義スクリプトファイル名が指定されていたら…
		if(sEnmOut) {
			//列挙定義スクリプトファイルを作成する。
			fp = fopen(sEnmOut, "w");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が作成できません。", sEnmOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			//状態マップ
			if(nMap) {
				fprintf(fp, "#//状態マップ\n");
				fprintf(fp, "FsmMap\n");
				for(iMap = 0; iMap < nMap; iMap++) {
					fprintf(fp, "\t%s", TBL_Map[iMap]);
					if(!iMap) { fprintf(fp, "\t1"); }		//状態マップ番号は1ベース
					fputc('\n', fp);
				}
				fprintf(fp, "end\n");
			}
			//状態
			if(nState) {
				fprintf(fp, "#//状態\n");
				fprintf(fp, "FsmState\n");
				for(iState = 0; iState < nState; iState++) {
					fprintf(fp, "\t%s", TBL_State[iState]);
					if(!iState) { fprintf(fp, "\t1"); }		//状態番号は1ベース
					fputc('\n', fp);
				}
				fprintf(fp, "end\n");
			}
			//遷移
			if(nTrans) {
				fprintf(fp, "#//遷移\n");
				fprintf(fp, "FsmTrans\n");
				for(iTrans = 0; iTrans < nTrans; iTrans++) {
					fprintf(fp, "\t%s", TBL_Trans[iTrans]);
					if(!iTrans) { fprintf(fp, "\t1"); }		//遷移番号は1ベース
					fputc('\n', fp);
				}
				fprintf(fp, "end\n");
			}
			//列挙定義スクリプトファイルを閉じる。
			fclose(fp);
		//列挙定義スクリプトファイル名が指定されていなければ…
		} else {
			//状態マップ
			if(nMap) {
				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);
			}
			//状態
			if(nState) {
				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);
			}
			//遷移
			if(nTrans) {
				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);
			}
		}
	}
	//標準出力
	{
		FILE* fp = stdout;
		//C言語ソースファイル名が指定されていたら…	C言語ソースファイル名が指定されていなければ、以下の'標準出力,又は,C言語ソースファイル'も標準出力に出力するので、ここでグローバル関数宣言を出力する事は不要です。
		if(sTblOut) {
			//グローバル関数が一つでも有れば、グローバル関数宣言ブロックを出力する。
			//グローバル関数が一つも無ければ、グローバル関数宣言ブロックを出力しない。
			// - グローバル関数が一つも無い時に、空の #ifdef  __cplusplus / extern "C" { / #endif//__cplusplus ～ #ifdef  __cplusplus / }//extern "C" / #endif//__cplusplus ブロックを出力しないようにするためです。
			//   グローバル関数が一つも無い時に、上記のブロックが生成させても動作上は問題無いのですが、出力コードの見易さのためです。
			for(iFunc = 0; iFunc < nFunc; iFunc++) {
				ST_Func* func = TBL_Func[iFunc];
				if(!func->code) { break; }	//グローバル関数が一つでも有れば、(iFunc<nFunc)で抜ける。
			}
			if(iFunc < nFunc) {	//グローバル関数が一つでも有れば…
				//グローバル関数宣言
				fprintf(fp, "struct _ST_FsmObj;\n");
				fprintf(fp, "//グローバル関数宣言\n");
				fprintf(fp, "#ifdef  __cplusplus\n");
				fprintf(fp, "extern \"C\" {\n");
				fprintf(fp, "#endif//__cplusplus\n");
				for(iFunc = 0; iFunc < nFunc; iFunc++) {
					ST_Func* func = TBL_Func[iFunc];
					if(!func->code) {
						switch(func->op) {
						default:DIE();
						case 1/*Entry/Exit/Suspend/Resume*/:
							fprintf(fp, "#define FSMFUNC_%s void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iAltState)\n", func->name, func->name);
							break;
						case 2/*Guard*/:
							fprintf(fp, "#define FSMFUNC_%s int FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam)\n", func->name, func->name);
							break;
						case 3/*Act*/:
							fprintf(fp, "#define FSMFUNC_%s void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam)\n", func->name, func->name);
							break;
						}
					}
				}
				fprintf(fp, "#define FSMFUNC_(name) FSMFUNC_##name\n");
				for(iFunc = 0; iFunc < nFunc; iFunc++) {
					ST_Func* func = TBL_Func[iFunc];
					if(!func->code) {
						switch(func->op) {
						default:DIE();
						case 1/*Entry/Exit/Suspend/Resume*/:
						case 2/*Guard*/:
						case 3/*Act*/:
							fprintf(fp, "FSMFUNC_(%s);\n", func->name);
							break;
						}
					}
				}
				fprintf(fp, "#ifdef  __cplusplus\n");
				fprintf(fp, "}//extern \"C\"\n");
				fprintf(fp, "#endif//__cplusplus\n");
			}
		}
	}
	//標準出力,又は,C言語ソースファイル
	{
		FILE* fp = stdout;
		int mode = 0;
		//C言語ソースファイル名が指定されていたら…
		if(sTblOut) {
			//C言語ソースファイルを作成する。
			fp = fopen(sTblOut, "w");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が作成できません。", sTblOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		}
		//%{～%}で指定されたソース
		if(g_source) { fprintf(fp, "%s", g_source); }		//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/*Entry/Exit/Suspend/Resume*/:
					fprintf(fp, "void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iAltState);\n", func->name);
					break;
				case 2/*Guard*/:
					fprintf(fp, "int FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iTrans, void* pParam);\n", func->name);
					break;
				case 3/*Act*/:
					fprintf(fp, "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/*Entry/Exit/Suspend/Resume*/:
					fprintf(fp, "static void FsmFunc_%s(struct _ST_FsmObj* pFsmObj, int iAltState) { %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) { error(EXIT_FAILURE, errno, "%s が作成できません。", sRegOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		}
		fprintf(fp, "FsmMap" "\n");
		//～～状態マップ～～
		pp_map = getSortedMap(ht_map);	//状態マップを、状態マップ名の昇順にソートする。	状態マップ名の昇順にソートする事は読み易さのためだけであり必須ではない。
		while((map = *pp_map++)) {	//NULLポインタで終端されている。
			ST_MapHdr*  mapHdr  = map->mapHdr;
			ST_MapBody* mapBody = map->mapBody;
			iMap = (int)ght_get(ht_mapNo,
				strlen(mapHdr->name) + 1/*nul*/,
				       mapHdr->name);
			if(!iMap) { DIE(); }	//ここで停止したらバグ
			fprintf(fp, "\t%d\t\t\t\t\t\t\t#//%s\n", iMap, mapHdr->name);
			iState = (int)ght_get(ht_stateNo,
				strlen(mapHdr->initState) + 1/*nul*/,
				       mapHdr->initState);
			if(!iState) { DIE(); }	//ここで停止したらバグ
//{{2017/01/06変更:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
//			fprintf(fp, "\t\t%d\t=\t%d\t\t\t\t#//\t%s\t%s\n", Fsm_State_init, iState, "init", mapHdr->initState);
//↓2017/01/06変更:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
			fprintf(fp, "\t\t%d\t\t\t\t\t\t#//\t%s\n", Fsm_Init, "init");
			if(mapHdr->initAct) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapHdr->initAct->name) + 1/*nul*/,
					       mapHdr->initAct->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\t{%s}\n", Fsm_InitAct, iFunc, "initAct", GetFuncDesc(mapHdr->initAct));
			}
			fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\t%s\n", Fsm_InitState, iState, "initState", mapHdr->initState);
			fprintf(fp, "\t\t" "end" "\n");
//}}2017/01/06変更:clipfsm.cにて状態マップに初期化Act関数を定義出来るようにした事に伴い、dFsmMapC.exeも変更後のレジストリ構造を出力するように変更しました。
			if(mapBody->entry) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapBody->entry->name) + 1/*nul*/,
					       mapBody->entry->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t%d\t=\t%d\t\t\t\t#//\t%s\t{%s}\n", Fsm_Entry, iFunc, "entry", GetFuncDesc(mapBody->entry));
			}
			if(mapBody->exit) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapBody->exit->name) + 1/*nul*/,
					       mapBody->exit->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t%d\t=\t%d\t\t\t\t#//\t%s\t{%s}\n", Fsm_Exit, iFunc, "exit", GetFuncDesc(mapBody->exit));
			}
			if(mapBody->suspend) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapBody->suspend->name) + 1/*nul*/,
					       mapBody->suspend->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t%d\t=\t%d\t\t\t\t#//\t%s\t{%s}\n", Fsm_Suspend, iFunc, "suspend", GetFuncDesc(mapBody->suspend));
			}
			if(mapBody->resume) {
				iFunc = (int)ght_get(ht_funcNo,
					strlen(mapBody->resume->name) + 1/*nul*/,
					       mapBody->resume->name);
				if(!iFunc) { DIE(); }	//ここで停止したらバグ
				fprintf(fp, "\t\t%d\t=\t%d\t\t\t\t#//\t%s\t{%s}\n", Fsm_Resume, iFunc, "resume", GetFuncDesc(mapBody->resume));
			}
			//～～状態マップの遷移セット～～
			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")) ||						//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
				   ( iTrans && !strcmp(transSet->name, "default"))) { DIE(); }	//ここで停止したらバグ		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
				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ポインタで終端されている。
					//Fsm_Guard	(3N+0)
					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++;
					//Fsm_Act	(3N+1)
					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++;
					//Fsm_State	(3N+2)
					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\t%s(%s)\n", iValNm, (iState|0x8000), "push", trans->transBody->nextState->state);	//遷移先状態番号が(0x8000～0xFFFE)ならば、下位15ビットを状態番号と見なす、プッシュ遷移である。
							break;
						case 3/*pop*/:
							fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\n", iValNm, 0xFFFF, "pop");
							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->entry) {
					iFunc = (int)ght_get(ht_funcNo,
						strlen(stateBody->entry->name) + 1/*nul*/,
						       stateBody->entry->name);
					if(!iFunc) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\t{%s}\n", Fsm_Entry, iFunc, "entry", GetFuncDesc(stateBody->entry));
				}
				if(stateBody->exit) {
					iFunc = (int)ght_get(ht_funcNo,
						strlen(stateBody->exit->name) + 1/*nul*/,
						       stateBody->exit->name);
					if(!iFunc) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\t{%s}\n", Fsm_Exit, iFunc, "exit", GetFuncDesc(stateBody->exit));
				}
				if(stateBody->suspend) {
					iFunc = (int)ght_get(ht_funcNo,
						strlen(stateBody->suspend->name) + 1/*nul*/,
						       stateBody->suspend->name);
					if(!iFunc) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\t{%s}\n", Fsm_Suspend, iFunc, "suspend", GetFuncDesc(stateBody->suspend));
				}
				if(stateBody->resume) {
					iFunc = (int)ght_get(ht_funcNo,
						strlen(stateBody->resume->name) + 1/*nul*/,
						       stateBody->resume->name);
					if(!iFunc) { DIE(); }	//ここで停止したらバグ
					fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\t{%s}\n", Fsm_Resume, iFunc, "resume", GetFuncDesc(stateBody->resume));
				}
				//～～状態マップの状態の遷移セット～～
				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")) ||						//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
					   ( iTrans && !strcmp(transSet->name, "default"))) { DIE(); }	//ここで停止したらバグ		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
					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ポインタで終端されている。
						//Fsm_Guard	(3N+0)
						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++;
						//Fsm_Act	(3N+1)
						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++;
						//Fsm_State	(3N+2)
						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\t%s(%s)\n", iValNm, (iState|0x8000), "push", 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\t%s\n", iValNm, 0xFFFF, "pop");
								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);
		}
	}
	//標準出力,又は,プロパティテーブルスクリプトファイル
	{
		ST_Map		**pp_map,	*map;
		FILE* fp = stdout;
		int mode = 0;
		//プロパティテーブルスクリプトファイル名が指定されていたら…
		if(sPrpOut) {
			//プロパティテーブルスクリプトファイルを作成する。
			fp = fopen(sPrpOut, "w");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が作成できません。", sPrpOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		}
		//状態マップの外で@{～@}で指定された、プロパティテーブルスクリプト
		if(g_property) { fprintf(fp, "%s", g_property); }	//g_propertyの末尾には確実に改行が補われているので、ここで末尾に改行を出力する必要は無い。
		//～～状態マップ～～
		pp_map = getSortedMap(ht_map);	//状態マップを、状態マップ名の昇順にソートする。	状態マップ名の昇順にソートする事は読み易さのためだけであり必須ではない。	'標準出力,又は,レジストリスクリプトファイル'のブロックでソート済みなのでここで再度ソートする必要は無いのだが、状態マップの配列を取得するためだけにgetSortedMap()を使っている。ここでは直接ht_mapをイテレートしても同じ結果になるのだが、処理を統一するためにこのようにした。
		while((map = *pp_map++)) {	//NULLポインタで終端されている。
			ST_MapHdr*  mapHdr  = map->mapHdr;
			ST_MapBody* mapBody = map->mapBody;
			if(mapBody->property) {
				fprintf(fp, "#//table\n");
				fprintf(fp, "%s\n", mapHdr->name);
				fprintf(fp, "%s", mapBody->property);	//mapBody->propertyの末尾には確実に改行が補われているので、ここで末尾に改行を出力する必要は無い。
				fprintf(fp, "end\n");
			}
		}
		//プロパティテーブルスクリプトファイル名が指定されていたら…
		if(sPrpOut) {
			//プロパティテーブルスクリプトファイルを閉じる。
			fclose(fp);
		}
	}
}
