解析器と構文木と評価器を分ける

前回 0 除算の実行時エラーの処理がノードのクラスにありました
ノードのクラスなのに実行時のエラー処理まで持っているのは美しくないですね
これはノードのクラスに評価器の機能まで持たせてしまっているのが原因でした
構文木と評価器とは分けてしまいましょう
そしてついでなので解析器も分けてしまいましょう

仕組み

解析器は、中身自体は今までの string_calc クラスと同じなのですが、エラー処理を分離するためにコールバック用のインターフェイスを持つようにします
そしてエラー時にメンバ関数を呼び出していたところをこのインターフェイスのメソッドを呼び出すように置き換えます

評価器は、評価の仕組み自体は同じなので、ノードにあった評価に関連した機能をそのまま評価器に移してしまえばいいだけです

ノードの実装

class node {
	std::string	m_operator;
	std::list<node>	m_operands;
	any_value_t	m_value;
public:
	const std::string&	get_operator() const {
		return	m_operator;
	}
	size_t	get_operand_count() const {
		return	m_operands.size();
	}
	const node&	operator[]( size_t	n ) const {
		std::list<node>::const_iterator	p = m_operands.begin();
		std::advance( p, n );
		return	*p;
	}
	const any_value_t&	get_value() const {
		return	m_value;
	}
	void	clear(){
		m_operator.clear();
		m_operands.clear();
		m_value.clear();
	}
	void	assign( const std::string&	operator_new, const node&	operand1 ){
		clear();
		m_operator = operator_new;
		m_operands.push_back( operand1 );
	}
	void	assign( const std::string&	operator_new, const node&	operand1, const node&	operand2 ){
		clear();
		m_operator = operator_new;
		m_operands.push_back( operand1 );
		m_operands.push_back( operand2 );
	}
	void	assign( const std::string&	operator_new, const std::list<node>&	operands ){
		clear();
		m_operator = operator_new;
		m_operands = operands;
	}
	void	assign( const std::string&	name, const std::string&	value ){
		clear();
		m_value.assign( name, value );
	}
	
	node&	operator=( const node&	rhs ){
		if( this != &rhs ){
			m_operator = rhs.m_operator;
			m_operands = rhs.m_operands;
			m_value = rhs.m_value;
		}
		return	*this;
	}
	node():
	m_operator(),
	m_operands(),
	m_value()
	{
	}
	node( const node&	rhs ):
	m_operator(rhs.m_operator),
	m_operands(rhs.m_operands),
	m_value(rhs.m_value)
	{
	}
	node( const std::string&	operator_new, const node&	operand1 ):
	m_operator(),
	m_operands(),
	m_value()
	{
		assign( operator_new, operand1 );
	}
	node( const std::string&	operator_new, const node&	operand1, const node&	operand2 ):
	m_operator(),
	m_operands(),
	m_value()
	{
		assign( operator_new, operand1, operand2 );
	}
	node( const std::string&	operator_new, std::list<node>	operands ):
	m_operator(),
	m_operands(),
	m_value()
	{
		assign( operator_new, operands );
	}
	node( const std::string&	name, const std::string&	value ):
	m_operator(),
	m_operands(),
	m_value()
	{
		assign( name, value );
	}
};

評価器の実装

struct evaluator_callback {
	virtual void	runtime_error_devide_by_zero() = 0;
};
class evaluator {
	evaluator_callback*	m_callback;
	
	bool	operator_comma( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			success = eval( _node[1], presult );
		}
		return	success;
	}
	bool	operator_add( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			any_value_t	rhs;
			success = eval( _node[1], &rhs );
			if( success ){
				success = presult->operator_assign_add( rhs );
			}
		}
		return	success;
	}
	bool	operator_sub( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			any_value_t	rhs;
			success = eval( _node[1], &rhs );
			if( success ){
				success = presult->operator_assign_sub( rhs );
			}
		}
		return	success;
	}
	bool	operator_mul( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			any_value_t	rhs;
			success = eval( _node[1], &rhs );
			if( success ){
				success = presult->operator_assign_mul( rhs );
			}
		}
		return	success;
	}
	bool	operator_div( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			any_value_t	rhs;
			success = eval( _node[1], &rhs );
			if( success ){
				if( rhs ){
					success = presult->operator_assign_div( rhs );
				}
				else{
					if( m_callback ){
						m_callback->runtime_error_devide_by_zero();
					}
					success = false;
				}
			}
		}
		return	success;
	}
	bool	operator_mod( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			any_value_t	rhs;
			success = eval( _node[1], &rhs );
			if( success ){
				if( rhs ){
					success = presult->operator_assign_mod( rhs );
				}
				else{
					if( m_callback ){
						m_callback->runtime_error_devide_by_zero();
					}
					success = false;
				}
			}
		}
		return	success;
	}
	bool	operator_unary_plus( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			*presult = presult->operator_unary_plus();
		}
		return	success;
	}
	bool	operator_unary_minus( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			*presult = presult->operator_unary_minus();
		}
		return	success;
	}
	bool	operator_unary_logical_not( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			*presult = presult->operator_unary_logical_not();
		}
		return	success;
	}
	bool	operator_unary_conditional_not( const node&	_node, any_value_t*	presult ){
		bool	success = eval( _node[0], presult );
		if( success ){
			*presult = presult->operator_unary_conditional_not();
		}
		return	success;
	}
	
	// とりあえず代入は無効
	evaluator&	operator=( const evaluator&	rhs );
	evaluator( const evaluator&	rhs );
public:
	bool	eval( const node&	_node, any_value_t*	presult ){
		bool	success = true;
		if( presult ){
			const std::string&	op = _node.get_operator();
			if( op.empty()){
				*presult = _node.get_value();
			}
			else if(( op == "," )&&( _node.get_operand_count() == 2 )){
				success = operator_comma( _node, presult );
			}
			else if(( op == "+" )&&( _node.get_operand_count() == 2 )){
				success = operator_add( _node, presult );
			}
			else if(( op == "-" )&&( _node.get_operand_count() == 2 )){
				success = operator_sub( _node, presult );
			}
			else if(( op == "*" )&&( _node.get_operand_count() == 2 )){
				success = operator_mul( _node, presult );
			}
			else if(( op == "/" )&&( _node.get_operand_count() == 2 )){
				success = operator_div( _node, presult );
			}
			else if(( op == "%" )&&( _node.get_operand_count() == 2 )){
				success = operator_mod( _node, presult );
			}
			else if(( op == "+" )&&( _node.get_operand_count() == 1 )){
				success = operator_unary_plus( _node, presult );
			}
			else if(( op == "-" )&&( _node.get_operand_count() == 1 )){
				success = operator_unary_minus( _node, presult );
			}
			else if(( op == "~" )&&( _node.get_operand_count() == 1 )){
				success = operator_unary_logical_not( _node, presult );
			}
			else if(( op == "!" )&&( _node.get_operand_count() == 1 )){
				success = operator_unary_conditional_not( _node, presult );
			}
			else{
				success = false;
			}
		}
		return	success;
	}
	evaluator( evaluator_callback*	callback ):
	m_callback(callback)
	{
	}
};

解析器の実装

ここでは istringstream クラスを protected継承していますが、この parser クラスの派生クラスを作ることを想定しないのであればここは private継承の方が適当でしょう

struct parser_callback {
	virtual void	error_illegal_character( char	c ) = 0;
	virtual void	error_unexpected_eof() = 0;
	virtual void	error_unbalanced_parenthesis() = 0;
};
class parser : protected istringstream<char> {
	parser_callback*	m_callback;
	
	bool	skipspace();
	bool	operator_new_number( node*	pnode );
	bool	constant( node*	pnode );
	bool	operator_unary_prefix( node*	pnode );
	bool	operator_mul_div_mod_( node*	pnode );
	bool	operator_mul_div_mod( node*	pnode );
	bool	operator_add_sub_( node*	pnode );
	bool	operator_add_sub( node*	pnode );
	bool	operator_comma_( node*	pnode );
	bool	operator_comma( node*	pnode );
	bool	full_expression( node*	pnode );
	bool	compile( node*	pnode );
	
	// とりあえず代入は無効
	parser&	operator=( const parser&	rhs );
	parser( const parser&	rhs );
public:
	bool	compile( const std::string&	s, node*	pnode );
	
	parser( parser_callback*	callback );
};

解析器・評価器を使う

解析器・評価器を使うクラスでは解析器に渡す parser_callback のインスタンスと評価器に渡す evaluator_callback インターフェイスインスタンスが必要です
それぞれを実装したクラスを別に用意しても構わないのですが、ここでは面倒なので両方のインターフェイスを基底クラスにします
この基底クラスは別に誰に見せるわけでもないので private継承が適当でしょう
(「この string_calc クラスを継承した派生クラスで独自のエラー処理ができるようにしたい」というような場合には、protected継承が妥当でしょう)

class string_calc : private parser_callback, private evaluator_callback {
	void	error_illegal_character( char	c );
	void	error_unexpected_eof();
	void	error_unbalanced_parenthesis();
	void	runtime_error_devide_by_zero();
	
	// とりあえず代入は無効
	string_calc&	operator=( const string_calc&	rhs );
	string_calc( const string_calc&	rhs );
public:
	bool	eval( const std::string&	s, any_value_t*	presult );
};

void	string_calc::runtime_error_devide_by_zero(){
	// 0 除算
	OutputDebugString( "runtime error: devide by zero.\n" );
}
void	string_calc::error_illegal_character( char	c ){
	// 変な文字だからお終い
	OutputDebugString( stringprintf( "error: illegal caracter '%c'.\n", c ));
}
void	string_calc::error_unexpected_eof(){
	// 予期せぬ EOF
	OutputDebugString( "error: unexpected EOF.\n" );
}
void	string_calc::error_unbalanced_parenthesis(){
	// 対応する () が見つからないから終わり
	OutputDebugString( "error: unbalanced parenthesis.\n" );
}
bool	string_calc::eval( const std::string&	s, any_value_t*	presult ){
	node	syntax_tree;
	parser	p( this );
	bool	success = p.compile( s, &syntax_tree );
	if( success ){
		evaluator	e( this );
		success = e.eval( syntax_tree, presult );
	}
	return	success;
}

評価器だけを使う

評価器が独立したことからこんな使い方もできるようになりました

#define	integer( n )	node( TYPEOF_INTEGER, #n )
#define	unary_operator( _operator_, operand )	node((_operator_), (operand))
#define	binary_operator( lhs, _operator_, rhs )	node((_operator_), (lhs), (rhs))
#define	negative( operand )	unary_operator( "-", (operand))
#define	sub( lhs, rhs )	binary_operator( (lhs), "-", (rhs))

node	root = sub( negative( sub( integer( 1 ), integer( 2 ))), integer( 3 ));
evaluator	e( NULL );
any_value_t	result;
bool	success = e.eval( root, &result );
if( success ){
	OutputDebugString( stringprintf( "%s=%g\n", "-(1-2)-3", result.operator_typecast_to_double()));
}