Add functions to batch runner 33/169733/6
authorPaweł Stawicki <p.stawicki@samsung.com>
Thu, 8 Feb 2018 18:44:50 +0000 (19:44 +0100)
committerPaweł Stawicki <p.stawicki@samsung.com>
Mon, 12 Feb 2018 15:32:44 +0000 (16:32 +0100)
Change-Id: I25c6c0d1ad65a611e5275e7824276fb73b312183

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

index c2e7b16..94fd7aa 100644 (file)
@@ -65,8 +65,8 @@ EvaluationValue EvaluationContext::getVariable(const Optional<TokenLocation> &lo
 {
        EvaluationContext *self = this;
        while (self) {
-               auto it = variables.find(ident);
-               if (it != variables.end()) return it->second;
+               auto it = self->variables.find(ident);
+               if (it != self->variables.end()) return it->second;
                self = self->parent;
        }
        try {
index e1ffe96..4fd9e4b 100644 (file)
@@ -484,6 +484,40 @@ void CallEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) co
        }
 }
 
+FunctionEvaluator::FunctionEvaluator(TokenLocation location_, std::string functionName, std::vector<std::string> argNames, StatPtr codeBlock):
+       StatementEvaluator(location_), functionName(std::move(functionName)), argNames(std::move(argNames)), codeBlock(std::move(codeBlock))  {}
+
+void FunctionEvaluator::evaluateImpl() const
+{
+       /*      TODO: add named parameters
+           std::vector<EvaluationValueFunction::Arg> tmp;
+               for (auto &arg: argNames) {
+                       tmp.push_back(arg);
+               }
+               */
+       EvaluationContext::getCurrentEvaluationContext().setVariable(functionName,
+       EvaluationValueFunction {[ = ](EvaluationValueFunction::Args args) -> EvaluationValue {
+                       if (this->argNames.size() != args.size())
+                               throw EvaluationFailure{} << "invalid number of arguments";
+                       EvaluationContext ctx;
+                       for (auto i = 0u; i < args.size(); ++i)
+                       {
+                               ctx.setVariable(argNames[i], args[i]);
+                       }
+                       codeBlock->evaluate();
+                       return {};
+               }});
+}
+
+void FunctionEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const
+{
+       printLocationAndIndent(os, location(), depth) << "function definition " << functionName << "\n";
+       for (auto i = 0u; i < argNames.size(); ++i) {
+               printLocationAndIndent(os, location(), depth) << "arg " << i << " is " << argNames[i] << "\n";
+       }
+       printLocationAndIndent(os, location(), depth) << "body\n";
+       codeBlock->printSelfInfo(os, depth + 1);
+}
 
 
 ExpressionAsStatementEvaluator::ExpressionAsStatementEvaluator(TokenLocation tokenLocation, ExprPtr expr) :
index 20e19e9..d42630e 100644 (file)
@@ -441,4 +441,19 @@ public:
        void printSelfInfo(std::ostringstream &os, unsigned int depth) const override;
 };
 
-#endif
\ No newline at end of file
+/**
+ * @brief Type representing group of statements, evaluated in order
+ */
+class FunctionEvaluator : public StatementEvaluator
+{
+       std::string functionName;
+       std::vector<std::string> argNames;
+       StatPtr codeBlock;
+public:
+       FunctionEvaluator(TokenLocation location_, std::string functionName, std::vector<std::string> argNames, StatPtr codeBlock);
+protected:
+       void evaluateImpl() const override;
+       void printSelfInfo(std::ostringstream &os, unsigned int depth) const override;
+};
+
+#endif
index 675ab1a..8d023a4 100644 (file)
@@ -78,7 +78,7 @@ std::string toString(TokenType t)
  * If found, resulting token will be KEYWORD rather than IDENTIFIER.
  */
 static std::unordered_set<std::string> keywords {
-       "true", "false"
+       "true", "false", "def"
 };
 
 /**
index 263f3b4..4fcebc7 100644 (file)
@@ -89,7 +89,7 @@ class Parser
         */
        std::string consumeIdentifier()
        {
-               if (checkTokenType(TokenType::IDENTIFIER)) return {};
+               if (!checkTokenType(TokenType::IDENTIFIER)) return {};
                auto ident = tokens[index].text();
                ++index;
                return std::move(ident);
@@ -538,6 +538,56 @@ class Parser
                }
                return std::make_shared<BlockEvaluator>(std::move(loc), std::move(statements));
        }
+
+       Optional<std::vector<std::string>> consumeArguments()
+       {
+               std::vector<std::string> args;
+               if (!consume("(")) return {};
+               while (index < tokens.size()) {
+                       if (canConsume(")")) break;
+                       if (!args.empty() && !consume(",")) return {};
+                       auto e = consumeIdentifier();
+                       if (e.empty()) return {};
+                       args.push_back(std::move(e));
+               }
+               if (!consume(")")) return {};
+               return std::move(args);
+       }
+
+       /**
+        * @brief Tries to parse a block of code
+        */
+
+       StatPtr parseBlock()
+       {
+               if (!consume("{"))
+                       return {};
+               auto line = parseBlockOfLines();
+               if (!consume("}"))
+                       return {};
+               return line;
+       }
+
+       /**
+        * @brief Tries to parse single function
+        */
+       StatPtr parseFunction()
+       {
+               auto location = tokens[index].location();
+               if (!consume("def"))
+                       return {};
+               auto functionName = consumeIdentifier();
+               if (functionName.empty())
+                       return {};
+               auto args = consumeArguments();
+               if (!args)
+                       return {};
+               auto block = parseBlock();
+               if (!block)
+                       return {};
+               return std::make_shared<FunctionEvaluator>(std::move(location), std::move(functionName), std::move(*args), std::move(block));
+       }
+
        /**
         * @brief Tries to parse single line of code, which is suppoused to be expression
         *
@@ -549,9 +599,9 @@ class Parser
                bool tryWait = true;
 
                if (tokens[index].text() == "{") {
-                       ++index;
-                       line = parseBlockOfLines();
-                       if (!consume("}")) return {};
+                       line = parseBlock();
+               } else if (tokens[index].text() == "def") {
+                       line = parseFunction();
                } else {
                        auto expr = parseExpr();
                        if (!expr) return {};
@@ -559,7 +609,8 @@ class Parser
                                if (!consume(TokenType::END_OF_LINE)) return {};
                                tryWait = false;
                        }
-                       line = std::make_shared<ExpressionAsStatementEvaluator>(expr->location(), std::move(expr));
+                       auto loc = expr->location();
+                       line = std::make_shared<ExpressionAsStatementEvaluator>(std::move(loc), std::move(expr));
                }
                if (tryWait && canConsume("->")) {
                        ExprPtrs waits;
index 66cc6a0..edb5566 100644 (file)
@@ -180,30 +180,44 @@ protected:
        void TearDown() override
        {
        }
-       void execute(std::string txt, EvaluationContext &ec, bool printException)
+
+       struct ParseFailure : EvaluationFailure { };
+       struct LexerFailure : EvaluationFailure { };
+
+       StatPtr parse(std::string txt, EvaluationContext &ec, bool printException)
        {
                std::string error;
                auto tokens = lexTest(error, "test", txt);
 
                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;
+                       if (printException) {
+                               for (auto &t : tokens)
+                                       std::cout << t.location().toString() << ": " << (index++) << " '" << t.text() << "' " << toString(t.type()) << "\n";
+                       }
+                       throw LexerFailure{} << error;
                }
 
                std::vector<std::string> errors;
                auto result = parseTokens(errors, tokens);
 
                if (!errors.empty()) {
-                       std::ostringstream tmp;
-                       result->printSelfInfo(tmp, 0);
-                       std::cout << tmp.str();
-                       for (auto &e : errors) {
-                               std::cout << e << "\n";
+                       if (printException) {
+                               std::ostringstream tmp;
+                               result->printSelfInfo(tmp, 0);
+                               std::cout << tmp.str();
+                               for (auto &e : errors) {
+                                       std::cout << e << "\n";
+                               }
                        }
-                       FAIL();
+                       throw ParseFailure{};
                }
+               return result;
+       }
+
+       void execute(std::string txt, EvaluationContext &ec, bool printException)
+       {
+               auto result = parse(txt, ec, printException);
                ASSERT_TRUE(result);
 
                try {
@@ -227,6 +241,16 @@ protected:
                        throw;
                }
        }
+       void executeWithInfo(std::string txt, EvaluationContext &ec)
+       {
+               try {
+                       execute(std::move(txt), ec, true);
+               } catch (EvaluationFailure &e) {
+                       std::cout << output.str() << (e.hasLocation() ? e.location().toString() + ": " : std::string{}) << e.message();
+                       throw;
+               }
+       }
+
        void execute(std::string txt, bool printException)
        {
                TestExecutor exec{ std::move(vars) };
@@ -563,6 +587,221 @@ TEST_F(ScriptTest, Import)
        ASSERT_EQ(funcCount, 2);
 }
 
+TEST_F(ScriptTest, functionDefinitionParser)
+{
+       TestBatchExecutor exec;
+       EvaluationContext ec{ exec };
+
+       execute("def myFunc(a) {}", ec, true);
+       execute("def myFunc(a,b) {}", ec, true);
+       execute("def myFunc(a,b,c) {}", ec, true);
+
+       ASSERT_THROW(parse("def () {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc) {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc( {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc()", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc(,) {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc(12) {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc(def) {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc(,a) {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def myFunc(a,) {}", ec, false), ParseFailure);
+       ASSERT_THROW(parse("def true() {}", ec, false), ParseFailure);
+
+       ASSERT_THROW(
+               parse("def myFunc(a,b) { }\n"
+                         "myFunc(,12)", ec, false)
+               , ParseFailure);
+
+       ASSERT_THROW(
+               parse("def myFunc(a,b) { }\n"
+                         "myFunc(12,)", ec, false)
+               , ParseFailure);
+
+       ASSERT_THROW(
+               parse("def myFunc(a,b) { }\n"
+                         "myFunc(,)", ec, false)
+               , ParseFailure);
+
+       ASSERT_THROW(
+               parse("def myFunc() { }\n"
+                         "myFunc(,)", ec, false)
+               , ParseFailure);
+
+}
+
+
+TEST_F(ScriptTest, functionDefinition)
+{
+       std::tuple<int, int> val, expected;
+       int funcCount = 0;
+
+       TestBatchExecutor exec;
+       EvaluationContext ec{ exec };
+
+       exec.variables["func"] = EvaluationValueFunction {
+               [&](int a1, int a2) -> EvaluationValue {
+                       std::get<0>(val) = a1;
+                       std::get<1>(val) = a2;
+                       funcCount++;
+                       return {};
+               }, {
+                       { "a1" },
+                       { "a2" },
+               } };
+
+
+       executeWithInfo("def myFunc(a) {}", ec);
+       executeWithInfo("def myFunc(a,b) {}", ec);
+       executeWithInfo("def myFunc(a,b,c) {}", ec);
+
+       executeWithInfo("def myFunc(a) { }\n"
+                                       "myFunc(12)", ec);
+
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "def myFunc(a) {\n"
+                       "   func(a,a)\n"
+                       "}\n"
+                       "myFunc(12)", ec)
+       );
+       expected = { 12, 12};
+       ASSERT_EQ(val, expected);
+
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "def myFunc(a) { \n"
+                       "   func(a,a) \n"
+                       "}\n"
+                       "def myFunc2(b) { \n"
+                       "   myFunc(b)\n"
+                       "}\n"
+                       "myFunc2(13)", ec)
+       );
+       expected = { 13, 13};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "def myFunc(a) {"
+                       "  def myFunc2(b) {\n"
+                       "    func(b,b)\n"
+                       "  }\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(14)", ec)
+       );
+       expected = { 14, 14};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "def myFunc(a) {\n"
+                       "  def myFunc2(b) {\n"
+                       "    func(a,b)\n"
+                       "  }\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(15)", ec)
+       );
+
+       expected = { 15, 15};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "w = 16\n"
+                       "def myFunc(a) {\n"
+                       "  def myFunc2(b) {\n"
+                       "    func(a,w)\n"
+                       "  }\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(15)", ec)
+       );
+
+       expected = { 15, 16};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "w = 16\n"
+                       "def myFunc(a) {\n"
+                       "  w = 17\n"
+                       "  def myFunc2(b) {\n"
+                       "    func(a,w)\n"
+                       "  }\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(15)\n"
+                       "assert(w == 16)", ec)
+       );
+
+       expected = { 15, 17};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "w = 16\n"
+                       "def myFunc(a) {\n"
+                       "  w = 17\n"
+                       "  def myFunc2(b) {\n"
+                       "    w = 18\n"
+                       "    func(a,w)\n"
+                       "  }\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(15)", ec)
+       );
+
+       expected = { 15, 18};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "w = 16\n"
+                       "def myFunc(a) {\n"
+                       "  def myFunc2(b) {\n"
+                       "    func(a,w)\n"
+                       "  }\n"
+                       "  w = 21\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(15)", ec)
+       );
+
+       expected = { 15, 21};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_NO_THROW(
+               executeWithInfo(
+                       "def myFunc(a) {\n"
+                       "  w1 = 20\n"
+                       "  def myFunc2(b) {\n"
+                       "    w2 = 21\n"
+                       "    func(w1,w2)\n"
+                       "  }\n"
+                       "  myFunc2(a)\n"
+                       "}\n"
+                       "myFunc(15)", ec)
+       );
+       expected = { 20, 21};
+       ASSERT_EQ(val, expected);
+
+       ASSERT_THROW(
+               execute("def myFunc(a) { }\n"
+                               "myFunc(12,12)", ec, true)
+               , EvaluationFailure);
+
+
+       ASSERT_THROW(
+               execute("def myFunc(a) { }\n"
+                               "myFunc()", ec, true);
+               , EvaluationFailure);
+
+}
+
 int main(int argc, char *argv[])
 {
        try {