#!/bin/sh
# This is a shar archive.
# The rest of this file is a shell script which will extract:
#
# 3_13.c 3_13.cmp 3_13.in 3_13a.c 3_13a1.c 3_13a2.c 3_13b.c 3_13b.cmp 3_13c.c 3_13d.c 3_13e.c 3_13f.c 3_13g.c 3_13h.c makefile
#
# To extract the files from this shell archive file simply
# create a directory for this file, move the archive file
# to it and enter the command
#
#	sh filename
# 
# The files will be extracted automatically.
# Note: Do not use csh.
#
# Archive created: Mon Jul 30 22:58:09 EDT 1990
#
echo x - 3_13.c
sed 's/^X//' > 3_13.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
#include <stream.h>
#include <string.h>
#include <stdlib.h>
#ifndef RAND_MAX
# define RAND_MAX 32767
#endif
#include <ctype.h>

int no_of_errors;

double error(char *s)
{
    cerr << "error: " << s << "\n";
    no_of_errors++;
    return 1;
}

#include "3_13a.c"

const TBLSZ = 23;
name *table[TBLSZ];

int lookhash(char *p)
{
    int ii = 0;
    while (*p) ii = ii << 1 ^ *p++;
    if (ii < 0) ii = -ii;
    return ii % TBLSZ;
}

name *look(char *p, int ins=0)
{
    int ii = lookhash(p);
    
    for (name *n = table[ii]; n; n=n->next)
	if (strcmp(p, n->string) == 0)
	    return n;
    
    if (ins == 0) error("name not found");
    
    name *nn = new name;
    nn->string = new char[strlen(p)+1];
    strcpy(nn->string, p);
    nn->next = table[ii];
#include "3_13c.c"		/* EXPAND4 */
    nn->value = 1;
    table[ii] = nn;
    return nn;
}

inline name *insert(char *s) { return look(s, 1); }

enum token_value
{
    NAME,	NUMBER,		END,		COMMA=',',
    PLUS='+',	MINUS='-',	MUL='*',	DIV='/',
    PRINT=';',	ASSIGN='=',	LP='(',		RP=')'
};

token_value curr_tok;
double number_value;
const int namesize = 100;
char name_string[namesize];
token_value get_token();
double expr(), term(), prim();

double expr()
{
    double left = term();
    for (;;)
	{
	switch (curr_tok)
	    {
	    case PLUS: get_token();
	        left += term();
		break;

	    case MINUS: get_token();
	        left -= term();
		break;

	    default:
	        return left;
	    }
	}
}

double term()
{
    double left = prim();
    for (;;)
	{
	switch (curr_tok) 
	    {
	    case MUL: get_token();
	        left *= prim();
		break;

	    case DIV:
	        get_token();
		double d = prim();
		if (d == 0) return error("divide by 0");
		left /= d;
		break;

	    default:
	        return left;
	    }
	}
}

double prim()
{
    switch (curr_tok) 
	{
	case NUMBER:
	    get_token();
	    return number_value;
#include "3_13b.c"

	case MINUS:
	    get_token();
	    return -prim();

	case LP:
	    get_token();
	    double e = expr();
	    if (curr_tok != RP) return error(") expected");
	    get_token();
	    return e;

	case END:
	    return 1;

	default:
	    return error("primary expected");
	}
}

token_value get_token()
{
    char ch;
    do  {
	if (!cin.get(ch)) return curr_tok = END;
    } while (ch != '\n' && isspace(ch));

    switch (ch)
	{
	case ';': case '\n':
	    cin >> WS;
	    return curr_tok = PRINT;

	case '*': case '/': case '+': case '-': case '(': case ')':
	case '=': case ',':
	    return curr_tok = ch;

	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	case '.':
	    cin.putback(ch);
	    cin >> number_value;
	    return curr_tok = NUMBER;

	default:
	    if (isalpha(ch))
		{
		char *p = name_string;
		*p++ = ch;
		while (cin.get(ch) && isalnum(ch) && p < &name_string[namesize-1])
		    *p++ = ch;
		if (p == &name_string[namesize])
		    while (cin.get(ch) && isalnum(ch))
		        ;
		cin.putback(ch);
		*p = 0;
		return curr_tok = NAME;
		}

	    error("bad token");
	    return curr_tok = PRINT;
	}
}

#include "3_13e.c"

static double doexp(double e[]) { return exp(e[0]); }
static double docos(double e[]) { return cos(e[0]); }
static double dotan(double e[]) { return tan(e[0]); }
static double doatan(double e[]) { return atan(e[0]); }

static double dolog10(double e[])
{
    if (e[0] <= 0)	return error("log10: requires non-positive number");
    else	return log10(e[0]);
}

static double doexp10(double e[])
{
    return pow(10, e[0]);
}

static double doasin(double e[])
{
    if (e[0] > 1 || e[0] < -1)	return error("asin: requires |x| <= 1.0");
    else			return asin(e[0]);
}

static double doacos(double e[])
{
    if (e[0] > 1 || e[0] < -1)	return error("acos: requires |x| <= 1.0");
    else			return acos(e[0]);
}

static double dopow(double e[])
{
    double x = e[0];
    double y = e[1];
    if (x == 0 && y <= 0)	return error("pow: x == 0 requires y > 0");
    else if (x < 0 && y != (double)(int)y)
    				return error("pow: x < 0 requires int y");
    else			return pow(x, y);
}

#include "3_13d.c"

int main(int argc, char **argv)
{
    switch (argc) 
	{
	case 1: break;
	case 2: cin = *new istream(strlen(argv[1]), argv[1]); break;
	default: error("too many arguments"); return 1;
	}
    
    // initialize known variables
    insert("pi")->value = 3.1415926535897932385;
    insert("e")->value = 2.7182818284590452354;

#include "3_13f.c"		/* EXPAND4 */
    insertfunction("exp", 1, (d_f_va)doexp);
    insertfunction("log10", 1, (d_f_va)dolog10);
    insertfunction("pow", 2, (d_f_va)dopow);
    insertfunction("cos", 1, (d_f_va)docos);
    insertfunction("tan", 1, (d_f_va)dotan);
    insertfunction("asin", 1, (d_f_va)doasin);
    insertfunction("acos", 1, (d_f_va)doacos);
    insertfunction("atan", 1, (d_f_va)doatan);
    
    while (cin)
	{
	get_token();
	if (curr_tok == END) break;
	if (curr_tok == PRINT) continue;
	cout << expr() << "\n";
	}
    
    return no_of_errors;
}
!EOF!
ls -l 3_13.c
echo x - 3_13.cmp
sed 's/^X//' > 3_13.cmp << '!EOF!'
8
-282.447
374.231
-374.231
3.14159
2.71828
3.36159e+162
2.57314
9.37959e+22
0.731689
0.931596
0.848062
0.722734
0.643501
0.866025
-0.287682
0.681639
55.0681
!EOF!
ls -l 3_13.cmp
echo x - 3_13.in
sed 's/^X//' > 3_13.in << '!EOF!'
3 + 5
39.547 - 321.993921
foo = 374.2312
0 - foo
pi
e
exp(foo)
log10(foo)
pow(34, 15)
cos(.75)
tan(.75)
asin(.75)
acos(.75)
atan(.75)
sqrt(.75)
log(.75)
sin(.75)
random(34, 75)
!EOF!
ls -l 3_13.in
echo x - 3_13a.c
sed 's/^X//' > 3_13a.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
typedef double (*d_f_va)(double[]);
const maxargs = 9;
const variable = -1;

struct name
{
    char *string;
    name *next;
    int nargs;
    union
        {
	double value;
        d_f_va funcptr;
	};
};
!EOF!
ls -l 3_13a.c
echo x - 3_13a1.c
sed 's/^X//' > 3_13a1.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
enum token_value
{
    NAME,       NUMBER,      END,      COMMA=',',
    PLUS='+',   MINUS='-',   MUL='*',  DIV='/',
    PRINT=';',  ASSIGN='=',  LP='(',   RP=')'
};
!EOF!
ls -l 3_13a1.c
echo x - 3_13a2.c
sed 's/^X//' > 3_13a2.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
case '*': case '+': case '(':	// same as before
case '/': case '-': case ')':
case '=':
case ',':		// new
    return curr_tok=ch;
!EOF!
ls -l 3_13a2.c
echo x - 3_13b.c
sed 's/^X//' > 3_13b.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
case NAME:
    if (get_token() == ASSIGN)	// same as before
	{
	name *n = insert(name_string);
	get_token();
	n->value = expr();
	return n->value;
	}

    else if (curr_tok == LP)	// function calls
	{
	name *n = look(name_string);
	if (n->nargs == variable)
	    return error("not a function");
	get_token();	// skip LP

	double e[maxargs];
	int i = 0;
	if (curr_tok != RP)
	    e[i++] = expr();

	while (curr_tok == COMMA)
	    {
	    get_token();	// skip COMMA
	    e[i++] = expr();
	    if (i == maxargs)
		break;
	    }

	if (curr_tok != RP)
	    return error(") expected");
	get_token();		// skip RP
	if (n->nargs != i)
	    return error("wrong number of arguments");

	return (*n->funcptr)(e);
	}

    else
	{
	name *n = look(name_string);
	if (n->nargs == variable)
	    return n->value;
	else
	    return error("( expected");
	}
!EOF!
ls -l 3_13b.c
echo x - 3_13b.cmp
sed 's/^X//' > 3_13b.cmp << '!EOF!'
909.542
41.5882
!EOF!
ls -l 3_13b.cmp
echo x - 3_13c.c
sed 's/^X//' > 3_13c.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
nn->nargs = variable;
!EOF!
ls -l 3_13c.c
echo x - 3_13d.c
sed 's/^X//' > 3_13d.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
void insertfunction(char *funcname,
    int nargs, d_f_va funcptr)
{
    name *n = insert(funcname);
    n->nargs = nargs;
    n->funcptr = funcptr;
}
!EOF!
ls -l 3_13d.c
echo x - 3_13e.c
sed 's/^X//' > 3_13e.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
/* declare helper functions for */
/* sqrt(), log(), sin() and random() */
#include <math.h>

// return the square root of e0
static double dosqrt(double e[])
{
    return (e[0] < 0) ?
	error("sqrt: requires non-negative number") :
	sqrt(e[0]);
}

// return the natural log of e0
static double dolog(double e[])
{
    return (e[0] <= 0) ?
	error("log: requires positive number") :
	log(e[0]);
}

// return the sin of e0
static double dosin(double e[])
{
    return sin(e[0]);
}

// return a random number between [e0, e1)
static double dorandom(double e[])
{
    return rand() / ( (double) RAND_MAX+1) *
	(e[1] - e[0]) + e[0];
}
!EOF!
ls -l 3_13e.c
echo x - 3_13f.c
sed 's/^X//' > 3_13f.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
// initialize known functions
insertfunction("sqrt", 1, dosqrt);
insertfunction("log", 1, dolog);
insertfunction("sin", 1, dosin);
insertfunction("random", 2, dorandom);
!EOF!
ls -l 3_13f.c
echo x - 3_13g.c
sed 's/^X//' > 3_13g.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
#include <math.h>

// return e to the power of e0
static double doexp(double e[])
{
    return exp(e[0]);
}

// return the log base 10 of e0
static double dolog10(double e[])
{
    return (e[0] <= 0) ?
	error("log10: requires positive number") :
	log10(e[0]);
}

// return e0 to the power of e1
static double dopow(double e[])
{
    if ((e[0] == 0) && (e[1] <= 0))
	return error("pow: if x==0, y must be >0");

    if ((e[0] < 0) && (int(e[1]) != e[1]))
	return error("pow: if x<0, y must be integral");

    return pow(e[0], e[1]);
}

// return the cosine of e0
static double docos(double e[])
{
    return cos(e[0]);
}

// return the tangent of e0
static double dotan(double e[])
{
    return tan(e[0]);
}

// return the arc sine of e0
static double doasin(double e[])
{
    return ((e[0] > -1) && (e[0] < 1)) ?
	asin(e[0]) :
	error("asin: requires number between -1 and 1");
}

// return the arc cosine of e0
static double doacos(double e[])
{
    return ((e[0] > -1) && (e[0] < 1)) ?
	acos(e[0]) :
	error("acos: requires number between -1 and 1");
}

// return the arc tangent of e0
static double doatan(double e[])
{
    return atan(e[0]);
}
!EOF!
ls -l 3_13g.c
echo x - 3_13h.c
sed 's/^X//' > 3_13h.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
// initialize more functions
insertfunction("exp", 1, exp);
insertfunction("log10", 1, dolog10);
insertfunction("pow", 2, dopow);
insertfunction("cos", 1, cos);
insertfunction("tan", 1, tan);
insertfunction("asin", 1, doasin);
insertfunction("acos", 1, doacos);
insertfunction("atan", 1, atan);
!EOF!
ls -l 3_13h.c
echo x - makefile
sed 's/^X//' > makefile << '!EOF!'
CC= CC -I. -I../../CC
all: 3_13

3_13: 3_13.c 3_13a.c 3_13c.c 3_13b.c 3_13e.c 3_13d.c 3_13f.c
	$(CC) -g +i 3_13.c -o3_13 -lm

test: all 3_13.cmp 3_13.in 3_13b.cmp
	3_13 < 3_13.in > 3_13.out
	cmp 3_13.out 3_13.cmp
	3_13 "57.893 + 283.8829 * 3; 47 - 92 / 17" > 3_13b.out
	cmp 3_13b.out 3_13b.cmp
	echo tests done
!EOF!
ls -l makefile
# The following exit is to ensure that extra garbage 
# after the end of the shar file will be ignored.
exit 0