#include "SystemFunctions.h" #include #include #include #include #include #include #include "Memory.h" #include "Utils.h" #include "Window.h" #define RETURN_INT(value) \ *r = INT_VALUE(value); \ return false static u32 seed = 0; static bool rawMode = false; static struct termios originalTerminal; static void append(char** buffer, size_t* n, int change) { if(change > 0) { *buffer += change; *n -= (size_t)change; } } static void printUnicode(const Value* v, char** buffer, size_t* n) { UTF8 u = convertUnicodeToUTF8(v->data); for(u32 l = 0; l < u.length && *n > 1; l++) { **buffer = u.data[l]; append(buffer, n, 1); } if(*n > 0) { **buffer = '\0'; } } #define APPEND(b, n, format, ...) \ append(b, n, snprintf(*b, *n, format __VA_OPT__(, ) __VA_ARGS__)); static void printArray( const Code* c, const Value* v, char** buffer, size_t* n) { if(v->data == -1) { APPEND(buffer, n, "null array"); return; } Allocation* a = memoryConvertToPointer(v->data); Value* end = a->values + a->values[0].data + 1; for(Value* i = a->values + 1; i != end; i++) { switch(i->type) { case VT_INT32: printUnicode(i, buffer, n); break; case VT_ARRAY: APPEND(buffer, n, "array"); break; case VT_CONSTANT_STRING: APPEND(buffer, n, "%s", c->code.data + i->data); break; } } } static void print(const Code* c, Value* vs, i32 n, char* buffer, size_t bn) { for(i32 i = 0; i < n; i++) { Value* v = vs + i; switch(v->type) { case VT_INT32: APPEND(&buffer, &bn, "%d", v->data); break; case VT_ARRAY: printArray(c, v, &buffer, &bn); break; case VT_CONSTANT_STRING: APPEND(&buffer, &bn, "%s", c->code.data + v->data); break; } } } static bool sfPrint(Code* c, Value* r, Value* vs, i32 n) { char buffer[1024]; print(c, vs, n, buffer, sizeof(buffer)); fputs(buffer, stdout); RETURN_INT(0); } static bool sfRender(Code* c, Value* r, Value* vs, i32 n) { i32 x = vs[0].data; i32 y = vs[1].data; Color color = (Color)vs[2].data; char buffer[1024]; print(c, vs + 3, n - 3, buffer, sizeof(buffer)); char* s = buffer; while(*s != 0) { UTF8 u = {.data = {*(s++)}, .length = 1}; for(int k = 0; k < 3 && isUTF8Remainder(*s); k++) { u.data[u.length] = *s; u.length++; s++; } i32 i = convertUTF8toUnicode(u); windowSetCharacter(x, y, i, color); x += 8; } RETURN_INT(0); } static bool sfPrintLine(Code* c, Value* r, Value* vs, i32 n) { bool b = sfPrint(c, r, vs, n); putchar('\n'); return b; } #define ESC "\33[" #define esc(s) fputs(ESC s, stdout) static bool sfClear(Code*, Value* r, Value*, i32) { esc("2J"); RETURN_INT(0); } static bool sfGetWidth(Code*, Value* r, Value*, i32) { struct winsize w; if(ioctl(0, TIOCGWINSZ, &w)) { RETURN_INT(0); } RETURN_INT(w.ws_col); } static bool sfGetHeight(Code*, Value* r, Value*, i32) { struct winsize w; if(ioctl(0, TIOCGWINSZ, &w)) { RETURN_INT(0); } RETURN_INT(w.ws_row); } static bool sfClearLine(Code*, Value* r, Value*, i32) { esc("2K\r"); RETURN_INT(0); } static bool sfHideCursor(Code*, Value* r, Value*, i32) { esc("?25l"); RETURN_INT(0); } static bool sfShowCursor(Code*, Value* r, Value*, i32) { esc("?25h"); RETURN_INT(0); } static bool sfResetCursor(Code*, Value* r, Value*, i32) { esc("H"); RETURN_INT(0); } static bool sfMoveCursorLeft(Code*, Value* r, Value* vs, i32) { if(vs[0].data > 0) { printf(ESC "%dD", vs[0].data); } RETURN_INT(0); } static bool sfMoveCursorRight(Code*, Value* r, Value* vs, i32) { if(vs[0].data > 0) { printf(ESC "%dC", vs[0].data); } RETURN_INT(0); } static bool sfMoveCursorUp(Code*, Value* r, Value* vs, i32) { if(vs[0].data > 0) { printf(ESC "%dA", vs[0].data); } RETURN_INT(0); } static bool sfMoveCursorDown(Code*, Value* r, Value* vs, i32) { if(vs[0].data > 0) { printf(ESC "%dB", vs[0].data); } RETURN_INT(0); } static bool sfSleep(Code*, Value* r, Value* vs, i32) { struct timespec t = {.tv_sec = vs[0].data, .tv_nsec = vs[1].data}; thrd_sleep(&t, nullptr); RETURN_INT(0); } static bool sfRandom(Code*, Value* r, Value* vs, i32) { seed = seed * 1664525u + 1013904223u; u32 s = seed % (u32)(vs[1].data + 1 - vs[0].data); RETURN_INT(vs[0].data + (i32)s); } static bool codeAllocate(Code* c, Value* v, i32 n) { v->type = VT_ARRAY; v->data = -1; if(n <= 0) { return false; } codeRunCollector(c); size_t s = sizeof(Allocation) + ((size_t)n + 1) * sizeof(Value); Allocation* a = memoryAllocate(s); if(a == nullptr) { SET_ERROR("Out of memory"); return true; } memset(a, 0, s); a->next = c->allocations; c->allocations = a; a->values[0].type = VT_INT32; a->values[0].data = n; v->data = memoryConvertToIndex(a); return false; } static bool sfArray(Code* c, Value* r, Value* vs, i32) { return codeAllocate(c, r, vs[0].data); } static bool sfGetAllocations(Code* c, Value* r, Value*, i32) { codeRunCollector(c); i32 counter = 0; for(Allocation* a = c->allocations; a != nullptr; a = a->next) { counter++; } RETURN_INT(counter); } static bool sfEnterRawTerminal(Code* c, Value* r, Value*, i32) { if(rawMode) { RETURN_INT(0); } if(tcgetattr(STDIN_FILENO, &originalTerminal)) { SET_ERROR("Cannot safe current terminal mode"); return true; } struct termios raw = originalTerminal; raw.c_iflag &= ~(tcflag_t)(ICRNL | IXON); raw.c_lflag &= ~(tcflag_t)(ECHO | ICANON | IEXTEN | ISIG); raw.c_cc[VMIN] = 0; raw.c_cc[VTIME] = 0; if(tcsetattr(STDIN_FILENO, TCSANOW, &raw)) { SET_ERROR("Cannot enter raw terminal mode"); return true; } RETURN_INT(0); } static bool sfLeaveRawTerminal(Code* c, Value* r, Value*, i32) { if(!rawMode) { RETURN_INT(0); } if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &originalTerminal)) { SET_ERROR("Cannot leave raw terminal mode"); return true; } RETURN_INT(0); } static bool sfReadChar(Code*, Value* r, Value*, i32) { u8 ch = 0; ssize_t bytes = read(STDIN_FILENO, &ch, 1); RETURN_INT(bytes <= 0 ? '\0' : ch); } static bool sfFlush(Code*, Value* r, Value*, i32) { fflush(stdout); RETURN_INT(0); } static bool sfWindowInit(Code* c, Value* r, Value* vs, i32) { WindowSettings ws = { .width = vs[0].data, .height = vs[1].data, .title = "Dummy"}; c->error = windowInit(&ws); *r = INT_VALUE(0); return hasError(&c->error); } #define WINDOW_CHECK() \ do { \ if(!windowExists()) { \ SET_ERROR("No window exists"); \ return true; \ } \ } while(false) static bool sfWindowShouldClose(Code* c, Value* r, Value*, i32) { WINDOW_CHECK(); RETURN_INT(windowShouldClose()); } static bool sfWindowNextFrame(Code* c, Value* r, Value*, i32) { WINDOW_CHECK(); windowNextFrame(); RETURN_INT(0); } static bool sfWindowClear(Code* c, Value* r, Value* vs, i32) { WINDOW_CHECK(); windowClear((Color)vs[0].data); RETURN_INT(0); } static bool sfWindowSetPixel(Code* c, Value* r, Value* vs, i32) { WINDOW_CHECK(); windowSetPixel(vs[0].data, vs[1].data, (Color)vs[2].data); RETURN_INT(0); } static bool sfColor(Code*, Value* r, Value* vs, i32) { RETURN_INT(COLOR(vs[0].data, vs[1].data, vs[2].data)); } static bool sfGetRed(Code*, Value* r, Value* vs, i32) { RETURN_INT((vs[0].data & 0b1111100000000000) >> 11); } static bool sfGetGreen(Code*, Value* r, Value* vs, i32) { RETURN_INT((vs[0].data & 0b11111000000) >> 6); } static bool sfGetBlue(Code*, Value* r, Value* vs, i32) { RETURN_INT((vs[0].data & 0b111110) >> 1); } static bool sfIsButtonDown(Code*, Value* r, Value* vs, i32) { RETURN_INT(isButtonDown((Button)vs[0].data)); } static bool sfGetButtonDownTime(Code*, Value* r, Value* vs, i32) { RETURN_INT(getButtonDownTime((Button)vs[0].data)); } typedef struct { SystemFunction function; i32 arguments; const char* name; } FunctionEntry; static FunctionEntry systemFunctions[] = { {sfPrint, -1, "print"}, {sfPrintLine, -1, "printLine"}, {sfClear, 0, "clear"}, {sfSleep, 2, "sleep"}, {sfGetWidth, 0, "getWidth"}, {sfGetHeight, 0, "getHeight"}, {sfClearLine, 0, "clearLine"}, {sfHideCursor, 0, "hideCursor"}, {sfShowCursor, 0, "showCursor"}, {sfResetCursor, 0, "resetCursor"}, {sfMoveCursorLeft, 1, "moveCursorLeft"}, {sfMoveCursorRight, 1, "moveCursorRight"}, {sfMoveCursorUp, 1, "moveCursorUp"}, {sfMoveCursorDown, 1, "moveCursorDown"}, {sfRandom, 2, "random"}, {sfArray, 1, "array"}, {sfGetAllocations, 0, "getAllocations"}, {sfEnterRawTerminal, 0, "enterRawTerminal"}, {sfLeaveRawTerminal, 0, "leaveRawTerminal"}, {sfReadChar, 0, "readChar"}, {sfFlush, 0, "flush"}, {sfWindowInit, 2, "initWindow"}, {sfWindowShouldClose, 0, "shouldClose"}, {sfWindowNextFrame, 0, "nextFrame"}, {sfWindowClear, 1, "clearWindow"}, {sfWindowSetPixel, 3, "setPixel"}, {sfColor, 3, "color"}, {sfGetRed, 1, "getRed"}, {sfGetGreen, 1, "getGreen"}, {sfGetBlue, 1, "getBlue"}, {sfIsButtonDown, 1, "isButtonDown"}, {sfGetButtonDownTime, 1, "getButtonDownTime"}, {sfRender, -4, "render"}, }; static constexpr i32 amount = sizeof(systemFunctions) / sizeof(FunctionEntry); i32 getSystemFunctionIndex(const char* s) { for(i32 i = 0; i < amount; i++) { if(strcmp(systemFunctions[i].name, s) == 0) { return i; } } return -1; } i32 getSystemFunctionArguments(i32 index) { if(index >= 0 && index < amount) { return systemFunctions[index].arguments; } return -1; } SystemFunction getSystemFunction(i32 index) { if(index >= 0 && index < amount) { return systemFunctions[index].function; } return nullptr; } void initSystemFunctions() { struct timespec t = {}; timespec_get(&t, TIME_UTC); seed = (u32)t.tv_nsec; }