%{
#include "hexcalc.h"
%}

%union			/* stack type */
{
  BASE_TYPE val;	/* actual value */
  Symbol *sym;		/* symbol table pointer */
}

%token	<val>	NUMBER
%token	<sym>	CONST VAR BLTIN UNDEF QUIT HELP CHRADIX
%type	<val>	expr asgn
%right	'='
%left	'|'
%left	'^'
%left	'&'
%left	LSH RSH
%left	'+' '-'
%left	'*' '/' '%'
%left	UNARYMINUS '~'
%right	POW		/* exponentiation */

%%
list:		  /* nothing */
		| list '\n'
		| list asgn '\n'
		| list expr '\n'	{ 
					  if (radix == 16)
						printf ("\t%lx\n", $2);
					  else if (radix == 10)
						printf ("\t%lu\n", $2);
					  else
						printf ("\t%lo\n", $2);
					}
		| list HELP '\n'        { puthelp(); }
		| list QUIT '\n'        { longjmp (prog_end, 1); }
		| list CHRADIX '\n'	{
					  if (*($2->name) == 'h')
						radix = 16; 
					  else if (*($2->name) == 'd')
						radix = 10; 
					  else
						radix = 8; 
					}
		| list error '\n'	{ yyerrok; }
		;
asgn:		  VAR '=' expr		{ $$ = $1->u.val = $3; $1->type = VAR; }
		;
expr:		  NUMBER		{ $$ = $1; }
		| CONST			{ $$ = $1->u.val; }
		| VAR			{ 
			if ($1->type == UNDEF)
				execerror ("undefined variable", $1->name);
			$$ = $1->u.val; }
		| asgn
		| BLTIN '(' expr ')'	{ $$ = (*($1->u.ptr))($3); }
		| expr LSH expr 	{ $$ = $1 << $3; }
		| expr RSH expr 	{ $$ = $1 >> $3; }
		| expr '|' expr         { $$ = $1 | $3; }
		| expr '^' expr         { $$ = $1 ^ $3; }
		| expr '&' expr         { $$ = $1 & $3; }
		| expr '+' expr		{ $$ = $1 + $3; }
		| expr '-' expr		{ $$ = $1 - $3; }
		| expr '*' expr		{ $$ = $1 * $3; }
		| expr '/' expr		{	
			if ($3 == 0)
				execerror ("division by zero", (char *)0);
			$$ = $1 / $3; }
		| expr '%' expr		{	
			if ($3 == 0)
				execerror ("modulo zero", (char *)0);
			$$ = $1 % $3; }
		| expr POW expr 	{ $$ = ulpow ($1, $3); }
		| '(' expr ')'		{ $$ = $2; }
		| '-' expr %prec UNARYMINUS { $$ = -$2; }
		| '~' expr              { $$ = ~ $2; }
		;
%%

#include <stdio.h>
#include <setjmp.h>

char *progname;		/* for error messages */
int lineno = 1;
int radix = 16;

STATIC char *credit =
"hexcalc 1.1 by Richard Hargrove, 25 June 1988; based on hoc3 from Kernighan and Pike";

jmp_buf prog_begin, prog_end;

/******************************************************************************/

void puthelp()
{
	static char *help_text[] = {
	"Type in an expression or command. Expressions other than assignment",
	"cause the evaluated result to be output. All values are 32-bit integers.",
	"Expressions are made up of the following operands and operators:",
	"",
	"Operands: numeric literals (must begin with a digit), variables",
	"(31 chars max, initialized with assignment), and built-in functions.",
	"Built-ins:	DEC(expr)	output decimal value of expr",
	"		OCT(expr)	output octal value of expr",
	"		HEX(expr)	output hex value of expr",
	"Operators:	(  )		grouping",
	"		**		exponentiation",
	"		-  ~		unary minus, bitwise not",
	"		*  /  %		multiplication, division, modulus",
	"		+  -		addition, subtraction",
	"		<<  >>		left-shift, right-shift",
	"		&		bitwise and",
	"		^		bitwise exclusive-or",
	"		|		bitwise or",
	"		=		assignment",
	"",
	"Commands:	help  HELP	print this message",
	"		quit  exit	terminate execution",
	"		hex dec oct	set the default radix",
	(char *)0
	};
	int i = 0;

	while (help_text[i] != (char *)0)
	{
	  puts(help_text[i++]);
	}
}

/******************************************************************************/

void warning (s, t)	/* print warning message */
char *s, *t;
{
	fprintf (stderr, "%s : %s", progname, s);
	if (t != (char *)0)
	{
		fprintf (stderr, " %s", t);
	}
	fprintf (stderr, " near line %d\n", lineno);
}


/******************************************************************************/

void yyerror (s)	/* called for yacc syntax error */
char *s;
{
	warning (s, (char *)0);
}

/******************************************************************************/

void execerror (s, t)
char *s, *t;
{
	warning (s, t);
	longjmp (prog_begin, 1);
}

/******************************************************************************/

main (argc, argv)
int argc;
char *argv [];
{
	progname = argv [0];
	init ();
	if (setjmp (prog_end) == 0)
	{
		(void)setjmp (prog_begin);
		yyparse ();
	}
	return 0;
}
