Skip to content

Commit

Permalink
Merge pull request #14 from brwhale/feature/EarlyReturn
Browse files Browse the repository at this point in the history
Feature/early return
  • Loading branch information
brwhale authored May 1, 2023
2 parents 119eb4e + e7e673c commit 02242ef
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 25 deletions.
6 changes: 5 additions & 1 deletion VSCodeExtension/katascriptlang/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@

## [0.0.80]

- adds function and fn keywords
- adds function and fn keywords

## [0.0.90]

- adds break keyword
2 changes: 1 addition & 1 deletion VSCodeExtension/katascriptlang/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Kata Script Language Tools",
"description": "Syntax Highlighting for KataScript",
"publisher": "GarrettSkelton",
"version": "0.0.80",
"version": "0.0.90",
"engines": {
"vscode": "^1.51.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"keywords": {
"patterns": [{
"name": "keyword.control.katascript",
"match": "\\b(var|if|else|while|for|foreach|fn|func|function|return|class|import)\\b"
"match": "\\b(var|if|else|while|for|foreach|fn|func|function|return|break|class|import)\\b"
}]
},
"constants": {
Expand Down
5 changes: 3 additions & 2 deletions src/Library/KataScript.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace KataScript {
classArgs,
funcArgs,
returnLine,
breakLine,
ifCall,
expectIfEnd,
loopCall,
Expand Down Expand Up @@ -57,8 +58,8 @@ namespace KataScript {
ParseState prevState = ParseState::beginExpression;
ModulePrivilegeFlags allowedModulePrivileges;

ValueRef needsToReturn(ExpressionRef expr, ScopeRef scope, Class* classs);
ValueRef needsToReturn(const vector<ExpressionRef>& subexpressions, ScopeRef scope, Class* classs);
ReturnResult needsToReturn(ExpressionRef expr, ScopeRef scope, Class* classs);
ReturnResult needsToReturn(const vector<ExpressionRef>& subexpressions, ScopeRef scope, Class* classs);
ExpressionRef consolidated(ExpressionRef exp, ScopeRef scope, Class* classs);

ExpressionRef getResolveVarExpression(const string& name, bool classScope);
Expand Down
35 changes: 19 additions & 16 deletions src/Library/expressionImplementation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@
#pragma once

namespace KataScript {
ValueRef KataScriptInterpreter::needsToReturn(ExpressionRef exp, ScopeRef scope, Class* classs) {
if (exp->type == ExpressionType::Return) {
return getValue(exp, scope, classs);
ReturnResult KataScriptInterpreter::needsToReturn(ExpressionRef exp, ScopeRef scope, Class* classs) {
if (exp->type == ExpressionType::Return || exp->type == ExpressionType::Break) {
return ReturnResult{ getValue(exp, scope, classs), exp->type == ExpressionType::Return ? ReturnType::Return : ReturnType::Break };
} else {
auto result = consolidated(exp, scope, classs);
if (result->type == ExpressionType::Return) {
return get<ValueRef>(result->expression);
if (result->type == ExpressionType::Return || result->type == ExpressionType::Break) {
return ReturnResult{ get<ValueRef>(result->expression), result->type == ExpressionType::Return ? ReturnType::Return : ReturnType::Break };
}
}
return nullptr;
return ReturnResult{ nullptr, ReturnType::None };
}

ValueRef KataScriptInterpreter::needsToReturn(const vector<ExpressionRef>& subexpressions, ScopeRef scope, Class* classs) {
ReturnResult KataScriptInterpreter::needsToReturn(const vector<ExpressionRef>& subexpressions, ScopeRef scope, Class* classs) {
for (auto&& sub : subexpressions) {
if (auto returnVal = needsToReturn(sub, scope, classs)) {
return returnVal;
}
}
return nullptr;
return ReturnResult{ nullptr, ReturnType::None };
}

// walk the tree depth first and replace any function expressions
Expand Down Expand Up @@ -64,6 +64,9 @@ namespace KataScript {
case ExpressionType::Return:
return make_shared<Expression>(getValue(get<Return>(exp->expression).expression, scope, classs), ExpressionType::Value);
break;
case ExpressionType::Break:
return make_shared<Expression>(make_shared<Value>(), ExpressionType::Value);
break;
case ExpressionType::FunctionCall:
{
List args;
Expand All @@ -86,16 +89,16 @@ namespace KataScript {
if (loopexp.initExpression) {
getValue(loopexp.initExpression, scope, classs);
}
ValueRef returnVal = nullptr;
while (returnVal == nullptr && getValue(loopexp.testExpression, scope, classs)->getBool()) {
ReturnResult returnVal;
while (returnVal.value == nullptr && getValue(loopexp.testExpression, scope, classs)->getBool()) {
returnVal = needsToReturn(loopexp.subexpressions, scope, classs);
if (returnVal == nullptr && loopexp.iterateExpression) {
if (returnVal.value == nullptr && loopexp.iterateExpression) {
getValue(loopexp.iterateExpression, scope, classs);
}
}
closeScope(scope);
if (returnVal) {
return make_shared<Expression>(returnVal, ExpressionType::Return);
return make_shared<Expression>(returnVal.value, returnVal.type == ReturnType::Return ? ExpressionType::Return : ExpressionType::Break);
} else {
return make_shared<Expression>(make_shared<Value>(), ExpressionType::Value);
}
Expand All @@ -107,7 +110,7 @@ namespace KataScript {
auto varr = resolveVariable(get<Foreach>(exp->expression).iterateName, scope);
auto list = getValue(get<Foreach>(exp->expression).listExpression, scope, classs);
auto& subs = get<Foreach>(exp->expression).subexpressions;
ValueRef returnVal = nullptr;
ReturnResult returnVal;
if (list->getType() == Type::Dictionary) {
for (auto&& in : *list->getDictionary().get()) {
*varr = *in.second;
Expand Down Expand Up @@ -159,15 +162,15 @@ namespace KataScript {
}
closeScope(scope);
if (returnVal) {
return make_shared<Expression>(returnVal, ExpressionType::Return);
return make_shared<Expression>(returnVal.value, returnVal.type == ReturnType::Return ? ExpressionType::Return : ExpressionType::Break);
} else {
return make_shared<Expression>(make_shared<Value>(), ExpressionType::Value);
}
}
break;
case ExpressionType::IfElse:
{
ValueRef returnVal = nullptr;
ReturnResult returnVal;
for (auto& express : get<IfElse>(exp->expression)) {
if (!express.testExpression || getValue(express.testExpression, scope, classs)->getBool()) {
scope = newScope("ifelse", scope);
Expand All @@ -177,7 +180,7 @@ namespace KataScript {
}
}
if (returnVal) {
return make_shared<Expression>(returnVal, ExpressionType::Return);
return make_shared<Expression>(returnVal.value, returnVal.type == ReturnType::Return ? ExpressionType::Return : ExpressionType::Break);
} else {
return make_shared<Expression>(make_shared<Value>(), ExpressionType::Value);
}
Expand Down
35 changes: 31 additions & 4 deletions src/Library/expressions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ namespace KataScript {
Return() {}
};

struct Break {
ExpressionRef expression;

Break(const Break& o) {
expression = o.expression ? make_shared<Expression>(*o.expression) : nullptr;
}
Break(ExpressionRef e) : expression(e) {}
Break() {}
};

struct If {
ExpressionRef testExpression;
vector<ExpressionRef> subexpressions;
Expand Down Expand Up @@ -143,15 +153,29 @@ namespace KataScript {
DefineVar(const string& n, ExpressionRef defExpr) : name(n), defineExpression(defExpr) {}
};

enum class ExpressionType : uint8_t {
enum class ReturnType : uint8_t {
None,
Break,
Return
};

struct ReturnResult {
ValueRef value = nullptr;
ReturnType type = ReturnType::None;

operator bool() { return value != nullptr; }
};

enum class ExpressionType : uint8_t {
Value,
ResolveVar,
DefineVar,
FunctionDef,
FunctionDef,
FunctionCall,
MemberFunctionCall,
MemberVariable,
Return,
Return,
Break,
Loop,
ForEach,
IfElse
Expand All @@ -165,7 +189,8 @@ namespace KataScript {
FunctionExpression,
MemberFunctionCall,
MemberVariable,
Return,
Return,
Break,
Loop,
Foreach,
IfElse
Expand Down Expand Up @@ -199,6 +224,8 @@ namespace KataScript {
: type(ExpressionType::IfElse), expression(val), parent(par) {}
Expression(Return val, ExpressionRef par = nullptr)
: type(ExpressionType::Return), expression(val), parent(par) {}
Expression(Break val, ExpressionRef par = nullptr)
: type(ExpressionType::Break), expression(val), parent(par) {}
Expression(ResolveVar val, ExpressionRef par = nullptr)
: type(ExpressionType::ResolveVar), expression(val), parent(par) {}
Expression(DefineVar val, ExpressionRef par = nullptr)
Expand Down
14 changes: 14 additions & 0 deletions src/Library/parsing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,8 @@ namespace KataScript {
isEndCurlBracket = true;
} else if (token == "return") {
parseState = ParseState::returnLine;
} else if (token == "break") {
parseState = ParseState::breakLine;
} else if (token == "import") {
parseState = ParseState::importModule;
} else if (token == ";") {
Expand Down Expand Up @@ -724,13 +726,25 @@ namespace KataScript {
case ParseState::returnLine:
if (token == ";") {
if (currentExpression) {
if (parseStrings.empty()) {
parseStrings.push_back("null");
}
currentExpression->push_back(make_shared<Expression>(Return(getExpression(move(parseStrings), parseScope, nullptr))));
}
clearParseStacks();
} else {
parseStrings.push_back(token);
}
break;

case ParseState::breakLine:
if (token == ";") {
if (currentExpression) {
currentExpression->push_back(make_shared<Expression>(Break(getExpression({"null"}, parseScope, nullptr))));
}
clearParseStacks();
}
break;
case ParseState::expectIfEnd:
if (token == "if") {
clearParseStacks();
Expand Down
32 changes: 32 additions & 0 deletions src/Tests/KataScriptTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,30 @@ namespace KataScriptTests {
Assert::AreEqual(KataScript::Int(10), value->getInt());
}

TEST_METHOD(ReturnInLoop) {
interpreter.evaluate("fn t() { var a = 0; for(i=0;i<=4;i++){a+=i; if (a >= 6){return a;}}} var x = t();"s);
auto value = interpreter.resolveVariable("x"s);

Assert::AreEqual(KataScript::Type::Int, value->getType());
Assert::AreEqual(KataScript::Int(6), value->getInt());
}

TEST_METHOD(BreakInFunc) {
interpreter.evaluate("fn t() { var a = 0; for(i=0;i<=4;i++){a+=i; if (a >= 6){break;}} return 2; } var x = t();"s);
auto value = interpreter.resolveVariable("x"s);

Assert::AreEqual(KataScript::Type::Int, value->getType());
Assert::AreEqual(KataScript::Int(2), value->getInt());
}

TEST_METHOD(LoopFor3StateBreak) {
interpreter.evaluate("a = 0; for(i=0;i<=4;i++){a+=i; if (a >= 6){break;}}"s);
auto value = interpreter.resolveVariable("a"s);

Assert::AreEqual(KataScript::Type::Int, value->getType());
Assert::AreEqual(KataScript::Int(6), value->getInt());
}

TEST_METHOD(LoopFor3State) {
interpreter.evaluate("a = 0; for(i=0;i<=4;i++){a+=i;}"s);
auto value = interpreter.resolveVariable("a"s);
Expand Down Expand Up @@ -1877,6 +1901,14 @@ namespace KataScriptTests {
Assert::AreEqual(KataScript::Type::Int, val->getType());
Assert::AreEqual(KataScript::Int(1), val->getInt());
}

TEST_METHOD(ConditionalOfAdditionClassMember) {
interpreter.evaluate("class b {var x; fn b(){x=4;}} var i = b(); var limit = 17; if (i.x + 1 >= limit) { i.x = 1; } else { i.x = 2;} var j = i.x;"s);

auto val = interpreter.resolveVariable("j"s);
Assert::AreEqual(KataScript::Type::Int, val->getType());
Assert::AreEqual(KataScript::Int(2), val->getInt());
}

// todo add more tests

Expand Down

0 comments on commit 02242ef

Please sign in to comment.