From a6744ed09b49b740dfc2852d655f5ed6dd471cbf Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 8 Feb 2022 16:39:20 +0800 Subject: fix a case combining the use of existential op and metatable op. raise error when use existential op in the left part in an assignment. --- spec/inputs/existential.yue | 7 ++++ spec/outputs/existential.lua | 28 ++++++++++--- src/yuescript/yue_compiler.cpp | 90 ++++++++++++++++++++++-------------------- 3 files changed, 78 insertions(+), 47 deletions(-) diff --git a/spec/inputs/existential.yue b/spec/inputs/existential.yue index 3055705..101e1d8 100644 --- a/spec/inputs/existential.yue +++ b/spec/inputs/existential.yue @@ -49,3 +49,10 @@ with? io.open "test.txt", "w" \write "hello" \close! +tb?.a#? 123 + +with? tb.#?.index# + .a = 1 + +nil + diff --git a/spec/outputs/existential.lua b/spec/outputs/existential.lua index 4a64c49..b594218 100644 --- a/spec/outputs/existential.lua +++ b/spec/outputs/existential.lua @@ -148,9 +148,27 @@ end)() ~= nil) or (function() end return nil end)() -local _with_0 = io.open("test.txt", "w") -if _with_0 ~= nil then - _with_0:write("hello") - _with_0:close() +do + local _with_0 = io.open("test.txt", "w") + if _with_0 ~= nil then + _with_0:write("hello") + _with_0:close() + end +end +if tb ~= nil then + local _obj_1 = getmetatable(tb).__a + if _obj_1 ~= nil then + _obj_1(123) + end +end +do + local _with_0 + local _obj_1 = getmetatable(tb) + if _obj_1 ~= nil then + _with_0 = getmetatable(_obj_1).__index + end + if _with_0 ~= nil then + _with_0.a = 1 + end end -return _with_0 +return nil diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 2da72da..17822c6 100755 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -60,7 +60,7 @@ using namespace parserlib; typedef std::list str_list; -const std::string_view version = "0.9.6"sv; +const std::string_view version = "0.9.7"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -800,6 +800,11 @@ private: return true; } } else { + if (std::find_if(chainItems.begin(), chainItems.end(), [](ast_node* node) { + return ast_is(node); + }) != chainItems.end()) { + return false; + } auto lastItem = chainItems.back(); switch (lastItem->getId()) { case id(): @@ -1207,48 +1212,49 @@ private: BREAK_IF(!value); auto chainValue = value->item.as(); BREAK_IF(!chainValue); - if (specialChainValue(chainValue) == ChainType::Metatable) { - str_list args; - chainValue->items.pop_back(); - if (chainValue->items.empty()) { - if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); - } else { - args.push_back(_withVars.top()); - } + auto dot = ast_cast(chainValue->items.back()); + BREAK_IF(!dot); + BREAK_IF(!dot->name.is()); + str_list args; + chainValue->items.pop_back(); + if (chainValue->items.empty()) { + if (_withVars.empty()) { + throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); } else { - transformExp(static_cast(*it), args, ExpUsage::Closure); + args.push_back(_withVars.top()); } - if (vit != values.end()) transformAssignItem(*vit, args); - else args.push_back("nil"s); - _buf << indent() << globalVar("setmetatable"sv, x) << '(' << join(args, ", "sv) << ')' << nll(x); - str_list temp; - temp.push_back(clearBuf()); - auto newExpList = x->new_ptr(); - auto newAssign = x->new_ptr(); - auto newAssignment = x->new_ptr(); - newAssignment->expList.set(newExpList); - newAssignment->action.set(newAssign); - for (auto exp : exprs) { - if (exp != *it) newExpList->exprs.push_back(exp); - } - for (auto value : values) { - if (value != *vit) newAssign->values.push_back(value); - } - if (newExpList->exprs.empty() && newAssign->values.empty()) { - out.push_back(temp.back()); - return; - } - if (newExpList->exprs.size() < newAssign->values.size()) { - auto exp = toAst("_"sv, x); - while (newExpList->exprs.size() < newAssign->values.size()) { - newExpList->exprs.push_back(exp); - } - } - transformAssignment(newAssignment, temp); - out.push_back(join(temp)); + } else { + transformExp(static_cast(*it), args, ExpUsage::Closure); + } + if (vit != values.end()) transformAssignItem(*vit, args); + else args.push_back("nil"s); + _buf << indent() << globalVar("setmetatable"sv, x) << '(' << join(args, ", "sv) << ')' << nll(x); + str_list temp; + temp.push_back(clearBuf()); + auto newExpList = x->new_ptr(); + auto newAssign = x->new_ptr(); + auto newAssignment = x->new_ptr(); + newAssignment->expList.set(newExpList); + newAssignment->action.set(newAssign); + for (auto exp : exprs) { + if (exp != *it) newExpList->exprs.push_back(exp); + } + for (auto value : values) { + if (value != *vit) newAssign->values.push_back(value); + } + if (newExpList->exprs.empty() && newAssign->values.empty()) { + out.push_back(temp.back()); return; } + if (newExpList->exprs.size() < newAssign->values.size()) { + auto exp = toAst("_"sv, x); + while (newExpList->exprs.size() < newAssign->values.size()) { + newExpList->exprs.push_back(exp); + } + } + transformAssignment(newAssignment, temp); + out.push_back(join(temp)); + return; BLOCK_END if (vit != values.end()) ++vit; } @@ -4287,15 +4293,15 @@ private: #endif // YUE_NO_MACRO } const auto& chainList = chainValue->items.objects(); - if (transformChainWithMetatable(chainList, out, usage, assignList)) { - return; - } if (transformChainEndWithEOP(chainList, out, usage, assignList)) { return; } if (transformChainWithEOP(chainList, out, usage, assignList)) { return; } + if (transformChainWithMetatable(chainList, out, usage, assignList)) { + return; + } if (transformChainEndWithColonItem(chainList, out, usage, assignList)) { return; } -- cgit v1.2.3-55-g6feb