diff options
| author | Li Jin <dragon-fly@qq.com> | 2025-01-31 01:02:59 +0800 |
|---|---|---|
| committer | Li Jin <dragon-fly@qq.com> | 2025-01-31 01:02:59 +0800 |
| commit | f5c1735262f07f8064575bebec8b593559abe619 (patch) | |
| tree | c0e3d660e647843c37df451e4028ec5590edb8e2 | |
| parent | cf91f61990babdd8a80774809e9e860a155e4201 (diff) | |
| download | yuescript-f5c1735262f07f8064575bebec8b593559abe619.tar.gz yuescript-f5c1735262f07f8064575bebec8b593559abe619.tar.bz2 yuescript-f5c1735262f07f8064575bebec8b593559abe619.zip | |
Fixing issue #178.
| -rwxr-xr-x | doc/docs/doc/README.md | 6 | ||||
| -rwxr-xr-x | doc/docs/zh/doc/README.md | 6 | ||||
| -rw-r--r-- | spec/inputs/macro.yue | 9 | ||||
| -rw-r--r-- | spec/outputs/macro.lua | 11 | ||||
| -rw-r--r-- | src/yuescript/yue_ast.cpp | 3 | ||||
| -rw-r--r-- | src/yuescript/yue_ast.h | 6 | ||||
| -rw-r--r-- | src/yuescript/yue_compiler.cpp | 86 | ||||
| -rw-r--r-- | src/yuescript/yue_parser.cpp | 2 | ||||
| -rw-r--r-- | src/yuescript/yue_parser.h | 1 |
9 files changed, 94 insertions, 36 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index 56d3635..e1ba56e 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md | |||
| @@ -278,12 +278,11 @@ macro yueFunc = (var) -> "local #{var} = ->" | |||
| 278 | $yueFunc funcA | 278 | $yueFunc funcA |
| 279 | funcA = -> "fail to assign to the Yue defined variable" | 279 | funcA = -> "fail to assign to the Yue defined variable" |
| 280 | 280 | ||
| 281 | -- take care and let YueScript know the | 281 | -- YueScript knows the |
| 282 | -- local variables you declared in Lua code | 282 | -- local variables you declared in Lua code |
| 283 | macro luaFunc = (var) -> { | 283 | macro luaFunc = (var) -> { |
| 284 | code: "local function #{var}() end" | 284 | code: "local function #{var}() end" |
| 285 | type: "lua" | 285 | type: "lua" |
| 286 | locals: {var} | ||
| 287 | } | 286 | } |
| 288 | $luaFunc funcB | 287 | $luaFunc funcB |
| 289 | funcB = -> "assign to the Lua defined variable" | 288 | funcB = -> "assign to the Lua defined variable" |
| @@ -307,12 +306,11 @@ macro yueFunc = (var) -> "local #{var} = ->" | |||
| 307 | $yueFunc funcA | 306 | $yueFunc funcA |
| 308 | funcA = -> "fail to assign to the Yue defined variable" | 307 | funcA = -> "fail to assign to the Yue defined variable" |
| 309 | 308 | ||
| 310 | -- take care and let YueScript know the | 309 | -- YueScript knows the |
| 311 | -- local variables you declared in Lua codes | 310 | -- local variables you declared in Lua codes |
| 312 | macro luaFunc = (var) -> { | 311 | macro luaFunc = (var) -> { |
| 313 | code: "local function #{var}() end" | 312 | code: "local function #{var}() end" |
| 314 | type: "lua" | 313 | type: "lua" |
| 315 | locals: {var} | ||
| 316 | } | 314 | } |
| 317 | $luaFunc funcB | 315 | $luaFunc funcB |
| 318 | funcB = -> "assign to the Lua defined variable" | 316 | funcB = -> "assign to the Lua defined variable" |
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md index 0e5b532..5e28122 100755 --- a/doc/docs/zh/doc/README.md +++ b/doc/docs/zh/doc/README.md | |||
| @@ -277,11 +277,10 @@ macro yueFunc = (var) -> "local #{var} = ->" | |||
| 277 | $yueFunc funcA | 277 | $yueFunc funcA |
| 278 | funcA = -> "无法访问宏生成月之脚本里定义的变量" | 278 | funcA = -> "无法访问宏生成月之脚本里定义的变量" |
| 279 | 279 | ||
| 280 | -- 让月之脚本知道你在Lua代码中声明的局部变量 | 280 | -- 月之脚本会知道你在 Lua 代码中声明的局部变量 |
| 281 | macro luaFunc = (var) -> { | 281 | macro luaFunc = (var) -> { |
| 282 | code: "local function #{var}() end" | 282 | code: "local function #{var}() end" |
| 283 | type: "lua" | 283 | type: "lua" |
| 284 | locals: {var} | ||
| 285 | } | 284 | } |
| 286 | $luaFunc funcB | 285 | $luaFunc funcB |
| 287 | funcB = -> "访问宏生成Lua代码里定义的变量" | 286 | funcB = -> "访问宏生成Lua代码里定义的变量" |
| @@ -305,11 +304,10 @@ macro yueFunc = (var) -> "local #{var} = ->" | |||
| 305 | $yueFunc funcA | 304 | $yueFunc funcA |
| 306 | funcA = -> "无法访问宏生成月之脚本里定义的变量" | 305 | funcA = -> "无法访问宏生成月之脚本里定义的变量" |
| 307 | 306 | ||
| 308 | -- 让月之脚本知道你在Lua代码中声明的局部变量 | 307 | -- 月之脚本会知道你在 Lua 代码中声明的局部变量 |
| 309 | macro luaFunc = (var) -> { | 308 | macro luaFunc = (var) -> { |
| 310 | code: "local function #{var}() end" | 309 | code: "local function #{var}() end" |
| 311 | type: "lua" | 310 | type: "lua" |
| 312 | locals: {var} | ||
| 313 | } | 311 | } |
| 314 | $luaFunc funcB | 312 | $luaFunc funcB |
| 315 | funcB = -> "访问宏生成Lua代码里定义的变量" | 313 | funcB = -> "访问宏生成Lua代码里定义的变量" |
diff --git a/spec/inputs/macro.yue b/spec/inputs/macro.yue index 5dafd84..5d5f1a9 100644 --- a/spec/inputs/macro.yue +++ b/spec/inputs/macro.yue | |||
| @@ -340,6 +340,15 @@ _1 = -> | |||
| 340 | print 2 | 340 | print 2 |
| 341 | print 3 | 341 | print 3 |
| 342 | 342 | ||
| 343 | do | ||
| 344 | macro foo = -> code: "tb:func(123)", type: "lua" | ||
| 345 | f = -> | ||
| 346 | x = $foo\bar 456 | ||
| 347 | $foo! | ||
| 348 | f1 = -> | ||
| 349 | $foo! | ||
| 350 | return | ||
| 351 | |||
| 343 | macro implicitReturnMacroIsAllowed = -> "print 'abc'\n123" | 352 | macro implicitReturnMacroIsAllowed = -> "print 'abc'\n123" |
| 344 | 353 | ||
| 345 | $implicitReturnMacroIsAllowed | 354 | $implicitReturnMacroIsAllowed |
diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua index 83c6a90..7163077 100644 --- a/spec/outputs/macro.lua +++ b/spec/outputs/macro.lua | |||
| @@ -330,6 +330,17 @@ _1 = function() | |||
| 330 | return _accum_0 | 330 | return _accum_0 |
| 331 | end | 331 | end |
| 332 | do | 332 | do |
| 333 | f = function() | ||
| 334 | x = (tb:func(123)):bar(456) | ||
| 335 | return (tb:func(123)) | ||
| 336 | end | ||
| 337 | local f1 | ||
| 338 | f1 = function() | ||
| 339 | tb:func(123) | ||
| 340 | return | ||
| 341 | end | ||
| 342 | end | ||
| 343 | do | ||
| 333 | print('abc') | 344 | print('abc') |
| 334 | return 123 | 345 | return 123 |
| 335 | end | 346 | end |
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index 3dac699..fe6e726 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp | |||
| @@ -167,6 +167,9 @@ std::string ExistentialOp_t::to_string(void*) const { | |||
| 167 | std::string TableAppendingOp_t::to_string(void*) const { | 167 | std::string TableAppendingOp_t::to_string(void*) const { |
| 168 | return "[]"s; | 168 | return "[]"s; |
| 169 | } | 169 | } |
| 170 | std::string PlainItem_t::to_string(void *) const { | ||
| 171 | return {}; | ||
| 172 | } | ||
| 170 | std::string GlobalOp_t::to_string(void* ud) const { | 173 | std::string GlobalOp_t::to_string(void* ud) const { |
| 171 | auto info = reinterpret_cast<YueFormat*>(ud); | 174 | auto info = reinterpret_cast<YueFormat*>(ud); |
| 172 | return info->convert(this); | 175 | return info->convert(this); |
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 139f4b3..e670126 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h | |||
| @@ -635,9 +635,13 @@ AST_END(ExistentialOp) | |||
| 635 | AST_LEAF(TableAppendingOp) | 635 | AST_LEAF(TableAppendingOp) |
| 636 | AST_END(TableAppendingOp) | 636 | AST_END(TableAppendingOp) |
| 637 | 637 | ||
| 638 | AST_LEAF(PlainItem) | ||
| 639 | AST_END(PlainItem) | ||
| 640 | |||
| 638 | AST_NODE(ChainValue) | 641 | AST_NODE(ChainValue) |
| 639 | ast_ptr<true, Seperator_t> sep; | 642 | ast_ptr<true, Seperator_t> sep; |
| 640 | ast_sel_list<true, Callable_t, Invoke_t, DotChainItem_t, ColonChainItem_t, Slice_t, Exp_t, String_t, InvokeArgs_t, ExistentialOp_t, TableAppendingOp_t> items; | 643 | ast_sel_list<true, Callable_t, Invoke_t, DotChainItem_t, ColonChainItem_t, Slice_t, Exp_t, String_t, InvokeArgs_t, ExistentialOp_t, TableAppendingOp_t, |
| 644 | /*non-syntax-rule*/ PlainItem_t> items; | ||
| 641 | AST_MEMBER(ChainValue, &sep, &items) | 645 | AST_MEMBER(ChainValue, &sep, &items) |
| 642 | AST_END(ChainValue) | 646 | AST_END(ChainValue) |
| 643 | 647 | ||
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 369f7e8..446414d 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp | |||
| @@ -24,6 +24,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | |||
| 24 | 24 | ||
| 25 | extern "C" { | 25 | extern "C" { |
| 26 | #include "lauxlib.h" | 26 | #include "lauxlib.h" |
| 27 | #include "lparser.h" | ||
| 27 | #include "lua.h" | 28 | #include "lua.h" |
| 28 | #include "lualib.h" | 29 | #include "lualib.h" |
| 29 | } // extern "C" | 30 | } // extern "C" |
| @@ -6272,6 +6273,9 @@ private: | |||
| 6272 | case id<TableAppendingOp_t>(): | 6273 | case id<TableAppendingOp_t>(): |
| 6273 | transform_table_appending_op(static_cast<TableAppendingOp_t*>(item), temp); | 6274 | transform_table_appending_op(static_cast<TableAppendingOp_t*>(item), temp); |
| 6274 | break; | 6275 | break; |
| 6276 | case id<PlainItem_t>(): | ||
| 6277 | temp.push_back(_parser.toString(item)); | ||
| 6278 | break; | ||
| 6275 | default: YUEE("AST node mismatch", item); break; | 6279 | default: YUEE("AST node mismatch", item); break; |
| 6276 | } | 6280 | } |
| 6277 | } | 6281 | } |
| @@ -6519,24 +6523,26 @@ private: | |||
| 6519 | throw CompileError("macro table must contain field \"type\" of value \"lua\" or \"text\""sv, x); | 6523 | throw CompileError("macro table must contain field \"type\" of value \"lua\" or \"text\""sv, x); |
| 6520 | } | 6524 | } |
| 6521 | lua_pop(L, 1); // cur tab | 6525 | lua_pop(L, 1); // cur tab |
| 6522 | lua_getfield(L, -1, "locals"); // cur tab locals | 6526 | if (type == "text"sv) { |
| 6523 | if (lua_istable(L, -1) != 0) { | 6527 | lua_getfield(L, -1, "locals"); // cur tab locals |
| 6524 | for (int i = 0; i < static_cast<int>(lua_objlen(L, -1)); i++) { | 6528 | if (lua_istable(L, -1) != 0) { |
| 6525 | lua_rawgeti(L, -1, i + 1); // cur tab locals item | 6529 | for (int i = 0; i < static_cast<int>(lua_objlen(L, -1)); i++) { |
| 6526 | size_t len = 0; | 6530 | lua_rawgeti(L, -1, i + 1); // cur tab locals item |
| 6527 | if (lua_isstring(L, -1) == 0) { | 6531 | size_t len = 0; |
| 6528 | throw CompileError("macro table field \"locals\" must be a table of strings"sv, x); | 6532 | if (lua_isstring(L, -1) == 0) { |
| 6529 | } | 6533 | throw CompileError("macro table field \"locals\" must be a table of strings"sv, x); |
| 6530 | auto name = lua_tolstring(L, -1, &len); | 6534 | } |
| 6531 | if (auto varNode = toAst<Variable_t>({name, len}, x)) { | 6535 | auto name = lua_tolstring(L, -1, &len); |
| 6532 | localVars.push_back(variableToString(varNode)); | 6536 | if (auto varNode = toAst<Variable_t>({name, len}, x)) { |
| 6533 | } else { | 6537 | localVars.push_back(variableToString(varNode)); |
| 6534 | throw CompileError("macro table field \"locals\" must contain names for local variables, got \""s + std::string(name, len) + '"', x); | 6538 | } else { |
| 6539 | throw CompileError("macro table field \"locals\" must contain names for local variables, got \""s + std::string(name, len) + '"', x); | ||
| 6540 | } | ||
| 6541 | lua_pop(L, 1); // cur tab locals | ||
| 6535 | } | 6542 | } |
| 6536 | lua_pop(L, 1); // cur tab locals | ||
| 6537 | } | 6543 | } |
| 6544 | lua_pop(L, 1); // cur tab | ||
| 6538 | } | 6545 | } |
| 6539 | lua_pop(L, 1); // cur tab | ||
| 6540 | } else { // cur code | 6546 | } else { // cur code |
| 6541 | codes = lua_tostring(L, -1); | 6547 | codes = lua_tostring(L, -1); |
| 6542 | } | 6548 | } |
| @@ -6554,23 +6560,49 @@ private: | |||
| 6554 | bool isBlock = (usage == ExpUsage::Common) && (chainList.size() < 2 || (chainList.size() == 2 && ast_is<Invoke_t, InvokeArgs_t>(chainList.back()))); | 6560 | bool isBlock = (usage == ExpUsage::Common) && (chainList.size() < 2 || (chainList.size() == 2 && ast_is<Invoke_t, InvokeArgs_t>(chainList.back()))); |
| 6555 | ParseInfo info; | 6561 | ParseInfo info; |
| 6556 | if (type == "lua"sv) { | 6562 | if (type == "lua"sv) { |
| 6557 | if (!isBlock) { | ||
| 6558 | throw CompileError("lua macro can only be placed where block macro is allowed"sv, x); | ||
| 6559 | } | ||
| 6560 | auto macroChunk = "=(macro "s + _parser.toString(x->name) + ')'; | 6563 | auto macroChunk = "=(macro "s + _parser.toString(x->name) + ')'; |
| 6561 | int top = lua_gettop(L); | 6564 | int top = lua_gettop(L); |
| 6562 | DEFER(lua_settop(L, top)); | 6565 | DEFER(lua_settop(L, top)); |
| 6563 | if (luaL_loadbuffer(L, codes.c_str(), codes.size(), macroChunk.c_str()) != 0) { | 6566 | if (isBlock) { |
| 6564 | std::string err = lua_tostring(L, -1); | 6567 | if (luaL_loadbuffer(L, codes.c_str(), codes.size(), macroChunk.c_str()) != 0) { |
| 6565 | throw CompileError(err, x); | 6568 | std::string err = lua_tostring(L, -1); |
| 6566 | } | 6569 | throw CompileError("lua macro is not expanding to valid block\n"s + err, x); |
| 6567 | if (!codes.empty()) { | 6570 | } else { |
| 6568 | if (_config.reserveLineNumber) { | 6571 | Proto* f = ((LClosure*)lua_topointer(L, -1))->p; |
| 6569 | codes.insert(0, nll(chainValue).substr(1)); | 6572 | for (int i = 0; i < f->sizelocvars; i++) { |
| 6573 | localVars.push_back(getstr(f->locvars[i].varname)); | ||
| 6574 | } | ||
| 6575 | } | ||
| 6576 | if (!codes.empty()) { | ||
| 6577 | if (_config.reserveLineNumber) { | ||
| 6578 | codes.insert(0, nll(chainValue).substr(1)); | ||
| 6579 | } | ||
| 6580 | codes.append(nlr(chainValue)); | ||
| 6570 | } | 6581 | } |
| 6571 | codes.append(nlr(chainValue)); | 6582 | return {nullptr, nullptr, std::move(codes), std::move(localVars)}; |
| 6583 | } else { | ||
| 6584 | auto expCode = "return ("s + codes + ')'; | ||
| 6585 | if (luaL_loadbuffer(L, expCode.c_str(), expCode.size(), macroChunk.c_str()) != 0) { | ||
| 6586 | std::string err = lua_tostring(L, -1); | ||
| 6587 | throw CompileError("lua macro is not expanding to valid expression\n"s + err, x); | ||
| 6588 | } | ||
| 6589 | Utils::trim(codes); | ||
| 6590 | codes = '(' + codes + ')'; | ||
| 6591 | auto plainItem = toAst<PlainItem_t>(codes, x); | ||
| 6592 | auto newChain = x->new_ptr<ChainValue_t>(); | ||
| 6593 | newChain->items.push_back(plainItem); | ||
| 6594 | ast_ptr<false, ast_node> exp; | ||
| 6595 | exp.set(newExp(newChain, x)); | ||
| 6596 | if (chainList.size() > 2 || (chainList.size() == 2 && !ast_is<Invoke_t, InvokeArgs_t>(chainList.back()))) { | ||
| 6597 | auto it = chainList.begin(); | ||
| 6598 | it++; | ||
| 6599 | if (chainList.size() > 1 && ast_is<Invoke_t, InvokeArgs_t>(*it)) it++; | ||
| 6600 | for (; it != chainList.end(); ++it) { | ||
| 6601 | newChain->items.push_back(*it); | ||
| 6602 | } | ||
| 6603 | } | ||
| 6604 | return {exp, nullptr, Empty, std::move(localVars)}; | ||
| 6572 | } | 6605 | } |
| 6573 | return {nullptr, nullptr, std::move(codes), std::move(localVars)}; | ||
| 6574 | } else if (type == "text"sv) { | 6606 | } else if (type == "text"sv) { |
| 6575 | if (!isBlock) { | 6607 | if (!isBlock) { |
| 6576 | throw CompileError("text macro can only be placed where block macro is allowed"sv, x); | 6608 | throw CompileError("text macro can only be placed where block macro is allowed"sv, x); |
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index ef242ea..5993ed5 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp | |||
| @@ -653,6 +653,8 @@ YueParser::YueParser() { | |||
| 653 | 653 | ||
| 654 | ExistentialOp = '?' >> not_('?'); | 654 | ExistentialOp = '?' >> not_('?'); |
| 655 | TableAppendingOp = and_('[') >> ("[]" | brackets_expression_error); | 655 | TableAppendingOp = and_('[') >> ("[]" | brackets_expression_error); |
| 656 | PlainItem = +any_char; | ||
| 657 | |||
| 656 | chain_call = ( | 658 | chain_call = ( |
| 657 | Callable >> -ExistentialOp >> -chain_items | 659 | Callable >> -ExistentialOp >> -chain_items |
| 658 | ) | ( | 660 | ) | ( |
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index f18cc1b..02292e1 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h | |||
| @@ -381,6 +381,7 @@ private: | |||
| 381 | AST_RULE(Invoke); | 381 | AST_RULE(Invoke); |
| 382 | AST_RULE(ExistentialOp); | 382 | AST_RULE(ExistentialOp); |
| 383 | AST_RULE(TableAppendingOp); | 383 | AST_RULE(TableAppendingOp); |
| 384 | AST_RULE(PlainItem); | ||
| 384 | AST_RULE(SpreadExp); | 385 | AST_RULE(SpreadExp); |
| 385 | AST_RULE(SpreadListExp); | 386 | AST_RULE(SpreadListExp); |
| 386 | AST_RULE(TableLit); | 387 | AST_RULE(TableLit); |
