From 0603800a4114ed8b4c9572a7d7852995c9b9f334 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 21 May 2025 11:44:54 +0800 Subject: Added break with value syntax. --- doc/docs/doc/README.md | 27 ++++++ doc/docs/zh/doc/README.md | 27 +++++- spec/inputs/loops.yue | 39 ++++++++ spec/outputs/5.1/loops.lua | 104 ++++++++++++++++++++- spec/outputs/codes_from_doc.lua | 60 +++++++++++- spec/outputs/codes_from_doc_zh.lua | 60 +++++++++++- spec/outputs/loops.lua | 104 ++++++++++++++++++++- spec/outputs/macro.lua | 2 - spec/outputs/unicode/loops.lua | 4 +- spec/outputs/unicode/macro.lua | 2 - src/yuescript/yue_ast.cpp | 12 ++- src/yuescript/yue_ast.h | 14 ++- src/yuescript/yue_compiler.cpp | 183 ++++++++++++++++++++++++------------- src/yuescript/yue_parser.cpp | 4 +- src/yuescript/yue_parser.h | 2 + 15 files changed, 561 insertions(+), 83 deletions(-) diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index c275c52..dac96ee 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md @@ -29,6 +29,16 @@ inventory = * name: "bread" count: 3 +-- list comprehension +map = (arr, action) -> + [action item for item in *arr] + +filter = (arr, cond) -> + [item for item in *arr when cond item] + +reduce = (arr, init, action): init -> + init = action init, item for item in *arr + -- pipe operator [1, 2, 3] |> map (x) -> x * 2 @@ -2324,6 +2334,23 @@ doubled_evens = for i = 1, 20 +In addition, for loops support break with a return value, allowing the loop itself to be used as an expression that exits early with a meaningful result. + +For example, to find the first number greater than 10: + +```moonscript +first_large = for n in *numbers + break n if n > 10 +``` + +
+first_large = for n in *numbers
+  break n if n > 10
+
+
+ +This break-with-value syntax enables concise and expressive search or early-exit patterns directly within loop expressions. + You can also filter values by combining the for loop expression with the continue statement. For loops at the end of a function body are not accumulated into a table for a return value (Instead the function will return nil). Either an explicit return statement can be used, or the loop can be converted into a list comprehension. diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md index 11dc108..850afed 100755 --- a/doc/docs/zh/doc/README.md +++ b/doc/docs/zh/doc/README.md @@ -29,6 +29,16 @@ inventory = * name: "bread" count: 3 +-- 列表推导 +map = (arr, action) -> + [action item for item in *arr] + +filter = (arr, cond) -> + [item for item in *arr when cond item] + +reduce = (arr, init, action): init -> + init = action init, item for item in *arr + -- 管道操作符 [1, 2, 3] |> map (x) -> x * 2 @@ -2286,9 +2296,24 @@ doubled_evens = for i = 1, 20 +此外,for循环还支持带返回值的break语句,这样循环本身就可以作为一个表达式,在满足条件时提前退出并返回有意义的结果。 + +例如,查找第一个大于10的数字: + +```moonscript +first_large = for n in *numbers + break n if n > 10 +``` + +
+first_large = for n in *numbers
+  break n if n > 10
+
+
+ 你还可以结合for循环表达式与continue语句来过滤值。 -注意出现在函数体末尾的for循环,不会被当作是一个表达式,并将循环结果累积到一个列表中作为返回值(相反,函数将返回nil)。如果要函数末尾的循环转换为列表表达式,可以使用返回语句加for循环表达式。 +注意出现在函数体末尾的for循环,不会被当作是一个表达式并将循环结果累积到一个列表中作为返回值(相反,函数将返回nil)。如果要函数末尾的循环转换为列表表达式,可以显式地使用返回语句加for循环表达式。 ```moonscript func_a = -> for i = 1, 10 do print i diff --git a/spec/inputs/loops.yue b/spec/inputs/loops.yue index c5b28b3..9a91b42 100644 --- a/spec/inputs/loops.yue +++ b/spec/inputs/loops.yue @@ -213,3 +213,42 @@ do do until x := func 'a', b do print "false expected" + +do + index = for i = 1, #tb + break i if tb[i] + + f for i = 1, #tb + break i if tb[i] + + f for i = 1, #tb + i if tb[i] + + i = 1 + ids = while tb[i] + i += 1 + i - 1 + + i = 1 + idx = while tb[i] + i += 1 + break i - 1 + + f1 = -> + i = 1 + f while tb[i] + i += 1 + i - 1 + + i = 1 + f while tb[i] + i += 1 + break i - 1 + + list = for item in *items + switch item + when type: "A", :value + if value > 5 + item + + diff --git a/spec/outputs/5.1/loops.lua b/spec/outputs/5.1/loops.lua index 57b19be..bc720f6 100644 --- a/spec/outputs/5.1/loops.lua +++ b/spec/outputs/5.1/loops.lua @@ -60,8 +60,8 @@ do local y = hello[_index_0] if y % 2 == 0 then _accum_0[_len_0] = y + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 end x = _accum_0 end @@ -132,13 +132,11 @@ do end do local _accum_0 = { } - local _len_0 = 1 local _list_2 = 3 for _index_0 = 1, #_list_2 do local thing = _list_2[_index_0] y = "hello" break - _len_0 = _len_0 + 1 end x = _accum_0 end @@ -489,3 +487,103 @@ do end until false end +local _anon_func_0 = function(i, tb) + local _accum_0 = { } + local _len_0 = 1 + while tb[i] do + i = i + 1 + _accum_0[_len_0] = i - 1 + _len_0 = _len_0 + 1 + end + return _accum_0 +end +do + local index + do + local _accum_0 + for i = 1, #tb do + if tb[i] then + _accum_0 = i + break + end + end + index = _accum_0 + end + f((function() + local _accum_0 + for i = 1, #tb do + if tb[i] then + _accum_0 = i + break + end + end + return _accum_0 + end)()) + f((function() + local _accum_0 = { } + local _len_0 = 1 + for i = 1, #tb do + if tb[i] then + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + end + return _accum_0 + end)()) + i = 1 + local ids + do + local _accum_0 = { } + local _len_0 = 1 + while tb[i] do + i = i + 1 + _accum_0[_len_0] = i - 1 + _len_0 = _len_0 + 1 + end + ids = _accum_0 + end + i = 1 + local idx + do + local _accum_0 + while tb[i] do + i = i + 1 + _accum_0 = i - 1 + break + end + idx = _accum_0 + end + local f1 + f1 = function() + i = 1 + return f(_anon_func_0(i, tb)) + end + i = 1 + f((function() + local _accum_0 + while tb[i] do + i = i + 1 + _accum_0 = i - 1 + break + end + return _accum_0 + end)()) + local _accum_0 = { } + local _len_0 = 1 + local _list_3 = items + for _index_0 = 1, #_list_3 do + local item = _list_3[_index_0] + local _type_0 = type(item) + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 + if _tab_0 then + local value = item.value + if "A" == item.type and value ~= nil then + if value > 5 then + _accum_0[_len_0] = item + _len_0 = _len_0 + 1 + end + end + end + end + list = _accum_0 +end diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua index a5412ab..644b7c3 100644 --- a/spec/outputs/codes_from_doc.lua +++ b/spec/outputs/codes_from_doc.lua @@ -20,6 +20,38 @@ local inventory = { } } } +local map +map = function(arr, action) + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #arr do + local item = arr[_index_0] + _accum_0[_len_0] = action(item) + _len_0 = _len_0 + 1 + end + return _accum_0 +end +local filter +filter = function(arr, cond) + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #arr do + local item = arr[_index_0] + if cond(item) then + _accum_0[_len_0] = item + _len_0 = _len_0 + 1 + end + end + return _accum_0 +end +local reduce +reduce = function(arr, init, action) + for _index_0 = 1, #arr do + local item = arr[_index_0] + init = action(init, item) + end + return init +end print(reduce(filter(map({ 1, 2, @@ -1026,12 +1058,24 @@ local _len_0 = 1 for i = 1, 20 do if i % 2 == 0 then _accum_0[_len_0] = i * 2 + _len_0 = _len_0 + 1 else _accum_0[_len_0] = i + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 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 + break + end +end +first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3189,12 +3233,24 @@ local _len_0 = 1 for i = 1, 20 do if i % 2 == 0 then _accum_0[_len_0] = i * 2 + _len_0 = _len_0 + 1 else _accum_0[_len_0] = i + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 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 + 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 d9cc4dc..2457c52 100644 --- a/spec/outputs/codes_from_doc_zh.lua +++ b/spec/outputs/codes_from_doc_zh.lua @@ -20,6 +20,38 @@ local inventory = { } } } +local map +map = function(arr, action) + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #arr do + local item = arr[_index_0] + _accum_0[_len_0] = action(item) + _len_0 = _len_0 + 1 + end + return _accum_0 +end +local filter +filter = function(arr, cond) + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #arr do + local item = arr[_index_0] + if cond(item) then + _accum_0[_len_0] = item + _len_0 = _len_0 + 1 + end + end + return _accum_0 +end +local reduce +reduce = function(arr, init, action) + for _index_0 = 1, #arr do + local item = arr[_index_0] + init = action(init, item) + end + return init +end print(reduce(filter(map({ 1, 2, @@ -1020,12 +1052,24 @@ local _len_0 = 1 for i = 1, 20 do if i % 2 == 0 then _accum_0[_len_0] = i * 2 + _len_0 = _len_0 + 1 else _accum_0[_len_0] = i + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 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 + break + end +end +first_large = _accum_0 local func_a func_a = function() for i = 1, 10 do @@ -3177,12 +3221,24 @@ local _len_0 = 1 for i = 1, 20 do if i % 2 == 0 then _accum_0[_len_0] = i * 2 + _len_0 = _len_0 + 1 else _accum_0[_len_0] = i + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 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 + 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 8624d49..9a47579 100644 --- a/spec/outputs/loops.lua +++ b/spec/outputs/loops.lua @@ -60,8 +60,8 @@ do local y = hello[_index_0] if y % 2 == 0 then _accum_0[_len_0] = y + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 end x = _accum_0 end @@ -132,13 +132,11 @@ do end do local _accum_0 = { } - local _len_0 = 1 local _list_2 = 3 for _index_0 = 1, #_list_2 do local thing = _list_2[_index_0] y = "hello" break - _len_0 = _len_0 + 1 end x = _accum_0 end @@ -370,3 +368,103 @@ do end until false end +local _anon_func_0 = function(i, tb) + local _accum_0 = { } + local _len_0 = 1 + while tb[i] do + i = i + 1 + _accum_0[_len_0] = i - 1 + _len_0 = _len_0 + 1 + end + return _accum_0 +end +do + local index + do + local _accum_0 + for i = 1, #tb do + if tb[i] then + _accum_0 = i + break + end + end + index = _accum_0 + end + f((function() + local _accum_0 + for i = 1, #tb do + if tb[i] then + _accum_0 = i + break + end + end + return _accum_0 + end)()) + f((function() + local _accum_0 = { } + local _len_0 = 1 + for i = 1, #tb do + if tb[i] then + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + end + return _accum_0 + end)()) + i = 1 + local ids + do + local _accum_0 = { } + local _len_0 = 1 + while tb[i] do + i = i + 1 + _accum_0[_len_0] = i - 1 + _len_0 = _len_0 + 1 + end + ids = _accum_0 + end + i = 1 + local idx + do + local _accum_0 + while tb[i] do + i = i + 1 + _accum_0 = i - 1 + break + end + idx = _accum_0 + end + local f1 + f1 = function() + i = 1 + return f(_anon_func_0(i, tb)) + end + i = 1 + f((function() + local _accum_0 + while tb[i] do + i = i + 1 + _accum_0 = i - 1 + break + end + return _accum_0 + end)()) + local _accum_0 = { } + local _len_0 = 1 + local _list_3 = items + for _index_0 = 1, #_list_3 do + local item = _list_3[_index_0] + local _type_0 = type(item) + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 + if _tab_0 then + local value = item.value + if "A" == item.type and value ~= nil then + if value > 5 then + _accum_0[_len_0] = item + _len_0 = _len_0 + 1 + end + end + end + end + list = _accum_0 +end diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua index 4d31574..9f5507c 100644 --- a/spec/outputs/macro.lua +++ b/spec/outputs/macro.lua @@ -330,10 +330,8 @@ local _1 _1 = function() print(1) local _accum_0 = { } - local _len_0 = 1 while false do break - _len_0 = _len_0 + 1 end return _accum_0 end diff --git a/spec/outputs/unicode/loops.lua b/spec/outputs/unicode/loops.lua index 8379993..27bbe2e 100644 --- a/spec/outputs/unicode/loops.lua +++ b/spec/outputs/unicode/loops.lua @@ -60,8 +60,8 @@ do local _u53d8_u91cfy = _u4f60_u597d[_index_0] if _u53d8_u91cfy % 2 == 0 then _accum_0[_len_0] = _u53d8_u91cfy + _len_0 = _len_0 + 1 end - _len_0 = _len_0 + 1 end _u53d8_u91cfx = _accum_0 end @@ -132,13 +132,11 @@ do end do local _accum_0 = { } - local _len_0 = 1 local _list_2 = 3 for _index_0 = 1, #_list_2 do local _u4e1c_u897f = _list_2[_index_0] _u53d8_u91cfy = "你好" break - _len_0 = _len_0 + 1 end _u53d8_u91cfx = _accum_0 end diff --git a/spec/outputs/unicode/macro.lua b/spec/outputs/unicode/macro.lua index b14f571..3b9327a 100644 --- a/spec/outputs/unicode/macro.lua +++ b/spec/outputs/unicode/macro.lua @@ -365,10 +365,8 @@ local _1 _1 = function() _u6253_u5370(1) local _accum_0 = { } - local _len_0 = 1 while false do break - _len_0 = _len_0 + 1 end return _accum_0 end diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index fe6e726..612bdcd 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp @@ -188,9 +188,17 @@ std::string ConstValue_t::to_string(void* ud) const { std::string NotIn_t::to_string(void*) const { return {}; } +std::string Break_t::to_string(void*) const { + return "break"s; +} +std::string Continue_t::to_string(void*) const { + return "continue"s; +} std::string BreakLoop_t::to_string(void* ud) const { - auto info = reinterpret_cast(ud); - return info->convert(this); + if (value) { + return type->to_string(ud) + ' ' + value->to_string(ud); + } + return type->to_string(ud); } std::string YueLineComment_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 5e70645..782db5a 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -273,6 +273,8 @@ AST_NODE(ExpList) ast_ptr sep; ast_list exprs; AST_MEMBER(ExpList, &sep, &exprs) + bool followStmtProcessed = false; + Statement_t* followStmt = nullptr; AST_END(ExpList) AST_NODE(Return) @@ -856,7 +858,17 @@ AST_NODE(WhileLine) AST_MEMBER(WhileLine, &type, &condition) AST_END(WhileLine) -AST_LEAF(BreakLoop) +AST_LEAF(Break) +AST_END(Break) + +AST_LEAF(Continue) +AST_END(Continue) + +AST_NODE(BreakLoop) + ast_sel type; + ast_ptr value; + AST_MEMBER(BreakLoop, &type, &value) + std::string varBWV; AST_END(BreakLoop) AST_NODE(PipeBody) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index bc4574b..1a9387b 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.27.6"sv; +const std::string_view version = "0.28.0"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -2464,6 +2464,10 @@ private: auto info = extractDestructureInfo(assignment, false, optionalDestruct); if (info.destructures.empty()) { transformAssignmentCommon(assignment, out); + if (assignment->expList->followStmt) { + transformStatement(assignment->expList->followStmt, out); + assignment->expList->followStmtProcessed = true; + } return true; } else { auto x = assignment; @@ -2729,8 +2733,12 @@ private: temp.push_back(indent() + "end"s + nlr(x)); } out.push_back(join(temp)); + if (assignment->expList->followStmt) { + transformStatement(assignment->expList->followStmt, out); + assignment->expList->followStmtProcessed = true; + } + return false; } - return false; } void transformAssignItem(ast_node* value, str_list& out) { @@ -4329,7 +4337,9 @@ private: return false; }; switch (usage) { - case ExpUsage::Common: YUEE("AST node mismatch", x); return; + case ExpUsage::Common: + YUEE("AST node mismatch", x); + return; case ExpUsage::Return: case ExpUsage::Closure: { prepareValue(); @@ -7195,7 +7205,7 @@ private: try { unsigned long long value = std::stoull(binaryPart, nullptr, 2); numStr = std::to_string(value); - } catch (const std::exception& e) { + } catch (const std::exception&) { throw CompileError("invalid binary literal"sv, num); } } else if (getLuaTarget(num) < 502) { @@ -8162,11 +8172,44 @@ private: } } - bool hasContinueStatement(ast_node* body) { - return traversal::Stop == body->traverse([&](ast_node* node) { + enum class BreakLoopType { + None = 0, + Break = 1, + BreakWithValue = 1 << 1, + Continue = 1 << 2 + }; + + bool hasBreak(uint32_t breakLoopType) const { + return (breakLoopType & int(BreakLoopType::Break)) != 0; + } + + bool hasBreakWithValue(uint32_t breakLoopType) const { + return (breakLoopType & int(BreakLoopType::BreakWithValue)) != 0; + } + + bool hasContinue(uint32_t breakLoopType) const { + return (breakLoopType & int(BreakLoopType::Continue)) != 0; + } + + uint32_t getBreakLoopType(ast_node* body, const std::string& varBWV) { + uint32_t type = 0; + body->traverse([&](ast_node* node) { if (auto stmt = ast_cast(node)) { - if (stmt->content.is()) { - return _parser.toString(stmt->content) == "continue"sv ? traversal::Stop : traversal::Return; + if (auto breakLoop = stmt->content.as()) { + if (breakLoop->type.is()) { + 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); + } + type |= int(BreakLoopType::BreakWithValue); + breakLoop->varBWV = varBWV; + } else { + type |= int(BreakLoopType::Break); + } + } } else if (auto expList = expListFrom(stmt)) { BLOCK_START auto value = singleValueFrom(expList); @@ -8177,40 +8220,30 @@ private: switch (sVal->get_id()) { case id(): { auto withNode = static_cast(sVal); - if (hasContinueStatement(withNode->body)) { - return traversal::Stop; - } - break; + type |= getBreakLoopType(withNode->body, varBWV); + return traversal::Return; } case id(): { auto doNode = static_cast(sVal); - if (hasContinueStatement(doNode->body)) { - return traversal::Stop; - } - break; + type |= getBreakLoopType(doNode->body, varBWV); + return traversal::Return; } case id(): { auto ifNode = static_cast(sVal); for (auto n : ifNode->nodes.objects()) { - if (hasContinueStatement(n)) { - return traversal::Stop; - } + type |= getBreakLoopType(n, varBWV); } - break; + return traversal::Return; } case id(): { auto switchNode = static_cast(sVal); for (auto branch : switchNode->branches.objects()) { - if (hasContinueStatement(static_cast(branch)->body)) { - return traversal::Stop; - } + type |= getBreakLoopType(static_cast(branch)->body, varBWV); } if (switchNode->lastBranch) { - if (hasContinueStatement(switchNode->lastBranch)) { - return traversal::Stop; - } + type |= getBreakLoopType(switchNode->lastBranch, varBWV); } - break; + return traversal::Return; } } BLOCK_END @@ -8224,6 +8257,7 @@ private: } return traversal::Return; }); + return type; } void addDoToLastLineReturn(ast_node* body) { @@ -8247,10 +8281,10 @@ private: } } - void transformLoopBody(ast_node* body, str_list& out, const std::string& appendContent, ExpUsage usage, ExpList_t* assignList = nullptr) { + void transformLoopBody(ast_node* body, str_list& out, uint32_t breakLoopType, ExpUsage usage, ExpList_t* assignList = nullptr) { str_list temp; bool extraDo = false; - bool withContinue = hasContinueStatement(body); + bool withContinue = hasContinue(breakLoopType); int target = getLuaTarget(body); std::string extraLabel; if (withContinue) { @@ -8259,7 +8293,7 @@ private: if (!block->statements.empty()) { auto stmt = static_cast(block->statements.back()); if (auto breakLoop = ast_cast(stmt->content)) { - extraDo = _parser.toString(breakLoop) == "break"sv; + extraDo = breakLoop->type.is(); } } } @@ -8292,9 +8326,6 @@ private: popScope(); _buf << indent() << "end"sv << nll(body); } - if (!appendContent.empty()) { - _buf << indent() << appendContent; - } _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); popScope(); _buf << indent() << "until true"sv << nlr(body); @@ -8304,14 +8335,9 @@ private: temp.push_back(clearBuf()); _continueVars.pop(); } else { - if (!appendContent.empty()) { - temp.push_back(indent() + appendContent); - } temp.push_back(extraLabel); _continueVars.pop(); } - } else if (!appendContent.empty()) { - temp.back().append(indent() + appendContent); } out.push_back(join(temp)); } @@ -8320,7 +8346,8 @@ private: str_list temp; bool extraDo = false; auto body = repeatNode->body->content.get(); - bool withContinue = hasContinueStatement(body); + auto breakLoopType = getBreakLoopType(body, Empty); + bool withContinue = hasContinue(breakLoopType); std::string conditionVar; std::string extraLabel; ast_ptr condAssign; @@ -8331,7 +8358,7 @@ private: if (!block->statements.empty()) { auto stmt = static_cast(block->statements.back()); if (auto breakLoop = ast_cast(stmt->content)) { - extraDo = _parser.toString(breakLoop) == "break"sv; + extraDo = breakLoop->type.is(); } } } @@ -8394,7 +8421,8 @@ private: void transformFor(For_t* forNode, str_list& out) { str_list temp; transformForHead(forNode, temp); - transformLoopBody(forNode->body, temp, Empty, ExpUsage::Common); + auto breakLoopType = getBreakLoopType(forNode->body, Empty); + transformLoopBody(forNode->body, temp, breakLoopType, ExpUsage::Common); popScope(); out.push_back(join(temp) + indent() + "end"s + nlr(forNode)); } @@ -8405,13 +8433,19 @@ private: addToScope(accum); std::string len = getUnusedName("_len_"sv); addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); + auto breakLoopType = getBreakLoopType(forNode->body, accum); + _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forNode); + out.emplace_back(clearBuf()); _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); - out.push_back(clearBuf()); + auto& lenAssign = out.emplace_back(clearBuf()); transformForHead(forNode, out); auto expList = toAst(accum + '[' + len + ']', x); - auto lenLine = len + " = "s + len + " + 1"s + nlr(forNode->body); - transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); + auto followStmt = toAst(len + "+=1"s, forNode->body); + expList->followStmt = followStmt.get(); + transformLoopBody(forNode->body, out, breakLoopType, ExpUsage::Assignment, expList); + if (!expList->followStmtProcessed) { + lenAssign.clear(); + } popScope(); out.push_back(indent() + "end"s + nlr(forNode)); return accum; @@ -8490,7 +8524,8 @@ private: void transformForEach(ForEach_t* forEach, str_list& out) { str_list temp; bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false); - transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); + auto breakLoopType = getBreakLoopType(forEach->body, Empty); + transformLoopBody(forEach->body, temp, breakLoopType, ExpUsage::Common); popScope(); out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach)); if (extraScoped) { @@ -8505,13 +8540,19 @@ private: addToScope(accum); std::string len = getUnusedName("_len_"sv); addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); + auto breakLoopType = getBreakLoopType(forEach->body, accum); + _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forEach); + out.emplace_back(clearBuf()); _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); - out.push_back(clearBuf()); + auto& lenAssign = out.emplace_back(clearBuf()); transformForEachHead(forEach->nameList, forEach->loopValue, out, true); auto expList = toAst(accum + '[' + len + ']', x); - auto lenLine = len + " = "s + len + " + 1"s + nlr(forEach->body); - transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); + auto followStmt = toAst(len + "+=1"s, forEach->body); + expList->followStmt = followStmt.get(); + transformLoopBody(forEach->body, out, breakLoopType, ExpUsage::Assignment, expList); + if (!expList->followStmtProcessed) { + lenAssign.clear(); + } popScope(); out.push_back(indent() + "end"s + nlr(forEach)); return accum; @@ -10440,15 +10481,22 @@ private: addToScope(accumVar); auto lenVar = getUnusedName("_len_"sv); addToScope(lenVar); - temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); - temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); + auto breakLoopType = getBreakLoopType(whileNode->body, accumVar); + _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode); + temp.emplace_back(clearBuf()); + _buf << indent() << "local "s << lenVar << " = 1"s << nll(whileNode); + auto& lenAssign = temp.emplace_back(clearBuf()); bool isUntil = _parser.toString(whileNode->type) == "until"sv; auto condStr = transformCondExp(whileNode->condition, isUntil); temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); pushScope(); auto assignLeft = toAst(accumVar + '[' + lenVar + ']', x); - auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); - transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); + auto followStmt = toAst(lenVar + "+=1"s, whileNode); + assignLeft->followStmt = followStmt.get(); + transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft); + if (!assignLeft->followStmtProcessed) { + lenAssign.clear(); + } popScope(); temp.push_back(indent() + "end"s + nlr(whileNode)); if (expList) { @@ -10484,15 +10532,21 @@ private: addToScope(accumVar); auto lenVar = getUnusedName("_len_"sv); addToScope(lenVar); - temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); - temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); + auto breakLoopType = getBreakLoopType(whileNode->body, accumVar); + _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode); + temp.emplace_back(clearBuf()); + auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); bool isUntil = _parser.toString(whileNode->type) == "until"sv; auto condStr = transformCondExp(whileNode->condition, isUntil); temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); pushScope(); auto assignLeft = toAst(accumVar + '[' + lenVar + ']', x); - auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); - transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); + auto followStmt = toAst(lenVar + "+=1"s, whileNode); + assignLeft->followStmt = followStmt.get(); + transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft); + if (!assignLeft->followStmtProcessed) { + lenAssign.clear(); + } popScope(); temp.push_back(indent() + "end"s + nlr(whileNode)); temp.push_back(indent() + "return "s + accumVar + nlr(whileNode)); @@ -10537,7 +10591,8 @@ private: pushScope(); bool isUntil = _parser.toString(whileNode->type) == "until"sv; auto condStr = transformCondExp(whileNode->condition, isUntil); - transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); + auto breakLoopType = getBreakLoopType(whileNode->body, Empty); + transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common); popScope(); _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode); _buf << temp.back(); @@ -11077,11 +11132,17 @@ private: } void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { - auto keyword = _parser.toString(breakLoop); + auto isBreak = breakLoop->type.is(); + auto keyword = isBreak ? "break"s : "continue"s; if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { throw CompileError(keyword + " is not inside a loop"s, breakLoop); } - if (keyword == "break"sv) { + if (isBreak) { + if (breakLoop->value) { + auto exp = toAst(breakLoop->varBWV, breakLoop->value); + auto assignment = assignmentFrom(exp, breakLoop->value, breakLoop); + transformAssignment(assignment, out); + } out.push_back(indent() + keyword + nll(breakLoop)); return; } diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 77c5901..eaabf0d 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -350,7 +350,9 @@ YueParser::YueParser() { ShortTabAppending = "[]" >> space >> Assign; - BreakLoop = (expr("break") | "continue") >> not_alpha_num; + Break = key("break"); + Continue = key("continue"); + BreakLoop = (Break >> -(space >> Exp) | Continue) >> not_alpha_num; Return = key("return") >> -(space >> (TableBlock | ExpListLow)); diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index 7281ec3..63afcb9 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -427,6 +427,8 @@ private: AST_RULE(ExpListAssign); AST_RULE(IfLine); AST_RULE(WhileLine); + AST_RULE(Break); + AST_RULE(Continue); AST_RULE(BreakLoop); AST_RULE(StatementAppendix); AST_RULE(Statement); -- cgit v1.2.3-55-g6feb