mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
562 lines
14 KiB
C
562 lines
14 KiB
C
#include "literal.h"
|
|
#include "memory.h"
|
|
|
|
#include "literal_array.h"
|
|
#include "literal_dictionary.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
//utils
|
|
static void stdoutWrapper(const char* output) {
|
|
fprintf(stdout, output);
|
|
}
|
|
|
|
//buffer the prints
|
|
static char* globalPrintBuffer = NULL;
|
|
static size_t globalPrintCapacity = 0;
|
|
static size_t globalPrintCount = 0;
|
|
|
|
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
|
static char quotes = 0; //set to 0 to not show string quotes
|
|
|
|
static void printToBuffer(const char* str) {
|
|
while (strlen(str) + globalPrintCount > globalPrintCapacity) {
|
|
int oldCapacity = globalPrintCapacity;
|
|
|
|
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
|
|
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
|
}
|
|
|
|
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
|
|
globalPrintCount += strlen(str);
|
|
}
|
|
|
|
//BUGFIX: <array | dictionary> is handled oddly for specific reasons, so this flag is for the debug output of the type
|
|
bool printTypeMarker = true;
|
|
|
|
//hash util functions
|
|
static unsigned int hashString(const char* string, int length) {
|
|
unsigned int hash = 2166136261u;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
hash *= string[i];
|
|
hash *= 16777619;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
static unsigned int hash(unsigned int x) {
|
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
|
x = (x >> 16) ^ x;
|
|
return x;
|
|
}
|
|
|
|
//exposed functions
|
|
void printLiteral(Literal literal) {
|
|
printLiteralCustom(literal, stdoutWrapper);
|
|
}
|
|
|
|
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
|
switch(literal.type) {
|
|
case LITERAL_NULL:
|
|
printFn("null");
|
|
break;
|
|
|
|
case LITERAL_BOOLEAN:
|
|
printFn(AS_BOOLEAN(literal) ? "true" : "false");
|
|
break;
|
|
|
|
case LITERAL_INTEGER: {
|
|
char buffer[256];
|
|
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
|
|
printFn(buffer);
|
|
}
|
|
break;
|
|
|
|
case LITERAL_FLOAT: {
|
|
char buffer[256];
|
|
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
|
|
printFn(buffer);
|
|
}
|
|
break;
|
|
|
|
case LITERAL_STRING: {
|
|
char buffer[4096];
|
|
if (!quotes) {
|
|
snprintf(buffer, 4096, "%.*s", STRLEN(literal), AS_STRING(literal));
|
|
}
|
|
else {
|
|
snprintf(buffer, 4096, "%c%.*s%c", quotes, STRLEN(literal), AS_STRING(literal), quotes);
|
|
}
|
|
printFn(buffer);
|
|
}
|
|
break;
|
|
|
|
case LITERAL_ARRAY: {
|
|
LiteralArray* ptr = AS_ARRAY(literal);
|
|
|
|
//hold potential parent-call buffers
|
|
char* cacheBuffer = globalPrintBuffer;
|
|
globalPrintBuffer = NULL;
|
|
int cacheCapacity = globalPrintCapacity;
|
|
globalPrintCapacity = 0;
|
|
int cacheCount = globalPrintCount;
|
|
globalPrintCount = 0;
|
|
|
|
//print the contents to the global buffer
|
|
printToBuffer("[");
|
|
for (int i = 0; i < ptr->count; i++) {
|
|
quotes = '"';
|
|
printLiteralCustom(ptr->literals[i], printToBuffer);
|
|
|
|
if (i + 1 < ptr->count) {
|
|
printToBuffer(",");
|
|
}
|
|
}
|
|
printToBuffer("]");
|
|
|
|
//swap the parent-call buffer back into place
|
|
char* printBuffer = globalPrintBuffer;
|
|
int printCapacity = globalPrintCapacity;
|
|
int printCount = globalPrintCount;
|
|
|
|
globalPrintBuffer = cacheBuffer;
|
|
globalPrintCapacity = cacheCapacity;
|
|
globalPrintCount = cacheCount;
|
|
|
|
//finally, output and cleanup
|
|
printFn(printBuffer);
|
|
FREE_ARRAY(char, printBuffer, printCapacity);
|
|
quotes = 0;
|
|
}
|
|
break;
|
|
|
|
case LITERAL_DICTIONARY: {
|
|
LiteralDictionary* ptr = AS_DICTIONARY(literal);
|
|
|
|
//hold potential parent-call buffers
|
|
char* cacheBuffer = globalPrintBuffer;
|
|
globalPrintBuffer = NULL;
|
|
int cacheCapacity = globalPrintCapacity;
|
|
globalPrintCapacity = 0;
|
|
int cacheCount = globalPrintCount;
|
|
globalPrintCount = 0;
|
|
|
|
//print the contents to the global buffer
|
|
int delimCount = 0;
|
|
printToBuffer("[");
|
|
for (int i = 0; i < ptr->capacity; i++) {
|
|
if (IS_NULL(ptr->entries[i].key)) {
|
|
continue;
|
|
}
|
|
|
|
if (delimCount++ > 0) {
|
|
printToBuffer(",");
|
|
}
|
|
|
|
quotes = '"';
|
|
printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
|
printToBuffer(":");
|
|
quotes = '"';
|
|
printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
|
}
|
|
|
|
//empty dicts MUST have a ":" printed
|
|
if (ptr->count == 0) {
|
|
printToBuffer(":");
|
|
}
|
|
|
|
printToBuffer("]");
|
|
|
|
//swap the parent-call buffer back into place
|
|
char* printBuffer = globalPrintBuffer;
|
|
int printCapacity = globalPrintCapacity;
|
|
int printCount = globalPrintCount;
|
|
|
|
globalPrintBuffer = cacheBuffer;
|
|
globalPrintCapacity = cacheCapacity;
|
|
globalPrintCount = cacheCount;
|
|
|
|
//finally, output and cleanup
|
|
printFn(printBuffer);
|
|
FREE_ARRAY(char, printBuffer, printCapacity);
|
|
quotes = 0;
|
|
}
|
|
break;
|
|
|
|
case LITERAL_IDENTIFIER: {
|
|
char buffer[256];
|
|
snprintf(buffer, 256, "%.*s", STRLEN_I(literal), AS_IDENTIFIER(literal));
|
|
printFn(buffer);
|
|
}
|
|
break;
|
|
|
|
case LITERAL_TYPE: {
|
|
//hold potential parent-call buffers
|
|
char* cacheBuffer = globalPrintBuffer;
|
|
globalPrintBuffer = NULL;
|
|
int cacheCapacity = globalPrintCapacity;
|
|
globalPrintCapacity = 0;
|
|
int cacheCount = globalPrintCount;
|
|
globalPrintCount = 0;
|
|
|
|
//print the type
|
|
int iterations = 0;
|
|
if (printTypeMarker) {
|
|
printToBuffer("<");
|
|
}
|
|
|
|
for (int i = 1; i < 8; i ++) { //0th bit is const
|
|
//zero mask = any type, anys can't be const
|
|
if (AS_TYPE(literal).mask == MASK_ANY) {
|
|
printToBuffer("any");
|
|
break;
|
|
}
|
|
|
|
if (AS_TYPE(literal).mask & MASK(i)) {
|
|
//pretty print
|
|
if (iterations++ > 0) {
|
|
printToBuffer(" | ");
|
|
}
|
|
|
|
//special case for array AND dictionary
|
|
if (i == TYPE_ARRAY) {
|
|
if ((AS_TYPE(literal).mask & (MASK_ARRAY|MASK_DICTIONARY)) == (MASK_ARRAY|MASK_DICTIONARY)) {
|
|
int pCache = printTypeMarker;
|
|
printTypeMarker = false;
|
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[0], printToBuffer);
|
|
printTypeMarker = pCache;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (i == TYPE_DICTIONARY) {
|
|
if ((AS_TYPE(literal).mask & (MASK_ARRAY|MASK_DICTIONARY)) == (MASK_ARRAY|MASK_DICTIONARY)) {
|
|
int pCache = printTypeMarker;
|
|
printTypeMarker = false;
|
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[1], printToBuffer);
|
|
printTypeMarker = pCache;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch(i) {
|
|
case TYPE_BOOLEAN:
|
|
printToBuffer("bool");
|
|
break;
|
|
|
|
case TYPE_INTEGER:
|
|
printToBuffer("int");
|
|
break;
|
|
|
|
case TYPE_FLOAT:
|
|
printToBuffer("float");
|
|
break;
|
|
|
|
case TYPE_STRING:
|
|
printToBuffer("string");
|
|
break;
|
|
|
|
case TYPE_ARRAY: {
|
|
//print all in the array
|
|
printToBuffer("[");
|
|
int it = 0;
|
|
for (int a = 0; a < AS_TYPE(literal).count; a++) {
|
|
if (it++ > 0) {
|
|
printToBuffer("] | [");
|
|
}
|
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[a], printToBuffer);
|
|
}
|
|
printToBuffer("]");
|
|
}
|
|
break;
|
|
|
|
case TYPE_DICTIONARY: {
|
|
printToBuffer("[");
|
|
|
|
int it = 0;
|
|
for (int a = 0; a < AS_TYPE(literal).count; a += 2) {
|
|
if (it++ > 0) {
|
|
printToBuffer("] | [");
|
|
}
|
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[a], printToBuffer);
|
|
printToBuffer(":");
|
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[a + 1], printToBuffer);
|
|
}
|
|
printToBuffer("]");
|
|
}
|
|
break;
|
|
|
|
//TODO: function
|
|
}
|
|
}
|
|
}
|
|
|
|
//const (printed last)
|
|
if (AS_TYPE(literal).mask & MASK_CONST) {
|
|
if (iterations++ > 0) {
|
|
printToBuffer(" ");
|
|
}
|
|
printToBuffer("const");
|
|
}
|
|
|
|
if (printTypeMarker) {
|
|
printToBuffer(">");
|
|
}
|
|
|
|
//swap the parent-call buffer back into place
|
|
char* printBuffer = globalPrintBuffer;
|
|
int printCapacity = globalPrintCapacity;
|
|
int printCount = globalPrintCount;
|
|
|
|
globalPrintBuffer = cacheBuffer;
|
|
globalPrintCapacity = cacheCapacity;
|
|
globalPrintCount = cacheCount;
|
|
|
|
//finally, output and cleanup
|
|
printFn(printBuffer);
|
|
FREE_ARRAY(char, printBuffer, printCapacity);
|
|
quotes = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//should never bee seen
|
|
fprintf(stderr, "[Internal] Unrecognized literal type in print: %d\n", literal.type);
|
|
}
|
|
}
|
|
|
|
void freeLiteral(Literal literal) {
|
|
if (IS_STRING(literal)) {
|
|
FREE_ARRAY(char, AS_STRING(literal), STRLEN(literal));
|
|
return;
|
|
}
|
|
|
|
if (IS_IDENTIFIER(literal)) {
|
|
FREE_ARRAY(char, AS_IDENTIFIER(literal), STRLEN_I(literal));
|
|
return;
|
|
}
|
|
|
|
if (IS_TYPE(literal)) {
|
|
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
|
freeLiteral(((Literal*)(AS_TYPE(literal).subtypes))[i]);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool _isTruthy(Literal x) {
|
|
return (IS_NULL(x) || (IS_BOOLEAN(x) && AS_BOOLEAN(x)) || (IS_INTEGER(x) && AS_INTEGER(x) != 0) || (IS_FLOAT(x) && AS_FLOAT(x) != 0));
|
|
}
|
|
|
|
Literal _toStringLiteral(char* str) {
|
|
return ((Literal){LITERAL_STRING, {.string.ptr = (char*)str, .string.length = strlen((char*)str)}});
|
|
}
|
|
|
|
Literal _toIdentifierLiteral(char* str, int length) {
|
|
return ((Literal){LITERAL_IDENTIFIER,{.identifier.ptr = (char*)str,.identifier.length = length, .identifier.hash = hashString(str, length)}});
|
|
}
|
|
|
|
Literal* _typePushSubtype(Literal* lit, unsigned char submask) {
|
|
if (AS_TYPE(*lit).count + 1 > AS_TYPE(*lit).capacity) {
|
|
int oldCapacity = AS_TYPE(*lit).capacity;
|
|
|
|
AS_TYPE(*lit).capacity = GROW_CAPACITY(oldCapacity);
|
|
AS_TYPE(*lit).subtypes = GROW_ARRAY(Literal, AS_TYPE(*lit).subtypes, oldCapacity, AS_TYPE(*lit).capacity);
|
|
}
|
|
|
|
//actually push
|
|
((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count++ ] = TO_TYPE_LITERAL( submask );
|
|
return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ];
|
|
}
|
|
|
|
char* copyString(char* original, int length) {
|
|
char* buffer = ALLOCATE(char, length + 1);
|
|
strncpy(buffer, original, length);
|
|
buffer[length] = '\0';
|
|
return buffer;
|
|
}
|
|
|
|
bool literalsAreEqual(Literal lhs, Literal rhs) {
|
|
if (lhs.type != rhs.type) {
|
|
// ints and floats are compatible
|
|
if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) {
|
|
if (IS_INTEGER(lhs)) {
|
|
return AS_INTEGER(lhs) + AS_FLOAT(rhs);
|
|
}
|
|
else {
|
|
return AS_FLOAT(lhs) + AS_INTEGER(rhs);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
switch(lhs.type) {
|
|
case LITERAL_BOOLEAN:
|
|
return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs);
|
|
|
|
case LITERAL_INTEGER:
|
|
return AS_INTEGER(lhs) == AS_INTEGER(rhs);
|
|
|
|
case LITERAL_FLOAT:
|
|
return AS_FLOAT(lhs) == AS_FLOAT(rhs);
|
|
|
|
case LITERAL_STRING:
|
|
if (STRLEN(lhs) != STRLEN(rhs)) {
|
|
return false;
|
|
}
|
|
return !strncmp(AS_STRING(lhs), AS_STRING(rhs), STRLEN(lhs));
|
|
|
|
case LITERAL_ARRAY:
|
|
if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < AS_ARRAY(lhs)->count; i++) {
|
|
if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
case LITERAL_DICTIONARY:
|
|
//relatively slow, especially when nested
|
|
for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) {
|
|
if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
|
//check it exists in rhs
|
|
if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) {
|
|
return false;
|
|
}
|
|
|
|
//compare the values
|
|
if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key) )) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
|
|
//TODO: functions
|
|
|
|
case LITERAL_IDENTIFIER:
|
|
if (HASH_I(lhs) != HASH_I(rhs) && STRLEN_I(lhs) != STRLEN_I(rhs)) {
|
|
return false;
|
|
}
|
|
return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), STRLEN_I(lhs));
|
|
|
|
case LITERAL_TYPE:
|
|
if (AS_TYPE(lhs).mask != AS_TYPE(rhs).mask) {
|
|
return false;
|
|
}
|
|
|
|
if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) {
|
|
return false;
|
|
}
|
|
|
|
//TODO: array & dictionaries (slot 0 is an array collection, slot 1 is a dictionary collection)
|
|
if ((AS_TYPE(lhs).mask & (MASK_ARRAY|MASK_DICTIONARY)) == (MASK_ARRAY|MASK_DICTIONARY)) {
|
|
//check arrays
|
|
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[0], ((Literal*)(AS_TYPE(rhs).subtypes))[0])) {
|
|
return false;
|
|
}
|
|
|
|
//check dictionaries
|
|
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[1], ((Literal*)(AS_TYPE(rhs).subtypes))[1])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//TODO: arrays (out of order)
|
|
if (AS_TYPE(lhs).mask & MASK_ARRAY) {
|
|
for (int i = 0; i < AS_TYPE(lhs).count; i++) {
|
|
bool match = false;
|
|
for (int j = 0; j < AS_TYPE(rhs).count; j++) {
|
|
//compare
|
|
if (literalsAreEqual( ((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[j] )) {
|
|
match = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: dictionaries (out of order)
|
|
if (AS_TYPE(lhs).mask & MASK_DICTIONARY) {
|
|
for (int i = 0; i < AS_TYPE(lhs).count; i += 2) {
|
|
bool match = false;
|
|
for (int j = 0; j < AS_TYPE(rhs).count; j += 2) {
|
|
//compare
|
|
if (literalsAreEqual( ((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[j] ) && literalsAreEqual( ((Literal*)(AS_TYPE(lhs).subtypes))[i + 1], ((Literal*)(AS_TYPE(rhs).subtypes))[j + 1] )) {
|
|
match = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
default:
|
|
//should never bee seen
|
|
fprintf(stderr, "[Internal] Unrecognized literal type in equality: %d\n", lhs.type);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int hashLiteral(Literal lit) {
|
|
switch(lit.type) {
|
|
case LITERAL_NULL:
|
|
return 0;
|
|
|
|
case LITERAL_BOOLEAN:
|
|
return AS_BOOLEAN(lit) ? 1 : 0;
|
|
|
|
case LITERAL_INTEGER:
|
|
return hash((unsigned int)AS_INTEGER(lit));
|
|
|
|
case LITERAL_FLOAT:
|
|
return hash(*(unsigned int*)(&AS_FLOAT(lit)));
|
|
|
|
case LITERAL_STRING:
|
|
return hashString(AS_STRING(lit), STRLEN(lit));
|
|
|
|
case LITERAL_ARRAY: {
|
|
unsigned int res = 0;
|
|
for (int i = 0; i < AS_DICTIONARY(lit)->count; i++) {
|
|
res += hashLiteral(AS_ARRAY(lit)->literals[i]);
|
|
}
|
|
return hash(res);
|
|
}
|
|
|
|
case LITERAL_DICTIONARY: {
|
|
unsigned int res = 0;
|
|
for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) {
|
|
if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
|
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key);
|
|
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value);
|
|
}
|
|
}
|
|
return hash(res);
|
|
}
|
|
|
|
case LITERAL_IDENTIFIER:
|
|
return HASH_I(lit); //pre-computed
|
|
|
|
default:
|
|
//should never bee seen
|
|
fprintf(stderr, "[Internal] Unrecognized literal type in hash: %d\n", lit.type);
|
|
return 0;
|
|
}
|
|
}
|