view Bison-Flex/CALC/discrete-parser/EUC/parser.cpp @ 0:db40c85cad7a default tip

upload sample source
author nobuyasu <dimolto@cr.ie.u-ryukyu.ac.jp>
date Mon, 09 May 2011 03:11:59 +0900
parents
children
line wrap: on
line source

//
// 四則演算パーサー
//
//	Chihiro.SAKAMOTO
//
#include <climits>
#include <cstdio>
#include "parser.h"

cparser::cparser()
	: token_type(OP_EOF)
{
}

cparser::~cparser()
{
}

// 構文解析
//
//	statement ::= <value> "=" <expr>
//				| "print" <expr>
//				| "list"
//
bool cparser::parse(calc_driver *driver, const std::string &f)
{
	if ((fp = std::fopen(f.c_str(), "r")) == NULL)
		return false;

	int token;
	std::string str;
	while ((token = get_token(str)) != OP_EOF) {
		if (token == OP_LIST) {				// listコマンド
			driver->list();
		}
		else if (token == OP_PRINT) {		// printコマンド
			cnode *node = add_sub_expr();
			driver->print(node);
			delete node;
		}
		else if (token == OP_VALUE) {		// 代入
			std::string equ;
			if (get_token(equ) != OP_EQU)
				return false;
			cnode *node = add_sub_expr();
			driver->assign(str, node);
			delete node;
		}
		else if (token != OP_NEWLINE) {		// 空行以外はエラー
			return false;
		}

		token = get_token(str);
		if (token != OP_NEWLINE)			// 行末の\nを読み捨てる
			unget_token(token, str);
	}

	std::fclose(fp);

	return true;
}

// +−の処理
//	expr ::= <muldiv> (("+" | "-") <muldiv>)*

cnode *cparser::add_sub_expr()
{
	cnode *left = mul_div_expr();
	if  (left == NULL)
		return NULL;

	for (;;) {
		std::string str;
		int token = get_token(str);
		if (token != OP_PLUS && token != OP_MINUS) {
			unget_token(token, str);
			break;
		}

		cnode *right = mul_div_expr();
		if (right == NULL) {
			delete left;
			return NULL;
		}
		left = new cnode(token, left, right);
	}
	return left;
}

// */の処理
//	muldiv ::= <term> (("*" | "/") <term>)*

cnode *cparser::mul_div_expr()
{
	cnode *left = term();
	if  (left == NULL)
		return NULL;

	for (;;) {
		std::string str;
		int token = get_token(str);
		if (token != OP_TIMES && token != OP_DIVIDE) {
			unget_token(token, str);
			break;
		}

		cnode *right = term();
		if (right == NULL) {
			delete left;
			return NULL;
		}
		left = new cnode(token, left, right);
	}
	return left;
}

// 終端
//	term ::= "-" <term> | "(" <expr> ")" | <number> | <value>

cnode *cparser::term()
{
	std::string str;
	int token = get_token(str);

	if (token == OP_MINUS) {
		return new cnode(OP_NEG, term());
	}
	if (token == OP_OPAR) {
		cnode *node = add_sub_expr();
		if (node == NULL)
			return NULL;

		token = get_token(str);
		if (token != OP_CPAR) {
			delete node;
			unget_token(token, str);
			return NULL;
		}
		return node;
	}
	if (token == OP_CONST) {
		return new cnode(OP_CONST, atoi(str.c_str()));
	}
	if (token == OP_VALUE) {
		return new cnode(OP_VALUE, new std::string(str));
	}
	return NULL;
}

// ロケール無視の文字判定

inline bool _isspace(int c)
{
	return c == ' ' || c == '\t';
}

inline bool _isnum(int c)
{
	return c >= '0' && c <= '9';
}

inline bool _isalpha(int c)
{
	return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_';
}

// 字句解析
//
// トークン					トークン種別
//
// '+'						OP_PLUS
// '-'						OP_MINUS
// '*'						OP_TIMES
// '/'						OP_DIVIDE
// '('						OP_OPAR
// ')'						OP_CPAR
// '='						OP_EQU
// '\n'						OP_NEWLINE
// EOF						OP_EOF
//
// "print"					OP_PRINT
// "list"					OP_LIST
// [0-9]+					OP_CONST
// [a-zA-Z_][a-zA-Z0-9_]*	OP_VALUE
//
int cparser::_get_token(std::string &str)
{
	int c;

	// 先行する空白類を読み飛ばす。
	do {
		c = std::fgetc(fp);
	} while (_isspace(c)) ;

	// strは再利用されているかもしれないので、クリアしておく
	str.clear();

	// 記号の場合は、1文字で1トークンなので、種別を返す。
	switch (c) {
	  case '+':		return OP_PLUS;
	  case '-':		return OP_MINUS;
	  case '*':		return OP_TIMES;
	  case '/':		return OP_DIVIDE;
	  case '(':		return OP_OPAR;
	  case ')':		return OP_CPAR;
	  case '=':		return OP_EQU;
	  case '\n':	return OP_NEWLINE;
	  case EOF:		return OP_EOF;
	}

	// 1文字目が数字の場合は、数字文字列としてトークンを切り出す。
	if (_isnum(c)) {
		while (_isnum(c)) {
			str += c;
			c = std::fgetc(fp);
		}
		std::ungetc(c, fp);
		return OP_CONST;
	}

	// 1文字目が英字の場合は、「識別子」としてトークンを切り出す。
	if (_isalpha(c)) {
		while (_isalpha(c) || _isnum(c)) {
			str += c;
			c = fgetc(fp);
		}
		std::ungetc(c, fp);

		// 切り出したトークンが「予約語」の場合は、対応したトークン種別を返す。
		if (str == "print")
			return OP_PRINT;
		if (str == "list")
			return OP_LIST;
		return OP_VALUE;
	}

	// その他の文字はエラーとする。
	return OP_ERROR;
}