diff options
| author | Li Jin <dragon-fly@qq.com> | 2022-11-15 17:23:46 +0800 |
|---|---|---|
| committer | Li Jin <dragon-fly@qq.com> | 2022-11-15 17:52:09 +0800 |
| commit | 94f8330613877b3582d32bd11abd83a97b4399ad (patch) | |
| tree | 5359de314be1ebde17f8d1e48632a97d18f9e50f /src/yue.cpp | |
| parent | 60f8f00a022ac08701792b2897b72d8c99b50f52 (diff) | |
| download | yuescript-94f8330613877b3582d32bd11abd83a97b4399ad.tar.gz yuescript-94f8330613877b3582d32bd11abd83a97b4399ad.tar.bz2 yuescript-94f8330613877b3582d32bd11abd83a97b4399ad.zip | |
adding -w option to Yuescript tool.
Diffstat (limited to 'src/yue.cpp')
| -rw-r--r-- | src/yue.cpp | 164 |
1 files changed, 160 insertions, 4 deletions
diff --git a/src/yue.cpp b/src/yue.cpp index 5b4dccc..9ae4c3e 100644 --- a/src/yue.cpp +++ b/src/yue.cpp | |||
| @@ -19,10 +19,14 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | |||
| 19 | #include <sstream> | 19 | #include <sstream> |
| 20 | #include <string_view> | 20 | #include <string_view> |
| 21 | #include <tuple> | 21 | #include <tuple> |
| 22 | #include <chrono> | ||
| 23 | #include <thread> | ||
| 22 | using namespace std::string_view_literals; | 24 | using namespace std::string_view_literals; |
| 23 | using namespace std::string_literals; | 25 | using namespace std::string_literals; |
| 26 | using namespace std::chrono_literals; | ||
| 24 | #include "ghc/fs_std.hpp" | 27 | #include "ghc/fs_std.hpp" |
| 25 | #include "linenoise.hpp" | 28 | #include "linenoise.hpp" |
| 29 | #include "efsw/efsw.hpp" | ||
| 26 | 30 | ||
| 27 | #if not(defined YUE_NO_MACRO && defined YUE_COMPILER_ONLY) | 31 | #if not(defined YUE_NO_MACRO && defined YUE_COMPILER_ONLY) |
| 28 | #define _DEFER(code, line) std::shared_ptr<void> _defer_##line(nullptr, [&](auto) { code; }) | 32 | #define _DEFER(code, line) std::shared_ptr<void> _defer_##line(nullptr, [&](auto) { code; }) |
| @@ -88,8 +92,8 @@ void pushOptions(lua_State* L, int lineOffset) { | |||
| 88 | #ifndef YUE_COMPILER_ONLY | 92 | #ifndef YUE_COMPILER_ONLY |
| 89 | static const char luaminifyCodes[] = | 93 | static const char luaminifyCodes[] = |
| 90 | #include "LuaMinify.h" | 94 | #include "LuaMinify.h" |
| 91 | 95 | // | |
| 92 | static void pushLuaminify(lua_State * L) { | 96 | static void pushLuaminify(lua_State * L) { |
| 93 | if (luaL_loadbuffer(L, luaminifyCodes, sizeof(luaminifyCodes) / sizeof(luaminifyCodes[0]) - 1, "=(luaminify)") != 0) { | 97 | if (luaL_loadbuffer(L, luaminifyCodes, sizeof(luaminifyCodes) / sizeof(luaminifyCodes[0]) - 1, "=(luaminify)") != 0) { |
| 94 | std::string err = "failed to load luaminify module.\n"s + lua_tostring(L, -1); | 98 | std::string err = "failed to load luaminify module.\n"s + lua_tostring(L, -1); |
| 95 | luaL_error(L, err.c_str()); | 99 | luaL_error(L, err.c_str()); |
| @@ -100,6 +104,126 @@ static const char luaminifyCodes[] = | |||
| 100 | } | 104 | } |
| 101 | #endif // YUE_COMPILER_ONLY | 105 | #endif // YUE_COMPILER_ONLY |
| 102 | 106 | ||
| 107 | fs::path getTargetFile(const fs::path& srcFile) { | ||
| 108 | auto ext = srcFile.extension().string(); | ||
| 109 | for (auto& ch : ext) ch = std::tolower(ch); | ||
| 110 | if (!ext.empty() && ext.substr(1) == yue::extension) { | ||
| 111 | auto targetFile = srcFile; | ||
| 112 | targetFile.replace_extension("lua"s); | ||
| 113 | if (fs::exists(targetFile)) { | ||
| 114 | return targetFile; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | return fs::path(); | ||
| 118 | } | ||
| 119 | |||
| 120 | fs::path getTargetFileDirty(const fs::path& srcFile) { | ||
| 121 | if (!fs::exists(srcFile)) return fs::path(); | ||
| 122 | auto ext = srcFile.extension().string(); | ||
| 123 | for (auto& ch : ext) ch = std::tolower(ch); | ||
| 124 | if (!fs::is_directory(srcFile) && !ext.empty() && ext.substr(1) == yue::extension) { | ||
| 125 | auto targetFile = srcFile; | ||
| 126 | targetFile.replace_extension("lua"s); | ||
| 127 | if (fs::exists(targetFile)) { | ||
| 128 | auto time = fs::last_write_time(targetFile); | ||
| 129 | auto targetTime = fs::last_write_time(srcFile); | ||
| 130 | if (time < targetTime) { | ||
| 131 | return targetFile; | ||
| 132 | } | ||
| 133 | } else { | ||
| 134 | return targetFile; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | return fs::path(); | ||
| 138 | } | ||
| 139 | |||
| 140 | static std::string compileFile(const fs::path& srcFile, yue::YueConfig conf, const std::string& workPath) { | ||
| 141 | auto targetFile = getTargetFileDirty(srcFile); | ||
| 142 | if (targetFile.empty()) return std::string(); | ||
| 143 | std::ifstream input(srcFile, std::ios::in); | ||
| 144 | if (input) { | ||
| 145 | std::string s( | ||
| 146 | (std::istreambuf_iterator<char>(input)), | ||
| 147 | std::istreambuf_iterator<char>()); | ||
| 148 | auto modulePath = srcFile.lexically_relative(workPath); | ||
| 149 | conf.module = modulePath.string(); | ||
| 150 | if (!workPath.empty()) { | ||
| 151 | auto it = conf.options.find("path"); | ||
| 152 | if (it != conf.options.end()) { | ||
| 153 | it->second += ';'; | ||
| 154 | it->second += (fs::path(workPath) / "?.lua"sv).string(); | ||
| 155 | } else { | ||
| 156 | conf.options["path"] = (fs::path(workPath) / "?.lua"sv).string(); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | auto result = yue::YueCompiler{YUE_ARGS}.compile(s, conf); | ||
| 160 | if (result.error.empty()) { | ||
| 161 | std::string targetExtension("lua"sv); | ||
| 162 | if (result.options) { | ||
| 163 | auto it = result.options->find("target_extension"s); | ||
| 164 | if (it != result.options->end()) { | ||
| 165 | targetExtension = it->second; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | if (targetFile.has_parent_path()) { | ||
| 169 | fs::create_directories(targetFile.parent_path()); | ||
| 170 | } | ||
| 171 | if (result.codes.empty()) { | ||
| 172 | return "Built "s + modulePath.string() + '\n'; | ||
| 173 | } | ||
| 174 | std::ofstream output(targetFile, std::ios::trunc | std::ios::out); | ||
| 175 | if (output) { | ||
| 176 | const auto& codes = result.codes; | ||
| 177 | if (conf.reserveLineNumber) { | ||
| 178 | auto head = "-- [yue]: "s + modulePath.string() + '\n'; | ||
| 179 | output.write(head.c_str(), head.size()); | ||
| 180 | } | ||
| 181 | output.write(codes.c_str(), codes.size()); | ||
| 182 | return "Built "s + modulePath.string() + '\n'; | ||
| 183 | } else { | ||
| 184 | return "Failed to write file: "s + targetFile.string() + '\n'; | ||
| 185 | } | ||
| 186 | } else { | ||
| 187 | return "Failed to compile: "s + modulePath.string() + '\n' + result.error + '\n'; | ||
| 188 | } | ||
| 189 | } else { | ||
| 190 | return "Failed to read file: "s + srcFile.string() + '\n'; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | class UpdateListener : public efsw::FileWatchListener { | ||
| 195 | public: | ||
| 196 | void handleFileAction(efsw::WatchID, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename) override { | ||
| 197 | switch(action) { | ||
| 198 | case efsw::Actions::Add: | ||
| 199 | if (auto res = compileFile(fs::path(dir) / filename, config, workPath); !res.empty()) { | ||
| 200 | std::cout << res; | ||
| 201 | } | ||
| 202 | break; | ||
| 203 | case efsw::Actions::Delete: { | ||
| 204 | auto srcFile = fs::path(dir) / filename; | ||
| 205 | auto targetFile = getTargetFile(srcFile); | ||
| 206 | if (!targetFile.empty()) { | ||
| 207 | fs::remove(targetFile); | ||
| 208 | std::cout << "Deleted " << targetFile.lexically_relative(workPath).string() << '\n'; | ||
| 209 | } | ||
| 210 | break; | ||
| 211 | } | ||
| 212 | case efsw::Actions::Modified: | ||
| 213 | if (auto res = compileFile(fs::path(dir) / filename, config, workPath); !res.empty()) { | ||
| 214 | std::cout << res; | ||
| 215 | } | ||
| 216 | break; | ||
| 217 | case efsw::Actions::Moved: | ||
| 218 | break; | ||
| 219 | default: | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | yue::YueConfig config; | ||
| 224 | std::string workPath; | ||
| 225 | }; | ||
| 226 | |||
| 103 | int main(int narg, const char** args) { | 227 | int main(int narg, const char** args) { |
| 104 | const char* help = | 228 | const char* help = |
| 105 | "Usage: yue [options|files|directories] ...\n\n" | 229 | "Usage: yue [options|files|directories] ...\n\n" |
| @@ -275,6 +399,7 @@ int main(int narg, const char** args) { | |||
| 275 | bool writeToFile = true; | 399 | bool writeToFile = true; |
| 276 | bool dumpCompileTime = false; | 400 | bool dumpCompileTime = false; |
| 277 | bool lintGlobal = false; | 401 | bool lintGlobal = false; |
| 402 | bool watchFiles = false; | ||
| 278 | std::string targetPath; | 403 | std::string targetPath; |
| 279 | std::string resultFile; | 404 | std::string resultFile; |
| 280 | std::string workPath; | 405 | std::string workPath; |
| @@ -412,6 +537,8 @@ int main(int narg, const char** args) { | |||
| 412 | std::cout << help; | 537 | std::cout << help; |
| 413 | return 1; | 538 | return 1; |
| 414 | } | 539 | } |
| 540 | } else if (arg == "-w"sv) { | ||
| 541 | watchFiles = true; | ||
| 415 | } else if (arg.size() > 2 && arg.substr(0, 2) == "--"sv && arg.substr(2, 1) != "-"sv) { | 542 | } else if (arg.size() > 2 && arg.substr(0, 2) == "--"sv && arg.substr(2, 1) != "-"sv) { |
| 416 | auto argStr = arg.substr(2); | 543 | auto argStr = arg.substr(2); |
| 417 | yue::Utils::trim(argStr); | 544 | yue::Utils::trim(argStr); |
| @@ -437,13 +564,16 @@ int main(int narg, const char** args) { | |||
| 437 | } | 564 | } |
| 438 | } | 565 | } |
| 439 | } | 566 | } |
| 567 | } else if (watchFiles) { | ||
| 568 | std::cout << "Error: -w can not be used with file\n"sv; | ||
| 569 | return 1; | ||
| 440 | } else { | 570 | } else { |
| 441 | workPath = fs::path(arg).parent_path().string(); | 571 | workPath = fs::path(arg).parent_path().string(); |
| 442 | files.emplace_back(arg, arg); | 572 | files.emplace_back(arg, arg); |
| 443 | } | 573 | } |
| 444 | } | 574 | } |
| 445 | } | 575 | } |
| 446 | if (files.empty()) { | 576 | if (!watchFiles && files.empty()) { |
| 447 | std::cout << help; | 577 | std::cout << help; |
| 448 | return 0; | 578 | return 0; |
| 449 | } | 579 | } |
| @@ -451,6 +581,32 @@ int main(int narg, const char** args) { | |||
| 451 | std::cout << "Error: -o can not be used with multiple input files\n"sv; | 581 | std::cout << "Error: -o can not be used with multiple input files\n"sv; |
| 452 | std::cout << help; | 582 | std::cout << help; |
| 453 | } | 583 | } |
| 584 | if (watchFiles) { | ||
| 585 | auto fullWorkPath = fs::absolute(fs::path(workPath)).string(); | ||
| 586 | std::list<std::future<std::string>> results; | ||
| 587 | for (const auto& file : files) { | ||
| 588 | auto task = std::async(std::launch::async, [=]() { | ||
| 589 | return compileFile(fs::absolute(file.first), config, fullWorkPath); | ||
| 590 | }); | ||
| 591 | results.push_back(std::move(task)); | ||
| 592 | } | ||
| 593 | for (auto& result : results) { | ||
| 594 | std::string msg = result.get(); | ||
| 595 | if (!msg.empty()) { | ||
| 596 | std::cout << msg; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | efsw::FileWatcher fileWatcher{}; | ||
| 600 | UpdateListener listener{}; | ||
| 601 | listener.config = config; | ||
| 602 | listener.workPath = fullWorkPath; | ||
| 603 | fileWatcher.addWatch(workPath, &listener, true); | ||
| 604 | fileWatcher.watch(); | ||
| 605 | while (true) { | ||
| 606 | std::this_thread::sleep_for(10000ms); | ||
| 607 | } | ||
| 608 | return 0; | ||
| 609 | } | ||
| 454 | std::list<std::future<std::tuple<int, std::string, std::string>>> results; | 610 | std::list<std::future<std::tuple<int, std::string, std::string>>> results; |
| 455 | for (const auto& file : files) { | 611 | for (const auto& file : files) { |
| 456 | auto task = std::async(std::launch::async, [=]() { | 612 | auto task = std::async(std::launch::async, [=]() { |
| @@ -518,7 +674,7 @@ int main(int narg, const char** args) { | |||
| 518 | } | 674 | } |
| 519 | targetFile.replace_extension('.' + targetExtension); | 675 | targetFile.replace_extension('.' + targetExtension); |
| 520 | } | 676 | } |
| 521 | if (!targetPath.empty()) { | 677 | if (targetFile.has_parent_path()) { |
| 522 | fs::create_directories(targetFile.parent_path()); | 678 | fs::create_directories(targetFile.parent_path()); |
| 523 | } | 679 | } |
| 524 | if (result.codes.empty()) { | 680 | if (result.codes.empty()) { |
