summaryrefslogtreecommitdiff
path: root/transpiler
diff options
context:
space:
mode:
authorMatthew Kosarek <mattkae@protonmail.com>2021-05-16 19:50:15 -0400
committerMatthew Kosarek <mattkae@protonmail.com>2021-05-16 19:50:15 -0400
commita00c0aab1eb5a7a55bef8ca08115bdd722ab5699 (patch)
tree45b5c4cc8c380d0630a8e0185af7229f26dc754a /transpiler
parent4941a1874b6ca9d142d94df70b2aec5e0b35b94e (diff)
Moved the frontend directory up so that it no longer exists
Diffstat (limited to 'transpiler')
-rw-r--r--transpiler/List.h222
-rw-r--r--transpiler/Logger.cpp123
-rw-r--r--transpiler/Logger.h43
-rw-r--r--transpiler/MathHelper.h33
-rw-r--r--transpiler/MyString.cpp392
-rw-r--r--transpiler/MyString.h76
-rwxr-xr-xtranspiler/build.sh1
-rw-r--r--transpiler/pages.txt17
-rwxr-xr-xtranspiler/transpilerbin0 -> 38744 bytes
-rw-r--r--transpiler/transpiler.cpp250
10 files changed, 1157 insertions, 0 deletions
diff --git a/transpiler/List.h b/transpiler/List.h
new file mode 100644
index 0000000..00a466a
--- /dev/null
+++ b/transpiler/List.h
@@ -0,0 +1,222 @@
+#pragma once
+#include <cstdlib>
+#include <cstring>
+#include "Logger.h"
+
+#define FOREACH(list) \
+ for (size_t idx = 0; idx < list.numElements; idx++) \
+ if (auto value = list.getValue(idx)) \
+
+
+template <typename T>
+struct List {
+ T* data = nullptr;
+ size_t capacity = 0;
+ size_t numElements = 0;
+ bool growDynamically = true;
+
+ void allocate(size_t size);
+ void add(T* element);
+ void add(T& element);
+ void add(T&& element);
+ bool grow(size_t newSize);
+ void set(T* value, size_t index);
+ void remove(size_t index);
+ void clear();
+ void deallocate();
+ bool isEmpty() {
+ return data == nullptr || numElements == 0;
+ }
+ T* getValue(int index) const;
+ T& operator[](int idx) const;
+ void binarySort(int (*f)(T *first, T* second));
+ void setFromArray(T* arry, int size) {
+ allocate(size);
+ memcpy(data, arry, size * sizeof(T));
+ numElements = size;
+ }
+ void remove(int index) {
+ if (index >= numElements) {
+ logger_error("Cannot remove element at index: %d", index);
+ return;
+ }
+
+
+ if (index == numElements - 1) {
+ numElements--;
+ return;
+ }
+
+ memmove(data[index], data[index + 1], sizeof(T) * (numElements - index));
+ numElements--;
+ }
+};
+
+template <typename T>
+void List<T>::allocate(size_t size) {
+ if (size == 0 || size == capacity) {
+ numElements = 0;
+ return;
+ }
+
+ if (data != nullptr) {
+ deallocate();
+ }
+
+ data = static_cast<T*>(malloc(sizeof(T) * size));
+ capacity = size;
+ numElements = 0;
+}
+
+template <typename T>
+bool List<T>::grow(size_t newSize) {
+ if (!growDynamically) {
+ return false;
+ }
+
+ if (newSize == 0) {
+ return false;
+ }
+
+ T* newData = static_cast<T*>(malloc(sizeof(T) * newSize));
+
+ if (data != nullptr) {
+ memcpy(newData, data, numElements * sizeof(T));
+ delete data;
+ }
+
+ data = newData;
+ capacity = newSize;
+ return true;
+}
+
+template <typename T>
+void List<T>::set(T* value, size_t index) {
+ if (index >= capacity && !grow(index * 2)) {
+ return;
+ }
+
+ memcpy(&data[index], value, sizeof(T));
+}
+
+template <typename T>
+void List<T>::add(T* element) {
+ if (data == nullptr) {
+ allocate(2);
+ }
+
+ if (element == nullptr) {
+ logger_error("Element not defined");
+ return;
+ }
+
+ size_t newNumElements = numElements + 1;
+ if (newNumElements > capacity) {
+ if (!grow(2 * capacity)) {
+ logger_error("Trying to add to list but unable to grow the array");
+ return;
+ }
+ }
+
+ memcpy(&data[numElements], element, sizeof(T));
+ numElements = newNumElements;
+}
+
+template <typename T>
+void List<T>::add(T& element) {
+ if (data == nullptr) {
+ allocate(2);
+ }
+
+ size_t newNumElements = numElements + 1;
+ if (newNumElements > capacity) {
+ if (!grow(2 * capacity)) {
+ logger_error("Trying to add to list but unable to grow the array");
+ return;
+ }
+ }
+
+ memcpy(&data[numElements], &element, sizeof(T));
+ numElements = newNumElements;
+}
+
+template <typename T>
+void List<T>::add(T&& element) {
+ if (data == nullptr) {
+ allocate(2);
+ }
+
+ size_t newNumElements = numElements + 1;
+ if (newNumElements > capacity) {
+ if (!grow(2 * capacity)) {
+ logger_error("Trying to add to list but unable to grow the array");
+ return;
+ }
+ }
+
+ memcpy(&data[numElements], &element, sizeof(T));
+ numElements = newNumElements;
+}
+
+template <typename T>
+void List<T>::remove(size_t idx) {
+ if (idx >= numElements) {
+ logger_error("Index is outside of the list: %d >= %d", idx, numElements);
+ return;
+ }
+
+ for (; idx < numElements - 1; idx++) {
+ data[idx] = data[idx + 1];
+ }
+
+ numElements--;
+}
+
+template <typename T>
+void List<T>::deallocate() {
+ if (data != nullptr) {
+ free(data);
+ data = nullptr;
+ }
+
+ capacity = 0;
+ numElements = 0;
+}
+
+template <typename T>
+void List<T>::clear() {
+ numElements = 0;
+}
+
+template <typename T>
+T* List<T>::getValue(int idx) const {
+ return &data[idx];
+}
+
+template <typename T>
+T& List<T>::operator[](int idx) const {
+ return data[idx];
+}
+
+template <typename T>
+void List<T>::binarySort(int (*f)(T *first, T* second)) {
+ if (data == nullptr) {
+ return;
+ }
+
+ for (size_t idx = 0; idx < numElements - 1; idx++) {
+ int minIdx = idx;
+ T firstValue = data[idx];
+
+ for (int innerIdx = idx + 1; innerIdx < numElements; innerIdx++) {\
+ T secondValue = data[innerIdx];
+ if (f(&firstValue, &secondValue) > 0) {
+ minIdx= innerIdx;
+ }
+ }
+
+ T temp = data[minIdx];
+ memmove(&data[minIdx], &data[idx], sizeof(T));
+ memmove(&data[idx], &temp, sizeof(T));
+ }
+}
diff --git a/transpiler/Logger.cpp b/transpiler/Logger.cpp
new file mode 100644
index 0000000..1068d88
--- /dev/null
+++ b/transpiler/Logger.cpp
@@ -0,0 +1,123 @@
+#include "Logger.h"
+#include <chrono>
+#include <cstdarg>
+#include <cstdio>
+
+namespace Logger {
+ LogLevel gLogLevel = LogLevel_Debug;
+ FILE* gFilePointer = NULL;
+
+ void initialize(LoggerOptions options) {
+ setLevel(options.level);
+ if (options.logToFile) {
+#ifdef WIN32
+ fopen_s(&gFilePointer, options.filePath, "a");
+#else
+ gFilePointer = fopen(options.filePath, "a");
+#endif
+ }
+ }
+
+ void setLevel(LogLevel level) {
+ gLogLevel = level;
+ }
+
+ LogLevel getLevel() {
+ return gLogLevel;
+ }
+
+ void printHeader(const char* levelStr, const char* fileName, int lineNumber) {
+ time_t t = time(0);
+ tm now;
+#ifdef WIN32
+ localtime_s(&now, &t);
+#else
+ now = *localtime(&t);
+#endif
+
+ printf("%s:%d [%d-%d-%d %d:%d:%d] %s: ", fileName, lineNumber, (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr);
+ if (gFilePointer != NULL) {
+ fprintf(gFilePointer, "[%d-%d-%d %d:%d:%d] %s: ", (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr);
+ }
+ }
+
+ void logInternal(const char* file, int lineNumber,LogLevel level, const char* format, va_list args) {
+ if (level < gLogLevel) {
+ return;
+ }
+
+
+ const char* levelStr;
+ switch (level) {
+ case LogLevel_Debug:
+ levelStr = "Debug";
+ break;
+ case LogLevel_Info:
+ levelStr = "Info";
+ break;
+ case LogLevel_Warn:
+ levelStr = "Warning";
+ break;
+ case LogLevel_Error:
+ levelStr = "Error";
+ break;
+ default:
+ levelStr = "Unknown";
+ break;
+ }
+
+ if (gFilePointer != NULL) {
+ va_list fileArgs;
+ va_copy(fileArgs, args);
+ vfprintf(gFilePointer, format, fileArgs);
+ fprintf(gFilePointer, "\n");
+ }
+
+ printHeader(levelStr, file, lineNumber);
+
+ vprintf(format, args);
+ printf("\n");
+ }
+
+ void doLog(const char* file, int lineNumber,LogLevel level, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ logInternal(file, lineNumber, level, format, args);
+ va_end(args);
+ }
+
+ void doDebug(const char* file, int lineNumber,const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ logInternal(file, lineNumber, LogLevel_Debug, format, args);
+ va_end(args);
+ }
+
+ void doInfo(const char* file, int lineNumber,const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ logInternal(file, lineNumber, LogLevel_Info, format, args);
+ va_end(args);
+ }
+
+ void doWarning(const char* file, int lineNumber,const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ logInternal(file, lineNumber, LogLevel_Warn, format, args);
+ va_end(args);
+ }
+
+ void doError(const char* file, int lineNumber,const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ logInternal(file, lineNumber, LogLevel_Error, format, args);
+ va_end(args);
+ }
+
+ void free() {
+ if (gFilePointer) {
+ fclose(gFilePointer);
+ gFilePointer = NULL;
+ }
+ }
+}
diff --git a/transpiler/Logger.h b/transpiler/Logger.h
new file mode 100644
index 0000000..7596b6f
--- /dev/null
+++ b/transpiler/Logger.h
@@ -0,0 +1,43 @@
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <cstring>
+
+enum LogLevel {
+ LogLevel_Debug = 0,
+ LogLevel_Info = 1,
+ LogLevel_Warn = 2,
+ LogLevel_Error = 3
+};
+
+struct LoggerOptions {
+ LogLevel level = LogLevel_Debug;
+ bool logToFile = false;
+ const char* filePath = "debug.log";
+};
+
+namespace Logger {
+ void initialize(LoggerOptions options);
+ void setLevel(LogLevel level);
+ LogLevel getLevel();
+ void doLog(const char* file, int lineNumber, LogLevel level, const char* format, ...);
+ void doDebug(const char* file, int lineNumber, const char* format, ...);
+ void doInfo(const char* file, int lineNumber, const char* format, ...);
+ void doWarning(const char* file, int lineNumber, const char* format, ...);
+ void doError(const char* file, int lineNumber, const char* format, ...);
+ void free();
+};
+
+#if WIN32
+#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
+#else
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+#endif
+
+#define logger_log(level, format, ...) Logger::doLog(__FILENAME__, __LINE__, level, format, ## __VA_ARGS__)
+#define logger_debug(format, ...) Logger::doDebug(__FILENAME__, __LINE__, format, ## __VA_ARGS__)
+#define logger_info(format, ...) Logger::doInfo(__FILENAME__, __LINE__, format, ## __VA_ARGS__)
+#define logger_warning(format, ...) Logger::doWarning(__FILENAME__, __LINE__, format, ## __VA_ARGS__)
+#define logger_error(format, ...) Logger::doError(__FILENAME__, __LINE__, format, ## __VA_ARGS__)
+
+#endif
diff --git a/transpiler/MathHelper.h b/transpiler/MathHelper.h
new file mode 100644
index 0000000..b9c9cb6
--- /dev/null
+++ b/transpiler/MathHelper.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#define PI 3.14159265358979323846f
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower)))
+#define DTOR (PI / 180.0f)
+#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)
+
+namespace MathHelper {
+ inline float radiansToDegrees(float radians) {
+ return radians * 180.f / PI;
+ }
+
+ inline float degreesToRadians(float degrees) {
+ return degrees * PI / 180.f;
+ }
+
+ inline int nearestPowerOfTwo(int n) {
+ int v = n;
+
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++; // next power of 2
+
+ return v;
+ }
+}
diff --git a/transpiler/MyString.cpp b/transpiler/MyString.cpp
new file mode 100644
index 0000000..1cb3d43
--- /dev/null
+++ b/transpiler/MyString.cpp
@@ -0,0 +1,392 @@
+#include "MyString.h"
+#include "MathHelper.h"
+#include <cstdlib>
+#include <cstdio>
+#include <iostream>
+#include <cstdarg>
+#include <cmath>
+
+int StringBuffer::add(const char* str) {
+ int spaceNeeded = strlen(str);
+ int spaceAvailable = BUFFER_SIZE - pointer;
+
+ int amountToCopy = 0;
+ if (spaceAvailable >= spaceNeeded) {
+ amountToCopy = spaceNeeded;
+ } else {
+ amountToCopy = spaceAvailable;
+ }
+
+ memcpy(&buffer[pointer], str, sizeof(char) * amountToCopy);
+ pointer += amountToCopy;
+ buffer[pointer] = '\0';
+ return amountToCopy;
+}
+
+void StringBuffer::reset() {
+ buffer[0] = '\0';
+ pointer = 0;
+}
+
+bool StringBuffer::isFull() {
+ return pointer == BUFFER_SIZE;
+}
+
+StringBuffer* StringBuilder::getCurrentBuffer() {
+ if (bufferPointer == 0) {
+ if (!defaultBuffer.isFull()) {
+ return &defaultBuffer;
+ }
+
+ StringBuffer newBuffer;
+ dynamicBuffer.allocate(2);
+ bufferPointer++;
+ dynamicBuffer.add(&newBuffer);
+ }
+
+ if (dynamicBuffer[bufferPointer - 1].isFull()) {
+ StringBuffer newBuffer;
+ dynamicBuffer.add(&newBuffer);
+ bufferPointer++;
+ }
+
+ return &dynamicBuffer[bufferPointer - 1];
+}
+
+StringBuffer* StringBuilder::getBufferAtIdx(int index) {
+ if (index == 0) {
+ return &defaultBuffer;
+ }
+
+ index = index - 1;
+ if (index < static_cast<int>(dynamicBuffer.numElements)) {
+ return &dynamicBuffer[index];
+ }
+
+ return nullptr;
+}
+
+const StringBuffer* StringBuilder::getBufferAtIdxConst(int index) const {
+ if (index == 0) {
+ return &defaultBuffer;
+ }
+
+ index = index - 1;
+ if (index < static_cast<int>(dynamicBuffer.numElements)) {
+ return &dynamicBuffer[index];
+ }
+
+ return nullptr;
+}
+
+String StringBuilder::toString() {
+ int strSize = (bufferPointer + 1) * StringBuffer::BUFFER_SIZE;
+ String retval;
+
+ if (strSize > String::SSO_SIZE) {
+ retval.dynamicBuffer = new char[strSize + 1];
+ memcpy(retval.dynamicBuffer, defaultBuffer.buffer, sizeof(char) * StringBuffer::BUFFER_SIZE);
+ FOREACH(dynamicBuffer) {
+ memcpy(&retval.dynamicBuffer[(idx + 1) * StringBuffer::BUFFER_SIZE], value->buffer, sizeof(char) * StringBuffer::BUFFER_SIZE);
+ }
+ retval.isSSO = false;
+ } else {
+ memcpy(retval.defaultBuffer, defaultBuffer.buffer, sizeof(char) * strSize);
+ retval.isSSO = true;
+ }
+
+ retval.capacity = strSize;
+ retval.length = length;
+ retval.getValue()[retval.length] = '\0';
+ return retval;
+}
+
+void StringBuilder::addStr(String* str) {
+ addStr(str->getValue());
+}
+
+void StringBuilder::addStr(const char* str) {
+ int amountLeft = strlen(str);
+ length += amountLeft;
+ int ptr = 0;
+
+ do {
+ StringBuffer* currentBuffer = getCurrentBuffer();
+ int amtAdded = currentBuffer->add(&str[ptr]);
+ amountLeft = amountLeft - amtAdded;
+ ptr += amtAdded;
+ } while (amountLeft > 0);
+}
+
+void StringBuilder::addChar(char c) {
+ char wrapper[2] = { c, '\0' };
+ addStr(wrapper);
+}
+
+const int DEFAULT_NUMBER_BUFFER_SIZE = 32;
+
+void StringBuilder::addInt(int value) {
+ char buffer[DEFAULT_NUMBER_BUFFER_SIZE];
+ sprintf(buffer, "%d", value);
+ addStr(buffer);
+}
+
+// Pulled from here: https://stackoverflow.com/questions/7228438/convert-double-float-to-string
+inline int n_tu(int number, int count) {
+ int result = 1;
+ while(count-- > 0)
+ result *= number;
+
+ return result;
+}
+
+void StringBuilder::addFloat(float value) {
+ char buffer[DEFAULT_NUMBER_BUFFER_SIZE];
+ sprintf(buffer, "%f", value);
+ addStr(buffer);
+}
+
+void StringBuilder::replace(const char* strToReplace, const char* replaceStr) {
+
+}
+
+int StringBuilder::indexOf(const char* str) {
+ return -1;
+}
+
+void StringBuilder::removeAt(int index, int count) {
+ if (index >= length) {
+ printf("Index is larger than the length");
+ return;
+ }
+
+ if (count <= 0) {
+ printf("Count was <= zero");
+ return;
+ }
+
+ int newLength = length - count;
+ if (newLength == 0) {
+ clear();
+ return;
+ }
+
+ // Easiest way to do this is to move the entire string to the left, starting at index by count
+ int currentIndex = index;
+ int copyIndex = index + count;
+
+ while (currentIndex < newLength) {
+ int bufferPos = floor(static_cast<float>(currentIndex) / static_cast<float>(StringBuffer::BUFFER_SIZE));
+ int pointerIdx = currentIndex % StringBuffer::BUFFER_SIZE;
+ StringBuffer* buffer = getBufferAtIdx(bufferPos);
+
+ char newChar = '\0';
+ int copyBufferPos = floor(static_cast<float>(copyIndex) / static_cast<float>(StringBuffer::BUFFER_SIZE));
+ StringBuffer* copyBuffer = getBufferAtIdx(copyBufferPos);
+ if (copyBuffer != nullptr) {
+ int bufferPointerIdx = copyIndex % StringBuffer::BUFFER_SIZE;
+ if (bufferPointerIdx < copyBuffer->pointer) {
+ newChar = copyBuffer->buffer[bufferPointerIdx];
+ }
+ }
+
+ buffer->buffer[pointerIdx] = newChar;
+
+ currentIndex++;
+ copyIndex++;
+ }
+
+ // Brute force update the buffer that we're currently using
+ length = newLength;
+ int newNumBuffers = ceil(static_cast<float>(length) / static_cast<float>(StringBuffer::BUFFER_SIZE)); // This is how many buffers we should have
+ if (newNumBuffers <= 1) {
+ dynamicBuffer.numElements = 0;
+ bufferPointer = 0;
+ } else {
+ dynamicBuffer.numElements = newNumBuffers - 1;
+ bufferPointer = newNumBuffers - 1;
+ }
+
+ StringBuffer* lastBuffer = getBufferAtIdx(bufferPointer);
+ if (lastBuffer->pointer != StringBuffer::BUFFER_SIZE) {
+ lastBuffer->pointer = length % StringBuffer::BUFFER_SIZE;
+ }
+ lastBuffer->buffer[lastBuffer->pointer] = '\0';
+}
+
+void StringBuilder::format(const char* str, ...) {
+ va_list args;
+ va_start(args, str);
+
+ while (*str != '\0') {
+ if (*str == '%') {
+ str++;
+ switch (*str) {
+ case 'd':
+ addInt(va_arg(args, int));
+ break;
+ case 'f':
+ addFloat(static_cast<float>(va_arg(args, double)));
+ break;
+ case 's':
+ addStr(va_arg(args, char*));
+ default:
+ break;
+ }
+ } else {
+ addChar(*str);
+ }
+
+ str++;
+ }
+
+ va_end(args);
+}
+
+void StringBuilder::clear() {
+ bufferPointer = 0;
+ length = 0;
+ defaultBuffer.reset();
+
+ if (!dynamicBuffer.isEmpty()) {
+ FOREACH(dynamicBuffer) {
+ value->reset();
+ }
+ }
+
+ dynamicBuffer.clear();
+}
+
+void StringBuilder::free() {
+ dynamicBuffer.deallocate();
+}
+
+char StringBuilder::getCharAtIdx(int index) const {
+ int bufferPos = floor(static_cast<float>(index) / static_cast<float>(StringBuffer::BUFFER_SIZE));
+ int pointerIdx = index % StringBuffer::BUFFER_SIZE;
+ const StringBuffer* buffer = getBufferAtIdxConst(bufferPos);
+ if (buffer != nullptr && pointerIdx < buffer->pointer) {
+ return buffer->buffer[pointerIdx];
+ }
+
+ return '\0';
+}
+
+void StringBuilder::insert(char c, int index) {
+ if (index >= length) {
+ addChar(c);
+ return;
+ }
+
+ // Move all of the characters forward by one
+ char currentChar = c;
+ for (int cIdx = index; cIdx < length + 1; cIdx++) {
+ int bufferPos = floor(static_cast<float>(cIdx) / static_cast<float>(StringBuffer::BUFFER_SIZE));
+ int pointerIdx = cIdx % StringBuffer::BUFFER_SIZE;
+ StringBuffer* buffer = getBufferAtIdx(bufferPos);
+ if (buffer == nullptr) {
+ buffer = getCurrentBuffer(); // Get a new buffer if this one is filled up
+ }
+
+ char nextCurrent = buffer->buffer[pointerIdx];
+ buffer->buffer[pointerIdx] = currentChar;
+ currentChar = nextCurrent;
+ }
+
+ // Move the final buffer forward
+ StringBuffer* finalBuffer = getCurrentBuffer();
+ finalBuffer->pointer++;
+ finalBuffer->buffer[finalBuffer->pointer] = '\0';
+ length = length + 1;
+}
+
+String::String() {
+}
+
+String::String(const char* str) {
+ set(str);
+}
+
+void String::operator =(const char* str) {
+ set(str);
+}
+
+char* String::getValue() {
+ return isSSO ? defaultBuffer : dynamicBuffer;
+}
+
+const char* String::getValueConst() const {
+ return isSSO ? defaultBuffer : dynamicBuffer;
+}
+
+void String::set(const char* str) {
+ int inLen = strlen(str);
+
+ if (inLen <= SSO_SIZE) {
+ isSSO = true;
+ memcpy(defaultBuffer, str, inLen);
+ defaultBuffer[inLen] = '\0';
+ } else {
+ if (capacity >= inLen) {
+ memcpy(dynamicBuffer, str, inLen);
+ } else {
+ free();
+ capacity = MathHelper::nearestPowerOfTwo(inLen + 1);
+ dynamicBuffer = new char[capacity];
+ memcpy(dynamicBuffer, str, inLen);
+ }
+ isSSO = false;
+ dynamicBuffer[inLen] = '\0';
+ }
+
+ length = inLen;
+}
+
+void String::free() {
+ capacity = 0;
+ length = 0;
+ isSSO = true;
+ if (dynamicBuffer != nullptr) {
+ delete dynamicBuffer;
+ }
+
+ defaultBuffer[0] = '\0';
+}
+
+int String::toInteger() {
+ return atoi(getValue());
+}
+
+float String::toFloat() {
+ return static_cast<float>(atof(getValue()));
+}
+
+int String::indexOf(char c) {
+ char* buffer = getValue();
+ int idx = 0;
+ while (buffer[idx] != '\0') {
+ if (buffer[idx] == c) {
+ return idx;
+ }
+ idx++;
+ }
+
+ return -1;
+}
+
+StringView String::substring(int start, int end) {
+ StringView retval;
+ if (start >= length) {
+ retval.error = true;
+ return retval;
+ }
+
+ if (end >= length) {
+ end = length - 1;
+ }
+
+ char* value = getValue();
+ retval.value = &value[start];
+ retval.length = end - start;
+ return retval;
+}
diff --git a/transpiler/MyString.h b/transpiler/MyString.h
new file mode 100644
index 0000000..d05d84a
--- /dev/null
+++ b/transpiler/MyString.h
@@ -0,0 +1,76 @@
+#pragma once
+#include "List.h"
+
+struct StringView {
+ bool error = false;
+ char* value = nullptr;
+ size_t length = 0;
+};
+
+struct String {
+ const static int SSO_SIZE = 31;
+
+ char defaultBuffer[String::SSO_SIZE + 1] = { '\0' };
+ char* dynamicBuffer = nullptr;
+
+ int length = 0;
+ int capacity = 0;
+ bool isSSO = true;
+
+ String();
+ String(const char* str);
+ char* getValue();
+ const char* getValueConst() const;
+ void operator =(const char* str);
+ void set(const char* str);
+ void free();
+ int toInteger();
+ float toFloat();
+ int indexOf(char c);
+ inline bool equals(const String& other) { return strcmp(getValueConst(), other.getValueConst()) == 0; };
+ inline bool equalsCstr(const char* str) { return strcmp(getValueConst(), str) == 0; };
+ StringView substring(int start, int end);
+};
+
+struct StringBuffer {
+ const static int BUFFER_SIZE = 31;
+
+ int pointer = 0;
+ char buffer[StringBuffer::BUFFER_SIZE + 1]; // Leave space for trailing escape character
+
+ /*
+ * Appends the string to the buffer
+ * @param str
+ * @returns number of characters copied
+ */
+ int add(const char* str);
+ bool isFull();
+ void reset();
+};
+
+struct StringBuilder {
+ int bufferPointer = 0;
+ int length = 0;
+
+ StringBuffer defaultBuffer;
+ List<StringBuffer> dynamicBuffer;
+
+ StringBuffer* getCurrentBuffer();
+ StringBuffer* getBufferAtIdx(int index);
+ const StringBuffer* getBufferAtIdxConst(int index) const;
+ void addStr(String* str);
+ void addStr(const char* str);
+ void addChar(char c);
+ void format(const char* str, ...);
+ void addInt(int value);
+ void addFloat(float value);
+ void replace(const char* strToReplace, const char* replaceStr);
+ void removeAt(int index, int count);
+ int indexOf(char c);
+ int indexOf(const char* str);
+ String toString();
+ void clear();
+ char getCharAtIdx(int index) const;
+ void insert(char c, int index);
+ void free();
+}; \ No newline at end of file
diff --git a/transpiler/build.sh b/transpiler/build.sh
new file mode 100755
index 0000000..6ca478e
--- /dev/null
+++ b/transpiler/build.sh
@@ -0,0 +1 @@
+g++ -o transpiler transpiler.cpp Logger.cpp MyString.cpp \ No newline at end of file
diff --git a/transpiler/pages.txt b/transpiler/pages.txt
new file mode 100644
index 0000000..1cb2769
--- /dev/null
+++ b/transpiler/pages.txt
@@ -0,0 +1,17 @@
+&#127936; "2D"
+>"Rigidbody"
+>>/2d/_rigidbody/part_1.html "Linear Forces"
+>>/2d/_rigidbody/part_2.html "Rotational Forces"
+>>/2d/_rigidbody/part_3.html "Collision Forces"
+>"Collisions"
+>>/2d/_collisions/circle_line.html "Circle-Line"
+>>/2d/_collisions/rectangle_line.html "Rectangle-Line"
+>>/2d/_collisions/rectangle_rectangle.html "Rectangle-Rectangle"
+>>/2d/_collisions/pill_line.html "Pill-Line"
+>>/2d/_collisions/pill_pill.html "Pill-Pill"
+>>/2d/_collisions/polygon_polygon.html "Polygon-Polygon"
+&#127776; "3D"
+&#128295; "WebAssembly"
+>>/intro/intro.html "Introduction"
+&#128712; "About"
+>>/roadmap.html "Roadmap"
diff --git a/transpiler/transpiler b/transpiler/transpiler
new file mode 100755
index 0000000..5b6e14f
--- /dev/null
+++ b/transpiler/transpiler
Binary files differ
diff --git a/transpiler/transpiler.cpp b/transpiler/transpiler.cpp
new file mode 100644
index 0000000..fa9820a
--- /dev/null
+++ b/transpiler/transpiler.cpp
@@ -0,0 +1,250 @@
+#include "MyString.h"
+#include "List.h"
+#include "Logger.h"
+#include <cstdio>
+#include <vector>
+#include <cstring>
+
+struct InnerCategory {
+ bool isLabel = false;
+ String label;
+ String path;
+
+ void toCode(StringBuilder& sb) {
+ if (isLabel) {
+ sb.format("\t\t\t\t\t<li><label>%s</label></li>\n", label.getValue());
+ } else {
+ sb.format("\t\t\t\t\t<li><a href=\"%s\">%s</a></li>\n", path.getValue(), label.getValue());
+ }
+ }
+
+ void free() {
+ label.free();
+ path.free();
+ }
+};
+
+struct OuterCategory {
+ String outerName;
+ String outerSymbol;
+ List<InnerCategory> items;
+
+ void toCode(StringBuilder& sb) {
+ sb.addStr("\t\t\t<li>\n");
+ sb.format("\t\t\t\t<span>%s<span>%s</span></span>\n", outerSymbol.getValue(), outerName.getValue());
+ sb.addStr("\t\t\t\t<ul class=\"inner-tree\">\n");
+
+ FOREACH(items) {
+ value->toCode(sb);
+ }
+
+ sb.addStr("\t\t\t\t</ul>\n");
+ sb.addStr("\t\t\t</li>\n");
+ }
+
+ void reset() {
+ outerName.free();
+ outerSymbol.free();
+ items.clear();
+ }
+
+ void free() {
+ outerName.free();
+ outerSymbol.free();
+
+ FOREACH(items) {
+ value->free();
+ }
+ items.deallocate();
+ }
+};
+
+int main() {
+ logger_info("Running transpiler");
+ FILE* pagesFile = fopen("transpiler/pages.txt", "r+");
+
+ char * cLine = NULL;
+ size_t len = 0;
+ ssize_t read;
+
+ StringBuilder sb;
+ StringBuilder otherSb;
+
+ sb.addStr("\t\t<nav>\n\t\t<ul class=\"outer-tree\">\n");
+ sb.addStr("\t\t\t<li><a href=\"/\">Introduction</a></li>\n");
+
+ bool isFirst = true;
+ OuterCategory oc;
+ List<String> servedFiles;
+
+ servedFiles.add("./index.html");
+ while ((read = getline(&cLine, &len, pagesFile)) != -1) {
+ char* line = cLine;
+
+ int indent = 0;
+ while (line[0] == '>') {
+ line++;
+ indent++;
+ }
+
+ switch (indent) {
+ case 0: {
+ if (!isFirst) {
+ oc.toCode(sb);
+ }
+ oc.reset();
+
+ isFirst = false;
+ // Outer tree item
+ while (line[0] != ' ') {
+ otherSb.addChar(line[0]);
+ line++;
+ }
+ line++;
+
+ oc.outerSymbol = otherSb.toString();
+ otherSb.clear();
+
+ while (line[0] != '\n') {
+ if (line[0] != '\"') {
+ otherSb.addChar(line[0]);
+ }
+ line++;
+ }
+
+ oc.outerName = otherSb.toString();
+ logger_info(oc.outerName.getValue());
+ otherSb.clear();
+
+ break;
+ }
+ case 1: {
+ InnerCategory ic;
+ ic.isLabel = true;
+ while (line[0] != '\n') {
+ if (line[0] != '\"') {
+ otherSb.addChar(line[0]);
+ }
+ line++;
+ }
+ ic.label = otherSb.toString();
+ otherSb.clear();
+ oc.items.add(ic);
+
+ break;
+ }
+ case 2: {
+ InnerCategory ic;
+ ic.isLabel = false;
+
+ while (line[0] != ' ') {
+ otherSb.addChar(line[0]);
+ line++;
+ }
+ line++;
+
+ ic.path = otherSb.toString();
+ otherSb.insert('.', 0);
+ String pathCopy = otherSb.toString();
+ servedFiles.add(pathCopy);
+ otherSb.clear();
+
+
+ while (line[0] != '\n') {
+ if (line[0] != '\"') {
+ otherSb.addChar(line[0]);
+ }
+ line++;
+ }
+
+ ic.label = otherSb.toString();
+ otherSb.clear();
+ oc.items.add(ic);
+
+ break;
+ }
+ }
+ }
+
+ oc.toCode(sb);
+ sb.addStr("\t\t</ul>\n\t\t</nav>\n");
+ oc.reset();
+ fclose(pagesFile);
+
+ String navbarRetval = sb.toString();
+ sb.clear();
+
+ logger_info("Creating served files");
+
+ // Create the header
+ FOREACH(servedFiles) {
+ otherSb.clear();
+ otherSb.format("%s.content", value->getValue());
+ String contentFileName = otherSb.toString();
+ FILE* contentFile = fopen(contentFileName.getValue(), "r+");
+ if (contentFile == NULL) {
+ logger_warning("Could not output file, most likely unimplemented: %s", contentFileName.getValue());
+ continue;
+ }
+
+ logger_info("Outputting file: %s", contentFileName.getValue());
+
+ // Header
+ sb.addStr("<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "\t<head>\n"
+ "\t\t<meta charset=\"utf-8\">\n"
+ "\t\t<link rel=\"stylesheet\" href=\"/index.css\">\n"
+ "\t\t<title>Physics for Games</title>\n"
+ "\t\t<link rel=\"shortcut icon\" href=\"favicon/favicon.ico\" type=\"image/x-icon\">\n"
+ "\t</head>\n"
+ "\t<body>\n"
+ "\t\t<header>\n"
+ "\t\t\t<h1>Physics for Games</h1>\n"
+ "\t\t</header>\n"
+ "\t\t<main>\n");
+
+ // Write navigation bar
+ sb.addStr(navbarRetval.getValue());
+ // Read body
+
+ fseek(contentFile, 0, SEEK_END);
+ long fsize = ftell(contentFile);
+ fseek(contentFile, 0, SEEK_SET);
+
+ char* bodyContent = new char[fsize + 1];
+ fread(bodyContent, 1, fsize, contentFile);
+ bodyContent[fsize] = '\0';
+ sb.addStr(bodyContent);
+ delete [] bodyContent;
+
+
+ contentFileName.free();
+ fclose(contentFile);
+
+ // Footer
+ sb.addStr("\t\t</main>\n"
+ "\t</body>\n"
+ "</html>\n");
+
+ FILE* outFile = fopen(value->getValue(), "w+");
+ String outStr = sb.toString();
+ fwrite(outStr.getValue(), 1, outStr.length, outFile);
+ outStr.free();
+ fclose(outFile);
+ sb.clear();
+ }
+
+ logger_info("Outputting files to their proper directories...");
+ sb.free();
+ navbarRetval.free();
+ otherSb.free();
+
+ FOREACH(servedFiles) {
+ value->free();
+ }
+
+ servedFiles.deallocate();
+
+ return 0;
+} \ No newline at end of file