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. --- spec/inputs/export.yue | 6 +- spec/inputs/syntax.yue | 2 +- spec/inputs/try-catch.yue | 3 +- spec/outputs/assign.lua | 6 +- spec/outputs/attrib.lua | 18 ++--- spec/outputs/do.lua | 6 +- spec/outputs/export.lua | 13 +++- spec/outputs/syntax.lua | 8 +- spec/outputs/try-catch.lua | 11 ++- src/yuescript/yue_ast.h | 14 ++-- src/yuescript/yue_compiler.cpp | 170 +++++++++++++++++++++++++++++++++-------- src/yuescript/yue_parser.cpp | 71 +++++++++++------ src/yuescript/yue_parser.h | 4 + 13 files changed, 240 insertions(+), 92 deletions(-) diff --git a/spec/inputs/export.yue b/spec/inputs/export.yue index 15ffbcb..66e6736 100644 --- a/spec/inputs/export.yue +++ b/spec/inputs/export.yue @@ -29,7 +29,7 @@ export cbVal = do (x)<- f return x h -export y = -> +export yy = -> h = 100 k = 100 @@ -84,3 +84,7 @@ export v2 = 2 export v3 = class v4 v5 = 5 +export.<"abc"> = 1 +export. = "export" +export. = => {} +export["a-b-c-x"] = 123 diff --git a/spec/inputs/syntax.yue b/spec/inputs/syntax.yue index 4f693d4..d60a145 100644 --- a/spec/inputs/syntax.yue +++ b/spec/inputs/syntax.yue @@ -214,7 +214,7 @@ tb[] = 2 tb[] = 3 x = 0 -_ = (if ntype(v) == "fndef" then x += 1) for v in *values +_ = (if ntype(v) == "fndef" then x + 1) for v in *values hello = diff --git a/spec/inputs/try-catch.yue b/spec/inputs/try-catch.yue index 7287c33..e38cbef 100644 --- a/spec/inputs/try-catch.yue +++ b/spec/inputs/try-catch.yue @@ -23,6 +23,7 @@ try tb.func try tb.func! try tb.func() try (tb.func!) +try (tb\func(1, 2, 3)) try tb.func 1 try tb.func(1) @@ -32,7 +33,7 @@ catch err print err) print "OK" -if try func 1 +if try (func 1) catch err print err print "OK" diff --git a/spec/outputs/assign.lua b/spec/outputs/assign.lua index d939769..228e859 100644 --- a/spec/outputs/assign.lua +++ b/spec/outputs/assign.lua @@ -36,10 +36,10 @@ local x do local f = getHandler() if f then - x = ((function() + do f() - return 123 - end)()) + x = 123 + end end end return _(function() diff --git a/spec/outputs/attrib.lua b/spec/outputs/attrib.lua index aed549d..fbac28e 100644 --- a/spec/outputs/attrib.lua +++ b/spec/outputs/attrib.lua @@ -32,21 +32,17 @@ do end)() local b = (function() if not false then - return ((function() - if x then - return 1 - end - end)()) + if x then + return 1 + end end end)() local c = (function() if true then - return ((function() - local _exp_0 = x - if "abc" == _exp_0 then - return 998 - end - end)()) + local _exp_0 = x + if "abc" == _exp_0 then + return 998 + end end end)() local d = (function() diff --git a/spec/outputs/do.lua b/spec/outputs/do.lua index 96d1022..6473e03 100644 --- a/spec/outputs/do.lua +++ b/spec/outputs/do.lua @@ -32,10 +32,10 @@ local t = { } return function(y, k) if y == nil then - y = ((function() + do x = 10 + 2 - return x - end)()) + y = x + end end if k == nil then do diff --git a/spec/outputs/export.lua b/spec/outputs/export.lua index 962f18c..f1beeee 100644 --- a/spec/outputs/export.lua +++ b/spec/outputs/export.lua @@ -1,4 +1,4 @@ -local _module_0 = { } +local _module_0 = setmetatable({ }, { }) local a, b, c = 223, 343, 123 _module_0["a"], _module_0["b"], _module_0["c"] = a, b, c local cool = "dad" @@ -62,11 +62,12 @@ do end) end _module_0["cbVal"] = cbVal -y = function() +local yy +yy = function() local h = 100 local k = 100 end -_module_0["y"] = y +_module_0["yy"] = yy do local _exp_0 = h if 100 == _exp_0 or 150 == _exp_0 then @@ -322,4 +323,10 @@ do end _module_0["v3"] = v3 v5 = 5 +getmetatable(_module_0)["abc"] = 1 +getmetatable(_module_0).__name = "export" +getmetatable(_module_0).__call = function(self) + return { } +end +_module_0["a-b-c-x"] = 123 return _module_0 diff --git a/spec/outputs/syntax.lua b/spec/outputs/syntax.lua index 990083f..2afadbb 100644 --- a/spec/outputs/syntax.lua +++ b/spec/outputs/syntax.lua @@ -239,11 +239,9 @@ x = 0 local _list_0 = values for _index_0 = 1, #_list_0 do local v = _list_0[_index_0] - _ = ((function() - if ntype(v) == "fndef" then - x = x + 1 - end - end)()) + if ntype(v) == "fndef" then + _ = x + 1 + end end hello = { something = world, diff --git a/spec/outputs/try-catch.lua b/spec/outputs/try-catch.lua index 692905e..129d412 100644 --- a/spec/outputs/try-catch.lua +++ b/spec/outputs/try-catch.lua @@ -21,7 +21,14 @@ end pcall(tb.func) pcall(tb.func) pcall(tb.func) -pcall((tb.func())) +pcall((tb.func)) +pcall(((function() + local _base_0 = tb + local _fn_0 = _base_0.func + return _fn_0 and function(...) + return _fn_0(_base_0, ...) + end +end)()), 1, 2, 3) pcall(tb.func, 1) pcall(tb.func, 1) if (xpcall(func, function(err) @@ -29,7 +36,7 @@ if (xpcall(func, function(err) end, 1)) then print("OK") end -if xpcall(func, function(err) +if xpcall((func), function(err) return print(err) end, 1) then print("OK") 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