ステップ12

数式解析

 数式解析とは字句解析によってできたトークン列の関係性を把握し、構文木を作ることです。これを行うことにより、定められた文法や規則に基づき、計算式をチェックすることができます。

図12-1 構文木

  上のような図を構文木といいます。
 ()や√のような先にしなければならない計算が下の方へなど、演算子の計算手順に合わせて計算式を成り立たせます。

■数式解析プログラム

#include <stdio.h>

#define NOTOKEN		(-3)
#define TokenError	(-2)
#define EOF			(-1)
#define Number	0	// 整数
#define Plus	1	// +
#define Minus	2	// -
#define Aster	3	// *
#define Slash	4	// /
#define LPar	5	// (
#define RPar	6	// )

char *nextch = "";
int has_stock = -1; // -1:初期状態  0:字句を戻していない状態  1:字句を戻した状態
int stock_code;
int stock_value;

// 字句操作関数群
void setText(char *s);
int  nextToken(int *value);
void putBack(void);

int expression(void);

#define BUFFSIZE	256

int syntaxError = 0;

/*
 * 因子 ::= 整数 | '(' 式 ')'
 */
int factor(void){
	int code, value;
	code = nextToken(&value);

	// (
	if (code == LPar) {
		int answer = expression();
		code = nextToken(&value);
		if (code != RPar)
			syntaxError = 1;
		return answer;
	}

	if (code != Number)
		syntaxError = 1;

	return value;
}

/*
 * 項 ::= 因子 | 項 (*|/) 因子
 */
int term(void){
	int code, value, answer;

	answer = factor();
	if (syntaxError)
		return answer;
	while (((code = nextToken(&value)) == Aster ) || (code == Slash)){ // * or / を見つけたら計算する。
		int t = factor();
		if (code == Aster)
			answer *= t;
		else
			answer /= t;
	}
	putBack();
	return answer;
}

/*
 * 式 ::= (+|-)? 項 | 式 (+|-) 項
 */
int expression(void){
	int code, value, answer, sign = 1;

	code = nextToken(&value);
	if (code == Minus)	//ここで数字が+か-を判断する。
		sign = -1;
	else if (code == Plus)
		;
	else
		putBack();

	answer = sign * term();
	if (syntaxError)
		return answer;
	while (((code = nextToken(&value)) == Plus ) || (code == Minus)){//+ or -を見つけたら計算する。
		int t = term();
		answer += (code == Plus) ? t : (-t);
	}
	putBack();
	return answer;
}

/*
 * 行 ::= 式?  EOF
 */
void line(void){
	int code, value, answer;

	code = nextToken(&value);
	if (code != EOF){
		putBack();
		answer = expression();
		if (!syntaxError)
			printf("\t%d\n", answer);
		code = nextToken(&value);
		if (code != EOF)
			syntaxError = 1;
	}
}
void setText(char *s){
	nextch = s;	//この中に解析対象の文字列を入れる。
}

int getToken(int *value){
	int c;

	// 空白を読み捨てる。
	while (isspace(*nextch))
		nextch++;

	if (!*nextch)
		return EOF;

	c = *nextch++;
	if (c == '+')
		return Plus;
	if (c == '-')
		return Minus;
	if (c == '*')
		return Aster;
	if (c == '/')
		return Slash;
	if (c == '(')
		return LPar;
	if (c == ')')
		return RPar;

	if (isdigit(c)){
		*value = 0;
		while (isdigit(c)){
			*value = 10 * *value + c - '0';
			c = *nextch++;
		}
		nextch--;
		return Number;
	}

	return TokenError; // エラー
}

/*
 * 字句をひとつ戻す。(ように見せかける)
 */
void putBack(void){
	if (has_stock == 0)
		has_stock = 1;
}

/*
 * 次の字句の字句コードを返す。
 * 次の字句が整数の場合は、
 * その数値を *valueへセットし、字句コードNumberを返す。
 */
int nextToken(int *value){
	if (has_stock > 0){
		has_stock = 0;
		*value = stock_value;
		return stock_code;
	}
	has_stock = 0;
	stock_code = getToken(&stock_value);
	*value = stock_value;
	return stock_code;
}

int main(){
	char buff[BUFFSIZE];

	fprintf(stderr, "%% ");
	while (gets(buff)){
		syntaxError = 0;
		setText(buff);
		line();
		if (syntaxError)
			fprintf(stderr, "構文エラー!\n");

		fprintf(stderr, "%% ");
	}
	fprintf(stderr, "See you.");

	return 0;
}

 ※このプログラムでは√の計算はできません。次のステップで√の計算ができるようになります。

■完成写真

次のステップでは、これまで学んだことを活かして関数電卓を作ってみましょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です