| %{
 #include <math.h>
 #include <stdio.h>
 
 #define FALSE  (0)
 #define TRUE   (1)
 
 int yylex(void);
 
 
 void yyerror(const char* s) {
 printf("%s\n", s);
 }
 
 
 typedef enum {
 VAR_DOUBLE,
 VAR_FNCTPTR,
 } VarType;
 
 
 typedef struct {
 VarType type;
 union {
 double d;
 double (*fnctptr)(double);
 } u;
 } Value;
 
 
 typedef struct symrec {
 struct symrec* next;
 char* name;
 } symrec;
 
 int refer(Value*, symrec*);
 void assign(const char*, Value);
 int funcall(Value*, Value, Value);
 int arith1(Value*, Value, double (*)(double));
 int arith2(Value*, Value, Value, double (*)(double, double));
 double add(double x, double y)  { return x + y; }
 double sub(double x, double y)  { return x - y; }
 double mul(double x, double y)  { return x * y; }
 double div(double x, double y)  { return x / y; }
 double neg(double x)  { return -x; }
 void dump_value(FILE*, Value);
 
 %}
 
 %union {
 Value val;
 symrec* sym;
 }
 
 %token <val>  VAL
 %token <sym>  SYM
 %type  <val>  exp prim
 
 
 %right '='
 %left  '+' '-'
 %left  '*' '/'
 %left  UNARY_NEG
 %left  '^'
 
 %%
 
 input
 :
 | input line
 ;
 
 line
 : '\n'
 | exp '\n'    { putchar('\t'); dump_value(stdout, $1); putchar('\n'); }
 | error '\n'  { yyerrok; }
 ;
 
 exp
 : prim                     { $$ = $1; }
 | SYM '=' exp              { assign($1->name, $$ = $3); }
 | exp '+' exp              { if (!arith2(&$$, $1, $3, add)) YYERROR; }
 | exp '-' exp              { if (!arith2(&$$, $1, $3, sub)) YYERROR; }
 | exp '*' exp              { if (!arith2(&$$, $1, $3, mul)) YYERROR; }
 | exp '/' exp              { if (!arith2(&$$, $1, $3, div)) YYERROR; }
 | '-' exp %prec UNARY_NEG  { if (!arith1(&$$, $2, neg)) YYERROR; }
 | exp '^' exp              { if (!arith2(&$$, $1, $3, pow)) YYERROR; }
 | '(' exp ')'              { $$ = $2; }
 ;
 
 prim
 : VAL               { $$ = $1; }
 | SYM               { if (!refer(&$$, $1)) YYERROR; }
 | prim '(' exp ')'  { if (!funcall(&$$, $1, $3)) YYERROR; }
 ;
 
 %%
 
 #include <ctype.h>
 #include <malloc.h>
 #include <string.h>
 
 symrec* sym_table = NULL;
 
 symrec* putsym(const char* sym_name) {
 symrec* ptr = (symrec*)malloc(sizeof(symrec));
 ptr->name = (char*)malloc(strlen(sym_name) + 1);
 strcpy(ptr->name, sym_name);
 ptr->next = sym_table;
 sym_table = ptr;
 return ptr;
 }
 
 symrec* getsym(const char* sym_name) {
 symrec* ptr;
 for (ptr = sym_table; ptr != NULL; ptr = ptr->next) {
 if (strcmp(ptr->name, sym_name) == 0)
 return ptr;
 }
 return NULL;
 }
 
 static int skip_spaces() {
 int c;
 while (c = getchar(), c == ' ' || c == '\t');
 return c;
 }
 
 static int read_symbol(int c) {
 static char* buf;
 static int length;
 symrec* s;
 int i = 0;
 do {
 if (i >= length) {
 if (length == 0)    length = 16/2;
 buf = (char*)realloc(buf, (length *= 2) + 1);
 }
 buf[i++] = c;
 c = getchar();
 } while (c != EOF && isalnum(c));
 ungetc(c, stdin);
 buf[i] = '\0';
 
 s = getsym(buf);
 if (s == NULL)
 s = putsym(buf);
 yylval.sym = s;
 return SYM;
 }
 
 
 int yylex(void) {
 int c = skip_spaces();
 if (c == EOF)    return 0;
 if (c == '.' || isdigit(c)) {
 ungetc(c, stdin);
 scanf("%lf", &yylval.val.u.d);
 yylval.val.type = VAR_DOUBLE;
 return VAL;
 }
 if (isalpha(c)) {
 return read_symbol(c);
 }
 
 return c;
 }
 
 void set_double(Value* p, double x)  { p->type = VAR_DOUBLE; p->u.d = x; }
 void set_fnctptr(Value* p, double (*f)(double))  { p->type = VAR_FNCTPTR; p->u.fnctptr = f; }
 
 int arith1(Value* result, Value a, double (*f)(double)) {
 if (a.type != VAR_DOUBLE) {
 fprintf(stderr, "Can't operate arithmetic: ");
 dump_value(stderr, a);
 fprintf(stderr, "\n");
 return FALSE;
 } else {
 set_double(result, f(a.u.d));
 return TRUE;
 }
 }
 
 int arith2(Value* result, Value a, Value b, double (*f)(double, double)) {
 if (a.type != VAR_DOUBLE) {
 fprintf(stderr, "Can't operate arithmetic: ");
 dump_value(stderr, a);
 fprintf(stderr, "\n");
 return FALSE;
 } else if (b.type != VAR_DOUBLE) {
 fprintf(stderr, "Can't operate arithmetic: ");
 dump_value(stderr, b);
 fprintf(stderr, "\n");
 return FALSE;
 } else {
 set_double(result, f(a.u.d, b.u.d));
 return TRUE;
 }
 }
 
 void dump_value(FILE* ofp, Value v) {
 switch (v.type) {
 case VAR_DOUBLE:  fprintf(ofp, "%.10g", v.u.d); break;
 case VAR_FNCTPTR:  fprintf(ofp, "function"); break;
 default:  fprintf(ofp, "?? Illegal value"); break;
 }
 }
 
 #include <map>
 #include <string>
 using namespace std;
 
 static map<string, Value> GlobalVariableTable;
 
 int refer(Value* result, symrec* sym) {
 map<string, Value>::const_iterator it = GlobalVariableTable.find(sym->name);
 if (it == GlobalVariableTable.end()) {
 fprintf(stderr, "unbound `%s'\n", sym->name);
 return FALSE;
 } else {
 *result = GlobalVariableTable[sym->name];
 return TRUE;
 }
 }
 
 void assign(const char* name, Value val) {
 GlobalVariableTable[name] = val;
 }
 
 int funcall(Value* result, Value fnct, Value x) {
 if (fnct.type != VAR_FNCTPTR) {
 fprintf(stderr, "Can't call: ");
 dump_value(stderr, fnct);
 fprintf(stderr, "\n");
 return FALSE;
 } else if (x.type != VAR_DOUBLE) {
 fprintf(stderr, "Illegal argument: ");
 dump_value(stderr, x);
 fprintf(stderr, "\n");
 return FALSE;
 } else {
 set_double(result, fnct.u.fnctptr(x.u.d));
 return TRUE;
 }
 }
 
 
 void init_table() {
 struct {
 const char* fname;
 double (*fnct)(double);
 } static const arith_fncts[] = {
 { "sin",   sin,  },
 { "cos",   cos,  },
 { "atan",  atan, },
 { "ln",    log,  },
 { "exp",   exp,  },
 { "sqrt",  sqrt, },
 { 0, 0 }
 };
 
 int i;
 for (i = 0; arith_fncts[i].fname != NULL; ++i) {
 Value v;
 set_fnctptr(&v, arith_fncts[i].fnct);
 assign(arith_fncts[i].fname, v);
 }
 }
 
 int main() {
 init_table();
 yyparse();
 return 0;
 }
 
 |