/* * common.cpp --- Common utilities. * * (c) Seq project. All rights reserved. * This file is subject to the terms and conditions defined in * file 'LICENSE', which is part of this source code package. */ #include #include #include #include "parser/common.h" #include "util/fmt/format.h" namespace seq { namespace ast { /// String and collection utilities vector split(const string &s, char delim) { vector items; string item; std::istringstream iss(s); while (std::getline(iss, item, delim)) items.push_back(item); return items; } // clang-format off string escape(const string &str) { string r; r.reserve(str.size()); for (unsigned char c : str) { switch (c) { case '\a': r += "\\a"; break; case '\b': r += "\\b"; break; case '\f': r += "\\f"; break; case '\n': r += "\\n"; break; case '\r': r += "\\r"; break; case '\t': r += "\\t"; break; case '\v': r += "\\v"; break; case '\'': r += "\\'"; break; case '\\': r += "\\\\"; break; default: if (c < 32 || c >= 127) r += fmt::format("\\x{:x}", c); else r += c; } } return r; } string unescape(const string &str) { string r; r.reserve(str.size()); for (int i = 0; i < str.size(); i++) { if (str[i] == '\\' && i + 1 < str.size()) switch(str[i + 1]) { case 'a': r += '\a'; i++; break; case 'b': r += '\b'; i++; break; case 'f': r += '\f'; i++; break; case 'n': r += '\n'; i++; break; case 'r': r += '\r'; i++; break; case 't': r += '\t'; i++; break; case 'v': r += '\v'; i++; break; case '"': r += '\"'; i++; break; case '\'': r += '\''; i++; break; case '\\': r += '\\'; i++; break; case 'x': { if (i + 3 > str.size()) throw std::invalid_argument("invalid \\x code"); size_t pos = 0; auto code = std::stoi(str.substr(i + 2, 2), &pos, 16); r += char(code); i += pos + 1; break; } default: if (str[i + 1] >= '0' && str[i + 1] <= '7') { size_t pos = 0; auto code = std::stoi(str.substr(i + 1, 3), &pos, 8); r += char(code); i += pos; } else { r += str[i]; } } else r += str[i]; } return r; } // clang-format on string escapeFStringBraces(const string &str, int start, int len) { string t; t.reserve(len); for (int i = start; i < start + len; i++) if (str[i] == '{') t += "{{"; else if (str[i] == '}') t += "}}"; else t += str[i]; return t; } bool startswith(const string &str, const string &prefix) { return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; } bool endswith(const string &str, const string &suffix) { return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix; } void ltrim(string &str) { str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) { return !std::isspace(ch); })); } void rtrim(string &str) { /// https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) { return !std::isspace(ch); }) .base(), str.end()); } int trimStars(string &str) { int stars = 0; for (; stars < str.size() && str[stars] == '*'; stars++) ; str = str.substr(stars); return stars; } bool isdigit(const string &str) { return std::all_of(str.begin(), str.end(), ::isdigit); } /// AST utilities void error(const char *format) { throw exc::ParserException(format); } void error(const ::seq::SrcInfo &info, const char *format) { throw exc::ParserException(format, info); } /// Path utilities #ifdef __APPLE__ #include string executable_path(const char *argv0) { typedef vector char_vector; char_vector buf(1024, 0); auto size = static_cast(buf.size()); bool havePath = false; bool shouldContinue = true; do { int result = _NSGetExecutablePath(&buf[0], &size); if (result == -1) { buf.resize(size + 1); std::fill(std::begin(buf), std::end(buf), 0); } else { shouldContinue = false; if (buf.at(0) != 0) { havePath = true; } } } while (shouldContinue); if (!havePath) { return string(argv0); } return string(&buf[0], size); } #elif __linux__ #include string executable_path(const char *argv0) { typedef vector char_vector; typedef vector::size_type size_type; char_vector buf(1024, 0); size_type size = buf.size(); bool havePath = false; bool shouldContinue = true; do { ssize_t result = readlink("/proc/self/exe", &buf[0], size); if (result < 0) { shouldContinue = false; } else if (static_cast(result) < size) { havePath = true; shouldContinue = false; size = result; } else { size *= 2; buf.resize(size); std::fill(std::begin(buf), std::end(buf), 0); } } while (shouldContinue); if (!havePath) { return string(argv0); } return string(&buf[0], size); } #else string executable_path(const char *argv0) { return string(argv0); } #endif shared_ptr getImportFile(const string &argv0, const string &what, const string &relativeTo, bool forceStdlib, const string &module0) { using fmt::format; auto getStdLibPaths = [](const string &argv0) { vector paths; char abs[PATH_MAX + 1]; if (auto c = getenv("SEQ_PATH")) { if (realpath(c, abs)) paths.push_back(abs); } if (!argv0.empty()) for (auto loci : {"../lib/seq/stdlib", "../stdlib", "stdlib"}) { strncpy(abs, executable_path(argv0.c_str()).c_str(), PATH_MAX); if (realpath(format("{}/{}", dirname(abs), loci).c_str(), abs)) paths.push_back(abs); } return paths; }; char abs[PATH_MAX + 1]; strncpy(abs, module0.c_str(), PATH_MAX); auto module0Root = string(dirname(abs)); auto getRoot = [&](const string &s) { bool isStdLib = false; string root; for (auto &p : getStdLibPaths(argv0)) if (startswith(s, p)) { root = p; isStdLib = true; break; } if (!isStdLib && startswith(s, module0Root)) root = module0Root; seqassert(startswith(s, root) && endswith(s, ".seq"), "bad path substitution: {}, {}", s, root); auto module = s.substr(root.size() + 1, s.size() - root.size() - 5); std::replace(module.begin(), module.end(), '/', '.'); return ImportFile{(!isStdLib && root == module0Root) ? ImportFile::PACKAGE : ImportFile::STDLIB, s, module}; }; vector paths; if (!forceStdlib) { realpath(relativeTo.c_str(), abs); auto parent = dirname(abs); paths.push_back(format("{}/{}.seq", parent, what)); paths.push_back(format("{}/{}/__init__.seq", parent, what)); } for (auto &p : getStdLibPaths(argv0)) { paths.push_back(format("{}/{}.seq", p, what)); paths.push_back(format("{}/{}/__init__.seq", p, what)); } for (auto &p : paths) { if (!realpath(p.c_str(), abs)) continue; auto path = string(abs); struct stat buffer; if (!stat(path.c_str(), &buffer)) return std::make_shared(getRoot(path)); } return nullptr; } } // namespace ast } // namespace seq