}
}
+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) :
*/
std::string consumeIdentifier()
{
- if (checkTokenType(TokenType::IDENTIFIER)) return {};
+ if (!checkTokenType(TokenType::IDENTIFIER)) return {};
auto ident = tokens[index].text();
++index;
return std::move(ident);
}
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
*
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 {};
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;
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 {
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) };
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 {