Added tables to the scripts, untested

This also has a lot of bugfixing.
This commit is contained in:
2024-12-24 16:08:42 +11:00
parent 4faa0c0476
commit b092b8ce50
12 changed files with 371 additions and 55 deletions

33
scripts/tables.toy Normal file
View File

@@ -0,0 +1,33 @@
//1-D table
var a = ["alpha": 1, "beta": 2, "gamma": 3];
a["beta"] = 6;
print a;
assert a == ["alpha": 1, "beta": 6, "gamma": 3], "1-D tables failed";
//nested
var b = [
"outer": ["inner": true],
"alpha": 1,
"beta": 2,
"gamma": 3
];
print b;
assert b == ["alpha": 1, "beta": 2, "gamma": 3, "outer": ["inner": true]], "nested tables failed";
//test empty tables
var empty = [:];
print empty;
assert empty == [:], "empty tables failed";
//test trailing commas
var trailing = [
"alpha":1,
"beta":2,
"gamma":3,
];
print trailing;
assert trailing == ["alpha": 1, "beta": 2, "gamma": 3], "trailing tables failed";

5
scripts/valgrind.toy Normal file
View File

@@ -0,0 +1,5 @@
var barr = [
[1, 2, 3],
// [4, 5, 6],
// [7, 8, 9]
];

View File

@@ -5,11 +5,6 @@
#include <stdlib.h> #include <stdlib.h>
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) { Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
if (capacity == 0) {
free(paramArray);
return NULL;
}
//if some values will be removed, free them first //if some values will be removed, free them first
if (paramArray != NULL && paramArray->count > capacity) { if (paramArray != NULL && paramArray->count > capacity) {
for (unsigned int i = capacity; i < paramArray->count; i++) { for (unsigned int i = capacity; i < paramArray->count; i++) {
@@ -17,6 +12,12 @@ Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
} }
} }
//if you're freeing everything, just return
if (capacity == 0) {
free(paramArray);
return NULL;
}
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity; unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity;
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array)); Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));

View File

@@ -60,9 +60,10 @@ typedef enum Toy_AstFlag {
TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25, TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25,
TOY_AST_FLAG_COMPOUND_ARRAY = 30, TOY_AST_FLAG_COMPOUND_ARRAY = 30,
// TOY_AST_FLAG_COMPOUND_TABLE = 31, TOY_AST_FLAG_COMPOUND_TABLE = 31,
TOY_AST_FLAG_COLLECTION = 32, TOY_AST_FLAG_COLLECTION = 32,
TOY_AST_FLAG_INDEX = 33, TOY_AST_FLAG_PAIR = 33,
TOY_AST_FLAG_INDEX = 34,
TOY_AST_FLAG_AND = 40, TOY_AST_FLAG_AND = 40,
TOY_AST_FLAG_OR = 41, TOY_AST_FLAG_OR = 41,

View File

@@ -206,7 +206,7 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_OR,NULL,binary},// TOY_TOKEN_OPERATOR_OR, {PREC_OR,NULL,binary},// TOY_TOKEN_OPERATOR_OR,
{PREC_NONE,unary,NULL},// TOY_TOKEN_OPERATOR_NEGATE, {PREC_NONE,unary,NULL},// TOY_TOKEN_OPERATOR_NEGATE,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_QUESTION, {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_QUESTION,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_COLON, {PREC_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_COLON,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_SEMICOLON, // ; {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_SEMICOLON, // ;
{PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, // , {PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, // ,
@@ -576,21 +576,39 @@ static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
return TOY_AST_FLAG_NONE; return TOY_AST_FLAG_NONE;
} }
//BUGFIX: special case for empty tables
if (match(parser, TOY_TOKEN_OPERATOR_COLON)) {
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of empty table");
Toy_private_emitAstPass(bucketHandle, rootHandle);
Toy_private_emitAstAggregate(bucketHandle, rootHandle, TOY_AST_FLAG_PAIR, *rootHandle);
Toy_private_emitAstCompound(bucketHandle, rootHandle, TOY_AST_FLAG_COMPOUND_TABLE);
return TOY_AST_FLAG_NONE;
}
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
//peek inside to see what you have
Toy_AstFlag flag = TOY_AST_FLAG_NONE;
if ((*rootHandle)->type == TOY_AST_AGGREGATE) {
//1 element in a table will mean the top-level node is a collection
flag = (*rootHandle)->aggregate.flag;
//more than 1 element in a table will mean you need to check the last element in the collection to find a pair
if (flag == TOY_AST_FLAG_COLLECTION && (*rootHandle)->aggregate.right->type == TOY_AST_AGGREGATE) {
flag = (*rootHandle)->aggregate.right->aggregate.flag; //yes, this is hacky
}
}
//BUGFIX: special case for trailing commas //BUGFIX: special case for trailing commas
if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_RIGHT && parser->current.type != TOY_TOKEN_OPERATOR_BRACKET_RIGHT) { if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_RIGHT && parser->current.type != TOY_TOKEN_OPERATOR_BRACKET_RIGHT) {
Toy_private_emitAstCompound(bucketHandle, rootHandle, TOY_AST_FLAG_COMPOUND_ARRAY); Toy_private_emitAstCompound(bucketHandle, rootHandle, flag == TOY_AST_FLAG_PAIR ? TOY_AST_FLAG_COMPOUND_TABLE : TOY_AST_FLAG_COMPOUND_ARRAY);
//NOTE: will probably need tweaking for tables
return TOY_AST_FLAG_NONE; return TOY_AST_FLAG_NONE;
} }
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of compound expression"); consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of compound expression");
Toy_private_emitAstCompound(bucketHandle, rootHandle, TOY_AST_FLAG_COMPOUND_ARRAY); Toy_private_emitAstCompound(bucketHandle, rootHandle, flag == TOY_AST_FLAG_PAIR ? TOY_AST_FLAG_COMPOUND_TABLE : TOY_AST_FLAG_COMPOUND_ARRAY);
return TOY_AST_FLAG_NONE; return TOY_AST_FLAG_NONE;
//TODO: read in a table
} }
else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_RIGHT) { else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_RIGHT) {
//allows for trailing commas //allows for trailing commas
@@ -613,6 +631,10 @@ static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive
return TOY_AST_FLAG_COLLECTION; return TOY_AST_FLAG_COLLECTION;
} }
if (parser->previous.type == TOY_TOKEN_OPERATOR_COLON) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive
return TOY_AST_FLAG_PAIR;
}
else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) { else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of index expression"); consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of index expression");

View File

@@ -266,7 +266,7 @@ static unsigned int writeInstructionCompound(Toy_Routine** rt, Toy_AstCompound a
unsigned int result = writeRoutineCode(rt, ast.child); unsigned int result = writeRoutineCode(rt, ast.child);
if (ast.flag == TOY_AST_FLAG_COMPOUND_ARRAY) { if (ast.flag == TOY_AST_FLAG_COMPOUND_ARRAY) {
//signal how many values to read in as an array value //signal how many values to read in as array elements
EMIT_BYTE(rt, code, TOY_OPCODE_READ); EMIT_BYTE(rt, code, TOY_OPCODE_READ);
EMIT_BYTE(rt, code, TOY_VALUE_ARRAY); EMIT_BYTE(rt, code, TOY_VALUE_ARRAY);
@@ -279,6 +279,20 @@ static unsigned int writeInstructionCompound(Toy_Routine** rt, Toy_AstCompound a
return 1; //leaves only 1 value on the stack return 1; //leaves only 1 value on the stack
} }
if (ast.flag == TOY_AST_FLAG_COMPOUND_TABLE) {
//signal how many values to read in as table elements
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
EMIT_BYTE(rt, code, TOY_VALUE_TABLE);
//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 { else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compound flag found\n" TOY_CC_RESET); fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compound flag found\n" TOY_CC_RESET);
exit(-1); exit(-1);
@@ -297,6 +311,10 @@ static unsigned int writeInstructionAggregate(Toy_Routine** rt, Toy_AstAggregate
//collections are handled above //collections are handled above
return result; return result;
} }
else if (ast.flag == TOY_AST_FLAG_PAIR) {
//pairs are handled above
return result;
}
else if (ast.flag == TOY_AST_FLAG_INDEX) { else if (ast.flag == TOY_AST_FLAG_INDEX) {
//value[index, length] //value[index, length]
EMIT_BYTE(rt, code, TOY_OPCODE_INDEX); EMIT_BYTE(rt, code, TOY_OPCODE_INDEX);

View File

@@ -175,7 +175,7 @@ Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
char buffer[key->info.length + 256]; char buffer[key->info.length + 256];
sprintf(buffer, "Undefined variable: %s\n", key->name.data); sprintf(buffer, "Undefined variable: %s\n", key->name.data);
Toy_error(buffer); Toy_error(buffer);
NULL; return NULL;
} }
return &(entryPtr->value); return &(entryPtr->value);

View File

@@ -114,7 +114,7 @@ void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value) {
probeAndInsert(tableHandle, key, value); probeAndInsert(tableHandle, key, value);
} }
Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) { Toy_TableEntry* Toy_private_lookupTableEntryPtr(Toy_Table** tableHandle, Toy_Value key) {
if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques
Toy_error(TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET); Toy_error(TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET);
} }
@@ -125,12 +125,12 @@ Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) {
while (true) { while (true) {
//found the entry //found the entry
if (Toy_checkValuesAreEqual((*tableHandle)->data[probe].key, key)) { if (Toy_checkValuesAreEqual((*tableHandle)->data[probe].key, key)) {
return (*tableHandle)->data[probe].value; return (*tableHandle)->data + probe;
} }
//if its an empty slot //if its an empty slot
if (TOY_VALUE_IS_NULL((*tableHandle)->data[probe].key)) { if (TOY_VALUE_IS_NULL((*tableHandle)->data[probe].key)) {
return TOY_VALUE_FROM_NULL(); return NULL;
} }
//adjust and continue //adjust and continue
@@ -139,6 +139,18 @@ Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) {
} }
} }
Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) {
//TODO: I hacked this in at a moment's notice, clean it up
Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(tableHandle, key);
if (entry == NULL) {
return TOY_VALUE_FROM_NULL();
}
else {
return entry->value;
}
}
void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key) { void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key) {
if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques
Toy_error(TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET); Toy_error(TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET);

View File

@@ -10,7 +10,7 @@ typedef struct Toy_TableEntry { //32 | 64 BITNESS
unsigned int psl; //4 | 4 unsigned int psl; //4 | 4
} Toy_TableEntry; //20 | 20 } Toy_TableEntry; //20 | 20
//key-value table (contains = count + tombstones) //key-value table
typedef struct Toy_Table { //32 | 64 BITNESS typedef struct Toy_Table { //32 | 64 BITNESS
unsigned int capacity; //4 | 4 unsigned int capacity; //4 | 4
unsigned int count; //4 | 4 unsigned int count; //4 | 4
@@ -26,7 +26,8 @@ TOY_API Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key);
TOY_API void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key); TOY_API void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key);
//NOTE: exposed to skip unnecessary allocations within Toy_Scope //NOTE: exposed to skip unnecessary allocations within Toy_Scope
TOY_API Toy_Table* Toy_private_adjustTableCapacity(Toy_Table* oldTable, unsigned int newCapacity); TOY_API Toy_Table* Toy_private_adjustTableCapacity(Toy_Table* oldTable, unsigned int newCapacity); //TODO: make it public
TOY_API Toy_TableEntry* Toy_private_lookupTableEntryPtr(Toy_Table** tableHandle, Toy_Value key); //TODO: make it public?
//some useful sizes, could be swapped out as needed //some useful sizes, could be swapped out as needed
#ifndef TOY_TABLE_INITIAL_CAPACITY #ifndef TOY_TABLE_INITIAL_CAPACITY

View File

@@ -4,6 +4,7 @@
#include "toy_bucket.h" #include "toy_bucket.h"
#include "toy_string.h" #include "toy_string.h"
#include "toy_array.h" #include "toy_array.h"
#include "toy_table.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -59,7 +60,21 @@ unsigned int Toy_hashValue(Toy_Value value) {
return hash; return hash;
} }
case TOY_VALUE_TABLE: case TOY_VALUE_TABLE: {
//since table internals can change, recalc the hash each time it's needed
Toy_Table* ptr = value.as.table;
unsigned int hash = 0;
for (unsigned int i = 0; i < ptr->capacity; i++) {
if (TOY_VALUE_IS_NULL(ptr->data[i].key) != true) {
hash ^= Toy_hashValue(ptr->data[i].key);
hash ^= Toy_hashValue(ptr->data[i].value);
}
}
return hash;
}
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:
@@ -102,7 +117,23 @@ Toy_Value Toy_copyValue(Toy_Value value) {
return TOY_VALUE_FROM_ARRAY(result); return TOY_VALUE_FROM_ARRAY(result);
} }
case TOY_VALUE_TABLE: case TOY_VALUE_TABLE: {
//tables probably won't get copied much
Toy_Table* ptr = value.as.table;
Toy_Table* result = Toy_private_adjustTableCapacity(NULL, ptr->capacity);
for (unsigned int i = 0; i < ptr->capacity; i++) {
if (TOY_VALUE_IS_NULL(ptr->data[i].key) != true) {
result->data[i].key = Toy_copyValue(ptr->data[i].key);
result->data[i].value = Toy_copyValue(ptr->data[i].value);
}
}
result->capacity = ptr->capacity;
result->count = ptr->count;
return TOY_VALUE_FROM_TABLE(result);
}
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:
@@ -130,22 +161,18 @@ void Toy_freeValue(Toy_Value value) {
break; break;
} }
case TOY_VALUE_ARRAY: { case TOY_VALUE_ARRAY:
Toy_Array* ptr = value.as.array; Toy_resizeArray(value.as.array, 0);
break;
for (unsigned int i = 0; i < ptr->count; i++) {
Toy_freeValue(ptr->data[i]); case TOY_VALUE_TABLE:
} Toy_freeTable(value.as.table);
Toy_resizeArray(ptr, 0);
break; break;
}
case TOY_VALUE_REFERENCE: case TOY_VALUE_REFERENCE:
//don't free references //don't free references
return; return;
case TOY_VALUE_TABLE:
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:
@@ -183,7 +210,7 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
return right.type == TOY_VALUE_NULL; return right.type == TOY_VALUE_NULL;
case TOY_VALUE_BOOLEAN: case TOY_VALUE_BOOLEAN:
return right.type == TOY_VALUE_NULL && left.as.boolean == right.as.boolean; return right.type == TOY_VALUE_BOOLEAN && left.as.boolean == right.as.boolean;
case TOY_VALUE_INTEGER: case TOY_VALUE_INTEGER:
if (right.type == TOY_VALUE_INTEGER) { if (right.type == TOY_VALUE_INTEGER) {
@@ -240,7 +267,37 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
return true; return true;
} }
case TOY_VALUE_TABLE: case TOY_VALUE_TABLE: {
if (right.type == TOY_VALUE_TABLE) {
Toy_Table* leftTable = left.as.table;
Toy_Table* rightTable = right.as.table;
//different counts
if (leftTable->count != rightTable->count) {
return false;
}
for (unsigned int i = 0; i < leftTable->capacity; i++) {
Toy_TableEntry* entry = leftTable->data + i;
if (TOY_VALUE_IS_NULL(entry->key) != true) {
//any mismatch is an easy difference
Toy_Value rightValue = Toy_lookupTable(&rightTable, entry->key);
if (TOY_VALUE_IS_NULL(rightValue) || Toy_checkValuesAreEqual(entry->value, rightValue) != true) {
return false;
}
}
}
}
else {
break;
}
//finally
return true;
}
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:
@@ -278,6 +335,9 @@ bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) {
return false; return false;
case TOY_VALUE_TABLE: case TOY_VALUE_TABLE:
//nothing is comparable with a table
return false;
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:
@@ -332,6 +392,8 @@ int Toy_compareValues(Toy_Value left, Toy_Value right) {
break; break;
case TOY_VALUE_TABLE: case TOY_VALUE_TABLE:
break;
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:
@@ -392,35 +454,120 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
case TOY_VALUE_ARRAY: { case TOY_VALUE_ARRAY: {
//TODO: concat + free is definitely a performance nightmare, could make an append function? //TODO: concat + free is definitely a performance nightmare, could make an append function?
Toy_Array* ptr = value.as.array; Toy_Array* ptr = value.as.array;
Toy_String* string = Toy_createStringLength(bucketHandle, "[", 1);
//if array is empty, skip below
if (ptr->count == 0) {
Toy_String* empty = Toy_createString(bucketHandle, "[]");
return empty;
}
Toy_String* open = Toy_createStringLength(bucketHandle, "[", 1);
Toy_String* close = Toy_createStringLength(bucketHandle, "]", 1);
Toy_String* comma = Toy_createStringLength(bucketHandle, ",", 1); //reusable Toy_String* comma = Toy_createStringLength(bucketHandle, ",", 1); //reusable
bool needsComma = false;
Toy_String* string = open;
for (unsigned int i = 0; i < ptr->count; i++) { for (unsigned int i = 0; i < ptr->count; i++) {
//append each element if (needsComma) {
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 < ptr->count) {
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref
Toy_freeString(string); //decrement ref Toy_freeString(string); //decrement ref
string = tmp; string = tmp;
} }
//append each element
Toy_String* element = Toy_stringifyValue(bucketHandle, ptr->data[i]);
Toy_String* final = Toy_concatStrings(bucketHandle, string, element);
Toy_freeString(element);
Toy_freeString(string);
string = final;
needsComma = true;
} }
//closing bracket //closing bracket
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, Toy_createStringLength(bucketHandle, "]", 1)); Toy_String* tmp = Toy_concatStrings(bucketHandle, string, close);
Toy_freeString(string); Toy_freeString(string);
string = tmp; string = tmp;
//clean up //clean up
Toy_freeString(open);
Toy_freeString(close);
Toy_freeString(comma); //TODO: reusable global, or string type "permanent"
return string;
}
case TOY_VALUE_TABLE: {
//TODO: concat + free is definitely a performance nightmare, could make an append function?
Toy_Table* ptr = value.as.table;
//if table is empty, skip below
if (ptr->count == 0) {
Toy_String* empty = Toy_createString(bucketHandle, "[:]");
return empty;
}
Toy_String* open = Toy_createStringLength(bucketHandle, "[", 1);
Toy_String* close = Toy_createStringLength(bucketHandle, "]", 1);
Toy_String* colon = Toy_createStringLength(bucketHandle, ":", 1); //reusable
Toy_String* comma = Toy_createStringLength(bucketHandle, ",", 1); //reusable
bool needsComma = false;
Toy_String* string = open;
for (unsigned int i = 0; i < ptr->capacity; i++) {
if (TOY_VALUE_IS_NULL(ptr->data[i].key)) {
continue;
}
if (needsComma) {
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref
Toy_freeString(string); //decrement ref
string = tmp;
}
//make the element pair
Toy_String* k = Toy_stringifyValue(bucketHandle, ptr->data[i].key);
Toy_String* v = Toy_stringifyValue(bucketHandle, ptr->data[i].value);
Toy_String* c = Toy_concatStrings(bucketHandle, k, colon); //stick the colon between
Toy_String* pair = Toy_concatStrings(bucketHandle, c, v);
//append the element pair
Toy_String* final = Toy_concatStrings(bucketHandle, string, pair);
//do a bunch of freeing so the internal refCounts stay balanced
Toy_freeString(k);
Toy_freeString(v);
Toy_freeString(c);
Toy_freeString(pair);
Toy_freeString(string);
//finally
string = final;
//TODO: would a simple buffer be faster here?
//if there's more elements
needsComma = true;
}
//closing bracket
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, close);
Toy_freeString(string);
string = tmp;
//clean up
Toy_freeString(open);
Toy_freeString(close);
Toy_freeString(colon); //TODO: reusable global, or string type "permanent"
Toy_freeString(comma); //TODO: reusable global, or string type "permanent" Toy_freeString(comma); //TODO: reusable global, or string type "permanent"
return string; return string;
} }
case TOY_VALUE_TABLE:
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_TYPE: case TOY_VALUE_TYPE:

View File

@@ -7,6 +7,7 @@
struct Toy_Bucket; struct Toy_Bucket;
union Toy_String_t; union Toy_String_t;
struct Toy_Array; struct Toy_Array;
struct Toy_Table;
typedef enum Toy_ValueType { typedef enum Toy_ValueType {
TOY_VALUE_NULL, TOY_VALUE_NULL,
@@ -34,6 +35,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS
float number; //4 | 4 float number; //4 | 4
union Toy_String_t* string; //4 | 8 union Toy_String_t* string; //4 | 8
struct Toy_Array* array; //4 | 8 struct Toy_Array* array; //4 | 8
struct Toy_Table* table; //4 | 8
//TODO: more types go here //TODO: more types go here
//TODO: consider 'stack' as a possible addition //TODO: consider 'stack' as a possible addition
@@ -59,6 +61,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS
#define TOY_VALUE_AS_FLOAT(value) (Toy_unwrapValue(value).as.number) #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_STRING(value) (Toy_unwrapValue(value).as.string)
#define TOY_VALUE_AS_ARRAY(value) (Toy_unwrapValue(value).as.array) #define TOY_VALUE_AS_ARRAY(value) (Toy_unwrapValue(value).as.array)
#define TOY_VALUE_AS_TABLE(value) (Toy_unwrapValue(value).as.table)
//TODO: more //TODO: more
#define TOY_VALUE_FROM_NULL() ((Toy_Value){{ .integer = 0 }, TOY_VALUE_NULL}) #define TOY_VALUE_FROM_NULL() ((Toy_Value){{ .integer = 0 }, TOY_VALUE_NULL})
@@ -67,6 +70,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS
#define TOY_VALUE_FROM_FLOAT(value) ((Toy_Value){{ .number = value }, TOY_VALUE_FLOAT}) #define TOY_VALUE_FROM_FLOAT(value) ((Toy_Value){{ .number = value }, TOY_VALUE_FLOAT})
#define TOY_VALUE_FROM_STRING(value) ((Toy_Value){{ .string = value }, TOY_VALUE_STRING}) #define TOY_VALUE_FROM_STRING(value) ((Toy_Value){{ .string = value }, TOY_VALUE_STRING})
#define TOY_VALUE_FROM_ARRAY(value) ((Toy_Value){{ .array = value }, TOY_VALUE_ARRAY}) #define TOY_VALUE_FROM_ARRAY(value) ((Toy_Value){{ .array = value }, TOY_VALUE_ARRAY})
#define TOY_VALUE_FROM_TABLE(value) ((Toy_Value){{ .table = value }, TOY_VALUE_TABLE})
//TODO: more //TODO: more
#define TOY_REFERENCE_FROM_POINTER(ptr) ((Toy_Value){{ .reference = ptr }, TOY_VALUE_REFERENCE}) #define TOY_REFERENCE_FROM_POINTER(ptr) ((Toy_Value){{ .reference = ptr }, TOY_VALUE_REFERENCE})

View File

@@ -119,8 +119,39 @@ static void processRead(Toy_VM* vm) {
} }
case TOY_VALUE_TABLE: { case TOY_VALUE_TABLE: {
// fixAlignment(vm);
// break;
//the number of values to read from the stack
unsigned int count = (unsigned int)READ_INT(vm);
//capacity covers keys AND values
unsigned int capacity = count / 2;
capacity = capacity > TOY_TABLE_INITIAL_CAPACITY ? capacity : TOY_TABLE_INITIAL_CAPACITY;
//neat trick to find the next power of two, inclusive (restriction of the table 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 table and read in the key-values
Toy_Table* table = Toy_private_adjustTableCapacity(NULL, capacity);
//read in backwards from the stack
for (unsigned int i = 0; i < count / 2; i++) {
Toy_Value v = Toy_popStack(&vm->stack);
Toy_Value k = Toy_popStack(&vm->stack);
Toy_insertTable(&table, k, v);
}
//finished
value = TOY_VALUE_FROM_TABLE(table);
break;
} }
case TOY_VALUE_FUNCTION: { case TOY_VALUE_FUNCTION: {
@@ -214,6 +245,9 @@ static void processAssignCompound(Toy_VM* vm) {
if (TOY_VALUE_IS_STRING(target) && TOY_VALUE_AS_STRING(target)->info.type == TOY_STRING_NAME) { if (TOY_VALUE_IS_STRING(target) && TOY_VALUE_AS_STRING(target)->info.type == TOY_STRING_NAME) {
Toy_Value* valuePtr = Toy_accessScopeAsPointer(vm->scope, TOY_VALUE_AS_STRING(target)); Toy_Value* valuePtr = Toy_accessScopeAsPointer(vm->scope, TOY_VALUE_AS_STRING(target));
Toy_freeValue(target); Toy_freeValue(target);
if (valuePtr == NULL) {
return;
}
target = TOY_REFERENCE_FROM_POINTER(valuePtr); target = TOY_REFERENCE_FROM_POINTER(valuePtr);
} }
@@ -245,6 +279,16 @@ static void processAssignCompound(Toy_VM* vm) {
Toy_freeValue(value); Toy_freeValue(value);
} }
else if (TOY_VALUE_IS_TABLE(target)) {
Toy_Table* table = TOY_VALUE_AS_TABLE(target);
//set the value
Toy_insertTable(&table, key, Toy_copyValue(Toy_unwrapValue(value)));
//cleanup
Toy_freeValue(value);
}
else { else {
Toy_error("Invalid assignment target"); Toy_error("Invalid assignment target");
Toy_freeValue(target); Toy_freeValue(target);
@@ -266,11 +310,15 @@ static void processAccess(Toy_VM* vm) {
//find the value //find the value
Toy_Value* valuePtr = Toy_accessScopeAsPointer(vm->scope, TOY_VALUE_AS_STRING(name)); 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 (valuePtr == NULL) {
if (TOY_VALUE_IS_REFERENCE(*valuePtr) || TOY_VALUE_IS_ARRAY(*valuePtr)) { Toy_freeValue(name);
//TODO: more types to be implemented return;
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(valuePtr); }
//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) || TOY_VALUE_IS_TABLE(*valuePtr)) {
//TODO: more types to be implemented as stack-only references
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(valuePtr);
Toy_pushStack(&vm->stack, ref); Toy_pushStack(&vm->stack, ref);
} }
@@ -649,7 +697,7 @@ static void processIndex(Toy_VM* vm) {
else if (TOY_VALUE_IS_ARRAY(value)) { else if (TOY_VALUE_IS_ARRAY(value)) {
//type checks //type checks
if (!TOY_VALUE_IS_INTEGER(index)) { if (!TOY_VALUE_IS_INTEGER(index)) {
Toy_error("Failed to index a string"); Toy_error("Failed to index an array");
Toy_freeValue(value); Toy_freeValue(value);
Toy_freeValue(index); Toy_freeValue(index);
Toy_freeValue(length); Toy_freeValue(length);
@@ -657,7 +705,7 @@ static void processIndex(Toy_VM* vm) {
} }
if (!(TOY_VALUE_IS_NULL(length) || TOY_VALUE_IS_INTEGER(length))) { if (!(TOY_VALUE_IS_NULL(length) || TOY_VALUE_IS_INTEGER(length))) {
Toy_error("Failed to index-length a string"); Toy_error("Failed to index-length an array");
Toy_freeValue(value); Toy_freeValue(value);
Toy_freeValue(index); Toy_freeValue(index);
Toy_freeValue(length); Toy_freeValue(length);
@@ -679,10 +727,9 @@ static void processIndex(Toy_VM* vm) {
} }
//in the event of a certain subset of types, create references instead (these should only exist on the stack) //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])) { if (TOY_VALUE_IS_REFERENCE(array->data[i]) || TOY_VALUE_IS_ARRAY(array->data[i]) || TOY_VALUE_IS_TABLE(array->data[i])) {
//TODO: more types to be implemented //TODO: more types to be implemented as stack-only references
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(&(array->data[i])); Toy_Value ref = TOY_REFERENCE_FROM_POINTER(&(array->data[i]));
Toy_pushStack(&vm->stack, ref); Toy_pushStack(&vm->stack, ref);
} }
@@ -691,6 +738,31 @@ static void processIndex(Toy_VM* vm) {
} }
} }
else if (TOY_VALUE_IS_TABLE(value)) {
if (TOY_VALUE_IS_NULL(length) != true) {
Toy_error("Can't index-length a table");
Toy_freeValue(value);
Toy_freeValue(index);
Toy_freeValue(length);
return;
}
//get the table & element value
Toy_Table* table = TOY_VALUE_AS_TABLE(value);
Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(&table, index);
//in the event of a certain subset of types, create references instead (these should only exist on the stack)
if (TOY_VALUE_IS_REFERENCE(entry->value) || TOY_VALUE_IS_ARRAY(entry->value) || TOY_VALUE_IS_TABLE(entry->value)) {
//TODO: more types to be implemented as stack-only references
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(&(entry->value));
Toy_pushStack(&vm->stack, ref);
}
else {
Toy_pushStack(&vm->stack, Toy_copyValue(entry->value));
}
}
else { else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type '%s' found in processIndex, exiting\n" TOY_CC_RESET, Toy_private_getValueTypeAsCString(value.type)); 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); exit(-1);