%{
/*{{定型コード:y.tab.y(宣言部,%{〜%}内)======================================*/
#ifdef  PIECE
#include "app.h"
#else //PIECE
#include "winapp.h"
#pragma warning(default: 4131)	//{{2016/03/01追加:clipstd.hにてC4131の警告レベルを引き上げましたが、Bison/Flexの生成コードには旧スタイルの宣言が使われているので、Bison/Flexの生成コードのみ警告レベルを元に戻しました。}}
#endif//PIECE
/* パーサースタックの初期値を定義します。
 * sizeof(YYSTYPE)*YYINITDEPTH) がスタック上に確保されます。
 * デフォルトでは200と定義されており、P/ECEには大きすぎます。
 * P/ECEに適切な、小さなサイズ(16)に、再定義しました。
 * パーサースタックが不足した場合は、自動的に拡張されるので、問題ありません。
 */
#define YYINITDEPTH 16
///* エラー報告関数を定義します。 */	→{{2015/03/06:'-e'オプション対応のためにwinapp.cへ移動しました。}}
//void yyerror(const char* fmt, ...) {
//  fprintf(stderr, "### error at line %d : ", yylineno);
//  { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); }
//  fputc('\n', stderr); }
/*}}定型コード:y.tab.y(宣言部,%{〜%}内)======================================*/
//{{アプリケーション固有の変数
//□テーブル全体の抽選値(weight)を、自動的に指数表現に変換して格納します(ラベル参照は除く)。
//│テーブル全体の抽選値に対して個別に、キャスト演算子'(exp10)'を明示的に指定するのと同じ結果になります。
static int bTblExp;
//□テーブル全体の抽選値(weight)を、自動的にnTblMul倍して格納します(ラベル参照は除く)。
//│テーブル全体の抽選値に対して個別に、乗算式'(〜)*nTblMul'を明示的に指定するのと同じ結果になります。(※注1)
//│指数表現と併用した場合は、nTblMul倍した結果を指数表現に変換して格納します。
//│nTblMul倍した結果が65536を超えたり、指数表現にエンコード出来ない値になった場合は、Weight_new()にてエラーを検出します。
//│(※注1)
//││ただし厳密には、元の値が小数だった場合、nTblMulによる乗算は'((int)dblVal)*nTblMul'に相当し、上記の乗算式は'(int)(dblVal*nTblMul)'に相当するという、違いが生じる事に注意して下さい。
//││<例>
//││:Test1 * 3
//││1, 2.5		#//'((int)2.5)*3'に相当し、6となります。
//││:Test2
//││1, 2.5 * 3	#//'(int)(2.5*3)'に相当し、7となります。
//│nTblMulには、式を指定する事も出来ます。
//│ただし、先頭の'*'は式の一部ではなく、あくまでもキーワードである事に注意して下さい。
//│例えば、':Test3*2+3'と指定した場合、抽選値を2倍して3を足すのではなく、抽選値を5倍します。
//│要するに、上記の指定は':Test3*(2+3)'と解釈されます。
static int nTblMul;
//}}アプリケーション固有の変数
%}

%union {
	int		v_num;			//0〜65536=数値,65537〜131071=TBL_LabRef[]へのインデクス	//{{抽選表idを自己参照できるように。}}
	double		v_dbl;
	const char*	v_str;
	//
	ST_Table*	v_table;
	ST_Blocks*	v_blocks;
	ST_Block*	v_block;
	ST_Header*	v_header;
	ST_Body*	v_body;
	ST_LabelRow*	v_labelRow;
	ST_HeaderRow*	v_headerRow;
	ST_BodyRow*	v_bodyRow;
	ST_Values*	v_values;
	ST_ValRef	v_valRef;
	ST_ValRefs*	v_valRefs;
}

%token			T_ID
%token	<v_num>		T_NUM
%token	<v_dbl>		T_DBL
%token	<v_str>		T_LAB
%token	<v_str>		T_STR
%token			T_EOL

%type	<v_num>		int_arg
%type	<v_num>		e10_arg		//v_numには、指数表現に変換する前の整数値をそのまま保持しています。指数表現に変換するタイミングは、colId,hitVal,weightとして格納する直前です。こうした理由は、指数表現に変換して格納する抽選値(weight)に対してもnTblMulを適用する必要が有り、指数表現に対しては演算が行えないので、指数表現に変換するタイミングをなるべく遅らせる方が良いと思ったからです。別の方法としては、v_numに指数表現を保持して、nTblMulを適用する前にExp10_Decode()で戻すという方法も有りますが、それよりも前述の方法の方が自然だと思いました。
%type	<v_str>		ref_str
%type	<v_num>		expr
%type	<v_dbl>		edbl
%type	<v_str>		estr
%type	<v_table>	table
%type	<v_blocks>	blocks
%type	<v_block>	block
%type	<v_header>	header
%type	<v_body>	body
%type	<v_labelRow>	labelRow
%type	<v_num>		tblExp
%type	<v_num>		tblMul
%type	<v_headerRow>	headerRow
%type	<v_bodyRow>	bodyRow
%type	<v_values>	colIds
%type	<v_valRefs>	weights
%type	<v_num>		colId
%type	<v_num>		hitVal
%type	<v_valRef>	weight
%type	<v_str>		labDef
%type	<v_num>		labRef		//{{抽選表idを自己参照できるように。}}

%left			'|'
%left			'^'
%left			'&'
%left			T_SHL T_SHR
%left			'+' '-'
%left			'*' '/' '%'
%nonassoc		'~' T_UPL T_UMI T_CAST_INT T_CAST_DBL T_CAST_E10

%%

all
		: /* empty */					/* no action */
	    //{{2015/02/21修正:allの「all eols」とbodyRowの「hitVal ',' weights eols」が衝突していたので、前者を「eols」に変更して修正しました。
	    //	| all eols					/* no action */			//ファイルの最初に一行以上の空行が有った場合に読み飛ばすために必要。
	    //↓2015/02/21修正:allの「all eols」とbodyRowの「hitVal ',' weights eols」が衝突していたので、前者を「eols」に変更して修正しました。
		| eols						/* no action */			//ファイルの最初に一行以上の空行が有った場合に読み飛ばすために必要。
	    //}}2015/02/21修正:allの「all eols」とbodyRowの「hitVal ',' weights eols」が衝突していたので、前者を「eols」に変更して修正しました。
		| all table					{ add_table($2); yyerrok; }	//error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
		| all error							//全てのシンタクスエラーを報告するために継続する。
		;
table
		: labelRow blocks				{ $$ = Table_newBlocks($1, $2); }
		| labelRow body					{ $$ = Table_newBody(  $1, $2); }
		;
blocks
		:        block					{ $$ = Blocks_add(NULL, $1); }
		| blocks block					{ $$ = Blocks_add(  $1, $2); }
		;
block
		: header body					{ $$ = Block_new($1, $2); }
		;
header
		:        headerRow				{ $$ = Header_add(NULL, $1); }
		| header headerRow				{ $$ = Header_add(  $1, $2); }
		;
body
		:      bodyRow					{ $$ = Body_add(NULL, $1); }
		| body bodyRow					{ $$ = Body_add(  $1, $2); }
		;
eols
		:      T_EOL					/* no action */
		| eols T_EOL					/* no action */
		;
labelRow
		: ':' tblExp labDef tblMul ref_str/*無視*/ eols	{ $$ = LabelRow_new($3); bTblExp = $2; nTblMul = $4; }	//テーブルの開始時に、確実に、bTblExpとnTblMulを設定する。
		;
tblExp
		: /* empty */					{ $$ = 0; }
		| T_CAST_E10					{ $$ = 1; }
		;
tblMul
		: /* empty */					{ $$ = 1; }
		| '*' int_arg					{ $$ = $2; }
		;
headerRow
		: id      ',' colIds          eols		{ $$ = HeaderRow_new($3); }
		;
bodyRow
		: hitVal  ',' weights         eols		{ $$ = BodyRow_new($1, $3); }
		;
id
		: T_ID        ref_str/*無視*/			/** no job **/
		;
colIds
		:             colId				{ $$ = Values_add(NULL, $1); }
		| colIds  ',' colId				{ $$ = Values_add(  $1, $3); }
		;
weights
		:             weight				{ $$ = ValRefs_add(NULL, $1); }
		| weights ',' weight				{ $$ = ValRefs_add(  $1, $3); }
		;
colId
		: /* empty */ ref_str/*無視*/			{ $$ = -1/*空白*/; }
		| int_arg     ref_str/*無視*/			{ $$ = ColId_new(                $1 ); }
		| e10_arg     ref_str/*無視*/			{ $$ = ColId_new(cast_int_to_e10($1)); }								//cast_int_to_e10()が(0〜65535)の範囲外の値を返す事は無いので、ColId_new()を省略して'$$=cast_int_to_e10($1)'としても構いません。どちらでも同じ結果になります。
		| labRef      ref_str/*無視*/			/* use default action */				//{{抽選表idを自己参照できるように。}}
		;
hitVal
		: int_arg     ref_str/*無視*/			{ $$ = HitVal_new(                $1 ); }
		| e10_arg     ref_str/*無視*/			{ $$ = HitVal_new(cast_int_to_e10($1)); }								//cast_int_to_e10()が(0〜65535)の範囲外の値を返す事は無いので、HitVal_new()を省略して'$$=cast_int_to_e10($1)'としても構いません。どちらでも同じ結果になります。
		| labRef      ref_str/*無視*/			/* use default action */				//{{抽選表idを自己参照できるように。}}
		;
weight
		: int_arg     ref_str				{ int tmp = $1 * nTblMul; if(bTblExp) { tmp = cast_int_to_e10(tmp); } $$.val = Weight_new(tmp); $$.ref = $2; }	//nTblMul倍した結果が65536を超えた場合は、Weight_new()にてエラーを検出します。
		| e10_arg     ref_str				{ int tmp = $1 * nTblMul;               tmp = cast_int_to_e10(tmp);   $$.val = Weight_new(tmp); $$.ref = $2; }	//nTblMul倍した結果が指数表現にエンコード出来ない値になった場合は、cast_int_to_e10()にてエラーを検出します。cast_int_to_e10()が(0〜65535)の範囲外の値を返す事は無いので、Weight_new()を省略して'$$.val=tmp'としても構いません。どちらでも同じ結果になります。
		| labRef      ref_str				{ int tmp = $1          ;                                             $$.val =            tmp ; $$.ref = $2; }	//{{抽選表idを自己参照できるように。}}
		;
labDef
		: T_LAB						{ if(!($$ = LabDef_new($1))    ) { YYERROR; } }								//ラベル定義が不正ならば、シンタクスエラーを生成して、このlabDefを受け入れない。
		;
labRef
		: T_LAB						{ if( ($$ = LabRef_new($1)) < 0) { YYERROR; } }		//{{抽選表idを自己参照できるように。}}		//ラベル参照が不正ならば、シンタクスエラーを生成して、このlabRefを受け入れない。
		;
		/*--------------------------------------------*/
int_arg
		: expr						/* use default action */
		| edbl						{ $$ = cast_dbl_to_int($1); }
		;
e10_arg
		: T_CAST_E10 expr				{ $$ =                 $2 ; }	//┬v_numには、指数表現に変換する前の整数値をそのまま保持しています。指数表現に変換するタイミングは、colId,hitVal,weightとして格納する直前です。こうした理由は、指数表現に変換して格納する抽選値(weight)に対してもnTblMulを適用する必要が有り、指数表現に対しては演算が行えないので、指数表現に変換するタイミングをなるべく遅らせる方が良いと思ったからです。別の方法としては、v_numに指数表現を保持して、nTblMulを適用する前にExp10_Decode()で戻すという方法も有りますが、それよりも前述の方法の方が自然だと思いました。
		| T_CAST_E10 edbl				{ $$ = cast_dbl_to_int($2); }	//┘
		;
ref_str
		: /* empty */					{ $$ = NULL; }
		| estr						/* use default action */
		;
		/*--------------------------------------------*/
expr
		: T_NUM						/* use default action */
		| '(' expr ')'					{ $$ =  $2; }
		| T_CAST_INT expr				{ $$ =  $2; }
		| T_CAST_INT edbl				{ $$ =  $2; }
		| '+' expr %prec T_UPL				{ $$ =  $2; }
		| '-' expr %prec T_UMI				{ $$ = -$2; }
		| '~' expr					{ $$ = ~$2; }
		| expr '+' expr					{ $$ = $1 + $3; }
		| expr '-' expr					{ $$ = $1 - $3; }
		| expr '*' expr					{ $$ = $1 * $3; }
		| expr '/' expr					{ if(!$3) { yyerror("division or modulo by zero"); yynerrs++; YYERROR; } $$ = $1 / $3; }
		| expr '%' expr					{ if(!$3) { yyerror("division or modulo by zero"); yynerrs++; YYERROR; } $$ = $1 % $3; }
		| expr '&' expr					{ $$ = $1 & $3; }
		| expr '^' expr					{ $$ = $1 ^ $3; }
		| expr '|' expr					{ $$ = $1 | $3; }
		| expr T_SHL expr				{ if((unsigned)$3 >= 32) { yyerror("shift count >= 32"); yynerrs++; YYERROR; } $$ = $1 << $3; }
		| expr T_SHR expr				{ if((unsigned)$3 >= 32) { yyerror("shift count >= 32"); yynerrs++; YYERROR; } $$ = $1 >> $3; }
		;
edbl
		: T_DBL
		| '(' edbl ')'					{ $$ =  $2; }
		| T_CAST_DBL expr				{ $$ =  $2; }
		| T_CAST_DBL edbl				{ $$ =  $2; }
		| '+' edbl %prec T_UPL				{ $$ =  $2; }
		| '-' edbl %prec T_UMI				{ $$ = -$2; }
		| edbl '+' edbl					{ $$ = $1 + $3; }
		| edbl '+' expr					{ $$ = $1 + $3; }
		| expr '+' edbl					{ $$ = $1 + $3; }
		| edbl '-' edbl					{ $$ = $1 - $3; }
		| edbl '-' expr					{ $$ = $1 - $3; }
		| expr '-' edbl					{ $$ = $1 - $3; }
		| edbl '*' edbl					{ $$ = $1 * $3; }
		| edbl '*' expr					{ $$ = $1 * $3; }
		| expr '*' edbl					{ $$ = $1 * $3; }
		| edbl '/' edbl					{ if(!$3) { yyerror("division or modulo by zero"); yynerrs++; YYERROR; } $$ = $1 / $3; }
		| edbl '/' expr					{ if(!$3) { yyerror("division or modulo by zero"); yynerrs++; YYERROR; } $$ = $1 / $3; }
		| expr '/' edbl					{ if(!$3) { yyerror("division or modulo by zero"); yynerrs++; YYERROR; } $$ = $1 / $3; }
		;
estr
		: T_STR
		| '(' estr ')'					{ $$ = $2; }
		| estr '+' estr					{ $$ = strconcat($1, $3, NULL); }
		;

%%
