diff --git a/lib_math.cc b/lib_math.cc index a508e673..6fd97b25 100644 --- a/lib_math.cc +++ b/lib_math.cc @@ -1,82 +1,161 @@ -#include "cubescript.hh" - +#include #include +#include + +#include "cubescript.hh" + namespace cscript { static constexpr CsFloat PI = 3.14159265358979f; static constexpr CsFloat RAD = PI / 180.0f; +template +struct CsMathVal; + +template<> +struct CsMathVal { + static CsInt get(TaggedValue &tv) { + return tv.get_int(); + } + static void set(TaggedValue &res, CsInt val) { + res.set_int(val); + } +}; + +template<> +struct CsMathVal { + static CsFloat get(TaggedValue &tv) { + return tv.get_float(); + } + static void set(TaggedValue &res, CsFloat val) { + res.set_float(val); + } +}; + +template +struct CsMathNoop { + T operator()(T arg) { + return arg; + } +}; + +template +static inline void cs_mathop( + TvalRange args, TaggedValue &res, T initval, + F1 binop, F2 unop +) { + T val; + if (args.size() >= 2) { + val = binop(CsMathVal::get(args[0]), CsMathVal::get(args[1])); + for (ostd::Size i = 2; i < args.size(); ++i) { + val = binop(val, CsMathVal::get(args[i])); + } + } else { + val = unop(!args.empty() ? CsMathVal::get(args[0]) : initval); + } + CsMathVal::set(res, val); +} + +template +static inline void cs_cmpop(TvalRange args, TaggedValue &res, F cmp) { + bool val; + if (args.size() >= 2) { + val = cmp(CsMathVal::get(args[0]), CsMathVal::get(args[1])); + for (ostd::Size i = 2; (i < args.size()) && val; ++i) { + val = cmp(val, CsMathVal::get(args[i])); + } + } else { + val = cmp(!args.empty() ? CsMathVal::get(args[0]) : T(0), T(0)); + } + res.set_int(CsInt(val)); +} + void cs_init_lib_math(CsState &cs) { - cs.add_command("sin", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("sin", "f", [](TvalRange args, TaggedValue &res) { res.set_float(sin(args[0].get_float() * RAD)); }); - cs.add_command("cos", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("cos", "f", [](TvalRange args, TaggedValue &res) { res.set_float(cos(args[0].get_float() * RAD)); }); - cs.add_command("tan", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("tan", "f", [](TvalRange args, TaggedValue &res) { res.set_float(tan(args[0].get_float() * RAD)); }); - cs.add_command("asin", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("asin", "f", [](TvalRange args, TaggedValue &res) { res.set_float(asin(args[0].get_float()) / RAD); }); - cs.add_command("acos", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("acos", "f", [](TvalRange args, TaggedValue &res) { res.set_float(acos(args[0].get_float()) / RAD); }); - cs.add_command("atan", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("atan", "f", [](TvalRange args, TaggedValue &res) { res.set_float(atan(args[0].get_float()) / RAD); }); - cs.add_command("atan2", "ff", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("atan2", "ff", [](TvalRange args, TaggedValue &res) { res.set_float(atan2(args[0].get_float(), args[1].get_float()) / RAD); }); - cs.add_command("sqrt", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("sqrt", "f", [](TvalRange args, TaggedValue &res) { res.set_float(sqrt(args[0].get_float())); }); - cs.add_command("loge", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("loge", "f", [](TvalRange args, TaggedValue &res) { res.set_float(log(args[0].get_float())); }); - cs.add_command("log2", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("log2", "f", [](TvalRange args, TaggedValue &res) { res.set_float(log(args[0].get_float()) / M_LN2); }); - cs.add_command("log10", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("log10", "f", [](TvalRange args, TaggedValue &res) { res.set_float(log10(args[0].get_float())); }); - cs.add_command("exp", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("exp", "f", [](TvalRange args, TaggedValue &res) { res.set_float(exp(args[0].get_float())); }); -#define CS_CMD_MIN_MAX(name, fmt, type, op) \ - cs.add_command(#name, #fmt "1V", [&cs](TvalRange args, TaggedValue &res) { \ - auto v = !args.empty() ? args[0].fmt : 0; \ - for (ostd::Size i = 1; i < args.size(); ++i) v = op(v, args[i].fmt); \ - res.set_##type(v); \ - }) + cs.add_command("min", "i1V", [](TvalRange args, TaggedValue &res) { + CsInt v = (!args.empty() ? args[0].get_int() : 0); + for (ostd::Size i = 1; i < args.size(); ++i) { + v = ostd::min(v, args[i].get_int()); + } + res.set_int(v); + }); + cs.add_command("max", "i1V", [](TvalRange args, TaggedValue &res) { + CsInt v = (!args.empty() ? args[0].get_int() : 0); + for (ostd::Size i = 1; i < args.size(); ++i) { + v = ostd::max(v, args[i].get_int()); + } + res.set_int(v); + }); + cs.add_command("minf", "f1V", [](TvalRange args, TaggedValue &res) { + CsFloat v = (!args.empty() ? args[0].get_float() : 0); + for (ostd::Size i = 1; i < args.size(); ++i) { + v = ostd::min(v, args[i].get_float()); + } + res.set_float(v); + }); + cs.add_command("maxf", "f1V", [](TvalRange args, TaggedValue &res) { + CsFloat v = (!args.empty() ? args[0].get_float() : 0); + for (ostd::Size i = 1; i < args.size(); ++i) { + v = ostd::max(v, args[i].get_float()); + } + res.set_float(v); + }); - CS_CMD_MIN_MAX(min, i, int, ostd::min); - CS_CMD_MIN_MAX(max, i, int, ostd::max); - CS_CMD_MIN_MAX(minf, f, float, ostd::min); - CS_CMD_MIN_MAX(maxf, f, float, ostd::max); - -#undef CS_CMD_MIN_MAX - - cs.add_command("abs", "i", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("abs", "i", [](TvalRange args, TaggedValue &res) { res.set_int(abs(args[0].get_int())); }); - cs.add_command("absf", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("absf", "f", [](TvalRange args, TaggedValue &res) { res.set_float(fabs(args[0].get_float())); }); - cs.add_command("floor", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("floor", "f", [](TvalRange args, TaggedValue &res) { res.set_float(floor(args[0].get_float())); }); - cs.add_command("ceil", "f", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("ceil", "f", [](TvalRange args, TaggedValue &res) { res.set_float(ceil(args[0].get_float())); }); - cs.add_command("round", "ff", [&cs](TvalRange args, TaggedValue &res) { + cs.add_command("round", "ff", [](TvalRange args, TaggedValue &res) { double step = args[1].get_float(); double r = args[0].get_float(); if (step > 0) { @@ -88,111 +167,200 @@ void cs_init_lib_math(CsState &cs) { res.set_float(CsFloat(r)); }); -#define CS_CMD_MATH(name, fmt, vtype, type, op, initval, unaryop) \ - cs.add_command(name, #fmt "1V", [&cs](TvalRange args, TaggedValue &res) { \ - type val; \ - if (args.size() >= 2) { \ - val = args[0].fmt; \ - type val2 = args[1].fmt; \ - op; \ - for (ostd::Size i = 2; i < args.size(); ++i) { \ - val2 = args[i].fmt; \ - op; \ - } \ - } else { \ - val = (args.size() > 0) ? args[0].fmt : initval; \ - unaryop; \ - } \ - res.set_##type(val); \ + cs.add_command("+", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop(args, res, 0, ostd::Add(), CsMathNoop()); + }); + cs.add_command("*", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 1, ostd::Multiply(), CsMathNoop() + ); + }); + cs.add_command("-", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::Subtract(), ostd::Negate() + ); }); -#define CS_CMD_MATHIN(name, op, initval, unaryop) \ - CS_CMD_MATH(#name, i, CsInt, int, val = val op val2, initval, unaryop) + cs.add_command("^", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::BitXor(), [](CsInt val) { return ~val; } + ); + }); + cs.add_command("~", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::BitXor(), [](CsInt val) { return ~val; } + ); + }); + cs.add_command("&", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::BitAnd(), CsMathNoop() + ); + }); + cs.add_command("|", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::BitOr(), CsMathNoop() + ); + }); -#define CS_CMD_MATHI(name, initval, unaryop) \ - CS_CMD_MATHIN(name, name, initval, unaryop) + /* special combined cases */ + cs.add_command("^~", "i1V", [](TvalRange args, TaggedValue &res) { + CsInt val; + if (args.size() >= 2) { + val = args[0].get_int() ^ ~args[1].get_int(); + for (ostd::Size i = 2; i < args.size(); ++i) { + val ^= ~args[i].get_int(); + } + } else { + val = !args.empty() ? args[0].get_int() : 0; + } + res.set_int(val); + }); + cs.add_command("&~", "i1V", [](TvalRange args, TaggedValue &res) { + CsInt val; + if (args.size() >= 2) { + val = args[0].get_int() & ~args[1].get_int(); + for (ostd::Size i = 2; i < args.size(); ++i) { + val &= ~args[i].get_int(); + } + } else { + val = !args.empty() ? args[0].get_int() : 0; + } + res.set_int(val); + }); + cs.add_command("|~", "i1V", [](TvalRange args, TaggedValue &res) { + CsInt val; + if (args.size() >= 2) { + val = args[0].get_int() | ~args[1].get_int(); + for (ostd::Size i = 2; i < args.size(); ++i) { + val |= ~args[i].get_int(); + } + } else { + val = !args.empty() ? args[0].get_int() : 0; + } + res.set_int(val); + }); -#define CS_CMD_MATHFN(name, op, initval, unaryop) \ - CS_CMD_MATH(#name "f", f, CsFloat, float, val = val op val2, initval, unaryop) + cs.add_command("<<", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsInt val1, CsInt val2) { + return (val2 < CsInt(sizeof(CsInt) * CHAR_BIT)) + ? (val1 << ostd::max(val2, 0)) + : 0; + }, CsMathNoop() + ); + }); + cs.add_command(">>", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsInt val1, CsInt val2) { + return val1 >> ostd::clamp( + val2, 0, CsInt(sizeof(CsInt) * CHAR_BIT) + ); + }, CsMathNoop() + ); + }); -#define CS_CMD_MATHF(name, initval, unaryop) \ - CS_CMD_MATHFN(name, name, initval, unaryop) + cs.add_command("+f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::Add(), CsMathNoop() + ); + }); + cs.add_command("*f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 1, ostd::Multiply(), CsMathNoop() + ); + }); + cs.add_command("-f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, ostd::Subtract(), ostd::Negate() + ); + }); - CS_CMD_MATHI(+, 0, {}); - CS_CMD_MATHI(*, 1, {}); - CS_CMD_MATHI(-, 0, val = -val); + cs.add_command("div", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsInt val1, CsInt val2) { + if (val2) { + return val1 / val2; + } + return 0; + }, CsMathNoop() + ); + }); + cs.add_command("mod", "i1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsInt val1, CsInt val2) { + if (val2) { + return val1 % val2; + } + return 0; + }, CsMathNoop() + ); + }); + cs.add_command("divf", "f1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsFloat val1, CsFloat val2) { + if (val2) { + return val1 / val2; + } + return CsFloat(0); + }, CsMathNoop() + ); + }); + cs.add_command("modf", "f1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsFloat val1, CsFloat val2) { + if (val2) { + return CsFloat(fmod(val1, val2)); + } + return CsFloat(0); + }, CsMathNoop() + ); + }); - CS_CMD_MATHI(^, 0, val = ~val); - CS_CMD_MATHIN(~, ^, 0, val = ~val); - CS_CMD_MATHI(&, 0, {}); - CS_CMD_MATHI(|, 0, {}); - CS_CMD_MATHI(^~, 0, {}); - CS_CMD_MATHI(&~, 0, {}); - CS_CMD_MATHI(|~, 0, {}); + cs.add_command("pow", "f1V", [](TvalRange args, TaggedValue &res) { + cs_mathop( + args, res, 0, [](CsFloat val1, CsFloat val2) { + return CsFloat(pow(val1, val2)); + }, CsMathNoop() + ); + }); - CS_CMD_MATH("<<", i, CsInt, int, { - val = (val2 < 32) ? (val << ostd::max(val2, 0)) : 0; - }, 0, {}); - CS_CMD_MATH(">>", i, CsInt, int, val >>= ostd::clamp(val2, 0, 31), 0, {}); + cs.add_command("=", "i1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::Equal()); + }); + cs.add_command("!=", "i1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::NotEqual()); + }); + cs.add_command("<", "i1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::Less()); + }); + cs.add_command(">", "i1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::Greater()); + }); + cs.add_command("<=", "i1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::LessEqual()); + }); + cs.add_command(">=", "i1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::GreaterEqual()); + }); - CS_CMD_MATHF(+, 0, {}); - CS_CMD_MATHF(*, 1, {}); - CS_CMD_MATHF(-, 0, val = -val); - -#define CS_CMD_DIV(name, fmt, vtype, type, op) \ - CS_CMD_MATH(#name, fmt, vtype, type, { if (val2) op; else val = 0; }, 0, {}) - - CS_CMD_DIV(div, i, CsInt, int, val /= val2); - CS_CMD_DIV(mod, i, CsInt, int, val %= val2); - CS_CMD_DIV(divf, f, CsFloat, float, val /= val2); - CS_CMD_DIV(modf, f, CsFloat, float, val = fmod(val, val2)); - -#undef CS_CMD_DIV - - CS_CMD_MATH("pow", f, CsFloat, float, val = pow(val, val2), 0, {}); - -#undef CS_CMD_MATHF -#undef CS_CMD_MATHFN -#undef CS_CMD_MATHI -#undef CS_CMD_MATHIN -#undef CS_CMD_MATH - -#define CS_CMD_CMP(name, fmt, type, op) \ - cs.add_command(name, #fmt "1V", [&cs](TvalRange args, TaggedValue &res) { \ - bool val; \ - if (args.size() >= 2) { \ - val = args[0].fmt op args[1].fmt; \ - for (ostd::Size i = 2; i < args.size() && val; ++i) \ - val = args[i-1].fmt op args[i].fmt; \ - } else \ - val = ((args.size() > 0) ? args[0].fmt : 0) op 0; \ - res.set_int(CsInt(val)); \ - }) - -#define CS_CMD_CMPIN(name, op) CS_CMD_CMP(#name, i, CsInt, op) -#define CS_CMD_CMPI(name) CS_CMD_CMPIN(name, name) -#define CS_CMD_CMPFN(name, op) CS_CMD_CMP(#name "f", f, CsFloat, op) -#define CS_CMD_CMPF(name) CS_CMD_CMPFN(name, name) - - CS_CMD_CMPIN(=, ==); - CS_CMD_CMPI(!=); - CS_CMD_CMPI(<); - CS_CMD_CMPI(>); - CS_CMD_CMPI(<=); - CS_CMD_CMPI(>=); - - CS_CMD_CMPFN(=, ==); - CS_CMD_CMPF(!=); - CS_CMD_CMPF(<); - CS_CMD_CMPF(>); - CS_CMD_CMPF(<=); - CS_CMD_CMPF(>=); - -#undef CS_CMD_CMPF -#undef CS_CMD_CMPFN -#undef CS_CMD_CMPI -#undef CS_CMD_CMPIN -#undef CS_CMD_CMP + cs.add_command("=f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::Equal()); + }); + cs.add_command("!=f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::NotEqual()); + }); + cs.add_command("(args, res, ostd::Less()); + }); + cs.add_command(">f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::Greater()); + }); + cs.add_command("<=f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::LessEqual()); + }); + cs.add_command(">=f", "f1V", [](TvalRange args, TaggedValue &res) { + cs_cmpop(args, res, ostd::GreaterEqual()); + }); } } /* namespace cscript */