From 69f896ca6960419133bf9a5ecc231f7aa934ac56 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Fri, 14 Nov 2025 18:23:38 +0800 Subject: Fixed issue #223. --- doc/docs/doc/README.md | 38 +++++++++++++++++ doc/docs/zh/doc/README.md | 38 +++++++++++++++++ spec/inputs/lists.yue | 10 +++++ spec/outputs/codes_from_doc.lua | 84 ++++++++++++++++++++++++++++++++++++++ spec/outputs/codes_from_doc_zh.lua | 84 ++++++++++++++++++++++++++++++++++++++ spec/outputs/lists.lua | 49 ++++++++++++++++++++++ src/yuescript/yue_ast.h | 2 +- src/yuescript/yue_compiler.cpp | 47 ++++++++++++++------- src/yuescript/yue_parser.cpp | 2 +- 9 files changed, 338 insertions(+), 16 deletions(-) diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index ad26be5..2a3047a 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md @@ -595,6 +595,23 @@ tab[] = "Value" +You can also use the spread operator `...` to append all elements from one list to another: + +```moonscript +tbA = [1, 2, 3] +tbB = [4, 5, 6] +tbA[] = ...tbB +-- tbA is now [1, 2, 3, 4, 5, 6] +``` + +
+tbA = [1, 2, 3]
+tbB = [4, 5, 6]
+tbA[] = ...tbB
+-- tbA is now [1, 2, 3, 4, 5, 6]
+
+
+ ### Table Spreading You can concatenate array tables or hash tables using spread operator `...` before expressions in table literals. @@ -2465,6 +2482,27 @@ doubled = [item * 2 for item in *items] +In list comprehensions, you can also use the spread operator `...` to flatten nested lists, achieving a flat map effect: + +```moonscript +data = + a: {1,2,3} + b: {4,5,6} + +flat = [...v for k,v in pairs data] +-- flat is now [1, 2, 3, 4, 5, 6] +``` + +
+data =
+  a: {1,2,3}
+  b: {4,5,6}
+
+flat = [...v for k,v in pairs data]
+-- flat is now [1, 2, 3, 4, 5, 6]
+
+
+ The for and when clauses can be chained as much as desired. The only requirement is that a comprehension has at least one for clause. Using multiple for clauses is the same as using nested loops: diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md index 86796e9..a799a5b 100755 --- a/doc/docs/zh/doc/README.md +++ b/doc/docs/zh/doc/README.md @@ -594,6 +594,23 @@ tab[] = "Value" +你还可以使用展开操作符 `...` 来将一个列表中的所有元素追加到另一个列表中: + +```moonscript +tbA = [1, 2, 3] +tbB = [4, 5, 6] +tbA[] = ...tbB +-- tbA 现在为 [1, 2, 3, 4, 5, 6] +``` + +
+tbA = [1, 2, 3]
+tbB = [4, 5, 6]
+tbA[] = ...tbB
+-- tbA 现在为 [1, 2, 3, 4, 5, 6]
+
+
+ ### 表扩展 你可以使用前置 `...` 操作符在Lua表中插入数组表或哈希表。 @@ -2425,6 +2442,27 @@ doubled = [item * 2 for item in *items] +在列表推导式中,你还可以使用展开操作符 `...` 来实现对列表嵌套层级进行扁平化的处理: + +```moonscript +data = + a: {1,2,3} + b: {4,5,6} + +flat = [...v for k,v in pairs data] +-- flat 现在为 [1, 2, 3, 4, 5, 6] +``` + +
+data =
+  a: {1,2,3}
+  b: {4,5,6}
+
+flat = [...v for k,v in pairs data]
+-- flat 现在为 [1, 2, 3, 4, 5, 6]
+
+
+ for和when子句可以根据需要进行链式操作。唯一的要求是推导式中至少要有一个for子句。 使用多个for子句与使用多重循环的效果相同: diff --git a/spec/inputs/lists.yue b/spec/inputs/lists.yue index f754cc1..dd951a5 100644 --- a/spec/inputs/lists.yue +++ b/spec/inputs/lists.yue @@ -267,4 +267,14 @@ do f = -> globalTB[#][#] = 1 f1 = -> globalTB[#][# - 1] +do + tbA[] = ...tbB + a, tb[], b[], c = 1, ...x, 3, 4 + + data = + a: {1,2,3} + b: {4,5,6} + + flat = [...v for k,v in pairs data] + nil diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua index 3971bb7..00b3b8b 100644 --- a/spec/outputs/codes_from_doc.lua +++ b/spec/outputs/codes_from_doc.lua @@ -147,6 +147,21 @@ print((function() end)()) local tab = { } tab[#tab + 1] = "Value" +local tbA = { + 1, + 2, + 3 +} +local tbB = { + 4, + 5, + 6 +} +local _len_0 = #tbA + 1 +for _index_0 = 1, #tbB do + local _elm_0 = tbB[_index_0] + tbA[_len_0], _len_0 = _elm_0, _len_0 + 1 +end local parts = { "shoulders", "knees" @@ -1080,6 +1095,33 @@ for _index_0 = 1, #_list_0 do _len_0 = _len_0 + 1 end doubled = _accum_0 +local data = { + a = { + 1, + 2, + 3 + }, + b = { + 4, + 5, + 6 + } +} +local flat +local _accum_0 = { } +local _len_0 = 1 +for k, v in pairs(data) do + do + local _obj_0 = _accum_0[_len_0] + local _len_1 = #_obj_0 + 1 + for _index_0 = 1, #v do + local _elm_0 = v[_index_0] + _obj_0[_len_1], _len_1 = _elm_0, _len_1 + 1 + end + end + _len_0 = _len_0 + 1 +end +flat = _accum_0 local x_coords = { 4, 5, @@ -2588,6 +2630,21 @@ print((function() end)()) local tab = { } tab[#tab + 1] = "Value" +local tbA = { + 1, + 2, + 3 +} +local tbB = { + 4, + 5, + 6 +} +local _len_0 = #tbA + 1 +for _index_0 = 1, #tbB do + local _elm_0 = tbB[_index_0] + tbA[_len_0], _len_0 = _elm_0, _len_0 + 1 +end local parts = { "shoulders", "knees" @@ -3551,6 +3608,33 @@ for _index_0 = 1, #_list_0 do _len_0 = _len_0 + 1 end doubled = _accum_0 +local data = { + a = { + 1, + 2, + 3 + }, + b = { + 4, + 5, + 6 + } +} +local flat +local _accum_0 = { } +local _len_0 = 1 +for k, v in pairs(data) do + do + local _obj_0 = _accum_0[_len_0] + local _len_1 = #_obj_0 + 1 + for _index_0 = 1, #v do + local _elm_0 = v[_index_0] + _obj_0[_len_1], _len_1 = _elm_0, _len_1 + 1 + end + end + _len_0 = _len_0 + 1 +end +flat = _accum_0 local x_coords = { 4, 5, diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua index 403ad3e..c382bca 100644 --- a/spec/outputs/codes_from_doc_zh.lua +++ b/spec/outputs/codes_from_doc_zh.lua @@ -147,6 +147,21 @@ print((function() end)()) local tab = { } tab[#tab + 1] = "Value" +local tbA = { + 1, + 2, + 3 +} +local tbB = { + 4, + 5, + 6 +} +local _len_0 = #tbA + 1 +for _index_0 = 1, #tbB do + local _elm_0 = tbB[_index_0] + tbA[_len_0], _len_0 = _elm_0, _len_0 + 1 +end local parts = { "shoulders", "knees" @@ -1074,6 +1089,33 @@ for _index_0 = 1, #_list_0 do _len_0 = _len_0 + 1 end doubled = _accum_0 +local data = { + a = { + 1, + 2, + 3 + }, + b = { + 4, + 5, + 6 + } +} +local flat +local _accum_0 = { } +local _len_0 = 1 +for k, v in pairs(data) do + do + local _obj_0 = _accum_0[_len_0] + local _len_1 = #_obj_0 + 1 + for _index_0 = 1, #v do + local _elm_0 = v[_index_0] + _obj_0[_len_1], _len_1 = _elm_0, _len_1 + 1 + end + end + _len_0 = _len_0 + 1 +end +flat = _accum_0 local x_coords = { 4, 5, @@ -2582,6 +2624,21 @@ print((function() end)()) local tab = { } tab[#tab + 1] = "Value" +local tbA = { + 1, + 2, + 3 +} +local tbB = { + 4, + 5, + 6 +} +local _len_0 = #tbA + 1 +for _index_0 = 1, #tbB do + local _elm_0 = tbB[_index_0] + tbA[_len_0], _len_0 = _elm_0, _len_0 + 1 +end local parts = { "shoulders", "knees" @@ -3539,6 +3596,33 @@ for _index_0 = 1, #_list_0 do _len_0 = _len_0 + 1 end doubled = _accum_0 +local data = { + a = { + 1, + 2, + 3 + }, + b = { + 4, + 5, + 6 + } +} +local flat +local _accum_0 = { } +local _len_0 = 1 +for k, v in pairs(data) do + do + local _obj_0 = _accum_0[_len_0] + local _len_1 = #_obj_0 + 1 + for _index_0 = 1, #v do + local _elm_0 = v[_index_0] + _obj_0[_len_1], _len_1 = _elm_0, _len_1 + 1 + end + end + _len_0 = _len_0 + 1 +end +flat = _accum_0 local x_coords = { 4, 5, diff --git a/spec/outputs/lists.lua b/spec/outputs/lists.lua index 2dd19e5..6115700 100644 --- a/spec/outputs/lists.lua +++ b/spec/outputs/lists.lua @@ -857,4 +857,53 @@ do end end end +do + do + local _obj_0 = tbA + local _len_0 = #_obj_0 + 1 + local _list_0 = tbB + for _index_0 = 1, #_list_0 do + local _elm_0 = _list_0[_index_0] + _obj_0[_len_0], _len_0 = _elm_0, _len_0 + 1 + end + end + a = 1 + do + local _obj_0 = tb + local _len_0 = #_obj_0 + 1 + for _index_0 = 1, #x do + local _elm_0 = x[_index_0] + _obj_0[_len_0], _len_0 = _elm_0, _len_0 + 1 + end + end + b[#b + 1] = 3 + c = 4 + local data = { + a = { + 1, + 2, + 3 + }, + b = { + 4, + 5, + 6 + } + } + local flat + local _accum_0 = { } + local _len_0 = 1 + for k, v in pairs(data) do + do + local _obj_0 = _accum_0[_len_0] + local _len_1 = #_obj_0 + 1 + for _index_0 = 1, #v do + local _elm_0 = v[_index_0] + _obj_0[_len_1], _len_1 = _elm_0, _len_1 + 1 + end + end + _len_0 = _len_0 + 1 + end + flat = _accum_0 +end return nil diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index b1a369b..0008fd6 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -443,7 +443,7 @@ AST_END(CompInner) AST_NODE(Assign) ast_ptr sep; - ast_sel_list values; + ast_sel_list values; AST_MEMBER(Assign, &sep, &values) AST_END(Assign) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index ed0a587..8c28e61 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.29.7"sv; +const std::string_view version = "0.29.8"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -2321,15 +2321,27 @@ private: transformAssignment(newAssignment, temp); varName = objVar; } - auto newAssignment = x->new_ptr(); - newAssignment->expList.set(toAst(varName + "[#"s + varName + "+1]"s, x)); - auto assign = x->new_ptr(); - if (vit == values.end()) { - throw CompileError("right value missing"sv, values.front()); + if (auto spread = ast_cast(*vit)) { + auto lenVar = getUnusedName("_len_"sv); + forceAddToScope(lenVar); + temp.push_back(indent() + "local "s + lenVar + " = #"s + varName + " + 1"s + nll(spread)); + auto elmVar = getUnusedName("_elm_"sv); + _buf << varName << '[' << lenVar << "],"s << lenVar << "="s << elmVar << ',' << lenVar << "+1 for "s << elmVar << " in *nil"s; + auto stmt = toAst(clearBuf(), spread); + auto comp = stmt->appendix->item.to(); + ast_to(comp->items.front())->loopValue.to()->value.set(spread->exp); + transformStatement(stmt, temp); + } else { + auto newAssignment = x->new_ptr(); + newAssignment->expList.set(toAst(varName + "[#"s + varName + "+1]"s, x)); + auto assign = x->new_ptr(); + if (vit == values.end()) { + throw CompileError("right value missing"sv, values.front()); + } + assign->values.push_back(*vit); + newAssignment->action.set(assign); + transformAssignment(newAssignment, temp); } - assign->values.push_back(*vit); - newAssignment->action.set(assign); - transformAssignment(newAssignment, temp); if (extraScoped) { popScope(); temp.push_back(indent() + "end"s + nlr(x)); @@ -2848,6 +2860,7 @@ private: case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; case id(): transformTableBlock(static_cast(value), out); break; case id(): transformExp(static_cast(value), out, ExpUsage::Closure); break; + case id(): throw CompileError("can only be used for ranged table append assignments"sv, value); break; default: YUEE("AST node mismatch", value); break; } } @@ -8268,9 +8281,16 @@ private: } return; } - auto def = ast_cast(comp->items.front()); - if (!def || def->defVal) { - throw CompileError("invalid comprehension expression", comp->items.front()); + ast_node* value = nullptr; + bool isSpread = ast_is(comp->items.front()); + if (isSpread) { + value = comp->items.front(); + } else { + auto def = ast_cast(comp->items.front()); + if (!def || def->defVal) { + throw CompileError("invalid comprehension expression", comp->items.front()); + } + value = def->item.get(); } bool extraScope = false; switch (usage) { @@ -8294,7 +8314,6 @@ private: default: break; } - auto value = def->item.get(); auto compInner = static_cast(comp->items.back()); str_list temp; std::string accumVar = getUnusedName("_accum_"sv); @@ -8318,7 +8337,7 @@ private: } } { - auto assignLeft = toAst(accumVar + '[' + lenVar + ']', x); + auto assignLeft = toAst(accumVar + '[' + lenVar + (isSpread ? "][]"s : "]"s), x); auto assign = x->new_ptr(); assign->values.push_back(value); auto assignment = x->new_ptr(); diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 44baced..fccb6fb 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -547,7 +547,7 @@ YueParser::YueParser() { Assign = '=' >> space >> Seperator >> ( With | If | Switch | TableBlock | - Exp >> *(space >> set(",;") >> space >> Exp) + (SpreadListExp | Exp) >> *(space >> set(",;") >> space >> (SpreadListExp | Exp)) ); UpdateOp = -- cgit v1.2.3-55-g6feb