/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
//-----------------------------------------------------------------------------
//警告定義
enum {
	WARN_CAST,			//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
};
static const char* const warn_tokens[]={
	"cast",				//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
NULL};//終端
int warn;				//各警告(WARN_*)について(warn&(1<<WARN_*))がセットされていたらその警告がOnである事を示します。
/*---------------------------------------------------------------------------*/
static ght_hash_table_t* ht_table;	//テーブルの名前⇒(ST_Table*)		┬タイプ名とテーブル名が重複してはいけない。
static ght_hash_table_t* ht_type;	//タイプの名前⇒(ST_Type*)		┘
static ght_hash_table_t* ht_var;	//変数の名前⇒'変数id'			変数名は異なるテーブル間で重複しても良い。変数名とタイプ名,及び,変数名とテーブル名が重複しても良い。当ツールは全ての変数の名前に一意な'変数id'を割り当てるためにこのハッシュを使う。
static ght_hash_table_t* ht_attr;	//属性の名前⇒'属性id'			　　　　　　　　　　　　　　　　　　　　　属性名とタイプ名,及び,属性名とテーブル名が重複しても良い。当ツールは全ての属性の名前に一意な'属性id'を割り当てるためにこのハッシュを使う。	┬属性名と書式名も重複して良い。
static ght_hash_table_t* ht_fmt;	//書式の名前⇒'書式id'			　　　　　　　　　　　　　　　　　　　　　書式名とタイプ名,及び,書式名とテーブル名が重複しても良い。当ツールは全ての書式の名前に一意な'書式id'を割り当てるためにこのハッシュを使う。	┘
//ラベル名のソート方法
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, int bOmitName, int bOmitDesc, int bOmitFmt);
/*---------------------------------------------------------------------------*/
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'オプションが指定されたらレジストリスクリプトファイル名を設定する。
	int bOmitName = 0;	//'-s',又は,'-sn'オプションが指定されたら名前の文字列を格納しない。
	int bOmitDesc = 0;	//'-s',又は,'-sd'オプションが指定されたら説明文を格納しない。
	int bOmitFmt  = 0;	//'-s',又は,'-sf'オプションが指定されたら書式を格納しない。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "dr:s::enw::qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;		//最適化を行わない。
			break;
		case 'r':
			sRegOut = optarg;	//レジストリスクリプトファイル名を設定する。
			break;
		case 's':
			if(optarg) {
				     if(!strcmp(optarg, "n")) { bOmitName = 1; }	//名前の文字列を格納しない。
				else if(!strcmp(optarg, "d")) { bOmitDesc = 1; }	//説明文を格納しない。
				else if(!strcmp(optarg, "f")) { bOmitFmt  = 1; }	//書式を格納しない。
				else { usage(); }
			} else {
				bOmitName = bOmitDesc = bOmitFmt = 1;	//名前の文字列と説明文と書式を格納しない。
			}
			break;
		case 'e':
			{
				//エラーログファイル名を作成する。
				// - カレントフォルダに'実行ファイル名.err'という名前で生成する事にした。この動作は、'lk33.exe'の仕様を参考にした。
				char fname[_MAX_FNAME];
				_splitpath(argv[0], NULL, NULL, fname, NULL);
				_makepath(err_path, NULL, NULL, fname, ".err");
				//'-e'オプションが指定されたら、最初に確実に、既存のエラーログファイルを削除しておく。
				//前回に生成したエラーログが残っていて、今回生成したエラーログと混同する危険を避けるためである。
				//尚、'-e'オプションが指定されなかった場合は、既存のエラーログファイルを削除する処理は行わない。
				remove(err_path);
			}
			break;
		case 'n':
			fnLabCmp = strnatcmp;	//ラベル名のソートにstrnatcmp()を使用する。
			break;
		case 'w':
			if(optarg) {
				char *optionp = optarg, *valuep;
				while(*optionp) {
					switch(getsubopt(&optionp, (char* const*)warn_tokens, &valuep)) {
					case WARN_CAST:
						if(valuep) { usage(); }
						warn |= (1<<WARN_CAST);
						break;
					default:
						usage();
					}
				}
			} else {
				warn = -1;
			}
			break;
		case 'q':
			opt_q = 1;
			break;
		case 'h':
		default:
			usage();
		}
	}
	//ハッシュテーブルを作成する。
	ght_set_rehash((ht_table = ght_create(0)), 1);
	ght_set_rehash((ht_type  = ght_create(0)), 1);
	ght_set_rehash((ht_var   = ght_create(0)), 1);
	ght_set_rehash((ht_attr  = ght_create(0)), 1);
	ght_set_rehash((ht_fmt   = ght_create(0)), 1);
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		init_location("-");	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
		yylex_init(stdin);
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			init_location(argv[i]);	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
			yylex_init(fp);
			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			fclose(fp);
		}
	}
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass2 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//--- 出力処理 ---
	//出力処理を行う。
	output(sRegOut, bOmitName, bOmitDesc, bOmitFmt);
	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, "dPrpTblC - プロパティテーブルコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015-2016 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dPrpTblC [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, "  -s           名前の文字列,説明文,書式を格納しません。\n");
	fprintf(stderr, "  -sn          名前の文字列を格納しません。\n");
	fprintf(stderr, "  -sd          説明文を格納しません。\n");
	fprintf(stderr, "  -sf          書式を格納しません。\n");
	fprintf(stderr, "               これらのオプションを指定しなければ、名前の文字列,説明文,書式を格納します。\n");
	fprintf(stderr, "  -e           エラーログファイルを生成します。\n");
	fprintf(stderr, "               このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -n           ラベル名のソートに、strnatcmp()を使用します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、strcmp()を使用します。\n");
	fprintf(stderr, "  -w           全ての警告を有効にします。\n");
	fprintf(stderr, "               このオプションを指定しなければ、個別に設定した警告のみが有効になります。\n");
	fprintf(stderr, "  -wcast       暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、警告表示もエラー終了もしません。\n");
	fprintf(stderr, "  -q           進捗表示を抑制します。\n");
	fprintf(stderr, "  -h           使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①dPrpTblC.exe PrpTbl.txt > PrpTbl.out\n");
	fprintf(stderr, "  　PrpTbl.txtをコンパイルして、PrpTbl.outへ出力します。\n");
	fprintf(stderr, "  ②dPrpTblC.exe -rRegPrpTbl.txt PrpTbl.txt > PrpTbl.h\n");
	fprintf(stderr, "  　PrpTbl.txtをコンパイルして、ラベルをPrpTbl.hへ出力します。\n");
	fprintf(stderr, "  　プロパティテーブルのレジストリスクリプトを、RegPrpTbl.txtへ出力します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int cast_dbl_to_int(double d) {
	int i = d;
	if(i != d) {
		if(warn & (1<<WARN_CAST)) {
			yyerror("切り捨て誤差が発生しました: %f → %d", d, i);
			yynerrs++;	//呼び出し元(main)に知らせるために。
			//全てのセマンティクスエラーを報告するために継続する。
		}
	}
	return i;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void addType(ST_Type* pType) {
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", pType->pLabel);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//タイプの名前がテーブルの名前と重複していない事を確認する。
	if(ght_get(ht_table,
		strlen(pType->pLabel) + 1/*nul*/,
		pType->pLabel)) {
		yyerror("type定義の名前がtable定義の名前と重複しています。(%s)", pType->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
	//タイプの名前が重複していない事を確認し、タイプを登録する。
	if(ght_insert(ht_type,
		       pType,
		strlen(pType->pLabel) + 1/*nul*/,
		       pType->pLabel)) {
		yyerror("type定義の名前が重複しています。(%s)", pType->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void addTable(ST_Table* pTable) {
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", pTable->pLabel);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//テーブルの名前がタイプの名前と重複していない事を確認する。
	if(ght_get(ht_type,
		strlen(pTable->pLabel) + 1/*nul*/,
		pTable->pLabel)) {
		yyerror("table定義の名前がtype定義の名前と重複しています。(%s)", pTable->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
	//テーブルの名前が重複していない事を確認し、テーブルを登録する。
	if(ght_insert(ht_table,
		       pTable,
		strlen(pTable->pLabel) + 1/*nul*/,
		       pTable->pLabel)) {
		yyerror("table定義の名前が重複しています。(%s)", pTable->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
char* addInlineType(ST_TypeBody* pTypeBody) {
	sds s;
	int i;
    //{{この範囲の処理内容はaddInlineType()もType_new()も同じです。
	if(pTypeBody->pRange && pTypeBody->pEnum) { DIE(); }	//バグ。TypeBody_setRange(),又は,TypeBody_setEnum()で検出しているはず。
	if(pTypeBody->pRange) {
		if(!pTypeBody->pInit) {
			pTypeBody->pInit = Init_new(pTypeBody->pRange->iMin);	//範囲型でinitが省略されたら下限値と見なす。
		}
	} else if(pTypeBody->pEnum) {
		if(!pTypeBody->pInit) {
			if(!pTypeBody->pEnum->pExprList->nExpr) { DIE(); }	//バグ。ExprList_new()とExprList_add()を使えば空の数値リストは作成されないはず。
			pTypeBody->pInit = Init_new(pTypeBody->pEnum->pExprList->aExpr[0]);	//列挙型でinitが省略されたら最初の列挙値と見なす。
		}
	} else {
		yyerror("inlineType定義にrangeもenumも指定されていません。");
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
    //}}この範囲の処理内容はaddInlineType()もType_new()も同じです。
	//(内部的な)インラインタイプ名を作成する。
	s = sdsnew("~");	//(内部的な)インラインタイプ名は'~'から始まる事とした。lex.yy.lにて'~'から始まるラベルは受理しないのでユーザー定義のラベルと衝突する恐れは無い。またラベルをソートした時に'~'は最後付近になるので見た目に判り易い。
	if(pTypeBody->pRange) {
		s = sdscat(s, "range");
		s = sdscat(s, "~");	//セパレータも'~'とする事にした。必然ではない。
		s = sdscatprintf(s, "%d", pTypeBody->pRange->iMin);
		s = sdscat(s, "~");
		s = sdscatprintf(s, "%d", pTypeBody->pRange->iMax);
	} else if(pTypeBody->pEnum) {
		s = sdscat(s, "enum");
		for(i = 0; i < pTypeBody->pEnum->pExprList->nExpr; i++) {
			s = sdscat(s, "~");
			s = sdscatprintf(s, "%d", pTypeBody->pEnum->pExprList->aExpr[i]);
		}
	} else {
		DIE();	//バグ
	}
	if(pTypeBody->pInit) {
		s = sdscat(s, "~");
		s = sdscat(s, "init");
		s = sdscat(s, "~");
		s = sdscatprintf(s, "%d", pTypeBody->pInit->iInit);
	} else {
		DIE();	//バグ
	}
	//このインラインタイプと同じ内容のインラインタイプが既に登録されていなければ…
	if(!ght_get(ht_type, sdslen(s) + 1/*nul*/, s)) {
		//このインラインタイプを登録する。
		ST_Type* pType = Type_new(LabelRow_new(s/*タイプ名*/, Fmt_new(NULL/*書式文字列無し*/, NULL/*書式ラベル無し*/), ""/*説明文無し*/), pTypeBody);
		if(!pType) { DIE(); }	//Type_new()が行うエラー検出と同じエラー検出を当関数の前半の処理で行っているので、Type_new()でエラー検出される事はないはず。もしここで止まったら当ツールのバグだろう。
		addType(pType);
	}
	//このインラインタイプの名前を返す。
	return s;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Fmt* Fmt_new(char* pFmtStr/*NULL可*/, char* pFmtLab/*NULL可*/) {
	ST_Fmt* pFmt = calloc(1, sizeof(ST_Fmt));
	if(!pFmt) { DIE(); }
	//書式ラベルが指定されていたら…
	if(pFmtLab) {
		//書式の名前を登録する。
		// - 既に登録されていた場合はght_insert()は失敗するが構わない。
		ght_insert(ht_fmt,
			(void*)-1/*dummy*/,
			strlen(pFmtLab) + 1/*nul*/,
			       pFmtLab);
	}
	pFmt->pFmtStr	= pFmtStr;	//NULL可。
	pFmt->pFmtLab	= pFmtLab;	//NULL可。
	return pFmt;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ExprList* ExprList_new(int iExpr) {
	ST_ExprList* pExprList = calloc(1, sizeof(ST_ExprList));
	if(!pExprList) { DIE(); }
	return ExprList_add(pExprList, iExpr);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ExprList* ExprList_add(ST_ExprList* pExprList, int iExpr) {
	if((iExpr < INT16_MIN) || (iExpr > INT16_MAX)) {
		yyerror("数値リストに範囲外の値が指定されています。(%d)", iExpr);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	pExprList = realloc(pExprList, sizeof(ST_ExprList) + (sizeof pExprList->aExpr[0]) * (pExprList->nExpr + 1));
	if(!pExprList) { DIE(); }
	pExprList->aExpr[pExprList->nExpr++]	= iExpr;
	return pExprList;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_AttrList* AttrList_new(char* pAttr) {
	ST_AttrList* pAttrList = calloc(1, sizeof(ST_AttrList));
	if(!pAttrList) { DIE(); }
	return AttrList_add(pAttrList, pAttr);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_AttrList* AttrList_add(ST_AttrList* pAttrList, char* pAttr) {
	int i;
	for(i = 0; i < pAttrList->nAttr; i++) {
		if(!strcmp(pAttrList->aAttr[i], pAttr)) {	//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			yyerror("属性リストに同じ属性が重複指定されています。(%s)", pAttr);
			yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;	//y.tab.yの処理でYYERRORを行う。
		}
	}
	//属性の名前を登録する。
	// - 既に登録されていた場合はght_insert()は失敗するが構わない。
	ght_insert(ht_attr,
		(void*)-1/*dummy*/,
		strlen(pAttr) + 1/*nul*/,
		       pAttr);
	pAttrList = realloc(pAttrList, sizeof(ST_AttrList) + (sizeof pAttrList->aAttr[0]) * (pAttrList->nAttr + 1));
	if(!pAttrList) { DIE(); }
	pAttrList->aAttr[pAttrList->nAttr++]	= pAttr;
	return pAttrList;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_LabelRow* LabelRow_new(char* pLabel, ST_Fmt* pFmt/*NULL不可*/, char* pDesc/*NULL不可*/) {
	ST_LabelRow* pLabelRow = calloc(1, sizeof(ST_LabelRow));
	if(!pLabelRow) { DIE(); }
	pLabelRow->pLabel	= pLabel;	//NULL不可。空文字不可。
	pLabelRow->pDesc	= pDesc;	//NULL不可。説明文無しならば""とする。
	pLabelRow->pFmt		= pFmt;		//NULL不可。
	return pLabelRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Type* Type_new(ST_LabelRow* pLabelRow, ST_TypeBody* pTypeBody) {
	char* const pLabel = pLabelRow->pLabel;	//┐
	char* const pDesc  = pLabelRow->pDesc;	//┼フィールドを取り出しておく。これ以降、pLabelRowは参照しない。
	ST_Fmt* const pFmt = pLabelRow->pFmt;	//┘
	ST_Type* pType;
    //{{この範囲の処理内容はaddInlineType()もType_new()も同じです。
	if(pTypeBody->pRange && pTypeBody->pEnum) { DIE(); }	//バグ。TypeBody_setRange(),又は,TypeBody_setEnum()で検出しているはず。
	if(pTypeBody->pRange) {
		if(!pTypeBody->pInit) {
			pTypeBody->pInit = Init_new(pTypeBody->pRange->iMin);	//範囲型でinitが省略されたら下限値と見なす。
		}
	} else if(pTypeBody->pEnum) {
		if(!pTypeBody->pInit) {
			if(!pTypeBody->pEnum->pExprList->nExpr) { DIE(); }	//バグ。ExprList_new()とExprList_add()を使えば空の数値リストは作成されないはず。
			pTypeBody->pInit = Init_new(pTypeBody->pEnum->pExprList->aExpr[0]);	//列挙型でinitが省略されたら最初の列挙値と見なす。
		}
	} else {
		yyerror("type定義にrangeもenumも指定されていません。(%s)", pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
    //}}この範囲の処理内容はaddInlineType()もType_new()も同じです。
#ifdef  USE_PRP_DOUBLE
	if(!strcmp(pLabel, "Double")) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
		yyerror("組み込み'Double'型を有効にしているので、アプリケーション定義のタイプに'Double'という名前は使用出来ません。");
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
#endif//USE_PRP_DOUBLE
	pType = calloc(1, sizeof(ST_Type));
	if(!pType) { DIE(); }
	pType->pLabel		= pLabel;
	pType->pDesc		= pDesc;
	pType->pFmt		= pFmt;
	pType->pTypeBody	= pTypeBody;
	return pType;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TypeBody* TypeBody_new() {
	ST_TypeBody* pTypeBody = calloc(1, sizeof(ST_TypeBody));
	if(!pTypeBody) { DIE(); }
	return pTypeBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TypeBody* TypeBody_setInit(ST_TypeBody* pTypeBody, ST_Init* pInit) {
	if(pTypeBody->pInit) {
		yyerror("type定義にinitが重複指定されています。");
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	pTypeBody->pInit	= pInit;
	return pTypeBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TypeBody* TypeBody_setRange(ST_TypeBody* pTypeBody, ST_Range* pRange) {
	if(pTypeBody->pRange || pTypeBody->pEnum) {
		yyerror("type定義にrangeとenumを両方指定する事は出来ません。");
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	pTypeBody->pRange	= pRange;
	return pTypeBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TypeBody* TypeBody_setEnum(ST_TypeBody* pTypeBody, ST_Enum* pEnum) {
	if(pTypeBody->pRange || pTypeBody->pEnum) {
		yyerror("type定義にrangeとenumを両方指定する事は出来ません。");
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	pTypeBody->pEnum	= pEnum;
	return pTypeBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Init* Init_new(int iExpr) {
	ST_Init* pInit;
//{{2016/02/28削除:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
//│上記の変更に伴い、ここで初期値を検査する処理を削除しました。
//│たとえば'range 0,65535'等の場合に、'init 65535'も有効とするためです。
//│ここで指定された初期値が有効な値であるかどうかは、range型でもenum型でも、pass2()内の'エンコード済みの初期値を決定する'処理で検査するので、ここで検査しなくても問題ありません。
//│元々、ここで検査を行っていた理由は、少しでも早くエラーを検出するためだけであり、行わなくても構わない処理でした。
//	if((iExpr < INT16_MIN) || (iExpr > INT16_MAX)) {
//		yyerror("initに範囲外の値が指定されています。(%d)", iExpr);
//		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
//		return NULL;	//y.tab.yの処理でYYERRORを行う。
//	}
//}}2016/02/28削除:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
	pInit = calloc(1, sizeof(ST_Init));
	if(!pInit) { DIE(); }
	pInit->iInit		= iExpr;
	return pInit;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Range* Range_new(int iMin, int iMax) {
	ST_Range* pRange;
//{{2016/02/28変更:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
//	if((iMin < INT16_MIN) || (iMin > INT16_MAX)) {
//		yyerror("rangeに範囲外の値が指定されています。(%d)", iMin);
//		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
//		return NULL;	//y.tab.yの処理でYYERRORを行う。
//	}
//	if((iMax < INT16_MIN) || (iMax > INT16_MAX)) {
//		yyerror("rangeに範囲外の値が指定されています。(%d)", iMax);
//		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
//		return NULL;	//y.tab.yの処理でYYERRORを行う。
//	}
//	if(iMin > iMax) {
//		yyerror("rangeの範囲が逆です。(%d,%d)", iMin, iMax);
//		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
//		return NULL;	//y.tab.yの処理でYYERRORを行う。
//	}
//↓2016/02/28変更:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
	if((iMin < INT16_MIN) || (iMin > INT16_MAX) || ((unsigned)(iMax - iMin) > UINT16_MAX)) {
		yyerror("rangeに範囲外の値が指定されています。(%d,%d)", iMin, iMax);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}

//}}2016/02/28変更:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
	pRange = calloc(1, sizeof(ST_Range));
	if(!pRange) { DIE(); }
	pRange->iMin		= iMin;
	pRange->iMax		= iMax;
	return pRange;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Enum* Enum_new(ST_ExprList* pExprList) {
	ST_Enum* pEnum;
	int i, j;
	if(!pExprList->nExpr) { DIE(); }	//バグ。ExprList_new()とExprList_add()を使えば空の数値リストは作成されないはず。
	for(i = 0; i < pExprList->nExpr; i++) {
		for(j = i + 1; j < pExprList->nExpr; j++) {
			if(pExprList->aExpr[i] == pExprList->aExpr[j]) {
				yyerror("enumに同じ値が重複指定されています。(%d)", pExprList->aExpr[i]);
				yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
				return NULL;	//y.tab.yの処理でYYERRORを行う。
			}
		}
	}
	pEnum = calloc(1, sizeof(ST_Enum));
	if(!pEnum) { DIE(); }
	pEnum->pExprList	= pExprList;
	return pEnum;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Table_new_compar(const void* _x, const void* _y) {
	const ST_Var** x = _x;
	const ST_Var** y = _y;
	if((*x)->pVarLabel[0] == '$') {
		if((*y)->pVarLabel[0] == '$') {
			return (*fnLabCmp)((*x)->pVarLabel, (*y)->pVarLabel);
		} else {
			return  1;	//'$'で始まる組み込み型ラベルを後ろにする事にしました。
		}
	} else {
		if((*y)->pVarLabel[0] == '$') {
			return -1;	//'$'で始まる組み込み型ラベルを後ろにする事にしました。
		} else {
			return (*fnLabCmp)((*x)->pVarLabel, (*y)->pVarLabel);
		}
	}
}
ST_Table* Table_new(ST_LabelRow* pLabelRow, ST_TableBody* pTableBody) {
	char* const pLabel = pLabelRow->pLabel;	//┐
	char* const pDesc  = pLabelRow->pDesc;	//┼フィールドを取り出しておく。これ以降、pLabelRowは参照しない。
	ST_Fmt* const pFmt = pLabelRow->pFmt;	//┘
	ST_Table* pTable;
	if(!pTableBody->nVar) {
		yyerror("空のtableは定義出来ません。(%s)", pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
#ifdef  USE_PRP_DOUBLE
	if(!strcmp(pLabel, "Double")) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
		yyerror("組み込み'Double'型を有効にしているので、アプリケーション定義のテーブルに'Double'という名前は使用出来ません。");
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
#endif//USE_PRP_DOUBLE
    //{{必須では有りません。もし、プロパティテーブルスクリプトで定義した順のメモリレイアウトにしたい場合は、この範囲をコメントアウトしてください。
	//変数を名前の昇順にソートする。
	qsort(pTableBody->aVar,
	      pTableBody->nVar,
	      sizeof pTableBody->aVar[0],
	      Table_new_compar);
    //}}必須では有りません。もし、プロパティテーブルスクリプトで定義した順のメモリレイアウトにしたい場合は、この範囲をコメントアウトしてください。
	pTable = calloc(1, sizeof(ST_Table));
	if(!pTable) { DIE(); }
	pTable->pLabel		= pLabel;
	pTable->pDesc		= pDesc;
	pTable->pFmt		= pFmt;
	pTable->pTableBody	= pTableBody;
	return pTable;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TableBody* TableBody_new() {
	ST_TableBody* pTableBody = calloc(1, sizeof(ST_TableBody));
	if(!pTableBody) { DIE(); }
	return pTableBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TableBody* TableBody_addVar(ST_TableBody* pTableBody, ST_Var* pVar) {
	int i;
	for(i = 0; i < pTableBody->nVar; i++) {
		if(!strcmp(pTableBody->aVar[i]->pVarLabel, pVar->pVarLabel)) {	//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			yyerror("変数の名前が重複しています。(%s)", pVar->pVarLabel);
			yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;	//y.tab.yの処理でYYERRORを行う。
		}
	}
	pTableBody = realloc(pTableBody, sizeof(ST_TableBody) + (sizeof pTableBody->aVar[0]) * (pTableBody->nVar + 1));
	if(!pTableBody) { DIE(); }
	pTableBody->aVar[pTableBody->nVar++]	= pVar;
	return pTableBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Var* Var_new(char* pVarLabel, ST_ExprList* pDim/*NULL可*/, char* pTypeLabel/*NULL可*/, ST_AttrList* pAttrList/*NULL可*/, ST_Fmt* pFmt/*NULL不可*/, char* pDesc/*NULL不可。説明文無しならば""とする。*/) {
	ST_Var* pVar;
	int i;
	if(pDim) {
		if(!pDim->nExpr) { DIE(); }	//バグ。ExprList_new()とExprList_add()を使えば空の数値リストは作成されないはず。
		for(i = 0; i < pDim->nExpr; i++) {
			if(pDim->aExpr[i] < 1) {
				yyerror("配列の要素数に1未満の値が指定されています。(%s)", pVarLabel);
				yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
				return NULL;	//y.tab.yの処理でYYERRORを行う。
			}
		}
	}
#ifdef  USE_PRP_DOUBLE
	//タイプの名前が'Double'ならば…
	if(pTypeLabel && !strcmp(pTypeLabel, "Double")) {	//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
		//タイプ名を'$32767'に置き換える。
		pTypeLabel = strdup_printf("$%d", PrpTbl_Double);
		//組み込み'Double'型のテーブルがまだ登録されていなければ…
		if(!ght_get(ht_table, strlen(pTypeLabel) + 1/*nul*/, pTypeLabel)) {
			//組み込み'Double'型のテーブルを登録する。
			ST_LabelRow*  pLabelRow  = LabelRow_new(pTypeLabel/*テーブル名='$32767'*/, Fmt_new(NULL/*書式文字列無し*/, NULL/*書式ラベル無し*/), "Double"/*説明文*/);	//{{2015/12/13変更:組み込み'Double'型の書式文字列に"%g"を格納しないようにしました。}}
			ST_TableBody* pTableBody = TableBody_new();
			pTableBody = TableBody_addVar(pTableBody, Var_new("$1", NULL/*pDim*/, NULL/*pTypeLabel*/, NULL/*pAttrList*/, Fmt_new(NULL/*書式文字列無し*/, NULL/*書式ラベル無し*/), "Double.Lo"/*説明文*/));	//下位ワード
			pTableBody = TableBody_addVar(pTableBody, Var_new("$2", NULL/*pDim*/, NULL/*pTypeLabel*/, NULL/*pAttrList*/, Fmt_new(NULL/*書式文字列無し*/, NULL/*書式ラベル無し*/), "Double.Hi"/*説明文*/));	//上位ワード
			addTable(Table_new(pLabelRow, pTableBody));
		}
	}
#endif//USE_PRP_DOUBLE
	//変数の名前を登録する。
	// - 既に登録されていた場合はght_insert()は失敗するが構わない。
	ght_insert(ht_var,
		(void*)-1/*dummy*/,
		strlen(pVarLabel) + 1/*nul*/,
		       pVarLabel);
	pVar = calloc(1, sizeof(ST_Var));
	if(!pVar) { DIE(); }
	pVar->pTypeLabel	= pTypeLabel;	//'var'変数ならばNULL
	pVar->pVarLabel		= pVarLabel;
	pVar->pDim		= pDim;		//スカラー変数ならばNULL
	pVar->pAttrList		= pAttrList;	//属性が無ければNULL
	pVar->pDesc		= pDesc;	//NULL不可。説明文無しならば""とする。
	pVar->pFmt		= pFmt;		//NULL不可。
	return pVar;
}
/*---------------------------------------------------------------------------*/
//2パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Label_compar(LIST_ENTRY* _x, LIST_ENTRY* _y) {
	//当関数の処理は、ght_hash_table.h/cモジュールの動作に依存しています。
	//外部からght_hash_table_t.le_iteratorの順番を並び替えても、動作上問題有りません。
	ght_hash_entry_t* x = CONTAINING_RECORD(_x, ght_hash_entry_t, le_iterator);
	ght_hash_entry_t* y = CONTAINING_RECORD(_y, ght_hash_entry_t, le_iterator);
	if(x->key_data[0] == '$') {
		if(y->key_data[0] == '$') {
			return (*fnLabCmp)(x->key_data, y->key_data);
		} else {
			return  1;	//'$'で始まる組み込み型ラベルを後ろにする事にしました。
		}
	} else {
		if(y->key_data[0] == '$') {
			return -1;	//'$'で始まる組み込み型ラベルを後ろにする事にしました。
		} else {
			return (*fnLabCmp)(x->key_data, y->key_data);
		}
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int AttrList_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	int ix = (int)ght_get(ht_attr, strlen(x) + 1/*nul*/, x);
	int iy = (int)ght_get(ht_attr, strlen(y) + 1/*nul*/, y);
	if(((ix < 0x00) || (ix > 0xFF)) ||
	   ((iy < 0x00) || (iy > 0xFF))) { DIE(); }	//バグ
	return ix - iy;	//オーバーフローの心配は無いので減算で比較出来る。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void pass2(int bOptimize) {
	ght_iterator_t it;
	char* pLabel;
	ST_Table* pTable;
	ST_Type* pType;
	ST_Var* pVar;
	int i, j;
	{
		//□NOTE:
		//│どのテーブルからも参照されていないタイプは、レジストリスクリプトに出力すると無駄なので、削除する事にした。
		//│アプリケーションは、テーブルの変数を介してのみタイプを意識し、タイプを直接扱う事は無いからです。
		//│テーブルから参照されていないタイプを、アプリケーションが直接扱う事は出来ないので、削除して支障ありません。
		//│尚、レジストリスクリプトの最適化では、参照されていないタイプ定義は削除出来ませんので、ここで削除するのが適切です。
		//│レジストリコンパイラは、タイプ定義をアプリケーションが直接参照出来ない事を知らないので、最適化せずに残してしまうからです。
		//各テーブルについて…
		for(pTable = ght_first(ht_table, &it, &pLabel);
		    pTable;
		    pTable = ght_next( ht_table, &it, &pLabel)) {
			//各変数について…
			for(i = 0; i < pTable->pTableBody->nVar; i++) {
				pVar = pTable->pTableBody->aVar[i];
				//テーブル変数,又は,タイプ変数ならば…
				if(pVar->pTypeLabel) {
					//タイプ変数であると仮定して、タイプを取得してみる。
					pType = ght_get(ht_type,  strlen(pVar->pTypeLabel) + 1/*nul*/, pVar->pTypeLabel);
					//タイプ変数ならば、このタイプが参照されている事をマークする。
					if(pType) { pType->bUse = 1; }
				}
			}
		}
		//各タイプについて…
		for(pType = ght_first(ht_type, &it, &pLabel);
		    pType;
		    pType = ght_next( ht_type, &it, &pLabel)) {
			//このタイプが参照されていなければ削除する。
			// - イテレーションの中で当該ノードをght_remove()しても安全です。
			//   ght_hash_table.cのght_finalize()のコメントを参照して下さい。
			if(!pType->bUse) { ght_remove(ht_type, strlen(pLabel) + 1/*nul*/, pLabel); }
		}
	}
	{
		//□NOTE:
		//│テーブルの'タイプid'の範囲は0x0000～0x7FFF,タイプの'タイプid'の範囲は0x8000～0xFFFFである。
		//│clipprp.cのPrpTbl_get_set_v()が、'タイプid'の値によって、テーブルかタイプかを判断しているので、上記は必須です。
		//│尚、0x0000と0x8000を割り当てても動作上は問題ないのですが、当ツールの判断で、0x0000と0x8000は割り当てない事にしました。
		//│テーブルの'タイプid'として0を割り当てない方が、アプリケーションが都合が良いと思うからです。
		int iType, iVar, iAttr, iFmt;
		char* endptr;
		//テーブルを名前の昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
		SortList(&ht_table->le_iterator, Label_compar);
		//テーブルに一意な'タイプid'を割り当てる。
		iType = 0;
		for(pTable = ght_first(ht_table, &it, &pLabel);
		    pTable;
		    pTable = ght_next( ht_table, &it, &pLabel)) {
			if(pLabel[0] == '$') {	//組み込み型の場合
				i = strtol(pLabel + 1/*'$'*/, &endptr, 0);
				if(*endptr) { DIE(); }	//バグ
			} else {		//通常の場合
				i = ++iType;	//プレインクリメントで1から割り当てる。
			}
			if((i < 0x0000) || (i > 0x7FFF)) { error(EXIT_FAILURE, 0, "テーブルidが範囲外です。"); }	//テーブルが多過ぎる,又は,当ツールのバグ。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			pTable->iType = i;
		}
		//タイプを名前の昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
		SortList(&ht_type->le_iterator, Label_compar);
		//タイプに一意な'タイプid'を割り当てる。
		iType = 0x8000;
		for(pType = ght_first(ht_type, &it, &pLabel);
		    pType;
		    pType = ght_next( ht_type, &it, &pLabel)) {
			if(pLabel[0] == '$') {	//組み込み型の場合
				i = strtol(pLabel + 1/*'$'*/, &endptr, 0);
				if(*endptr) { DIE(); }	//バグ
			} else {		//通常の場合
				i = ++iType;	//プレインクリメントで0x8001から割り当てる。
			}
			if((i < 0x8000) || (i > 0xFFFF)) { error(EXIT_FAILURE, 0, "タイプidが範囲外です。"); }	//タイプが多過ぎる,又は,当ツールのバグ。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			pType->iType = i;
		}
		//□NOTE:
		//│'変数id'の範囲は0x0000～0xFFFFである。
		//│0x0000を割り当てても動作上は問題ないのですが、当ツールの判断で、0x0000は割り当てない事にしました。
		//│'変数id'として0を割り当てない方が、アプリケーションが都合が良いと思うからです。
		//変数の名前を昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
		SortList(&ht_var->le_iterator, Label_compar);
		//変数の名前に一意な'変数id'を割り当てる。
		iVar = 0;
		for(i/*dummy*/ = (int)ght_first(ht_var, &it, &pLabel);
		    i/*dummy*/;
		    i/*dummy*/ = (int)ght_next( ht_var, &it, &pLabel)) {
			if(i != -1/*dummy*/) { DIE(); }	//バグ
			if(pLabel[0] == '$') {	//組み込み型の場合
				i = strtol(pLabel + 1/*'$'*/, &endptr, 0);
				if(*endptr) { DIE(); }	//バグ
			} else {		//通常の場合
				i = ++iVar;	//プレインクリメントで1から割り当てる。
			}
			if((i < 0x0000) || (i > 0xFFFF)) { error(EXIT_FAILURE, 0, "変数idが範囲外です。"); }	//変数が多過ぎる,又は,当ツールのバグ。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			if((int)ght_replace(ht_var,		//■■■注意：ght_hash_table_tの実装依存の処理です。ght_replace()はハッシュの並びを変えない事に依存しています。■■■
				(void*)i,
				strlen(pLabel) + 1/*nul*/,
				pLabel) != -1/*dummy*/) { DIE(); }
		}
		//□NOTE:
		//│'属性id'の範囲は0x00～0xFFである。
		//│0x00を割り当てても動作上は問題ないのですが、当ツールの判断で、0x00は割り当てない事にしました。
		//│'属性id'として0を割り当てない方が、アプリケーションが都合が良いと思うからです。
		//属性の名前を昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
		SortList(&ht_attr->le_iterator, Label_compar);
		//属性の名前に一意な'属性id'を割り当てる。
		iAttr = 0;
		for(i/*dummy*/ = (int)ght_first(ht_attr, &it, &pLabel);
		    i/*dummy*/;
		    i/*dummy*/ = (int)ght_next( ht_attr, &it, &pLabel)) {
			if(i != -1/*dummy*/) { DIE(); }	//バグ
			if(pLabel[0] == '$') {	//組み込み型の場合												//┐
				i = strtol(pLabel + 1/*'$'*/, &endptr, 0);											//├2015/10/19現在は組み込み型の属性idは使用していないが、もし今後使用する場合のためにタイプ,変数と同じ処理にしておく事にした。
				if(*endptr) { DIE(); }	//バグ													//┘
			} else {		//通常の場合
				i = ++iAttr;	//プレインクリメントで1から割り当てる。
			}
			if((i < 0x00) || (i > 0xFF)) { error(EXIT_FAILURE, 0, "属性idが範囲外です。"); }		//変数が多過ぎる,又は,当ツールのバグ。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			if((int)ght_replace(ht_attr,		//■■■注意：ght_hash_table_tの実装依存の処理です。ght_replace()はハッシュの並びを変えない事に依存しています。■■■
				(void*)i,
				strlen(pLabel) + 1/*nul*/,
				pLabel) != -1/*dummy*/) { DIE(); }
		}
		//各テーブルの属性リストを、属性idの昇順に並び替える。
		for(pTable = ght_first(ht_table, &it, &pLabel);
		    pTable;
		    pTable = ght_next( ht_table, &it, &pLabel)) {
			//各変数について…
			for(i = 0; i < pTable->pTableBody->nVar; i++) {
				pVar = pTable->pTableBody->aVar[i];
				if(pVar->pAttrList) {
					qsort(pVar->pAttrList->aAttr,
					      pVar->pAttrList->nAttr,
					      sizeof pVar->pAttrList->aAttr[0],
					      AttrList_compar);
				}
			}
		}
		//□NOTE:
		//│'書式id'の範囲は0x0000～0xFFFFである。
		//│0x0000を割り当てても動作上は問題ないのですが、当ツールの判断で、0x0000は割り当てない事にしました。
		//│'書式id'として0を割り当てない方が、アプリケーションが都合が良いと思うからです。
		//書式の名前を昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
		SortList(&ht_fmt->le_iterator, Label_compar);
		//書式の名前に一意な'書式id'を割り当てる。
		iFmt = 0;
		for(i/*dummy*/ = (int)ght_first(ht_fmt, &it, &pLabel);
		    i/*dummy*/;
		    i/*dummy*/ = (int)ght_next( ht_fmt, &it, &pLabel)) {
			if(i != -1/*dummy*/) { DIE(); }	//バグ
			if(pLabel[0] == '$') {	//組み込み型の場合
				i = strtol(pLabel + 1/*'$'*/, &endptr, 0);
				if(*endptr) { DIE(); }	//バグ
			} else {		//通常の場合
				i = ++iFmt;	//プレインクリメントで1から割り当てる。
			}
			if((i < 0x0000) || (i > 0xFFFF)) { error(EXIT_FAILURE, 0, "書式idが範囲外です。"); }	//書式が多過ぎる,又は,当ツールのバグ。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			if((int)ght_replace(ht_fmt,		//■■■注意：ght_hash_table_tの実装依存の処理です。ght_replace()はハッシュの並びを変えない事に依存しています。■■■
				(void*)i,
				strlen(pLabel) + 1/*nul*/,
				pLabel) != -1/*dummy*/) { DIE(); }
		}
	}
	{
		//タイプの初期値とビット数を決定する。
		for(pType = ght_first(ht_type, &it, &pLabel);
		    pType;
		    pType = ght_next( ht_type, &it, &pLabel)) {
			ST_Range* pRange = pType->pTypeBody->pRange;
			ST_Enum*  pEnum  = pType->pTypeBody->pEnum;
			if(pRange && !pEnum) {
				//ビット数を決定する。
				for(i = 1; (pRange->iMax - pRange->iMin) >= (1 << i); i++) { /** no job **/ }	//(iMax-iMin)=0⇒i=1,(iMax-iMin)=1⇒i=1,(iMax-iMin)=2⇒i=2,(iMax-iMin)=3⇒i=2,(iMax-iMin)=4⇒i=3,(iMax-iMin)=5⇒i=3,(iMax-iMin)=6⇒i=3,(iMax-iMin)=7⇒i=3,(iMax-iMin)=8⇒i=4,...
				pType->iBitLen = i;
				//エンコード済みの初期値を決定する。
				i = pType->pTypeBody->pInit->iInit;
				if((i < pRange->iMin) || (i > pRange->iMax)) {
					yyerror("initに指定された値がrangeの範囲外です。(%d)", pType->pTypeBody->pInit->iInit);
					yynerrs++;	//呼び出し元(main)に知らせるために。
					//全てのセマンティクスエラーを報告するために継続する。
					i = 0;	//仮の値に変更しておく。
				}
				pType->iInit	= i - pRange->iMin;
			} else if(pEnum && !pRange) {
				//ビット数を決定する。
				for(i = 1; (pEnum->pExprList->nExpr - 1) >= (1 << i); i++) { /** no job **/ }	//nExpr=1⇒i=1,nExpr=2⇒i=1,nExpr=3⇒i=2,nExpr=4⇒i=2,nExpr=5⇒i=3,nExpr=6⇒i=3,nExpr=7⇒i=3,nExpr=8⇒i=3,nExpr=9⇒i=4,...
				pType->iBitLen = i;
				//エンコード済みの初期値を決定する。
				for(i = 0; i < pEnum->pExprList->nExpr; i++) {
					if(pEnum->pExprList->aExpr[i] == pType->pTypeBody->pInit->iInit) { break; }
				}
				if(i >= pEnum->pExprList->nExpr) {
					yyerror("initに指定された値が列挙値に含まれていません。(%d)", pType->pTypeBody->pInit->iInit);
					yynerrs++;	//呼び出し元(main)に知らせるために。
					//全てのセマンティクスエラーを報告するために継続する。
					i = 0;	//仮の値に変更しておく。
				}
				pType->iInit	= i;
			} else {
				DIE();	//バグ。TypeBody_setRange(),TypeBody_setEnum(),Type_new()でエラー検出済みであるはず。
			}
		}
	}
	for(;;) {
		//テーブルのビット数と、変数のビット位置を求める。
		// - テーブル型変数の依存関係が存在するため、全てのテーブルが処理出来るまでループする。
		char* pProgress = NULL;	//今回のループで最後に処理したtableのラベルを保持する。
		char* pPending  = NULL;	//今回のループで最後に保留したtableのラベルを保持する。
		//各テーブルについて…
		for(pTable = ght_first(ht_table, &it, &pLabel);
		    pTable;
		    pTable = ght_next( ht_table, &it, &pLabel)) {
			int iBitPos;
			if(pTable->iBitLen) { continue; }	//前回までのループで処理済みならば飛ばす。
			iBitPos = 0;
			//各変数について…
			for(i = 0; i < pTable->pTableBody->nVar; i++) {
				int iBitLen;
				pVar = pTable->pTableBody->aVar[i];
				//変数idを格納する。
				pVar->iVar = (int)ght_get(ht_var, strlen(pVar->pVarLabel) + 1/*nul*/, pVar->pVarLabel);
				if(!pVar->iVar) { DIE(); }	//バグ
				//変数のビット位置を格納する。
				pVar->iBitPos	= iBitPos;
				//テーブル変数,又は,タイプ変数ならば…
				if(pVar->pTypeLabel) {
					//変数の型(テーブル,又は,タイプ)を取得する。
					ST_Table* pTable2 = ght_get(ht_table, strlen(pVar->pTypeLabel) + 1/*nul*/, pVar->pTypeLabel);
					ST_Type* pType2   = ght_get(ht_type,  strlen(pVar->pTypeLabel) + 1/*nul*/, pVar->pTypeLabel);
					//テーブル変数ならば…
					if(pTable2 && !pType2) {
						pVar->iType = pTable2->iType;	//変数のタイプidを格納する。
						iBitLen     = pTable2->iBitLen;
						if(!iBitLen) { break; }	//ペンディング
					//タイプ変数ならば…
					} else if(pType2 && !pTable2) {
						pVar->iType = pType2->iType;	//変数のタイプidを格納する。
						iBitLen     = pType2->iBitLen;
						if(!iBitLen) { DIE(); }	//バグ。全てのタイプのビット数はこの上のブロックの処理で決定済みであるはず。
					} else {
						yyerror("テーブル(%s)の変数(%s)の型(%s)が不明です。", pTable->pLabel, pVar->pVarLabel, pVar->pTypeLabel);
						yynerrs++;	//呼び出し元(main)に知らせるために。
						//全てのセマンティクスエラーを報告するために継続する。
						iBitLen = 1;	//仮の値に変更しておく。
					}
				//'var'変数ならば…
				} else {
					iBitLen = 32;
				}
				//配列変数ならば…
				if(pVar->pDim) {
					//変数のビット数に、配列変数の要素数を乗算する。
					for(j = 0; j < pVar->pDim->nExpr; j++) {
						iBitLen *= pVar->pDim->aExpr[j];
					}
				}
				//変数のビット位置に、変数のビット数を加算する。
				iBitPos += iBitLen;
			}
			//ペンディングでなければ…
			if(i >= pTable->pTableBody->nVar) {
				//テーブルのビット数を決定する。
				pTable->iBitLen	= iBitPos;	//iBitLenを代入するのではない事に注意。
				pProgress	= pTable->pLabel;
			//ペンディングならば…
			} else {
				pPending	= pTable->pLabel;
			}
		}
		//1テーブルも保留が発生しなければ、完了する。
		if(!pPending) { break; }	//完了
		//1テーブルでも保留が発生し,かつ,1テーブルも処理出来なければ、テーブルの相互参照エラーである。
		if(!pProgress) {
			yyerror("table定義が相互参照しています。(%s)", pPending);
			yynerrs++;	//呼び出し元(main)に知らせるために。
			return;		//ここまで。
		}
	}
	//2015/06/21現在、最適化処理は有りません。
	//今後、最適化機能を追加する場合のためのプレースホルダとして、関数のみ用意して有ります。
	//現在のところは、コマンドラインオプションに'-d'を指定してもしなくても違いは有りません。
	if(bOptimize) { /** no job **/ }
}
/*---------------------------------------------------------------------------*/
//出力処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void output(char* sRegOut, int bOmitName, int bOmitDesc, int bOmitFmt) {
	ght_iterator_t it;
	char* pLabel;
	ST_Table* pTable;
	ST_Type* pType;
	ST_Var* pVar;
	ST_Fmt* pFmt;
	int i, j, iVar, iAttr, iFmt;
	//標準出力
	{
		FILE* fp = stdout;
		int iMax;
		//テーブル
		fprintf(fp, "//テーブル\n");
		iMax = 0;
		for(pTable = ght_first(ht_table, &it, &pLabel);
		    pTable;
		    pTable = ght_next( ht_table, &it, &pLabel)) {
			if(pLabel[0] == '$') { continue; }	//組み込み型のテーブル名はレジストリデータ内だけに出力しヘッダファイルには出力しない。
			fprintf(fp, "#define PrpTbl_%s %d\n", pTable->pLabel, pTable->iType);
			if(pTable->iType > iMax) { iMax = pTable->iType; }
		}
		fprintf(fp, "#define PrpTbl_Max %d\n", iMax);
		//テーブルサイズ
		fprintf(fp, "//テーブルのビット数\n");
		for(pTable = ght_first(ht_table, &it, &pLabel);
		    pTable;
		    pTable = ght_next( ht_table, &it, &pLabel)) {
			if(pLabel[0] == '$') { continue; }	//組み込み型のテーブルサイズはレジストリデータ内だけに出力しヘッダファイルには出力しない。
			fprintf(fp, "#define PrpLen_%s %d\n", pTable->pLabel, pTable->iBitLen);
		}
		//変数
		fprintf(fp, "//変数\n");
		iMax = 0;
		for(iVar = (int)ght_first(ht_var, &it, &pLabel);
		    iVar;
		    iVar = (int)ght_next( ht_var, &it, &pLabel)) {
			if(pLabel[0] == '$') { continue; }	//組み込み型の変数名はレジストリデータ内だけに出力しヘッダファイルには出力しない。
			fprintf(fp, "#define PrpVar_%s %d\n", pLabel, iVar);
			if(iVar > iMax) { iMax = iVar; }
		}
		fprintf(fp, "#define PrpVar_Max %d\n", iMax);
		//属性
		fprintf(fp, "//属性\n");
		iMax = 0;
		for(iAttr = (int)ght_first(ht_attr, &it, &pLabel);
		    iAttr;
		    iAttr = (int)ght_next( ht_attr, &it, &pLabel)) {
			if(pLabel[0] == '$') { continue; }	//組み込み型の属性名はレジストリデータ内だけに出力しヘッダファイルには出力しない。		//─2015/10/19現在は組み込み型の属性idは使用していないが、もし今後使用する場合のためにタイプ,変数と同じ処理にしておく事にした。
			fprintf(fp, "#define PrpAtr_%s %d\n", pLabel, iAttr);
			if(iAttr > iMax) { iMax = iAttr; }
		}
		fprintf(fp, "#define PrpAtr_Max %d\n", iMax);
		//書式
		fprintf(fp, "//書式\n");
		iMax = 0;
		for(iFmt = (int)ght_first(ht_fmt, &it, &pLabel);
		    iFmt;
		    iFmt = (int)ght_next( ht_fmt, &it, &pLabel)) {
			if(pLabel[0] == '$') { continue; }	//組み込み型の書式名はレジストリデータ内だけに出力しヘッダファイルには出力しない。		//─2015/12/07現在は組み込み型の書式idは使用していないが、もし今後使用する場合のためにタイプ,変数と同じ処理にしておく事にした。
			fprintf(fp, "#define PrpFmt_%s %d\n", pLabel, iFmt);
			if(iFmt > iMax) { iMax = iFmt; }
		}
		fprintf(fp, "#define PrpFmt_Max %d\n", iMax);
	}
	//標準出力,又は,レジストリスクリプトファイル
	{
		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, "%s\t\t\t\t\t#//\t%s\n", "PrpTbl", "PrpTbl");
		//テーブル
		{
			fprintf(fp, "\t#//%s\t\t\t\t#//\t\t#//%s\n", "table", "table");
			for(pTable = ght_first(ht_table, &it, &pLabel);
			    pTable;
			    pTable = ght_next( ht_table, &it, &pLabel)) {
				fprintf(fp, "\t%d\t\t\t\t#//\t\t%s\n", pTable->iType, pTable->pLabel);
	if(!bOmitName) {	fprintf(fp, "\t\t%d\t= \"%s\"\t\t#//\t\t\t%s\t= \"%s\"\n", Prp_name, pTable->pLabel, "name", pTable->pLabel);				}
	if(!bOmitDesc) {	if(*pTable->pDesc) { fprintf(fp, "\t\t%d\t= \"%s\"\t\t#//\t\t\t%s\t= \"%s\"\n", Prp_desc, pTable->pDesc, "desc", pTable->pDesc); }	}
	if(!bOmitFmt) {		if((pFmt = pTable->pFmt)) {
					if(pFmt->pFmtStr) {
						fprintf(fp, "\t\t%d\t= \"%s\"\t\t#//\t\t\t%s\t= \"%s\"\n", Prp_fmt, pFmt->pFmtStr, "fmt", pFmt->pFmtStr);
					}
					if(pFmt->pFmtLab) {
						int iFmt = (int)ght_get(ht_fmt, strlen(pFmt->pFmtLab) + 1/*nul*/, pFmt->pFmtLab);
						if(!iFmt) { DIE(); }	//バグ
						fprintf(fp, "\t\t%d\t= %d\t\t#//\t\t\t%s\t= %s\n", Prp_fmt, iFmt, "fmt", pFmt->pFmtLab);
					}
				}}
				fprintf(fp, "\t\t%d\t= %d-1\t\t#//\t\t\t%s\t= %d-1\n", Prp_len, pTable->iBitLen, "len", pTable->iBitLen);
				for(i = 0; i < pTable->pTableBody->nVar; i++) {
					pVar = pTable->pTableBody->aVar[i];
					fprintf(fp, "\t\t%d\t\t\t#//\t\t\t%s\n", pVar->iVar, pVar->pVarLabel);
	if(!bOmitName) {		fprintf(fp, "\t\t\t%d\t= \"%s\"\t#//\t\t\t\t%s\t= \"%s\"\n", Prp_name, pVar->pVarLabel, "name", pVar->pVarLabel);		}
	if(!bOmitDesc) {		if(*pVar->pDesc) { fprintf(fp, "\t\t\t%d\t= \"%s\"\t#//\t\t\t\t%s\t= \"%s\"\n", Prp_desc, pVar->pDesc, "desc", pVar->pDesc); }	}
	if(!bOmitFmt) {			if((pFmt = pVar->pFmt)) {
						if(pFmt->pFmtStr) {
							fprintf(fp, "\t\t\t%d\t= \"%s\"\t#//\t\t\t\t%s\t= \"%s\"\n", Prp_fmt, pFmt->pFmtStr, "fmt", pFmt->pFmtStr);
						}
						if(pFmt->pFmtLab) {
							int iFmt = (int)ght_get(ht_fmt, strlen(pFmt->pFmtLab) + 1/*nul*/, pFmt->pFmtLab);
							if(!iFmt) { DIE(); }	//バグ
							fprintf(fp, "\t\t\t%d\t= %d\t#//\t\t\t\t%s\t= %s\n", Prp_fmt, iFmt, "fmt", pFmt->pFmtLab);
						}
					}}
					fprintf(fp, "\t\t\t%d\t= %d\t#//\t\t\t\t%s\t= %d\n", Prp_pos, pVar->iBitPos, "pos", pVar->iBitPos);
					if(pVar->pTypeLabel) {
						fprintf(fp, "\t\t\t%d\t= %d\t#//\t\t\t\t%s\t= %s\n", Prp_type, pVar->iType, "type", pVar->pTypeLabel);
					}
					if(pVar->pDim) {
						fprintf(fp, "\t\t\t%d\t= {\t#//\t\t\t\t%s\t= {\n", Prp_dim, "dim");
						for(j = 0; j < pVar->pDim->nExpr; j++) {
							if(j < pVar->pDim->nExpr - 1) {
								fprintf(fp, "\t\t\t\t  half %d-1,\t#//\t\t\t\t\t  %d-1,\n", pVar->pDim->aExpr[j], pVar->pDim->aExpr[j]);
							} else {
								fprintf(fp, "\t\t\t\t  half %d-1\t#//\t\t\t\t\t  %d-1\n", pVar->pDim->aExpr[j], pVar->pDim->aExpr[j]);
							}
						}
						fprintf(fp, "\t\t\t\t  }\t#//\t\t\t\t\t  }\n");
					}
					if(pVar->pAttrList) {
						fprintf(fp, "\t\t\t%d\t= {\t#//\t\t\t\t%s\t= {\n", Prp_attr, "attr");
						for(j = 0; j < pVar->pAttrList->nAttr; j++) {
							char* pAttr = pVar->pAttrList->aAttr[j];
							int iAttr = (int)ght_get(ht_attr, strlen(pAttr) + 1/*nul*/, pAttr);
							if(!iAttr) { DIE(); }	//バグ
							if(j < pVar->pAttrList->nAttr - 1) {
								fprintf(fp, "\t\t\t\t  %d,\t#//\t\t\t\t\t  %s,\n", iAttr, pAttr);
							} else {
								fprintf(fp, "\t\t\t\t  %d\t#//\t\t\t\t\t  %s\n", iAttr, pAttr);
							}
						}
						fprintf(fp, "\t\t\t\t  }\t#//\t\t\t\t\t  }\n");
					}
					fprintf(fp, "\t\t%s\t\t\t#//\t\t\t%s\n", "end", "end");
				}
				fprintf(fp, "\t%s\t\t\t\t#//\t\t%s\n", "end", "end");
			}
		}
		//タイプ
		{
			fprintf(fp, "\t#//%s\t\t\t\t#//\t\t#//%s\n", "type", "type");
			for(pType = ght_first(ht_type, &it, &pLabel);
			    pType;
			    pType = ght_next( ht_type, &it, &pLabel)) {
				ST_Range* pRange = pType->pTypeBody->pRange;
				ST_Enum*  pEnum  = pType->pTypeBody->pEnum;
				fprintf(fp, "\t%d\t\t\t\t#//\t\t%s\n", pType->iType, pType->pLabel);
	if(!bOmitName) {	fprintf(fp, "\t\t%d\t= \"%s\"\t\t#//\t\t\t%s\t= \"%s\"\n", Prp_name, pType->pLabel, "name", pType->pLabel);				}
	if(!bOmitDesc) {	if(*pType->pDesc) { fprintf(fp, "\t\t%d\t= \"%s\"\t\t#//\t\t\t%s\t= \"%s\"\n", Prp_desc, pType->pDesc, "desc", pType->pDesc); }		}
	if(!bOmitFmt) {		if((pFmt = pType->pFmt)) {
					if(pFmt->pFmtStr) {
						fprintf(fp, "\t\t%d\t= \"%s\"\t\t#//\t\t\t%s\t= \"%s\"\n", Prp_fmt, pFmt->pFmtStr, "fmt", pFmt->pFmtStr);
					}
					if(pFmt->pFmtLab) {
						int iFmt = (int)ght_get(ht_fmt, strlen(pFmt->pFmtLab) + 1/*nul*/, pFmt->pFmtLab);
						if(!iFmt) { DIE(); }	//バグ
						fprintf(fp, "\t\t%d\t= %d\t\t#//\t\t\t%s\t= %s\n", Prp_fmt, iFmt, "fmt", pFmt->pFmtLab);
					}
				}}
				fprintf(fp, "\t\t%d\t= %d-1\t\t#//\t\t\t%s\t= %d-1\n", Prp_len, pType->iBitLen, "len", pType->iBitLen);
				if(pRange && !pEnum) {
					fprintf(fp, "\t\t%d\t= (%d)-(%d)\t\t#//\t\t\t%s\t= (%d)-%s\n", Prp_init, pRange->iMin + pType->iInit, pRange->iMin, "init", pRange->iMin + pType->iInit, "min");
					fprintf(fp, "\t\t%d\t= {\t\t#//\t\t\t%s\t= {\n", Prp_range, "range");
					fprintf(fp, "\t\t\t  half (%d)&65535,\t\t#//\t\t\t\t  (%d)&65535,\n", pRange->iMin, pRange->iMin);
//{{2016/02/28変更:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
//					fprintf(fp, "\t\t\t  half (%d)&65535\t\t#//\t\t\t\t  (%d)&65535\n", pRange->iMax, pRange->iMax);
//↓2016/02/28変更:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
					fprintf(fp, "\t\t\t  half (%d)-(%d)\t\t#//\t\t\t\t  (%d)-(%d)\n", pRange->iMax, pRange->iMin, pRange->iMax, pRange->iMin);
//}}2016/02/28変更:range型の範囲の制限を緩和しました。変更前は、最小値,最大値共に(-32768～32767)である必要がありました。変更後は、最小値が(-32768～32767)で、最大値は(最小値+0～最小値+65535)となりました。
					fprintf(fp, "\t\t\t  }\t\t#//\t\t\t\t  }\n");
				} else if(pEnum && !pRange) {
					fprintf(fp, "\t\t%d\t= %d\t\t#//\t\t\t%s\t= index(%d)\n", Prp_init, pType->iInit, "init", pEnum->pExprList->aExpr[pType->iInit]);
					fprintf(fp, "\t\t%d\t= {\t\t#//\t\t\t%s\t= {\n", Prp_enum, "enum");
					for(i = 0; i < pEnum->pExprList->nExpr; i++) {
						if(i < pEnum->pExprList->nExpr - 1) {
							fprintf(fp, "\t\t\t  half (%d)&65535,\t\t#//\t\t\t\t  (%d)&65535,\n", pEnum->pExprList->aExpr[i], pEnum->pExprList->aExpr[i]);
						} else {
							fprintf(fp, "\t\t\t  half (%d)&65535\t\t#//\t\t\t\t  (%d)&65535\n", pEnum->pExprList->aExpr[i], pEnum->pExprList->aExpr[i]);
						}
					}
					fprintf(fp, "\t\t\t  }\t\t#//\t\t\t\t  }\n");
				} else {
					DIE();	//バグ
				}
				fprintf(fp, "\t%s\t\t\t\t#//\t\t%s\n", "end", "end");
			}
		}
		fprintf(fp, "%s\t\t\t\t\t#//\t%s\n", "end", "end");
		//レジストリスクリプトファイル名が指定されていたら…
		if(sRegOut) {
			//レジストリスクリプトファイルを閉じる。
			fclose(fp);
		}
	}
}
