mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
I've ripped out the deep copying, and flattened the bucket usage, but
this results in no memory being freed or reused for the lifetime of the
program.
This is shown most clearly with this script:
```toy
fn makeCounter() {
var counter: int = 0;
fn increment() {
return ++counter;
}
return increment;
}
var tally = makeCounter();
while (true) {
var result = tally();
if (result >= 10_000_000) {
break;
}
}
```
The number of calls vs amount of memory consumed is:
```
function calls -> memory used in megabytes
1_000 -> 0.138128
10_000 -> 2.235536
100_000 -> 21.7021
1_000_000 -> 216.1712
10_000_000 -> 1520.823
```
Obviously this needs to be fixed, as ballooning to gigabytes of memory
in only a moment isn't practical. That will be the next task - to find
some way to free memory that isn't needed anymore.
See #163, #160
661 lines
17 KiB
C
661 lines
17 KiB
C
#include "toy_value.h"
|
|
#include "toy_console_colors.h"
|
|
|
|
#include "toy_bucket.h"
|
|
#include "toy_string.h"
|
|
#include "toy_array.h"
|
|
#include "toy_table.h"
|
|
#include "toy_function.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
//utils
|
|
static unsigned int hashUInt(unsigned int x) {
|
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
|
x = (x >> 16) ^ x;
|
|
return x;
|
|
}
|
|
|
|
#define MAYBE_UNWRAP(value) if (TOY_VALUE_IS_REFERENCE(value)) { value = Toy_unwrapValue(value); }
|
|
|
|
//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) {
|
|
MAYBE_UNWRAP(value);
|
|
|
|
switch(value.type) {
|
|
case TOY_VALUE_NULL:
|
|
return 0;
|
|
|
|
case TOY_VALUE_BOOLEAN:
|
|
return value.as.boolean ? 1 : 0;
|
|
|
|
case TOY_VALUE_INTEGER:
|
|
return hashUInt((unsigned int)value.as.integer);
|
|
|
|
case TOY_VALUE_FLOAT:
|
|
return hashUInt( *((unsigned int*)(&value.as.number)) );
|
|
|
|
case TOY_VALUE_STRING:
|
|
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* ptr = value.as.array;
|
|
unsigned int hash = 0;
|
|
|
|
for (unsigned int i = 0; i < ptr->count; i++) {
|
|
hash ^= Toy_hashValue(ptr->data[i]);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
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_ANY:
|
|
case TOY_VALUE_REFERENCE:
|
|
case TOY_VALUE_UNKNOWN:
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't hash an unknown value type (%d), exiting\n" TOY_CC_RESET, (int)value.type);
|
|
exit(-1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Toy_Value Toy_copyValue(Toy_Value value) {
|
|
MAYBE_UNWRAP(value);
|
|
|
|
switch(value.type) {
|
|
case TOY_VALUE_NULL:
|
|
case TOY_VALUE_BOOLEAN:
|
|
case TOY_VALUE_INTEGER:
|
|
case TOY_VALUE_FLOAT:
|
|
return value;
|
|
|
|
case TOY_VALUE_STRING: {
|
|
return TOY_VALUE_FROM_STRING(Toy_copyString(value.as.string));
|
|
}
|
|
|
|
case TOY_VALUE_ARRAY: {
|
|
//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 < ptr->count; i++) {
|
|
result->data[i] = Toy_copyValue(ptr->data[i]);
|
|
}
|
|
|
|
result->capacity = ptr->capacity;
|
|
result->count = ptr->count;
|
|
|
|
return TOY_VALUE_FROM_ARRAY(result);
|
|
}
|
|
|
|
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:
|
|
return value; //URGENT: concerning
|
|
|
|
case TOY_VALUE_OPAQUE:
|
|
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);
|
|
}
|
|
|
|
//dummy return
|
|
return TOY_VALUE_FROM_NULL();
|
|
}
|
|
|
|
void Toy_freeValue(Toy_Value value) {
|
|
switch(value.type) {
|
|
case TOY_VALUE_NULL:
|
|
case TOY_VALUE_BOOLEAN:
|
|
case TOY_VALUE_INTEGER:
|
|
case TOY_VALUE_FLOAT:
|
|
break;
|
|
|
|
case TOY_VALUE_STRING: {
|
|
Toy_freeString(value.as.string);
|
|
break;
|
|
}
|
|
|
|
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_FUNCTION:
|
|
//not sure this needs to be freed
|
|
return;
|
|
|
|
case TOY_VALUE_OPAQUE:
|
|
case TOY_VALUE_ANY:
|
|
case TOY_VALUE_UNKNOWN:
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't free an unknown value type, exiting\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
bool Toy_checkValueIsTruthy(Toy_Value value) {
|
|
MAYBE_UNWRAP(value);
|
|
|
|
//null is an error
|
|
if (value.type == TOY_VALUE_NULL) {
|
|
Toy_error("'null' is neither true nor false");
|
|
return false;
|
|
}
|
|
|
|
//only 'false' is falsy
|
|
if (value.type == TOY_VALUE_BOOLEAN) {
|
|
return value.as.boolean;
|
|
}
|
|
|
|
if (value.type == TOY_VALUE_INTEGER) {
|
|
return value.as.integer != 0;
|
|
}
|
|
|
|
if (value.type == TOY_VALUE_FLOAT) {
|
|
return value.as.number != 0.0f;
|
|
}
|
|
|
|
//anything else is truthy
|
|
return true;
|
|
}
|
|
|
|
bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
|
|
MAYBE_UNWRAP(left);
|
|
MAYBE_UNWRAP(right);
|
|
|
|
switch(left.type) {
|
|
case TOY_VALUE_NULL:
|
|
return right.type == TOY_VALUE_NULL;
|
|
|
|
case TOY_VALUE_BOOLEAN:
|
|
return right.type == TOY_VALUE_BOOLEAN && left.as.boolean == right.as.boolean;
|
|
|
|
case TOY_VALUE_INTEGER:
|
|
if (right.type == TOY_VALUE_INTEGER) {
|
|
return left.as.integer == right.as.integer;
|
|
}
|
|
else if (right.type == TOY_VALUE_FLOAT) {
|
|
return left.as.integer == right.as.number;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
case TOY_VALUE_FLOAT:
|
|
if (right.type == TOY_VALUE_INTEGER) {
|
|
return left.as.number == right.as.integer;
|
|
}
|
|
else if (right.type == TOY_VALUE_FLOAT) {
|
|
return left.as.number == right.as.number;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
case TOY_VALUE_STRING:
|
|
if (right.type == TOY_VALUE_STRING) {
|
|
return Toy_compareStrings(left.as.string, right.as.string) == 0;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
case TOY_VALUE_ARRAY: {
|
|
if (right.type == TOY_VALUE_ARRAY) {
|
|
Toy_Array* leftArray = left.as.array;
|
|
Toy_Array* rightArray = right.as.array;
|
|
|
|
//different lengths is an easy way to check
|
|
if (leftArray->count != rightArray->count) {
|
|
return false;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < leftArray->count; i++) {
|
|
//any mismatch is an easy difference
|
|
if (Toy_checkValuesAreEqual(leftArray->data[i], rightArray->data[i]) != true) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
//finally
|
|
return true;
|
|
}
|
|
|
|
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:
|
|
return false; //URGENT: test this
|
|
|
|
case TOY_VALUE_OPAQUE:
|
|
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);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) {
|
|
MAYBE_UNWRAP(left);
|
|
MAYBE_UNWRAP(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 right.type == TOY_VALUE_BOOLEAN;
|
|
|
|
case TOY_VALUE_INTEGER:
|
|
case TOY_VALUE_FLOAT:
|
|
return right.type == TOY_VALUE_INTEGER || right.type == TOY_VALUE_FLOAT;
|
|
|
|
case TOY_VALUE_STRING:
|
|
return right.type == TOY_VALUE_STRING;
|
|
|
|
case TOY_VALUE_ARRAY:
|
|
//nothing is comparable with an array
|
|
return false;
|
|
|
|
case TOY_VALUE_TABLE:
|
|
//nothing is comparable with a table
|
|
return false;
|
|
|
|
case TOY_VALUE_FUNCTION:
|
|
//nothing is comparable with a function
|
|
return false;
|
|
|
|
case TOY_VALUE_OPAQUE:
|
|
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);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int Toy_compareValues(Toy_Value left, Toy_Value right) {
|
|
MAYBE_UNWRAP(left);
|
|
MAYBE_UNWRAP(right);
|
|
|
|
//comparison means there's a difference in value, with some kind of quantity - so null, bool, etc. aren't comparable
|
|
switch(left.type) {
|
|
case TOY_VALUE_NULL:
|
|
case TOY_VALUE_BOOLEAN:
|
|
break;
|
|
|
|
case TOY_VALUE_INTEGER:
|
|
if (right.type == TOY_VALUE_INTEGER) {
|
|
return left.as.integer - right.as.integer;
|
|
}
|
|
else if (right.type == TOY_VALUE_FLOAT) {
|
|
return left.as.integer - right.as.number;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
case TOY_VALUE_FLOAT:
|
|
if (right.type == TOY_VALUE_INTEGER) {
|
|
return left.as.number - right.as.integer;
|
|
}
|
|
else if (right.type == TOY_VALUE_FLOAT) {
|
|
return left.as.number - right.as.number;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
case TOY_VALUE_STRING:
|
|
if (right.type == TOY_VALUE_STRING) {
|
|
return Toy_compareStrings(left.as.string, right.as.string);
|
|
}
|
|
|
|
case TOY_VALUE_ARRAY:
|
|
break;
|
|
|
|
case TOY_VALUE_TABLE:
|
|
break;
|
|
|
|
case TOY_VALUE_FUNCTION:
|
|
break;
|
|
|
|
case TOY_VALUE_OPAQUE:
|
|
case TOY_VALUE_ANY:
|
|
case TOY_VALUE_REFERENCE:
|
|
case TOY_VALUE_UNKNOWN:
|
|
break;
|
|
}
|
|
|
|
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) {
|
|
MAYBE_UNWRAP(value);
|
|
|
|
//TODO: could have "constant" strings that can be referenced, instead of null, true, false, etc. - new string type of 'permanent'
|
|
|
|
switch(value.type) {
|
|
case TOY_VALUE_NULL:
|
|
return Toy_createString(bucketHandle, "<null>");
|
|
|
|
case TOY_VALUE_BOOLEAN:
|
|
return Toy_createString(bucketHandle, value.as.boolean ? "<true>" : "<false>");
|
|
|
|
case TOY_VALUE_INTEGER: {
|
|
char buffer[16];
|
|
sprintf(buffer, "%d", value.as.integer);
|
|
return Toy_createString(bucketHandle, buffer);
|
|
}
|
|
|
|
case TOY_VALUE_FLOAT: {
|
|
//using printf
|
|
char buffer[16];
|
|
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);
|
|
|
|
//find the decimal, if it exists
|
|
unsigned int decimal = 0;
|
|
while (decimal != length && buffer[decimal] != '.' && buffer[decimal] != ',') decimal++; //'.' and ',' supports more locales
|
|
|
|
//locales are hard, sorry!
|
|
if (decimal != length && buffer[decimal] == ',') buffer[decimal] = '.';
|
|
|
|
//wipe the trailing zeros
|
|
while(decimal != length && buffer[length-1] == '0') buffer[--length] = '\0';
|
|
|
|
return Toy_createStringLength(bucketHandle, buffer, length);
|
|
}
|
|
|
|
case TOY_VALUE_STRING:
|
|
return Toy_copyString(value.as.string);
|
|
|
|
case TOY_VALUE_ARRAY: {
|
|
//TODO: concat + free is definitely a performance nightmare, could make an append function?
|
|
Toy_Array* ptr = value.as.array;
|
|
|
|
//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* quote = Toy_createStringLength(bucketHandle, "\"", 1); //reusable
|
|
bool needsComma = false;
|
|
|
|
Toy_String* string = open;
|
|
|
|
for (unsigned int i = 0; i < ptr->count; i++) {
|
|
if (needsComma) {
|
|
Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref
|
|
Toy_freeString(string); //decrement ref
|
|
string = tmp;
|
|
}
|
|
|
|
//get the element
|
|
Toy_String* element = Toy_stringifyValue(bucketHandle, ptr->data[i]);
|
|
|
|
//put quotemarks around internal string elements
|
|
if (TOY_VALUE_IS_STRING(ptr->data[i])) {
|
|
Toy_String* tmpA = Toy_concatStrings(bucketHandle, quote, element);
|
|
Toy_String* tmpB = Toy_concatStrings(bucketHandle, tmpA, quote);
|
|
|
|
Toy_freeString(element);
|
|
Toy_freeString(tmpA);
|
|
element = tmpB;
|
|
}
|
|
|
|
//append each element
|
|
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, close);
|
|
Toy_freeString(string);
|
|
string = tmp;
|
|
|
|
//clean up
|
|
Toy_freeString(open);
|
|
Toy_freeString(close);
|
|
Toy_freeString(comma); //TODO: reusable global, or string type "permanent"
|
|
Toy_freeString(quote); //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
|
|
Toy_String* quote = 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);
|
|
|
|
//put quotemarks around internal string elements (key)
|
|
if (TOY_VALUE_IS_STRING(ptr->data[i].key)) {
|
|
Toy_String* tmpA = Toy_concatStrings(bucketHandle, quote, k);
|
|
Toy_String* tmpB = Toy_concatStrings(bucketHandle, tmpA, quote);
|
|
|
|
Toy_freeString(k);
|
|
Toy_freeString(tmpA);
|
|
k = tmpB;
|
|
}
|
|
|
|
//put quotemarks around internal string elements (value)
|
|
if (TOY_VALUE_IS_STRING(ptr->data[i].value)) {
|
|
Toy_String* tmpA = Toy_concatStrings(bucketHandle, quote, v);
|
|
Toy_String* tmpB = Toy_concatStrings(bucketHandle, tmpA, quote);
|
|
|
|
Toy_freeString(v);
|
|
Toy_freeString(tmpA);
|
|
v = tmpB;
|
|
}
|
|
|
|
//stick the colon between, make the pair
|
|
Toy_String* c = Toy_concatStrings(bucketHandle, k, colon);
|
|
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(quote); //TODO: reusable global, or string type "permanent"
|
|
|
|
return string;
|
|
}
|
|
|
|
case TOY_VALUE_FUNCTION:
|
|
//dummy
|
|
return Toy_createString(bucketHandle, "<fn>");
|
|
|
|
case TOY_VALUE_OPAQUE:
|
|
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);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char* Toy_private_getValueTypeAsCString(Toy_ValueType type) {
|
|
switch (type) {
|
|
case TOY_VALUE_NULL: return "null";
|
|
case TOY_VALUE_BOOLEAN: return "bool";
|
|
case TOY_VALUE_INTEGER: return "int";
|
|
case TOY_VALUE_FLOAT: return "float";
|
|
case TOY_VALUE_STRING: return "string";
|
|
case TOY_VALUE_ARRAY: return "array";
|
|
case TOY_VALUE_TABLE: return "table";
|
|
case TOY_VALUE_FUNCTION: return "function";
|
|
case TOY_VALUE_OPAQUE: return "opaque";
|
|
case TOY_VALUE_ANY: return "any";
|
|
case TOY_VALUE_REFERENCE: return "reference";
|
|
case TOY_VALUE_UNKNOWN: return "unknown";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|