Finished runner library

This commit is contained in:
2023-01-20 23:03:00 +00:00
parent c47ee68b3f
commit 69f5151310
7 changed files with 334 additions and 23 deletions

View File

@@ -13,7 +13,7 @@ typedef struct Runner {
unsigned char* bytecode; unsigned char* bytecode;
size_t size; size_t size;
// bool dirty;
} Runner; } Runner;
//Toy native functions //Toy native functions
@@ -75,6 +75,15 @@ static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) {
deleteRefString(drivePath); deleteRefString(drivePath);
freeLiteral(drivePathLiteral); freeLiteral(drivePathLiteral);
//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 //load and compile the bytecode
size_t fileSize = 0; size_t fileSize = 0;
char* source = readFile(filePath, &fileSize); char* source = readFile(filePath, &fileSize);
@@ -95,8 +104,13 @@ static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) {
//build the runner object //build the runner object
Runner* runner = ALLOCATE(Runner, 1); Runner* runner = ALLOCATE(Runner, 1);
initInterpreter(&runner->interpreter); initInterpreter(&runner->interpreter);
setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
setInterpreterError(&runner->interpreter, interpreter->errorOutput);
runner->interpreter.hooks = interpreter->hooks;
runner->bytecode = bytecode; runner->bytecode = bytecode;
runner->size = fileSize; runner->size = fileSize;
runner->dirty = false;
//build the opaque object, and push it to the stack //build the opaque object, and push it to the stack
Literal runnerLiteral = TO_OPAQUE_LITERAL(runner, OPAQUE_TAG_RUNNER); Literal runnerLiteral = TO_OPAQUE_LITERAL(runner, OPAQUE_TAG_RUNNER);
@@ -109,52 +123,234 @@ static int nativeLoadScript(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeRunScript(Interpreter* interpreter, LiteralArray* arguments) { static int nativeRunScript(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments //no arguments
if (arguments->count != 0) { if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _runScript\n"); interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
return -1; 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; 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) { static int nativeGetScriptVar(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments //no arguments
if (arguments->count != 0) { if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n"); interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n");
return -1; 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; 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) { static int nativeCallScriptFn(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments //no arguments
if (arguments->count != 0) { if (arguments->count < 2) {
interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n"); interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n");
return -1; 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; 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) { static int nativeResetScript(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments //no arguments
if (arguments->count != 0) { if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _resetScript\n"); interpreter->errorOutput("Incorrect number of arguments to _resetScript\n");
return -1; 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; 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) { static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments //no arguments
if (arguments->count != 1) { if (arguments->count != 1) {
@@ -179,6 +375,7 @@ static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) {
Runner* runner = AS_OPAQUE(runnerLiteral); Runner* runner = AS_OPAQUE(runnerLiteral);
//clear out the runner object //clear out the runner object
runner->interpreter.hooks = NULL;
freeInterpreter(&runner->interpreter); freeInterpreter(&runner->interpreter);
FREE_ARRAY(unsigned char, runner->bytecode, runner->size); FREE_ARRAY(unsigned char, runner->bytecode, runner->size);
@@ -189,6 +386,41 @@ static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) {
return 0; 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 //call the hook
typedef struct Natives { typedef struct Natives {
char* name; char* name;
@@ -199,11 +431,12 @@ int hookRunner(Interpreter* interpreter, Literal identifier, Literal alias) {
//build the natives list //build the natives list
Natives natives[] = { Natives natives[] = {
{"loadScript", nativeLoadScript}, {"loadScript", nativeLoadScript},
{"x_runScript", nativeRunScript}, {"_runScript", nativeRunScript},
{"x_getScriptVar", nativeGetScriptVar}, {"_getScriptVar", nativeGetScriptVar},
{"x_callScriptFn", nativeCallScriptFn}, {"_callScriptFn", nativeCallScriptFn},
{"x_resetScript", nativeResetScript}, {"_resetScript", nativeResetScript},
{"_freeScript", nativeFreeScript}, {"_freeScript", nativeFreeScript},
{"_checkScriptDirty", nativeCheckScriptDirty},
{NULL, NULL} {NULL, NULL}
}; };

View File

@@ -2438,7 +2438,10 @@ void freeInterpreter(Interpreter* interpreter) {
interpreter->scope = popScope(interpreter->scope); interpreter->scope = popScope(interpreter->scope);
} }
if (interpreter->hooks) {
freeLiteralDictionary(interpreter->hooks); freeLiteralDictionary(interpreter->hooks);
FREE(LiteralDictionary, interpreter->hooks); FREE(LiteralDictionary, interpreter->hooks);
}
interpreter->hooks = NULL; interpreter->hooks = NULL;
} }

View File

@@ -7,10 +7,66 @@ import runner;
s.freeScript(); s.freeScript();
} }
//TODO: test running an external script //test running an external script
//TODO: test resetting an external script {
//TODO: test retrieving a script variable var s = loadScript("scripts:/runner_sample_code.toy");
//TODO: test calling a script function
s.runScript();
s.freeScript();
}
//test resetting an external script
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
s.resetScript();
assert !s.checkScriptDirty(), "checkScriptDirty failed";
s.runScript();
assert s.checkScriptDirty(), "_checkScriptDirty() failed";
s.resetScript();
s.runScript();
s.freeScript();
}
//test running a nested external script
{
var s = loadScript("scripts:/lib/runner/sample_1.toy");
s.runScript();
s.freeScript();
}
//test retrieving a script variable
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
var fib = s.getScriptVar("fib");
assert fib(12) == 144, "_getScriptVar() failed";
s.freeScript();
}
//test calling a script function
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
assert s.callScriptFn("fib", 12) == 144, "_callScriptFn() failed";
s.freeScript();
}
print "All good"; print "All good";

View File

@@ -0,0 +1,9 @@
import runner;
//delegate to the other sample script
var s = loadScript("scripts:/lib/runner/sample_2.toy");
s.runScript();
s.freeScript();

View File

@@ -0,0 +1 @@
assert true, "Nested sample scripts worked";

View File

@@ -0,0 +1,8 @@
import runner;
var s = loadScript("scripts:/lib/../runner_sample_code.toy");
s.runScript();
s.freeScript();

View File

@@ -95,6 +95,7 @@ int main() {
{ {
//run each file in tests/scripts/ //run each file in tests/scripts/
char* filenames[] = { char* filenames[] = {
"access-parent-directory.toy",
"declare-types-array.toy", "declare-types-array.toy",
"declare-types-dictionary-key.toy", "declare-types-dictionary-key.toy",
"declare-types-dictionary-value.toy", "declare-types-dictionary-value.toy",