Updated Toy, added timer and runner libs
This commit is contained in:
2
Toy
2
Toy
Submodule Toy updated: 51740e2b9e...6d961cea78
@@ -4,6 +4,8 @@
|
|||||||
#include "lib_input.h"
|
#include "lib_input.h"
|
||||||
#include "lib_node.h"
|
#include "lib_node.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
|
#include "lib_timer.h"
|
||||||
|
#include "lib_runner.h"
|
||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
//define the engine object
|
//define the extern engine object
|
||||||
Engine engine;
|
Engine engine;
|
||||||
|
|
||||||
//errors here should be fatal
|
//errors here should be fatal
|
||||||
@@ -54,6 +56,8 @@ void initEngine() {
|
|||||||
injectNativeHook(&engine.interpreter, "node", hookNode);
|
injectNativeHook(&engine.interpreter, "node", hookNode);
|
||||||
injectNativeHook(&engine.interpreter, "input", hookInput);
|
injectNativeHook(&engine.interpreter, "input", hookInput);
|
||||||
injectNativeHook(&engine.interpreter, "standard", hookStandard);
|
injectNativeHook(&engine.interpreter, "standard", hookStandard);
|
||||||
|
injectNativeHook(&engine.interpreter, "timer", hookTimer);
|
||||||
|
injectNativeHook(&engine.interpreter, "runner", hookRunner);
|
||||||
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char* source = readFile("./assets/scripts/init.toy", &size);
|
char* source = readFile("./assets/scripts/init.toy", &size);
|
||||||
@@ -102,7 +106,7 @@ static void execEvents() {
|
|||||||
freeLiteralArray(&engine.keyUpEvents);
|
freeLiteralArray(&engine.keyUpEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
//poll events
|
//poll all events
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch(event.type) {
|
switch(event.type) {
|
||||||
@@ -167,10 +171,10 @@ static void execEvents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//callbacks
|
//process input events
|
||||||
if (engine.rootNode != NULL) {
|
if (engine.rootNode != NULL) {
|
||||||
//key down events
|
//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;
|
LiteralArray args;
|
||||||
initLiteralArray(&args);
|
initLiteralArray(&args);
|
||||||
pushLiteralArray(&args, engine.keyDownEvents.literals[i]);
|
pushLiteralArray(&args, engine.keyDownEvents.literals[i]);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ typedef struct _engine {
|
|||||||
LiteralDictionary symKeyUpEvents; //keysym -> event names
|
LiteralDictionary symKeyUpEvents; //keysym -> event names
|
||||||
} Engine;
|
} Engine;
|
||||||
|
|
||||||
//extern singleton
|
//extern singleton - used by various libraries
|
||||||
extern Engine engine;
|
extern Engine engine;
|
||||||
|
|
||||||
//APIs for running the engine in main()
|
//APIs for running the engine in main()
|
||||||
|
|||||||
620
core/lib_runner.c
Normal file
620
core/lib_runner.c
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
#include "lib_runner.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
#include "repl_tools.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
12
core/lib_runner.h
Normal file
12
core/lib_runner.h
Normal file
@@ -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
|
||||||
@@ -8,6 +8,8 @@ ODIR = obj
|
|||||||
SRC = $(wildcard *.c)
|
SRC = $(wildcard *.c)
|
||||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||||
|
|
||||||
|
TOYLIBS = $(wildcard ../Toy/repl/lib*) $(wildcard ../Toy/repl/repl_tools.*)
|
||||||
|
|
||||||
OUTNAME=core
|
OUTNAME=core
|
||||||
|
|
||||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
||||||
@@ -33,8 +35,7 @@ static: libs $(OBJ)
|
|||||||
|
|
||||||
#copy the stuff from Toy/repl that is needed
|
#copy the stuff from Toy/repl that is needed
|
||||||
libs:
|
libs:
|
||||||
cp ../Toy/repl/lib* .
|
cp $(TOYLIBS) .
|
||||||
cp ../Toy/repl/repl_tools.* .
|
|
||||||
|
|
||||||
$(OBJ): | $(ODIR)
|
$(OBJ): | $(ODIR)
|
||||||
|
|
||||||
@@ -46,5 +47,5 @@ $(ODIR)/%.o: %.c
|
|||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean-libs:
|
||||||
$(RM) $(ODIR)
|
$(RM) $(notdir $(TOYLIBS))
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
#include "lib_timer.h"
|
#include "lib_timer.h"
|
||||||
|
#include "lib_runner.h"
|
||||||
|
|
||||||
#include "console_colors.h"
|
#include "console_colors.h"
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ char* readFile(char* path, size_t* fileSize) {
|
|||||||
|
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
exit(-1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(file, 0L, SEEK_END);
|
fseek(file, 0L, SEEK_END);
|
||||||
@@ -29,7 +30,7 @@ char* readFile(char* path, size_t* fileSize) {
|
|||||||
|
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
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);
|
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||||
@@ -38,7 +39,7 @@ char* readFile(char* path, size_t* fileSize) {
|
|||||||
|
|
||||||
if (bytesRead < *fileSize) {
|
if (bytesRead < *fileSize) {
|
||||||
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||||
exit(-1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
@@ -46,22 +47,24 @@ char* readFile(char* path, size_t* fileSize) {
|
|||||||
return buffer;
|
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");
|
FILE* file = fopen(path, "wb");
|
||||||
|
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int written = fwrite(bytes, size, 1, file);
|
int written = fwrite(bytes, size, 1, file);
|
||||||
|
|
||||||
if (written != 1) {
|
if (written != 1) {
|
||||||
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
|
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//repl functions
|
//repl functions
|
||||||
@@ -79,7 +82,6 @@ unsigned char* compileString(char* source, size_t* size) {
|
|||||||
while(node != NULL) {
|
while(node != NULL) {
|
||||||
//pack up and leave
|
//pack up and leave
|
||||||
if (node->type == AST_NODE_ERROR) {
|
if (node->type == AST_NODE_ERROR) {
|
||||||
printf(ERROR "error node detected\n" RESET);
|
|
||||||
freeASTNode(node);
|
freeASTNode(node);
|
||||||
freeCompiler(&compiler);
|
freeCompiler(&compiler);
|
||||||
freeParser(&parser);
|
freeParser(&parser);
|
||||||
@@ -110,6 +112,7 @@ void runBinary(unsigned char* tb, size_t size) {
|
|||||||
//inject the libs
|
//inject the libs
|
||||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||||
injectNativeHook(&interpreter, "timer", hookTimer);
|
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||||
|
injectNativeHook(&interpreter, "runner", hookRunner);
|
||||||
|
|
||||||
runInterpreter(&interpreter, tb, size);
|
runInterpreter(&interpreter, tb, size);
|
||||||
freeInterpreter(&interpreter);
|
freeInterpreter(&interpreter);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
char* readFile(char* path, size_t* fileSize);
|
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);
|
unsigned char* compileString(char* source, size_t* size);
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,31 @@
|
|||||||
|
|
||||||
#include "engine.h"
|
#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[]) {
|
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();
|
initEngine();
|
||||||
execEngine();
|
execEngine();
|
||||||
freeEngine();
|
freeEngine();
|
||||||
|
|
||||||
|
//clean up the drive dictionary when you're done
|
||||||
|
freeDriveDictionary();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user