#include <tuple>
#include <ostream>
#include <type_traits>
+#include <algorithm>
class EvaluationFailure : public std::exception
{
void convertTo(unsigned short &v) const;
void convertTo(signed char &v) const;
void convertTo(unsigned char &v) const;
- template <typename T> void convertTo(std::vector<T> &v) const {
-
+ template <typename T> void convertTo(std::vector<T> &v) const
+ {
+ auto src = convertToVector();
+ v.reserve(src.size());
+ for (auto &s : src) {
+ T t;
+ s.convertTo(t);
+ v.push_back(std::move(t));
+ }
+ }
+ template <typename T> void convertTo(std::unordered_set<T> &v) const
+ {
+ auto src = convertToSet();
+ for (auto &s : src) {
+ T t;
+ s.convertTo(t);
+ v.insert(std::move(t));
+ }
+ }
+ template <typename K, typename V> void convertTo(std::unordered_map<K, V> &v) const
+ {
+ auto src = convertToDict();
+ for (auto &s : src) {
+ K k;
+ V v;
+ s.first.convertTo(k);
+ s.second.convertTo(v);
+ v.insert({ std::move(k), std::move(v) });
+ }
}
#define Q(type) \
EvaluationValue_(EvaluationValue ## type); \
private:
EvaluationValuePtr value;
- template <typename T> static EvaluationValueVector fromVector(std::vector<T> tmp) {
+ template <typename T> static EvaluationValueVector fromVector(std::vector<T> tmp)
+ {
EvaluationValueVector res;
res.reserve(tmp.size());
- for(auto &t : tmp)
+ for (auto &t : tmp)
res.push_back(std::move(t));
return res;
}
- template <typename T> static EvaluationValueSet fromSet(std::unordered_set<T> tmp) {
+ template <typename T> static EvaluationValueSet fromSet(std::unordered_set<T> tmp)
+ {
EvaluationValueSet res;
res.reserve(tmp.size());
- for(auto &t : tmp)
+ for (auto &t : tmp)
res.insert(std::move(t));
return res;
}
- template <typename K, typename EvaluationValuePtr> static EvaluationValueDict fromMap(std::unordered_map<K, EvaluationValuePtr> tmp) {
+ template <typename K, typename V> static EvaluationValueDict fromMap(std::unordered_map<K, V> tmp)
+ {
EvaluationValueDict res;
res.reserve(tmp.size());
- for(auto &t : tmp)
+ for (auto &t : tmp)
res.insert({ t.first, std::move(t.second) });
return res;
}
return ah.apply_1();
}
template <size_t I, size_t S> struct Converter {
- template <typename ... ARGS> static void convert(std::tuple<ARGS...> &dst, const std::vector<EvaluationValue_> &sourceArgs)
+ template <typename ... ARGS> static void convert(std::tuple<ARGS...> &dst,
+ const std::vector<EvaluationValue_> &sourceArgs,
+ size_t firstDefaultArgIndex,
+ const std::vector<EvaluationValue_> &defaultArguments)
{
try {
- sourceArgs[I].convertTo(std::get<I>(dst));
+ auto &s = I < sourceArgs.size() ? sourceArgs[I] : defaultArguments[I - firstDefaultArgIndex];
+ s.convertTo(std::get<I>(dst));
} catch (EvaluationFailure &e) {
e << ", when converting " << (I + 1) << " argument";
throw;
}
- Converter < I + 1, S >::convert(dst, sourceArgs);
+ Converter < I + 1, S >::convert(dst, sourceArgs, firstDefaultArgIndex, defaultArguments);
}
};
template <size_t S> struct Converter<S, S> {
- template <typename ... ARGS> static void convert(std::tuple<ARGS...> &, const std::vector<EvaluationValue_> &)
+ template <typename ... ARGS> static void convert(std::tuple<ARGS...> &, const std::vector<EvaluationValue_> &,
+ size_t, const std::vector<EvaluationValue_> &)
{
}
};
enum { value = 0 };
};
- template <typename T> struct is_allowed_evaluatation_value_type { enum { value = 0 }; };
- template <typename T> struct is_allowed_evaluatation_value_type<T&> { enum { value = is_allowed_evaluatation_value_type<T>::value }; };
- template <typename T> struct is_allowed_evaluatation_value_type<const T&> { enum { value = is_allowed_evaluatation_value_type<T>::value }; };
- template <typename T> struct is_allowed_evaluatation_value_type<const T> { enum { value = is_allowed_evaluatation_value_type<T>::value }; };
- template <> struct is_allowed_evaluatation_value_type<int> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<unsigned int> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<short> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<unsigned short> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<signed char> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<unsigned char> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValue_> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueEmpty> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueString> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueInteger> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueDouble> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueUIElement> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueBoolean> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValuePoint> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueFunction> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueVector> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueSet> { enum { value = 1 }; };
- template <> struct is_allowed_evaluatation_value_type<EvaluationValueDict> { enum { value = 1 }; };
+ template <typename T> struct is_allowed_evaluatation_value_type {
+ enum { value = 0 };
+ };
+ template <typename T> struct is_allowed_evaluatation_value_type<T &> {
+ enum { value = is_allowed_evaluatation_value_type<T>::value };
+ };
+ template <typename T> struct is_allowed_evaluatation_value_type<const T &> {
+ enum { value = is_allowed_evaluatation_value_type<T>::value };
+ };
+ template <typename T> struct is_allowed_evaluatation_value_type<const T> {
+ enum { value = is_allowed_evaluatation_value_type<T>::value };
+ };
+ template <> struct is_allowed_evaluatation_value_type<int> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<unsigned int> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<short> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<unsigned short> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<signed char> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<unsigned char> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValue_> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueEmpty> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueString> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueInteger> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueDouble> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueUIElement> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueBoolean> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValuePoint> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueFunction> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueVector> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueSet> {
+ enum { value = 1 };
+ };
+ template <> struct is_allowed_evaluatation_value_type<EvaluationValueDict> {
+ enum { value = 1 };
+ };
template <typename T> struct is_allowed_evaluatation_value_type<std::vector<T>> {
enum { value = is_allowed_evaluatation_value_type<T>::value };
};
{
public:
using Type = std::function<EvaluationValue_(const std::vector<EvaluationValue_>&)>;
+ class Arg
+ {
+ public:
+ Arg(std::string name) : name(std::move(name))
+ {
+ ASSERT(!this->name.empty());
+ }
+ Arg(const char *name) : name(name)
+ {
+ ASSERT(!this->name.empty());
+ }
+ Arg(std::string name, EvaluationValue_ val) : name(std::move(name)), defaultValue(std::move(val)) { }
+
+ std::string getName() const
+ {
+ return name;
+ }
+ bool hasDefaultValue() const
+ {
+ return bool(defaultValue);
+ }
+ EvaluationValue_ stealDefaultValue()
+ {
+ auto tmp = std::move(*defaultValue);
+ defaultValue = {};
+ return tmp;
+ }
+ private:
+ std::string name;
+ Optional<EvaluationValue_> defaultValue;
+ };
+ class Args
+ {
+ public:
+ Args(const std::vector<EvaluationValue_> &args) : args(args) { }
+ bool empty() const
+ {
+ return args.empty();
+ }
+ size_t size() const
+ {
+ return args.size();
+ }
+ const EvaluationValue_ &operator [](size_t index) const
+ {
+ return args[index];
+ }
+ private:
+ const std::vector<EvaluationValue_> &args;
+ };
EvaluationValueFunction() = default;
template <typename T, typename ARGS_TUPLE = typename detail::get_args_as_tuple_from_lambda<T>::type,
- typename = typename std::enable_if<detail::are_args_allowed_evaluation_value_from_tuple<ARGS_TUPLE>::value>::type>
- EvaluationValueFunction(T && f) : function(constructConvertingArgsFunction<ARGS_TUPLE>(std::move(f))) { }
- EvaluationValueFunction(std::function<EvaluationValue_(const std::vector<EvaluationValue_> &)> f) : function(std::move(f)) {}
+ typename = typename std::enable_if<detail::are_args_allowed_evaluation_value_from_tuple<ARGS_TUPLE>::value>::type>
+ EvaluationValueFunction(T && f, std::vector<Arg> argDescriptions = {}) :
+ function(constructConvertingArgsFunction<ARGS_TUPLE>(std::move(f), std::move(argDescriptions))) { }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<std::function<EvaluationValue_(Args)>, T>::value>::type>
+ EvaluationValueFunction(T && f) : function([f = std::forward<T>(f)](const std::vector<EvaluationValue_> & args)
+ {
+ return f(Args{ args });
+ }) {}
explicit operator bool () const
{
return bool(function);
}
- EvaluationValue_ operator () (const std::vector<EvaluationValue_> &args) const
+ EvaluationValue_ operator()(const std::vector<EvaluationValue_> &args) const
{
return function(args);
}
-protected:
+protected:
Type function;
- static void validateArgumentCount(size_t expected, size_t got)
+ static void validateArgumentCount(size_t got, size_t expectedMin, size_t expectedMax)
{
- if (got < expected)
- throw EvaluationFailure{} << "not enough arguments, expected " << expected << ", got " << got;
- if (got > expected)
- throw EvaluationFailure{} << "too many arguments, expected " << expected << ", got " << got;
+ if (got < expectedMin)
+ throw EvaluationFailure{} << "not enough arguments, expected " <<
+ (expectedMin < expectedMax ? "at least " : "") << expectedMin << ", got " << got;
+ if (got > expectedMax)
+ throw EvaluationFailure{} << "too many arguments, expected " <<
+ (expectedMin < expectedMax ? "at most " : "") << expectedMax << ", got " << got;
}
private:
template <typename ARGS_TUPLE, typename T>
- static EvaluationValueFunction::Type constructConvertingArgsFunction(T &&f)
+ static EvaluationValueFunction::Type constructConvertingArgsFunction(T &&f, std::vector<Arg> argDescriptions)
{
- return [f = std::move(f)](const std::vector<EvaluationValue_> &sourceArgs) -> EvaluationValue_ {
- constexpr auto expectedArgs = std::tuple_size<ARGS_TUPLE>::value;
- validateArgumentCount(expectedArgs, sourceArgs.size());
- //std::tuple<ARGS...>
+ constexpr auto expectedArgCount = std::tuple_size<ARGS_TUPLE>::value;
+ ASSERT(expectedArgCount >= argDescriptions.size());
+ std::vector<EvaluationValue_> defaultArguments;
+
+ for (auto it = argDescriptions.rbegin(); it != argDescriptions.rend() && it->hasDefaultValue(); ++it) {
+ defaultArguments.push_back(std::move(it->stealDefaultValue()));
+ }
+ std::reverse(defaultArguments.begin(), defaultArguments.end());
+ auto firstDefaultArgIndex = expectedArgCount - defaultArguments.size();
+
+ return [f = std::move(f), defaultArguments = std::move(defaultArguments), firstDefaultArgIndex]
+ (const std::vector<EvaluationValue_> &sourceArgs) -> EvaluationValue_ {
+ validateArgumentCount(sourceArgs.size(), firstDefaultArgIndex, expectedArgCount);
ARGS_TUPLE args;
- detail::Converter<0, expectedArgs>::convert(args, sourceArgs);
+ detail::Converter<0, expectedArgCount>::convert(args, sourceArgs, firstDefaultArgIndex, defaultArguments);
return detail::apply(f, args);
};
}
class ScriptTest : public ::testing::Test
{
protected:
+ size_t assertCount = 0;
+ std::ostringstream output;
+ VarsType vars;
+
+ ScriptTest()
+ {
+ vars["assert"] = [this](bool val) -> EvaluationValue_ {
+ if (!val) throw EvaluationFailure{} << "assertion failed";
+ ++assertCount;
+ return {};
+ };
+ vars["print"] = [this](EvaluationValue_ val) -> EvaluationValue_ {
+ output << ">>> " << val << "\n";
+ return {};
+ };
+ }
+
void SetUp(void) override
{
}
void TearDown() override
{
}
- void execute(std::string txt, size_t count = 1)
+ void execute(std::string txt, EvaluationContext &ec, bool printException)
{
std::string error;
auto tokens = lexTest(error, "test", txt);
- ASSERT_TRUE(error.empty()) << error;
- // {
- // size_t index = 0;
- // for(auto &t : tokens)
- // std::cout << t.location().toString() << ": " << (index++) << " '" << t.text() << "' " << toString(t.type()) << "\n";
- // }
+ if (!error.empty()) {
+ size_t index = 0;
+ for (auto &t : tokens)
+ std::cout << t.location().toString() << ": " << (index++) << " '" << t.text() << "' " << toString(t.type()) << "\n";
+ ASSERT_TRUE(error.empty()) << error;
+ }
std::vector<std::string> errors;
auto result = parseTokens(errors, tokens);
- for (auto &e : errors) {
- std::cout << e << "\n";
+ if (!errors.empty()) {
+ std::ostringstream tmp;
+ result->printSelfInfo(tmp, 0);
+ std::cout << tmp.str();
+ for (auto &e : errors) {
+ std::cout << e << "\n";
+ }
+ FAIL();
}
- ASSERT_TRUE(errors.empty());
ASSERT_TRUE(result);
- // {
- // std::ostringstream tmp;
- // result->printSelfInfo(tmp, 0);
- // std::cout << tmp.str();
- // }
-
- size_t callCount = 0;
- auto vars = VarsType { };
- std::ostringstream output;
- vars["assert"] = [&](bool val) -> EvaluationValue_ {
- if (!val) throw EvaluationFailure{} << "assertion failed";
- ++callCount;
- return {};
- };
- vars["print"] = [&](EvaluationValue_ val) -> EvaluationValue_ {
- output << ">>> " << val << "\n";
- return {};
- };
- TestExecutor exec{ std::move(vars) };
- EvaluationContext ec{ exec };
-
try {
result->evaluate();
} catch (EvaluationFailure &e) {
- size_t i = 0, c = 1;
- while (i < txt.size()) {
- auto n = txt.find('\n', i);
- if (n == std::string::npos) n = txt.size();
- output << std::setw(2) << c << ": " << txt.substr(i, n - i) << "\n";
- if (e.hasLocation() && c == e.location().lineNum()) {
- for (auto i = 1u; i < e.location().offsetNum(); ++i)
- output << " ";
- output << " ^\n";
+ if (printException) {
+ size_t i = 0, c = 1;
+ while (i < txt.size()) {
+ auto n = txt.find('\n', i);
+ if (n == std::string::npos) n = txt.size();
+ output << std::setw(2) << c << ": " << txt.substr(i, n - i) << "\n";
+ if (e.hasLocation() && c == e.location().lineNum()) {
+ for (auto i = 1u; i < e.location().offsetNum(); ++i)
+ output << " ";
+ output << " ^\n";
+ }
+ i = n + 1;
+ ++c;
}
- i = n + 1;
- ++c;
}
+ throw;
+ }
+ }
+ void execute(std::string txt, bool printException)
+ {
+ TestExecutor exec{ std::move(vars) };
+ EvaluationContext ec{ exec };
+ execute(std::move(txt), ec, printException);
+ }
+ void executeCountAsserts(std::string txt, size_t count = 1)
+ {
+ try {
+ execute(std::move(txt), true);
+ } catch (EvaluationFailure &e) {
FAIL() << output.str() << (e.hasLocation() ? e.location().toString() + ": " : std::string{}) << e.message();
}
-
- ASSERT_EQ(callCount, count);
+ ASSERT_EQ(assertCount, count);
}
};
TEST_F(ScriptTest, simpleComparisions)
{
- execute(
+ executeCountAsserts(
"assert(1 == 1)\n"
"assert(1 != 2)\n"
"assert(1 < 2)\n"
TEST_F(ScriptTest, vector)
{
- execute(
+ executeCountAsserts(
"v = [1, 2, 3]\n"
"assert(v[0] == 1)\n"
"assert(v[1] == 2)\n"
TEST_F(ScriptTest, set)
{
- execute(
+ executeCountAsserts(
"v = {1, 2, 3, '1', '2', '3'}\n"
"assert(1 in v)\n"
"assert(2 in v)\n"
TEST_F(ScriptTest, map)
{
- execute(
+ executeCountAsserts(
"v = {1: 1, 2: 2, '1': 3}\n"
"assert(1 in v)\n"
"assert(2 in v)\n"
TEST_F(ScriptTest, point)
{
- execute(
+ executeCountAsserts(
"v = <1,2>\n"
"assert(v == <1,2>)\n"
, 1);
, 1);
}
+TEST_F(ScriptTest, defaultArguments)
+{
+ std::tuple<int, int, int, int> val, expected;
+
+ vars["func"] = EvaluationValueFunction{
+ [&](int a1, int a2, int a3, int a4) -> EvaluationValue_ {
+ std::get<0>(val) = a1;
+ std::get<1>(val) = a2;
+ std::get<2>(val) = a3;
+ std::get<3>(val) = a4;
+ return {};
+ }, {
+ { "a2" },
+ { "a3", 1 },
+ { "a4", 2 }
+ } };
+
+ TestExecutor exec{ std::move(vars) };
+ EvaluationContext ec{ exec };
+
+ execute("func(6,7,8,9)", ec, true);
+ expected = { 6, 7, 8, 9};
+ ASSERT_EQ(val, expected);
+
+ execute("func(5, 6)", ec, true);
+ expected = { 5, 6, 1, 2};
+ ASSERT_EQ(val, expected);
+
+ execute("func(2, 3, 4)", ec, true);
+ expected = { 2, 3, 4, 2};
+ ASSERT_EQ(val, expected);
+
+ try {
+ execute("func(2)", ec, true);
+ FAIL() << "didn't throw";
+ } catch (EvaluationFailure &) {
+ }
+
+ try {
+ execute("func()", ec, true);
+ FAIL() << "didn't throw";
+ } catch (EvaluationFailure &) {
+ }
+
+ try {
+ execute("func(1,2,3,4,5)", ec, true);
+ FAIL() << "didn't throw";
+ } catch (EvaluationFailure &) {
+ }
+}
+
int main(int argc, char *argv[])
{
try {