#ifndef YERBACON_TARGET_HPP #define YERBACON_TARGET_HPP #include #include #include #include #include #include #include #include #include #ifdef __GNUC__ #include #endif #include "../parsing/ParseComponents.hpp" class Target { constexpr static const char* const interpolationString = "${"; constexpr static const char* const interpolationCloseString = "}"; protected: virtual constexpr bool supportsOneLine() { return true; }; std::stringstream output; inline void stringInterpolation(const char* multiline, const string& view) { stringInterpolation(view, multiline, multiline); } void stringInterpolation(string view, const char* openMultiline = "", const char* closeMultiline = "", const char* concatenationCharacters = "+") { typedef string::size_type strSize; vector interpolationVector; strSize occurrence_position = view.find(interpolationString); if (view.find(interpolationCloseString) != string::npos) { while (occurrence_position != string::npos) { interpolationVector.push_back(occurrence_position); occurrence_position = view.find(interpolationString, occurrence_position + strlen(interpolationString)); } } strSize newLine = view.find('\n'); const bool multiline = newLine != string::npos && not !strcmp(openMultiline, "") && not !strcmp(closeMultiline, openMultiline); if (not multiline) { while (newLine != string::npos) { view.erase(newLine, 1); view.insert(newLine, "\\n"); newLine = view.find('\n', newLine); } } const char* const openCharacters = multiline ? openMultiline : "\""; decltype(openCharacters) closeCharacters = multiline ? closeMultiline : openCharacters; if (not interpolationVector.empty()) { strSize closingBrace = 0; for (unsigned long i = 0; i < interpolationVector.size(); ++i) { const auto& occurrence = interpolationVector[i]; const bool hasNext = (i + 1) < interpolationVector.size(); if (i > 0) output << concatenationCharacters; output << openCharacters << view.substr(closingBrace + (i > 0), (occurrence - closingBrace) - (i > 0)); closingBrace = view.find_first_of(interpolationCloseString, occurrence); const bool closingBraceIsNPOS = closingBrace == string::npos; const bool wrongClosingBrace = !closingBraceIsNPOS && ((hasNext && closingBrace > interpolationVector[i + 1])); if (closingBraceIsNPOS || wrongClosingBrace) { output << view.substr(occurrence, (hasNext ? interpolationVector[i + 1] - occurrence : string::npos)); } output << closeCharacters; if (not closingBraceIsNPOS && not wrongClosingBrace) { output << concatenationCharacters; output << view.substr(occurrence + strlen(interpolationString), (closingBrace - occurrence) - strlen(interpolationString)); } } } else output << openCharacters << view << closeCharacters << '\n'; } typedef function task; #define make_task(X, L) make_pair(type_index(typeid(X)), [this](const ParseTree& parsedTree, unsigned int& index) { const X& parseComponent = pointerAs(parsedTree[index]); L }) typedef unordered_map> unordered_task_map; virtual unordered_task_map getTaskMap() = 0; public: unordered_task_map& getTaskMapInstance() { static unordered_task_map staticMap = getTaskMap(); return staticMap; }; static shared_ptr forName(string_view name, bool newLines); const bool newLines; string transpileWithTree(const ParseTree& tree) { const unordered_task_map& taskMap = getTaskMapInstance(); if (not newLines && !supportsOneLine()) { throw Yerbacon::Exception("--newlines=off is not supported by the current target"); } output.str(string()); for (unsigned int i = 0; i < tree.getCompCount(); ++i) { const unique_ptr& component = tree[i]; const type_info& id = component->getId(); try { const optional& currentTask = taskMap.at(id); if (currentTask.has_value()) currentTask.value()(tree, i); } catch (const out_of_range&) { throw Yerbacon::Exception(string( #ifndef __GNUC__ id.name() #else abi::__cxa_demangle(id.name(), nullptr, nullptr, nullptr) #endif ) += " is not supported by the current target"); } } return output.str(); }; explicit Target(const bool newLines): newLines(newLines) {}; virtual ~Target() = default; }; #include "implementations/Lua.hpp" #include "implementations/Js.hpp" #include "implementations/Py.hpp" enum LANGUAGE: signed short { NONE = -1, LUA, JS, PY }; constinit const array languages { ".lua", ".js", ".py" }; shared_ptr Target::forName(string_view name, const bool newLines = true) { LANGUAGE selected = NONE; for (unsigned int i = 0; i < languages.size(); ++i) { if (name == languages[i]) { selected = static_cast(i); break; } } shared_ptr target; #define ADDTARGET(X) target = shared_ptr(new X(newLines)) switch (selected) { #ifdef LUA_HPP case LUA: ADDTARGET(LUA_HPP); break; #endif #ifdef JS_HPP case JS: ADDTARGET(JS_HPP); break; #endif #ifdef PY_HPP case PY: ADDTARGET(PY_HPP); break; #endif case NONE: default: Yerbacon::exit({"\"", string(1, (char) toupper(name.at(1))).data(), name.substr(2).data(), "\" is not a valid target."}); } #undef ADDTARGET #undef make_task return target; }; #endif //YERBACON_TARGET_HPP