From 09bbc313207ce4b68ef070bf6f818980e9a496ce Mon Sep 17 00:00:00 2001 From: bog Date: Fri, 16 Feb 2024 22:47:37 +0100 Subject: [PATCH] :sparkles: functions --- CMakeLists.txt | 2 +- Makefile | 3 + bc/CMakeLists.txt | 22 --- doc/gux.bnf | 32 +++- guxi/CMakeLists.txt | 17 +- guxi/src/main.c | 16 +- lang/CMakeLists.txt | 8 +- {bc => lang}/src/compiler.c | 137 ++++++++++++-- {bc => lang}/src/compiler.h | 6 +- lang/src/fun.c | 39 ++++ lang/src/fun.h | 16 ++ lang/src/lexer.c | 5 + lang/src/node.c | 33 ++++ lang/src/node.h | 12 +- {bc => lang}/src/opcodes.c | 0 {bc => lang}/src/opcodes.h | 4 +- lang/src/parser.c | 355 ++++++++++++++++++++++++++++++++++-- lang/src/parser.h | 7 + {bc => lang}/src/program.c | 27 ++- {bc => lang}/src/program.h | 4 +- lang/src/sym_table.c | 156 ++++++++++++++++ lang/src/sym_table.h | 48 +++++ lang/src/syms.c | 191 ------------------- lang/src/syms.h | 49 ----- lang/src/type.c | 125 +++++++++++++ lang/src/type.h | 7 +- lang/src/type_checker.c | 255 ++++++++++++++++++++------ lang/src/type_checker.h | 6 +- lang/src/type_resolver.c | 68 +++++-- lang/src/type_resolver.h | 6 +- lang/src/value.c | 41 +++++ lang/src/value.h | 4 + lang/tests/lexer.c | 4 + lang/tests/parser.c | 43 +++++ lang/tests/sym_table.c | 91 +++++++++ lang/tests/type_checker.c | 41 +++-- lib/src/commons.h | 5 +- tests/flow_control.gux | 2 +- tests/fun.gux | 67 +++++++ tests/run.sh | 2 +- vm/CMakeLists.txt | 6 +- vm/src/vm.c | 73 +++++++- vm/src/vm.h | 1 + 43 files changed, 1616 insertions(+), 420 deletions(-) delete mode 100644 bc/CMakeLists.txt rename {bc => lang}/src/compiler.c (78%) rename {bc => lang}/src/compiler.h (89%) create mode 100644 lang/src/fun.c create mode 100644 lang/src/fun.h rename {bc => lang}/src/opcodes.c (100%) rename {bc => lang}/src/opcodes.h (90%) rename {bc => lang}/src/program.c (72%) rename {bc => lang}/src/program.h (87%) create mode 100644 lang/src/sym_table.c create mode 100644 lang/src/sym_table.h delete mode 100644 lang/src/syms.c delete mode 100644 lang/src/syms.h create mode 100644 lang/tests/sym_table.c create mode 100644 tests/fun.gux diff --git a/CMakeLists.txt b/CMakeLists.txt index f879361..16d4c24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,6 @@ project(gux LANGUAGES C) add_subdirectory(lib) add_subdirectory(lang) -add_subdirectory(bc) +# add_subdirectory(bc) add_subdirectory(vm) add_subdirectory(guxi) diff --git a/Makefile b/Makefile index 0e71eca..cecd606 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,9 @@ tests: build install: check tests sudo cmake --install build +force-install: build + sudo cmake --install build + check: @cppcheck --enable=all lib lang vm -q \ --suppress=missingIncludeSystem \ diff --git a/bc/CMakeLists.txt b/bc/CMakeLists.txt deleted file mode 100644 index 7679e3f..0000000 --- a/bc/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 3.2) - -project(gux-bc LANGUAGES C) - -add_library(gux-bc OBJECT - src/compiler.c - src/program.c - src/opcodes.c -) - -set_property(TARGET gux-bc PROPERTY C_STANDARD 99) - -add_dependencies(gux-bc gux-lang) - -target_include_directories(gux-bc - PUBLIC gux-lang - PUBLIC ${CMAKE_SOURCE_DIR}/bc/src -) - -target_link_libraries(gux-bc - PUBLIC gux-lang -) diff --git a/doc/gux.bnf b/doc/gux.bnf index 149fe42..ac268fd 100644 --- a/doc/gux.bnf +++ b/doc/gux.bnf @@ -11,15 +11,19 @@ LEXPR ::= | ASSERT EXPR | VARDECL | CONSTDECL +| FUNDECL | ASSIGN | break | continue +| return EXPR? +| FUN BEXPR ::= | BLOCK | IF | WHILE + IF ::= | if EXPR BLOCK | if EXPR BLOCK else BLOCK @@ -27,9 +31,15 @@ IF ::= WHILE ::= while EXPR BLOCK +FUN ::= fun PARAMS (rarrow TYPE) BLOCK + +PARAMS ::= +| opar ident colon TYPE (comma ident colon type)* cpar + BLOCK ::= obrace INSTR* cbrace -VARDECL ::= var ident colon type? assign EXPR -CONSTDECL ::= ident colon type? assign EXPR +VARDECL ::= var ident colon TYPE? assign EXPR +CONSTDECL ::= ident colon TYPE? assign EXPR +FUNDECL ::= fun ident PARAMS (rarrow TYPE) BLOCK ASSIGN ::= ident assign EXPR OR ::= AND (or AND)* @@ -40,6 +50,20 @@ TERM ::= FACTOR ((add|sub) FACTOR)* FACTOR ::= POW ((mul|div|mod) POW)* POW ::= NOT (pow NOT)? -NOT ::= not* LITERAL -LITERAL ::= ident | BUILTIN | opar EXPR cpar +NOT ::= not* CALL +CALL ::= LITERAL ARGS* +ARGS ::= opar (EXPR (comma EXPR)*)? cpar + +LITERAL ::= +| ident +| BUILTIN +| opar EXPR cpar + +CALL ::= LITERAL ARGS* +ARGS ::= opar (EXPR (comma EXPR)*)? cpar + BUILTIN ::= bool | int | float | string + +TYPE ::= +| type +| opar TYPE+ rarrow TYPE+ cpar diff --git a/guxi/CMakeLists.txt b/guxi/CMakeLists.txt index 3ef423d..56b6926 100644 --- a/guxi/CMakeLists.txt +++ b/guxi/CMakeLists.txt @@ -1,24 +1,23 @@ cmake_minimum_required(VERSION 3.2) -project(guxi LANGUAGES C) +project(gux LANGUAGES C) -add_executable(guxi +add_executable(gux src/main.c ) -set_property(TARGET guxi PROPERTY C_STANDARD 99) +set_property(TARGET gux PROPERTY C_STANDARD 99) -add_dependencies(guxi gux-vm) +add_dependencies(gux gux-vm) -target_include_directories(guxi - PUBLIC gux-vm gux-bc +target_include_directories(gux + PUBLIC gux-vm ) -target_link_libraries(guxi +target_link_libraries(gux PUBLIC gux-vm PUBLIC gux-lang - PUBLIC gux-bc PUBLIC gux-lib ) -install(TARGETS guxi) +install(TARGETS gux) diff --git a/guxi/src/main.c b/guxi/src/main.c index 1589ce8..8390359 100644 --- a/guxi/src/main.c +++ b/guxi/src/main.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include char* load_new_source(char const* path) { @@ -159,11 +159,8 @@ int main(int argc, char** argv) printf("%s\n", msg); } - struct syms syms; - syms_init(&syms); - struct type_checker checker; - type_checker_init(&checker, &syms); + type_checker_init(&checker); if (type_checker_check(&checker, ast) != 0) { @@ -172,7 +169,6 @@ int main(int argc, char** argv) checker.error_line, checker.error_msg); - syms_free(&syms); type_checker_free(&checker); if (ast) @@ -200,7 +196,7 @@ int main(int argc, char** argv) program_init(&program); struct compiler compiler; - compiler_init(&compiler, &syms); + compiler_init(&compiler); if (compiler_compile(&compiler, ast, &program) != 0) { @@ -211,7 +207,7 @@ int main(int argc, char** argv) compiler_free(&compiler); program_free(&program); - syms_free(&syms); + type_checker_free(&checker); if (ast) @@ -245,7 +241,7 @@ int main(int argc, char** argv) if (show_syms) { char buffer[GUX_STR_SIZE]; - syms_str(&syms, buffer, GUX_STR_SIZE); + sym_table_str(&compiler.sym_table, buffer, GUX_STR_SIZE); printf("%s\n", buffer); } @@ -265,7 +261,6 @@ int main(int argc, char** argv) vm_free(&vm); compiler_free(&compiler); program_free(&program); - syms_free(&syms); type_checker_free(&checker); if (ast) @@ -301,7 +296,6 @@ int main(int argc, char** argv) vm_free(&vm); compiler_free(&compiler); program_free(&program); - syms_free(&syms); type_checker_free(&checker); if (ast) diff --git a/lang/CMakeLists.txt b/lang/CMakeLists.txt index fe5eb50..c945fc6 100644 --- a/lang/CMakeLists.txt +++ b/lang/CMakeLists.txt @@ -10,9 +10,13 @@ add_library(gux-lang OBJECT src/parser.c src/type.c src/value.c + src/fun.c src/type_resolver.c src/type_checker.c - src/syms.c + src/sym_table.c + src/compiler.c + src/program.c + src/opcodes.c ) set_property(TARGET gux-lang PROPERTY C_STANDARD 99) @@ -42,7 +46,7 @@ add_executable(gux-lang-tests tests/type.c tests/value.c tests/type_checker.c - tests/syms.c + tests/sym_table.c ) set_property(TARGET gux-lang-tests PROPERTY C_STANDARD 99) diff --git a/bc/src/compiler.c b/lang/src/compiler.c similarity index 78% rename from bc/src/compiler.c rename to lang/src/compiler.c index bcbb16a..601a059 100644 --- a/bc/src/compiler.c +++ b/lang/src/compiler.c @@ -3,14 +3,17 @@ #include "node.h" #include "opcodes.h" #include "program.h" -#include "syms.h" +#include "sym_table.h" +#include "type.h" +#include "type_checker.h" #include "value.h" #include "vec.h" +#include "fun.h" -void compiler_init(struct compiler* self, struct syms* syms) +void compiler_init(struct compiler* self) { assert(self); - self->syms = syms; + sym_table_init(&self->sym_table); memset(self->error_msg, 0, GUX_STR_SIZE); self->stack_size = 0; vec_init(&self->loops, 1); @@ -18,6 +21,7 @@ void compiler_init(struct compiler* self, struct syms* syms) void compiler_free(struct compiler* self) { + sym_table_free(&self->sym_table); vec_free_elements(&self->loops); vec_free(&self->loops); assert(self); @@ -33,6 +37,102 @@ int compiler_compile(struct compiler* self, switch (node->type) { + case NODE_RETURN: { + if (node->children.size > 0) + { + compiler_compile(self, node->children.data[0], program); + } + compiler_gen_instr(self, program, OP_RET, NO_PARAM); + } break; + + case NODE_FUN: { + struct node* params = node->children.data[0]; + struct node* body = node->children.data[2]; + + struct fun* fun = malloc(sizeof(struct fun)); + fun_init(fun, node); + + sym_table_push_table(&self->sym_table); + + struct compiler compiler; + compiler_init(&compiler); + + struct vec idents; + vec_init(&idents, 1); + + for (size_t i=0; ichildren.size; i++) + { + struct node* param = params->children.data[i]; + + if (param->type != NODE_TYPE) + { + vec_push(&idents, param); + } + else + { + for (size_t j=0; jvalue, + ty); + } + + vec_free(&idents); + vec_init(&idents, 1); + } + } + + vec_free(&idents); + + struct type* ty = malloc(sizeof(struct type)); + type_init_from_node(ty, node); + + sym_table_declare_const(&compiler.sym_table, "this", ty); + + if (compiler_compile(&compiler, body, &fun->program) != 0) + { + fun_free(fun); + free(fun); + return 1; + } + + compiler_gen_instr(&compiler, &fun->program, OP_RET, NO_PARAM); + + struct value* value = malloc(sizeof(struct value)); + value_init_new_fun(value, fun, node->line); + + compiler_gen_instr(self, + program, OP_PUSH, + program_push_constant(program, value)); + + compiler_free(&compiler); + sym_table_pop_table(&self->sym_table); + } break; + + case NODE_CALL: { + struct node* target = node->children.data[0]; + struct node* args = node->children.data[1]; + + for (size_t i=0; ichildren.size; i++) + { + struct node* arg = args->children.data[i]; + compiler_compile(self, arg, program); + } + + struct sym* entry = sym_table_try_by_name(&self->sym_table, + target->value); + + assert(entry); + + compiler_gen_instr(self, program, OP_LOAD, entry->addr); + compiler_gen_instr(self, program, OP_CALL, args->children.size); + } break; + case NODE_BREAK: { size_t* addr = malloc(sizeof(size_t)); *addr = compiler_gen_instr(self, program, OP_BR, NO_PARAM); @@ -78,8 +178,8 @@ int compiler_compile(struct compiler* self, for (size_t i=0; ito_end.size; i++) { - size_t goto_end = *(size_t*)loop->to_end.data[i]; - instr = program->instructions.data[goto_end]; + size_t my_goto_end = *(size_t*)loop->to_end.data[i]; + instr = program->instructions.data[my_goto_end]; instr->param = program->instructions.size; } @@ -114,6 +214,7 @@ int compiler_compile(struct compiler* self, case NODE_BLOCK: { int stack_base = self->stack_size; + sym_table_push_table(&self->sym_table); for (size_t i=0; ichildren.size; i++) { @@ -130,13 +231,14 @@ int compiler_compile(struct compiler* self, compiler_gen_instr(self, program, OP_POP, NO_PARAM); } + sym_table_pop_table(&self->sym_table); } break; case NODE_ASSIGN: { struct node* ident = node->children.data[0]; struct node* expr = node->children.data[1]; - struct syms_entry* entry = syms_try_get(self->syms, ident->value, - node); + struct sym* entry = sym_table_try_by_name(&self->sym_table, + ident->value); assert(entry); if (compiler_compile(self, expr, program) != 0) @@ -148,9 +250,8 @@ int compiler_compile(struct compiler* self, } break; case NODE_IDENT: { - struct syms_entry* entry = syms_try_get(self->syms, - node->value, - node); + struct sym* entry = sym_table_try_by_name(&self->sym_table, + node->value); if (!entry) { @@ -165,6 +266,7 @@ int compiler_compile(struct compiler* self, case NODE_CONSTDECL: case NODE_VARDECL: { + int err = compiler_compile(self, node->children.size == 1 ? node->children.data[0] : node->children.data[1], @@ -175,9 +277,15 @@ int compiler_compile(struct compiler* self, return 1; } - struct syms_entry* entry = syms_try_get(self->syms, - node->value, - node); + struct type* ty = malloc(sizeof(struct type)); + type_init_from_node(ty, node->children.data[0]); + + sym_table_declare(&self->sym_table, node->value, ty, + node->type == NODE_VARDECL); + + struct sym* entry = sym_table_try_by_name(&self->sym_table, + node->value); + assert(entry); compiler_gen_instr(self, program, OP_STORE, entry->addr); @@ -487,6 +595,8 @@ size_t compiler_gen_instr(struct compiler* self, switch (op) { + case OP_CALL: break; + case OP_LOAD: case OP_PUSH: self->stack_size += 1; break; @@ -507,6 +617,7 @@ size_t compiler_gen_instr(struct compiler* self, case OP_BRT: self->stack_size -= 1; break; + case OP_RET: case OP_SWAP: case OP_BR: case OP_NOT: diff --git a/bc/src/compiler.h b/lang/src/compiler.h similarity index 89% rename from bc/src/compiler.h rename to lang/src/compiler.h index a3cb162..d471ac7 100644 --- a/bc/src/compiler.h +++ b/lang/src/compiler.h @@ -4,7 +4,7 @@ #include #include #include "program.h" -#include "syms.h" +#include "sym_table.h" struct loop_info { size_t to_cond; @@ -13,13 +13,13 @@ struct loop_info { struct compiler { char error_msg[GUX_STR_SIZE]; - struct syms* syms; + struct sym_table sym_table; int error_line; int stack_size; struct vec loops; }; -void compiler_init(struct compiler* self, struct syms* syms); +void compiler_init(struct compiler* self); void compiler_free(struct compiler* self); int compiler_compile(struct compiler* self, diff --git a/lang/src/fun.c b/lang/src/fun.c new file mode 100644 index 0000000..ae5f7c9 --- /dev/null +++ b/lang/src/fun.c @@ -0,0 +1,39 @@ +#include "fun.h" +#include "program.h" + +void fun_init(struct fun* self, struct node* node) +{ + assert(self); + self->node = node; + program_init(&self->program); +} + +void fun_free(struct fun* self) +{ + program_free(&self->program); + assert(self); +} + +struct fun* fun_new_clone(struct fun* self) +{ + assert(self); + + struct fun* clone = malloc(sizeof(struct fun)); + assert(clone); + + fun_init(clone, self->node); + + for (size_t i=0; iprogram.instructions.size; i++) + { + struct instruction* instr = self->program.instructions.data[i]; + program_push_instr(&clone->program, instr->opcode, instr->param); + } + + for (size_t i=0; iprogram.constant_pool.size; i++) + { + struct value* val = self->program.constant_pool.data[i]; + program_push_constant(&clone->program, value_new_clone(val)); + } + + return clone; +} diff --git a/lang/src/fun.h b/lang/src/fun.h new file mode 100644 index 0000000..25b63e1 --- /dev/null +++ b/lang/src/fun.h @@ -0,0 +1,16 @@ +#ifndef FUN_H +#define FUN_H + +#include +#include "program.h" + +struct fun { + struct node* node; + struct program program; +}; + +void fun_init(struct fun* self, struct node* node); +void fun_free(struct fun* self); +struct fun* fun_new_clone(struct fun* self); + +#endif diff --git a/lang/src/lexer.c b/lang/src/lexer.c index dc96426..aeb4b00 100644 --- a/lang/src/lexer.c +++ b/lang/src/lexer.c @@ -12,6 +12,10 @@ void lexer_init(struct lexer* self, char const* source) vec_init(&self->toks, 1); + lexer_add_tok(self, "->", NODE_RARROW, "", 1); + lexer_add_tok(self, "fun", NODE_FUN, "", 1); + lexer_add_tok(self, "return", NODE_RETURN, "", 1); + lexer_add_tok(self, "continue", NODE_CONTINUE, "", 1); lexer_add_tok(self, "break", NODE_BREAK, "", 1); lexer_add_tok(self, "if", NODE_IF, "", 1); @@ -27,6 +31,7 @@ void lexer_init(struct lexer* self, char const* source) lexer_add_tok(self, "bool", NODE_TYPE, "bool", 1); lexer_add_tok(self, "string", NODE_TYPE, "string", 1); + lexer_add_tok(self, ",", NODE_COMMA, "", 0); lexer_add_tok(self, "{", NODE_OBRACE, "", 0); lexer_add_tok(self, "}", NODE_CBRACE, "", 0); lexer_add_tok(self, ":", NODE_COLON, "", 0); diff --git a/lang/src/node.c b/lang/src/node.c index fdb3899..9e43272 100644 --- a/lang/src/node.c +++ b/lang/src/node.c @@ -28,6 +28,21 @@ void node_free(struct node* self) vec_free(&self->children); } +struct node* node_new_clone(struct node* self) +{ + assert(self); + struct node* clone = malloc(sizeof(struct node)); + node_init(clone, self->type, self->value, self->line); + clone->parent = self->parent; + + for (size_t i=0; ichildren.size; i++) + { + node_add_child(clone, node_new_clone(self->children.data[i])); + } + + return clone; +} + struct node* node_add_new_child(struct node* self, enum NodeType type, char const* value) { @@ -156,3 +171,21 @@ size_t node_block_index(struct node* self) return node_block_index(self->parent); } + +void node_try_find_all(struct node* self, + enum NodeType type, + struct vec* res) +{ + assert(self); + + if (self->type == type) + { + vec_push(res, self); + } + + for (size_t i=0; ichildren.size; i++) + { + struct node* child = self->children.data[i]; + node_try_find_all(child, type, res); + } +} diff --git a/lang/src/node.h b/lang/src/node.h index aceba1d..30f9dca 100644 --- a/lang/src/node.h +++ b/lang/src/node.h @@ -12,11 +12,13 @@ G(NODE_EQ), G(NODE_NE), G(NODE_INT), G(NODE_FLOAT), \ G(NODE_STRING), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \ G(NODE_MOD), G(NODE_POW), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ - G(NODE_GE), G(NODE_COLON), G(NODE_ASSIGN), G(NODE_IDENT), \ + G(NODE_GE), G(NODE_COLON), G(NODE_ASSIGN), G(NODE_IDENT), \ G(NODE_TYPE), G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_VAR), \ G(NODE_OBRACE), G(NODE_CBRACE), G(NODE_BLOCK), \ G(NODE_IF), G(NODE_ELSE), G(NODE_COND), G(NODE_WHILE), G(NODE_FOR), \ - G(NODE_CONTINUE), G(NODE_BREAK) + G(NODE_CONTINUE), G(NODE_BREAK), G(NODE_RARROW), G(NODE_FUN), \ + G(NODE_RETURN), G(NODE_PARAMS), G(NODE_COMMA), G(NODE_CALL), \ + G(NODE_ARGS) GUX_ENUM_H(NodeType, NODE_TYPE); @@ -33,6 +35,8 @@ void node_init(struct node* self, enum NodeType type, char const* value, int line); void node_free(struct node* self); +struct node* node_new_clone(struct node* self); + struct node* node_add_new_child(struct node* self, enum NodeType type, char const* value); @@ -46,4 +50,8 @@ size_t node_parent_index(struct node* self); size_t node_block_index(struct node* self); +void node_try_find_all(struct node* self, + enum NodeType type, + struct vec* res); + #endif diff --git a/bc/src/opcodes.c b/lang/src/opcodes.c similarity index 100% rename from bc/src/opcodes.c rename to lang/src/opcodes.c diff --git a/bc/src/opcodes.h b/lang/src/opcodes.h similarity index 90% rename from bc/src/opcodes.h rename to lang/src/opcodes.h index 2f90342..6ce5b81 100644 --- a/bc/src/opcodes.h +++ b/lang/src/opcodes.h @@ -5,8 +5,8 @@ G(OP_PUSH), G(OP_POP), G(OP_BRF), G(OP_BR), G(OP_HALT), \ G(OP_BRT), G(OP_NOP), G(OP_NOT), G(OP_EQ), \ G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), G(OP_MOD), G(OP_POW), \ - G(OP_LT), G(OP_LE), G(OP_GT),G(OP_GE), G(OP_LOAD), G(OP_STORE),\ - G(OP_SWAP) + G(OP_LT), G(OP_LE), G(OP_GT),G(OP_GE), G(OP_LOAD), G(OP_STORE), \ + G(OP_SWAP), G(OP_CALL), G(OP_RET) #include diff --git a/lang/src/parser.c b/lang/src/parser.c index 5c1bbb5..66f49f3 100644 --- a/lang/src/parser.c +++ b/lang/src/parser.c @@ -132,11 +132,26 @@ int parser_start_bexpr(struct parser* self) assert(self); struct node* tok = self->tokens.data[self->cursor]; + if (parser_type_is(self, NODE_FUN, 0) + && parser_type_is(self, NODE_IDENT, 1)) + { + return 1; + } + return tok->type == NODE_OBRACE || tok->type == NODE_IF || tok->type == NODE_WHILE; } +int parser_start_type(struct parser* self) +{ + assert(self); + struct node* tok = self->tokens.data[self->cursor]; + + return tok->type == NODE_OPAR + || tok->type == NODE_TYPE; +} + struct node* parser_try_new_root(struct parser* self, char const* source) { assert(self); @@ -217,6 +232,24 @@ struct node* parser_try_new_lexpr(struct parser* self) { assert(self); + if (parser_type_is(self, NODE_FUN, 0)) + { + return parser_try_new_fun(self); + } + + if (parser_try_consume(self, NODE_RETURN) == 0) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_RETURN, "", parser_current_line(self)); + + if (!parser_type_is(self, NODE_SEMICOLON, 0)) + { + node_add_child(node, parser_try_new_expr(self)); + } + + return node; + } + if (parser_try_consume(self, NODE_BREAK) == 0) { struct node* node = malloc(sizeof(struct node)); @@ -279,7 +312,12 @@ struct node* parser_try_new_bexpr(struct parser* self) { assert(self); - if (parser_type_is(self, NODE_IF, 0)) + if (parser_type_is(self, NODE_FUN, 0) + && parser_type_is(self, NODE_IDENT, 1)) + { + return parser_try_new_fundecl(self); + } + else if (parser_type_is(self, NODE_IF, 0)) { return parser_try_new_if(self); } @@ -331,9 +369,149 @@ struct node* parser_try_new_while(struct parser* self) return node; } +struct node* parser_try_new_fun(struct parser* self) +{ + assert(self); + + if (parser_try_consume(self, NODE_FUN) != 0) + { + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_FUN, "", parser_current_line(self)); + + node_add_child(node, parser_try_new_params(self)); + + struct node* type = NULL; + + if (parser_try_consume(self, NODE_RARROW) == 0) + { + type = parser_try_new_type(self); + } + else + { + type = malloc(sizeof(struct node)); + node_init(type, NODE_TYPE, "void", parser_current_line(self)); + } + + self->cursor++; //type + + node_add_child(node, type); + node_add_child(node, parser_try_new_block(self)); + + return node; +} + +struct node* parser_try_new_params(struct parser* self) +{ + assert(self); + + if (parser_try_consume(self, NODE_OPAR) != 0) + { + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_PARAMS, "", parser_current_line(self)); + + if (parser_try_consume(self, NODE_CPAR) == 0) + { + return node; + } + + struct vec types; + vec_init(&types, 1); + + while (1) + { + if (!parser_type_is(self, NODE_IDENT, 0)) + { + vec_free_elements(&types); + vec_free(&types); + node_free(node); + free(node); + return NULL; + } + + // ident + struct node* current = self->tokens.data[self->cursor]; + struct node* ident = malloc(sizeof(struct node)); + node_init(ident, NODE_IDENT, current->value, + parser_current_line(self)); + node_add_child(node, ident); + self->cursor++; // ident + + if (parser_try_consume(self, NODE_COLON) == 0) + { + // type + struct node* type = parser_try_new_type(self); + node_add_child(node, type); + + for (size_t i=0; ichildren.data[idx]); + free(node->children.data[idx]); + + node->children.data[idx] = node_new_clone(type); + } + + vec_free(&types); + vec_init(&types, 1); + } + else + { + struct node* type = malloc(sizeof(struct node)); + node_init(type, NODE_TYPE, "", parser_current_line(self)); + size_t idx = node->children.size; + node_add_child(node, type); + size_t* t = malloc(sizeof(size_t)); + *t = idx; + vec_push(&types, t); + } + + if (parser_type_is(self, NODE_CPAR, 0)) + { + break; + } + + if (parser_try_consume(self, NODE_COMMA) != 0) + { + break; + } + } + + if (types.size > 0) + { + snprintf(self->error_msg, GUX_STR_SIZE, + "missing type from function parameters"); + self->error_line = node->line; + + vec_free_elements(&types); + vec_free(&types); + node_free(node); + free(node); + return NULL; + } + + vec_free_elements(&types); + vec_free(&types); + + if (parser_try_consume(self, NODE_CPAR) != 0) + { + node_free(node); + free(node); + return NULL; + } + + return node; +} + struct node* parser_try_new_block(struct parser* self) { - parser_try_consume(self, NODE_OBRACE); struct node* node = malloc(sizeof(struct node)); node_init(node, NODE_BLOCK, "", parser_current_line(self)); @@ -387,12 +565,13 @@ struct node* parser_try_new_vardecl(struct parser* self) if (parser_type_is(self, NODE_TYPE, 0)) { - current = self->tokens.data[self->cursor]; + /*current = self->tokens.data[self->cursor]; struct node* ty = malloc(sizeof(struct node)); node_init(ty, NODE_TYPE, current->value, parser_current_line(self)); node_add_child(node, ty); - self->cursor++; + self->cursor++;*/ + node_add_child(node, parser_try_new_type(self)); } if (parser_try_consume(self, NODE_ASSIGN) != 0) @@ -424,14 +603,9 @@ struct node* parser_try_new_constdecl(struct parser* self) return NULL; } - if (parser_type_is(self, NODE_TYPE, 0)) + if (parser_start_type(self)) { - current = self->tokens.data[self->cursor]; - struct node* ty = malloc(sizeof(struct node)); - node_init(ty, NODE_TYPE, current->value, parser_current_line(self)); - - node_add_child(node, ty); - self->cursor++; + node_add_child(node, parser_try_new_type(self)); } if (parser_try_consume(self, NODE_ASSIGN) != 0) @@ -448,6 +622,57 @@ struct node* parser_try_new_constdecl(struct parser* self) return node; } +struct node* parser_try_new_fundecl(struct parser* self) +{ + assert(self); + + if (parser_try_consume(self, NODE_FUN) != 0) + { + return NULL; + } + + size_t ident_idx = self->cursor; + self->cursor++; // ident + + struct node* node_fun = malloc(sizeof(struct node)); + node_init(node_fun, NODE_FUN, "", parser_current_line(self)); + + struct node* params = parser_try_new_params(self); + + if (params == NULL) + { + node_free(node_fun); + free(node_fun); + return NULL; + } + + node_add_child(node_fun, params); + + struct node* type = NULL; + + if (parser_try_consume(self, NODE_RARROW) == 0) + { + type = parser_try_new_type(self); + } + else + { + type = malloc(sizeof(struct node)); + node_init(type, NODE_TYPE, "void", parser_current_line(self)); + } + + self->cursor++; //type + + node_add_child(node_fun, type); + node_add_child(node_fun, parser_try_new_block(self)); + + struct node* node = malloc(sizeof(struct node)); + struct node* ident = self->tokens.data[ident_idx]; + node_init(node, NODE_CONSTDECL, ident->value, parser_current_line(self)); + node_add_child(node, node_fun); + + return node; +} + struct node* parser_try_new_or(struct parser* self) { struct node* lhs = parser_try_new_and(self); @@ -608,7 +833,68 @@ struct node* parser_try_new_not(struct parser* self) return node; } - return parser_try_new_literal(self); + return parser_try_new_call(self); +} + +struct node* parser_try_new_call(struct parser* self) +{ + assert(self); + + struct node* lhs = parser_try_new_literal(self); + + if (parser_type_is(self, NODE_OPAR, 0)) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_CALL, "", parser_current_line(self)); + + node_add_child(node, lhs); + + while (parser_type_is(self, NODE_OPAR, 0)) + { + struct node* args = parser_try_new_args(self); + node_add_child(node, args); + } + + lhs = node; + } + + return lhs; +} + +struct node* parser_try_new_args(struct parser* self) +{ + assert(self); + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_ARGS, "", parser_current_line(self)); + + if (parser_try_consume(self, NODE_OPAR) != 0) + { + node_free(node); + free(node); + return NULL; + } + + if (parser_try_consume(self, NODE_CPAR) == 0) + { + return node; + } + + node_add_child(node, parser_try_new_expr(self)); + + while (parser_try_consume(self, NODE_COMMA) == 0) + { + node_add_child(node, parser_try_new_expr(self)); + } + + if (parser_try_consume(self, NODE_CPAR) != 0) + { + node_free(node); + free(node); + return NULL; + } + + return node; } struct node* parser_try_new_literal(struct parser* self) @@ -661,3 +947,48 @@ struct node* parser_try_new_builtin(struct parser* self) return NULL; } + +struct node* parser_try_new_type(struct parser* self) +{ + assert(self); + + if (parser_type_is(self, NODE_OPAR, 0)) + { + self->cursor++; // opar + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_TYPE, "fun", parser_current_line(self)); + + while (!parser_type_is(self, NODE_RARROW, 0)) + { + node_add_child(node, parser_try_new_type(self)); + } + + struct node* rarrow = malloc(sizeof(struct node)); + node_init(rarrow, NODE_RARROW, "", parser_current_line(self)); + node_add_child(node, rarrow); + self->cursor++; // rarrow + + while (!parser_type_is(self, NODE_CPAR, 0)) + { + node_add_child(node, parser_try_new_type(self)); + } + + if (parser_try_consume(self, NODE_CPAR) != 0) + { + node_free(node); + free(node); + return NULL; + } + + return node; + } + else + { + struct node* node = malloc(sizeof(struct node)); + struct node* current = self->tokens.data[self->cursor]; + + node_init(node, NODE_TYPE, current->value, parser_current_line(self)); + self->cursor++; + return node; + } +} diff --git a/lang/src/parser.h b/lang/src/parser.h index a7e9f48..cb58bd8 100644 --- a/lang/src/parser.h +++ b/lang/src/parser.h @@ -25,6 +25,7 @@ int parser_try_consume(struct parser* self, enum NodeType type); int parser_ensure(struct parser* self, enum NodeType type); int parser_start_bexpr(struct parser* self); +int parser_start_type(struct parser* self); struct node* parser_try_new_root(struct parser* self, char const* source); struct node* parser_try_new_instr(struct parser* self); @@ -33,10 +34,13 @@ struct node* parser_try_new_lexpr(struct parser* self); struct node* parser_try_new_bexpr(struct parser* self); struct node* parser_try_new_if(struct parser* self); struct node* parser_try_new_while(struct parser* self); +struct node* parser_try_new_fun(struct parser* self); +struct node* parser_try_new_params(struct parser* self); struct node* parser_try_new_block(struct parser* self); struct node* parser_try_new_assign(struct parser* self); struct node* parser_try_new_vardecl(struct parser* self); struct node* parser_try_new_constdecl(struct parser* self); +struct node* parser_try_new_fundecl(struct parser* self); struct node* parser_try_new_or(struct parser* self); struct node* parser_try_new_and(struct parser* self); struct node* parser_try_new_eqne(struct parser* self); @@ -45,7 +49,10 @@ struct node* parser_try_new_term(struct parser* self); struct node* parser_try_new_factor(struct parser* self); struct node* parser_try_new_pow(struct parser* self); struct node* parser_try_new_not(struct parser* self); +struct node* parser_try_new_call(struct parser* self); +struct node* parser_try_new_args(struct parser* self); struct node* parser_try_new_literal(struct parser* self); struct node* parser_try_new_builtin(struct parser* self); +struct node* parser_try_new_type(struct parser* self); #endif diff --git a/bc/src/program.c b/lang/src/program.c similarity index 72% rename from bc/src/program.c rename to lang/src/program.c index f751e50..557bdc7 100644 --- a/bc/src/program.c +++ b/lang/src/program.c @@ -24,6 +24,27 @@ void program_free(struct program* self) vec_free(&self->instructions); } +void program_copy(struct program* self, struct program* dest) +{ + assert(self); + assert(dest); + + program_free(dest); + program_init(dest); + + for (size_t i=0; iinstructions.size; i++) + { + struct instruction* instr = self->instructions.data[i]; + program_push_instr(dest, instr->opcode, instr->param); + } + + for (size_t i=0; iconstant_pool.size; i++) + { + struct value* constant = value_new_clone(self->constant_pool.data[i]); + program_push_constant(dest, constant); + } +} + size_t program_push_instr(struct program* self, enum Opcodes op, int param) { assert(self); @@ -55,7 +76,7 @@ size_t program_str(struct program* self, char* buffer, size_t size) for (size_t i=0; iconstant_pool.size; i++) { - sz += snprintf(buffer + sz, size - sz, "\t%ld: ", i); + sz += snprintf(buffer + sz, size - sz, "\t%zu: ", i); sz += value_str(self->constant_pool.data[i], buffer + sz, size - sz); sz += snprintf(buffer + sz, size - sz, "\n"); } @@ -69,14 +90,14 @@ size_t program_str(struct program* self, char* buffer, size_t size) if (instr->param != NO_PARAM) { - sz += snprintf(buffer + sz, size - sz, "\t%ld: %s %d\n", + sz += snprintf(buffer + sz, size - sz, "\t%zu: %s %d\n", i, OpcodesStr[instr->opcode] + strlen("OP_"), instr->param); } else { - sz += snprintf(buffer + sz, size - sz, "\t%ld: %s\n", + sz += snprintf(buffer + sz, size - sz, "\t%zu: %s\n", i, OpcodesStr[instr->opcode] + strlen("OP_")); } diff --git a/bc/src/program.h b/lang/src/program.h similarity index 87% rename from bc/src/program.h rename to lang/src/program.h index da71763..12099d6 100644 --- a/bc/src/program.h +++ b/lang/src/program.h @@ -3,7 +3,7 @@ #include #include -#include +#include "value.h" #include "opcodes.h" #define NO_PARAM -1 @@ -21,6 +21,8 @@ struct program { void program_init(struct program* self); void program_free(struct program* self); +void program_copy(struct program* self, struct program* dest); + size_t program_push_instr(struct program* self, enum Opcodes op, int param); size_t program_push_constant(struct program* self, struct value* value); diff --git a/lang/src/sym_table.c b/lang/src/sym_table.c new file mode 100644 index 0000000..1a575e0 --- /dev/null +++ b/lang/src/sym_table.c @@ -0,0 +1,156 @@ +#include "sym_table.h" +#include "vec.h" + +void sym_table_init(struct sym_table* self) +{ + assert(self); + self->root = NULL; + + sym_table_push_table(self); + self->addr_counter = 0; +} + +void sym_table_free(struct sym_table* self) +{ + assert(self); + + while (self->root) + { + sym_table_pop_table(self); + } +} + +void sym_table_push_table(struct sym_table* self) +{ + assert(self); + + struct table* next; + next = malloc(sizeof(struct table)); + next->prev = NULL; + vec_init(&next->syms, 1); + + if (self->root == NULL) + { + self->root = next; + } + else + { + next->prev = self->root; + self->root = next; + } +} + +void sym_table_pop_table(struct sym_table* self) +{ + assert(self); + + if (self->root == NULL) + { + return; + } + + struct table* to_rm = self->root; + struct table* next = self->root->prev; + + for (size_t i=0; isyms.size; i++) + { + struct sym* sym = to_rm->syms.data[i]; + free(sym->name); + type_free(sym->type); + free(sym->type); + } + + vec_free_elements(&to_rm->syms); + vec_free(&to_rm->syms); + free(to_rm); + self->root = next; +} + +struct sym* sym_table_try_by_name(struct sym_table* self, char* const name) +{ + assert(self); + + struct table* table = self->root; + + while (table) + { + for (size_t i=0; isyms.size; i++) + { + struct sym* sym = table->syms.data[i]; + + if (strcmp(sym->name, name) == 0) + { + return sym; + } + } + + table = table->prev; + } + + return NULL; +} + +int sym_table_declare_const(struct sym_table* self, + char* const name, + struct type* new_type) +{ + return sym_table_declare(self, name, new_type, 0); +} + +int sym_table_declare_var(struct sym_table* self, + char* const name, + struct type* new_type) +{ + return sym_table_declare(self, name, new_type, 1); +} + +int sym_table_declare(struct sym_table* self, + char* const name, + struct type* new_type, + int is_var) +{ + assert(self); + assert(strcmp(name, "") != 0); + + struct sym* s = sym_table_try_by_name(self, name); + + if (s && s->parent == self->root) + { + return 1; + } + + struct table* table = self->root; + struct sym* sym = malloc(sizeof(struct sym)); + sym->name = strdup(name); + sym->type = new_type; + sym->parent = self->root; + sym->is_var = is_var; + sym->addr = self->addr_counter; + vec_push(&table->syms, sym); + + self->addr_counter++; + + return 0; +} + +size_t sym_table_str(struct sym_table* self, char* buffer, size_t size) +{ + assert(self); + assert(buffer); + + size_t sz = 0; + + sz += snprintf(buffer + sz, size - sz, "SYMBOLS\n"); + + struct table* table = self->root; + + for (size_t i=0; isyms.size; i++) + { + struct sym* sym = table->syms.data[i]; + sz += snprintf(buffer + sz, size - sz, + "[%s::%p] -> %d\n", + sym->name, sym->parent, sym->addr); + } + + return sz; +} diff --git a/lang/src/sym_table.h b/lang/src/sym_table.h new file mode 100644 index 0000000..fffc140 --- /dev/null +++ b/lang/src/sym_table.h @@ -0,0 +1,48 @@ +#ifndef SYM_TABLE_H +#define SYM_TABLE_H + +#include +#include +#include + +struct sym { + struct table* parent; + char* name; + int is_var; + int addr; + struct type* type; +}; + +struct table { + struct table* prev; + struct vec syms; +}; + +struct sym_table { + struct table* root; + int addr_counter; +}; + +void sym_table_init(struct sym_table* self); +void sym_table_free(struct sym_table* self); + +void sym_table_push_table(struct sym_table* self); +void sym_table_pop_table(struct sym_table* self); + +struct sym* sym_table_try_by_name(struct sym_table* self, char* const name); +int sym_table_declare_const(struct sym_table* self, + char* const name, + struct type* new_type); + +int sym_table_declare_var(struct sym_table* self, + char* const name, + struct type* new_type); + +int sym_table_declare(struct sym_table* self, + char* const name, + struct type* new_type, + int is_var); + +size_t sym_table_str(struct sym_table* self, char* buffer, size_t size); + +#endif diff --git a/lang/src/syms.c b/lang/src/syms.c deleted file mode 100644 index 052a3ed..0000000 --- a/lang/src/syms.c +++ /dev/null @@ -1,191 +0,0 @@ -#include "syms.h" -#include "commons.h" -#include "type.h" -#include "node.h" - -void syms_init(struct syms* self) -{ - assert(self); - vec_init(&self->types, 1); - vec_init(&self->entries, 1); - self->addr_counter = 0; - size_t k = 0; - syms_add_type(self, k++, TYPE_INT, 0); - syms_add_type(self, k++, TYPE_FLOAT, 0); - syms_add_type(self, k++, TYPE_BOOL, 0); - syms_add_type(self, k++, TYPE_STRING, 0); -} - -void syms_free(struct syms* self) -{ - assert(self); - - for (size_t i=0; itypes.size; i++) - { - struct syms_type* st = self->types.data[i]; - type_free(&st->type); - } - - vec_free_elements(&self->types); - vec_free(&self->types); - - vec_free_elements(&self->entries); - vec_free(&self->entries); -} - -void syms_add_type(struct syms* self, size_t id, enum TypeKind type, - int count, ...) -{ - va_list va; - va_start(va, count); - - struct syms_type* ty = malloc(sizeof(struct syms_type)); - ty->id = id; - type_init(&ty->type, type); - - for (int i=0; itype, va_arg(va, enum TypeKind)); - } - - vec_push(&self->types, ty); - va_end(va); -} - -size_t syms_type_id(struct syms* self, enum TypeKind type) -{ - assert(self); - - for (size_t i=0; itypes.size; i++) - { - struct syms_type* ty = self->types.data[i]; - - if (ty->type.kind == type) - { - return ty->id; - } - } - - fprintf(stderr, "type not found\n"); - abort(); -} - -struct type* syms_try_type(struct syms* self, size_t id) -{ - for (size_t i=0; itypes.size; i++) - { - struct syms_type* ty = self->types.data[i]; - - if (ty->id == id) - { - return &ty->type; - } - } - - return NULL; -} - -void syms_declare(struct syms* self, char* name, - size_t type, int is_var, - struct node* node) -{ - assert(self); - struct syms_entry* entry = malloc(sizeof(struct syms_entry)); - size_t sz = strlen(name); - - if (sz > GUX_STR_SIZE) - { - sz = GUX_STR_SIZE; - } - - memset(entry->name, 0, GUX_STR_SIZE); - memcpy(entry->name, name, sz); - - entry->type = type; - entry->addr = self->addr_counter++; - entry->is_var = is_var; - entry->node = node; - - entry->depth = 0; - struct node* itr = entry->node; - - while (itr != NULL) - { - if (itr->type == NODE_BLOCK) - { - entry->depth++; - } - - itr = itr->parent; - } - - vec_push(&self->entries, entry); -} - -struct syms_entry* syms_try_get(struct syms* self, char* name, - struct node* node) -{ - assert(self); - - struct syms_entry* res = NULL; - int max_depth = -1; - int depth = node ? node_depth(node) : 0; - - for (size_t i=0; ientries.size; i++) - { - struct syms_entry* entry = self->entries.data[i]; - - if (strcmp(entry->name, name) == 0 && entry->depth > max_depth - && entry->depth <= depth) - { - if (node && entry->node && entry->depth == depth) - { - struct node* node_block = - node_try_find_parent(node, NODE_BLOCK); - struct node* entry_block = - node_try_find_parent(entry->node, NODE_BLOCK); - - if (node_block == entry_block) - { - int node_index = node_block_index(node); - int entry_index = node_block_index(entry->node); - - if (entry_index > node_index) - { - continue; - } - } - else - { - continue; - } - } - - max_depth = entry->depth; - res = entry; - } - } - - return res; -} - -size_t syms_str(struct syms* self, char* buffer, size_t size) -{ - assert(self); - assert(buffer); - - size_t sz = 0; - - for (size_t i=0; ientries.size; i++) - { - struct syms_entry* entry = self->entries.data[i]; - struct type* type = syms_try_type(self, entry->type); - sz += snprintf(buffer + sz, size - sz, "[%zu] ", entry->addr); - sz += type_str(type, buffer + sz, size - sz); - sz += snprintf(buffer + sz, size - sz, " %s, ", entry->name); - sz += snprintf(buffer + sz, size - sz, " depth=%d, ", entry->depth); - sz += snprintf(buffer + sz, size - sz, " block=%p\n", entry->node); - } - - return sz; -} diff --git a/lang/src/syms.h b/lang/src/syms.h deleted file mode 100644 index 2f679f2..0000000 --- a/lang/src/syms.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef SYMS_H -#define SYMS_H - -#include -#include -#include - -#define SYM_IS_VAR 1 -#define SYM_IS_CONST 0 - -struct syms_type { - size_t id; - struct type type; -}; - -struct syms_entry { - char name[GUX_STR_SIZE]; - size_t type; - size_t addr; - int is_var; - int depth; - struct node* node; -}; - -struct syms { - struct vec types; - struct vec entries; - size_t addr_counter; -}; - -void syms_init(struct syms* self); -void syms_free(struct syms* self); - -void syms_add_type(struct syms* self, size_t id, enum TypeKind type, - int count, ...); - -size_t syms_type_id(struct syms* self, enum TypeKind type); -struct type* syms_try_type(struct syms* self, size_t id); - -void syms_declare(struct syms* self, char* name, - size_t type, int is_var, - struct node* node); - -struct syms_entry* syms_try_get(struct syms* self, char* name, - struct node* node); - -size_t syms_str(struct syms* self, char* buffer, size_t size); - -#endif diff --git a/lang/src/type.c b/lang/src/type.c index 822ab84..b4c478b 100644 --- a/lang/src/type.c +++ b/lang/src/type.c @@ -11,6 +11,96 @@ void type_init(struct type* self, enum TypeKind kind) vec_init(&self->subtypes, 1); } +void type_init_from_node(struct type* self, struct node* node) +{ + assert(self); + + vec_init(&self->subtypes, 1); + + if (node->type == NODE_TYPE && strcmp(node->value, "fun") == 0) + { + self->kind = TYPE_FUN; + int input = 1; + + struct vec inputs; + vec_init(&inputs, 1); + + struct type* output; + + for (size_t i=0; ichildren.size; i++) + { + struct node* comp = node->children.data[i]; + + if (comp->type == NODE_RARROW) + { + input = 0; + } + else if (input) + { + struct type* sub = malloc(sizeof(struct type)); + type_init_from_node(sub, comp); + vec_push(&inputs, sub); + } + else + { + output = malloc(sizeof(struct type)); + type_init_from_node(output, comp); + } + } + + vec_push(&self->subtypes, output); + + for (size_t i=0; isubtypes, inputs.data[i]); + } + + vec_free(&inputs); + } + else if (node->type == NODE_FUN) + { + self->kind = TYPE_FUN; + + struct node* params = node->children.data[0]; + struct node* ret = node->children.data[1]; + + struct type* ret_ty = malloc(sizeof(struct type)); + type_init_from_node(ret_ty, ret); + vec_push(&self->subtypes, ret_ty); + + for (size_t i=0; ichildren.size; i++) + { + struct node* child = params->children.data[i]; + + if (child->type == NODE_TYPE) + { + struct type* param_ty = malloc(sizeof(struct type)); + type_init_from_node(param_ty, child); + vec_push(&self->subtypes, param_ty); + } + } + } + else + { + self->kind = type_kind_from_str(self, node->value, NULL); + } +} + +void type_copy(struct type* self, struct type* dest) +{ + assert(self); + assert(dest); + + type_free(dest); + type_init(dest, self->kind); + + for (size_t i=0; isubtypes.size; i++) + { + type_add_subtype(dest, TYPE_VOID); + type_copy(self->subtypes.data[i], dest->subtypes.data[i]); + } +} + void type_free(struct type* self) { for (size_t i=0; isubtypes.size; i++) @@ -108,3 +198,38 @@ int type_is(struct type* self, char const* repr) int result = strcmp(repr, buffer) == 0; return result; } + +enum TypeKind type_kind_from_str(struct type* self, char const* str, int* err) +{ + assert(self); + + if (err) { *err = 0; } + + if (strcmp(str, "int") == 0) // natives + { + return TYPE_INT; + } + else if (strcmp(str, "float") == 0) + { + return TYPE_FLOAT; + } + else if (strcmp(str, "bool") == 0) + { + return TYPE_BOOL; + } + else if (strcmp(str, "string") == 0) + { + return TYPE_STRING; + } + else if (strcmp(str, "void") == 0) + { + return TYPE_VOID; + } + + if (err) + { + *err = 1; + } + + return TYPE_VOID; +} diff --git a/lang/src/type.h b/lang/src/type.h index e30863c..f0d548b 100644 --- a/lang/src/type.h +++ b/lang/src/type.h @@ -3,10 +3,11 @@ #include #include +#include "node.h" #define TYPE_KIND(G) \ G(TYPE_VOID), G(TYPE_BOOL), G(TYPE_INT), \ - G(TYPE_FLOAT), G(TYPE_STRING) + G(TYPE_FLOAT), G(TYPE_STRING), G(TYPE_FUN) GUX_ENUM_H(TypeKind, TYPE_KIND); @@ -16,8 +17,10 @@ struct type { }; void type_init(struct type* self, enum TypeKind kind); +void type_init_from_node(struct type* self, struct node* node); void type_free(struct type* self); +void type_copy(struct type* self, struct type* dest); void type_clear(struct type* self); int type_equals(struct type* self, struct type* rhs); @@ -25,4 +28,6 @@ void type_add_subtype(struct type* self, enum TypeKind kind); size_t type_str(struct type* self, char* buffer, size_t size); int type_is(struct type* self, char const* repr); +enum TypeKind type_kind_from_str(struct type* self, char const* str, int* err); + #endif diff --git a/lang/src/type_checker.c b/lang/src/type_checker.c index 92fa3f1..8f0d7c3 100644 --- a/lang/src/type_checker.c +++ b/lang/src/type_checker.c @@ -1,22 +1,26 @@ #include "type_checker.h" #include "commons.h" #include "node.h" +#include "sym_table.h" #include "type.h" #include "type_resolver.h" -#include "syms.h" +#include "fun.h" -void type_checker_init(struct type_checker* self, struct syms* syms) +void type_checker_init(struct type_checker* self) { assert(self); - self->syms = syms; - type_resolver_init(&self->resolver, self->syms); + sym_table_init(&self->sym_table); + type_resolver_init(&self->resolver, &self->sym_table); memset(self->error_msg, 0, GUX_STR_SIZE); + vec_init(&self->fun_stack, 1); } void type_checker_free(struct type_checker* self) { assert(self); type_resolver_free(&self->resolver); + sym_table_free(&self->sym_table); + vec_free(&self->fun_stack); } int type_checker_check(struct type_checker* self, @@ -27,6 +31,167 @@ int type_checker_check(struct type_checker* self, switch (node->type) { + case NODE_RETURN: { + struct node* ret = self->fun_stack.data[self->fun_stack.size - 1]; + struct type fun_ty; + type_init_from_node(&fun_ty, ret); + + struct type ret_ty; + type_init(&ret_ty, TYPE_VOID); + + if (type_resolver_resolve(&self->resolver, node, &ret_ty) != 0) + { + return 1; + } + + if (!type_equals(&fun_ty, &ret_ty)) + { + type_checker_error_msg(self, node, &fun_ty, &ret_ty); + + type_free(&ret_ty); + type_free(&fun_ty); + + return 1; + } + + type_free(&ret_ty); + type_free(&fun_ty); + + } return 0; + + case NODE_BLOCK: { + sym_table_push_table(&self->sym_table); + for (size_t i=0; ichildren.size; i++) + { + struct node* child = node->children.data[i]; + + if ( type_checker_check(self, child) != 0 ) + { + return 1; + } + } + + sym_table_pop_table(&self->sym_table); + } return 0; + + case NODE_ARGS: + case NODE_PARAMS: { + } return 0; + + case NODE_CALL: { + struct node* target = node->children.data[0]; + struct node* args = node->children.data[1]; + + struct sym* entry = sym_table_try_by_name(&self->sym_table, + target->value); + + if (!entry) + { + self->error_line = node->line; + snprintf(self->error_msg, GUX_STR_SIZE, + "undefined function <%s>", target->value); + return 1; + } + + struct type* ty = entry->type; + assert(ty); + + if (ty->subtypes.size - 1 != args->children.size) + { + self->error_line = node->line; + snprintf(self->error_msg, GUX_STR_SIZE, + "wrong arity"); + return 1; + } + + } return 0; + + case NODE_FUN: { + sym_table_push_table(&self->sym_table); + struct node* params = node->children.data[0]; + struct node* ret_type = node->children.data[1]; + struct node* body = node->children.data[2]; + + { + struct vec names; + vec_init(&names, 1); + + for (size_t i=0; ichildren.size; i++) + { + struct node* param = params->children.data[i]; + + if (param->type == NODE_TYPE) + { + for (size_t j=0; jsym_table, + n->value, + ty); + } + + vec_free(&names); + vec_init(&names, 1); + } + else + { + vec_push(&names, param); + } + } + + // check body + vec_push(&self->fun_stack, ret_type); + + if (type_checker_check(self, body) != 0) + { + vec_free(&names); + + return 1; + } + + struct type ret_ty; + type_init_from_node(&ret_ty, ret_type); + + struct vec ret_instrs; + vec_init(&ret_instrs, 1); + node_try_find_all(body, NODE_RETURN, &ret_instrs); + + if (ret_ty.kind != TYPE_VOID && ret_instrs.size == 0) + { + snprintf(self->error_msg, GUX_STR_SIZE, + "missing return statement"); + self->error_line = ret_type->line; + type_free(&ret_ty); + vec_free(&ret_instrs); + vec_free(&names); + + return 1; + } + + if (ret_ty.kind != TYPE_VOID && ret_instrs.size == 0) + { + snprintf(self->error_msg, GUX_STR_SIZE, + "missing return statement"); + self->error_line = ret_type->line; + type_free(&ret_ty); + vec_free(&ret_instrs); + vec_free(&names); + + return 1; + } + + vec_pop(&self->fun_stack); + type_free(&ret_ty); + vec_free(&ret_instrs); + vec_free(&names); + + sym_table_pop_table(&self->sym_table); + } + } return 0; + case NODE_WHILE: { struct type have; type_init(&have, TYPE_VOID); @@ -90,8 +255,8 @@ int type_checker_check(struct type_checker* self, struct node* ident = node->children.data[0]; struct node* expr = node->children.data[1]; - struct syms_entry* entry = syms_try_get(self->syms, ident->value, - node); + struct sym* entry = sym_table_try_by_name(&self->sym_table, + ident->value); if (!entry) { @@ -111,7 +276,7 @@ int type_checker_check(struct type_checker* self, return 1; } - struct type* var_type = syms_try_type(self->syms, entry->type); + struct type* var_type = entry->type; struct type expr_type; type_init(&expr_type, TYPE_VOID); @@ -130,11 +295,10 @@ int type_checker_check(struct type_checker* self, case NODE_VARDECL: case NODE_CONSTDECL:{ - struct syms_entry* entry = syms_try_get(self->syms, - node->value, - node); + struct sym* entry = sym_table_try_by_name(&self->sym_table, + node->value); - if (entry && entry->depth == node_depth(node)) + if (entry && entry->parent == self->sym_table.root) { snprintf(self->error_msg, GUX_STR_SIZE, "%s is already defined", node->value); @@ -143,83 +307,60 @@ int type_checker_check(struct type_checker* self, return 1; } - char* type_name = NULL; struct node* expr = NULL; int is_var = node->type == NODE_VARDECL; for (size_t i=0; ichildren.size; i++) { - type_checker_check(self, node->children.data[i]); + if (type_checker_check(self, node->children.data[i]) != 0) + { + return 1; + } } if (node->children.size == 1) { expr = node->children.data[0]; - struct type ty; - type_init(&ty, TYPE_VOID); - if (type_resolver_resolve(&self->resolver, expr, &ty) != 0) + struct type* ty = malloc(sizeof(struct type)); + type_init(ty, TYPE_VOID); + + if (type_resolver_resolve(&self->resolver, expr, ty) != 0) { - type_free(&ty); + type_free(ty); + free(ty); return 1; } - syms_declare(self->syms, node->value, - syms_type_id(self->syms, ty.kind), is_var, - node); + sym_table_declare(&self->sym_table, + node->value, + ty, is_var); - type_free(&ty); } else if (node->children.size == 2) { expr = node->children.data[1]; - type_name = ((struct node*) node->children.data[0])->value; - struct type ty; - - if (strcmp(type_name, "int") == 0) - { - type_init(&ty, TYPE_INT); - } - else if (strcmp(type_name, "float") == 0) - { - type_init(&ty, TYPE_FLOAT); - } - else if (strcmp(type_name, "bool") == 0) - { - type_init(&ty, TYPE_BOOL); - } - else if (strcmp(type_name, "string") == 0) - { - type_init(&ty, TYPE_STRING); - } - else - { - snprintf(self->error_msg, GUX_STR_SIZE, - "unknown type <%s>", - type_name); - - self->error_line = node->line; - - return 1; - } + struct type* ty = malloc(sizeof(struct type)); + type_init_from_node(ty, node->children.data[0]); struct type expr_ty; type_init(&expr_ty, TYPE_VOID); if (type_resolver_resolve(&self->resolver, expr, &expr_ty) != 0) { type_free(&expr_ty); - type_free(&ty); + type_free(ty); + free(ty); return 1; } - if (!type_equals(&ty, &expr_ty)) + if (!type_equals(ty, &expr_ty)) { char ty0[GUX_STR_SIZE / 3]; type_str(&expr_ty, ty0, GUX_STR_SIZE/3); char ty1[GUX_STR_SIZE / 3]; - type_str(&ty, ty1, GUX_STR_SIZE/3); + type_str(ty, ty1, GUX_STR_SIZE/3); snprintf(self->error_msg, GUX_STR_SIZE, "mismatch type: expected <%s>, got <%s>", @@ -228,16 +369,16 @@ int type_checker_check(struct type_checker* self, self->error_line = node->line; - type_free(&ty); + type_free(ty); + free(ty); type_free(&expr_ty); return 1; } - syms_declare(self->syms, node->value, - syms_type_id(self->syms, ty.kind), is_var, node); + sym_table_declare(&self->sym_table, node->value, + ty, is_var); - type_free(&ty); type_free(&expr_ty); } } return 0; diff --git a/lang/src/type_checker.h b/lang/src/type_checker.h index 701a733..d65cd1e 100644 --- a/lang/src/type_checker.h +++ b/lang/src/type_checker.h @@ -7,13 +7,14 @@ #include struct type_checker { - struct syms* syms; + struct sym_table sym_table; struct type_resolver resolver; char error_msg[GUX_STR_SIZE]; int error_line; + struct vec fun_stack; }; -void type_checker_init(struct type_checker* self, struct syms* syms); +void type_checker_init(struct type_checker* self); void type_checker_free(struct type_checker* self); int type_checker_check(struct type_checker* self, @@ -27,5 +28,4 @@ void type_checker_error_msg(struct type_checker* self, struct node* node, struct type* want, struct type* have); - #endif diff --git a/lang/src/type_resolver.c b/lang/src/type_resolver.c index 6d712af..7e7a621 100644 --- a/lang/src/type_resolver.c +++ b/lang/src/type_resolver.c @@ -1,12 +1,12 @@ #include "type_resolver.h" #include "node.h" -#include "syms.h" #include "type.h" -void type_resolver_init(struct type_resolver* self, struct syms* syms) +void type_resolver_init(struct type_resolver* self, + struct sym_table* sym_table) { assert(self); - self->syms = syms; + self->sym_table = sym_table; } void type_resolver_free(struct type_resolver* self) @@ -26,12 +26,42 @@ int type_resolver_resolve(struct type_resolver* self, switch (node->type) { + case NODE_RETURN: { + if (node->children.size == 0) + { + struct type ty; + type_init(&ty, TYPE_VOID); + type_copy(&ty, type); + type_free(&ty); + return 0; + } + + return type_resolver_resolve(self, node->children.data[0], type); + }; + + case NODE_CALL: { + struct type call_type; + type_init(&call_type, TYPE_VOID); + + if (type_resolver_resolve(self, node->children.data[0], &call_type) == 0) + { + type_copy(call_type.subtypes.data[0], type); + } + + type_free(&call_type); + } return 0; + + case NODE_FUN: { + struct type ty; + type_init_from_node(&ty, node); + type_copy(&ty, type); + type_free(&ty); + } return 0; case NODE_ASSIGN: { return type_resolver_resolve(self, node->children.data[1], type); } return 0; - case NODE_BLOCK: { size_t last = node->children.size - 1; return type_resolver_resolve(self, node->children.data[last], type); @@ -39,30 +69,46 @@ int type_resolver_resolve(struct type_resolver* self, case NODE_VARDECL: case NODE_CONSTDECL: { + struct type* ty = malloc(sizeof(struct type)); + type_init(ty, TYPE_VOID); + if (node->children.size == 1) { - type_resolver_resolve(self, node->children.data[0], type); + type_resolver_resolve(self, node->children.data[0], ty); } else { - type_resolver_resolve(self, node->children.data[1], type); + type_resolver_resolve(self, node->children.data[1], ty); } + type_copy(ty, type); + + sym_table_declare(self->sym_table, node->value, ty, + node->type == NODE_VARDECL); + } return 0; case NODE_IDENT: { - struct syms_entry* entry = syms_try_get(self->syms, - node->value, - node); + struct sym* entry = sym_table_try_by_name(self->sym_table, + node->value); + + if (strcmp(node->value, "this") == 0) + { + struct node* fun = node_try_find_parent(node, NODE_FUN); + struct type ty; + type_init_from_node(&ty, fun); + type_copy(&ty, type); + type_free(&ty); + return 0; + } if (!entry) { return 1; } - struct type* ty = syms_try_type(self->syms, entry->type); + type_copy(entry->type, type); - type->kind = ty->kind; } return 0; case NODE_EQ: diff --git a/lang/src/type_resolver.h b/lang/src/type_resolver.h index c50691e..9ee663e 100644 --- a/lang/src/type_resolver.h +++ b/lang/src/type_resolver.h @@ -4,12 +4,14 @@ #include #include #include +#include "sym_table.h" struct type_resolver { - struct syms* syms; + struct sym_table* sym_table; }; -void type_resolver_init(struct type_resolver* self, struct syms* syms); +void type_resolver_init(struct type_resolver* self, + struct sym_table* sym_table); void type_resolver_free(struct type_resolver* self); int type_resolver_resolve(struct type_resolver* self, diff --git a/lang/src/value.c b/lang/src/value.c index badea9b..f19c9fd 100644 --- a/lang/src/value.c +++ b/lang/src/value.c @@ -1,4 +1,6 @@ #include "value.h" +#include "type.h" +#include "fun.h" void value_init_bool(struct value* self, int value, int line) { @@ -37,16 +39,49 @@ void value_init_string(struct value* self, char* value, int line) self->line = line; } +void value_init_new_fun(struct value* self, struct fun* value, int line) +{ + assert(self); + assert(value->node); + type_init_from_node(&self->type, value->node); + self->data.fun = value; + self->line = line; +} + void value_free(struct value* self) { assert(self); + if (self->type.kind == TYPE_STRING) { free(self->data.s); } + else if (self->type.kind == TYPE_FUN) + { + fun_free(self->data.fun); + free(self->data.fun); + } + type_free(&self->type); } +struct value* value_new_clone(struct value* self) +{ + assert(self); + struct value* clone = malloc(sizeof(struct value)); + type_init(&clone->type, TYPE_VOID); + type_copy(&self->type, &clone->type); + + clone->data = self->data; + + if (clone->type.kind == TYPE_FUN) + { + clone->data.fun = fun_new_clone(self->data.fun); + } + + return clone; +} + size_t value_str(struct value* self, char* buffer, size_t size) { assert(self); @@ -54,6 +89,12 @@ size_t value_str(struct value* self, char* buffer, size_t size) size_t sz = 0; + if (self->type.kind == TYPE_FUN) + { + sz += snprintf(buffer + sz, size - sz, ""); + return sz; + } + if (self->type.subtypes.size == 0) { if (self->type.kind == TYPE_VOID) diff --git a/lang/src/value.h b/lang/src/value.h index c3e458e..7056ca8 100644 --- a/lang/src/value.h +++ b/lang/src/value.h @@ -12,6 +12,7 @@ union value_data { char* s; float f; int i; + struct fun* fun; }; struct value { @@ -24,9 +25,12 @@ void value_init_bool(struct value* self, int value, int line); void value_init_int(struct value* self, int value, int line); void value_init_float(struct value* self, float value, int line); void value_init_string(struct value* self, char* value, int line); +void value_init_new_fun(struct value* self, struct fun* value, int line); void value_free(struct value* self); +struct value* value_new_clone(struct value* self); + size_t value_str(struct value* self, char* buffer, size_t size); struct value* value_try_new_and(struct value* self, struct value* rhs); diff --git a/lang/tests/lexer.c b/lang/tests/lexer.c index c866073..9de7ac1 100644 --- a/lang/tests/lexer.c +++ b/lang/tests/lexer.c @@ -130,3 +130,7 @@ Test(lexer, flow_control) { test_lexer("if else cond while for break continue", 7, "IF", "ELSE", "COND", "WHILE", "FOR", "BREAK", "CONTINUE"); } + +Test(lexer, fun) { + test_lexer("-> fun return,", 4, "RARROW", "FUN", "RETURN", "COMMA"); +} diff --git a/lang/tests/parser.c b/lang/tests/parser.c index 87b3e50..39765e2 100644 --- a/lang/tests/parser.c +++ b/lang/tests/parser.c @@ -159,3 +159,46 @@ Test(parser, while_break_continue) { test_parser("ROOT(WHILE(BOOL[true],BLOCK(BREAK,CONTINUE)))", "while true { break; continue; }"); } + +Test(parser, fun) { + test_parser("ROOT(FUN(PARAMS(IDENT[x],TYPE[int],IDENT[y],TYPE[float])," + "TYPE[int],BLOCK(RETURN(IDENT[x]))))", + "fun (x: int, y: float) -> int { return x; };"); + + test_parser("ROOT(FUN(PARAMS(IDENT[x],TYPE[int],IDENT[y],TYPE[float])," + "TYPE[void],BLOCK(RETURN(INT[8]))))", + "fun (x: int, y: float) { return 8; };"); +} + +Test(parser, types) { + test_parser("ROOT(CONSTDECL[x](" + "TYPE[fun](TYPE[int],TYPE[int],RARROW,TYPE[float]),INT[0]))", + "x : (int int -> float) = 0;"); +} + +Test(parser, fun_call) { + test_parser("ROOT(CALL(IDENT[k],ARGS))", + "k();"); + + test_parser("ROOT(CALL(IDENT[f],ARGS(IDENT[x],IDENT[y])))", + "f(x, y);"); + + test_parser("ROOT(CALL(IDENT[f],ARGS(IDENT[x],IDENT[y]),ARGS(IDENT[z])))", + "f(x, y)(z);"); + + test_parser("ROOT(CALL(INT[72],ARGS(IDENT[a])))", + "72(a);"); +} + +Test(parser, fun_decl) { + test_parser("ROOT(CONSTDECL[hello](FUN(PARAMS(IDENT[n],TYPE[int])," + "TYPE[int],BLOCK(RETURN(IDENT[n])))))", + "fun hello (n: int) -> int { return n; }"); +} + +Test(parser, param_sugar) { + test_parser("ROOT(CONSTDECL[hello](FUN(PARAMS(IDENT[n],TYPE[int]," + "IDENT[m],TYPE[int],IDENT[k],TYPE[int])," + "TYPE[int],BLOCK(RETURN(IDENT[n])))))", + "fun hello (n, m, k: int) -> int { return n; }"); +} diff --git a/lang/tests/sym_table.c b/lang/tests/sym_table.c new file mode 100644 index 0000000..30fb658 --- /dev/null +++ b/lang/tests/sym_table.c @@ -0,0 +1,91 @@ +#include +#include +#include + +Test(sym_table, simple) { + struct sym_table table; + sym_table_init(&table); + + struct sym* sym = sym_table_try_by_name(&table, "hello"); + cr_assert_eq(sym, 0); + + struct type* ty = malloc(sizeof(struct type)); + type_init(ty, TYPE_INT); + + int err = sym_table_declare_const(&table, "hello", ty); + cr_assert_eq(err, 0); + + sym = sym_table_try_by_name(&table, "hello"); + cr_assert_neq(sym, 0); + cr_assert_str_eq("hello", sym->name); + + sym_table_free(&table); +} + +Test(sym_table, already_exists) { + struct sym_table table; + sym_table_init(&table); + + struct type* ty = malloc(sizeof(struct type)); + type_init(ty, TYPE_INT); + + int err = sym_table_declare_const(&table, "hello", ty); + cr_assert_eq(err, 0); + + err = sym_table_declare_const(&table, "hello", ty); + cr_assert_eq(err, 1); + + sym_table_free(&table); +} + +Test(sym_table, scope) { + struct sym_table table; + sym_table_init(&table); + + struct type* ty_int = malloc(sizeof(struct type)); + type_init(ty_int, TYPE_INT); + sym_table_declare_const(&table, "abc", ty_int); + + sym_table_push_table(&table); + struct type* ty_float = malloc(sizeof(struct type)); + type_init(ty_float, TYPE_FLOAT); + sym_table_declare_const(&table, "abc", ty_float); + + sym_table_push_table(&table); + struct type* ty_string = malloc(sizeof(struct type)); + type_init(ty_string, TYPE_STRING); + sym_table_declare_const(&table, "abc", ty_string); + + struct sym* sym; + + sym = sym_table_try_by_name(&table, "abc"); + cr_assert_eq(sym->type->kind, TYPE_STRING); + + sym_table_pop_table(&table); + sym = sym_table_try_by_name(&table, "abc"); + cr_assert_eq(sym->type->kind, TYPE_FLOAT); + + sym_table_pop_table(&table); + sym = sym_table_try_by_name(&table, "abc"); + cr_assert_eq(sym->type->kind, TYPE_INT); + + sym_table_free(&table); +} + +Test(sym_table, outer_scope) { + struct sym_table table; + sym_table_init(&table); + + struct type* ty_int = malloc(sizeof(struct type)); + type_init(ty_int, TYPE_INT); + sym_table_declare_const(&table, "abc", ty_int); + + sym_table_push_table(&table); + sym_table_push_table(&table); + sym_table_push_table(&table); + + struct sym* sym = sym_table_try_by_name(&table, "abc"); + cr_assert_eq(sym->type->kind, TYPE_INT); + + sym_table_free(&table); +} diff --git a/lang/tests/type_checker.c b/lang/tests/type_checker.c index ee1ba20..26cad62 100644 --- a/lang/tests/type_checker.c +++ b/lang/tests/type_checker.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include static void test_check_ok(char const* source) { @@ -13,20 +13,16 @@ static void test_check_ok(char const* source) struct node* ast = parser_try_new_root(&parser, source); cr_assert_neq(ast, NULL); - struct syms syms; - syms_init(&syms); - struct type_checker tc; - type_checker_init(&tc, &syms); + type_checker_init(&tc); char buf[GUX_STR_SIZE]; node_str(ast, buf, GUX_STR_SIZE); cr_assert_eq(type_checker_check(&tc, ast), 0, - "type checker failed: %s", buf); + "%s, %s", source, tc.error_msg); type_checker_free(&tc); - syms_free(&syms); node_free(ast); free(ast); @@ -39,17 +35,14 @@ static void test_check_ko(char const* source) parser_init(&parser); struct node* ast = parser_try_new_root(&parser, source); - cr_assert_neq(ast, NULL, "NULL: %s", source); - - struct syms syms; - syms_init(&syms); + cr_assert_neq(ast, NULL, "cannot parse: %s", source); struct type_checker tc; - type_checker_init(&tc, &syms); + type_checker_init(&tc); - cr_assert_neq(type_checker_check(&tc, ast), 0, "%s == null", source); + cr_assert_neq(type_checker_check(&tc, ast), 0, + "%s shouldn't pass check test", source); - syms_free(&syms); type_checker_free(&tc); node_free(ast); @@ -178,3 +171,23 @@ Test(type_checker, while_loop) { test_check_ko("while 5.0 {}"); test_check_ko("while 'bim' {}"); } + +Test(type_checker, fun) { + test_check_ok("hello := fun() {};"); + test_check_ok("hello := fun() -> void {0;};"); + + test_check_ok("hello := fun() -> int { return 0; };"); + test_check_ok("hello := fun(n: int) -> int { return n * 2; };"); + + test_check_ko("hello := fun() -> int {};"); + + test_check_ko("hello := fun() -> bool { return 3.1; };"); + test_check_ko("hello := fun() -> bool { return true; return 'salut'; };"); + test_check_ko("hello := fun(n: float) -> int { return n; };"); + + test_check_ok("hello := fun() -> int { return 4;}; x : int = hello();"); + test_check_ko("hello := fun() -> float { return 4.2;}; x : int = hello();"); + + test_check_ko("hello := fun() -> int { return 0;}; hello(1);"); + test_check_ko("hello := fun(x: int, y: int) -> int { return 0;}; hello(1);"); +} diff --git a/lib/src/commons.h b/lib/src/commons.h index 23503b1..09ff65b 100644 --- a/lib/src/commons.h +++ b/lib/src/commons.h @@ -9,7 +9,7 @@ #include #include -#define GUX_STR_SIZE 512 +#define GUX_STR_SIZE 1024 #define GUX_ENUM_IDENT(X) X #define GUX_ENUM_STR(X) #X #define GUX_ENUM_H(Prefix, Types) \ @@ -22,4 +22,7 @@ #define GUX_RET_ERROR 1 #define GUX_RET_ASSERT 2 +#define GUX_EPRINT(OBJECT, FUNC) \ + {char m[512]; FUNC(OBJECT, m, 512); printf("%s\n", m);} + #endif diff --git a/tests/flow_control.gux b/tests/flow_control.gux index 90c935c..84c0aa0 100644 --- a/tests/flow_control.gux +++ b/tests/flow_control.gux @@ -107,4 +107,4 @@ while g < 100 { h = h + 1; } -assert h == 50; \ No newline at end of file +assert h == 50; diff --git a/tests/fun.gux b/tests/fun.gux new file mode 100644 index 0000000..105f3b3 --- /dev/null +++ b/tests/fun.gux @@ -0,0 +1,67 @@ +double := fun (n: int) -> int { + return n * 2; +}; + +assert double(2) == 4; +assert double(18 + 3) == 42; +assert double(double(-3)) == -12; + +fac := fun (n: int) -> int { + if n == 0 { return 1; } + return n * this(n - 1); +}; + +assert fac(5) == 120; + +inner_val := fun () -> int { + n := 28; + return n; +}; + +assert inner_val() == 28; + +inner_fun := fun (n: int) -> int { + f := fun (n: int) -> int { + return n * 2; + }; + + return f(f(n)); +}; + +assert inner_fun(4) == 16; + +twice := fun (n: int, f: (int -> int)) -> int { + return f(f(n)); +}; + +incr := fun (n: int) -> int { + return n + 1; +}; + +dble := fun (n: int) -> int { + return n * 2; +}; + +assert twice(4, incr) == 6; +assert twice(4, dble) == 16; +assert twice(3, fun (x: int) -> int { + return x + 1; +} ) == 5; + +fun sugar(n: int) -> int { + return n * 2; +} + +assert sugar(21) == 42; + +fun add3(x, y, z: int) -> int { + return x + y + z; +} + +assert add3(2, 4, 6) == 12; + +fun empty(n: string) { + return; +} + +empty('useless'); \ No newline at end of file diff --git a/tests/run.sh b/tests/run.sh index 50f704c..cbe30e5 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -5,7 +5,7 @@ TOTAL=0 for file in $(find . -name "*.gux" | sort) do - MSG=$(guxi $file 2>&1) + MSG=$(gux $file 2>&1) RET=$? echo -en "$file -> " diff --git a/vm/CMakeLists.txt b/vm/CMakeLists.txt index 72a4ff7..cbefdd4 100644 --- a/vm/CMakeLists.txt +++ b/vm/CMakeLists.txt @@ -9,13 +9,13 @@ add_library(gux-vm OBJECT set_property(TARGET gux-vm PROPERTY C_STANDARD 99) -add_dependencies(gux-vm gux-bc) +add_dependencies(gux-vm gux-lang) target_include_directories(gux-vm - PUBLIC gux-bc + PUBLIC gux-lang PUBLIC ${CMAKE_SOURCE_DIR}/vm/src ) target_link_libraries(gux-vm - PUBLIC gux-bc + PUBLIC gux-lang ) diff --git a/vm/src/vm.c b/vm/src/vm.c index f0b59f7..0d0f42d 100644 --- a/vm/src/vm.c +++ b/vm/src/vm.c @@ -4,6 +4,7 @@ #include "type.h" #include "value.h" #include "vec.h" +#include "fun.h" void vm_init(struct vm* self) { @@ -11,6 +12,9 @@ void vm_init(struct vm* self) self->pc = 0; self->fp = 0; + self->error_line = 0; + memset(self->error_msg, 0, GUX_STR_SIZE); + vm_add_frame(self); } @@ -33,6 +37,7 @@ void vm_add_frame(struct vm* self) assert(self); self->stack[self->fp] = malloc(sizeof(struct frame)); self->stack[self->fp]->sp = 0; + self->stack[self->fp]->return_address = 0; vec_init(&self->stack[self->fp]->locals, 1); self->fp++; @@ -73,7 +78,8 @@ int vm_load(struct vm* self, int addr) } } - assert(0); + fprintf(stderr, "cannot load addr %d\n", addr); + abort(); } void vm_store(struct vm* self, int addr) @@ -124,6 +130,71 @@ int vm_exec(struct vm* self, struct program* program) switch (opcode) { + case OP_CALL: { + int fun_addr = vm_pop(self); + struct value* fun_val = program->constant_pool.data[fun_addr]; + struct fun* fun = fun_val->data.fun; + + struct vec args; + vec_init(&args, 1); + + for (int i=0; istack[self->fp - 1]; + + for (size_t i=0; iconstant_pool.data[*addr]); + + size_t new_addr = program_push_constant(&fun->program, val); + vm_push(self, new_addr); + vm_store(self, i); + } + + struct value* new_fun = + value_new_clone(program->constant_pool.data[fun_addr]); + + size_t new_addr = program_push_constant(&fun->program, new_fun); + vm_push(self, new_addr); + + vm_store(self, args.size); + + size_t ret_addr = self->pc; + self->pc = 0; + + vm_exec(self, &fun->program); + + vec_free_elements(&args); + vec_free(&args); + + struct value* ret_val = + fun->program.constant_pool.data[my_frame->stack[my_frame->sp - 1]]; + + struct value* new_ret = value_new_clone(ret_val); + + vm_remove_frame(self); + + vm_push(self, program_push_constant(program, new_ret)); + + self->pc = ret_addr + 1; + + } break; + + case OP_RET: { + return 0; + } break; + case OP_SWAP: { assert(frame->sp > 1); int tmp = frame->stack[frame->sp - 1]; diff --git a/vm/src/vm.h b/vm/src/vm.h index 7ef7a2e..fe91fd1 100644 --- a/vm/src/vm.h +++ b/vm/src/vm.h @@ -16,6 +16,7 @@ struct frame { int stack[STACK_DEPTH]; size_t sp; struct vec locals; + int return_address; }; struct vm {