From 92c71a374d3a13e91ea28bc13dd2ddfb282798aa Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Thu, 23 Feb 2023 19:19:17 +1100 Subject: [PATCH] Implemented a basic random library --- repl/lib_random.c | 196 ++++++++++++++++++++++++++++++ repl/lib_random.h | 7 ++ repl/repl_main.c | 2 + repl/repl_tools.c | 2 + scripts/small.toy | 12 ++ test/scripts/lib/random-stuff.toy | 0 test/scripts/lib/random.toy | 17 +++ test/test_libraries.c | 58 +-------- 8 files changed, 241 insertions(+), 53 deletions(-) create mode 100644 repl/lib_random.c create mode 100644 repl/lib_random.h delete mode 100644 test/scripts/lib/random-stuff.toy create mode 100644 test/scripts/lib/random.toy diff --git a/repl/lib_random.c b/repl/lib_random.c new file mode 100644 index 0000000..6b70cc8 --- /dev/null +++ b/repl/lib_random.c @@ -0,0 +1,196 @@ +#include "lib_random.h" + +#include "toy_memory.h" + +static int hashInt(int x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; +} + +typedef struct Toy_RandomGenerator { + int seed; //mutated with each call +} Toy_RandomGenerator; + +//Toy native functions +static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n"); + return -1; + } + + //get the seed argument + Toy_Literal seedLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal seedLiteralIdn = seedLiteral; + if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) { + Toy_freeLiteral(seedLiteralIdn); + } + + if (TOY_IS_IDENTIFIER(seedLiteral)) { + Toy_freeLiteral(seedLiteral); + return -1; + } + + if (!TOY_IS_INTEGER(seedLiteral)) { + interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator"); + Toy_freeLiteral(seedLiteral); + return -1; + } + + //generate the generator object + Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1); + generator->seed = TOY_AS_INTEGER(seedLiteral); + Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM); + + //return and cleanup + Toy_pushLiteralArray(&interpreter->stack, generatorLiteral); + + Toy_freeLiteral(seedLiteral); + Toy_freeLiteral(generatorLiteral); + + return 1; +} + +static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n"); + return -1; + } + + //get the runner object + Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal generatorLiteralIdn = generatorLiteral; + if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) { + Toy_freeLiteral(generatorLiteralIdn); + } + + if (TOY_IS_IDENTIFIER(generatorLiteral)) { + Toy_freeLiteral(generatorLiteral); + return -1; + } + + if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) { + interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n"); + return -1; + } + + Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral); + + //generate the new value and package up the return + generator->seed = hashInt(generator->seed); + + Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed); + + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); + + //cleanup + Toy_freeLiteral(generatorLiteral); + Toy_freeLiteral(resultLiteral); + + return 0; +} + +static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n"); + return -1; + } + + //get the runner object + Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal generatorLiteralIdn = generatorLiteral; + if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) { + Toy_freeLiteral(generatorLiteralIdn); + } + + if (TOY_IS_IDENTIFIER(generatorLiteral)) { + Toy_freeLiteral(generatorLiteral); + return -1; + } + + if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) { + interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n"); + return -1; + } + + Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral); + + //clear out the runner object + TOY_FREE(Toy_RandomGenerator, generator); + Toy_freeLiteral(generatorLiteral); + + return 0; +} + +//call the hook +typedef struct Natives { + const char* name; + Toy_NativeFn fn; +} Natives; + +int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) { + //build the natives list + Natives natives[] = { + {"createRandomGenerator", nativeCreateRandomGenerator}, + {"generateRandomNumber", nativeGenerateRandomNumber}, + {"freeRandomGenerator", nativeFreeRandomGenerator}, + {NULL, NULL} + }; + + //store the library in an aliased dictionary + if (!TOY_IS_NULL(alias)) { + //make sure the name isn't taken + if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) { + interpreter->errorOutput("Can't override an existing variable\n"); + Toy_freeLiteral(alias); + return -1; + } + + //create the dictionary to load up with functions + Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1); + Toy_initLiteralDictionary(dictionary); + + //load the dict with functions + for (int i = 0; natives[i].name; i++) { + Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name)); + Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn); + + Toy_setLiteralDictionary(dictionary, name, func); + + Toy_freeLiteral(name); + Toy_freeLiteral(func); + } + + //build the type + Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true); + Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true); + Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true); + TOY_TYPE_PUSH_SUBTYPE(&type, strType); + TOY_TYPE_PUSH_SUBTYPE(&type, fnType); + + //set scope + Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary); + Toy_declareScopeVariable(interpreter->scope, alias, type); + Toy_setScopeVariable(interpreter->scope, alias, dict, false); + + //cleanup + Toy_freeLiteral(dict); + Toy_freeLiteral(type); + return 0; + } + + //default + for (int i = 0; natives[i].name; i++) { + Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn); + } + + return 0; +} diff --git a/repl/lib_random.h b/repl/lib_random.h new file mode 100644 index 0000000..2dfae4d --- /dev/null +++ b/repl/lib_random.h @@ -0,0 +1,7 @@ +#pragma once + +#include "toy_interpreter.h" + +int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias); + +#define TOY_OPAQUE_TAG_RANDOM 200 diff --git a/repl/repl_main.c b/repl/repl_main.c index 2f601b2..c7249a5 100644 --- a/repl/repl_main.c +++ b/repl/repl_main.c @@ -1,6 +1,7 @@ #include "repl_tools.h" #include "lib_about.h" #include "lib_standard.h" +#include "lib_random.h" #include "lib_runner.h" #include "toy_console_colors.h" @@ -29,6 +30,7 @@ void repl(const char* initialInput) { //inject the libs Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout); Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard); + Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom); Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner); for(;;) { diff --git a/repl/repl_tools.c b/repl/repl_tools.c index 0b46080..e26b861 100644 --- a/repl/repl_tools.c +++ b/repl/repl_tools.c @@ -1,6 +1,7 @@ #include "repl_tools.h" #include "lib_about.h" #include "lib_standard.h" +#include "lib_random.h" #include "lib_runner.h" #include "toy_console_colors.h" @@ -112,6 +113,7 @@ void Toy_runBinary(const unsigned char* tb, size_t size) { //inject the libs Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout); Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard); + Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom); Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner); Toy_runInterpreter(&interpreter, tb, (int)size); diff --git a/scripts/small.toy b/scripts/small.toy index e69de29..a9afb07 100644 --- a/scripts/small.toy +++ b/scripts/small.toy @@ -0,0 +1,12 @@ +import standard; +import random; + +for (var i: int = 0; i < 1_000_000; i++) { + var generator: opaque = createRandomGenerator(clock().hash()); + + print generator.generateRandomNumber(); + print generator.generateRandomNumber(); + print generator.generateRandomNumber(); + + generator.freeRandomGenerator(); +} \ No newline at end of file diff --git a/test/scripts/lib/random-stuff.toy b/test/scripts/lib/random-stuff.toy deleted file mode 100644 index e69de29..0000000 diff --git a/test/scripts/lib/random.toy b/test/scripts/lib/random.toy new file mode 100644 index 0000000..b2bf955 --- /dev/null +++ b/test/scripts/lib/random.toy @@ -0,0 +1,17 @@ +import standard; +import random; + +var generator: opaque = createRandomGenerator(clock().hash()); //create a new generator object, from a non-determinant source + +var a: int = generator.generateRandomNumber(); +var b: int = generator.generateRandomNumber(); +var c: int = generator.generateRandomNumber(); + +generator.freeRandomGenerator(); + +assert a != b, "random a != random b failed"; +assert a != c, "random a != random c failed"; +assert b != c, "random b != random c failed"; + + +print "All good"; diff --git a/test/test_libraries.c b/test/test_libraries.c index d850358..dadd25b 100644 --- a/test/test_libraries.c +++ b/test/test_libraries.c @@ -14,6 +14,7 @@ #include "../repl/repl_tools.h" #include "../repl/lib_about.h" +#include "../repl/lib_random.h" #include "../repl/lib_runner.h" #include "../repl/lib_standard.h" @@ -45,30 +46,15 @@ void runBinaryWithLibrary(const unsigned char* tb, size_t size, const char* libr Toy_setInterpreterError(&interpreter, errorWrapper); //inject the standard libraries into this interpreter + if (hook != Toy_hookStandard) { + Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard); + } Toy_injectNativeHook(&interpreter, library, hook); Toy_runInterpreter(&interpreter, tb, size); Toy_freeInterpreter(&interpreter); } -void runBinaryQuietly(const unsigned char* tb, size_t size) { - Toy_Interpreter interpreter; - Toy_initInterpreter(&interpreter); - - //NOTE: supress print output for testing - Toy_setInterpreterPrint(&interpreter, noPrintFn); - Toy_setInterpreterAssert(&interpreter, assertWrapper); - Toy_setInterpreterError(&interpreter, errorWrapper); - - //inject the libs - Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout); - Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard); - Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner); - - Toy_runInterpreter(&interpreter, tb, size); - Toy_freeInterpreter(&interpreter); -} - typedef struct Payload { char* fname; char* libname; @@ -94,6 +80,7 @@ int main() { {"about.toy", "about", Toy_hookAbout}, {"standard.toy", "standard", Toy_hookStandard}, {"runner.toy", "runner", Toy_hookRunner}, + {"random.toy", "random", Toy_hookRandom}, {NULL, NULL, NULL} }; @@ -125,41 +112,6 @@ int main() { } } - { - //run whatever, testing stuff together to check for memory leaks - char* whatever[] = { - "random-stuff.toy", - NULL - }; - - for (int i = 0; whatever[i]; i++) { - printf("Running %s\n", whatever[i]); - - char fname[128]; - snprintf(fname, 128, "scripts/lib/%s", whatever[i]); - - //compile the source - size_t size = 0; - const char* source = (const char*)Toy_readFile(fname, &size); - if (!source) { - printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname); - failedAsserts++; - continue; - } - - const unsigned char* tb = Toy_compileString(source, &size); - free((void*)source); - - if (!tb) { - printf(TOY_CC_ERROR "Failed to compile file: %s\n" TOY_CC_RESET, fname); - failedAsserts++; - continue; - } - - runBinaryQuietly(tb, size); - } - } - //lib cleanup Toy_freeDriveDictionary();