Compare commits
4 Commits
b4301ed650
...
0f6efa8050
Author | SHA1 | Date | |
---|---|---|---|
0f6efa8050 | |||
36af377ba0 | |||
bd37ddaeea | |||
42da7b1d05 |
36
doc/parser_grammar.txt
Normal file
36
doc/parser_grammar.txt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* string literals are lexer identifier tokens with that particular value */
|
||||||
|
<program> ::= <statement>*
|
||||||
|
<statement> ::= ( <label> | <directive> | <instruction> ) <newline>
|
||||||
|
|
||||||
|
<label> ::= <identifier> <colon>
|
||||||
|
|
||||||
|
<directive> ::= <dot> <section>
|
||||||
|
|
||||||
|
<section> ::= "section" <identifier>
|
||||||
|
|
||||||
|
<instruction> ::= <identifier> <operands>
|
||||||
|
|
||||||
|
<operands> ::= <operand> ( <comma> <operands> )*
|
||||||
|
|
||||||
|
<operand> ::= <register> | <immediate> | <memory>
|
||||||
|
|
||||||
|
<register> ::= <register_base> | <register_extra>
|
||||||
|
<register_base> ::= "rax" | "rbx" | "rcx" | "rdx" | "rsi" | "rdi" | "rbp" | "rsp"
|
||||||
|
<register_extra> ::= "r8" | "r9" | "r10" | "r11" | "r12" | "r13" | "r14" | "r15"
|
||||||
|
|
||||||
|
<immediate> ::= <number> | <label_reference>
|
||||||
|
<number> ::= <octal> | <binary> | <decimal> | <hexadecimal>
|
||||||
|
|
||||||
|
<label_reference> ::= <identifier>
|
||||||
|
|
||||||
|
<memory> ::= <lbracket> <memory_expression> <rbracket>
|
||||||
|
|
||||||
|
<memory_expression> ::= <label_reference> | <register_expression>
|
||||||
|
|
||||||
|
<register_expression> ::= <register> <register_index>? <register_offset>?
|
||||||
|
|
||||||
|
<register_index> ::= <plus> <register> <asterisk> <number>
|
||||||
|
|
||||||
|
<register_offset> ::= <plus_or_minus> <number>
|
||||||
|
|
||||||
|
<plus_or_minus> ::= <plus> | <minus>
|
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_
|
Loading…
x
Reference in New Issue
Block a user