From 1f71fce283d29188323e4e7e32445c45498f6bf8 Mon Sep 17 00:00:00 2001 From: bog Date: Sun, 31 Mar 2024 23:10:56 +0200 Subject: [PATCH] :sparkles: int literals. --- Makefile | 5 +- cli/main.c | 18 +++++- doc/grammar.bnf | 4 ++ lib/CMakeLists.txt | 20 ++++++- lib/include/commons.h | 20 +++++++ lib/include/compiler.h | 19 ++++++ lib/include/errors.h | 30 ++++++++++ lib/include/exec.h | 20 +++++++ lib/include/lexer.h | 30 ++++++++++ lib/include/module.h | 21 +++++++ lib/include/node.h | 27 +++++++++ lib/include/parser.h | 25 ++++++++ lib/include/prog.h | 29 ++++++++++ lib/include/state.h | 47 +++++++++++++++ lib/include/str.h | 24 ++++++++ lib/include/token.h | 27 +++++++++ lib/include/value.h | 32 +++++++++++ lib/include/vec.h | 22 +++++++ lib/src/compiler.c | 48 ++++++++++++++++ lib/src/errors.c | 64 +++++++++++++++++++++ lib/src/exec.c | 48 ++++++++++++++++ lib/src/lexer.c | 127 +++++++++++++++++++++++++++++++++++++++++ lib/src/module.c | 89 +++++++++++++++++++++++++++++ lib/src/node.c | 57 ++++++++++++++++++ lib/src/parser.c | 83 +++++++++++++++++++++++++++ lib/src/prog.c | 42 ++++++++++++++ lib/src/state.c | 117 +++++++++++++++++++++++++++++++++++++ lib/src/str.c | 72 +++++++++++++++++++++++ lib/src/token.c | 38 ++++++++++++ lib/src/value.c | 36 ++++++++++++ lib/src/vec.c | 59 +++++++++++++++++++ tests/lexer.h | 54 ++++++++++++++++++ tests/main.c | 14 ++--- tests/parser.h | 38 ++++++++++++ 34 files changed, 1393 insertions(+), 13 deletions(-) create mode 100644 doc/grammar.bnf create mode 100644 lib/include/compiler.h create mode 100644 lib/include/errors.h create mode 100644 lib/include/exec.h create mode 100644 lib/include/lexer.h create mode 100644 lib/include/module.h create mode 100644 lib/include/node.h create mode 100644 lib/include/parser.h create mode 100644 lib/include/prog.h create mode 100644 lib/include/state.h create mode 100644 lib/include/str.h create mode 100644 lib/include/token.h create mode 100644 lib/include/value.h create mode 100644 lib/include/vec.h create mode 100644 lib/src/compiler.c create mode 100644 lib/src/errors.c create mode 100644 lib/src/exec.c create mode 100644 lib/src/lexer.c create mode 100644 lib/src/module.c create mode 100644 lib/src/node.c create mode 100644 lib/src/parser.c create mode 100644 lib/src/prog.c create mode 100644 lib/src/state.c create mode 100644 lib/src/str.c create mode 100644 lib/src/token.c create mode 100644 lib/src/value.c create mode 100644 lib/src/vec.c create mode 100644 tests/lexer.h create mode 100644 tests/parser.h diff --git a/Makefile b/Makefile index ffefaa2..9768e13 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ build: cmake --build build tests: build - build/tests/skopy-tests \ + @build/tests/skopy-tests \ && echo -e "\e[32m--- all tests passed ---\e[0m" \ || echo -e "\e[31m--- some tests failed ---\e[0m" @@ -14,4 +14,5 @@ install: tests check: @cppcheck --enable=all -q lib tests cli \ - --suppress=missingIncludeSystem + --suppress=missingIncludeSystem \ + --suppress=missingInclude diff --git a/cli/main.c b/cli/main.c index 059c5a0..e5f9958 100644 --- a/cli/main.c +++ b/cli/main.c @@ -1,7 +1,21 @@ #include +#include +#include -int main() +int main(int argc, char** argv) { - printf("Hello World!\n"); + if (argc == 2) + { + errors_init(); + struct module module; + module_init(&module); + + module_load_source(&module, argv[1]); + module_compile(&module); + + module_free(&module); + errors_free(); + } + return 0; } diff --git a/doc/grammar.bnf b/doc/grammar.bnf new file mode 100644 index 0000000..1c938c8 --- /dev/null +++ b/doc/grammar.bnf @@ -0,0 +1,4 @@ +ROOT ::= EXPR* +EXPR ::= BUILTIN +BUILTIN ::= +| int diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e1adc96..e21a5f6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -4,6 +4,24 @@ project(skopy-lib) add_library(skopy-lib SHARED src/commons.c + src/vec.c + src/str.c + + src/token.c + src/lexer.c + + src/node.c + src/parser.c + + src/prog.c + src/value.c + + src/compiler.c + src/state.c + src/exec.c + src/module.c + + src/errors.c ) file(GLOB_RECURSE @@ -14,7 +32,7 @@ file(GLOB_RECURSE install(TARGETS skopy-lib) install( - FILES ${includes} + FILES ${includes} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/skopy" ) diff --git a/lib/include/commons.h b/lib/include/commons.h index 31a435e..aedde43 100644 --- a/lib/include/commons.h +++ b/lib/include/commons.h @@ -1,5 +1,25 @@ #ifndef SK_COMMONS_H #define SK_COMMONS_H +#include +#include +#include +#include +#include +#include + +#include "str.h" +#include "vec.h" +#include "errors.h" + +#define SK_ENUM_ENUM(X) X +#define SK_ENUM_STR(X) #X + +#define SK_ENUM_H(PREFIX, DEF) \ +typedef enum { DEF(SK_ENUM_ENUM) } PREFIX;\ +extern char const* PREFIX ## Str [] + +#define SK_ENUM_C(PREFIX, DEF) \ +char const* PREFIX ## Str [] = {DEF(SK_ENUM_STR)} #endif diff --git a/lib/include/compiler.h b/lib/include/compiler.h new file mode 100644 index 0000000..9109439 --- /dev/null +++ b/lib/include/compiler.h @@ -0,0 +1,19 @@ +#ifndef SK_COMPILER_H +#define SK_COMPILER_H + +#include "commons.h" +#include "prog.h" + +struct compiler +{ +}; + +void compiler_init(struct compiler* self); +void compiler_free(struct compiler* self); + +void compiler_compile(struct compiler* self, + struct node* node, + struct prog* prog); + +#endif + diff --git a/lib/include/errors.h b/lib/include/errors.h new file mode 100644 index 0000000..6de30d3 --- /dev/null +++ b/lib/include/errors.h @@ -0,0 +1,30 @@ +#ifndef SK_ERROR_H +#define SK_ERROR_H + +#include +#include +#include +#include +#include +#include +#include + +struct error +{ + int line; + char* msg; +}; + +extern struct vec* errors; + +void errors_init(); +void errors_free(); + +void errors_push(int line, char const* format, ...); +bool errors_ok(); +void errors_dump(); + +void error_init(struct error* self, int line, char* msg); +void error_free(struct error* self); + +#endif diff --git a/lib/include/exec.h b/lib/include/exec.h new file mode 100644 index 0000000..7d23709 --- /dev/null +++ b/lib/include/exec.h @@ -0,0 +1,20 @@ +#ifndef SK_EXEC_H +#define SK_EXEC_H + +#include "commons.h" +#include "state.h" +#include "prog.h" + +struct exec +{ + size_t pc; +}; + +void exec_init(struct exec* self); +void exec_free(struct exec* self); + +void exec_execute(struct exec* self, + struct state* state, + struct prog* prog); + +#endif diff --git a/lib/include/lexer.h b/lib/include/lexer.h new file mode 100644 index 0000000..97e759b --- /dev/null +++ b/lib/include/lexer.h @@ -0,0 +1,30 @@ +#ifndef SK_LEXER_H +#define SK_LEXER_H + +#include "commons.h" +#include "token.h" + +struct context +{ + int line; + size_t cursor; +}; + +struct lexer +{ + char* source; + size_t len; + struct context context; +}; + +void lexer_init(struct lexer* self, char const* source); +void lexer_free(struct lexer* self); + +void lexer_skip_spaces(struct lexer* self); + +bool lexer_next_is(struct lexer* self, TokenKind kind); + +struct token* lexer_try_new_next(struct lexer* self); +struct token* lexer_try_scan_int(struct lexer* self); + +#endif diff --git a/lib/include/module.h b/lib/include/module.h new file mode 100644 index 0000000..51a2797 --- /dev/null +++ b/lib/include/module.h @@ -0,0 +1,21 @@ +#ifndef SK_MODULE_H +#define SK_MODULE_H + +#include "commons.h" +#include "prog.h" + +struct module +{ + struct str source; + struct prog prog; +}; + +void module_init(struct module* self); +void module_free(struct module* self); + +void module_load_source(struct module* self, + char const* path); + +void module_compile(struct module* self); + +#endif diff --git a/lib/include/node.h b/lib/include/node.h new file mode 100644 index 0000000..02b95c2 --- /dev/null +++ b/lib/include/node.h @@ -0,0 +1,27 @@ +#ifndef SK_NODE_H +#define SK_NODE_H + +#include "commons.h" + +#define NODE_KIND(G) \ +G(NODE_ROOT), \ +G(NODE_INT) + +SK_ENUM_H(NodeKind, NODE_KIND); + +struct node +{ + NodeKind kind; + struct vec children; + struct token* token; +}; + +void node_init(struct node* self, + NodeKind kind, + struct token* token); +void node_free(struct node* self); + +void node_push_new_child(struct node* self, struct node* child); + +void node_str(struct node* self, struct str* dest); +#endif diff --git a/lib/include/parser.h b/lib/include/parser.h new file mode 100644 index 0000000..2f38076 --- /dev/null +++ b/lib/include/parser.h @@ -0,0 +1,25 @@ +#ifndef SK_PARSER_H +#define SK_PARSER_H + +#include "commons.h" +#include "node.h" +#include "lexer.h" + +struct parser +{ + struct lexer lexer; +}; + +void parser_init(struct parser* self, char const* source); +void parser_free(struct parser* self); + +struct node* parser_try(struct parser* self, + struct node* (*rule)(struct parser*)); + +struct node* parser_try_parse(struct parser* self); +struct node* parser_try_root(struct parser* self); +struct node* parser_try_expr(struct parser* self); +struct node* parser_try_builtin(struct parser* self); + +#endif + diff --git a/lib/include/prog.h b/lib/include/prog.h new file mode 100644 index 0000000..7bbab95 --- /dev/null +++ b/lib/include/prog.h @@ -0,0 +1,29 @@ +#ifndef SK_PROG_H +#define SK_PROG_H + +#include "commons.h" +#include "value.h" + +#define SK_NO_PARAM (-1) +#define OPCODE(G) \ +G(OP_PUSH) + +SK_ENUM_H(Opcode, OPCODE); + +struct prog +{ + struct vec opcodes; + struct vec params; + struct vec constants; +}; + +void prog_init(struct prog* self); +void prog_free(struct prog* self); + +size_t prog_add_instr(struct prog* self, + Opcode opcode, + size_t param); + +size_t prog_add_constant(struct prog* self, + struct value* new_value); +#endif diff --git a/lib/include/state.h b/lib/include/state.h new file mode 100644 index 0000000..1306d4a --- /dev/null +++ b/lib/include/state.h @@ -0,0 +1,47 @@ +#ifndef SK_STATE_H +#define SK_STATE_H + +#include "commons.h" +#include "value.h" +#define SK size_t + +struct local +{ + size_t addr; + struct value* value; +}; + +struct frame +{ + struct vec locals; + struct vec stack; +}; + +struct state +{ + struct vec frames; + size_t addr; +}; + +void local_init(struct local* self, + size_t addr, + struct value* new_value); + +void local_free(struct local* self); + +void frame_init(struct frame* self); +void frame_free(struct frame* self); + +void state_init(struct state* self); +void state_free(struct state* self); + +struct frame* state_frame(struct state* self); +void state_push_frame(struct state* self); + +bool state_has_top(struct state* self); +SK state_top(struct state* self); +struct value* state_try_get_value(struct state* self, SK value); + +SK state_push_int(struct state* self, int integer); + +#endif diff --git a/lib/include/str.h b/lib/include/str.h new file mode 100644 index 0000000..8b82751 --- /dev/null +++ b/lib/include/str.h @@ -0,0 +1,24 @@ +#ifndef SK_STR_H +#define SK_STR_H + +#include +#include +#include +#include +#include + +struct str +{ + size_t size; + size_t capacity; + char* value; +}; + +void str_init(struct str* self); +void str_free(struct str* self); + +void str_push(struct str* self, char c); +void str_extend(struct str* self, char* src); +void str_format(struct str* self, char const* format, ...); + +#endif diff --git a/lib/include/token.h b/lib/include/token.h new file mode 100644 index 0000000..78888a6 --- /dev/null +++ b/lib/include/token.h @@ -0,0 +1,27 @@ +#ifndef SK_TOKEN_H +#define SK_TOKEN_H + +#include "commons.h" + +#define TOKEN_KIND(G) \ +G(TOKEN_ROOT), \ +G(TOKEN_INT) + +SK_ENUM_H(TokenKind, TOKEN_KIND); + +struct token +{ + TokenKind kind; + char* value; + int line; +}; + +void token_init(struct token* self, + TokenKind kind, + char const* value, + int line); +void token_free(struct token* self); + +void token_str(struct token* self, struct str* dest); + +#endif diff --git a/lib/include/value.h b/lib/include/value.h new file mode 100644 index 0000000..b22d1d5 --- /dev/null +++ b/lib/include/value.h @@ -0,0 +1,32 @@ +#ifndef SK_VALUE_H +#define SK_VALUE_H + +#include "commons.h" +#include "node.h" + +#define TYPE_KIND(G) \ +G(TYPE_INT) + +SK_ENUM_H(TypeKind, TYPE_KIND); + +union val +{ + int integer; +}; + +struct value +{ + TypeKind type; + union val val; +}; + +void value_init(struct value* self, + TypeKind type, + union val val); + +void value_free(struct value* self); + +void value_str(struct value* self, struct str* dest); + +#endif + diff --git a/lib/include/vec.h b/lib/include/vec.h new file mode 100644 index 0000000..09f290c --- /dev/null +++ b/lib/include/vec.h @@ -0,0 +1,22 @@ +#ifndef SK_VEC_H +#define SK_VEC_H + +#include +#include + +struct vec +{ + size_t size; + size_t capacity; + void** data; +}; + +void vec_init(struct vec* self); +void vec_free_elements(struct vec* self, + void (*destructor)(void*)); +void vec_free(struct vec* self); + +void vec_push(struct vec* self, void* element); + +#endif + diff --git a/lib/src/compiler.c b/lib/src/compiler.c new file mode 100644 index 0000000..73b97d0 --- /dev/null +++ b/lib/src/compiler.c @@ -0,0 +1,48 @@ +#include "compiler.h" +#include "token.h" + +void compiler_init(struct compiler* self) +{ + assert(self); +} + +void compiler_free(struct compiler* self) +{ + assert(self); +} + +void compiler_compile(struct compiler* self, + struct node* node, + struct prog* prog) +{ + assert(self); + assert(node); + assert(prog); + + switch (node->kind) + { + case NODE_ROOT: { + for (size_t i=0; ichildren.size; i++) + { + compiler_compile(self, node->children.data[i], + prog); + } + } break; + + case NODE_INT: { + struct value* value = malloc(sizeof(struct value)); + union val val; + val.integer = atoi(node->token->value); + value_init(value, TYPE_INT, val); + + size_t idx = prog_add_constant(prog, value); + prog_add_instr(prog, OP_PUSH, idx); + } break; + + default: { + fprintf(stderr, "cannot compile node '%s'\n", + NodeKindStr[node->kind]); + abort(); + } break; + } +} diff --git a/lib/src/errors.c b/lib/src/errors.c new file mode 100644 index 0000000..370f989 --- /dev/null +++ b/lib/src/errors.c @@ -0,0 +1,64 @@ +#include "errors.h" + +struct vec* errors = NULL; + +void errors_init() +{ + errors = malloc(sizeof(struct vec)); + vec_init(errors); +} + +void errors_free() +{ + vec_free_elements(errors, (void*) error_free); + vec_free(errors); + free(errors); +} + +void errors_push(int line, char const* format, ...) +{ + va_list lst; + va_start(lst, format); + + size_t const sz = 4096; + char buffer[sz]; + + vsnprintf(buffer, sz, format, lst); + + struct error* err = malloc(sizeof(struct error)); + error_init(err, line, buffer); + + vec_push(errors, err); + + va_end(lst); +} + +bool errors_ok() +{ + return errors->size == 0; +} + +void errors_dump() +{ + for (size_t i=0; isize; i++) + { + struct error const* err = errors->data[i]; + + fprintf(stderr, "[ERR:%d] %s\n", err->line, err->msg); + } +} + +void error_init(struct error* self, int line, char* msg) +{ + assert(self); + assert(msg); + + self->line = line; + self->msg = strdup(msg); +} + +void error_free(struct error* self) +{ + assert(self); + free(self->msg); +} diff --git a/lib/src/exec.c b/lib/src/exec.c new file mode 100644 index 0000000..4dfd672 --- /dev/null +++ b/lib/src/exec.c @@ -0,0 +1,48 @@ +#include "exec.h" + +void exec_init(struct exec* self) +{ + assert(self); + self->pc = 0; +} + +void exec_free(struct exec* self) +{ + assert(self); +} + +void exec_execute(struct exec* self, + struct state* state, + struct prog* prog) +{ + while (self->pc < prog->opcodes.size) + { + Opcode opcode = (size_t) prog->opcodes.data[self->pc]; + size_t param = (size_t) prog->params.data[self->pc]; + + switch (opcode) + { + case OP_PUSH: { + struct value* constant = prog->constants.data[param]; + switch (constant->type) + { + case TYPE_INT: { + state_push_int(state, constant->val.integer); + } break; + default: { + fprintf(stderr, "cannot push value '%s'\n", + TypeKindStr[constant->type]); + abort(); + } break; + } + self->pc++; + } break; + + default: { + fprintf(stderr, "cannot execute opcode '%s'\n", + OpcodeStr[opcode]); + abort(); + } break; + } + } +} diff --git a/lib/src/lexer.c b/lib/src/lexer.c new file mode 100644 index 0000000..059de43 --- /dev/null +++ b/lib/src/lexer.c @@ -0,0 +1,127 @@ +#include "lexer.h" + +void lexer_init(struct lexer* self, char const* source) +{ + assert(self); + self->source = strdup(source); + self->len = strlen(self->source); + self->context.line = 1; + self->context.cursor = 0; +} + +void lexer_free(struct lexer* self) +{ + assert(self); + free(self->source); +} + +void lexer_skip_spaces(struct lexer* self) +{ + assert(self); + + while (self->context.cursor < self->len + && isspace(self->source[self->context.cursor])) + { + if (self->source[self->context.cursor] == '\n') + { + self->context.line++; + } + + self->context.cursor++; + } +} + +bool lexer_next_is(struct lexer* self, TokenKind kind) +{ + struct context ctx = self->context; + + struct token* tok = lexer_try_new_next(self); + + if (!tok) + { + return false; + } + + bool res = tok->kind == kind; + token_free(tok); + free(tok); + + self->context = ctx; + + return res; +} + +struct token* lexer_try_new_next(struct lexer* self) +{ + assert(self); + + if (!errors_ok()) + { + return NULL; + } + + lexer_skip_spaces(self); + + struct token* tok = NULL; + + if ( (tok=lexer_try_scan_int(self)) ) + { + return tok; + } + + if (self->context.cursor < self->len) + { + struct str str; + str_init(&str); + + while (self->context.cursor < self->len + && !isspace(self->source[self->context.cursor])) + { + str_push(&str, self->source[self->context.cursor]); + self->context.cursor++; + } + + errors_push(self->context.line, "unknown symbol '%s'", + str.value); + str_free(&str); + } + + return NULL; +} + +struct token* lexer_try_scan_int(struct lexer* self) +{ + assert(self); + size_t cursor = self->context.cursor; + struct str value; + str_init(&value); + + if (cursor < self->len + && self->source[cursor] == '-') + { + str_push(&value, self->source[cursor]); + cursor++; + } + + while (cursor < self->len + && isdigit(self->source[cursor])) + { + str_push(&value, self->source[cursor]); + cursor++; + } + + struct token* tok = NULL; + + if (value.size > 0) + { + tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_INT, + value.value, self->context.line); + + self->context.cursor = cursor; + } + + str_free(&value); + return tok; +} + diff --git a/lib/src/module.c b/lib/src/module.c new file mode 100644 index 0000000..2f45dbf --- /dev/null +++ b/lib/src/module.c @@ -0,0 +1,89 @@ +#include "module.h" +#include "parser.h" +#include "compiler.h" +#include "state.h" +#include "exec.h" + +void module_init(struct module* self) +{ + assert(self); + str_init(&self->source); + prog_init(&self->prog); +} + +void module_free(struct module* self) +{ + assert(self); + str_free(&self->source); + prog_free(&self->prog); +} + +void module_load_source(struct module* self, + char const* path) +{ + assert(self); + FILE* file = fopen(path, "r"); + assert(file); + + char buf; + + while ( fread(&buf, 1, sizeof(char), file) > 0 ) + { + str_push(&self->source, buf); + } + + fclose(file); +} + +void module_compile(struct module* self) +{ + assert(self); + struct parser parser; + + parser_init(&parser, self->source.value); + struct node* root = parser_try_parse(&parser); + + if (!errors_ok()) + { + errors_dump(); + goto free_node; + } + + struct compiler compiler; + compiler_init(&compiler); + + compiler_compile(&compiler, root, &self->prog); + + struct exec exec; + exec_init(&exec); + + struct state state; + state_init(&state); + + exec_execute(&exec, &state, &self->prog); + + if (state_has_top(&state)) + { + struct value* value = state_try_get_value( + &state, + state_top(&state) + ); + + assert(value); + + struct str str; + str_init(&str); + value_str(value, &str); + printf("%s\n", str.value); + str_free(&str); + } + + // Free + state_free(&state); + exec_free(&exec); + compiler_free(&compiler); +free_node: + node_free(root); + free(root); + parser_free(&parser); +} diff --git a/lib/src/node.c b/lib/src/node.c new file mode 100644 index 0000000..f444b5a --- /dev/null +++ b/lib/src/node.c @@ -0,0 +1,57 @@ +#include "node.h" +#include "token.h" + +SK_ENUM_C(NodeKind, NODE_KIND); + +void node_init(struct node* self, + NodeKind kind, + struct token* token) +{ + assert(self); + self->kind = kind; + vec_init(&self->children); + self->token = token; +} + +void node_free(struct node* self) +{ + assert(self); + vec_free_elements(&self->children, (void*) node_free); + vec_free(&self->children); + + if (self->token) + { + token_free(self->token); + free(self->token); + } +} + +void node_push_new_child(struct node* self, struct node* child) +{ + vec_push(&self->children, child); +} + +void node_str(struct node* self, struct str* dest) +{ + assert(self); + assert(dest); + assert(self->token); + str_format(dest, "%s", + NodeKindStr[self->kind] + strlen("NODE_")); + + if (strcmp(self->token->value, "") != 0) + { + str_format(dest, "[%s]", self->token->value); + } + + if (self->children.size > 0) + { + str_extend(dest, "("); + for (size_t i=0; ichildren.size; i++) + { + if (i > 0) { str_push(dest, ','); } + node_str(self->children.data[i], dest); + } + str_extend(dest, ")"); + } +} diff --git a/lib/src/parser.c b/lib/src/parser.c new file mode 100644 index 0000000..1014d61 --- /dev/null +++ b/lib/src/parser.c @@ -0,0 +1,83 @@ +#include "parser.h" + +#define SK_TRY(RULE) parser_try(self, RULE) + +void parser_init(struct parser* self, char const* source) +{ + assert(self); + lexer_init(&self->lexer, source); +} + +void parser_free(struct parser* self) +{ + assert(self); + lexer_free(&self->lexer); +} + +struct node* parser_try(struct parser* self, + struct node* (*rule)(struct parser*)) +{ + assert(self); + assert(rule); + + struct context ctx = self->lexer.context; + + struct node* res = (*rule)(self); + + if (res == NULL) + { + self->lexer.context = ctx; + } + + return res; +} + +struct node* parser_try_parse(struct parser* self) +{ + assert(self); + return SK_TRY(parser_try_root); +} + +struct node* parser_try_root(struct parser* self) +{ + assert(self); + struct node* node = malloc(sizeof(struct node)); + struct token* tok = malloc(sizeof(struct token)); + token_init(tok, + TOKEN_ROOT, + "", + self->lexer.context.line); + + node_init(node, NODE_ROOT, tok); + + while (true) + { + struct node* expr = SK_TRY(parser_try_expr); + if (!expr) { break; } + + node_push_new_child(node, expr); + } + + return node; +} + +struct node* parser_try_expr(struct parser* self) +{ + return SK_TRY(parser_try_builtin); +} + +struct node* parser_try_builtin(struct parser* self) +{ + if (lexer_next_is(&self->lexer, TOKEN_INT)) + { + struct node* node = malloc(sizeof(struct node)); + + node_init(node, + NODE_INT, + lexer_try_new_next(&self->lexer)); + + return node; + } + + return NULL; +} diff --git a/lib/src/prog.c b/lib/src/prog.c new file mode 100644 index 0000000..dcea0ff --- /dev/null +++ b/lib/src/prog.c @@ -0,0 +1,42 @@ +#include "prog.h" +#include "value.h" + +SK_ENUM_C(Opcode, OPCODE); + +void prog_init(struct prog* self) +{ + assert(self); + vec_init(&self->opcodes); + vec_init(&self->params); + vec_init(&self->constants); +} + +void prog_free(struct prog* self) +{ + assert(self); + vec_free(&self->opcodes); + vec_free(&self->params); + vec_free_elements(&self->constants, (void*)value_free); + vec_free(&self->constants); +} + +size_t prog_add_instr(struct prog* self, + Opcode opcode, + size_t param) +{ + assert(self); + size_t addr = self->opcodes.size; + vec_push(&self->opcodes, (void*) opcode); + vec_push(&self->params, (void*) param); + + return addr; +} + +size_t prog_add_constant(struct prog* self, + struct value* new_value) +{ + assert(self); + vec_push(&self->constants, new_value); + + return self->constants.size - 1; +} diff --git a/lib/src/state.c b/lib/src/state.c new file mode 100644 index 0000000..f2fc688 --- /dev/null +++ b/lib/src/state.c @@ -0,0 +1,117 @@ +#include "state.h" + +void local_init(struct local* self, + size_t addr, + struct value* new_value) +{ + assert(self); + assert(new_value); + + self->addr = addr; + self->value = new_value; +} + +void local_free(struct local* self) +{ + value_free(self->value); + free(self->value); + self->value = NULL; +} + +void frame_init(struct frame* self) +{ + vec_init(&self->locals); + vec_init(&self->stack); +} + +void frame_free(struct frame* self) +{ + vec_free_elements(&self->locals, (void*) local_free); + vec_free(&self->locals); + vec_free(&self->stack); +} + +void state_init(struct state* self) +{ + assert(self); + vec_init(&self->frames); + self->addr = 1; + state_push_frame(self); +} + +void state_free(struct state* self) +{ + assert(self); + vec_free_elements(&self->frames, (void*) frame_free); + vec_free(&self->frames); +} + +struct frame* state_frame(struct state* self) +{ + assert(self); + assert(self->frames.size > 0); + + return self->frames.data[self->frames.size - 1]; +} + +void state_push_frame(struct state* self) +{ + assert(self); + struct frame* frame = malloc(sizeof(struct frame)); + frame_init(frame); + + vec_push(&self->frames, frame); +} + +bool state_has_top(struct state* self) +{ + struct frame* frame = state_frame(self); + return frame->stack.size > 0; +} + +SK state_top(struct state* self) +{ + assert(self); + struct frame* frame = state_frame(self); + assert(frame->stack.size > 0); + + return (SK) frame->stack.data[frame->stack.size - 1]; +} + +struct value* state_try_get_value(struct state* self, SK value) +{ + assert(self); + struct frame* frame = state_frame(self); + + for (size_t i=0; ilocals.size; i++) + { + struct local* local = frame->locals.data[i]; + + if (local->addr == value) + { + return local->value; + } + } + + return NULL; +} + +SK state_push_int(struct state* self, int integer) +{ + assert(self); + + struct value* value = malloc(sizeof(struct value)); + union val val; + val.integer = integer; + value_init(value, TYPE_INT, val); + + struct frame* frame = state_frame(self); + struct local* local = malloc(sizeof(struct local)); + local_init(local, self->addr, value); + + vec_push(&frame->locals, local); + vec_push(&frame->stack, (void*) self->addr); + + self->addr++; + return self->addr - 1; + } diff --git a/lib/src/str.c b/lib/src/str.c new file mode 100644 index 0000000..7805821 --- /dev/null +++ b/lib/src/str.c @@ -0,0 +1,72 @@ +#include "str.h" + +void str_init(struct str* self) +{ + assert(self); + self->size = 0; + self->capacity = 0; + self->value = NULL; +} + +void str_free(struct str* self) +{ + assert(self); + + if (self->value) + { + free(self->value); + } + + self->size = 0; + self->capacity = 0; + self->value = NULL; +} + +void str_push(struct str* self, char c) +{ + assert(self); + + if (self->capacity == 0) + { + self->capacity = 2; + self->value = malloc(sizeof(char) * self->capacity); + } + + if (self->size + 2 >= self->capacity) + { + self->capacity *= 2; + char* buffer = realloc(self->value, + sizeof(char) * self->capacity); + assert(buffer); + self->value = buffer; + } + + self->value[self->size] = c; + self->value[self->size + 1] = '\0'; + self->size++; +} + +void str_extend(struct str* self, char* src) +{ + assert(self); + assert(src); + size_t sz = strlen(src); + + for (size_t i=0; ikind = kind; + self->value = strdup(value); + self->line = line; +} + +void token_free(struct token* self) +{ + assert(self); + free(self->value); +} + +void token_str(struct token* self, struct str* dest) +{ + assert(self); + assert(dest); + + if (self->value == NULL || strcmp(self->value, "") == 0) + { + str_format(dest, "%s", + TokenKindStr[self->kind] + strlen("TOKEN_")); + } + else + { + str_format(dest, "%s[%s]", + TokenKindStr[self->kind] + strlen("TOKEN_"), + self->value); + } +} diff --git a/lib/src/value.c b/lib/src/value.c new file mode 100644 index 0000000..d7f5959 --- /dev/null +++ b/lib/src/value.c @@ -0,0 +1,36 @@ +#include "value.h" + +SK_ENUM_C(TypeKind, TYPE_KIND); + +void value_init(struct value* self, + TypeKind type, + union val val) +{ + assert(self); + self->type = type; + self->val = val; +} + +void value_free(struct value* self) +{ + assert(self); +} + +void value_str(struct value* self, struct str* dest) +{ + assert(self); + assert(dest); + + switch (self->type) + { + case TYPE_INT: { + str_format(dest, "%d", self->val.integer); + } break; + default: { + fprintf(stderr, "cannot get value string of '%s'\n", + TypeKindStr[self->type]); + abort(); + } break; + } + +} diff --git a/lib/src/vec.c b/lib/src/vec.c new file mode 100644 index 0000000..9684f67 --- /dev/null +++ b/lib/src/vec.c @@ -0,0 +1,59 @@ +#include "vec.h" + +void vec_init(struct vec* self) +{ + assert(self); + self->size = 0; + self->capacity = 0; + self->data = NULL; +} + +void vec_free_elements(struct vec* self, + void (*destructor)(void*)) +{ + for (size_t i=0; isize; i++) + { + if (destructor) + { + (*destructor)(self->data[i]); + } + + free(self->data[i]); + } +} + +void vec_free(struct vec* self) +{ + assert(self); + + free(self->data); + + self->size = 0; + self->capacity = 0; + self->data = NULL; +} + +void vec_push(struct vec* self, void* element) +{ + assert(self); + + if (self->data == NULL) + { + self->capacity = 2; + self->data = malloc(sizeof(void*) * self->capacity); + } + + if (self->size >= self->capacity) + { + self->capacity *= 2; + void** buffer = realloc( + self->data, + sizeof(void*) * self->capacity + ); + assert(buffer); + self->data = buffer; + } + + self->data[self->size] = element; + self->size++; +} diff --git a/tests/lexer.h b/tests/lexer.h new file mode 100644 index 0000000..f1b18db --- /dev/null +++ b/tests/lexer.h @@ -0,0 +1,54 @@ +#ifndef SK_TEST_LEXER_H +#define SK_TEST_LEXER_H + +#include +#include +#include + +static void test_lexer(char const* source, int count, ...) +{ + struct lexer lexer; + lexer_init(&lexer, source); + + va_list lst; + va_start(lst, count); + + for (int i=0; i #include - -void test_trivial() -{ - CU_ASSERT(1 + 1 == 2); -} +#include "lexer.h" +#include "parser.h" int main() { + errors_init(); CU_initialize_registry(); - CU_pSuite suite = CU_add_suite("First suite", 0, 0); - CU_add_test(suite, "Trivial", test_trivial); + register_lexer(); + register_parser(); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); @@ -19,6 +17,6 @@ int main() int status = CU_get_number_of_failures(); CU_cleanup_registry(); - + errors_free(); return status; } diff --git a/tests/parser.h b/tests/parser.h new file mode 100644 index 0000000..6370d2e --- /dev/null +++ b/tests/parser.h @@ -0,0 +1,38 @@ +#ifndef SK_TEST_PARSER_H +#define SK_TEST_PARSER_H + +#include +#include "node.h" +#include + +static void test_parser(char const* oracle, char const* source) +{ + struct parser parser; + parser_init(&parser, source); + struct node* node = parser_try_parse(&parser); + CU_ASSERT_FATAL(node != NULL); + + struct str str; + str_init(&str); + node_str(node, &str); + CU_ASSERT_STRING_EQUAL(oracle, str.value); + str_free(&str); + + node_free(node); + free(node); + parser_free(&parser); +} + +static void test_parser_int() +{ + test_parser("ROOT(INT[32])", " 32 "); + test_parser("ROOT(INT[32],INT[-2],INT[24])", " 32 -2 24"); +} + +void register_parser() +{ + CU_pSuite suite = CU_add_suite("Parser", 0, 0); + CU_add_test(suite, "Integers", test_parser_int); +} + +#endif