diff options
| -rwxr-xr-x | doc/docs/doc/README.md | 42 | ||||
| -rwxr-xr-x | doc/docs/zh/doc/README.md | 41 | ||||
| -rw-r--r-- | spec/inputs/macro.yue | 33 | ||||
| -rw-r--r-- | spec/inputs/macro_export.yue | 18 | ||||
| -rw-r--r-- | spec/outputs/codes_from_doc.lua | 2 | ||||
| -rw-r--r-- | spec/outputs/codes_from_doc_zh.lua | 2 | ||||
| -rw-r--r-- | spec/outputs/macro.lua | 11 | ||||
| -rw-r--r-- | src/yuescript/yue_ast.cpp | 5 | ||||
| -rw-r--r-- | src/yuescript/yue_ast.h | 10 | ||||
| -rw-r--r-- | src/yuescript/yue_compiler.cpp | 86 | ||||
| -rw-r--r-- | src/yuescript/yue_parser.cpp | 3 | ||||
| -rw-r--r-- | src/yuescript/yue_parser.h | 1 |
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 | |||
| 379 | In 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 | ||
| 382 | macro 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 | |||
| 389 | macro BodyType = $Enum( | ||
| 390 | Static | ||
| 391 | Dynamic | ||
| 392 | Kinematic | ||
| 393 | ) | ||
| 394 | |||
| 395 | print "Valid enum type:", $BodyType Static | ||
| 396 | -- print "Compilation error with enum type:", $BodyType Unknown | ||
| 397 | ``` | ||
| 398 | |||
| 399 | <YueDisplay> | ||
| 400 | <pre> | ||
| 401 | macro 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 | |||
| 408 | macro BodyType = $Enum( | ||
| 409 | Static | ||
| 410 | Dynamic | ||
| 411 | Kinematic | ||
| 412 | ) | ||
| 413 | |||
| 414 | print "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 | ||
| 379 | All 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. | 421 | All 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 | ||
| 379 | macro 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 | |||
| 386 | macro BodyType = $Enum( | ||
| 387 | Static | ||
| 388 | Dynamic | ||
| 389 | Kinematic | ||
| 390 | ) | ||
| 391 | |||
| 392 | print "有效的枚举类型:", $BodyType Static | ||
| 393 | -- print "编译报错的枚举类型:", $BodyType Unknown | ||
| 394 | ``` | ||
| 395 | <YueDisplay> | ||
| 396 | <pre> | ||
| 397 | macro 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 | |||
| 404 | macro BodyType = $Enum( | ||
| 405 | Static | ||
| 406 | Dynamic | ||
| 407 | Kinematic | ||
| 408 | ) | ||
| 409 | |||
| 410 | print "有效的枚举类型:", $BodyType Static | ||
| 411 | -- print "编译报错的枚举类型:", $BodyType Unknown | ||
| 412 | </pre> | ||
| 413 | </YueDisplay> | ||
| 414 | |||
| 374 | ## 操作符 | 415 | ## 操作符 |
| 375 | 416 | ||
| 376 | Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。 | 417 | Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `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 | ||
| 6 | import "macro_todo" as $ | 6 | import "macro_todo" as $ |
| 7 | 7 | ||
| 8 | macro 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 | |||
| 31 | print $WindowFlag AlwaysAutoResize | ||
| 32 | print $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 | ||
| 47 | export 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 | |||
| 71 | end | 71 | end |
| 72 | print("yuescript") | 72 | print("yuescript") |
| 73 | print(3) | 73 | print(3) |
| 74 | print("Valid enum type:", "Static") | ||
| 74 | if tb ~= nil then | 75 | if tb ~= nil then |
| 75 | tb:func() | 76 | tb:func() |
| 76 | end | 77 | end |
| @@ -2068,6 +2069,7 @@ if cond then | |||
| 2068 | end | 2069 | end |
| 2069 | print("yuescript") | 2070 | print("yuescript") |
| 2070 | print(3) | 2071 | print(3) |
| 2072 | print("Valid enum type:", "Static") | ||
| 2071 | if tb ~= nil then | 2073 | if tb ~= nil then |
| 2072 | tb:func() | 2074 | tb:func() |
| 2073 | end | 2075 | end |
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 | |||
| 71 | end | 71 | end |
| 72 | print("yuescript") | 72 | print("yuescript") |
| 73 | print(3) | 73 | print(3) |
| 74 | print("有效的枚举类型:", "Static") | ||
| 74 | if tb ~= nil then | 75 | if tb ~= nil then |
| 75 | tb:func() | 76 | tb:func() |
| 76 | end | 77 | end |
| @@ -2062,6 +2063,7 @@ if cond then | |||
| 2062 | end | 2063 | end |
| 2063 | print("yuescript") | 2064 | print("yuescript") |
| 2064 | print(3) | 2065 | print(3) |
| 2066 | print("有效的枚举类型:", "Static") | ||
| 2065 | if tb ~= nil then | 2067 | if tb ~= nil then |
| 2066 | tb:func() | 2068 | tb:func() |
| 2067 | end | 2069 | end |
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 @@ | |||
| 1 | print("AlwaysAutoResize") | ||
| 2 | print({ | ||
| 3 | "NoNav", | ||
| 4 | "NoDecoration", | ||
| 5 | "NoTitleBar", | ||
| 6 | "NoResize", | ||
| 7 | "NoMove", | ||
| 8 | "NoScrollbar" | ||
| 9 | }) | ||
| 1 | do | 10 | do |
| 2 | assert(item == nil) | 11 | assert(item == nil) |
| 3 | end | 12 | end |
| @@ -285,7 +294,7 @@ print((setmetatable({ | |||
| 285 | return 998 | 294 | return 998 |
| 286 | end | 295 | end |
| 287 | })) | 296 | })) |
| 288 | print("current line: " .. tostring(268)) | 297 | print("current line: " .. tostring(301)) |
| 289 | do | 298 | do |
| 290 | -- TODO | 299 | -- TODO |
| 291 | end | 300 | end |
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 | } |
| 1365 | std::string MacroFunc_t::to_string(void* ud) const { | ||
| 1366 | return name->to_string(ud) + invoke->to_string(ud); | ||
| 1367 | } | ||
| 1365 | std::string Macro_t::to_string(void* ud) const { | 1368 | std::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 | } |
| 1368 | std::string MacroInPlace_t::to_string(void* ud) const { | 1371 | std::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) |
| 775 | AST_END(MacroLit, "macro_lit"sv) | 775 | AST_END(MacroLit, "macro_lit"sv) |
| 776 | 776 | ||
| 777 | AST_NODE(MacroFunc) | ||
| 778 | ast_ptr<true, MacroName_t> name; | ||
| 779 | ast_sel<true, Invoke_t, InvokeArgs_t> invoke; | ||
| 780 | AST_MEMBER(MacroFunc, &name, &invoke) | ||
| 781 | AST_END(MacroFunc, "macro_func"sv) | ||
| 782 | |||
| 777 | AST_NODE(MacroInPlace) | 783 | AST_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 | ||
| 782 | AST_NODE(Macro) | 788 | AST_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, ¯oLit) | 791 | AST_MEMBER(Macro, &name, &decl) |
| 786 | AST_END(Macro, "macro"sv) | 792 | AST_END(Macro, "macro"sv) |
| 787 | 793 | ||
| 788 | AST_NODE(NameOrDestructure) | 794 | AST_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 | ||
| 78 | const std::string_view version = "0.23.8"sv; | 78 | const std::string_view version = "0.23.9"sv; |
| 79 | const std::string_view extension = "yue"sv; | 79 | const std::string_view extension = "yue"sv; |
| 80 | 80 | ||
| 81 | class CompileError : public std::logic_error { | 81 | class 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); |
