From d90857cc05cd0820a2057c547e95b02d24d15412 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 26 Aug 2025 12:50:50 +0800 Subject: Added function argument destructuring. --- spec/inputs/funcs.yue | 31 +++++++++ spec/outputs/funcs.lua | 153 +++++++++++++++++++++++++++++++++++++++++ src/yuescript/yue_ast.h | 2 +- src/yuescript/yue_compiler.cpp | 54 ++++++++++++++- src/yuescript/yue_parser.cpp | 2 +- 5 files changed, 237 insertions(+), 5 deletions(-) diff --git a/spec/inputs/funcs.yue b/spec/inputs/funcs.yue index e647edc..6b1669b 100644 --- a/spec/inputs/funcs.yue +++ b/spec/inputs/funcs.yue @@ -193,4 +193,35 @@ do func = (): -> check 123 print func! -- get nil +do + f = ({:a, :b, :c}) -> print a, b, c + f = (:a, :b, :c) -> print a, b, c + g = (x, :y) -> print x, y + i = ({a: ax = 0, b: by = 0}) -> print ax, by + j = (name, {id: uid = "n/a", :role = "guest"}) -> print name, uid, role + m = ({user: {:name, :age}, meta: {:ver = 1}}) -> print name, age, ver + m1 = ({user: {:name, :age}, :meta = {}}) -> print name, age, meta and meta.ver or "nil" + new = ({:name = "anon", :age = 0}) => + @name = name + @age = age + set = ({:name = @name, :age = @age}) => + @name = name + @age = age + logKV = ({:k, :v}, ...) -> + print "kv:", k, v + print "rest count:", select "#", ... + macro gen = (fname) -> | + #{fname} = ({:a, :b = 0}) -> print a, b + $gen foo + t1 = (:a, x) -> print a, x + t2 = (:a) -> print a + w = ( + id + {:x = 0, :y = 0} + :flag + ) -> + print id, x, y, flag + g1 = ({:a, a: ax}) -> print a, ax + g4 = ({:a, :b, ...rest}) -> print a, b + nil diff --git a/spec/outputs/funcs.lua b/spec/outputs/funcs.lua index c1735c4..db7ed67 100644 --- a/spec/outputs/funcs.lua +++ b/spec/outputs/funcs.lua @@ -283,4 +283,157 @@ do end print(func()) end +local _anon_func_0 = function(_arg_0) + local _accum_0 = { } + local _len_0 = 1 + local _max_0 = #_arg_0 + for _index_0 = 1, _max_0 do + local _item_0 = _arg_0[_index_0] + _accum_0[_len_0] = _item_0 + _len_0 = _len_0 + 1 + end + return _accum_0 +end +do + local f + f = function(_arg_0) + local a, b, c + a, b, c = _arg_0.a, _arg_0.b, _arg_0.c + return print(a, b, c) + end + f = function(_arg_0) + local a, b, c + a, b, c = _arg_0.a, _arg_0.b, _arg_0.c + return print(a, b, c) + end + local g + g = function(x, _arg_0) + local y + y = _arg_0.y + return print(x, y) + end + local i + i = function(_arg_0) + local ax, by + ax, by = _arg_0.a, _arg_0.b + if ax == nil then + ax = 0 + end + if by == nil then + by = 0 + end + return print(ax, by) + end + j = function(name, _arg_0) + local uid, role + uid, role = _arg_0.id, _arg_0.role + if uid == nil then + uid = "n/a" + end + if role == nil then + role = "guest" + end + return print(name, uid, role) + end + local m + m = function(_arg_0) + local name, age, ver + name, age, ver = _arg_0.user.name, _arg_0.user.age, _arg_0.meta.ver + if ver == nil then + ver = 1 + end + return print(name, age, ver) + end + local m1 + m1 = function(_arg_0) + local name, age, meta + name, age, meta = _arg_0.user.name, _arg_0.user.age, _arg_0.meta + if meta == nil then + meta = { } + end + return print(name, age, meta and meta.ver or "nil") + end + local new + new = function(self, _arg_0) + local name, age + name, age = _arg_0.name, _arg_0.age + if name == nil then + name = "anon" + end + if age == nil then + age = 0 + end + self.name = name + self.age = age + end + local set + set = function(self, _arg_0) + local name, age + name, age = _arg_0.name, _arg_0.age + if name == nil then + name = self.name + end + if age == nil then + age = self.age + end + self.name = name + self.age = age + end + local logKV + logKV = function(_arg_0, ...) + local k, v + k, v = _arg_0.k, _arg_0.v + print("kv:", k, v) + return print("rest count:", select("#", ...)) + end + do + local foo + foo = function(_arg_0) + local a, b + a, b = _arg_0.a, _arg_0.b + if b == nil then + b = 0 + end + return print(a, b) + end + end + local t1 + t1 = function(_arg_0, x) + local a + a = _arg_0.a + return print(a, x) + end + local t2 + t2 = function(_arg_0) + local a + a = _arg_0.a + return print(a) + end + local w + w = function(id, _arg_0, _arg_1) + local x, y + x, y = _arg_0.x, _arg_0.y + if x == nil then + x = 0 + end + if y == nil then + y = 0 + end + local flag + flag = _arg_1.flag + return print(id, x, y, flag) + end + local g1 + g1 = function(_arg_0) + local a, ax + a, ax = _arg_0.a, _arg_0.a + return print(a, ax) + end + local g4 + g4 = function(_arg_0) + local a, b, rest + a, b, rest = _arg_0.a, _arg_0.b, _anon_func_0(_arg_0) + return print(a, b) + end +end return nil diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 1937eb8..6e1bb88 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -780,7 +780,7 @@ AST_NODE(Export) AST_END(Export) AST_NODE(FnArgDef) - ast_sel name; + ast_sel name; ast_ptr op; ast_ptr label; ast_ptr defaultValue; diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index d676750..33161a7 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.3"sv; +const std::string_view version = "0.29.4"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -1982,7 +1982,7 @@ private: return indent() + "local "s + join(defs, ", "sv); } - std::string getDestrucureDefine(ExpListAssign_t* assignment) { + std::string getDestructureDefine(ExpListAssign_t* assignment) { auto info = extractDestructureInfo(assignment, true, false); if (!info.destructures.empty()) { str_list defs; @@ -2013,8 +2013,31 @@ private: return clearBuf(); } + str_list getArgDestructureList(ExpListAssign_t* assignment) { + str_list defs; + auto info = extractDestructureInfo(assignment, true, false); + if (!info.destructures.empty()) { + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { + if (item.targetVar.empty()) { + throw CompileError("can only destruct argument to variable"sv, item.target); + } else { + defs.push_back(item.targetVar); + } + } + } else { + const auto& assignment = std::get(des); + YUEE("AST node mismatch", assignment.ptr); + } + } + } + return defs; + } + std::string getPreDefine(ExpListAssign_t* assignment) { - auto preDefine = getDestrucureDefine(assignment); + auto preDefine = getDestructureDefine(assignment); if (preDefine.empty()) { preDefine = toLocalDecl(transformAssignDefs(assignment->expList, DefOp::Mark)); } @@ -5652,6 +5675,7 @@ private: bool checkExistence = false; std::string name; std::string assignSelf; + ast_ptr assignment; }; std::list argItems; str_list temp; @@ -5706,6 +5730,22 @@ private: } break; } + case id(): { + arg.name = getUnusedName("_arg_"sv); + auto simpleValue = def->new_ptr(); + simpleValue->value.set(def->name); + auto asmt = assignmentFrom(newExp(simpleValue, def), toAst(arg.name, def), def); + arg.assignment = asmt; + break; + } + case id(): { + arg.name = getUnusedName("_arg_"sv); + auto value = def->new_ptr(); + value->item.set(def->name); + auto asmt = assignmentFrom(newExp(value, def), toAst(arg.name, def), def); + arg.assignment = asmt; + break; + } default: YUEE("AST node mismatch", def->name.get()); break; } forceAddToScope(arg.name); @@ -5724,6 +5764,14 @@ private: _buf << indent() << "end"sv << nll(def); temp.back() = clearBuf(); } + if (arg.assignment) { + auto names = getArgDestructureList(arg.assignment); + for (const auto& name : names) { + forceAddToScope(name); + } + temp.emplace_back(indent() + "local "s + join(names, ", "sv) + nll(def)); + transformAssignment(arg.assignment, temp); + } if (varNames.empty()) varNames = arg.name; else diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 1942e23..01ca083 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -893,7 +893,7 @@ YueParser::YueParser() { fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line); - FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '`' >> space >> Name) >> -(space >> '=' >> space >> Exp); + FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '`' >> space >> Name) >> -(space >> '=' >> space >> Exp) | TableLit | SimpleTable; FnArgDefList = Seperator >> ( fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg >> -(space >> '`' >> space >> Name)) | -- cgit v1.2.3-55-g6feb