/************************************************************************/
/*	Calc program for cli, as suggested by 'INFO' March/April 1988	*/
/*	in their 'Things We'd like to see' column.			*/
/*	Written by James A. Grimaldi for free redistribution along with	*/
/*	these comments. This program was developed using Lattice C 4.0	*/
/************************************************************************/

#include	<stdio.h>
#include	<math.h>

extern	double	pow(),atof();
double	evaluate(),do_op();

void	main(argc,argv)
int	argc;
char	**argv;
	{
	int	argv_idx=1,print_idx;
	char	print_work[32];
	printf("   ");			/*	indent first number	*/
	for(;argv_idx!=argc;argv_idx++)	/*	process each parm	*/
		{
		sprintf(print_work,
			"%lf",		/*	evaluate each expresion	*/
			evaluate(argv[argv_idx]));
		for(print_idx=strlen(print_work)-1;
			print_work[print_idx]=='0';
			print_idx--)
			{	/*	strip trailing '0's	*/
			print_work[print_idx]='\0';
			}
		if(print_work[print_idx]=='.')
			{	/*	remove '.' if last character	*/
			print_work[print_idx]='\0';
			}
		printf("%s=%s ",print_work,argv[argv_idx]);
		/*	print result and expresion	*/
		}
	printf("\n");	/*	new line after printing all values	*/
	exit(0);
	}
double	evaluate(s)	/*	decompose expresions	*/
char	s[];
	{
	int	s_idx=0,exp_idx,paren_cnt=0;
	double	number_work;
	char	exp_work[128],op_work;
	if(s[0]=='(')	/*	process parens, strip first paren and	*/
		{	/*	matching right paren	*/
		exp_idx=0;
		while(-1)
			{
			switch(s[s_idx])
				{
				case '(':
					if(paren_cnt!=0)
						/*	keep additional	*/
						/*	left parens	*/
						{	
						exp_work[exp_idx++]=s[s_idx];
						}
					paren_cnt++;
						/*	count left	*/
						/*	parens		*/
					break;
				case ')':
					paren_cnt--;
						/*	count right	*/
						/*	parens		*/
					if(paren_cnt==0)
						{
						/*	matching paren	*/
						s_idx++;
						exp_work[exp_idx]='\0';
						number_work=evaluate(exp_work);
						/*	evaluate within	*/
						/*	parens		*/
						op_work=s[s_idx++];
						/*	get operation	*/
						if(op_work=='\0')
							{
							/*	no op	*/
							return(number_work);
							/*	done	*/
							}
						for(exp_idx=0;
							s[s_idx]!='\0';
							s_idx++)
							{
							/*	right	*/
							/*	side of	*/
							/*	op.	*/
							exp_work[exp_idx++]
								=s[s_idx];
							}
						exp_work[exp_idx]='\0';
						return(
							do_op(number_work,
								op_work,
								exp_work)
							);
						/*	do operation	*/
						}
					else
						{
						exp_work[exp_idx++]=s[s_idx];
						/*	keep non match	*/
						/*	right parens	*/
						}
					break;
				case '\0':	/*	end of exp	*/
					exp_work[exp_idx]='\0';
					if(paren_cnt!=0)
						{
						/*	parens not	*/
						/*	balenced	*/
						printf("\nparenthesis mismatch ");
						}
					return(evaluate(exp_work));
					/*	eval remainder exp	*/
				default:
					exp_work[exp_idx++]=s[s_idx];
					/*	keep all non parens	*/
				}
			s_idx++;	/*	advance	a position	*/
			}
		}
	if(isdigit(s[0])||s[0]=='.'||s[0]=='-')
		{
		/*	process a number	*/
		exp_idx=0;
		exp_work[exp_idx++]=s[s_idx++];
		for(;isdigit(s[s_idx])||s[s_idx]=='.';)
			{	/*	get first number string	*/
			exp_work[exp_idx++]=s[s_idx++];
			}
		exp_work[exp_idx]='\0';
		number_work=atof(exp_work);
			/*	turn number string into a number	*/
		if(s[s_idx]=='\0')
			{
			return(number_work);
			/*	no operation, done	*/
			}
		op_work=s[s_idx++];	/*	get operation	*/
		for(exp_idx=0;s[s_idx]!='\0';s_idx++)
		/*	get the expression after operation	*/
			{
			exp_work[exp_idx++]=s[s_idx];
			}
		exp_work[exp_idx]='\0';
		return(do_op(number_work,op_work,exp_work));
		/*	return result opertion	*/
		}
	if(*s=='?')
		{
		/*	command line info wanted	*/
		printf("\nEnter 1 or more expresions on the command line.");
		printf("\nEach expression may consist of numbers seperated ");
		printf("by math operations.");
		printf("\nParts of each expression may be grouped with ");
		printf("parenthisis.");
		printf("\nWithout parens expressions evaluated right to ");
		printf("left. Just like APL.");
		printf("\n");
		return((double) 0);
		}
	if(*s!='\0')
		{
		/*	there is an expression that does not start	*/
		/*	with a number or a left paren, syntax invalid	*/
		printf("\ninvalid syntax '%c'",*s);
		return(evaluate(s+1));
		/*	try again one character on	*/
		}
	return((double) 0);
	}
double	do_op(number_work,op_work,exp_work)
	/*	process operation	*/
double	number_work;
char	op_work,exp_work[];
	{
	switch(op_work)
		{
		case '+':	/*	addition	*/
			return(number_work+evaluate(exp_work));
		case '-':	/*	subtraction	*/
			return(number_work-evaluate(exp_work));
		case '*':	/*	multiplication	*/
			return(number_work*evaluate(exp_work));
		case '/':	/*	division	*/
			return(number_work/evaluate(exp_work));
		case '>':	/*	maximum		*/
			return((number_work>evaluate(exp_work))?
				number_work:evaluate(exp_work));
		case '<':	/*	minimum		*/
			return((number_work<evaluate(exp_work))?
				number_work:evaluate(exp_work));
		case '^':	/*	power of	*/
			return(pow(number_work,evaluate(exp_work)));
		default:	/*	invalid operation	*/
			printf("\ninvalid operation '%c' ",op_work);
			return(number_work);
		}
	}
