diff --git a/src/Library/KataScript.hpp b/src/Library/KataScript.hpp index 6286df2..8cad9b0 100644 --- a/src/Library/KataScript.hpp +++ b/src/Library/KataScript.hpp @@ -45,6 +45,7 @@ namespace KataScript { ScopeRef globalScope = make_shared(this); ScopeRef parseScope = globalScope; ExpressionRef currentExpression; + ExpressionRef previousExpression; ValueRef listIndexFunctionVarLocation; ValueRef identityFunctionVarLocation; @@ -52,9 +53,6 @@ namespace KataScript { vector parseStrings; int outerNestLayer = 0; bool lastStatementClosedScope = false; - bool lastStatementWasElse = false; - bool lastTokenEndCurlBraket = false; - bool lastStatementWasIf = false; uint64_t currentLine = 0; ParseState prevState = ParseState::beginExpression; ModulePrivilegeFlags allowedModulePrivileges; @@ -69,7 +67,6 @@ namespace KataScript { ValueRef getValue(ExpressionRef expr, ScopeRef scope, Class* classs); void clearParseStacks(); - void closeDanglingIfExpression(); void parse(string_view token); ScopeRef newClassScope(const string& name, ScopeRef scope); diff --git a/src/Library/expressionImplementation.hpp b/src/Library/expressionImplementation.hpp index 21d292d..d713359 100644 --- a/src/Library/expressionImplementation.hpp +++ b/src/Library/expressionImplementation.hpp @@ -192,6 +192,7 @@ namespace KataScript { break; } } + if (returnVal) { return make_shared(returnVal.value, returnVal.type == ReturnType::Return ? ExpressionType::Return : ExpressionType::Break); } else { @@ -216,33 +217,20 @@ namespace KataScript { return get(consolidated(exp, scope, classs)->expression); } - // since the 'else' block in an if/elfe is technically in a different scope - // ifelse espressions are not closed immediately and instead left dangling - // until the next expression is anything other than an 'else' or the else is unconditional - void KataScriptInterpreter::closeDanglingIfExpression() { - if (currentExpression && currentExpression->type == ExpressionType::IfElse) { + bool KataScriptInterpreter::closeCurrentExpression() { + previousExpression = currentExpression; + if (currentExpression) { if (currentExpression->parent) { currentExpression = currentExpression->parent; } else { - getValue(currentExpression, parseScope, nullptr); - currentExpression = nullptr; - } - } - } - - bool KataScriptInterpreter::closeCurrentExpression() { - if (currentExpression) { - if (currentExpression->type != ExpressionType::IfElse) { - if (currentExpression->parent) { - currentExpression = currentExpression->parent; - } else { - if (currentExpression->type != ExpressionType::FunctionDef) { - getValue(currentExpression, parseScope, nullptr); - } - currentExpression = nullptr; + if (currentExpression->type != ExpressionType::FunctionDef + && currentExpression->type != ExpressionType::IfElse + ) { + getValue(currentExpression, parseScope, nullptr); } + currentExpression = nullptr; return true; - } + } } return false; } diff --git a/src/Library/functionImplementation.hpp b/src/Library/functionImplementation.hpp index ad5c668..9b7ab88 100644 --- a/src/Library/functionImplementation.hpp +++ b/src/Library/functionImplementation.hpp @@ -208,10 +208,16 @@ namespace KataScript { while (scope) { auto iter = scope->scopes.find(name); if (iter != scope->scopes.end()) { + if (initialScope != iter->second) { + iter->second->parent = initialScope; + } return iter->second; } else { if (!scope->parent) { if (scope->name == name) { + if (initialScope != scope) { + scope->parent = initialScope; + } return scope; } } diff --git a/src/Library/modulesImplementation.hpp b/src/Library/modulesImplementation.hpp index 092e05e..9d870b5 100644 --- a/src/Library/modulesImplementation.hpp +++ b/src/Library/modulesImplementation.hpp @@ -674,11 +674,21 @@ namespace KataScript { } if (args[0]->getType() == Type::String) { return make_shared((Int)args[0]->getString().size()); - } + } else if (args[0]->getType() == Type::Array) { return make_shared((Int)args[0]->getArray().size()); + } else + if (args[0]->getType() == Type::List) { + return make_shared((Int)args[0]->getList().size()); + } else + if (args[0]->getType() == Type::List) { + return make_shared((Int)args[0]->getList().size()); + } else + if (args[0]->getType() == Type::Dictionary) { + return make_shared((Int)args[0]->getDictionary()->size()); } - return make_shared((Int)args[0]->getList().size()); + + return make_shared(); }}, {"find", [](const List& args) { @@ -754,11 +764,14 @@ namespace KataScript { }}, {"erase", [](const List& args) { - if (args.size() < 2 || (int)args[0]->getType() < (int)Type::Array || args[1]->getType() != Type::Int) { + if (args.size() < 2 || (int)args[0]->getType() < (int)Type::Array) { return make_shared(); } if (args[0]->getType() == Type::Array) { + if (args[1]->getType() != Type::Int) { + return make_shared(); + } switch (args[0]->getArray().getType()) { case Type::Int: args[0]->getStdVector().erase(args[0]->getStdVector().begin() + args[1]->getInt()); @@ -778,8 +791,13 @@ namespace KataScript { default: break; } - } else { + } else if (args[0]->getType() == Type::List) { + if (args[1]->getType() != Type::Int) { + return make_shared(); + } args[0]->getList().erase(args[0]->getList().begin() + args[1]->getInt()); + } else if (args[0]->getType() == Type::Dictionary) { + args[0]->getDictionary()->erase(args[1]->getHash()); } return make_shared(); }}, @@ -1118,12 +1136,15 @@ namespace KataScript { break; } return make_shared(Int(0)); - } - auto& list = args[0]->getList(); - for (size_t i = 0; i < list.size(); ++i) { - if (*list[i] == *args[1]) { - return make_shared(Int(1)); + } else if (args[0]->getType() == Type::List) { + auto& list = args[0]->getList(); + for (size_t i = 0; i < list.size(); ++i) { + if (*list[i] == *args[1]) { + return make_shared(Int(1)); + } } + } else if (args[0]->getType() == Type::Dictionary) { + return make_shared(Int(args[0]->getDictionary()->contains(args[1]->getHash()))); } return make_shared(Int(0)); }}, diff --git a/src/Library/parsing.hpp b/src/Library/parsing.hpp index df5215b..347e7ed 100644 --- a/src/Library/parsing.hpp +++ b/src/Library/parsing.hpp @@ -533,18 +533,12 @@ namespace KataScript { switch (parseState) { case ParseState::beginExpression: { - if (lastTokenEndCurlBraket && lastStatementWasIf) { - if (token != ";" && token != "}" && token != "else") { - lastStatementWasIf = false; - parse(";"); + if (lastStatementClosedScope && previousExpression) { + if (token != "else" && token != "if") { + getValue(previousExpression, parseScope, nullptr); } } - - bool wasElse = false; - bool closedScope = false; bool closedExpr = false; - bool isEndCurlBracket = false; - lastStatementWasIf = false; if (token == "fn" || token == "func" || token == "function") { parseState = ParseState::defineFunc; @@ -579,23 +573,20 @@ namespace KataScript { } } else if (token == "else") { parseState = ParseState::expectIfEnd; - wasElse = true; + currentExpression = previousExpression; } else if (token == "class") { parseState = ParseState::defineClass; } else if (token == "{") { parseScope = newScope("__anon"s, parseScope); clearParseStacks(); } else if (token == "}") { - wasElse = !currentExpression || currentExpression->type != ExpressionType::IfElse; - lastStatementWasIf = currentExpression && currentExpression->type == ExpressionType::IfElse; - bool wasFreefunc = !currentExpression || (currentExpression->type == ExpressionType::FunctionDef - && get(currentExpression->expression).function->getFunction()->type == FunctionType::free); closedExpr = closeCurrentExpression(); - if ((!closedExpr && wasFreefunc) || parseScope->name == "__anon") { + if ((!closedExpr) || parseScope->name == "__anon") { closeScope(parseScope); } - closedScope = true; - isEndCurlBracket = true; + if (previousExpression && previousExpression->type != ExpressionType::IfElse) { + closedExpr = false; + } } else if (token == "return") { parseState = ParseState::returnLine; } else if (token == "break") { @@ -608,18 +599,8 @@ namespace KataScript { parseState = ParseState::readLine; parseStrings.push_back(token); } - if (!closedExpr && (closedScope && lastStatementClosedScope || (!lastStatementWasElse && !wasElse && lastTokenEndCurlBraket))) { - bool wasIfExpr = currentExpression && currentExpression->type == ExpressionType::IfElse; - auto oldExpr = ¤tExpression; - closeDanglingIfExpression(); - if (closedScope && wasIfExpr && ¤tExpression == oldExpr) { - closeCurrentExpression(); - closedScope = false; - } - } - lastStatementClosedScope = closedScope; - lastTokenEndCurlBraket = isEndCurlBracket; - lastStatementWasElse = wasElse; + + lastStatementClosedScope = closedExpr; } break; case ParseState::loopCall: @@ -707,8 +688,10 @@ namespace KataScript { case ParseState::ifCall: if (token == ")") { if (--outerNestLayer <= 0) { - currentExpression->push_back(If()); - get(currentExpression->expression).back().testExpression = getExpression(move(parseStrings), parseScope, nullptr); + auto& expx = get(currentExpression->expression); + expx.push_back(If()); + expx.back().testExpression = getExpression(move(parseStrings), parseScope, nullptr); + clearParseStacks(); } else { parseStrings.push_back(token); diff --git a/src/Library/value.hpp b/src/Library/value.hpp index c2fc18c..4abd9f3 100644 --- a/src/Library/value.hpp +++ b/src/Library/value.hpp @@ -25,6 +25,7 @@ namespace KataScript { // Construct a Value from any underlying type explicit Value() : value(Null{}) {} explicit Value(bool a) :value(static_cast(a)) {} + explicit Value(int a) :value(static_cast(a)) {} explicit Value(Int a) : value(a) {} explicit Value(Float a) : value(a) {} explicit Value(vec3 a) : value(a) {} diff --git a/src/Tests/KataScriptTests.cpp b/src/Tests/KataScriptTests.cpp index 591cf17..f36f217 100644 --- a/src/Tests/KataScriptTests.cpp +++ b/src/Tests/KataScriptTests.cpp @@ -1927,6 +1927,64 @@ namespace KataScriptTests { Assert::AreEqual(KataScript::Int(8), val->getInt()); } + TEST_METHOD(NestedScopeClearing) { + interpreter.evaluate("var statsDict = dictionary();\ +var currPlayerIndex = 0;\ +class playerStats {\ + var name;\ + var holeNum;\ + var shots;\ + var totalShots;\ + var isComplete;\ + var index;\ +\ + fn playerStats(nm, hole, score) {\ + name = nm;\ + holeNum = hole;\ + shots = score;\ + totalShots = score;\ + isComplete = false;\ + index = currPlayerIndex;\ + ++currPlayerIndex;\ + if (currPlayerIndex > 3) {\ + currPlayerIndex = 0;\ + }\ + }\ +}\ +fn IsFreePlay() { return false; }\ +fn PlayerJoin(playerName, initialHole, initialScore) {\ + statsDict[playerName] = playerStats(playerName, initialHole, initialScore);\ + if (false) {\ + print();\ + }\ +\ + if (!IsFreePlay() && length(statsDict) >= 1) {\ + var furthest = 1;\ + var secondFurthest = 1;\ + foreach (stat; statsDict) {\ + var holeNum = stat.holeNum;\ + if (holeNum != null) {\ + if (holeNum > furthest) {\ + secondFurthest = furthest;\ + furthest = holeNum;\ + } else if (holeNum < furthest && holeNum > secondFurthest) {\ + secondFurthest = holeNum;\ + }\ + }\ + }\ + if (secondFurthest == 1) {\ + secondFurthest = furthest;\ + }\ + return secondFurthest;\ + }\ +return 0;\ +}\ +var asd = PlayerJoin(\"dd\", 23, 4);"); + auto val = interpreter.resolveVariable("asd"s); + Assert::AreEqual(KataScript::Type::Int, val->getType()); + Assert::AreEqual(KataScript::Int(23), val->getInt()); + } + TEST_METHOD(NestedEarlyReturn) { interpreter.evaluate("var dict = dictionary(); dict[1]=1;dict[2]=1;dict[3]=1;fn test(i) {\ var ready = true;\