%{
/*{{定型コード: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_Fmt*		v_fmt;
	ST_ExprList*	v_exprList;
	ST_AttrList*	v_attrList;
	ST_LabelRow*	v_labelRow;
	ST_Type*	v_type;
	ST_TypeBody*	v_typeBody;
	ST_Init*	v_init;
	ST_Range*	v_range;
	ST_Enum*	v_enum;
	ST_Table*	v_table;
	ST_TableBody*	v_tableBody;
	ST_Var*		v_var;
}

%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_INIT
%token			T_RANGE
%token			T_LIMIT
%token			T_ENUM

%type	<v_num>		int_arg
%type	<v_num>		expr
%type	<v_dbl>		edbl
%type	<v_str>		estr
%type	<v_fmt>		fmt
%type	<v_exprList>	exprList
%type	<v_attrList>	attrList
%type	<v_labelRow>	labelRow
%type	<v_type>	type
%type	<v_typeBody>	typeBody
%type	<v_typeBody>	typeBody1
%type	<v_str>		inlineType		//テーブル定義の中の変数定義行で、インラインタイプ定義が使用されたら、addInlineType()が(内部的な)インラインタイプ名に変換してタイプを登録し、そのタイプ名を返す。
%type	<v_typeBody>	inlineTypeBody
%type	<v_typeBody>	inlineTypeBody1
%type	<v_init>	init
%type	<v_range>	range
%type	<v_enum>	enum
%type	<v_table>	table
%type	<v_tableBody>	tableBody
%type	<v_var>		var
%type	<v_str>		typeName
%type	<v_str>		desc

%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 type							{ addType( $2); yyerrok; }						//┬error記号の位置について『http://www.pwv.co.jp/~take/TakeWiki/index.php?Cコンパイラ設計(yacc・lexの応用)/第4章　エラー・リカバリ#dbd8d417』の【optional sequence】を参考にした。
		| all table							{ addTable($2); yyerrok; }						//┘
		| 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
		| '(' 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); }
		;
		/*--------------------------------------------*/
exprList
		:              int_arg						{ if(!($$ = ExprList_new(    $1))) { YYERROR; } }			//int_argがINT16_MIN〜INT16_MAXの範囲外ならばエラーとする。
		| exprList ',' int_arg						{ if(!($$ = ExprList_add($1, $3))) { YYERROR; } }			//int_argがINT16_MIN〜INT16_MAXの範囲外ならばエラーとする。
		;
attrList
		:          T_LAB						{ if(!($$ = AttrList_new(    $1))) { YYERROR; } }			//現在の所エラーが発生する事は無い。
		| attrList T_LAB						{ if(!($$ = AttrList_add($1, $2))) { YYERROR; } }			//同じ名前の属性が既に追加されていたらエラーとする。
		;
		/*--------------------------------------------*/
labelRow
		: T_LAB fmt desc eols						{ if(!($$ = LabelRow_new($1, $2, $3))) { YYERROR; } }			//現在の所エラーが発生する事は無い。
		;
		/*--------------------------------------------*/
type
		: labelRow typeBody end						{ if(!($$ = Type_new($1, $2))) { YYERROR; } }				//range,又は,enumが指定されていなければエラーとする。
		;
inlineType
		: inlineTypeBody						{ if(!($$ = addInlineType($1))) { YYERROR; } }				//range,又は,enumが指定されていなければエラーとする。
		;
typeBody1																								//┐
		: init eols							{ if(!($$ = TypeBody_setInit( TypeBody_new(), $1))) { YYERROR; } }	//現在の所エラーが発生する事は無い。			//│
		| range eols							{ if(!($$ = TypeBody_setRange(TypeBody_new(), $1))) { YYERROR; } }	//現在の所エラーが発生する事は無い。			//│
		| enum eols							{ if(!($$ = TypeBody_setEnum( TypeBody_new(), $1))) { YYERROR; } }	//現在の所エラーが発生する事は無い。			//│
		;																							//│
typeBody																								//│
		: typeBody1							/* use default action */													//│
		| typeBody init eols						{ if(!($$ = TypeBody_setInit( $1, $2))) { YYERROR; } }			//initが重複指定されたらエラーとする。			//│
		| typeBody range eols						{ if(!($$ = TypeBody_setRange($1, $2))) { YYERROR; } }			//range,又は,enumが重複指定されたらエラーとする。	//├┐
		| typeBody enum eols						{ if(!($$ = TypeBody_setEnum( $1, $2))) { YYERROR; } }			//range,又は,enumが重複指定されたらエラーとする。	//┘│
		;																							//　│処理は同じです。
inlineTypeBody1																								//　│
		: init								{ if(!($$ = TypeBody_setInit( TypeBody_new(), $1))) { YYERROR; } }	//現在の所エラーが発生する事は無い。			//┐│
		| range								{ if(!($$ = TypeBody_setRange(TypeBody_new(), $1))) { YYERROR; } }	//現在の所エラーが発生する事は無い。			//├┘
		| enum								{ if(!($$ = TypeBody_setEnum( TypeBody_new(), $1))) { YYERROR; } }	//現在の所エラーが発生する事は無い。			//│
		;																							//│
inlineTypeBody																								//│
		: inlineTypeBody1						/* use default action */													//│
		| inlineTypeBody init						{ if(!($$ = TypeBody_setInit( $1, $2))) { YYERROR; } }			//initが重複指定されたらエラーとする。			//│
		| inlineTypeBody range						{ if(!($$ = TypeBody_setRange($1, $2))) { YYERROR; } }			//range,又は,enumが重複指定されたらエラーとする。	//│
		| inlineTypeBody enum						{ if(!($$ = TypeBody_setEnum( $1, $2))) { YYERROR; } }			//range,又は,enumが重複指定されたらエラーとする。	//│
		;																							//┘
init
		: T_INIT int_arg						{ if(!($$ = Init_new($2))) { YYERROR; } }				//int_argがINT16_MIN〜INT16_MAXの範囲外ならばエラーとする。
		;
range
		: T_RANGE int_arg ',' int_arg					{ if(!($$ = Range_new($2, $4))) { YYERROR; } }				//int_argがINT16_MIN〜INT16_MAXの範囲外ならばエラーとする。min,maxが逆ならばエラーとする。
		| T_LIMIT             int_arg					{ if(!($$ = Range_new( 0, $2))) { YYERROR; } }				//int_argがINT16_MIN〜INT16_MAXの範囲外ならばエラーとする。min,maxが逆ならばエラーとする。
		;
enum
		: T_ENUM exprList						{ if(!($$ = Enum_new($2))) { YYERROR; } }				//exprListの中に同じ値が重複指定されていたらエラーとする。
		;
		/*--------------------------------------------*/
table
		: labelRow tableBody end					{ if(!($$ = Table_new($1, $2))) { YYERROR; } }				//tableBodyが空ならばエラーとする。
		;
tableBody
		: /* empty */							{ if(!($$ = TableBody_new())) { YYERROR; } }				//現在の所エラーが発生する事は無い。
		| tableBody var							{ if(!($$ = TableBody_addVar($1, $2))) { YYERROR; } }			//同じ名前の変数が既に追加されていたらエラーとする。
		;
var
		: T_LAB                  typeName          fmt desc eols	{ if(!($$ = Var_new($1, NULL,   $2, NULL, $3, $4))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| T_LAB '[' exprList ']' typeName          fmt desc eols	{ if(!($$ = Var_new($1, $3  ,   $5, NULL, $6, $7))) { YYERROR; } }	//exprListの中に1未満の値が指定されていたらエラーとする。
		| T_LAB                  typeName attrList fmt desc eols	{ if(!($$ = Var_new($1, NULL,   $2, $3  , $4, $5))) { YYERROR; } }	//現在の所エラーが発生する事は無い。
		| T_LAB '[' exprList ']' typeName attrList fmt desc eols	{ if(!($$ = Var_new($1, $3  ,   $5, $6  , $7, $8))) { YYERROR; } }	//exprListの中に1未満の値が指定されていたらエラーとする。
		;
typeName
		: /* empty */							{ $$ = NULL/*'var'変数*/; }
		| ':' T_LAB							{ $$ = $2; }
		| ':' inlineType						{ $$ = $2; }
		;
desc
		: /* empty */							{ $$ = ""/*説明文無し*/; }
		| estr								{ $$ = $1; }
		;
fmt
		: /* empty */							{ $$ = Fmt_new(NULL, NULL); }
		| '<' estr  '>'							{ $$ = Fmt_new(  $2, NULL); }
		| '<' T_LAB '>'							{ $$ = Fmt_new(NULL,   $2); }
		;

%%
