diff --git a/src/condition.cpp b/src/condition.cpp index 5e0093d92f7a5..c4bb35c7a2ffc 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -93,14 +93,16 @@ namespace struct deferred_math { JsonObject jo; std::string str; - bool assignment; + math_type_t type; std::shared_ptr exp; - deferred_math( JsonObject const &jo_, std::string_view str_, bool ass_ ) - : jo( jo_ ), str( str_ ), assignment( ass_ ), exp( std::make_shared() ) { + deferred_math( JsonObject const &jo_, std::string_view str_, math_type_t type_ ) + : jo( jo_ ), str( str_ ), type( type_ ), exp( std::make_shared() ) { jo.allow_omitted_members(); } + + void _validate_type() const; }; struct condition_parser { @@ -156,9 +158,10 @@ void clear_deferred_math() get_deferred_math().swap( empty ); } -std::shared_ptr &defer_math( JsonObject const &jo, std::string_view str, bool ass ) +std::shared_ptr &defer_math( JsonObject const &jo, std::string_view str, + math_type_t type ) { - get_deferred_math().emplace( jo, str, ass ); + get_deferred_math().emplace( jo, str, type ); return get_deferred_math().back().exp; } @@ -197,7 +200,7 @@ dbl_or_var_part get_dbl_or_var_part( const JsonValue &jv, std::string_view membe JsonObject jo = jv.get_object(); if( jo.has_array( "math" ) ) { ret_val.math_val.emplace(); - ret_val.math_val->from_json( jo, "math", eoc_math::type_t::ret ); + ret_val.math_val->from_json( jo, "math", math_type_t::ret ); } else { jo.allow_omitted_members(); ret_val.var_val = read_var_info( jo ); @@ -247,7 +250,7 @@ duration_or_var_part get_duration_or_var_part( const JsonValue &jv, const std::s JsonObject jo = jv.get_object(); if( jo.has_array( "math" ) ) { ret_val.math_val.emplace(); - ret_val.math_val->from_json( jo, "math", eoc_math::type_t::ret ); + ret_val.math_val->from_json( jo, "math", math_type_t::ret ); } else { jo.allow_omitted_members(); ret_val.var_val = read_var_info( jo ); @@ -604,7 +607,8 @@ void finalize_conditions() while( !dfr.empty() ) { deferred_math &math = dfr.front(); try { - math.exp->parse( math.str, math.assignment, false ); + math.exp->parse( math.str, false ); + math._validate_type(); } catch( std::invalid_argument const &ex ) { JsonObject jo{ std::move( math.jo ) }; clear_deferred_math(); @@ -1945,10 +1949,9 @@ conditional_t::func f_has_ammo() conditional_t::func f_math( const JsonObject &jo, const std::string_view member ) { eoc_math math; - math.from_json( jo, member, eoc_math::type_t::compare ); - return [math = std::move( math )]( const_dialogue const & d ) { - dialogue loosey_goosey( d ); - return math.act( loosey_goosey ); + math.from_json( jo, member, math_type_t::compare ); + return [math = std::move( math )]( const_dialogue const & d ) ->bool { + return math.act( d ); }; } @@ -2474,131 +2477,36 @@ conditional_t::get_set_dbl( std::string_view checked_value, char scope ) throw std::invalid_argument( string_format( R"(Invalid aspect "%s" for val())", checked_value ) ); } -void eoc_math::_validate_type( JsonArray const &objects, type_t type_ ) const +void deferred_math::_validate_type() const { - if( type_ != type_t::compare && action >= oper::equal ) { - objects.throw_error( "Comparison operators can only be used in conditional statements" ); - } else if( type_ == type_t::compare && action < oper::equal ) { - if( action == oper::assign ) { - objects.throw_error( - R"(Assignment operator "=" can't be used in a conditional statement. Did you mean to use "=="? )" ); - } else if( action != oper::ret ) { - objects.throw_error( "Only comparison operators can be used in conditional statements" ); - } - } else if( type_ == type_t::ret && action > oper::ret ) { - objects.throw_error( "Only return expressions are allowed in this context" ); - } else if( type_ != type_t::ret && action == oper::ret ) { - objects.throw_error( "Return expression in assignment context has no effect" ); + math_type_t exp_type = exp->get_type(); + if( exp_type == math_type_t::assign && type != math_type_t::assign ) { + jo.throw_error( + R"(Assignment operators can't be used in this context. Did you mean to use "=="? )" ); + } else if( exp_type != math_type_t::assign && type == math_type_t::assign ) { + jo.throw_error( + R"(Eval statement in assignment context has no effect)" ); } } -void eoc_math::from_json( const JsonObject &jo, std::string_view member, type_t type_ ) +void eoc_math::from_json( const JsonObject &jo, std::string_view member, math_type_t type_ ) { JsonArray const objects = jo.get_array( member ); - if( objects.size() > 3 ) { - jo.throw_error( "Invalid number of args in " + jo.str() ); - return; - } - - std::string const oper = objects.size() >= 2 ? objects.get_string( 1 ) : std::string{}; - - if( objects.size() == 1 ) { - action = oper::ret; - } else if( objects.size() == 2 ) { - if( oper == "++" ) { - action = oper::increase; - } else if( oper == "--" ) { - action = oper::decrease; - } else { - jo.throw_error( "Invalid unary operator in " + jo.str() ); - return; - } - } else if( objects.size() == 3 ) { - rhs = defer_math( jo, objects.get_string( 2 ), false ); - if( oper == "=" ) { - action = oper::assign; - } else if( oper == "+=" ) { - action = oper::plus_assign; - } else if( oper == "-=" ) { - action = oper::minus_assign; - } else if( oper == "*=" ) { - action = oper::mult_assign; - } else if( oper == "/=" ) { - action = oper::div_assign; - } else if( oper == "%=" ) { - action = oper::mod_assign; - } else if( oper == "==" ) { - action = oper::equal; - } else if( oper == "!=" ) { - action = oper::not_equal; - } else if( oper == "<" ) { - action = oper::less; - } else if( oper == "<=" ) { - action = oper::equal_or_less; - } else if( oper == ">" ) { - action = oper::greater; - } else if( oper == ">=" ) { - action = oper::equal_or_greater; - } else { - jo.throw_error( "Invalid binary operator in " + jo.str() ); - return; - } - } - _validate_type( objects, type_ ); - bool const lhs_assign = action >= oper::assign && action <= oper::decrease; - lhs = defer_math( jo, objects.get_string( 0 ), lhs_assign ); - if( action >= oper::plus_assign && action <= oper::decrease ) { - mhs = defer_math( jo, objects.get_string( 0 ), false ); + std::string combined; + for( size_t i = 0; i < objects.size(); i++ ) { + combined.append( objects.get_string( i ) ); } + exp = defer_math( jo, combined, type_ ); } double eoc_math::act( dialogue &d ) const { - switch( action ) { - case oper::ret: - return lhs->eval( d ); - case oper::assign: - lhs->assign( d, rhs->eval( d ) ); - break; - case oper::plus_assign: - lhs->assign( d, mhs->eval( d ) + rhs->eval( d ) ); - break; - case oper::minus_assign: - lhs->assign( d, mhs->eval( d ) - rhs->eval( d ) ); - break; - case oper::mult_assign: - lhs->assign( d, mhs->eval( d ) * rhs->eval( d ) ); - break; - case oper::div_assign: - lhs->assign( d, mhs->eval( d ) / rhs->eval( d ) ); - break; - case oper::mod_assign: - lhs->assign( d, std::fmod( mhs->eval( d ), rhs->eval( d ) ) ); - break; - case oper::increase: - lhs->assign( d, mhs->eval( d ) + 1 ); - break; - case oper::decrease: - lhs->assign( d, mhs->eval( d ) - 1 ); - break; - case oper::equal: - return static_cast( float_equals( lhs->eval( d ), rhs->eval( d ) ) ); - case oper::not_equal: - return static_cast( !float_equals( lhs->eval( d ), rhs->eval( d ) ) ); - case oper::less: - return lhs->eval( d ) < rhs->eval( d ); - case oper::equal_or_less: - return lhs->eval( d ) <= rhs->eval( d ); - case oper::greater: - return lhs->eval( d ) > rhs->eval( d ); - case oper::equal_or_greater: - return lhs->eval( d ) >= rhs->eval( d ); - case oper::invalid: - default: - debugmsg( "unknown eoc math operator %d %s", action, d.get_callstack() ); - } + return exp->eval( d ); +} - return 0; +double eoc_math::act( const_dialogue const &d ) const +{ + return exp->eval( d ); } static const diff --git a/src/dialogue.h b/src/dialogue.h index 42482c5906c2e..1b56b10db96df 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -280,7 +280,6 @@ struct dialogue: public const_dialogue { dialogue() = default; ~dialogue() = default; dialogue( const dialogue &d ); - explicit dialogue( const_dialogue const &d ); dialogue( dialogue && ) = default; dialogue &operator=( const dialogue & ); dialogue &operator=( dialogue && ) = default; diff --git a/src/dialogue_helpers.cpp b/src/dialogue_helpers.cpp index 95d04ed724c02..280f00002430b 100644 --- a/src/dialogue_helpers.cpp +++ b/src/dialogue_helpers.cpp @@ -172,8 +172,7 @@ double dbl_or_var_part::evaluate( const_dialogue const &d ) const return 0; } if( math_val ) { - dialogue loosey_goosey( d ); - return math_val->act( loosey_goosey ); + return math_val->act( d ); } debugmsg( "No valid value for dbl_or_var_part. %s", d.get_callstack() ); return 0; @@ -209,8 +208,7 @@ time_duration duration_or_var_part::evaluate( const_dialogue const &d ) const return 0_seconds; } if( math_val ) { - dialogue loosey_goosey( d ); - return time_duration::from_turns( math_val->act( loosey_goosey ) ); + return time_duration::from_turns( math_val->act( d ) ); } debugmsg( "No valid value for duration_or_var_part. %s", d.get_callstack() ); return 0_seconds; diff --git a/src/dialogue_helpers.h b/src/dialogue_helpers.h index 87ee6df1964e7..b3222225a52ef 100644 --- a/src/dialogue_helpers.h +++ b/src/dialogue_helpers.h @@ -15,6 +15,7 @@ #include "calendar.h" #include "debug.h" #include "global_vars.h" +#include "math_parser_type.h" #include "translation.h" class JsonArray; @@ -100,41 +101,11 @@ std::optional maybe_read_var_value( var_info process_variable( const std::string &type ); struct eoc_math { - enum class oper : int { - ret = 0, - assign, - - // these need mhs - plus_assign, - minus_assign, - mult_assign, - div_assign, - mod_assign, - increase, - decrease, - - equal, - not_equal, - less, - equal_or_less, - greater, - equal_or_greater, - - invalid, - }; - enum class type_t : int { - ret = 0, - compare, - assign, - }; - std::shared_ptr lhs; - std::shared_ptr mhs; - std::shared_ptr rhs; - eoc_math::oper action = oper::invalid; - - void from_json( const JsonObject &jo, std::string_view member, type_t type_ ); + std::shared_ptr exp; + + void from_json( const JsonObject &jo, std::string_view member, math_type_t type_ ); double act( dialogue &d ) const; - void _validate_type( JsonArray const &objects, type_t type_ ) const; + double act( const_dialogue const &d ) const; }; struct dbl_or_var_part { diff --git a/src/math_parser.cpp b/src/math_parser.cpp index e5d9f7f3df633..b9c68d4a1d5a0 100644 --- a/src/math_parser.cpp +++ b/src/math_parser.cpp @@ -301,7 +301,7 @@ class math_exp::math_exp_impl math_exp_impl() = default; explicit math_exp_impl( thingie &&t ): tree( t ) {} - bool parse( std::string_view str, bool assignment, bool handle_errors ) { + bool parse( std::string_view str, bool handle_errors ) { if( str.empty() ) { return false; } @@ -310,7 +310,6 @@ class math_exp::math_exp_impl std::locale::global( oldloc ); } ); try { - _force_assignment = assignment; _parse( str ); } catch( std::invalid_argument const &ex ) { if( handle_errors ) { @@ -329,21 +328,12 @@ class math_exp::math_exp_impl double eval( const_dialogue const &d ) const { return tree.eval( d ); } + double eval( dialogue &d ) const { + return tree.eval( d ); + } - void assign( dialogue &d, double val ) const { - std::visit( overloaded{ - [&d, val]( func_diag const & v ) { - v.assign( d, val ); - }, - [&d, val]( var const & v ) { - write_var_value( v.varinfo.type, v.varinfo.name, - &d, val ); - }, - []( auto &/* v */ ) { - debugmsg( "Assignment called on eval tree" ); - }, - }, - tree.data ); + math_type_t get_type() const { + return type; } private: @@ -373,7 +363,7 @@ class math_exp::math_exp_impl thingie tree{ 0.0 }; std::string_view last_token; parse_state state; - bool _force_assignment = false; // FXIME: TEMPORARY REMOVE + math_type_t type = math_type_t::ret; void _parse( std::string_view str ); void parse_string( std::string_view token, std::string_view full ); @@ -483,10 +473,7 @@ void math_exp::math_exp_impl::_parse( std::string_view str ) new_oper(); } - tree = _resolve_proto( output.top(), _force_assignment ); - if( _force_assignment && !is_assign_target( tree ) ) { - throw std::invalid_argument( "Assignment tree can only contain one variable name or dialogue assignment function" ); - } + tree = _resolve_proto( output.top() ); if( output.size() != 1 ) { throw std::invalid_argument( "Invalid expression. That's all we know. Blame andrei." ); @@ -826,6 +813,9 @@ void math_exp::math_exp_impl::new_oper() } else { _validate_operand( lhs, v->symbol ); _validate_operand( rhs, v->symbol ); + if( output.empty() && arity.empty() ) { + type = v->type; + } output.emplace( std::in_place_type_t(), lhs, rhs, v->f ); } }, @@ -862,6 +852,7 @@ void math_exp::math_exp_impl::new_oper() if( !output.empty() || !arity.empty() ) { throw std::invalid_argument( "misplaced assignment operator" ); } + type = math_type_t::assign; output.emplace( std::in_place_type_t(), lhs, mhs, rhs, v->f ); }, []( auto /* v */ ) @@ -940,10 +931,10 @@ math_exp::math_exp( math_exp_impl impl_ ) { } -bool math_exp::parse( std::string_view str, bool assignment, bool handle_errors ) +bool math_exp::parse( std::string_view str, bool handle_errors ) { impl = std::make_unique(); - return impl->parse( str, assignment, handle_errors ); + return impl->parse( str, handle_errors ); } math_exp::math_exp( math_exp const &other ) : @@ -965,7 +956,12 @@ double math_exp::eval( const_dialogue const &d ) const return impl->eval( d ); } -void math_exp::assign( dialogue &d, double val ) const +double math_exp::eval( dialogue &d ) const +{ + return impl->eval( d ); +} + +math_type_t math_exp::get_type() const { - return impl->assign( d, val ); + return impl->get_type(); } diff --git a/src/math_parser.h b/src/math_parser.h index 2365614acd159..4684b3788552e 100644 --- a/src/math_parser.h +++ b/src/math_parser.h @@ -5,6 +5,8 @@ #include #include +#include "math_parser_type.h" + struct dialogue; struct const_dialogue; @@ -21,9 +23,11 @@ class math_exp math_exp &operator=( math_exp &&/* other */ ) noexcept; explicit math_exp( math_exp_impl impl_ ); - bool parse( std::string_view str, bool assignment = false, bool handle_errors = true ); + bool parse( std::string_view str, bool handle_errors = true ); + double eval( dialogue &d ) const; double eval( const_dialogue const &d ) const; - void assign( dialogue &d, double val ) const; + + math_type_t get_type() const; private: std::unique_ptr impl; diff --git a/src/math_parser_impl.h b/src/math_parser_impl.h index cbe1b0c53a8d1..4a965f69194e3 100644 --- a/src/math_parser_impl.h +++ b/src/math_parser_impl.h @@ -16,6 +16,7 @@ #include "dialogue_helpers.h" #include "math_parser_diag.h" #include "math_parser_func.h" +#include "math_parser_type.h" #include "type_id.h" struct binary_op { @@ -28,6 +29,7 @@ struct binary_op { associativity assoc; using f_t = double ( * )( double, double ); f_t f = nullptr; + math_type_t type = math_type_t::ret; }; using pbin_op = binary_op const *; struct unary_op { @@ -333,11 +335,11 @@ constexpr std::array binary_ops{ binary_op{ "?", 0, binary_op::associativity::right }, binary_op{ ":", 0, binary_op::associativity::right }, binary_op{ "<", 1, binary_op::associativity::left, math_opers::lt }, - binary_op{ "<=", 1, binary_op::associativity::left, math_opers::lte }, - binary_op{ ">", 1, binary_op::associativity::left, math_opers::gt }, - binary_op{ ">=", 1, binary_op::associativity::left, math_opers::gte }, - binary_op{ "==", 1, binary_op::associativity::left, math_opers::eq }, - binary_op{ "!=", 1, binary_op::associativity::left, math_opers::neq }, + binary_op{ "<=", 1, binary_op::associativity::left, math_opers::lte, math_type_t::compare }, + binary_op{ ">", 1, binary_op::associativity::left, math_opers::gt, math_type_t::compare }, + binary_op{ ">=", 1, binary_op::associativity::left, math_opers::gte, math_type_t::compare }, + binary_op{ "==", 1, binary_op::associativity::left, math_opers::eq, math_type_t::compare }, + binary_op{ "!=", 1, binary_op::associativity::left, math_opers::neq, math_type_t::compare }, binary_op{ "+", 2, binary_op::associativity::left, math_opers::add }, binary_op{ "-", 2, binary_op::associativity::left, math_opers::sub }, binary_op{ "*", 3, binary_op::associativity::left, math_opers::mul }, diff --git a/src/math_parser_type.h b/src/math_parser_type.h new file mode 100644 index 0000000000000..49ee3aa68988d --- /dev/null +++ b/src/math_parser_type.h @@ -0,0 +1,11 @@ +#pragma once +#ifndef CATA_SRC_MATH_PARSER_TYPE_H +#define CATA_SRC_MATH_PARSER_TYPE_H + +enum class math_type_t : int { + ret = 0, + compare, + assign, +}; + +#endif // CATA_SRC_MATH_PARSER_TYPE_H diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 0a68de8b104e5..b78c93c6791e1 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -2667,10 +2667,6 @@ dialogue::dialogue( const dialogue &d ) : const_dialogue( d ) } } -dialogue::dialogue( const_dialogue const &d ) : const_dialogue( d ) -{ -} - const_dialogue::const_dialogue( std::unique_ptr alpha_in, std::unique_ptr beta_in, const std::unordered_map> &cond, @@ -5486,7 +5482,7 @@ talk_effect_fun_t::func f_math( const JsonObject &jo, std::string_view member, const std::string_view ) { eoc_math math; - math.from_json( jo, member, eoc_math::type_t::assign ); + math.from_json( jo, member, math_type_t::assign ); return [math = std::move( math )]( dialogue & d ) { return math.act( d ); };