It only worked properly with the "--parallel" flag Signed-off-by: Username404 <w.iron.zombie@gmail.com>
177 lines
8.8 KiB
C++
177 lines
8.8 KiB
C++
#ifndef YERBACON_TARGET_HPP
|
|
#define YERBACON_TARGET_HPP
|
|
|
|
#include <vector>
|
|
#include <string_view>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <cstring>
|
|
#include <unordered_map>
|
|
#include <typeindex>
|
|
#include <limits>
|
|
#ifdef __GNUC__
|
|
#include <cxxabi.h>
|
|
#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<strSize> 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<void (const ParseTree& parsedTree, unsigned int& index)> 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<T&>(*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<type_index, task> unordered_task_map;
|
|
typedef pair<const char*, const char*> 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<void(decltype(parseTree.cbegin())&)>&& 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<const char*> 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<Target> 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> Target::forName(string_view name, const bool newLines = true) {
|
|
#define ADDTARGET(X, target_class) if (name == X) return unique_ptr<target_class>(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
|