diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 584abdf89..c168f7a89 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -12,8 +12,7 @@ include_directories( set(llmath_SOURCE_FILES llbbox.cpp llbboxlocal.cpp - llcalc.cpp - llcalcparser.cpp + llcalc.cpp llcamera.cpp llcoordframe.cpp llline.cpp @@ -48,8 +47,8 @@ set(llmath_HEADER_FILES coordframe.h llbbox.h llbboxlocal.h - llcalc.h - llcalcparser.h + llcalc.h + llcalcparser.h llcamera.h llcoord.h llcoordframe.h diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index 597d0815f..331e363e7 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -1,9 +1,26 @@ /* * LLCalc.cpp - * SecondLife - * - * Created by Aimee Walton on 28/09/2008. - * Copyright 2008 Aimee Walton. + * 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$ * */ @@ -11,59 +28,61 @@ #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"; - +// 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"; LLCalc* LLCalc::sInstance = NULL; +//TODO: Make this a static global class 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() @@ -80,7 +99,7 @@ void LLCalc::cleanUp() //static LLCalc* LLCalc::getInstance() { - if (!sInstance) sInstance = new LLCalc(); + if (!sInstance) sInstance = new LLCalc(); return sInstance; } @@ -99,47 +118,35 @@ 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) { - 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 info; - - try + 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) { - info = parse(start, expr_upper.end(), calc, space_p); - lldebugs << "Math expression: " << expression << " = " << result << llendl; + calc.constant.add(iter->first, iter->second); } - catch(parser_error &e) + + if (!expression::parse(itr, expression.end(), calc, result) || itr != expression.end()) { - mLastErrorPos = e.where - expr_upper.begin(); - - llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl; - return false; - } - - if (!info.full) - { - mLastErrorPos = info.stop - expr_upper.begin(); + mLastErrorPos = itr - expression.begin(); llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl; return false; } - + lldebugs << "Math expression: " << expression << " = " << result << llendl; return true; } diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index 886e0bc8f..8c71569f4 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -1,9 +1,26 @@ /* * LLCalc.h - * SecondLife - * - * Created by Aimee Walton on 28/09/2008. * 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$ * */ @@ -69,13 +86,8 @@ public: 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.h b/indra/llmath/llcalcparser.h index e0ad27026..0fcd38064 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -27,165 +27,207 @@ #ifndef LL_CALCPARSER_H #define LL_CALCPARSER_H -#include -#include -#include -#include -#include -#include -using namespace boost::spirit::classic; +#include +#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 +#include +#include -struct LLCalcParser : grammar +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) { - LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : - mResult(result), mConstants(constants), mVariables(vars) {}; - - struct value_closure : closure - { - member1 value; - }; - - template - struct definition - { - // Rule declarations - rule statement, identifier; - rule expression, term, - power, - unary_expr, - factor, - unary_func, - binary_func, - group; + return std::min(a, b); +} - // start() should return the starting symbol - rule const& start() const { return statement; } - - definition(LLCalcParser const& self) +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 + { + return std::pow(x, y); + } +}; + +struct lazy_ufunc_ +{ + template + struct result { typedef A1 type; }; + + template + A1 operator()(F f, A1 a1) const + { + 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_() { - 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 | '_')] - ; - - 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))) - ; - - 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)])) - ) - ; - - expression = - assert_syntax(term[expression.value = arg1]) >> - *(('+' >> assert_syntax(term[expression.value += arg1])) | - ('-' >> assert_syntax(term[expression.value -= arg1])) - ) - ; - - statement = - !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) + } + } 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 ) ; } - }; - -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 ambigious 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); } + } ufunc; - 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; + // 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) + ; + } + } 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]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) + ) + ; + + factor = + primary [_val = _1] + >> *( ("**" >> factor [_val = lazy_pow(_val, _1)]) + ) + ; + + 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] + ; + + } }; + +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