mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Reordered some functions for clarity, caught a bug or two
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
//printing utilities
|
||||||
static void printWrapper(const char* output) {
|
static void printWrapper(const char* output) {
|
||||||
//allow for disabling of newlines in the repl
|
//allow for disabling of newlines in the repl
|
||||||
#ifndef TOY_EXPORT
|
#ifndef TOY_EXPORT
|
||||||
@@ -33,6 +34,19 @@ static void errorWrapper(const char* output) {
|
|||||||
fprintf(stderr, TOY_CC_ERROR "%s" TOY_CC_RESET, output); //no newline
|
fprintf(stderr, TOY_CC_ERROR "%s" TOY_CC_RESET, output); //no newline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput) {
|
||||||
|
interpreter->printOutput = printOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput) {
|
||||||
|
interpreter->assertOutput = assertOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput) {
|
||||||
|
interpreter->errorOutput = errorOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
//injection utilities
|
||||||
bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func) {
|
bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func) {
|
||||||
//reject reserved words
|
//reject reserved words
|
||||||
if (Toy_findTypeByKeyword(name) != TOY_TOKEN_EOF) {
|
if (Toy_findTypeByKeyword(name) != TOY_TOKEN_EOF) {
|
||||||
@@ -42,19 +56,21 @@ bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_Nati
|
|||||||
|
|
||||||
Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(name));
|
Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(name));
|
||||||
|
|
||||||
//make sure the name isn't taken
|
//make sure the name isn't taken (manually to skip the scope chain)
|
||||||
if (Toy_existsLiteralDictionary(&interpreter->scope->variables, identifier)) {
|
if (Toy_existsLiteralDictionary(&interpreter->scope->variables, identifier)) {
|
||||||
interpreter->errorOutput("Can't override an existing variable\n");
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
Toy_freeLiteral(identifier);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal fn = TOY_TO_FUNCTION_NATIVE_LITERAL(func);
|
Toy_Literal fn = TOY_TO_FUNCTION_NATIVE_LITERAL(func);
|
||||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(fn.type, true);
|
Toy_Literal type = TOY_TO_TYPE_LITERAL(fn.type, true);
|
||||||
|
|
||||||
Toy_setLiteralDictionary(&interpreter->scope->variables, identifier, fn);
|
Toy_declareScopeVariable(interpreter->scope, identifier, type);
|
||||||
Toy_setLiteralDictionary(&interpreter->scope->types, identifier, type);
|
Toy_setScopeVariable(interpreter->scope, identifier, fn, false);
|
||||||
|
|
||||||
Toy_freeLiteral(identifier);
|
Toy_freeLiteral(identifier);
|
||||||
|
Toy_freeLiteral(fn);
|
||||||
Toy_freeLiteral(type);
|
Toy_freeLiteral(type);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -67,12 +83,12 @@ bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_Ho
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int identifierLength = strlen(name);
|
Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(name));
|
||||||
Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefStringLength(name, identifierLength));
|
|
||||||
|
|
||||||
//make sure the name isn't taken
|
//make sure the name isn't taken
|
||||||
if (Toy_existsLiteralDictionary(interpreter->hooks, identifier)) {
|
if (Toy_existsLiteralDictionary(interpreter->hooks, identifier)) {
|
||||||
interpreter->errorOutput("Can't override an existing hook\n");
|
interpreter->errorOutput("Can't override an existing hook\n");
|
||||||
|
Toy_freeLiteral(identifier);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +96,27 @@ bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_Ho
|
|||||||
Toy_setLiteralDictionary(interpreter->hooks, identifier, fn);
|
Toy_setLiteralDictionary(interpreter->hooks, identifier, fn);
|
||||||
|
|
||||||
Toy_freeLiteral(identifier);
|
Toy_freeLiteral(identifier);
|
||||||
|
Toy_freeLiteral(fn);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_parseCompoundToPureValues(Toy_Interpreter* interpreter, Toy_Literal* literalPtr) {
|
//parsing to value utilities
|
||||||
|
bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr) {
|
||||||
|
//this converts identifiers to values
|
||||||
|
if (TOY_IS_IDENTIFIER(*literalPtr)) {
|
||||||
|
if (!Toy_getScopeVariable(interpreter->scope, *literalPtr, literalPtr)) {
|
||||||
|
interpreter->errorOutput("Undeclared variable ");
|
||||||
|
Toy_printLiteralCustom(*literalPtr, interpreter->errorOutput);
|
||||||
|
interpreter->errorOutput("\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_parseCompoundToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr) {
|
||||||
//parse out an array
|
//parse out an array
|
||||||
if (TOY_IS_ARRAY(*literalPtr)) {
|
if (TOY_IS_ARRAY(*literalPtr)) {
|
||||||
for (int i = 0; i < TOY_AS_ARRAY(*literalPtr)->count; i++) {
|
for (int i = 0; i < TOY_AS_ARRAY(*literalPtr)->count; i++) {
|
||||||
@@ -105,7 +137,7 @@ void Toy_parseCompoundToPureValues(Toy_Interpreter* interpreter, Toy_Literal* li
|
|||||||
|
|
||||||
//recurse on sub-compounds
|
//recurse on sub-compounds
|
||||||
if (TOY_IS_ARRAY(TOY_AS_ARRAY(*literalPtr)->literals[i]) || TOY_IS_DICTIONARY(TOY_AS_ARRAY(*literalPtr)->literals[i])) {
|
if (TOY_IS_ARRAY(TOY_AS_ARRAY(*literalPtr)->literals[i]) || TOY_IS_DICTIONARY(TOY_AS_ARRAY(*literalPtr)->literals[i])) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &TOY_AS_ARRAY(*literalPtr)->literals[i]);
|
Toy_parseCompoundToValue(interpreter, &TOY_AS_ARRAY(*literalPtr)->literals[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,11 +154,11 @@ void Toy_parseCompoundToPureValues(Toy_Interpreter* interpreter, Toy_Literal* li
|
|||||||
|
|
||||||
//recurse on sub-compounds
|
//recurse on sub-compounds
|
||||||
if (TOY_IS_ARRAY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].key) || TOY_IS_DICTIONARY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].key)) {
|
if (TOY_IS_ARRAY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].key) || TOY_IS_DICTIONARY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].key)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &TOY_AS_DICTIONARY(*literalPtr)->entries[i].key);
|
Toy_parseCompoundToValue(interpreter, &TOY_AS_DICTIONARY(*literalPtr)->entries[i].key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_IS_ARRAY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].value) || TOY_IS_DICTIONARY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].value)) {
|
if (TOY_IS_ARRAY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].value) || TOY_IS_DICTIONARY(TOY_AS_DICTIONARY(*literalPtr)->entries[i].value)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &TOY_AS_DICTIONARY(*literalPtr)->entries[i].value);
|
Toy_parseCompoundToValue(interpreter, &TOY_AS_DICTIONARY(*literalPtr)->entries[i].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,14 +191,14 @@ void Toy_parseCompoundToPureValues(Toy_Interpreter* interpreter, Toy_Literal* li
|
|||||||
Toy_parseIdentifierToValue(interpreter, &key);
|
Toy_parseIdentifierToValue(interpreter, &key);
|
||||||
}
|
}
|
||||||
if (TOY_IS_ARRAY(key) || TOY_IS_DICTIONARY(key)) {
|
if (TOY_IS_ARRAY(key) || TOY_IS_DICTIONARY(key)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &key);
|
Toy_parseCompoundToValue(interpreter, &key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_IS_IDENTIFIER(value)) {
|
if (TOY_IS_IDENTIFIER(value)) {
|
||||||
Toy_parseIdentifierToValue(interpreter, &value);
|
Toy_parseIdentifierToValue(interpreter, &value);
|
||||||
}
|
}
|
||||||
if (TOY_IS_ARRAY(value) || TOY_IS_DICTIONARY(value)) {
|
if (TOY_IS_ARRAY(value) || TOY_IS_DICTIONARY(value)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &value);
|
Toy_parseCompoundToValue(interpreter, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_setLiteralDictionary(ret, key, value);
|
Toy_setLiteralDictionary(ret, key, value);
|
||||||
@@ -181,38 +213,29 @@ void Toy_parseCompoundToPureValues(Toy_Interpreter* interpreter, Toy_Literal* li
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr) {
|
void parseTypeToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr) {
|
||||||
//this converts identifiers to values
|
//if an identifier is embedded in the type, figure out what it is
|
||||||
if (TOY_IS_IDENTIFIER(*literalPtr)) {
|
Toy_Literal idn = *literalPtr;
|
||||||
if (!Toy_getScopeVariable(interpreter->scope, *literalPtr, literalPtr)) {
|
if (TOY_IS_IDENTIFIER(*literalPtr) && Toy_parseIdentifierToValue(interpreter, literalPtr)) {
|
||||||
interpreter->errorOutput("Undeclared variable ");
|
Toy_freeLiteral(idn);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if this is an array or dictionary, continue to the subtypes
|
||||||
|
if (TOY_IS_TYPE(*literalPtr) && (TOY_AS_TYPE(*literalPtr).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(*literalPtr).typeOf == TOY_LITERAL_DICTIONARY)) {
|
||||||
|
for (int i = 0; i < TOY_AS_TYPE(*literalPtr).count; i++) {
|
||||||
|
parseTypeToValue(interpreter, &((Toy_Literal*)(TOY_AS_TYPE(*literalPtr).subtypes))[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//BUGFIX: make sure it actually is a type
|
||||||
|
if (!TOY_IS_TYPE(*literalPtr)) {
|
||||||
|
interpreter->errorOutput("Bad type encountered: ");
|
||||||
Toy_printLiteralCustom(*literalPtr, interpreter->errorOutput);
|
Toy_printLiteralCustom(*literalPtr, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// if (TOY_IS_ARRAY(*literalPtr) || TOY_IS_DICTIONARY(*literalPtr)) {
|
|
||||||
// Toy_parseCompoundToPureValues(interpreter, literalPtr);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//utilities for the host program
|
//reading utils
|
||||||
void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput) {
|
|
||||||
interpreter->printOutput = printOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput) {
|
|
||||||
interpreter->assertOutput = assertOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput) {
|
|
||||||
interpreter->errorOutput = errorOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
//utils
|
|
||||||
static unsigned char readByte(const unsigned char* tb, int* count) {
|
static unsigned char readByte(const unsigned char* tb, int* count) {
|
||||||
unsigned char ret = *(unsigned char*)(tb + *count);
|
unsigned char ret = *(unsigned char*)(tb + *count);
|
||||||
*count += 1;
|
*count += 1;
|
||||||
@@ -332,7 +355,8 @@ static bool execPushLiteral(Toy_Interpreter* interpreter, bool lng) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool rawLiteral(Toy_Interpreter* interpreter) {
|
static bool execRawLiteral(Toy_Interpreter* interpreter) {
|
||||||
|
//this seems to be only used by increments and decrements
|
||||||
Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack);
|
Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack);
|
||||||
|
|
||||||
Toy_Literal idn = lit;
|
Toy_Literal idn = lit;
|
||||||
@@ -544,31 +568,6 @@ static bool execArithmetic(Toy_Interpreter* interpreter, Toy_Opcode opcode) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Literal parseTypeToValue(Toy_Interpreter* interpreter, Toy_Literal type) {
|
|
||||||
//if an identifier is embedded in the type, figure out what it iss
|
|
||||||
Toy_Literal typeIdn = type;
|
|
||||||
if (TOY_IS_IDENTIFIER(type) && Toy_parseIdentifierToValue(interpreter, &type)) {
|
|
||||||
Toy_freeLiteral(typeIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if this is an array or dictionary, continue to the subtypes
|
|
||||||
if (TOY_IS_TYPE(type) && (TOY_AS_TYPE(type).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(type).typeOf == TOY_LITERAL_DICTIONARY)) {
|
|
||||||
for (int i = 0; i < TOY_AS_TYPE(type).count; i++) {
|
|
||||||
((Toy_Literal*)(TOY_AS_TYPE(type).subtypes))[i] = parseTypeToValue(interpreter, ((Toy_Literal*)(TOY_AS_TYPE(type).subtypes))[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//BUGFIX: make sure it actually is a type
|
|
||||||
if (!TOY_IS_TYPE(type)) {
|
|
||||||
interpreter->errorOutput("Bad type encountered: ");
|
|
||||||
Toy_printLiteralCustom(type, interpreter->errorOutput);
|
|
||||||
interpreter->errorOutput("\n");
|
|
||||||
//TODO: would be better to return an int here...
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) {
|
static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) {
|
||||||
//read the index in the cache
|
//read the index in the cache
|
||||||
int identifierIndex = 0;
|
int identifierIndex = 0;
|
||||||
@@ -592,7 +591,7 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: because identifiers are getting embedded in type definitions
|
//BUGFIX: because identifiers are getting embedded in type definitions
|
||||||
type = parseTypeToValue(interpreter, type);
|
parseTypeToValue(interpreter, &type);
|
||||||
|
|
||||||
if (!Toy_declareScopeVariable(interpreter->scope, identifier, type)) {
|
if (!Toy_declareScopeVariable(interpreter->scope, identifier, type)) {
|
||||||
interpreter->errorOutput("Can't redefine the variable \"");
|
interpreter->errorOutput("Can't redefine the variable \"");
|
||||||
@@ -609,7 +608,7 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_IS_ARRAY(val) || TOY_IS_DICTIONARY(val)) {
|
if (TOY_IS_ARRAY(val) || TOY_IS_DICTIONARY(val)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &val);
|
Toy_parseCompoundToValue(interpreter, &val);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: could restrict opaque data to only opaque variables
|
//TODO: could restrict opaque data to only opaque variables
|
||||||
@@ -689,7 +688,7 @@ static bool execVarAssign(Toy_Interpreter* interpreter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_IS_ARRAY(rhs) || TOY_IS_DICTIONARY(rhs)) {
|
if (TOY_IS_ARRAY(rhs) || TOY_IS_DICTIONARY(rhs)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &rhs);
|
Toy_parseCompoundToValue(interpreter, &rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_IDENTIFIER(lhs)) {
|
if (!TOY_IS_IDENTIFIER(lhs)) {
|
||||||
@@ -733,11 +732,11 @@ static bool execVarAssign(Toy_Interpreter* interpreter) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool execVarArithmeticAssign(Toy_Interpreter* interpreter) {
|
static bool execVarArithmeticAssignInterjection(Toy_Interpreter* interpreter) {
|
||||||
Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack);
|
Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack);
|
||||||
Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack);
|
Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack);
|
||||||
|
|
||||||
//duplicate the name
|
//duplicate the (presumably) identifier
|
||||||
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
||||||
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
||||||
Toy_pushLiteralArray(&interpreter->stack, rhs);
|
Toy_pushLiteralArray(&interpreter->stack, rhs);
|
||||||
@@ -757,7 +756,10 @@ static bool execValCast(Toy_Interpreter* interpreter) {
|
|||||||
Toy_freeLiteral(valueIdn);
|
Toy_freeLiteral(valueIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
Toy_Literal typeIdn = type;
|
||||||
|
if (TOY_IS_IDENTIFIER(type) && Toy_parseIdentifierToValue(interpreter, &type)) {
|
||||||
|
Toy_freeLiteral(typeIdn);
|
||||||
|
}
|
||||||
|
|
||||||
if (TOY_IS_NULL(value)) {
|
if (TOY_IS_NULL(value)) {
|
||||||
interpreter->errorOutput("Can't cast a null value\n");
|
interpreter->errorOutput("Can't cast a null value\n");
|
||||||
@@ -768,6 +770,8 @@ static bool execValCast(Toy_Interpreter* interpreter) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||||
|
|
||||||
//cast the rhs to the type represented by lhs
|
//cast the rhs to the type represented by lhs
|
||||||
switch(TOY_AS_TYPE(type).typeOf) {
|
switch(TOY_AS_TYPE(type).typeOf) {
|
||||||
case TOY_LITERAL_BOOLEAN:
|
case TOY_LITERAL_BOOLEAN:
|
||||||
@@ -845,6 +849,11 @@ static bool execValCast(Toy_Interpreter* interpreter) {
|
|||||||
interpreter->errorOutput("Unknown cast type found: ");
|
interpreter->errorOutput("Unknown cast type found: ");
|
||||||
Toy_printLiteralCustom(type, interpreter->errorOutput);
|
Toy_printLiteralCustom(type, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
|
Toy_freeLiteral(value);
|
||||||
|
Toy_freeLiteral(type);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,14 +900,16 @@ static bool execCompareEqual(Toy_Interpreter* interpreter, bool invert) {
|
|||||||
Toy_freeLiteral(lhsIdn);
|
Toy_freeLiteral(lhsIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result = Toy_literalsAreEqual(lhs, rhs);
|
bool equal = Toy_literalsAreEqual(lhs, rhs);
|
||||||
|
|
||||||
if (invert) {
|
if (invert) {
|
||||||
result = !result;
|
equal = !equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(result));
|
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(equal);
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
Toy_freeLiteral(lhs);
|
Toy_freeLiteral(lhs);
|
||||||
Toy_freeLiteral(rhs);
|
Toy_freeLiteral(rhs);
|
||||||
|
|
||||||
@@ -948,17 +959,19 @@ static bool execCompareLess(Toy_Interpreter* interpreter, bool invert) {
|
|||||||
rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs));
|
rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result;
|
bool less;
|
||||||
|
|
||||||
if (!invert) {
|
if (!invert) {
|
||||||
result = (TOY_AS_FLOAT(lhs) < TOY_AS_FLOAT(rhs));
|
less = (TOY_AS_FLOAT(lhs) < TOY_AS_FLOAT(rhs));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = (TOY_AS_FLOAT(lhs) > TOY_AS_FLOAT(rhs));
|
less = (TOY_AS_FLOAT(lhs) > TOY_AS_FLOAT(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(result));
|
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(less);
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
Toy_freeLiteral(lhs);
|
Toy_freeLiteral(lhs);
|
||||||
Toy_freeLiteral(rhs);
|
Toy_freeLiteral(rhs);
|
||||||
|
|
||||||
@@ -1009,17 +1022,19 @@ static bool execCompareLessEqual(Toy_Interpreter* interpreter, bool invert) {
|
|||||||
rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs));
|
rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result;
|
bool lessEqual;
|
||||||
|
|
||||||
if (!invert) {
|
if (!invert) {
|
||||||
result = (TOY_AS_FLOAT(lhs) < TOY_AS_FLOAT(rhs)) || Toy_literalsAreEqual(lhs, rhs);
|
lessEqual = (TOY_AS_FLOAT(lhs) < TOY_AS_FLOAT(rhs)) || Toy_literalsAreEqual(lhs, rhs);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = (TOY_AS_FLOAT(lhs) > TOY_AS_FLOAT(rhs)) || Toy_literalsAreEqual(lhs, rhs);
|
lessEqual = (TOY_AS_FLOAT(lhs) > TOY_AS_FLOAT(rhs)) || Toy_literalsAreEqual(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(result));
|
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(lessEqual);
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
Toy_freeLiteral(lhs);
|
Toy_freeLiteral(lhs);
|
||||||
Toy_freeLiteral(rhs);
|
Toy_freeLiteral(rhs);
|
||||||
|
|
||||||
@@ -1040,7 +1055,7 @@ static bool execAnd(Toy_Interpreter* interpreter) {
|
|||||||
Toy_freeLiteral(lhsIdn);
|
Toy_freeLiteral(lhsIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
//short-circuit support
|
//short-circuit - broken, see issue #73
|
||||||
if (!TOY_IS_TRUTHY(lhs)) {
|
if (!TOY_IS_TRUTHY(lhs)) {
|
||||||
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
||||||
}
|
}
|
||||||
@@ -1068,7 +1083,7 @@ static bool execOr(Toy_Interpreter* interpreter) {
|
|||||||
Toy_freeLiteral(lhsIdn);
|
Toy_freeLiteral(lhsIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
//short-circuit support
|
//short-circuit - broken, see issue #73
|
||||||
if (TOY_IS_TRUTHY(lhs)) {
|
if (TOY_IS_TRUTHY(lhs)) {
|
||||||
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
Toy_pushLiteralArray(&interpreter->stack, lhs);
|
||||||
}
|
}
|
||||||
@@ -1096,7 +1111,7 @@ static bool execJump(Toy_Interpreter* interpreter) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool execFalseJump(Toy_Interpreter* interpreter) {
|
static bool execJumpIfFalse(Toy_Interpreter* interpreter) {
|
||||||
int target = (int)readShort(interpreter->bytecode, &interpreter->count);
|
int target = (int)readShort(interpreter->bytecode, &interpreter->count);
|
||||||
|
|
||||||
if (target + interpreter->codeStart > interpreter->length) {
|
if (target + interpreter->codeStart > interpreter->length) {
|
||||||
@@ -1127,10 +1142,6 @@ static bool execFalseJump(Toy_Interpreter* interpreter) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//forward declare
|
|
||||||
static void execInterpreter(Toy_Interpreter*);
|
|
||||||
static void readInterpreterSections(Toy_Interpreter* interpreter);
|
|
||||||
|
|
||||||
//expect stack: identifier, arg1, arg2, arg3..., stackSize
|
//expect stack: identifier, arg1, arg2, arg3..., stackSize
|
||||||
//also supports identifier & arg1 to be other way around (looseFirstArgument)
|
//also supports identifier & arg1 to be other way around (looseFirstArgument)
|
||||||
static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) {
|
static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) {
|
||||||
@@ -1213,275 +1224,6 @@ static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
//expects arguments in correct order
|
|
||||||
bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns) {
|
|
||||||
//check for side-loaded native functions
|
|
||||||
if (TOY_IS_FUNCTION_NATIVE(func)) {
|
|
||||||
//TODO: parse out identifier values, see issue #64
|
|
||||||
|
|
||||||
//call the native function
|
|
||||||
int returnsCount = TOY_AS_FUNCTION_NATIVE(func)(interpreter, arguments);
|
|
||||||
|
|
||||||
if (returnsCount < 0) {
|
|
||||||
// interpreter->errorOutput("Unknown error from native function\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the results
|
|
||||||
Toy_LiteralArray returnsFromInner;
|
|
||||||
Toy_initLiteralArray(&returnsFromInner);
|
|
||||||
|
|
||||||
for (int i = 0; i < (returnsCount || 1); i++) {
|
|
||||||
Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack);
|
|
||||||
Toy_pushLiteralArray(&returnsFromInner, lit); //NOTE: also reverses the order
|
|
||||||
Toy_freeLiteral(lit);
|
|
||||||
}
|
|
||||||
|
|
||||||
//flip them around and pass to returns
|
|
||||||
while (returnsFromInner.count > 0) {
|
|
||||||
Toy_Literal lit = Toy_popLiteralArray(&returnsFromInner);
|
|
||||||
Toy_pushLiteralArray(returns, lit);
|
|
||||||
Toy_freeLiteral(lit);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_freeLiteralArray(&returnsFromInner);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//normal Toy function
|
|
||||||
if (!TOY_IS_FUNCTION(func)) {
|
|
||||||
interpreter->errorOutput("Function literal required in Toy_callLiteralFn()\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//set up a new interpreter
|
|
||||||
Toy_Interpreter inner;
|
|
||||||
|
|
||||||
//init the inner interpreter manually
|
|
||||||
Toy_initLiteralArray(&inner.literalCache);
|
|
||||||
inner.scope = Toy_pushScope(func.as.function.scope);
|
|
||||||
inner.bytecode = ((Toy_RefFunction*)(TOY_AS_FUNCTION(func).inner.ptr))->data;
|
|
||||||
inner.length = ((Toy_RefFunction*)(TOY_AS_FUNCTION(func).inner.ptr))->length;
|
|
||||||
inner.count = 0;
|
|
||||||
inner.codeStart = -1;
|
|
||||||
inner.depth = interpreter->depth + 1;
|
|
||||||
inner.panic = false;
|
|
||||||
Toy_initLiteralArray(&inner.stack);
|
|
||||||
inner.hooks = interpreter->hooks;
|
|
||||||
Toy_setInterpreterPrint(&inner, interpreter->printOutput);
|
|
||||||
Toy_setInterpreterAssert(&inner, interpreter->assertOutput);
|
|
||||||
Toy_setInterpreterError(&inner, interpreter->errorOutput);
|
|
||||||
|
|
||||||
//prep the sections
|
|
||||||
readInterpreterSections(&inner);
|
|
||||||
|
|
||||||
//prep the arguments
|
|
||||||
Toy_LiteralArray* paramArray = TOY_AS_ARRAY(inner.literalCache.literals[ readShort(inner.bytecode, &inner.count) ]);
|
|
||||||
Toy_LiteralArray* returnArray = TOY_AS_ARRAY(inner.literalCache.literals[ readShort(inner.bytecode, &inner.count) ]);
|
|
||||||
|
|
||||||
//get the rest param, if it exists
|
|
||||||
Toy_Literal restParam = TOY_TO_NULL_LITERAL;
|
|
||||||
if (paramArray->count >= 2 && TOY_AS_TYPE(paramArray->literals[ paramArray->count -1 ]).typeOf == TOY_LITERAL_FUNCTION_ARG_REST) {
|
|
||||||
restParam = paramArray->literals[ paramArray->count -2 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
//check the param total is correct
|
|
||||||
if ((TOY_IS_NULL(restParam) && paramArray->count != arguments->count * 2) || (!TOY_IS_NULL(restParam) && paramArray->count -2 > arguments->count * 2)) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments passed to a function\n");
|
|
||||||
|
|
||||||
//free, and skip out
|
|
||||||
Toy_popScope(inner.scope);
|
|
||||||
|
|
||||||
Toy_freeLiteralArray(&inner.stack);
|
|
||||||
Toy_freeLiteralArray(&inner.literalCache);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BUGFIX: access the arguments from the beginning
|
|
||||||
int argumentIndex = 0;
|
|
||||||
|
|
||||||
//contents is the indexes of identifier & type
|
|
||||||
for (int i = 0; i < paramArray->count - (TOY_IS_NULL(restParam) ? 0 : 2); i += 2) { //don't count the rest parameter, if present
|
|
||||||
//declare and define each entry in the scope
|
|
||||||
if (!Toy_declareScopeVariable(inner.scope, paramArray->literals[i], paramArray->literals[i + 1])) {
|
|
||||||
interpreter->errorOutput("[internal] Could not re-declare parameter\n");
|
|
||||||
|
|
||||||
//free, and skip out
|
|
||||||
Toy_popScope(inner.scope);
|
|
||||||
|
|
||||||
Toy_freeLiteralArray(&inner.stack);
|
|
||||||
Toy_freeLiteralArray(&inner.literalCache);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//access the arguments in order
|
|
||||||
Toy_Literal arg = TOY_TO_NULL_LITERAL;
|
|
||||||
if (argumentIndex < arguments->count) {
|
|
||||||
arg = Toy_copyLiteral(arguments->literals[argumentIndex++]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Literal argIdn = arg;
|
|
||||||
if (TOY_IS_IDENTIFIER(arg) && Toy_parseIdentifierToValue(interpreter, &arg)) {
|
|
||||||
Toy_freeLiteral(argIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(inner.scope, paramArray->literals[i], arg, false)) {
|
|
||||||
interpreter->errorOutput("[internal] Could not define parameter (bad type?)\n");
|
|
||||||
|
|
||||||
//free, and skip out
|
|
||||||
Toy_freeLiteral(arg);
|
|
||||||
Toy_popScope(inner.scope);
|
|
||||||
|
|
||||||
Toy_freeLiteralArray(&inner.stack);
|
|
||||||
Toy_freeLiteralArray(&inner.literalCache);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Toy_freeLiteral(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if using rest, pack the optional extra arguments into the rest parameter (array)
|
|
||||||
if (!TOY_IS_NULL(restParam)) {
|
|
||||||
Toy_LiteralArray rest;
|
|
||||||
Toy_initLiteralArray(&rest);
|
|
||||||
|
|
||||||
//access the arguments in order
|
|
||||||
while (argumentIndex < arguments->count) {
|
|
||||||
Toy_Literal lit = Toy_copyLiteral(arguments->literals[argumentIndex++]);
|
|
||||||
Toy_pushLiteralArray(&rest, lit);
|
|
||||||
Toy_freeLiteral(lit);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Literal restType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ARRAY, true);
|
|
||||||
Toy_Literal any = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, false);
|
|
||||||
TOY_TYPE_PUSH_SUBTYPE(&restType, any);
|
|
||||||
|
|
||||||
//declare & define the rest parameter
|
|
||||||
if (!Toy_declareScopeVariable(inner.scope, restParam, restType)) {
|
|
||||||
interpreter->errorOutput("[internal] Could not declare rest parameter\n");
|
|
||||||
|
|
||||||
//free, and skip out
|
|
||||||
Toy_freeLiteral(restType);
|
|
||||||
Toy_freeLiteralArray(&rest);
|
|
||||||
Toy_popScope(inner.scope);
|
|
||||||
|
|
||||||
Toy_freeLiteralArray(&inner.stack);
|
|
||||||
Toy_freeLiteralArray(&inner.literalCache);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Literal lit = TOY_TO_ARRAY_LITERAL(&rest);
|
|
||||||
if (!Toy_setScopeVariable(inner.scope, restParam, lit, false)) {
|
|
||||||
interpreter->errorOutput("[internal] Could not define rest parameter\n");
|
|
||||||
|
|
||||||
//free, and skip out
|
|
||||||
Toy_freeLiteral(restType);
|
|
||||||
Toy_freeLiteral(lit);
|
|
||||||
Toy_popScope(inner.scope);
|
|
||||||
|
|
||||||
Toy_freeLiteralArray(&inner.stack);
|
|
||||||
Toy_freeLiteralArray(&inner.literalCache);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_freeLiteral(restType);
|
|
||||||
Toy_freeLiteralArray(&rest);
|
|
||||||
}
|
|
||||||
|
|
||||||
//execute the interpreter
|
|
||||||
execInterpreter(&inner);
|
|
||||||
|
|
||||||
//adopt the panic state
|
|
||||||
interpreter->panic = inner.panic;
|
|
||||||
|
|
||||||
//accept the stack as the results
|
|
||||||
Toy_LiteralArray returnsFromInner;
|
|
||||||
Toy_initLiteralArray(&returnsFromInner);
|
|
||||||
|
|
||||||
//unpack the results
|
|
||||||
for (int i = 0; i < (returnArray->count || 1); i++) {
|
|
||||||
Toy_Literal lit = Toy_popLiteralArray(&inner.stack);
|
|
||||||
Toy_pushLiteralArray(&returnsFromInner, lit); //NOTE: also reverses the order
|
|
||||||
Toy_freeLiteral(lit);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool returnValue = true;
|
|
||||||
|
|
||||||
//TODO: remove this when multiple assignment is enabled - note the BUGFIX that balances the stack
|
|
||||||
if (returnsFromInner.count > 1) {
|
|
||||||
interpreter->errorOutput("Too many values returned (multiple returns not yet supported)\n");
|
|
||||||
|
|
||||||
returnValue = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < returnsFromInner.count && returnValue; i++) {
|
|
||||||
Toy_Literal ret = Toy_popLiteralArray(&returnsFromInner);
|
|
||||||
|
|
||||||
//check the return types
|
|
||||||
if (returnArray->count > 0 && TOY_AS_TYPE(returnArray->literals[i]).typeOf != ret.type) {
|
|
||||||
interpreter->errorOutput("Bad type found in return value\n");
|
|
||||||
|
|
||||||
//free, and skip out
|
|
||||||
returnValue = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_pushLiteralArray(returns, ret); //NOTE: reverses again
|
|
||||||
Toy_freeLiteral(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
//manual free
|
|
||||||
//BUGFIX: handle scopes of functions, which refer to the parent scope (leaking memory)
|
|
||||||
while(inner.scope != TOY_AS_FUNCTION(func).scope) {
|
|
||||||
for (int i = 0; i < inner.scope->variables.capacity; i++) {
|
|
||||||
//handle keys, just in case
|
|
||||||
if (TOY_IS_FUNCTION(inner.scope->variables.entries[i].key)) {
|
|
||||||
Toy_popScope(TOY_AS_FUNCTION(inner.scope->variables.entries[i].key).scope);
|
|
||||||
TOY_AS_FUNCTION(inner.scope->variables.entries[i].key).scope = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TOY_IS_FUNCTION(inner.scope->variables.entries[i].value)) {
|
|
||||||
Toy_popScope(TOY_AS_FUNCTION(inner.scope->variables.entries[i].value).scope);
|
|
||||||
TOY_AS_FUNCTION(inner.scope->variables.entries[i].value).scope = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner.scope = Toy_popScope(inner.scope);
|
|
||||||
}
|
|
||||||
Toy_freeLiteralArray(&returnsFromInner);
|
|
||||||
Toy_freeLiteralArray(&inner.stack);
|
|
||||||
Toy_freeLiteralArray(&inner.literalCache);
|
|
||||||
|
|
||||||
//BUGFIX: this function needs to eat the arguments
|
|
||||||
Toy_freeLiteralArray(arguments);
|
|
||||||
|
|
||||||
//actual bytecode persists until next call
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns) {
|
|
||||||
Toy_Literal key = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefStringLength(name, strlen(name)));
|
|
||||||
Toy_Literal val = TOY_TO_NULL_LITERAL;
|
|
||||||
|
|
||||||
if (!Toy_isDeclaredScopeVariable(interpreter->scope, key)) {
|
|
||||||
interpreter->errorOutput("No function with that name\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_getScopeVariable(interpreter->scope, key, &val);
|
|
||||||
|
|
||||||
bool ret = Toy_callLiteralFn(interpreter, val, arguments, returns);
|
|
||||||
|
|
||||||
Toy_freeLiteral(key);
|
|
||||||
Toy_freeLiteral(val);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool execFnReturn(Toy_Interpreter* interpreter) {
|
static bool execFnReturn(Toy_Interpreter* interpreter) {
|
||||||
Toy_LiteralArray returns;
|
Toy_LiteralArray returns;
|
||||||
Toy_initLiteralArray(&returns);
|
Toy_initLiteralArray(&returns);
|
||||||
@@ -1496,7 +1238,7 @@ static bool execFnReturn(Toy_Interpreter* interpreter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_IS_ARRAY(lit) || TOY_IS_DICTIONARY(lit)) {
|
if (TOY_IS_ARRAY(lit) || TOY_IS_DICTIONARY(lit)) {
|
||||||
Toy_parseCompoundToPureValues(interpreter, &lit);
|
Toy_parseCompoundToValue(interpreter, &lit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_pushLiteralArray(&returns, lit); //reverses the order
|
Toy_pushLiteralArray(&returns, lit); //reverses the order
|
||||||
@@ -1639,7 +1381,13 @@ static bool execIndex(Toy_Interpreter* interpreter, bool assignIntermediate) {
|
|||||||
static bool execIndexAssign(Toy_Interpreter* interpreter, int assignDepth) {
|
static bool execIndexAssign(Toy_Interpreter* interpreter, int assignDepth) {
|
||||||
//assume -> compound, first, second, third, assign are all on the stack
|
//assume -> compound, first, second, third, assign are all on the stack
|
||||||
|
|
||||||
Toy_Literal assign = TOY_TO_NULL_LITERAL, third = TOY_TO_NULL_LITERAL, second = TOY_TO_NULL_LITERAL, first = TOY_TO_NULL_LITERAL, compound = TOY_TO_NULL_LITERAL, result = TOY_TO_NULL_LITERAL;
|
Toy_Literal assign = TOY_TO_NULL_LITERAL;
|
||||||
|
Toy_Literal third = TOY_TO_NULL_LITERAL;
|
||||||
|
Toy_Literal second = TOY_TO_NULL_LITERAL;
|
||||||
|
Toy_Literal first = TOY_TO_NULL_LITERAL;
|
||||||
|
Toy_Literal compound = TOY_TO_NULL_LITERAL;
|
||||||
|
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||||
|
|
||||||
Toy_Literal compoundIdn = TOY_TO_NULL_LITERAL;
|
Toy_Literal compoundIdn = TOY_TO_NULL_LITERAL;
|
||||||
bool freeIdn = false;
|
bool freeIdn = false;
|
||||||
|
|
||||||
@@ -1840,7 +1588,7 @@ static void execInterpreter(Toy_Interpreter* interpreter) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_OP_LITERAL_RAW:
|
case TOY_OP_LITERAL_RAW:
|
||||||
if (!rawLiteral(interpreter)) {
|
if (!execRawLiteral(interpreter)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1866,11 +1614,13 @@ static void execInterpreter(Toy_Interpreter* interpreter) {
|
|||||||
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
|
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
|
||||||
case TOY_OP_VAR_DIVISION_ASSIGN:
|
case TOY_OP_VAR_DIVISION_ASSIGN:
|
||||||
case TOY_OP_VAR_MODULO_ASSIGN:
|
case TOY_OP_VAR_MODULO_ASSIGN:
|
||||||
execVarArithmeticAssign(interpreter);
|
execVarArithmeticAssignInterjection(interpreter); //hang on, let me just prep this first
|
||||||
|
|
||||||
if (!execArithmetic(interpreter, opcode)) {
|
if (!execArithmetic(interpreter, opcode)) {
|
||||||
Toy_freeLiteral(Toy_popLiteralArray(&interpreter->stack));
|
Toy_freeLiteral(Toy_popLiteralArray(&interpreter->stack)); //remove the extra identifier if this went south
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!execVarAssign(interpreter)) {
|
if (!execVarAssign(interpreter)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1881,6 +1631,7 @@ static void execInterpreter(Toy_Interpreter* interpreter) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_OP_GROUPING_END:
|
case TOY_OP_GROUPING_END:
|
||||||
|
//And so doeth, yeet thine operation upwards
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//scope
|
//scope
|
||||||
@@ -1987,7 +1738,7 @@ static void execInterpreter(Toy_Interpreter* interpreter) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_OP_IF_FALSE_JUMP:
|
case TOY_OP_IF_FALSE_JUMP:
|
||||||
if (!execFalseJump(interpreter)) {
|
if (!execJumpIfFalse(interpreter)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2456,3 +2207,272 @@ void Toy_freeInterpreter(Toy_Interpreter* interpreter) {
|
|||||||
|
|
||||||
interpreter->hooks = NULL;
|
interpreter->hooks = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//for function calls
|
||||||
|
bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns) {
|
||||||
|
//check for side-loaded native functions
|
||||||
|
if (TOY_IS_FUNCTION_NATIVE(func)) {
|
||||||
|
//TODO: parse out identifier values, see issue #64
|
||||||
|
|
||||||
|
//call the native function
|
||||||
|
int returnsCount = TOY_AS_FUNCTION_NATIVE(func)(interpreter, arguments);
|
||||||
|
|
||||||
|
if (returnsCount < 0) {
|
||||||
|
// interpreter->errorOutput("Unknown error from native function\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the results
|
||||||
|
Toy_LiteralArray returnsFromInner;
|
||||||
|
Toy_initLiteralArray(&returnsFromInner);
|
||||||
|
|
||||||
|
for (int i = 0; i < (returnsCount || 1); i++) {
|
||||||
|
Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack);
|
||||||
|
Toy_pushLiteralArray(&returnsFromInner, lit); //NOTE: also reverses the order
|
||||||
|
Toy_freeLiteral(lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
//flip them around and pass to returns
|
||||||
|
while (returnsFromInner.count > 0) {
|
||||||
|
Toy_Literal lit = Toy_popLiteralArray(&returnsFromInner);
|
||||||
|
Toy_pushLiteralArray(returns, lit);
|
||||||
|
Toy_freeLiteral(lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&returnsFromInner);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//normal Toy function
|
||||||
|
if (!TOY_IS_FUNCTION(func)) {
|
||||||
|
interpreter->errorOutput("Function literal required in Toy_callLiteralFn()\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set up a new interpreter
|
||||||
|
Toy_Interpreter inner;
|
||||||
|
|
||||||
|
//init the inner interpreter manually
|
||||||
|
Toy_initLiteralArray(&inner.literalCache);
|
||||||
|
inner.scope = Toy_pushScope(func.as.function.scope);
|
||||||
|
inner.bytecode = ((Toy_RefFunction*)(TOY_AS_FUNCTION(func).inner.ptr))->data;
|
||||||
|
inner.length = ((Toy_RefFunction*)(TOY_AS_FUNCTION(func).inner.ptr))->length;
|
||||||
|
inner.count = 0;
|
||||||
|
inner.codeStart = -1;
|
||||||
|
inner.depth = interpreter->depth + 1;
|
||||||
|
inner.panic = false;
|
||||||
|
Toy_initLiteralArray(&inner.stack);
|
||||||
|
inner.hooks = interpreter->hooks;
|
||||||
|
Toy_setInterpreterPrint(&inner, interpreter->printOutput);
|
||||||
|
Toy_setInterpreterAssert(&inner, interpreter->assertOutput);
|
||||||
|
Toy_setInterpreterError(&inner, interpreter->errorOutput);
|
||||||
|
|
||||||
|
//prep the sections
|
||||||
|
readInterpreterSections(&inner);
|
||||||
|
|
||||||
|
//prep the arguments
|
||||||
|
Toy_LiteralArray* paramArray = TOY_AS_ARRAY(inner.literalCache.literals[ readShort(inner.bytecode, &inner.count) ]);
|
||||||
|
Toy_LiteralArray* returnArray = TOY_AS_ARRAY(inner.literalCache.literals[ readShort(inner.bytecode, &inner.count) ]);
|
||||||
|
|
||||||
|
//get the rest param, if it exists
|
||||||
|
Toy_Literal restParam = TOY_TO_NULL_LITERAL;
|
||||||
|
if (paramArray->count >= 2 && TOY_AS_TYPE(paramArray->literals[ paramArray->count -1 ]).typeOf == TOY_LITERAL_FUNCTION_ARG_REST) {
|
||||||
|
restParam = paramArray->literals[ paramArray->count -2 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
//check the param total is correct
|
||||||
|
if ((TOY_IS_NULL(restParam) && paramArray->count != arguments->count * 2) || (!TOY_IS_NULL(restParam) && paramArray->count -2 > arguments->count * 2)) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments passed to a function\n");
|
||||||
|
|
||||||
|
//free, and skip out
|
||||||
|
Toy_popScope(inner.scope);
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&inner.stack);
|
||||||
|
Toy_freeLiteralArray(&inner.literalCache);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//BUGFIX: access the arguments from the beginning
|
||||||
|
int argumentIndex = 0;
|
||||||
|
|
||||||
|
//contents is the indexes of identifier & type
|
||||||
|
for (int i = 0; i < paramArray->count - (TOY_IS_NULL(restParam) ? 0 : 2); i += 2) { //don't count the rest parameter, if present
|
||||||
|
//declare and define each entry in the scope
|
||||||
|
if (!Toy_declareScopeVariable(inner.scope, paramArray->literals[i], paramArray->literals[i + 1])) {
|
||||||
|
interpreter->errorOutput("[internal] Could not re-declare parameter\n");
|
||||||
|
|
||||||
|
//free, and skip out
|
||||||
|
Toy_popScope(inner.scope);
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&inner.stack);
|
||||||
|
Toy_freeLiteralArray(&inner.literalCache);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//access the arguments in order
|
||||||
|
Toy_Literal arg = TOY_TO_NULL_LITERAL;
|
||||||
|
if (argumentIndex < arguments->count) {
|
||||||
|
arg = Toy_copyLiteral(arguments->literals[argumentIndex++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal argIdn = arg;
|
||||||
|
if (TOY_IS_IDENTIFIER(arg) && Toy_parseIdentifierToValue(interpreter, &arg)) {
|
||||||
|
Toy_freeLiteral(argIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Toy_setScopeVariable(inner.scope, paramArray->literals[i], arg, false)) {
|
||||||
|
interpreter->errorOutput("[internal] Could not define parameter (bad type?)\n");
|
||||||
|
|
||||||
|
//free, and skip out
|
||||||
|
Toy_freeLiteral(arg);
|
||||||
|
Toy_popScope(inner.scope);
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&inner.stack);
|
||||||
|
Toy_freeLiteralArray(&inner.literalCache);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Toy_freeLiteral(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if using rest, pack the optional extra arguments into the rest parameter (array)
|
||||||
|
if (!TOY_IS_NULL(restParam)) {
|
||||||
|
Toy_LiteralArray rest;
|
||||||
|
Toy_initLiteralArray(&rest);
|
||||||
|
|
||||||
|
//access the arguments in order
|
||||||
|
while (argumentIndex < arguments->count) {
|
||||||
|
Toy_Literal lit = Toy_copyLiteral(arguments->literals[argumentIndex++]);
|
||||||
|
Toy_pushLiteralArray(&rest, lit);
|
||||||
|
Toy_freeLiteral(lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal restType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ARRAY, true);
|
||||||
|
Toy_Literal any = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, false);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&restType, any);
|
||||||
|
|
||||||
|
//declare & define the rest parameter
|
||||||
|
if (!Toy_declareScopeVariable(inner.scope, restParam, restType)) {
|
||||||
|
interpreter->errorOutput("[internal] Could not declare rest parameter\n");
|
||||||
|
|
||||||
|
//free, and skip out
|
||||||
|
Toy_freeLiteral(restType);
|
||||||
|
Toy_freeLiteralArray(&rest);
|
||||||
|
Toy_popScope(inner.scope);
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&inner.stack);
|
||||||
|
Toy_freeLiteralArray(&inner.literalCache);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal lit = TOY_TO_ARRAY_LITERAL(&rest);
|
||||||
|
if (!Toy_setScopeVariable(inner.scope, restParam, lit, false)) {
|
||||||
|
interpreter->errorOutput("[internal] Could not define rest parameter\n");
|
||||||
|
|
||||||
|
//free, and skip out
|
||||||
|
Toy_freeLiteral(restType);
|
||||||
|
Toy_freeLiteral(lit);
|
||||||
|
Toy_popScope(inner.scope);
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&inner.stack);
|
||||||
|
Toy_freeLiteralArray(&inner.literalCache);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_freeLiteral(restType);
|
||||||
|
Toy_freeLiteralArray(&rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute the interpreter
|
||||||
|
execInterpreter(&inner);
|
||||||
|
|
||||||
|
//adopt the panic state
|
||||||
|
interpreter->panic = inner.panic;
|
||||||
|
|
||||||
|
//accept the stack as the results
|
||||||
|
Toy_LiteralArray returnsFromInner;
|
||||||
|
Toy_initLiteralArray(&returnsFromInner);
|
||||||
|
|
||||||
|
//unpack the results
|
||||||
|
for (int i = 0; i < (returnArray->count || 1); i++) {
|
||||||
|
Toy_Literal lit = Toy_popLiteralArray(&inner.stack);
|
||||||
|
Toy_pushLiteralArray(&returnsFromInner, lit); //NOTE: also reverses the order
|
||||||
|
Toy_freeLiteral(lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool returnValue = true;
|
||||||
|
|
||||||
|
//TODO: remove this when multiple assignment is enabled - note the BUGFIX that balances the stack
|
||||||
|
if (returnsFromInner.count > 1) {
|
||||||
|
interpreter->errorOutput("Too many values returned (multiple returns not yet supported)\n");
|
||||||
|
|
||||||
|
returnValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < returnsFromInner.count && returnValue; i++) {
|
||||||
|
Toy_Literal ret = Toy_popLiteralArray(&returnsFromInner);
|
||||||
|
|
||||||
|
//check the return types
|
||||||
|
if (returnArray->count > 0 && TOY_AS_TYPE(returnArray->literals[i]).typeOf != ret.type) {
|
||||||
|
interpreter->errorOutput("Bad type found in return value\n");
|
||||||
|
|
||||||
|
//free, and skip out
|
||||||
|
returnValue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(returns, ret); //NOTE: reverses again
|
||||||
|
Toy_freeLiteral(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
//manual free
|
||||||
|
//BUGFIX: handle scopes of functions, which refer to the parent scope (leaking memory)
|
||||||
|
while(inner.scope != TOY_AS_FUNCTION(func).scope) {
|
||||||
|
for (int i = 0; i < inner.scope->variables.capacity; i++) {
|
||||||
|
//handle keys, just in case
|
||||||
|
if (TOY_IS_FUNCTION(inner.scope->variables.entries[i].key)) {
|
||||||
|
Toy_popScope(TOY_AS_FUNCTION(inner.scope->variables.entries[i].key).scope);
|
||||||
|
TOY_AS_FUNCTION(inner.scope->variables.entries[i].key).scope = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_FUNCTION(inner.scope->variables.entries[i].value)) {
|
||||||
|
Toy_popScope(TOY_AS_FUNCTION(inner.scope->variables.entries[i].value).scope);
|
||||||
|
TOY_AS_FUNCTION(inner.scope->variables.entries[i].value).scope = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner.scope = Toy_popScope(inner.scope);
|
||||||
|
}
|
||||||
|
Toy_freeLiteralArray(&returnsFromInner);
|
||||||
|
Toy_freeLiteralArray(&inner.stack);
|
||||||
|
Toy_freeLiteralArray(&inner.literalCache);
|
||||||
|
|
||||||
|
//BUGFIX: this function needs to eat the arguments
|
||||||
|
Toy_freeLiteralArray(arguments);
|
||||||
|
|
||||||
|
//actual bytecode persists until next call
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns) {
|
||||||
|
Toy_Literal key = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefStringLength(name, strlen(name)));
|
||||||
|
Toy_Literal val = TOY_TO_NULL_LITERAL;
|
||||||
|
|
||||||
|
if (!Toy_isDeclaredScopeVariable(interpreter->scope, key)) {
|
||||||
|
interpreter->errorOutput("No function with that name\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_getScopeVariable(interpreter->scope, key, &val);
|
||||||
|
|
||||||
|
bool ret = Toy_callLiteralFn(interpreter, val, arguments, returns);
|
||||||
|
|
||||||
|
Toy_freeLiteral(key);
|
||||||
|
Toy_freeLiteral(val);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user