aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2025-12-25 00:00:08 +0800
committerLi Jin <dragon-fly@qq.com>2025-12-25 00:00:08 +0800
commit7ee395d918b97795f151c24ed877bfcc2edf602a (patch)
tree0fbb5f4d3011ee6459e011416a1307d74cf331a7
parentbb563257953628a9849c8532a684b5064bf8e655 (diff)
downloadyuescript-7ee395d918b97795f151c24ed877bfcc2edf602a.tar.gz
yuescript-7ee395d918b97795f151c24ed877bfcc2edf602a.tar.bz2
yuescript-7ee395d918b97795f151c24ed877bfcc2edf602a.zip
Added named vararg support.
-rwxr-xr-xdoc/docs/doc/README.md49
-rwxr-xr-xdoc/docs/zh/doc/README.md49
-rw-r--r--spec/inputs/vararg.yue44
-rw-r--r--spec/outputs/codes_from_doc.lua60
-rw-r--r--spec/outputs/codes_from_doc_zh.lua60
-rw-r--r--spec/outputs/vararg.lua72
-rw-r--r--src/yuescript/yue_ast.cpp3
-rw-r--r--src/yuescript/yue_ast.h7
-rw-r--r--src/yuescript/yue_compiler.cpp29
-rw-r--r--src/yuescript/yue_parser.cpp5
-rw-r--r--src/yuescript/yue_parser.h1
11 files changed, 369 insertions, 10 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index 1a46e59..d2f838d 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -1528,6 +1528,55 @@ print ok, count, first
1528</pre> 1528</pre>
1529</YueDisplay> 1529</YueDisplay>
1530 1530
1531### Named Varargs
1532
1533You can use the `(...t) ->` syntax to automatically store varargs into a named table. This table will contain all passed arguments (including `nil` values), and the `n` field of the table will store the actual number of arguments passed (including `nil` values).
1534
1535```moonscript
1536f = (...t) ->
1537 print "argument count:", t.n
1538 print "table length:", #t
1539 for i = 1, t.n
1540 print t[i]
1541
1542f 1, 2, 3
1543f "a", "b", "c", "d"
1544f!
1545
1546-- Handling cases with nil values
1547process = (...args) ->
1548 sum = 0
1549 for i = 1, args.n
1550 if args[i] != nil and type(args[i]) == "number"
1551 sum += args[i]
1552 sum
1553
1554process 1, nil, 3, nil, 5
1555```
1556<YueDisplay>
1557<pre>
1558f = (...t) ->
1559 print "argument count:", t.n
1560 print "table length:", #t
1561 for i = 1, t.n
1562 print t[i]
1563
1564f 1, 2, 3
1565f "a", "b", "c", "d"
1566f!
1567
1568-- Handling cases with nil values
1569process = (...args) ->
1570 sum = 0
1571 for i = 1, args.n
1572 if args[i] != nil and type(args[i]) == "number"
1573 sum += args[i]
1574 sum
1575
1576process 1, nil, 3, nil, 5
1577</pre>
1578</YueDisplay>
1579
1531## Whitespace 1580## Whitespace
1532 1581
1533YueScript is a whitespace significant language. You have to write some code block in the same indent with space **' '** or tab **'\t'** like function body, value list and some control blocks. And expressions containing different whitespaces might mean different things. Tab is treated like 4 space, but it's better not mix the use of spaces and tabs. 1582YueScript is a whitespace significant language. You have to write some code block in the same indent with space **' '** or tab **'\t'** like function body, value list and some control blocks. And expressions containing different whitespaces might mean different things. Tab is treated like 4 space, but it's better not mix the use of spaces and tabs.
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md
index b348e06..34d577c 100755
--- a/doc/docs/zh/doc/README.md
+++ b/doc/docs/zh/doc/README.md
@@ -1526,6 +1526,55 @@ print ok, count, first
1526</pre> 1526</pre>
1527</YueDisplay> 1527</YueDisplay>
1528 1528
1529### 命名变长参数
1530
1531你可以使用 `(...t) ->` 语法来将变长参数自动存储到一个命名表中。这个表会包含所有传入的参数(包括 `nil` 值),并且会在表的 `n` 字段中存储实际传入的参数个数(包括 `nil` 值在内的个数)。
1532
1533```moonscript
1534f = (...t) ->
1535 print "参数个数:", t.n
1536 print "表长度:", #t
1537 for i = 1, t.n
1538 print t[i]
1539
1540f 1, 2, 3
1541f "a", "b", "c", "d"
1542f!
1543
1544-- 处理包含 nil 的情况
1545process = (...args) ->
1546 sum = 0
1547 for i = 1, args.n
1548 if args[i] != nil and type(args[i]) == "number"
1549 sum += args[i]
1550 sum
1551
1552process 1, nil, 3, nil, 5
1553```
1554<YueDisplay>
1555<pre>
1556f = (...t) ->
1557 print "参数个数:", t.n
1558 print "表长度:", #t
1559 for i = 1, t.n
1560 print t[i]
1561
1562f 1, 2, 3
1563f "a", "b", "c", "d"
1564f!
1565
1566-- 处理包含 nil 的情况
1567process = (...args) ->
1568 sum = 0
1569 for i = 1, args.n
1570 if args[i] != nil and type(args[i]) == "number"
1571 sum += args[i]
1572 sum
1573
1574process 1, nil, 3, nil, 5
1575</pre>
1576</YueDisplay>
1577
1529## 空白 1578## 空白
1530 1579
1531月之脚本是一个对空白敏感的语言。你必须在相同的缩进中使用空格 **' '** 或制表符 **'\t'** 来编写一些代码块,如函数体、值列表和一些控制块。包含不同空白的表达式可能意味着不同的事情。制表符被视为4个空格,但最好不要混合使用空格和制表符。 1580月之脚本是一个对空白敏感的语言。你必须在相同的缩进中使用空格 **' '** 或制表符 **'\t'** 来编写一些代码块,如函数体、值列表和一些控制块。包含不同空白的表达式可能意味着不同的事情。制表符被视为4个空格,但最好不要混合使用空格和制表符。
diff --git a/spec/inputs/vararg.yue b/spec/inputs/vararg.yue
index 6100250..4f8a0d7 100644
--- a/spec/inputs/vararg.yue
+++ b/spec/inputs/vararg.yue
@@ -86,3 +86,47 @@ join = (...) ->
86 print ... 86 print ...
87 nil 87 nil
88 88
89do
90 f1 = (...t) ->
91 print t.n
92 print #t
93 for i = 1, t.n
94 print t[i]
95
96 f1 1, 2, 3
97 f1 "a", "b", "c", "d"
98 f1!
99
100 f2 = (...args) ->
101 print "args count:", args.n
102 print "args length:", #args
103 for i = 1, args.n
104 if args[i] == nil
105 print "position", i, "is nil"
106 else
107 print "position", i, ":", args[i]
108
109 f2 1, nil, 3, nil, 5
110
111 f3 = (prefix, ...items) ->
112 result = {}
113 for i = 1, items.n
114 result[i] = prefix .. tostring items[i]
115 result
116
117 f3 "item_", 1, 2, 3
118
119 f4 = (...empty) ->
120 print "empty count:", empty.n
121 print "empty length:", #empty
122
123 f4!
124
125 process = (...data) ->
126 sum = 0
127 for i = 1, data.n
128 if type(data[i]) == "number"
129 sum += data[i]
130 sum
131
132 process 1, 2, 3, "skip", 5
diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua
index d6617a9..de5abdd 100644
--- a/spec/outputs/codes_from_doc.lua
+++ b/spec/outputs/codes_from_doc.lua
@@ -737,6 +737,36 @@ end
737 local first = select(1, ...) 737 local first = select(1, ...)
738 return print(ok, count, first) 738 return print(ok, count, first)
739end)(fn(true)) 739end)(fn(true))
740local f
741f = function(...)
742 local t = {
743 n = select("#", ...),
744 ...
745 }
746 print("argument count:", t.n)
747 print("table length:", #t)
748 for i = 1, t.n do
749 print(t[i])
750 end
751end
752f(1, 2, 3)
753f("a", "b", "c", "d")
754f()
755local process
756process = function(...)
757 local args = {
758 n = select("#", ...),
759 ...
760 }
761 local sum = 0
762 for i = 1, args.n do
763 if args[i] ~= nil and type(args[i]) == "number" then
764 sum = sum + args[i]
765 end
766 end
767 return sum
768end
769process(1, nil, 3, nil, 5)
740Rx.Observable.fromRange(1, 8):filter(function(x) 770Rx.Observable.fromRange(1, 8):filter(function(x)
741 return x % 2 == 0 771 return x % 2 == 0
742end):concat(Rx.Observable.of('who do we appreciate')):map(function(value) 772end):concat(Rx.Observable.of('who do we appreciate')):map(function(value)
@@ -3215,6 +3245,36 @@ end
3215 local first = select(1, ...) 3245 local first = select(1, ...)
3216 return print(ok, count, first) 3246 return print(ok, count, first)
3217end)(fn(true)) 3247end)(fn(true))
3248local f
3249f = function(...)
3250 local t = {
3251 n = select("#", ...),
3252 ...
3253 }
3254 print("argument count:", t.n)
3255 print("table length:", #t)
3256 for i = 1, t.n do
3257 print(t[i])
3258 end
3259end
3260f(1, 2, 3)
3261f("a", "b", "c", "d")
3262f()
3263local process
3264process = function(...)
3265 local args = {
3266 n = select("#", ...),
3267 ...
3268 }
3269 local sum = 0
3270 for i = 1, args.n do
3271 if args[i] ~= nil and type(args[i]) == "number" then
3272 sum = sum + args[i]
3273 end
3274 end
3275 return sum
3276end
3277process(1, nil, 3, nil, 5)
3218Rx.Observable.fromRange(1, 8):filter(function(x) 3278Rx.Observable.fromRange(1, 8):filter(function(x)
3219 return x % 2 == 0 3279 return x % 2 == 0
3220end):concat(Rx.Observable.of('who do we appreciate')):map(function(value) 3280end):concat(Rx.Observable.of('who do we appreciate')):map(function(value)
diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua
index ec84112..6a6c38c 100644
--- a/spec/outputs/codes_from_doc_zh.lua
+++ b/spec/outputs/codes_from_doc_zh.lua
@@ -737,6 +737,36 @@ end
737 local first = select(1, ...) 737 local first = select(1, ...)
738 return print(ok, count, first) 738 return print(ok, count, first)
739end)(fn(true)) 739end)(fn(true))
740local f
741f = function(...)
742 local t = {
743 n = select("#", ...),
744 ...
745 }
746 print("参数个数:", t.n)
747 print("表长度:", #t)
748 for i = 1, t.n do
749 print(t[i])
750 end
751end
752f(1, 2, 3)
753f("a", "b", "c", "d")
754f()
755local process
756process = function(...)
757 local args = {
758 n = select("#", ...),
759 ...
760 }
761 local sum = 0
762 for i = 1, args.n do
763 if args[i] ~= nil and type(args[i]) == "number" then
764 sum = sum + args[i]
765 end
766 end
767 return sum
768end
769process(1, nil, 3, nil, 5)
740Rx.Observable.fromRange(1, 8):filter(function(x) 770Rx.Observable.fromRange(1, 8):filter(function(x)
741 return x % 2 == 0 771 return x % 2 == 0
742end):concat(Rx.Observable.of('who do we appreciate')):map(function(value) 772end):concat(Rx.Observable.of('who do we appreciate')):map(function(value)
@@ -3209,6 +3239,36 @@ end
3209 local first = select(1, ...) 3239 local first = select(1, ...)
3210 return print(ok, count, first) 3240 return print(ok, count, first)
3211end)(fn(true)) 3241end)(fn(true))
3242local f
3243f = function(...)
3244 local t = {
3245 n = select("#", ...),
3246 ...
3247 }
3248 print("参数个数:", t.n)
3249 print("表长度:", #t)
3250 for i = 1, t.n do
3251 print(t[i])
3252 end
3253end
3254f(1, 2, 3)
3255f("a", "b", "c", "d")
3256f()
3257local process
3258process = function(...)
3259 local args = {
3260 n = select("#", ...),
3261 ...
3262 }
3263 local sum = 0
3264 for i = 1, args.n do
3265 if args[i] ~= nil and type(args[i]) == "number" then
3266 sum = sum + args[i]
3267 end
3268 end
3269 return sum
3270end
3271process(1, nil, 3, nil, 5)
3212Rx.Observable.fromRange(1, 8):filter(function(x) 3272Rx.Observable.fromRange(1, 8):filter(function(x)
3213 return x % 2 == 0 3273 return x % 2 == 0
3214end):concat(Rx.Observable.of('who do we appreciate')):map(function(value) 3274end):concat(Rx.Observable.of('who do we appreciate')):map(function(value)
diff --git a/spec/outputs/vararg.lua b/spec/outputs/vararg.lua
index 254aa6a..9f97681 100644
--- a/spec/outputs/vararg.lua
+++ b/spec/outputs/vararg.lua
@@ -294,3 +294,75 @@ join = function(...)
294 end 294 end
295 return nil 295 return nil
296end 296end
297do
298 local f1
299 f1 = function(...)
300 local t = {
301 n = select("#", ...),
302 ...
303 }
304 print(t.n)
305 print(#t)
306 for i = 1, t.n do
307 print(t[i])
308 end
309 end
310 f1(1, 2, 3)
311 f1("a", "b", "c", "d")
312 f1()
313 local f2
314 f2 = function(...)
315 local args = {
316 n = select("#", ...),
317 ...
318 }
319 print("args count:", args.n)
320 print("args length:", #args)
321 for i = 1, args.n do
322 if args[i] == nil then
323 print("position", i, "is nil")
324 else
325 print("position", i, ":", args[i])
326 end
327 end
328 end
329 f2(1, nil, 3, nil, 5)
330 local f3
331 f3 = function(prefix, ...)
332 local items = {
333 n = select("#", ...),
334 ...
335 }
336 local result = { }
337 for i = 1, items.n do
338 result[i] = prefix .. tostring(items[i])
339 end
340 return result
341 end
342 f3("item_", 1, 2, 3)
343 local f4
344 f4 = function(...)
345 local empty = {
346 n = select("#", ...),
347 ...
348 }
349 print("empty count:", empty.n)
350 return print("empty length:", #empty)
351 end
352 f4()
353 local process
354 process = function(...)
355 local data = {
356 n = select("#", ...),
357 ...
358 }
359 local sum = 0
360 for i = 1, data.n do
361 if type(data[i]) == "number" then
362 sum = sum + data[i]
363 end
364 end
365 return sum
366 end
367 return process(1, 2, 3, "skip", 5)
368end
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp
index be0ec45..55b5d03 100644
--- a/src/yuescript/yue_ast.cpp
+++ b/src/yuescript/yue_ast.cpp
@@ -82,6 +82,9 @@ std::string SelfClass_t::to_string(void*) const {
82std::string VarArg_t::to_string(void*) const { 82std::string VarArg_t::to_string(void*) const {
83 return "..."s; 83 return "..."s;
84} 84}
85std::string VarArgDef_t::to_string(void* ud) const {
86 return "..."s + name->to_string(ud);
87}
85std::string Seperator_t::to_string(void*) const { 88std::string Seperator_t::to_string(void*) const {
86 return {}; 89 return {};
87} 90}
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h
index b7cd73e..885a038 100644
--- a/src/yuescript/yue_ast.h
+++ b/src/yuescript/yue_ast.h
@@ -134,6 +134,11 @@ AST_NODE(KeyName)
134 AST_MEMBER(KeyName, &name) 134 AST_MEMBER(KeyName, &name)
135AST_END(KeyName) 135AST_END(KeyName)
136 136
137AST_NODE(VarArgDef)
138 ast_ptr<false, Variable_t> name;
139 AST_MEMBER(VarArgDef, &name)
140AST_END(VarArgDef)
141
137AST_LEAF(VarArg) 142AST_LEAF(VarArg)
138AST_END(VarArg) 143AST_END(VarArg)
139 144
@@ -790,7 +795,7 @@ AST_END(FnArgDef)
790AST_NODE(FnArgDefList) 795AST_NODE(FnArgDefList)
791 ast_ptr<true, Seperator_t> sep; 796 ast_ptr<true, Seperator_t> sep;
792 ast_list<false, FnArgDef_t> definitions; 797 ast_list<false, FnArgDef_t> definitions;
793 ast_ptr<false, VarArg_t> varArg; 798 ast_ptr<false, VarArgDef_t> varArg;
794 ast_ptr<false, Name_t> label; 799 ast_ptr<false, Name_t> label;
795 AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg, &label) 800 AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg, &label)
796AST_END(FnArgDefList) 801AST_END(FnArgDefList)
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index 77643d3..cae01dd 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -78,7 +78,7 @@ static std::unordered_set<std::string> Metamethods = {
78 "close"s // Lua 5.4 78 "close"s // Lua 5.4
79}; 79};
80 80
81const std::string_view version = "0.30.3"sv; 81const std::string_view version = "0.30.4"sv;
82const std::string_view extension = "yue"sv; 82const std::string_view extension = "yue"sv;
83 83
84class CompileError : public std::logic_error { 84class CompileError : public std::logic_error {
@@ -5872,18 +5872,33 @@ private:
5872 temp.emplace_back(indent() + "local "s + join(names, ", "sv) + nl(def)); 5872 temp.emplace_back(indent() + "local "s + join(names, ", "sv) + nl(def));
5873 transformAssignment(arg.assignment, temp); 5873 transformAssignment(arg.assignment, temp);
5874 } 5874 }
5875 if (varNames.empty()) 5875 if (varNames.empty()) {
5876 varNames = arg.name; 5876 varNames = arg.name;
5877 else 5877 } else {
5878 varNames.append(", "s + arg.name); 5878 varNames.append(", "s + arg.name);
5879 }
5879 } 5880 }
5880 if (argDefList->varArg) { 5881 if (argDefList->varArg) {
5882 std::string varStr;
5883 if (auto varName = argDefList->varArg->name.get()) {
5884 varStr = variableToString(varName);
5885 int target = getLuaTarget(varName);
5886 forceAddToScope(varStr);
5887 if (target < 505) {
5888 temp.push_back(indent() + "local "s + varStr + " = {"s + nl(varName));
5889 temp.push_back(indent(1) + "n = "s + globalVar("select", varName, AccessType::Read) + "(\"#\", ...),"s + nl(varName));
5890 temp.push_back(indent(1) + "..."s + nl(varName));
5891 temp.push_back(indent() + '}' + nl(varName));
5892 varStr.clear();
5893 }
5894 }
5881 auto& arg = argItems.emplace_back(); 5895 auto& arg = argItems.emplace_back();
5882 arg.name = "..."sv; 5896 arg.name = "..."sv;
5883 if (varNames.empty()) 5897 if (varNames.empty()) {
5884 varNames = arg.name; 5898 varNames = arg.name + varStr;
5885 else 5899 } else {
5886 varNames.append(", "s + arg.name); 5900 varNames.append(", "s + arg.name + varStr);
5901 }
5887 _varArgs.top().hasVar = true; 5902 _varArgs.top().hasVar = true;
5888 } 5903 }
5889 if (assignSelf) { 5904 if (assignSelf) {
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index 1999721..17b10c6 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -308,6 +308,7 @@ YueParser::YueParser() {
308 308
309 SelfItem = SelfClassName | SelfClass | SelfName | Self; 309 SelfItem = SelfClassName | SelfClass | SelfName | Self;
310 KeyName = SelfItem | Name | UnicodeName; 310 KeyName = SelfItem | Name | UnicodeName;
311 VarArgDef = "..." >> -(space >> Variable);
311 VarArg = "..."; 312 VarArg = "...";
312 313
313 auto getIndent = [](const item_t& item) -> int { 314 auto getIndent = [](const item_t& item) -> int {
@@ -1032,8 +1033,8 @@ YueParser::YueParser() {
1032 check_vararg_position = and_(white >> (')' | key("using"))) | white >> -(',' >> white) >> vararg_position_error; 1033 check_vararg_position = and_(white >> (')' | key("using"))) | white >> -(',' >> white) >> vararg_position_error;
1033 1034
1034 var_arg_def = ( 1035 var_arg_def = (
1035 VarArg | 1036 VarArgDef |
1036 +space_break >> push_indent_match >> ensure(space >> VarArg >> -(space >> '`' >> space >> Name), pop_indent) 1037 +space_break >> push_indent_match >> ensure(space >> VarArgDef >> -(space >> '`' >> space >> Name), pop_indent)
1037 ) >> check_vararg_position; 1038 ) >> check_vararg_position;
1038 1039
1039 FnArgDefList = Seperator >> 1040 FnArgDefList = Seperator >>
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h
index b68742f..7deaa18 100644
--- a/src/yuescript/yue_parser.h
+++ b/src/yuescript/yue_parser.h
@@ -333,6 +333,7 @@ private:
333 AST_RULE(SelfItem); 333 AST_RULE(SelfItem);
334 AST_RULE(KeyName); 334 AST_RULE(KeyName);
335 AST_RULE(VarArg); 335 AST_RULE(VarArg);
336 AST_RULE(VarArgDef);
336 AST_RULE(Seperator); 337 AST_RULE(Seperator);
337 AST_RULE(NameList); 338 AST_RULE(NameList);
338 AST_RULE(LocalFlag); 339 AST_RULE(LocalFlag);