From 2f8215df7288e0aac690c8e8b1ff79865f114302 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 3 Sep 2024 23:23:25 +0800 Subject: fix correct evaluation order for multi-value assignments. --- src/yuescript/parser.cpp | 10 +- src/yuescript/parser.hpp | 11 ++ src/yuescript/yue_compiler.cpp | 247 ++++++++++++++++++++++++++++------------- 3 files changed, 184 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/yuescript/parser.cpp b/src/yuescript/parser.cpp index 5e4caa2..5d0773c 100644 --- a/src/yuescript/parser.cpp +++ b/src/yuescript/parser.cpp @@ -15,15 +15,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #include #include #include -#include #include "yuescript/parser.hpp" -#define _DEFER(code, line) std::shared_ptr _defer_##line(nullptr, [&](auto) { \ - code; \ -}) -#define DEFER(code) _DEFER(code, __LINE__) - namespace parserlib { // internal private class that manages access to the public classes' internals. @@ -914,7 +908,7 @@ bool _context::parse_non_term(rule& r) { // save the state of the rule rule::_state old_state = r.m_state; // restore the rule's state - DEFER(r.m_state = old_state); + rule::_state_guard quard(old_state, &r.m_state); // success/failure result bool ok = false; @@ -1008,7 +1002,7 @@ bool _context::parse_term(rule& r) { // save the state of the rule rule::_state old_state = r.m_state; // restore the rule's state - DEFER(r.m_state = old_state); + rule::_state_guard quard(old_state, &r.m_state); // success/failure result bool ok = false; diff --git a/src/yuescript/parser.hpp b/src/yuescript/parser.hpp index 71bbc1a..5ab327f 100644 --- a/src/yuescript/parser.hpp +++ b/src/yuescript/parser.hpp @@ -294,6 +294,17 @@ private: , m_mode(mode) { } }; + struct _state_guard { + _state m_old_state; + _state* m_current_state; + _state_guard(const _state& old, _state* new_) + : m_old_state(old) + , m_current_state(new_) { } + ~_state_guard() { + *m_current_state = m_old_state; + } + }; + // internal expression _expr* m_expr; diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 32db488..cbb5f81 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -75,7 +75,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.24.1"sv; +const std::string_view version = "0.25.0"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -1943,16 +1943,26 @@ private: std::string getDestrucureDefine(ExpListAssign_t* assignment) { auto info = extractDestructureInfo(assignment, true, false); - if (info.assignment) { - _buf << getPreDefineLine(info.assignment); - } if (!info.destructures.empty()) { str_list defs; - for (const auto& destruct : info.destructures) { - for (const auto& item : destruct.items) { - if (!item.targetVar.empty()) { - if (addToScope(item.targetVar)) { - defs.push_back(item.targetVar); + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { + if (!item.targetVar.empty()) { + if (addToScope(item.targetVar)) { + defs.push_back(item.targetVar); + } + } + } + } else { + const auto& assignment = std::get(des); + if (!assignment.extraAssignment) { + auto names = transformAssignDefs(assignment.ptr->expList, DefOp::Get); + for (const auto& name : names) { + if (addToScope(name.first)) { + defs.push_back(name.first); + } } } } @@ -2007,12 +2017,15 @@ private: void markDestructureConst(ExpListAssign_t* assignment) { auto info = extractDestructureInfo(assignment, true, false); - for (auto& destruct : info.destructures) { - for (auto& item : destruct.items) { - if (item.targetVar.empty()) { - throw CompileError("can only declare variable as const"sv, item.target); + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { + if (item.targetVar.empty()) { + throw CompileError("can only declare variable as const"sv, item.target); + } + markVarConst(item.targetVar); } - markVarConst(item.targetVar); } } } @@ -2079,7 +2092,7 @@ private: BLOCK_START auto value = singleValueFrom(*it); BREAK_IF(!value); - if (value->item.is() || value->get_by_path()) { + if (value->item.is() || value->get_by_path() || value->get_by_path()) { holdItem = true; break; } @@ -2404,11 +2417,24 @@ private: bool extraScope = false; if (info.extraScope) { str_list defs; - for (auto& destruct : info.destructures) { - for (auto& item : destruct.items) { - if (!item.targetVar.empty()) { - if (!isDefined(item.targetVar)) { - defs.push_back(item.targetVar); + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + const auto& destruct = std::get(des); + for (auto& item : destruct.items) { + if (!item.targetVar.empty()) { + if (!isDefined(item.targetVar)) { + defs.push_back(item.targetVar); + } + } + } + } else { + const auto& assignment = std::get(des); + if (!assignment.extraAssignment) { + auto names = transformAssignDefs(assignment.ptr->expList, DefOp::Get); + for (const auto& name : names) { + if (addToScope(name.first)) { + defs.push_back(name.first); + } } } } @@ -2426,10 +2452,13 @@ private: pushScope(); } } - if (info.assignment) { - transformAssignmentCommon(info.assignment, temp); - } - for (auto& destruct : info.destructures) { + for (auto& des : info.destructures) { + if (std::holds_alternative(des)) { + auto assignment = std::get(des).ptr.get(); + transformAssignment(assignment, temp); + continue; + } + auto& destruct = std::get(des); std::list, ast_ptr>> leftPairs; bool extraScope = false; if (!destruct.inlineAssignment && destruct.items.size() == 1) { @@ -2931,17 +2960,21 @@ private: return pairs; } + struct AssignmentPtr { + ast_ptr ptr; + bool extraAssignment = false; + }; + struct DestructureInfo { - std::list destructures; - ast_ptr assignment; + std::list> destructures; bool extraScope = false; }; DestructureInfo extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly, bool optional) { + if (!assignment->action.is()) return {}; auto x = assignment; bool extraScope = false; - std::list destructs; - if (!assignment->action.is()) return {destructs, nullptr}; + std::list> destructs; auto exprs = assignment->expList->exprs.objects(); auto values = assignment->action.to()->values.objects(); size_t size = std::max(exprs.size(), values.size()); @@ -2951,10 +2984,26 @@ private: while (values.size() < size) values.emplace_back(nil); } using iter = node_container::iterator; - std::vector> destructPairs; + std::vector> assignPairs; ast_list valueItems; str_list temp; pushScope(); + auto checkCommonAssignment = [&]() { + if (!assignPairs.empty()) { + auto expList = x->new_ptr(); + auto newAssign = x->new_ptr(); + newAssign->expList.set(expList); + auto assign = x->new_ptr(); + newAssign->action.set(assign); + for (const auto& pair : assignPairs) { + expList->exprs.push_back(*pair.first); + assign->values.push_back(*pair.second); + } + assignPairs.clear(); + destructs.push_back(AssignmentPtr{newAssign, false}); + } + }; + bool hasDestructuring = false; for (auto i = exprs.begin(), j = values.begin(); i != exprs.end(); ++i, ++j) { auto expr = *i; auto value = singleValueFrom(expr); @@ -2974,6 +3023,8 @@ private: } } if (destructNode) { + hasDestructuring = true; + checkCommonAssignment(); if (*j != nil) { if (auto ssVal = simpleSingleValueFrom(*j)) { switch (ssVal->value->get_id()) { @@ -2989,7 +3040,6 @@ private: } } } - destructPairs.push_back({i, j}); auto subDestruct = destructNode->new_ptr(); auto subMetaDestruct = destructNode->new_ptr(); const node_container* dlist = nullptr; @@ -3113,19 +3163,26 @@ private: if (!varDefOnly && !subDestruct->values.empty() && !subMetaDestruct->values.empty()) { auto var = singleVariableFrom(*j, AccessType::None); if (var.empty() || !isLocal(var)) { + checkCommonAssignment(); auto objVar = getUnusedName("_obj_"sv); addToScope(objVar); valueItems.pop_back(); valueItems.push_back(toAst(objVar, *j)); - exprs.push_back(valueItems.back()); - values.push_back(*j); + auto expList = x->new_ptr(); + auto newAssign = x->new_ptr(); + newAssign->expList.set(expList); + auto assign = x->new_ptr(); + newAssign->action.set(assign); + expList->exprs.push_back(valueItems.back()); + assign->values.push_back(*j); + destructs.push_back(AssignmentPtr{newAssign, true}); extraScope = true; } } TableLit_t* tabs[] = {subDestruct.get(), subMetaDestruct.get()}; for (auto tab : tabs) { if (!tab->values.empty()) { - auto& destruct = destructs.emplace_back(); + Destructure destruct; if (!varDefOnly) { destruct.value = valueItems.back(); destruct.valueVar = singleVariableFrom(destruct.value, AccessType::None); @@ -3172,29 +3229,28 @@ private: destruct.valueVar.clear(); } } + destructs.push_back(destruct); } } + } else { + assignPairs.push_back({i, j}); } } - for (const auto& p : destructPairs) { - exprs.erase(p.first); - values.erase(p.second); - } - ast_ptr newAssignment; - if (!destructPairs.empty() && !exprs.empty()) { - auto x = assignment; - auto expList = x->new_ptr(); - auto newAssign = x->new_ptr(); - newAssign->expList.set(expList); - for (auto expr : exprs) expList->exprs.push_back(expr); - auto assign = x->new_ptr(); - for (auto value : values) assign->values.push_back(value); - newAssign->action.set(assign); - newAssignment = newAssign; + if (!hasDestructuring) { + popScope(); + return {}; } + checkCommonAssignment(); if (!varDefOnly) { - for (auto& des : destructs) { + for (auto& d : destructs) { + if (std::holds_alternative(d)) { + continue; + } + auto& des = std::get(d); for (const auto& item : des.items) { + if (!item.structure) { + continue; + } for (auto node : item.structure->items.objects()) { if (auto exp = ast_cast(node)) { if (auto value = simpleSingleValueFrom(node)) { @@ -3236,7 +3292,7 @@ private: } } popScope(); - return {std::move(destructs), newAssignment, extraScope}; + return {std::move(destructs), extraScope}; } void transformAssignmentCommon(ExpListAssign_t* assignment, str_list& out) { @@ -4746,23 +4802,30 @@ private: } auto info = extractDestructureInfo(assignment, true, false); if (!info.destructures.empty()) { - for (const auto& destruct : info.destructures) - for (const auto& item : destruct.items) - if (!item.targetVar.empty()) { - if (std::isupper(item.targetVar[0]) && capital) { - capital->decls.push_back(item.targetVar); - } else if (any) { - any->decls.push_back(item.targetVar); + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { + if (!item.targetVar.empty()) { + if (std::isupper(item.targetVar[0]) && capital) { + capital->decls.push_back(item.targetVar); + } else if (any) { + any->decls.push_back(item.targetVar); + } + } + } + } else { + const auto& assignment = std::get(des); + if (!assignment.extraAssignment) { + auto defs = transformAssignDefs(assignment.ptr->expList, DefOp::Get); + for (const auto& def : defs) { + if (std::isupper(def.first[0]) && capital) { + capital->decls.push_back(def.first); + } else if (any) { + any->decls.push_back(def.first); + } } } - } - if (info.assignment) { - auto defs = transformAssignDefs(info.assignment->expList, DefOp::Get); - for (const auto& def : defs) { - if (std::isupper(def.first[0]) && capital) { - capital->decls.push_back(def.first); - } else if (any) { - any->decls.push_back(def.first); } } } @@ -8551,10 +8614,17 @@ private: } auto info = extractDestructureInfo(assignment, true, false); if (!info.destructures.empty()) { - for (const auto& destruct : info.destructures) - for (const auto& item : destruct.items) - if (!item.targetVar.empty() && addToScope(item.targetVar)) + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + continue; + } + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { + if (!item.targetVar.empty() && addToScope(item.targetVar)) { varDefs.push_back(item.targetVar); + } + } + } } BLOCK_START auto assign = assignment->action.as(); @@ -9006,10 +9076,17 @@ private: } auto info = extractDestructureInfo(assignment, true, false); if (!info.destructures.empty()) { - for (const auto& destruct : info.destructures) - for (const auto& item : destruct.items) - if (!item.targetVar.empty() && !isDefined(item.targetVar)) + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + continue; + } + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { + if (!item.targetVar.empty() && !isDefined(item.targetVar)) { return traversal::Stop; + } + } + } } BLOCK_START auto assign = assignment->action.as(); @@ -9248,13 +9325,19 @@ private: auto names = transformAssignDefs(expList, DefOp::Get); auto info = extractDestructureInfo(assignment, true, false); if (!info.destructures.empty()) { - for (const auto& destruct : info.destructures) - for (const auto& item : destruct.items) + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + continue; + } + const auto& destruct = std::get(des); + for (const auto& item : destruct.items) { if (!item.targetVar.empty()) { auto dot = ast_cast(item.structure->items.back()); auto uname = dot->name.as(); names.emplace_back(item.targetVar, uname ? _parser.toString(uname) : Empty); } + } + } } if (_info.exportDefault) { out.back().append(indent() + _info.moduleName + " = "s + names.back().first + nlr(exportNode)); @@ -10213,7 +10296,11 @@ private: auto info = extractDestructureInfo(assignment, true, false); transformAssignment(assignment, temp, true); str_list conds; - for (const auto& destruct : info.destructures) { + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + continue; + } + const auto& destruct = std::get(des); for (const auto& item : destruct.items) { if (!item.defVal) { transformExp(item.target, conds, ExpUsage::Closure); @@ -10475,7 +10562,11 @@ private: assignment->expList.set(leftList); assignment->action.set(assign); auto info = extractDestructureInfo(assignment, true, false); - for (auto& destruct : info.destructures) { + for (const auto& des : info.destructures) { + if (std::holds_alternative(des)) { + continue; + } + const auto& destruct = std::get(des); for (auto& item : destruct.items) { if (item.targetVar.empty()) { throw CompileError("can only declare variable as const"sv, item.target); @@ -10569,7 +10660,11 @@ private: assignment->action.set(assignB); auto info = extractDestructureInfo(assignment, true, false); str_list vars; - for (auto& destruct : info.destructures) { + for (auto& des : info.destructures) { + if (std::holds_alternative(des)) { + continue; + } + const auto& destruct = std::get(des); for (auto& item : destruct.items) { if (item.targetVar.empty()) { throw CompileError("can only declare variable as const"sv, item.target); -- cgit v1.2.3-55-g6feb