解析器と構文木と評価器を分ける
前回 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
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())); }