From e30d50600327458e7b510ad2ae3d9649265f1a08 Mon Sep 17 00:00:00 2001 From: Rye Mutt <rye@alchemyviewer.org> Date: Sat, 8 Aug 2020 17:26:52 -0400 Subject: [PATCH] Replace LLCalc code with alchemy version --- indra/llmath/llcalc.cpp | 133 +++++++------- indra/llmath/llcalc.h | 9 +- indra/llmath/llcalcparser.cpp | 63 ------- indra/llmath/llcalcparser.h | 328 +++++++++++++++++++--------------- 4 files changed, 255 insertions(+), 278 deletions(-) diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index edc6986cc91..85b57d21eba 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -28,70 +28,67 @@ #include "llcalc.h" -#include "llcalcparser.h" #include "llmath.h" +#include "llcalcparser.h" // Variable names for use in the build floater -const char* LLCalc::X_POS = "PX"; -const char* LLCalc::Y_POS = "PY"; -const char* LLCalc::Z_POS = "PZ"; -const char* LLCalc::X_SCALE = "SX"; -const char* LLCalc::Y_SCALE = "SY"; -const char* LLCalc::Z_SCALE = "SZ"; -const char* LLCalc::X_ROT = "RX"; -const char* LLCalc::Y_ROT = "RY"; -const char* LLCalc::Z_ROT = "RZ"; -const char* LLCalc::HOLLOW = "HLW"; -const char* LLCalc::CUT_BEGIN = "CB"; -const char* LLCalc::CUT_END = "CE"; -const char* LLCalc::PATH_BEGIN = "PB"; -const char* LLCalc::PATH_END = "PE"; -const char* LLCalc::TWIST_BEGIN = "TB"; -const char* LLCalc::TWIST_END = "TE"; -const char* LLCalc::X_SHEAR = "SHX"; -const char* LLCalc::Y_SHEAR = "SHY"; -const char* LLCalc::X_TAPER = "TPX"; -const char* LLCalc::Y_TAPER = "TPY"; -const char* LLCalc::RADIUS_OFFSET = "ROF"; -const char* LLCalc::REVOLUTIONS = "REV"; -const char* LLCalc::SKEW = "SKW"; -const char* LLCalc::X_HOLE = "HLX"; -const char* LLCalc::Y_HOLE = "HLY"; -const char* LLCalc::TEX_U_SCALE = "TSU"; -const char* LLCalc::TEX_V_SCALE = "TSV"; -const char* LLCalc::TEX_U_OFFSET = "TOU"; -const char* LLCalc::TEX_V_OFFSET = "TOV"; -const char* LLCalc::TEX_ROTATION = "TROT"; -const char* LLCalc::TEX_TRANSPARENCY = "TRNS"; -const char* LLCalc::TEX_GLOW = "GLOW"; - - -LLCalc* LLCalc::sInstance = NULL; +const char* LLCalc::X_POS = "px"; +const char* LLCalc::Y_POS = "py"; +const char* LLCalc::Z_POS = "pz"; +const char* LLCalc::X_SCALE = "sx"; +const char* LLCalc::Y_SCALE = "sy"; +const char* LLCalc::Z_SCALE = "sz"; +const char* LLCalc::X_ROT = "rx"; +const char* LLCalc::Y_ROT = "ry"; +const char* LLCalc::Z_ROT = "rz"; +const char* LLCalc::HOLLOW = "hlw"; +const char* LLCalc::CUT_BEGIN = "cb"; +const char* LLCalc::CUT_END = "ce"; +const char* LLCalc::PATH_BEGIN = "pb"; +const char* LLCalc::PATH_END = "pe"; +const char* LLCalc::TWIST_BEGIN = "tb"; +const char* LLCalc::TWIST_END = "te"; +const char* LLCalc::X_SHEAR = "shx"; +const char* LLCalc::Y_SHEAR = "shy"; +const char* LLCalc::X_TAPER = "tpx"; +const char* LLCalc::Y_TAPER = "tpy"; +const char* LLCalc::RADIUS_OFFSET = "rof"; +const char* LLCalc::REVOLUTIONS = "rev"; +const char* LLCalc::SKEW = "skw"; +const char* LLCalc::X_HOLE = "hlx"; +const char* LLCalc::Y_HOLE = "hly"; +const char* LLCalc::TEX_U_SCALE = "tsu"; +const char* LLCalc::TEX_V_SCALE = "tsv"; +const char* LLCalc::TEX_U_OFFSET = "tou"; +const char* LLCalc::TEX_V_OFFSET = "tov"; +const char* LLCalc::TEX_ROTATION = "trot"; +const char* LLCalc::TEX_TRANSPARENCY = "trns"; +const char* LLCalc::TEX_GLOW = "glow"; + + +LLCalc* LLCalc::sInstance = nullptr; LLCalc::LLCalc() : mLastErrorPos(0) { // Init table of constants - mConstants["PI"] = F_PI; - mConstants["TWO_PI"] = F_TWO_PI; - mConstants["PI_BY_TWO"] = F_PI_BY_TWO; - mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI; - mConstants["SQRT2"] = F_SQRT2; - mConstants["SQRT3"] = F_SQRT3; - mConstants["DEG_TO_RAD"] = DEG_TO_RAD; - mConstants["RAD_TO_DEG"] = RAD_TO_DEG; - mConstants["GRAVITY"] = GRAVITY; + /*setVar("PI", F_PI); + setVar("TWO_PI", F_TWO_PI); + setVar("PI_BY_TWO", F_PI_BY_TWO); + setVar("SQRT_TWO_PI", F_SQRT_TWO_PI); + setVar("SQRT2", F_SQRT2); + setVar("SQRT3", F_SQRT3); + setVar("DEG_TO_RAD", DEG_TO_RAD); + setVar("RAD_TO_DEG", RAD_TO_DEG); + setVar("GRAVITY", GRAVITY);*/ } -LLCalc::~LLCalc() -{ -} //static void LLCalc::cleanUp() { delete sInstance; - sInstance = NULL; + sInstance = nullptr; } //static @@ -129,34 +126,36 @@ void LLCalc::updateVariables(LLSD& vars) bool LLCalc::evalString(const std::string& expression, F32& result) { - std::string expr_upper = expression; - LLStringUtil::toUpper(expr_upper); - LLCalcParser calc(result, &mConstants, &mVariables); mLastErrorPos = 0; - std::string::iterator start = expr_upper.begin(); - parse_info<std::string::iterator> info; - - try + std::string::const_iterator itr = expression.begin(); + expression::grammar<F32,std::string::const_iterator> calc; + calc.constant.add + ("pi", F_PI) + ("two_pi", F_TWO_PI) + ("pi_by_two", F_PI_BY_TWO) + ("sqrt_two_pi", F_SQRT_TWO_PI) + ("sqrt2", F_SQRT2) + ("sqrt3", F_SQRT3) + ("deg_to_rad", DEG_TO_RAD) + ("rad_to_deg", RAD_TO_DEG) + ("gravity", GRAVITY) + ; + for(calc_map_t::const_iterator iter = mVariables.begin(); + iter != mVariables.end(); + ++iter) { - info = parse(start, expr_upper.end(), calc, space_p); - LL_DEBUGS() << "Math expression: " << expression << " = " << result << LL_ENDL; - } - catch(parser_error<std::string, std::string::iterator> &e) - { - mLastErrorPos = e.where - expr_upper.begin(); + calc.constant.add(iter->first, iter->second); - LL_INFOS() << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; - return false; } - if (!info.full) + if (!expression::parse<F32,std::string::const_iterator>(itr, expression.end(), calc, result) || itr != expression.end()) { - mLastErrorPos = info.stop - expr_upper.begin(); + mLastErrorPos = itr - expression.begin(); LL_INFOS() << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; return false; } - + LL_DEBUGS() << "Math expression: " << expression << " = " << result << LL_ENDL; return true; } diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index ceb9dce585d..17782fb88bf 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -34,7 +34,7 @@ class LLCalc { public: LLCalc(); - ~LLCalc(); + ~LLCalc() = default; // Variable name constants static const char* X_POS; @@ -86,13 +86,8 @@ class LLCalc private: std::string::size_type mLastErrorPos; - calc_map_t mConstants; calc_map_t mVariables; - - // *TODO: Add support for storing user defined variables, and stored functions. - // Will need UI work, and a means to save them between sessions. -// calc_map_t mUserVariables; - + // "There shall be only one" static LLCalc* sInstance; }; diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp index b4ca3206594..e69de29bb2d 100644 --- a/indra/llmath/llcalcparser.cpp +++ b/indra/llmath/llcalcparser.cpp @@ -1,63 +0,0 @@ -/* - * LLCalcParser.cpp - * Copyright 2008 Aimee Walton. - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2008, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * - */ - -#include "linden_common.h" - -#include "llcalcparser.h" -using namespace boost::spirit::classic; - -F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const -{ - LLCalc::calc_map_t::iterator iter; - - std::string name(start, end); - - if (mConstants) - { - iter = mConstants->find(name); - if (iter != mConstants->end()) - { - return (*iter).second; - } - } - else - { - // This should never happen! - throw_(end, std::string("Missing constants table")); - } - - if (mVariables) - { - iter = mVariables->find(name); - if (iter != mVariables->end()) - { - return (*iter).second; - } - } - - throw_(end, std::string("Unknown symbol " + name)); - return 0.f; -} diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index e2388d6702d..bc1b2b150da 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -27,162 +27,208 @@ #ifndef LL_CALCPARSER_H #define LL_CALCPARSER_H -#include <boost/spirit/include/classic_attribute.hpp> -#include <boost/spirit/include/classic_core.hpp> -#include <boost/spirit/include/classic_error_handling.hpp> -#include <boost/spirit/include/classic_position_iterator.hpp> -#include <boost/spirit/include/phoenix1_binders.hpp> -#include <boost/spirit/include/classic_symbols.hpp> -using namespace boost::spirit::classic; +#include <boost/spirit/version.hpp> +#if !defined(SPIRIT_VERSION) || SPIRIT_VERSION < 0x2010 +#error "At least Spirit version 2.1 required" +#endif -#include "llcalc.h" -#include "llmath.h" +// Add this in if we want boost math constants. +#include <boost/bind.hpp> +//#include <boost/math/constants/constants.hpp> +#include <boost/spirit/include/phoenix.hpp> +#include <boost/spirit/include/qi.hpp> -struct LLCalcParser : grammar<LLCalcParser> +namespace expression { + + +//TODO: If we can find a better way to do this with boost::pheonix::bind lets do it +//namespace { // anonymous + +template <typename T> +T min_glue(T a, T b) +{ + return std::min(a, b); +} + +template <typename T> +T max_glue(T a, T b) { - LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : - mResult(result), mConstants(constants), mVariables(vars) {}; - - struct value_closure : closure<value_closure, F32> + return std::max(a, b); +} + +struct lazy_pow_ +{ + template <typename X, typename Y> + struct result { typedef X type; }; + + template <typename X, typename Y> + X operator()(X x, Y y) const { - member1 value; - }; - - template <typename ScannerT> - struct definition + return std::pow(x, y); + } +}; + +struct lazy_ufunc_ +{ + template <typename F, typename A1> + struct result { typedef A1 type; }; + + template <typename F, typename A1> + A1 operator()(F f, A1 a1) const { - // Rule declarations - rule<ScannerT> statement, identifier; - rule<ScannerT, value_closure::context_t> expression, term, - power, - unary_expr, - factor, - unary_func, - binary_func, - group; - - // start() should return the starting symbol - rule<ScannerT> const& start() const { return statement; } - - definition(LLCalcParser const& self) + return f(a1); + } +}; + +struct lazy_bfunc_ +{ + template <typename F, typename A1, typename A2> + struct result { typedef A1 type; }; + + template <typename F, typename A1, typename A2> + A1 operator()(F f, A1 a1, A2 a2) const + { + return f(a1, a2); + } +}; + +//} // end namespace anonymous + +template <typename FPT, typename Iterator> +struct grammar + : boost::spirit::qi::grammar< + Iterator, FPT(), boost::spirit::ascii::space_type + > +{ + + // symbol table for constants + // to be added by the actual calculator + struct constant_ + : boost::spirit::qi::symbols< + typename std::iterator_traits<Iterator>::value_type, + FPT + > + { + constant_() { - using namespace phoenix; - - assertion<std::string> assert_domain("Domain error"); -// assertion<std::string> assert_symbol("Unknown symbol"); - assertion<std::string> assert_syntax("Syntax error"); - - identifier = - lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] - ; - - group = - '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) - ; - - unary_func = - ((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) | - (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) | - (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) | - (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) | - (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) | - (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) | - (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) | - (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) | - (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) | - (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) | - (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) | - (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)]) - ) >> assert_syntax(ch_p(')')) - ; - - binary_func = - ((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) | - (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) | - (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)]) - ) >> assert_syntax(ch_p(')')) - ; - - // *TODO: Localisation of the decimal point? - // Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate - // for the current locale. However to do that here could clash with using - // the comma as a separator when passing arguments to functions. - factor = - (ureal_p[factor.value = arg1] | - group[factor.value = arg1] | - unary_func[factor.value = arg1] | - binary_func[factor.value = arg1] | - // Lookup throws an Unknown Symbol error if it is unknown, while this works fine, - // would be "neater" to handle symbol lookup from here with an assertive parser. -// constants_p[factor.value = arg1]| - identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)] - ) >> - // Detect and throw math errors. - assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value))) + } + } constant; + + // symbol table for unary functions like "abs" + struct ufunc_ + : boost::spirit::qi::symbols< + typename std::iterator_traits<Iterator>::value_type, + FPT (*)(FPT) + > + { + ufunc_() + { + this->add + ("abs" , (FPT (*)(FPT)) std::abs ) + ("acos" , (FPT (*)(FPT)) std::acos ) + ("asin" , (FPT (*)(FPT)) std::asin ) + ("atan" , (FPT (*)(FPT)) std::atan ) + ("ceil" , (FPT (*)(FPT)) std::ceil ) + ("cos" , (FPT (*)(FPT)) std::cos ) + ("cosh" , (FPT (*)(FPT)) std::cosh ) + ("exp" , (FPT (*)(FPT)) std::exp ) + ("floor" , (FPT (*)(FPT)) std::floor) + ("log" , (FPT (*)(FPT)) std::log ) + ("log10" , (FPT (*)(FPT)) std::log10) + ("sin" , (FPT (*)(FPT)) std::sin ) + ("sinh" , (FPT (*)(FPT)) std::sinh ) + ("sqrt" , (FPT (*)(FPT)) std::sqrt ) + ("tan" , (FPT (*)(FPT)) std::tan ) + ("tanh" , (FPT (*)(FPT)) std::tanh ) ; + } + } ufunc; - unary_expr = - !ch_p('+') >> factor[unary_expr.value = arg1] | - '-' >> factor[unary_expr.value = -arg1] + // symbol table for binary functions like "pow" + struct bfunc_ + : boost::spirit::qi::symbols< + typename std::iterator_traits<Iterator>::value_type, + FPT (*)(FPT, FPT) + > + { + bfunc_() + { + using boost::bind; + this->add + ("pow" , (FPT (*)(FPT, FPT)) std::pow ) + ("atan2", (FPT (*)(FPT, FPT)) std::atan2) + ("min" , (FPT (*)(FPT, FPT)) min_glue) + ("max" , (FPT (*)(FPT, FPT)) max_glue) ; - - power = - unary_expr[power.value = arg1] >> - *('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)])) + } + } bfunc; + + boost::spirit::qi::rule< + Iterator, FPT(), boost::spirit::ascii::space_type + > expression, term, factor, primary; + + grammar() : grammar::base_type(expression) + { + using boost::spirit::qi::real_parser; + using boost::spirit::qi::real_policies; + real_parser<FPT,real_policies<FPT> > real; + + using boost::spirit::qi::_1; + using boost::spirit::qi::_2; + using boost::spirit::qi::_3; + using boost::spirit::qi::no_case; + using boost::spirit::qi::_val; + + boost::phoenix::function<lazy_pow_> lazy_pow; + boost::phoenix::function<lazy_ufunc_> lazy_ufunc; + boost::phoenix::function<lazy_bfunc_> lazy_bfunc; + + expression = + term [_val = _1] + >> *( ('+' >> term [_val += _1]) + | ('-' >> term [_val -= _1]) + ) ; - - term = - power[term.value = arg1] >> - *(('*' >> assert_syntax(power[term.value *= arg1])) | - ('/' >> assert_syntax(power[term.value /= arg1])) | - ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)])) + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) ) ; - - expression = - assert_syntax(term[expression.value = arg1]) >> - *(('+' >> assert_syntax(term[expression.value += arg1])) | - ('-' >> assert_syntax(term[expression.value -= arg1])) + + factor = + primary [_val = _1] + >> *( ("**" >> factor [_val = lazy_pow(_val, _1)]) ) ; - - statement = - !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) + + primary = + real [_val = _1] + | '(' >> expression [_val = _1] >> ')' + | ('-' >> primary [_val = -_1]) + | ('+' >> primary [_val = _1]) + | (no_case[ufunc] >> '(' >> expression >> ')') + [_val = lazy_ufunc(_1, _2)] + | (no_case[bfunc] >> '(' >> expression >> ',' + >> expression >> ')') + [_val = lazy_bfunc(_1, _2, _3)] + | no_case[constant] [_val = _1] ; - } - }; - -private: - // Member functions for semantic actions - F32 lookup(const std::string::iterator&, const std::string::iterator&) const; - F32 _min(const F32& a, const F32& b) const { return llmin(a, b); } - F32 _max(const F32& a, const F32& b) const { return llmax(a, b); } - - bool checkNaN(const F32& a) const { return !llisnan(a); } - - //FIX* non ambiguous function fix making SIN() work for calc -Cryogenic Blitz - F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); } - F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); } - F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); } - F32 _asin(const F32& a) const { return asin(a) * RAD_TO_DEG; } - F32 _acos(const F32& a) const { return acos(a) * RAD_TO_DEG; } - F32 _atan(const F32& a) const { return atan(a) * RAD_TO_DEG; } - F32 _sqrt(const F32& a) const { return sqrt(a); } - F32 _log(const F32& a) const { return log(a); } - F32 _exp(const F32& a) const { return exp(a); } - F32 _fabs(const F32& a) const { return fabs(a); } - F32 _floor(const F32& a) const { return (F32)llfloor(a); } - F32 _ceil(const F32& a) const { return llceil(a); } - F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } - - LLCalc::calc_map_t* mConstants; - LLCalc::calc_map_t* mVariables; -// LLCalc::calc_map_t* mUserVariables; - - F32& mResult; + + } }; + +template <typename FPT, typename Iterator> +bool parse(Iterator &iter, + Iterator end, + const grammar<FPT,Iterator> &g, + FPT &result) +{ + return boost::spirit::qi::phrase_parse( + iter, end, g, boost::spirit::ascii::space, result); +} + +} // end namespace expression #endif // LL_CALCPARSER_H -- GitLab