#ifndef YERBACON_TARGET_HPP #define YERBACON_TARGET_HPP #include #include #include #include #include #include #include "../parsing/ParseComponents.hpp" #define INCLUDECOMPONENT(X) virtual void on(const X& parseComponent) {} class Target { constexpr static const char* const interpolationString = "${"; constexpr static const char* const interpolationCloseString = "}"; protected: virtual constexpr bool supportsOneLine() { return true; }; std::stringstream output; INCLUDECOMPONENT(StandardComponents::Define); INCLUDECOMPONENT(StandardComponents::types::String); 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 = "+") { vector interpolationVector; unsigned long 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)); } } unsigned long 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()) { unsigned long 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'; } INCLUDECOMPONENT(StandardComponents::Reference); INCLUDECOMPONENT(StandardComponents::Class); public: static shared_ptr forName(string_view name, bool newLines); #define with(X) if (id == typeid(X)) { this->on(reinterpret_cast(*component)); continue; } string transpileWithTree(const ParseTree& tree) { output.str(string()); for (const unique_ptr& component: tree) { const type_info& id = component->getId(); with(StandardComponents::Define); with(StandardComponents::types::String); with(StandardComponents::Reference); with(StandardComponents::Class); } return output.str(); }; #undef with const bool newLines; explicit Target(const bool newLines): newLines(newLines) {}; }; #undef INCLUDECOMPONENT inline string transpile(const ParseTree& tree, const string_view& language, const bool newLines) { return Target::forName(language, newLines)->transpileWithTree(tree); } #include "implementations/Lua.hpp" #include "implementations/Js.hpp" #include "implementations/Py.hpp" enum LANGUAGE: signed short { NONE = -1, LUA, JS, PY }; constinit 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: { if (not name.empty()) { cout << '"' << (char) toupper(name.at(1)); name.remove_prefix(2); cout << name << "\" is not a valid target." << endl; } else cout << "No target was provided." << endl; exit(0); } } #undef ADDTARGET if (not newLines and not target->supportsOneLine()) { cout << "--newlinesoff cannot be used with --target=" << name.substr(1) << endl; exit(0); } return target; }; #endif //YERBACON_TARGET_HPP