/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
/*---------------------------------------------------------------------------*/
/* グローバル変数定義 */
int g_keepTransOrder;			//{{2018/04/18追加:「keep-trans-order」「sort-trans-order」を追加しました。}}
/*---------------------------------------------------------------------------*/
//{{2017/03/05追加:'-xc','-xcs'オプションを追加しました。
//言語選択
static char* opt_x = "c";		//"c"=C言語形式,"cs"=C#形式
//}}2017/03/05追加:'-xc','-xcs'オプションを追加しました。
/*---------------------------------------------------------------------------*/
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:enx:qh")) != -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;
//{{2017/03/05追加:'-xc','-xcs'オプションを追加しました。
		case 'x':
			opt_x = optarg;		//言語選択を設定する。
			if(strcmp(opt_x, "c") && strcmp(opt_x, "cs")) { usage(); }
			break;
//}}2017/03/05追加:'-xc','-xcs'オプションを追加しました。
		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);
//{{2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
//			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
//↓2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			{
				int save_yynerrs = yynerrs;	//yyparse()がyynerrsをクリアするので、その前に現在のyynerrsを保存しておく。
				if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
				yynerrs += save_yynerrs;	//今回のyyparse()が0から数えたであろうyynerrsに対して、先ほど保存しておいたyynerrsを足し込む。
			}
//}}2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			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-2018 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");
//{{2017/03/05追加:'-xc','-xcs'オプションを追加しました。
	fprintf(stderr, "  -xc          出力ソースをC言語形式とします。(既定値)\n");
	fprintf(stderr, "  -xcs         出力ソースをC#形式とします。\n");
//}}2017/03/05追加:'-xc','-xcs'オプションを追加しました。
	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); }			//末尾に確実に改行を補う。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2018/04/22追加:自動生成した関数名にサフィックスを含める方法を追加しました。
static void GenerateFuncName(ST_Func* pFunc, ...) {
	char* name = pFunc->name;
	//①指定された関数名が空文字ならば、関数名を自動的に生成する。
	//②指定された関数名が'_'で始まっていれば、関数名を自動的に生成し、指定された関数名をサフィックスとして追加する。
	//③指定された関数名が空文字でなく、'_'で始まってもいなければ、全体を明示的に指定された関数名なのでそのまま返す。
	if(!*name/*①*/ || (*name == '_')/*②*/) {
		va_list ap;
		va_start(ap, pFunc);
		pFunc->name = strjoinv("_", strv_new_ap(""/*ダミー*/, ap) + 1/*ダミー*/);	//strv_new_ap()は最初の要素のみapとは別の引数として受け取るため、ダミーを渡して生成した後に最初の要素を無視するようにした。strv_new_ap()の関数仕様が良くないと思うのだが仕方が無い。。。
		if(*name == '_')/*②*/ { pFunc->name = strconcat(pFunc->name, name, NULL); }
	} else {
		/** no job **/	/*③*/
	}
}
//}}2018/04/22追加:自動生成した関数名にサフィックスを含める方法を追加しました。
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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//			if(!*map->mapHdr->initAct->name) { map->mapHdr->initAct->name = strjoin("_", map->mapHdr->name, "init", "act", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			GenerateFuncName(map->mapHdr->initAct, map->mapHdr->name, "init", "act", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//			if(!*mapBody->entry->name) { mapBody->entry->name = strjoin("_", map->mapHdr->name, "entry", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			GenerateFuncName(mapBody->entry, map->mapHdr->name, "entry", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//			if(!*mapBody->exit->name) { mapBody->exit->name = strjoin("_", map->mapHdr->name, "exit", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			GenerateFuncName(mapBody->exit, map->mapHdr->name, "exit", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//			if(!*mapBody->suspend->name) { mapBody->suspend->name = strjoin("_", map->mapHdr->name, "suspend", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			GenerateFuncName(mapBody->suspend, map->mapHdr->name, "suspend", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//			if(!*mapBody->resume->name) { mapBody->resume->name = strjoin("_", map->mapHdr->name, "resume", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			GenerateFuncName(mapBody->resume, map->mapHdr->name, "resume", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
			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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//					if(!*trans->transBody->guard->name) { trans->transBody->guard->name = strjoin("_", map->mapHdr->name, trans->name, "guard", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
					GenerateFuncName(trans->transBody->guard, map->mapHdr->name, trans->name, "guard", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
					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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//					if(!*trans->transBody->act->name) { trans->transBody->act->name = strjoin("_", map->mapHdr->name, trans->name, "act", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
					GenerateFuncName(trans->transBody->act, map->mapHdr->name, trans->name, "act", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
					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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//				if(!*stateBody->entry->name) { stateBody->entry->name = strjoin("_", map->mapHdr->name, state->name, "entry", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				GenerateFuncName(stateBody->entry, map->mapHdr->name, state->name, "entry", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//				if(!*stateBody->exit->name) { stateBody->exit->name = strjoin("_", map->mapHdr->name, state->name, "exit", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				GenerateFuncName(stateBody->exit, map->mapHdr->name, state->name, "exit", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//				if(!*stateBody->suspend->name) { stateBody->suspend->name = strjoin("_", map->mapHdr->name, state->name, "suspend", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				GenerateFuncName(stateBody->suspend, map->mapHdr->name, state->name, "suspend", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//				if(!*stateBody->resume->name) { stateBody->resume->name = strjoin("_", map->mapHdr->name, state->name, "resume", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				GenerateFuncName(stateBody->resume, map->mapHdr->name, state->name, "resume", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
				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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//						if(!*trans->transBody->guard->name) { trans->transBody->guard->name = strjoin("_", map->mapHdr->name, state->name, trans->name, "guard", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
						GenerateFuncName(trans->transBody->guard, map->mapHdr->name, state->name, trans->name, "guard", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
						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) {
//{{2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
//						if(!*trans->transBody->act->name) { trans->transBody->act->name = strjoin("_", map->mapHdr->name, state->name, trans->name, "act", NULL); }
//↓2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
						GenerateFuncName(trans->transBody->act, map->mapHdr->name, state->name, trans->name, "act", NULL);
//}}2018/04/22変更:自動生成した関数名にサフィックスを含める方法を追加しました。
						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を行う。
						}
//{{2018/04/20追加:自己遷移を抑止する方法を変更しました。
						//自己遷移でなければ…
						if(strcmp(state->name, trans->transBody->nextState->state)) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
							//明らかに自己遷移でない遷移に対して「状態名!」が指定されていたら、セマンティクスエラーを生成してこのstateを受け入れない事にした。
							// - エラーとせずにそのままデータを生成しても問題は無く、動作上も問題はないのだが、厳密さのためにエラーと見なす事にした。
							//   うかつなコピーペースト等に起因するスクリプトバグかも知れず、それらのバグに出来るだけ早く気付けるようにするためです。
							if(trans->transBody->nextState->op == Fsm_TransType_setx) {
								yyerror("状態マップ%s内の状態%sの遷移%sにおいて、自己遷移でない遷移先状態%sに対して'!'が指定されています。", mapHdr->name, state->name, trans->name, trans->transBody->nextState->state);
								yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
								return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
							}
						//自己遷移ならば…
						} else {
							//明らかに自己遷移である遷移に対して「状態名!」が指定されていなければ、セマンティクスエラーを生成してこのstateを受け入れない事にした。
							// - エラーとせずにそのままデータを生成しても問題は無く、動作上も問題はないのだが、厳密さのためにエラーと見なす事にした。
							//   うかつなコピーペースト等に起因するスクリプトバグかも知れず、それらのバグに出来るだけ早く気付けるようにするためです。
							if(trans->transBody->nextState->op == Fsm_TransType_set) {
								yyerror("状態マップ%s内の状態%sの遷移%sにおいて、自己遷移である遷移先状態%sに対して'!'が指定されていません。", mapHdr->name, state->name, trans->name, trans->transBody->nextState->state);
								yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
								return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
							}
						}
//}}2018/04/20追加:自己遷移を抑止する方法を変更しました。
					}
				}
			}
		}
	}
	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;		//全てのセマンティクスエラーを報告するために継続する。
	}
//{{2018/04/18追加:「keep-trans-order」「sort-trans-order」を追加しました。
	if(g_keepTransOrder) {
		trans->order = ght_size(transSet->ht_trans);		//(g_keepTransOrder==1)ならば(order=1～)を設定する。((g_keepTransOrder==0)ならばこのブロックへ入って来ないので、(order=0)のままとなる。)
		if(trans->transBody->guard) {				//今回追加した遷移にガードが有り…
			if(ght_get(transSet->ht_trans, 0, NULL)) {	//既にガードが無い遷移が追加されていたら、エラーとする。
				yyerror("「keep-trans-order」を指定した場合、同じ遷移名で、ガードが無い遷移の後に、ガードが有る遷移を記述する事は出来ません。(%s[%s])", trans->name, GetFuncDesc(trans->transBody->guard));
				yynerrs++;		//呼び出し元(main)に知らせるために
				return mapBody;		//全てのセマンティクスエラーを報告するために継続する。
			}
		}
	}
//}}2018/04/18追加:「keep-trans-order」「sort-trans-order」を追加しました。
	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;
//{{2018/04/12削除:空の状態も作成出来るようにしました。
//	//状態の構造が正しい事を確認する。
//	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を行う。
//	}
//}}2018/04/12削除:空の状態も作成出来るようにしました。
	//--- 状態名登録処理 ---
	{
		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;	//全てのセマンティクスエラーを報告するために継続する。
	}
//{{2018/04/18追加:「keep-trans-order」「sort-trans-order」を追加しました。
	if(g_keepTransOrder) {
		trans->order = ght_size(transSet->ht_trans);		//(g_keepTransOrder==1)ならば(order=1～)を設定する。((g_keepTransOrder==0)ならばこのブロックへ入って来ないので、(order=0)のままとなる。)
		if(trans->transBody->guard) {				//今回追加した遷移にガードが有り…
			if(ght_get(transSet->ht_trans, 0, NULL)) {	//既にガードが無い遷移が追加されていたら、エラーとする。
				yyerror("「keep-trans-order」を指定した場合、同じ遷移名で、ガードが無い遷移の後に、ガードが有る遷移を記述する事は出来ません。(%s[%s])", trans->name, GetFuncDesc(trans->transBody->guard));
				yynerrs++;		//呼び出し元(main)に知らせるために
				return stateBody;	//全てのセマンティクスエラーを報告するために継続する。
			}
		}
	}
//}}2018/04/18追加:「keep-trans-order」「sort-trans-order」を追加しました。
	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を行う。
	}
//{{2018/04/20削除:自己遷移を抑止する方法を変更しました。
////{{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関数の関数オブジェクトを作り直して置き換える。
//			//元の関数オブジェクトはもう不要になり、ポインタ参照されなくなるので、いずれガーベージコレクタに回収されます。
////{{2017/03/05変更:'-xc','-xcs'オプションを追加しました。
////			transBody->guard = Func_new(transBody->guard->op, NULL, strdup_printf("FsmObj_GetState(pFsmObj) != FsmState_%s", transBody->nextState->state));
////↓2017/03/05変更:'-xc','-xcs'オプションを追加しました。
//			//C言語形式
//			if(!strcmp(opt_x, "c")) {
//				transBody->guard = Func_new(transBody->guard->op, NULL, strdup_printf("FsmObj_GetState(pFsmObj) != FsmState_%s", transBody->nextState->state));
//			//C#形式
//			} else if(!strcmp(opt_x, "cs")) {
//				transBody->guard = Func_new(transBody->guard->op, NULL, strdup_printf("org.piece_me.libclip.FsmObj_GetState(pFsmObj) != Const.FsmState_%s", transBody->nextState->state));
//			} else {
//				DIE();	//バグ
//			}
////}}2017/03/05変更:'-xc','-xcs'オプションを追加しました。
//		}
//	}
////}}2017/01/15追加:自己遷移を抑止するGuard関数の省略記法を追加しました。
//}}2018/04/20削除:自己遷移を抑止する方法を変更しました。
	//--- 遷移名登録処理 ---
	{
		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();
//{{2018/04/20変更:自己遷移を抑止する方法を変更しました。
//		case 1/*set*/:
//			yyerror("遷移先状態が重複しています。(%s)", nextState->state);
//			break;
//		case 2/*push*/:
//			yyerror("遷移先状態が重複しています。(push(%s))", nextState->state);
//			break;
//		case 3/*pop*/:
//			yyerror("遷移先状態が重複しています。(pop)");
//			break;
//↓2018/04/20変更:自己遷移を抑止する方法を変更しました。
		case Fsm_TransType_set:
			yyerror("遷移先状態が重複しています。(%s)", nextState->state);
			break;
		case Fsm_TransType_setx:
			yyerror("遷移先状態が重複しています。(%s!)", nextState->state);
			break;
		case Fsm_TransType_push:
			yyerror("遷移先状態が重複しています。(push(%s))", nextState->state);
			break;
		case Fsm_TransType_pop:
			yyerror("遷移先状態が重複しています。(pop)");
			break;
//}}2018/04/20変更:自己遷移を抑止する方法を変更しました。
		}
		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) {
//{{2018/04/18変更:「keep-trans-order」「sort-trans-order」を追加しました。
//	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)はガードが無い⇒バグ。ガードが無い遷移が重複して登録される事は無いはず。登録処理での検出処理にバグが有る可能性有り。
//		}
//	}
//↓2018/04/18変更:「keep-trans-order」「sort-trans-order」を追加しました。
	const ST_Trans* x1 = *(ST_Trans**)_x;
	const ST_Trans* y1 = *(ST_Trans**)_y;
	if(x1->order) {
		if(y1->order) {
			if(x1->order < y1->order) { return -1; }
			if(x1->order > y1->order) { return  1; }
			DIE();	//バグ	一つの遷移セットの中に(order=1～)で同一の値が重複する事は無いはず。((order=1～)は登録後のハッシュの要素数だから。MapBody_addTrans(),StateBody_addTrans()の実装を参照せよ。)
		} else {
			DIE();	//バグ	一つの遷移セットの中で(order=0)と(order=1～)が混在する事は無いはず。
		}
	} else {
		if(y1->order) {
			DIE();	//バグ	一つの遷移セットの中で(order=0)と(order=1～)が混在する事は無いはず。
		} else {
			const ST_Func* x2 = x1->transBody->guard;
			const ST_Func* y2 = y1->transBody->guard;
			if(x2) {
				if(y2) {
					return (*fnLabCmp)(x2->name, y2->name);
				} else {
					return -1;	//(x)はガードが有る,(y)はガードが無い⇒(y)を後にする
				}
			} else {
				if(y2) {
					return  1;	//(x)はガードが無い,(y)はガードが有る⇒(x)を後にする
				} else {
					DIE();		//(x)はガードが無い,(y)はガードが無い⇒バグ。ガードが無い遷移が重複して登録される事は無いはず。登録処理での検出処理にバグが有る可能性有り。
				}
			}
		}
	}
//}}2018/04/18変更:「keep-trans-order」「sort-trans-order」を追加しました。
}
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++) {
//{{2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
//					fprintf(fp, "\t%s", TBL_Map[iMap]);
//↓2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
					if(iscsymf(*TBL_Map[iMap])) {
						fprintf(fp, "\t%s", TBL_Map[iMap]);
					} else {
						fprintf(fp, "\t`%s", TBL_Map[iMap]);
					}
//}}2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
					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++) {
//{{2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
//					fprintf(fp, "\t%s", TBL_State[iState]);
//↓2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
					if(iscsymf(*TBL_State[iState])) {
						fprintf(fp, "\t%s", TBL_State[iState]);
					} else {
						fprintf(fp, "\t`%s", TBL_State[iState]);
					}
//}}2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
					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++) {
//{{2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
//					fprintf(fp, "\t%s", TBL_Trans[iTrans]);
//↓2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
					if(iscsymf(*TBL_Trans[iTrans])) {
						fprintf(fp, "\t%s", TBL_Trans[iTrans]);
					} else {
						fprintf(fp, "\t`%s", TBL_Trans[iTrans]);
					}
//}}2018/04/17変更:状態マップ名,状態名,遷移名の一文字がCシンボル文字でなかった場合、先頭に「`」を付けて列挙定義スクリプトに出力するように修正しました。
					if(!iTrans) { fprintf(fp, "\t1"); }		//遷移番号は1ベース
					fputc('\n', fp);
				}
				fprintf(fp, "end\n");
			}
			//列挙定義スクリプトファイルを閉じる。
			fclose(fp);
		//列挙定義スクリプトファイル名が指定されていなければ…
		} else {
//{{2017/03/05変更:'-xc','-xcs'オプションを追加しました。
//			//状態マップ
//			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);
//			}
//↓2017/03/05変更:'-xc','-xcs'オプションを追加しました。
			//C言語形式
			if(!strcmp(opt_x, "c")) {
				//状態マップ
				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);
				}
			//C#形式
			} else if(!strcmp(opt_x, "cs")) {
				//C#には、ヘッダファイルとソースファイルを分離する概念は有りません。
				//全て、この後の「'標準出力,又は,C言語ソースファイル'」への出力処理にて出力します。
				//従って、列挙定義スクリプトファイル名が指定されていない場合に、ここで状態マップ番号,状態番号,遷移番号を出力する事は不要です。
				/** no job **/
			} else {
				DIE();	//バグ
			}
//}}2017/03/05変更:'-xc','-xcs'オプションを追加しました。
		}
	}
	//標準出力
	{
		FILE* fp = stdout;
		//C言語ソースファイル名が指定されていたら…	C言語ソースファイル名が指定されていなければ、以下の'標準出力,又は,C言語ソースファイル'も標準出力に出力するので、ここでグローバル関数宣言を出力する事は不要です。
		if(sTblOut) {
//{{2017/03/05変更:'-xc','-xcs'オプションを追加しました。
//			//グローバル関数が一つでも有れば、グローバル関数宣言ブロックを出力する。
//			//グローバル関数が一つも無ければ、グローバル関数宣言ブロックを出力しない。
//			// - グローバル関数が一つも無い時に、空の #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");
//			}
//↓2017/03/05変更:'-xc','-xcs'オプションを追加しました。
			//C言語形式
			if(!strcmp(opt_x, "c")) {
//{{2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
				fprintf(fp, "#ifdef  __cplusplus\n");
				fprintf(fp, "extern \"C\" {\n");
				fprintf(fp, "#endif//__cplusplus\n");
//}}2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
				//グローバル関数が一つでも有れば、グローバル関数宣言ブロックを出力する。
				//グローバル関数が一つも無ければ、グローバル関数宣言ブロックを出力しない。
				// - グローバル関数が一つも無い時に、空の #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");
//{{2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
//					fprintf(fp, "#ifdef  __cplusplus\n");
//					fprintf(fp, "extern \"C\" {\n");
//					fprintf(fp, "#endif//__cplusplus\n");
//}}2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
					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;
							}
						}
					}
//{{2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
//					fprintf(fp, "#ifdef  __cplusplus\n");
//					fprintf(fp, "}//extern \"C\"\n");
//					fprintf(fp, "#endif//__cplusplus\n");
//}}2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
				}
//{{2018/04/14追加:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
				fprintf(fp, "//関数テーブル\n");
				fprintf(fp, "extern void* const TBL_FsmFunc[];\n");
//}}2018/04/14追加:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
//{{2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
				fprintf(fp, "#ifdef  __cplusplus\n");
				fprintf(fp, "}//extern \"C\"\n");
				fprintf(fp, "#endif//__cplusplus\n");
//}}2018/04/14移動:clipfsm.cモジュールにてTBL_FsmFuncをグローバル変数⇒ST_FsmObj構造体のフィールドへ移動した事に伴い、当ツールが出力するヘッダファイルにTBL_FsmFuncの宣言を追加しました。
			//C#形式
			} else if(!strcmp(opt_x, "cs")) {
				//C#には、ヘッダファイルとソースファイルを分離する概念は有りません。
				//全て、この後の「'標準出力,又は,C言語ソースファイル'」への出力処理にて出力します。
				//従って、ソースファイル名の指定の有無に係わらず、ここでグローバル関数宣言を出力する事は不要です。
				/** no job **/
			} else {
				DIE();	//バグ
			}
//}}2017/03/05変更:'-xc','-xcs'オプションを追加しました。
		}
	}
	//標準出力,又は,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の末尾には確実に改行が補われているので、ここで末尾に改行を出力する必要は無い。
//{{2017/03/05変更:'-xc','-xcs'オプションを追加しました。
//		//グローバル関数宣言,及び,ローカル関数定義
//		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, "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");
//↓2017/03/05変更:'-xc','-xcs'オプションを追加しました。
		//C言語形式
		if(!strcmp(opt_x, "c")) {
			//グローバル関数宣言,及び,ローカル関数定義
			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, "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#形式
		} else if(!strcmp(opt_x, "cs")) {
			//列挙定義スクリプトファイル名が指定されていなければ…
			if(!sEnmOut) {
				fprintf(fp, "static partial class Const {\n");
				//状態マップ
				if(nMap) {
					fprintf(fp, "\t//状態マップ\n");
					for(iMap = 0; iMap < nMap; iMap++) {
						fprintf(fp, "\tpublic const int FsmMap_%s = %d;\n", TBL_Map[iMap], iMap + 1);		//状態マップ番号は1ベース
					}
					fprintf(fp, "\tpublic const int FsmMap_Max = %d;\n", nMap);
				}
				//状態
				if(nState) {
					fprintf(fp, "\t//状態\n");
					for(iState = 0; iState < nState; iState++) {
						fprintf(fp, "\tpublic const int FsmState_%s = %d;\n", TBL_State[iState], iState + 1);	//状態番号は1ベース
					}
					fprintf(fp, "\tpublic const int FsmState_Max = %d;\n", nState);
				}
				//遷移
				if(nTrans) {
					fprintf(fp, "\t//遷移\n");
					for(iTrans = 0; iTrans < nTrans; iTrans++) {
						fprintf(fp, "\tpublic const int FsmTrans_%s = %d;\n", TBL_Trans[iTrans], iTrans + 1);	//遷移番号は1ベース		default遷移(=0)の定義は有無にかかわらず出力しない。アプリケーションが明示的にdefault遷移を指定する必要は無いはずである。
					}
					fprintf(fp, "\tpublic const int FsmTrans_Max = %d;\n", nTrans);
				}
				fprintf(fp, "}\n");
			}
			//インターフェイス定義
			fprintf(fp, "interface IFsmFunc {\n");
			for(iFunc = 0; iFunc < nFunc; iFunc++) {
				ST_Func* func = TBL_Func[iFunc];
				if(!func->code) {
					if(mode != 1) { mode = 1; fprintf(fp, "\t//グローバル関数宣言\n"); }
				} else {
					if(mode != 2) { mode = 2; fprintf(fp, "\t//ローカル関数宣言\n"); }
				}
				switch(func->op) {
				default:DIE();
				case 1/*Entry/Exit/Suspend/Resume*/:
					fprintf(fp, "\tvoid %s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iAltState);\n", (iscsymf(*func->name) ? "" : "_"), func->name);
					break;
				case 2/*Guard*/:
					fprintf(fp, "\tbool %s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iTrans, object pParam);\n", (iscsymf(*func->name) ? "" : "_"), func->name);
					break;
				case 3/*Act*/:
					fprintf(fp, "\tvoid %s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iTrans, object pParam);\n", (iscsymf(*func->name) ? "" : "_"), func->name);
					break;
				}
			}
			fprintf(fp, "}\n");
			//クラス定義
			fprintf(fp, "partial class FsmFunc : IFsmFunc {\n");
			{
				//関数テーブル
				fprintf(fp, "\t//関数テーブル\n");
				fprintf(fp, "\tpublic static object[] CreateTable() {\n");
				fprintf(fp, "\t\tIFsmFunc pFsmFunc = new FsmFunc();\n");
				fprintf(fp, "\t\treturn new object[] {\n");
				fprintf(fp, "\t\t\t%d,\n", nFunc + 1);																							//先頭要素に関数配列の要素数を格納する
				for(iFunc = 0; iFunc < nFunc; iFunc++) {
					ST_Func* func = TBL_Func[iFunc];
					switch(func->op) {
					default:DIE();
					case 1/*Entry/Exit/Suspend/Resume*/:
						fprintf(fp, "\t\t\tnew System.Action<org.piece_me.libclip.ST_FsmObj/*pFsmObj*/,int/*iAltState*/>(pFsmFunc.%s%s),//%d\n", (iscsymf(*func->name) ? "" : "_"), func->name, iFunc + 1);			//関数番号は1ベース
						break;
					case 2/*Guard*/:
						fprintf(fp, "\t\t\tnew System.Func<org.piece_me.libclip.ST_FsmObj/*pFsmObj*/,int/*iTrans*/,object/*pParam*/,bool>(pFsmFunc.%s%s),//%d\n", (iscsymf(*func->name) ? "" : "_"), func->name, iFunc + 1);	//関数番号は1ベース
						break;
					case 3/*Act*/:
						fprintf(fp, "\t\t\tnew System.Action<org.piece_me.libclip.ST_FsmObj/*pFsmObj*/,int/*iTrans*/,object/*pParam*/>(pFsmFunc.%s%s),//%d\n", (iscsymf(*func->name) ? "" : "_"), func->name, iFunc + 1);	//関数番号は1ベース
						break;
					}
				}
				fprintf(fp, "\t\t};\n");
				fprintf(fp, "\t}\n");
				//ローカル関数定義
				mode = 0;
				for(iFunc = 0; iFunc < nFunc; iFunc++) {
					ST_Func* func = TBL_Func[iFunc];
					if(func->code) {
						if(mode != 2) { mode = 2; fprintf(fp, "\t//ローカル関数定義\n"); }
						switch(func->op) {
						default:DIE();
						case 1/*Entry/Exit/Suspend/Resume*/:
							fprintf(fp, "\tvoid IFsmFunc.%s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iAltState) { %s; }\n", (iscsymf(*func->name) ? "" : "_"), func->name, func->code);
							break;
						case 2/*Guard*/:
							fprintf(fp, "\tbool IFsmFunc.%s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iTrans, object pParam) { return %s; }\n", (iscsymf(*func->name) ? "" : "_"), func->name, func->code);
							break;
						case 3/*Act*/:
							fprintf(fp, "\tvoid IFsmFunc.%s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iTrans, object pParam) { %s; }\n", (iscsymf(*func->name) ? "" : "_"), func->name, func->code);
							break;
						}
					}
				}
			}
			fprintf(fp, "}\n");
			//グローバル関数定義の雛形
			fprintf(fp, "#if false\n");
			fprintf(fp, "//グローバル関数定義の雛形\n");
			fprintf(fp, "partial class FsmFunc {\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, "\tvoid IFsmFunc.%s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iAltState) { }\n", (iscsymf(*func->name) ? "" : "_"), func->name);
							break;
						case 2/*Guard*/:
							fprintf(fp, "\tbool IFsmFunc.%s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iTrans, object pParam) { return true; }\n", (iscsymf(*func->name) ? "" : "_"), func->name);
							break;
						case 3/*Act*/:
							fprintf(fp, "\tvoid IFsmFunc.%s%s(org.piece_me.libclip.ST_FsmObj pFsmObj, int iTrans, object pParam) { }\n", (iscsymf(*func->name) ? "" : "_"), func->name);
							break;
						}
					}
				}
			}
			fprintf(fp, "}\n");
			fprintf(fp, "#endif\n");
		} else {
			DIE();	//バグ
		}
//}}2017/03/05変更:'-xc','-xcs'オプションを追加しました。
		//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%s\t\t\t\t\t\t#//\t%s\n", "end", "end");
//}}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();
//{{2018/04/20変更:自己遷移を抑止する方法を変更しました。
//						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;
//↓2018/04/20変更:自己遷移を抑止する方法を変更しました。
						case Fsm_TransType_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, (Fsm_TransType_set <<16)|iState,         trans->transBody->nextState->state);
							break;
						case Fsm_TransType_setx:
							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, (Fsm_TransType_setx<<16)|iState,         trans->transBody->nextState->state);
							break;
						case Fsm_TransType_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, (Fsm_TransType_push<<16)|iState, "push", trans->transBody->nextState->state);
							break;
						case Fsm_TransType_pop:
							fprintf(fp, "\t\t\t%d\t=\t%d\t\t\t#//\t\t%s\n",     iValNm, (Fsm_TransType_pop <<16),        "pop");
							break;
//}}2018/04/20変更:自己遷移を抑止する方法を変更しました。
						}
					}
					iValNm++;
					if(*pp_trans) { fprintf(fp, "\n"); }	//読み易さのために一行空ける。
				}
				fprintf(fp, "\t\t%s\t\t\t\t\t\t#//\t%s\n", "end", "end");
			}
			//～～状態マップの状態～～
			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);	//状態のキーの名前は最上位ビットをセットする。状態マップのキーの直下に状態と遷移のキーが並存するので、状態と遷移のキーの名前が重複する事を避けるためです。
//{{2018/04/12追加:空の状態も作成出来るようにしました。
				if(!state->stateBody->entry &&
				   !state->stateBody->exit &&
				   !state->stateBody->suspend &&
				   !state->stateBody->resume &&
				   !ght_size(state->stateBody->ht_transSet)) {
					fprintf(fp, "\t\t\t%s\t\t\t\t\t#//\t\t%s\n", "prevent-deletion", "prevent-deletion");	//空の状態ならばdRegTblC.exeの最適化によってキーが削除されないように「prevent-deletion」を出力する。空の状態でなくても常に「prevent-deletion」を出力しても動作上は問題無いのだが、紛らわしさを避けるために空の状態の場合のみ「prevent-deletion」を出力する事にした。
				}
//}}2018/04/12追加:空の状態も作成出来るようにしました。
				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();
//{{2018/04/20変更:自己遷移を抑止する方法を変更しました。
//							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;
//↓2018/04/20変更:自己遷移を抑止する方法を変更しました。
							case Fsm_TransType_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, (Fsm_TransType_set <<16)|iState,         trans->transBody->nextState->state);
								break;
							case Fsm_TransType_setx:
								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, (Fsm_TransType_setx<<16)|iState,         trans->transBody->nextState->state);
								break;
							case Fsm_TransType_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, (Fsm_TransType_push<<16)|iState, "push", trans->transBody->nextState->state);
								break;
							case Fsm_TransType_pop:
								fprintf(fp, "\t\t\t\t%d\t=\t%d\t\t#//\t\t\t%s\n",     iValNm, (Fsm_TransType_pop <<16),        "pop");
								break;
//}}2018/04/20変更:自己遷移を抑止する方法を変更しました。
							}
						}
						iValNm++;
						if(*pp_trans) { fprintf(fp, "\n"); }	//読み易さのために一行空ける。
					}
					fprintf(fp, "\t\t\t%s\t\t\t\t\t#//\t\t%s\n", "end", "end");
				}
				fprintf(fp, "\t\t%s\t\t\t\t\t\t#//\t%s\n", "end", "end");
			}
			fprintf(fp, "\t%s\t\t\t\t\t\t\t#//%s\n", "end", "end");
		}
		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);
		}
	}
}
