From 681c85b12282ce9436754c7561a38edf24f70bd9 Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Sun, 17 Jan 2016 23:31:38 -0500 Subject: [PATCH] Revert to older calc parser code for now --- indra/llmath/CMakeLists.txt | 1 + indra/llmath/llcalc.cpp | 148 ++++++++------- indra/llmath/llcalc.h | 1 + indra/llmath/llcalcparser.cpp | 63 +++++++ indra/llmath/llcalcparser.h | 342 ++++++++++++++-------------------- 5 files changed, 287 insertions(+), 268 deletions(-) create mode 100644 indra/llmath/llcalcparser.cpp diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 96986b79f..ff174112a 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -13,6 +13,7 @@ set(llmath_SOURCE_FILES llbbox.cpp llbboxlocal.cpp llcalc.cpp + llcalcparser.cpp llcamera.cpp llcoordframe.cpp llline.cpp diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index 46330a767..edc6986cc 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -28,61 +28,59 @@ #include "llcalc.h" +#include "llcalcparser.h" #include "llmath.h" -#include "llcalcparser.h" // Variable names for use in the build floater -// must be lower case for parser definition -// case-insensitive for actual parsing -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"; +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; -//TODO: Make this a static global class LLCalc::LLCalc() : mLastErrorPos(0) { // Init table of constants - /*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);*/ + 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; } LLCalc::~LLCalc() @@ -99,7 +97,7 @@ void LLCalc::cleanUp() //static LLCalc* LLCalc::getInstance() { - if (!sInstance) sInstance = new LLCalc(); + if (!sInstance) sInstance = new LLCalc(); return sInstance; } @@ -118,35 +116,47 @@ void LLCalc::clearAllVariables() mVariables.clear(); } +/* +void LLCalc::updateVariables(LLSD& vars) +{ + LLSD::map_iterator cIt = vars.beginMap(); + for(; cIt != vars.endMap(); cIt++) + { + setVar(cIt->first, (F32)(LLSD::Real)cIt->second); + } +} +*/ + bool LLCalc::evalString(const std::string& expression, F32& result) { - mLastErrorPos = 0; - std::string::const_iterator itr = expression.begin(); - expression::grammar 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) - { - calc.constant.add(iter->first, iter->second); - } + std::string expr_upper = expression; + LLStringUtil::toUpper(expr_upper); + + LLCalcParser calc(result, &mConstants, &mVariables); - if (!expression::parse(itr, expression.end(), calc, result) || itr != expression.end()) + mLastErrorPos = 0; + std::string::iterator start = expr_upper.begin(); + parse_info info; + + try { - mLastErrorPos = itr - expression.begin(); + info = parse(start, expr_upper.end(), calc, space_p); + LL_DEBUGS() << "Math expression: " << expression << " = " << result << LL_ENDL; + } + catch(parser_error &e) + { + mLastErrorPos = e.where - expr_upper.begin(); + + LL_INFOS() << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; + return false; + } + + if (!info.full) + { + mLastErrorPos = info.stop - expr_upper.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 8c71569f4..7f4c6e317 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -86,6 +86,7 @@ public: private: std::string::size_type mLastErrorPos; + calc_map_t mConstants; calc_map_t mVariables; // "There shall be only one" diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp new file mode 100644 index 000000000..b4ca32065 --- /dev/null +++ b/indra/llmath/llcalcparser.cpp @@ -0,0 +1,63 @@ +/* + * 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 0fbf358a1..faa699ff7 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -27,218 +27,162 @@ #ifndef LL_CALCPARSER_H #define LL_CALCPARSER_H -#include -#if !defined(SPIRIT_VERSION) || SPIRIT_VERSION < 0x2010 -#error "At least Spirit version 2.1 required" -#endif +#include +#include +#include +#include +#include +#include +using namespace boost::spirit::classic; -// Add this in if we want boost math constants. -#include +#include "llcalc.h" +#include "llmath.h" -#if defined(LL_WINDOWS) -#pragma warning(push) -// warning C4348: 'boost::spirit::terminal<...>::result_helper': redefinition of default parameter: parameter 3, 4 -#pragma warning(disable: 4348) -#endif - -//#include -#include -#include - -#if defined(LL_WINDOWS) -#pragma warning(pop) -#endif -namespace expression { - - -//TODO: If we can find a better way to do this with boost::pheonix::bind lets do it -//namespace { // anonymous - -template -T min_glue(T a, T b) +struct LLCalcParser : grammar { - return std::min(a, b); -} - -template -T max_glue(T a, T b) -{ - return std::max(a, b); -} - -struct lazy_pow_ -{ - template - struct result { typedef X type; }; - - template - X operator()(X x, Y y) const + LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : + mResult(result), mConstants(constants), mVariables(vars) {}; + + struct value_closure : closure { - return std::pow(x, y); - } -}; - -struct lazy_ufunc_ -{ - template - struct result { typedef A1 type; }; - - template - A1 operator()(F f, A1 a1) const + member1 value; + }; + + template + struct definition { - return f(a1); - } -}; - -struct lazy_bfunc_ -{ - template - struct result { typedef A1 type; }; - - template - A1 operator()(F f, A1 a1, A2 a2) const - { - return f(a1, a2); - } -}; - -//} // end namespace anonymous - -template -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::value_type, - FPT - > - { - constant_() + // Rule declarations + rule statement, identifier; + rule expression, term, + power, + unary_expr, + factor, + unary_func, + binary_func, + group; + + // start() should return the starting symbol + rule const& start() const { return statement; } + + definition(LLCalcParser const& self) { - } - } constant; - - // symbol table for unary functions like "abs" - struct ufunc_ - : boost::spirit::qi::symbols< - typename std::iterator_traits::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 ) + using namespace phoenix; + + assertion assert_domain("Domain error"); +// assertion assert_symbol("Unknown symbol"); + assertion assert_syntax("Syntax error"); + + identifier = + lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] ; - } - } ufunc; - - // symbol table for binary functions like "pow" - struct bfunc_ - : boost::spirit::qi::symbols< - typename std::iterator_traits::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) + + group = + '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) ; - } - } 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 > 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; - boost::phoenix::function lazy_ufunc; - boost::phoenix::function lazy_bfunc; - - expression = - term [_val = _1] - >> *( ('+' >> term [_val += _1]) - | ('-' >> term [_val -= _1]) + + 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))) + ; + + unary_expr = + !ch_p('+') >> factor[unary_expr.value = arg1] | + '-' >> factor[unary_expr.value = -arg1] + ; + + power = + unary_expr[power.value = arg1] >> + *('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)])) + ; + + 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 -bool parse(Iterator &iter, - Iterator end, - const grammar &g, - FPT &result) -{ - return boost::spirit::qi::phrase_parse( - iter, end, g, boost::spirit::ascii::space, result); -} - -} // end namespace expression #endif // LL_CALCPARSER_H