CPU: PentiumII 400MHz OS: Kondara MNU/Linux 2.0 KERNEL: Linux 2.4.4-18ksmp CC: gcc-2.96-80k LIBC: glibc-2.2.2-12k LIBREADLINE: readline-4.1-14k → readline-2.2.1
まずは、 [] にある前橋氏の電卓「mycalc」をコンパイル してみる。
手順通りにコンパイルしていくと、いきなりコンパイルエラー。
[q@krb mycalc]$ make cc lex.yy.o y.tab.o create.o eval.o interface.o util.o error.o main.o ./memory/mem.o ./debug/dbg.o -o mycalc -lfl -lm -lreadline /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `tgetnum' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `tgoto' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `tgetflag' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `BC' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `tputs' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `PC' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `tgetent' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `UP' /usr/lib/gcc-lib/i586-redhat-linux/2.96/../../../libreadline.so: undefined reference to `tgetstr' collect2: ld returned 1 exit status make: *** [mycalc] エラー 1
最後のリンカがエラーを吐いているようだ。まずは疑ったのがgcc。Kondara-devel-ja メーリングリストに入っているのでいろいろな情報が流れてくるが、その中には gcc2.9.6だと上手くコンパイルできないものがある、というものがあったのを 思いだした。早速、gcc-2.95.3で実験してみると、同じエラーを吐いて落ちた。 そこで次に疑ったのが、 [] にある以下の記述である。
入力ルーチンを置き換える方法は、処理系によって違ったりするようなんですが、 flexやFreeBSDの標準のlexでは、YY_INPUTマクロを置き換えることで行なうようです。Linuxでは異っているかもしれない、と思ったが、
$ man lex
を抜粋してみると、問題ないことがわかった。
By default (and for purposes of efficiency), the scanner uses block-reads rather than simple getc() calls to read characters from yyin. The nature of how it gets its input can be controlled by defining the YY_INPUT macro. YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its action is to place up to max_size characters in the character array buf and return in the integer variable result either the number of characters read or the constant YY_NULL (0 on Unix systems) to indicate EOF. The default YY_INPUT reads from the global file-pointer "yyin".
ここで一旦投げ出しかけたが、readlineのChangelogにreadline4系は readline2系とは互換性があまりない、ということが書いてあったので、 readline-2.2.1をインストールし、Makefileを修正してreadline2系ライブラリ を参照するようにしてgcc-2.9.6でコンパイルしてみたところ、問題なく コンパイルできた。
[root@krb lib]# rpm -qi readline2.2.1 Name : readline2.2.1 Relocations: /usr Distribution: Kondara MNU/Linux 2.0(Mary) Vendor: (none) Version : 2.2.1 Option: (none) Release : 4k Build Date: Sat Mar 17 00:01:57 2001 Install date: Sat Jul 28 17:10:53 2001 Build Host: mukiri.kondara.org Group : System Environment/Libraries Source RPM: readline2.2.1-2.2.1-4k.nosrc.rpm Size : 204081 License: GPL Summary : A library for reading and returning lines from a terminal. Description : The readline library reads a line from the terminal and returns it, allowing the user to edit the line with standard Emacs editing keys. The readline library allows programmers to provide an easy to use and more intuitive interface for users. This version of the readline package provides backwards compatibility for programs built against Red Hat Linux 6.2.
とあるので、第4,5端末室やVineLinux2.1などRedHat6.2系Linuxでは 問題なくコンパイルできるはずである。
まず、sinを計算する関数を組み込んでみる。
前橋氏の電卓では、演算は全て「式」の形式で実装されている。 これをそのまま採用し、「sin式」を定義して実装することにした。
もちろん、tokenに''sin''を加えるなど、字句解析部も拡張する。 (これより、ソースコードを掲載するときは、前橋氏のmycalcから 変更があった部分のみを掲載する)
sin式の構文規則はこのようにする。
sin式=‘sin’‘(’式‘)’
例示すると、sin(3.1415)は有効、sin(10)は有効、そして、 sin(1+3)も有効である。
sin式=‘sin’‘(’実数値‘)’にしようかどうか迷ったが、これにすると、sin(1+3)がエラーになってしまう。 しかし、上記の規則では、sin(if(…))と書けてしまう。 迷ったが、結局上記のものにした。
これを実装すると、このようになる。 (cacl.yに追加)
sin_expression : SIN LP expression RP { $$ = clc_create_sin_expression($3); } ;
なお、下記の部分は、sin式を評価する際は、渡された式のみが 必要なので、3番目の引数、すなわち$3を渡せばよいことになる。
$$ = clc_create_sin_expression($3);
これを実装することにより芋蔓式に変更が必要になる。 clc_create_sin_expression(expression)の実装は 一旦置いておくとしてまずは字句解析部と構文解析部で必要な変更を 加える。
まず字句解析部だが、SINというtokenを定義する必要がある。 calc.lに以下を加える。
<INITIAL>"sin" return SIN;calc.yを以下のように変更する。
%token SIN DEFINE IF ELSE WHILE LP RP LC RC SEMICOLON COMMA ASSIGN EQ NE GT GE LT LE ADD SUB MUL DIV MOD %type <expression> expression expression_list equality_expression relational_expression additive_expression multiplicative_expression unary_expression postfix_expression primary_expression if_expression while_expression sin_expression primary_expression : IDENTIFIER LP expression_list RP { $$ = clc_create_function_call_expression($1, $3); } | IDENTIFIER LP RP { $$ = clc_create_function_call_expression($1, NULL); } | if_expression | while_expression | sin_expression | LP expression RP { $$ = $2; } | IDENTIFIER { $$ = clc_create_identifier_expression($1); } | INT_LITERAL | DOUBLE_LITERAL ;
次に実際の処理をするのだが、これはclc_create_sin_expression(expression)を 実装すればよい。
まずは定義する必要がある。calc.hファイルをいじる。
typedef enum { INT_EXPRESSION = 1, DOUBLE_EXPRESSION, IDENTIFIER_EXPRESSION, EXPRESSION_LIST_EXPRESSION, ASSIGN_EXPRESSION, ADD_EXPRESSION, SUB_EXPRESSION, MUL_EXPRESSION, DIV_EXPRESSION, MOD_EXPRESSION, EQ_EXPRESSION, NE_EXPRESSION, GT_EXPRESSION, GE_EXPRESSION, LT_EXPRESSION, LE_EXPRESSION, MINUS_EXPRESSION, IF_EXPRESSION, WHILE_EXPRESSION, SIN_EXPRESSION, FUNCTION_CALL_EXPRESSION, EXPRESSION_TYPE_NUM } ExpressionType; typedef struct { Expression *inside_bracket ; } SinExpression; struct Expression_tag { ExpressionType type; union { int int_value; double double_value; char *identifier; ExpressionList expression_list; AssignExpression assign_expression; BinaryExpression binary_expression; Expression *minus_expression; IfExpression if_expression; FunctionCallExpression function_call_expression; WhileExpression while_expression; SinExpression sin_expression; } u; }; /* create.c */ void clc_function_define(char *identifier, ParameterList *parameter_list, Expression *expression_list); ParameterList *clc_create_parameter(char *identifier); ParameterList *clc_chain_parameter(ParameterList *list, char *identifier); Expression *clc_alloc_expression(ExpressionType type); Expression *clc_create_expression_list(Expression *expression); Expression *clc_chain_expression_list(Expression *list, Expression *add); Expression *clc_create_assign_expression(char *variable, Expression *operand); Expression *clc_create_binary_expression(ExpressionType operator, Expression *left, Expression *right); Expression *clc_create_minus_expression(Expression *operand); Expression *clc_create_identifier_expression(char *identifier); Expression *clc_create_if_expression(Expression *condition, Expression *then_expression, Expression *else_expression); Expression *clc_create_while_expression(Expression *condition, Expression *expression); Expression *clc_create_sin_expression(Expression *inside_bracket); Expression *clc_create_function_call_expression(char *func_name, Expression *expression);
そして、上記ソースコード抜粋の中でcreate.cをいじれ、と書いてあるのでそうする。 前橋氏の電卓の仕様では、EXPRESSIONの構築にはそのtypeが必要なので、それも 上記の定義を用いる。
Expression * clc_create_sin_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(SIN_EXPRESSION); exp->u.sin_expression_inside_bracket = inside_bracket; return exp; }
実際の処理はeval.cで定義されているので、そこにsin関数の処理を追加する。 Cのsin()関数はdoubleしか受け付けないので、doubleではないときは 括弧の中身は式であると見なして処理をする。 なお、eval.c内で式の種類を判別して処理内容を分けているので、 その判別ルーチンにsin式も追加する。
static int eval_binary_int(ExpressionType operator, int left, int right) { int result; switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result = left + right; break; case SUB_EXPRESSION: result = left - right; break; case MUL_EXPRESSION: result = left * right; break; case DIV_EXPRESSION: result = left / right; break; case MOD_EXPRESSION: result = left % right; break; case EQ_EXPRESSION: result = left == right; break; case NE_EXPRESSION: result = left != right; break; case GT_EXPRESSION: result = left > right; break; case GE_EXPRESSION: result = left >= right; break; case LT_EXPRESSION: result = left < right; break; case LE_EXPRESSION: result = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case...%d", operator)); } return result; } static void eval_binary_double(ExpressionType operator, double left, double right, Value *result) { if (operator == ADD_EXPRESSION || operator == SUB_EXPRESSION || operator == MUL_EXPRESSION || operator == DIV_EXPRESSION || operator == MOD_EXPRESSION) { result->type = DOUBLE_VALUE; } else { DBG_assert(operator == EQ_EXPRESSION || operator == NE_EXPRESSION || operator == GT_EXPRESSION || operator == GE_EXPRESSION || operator == LT_EXPRESSION || operator == LE_EXPRESSION, ("operator..%d\n", operator)); result->type = INT_VALUE; } switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result->u.double_value = left + right; break; case SUB_EXPRESSION: result->u.double_value = left - right; break; case MUL_EXPRESSION: result->u.double_value = left * right; break; case DIV_EXPRESSION: result->u.double_value = left / right; break; case MOD_EXPRESSION: result->u.double_value = fmod(left, right); break; case EQ_EXPRESSION: result->u.int_value = left == right; break; case NE_EXPRESSION: result->u.int_value = left != right; break; case GT_EXPRESSION: result->u.int_value = left > right; break; case GE_EXPRESSION: result->u.int_value = left >= right; break; case LT_EXPRESSION: result->u.int_value = left < right; break; case LE_EXPRESSION: result->u.int_value = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad default...%d", operator)); } } static Value eval_sin_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { if (!inside_bracket.u.double_value){ result = sin(inside_bracket); } else { result = eval_expression(env, inside_bracket); } } return result; } static Value eval_expression(LocalEnvironment *env, Expression *expr) { Value v; switch (expr->type) { case INT_EXPRESSION: v = eval_int_expression(expr->u.int_value); break; case DOUBLE_EXPRESSION: v = eval_double_expression(expr->u.double_value); break; case IDENTIFIER_EXPRESSION: v = eval_identifier_expression(env, expr->u.identifier); break; case EXPRESSION_LIST_EXPRESSION: v = eval_expression_list_expression (env, expr->u.expression_list.expression, expr->u.expression_list.next); break; case ASSIGN_EXPRESSION: v = eval_assign_expression(env, expr->u.assign_expression.variable, expr->u.assign_expression.operand); break; case ADD_EXPRESSION: /* FALLTHRU */ case SUB_EXPRESSION: /* FALLTHRU */ case MUL_EXPRESSION: /* FALLTHRU */ case DIV_EXPRESSION: /* FALLTHRU */ case MOD_EXPRESSION: /* FALLTHRU */ case EQ_EXPRESSION: /* FALLTHRU */ case NE_EXPRESSION: /* FALLTHRU */ case GT_EXPRESSION: /* FALLTHRU */ case GE_EXPRESSION: /* FALLTHRU */ case LT_EXPRESSION: /* FALLTHRU */ case LE_EXPRESSION: /* FALLTHRU */ v = clc_eval_binary_expression(env, expr->type, expr->u.binary_expression.left, expr->u.binary_expression.right); break; case MINUS_EXPRESSION: v = clc_eval_minus_expression(env, expr->u.minus_expression); break; case IF_EXPRESSION: v = eval_if_expression(env, expr->u.if_expression.condition, expr->u.if_expression.then_expression, expr->u.if_expression.else_expression); break; case WHILE_EXPRESSION: v = eval_while_expression(env, expr->u.while_expression.condition, expr->u.while_expression.expression_list); break; case SIN_EXPRESSION: v = eval_sin_expression(env, expr->u.sin_expression_inside_bracket); break; case FUNCTION_CALL_EXPRESSION: v = eval_function_call_expression (env, expr->u.function_call_expression.identifier, expr->u.function_call_expression.argument); break; case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case. type..%d\n", expr->type)); } return v; }
これでsin式の処理が追加されたと思うので、コンパイルしてみる。 …いきなりエラー吐いて落ちた。
$ make bison --yacc -dv calc.y flex calc.l cc -c -g lex.yy.c In file included from calc.l:7: DBG.h:54:8: warning: extra tokens at end of #endif directive In file included from calc.h:6, from calc.l:8: CLC.h:17:8: warning: extra tokens at end of #endif directive cc -c -g y.tab.c In file included from calc.h:6, from calc.y:3: CLC.h:17:8: warning: extra tokens at end of #endif directive cc -c -g -Wall create.c In file included from create.c:2: DBG.h:54:8: warning: extra tokens at end of #endif directive In file included from calc.h:6, from create.c:3: CLC.h:17:8: warning: extra tokens at end of #endif directive create.c: In function `clc_create_sin_expression': create.c:218: union has no member named `sin_expression_inside_bracket' make: *** [create.o] エラー 1 $
create.cに追加した関数がエラーの原因と言っているので、それを修正する。 _を.に一箇所直す。
Expression * clc_create_sin_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(SIN_EXPRESSION); exp->u.sin_expression.inside_bracket = inside_bracket; return exp; }
これでコンパイルしてみる。 …またエラー吐いて落ちた。
$ make cc -c -g -Wall eval.c In file included from eval.c:4: DBG.h:54:8: warning: extra tokens at end of #endif directive In file included from calc.h:6, from eval.c:5: CLC.h:17:8: warning: extra tokens at end of #endif directive eval.c: In function `eval_sin_expression': eval.c:319: request for member `u' in something not a structure or union eval.c:320: incompatible type for argument 1 of `sin' eval.c:320: incompatible types in assignment eval.c:315: warning: unused variable `inside_bracket_val' eval.c: In function `eval_expression': eval.c:450: union has no member named `sin_expression_inside_bracket' make: *** [eval.o] エラー 1 $
今度はeval.cが文句言っている。create.cでいじったところをこっちにも 反映しろ、と言っているのでそうする。_を.に一箇所直す。 さらに、eval_sin_expression()でもいろいろ文句を言っているので直す。 こちらは、返り値の型が合っていなかった。
static Value eval_sin_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sin(inside_bracket_val.u.double_value); } } return result; } static Value eval_expression(LocalEnvironment *env, Expression *expr) { Value v; switch (expr->type) { case INT_EXPRESSION: v = eval_int_expression(expr->u.int_value); break; case DOUBLE_EXPRESSION: v = eval_double_expression(expr->u.double_value); break; case IDENTIFIER_EXPRESSION: v = eval_identifier_expression(env, expr->u.identifier); break; case EXPRESSION_LIST_EXPRESSION: v = eval_expression_list_expression (env, expr->u.expression_list.expression, expr->u.expression_list.next); break; case ASSIGN_EXPRESSION: v = eval_assign_expression(env, expr->u.assign_expression.variable, expr->u.assign_expression.operand); break; case ADD_EXPRESSION: /* FALLTHRU */ case SUB_EXPRESSION: /* FALLTHRU */ case MUL_EXPRESSION: /* FALLTHRU */ case DIV_EXPRESSION: /* FALLTHRU */ case MOD_EXPRESSION: /* FALLTHRU */ case EQ_EXPRESSION: /* FALLTHRU */ case NE_EXPRESSION: /* FALLTHRU */ case GT_EXPRESSION: /* FALLTHRU */ case GE_EXPRESSION: /* FALLTHRU */ case LT_EXPRESSION: /* FALLTHRU */ case LE_EXPRESSION: /* FALLTHRU */ v = clc_eval_binary_expression(env, expr->type, expr->u.binary_expression.left, expr->u.binary_expression.right); break; case MINUS_EXPRESSION: v = clc_eval_minus_expression(env, expr->u.minus_expression); break; case IF_EXPRESSION: v = eval_if_expression(env, expr->u.if_expression.condition, expr->u.if_expression.then_expression, expr->u.if_expression.else_expression); break; case WHILE_EXPRESSION: v = eval_while_expression(env, expr->u.while_expression.condition, expr->u.while_expression.expression_list); break; case SIN_EXPRESSION: v = eval_sin_expression(env, expr->u.sin_expression.inside_bracket); break; case FUNCTION_CALL_EXPRESSION: v = eval_function_call_expression (env, expr->u.function_call_expression.identifier, expr->u.function_call_expression.argument); break; case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case. type..%d\n", expr->type)); } return v; }
これでコンパイルしてみる。 …通った!
$ make cc -c -g -Wall eval.c In file included from eval.c:4: DBG.h:54:8: warning: extra tokens at end of #endif directive In file included from calc.h:6, from eval.c:5: CLC.h:17:8: warning: extra tokens at end of #endif directive cc -c -g -Wall interface.c In file included from calc.h:6, from interface.c:3: CLC.h:17:8: warning: extra tokens at end of #endif directive cc -c -g -Wall util.c In file included from calc.h:6, from util.c:1: CLC.h:17:8: warning: extra tokens at end of #endif directive util.c: In function `clc_search_local_variable': util.c:31: warning: implicit declaration of function `strcmp' util.c: In function `clc_add_global_variable': util.c:77: warning: implicit declaration of function `strlen' util.c:78: warning: implicit declaration of function `strcpy' cc -c -g -Wall error.c In file included from error.c:4: DBG.h:54:8: warning: extra tokens at end of #endif directive In file included from calc.h:6, from error.c:5: CLC.h:17:8: warning: extra tokens at end of #endif directive cc lex.yy.o y.tab.o create.o eval.o interface.o util.o error.o main.o ./memory/mem.o ./debug/dbg.o -o mycalc -lreadline2 -lfl -lm $
動作実験をする。 まずは簡単な例から入れてみる。
[q@krb mycalc]$ ./mycalc >sin(3.1415);
…暴走した。Ctrl-Cで止めた。以下はtopコマンドの出力の抜粋である。
PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND 1260 q 14 0 812 812 652 R 94.0 0.2 0:53 mycalc
なんでだろう…と思ってeval.c内のeval_sin_expression()を見たら、 あたり前だった。無限forループ内でbreakし忘れていた(爆)。 ついでに、intが渡された場合でも大丈夫なようにする。
static Value eval_sin_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sin(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sin(inside_bracket_val.u.int_value); break; } } return result; }
これでコンパイルしてみる。通ったので、メッセージは省略する。
動作確認をしてみる。 まずは簡単な例から、複雑な例へと進む。
$ ./mycalc >sin(3.1415); >>0.000093 >sin(0.0); >>0.000000 >sin(3.1415/2); >>1.000000 >sin(1+3*(4+5)); >>0.270906 >sin(28); >>0.270906 >sin(28.0); >>0.270906 > $
動いた!感動(涙)。ただ、上記使用だと、入力された式がintかdoubleを返さない場合、 どうなるかわからない。予想としては、forループからbreakする条件を永久に 満たさないので無限ループすると思う。というわけで、エラーを吐いて終了するように する。while文の処理(eval.c内のeval_while_expression())を見ると、 clc_runtime_error()という関数を呼びだせばエラー終了することがわかる。 この関数に、「引数をevalした際にdoubleかintが返ってこない」という エラーを認識させるように改造することにした。
error.cを見ると、エラーの種類の定義はcalc.hでなされているようなので、 calc.hもいじる必要がある。while文のエラー処理では、コンパイルエラーでは なく、ランタイムエラーとして処理している。これにならう。
calc.hは以下の部分を変更した。
typedef enum { VARIABLE_NOT_FOUND_ERR = 1, FUNCTION_NOT_FOUND_ERR, BOOLEAN_EXPECTED_ERR, ARGUMENT_TOO_MANY_ERR, ARGUMENT_TOO_FEW_ERR, INT_OR_DOUBLE_EXPECTED_ERR RUNTIME_ERROR_NUM, } RuntimeError;
error.cは以下の部分を変更した。
static char *runtime_error_message[] = { "internal error: bad error message(0)", "variable not found. ", "function not found error. ", "boolean expected error. ", "argument too many error. ", "argument too few error. ", "int or double expected error.", "internal error: bad error message(MAX)" };
当然、eval.cも変更する必要がある。以下の部分を変更した。
static Value eval_sin_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sin(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sin(inside_bracket_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; }
コンパイルする。エラーなく通ったので、メッセージは省略する。
動作実験をする。
$ ./mycalc >sin(3.00); >>0.141120 >sin(3); >>0.141120 >sin(3fdjfkdlfsl); parse error. near token fdjfkdlfsl >sin(1=1); parse error. near token = >sin(true); variable not found. (true) >
なんとかintかdoubleにならない返り値を持つ式をsin()の中に入れようとしたが、 判定式では=でparse errorになってしまう…。というわけで例が見つからなかった ので、これでよしとした(爆)。
次にcos()を実装する。sin()と同じように実装することにする。 つまり、「cos式」なるものを定義して、それを処理する、という方式である。
cos式の構文規則はこのようにする。
cos式=‘cos’‘(’式‘)’
これをcalc.yに実装する。ついでにcosというtokenを定義する。
%token COS SIN DEFINE IF ELSE WHILE LP RP LC RC SEMICOLON COMMA ASSIGN EQ NE GT GE LT LE ADD SUB MUL DIV MOD %type <expression> expression expression_list equality_expression relational_expression additive_expression multiplicative_expression unary_expression postfix_expression primary_expression if_expression while_expression sin_expression cos_expression primary_expression : IDENTIFIER LP expression_list RP { $$ = clc_create_function_call_expression($1, $3); } | IDENTIFIER LP RP { $$ = clc_create_function_call_expression($1, NULL); } | if_expression | while_expression | sin_expression | cos_expression | LP expression RP { $$ = $2; } | IDENTIFIER { $$ = clc_create_identifier_expression($1); } | INT_LITERAL | DOUBLE_LITERAL ; cos_expression : COS LP expression RP { $$ = clc_create_cos_expression($3); } ;
ここまでで、字句解析部と構文解析部の実装を一気に行なった。 次に、clc_create_cos_expression(expression)を実装する。
まずは付随する定義などをする。
calc.hをいじる。
typedef enum { INT_EXPRESSION = 1, DOUBLE_EXPRESSION, IDENTIFIER_EXPRESSION, EXPRESSION_LIST_EXPRESSION, ASSIGN_EXPRESSION, ADD_EXPRESSION, SUB_EXPRESSION, MUL_EXPRESSION, DIV_EXPRESSION, MOD_EXPRESSION, EQ_EXPRESSION, NE_EXPRESSION, GT_EXPRESSION, GE_EXPRESSION, LT_EXPRESSION, LE_EXPRESSION, MINUS_EXPRESSION, IF_EXPRESSION, WHILE_EXPRESSION, SIN_EXPRESSION, COS_EXPRESSION, FUNCTION_CALL_EXPRESSION, EXPRESSION_TYPE_NUM } ExpressionType; typedef struct { Expression *inside_bracket ; } CosExpression; struct Expression_tag { ExpressionType type; union { int int_value; double double_value; char *identifier; ExpressionList expression_list; AssignExpression assign_expression; BinaryExpression binary_expression; Expression *minus_expression; IfExpression if_expression; FunctionCallExpression function_call_expression; WhileExpression while_expression; SinExpression sin_expression; CosExpression cos_expression; } u; }; /* create.c */ void clc_function_define(char *identifier, ParameterList *parameter_list, Expression *expression_list); ParameterList *clc_create_parameter(char *identifier); ParameterList *clc_chain_parameter(ParameterList *list, char *identifier); Expression *clc_alloc_expression(ExpressionType type); Expression *clc_create_expression_list(Expression *expression); Expression *clc_chain_expression_list(Expression *list, Expression *add); Expression *clc_create_assign_expression(char *variable, Expression *operand); Expression *clc_create_binary_expression(ExpressionType operator, Expression *left, Expression *right); Expression *clc_create_minus_expression(Expression *operand); Expression *clc_create_identifier_expression(char *identifier); Expression *clc_create_if_expression(Expression *condition, Expression *then_expression, Expression *else_expression); Expression *clc_create_while_expression(Expression *condition, Expression *expression); Expression *clc_create_sin_expression(Expression *inside_bracket); Expression *clc_create_cos_expression(Expression *inside_bracket); Expression *clc_create_function_call_expression(char *func_name, Expression *expression);
clc_create_cos_expression()を実装する。 create.cをいじる。
Expression * clc_create_cos_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(COS_EXPRESSION); exp->u.cos_expression.inside_bracket = inside_bracket; return exp; }
eval_cos_expression()を実装する。 eval.cをいじる。
static int eval_binary_int(ExpressionType operator, int left, int right) { int result; switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result = left + right; break; case SUB_EXPRESSION: result = left - right; break; case MUL_EXPRESSION: result = left * right; break; case DIV_EXPRESSION: result = left / right; break; case MOD_EXPRESSION: result = left % right; break; case EQ_EXPRESSION: result = left == right; break; case NE_EXPRESSION: result = left != right; break; case GT_EXPRESSION: result = left > right; break; case GE_EXPRESSION: result = left >= right; break; case LT_EXPRESSION: result = left < right; break; case LE_EXPRESSION: result = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case...%d", operator)); } return result; } static void eval_binary_double(ExpressionType operator, double left, double right, Value *result) { if (operator == ADD_EXPRESSION || operator == SUB_EXPRESSION || operator == MUL_EXPRESSION || operator == DIV_EXPRESSION || operator == MOD_EXPRESSION) { result->type = DOUBLE_VALUE; } else { DBG_assert(operator == EQ_EXPRESSION || operator == NE_EXPRESSION || operator == GT_EXPRESSION || operator == GE_EXPRESSION || operator == LT_EXPRESSION || operator == LE_EXPRESSION, ("operator..%d\n", operator)); result->type = INT_VALUE; } switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result->u.double_value = left + right; break; case SUB_EXPRESSION: result->u.double_value = left - right; break; case MUL_EXPRESSION: result->u.double_value = left * right; break; case DIV_EXPRESSION: result->u.double_value = left / right; break; case MOD_EXPRESSION: result->u.double_value = fmod(left, right); break; case EQ_EXPRESSION: result->u.int_value = left == right; break; case NE_EXPRESSION: result->u.int_value = left != right; break; case GT_EXPRESSION: result->u.int_value = left > right; break; case GE_EXPRESSION: result->u.int_value = left >= right; break; case LT_EXPRESSION: result->u.int_value = left < right; break; case LE_EXPRESSION: result->u.int_value = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad default...%d", operator)); } } static Value eval_cos_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = cos(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = cos(inside_bracket_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; } static Value eval_expression(LocalEnvironment *env, Expression *expr) { Value v; switch (expr->type) { case INT_EXPRESSION: v = eval_int_expression(expr->u.int_value); break; case DOUBLE_EXPRESSION: v = eval_double_expression(expr->u.double_value); break; case IDENTIFIER_EXPRESSION: v = eval_identifier_expression(env, expr->u.identifier); break; case EXPRESSION_LIST_EXPRESSION: v = eval_expression_list_expression (env, expr->u.expression_list.expression, expr->u.expression_list.next); break; case ASSIGN_EXPRESSION: v = eval_assign_expression(env, expr->u.assign_expression.variable, expr->u.assign_expression.operand); break; case ADD_EXPRESSION: /* FALLTHRU */ case SUB_EXPRESSION: /* FALLTHRU */ case MUL_EXPRESSION: /* FALLTHRU */ case DIV_EXPRESSION: /* FALLTHRU */ case MOD_EXPRESSION: /* FALLTHRU */ case EQ_EXPRESSION: /* FALLTHRU */ case NE_EXPRESSION: /* FALLTHRU */ case GT_EXPRESSION: /* FALLTHRU */ case GE_EXPRESSION: /* FALLTHRU */ case LT_EXPRESSION: /* FALLTHRU */ case LE_EXPRESSION: /* FALLTHRU */ v = clc_eval_binary_expression(env, expr->type, expr->u.binary_expression.left, expr->u.binary_expression.right); break; case MINUS_EXPRESSION: v = clc_eval_minus_expression(env, expr->u.minus_expression); break; case IF_EXPRESSION: v = eval_if_expression(env, expr->u.if_expression.condition, expr->u.if_expression.then_expression, expr->u.if_expression.else_expression); break; case WHILE_EXPRESSION: v = eval_while_expression(env, expr->u.while_expression.condition, expr->u.while_expression.expression_list); break; case SIN_EXPRESSION: v = eval_sin_expression(env, expr->u.sin_expression.inside_bracket); break; case COS_EXPRESSION: v = eval_cos_expression(env, expr->u.cos_expression.inside_bracket); break; case FUNCTION_CALL_EXPRESSION: v = eval_function_call_expression (env, expr->u.function_call_expression.identifier, expr->u.function_call_expression.argument); break; case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case. type..%d\n", expr->type)); } return v; }
エラー処理もsin()と同じようにした。
コンパイルする。エラーなく通ったので、メッセージは省略する。 動作実験をする。
$ ./mycalc >cos(0); function not found error. name..cos >cos(0.0); function not found error. name..cos >sin(0.0); >>0.000000 >sin(0); >>0.000000 >
動かない(涙)。
一回 make clean を実行した後にコンパイルしても駄目なので、 よくわからない。
…3日後、calc.lにtokenを追加するのを忘れていることが 判明。急いでcalc.lに追加する。なお、これは、羃乗を実装しようと して判明したことなので、羃乗のtokenも実装されている。
%% <INITIAL>"pow" return POW; <INITIAL>"cos" return COS; <INITIAL>"sin" return SIN; <INITIAL>"define" return DEFINE; <INITIAL>"if" return IF; <INITIAL>"else" return ELSE; <INITIAL>"while" return WHILE; <INITIAL>"(" return LP; <INITIAL>")" return RP; <INITIAL>"{" return LC; <INITIAL>"}" return RC; <INITIAL>";" return SEMICOLON; <INITIAL>"," return COMMA; <INITIAL>"=" return ASSIGN; <INITIAL>"==" return EQ; <INITIAL>"!=" return NE; <INITIAL>">" return GT; <INITIAL>">=" return GE; <INITIAL>"<" return LT; <INITIAL>"<=" return LE; <INITIAL>"+" return ADD; <INITIAL>"-" return SUB; <INITIAL>"*" return MUL; <INITIAL>"/" return DIV; <INITIAL>"%" return MOD; <INITIAL>[A-Za-z_][A-Za-z_0-9]* { yylval.identifier = clc_malloc(strlen(yytext) + 1); strcpy(yylval.identifier, yytext); return IDENTIFIER; }
コンパイルをかける。エラーなく通ったので、メッセージは省略する。 動作実験をする。
$ ./mycalc >cos(0); >>1.000000 >cos(0.0); >>1.000000 >
動いた!
次に、羃乗を実装する。これも式にする。関数名などは、c言語と同じにした。 構文規則は以下のようになる。
pow式=‘pow’‘(’double値‘,’double値‘)’
しかし、pow(式, 式)などの表記もしたい人がいるかもしれないので、 以下のようにした。
pow式=‘pow’‘(’式‘,’式‘)’
powというtokenと上記の構文規則をcalc.yに実装する。
%token POW COS SIN DEFINE IF ELSE WHILE LP RP LC RC SEMICOLON COMMA ASSIGN EQ NE GT GE LT LE ADD SUB MUL DIV MOD %type <expression> expression expression_list equality_expression relational_expression additive_expression multiplicative_expression unary_expression postfix_expression primary_expression if_expression while_expression sin_expression cos_expression power_expression primary_expression : IDENTIFIER LP expression_list RP { $$ = clc_create_function_call_expression($1, $3); } | IDENTIFIER LP RP { $$ = clc_create_function_call_expression($1, NULL); } | if_expression | while_expression | sin_expression | cos_expression | power_expression | LP expression RP { $$ = $2; } | IDENTIFIER { $$ = clc_create_identifier_expression($1); } | INT_LITERAL | DOUBLE_LITERAL ; power_expression : POW LP expression expression RP { $$ = clc_create_power_expression($3, $4); } ;
次に、calc.hをいじる。
typedef enum { INT_EXPRESSION = 1, DOUBLE_EXPRESSION, IDENTIFIER_EXPRESSION, EXPRESSION_LIST_EXPRESSION, ASSIGN_EXPRESSION, ADD_EXPRESSION, SUB_EXPRESSION, MUL_EXPRESSION, DIV_EXPRESSION, MOD_EXPRESSION, EQ_EXPRESSION, NE_EXPRESSION, GT_EXPRESSION, GE_EXPRESSION, LT_EXPRESSION, LE_EXPRESSION, MINUS_EXPRESSION, IF_EXPRESSION, WHILE_EXPRESSION, SIN_EXPRESSION, COS_EXPRESSION, POWER_EXPRESSION, FUNCTION_CALL_EXPRESSION, EXPRESSION_TYPE_NUM } ExpressionType; typedef struct { Expression *atai; Expression *jyosu; } PowerExpression; struct Expression_tag { ExpressionType type; union { int int_value; double double_value; char *identifier; ExpressionList expression_list; AssignExpression assign_expression; BinaryExpression binary_expression; Expression *minus_expression; IfExpression if_expression; FunctionCallExpression function_call_expression; WhileExpression while_expression; SinExpression sin_expression; CosExpression cos_expression; PowerExpression power_expression; } u; }; /* create.c */ void clc_function_define(char *identifier, ParameterList *parameter_list, Expression *expression_list); ParameterList *clc_create_parameter(char *identifier); ParameterList *clc_chain_parameter(ParameterList *list, char *identifier); Expression *clc_alloc_expression(ExpressionType type); Expression *clc_create_expression_list(Expression *expression); Expression *clc_chain_expression_list(Expression *list, Expression *add); Expression *clc_create_assign_expression(char *variable, Expression *operand); Expression *clc_create_binary_expression(ExpressionType operator, Expression *left, Expression *right); Expression *clc_create_minus_expression(Expression *operand); Expression *clc_create_identifier_expression(char *identifier); Expression *clc_create_if_expression(Expression *condition, Expression *then_expression, Expression *else_expression); Expression *clc_create_while_expression(Expression *condition, Expression *expression); Expression *clc_create_sin_expression(Expression *inside_bracket); Expression *clc_create_cos_expression(Expression *inside_bracket); Expression *clc_create_power_expression(Expression *atai, Expression *jyosu); Expression *clc_create_function_call_expression(char *func_name, Expression *expression);
そして、IDENTIFIERとしてのPOWトークンをcalc.lに実装する。 (ここで、COSトークンの実装を忘れていたことを発見する。)
<INITIAL>"pow" return POW; <INITIAL>"cos" return COS; <INITIAL>"sin" return SIN; <INITIAL>"define" return DEFINE; <INITIAL>"if" return IF; <INITIAL>"else" return ELSE; <INITIAL>"while" return WHILE; <INITIAL>"(" return LP; <INITIAL>")" return RP; <INITIAL>"{" return LC; <INITIAL>"}" return RC; <INITIAL>";" return SEMICOLON; <INITIAL>"," return COMMA; <INITIAL>"=" return ASSIGN; <INITIAL>"==" return EQ; <INITIAL>"!=" return NE; <INITIAL>">" return GT; <INITIAL>">=" return GE; <INITIAL>"<" return LT; <INITIAL>"<=" return LE; <INITIAL>"+" return ADD; <INITIAL>"-" return SUB; <INITIAL>"*" return MUL; <INITIAL>"/" return DIV; <INITIAL>"%" return MOD; <INITIAL>[A-Za-z_][A-Za-z_0-9]* { yylval.identifier = clc_malloc(strlen(yytext) + 1); strcpy(yylval.identifier, yytext); return IDENTIFIER; }
次に、create.cをいじる。
Expression * clc_create_power_expression(Expression *atai, Expression *jyosu) { Expression *exp; exp = clc_alloc_expression(POWER_EXPRESSION); exp->u.power_expression.atai = atai; exp->u.power_expression.jyosu = jyosu; return exp; }
さらに、eval.cをいじる。
static int eval_binary_int(ExpressionType operator, int left, int right) { int result; switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result = left + right; break; case SUB_EXPRESSION: result = left - right; break; case MUL_EXPRESSION: result = left * right; break; case DIV_EXPRESSION: result = left / right; break; case MOD_EXPRESSION: result = left % right; break; case EQ_EXPRESSION: result = left == right; break; case NE_EXPRESSION: result = left != right; break; case GT_EXPRESSION: result = left > right; break; case GE_EXPRESSION: result = left >= right; break; case LT_EXPRESSION: result = left < right; break; case LE_EXPRESSION: result = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case POWER_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case...%d", operator)); } return result; } static void eval_binary_double(ExpressionType operator, double left, double right, Value *result) { if (operator == ADD_EXPRESSION || operator == SUB_EXPRESSION || operator == MUL_EXPRESSION || operator == DIV_EXPRESSION || operator == MOD_EXPRESSION) { result->type = DOUBLE_VALUE; } else { DBG_assert(operator == EQ_EXPRESSION || operator == NE_EXPRESSION || operator == GT_EXPRESSION || operator == GE_EXPRESSION || operator == LT_EXPRESSION || operator == LE_EXPRESSION, ("operator..%d\n", operator)); result->type = INT_VALUE; } switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result->u.double_value = left + right; break; case SUB_EXPRESSION: result->u.double_value = left - right; break; case MUL_EXPRESSION: result->u.double_value = left * right; break; case DIV_EXPRESSION: result->u.double_value = left / right; break; case MOD_EXPRESSION: result->u.double_value = fmod(left, right); break; case EQ_EXPRESSION: result->u.int_value = left == right; break; case NE_EXPRESSION: result->u.int_value = left != right; break; case GT_EXPRESSION: result->u.int_value = left > right; break; case GE_EXPRESSION: result->u.int_value = left >= right; break; case LT_EXPRESSION: result->u.int_value = left < right; break; case LE_EXPRESSION: result->u.int_value = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case POWER_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad default...%d", operator)); } } static Value eval_power_expression(LocalEnvironment *env, Expression *atai, Expression *jyosu) { Value atai_val; Value jyosu_val; Value result; for (;;) { atai_val = eval_expression(env, atai); if (atai_val.type == DOUBLE_VALUE) { jyosu_val = eval_expression(env, jyosu); if (jyosu_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = pow(atai_val.u.double_value, jyosu_val.u.double_value); break; } else if(jyosu_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = pow(atai_val.u.double_value, jyosu_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } else if(atai_val.type == INT_VALUE) { jyosu_val = eval_expression(env, jyosu); if (jyosu_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = pow(atai_val.u.int_value, jyosu_val.u.double_value); break; } else if(jyosu_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = pow(atai_val.u.int_value, jyosu_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; }
条件分岐が複雑だが、他にいい方法が思いつかなかった。
コンパイルをする。エラーが出力されなかったのでメッセージは省略する。
動作実験をする。
$ ./mycalc >pow(2,2); >>4.000000 >pow(2,10); >>1024.000000 >pow(3.14, 45); >>23005631724245846327296.000000 >pow(2,-2); >>0.250000 >pow(2,-5); >>0.031250 >pow(2,0); >>1.000000 >
動いた!
こうなると、いちいち3.1415…と入力するのがバカらしくなるので、 例えばpiで補完できるようにしたくなる。Linux版glibc2.2.2のmath.h には、以下の定数が定義されている(math.hより抜粋)。
/* Some useful constants. */ #if defined __USE_BSD || defined __USE_XOPEN # define M_E 2.7182818284590452354 /* e */ # define M_LOG2E 1.4426950408889634074 /* log_2 e */ # define M_LOG10E 0.43429448190325182765 /* log_10 e */ # define M_LN2 0.69314718055994530942 /* log_e 2 */ # define M_LN10 2.30258509299404568402 /* log_e 10 */ # define M_PI 3.14159265358979323846 /* pi */ # define M_PI_2 1.57079632679489661923 /* pi/2 */ # define M_PI_4 0.78539816339744830962 /* pi/4 */ # define M_1_PI 0.31830988618379067154 /* 1/pi */ # define M_2_PI 0.63661977236758134308 /* 2/pi */ # define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ # define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ # define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ #endif
まずは、三角関数を定義したので、使用頻度が高いと思われる pから定義することにした。
これは単純で、PIなるトークンを定義し、それを字句解析部で置きかえて しまえばよい。
calc.lをいじる。
#include <math.h> <INITIAL>"pi" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_PI; yylval.expression = expression; return DOUBLE_LITERAL; }
ちなみに、上記規則は
<INITIAL>[A-Za-z_][A-Za-z_0-9]* { yylval.identifier = clc_malloc(strlen(yytext) + 1); strcpy(yylval.identifier, yytext); return IDENTIFIER; }の前に入れないと、この規則の正規表現にひっかかってしまい、 コンパイルエラーとなるので注意する必要がある。
コンパイルは、エラーなく終了したのでメッセージは省略する。
動作実験をする。
$ ./mycalc >pi; >>3.141593 >sin(pi); >>0.000000 >cos(pi); >>-1.000000 >sin(pi/2); >>1.000000 >cos(pi/2); >>0.00 >
動いた!
次に、math.hにある残りのものも実装してしまうことにした。
定数名は、それぞれC言語のものを流用することにした。 そのため、pの定数名も変更することにした。
calc.lを変更する。
<INITIAL>"M_PI" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_PI; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_E" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_E; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_LOG2E" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_LOG2E; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_LOG10E" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_LOG10E; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_LN2" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_LN2; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_LN10" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_LN10; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_PI_2" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_PI_2; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_PI_4" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_PI_4; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_1_PI" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_1_PI; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_2_PI" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_2_PI; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_2_SQRTPI" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_2_SQRTPI; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_SQRT2" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_SQRT2; yylval.expression = expression; return DOUBLE_LITERAL; } <INITIAL>"M_SQRT1_2" { Expression *expression = clc_alloc_expression(DOUBLE_EXPRESSION); expression->u.double_value = M_SQRT1_2; yylval.expression = expression; return DOUBLE_LITERAL; }
コンパイルは、エラーなく終了したのでメッセージは省略する。
動作実験をする。
$ ./mycalc >M_PI; >>3.141593 >M_E; >>2.718282 >M_LOG2E; >>1.442695 >M_LN2; >>0.693147 >M_LN10; >>2.302585 >M_PI_2; >>1.570796 >M_PI_4; >>0.785398 >M_1_PI; >>0.318310 >M_2_PI; >>0.636620 >M_SQRT2; >>1.414214 >M_SQRT1_2; >>0.707107 >
動いた!
そして、よくよく考えると、例えばlog2eが定義されているのに、対数関数が 定義されていないのは意味がないので、指数関数と対数関数を定義することにした。 平方根に関しては、pow(羃乗)で代用できるので、いらないような気もしたが、 いちいち
> pow(値, 1/2);と入力するのもバカバカしい話なので実装することにした。
まずは平方根関数から実装することにした。 これは、式として定義することにした。sin式と同じ方式である。
構文規則はこのようにする。
sqrt式=‘sqrt’‘(’式‘)’
まずはcalc.lをいじる。
<INITIAL>"sqrt" return SQRT;
次にcalc.hをいじる。
typedef enum { INT_EXPRESSION = 1, DOUBLE_EXPRESSION, IDENTIFIER_EXPRESSION, EXPRESSION_LIST_EXPRESSION, ASSIGN_EXPRESSION, ADD_EXPRESSION, SUB_EXPRESSION, MUL_EXPRESSION, DIV_EXPRESSION, MOD_EXPRESSION, EQ_EXPRESSION, NE_EXPRESSION, GT_EXPRESSION, GE_EXPRESSION, LT_EXPRESSION, LE_EXPRESSION, MINUS_EXPRESSION, IF_EXPRESSION, WHILE_EXPRESSION, SIN_EXPRESSION, COS_EXPRESSION, POWER_EXPRESSION, SQRT_EXPRESSION, FUNCTION_CALL_EXPRESSION, EXPRESSION_TYPE_NUM } ExpressionType; typedef struct { Expression *inside_bracket ; } SqrtExpression; struct Expression_tag { ExpressionType type; union { int int_value; double double_value; char *identifier; ExpressionList expression_list; AssignExpression assign_expression; BinaryExpression binary_expression; Expression *minus_expression; IfExpression if_expression; FunctionCallExpression function_call_expression; WhileExpression while_expression; SinExpression sin_expression; CosExpression cos_expression; PowerExpression power_expression; SqrtExpression sqrt_expression; } u; }; /* create.c */ void clc_function_define(char *identifier, ParameterList *parameter_list, Expression *expression_list); ParameterList *clc_create_parameter(char *identifier); ParameterList *clc_chain_parameter(ParameterList *list, char *identifier); Expression *clc_alloc_expression(ExpressionType type); Expression *clc_create_expression_list(Expression *expression); Expression *clc_chain_expression_list(Expression *list, Expression *add); Expression *clc_create_assign_expression(char *variable, Expression *operand); Expression *clc_create_binary_expression(ExpressionType operator, Expression *left, Expression *right); Expression *clc_create_minus_expression(Expression *operand); Expression *clc_create_identifier_expression(char *identifier); Expression *clc_create_if_expression(Expression *condition, Expression *then_expression, Expression *else_expression); Expression *clc_create_while_expression(Expression *condition, Expression *expression); Expression *clc_create_sin_expression(Expression *inside_bracket); Expression *clc_create_cos_expression(Expression *inside_bracket); Expression *clc_create_power_expression(Expression *atai, Expression *jyosu); Expression *clc_create_sqrt_expression(Expression *inside_bracket); Expression *clc_create_function_call_expression(char *func_name, Expression *expression);
次にcalc.yをいじる。
%token SQRT POW COS SIN DEFINE IF ELSE WHILE LP RP LC RC SEMICOLON COMMA ASSIGN EQ NE GT GE LT LE ADD SUB MUL DIV MOD %type <expression> expression expression_list equality_expression relational_expression additive_expression multiplicative_expression unary_expression postfix_expression primary_expression if_expression while_expression sin_expression cos_expression power_expression sqrt_expression primary_expression : IDENTIFIER LP expression_list RP { $$ = clc_create_function_call_expression($1, $3); } | IDENTIFIER LP RP { $$ = clc_create_function_call_expression($1, NULL); } | if_expression | while_expression | sin_expression | cos_expression | power_expression | sqrt_expression | LP expression RP { $$ = $2; } | IDENTIFIER { $$ = clc_create_identifier_expression($1); } | INT_LITERAL | DOUBLE_LITERAL ; sqrt_expression : SQRT LP expression RP { $$ = clc_create_sqrt_expression($3); } ;
次に、create.cをいじる。
Expression * clc_create_sqrt_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(SQRT_EXPRESSION); exp->u.sqrt_expression.inside_bracket = inside_bracket; return exp; }
次に、eval.cをいじる。処理の方法は、sinやcosとまったく同じで、 括弧内の式を評価した後、その値がdoubleかintかそれ以外で 条件分岐をする方法である。
static int eval_binary_int(ExpressionType operator, int left, int right) { int result; switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result = left + right; break; case SUB_EXPRESSION: result = left - right; break; case MUL_EXPRESSION: result = left * right; break; case DIV_EXPRESSION: result = left / right; break; case MOD_EXPRESSION: result = left % right; break; case EQ_EXPRESSION: result = left == right; break; case NE_EXPRESSION: result = left != right; break; case GT_EXPRESSION: result = left > right; break; case GE_EXPRESSION: result = left >= right; break; case LT_EXPRESSION: result = left < right; break; case LE_EXPRESSION: result = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case POWER_EXPRESSION: /* FALLTHRU */ case SQRT_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case...%d", operator)); } return result; } static void eval_binary_double(ExpressionType operator, double left, double right, Value *result) { if (operator == ADD_EXPRESSION || operator == SUB_EXPRESSION || operator == MUL_EXPRESSION || operator == DIV_EXPRESSION || operator == MOD_EXPRESSION) { result->type = DOUBLE_VALUE; } else { DBG_assert(operator == EQ_EXPRESSION || operator == NE_EXPRESSION || operator == GT_EXPRESSION || operator == GE_EXPRESSION || operator == LT_EXPRESSION || operator == LE_EXPRESSION, ("operator..%d\n", operator)); result->type = INT_VALUE; } switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result->u.double_value = left + right; break; case SUB_EXPRESSION: result->u.double_value = left - right; break; case MUL_EXPRESSION: result->u.double_value = left * right; break; case DIV_EXPRESSION: result->u.double_value = left / right; break; case MOD_EXPRESSION: result->u.double_value = fmod(left, right); break; case EQ_EXPRESSION: result->u.int_value = left == right; break; case NE_EXPRESSION: result->u.int_value = left != right; break; case GT_EXPRESSION: result->u.int_value = left > right; break; case GE_EXPRESSION: result->u.int_value = left >= right; break; case LT_EXPRESSION: result->u.int_value = left < right; break; case LE_EXPRESSION: result->u.int_value = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case POWER_EXPRESSION: /* FALLTHRU */ case SQRT_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad default...%d", operator)); } } static Value eval_sqrt_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sqrt(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = sqrt(inside_bracket_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; } static Value eval_expression(LocalEnvironment *env, Expression *expr) { Value v; switch (expr->type) { case INT_EXPRESSION: v = eval_int_expression(expr->u.int_value); break; case DOUBLE_EXPRESSION: v = eval_double_expression(expr->u.double_value); break; case IDENTIFIER_EXPRESSION: v = eval_identifier_expression(env, expr->u.identifier); break; case EXPRESSION_LIST_EXPRESSION: v = eval_expression_list_expression (env, expr->u.expression_list.expression, expr->u.expression_list.next); break; case ASSIGN_EXPRESSION: v = eval_assign_expression(env, expr->u.assign_expression.variable, expr->u.assign_expression.operand); break; case ADD_EXPRESSION: /* FALLTHRU */ case SUB_EXPRESSION: /* FALLTHRU */ case MUL_EXPRESSION: /* FALLTHRU */ case DIV_EXPRESSION: /* FALLTHRU */ case MOD_EXPRESSION: /* FALLTHRU */ case EQ_EXPRESSION: /* FALLTHRU */ case NE_EXPRESSION: /* FALLTHRU */ case GT_EXPRESSION: /* FALLTHRU */ case GE_EXPRESSION: /* FALLTHRU */ case LT_EXPRESSION: /* FALLTHRU */ case LE_EXPRESSION: /* FALLTHRU */ v = clc_eval_binary_expression(env, expr->type, expr->u.binary_expression.left, expr->u.binary_expression.right); break; case MINUS_EXPRESSION: v = clc_eval_minus_expression(env, expr->u.minus_expression); break; case IF_EXPRESSION: v = eval_if_expression(env, expr->u.if_expression.condition, expr->u.if_expression.then_expression, expr->u.if_expression.else_expression); break; case WHILE_EXPRESSION: v = eval_while_expression(env, expr->u.while_expression.condition, expr->u.while_expression.expression_list); break; case SIN_EXPRESSION: v = eval_sin_expression(env, expr->u.sin_expression.inside_bracket); break; case COS_EXPRESSION: v = eval_cos_expression(env, expr->u.cos_expression.inside_bracket); break; case POWER_EXPRESSION: v = eval_power_expression(env, expr->u.power_expression.atai, expr->u.power_expression.jyosu); break; case SQRT_EXPRESSION: v = eval_sqrt_expression(env, expr->u.sqrt_expression.inside_bracket); break; case FUNCTION_CALL_EXPRESSION: v = eval_function_call_expression (env, expr->u.function_call_expression.identifier, expr->u.function_call_expression.argument); break; case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case. type..%d\n", expr->type)); } return v; }
コンパイルする。エラーなく通ったので、メッセージは省略する。
動作実験をする。
$ ./mycalc >sqrt(4); >>2.000000 >M_SQRT2; >>1.414214 >sqrt(2); >>1.414214 >
きちんと動いている。
ここまでくると流れ作業と化しているので、指数関数、対数関数は 一度に実装してしまう。
ユーザインタフェースは、C言語のものを採用する。つまり、 exp() 関数は自然対数の底(e)のx乗の値を返し、log() 関数はxの自然対数を返し、 log10() 関数は x の常用対数を返すようにする。
構文規則はそれぞれこのようにする。
exp式=‘exp’‘(’式‘)’ log式=‘log’‘(’式‘)’ log10式=‘log10’‘(’式‘)’
まずはcalc.lをいじる。
<INITIAL>"exp" return EXP; <INITIAL>"log" return LOG; <INITIAL>"log10" return LOG10;
次にcalc.yをいじる。
%token EXP LOG LOG10 SQRT POW COS SIN DEFINE IF ELSE WHILE LP RP LC RC SEMICOLON COMMA ASSIGN EQ NE GT GE LT LE ADD SUB MUL DIV MOD %type <expression> expression expression_list equality_expression relational_expression additive_expression multiplicative_expression unary_expression postfix_expression primary_expression if_expression while_expression sin_expression cos_expression power_expression sqrt_expression exp_expression log_expression log10_expression primary_expression : IDENTIFIER LP expression_list RP { $$ = clc_create_function_call_expression($1, $3); } | IDENTIFIER LP RP { $$ = clc_create_function_call_expression($1, NULL); } | if_expression | while_expression | sin_expression | cos_expression | power_expression | sqrt_expression | exp_expression | log_expression | log10_expression | LP expression RP { $$ = $2; } | IDENTIFIER { $$ = clc_create_identifier_expression($1); } | INT_LITERAL | DOUBLE_LITERAL ; exp_expression : EXP LP expression RP { $$ = clc_create_exp_expression($3); } ; log_expression : LOG LP expression RP { $$ = clc_create_log_expression($3); } ; log10_expression : LOG10 LP expression RP { $$ = clc_create_log10_expression($3); } ;
次にcalc.hをいじる。
typedef enum { INT_EXPRESSION = 1, DOUBLE_EXPRESSION, IDENTIFIER_EXPRESSION, EXPRESSION_LIST_EXPRESSION, ASSIGN_EXPRESSION, ADD_EXPRESSION, SUB_EXPRESSION, MUL_EXPRESSION, DIV_EXPRESSION, MOD_EXPRESSION, EQ_EXPRESSION, NE_EXPRESSION, GT_EXPRESSION, GE_EXPRESSION, LT_EXPRESSION, LE_EXPRESSION, MINUS_EXPRESSION, IF_EXPRESSION, WHILE_EXPRESSION, SIN_EXPRESSION, COS_EXPRESSION, POWER_EXPRESSION, SQRT_EXPRESSION, EXP_EXPRESSION, LOG_EXPRESSION, LOG10_EXPRESSION, FUNCTION_CALL_EXPRESSION, EXPRESSION_TYPE_NUM } ExpressionType; typedef struct { Expression *inside_bracket ; } ExpExpression; typedef struct { Expression *inside_bracket ; } LogExpression; typedef struct { Expression *inside_bracket ; } Log10Expression; struct Expression_tag { ExpressionType type; union { int int_value; double double_value; char *identifier; ExpressionList expression_list; AssignExpression assign_expression; BinaryExpression binary_expression; Expression *minus_expression; IfExpression if_expression; FunctionCallExpression function_call_expression; WhileExpression while_expression; SinExpression sin_expression; CosExpression cos_expression; PowerExpression power_expression; SqrtExpression sqrt_expression; ExpExpression exp_expression; LogExpression log_expression; Log10Expression log10_expression; } u; }; /* create.c */ void clc_function_define(char *identifier, ParameterList *parameter_list, Expression *expression_list); ParameterList *clc_create_parameter(char *identifier); ParameterList *clc_chain_parameter(ParameterList *list, char *identifier); Expression *clc_alloc_expression(ExpressionType type); Expression *clc_create_expression_list(Expression *expression); Expression *clc_chain_expression_list(Expression *list, Expression *add); Expression *clc_create_assign_expression(char *variable, Expression *operand); Expression *clc_create_binary_expression(ExpressionType operator, Expression *left, Expression *right); Expression *clc_create_minus_expression(Expression *operand); Expression *clc_create_identifier_expression(char *identifier); Expression *clc_create_if_expression(Expression *condition, Expression *then_expression, Expression *else_expression); Expression *clc_create_while_expression(Expression *condition, Expression *expression); Expression *clc_create_sin_expression(Expression *inside_bracket); Expression *clc_create_cos_expression(Expression *inside_bracket); Expression *clc_create_power_expression(Expression *atai, Expression *jyosu); Expression *clc_create_sqrt_expression(Expression *inside_bracket); Expression *clc_create_exp_expression(Expression *inside_bracket); Expression *clc_create_log_expression(Expression *inside_bracket); Expression *clc_create_log10_expression(Expression *inside_bracket); Expression *clc_create_function_call_expression(char *func_name, Expression *expression);
次にcreate.cをいじる。
Expression * clc_create_exp_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(EXP_EXPRESSION); exp->u.exp_expression.inside_bracket = inside_bracket; return exp; } Expression * clc_create_log_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(LOG_EXPRESSION); exp->u.log_expression.inside_bracket = inside_bracket; return exp; } Expression * clc_create_log10_expression(Expression *inside_bracket) { Expression *exp; exp = clc_alloc_expression(LOG10_EXPRESSION); exp->u.log10_expression.inside_bracket = inside_bracket; return exp; }
次にeval.cをいじる。
static int eval_binary_int(ExpressionType operator, int left, int right) { int result; switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result = left + right; break; case SUB_EXPRESSION: result = left - right; break; case MUL_EXPRESSION: result = left * right; break; case DIV_EXPRESSION: result = left / right; break; case MOD_EXPRESSION: result = left % right; break; case EQ_EXPRESSION: result = left == right; break; case NE_EXPRESSION: result = left != right; break; case GT_EXPRESSION: result = left > right; break; case GE_EXPRESSION: result = left >= right; break; case LT_EXPRESSION: result = left < right; break; case LE_EXPRESSION: result = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case POWER_EXPRESSION: /* FALLTHRU */ case SQRT_EXPRESSION: /* FALLTHRU */ case EXP_EXPRESSION: /* FALLTHRU */ case LOG_EXPRESSION: /* FALLTHRU */ case LOG10_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad case...%d", operator)); } return result; } static void eval_binary_double(ExpressionType operator, double left, double right, Value *result) { if (operator == ADD_EXPRESSION || operator == SUB_EXPRESSION || operator == MUL_EXPRESSION || operator == DIV_EXPRESSION || operator == MOD_EXPRESSION) { result->type = DOUBLE_VALUE; } else { DBG_assert(operator == EQ_EXPRESSION || operator == NE_EXPRESSION || operator == GT_EXPRESSION || operator == GE_EXPRESSION || operator == LT_EXPRESSION || operator == LE_EXPRESSION, ("operator..%d\n", operator)); result->type = INT_VALUE; } switch (operator) { case INT_EXPRESSION: /* FALLTHRU */ case DOUBLE_EXPRESSION: /* FALLTHRU */ case IDENTIFIER_EXPRESSION: /* FALLTHRU */ case EXPRESSION_LIST_EXPRESSION: /* FALLTHRU */ case ASSIGN_EXPRESSION: DBG_assert(0, ("bad case...%d", operator)); break; case ADD_EXPRESSION: result->u.double_value = left + right; break; case SUB_EXPRESSION: result->u.double_value = left - right; break; case MUL_EXPRESSION: result->u.double_value = left * right; break; case DIV_EXPRESSION: result->u.double_value = left / right; break; case MOD_EXPRESSION: result->u.double_value = fmod(left, right); break; case EQ_EXPRESSION: result->u.int_value = left == right; break; case NE_EXPRESSION: result->u.int_value = left != right; break; case GT_EXPRESSION: result->u.int_value = left > right; break; case GE_EXPRESSION: result->u.int_value = left >= right; break; case LT_EXPRESSION: result->u.int_value = left < right; break; case LE_EXPRESSION: result->u.int_value = left <= right; break; case MINUS_EXPRESSION: /* FALLTHRU */ case IF_EXPRESSION: /* FALLTHRU */ case WHILE_EXPRESSION: /* FALLTHRU */ case SIN_EXPRESSION: /* FALLTHRU */ case COS_EXPRESSION: /* FALLTHRU */ case POWER_EXPRESSION: /* FALLTHRU */ case SQRT_EXPRESSION: /* FALLTHRU */ case EXP_EXPRESSION: /* FALLTHRU */ case LOG_EXPRESSION: /* FALLTHRU */ case LOG10_EXPRESSION: /* FALLTHRU */ case FUNCTION_CALL_EXPRESSION: /* FALLTHRU */ case EXPRESSION_TYPE_NUM: /* FALLTHRU */ default: DBG_assert(0, ("bad default...%d", operator)); } } static Value eval_exp_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = exp(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = exp(inside_bracket_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; } static Value eval_log_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = log(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = log(inside_bracket_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; } static Value eval_log10_expression(LocalEnvironment *env, Expression *inside_bracket) { Value inside_bracket_val; Value result; for (;;) { inside_bracket_val = eval_expression(env, inside_bracket); if (inside_bracket_val.type == DOUBLE_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = log10(inside_bracket_val.u.double_value); break; } else if (inside_bracket_val.type == INT_VALUE) { result.type = DOUBLE_VALUE; result.u.double_value = log10(inside_bracket_val.u.int_value); break; } else { clc_runtime_error(INT_OR_DOUBLE_EXPECTED_ERR, NULL); } } return result; }
コンパイルをする。エラーが出力されなかったのでメッセージは省略する。
動作実験をする。
$ ./mycalc >exp(1); >>2.718282 >M_E; >>2.718282 >log(M_E); >>1.000000 >log10(10); >>1.000000 >log10(1); >>0.000000 >動いた!
mycalcのコンパイルが通るまで6時間を費やしてしまった。不覚。
前橋氏の解説ページでは、「詳しいことはソースを読め」という説明(?)が 多く、慣れないCの文法を読むのに苦労した。
8/12以降、理工UNIXシステムが停止し、DNSまでもが引けなくなってしまった。 ウェブは見られない、メールは出せない、のでどうやって提出しようかパニックに 陥った。
http://member.nifty.ne.jp/maebashi/programmer/c_yota/calc.html