#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: inline bool supportsOneLine() { return uniqueLineSeparator().has_value(); }; 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; } typedef function task; #define make_task_base(start, type, captures, function_body) make_pair(type_index(typeid(type)), [captures](const ParseTree& parsedTree, unsigned int& index) { start; function_body }) #define make_task_base_R(T, C, F) make_task_base(const T& parseComponent = reinterpret_cast(*parsedTree[index]), T, C, F) #define make_task(T, F) make_task_base_R(T, this, F) #define make_task_noR(T, F) make_task_base(,T, this, F) #define make_nonlocal_task(T, F) make_task_base_R(T, , F) typedef unordered_map unordered_task_map; typedef pair print_functions_pair; virtual unordered_task_map getTaskMap() = 0; virtual print_functions_pair printFunctions() = 0; IS(ParseTree) void transpileTree(const T& parseTree, const unsigned short& indentationLevel = 0, const function&& postInsertionFunction = [](auto&){}) { if (not parseTree.empty()) { const auto added_size = indentationLevel * strlen(indentation); if (newLines) { separator.reserve(separator.size() + added_size); for (unsigned short level = 0; level < indentationLevel; ++level) { separator.append(indentation); } } unsigned int subIndex = 0; for (auto pointer_iterator = parseTree.cbegin(); pointer_iterator < parseTree.cend(); ++pointer_iterator) { const type_info& id = pointer_iterator->get()->getId(); try { getTaskMapInstance().at(id)(parseTree, subIndex); ++subIndex; } 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"); } postInsertionFunction(pointer_iterator); } if (newLines) separator.erase(separator.size() - added_size); } } IS(ParseTree) void separate_transpileTree(const T& parseTree, const unsigned short& indentationLevel = 0) { transpileTree(parseTree, indentationLevel, [this, &parseTree](const auto& iterator){ if ((newLines || iterator + 1 == parseTree.cend()) && use_uniqueLineSeparator()) output << uniqueLineSeparator().value(); if (iterator + 1 != parseTree.cend()) output << separator; }); } IS(ParseTree) void separate_transpileTree(const T& parseTree, const string_view separation_characters) { transpileTree(parseTree, 0, [this, &parseTree, &separation_characters](const auto& iterator){ if (iterator + 1 != parseTree.cend()) { output << separation_characters; } }); } typedef optional optional_string; virtual optional_string uniqueLineSeparator() { return ";"; }; virtual bool use_uniqueLineSeparator() { return false; }; const bool newLines; string separator; static constexpr const char* indentation = " "; public: unordered_task_map getTaskMapInstance() { unordered_task_map fullMap = getTaskMap(); // Default / Shared tasks: fullMap.merge(unordered_task_map({ make_task(ParseTree, transpileTree(parseComponent);), make_task(StandardComponents::Reference, output << parseComponent.name;), make_task(StandardComponents::Call, const auto print_functions = printFunctions(); using enum ReservedIdentifier; output << ((parseComponent.name == ID(PRINT_FUNCTION)) ? print_functions.first : (parseComponent.name == ID(PRINT_LINE_FUNCTION)) ? print_functions.second : parseComponent.name) << '('; separate_transpileTree(parseComponent, ", "); output << ')'; ), make_task(StandardComponents::types::Integer, output << setprecision(parseComponent.precision) << fixed << parseComponent.value;) })); return fullMap; }; static unique_ptr forName(string_view name, bool newLines); string transpileWithTree(const ParseTree& tree) { separator = newLines ? "\n" : (supportsOneLine() ? uniqueLineSeparator().value() : throw Yerbacon::Exception("--newlines=off is not supported by the current target")); output.str(string()); separate_transpileTree(tree); return output.str() + '\n'; }; explicit Target(const bool& newLines): output(), newLines(newLines), separator() {}; Target() = delete; virtual ~Target() = default; }; #include "implementations/Lua.hpp" #include "implementations/Js.hpp" #include "implementations/Py.hpp" #include "implementations/GodotScript.hpp" unique_ptr Target::forName(string_view name, const bool newLines = true) { #define ADDTARGET(X, target_class) if (name == X) return unique_ptr(new target_class(newLines)) ADDTARGET("lua", LuaTarget); ADDTARGET("js", JsTarget); ADDTARGET("py", PyTarget); ADDTARGET("gd", GsTarget); #undef ADDTARGET #undef make_nonlocal_task #undef make_task_noR #undef make_task #undef make_task_base_R #undef make_task_base Yerbacon::fail({"\"", string(1, (char) toupper(name.at(1))).data(), name.substr(2).data(), "\" is not a valid target."}); return nullptr; } #endif //YERBACON_TARGET_HPP