Yerbacon/src/headers/transpiler/Target.hpp
2022-02-14 14:12:00 +01:00

171 lines
7.6 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>
#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:
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<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 = pointerAs<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;
public:
const unordered_task_map& getTaskMapInstance() {
static unordered_task_map staticMap = getTaskMap();
// Default / Shared tasks:
staticMap.merge(unordered_task_map({
make_task(ParseTree,
unsigned int subIndex = 0;
for (auto pointer_iterator = parseComponent.cbegin(); pointer_iterator < parseComponent.cend(); ++pointer_iterator) {
staticMap[pointer_iterator->get()->getId()](parseComponent, subIndex); ++subIndex;
if ((pointer_iterator + 1) != parseComponent.cend() && parseComponent.getId() == typeid(StandardComponents::Call)) {
output << ", ";
}
}
),
make_task(StandardComponents::Reference,
const auto print_functions = printFunctions();
output << ((parseComponent.name == "print") ? print_functions.first : (parseComponent.name == "print_line") ? print_functions.second : parseComponent.name);
),
make_task_noR(StandardComponents::Call,
output << '(';
staticMap[typeid(ParseTree)](parsedTree, index);
output << ')';
)
}));
return staticMap;
};
static shared_ptr<Target> 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.size(); ++i) {
const unique_ptr<ParseComponent>& component = tree[i];
const type_info& id = component->getId();
try {
taskMap.at(id)(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() + '\n';
};
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<string_view, 3> languages { ".lua", ".js", ".py" };
shared_ptr<Target> 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<LANGUAGE>(i);
break;
}
}
shared_ptr<Target> target;
#define ADDTARGET(X) target = shared_ptr<X>(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_nonlocal_task
#undef make_task_noR
#undef make_task
#undef make_task_base_R
#undef make_task_base
return target;
}
#endif //YERBACON_TARGET_HPP