mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Finished runner library
This commit is contained in:
@@ -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,50 +123,232 @@ 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;
|
||||||
|
|
||||||
return -1;
|
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) {
|
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);
|
||||||
|
|
||||||
return -1;
|
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) {
|
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);
|
||||||
|
|
||||||
return -1;
|
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) {
|
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);
|
||||||
|
|
||||||
return -1;
|
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) {
|
static int nativeFreeScript(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
@@ -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}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2438,7 +2438,10 @@ void freeInterpreter(Interpreter* interpreter) {
|
|||||||
interpreter->scope = popScope(interpreter->scope);
|
interpreter->scope = popScope(interpreter->scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
freeLiteralDictionary(interpreter->hooks);
|
if (interpreter->hooks) {
|
||||||
FREE(LiteralDictionary, interpreter->hooks);
|
freeLiteralDictionary(interpreter->hooks);
|
||||||
|
FREE(LiteralDictionary, interpreter->hooks);
|
||||||
|
}
|
||||||
|
|
||||||
interpreter->hooks = NULL;
|
interpreter->hooks = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
9
test/scripts/lib/runner/sample_1.toy
Normal file
9
test/scripts/lib/runner/sample_1.toy
Normal 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();
|
||||||
|
|
||||||
1
test/scripts/lib/runner/sample_2.toy
Normal file
1
test/scripts/lib/runner/sample_2.toy
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert true, "Nested sample scripts worked";
|
||||||
8
test/scripts/mustfail/access-parent-directory.toy
Normal file
8
test/scripts/mustfail/access-parent-directory.toy
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import runner;
|
||||||
|
|
||||||
|
var s = loadScript("scripts:/lib/../runner_sample_code.toy");
|
||||||
|
|
||||||
|
s.runScript();
|
||||||
|
|
||||||
|
s.freeScript();
|
||||||
|
|
||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user