C言語アルゴリズム-計算式
前へ 目次へ 次へ 

3.3 変数の処理

入力された計算式を評価するevale.cに変数が使用できる処理を追加する。

 計算式には、数値以外に変数が使用できる。変数名は先頭の1文字だけで区別するので、使用できる変数は26個だけである。また、変数は0で初期化されるものとする。
 変数に値を格納するために代入文が使用可能である。

実行結果
OK : a=11+20*30
OK : ?a
 611.000000
OK : x=1.23
OK : y=4.56
OK : z=x*10+y*10
OK : ?x;y,z
 1.230000  4.560000      57.900000
OK : ?1.2-3.4+1.2
-1.000000
OK : !
<変数>=<値>
  <値>:数値、変数、計算式
 また、<値>を表示するために ? 命令を追加してある。使用方法は、BASICのPRINT命令と同じである。
? 12, a; 1+2

 プログラムを終了するための ! 命令(BASICのEND命令と同じ)を追加する。
 ただし、これまでのように計算式だけでは実行できない。


[evalv.c]
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <setjmp.h>
#include "gettoken.h"       /* gettoken.c */

double expression(void);    /* 式 */
double term(void);          /* 項 */
double factor(void);        /* 因子 */
double number(void);        /* 数値データ */
void errorx(char *, char *);    /* エラー処理 */
void assign(void);          /* 代入文 */
void exec_print(void);      /* ? 命令 */
jmp_buf env;            /* setjmp 用 */
double  val[26];        /* 変数用 A - Z */

void main(void)
{
    while(1){
        printf("OK : ");
        gets(gt_line);
        get_token();
        if(*token == '\0')      /* 空入力 */
            break;
        if(setjmp(env) == 0){       /* longjmp()の戻り位置 */
            if(*token == '?')           /* 表示 */
                exec_print();
            else if(*token == '!')      /* 終了 */
                break;
            else if(isalpha(*token))    /* 代入文 */
                assign();
            else
                errorx("このような命令はない", token);
        }
    }
}
void assign(void)
{
    int     index;

    index = toupper(*token) - 'A';    /* 格納場所である配列の添え字を計算 */
    get_token();
    if(*token != '=')
        errorx("代入文の文法がおかしい", token);
    get_token();
    /* 評価結果を変数領域に格納 */
    val[index] = expression();
}
void exec_print(void)
{
    while(1){
        get_token();
        if(*token == '\0')
            break;
        else
            printf("% f", expression());
        /* 継続処理 */
        if(*token == ',')
            printf("\t");
        else if(*token == ';')
            printf(" ");
        else if(*token == '\0')
            break;
        else
            errorx("?命令に誤りがある", token);
    }
    printf("\n");
}
double expression(void)
{
    double  ans;

    ans = term();
    while(1){
        if(*token == '+'){
            get_token();
            ans = ans + term();
        }else if(*token == '-'){
            get_token();
            ans = ans - term();
        }else
            break;
    }
    return(ans);
}
double term(void)
{
    double  ans, x;

    ans = factor();
    while(1){
        if(*token == '*'){
            get_token();
            ans = ans * factor();
        }else if(*token == '/'){
            get_token();
            x = factor();
            if(x == 0)
                errorx("0では割れない", "0");
            ans = ans / x;
        }else
            break;
    }
    return(ans);
}
double factor(void)
{
    double  ans;

    if(*token == '('){
        get_token();
        ans = expression();
        if(*token != ')'){
            printf(")がない\n");
            exit(1);
        }
        get_token();
        return(ans);
    }else
        return(number());
}
double number(void)
{
    double  ans;
    char    sign;

    if(*token == '+' || *token == '-'){     /* 符号判定 */
        sign = *token;
        get_token();
    }
    if(isalpha(*token))         /* 先頭英字 */
        ans = val[toupper(*token) - 'A'];   /* 変数の内容取得 */
    else if(isdigit(*token))    /* 先頭数字 */
        ans = atof(token);      /* 数字を実数値に変換 */
    else
        errorx("数字または変数名でなければならない", token);
    get_token();
    if(strchr("+-*/),;", *token) == NULL)
        errorx("演算子でなければならない", token);
    if(sign == '-')
        return(-ans);
    return(ans);
}
void errorx(char *s1, char *s2)
{
    printf("%s <%s>\n", s1, s2);
    do{
        get_token();    /* 行の終わりまで読み飛ばす */
    }while(*token != '\0');
    longjmp(env, 1);
}

 16行目は、変数のデータを格納するための配列をグローバルで宣言している。変数Aの値は配列val[0]に、変数Zの値は配列val[25]に格納される。これは、A〜Zの文字コード(0x41〜0x5a)が連続しているので、各文字コードから0x41(A)を減じれば配列の添え字が得られる。
 27〜34行で、入力データが表示命令(?)、終了命令(!)か代入文かを判定している。

 代入文を処理する関数assignでは、
 42行目で、右辺の変数値の格納場所である配列の添え字を計算している。変数は英小文字でも使えるように関数toupperで大文字に変換してから計算している。
 43行目で、次のトークンを取り出す。代入文であるから=のはずである。
 44行目で、=かどうか判定し、=でなければ45行目でエラー処理関数errorxを呼び出している。
 48行目で、計算式処理の関数expressionを呼び出し、その結果を配列valに格納している。

 表示命令(?)を処理する関数exec_printでは、
 57行目で、<値>の表示をしている。 <値>:数値、変数、計算式
 59〜62行目で、続きの<値>があるかどうかを判定している。

 値を取り出す関数numberでは、値の種類として変数が出てくる。
 132行目で、変数かどうか判定し、変数であれば133行目でその値を取り出している。
 134行目で、数字かどうか判定し、数字であれば135行目で数値に変換している。
 139行目の、関数strchrは第1引数の文字列の中に第2引数の文字があるかどうかを調べる関数である。無ければNULLを返す。


前へ 目次へ 次へ 
Copyright © 2001 Hiroshi Masuda 

 

 

inserted by FC2 system