From 31519e8a586791d60e6e56e558c2a3bf973cc1f9 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 26 Jan 2020 08:02:17 +0100 Subject: Implement plus operator and indexing of strings --- example.tsl | 6 +++ include/program.h | 2 + include/std_gc/hash_map.h | 33 +++++++++++- include/std_gc/list.h | 2 +- include/value.h | 12 ++++- src/program.c | 52 +++++++++++++++++-- src/std_gc/hash_map.c | 37 +++++++++---- src/std_gc/list.c | 2 +- src/value.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 257 insertions(+), 19 deletions(-) diff --git a/example.tsl b/example.tsl index e85b779..e868f77 100644 --- a/example.tsl +++ b/example.tsl @@ -18,6 +18,12 @@ value3 = null # setv "value4" value4 = "hello world" +# loadv "value4" +# loadn 0.000000 +# mindex +# setv "c" +c = value4[0] + # loads "hello" # loads "world" # loadn 5 diff --git a/include/program.h b/include/program.h index bbd3f37..52b4fe4 100644 --- a/include/program.h +++ b/include/program.h @@ -12,6 +12,7 @@ typedef enum { TSL_STACK_VALUE_TYPE_STRING, TSL_STACK_VALUE_TYPE_NULL, TSL_STACK_VALUE_TYPE_FUNCTION, + TSL_STACK_VALUE_TYPE_VARIABLE_NAME, TSL_STACK_VALUE_TYPE_VARIABLE, TSL_STACK_VALUE_TYPE_LIST, TSL_STACK_VALUE_TYPE_MAP, @@ -25,6 +26,7 @@ typedef struct { double number; TslBool boolean; TslStringView str; + TslValue variable; } data; TslStackValueType type; } TslStackValue; diff --git a/include/std_gc/hash_map.h b/include/std_gc/hash_map.h index e1a016d..fb6221b 100644 --- a/include/std_gc/hash_map.h +++ b/include/std_gc/hash_map.h @@ -2,15 +2,30 @@ #define TSL_HASH_MAP_H #include "../forward_decl.h" +#include "../value.h" #include #include +typedef struct TslHashMapNode TslHashMapNode; +struct TslHashMapNode { + uint64_t hash; + TslValue key; + TslValue value; + TslHashMapNode *next; +}; + /* TODO: Optimize small hash map by using the members of the struct instead of allocating on heap */ -typedef struct { +struct TslHashMap { void *buckets_data; /* value=TslHashMapNode */ size_t buckets_size; size_t buckets_capacity; -} TslHashMap; +}; + +typedef struct { + TslHashMap *hash_map; + size_t index; + TslHashMapNode *node; +} TslHashMapIterator; void tsl_hash_map_init(TslHashMap *self); @@ -27,4 +42,18 @@ TslValue* tsl_hash_map_get(TslHashMap *self, const TslValue *key); */ TslValue* tsl_hash_map_get_or_create(TslHashMap *self, const TslValue *key); +/* + How to use the iterator: + + TslHashMapIterator iterator; + tsl_hash_map_create_iterator(hash_map, &iterator); + while(tsl_hash_map_iterator_next(&iterator)) { + TslValue *key = &iterator.node->key; + TslValue *value = &iterator.node->value; + } +*/ + +void tsl_hash_map_create_iterator(TslHashMap *self, TslHashMapIterator *iterator); +int tsl_hash_map_iterator_next(TslHashMapIterator *self); + #endif /* TSL_HASH_MAP_H */ diff --git a/include/std_gc/list.h b/include/std_gc/list.h index 469f585..a97f311 100644 --- a/include/std_gc/list.h +++ b/include/std_gc/list.h @@ -16,6 +16,6 @@ int tsl_list_set_capacity_hint(TslList *self, size_t capacity); TslValue* tsl_list_begin(TslList *self); TslValue* tsl_list_end(TslList *self); /* Returns NULL if index is out of bounds of the list */ -TslValue* tsl_list_get(TslList *self, double index); +TslValue* tsl_list_get(const TslList *self, double index); #endif /* TSL_LIST_H */ diff --git a/include/value.h b/include/value.h index 7dd6d28..b90b446 100644 --- a/include/value.h +++ b/include/value.h @@ -6,7 +6,6 @@ #include #include "string_view.h" #include "std_gc/list.h" -#include "std_gc/hash_map.h" typedef enum { TSL_TYPE_NULL, @@ -36,10 +35,16 @@ typedef struct { /* This is an index to the function */ typedef int TslFunction; +typedef struct TslHashMap TslHashMap; + struct TslValue { union { TslNumber number; TslString *string; + /* + TODO: Make this an int that refers to the index in the bytecode which contains the string view. + Then the size of this union will be 8 bytes instead of 16 bytes on 64-bit systems. + */ TslStringView string_ref; TslBool boolean; TslBool null; @@ -58,4 +63,9 @@ int tsl_value_equals(const TslValue *lhs, const TslValue *rhs); /* This should be called before setting/modifying the data of the value */ void tsl_value_clear(TslValue *self); +int tsl_value_add(const TslValue *lhs_value, const TslValue *rhs_value, TslValue *result); + +/* Returns NULL if index is out of bounds of the list */ +TslValue* tsl_string_ref_get(const TslStringView *self, double index); + #endif /* TSL_VALUE_H */ diff --git a/src/program.c b/src/program.c index 78533f6..2f0a9df 100644 --- a/src/program.c +++ b/src/program.c @@ -47,6 +47,29 @@ static int program_output_temp(char *data, int size, void *userdata) { return 1; } +static void tsl_string_to_ref(const TslString *self, TslStringView *result) { + result->data = self->data; + result->size = self->size; +} + +static TslProgramResult tsl_string_ref_get_item_at_index(const TslStringView *self, const TslValue *key, TslValue *result) { + TslValue *list_item; + if(key->type != TSL_TYPE_NUMBER) { + /* TODO: Print type as string */ + fprintf(stderr, "Error: Unable to index string %s using a variable of type %d. The index has to be a number\n", "TODO", key->type); + return TSL_PROGRAM_RESULT_ERR; + } + + list_item = tsl_string_ref_get(self, key->data.number); + if(list_item) { + *result = *list_item; + } else { + result->type = TSL_TYPE_NULL; + } + + return TSL_PROGRAM_RESULT_OK; +} + static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslStackValue *src, TslValue *dst) { tsl_value_clear(dst); switch(src->type) { @@ -93,7 +116,7 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS dst->type = TSL_TYPE_FUNCTION; break; } - case TSL_STACK_VALUE_TYPE_VARIABLE: { + case TSL_STACK_VALUE_TYPE_VARIABLE_NAME: { TslValue *var; TslValue map_key; map_key.type = TSL_TYPE_STRING_REF; @@ -108,6 +131,10 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS *dst = *var; break; } + case TSL_STACK_VALUE_TYPE_VARIABLE: { + *dst = src->data.variable; + break; + } case TSL_STACK_VALUE_TYPE_LIST: { TslStackValue *items_begin = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * src->data.integer); TslStackValue *items_end = items_begin + src->data.integer; @@ -174,6 +201,7 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS } else { dst->type = TSL_TYPE_NULL; } + return TSL_PROGRAM_RESULT_OK; } else if(var.type == TSL_TYPE_MAP) { TslValue *map_value = tsl_hash_map_get(var.data.map, &key); if(map_value) { @@ -181,12 +209,18 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS } else { dst->type = TSL_TYPE_NULL; } + return TSL_PROGRAM_RESULT_OK; + } else if(var.type == TSL_TYPE_STRING) { + TslStringView str_ref; + tsl_string_to_ref(var.data.string, &str_ref); + return tsl_string_ref_get_item_at_index(&str_ref, &key, dst); + } else if(var.type == TSL_TYPE_STRING_REF) { + return tsl_string_ref_get_item_at_index(&var.data.string_ref, &key, dst); } else { /* TODO: Print type as string */ fprintf(stderr, "Error: Unable to index data of type %d. Expected list or map\n", var.type); return TSL_PROGRAM_RESULT_ERR; } - break; } case TSL_STACK_VALUE_TYPE_NULL: { dst->type = TSL_TYPE_NULL; @@ -264,7 +298,7 @@ TslProgramResult tsl_program_run(TslProgram *self) { case TSL_OPCODE_LOADV: { TslStackValue stack_value; stack_value.data.str = instruction_type4->value; - stack_value.type = TSL_STACK_VALUE_TYPE_VARIABLE; + stack_value.type = TSL_STACK_VALUE_TYPE_VARIABLE_NAME; cleanup_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadv \"%.*s\"\n", (int)instruction_type4->value.size, instruction_type4->value.data); instruction += sizeof(TslInstructionType4); @@ -330,8 +364,16 @@ TslProgramResult tsl_program_run(TslProgram *self) { } case TSL_OPCODE_ADD: { TslStackValue *args = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * 2); - (void)args; - /* TODO: Implement this */ + TslValue lhs_value; + TslValue rhs_value; + TslStackValue stack_value; + + cleanup_if_error(tsl_value_create_from_stack_value(self, args + 1, &rhs_value)); + cleanup_if_error(tsl_value_create_from_stack_value(self, args, &lhs_value)); + cleanup_if_error(tsl_value_add(&lhs_value, &rhs_value, &stack_value.data.variable)); + + stack_value.type = TSL_STACK_VALUE_TYPE_VARIABLE; + cleanup_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("add\n"); instruction += sizeof(TslInstructionType5); break; diff --git a/src/std_gc/hash_map.c b/src/std_gc/hash_map.c index 4d4c026..3f12f35 100644 --- a/src/std_gc/hash_map.c +++ b/src/std_gc/hash_map.c @@ -1,18 +1,9 @@ #include "../../include/std_gc/hash_map.h" -#include "../../include/value.h" #include #include #include #include -typedef struct TslHashMapNode TslHashMapNode; -struct TslHashMapNode { - uint64_t hash; - TslValue key; - TslValue value; - TslHashMapNode *next; -}; - void tsl_hash_map_init(TslHashMap *self) { self->buckets_data = NULL; self->buckets_size = 0; @@ -193,3 +184,31 @@ TslValue* tsl_hash_map_get_or_create(TslHashMap *self, const TslValue *key) { return value; } + +void tsl_hash_map_create_iterator(TslHashMap *self, TslHashMapIterator *iterator) { + iterator->hash_map = self; + iterator->index = -1; + iterator->node = NULL; +} + +int tsl_hash_map_iterator_next(TslHashMapIterator *self) { + if(self->node) { + TslHashMapNode *new_node = self->node->next; + if(new_node) { + self->node = new_node; + return 1; + } + } + + ++self->index; + while(self->index < self->hash_map->buckets_size) { + TslHashMapNode **bucket = (TslHashMapNode**)self->hash_map->buckets_data + self->index; + if(*bucket) { + self->node = *bucket; + return 1; + } + ++self->index; + } + + return 0; +} diff --git a/src/std_gc/list.c b/src/std_gc/list.c index 5b7c7b6..7c56d64 100644 --- a/src/std_gc/list.c +++ b/src/std_gc/list.c @@ -58,7 +58,7 @@ TslValue* tsl_list_end(TslList *self) { return (TslValue*)((char*)self->data + self->size); } -TslValue* tsl_list_get(TslList *self, double index) { +TslValue* tsl_list_get(const TslList *self, double index) { /* TODO: Verify if overflow check is needed */ intptr_t index_i = index; if(index_i >= 0 && index_i < (intptr_t)self->size / (intptr_t)sizeof(TslValue)) diff --git a/src/value.c b/src/value.c index 1a76880..61d6d4a 100644 --- a/src/value.c +++ b/src/value.c @@ -1,5 +1,11 @@ #include "../include/value.h" +#include "../include/std_gc/hash_map.h" #include +#include +#ifdef DEBUG +#define GC_DEBUG +#endif +#include static uint64_t hash_range(const uint8_t *data, size_t size) { uint64_t result = 0xdec05eba; @@ -11,6 +17,11 @@ static uint64_t hash_range(const uint8_t *data, size_t size) { return result; } +static void tsl_string_to_ref(const TslString *self, TslStringView *result) { + result->data = self->data; + result->size = self->size; +} + uint64_t tsl_value_hash(const TslValue *key) { switch((TslType)key->type) { case TSL_TYPE_NULL: @@ -60,3 +71,122 @@ int tsl_value_equals(const TslValue *lhs, const TslValue *rhs) { void tsl_value_clear(TslValue *self) { memset(self, 0, sizeof(TslValue)); } + +static int tsl_string_merge(const TslStringView *str1, const TslStringView *str2, TslString **result) { + size_t new_size = str1->size + str2->size; + char *new_str_data; + *result = GC_MALLOC_ATOMIC(sizeof(TslString)); + new_str_data = GC_MALLOC_ATOMIC(new_size + 1); + + if(!*result) { + GC_FREE(*result); + GC_FREE(new_str_data); + fprintf(stderr, "Error: out of memory\n"); + return 0; + } + if(!new_str_data) { + GC_FREE(*result); + GC_FREE(new_str_data); + fprintf(stderr, "Error: out of memory\n"); + return 0; + } + + memcpy(new_str_data, str1->data, str1->size); + memcpy(new_str_data + str1->size, str2->data, str2->size); + new_str_data[new_size] = '\0'; + (*result)->data = new_str_data; + (*result)->size = new_size; + + return 1; +} + +int tsl_value_add(const TslValue *lhs_value, const TslValue *rhs_value, TslValue *result) { + tsl_value_clear(result); + switch((TslType)lhs_value->type) { + case TSL_TYPE_NULL: + case TSL_TYPE_BOOL: + case TSL_TYPE_MAP: + case TSL_TYPE_FUNCTION: + case TSL_TYPE_USERDATA: { + fprintf(stderr, "Error: Unable to perform operation '+' on a TODO\n"); + return 0; + } + case TSL_TYPE_NUMBER: { + if(rhs_value->type != TSL_TYPE_NUMBER) { + fprintf(stderr, "Error: Unable to perform operation '+' on a number and a TODO\n"); + return 0; + } + result->data.number = lhs_value->data.number + rhs_value->data.number; + result->type = TSL_TYPE_NUMBER; + return 1; + } + case TSL_TYPE_STRING: { + TslStringView lhs_str; + tsl_string_to_ref(lhs_value->data.string, &lhs_str); + result->type = TSL_TYPE_STRING; + if(rhs_value->type == TSL_TYPE_STRING) { + TslStringView rhs_str; + tsl_string_to_ref(rhs_value->data.string, &rhs_str); + return tsl_string_merge(&lhs_str, &rhs_str, &result->data.string); + } else if(rhs_value->type == TSL_TYPE_STRING_REF) { + return tsl_string_merge(&lhs_str, &rhs_value->data.string_ref, &result->data.string); + } else { + fprintf(stderr, "Error: Unable to perform operation '+' on a string and TODO\n"); + return 0; + } + result->type = TSL_TYPE_STRING; + return 1; + } + case TSL_TYPE_STRING_REF: { + result->type = TSL_TYPE_STRING; + if(rhs_value->type == TSL_TYPE_STRING) { + TslStringView rhs_str; + tsl_string_to_ref(rhs_value->data.string, &rhs_str); + return tsl_string_merge(&lhs_value->data.string_ref, &rhs_str, &result->data.string); + } else if(rhs_value->type == TSL_TYPE_STRING_REF) { + return tsl_string_merge(&lhs_value->data.string_ref, &rhs_value->data.string_ref, &result->data.string); + } else { + fprintf(stderr, "Error: Unable to perform operation '+' on a string and TODO\n"); + return 0; + } + result->type = TSL_TYPE_STRING; + return 1; + } + case TSL_TYPE_LIST: { + /* + Create a new list by combining lhs_value list and rhs_value list. + For example, [1, 2] + [3, 4] becomes [1, 2, 3, 4] + */ + size_t list_size = sizeof(TslValue) * (lhs_value->data.list->size + rhs_value->data.list->size); + + if(rhs_value->type != TSL_TYPE_LIST) { + fprintf(stderr, "Error: Unable to perform operation '+' on a list and a TODO\n"); + return 0; + } + + result->data.list = GC_MALLOC(sizeof(TslList)); + if(!result->data.list) { + fprintf(stderr, "Error: Failed to create list. Reason: out of memory\n"); + return 0; + } + + tsl_list_set_capacity_hint(result->data.list, list_size); + result->data.list->size = list_size; + + memcpy((char*)result->data.list->data, lhs_value->data.list->data, lhs_value->data.list->size); + memcpy((char*)result->data.list->data + lhs_value->data.list->size, rhs_value->data.list->data, rhs_value->data.list->size); + + result->type = TSL_TYPE_LIST; + return 1; + } + } + return 0; +} + +TslValue* tsl_string_ref_get(const TslStringView *self, double index) { + /* TODO: Verify if overflow check is needed */ + intptr_t index_i = index; + if(index_i >= 0 && index_i < (intptr_t)self->size / (intptr_t)sizeof(TslValue)) + return (TslValue*)self->data + index_i; + return NULL; +} -- cgit v1.2.3