/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"

/*---------------------------------------------------------------------------*/
#define Label_Max   50000	/* ラベル登録最大数 */		//調整可
#define Inst_Max  1000000	/* 命令登録最大数 */		//調整可
typedef struct _ST_Inst {
	uint16_t line;		/* 行番号 */			//1パス目で格納する。エラーメッセージのためだけに使用する。
	uint16_t op;		/* 命令コード */		//1パス目で格納する。goto,callの場合は、相対位置フィールドを0にしておき、2パス目で設定する。
	const char* lab;	/* 分岐先ラベル */		//goto,callの場合に、1パス目で格納する。
} ST_Inst;
ST_Inst TBL_Inst[Inst_Max];	/* 命令配列 */
int nInst;			/* 命令配列の格納数 */

/*---------------------------------------------------------------------------*/
/* ローカル関数宣言 */
__declspec(noreturn) static void usage();
static ST_Inst* add_inst();
static void pass2();

/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int opt;
#if     (YYDEBUG!=0)
//	yydebug = 1; /* nonzero means print parse trace */
#endif/*(YYDEBUG!=0)*/
	/* コマンドラインオプションを処理する。 */
	opterr = 0; /* オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。 */
	while((opt = getopt(argc, argv, "h")) != -1) {
		switch(opt) {
		default:
		case 'h': usage();
		}
	}
	/* ラベルを登録するためのハッシュテーブルを作成する。 */
	hcreate(Label_Max);
	/* 判り易さのために、命令位置0は使用しないことにした。命令位置0には、ダミーのend命令を置くことにした。 */
	inst_end();
	/* ファイルが指定されていなければ、標準入力から読み込む。 */
	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);
		}
	}
	/* 2パス目の処理を行う。 */
	pass2();
	if(yynerrs != 0) { die("エラーが発生しました。\n"); }
	return EXIT_SUCCESS;
}

/*---------------------------------------------------------------------------*/
/* 使い方を表示して終了する。 */
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dRudSeqC - パルスモーターの台形制御シーケンサコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2014 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dRudSeqC [filename ...] \n");
	fprintf(stderr, "  ・filenameを指定しなければ、標準入力から読み込みます。\n");
	fprintf(stderr, "  ・filenameは、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -h 使い方を表示します。\n");
	exit(EXIT_FAILURE);
}

/*---------------------------------------------------------------------------*/
/* 1パス目の処理 */
#define s12_Min (( int16_t)0xF800)
#define s12_Max (( int16_t)0x07FF)
#define u12_Max ((uint16_t)0x0FFF)
#define u15_Max ((uint16_t)0x7FFF)
static ST_Inst* add_inst() {
	ST_Inst* pInst;
	if((unsigned)nInst >= Inst_Max) {
		yyerror("命令数が制限を超えました。(%d)\n", Inst_Max);
		exit(EXIT_FAILURE);
	}
	pInst = &TBL_Inst[nInst++];
	pInst->line = yylineno;		/* 行番号 */
	return pInst;
}
void inst_def(const char* lab) {
	ENTRY item;
	item.key = (char*)lab;
	if(hsearch(item, FIND)) {
		yyerror("defのラベル定義が重複しています。(%s)", lab);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	item.data = (void*)nInst;
	if(!hsearch(item, ENTER)) {
		yyerror("ラベル数が制限を超えました。(%d)", Label_Max);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	printf("#define %s %d\n", lab, nInst);
}
void inst_end() {
	ST_Inst* pInst = add_inst();
	pInst->op = RudSeq_Inst_end;
}
void inst_goto(const char* lab) {
	ST_Inst* pInst = add_inst();
	pInst->op = RudSeq_Inst_goto(0);	/* 相対位置フィールドは、2パス目で設定する。 */
	pInst->lab = lab;
}
void inst_call(const char* lab) {
	ST_Inst* pInst = add_inst();
	pInst->op = RudSeq_Inst_call(0);	/* 相対位置フィールドは、2パス目で設定する。 */
	pInst->lab = lab;
}
void inst_repeat(int num) {
	ST_Inst* pInst = add_inst();
	if((unsigned)num > u12_Max) {
		yyerror("repeatの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_repeat(num);
}
void inst_speed(int num) {
	ST_Inst* pInst = add_inst();
	if((unsigned)num > u12_Max) {
		yyerror("speedの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_speed(num);
}
void inst_move_init() {
	ST_Inst* pInst = add_inst();
	pInst->op = RudSeq_Inst_move_init;
}
void inst_move_rel(int num) {
	ST_Inst* pInst = add_inst();
	if((num < s12_Min) || (num > s12_Max)) {
		yyerror("move relの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_move_rel(num);
}
void inst_move_abs(int num) {
	ST_Inst* pInst = add_inst();
	if((num < s12_Min) || (num > s12_Max)) {
		yyerror("move absの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_move_abs(num);
}
void inst_move_stop() {
	ST_Inst* pInst = add_inst();
	pInst->op = RudSeq_Inst_move_stop;
}
void inst_wait_rel(int num) {
	ST_Inst* pInst = add_inst();
	if((unsigned)num > u12_Max) {
		yyerror("wait relの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_wait_rel(num);
}
void inst_wait_abs(int num) {
	ST_Inst* pInst = add_inst();
	if((unsigned)num > u15_Max) {
		yyerror("wait absの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_wait_abs(num);
}
void inst_wait_stop() {
	ST_Inst* pInst = add_inst();
	pInst->op = RudSeq_Inst_wait_stop;
}
void inst_user(int num) {
	ST_Inst* pInst = add_inst();
	if((unsigned)num > (u12_Max - 4)) {
		yyerror("userの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->op = RudSeq_Inst_user(num);
}

/*---------------------------------------------------------------------------*/
/* 2パス目の処理 */
static void pass2() {
	int iInst, iDist;
	printf("const unsigned short TBL_RudSeq[%d]={", nInst);
	for(iInst = 0; iInst < nInst; iInst++) {
		ST_Inst* pInst = &TBL_Inst[iInst];
		yylineno = pInst->line;		/* yyerrorのために */
		/* goto,又は,callならば… */
		if(pInst->lab) {
			ENTRY item, *pEntry;
			item.key = (char*)pInst->lab;
			if(!(pEntry = hsearch(item, FIND))) {
				yyerror("goto,又は,callのラベルが未定義です。(%s)", pInst->lab);
				yynerrs++;	/* 呼び出し元(main)に知らせるために */
				/* 全てのセマンティクスエラーを報告するために継続する */
			} else {
				iDist = (int)pEntry->data - iInst;
				if((iDist < s12_Min) || (iDist > s12_Max)) {
					yyerror("goto,又は,callの距離が範囲外です。(%s)", iDist);
					yynerrs++;	/* 呼び出し元(main)に知らせるために */
					/* 全てのセマンティクスエラーを報告するために継続する */
				} else {
					if(pInst->op & u12_Max) { DIE(); } /* バグ */
					pInst->op |= (iDist & u12_Max);
				}
			}
		}
		if(iInst) { printf(","); }
		if(!(iInst & 31)) { printf("\n"); }
		printf("0x%04X", pInst->op);
	}
	printf("};\n");
}
