From 303834e1b1e6cd9cae64b66c2ae44dcd7185238f Mon Sep 17 00:00:00 2001 From: Li Jin Date: Sun, 24 Jul 2022 22:13:08 +0800 Subject: add option --target=5.1 to generate Lua 5.1 compatible codes. add const destructure. make import item const by default. --- makefile | 4 +- spec/inputs/destructure.yue | 5 +- spec/inputs/export.yue | 4 +- spec/inputs/import.yue | 2 +- spec/inputs/macro.yue | 2 +- spec/inputs/metatable.yue | 2 +- spec/inputs/syntax.yue | 10 +- spec/inputs/tables.yue | 8 +- spec/inputs/with.yue | 2 +- spec/outputs/destructure.lua | 25 ++++- spec/outputs/export.lua | 10 +- spec/outputs/import.lua | 2 +- spec/outputs/macro.lua | 3 +- spec/outputs/metatable.lua | 1 - spec/outputs/syntax.lua | 10 +- spec/outputs/tables.lua | 8 +- spec/outputs/with.lua | 2 +- src/yue.cpp | 39 ++++--- src/yuescript/yue_ast.h | 24 +++-- src/yuescript/yue_compiler.cpp | 232 ++++++++++++++++++++++++++++++++--------- src/yuescript/yue_parser.cpp | 42 +++++--- src/yuescript/yue_parser.h | 8 +- src/yuescript/yuescript.cpp | 10 +- 23 files changed, 321 insertions(+), 134 deletions(-) diff --git a/makefile b/makefile index bf5298a..913d70b 100644 --- a/makefile +++ b/makefile @@ -266,7 +266,7 @@ test: release @mkdir -p $(TEST_OUTPUT) @echo "Compiling Yuescript codes..." @$(START_TIME) - @./$(BIN_NAME) $(TEST_INPUT) -t $(TEST_OUTPUT) -tl_enabled + @./$(BIN_NAME) $(TEST_INPUT) -t $(TEST_OUTPUT) --tl_enabled @./$(BIN_NAME) $(TEST_INPUT)/teal-lang.yue -o $(TEST_OUTPUT)/teal-lang.lua @echo -en "Compile time: " @$(END_TIME) @@ -279,7 +279,7 @@ test: release gen: release @echo "Compiling Yuescript codes..." @$(START_TIME) - @./$(BIN_NAME) $(TEST_INPUT) -t $(GEN_OUTPUT) -tl_enabled + @./$(BIN_NAME) $(TEST_INPUT) -t $(GEN_OUTPUT) --tl_enabled @./$(BIN_NAME) $(TEST_INPUT)/teal-lang.yue -o $(GEN_OUTPUT)/teal-lang.lua @echo -en "Compile time: " @$(END_TIME) diff --git a/spec/inputs/destructure.yue b/spec/inputs/destructure.yue index 3007adf..24282c0 100644 --- a/spec/inputs/destructure.yue +++ b/spec/inputs/destructure.yue @@ -11,7 +11,7 @@ do {:a,:b,:c,:d} = yeah - {a} = one, two + {a}, b = one, two {b}, c = one {d}, e = one, two @@ -184,3 +184,6 @@ do do {x: a.b = 1, y: a.c = 2} = x.x.x +do + const :width, :height = View.size + const {:x = 0.0, :y = 0.0} = point diff --git a/spec/inputs/export.yue b/spec/inputs/export.yue index 9113508..15ffbcb 100644 --- a/spec/inputs/export.yue +++ b/spec/inputs/export.yue @@ -55,7 +55,7 @@ f switch a f [i for i = 1,10] f for i = 1,10 do i f {k,v for k,v in pairs tb} -f for k,v in pairs tb do k,v +f for k,v in pairs tb do {k,v} f while a do true f with a .b = 123 @@ -70,7 +70,7 @@ _ = "#{switch a _ = "#{[i for i = 1,10]}" _ = "#{for i = 1,10 do i}" _ = "#{{k,v for k,v in pairs tb}}" -_ = "#{for k,v in pairs tb do k,v}" +_ = "#{for k,v in pairs tb do {k,v}}" _ = "#{while a do true}" _ = "#{with a .b = 123}" diff --git a/spec/inputs/import.yue b/spec/inputs/import.yue index 8e82a01..e206d04 100644 --- a/spec/inputs/import.yue +++ b/spec/inputs/import.yue @@ -70,7 +70,7 @@ do do import "m" as {a#: b} - import "m" as {e: f, a#: b} + import "m" as {e: f, a#: c} import "m" as {c: d} import "m" as {g, {h#: i}} diff --git a/spec/inputs/macro.yue b/spec/inputs/macro.yue index 7bbb06d..3c89c1c 100644 --- a/spec/inputs/macro.yue +++ b/spec/inputs/macro.yue @@ -279,7 +279,7 @@ do macro skip = -> "while false do break" -_ = -> +_1 = -> print 1 <- $skip print 2 diff --git a/spec/inputs/metatable.yue b/spec/inputs/metatable.yue index ed68a17..7e9b4f2 100644 --- a/spec/inputs/metatable.yue +++ b/spec/inputs/metatable.yue @@ -23,7 +23,7 @@ do = 123, a.b.c, func! x.abc, a.b.# = 123, {} -func!.# = mt, extra +func!.# = mt --, extra a, b.c.#, d, e = 1, mt, "abc" is_same = a.#.__index == a.index# diff --git a/spec/inputs/syntax.yue b/spec/inputs/syntax.yue index f212bec..d18bd0e 100644 --- a/spec/inputs/syntax.yue +++ b/spec/inputs/syntax.yue @@ -71,10 +71,10 @@ _ = something[======[hey]======] * 2 _ = something[ [======[hey]======] ] * 2 -_ = something'else', 2 -_ = something"else", 2 -_ = something[[else]], 2 -_ = something[ [[else]] ], 2 +_, _ = something'else', 2 +_, _ = something"else", 2 +_, _ = something[[else]], 2 +_, _ = something[ [[else]] ], 2 something 'else', 2 something "else", 2 @@ -157,7 +157,7 @@ y = #"hello" x = #{#{},#{1},#{1,2}} -_ = hello, world +_, _ = hello, world something\hello(what) a,b something\hello what diff --git a/spec/inputs/tables.yue b/spec/inputs/tables.yue index e1b9e1d..0b5af46 100644 --- a/spec/inputs/tables.yue +++ b/spec/inputs/tables.yue @@ -152,12 +152,12 @@ k = { "hello": 'world', "hat": "zat" } please "hello": "world" k = "hello": "world", "one": "zone" -f = "one", "two": three, "four" -f = "two": three, "four" -f = { "one", "two": three, "four" } +f1, f2, f3 = "one", "two": three, "four" +f1, f2 = "two": three, "four" +f1 = { "one", "two": three, "four" } -j = "one", "two": three, "four": five, 6, 7 +j1, j2, j3, j4 = "one", "two": three, "four": five, 6, 7 heroine = name: "Christina" diff --git a/spec/inputs/with.yue b/spec/inputs/with.yue index 6f3e3ba..5da0980 100644 --- a/spec/inputs/with.yue +++ b/spec/inputs/with.yue @@ -45,7 +45,7 @@ do -- do - with a, b -- b is lost + with a -- only one value allowed print .world mod = with _M = {} diff --git a/spec/outputs/destructure.lua b/spec/outputs/destructure.lua index 2fe4ba9..59ee358 100644 --- a/spec/outputs/destructure.lua +++ b/spec/outputs/destructure.lua @@ -26,7 +26,7 @@ do local _obj_0 = yeah a, b, c, d = _obj_0.a, _obj_0.b, _obj_0.c, _obj_0.d end - local _ = two + b = two a = one[1] c = nil b = one[1] @@ -56,7 +56,10 @@ do end do a = tbl - b, c = _.b, _.c + do + local _obj_0 = _ + b, c = _obj_0.b, _obj_0.c + end end do b = _ @@ -386,3 +389,21 @@ do a.c = _tmp_1 end end +do + local width, height + do + local _obj_0 = View.size + width, height = _obj_0.width, _obj_0.height + end + local x, y + do + local _obj_0 = point + x, y = _obj_0.x, _obj_0.y + if x == nil then + x = 0.0 + end + if y == nil then + y = 0.0 + end + end +end diff --git a/spec/outputs/export.lua b/spec/outputs/export.lua index 84e5424..962f18c 100644 --- a/spec/outputs/export.lua +++ b/spec/outputs/export.lua @@ -134,7 +134,10 @@ f((function() local _accum_0 = { } local _len_0 = 1 for k, v in pairs(tb) do - _accum_0[_len_0] = k, v + _accum_0[_len_0] = { + k, + v + } _len_0 = _len_0 + 1 end return _accum_0 @@ -232,7 +235,10 @@ _ = tostring((function() local _accum_0 = { } local _len_0 = 1 for k, v in pairs(tb) do - _accum_0[_len_0] = k, v + _accum_0[_len_0] = { + k, + v + } _len_0 = _len_0 + 1 end return _accum_0 diff --git a/spec/outputs/import.lua b/spec/outputs/import.lua index 65e703f..6979e47 100644 --- a/spec/outputs/import.lua +++ b/spec/outputs/import.lua @@ -94,7 +94,7 @@ do b = getmetatable(require("m")).__a local _obj_0 = require("m") local f = _obj_0.e - b = getmetatable(_obj_0).__a + c = getmetatable(_obj_0).__a local d = require("m").c local g, i do diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua index c19b2d7..fbc1d48 100644 --- a/spec/outputs/macro.lua +++ b/spec/outputs/macro.lua @@ -282,7 +282,8 @@ print("current line: " .. tostring(268)); do print(1) end -_ = function() +local _1 +_1 = function() print(1) local _accum_0 = { } local _len_0 = 1 diff --git a/spec/outputs/metatable.lua b/spec/outputs/metatable.lua index 935202c..2d72e1d 100644 --- a/spec/outputs/metatable.lua +++ b/spec/outputs/metatable.lua @@ -65,7 +65,6 @@ end setmetatable(a.b, { }) x.abc = 123 setmetatable(func(), mt) -local _ = extra setmetatable(b.c, mt) a, d, e = 1, "abc" local is_same = getmetatable(a).__index == getmetatable(a).__index diff --git a/spec/outputs/syntax.lua b/spec/outputs/syntax.lua index 24b78c6..a4cb8e2 100644 --- a/spec/outputs/syntax.lua +++ b/spec/outputs/syntax.lua @@ -56,10 +56,10 @@ something("else") _ = something([[hey]]) * 2 _ = something([======[hey]======]) * 2 _ = something[ [======[hey]======]] * 2 -_ = something('else'), 2 -_ = something("else"), 2 -_ = something([[else]]), 2 -_ = something[ [[else]]], 2 +_, _ = something('else'), 2 +_, _ = something("else"), 2 +_, _ = something([[else]]), 2 +_, _ = something[ [[else]]], 2 something('else', 2) something("else", 2) something([[else]], 2) @@ -165,7 +165,7 @@ x = #{ 2 } } -_ = hello, world +_, _ = hello, world something:hello(what)(a, b) something:hello(what) something.hello:world(a, b) diff --git a/spec/outputs/tables.lua b/spec/outputs/tables.lua index 1d28a43..e9df1c4 100644 --- a/spec/outputs/tables.lua +++ b/spec/outputs/tables.lua @@ -187,18 +187,18 @@ k = { ["hello"] = "world", ["one"] = "zone" } -local f = "one", { +local f1, f2, f3 = "one", { ["two"] = three }, "four" -f = { +f1, f2 = { ["two"] = three }, "four" -f = { +f1 = { "one", ["two"] = three, "four" } -local j = "one", { +local j1, j2, j3, j4 = "one", { ["two"] = three, ["four"] = five }, 6, 7 diff --git a/spec/outputs/with.lua b/spec/outputs/with.lua index 9dcaca3..304d26e 100644 --- a/spec/outputs/with.lua +++ b/spec/outputs/with.lua @@ -54,7 +54,7 @@ do end do do - local _with_0 = a, b + local _with_0 = a print(_with_0.world) end local mod diff --git a/src/yue.cpp b/src/yue.cpp index 3e30c70..cb27db8 100644 --- a/src/yue.cpp +++ b/src/yue.cpp @@ -21,6 +21,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include using namespace std::string_view_literals; +using namespace std::string_literals; #include "ghc/fs_std.hpp" #include "linenoise.hpp" @@ -91,10 +92,10 @@ static const char luaminifyCodes[] = static void pushLuaminify(lua_State* L) { if (luaL_loadbuffer(L, luaminifyCodes, sizeof(luaminifyCodes) / sizeof(luaminifyCodes[0]) - 1, "=(luaminify)") != 0) { - std::string err = std::string("failed to load luaminify module.\n") + lua_tostring(L, -1); + std::string err = "failed to load luaminify module.\n"s + lua_tostring(L, -1); luaL_error(L, err.c_str()); } else if (lua_pcall(L, 0, 1, 0) != 0) { - std::string err = std::string("failed to init luaminify module.\n") + lua_tostring(L, -1); + std::string err = "failed to init luaminify module.\n"s + lua_tostring(L, -1); luaL_error(L, err.c_str()); } } @@ -209,7 +210,7 @@ int main(int narg, const char** args) { DEFER(lua_settop(L, top)); pushYue(L, "loadstring"sv); lua_pushlstring(L, codes.c_str(), codes.size()); - lua_pushstring(L, (std::string("=(repl ") + std::to_string(count) + ')').c_str()); + lua_pushstring(L, ("=(repl "s + std::to_string(count) + ')').c_str()); pushOptions(L, -1); const std::string_view Err = "\033[35m"sv, Val = "\033[33m"sv, Stop = "\033[0m\n"sv; if (lua_pcall(L, 3, 2, 0) != 0) { @@ -218,7 +219,7 @@ int main(int narg, const char** args) { } if (lua_isnil(L, -2) != 0) { std::string err = lua_tostring(L, -1); - auto modName = std::string("(repl "sv) + std::to_string(count) + "):"; + auto modName = "(repl "s + std::to_string(count) + "):"s; if (err.substr(0, modName.size()) == modName) { err = err.substr(modName.size()); } @@ -409,8 +410,8 @@ int main(int narg, const char** args) { std::cout << help; return 1; } - } else if (arg.size() > 1 && arg.substr(0, 1) == "-"sv && arg.substr(1, 1) != "-"sv) { - auto argStr = arg.substr(1); + } else if (arg.size() > 2 && arg.substr(0, 2) == "--"sv && arg.substr(2, 1) != "-"sv) { + auto argStr = arg.substr(2); yue::Utils::trim(argStr); size_t idx = argStr.find('='); if (idx != std::string::npos) { @@ -420,7 +421,7 @@ int main(int narg, const char** args) { yue::Utils::trim(value); config.options[key] = value; } else { - config.options[argStr] = ""; + config.options[argStr] = std::string(); } } else { if (fs::is_directory(arg)) { @@ -459,7 +460,13 @@ int main(int narg, const char** args) { auto conf = config; conf.module = file.first; if (!workPath.empty()) { - conf.options["path"] = (fs::path(workPath) / "?.lua").string(); + auto it = conf.options.find("path"); + if (it != conf.options.end()) { + it->second += ';'; + it->second += (fs::path(workPath) / "?.lua"sv).string(); + } else { + conf.options["path"] = (fs::path(workPath) / "?.lua"sv).string(); + } } if (dumpCompileTime) { auto start = std::chrono::high_resolution_clock::now(); @@ -499,7 +506,7 @@ int main(int narg, const char** args) { } else { std::string targetExtension("lua"sv); if (result.options) { - auto it = result.options->find("target_extension"); + auto it = result.options->find("target_extension"s); if (it != result.options->end()) { targetExtension = it->second; } @@ -519,19 +526,19 @@ int main(int narg, const char** args) { fs::create_directories(targetFile.parent_path()); } if (result.codes.empty()) { - return std::tuple{0, targetFile.string(), std::string("Built "sv) + file.first + '\n'}; + return std::tuple{0, targetFile.string(), "Built "s + file.first + '\n'}; } std::ofstream output(targetFile, std::ios::trunc | std::ios::out); if (output) { const auto& codes = result.codes; if (config.reserveLineNumber) { - auto head = std::string("-- [yue]: "sv) + file.first + '\n'; + auto head = "-- [yue]: "s + file.first + '\n'; output.write(head.c_str(), head.size()); } output.write(codes.c_str(), codes.size()); - return std::tuple{0, targetFile.string(), std::string("Built "sv) + file.first + '\n'}; + return std::tuple{0, targetFile.string(), "Built "s + file.first + '\n'}; } else { - return std::tuple{1, std::string(), std::string("Failed to write file: "sv) + targetFile.string() + '\n'}; + return std::tuple{1, std::string(), "Failed to write file: "s + targetFile.string() + '\n'}; } } } else { @@ -541,7 +548,7 @@ int main(int narg, const char** args) { return std::tuple{1, std::string(), buf.str()}; } } else { - return std::tuple{1, std::string(), std::string("Failed to read file: "sv) + file.first + ".\n"}; + return std::tuple{1, std::string(), "Failed to read file: "s + file.first + ".\n"}; } }); results.push_back(std::move(task)); @@ -588,7 +595,7 @@ int main(int narg, const char** args) { if (lua_pcall(L, 1, 1, 0) != 0) { ret = 2; std::string err = lua_tostring(L, -1); - errs.push_back(std::string("Failed to minify: "sv) + file + '\n' + err + '\n'); + errs.push_back("Failed to minify: "s + file + '\n' + err + '\n'); } else { size_t size = 0; const char* minifiedCodes = lua_tolstring(L, -1, &size); @@ -603,7 +610,7 @@ int main(int narg, const char** args) { } } else { ret = 2; - errs.push_back(std::string("Failed to minify: "sv) + file + '\n'); + errs.push_back("Failed to minify: "s + file + '\n'); } } else { std::cout << msg; diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 05ac5ef..620dfcc 100755 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -118,16 +118,22 @@ AST_NODE(Local) AST_MEMBER(Local, &item) AST_END(Local, "local"sv) -class Assign_t; +AST_LEAF(const_attrib) +AST_END(const_attrib, "const"sv) + +AST_LEAF(close_attrib) +AST_END(close_attrib, "close"sv) -AST_LEAF(Attrib) -AST_END(Attrib, "attrib"sv) +class simple_table_t; +class TableLit_t; +class Assign_t; AST_NODE(LocalAttrib) - ast_ptr attrib; - ast_ptr nameList; + ast_sel attrib; + ast_ptr sep; + ast_sel_list leftList; ast_ptr assign; - AST_MEMBER(LocalAttrib, &attrib, &nameList, &assign) + AST_MEMBER(LocalAttrib, &attrib, &sep, &leftList, &assign) AST_END(LocalAttrib, "local_attrib"sv) AST_NODE(colon_import_name) @@ -135,9 +141,6 @@ AST_NODE(colon_import_name) AST_MEMBER(colon_import_name, &name) AST_END(colon_import_name, "colon_import_name"sv) -class Exp_t; -class TableLit_t; - AST_LEAF(import_literal_inner) AST_END(import_literal_inner, "import_literal_inner"sv) @@ -147,6 +150,8 @@ AST_NODE(ImportLiteral) AST_MEMBER(ImportLiteral, &sep, &inners) AST_END(ImportLiteral, "import_literal"sv) +class Exp_t; + AST_NODE(ImportFrom) ast_ptr sep; ast_sel_list names; @@ -499,7 +504,6 @@ class String_t; class const_value_t; class ClassDecl_t; class unary_value_t; -class TableLit_t; class FunLit_t; AST_NODE(SimpleValue) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 7bbf5e9..0d027a0 100755 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -13,6 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include "yuescript/yue_parser.h" #include "yuescript/yue_compiler.h" @@ -37,9 +38,6 @@ extern "C" { #endif // YUE_NO_MACRO namespace yue { -using namespace std::string_view_literals; -using namespace std::string_literals; -using namespace parserlib; #define BLOCK_START do { #define BLOCK_END } while (false); @@ -56,7 +54,7 @@ using namespace parserlib; typedef std::list str_list; -const std::string_view version = "0.13.6"sv; +const std::string_view version = "0.14.0"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -1266,8 +1264,8 @@ private: std::string getDestrucureDefine(ExpListAssign_t* assignment) { auto info = extractDestructureInfo(assignment, true, false); - if (!info.first.empty()) { - for (const auto& destruct : info.first) { + if (!info.destructures.empty()) { + for (const auto& destruct : info.destructures) { str_list defs; for (const auto& item : destruct.items) { if (!item.targetVar.empty()) { @@ -1319,11 +1317,30 @@ private: return assignment; } + 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 std::logic_error(_info.errorMessage("can only declare variable as const"sv, item.target)); + } + markVarConst(item.targetVar); + } + } + } + void transformAssignment(ExpListAssign_t* assignment, str_list& out, bool optionalDestruct = false) { checkAssignable(assignment->expList); BLOCK_START auto assign = ast_cast(assignment->action); BREAK_IF(!assign); + if (assignment->expList->exprs.size() < assign->values.size()) { + auto num = assignment->expList->exprs.size(); + _buf << "no more than "sv << num << " right value"sv; + if (num > 1) _buf << 's'; + _buf << " required"sv; + throw std::logic_error(_info.errorMessage(clearBuf(), assign->values.front())); + } auto x = assignment; const auto& exprs = assignment->expList->exprs.objects(); const auto& values = assign->values.objects(); @@ -1554,15 +1571,15 @@ private: } BLOCK_END auto info = extractDestructureInfo(assignment, false, optionalDestruct); - if (info.first.empty()) { + if (info.destructures.empty()) { transformAssignmentCommon(assignment, out); } else { str_list temp; - if (info.second) { - transformAssignmentCommon(info.second, temp); + if (info.assignment) { + transformAssignmentCommon(info.assignment, temp); } auto x = assignment; - for (auto& destruct : info.first) { + for (auto& destruct : info.destructures) { std::list, ast_ptr>> leftPairs; bool extraScope = false; if (destruct.items.size() == 1) { @@ -2030,24 +2047,22 @@ private: return pairs; } - std::pair, ast_ptr> - extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly, bool optional) { + struct DestructureInfo { + std::list destructures; + ast_ptr assignment; + }; + + DestructureInfo extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly, bool optional) { auto x = assignment; std::list destructs; if (!assignment->action.is()) return {destructs, nullptr}; auto exprs = assignment->expList->exprs.objects(); auto values = assignment->action.to()->values.objects(); size_t size = std::max(exprs.size(), values.size()); - ast_list cache; - if (exprs.size() < size) { - auto var = toAst("_"sv, x); - cache.push_back(var); - while (exprs.size() < size) exprs.emplace_back(var); - } - ast_ptr nullNode; + ast_ptr nil; if (values.size() < size) { - nullNode = toAst("nil"sv, x); - while (values.size() < size) values.emplace_back(nullNode); + nil = toAst("nil"sv, x); + while (values.size() < size) values.emplace_back(nil); } using iter = node_container::iterator; std::vector> destructPairs; @@ -2059,11 +2074,11 @@ private: auto value = singleValueFrom(expr); ast_node* destructNode = value->getByPath(); if (destructNode || (destructNode = value->item.as())) { - if (*j != nullNode) { + if (*j != nil) { if (auto ssVal = simpleSingleValueFrom(*j)) { switch (ssVal->value->getId()) { case id(): - throw std::logic_error(_info.errorMessage("can not destructure a const value"sv, ssVal->value)); + throw std::logic_error(_info.errorMessage("can not destructure a constant"sv, ssVal->value)); break; case id(): throw std::logic_error(_info.errorMessage("can not destructure a number"sv, ssVal->value)); @@ -2074,7 +2089,7 @@ private: } } } else { - throw std::logic_error(_info.errorMessage("can not destructure a nil value"sv, destructNode)); + throw std::logic_error(_info.errorMessage("an explicit destructure target required"sv, destructNode)); } destructPairs.push_back({i, j}); auto subDestruct = destructNode->new_ptr(); @@ -2173,7 +2188,7 @@ private: } destruct.items = std::move(pairs); if (!varDefOnly) { - if (*j == nullNode) { + if (*j == nil) { for (auto& item : destruct.items) { item.structure.clear(); } @@ -3104,8 +3119,8 @@ private: } } auto info = extractDestructureInfo(assignment, true, false); - if (!info.first.empty()) { - for (const auto& destruct : info.first) + 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); @@ -3114,8 +3129,8 @@ private: } } } - if (info.second) { - auto defs = transformAssignDefs(info.second->expList, DefOp::Get); + if (info.assignment) { + auto defs = transformAssignDefs(info.assignment->expList, DefOp::Get); for (const auto& def : defs) { if (std::isupper(def[0]) && capital) { capital->decls.push_back(def); } else if (any) { @@ -3236,6 +3251,49 @@ private: } } + std::optional getOption(std::string_view key) { +#ifndef YUE_NO_MACRO + if (L) { + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + pushYue("options"sv); // options + lua_pushlstring(L, &key.front(), key.size()); + lua_gettable(L, -2); + if (lua_isstring(L, -1) != 0) { + size_t size = 0; + const char* str = lua_tolstring(L, -1, &size); + return std::string(str, size); + } + } +#endif // YUE_NO_MACRO + auto it = _config.options.find(std::string(key)); + if (it != _config.options.end()) { + return it->second; + } + return std::nullopt; + } + + int getLuaTarget(ast_node* x) { + if (auto target = getOption("target")) { + if (target.value() == "5.1"sv) { + return 501; + } else if (target.value() == "5.2"sv) { + return 502; + } else if (target.value() == "5.3"sv) { + return 503; + } else if (target.value() == "5.4"sv) { + return 504; + } else { + throw std::logic_error(_info.errorMessage("get invalid Lua target \""s + target.value() + "\", should be 5.1, 5.2, 5.3 or 5.4"s, x)); + } + } +#ifndef YUE_NO_MACRO + return LUA_VERSION_NUM; +#else + return 504; +#endif // YUE_NO_MACRO + } + #ifndef YUE_NO_MACRO void passOptions() { if (!_config.options.empty()) { @@ -5898,8 +5956,8 @@ private: auto names = transformAssignDefs(assignment->expList.get(), DefOp::Mark); varDefs.insert(varDefs.end(), names.begin(), names.end()); auto info = extractDestructureInfo(assignment, true, false); - if (!info.first.empty()) { - for (const auto& destruct : info.first) + if (!info.destructures.empty()) { + for (const auto& destruct : info.destructures) for (const auto& item : destruct.items) if (!item.targetVar.empty() && addToScope(item.targetVar)) varDefs.push_back(item.targetVar); @@ -6329,8 +6387,8 @@ private: return traversal::Stop; } auto info = extractDestructureInfo(assignment, true, false); - if (!info.first.empty()) { - for (const auto& destruct : info.first) + if (!info.destructures.empty()) { + for (const auto& destruct : info.destructures) for (const auto& item : destruct.items) if (!item.targetVar.empty() && !isDefined(item.targetVar)) return traversal::Stop; @@ -6481,8 +6539,8 @@ private: transformAssignment(assignment, out); str_list names = transformAssignDefs(expList, DefOp::Get); auto info = extractDestructureInfo(assignment, true, false); - if (!info.first.empty()) { - for (const auto& destruct : info.first) + 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); @@ -6848,6 +6906,7 @@ private: temp.push_back(indent() + "end"s + nlr(import)); } out.push_back(join(temp)); + markDestructureConst(assignment); } std::string moduleNameFrom(ImportLiteral_t* literal) { @@ -7013,6 +7072,12 @@ private: assignment->expList.set(assignList); assignment->action.set(assign); transformAssignment(assignment, out); + if (auto var = ast_cast(target)) { + auto moduleName = _parser.toString(var); + markVarConst(moduleName); + } else { + markDestructureConst(assignment); + } } void transformImport(Import_t* import, str_list& out) { @@ -7191,7 +7256,7 @@ private: auto info = extractDestructureInfo(assignment, true, false); transformAssignment(assignment, temp, true); str_list conds; - for (const auto& destruct : info.first) { + for (const auto& destruct : info.destructures) { for (const auto& item : destruct.items) { if (!item.defVal) { transformExp(item.target, conds, ExpUsage::Closure); @@ -7334,23 +7399,84 @@ private: void transformLocalAttrib(LocalAttrib_t* localAttrib, str_list& out) { auto x = localAttrib; - auto attrib = _parser.toString(localAttrib->attrib); - str_list vars; - for (auto name : localAttrib->nameList->names.objects()) { - auto var = _parser.toString(name); - forceAddToScope(var); - vars.push_back(var); + if (x->leftList.size() < x->assign->values.size()) { + throw std::logic_error(_info.errorMessage("number of right values should not be greater than left values"sv, x->assign->values.front())); + } + auto listA = x->new_ptr(); + auto assignA = x->new_ptr(); + auto listB = x->new_ptr(); + auto assignB = x->new_ptr(); + auto i = x->leftList.objects().begin(); + auto ie = x->leftList.objects().end(); + auto j = x->assign->values.objects().begin(); + auto je = x->assign->values.objects().end(); + while (i != ie) { + if (ast_is(*i)) { + listA->names.push_back(*i); + if (j != je) assignA->values.push_back(*j); + } else { + auto item = *i; + auto value = item->new_ptr(); + switch (item->getId()) { + case id(): + value->item.set(item); + break; + case id(): { + auto simpleValue = item->new_ptr(); + simpleValue->value.set(item); + value->item.set(simpleValue); + break; + } + default: YUEE("AST node mismatch", item); break; + } + auto exp = newExp(value, item); + listB->exprs.push_back(exp); + if (j != je) assignB->values.push_back(*j); + } + ++i; + if (j != je) ++j; } - attrib = " <"s + attrib + '>'; - for (auto& var : vars) { - markVarConst(var); - var.append(attrib); + if (!listA->names.empty()) { + str_list vars; + for (auto name : listA->names.objects()) { + auto var = _parser.toString(name); + forceAddToScope(var); + vars.push_back(var); + } + if (getLuaTarget(x) >= 504) { + std::string attrib; + if (localAttrib->attrib.is()) { + attrib = " "s; + } else if (localAttrib->attrib.is()) { + attrib = " "s; + } else { + YUEE("AST node mismatch", localAttrib->attrib); + } + for (auto& var : vars) { + markVarConst(var); + var.append(attrib); + } + } else { + if (localAttrib->attrib.is()) { + throw std::logic_error(_info.errorMessage("close attribute is not available when not targeting Lua 5.4 or higher version"sv, x)); + } + for (auto& var : vars) { + markVarConst(var); + } + } + str_list temp; + for (auto item : assignA->values.objects()) { + transformAssignItem(item, temp); + } + out.push_back(indent() + "local "s + join(vars, ", "sv) + " = "s + join(temp, ", "sv) + nll(x)); } - str_list temp; - for (auto item : localAttrib->assign->values.objects()) { - transformAssignItem(item, temp); + if (!listB->exprs.empty()) { + auto assignment = x->new_ptr(); + assignment->expList.set(listB); + assignment->action.set(assignB); + transformAssignment(assignment, out); + markDestructureConst(assignment); } - out.push_back(indent() + "local "s + join(vars, ", "sv) + " = "s + join(temp, ", "sv) + nll(x)); } void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { @@ -7372,10 +7498,16 @@ private: } void transformLabel(Label_t* label, str_list& out) { + if (getLuaTarget(label) < 502) { + throw std::logic_error(_info.errorMessage("label statement is not available when not targeting Lua 5.2 or higher version"sv, label)); + } out.push_back(indent() + "::"s + _parser.toString(label->label) + "::"s + nll(label)); } void transformGoto(Goto_t* gotoNode, str_list& out) { + if (getLuaTarget(gotoNode) < 502) { + throw std::logic_error(_info.errorMessage("goto statement is not available when not targeting Lua 5.2 or higher version"sv, gotoNode)); + } out.push_back(indent() + "goto "s + _parser.toString(gotoNode->label) + nll(gotoNode)); } diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 66043d3..94ce550 100755 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -11,25 +11,24 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI namespace pl = parserlib; namespace yue { -using namespace std::string_view_literals; std::unordered_set LuaKeywords = { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", - "if", "in", "local", "nil", "not", - "or", "repeat", "return", "then", "true", - "until", "while" + "and"s, "break"s, "do"s, "else"s, "elseif"s, + "end"s, "false"s, "for"s, "function"s, "goto"s, + "if"s, "in"s, "local"s, "nil"s, "not"s, + "or"s, "repeat"s, "return"s, "then"s, "true"s, + "until"s, "while"s }; std::unordered_set Keywords = { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", - "if", "in", "local", "nil", "not", - "or", "repeat", "return", "then", "true", - "until", "while", // Lua keywords - "as", "class", "continue", "export", "extends", - "from", "global", "import", "macro", "switch", - "try", "unless", "using", "when", "with" // Yue keywords + "and"s, "break"s, "do"s, "else"s, "elseif"s, + "end"s, "false"s, "for"s, "function"s, "goto"s, + "if"s, "in"s, "local"s, "nil"s, "not"s, + "or"s, "repeat"s, "return"s, "then"s, "true"s, + "until"s, "while"s, // Lua keywords + "as"s, "class"s, "continue"s, "export"s, "extends"s, + "from"s, "global"s, "import"s, "macro"s, "switch"s, + "try"s, "unless"s, "using"s, "when"s, "with"s // Yue keywords }; YueParser::YueParser() { @@ -86,7 +85,7 @@ YueParser::YueParser() { if (isValid) { if (st->buffer == st->moduleName) { st->moduleFix++; - st->moduleName = std::string("_module_"sv) + std::to_string(st->moduleFix); + st->moduleName = "_module_"s + std::to_string(st->moduleFix); } } st->buffer.clear(); @@ -179,10 +178,15 @@ YueParser::YueParser() { local_flag = expr('*') | expr('^'); local_values = NameList >> -(sym('=') >> (TableBlock | ExpListLow)); - Attrib = (expr("const") | expr("close")) >> not_(AlphaNum); Local = key("local") >> (Space >> local_flag | local_values); - LocalAttrib = Attrib >> NameList >> Assign; + const_attrib = key("const"); + close_attrib = key("close"); + local_const_item = (Space >> Variable | simple_table | TableLit); + LocalAttrib = ( + const_attrib >> Seperator >> local_const_item >> *(sym(',') >> local_const_item) | + close_attrib >> Seperator >> Space >> Variable >> *(sym(',') >> Space >> Variable) + ) >> Assign; colon_import_name = sym('\\') >> Space >> Variable; ImportName = colon_import_name | Space >> Variable; @@ -754,6 +758,10 @@ std::string ParseInfo::errorMessage(std::string_view msg, const input_range* loc ++it; } auto line = Converter{}.to_bytes(std::wstring(begin, end)); + while (col < static_cast(line.size()) + && (line[col] == ' ' || line[col] == '\t')) { + col++; + } Utils::replace(line, "\t"sv, " "sv); std::ostringstream buf; buf << loc->m_begin.m_line << ": "sv << msg << diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index b71a67c..b363ad7 100755 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -21,6 +21,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include "yuescript/yue_ast.h" namespace yue { +using namespace std::string_view_literals; +using namespace std::string_literals; using namespace parserlib; struct ParseInfo { @@ -77,7 +79,7 @@ protected: int exportCount = 0; int moduleFix = 0; size_t stringOpen = 0; - std::string moduleName = "_module_0"; + std::string moduleName = "_module_0"s; std::string buffer; std::stack indents; std::stack noDoStack; @@ -189,6 +191,7 @@ private: rule expo_value; rule expo_exp; rule exp_not_tab; + rule local_const_item; rule empty_line_stop; rule Line; rule Shebang; @@ -209,8 +212,9 @@ private: AST_RULE(NameList) AST_RULE(local_flag) AST_RULE(local_values) - AST_RULE(Attrib) AST_RULE(Local) + AST_RULE(const_attrib) + AST_RULE(close_attrib) AST_RULE(LocalAttrib); AST_RULE(colon_import_name) AST_RULE(import_literal_inner) diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp index 2e96f16..98e214f 100644 --- a/src/yuescript/yuescript.cpp +++ b/src/yuescript/yuescript.cpp @@ -9,6 +9,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include "yuescript/yue_compiler.h" #include "yuescript/yue_parser.h" +using namespace std::string_literals; + #if defined(YUE_BUILD_AS_DLL) #define YUE_API __declspec(dllexport) #else @@ -25,12 +27,12 @@ static const char yuescriptCodes[] = static void init_yuescript(lua_State* L) { if (luaL_loadbuffer(L, yuescriptCodes, sizeof(yuescriptCodes) / sizeof(yuescriptCodes[0]) - 1, "=(yuescript)") != 0) { - std::string err = std::string("failed to load yuescript module.\n") + lua_tostring(L, -1); + std::string err = "failed to load yuescript module.\n"s + lua_tostring(L, -1); luaL_error(L, err.c_str()); } else { lua_insert(L, -2); if (lua_pcall(L, 1, 0, 0) != 0) { - std::string err = std::string("failed to init yuescript module.\n") + lua_tostring(L, -1); + std::string err = "failed to init yuescript module.\n"s + lua_tostring(L, -1); luaL_error(L, err.c_str()); } } @@ -41,10 +43,10 @@ static const char stpCodes[] = static int init_stacktraceplus(lua_State* L) { if (luaL_loadbuffer(L, stpCodes, sizeof(stpCodes) / sizeof(stpCodes[0]) - 1, "=(stacktraceplus)") != 0) { - std::string err = std::string("failed to load stacktraceplus module.\n") + lua_tostring(L, -1); + std::string err = "failed to load stacktraceplus module.\n"s + lua_tostring(L, -1); luaL_error(L, err.c_str()); } else if (lua_pcall(L, 0, 1, 0) != 0) { - std::string err = std::string("failed to init stacktraceplus module.\n") + lua_tostring(L, -1); + std::string err = "failed to init stacktraceplus module.\n"s + lua_tostring(L, -1); luaL_error(L, err.c_str()); } return 1; -- cgit v1.2.3-55-g6feb