mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Started working on Toy_Scope, incomplete
I only worked for a couple hours today.
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
typedef enum Toy_OpcodeType {
|
typedef enum Toy_OpcodeType {
|
||||||
//variable instructions
|
//variable instructions
|
||||||
TOY_OPCODE_READ,
|
TOY_OPCODE_READ,
|
||||||
TOY_OPCODE_LOAD,
|
|
||||||
TOY_OPCODE_LOAD_LONG, //corner case
|
|
||||||
TOY_OPCODE_DECLARE,
|
TOY_OPCODE_DECLARE,
|
||||||
TOY_OPCODE_ASSIGN,
|
TOY_OPCODE_ASSIGN,
|
||||||
TOY_OPCODE_ACCESS,
|
TOY_OPCODE_ACCESS,
|
||||||
@@ -18,7 +16,7 @@ typedef enum Toy_OpcodeType {
|
|||||||
|
|
||||||
//comparison instructions
|
//comparison instructions
|
||||||
TOY_OPCODE_COMPARE_EQUAL,
|
TOY_OPCODE_COMPARE_EQUAL,
|
||||||
// TOY_OPCODE_COMPARE_NOT,
|
// TOY_OPCODE_COMPARE_NOT, //NOTE: optimized into a composite of TOY_OPCODE_COMPARE_EQUAL + TOY_OPCODE_NEGATE
|
||||||
TOY_OPCODE_COMPARE_LESS,
|
TOY_OPCODE_COMPARE_LESS,
|
||||||
TOY_OPCODE_COMPARE_LESS_EQUAL,
|
TOY_OPCODE_COMPARE_LESS_EQUAL,
|
||||||
TOY_OPCODE_COMPARE_GREATER,
|
TOY_OPCODE_COMPARE_GREATER,
|
||||||
|
|||||||
149
source/toy_scope.c.txt
Normal file
149
source/toy_scope.c.txt
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#include "toy_scope.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "toy_print.h"
|
||||||
|
|
||||||
|
//utils
|
||||||
|
static void incrementRefCount(Toy_Scope* scope) {
|
||||||
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
||||||
|
iter->refCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decrementRefCount(Toy_Scope* scope) {
|
||||||
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
||||||
|
iter->refCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Toy_Value* lookupScope(Toy_Scope* scope, Toy_Value key, unsigned int hash, bool recursive) {
|
||||||
|
//terminate
|
||||||
|
if (scope == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy and modify the code from Toy_lookupTable, so it can behave slightly differently
|
||||||
|
unsigned int probe = hash % scope->table->capacity;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
//found the entry
|
||||||
|
if (TOY_VALUES_ARE_EQUAL(scope->table->data[probe].key, key)) {
|
||||||
|
return &(scope->table->data[probe].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if its an empty slot
|
||||||
|
if (TOY_VALUE_IS_NULL(scope->table->data[probe].key)) {
|
||||||
|
return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//adjust and continue
|
||||||
|
probe = (probe + 1) % scope->table->capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
||||||
|
Toy_Scope* newScope = Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
||||||
|
|
||||||
|
newScope->next = scope;
|
||||||
|
newScope->table = Toy_allocateTable();
|
||||||
|
newScope->refCount = 0;
|
||||||
|
|
||||||
|
incrementRefCount(newScope);
|
||||||
|
|
||||||
|
return newScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
||||||
|
decrementRefCount(scope);
|
||||||
|
|
||||||
|
if (scope->refCount == 0) {
|
||||||
|
Toy_freeTable(scope->table);
|
||||||
|
scope->table = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Scope* deepCopyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
||||||
|
Toy_Scope* newScope = Toy_pushScope(bucketHandle, scope->next);
|
||||||
|
|
||||||
|
//forcibly copy the contents
|
||||||
|
for (int i = 0; i < scope->table->capacity; i++) {
|
||||||
|
if (!TOY_VALUE_IS_NULL(scope->table->data[i].key)) {
|
||||||
|
Toy_insertTable(&newScope->table, scope->table->data[i].key, scope->table->data[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_declareScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key, Toy_Value value) {
|
||||||
|
if (key.type != TOY_STRING_NAME) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), false);
|
||||||
|
|
||||||
|
if (valuePtr != NULL) {
|
||||||
|
|
||||||
|
char buffer[key.length + 256];
|
||||||
|
snprintf(buffer, "Can't redefine a variable: %s", key.as.name.data);
|
||||||
|
Toy_error(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_insertTable(&scope->table, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_assignScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key, Toy_Value value) {
|
||||||
|
if (key.type != TOY_STRING_NAME) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), true);
|
||||||
|
|
||||||
|
if (valuePtr == NULL) {
|
||||||
|
char buffer[key.length + 256];
|
||||||
|
snprintf(buffer, "Undefined variable: %s", key.as.name.data);
|
||||||
|
Toy_error(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*valuePtr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value Toy_accessScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key) {
|
||||||
|
if (key.type != TOY_STRING_NAME) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), true);
|
||||||
|
|
||||||
|
if (valuePtr == NULL) {
|
||||||
|
char buffer[key.length + 256];
|
||||||
|
snprintf(buffer, "Undefined variable: %s", key.as.name.data);
|
||||||
|
Toy_error(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *valuePtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Toy_isDeclaredScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key) {
|
||||||
|
if (key.type != TOY_STRING_NAME) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), true);
|
||||||
|
|
||||||
|
return valuePtr != NULL;
|
||||||
|
}
|
||||||
28
source/toy_scope.h.txt
Normal file
28
source/toy_scope.h.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_bucket.h"
|
||||||
|
#include "toy_value.h"
|
||||||
|
#include "toy_string.h"
|
||||||
|
#include "toy_table.h"
|
||||||
|
|
||||||
|
//wraps Toy_Table, restricting keys to name strings, and handles scopes as a linked list
|
||||||
|
typedef struct Toy_Scope {
|
||||||
|
struct Toy_Scope* next;
|
||||||
|
Toy_Table* table;
|
||||||
|
unsigned int refCount;
|
||||||
|
} Toy_Scope;
|
||||||
|
|
||||||
|
//handle deep scopes
|
||||||
|
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
|
||||||
|
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||||
|
|
||||||
|
TOY_API Toy_Scope* deepCopyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
|
||||||
|
|
||||||
|
//manage the contents
|
||||||
|
TOY_API void Toy_declareScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key, Toy_Value value);
|
||||||
|
TOY_API void Toy_assignScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key, Toy_Value value);
|
||||||
|
TOY_API Toy_Value Toy_accessScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key);
|
||||||
|
|
||||||
|
TOY_API bool Toy_isDeclaredScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key);
|
||||||
@@ -34,6 +34,17 @@ static void decrementRefCount(Toy_String* str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int hashCString(const char* string) {
|
||||||
|
unsigned int hash = 2166136261u;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; string[i]; i++) {
|
||||||
|
hash *= string[i];
|
||||||
|
hash ^= 16777619;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring) {
|
Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring) {
|
||||||
int length = strlen(cstring);
|
int length = strlen(cstring);
|
||||||
@@ -59,7 +70,7 @@ Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstrin
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname) {
|
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type) {
|
||||||
int length = strlen(cname);
|
int length = strlen(cname);
|
||||||
|
|
||||||
if (length > TOY_STRING_MAX_LENGTH) {
|
if (length > TOY_STRING_MAX_LENGTH) {
|
||||||
@@ -75,6 +86,7 @@ TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char*
|
|||||||
ret->cachedHash = 0; //don't calc until needed
|
ret->cachedHash = 0; //don't calc until needed
|
||||||
memcpy(ret->as.name.data, cname, length + 1);
|
memcpy(ret->as.name.data, cname, length + 1);
|
||||||
ret->as.name.data[length] = '\0';
|
ret->as.name.data[length] = '\0';
|
||||||
|
ret->as.name.type = type;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -271,3 +283,23 @@ int Toy_compareStrings(Toy_String* left, Toy_String* right) {
|
|||||||
|
|
||||||
return deepCompareUtil(left, right, &leftHead, &rightHead);
|
return deepCompareUtil(left, right, &leftHead, &rightHead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int Toy_hashString(Toy_String* str) {
|
||||||
|
if (str->cachedHash != 0) {
|
||||||
|
return str->cachedHash;
|
||||||
|
}
|
||||||
|
else if (str->type == TOY_STRING_NODE) {
|
||||||
|
//TODO: I wonder if it would be possible to discretely swap the composite node string with a new leaf string here? Would that speed up other parts of the code by not having to walk the tree in future?
|
||||||
|
char* buffer = Toy_getStringRawBuffer(str);
|
||||||
|
str->cachedHash = hashCString(buffer);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
else if (str->type == TOY_STRING_LEAF) {
|
||||||
|
str->cachedHash = hashCString(str->as.leaf.data);
|
||||||
|
}
|
||||||
|
else if (str->type == TOY_STRING_NAME) {
|
||||||
|
str->cachedHash = hashCString(str->as.name.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str->cachedHash;
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ typedef struct Toy_String { //32 | 64 BITNESS
|
|||||||
TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring);
|
TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring);
|
||||||
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, int length);
|
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, int length);
|
||||||
|
|
||||||
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname); //for variable names
|
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type); //for variable names
|
||||||
|
|
||||||
TOY_API Toy_String* Toy_copyString(Toy_Bucket** bucketHandle, Toy_String* str);
|
TOY_API Toy_String* Toy_copyString(Toy_Bucket** bucketHandle, Toy_String* str);
|
||||||
TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str);
|
TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str);
|
||||||
@@ -57,3 +57,4 @@ TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on
|
|||||||
|
|
||||||
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right); //return value mimics strcmp()
|
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right); //return value mimics strcmp()
|
||||||
|
|
||||||
|
TOY_API unsigned int Toy_hashString(Toy_String* string);
|
||||||
|
|||||||
@@ -70,18 +70,7 @@ bool Toy_private_isEqual(Toy_Value left, Toy_Value right) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//hash utils
|
//hash util
|
||||||
static unsigned int hashCString(const char* string) {
|
|
||||||
unsigned int hash = 2166136261u;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; string[i]; i++) {
|
|
||||||
hash *= string[i];
|
|
||||||
hash ^= 16777619;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int hashUInt(unsigned int x) {
|
static unsigned int hashUInt(unsigned int x) {
|
||||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
@@ -89,7 +78,6 @@ static unsigned int hashUInt(unsigned int x) {
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned int Toy_hashValue(Toy_Value value) {
|
unsigned int Toy_hashValue(Toy_Value value) {
|
||||||
switch(value.type) {
|
switch(value.type) {
|
||||||
case TOY_VALUE_NULL:
|
case TOY_VALUE_NULL:
|
||||||
@@ -104,27 +92,8 @@ unsigned int Toy_hashValue(Toy_Value value) {
|
|||||||
case TOY_VALUE_FLOAT:
|
case TOY_VALUE_FLOAT:
|
||||||
return hashUInt( *((int*)(&TOY_VALUE_AS_FLOAT(value))) );
|
return hashUInt( *((int*)(&TOY_VALUE_AS_FLOAT(value))) );
|
||||||
|
|
||||||
case TOY_VALUE_STRING: {
|
case TOY_VALUE_STRING:
|
||||||
Toy_String* str = TOY_VALUE_AS_STRING(value);
|
return Toy_hashString(TOY_VALUE_AS_STRING(value));
|
||||||
|
|
||||||
if (str->cachedHash != 0) {
|
|
||||||
return str->cachedHash;
|
|
||||||
}
|
|
||||||
else if (str->type == TOY_STRING_NODE) {
|
|
||||||
//TODO: I wonder if it would be possible to discretely swap the composite node string with a new leaf string here? Would that speed up other parts of the code by not having to walk the tree in future?
|
|
||||||
char* buffer = Toy_getStringRawBuffer(str);
|
|
||||||
str->cachedHash = hashCString(buffer);
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
else if (str->type == TOY_STRING_LEAF) {
|
|
||||||
str->cachedHash = hashCString(str->as.leaf.data);
|
|
||||||
}
|
|
||||||
else if (str->type == TOY_STRING_NAME) {
|
|
||||||
str->cachedHash = hashCString(str->as.name.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return str->cachedHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TOY_VALUE_ARRAY:
|
case TOY_VALUE_ARRAY:
|
||||||
case TOY_VALUE_DICTIONARY:
|
case TOY_VALUE_DICTIONARY:
|
||||||
|
|||||||
@@ -371,8 +371,6 @@ static void process(Toy_VM* vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
//not yet implemented
|
//not yet implemented
|
||||||
case TOY_OPCODE_LOAD:
|
|
||||||
case TOY_OPCODE_LOAD_LONG:
|
|
||||||
case TOY_OPCODE_DECLARE:
|
case TOY_OPCODE_DECLARE:
|
||||||
case TOY_OPCODE_ASSIGN:
|
case TOY_OPCODE_ASSIGN:
|
||||||
case TOY_OPCODE_ACCESS:
|
case TOY_OPCODE_ACCESS:
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ int test_string_allocation() {
|
|||||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
|
||||||
const char* cstring = "Hello world";
|
const char* cstring = "Hello world";
|
||||||
Toy_String* str = Toy_createNameString(&bucket, cstring);
|
Toy_String* str = Toy_createNameString(&bucket, cstring, TOY_VALUE_NULL);
|
||||||
|
|
||||||
//shallow and deep
|
//shallow and deep
|
||||||
Toy_String* shallow = Toy_copyString(&bucket, str);
|
Toy_String* shallow = Toy_copyString(&bucket, str);
|
||||||
@@ -652,9 +652,9 @@ int test_string_equality() {
|
|||||||
{
|
{
|
||||||
//setup
|
//setup
|
||||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
Toy_String* helloWorldOne = Toy_createNameString(&bucket, "Hello world");
|
Toy_String* helloWorldOne = Toy_createNameString(&bucket, "Hello world", TOY_VALUE_NULL);
|
||||||
Toy_String* helloWorldTwo = Toy_createNameString(&bucket, "Hello world");
|
Toy_String* helloWorldTwo = Toy_createNameString(&bucket, "Hello world", TOY_VALUE_NULL);
|
||||||
Toy_String* helloEveryone = Toy_createNameString(&bucket, "Hello everyone");
|
Toy_String* helloEveryone = Toy_createNameString(&bucket, "Hello everyone", TOY_VALUE_NULL); //TODO: compare types?
|
||||||
|
|
||||||
int result = 0; //for print the errors
|
int result = 0; //for print the errors
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user