Compare commits
	
		
			4 Commits
		
	
	
		
			b4301ed650
			...
			0f6efa8050
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0f6efa8050 | |||
| 36af377ba0 | |||
| bd37ddaeea | |||
| 42da7b1d05 | 
| @@ -27,9 +27,9 @@ | |||||||
|  |  | ||||||
| <memory_expression> ::= <label_reference> | <register_expression> | <memory_expression> ::= <label_reference> | <register_expression> | ||||||
|  |  | ||||||
| <register_expression> ::= <register> ( <plus> <register> <asterisk> <number> )? ( <plus_or_minus> <number> )? | <register_expression> ::= <register> <register_index>? <register_offset>? | ||||||
|  |  | ||||||
| <register_displacement> ::= <plus> <register> <asterisk> <number> | <register_index> ::= <plus> <register> <asterisk> <number> | ||||||
|  |  | ||||||
| <register_offset> ::= <plus_or_minus> <number> | <register_offset> ::= <plus_or_minus> <number> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								src/ast.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/ast.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | #include "ast.h" | ||||||
|  | #include "error.h" | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | error_t *err_node_children_cap = &(error_t){ | ||||||
|  |     .message = "Failed to increase ast node children, max capacity reached"}; | ||||||
|  |  | ||||||
|  | error_t *ast_node_alloc(ast_node_t **output) { | ||||||
|  |     *output = nullptr; | ||||||
|  |  | ||||||
|  |     ast_node_t *node = calloc(1, sizeof(ast_node_t)); | ||||||
|  |     if (node == nullptr) | ||||||
|  |         return err_allocation_failed; | ||||||
|  |  | ||||||
|  |     *output = node; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ast_node_free_value(ast_node_t *node) { | ||||||
|  |     // TODO: decide how value ownership will work and clean it up here | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ast_node_free(ast_node_t *node) { | ||||||
|  |     if (node == nullptr) | ||||||
|  |         return; | ||||||
|  |     if (node->children) { | ||||||
|  |         for (size_t i = 0; i < node->len; ++i) | ||||||
|  |             ast_node_free(node->children[i]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ast_node_free_value(node); | ||||||
|  |  | ||||||
|  |     memset(node, 0, sizeof(ast_node_t)); | ||||||
|  |     free(node); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @pre node->children must be nullptr | ||||||
|  |  */ | ||||||
|  | error_t *ast_node_alloc_children(ast_node_t *node) { | ||||||
|  |     node->children = calloc(node_default_children_cap, sizeof(ast_node_t *)); | ||||||
|  |     if (node->children == nullptr) | ||||||
|  |         return err_allocation_failed; | ||||||
|  |  | ||||||
|  |     node->cap = node_default_children_cap; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | error_t *ast_node_grow_cap(ast_node_t *node) { | ||||||
|  |     if (node->cap >= node_max_children_cap) { | ||||||
|  |         return err_node_children_cap; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t new_cap = node->cap * 2; | ||||||
|  |     if (new_cap > node_max_children_cap) { | ||||||
|  |         new_cap = node_max_children_cap; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ast_node_t **new_children = | ||||||
|  |         realloc(node->children, new_cap * sizeof(ast_node_t *)); | ||||||
|  |     if (new_children == nullptr) { | ||||||
|  |         return err_allocation_failed; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     node->children = new_children; | ||||||
|  |     node->cap = new_cap; | ||||||
|  |  | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | error_t *ast_node_add_child(ast_node_t *node, ast_node_t *child) { | ||||||
|  |     error_t *err = nullptr; | ||||||
|  |     if (node->children == nullptr) | ||||||
|  |         err = ast_node_alloc_children(node); | ||||||
|  |     else if (node->len >= node->cap) | ||||||
|  |         err = ast_node_grow_cap(node); | ||||||
|  |     if (err) | ||||||
|  |         return err; | ||||||
|  |  | ||||||
|  |     node->children[node->len] = child; | ||||||
|  |     node->len += 1; | ||||||
|  |  | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								src/ast.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/ast.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | #ifndef INCLUDE_SRC_AST_H_ | ||||||
|  | #define INCLUDE_SRC_AST_H_ | ||||||
|  |  | ||||||
|  | #include "error.h" | ||||||
|  | #include "lexer.h" | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdint.h> | ||||||
|  |  | ||||||
|  | typedef enum node_id { | ||||||
|  |     NODE_PROGRAM, | ||||||
|  |     NODE_DIRECTIVE, | ||||||
|  |     NODE_LABEL, | ||||||
|  |     NODE_INSTRUCTION | ||||||
|  | } node_id_t; | ||||||
|  |  | ||||||
|  | typedef struct ast_node ast_node_t; | ||||||
|  |  | ||||||
|  | constexpr size_t node_default_children_cap = 8; | ||||||
|  | /* 65K ought to be enough for anybody */ | ||||||
|  | constexpr size_t node_max_children_cap = 1 << 16; | ||||||
|  |  | ||||||
|  | struct ast_node { | ||||||
|  |     node_id_t id; | ||||||
|  |     lexer_token_t *token; | ||||||
|  |     size_t len; | ||||||
|  |     size_t cap; | ||||||
|  |     ast_node_t **children; | ||||||
|  |  | ||||||
|  |     union { | ||||||
|  |         struct { | ||||||
|  |             uint64_t value; | ||||||
|  |             int size; | ||||||
|  |         } integer; | ||||||
|  |         char *name; | ||||||
|  |     } value; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Allocates a new AST node | ||||||
|  |  * | ||||||
|  |  * Creates and initializes a new AST node with default (zero) values. | ||||||
|  |  * | ||||||
|  |  * @param[out] output Pointer to store the allocated node | ||||||
|  |  * @return error_t* nullptr on success, allocation error on failure | ||||||
|  |  */ | ||||||
|  | error_t *ast_node_alloc(ast_node_t **node); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Frees an AST node and all its children recursively | ||||||
|  |  * | ||||||
|  |  * Recursively frees all children of the node, then frees the node itself. | ||||||
|  |  * If node is nullptr, the function returns without doing anything. | ||||||
|  |  * | ||||||
|  |  * @param node The node to free | ||||||
|  |  */ | ||||||
|  | void ast_node_free(ast_node_t *node); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Adds a child node to a parent node | ||||||
|  |  * | ||||||
|  |  * Adds the specified child node to the parent's children array. | ||||||
|  |  * If this is the first child, the function allocates the children array. | ||||||
|  |  * If the children array is full, the function increases its capacity. | ||||||
|  |  * | ||||||
|  |  * @param node The parent node to add the child to | ||||||
|  |  * @param child The child node to add | ||||||
|  |  * @return error_t* nullptr on success, allocation error on failure, | ||||||
|  |  *                  or err_node_children_cap if maximum capacity is reached | ||||||
|  |  */ | ||||||
|  | error_t *ast_node_add_child(ast_node_t *node, ast_node_t *child); | ||||||
|  |  | ||||||
|  | #endif // INCLUDE_SRC_AST_H_ | ||||||
| @@ -10,6 +10,9 @@ error_t *const err_errorf_length = &(error_t){ | |||||||
|     .message = |     .message = | ||||||
|         "Formatting of another error failed to determine the error length"}; |         "Formatting of another error failed to determine the error length"}; | ||||||
|  |  | ||||||
|  | error_t *err_allocation_failed = | ||||||
|  |     &(error_t){.message = "Memory allocation failed"}; | ||||||
|  |  | ||||||
| error_t *errorf(const char *fmt, ...) { | error_t *errorf(const char *fmt, ...) { | ||||||
|     error_t *err = calloc(1, sizeof(error_t)); |     error_t *err = calloc(1, sizeof(error_t)); | ||||||
|     if (err == nullptr) |     if (err == nullptr) | ||||||
|   | |||||||
| @@ -18,4 +18,7 @@ static inline void error_free(error_t *err) { | |||||||
|     free(err); |     free(err); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Some global errors */ | ||||||
|  | extern error_t *err_allocation_failed; | ||||||
|  |  | ||||||
| #endif // INCLUDE_SRC_ERROR_H_ | #endif // INCLUDE_SRC_ERROR_H_ | ||||||
|   | |||||||
| @@ -20,9 +20,6 @@ error_t *err_eof = | |||||||
|  |  | ||||||
| error_t *err_unknown_read = &(error_t){.message = "Unknown read error"}; | error_t *err_unknown_read = &(error_t){.message = "Unknown read error"}; | ||||||
|  |  | ||||||
| error_t *err_allocation_failed = |  | ||||||
|     &(error_t){.message = "Memory allocation failed"}; |  | ||||||
|  |  | ||||||
| typedef bool (*char_predicate_t)(char); | typedef bool (*char_predicate_t)(char); | ||||||
|  |  | ||||||
| const char *lexer_token_id_to_cstr(lexer_token_id_t id) { | const char *lexer_token_id_to_cstr(lexer_token_id_t id) { | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/main.c
									
									
									
									
									
								
							| @@ -1,5 +1,6 @@ | |||||||
| #include "error.h" | #include "error.h" | ||||||
| #include "lexer.h" | #include "lexer.h" | ||||||
|  | #include "tokenlist.h" | ||||||
|  |  | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @@ -39,24 +40,33 @@ int main(int argc, char *argv[]) { | |||||||
|         print_fn = print_value; |         print_fn = print_value; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lexer_t lex = {0}; |     lexer_t *lex = &(lexer_t){}; | ||||||
|     lexer_token_t token; |     error_t *err = lexer_open(lex, filename); | ||||||
|     error_t *err = lexer_open(&lex, filename); |     if (err) | ||||||
|     if (err) { |         goto cleanup_error; | ||||||
|  |  | ||||||
|  |     tokenlist_t *list; | ||||||
|  |     err = tokenlist_alloc(&list); | ||||||
|  |     if (err) | ||||||
|  |         goto cleanup_lexer; | ||||||
|  |  | ||||||
|  |     err = tokenlist_fill(list, lex); | ||||||
|  |     if (err) | ||||||
|  |         goto cleanup_tokens; | ||||||
|  |  | ||||||
|  |     for (auto entry = list->head; entry; entry = entry->next) { | ||||||
|  |         print_fn(&entry->token); | ||||||
|  |     } | ||||||
|  |     tokenlist_free(list); | ||||||
|  |     error_free(err); | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  | cleanup_tokens: | ||||||
|  |     tokenlist_free(list); | ||||||
|  | cleanup_lexer: | ||||||
|  |     lexer_close(lex); | ||||||
|  | cleanup_error: | ||||||
|     puts(err->message); |     puts(err->message); | ||||||
|     error_free(err); |     error_free(err); | ||||||
|     return 1; |     return 1; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool keep_going = true; |  | ||||||
|     while (keep_going && (err = lexer_next(&lex, &token)) == nullptr) { |  | ||||||
|         keep_going = print_fn(&token); |  | ||||||
|         free(token.value); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (err && err != err_eof) { |  | ||||||
|         puts(err->message); |  | ||||||
|     } |  | ||||||
|     error_free(err); |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								src/tokenlist.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/tokenlist.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | #include "tokenlist.h" | ||||||
|  | #include "error.h" | ||||||
|  | #include "lexer.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | error_t *tokenlist_alloc(tokenlist_t **output) { | ||||||
|  |     *output = nullptr; | ||||||
|  |  | ||||||
|  |     tokenlist_t *list = calloc(1, sizeof(tokenlist_t)); | ||||||
|  |     if (list == nullptr) | ||||||
|  |         return err_allocation_failed; | ||||||
|  |  | ||||||
|  |     list->head = nullptr; | ||||||
|  |     list->tail = nullptr; | ||||||
|  |  | ||||||
|  |     *output = list; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | error_t *tokenlist_entry_alloc(tokenlist_entry_t **output) { | ||||||
|  |     *output = nullptr; | ||||||
|  |  | ||||||
|  |     tokenlist_entry_t *entry = calloc(1, sizeof(tokenlist_entry_t)); | ||||||
|  |     if (entry == nullptr) | ||||||
|  |         return err_allocation_failed; | ||||||
|  |  | ||||||
|  |     entry->next = nullptr; | ||||||
|  |     entry->prev = nullptr; | ||||||
|  |  | ||||||
|  |     *output = entry; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tokenlist_append(tokenlist_t *list, tokenlist_entry_t *entry) { | ||||||
|  |     if (list->head == nullptr) { | ||||||
|  |         list->head = entry; | ||||||
|  |         list->tail = entry; | ||||||
|  |         entry->next = nullptr; | ||||||
|  |         entry->prev = nullptr; | ||||||
|  |     } else { | ||||||
|  |         entry->prev = list->tail; | ||||||
|  |         entry->next = nullptr; | ||||||
|  |         list->tail->next = entry; | ||||||
|  |         list->tail = entry; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tokenlist_entry_free(tokenlist_entry_t *entry) { | ||||||
|  |     lexer_token_cleanup(&entry->token); | ||||||
|  |     free(entry); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tokenlist_free(tokenlist_t *list) { | ||||||
|  |     if (list == nullptr) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     tokenlist_entry_t *current = list->head; | ||||||
|  |     while (current) { | ||||||
|  |         tokenlist_entry_t *next = current->next; | ||||||
|  |         tokenlist_entry_free(current); | ||||||
|  |         current = next; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     free(list); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | error_t *tokenlist_fill(tokenlist_t *list, lexer_t *lex) { | ||||||
|  |     error_t *err = nullptr; | ||||||
|  |     lexer_token_t token = {}; | ||||||
|  |     while ((err = lexer_next(lex, &token)) == nullptr) { | ||||||
|  |         tokenlist_entry_t *entry; | ||||||
|  |         err = tokenlist_entry_alloc(&entry); | ||||||
|  |         if (err) { | ||||||
|  |             lexer_token_cleanup(&token); | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |         entry->token = token; | ||||||
|  |         tokenlist_append(list, entry); | ||||||
|  |     } | ||||||
|  |     if (err != err_eof) | ||||||
|  |         return err; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								src/tokenlist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/tokenlist.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | #ifndef INCLUDE_SRC_TOKENLIST_H_ | ||||||
|  | #define INCLUDE_SRC_TOKENLIST_H_ | ||||||
|  | #include "lexer.h" | ||||||
|  |  | ||||||
|  | typedef struct tokenlist_entry tokenlist_entry_t; | ||||||
|  |  | ||||||
|  | struct tokenlist_entry { | ||||||
|  |     lexer_token_t token; | ||||||
|  |     tokenlist_entry_t *next; | ||||||
|  |     tokenlist_entry_t *prev; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef struct tokenlist { | ||||||
|  |     tokenlist_entry_t *head; | ||||||
|  |     tokenlist_entry_t *tail; | ||||||
|  | } tokenlist_t; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Allocate a new doubly linked list of lexer tokens | ||||||
|  |  */ | ||||||
|  | error_t *tokenlist_alloc(tokenlist_t **list); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Consume all tokens from the lexer and add them to the list | ||||||
|  |  */ | ||||||
|  | error_t *tokenlist_fill(tokenlist_t *list, lexer_t *lex); | ||||||
|  |  | ||||||
|  | void tokenlist_free(tokenlist_t *list); | ||||||
|  |  | ||||||
|  | #endif // INCLUDE_SRC_TOKENLIST_H_ | ||||||
		Reference in New Issue
	
	Block a user