mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 06:44:07 +10:00
Added tables to the scripts, untested
This also has a lot of bugfixing.
This commit is contained in:
33
scripts/tables.toy
Normal file
33
scripts/tables.toy
Normal 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
5
scripts/valgrind.toy
Normal file
@@ -0,0 +1,5 @@
|
||||
var barr = [
|
||||
[1, 2, 3],
|
||||
// [4, 5, 6],
|
||||
// [7, 8, 9]
|
||||
];
|
||||
@@ -5,11 +5,6 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
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 (paramArray != NULL && paramArray->count > capacity) {
|
||||
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;
|
||||
|
||||
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
|
||||
|
||||
@@ -60,9 +60,10 @@ typedef enum Toy_AstFlag {
|
||||
TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25,
|
||||
|
||||
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_INDEX = 33,
|
||||
TOY_AST_FLAG_PAIR = 33,
|
||||
TOY_AST_FLAG_INDEX = 34,
|
||||
|
||||
TOY_AST_FLAG_AND = 40,
|
||||
TOY_AST_FLAG_OR = 41,
|
||||
|
||||
@@ -206,7 +206,7 @@ static ParsingTuple parsingRulesetTable[] = {
|
||||
{PREC_OR,NULL,binary},// TOY_TOKEN_OPERATOR_OR,
|
||||
{PREC_NONE,unary,NULL},// TOY_TOKEN_OPERATOR_NEGATE,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_QUESTION,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_COLON,
|
||||
{PREC_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_COLON,
|
||||
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_SEMICOLON, // ;
|
||||
{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;
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
//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
|
||||
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);
|
||||
//NOTE: will probably need tweaking for tables
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
//TODO: read in a table
|
||||
}
|
||||
else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_RIGHT) {
|
||||
//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
|
||||
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) {
|
||||
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of index expression");
|
||||
|
||||
@@ -266,7 +266,7 @@ static unsigned int writeInstructionCompound(Toy_Routine** rt, Toy_AstCompound a
|
||||
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
|
||||
//signal how many values to read in as array elements
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
|
||||
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
|
||||
}
|
||||
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 {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compound flag found\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
@@ -297,6 +311,10 @@ static unsigned int writeInstructionAggregate(Toy_Routine** rt, Toy_AstAggregate
|
||||
//collections are handled above
|
||||
return result;
|
||||
}
|
||||
else if (ast.flag == TOY_AST_FLAG_PAIR) {
|
||||
//pairs are handled above
|
||||
return result;
|
||||
}
|
||||
else if (ast.flag == TOY_AST_FLAG_INDEX) {
|
||||
//value[index, length]
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_INDEX);
|
||||
|
||||
@@ -175,7 +175,7 @@ Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
|
||||
char buffer[key->info.length + 256];
|
||||
sprintf(buffer, "Undefined variable: %s\n", key->name.data);
|
||||
Toy_error(buffer);
|
||||
NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(entryPtr->value);
|
||||
|
||||
@@ -114,7 +114,7 @@ void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value 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
|
||||
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) {
|
||||
//found the entry
|
||||
if (Toy_checkValuesAreEqual((*tableHandle)->data[probe].key, key)) {
|
||||
return (*tableHandle)->data[probe].value;
|
||||
return (*tableHandle)->data + probe;
|
||||
}
|
||||
|
||||
//if its an empty slot
|
||||
if (TOY_VALUE_IS_NULL((*tableHandle)->data[probe].key)) {
|
||||
return TOY_VALUE_FROM_NULL();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//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) {
|
||||
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);
|
||||
|
||||
@@ -10,7 +10,7 @@ typedef struct Toy_TableEntry { //32 | 64 BITNESS
|
||||
unsigned int psl; //4 | 4
|
||||
} Toy_TableEntry; //20 | 20
|
||||
|
||||
//key-value table (contains = count + tombstones)
|
||||
//key-value table
|
||||
typedef struct Toy_Table { //32 | 64 BITNESS
|
||||
unsigned int capacity; //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);
|
||||
|
||||
//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
|
||||
#ifndef TOY_TABLE_INITIAL_CAPACITY
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "toy_bucket.h"
|
||||
#include "toy_string.h"
|
||||
#include "toy_array.h"
|
||||
#include "toy_table.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -59,7 +60,21 @@ unsigned int Toy_hashValue(Toy_Value value) {
|
||||
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_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
@@ -102,7 +117,23 @@ Toy_Value Toy_copyValue(Toy_Value value) {
|
||||
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_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
@@ -130,22 +161,18 @@ void Toy_freeValue(Toy_Value value) {
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_ARRAY: {
|
||||
Toy_Array* ptr = value.as.array;
|
||||
|
||||
for (unsigned int i = 0; i < ptr->count; i++) {
|
||||
Toy_freeValue(ptr->data[i]);
|
||||
}
|
||||
|
||||
Toy_resizeArray(ptr, 0);
|
||||
case TOY_VALUE_ARRAY:
|
||||
Toy_resizeArray(value.as.array, 0);
|
||||
break;
|
||||
|
||||
case TOY_VALUE_TABLE:
|
||||
Toy_freeTable(value.as.table);
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_REFERENCE:
|
||||
//don't free references
|
||||
return;
|
||||
|
||||
case TOY_VALUE_TABLE:
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
@@ -183,7 +210,7 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
|
||||
return right.type == TOY_VALUE_NULL;
|
||||
|
||||
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:
|
||||
if (right.type == TOY_VALUE_INTEGER) {
|
||||
@@ -240,7 +267,37 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
|
||||
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_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
@@ -278,6 +335,9 @@ bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) {
|
||||
return false;
|
||||
|
||||
case TOY_VALUE_TABLE:
|
||||
//nothing is comparable with a table
|
||||
return false;
|
||||
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
@@ -332,6 +392,8 @@ int Toy_compareValues(Toy_Value left, Toy_Value right) {
|
||||
break;
|
||||
|
||||
case TOY_VALUE_TABLE:
|
||||
break;
|
||||
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
@@ -392,35 +454,120 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
case TOY_VALUE_ARRAY: {
|
||||
//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);
|
||||
|
||||
//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
|
||||
bool needsComma = false;
|
||||
|
||||
Toy_String* string = open;
|
||||
|
||||
for (unsigned int i = 0; i < ptr->count; i++) {
|
||||
//append each element
|
||||
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) {
|
||||
if (needsComma) {
|
||||
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref
|
||||
Toy_freeString(string); //decrement ref
|
||||
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
|
||||
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, Toy_createStringLength(bucketHandle, "]", 1));
|
||||
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, close);
|
||||
Toy_freeString(string);
|
||||
string = tmp;
|
||||
|
||||
//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"
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
case TOY_VALUE_TABLE:
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
case TOY_VALUE_TYPE:
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
struct Toy_Bucket;
|
||||
union Toy_String_t;
|
||||
struct Toy_Array;
|
||||
struct Toy_Table;
|
||||
|
||||
typedef enum Toy_ValueType {
|
||||
TOY_VALUE_NULL,
|
||||
@@ -34,6 +35,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS
|
||||
float number; //4 | 4
|
||||
union Toy_String_t* string; //4 | 8
|
||||
struct Toy_Array* array; //4 | 8
|
||||
struct Toy_Table* table; //4 | 8
|
||||
//TODO: more types go here
|
||||
//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_STRING(value) (Toy_unwrapValue(value).as.string)
|
||||
#define TOY_VALUE_AS_ARRAY(value) (Toy_unwrapValue(value).as.array)
|
||||
#define TOY_VALUE_AS_TABLE(value) (Toy_unwrapValue(value).as.table)
|
||||
//TODO: more
|
||||
|
||||
#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_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_TABLE(value) ((Toy_Value){{ .table = value }, TOY_VALUE_TABLE})
|
||||
//TODO: more
|
||||
|
||||
#define TOY_REFERENCE_FROM_POINTER(ptr) ((Toy_Value){{ .reference = ptr }, TOY_VALUE_REFERENCE})
|
||||
|
||||
@@ -119,8 +119,39 @@ static void processRead(Toy_VM* vm) {
|
||||
}
|
||||
|
||||
case TOY_VALUE_TABLE: {
|
||||
//
|
||||
// break;
|
||||
fixAlignment(vm);
|
||||
|
||||
//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: {
|
||||
@@ -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) {
|
||||
Toy_Value* valuePtr = Toy_accessScopeAsPointer(vm->scope, TOY_VALUE_AS_STRING(target));
|
||||
Toy_freeValue(target);
|
||||
if (valuePtr == NULL) {
|
||||
return;
|
||||
}
|
||||
target = TOY_REFERENCE_FROM_POINTER(valuePtr);
|
||||
}
|
||||
|
||||
@@ -245,6 +279,16 @@ static void processAssignCompound(Toy_VM* vm) {
|
||||
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 {
|
||||
Toy_error("Invalid assignment target");
|
||||
Toy_freeValue(target);
|
||||
@@ -266,11 +310,15 @@ static void processAccess(Toy_VM* vm) {
|
||||
//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);
|
||||
if (valuePtr == NULL) {
|
||||
Toy_freeValue(name);
|
||||
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(*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);
|
||||
}
|
||||
|
||||
@@ -649,7 +697,7 @@ static void processIndex(Toy_VM* vm) {
|
||||
else if (TOY_VALUE_IS_ARRAY(value)) {
|
||||
//type checks
|
||||
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(index);
|
||||
Toy_freeValue(length);
|
||||
@@ -657,7 +705,7 @@ static void processIndex(Toy_VM* vm) {
|
||||
}
|
||||
|
||||
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(index);
|
||||
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)
|
||||
if (TOY_VALUE_IS_REFERENCE(array->data[i]) || TOY_VALUE_IS_ARRAY(array->data[i])) {
|
||||
//TODO: more types to be implemented
|
||||
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 as stack-only references
|
||||
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(&(array->data[i]));
|
||||
|
||||
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 {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user