%{
/*
Auto:		smake MCalc
*/

static void	__yy_bcopy(char *from, char *to, int count);

#undef		alloca
#define		alloca(x) AllocVecPool(ParsePool, x)
#undef		malloc
#define		malloc(x) AllocVecPool(ParsePool, x)
#undef		free
#define		free(x) FreeVecPool(ParsePool, x)

#undef		YYINITDEPTH
#define		YYINITDEPTH 512

#undef		_STDC_
#define		_STDC_

#undef		error
#define		error

extern		APTR		ParsePool;
extern		double		Value;
extern		double		XMem, YMem, ZMem;

#define		outputr(x)	{Value = x;}
#define		outputx(x)	{Value = XMem = x;}
#define		outputy(x)	{Value = YMem = x;}
#define		outputz(x)	{Value = ZMem = x;}
#define		errout(x)	;
#define		erroutm(x, y)	;


/**********************************************************************/
/*                    Chars read and read-position                    */
/**********************************************************************/
extern	UWORD	PColumn;
extern	UWORD	PCharRead;


	// This is for the wanted angle

extern	UWORD		IntAngle;


/**********************************************************************/
/*                            Error-Value                             */
/**********************************************************************/
UWORD		PError;
%}



/**********************************************************************/
/*                       Possible Return-Value                        */
/**********************************************************************/
%union
{
	double	Real;
}


/**********************************************************************/
/*                      Token which returns Real                      */
/**********************************************************************/
%token <Real>   INT_CONSTANT
%token <Real>   X_MEM Y_MEM Z_MEM



/**********************************************************************/
/*                       Non-Priority functions                       */
/**********************************************************************/
%token EQU_OP OPEN_OP CLOSE_OP



/**********************************************************************/
/*                           Set priorities                           */
/**********************************************************************/
%left AND_OP OR_OP XOR_OP ASL ASR LSL LSR ROL ROR
%left ADD_OP SUB_OP
%left MUL_OP DIV_OP MOD_OP
%left NEG_OP NOT_OP
%left POW
%nonassoc MY_ABS COS SIN TAN ACOS ASIN ATAN EXP LOG LOG10 SQRT SINH COSH TANH FAK COT



/**********************************************************************/
/*                    Return-Value of my terminal                     */
/**********************************************************************/
%type <Real>   int_expr

%%



/**********************************************************************/
/*                          Main "sentence"                           */
/**********************************************************************/
eingabe
	: int_expr					{outputr($1);}
	| X_MEM EQU_OP int_expr				{outputx($3);}
	| Y_MEM EQU_OP int_expr				{outputy($3);}
	| Z_MEM EQU_OP int_expr				{outputz($3);}
	| error
	;




/**********************************************************************/
/*                           Do the grammar                           */
/**********************************************************************/
int_expr
	: OPEN_OP int_expr CLOSE_OP			{$$ = $2;}
	| int_expr AND_OP int_expr			{$$ = (make_ulong($1)) & (make_ulong($3));}
	| int_expr OR_OP int_expr			{$$ = (make_ulong($1)) | (make_ulong($3));}
	| int_expr XOR_OP int_expr			{$$ = MyXOR(make_ulong($1),make_ulong($3));}
	| int_expr NOT_OP AND_OP int_expr		{$$ = MyNAND(make_ulong($1), make_ulong($4));}
	| int_expr NOT_OP OR_OP int_expr		{$$ = MyNOR(make_ulong($1), make_ulong($4));}
	| int_expr NOT_OP XOR_OP int_expr		{$$ = MyNXOR(make_ulong($1), make_ulong($4));}
	| int_expr ADD_OP int_expr			{$$ = $1 + $3;}
	| int_expr SUB_OP int_expr			{$$ = $1 - $3;}
	| int_expr MUL_OP int_expr			{$$ = $1 * $3;}
	| int_expr DIV_OP int_expr			{if($3 == 0.0) {PError = ERR_DIVBY0; yyerror(NULL); } else $$ = $1 / $3;}
	| int_expr MOD_OP int_expr			{if($3 == 0.0) {PError = ERR_DIVBY0; yyerror(NULL); } else $$ = fmod($1,$3);}
	| SUB_OP int_expr %prec NEG_OP			{$$ = -$2; }
	| NOT_OP int_expr				{$$ = (~make_ulong($2)); }
	| int_expr ASL int_expr				{$$ = (double)MyASL(make_ulong($1),make_ulong($3)); }
	| int_expr ASR int_expr				{$$ = (double)MyASR(make_ulong($1),make_ulong($3)); }
	| int_expr LSL int_expr				{$$ = (double)MyLSL(make_ulong($1),make_ulong($3)); }
	| int_expr LSR int_expr				{$$ = (double)MyLSR(make_ulong($1),make_ulong($3)); }
	| int_expr ROL int_expr				{$$ = (double)MyROL(make_ulong($1),make_ulong($3)); }
	| int_expr ROR int_expr				{$$ = (double)MyROR(make_ulong($1),make_ulong($3)); }
	| int_expr POW int_expr				{$$ = pow($1,$3); }
	| SIN int_expr					{$$ = sin(calc_angle($2)); }
	| COS int_expr					{$$ = cos(calc_angle($2)); }
	| TAN int_expr					{if(!check_angle($2, TAN)) yyerror(NULL); else $$ = tan(calc_angle($2)); }
	| ASIN int_expr					{if(!check_angle($2, ASIN)) yyerror(NULL); else $$ = asin(calc_angle($2)); }
	| ACOS int_expr					{if(!check_angle($2, ACOS)) yyerror(NULL); else $$ = acos(calc_angle($2)); }
	| ATAN int_expr					{if(!check_angle($2, ATAN)) yyerror(NULL); else $$ = atan(calc_angle($2)); }
	| COT int_expr					{if(!check_angle($2, COT)) yyerror(NULL); else $$ = cot(calc_angle($2)); }
	| SINH int_expr					{$$ = sinh(calc_angle($2)); }
	| COSH int_expr					{$$ = cosh(calc_angle($2)); }
	| TANH int_expr					{if(!check_angle($2, TANH)) yyerror(NULL); else $$ = tanh(calc_angle($2)); }
	| EXP int_expr					{$$ = exp($2); }
	| LOG int_expr					{$$ = log($2); }
	| LOG10 int_expr				{$$ = log10($2); }
	| MY_ABS int_expr				{$$ = fabs($2); }
	| SQRT int_expr					{if($2 < 0.0) {PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = sqrt($2);}
	| int_expr FAK					{if($1 > 170.0) { PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = calc_fak($1);}
	| FAK int_expr					{if($2 > 170.0) { PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = calc_fak($2);}
	| INT_CONSTANT
	| X_MEM
	| Y_MEM
	| Z_MEM
	;

%%




/**********************************************************************/
/*             Convert a double to a long (the hard way)              */
/**********************************************************************/
ULONG make_ulong(double OldVal)
{
	char	Buffer[26];
	ULONG	Dummy;

	sprintf(Buffer, "%f", OldVal);
	stcd_l(Buffer, (LONG *)&Dummy);
	return(Dummy);
}





/**********************************************************************/
/*                           Calculate Fak                            */
/**********************************************************************/
double calc_fak(double Fak)
{
	double	RetVal = 1.0, i = 1.0;

	while(i <= Fak)
	{
		RetVal *= i;
		i++;
	}

	return(RetVal);
}





/**********************************************************************/
/*              Calculate correct angle value (rad, deg)              */
/**********************************************************************/
double calc_angle(double Value)
{
	if(IntAngle == ID_DEG)
		Value = Value / 180.0 * PI;

	return(Value);
}





/**********************************************************************/
/*                  Check if input is within bounds                   */
/**********************************************************************/
BOOL check_angle(double Value, int Mode)
{
	Value	= calc_angle(Value);

	switch(Mode)
	{
		case TAN :
		case TANH :
		{
			if(Value < -(PI / 2) || Value > (PI / 2))
			{
				PError = ERR_OVERFLOW;
				return(FALSE);
			}
		}
		case COT :
		{
			if(Value < -PI || Value > PI)
			{
				PError = ERR_OVERFLOW;
				return(FALSE);
			}
		}
		case ASIN :
		case ACOS :
		{
			if(Value < -1.0 || Value > 1.0)
			{
				PError = ERR_OVERFLOW;
				return(FALSE);
			}
			break;
		}
		case ATAN :
		{
			if(Value <= -1.0 || Value >= 1.0)
			{
				PError = ERR_OVERFLOW;
				return(FALSE);
			}
			break;
		}
	}

	return(TRUE);
}





/**********************************************************************/
/*                          Error-Routine ;)                          */
/**********************************************************************/
int yyerror(char *s)
{
	return(0);
}
