From 8d648dc8c075fc1cf8d89d74c1425cdeb47e8089 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Sun, 29 Jan 2023 16:39:35 +0800 Subject: refactor break loop keyword checking. --- spec/inputs/loops.yue | 24 +++++++++++- spec/outputs/5.1/loops.lua | 60 +++++++++++++++++++++++++++++ spec/outputs/loops.lua | 41 ++++++++++++++++++++ src/yuescript/yue_compiler.cpp | 86 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 198 insertions(+), 13 deletions(-) diff --git a/spec/inputs/loops.yue b/spec/inputs/loops.yue index 51eb10b..5cadbf0 100644 --- a/spec/inputs/loops.yue +++ b/spec/inputs/loops.yue @@ -173,4 +173,26 @@ do for thing in *xxx print thing - +do + for i = 1, 10 + repeat + with? tb + .a = 1 + continue if .b + do + if .c + break + until true + switch x + when 123 + break + else + continue + if y + continue + else + break + do do do + print i + continue + print "abc" diff --git a/spec/outputs/5.1/loops.lua b/spec/outputs/5.1/loops.lua index 85e9de8..6f0d072 100644 --- a/spec/outputs/5.1/loops.lua +++ b/spec/outputs/5.1/loops.lua @@ -382,3 +382,63 @@ do print(thing) end end +do + for i = 1, 10 do + local _continue_0 = false + repeat + repeat + local _cond_0 = false + local _continue_1 = false + repeat + do + local _with_0 = tb + if _with_0 ~= nil then + _with_0.a = 1 + if _with_0.b then + _cond_0 = true + _continue_1 = true + break + end + do + if _with_0.c then + break + end + end + end + end + _cond_0 = true + _continue_1 = true + until true + if not _continue_1 then + break + end + until _cond_0 + if 123 == x then + break + else + _continue_0 = true + break + end + if y then + _continue_0 = true + break + else + break + end + do + do + do + print(i) + _continue_0 = true + break + end + end + end + print("abc") + _continue_0 = true + until true + if not _continue_0 then + break + end + end +end diff --git a/spec/outputs/loops.lua b/spec/outputs/loops.lua index eeea15f..79ef789 100644 --- a/spec/outputs/loops.lua +++ b/spec/outputs/loops.lua @@ -294,3 +294,44 @@ do print(thing) end end +do + for i = 1, 10 do + repeat + do + local _with_0 = tb + if _with_0 ~= nil then + _with_0.a = 1 + if _with_0.b then + goto _continue_12 + end + do + if _with_0.c then + break + end + end + end + end + ::_continue_12:: + until true + if 123 == x then + break + else + goto _continue_11 + end + if y then + goto _continue_11 + else + break + end + do + do + do + print(i) + goto _continue_11 + end + end + end + print("abc") + ::_continue_11:: + end +end diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index c7f0f27..83933d1 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -71,7 +71,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.15.24"sv; +const std::string_view version = "0.15.25"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -296,6 +296,7 @@ private: }; std::stack _varArgs; std::stack _enableReturn; + std::stack _enableBreakLoop; std::stack _withVars; struct ContinueVar { std::string var; @@ -1072,12 +1073,14 @@ private: void pushFunctionScope() { _enableReturn.push(true); + _enableBreakLoop.push(false); _gotoScopes.push(_gotoScope); _gotoScope++; } void popFunctionScope() { _enableReturn.pop(); + _enableBreakLoop.pop(); _gotoScopes.pop(); } @@ -1224,8 +1227,16 @@ private: } default: YUEE("AST node mismatch", appendix->item.get()); break; } - } else if (statement->content.is() && !statement->appendix->item.is()) { - throw std::logic_error(_info.errorMessage("loop line decorator can not be used in a return statement"sv, statement->appendix->item)); + } else if (!statement->appendix->item.is()) { + auto appendix = statement->appendix->item.get(); + switch (statement->content->getId()) { + case id(): + throw std::logic_error(_info.errorMessage("loop line decorator can not be used in a return statement"sv, appendix)); + break; + case id(): + throw std::logic_error(_info.errorMessage("loop line decorator can not be used in a break-loop statement"sv, appendix)); + break; + } } auto appendix = statement->appendix.get(); switch (appendix->item->getId()) { @@ -6099,18 +6110,62 @@ private: if (auto stmt = ast_cast(node)) { if (stmt->content.is()) { return _parser.toString(stmt->content) == "continue"sv ? traversal::Stop : traversal::Return; - } else if (expListFrom(stmt)) { - return traversal::Continue; + } else if (auto expList = expListFrom(stmt)) { + BLOCK_START + auto value = singleValueFrom(expList); + BREAK_IF(!value); + auto simpleValue = value->item.as(); + BREAK_IF(!simpleValue); + auto sVal = simpleValue->value.get(); + switch (sVal->getId()) { + case id(): { + auto withNode = static_cast(sVal); + if (hasContinueStatement(withNode->body)) { + return traversal::Stop; + } + break; + } + case id(): { + auto doNode = static_cast(sVal); + if (hasContinueStatement(doNode->body)) { + return traversal::Stop; + } + break; + } + case id(): { + auto ifNode = static_cast(sVal); + for (auto n : ifNode->nodes.objects()) { + if (hasContinueStatement(n)) { + return traversal::Stop; + } + } + break; + } + case id(): { + auto switchNode = static_cast(sVal); + for (auto branch : switchNode->branches.objects()) { + if (hasContinueStatement(static_cast(branch)->body)) { + return traversal::Stop; + } + } + if (switchNode->lastBranch) { + if (hasContinueStatement(switchNode->lastBranch)) { + return traversal::Stop; + } + } + break; + } + } + BLOCK_END } - return traversal::Return; - } else + } else { switch (node->getId()) { - case id(): - case id(): - case id(): - return traversal::Return; + case id(): + case id(): + return traversal::Continue; } - return traversal::Continue; + } + return traversal::Return; }); } @@ -6171,7 +6226,9 @@ private: } addDoToLastLineReturn(body); } + _enableBreakLoop.push(true); transform_plain_body(body, temp, usage, assignList); + _enableBreakLoop.pop(); if (withContinue) { if (target < 502) { if (extraDo) { @@ -6250,7 +6307,9 @@ private: } addDoToLastLineReturn(body); } + _enableBreakLoop.push(true); transform_plain_body(body, temp, ExpUsage::Common); + _enableBreakLoop.pop(); if (withContinue) { if (target < 502) { transformAssignment(_continueVars.top().condAssign, temp); @@ -8203,6 +8262,9 @@ private: void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { auto keyword = _parser.toString(breakLoop); + if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { + throw std::logic_error(_info.errorMessage(keyword + " is not inside a loop"s, breakLoop)); + } if (keyword == "break"sv) { out.push_back(indent() + keyword + nll(breakLoop)); return; -- cgit v1.2.3-55-g6feb