WIP: Implementing arrays into the script, read more

I had intended to solve the Advent of Code puzzles in Toy, but they
don't work without arrays. I wasn't able to enable arrays in time, so
I need to focus on doing things correctly.

The most immediate tasks are marked with 'URGENT', and a lot of tests
need writing.
This commit is contained in:
2024-12-02 11:58:03 +11:00
parent 12c6ac938c
commit 62ca7a1fb7
16 changed files with 599 additions and 241 deletions

View File

@@ -40,3 +40,5 @@ TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
#ifndef TOY_ARRAY_PUSHBACK
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array),(array)->data[(array)->count++] = (value))
#endif
//URGENT: check array length from scripts

View File

@@ -84,13 +84,23 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
(*astHandle) = tmp;
}
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_COMPOUND;
tmp->compound.flag = flag;
tmp->compound.left = *astHandle; //left-recursive
tmp->compound.right = right;
tmp->compound.child = *astHandle;
(*astHandle) = tmp;
}
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_AGGREGATE;
tmp->aggregate.flag = flag;
tmp->aggregate.left = *astHandle; //left-recursive
tmp->aggregate.right = right;
(*astHandle) = tmp;
}

View File

@@ -16,6 +16,7 @@ typedef enum Toy_AstType {
TOY_AST_COMPARE,
TOY_AST_GROUP,
TOY_AST_COMPOUND,
TOY_AST_AGGREGATE,
TOY_AST_ASSERT,
TOY_AST_IF_THEN_ELSE,
@@ -58,8 +59,10 @@ typedef enum Toy_AstFlag {
TOY_AST_FLAG_COMPARE_GREATER = 24,
TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25,
TOY_AST_FLAG_COMPOUND_COLLECTION = 30,
TOY_AST_FLAG_COMPOUND_INDEX = 31,
TOY_AST_FLAG_COMPOUND_ARRAY = 30,
// TOY_AST_FLAG_COMPOUND_TABLE = 31,
TOY_AST_FLAG_COLLECTION = 32,
TOY_AST_FLAG_INDEX = 33,
TOY_AST_FLAG_AND = 40,
TOY_AST_FLAG_OR = 41,
@@ -115,11 +118,17 @@ typedef struct Toy_AstGroup {
} Toy_AstGroup;
typedef struct Toy_AstCompound {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* child;
} Toy_AstCompound;
typedef struct Toy_AstAggregate {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstCompound;
} Toy_AstAggregate;
typedef struct Toy_AstAssert {
Toy_AstType type;
@@ -191,7 +200,8 @@ union Toy_Ast { //32 | 64 BITNESS
Toy_AstBinary binary; //16 | 24
Toy_AstCompare compare; //16 | 24
Toy_AstGroup group; //8 | 16
Toy_AstCompound compound; //16 | 24
Toy_AstCompound compound; //12 | 16
Toy_AstAggregate aggregate; //16 | 24
Toy_AstAssert assert; //16 | 24
Toy_AstIfThenElse ifThenElse; //16 | 32
Toy_AstWhileThen whileThen; //16 | 24
@@ -214,7 +224,8 @@ void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, To
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag);
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);

View File

@@ -117,6 +117,7 @@ static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
//precedence definitions
static ParsingTuple parsingRulesetTable[] = {
@@ -195,7 +196,7 @@ static ParsingTuple parsingRulesetTable[] = {
//structural operators
{PREC_NONE,group,NULL},// TOY_TOKEN_OPERATOR_PAREN_LEFT,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_PAREN_RIGHT,
{PREC_GROUP,NULL,compound},// TOY_TOKEN_OPERATOR_BRACKET_LEFT,
{PREC_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_LEFT,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_BRACKET_RIGHT,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_BRACE_LEFT,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_BRACE_RIGHT,
@@ -208,7 +209,7 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_COLON,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_SEMICOLON, // ;
{PREC_GROUP,NULL,compound},// TOY_TOKEN_OPERATOR_COMMA, // ,
{PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, // ,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_DOT, // .
{PREC_CALL,NULL,binary},// TOY_TOKEN_OPERATOR_CONCAT, // ..
@@ -562,17 +563,16 @@ static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
}
static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
//infix must advance
advance(parser);
//read in an array or dictionary aggregate
if (parser->previous.type == TOY_TOKEN_OPERATOR_COMMA) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive
return TOY_AST_FLAG_COMPOUND_COLLECTION;
}
else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) {
if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of index expression");
return TOY_AST_FLAG_COMPOUND_INDEX;
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of compound expression");
Toy_private_emitAstCompound(bucketHandle, rootHandle, TOY_AST_FLAG_COMPOUND_ARRAY);
return TOY_AST_FLAG_NONE;
//TODO: read in a dictionary
}
else {
printError(parser, parser->previous, "Unexpected token passed to compound precedence rule");
@@ -581,6 +581,31 @@ static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
}
}
static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
//infix must advance
advance(parser);
if (parser->previous.type == TOY_TOKEN_OPERATOR_COMMA) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive
return TOY_AST_FLAG_COLLECTION;
}
else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of index expression");
return TOY_AST_FLAG_INDEX;
}
else {
printError(parser, parser->previous, "Unexpected token passed to aggregate precedence rule");
Toy_private_emitAstError(bucketHandle, rootHandle);
return TOY_AST_FLAG_NONE;
}
}
//TODO: allow trailing commas
// static Toy_AstFlag noop(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
// return return TOY_AST_FLAG_NONE;
// }
static ParsingTuple* getParsingRule(Toy_TokenType type) {
return &parsingRulesetTable[type];
}
@@ -638,7 +663,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
Toy_private_emitAstCompare(bucketHandle, rootHandle, flag, ptr);
}
else if (flag >= 30 && flag <= 39) {
Toy_private_emitAstCompound(bucketHandle, rootHandle, flag, ptr);
Toy_private_emitAstAggregate(bucketHandle, rootHandle, flag, ptr);
}
else {
Toy_private_emitAstBinary(bucketHandle, rootHandle, flag, ptr);
@@ -668,9 +693,9 @@ static void makeAssertStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As
Toy_private_emitAstPass(bucketHandle, rootHandle);
}
else {
//NOTE: if it's a compound, then it's got a second arg
if (ast->type == TOY_AST_COMPOUND) {
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast->compound.left, ast->compound.right);
//NOTE: if it's an aggregate node, then it's got a second arg
if (ast->type == TOY_AST_AGGREGATE) {
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast->aggregate.left, ast->aggregate.right);
}
else {
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast, NULL);

View File

@@ -208,7 +208,7 @@ static unsigned int writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast)
}
//4-byte alignment
EMIT_BYTE(rt, code,TOY_OPCODE_PASS); //checked in compound assignments
EMIT_BYTE(rt, code,TOY_OPCODE_PASS); //checked in combined assignments
EMIT_BYTE(rt, code,0);
EMIT_BYTE(rt, code,0);
@@ -263,17 +263,41 @@ static unsigned int writeInstructionGroup(Toy_Routine** rt, Toy_AstGroup ast) {
}
static unsigned int writeInstructionCompound(Toy_Routine** rt, Toy_AstCompound ast) {
unsigned int result = writeRoutineCode(rt, ast.child);
if (ast.flag == TOY_AST_FLAG_COMPOUND_ARRAY) {
//signal how many values to read in as an array value
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
EMIT_BYTE(rt, code, TOY_VALUE_ARRAY);
//4-byte alignment
EMIT_BYTE(rt, code,0);
EMIT_BYTE(rt, code,0);
//how many elements
EMIT_INT(rt, code, result);
return 1; //leaves only 1 value on the stack
}
else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compound flag found\n" TOY_CC_RESET);
exit(-1);
return 0;
}
}
static unsigned int writeInstructionAggregate(Toy_Routine** rt, Toy_AstAggregate ast) {
unsigned int result = 0;
//left, then right
result += writeRoutineCode(rt, ast.left);
result += writeRoutineCode(rt, ast.right);
if (ast.flag == TOY_AST_FLAG_COMPOUND_COLLECTION) {
if (ast.flag == TOY_AST_FLAG_COLLECTION) {
//collections are handled above
return result;
}
else if (ast.flag == TOY_AST_FLAG_COMPOUND_INDEX) {
else if (ast.flag == TOY_AST_FLAG_INDEX) {
//value[index, length]
EMIT_BYTE(rt, code, TOY_OPCODE_INDEX);
EMIT_BYTE(rt, code, result);
@@ -285,7 +309,7 @@ static unsigned int writeInstructionCompound(Toy_Routine** rt, Toy_AstCompound a
return 1; //leaves only 1 value on the stack
}
else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compound flag found\n" TOY_CC_RESET);
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST aggregate flag found\n" TOY_CC_RESET);
exit(-1);
return 0;
}
@@ -429,10 +453,12 @@ static unsigned int writeInstructionVarDeclare(Toy_Routine** rt, Toy_AstVarDecla
static unsigned int writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign ast) {
unsigned int result = 0;
//URGENT: check for LHS in index
//don't treat these as valid values
switch (ast.expr->type) {
case TOY_AST_BLOCK:
case TOY_AST_COMPOUND:
case TOY_AST_AGGREGATE:
case TOY_AST_ASSERT:
case TOY_AST_PRINT:
case TOY_AST_VAR_DECLARE:
@@ -594,7 +620,7 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
return 0;
}
//NOTE: 'result' is used to in 'writeInstructionCompound()'
//NOTE: 'result' is used to in 'writeInstructionAggregate()'
unsigned int result = 0;
//determine how to write each instruction based on the Ast
@@ -642,6 +668,10 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
result += writeInstructionCompound(rt, ast->compound);
break;
case TOY_AST_AGGREGATE:
result += writeInstructionAggregate(rt, ast->aggregate);
break;
case TOY_AST_ASSERT:
result += writeInstructionAssert(rt, ast->assert);
break;

View File

@@ -7,6 +7,8 @@
#include "toy_print.h"
//URGENT: don't let references get saved into a scope
//utils
static void incrementRefCount(Toy_Scope* scope) {
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
@@ -126,6 +128,8 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value);
}
//TODO: check for clearign old values
void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
if (key->type != TOY_STRING_NAME) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
@@ -161,7 +165,7 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
entryPtr->value = value;
}
Toy_Value Toy_accessScope(Toy_Scope* scope, Toy_String* key) {
Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
if (key->type != TOY_STRING_NAME) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
exit(-1);
@@ -173,10 +177,10 @@ Toy_Value Toy_accessScope(Toy_Scope* scope, Toy_String* key) {
char buffer[key->length + 256];
sprintf(buffer, "Undefined variable: %s\n", key->as.name.data);
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
NULL;
}
return entryPtr->value;
return &(entryPtr->value);
}
bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) {

View File

@@ -23,6 +23,6 @@ TOY_API Toy_Scope* Toy_deepCopyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope
//manage the contents
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
TOY_API Toy_Value Toy_accessScope(Toy_Scope* scope, Toy_String* key);
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);

View File

@@ -18,30 +18,42 @@ static unsigned int hashUInt(unsigned int x) {
}
//exposed functions
Toy_Value Toy_unwrapValue(Toy_Value value) {
//turns out C doesn't have actual references
if (value.type == TOY_VALUE_REFERENCE) {
return Toy_unwrapValue(*(value.as.reference));
}
else {
return value;
}
}
unsigned int Toy_hashValue(Toy_Value value) {
value = Toy_unwrapValue(value);
switch(value.type) {
case TOY_VALUE_NULL:
return 0;
case TOY_VALUE_BOOLEAN:
return TOY_VALUE_AS_BOOLEAN(value) ? 1 : 0;
return value.as.boolean ? 1 : 0;
case TOY_VALUE_INTEGER:
return hashUInt(TOY_VALUE_AS_INTEGER(value));
return hashUInt((unsigned int)value.as.integer);
case TOY_VALUE_FLOAT:
return hashUInt( *((int*)(&TOY_VALUE_AS_FLOAT(value))) );
return hashUInt( *((unsigned int*)(&value.as.number)) );
case TOY_VALUE_STRING:
return Toy_hashString(TOY_VALUE_AS_STRING(value));
return Toy_hashString(value.as.string);
case TOY_VALUE_ARRAY: {
//since array internals can change, recalc the hash each time it's needed
Toy_Array* array = TOY_VALUE_AS_ARRAY(value);
Toy_Array* ptr = value.as.array;
unsigned int hash = 0;
for (unsigned int i = 0; i < array->count; i++) {
hash ^= Toy_hashValue(array->data[i]);
for (unsigned int i = 0; i < ptr->count; i++) {
hash ^= Toy_hashValue(ptr->data[i]);
}
return hash;
@@ -52,6 +64,7 @@ unsigned int Toy_hashValue(Toy_Value value) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't hash an unknown value type, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -61,6 +74,8 @@ unsigned int Toy_hashValue(Toy_Value value) {
}
Toy_Value Toy_copyValue(Toy_Value value) {
value = Toy_unwrapValue(value);
switch(value.type) {
case TOY_VALUE_NULL:
case TOY_VALUE_BOOLEAN:
@@ -69,20 +84,20 @@ Toy_Value Toy_copyValue(Toy_Value value) {
return value;
case TOY_VALUE_STRING: {
Toy_String* string = TOY_VALUE_AS_STRING(value);
return TOY_VALUE_FROM_STRING(Toy_copyString(string));
return TOY_VALUE_FROM_STRING(Toy_copyString(value.as.string));
}
case TOY_VALUE_ARRAY: {
Toy_Array* array = TOY_VALUE_AS_ARRAY(value);
Toy_Array* result = Toy_resizeArray(NULL, array->capacity);
//arrays probably won't get copied much
Toy_Array* ptr = value.as.array;
Toy_Array* result = Toy_resizeArray(NULL, ptr->capacity);
for (unsigned int i = 0; i < array->count; i++) {
result->data[i] = Toy_copyValue(array->data[i]);
for (unsigned int i = 0; i < ptr->count; i++) {
result->data[i] = Toy_copyValue(ptr->data[i]);
}
result->capacity = array->capacity;
result->count = array->count;
result->capacity = ptr->capacity;
result->count = ptr->count;
return TOY_VALUE_FROM_ARRAY(result);
}
@@ -92,6 +107,7 @@ Toy_Value Toy_copyValue(Toy_Value value) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't copy an unknown value type, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -102,6 +118,8 @@ Toy_Value Toy_copyValue(Toy_Value value) {
}
void Toy_freeValue(Toy_Value value) {
//NOTE: do not unwrap this value, as references shouldn't be freed
switch(value.type) {
case TOY_VALUE_NULL:
case TOY_VALUE_BOOLEAN:
@@ -110,19 +128,18 @@ void Toy_freeValue(Toy_Value value) {
break;
case TOY_VALUE_STRING: {
Toy_String* string = TOY_VALUE_AS_STRING(value);
Toy_freeString(string);
Toy_freeString(value.as.string);
break;
}
case TOY_VALUE_ARRAY: {
Toy_Array* array = TOY_VALUE_AS_ARRAY(value);
Toy_Array* ptr = value.as.array;
for (unsigned int i = 0; i < array->count; i++) {
Toy_freeValue(array->data[i]);
for (unsigned int i = 0; i < ptr->count; i++) {
Toy_freeValue(ptr->data[i]);
}
TOY_ARRAY_FREE(array);
TOY_ARRAY_FREE(ptr);
break;
}
@@ -131,6 +148,7 @@ void Toy_freeValue(Toy_Value value) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't free an unknown value type, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -138,15 +156,17 @@ void Toy_freeValue(Toy_Value value) {
}
bool Toy_checkValueIsTruthy(Toy_Value value) {
value = Toy_unwrapValue(value);
//null is an error
if (TOY_VALUE_IS_NULL(value)) {
if (value.type == TOY_VALUE_NULL) {
Toy_error("'null' is neither true nor false");
return false;
}
//only 'false' is falsy
if (TOY_VALUE_IS_BOOLEAN(value)) {
return TOY_VALUE_AS_BOOLEAN(value);
if (value.type == TOY_VALUE_BOOLEAN) {
return value.as.boolean;
}
//anything else is truthy
@@ -154,57 +174,65 @@ bool Toy_checkValueIsTruthy(Toy_Value value) {
}
bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
left = Toy_unwrapValue(left);
right = Toy_unwrapValue(right);
switch(left.type) {
case TOY_VALUE_NULL:
return TOY_VALUE_IS_NULL(right);
return right.type == TOY_VALUE_NULL;
case TOY_VALUE_BOOLEAN:
return TOY_VALUE_IS_BOOLEAN(right) && TOY_VALUE_AS_BOOLEAN(left) == TOY_VALUE_AS_BOOLEAN(right);
return right.type == TOY_VALUE_NULL && left.as.boolean == right.as.boolean;
case TOY_VALUE_INTEGER:
if (TOY_VALUE_IS_INTEGER(right)) {
return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_INTEGER(right);
if (right.type == TOY_VALUE_INTEGER) {
return left.as.integer == right.as.integer;
}
else if (TOY_VALUE_IS_FLOAT(right)) {
return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_FLOAT(right);
else if (right.type == TOY_VALUE_FLOAT) {
return left.as.integer == right.as.number;
}
else {
break;
}
case TOY_VALUE_FLOAT:
if (TOY_VALUE_IS_INTEGER(right)) {
return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_INTEGER(right);
if (right.type == TOY_VALUE_INTEGER) {
return left.as.number == right.as.integer;
}
else if (TOY_VALUE_IS_FLOAT(right)) {
return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_FLOAT(right);
else if (right.type == TOY_VALUE_FLOAT) {
return left.as.number == right.as.number;
}
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;
if (right.type == TOY_VALUE_STRING) {
return Toy_compareStrings(left.as.string, right.as.string) == 0;
}
else {
break;
}
case TOY_VALUE_ARRAY: {
Toy_Array* leftArray = TOY_VALUE_AS_ARRAY(left);
Toy_Array* rightArray = TOY_VALUE_AS_ARRAY(right);
if (right.type == TOY_VALUE_ARRAY) {
Toy_Array* leftArray = left.as.array;
Toy_Array* rightArray = right.as.array;
//different lengths is an easy way to check
if (leftArray->count != rightArray->count) {
return false;
}
for (unsigned int i = 0; i < leftArray->count; i++) {
//any mismatch is an easy difference
if (Toy_checkValuesAreEqual(leftArray->data[i], rightArray->data[i])) {
//different lengths is an easy way to check
if (leftArray->count != rightArray->count) {
return false;
}
for (unsigned int i = 0; i < leftArray->count; i++) {
//any mismatch is an easy difference
if (Toy_checkValuesAreEqual(leftArray->data[i], rightArray->data[i])) {
return false;
}
}
}
else {
break;
}
//finally
@@ -216,6 +244,7 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown types in value equality, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -225,21 +254,23 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
}
bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) {
//NOTE: "equal" and "comparable" are different - equal means they're identical, comparable is only possible for certain types
left = Toy_unwrapValue(left);
right = Toy_unwrapValue(right);
//NOTE: "equal" and "comparable" are different - equal means they're identical, comparable is only possible for certain types
switch(left.type) {
case TOY_VALUE_NULL:
return false;
case TOY_VALUE_BOOLEAN:
return TOY_VALUE_IS_BOOLEAN(right);
return right.type == TOY_VALUE_BOOLEAN;
case TOY_VALUE_INTEGER:
case TOY_VALUE_FLOAT:
return TOY_VALUE_IS_INTEGER(right) || TOY_VALUE_IS_FLOAT(right);
return right.type == TOY_VALUE_INTEGER || right.type == TOY_VALUE_FLOAT;
case TOY_VALUE_STRING:
return TOY_VALUE_IS_STRING(right);
return right.type == TOY_VALUE_STRING;
case TOY_VALUE_ARRAY:
//nothing is comparable with an array
@@ -250,6 +281,7 @@ bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "Unknown types in value comparison check, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -259,6 +291,9 @@ bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) {
}
int Toy_compareValues(Toy_Value left, Toy_Value right) {
left = Toy_unwrapValue(left);
right = Toy_unwrapValue(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:
@@ -266,30 +301,30 @@ int Toy_compareValues(Toy_Value left, Toy_Value right) {
break;
case TOY_VALUE_INTEGER:
if (TOY_VALUE_IS_INTEGER(right)) {
return TOY_VALUE_AS_INTEGER(left) - TOY_VALUE_AS_INTEGER(right);
if (right.type == TOY_VALUE_INTEGER) {
return left.as.integer - right.as.integer;
}
else if (TOY_VALUE_IS_FLOAT(right)) {
return TOY_VALUE_AS_INTEGER(left) - TOY_VALUE_AS_FLOAT(right);
else if (right.type == TOY_VALUE_FLOAT) {
return left.as.integer - right.as.number;
}
else {
break;
}
case TOY_VALUE_FLOAT:
if (TOY_VALUE_IS_INTEGER(right)) {
return TOY_VALUE_AS_FLOAT(left) - TOY_VALUE_AS_INTEGER(right);
if (right.type == TOY_VALUE_INTEGER) {
return left.as.number - right.as.integer;
}
else if (TOY_VALUE_IS_FLOAT(right)) {
return TOY_VALUE_AS_FLOAT(left) - TOY_VALUE_AS_FLOAT(right);
else if (right.type == TOY_VALUE_FLOAT) {
return left.as.number - right.as.number;
}
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));
if (right.type == TOY_VALUE_STRING) {
return Toy_compareStrings(left.as.string, right.as.string);
}
case TOY_VALUE_ARRAY:
@@ -300,15 +335,20 @@ int Toy_compareValues(Toy_Value left, Toy_Value right) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "Unknown types in value comparison, exiting\n" TOY_CC_RESET);
exit(-1);
break;
}
return -1;
fprintf(stderr, TOY_CC_ERROR "Unknown types in value comparison, exiting\n" TOY_CC_RESET);
exit(-1);
return ~0;
}
Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
value = Toy_unwrapValue(value);
//TODO: could have "constant" strings that can be referenced, instead of null, true, false, etc.
switch(value.type) {
@@ -316,18 +356,18 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
return Toy_createString(bucketHandle, "null");
case TOY_VALUE_BOOLEAN:
return Toy_createString(bucketHandle, TOY_VALUE_AS_BOOLEAN(value) ? "true" : "false");
return Toy_createString(bucketHandle, value.as.boolean ? "true" : "false");
case TOY_VALUE_INTEGER: {
char buffer[16];
sprintf(buffer, "%d", TOY_VALUE_AS_INTEGER(value));
sprintf(buffer, "%d", value.as.integer);
return Toy_createString(bucketHandle, buffer);
}
case TOY_VALUE_FLOAT: {
//using printf
char buffer[16];
sprintf(buffer, "%f", TOY_VALUE_AS_FLOAT(value));
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
unsigned int length = strlen(buffer);
@@ -343,28 +383,33 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
}
case TOY_VALUE_STRING:
return Toy_copyString(TOY_VALUE_AS_STRING(value));
return Toy_copyString(value.as.string);
case TOY_VALUE_ARRAY: {
//TODO: concat + free is definitely a performance nightmare
Toy_Array* array = TOY_VALUE_AS_ARRAY(value);
//TODO: concat + free is definitely a performance nightmare, could make an append function?
Toy_Array* ptr = value.as.array;
Toy_String* string = Toy_createStringLength(bucketHandle, "[", 1);
Toy_String* comma = Toy_createStringLength(bucketHandle, ",", 1); //reusable
for (unsigned int i = 0; i < array->count; i++) {
for (unsigned int i = 0; i < ptr->count; i++) {
//append each element
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, Toy_stringifyValue(bucketHandle, array->data[i])); //increment ref
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, Toy_stringifyValue(bucketHandle, ptr->data[i])); //increment ref
Toy_freeString(string); //decrement ref
string = tmp;
//if we need a comma
if (i + 1 < array->count) {
if (i + 1 < ptr->count) {
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref
Toy_freeString(string); //decrement ref
string = tmp;
}
}
//closing bracket
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, Toy_createStringLength(bucketHandle, "]", 1));
Toy_freeString(string);
string = tmp;
//clean up
Toy_freeString(comma); //TODO: reusable global, or string type "permanent"
@@ -376,6 +421,7 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
fprintf(stderr, TOY_CC_ERROR "Unknown types in value stringify, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -397,6 +443,7 @@ const char* Toy_private_getValueTypeAsCString(Toy_ValueType type) {
case TOY_VALUE_OPAQUE: return "opaque";
case TOY_VALUE_TYPE: return "type";
case TOY_VALUE_ANY: return "any";
case TOY_VALUE_REFERENCE: return "reference";
case TOY_VALUE_UNKNOWN: return "unknown";
}

View File

@@ -20,39 +20,45 @@ typedef enum Toy_ValueType {
TOY_VALUE_OPAQUE,
TOY_VALUE_TYPE,
TOY_VALUE_ANY,
TOY_VALUE_REFERENCE, //not a value itself, but pointing to one
TOY_VALUE_UNKNOWN, //The correct type is unknown, but will be determined later
} Toy_ValueType;
//8 bytes in size
typedef struct Toy_Value { //32 | 64 BITNESS
typedef struct Toy_Value { //32 | 64 BITNESS
union {
bool boolean; //1 | 1
int integer; //4 | 4
float number; //4 | 4
struct Toy_String* string; //4 | 8
struct Toy_Array* array; //4 | 8
struct Toy_Value* reference; //4 | 8
bool boolean; //1 | 1
int integer; //4 | 4
float number; //4 | 4
struct Toy_String* string; //4 | 8
struct Toy_Array* array; //4 | 8
//TODO: more types go here
//TODO: consider 'stack' as a possible addition
} as; //4 | 8
Toy_ValueType type; //4 | 4
} Toy_Value; //8 | 16
} as; //4 | 8
#define TOY_VALUE_IS_NULL(value) ((value).type == TOY_VALUE_NULL)
#define TOY_VALUE_IS_BOOLEAN(value) ((value).type == TOY_VALUE_BOOLEAN)
#define TOY_VALUE_IS_INTEGER(value) ((value).type == TOY_VALUE_INTEGER)
#define TOY_VALUE_IS_FLOAT(value) ((value).type == TOY_VALUE_FLOAT)
#define TOY_VALUE_IS_STRING(value) ((value).type == TOY_VALUE_STRING)
#define TOY_VALUE_IS_ARRAY(value) ((value).type == TOY_VALUE_ARRAY)
#define TOY_VALUE_IS_TABLE(value) ((value).type == TOY_VALUE_TABLE)
#define TOY_VALUE_IS_FUNCTION(value) ((value).type == TOY_VALUE_FUNCTION)
#define TOY_VALUE_IS_OPAQUE(value) ((value).type == TOY_VALUE_OPAQUE)
Toy_ValueType type; //4 | 4
} Toy_Value; //8 | 16
#define TOY_VALUE_AS_BOOLEAN(value) ((value).as.boolean)
#define TOY_VALUE_AS_INTEGER(value) ((value).as.integer)
#define TOY_VALUE_AS_FLOAT(value) ((value).as.number)
#define TOY_VALUE_AS_STRING(value) ((value).as.string)
#define TOY_VALUE_AS_ARRAY(value) ((value).as.array)
#define TOY_VALUE_IS_NULL(value) (Toy_unwrapValue(value).type == TOY_VALUE_NULL)
#define TOY_VALUE_IS_BOOLEAN(value) (Toy_unwrapValue(value).type == TOY_VALUE_BOOLEAN)
#define TOY_VALUE_IS_INTEGER(value) (Toy_unwrapValue(value).type == TOY_VALUE_INTEGER)
#define TOY_VALUE_IS_FLOAT(value) (Toy_unwrapValue(value).type == TOY_VALUE_FLOAT)
#define TOY_VALUE_IS_STRING(value) (Toy_unwrapValue(value).type == TOY_VALUE_STRING)
#define TOY_VALUE_IS_ARRAY(value) (Toy_unwrapValue(value).type == TOY_VALUE_ARRAY)
#define TOY_VALUE_IS_TABLE(value) (Toy_unwrapValue(value).type == TOY_VALUE_TABLE)
#define TOY_VALUE_IS_FUNCTION(value) (Toy_unwrapValue(value).type == TOY_VALUE_FUNCTION)
#define TOY_VALUE_IS_OPAQUE(value) (Toy_unwrapValue(value).type == TOY_VALUE_OPAQUE)
#define TOY_VALUE_IS_TYPE(value) (Toy_unwrapValue(value).type == TOY_VALUE_TYPE)
#define TOY_VALUE_IS_REFERENCE(value) ((value).type == TOY_VALUE_REFERENCE)
#define TOY_VALUE_AS_BOOLEAN(value) (Toy_unwrapValue(value).as.boolean)
#define TOY_VALUE_AS_INTEGER(value) (Toy_unwrapValue(value).as.integer)
#define TOY_VALUE_AS_FLOAT(value) (Toy_unwrapValue(value).as.number)
#define TOY_VALUE_AS_STRING(value) (Toy_unwrapValue(value).as.string)
#define TOY_VALUE_AS_ARRAY(value) (Toy_unwrapValue(value).as.array)
//TODO: more
#define TOY_VALUE_FROM_NULL() ((Toy_Value){{ .integer = 0 }, TOY_VALUE_NULL})
@@ -63,7 +69,10 @@ typedef struct Toy_Value { //32 | 64 BITNESS
#define TOY_VALUE_FROM_ARRAY(value) ((Toy_Value){{ .array = value }, TOY_VALUE_ARRAY})
//TODO: more
#define TOY_REFERENCE_FROM_POINTER(ptr) ((Toy_Value){{ .reference = ptr }, TOY_VALUE_REFERENCE})
//utilities
TOY_API Toy_Value Toy_unwrapValue(Toy_Value value);
TOY_API unsigned int Toy_hashValue(Toy_Value value);
TOY_API Toy_Value Toy_copyValue(Toy_Value value);

View File

@@ -5,6 +5,7 @@
#include "toy_opcodes.h"
#include "toy_value.h"
#include "toy_string.h"
#include "toy_array.h"
#include <stdio.h>
#include <stdlib.h>
@@ -90,8 +91,34 @@ static void processRead(Toy_VM* vm) {
}
case TOY_VALUE_ARRAY: {
//
// break;
fixAlignment(vm);
//the number of values to read from the stack
unsigned int count = (unsigned int)READ_INT(vm);
unsigned int capacity = count > TOY_ARRAY_INITIAL_CAPACITY ? count : TOY_ARRAY_INITIAL_CAPACITY;
//neat trick to find the next power of two, inclusive (restriction of the array system) TODO: move this into a function
capacity--;
capacity |= capacity >> 1;
capacity |= capacity >> 2;
capacity |= capacity >> 4;
capacity |= capacity >> 8;
capacity |= capacity >> 16;
capacity++;
//create the array and read in the values
Toy_Array* array = Toy_resizeArray(NULL, capacity);
array->capacity = capacity;
array->count = count;
for (int i = count - 1; i >= 0; i--) { //read in backwards from the stack
array->data[i] = Toy_popStack(&vm->stack);
}
//finished
value = TOY_VALUE_FROM_ARRAY(array);
break;
}
case TOY_VALUE_TABLE: {
@@ -174,6 +201,8 @@ static void processAssign(Toy_VM* vm) {
//assign it
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value);
//URGENT: complex assignments
//cleanup
Toy_freeValue(name);
}
@@ -187,9 +216,20 @@ static void processAccess(Toy_VM* vm) {
return;
}
//find and push the value
Toy_Value value = Toy_accessScope(vm->scope, TOY_VALUE_AS_STRING(name));
Toy_pushStack(&vm->stack, Toy_copyValue(value));
//find the value
Toy_Value* valuePtr = Toy_accessScopeAsPointer(vm->scope, TOY_VALUE_AS_STRING(name));
//in the event of a certain subset of types, create references instead (these should only exist on the stack)
if (TOY_VALUE_IS_REFERENCE(*valuePtr) || TOY_VALUE_IS_ARRAY(*valuePtr)) {
//TODO: more types to be implemented
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(valuePtr);
Toy_pushStack(&vm->stack, ref);
}
else {
Toy_pushStack(&vm->stack, Toy_copyValue(*valuePtr));
}
//cleanup
Toy_freeValue(name);
@@ -198,7 +238,6 @@ static void processAccess(Toy_VM* vm) {
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);
@@ -216,8 +255,15 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
char buffer[256];
snprintf(buffer, 256, "Invalid types '%s' and '%s' passed in arithmetic", Toy_private_getValueTypeAsCString(left.type), Toy_private_getValueTypeAsCString(right.type));
Toy_error(buffer);
Toy_freeValue(left);
Toy_freeValue(right);
if (TOY_VALUE_IS_REFERENCE(left) != true) {
Toy_freeValue(left);
}
if (TOY_VALUE_IS_REFERENCE(right) != true) {
Toy_freeValue(right);
}
return;
}
@@ -297,17 +343,30 @@ static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(!equal) );
}
Toy_freeValue(left);
Toy_freeValue(right);
if (TOY_VALUE_IS_REFERENCE(left) != true) {
Toy_freeValue(left);
}
if (TOY_VALUE_IS_REFERENCE(right) != true) {
Toy_freeValue(right);
}
return;
}
if (Toy_checkValuesAreComparable(left, right) == false) {
if (Toy_checkValuesAreComparable(left, right) != true) {
char buffer[256];
snprintf(buffer, 256, "Can't compare value types '%s' and '%s'", Toy_private_getValueTypeAsCString(left.type), Toy_private_getValueTypeAsCString(right.type));
Toy_error(buffer);
Toy_freeValue(left);
Toy_freeValue(right);
if (TOY_VALUE_IS_REFERENCE(left) != true) {
Toy_freeValue(left);
}
if (TOY_VALUE_IS_REFERENCE(right) != true) {
Toy_freeValue(right);
}
return;
}
@@ -333,8 +392,13 @@ static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(false));
}
Toy_freeValue(left);
Toy_freeValue(right);
if (TOY_VALUE_IS_REFERENCE(left) != true) {
Toy_freeValue(left);
}
if (TOY_VALUE_IS_REFERENCE(right) != true) {
Toy_freeValue(right);
}
}
static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) {
@@ -382,20 +446,29 @@ static void processJump(Toy_VM* vm) {
case TOY_OP_PARAM_JUMP_IF_TRUE: {
Toy_Value value = Toy_popStack(&vm->stack);
if (Toy_checkValueIsTruthy(value) == true) {
Toy_freeValue(value);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
break;
}
Toy_freeValue(value);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
return;
}
case TOY_OP_PARAM_JUMP_IF_FALSE: {
Toy_Value value = Toy_popStack(&vm->stack);
if (Toy_checkValueIsTruthy(value) != true) {
Toy_freeValue(value);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
break;
}
Toy_freeValue(value);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
return;
}
}
@@ -433,7 +506,7 @@ static void processAssert(Toy_VM* vm) {
}
//do the check
if (TOY_VALUE_IS_NULL(value) || Toy_checkValueIsTruthy(value) == false) {
if (TOY_VALUE_IS_NULL(value) || Toy_checkValueIsTruthy(value) != true) {
//on a failure, print the message
Toy_String* string = Toy_stringifyValue(&vm->stringBucket, message);
char* buffer = Toy_getStringRawBuffer(string);
@@ -446,8 +519,13 @@ static void processAssert(Toy_VM* vm) {
}
//cleanup
Toy_freeValue(value);
Toy_freeValue(message);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(message) != true) {
Toy_freeValue(message);
}
}
static void processPrint(Toy_VM* vm) {
@@ -460,7 +538,10 @@ static void processPrint(Toy_VM* vm) {
free(buffer);
Toy_freeString(string);
Toy_freeValue(value);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
}
static void processConcat(Toy_VM* vm) {
@@ -469,6 +550,13 @@ static void processConcat(Toy_VM* vm) {
if (!TOY_VALUE_IS_STRING(left) || !TOY_VALUE_IS_STRING(right)) {
Toy_error("Failed to concatenate a value that is not a string");
if (TOY_VALUE_IS_REFERENCE(left) != true) {
Toy_freeValue(left);
}
if (TOY_VALUE_IS_REFERENCE(right) != true) {
Toy_freeValue(right);
}
return;
}
@@ -495,7 +583,7 @@ static void processIndex(Toy_VM* vm) {
}
else {
Toy_error("Incorrect number of elements found in index");
//TODO: clear stack
//URGENT: clear stack, then leave null
return;
}
@@ -504,26 +592,53 @@ static void processIndex(Toy_VM* vm) {
//type checks
if (!TOY_VALUE_IS_INTEGER(index)) {
Toy_error("Failed to index a string");
Toy_freeValue(value);
Toy_freeValue(index);
Toy_freeValue(length);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
return;
}
if (!(TOY_VALUE_IS_NULL(length) || TOY_VALUE_IS_INTEGER(length))) {
Toy_error("Failed to index-length a string");
Toy_freeValue(value);
Toy_freeValue(index);
Toy_freeValue(length);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
return;
}
//extract values
int i = TOY_VALUE_AS_INTEGER(index);
int l = TOY_VALUE_IS_INTEGER(length) ? TOY_VALUE_AS_INTEGER(length) : 1;
Toy_String* str = TOY_VALUE_AS_STRING(value);
//check indexing is within bounds
if ( (i < 0 || i >= str->length) || (i+l <= 0 || i+l > str->length)) {
Toy_error("String index is out of bounds");
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
return;
}
//extract string
Toy_String* str = TOY_VALUE_AS_STRING(value);
Toy_String* result = NULL;
//extract cstring, based on type
@@ -545,14 +660,83 @@ static void processIndex(Toy_VM* vm) {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_STRING(result));
}
else if (TOY_VALUE_IS_ARRAY(value)) {
//type checks
if (!TOY_VALUE_IS_INTEGER(index)) {
Toy_error("Failed to index a string");
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
return;
}
if (!(TOY_VALUE_IS_NULL(length) || TOY_VALUE_IS_INTEGER(length))) {
Toy_error("Failed to index-length a string");
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
return;
}
//extract values
int i = TOY_VALUE_AS_INTEGER(index);
int l = TOY_VALUE_IS_INTEGER(length) ? TOY_VALUE_AS_INTEGER(length) : 1;
Toy_Array* array = TOY_VALUE_AS_ARRAY(value);
//check indexing is within bounds
if ( (i < 0 || i >= array->count) || (i+l <= 0 || i+l > array->count)) {
Toy_error("Array index is out of bounds");
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
return;
}
//in the event of a certain subset of types, create references instead (these should only exist on the stack)
if (TOY_VALUE_IS_REFERENCE(array->data[i]) || TOY_VALUE_IS_ARRAY(array->data[i])) {
//TODO: more types to be implemented
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(&(array->data[i]));
Toy_pushStack(&vm->stack, ref);
}
else {
Toy_pushStack(&vm->stack, Toy_copyValue(array->data[i]));
}
}
else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type '%s' found in processIndex, exiting\n" TOY_CC_RESET, Toy_private_getValueTypeAsCString(value.type));
exit(-1);
}
Toy_freeValue(value);
Toy_freeValue(index);
Toy_freeValue(length);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
if (TOY_VALUE_IS_REFERENCE(index) != true) {
Toy_freeValue(index);
}
if (TOY_VALUE_IS_REFERENCE(length) != true) {
Toy_freeValue(length);
}
}
static void process(Toy_VM* vm) {