/*****************************************************************************
 * 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 ST_Table* TBL_Table[UINT16_MAX];	//テーブル配列
static int nTable;			//テーブル配列の格納数
//テーブルデータ
typedef struct _ST_TableData {
	int		addr, cap, n;
	uint16_t*	data/*[cap]*/;
} ST_TableData;
static ST_TableData* TBL_TableData[UINT16_MAX];	//テーブルデータ配列
static int nTableData;			//テーブルデータ配列の格納数
//ラベル定義
static const char* TBL_LabDef[UINT16_MAX];	//ラベル定義配列
static int nLabDef;			//ラベル定義配列の格納数
//ラベル参照
static const char* TBL_LabRef[UINT16_MAX];	//ラベル参照配列。'v_num'の65537～131071の値を割り当てるために使う。	//{{抽選表idを自己参照できるように。}}
static int nLabRef;			//ラベル参照配列の格納数
//ローカルラベル
static int nAtmarkLocalLabel;			//'@'ローカルラベルの出現数		//'@@'が出現したら、'@%d'という形式の内部ローカルラベルに変換する。その後、+1する。要するに、次に出現する'@@'の番号を表していると見なせるので、'@F'からは、nAtmarkLocalLabelで参照できる。'@B'からは、(nAtmarkLocalLabel-1)で参照できる。
static const char* sDotLocalLabelPrefix = "";	//'.'ローカルラベルのプレフィクス	//'.～'が出現したら、'%s.～'という形式の内部ローカルラベルに変換する。グローバルラベルの定義が出現したら、sDotLocalLabelPrefixを更新する。結果として、グローバルラベル定義～グローバルラベル定義の間でのローカルラベルとなる。
//ラベル名のソート方法
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, int bIgnHitValDup, FILE* fp_cref);
static void output(char* sBinOut);
/*---------------------------------------------------------------------------*/
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'オプションが指定されなければ最適化を行う。
	int bIgnHitValDup = 0;	//'-i'オプションが指定されたら当選値が重複していても警告に留めエラーにしない。
	char* sBinOut = NULL;	//'-o'オプションが指定されたらバイナリ出力ファイル名を設定する。
	FILE* fp_cref = NULL;	//'-c'オプションが指定されたらクロスリファレンス出力ファイルを作成する。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "dio:c:enw::qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;		//最適化を行わない。
			break;
		case 'i':
			bIgnHitValDup = 1;	//当選値が重複していても警告に留めエラーにしない。
			break;
		case 'o':
			sBinOut = optarg;	//バイナリ出力ファイル名を設定する。
			break;
		case 'c':
			if(fp_cref) { usage(); }
			fp_cref = fopen(optarg, "w");	//クロスリファレンス出力ファイルを作成する。
			if(!fp_cref) { error(EXIT_FAILURE, errno, "%s が作成できません。", optarg); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			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();
		}
	}
	//ラベルを登録するためのハッシュテーブルを作成する。
	hcreate(UINT16_MAX);
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		init_location("-");	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
		yylex_init(stdin);
	    //{{2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
	    //	if((yyparse() != 0) || (yynerrs != 0)) { die("エラーが発生しました。\n"); }
	    //↓2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	    //}}2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
	//ファイルが指定されていたら、各ファイルを読み込む。
	} 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);
		    //{{2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
		    //	if((yyparse() != 0) || (yynerrs != 0)) { die("%s: エラーが発生しました。\n", argv[i]); }
		    //↓2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		    //}}2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=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, bIgnHitValDup, fp_cref);
	if(fp_cref) { fclose(fp_cref); }	//クロスリファレンス出力ファイルが開かれていたら閉じる。
    //{{進捗表示用
	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(sBinOut);
	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, "dLotTblC - 抽選テーブルコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015-2016 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dLotTblC [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, "  -i           当選値が重複していても警告に留めエラーにしません。\n");
	fprintf(stderr, "               このオプションを指定しなければ、エラーになります。\n");
	fprintf(stderr, "  -oファイル名 抽選表のバイナリデータを、バイナリ形式でファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、Cソース形式で標準出力へ出力します。\n");
	fprintf(stderr, "               このオプションの指定に関らず、抽選列idは常に標準出力へ出力します。\n");
	fprintf(stderr, "  -cファイル名 クロスリファレンスファイルを出力します。\n");
	fprintf(stderr, "               出力対象となるのは、参照表現が設定されている重み値(>0)のセルだけです。\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, "  ①dLotTblC.exe LotTbl.txt > LotTbl.c\n");
	fprintf(stderr, "  　LotTbl.txtをコンパイルして、LotTbl.cへ出力します。\n");
	fprintf(stderr, "  ②dLotTblC.exe -oLotTbl.bin LotTbl.txt > LotTbl.h\n");
	fprintf(stderr, "  　LotTbl.txtをコンパイルして、抽選表idをLotTbl.hへ出力します。\n");
	fprintf(stderr, "  　抽選表のバイナリデータを、LotTbl.binへ出力します。\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;
}
int cast_int_to_e10(int i) {
	int x = Exp10_Encode(i);
	if(x == -1) {
		yyerror("指数表現にエンコード出来ない値です。(%d)", i);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		x = 0;		//処理を継続するために、仮の値に置き換える。
	}
	return x;
}
int cast_dbl_to_e10(double d) {
	int i = cast_dbl_to_int(d);	//double⇒int
	int x = cast_int_to_e10(i);	//        int⇒exp10
	return x;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Table* Table_newBlocks(ST_LabelRow* labelRow, ST_Blocks* blocks) {
	ST_Table* table = calloc(1, sizeof(ST_Table));
	table->labelRow				= labelRow;
	table->blocks				= blocks;
	return table;
}
ST_Table* Table_newBody(ST_LabelRow* labelRow, ST_Body* body) {
	ST_Table* table = calloc(1, sizeof(ST_Table));
	table->labelRow				= labelRow;
	table->blocks				= Blocks_add(NULL, Block_new(calloc(1, sizeof(ST_Header)), body));
	return table;
}
ST_Blocks* Blocks_add(ST_Blocks* blocks, ST_Block* block) {
	if(!blocks) { blocks = calloc(1, sizeof(ST_Blocks)); }
	blocks->TBL_block			= realloc(blocks->TBL_block, sizeof(ST_Block*) * (blocks->n + 1));
	blocks->TBL_block[blocks->n++]		= block;
	return blocks;
}
ST_Block* Block_new(ST_Header* header, ST_Body* body) {
	ST_Block* block = calloc(1, sizeof(ST_Block));
	block->header				= header;
	block->body				= body;
	return block;
}
ST_Header* Header_add(ST_Header* header, ST_HeaderRow* headerRow) {
	if(!header) { header = calloc(1, sizeof(ST_Header)); }
	header->TBL_headerRow			= realloc(header->TBL_headerRow, sizeof(ST_HeaderRow*) * (header->n + 1));
	header->TBL_headerRow[header->n++]	= headerRow;
	return header;
}
ST_Body* Body_add(ST_Body* body, ST_BodyRow* bodyRow) {
	if(!body) { body = calloc(1, sizeof(ST_Body)); }
	body->TBL_bodyRow			= realloc(body->TBL_bodyRow, sizeof(ST_BodyRow*) * (body->n + 1));
	body->TBL_bodyRow[body->n++]		= bodyRow;
	return body;
}
ST_LabelRow* LabelRow_new(const char* label) {
	ST_LabelRow* labelRow = calloc(1, sizeof(ST_LabelRow));
	labelRow->line				= yylineno;	//行番号。エラーメッセージのためだけに使用する。
	labelRow->label				= label;
	return labelRow;
}
ST_HeaderRow* HeaderRow_new(ST_Values* colIds) {
	ST_HeaderRow* headerRow = calloc(1, sizeof(ST_HeaderRow));
	headerRow->line				= yylineno;	//行番号。エラーメッセージのためだけに使用する。
	headerRow->colIds			= colIds;
	return headerRow;
}
ST_BodyRow* BodyRow_new(int hitVal, ST_ValRefs* weights) {
	ST_BodyRow* bodyRow = calloc(1, sizeof(ST_BodyRow));
	bodyRow->line				= yylineno;	//行番号。エラーメッセージのためだけに使用する。
	bodyRow->hitVal				= hitVal;
	bodyRow->weights			= weights;
	return bodyRow;
}
ST_Values* Values_add(ST_Values* values, int value) {
	if(!values) { values = calloc(1, sizeof(ST_Values)); }
	values->TBL_value			= realloc(values->TBL_value, sizeof(int) * (values->n + 1));
	values->TBL_value[values->n++]		= value;
	return values;
}
ST_ValRefs* ValRefs_add(ST_ValRefs* valRefs, ST_ValRef valRef) {
	if(!valRefs) { valRefs = calloc(1, sizeof(ST_ValRefs)); }
	valRefs->TBL_valRef			= realloc(valRefs->TBL_valRef, sizeof(ST_ValRef) * (valRefs->n + 1));
	valRefs->TBL_valRef[valRefs->n++]	= valRef;
	return valRefs;
}
int ColId_new(int num) {
	if((unsigned)num > UINT16_MAX) {	//0～65535
		yyerror("抽選列idが範囲外です。(%d)", num);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		num = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
	}
	return num;
}
int HitVal_new(int num) {
	if((unsigned)num > UINT16_MAX) {	//0～65535
		yyerror("当選値が範囲外です。(%d)", num);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		num = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
	}
	return num;
}
int Weight_new(int num) {
	if((unsigned)num > UINT16_MAX + 1) {	//0～65536
//{{2015/03/24変更:Weight_new()のエラーメッセージが間違っていたので修正しました。
//		yyerror("当選値が範囲外です。(%d)", num);
//↓2015/03/24変更:Weight_new()のエラーメッセージが間違っていたので修正しました。
		yyerror("抽選値が範囲外です。(%d)", num);
//}}2015/03/24変更:Weight_new()のエラーメッセージが間違っていたので修正しました。
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		num = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
	}
	return num;
}
const char* LabDef_new(const char* lab) {
	int iLabDef;
    //{{ローカルラベルの処理
	if(lab[0] == '@') {
		if(lab[1] == '@') {	//'@@'
			lab = strdup_printf("@%d", nAtmarkLocalLabel++);	//内部ローカルラベルに変換する。その後、+1する。
		} else {		//'@B','@F'
			yyerror("defのラベル定義が不正です。(%s)", lab);
			yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;	//y.tab.yのlabDefの処理でYYERRORを行う。
		}
	} else if(lab[0] == '.') {
		lab = strconcat(sDotLocalLabelPrefix, lab, NULL);		//内部ローカルラベルに変換する。
	} else {
		sDotLocalLabelPrefix = lab;					//グローバルラベルの定義が出現したら、sDotLocalLabelPrefixを更新する。
	}
    //}}ローカルラベルの処理
	//既存のラベル定義に重複していないか検査する。(重複していたらエラーとする。)
	for(iLabDef = 0; iLabDef < nLabDef; iLabDef++) {
		if(!strcmp(TBL_LabDef[iLabDef], lab)) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			yyerror("ラベルが重複しています。(%s)", lab);
			yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;	//y.tab.yのlabDefの処理でYYERRORを行う。
		}
	}
	//ラベル定義を追加する。
	if(nLabDef >= UINT16_MAX) {
		yyerror("ラベル定義数が制限を超えました。(%s)", lab);
		DIE();	//継続しても無駄なので終了する。
	}
	TBL_LabDef[nLabDef++] = lab;
	return lab;
}
int LabRef_new(const char* lab) {	//{{抽選表idを自己参照できるように。}}
	int iLabRef;
    //{{ローカルラベルの処理
	if(lab[0] == '@') {
		if(lab[1] == 'B') {		//'@B'
			lab = strdup_printf("@%d", nAtmarkLocalLabel - 1);	//'play @B'や'play @B'からは、(nAtmarkLocalLabel-1)で参照できる。もし'def @@'が出現する前に@Bを参照した場合は-1になってしまうが、2パス目に未定義ラベルとしてエラー検出できるはずであり、安全です。
		} else if(lab[1] == 'F') {	//'@F'
			lab = strdup_printf("@%d", nAtmarkLocalLabel);		//'play @F'や'play @F'からは、nAtmarkLocalLabelで参照できる。もしこの後に'@@'が出現しなければ存在しない内部ローカルラベルを参照してしまうが、2パス目に未定義ラベルとしてエラー検出できるはずであり、安全です
		} else {			//'@@'
			yyerror("ラベル参照が不正です。(%s)", lab);
			yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return -1;	//y.tab.yのlabRefの処理でYYERRORを行う。
		}
	} else if(lab[0] == '.') {
		lab = strconcat(sDotLocalLabelPrefix, lab, NULL);		//内部ローカルラベルに変換する。
	}
    //}}ローカルラベルの処理
	//既存のラベル参照と同じならば、既存のラベル参照の番号を返す。
	for(iLabRef = 0; iLabRef < nLabRef; iLabRef++) {
		if(!strcmp(TBL_LabRef[iLabRef], lab)) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			return (UINT16_MAX + 2) + iLabRef;	//65537～131071=TBL_LabRef[]へのインデクス	y.tab.yの'%union'を参照して下さい。
		}
	}
	//初出のラベル参照ならば、ラベル参照を登録する。
	if(nLabRef >= UINT16_MAX) {
		yyerror("ラベル参照数が制限を超えました。(%s)", lab);
		DIE();	//継続しても無駄なので終了する。
	}
	TBL_LabRef[nLabRef] = lab;
	return (UINT16_MAX + 2) + nLabRef++;			//65537～131071=TBL_LabRef[]へのインデクス	y.tab.yの'%union'を参照して下さい。
}
void add_table(ST_Table* table) {
	const char* label = table->labelRow->label;
	//テーブルを登録する。
	if(nTable >= UINT16_MAX) {
		yyerror("テーブル数が制限を超えました。(%s)", table->labelRow->label);
		DIE();	//継続しても無駄なので終了する。
	}
	TBL_Table[nTable++] = table;
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %d %s", nTable, table->labelRow->label);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
}
/*---------------------------------------------------------------------------*/
//2パス目の処理
static int IsLocalLabel(const char* lab) {
	return (lab[0] == '@') ||	//'@'ローカルラベル('@%d')
		strchr(lab, '.');	//'.'ローカルラベル('%s.～')
}
static int LabDef_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	if(IsLocalLabel(x)) {
		if(IsLocalLabel(y)) {
			return (*fnLabCmp)(x, y);	//xもyもローカルラベルならば、文字列比較する。
		} else {
			return  1;			//xがローカルラベルでyがグローバルラベルならば、xを後ろにする。
		}
	} else {
		if(IsLocalLabel(y)) {
			return -1;			//xがグローバルラベルでyがローカルラベルならば、yを後ろにする。
		} else {
			return (*fnLabCmp)(x, y);	//xもyもグローバルラベルならば、文字列比較する。
		}
	}
}
static int BodyRow_compar(const void* _x, const void* _y) {
	ST_BodyRow* x = *(ST_BodyRow**)_x;
	ST_BodyRow* y = *(ST_BodyRow**)_y;
	if(x->hitVal < y->hitVal) { return -1; }
	if(x->hitVal > y->hitVal) { return  1; }
	                            return  0;
}
static int getLotTblNo(int num) {	//{{抽選表idを自己参照できるように。}}
	const char* label;
	int iLabDef;
	num -= (UINT16_MAX + 2);				//65537～131071=TBL_LabRef[]へのインデクス	y.tab.yの'%union'を参照して下さい。
	if((unsigned)num > (unsigned)nLabRef) { DIE(); }
	label = TBL_LabRef[num];
	for(iLabDef = 0; iLabDef < nLabDef; iLabDef++) {
		if(!strcmp(TBL_LabDef[iLabDef], label)) {	//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			return iLabDef + 1;			//抽選表idは1ベース
		}
	}
	yyerror("ラベルが定義されていません。(%s)", label);
	yynerrs++;	//呼び出し元(main)に知らせるために
	//全てのセマンティクスエラーを報告するために継続する
	return 0;						//見つからなかった場合はダミー値として0を返す事にする。このあと出力処理は行われないので値は何でも良い。
}
static void TableData_add(ST_TableData* tableData, int value) {
	if((unsigned)value > UINT16_MAX) { DIE(); }	//バグ
	while(tableData->cap < tableData->n + 1) {
		tableData->cap += 16/*調整可*/;
		tableData->data = realloc(tableData->data, sizeof(uint16_t) * tableData->cap);
	}
	tableData->data[tableData->n++] = value;
}
static ST_TableData* TableData_new() {
	ST_TableData* tableData = calloc(1, sizeof(ST_TableData));
	TableData_add(tableData, 0);	//'抽選表の列数'のプレースホルダ
	TableData_add(tableData, 0);	//'抽選列のid行数'のプレースホルダ
	return tableData;
}
static void pass2(int bOptimize, int bIgnHitValDup, FILE* fp_cref) {
    //{{抽選列idの重複チェック
	int capColIdChk = 0;
	uint16_t** TBL_ColIdChk/*[capColIdChk]*/ = NULL;
    //}}抽選列idの重複チェック
	ENTRY item;
	int iTable, iTableData;
	//判り易さのために、最初に1ハーフワードのダミーデータを格納する事にした。
	if(nTableData != 0) { DIE(); }			//ここではまだ0であるはず。
	TBL_TableData[nTableData] = TableData_new();
	if(TBL_TableData[nTableData]->n < 1) { DIE(); }	//TableData_new()が2にしているはず。
	TBL_TableData[nTableData]->n = 1;		//2⇒1に減らす。
	nTableData++;
	//ラベル定義を名前の昇順にソートする。
	qsort((void*)TBL_LabDef, nLabDef, sizeof(char*), LabDef_compar);	//キャストはconst警告抑制のため。
	//各テーブルについて…
	for(iTable = 0; iTable < nTable; iTable++) {
	    //{{抽選列idの重複チェック
		int nColIdChk = 0, iColIdChk;
	    //}}抽選列idの重複チェック
	    //{{当選値の重複チェック
		int bHitValDup = 0;	//当選値が重複していたことを示すエラー,又は,警告メッセージを、一テーブル当り一回だけ表示するためのフラグ。
	    //}}当選値の重複チェック
		ST_TableData* tableData = TableData_new();	//テーブルデータを作成する。重複無く登録するかどうかはこのループの最後に判断する。
		ST_Table* table = TBL_Table[iTable];
		ST_LabelRow* labelRow = table->labelRow;
		ST_Blocks* blocks = table->blocks;
		int iBlock, nBlock = blocks->n;
	    //{{クロスリファレンス用
		int iLotTblNo;	//このテーブルの抽選表id(1～)を格納する。クロスリファレンスにはローカルテーブルも出力するので、C言語ヘッダファイルには出力されない抽選表idも、クロスリファレンスには出現し得る事に注意せよ。
		for(iLotTblNo = 0; iLotTblNo < nLabDef; iLotTblNo++) {	//このテーブルのラベルが、ラベル定義配列の何番目かを検索する。クロスリファレンスにはローカルテーブルも出力するので、ローカルラベルの範囲も検索する。
			if(!strcmp(TBL_LabDef[iLotTblNo], labelRow->label)) {	//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
				break;	//ここまで。抽選表idは1ベースだから、1増やす必要が有る事に注意せよ。
			}
		}
		if(iLotTblNo >= nLabDef) { DIE(); }	//テーブルのラベルが、ラベル定義配列に含まれていない事は無いはず。もしここで停止したらバグです。
		iLotTblNo++;	//抽選表idは1ベースだから、1増やす。忘れないで!!
	    //}}クロスリファレンス用
	    //{{進捗表示用
		if(!opt_q) {
			newMsgLen = fprintf(stderr, "\rpass2 %d/%d %s", iTable + 1, nTable, labelRow->label);
			if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
		}
	    //}}進捗表示用
		yylineno = labelRow->line;	//yyerrorのために
		//各ブロックについて…
		for(iBlock = 0; iBlock < nBlock; iBlock++) {
			ST_Block* block = blocks->TBL_block[iBlock];
			ST_Header* header = block->header;
			int nHeaderRow = header->n;				//	'labelRow body'形式の場合(header->n=0)は(nHeaderRow=0)になる。
			int iHeaderRow;
			ST_Body* body = block->body;
			int nBodyRow = body->n;
			int iBodyRow;
			int maxColId = 1;	//このブロックの抽選列の最大の列数	'labelRow body'形式の場合(header->n=0)であるが抽選列数を1とするために初期値を1としておく。
			int iColId;
			//--- ヘッダ補完処理 ---
			//全てのブロックのヘッダ行数は、同じでなければならない。
			if(nHeaderRow != blocks->TBL_block[0]->header->n) {
				yyerror("ヘッダ行数が不正です。(%s)", labelRow->label);
				yynerrs++;	//呼び出し元(main)に知らせるために
				goto L_NextTable;	//このテーブルの処理を飛ばす。
			}
			//このブロックの抽選列の最大の列数を求める。
			for(iHeaderRow = 0; iHeaderRow < nHeaderRow; iHeaderRow++) {
				ST_HeaderRow* headerRow = header->TBL_headerRow[iHeaderRow];
				ST_Values* colIds = headerRow->colIds;
				int nColId = colIds->n;
				if(nColId > maxColId) { maxColId = nColId; }
			}
			//各ヘッダ行について…
			for(iHeaderRow = 0; iHeaderRow < nHeaderRow; iHeaderRow++) {
				ST_HeaderRow* headerRow = header->TBL_headerRow[iHeaderRow];
				ST_Values* colIds = headerRow->colIds;
				int* TBL_value = colIds->TBL_value;
				int nColId = colIds->n;
				//抽選列の列数を、最大の列数に揃える。
				if(nColId < maxColId) {
					TBL_value = realloc(TBL_value, sizeof(int) * maxColId);
				}
				//増えた部分,又は,途中の空白だった部分を、前列の値の継続と見なす。
				for(iColId = 0; iColId < maxColId; iColId++) {
					if((iColId >= nColId) || (TBL_value[iColId] == -1/*空白*/)) {
						TBL_value[iColId] = iColId ? TBL_value[iColId - 1] : 0;
					}
				}
				//最大の列数に揃えた抽選列をフィールドに書き戻す。元から最大の列数だった場合はダミー処理となる。
				colIds->TBL_value = TBL_value;
				colIds->n = maxColId;
			}
		    //{{抽選表idの自己参照を解決する処理。'抽選列idの重複チェック','当選値を昇順にソート','当選値の重複チェック'より先に行っておく必要が有る。
			{
				//各ヘッダ行について…
				for(iHeaderRow = 0; iHeaderRow < nHeaderRow; iHeaderRow++) {
					ST_HeaderRow* headerRow = header->TBL_headerRow[iHeaderRow];
					ST_Values* colIds = headerRow->colIds;
					int* TBL_value = colIds->TBL_value;
					int nColId = colIds->n;
					//各抽選列について…
					for(iColId = 0; iColId < nColId; iColId++) {
						int colId = TBL_value[iColId];
						if(colId >= (UINT16_MAX + 2)) { colId = getLotTblNo(colId); }		//{{抽選表idを自己参照できるように。}}
						TBL_value[iColId] = colId;
					}
				}
				//各ボディ行について…
				for(iBodyRow = 0; iBodyRow < nBodyRow; iBodyRow++) {
					ST_BodyRow* bodyRow = body->TBL_bodyRow[iBodyRow];
					ST_ValRefs* weights = bodyRow->weights;
					ST_ValRef* TBL_valRef = weights->TBL_valRef;
					int nWeight = weights->n;
					//各抽選列について…
					for(iColId = 0; iColId < nWeight; iColId++) {
						int weight = TBL_valRef[iColId].val;
						if(weight >= (UINT16_MAX + 2)) { weight = getLotTblNo(weight); }	//{{抽選表idを自己参照できるように。}}
						TBL_valRef[iColId].val = weight;
					}
					//当選値について…
					{
						int hitVal = bodyRow->hitVal;
						if(hitVal >= (UINT16_MAX + 2)) { hitVal = getLotTblNo(hitVal); }	//{{抽選表idを自己参照できるように。}}
						bodyRow->hitVal = hitVal;
					}
				}
			}
		    //}}抽選表idの自己参照を解決する処理。'抽選列idの重複チェック','当選値を昇順にソート','当選値の重複チェック'より先に行っておく必要が有る。
		    //{{抽選列idの重複チェック
			{
				//このブロックの抽選列の列数を追加する分(以上)、抽選列idの重複チェック用配列の容量を増やす。
				while(capColIdChk < nColIdChk + maxColId) {
					capColIdChk += 16/*調整可*/;
					TBL_ColIdChk = realloc(TBL_ColIdChk, sizeof(uint16_t*) * capColIdChk);
				}
				//このブロックの各抽選列について…
				for(iColId = 0; iColId < maxColId; iColId++) {
					//抽選列idの重複チェック用配列の末尾に、抽選列idの組合せを格納する。
					TBL_ColIdChk[nColIdChk] = malloc(sizeof(uint16_t) * nHeaderRow);
					for(iHeaderRow = 0; iHeaderRow < nHeaderRow; iHeaderRow++) {
						ST_HeaderRow* headerRow = header->TBL_headerRow[iHeaderRow];
						ST_Values* colIds = headerRow->colIds;
						int* TBL_value = colIds->TBL_value;
						TBL_ColIdChk[nColIdChk][iHeaderRow] = TBL_value[iColId];
					}
					//抽選列idの重複チェック用配列の既存の要素と、重複していない事を確認する。
					for(iColIdChk = 0; iColIdChk < nColIdChk; iColIdChk++) {
						if(!memcmp(TBL_ColIdChk[iColIdChk],
						           TBL_ColIdChk[nColIdChk], sizeof(uint16_t) * nHeaderRow)) {
							yyerror("抽選列idが重複しています。(%s)", labelRow->label);
							yynerrs++;	//呼び出し元(main)に知らせるために
							goto L_NextTable;	//このテーブルの処理を飛ばす。
						}
					}
					//抽選列idの重複チェック用配列の要素数を増やす。
					nColIdChk++;
				}
			}
		    //}}抽選列idの重複チェック
		    //{{当選値を昇順にソート
			//ボディ行を当選値の昇順にソートする。
			qsort(body->TBL_bodyRow, nBodyRow, sizeof(ST_BodyRow*), BodyRow_compar);
		    //}}当選値を昇順にソート
			//--- ボディ確認処理 ---
			//各ボディ行について…
			for(iBodyRow = 0; iBodyRow < nBodyRow; iBodyRow++) {
				ST_BodyRow* bodyRow = body->TBL_bodyRow[iBodyRow];
				ST_ValRefs* weights = bodyRow->weights;
				ST_ValRef* TBL_valRef = weights->TBL_valRef;
				int nWeight = weights->n;
				//全てのボディ行の列数は、抽選列の列数に一致していなければならない。
				if(nWeight != maxColId) {
					yyerror("ボディ列数が不正です。(%s)", labelRow->label);
					yynerrs++;	//呼び出し元(main)に知らせるために
					goto L_NextTable;	//このテーブルの処理を飛ばす。
				}
			    //{{当選値の重複チェック
				//当選値が重複していない事を確認する。当選値を昇順にソート済みなので隣接した行を検査すれば良い。
				if(iBodyRow) {	//二行目以降ならば…
					ST_BodyRow* prevRow = body->TBL_bodyRow[iBodyRow - 1];	//前行
					if(bodyRow->hitVal == prevRow->hitVal) {	//現在行と前行の当選値が同じならば…
						//当選値が重複していたことを示すエラー,又は,警告メッセージは、一テーブル当り一回だけ表示する事にした。
						//'-i'オプションを指定した時に当選値が重複していたことを示す警告が大量に表示されると煩わしいからです。
						//'-i'オプションを指定しなかった場合は、この判定を行わなくても一テーブル当り一回しか表示されないので、この判定は無駄であるが害は無い。
						if(!bHitValDup) {
							bHitValDup = 1;
							yyerror("当選値が重複しています。(%s)", labelRow->label);
						}
						//'-i'オプションが指定されていなければ…
						if(!bIgnHitValDup) {
							yynerrs++;	//呼び出し元(main)に知らせるために
							goto L_NextTable;	//このテーブルの処理を飛ばす。
						}
						//'-i'オプションが指定されていたら、警告に留めエラーにしない。
						//このテーブルも通常通りに処理する。
						//当選値が重複していても、当ツールの処理上は問題無い。
					}
				}
			    //}}当選値の重複チェック
			}
			//--- テーブルデータ追加処理 ---
			//このブロックの各抽選列について…
			for(iColId = 0; iColId < maxColId; iColId++) {
				int iHeaderRow, i, n = 0;
				//テーブルデータに、抽選列idの組合せを追加する。
				for(iHeaderRow = 0; iHeaderRow < nHeaderRow; iHeaderRow++) {
					ST_HeaderRow* headerRow = header->TBL_headerRow[iHeaderRow];
					ST_Values* colIds = headerRow->colIds;
					int colId = colIds->TBL_value[iColId];
					if(colId >= (UINT16_MAX + 2)) { DIE(); }	//既に抽選表idの自己参照を解決する処理を行った後なので、抽選表idの自己参照は残っていないはず。もし残っていたらバグです。
					TableData_add(tableData, colId);
				}
				i = tableData->n;		//'抽選列の要素数'を格納する位置を記憶しておく。
				TableData_add(tableData, 0);	//'抽選列の要素数'のプレースホルダを追加する。
				//抽選列の要素を追加する。
				for(iBodyRow = 0; iBodyRow < nBodyRow; iBodyRow++) {
					ST_BodyRow* bodyRow = body->TBL_bodyRow[iBodyRow];
					ST_ValRefs* weights = bodyRow->weights;
					int hitVal = bodyRow->hitVal;
					int weight = weights->TBL_valRef[iColId].val;
					if(hitVal >= (UINT16_MAX + 2)) { DIE(); }	//既に抽選表idの自己参照を解決する処理を行った後なので、抽選表idの自己参照は残っていないはず。もし残っていたらバグです。
					if(weight >= (UINT16_MAX + 2)) { DIE(); }	//既に抽選表idの自己参照を解決する処理を行った後なので、抽選表idの自己参照は残っていないはず。もし残っていたらバグです。
					if(weight) {	//重み値が0でなければ…
						TableData_add(tableData, hitVal);	//当選値
						TableData_add(tableData, weight - 1);	//重み値-1	(1～65536)⇒(0～65535)
						n++;
						//--- クロスリファレンス出力処理 ---
						//クロスリファレンス出力ファイルが開かれていたら、クロスリファレンスを出力する。
						// - 本来、出力処理はoutput()にまとめるべきなのですが、クロスリファレンス出力処理だけは2パス目の処理の中で出力する事にしました。
						//   output()の時点では、出力データと参照表現の関連は失われているからです。
						//   新たに出力データと参照表現の関連を保持するためのデータを追加する事も可能ではありますが、もしそうすると大幅にメモリ使用量が増えたり処理が複雑になる問題が生じます。
						//   それよりも、クロスリファレンス出力処理だけは2パス目の処理の中で出力する方が、単純で安全であると思い、そうする事にしました。
						// - 重み値が0のセルについては、クロスリファレンスを出力する必要は有りません。
						//   元々、クロスリファレンスを作成する目的は、クロスリファレンス内の全ての参照表現が指すセルを、当選回数に置き換えて、抽選結果表を作成するために利用する事が目的です。
						//   重み値が0のセルは、当選する事は有り得ないので、当選回数は0となるはずです。
						//   元々の重み値0を、書き換えずにそのまま残しておいて、当選回数0と見なせます。
						// - 重み値0のセルもクロスリファレンスに出力したい場合は、この処理を「if(weight){～}」のブロック外に移すだけで可能ではあります。
						//   しかし、もしそうすると、クロスリファレンスの量が膨大になると思うので、実用的ではないと思います。
						if(fp_cref) {
							const char* ref = weights->TBL_valRef[iColId].ref;
							if(ref) {
								//参照表現 (タブ) 抽選表id (タブ) 当選値 [ (タブ) 抽選列id [ (タブ) 抽選列id ... ] ]
								fprintf(fp_cref, "%s\t%d\t%d", ref, iLotTblNo, hitVal);
								for(iHeaderRow = 0; iHeaderRow < nHeaderRow; iHeaderRow++) {
									ST_HeaderRow* headerRow = header->TBL_headerRow[iHeaderRow];
									ST_Values* colIds = headerRow->colIds;
									int colId = colIds->TBL_value[iColId];
									if(colId >= (UINT16_MAX + 2)) { DIE(); }	//既に抽選表idの自己参照を解決する処理を行った後なので、抽選表idの自己参照は残っていないはず。もし残っていたらバグです。
									fprintf(fp_cref, "\t%d", colId);
								}
								fputc('\n', fp_cref);
							} else {
								//参照表現が設定されていないセルは、単に無視して、クロスリファレンスに出力しない事にした。
								//もしかすると、クロスリファレンス出力ファイルが開かれている場合は、参照表現が設定されていないセルが有ったらエラー停止する方が良いかも知れない。(入力ファイルにおいて、参照表現の設定漏れの恐れが有るので)
								//或いは、エラー停止しないまでも、警告程度は表示すべきか? しばらく使ってみて、検討しよう。
							    //{{※TODO:要検討
								/** no job **/
							    //│※TODO:要検討
							    //	DIE();
							    //}}※TODO:要検討
							}
						}
					}
				}
				tableData->data[i] = n;		//'抽選列の要素数'を格納する。
				tableData->data[0]++;		//'抽選表の列数'を増やす。		テーブルデータの[0]
			}
		}
		tableData->data[1] = blocks->TBL_block[0]->header->n;	//'抽選列のid行数'を格納する。	テーブルデータの[1]
		//テーブルデータを内容が重複無く登録する。
		//最適化を行わない場合はテーブルデータの内容が重複していても全て登録する。
		{
			//既存のラベル名に重複していないか検査する。
			item.key = (char*)labelRow->label;	//キャストはconst警告抑制のため。
			if(hsearch(item, FIND)) { DIE(); }	//ここでラベル名の重複を検出する事は無いはずです。もし重複していたらLabDef_new()がシンタクスエラーを生成してそのテーブルのadd_table()は呼び出されず、ラベル名が重複したテーブルは登録されていないはずだからです。もしここで止まったらバグです。
			//最適化有りならば…
			if(bOptimize) {
				//既存のテーブルデータに一致しているか検索する。(重複していたら最適化する。エラーではない。)
				for(iTableData = 0; iTableData < nTableData; iTableData++) {
					if((       TBL_TableData[iTableData]->n ==                                      tableData->n) &&
					   !memcmp(TBL_TableData[iTableData]->data, tableData->data, sizeof(uint16_t) * tableData->n)) { break; }	//一致したら(iTableData<nTableData)で抜ける。
				}                                                                  //~~~~~~~~~~~~~~~~忘れないで!!
			//最適化無しならば…
			} else {
				//既存のテーブルデータに一致しなかった事にし、テーブルデータ配列の末尾に追加する。
				iTableData = nTableData;
			}
			//既存のテーブルデータに一致したら…
			if(iTableData < nTableData) {
				tableData = TBL_TableData[iTableData];		//既存のテーブルデータを取得する。
			//既存のテーブルデータにも一致しなければ…
			} else {
				//最初にTBL_TableData[0]にダミーデータを格納しているので、TBL_TableData[nTableData-1]へのアクセスは安全。
				tableData->addr = TBL_TableData[nTableData - 1]->addr + TBL_TableData[nTableData - 1]->n;	//アドレスを計算する。
				TBL_TableData[nTableData++] = tableData;	//テーブルデータを登録する。
			}
			//ラベル名⇒テーブルデータのマップを作成する。
			item.data = tableData;
			if(!hsearch(item, ENTER)) { DIE(); }	//ハッシュテーブルはUINT16_MAXで初期化しているのでここでエラーになることは無いはず。テーブル数超過の場合はadd_table()で検出済みのはず。
		}
L_NextTable:	;
	}
}
/*---------------------------------------------------------------------------*/
//出力処理
static void output(char* sBinOut) {
	ENTRY item, *pEntry;
	int iLabDef, iTableData;
	//--- 抽選表idを標準出力に出力する ---
	for(iLabDef = 0; iLabDef < nLabDef; iLabDef++) {			//名前の昇順	抽選表id=0(ダミー)は定義しない。
		const char* label = TBL_LabDef[iLabDef];
		if(IsLocalLabel(label)) { break; }				//ローカルラベルの範囲に達したら抜ける。ローカルラベルはLabDef_compar()でTBL_LabDef[]の後ろにまとめられている。
		printf("#define LotTblNo_%s %d\n", label, iLabDef + 1);		//抽選表idは1ベース
	}
	printf("#define LotTblNo_Max %d\n", nLabDef);				//最大値
	//--- 抽選表データを出力する ---
	//バイナリ形式の場合…
	if(sBinOut) {
		FILE* fp = fopen(sBinOut, "wb");
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成できません。", sBinOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		//アドレステーブルを出力する。
		for(iLabDef = -1; iLabDef < nLabDef; iLabDef++) {		//名前の昇順	抽選表id=0(ダミー)から開始する。	ローカルラベルは'#define'には出力しないが、アドレステーブルには出力する。たとえばグローバルラベルa,bとローカルラベル@0,@1,a.1,a.2が有った場合、「#define LotTblNo_a 1」「#define LotTblNo_b 2」「#define LotTblNo_Max 6」となる。アドレステーブルにはa,b,@0,@1,a.1,a.2が全て出力される。要するに、アプリケーションが#defineを通してアクセスできる抽選表はグローバルラベルのみであるが、当選値や重み値として取得したローカルラベルの抽選表idもアドレステーブルを介して参照可能である。
			int addr;
			if(iLabDef < 0) {
    //{{2015/03/02変更:TBL_LotTblNo[0]に(LotTblNo_Max+1)を格納するようにしました。
    //				addr = 0;					//		抽選表id=0(ダミー)
    //↓2015/03/02変更:TBL_LotTblNo[0]に(LotTblNo_Max+1)を格納するようにしました。
				addr = nLabDef + 1;				//TBL_LotTblNo[0]=(LotTblNo_Max+1)	ラベルは1ベースなのでnLabDefではなく(nLabDef+1)とする事に注意せよ。
    //}}2015/03/02変更:TBL_LotTblNo[0]に(LotTblNo_Max+1)を格納するようにしました。
			} else {
				const char* label = TBL_LabDef[iLabDef];
				ST_TableData* tableData;
				item.key = (char*)label;	//キャストはconst警告抑制のため。
				if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
				tableData = pEntry->data;
				addr = tableData->addr;
			}
			fwrite(&addr, sizeof(int), 1, fp);
		}
		//テーブルデータを出力する。
		for(iTableData = 0; iTableData < nTableData; iTableData++) {
			ST_TableData* tableData = TBL_TableData[iTableData];
			//命令コードを出力する。
			int i;
			for(i = 0; i < tableData->n; i++) {
				int data = tableData->data[i];
				fwrite(&data, sizeof(unsigned short), 1, fp);
			}
		}
		fclose(fp);
	//テキスト形式の場合…
	} else {
		//アドレステーブルを出力する。
		printf("const int TBL_LotTblNo[%d]={", nLabDef + 1);
		for(iLabDef = -1; iLabDef < nLabDef; iLabDef++) {		//名前の昇順	抽選表id=0(ダミー)から開始する。	ローカルラベルは'#define'には出力しないが、アドレステーブルには出力する。たとえばグローバルラベルa,bとローカルラベル@0,@1,a.1,a.2が有った場合、「#define LotTblNo_a 1」「#define LotTblNo_b 2」「#define LotTblNo_Max 6」となる。アドレステーブルにはa,b,@0,@1,a.1,a.2が全て出力される。要するに、アプリケーションが#defineを通してアクセスできる抽選表はグローバルラベルのみであるが、当選値や重み値として取得したローカルラベルの抽選表idもアドレステーブルを介して参照可能である。
			int addr;
			if(iLabDef < 0) {
    //{{2015/03/02変更:TBL_LotTblNo[0]に(LotTblNo_Max+1)を格納するようにしました。
    //				addr = 0;					//		抽選表id=0(ダミー)
    //↓2015/03/02変更:TBL_LotTblNo[0]に(LotTblNo_Max+1)を格納するようにしました。
				addr = nLabDef + 1;				//TBL_LotTblNo[0]=(LotTblNo_Max+1)	ラベルは1ベースなのでnLabDefではなく(nLabDef+1)とする事に注意せよ。
    //}}2015/03/02変更:TBL_LotTblNo[0]に(LotTblNo_Max+1)を格納するようにしました。
			} else {
				const char* label = TBL_LabDef[iLabDef];
				ST_TableData* tableData;
				item.key = (char*)label;	//キャストはconst警告抑制のため。
				if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
				tableData = pEntry->data;
				addr = tableData->addr;
			}
			if((iLabDef & 15) == 15)     { putchar('\n'); }
			printf("0x%08X", addr);
			if(iLabDef != (nLabDef - 1)) { putchar(',' ); }
		}
		puts("};");
		//テーブルデータを出力する。
		printf("const unsigned short TBL_LotTbl[%d]={", TBL_TableData[nTableData - 1]->addr + TBL_TableData[nTableData - 1]->n);
		for(iTableData = 0; iTableData < nTableData; iTableData++) {
			ST_TableData* tableData = TBL_TableData[iTableData];
			//命令コードを出力する。
			int i;
			for(i = 0; i < tableData->n; i++) {
				int data = tableData->data[i];
				if(   tableData->addr + i       ) { putchar(',' ); }
				if(!((tableData->addr + i) & 31)) { putchar('\n'); }
				printf("0x%04X", data);
			}
		}
		printf("};\n");
	}
}
