From 55149f8864c6c3e1c1fff9268384f8302dc5488d Mon Sep 17 00:00:00 2001 From: Li Jin Date: Mon, 15 Sep 2025 18:20:15 +0800 Subject: Fixed issue for reversed indexing. --- doc/docs/doc/README.md | 2 + doc/docs/zh/doc/README.md | 2 + spec/inputs/lists.yue | 133 +++++++++++++++ spec/outputs/codes_from_doc.lua | 4 + spec/outputs/codes_from_doc_zh.lua | 4 + spec/outputs/lists.lua | 339 ++++++++++++++++++++++++++++++++++++- src/yuescript/yue_compiler.cpp | 83 ++++++++- 7 files changed, 557 insertions(+), 10 deletions(-) diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index 57f27c8..5c26dac 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md @@ -641,11 +641,13 @@ You can use the **#** operator to get the last elements of a table. ```moonscript last = data.items[#] second_last = data.items[#-1] +data.items[#] = 1 ```
 last = data.items[#]
 second_last = data.items[#-1]
+data.items[#] = 1
 
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md index 5cfe4e6..29cd032 100755 --- a/doc/docs/zh/doc/README.md +++ b/doc/docs/zh/doc/README.md @@ -640,11 +640,13 @@ merge = {...a, ...b} ```moonscript last = data.items[#] second_last = data.items[#-1] +data.items[#] = 1 ```
 last = data.items[#]
 second_last = data.items[#-1]
+data.items[#] = 1
 
diff --git a/spec/inputs/lists.yue b/spec/inputs/lists.yue index c493b68..f754cc1 100644 --- a/spec/inputs/lists.yue +++ b/spec/inputs/lists.yue @@ -134,4 +134,137 @@ do x?\y?!.z?[# - 3]?[, -3] ) +do + tb = [1, 2, 3] + tb[#] = 40 + tb[# - 1] = 20 + +do + a = b = c = "x" + lst = [] + lst[#] = a + lst[# - 1] = b + +do + x, y, z = 1, 2, 3 + arr = [] + arr[#], head = x, y + arr[#] = z + +do + triple = ["keep", "skip", "tail"] + [head, _, tailv] = triple + buf = [] + buf[#] = head + buf[#] = tailv + +do + src = ["a", "", "c", nil, "d"] + collected = [] + for item in *src + if item and #item > 0 + collected[#] = item + +do + nums = [1, 2, 3, 4, 5] + last_two = [v for v in *nums when v > 3] + nums[#] = last_two[1] + nums[#] = last_two[2] + +do + store = [] + store[#] = { meta: { id: 1, ok: true }, payload: [10, 20] } + store[#] = { meta: { id: 1, ok: false }, payload: [10, 20, 30] } + +do + f = -> + q = [] + tb.tmp = [n for n = 1, 4] + if #tb.tmp >= 3 + q[#] = {head: tb.tmp[1], tail: tb.tmp[#]} + +do + make_pair = (a, b) -> [a, b] + pairs = [] + p1 = make_pair 7, 8 + pairs[#] = p1 + k, v = "key", 42 + pairs[#] = {k: k, v: v} + +do + cfg = {mode: "safe", tags: []} + if cfg.mode == "safe" + cfg.mode = "fast" + cfg.tags[#] = "newbie" + +do + mat = [ [1,2], [3,4], [5,6]] + last_row = mat[#] + rows = [] + rows[#] = last_row[1] + +do + kv = [] + kv[#] = {k: "a", v: 1} + kv[#] = {k: "b", v: 2} + pair_last = kv[#] + dict = {} + dict[pair_last.k] = pair_last.v + dict[pair_last.k] = 3 + +do + base = [ i for i = 1, 4 ] + pack = [] + pack[#] = [ base[1], base[#] ] + pack[#] = { first: base[1], last: base[#] } + +do + opts = {limit: 10} + {:limit, :offset = 0} = opts + pages = [] + pages[#] = {limit: limit, offset: offset} + +do + chain = { a: { b: { c: 0 } }, list: [ {x:0}, {x:0} ] } + chain.a.b.c = 1 + chain.list[1].x = 10 + chain.list[#].x = 20 + chain.list[# - 1] = { x: 30 } + +do + node = {left: {v:0}, right: {v:0}} + bag = [] + { :left, :right } = node + bag[#], left.v, right.v = "k", 1, 2 + +do + a1, a2, a3 = 100, 200, 300 + mix = [] + mix[#], mix[#], meta = a1, a2, {tag: "ok"} + +do + cfg2 = {limit: 5, opts: {flag: false}} + {limit: lim, opts: opt2} = cfg2 + bucket = {xs: []} + bucket.xs[#], bucket.flag, opt2.flags[] = lim, true, 123 + +do + ret2 = ()-> 7, 8 + box = [] + box[#], x1 = ret2! + +do + q = [1, 2] + lastq = q[#] + q[# - 1] = lastq * 10 + +do + mat2 = [[9,8], [7,6]] + t = { hold: nil } + t.hold = mat2[#][1] + +do + f = -> globalTB[#][#] = 1 + f1 = -> globalTB[#][# - 1] + nil diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua index 055e79b..b25753d 100644 --- a/spec/outputs/codes_from_doc.lua +++ b/spec/outputs/codes_from_doc.lua @@ -225,6 +225,8 @@ do local _item_0 = data.items second_last = _item_0[#_item_0 - 1] end +local _obj_0 = data.items +_obj_0[#_obj_0] = 1 local mt = { } local add add = function(self, right) @@ -2664,6 +2666,8 @@ do local _item_0 = data.items second_last = _item_0[#_item_0 - 1] end +local _obj_0 = data.items +_obj_0[#_obj_0] = 1 local mt = { } local add add = function(self, right) diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua index de76829..e70c20c 100644 --- a/spec/outputs/codes_from_doc_zh.lua +++ b/spec/outputs/codes_from_doc_zh.lua @@ -225,6 +225,8 @@ do local _item_0 = data.items second_last = _item_0[#_item_0 - 1] end +local _obj_0 = data.items +_obj_0[#_obj_0] = 1 local mt = { } local add add = function(self, right) @@ -2658,6 +2660,8 @@ do local _item_0 = data.items second_last = _item_0[#_item_0 - 1] end +local _obj_0 = data.items +_obj_0[#_obj_0] = 1 local mt = { } local add add = function(self, right) diff --git a/spec/outputs/lists.lua b/spec/outputs/lists.lua index 2ed7b95..2dd19e5 100644 --- a/spec/outputs/lists.lua +++ b/spec/outputs/lists.lua @@ -470,8 +470,11 @@ do end)()) end local _anon_func_0 = function(globalTB) - local _item_0 = globalTB - local _call_0 = _item_0[#_item_0] + local _call_0 + do + local _item_0 = globalTB + _call_0 = _item_0[#_item_0] + end return _call_0["end"](_call_0, 123) end local _anon_func_1 = function(a) @@ -522,4 +525,336 @@ do return print(_anon_func_0(globalTB), _anon_func_1(a), _anon_func_2(x)) end end +do + local tb = { + 1, + 2, + 3 + } + tb[#tb] = 40 + tb[#tb - 1] = 20 +end +do + a = "x" + b = a + c = a + local lst = { } + lst[#lst] = a + lst[#lst - 1] = b +end +do + local y, z + x, y, z = 1, 2, 3 + local arr = { } + local head + arr[#arr], head = x, y + arr[#arr] = z +end +do + local triple = { + "keep", + "skip", + "tail" + } + local head, tailv = triple[1], triple[3] + local buf = { } + buf[#buf] = head + buf[#buf] = tailv +end +do + local src = { + "a", + "", + "c", + nil, + "d" + } + local collected = { } + for _index_0 = 1, #src do + local item = src[_index_0] + if item and #item > 0 then + collected[#collected] = item + end + end +end +do + local nums = { + 1, + 2, + 3, + 4, + 5 + } + local last_two + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #nums do + local v = nums[_index_0] + if v > 3 then + _accum_0[_len_0] = v + _len_0 = _len_0 + 1 + end + end + last_two = _accum_0 + end + nums[#nums] = last_two[1] + nums[#nums] = last_two[2] +end +do + local store = { } + store[#store] = { + meta = { + id = 1, + ok = true + }, + payload = { + 10, + 20 + } + } + store[#store] = { + meta = { + id = 1, + ok = false + }, + payload = { + 10, + 20, + 30 + } + } +end +local _anon_func_3 = function(tb) + local _item_0 = tb.tmp + return _item_0[#_item_0] +end +do + local f + f = function() + local q = { } + do + local _accum_0 = { } + local _len_0 = 1 + for n = 1, 4 do + _accum_0[_len_0] = n + _len_0 = _len_0 + 1 + end + tb.tmp = _accum_0 + end + if #tb.tmp >= 3 then + q[#q] = { + head = tb.tmp[1], + tail = _anon_func_3(tb) + } + end + end +end +do + local make_pair + make_pair = function(a, b) + return { + a, + b + } + end + local pairs = { } + local p1 = make_pair(7, 8) + pairs[#pairs] = p1 + local k, v = "key", 42 + pairs[#pairs] = { + k = k, + v = v + } +end +do + local cfg = { + mode = "safe", + tags = { } + } + if cfg.mode == "safe" then + cfg.mode = "fast" + local _obj_0 = cfg.tags + _obj_0[#_obj_0] = "newbie" + end +end +do + local mat = { + { + 1, + 2 + }, + { + 3, + 4 + }, + { + 5, + 6 + } + } + local last_row = mat[#mat] + local rows = { } + rows[#rows] = last_row[1] +end +do + local kv = { } + kv[#kv] = { + k = "a", + v = 1 + } + kv[#kv] = { + k = "b", + v = 2 + } + local pair_last = kv[#kv] + local dict = { } + dict[pair_last.k] = pair_last.v + dict[pair_last.k] = 3 +end +do + local base + do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 4 do + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + base = _accum_0 + end + local pack = { } + pack[#pack] = { + base[1], + base[#base] + } + pack[#pack] = { + first = base[1], + last = base[#base] + } +end +do + local opts = { + limit = 10 + } + local limit, offset = opts.limit, opts.offset + if offset == nil then + offset = 0 + end + local pages = { } + pages[#pages] = { + limit = limit, + offset = offset + } +end +do + local chain = { + a = { + b = { + c = 0 + } + }, + list = { + { + x = 0 + }, + { + x = 0 + } + } + } + chain.a.b.c = 1 + chain.list[1].x = 10; + ((function() + local _item_0 = chain.list + return _item_0[#_item_0] + end)()).x = 20 + local _obj_0 = chain.list + _obj_0[#_obj_0 - 1] = { + x = 30 + } +end +do + local node = { + left = { + v = 0 + }, + right = { + v = 0 + } + } + local bag = { } + local left, right = node.left, node.right + bag[#bag], left.v, right.v = "k", 1, 2 +end +do + local a1, a2, a3 = 100, 200, 300 + local mix = { } + local meta + mix[#mix], mix[#mix], meta = a1, a2, { + tag = "ok" + } +end +do + local cfg2 = { + limit = 5, + opts = { + flag = false + } + } + local lim, opt2 = cfg2.limit, cfg2.opts + local bucket = { + xs = { } + } + local _obj_0 = bucket.xs + _obj_0[#_obj_0] = lim + bucket.flag = true + local _obj_1 = opt2.flags + _obj_1[#_obj_1 + 1] = 123 +end +do + local ret2 + ret2 = function() + return 7, 8 + end + local box = { } + local x1 + box[#box], x1 = ret2() +end +do + local q = { + 1, + 2 + } + local lastq = q[#q] + q[#q - 1] = lastq * 10 +end +do + local mat2 = [[9,8], [7,6]] + local t = { + hold = nil + } + t.hold = mat2[#mat2][1] +end +do + local f + f = function() + local _obj_0 + do + local _item_0 = globalTB + _obj_0 = _item_0[#_item_0] + end + _obj_0[#_obj_0] = 1 + end + local f1 + f1 = function() + do + local _item_0 = globalTB + do + local _item_1 = _item_0[#_item_0] + return _item_1[#_item_1 - 1] + end + end + end +end return nil diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 33161a7..d7d117a 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -78,7 +78,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.29.4"sv; +const std::string_view version = "0.29.5"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -1445,6 +1445,7 @@ private: case id(): case id(): case id(): + case id(): return true; } } @@ -2254,7 +2255,8 @@ private: BREAK_IF(!value); auto chainValue = value->item.as(); BREAK_IF(!chainValue); - if (auto dot = ast_cast(chainValue->items.back())) { + auto last = chainValue->items.back(); + if (auto dot = ast_cast(last)) { BREAK_IF(!dot->name.is()); str_list temp; auto [beforeAssignment, afterAssignment] = splitAssignment(); @@ -2285,7 +2287,7 @@ private: } out.push_back(join(temp)); return false; - } else if (ast_is(chainValue->items.back())) { + } else if (ast_is(last)) { str_list temp; auto [beforeAssignment, afterAssignment] = splitAssignment(); if (!beforeAssignment->expList->exprs.empty()) { @@ -2337,6 +2339,44 @@ private: } out.push_back(join(temp)); return false; + } else if (ast_is(last)) { + if (chainValue->items.size() == 1) { + if (_withVars.empty()) { + throw CompileError("short dot/colon syntax must be called within a with block"sv, x); + } else { + break; + } + } + auto tmpChain = chainValue->new_ptr(); + tmpChain->items.dup(chainValue->items); + tmpChain->items.pop_back(); + auto tmpLeft = newExp(tmpChain, tmpChain); + auto leftVar = singleVariableFrom(tmpLeft, AccessType::Read); + if (!leftVar.empty() && isLocal(leftVar)) { + break; + } + leftVar = getUnusedName("_obj_"sv); + auto tmpAsmt = assignmentFrom(toAst(leftVar, tmpLeft), tmpLeft, tmpLeft); + str_list temp; + transformAssignment(tmpAsmt, temp); + auto [beforeAssignment, afterAssignment] = splitAssignment(); + if (!beforeAssignment->expList->exprs.empty()) { + transformAssignment(beforeAssignment, temp); + } + if (vit == values.end()) { + throw CompileError("right value missing"sv, values.front()); + } + auto newChain = chainValue->new_ptr(); + newChain->items.push_back(toAst(leftVar, newChain)); + newChain->items.push_back(chainValue->items.back()); + auto newLeft = newExp(newChain, newChain); + auto newAsmt = assignmentFrom(newLeft, *vit, newLeft); + transformAssignment(newAsmt, temp); + if (!afterAssignment->expList->exprs.empty()) { + transformAssignment(afterAssignment, temp); + } + out.push_back(join(temp)); + return false; } else { break; } @@ -6608,11 +6648,6 @@ private: indexNode->nilCoalesed.set(rIndex->modifier->nilCoalesed); } newChain->items.push_back(indexNode); - auto next = current; - ++next; - for (auto i = next; i != chainList.end(); ++i) { - newChain->items.push_back(*i); - } auto expList = x->new_ptr(); expList->exprs.push_back(newExp(newChain, x)); auto expListAssign = x->new_ptr(); @@ -6627,6 +6662,11 @@ private: auto doNode = x->new_ptr(); doNode->body.set(body); if (usage == ExpUsage::Assignment) { + auto next = current; + ++next; + for (auto i = next; i != chainList.end(); ++i) { + newChain->items.push_back(*i); + } auto assignment = x->new_ptr(); assignment->expList.set(assignList); auto assign = x->new_ptr(); @@ -6637,6 +6677,33 @@ private: transformAssignment(assignment, out); return; } + if (usage == ExpUsage::Closure) { + auto next = current; + ++next; + if (next != chainList.end()) { + doNode->new_ptr(); + auto dVal = doNode->new_ptr(); + dVal->value.set(doNode); + auto dExp = newExp(dVal, dVal); + auto dParen = dExp->new_ptr(); + dParen->extra = true; + dParen->expr.set(dExp); + auto dCallable = dExp->new_ptr(); + dCallable->item.set(dParen); + auto dChain = doNode->new_ptr(); + dChain->items.push_back(dCallable); + for (auto i = next; i != chainList.end(); ++i) { + dChain->items.push_back(*i); + } + transformExp(newExp(dChain, dExp), out, usage); + return; + } + } + auto next = current; + ++next; + for (auto i = next; i != chainList.end(); ++i) { + newChain->items.push_back(*i); + } transformDo(doNode, out, usage); return; } -- cgit v1.2.3-55-g6feb