Adds support for vector's / string's array set / get operators 44/164544/20
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Tue, 19 Dec 2017 14:23:24 +0000 (15:23 +0100)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Wed, 10 Jan 2018 12:05:15 +0000 (13:05 +0100)
Adds support for accessing elements by index in string / vector values.
Both single indexes and dual (range - first inclusive, second
exclusive) versions are supported. You can do v[1] to get first element
of v, v[-1] to get last element of v (negative indexes are counted from
the end) and v[1:3] to get array including first and second element,
but not third.

Change-Id: I622d1c22a30f708007f0e8d27b4d9f3d5eb51b63

src/batch/EvaluationContext.cpp
src/batch/EvaluationContext.hpp
tests/no-ui-scenarios/BatchExecTests.cpp

index 0d7d95e..5c53379 100644 (file)
@@ -180,8 +180,82 @@ EvaluationValue EvaluationContext::convert(const Optional<TokenLocation> &loc, c
                                                                 " 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) {
@@ -194,6 +268,48 @@ EvaluationValue EvaluationContext::evaluateAccessGet(const Optional<TokenLocatio
 
 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) {
index a21288c..e4b35c3 100644 (file)
@@ -61,6 +61,9 @@ public:
        ExecutorInterface &executionInterface() const;
 private:
        void initializeGlobalContext(ExecutorInterface &ei);
+       int getSingleIndex(const std::vector<EvaluationValue> &args, int size, std::string typeName);
+       std::pair<int, int> getDoubleIndexes(const std::vector<EvaluationValue> &args, int size);
+
        std::unordered_map<std::string, EvaluationValue> variables;
        EvaluationContext *parent = nullptr;
        std::shared_ptr<GlobalContext> globalContext;
index 9da70a2..311919b 100644 (file)
@@ -144,6 +144,119 @@ TEST(TestExec, simpleParser)
        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 {