diff --git a/Toy b/Toy index 8498864..7bf18a7 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit 8498864dde6ab11bda8ed8b6d0bf09a4c4de0af0 +Subproject commit 7bf18a744c84a14f9942fba6bdc2e1f6bd02d09a diff --git a/core/lib_standard.c b/core/lib_standard.c index 5ad4bee..8061941 100644 --- a/core/lib_standard.c +++ b/core/lib_standard.c @@ -2,7 +2,7 @@ #include "memory.h" -#include +#include static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) { //no arguments diff --git a/core/lib_timer.c b/core/lib_timer.c new file mode 100644 index 0000000..efb2426 --- /dev/null +++ b/core/lib_timer.c @@ -0,0 +1,411 @@ +#include "lib_timer.h" + +#include "toy_common.h" +#include "memory.h" + +#include + +//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation +int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) { + //normallize + if (x->tv_usec > 999999) { + x->tv_sec += x->tv_usec / 1000000; + x->tv_usec %= 1000000; + } + + if (y->tv_usec > 999999) { + y->tv_sec += y->tv_usec / 1000000; + y->tv_usec %= 1000000; + } + + //calc + result->tv_sec = x->tv_sec - y->tv_sec; + + if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) { + if (result->tv_sec != 0) { //only works far from 0 + result->tv_usec += 1000000; + result->tv_sec--; // borrow + } + } + + return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0); +} + +//god damn it +static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) { + struct timeval* d = ALLOCATE(struct timeval, 1); + + //I gave up, copied from SO + timeval_subtract(d, rhs, lhs); + + return d; +} + +//callbacks +static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 0) { + interpreter->errorOutput("Incorrect number of arguments to startTimer\n"); + return -1; + } + + //get the timeinfo from C + struct timeval* timeinfo = ALLOCATE(struct timeval, 1); + gettimeofday(timeinfo, NULL); + + //wrap in an opaque literal for Toy + Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1); + pushLiteralArray(&interpreter->stack, timeLiteral); + + freeLiteral(timeLiteral); + + return 1; +} + +static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n"); + return -1; + } + + //get the timeinfo from C + struct timeval timerStop; + gettimeofday(&timerStop, NULL); + + //unwrap the opaque literal + Literal timeLiteral = popLiteralArray(arguments); + + Literal timeLiteralIdn = timeLiteral; + if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) { + freeLiteral(timeLiteralIdn); + } + + if (!IS_OPAQUE(timeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n"); + freeLiteral(timeLiteral); + return -1; + } + + struct timeval* timerStart = AS_OPAQUE(timeLiteral); + + //determine the difference, and wrap it + struct timeval* d = diff(timerStart, &timerStop); + Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1); + pushLiteralArray(&interpreter->stack, diffLiteral); + + //cleanup + freeLiteral(timeLiteral); + freeLiteral(diffLiteral); + + return 1; +} + +static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments to createTimer\n"); + return -1; + } + + //get the args + Literal microsecondLiteral = popLiteralArray(arguments); + Literal secondLiteral = popLiteralArray(arguments); + + Literal secondLiteralIdn = secondLiteral; + if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) { + freeLiteral(secondLiteralIdn); + } + + Literal microsecondLiteralIdn = microsecondLiteral; + if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, µsecondLiteral)) { + freeLiteral(microsecondLiteralIdn); + } + + if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to createTimer\n"); + freeLiteral(secondLiteral); + freeLiteral(microsecondLiteral); + return -1; + } + + if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) { + interpreter->errorOutput("Microseconds out of range in createTimer\n"); + freeLiteral(secondLiteral); + freeLiteral(microsecondLiteral); + return -1; + } + + //get the timeinfo from toy + struct timeval* timeinfo = ALLOCATE(struct timeval, 1); + timeinfo->tv_sec = AS_INTEGER(secondLiteral); + timeinfo->tv_usec = AS_INTEGER(microsecondLiteral); + + //wrap in an opaque literal for Toy + Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1); + pushLiteralArray(&interpreter->stack, timeLiteral); + + freeLiteral(timeLiteral); + freeLiteral(secondLiteral); + freeLiteral(microsecondLiteral); + + return 1; +} + +static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n"); + return -1; + } + + //unwrap the opaque literal + Literal timeLiteral = popLiteralArray(arguments); + + Literal timeLiteralIdn = timeLiteral; + if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) { + freeLiteral(timeLiteralIdn); + } + + if (!IS_OPAQUE(timeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n"); + freeLiteral(timeLiteral); + return -1; + } + + struct timeval* timer = AS_OPAQUE(timeLiteral); + + //create the result literal + Literal result = TO_INTEGER_LITERAL(timer->tv_sec); + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteral(timeLiteral); + freeLiteral(result); + + return 1; +} + +static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n"); + return -1; + } + + //unwrap the opaque literal + Literal timeLiteral = popLiteralArray(arguments); + + Literal timeLiteralIdn = timeLiteral; + if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) { + freeLiteral(timeLiteralIdn); + } + + if (!IS_OPAQUE(timeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n"); + freeLiteral(timeLiteral); + return -1; + } + + struct timeval* timer = AS_OPAQUE(timeLiteral); + + //create the result literal + Literal result = TO_INTEGER_LITERAL(timer->tv_usec); + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteral(timeLiteral); + freeLiteral(result); + + return 1; +} + +static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n"); + return -1; + } + + //unwrap the opaque literals + Literal rhsLiteral = popLiteralArray(arguments); + Literal lhsLiteral = popLiteralArray(arguments); + + Literal lhsLiteralIdn = lhsLiteral; + if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) { + freeLiteral(lhsLiteralIdn); + } + + Literal rhsLiteralIdn = rhsLiteral; + if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) { + freeLiteral(rhsLiteralIdn); + } + + if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n"); + freeLiteral(lhsLiteral); + freeLiteral(rhsLiteral); + return -1; + } + + struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral); + struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral); + + //determine the difference, and wrap it + struct timeval* d = diff(lhsTimer, rhsTimer); + Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1); + pushLiteralArray(&interpreter->stack, diffLiteral); + + //cleanup + freeLiteral(lhsLiteral); + freeLiteral(rhsLiteral); + freeLiteral(diffLiteral); + + return 1; +} + +static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _timerToString\n"); + return -1; + } + + //unwrap in an opaque literal + Literal timeLiteral = popLiteralArray(arguments); + + Literal timeLiteralIdn = timeLiteral; + if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) { + freeLiteral(timeLiteralIdn); + } + + if (!IS_OPAQUE(timeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _timerToString\n"); + freeLiteral(timeLiteral); + return -1; + } + + struct timeval* timer = AS_OPAQUE(timeLiteral); + + //create the string literal + Literal resultLiteral = TO_NULL_LITERAL; + if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec + char buffer[128]; + snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec); + resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer)); + } + else { //normal case + char buffer[128]; + snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec); + resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer)); + } + + pushLiteralArray(&interpreter->stack, resultLiteral); + + //cleanup + freeLiteral(timeLiteral); + freeLiteral(resultLiteral); + + return 1; +} + +static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n"); + return -1; + } + + //unwrap in an opaque literal + Literal timeLiteral = popLiteralArray(arguments); + + Literal timeLiteralIdn = timeLiteral; + if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) { + freeLiteral(timeLiteralIdn); + } + + if (!IS_OPAQUE(timeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n"); + freeLiteral(timeLiteral); + return -1; + } + + struct timeval* timer = AS_OPAQUE(timeLiteral); + + FREE(struct timeval, timer); + + freeLiteral(timeLiteral); + + return 0; +} + +//call the hook +typedef struct Natives { + char* name; + NativeFn fn; +} Natives; + +int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) { + //build the natives list + Natives natives[] = { + {"startTimer", nativeStartTimer}, + {"_stopTimer", nativeStopTimer}, + {"createTimer", nativeCreateTimer}, + {"_getTimerSeconds", nativeGetTimerSeconds}, + {"_getTimerMicroseconds", nativeGetTimerMicroseconds}, + {"_compareTimer", nativeCompareTimer}, + {"_timerToString", nativeTimerToString}, + {"_destroyTimer", nativeDestroyTimer}, + {NULL, NULL} + }; + + //store the library in an aliased dictionary + if (!IS_NULL(alias)) { + //make sure the name isn't taken + if (isDelcaredScopeVariable(interpreter->scope, alias)) { + interpreter->errorOutput("Can't override an existing variable\n"); + freeLiteral(alias); + return false; + } + + //create the dictionary to load up with functions + LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1); + initLiteralDictionary(dictionary); + + //load the dict with functions + for (int i = 0; natives[i].name; i++) { + Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name)); + Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0); + func.type = LITERAL_FUNCTION_NATIVE; + + setLiteralDictionary(dictionary, name, func); + + freeLiteral(name); + freeLiteral(func); + } + + //build the type + Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true); + Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true); + Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true); + TYPE_PUSH_SUBTYPE(&type, strType); + TYPE_PUSH_SUBTYPE(&type, fnType); + + //set scope + Literal dict = TO_DICTIONARY_LITERAL(dictionary); + declareScopeVariable(interpreter->scope, alias, type); + setScopeVariable(interpreter->scope, alias, dict, false); + + //cleanup + freeLiteral(dict); + freeLiteral(type); + return 0; + } + + //default + for (int i = 0; natives[i].name; i++) { + injectNativeFn(interpreter, natives[i].name, natives[i].fn); + } + + return 0; +} diff --git a/core/lib_timer.h b/core/lib_timer.h new file mode 100644 index 0000000..4fa99e7 --- /dev/null +++ b/core/lib_timer.h @@ -0,0 +1,6 @@ +#pragma once + +#include "interpreter.h" + +int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias); + diff --git a/core/makefile b/core/makefile index 070de35..713c9ce 100644 --- a/core/makefile +++ b/core/makefile @@ -25,12 +25,16 @@ else exit 1 endif -library: $(OBJ) +library: libs $(OBJ) $(CC) -DCORE_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE) -L../$(LIBDIR) $(LIBS) -static: $(OBJ) +static: libs $(OBJ) ar crs $(CORE_OUTDIR)/lib$(OUTNAME).a $(OBJ) -L../$(LIBDIR) $(LIBS) +libs: + cp ../Toy/repl/lib* . + cp ../Toy/repl/repl_tools.* . + $(OBJ): | $(ODIR) $(ODIR): diff --git a/core/repl_tools.c b/core/repl_tools.c index a115525..b9bf20d 100644 --- a/core/repl_tools.c +++ b/core/repl_tools.c @@ -1,5 +1,6 @@ #include "repl_tools.h" #include "lib_standard.h" +#include "lib_timer.h" #include "console_colors.h" @@ -108,6 +109,7 @@ void runBinary(unsigned char* tb, size_t size) { //inject the libs injectNativeHook(&interpreter, "standard", hookStandard); + injectNativeHook(&interpreter, "timer", hookTimer); runInterpreter(&interpreter, tb, size); freeInterpreter(&interpreter); diff --git a/core/repl_tools.h b/core/repl_tools.h index 877f1f2..e61a1ed 100644 --- a/core/repl_tools.h +++ b/core/repl_tools.h @@ -1,6 +1,6 @@ #pragma once -#include "core_common.h" +#include "toy_common.h" char* readFile(char* path, size_t* fileSize); void writeFile(char* path, unsigned char* bytes, size_t size);