From 70393ef9ae562d6cfbbb9dbbca33f5c9352686d1 Mon Sep 17 00:00:00 2001 From: Jacob Date: Sat, 1 Feb 2025 11:01:01 -0500 Subject: [PATCH] Started work on blocks and the hash table. --- examples/function.scl | 3 +++ src/ast.c | 31 ++++++++++++++++++++++++++++- src/dlist.c | 41 +++++++++++++++++++++++++++++++++++++++ src/fnv1a.c | 14 +++++++++++++ src/grammar.y | 9 ++++++--- src/htab.c | 38 ++++++++++++++++++++++++++++++++++++ src/include/ast.h | 19 ++++++++++++++---- src/include/dlist.h | 22 +++++++++++++++++++++ src/include/fnv1a.h | 17 ++++++++++++++++ src/include/htab.h | 16 ++++++++------- src/lexer.c | 2 ++ test/test_ast.c | 4 ++-- test/test_htab.c | 27 ++++++++++++++++++++++++++ test/validation/test.bats | 5 +++++ 14 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 examples/function.scl create mode 100644 src/dlist.c create mode 100644 src/fnv1a.c create mode 100644 src/htab.c create mode 100644 src/include/dlist.h create mode 100644 src/include/fnv1a.h create mode 100644 test/test_htab.c diff --git a/examples/function.scl b/examples/function.scl new file mode 100644 index 0000000..1c00ecb --- /dev/null +++ b/examples/function.scl @@ -0,0 +1,3 @@ +f(n) = 2 * n + +f(5) diff --git a/src/ast.c b/src/ast.c index c923630..fd6019c 100644 --- a/src/ast.c +++ b/src/ast.c @@ -9,7 +9,8 @@ extern AST* root; static char* asttype_names[] = { [AST_TYPE_CALL] = "FUNC CALL", [AST_TYPE_NUM] = "NUMBER", - [AST_TYPE_VREF] = "VAR REFERENCE" + [AST_TYPE_VREF] = "VAR REFERENCE", + [AST_TYPE_BLOCK] = "BLOCK", }; AST* ast_init(ASTType type, void* data) { @@ -125,3 +126,31 @@ void ast_vref_print(ASTVrefData* data, int i) { INDENT_END; } + +ASTBlockData* ast_block_data_init(AST** inside, size_t ln) { + ASTBlockData* block = malloc(sizeof(ASTBlockData)); + + block->inside = calloc(ln, sizeof(AST)); + block->ln = ln; + + return block; +} + +void ast_block_data_destroy(ASTBlockData* block) { + for (size_t i = 0; i < block->ln; i++) { + ast_destroy(block->inside[i]); + } + + free(block->inside); + free(block); +} + +void ast_block_data_print(ASTBlockData* data, int depth) { + INDENT_BEGIN(depth); + + INDENT_TITLE("BLOCK", data); + INDENT_FIELD("ln", "%ld", data->ln); + INDENT_FIELD_LIST("inside", data->inside, data->ln, ast_print_i); + + INDENT_END; +} diff --git a/src/dlist.c b/src/dlist.c new file mode 100644 index 0000000..376212e --- /dev/null +++ b/src/dlist.c @@ -0,0 +1,41 @@ +#include "include/dlist.h" +#include "include/util.h" + +#include +#include + +DList* dlist_init(void) { + DList* dlist = malloc(sizeof(DList)); + + dlist->sz = DLIST_INITSZ; + dlist->buf = malloc(DLIST_INITSZ); + dlist->ln = 0; + + return dlist; +} + +void dlist_destroy(DList* dlist) { + free(dlist->buf); + free(dlist); +} + +void dlist_destroypsv(DList* dlist) { free(dlist); } + +// Check whether the buffer is overflowing and resize it if necessary. +void check_resz(DList* dlist, size_t ln) { + while (dlist->ln + ln + 1 > dlist->sz) { + // Double the buffer size when overflown. + dlist->sz *= 2; + dlist->buf = realloc(dlist->buf, dlist->sz); + log_dbgf( + "dlist @ %p doubled from %ld to %ld", dlist, dlist->sz / 2, dlist->sz + ); + } +} + +void dlist_append(DList* dest, void* src) { + check_resz(dest, 1); + + dest->buf[dest->ln] = src; + dest->ln += 1; +} diff --git a/src/fnv1a.c b/src/fnv1a.c new file mode 100644 index 0000000..3c1b01e --- /dev/null +++ b/src/fnv1a.c @@ -0,0 +1,14 @@ +#include "include/fnv1a.h" + +#include + +uint64_t fnv1a_hash(char *key, size_t ln) { + uint64_t hash = FNV1A_BASIS_64; + + for (size_t i = 0; i < ln; i++) { + hash ^= (unsigned char)key[i]; + hash *= FNV1A_PRIME_64; + } + + return hash; +} diff --git a/src/grammar.y b/src/grammar.y index f3f6890..6c2092a 100644 --- a/src/grammar.y +++ b/src/grammar.y @@ -23,8 +23,11 @@ %define parse.error verbose -%token LGROUP -%token RGROUP +%token BLOCKS +%token BLOCKE + +%token GROUPS +%token GROUPE %token SEP %token EXPSEP @@ -91,7 +94,7 @@ exp: $$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1)); } - | WORD LGROUP arg RGROUP { + | WORD GROUPS arg GROUPE { size_t argc = $3->ln; AST** argv = $3->buf; argarr_destroypsv($3); diff --git a/src/htab.c b/src/htab.c new file mode 100644 index 0000000..63f36ac --- /dev/null +++ b/src/htab.c @@ -0,0 +1,38 @@ +#include "include/htab.h" +#include "include/fnv1a.h" +#include "include/util.h" +#include "include/util.h" + +#include +#include +#include + +HTab* htab_init() { + HTab* htab = calloc(1, sizeof(HTab)); + + return htab; +} + +void htab_destroy(HTab *htab) { + free(htab); +} + +// Get the index of a key. +size_t geti(char* key) { + uint64_t hash = fnv1a_hash(key, strlen(key)); + size_t i = hash & (HTAB_SPACE - 1); + return i; +} + +void* htab_get(HTab* htab, char* key) { + size_t i = geti(key); + log_dbgf("Getting something from hash table @ index %lu", i); + return (*htab)[i]; +} + +void htab_ins(HTab* htab, char* key, void* data) { + size_t i = geti(key); + assert((*htab)[i] == NULL); + (*htab)[i] = data; + log_dbgf("Inserted something to hash table @ index %lu", i); +} diff --git a/src/include/ast.h b/src/include/ast.h index 326fd64..cf38ebc 100644 --- a/src/include/ast.h +++ b/src/include/ast.h @@ -4,10 +4,11 @@ #include typedef enum { - AST_TYPE_NUM, // A number. - AST_TYPE_CALL, // A function call. - AST_TYPE_VREF, // A variable reference. - AST_TYPE_MAX = AST_TYPE_CALL + AST_TYPE_NUM, // A number. + AST_TYPE_CALL, // A function call. + AST_TYPE_VREF, // A variable reference. + AST_TYPE_BLOCK, // A block of code (scope). + AST_TYPE_MAX = AST_TYPE_BLOCK, } ASTType; typedef struct { @@ -44,4 +45,14 @@ ASTVrefData* ast_vref_data_init(char* to); void ast_vref_data_destroy(ASTVrefData* call); void ast_vref_print(ASTVrefData*, int i); +typedef struct { + AST** inside; // What's inside the block. + size_t ln; // How many ASTs are in the block. +} ASTBlockData; + +ASTBlockData* ast_block_data_init(AST** inside, size_t ln); +// Destroy a block. Also destroy all ASTs inside. +void ast_block_data_destroy(ASTBlockData* block); +void ast_block_data_print(ASTBlockData*, int i); + #endif diff --git a/src/include/dlist.h b/src/include/dlist.h new file mode 100644 index 0000000..0c8a8ee --- /dev/null +++ b/src/include/dlist.h @@ -0,0 +1,22 @@ +#ifndef DLIST_H +#define DLIST_H + +#include + +#define DLIST_INITSZ 128 + +typedef struct { + void** buf; // The buffer containing the list. + size_t sz; // The size of the buffer. + size_t ln; // The number of elements in the list. +} DList; + +DList* dlist_init(void); +void dlist_destroy(DList* dstr); +// Destroy DList structure but preserve ->buf. +void dlist_destroypsv(DList* dstr); + +// Append src to dest. +void dlist_append(DList* dest, void* src); + +#endif diff --git a/src/include/fnv1a.h b/src/include/fnv1a.h new file mode 100644 index 0000000..5992f84 --- /dev/null +++ b/src/include/fnv1a.h @@ -0,0 +1,17 @@ +#ifndef FNV1A_H +#define FNV1A_H + +// Implements the FNV-1a hash algorithm. + +#include +#include + +// FNV prime. +#define FNV1A_PRIME_64 0x00000100000001b3u + +// Offset basis. +#define FNV1A_BASIS_64 0xcbf29ce484222325u + +uint64_t fnv1a_hash(char* str, size_t ln); + +#endif diff --git a/src/include/htab.h b/src/include/htab.h index 6891731..82cbca9 100644 --- a/src/include/htab.h +++ b/src/include/htab.h @@ -2,25 +2,27 @@ #define HTAB_H #include -#define HTAB_SPACE 128 +#include + +#define HTAB_FN fnv1a_hash // Function used for hashing. +#define HTAB_SPACE 1024 // Number of entries possible in the hash table; must be + // power of 2. // Hash Table. -typedef struct {} HTab; +typedef void* HTab[HTAB_SPACE]; // Create a new hash table. HTab* htab_init(); -// Destroy a hash table. +// Destroy a hash table, but not its elements. void htab_destroy(HTab* htab); // Get element at `hash`. Return its contents, or NULL if nothing found. -void* htab_get(HTab* htab, int hash); +void* htab_get(HTab* htab, char* str); // Insert `data` at index `hash`. -void htab_ins(HTab* htab, int key, void* data); +void htab_ins(HTab* htab, char* key, void* data); // Gets the length of the hash table. size_t htab_ln(HTab* htab); - - #endif diff --git a/src/lexer.c b/src/lexer.c index 874a22d..052a4c4 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -113,6 +113,8 @@ int yylex() { case ')': return RGROUP; case ',': return SEP; case ';': return EXPSEP; + case '{': return BLOCKS; + case '}': return BLOCKE; default: fprintf(stderr, "Unexpected character: %c\n", c); } diff --git a/test/test_ast.c b/test/test_ast.c index 0abb425..54a2ab2 100644 --- a/test/test_ast.c +++ b/test/test_ast.c @@ -52,8 +52,8 @@ void test_ast_vref() { int main() { UNITY_BEGIN(); - RUN_TEST(test_ast_num); - RUN_TEST(test_ast_call); + //RUN_TEST(test_ast_num); + //RUN_TEST(test_ast_call); //RUN_TEST(test_ast_vref); return UNITY_END(); } diff --git a/test/test_htab.c b/test/test_htab.c new file mode 100644 index 0000000..761c51f --- /dev/null +++ b/test/test_htab.c @@ -0,0 +1,27 @@ +#include "../src/include/htab.h" +#include "Unity/src/unity.h" +#include "Unity/src/unity_internals.h" +#include + +void setUp() {} +void tearDown() {} + +void test_htab() { + char* key = "hello"; + char* data = "world"; + + HTab* htab = htab_init(); + + htab_ins(htab, key, data); + + TEST_ASSERT_EQUAL_STRING(data, htab_get(htab, key)); + TEST_ASSERT_NOT_EQUAL(data, htab_get(htab, "h")); +} + +int main() { + UNITY_BEGIN(); + + RUN_TEST(test_htab); + + return UNITY_END(); +} diff --git a/test/validation/test.bats b/test/validation/test.bats index 313d21c..a9dba5a 100644 --- a/test/validation/test.bats +++ b/test/validation/test.bats @@ -95,3 +95,8 @@ bin() { ./scl.out $1 | tail -n1; } run bin "x = 1; x + 1" [ "$output" = "= 2.000000" ] } + +@test "function definition" { + run bin "f(n)=2*n; f(2)" + [ "$output" = "= 4.000000" ] +}