%{
/*{{定型コード: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
/*}}定型コード:y.tab.y(宣言部,%{〜%}内)======================================*/
%}

%union {
	int		v_num;
	double		v_dbl;
	char*		v_lab;
	char*		v_str;
	//
	ST_KeyBody*	v_keyBody;
	ST_Key*		v_key;
	ST_NameRow*	v_nameRow;
	ST_ValueRow*	v_valueRow;
	ST_StringRow*	v_stringRow;
	ST_BlobRow*	v_blobRow;
	ST_Blob*	v_blob;
}

%token			T_END
%token	<v_num>		T_NUM
%token	<v_dbl>		T_DBL
%token	<v_lab>		T_LAB
%token	<v_str>		T_STR
%token			T_EOL
%token			T_BIT
%token			T_BYTE
%token			T_HALF
%token			T_WORD
%token			T_FP16
%token			T_FP32
%token			T_FP64
%token			T_FILE

%type	<v_keyBody>	keyBody
%type	<v_key>		key
%type	<v_nameRow>	nameRow
%type	<v_valueRow>	valueRow
%type	<v_stringRow>	stringRow
%type	<v_num>		name
%type	<v_num>		data
%type	<v_num>		bit
%type	<v_num>		byte
%type	<v_num>		half
%type	<v_num>		word
%type	<v_dbl>		fp16
%type	<v_dbl>		fp32
%type	<v_dbl>		fp64
%type	<v_str>		file
%type	<v_num>		int_arg
%type	<v_dbl>		dbl_arg
%type	<v_num>		expr
%type	<v_dbl>		edbl
%type	<v_str>		estr
%type	<v_str>		string
%type	<v_blobRow>	blobRow
%type	<v_blob>	blobBody
%type	<v_blob>	blob

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

%%

all
		: keyBody			{ add_keyBody($1); }
		;
keyBody		: /* empty */			{ $$ = KeyBody_new(); }
		| keyBody T_EOL			/* use default action */
		| keyBody key			{ $$ = KeyBody_addKey($1, $2);       yyerrok; }	//error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
		| keyBody valueRow		{ $$ = KeyBody_addValueRow($1, $2);  yyerrok; }	//error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
		| keyBody stringRow		{ $$ = KeyBody_addStringRow($1, $2); yyerrok; }	//error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
		| keyBody blobRow		{ $$ = KeyBody_addBlobRow($1, $2);   yyerrok; }	//error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
		| keyBody error									//全てのシンタクスエラーを報告するために継続する。
		;
key
		: nameRow keyBody endRow	{ $$ = Key_new($1, $2); }
		;
nameRow
		: name T_EOL			{ $$ = NameRow_new($1); }
		;
valueRow
		: name '=' data T_EOL		{ $$ = ValueRow_new($1, $3); }
		;
stringRow
		: name '=' string T_EOL		{ $$ = StringRow_new($1, $3); }
		;
blobRow
		: name '=' blob T_EOL		{ $$ = BlobRow_new($1, $3); }
		;
endRow
		: T_END T_EOL			/* no action */
		;
name
		: int_arg			{ $$ = Name_newExpr($1); }
		| T_LAB				{ $$ = Name_newLabel($1); }
		;
data
		: int_arg			{ $$ = Data_newExpr($1); }
		| T_LAB				{ $$ = Data_newLabel($1); }
		;
string
		: estr				{ $$ = String_intern($1); }
		;
blob
		: '{' xeol blobBody '}'		{ if(!($$ = Blob_intern($3))) { YYERROR; } }		//空のblobBodyならばエラーとする。	普通は、blobBodyの変換規則が空要素を認めていないから空のblobBodyが生成される事は無いのだが、唯一、file要素のみから成るblobBodyで,かつ,そのファイルが0バイトの時に空のblobBodyが生成されてしまう。そのようなケースはy.tab.yで検出出来ないので、Blob_intern()で検出してエラーとする事にした。
		;
blobBody
		:                   bit  xeol	{ if(!($$ = Blob_newBit( $1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol bit  xeol	{ if(!($$ = Blob_addBit( $1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   byte xeol	{ if(!($$ = Blob_newByte($1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol byte xeol	{ if(!($$ = Blob_addByte($1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   half xeol	{ if(!($$ = Blob_newHalf($1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol half xeol	{ if(!($$ = Blob_addHalf($1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   word xeol	{ if(!($$ = Blob_newWord($1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol word xeol	{ if(!($$ = Blob_addWord($1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   fp16 xeol	{ if(!($$ = Blob_newFp16($1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol fp16 xeol	{ if(!($$ = Blob_addFp16($1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   fp32 xeol	{ if(!($$ = Blob_newFp32($1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol fp32 xeol	{ if(!($$ = Blob_addFp32($1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   fp64 xeol	{ if(!($$ = Blob_newFp64($1    ))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| blobBody ',' xeol fp64 xeol	{ if(!($$ = Blob_addFp64($1, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		|                   file xeol	{ if(!($$ = Blob_newFile($1    ))) { YYERROR; } }	//ファイルが存在しない,又は,ファイルが大き過ぎたらエラーとする。
		| blobBody ',' xeol file xeol	{ if(!($$ = Blob_addFile($1, $4))) { YYERROR; } }	//ファイルが存在しない,又は,ファイルが大き過ぎたらエラーとする。
		;
bit
		: T_BIT  int_arg		{ $$ = Bit_new( $2); }
		;
byte
		:        int_arg		{ $$ = Byte_new($1); }		//省略記法
		| T_BYTE int_arg		{ $$ = Byte_new($2); }
		;
half
		: T_HALF int_arg		{ $$ = Half_new($2); }
		;
word
		: T_WORD int_arg		{ $$ = $2; }
		;
fp16
		: T_FP16 dbl_arg		{ $$ = $2; }
		;
fp32
		: T_FP32 dbl_arg		{ $$ = $2; }
		;
fp64
		: T_FP64 dbl_arg		{ $$ = $2; }
		;
file
		: T_FILE estr			{ $$ = $2; }
		;
xeol
		: /* empty */			/* no action */
		| xeol T_EOL			/* no action */
		;
int_arg
		: expr				/* use default action */
		| edbl				{ $$ = cast_dbl_to_int($1); }
		;
dbl_arg
		: expr				{ $$ = $1; }
		| edbl				/* use default action */
		;
expr
		: T_NUM
		| '(' 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); }
		;

%%
