Mae向きなブログ

Mae向きな情報発信を続けていきたいと思います。

mfcalc

昨日に引き続き、bisonを学習。
http://www.mi.s.osakafu-u.ac.jp/~kada/course-kitami/j3_03/bison.pdf
の「2.5 練習問題」をやってみました。

(1),(2)は解けたのですが、

(3)初期化されていない変数について、値を書き込むのではなく、値を使おうとするとエラーを報告するようにプログラムを改良しなさい。

という問題でつまずいています。

(1),(2)までの解答は以下です。
mfcalc2.y

%{
#include <math.h>
#include "calc.h"
%}

%union {
double val;
symrec *tptr;
}

%token <val>  NUM
%token <tptr> VAR FNCT
%type <val>  exp

%right '='
%left '-' '+'
%left '*' '/'
%left NEG
%right '^'

%%
input:   /* 空 */
        | input line
;

line:
        '\n'
        | exp '\n'   { printf ("\t%.10g\n", $1); }
        | error '\n' { yyerrok; }
;

exp:      NUM                { $$ = $1;                         }
        | VAR                { $$ = $1->value.var;              }
        | VAR '=' exp        { $$ = $3; $1->value.var = $3;     }
        | FNCT '(' exp ')'   { $$ = (*($1->value.fnctptr))($3); }
        | exp '+' exp        { $$ = $1 + $3;                    }
        | exp '-' exp        { $$ = $1 - $3;                    }
        | exp '*' exp        { $$ = $1 * $3;                    }
        | exp '/' exp        { $$ = $1 / $3;                    }
        | '-' exp  %prec NEG { $$ = -$2;                        }
        | exp '^' exp        { $$ = pow($1, $3);                }
        | '(' exp ')'        { $$ = $2;                         }
;

%%

#include <stdio.h>

int main()
{
  init_table();
  yyparse();
  return 0;
}

void yyerror(char *s)
{
  printf("%s\n", s);
}

/* (2) init => init_f  */
struct init_f
{
  char *fname;
  double (*fnct)();
};

/* (2) */
struct init_v
{
  char *vname;
  double var;
};

struct init_f arith_fncts[] = { 
  "sin", sin,
  "cos", cos,
  "atan", atan,
  "ln", log,
  "exp", exp,
  "sqrt", sqrt,
  "fabs", fabs,                 /* (1) */
  0, 0
};

struct init_v const_vars[] = {    /* (2) */
  "PI", 3.14159,
  "E",  2.71828,
  0, 0
};

symrec *sym_table = (symrec *)0;

void init_table()
{
  int i;
  symrec *ptr;
  for (i = 0; arith_fncts[i].fname != 0; i++) {
    ptr = putsym(arith_fncts[i].fname, FNCT);
    ptr->value.fnctptr = arith_fncts[i].fnct;
  }
  for (i = 0; const_vars[i].vname != 0; i++) { /* (2) */
    ptr = putsym(const_vars[i].vname, VAR);
    ptr->value.var = const_vars[i].var;
  }
}

symrec *putsym(char *sym_name, int sym_type)
{
  symrec *ptr;
  ptr = (symrec *)malloc(sizeof(symrec));
  ptr->name = (char *)malloc(strlen(sym_name) + 1);
  strcpy(ptr->name, sym_name);
  ptr->type = sym_type;
  ptr->value.var = 0;
  ptr->next = (struct symrec *)sym_table;
  sym_table = ptr;
  return ptr;
}

symrec *getsym(char *sym_name)
{
  symrec *ptr;
  for (ptr = sym_table; ptr != (symrec *)0; ptr = (symrec *)ptr->next)
    if (strcmp(ptr->name,sym_name) == 0)
      return ptr;
  return 0;
}

#include <ctype.h>

int yylex()
{
  int c;

  while ((c = getchar ()) == ' ' || c == '\t');

  if (c == EOF)
    return 0;

  if (c == '.' || isdigit (c)) {
    ungetc(c, stdin);
    scanf("%lf", &yylval.val);
    return NUM;
  }

  if (isalpha (c)) {
    symrec *s;
    static char *symbuf = 0;
    static int length = 0;
    int i;
    
    if (length == 0)
      length = 40, symbuf = (char *)malloc (length + 1);

    i = 0;
    do {
      if (i == length) {
        length *= 2;
        symbuf = (char *)realloc(symbuf, length + 1);
      }
      symbuf[i++] = c;
      c = getchar();
    } while (c != EOF && isalnum(c));
    ungetc(c, stdin);
    symbuf[i] = '\0';

    s = getsym(symbuf);
    if (s == 0) 
      s = putsym(symbuf, VAR);
    yylval.tptr = s;
    return s->type;
  }
  return c;
}

calc.h

struct symrec
{
  char *name;
  int type;
  union {
    double var;
    double (*fnctptr)();
  } value;
  struct symrec *next;
};

typedef struct symrec symrec;

extern symrec *sym_table;

symrec *putsym ();
symrec *getsym ();