diff --git a/Toy b/Toy index 51740e2..6d961ce 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit 51740e2b9ecdf1dc83c177cb188e8c0d8804e493 +Subproject commit 6d961cea78d6b513eba62dec751892703f523734 diff --git a/core/engine.c b/core/engine.c index f7a6ccc..38abd70 100644 --- a/core/engine.c +++ b/core/engine.c @@ -4,6 +4,8 @@ #include "lib_input.h" #include "lib_node.h" #include "lib_standard.h" +#include "lib_timer.h" +#include "lib_runner.h" #include "repl_tools.h" #include "memory.h" @@ -20,7 +22,7 @@ #include #include -//define the engine object +//define the extern engine object Engine engine; //errors here should be fatal @@ -54,6 +56,8 @@ void initEngine() { injectNativeHook(&engine.interpreter, "node", hookNode); injectNativeHook(&engine.interpreter, "input", hookInput); injectNativeHook(&engine.interpreter, "standard", hookStandard); + injectNativeHook(&engine.interpreter, "timer", hookTimer); + injectNativeHook(&engine.interpreter, "runner", hookRunner); size_t size = 0; char* source = readFile("./assets/scripts/init.toy", &size); @@ -102,7 +106,7 @@ static void execEvents() { freeLiteralArray(&engine.keyUpEvents); } - //poll events + //poll all events SDL_Event event; while (SDL_PollEvent(&event)) { switch(event.type) { @@ -167,10 +171,10 @@ static void execEvents() { } } - //callbacks + //process input events if (engine.rootNode != NULL) { //key down events - for (int i = 0; i < engine.keyDownEvents.count; i++) { + for (int i = 0; i < engine.keyDownEvents.count; i++) { //TODO: could pass in the whole array? LiteralArray args; initLiteralArray(&args); pushLiteralArray(&args, engine.keyDownEvents.literals[i]); diff --git a/core/engine.h b/core/engine.h index 15e0235..134582b 100644 --- a/core/engine.h +++ b/core/engine.h @@ -36,7 +36,7 @@ typedef struct _engine { LiteralDictionary symKeyUpEvents; //keysym -> event names } Engine; -//extern singleton +//extern singleton - used by various libraries extern Engine engine; //APIs for running the engine in main() diff --git a/core/lib_runner.c b/core/lib_runner.c new file mode 100644 index 0000000..e25ea7b --- /dev/null +++ b/core/lib_runner.c @@ -0,0 +1,620 @@ +#include "lib_runner.h" + +#include "memory.h" +#include "interpreter.h" + +#include "repl_tools.h" + +#include +#include + +typedef struct Runner { + Interpreter interpreter; + unsigned char* bytecode; + size_t size; + + bool dirty; +} Runner; + +//Toy native functions +static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) { + //arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to loadScript\n"); + return -1; + } + + //get the argument + Literal drivePathLiteral = popLiteralArray(arguments); + RefString* drivePath = copyRefString(AS_STRING(drivePathLiteral)); + + //get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library + int driveLength = 0; + while (toCString(drivePath)[driveLength] != ':') { + if (driveLength >= lengthRefString(drivePath)) { + interpreter->errorOutput("Incorrect drive path format given to loadScript\n"); + deleteRefString(drivePath); + freeLiteral(drivePathLiteral); + return -1; + } + + driveLength++; + } + + RefString* drive = createRefStringLength(toCString(drivePath), driveLength); + RefString* path = createRefStringLength( &toCString(drivePath)[driveLength + 1], lengthRefString(drivePath) - driveLength ); + + //get the real drive file path + Literal driveLiteral = TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString + Literal realDriveLiteral = getLiteralDictionary(getDriveDictionary(), driveLiteral); + + if (!IS_STRING(realDriveLiteral)) { + interpreter->errorOutput("Incorrect literal type found for drive: "); + printLiteralCustom(realDriveLiteral, interpreter->errorOutput); + interpreter->errorOutput("\n"); + freeLiteral(realDriveLiteral); + freeLiteral(driveLiteral); + deleteRefString(path); + deleteRefString(drivePath); + freeLiteral(drivePathLiteral); + return -1; + } + + //get the final real file path (concat) TODO: move this concat to refstring library + RefString* realDrive = copyRefString(AS_STRING(realDriveLiteral)); + int realLength = lengthRefString(realDrive) + lengthRefString(path); + + char* filePath = ALLOCATE(char, realLength + 1); //+1 for null + snprintf(filePath, realLength, "%s%s", toCString(realDrive), toCString(path)); + + //clean up the drivepath stuff + deleteRefString(realDrive); + freeLiteral(realDriveLiteral); + freeLiteral(driveLiteral); + deleteRefString(path); + deleteRefString(drivePath); + freeLiteral(drivePathLiteral); + + //check for file extensions + if (!(filePath[realLength - 5] == '.' && filePath[realLength - 4] == 't' && filePath[realLength - 3] == 'o' && filePath[realLength - 2] == 'y')) { + interpreter->errorOutput("Bad script file extension (expected .toy)\n"); + FREE_ARRAY(char, filePath, realLength); + return -1; + } + + //check for break-out attempts + for (int i = 0; i < realLength - 1; i++) { + if (filePath[i] == '.' && filePath[i + 1] == '.') { + interpreter->errorOutput("Parent directory access not allowed\n"); + FREE_ARRAY(char, filePath, realLength); + return -1; + } + } + + //load and compile the bytecode + size_t fileSize = 0; + char* source = readFile(filePath, &fileSize); + + if (!source) { + interpreter->errorOutput("Failed to load source file\n"); + return -1; + } + + unsigned char* bytecode = compileString(source, &fileSize); + free((void*)source); + + if (!bytecode) { + interpreter->errorOutput("Failed to compile source file\n"); + return -1; + } + + //build the runner object + Runner* runner = ALLOCATE(Runner, 1); + setInterpreterPrint(&runner->interpreter, interpreter->printOutput); + setInterpreterAssert(&runner->interpreter, interpreter->assertOutput); + setInterpreterError(&runner->interpreter, interpreter->errorOutput); + runner->interpreter.hooks = interpreter->hooks; + runner->interpreter.scope = NULL; + resetInterpreter(&runner->interpreter); + runner->bytecode = bytecode; + runner->size = fileSize; + runner->dirty = false; + + //build the opaque object, and push it to the stack + Literal runnerLiteral = TO_OPAQUE_LITERAL(runner, OPAQUE_TAG_RUNNER); + pushLiteralArray(&interpreter->stack, runnerLiteral); + + FREE_ARRAY(char, filePath, realLength); + + return 1; +} + +static int nativeLoadScriptBytecode(Interpreter* interpreter, LiteralArray* arguments) { + //arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to loadScriptBytecode\n"); + return -1; + } + + //get the argument + Literal drivePathLiteral = popLiteralArray(arguments); + RefString* drivePath = copyRefString(AS_STRING(drivePathLiteral)); + + //get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library + int driveLength = 0; + while (toCString(drivePath)[driveLength] != ':') { + if (driveLength >= lengthRefString(drivePath)) { + interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n"); + deleteRefString(drivePath); + freeLiteral(drivePathLiteral); + return -1; + } + + driveLength++; + } + + RefString* drive = createRefStringLength(toCString(drivePath), driveLength); + RefString* path = createRefStringLength( &toCString(drivePath)[driveLength + 1], lengthRefString(drivePath) - driveLength ); + + //get the real drive file path + Literal driveLiteral = TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString + Literal realDriveLiteral = getLiteralDictionary(getDriveDictionary(), driveLiteral); + + if (!IS_STRING(realDriveLiteral)) { + interpreter->errorOutput("Incorrect literal type found for drive: "); + printLiteralCustom(realDriveLiteral, interpreter->errorOutput); + interpreter->errorOutput("\n"); + freeLiteral(realDriveLiteral); + freeLiteral(driveLiteral); + deleteRefString(path); + deleteRefString(drivePath); + freeLiteral(drivePathLiteral); + return -1; + } + + //get the final real file path (concat) TODO: move this concat to refstring library + RefString* realDrive = copyRefString(AS_STRING(realDriveLiteral)); + int realLength = lengthRefString(realDrive) + lengthRefString(path); + + char* filePath = ALLOCATE(char, realLength + 1); //+1 for null + snprintf(filePath, realLength, "%s%s", toCString(realDrive), toCString(path)); + + //clean up the drivepath stuff + deleteRefString(realDrive); + freeLiteral(realDriveLiteral); + freeLiteral(driveLiteral); + deleteRefString(path); + deleteRefString(drivePath); + freeLiteral(drivePathLiteral); + + //check for file extensions + if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) { + interpreter->errorOutput("Bad binary file extension (expected .tb)\n"); + FREE_ARRAY(char, filePath, realLength); + return -1; + } + + //check for break-out attempts + for (int i = 0; i < realLength - 1; i++) { + if (filePath[i] == '.' && filePath[i + 1] == '.') { + interpreter->errorOutput("Parent directory access not allowed\n"); + FREE_ARRAY(char, filePath, realLength); + return -1; + } + } + + //load the bytecode + size_t fileSize = 0; + unsigned char* bytecode = (unsigned char*)readFile(filePath, &fileSize); + + if (!bytecode) { + interpreter->errorOutput("Failed to load bytecode file\n"); + return -1; + } + + //build the runner object + Runner* runner = ALLOCATE(Runner, 1); + setInterpreterPrint(&runner->interpreter, interpreter->printOutput); + setInterpreterAssert(&runner->interpreter, interpreter->assertOutput); + setInterpreterError(&runner->interpreter, interpreter->errorOutput); + runner->interpreter.hooks = interpreter->hooks; + runner->interpreter.scope = NULL; + resetInterpreter(&runner->interpreter); + runner->bytecode = bytecode; + runner->size = fileSize; + runner->dirty = false; + + //build the opaque object, and push it to the stack + Literal runnerLiteral = TO_OPAQUE_LITERAL(runner, OPAQUE_TAG_RUNNER); + pushLiteralArray(&interpreter->stack, runnerLiteral); + + FREE_ARRAY(char, filePath, realLength); + + return 1; +} + +static int nativeRunScript(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _runScript\n"); + return -1; + } + + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); + Literal idn = runnerLiteral; + + if (parseIdentifierToValue(interpreter, &runnerLiteral)) { + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //run + if (runner->dirty) { + interpreter->errorOutput("Can't re-run a dirty script (try resetting it first)\n"); + freeLiteral(runnerLiteral); + return -1; + } + + unsigned char* bytecodeCopy = ALLOCATE(unsigned char, runner->size); + memcpy(bytecodeCopy, runner->bytecode, runner->size); //need a COPY of the bytecode, because the interpreter eats it + + runInterpreter(&runner->interpreter, bytecodeCopy, runner->size); + runner->dirty = true; + + //cleanup + freeLiteral(runnerLiteral); + + return 0; +} + +static int nativeGetScriptVar(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n"); + return -1; + } + + //get the runner object + Literal varName = popLiteralArray(arguments); + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(varName)) { + Literal idn = varName; + parseIdentifierToValue(interpreter, &varName); + freeLiteral(idn); + } + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //dirty check + if (!runner->dirty) { + interpreter->errorOutput("Can't access variable from a non-dirty script (try running it first)\n"); + freeLiteral(runnerLiteral); + return -1; + } + + //get the desired variable + Literal varIdn = TO_IDENTIFIER_LITERAL(copyRefString(AS_STRING(varName))); + Literal result = TO_NULL_LITERAL; + getScopeVariable(runner->interpreter.scope, varIdn, &result); + + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteral(result); + freeLiteral(varIdn); + freeLiteral(varName); + freeLiteral(runnerLiteral); + + return 1; +} + +static int nativeCallScriptFn(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count < 2) { + interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n"); + return -1; + } + + //get the rest args + LiteralArray tmp; + initLiteralArray(&tmp); + + while (arguments->count > 2) { + Literal lit = popLiteralArray(arguments); + pushLiteralArray(&tmp, lit); + freeLiteral(lit); + } + + LiteralArray rest; + initLiteralArray(&rest); + + while (tmp.count) { //correct the order of the rest args + Literal lit = popLiteralArray(&tmp); + pushLiteralArray(&rest, lit); + freeLiteral(lit); + } + + freeLiteralArray(&tmp); + + + //get the runner object + Literal varName = popLiteralArray(arguments); + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(varName)) { + Literal idn = varName; + parseIdentifierToValue(interpreter, &varName); + freeLiteral(idn); + } + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //dirty check + if (!runner->dirty) { + interpreter->errorOutput("Can't access fn from a non-dirty script (try running it first)\n"); + freeLiteral(runnerLiteral); + freeLiteralArray(&rest); + return -1; + } + + //get the desired variable + Literal varIdn = TO_IDENTIFIER_LITERAL(copyRefString(AS_STRING(varName))); + Literal fn = TO_NULL_LITERAL; + getScopeVariable(runner->interpreter.scope, varIdn, &fn); + + if (!IS_FUNCTION(fn)) { + interpreter->errorOutput("Can't run a non-function literal\n"); + freeLiteral(fn); + freeLiteral(varIdn); + freeLiteral(varName); + freeLiteral(runnerLiteral); + freeLiteralArray(&rest); + } + + //call + LiteralArray resultArray; + initLiteralArray(&resultArray); + + callLiteralFn(interpreter, fn, &rest, &resultArray); + + Literal result = TO_NULL_LITERAL; + if (resultArray.count > 0) { + result = popLiteralArray(&resultArray); + } + + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteralArray(&resultArray); + freeLiteral(result); + freeLiteral(fn); + freeLiteral(varIdn); + freeLiteral(varName); + freeLiteral(runnerLiteral); + freeLiteralArray(&rest); + + return 1; +} + +static int nativeResetScript(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _resetScript\n"); + return -1; + } + + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //reset + if (!runner->dirty) { + interpreter->errorOutput("Can't reset a non-dirty script (try running it first)\n"); + freeLiteral(runnerLiteral); + return -1; + } + + resetInterpreter(&runner->interpreter); + runner->dirty = false; + freeLiteral(runnerLiteral); + + return 0; +} + +static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _freeScript\n"); + return -1; + } + + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _freeScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //clear out the runner object + runner->interpreter.hooks = NULL; + freeInterpreter(&runner->interpreter); + FREE_ARRAY(unsigned char, runner->bytecode, runner->size); + + FREE(Runner, runner); + + freeLiteral(runnerLiteral); + + return 0; +} + +static int nativeCheckScriptDirty(Interpreter* interpreter, LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _runScript\n"); + return -1; + } + + //get the runner object + Literal runnerLiteral = popLiteralArray(arguments); + + if (IS_IDENTIFIER(runnerLiteral)) { + Literal idn = runnerLiteral; + parseIdentifierToValue(interpreter, &runnerLiteral); + freeLiteral(idn); + } + + if (OPAQUE_TAG(runnerLiteral) != OPAQUE_TAG_RUNNER) { + interpreter->errorOutput("Unrecognized opaque literal in _runScript\n"); + return -1; + } + + Runner* runner = AS_OPAQUE(runnerLiteral); + + //run + Literal result = TO_BOOLEAN_LITERAL(runner->dirty); + + pushLiteralArray(&interpreter->stack, result); + + //cleanup + freeLiteral(result); + freeLiteral(runnerLiteral); + + return 0; +} + +//call the hook +typedef struct Natives { + char* name; + NativeFn fn; +} Natives; + +int hookRunner(Interpreter* interpreter, Literal identifier, Literal alias) { + //build the natives list + Natives natives[] = { + {"loadScript", nativeLoadScript}, + {"loadScriptBytecode", nativeLoadScriptBytecode}, + {"_runScript", nativeRunScript}, + {"_getScriptVar", nativeGetScriptVar}, + {"_callScriptFn", nativeCallScriptFn}, + {"_resetScript", nativeResetScript}, + {"_freeScript", nativeFreeScript}, + {"_checkScriptDirty", nativeCheckScriptDirty}, + {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(createRefString(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; +} + +//file system API +static LiteralDictionary driveDictionary; + +void initDriveDictionary() { + initLiteralDictionary(&driveDictionary); +} + +void freeDriveDictionary() { + freeLiteralDictionary(&driveDictionary); +} + +LiteralDictionary* getDriveDictionary() { + return &driveDictionary; +} \ No newline at end of file diff --git a/core/lib_runner.h b/core/lib_runner.h new file mode 100644 index 0000000..e7d2c13 --- /dev/null +++ b/core/lib_runner.h @@ -0,0 +1,12 @@ +#pragma once + +#include "interpreter.h" + +int hookRunner(Interpreter* interpreter, Literal identifier, Literal alias); + +//file system API - these need to be set by the host +void initDriveDictionary(); +void freeDriveDictionary(); +LiteralDictionary* getDriveDictionary(); + +#define OPAQUE_TAG_RUNNER 100 diff --git a/core/makefile b/core/makefile index 63955e1..eecf6e7 100644 --- a/core/makefile +++ b/core/makefile @@ -8,6 +8,8 @@ ODIR = obj SRC = $(wildcard *.c) OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o)) +TOYLIBS = $(wildcard ../Toy/repl/lib*) $(wildcard ../Toy/repl/repl_tools.*) + OUTNAME=core ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN) @@ -33,8 +35,7 @@ static: libs $(OBJ) #copy the stuff from Toy/repl that is needed libs: - cp ../Toy/repl/lib* . - cp ../Toy/repl/repl_tools.* . + cp $(TOYLIBS) . $(OBJ): | $(ODIR) @@ -46,5 +47,5 @@ $(ODIR)/%.o: %.c .PHONY: clean -clean: - $(RM) $(ODIR) +clean-libs: + $(RM) $(notdir $(TOYLIBS)) \ No newline at end of file diff --git a/core/repl_tools.c b/core/repl_tools.c index df3fd2c..17b12ff 100644 --- a/core/repl_tools.c +++ b/core/repl_tools.c @@ -1,6 +1,7 @@ #include "repl_tools.h" #include "lib_standard.h" #include "lib_timer.h" +#include "lib_runner.h" #include "console_colors.h" @@ -18,7 +19,7 @@ char* readFile(char* path, size_t* fileSize) { if (file == NULL) { fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path); - exit(-1); + return NULL; } fseek(file, 0L, SEEK_END); @@ -29,7 +30,7 @@ char* readFile(char* path, size_t* fileSize) { if (buffer == NULL) { fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path); - exit(-1); + return NULL; } size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file); @@ -38,7 +39,7 @@ char* readFile(char* path, size_t* fileSize) { if (bytesRead < *fileSize) { fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path); - exit(-1); + return NULL; } fclose(file); @@ -46,22 +47,24 @@ char* readFile(char* path, size_t* fileSize) { return buffer; } -void writeFile(char* path, unsigned char* bytes, size_t size) { +int writeFile(char* path, unsigned char* bytes, size_t size) { FILE* file = fopen(path, "wb"); if (file == NULL) { fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path); - exit(-1); + return -1; } int written = fwrite(bytes, size, 1, file); if (written != 1) { fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path); - exit(-1); + return -1; } fclose(file); + + return 0; } //repl functions @@ -79,7 +82,6 @@ unsigned char* compileString(char* source, size_t* size) { while(node != NULL) { //pack up and leave if (node->type == AST_NODE_ERROR) { - printf(ERROR "error node detected\n" RESET); freeASTNode(node); freeCompiler(&compiler); freeParser(&parser); @@ -110,6 +112,7 @@ void runBinary(unsigned char* tb, size_t size) { //inject the libs injectNativeHook(&interpreter, "standard", hookStandard); injectNativeHook(&interpreter, "timer", hookTimer); + injectNativeHook(&interpreter, "runner", hookRunner); runInterpreter(&interpreter, tb, size); freeInterpreter(&interpreter); diff --git a/core/repl_tools.h b/core/repl_tools.h index e61a1ed..289abd1 100644 --- a/core/repl_tools.h +++ b/core/repl_tools.h @@ -3,7 +3,7 @@ #include "toy_common.h" char* readFile(char* path, size_t* fileSize); -void writeFile(char* path, unsigned char* bytes, size_t size); +int writeFile(char* path, unsigned char* bytes, size_t size); unsigned char* compileString(char* source, size_t* size); diff --git a/source/main.c b/source/main.c index c83c183..7680310 100644 --- a/source/main.c +++ b/source/main.c @@ -3,9 +3,31 @@ #include "engine.h" +//the runner library needs a little more setup since it accesses the disk +#include "lib_runner.h" + int main(int argc, char* argv[]) { + //the drive system uses a LiteralDictionary, which must be initialized with this + initDriveDictionary(); + + //create a pair of literals, the first for the drive name, the second for the path + Literal driveLiteral = TO_STRING_LITERAL(createRefString("scripts")); + Literal pathLiteral = TO_STRING_LITERAL(createRefString("assets/scripts")); + + //set these within the drive dictionary + setLiteralDictionary(getDriveDictionary(), driveLiteral, pathLiteral); + + //these literals are no longer needed + freeLiteral(driveLiteral); + freeLiteral(pathLiteral); + + //run the rest of your program initEngine(); execEngine(); freeEngine(); + + //clean up the drive dictionary when you're done + freeDriveDictionary(); + return 0; }