/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
/*---------------------------------------------------------------------------*/
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 char* TBL_LabDef[UINT16_MAX];	//ラベル定義配列
static int nLabDef;		//ラベル定義配列の格納数
//ラベル参照
static char* TBL_LabRef[UINT16_MAX];	//ラベル参照配列。'v_num'の65537～131071の値を割り当てるために使う。	//{{抽選表idを自己参照できるように。}}
static int nLabRef;		//ラベル参照配列の格納数
//ローカルラベル
static int nAtmarkLocalLabel;		//'@'ローカルラベルの出現数		//'@@'が出現したら、'@%d'という形式の内部ローカルラベルに変換する。その後、+1する。要するに、次に出現する'@@'の番号を表していると見なせるので、'@F'からは、nAtmarkLocalLabelで参照できる。'@B'からは、(nAtmarkLocalLabel-1)で参照できる。
static char* sDotLocalLabelPrefix = "";	//'.'ローカルラベルのプレフィクス	//'.～'が出現したら、'%s.～'という形式の内部ローカルラベルに変換する。グローバルラベルの定義が出現したら、sDotLocalLabelPrefixを更新する。結果として、グローバルラベル定義～グローバルラベル定義の間でのローカルラベルとなる。
//{{進捗表示用
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
void yyerror(const char* fmt, ...) {	//←{{2015/03/06:'-e'オプション対応のためにwinapp.cから移動しました。}}
	//'-e'オプションが指定されていて、最初のエラーログを記録する時ならば…
	if(*err_path && !fp_err) {
		//エラーログファイルを作成する。
		fp_err = fopen(err_path, "w");
		if(!fp_err) { die("%s が作成できません。\n", err_path); }
		//エラーログファイルの出力ストリームは、バッファリング無しとする。
		// - die()で終了した時(abort()時)に、バッファ分がフラッシュされないようなので、こうしておく必要が有る。
		//   別の案として、この下の処理で毎回fflush()する方法も有るが、バッファリング無しとする方が簡単だろう。
		setbuf(fp_err, NULL);
	}
	//エラーメッセージ(,及び,エラーログ)を出力する。
	             fprintf(stderr, "### error at line %d : ", yylineno);	//エラーメッセージ
	if(fp_err) { fprintf(fp_err, "### error at line %d : ", yylineno); }	//エラーログ
	{
		va_list ap;
		va_start(ap, fmt);
		             vfprintf(stderr, fmt, ap);		//エラーメッセージ
		if(fp_err) { vfprintf(fp_err, fmt, ap); }	//エラーログ
		va_end(ap);
	}
	             fputc('\n', stderr);	//エラーメッセージ
	if(fp_err) { fputc('\n', fp_err); }	//エラーログ
}
/*---------------------------------------------------------------------------*/
/* ローカル関数宣言 */
__declspec(noreturn) static void usage();
static void pass2(int bOptimize);
static void output(char* sPrefix, char* sBinOut);
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int opt, bOptimize = 1;	//'-d'オプションが指定されなければ最適化を行う。
	char* sPrefix = "";	//'-p'オプションが指定されたらプレフィクス文字列を設定する。
	char* sBinOut = NULL;	//'-o'オプションが指定されたらバイナリ出力ファイル名を設定する。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "dp:o:eh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;		//最適化を行わない。
			break;
		case 'p':
			sPrefix = optarg;	//プレフィクス文字列を設定する。
			break;
		case 'o':
			sBinOut = 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 'h':
		default:
			usage();
		}
	}
	//ラベルを登録するためのハッシュテーブルを作成する。
	hcreate(UINT16_MAX);
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		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)*/) { die("エラーが発生しました。\n"); }
	    //}}2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { die("%s が開けません。\n", argv[i]); }
			yylex_init(fp);
		    //{{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)*/) { die("%s: エラーが発生しました。\n", argv[i]); }
		    //}}2015/02/22修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
			fclose(fp);
		}
	}
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass1 ok");
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass2 ok");
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
    //}}進捗表示用
	if(yynerrs != 0) { die("エラーが発生しました。\n"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。
	//--- 出力処理 ---
	//出力処理を行う。
	output(sPrefix, sBinOut);
	if(yynerrs != 0) { die("エラーが発生しました。\n"); }
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dLotTblC - 抽選テーブルコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015 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, "  -p文字列     抽選表idに、プレフィクス文字列を追加します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、プレフィクス文字列を追加しません。\n");
	fprintf(stderr, "  -oファイル名 抽選表のバイナリデータを、バイナリ形式でファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、Cソース形式で標準出力へ出力します。\n");
	fprintf(stderr, "               このオプションの指定に関らず、抽選列idは常に標準出力へ出力します。\n");
	fprintf(stderr, "  -e           エラーログファイルを生成します。\n");
	fprintf(stderr, "               このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -h           使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①dLotTblC.exe -pLotTblNo_ LotTbl.csv > LotTbl.c\n");
	fprintf(stderr, "  　LotTbl.csvをコンパイルして、LotTbl.cへ出力します。\n");
	fprintf(stderr, "  　抽選表idのプレフィクス文字列として、LotTblNo_を追加します。\n");
	fprintf(stderr, "  ②dLotTblC.exe -pLotTblNo_ -oLotTbl.bin LotTbl.csv > LotTbl.h\n");
	fprintf(stderr, "  　LotTbl.csvをコンパイルして、抽選表idをLotTbl.hへ出力します。\n");
	fprintf(stderr, "  　抽選表のバイナリデータを、LotTbl.binへ出力します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
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(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_Values* 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;
}
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
		yyerror("当選値が範囲外です。(%d)", num);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		num = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
	}
	return num;
}
char* LabDef_new(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)) {
			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(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)) {
			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) {
	char* label = table->labelRow->label;
	//テーブルを登録する。
	if(nTable >= UINT16_MAX) {
		yyerror("テーブル数が制限を超えました。(%s)", table->labelRow->label);
		DIE();	//継続しても無駄なので終了する。
	}
	TBL_Table[nTable++] = table;
    //{{進捗表示用
	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 strcmp(x, y);	//xもyもローカルラベルならば、文字列比較する。
		} else {
			return  1;		//xがローカルラベルでyがグローバルラベルならば、xを後ろにする。
		}
	} else {
		if(IsLocalLabel(y)) {
			return -1;		//xがグローバルラベルでyがローカルラベルならば、yを後ろにする。
		} else {
			return strcmp(x, y);	//xもyもグローバルラベルならば、文字列比較する。
		}
	}
}
static int getLotTblNo(int num) {	//{{抽選表idを自己参照できるように。}}
	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)) {
			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) {
    //{{抽選列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(TBL_LabDef, nLabDef, sizeof(char*), LabDef_compar);
	//各テーブルについて…
	for(iTable = 0; iTable < nTable; iTable++) {
	    //{{抽選列idの重複チェック
		int nColIdChk = 0, iColIdChk;
	    //}}抽選列idの重複チェック
		ST_TableData* tableData = TableData_new();	//テーブルデータを作成する。重複無く登録するかどうかはこのループの最後に判断する。
		ST_Table* table = TBL_Table[iTable];
		ST_LabelRow* labelRow = table->labelRow;
		ST_Blocks* blocks = table->blocks;
		int nBlock = blocks->n;
		int iBlock;
	    //{{進捗表示用
		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の重複チェック用配列の容量を増やす。
				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の重複チェック
			//--- ボディ確認処理 ---
			//各ボディ行について…
			for(iBodyRow = 0; iBodyRow < nBodyRow; iBodyRow++) {
				ST_BodyRow* bodyRow = body->TBL_bodyRow[iBodyRow];
				ST_Values* weights = bodyRow->weights;
				int* TBL_value = weights->TBL_value;
				int nWeight = weights->n;
				//全てのボディ行の列数は、抽選列の列数に一致していなければならない。
				if(nWeight != maxColId) {
					yyerror("ボディ列数が不正です。(%s)", labelRow->label);
					yynerrs++;	//呼び出し元(main)に知らせるために
					goto L_NextTable;	//このテーブルの処理を飛ばす。
				}
			}
			//--- テーブルデータ追加処理 ---
			//このブロックの各抽選列について…
			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)) { colId = getLotTblNo(colId); }		//{{抽選表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_Values* weights = bodyRow->weights;
					int hitVal = bodyRow->hitVal;
					int weight = weights->TBL_value[iColId];
					if(hitVal >= (UINT16_MAX + 2)) { hitVal = getLotTblNo(hitVal); }	//{{抽選表idを自己参照できるように。}}
					if(weight >= (UINT16_MAX + 2)) { weight = getLotTblNo(weight); }	//{{抽選表idを自己参照できるように。}}
					if(weight) {	//重み値が0でなければ…
						TableData_add(tableData, hitVal);	//当選値
						TableData_add(tableData, weight - 1);	//重み値-1	(1～65536)⇒(0～65535)
						n++;
					}
				}
				tableData->data[i] = n;		//'抽選列の要素数'を格納する。
				tableData->data[0]++;		//'抽選表の列数'を増やす。		テーブルデータの[0]
			}
		}
		tableData->data[1] = blocks->TBL_block[0]->header->n;	//'抽選列のid行数'を格納する。	テーブルデータの[1]
		//テーブルデータを内容が重複無く登録する。
		//最適化を行わない場合はテーブルデータの内容が重複していても全て登録する。
		{
			//既存のラベル名に重複していないか検査する。
			item.key = labelRow->label;
			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* sPrefix, char* sBinOut) {
	ENTRY item, *pEntry;
	int iLabDef, iTableData;
	//--- 抽選表idを標準出力に出力する ---
	for(iLabDef = 0; iLabDef < nLabDef; iLabDef++) {			//名前の昇順	抽選表id=0(ダミー)は定義しない。
		char* label = TBL_LabDef[iLabDef];
		if(IsLocalLabel(label)) { break; }				//ローカルラベルの範囲に達したら抜ける。ローカルラベルはLabDef_compar()でTBL_LabDef[]の後ろにまとめられている。
		printf("#define %s%s %d\n", sPrefix, label, iLabDef + 1);	//抽選表idは1ベース
	}
	if(*sPrefix) { printf("#define %sMax %d\n", sPrefix, nLabDef); }	//プレフィクス文字列が有る場合のみ最大値を定義する。(プレフィクス文字列が無ければ定義しない。'Max'という単純なマクロ定義になり問題が有るから。)
	//--- 抽選表データを出力する ---
	//バイナリ形式の場合…
	if(sBinOut) {
		FILE* fp = fopen(sBinOut, "wb");
		if(!fp) { die("%s が作成できません。\n", sBinOut); }
		//アドレステーブルを出力する。
		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 {
				char* label = TBL_LabDef[iLabDef];
				ST_TableData* tableData;
				item.key = label;
				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 {
				char* label = TBL_LabDef[iLabDef];
				ST_TableData* tableData;
				item.key = label;
				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");
	}
}
