✨ functions
parent
6642b6613e
commit
09bbc31320
|
@ -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)
|
||||
|
|
3
Makefile
3
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 \
|
||||
|
|
|
@ -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
|
||||
)
|
32
doc/gux.bnf
32
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <type_checker.h>
|
||||
#include <compiler.h>
|
||||
#include <vm.h>
|
||||
#include <syms.h>
|
||||
#include <sym_table.h>
|
||||
|
||||
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; i<params->children.size; i++)
|
||||
{
|
||||
struct node* param = params->children.data[i];
|
||||
|
||||
if (param->type != NODE_TYPE)
|
||||
{
|
||||
vec_push(&idents, param);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j=0; j<idents.size; j++)
|
||||
{
|
||||
struct node* ident = idents.data[j];
|
||||
|
||||
struct type* ty = malloc(sizeof(struct type));
|
||||
type_init_from_node(ty, param);
|
||||
|
||||
sym_table_declare_const(&compiler.sym_table,
|
||||
ident->value,
|
||||
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; i<args->children.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; i<loop->to_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; i<node->children.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:
|
|
@ -4,7 +4,7 @@
|
|||
#include <commons.h>
|
||||
#include <node.h>
|
||||
#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,
|
|
@ -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; i<self->program.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; i<self->program.constant_pool.size; i++)
|
||||
{
|
||||
struct value* val = self->program.constant_pool.data[i];
|
||||
program_push_constant(&clone->program, value_new_clone(val));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef FUN_H
|
||||
#define FUN_H
|
||||
|
||||
#include <commons.h>
|
||||
#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
|
|
@ -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);
|
||||
|
|
|
@ -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; i<self->children.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; i<self->children.size; i++)
|
||||
{
|
||||
struct node* child = self->children.data[i];
|
||||
node_try_find_all(child, type, res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
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
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
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_SWAP), G(OP_CALL), G(OP_RET)
|
||||
|
||||
#include <commons.h>
|
||||
|
|
@ -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; i<types.size; i++)
|
||||
{
|
||||
size_t* t = types.data[i];
|
||||
size_t idx = *t;
|
||||
|
||||
node_free(node->children.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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; i<self->instructions.size; i++)
|
||||
{
|
||||
struct instruction* instr = self->instructions.data[i];
|
||||
program_push_instr(dest, instr->opcode, instr->param);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<self->constant_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; i<self->constant_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_"));
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <commons.h>
|
||||
#include <vec.h>
|
||||
#include <value.h>
|
||||
#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);
|
||||
|
|
@ -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; i<to_rm->syms.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; i<table->syms.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; i<table->syms.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;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef SYM_TABLE_H
|
||||
#define SYM_TABLE_H
|
||||
|
||||
#include <commons.h>
|
||||
#include <vec.h>
|
||||
#include <type.h>
|
||||
|
||||
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
|
191
lang/src/syms.c
191
lang/src/syms.c
|
@ -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; i<self->types.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; i<count; i++)
|
||||
{
|
||||
type_add_subtype(&ty->type, 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; i<self->types.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; i<self->types.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; i<self->entries.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; i<self->entries.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;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef SYMS_H
|
||||
#define SYMS_H
|
||||
|
||||
#include <commons.h>
|
||||
#include <vec.h>
|
||||
#include <type.h>
|
||||
|
||||
#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
|
125
lang/src/type.c
125
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; i<node->children.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; i<inputs.size; i++)
|
||||
{
|
||||
vec_push(&self->subtypes, 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; i<params->children.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; i<self->subtypes.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; i<self->subtypes.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;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
#include <commons.h>
|
||||
#include <vec.h>
|
||||
#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
|
||||
|
|
|
@ -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; i<node->children.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; i<params->children.size; i++)
|
||||
{
|
||||
struct node* param = params->children.data[i];
|
||||
|
||||
if (param->type == NODE_TYPE)
|
||||
{
|
||||
for (size_t j=0; j<names.size; j++)
|
||||
{
|
||||
struct node* n = names.data[j];
|
||||
struct type* ty = malloc(sizeof(struct type));
|
||||
type_init_from_node(ty, param);
|
||||
|
||||
sym_table_declare_const(&self->sym_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; i<node->children.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;
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
#include <type_resolver.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
#include <commons.h>
|
||||
#include <node.h>
|
||||
#include <type.h>
|
||||
#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,
|
||||
|
|
|
@ -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, "<function>");
|
||||
return sz;
|
||||
}
|
||||
|
||||
if (self->type.subtypes.size == 0)
|
||||
{
|
||||
if (self->type.kind == TYPE_VOID)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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; }");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
#include <criterion/criterion.h>
|
||||
#include <sym_table.h>
|
||||
#include <type.h>
|
||||
|
||||
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);
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
#include <parser.h>
|
||||
#include <parser.h>
|
||||
#include <type_checker.h>
|
||||
#include <syms.h>
|
||||
#include <sym_table.h>
|
||||
|
||||
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);");
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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');
|
|
@ -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 -> "
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
73
vm/src/vm.c
73
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; i<param; i++)
|
||||
{
|
||||
int* addr = malloc(sizeof(int));
|
||||
*addr = vm_pop(self);
|
||||
vec_push(&args, addr);
|
||||
}
|
||||
|
||||
vm_add_frame(self);
|
||||
struct frame* my_frame = self->stack[self->fp - 1];
|
||||
|
||||
for (size_t i=0; i<args.size; i++)
|
||||
{
|
||||
size_t k = args.size - 1 - i;
|
||||
|
||||
int* addr = args.data[k];
|
||||
|
||||
struct value* val =
|
||||
value_new_clone(program->constant_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];
|
||||
|
|
|
@ -16,6 +16,7 @@ struct frame {
|
|||
int stack[STACK_DEPTH];
|
||||
size_t sp;
|
||||
struct vec locals;
|
||||
int return_address;
|
||||
};
|
||||
|
||||
struct vm {
|
||||
|
|
Loading…
Reference in New Issue