135 lines
5.7 KiB
C++
135 lines
5.7 KiB
C++
#ifndef YERBACON_TARGET_HPP
|
|
#define YERBACON_TARGET_HPP
|
|
|
|
#include <vector>
|
|
#include <string_view>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <cstring>
|
|
|
|
#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<size_t> 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<Target> forName(string_view name, bool newLines);
|
|
|
|
#define with(X) if (id == typeid(X)) { this->on(reinterpret_cast<X&>(*component)); continue; }
|
|
string transpileWithTree(const ParseTree& tree) {
|
|
output.str(string());
|
|
for (const unique_ptr<ParseComponent>& 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<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: {
|
|
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
|