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. --- makefile | 2 + spec/inputs/goto.yue | 4 +- spec/inputs/loops.yue | 8 +- spec/inputs/test/loops_spec.yue | 6 +- spec/outputs/5.1/loops.lua | 381 +++++++++++++++++++++++++++++++++++ spec/outputs/5.1/test/loops_spec.lua | 69 +++++++ spec/outputs/goto.lua | 4 +- spec/outputs/loops.lua | 204 ++++++------------- spec/outputs/test/loops_spec.lua | 51 ++--- 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 ++++++++++++++++++++++---------- 19 files changed, 811 insertions(+), 324 deletions(-) create mode 100644 spec/outputs/5.1/loops.lua create mode 100644 spec/outputs/5.1/test/loops_spec.lua mode change 100755 => 100644 src/yuescript/yue_compiler.cpp diff --git a/makefile b/makefile index 11e59f3..0c86ccb 100644 --- a/makefile +++ b/makefile @@ -282,6 +282,8 @@ gen: release @$(START_TIME) @./$(BIN_NAME) $(TEST_INPUT) -t $(GEN_OUTPUT) --tl_enabled @./$(BIN_NAME) $(TEST_INPUT)/teal-lang.yue -o $(GEN_OUTPUT)/teal-lang.lua + @./$(BIN_NAME) $(TEST_INPUT)/loops.yue -o $(GEN_OUTPUT)/5.1/loops.lua --target=5.1 + @./$(BIN_NAME) $(TEST_INPUT)/test/loops_spec.yue -o $(GEN_OUTPUT)/5.1/test/loops_spec.lua --target=5.1 @echo -en "Compile time: " @$(END_TIME) diff --git a/spec/inputs/goto.yue b/spec/inputs/goto.yue index 1197251..61584ca 100644 --- a/spec/inputs/goto.yue +++ b/spec/inputs/goto.yue @@ -10,8 +10,8 @@ do for z = 1, 10 do for y = 1, 10 do for x = 1, 10 if x^2 + y^2 == z^2 print 'found a Pythagorean triple:', x, y, z - goto done - ::done:: + goto ok + ::ok:: do for z = 1, 10 diff --git a/spec/inputs/loops.yue b/spec/inputs/loops.yue index d03e661..d997c65 100644 --- a/spec/inputs/loops.yue +++ b/spec/inputs/loops.yue @@ -111,10 +111,10 @@ until a == 10 x = 0 repeat - x += 1 - y = x - continue if x < 5 - print y + x += 1 + y = x + continue if x < 5 + print y until y == 10 a = 3 diff --git a/spec/inputs/test/loops_spec.yue b/spec/inputs/test/loops_spec.yue index 817919f..a115581 100644 --- a/spec/inputs/test/loops_spec.yue +++ b/spec/inputs/test/loops_spec.yue @@ -5,8 +5,8 @@ describe "loops", -> continue if x % 2 == 1 x - assert.same output, { 2,4,6 } - + assert.same { 2,4,6 }, output + it "continue in repeat", -> output = {} a = 0 @@ -19,5 +19,5 @@ describe "loops", -> output[] = a until a == 8 - assert.same output, { 1,2,4 } + assert.same { 1,2,4 }, output diff --git a/spec/outputs/5.1/loops.lua b/spec/outputs/5.1/loops.lua new file mode 100644 index 0000000..a3b9548 --- /dev/null +++ b/spec/outputs/5.1/loops.lua @@ -0,0 +1,381 @@ +for x = 1, 10 do + print("yeah") +end +for x = 1, #something do + print("yeah") +end +for y = 100, 60, -3 do + print("count down", y) +end +for a = 1, 10 do + print("okay") +end +for a = 1, 10 do + for b = 2, 43 do + print(a, b) + end +end +for i in iter do + for j in yeah do + local x = 343 + i + j + print(i, j) + end +end +local _list_0 = something +for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + print(x) +end +for k, v in pairs(hello) do + print(k, v) +end +for x in y, z do + print(x) +end +for x in y, z, k do + print(x) +end +local _list_1 = modules +for _index_0 = 1, #_list_1 do + local name, members = _list_1[_index_0] + print(name, member) +end +local x +x = function() + for x in y do + local _ = y + end +end +local hello = { + 1, + 2, + 3, + 4, + 5 +} +do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #hello do + local y = hello[_index_0] + if y % 2 == 0 then + _accum_0[_len_0] = y + end + _len_0 = _len_0 + 1 + end + x = _accum_0 +end +x = function() + for _index_0 = 1, #hello do + local x = hello[_index_0] + local _ = y + end +end +local t +do + local _accum_0 = { } + local _len_0 = 1 + for i = 10, 20 do + _accum_0[_len_0] = i * 2 + _len_0 = _len_0 + 1 + end + t = _accum_0 +end +local hmm = 0 +local y +do + local _accum_0 = { } + local _len_0 = 1 + for j = 3, 30, 8 do + hmm = hmm + 1 + _accum_0[_len_0] = j * hmm + _len_0 = _len_0 + 1 + end + y = _accum_0 +end +local _ +_ = function() + for k = 10, 40 do + _ = "okay" + end +end +_ = function() + local _accum_0 = { } + local _len_0 = 1 + for k = 10, 40 do + _accum_0[_len_0] = "okay" + _len_0 = _len_0 + 1 + end + return _accum_0 +end +while true do + print("name") +end +while 5 + 5 do + print("okay world") + working(man) +end +while also do + i(work(too)) + _ = "okay" +end +local i = 0 +do + local _accum_0 = { } + local _len_0 = 1 + while i < 10 do + i = i + 1 + _len_0 = _len_0 + 1 + end + x = _accum_0 +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" + _len_0 = _len_0 + 1 + end + x = _accum_0 +end +do + local _accum_0 = { } + local _len_0 = 1 + for x = 1, 2 do + y = "hello" + _len_0 = _len_0 + 1 + end + x = _accum_0 +end +while true do + local _continue_0 = false + repeat + if false then + _continue_0 = true + break + end + print("yes") + if true then + break + end + print("no") + _continue_0 = true + until true + if not _continue_0 then + break + end +end +for i = 1, 10 do + while true do + local _continue_0 = false + repeat + do + if not true then + _continue_0 = true + break + end + break + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end +end +local a = 1 +repeat + local _cond_0 = false + local _continue_0 = false + repeat + a = a + 1 + if a == 5 then + _cond_0 = a == 10 + _continue_0 = true + break + end + if a == 6 then + break + end + print(a) + _cond_0 = a == 10 + _continue_0 = true + until true + if not _continue_0 then + break + end +until _cond_0 +x = 0 +repeat + local _cond_0 = false + local _continue_0 = false + repeat + x = x + 1 + y = x + if x < 5 then + _cond_0 = y == 10 + _continue_0 = true + break + end + print(y) + _cond_0 = y == 10 + _continue_0 = true + until true + if not _continue_0 then + break + end +until _cond_0 +a = 3 +while not (a == 0) do + a = a - 1 +end +local done = false +while not done do + done = true +end +repeat + print("hello") +until true +while not done do + x = 10 + repeat + x = x - 1 + until x == 0 +end +while not cond do + print("okay") +end +for x = 1, 10 do + local _continue_0 = false + repeat + if x > 3 and x < 7 then + _continue_0 = true + break + end + print(x) + _continue_0 = true + until true + if not _continue_0 then + break + end +end +local list +do + local _accum_0 = { } + local _len_0 = 1 + for x = 1, 10 do + local _continue_0 = false + repeat + if x > 3 and x < 7 then + _continue_0 = true + break + end + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + list = _accum_0 +end +local _list_2 = { + 1, + 2, + 3, + 4, + 5, + 6 +} +for _index_0 = 1, #_list_2 do + local a = _list_2[_index_0] + local _continue_0 = false + repeat + if a == 1 then + _continue_0 = true + break + end + if a == 3 then + _continue_0 = true + break + end + print(a) + _continue_0 = true + until true + if not _continue_0 then + break + end +end +for x = 1, 10 do + local _continue_0 = false + repeat + if x % 2 == 0 then + _continue_0 = true + break + end + for y = 2, 12 do + local _continue_1 = false + repeat + if y % 3 == 0 then + _continue_1 = true + break + end + _continue_1 = true + until true + if not _continue_1 then + break + end + end + _continue_0 = true + until true + if not _continue_0 then + break + end +end +while true do + local _continue_0 = false + repeat + do + if false then + _continue_0 = true + break + end + break + end + _continue_0 = true + until true + if not _continue_0 then + break + end +end +while true do + local _continue_0 = false + repeat + if false then + _continue_0 = true + break + end + do + return 22 + end + _continue_0 = true + until true + if not _continue_0 then + break + end +end +do + local xxx = { + 1, + 2, + 3, + 4 + } + for _index_0 = 1, #xxx do + local thing = xxx[_index_0] + print(thing) + end +end diff --git a/spec/outputs/5.1/test/loops_spec.lua b/spec/outputs/5.1/test/loops_spec.lua new file mode 100644 index 0000000..bad17a6 --- /dev/null +++ b/spec/outputs/5.1/test/loops_spec.lua @@ -0,0 +1,69 @@ +return describe("loops", function() + it("should continue", function() + local input = { + 1, + 2, + 3, + 4, + 5, + 6 + } + local output + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #input do + local x = input[_index_0] + local _continue_0 = false + repeat + if x % 2 == 1 then + _continue_0 = true + break + end + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + output = _accum_0 + end + return assert.same({ + 2, + 4, + 6 + }, output) + end) + return it("continue in repeat", function() + local output = { } + local a = 0 + repeat + local _cond_0 = false + local _continue_0 = false + repeat + a = a + 1 + if a == 3 then + _cond_0 = a == 8 + _continue_0 = true + break + end + if a == 5 then + break + end + output[#output + 1] = a + _cond_0 = a == 8 + _continue_0 = true + until true + if not _continue_0 then + break + end + until _cond_0 + return assert.same({ + 1, + 2, + 4 + }, output) + end) +end) diff --git a/spec/outputs/goto.lua b/spec/outputs/goto.lua index d1f7b77..421508a 100644 --- a/spec/outputs/goto.lua +++ b/spec/outputs/goto.lua @@ -14,12 +14,12 @@ do for x = 1, 10 do if x ^ 2 + y ^ 2 == z ^ 2 then print('found a Pythagorean triple:', x, y, z) - goto done + goto ok end end end end - ::done:: + ::ok:: end do for z = 1, 10 do diff --git a/spec/outputs/loops.lua b/spec/outputs/loops.lua index e5c189e..9c16ee4 100644 --- a/spec/outputs/loops.lua +++ b/spec/outputs/loops.lua @@ -150,83 +150,47 @@ do x = _accum_0 end while true do - local _continue_0 = false - repeat - if false then - _continue_0 = true - break - end - print("yes") - if true then - break - end - print("no") - _continue_0 = true - until true - if not _continue_0 then + if false then + goto _continue_0 + end + print("yes") + if true then break end + print("no") + ::_continue_0:: end for i = 1, 10 do while true do - local _continue_0 = false - repeat - do - if not true then - _continue_0 = true - break - end - break - end - _continue_0 = true - until true - if not _continue_0 then - break + if not true then + goto _continue_1 end + break + ::_continue_1:: end end local a = 1 repeat - local _cond_0 = false - local _continue_0 = false - repeat - a = a + 1 - if a == 5 then - _cond_0 = a == 10 - _continue_0 = true - break - end - if a == 6 then - break - end - print(a) - _cond_0 = a == 10 - _continue_0 = true - until true - if not _continue_0 then + a = a + 1 + if a == 5 then + goto _continue_2 + end + if a == 6 then break end -until _cond_0 + print(a) + ::_continue_2:: +until a == 10 x = 0 repeat - local _cond_0 = false - local _continue_0 = false - repeat - x = x + 1 - y = x - if x < 5 then - _cond_0 = y == 10 - _continue_0 = true - break - end - print(y) - _cond_0 = y == 10 - _continue_0 = true - until true - if not _continue_0 then - break + x = x + 1 + y = x + if x < 5 then + goto _continue_3 end -until _cond_0 + print(y) + ::_continue_3:: +until y == 10 a = 3 while not (a == 0) do a = a - 1 @@ -248,37 +212,23 @@ while not cond do print("okay") end for x = 1, 10 do - local _continue_0 = false - repeat - if x > 3 and x < 7 then - _continue_0 = true - break - end - print(x) - _continue_0 = true - until true - if not _continue_0 then - break + if x > 3 and x < 7 then + goto _continue_4 end + print(x) + ::_continue_4:: end local list do local _accum_0 = { } local _len_0 = 1 for x = 1, 10 do - local _continue_0 = false - repeat - if x > 3 and x < 7 then - _continue_0 = true - break - end - _accum_0[_len_0] = x - _len_0 = _len_0 + 1 - _continue_0 = true - until true - if not _continue_0 then - break + if x > 3 and x < 7 then + goto _continue_5 end + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + ::_continue_5:: end list = _accum_0 end @@ -292,78 +242,42 @@ local _list_2 = { } for _index_0 = 1, #_list_2 do local a = _list_2[_index_0] - local _continue_0 = false - repeat - if a == 1 then - _continue_0 = true - break - end - if a == 3 then - _continue_0 = true - break - end - print(a) - _continue_0 = true - until true - if not _continue_0 then - break + if a == 1 then + goto _continue_6 end + if a == 3 then + goto _continue_6 + end + print(a) + ::_continue_6:: end for x = 1, 10 do - local _continue_0 = false - repeat - if x % 2 == 0 then - _continue_0 = true - break - end - for y = 2, 12 do - local _continue_1 = false - repeat - if y % 3 == 0 then - _continue_1 = true - break - end - _continue_1 = true - until true - if not _continue_1 then - break - end + if x % 2 == 0 then + goto _continue_7 + end + for y = 2, 12 do + if y % 3 == 0 then + goto _continue_8 end - _continue_0 = true - until true - if not _continue_0 then - break + ::_continue_8:: end + ::_continue_7:: end while true do - local _continue_0 = false - repeat - do - if false then - _continue_0 = true - break - end - break - end - _continue_0 = true - until true - if not _continue_0 then - break + if false then + goto _continue_9 end + break + ::_continue_9:: end while true do - local _continue_0 = false - repeat - if false then - _continue_0 = true - break - end + if false then + goto _continue_10 + end + do return 22 - _continue_0 = true - until true - if not _continue_0 then - break end + ::_continue_10:: end do local xxx = { diff --git a/spec/outputs/test/loops_spec.lua b/spec/outputs/test/loops_spec.lua index 34f2e9c..9c85e1a 100644 --- a/spec/outputs/test/loops_spec.lua +++ b/spec/outputs/test/loops_spec.lua @@ -14,56 +14,39 @@ return describe("loops", function() local _len_0 = 1 for _index_0 = 1, #input do local x = input[_index_0] - local _continue_0 = false - repeat - if x % 2 == 1 then - _continue_0 = true - break - end - _accum_0[_len_0] = x - _len_0 = _len_0 + 1 - _continue_0 = true - until true - if not _continue_0 then - break + if x % 2 == 1 then + goto _continue_0 end + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + ::_continue_0:: end output = _accum_0 end - return assert.same(output, { + return assert.same({ 2, 4, 6 - }) + }, output) end) return it("continue in repeat", function() local output = { } local a = 0 repeat - local _cond_0 = false - local _continue_0 = false - repeat - a = a + 1 - if a == 3 then - _cond_0 = a == 8 - _continue_0 = true - break - end - if a == 5 then - break - end - output[#output + 1] = a - _cond_0 = a == 8 - _continue_0 = true - until true - if not _continue_0 then + a = a + 1 + if a == 3 then + goto _continue_0 + end + if a == 5 then break end - until _cond_0 - return assert.same(output, { + output[#output + 1] = a + ::_continue_0:: + until a == 8 + return assert.same({ 1, 2, 4 - }) + }, output) end) end) 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