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

@@ -341,6 +341,8 @@ static void debugStackPrint(Toy_Stack* stack) {
printf("%s\t", Toy_private_getValueTypeAsCString(v.type));
v = Toy_unwrapValue(v);
switch(v.type) {
case TOY_VALUE_NULL:
printf("null");
@@ -382,6 +384,7 @@ static void debugStackPrint(Toy_Stack* stack) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
printf("???");
break;
@@ -406,6 +409,9 @@ static void debugScopePrint(Toy_Scope* scope, int depth) {
printf("%s\t%s\t", Toy_private_getValueTypeAsCString(v.type), TOY_VALUE_AS_STRING(k)->as.name.data);
k = Toy_unwrapValue(k);
v = Toy_unwrapValue(v);
switch(v.type) {
case TOY_VALUE_NULL:
printf("null");
@@ -447,6 +453,7 @@ static void debugScopePrint(Toy_Scope* scope, int depth) {
case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE:
case TOY_VALUE_ANY:
case TOY_VALUE_REFERENCE:
case TOY_VALUE_UNKNOWN:
printf("???");
break;

27
scripts/advent_1a.toy Normal file
View File

@@ -0,0 +1,27 @@
//advent of code thingy
var arr = [
3, 4,
4, 3,
2, 5,
1, 3,
3, 9,
3 , 3
];
var total = 0;
var counter = 0;
while (counter < arr.length) {
var difference = arr[counter] - arr[counter+1];
if (difference < 0) {
difference = -difference;
}
total += difference;
counter += 2;
}
print difference;

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,46 +174,50 @@ 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) {
@@ -206,6 +230,10 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
return false;
}
}
}
else {
break;
}
//finally
return true;
@@ -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,12 +20,15 @@ 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
union {
struct Toy_Value* reference; //4 | 8
bool boolean; //1 | 1
int integer; //4 | 4
float number; //4 | 4
@@ -33,26 +36,29 @@ typedef struct Toy_Value { //32 | 64 BITNESS
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
#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)
#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) ((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_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);
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) );
}
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);
if (TOY_VALUE_IS_REFERENCE(left) != true) {
Toy_freeValue(left);
}
if (TOY_VALUE_IS_REFERENCE(right) != true) {
Toy_freeValue(right);
}
return;
}
@@ -333,9 +392,14 @@ static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_BOOLEAN(false));
}
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) {
if (opcode == TOY_OPCODE_AND) {
@@ -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) {
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
break;
}
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) {
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
break;
}
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,9 +519,14 @@ static void processAssert(Toy_VM* vm) {
}
//cleanup
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) {
//print the value on top of the stack, popping it
@@ -460,8 +538,11 @@ static void processPrint(Toy_VM* vm) {
free(buffer);
Toy_freeString(string);
if (TOY_VALUE_IS_REFERENCE(value) != true) {
Toy_freeValue(value);
}
}
static void processConcat(Toy_VM* vm) {
Toy_Value right = Toy_popStack(&vm->stack);
@@ -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");
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_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,15 +660,84 @@ 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);
}
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) {
while(true) {

View File

@@ -23,7 +23,8 @@ int test_sizeof_ast_64bit() {
TEST_SIZEOF(Toy_AstBinary, 24);
TEST_SIZEOF(Toy_AstCompare, 24);
TEST_SIZEOF(Toy_AstGroup, 16);
TEST_SIZEOF(Toy_AstCompound, 24);
TEST_SIZEOF(Toy_AstCompound, 16);
TEST_SIZEOF(Toy_AstAggregate, 24);
TEST_SIZEOF(Toy_AstAssert, 24);
TEST_SIZEOF(Toy_AstIfThenElse, 32);
TEST_SIZEOF(Toy_AstWhileThen, 24);
@@ -61,7 +62,8 @@ int test_sizeof_ast_32bit() {
TEST_SIZEOF(Toy_AstBinary, 16);
TEST_SIZEOF(Toy_AstCompare, 16);
TEST_SIZEOF(Toy_AstGroup, 8);
TEST_SIZEOF(Toy_AstCompound, 16);
TEST_SIZEOF(Toy_AstCompound, 12);
TEST_SIZEOF(Toy_AstAggregate, 16);
TEST_SIZEOF(Toy_AstAssert, 12);
TEST_SIZEOF(Toy_AstIfThenElse, 16);
TEST_SIZEOF(Toy_AstWhileThen, 12);
@@ -194,33 +196,33 @@ int test_type_emission(Toy_Bucket** bucketHandle) {
}
}
//emit compound
//emit aggregate
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstCompound(bucketHandle, &ast, TOY_AST_FLAG_COMPOUND_COLLECTION, right);
Toy_private_emitAstAggregate(bucketHandle, &ast, TOY_AST_FLAG_COLLECTION, right);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_COMPOUND ||
ast->type != TOY_AST_AGGREGATE ||
ast->compound.left == NULL ||
ast->compound.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->compound.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->compound.left->value.value) != 42 ||
ast->aggregate.left == NULL ||
ast->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->aggregate.left->value.value) != 42 ||
ast->compound.right == NULL ||
ast->compound.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->compound.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->compound.right->value.value) != 69 ||
ast->aggregate.right == NULL ||
ast->aggregate.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->aggregate.right->value.value) != 69 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a compound as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an aggregate as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}

View File

@@ -455,7 +455,7 @@ int test_binary(Toy_Bucket** bucketHandle) {
return 0;
}
int test_compound(Toy_Bucket** bucketHandle) {
int test_aggregate(Toy_Bucket** bucketHandle) {
//test collections (do it first, because it's used below)
{
char* source = "1, 2, 3;";
@@ -467,27 +467,27 @@ int test_compound(Toy_Bucket** bucketHandle) {
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_COMPOUND ||
ast->block.child->compound.flag != TOY_AST_FLAG_COMPOUND_COLLECTION ||
ast->block.child->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.flag != TOY_AST_FLAG_COLLECTION ||
ast->block.child->compound.left == NULL ||
ast->block.child->compound.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->compound.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->compound.left->value.value) != 1 ||
ast->block.child->aggregate.left == NULL ||
ast->block.child->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.left->value.value) != 1 ||
ast->block.child->compound.right == NULL ||
ast->block.child->compound.right->type != TOY_AST_COMPOUND ||
ast->block.child->compound.right->compound.flag != TOY_AST_FLAG_COMPOUND_COLLECTION ||
ast->block.child->aggregate.right == NULL ||
ast->block.child->aggregate.right->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.right->aggregate.flag != TOY_AST_FLAG_COLLECTION ||
ast->block.child->compound.right->compound.left == NULL ||
ast->block.child->compound.right->compound.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->compound.right->compound.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->compound.right->compound.left->value.value) != 2 ||
ast->block.child->aggregate.right->aggregate.left == NULL ||
ast->block.child->aggregate.right->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != 2 ||
ast->block.child->compound.right->compound.right == NULL ||
ast->block.child->compound.right->compound.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->compound.right->compound.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->compound.right->compound.right->value.value) != 3 ||
ast->block.child->aggregate.right->aggregate.right == NULL ||
ast->block.child->aggregate.right->aggregate.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != 3 ||
false)
{
@@ -507,17 +507,17 @@ int test_compound(Toy_Bucket** bucketHandle) {
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_COMPOUND ||
ast->block.child->compound.flag != TOY_AST_FLAG_COMPOUND_INDEX ||
ast->block.child->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.flag != TOY_AST_FLAG_INDEX ||
ast->block.child->compound.left == NULL ||
ast->block.child->compound.left->type != TOY_AST_VAR_ACCESS ||
ast->block.child->compound.left->varAccess.name->type != TOY_STRING_NAME ||
strcmp(ast->block.child->compound.left->varAccess.name->as.name.data, "name") != 0 ||
ast->block.child->aggregate.left == NULL ||
ast->block.child->aggregate.left->type != TOY_AST_VAR_ACCESS ||
ast->block.child->aggregate.left->varAccess.name->type != TOY_STRING_NAME ||
strcmp(ast->block.child->aggregate.left->varAccess.name->as.name.data, "name") != 0 ||
ast->block.child->compound.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->compound.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->compound.right->value.value) != 0 ||
ast->block.child->aggregate.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->value.value) != 0 ||
false)
{
@@ -537,23 +537,23 @@ int test_compound(Toy_Bucket** bucketHandle) {
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_COMPOUND ||
ast->block.child->compound.flag != TOY_AST_FLAG_COMPOUND_INDEX ||
ast->block.child->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.flag != TOY_AST_FLAG_INDEX ||
ast->block.child->compound.left == NULL ||
ast->block.child->compound.left->type != TOY_AST_VAR_ACCESS ||
ast->block.child->compound.left->varAccess.name->type != TOY_STRING_NAME ||
strcmp(ast->block.child->compound.left->varAccess.name->as.name.data, "name") != 0 ||
ast->block.child->aggregate.left == NULL ||
ast->block.child->aggregate.left->type != TOY_AST_VAR_ACCESS ||
ast->block.child->aggregate.left->varAccess.name->type != TOY_STRING_NAME ||
strcmp(ast->block.child->aggregate.left->varAccess.name->as.name.data, "name") != 0 ||
ast->block.child->compound.right->type != TOY_AST_COMPOUND ||
ast->block.child->compound.right->compound.flag != TOY_AST_FLAG_COMPOUND_COLLECTION ||
ast->block.child->compound.right->compound.left->type != TOY_AST_VALUE ||
ast->block.child->aggregate.right->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.right->aggregate.flag != TOY_AST_FLAG_COLLECTION ||
ast->block.child->aggregate.right->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->compound.right->compound.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->compound.right->compound.left->value.value) != 0 ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != 0 ||
TOY_VALUE_IS_INTEGER(ast->block.child->compound.right->compound.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->compound.right->compound.right->value.value) != 1 ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != 1 ||
false)
{
@@ -927,7 +927,7 @@ int main() {
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_compound(&bucket);
res = test_aggregate(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);

View File

@@ -329,7 +329,7 @@ int test_scope_elements() {
return -1;
}
Toy_Value result = Toy_accessScope(scope, hello2);
Toy_Value* result = Toy_accessScopeAsPointer(scope, hello2);
//check integer
if (scope == NULL ||
@@ -338,8 +338,8 @@ int test_scope_elements() {
scope->table->capacity != 8 ||
scope->refCount != 1 ||
TOY_VALUE_IS_INTEGER(result) != true ||
TOY_VALUE_AS_INTEGER(result) != 42 ||
TOY_VALUE_IS_INTEGER(*result) != true ||
TOY_VALUE_AS_INTEGER(*result) != 42 ||
false)
{
@@ -354,7 +354,7 @@ int test_scope_elements() {
//assign values
Toy_assignScope(scope, hello1, TOY_VALUE_FROM_FLOAT(3.1415f));
Toy_Value resultTwo = Toy_accessScope(scope, hello2);
Toy_Value* resultTwo = Toy_accessScopeAsPointer(scope, hello2);
//check float
if (scope == NULL ||
@@ -363,8 +363,8 @@ int test_scope_elements() {
scope->table->capacity != 8 ||
scope->refCount != 1 ||
TOY_VALUE_IS_FLOAT(resultTwo) != true ||
TOY_VALUE_AS_FLOAT(resultTwo) != 3.1415f ||
TOY_VALUE_IS_FLOAT(*resultTwo) != true ||
TOY_VALUE_AS_FLOAT(*resultTwo) != 3.1415f ||
false)
{
@@ -399,10 +399,10 @@ int test_scope_elements() {
{
//check it's accessible
Toy_Value result1 = Toy_accessScope(scope, hello);
Toy_Value* result1 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(result1) != true ||
TOY_VALUE_AS_INTEGER(result1) != 42)
if (TOY_VALUE_IS_INTEGER(*result1) != true ||
TOY_VALUE_AS_INTEGER(*result1) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to access from an ancestor Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
@@ -416,10 +416,10 @@ int test_scope_elements() {
{
//check it's shadowed correctly
Toy_Value result2 = Toy_accessScope(scope, hello);
Toy_Value* result2 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_FLOAT(result2) != true ||
TOY_VALUE_AS_FLOAT(result2) != 3.1415f)
if (TOY_VALUE_IS_FLOAT(*result2) != true ||
TOY_VALUE_AS_FLOAT(*result2) != 3.1415f)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to shadow an entry in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
@@ -433,10 +433,10 @@ int test_scope_elements() {
{
//check it's recovered correctly
Toy_Value result3 = Toy_accessScope(scope, hello);
Toy_Value* result3 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(result3) != true ||
TOY_VALUE_AS_INTEGER(result3) != 42)
if (TOY_VALUE_IS_INTEGER(*result3) != true ||
TOY_VALUE_AS_INTEGER(*result3) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to recover an entry in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
@@ -450,10 +450,10 @@ int test_scope_elements() {
{
//check it's assigned correctly
Toy_Value result4 = Toy_accessScope(scope, hello);
Toy_Value* result4 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(result4) != true ||
TOY_VALUE_AS_INTEGER(result4) != 8891)
if (TOY_VALUE_IS_INTEGER(*result4) != true ||
TOY_VALUE_AS_INTEGER(*result4) != 8891)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to assign to an ancestor in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
@@ -467,10 +467,10 @@ int test_scope_elements() {
{
//check it's in the correct state
Toy_Value result5 = Toy_accessScope(scope, hello);
Toy_Value* result5 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(result5) != true ||
TOY_VALUE_AS_INTEGER(result5) != 8891)
if (TOY_VALUE_IS_INTEGER(*result5) != true ||
TOY_VALUE_AS_INTEGER(*result5) != 8891)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to access an altered entry of an ancestor in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);

View File

@@ -663,8 +663,8 @@ int test_scope(Toy_Bucket** bucketHandle) {
vm.scope == NULL ||
Toy_isDeclaredScope(vm.scope, key) == false ||
TOY_VALUE_IS_INTEGER(Toy_accessScope(vm.scope, key)) != true ||
TOY_VALUE_AS_INTEGER(Toy_accessScope(vm.scope, key)) != 42
TOY_VALUE_IS_INTEGER(*Toy_accessScopeAsPointer(vm.scope, key)) != true ||
TOY_VALUE_AS_INTEGER(*Toy_accessScopeAsPointer(vm.scope, key)) != 42
)
{
@@ -712,7 +712,7 @@ int test_scope(Toy_Bucket** bucketHandle) {
vm.scope == NULL ||
Toy_isDeclaredScope(vm.scope, key) == false ||
TOY_VALUE_IS_NULL(Toy_accessScope(vm.scope, key)) != true
TOY_VALUE_IS_NULL(*Toy_accessScopeAsPointer(vm.scope, key)) != true
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source);