From 663a8c622db13b3a9dad962071ff6f3cfb482de3 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 11 Feb 2026 16:58:21 +0800 Subject: Supported multiple break values. Simplified some generated codes. --- spec/outputs/5.1/loops.lua | 36 ++- spec/outputs/codes_from_doc_de.lua | 8 +- spec/outputs/codes_from_doc_en.lua | 8 +- spec/outputs/codes_from_doc_id-id.lua | 8 +- spec/outputs/codes_from_doc_pt-br.lua | 8 +- spec/outputs/codes_from_doc_zh.lua | 8 +- spec/outputs/loops.lua | 36 ++- spec/outputs/test/config_spec.lua | 12 +- spec/outputs/test/do_statement_spec.lua | 4 +- spec/outputs/test/with_statement_spec.lua | 98 +++---- spec/outputs/with.lua | 50 ++-- src/yuescript/yue_ast.cpp | 4 +- src/yuescript/yue_ast.h | 6 +- src/yuescript/yue_compiler.cpp | 413 +++++++++++++++++++++--------- src/yuescript/yue_parser.cpp | 2 +- 15 files changed, 406 insertions(+), 295 deletions(-) diff --git a/spec/outputs/5.1/loops.lua b/spec/outputs/5.1/loops.lua index e4f2871..019cd60 100644 --- a/spec/outputs/5.1/loops.lua +++ b/spec/outputs/5.1/loops.lua @@ -499,25 +499,21 @@ local _anon_func_0 = function(i, tb) end do local index - do - local _accum_0 - for i = 1, #tb do - if tb[i] then - _accum_0 = i - break - end + for i = 1, #tb do + if tb[i] then + index = i + break end - index = _accum_0 end f((function() - local _accum_0 + local _val_0 for i = 1, #tb do if tb[i] then - _accum_0 = i + _val_0 = i break end end - return _accum_0 + return _val_0 end)()) f((function() local _accum_0 = { } @@ -545,13 +541,13 @@ do i = 1 local idx do - local _accum_0 + local _val_0 while tb[i] do i = i + 1 - _accum_0 = i - 1 + _val_0 = i - 1 break end - idx = _accum_0 + idx = _val_0 end local f1 f1 = function() @@ -560,13 +556,13 @@ do end i = 1 f((function() - local _accum_0 + local _val_0 while tb[i] do i = i + 1 - _accum_0 = i - 1 + _val_0 = i - 1 break end - return _accum_0 + return _val_0 end)()) local _accum_0 = { } local _len_0 = 1 @@ -592,13 +588,13 @@ do print(1) until true do - local _accum_0 + local _val_0 repeat a = func() - _accum_0 = a.x + _val_0 = a.x break until a.v - x = _accum_0 + x = _val_0 end local items local _accum_0 = { } diff --git a/spec/outputs/codes_from_doc_de.lua b/spec/outputs/codes_from_doc_de.lua index 44fc28a..7de1a35 100644 --- a/spec/outputs/codes_from_doc_de.lua +++ b/spec/outputs/codes_from_doc_de.lua @@ -3260,16 +3260,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3324,16 +3322,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do diff --git a/spec/outputs/codes_from_doc_en.lua b/spec/outputs/codes_from_doc_en.lua index 5320961..cd83bbf 100644 --- a/spec/outputs/codes_from_doc_en.lua +++ b/spec/outputs/codes_from_doc_en.lua @@ -3260,16 +3260,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3324,16 +3322,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do diff --git a/spec/outputs/codes_from_doc_id-id.lua b/spec/outputs/codes_from_doc_id-id.lua index e952434..b024989 100644 --- a/spec/outputs/codes_from_doc_id-id.lua +++ b/spec/outputs/codes_from_doc_id-id.lua @@ -3260,16 +3260,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3324,16 +3322,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do diff --git a/spec/outputs/codes_from_doc_pt-br.lua b/spec/outputs/codes_from_doc_pt-br.lua index 0c9affb..f5643c8 100644 --- a/spec/outputs/codes_from_doc_pt-br.lua +++ b/spec/outputs/codes_from_doc_pt-br.lua @@ -3260,16 +3260,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3324,16 +3322,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua index fa985e2..087782e 100644 --- a/spec/outputs/codes_from_doc_zh.lua +++ b/spec/outputs/codes_from_doc_zh.lua @@ -3260,16 +3260,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3324,16 +3322,14 @@ for i = 1, 20 do end doubled_evens = _accum_0 local first_large -local _accum_0 local _list_0 = numbers for _index_0 = 1, #_list_0 do local n = _list_0[_index_0] if n > 10 then - _accum_0 = n + first_large = n break end end -first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do diff --git a/spec/outputs/loops.lua b/spec/outputs/loops.lua index 6ab4bbb..4fb4187 100644 --- a/spec/outputs/loops.lua +++ b/spec/outputs/loops.lua @@ -380,25 +380,21 @@ local _anon_func_0 = function(i, tb) end do local index - do - local _accum_0 - for i = 1, #tb do - if tb[i] then - _accum_0 = i - break - end + for i = 1, #tb do + if tb[i] then + index = i + break end - index = _accum_0 end f((function() - local _accum_0 + local _val_0 for i = 1, #tb do if tb[i] then - _accum_0 = i + _val_0 = i break end end - return _accum_0 + return _val_0 end)()) f((function() local _accum_0 = { } @@ -426,13 +422,13 @@ do i = 1 local idx do - local _accum_0 + local _val_0 while tb[i] do i = i + 1 - _accum_0 = i - 1 + _val_0 = i - 1 break end - idx = _accum_0 + idx = _val_0 end local f1 f1 = function() @@ -441,13 +437,13 @@ do end i = 1 f((function() - local _accum_0 + local _val_0 while tb[i] do i = i + 1 - _accum_0 = i - 1 + _val_0 = i - 1 break end - return _accum_0 + return _val_0 end)()) local _accum_0 = { } local _len_0 = 1 @@ -473,13 +469,13 @@ do print(1) until true do - local _accum_0 + local _val_0 repeat a = func() - _accum_0 = a.x + _val_0 = a.x break until a.v - x = _accum_0 + x = _val_0 end local items local _accum_0 = { } diff --git a/spec/outputs/test/config_spec.lua b/spec/outputs/test/config_spec.lua index 410bacc..dc98760 100644 --- a/spec/outputs/test/config_spec.lua +++ b/spec/outputs/test/config_spec.lua @@ -114,14 +114,10 @@ return describe("config", function() y = 20 } local result - do - local _accum_0 - repeat - _accum_0 = obj.x + obj.y - break - until true - result = _accum_0 - end + repeat + result = obj.x + obj.y + break + until true return assert.same(result, 30) end) it("should handle existential operators", function() diff --git a/spec/outputs/test/do_statement_spec.lua b/spec/outputs/test/do_statement_spec.lua index fb93fa0..7c491a0 100644 --- a/spec/outputs/test/do_statement_spec.lua +++ b/spec/outputs/test/do_statement_spec.lua @@ -104,12 +104,10 @@ return describe("do statement", function() } local result do - local _accum_0 repeat - _accum_0 = obj:double() + result = obj:double() break until true - result = _accum_0 end return assert.same(result, 20) end) diff --git a/spec/outputs/test/with_statement_spec.lua b/spec/outputs/test/with_statement_spec.lua index dcd2aa1..e6f64e8 100644 --- a/spec/outputs/test/with_statement_spec.lua +++ b/spec/outputs/test/with_statement_spec.lua @@ -53,14 +53,10 @@ return describe("with statement", function() value = 5 } local result - do - local _accum_0 - repeat - _accum_0 = obj.value * 2 - break - until true - result = _accum_0 - end + repeat + result = obj.value * 2 + break + until true return assert.same(result, 10) end) it("should support multiple statements", function() @@ -125,27 +121,19 @@ return describe("with statement", function() y = 10 } local result - do - local _accum_0 - repeat - _accum_0 = obj.x + obj.y - break - until true - result = _accum_0 - end + repeat + result = obj.x + obj.y + break + until true return assert.same(result, 15) end) it("should work with string methods", function() local s = "hello" local result - do - local _accum_0 - repeat - _accum_0 = s:upper() - break - until true - result = _accum_0 - end + repeat + result = s:upper() + break + until true return assert.same(result, "HELLO") end) it("should handle metatable access", function() @@ -169,12 +157,10 @@ return describe("with statement", function() x = 10 } local _val_0 - local _accum_0 repeat - _accum_0 = obj.x * 2 + _val_0 = obj.x * 2 break until true - _val_0 = _accum_0 return _val_0 end local result = fn() @@ -187,12 +173,10 @@ return describe("with statement", function() value = 42 } local _val_0 - local _accum_0 repeat - _accum_0 = obj.value + _val_0 = obj.value break until true - _val_0 = _accum_0 return _val_0 end return assert.same(get_value(), 42) @@ -202,19 +186,15 @@ return describe("with statement", function() value = 10 } local result - do - local _accum_0 - repeat - local _exp_0 = obj.value - if _exp_0 ~= nil then - _accum_0 = _exp_0 - else - _accum_0 = 0 - end - break - until true - result = _accum_0 - end + repeat + local _exp_0 = obj.value + if _exp_0 ~= nil then + result = _exp_0 + else + result = 0 + end + break + until true return assert.same(result, 10) end) it("should handle nil object safely", function() @@ -222,10 +202,10 @@ return describe("with statement", function() do local _with_0 = nil do - local _accum_0 + local _accum_0 = { } repeat if _with_0 ~= nil then - _accum_0 = _with_0.value + result = _with_0.value break end until true @@ -245,16 +225,12 @@ return describe("with statement", function() end } local result - do - local _accum_0 - repeat - obj:add(10) - obj:add(5) - _accum_0 = obj:get() - break - until true - result = _accum_0 - end + repeat + obj:add(10) + obj:add(5) + result = obj:get() + break + until true return assert.same(result, 20) end) return it("should support nested property access", function() @@ -266,14 +242,10 @@ return describe("with statement", function() } } local result - do - local _accum_0 - repeat - _accum_0 = obj.level1.level2.level3 - break - until true - result = _accum_0 - end + repeat + result = obj.level1.level2.level3 + break + until true return assert.same(result, "deep") end) end) diff --git a/spec/outputs/with.lua b/spec/outputs/with.lua index 3cd8aab..ce0e495 100644 --- a/spec/outputs/with.lua +++ b/spec/outputs/with.lua @@ -191,62 +191,52 @@ do f((function() local _with_0 = item local _val_0 - do - local _accum_0 - repeat - if _with_0.id > 0 then - _accum_0 = _with_0.content - break - end - until true - _val_0 = _accum_0 - end + repeat + if _with_0.id > 0 then + _val_0 = _with_0.content + break + end + until true return _val_0 end)()) local a do local _with_0 = tb - do - local _accum_0 - repeat - if _with_0.v then - _accum_0 = _with_0.a - break - end - until true - a = _accum_0 - end + repeat + if _with_0.v then + a = _with_0.a + break + end + until true end - local _accum_0 + local _val_0 while true do local _with_0 = tb - local _accum_1 + local _accum_0 = { } repeat if _with_0 ~= nil then - _accum_1 = 1 + _val_0 = 1 break end until true - _accum_0 = _accum_1 + _val_0 = _accum_0 break end - a = _accum_0 + a = _val_0 end do local a - local _accum_0 for i = 1, 100 do local x = tb[i] if x ~= nil then local _des_0 = 1 if _des_0 then x.id = _des_0 - _accum_0 = x + a = x break end end end - a = _accum_0 end do local tb = { @@ -254,11 +244,9 @@ do y = 2 } local a - local _accum_0 repeat - _accum_0 = tb.x + tb.y + a = tb.x + tb.y break until true - a = _accum_0 end return nil diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index 2fccd35..9c398c8 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp @@ -201,8 +201,8 @@ std::string Continue_t::to_string(void*) const { return "continue"s; } std::string BreakLoop_t::to_string(void* ud) const { - if (value) { - return type->to_string(ud) + ' ' + value->to_string(ud); + if (valueList) { + return type->to_string(ud) + ' ' + valueList->to_string(ud); } return type->to_string(ud); } diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 98f9014..91800b0 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -914,9 +914,9 @@ AST_END(Continue) AST_NODE(BreakLoop) ast_sel type; - ast_ptr value; - AST_MEMBER(BreakLoop, &type, &value) - std::string varBWV; + ast_ptr valueList; + AST_MEMBER(BreakLoop, &type, &valueList) + std::list vars; AST_END(BreakLoop) AST_NODE(PipeBody) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index eb31ab5..bd52d33 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -78,7 +78,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.32.9"sv; +const std::string_view version = "0.33.0"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -1996,6 +1996,18 @@ private: } } + str_list getAssignVars(ExpList_t* assignList) { + str_list vars; + bool lintGlobal = _config.lintGlobalVariable; + _config.lintGlobalVariable = false; + for (auto exp : assignList->exprs.objects()) { + auto var = singleVariableFrom(exp, AccessType::None); + vars.push_back(var.empty() ? Empty : var); + } + _config.lintGlobalVariable = lintGlobal; + return vars; + } + str_list getAssignVars(ExpListAssign_t* assignment) { str_list vars; bool lintGlobal = _config.lintGlobalVariable; @@ -2211,9 +2223,9 @@ private: if (exprs.size() < values.size()) { auto num = exprs.size(); if (num > 1) { - _buf << "no more than "sv << num << " right values expected, got "sv << values.size(); + _buf << "expected no more than "sv << num << " right values, got "sv << values.size(); } else { - _buf << "only one right value expected, got "sv << values.size(); + _buf << "expected only one right value, got "sv << values.size(); } throw CompileError(clearBuf(), values.front()); } @@ -2234,6 +2246,9 @@ private: case id(): case id(): case id(): + case id(): + case id(): + case id(): needHoldValues = true; break; } @@ -2245,7 +2260,7 @@ private: } } if (!needHoldValues) { - _buf << exprs.size() << " right values expected, got "sv << values.size(); + _buf << "expected "sv << exprs.size() << " right values, got "sv << values.size(); throw CompileError(clearBuf(), values.front()); } auto newAssign = assign->new_ptr(); @@ -8911,7 +8926,7 @@ private: return (breakLoopType & int(BreakLoopType::Continue)) != 0; } - uint32_t getBreakLoopType(ast_node* body, const std::string& varBWV) { + uint32_t getBreakLoopType(ast_node* body, bool allowBreakWithValues, str_list& breakWithValues) { uint32_t type = 0; body->traverse([&](ast_node* node) { if (auto stmt = ast_cast(node)) { @@ -8920,12 +8935,25 @@ private: type |= int(BreakLoopType::Continue); return traversal::Return; } else { - if (breakLoop->value) { - if (varBWV.empty()) { - throw CompileError("break with a value is not allowed here"sv, breakLoop->value); + if (breakLoop->valueList && breakLoop->vars.empty()) { + if (!allowBreakWithValues) { + throw CompileError("break with values is not allowed here"sv, breakLoop->valueList->exprs.front()); } type |= int(BreakLoopType::BreakWithValue); - breakLoop->varBWV = varBWV; + if (breakWithValues.empty()) { + pushScope(); + for (size_t i = 0; i < breakLoop->valueList->exprs.size(); i++) { + auto var = getUnusedName("_val_"sv); + breakWithValues.push_back(var); + addToScope(var); + } + popScope(); + } else { + if (breakLoop->valueList->exprs.size() != breakWithValues.size()) { + throw CompileError("expecting "s + std::to_string(breakWithValues.size()) + " break values, got "s + std::to_string(breakLoop->valueList->exprs.size()), breakLoop->valueList->exprs.front()); + } + } + breakLoop->vars = breakWithValues; } else { type |= int(BreakLoopType::Break); } @@ -8940,28 +8968,28 @@ private: switch (sVal->get_id()) { case id(): { auto withNode = static_cast(sVal); - type |= getBreakLoopType(withNode->body, varBWV); + type |= getBreakLoopType(withNode->body, allowBreakWithValues, breakWithValues); return traversal::Return; } case id(): { auto doNode = static_cast(sVal); - type |= getBreakLoopType(doNode->body, varBWV); + type |= getBreakLoopType(doNode->body, allowBreakWithValues, breakWithValues); return traversal::Return; } case id(): { auto ifNode = static_cast(sVal); for (auto n : ifNode->nodes.objects()) { - type |= getBreakLoopType(n, varBWV); + type |= getBreakLoopType(n, allowBreakWithValues, breakWithValues); } return traversal::Return; } case id(): { auto switchNode = static_cast(sVal); for (auto branch : switchNode->branches.objects()) { - type |= getBreakLoopType(static_cast(branch)->body, varBWV); + type |= getBreakLoopType(static_cast(branch)->body, allowBreakWithValues, breakWithValues); } if (switchNode->lastBranch) { - type |= getBreakLoopType(switchNode->lastBranch, varBWV); + type |= getBreakLoopType(switchNode->lastBranch, allowBreakWithValues, breakWithValues); } return traversal::Return; } @@ -8980,6 +9008,17 @@ private: return type; } + uint32_t getBreakLoopTypeNoValues(ast_node* body) { + str_list values; + return getBreakLoopType(body, false, values); + } + + std::pair getBreakLoopTypeAllowValues(ast_node* body) { + str_list values; + auto breakLoopType = getBreakLoopType(body, true, values); + return {breakLoopType, std::move(values)}; + } + void addDoToLastLineReturn(ast_node* body) { if (auto block = ast_cast(body); block && !block->statementOrComments.empty()) { auto last = lastStatementFrom(block); @@ -9066,7 +9105,7 @@ private: str_list temp; bool extraDo = false; auto body = repeatNode->body.get(); - auto breakLoopType = getBreakLoopType(body, Empty); + auto breakLoopType = getBreakLoopTypeNoValues(body); bool withContinue = hasContinue(breakLoopType); std::string conditionVar; std::string extraLabel; @@ -9141,28 +9180,42 @@ private: void transformForNum(ForNum_t* forNum, str_list& out) { str_list temp; transformForNumHead(forNum, temp); - auto breakLoopType = getBreakLoopType(forNum->body, Empty); + auto breakLoopType = getBreakLoopTypeNoValues(forNum->body); transformLoopBody(forNum->body, temp, breakLoopType, ExpUsage::Common); popScope(); out.push_back(join(temp) + indent() + "end"s + nl(forNum)); } - std::string transformForNumInner(ForNum_t* forNum, str_list& out) { + str_list transformForNumInner(ForNum_t* forNum, str_list& out, uint32_t breakLoopType, str_list&& vars) { auto x = forNum; - std::string accum = getUnusedName("_accum_"sv); - addToScope(accum); - std::string len = getUnusedName("_len_"sv); - addToScope(len); - auto breakLoopType = getBreakLoopType(forNum->body, accum); - _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nl(forNum); + std::string accum; + if (hasBreakWithValue(breakLoopType)) { + str_list vs; + for (const auto& var : vars) { + if (!isDefined(var)) { + addToScope(var); + vs.push_back(var); + } + } + if (!vs.empty()) { + _buf << indent() << "local "sv << join(vs, ", "sv) << nl(forNum); + } + } else { + accum = getUnusedName("_accum_"sv); + addToScope(accum); + vars = {accum}; + _buf << indent() << "local "sv << accum << " = { }"sv << nl(forNum); + } out.emplace_back(clearBuf()); - _buf << indent() << "local "sv << len << " = 1"sv << nl(forNum); - auto& lenAssign = out.emplace_back(clearBuf()); - transformForNumHead(forNum, out); if (hasBreakWithValue(breakLoopType)) { - lenAssign.clear(); + transformForNumHead(forNum, out); transformLoopBody(forNum->body, out, breakLoopType, ExpUsage::Common); } else { + std::string len = getUnusedName("_len_"sv); + addToScope(len); + _buf << indent() << "local "sv << len << " = 1"sv << nl(forNum); + auto& lenAssign = out.emplace_back(clearBuf()); + transformForNumHead(forNum, out); auto expList = toAst(accum + '[' + len + ']', x); auto followStmt = toAst(len + "+=1"s, forNum->body); expList->followStmt = followStmt.get(); @@ -9173,7 +9226,7 @@ private: } popScope(); out.push_back(indent() + "end"s + nl(forNum)); - return accum; + return vars; } void transformForNumClosure(ForNum_t* forNum, str_list& out) { @@ -9189,8 +9242,10 @@ private: pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - auto accum = transformForNumInner(forNum, temp); - temp.push_back(indent() + "return "s + accum + nl(forNum)); + str_list vars; + auto breakLoopType = getBreakLoopType(forNum->body, true, vars); + vars = transformForNumInner(forNum, temp, breakLoopType, std::move(vars)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(forNum)); popScope(); funcStart = anonFuncStart() + nl(forNum); temp.push_back(indent() + anonFuncEnd()); @@ -9200,29 +9255,45 @@ private: } void transformForNumInPlace(ForNum_t* forNum, str_list& out, ExpList_t* assignExpList) { - auto x = forNum; str_list temp; - bool isScoped = !currentScope().lastStatement; if (assignExpList) { + auto vars = getAssignVars(assignExpList); + bool extraVar = false; + for (const auto& var : vars) { + if (var.empty()) { + vars.clear(); + extraVar = true; + break; + } + } + auto breakLoopType = getBreakLoopType(forNum->body, true, vars); + bool isScoped = true; + if (currentScope().lastStatement) { + isScoped = false; + } else if (!extraVar && hasBreakWithValue(breakLoopType)) { + isScoped = false; + } if (isScoped) { _buf << indent() << "do"sv << nl(forNum); pushScope(); } - auto accum = transformForNumInner(forNum, temp); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accum, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignExpList); - assignment->action.set(assign); - transformAssignment(assignment, temp); + vars = transformForNumInner(forNum, temp, breakLoopType, std::move(vars)); + if (extraVar || !hasBreakWithValue(breakLoopType)) { + auto assign = toAst('=' + join(vars, ","sv), assignExpList); + auto assignment = assignExpList->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } if (isScoped) { popScope(); temp.push_back(indent() + "end"s + nl(forNum)); } } else { - auto accum = transformForNumInner(forNum, temp); - auto returnNode = newReturn(toAst(accum, forNum)); - transformReturn(returnNode, temp); + str_list vars; + auto breakLoopType = getBreakLoopType(forNum->body, true, vars); + vars = transformForNumInner(forNum, temp, breakLoopType, std::move(vars)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(forNum)); } out.push_back(join(temp)); } @@ -9248,7 +9319,7 @@ private: void transformForEach(ForEach_t* forEach, str_list& out) { str_list temp; bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false); - auto breakLoopType = getBreakLoopType(forEach->body, Empty); + auto breakLoopType = getBreakLoopTypeNoValues(forEach->body); transformLoopBody(forEach->body, temp, breakLoopType, ExpUsage::Common); popScope(); out.push_back(temp.front() + temp.back() + indent() + "end"s + nl(forEach)); @@ -9258,22 +9329,36 @@ private: } } - std::string transformForEachInner(ForEach_t* forEach, str_list& out) { + str_list transformForEachInner(ForEach_t* forEach, str_list& out, uint32_t breakLoopType, str_list&& vars) { auto x = forEach; - std::string accum = getUnusedName("_accum_"sv); - addToScope(accum); - std::string len = getUnusedName("_len_"sv); - addToScope(len); - auto breakLoopType = getBreakLoopType(forEach->body, accum); - _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nl(forEach); + std::string accum; + if (hasBreakWithValue(breakLoopType)) { + str_list vs; + for (const auto& var : vars) { + if (!isDefined(var)) { + addToScope(var); + vs.push_back(var); + } + } + if (!vs.empty()) { + _buf << indent() << "local "sv << join(vs, ", "sv) << nl(forEach); + } + } else { + accum = getUnusedName("_accum_"sv); + addToScope(accum); + vars = {accum}; + _buf << indent() << "local "sv << accum << " = { }"sv << nl(forEach); + } out.emplace_back(clearBuf()); - _buf << indent() << "local "sv << len << " = 1"sv << nl(forEach); - auto& lenAssign = out.emplace_back(clearBuf()); - transformForEachHead(forEach->nameList, forEach->loopValue, out, true); if (hasBreakWithValue(breakLoopType)) { - lenAssign.clear(); + transformForEachHead(forEach->nameList, forEach->loopValue, out, true); transformLoopBody(forEach->body, out, breakLoopType, ExpUsage::Common); } else { + std::string len = getUnusedName("_len_"sv); + addToScope(len); + _buf << indent() << "local "sv << len << " = 1"sv << nl(forEach); + auto& lenAssign = out.emplace_back(clearBuf()); + transformForEachHead(forEach->nameList, forEach->loopValue, out, true); auto expList = toAst(accum + '[' + len + ']', x); auto followStmt = toAst(len + "+=1"s, forEach->body); expList->followStmt = followStmt.get(); @@ -9284,7 +9369,7 @@ private: } popScope(); out.push_back(indent() + "end"s + nl(forEach)); - return accum; + return vars; } void transformForEachClosure(ForEach_t* forEach, str_list& out) { @@ -9300,8 +9385,10 @@ private: pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - auto accum = transformForEachInner(forEach, temp); - temp.push_back(indent() + "return "s + accum + nl(forEach)); + str_list vars; + auto breakLoopType = getBreakLoopType(forEach->body, true, vars); + vars = transformForEachInner(forEach, temp, breakLoopType, std::move(vars)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(forEach)); popScope(); funcStart = anonFuncStart() + nl(forEach); temp.push_back(indent() + anonFuncEnd()); @@ -9311,29 +9398,45 @@ private: } void transformForEachInPlace(ForEach_t* forEach, str_list& out, ExpList_t* assignExpList) { - auto x = forEach; str_list temp; - bool isScoped = !currentScope().lastStatement; if (assignExpList) { + auto vars = getAssignVars(assignExpList); + bool extraVar = false; + for (const auto& var : vars) { + if (var.empty()) { + vars.clear(); + extraVar = true; + break; + } + } + auto breakLoopType = getBreakLoopType(forEach->body, true, vars); + bool isScoped = true; + if (currentScope().lastStatement) { + isScoped = false; + } else if (!extraVar && hasBreakWithValue(breakLoopType)) { + isScoped = false; + } if (isScoped) { _buf << indent() << "do"sv << nl(forEach); pushScope(); } - auto accum = transformForEachInner(forEach, temp); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accum, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignExpList); - assignment->action.set(assign); - transformAssignment(assignment, temp); + vars = transformForEachInner(forEach, temp, breakLoopType, std::move(vars)); + if (extraVar || !hasBreakWithValue(breakLoopType)) { + auto assign = toAst('=' + join(vars, ","sv), assignExpList); + auto assignment = assignExpList->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } if (isScoped) { popScope(); temp.push_back(indent() + "end"s + nl(forEach)); } } else { - auto accum = transformForEachInner(forEach, temp); - auto returnNode = newReturn(toAst(accum, forEach)); - transformReturn(returnNode, temp); + str_list vars; + auto breakLoopType = getBreakLoopType(forEach->body, true, vars); + vars = transformForEachInner(forEach, temp, breakLoopType, std::move(vars)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(forEach)); } out.push_back(join(temp)); } @@ -10274,7 +10377,8 @@ private: breakWithVar = getUnusedName("_val_"sv); extraBreakVar = true; } - auto breakLoopType = getBreakLoopType(with->body, breakWithVar); + str_list vars{breakWithVar}; + auto breakLoopType = getBreakLoopType(with->body, true, vars); if (!hasBreakWithValue(breakLoopType)) { breakWithVar.clear(); } @@ -10308,6 +10412,10 @@ private: } else { bool transformed = false; if (!breakWithVar.empty()) { + if (extraBreakVar) { + addToScope(breakWithVar); + temp.push_back(indent() + "local "s + breakWithVar + nl(with)); + } auto repeatNode = toAst("repeat\n\t--\nuntil true"s, x); auto block = x->new_ptr(); if (auto blk = with->body.as()) { @@ -10317,10 +10425,7 @@ private: block->statementOrComments.push_back(stmt); } repeatNode->body.set(block); - auto sVal = x->new_ptr(); - sVal->value.set(repeatNode); - auto asmt = assignmentFrom(toAst(breakWithVar, x), newExp(sVal, x), x); - transformAssignment(asmt, temp); + transformRepeat(repeatNode, temp); transformed = true; } else if (!extraScope && assignList) { if (auto block = with->body.as()) { @@ -10784,7 +10889,57 @@ private: temp.push_back(indent() + "do"s + nl(doNode)); pushScope(); } - transformBody(doNode->body, temp, usage, assignList); + str_list breakVars; + bool extraVar = false; + switch (usage) { + case ExpUsage::Closure: + case ExpUsage::Return: { + uint32_t breakLoopType = 0; + std::tie(breakLoopType, breakVars) = getBreakLoopTypeAllowValues(doNode->body); + if (hasBreakWithValue(breakLoopType)) { + for (const auto& var : breakVars) addToScope(var); + temp.push_back(indent() + "local "s + join(breakVars, ", "sv) + nl(doNode)); + extraVar = true; + } + break; + } + case ExpUsage::Assignment: { + auto vars = getAssignVars(assignList); + for (const auto& var : vars) { + if (var.empty()) { + vars.clear(); + extraVar = true; + break; + } + } + auto breakLoopType = getBreakLoopType(doNode->body, true, vars); + if (hasBreakWithValue(breakLoopType)) { + breakVars = std::move(vars); + } + break; + } + case ExpUsage::Common: { + getBreakLoopTypeNoValues(doNode->body); + break; + } + } + if (breakVars.empty()) { + transformBody(doNode->body, temp, usage, assignList); + } else { + auto x = doNode; + auto repeatNode = toAst("repeat\n\t--\nuntil true"s, x); + repeatNode->body.set(doNode->body->content); + transformRepeat(repeatNode, temp); + if (assignList && extraVar) { + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = toAst('=' + join(breakVars, ","sv), x); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else if (usage == ExpUsage::Closure || usage == ExpUsage::Return) { + temp.push_back(indent() + "return "s + join(breakVars, ", "sv) + nl(doNode)); + } + } if (usage == ExpUsage::Closure) { popScope(); *funcStart = anonFuncStart() + nl(doNode); @@ -11438,13 +11593,20 @@ private: pushScope(); } } - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); + std::string accumVar; + auto [breakLoopType, vars] = getBreakLoopTypeAllowValues(whileNode->body); + if (hasBreakWithValue(breakLoopType)) { + for (const auto& var : vars) addToScope(var); + _buf << indent() << "local "sv << join(vars, ", "sv) << nl(whileNode); + } else { + accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + vars.push_back(accumVar); + _buf << indent() << "local "sv << accumVar << " = { }"sv << nl(whileNode); + } + temp.emplace_back(clearBuf()); auto lenVar = getUnusedName("_len_"sv); addToScope(lenVar); - auto breakLoopType = getBreakLoopType(whileNode->body, accumVar); - _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nl(whileNode); - temp.emplace_back(clearBuf()); _buf << indent() << "local "s << lenVar << " = 1"s << nl(whileNode); auto& lenAssign = temp.emplace_back(clearBuf()); bool isUntil = _parser.toString(whileNode->type) == "until"sv; @@ -11466,15 +11628,14 @@ private: popScope(); temp.push_back(indent() + "end"s + nl(whileNode)); if (expList) { - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accumVar, x)); - auto assignment = x->new_ptr(); + auto assign = toAst('=' + join(vars, ","sv), expList); + auto assignment = expList->new_ptr(); assignment->expList.set(expList); assignment->action.set(assign); transformAssignment(assignment, temp); if (extraScope) popScope(); } else { - temp.push_back(indent() + "return "s + accumVar + nl(whileNode)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(whileNode)); } if (expList && extraScope) { temp.push_back(indent() + "end"s + nl(whileNode)); @@ -11494,13 +11655,20 @@ private: pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); + std::string accumVar; + auto [breakLoopType, vars] = getBreakLoopTypeAllowValues(whileNode->body); + if (hasBreakWithValue(breakLoopType)) { + for (const auto& var : vars) addToScope(var); + _buf << indent() << "local "sv << join(vars, ", "sv) << nl(whileNode); + } else { + accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + vars.push_back(accumVar); + _buf << indent() << "local "sv << accumVar << " = { }"sv << nl(whileNode); + } + temp.emplace_back(clearBuf()); auto lenVar = getUnusedName("_len_"sv); addToScope(lenVar); - auto breakLoopType = getBreakLoopType(whileNode->body, accumVar); - _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nl(whileNode); - temp.emplace_back(clearBuf()); auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nl(whileNode)); bool isUntil = _parser.toString(whileNode->type) == "until"sv; auto condStr = transformCondExp(whileNode->condition, isUntil); @@ -11520,7 +11688,7 @@ private: } popScope(); temp.push_back(indent() + "end"s + nl(whileNode)); - temp.push_back(indent() + "return "s + accumVar + nl(whileNode)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(whileNode)); popScope(); funcStart = anonFuncStart() + nl(whileNode); temp.push_back(indent() + anonFuncEnd()); @@ -11560,7 +11728,7 @@ private: pushScope(); bool isUntil = _parser.toString(whileNode->type) == "until"sv; auto condStr = transformCondExp(whileNode->condition, isUntil); - auto breakLoopType = getBreakLoopType(whileNode->body, Empty); + auto breakLoopType = getBreakLoopTypeNoValues(whileNode->body); transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common); popScope(); _buf << indent() << "while "sv << condStr << " do"sv << nl(whileNode); @@ -11580,13 +11748,20 @@ private: pushScope(); } } - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); + std::string accumVar; + auto [breakLoopType, vars] = getBreakLoopTypeAllowValues(repeatNode->body); + if (hasBreakWithValue(breakLoopType)) { + for (const auto& var : vars) addToScope(var); + _buf << indent() << "local "sv << join(vars, ", "sv) << nl(repeatNode); + } else { + accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + vars.push_back(accumVar); + _buf << indent() << "local "sv << accumVar << " = { }"sv << nl(repeatNode); + } + temp.emplace_back(clearBuf()); auto lenVar = getUnusedName("_len_"sv); addToScope(lenVar); - auto breakLoopType = getBreakLoopType(repeatNode->body, accumVar); - _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nl(repeatNode); - temp.emplace_back(clearBuf()); _buf << indent() << "local "s << lenVar << " = 1"s << nl(repeatNode); auto& lenAssign = temp.emplace_back(clearBuf()); auto condStr = transformCondExp(repeatNode->condition, false); @@ -11607,15 +11782,14 @@ private: popScope(); temp.push_back(indent() + "until "s + condStr + nl(repeatNode)); if (expList) { - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accumVar, x)); - auto assignment = x->new_ptr(); + auto assign = toAst('=' + join(vars, ","sv), expList); + auto assignment = expList->new_ptr(); assignment->expList.set(expList); assignment->action.set(assign); transformAssignment(assignment, temp); if (extraScope) popScope(); } else { - temp.push_back(indent() + "return "s + accumVar + nl(repeatNode)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(repeatNode)); } if (expList && extraScope) { temp.push_back(indent() + "end"s + nl(repeatNode)); @@ -11635,13 +11809,20 @@ private: pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); + std::string accumVar; + auto [breakLoopType, vars] = getBreakLoopTypeAllowValues(repeatNode->body); + if (hasBreakWithValue(breakLoopType)) { + for (const auto& var : vars) addToScope(var); + _buf << indent() << "local "sv << join(vars, ", "sv) << nl(repeatNode); + } else { + accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + vars.push_back(accumVar); + _buf << indent() << "local "sv << accumVar << " = { }"sv << nl(repeatNode); + } + temp.emplace_back(clearBuf()); auto lenVar = getUnusedName("_len_"sv); addToScope(lenVar); - auto breakLoopType = getBreakLoopType(repeatNode->body, accumVar); - _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nl(repeatNode); - temp.emplace_back(clearBuf()); auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nl(repeatNode)); auto condStr = transformCondExp(repeatNode->condition, false); temp.push_back(indent() + "repeat"s + nl(repeatNode)); @@ -11660,7 +11841,7 @@ private: } popScope(); temp.push_back(indent() + "until "s + condStr + nl(repeatNode)); - temp.push_back(indent() + "return "s + accumVar + nl(repeatNode)); + temp.push_back(indent() + "return "s + join(vars, ", "sv) + nl(repeatNode)); popScope(); funcStart = anonFuncStart() + nl(repeatNode); temp.push_back(indent() + anonFuncEnd()); @@ -11975,9 +12156,9 @@ private: if (x->leftList.size() < x->assign->values.size()) { auto num = x->leftList.size(); if (num > 1) { - _buf << "no more than "sv << num << " right values expected, got "sv << x->assign->values.size(); + _buf << "expected no more than "sv << num << " right values, got "sv << x->assign->values.size(); } else { - _buf << "only one right value expected, got "sv << x->assign->values.size(); + _buf << "expected only one right value, got "sv << x->assign->values.size(); } throw CompileError(clearBuf(), x->assign->values.front()); } @@ -12044,7 +12225,7 @@ private: auto value = singleValueFrom(assignA->values.back()); if (!value) { if (listA->names.size() > assignA->values.size()) { - _buf << listA->names.size() << " right values expected, got "sv << assignA->values.size(); + _buf << "expected "sv << listA->names.size() << " right values, got "sv << assignA->values.size(); throw CompileError(clearBuf(), assignA->values.front()); } else { break; @@ -12067,7 +12248,7 @@ private: if (listA->names.size() > assignA->values.size()) { auto chainValue = value->item.as(); if (!chainValue || !ast_is(chainValue->items.back())) { - _buf << listA->names.size() << " right values expected, got "sv << assignA->values.size(); + _buf << "expected "sv << listA->names.size() << " right values, got "sv << assignA->values.size(); throw CompileError(clearBuf(), assignA->values.front()); } } @@ -12223,9 +12404,13 @@ private: throw CompileError(keyword + " is not inside a loop"s, breakLoop); } if (isBreak) { - if (breakLoop->value) { - auto exp = toAst(breakLoop->varBWV, breakLoop->value); - auto assignment = assignmentFrom(exp, breakLoop->value, breakLoop); + if (breakLoop->valueList) { + auto expList = toAst(join(breakLoop->vars, ","sv), breakLoop); + auto assignment = breakLoop->new_ptr(); + assignment->expList.set(expList); + auto assign = breakLoop->new_ptr(); + assign->values.dup(breakLoop->valueList->exprs); + assignment->action.set(assign); transformAssignment(assignment, out); } out.push_back(indent() + keyword + nl(breakLoop)); @@ -12293,7 +12478,7 @@ private: auto x = chainAssign; auto value = chainAssign->assign->values.front(); if (chainAssign->assign->values.size() != 1) { - throw CompileError("only one right value expected"sv, value); + throw CompileError("expected only one right value"sv, value); } str_list temp; bool constVal = false; diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 3d8e315..b52a452 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -476,7 +476,7 @@ YueParser::YueParser() { Break = key("break"); Continue = key("continue"); - BreakLoop = (Break >> -(space >> Exp) | Continue) >> not_alpha_num; + BreakLoop = (Break >> -(space >> ExpList) | Continue) >> not_alpha_num; Return = key("return") >> -(space >> (TableBlock | ExpList)); -- cgit v1.2.3-55-g6feb