/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
/*---------------------------------------------------------------------------*/
static ST_KeyBody* g_keyBody;				//トップレベルのKeyBody
static char* TBL_Label[UINT16_MAX];			//名前として使用されたラベルを登録する。データとして使用されたラベルは登録しない。
static int nLabel;					//名前として使用されたラベルの登録数。
static uint32_t TBL_UsedExpr[(UINT16_MAX+1)/32];	//明示的な数値として使用されたビットに1を立てる。0のまま残った箇所にラベルの数値を割り当てる。
static uint16_t TBL_LabelExpr[UINT16_MAX];		//明示的な数値として使用されなかった数値をラベルに割り当てる。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
#define String_Max	(1<<(24-2))			//文字列データの最大数。レジストリの最大容量が(1<<24)，文字列データへの距離がが3バイト，文字列データが最短でも1バイトなので、文字列データの数がレジストリの最大容量の1/4を超える事は無い。
static char* TBL_String[String_Max];			//文字列データを重複無く登録する配列。
static int nString;					//文字列データの登録数。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{進捗表示用
static int g_nKeyBodyAll;	//最適化開始前の全キーボディ数
static int g_nKeyBodyNow;	//最適化後に生き残っているキーボディ数(進捗)
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
void yyerror(const char* fmt, ...) {
	//'-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 optimize1();
static void optimize2();
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();
		}
	}
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		yylex_init(stdin);
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { die("エラーが発生しました。\n"); }
	//ファイルが指定されていたら、各ファイルを読み込む。
	} 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);
			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { die("%s: エラーが発生しました。\n", argv[i]); }
			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, "dRegTblC - レジストリテーブルコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dRegTblC [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文字列     ラベルに、プレフィクス文字列を追加します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、プレフィクス文字列を追加しません。\n");
	fprintf(stderr, "  -oファイル名 レジストリテーブルのバイナリデータを、バイナリ形式でファイルへ出力します。\n");
	fprintf(stderr, "               このオプションを指定しなければ、Cソース形式で標準出力へ出力します。\n");
	fprintf(stderr, "               このオプションの指定に関らず、ラベルは常に標準出力へ出力します。\n");
	fprintf(stderr, "  -e           エラーログファイルを生成します。\n");
	fprintf(stderr, "               このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -h           使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①dRegTblC.exe -pRegKey_ RegTbl.csv > RegTbl.c\n");
	fprintf(stderr, "  　RegTbl.csvをコンパイルして、RegTbl.cへ出力します。\n");
	fprintf(stderr, "  　ラベルのプレフィクス文字列として、RegKey_を追加します。\n");
	fprintf(stderr, "  ②dRegTblC.exe -pRegKey_ -oRegTbl.bin RegTbl.csv > RegTbl.h\n");
	fprintf(stderr, "  　RegTbl.csvをコンパイルして、ラベルをRegTbl.hへ出力します。\n");
	fprintf(stderr, "  　レジストリテーブルのバイナリデータを、RegTbl.binへ出力します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void add_keyBody(ST_KeyBody* keyBody) {
	//トップレベルのKeyBodyを格納する。
	g_keyBody = keyBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_KeyBody* KeyBody_new() {
	ST_KeyBody* keyBody = calloc(1, sizeof(ST_KeyBody));
	return keyBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_KeyBody* KeyBody_addKey(ST_KeyBody* keyBody, ST_Key* key) {
	int iKey;
	int name1 = key->nameRow->name;
    //{{進捗表示用
	if(name1 <= UINT16_MAX) {
		newMsgLen = fprintf(stderr, "\rpass1 %d",        name1);
	} else {
		newMsgLen = fprintf(stderr, "\rpass1 %s", (char*)name1);
	}
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
    //}}進捗表示用
	//同じ階層に存在する同じ名前のキーを検索する。
	//尚、キーと値と文字列は別の名前空間と考えるので、ここでは値や文字列は検索しない。
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		int name2 = keyBody->TBL_Key[iKey]->nameRow->name;
		//スクリプト上での表現としては、同じ階層に同じ名前のキーが存在しても構わない。出力データではマージされて一つのキーとなる。
		//ただし、マージするキー同士の同じ階層に同じ名前で異なるデータの値や文字列が存在していた場合はエラーとする。(KeyBody_addValueRow(),KeyBody_addStringRow()にて判定する。)
		if((((unsigned)name1 <= UINT16_MAX) && ((unsigned)name2 <= UINT16_MAX) && (              name1 ==      name2)) ||
		   (((unsigned)name1 >  UINT16_MAX) && ((unsigned)name2 >  UINT16_MAX) && !strcmp((char*)name1, (char*)name2))) {
			int i;
			for(i = 0; i < key->keyBody->nKey; i++) {
				KeyBody_addKey(keyBody->TBL_Key[iKey]->keyBody, key->keyBody->TBL_Key[i]);	//キーをマージする。
			}
			for(i = 0; i < key->keyBody->nValueRow; i++) {
				KeyBody_addValueRow(keyBody->TBL_Key[iKey]->keyBody, key->keyBody->TBL_ValueRow[i]);	//値をマージする。
			}
			for(i = 0; i < key->keyBody->nStringRow; i++) {
				KeyBody_addStringRow(keyBody->TBL_Key[iKey]->keyBody, key->keyBody->TBL_StringRow[i]);	//文字列をマージする。
			}
			return keyBody;	//ここまで
		}
	}
	if(keyBody->nKey >= UINT16_MAX) {
		if((unsigned)name1 <= UINT16_MAX) {
			yyerror("キーの数が制限を超えました。(%d)",        name1);
		} else {
			yyerror("キーの数が制限を超えました。(%s)", (char*)name1);
		}
		DIE();	//継続しても無駄なので終了する。
	}
	keyBody->TBL_Key			= realloc(keyBody->TBL_Key, sizeof(ST_Key*) * (keyBody->nKey + 1));
	keyBody->TBL_Key[keyBody->nKey++]	= key;
	return keyBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_KeyBody* KeyBody_addValueRow(ST_KeyBody* keyBody, ST_ValueRow* valueRow) {
	int iValueRow;
	int name1 = valueRow->name;
	int data1 = valueRow->data;
    //{{進捗表示用
	if(name1 <= UINT16_MAX) {
		newMsgLen = fprintf(stderr, "\rpass1 %d",        name1);
	} else {
		newMsgLen = fprintf(stderr, "\rpass1 %s", (char*)name1);
	}
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
    //}}進捗表示用
	//同じ階層に存在する同じ名前の値を検索する。
	//尚、キーと値と文字列は別の名前空間と考えるので、ここではキーや文字列は検索しない。
	for(iValueRow = 0; iValueRow < keyBody->nValueRow; iValueRow++) {
		int name2 = keyBody->TBL_ValueRow[iValueRow]->name;
		int data2 = keyBody->TBL_ValueRow[iValueRow]->data;
		//同じ階層に同じ名前で異なるデータの値が存在してはいけない。エラーとする。
		if(((unsigned)name1 <= UINT16_MAX) && ((unsigned)name2 <= UINT16_MAX) && (              name1 ==      name2)) {
			//ただし、同じ階層に同じ名前で同じデータの値は存在していても構わない。
			//つまり単純に二回目以降の同じ(値:名前=データ)の定義は無視すれば良い。(格納してはいけない事に注意せよ)
			if((((unsigned)data1 <= UINT16_MAX) && ((unsigned)data2 <= UINT16_MAX) && (              data1 ==      data2)) ||
			   (((unsigned)data1 >  UINT16_MAX) && ((unsigned)data2 >  UINT16_MAX) && !strcmp((char*)data1, (char*)data2))) { return keyBody; }	//ここまで
			yyerror("同じ階層に同じ名前で異なるデータの値が存在してはいけません。(%d)",        name1);
			yynerrs++;	//呼び出し元(main)に知らせるために
			break;	//全てのセマンティクスエラーを報告するために継続する。ただし三個以上の重複が有る時に二回以上エラーメッセージを表示しても無駄なのでこのループは抜ける。
		}
		if(((unsigned)name1 >  UINT16_MAX) && ((unsigned)name2 >  UINT16_MAX) && !strcmp((char*)name1, (char*)name2)) {
			//ただし、同じ階層に同じ名前で同じデータの値は存在していても構わない。
			//つまり単純に二回目以降の同じ(値:名前=データ)の定義は無視すれば良い。(格納してはいけない事に注意せよ)
			if((((unsigned)data1 <= UINT16_MAX) && ((unsigned)data2 <= UINT16_MAX) && (              data1 ==      data2)) ||
			   (((unsigned)data1 >  UINT16_MAX) && ((unsigned)data2 >  UINT16_MAX) && !strcmp((char*)data1, (char*)data2))) { return keyBody; }	//ここまで
			yyerror("同じ階層に同じ名前で異なるデータの値が存在してはいけません。(%s)", (char*)name1);
			yynerrs++;	//呼び出し元(main)に知らせるために
			break;	//全てのセマンティクスエラーを報告するために継続する。ただし三個以上の重複が有る時に二回以上エラーメッセージを表示しても無駄なのでこのループは抜ける。
		}
	}
	if(keyBody->nValueRow >= UINT16_MAX) {
		if((unsigned)name1 <= UINT16_MAX) {
			yyerror("値の数が制限を超えました。(%d)",        name1);
		} else {
			yyerror("値の数が制限を超えました。(%s)", (char*)name1);
		}
		DIE();	//継続しても無駄なので終了する。
	}
	keyBody->TBL_ValueRow				= realloc(keyBody->TBL_ValueRow, sizeof(ST_ValueRow*) * (keyBody->nValueRow + 1));
	keyBody->TBL_ValueRow[keyBody->nValueRow++]	= valueRow;
	return keyBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_KeyBody* KeyBody_addStringRow(ST_KeyBody* keyBody, ST_StringRow* stringRow) {
	int iStringRow;
	int name1 = stringRow->name;
	char*  s1 = stringRow->s;
    //{{進捗表示用
	if(name1 <= UINT16_MAX) {
		newMsgLen = fprintf(stderr, "\rpass1 %d",        name1);
	} else {
		newMsgLen = fprintf(stderr, "\rpass1 %s", (char*)name1);
	}
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
    //}}進捗表示用
	//同じ階層に存在する同じ名前の文字列を検索する。
	//尚、キーと値と文字列は別の名前空間と考えるので、ここではキーや値は検索しない。
	for(iStringRow = 0; iStringRow < keyBody->nStringRow; iStringRow++) {
		int name2 = keyBody->TBL_StringRow[iStringRow]->name;
		char*  s2 = keyBody->TBL_StringRow[iStringRow]->s;
		//同じ階層に同じ名前で異なるデータの文字列が存在してはいけない。エラーとする。
		if(((unsigned)name1 <= UINT16_MAX) && ((unsigned)name2 <= UINT16_MAX) && (              name1 ==      name2)) {
			//ただし、同じ階層に同じ名前で同じデータの文字列は存在していても構わない。
			//つまり単純に二回目以降の同じ(文字列:名前=データ)の定義は無視すれば良い。(格納してはいけない事に注意せよ)
			if(s1 == s2) { return keyBody; }	//ここまで	//文字列データはインターンしてあるのでポインタ比較可能です。
			yyerror("同じ階層に同じ名前で異なるデータの文字列が存在してはいけません。(%d)",        name1);
			yynerrs++;	//呼び出し元(main)に知らせるために
			break;	//全てのセマンティクスエラーを報告するために継続する。ただし三個以上の重複が有る時に二回以上エラーメッセージを表示しても無駄なのでこのループは抜ける。
		}
		if(((unsigned)name1 >  UINT16_MAX) && ((unsigned)name2 >  UINT16_MAX) && !strcmp((char*)name1, (char*)name2)) {
			//ただし、同じ階層に同じ名前で同じデータの文字列は存在していても構わない。
			//つまり単純に二回目以降の同じ(文字列:名前=データ)の定義は無視すれば良い。(格納してはいけない事に注意せよ)
			if(s1 == s2) { return keyBody; }	//ここまで	//文字列データはインターンしてあるのでポインタ比較可能です。
			yyerror("同じ階層に同じ名前で異なるデータの文字列が存在してはいけません。(%s)", (char*)name1);
			yynerrs++;	//呼び出し元(main)に知らせるために
			break;	//全てのセマンティクスエラーを報告するために継続する。ただし三個以上の重複が有る時に二回以上エラーメッセージを表示しても無駄なのでこのループは抜ける。
		}
	}
	if(keyBody->nStringRow >= UINT16_MAX) {
		if((unsigned)name1 <= UINT16_MAX) {
			yyerror("文字列の数が制限を超えました。(%d)",        name1);
		} else {
			yyerror("文字列の数が制限を超えました。(%s)", (char*)name1);
		}
		DIE();	//継続しても無駄なので終了する。
	}
	keyBody->TBL_StringRow				= realloc(keyBody->TBL_StringRow, sizeof(ST_StringRow*) * (keyBody->nStringRow + 1));
	keyBody->TBL_StringRow[keyBody->nStringRow++]	= stringRow;
	return keyBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Key* Key_new(ST_NameRow* nameRow, ST_KeyBody* keyBody) {
	ST_Key* key = calloc(1, sizeof(ST_Key));
	key->nameRow	= nameRow;
	key->keyBody	= keyBody;
	return key;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_NameRow* NameRow_new(int name) {
	ST_NameRow* nameRow = calloc(1, sizeof(ST_NameRow));
	nameRow->line	= yylineno;	//行番号。エラーメッセージのためだけに使用する。
	nameRow->name	= name;
	return nameRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ValueRow* ValueRow_new(int name, int data) {
	ST_ValueRow* valueRow = calloc(1, sizeof(ST_ValueRow));
	valueRow->line	= yylineno;	//行番号。エラーメッセージのためだけに使用する。
	valueRow->name	= name;
	valueRow->data	= data;
	return valueRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_StringRow* StringRow_new(int name, char* s) {
	ST_StringRow* stringRow = calloc(1, sizeof(ST_StringRow));
	stringRow->line	= yylineno;	//行番号。エラーメッセージのためだけに使用する。
	stringRow->name	= name;
	stringRow->s	= s;
	return stringRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int Name_newExpr(int expr) {
	if((unsigned)expr > UINT16_MAX) {
		yyerror("名前が範囲外です。(%d)", expr);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		expr = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
	}
	//明示的な数値として使用されたビットに1を立てる。
	TBL_UsedExpr[expr/32] |= (1<<(expr%32));
	return expr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int Name_newLabel(char* label) {
	//名前として使用されたラベルを登録する。
	int iLabel;
	for(iLabel = 0; iLabel < nLabel; iLabel++) {
		if(!strcmp(TBL_Label[iLabel], label)) { break; }	//既に登録されていたら抜ける。
	}
	if(iLabel == nLabel) {	//まだ登録されていなければ…
		if(nLabel >= UINT16_MAX) {
			yyerror("ラベル定義数が制限を超えました。(%s)", label);
			DIE();	//継続しても無駄なので終了する。
		}
		TBL_Label[nLabel++] = label;
	}
	//文字列ポインタを数値にキャストして返す。
	return (int)label;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int Data_newExpr(int expr) {
	if((unsigned)expr > UINT16_MAX) {
		yyerror("データが範囲外です。(%d)", expr);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
		expr = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
	}
	//明示的な数値として使用されたビットに1を立てる。
	TBL_UsedExpr[expr/32] |= (1<<(expr%32));
	return expr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int Data_newLabel(char* label) {
	//文字列ポインタを数値にキャストして返す。
	return (int)label;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
char* String_intern(char* s) {
	//文字列データを重複無く登録する。
	int iString;
	for(iString = 0; iString < nString; iString++) {
		if(!strcmp(TBL_String[iString], s)) { break; }	//既に登録されていたら抜ける。
	}
	if(iString == nString) {	//まだ登録されていなければ…
		if(nString >= String_Max) {
			yyerror("文字列データの数が制限を超えました。(%s)", s);
			DIE();	//継続しても無駄なので終了する。
		}
		TBL_String[nString++] = s;
	}
	//TBL_String[]内の文字列ポインタを返す。's'を返さないように注意せよ。
	return TBL_String[iString];
}
/*---------------------------------------------------------------------------*/
//2パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Label_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	if(x[0] == '.') {
		if(y[0] == '.') {
			return strcmp(x, y);	//xもyもローカルラベルならば、文字列比較する。
		} else {
			return  1;		//xがローカルラベルでyがグローバルラベルならば、xを後ろにする。
		}
	} else {
		if(y[0] == '.') {
			return -1;		//xがグローバルラベルでyがローカルラベルならば、yを後ろにする。
		} else {
			return strcmp(x, y);	//xもyもグローバルラベルならば、文字列比較する。
		}
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int String_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	return strcmp(x, y);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int getLabelExpr(int label) {
	int iLabel;
	if((unsigned)label <= UINT16_MAX) { DIE(); }	//バグ
	for(iLabel = 0; iLabel < nLabel; iLabel++) {
		if(!strcmp(TBL_Label[iLabel], (char*)label)) {
			return TBL_LabelExpr[iLabel];	//見つかったらラベルに割り当てた数値を返す。
		}
	}
	return -1;	//見つからなければ(-1)を返す。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Key_compar(const void* _x, const void* _y) {
	ST_Key* x = *(ST_Key**)_x;
	ST_Key* y = *(ST_Key**)_y;
	if(x->nameRow->name < y->nameRow->name) { return -1; }
	if(x->nameRow->name > y->nameRow->name) { return  1; }
	                                        { return  0; }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int ValueRow_compar(const void* _x, const void* _y) {
	ST_ValueRow* x = *(ST_ValueRow**)_x;
	ST_ValueRow* y = *(ST_ValueRow**)_y;
	if(x->name < y->name) { return -1; }
	if(x->name > y->name) { return  1; }
	                      { return  0; }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int StringRow_compar(const void* _x, const void* _y) {
	ST_StringRow* x = *(ST_StringRow**)_x;
	ST_StringRow* y = *(ST_StringRow**)_y;
	if(x->name < y->name) { return -1; }
	if(x->name > y->name) { return  1; }
	                      { return  0; }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void resolveLabel(ST_KeyBody* keyBody) {
	int iKey, iValueRow, iStringRow;
	//--- キー ---
	//キーの名前を数値に変換する。
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		int name = keyBody->TBL_Key[iKey]->nameRow->name;
		yylineno = keyBody->TBL_Key[iKey]->nameRow->line;	//yyerrorのために
	    //{{進捗表示用
		if(name <= UINT16_MAX) {
			newMsgLen = fprintf(stderr, "\rpass2 %d",        name);
		} else {
			newMsgLen = fprintf(stderr, "\rpass2 %s", (char*)name);
		}
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	    //}}進捗表示用
		//キーの名前
		if((unsigned)name > UINT16_MAX) {
			name = getLabelExpr(name);
			if(name < 0) { DIE(); }	//名前は1パス目で登録しているはず。ここで止まったらバグ。
			keyBody->TBL_Key[iKey]->nameRow->name = name;
		}
		//サブキー(再帰)
		resolveLabel(keyBody->TBL_Key[iKey]->keyBody);
	}
	//キー配列を名前の数値の昇順にソートする。
	qsort(keyBody->TBL_Key, keyBody->nKey, sizeof(ST_Key*), Key_compar);
	//--- 値 ---
	//値の名前とデータを数値に変換する。
	for(iValueRow = 0; iValueRow < keyBody->nValueRow; iValueRow++) {
		int name = keyBody->TBL_ValueRow[iValueRow]->name;
		int data = keyBody->TBL_ValueRow[iValueRow]->data;
		yylineno = keyBody->TBL_ValueRow[iValueRow]->line;	//yyerrorのために
	    //{{進捗表示用
		if(name <= UINT16_MAX) {
			newMsgLen = fprintf(stderr, "\rpass2 %d",        name);
		} else {
			newMsgLen = fprintf(stderr, "\rpass2 %s", (char*)name);
		}
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	    //}}進捗表示用
		//値の名前
		if((unsigned)name > UINT16_MAX) {
			name = getLabelExpr(name);
			if(name < 0) { DIE(); }	//名前は1パス目で登録しているはず。ここで止まったらバグ。
			keyBody->TBL_ValueRow[iValueRow]->name = name;
		}
		//値のデータ
		if((unsigned)data > UINT16_MAX) {
			data = getLabelExpr(data);
			if(data < 0) {
				yyerror("ラベルが定義されていません。(%s)", (char*)keyBody->TBL_ValueRow[iValueRow]->data);
				yynerrs++;	//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する
				data = 0;	//処理を継続するために、範囲内の仮の値に置き換える。
			}
			keyBody->TBL_ValueRow[iValueRow]->data = data;
		}
	}
	//値の配列を名前の数値の昇順にソートする。
	qsort(keyBody->TBL_ValueRow, keyBody->nValueRow, sizeof(ST_ValueRow*), ValueRow_compar);
	//--- 文字列 ---
	//文字列の名前を数文字列に変換する。
	for(iStringRow = 0; iStringRow < keyBody->nStringRow; iStringRow++) {
		int name = keyBody->TBL_StringRow[iStringRow]->name;
		yylineno = keyBody->TBL_StringRow[iStringRow]->line;	//yyerrorのために
	    //{{進捗表示用
		if(name <= UINT16_MAX) {
			newMsgLen = fprintf(stderr, "\rpass2 %d",        name);
		} else {
			newMsgLen = fprintf(stderr, "\rpass2 %s", (char*)name);
		}
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	    //}}進捗表示用
		//文字列の名前
		if((unsigned)name > UINT16_MAX) {
			name = getLabelExpr(name);
			if(name < 0) { DIE(); }	//名前は1パス目で登録しているはず。ここで止まったらバグ。
			keyBody->TBL_StringRow[iStringRow]->name = name;
		}
	}
	//文字列の配列を名前の数値の昇順にソートする。
	qsort(keyBody->TBL_StringRow, keyBody->nStringRow, sizeof(ST_StringRow*), StringRow_compar);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void pass2(int bOptimize) {
	int iLabel, iExpr;
	//ラベル定義を名前の昇順にソートする。				//┬必須
	qsort(TBL_Label, nLabel, sizeof(char*), Label_compar);		//┘
	//文字列データを昇順にソートする。				//┬こちらは必須では無いが、デバッグのし易さのためにこうする事にした。
	qsort(TBL_String, nString, sizeof(char*), String_compar);	//┘
	//明示的な数値として使用されなかった数値をラベルに割り当てる。
	//[note]
	//│「明示的な数値として使用されなかった数値をラベルに割り当てる」ようにした理由は、偶然のバグを検出し易くするためです。
	//│厳密には、同一階層のキーの名前の数値にさえ重複しなければどんな値でも良く、全体で使用されていない数値にする必要は無いのですが、
	//│もし、値のデータとしてキーの数値を指定する使い方をしている場合に、(例:'valueName = keyName')、
	//│そういう使い方でない、普通のデータを持っている値(例:'valueName = parameter')のparameterがkeyNameに一致して、
	//│それを使ってキー参照を行い、たまたまそれが成功すると、発見しづらいバグになります。
	//│ラベルに自動的に割り当てる数値に対して、全体で使用されていない数値を割り当てれば、少しでもバグを発見し易くなると思いました。
	//│もっとも実際には、ほとんどの場合はキー名の数値は自動割当でなく、ユーザーが明示的に(外部の#define等で)指定するでしょうから、
	//│この対策が有効に働くのはごく限られたケースだとは思いますが、少なくとも害にはならないと思います。
	if(nLabel) {								//ラベルが1個以上使用されていたら…
		iLabel = 0;
		for(iExpr = /*0*/1; iExpr < UINT16_MAX; iExpr++) {		//1～UINT16_MAXの各数値について…	/*'0'はもし使用されていなくてもラベルに割り当てない事にした。判り易さのためだけであり必須の仕様ではない。*/
			if(!(TBL_UsedExpr[iExpr/32] & (1<<(iExpr%32)))) {	//この数値が明示的に使用されていなければ…
				TBL_LabelExpr[iLabel++] = iExpr;		//ラベルに数値を割り当てる。
				if(iLabel >= nLabel) { break; }			//全てのラベルに値を割り当てたら抜ける。
			}
		}
		if(iLabel < nLabel) {
			yyerror("ラベルに割り当てるための未使用の数値が不足しています。");
			DIE();	//継続しても無駄なので終了する。
		}
	}
	//キーの名前と,値名前と,値のデータの文字列を、数値に変換して、その数値の昇順にソートする。
	resolveLabel(g_keyBody);
	//最適化を行う。
	if(bOptimize) {
		//■最適化処理1
		//ルートのキーボディから階層を辿って、空のキー(値もキーも無いキー)を削除する。
		// - ただし、ルートのキーボディ自身は空になっても削除しない。
		//   ルートのキーボディを削除すると、出力処理が行えないから。
		optimize1();
		//■最適化処理2
		//同じ内容のキーボディを統合する。
		// - 同じ内容のキーボディとは、以下の条件を満たすキーボディの事です:
		//   ・値の配列が一致する事。値の配列をバイナリ比較する事で判断できます。
		//   ・キーの配列が一致する事。キーボディはリダイレクト後のポインタで判断する必要が有るのでバイナリ比較してはいけません。
		optimize2();
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int getKeyBodyCount(int count, ST_KeyBody* keyBody) {
	int iKey;
	//最適化されたキーボディは数えない。
	if(keyBody->redir) { return count; }
	//サブキー(再帰)
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		count = getKeyBodyCount(count, keyBody->TBL_Key[iKey]->keyBody);
	}
	return count + 1/*自分自身の分*/;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void optimizeKeyBody1(ST_KeyBody* keyBody) {
	int iKey, nKey = 0;
	//各キーについて…
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		ST_Key* key = keyBody->TBL_Key[iKey];
		//このキーのキーボディを最適化する。
		optimizeKeyBody1(key->keyBody);
		//このキーのキーボディが空でなければ、登録する。
		//このキーのキーボディが空になったら、登録しない(=削除する)。
		if(key->keyBody->nKey ||	//キーか、
		   key->keyBody->nValueRow ||	//値か、
		   key->keyBody->nStringRow) {	//文字列の、少なくともどれかが1個以上有れば空ではない。
			keyBody->TBL_Key[nKey++] = key;
		}
	}
	//このキーボディのキーの数を更新する。
	keyBody->nKey = nKey;
    //{{進捗表示用
	if(keyBody->nKey ||		//キーか、
	   keyBody->nValueRow ||	//値か、
	   keyBody->nStringRow) {	//文字列の、少なくともどれかが1個以上有れば空ではない。
		g_nKeyBodyNow++;	//最適化後に生き残っているキーボディ数(進捗)を増やす
		newMsgLen = fprintf(stderr, "\rpass2 最適化1 %d/%d", g_nKeyBodyNow, g_nKeyBodyAll);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
}
//■最適化処理1について
//│'-d'オプションを指定しなければ、最適化処理1を行います。(デフォルト)
//│最適化処理1は、ルートのキーボディから階層を辿って、空のキー(値もキーも無いキー)を削除します。
//│ただし、以下の点について注意してください。
//│---
//│最適化処理1によって削除されたキーのラベルも、'#define'には出力されます。たとえば、
//││Key1
//││end
//│というキーを定義した場合、このキーは最適化によって削除されますが、'#define Key1'は出力されます。
//│このようにした理由は、最適化の影響によって'#define'に出力されるラベルが無くなると、アプリケーションプログラムがコンパイルできなくなる可能性が有るからです。
//│アプリケーションプログラムは、最適化されたキーをREG_open_key()で開こうとした時に開けない事で、最適化によって削除された事を(実行時に)判断してください。
//│---
//│最適化処理1によって削除されたキーのラベルを参照している値のデータが存在する場合、そのデータは残ります。たとえば、
//││Key1
//││  x=Key2
//││end
//││Key2
//││end
//│という値を定義した場合、出力されるデータ構造は以下のようになります。
//││#define Key1 1
//││#define Key2 2
//││1
//││ 10=2
//│Key2が削除されるが、Key2を参照している値のデータは残っている事に注意してください。
//│この場合も前述のケースと同様に、アプリケーションプログラムは、参照先のキーをREG_open_key()で開こうとした時に開けない事で、最適化によって削除された事を(実行時に)判断してください。
//│---
static void optimize1() {
    //{{進捗表示用
	g_nKeyBodyAll = getKeyBodyCount(0, g_keyBody);	//最適化開始前の全キーボディ数
	g_nKeyBodyNow = 0;				//最適化後に生き残っているキーボディ数(進捗)
	newMsgLen = fprintf(stderr, "\rpass2 最適化1 %d/%d", g_nKeyBodyNow, g_nKeyBodyAll);
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
    //}}進捗表示用
	//ルートのキーボディから階層を辿って、空のキー(値もキーも無いキー)を削除する。
	optimizeKeyBody1(g_keyBody);
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass2 最適化1 %d/%d", g_nKeyBodyNow, g_nKeyBodyAll);
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
    //}}進捗表示用
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int setKeyBodyIndex(int index, ST_KeyBody* keyBody) {
	int iKey;
	//このキーボディのインデクスを格納する。
	keyBody->index = index;
	//このキーボディの分、インデクスを進める。
	index++;
	//サブキー(再帰)
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		index = setKeyBodyIndex(index, keyBody->TBL_Key[iKey]->keyBody);
	}
	return index;
}
//このキーボディが最適化されているならば、統合先のキーボディに対して再帰する。
//このキーボディが最適化されていなければ、このキーボディ自身を返す。
static ST_KeyBody* redirKeyBody(ST_KeyBody* keyBody) {
	//リダイレクト先が有ればリダイレクト先に対して再帰する。リダイレクト先が無ければそのまま返す。
	return keyBody->redir ? redirKeyBody(keyBody->redir) : keyBody;
}
//keyBody1をルートとするツリーの中で、keyBody2と同じ内容のキーボディを検索し、有ればそのポインタを返す。無ければNULLを返す。
static ST_KeyBody* searchKeyBody(ST_KeyBody* keyBody1/*比較先*/, ST_KeyBody* keyBody2/*比較元*/) {
	int iKey, iValueRow, iStringRow;
	if(keyBody2->redir) { DIE(); }	//バグ。optimizeKeyBody2()からの呼び出しの流れで、既に最適化されたキーボディに対してこの関数は呼ばれないはずである。
	//keyBody2に対してg_keyBodyから深さ優先探索で検索して来てkeyBody2に到達したら、マッチしなかった事になるのでNULLを返す。
	if(keyBody1->index >= keyBody2->index) { return NULL; }
	//最適化されたキーボディにはマッチしないし、サブキーの検索も行わない。
	// - もし一致するならばもっと早く、最適化されたキーのリダイレクト先キーボディにマッチしているであろう。
	//   最適化は深さ優先探索で前方のキーボディに対して行われるから。
	if(keyBody1->redir) { return NULL; }
	//キーの配列が一致するか?
	if(keyBody1->nKey !=
	   keyBody2->nKey) { goto L_SEARCH_SUB_KEY; }						//このキーボディ自身に一致しなければサブキーの検索へ。
	for(iKey = 0; iKey < keyBody1->nKey; iKey++) {
		if(keyBody1->TBL_Key[iKey]->nameRow->name !=
		   keyBody2->TBL_Key[iKey]->nameRow->name) { goto L_SEARCH_SUB_KEY; }		//このキーボディ自身に一致しなければサブキーの検索へ。
		if(redirKeyBody(keyBody1->TBL_Key[iKey]->keyBody) !=											//┬キー配列の比較は比較先,比較元ともリダイレクト結果で比較する事に注意せよ。
		   redirKeyBody(keyBody2->TBL_Key[iKey]->keyBody)) { goto L_SEARCH_SUB_KEY; }	//このキーボディ自身に一致しなければサブキーの検索へ。	//┘
	}
	//値の配列が一致するか?
	if(keyBody1->nValueRow !=
	   keyBody2->nValueRow) { goto L_SEARCH_SUB_KEY; }					//このキーボディ自身に一致しなければサブキーの検索へ。
	for(iValueRow = 0; iValueRow < keyBody1->nValueRow; iValueRow++) {
		if(keyBody1->TBL_ValueRow[iValueRow]->name !=
		   keyBody2->TBL_ValueRow[iValueRow]->name) { goto L_SEARCH_SUB_KEY; }		//このキーボディ自身に一致しなければサブキーの検索へ。
		if(keyBody1->TBL_ValueRow[iValueRow]->data !=
		   keyBody2->TBL_ValueRow[iValueRow]->data) { goto L_SEARCH_SUB_KEY; }		//このキーボディ自身に一致しなければサブキーの検索へ。
	}
	//文字列の配列が一致するか?
	if(keyBody1->nStringRow !=
	   keyBody2->nStringRow) { goto L_SEARCH_SUB_KEY; }					//このキーボディ自身に一致しなければサブキーの検索へ。
	for(iStringRow = 0; iStringRow < keyBody1->nStringRow; iStringRow++) {
		if(keyBody1->TBL_StringRow[iStringRow]->name !=
		   keyBody2->TBL_StringRow[iStringRow]->name) { goto L_SEARCH_SUB_KEY; }	//このキーボディ自身に一致しなければサブキーの検索へ。
		if(keyBody1->TBL_StringRow[iStringRow]->s !=												//┬文字列データはインターンしてあるのでポインタ比較可能です。
		   keyBody2->TBL_StringRow[iStringRow]->s) { goto L_SEARCH_SUB_KEY; }		//このキーボディ自身に一致しなければサブキーの検索へ。	//┘
	}
	//keyBody1とkeyBody2の内容が一致したので、keyBody1を返す。
	return keyBody1;
L_SEARCH_SUB_KEY:
	//keyBody1とkeyBody2の内容が一致しなければ、keyBody1のサブキーを探索する。
	for(iKey = 0; iKey < keyBody1->nKey; iKey++) {
		ST_KeyBody* keyBody1sub = keyBody1->TBL_Key[iKey]->keyBody;	//keyBody1のサブキーを取得する。
		if(!keyBody1sub->redir) {	//最適化されたキーボディにはマッチしないし、サブキーの検索も行わない。もし一致するならばもっと早く、最適化されたキーのリダイレクト先キーボディにマッチしているであろう。最適化は深さ優先探索で前方のキーボディに対して行われるから。
		    //{{この判断必要!!こうしないとキー配列の1個目に対して検索した時に同じキー配列内の2個目にマッチしてしまう。正しくはキー配列の1個目自身に到達したら打ち切るのが正しい。この関数の最初の方で行っているのと同様の処理をここでも行う必要が有る。
			//keyBody2に対してg_keyBodyから深さ優先探索で検索して来てkeyBody2に到達したら、マッチしなかった事になるのでNULLを返す。
			if(keyBody1sub->index >= keyBody2->index) { return NULL; }
		    //}}この判断必要!!こうしないとキー配列の1個目に対して検索した時に同じキー配列内の2個目にマッチしてしまう。正しくはキー配列の1個目自身に到達したら打ち切るのが正しい。この関数の最初の方で行っているのと同様の処理をここでも行う必要が有る。
			keyBody1sub = searchKeyBody(keyBody1sub, keyBody2);
			if(keyBody1sub) { return keyBody1sub; }	//keyBody1sub(=keyBody1のサブキー)とkeyBody2の内容が一致したので、keyBody1sub(=keyBody1のサブキー)を返す。
		}
	}
	//keyBody1をルートとするツリーの中で、keyBody2と同じ内容のキーボディが無かったので、NULLを返す。
	return NULL;
}
static void optimizeKeyBody2(ST_KeyBody* keyBody) {
	int iKey;
	//このキーボディが既に最適化されていたら、何もしない。
	if(keyBody->redir) { return; }
	//各キーについて…
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		ST_Key* key = keyBody->TBL_Key[iKey];
		//このキーのキーボディを最適化する。
		optimizeKeyBody2(key->keyBody);
	}
	//このキーボディに一致するキーボディを検索する。有ればリダイレクト先に格納して最適化する。無ければ最適化しないままとする。
	// - ツリーを深さ優先探索して、前方に有るキーボディを検索対象とする。
	keyBody->redir = searchKeyBody(g_keyBody/*比較先*/, keyBody/*比較元*/);
	if(redirKeyBody(keyBody)->index > keyBody->index) { DIE(); }	//バグ。最適化されなければ(keyBody->redir==NULL)なので左の条件は'=='になるはず。最適化されたら左の条件は'<'になるはず。いずれにしても'>'になる事はないはずなのでもしそうなったらバグ。
    //{{進捗表示用
	if(!keyBody->redir) {		//リダイレクトされなかったら…
		g_nKeyBodyNow++;	//最適化後に生き残っているキーボディ数(進捗)を増やす
		newMsgLen = fprintf(stderr, "\rpass2 最適化2 %d/%d", g_nKeyBodyNow, g_nKeyBodyAll);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
}
//■最適化処理2について
//│'-d'オプションを指定しなければ、最適化処理1を行います。(デフォルト)
//│最適化処理2は、内容が同じキーを統合します。
//│<例>
//││Key1
//││  a=b
//││end
//││Key2
//││  Key3
//││    a=b
//││  end
//││  c=d
//││end
//│Key3の中の'a=b'は、Key1の中の'a=b'に統合されます。
//│出力データ構造の中で統合されるだけであり、統合されている事を明示的に意識する必要は有りません。
//│出力データ構造の仕様に沿って参照すれば、自動的に統合先のキーを参照する事が出来ます。
//│---
static void optimize2() {
    //{{進捗表示用
	g_nKeyBodyAll = getKeyBodyCount(0, g_keyBody);	//最適化開始前の全キーボディ数
	g_nKeyBodyNow = 0;				//最適化後に生き残っているキーボディ数(進捗)
	newMsgLen = fprintf(stderr, "\rpass2 最適化2 %d/%d", g_nKeyBodyNow, g_nKeyBodyAll);
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
    //}}進捗表示用
	//全てのキーボディに走査順のインデクスを振る。
	//最適化する際、前方(インデクスが小さい方)のキーボディにのみ一致させ、後方(インデクスが大きい方)には一致させないためです。
	setKeyBodyIndex(0, g_keyBody);
	//内容が同じキーを統合します。
	optimizeKeyBody2(g_keyBody);
    //{{進捗表示用
	newMsgLen = fprintf(stderr, "\rpass2 最適化2 %d/%d", g_nKeyBodyNow, g_nKeyBodyAll);
	if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
    //}}進捗表示用
}
/*---------------------------------------------------------------------------*/
//出力処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int setKeyBodyAddr(int addr, ST_KeyBody* keyBody) {
	int iKey;
	//最適化されたキーボディは出力しないのでアドレスも割り当てない。
	if(keyBody->redir) { return addr; }
	//このキーボディのアドレスを格納する。
	keyBody->addr = addr;
	//このキーボディのバイト数の分、アドレスを進める。
	addr += 2/*nKey*/;									//符号無し16ビットBE
	addr += keyBody->nKey * (2/*keyName*/ + 3/*keyBodyDist*/);				//符号無し16ビットBE+符号有り24ビットBE
	addr += 2/*nValue*/;									//符号無し16ビットBE
	addr += keyBody->nValueRow * (2/*valueName*/ + 2/*valueData*/);				//符号無し16ビットBE+符号無し16ビットBE
	addr += 2/*nString*/;									//符号無し16ビットBE
	addr += keyBody->nStringRow * (2/*stringName*/ + 3/*stringDist*/);			//符号無し16ビットBE+符号有り24ビットBE
	//サブキー(再帰)
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		addr = setKeyBodyAddr(addr, keyBody->TBL_Key[iKey]->keyBody);
	}
	return addr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int outputBinBE(int addr, int size, int _data, FILE* fp) {
	unsigned char data[4];
	*(int*)data = _data;
	while(--size >= 0) {			//┬ビッグエンディアン(BE)で出力する事にした。実行時にアライメントされていないバイト列から符号無し16ビット値,又は,符号有り24ビット値を組み立てる時にBEの方がインターロックが発生せずに処理できるから。(1バイト目ロード⇒2バイト目ロード⇒1バイト目シフト⇒論理和)
		fwrite(&data[size], 1, 1, fp);	//┘
		addr++;
	}
	return addr;
}
static int outputKeyBodyBin(int addr, ST_KeyBody* keyBody, FILE* fp) {
	int iKey, keyBodyDist, iValueRow, iStringRow, stringDist;
	//最適化されたキーボディは出力しない。
	if(keyBody->redir) { return addr; }
	//キーの数
	addr = outputBinBE(addr, 2, keyBody->nKey, fp);						//符号無し16ビットBE
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		ST_KeyBody* keyBodyRedir;
		//キーの名前
		addr = outputBinBE(addr, 2, keyBody->TBL_Key[iKey]->nameRow->name, fp);		//符号無し16ビットBE
		//最適化先のキーボディへの距離
		keyBodyRedir = redirKeyBody(keyBody->TBL_Key[iKey]->keyBody);
		keyBodyDist  = keyBodyRedir->addr/*最適化先のキーボディのアドレス*/ - (addr + 3)/*このフィールドの次のアドレス*/;
		if((keyBodyDist < -((1<<(24-1))-1)) ||
		   (keyBodyDist >  ((1<<(24-1))-1))) {
			yyerror("相対位置が範囲外です。");
			DIE();	//継続しても無駄なので終了する。
		}
		addr = outputBinBE(addr, 3, keyBodyDist, fp);					//符号有り24ビットBE
	}
	//値の数
	addr = outputBinBE(addr, 2, keyBody->nValueRow, fp);					//符号無し16ビットBE
	for(iValueRow = 0; iValueRow < keyBody->nValueRow; iValueRow++) {
		//値の名前
		addr = outputBinBE(addr, 2, keyBody->TBL_ValueRow[iValueRow]->name, fp);	//符号無し16ビットBE
		//値のデータ
		addr = outputBinBE(addr, 2, keyBody->TBL_ValueRow[iValueRow]->data, fp);	//符号無し16ビットBE
	}
	//文字列の数
	addr = outputBinBE(addr, 2, keyBody->nStringRow, fp);					//符号無し16ビットBE
	for(iStringRow = 0; iStringRow < keyBody->nStringRow; iStringRow++) {
		ENTRY item, *pEntry;
		//文字列の名前
		addr = outputBinBE(addr, 2, keyBody->TBL_StringRow[iStringRow]->name, fp);	//符号無し16ビットBE
		//文字列への距離
		item.key = keyBody->TBL_StringRow[iStringRow]->s;
		if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
		stringDist = (int)pEntry->data - (addr + 3)/*このフィールドの次のアドレス*/;
		if((stringDist < -((1<<(24-1))-1)) ||
		   (stringDist >  ((1<<(24-1))-1))) {
			yyerror("相対位置が範囲外です。");
			DIE();	//継続しても無駄なので終了する。
		}
		addr = outputBinBE(addr, 3, stringDist, fp);					//符号有り24ビットBE
	}
	//サブキー(再帰)
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		addr = outputKeyBodyBin(addr, keyBody->TBL_Key[iKey]->keyBody, fp);
	}
	return addr;
}
static int outputStringBin(int addr, char* s, FILE* fp) {
	int c;
	do {
		c = *s++;
		addr = outputBinBE(addr, 1, c, fp);
	} while(c);
	return addr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int outputTxtBE(int addr, int size, int _data) {
	unsigned char data[4];
	*(int*)data = _data;
	while(--size >= 0) {
		if(  addr      ) { putchar(',' ); }
		if(!(addr & 31)) { putchar('\n'); }
		printf("0x%02X", data[size]);
		addr++;
	}
	return addr;
}
static int outputKeyBodyTxt(int addr, ST_KeyBody* keyBody) {
	int iKey, keyBodyDist, iValueRow, iStringRow, stringDist;
	//最適化されたキーボディは出力しない。
	if(keyBody->redir) { return addr; }
	//キーの数
	addr = outputTxtBE(addr, 2, keyBody->nKey);						//符号無し16ビットBE
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		ST_KeyBody* keyBodyRedir;
		//キーの名前
		addr = outputTxtBE(addr, 2, keyBody->TBL_Key[iKey]->nameRow->name);		//符号無し16ビットBE
		//最適化先のキーボディへの距離
		keyBodyRedir = redirKeyBody(keyBody->TBL_Key[iKey]->keyBody);
		keyBodyDist  = keyBodyRedir->addr/*最適化先のキーボディのアドレス*/ - (addr + 3)/*このフィールドの次のアドレス*/;
		if((keyBodyDist < -((1<<(24-1))-1)) ||
		   (keyBodyDist >  ((1<<(24-1))-1))) {
			yyerror("相対位置が範囲外です。");
			DIE();	//継続しても無駄なので終了する。
		}
		addr = outputTxtBE(addr, 3, keyBodyDist);					//符号有り24ビットBE
	}
	//値の数
	addr = outputTxtBE(addr, 2, keyBody->nValueRow);					//符号無し16ビットBE
	for(iValueRow = 0; iValueRow < keyBody->nValueRow; iValueRow++) {
		//値の名前
		addr = outputTxtBE(addr, 2, keyBody->TBL_ValueRow[iValueRow]->name);		//符号無し16ビットBE
		//値のデータ
		addr = outputTxtBE(addr, 2, keyBody->TBL_ValueRow[iValueRow]->data);		//符号無し16ビットBE
	}
	//文字列の数
	addr = outputTxtBE(addr, 2, keyBody->nStringRow);					//符号無し16ビットBE
	for(iStringRow = 0; iStringRow < keyBody->nStringRow; iStringRow++) {
		ENTRY item, *pEntry;
		//文字列の名前
		addr = outputTxtBE(addr, 2, keyBody->TBL_StringRow[iStringRow]->name);		//符号無し16ビットBE
		//文字列への距離
		item.key = keyBody->TBL_StringRow[iStringRow]->s;
		if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
		stringDist = (int)pEntry->data - (addr + 3)/*このフィールドの次のアドレス*/;
		if((stringDist < -((1<<(24-1))-1)) ||
		   (stringDist >  ((1<<(24-1))-1))) {
			yyerror("相対位置が範囲外です。");
			DIE();	//継続しても無駄なので終了する。
		}
		addr = outputTxtBE(addr, 3, stringDist);					//符号有り24ビットBE
	}
	//サブキー(再帰)
	for(iKey = 0; iKey < keyBody->nKey; iKey++) {
		addr = outputKeyBodyTxt(addr, keyBody->TBL_Key[iKey]->keyBody);
	}
	return addr;
}
static int outputStringTxt(int addr, char* s) {
	int c;
	do {
		c = *s++;
		addr = outputTxtBE(addr, 1, c);
	} while(c);
	return addr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void output(char* sPrefix, char* sBinOut) {
	int addr, iLabel, iString;
	//--- ラベルを標準出力に出力する ---
	for(iLabel = 0; iLabel < nLabel; iLabel++) {	//名前の昇順
		char* label = TBL_Label[iLabel];
		if(label[0] != '.') {	//ローカルラベルは出力しない。
			printf("#define %s%s %d\n", sPrefix, label, TBL_LabelExpr[iLabel]);
		}
	}
	//--- キーボディのアドレスを設定する ---
	addr = setKeyBodyAddr(0, g_keyBody);
	//--- 文字列データのアドレスを設定する ---
	if(nString) {	//hcreate(0)はエラーになるので、この判断必要。
		hcreate(nString);
		for(iString = 0; iString < nString; iString++) {
			ENTRY item;
			item.key  = TBL_String[iString];
			item.data = (void*)addr;
			if(!hsearch(item, ENTER)) { DIE(); }	//バグ
			addr += (strlen(TBL_String[iString]) + 1/*nul*/);
		}
	}
	//--- データを出力する ---
	//バイナリ形式の場合…
	if(sBinOut) {
		FILE* fp = fopen(sBinOut, "wb");
		if(!fp) { die("%s が作成できません。\n", sBinOut); }
		addr = outputKeyBodyBin(0, g_keyBody, fp);
		for(iString = 0; iString < nString; iString++) {
			addr = outputStringBin(addr, TBL_String[iString], fp);
		}
		fclose(fp);
	//テキスト形式の場合…
	} else {
		printf("const unsigned char TBL_RegTbl[%d]={", addr);
		addr = outputKeyBodyTxt(0, g_keyBody);
		for(iString = 0; iString < nString; iString++) {
			addr = outputStringTxt(addr, TBL_String[iString]);
		}
		puts("};");
	}
}
