view boost-spirit/CALC/AST/EUC/calc.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

#include <boost/spirit/core.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/tree/ast.hpp>
#include <boost/spirit/utility/escape_char.hpp>
#include <boost/spirit/iterator/position_iterator.hpp>
#include <boost/spirit/iterator/file_iterator.hpp>
#include <boost/spirit/utility/functor_parser.hpp>
#include <iostream>
#include <map>
#include <string>
#include <cassert>
#include <algorithm>

using namespace std;
using namespace boost::spirit;

// ノードのID
enum	{
	ID_NEG = 1,
	ID_PLUS,
	ID_MINUS,
	ID_TIMES,
	ID_DIVIDE,
	ID_SUBST,
	ID_VALUE,
	ID_CONST,
	ID_PRIME,
	ID_UNARY,
	ID_MUL_EXPR,
	ID_ADD_EXPR,
	ID_STATEMENT,
	ID_INPUT,
	ID_ASSIGN,
	ID_PRINT,
	ID_LIST,
} ;

typedef file_iterator<>							iterator_t;
typedef tree_match<iterator_t>::tree_iterator	tree_iter_t;

map<string, int> values;	// 変数テーブル

struct error_parser {
	typedef nil_t result_t;		// パーサーの結果型(nil_t)

	error_parser(char const *msg)
		: msg_(msg)
	{
	}

	template <typename ScannerT>
	int operator()(ScannerT const &scan, result_t &result) const
	{
		// 終わりまで来たら-1を返す
		if (scan.at_end()) {
			return -1;
		}

		cout << msg_ << endl;
		// 解釈した「長さ」を返すと、その分スキップするので
		// 改行までをスキャンし、その分をスキップ
		return (int)(*(anychar_p - '\n') >> ch_p('\n')).parse(scan).length();
	}

  private:
	const char *msg_;
} ;

typedef functor_parser<error_parser> error_p;

// エラー処理パーサー定義
error_p syntax_error_p = error_parser("文法エラー");

// 文法定義
struct calc_grammer: public grammar<calc_grammer> {
	template <typename ScannerT>
	struct definition {
		rule< ScannerT, parser_context<>, parser_tag<ID_VALUE> >		identifier;
		rule< ScannerT, parser_context<>, parser_tag<ID_CONST> >		number;
		rule< ScannerT, parser_context<>, parser_tag<ID_NEG> >			minus;
		rule< ScannerT, parser_context<>, parser_tag<ID_PLUS> >			add;
		rule< ScannerT, parser_context<>, parser_tag<ID_MINUS> >		sub;
		rule< ScannerT, parser_context<>, parser_tag<ID_TIMES> >		mul;
		rule< ScannerT, parser_context<>, parser_tag<ID_DIVIDE> >		div;
		rule< ScannerT, parser_context<>, parser_tag<ID_SUBST> >		subst;
		rule< ScannerT, parser_context<>, parser_tag<ID_PRIME> >		prime;
		rule< ScannerT, parser_context<>, parser_tag<ID_UNARY> >		unary;
		rule< ScannerT, parser_context<>, parser_tag<ID_MUL_EXPR> >		mul_expr;
		rule< ScannerT, parser_context<>, parser_tag<ID_ADD_EXPR> >		add_expr;
		rule< ScannerT, parser_context<>, parser_tag<ID_ASSIGN> >		assign;
		rule< ScannerT, parser_context<>, parser_tag<ID_PRINT> >		print;
		rule< ScannerT, parser_context<>, parser_tag<ID_LIST> >			list;
		rule< ScannerT, parser_context<>, parser_tag<ID_STATEMENT> >	statement;
		rule< ScannerT, parser_context<>, parser_tag<ID_INPUT> >		input;

		definition(calc_grammer const& self)
		{
			identifier = lexeme_d[leaf_node_d[alpha_p >> *alnum_p]];
			number = uint_p;

			minus = ch_p('-');
			add   = ch_p('+');
			sub   = ch_p('-');
			mul   = ch_p('*');
			div   = ch_p('/');
			subst = ch_p('=');

			prime	= identifier
					| number
					| inner_node_d['(' >> add_expr >> ')']
					;

			unary	= prime
					| root_node_d[minus] >> prime
					;

			mul_expr = unary >> *(root_node_d[mul | div] >> unary);

			add_expr = mul_expr >> *(root_node_d[add | sub] >> mul_expr);

			assign = identifier >> root_node_d[subst] >> add_expr;
			print = str_p("print") >> add_expr;
			list = str_p("list");

			statement = root_node_d[assign | print | list] >> discard_node_d[ch_p('\n')];

			// 入力は、「文」、「空行」もしくは「エラー」
			input = *(statement | discard_node_d[ch_p('\n')] | syntax_error_p);
		}

		rule< ScannerT, parser_context<>, parser_tag<ID_INPUT> > const& start() const
		{
			return input;
		}
	};
} ;

// ツリーのイテレータから、変数を取り出す

int &value_ref(const tree_iter_t &it)
{
	return values[string(it->value.begin(), it->value.end())];
}

// リストコマンドの関数オブジェクト

struct list_action {
	void operator()(const std::pair<std::string, int> &it)
	{
		cout << it.first << " = " << it.second << endl;
	}
} ;

int eval(const tree_iter_t &it)
{
	if (it->value.id() == ID_NEG) {
        assert(it->children.size() == 1);
		return -eval(it->children.begin());
	}
	else if (it->value.id() == ID_PLUS) {
		assert(it->children.size() == 2);
		return eval(it->children.begin()) + eval(it->children.begin() + 1);
	}
	else if (it->value.id() == ID_MINUS) {
		assert(it->children.size() == 2);
		return eval(it->children.begin()) - eval(it->children.begin() + 1);
	}
	else if (it->value.id() == ID_TIMES) {
		assert(it->children.size() == 2);
		return eval(it->children.begin()) * eval(it->children.begin() + 1);
	}
	else if (it->value.id() == ID_DIVIDE) {
		assert(it->children.size() == 2);
		return eval(it->children.begin()) / eval(it->children.begin() + 1);
	}
	else if (it->value.id() == ID_SUBST) {
		assert(it->children.size() == 2);
		return value_ref(it->children.begin()) = eval(it->children.begin() + 1);
	}
	else if (it->value.id() == ID_VALUE) {
		return values[string(it->value.begin(), it->value.end())];
	}
	else if (it->value.id() == ID_CONST) {
		return atoi(string(it->value.begin(), it->value.end()).c_str());
	}
	else if (it->value.id() == ID_PRINT) {
		cout << eval(it->children.begin() + 1) << endl;
	}
	else if (it->value.id() == ID_LIST) {
		for_each(values.begin(), values.end(), list_action());
	}
	else {
		cout << "エラー" << endl;
	}
	return 0;
}

// main

int main(int argc, char *argv[])
{
	// ファイルの読み込みに、file_iteratorを使用する
	file_iterator<> first("input.txt");
	if (!first) {
		cout << "ファイルがオープンできません。" << endl;
		return 1;
	}
	file_iterator<> last = first.make_end();

	// 抽象構文木作成
	calc_grammer gr;
	tree_parse_info<iterator_t>	info;		// 解析情報を保持する変数
	info = ast_parse(first, last, gr, ch_p(' ') | ch_p('\t') | ch_p('\r'));

	if (info.full) {
		const tree_iter_t &root = info.trees.begin();
		size_t size = root->children.size();
		for(size_t i=0; i<size; i++) {
			eval(root->children.begin() + i);
		}
	}
	else {
		// エラー処理が入っているので、エラー処理されるとここには来ない
		// エラー処理パーサーで、エラー処理を行ったら失敗としたほうが
		// 良い。
		cout << "構文解析失敗" << endl;
	}

	return 0;
}