実数も使えるようにする
これまでは使える数値は整数だけで実数を指定する方法がありませんでした
実数も使えるようにしてみましょう
仕組み
実数も使えるようにしたパーサが処理すべき式は必ず次のような形式になっています
完結式 ::= 部分式_comma
部分式_comma ::= 部分式_add_sub 部分式_comma_
部分式_comma_ ::= 演算子_comma 部分式_add_sub 部分式_comma_
演算子_comma ::= ,
部分式_add_sub ::= 部分式_mul_div_mod 部分式_add_sub_
部分式_add_sub_ ::= 演算子_add_sub 部分式_mul_div_mod 部分式_add_sub
演算子_add_sub ::= + あるいは −
部分式_mul_div_mod ::= 部分式_unary 部分式_mul_div_mod_
部分式_mul_div_mod_ ::= 演算子_mul_div_mod 部分式_unary 部分式_mul_div_mod_
演算子_mul_div_mod ::= * あるいは / あるいは %
部分式_unary ::= 演算子_unary 定数式
演算子_unary ::= + あるいは − あるいは ~ あるいは !
定数式 ::= 数値 あるいは ( 部分式_comma )
数値 ::= 実数 あるいは 整数
実数 ::= [0-9]*.[0-9]+ あるいは [0-9]+. あるいは [0-9]*.?[0-9]+[eE] ± [0-9]+ あるいは [0-9]+.[eE] ± [0-9]+
整数 ::= 16進数 あるいは 8進数 あるいは 10進数
16進数 ::= 0x[0-9a-fA-F]+
8進数 ::= 0[0-7]*
10進数 ::= [1-9][0-9]*
実数として有効な形式かどうかのチェックなどは面倒なのでとりあえず strtod にでもやらせておきましょう
「数値」の評価に関するところだけなので any_value_t クラスを実数対応にしたら、パーサ側では strtol に加えて strtod を呼ぶようにすればいいだけです
簡単ですね
では実装してみましょう
実装
bool string_calc::operator_new_number( any_value_t* presult ){ bool success = true; if( !skipspace()){ error_unexpected_eof(); success = false; } else{ const char* s = getp(); char* endp = const_cast<char*>(s); strtol( s, &endp, 0 ); if(( *endp == '.' )||( *endp == 'e' )||( *endp == 'E' )){ strtod( s, &endp ); size_type cch = endp - s; if( !cch ){ error_illegal_character( *s ); success = false; } else{ std::string value = gets( cch ); if( presult ){ presult->assign( TYPEOF_FLOAT, value ); } } } else{ size_type cch = endp - s; std::string value = gets( cch ); if( presult ){ presult->assign( TYPEOF_INTEGER, value ); } } } return success; } bool string_calc::constant( any_value_t* presult ){ bool success = true; if( !skipspace()){ error_unexpected_eof(); success = false; } else{ char c = at( 0, SEEK_CUR ); if( c == '(' ){ size_type offset_prev = tell(); seek( 1, SEEK_CUR ); success = operator_comma( presult ); if( success ){ skipspace(); c = getch(); if( c != ')' ){ error_illegal_character( c ); seek( offset_prev, SEEK_SET ); error_unbalanced_parenthesis(); success = false; } } } else if( isdigit( c ) <strong>||( c == '.' )</strong>){ success = operator_new_number( presult ); } else{ error_illegal_character( c ); success = false; } } return success; }
any_value_t を実数対応に改造しましょう
#define TYPEOF_INTEGER "Integer" #define TYPEOF_FLOAT "Float" class any_value_t { std::string m_name; std::string m_value; public: std::string get_value() const { return m_value; } void put_value( const std::string& value_new ){ m_value = value_new; } std::string get_name() const { return m_name; } void put_name( const std::string& name_new ){ m_name = name_new; } bool defined() const { return !m_name.empty(); } any_value_t& assign( const std::string& name, const std::string& value ){ m_name = name; m_value = value; return *this; } void undef(){ m_name.clear(); m_value.clear(); } bool is_float() const { return ( m_name == TYPEOF_FLOAT ); } double operator_typecast_to_double() const { const char* p = &m_value[0]; char* endp = const_cast<char*>(p); return strtod( p, &endp ); } long operator_typecast_to_long() const { if( is_float()){ return ( long )operator_typecast_to_double(); } else{ const char* p = &m_value[0]; char* endp = const_cast<char*>(p); return strtol( p, &endp, 0 ); } } unsigned long operator_typecast_to_ulong() const { if( is_float()){ return ( unsigned long )operator_typecast_to_double(); } else{ const char* p = &m_value[0]; char* endp = const_cast<char*>(p); return strtoul( p, &endp, 0 ); } } any_value_t& operator_assign( const any_value_t& rhs ){ return operator=( rhs ); } any_value_t& operator_assign_add( const any_value_t& rhs ){ if( is_float() || rhs.is_float()){ double l = operator_typecast_to_double(); double r = rhs.operator_typecast_to_double(); l += r; return assign( TYPEOF_FLOAT, stringprintf( "%g", l )); } else{ long l = operator_typecast_to_long(); long r = rhs.operator_typecast_to_long(); l += r; return assign( TYPEOF_INTEGER, stringprintf( "%d", l )); } } any_value_t& operator_assign_sub( const any_value_t& rhs ){ if( is_float() || rhs.is_float()){ double l = operator_typecast_to_double(); double r = rhs.operator_typecast_to_double(); l -= r; return assign( TYPEOF_FLOAT, stringprintf( "%g", l )); } else{ long l = operator_typecast_to_long(); long r = rhs.operator_typecast_to_long(); l -= r; return assign( TYPEOF_INTEGER, stringprintf( "%d", l )); } } any_value_t& operator_assign_mul( const any_value_t& rhs ){ if( is_float() || rhs.is_float()){ double l = operator_typecast_to_double(); double r = rhs.operator_typecast_to_double(); l *= r; return assign( TYPEOF_FLOAT, stringprintf( "%g", l )); } else{ long l = operator_typecast_to_long(); long r = rhs.operator_typecast_to_long(); l *= r; return assign( TYPEOF_INTEGER, stringprintf( "%d", l )); } } any_value_t& operator_assign_div( const any_value_t& rhs ){ if( is_float() || rhs.is_float()){ double l = operator_typecast_to_double(); double r = rhs.operator_typecast_to_double(); l /= r; return assign( TYPEOF_FLOAT, stringprintf( "%g", l )); } else{ long l = operator_typecast_to_long(); long r = rhs.operator_typecast_to_long(); l /= r; return assign( TYPEOF_INTEGER, stringprintf( "%d", l )); } } any_value_t& operator_assign_mod( const any_value_t& rhs ){ long l = operator_typecast_to_long(); long r = rhs.operator_typecast_to_long(); l %= r; return assign( TYPEOF_INTEGER, stringprintf( "%d", l )); } any_value_t operator_add( const any_value_t& rhs ) const { any_value_t result = *this; return result.operator_assign_add( rhs ); } any_value_t operator_sub( const any_value_t& rhs ) const { any_value_t result = *this; return result.operator_assign_sub( rhs ); } any_value_t operator_mul( const any_value_t& rhs ) const { any_value_t result = *this; return result.operator_assign_mul( rhs ); } any_value_t operator_div( const any_value_t& rhs ) const { any_value_t result = *this; return result.operator_assign_div( rhs ); } any_value_t operator_mod( const any_value_t& rhs ) const { any_value_t result = *this; return result.operator_assign_mod( rhs ); } any_value_t operator_unary_plus() const { if( is_float()){ any_value_t result = *this; return result.assign( TYPEOF_FLOAT, stringprintf( "%g", +result.operator_typecast_to_double())); } else{ any_value_t result = *this; return result.assign( TYPEOF_INTEGER, stringprintf( "%d", +result.operator_typecast_to_long())); } } any_value_t operator_unary_minus() const { if( is_float()){ any_value_t result = *this; return result.assign( TYPEOF_FLOAT, stringprintf( "%g", -result.operator_typecast_to_double())); } else{ any_value_t result = *this; return result.assign( TYPEOF_INTEGER, stringprintf( "%d", -result.operator_typecast_to_long())); } } any_value_t operator_unary_conditional_not() const { if( is_float()){ any_value_t result = *this; return result.assign( TYPEOF_FLOAT, ( result.operator_typecast_to_double() != 0.0 ) ? "1" : "0" ); } else{ any_value_t result = *this; return result.assign( TYPEOF_INTEGER, !operator_typecast_to_bool() ? "1" : "0" ); } } any_value_t operator_unary_logical_not() const { any_value_t result = *this; return result.assign( TYPEOF_INTEGER, stringprintf( "%u", ~result.operator_typecast_to_ulong())); } bool operator_typecast_to_bool() const { return !!operator_typecast_to_long(); } operator bool() const { return operator_typecast_to_bool(); } any_value_t& operator=( const any_value_t& rhs ){ if( this != &rhs ){ m_name = rhs.m_name; m_value = rhs.m_value; } return *this; } any_value_t(): m_name(), m_value() { } any_value_t( const any_value_t& rhs ): m_name(rhs.m_name), m_value(rhs.m_value) { } any_value_t( const std::string& name, const std::string& value ): m_name(name), m_value(value) { } };
練習
ここまでの機能でだいたい電卓が作れそうなくらいにはなりました
初心者の方などはこの辺りで関数電卓でも作ってみるといいですよ