#include "standard_library.h" #include "toy_console_colors.h" #include "toy_scope.h" #include "toy_stack.h" #include #include #include #include typedef struct CallbackPairs { const char* name; Toy_nativeCallback callback; } CallbackPairs; //example of how to write and use C bindings static void std_min(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; //return the lesser of two values, or null on error Toy_Value first = Toy_popStack(&vm->stack); Toy_Value second = Toy_popStack(&vm->stack); //check types if ((!TOY_VALUE_IS_INTEGER(first) && !TOY_VALUE_IS_FLOAT(first)) || (!TOY_VALUE_IS_INTEGER(second) && !TOY_VALUE_IS_FLOAT(second))) { char buffer[256]; snprintf(buffer, 256, "Invalid types '%s' and '%s' found in 'min()'", Toy_getValueTypeAsCString(Toy_unwrapValue(first).type), Toy_getValueTypeAsCString(Toy_unwrapValue(second).type)); Toy_error(buffer); Toy_freeValue(first); Toy_freeValue(second); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); return; } //compare ints, or coerce ints into floats if needed if (TOY_VALUE_IS_INTEGER(first) && TOY_VALUE_IS_INTEGER(second)) { Toy_Value result = TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_INTEGER(first) < TOY_VALUE_AS_INTEGER(second) ? TOY_VALUE_AS_INTEGER(first) : TOY_VALUE_AS_INTEGER(second)); Toy_pushStack(&vm->stack, result); return; } else if (TOY_VALUE_IS_INTEGER(first) && TOY_VALUE_IS_FLOAT(second)) { first = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(first) ); } else if (TOY_VALUE_IS_FLOAT(first) && TOY_VALUE_IS_INTEGER(second)) { second = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(second) ); } //finally, do the comparison on floats Toy_Value result = TOY_VALUE_FROM_FLOAT(TOY_VALUE_AS_FLOAT(first) < TOY_VALUE_AS_FLOAT(second) ? TOY_VALUE_AS_FLOAT(first) : TOY_VALUE_AS_FLOAT(second)); Toy_pushStack(&vm->stack, result); //NOTE: not freeing scalar values does work, but only in narrow cases } static void std_max(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; //return the lesser of two values, or null on error Toy_Value first = Toy_popStack(&vm->stack); Toy_Value second = Toy_popStack(&vm->stack); //check types if ((!TOY_VALUE_IS_INTEGER(first) && !TOY_VALUE_IS_FLOAT(first)) || (!TOY_VALUE_IS_INTEGER(second) && !TOY_VALUE_IS_FLOAT(second))) { char buffer[256]; snprintf(buffer, 256, "Invalid types '%s' and '%s' found in 'max()'", Toy_getValueTypeAsCString(Toy_unwrapValue(first).type), Toy_getValueTypeAsCString(Toy_unwrapValue(second).type)); Toy_error(buffer); Toy_freeValue(first); Toy_freeValue(second); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); return; } //compare ints, or coerce ints into floats if needed if (TOY_VALUE_IS_INTEGER(first) && TOY_VALUE_IS_INTEGER(second)) { Toy_Value result = TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_INTEGER(first) > TOY_VALUE_AS_INTEGER(second) ? TOY_VALUE_AS_INTEGER(first) : TOY_VALUE_AS_INTEGER(second)); Toy_pushStack(&vm->stack, result); return; } else if (TOY_VALUE_IS_INTEGER(first) && TOY_VALUE_IS_FLOAT(second)) { first = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(first) ); } else if (TOY_VALUE_IS_FLOAT(first) && TOY_VALUE_IS_INTEGER(second)) { second = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(second) ); } //finally, do the comparison on floats Toy_Value result = TOY_VALUE_FROM_FLOAT(TOY_VALUE_AS_FLOAT(first) > TOY_VALUE_AS_FLOAT(second) ? TOY_VALUE_AS_FLOAT(first) : TOY_VALUE_AS_FLOAT(second)); Toy_pushStack(&vm->stack, result); //NOTE: not freeing scalar values does work, but only in narrow cases } static void std_floor(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; Toy_Value value = Toy_popStack(&vm->stack); if (!TOY_VALUE_IS_INTEGER(value) && !TOY_VALUE_IS_FLOAT(value)) { char buffer[256]; snprintf(buffer, 256, "Invalid type '%s' found in 'floor()'", Toy_getValueTypeAsCString(Toy_unwrapValue(value).type)); Toy_error(buffer); Toy_freeValue(value); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); return; } //this only runs on floats, but converts them to integers if (TOY_VALUE_IS_FLOAT(value)) { double d = (double)TOY_VALUE_AS_FLOAT(value); d = floor(d); value = TOY_VALUE_FROM_INTEGER((int)d); } Toy_pushStack(&vm->stack, value); } static void std_ceil(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; Toy_Value value = Toy_popStack(&vm->stack); if (!TOY_VALUE_IS_INTEGER(value) && !TOY_VALUE_IS_FLOAT(value)) { char buffer[256]; snprintf(buffer, 256, "Invalid type '%s' found in 'ceil()'", Toy_getValueTypeAsCString(Toy_unwrapValue(value).type)); Toy_error(buffer); Toy_freeValue(value); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); return; } //this only runs on floats, but converts them to integers if (TOY_VALUE_IS_FLOAT(value)) { double d = (double)TOY_VALUE_AS_FLOAT(value); d = ceil(d); value = TOY_VALUE_FROM_INTEGER((int)d); } Toy_pushStack(&vm->stack, value); } static void std_sqrt(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; Toy_Value value = Toy_popStack(&vm->stack); if (!TOY_VALUE_IS_INTEGER(value) && !TOY_VALUE_IS_FLOAT(value)) { char buffer[256]; snprintf(buffer, 256, "Invalid type '%s' found in 'sqrt()'", Toy_getValueTypeAsCString(Toy_unwrapValue(value).type)); Toy_error(buffer); Toy_freeValue(value); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); return; } double d = 0; if (TOY_VALUE_IS_INTEGER(value)) d = (double)TOY_VALUE_AS_INTEGER(value); else if (TOY_VALUE_IS_FLOAT(value)) d = (double)TOY_VALUE_AS_FLOAT(value); //only works on non-negative values if (d < 0) { char buffer[256]; snprintf(buffer, 256, "Can't get the sqrt of a negative number (found '%g')", d); Toy_error(buffer); Toy_freeValue(value); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); return; } //do the thing and push the result d = sqrt(d); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_FLOAT((float)d)); } static void next(Toy_VM* vm, Toy_FunctionNative* self) { //used by 'std_range' if (self->meta2 < self->meta1) { Toy_Value result = TOY_VALUE_FROM_INTEGER(self->meta2); Toy_pushStack(&vm->stack, result); self->meta2++; } else { Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); } } static void std_range(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; //one arg to represent the number of iterations Toy_Value value = Toy_popStack(&vm->stack); //check types if (!TOY_VALUE_IS_INTEGER(value)) { char buffer[256]; snprintf(buffer, 256, "Expected Integer argument in 'range', found '%s'", Toy_getValueTypeAsCString(value.type)); Toy_error(buffer); Toy_freeValue(value); return; } //make the callback Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, next); fn->native.meta1 = TOY_VALUE_AS_INTEGER(value); //fake a closure fn->native.meta2 = 0; //counter Toy_Value result = TOY_VALUE_FROM_FUNCTION(fn); Toy_pushStack(&vm->stack, result); } CallbackPairs callbackPairs[] = { {"min", std_min}, {"max", std_max}, {"floor", std_floor}, {"ceil", std_ceil}, {"sqrt", std_sqrt}, {"range", std_range}, {NULL, NULL}, }; //exposed functions void initStandardLibrary(Toy_VM* vm) { if (vm == NULL || vm->scope == NULL || vm->memoryBucket == NULL) { fprintf(stderr, TOY_CC_ERROR "ERROR: Can't initialize standard library, exiting\n" TOY_CC_RESET); exit(-1); } //declare each pair for (int i = 0; callbackPairs[i].name; i++) { Toy_String* key = Toy_createStringLength(&vm->memoryBucket, callbackPairs[i].name, strlen(callbackPairs[i].name)); Toy_Function* fn = Toy_createFunctionFromCallback(&(vm->memoryBucket), callbackPairs[i].callback); Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true); } }