From d19ca1bcee8cf84f7b4fc9af949302317745a54b Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Wed, 30 Oct 2024 19:58:55 +1100 Subject: [PATCH] Reworked variable equality and comparisons Fixed #146 --- scripts/testificate.toy | 48 ++++-- source/toy_parser.c | 15 +- source/toy_table.c | 6 +- source/toy_value.c | 224 +++++++++++++++++--------- source/toy_value.h | 14 +- source/toy_vm.c | 81 +++++----- tests/cases/test_value.c | 176 ++++++++++++++++++-- tests/integrations/test_variables.toy | 36 +++++ 8 files changed, 447 insertions(+), 153 deletions(-) diff --git a/scripts/testificate.toy b/scripts/testificate.toy index 811b052..6c92569 100644 --- a/scripts/testificate.toy +++ b/scripts/testificate.toy @@ -1,22 +1,38 @@ -//normal scope stuff -var answer = 42; -print answer; -{ - var answer = 7; - print answer; -} +print 1 == 1; //true +print 1 != 1; //false -print answer; +print 1 < 2; //true -//I wonder if... -var question = 42; -print question; +print "foo" > "bar"; //true -{ - var question = question; - print question; -} -print question; +print 1 < 2; //true +print 1 > 2; //false + +print 2 <= 2; //true +print 2 >= 2; //true + +print 1 <= 2; //true +print 1 >= 2; //false + +print true && true; //true +print true && false; //false +print false && true; //false +print false && false; //false + +print true || true; //true +print true || false; //true +print false || true; //true +print false || false; //false + +print !true; //false +print !false; //true + + + + + +//nesting +print true && false || true; //TODO: a warning is needed for this \ No newline at end of file diff --git a/source/toy_parser.c b/source/toy_parser.c index 47901ff..a41c590 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -199,8 +199,8 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_BRACE_RIGHT, //other operators - {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_AND, - {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_OR, + {PREC_AND,NULL,binary},// TOY_TOKEN_OPERATOR_AND, + {PREC_OR,NULL,binary},// TOY_TOKEN_OPERATOR_OR, {PREC_NONE,unary,NULL},// TOY_TOKEN_OPERATOR_NEGATE, {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_QUESTION, {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_COLON, @@ -472,6 +472,17 @@ static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast return TOY_AST_FLAG_COMPARE_GREATER_EQUAL; } + //logical + case TOY_TOKEN_OPERATOR_AND: { + parsePrecedence(bucketHandle, parser, rootHandle, PREC_AND + 1); + return TOY_AST_FLAG_AND; + } + + case TOY_TOKEN_OPERATOR_OR: { + parsePrecedence(bucketHandle, parser, rootHandle, PREC_OR + 1); + return TOY_AST_FLAG_OR; + } + case TOY_TOKEN_OPERATOR_CONCAT: { parsePrecedence(bucketHandle, parser, rootHandle, PREC_CALL + 1); return TOY_AST_FLAG_CONCAT; diff --git a/source/toy_table.c b/source/toy_table.c index ca50daf..8309372 100644 --- a/source/toy_table.c +++ b/source/toy_table.c @@ -15,7 +15,7 @@ static void probeAndInsert(Toy_Table** tableHandle, Toy_Value key, Toy_Value val //probe while (true) { //if we're overriding an existing value - if (TOY_VALUES_ARE_EQUAL((*tableHandle)->data[probe].key, key)) { + if (Toy_checkValuesAreEqual((*tableHandle)->data[probe].key, key)) { (*tableHandle)->data[probe] = entry; //TODO: benchmark the psl optimisation @@ -123,7 +123,7 @@ Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) { while (true) { //found the entry - if (TOY_VALUES_ARE_EQUAL((*tableHandle)->data[probe].key, key)) { + if (Toy_checkValuesAreEqual((*tableHandle)->data[probe].key, key)) { return (*tableHandle)->data[probe].value; } @@ -148,7 +148,7 @@ void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key) { while (true) { //found the entry - if (TOY_VALUES_ARE_EQUAL((*tableHandle)->data[probe].key, key)) { + if (Toy_checkValuesAreEqual((*tableHandle)->data[probe].key, key)) { break; } diff --git a/source/toy_value.c b/source/toy_value.c index 5633b33..1d43ee2 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -1,78 +1,10 @@ #include "toy_value.h" #include "toy_console_colors.h" -#include "toy_print.h" #include "toy_string.h" +#include "toy_print.h" -#include -#include - -bool Toy_private_isTruthy(Toy_Value value) { - //null is an error - if (TOY_VALUE_IS_NULL(value)) { - Toy_error(TOY_CC_ERROR "ERROR: 'null' is neither true nor false\n" TOY_CC_RESET); - } - - //only 'false' is falsy - if (TOY_VALUE_IS_BOOLEAN(value)) { - return TOY_VALUE_AS_BOOLEAN(value); - } - - //anything else is truthy - return true; -} - -bool Toy_private_isEqual(Toy_Value left, Toy_Value right) { - //temp check - if (right.type > TOY_VALUE_STRING) { - Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value equality comparison\n" TOY_CC_RESET); - } - - switch(left.type) { - case TOY_VALUE_NULL: - return TOY_VALUE_IS_NULL(right); - - case TOY_VALUE_BOOLEAN: - return TOY_VALUE_IS_BOOLEAN(right) && TOY_VALUE_AS_BOOLEAN(left) == TOY_VALUE_AS_BOOLEAN(right); - - case TOY_VALUE_INTEGER: - if (TOY_VALUE_AS_INTEGER(right)) { - return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_INTEGER(right); - } - if (TOY_VALUE_AS_FLOAT(right)) { - return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_FLOAT(right); - } - return false; - - case TOY_VALUE_FLOAT: - if (TOY_VALUE_AS_FLOAT(right)) { - return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_FLOAT(right); - } - if (TOY_VALUE_AS_INTEGER(right)) { - return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_INTEGER(right); - } - return false; - - case TOY_VALUE_STRING: - if (TOY_VALUE_IS_STRING(right)) { - return Toy_compareStrings(TOY_VALUE_AS_STRING(left), TOY_VALUE_AS_STRING(right)) == 0; - } - return false; - - case TOY_VALUE_ARRAY: - case TOY_VALUE_TABLE: - case TOY_VALUE_FUNCTION: - case TOY_VALUE_OPAQUE: - case TOY_VALUE_TYPE: - case TOY_VALUE_ANY: - case TOY_VALUE_UNKNOWN: - Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value equality comparison\n" TOY_CC_RESET); - } - - return 0; -} - -//hash util +//utils static unsigned int hashUInt(unsigned int x) { x = ((x >> 16) ^ x) * 0x45d9f3b; x = ((x >> 16) ^ x) * 0x45d9f3b; @@ -80,6 +12,7 @@ static unsigned int hashUInt(unsigned int x) { return x; } +//exposed functions unsigned int Toy_hashValue(Toy_Value value) { switch(value.type) { case TOY_VALUE_NULL: @@ -104,9 +37,10 @@ unsigned int Toy_hashValue(Toy_Value value) { case TOY_VALUE_TYPE: case TOY_VALUE_ANY: case TOY_VALUE_UNKNOWN: - Toy_error(TOY_CC_ERROR "ERROR: Can't hash an unknown type\n" TOY_CC_RESET); + break; } + Toy_error(TOY_CC_ERROR "ERROR: Can't hash an unknown value type\n" TOY_CC_RESET); return 0; } @@ -130,10 +64,11 @@ Toy_Value Toy_copyValue(Toy_Value value) { case TOY_VALUE_TYPE: case TOY_VALUE_ANY: case TOY_VALUE_UNKNOWN: - Toy_error(TOY_CC_ERROR "ERROR: Can't copy an unknown type\n" TOY_CC_RESET); + break; } //dummy return + Toy_error(TOY_CC_ERROR "ERROR: Can't copy an unknown value type\n" TOY_CC_RESET); return TOY_VALUE_FROM_NULL(); } @@ -161,3 +96,148 @@ void Toy_freeValue(Toy_Value value) { Toy_error(TOY_CC_ERROR "ERROR: Can't free an unknown type\n" TOY_CC_RESET); } } + +bool Toy_checkValueIsTruthy(Toy_Value value) { + //null is an error + if (TOY_VALUE_IS_NULL(value)) { + Toy_error(TOY_CC_ERROR "ERROR: 'null' is neither true nor false\n" TOY_CC_RESET); + return false; + } + + //only 'false' is falsy + if (TOY_VALUE_IS_BOOLEAN(value)) { + return TOY_VALUE_AS_BOOLEAN(value); + } + + //anything else is truthy + return true; +} + +bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) { + switch(left.type) { + case TOY_VALUE_NULL: + return TOY_VALUE_IS_NULL(right); + + case TOY_VALUE_BOOLEAN: + return TOY_VALUE_IS_BOOLEAN(right) && TOY_VALUE_AS_BOOLEAN(left) == TOY_VALUE_AS_BOOLEAN(right); + + case TOY_VALUE_INTEGER: + if (TOY_VALUE_IS_INTEGER(right)) { + return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_INTEGER(right); + } + else if (TOY_VALUE_IS_FLOAT(right)) { + return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_FLOAT(right); + } + else { + break; + } + + case TOY_VALUE_FLOAT: + if (TOY_VALUE_IS_INTEGER(right)) { + return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_INTEGER(right); + } + else if (TOY_VALUE_IS_FLOAT(right)) { + return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_FLOAT(right); + } + else { + break; + } + + case TOY_VALUE_STRING: + if (TOY_VALUE_IS_STRING(right)) { + return Toy_compareStrings(TOY_VALUE_AS_STRING(left), TOY_VALUE_AS_STRING(right)) == 0; + } + else { + break; + } + + case TOY_VALUE_ARRAY: + case TOY_VALUE_TABLE: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: + break; + } + + Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value equality\n" TOY_CC_RESET); + return false; +} + +bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) { + switch(left.type) { + case TOY_VALUE_NULL: + return false; + + case TOY_VALUE_BOOLEAN: + return TOY_VALUE_IS_BOOLEAN(right); + + case TOY_VALUE_INTEGER: + case TOY_VALUE_FLOAT: + return TOY_VALUE_IS_INTEGER(right) || TOY_VALUE_IS_FLOAT(right); + + case TOY_VALUE_STRING: + return TOY_VALUE_IS_STRING(right); + + case TOY_VALUE_ARRAY: + case TOY_VALUE_TABLE: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: + break; + } + + Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value comparison check\n" TOY_CC_RESET); + return false; +} + +int Toy_compareValues(Toy_Value left, Toy_Value right) { + //comparison means there's a difference in value, with some kind of quantity - so null, bool, etc. aren't comparable + switch(left.type) { + case TOY_VALUE_NULL: + case TOY_VALUE_BOOLEAN: + break; + + case TOY_VALUE_INTEGER: + if (TOY_VALUE_IS_INTEGER(right)) { + return TOY_VALUE_AS_INTEGER(left) - TOY_VALUE_AS_INTEGER(right); + } + else if (TOY_VALUE_IS_FLOAT(right)) { + return TOY_VALUE_AS_INTEGER(left) - TOY_VALUE_AS_FLOAT(right); + } + else { + break; + } + + case TOY_VALUE_FLOAT: + if (TOY_VALUE_IS_INTEGER(right)) { + return TOY_VALUE_AS_FLOAT(left) - TOY_VALUE_AS_INTEGER(right); + } + else if (TOY_VALUE_IS_FLOAT(right)) { + return TOY_VALUE_AS_FLOAT(left) - TOY_VALUE_AS_FLOAT(right); + } + else { + break; + } + + case TOY_VALUE_STRING: + if (TOY_VALUE_IS_STRING(right)) { + return Toy_compareStrings(TOY_VALUE_AS_STRING(left), TOY_VALUE_AS_STRING(right)); + } + + case TOY_VALUE_ARRAY: + case TOY_VALUE_TABLE: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: + break; + } + + Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value comparison\n" TOY_CC_RESET); + return -1; +} diff --git a/source/toy_value.h b/source/toy_value.h index eea36c1..2606ca8 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -57,13 +57,13 @@ typedef struct Toy_Value { //32 | 64 BITNESS #define TOY_VALUE_FROM_STRING(value) ((Toy_Value){{ .string = value }, TOY_VALUE_STRING}) //TODO: more -#define TOY_VALUE_IS_TRUTHY(value) Toy_private_isTruthy(value) -TOY_API bool Toy_private_isTruthy(Toy_Value value); - -#define TOY_VALUES_ARE_EQUAL(left, right) Toy_private_isEqual(left, right) -TOY_API bool Toy_private_isEqual(Toy_Value left, Toy_Value right); - -unsigned int Toy_hashValue(Toy_Value value); +//utilities +TOY_API unsigned int Toy_hashValue(Toy_Value value); TOY_API Toy_Value Toy_copyValue(Toy_Value value); TOY_API void Toy_freeValue(Toy_Value value); + +TOY_API bool Toy_checkValueIsTruthy(Toy_Value value); +TOY_API bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right); +TOY_API bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right); +TOY_API int Toy_compareValues(Toy_Value left, Toy_Value right); diff --git a/source/toy_vm.c b/source/toy_vm.c index 81728db..b154848 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -194,6 +194,18 @@ static void processAccess(Toy_VM* vm) { Toy_freeValue(name); } +static void processDuplicate(Toy_VM* vm) { + Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack)); + Toy_pushStack(&vm->stack, value); + Toy_freeValue(value); + + //check for compound assignments + Toy_OpcodeType squeezed = READ_BYTE(vm); + if (squeezed == TOY_OPCODE_ACCESS) { + processAccess(vm); + } +} + static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); @@ -214,7 +226,7 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { //check for modulo by a float if (opcode == TOY_OPCODE_MODULO && TOY_VALUE_IS_FLOAT(right)) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Can't modulo by a float, exiting\n" TOY_CC_RESET); + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't modulo by a float, exiting\n" TOY_CC_RESET); //TODO: swap these with Toy_error so the repl doens't exit exit(-1); } @@ -260,25 +272,13 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { } } -static void processDuplicate(Toy_VM* vm) { - Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack)); - Toy_pushStack(&vm->stack, value); - Toy_freeValue(value); - - //check for compound assignments - Toy_OpcodeType squeezed = READ_BYTE(vm); - if (squeezed == TOY_OPCODE_ACCESS) { - processAccess(vm); - } -} - static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); //most things can be equal, so handle it separately if (opcode == TOY_OPCODE_COMPARE_EQUAL) { - bool equal = TOY_VALUES_ARE_EQUAL(left, right); + bool equal = Toy_checkValuesAreEqual(left, right); //equality has an optional "negate" opcode within it's word if (READ_BYTE(vm) != TOY_OPCODE_NEGATE) { @@ -291,31 +291,31 @@ static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) { return; } - //coerce ints into floats if needed - if (TOY_VALUE_IS_INTEGER(left) && TOY_VALUE_IS_FLOAT(right)) { - left = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(left) ); - } - else - if (TOY_VALUE_IS_FLOAT(left) && TOY_VALUE_IS_INTEGER(right)) { - right = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(right) ); + if (Toy_checkValuesAreComparable(left, right) == false) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't compare value types %d and %d\n" TOY_CC_RESET, left.type, right.type); //TODO: typeToCString for error messages + exit(-1); } - //other opcodes - if (opcode == TOY_OPCODE_COMPARE_LESS) { - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) < TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) < TOY_VALUE_AS_INTEGER(right)) ); + //get the comparison + int comparison = Toy_compareValues(left, right); + + //push the result of the comparison as a boolean, based on the opcode + if (opcode == TOY_OPCODE_COMPARE_LESS && comparison < 0) { + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(true)); } - else if (opcode == TOY_OPCODE_COMPARE_LESS_EQUAL) { - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) <= TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) <= TOY_VALUE_AS_INTEGER(right)) ); + else if (opcode == TOY_OPCODE_COMPARE_LESS_EQUAL && (comparison < 0 || comparison == 0)) { + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(true)); } - else if (opcode == TOY_OPCODE_COMPARE_GREATER) { - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) > TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) > TOY_VALUE_AS_INTEGER(right)) ); + else if (opcode == TOY_OPCODE_COMPARE_GREATER && comparison > 0) { + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(true)); } - else if (opcode == TOY_OPCODE_COMPARE_GREATER_EQUAL) { - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) >= TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) >= TOY_VALUE_AS_INTEGER(right)) ); + else if (opcode == TOY_OPCODE_COMPARE_GREATER_EQUAL && (comparison > 0 || comparison == 0)) { + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(true)); } + + //if all else failed, then it's not true else { - fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d passed to processComparison, exiting\n" TOY_CC_RESET, opcode); - exit(-1); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(false)); } } @@ -324,23 +324,23 @@ static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( TOY_VALUE_IS_TRUTHY(left) && TOY_VALUE_IS_TRUTHY(right) )); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( Toy_checkValueIsTruthy(left) && Toy_checkValueIsTruthy(right) )); } else if (opcode == TOY_OPCODE_OR) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( TOY_VALUE_IS_TRUTHY(left) || TOY_VALUE_IS_TRUTHY(right) )); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( Toy_checkValueIsTruthy(left) || Toy_checkValueIsTruthy(right) )); } else if (opcode == TOY_OPCODE_TRUTHY) { Toy_Value top = Toy_popStack(&vm->stack); - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( TOY_VALUE_IS_TRUTHY(top) )); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( Toy_checkValueIsTruthy(top) )); } else if (opcode == TOY_OPCODE_NEGATE) { - Toy_Value top = Toy_popStack(&vm->stack); + Toy_Value top = Toy_popStack(&vm->stack); //bad values are filtered by the parser - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( !TOY_VALUE_IS_TRUTHY(top) )); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN( !Toy_checkValueIsTruthy(top) )); } else { fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d passed to processLogical, exiting\n" TOY_CC_RESET, opcode); @@ -410,12 +410,7 @@ static void processConcat(Toy_VM* vm) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); - if (!TOY_VALUE_IS_STRING(left)) { - Toy_error("Failed to concatenate a value that is not a string"); - return; - } - - if (!TOY_VALUE_IS_STRING(left)) { + if (!TOY_VALUE_IS_STRING(left) || !TOY_VALUE_IS_STRING(right)) { Toy_error("Failed to concatenate a value that is not a string"); return; } diff --git a/tests/cases/test_value.c b/tests/cases/test_value.c index d01c701..f0b48b8 100644 --- a/tests/cases/test_value.c +++ b/tests/cases/test_value.c @@ -5,16 +5,18 @@ #include "toy_string.h" #include +#include -int main() { +int test_value_creation() { //test for the correct size { #if TOY_BITNESS == 64 - if (sizeof(Toy_Value) != 16) { + if (sizeof(Toy_Value) != 16) #else - if (sizeof(Toy_Value) != 8) { + if (sizeof(Toy_Value) != 8) #endif - fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_Value' is an unexpected size in memory\n" TOY_CC_RESET); + { + fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_Value' is an unexpected size in memory, expected %d found %d\n" TOY_CC_RESET, TOY_BITNESS, sizeof(Toy_Value)); return -1; } } @@ -29,34 +31,147 @@ int main() { } } - //test creating values + //test creating booleans { Toy_Value t = TOY_VALUE_FROM_BOOLEAN(true); Toy_Value f = TOY_VALUE_FROM_BOOLEAN(false); - if (!TOY_VALUE_IS_TRUTHY(t) || TOY_VALUE_IS_TRUTHY(f)) { + if (!Toy_checkValueIsTruthy(t) || Toy_checkValueIsTruthy(f)) { fprintf(stderr, TOY_CC_ERROR "ERROR: 'boolean' value failed\n" TOY_CC_RESET); return -1; } } + //test creating strings + { + //setup + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_SMALL); + + Toy_Value greeting = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world!")); + + if (TOY_VALUE_IS_STRING(greeting) == false || + TOY_VALUE_AS_STRING(greeting)->type != TOY_STRING_LEAF || + strcmp(TOY_VALUE_AS_STRING(greeting)->as.leaf.data, "Hello world!") != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: 'string' value failed\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //cleanup + Toy_freeBucket(&bucket); + } + + return 0; +} + +int test_value_equality() { //test value equality { Toy_Value answer = TOY_VALUE_FROM_INTEGER(42); Toy_Value question = TOY_VALUE_FROM_INTEGER(42); Toy_Value nice = TOY_VALUE_FROM_INTEGER(69); - if (!TOY_VALUES_ARE_EQUAL(answer, question)) { - fprintf(stderr, TOY_CC_ERROR "ERROR: equality check failed, expected true\n" TOY_CC_RESET); + if (Toy_checkValuesAreEqual(answer, question) != true) { + fprintf(stderr, TOY_CC_ERROR "ERROR: value equality check failed, expected true\n" TOY_CC_RESET); return -1; } - if (TOY_VALUES_ARE_EQUAL(answer, nice)) { - fprintf(stderr, TOY_CC_ERROR "ERROR: equality check failed, expected false\n" TOY_CC_RESET); + if (Toy_checkValuesAreEqual(answer, nice) != false) { + fprintf(stderr, TOY_CC_ERROR "ERROR: value equality check failed, expected false\n" TOY_CC_RESET); return -1; } } + //again with strings + { + //setup + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_SMALL); + + Toy_Value answer = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both")); + Toy_Value question = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "why is a raven like a writing desk?")); + Toy_Value duplicate = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both")); + + if (Toy_checkValuesAreEqual(answer, duplicate) != true) { + fprintf(stderr, TOY_CC_ERROR "ERROR: string value equality check failed, expected true\n" TOY_CC_RESET); + return -1; + } + + if (Toy_checkValuesAreEqual(answer, question) != false) { + fprintf(stderr, TOY_CC_ERROR "ERROR: string value equality check failed, expected false\n" TOY_CC_RESET); + return -1; + } + + //cleanup + Toy_freeBucket(&bucket); + } + + return 0; +} + +int test_value_comparison() { + //test value comparable + { + Toy_Value answer = TOY_VALUE_FROM_INTEGER(42); + Toy_Value question = TOY_VALUE_FROM_INTEGER(42); + Toy_Value nope = TOY_VALUE_FROM_NULL(); + + if (Toy_checkValuesAreComparable(answer, question) != true) { + fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison check failed, expected true\n" TOY_CC_RESET); + return -1; + } + + if (Toy_checkValuesAreComparable(answer, nope) != false) { + fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison check failed, expected false\n" TOY_CC_RESET); + return -1; + } + } + + //test comparison + { + Toy_Value answer = TOY_VALUE_FROM_INTEGER(42); + Toy_Value question = TOY_VALUE_FROM_INTEGER(42); + Toy_Value nice = TOY_VALUE_FROM_INTEGER(69); + + if (Toy_compareValues(answer, question) != 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison failed, expected 0\n" TOY_CC_RESET); + return -1; + } + + if (Toy_compareValues(answer, nice) == 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison failed, expected not 0\n" TOY_CC_RESET); + return -1; + } + } + + //again with strings + { + //setup + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_SMALL); + + Toy_Value answer = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both")); + Toy_Value question = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "why is a raven like a writing desk?")); + Toy_Value duplicate = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both")); + + if (Toy_compareValues(answer, duplicate) != 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: string value comparison failed, expected 0\n" TOY_CC_RESET); + return -1; + } + + if (Toy_compareValues(answer, question) == 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: string value comparison failed, expected not 0\n" TOY_CC_RESET); + return -1; + } + + //cleanup + Toy_freeBucket(&bucket); + } + + return 0; +} + +int test_value_hashing() { //test value hashing { //setup @@ -87,6 +202,47 @@ int main() { Toy_freeBucket(&bucket); } + //NOTE: string hash is a PITA, skipping + + return 0; +} + +int main() { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_value_creation(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_value_equality(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_value_comparison(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_value_hashing(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); return 0; } diff --git a/tests/integrations/test_variables.toy b/tests/integrations/test_variables.toy index 43af2ef..d042756 100644 --- a/tests/integrations/test_variables.toy +++ b/tests/integrations/test_variables.toy @@ -17,3 +17,39 @@ answer *= 9; answer /= 2; answer %= 10; +//equality checks +print 1 == 1; //true +print 1 != 1; //false + +//comparison checks +print 1 < 2; //true + +print "foo" > "bar"; //true + +print 1 < 2; //true +print 1 > 2; //false + +print 2 <= 2; //true +print 2 >= 2; //true + +print 1 <= 2; //true +print 1 >= 2; //false + +//logical checks +print true && true; //true +print true && false; //false +print false && true; //false +print false && false; //false + +print true || true; //true +print true || false; //true +print false || true; //true +print false || false; //false + +print !true; //false +print !false; //true + +//precedence +print true && false || true; //TODO: a warning is needed for this + +//TODO: type casting \ No newline at end of file