diff options
author | Li Jin <dragon-fly@qq.com> | 2025-05-21 11:44:54 +0800 |
---|---|---|
committer | Li Jin <dragon-fly@qq.com> | 2025-05-21 11:44:54 +0800 |
commit | 0603800a4114ed8b4c9572a7d7852995c9b9f334 (patch) | |
tree | 456524685562bcd0d874530e3ddc2a0fc0731525 /src | |
parent | ff137ac73d999a5849f02706cfd52f4659b025ef (diff) | |
download | yuescript-0603800a4114ed8b4c9572a7d7852995c9b9f334.tar.gz yuescript-0603800a4114ed8b4c9572a7d7852995c9b9f334.tar.bz2 yuescript-0603800a4114ed8b4c9572a7d7852995c9b9f334.zip |
Added break with value syntax.
Diffstat (limited to 'src')
-rw-r--r-- | src/yuescript/yue_ast.cpp | 12 | ||||
-rw-r--r-- | src/yuescript/yue_ast.h | 14 | ||||
-rw-r--r-- | src/yuescript/yue_compiler.cpp | 183 | ||||
-rw-r--r-- | src/yuescript/yue_parser.cpp | 4 | ||||
-rw-r--r-- | src/yuescript/yue_parser.h | 2 |
5 files changed, 150 insertions, 65 deletions
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 { | |||
188 | std::string NotIn_t::to_string(void*) const { | 188 | std::string NotIn_t::to_string(void*) const { |
189 | return {}; | 189 | return {}; |
190 | } | 190 | } |
191 | std::string Break_t::to_string(void*) const { | ||
192 | return "break"s; | ||
193 | } | ||
194 | std::string Continue_t::to_string(void*) const { | ||
195 | return "continue"s; | ||
196 | } | ||
191 | std::string BreakLoop_t::to_string(void* ud) const { | 197 | std::string BreakLoop_t::to_string(void* ud) const { |
192 | auto info = reinterpret_cast<YueFormat*>(ud); | 198 | if (value) { |
193 | return info->convert(this); | 199 | return type->to_string(ud) + ' ' + value->to_string(ud); |
200 | } | ||
201 | return type->to_string(ud); | ||
194 | } | 202 | } |
195 | std::string YueLineComment_t::to_string(void* ud) const { | 203 | std::string YueLineComment_t::to_string(void* ud) const { |
196 | auto info = reinterpret_cast<YueFormat*>(ud); | 204 | auto info = reinterpret_cast<YueFormat*>(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) | |||
273 | ast_ptr<true, Seperator_t> sep; | 273 | ast_ptr<true, Seperator_t> sep; |
274 | ast_list<true, Exp_t> exprs; | 274 | ast_list<true, Exp_t> exprs; |
275 | AST_MEMBER(ExpList, &sep, &exprs) | 275 | AST_MEMBER(ExpList, &sep, &exprs) |
276 | bool followStmtProcessed = false; | ||
277 | Statement_t* followStmt = nullptr; | ||
276 | AST_END(ExpList) | 278 | AST_END(ExpList) |
277 | 279 | ||
278 | AST_NODE(Return) | 280 | AST_NODE(Return) |
@@ -856,7 +858,17 @@ AST_NODE(WhileLine) | |||
856 | AST_MEMBER(WhileLine, &type, &condition) | 858 | AST_MEMBER(WhileLine, &type, &condition) |
857 | AST_END(WhileLine) | 859 | AST_END(WhileLine) |
858 | 860 | ||
859 | AST_LEAF(BreakLoop) | 861 | AST_LEAF(Break) |
862 | AST_END(Break) | ||
863 | |||
864 | AST_LEAF(Continue) | ||
865 | AST_END(Continue) | ||
866 | |||
867 | AST_NODE(BreakLoop) | ||
868 | ast_sel<true, Break_t, Continue_t> type; | ||
869 | ast_ptr<false, Exp_t> value; | ||
870 | AST_MEMBER(BreakLoop, &type, &value) | ||
871 | std::string varBWV; | ||
860 | AST_END(BreakLoop) | 872 | AST_END(BreakLoop) |
861 | 873 | ||
862 | AST_NODE(PipeBody) | 874 | 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<std::string> Metamethods = { | |||
78 | "close"s // Lua 5.4 | 78 | "close"s // Lua 5.4 |
79 | }; | 79 | }; |
80 | 80 | ||
81 | const std::string_view version = "0.27.6"sv; | 81 | const std::string_view version = "0.28.0"sv; |
82 | const std::string_view extension = "yue"sv; | 82 | const std::string_view extension = "yue"sv; |
83 | 83 | ||
84 | class CompileError : public std::logic_error { | 84 | class CompileError : public std::logic_error { |
@@ -2464,6 +2464,10 @@ private: | |||
2464 | auto info = extractDestructureInfo(assignment, false, optionalDestruct); | 2464 | auto info = extractDestructureInfo(assignment, false, optionalDestruct); |
2465 | if (info.destructures.empty()) { | 2465 | if (info.destructures.empty()) { |
2466 | transformAssignmentCommon(assignment, out); | 2466 | transformAssignmentCommon(assignment, out); |
2467 | if (assignment->expList->followStmt) { | ||
2468 | transformStatement(assignment->expList->followStmt, out); | ||
2469 | assignment->expList->followStmtProcessed = true; | ||
2470 | } | ||
2467 | return true; | 2471 | return true; |
2468 | } else { | 2472 | } else { |
2469 | auto x = assignment; | 2473 | auto x = assignment; |
@@ -2729,8 +2733,12 @@ private: | |||
2729 | temp.push_back(indent() + "end"s + nlr(x)); | 2733 | temp.push_back(indent() + "end"s + nlr(x)); |
2730 | } | 2734 | } |
2731 | out.push_back(join(temp)); | 2735 | out.push_back(join(temp)); |
2736 | if (assignment->expList->followStmt) { | ||
2737 | transformStatement(assignment->expList->followStmt, out); | ||
2738 | assignment->expList->followStmtProcessed = true; | ||
2739 | } | ||
2740 | return false; | ||
2732 | } | 2741 | } |
2733 | return false; | ||
2734 | } | 2742 | } |
2735 | 2743 | ||
2736 | void transformAssignItem(ast_node* value, str_list& out) { | 2744 | void transformAssignItem(ast_node* value, str_list& out) { |
@@ -4329,7 +4337,9 @@ private: | |||
4329 | return false; | 4337 | return false; |
4330 | }; | 4338 | }; |
4331 | switch (usage) { | 4339 | switch (usage) { |
4332 | case ExpUsage::Common: YUEE("AST node mismatch", x); return; | 4340 | case ExpUsage::Common: |
4341 | YUEE("AST node mismatch", x); | ||
4342 | return; | ||
4333 | case ExpUsage::Return: | 4343 | case ExpUsage::Return: |
4334 | case ExpUsage::Closure: { | 4344 | case ExpUsage::Closure: { |
4335 | prepareValue(); | 4345 | prepareValue(); |
@@ -7195,7 +7205,7 @@ private: | |||
7195 | try { | 7205 | try { |
7196 | unsigned long long value = std::stoull(binaryPart, nullptr, 2); | 7206 | unsigned long long value = std::stoull(binaryPart, nullptr, 2); |
7197 | numStr = std::to_string(value); | 7207 | numStr = std::to_string(value); |
7198 | } catch (const std::exception& e) { | 7208 | } catch (const std::exception&) { |
7199 | throw CompileError("invalid binary literal"sv, num); | 7209 | throw CompileError("invalid binary literal"sv, num); |
7200 | } | 7210 | } |
7201 | } else if (getLuaTarget(num) < 502) { | 7211 | } else if (getLuaTarget(num) < 502) { |
@@ -8162,11 +8172,44 @@ private: | |||
8162 | } | 8172 | } |
8163 | } | 8173 | } |
8164 | 8174 | ||
8165 | bool hasContinueStatement(ast_node* body) { | 8175 | enum class BreakLoopType { |
8166 | return traversal::Stop == body->traverse([&](ast_node* node) { | 8176 | None = 0, |
8177 | Break = 1, | ||
8178 | BreakWithValue = 1 << 1, | ||
8179 | Continue = 1 << 2 | ||
8180 | }; | ||
8181 | |||
8182 | bool hasBreak(uint32_t breakLoopType) const { | ||
8183 | return (breakLoopType & int(BreakLoopType::Break)) != 0; | ||
8184 | } | ||
8185 | |||
8186 | bool hasBreakWithValue(uint32_t breakLoopType) const { | ||
8187 | return (breakLoopType & int(BreakLoopType::BreakWithValue)) != 0; | ||
8188 | } | ||
8189 | |||
8190 | bool hasContinue(uint32_t breakLoopType) const { | ||
8191 | return (breakLoopType & int(BreakLoopType::Continue)) != 0; | ||
8192 | } | ||
8193 | |||
8194 | uint32_t getBreakLoopType(ast_node* body, const std::string& varBWV) { | ||
8195 | uint32_t type = 0; | ||
8196 | body->traverse([&](ast_node* node) { | ||
8167 | if (auto stmt = ast_cast<Statement_t>(node)) { | 8197 | if (auto stmt = ast_cast<Statement_t>(node)) { |
8168 | if (stmt->content.is<BreakLoop_t>()) { | 8198 | if (auto breakLoop = stmt->content.as<BreakLoop_t>()) { |
8169 | return _parser.toString(stmt->content) == "continue"sv ? traversal::Stop : traversal::Return; | 8199 | if (breakLoop->type.is<Continue_t>()) { |
8200 | type |= int(BreakLoopType::Continue); | ||
8201 | return traversal::Return; | ||
8202 | } else { | ||
8203 | if (breakLoop->value) { | ||
8204 | if (varBWV.empty()) { | ||
8205 | throw CompileError("break with a value is not allowed here"sv, breakLoop->value); | ||
8206 | } | ||
8207 | type |= int(BreakLoopType::BreakWithValue); | ||
8208 | breakLoop->varBWV = varBWV; | ||
8209 | } else { | ||
8210 | type |= int(BreakLoopType::Break); | ||
8211 | } | ||
8212 | } | ||
8170 | } else if (auto expList = expListFrom(stmt)) { | 8213 | } else if (auto expList = expListFrom(stmt)) { |
8171 | BLOCK_START | 8214 | BLOCK_START |
8172 | auto value = singleValueFrom(expList); | 8215 | auto value = singleValueFrom(expList); |
@@ -8177,40 +8220,30 @@ private: | |||
8177 | switch (sVal->get_id()) { | 8220 | switch (sVal->get_id()) { |
8178 | case id<With_t>(): { | 8221 | case id<With_t>(): { |
8179 | auto withNode = static_cast<With_t*>(sVal); | 8222 | auto withNode = static_cast<With_t*>(sVal); |
8180 | if (hasContinueStatement(withNode->body)) { | 8223 | type |= getBreakLoopType(withNode->body, varBWV); |
8181 | return traversal::Stop; | 8224 | return traversal::Return; |
8182 | } | ||
8183 | break; | ||
8184 | } | 8225 | } |
8185 | case id<Do_t>(): { | 8226 | case id<Do_t>(): { |
8186 | auto doNode = static_cast<Do_t*>(sVal); | 8227 | auto doNode = static_cast<Do_t*>(sVal); |
8187 | if (hasContinueStatement(doNode->body)) { | 8228 | type |= getBreakLoopType(doNode->body, varBWV); |
8188 | return traversal::Stop; | 8229 | return traversal::Return; |
8189 | } | ||
8190 | break; | ||
8191 | } | 8230 | } |
8192 | case id<If_t>(): { | 8231 | case id<If_t>(): { |
8193 | auto ifNode = static_cast<If_t*>(sVal); | 8232 | auto ifNode = static_cast<If_t*>(sVal); |
8194 | for (auto n : ifNode->nodes.objects()) { | 8233 | for (auto n : ifNode->nodes.objects()) { |
8195 | if (hasContinueStatement(n)) { | 8234 | type |= getBreakLoopType(n, varBWV); |
8196 | return traversal::Stop; | ||
8197 | } | ||
8198 | } | 8235 | } |
8199 | break; | 8236 | return traversal::Return; |
8200 | } | 8237 | } |
8201 | case id<Switch_t>(): { | 8238 | case id<Switch_t>(): { |
8202 | auto switchNode = static_cast<Switch_t*>(sVal); | 8239 | auto switchNode = static_cast<Switch_t*>(sVal); |
8203 | for (auto branch : switchNode->branches.objects()) { | 8240 | for (auto branch : switchNode->branches.objects()) { |
8204 | if (hasContinueStatement(static_cast<SwitchCase_t*>(branch)->body)) { | 8241 | type |= getBreakLoopType(static_cast<SwitchCase_t*>(branch)->body, varBWV); |
8205 | return traversal::Stop; | ||
8206 | } | ||
8207 | } | 8242 | } |
8208 | if (switchNode->lastBranch) { | 8243 | if (switchNode->lastBranch) { |
8209 | if (hasContinueStatement(switchNode->lastBranch)) { | 8244 | type |= getBreakLoopType(switchNode->lastBranch, varBWV); |
8210 | return traversal::Stop; | ||
8211 | } | ||
8212 | } | 8245 | } |
8213 | break; | 8246 | return traversal::Return; |
8214 | } | 8247 | } |
8215 | } | 8248 | } |
8216 | BLOCK_END | 8249 | BLOCK_END |
@@ -8224,6 +8257,7 @@ private: | |||
8224 | } | 8257 | } |
8225 | return traversal::Return; | 8258 | return traversal::Return; |
8226 | }); | 8259 | }); |
8260 | return type; | ||
8227 | } | 8261 | } |
8228 | 8262 | ||
8229 | void addDoToLastLineReturn(ast_node* body) { | 8263 | void addDoToLastLineReturn(ast_node* body) { |
@@ -8247,10 +8281,10 @@ private: | |||
8247 | } | 8281 | } |
8248 | } | 8282 | } |
8249 | 8283 | ||
8250 | void transformLoopBody(ast_node* body, str_list& out, const std::string& appendContent, ExpUsage usage, ExpList_t* assignList = nullptr) { | 8284 | void transformLoopBody(ast_node* body, str_list& out, uint32_t breakLoopType, ExpUsage usage, ExpList_t* assignList = nullptr) { |
8251 | str_list temp; | 8285 | str_list temp; |
8252 | bool extraDo = false; | 8286 | bool extraDo = false; |
8253 | bool withContinue = hasContinueStatement(body); | 8287 | bool withContinue = hasContinue(breakLoopType); |
8254 | int target = getLuaTarget(body); | 8288 | int target = getLuaTarget(body); |
8255 | std::string extraLabel; | 8289 | std::string extraLabel; |
8256 | if (withContinue) { | 8290 | if (withContinue) { |
@@ -8259,7 +8293,7 @@ private: | |||
8259 | if (!block->statements.empty()) { | 8293 | if (!block->statements.empty()) { |
8260 | auto stmt = static_cast<Statement_t*>(block->statements.back()); | 8294 | auto stmt = static_cast<Statement_t*>(block->statements.back()); |
8261 | if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { | 8295 | if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { |
8262 | extraDo = _parser.toString(breakLoop) == "break"sv; | 8296 | extraDo = breakLoop->type.is<Break_t>(); |
8263 | } | 8297 | } |
8264 | } | 8298 | } |
8265 | } | 8299 | } |
@@ -8292,9 +8326,6 @@ private: | |||
8292 | popScope(); | 8326 | popScope(); |
8293 | _buf << indent() << "end"sv << nll(body); | 8327 | _buf << indent() << "end"sv << nll(body); |
8294 | } | 8328 | } |
8295 | if (!appendContent.empty()) { | ||
8296 | _buf << indent() << appendContent; | ||
8297 | } | ||
8298 | _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); | 8329 | _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); |
8299 | popScope(); | 8330 | popScope(); |
8300 | _buf << indent() << "until true"sv << nlr(body); | 8331 | _buf << indent() << "until true"sv << nlr(body); |
@@ -8304,14 +8335,9 @@ private: | |||
8304 | temp.push_back(clearBuf()); | 8335 | temp.push_back(clearBuf()); |
8305 | _continueVars.pop(); | 8336 | _continueVars.pop(); |
8306 | } else { | 8337 | } else { |
8307 | if (!appendContent.empty()) { | ||
8308 | temp.push_back(indent() + appendContent); | ||
8309 | } | ||
8310 | temp.push_back(extraLabel); | 8338 | temp.push_back(extraLabel); |
8311 | _continueVars.pop(); | 8339 | _continueVars.pop(); |
8312 | } | 8340 | } |
8313 | } else if (!appendContent.empty()) { | ||
8314 | temp.back().append(indent() + appendContent); | ||
8315 | } | 8341 | } |
8316 | out.push_back(join(temp)); | 8342 | out.push_back(join(temp)); |
8317 | } | 8343 | } |
@@ -8320,7 +8346,8 @@ private: | |||
8320 | str_list temp; | 8346 | str_list temp; |
8321 | bool extraDo = false; | 8347 | bool extraDo = false; |
8322 | auto body = repeatNode->body->content.get(); | 8348 | auto body = repeatNode->body->content.get(); |
8323 | bool withContinue = hasContinueStatement(body); | 8349 | auto breakLoopType = getBreakLoopType(body, Empty); |
8350 | bool withContinue = hasContinue(breakLoopType); | ||
8324 | std::string conditionVar; | 8351 | std::string conditionVar; |
8325 | std::string extraLabel; | 8352 | std::string extraLabel; |
8326 | ast_ptr<false, ExpListAssign_t> condAssign; | 8353 | ast_ptr<false, ExpListAssign_t> condAssign; |
@@ -8331,7 +8358,7 @@ private: | |||
8331 | if (!block->statements.empty()) { | 8358 | if (!block->statements.empty()) { |
8332 | auto stmt = static_cast<Statement_t*>(block->statements.back()); | 8359 | auto stmt = static_cast<Statement_t*>(block->statements.back()); |
8333 | if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { | 8360 | if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { |
8334 | extraDo = _parser.toString(breakLoop) == "break"sv; | 8361 | extraDo = breakLoop->type.is<Break_t>(); |
8335 | } | 8362 | } |
8336 | } | 8363 | } |
8337 | } | 8364 | } |
@@ -8394,7 +8421,8 @@ private: | |||
8394 | void transformFor(For_t* forNode, str_list& out) { | 8421 | void transformFor(For_t* forNode, str_list& out) { |
8395 | str_list temp; | 8422 | str_list temp; |
8396 | transformForHead(forNode, temp); | 8423 | transformForHead(forNode, temp); |
8397 | transformLoopBody(forNode->body, temp, Empty, ExpUsage::Common); | 8424 | auto breakLoopType = getBreakLoopType(forNode->body, Empty); |
8425 | transformLoopBody(forNode->body, temp, breakLoopType, ExpUsage::Common); | ||
8398 | popScope(); | 8426 | popScope(); |
8399 | out.push_back(join(temp) + indent() + "end"s + nlr(forNode)); | 8427 | out.push_back(join(temp) + indent() + "end"s + nlr(forNode)); |
8400 | } | 8428 | } |
@@ -8405,13 +8433,19 @@ private: | |||
8405 | addToScope(accum); | 8433 | addToScope(accum); |
8406 | std::string len = getUnusedName("_len_"sv); | 8434 | std::string len = getUnusedName("_len_"sv); |
8407 | addToScope(len); | 8435 | addToScope(len); |
8408 | _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); | 8436 | auto breakLoopType = getBreakLoopType(forNode->body, accum); |
8437 | _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forNode); | ||
8438 | out.emplace_back(clearBuf()); | ||
8409 | _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); | 8439 | _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); |
8410 | out.push_back(clearBuf()); | 8440 | auto& lenAssign = out.emplace_back(clearBuf()); |
8411 | transformForHead(forNode, out); | 8441 | transformForHead(forNode, out); |
8412 | auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); | 8442 | auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); |
8413 | auto lenLine = len + " = "s + len + " + 1"s + nlr(forNode->body); | 8443 | auto followStmt = toAst<Statement_t>(len + "+=1"s, forNode->body); |
8414 | transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); | 8444 | expList->followStmt = followStmt.get(); |
8445 | transformLoopBody(forNode->body, out, breakLoopType, ExpUsage::Assignment, expList); | ||
8446 | if (!expList->followStmtProcessed) { | ||
8447 | lenAssign.clear(); | ||
8448 | } | ||
8415 | popScope(); | 8449 | popScope(); |
8416 | out.push_back(indent() + "end"s + nlr(forNode)); | 8450 | out.push_back(indent() + "end"s + nlr(forNode)); |
8417 | return accum; | 8451 | return accum; |
@@ -8490,7 +8524,8 @@ private: | |||
8490 | void transformForEach(ForEach_t* forEach, str_list& out) { | 8524 | void transformForEach(ForEach_t* forEach, str_list& out) { |
8491 | str_list temp; | 8525 | str_list temp; |
8492 | bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false); | 8526 | bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false); |
8493 | transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); | 8527 | auto breakLoopType = getBreakLoopType(forEach->body, Empty); |
8528 | transformLoopBody(forEach->body, temp, breakLoopType, ExpUsage::Common); | ||
8494 | popScope(); | 8529 | popScope(); |
8495 | out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach)); | 8530 | out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach)); |
8496 | if (extraScoped) { | 8531 | if (extraScoped) { |
@@ -8505,13 +8540,19 @@ private: | |||
8505 | addToScope(accum); | 8540 | addToScope(accum); |
8506 | std::string len = getUnusedName("_len_"sv); | 8541 | std::string len = getUnusedName("_len_"sv); |
8507 | addToScope(len); | 8542 | addToScope(len); |
8508 | _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); | 8543 | auto breakLoopType = getBreakLoopType(forEach->body, accum); |
8544 | _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forEach); | ||
8545 | out.emplace_back(clearBuf()); | ||
8509 | _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); | 8546 | _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); |
8510 | out.push_back(clearBuf()); | 8547 | auto& lenAssign = out.emplace_back(clearBuf()); |
8511 | transformForEachHead(forEach->nameList, forEach->loopValue, out, true); | 8548 | transformForEachHead(forEach->nameList, forEach->loopValue, out, true); |
8512 | auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); | 8549 | auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); |
8513 | auto lenLine = len + " = "s + len + " + 1"s + nlr(forEach->body); | 8550 | auto followStmt = toAst<Statement_t>(len + "+=1"s, forEach->body); |
8514 | transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); | 8551 | expList->followStmt = followStmt.get(); |
8552 | transformLoopBody(forEach->body, out, breakLoopType, ExpUsage::Assignment, expList); | ||
8553 | if (!expList->followStmtProcessed) { | ||
8554 | lenAssign.clear(); | ||
8555 | } | ||
8515 | popScope(); | 8556 | popScope(); |
8516 | out.push_back(indent() + "end"s + nlr(forEach)); | 8557 | out.push_back(indent() + "end"s + nlr(forEach)); |
8517 | return accum; | 8558 | return accum; |
@@ -10440,15 +10481,22 @@ private: | |||
10440 | addToScope(accumVar); | 10481 | addToScope(accumVar); |
10441 | auto lenVar = getUnusedName("_len_"sv); | 10482 | auto lenVar = getUnusedName("_len_"sv); |
10442 | addToScope(lenVar); | 10483 | addToScope(lenVar); |
10443 | temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); | 10484 | auto breakLoopType = getBreakLoopType(whileNode->body, accumVar); |
10444 | temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); | 10485 | _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode); |
10486 | temp.emplace_back(clearBuf()); | ||
10487 | _buf << indent() << "local "s << lenVar << " = 1"s << nll(whileNode); | ||
10488 | auto& lenAssign = temp.emplace_back(clearBuf()); | ||
10445 | bool isUntil = _parser.toString(whileNode->type) == "until"sv; | 10489 | bool isUntil = _parser.toString(whileNode->type) == "until"sv; |
10446 | auto condStr = transformCondExp(whileNode->condition, isUntil); | 10490 | auto condStr = transformCondExp(whileNode->condition, isUntil); |
10447 | temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); | 10491 | temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); |
10448 | pushScope(); | 10492 | pushScope(); |
10449 | auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); | 10493 | auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); |
10450 | auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); | 10494 | auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, whileNode); |
10451 | transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); | 10495 | assignLeft->followStmt = followStmt.get(); |
10496 | transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft); | ||
10497 | if (!assignLeft->followStmtProcessed) { | ||
10498 | lenAssign.clear(); | ||
10499 | } | ||
10452 | popScope(); | 10500 | popScope(); |
10453 | temp.push_back(indent() + "end"s + nlr(whileNode)); | 10501 | temp.push_back(indent() + "end"s + nlr(whileNode)); |
10454 | if (expList) { | 10502 | if (expList) { |
@@ -10484,15 +10532,21 @@ private: | |||
10484 | addToScope(accumVar); | 10532 | addToScope(accumVar); |
10485 | auto lenVar = getUnusedName("_len_"sv); | 10533 | auto lenVar = getUnusedName("_len_"sv); |
10486 | addToScope(lenVar); | 10534 | addToScope(lenVar); |
10487 | temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); | 10535 | auto breakLoopType = getBreakLoopType(whileNode->body, accumVar); |
10488 | temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); | 10536 | _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode); |
10537 | temp.emplace_back(clearBuf()); | ||
10538 | auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); | ||
10489 | bool isUntil = _parser.toString(whileNode->type) == "until"sv; | 10539 | bool isUntil = _parser.toString(whileNode->type) == "until"sv; |
10490 | auto condStr = transformCondExp(whileNode->condition, isUntil); | 10540 | auto condStr = transformCondExp(whileNode->condition, isUntil); |
10491 | temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); | 10541 | temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); |
10492 | pushScope(); | 10542 | pushScope(); |
10493 | auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); | 10543 | auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); |
10494 | auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); | 10544 | auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, whileNode); |
10495 | transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); | 10545 | assignLeft->followStmt = followStmt.get(); |
10546 | transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft); | ||
10547 | if (!assignLeft->followStmtProcessed) { | ||
10548 | lenAssign.clear(); | ||
10549 | } | ||
10496 | popScope(); | 10550 | popScope(); |
10497 | temp.push_back(indent() + "end"s + nlr(whileNode)); | 10551 | temp.push_back(indent() + "end"s + nlr(whileNode)); |
10498 | temp.push_back(indent() + "return "s + accumVar + nlr(whileNode)); | 10552 | temp.push_back(indent() + "return "s + accumVar + nlr(whileNode)); |
@@ -10537,7 +10591,8 @@ private: | |||
10537 | pushScope(); | 10591 | pushScope(); |
10538 | bool isUntil = _parser.toString(whileNode->type) == "until"sv; | 10592 | bool isUntil = _parser.toString(whileNode->type) == "until"sv; |
10539 | auto condStr = transformCondExp(whileNode->condition, isUntil); | 10593 | auto condStr = transformCondExp(whileNode->condition, isUntil); |
10540 | transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); | 10594 | auto breakLoopType = getBreakLoopType(whileNode->body, Empty); |
10595 | transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common); | ||
10541 | popScope(); | 10596 | popScope(); |
10542 | _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode); | 10597 | _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode); |
10543 | _buf << temp.back(); | 10598 | _buf << temp.back(); |
@@ -11077,11 +11132,17 @@ private: | |||
11077 | } | 11132 | } |
11078 | 11133 | ||
11079 | void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { | 11134 | void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { |
11080 | auto keyword = _parser.toString(breakLoop); | 11135 | auto isBreak = breakLoop->type.is<Break_t>(); |
11136 | auto keyword = isBreak ? "break"s : "continue"s; | ||
11081 | if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { | 11137 | if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { |
11082 | throw CompileError(keyword + " is not inside a loop"s, breakLoop); | 11138 | throw CompileError(keyword + " is not inside a loop"s, breakLoop); |
11083 | } | 11139 | } |
11084 | if (keyword == "break"sv) { | 11140 | if (isBreak) { |
11141 | if (breakLoop->value) { | ||
11142 | auto exp = toAst<Exp_t>(breakLoop->varBWV, breakLoop->value); | ||
11143 | auto assignment = assignmentFrom(exp, breakLoop->value, breakLoop); | ||
11144 | transformAssignment(assignment, out); | ||
11145 | } | ||
11085 | out.push_back(indent() + keyword + nll(breakLoop)); | 11146 | out.push_back(indent() + keyword + nll(breakLoop)); |
11086 | return; | 11147 | return; |
11087 | } | 11148 | } |
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() { | |||
350 | 350 | ||
351 | ShortTabAppending = "[]" >> space >> Assign; | 351 | ShortTabAppending = "[]" >> space >> Assign; |
352 | 352 | ||
353 | BreakLoop = (expr("break") | "continue") >> not_alpha_num; | 353 | Break = key("break"); |
354 | Continue = key("continue"); | ||
355 | BreakLoop = (Break >> -(space >> Exp) | Continue) >> not_alpha_num; | ||
354 | 356 | ||
355 | Return = key("return") >> -(space >> (TableBlock | ExpListLow)); | 357 | Return = key("return") >> -(space >> (TableBlock | ExpListLow)); |
356 | 358 | ||
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: | |||
427 | AST_RULE(ExpListAssign); | 427 | AST_RULE(ExpListAssign); |
428 | AST_RULE(IfLine); | 428 | AST_RULE(IfLine); |
429 | AST_RULE(WhileLine); | 429 | AST_RULE(WhileLine); |
430 | AST_RULE(Break); | ||
431 | AST_RULE(Continue); | ||
430 | AST_RULE(BreakLoop); | 432 | AST_RULE(BreakLoop); |
431 | AST_RULE(StatementAppendix); | 433 | AST_RULE(StatementAppendix); |
432 | AST_RULE(Statement); | 434 | AST_RULE(Statement); |