From f38c4f94c9ba0499d923e59483d6381b97cce926 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Thu, 19 Dec 2024 11:26:23 +0800 Subject: Fixed MoonScript issue # 459. Supported local/const/close hoist in class body. --- spec/inputs/class.yue | 15 +++++++ spec/outputs/class.lua | 60 +++++++++++++++++++++++++ src/yuescript/yue_ast.h | 2 +- src/yuescript/yue_compiler.cpp | 99 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 169 insertions(+), 7 deletions(-) diff --git a/spec/inputs/class.yue b/spec/inputs/class.yue index 49537c2..3c73ebc 100644 --- a/spec/inputs/class.yue +++ b/spec/inputs/class.yue @@ -242,4 +242,19 @@ class Foo :add : +do + global xa, xb + class CX + xa = 1 + xb = 1 + new: => + +do + class CY + xa = 1 + local xb = 2 + new: => print xa, xb, xc, xd + const xc = 3 + close xd = : => + nil diff --git a/spec/outputs/class.lua b/spec/outputs/class.lua index 442ff4b..aaea33c 100644 --- a/spec/outputs/class.lua +++ b/spec/outputs/class.lua @@ -1229,4 +1229,64 @@ do _base_0.__class = _class_0 Foo = _class_0 end +do + local CX + do + local _class_0 + local _base_0 = { } + if _base_0.__index == nil then + _base_0.__index = _base_0 + end + _class_0 = setmetatable({ + __init = function(self) end, + __base = _base_0, + __name = "CX" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({ }, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0; + xa = 1 + xb = 1 + CX = _class_0 + end +end +do + local CY + local _class_0 + local xa, xb, xc, xd + local _base_0 = { } + if _base_0.__index == nil then + _base_0.__index = _base_0 + end + _class_0 = setmetatable({ + __init = function(self) + return print(xa, xb, xc, xd) + end, + __base = _base_0, + __name = "CY" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({ }, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0; + xa = 1 + xb = 2 + xc = 3 + xd = setmetatable({ }, { + __close = function(self) end + }) + local _close_0 = xd + CY = _class_0 +end return nil diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 92d67ac..033fe30 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -52,7 +52,6 @@ std::string_view ast_name() { return {}; } namespace yue { class ExpListLow_t; class TableBlock_t; -class Attrib_t; class SimpleTable_t; class TableLit_t; class Assign_t; @@ -181,6 +180,7 @@ AST_NODE(LocalAttrib) ast_ptr sep; ast_sel_list leftList; ast_ptr assign; + bool forceLocal = true; AST_MEMBER(LocalAttrib, &attrib, &sep, &leftList, &assign) AST_END(LocalAttrib) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 240e3b5..0b5a8c1 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.26.0"sv; +const std::string_view version = "0.26.1"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -8701,12 +8701,15 @@ private: auto classVar = getUnusedName("_class_"sv); addToScope(classVar); temp.push_back(indent() + "local "s + classVar + nll(classDecl)); + auto block = classDecl->new_ptr(); + str_list classConstVars; if (body) { str_list varDefs; for (auto item : body->contents.objects()) { if (auto statement = ast_cast(item)) { ClassDecl_t* clsDecl = nullptr; if (auto assignment = assignmentFrom(statement)) { + block->statements.push_back(statement); auto names = transformAssignDefs(assignment->expList.get(), DefOp::Mark); for (const auto& name : names) { varDefs.push_back(name.first); @@ -8736,9 +8739,85 @@ private: clsDecl = value->get_by_path(); BLOCK_END } else if (auto expList = expListFrom(statement)) { + block->statements.push_back(statement); if (auto value = singleValueFrom(expList)) { clsDecl = value->get_by_path(); } + } else if (auto local = statement->content.as()) { + block->statements.push_back(statement); + if (auto values = local->item.as()) { + for (auto name : values->nameList->names.objects()) { + auto varName = variableToString(static_cast(name)); + forceAddToScope(varName); + varDefs.push_back(varName); + } + } + } else if (auto localAttrib = statement->content.as()) { + auto explist = localAttrib->new_ptr(); + for (auto item : localAttrib->leftList.objects()) { + auto value = item->new_ptr(); + switch (item->get_id()) { + case id(): { + auto callable = item->new_ptr(); + callable->item.set(item); + auto chainValue = item->new_ptr(); + chainValue->items.push_back(callable); + value->item.set(chainValue); + break; + } + case id(): + value->item.set(item); + break; + case id(): + case id(): { + auto simpleValue = item->new_ptr(); + simpleValue->value.set(item); + value->item.set(simpleValue); + break; + } + default: YUEE("AST node mismatch", item); break; + } + explist->exprs.push_back(newExp(value, value)); + } + auto assignment = localAttrib->new_ptr(); + assignment->expList.set(explist); + assignment->action.set(localAttrib->assign); + auto names = transformAssignDefs(assignment->expList.get(), DefOp::Get); + for (const auto& name : names) { + forceAddToScope(name.first); + markVarConst(name.first); + varDefs.push_back(name.first); + classConstVars.push_back(name.first); + } + auto info = extractDestructureInfo(assignment, true, false); + if (!info.destructures.empty()) { + 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()) { + forceAddToScope(item.targetVar); + markVarConst(item.targetVar); + varDefs.push_back(item.targetVar); + classConstVars.push_back(item.targetVar); + } + } + } + } + auto stmt = statement->new_ptr(); + stmt->comments.dup(statement->comments); + auto newAttrib = localAttrib->new_ptr(); + newAttrib->attrib.set(localAttrib->attrib); + newAttrib->leftList.dup(localAttrib->leftList); + newAttrib->assign.set(localAttrib->assign); + newAttrib->forceLocal = false; + stmt->content.set(newAttrib); + stmt->appendix.set(statement->appendix); + block->statements.push_back(stmt); + } else if (statement->content.is()) { + throw CompileError("global statement is not allowed here"sv, statement->content); } if (clsDecl) { std::string clsName; @@ -8786,12 +8865,17 @@ private: } break; } - case id(): - transformStatement(static_cast(content), statements); - break; + case id(): break; default: YUEE("AST node mismatch", content); break; } } + for (const auto& classVar : classConstVars) { + auto& scope = _scopes.back(); + scope.vars->insert_or_assign(classVar, VarType::Local); + } + for (auto stmt_ : block->statements.objects()) { + transformStatement(static_cast(stmt_), statements); + } for (auto& member : members) { switch (member.type) { case MemType::Common: @@ -10608,6 +10692,7 @@ private: void transformLocalAttrib(LocalAttrib_t* localAttrib, str_list& out) { auto x = localAttrib; + bool forceLocal = localAttrib->forceLocal; if (x->leftList.size() < x->assign->values.size()) { auto num = x->leftList.size(); if (num > 1) { @@ -10652,7 +10737,7 @@ private: ++i; if (j != je) ++j; } - bool checkValuesLater = false; + bool checkValuesLater = !forceLocal; for (ast_node* value : assignA->values.objects()) { if (ast_is(value)) { if (auto sVal = singleValueFrom(value)) { @@ -10744,7 +10829,9 @@ private: } } str_list temp; - temp.push_back(indent() + "local "s + join(vars, ", "sv) + nll(x)); + if (localAttrib->forceLocal) { + temp.push_back(indent() + "local "s + join(vars, ", "sv) + nll(x)); + } transformAssignment(assignment, temp); for (const auto& name : vars) { markVarConst(name); -- cgit v1.2.3-55-g6feb