aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2024-08-05 17:20:00 +0800
committerLi Jin <dragon-fly@qq.com>2024-08-05 17:20:00 +0800
commit94edfbc8c7d62d700dfb59334a0ed3beedd49493 (patch)
treef695cec8b8873d5a6e763ac94bf3c81fb6ba0be9
parentf8cd1220147d606b7e96f88c12fd0f163fb4e1c5 (diff)
downloadyuescript-94edfbc8c7d62d700dfb59334a0ed3beedd49493.tar.gz
yuescript-94edfbc8c7d62d700dfb59334a0ed3beedd49493.tar.bz2
yuescript-94edfbc8c7d62d700dfb59334a0ed3beedd49493.zip
add macros generating macros feature.v0.23.9
-rwxr-xr-xdoc/docs/doc/README.md42
-rwxr-xr-xdoc/docs/zh/doc/README.md41
-rw-r--r--spec/inputs/macro.yue33
-rw-r--r--spec/inputs/macro_export.yue18
-rw-r--r--spec/outputs/codes_from_doc.lua2
-rw-r--r--spec/outputs/codes_from_doc_zh.lua2
-rw-r--r--spec/outputs/macro.lua11
-rw-r--r--src/yuescript/yue_ast.cpp5
-rw-r--r--src/yuescript/yue_ast.h10
-rw-r--r--src/yuescript/yue_compiler.cpp86
-rw-r--r--src/yuescript/yue_parser.cpp3
-rw-r--r--src/yuescript/yue_parser.h1
12 files changed, 225 insertions, 29 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index 811497a..378e0f4 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -374,6 +374,48 @@ print $LINE -- get number 2
374</pre> 374</pre>
375</YueDisplay> 375</YueDisplay>
376 376
377### Generating Macros with Macros
378
379In Yuescript, macro functions allow you to generate code at compile time. By nesting macro functions, you can create more complex generation patterns. This feature enables you to define a macro function that generates another macro function, allowing for more dynamic code generation.
380
381```moonscript
382macro Enum = (...) ->
383 items = {...}
384 itemSet = {item, true for item in *items}
385 (item) ->
386 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
387 "\"#{item}\""
388
389macro BodyType = $Enum(
390 Static
391 Dynamic
392 Kinematic
393)
394
395print "Valid enum type:", $BodyType Static
396-- print "Compilation error with enum type:", $BodyType Unknown
397```
398
399<YueDisplay>
400<pre>
401macro Enum = (...) ->
402 items = {...}
403 itemSet = {item, true for item in *items}
404 (item) ->
405 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
406 "\"#{item}\""
407
408macro BodyType = $Enum(
409 Static
410 Dynamic
411 Kinematic
412)
413
414print "Valid enum type:", $BodyType Static
415-- print "Compilation error with enum type:", $BodyType Unknown
416</pre>
417</YueDisplay>
418
377## Operator 419## Operator
378 420
379All of Lua's binary and unary operators are available. Additionally **!=** is as an alias for **~=**, and either **\\** or **::** can be used to write a chaining function call like `tb\func!` or `tb::func!`. And Yuescipt offers some other special operators to write more expressive codes. 421All of Lua's binary and unary operators are available. Additionally **!=** is as an alias for **~=**, and either **\\** or **::** can be used to write a chaining function call like `tb\func!` or `tb::func!`. And Yuescipt offers some other special operators to write more expressive codes.
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md
index 0257dee..90d1820 100755
--- a/doc/docs/zh/doc/README.md
+++ b/doc/docs/zh/doc/README.md
@@ -371,6 +371,47 @@ print $LINE -- 获取当前代码行数:2
371</pre> 371</pre>
372</YueDisplay> 372</YueDisplay>
373 373
374### 用宏生成宏
375
376在月之脚本中,宏函数允许你在编译时生成代码。通过嵌套的宏函数,你可以创建更复杂的生成模式。这个特性允许你定义一个宏函数,用它来生成另一个宏函数,从而实现更加动态的代码生成。
377
378```moonscript
379macro Enum = (...) ->
380 items = {...}
381 itemSet = {item, true for item in *items}
382 (item) ->
383 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
384 "\"#{item}\""
385
386macro BodyType = $Enum(
387 Static
388 Dynamic
389 Kinematic
390)
391
392print "有效的枚举类型:", $BodyType Static
393-- print "编译报错的枚举类型:", $BodyType Unknown
394```
395<YueDisplay>
396<pre>
397macro Enum = (...) ->
398 items = {...}
399 itemSet = {item, true for item in *items}
400 (item) ->
401 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
402 "\"#{item}\""
403
404macro BodyType = $Enum(
405 Static
406 Dynamic
407 Kinematic
408)
409
410print "有效的枚举类型:", $BodyType Static
411-- print "编译报错的枚举类型:", $BodyType Unknown
412</pre>
413</YueDisplay>
414
374## 操作符 415## 操作符
375 416
376Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。 417Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。
diff --git a/spec/inputs/macro.yue b/spec/inputs/macro.yue
index ae14f53..a2e1046 100644
--- a/spec/inputs/macro.yue
+++ b/spec/inputs/macro.yue
@@ -5,6 +5,39 @@ import "macro_export" as {
5 5
6import "macro_todo" as $ 6import "macro_todo" as $
7 7
8macro WindowFlag = $enum(
9 NoNav
10 NoDecoration
11 NoTitleBar
12 NoResize
13 NoMove
14 NoScrollbar
15 NoScrollWithMouse
16 NoCollapse
17 AlwaysAutoResize
18 NoSavedSettings
19 NoInputs
20 MenuBar
21 HorizontalScrollbar
22 NoFocusOnAppearing
23 NoBringToFrontOnFocus
24 AlwaysVerticalScrollbar
25 AlwaysHorizontalScrollbar
26 NoNavInputs
27 NoNavFocus
28 UnsavedDocument
29)
30
31print $WindowFlag AlwaysAutoResize
32print $WindowFlag(
33 NoNav
34 NoDecoration
35 NoTitleBar
36 NoResize
37 NoMove
38 NoScrollbar
39)
40
8$asserts item == nil 41$asserts item == nil
9 42
10$myconfig false 43$myconfig false
diff --git a/spec/inputs/macro_export.yue b/spec/inputs/macro_export.yue
index eec5848..cc7d459 100644
--- a/spec/inputs/macro_export.yue
+++ b/spec/inputs/macro_export.yue
@@ -44,6 +44,24 @@ do
44 _dst_.#{field} = _src_.#{field} 44 _dst_.#{field} = _src_.#{field}
45"}" 45"}"
46 46
47export macro enum = (...) ->
48 items = {...}
49 items = [item\gsub('"', '') for item in *items]
50 itemSet = {item, true for item in *items}
51 (...) ->
52 count = select "#", ...
53 if 1 < count
54 result = "["
55 for i = 1, count
56 item = select i, ...
57 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
58 result ..= "\"#{item}\","
59 result .. "]"
60 else
61 item = select 1, ...
62 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
63 "\"#{item}\""
64
47$ -> 65$ ->
48 global debugMode = true 66 global debugMode = true
49 global debugMacro = true 67 global debugMacro = true
diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua
index 6f61569..4073056 100644
--- a/spec/outputs/codes_from_doc.lua
+++ b/spec/outputs/codes_from_doc.lua
@@ -71,6 +71,7 @@ if cond then
71end 71end
72print("yuescript") 72print("yuescript")
73print(3) 73print(3)
74print("Valid enum type:", "Static")
74if tb ~= nil then 75if tb ~= nil then
75 tb:func() 76 tb:func()
76end 77end
@@ -2068,6 +2069,7 @@ if cond then
2068end 2069end
2069print("yuescript") 2070print("yuescript")
2070print(3) 2071print(3)
2072print("Valid enum type:", "Static")
2071if tb ~= nil then 2073if tb ~= nil then
2072 tb:func() 2074 tb:func()
2073end 2075end
diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua
index aa53926..f251450 100644
--- a/spec/outputs/codes_from_doc_zh.lua
+++ b/spec/outputs/codes_from_doc_zh.lua
@@ -71,6 +71,7 @@ if cond then
71end 71end
72print("yuescript") 72print("yuescript")
73print(3) 73print(3)
74print("有效的枚举类型:", "Static")
74if tb ~= nil then 75if tb ~= nil then
75 tb:func() 76 tb:func()
76end 77end
@@ -2062,6 +2063,7 @@ if cond then
2062end 2063end
2063print("yuescript") 2064print("yuescript")
2064print(3) 2065print(3)
2066print("有效的枚举类型:", "Static")
2065if tb ~= nil then 2067if tb ~= nil then
2066 tb:func() 2068 tb:func()
2067end 2069end
diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua
index 2ee3e0a..953c260 100644
--- a/spec/outputs/macro.lua
+++ b/spec/outputs/macro.lua
@@ -1,3 +1,12 @@
1print("AlwaysAutoResize")
2print({
3 "NoNav",
4 "NoDecoration",
5 "NoTitleBar",
6 "NoResize",
7 "NoMove",
8 "NoScrollbar"
9})
1do 10do
2 assert(item == nil) 11 assert(item == nil)
3end 12end
@@ -285,7 +294,7 @@ print((setmetatable({
285 return 998 294 return 998
286 end 295 end
287})) 296}))
288print("current line: " .. tostring(268)) 297print("current line: " .. tostring(301))
289do 298do
290-- TODO 299-- TODO
291end 300end
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp
index 3a22345..a454908 100644
--- a/src/yuescript/yue_ast.cpp
+++ b/src/yuescript/yue_ast.cpp
@@ -1362,8 +1362,11 @@ std::string MacroLit_t::to_string(void* ud) const {
1362 } 1362 }
1363 return line; 1363 return line;
1364} 1364}
1365std::string MacroFunc_t::to_string(void* ud) const {
1366 return name->to_string(ud) + invoke->to_string(ud);
1367}
1365std::string Macro_t::to_string(void* ud) const { 1368std::string Macro_t::to_string(void* ud) const {
1366 return "macro "s + name->to_string(ud) + " = "s + macroLit->to_string(ud); 1369 return "macro "s + name->to_string(ud) + " = "s + decl->to_string(ud);
1367} 1370}
1368std::string MacroInPlace_t::to_string(void* ud) const { 1371std::string MacroInPlace_t::to_string(void* ud) const {
1369 auto info = reinterpret_cast<YueFormat*>(ud); 1372 auto info = reinterpret_cast<YueFormat*>(ud);
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h
index 2ea0a85..b287202 100644
--- a/src/yuescript/yue_ast.h
+++ b/src/yuescript/yue_ast.h
@@ -774,6 +774,12 @@ AST_NODE(MacroLit)
774 AST_MEMBER(MacroLit, &argsDef, &body) 774 AST_MEMBER(MacroLit, &argsDef, &body)
775AST_END(MacroLit, "macro_lit"sv) 775AST_END(MacroLit, "macro_lit"sv)
776 776
777AST_NODE(MacroFunc)
778 ast_ptr<true, MacroName_t> name;
779 ast_sel<true, Invoke_t, InvokeArgs_t> invoke;
780 AST_MEMBER(MacroFunc, &name, &invoke)
781AST_END(MacroFunc, "macro_func"sv)
782
777AST_NODE(MacroInPlace) 783AST_NODE(MacroInPlace)
778 ast_ptr<true, Body_t> body; 784 ast_ptr<true, Body_t> body;
779 AST_MEMBER(MacroInPlace, &body) 785 AST_MEMBER(MacroInPlace, &body)
@@ -781,8 +787,8 @@ AST_END(MacroInPlace, "macro_in_place"sv)
781 787
782AST_NODE(Macro) 788AST_NODE(Macro)
783 ast_ptr<true, UnicodeName_t> name; 789 ast_ptr<true, UnicodeName_t> name;
784 ast_ptr<true, MacroLit_t> macroLit; 790 ast_sel<true, MacroLit_t, MacroFunc_t> decl;
785 AST_MEMBER(Macro, &name, &macroLit) 791 AST_MEMBER(Macro, &name, &decl)
786AST_END(Macro, "macro"sv) 792AST_END(Macro, "macro"sv)
787 793
788AST_NODE(NameOrDestructure) 794AST_NODE(NameOrDestructure)
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index 7b34d0e..994ab57 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -75,7 +75,7 @@ static std::unordered_set<std::string> Metamethods = {
75 "close"s // Lua 5.4 75 "close"s // Lua 5.4
76}; 76};
77 77
78const std::string_view version = "0.23.8"sv; 78const std::string_view version = "0.23.9"sv;
79const std::string_view extension = "yue"sv; 79const std::string_view extension = "yue"sv;
80 80
81class CompileError : public std::logic_error { 81class CompileError : public std::logic_error {
@@ -5065,7 +5065,36 @@ private:
5065 throw CompileError("can not define macro outside the root block"sv, macro); 5065 throw CompileError("can not define macro outside the root block"sv, macro);
5066 } 5066 }
5067 auto macroName = _parser.toString(macro->name); 5067 auto macroName = _parser.toString(macro->name);
5068 auto argsDef = macro->macroLit->argsDef.get(); 5068 if (auto macroFunc = macro->decl.as<MacroFunc_t>()) {
5069 auto chainValue = macroFunc->new_ptr<ChainValue_t>();
5070 auto callable = macroFunc->new_ptr<Callable_t>();
5071 callable->item.set(macroFunc->name);
5072 chainValue->items.push_back(callable);
5073 chainValue->items.push_back(macroFunc->invoke);
5074 pushCurrentModule(); // cur
5075 int top = lua_gettop(L) - 1;
5076 DEFER(lua_settop(L, top));
5077 if (auto builtinCode = expandMacroChain(chainValue)) {
5078 throw CompileError("macro generating function must return a function"sv, chainValue);
5079 } // cur res
5080 if (lua_isfunction(L, -1) == 0) {
5081 throw CompileError("macro generating function must return a function"sv, chainValue);
5082 } // cur macro
5083 if (exporting && _config.exporting && !_config.module.empty()) {
5084 pushModuleTable(_config.module); // cur macro module
5085 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name
5086 lua_pushvalue(L, -3); // cur macro module name macro
5087 lua_rawset(L, -3); // cur macro module
5088 lua_pop(L, 1);
5089 } // cur macro
5090 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro name
5091 lua_insert(L, -2); // cur name macro
5092 lua_rawset(L, -3); // cur[name] = macro, cur
5093 out.push_back(Empty);
5094 return;
5095 }
5096 auto macroLit = macro->decl.to<MacroLit_t>();
5097 auto argsDef = macroLit->argsDef.get();
5069 str_list newArgs; 5098 str_list newArgs;
5070 if (argsDef) { 5099 if (argsDef) {
5071 for (auto def_ : argsDef->definitions.objects()) { 5100 for (auto def_ : argsDef->definitions.objects()) {
@@ -5088,7 +5117,7 @@ private:
5088 } 5117 }
5089 } 5118 }
5090 _buf << "("sv << join(newArgs, ","sv) << ")->"sv; 5119 _buf << "("sv << join(newArgs, ","sv) << ")->"sv;
5091 _buf << _parser.toString(macro->macroLit->body); 5120 _buf << _parser.toString(macroLit->body);
5092 auto macroCodes = clearBuf(); 5121 auto macroCodes = clearBuf();
5093 _buf << "=(macro "sv << macroName << ")"; 5122 _buf << "=(macro "sv << macroName << ")";
5094 auto chunkName = clearBuf(); 5123 auto chunkName = clearBuf();
@@ -5101,22 +5130,22 @@ private:
5101 pushOptions(macro->m_begin.m_line - 1); // cur loadstring codes chunk options 5130 pushOptions(macro->m_begin.m_line - 1); // cur loadstring codes chunk options
5102 if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), cur f err 5131 if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), cur f err
5103 std::string err = lua_tostring(L, -1); 5132 std::string err = lua_tostring(L, -1);
5104 throw CompileError("failed to load macro codes\n"s + err, macro->macroLit); 5133 throw CompileError("failed to load macro codes\n"s + err, macroLit);
5105 } // cur f err 5134 } // cur f err
5106 if (lua_isnil(L, -2) != 0) { // f == nil, cur f err 5135 if (lua_isnil(L, -2) != 0) { // f == nil, cur f err
5107 std::string err = lua_tostring(L, -1); 5136 std::string err = lua_tostring(L, -1);
5108 throw CompileError("failed to load macro codes, at (macro "s + macroName + "): "s + err, macro->macroLit); 5137 throw CompileError("failed to load macro codes, at (macro "s + macroName + "): "s + err, macroLit);
5109 } 5138 }
5110 lua_pop(L, 1); // cur f 5139 lua_pop(L, 1); // cur f
5111 pushYue("pcall"sv); // cur f pcall 5140 pushYue("pcall"sv); // cur f pcall
5112 lua_insert(L, -2); // cur pcall f 5141 lua_insert(L, -2); // cur pcall f
5113 if (lua_pcall(L, 1, 2, 0) != 0) { // f(), cur success macro 5142 if (lua_pcall(L, 1, 2, 0) != 0) { // f(), cur success macro
5114 std::string err = lua_tostring(L, -1); 5143 std::string err = lua_tostring(L, -1);
5115 throw CompileError("failed to generate macro function\n"s + err, macro->macroLit); 5144 throw CompileError("failed to generate macro function\n"s + err, macroLit);
5116 } // cur success res 5145 } // cur success res
5117 if (lua_toboolean(L, -2) == 0) { 5146 if (lua_toboolean(L, -2) == 0) {
5118 std::string err = lua_tostring(L, -1); 5147 std::string err = lua_tostring(L, -1);
5119 throw CompileError("failed to generate macro function\n"s + err, macro->macroLit); 5148 throw CompileError("failed to generate macro function\n"s + err, macroLit);
5120 } // cur true macro 5149 } // cur true macro
5121 lua_remove(L, -2); // cur macro 5150 lua_remove(L, -2); // cur macro
5122 if (exporting && _config.exporting && !_config.module.empty()) { 5151 if (exporting && _config.exporting && !_config.module.empty()) {
@@ -6180,23 +6209,20 @@ private:
6180 return Empty; 6209 return Empty;
6181 } 6210 }
6182 6211
6183 std::tuple<std::string, std::string, str_list> expandMacroStr(ChainValue_t* chainValue) { 6212 std::optional<std::string> expandMacroChain(ChainValue_t* chainValue) {
6184 const auto& chainList = chainValue->items.objects(); 6213 const auto& chainList = chainValue->items.objects();
6185 auto x = ast_to<Callable_t>(chainList.front())->item.to<MacroName_t>(); 6214 auto x = ast_to<Callable_t>(chainList.front())->item.to<MacroName_t>();
6186 auto macroName = _parser.toString(x->name); 6215 auto macroName = _parser.toString(x->name);
6187 if (!_useModule) { 6216 if (!_useModule) {
6188 auto code = expandBuiltinMacro(macroName, x); 6217 auto code = expandBuiltinMacro(macroName, x);
6189 if (!code.empty()) return {Empty, code, {}}; 6218 if (!code.empty()) return code;
6190 throw CompileError("can not resolve macro"sv, x); 6219 throw CompileError("can not resolve macro"sv, x);
6191 } 6220 }
6192 pushCurrentModule(); // cur
6193 int top = lua_gettop(L) - 1;
6194 DEFER(lua_settop(L, top));
6195 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macroName 6221 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macroName
6196 lua_rawget(L, -2); // cur[macroName], cur macroFunc 6222 lua_rawget(L, -2); // cur[macroName], cur macroFunc
6197 if (lua_isfunction(L, -1) == 0) { 6223 if (lua_isfunction(L, -1) == 0) {
6198 auto code = expandBuiltinMacro(macroName, x); 6224 auto code = expandBuiltinMacro(macroName, x);
6199 if (!code.empty()) return {Empty, code, {}}; 6225 if (!code.empty()) return code;
6200 throw CompileError("can not resolve macro"sv, x); 6226 throw CompileError("can not resolve macro"sv, x);
6201 } // cur macroFunc 6227 } // cur macroFunc
6202 pushYue("pcall"sv); // cur macroFunc pcall 6228 pushYue("pcall"sv); // cur macroFunc pcall
@@ -6261,32 +6287,44 @@ private:
6261 throw CompileError("failed to expand macro: "s + err, x); 6287 throw CompileError("failed to expand macro: "s + err, x);
6262 } 6288 }
6263 lua_remove(L, -2); // cur res 6289 lua_remove(L, -2); // cur res
6290 return std::nullopt;
6291 }
6292
6293 std::tuple<std::string, std::string, str_list> expandMacroStr(ChainValue_t* chainValue) {
6294 auto x = chainValue->items.front();
6295 pushCurrentModule(); // cur
6296 int top = lua_gettop(L) - 1;
6297 DEFER(lua_settop(L, top));
6298 auto builtinCode = expandMacroChain(chainValue);
6299 if (builtinCode) {
6300 return {Empty, builtinCode.value(), {}};
6301 } // cur res
6264 if (lua_isstring(L, -1) == 0 && lua_istable(L, -1) == 0) { 6302 if (lua_isstring(L, -1) == 0 && lua_istable(L, -1) == 0) {
6265 throw CompileError("macro function must return string or table"sv, x); 6303 throw CompileError("macro function must return a string or a table"sv, x);
6266 } // cur res 6304 } // cur res
6267 std::string codes; 6305 std::string codes;
6268 std::string type; 6306 std::string type;
6269 str_list localVars; 6307 str_list localVars;
6270 if (lua_istable(L, -1) != 0) { 6308 if (lua_istable(L, -1) != 0) { // cur tab
6271 lua_getfield(L, -1, "code"); // cur res code 6309 lua_getfield(L, -1, "code"); // cur tab code
6272 if (lua_isstring(L, -1) != 0) { 6310 if (lua_isstring(L, -1) != 0) {
6273 codes = lua_tostring(L, -1); 6311 codes = lua_tostring(L, -1);
6274 } else { 6312 } else {
6275 throw CompileError("macro table must contain field \"code\" of string"sv, x); 6313 throw CompileError("macro table must contain field \"code\" of string"sv, x);
6276 } 6314 }
6277 lua_pop(L, 1); // cur res 6315 lua_pop(L, 1); // cur tab
6278 lua_getfield(L, -1, "type"); // cur res type 6316 lua_getfield(L, -1, "type"); // cur tab type
6279 if (lua_isstring(L, -1) != 0) { 6317 if (lua_isstring(L, -1) != 0) {
6280 type = lua_tostring(L, -1); 6318 type = lua_tostring(L, -1);
6281 } 6319 }
6282 if (type != "lua"sv && type != "text"sv) { 6320 if (type != "lua"sv && type != "text"sv) {
6283 throw CompileError("macro table must contain field \"type\" of value \"lua\" or \"text\""sv, x); 6321 throw CompileError("macro table must contain field \"type\" of value \"lua\" or \"text\""sv, x);
6284 } 6322 }
6285 lua_pop(L, 1); // cur res 6323 lua_pop(L, 1); // cur tab
6286 lua_getfield(L, -1, "locals"); // cur res locals 6324 lua_getfield(L, -1, "locals"); // cur tab locals
6287 if (lua_istable(L, -1) != 0) { 6325 if (lua_istable(L, -1) != 0) {
6288 for (int i = 0; i < static_cast<int>(lua_objlen(L, -1)); i++) { 6326 for (int i = 0; i < static_cast<int>(lua_objlen(L, -1)); i++) {
6289 lua_rawgeti(L, -1, i + 1); // cur res locals item 6327 lua_rawgeti(L, -1, i + 1); // cur tab locals item
6290 size_t len = 0; 6328 size_t len = 0;
6291 if (lua_isstring(L, -1) == 0) { 6329 if (lua_isstring(L, -1) == 0) {
6292 throw CompileError("macro table field \"locals\" must be a table of strings"sv, x); 6330 throw CompileError("macro table field \"locals\" must be a table of strings"sv, x);
@@ -6297,11 +6335,11 @@ private:
6297 } else { 6335 } else {
6298 throw CompileError("macro table field \"locals\" must contain names for local variables, got \""s + std::string(name, len) + '"', x); 6336 throw CompileError("macro table field \"locals\" must contain names for local variables, got \""s + std::string(name, len) + '"', x);
6299 } 6337 }
6300 lua_pop(L, 1); 6338 lua_pop(L, 1); // cur tab locals
6301 } 6339 }
6302 } 6340 }
6303 lua_pop(L, 1); // cur res 6341 lua_pop(L, 1); // cur tab
6304 } else { 6342 } else { // cur code
6305 codes = lua_tostring(L, -1); 6343 codes = lua_tostring(L, -1);
6306 } 6344 }
6307 Utils::trim(codes); 6345 Utils::trim(codes);
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index 3ffaf18..986f67a 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -870,7 +870,8 @@ YueParser::YueParser() {
870 MacroName = '$' >> UnicodeName; 870 MacroName = '$' >> UnicodeName;
871 macro_args_def = '(' >> white >> -FnArgDefList >> white >> ')'; 871 macro_args_def = '(' >> white >> -FnArgDefList >> white >> ')';
872 MacroLit = -(macro_args_def >> space) >> "->" >> space >> Body; 872 MacroLit = -(macro_args_def >> space) >> "->" >> space >> Body;
873 Macro = key("macro") >> space >> UnicodeName >> space >> '=' >> space >> MacroLit; 873 MacroFunc = MacroName >> (Invoke | InvokeArgs);
874 Macro = key("macro") >> space >> UnicodeName >> space >> '=' >> space >> (MacroLit | MacroFunc);
874 MacroInPlace = '$' >> space >> "->" >> space >> Body; 875 MacroInPlace = '$' >> space >> "->" >> space >> Body;
875 876
876 NameList = Seperator >> Variable >> *(space >> ',' >> space >> Variable); 877 NameList = Seperator >> Variable >> *(space >> ',' >> space >> Variable);
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h
index 05aa9e6..6623653 100644
--- a/src/yuescript/yue_parser.h
+++ b/src/yuescript/yue_parser.h
@@ -396,6 +396,7 @@ private:
396 AST_RULE(FunLit); 396 AST_RULE(FunLit);
397 AST_RULE(MacroName); 397 AST_RULE(MacroName);
398 AST_RULE(MacroLit); 398 AST_RULE(MacroLit);
399 AST_RULE(MacroFunc);
399 AST_RULE(Macro); 400 AST_RULE(Macro);
400 AST_RULE(MacroInPlace); 401 AST_RULE(MacroInPlace);
401 AST_RULE(NameOrDestructure); 402 AST_RULE(NameOrDestructure);