From 78d5e6f44c06ac24aee667b5f9a9e642dcc6208d Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 17 May 2023 09:19:20 +0800 Subject: fix issue #133. --- src/yuescript/yue_ast.h | 14 ++-- src/yuescript/yue_compiler.cpp | 170 +++++++++++++++++++++++++++++++++-------- src/yuescript/yue_parser.cpp | 71 +++++++++++------ src/yuescript/yue_parser.h | 4 + 4 files changed, 197 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 47ddb0b..65008fb 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -522,12 +522,12 @@ class FunLit_t; AST_NODE(SimpleValue) ast_sel value; + TableLit_t, ConstValue_t, + If_t, Switch_t, With_t, ClassDecl_t, + ForEach_t, For_t, While_t, Do_t, Try_t, + UnaryValue_t, + TblComprehension_t, Comprehension_t, + FunLit_t, Num_t, VarArg_t> value; AST_MEMBER(SimpleValue, &value) AST_END(SimpleValue, "simple_value"sv) @@ -708,7 +708,7 @@ class Macro_t; AST_NODE(Export) ast_ptr def; - ast_sel target; + ast_sel target; ast_ptr assign; AST_MEMBER(Export, &def, &target, &assign) AST_END(Export, "export"sv) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 5c8ff5e..b131eff 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -72,7 +72,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.16.3"sv; +const std::string_view version = "0.16.4"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -395,6 +395,8 @@ private: int level; }; std::list gotos; + std::unordered_set _exportedKeys; + std::unordered_set _exportedMetaKeys; enum class LocalMode { None = 0, @@ -778,7 +780,15 @@ private: Value_t* singleValueFrom(ast_node* item) const { if (auto unary = singleUnaryExpFrom(item)) { if (unary->ops.empty()) { - return static_cast(unary->expos.back()); + Value_t* value = static_cast(unary->expos.back()); + if (auto chain = ast_cast(value->item); chain && chain->items.size() == 1) { + if (auto exp = chain->getByPath()) { + if (auto insideValue = singleValueFrom(exp)) { + return insideValue; + } + } + } + return value; } } return nullptr; @@ -3626,7 +3636,7 @@ private: ClassDecl_t* classDecl = nullptr; ast_ptr assignment; if (auto exportNode = stmt->content.as()) { - if (exportNode->assign) { + if (exportNode->assign && exportNode->target.is()) { assignment = stmt->new_ptr(); assignment->expList.set(exportNode->target); assignment->action.set(exportNode->assign); @@ -3694,7 +3704,7 @@ private: } } if (isRoot && !_info.moduleName.empty() && !_info.exportMacro) { - block->statements.push_front(toAst(_info.moduleName + (_info.exportDefault ? "=nil"s : "={}"s), block)); + block->statements.push_front(toAst(_info.moduleName + (_info.exportDefault ? "=nil"s : (_info.exportMetatable ? "=<>:{}"s : "={}"s)), block)); } switch (usage) { case ExpUsage::Closure: @@ -7363,43 +7373,139 @@ private: } } + std::optional getExportKey(ast_node* node) { + switch (node->getId()) { + case id(): { + return _parser.toString(node); + } + case id(): { + auto strNode = static_cast(node); + switch (strNode->str->getId()) { + case id(): { + auto str = static_cast(strNode->str.get()); + if (str->segments.size() == 1) { + auto content = static_cast(str->segments.front()); + if (auto inner = content->content.as()) { + return _parser.toString(inner); + } + } + return std::nullopt; + } + case id(): { + auto str = _parser.toString(strNode->str); + Utils::replace(str, "\r\n"sv, "\n"); + return str.substr(1, str.length() - 2); + } + case id(): { + auto str = static_cast(strNode->str.get()); + return _parser.toString(str->content); + } + default: { + YUEE("AST node mismatch", strNode->str); + return std::nullopt; + } + } + } + case id(): { + auto metamethod = static_cast(node); + if (auto key = getExportKey(metamethod->item)) { + if (metamethod->item.is()) { + return "__"s + key.value(); + } else { + return key; + } + } + return std::nullopt; + } + case id(): { + auto dotChain = static_cast(node); + return getExportKey(dotChain->name); + } + case id(): { + if (auto value = singleValueFrom(node)) { + if (auto str = value->item.as()) { + return getExportKey(str); + } + } + } + } + return std::nullopt; + } + void transformExport(Export_t* exportNode, str_list& out) { auto x = exportNode; if (_scopes.size() > 1) { throw CompileError("can not do module export outside the root block"sv, exportNode); } if (exportNode->assign) { - auto expList = exportNode->target.to(); - if (expList->exprs.size() != exportNode->assign->values.size()) { - throw CompileError("left and right expressions must be matched in export statement"sv, x); - } - for (auto _exp : expList->exprs.objects()) { - auto exp = static_cast(_exp); - if (!variableFrom(exp) && !exp->getByPath() && !exp->getByPath()) { - throw CompileError("left hand expressions must be variables in export statement"sv, x); + if (ast_is(exportNode->target)) { + if (auto name = getExportKey(exportNode->target)) { + if (auto dotChain = exportNode->target.as(); + dotChain && dotChain->name.is()) { + auto nameStr = name.value(); + if (_exportedMetaKeys.find(nameStr) != _exportedMetaKeys.end()) { + throw CompileError("export module metamethod key \"" + nameStr + "\" duplicated"s, dotChain->name); + } else { + _exportedMetaKeys.insert(nameStr); + } + } else { + auto nameStr = name.value(); + if (_exportedKeys.find(nameStr) != _exportedKeys.end()) { + throw CompileError("export module key \"" + nameStr + "\" duplicated"s, exportNode->target); + } else { + _exportedKeys.insert(nameStr); + } + } } - } - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(exportNode->assign); - transformAssignment(assignment, out); - str_list 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) - if (!item.targetVar.empty()) - names.push_back(item.targetVar); - } - if (_info.exportDefault) { - out.back().append(indent() + _info.moduleName + " = "s + names.back() + nlr(exportNode)); + auto newChain = x->new_ptr(); + auto callable = toAst(_info.moduleName, x); + newChain->items.push_front(callable); + newChain->items.push_back(exportNode->target); + auto exp = newExp(newChain, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(exportNode->assign); + transformAssignment(assignment, out); } else { - str_list lefts, rights; - for (const auto& name : names) { - lefts.push_back(_info.moduleName + "[\""s + name + "\"]"s); - rights.push_back(name); + auto expList = exportNode->target.to(); + if (expList->exprs.size() != exportNode->assign->values.size()) { + throw CompileError("left and right expressions must be matched in export statement"sv, x); + } + for (auto _exp : expList->exprs.objects()) { + auto exp = static_cast(_exp); + if (!variableFrom(exp) && !exp->getByPath() && !exp->getByPath()) { + throw CompileError("left hand expressions must be variables in export statement"sv, x); + } + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(exportNode->assign); + transformAssignment(assignment, out); + str_list 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) + if (!item.targetVar.empty()) + names.push_back(item.targetVar); + } + if (_info.exportDefault) { + out.back().append(indent() + _info.moduleName + " = "s + names.back() + nlr(exportNode)); + } else { + str_list lefts, rights; + for (const auto& name : names) { + if (_exportedKeys.find(name) != _exportedKeys.end()) { + throw CompileError("export module key \"" + name + "\" duplicated"s, x); + } else { + _exportedKeys.insert(name); + } + lefts.push_back(_info.moduleName + "[\""s + name + "\"]"s); + rights.push_back(name); + } + out.back().append(indent() + join(lefts, ", "sv) + " = "s + join(rights, ", "sv) + nlr(exportNode)); } - out.back().append(indent() + join(lefts, ", "sv) + " = "s + join(rights, ", "sv) + nlr(exportNode)); } } else { if (auto macro = exportNode->target.as()) { diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index f215ee4..99fea0a 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -93,6 +93,11 @@ YueParser::YueParser() { return false; }); + export_expression_error = pl::user(true_(), [](const item_t& item) { + throw ParserError("invalid export expression"sv, item.begin); + return false; + }); + #define ensure(patt, finally) ((patt) >> (finally) | (finally) >> cut) #define key(str) (expr(str) >> not_alpha_num) @@ -666,29 +671,48 @@ YueParser::YueParser() { State* st = reinterpret_cast(item.user_data); st->exportCount++; return true; - }) >> (pl::user(space >> ExportDefault >> space >> Exp, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - if (st->exportDefault) { - throw ParserError("export default has already been declared"sv, item.begin); - } - if (st->exportCount > 1) { - throw ParserError("there are items already being exported"sv, item.begin); - } - st->exportDefault = true; - return true; - }) - | (not_(space >> ExportDefault) >> pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - if (st->exportDefault && st->exportCount > 1) { - throw ParserError("can not export any more items when 'export default' is declared"sv, item.begin); - } - return true; - }) >> space >> ExpList >> -(space >> Assign)) - | space >> pl::user(Macro, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->exportMacro = true; - return true; - })) >> not_(space >> StatementAppendix); + }) >> ( + pl::user(space >> ExportDefault >> space >> Exp, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + if (st->exportDefault) { + throw ParserError("export default has already been declared"sv, item.begin); + } + if (st->exportCount > 1) { + throw ParserError("there are items already being exported"sv, item.begin); + } + st->exportDefault = true; + return true; + }) | + not_(space >> ExportDefault) >> pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + if (st->exportDefault && st->exportCount > 1) { + throw ParserError("can not export any more items when 'export default' is declared"sv, item.begin); + } + return true; + }) >> ( + and_(set(".[")) >> ((pl::user(and_('.' >> Metatable), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + if (st->exportMetatable) { + throw ParserError("module metatable duplicated"sv, item.begin); + } + if (st->exportMetamethod) { + throw ParserError("metatable should be exported before metamethod"sv, item.begin); + } + st->exportMetatable = true; + return true; + }) | pl::user(and_(".<"), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->exportMetamethod = true; + return true; + }) | true_()) >> (DotChainItem | index) >> space >> Assign | export_expression_error) | + space >> ExpList >> -(space >> Assign) + ) | + space >> pl::user(Macro, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->exportMacro = true; + return true; + }) + ) >> not_(space >> StatementAppendix); VariablePair = ':' >> Variable; @@ -897,6 +921,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) { res.moduleName = std::move(state.moduleName); res.exportDefault = state.exportDefault; res.exportMacro = state.exportMacro; + res.exportMetatable = !state.exportMetatable && state.exportMetamethod; } } catch (const ParserError& err) { res.error = {err.what(), err.line, err.col}; diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index 31134e1..59a16a8 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -37,6 +37,7 @@ struct ParseInfo { std::unique_ptr codes; bool exportDefault = false; bool exportMacro = false; + bool exportMetatable = false; std::string moduleName; std::string errorMessage(std::string_view msg, int errLine, int errCol) const; }; @@ -97,6 +98,8 @@ protected: } bool exportDefault = false; bool exportMacro = false; + bool exportMetatable = false; + bool exportMetamethod = false; int exportCount = 0; int moduleFix = 0; int expLevel = 0; @@ -129,6 +132,7 @@ private: NONE_AST_RULE(indentation_error); NONE_AST_RULE(braces_expression_error); NONE_AST_RULE(brackets_expression_error); + NONE_AST_RULE(export_expression_error); NONE_AST_RULE(inc_exp_level); NONE_AST_RULE(dec_exp_level); -- cgit v1.2.3-55-g6feb