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

%union {
	int			v_num;
	double			v_dbl;
	char*			v_str;
	ST_SprFnt*		v_sprFnt;
	ST_SprFntHdr*		v_sprFntHdr;
	ST_SprFntBody*		v_sprFntBody;
	ST_SprFntRow*		v_sprFntRow;
}

%token	<v_num>			T_NUM
%token	<v_dbl>			T_DBL
%token	<v_str>			T_LAB
%token	<v_str>			T_STR
%token				T_EOL
%token				T_END
%token				T_FALLBACK
%token				T_BLANK

%type	<v_num>			int_arg
%type	<v_num>			expr
%type	<v_dbl>			edbl
%type	<v_str>			estr
%type	<v_sprFnt>		sprFnt
%type	<v_sprFntHdr>		sprFntHdr
%type	<v_sprFntBody>		sprFntBody
%type	<v_sprFntRow>		sprFntRow
%type	<v_num>			sprFntChar	//fallbackならば0で示す。それ以外は文字コード1〜0xFFFFを格納する。
%type	<v_num>			sprNo		//blankならば0で示す。それ以外はスプライト番号1〜0xFFFFを格納する。
%type	<v_num>			optValue	//値が省略されたら-1で示す。それ以外は有効な値(符号付き24ビット値)を符号無し24ビット値にマスクして格納する。
%type	<v_num>			value		//値が省略されたら-1で示す。それ以外は有効な値(符号付き24ビット値)を符号無し24ビット値にマスクして格納する。

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

%%

all
				: pre_blank
				| all sprFnt					{ addSprFnt($2); yyerrok; }	//error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
				| all error									//全てのシンタクスエラーを報告するために継続する。
				;
pre_blank
				: /* empty */
				| pre_blank T_EOL
				;
eols
				:      T_EOL
				| eols T_EOL
				;
end
				: T_END eols
				;
int_arg
				: expr						/* use default action */
				| edbl						{ $$ =  cast_dbl_to_int($1); }
				;
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); }
				;
				/*--------------------------------------------*/
sprFnt
				: sprFntHdr sprFntBody end							{ if(!($$ = SprFnt_New($1, $2))) { YYERROR; } }
				;
sprFntHdr
				: T_LAB optValue ',' optValue ',' optValue ',' value eols			{ if(!($$ = SprFntHdr_New($1, $2, $4, $6, $8))) { YYERROR; } }		//スプライトフォントラベル [NextX],[NextY],[DrawX],DrawY	元々のDxlibHelper_SprFnt.cppではDrawX,DrawY,NextX,NextYの順でしたが、当ツールではNextX,NextY,DrawX,DrawYの順に変更した理由は、DrawX,DrawYを0とする事が多いので、途中の','無しに省略して書き易いようにするためです。
				| T_LAB optValue ',' optValue ',' int_arg            eols			{ if(!($$ = SprFntHdr_New($1, $2, $4, $6, -1))) { YYERROR; } }		//スプライトフォントラベル [NextX],[NextY], DrawX
				| T_LAB optValue ',' int_arg                         eols			{ if(!($$ = SprFntHdr_New($1, $2, $4, -1, -1))) { YYERROR; } }		//スプライトフォントラベル [NextX], NextY
				| T_LAB optValue                                     eols			{ if(!($$ = SprFntHdr_New($1, $2, -1, -1, -1))) { YYERROR; } }		//スプライトフォントラベル [NextX]
				;
sprFntBody
				:            sprFntRow								{ if(!($$ = SprFntBody_New(       $1))) { YYERROR; } }
				| sprFntBody sprFntRow								{ if(!($$ = SprFntBody_AddRow($1, $2))) { YYERROR; } }
				;
sprFntRow
				: sprFntChar '=' sprNo ',' optValue ',' optValue ',' optValue ',' value eols	{ if(!($$ = SprFntRow_New($1, $3, $5, $7, $9, $11))) { YYERROR; } }	//(文字|fallback)=(スプライト番号|blank),[NextX],[NextY],[DrawX],DrawY
				| sprFntChar '=' sprNo ',' optValue ',' optValue ',' value              eols	{ if(!($$ = SprFntRow_New($1, $3, $5, $7, $9,  -1))) { YYERROR; } }	//(文字|fallback)=(スプライト番号|blank),[NextX],[NextY], DrawX
				| sprFntChar '=' sprNo ',' optValue ',' value                           eols	{ if(!($$ = SprFntRow_New($1, $3, $5, $7, -1,  -1))) { YYERROR; } }	//(文字|fallback)=(スプライト番号|blank),[NextX], NextY
				| sprFntChar '=' sprNo ',' value                                        eols	{ if(!($$ = SprFntRow_New($1, $3, $5, -1, -1,  -1))) { YYERROR; } }	//(文字|fallback)=(スプライト番号|blank), NextX
				| sprFntChar '=' sprNo                                                  eols	{ if(!($$ = SprFntRow_New($1, $3, -1, -1, -1,  -1))) { YYERROR; } }	//(文字|fallback)=(スプライト番号|blank)
sprFntChar
			//{{2017/04/12変更:文字コードを、文字列だけでなく、数値でも指定出来るようにしました。
			//	: estr										{ if(!($$ = SprFntChar_New($1))) { YYERROR; } }				//文字列が、1バイト文字一文字,又は,2バイト文字一文字である事を確認し、文字コード(1〜0xFFFF)を返す。そうでなければエラーとして0を返す。
			//↓2017/04/12変更:文字コードを、文字列だけでなく、数値でも指定出来るようにしました。
				: estr										{ if(!($$ = SprFntChar_NewStr($1))) { YYERROR; } }			//文字列が、1バイト文字一文字,又は,2バイト文字一文字である事を確認し、文字コード(1〜0xFFFF)を返す。そうでなければエラーとして0を返す。
				| int_arg									{ if(!($$ = SprFntChar_NewVal($1))) { YYERROR; } }			//文字コードが、1〜0xFFFFの範囲である事を確認し、文字コード(1〜0xFFFF)を返す。そうでなければエラーとして0を返す。
			//}}2017/04/12変更:文字コードを、文字列だけでなく、数値でも指定出来るようにしました。
				| T_FALLBACK									{ $$ = 0; }								//フォールバック用の描画設定は文字コード0で示す。
				;
sprNo
				: int_arg									{ if(!($$ = SprNo_New($1))) { YYERROR; } }				//1〜0xFFFFの範囲外ならばエラーとして0を返す。エラーでなければそのままの値を返す。
				| T_BLANK									{ $$ = 0; }								//表示無しは0で示す。(※厳密には0も有効なスプライト番号である可能性も有る。dTexDefC.exeを使ってスプライト番号を生成した場合、Windows環境では1〜になるが、P/ECE環境では0〜になるから。厳密には0〜0xFFFFを有効なスプライト番号として表示無しを-1で表すべきなのだが、実行時の判定の都合を優先して表示無しは0で示す事にした。実際にはP/ECEでも、スプライト番号0をスプライトフォントに割り当てる事は滅多に起きないと思うので大丈夫だろう。)
				;
optValue
				: value										/* use default action */
				| /* empty */									{ $$ = -1; }								//値が省略された事は-1で示す。valueの処理において、有効な値-1は上位8ビットがマスクされて符号無し24ビットの最大値になるので、有効な値-1と値が省略された事を示す-1を混同する危険は無い。
				;
value
				: int_arg									{ if(($$ = Value_New($1)) == -1) { YYERROR; } }				//符号付き24ビットの範囲外ならばエラーとして-1を返す。エラーでなければ符号無し24ビット値にマスクして返す。有効な値-1は上位8ビットがマスクされて符号無し24ビットの最大値になるので、有効な値-1と値が省略された事を示す-1を混同する危険は無い。
				;

%%
