initial symbol table implementation
This commit is contained in:
		
							
								
								
									
										149
									
								
								src/encoder/symbols.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/encoder/symbols.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| #include "symbols.h" | ||||
| #include "../error.h" | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| constexpr size_t symbol_table_default_cap = 64; | ||||
| constexpr size_t symbol_table_max_cap = 1 << 16; | ||||
|  | ||||
| error_t *err_symbol_table_invalid_node = &(error_t){ | ||||
|     .message = "Unexpected node id when adding symbol to symbol table"}; | ||||
| error_t *err_symbol_table_max_cap = &(error_t){ | ||||
|     .message = "Failed to increase symbol table length, max capacity reached"}; | ||||
| error_t *err_symbol_table_incompatible_symbols = | ||||
|     &(error_t){.message = "Failed to update symbol with incompatible kind"}; | ||||
|  | ||||
| error_t *symbol_table_alloc(symbol_table_t **output) { | ||||
|     *output = nullptr; | ||||
|  | ||||
|     symbol_table_t *table = calloc(1, sizeof(symbol_table_t)); | ||||
|     if (table == nullptr) | ||||
|         return err_allocation_failed; | ||||
|  | ||||
|     table->symbols = calloc(symbol_table_default_cap, sizeof(symbol_t)); | ||||
|     if (table->symbols == nullptr) { | ||||
|         free(table); | ||||
|         return err_allocation_failed; | ||||
|     } | ||||
|  | ||||
|     table->cap = symbol_table_default_cap; | ||||
|     table->len = 0; | ||||
|  | ||||
|     *output = table; | ||||
|     return nullptr; | ||||
| } | ||||
|  | ||||
| void symbol_table_free(symbol_table_t *table) { | ||||
|     free(table->symbols); | ||||
|     free(table); | ||||
| } | ||||
|  | ||||
| error_t *symbol_table_grow_cap(symbol_table_t *table) { | ||||
|     if (table->cap >= symbol_table_max_cap) | ||||
|         return err_symbol_table_max_cap; | ||||
|  | ||||
|     size_t new_cap = table->cap * 2; | ||||
|     symbol_t *new_symbols = realloc(table->symbols, new_cap * sizeof(symbol_t)); | ||||
|     if (new_symbols == nullptr) | ||||
|         return err_allocation_failed; | ||||
|  | ||||
|     table->symbols = new_symbols; | ||||
|     table->cap = new_cap; | ||||
|  | ||||
|     return nullptr; | ||||
| } | ||||
|  | ||||
| error_t *symbol_table_get_node_info(ast_node_t *node, symbol_kind_t *kind, | ||||
|                                     char **name) { | ||||
|     switch (node->id) { | ||||
|     case NODE_LABEL: | ||||
|         *kind = SYMBOL_LOCAL; | ||||
|         *name = node->children[0]->token_entry->token.value; | ||||
|         break; | ||||
|     case NODE_LABEL_REFERENCE: | ||||
|         *kind = SYMBOL_REFERENCE; | ||||
|         *name = node->token_entry->token.value; | ||||
|         break; | ||||
|     // TODO: when .import and .export directives are created add them here | ||||
|     default: | ||||
|         return err_symbol_table_invalid_node; | ||||
|     } | ||||
|     __builtin_unreachable(); | ||||
| } | ||||
|  | ||||
| /* | ||||
| old  \  new  | REFERENCE | LOCAL    | IMPORT   | EXPORT   | | ||||
| -------------|-----------|----------|----------|----------| | ||||
| REFERENCE    |           | replace  | replace  | replace  | | ||||
| -------------|-----------|----------|----------|----------| | ||||
| LOCAL        |           |          |   ERR    | replace  | | ||||
| -------------|-----------|----------|----------|----------| | ||||
| IMPORT       |           |          |          |   ERR    | | ||||
| -------------|-----------|----------|----------|----------| | ||||
| EXPORT       |           |          |   ERR    |          | | ||||
| -------------|-----------|----------|----------|----------| | ||||
| */ | ||||
|  | ||||
| bool symbol_table_should_update(symbol_kind_t old, symbol_kind_t new) { | ||||
|     if (old == SYMBOL_REFERENCE) | ||||
|         return new != SYMBOL_REFERENCE; | ||||
|     if (old == SYMBOL_LOCAL) | ||||
|         return new == SYMBOL_EXPORT; | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool symbol_table_should_error(symbol_kind_t old, symbol_kind_t new) { | ||||
|     if (new == SYMBOL_IMPORT) | ||||
|         return old == SYMBOL_LOCAL || old == SYMBOL_EXPORT; | ||||
|     if (new == SYMBOL_EXPORT) | ||||
|         return old == SYMBOL_IMPORT; | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @pre The symbol _must not_ already be in the table. | ||||
|  */ | ||||
| error_t *symbol_table_add(symbol_table_t *table, char *name, symbol_kind_t kind, | ||||
|                           ast_node_t *node) { | ||||
|     if (table->len >= table->cap) { | ||||
|         error_t *err = symbol_table_grow_cap(table); | ||||
|         if (err) | ||||
|             return err; | ||||
|     } | ||||
|  | ||||
|     table->symbols[table->len] = (symbol_t){ | ||||
|         .name = name, | ||||
|         .kind = kind, | ||||
|         .node = node, | ||||
|     }; | ||||
|  | ||||
|     return nullptr; | ||||
| } | ||||
|  | ||||
| error_t *symbol_table_update(symbol_table_t *table, ast_node_t *node) { | ||||
|     char *name; | ||||
|     symbol_kind_t kind; | ||||
|     error_t *err = symbol_table_get_node_info(node, &kind, &name); | ||||
|     if (err) | ||||
|         return err; | ||||
|  | ||||
|     symbol_t *symbol = symbol_table_lookup(table, name); | ||||
|     if (!symbol) | ||||
|         return symbol_table_add(table, name, kind, node); | ||||
|     if (symbol_table_should_error(symbol->kind, kind)) | ||||
|         return err_symbol_table_incompatible_symbols; | ||||
|     if (symbol_table_should_update(symbol->kind, kind)) { | ||||
|         symbol->name = name; | ||||
|         symbol->kind = kind; | ||||
|         symbol->node = node; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
|  | ||||
| symbol_t *symbol_table_lookup(symbol_table_t *table, const char *name) { | ||||
|     for (size_t i = 0; i < table->len; ++i) { | ||||
|         if (strcmp(table->symbols[i].name, name) == 0) | ||||
|             return &table->symbols[i]; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/encoder/symbols.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/encoder/symbols.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| #ifndef INCLUDE_ENCODER_SYMBOLS_H_ | ||||
| #define INCLUDE_ENCODER_SYMBOLS_H_ | ||||
|  | ||||
| #include "../ast.h" | ||||
|  | ||||
| typedef enum symbol_kind { | ||||
|     SYMBOL_REFERENCE, | ||||
|     SYMBOL_LOCAL, | ||||
|     SYMBOL_EXPORT, | ||||
|     SYMBOL_IMPORT, | ||||
| } symbol_kind_t; | ||||
|  | ||||
| /** | ||||
|  * Represent a symbol in the program | ||||
|  * | ||||
|  * Symbols with the same name can only be in the table once. IMPORT or EXPORT | ||||
|  * symbols take precedence over REFERENCE symbols. If any reference symbols | ||||
|  * remain after the first encoding pass this indicates an error. Trying to add | ||||
|  * an IMPORT or EXPORT symbol if the same name already exists as the other kind | ||||
|  * is an error. | ||||
|  * | ||||
|  * This symbol table never taken ownership of the name string, it's lifted | ||||
|  * straight from the node->token.value. | ||||
|  */ | ||||
| typedef struct symbol { | ||||
|     char *name; | ||||
|     symbol_kind_t kind; | ||||
|     ast_node_t *node; | ||||
| } symbol_t; | ||||
|  | ||||
| typedef struct symbol_table { | ||||
|     size_t cap; | ||||
|     size_t len; | ||||
|     symbol_t *symbols; | ||||
| } symbol_table_t; | ||||
|  | ||||
| error_t *symbol_table_alloc(symbol_table_t **table); | ||||
| void symbol_table_free(symbol_table_t *table); | ||||
| error_t *symbol_table_update(symbol_table_t *table, ast_node_t *node); | ||||
| symbol_t *symbol_table_lookup(symbol_table_t *table, const char *name); | ||||
|  | ||||
| #endif // INCLUDE_ENCODER_SYMBOLS_H_ | ||||
		Reference in New Issue
	
	Block a user