Reworked variable equality and comparisons

Fixed #146
This commit is contained in:
2024-10-30 19:58:55 +11:00
parent b30a092ab8
commit d19ca1bcee
8 changed files with 447 additions and 153 deletions

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 <stdio.h>
#include <stdlib.h>
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;
}

View File

@@ -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);

View File

@@ -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;
}