#include "toy_interpreter.h" #include "toy_console_colors.h" #include "toy_common.h" #include "toy_memory.h" #include "toy_keyword_types.h" #include "toy_opcodes.h" #include "toy_builtin.h" #include #include static void printWrapper(const char* output) { //allow for disabling of newlines in the repl #ifndef TOY_EXPORT if (Toy_commandLine.enablePrintNewline) { printf("%s\n", output); } else { printf("%s", output); } #else printf("%s\n", output); #endif } static void assertWrapper(const char* output) { fprintf(stderr, TOY_CC_ERROR "Assertion failure: %s\n" TOY_CC_RESET, output); } static void errorWrapper(const char* output) { fprintf(stderr, TOY_CC_ERROR "%s" TOY_CC_RESET, output); //no newline } bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func) { //reject reserved words if (Toy_findTypeByKeyword(name) != TOY_TOKEN_EOF) { interpreter->errorOutput("Can't override an existing keyword\n"); return false; } Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(name)); //make sure the name isn't taken if (Toy_existsLiteralDictionary(&interpreter->scope->variables, identifier)) { interpreter->errorOutput("Can't override an existing variable\n"); return false; } Toy_Literal fn = TOY_TO_FUNCTION_NATIVE_LITERAL(func); Toy_Literal type = TOY_TO_TYPE_LITERAL(fn.type, true); Toy_setLiteralDictionary(&interpreter->scope->variables, identifier, fn); Toy_setLiteralDictionary(&interpreter->scope->types, identifier, type); Toy_freeLiteral(identifier); Toy_freeLiteral(type); return true; } bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook) { //reject reserved words if (Toy_findTypeByKeyword(name) != TOY_TOKEN_EOF) { interpreter->errorOutput("Can't inject a hook on an existing keyword\n"); return false; } int identifierLength = strlen(name); Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefStringLength(name, identifierLength)); //make sure the name isn't taken if (Toy_existsLiteralDictionary(interpreter->hooks, identifier)) { interpreter->errorOutput("Can't override an existing hook\n"); return false; } Toy_Literal fn = TOY_TO_FUNCTION_HOOK_LITERAL(hook); Toy_setLiteralDictionary(interpreter->hooks, identifier, fn); Toy_freeLiteral(identifier); return true; } void Toy_parseCompoundToPureValues(Toy_Interpreter* interpreter, Toy_Literal* literalPtr) { //parse out an array if (TOY_IS_ARRAY(*literalPtr)) { for (int i = 0; i < TOY_AS_ARRAY(*literalPtr)->count; i++) { //check each individual element for an identifier if (TOY_IS_IDENTIFIER( TOY_AS_ARRAY(*literalPtr)->literals[i] )) { Toy_Literal index = TOY_TO_INTEGER_LITERAL(i); Toy_Literal entry = Toy_getLiteralArray(TOY_AS_ARRAY(*literalPtr), index); Toy_Literal idn = entry; Toy_parseIdentifierToValue(interpreter, &entry); Toy_setLiteralArray(TOY_AS_ARRAY(*literalPtr), index, entry); Toy_freeLiteral(idn); Toy_freeLiteral(index); Toy_freeLiteral(entry); } //recurse on sub-compounds 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]); } } } //parse out a dictionary else if (TOY_IS_DICTIONARY(*literalPtr)) { //check for any identifiers bool idnFound = false; for (int i = 0; i < TOY_AS_DICTIONARY(*literalPtr)->capacity; i++) { if (TOY_IS_IDENTIFIER( TOY_AS_DICTIONARY(*literalPtr)->entries[i].key ) || TOY_IS_IDENTIFIER(TOY_AS_DICTIONARY(*literalPtr)->entries[i].value)) { idnFound = true; break; } //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)) { Toy_parseCompoundToPureValues(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)) { Toy_parseCompoundToPureValues(interpreter, &TOY_AS_DICTIONARY(*literalPtr)->entries[i].value); } } if (!idnFound) { return; } Toy_LiteralDictionary* ret = TOY_ALLOCATE(Toy_LiteralDictionary, 1); Toy_initLiteralDictionary(ret); //this basically copies the dict, so preallocate it manually ret->capacity = TOY_AS_DICTIONARY(*literalPtr)->capacity; ret->entries = TOY_ALLOCATE(Toy_private_dictionary_entry, ret->capacity); for (int i = 0; i < ret->capacity; i++) { ret->entries[i].key = TOY_TO_NULL_LITERAL; ret->entries[i].value = TOY_TO_NULL_LITERAL; } for (int i = 0; i < TOY_AS_DICTIONARY(*literalPtr)->capacity; i++) { if ( TOY_IS_NULL(TOY_AS_DICTIONARY(*literalPtr)->entries[i].key) ) { continue; } Toy_Literal key = Toy_copyLiteral(TOY_AS_DICTIONARY(*literalPtr)->entries[i].key); Toy_Literal value = Toy_copyLiteral(TOY_AS_DICTIONARY(*literalPtr)->entries[i].value); //check each key/value pair, and sub-compounds if (TOY_IS_IDENTIFIER( key )) { Toy_parseIdentifierToValue(interpreter, &key); } if (TOY_IS_ARRAY(key) || TOY_IS_DICTIONARY(key)) { Toy_parseCompoundToPureValues(interpreter, &key); } if (TOY_IS_IDENTIFIER(value)) { Toy_parseIdentifierToValue(interpreter, &value); } if (TOY_IS_ARRAY(value) || TOY_IS_DICTIONARY(value)) { Toy_parseCompoundToPureValues(interpreter, &value); } Toy_setLiteralDictionary(ret, key, value); //cleanup Toy_freeLiteral(key); Toy_freeLiteral(value); } Toy_freeLiteral(*literalPtr); *literalPtr = TOY_TO_DICTIONARY_LITERAL(ret); } } 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; } } // if (TOY_IS_ARRAY(*literalPtr) || TOY_IS_DICTIONARY(*literalPtr)) { // Toy_parseCompoundToPureValues(interpreter, literalPtr); // } return true; } //utilities for the host program 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) { unsigned char ret = *(unsigned char*)(tb + *count); *count += 1; return ret; } static unsigned short readShort(const unsigned char* tb, int* count) { unsigned short ret = 0; memcpy(&ret, tb + *count, 2); *count += 2; return ret; } static int readInt(const unsigned char* tb, int* count) { int ret = 0; memcpy(&ret, tb + *count, 4); *count += 4; return ret; } static float readFloat(const unsigned char* tb, int* count) { float ret = 0; memcpy(&ret, tb + *count, 4); *count += 4; return ret; } static const char* readString(const unsigned char* tb, int* count) { const unsigned char* ret = tb + *count; *count += strlen((char*)ret) + 1; //+1 for null character return (const char*)ret; } static void consumeByte(Toy_Interpreter* interpreter, unsigned char byte, const unsigned char* tb, int* count) { if (byte != tb[*count]) { char buffer[512]; snprintf(buffer, 512, "[internal] Failed to consume the correct byte (expected %u, found %u)\n", byte, tb[*count]); interpreter->errorOutput(buffer); } *count += 1; } static void consumeShort(Toy_Interpreter* interpreter, unsigned short bytes, const unsigned char* tb, int* count) { if (bytes != *(unsigned short*)(tb + *count)) { char buffer[512]; snprintf(buffer, 512, "[internal] Failed to consume the correct bytes (expected %u, found %u)\n", bytes, *(unsigned short*)(tb + *count)); interpreter->errorOutput(buffer); } *count += 2; } //each available statement static bool execAssert(Toy_Interpreter* interpreter) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } if (!TOY_IS_STRING(rhs)) { interpreter->errorOutput("The assert keyword needs a string as the second argument, received: "); Toy_printLiteralCustom(rhs, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(rhs); Toy_freeLiteral(lhs); return false; } if (TOY_IS_NULL(lhs) || !TOY_IS_TRUTHY(lhs)) { (*interpreter->assertOutput)(Toy_toCString(TOY_AS_STRING(rhs))); interpreter->panic = true; Toy_freeLiteral(rhs); Toy_freeLiteral(lhs); return false; } Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execPrint(Toy_Interpreter* interpreter) { //print what is on top of the stack, then pop it Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_Literal idn = lit; if (TOY_IS_IDENTIFIER(lit) && Toy_parseIdentifierToValue(interpreter, &lit)) { Toy_freeLiteral(idn); } Toy_printLiteralCustom(lit, interpreter->printOutput); Toy_freeLiteral(lit); return true; } static bool execPushLiteral(Toy_Interpreter* interpreter, bool lng) { //read the index in the cache int index = 0; if (lng) { index = (int)readShort(interpreter->bytecode, &interpreter->count); } else { index = (int)readByte(interpreter->bytecode, &interpreter->count); } //push from cache to stack (DO NOT account for identifiers - will do that later) Toy_pushLiteralArray(&interpreter->stack, interpreter->literalCache.literals[index]); return true; } static bool rawLiteral(Toy_Interpreter* interpreter) { Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_Literal idn = lit; if (TOY_IS_IDENTIFIER(lit) && Toy_parseIdentifierToValue(interpreter, &lit)) { Toy_freeLiteral(idn); } Toy_pushLiteralArray(&interpreter->stack, lit); Toy_freeLiteral(lit); return true; } static bool execNegate(Toy_Interpreter* interpreter) { //negate the top literal on the stack (numbers only) Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_Literal idn = lit; if (TOY_IS_IDENTIFIER(lit) && Toy_parseIdentifierToValue(interpreter, &lit)) { Toy_freeLiteral(idn); } if (TOY_IS_INTEGER(lit)) { lit = TOY_TO_INTEGER_LITERAL(-TOY_AS_INTEGER(lit)); } else if (TOY_IS_FLOAT(lit)) { lit = TOY_TO_FLOAT_LITERAL(-TOY_AS_FLOAT(lit)); } else { interpreter->errorOutput("Can't negate that literal: "); Toy_printLiteralCustom(lit, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(lit); return false; } Toy_pushLiteralArray(&interpreter->stack, lit); Toy_freeLiteral(lit); return true; } static bool execInvert(Toy_Interpreter* interpreter) { //negate the top literal on the stack (booleans only) Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_Literal idn = lit; if (TOY_IS_IDENTIFIER(lit) && Toy_parseIdentifierToValue(interpreter, &lit)) { Toy_freeLiteral(idn); } if (TOY_IS_BOOLEAN(lit)) { lit = TOY_TO_BOOLEAN_LITERAL(!TOY_AS_BOOLEAN(lit)); } else { interpreter->errorOutput("Can't invert that literal: "); Toy_printLiteralCustom(lit, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(lit); return false; } Toy_pushLiteralArray(&interpreter->stack, lit); Toy_freeLiteral(lit); return true; } static bool execArithmetic(Toy_Interpreter* interpreter, Toy_Opcode opcode) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } //special case for string concatenation ONLY if (TOY_IS_STRING(lhs) && TOY_IS_STRING(rhs) && (opcode == TOY_OP_ADDITION || opcode == TOY_OP_VAR_ADDITION_ASSIGN)) { //check for overflow int totalLength = TOY_AS_STRING(lhs)->length + TOY_AS_STRING(rhs)->length; if (totalLength > TOY_MAX_STRING_LENGTH) { interpreter->errorOutput("Can't concatenate these strings, result is too long (error found in interpreter)\n"); return false; } //concat the strings char buffer[TOY_MAX_STRING_LENGTH]; snprintf(buffer, TOY_MAX_STRING_LENGTH, "%s%s", Toy_toCString(TOY_AS_STRING(lhs)), Toy_toCString(TOY_AS_STRING(rhs))); Toy_Literal literal = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, totalLength)); Toy_pushLiteralArray(&interpreter->stack, literal); //cleanup Toy_freeLiteral(literal); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } //type coersion if (TOY_IS_FLOAT(lhs) && TOY_IS_INTEGER(rhs)) { rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs)); } if (TOY_IS_INTEGER(lhs) && TOY_IS_FLOAT(rhs)) { lhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(lhs)); } //maths based on types if(TOY_IS_INTEGER(lhs) && TOY_IS_INTEGER(rhs)) { switch(opcode) { case TOY_OP_ADDITION: case TOY_OP_VAR_ADDITION_ASSIGN: Toy_pushLiteralArray(&interpreter->stack, TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(lhs) + TOY_AS_INTEGER(rhs) )); return true; case TOY_OP_SUBTRACTION: case TOY_OP_VAR_SUBTRACTION_ASSIGN: Toy_pushLiteralArray(&interpreter->stack, TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(lhs) - TOY_AS_INTEGER(rhs) )); return true; case TOY_OP_MULTIPLICATION: case TOY_OP_VAR_MULTIPLICATION_ASSIGN: Toy_pushLiteralArray(&interpreter->stack, TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(lhs) * TOY_AS_INTEGER(rhs) )); return true; case TOY_OP_DIVISION: case TOY_OP_VAR_DIVISION_ASSIGN: if (TOY_AS_INTEGER(rhs) == 0) { interpreter->errorOutput("Can't divide by zero (error found in interpreter)\n"); return false; } Toy_pushLiteralArray(&interpreter->stack, TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(lhs) / TOY_AS_INTEGER(rhs) )); return true; case TOY_OP_MODULO: case TOY_OP_VAR_MODULO_ASSIGN: if (TOY_AS_INTEGER(rhs) == 0) { interpreter->errorOutput("Can't modulo by zero (error found in interpreter)\n"); return false; } Toy_pushLiteralArray(&interpreter->stack, TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(lhs) % TOY_AS_INTEGER(rhs) )); return true; default: interpreter->errorOutput("[internal] bad opcode argument passed to execArithmetic()\n"); return false; } } //catch bad modulo if (opcode == TOY_OP_MODULO || opcode == TOY_OP_VAR_MODULO_ASSIGN) { interpreter->errorOutput("Bad arithmetic argument (modulo on floats not allowed)\n"); return false; } if(TOY_IS_FLOAT(lhs) && TOY_IS_FLOAT(rhs)) { switch(opcode) { case TOY_OP_ADDITION: case TOY_OP_VAR_ADDITION_ASSIGN: Toy_pushLiteralArray(&interpreter->stack, TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(lhs) + TOY_AS_FLOAT(rhs) )); return true; case TOY_OP_SUBTRACTION: case TOY_OP_VAR_SUBTRACTION_ASSIGN: Toy_pushLiteralArray(&interpreter->stack, TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(lhs) - TOY_AS_FLOAT(rhs) )); return true; case TOY_OP_MULTIPLICATION: case TOY_OP_VAR_MULTIPLICATION_ASSIGN: Toy_pushLiteralArray(&interpreter->stack, TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(lhs) * TOY_AS_FLOAT(rhs) )); return true; case TOY_OP_DIVISION: case TOY_OP_VAR_DIVISION_ASSIGN: if (TOY_AS_FLOAT(rhs) == 0) { interpreter->errorOutput("Can't divide by zero (error found in interpreter)\n"); return false; } Toy_pushLiteralArray(&interpreter->stack, TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(lhs) / TOY_AS_FLOAT(rhs) )); return true; default: interpreter->errorOutput("[internal] bad opcode argument passed to execArithmetic()\n"); return false; } } //wrong types interpreter->errorOutput("Bad arithmetic argument "); Toy_printLiteralCustom(lhs, interpreter->errorOutput); interpreter->errorOutput(" and "); Toy_printLiteralCustom(rhs, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); 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) { //read the index in the cache int identifierIndex = 0; int typeIndex = 0; if (lng) { identifierIndex = (int)readShort(interpreter->bytecode, &interpreter->count); typeIndex = (int)readShort(interpreter->bytecode, &interpreter->count); } else { identifierIndex = (int)readByte(interpreter->bytecode, &interpreter->count); typeIndex = (int)readByte(interpreter->bytecode, &interpreter->count); } Toy_Literal identifier = interpreter->literalCache.literals[identifierIndex]; Toy_Literal type = Toy_copyLiteral(interpreter->literalCache.literals[typeIndex]); Toy_Literal typeIdn = type; if (TOY_IS_IDENTIFIER(type) && Toy_parseIdentifierToValue(interpreter, &type)) { Toy_freeLiteral(typeIdn); } //BUGFIX: because identifiers are getting embedded in type definitions type = parseTypeToValue(interpreter, type); if (!Toy_declareScopeVariable(interpreter->scope, identifier, type)) { interpreter->errorOutput("Can't redefine the variable \""); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); return false; } Toy_Literal val = Toy_popLiteralArray(&interpreter->stack); Toy_Literal valIdn = val; if (TOY_IS_IDENTIFIER(val) && Toy_parseIdentifierToValue(interpreter, &val)) { Toy_freeLiteral(valIdn); } if (TOY_IS_ARRAY(val) || TOY_IS_DICTIONARY(val)) { Toy_parseCompoundToPureValues(interpreter, &val); } //TODO: could restrict opaque data to only opaque variables //BUGFIX: allow easy coercion on decl if (TOY_AS_TYPE(type).typeOf == TOY_LITERAL_FLOAT && TOY_IS_INTEGER(val)) { val = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(val)); } if (!TOY_IS_NULL(val) && !Toy_setScopeVariable(interpreter->scope, identifier, val, false)) { interpreter->errorOutput("Incorrect type assigned to variable \""); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(type); Toy_freeLiteral(val); return false; } Toy_freeLiteral(val); Toy_freeLiteral(type); return true; } static bool execFnDecl(Toy_Interpreter* interpreter, bool lng) { //read the index in the cache int identifierIndex = 0; int functionIndex = 0; if (lng) { identifierIndex = (int)readShort(interpreter->bytecode, &interpreter->count); functionIndex = (int)readShort(interpreter->bytecode, &interpreter->count); } else { identifierIndex = (int)readByte(interpreter->bytecode, &interpreter->count); functionIndex = (int)readByte(interpreter->bytecode, &interpreter->count); } Toy_Literal identifier = interpreter->literalCache.literals[identifierIndex]; Toy_Literal function = interpreter->literalCache.literals[functionIndex]; TOY_AS_FUNCTION(function).scope = Toy_pushScope(interpreter->scope); //hacked in (needed for closure persistance) Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION, true); if (!Toy_declareScopeVariable(interpreter->scope, identifier, type)) { interpreter->errorOutput("Can't redefine the function \""); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); return false; } if (!Toy_setScopeVariable(interpreter->scope, identifier, function, false)) { //scope gets copied here interpreter->errorOutput("Incorrect type assigned to variable \""); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); return false; } Toy_popScope(TOY_AS_FUNCTION(function).scope); //hacked out TOY_AS_FUNCTION(function).scope = NULL; Toy_freeLiteral(type); return true; } static bool execVarAssign(Toy_Interpreter* interpreter) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } if (TOY_IS_ARRAY(rhs) || TOY_IS_DICTIONARY(rhs)) { Toy_parseCompoundToPureValues(interpreter, &rhs); } if (!TOY_IS_IDENTIFIER(lhs)) { interpreter->errorOutput("Can't assign to a non-variable \""); Toy_printLiteralCustom(lhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); return false; } if (!Toy_isDelcaredScopeVariable(interpreter->scope, lhs)) { interpreter->errorOutput("Undeclared variable \""); Toy_printLiteralCustom(lhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return false; } //BUGFIX: allow easy coercion on assign Toy_Literal type = Toy_getScopeType(interpreter->scope, lhs); if (TOY_AS_TYPE(type).typeOf == TOY_LITERAL_FLOAT && TOY_IS_INTEGER(rhs)) { rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs)); } if (!Toy_setScopeVariable(interpreter->scope, lhs, rhs, true)) { interpreter->errorOutput("Incorrect type assigned to variable \""); Toy_printLiteralCustom(lhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); Toy_freeLiteral(type); return false; } Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); Toy_freeLiteral(type); return true; } static bool execVarArithmeticAssign(Toy_Interpreter* interpreter) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); //duplicate the name Toy_pushLiteralArray(&interpreter->stack, lhs); Toy_pushLiteralArray(&interpreter->stack, lhs); Toy_pushLiteralArray(&interpreter->stack, rhs); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execValCast(Toy_Interpreter* interpreter) { Toy_Literal value = Toy_popLiteralArray(&interpreter->stack); Toy_Literal type = Toy_popLiteralArray(&interpreter->stack); Toy_Literal valueIdn = value; if (TOY_IS_IDENTIFIER(value) && Toy_parseIdentifierToValue(interpreter, &value)) { Toy_freeLiteral(valueIdn); } Toy_Literal result = TOY_TO_NULL_LITERAL; if (TOY_IS_NULL(value)) { interpreter->errorOutput("Can't cast a null value\n"); Toy_freeLiteral(value); Toy_freeLiteral(type); return false; } //cast the rhs to the type represented by lhs switch(TOY_AS_TYPE(type).typeOf) { case TOY_LITERAL_BOOLEAN: result = TOY_TO_BOOLEAN_LITERAL(TOY_IS_TRUTHY(value)); break; case TOY_LITERAL_INTEGER: if (TOY_IS_BOOLEAN(value)) { result = TOY_TO_INTEGER_LITERAL(TOY_AS_BOOLEAN(value) ? 1 : 0); } if (TOY_IS_INTEGER(value)) { result = Toy_copyLiteral(value); } if (TOY_IS_FLOAT(value)) { result = TOY_TO_INTEGER_LITERAL(TOY_AS_FLOAT(value)); } if (TOY_IS_STRING(value)) { int val = 0; sscanf(Toy_toCString(TOY_AS_STRING(value)), "%d", &val); result = TOY_TO_INTEGER_LITERAL(val); } break; case TOY_LITERAL_FLOAT: if (TOY_IS_BOOLEAN(value)) { result = TOY_TO_FLOAT_LITERAL(TOY_AS_BOOLEAN(value) ? 1 : 0); } if (TOY_IS_INTEGER(value)) { result = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(value)); } if (TOY_IS_FLOAT(value)) { result = Toy_copyLiteral(value); } if (TOY_IS_STRING(value)) { float val = 0; sscanf(Toy_toCString(TOY_AS_STRING(value)), "%f", &val); result = TOY_TO_FLOAT_LITERAL(val); } break; case TOY_LITERAL_STRING: if (TOY_IS_BOOLEAN(value)) { char* str = TOY_AS_BOOLEAN(value) ? "true" : "false"; int length = strlen(str); result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(str, length)); //TODO: static reference optimisation? } if (TOY_IS_INTEGER(value)) { char buffer[128]; snprintf(buffer, 128, "%d", TOY_AS_INTEGER(value)); int length = strlen(buffer); result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, length)); } if (TOY_IS_FLOAT(value)) { char buffer[128]; snprintf(buffer, 128, "%g", TOY_AS_FLOAT(value)); int length = strlen(buffer); result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, length)); } if (TOY_IS_STRING(value)) { result = Toy_copyLiteral(value); } break; default: interpreter->errorOutput("Unknown cast type found: "); Toy_printLiteralCustom(type, interpreter->errorOutput); interpreter->errorOutput("\n"); return false; } //leave the new value on the stack Toy_pushLiteralArray(&interpreter->stack, result); Toy_freeLiteral(result); Toy_freeLiteral(value); Toy_freeLiteral(type); return true; } static bool execTypeOf(Toy_Interpreter* interpreter) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal type = TOY_TO_NULL_LITERAL; if (TOY_IS_IDENTIFIER(rhs)) { type = Toy_getScopeType(interpreter->scope, rhs); } else { type = TOY_TO_TYPE_LITERAL(rhs.type, false); //see issue #53 } Toy_pushLiteralArray(&interpreter->stack, type); Toy_freeLiteral(rhs); Toy_freeLiteral(type); return true; } static bool execCompareEqual(Toy_Interpreter* interpreter, bool invert) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } bool result = Toy_literalsAreEqual(lhs, rhs); if (invert) { result = !result; } Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(result)); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execCompareLess(Toy_Interpreter* interpreter, bool invert) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } //not a number, return falure if (!(TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs))) { interpreter->errorOutput("Incorrect type in comparison, value \""); Toy_printLiteralCustom(lhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return false; } if (!(TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) { interpreter->errorOutput("Incorrect type in comparison, value \""); Toy_printLiteralCustom(rhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return false; } //convert to floats - easier if (TOY_IS_INTEGER(lhs)) { lhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(lhs)); } if (TOY_IS_INTEGER(rhs)) { rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs)); } bool result; if (!invert) { result = (TOY_AS_FLOAT(lhs) < TOY_AS_FLOAT(rhs)); } else { result = (TOY_AS_FLOAT(lhs) > TOY_AS_FLOAT(rhs)); } Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(result)); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execCompareLessEqual(Toy_Interpreter* interpreter, bool invert) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } //not a number, return falure if (!(TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs))) { interpreter->errorOutput("Incorrect type in comparison, value \""); Toy_printLiteralCustom(lhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return false; } if (!(TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) { interpreter->errorOutput("Incorrect type in comparison, value \""); Toy_printLiteralCustom(rhs, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return false; } //convert to floats - easier if (TOY_IS_INTEGER(lhs)) { lhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(lhs)); } if (TOY_IS_INTEGER(rhs)) { rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs)); } bool result; if (!invert) { result = (TOY_AS_FLOAT(lhs) < TOY_AS_FLOAT(rhs)) || Toy_literalsAreEqual(lhs, rhs); } else { result = (TOY_AS_FLOAT(lhs) > TOY_AS_FLOAT(rhs)) || Toy_literalsAreEqual(lhs, rhs); } Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(result)); Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execAnd(Toy_Interpreter* interpreter) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } //short-circuit support if (!TOY_IS_TRUTHY(lhs)) { Toy_pushLiteralArray(&interpreter->stack, lhs); } else { Toy_pushLiteralArray(&interpreter->stack, rhs); } Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execOr(Toy_Interpreter* interpreter) { Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal rhsIdn = rhs; if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { Toy_freeLiteral(rhsIdn); } Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } //short-circuit support if (TOY_IS_TRUTHY(lhs)) { Toy_pushLiteralArray(&interpreter->stack, lhs); } else { Toy_pushLiteralArray(&interpreter->stack, rhs); } Toy_freeLiteral(lhs); Toy_freeLiteral(rhs); return true; } static bool execJump(Toy_Interpreter* interpreter) { int target = (int)readShort(interpreter->bytecode, &interpreter->count); if (target + interpreter->codeStart > interpreter->length) { interpreter->errorOutput("[internal] Jump out of range\n"); return false; } //actually jump interpreter->count = target + interpreter->codeStart; return true; } static bool execFalseJump(Toy_Interpreter* interpreter) { int target = (int)readShort(interpreter->bytecode, &interpreter->count); if (target + interpreter->codeStart > interpreter->length) { interpreter->errorOutput("[internal] Jump out of range (false jump)\n"); return false; } //actually jump Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_Literal litIdn = lit; if (TOY_IS_IDENTIFIER(lit) && Toy_parseIdentifierToValue(interpreter, &lit)) { Toy_freeLiteral(litIdn); } if (TOY_IS_NULL(lit)) { interpreter->errorOutput("Null detected in comparison\n"); Toy_freeLiteral(lit); return false; } if (!TOY_IS_TRUTHY(lit)) { interpreter->count = target + interpreter->codeStart; } Toy_freeLiteral(lit); return true; } //forward declare static void execInterpreter(Toy_Interpreter*); static void readInterpreterSections(Toy_Interpreter* interpreter); //expect stack: identifier, arg1, arg2, arg3..., stackSize //also supports identifier & arg1 to be other way around (looseFirstArgument) static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) { //BUGFIX: depth check - don't drown! if (interpreter->depth >= 1000) { interpreter->errorOutput("Infinite recursion detected - panicking\n"); interpreter->panic = true; return false; } Toy_LiteralArray arguments; Toy_initLiteralArray(&arguments); Toy_Literal stackSize = Toy_popLiteralArray(&interpreter->stack); //unpack the stack of arguments for (int i = 0; i < TOY_AS_INTEGER(stackSize) - 1; i++) { Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_pushLiteralArray(&arguments, lit); //NOTE: also reverses the order Toy_freeLiteral(lit); } //collect one more argument if (!looseFirstArgument && TOY_AS_INTEGER(stackSize) > 0) { Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_pushLiteralArray(&arguments, lit); //NOTE: also reverses the order Toy_freeLiteral(lit); } Toy_Literal identifier = Toy_popLiteralArray(&interpreter->stack); //collect one more argument if (looseFirstArgument) { Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_pushLiteralArray(&arguments, lit); //NOTE: also reverses the order Toy_freeLiteral(lit); } //get the function literal Toy_Literal func = identifier; if (!TOY_IS_FUNCTION(func) && Toy_parseIdentifierToValue(interpreter, &func)) { Toy_freeLiteral(identifier); } if (!TOY_IS_FUNCTION(func) && !TOY_IS_FUNCTION_NATIVE(func)) { interpreter->errorOutput("Function not found: "); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(identifier); Toy_freeLiteral(stackSize); Toy_freeLiteralArray(&arguments); return false; } //BUGFIX: correct the argument order Toy_LiteralArray correct; Toy_initLiteralArray(&correct); while (arguments.count > 0) { Toy_Literal lit = Toy_popLiteralArray(&arguments); Toy_pushLiteralArray(&correct, lit); Toy_freeLiteral(lit); } //call the function literal bool ret = Toy_callLiteralFn(interpreter, func, &correct, &interpreter->stack); if (!ret) { interpreter->errorOutput("Error encountered in function \""); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); } Toy_freeLiteralArray(&correct); Toy_freeLiteralArray(&arguments); Toy_freeLiteral(func); Toy_freeLiteral(stackSize); 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_isDelcaredScopeVariable(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) { Toy_LiteralArray returns; Toy_initLiteralArray(&returns); //get the values of everything on the stack while (interpreter->stack.count > 0) { Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_Literal litIdn = lit; if (TOY_IS_IDENTIFIER(lit) && Toy_parseIdentifierToValue(interpreter, &lit)) { Toy_freeLiteral(litIdn); } if (TOY_IS_ARRAY(lit) || TOY_IS_DICTIONARY(lit)) { Toy_parseCompoundToPureValues(interpreter, &lit); } Toy_pushLiteralArray(&returns, lit); //reverses the order Toy_freeLiteral(lit); } //and back again while (returns.count > 0) { Toy_Literal lit = Toy_popLiteralArray(&returns); Toy_pushLiteralArray(&interpreter->stack, lit); Toy_freeLiteral(lit); } Toy_freeLiteralArray(&returns); //finally return false; } static bool execImport(Toy_Interpreter* interpreter) { Toy_Literal alias = Toy_popLiteralArray(&interpreter->stack); Toy_Literal identifier = Toy_popLiteralArray(&interpreter->stack); //access the hooks if (!Toy_existsLiteralDictionary(interpreter->hooks, identifier)) { interpreter->errorOutput("Unknown library name in import statement: "); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(alias); Toy_freeLiteral(identifier); return false; } Toy_Literal func = Toy_getLiteralDictionary(interpreter->hooks, identifier); if (!TOY_IS_FUNCTION_HOOK(func)) { interpreter->errorOutput("Expected hook function, found: "); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); Toy_freeLiteral(func); Toy_freeLiteral(alias); Toy_freeLiteral(identifier); return false; } TOY_AS_FUNCTION_HOOK(func)(interpreter, identifier, alias); Toy_freeLiteral(func); Toy_freeLiteral(alias); Toy_freeLiteral(identifier); return true; } static bool execIndex(Toy_Interpreter* interpreter, bool assignIntermediate) { //assume -> compound, first, second, third are all on the stack Toy_Literal third = Toy_popLiteralArray(&interpreter->stack); Toy_Literal second = Toy_popLiteralArray(&interpreter->stack); Toy_Literal first = Toy_popLiteralArray(&interpreter->stack); Toy_Literal compound = Toy_popLiteralArray(&interpreter->stack); Toy_Literal compoundIdn = compound; bool freeIdn = false; //wtf? if (TOY_IS_IDENTIFIER(compound) && Toy_parseIdentifierToValue(interpreter, &compound)) { freeIdn = true; } if (!TOY_IS_ARRAY(compound) && !TOY_IS_DICTIONARY(compound) && !TOY_IS_STRING(compound)) { interpreter->errorOutput("Unknown compound found in index notation: "); Toy_printLiteralCustom(compound, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } return false; } //build the argument list Toy_LiteralArray arguments; Toy_initLiteralArray(&arguments); Toy_pushLiteralArray(&arguments, compound); Toy_pushLiteralArray(&arguments, first); Toy_pushLiteralArray(&arguments, second); Toy_pushLiteralArray(&arguments, third); Toy_pushLiteralArray(&arguments, TOY_TO_NULL_LITERAL); //it expects an assignment command Toy_pushLiteralArray(&arguments, TOY_TO_NULL_LITERAL); //it expects an assignment "opcode" //leave the idn and compound on the stack if (assignIntermediate) { if (TOY_IS_IDENTIFIER(compoundIdn)) { Toy_pushLiteralArray(&interpreter->stack, compoundIdn); } Toy_pushLiteralArray(&interpreter->stack, compound); Toy_pushLiteralArray(&interpreter->stack, first); Toy_pushLiteralArray(&interpreter->stack, second); Toy_pushLiteralArray(&interpreter->stack, third); } //call the index function if (Toy_private_index(interpreter, &arguments) < 0) { interpreter->errorOutput("Something went wrong while indexing (simple index): "); Toy_printLiteralCustom(compoundIdn, interpreter->errorOutput); interpreter->errorOutput("\n"); //clean up Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } Toy_freeLiteralArray(&arguments); return false; } //clean up Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } Toy_freeLiteralArray(&arguments); return true; } static bool execIndexAssign(Toy_Interpreter* interpreter, int assignDepth) { //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 compoundIdn = TOY_TO_NULL_LITERAL; bool freeIdn = false; //build the opcode unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); char* opStr = ""; switch (opcode) { case TOY_OP_VAR_ASSIGN: opStr = "="; break; case TOY_OP_VAR_ADDITION_ASSIGN: opStr = "+="; break; case TOY_OP_VAR_SUBTRACTION_ASSIGN: opStr = "-="; break; case TOY_OP_VAR_MULTIPLICATION_ASSIGN: opStr = "*="; break; case TOY_OP_VAR_DIVISION_ASSIGN: opStr = "/="; break; case TOY_OP_VAR_MODULO_ASSIGN: opStr = "%="; break; default: interpreter->errorOutput("bad opcode in index assigning notation\n"); return false; } //iterate... while(assignDepth-- >= 0) { Toy_freeLiteral(assign); Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (TOY_IS_NULL(result)) { assign = Toy_popLiteralArray(&interpreter->stack); } else { assign = result; } third = Toy_popLiteralArray(&interpreter->stack); second = Toy_popLiteralArray(&interpreter->stack); first = Toy_popLiteralArray(&interpreter->stack); compound = Toy_popLiteralArray(&interpreter->stack); if (TOY_IS_IDENTIFIER(compound)) { if (freeIdn) { Toy_freeLiteral(compoundIdn); } compoundIdn = compound; Toy_parseIdentifierToValue(interpreter, &compound); freeIdn = true; } Toy_Literal assignIdn = assign; if (TOY_IS_IDENTIFIER(assign) && Toy_parseIdentifierToValue(interpreter, &assign)) { Toy_freeLiteral(assignIdn); } if (!TOY_IS_ARRAY(compound) && !TOY_IS_DICTIONARY(compound) && !TOY_IS_STRING(compound)) { interpreter->errorOutput("Unknown compound found in index assigning notation: "); Toy_printLiteralCustom(compound, interpreter->errorOutput); interpreter->errorOutput("\n"); Toy_freeLiteral(assign); Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } return false; } int opLength = strlen(opStr); Toy_Literal op = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(opStr, opLength)); //TODO: static reference optimisation? //build the argument list Toy_LiteralArray arguments; Toy_initLiteralArray(&arguments); Toy_pushLiteralArray(&arguments, compound); Toy_pushLiteralArray(&arguments, first); Toy_pushLiteralArray(&arguments, second); Toy_pushLiteralArray(&arguments, third); Toy_pushLiteralArray(&arguments, assign); //it expects an assignment command Toy_pushLiteralArray(&arguments, op); //it expects an assignment "opcode" //call the index function if (Toy_private_index(interpreter, &arguments) < 0) { //clean up Toy_freeLiteral(assign); Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } Toy_freeLiteral(op); Toy_freeLiteralArray(&arguments); return false; } //save the result (assume top of the interpreter stack is the new compound value) result = Toy_popLiteralArray(&interpreter->stack); Toy_freeLiteral(op); Toy_freeLiteralArray(&arguments); //if we loop, then we need to be assigning opStr = "="; } //BUGFIX: make sure the compound name can be assigned if (TOY_IS_NULL(compoundIdn)) { compoundIdn = Toy_popLiteralArray(&interpreter->stack); freeIdn = true; } if (TOY_IS_IDENTIFIER(compoundIdn) && !Toy_setScopeVariable(interpreter->scope, compoundIdn, result, true)) { interpreter->errorOutput("Incorrect type assigned to compound member "); Toy_printLiteralCustom(compoundIdn, interpreter->errorOutput); interpreter->errorOutput(", value: "); Toy_printLiteralCustom(result, interpreter->errorOutput); interpreter->errorOutput("\n"); //clean up Toy_freeLiteral(assign); Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } Toy_freeLiteral(result); return false; } //clean up Toy_freeLiteral(assign); Toy_freeLiteral(third); Toy_freeLiteral(second); Toy_freeLiteral(first); Toy_freeLiteral(compound); if (freeIdn) { Toy_freeLiteral(compoundIdn); } Toy_freeLiteral(result); return true; } //the heart of toy static void execInterpreter(Toy_Interpreter* interpreter) { //set the starting point for the interpreter if (interpreter->codeStart == -1) { interpreter->codeStart = interpreter->count; } //BUGFIX int intermediateAssignDepth = 0; unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); while(opcode != TOY_OP_EOF && opcode != TOY_OP_SECTION_END && !interpreter->panic) { switch(opcode) { case TOY_OP_PASS: //DO NOTHING break; case TOY_OP_ASSERT: if (!execAssert(interpreter)) { return; } break; case TOY_OP_PRINT: if (!execPrint(interpreter)) { return; } break; case TOY_OP_LITERAL: case TOY_OP_LITERAL_LONG: if (!execPushLiteral(interpreter, opcode == TOY_OP_LITERAL_LONG)) { return; } break; case TOY_OP_LITERAL_RAW: if (!rawLiteral(interpreter)) { return; } break; case TOY_OP_NEGATE: if (!execNegate(interpreter)) { return; } break; case TOY_OP_ADDITION: case TOY_OP_SUBTRACTION: case TOY_OP_MULTIPLICATION: case TOY_OP_DIVISION: case TOY_OP_MODULO: if (!execArithmetic(interpreter, opcode)) { return; } break; case TOY_OP_VAR_ADDITION_ASSIGN: case TOY_OP_VAR_SUBTRACTION_ASSIGN: case TOY_OP_VAR_MULTIPLICATION_ASSIGN: case TOY_OP_VAR_DIVISION_ASSIGN: case TOY_OP_VAR_MODULO_ASSIGN: execVarArithmeticAssign(interpreter); if (!execArithmetic(interpreter, opcode)) { Toy_freeLiteral(Toy_popLiteralArray(&interpreter->stack)); return; } if (!execVarAssign(interpreter)) { return; } break; case TOY_OP_GROUPING_BEGIN: execInterpreter(interpreter); break; case TOY_OP_GROUPING_END: return; //scope case TOY_OP_SCOPE_BEGIN: interpreter->scope = Toy_pushScope(interpreter->scope); break; case TOY_OP_SCOPE_END: interpreter->scope = Toy_popScope(interpreter->scope); break; //TODO: custom type declarations? case TOY_OP_VAR_DECL: case TOY_OP_VAR_DECL_LONG: if (!execVarDecl(interpreter, opcode == TOY_OP_VAR_DECL_LONG)) { return; } break; case TOY_OP_FN_DECL: case TOY_OP_FN_DECL_LONG: if (!execFnDecl(interpreter, opcode == TOY_OP_FN_DECL_LONG)) { return; } break; case TOY_OP_VAR_ASSIGN: if (!execVarAssign(interpreter)) { return; } break; case TOY_OP_TYPE_CAST: if (!execValCast(interpreter)) { return; } break; case TOY_OP_TYPE_OF: if (!execTypeOf(interpreter)) { return; } break; case TOY_OP_COMPARE_EQUAL: if (!execCompareEqual(interpreter, false)) { return; } break; case TOY_OP_COMPARE_NOT_EQUAL: if (!execCompareEqual(interpreter, true)) { return; } break; case TOY_OP_COMPARE_LESS: if (!execCompareLess(interpreter, false)) { return; } break; case TOY_OP_COMPARE_LESS_EQUAL: if (!execCompareLessEqual(interpreter, false)) { return; } break; case TOY_OP_COMPARE_GREATER: if (!execCompareLess(interpreter, true)) { return; } break; case TOY_OP_COMPARE_GREATER_EQUAL: if (!execCompareLessEqual(interpreter, true)) { return; } break; case TOY_OP_INVERT: if (!execInvert(interpreter)) { return; } break; case TOY_OP_AND: if (!execAnd(interpreter)) { return; } break; case TOY_OP_OR: if (!execOr(interpreter)) { return; } break; case TOY_OP_JUMP: if (!execJump(interpreter)) { return; } break; case TOY_OP_IF_FALSE_JUMP: if (!execFalseJump(interpreter)) { return; } break; case TOY_OP_FN_CALL: if (!execFnCall(interpreter, false)) { return; } break; case TOY_OP_DOT: if (!execFnCall(interpreter, true)) { //compensate for the out-of-order arguments return; } break; case TOY_OP_FN_RETURN: if (!execFnReturn(interpreter)) { return; } break; case TOY_OP_IMPORT: if (!execImport(interpreter)) { return; } break; case TOY_OP_INDEX: if (!execIndex(interpreter, false)) { return; } break; case TOY_OP_INDEX_ASSIGN_INTERMEDIATE: if (!execIndex(interpreter, true)) { return; } intermediateAssignDepth++; break; case TOY_OP_INDEX_ASSIGN: if (!execIndexAssign(interpreter, intermediateAssignDepth)) { return; } intermediateAssignDepth = 0; break; case TOY_OP_POP_STACK: while (interpreter->stack.count > 0) { Toy_freeLiteral(Toy_popLiteralArray(&interpreter->stack)); } break; default: interpreter->errorOutput("Unknown opcode found, terminating\n"); return; } opcode = readByte(interpreter->bytecode, &interpreter->count); } } static void readInterpreterSections(Toy_Interpreter* interpreter) { //data section const unsigned short literalCount = readShort(interpreter->bytecode, &interpreter->count); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf(TOY_CC_NOTICE "Reading %d literals\n" TOY_CC_RESET, literalCount); } #endif for (int i = 0; i < literalCount; i++) { const unsigned char literalType = readByte(interpreter->bytecode, &interpreter->count); switch(literalType) { case TOY_LITERAL_NULL: //read the null Toy_pushLiteralArray(&interpreter->literalCache, TOY_TO_NULL_LITERAL); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(null)\n"); } #endif break; case TOY_LITERAL_BOOLEAN: { //read the booleans const bool b = readByte(interpreter->bytecode, &interpreter->count); Toy_Literal literal = TOY_TO_BOOLEAN_LITERAL(b); Toy_pushLiteralArray(&interpreter->literalCache, literal); Toy_freeLiteral(literal); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(boolean %s)\n", b ? "true" : "false"); } #endif } break; case TOY_LITERAL_INTEGER: { const int d = readInt(interpreter->bytecode, &interpreter->count); Toy_Literal literal = TOY_TO_INTEGER_LITERAL(d); Toy_pushLiteralArray(&interpreter->literalCache, literal); Toy_freeLiteral(literal); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(integer %d)\n", d); } #endif } break; case TOY_LITERAL_FLOAT: { const float f = readFloat(interpreter->bytecode, &interpreter->count); Toy_Literal literal = TOY_TO_FLOAT_LITERAL(f); Toy_pushLiteralArray(&interpreter->literalCache, literal); Toy_freeLiteral(literal); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(float %f)\n", f); } #endif } break; case TOY_LITERAL_STRING: { const char* s = readString(interpreter->bytecode, &interpreter->count); int length = strlen(s); Toy_Literal literal = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(s, length)); Toy_pushLiteralArray(&interpreter->literalCache, literal); Toy_freeLiteral(literal); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(string \"%s\")\n", s); } #endif } break; case TOY_LITERAL_ARRAY_INTERMEDIATE: case TOY_LITERAL_ARRAY: { Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1); Toy_initLiteralArray(array); unsigned short length = readShort(interpreter->bytecode, &interpreter->count); //read each index, then unpack the value from the existing literal cache for (int i = 0; i < length; i++) { int index = readShort(interpreter->bytecode, &interpreter->count); Toy_pushLiteralArray(array, interpreter->literalCache.literals[index]); } #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(array "); Toy_Literal literal = TOY_TO_ARRAY_LITERAL(array); Toy_printLiteral(literal); printf(")\n"); } #endif //finally, push the array proper Toy_Literal literal = TOY_TO_ARRAY_LITERAL(array); Toy_pushLiteralArray(&interpreter->literalCache, literal); //copied Toy_freeLiteralArray(array); TOY_FREE(Toy_LiteralArray, array); } break; case TOY_LITERAL_DICTIONARY_INTERMEDIATE: case TOY_LITERAL_DICTIONARY: { Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1); Toy_initLiteralDictionary(dictionary); unsigned short length = readShort(interpreter->bytecode, &interpreter->count); //read each index, then unpack the value from the existing literal cache for (int i = 0; i < length / 2; i++) { int key = readShort(interpreter->bytecode, &interpreter->count); int val = readShort(interpreter->bytecode, &interpreter->count); Toy_setLiteralDictionary(dictionary, interpreter->literalCache.literals[key], interpreter->literalCache.literals[val]); } #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(dictionary "); Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(dictionary); Toy_printLiteral(literal); printf(")\n"); } #endif //finally, push the dictionary proper Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(dictionary); Toy_pushLiteralArray(&interpreter->literalCache, literal); //copied Toy_freeLiteralDictionary(dictionary); TOY_FREE(Toy_LiteralDictionary, dictionary); } break; case TOY_LITERAL_FUNCTION: { //read the index unsigned short index = readShort(interpreter->bytecode, &interpreter->count); Toy_Literal literal = TOY_TO_INTEGER_LITERAL(index); //change the type, to read it PROPERLY below literal.type = TOY_LITERAL_FUNCTION_INTERMEDIATE; //push to the literal cache Toy_pushLiteralArray(&interpreter->literalCache, literal); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(function)\n"); } #endif } break; case TOY_LITERAL_IDENTIFIER: { const char* str = readString(interpreter->bytecode, &interpreter->count); int length = strlen(str); Toy_Literal identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefStringLength(str, length)); Toy_pushLiteralArray(&interpreter->literalCache, identifier); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(identifier %s (hash: %x))\n", Toy_toCString(TOY_AS_IDENTIFIER(identifier)), identifier.as.identifier.hash); } #endif Toy_freeLiteral(identifier); } break; case TOY_LITERAL_TYPE: { //what the literal is Toy_LiteralType literalType = (Toy_LiteralType)readByte(interpreter->bytecode, &interpreter->count); unsigned char constant = readByte(interpreter->bytecode, &interpreter->count); Toy_Literal typeLiteral = TOY_TO_TYPE_LITERAL(literalType, constant); //save the type Toy_pushLiteralArray(&interpreter->literalCache, typeLiteral); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(type "); Toy_printLiteral(typeLiteral); printf(")\n"); } #endif } break; case TOY_LITERAL_TYPE_INTERMEDIATE: { //what the literal represents Toy_LiteralType literalType = (Toy_LiteralType)readByte(interpreter->bytecode, &interpreter->count); unsigned char constant = readByte(interpreter->bytecode, &interpreter->count); Toy_Literal typeLiteral = TOY_TO_TYPE_LITERAL(literalType, constant); //if it's an array type if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) { unsigned short vt = readShort(interpreter->bytecode, &interpreter->count); TOY_TYPE_PUSH_SUBTYPE(&typeLiteral, Toy_copyLiteral(interpreter->literalCache.literals[vt])); } if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_DICTIONARY) { unsigned short kt = readShort(interpreter->bytecode, &interpreter->count); unsigned short vt = readShort(interpreter->bytecode, &interpreter->count); TOY_TYPE_PUSH_SUBTYPE(&typeLiteral, Toy_copyLiteral(interpreter->literalCache.literals[kt])); TOY_TYPE_PUSH_SUBTYPE(&typeLiteral, Toy_copyLiteral(interpreter->literalCache.literals[vt])); } //save the type Toy_pushLiteralArray(&interpreter->literalCache, typeLiteral); //copied #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(type "); Toy_printLiteral(typeLiteral); printf(")\n"); } #endif Toy_freeLiteral(typeLiteral); } break; case TOY_LITERAL_INDEX_BLANK: //read the blank Toy_pushLiteralArray(&interpreter->literalCache, TOY_TO_INDEX_BLANK_LITERAL); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf("(blank)\n"); } #endif break; } } consumeByte(interpreter, TOY_OP_SECTION_END, interpreter->bytecode, &interpreter->count); //terminate the literal section //read the function metadata int functionCount = readShort(interpreter->bytecode, &interpreter->count); int functionSize = readShort(interpreter->bytecode, &interpreter->count); //might not be needed //read in the functions for (int i = 0; i < interpreter->literalCache.count; i++) { if (interpreter->literalCache.literals[i].type == TOY_LITERAL_FUNCTION_INTERMEDIATE) { //get the size of the function size_t size = (size_t)readShort(interpreter->bytecode, &interpreter->count); //assert that the last memory slot is function end if (interpreter->bytecode[interpreter->count + size - 1] != TOY_OP_FN_END) { interpreter->errorOutput("[internal] Failed to find function end"); return; } //copies internally, since functions can exist independant of literalCache interpreter->literalCache.literals[i] = TOY_TO_FUNCTION_LITERAL(Toy_createRefFunction(interpreter->bytecode + interpreter->count, size)); interpreter->count += size; } } consumeByte(interpreter, TOY_OP_SECTION_END, interpreter->bytecode, &interpreter->count); //terminate the function section } //exposed functions void Toy_initInterpreter(Toy_Interpreter* interpreter) { interpreter->hooks = TOY_ALLOCATE(Toy_LiteralDictionary, 1); Toy_initLiteralDictionary(interpreter->hooks); //set up the output streams Toy_setInterpreterPrint(interpreter, printWrapper); Toy_setInterpreterAssert(interpreter, assertWrapper); Toy_setInterpreterError(interpreter, errorWrapper); interpreter->scope = NULL; Toy_resetInterpreter(interpreter); } void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length) { //initialize here instead of initInterpreter() Toy_initLiteralArray(&interpreter->literalCache); interpreter->bytecode = NULL; interpreter->length = 0; interpreter->count = 0; interpreter->codeStart = -1; Toy_initLiteralArray(&interpreter->stack); interpreter->depth = 0; interpreter->panic = false; //prep the bytecode interpreter->bytecode = bytecode; interpreter->length = length; interpreter->count = 0; if (!interpreter->bytecode) { interpreter->errorOutput("No valid bytecode given\n"); return; } //prep the literal cache if (interpreter->literalCache.count > 0) { Toy_freeLiteralArray(&interpreter->literalCache); //automatically inits } //header section const unsigned char major = readByte(interpreter->bytecode, &interpreter->count); const unsigned char minor = readByte(interpreter->bytecode, &interpreter->count); const unsigned char patch = readByte(interpreter->bytecode, &interpreter->count); if (major != TOY_VERSION_MAJOR || minor > TOY_VERSION_MINOR) { char buffer[TOY_MAX_STRING_LENGTH]; snprintf(buffer, TOY_MAX_STRING_LENGTH, "Interpreter/bytecode version mismatch (expected %d.%d.%d or earlier, given %d.%d.%d)\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, major, minor, patch); interpreter->errorOutput(buffer); return; } const char* build = readString(interpreter->bytecode, &interpreter->count); #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD))) { printf(TOY_CC_WARN "Warning: interpreter/bytecode build mismatch\n" TOY_CC_RESET); } } #endif consumeByte(interpreter, TOY_OP_SECTION_END, interpreter->bytecode, &interpreter->count); //read the sections of the bytecode readInterpreterSections(interpreter); //code section #ifndef TOY_EXPORT if (Toy_commandLine.verbose) { printf(TOY_CC_NOTICE "executing bytecode\n" TOY_CC_RESET); } #endif //execute the interpreter execInterpreter(interpreter); //BUGFIX: clear the stack (for repl - stack must be balanced) while(interpreter->stack.count > 0) { Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); Toy_freeLiteral(lit); } //free the bytecode immediately after use TODO: because why? TOY_FREE_ARRAY(unsigned char, interpreter->bytecode, interpreter->length); //free the associated data Toy_freeLiteralArray(&interpreter->literalCache); Toy_freeLiteralArray(&interpreter->stack); } void Toy_resetInterpreter(Toy_Interpreter* interpreter) { //free the interpreter scope while(interpreter->scope != NULL) { interpreter->scope = Toy_popScope(interpreter->scope); } //prep the scope interpreter->scope = Toy_pushScope(NULL); //globally available functions Toy_injectNativeFn(interpreter, "set", Toy_private_set); Toy_injectNativeFn(interpreter, "get", Toy_private_get); Toy_injectNativeFn(interpreter, "push", Toy_private_push); Toy_injectNativeFn(interpreter, "pop", Toy_private_pop); Toy_injectNativeFn(interpreter, "length", Toy_private_length); Toy_injectNativeFn(interpreter, "clear", Toy_private_clear); } void Toy_freeInterpreter(Toy_Interpreter* interpreter) { //free the interpreter scope while(interpreter->scope != NULL) { interpreter->scope = Toy_popScope(interpreter->scope); } if (interpreter->hooks) { Toy_freeLiteralDictionary(interpreter->hooks); TOY_FREE(Toy_LiteralDictionary, interpreter->hooks); } interpreter->hooks = NULL; }