M.Hiroi's Home Page

Linux Programming

お気楽C言語プログラミング超入門:番外編

[ PrevPage | Clang | NextPage ]

続・電卓プログラムの作成 (5)

今回は複数の式を順番に実行する begin 式と、処理を繰り返し実行する while 式を追加してみましょう。

●begin 式と while 式

最初に、begin と while の構文を示します。

begin 式1, 式2, ..., 式n end
while 条件式 do 式 end

begin は複数の式を順番に評価し、最後に評価した式の返り値が begin の値になります。機能は Scheme の begin と同じです。while は条件式を評価して、その値が真であれば本体の式を繰り返し評価します。条件式が偽の場合は本体の式を評価しないで 0 を返します。

文法を EBNF で表すと次のようになります。

[EBNF]
   文    = 関数定義 | 式.
関数定義 = "def", 関数, "(", [仮引数リスト], ")", 式, "end".
   式    = 代入式 | 式1.
 代入式  = 変数, "=", 式.
  式1   = 式2, { ("and" | "or"), 式2}.
  式2   = 式3, ("==" | "!=" | "<" | "<=" | ">" | ">="), 式3.
  式3   = 項, { ("+" | "-"), 項 }.
   項    = 因子, { ("*" | "/"), 因子 }.
  因子   = 数値 | ("+" | "-" | "not"), 因子 | "(", 式, ")" | 変数 | 関数, "(", [引数リスト], ")" |
           if式 | begin式 | while式.
  if式   = "if", 式, "then", 式, ["else", 式], "end".
 begin式 = "begin", 式, { ",", 式 }, "end".
 while式 = "while", 式, "do", 式, "end".
  変数   = 識別子
  関数   = 識別子

仮引数リスト = 変数, { ",", 変数 }.
引数リスト   = 式, { ",", 式 }.

[注意] 数値と識別子の定義は省略

begin は式を連結リストに格納して、構文木 Expr の ref_value にセットします。タグは Bgn とします。while は条件式を構文木 Expr のメンバ変数 left に、本体の式を right に格納します。タグは Whl2 とします。トークンには while 式を表す While と do を、begin 式を表す Begin を追加します。

字句解析を行う関数 get_token の修正は簡単なので説明は割愛します。詳細は プログラムリスト をお読みください。

●構文解析の修正

begin と while の構文解析は関数 factor で行います。

リスト : 因子の処理

Expr *factor(void)
{
  Symbol *sym;
  switch (token) {

  ・・・ 省略 ・・・

  case Begin:
    get_token();
    return make_begin(get_expr());
  case While:
    {
      get_token();
      Expr *test = expression();
      if (token != Do) error("Do expected");
      get_token();
      Expr *body = expression();
      if (token != End) error("End expected");
      get_token();
      return make_op2(Whl2, test, body);
    }

  ・・・ 省略 ・・・

  }
}

トークンが Begin の場合は関数 get_expr で式を取得して、それを関数 make_begin に渡して、begin 式を表す構文木を生成します。get_expr はあとで説明します。

トークンが While の場合、最初に expression で条件式を取り出し、変数 test にセットします。そして、トークンが Do であることを確認したら、expression で while 式の本体を取り出し、変数 body にセットします。最後にトークンが End であることを確認して、make_op2 で While 式を表す構文木を生成して返します。do や end がない場合はエラーを送出します。

関数 get_expr は次のようになります。

リスト : カンマで区切られた式を取得

Cell *get_expr()
{
  Cell *xs = NULL;
  if (token == End) error("begin: empty expression");
  while (true) {
    xs = cons(expression(), xs);
    if (token == End) {
      get_token();
      return reverse(xs);
    } else if (token == Comma) {
      get_token();
    } else {
      error("begin: unexpected token");
    }
  }
}

token が End の場合、式が無いのでエラーを送出します。そうでなければ、while ループの中で、式を expression で取得して、連結リストに格納します。このとき、式は逆順に格納されることに注意してください。token が End であれば、関数 reverse で連結リストを反転してから return で返します。token が Comma であれば、get_token で Comma を読み飛ばして、次の式を取得します。それ以外の場合はエラーを送出します。

リストを反転する関数 reverse は簡単です。次のリストを見てください。

リスト : リストの反転

Cell *reverse(Cell *xs)
{
  Cell *ys = NULL;
  while (xs != NULL) {
    ys = cons(car(xs), ys);
    xs = cdr(xs);
  }
  return ys;
}

引数のリスト xs の要素を順番に取り出して、リスト ys の先頭に追加していくだけです。これで xs を反転することができます。

●eval の修正

最後に、式を評価する関数 eval_expr を修正します。

リスト : 式の評価

double eval(Expr *e, Env *env)
{
  Symbol *sym;
  Env *env1;
  switch (e->tag) {

  ・・・ 省略 ・・・

  case Bgn:
    {
      Cell *xs = e->ref_value;
      double val = 0;
      while (xs != NULL) {
	val = eval(car(xs), env);
	xs = cdr(xs);
      }
      return val;
    }
  case Whl2:
    while (eval(e->left, env) != 0) eval(e->right, env);
    return 0;

  ・・・ 省略 ・・・

  }
}

Whl2 の場合はC言語の while 文を使って簡単に実装できます。条件式 left を eval で評価し、結果が 0 以外 (真) であれば、本体を表す式 right を eval で評価します。繰り返しを終了したら 0 を返します。Bgn の場合も簡単です。リスト xs に格納された式を順番に eval で評価していくだけです。ただし、最後の式の評価結果を返すことに注意してください。

大きな修正はこれだけです。あとの修正は簡単なので説明は割愛します。プログラムの詳細は プログラムリスト をお読みください。

●実行例

それでは簡単な実行例を示します。

Calc> begin print(1), print(2), print(3) end;
1
2
3
=> 3
Calc> a = 0;
=> 0
Calc> while a < 10 do begin print(a), a = a + 1 end end;
0
1
2
3
4
5
6
7
8
9
=> 0
Calc> a;
=> 10

begin と while は正常に動作していますね。

次は while で階乗を計算する関数 fact を作ってみましょう。

Calc> def fact(n, a) begin a = 1, while n > 0 do begin a = a * n, n = n - 1 end end, a end end
=> fact
Calc> n = 0;
=> 0
Calc> while n < 15 do begin print(fact(n, 0)), n = n + 1 end end;
1
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
=> 0

電卓プログラムは関数内で局所変数を定義できないので、局所変数の代用として関数の引数を使っています。fact は変数 a を 1 に初期化し、n が 0 よりも大きければ、a = a * n を計算して n の値を -1 します。最後に a を返します。これで階乗を計算することができます。

関数 fact を清書すると次のようになります。

リスト : 階乗

def fact(n, a)
  begin
    a = 1,
    while n > 0 do
      begin
        a = a * n,
        n = n - 1
      end
    end,
    a
  end
end

begin の中では式をカンマで区切っているので、見た目はちょっと変わっていますが、雰囲気はずいぶんとプログラミング言語らしくなってきましたね。関数本体や while 式の本体を暗黙の begin で囲めば、もう少し使いやすくなるでしょう。興味のある方はプログラムを改良してみてください。

●参考文献

  1. 松田晋, 『実践アルゴリズム戦略 解法のテクニック 再帰降下型構文解析』, C MAGAZINE 1992 年 9 月号, ソフトバンク
  2. 水野順, 『スクリプト言語を作ろう』, C MAGAZINE 2000 年 5 月号, ソフトバンク
  3. 松浦健一郎, 『コンパイラの作成』, C MAGAZINE 2003 年 1 月号, ソフトバンク
  4. 高田昌之, 『インタプリタ進化論』, CQ出版社, 1992
  5. 久野靖, 『言語プロセッサ』, 丸善株式会社, 1993

●プログラムリスト

//
// calc7.c : 電卓プログラム (bigin, while の追加)
//
//           Copyright (C) 2015-2023 Makoto Hiroi
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <math.h>
#include <setjmp.h>
#include <gc.h>

#define SYM_NAME_SIZE 32

// 連結リスト
typedef struct cell {
  void *item;
  struct cell *next;
} Cell;

// セルの生成
Cell *cons(void *x, Cell *xs)
{
  Cell *cp = GC_MALLOC(sizeof(Cell));
  cp->item = x;
  cp->next = xs;
  return cp;
}

// 先頭要素を取り出す
void *car(Cell *cp)
{
  return cp->item;
}

// 先頭要素を取り除く
Cell *cdr(Cell *cp)
{
  return cp->next;
}

// 2 番目の要素
void *second(Cell *cp)
{
  return car(cdr(cp));
}

// 3 番目の要素
void *third(Cell *cp)
{
  return car(cdr(cdr(cp)));
}

// 長さ
int length(Cell *cp)
{
  int n = 0;
  while (cp != NULL) {
    cp = cdr(cp);
    n++;
  }
  return n;
}

// 反転
Cell *reverse(Cell *xs)
{
  Cell *ys = NULL;
  while (xs != NULL) {
    ys = cons(car(xs), ys);
    xs = cdr(xs);
  }
  return ys;
}

// シンボル
typedef struct {
  int type;
  char name[SYM_NAME_SIZE + 1];    // 変数名
  union {
    double val;     // 即値
    void *ref_val;  // 参照型
  };
} Symbol;

// シンボル表
#define SYM_TBL_SIZE 512

Symbol sym_table[SYM_TBL_SIZE];
int sym_count;

// 局所変数の環境
typedef struct _env {
  Symbol *var;
  double val;
  struct _env *next;
} Env;

// コンストラクタ
Env *acons(Symbol *sym, double n, Env *env)
{
  Env *e = GC_MALLOC(sizeof(Env));
  e->var = sym;
  e->val = n;
  e->next = env;
  return e;
}

// 参照
Env *assoc(Symbol *sym, Env *env)
{
  while (env != NULL) {
    if (sym == env->var) break;
    env = env->next;
  }
  return env;
}

// 更新
double update(Symbol *sym, double x, Env *env)
{
  Env *e = assoc(sym, env);
  if (e != NULL)
    e->val = x;    // 局所変数
  else
    sym->val = x;  // 大域変数
  return x;
}

// タグ
enum {Num, Add2, Sub2, Mul2, Div2, Add1, Sub1,
      Assign2, Sym, Func1, Func2, FuncU, Sel3,
      Eq2, Ne2, Lt2, Gt2, Le2, Ge2, And2, Or2,
      Not1, Bgn, Whl2};

// 構文木
typedef struct expr {
  int tag;
  union {
    double num;      // 数値
    void *ref_value; // 参照型
    struct {         // 構文木
      struct expr *left;
      struct expr *right;
    };
    // if 
    struct {
      struct expr *test_c;   // 条件節
      struct expr *then_c;   // then 節
      struct expr *else_c;   // else 節
    };
  };
} Expr;

// 数値の生成
Expr *make_number(double n)
{
  Expr *e = GC_MALLOC_ATOMIC(sizeof(Expr));
  e->tag = Num;
  e->num = n;
  return e;
}

// 単項演算子の生生
Expr *make_op1(int op, Expr *e1)
{
  Expr *e = GC_MALLOC(sizeof(Expr));
  e->tag = op;
  e->left = e1;
  e->right = NULL;
  return e;
}

// 二項演算子
Expr *make_op2(int op, Expr *e1, Expr *e2)
{
  Expr *e = GC_MALLOC(sizeof(Expr));
  e->tag = op;
  e->left = e1;
  e->right = e2;
  return e;
}

// 関数呼び出し
Expr *make_func(Symbol *sym, int tag, Cell *cp)
{
  Expr *e = GC_MALLOC(sizeof(Expr));
  e->tag = tag;
  e->ref_value = cons(sym->ref_val, cp);
  return e;
}

// 変数の生成
Expr *make_sym(Symbol *sym)
{
  Expr *e = GC_MALLOC(sizeof(Expr));
  e->tag = Sym;
  e->ref_value = sym;
  return e;
}

// if 文の生成
Expr *make_if(Expr *test_c, Expr *then_c, Expr *else_c)
{
  Expr *e = GC_MALLOC(sizeof(Expr));
  e->tag = Sel3;
  e->test_c = test_c;
  e->then_c = then_c;
  e->else_c = else_c;
  return e;
}

// begin 式の生成
Expr *make_begin(Cell *cp)
{
  Expr *e = GC_MALLOC(sizeof(Expr));
  e->tag = Bgn;
  e->ref_value = cp;
  return e;
}

// トークン
enum {Eof, Number, Ident, Assign, Add, Sub, Mul, Div, Lpar, Rpar,
      Eq, Ne, Lt, Gt, Le, Ge, And, Or, Not, Comma, Semic, Def, End,
      If, Then, Else, Begin, While, Do, Others};

// 外部変数
int ch;         // 記号
int token;      // トークン
double value;   // 数値
Symbol *symbol; // シンボル
jmp_buf err;    // エラー脱出用

// トークン名
char *token_name[] = {
  "EOF",
  "Number",
  "Ident",
  "=",
  "+",
  "-",
  "*",
  "/",
  "(",
  ")",
  "==",
  "!=",
  "<",
  ">",
  "<=",
  ">=",
  "And",
  "Or",
  "Not",
  ",",
  ";",
  "Def",
  "End",
  "If",
  "Then",
  "Else",
  "Begin",
  "While",
  "Do",
  "Others",
};

// 記号の先読み
void nextch(void)
{
  ch = getchar();
}

// 先読み記号の取得
int getch(void)
{
  return ch;
}

// エラー
_Noreturn
void error(char *mes)
{
  fprintf(stderr, "%s, %s\n", mes, token_name[token]);
  longjmp(err, 1);
}

// シンボルの探索
Symbol *lookup(const char *name)
{
  for (int i = 0; i < sym_count; i++) {
    Symbol *sym = &sym_table[i];
    if (strcmp(sym->name, name) == 0) return sym;
  }
  return NULL;
}

// シンボルの生成
Symbol *gensym(const char *name)
{
  if (sym_count >= SYM_TBL_SIZE) error("Symbol table is full");
  Symbol *sym = &sym_table[sym_count++];
  strcpy(sym->name, name);
  sym->val = 0;
  sym->type = 0;
  return sym;
}

// 組み込み関数

// 表示
double print(double x)
{
  printf("%.16g\n", x);
  return x;
}

// 組み込み関数の初期化
void init_func1(const char *name, double (*func)(double))
{
  Symbol *sym = gensym(name);
  sym->type = 1;
  sym->ref_val = func;
}

void init_func2(const char *name, double (*func)(double, double))
{
  Symbol *sym = gensym(name);
  sym->type = 2;
  sym->ref_val = func;
}

void init_func(void)
{
  init_func1("sqrt", sqrt);
  init_func2("fmod", fmod);
  init_func2("pow", pow);
  init_func1("exp", exp);
  init_func1("log", log);
  init_func1("log10", log10);
  init_func1("fabs", fabs);
  init_func1("ceil", ceil);
  init_func1("floor", floor);
  init_func1("sin", sin);
  init_func1("cos", cos);
  init_func1("tan", tan);
  init_func1("asin", asin);
  init_func1("acos", acos);
  init_func1("atan", atan);
  init_func2("atan2", atan2);
  init_func1("sinh", sinh);
  init_func1("cosh", cosh);
  init_func1("tanh", tanh);
  init_func1("print", print);
}

// シンボル (識別子) を取得する
Symbol *get_ident(void)
{
  char name[SYM_NAME_SIZE + 1];
  int i = 0;
  name[i++] = getch();
  nextch();
  while (true) {
    int c = getch();
    if (!isalnum(c) && c != '_') break;
    name[i++] = c;
    if (i > SYM_NAME_SIZE)
      error("symbol name is too long");
    nextch();
  }
  name[i] = '\0';
  Symbol *sym = lookup(name);
  if (sym == NULL)
    sym = gensym(name);
  return sym; 
}

#define SIZE 1024

// 整数部をバッファに格納する
int get_fixnum(char *buff, int i)
{
  while (isdigit(getch())) {
    buff[i++] = getch();
    nextch();
  }
  return i;
}

// 数値を求める
double get_number(void)
{
  char buff[SIZE + 1];
  char *err;
  int i = get_fixnum(buff, 0);
  if (getch() == '.') {
    buff[i++] = getch();
    nextch();
    i = get_fixnum(buff,i);
  }
  if (getch() == 'e' || getch() == 'E') {
    buff[i++] = getch();
    nextch();
    if (getch() == '+' || getch() == '-') {
      buff[i++] = getch();
      nextch();
    }
    i = get_fixnum(buff, i);
  }
  buff[i] = '\0';
  double value = strtod(buff, &err);
  if (*err != '\0')
    error("get_number: not Number\n");
  return value;
}

// トークンの切り分け
void get_token(void)
{
  // 空白文字の読み飛ばし
  while (isspace(getch())) nextch();
  if (isdigit(getch())) {
    token = Number;
    value = get_number();
  } else if (isalpha(getch())) {
    symbol = get_ident();
    if (strcmp(symbol->name, "def") == 0)
      token = Def;
    else if (strcmp(symbol->name, "end") == 0)
      token = End;
    else if (strcmp(symbol->name, "if") == 0)
      token = If;
    else if (strcmp(symbol->name, "then") == 0)
      token = Then;
    else if (strcmp(symbol->name, "else") == 0)
      token = Else;
    else if (strcmp(symbol->name, "and") == 0)
      token = And;
    else if (strcmp(symbol->name, "or") == 0)
      token = Or;
    else if (strcmp(symbol->name, "not") == 0)
      token = Not;
    else if (strcmp(symbol->name, "begin") == 0)
      token = Begin;
    else if (strcmp(symbol->name, "while") == 0)
      token = While;
    else if (strcmp(symbol->name, "do") == 0)
      token = Do;
    else
      token = Ident;
  } else {
    switch(getch()){
    case '=':
      nextch();
      if (getch() == '=') {
	nextch();
	token = Eq;
      } else 
	token = Assign;
      break;
    case '!':
      nextch();
      if (getch() == '=') {
	nextch();
	token = Ne;
      } else
	token = Not;
      break;
    case '<':
      nextch();
      if (getch() == '=') {
	nextch();
	token = Le;
      } else
	token = Lt;
      break;
    case '>':
      nextch();
      if (getch() == '=') {
	nextch();
	token = Ge;
      } else
	token = Gt;
      break;
    case '+':
      token = Add;
      nextch();
      break;
    case '-':
      token = Sub;
      nextch();
      break;
    case '*':
      token = Mul;
      nextch();
      break;
    case '/':
      token = Div;
      nextch();
      break;
    case '(':
      token = Lpar;
      nextch();
      break;
    case ')':
      token = Rpar;
      nextch();
      break;
    case ';':
      token = Semic;
      nextch();
      break;
    case ',':
      token = Comma;
      nextch();
      break;
    case EOF:
      token = Eof;
      break;
    default:
      token = Others;
    }
  }
}

// 構文解析
Expr *expression(void);  // 代入演算子
Expr *expr1(void);       // 論理演算子
Expr *expr2(void);       // 比較演算子
Expr *expr3(void);       // 加法演算子
Expr *term(void);        // 乗法演算子
Expr *factor(void);      // 因子

Expr *expression(void)
{
  Expr *e = expr1();
  if (token == Assign) {
    if (e->tag != Sym) error("invalid assign form");
    get_token();
    return make_op2(Assign2, e, expression());
  }
  return e;
}

// 論理演算子
Expr *expr1(void)
{
  Expr *e = expr2();
  while (true) {
    switch (token) {
    case And:
      get_token();
      e = make_op2(And2, e, expr2());
      break;
    case Or:
      get_token();
      e = make_op2(Or2, e, expr2());
      break;
    default:
      return e;
    }
  }
}

// 比較演算子
Expr *expr2(void)
{
  Expr *e = expr3();
  while (true) {
    switch (token) {
    case Eq:
      get_token();
      e = make_op2(Eq2, e, expr3());
      break;
    case Ne:
      get_token();
      e = make_op2(Ne2, e, expr3());
      break;
    case Lt:
      get_token();
      e = make_op2(Lt2, e, expr3());
      break;
    case Gt:
      get_token();
      e = make_op2(Gt2, e, expr3());
      break;
    case Le:
      get_token();
      e = make_op2(Le2, e, expr3());
      break;
    case Ge:
      get_token();
      e = make_op2(Ge2, e, expr3());
      break;
    default:
      return e;
    }
  }
}
  
// 加法演算子
Expr *expr3(void)
{
  Expr *e = term();
  while (true) {
    switch (token) {
    case Add:
      get_token();
      e = make_op2(Add2, e, term());
      break;
    case Sub:
      get_token();
      e = make_op2(Sub2, e, term());
      break;
    default:
      return e;
    }
  }
}

// 乗法演算子
Expr *term(void)
{
  Expr *e = factor();
  while (true) {
    switch (token) {
    case Mul:
      get_token();
      e = make_op2(Mul2, e, factor());
      break;
    case Div:
      get_token();
      e = make_op2(Div2, e, factor());
      break;
    default:
      return e;
    } 
  }
}

// 引数の取得
Cell *get_argument(void)
{
  Cell *cp = NULL;
  get_token();
  if (token == Rpar) {
    get_token();
    return cp;
  }
  while (true) {
    cp = cons(expression(), cp);
    if (token == Rpar) {
      get_token();
      return cp;
    } else if (token == Comma) {
      get_token();
    } else 
      error("unexpected token in argument list");
  }
}

// カンマで区切られた式を取得
Cell *get_expr()
{
  Cell *xs = NULL;
  if (token == End) error("begin: empty expression");
  while (true) {
    xs = cons(expression(), xs);
    if (token == End) {
      get_token();
      return reverse(xs);
    } else if (token == Comma) {
      get_token();
    } else {
      error("begin: unexpected token");
    }
  }
}

Expr *factor(void)
{
  Symbol *sym;
  switch (token) {
  case Lpar:
    get_token();
    Expr *e = expression();
    if (token == Rpar)
      get_token();
    else 
      error("')' expected");
    return e;
  case Number:
    get_token();
    return make_number(value);
  case Add:
    get_token();
    return make_op1(Add1, factor());
  case Sub:
    get_token();
    return make_op1(Sub1, factor());
  case Not:
    get_token();
    return make_op1(Not1, factor());
  case If:
    {
      get_token();
      Expr *test_c = expression();
      if (token != Then) error("invalid if form");
      get_token();
      Expr *then_c = expression();
      Expr *else_c = NULL;
      if (token == Else) {
	get_token();
	else_c = expression();
      }
      if (token != End) error("end expected");
      get_token();
      return make_if(test_c, then_c, else_c);
    }
  case Begin:
    get_token();
    return make_begin(get_expr());
  case While:
    {
      get_token();
      Expr *test = expression();
      if (token != Do) error("Do expected");
      get_token();
      Expr *body = expression();
      if (token != End) error("End expected");
      get_token();
      return make_op2(Whl2, test, body);
    }
  case Ident:
    sym = symbol;
    get_token();
    if (sym->type == 0)
      return make_sym(sym);
    else {
      if (token != Lpar) error("'(' expected");
      Cell *cp = get_argument();
      if (sym->type == 1) {
	if (length(cp) != 1)
	  error("wrong number of arguments");
	return make_func(sym, Func1, cp);
      } else if(sym->type == 2) {
	if (length(cp) != 2)
	  error("wrong number of arguments");
	return make_func(sym, Func2, cp);
      } else {
	if (length(cp) != length(car(sym->ref_val)))
	  error("wrong number of arguments");
	return make_func(sym, FuncU, cp);      
      }
    }
  default:
    error("unexpected token");
  }
}

// プロトタイプ宣言
double eval(Expr *, Env *);

// 変数束縛
Env *add_bindings(Cell *para, Cell *args, Env *env)
{
  Env *env1 = NULL; //  env で初期化するとダイナミックスコープになる
  while (para != NULL) {
    env1 = acons(car(para), eval(car(args), env), env1);
    para = cdr(para);
    args = cdr(args);
  }
  return env1;
}

// 数式の評価
double eval(Expr *e, Env *env)
{
  Symbol *sym;
  Env *env1;
  switch (e->tag) {
  case Num: return e->num;
  case Sym:
    sym = e->ref_value;
    env1 = assoc(sym, env);
    if (env1 != NULL) return env1->val;  // 局所変数
    return sym->val;                     // 大域変数
  case Add1: return eval(e->left, env);
  case Sub1: return -eval(e->left, env);
  case Not1: return eval(e->left, env) == 0 ? 1 : 0;
  case Add2: return eval(e->left, env) + eval(e->right, env); 
  case Sub2: return eval(e->left, env) - eval(e->right, env); 
  case Mul2: return eval(e->left, env) * eval(e->right, env); 
  case Div2: return eval(e->left, env) / eval(e->right, env);
  case Eq2:
    return eval(e->left, env) == eval(e->right, env) ? 1 : 0;
  case Ne2:
    return eval(e->left, env) != eval(e->right, env) ? 1 : 0;
  case Lt2:
    return eval(e->left, env) < eval(e->right, env) ? 1 : 0;
  case Gt2:
    return eval(e->left, env) > eval(e->right, env) ? 1 : 0;
  case Le2:
    return eval(e->left, env) <= eval(e->right, env) ? 1 : 0;
  case Ge2:
    return eval(e->left, env) >= eval(e->right, env) ? 1 : 0;
  case And2:
    return eval(e->left, env) != 0 ? eval(e->right, env) : 0;
  case Or2:
    {
      double val = eval(e->left, env);
      return val == 0 ? eval(e->right, env) : val;
    }
  case Assign2:
    sym = e->left->ref_value;
    return update(sym, eval(e->right, env), env);
  case Sel3:
    if (eval(e->test_c, env) != 0)
      return eval(e->then_c, env);
    else if (e->else_c != NULL)
      return eval(e->else_c, env);
    else
      return 0;
  case Bgn:
    {
      Cell *xs = e->ref_value;
      double val = 0;
      while (xs != NULL) {
	val = eval(car(xs), env);
	xs = cdr(xs);
      }
      return val;
    }
  case Whl2:
    while (eval(e->left, env) != 0) eval(e->right, env);
    return 0;
  case Func1:
    {
      Cell *cp = e->ref_value;
      double (*func1)(double) = car(cp);
      return func1(eval(second(cp), env));
    }
  case Func2:
    {
      Cell *cp = e->ref_value;
      double (*func2)(double, double) = car(cp);
      return func2(eval(third(cp), env), eval(second(cp), env));
    }
  case FuncU:
    {
      Cell *cp = e->ref_value;
      Cell *fn = car(cp);
      return eval(second(fn), add_bindings(car(fn), cdr(cp), env));
    } 
  default:
    fprintf(stderr, "invalid Expr tag %d\n", e->tag);
    exit(1);
  }
}

// 仮引数の取得
Cell *get_parameter(void)
{
  Cell *cp = NULL;
  get_token();
  if (token == Rpar) {
    get_token();
    return cp;
  }
  while (true) {
    Expr *e = expression();
    if (e->tag != Sym)
      error("invalid parameter list");
    cp = cons(e->ref_value, cp);
    if (token == Rpar) {
      get_token();
      return cp;
    } else if (token == Comma) {
      get_token();
    } else 
      error("unexpected token in argument list");
  }
}

void toplevel(void)
{
  if (token == Def) {
    // 関数定義
    get_token();
    if (token != Ident) error("Symbol expected");
    Symbol *sym = symbol;
    if (sym->type == 1 || sym->type == 2)
      error("build-in function");
    get_token();
    if (token != Lpar) error("'(' expected");
    Cell *para = get_parameter();
    // 再帰定義対策
    sym->type = 3;
    Cell *fn = cons(para, NULL);
    sym->ref_val = fn;
    Expr *body = expression();
    if (token != End) {
      sym->type = 0;
      error("end expected");
    } else {
      fn->next = cons(body, NULL);
    }
    printf("=> %s\nCalc> ", sym->name);
    fflush(stdout);
  } else { 
    Expr *e = expression();
    if (token == Semic) {
      printf("=> %.16g\nCalc> ", eval(e, NULL));
      fflush(stdout);
    } else {
      error("invalid token");
    }
  }
}

int main(void)
{
  init_func();
  printf("Calc> ");
  fflush(stdout);
  nextch();
  while (true) {
    if (!setjmp(err)) {
      get_token();
      if (token == Eof) break;
      toplevel();
    } else {
      // 入力のクリア
      while (getch() != '\n') nextch();
      printf("Calc> ");
      fflush(stdout);
    }
  }
  printf("bye\n");
  return 0;
}

初版 2015 年 5 月 9 日
改訂 2023 年 4 月 8 日

Copyright (C) 2015-2023 Makoto Hiroi
All rights reserved.

[ PrevPage | Clang | NextPage ]