" to " << EvaluationValue::toString(EvaluationValue::Kind::FUNCTION);
}
+int EvaluationContext::getSingleIndex(const std::vector<EvaluationValue> &args, int size, std::string typeName)
+{
+ ASSERT(args.size() >= 2);
+ auto index = detail::ConvertTo<int>::convert(*this, args[1]);
+ auto origIndex = index;
+ if (index < 0) index += size;
+ if (index < 0 || index >= size) throw EvaluationFailure{} << origIndex << " is out of range for " << typeName << " of size " << size;
+ return index;
+}
+
+std::pair<int, int> EvaluationContext::getDoubleIndexes(const std::vector<EvaluationValue> &args, int size)
+{
+ ASSERT(args.size() >= 3);
+ auto index1 = detail::ConvertTo<int>::convert(*this, args[1]);
+ auto index2 = detail::ConvertTo<int>::convert(*this, args[2]);
+ if (index1 < 0) index1 += size;
+ if (index2 < 0) index2 += size;
+
+ if (index1 < 0) index1 = 0;
+ else if (index1 > size) index1 = size;
+ if (index2 < index1) index2 = index1;
+ else if (index2 > size) index2 = size;
+ return std::pair<int, int> { index1, index2 };
+}
+
EvaluationValue EvaluationContext::evaluateAccessGet(const Optional<TokenLocation> &loc, const std::vector<EvaluationValue> &args)
{
+ try {
+ ASSERT(!args.empty());
+ auto &self = args[0];
+ switch (self.kind()) {
+ case EvaluationValue::Kind::STRING:
+ if (args.size() == 2) {
+ auto index = getSingleIndex(args, static_cast<int>(self.asString().size()), "string");
+ return self.asString().substr(index, 1);
+ } else if (args.size() == 3) {
+ auto indexes = getDoubleIndexes(args, static_cast<int>(self.asString().size()));
+ return self.asString().substr(indexes.first, indexes.second - indexes.first);
+ }
+ break;
+ case EvaluationValue::Kind::FUNCTION:
+ case EvaluationValue::Kind::INTEGER:
+ case EvaluationValue::Kind::DOUBLE:
+ case EvaluationValue::Kind::UIELEMENT:
+ case EvaluationValue::Kind::BOOLEAN:
+ case EvaluationValue::Kind::POINT:
+ case EvaluationValue::Kind::EMPTY:
+ case EvaluationValue::Kind::SET:
+ break;
+ case EvaluationValue::Kind::VECTOR:
+ if (args.size() == 2) {
+ auto index = getSingleIndex(args, static_cast<int>(self.asVector().size()), "vector");
+ return self.asVector()[index];
+ } else if (args.size() == 3) {
+ auto indexes = getDoubleIndexes(args, static_cast<int>(self.asVector().size()));
+ std::vector<EvaluationValue> tmp;
+ tmp.reserve(indexes.second - indexes.first);
+ for (auto i = indexes.first; i < indexes.second; ++i)
+ tmp.push_back(self.asVector()[i]);
+ return std::move(tmp);
+ }
+ break;
+ case EvaluationValue::Kind::DICT:
+ if (args.size() == 2) {
+ auto key = EvaluationValueKey{ args[1] };
+ auto it = self.asDict().find(key);
+ if (it == self.asDict().end()) throw EvaluationFailure{loc} << "key not found";
+ return it->second;
+ }
+ break;
+ }
+ } catch (EvaluationFailure &e) {
+ if (loc) e.setLocationIfMissing(*loc);
+ throw;
+ }
+
std::ostringstream tmp;
tmp << EvaluationValue::toString(args[0].kind()) << "[";
for (auto i = 1u; i < args.size(); ++i) {
EvaluationValue EvaluationContext::evaluateAccessSet(const Optional<TokenLocation> &loc, std::vector<EvaluationValue> args)
{
+ try {
+ ASSERT(!args.empty());
+ auto &self = args[0];
+ switch (self.kind()) {
+ case EvaluationValue::Kind::STRING:
+ case EvaluationValue::Kind::FUNCTION:
+ case EvaluationValue::Kind::INTEGER:
+ case EvaluationValue::Kind::DOUBLE:
+ case EvaluationValue::Kind::UIELEMENT:
+ case EvaluationValue::Kind::BOOLEAN:
+ case EvaluationValue::Kind::POINT:
+ case EvaluationValue::Kind::EMPTY:
+ case EvaluationValue::Kind::SET:
+ break;
+ case EvaluationValue::Kind::VECTOR:
+ if (args.size() == 3) {
+ auto index = getSingleIndex(args, static_cast<int>(self.asVector().size()), "vector");
+ self.asVector()[index] = args[2];
+ return std::move(args[2]);
+ } else if (args.size() == 4 && args[3].kind() == EvaluationValue::Kind::VECTOR) {
+ auto indexes = getDoubleIndexes(args, static_cast<int>(self.asVector().size()));
+ std::vector<EvaluationValue> tmp;
+ tmp.reserve(self.asVector().size() - (indexes.second - indexes.first) + args[3].asVector().size());
+ for (auto i = 0; i < indexes.first; ++i) tmp.push_back(std::move(self.asVector()[i]));
+ for (auto &v : args[3].asVector()) tmp.push_back(v);
+ for (auto i = indexes.second; i < static_cast<int>(self.asVector().size()); ++i) tmp.push_back(std::move(self.asVector()[i]));
+ self.asVector() = std::move(tmp);
+ return {};
+ }
+ break;
+ case EvaluationValue::Kind::DICT:
+ if (args.size() == 3) {
+ self.asDict()[std::move(args[1])] = args[2];
+ return std::move(args[2]);
+ }
+ break;
+ }
+ } catch (EvaluationFailure &e) {
+ if (loc) e.setLocationIfMissing(*loc);
+ throw;
+ }
+
std::ostringstream tmp;
tmp << EvaluationValue::toString(args[0].kind()) << "[";
for (auto i = 1; i < (int)args.size() - 1; ++i) {
ASSERT_EQ(result2, "1,qwe,rty,true");
}
+
+class ScriptTest : public ::testing::Test
+{
+protected:
+ void SetUp(void) override
+ {
+ }
+ void TearDown() override
+ {
+ }
+ void execute(std::string txt, size_t count = 1)
+ {
+ std::string error;
+ auto tokens = lexTest(error, "test", txt);
+ ASSERT_TRUE(error.empty()) << error;
+
+ std::vector<std::string> errors;
+ auto result = parseTokens(errors, tokens);
+
+ for (auto &e : errors) {
+ std::cout << e << "\n";
+ }
+ ASSERT_TRUE(errors.empty());
+ ASSERT_TRUE(result);
+
+ // {
+ // std::ostringstream tmp;
+ // result->debug(tmp, 0);
+ // std::cout << tmp.str();
+ // }
+
+ size_t callCount = 0;
+ auto vars = VarsType { };
+ std::ostringstream output;
+ vars["assert"] = EvaluationValue::UserFunctionType<bool> {
+ [&](EvaluationContext &, bool val) -> EvaluationValue {
+ if (!val) throw EvaluationFailure{} << "assertion failed2";
+ ++callCount;
+ return {};
+ }
+ };
+ vars["print"] = EvaluationValue::UserFunctionType<EvaluationValue> {
+ [&](EvaluationContext &, EvaluationValue val) -> EvaluationValue {
+ output << ">>> " << val << "\n";
+ return {};
+ }
+ };
+ TestExecutor exec{ std::move(vars) };
+ EvaluationContext ec{ exec };
+
+ try {
+ result->evaluate(ec);
+ } 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";
+ }
+ i = n + 1;
+ ++c;
+ }
+ FAIL() << output.str() << (e.hasLocation() ? e.location().toString() + ": " : std::string{}) << e.message();
+ }
+
+ ASSERT_EQ(callCount, count);
+ }
+};
+
+TEST_F(ScriptTest, simpleComparisions)
+{
+ execute(
+ "assert(1 == 1)\n"
+ "assert(1 != 2)\n"
+ "assert(1 < 2)\n"
+ "assert(1 > 0)\n"
+ "assert(1 <= 2)\n"
+ "assert(1 <= 1)\n"
+ "assert(1 >= 0)\n"
+ "assert(1 >= 1)\n"
+ "assert(1 == 1 != 2 == 2)\n"
+ "assert(1 < 2 > 1)\n"
+ "assert(1 < 2 < 3)\n"
+ "assert(not (1 > 2))\n"
+ , 12);
+}
+
+TEST_F(ScriptTest, vector)
+{
+ execute(
+ "v = [1, 2, 3]\n"
+ "assert(v[0] == 1)\n"
+ "assert(v[1] == 2)\n"
+ "assert(v[2] == 3)\n"
+ "assert(v[1:2] == [2])\n"
+ "v[1:2] = [4, 5, 6]\n"
+ "assert(v == [1, 4, 5, 6, 3])\n"
+ "assert([1] != [2])\n"
+ "assert([1] == [1])\n"
+ "assert([1] != [])\n"
+ "assert([] == [])\n"
+ "assert([1, 2, 3] != [1, 2, 3, 4])\n"
+ "assert([1, 2, 3] < [1, 2, 3, 4])\n"
+ "assert([1, 2, 3] <= [1, 2, 3, 4])\n"
+ "assert([1, 2, 3, 5 ] > [1, 2, 3, 4])\n"
+ "assert([1, 2, 3, 5 ] >= [1, 2, 3, 4])\n"
+ , 14);
+}
+
int main(int argc, char *argv[])
{
try {