diff --git a/repl/main.c b/repl/main.c index 3fcdc2b..c60a31b 100644 --- a/repl/main.c +++ b/repl/main.c @@ -9,7 +9,6 @@ #include "toy_compiler.h" #include "toy_vm.h" -//NOTE: for testing #include "standard_library.h" #include diff --git a/repl/standard_library.c b/repl/standard_library.c index ee78518..4f13f79 100644 --- a/repl/standard_library.c +++ b/repl/standard_library.c @@ -1,7 +1,6 @@ #include "standard_library.h" #include "toy_console_colors.h" -#include "toy_print.h" #include "toy_scope.h" #include "toy_stack.h" @@ -14,36 +13,85 @@ typedef struct CallbackPairs { Toy_nativeCallback callback; } CallbackPairs; -//example callbacks -static void answer(Toy_VM* vm, Toy_FunctionNative* self) { - (void)vm; +//example of how to write and use C bindings +static void std_min(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; - Toy_print(TOY_CC_DEBUG "This function returns the integer '42' to the calling scope." TOY_CC_RESET); - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(42)); + + //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 identity(Toy_VM* vm, Toy_FunctionNative* self) { - //does nothing, but any arguements are left on the stack as results - (void)vm; +static void std_max(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; -} -static void echo(Toy_VM* vm, Toy_FunctionNative* self) { - (void)self; - //pops one argument, and prints it - Toy_Value value = Toy_popStack(&vm->stack); - Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value); - char* cstr = Toy_getStringRaw(string); + //return the lesser of two values, or null on error + Toy_Value first = Toy_popStack(&vm->stack); + Toy_Value second = Toy_popStack(&vm->stack); - Toy_print(cstr); + //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); - free(cstr); - Toy_freeString(string); - Toy_freeValue(value); + 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 next(Toy_VM* vm, Toy_FunctionNative* self) { - //used by 'range' + //used by 'std_range' if (self->meta2 < self->meta1) { Toy_Value result = TOY_VALUE_FROM_INTEGER(self->meta2); Toy_pushStack(&vm->stack, result); @@ -54,7 +102,7 @@ static void next(Toy_VM* vm, Toy_FunctionNative* self) { } } -static void range(Toy_VM* vm, Toy_FunctionNative* self) { +static void std_range(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; //one arg to represent the number of iterations @@ -79,12 +127,10 @@ static void range(Toy_VM* vm, Toy_FunctionNative* self) { Toy_pushStack(&vm->stack, result); } - CallbackPairs callbackPairs[] = { - {"dbg_answer", answer}, - {"dbg_identity", identity}, - {"dbg_echo", echo}, - {"range", range}, + {"min", std_min}, + {"max", std_max}, + {"range", std_range}, {NULL, NULL}, }; diff --git a/source/toy_bucket.h b/source/toy_bucket.h index 00014e2..c3572f4 100644 --- a/source/toy_bucket.h +++ b/source/toy_bucket.h @@ -56,5 +56,3 @@ TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle); #ifndef TOY_BUCKET_IDEAL #define TOY_BUCKET_IDEAL (TOY_BUCKET_64KB - sizeof(Toy_Bucket)) #endif - -//TODO: check for leaks when freeBucket is called, for debugging \ No newline at end of file diff --git a/source/toy_compiler.c b/source/toy_compiler.c index ed61be4..a0ab6ea 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -787,7 +787,7 @@ static unsigned int writeInstructionForThen(Toy_Bytecode** mb, Toy_AstForThen as emitString(mb, ast.condBranch->iterable.left->varDeclare.name); - //TODO: use a different approach + //TODO: use a different approach? //BUGFIX: shadow the iterable's name EMIT_BYTE(mb, code, TOY_OPCODE_READ); EMIT_BYTE(mb, code, TOY_VALUE_NULL); diff --git a/source/toy_value.c b/source/toy_value.c index 9439366..380a8a5 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -464,29 +464,15 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { } case TOY_VALUE_INTEGER: { - char buffer[16]; + char buffer[128]; sprintf(buffer, "%d", value.as.integer); return Toy_createStringLength(bucketHandle, buffer, strlen(buffer)); } case TOY_VALUE_FLOAT: { - //using printf - char buffer[16]; - sprintf(buffer, "%f", value.as.number); - - //BUGFIX: printf format specificer '%f' will set the precision to 6 decimal places, which means there's trailing zeroes + char buffer[128]; + sprintf(buffer, "%g", value.as.number); unsigned int length = strlen(buffer); - - //find the decimal, if it exists - unsigned int decimal = 0; - while (decimal != length && buffer[decimal] != '.' && buffer[decimal] != ',') decimal++; //'.' and ',' supports more locales - - //locales are hard, sorry! - if (decimal != length && buffer[decimal] == ',') buffer[decimal] = '.'; - - //wipe the trailing zeros - while(decimal != length && buffer[length-1] == '0') buffer[--length] = '\0'; - return Toy_createStringLength(bucketHandle, buffer, length); } diff --git a/source/toy_vm.c b/source/toy_vm.c index 10d906b..6bbee08 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -546,7 +546,7 @@ static void processIterate(Toy_VM* vm) { processJump(vm); } } - //TODO: support closures as a parameter + //TODO: support closures in for-loops else { fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown iterable type '%s' found in for loop, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(Toy_unwrapValue(compound).type)); exit(-1); @@ -826,7 +826,7 @@ static void processAssert(Toy_VM* vm) { //determine the args if (count == 1) { - message = TOY_VALUE_FROM_STRING(Toy_toString(&vm->memoryBucket, "assertion failed")); //TODO: better default error message + message = TOY_VALUE_FROM_STRING(Toy_toString(&vm->memoryBucket, "assertion failed")); //TODO: better default assert message value = Toy_popStack(&vm->stack); } else if (count == 2) { diff --git a/tests/makefile b/tests/makefile index 7563927..0ed113d 100644 --- a/tests/makefile +++ b/tests/makefile @@ -7,5 +7,3 @@ all: gdb: $(MAKE) -C units -k gdb $(MAKE) -C scripts -k gdb - -#TODO: valgrind \ No newline at end of file diff --git a/tests/scripts/test_attribute.toy b/tests/scripts/test_attribute.toy index e2f2524..3873d0e 100644 --- a/tests/scripts/test_attribute.toy +++ b/tests/scripts/test_attribute.toy @@ -1 +1 @@ -//TODO: empty test script \ No newline at end of file +//URGENT: empty test script \ No newline at end of file diff --git a/tests/scripts/test_functions.toy b/tests/scripts/test_functions.toy index e2f2524..3873d0e 100644 --- a/tests/scripts/test_functions.toy +++ b/tests/scripts/test_functions.toy @@ -1 +1 @@ -//TODO: empty test script \ No newline at end of file +//URGENT: empty test script \ No newline at end of file diff --git a/tests/scripts/test_keyword_for_iteration.toy b/tests/scripts/test_keyword_for_iteration.toy index 1010ae9..04476a4 100644 --- a/tests/scripts/test_keyword_for_iteration.toy +++ b/tests/scripts/test_keyword_for_iteration.toy @@ -1,4 +1,4 @@ -//TODO: empty test script +//URGENT: empty test script //TODO: test iteration on arrays, tables, closures diff --git a/tests/units/test_attribute.c b/tests/units/test_attribute.c index 9b811ec..e6c5021 100644 --- a/tests/units/test_attribute.c +++ b/tests/units/test_attribute.c @@ -4,7 +4,7 @@ #include int main(void) { - //TODO: Test not yet implemented + //URGENT: Test not yet implemented printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); return 0; } \ No newline at end of file diff --git a/tests/units/test_function.c b/tests/units/test_function.c index 49239f3..cac9a80 100644 --- a/tests/units/test_function.c +++ b/tests/units/test_function.c @@ -117,7 +117,7 @@ int test_functions_from_bytecodes(void) { } int test_functions_from_callbacks(void) { - //TODO: Test not yet implemented + //URGENT: Test not yet implemented printf(TOY_CC_WARN "WIP test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); return 0; } diff --git a/tests/units/test_value.c b/tests/units/test_value.c index 6f943ca..c3825e1 100644 --- a/tests/units/test_value.c +++ b/tests/units/test_value.c @@ -607,8 +607,8 @@ int main(void) { total += res; } - //TODO: references - //TODO: type coersions + //TODO: references? + //TODO: type coersions? //TODO: opaques? return total;