#!/bin/sh # This is a shar archive. # The rest of this file is a shell script which will extract: # # 3_14.c 3_14.cmp 3_14.in 3_14.in2 3_14a.c 3_14a2.c 3_14b.c 3_14b.cmp 3_14b.in 3_14c.c 3_14c2.c 3_14d.c 3_14d1.c 3_14d2.c 3_14e.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:19 EDT 1990 # echo x - 3_14.c sed 's/^X//' > 3_14.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ #include #include #include #ifndef RAND_MAX # define RAND_MAX 32767 #endif #include int no_of_errors; double error(char *s) { cerr << "error: " << s << "\n"; no_of_errors++; return 1; } #include "3_14b.c" // d_f_va, maxargs, variable, interp_func, struct name 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]; nn->nargs = variable; // #include "3_13c.c" /* EXPAND4 */ nn->value = 1; table[ii] = nn; return nn; } inline name *insert(char *s) { return look(s, 1); } #include "3_14a.c" // enum token_value { NAME, ... }; #include "3_14d1.c" // double *curfuncargs = 0; 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() { //cerr << "expr()\n"; /* DELETE */ 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() { //cerr << "term()\n"; /* DELETE */ 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; } } } #include "3_14c2.c" // char *get_funcdefn() #include "3_14e.c" // double interpfunc(double*, char*) double prim() { //cerr << "prim(): curr_tok = " << chr(curr_tok) << "\n"; /* DELETE */ switch (curr_tok) { case NUMBER: //cerr << "prim(): case NUMBER:\n"; /* DELETE */ get_token(); return number_value; #include "3_14d2.c" // case DOL: case NAME: //cerr << "prim(): case NAME:\n"; /* DELETE */ get_token(); //cerr << "prim(): curr_tok = " << chr(curr_tok) << "\n"; /* DELETE */ if (curr_tok == ASSIGN) // same as before { //cerr << "prim: case NAME: ASSIGN\n"; /* DELETE */ name *n = insert(name_string); get_token(); n->value = expr(); return n->value; } #include "3_14d.c" // else if (curr_tok == LP) #include "3_14c.c" // else if (curr_tok == LB) else // same as before { //cerr << "prim: case NAME: LOOKUP\n"; /* DELETE */ name *n = look(name_string); if (n->nargs == variable) return n->value; else return error("( expected"); } case MINUS: //cerr << "prim(): case MINUS:\n"; /* DELETE */ get_token(); return -prim(); case LP: //cerr << "prim(): case LP:\n"; /* DELETE */ get_token(); double e = expr(); if (curr_tok != RP) return error(") expected"); get_token(); return e; case END: //cerr << "prim(): case END:\n"; /* DELETE */ return 1; default: //cerr << "prim(): default:\n"; /* DELETE */ 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; #include "3_14a2.c" 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; } } /* declare helper functions for */ /* sqrt(), log(), sin() and random() */ #include // 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]; } 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); } void insertfunction(char *funcname, int nargs, d_f_va funcptr) { name *n = insert(funcname); n->nargs = nargs; n->funcptr = funcptr; } 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; // initialize known functions insertfunction("sqrt", 1, (d_f_va)dosqrt); insertfunction("log", 1, (d_f_va)dolog); insertfunction("sin", 1, (d_f_va)dosin); insertfunction("random", 2, (d_f_va)dorandom); 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_14.c echo x - 3_14.cmp sed 's/^X//' > 3_14.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_14.cmp echo x - 3_14.in sed 's/^X//' > 3_14.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_14.in echo x - 3_14.in2 sed 's/^X//' > 3_14.in2 << '!EOF!' bar { $0 * 15 } bar(5) !EOF! ls -l 3_14.in2 echo x - 3_14a.c sed 's/^X//' > 3_14a.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='N', NUMBER='#', END='E', COMMA=',', PLUS='+', MINUS='-', MUL='*', DIV='/', PRINT=';', ASSIGN='=', LP='(', RP=')', LB='{', RB='}', DOL='$' }; !EOF! ls -l 3_14a.c echo x - 3_14a2.c sed 's/^X//' > 3_14a2.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ case '*': case '+': case '(': case '=': // same as before case '/': case '-': case ')': case ',': case '{': case '}': // new return curr_tok=ch; case '$': // new, look for function args if (!cin.get(ch)) return curr_tok = END; if (!isdigit(ch)) { error("$ must be followed by digit"); return curr_tok = PRINT; } number_value = ch - '0'; return curr_tok = DOL; !EOF! ls -l 3_14a2.c echo x - 3_14b.c sed 's/^X//' > 3_14b.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; const interp_func = -2; struct name { char *string; name *next; int nargs; union { double value; d_f_va funcptr; char *funcdefn; }; }; !EOF! ls -l 3_14b.c echo x - 3_14b.cmp sed 's/^X//' > 3_14b.cmp << '!EOF!' 0 75 88 3375 !EOF! ls -l 3_14b.cmp echo x - 3_14b.in sed 's/^X//' > 3_14b.in << '!EOF!' bar { $0 * 15 } x = bar(5) 13 + x bar(3*x) !EOF! ls -l 3_14b.in echo x - 3_14c.c sed 's/^X//' > 3_14c.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ // case NAME: // ... else if (curr_tok == LB) // function definition { //cerr << "prim: case NAME: LB\n"; /* DELETE */ name *n = insert(name_string); n->nargs = interp_func; n->funcdefn = get_funcdefn(); return 0; } !EOF! ls -l 3_14c.c echo x - 3_14c2.c sed 's/^X//' > 3_14c2.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ #include char *get_funcdefn() { const chunksize = 128; int slen = chunksize; char *s = new char[slen]; char *sp = s; char ch; for (;;) // loop until end of definition { if (!cin.get(ch)) // oops, EOF { curr_tok = END; return 0; } if (ch == LB) // another oops { error("no recursively defined functions"); curr_tok = PRINT; return 0; } if (ch == RB) // found end { *sp = '\0'; (void) get_token(); // skip RB return s; } *sp++ = ch; if (sp - s >= slen) // check for buffer end { // and reallocate space int oslen = slen; slen += chunksize; char *t = new char[slen]; memcpy(t, s, oslen); delete [oslen] s; s = t; sp = s + oslen; } } } !EOF! ls -l 3_14c2.c echo x - 3_14d.c sed 's/^X//' > 3_14d.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ // case NAME: // ... else if (curr_tok == LP) // function calls { //cerr << "prim: case NAME: LP\n"; /* DELETE */ 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 == interp_func) // function defn { while (i < maxargs) e[i++] = 0; return interpfunc(e, n->funcdefn); } else if (n->nargs != i) return error("wrong number of arguments"); else // built-in function return (*n->funcptr)(e); } !EOF! ls -l 3_14d.c echo x - 3_14d1.c sed 's/^X//' > 3_14d1.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ double *curfuncargs = 0; !EOF! ls -l 3_14d1.c echo x - 3_14d2.c sed 's/^X//' > 3_14d2.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ case DOL: // look up func. arg. value get_token(); if (curfuncargs) return curfuncargs[(int)number_value]; else return error("function not active for $n"); !EOF! ls -l 3_14d2.c echo x - 3_14e.c sed 's/^X//' > 3_14e.c << '!EOF!' /* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */ /* The C++ Answer Book */ /* Tony Hansen */ /* All rights reserved. */ // Interpret the function whose definition // is stored in the string funcdefn, using the // arguments newargs. #ifdef STRSTREAM #include #endif double interpfunc(double *newargs, char *funcdefn) { //cerr << "interpfunc(" << funcdefn << ")\n"; /* DELETE */ // save old values istream savecin = cin; token_value savecurr_tok = curr_tok; double *saveargs = curfuncargs; // set new values #ifdef STRSTREAM strstreambuf str(strlen(funcdefn), funcdefn); istream ncin(&str); cin = ncin; #else istream *ncin = new istream(strlen(funcdefn), funcdefn); cin = *ncin; #endif curfuncargs = newargs; // interpret the string double val = 0; while (cin) { get_token(); if (curr_tok == END) break; if (curr_tok == PRINT) continue; val = expr(); } //cerr << "interpfunc() returns " << val << "\n"; /* DELETE */ // restore the old values #ifndef STRSTREAM delete ncin; #endif curfuncargs = saveargs; curr_tok = savecurr_tok; cin = savecin; return val; } !EOF! ls -l 3_14e.c echo x - makefile sed 's/^X//' > makefile << '!EOF!' CC= CC -I. -I../../CC all: 3_14 3_14: 3_14.c 3_14b.c 3_14a.c 3_14d1.c 3_14c2.c 3_14e.c 3_14d2.c 3_14d.c 3_14c.c 3_14a2.c $(CC) 3_14.c -o 3_14 -lm test: all 3_14.cmp 3_14b.cmp 3_14 < 3_14.in > 3_14.out cmp 3_14.out 3_14.cmp 3_14 < 3_14b.in > 3_14b.out cmp 3_14b.out 3_14b.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