From 5aa41b436b3fdf29f5a0046c68cb60b16fa09eb2 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Fri, 30 Sep 2022 11:29:41 +0800 Subject: fix issue #81, refactor continue with gotos. --- src/3rdParty/lua/lauxlib.c | 8 +- src/3rdParty/lua/lobject.c | 2 +- src/3rdParty/lua/loslib.c | 4 +- src/3rdParty/lua/ltablib.c | 2 +- src/3rdParty/lua/luaconf.h | 5 +- src/3rdParty/lua/lutf8lib.c | 27 ++-- src/3rdParty/lua/lvm.c | 4 +- src/3rdParty/lua/lvm.h | 5 + src/yuescript/yue_ast.h | 1 - src/yuescript/yue_compiler.cpp | 348 ++++++++++++++++++++++++++++------------- 10 files changed, 272 insertions(+), 134 deletions(-) mode change 100755 => 100644 src/yuescript/yue_compiler.cpp (limited to 'src') diff --git a/src/3rdParty/lua/lauxlib.c b/src/3rdParty/lua/lauxlib.c index cba5df9..4ca6c65 100644 --- a/src/3rdParty/lua/lauxlib.c +++ b/src/3rdParty/lua/lauxlib.c @@ -526,14 +526,14 @@ static void newbox (lua_State *L) { /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' -** bytes. (The test for "double is not big enough" also gets the -** case when the multiplication by 2 overflows.) +** bytes. (The test for "not big enough" also gets the case when the +** computation of 'newsize' overflows.) */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { - size_t newsize = B->size * 2; /* double buffer size */ + size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ return luaL_error(B->L, "buffer too large"); - if (newsize < B->n + sz) /* double is not big enough? */ + if (newsize < B->n + sz) /* not big enough? */ newsize = B->n + sz; return newsize; } diff --git a/src/3rdParty/lua/lobject.c b/src/3rdParty/lua/lobject.c index a2c0060..03e2798 100644 --- a/src/3rdParty/lua/lobject.c +++ b/src/3rdParty/lua/lobject.c @@ -62,7 +62,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2); - case LUA_OPSHR: return luaV_shiftl(v1, -v2); + case LUA_OPSHR: return luaV_shiftr(v1, v2); case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; diff --git a/src/3rdParty/lua/loslib.c b/src/3rdParty/lua/loslib.c index 3e20d62..854dcf6 100644 --- a/src/3rdParty/lua/loslib.c +++ b/src/3rdParty/lua/loslib.c @@ -260,9 +260,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { res = d; } else { - /* unsigned avoids overflow when lua_Integer has 32 bits */ - if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta - : (lua_Integer)INT_MIN + delta <= res)) + if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } diff --git a/src/3rdParty/lua/ltablib.c b/src/3rdParty/lua/ltablib.c index 868d78f..e6bc4d0 100644 --- a/src/3rdParty/lua/ltablib.c +++ b/src/3rdParty/lua/ltablib.c @@ -93,7 +93,7 @@ static int tremove (lua_State *L) { lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ /* check whether 'pos' is in [1, size + 1] */ - luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1, + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2, "position out of bounds"); lua_geti(L, 1, pos); /* result = t[pos] */ for ( ; pos < size; pos++) { diff --git a/src/3rdParty/lua/luaconf.h b/src/3rdParty/lua/luaconf.h index fcc0018..e4650fb 100644 --- a/src/3rdParty/lua/luaconf.h +++ b/src/3rdParty/lua/luaconf.h @@ -747,14 +747,15 @@ /* @@ LUA_IDSIZE gives the maximum size for the description of the source -@@ of a function in debug information. +** of a function in debug information. ** CHANGE it if you want a different size. */ #define LUA_IDSIZE 60 /* -@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. */ #define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) diff --git a/src/3rdParty/lua/lutf8lib.c b/src/3rdParty/lua/lutf8lib.c index e7bf098..3a5b9bc 100644 --- a/src/3rdParty/lua/lutf8lib.c +++ b/src/3rdParty/lua/lutf8lib.c @@ -25,6 +25,9 @@ #define MAXUTF 0x7FFFFFFFu + +#define MSGInvalid "invalid UTF-8 code" + /* ** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. */ @@ -35,7 +38,8 @@ typedef unsigned long utfint; #endif -#define iscont(p) ((*(p) & 0xC0) == 0x80) +#define iscont(c) (((c) & 0xC0) == 0x80) +#define iscontp(p) iscont(*(p)) /* from strlib */ @@ -65,7 +69,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) { int count = 0; /* to count number of continuation bytes */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ unsigned int cc = (unsigned char)s[++count]; /* read next byte */ - if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ + if (!iscont(cc)) /* not a continuation byte? */ return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ } @@ -140,7 +144,7 @@ static int codepoint (lua_State *L) { utfint code; s = utf8_decode(s, &code, !lax); if (s == NULL) - return luaL_error(L, "invalid UTF-8 code"); + return luaL_error(L, MSGInvalid); lua_pushinteger(L, code); n++; } @@ -190,16 +194,16 @@ static int byteoffset (lua_State *L) { "position out of bounds"); if (n == 0) { /* find beginning of current byte sequence */ - while (posi > 0 && iscont(s + posi)) posi--; + while (posi > 0 && iscontp(s + posi)) posi--; } else { - if (iscont(s + posi)) + if (iscontp(s + posi)) return luaL_error(L, "initial position is a continuation byte"); if (n < 0) { while (n < 0 && posi > 0) { /* move back */ do { /* find beginning of previous character */ posi--; - } while (posi > 0 && iscont(s + posi)); + } while (posi > 0 && iscontp(s + posi)); n++; } } @@ -208,7 +212,7 @@ static int byteoffset (lua_State *L) { while (n > 0 && posi < (lua_Integer)len) { do { /* find beginning of next character */ posi++; - } while (iscont(s + posi)); /* (cannot pass final '\0') */ + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ n--; } } @@ -226,15 +230,15 @@ static int iter_aux (lua_State *L, int strict) { const char *s = luaL_checklstring(L, 1, &len); lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); if (n < len) { - while (iscont(s + n)) n++; /* skip continuation bytes */ + while (iscontp(s + n)) n++; /* go to next character */ } if (n >= len) /* (also handles original 'n' being negative) */ return 0; /* no more codepoints */ else { utfint code; const char *next = utf8_decode(s + n, &code, strict); - if (next == NULL) - return luaL_error(L, "invalid UTF-8 code"); + if (next == NULL || iscontp(next)) + return luaL_error(L, MSGInvalid); lua_pushinteger(L, n + 1); lua_pushinteger(L, code); return 2; @@ -253,7 +257,8 @@ static int iter_auxlax (lua_State *L) { static int iter_codes (lua_State *L) { int lax = lua_toboolean(L, 2); - luaL_checkstring(L, 1); + const char *s = luaL_checkstring(L, 1); + luaL_argcheck(L, !iscontp(s), 1, MSGInvalid); lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); lua_pushvalue(L, 1); lua_pushinteger(L, 0); diff --git a/src/3rdParty/lua/lvm.c b/src/3rdParty/lua/lvm.c index 614df05..73a19ba 100644 --- a/src/3rdParty/lua/lvm.c +++ b/src/3rdParty/lua/lvm.c @@ -765,12 +765,10 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { /* number of bits in an integer */ #define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + /* ** Shift left operation. (Shift right just negates 'y'.) */ -#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) - - lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { if (y < 0) { /* shift right? */ if (y <= -NBITS) return 0; diff --git a/src/3rdParty/lua/lvm.h b/src/3rdParty/lua/lvm.h index 1bc16f3..dba1ad2 100644 --- a/src/3rdParty/lua/lvm.h +++ b/src/3rdParty/lua/lvm.h @@ -110,6 +110,11 @@ typedef enum { luaC_barrierback(L, gcvalue(t), v); } +/* +** Shift right is the same as shift left with a negative 'y' +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) + LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 1712e8a..ed963be 100755 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -828,7 +828,6 @@ AST_NODE(Statement) Import_t, While_t, Repeat_t, For_t, ForEach_t, Return_t, Local_t, Global_t, Export_t, Macro_t, MacroInPlace_t, BreakLoop_t, Label_t, Goto_t, ShortTabAppending_t, - BreakLoop_t, Label_t, Goto_t, ShortTabAppending_t, Backcall_t, LocalAttrib_t, PipeBody_t, ExpListAssign_t > content; ast_ptr appendix; diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp old mode 100755 new mode 100644 index 1d949fe..672f2d4 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -14,6 +14,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include "yuescript/yue_compiler.h" #include "yuescript/yue_parser.h" @@ -59,7 +60,7 @@ namespace yue { typedef std::list str_list; -const std::string_view version = "0.15.3"sv; +const std::string_view version = "0.15.4"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -146,11 +147,30 @@ public: str_list out; pushScope(); _enableReturn.push(_info.moduleName.empty()); + _gotoScopes.push(0); + _gotoScope = 1; _varArgs.push({true, false}); transformBlock(block, out, config.implicitReturnRoot ? ExpUsage::Return : ExpUsage::Common, nullptr, true); popScope(); + if (!gotos.empty()) { + for (const auto& gotoNode : gotos) { + bool noLabel = true; + BLOCK_START + BREAK_IF(static_cast(_labels.size()) <= gotoNode.scope); + BREAK_IF(_labels[gotoNode.scope] == std::nullopt); + const auto& scope = _labels[gotoNode.scope].value(); + auto it = scope.find(gotoNode.label); + BREAK_IF(it == scope.end()); + BREAK_IF(gotoNode.level < it->second.level); + noLabel = false; + BLOCK_END + if (noLabel) { + throw std::logic_error(_info.errorMessage("no visible label '"s + gotoNode.label + "' for "s, gotoNode.ptr->label)); + } + } + } if (config.lintGlobalVariable) { globals = std::make_unique(); for (const auto& var : _globals) { @@ -256,6 +276,20 @@ private: std::ostringstream _buf; std::ostringstream _joinBuf; const std::string _newLine = "\n"; + int _gotoScope = 0; + std::stack _gotoScopes; + struct LabelNode { + int line; + int level; + }; + std::vector>> _labels; + struct GotoNode { + ast_ptr ptr; + std::string label; + int scope; + int level; + }; + std::list gotos; enum class LocalMode { None = 0, @@ -457,13 +491,30 @@ private: std::string getUnusedName(std::string_view name) const { int index = 0; std::string newName; + std::string nameStr(name); do { - newName = std::string(name) + std::to_string(index); + newName = nameStr + std::to_string(index); index++; } while (isLocal(newName)); return newName; } + std::string getUnusedLabel(std::string_view label) const { + int scopeIndex = _gotoScopes.top(); + if (static_cast(_labels.size()) <= scopeIndex || _labels[scopeIndex] == std::nullopt) { + return std::string(label) + '0'; + } + auto& scope = _labels[scopeIndex].value(); + int index = 0; + std::string newLabel; + std::string labelStr(label); + do { + newLabel = labelStr + std::to_string(index); + index++; + } while (scope.find(newLabel) != scope.end()); + return newLabel; + } + std::string transformCondExp(Exp_t* cond, bool unless) { str_list tmp; if (unless) { @@ -975,6 +1026,17 @@ private: return false; } + void pushFunctionScope() { + _enableReturn.push(true); + _gotoScopes.push(_gotoScope); + _gotoScope++; + } + + void popFunctionScope() { + _enableReturn.pop(); + _gotoScopes.pop(); + } + void pushAnonVarArg() { if (!_varArgs.empty() && _varArgs.top().hasVar) { _varArgs.push({true, false}); @@ -2642,7 +2704,7 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); @@ -2765,7 +2827,7 @@ private: *funcStart = anonFuncStart() + nll(nodes.front()); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); } out.push_back(join(temp)); } @@ -2928,7 +2990,7 @@ private: } std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); @@ -2974,7 +3036,7 @@ private: *funcStart = anonFuncStart() + nll(x); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); } break; } @@ -3091,7 +3153,7 @@ private: } void transformFunLit(FunLit_t* funLit, str_list& out) { - _enableReturn.push(true); + pushFunctionScope(); _varArgs.push({false, false}); bool isFatArrow = _parser.toString(funLit->arrow) == "=>"sv; pushScope(); @@ -3142,7 +3204,7 @@ private: } } out.push_back(clearBuf()); - _enableReturn.pop(); + popFunctionScope(); _varArgs.pop(); } @@ -3979,7 +4041,7 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); @@ -4122,7 +4184,7 @@ private: *funcStart = anonFuncStart() + nll(x); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); break; default: break; @@ -4148,7 +4210,7 @@ private: pushScope(); break; case ExpUsage::Closure: - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); @@ -4226,7 +4288,7 @@ private: *funcStart = anonFuncStart() + nll(x); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); break; default: break; @@ -4283,7 +4345,7 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); @@ -4326,7 +4388,7 @@ private: *funcStart = anonFuncStart() + nll(x); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); break; } case ExpUsage::Return: { @@ -5052,7 +5114,7 @@ private: auto x = values.front(); switch (usage) { case ExpUsage::Closure: - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); pushScope(); break; @@ -5255,7 +5317,7 @@ private: out.back().insert(0, anonFuncStart() + nll(x)); out.back().append(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); break; } case ExpUsage::Assignment: { @@ -5447,7 +5509,7 @@ private: auto x = comp; switch (usage) { case ExpUsage::Closure: - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); pushScope(); break; @@ -5511,7 +5573,7 @@ private: out.back().insert(0, anonFuncStart() + nll(comp)); out.back().append(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); break; } case ExpUsage::Assignment: { @@ -5804,44 +5866,80 @@ private: str_list temp; bool extraDo = false; bool withContinue = hasContinueStatement(body); + int target = getLuaTarget(body); + std::string extraLabel; if (withContinue) { - if (auto block = ast_cast(body)) { - if (!block->statements.empty()) { - auto stmt = static_cast(block->statements.back()); - if (auto breakLoop = ast_cast(stmt->content)) { - extraDo = _parser.toString(breakLoop) == "break"sv; + if (target < 502) { + if (auto block = ast_cast(body)) { + if (!block->statements.empty()) { + auto stmt = static_cast(block->statements.back()); + if (auto breakLoop = ast_cast(stmt->content)) { + extraDo = _parser.toString(breakLoop) == "break"sv; + } } } - } - auto continueVar = getUnusedName("_continue_"sv); - addToScope(continueVar); - _continueVars.push({continueVar, nullptr}); - _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); - _buf << indent() << "repeat"sv << nll(body); - pushScope(); - if (extraDo) { - _buf << indent() << "do"sv << nll(body); + auto continueVar = getUnusedName("_continue_"sv); + addToScope(continueVar); + _continueVars.push({continueVar, nullptr}); + _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); + _buf << indent() << "repeat"sv << nll(body); pushScope(); + if (extraDo) { + _buf << indent() << "do"sv << nll(body); + pushScope(); + } + temp.push_back(clearBuf()); + } else { + auto continueLabel = getUnusedLabel("_continue_"sv); + _continueVars.push({continueLabel, nullptr}); + transformLabel(toAst("::"s + _continueVars.top().var + "::"s, body), temp); + extraLabel = temp.back(); + temp.pop_back(); + } + if (auto block = ast_cast(body); body && !block->statements.empty()) { + auto last = static_cast(block->statements.back()); + if (last->content.is()) { + auto doNode = last->new_ptr(); + auto newBody = last->new_ptr(); + auto newStmt = last->new_ptr(); + newStmt->content.set(last->content); + newBody->content.set(newStmt); + doNode->body.set(newBody); + auto simpleValue = last->new_ptr(); + simpleValue->value.set(doNode); + auto expList = last->new_ptr(); + expList->exprs.push_back(newExp(simpleValue, last)); + auto expListAssign = last->new_ptr(); + expListAssign->expList.set(expList); + last->content.set(expListAssign); + } } - temp.push_back(clearBuf()); } transform_plain_body(body, temp, usage, assignList); if (withContinue) { - if (extraDo) { + if (target < 502) { + if (extraDo) { + 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() << "end"sv << nll(body); - } - if (!appendContent.empty()) { - _buf << indent() << appendContent; + _buf << indent() << "until true"sv << nlr(body); + _buf << indent() << "if not "sv << _continueVars.top().var << " then"sv << nlr(body); + _buf << indent(1) << "break"sv << nlr(body); + _buf << indent() << "end"sv << nlr(body); + temp.push_back(clearBuf()); + _continueVars.pop(); + } else { + if (!appendContent.empty()) { + temp.push_back(indent() + appendContent); + } + temp.push_back(extraLabel); + _continueVars.pop(); } - _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); - popScope(); - _buf << indent() << "until true"sv << nlr(body); - _buf << indent() << "if not "sv << _continueVars.top().var << " then"sv << nlr(body); - _buf << indent(1) << "break"sv << nlr(body); - _buf << indent() << "end"sv << nlr(body); - temp.push_back(clearBuf()); - _continueVars.pop(); } else if (!appendContent.empty()) { temp.back().append(indent() + appendContent); } @@ -5854,56 +5952,67 @@ private: auto body = repeatNode->body->content.get(); bool withContinue = hasContinueStatement(body); std::string conditionVar; + std::string extraLabel; + ast_ptr condAssign; + int target = getLuaTarget(repeatNode); if (withContinue) { - if (auto block = ast_cast(body)) { - if (!block->statements.empty()) { - auto stmt = static_cast(block->statements.back()); - if (auto breakLoop = ast_cast(stmt->content)) { - extraDo = _parser.toString(breakLoop) == "break"sv; + if (target < 502) { + if (auto block = ast_cast(body)) { + if (!block->statements.empty()) { + auto stmt = static_cast(block->statements.back()); + if (auto breakLoop = ast_cast(stmt->content)) { + extraDo = _parser.toString(breakLoop) == "break"sv; + } } } - } - conditionVar = getUnusedName("_cond_"); - forceAddToScope(conditionVar); - auto continueVar = getUnusedName("_continue_"sv); - forceAddToScope(continueVar); - { - auto assignment = toAst(conditionVar + "=nil"s, repeatNode->condition); - auto assign = assignment->action.to(); - assign->values.clear(); - assign->values.push_back(repeatNode->condition); - _continueVars.push({continueVar, assignment.get()}); - } - _buf << indent() << "local "sv << conditionVar << " = false"sv << nll(body); - _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); - _buf << indent() << "repeat"sv << nll(body); - pushScope(); - if (extraDo) { - _buf << indent() << "do"sv << nll(body); + conditionVar = getUnusedName("_cond_"); + forceAddToScope(conditionVar); + auto continueVar = getUnusedName("_continue_"sv); + forceAddToScope(continueVar); + { + auto assignment = toAst(conditionVar + "=nil"s, repeatNode->condition); + auto assign = assignment->action.to(); + assign->values.clear(); + assign->values.push_back(repeatNode->condition); + _continueVars.push({continueVar, assignment.get()}); + } + _buf << indent() << "local "sv << conditionVar << " = false"sv << nll(body); + _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); + _buf << indent() << "repeat"sv << nll(body); pushScope(); + if (extraDo) { + _buf << indent() << "do"sv << nll(body); + pushScope(); + } + temp.push_back(clearBuf()); + } else { + auto continueLabel = getUnusedLabel("_continue_"sv); + _continueVars.push({continueLabel, nullptr}); + transformLabel(toAst("::"s + _continueVars.top().var + "::"s, body), temp); + extraLabel = temp.back(); + temp.pop_back(); } - temp.push_back(clearBuf()); } transform_plain_body(body, temp, ExpUsage::Common); if (withContinue) { - { + if (target < 502) { transformAssignment(_continueVars.top().condAssign, temp); - auto assignCond = std::move(temp.back()); - temp.pop_back(); - temp.back().append(assignCond); - } - if (extraDo) { + if (extraDo) { + popScope(); + _buf << indent() << "end"sv << nll(body); + } + _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); popScope(); - _buf << indent() << "end"sv << nll(body); + _buf << indent() << "until true"sv << nlr(body); + _buf << indent() << "if not "sv << _continueVars.top().var << " then"sv << nlr(body); + _buf << indent(1) << "break"sv << nlr(body); + _buf << indent() << "end"sv << nlr(body); + temp.push_back(clearBuf()); + _continueVars.pop(); + } else { + temp.push_back(extraLabel); + _continueVars.pop(); } - _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); - popScope(); - _buf << indent() << "until true"sv << nlr(body); - _buf << indent() << "if not "sv << _continueVars.top().var << " then"sv << nlr(body); - _buf << indent(1) << "break"sv << nlr(body); - _buf << indent() << "end"sv << nlr(body); - temp.push_back(clearBuf()); - _continueVars.pop(); } out.push_back(join(temp)); return conditionVar; @@ -5937,7 +6046,7 @@ private: void transformForClosure(For_t* forNode, str_list& out) { str_list temp; - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); @@ -5947,7 +6056,7 @@ private: funcStart = anonFuncStart() + nll(forNode); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); out.push_back(join(temp)); } @@ -6026,7 +6135,7 @@ private: void transformForEachClosure(ForEach_t* forEach, str_list& out) { str_list temp; - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); @@ -6036,7 +6145,7 @@ private: funcStart = anonFuncStart() + nll(forEach); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); out.push_back(join(temp)); } @@ -6202,7 +6311,7 @@ private: void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { str_list temp; - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); @@ -6211,7 +6320,7 @@ private: funcStart = anonFuncStart() + nll(classDecl); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); out.push_back(join(temp)); } @@ -6611,7 +6720,7 @@ private: void transformWithClosure(With_t* with, str_list& out) { str_list temp; - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); @@ -6620,7 +6729,7 @@ private: funcStart = anonFuncStart() + nll(with); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); out.push_back(join(temp)); } @@ -6918,7 +7027,7 @@ private: void transformTblComprehension(TblComprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { switch (usage) { case ExpUsage::Closure: - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); pushScope(); break; @@ -6980,7 +7089,7 @@ private: out.back().insert(0, anonFuncStart() + nll(comp)); out.back().append(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); break; case ExpUsage::Assignment: { out.push_back(clearBuf()); @@ -7025,7 +7134,7 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); } else { @@ -7038,7 +7147,7 @@ private: *funcStart = anonFuncStart() + nll(doNode); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); } else { temp.push_back(indent() + "end"s + nlr(doNode)); } @@ -7445,7 +7554,7 @@ private: void transformWhileClosure(While_t* whileNode, str_list& out) { auto x = whileNode; str_list temp; - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); @@ -7469,7 +7578,7 @@ private: funcStart = anonFuncStart() + nll(whileNode); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); - _enableReturn.pop(); + popFunctionScope(); out.push_back(join(temp)); } @@ -7507,7 +7616,7 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { - _enableReturn.push(true); + pushFunctionScope(); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); @@ -7631,7 +7740,7 @@ private: } temp.push_back(indent() + "end"s + nlr(switchNode)); if (usage == ExpUsage::Closure) { - _enableReturn.pop(); + popFunctionScope(); popScope(); *funcStart = anonFuncStart() + nll(switchNode); temp.push_back(indent() + anonFuncEnd()); @@ -7798,29 +7907,52 @@ private: return; } if (_continueVars.empty()) throw std::logic_error(_info.errorMessage("continue is not inside a loop"sv, breakLoop)); + str_list temp; auto& item = _continueVars.top(); if (item.condAssign) { - str_list temp; transformAssignment(item.condAssign, temp); - _buf << temp.back(); } - _buf << indent() << item.var << " = true"sv << nll(breakLoop); - _buf << indent() << "break"sv << nll(breakLoop); - out.push_back(clearBuf()); + if (getLuaTarget(breakLoop) < 502) { + if (!temp.empty()) { + _buf << temp.back(); + } + _buf << indent() << item.var << " = true"sv << nll(breakLoop); + _buf << indent() << "break"sv << nll(breakLoop); + out.push_back(clearBuf()); + } else { + transformGoto(toAst("goto "s + item.var, breakLoop), temp); + out.push_back(join(temp)); + } } void transformLabel(Label_t* label, str_list& out) { if (getLuaTarget(label) < 502) { throw std::logic_error(_info.errorMessage("label statement is not available when not targeting Lua version 5.2 or higher"sv, label)); } - out.push_back(indent() + "::"s + _parser.toString(label->label) + "::"s + nll(label)); + auto labelStr = _parser.toString(label->label); + int currentScope = _gotoScopes.top(); + if (static_cast(_labels.size()) <= currentScope) { + _labels.resize(currentScope + 1, std::nullopt); + _labels[currentScope] = std::unordered_map(); + } + if (!_labels[currentScope]) { + _labels[currentScope] = std::unordered_map(); + } + auto& scope = _labels[currentScope].value(); + if (auto it = scope.find(labelStr); it != scope.end()) { + throw std::logic_error(_info.errorMessage("label '"s + labelStr + "' already defined at line "s + std::to_string(it->second.line), label)); + } + scope[labelStr] = {label->m_begin.m_line, static_cast(_scopes.size())}; + out.push_back(indent() + "::"s + labelStr + "::"s + nll(label)); } void transformGoto(Goto_t* gotoNode, str_list& out) { if (getLuaTarget(gotoNode) < 502) { throw std::logic_error(_info.errorMessage("goto statement is not available when not targeting Lua version 5.2 or higher"sv, gotoNode)); } - out.push_back(indent() + "goto "s + _parser.toString(gotoNode->label) + nll(gotoNode)); + auto labelStr = _parser.toString(gotoNode->label); + gotos.push_back({gotoNode, labelStr, _gotoScopes.top(), static_cast(_scopes.size())}); + out.push_back(indent() + "goto "s + labelStr + nll(gotoNode)); } void transformShortTabAppending(ShortTabAppending_t* tab, str_list& out) { -- cgit v1.2.3-55-g6feb