diff --git a/meson.build b/meson.build index 9d0f71a..8fa7cf9 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,7 @@ executable( 'src/vm.c', 'src/symtable.c', + 'src/fun.c', ], dependencies: [ m_dep diff --git a/src/compiler.c b/src/compiler.c index 9fa5ac0..0bb3b6c 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -341,9 +341,139 @@ void compile_node(compiler* self, node* root, program* prog) compile_number(self, root, prog, OP_IADD, OP_FADD); } } + else if (root->type == NODE_FUN) + { + // init body + compiler comp; + compiler_init(&comp); + program* fun_prog = malloc(sizeof(program)); + program_init(fun_prog); + node* block = root->children.data[root->children.size - 1]; + + // add params + for (size_t i=0; ichildren.size; i++) + { + node* param = root->children.data[i]; + + if (param->type == NODE_FUN_PARAM) + { + char const* param_name = param->children.data[0]->value; + type* param_ty = + type_new_from_node(param->children.data[1]); + + symtable_declare(comp.sym, param_name + , param_ty + , param->children.data[0]); + + type_free(param_ty); free(param_ty); + } + } + + + // compile body + compile_node(&comp, block, fun_prog); + compiler_free(&comp); + + // init fun object + cstatic cs; + cstatic_init(&cs); + + fun* fn = malloc(sizeof(fun)); + type* ty = cstatic_resolve_new(&cs, self->sym, root); + + fun_init(fn, ty, (struct program*) fun_prog); + program_free(fun_prog); free(fun_prog); fun_prog = NULL; + + type_free(ty); free(ty); + + // create value + value* val = malloc(sizeof(value)); + value_init_fun(val, fn, root->lineno); + + // push it + program_add_instr( + prog, + OP_PUSH, + program_add_pool(prog, val) + ); + + value_free(val); free(val); + fun_free(fn); free(fn); + + cstatic_free(&cs); + } + else if (root->type == NODE_CALL) + { + //{char c[512]; node_str(root, c, 512); printf("%s\n", c);} + char const* fun_name = root->children.data[0]->value; + node* args = root->children.data[1]; + + symentry* entry = symtable_find(self->sym, fun_name); + assert(entry); + + program_add_instr(prog, OP_LOAD, entry->id); + + for (size_t i=0; ichildren.size; i++) + { + node* arg = args->children.data[i]; + compile_node(self, arg, prog); + } + + program_add_instr(prog, OP_CALL, args->children.size); + } + else if (root->type == NODE_RETURN) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_RET, NO_PARAM); + } + else if (root->type == NODE_FUN_TYPE) + { + type* ty = malloc(sizeof(type)); + type_init(ty, TY_FUNCTION); + + node* lhs = root->children.data[0]; + + size_t lhs_sz = lhs->children.size; + + for (size_t i=0; ichildren.data[lhs_sz - 1 - i]); + type_add_sub_type(ty, sub); + type_free(sub); free(sub); + } + + if (root->children.data[1]->children.size > 0) + { + node* rhs = root->children.data[1]; + size_t rhs_sz = rhs->children.size; + for (size_t i=0; ichildren.data[rhs_sz - 1 - i]); + sub->kind = KIND_RETURN; + type_add_sub_type(ty, sub); + type_free(sub); free(sub); + } + } + + value val; + value_init_type(&val, + ty, + root->lineno); + + size_t idx = program_add_pool(prog, &val); + program_add_instr(prog, OP_PUSH, idx); + + + type_free(ty); + free(ty); + + value_free(&val); + } else if (root->type == NODE_TYPE) { - type* ty = compile_get_type(self, root); + type* ty = type_new_from_node(root); value val; value_init_type(&val, @@ -572,71 +702,6 @@ void compile_children(compiler* self, node* root, program* prog) } } -type* compile_get_type(compiler* self, node* root) -{ - assert(self); - assert(root); - type* ty = malloc(sizeof(type)); - - if (strcmp(root->value, "int") == 0) - { - type_init(ty, TY_INTEGER); - } - - else if (strcmp(root->value, "float") == 0) - { - type_init(ty, TY_FLOAT); - } - - else if (strcmp(root->value, "str") == 0) - { - type_init(ty, TY_STRING); - } - - else if (strcmp(root->value, "bool") == 0) - { - type_init(ty, TY_BOOLEAN); - } - - else if (strcmp(root->value, "type") == 0) - { - type_init(ty, TY_TYPE); - } - - else if (strcmp(root->value, "array") == 0) - { - type_init(ty, TY_ARRAY); - ty->kind = KIND_SEQUENTIAL; - } - else if (root->type == NODE_ADD) - { - type_init(ty, TY_TYPE); - ty->kind = KIND_DISJUNCTION; - } - else if (root->type == NODE_MUL) - { - type_init(ty, TY_TYPE); - ty->kind = KIND_CONJUNCTION; - } - else - { - fprintf(stderr, - "E(%d): cannot compile unknown type '%s'.\n", - root->lineno, - NodeTypeStr[root->type]); - } - - for (size_t i=0; ichildren.size; i++) - { - type* sub = compile_get_type(self, root->children.data[i]); - type_add_sub_type(ty, sub); - type_free(sub); - free(sub); - } - - return ty; -} - void compile_if(compiler* self, node* root, program* prog) { if (root->children.size == 1) diff --git a/src/compiler.h b/src/compiler.h index b6d6227..386625e 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -30,8 +30,6 @@ void compiler_pop_info(compiler* self); void compile_node(compiler* self, node* root, program* prog); void compile_children(compiler* self, node* root, program* prog); -type* compile_get_type(compiler* self, node* root); - void compile_number(compiler* self, node* root, program* prog, int integer_op, int float_op); diff --git a/src/cstatic.c b/src/cstatic.c index db476e9..1152089 100644 --- a/src/cstatic.c +++ b/src/cstatic.c @@ -4,11 +4,26 @@ void cstatic_init(cstatic* self) { assert(self); + + self->fun_types.size = 0; + self->fun_types.capacity = 1; + self->fun_types.data = malloc(sizeof(type*) * self->fun_types.capacity); } void cstatic_free(cstatic* self) { assert(self); + + for (size_t i=0; ifun_types.size; i++) + { + type_free(self->fun_types.data[i]); + free(self->fun_types.data[i]); + } + + free(self->fun_types.data); + self->fun_types.size = 0; + self->fun_types.capacity = 0; + self->fun_types.data = NULL; } type* cstatic_new_type(cstatic* self, int base_type) @@ -35,10 +50,96 @@ type* cstatic_resolve_new(cstatic* self, symtable* sym, node* ast) assert(self); assert(ast); + if (ast->type == NODE_FUN) + { + type* ty = cstatic_new_type(self, TY_FUNCTION); + + size_t i = 0; + + while (i < ast->children.size + && ast->children.data[i]->type == NODE_FUN_PARAM) + { + type* param_ty = + type_new_from_node( + ast->children.data[i]->children.data[1] + ); + + type_add_sub_type(ty, param_ty); + type_free(param_ty); free(param_ty); + + i++; + } + + if (i < ast->children.size + && ast->children.data[i]->type == NODE_FUN_RET) + { + node* ret = ast->children.data[i]; + + for (size_t i=0; ichildren.size; i++) + { + type* r = type_new_from_node(ret->children.data[i]); + r->kind = KIND_RETURN; + type_add_sub_type(ty, r); + type_free(r); free(r); + } + } + + return ty; + } + + if (ast->type == NODE_CALL) + { + char const* fun_name = ast->children.data[0]->value; + + symentry* entry = symtable_find(sym, fun_name); + assert(entry); + + type* fun_type = entry->ty; + assert(fun_type); + + size_t ret_count = fun_type->sub_types.size; + + if (ret_count > 0) + { + type* ret_type = fun_type->sub_types.data[ret_count - 1]; + assert(ret_type); + + if (ret_type->kind == KIND_RETURN) + { + type* res = type_new_clone(ret_type); + res->kind = KIND_CONJUNCTION; + return res; + } + } + + return NULL; + } + + if (ast->type == NODE_RETURN) + { + return cstatic_resolve_new(self, sym, ast->children.data[0]); + } + + if (ast->type == NODE_IF) + { + type* ty = cstatic_resolve_new(self, + sym, + ast->children.data[1]); + return ty; + } + + if (ast->type == NODE_DO) + { + type* ty = cstatic_resolve_new(self, + sym, + ast->children.data[0]); + return ty; + } if (ast->type == NODE_IDENT) { symentry* entry = symtable_find(sym, ast->value); + assert(entry); type* ty = type_new_clone(entry->ty); return ty; } @@ -184,7 +285,8 @@ type* cstatic_resolve_new(cstatic* self, symtable* sym, node* ast) return cstatic_new_type(self, TY_STRING); } - else if (ast->type == NODE_TYPE) + else if (ast->type == NODE_TYPE + || ast->type == NODE_FUN_TYPE) { return cstatic_new_type(self, TY_TYPE); } @@ -250,7 +352,7 @@ int cstatic_check(cstatic* self, node* ast, symtable* sym, char* msg, size_t siz if (!status) { - return -1; + return 0; } } @@ -259,6 +361,157 @@ int cstatic_check(cstatic* self, node* ast, symtable* sym, char* msg, size_t siz return 1; } + // Function Stuff + if (ast->type == NODE_FUN) + { + type* ty = cstatic_resolve_new(self, sym, ast); + + cstatic_push_fun(self, ty); + + type_free(ty); free(ty); + + symtable* inner_table = symtable_new_clone(sym); + + // function args + for (size_t i=0; ichildren.size; i++) + { + node* child = ast->children.data[i]; + + if (child->type == NODE_FUN_PARAM) + { + char const* name = child->children.data[0]->value; + type* t = type_new_from_node(child->children.data[1]); + + symtable_declare_mut(inner_table, name, t, child); + + type_free(t); free(t); + } + } + + int status = cstatic_check(self, + ast->children.data[ast->children.size - 1], + inner_table, + msg, + size + ); + + symtable_free(inner_table); free(inner_table); + cstatic_pop_fun(self); + + + return status; + } + + // Function Stuff + if (ast->type == NODE_RETURN) + { + type* ret_ty = cstatic_resolve_new(self, sym, ast); + type* fun_ty = cstatic_top_fun(self); + + type* fun_res = NULL; + + for (size_t i=0; isub_types.size; i++) + { + if (fun_ty->sub_types.data[i]->kind == KIND_RETURN) + { + fun_res = type_new_clone(fun_ty->sub_types.data[i]); + fun_res->kind = KIND_CONJUNCTION; + break; + } + } + + assert(fun_res); + + int status = cstatic_check_same_type_ptr( + self, + ast, + fun_res, + ret_ty, + sym, + msg, + size + ); + + if (status) + { + if (fun_res) + { + type_free(fun_res); free(fun_res); + } + + type_free(ret_ty); free(ret_ty); + } + + return status; + } + + if (ast->type == NODE_CALL) + { + char const* fun_name = ast->children.data[0]->value; + symentry* entry = symtable_find(sym, fun_name); + assert(entry); + + type* fun_type = entry->ty; + + node* args = ast->children.data[1]; + size_t arity = 0; + + for (size_t i=0; isub_types.size; i++) + { + type* ty = fun_type->sub_types.data[i]; + if (ty->kind != KIND_RETURN) + { + arity++; + } + } + + if (arity != args->children.size) + { + snprintf(msg, size, "E(%d): function arity mismatch: " + "expected '%ld', got '%ld'.\n", + args->lineno, + arity, + args->children.size + ); + return 0; + } + + for (size_t i=0; ichildren.size; i++) + { + + type* sub = type_new_clone(fun_type->sub_types.data[i]); + assert(sub); + + type* arg_type = cstatic_resolve_new( + self, + sym, + args->children.data[i] + ); + + int status = cstatic_check_same_type_ptr( + self, + args->children.data[i], + sub, + arg_type, + sym, + msg, + size + ); + + if (!status) + { + return 0; + } + + type_free(sub); free(sub); + type_free(arg_type); free(arg_type); + + } + + + return 1; + } + // Children for (size_t i=0; ichildren.size; i++) { @@ -914,3 +1167,38 @@ int cstatic_check_same_type(cstatic* self, node* lhs, node* rhs, symtable* sym, return 1; } + +void cstatic_push_fun(cstatic* self, type* fun_ty) +{ + assert(self); + assert(fun_ty); + + if (self->fun_types.size >= self->fun_types.capacity) + { + self->fun_types.capacity *= 2; + self->fun_types.data = realloc( + self->fun_types.data, + sizeof(type*) * self->fun_types.capacity + ); + } + + self->fun_types.data[self->fun_types.size] = type_new_clone(fun_ty); + self->fun_types.size++; + +} + +void cstatic_pop_fun(cstatic* self) +{ + assert(self->fun_types.size > 0); + + type_free(self->fun_types.data[self->fun_types.size - 1]); + free(self->fun_types.data[self->fun_types.size - 1]); + + self->fun_types.size--; +} + +type* cstatic_top_fun(cstatic* self) +{ + assert(self->fun_types.size > 0); + return self->fun_types.data[self->fun_types.size - 1]; +} diff --git a/src/cstatic.h b/src/cstatic.h index 9b6d3ad..e805ea9 100644 --- a/src/cstatic.h +++ b/src/cstatic.h @@ -8,7 +8,11 @@ #define TYPE_END (-1) typedef struct { - int _unused; + struct { + size_t size; + size_t capacity; + type** data; + } fun_types; } cstatic; void cstatic_init(cstatic* self); @@ -33,4 +37,7 @@ int cstatic_check_same_type_ptr(cstatic* self, int cstatic_check_same_type(cstatic* self, node* lhs, node* rhs, symtable* sym, char* msg, size_t size); +void cstatic_push_fun(cstatic* self, type* fun_ty); +void cstatic_pop_fun(cstatic* self); +type* cstatic_top_fun(cstatic* self); #endif diff --git a/src/fun.c b/src/fun.c new file mode 100644 index 0000000..6dbfa32 --- /dev/null +++ b/src/fun.c @@ -0,0 +1,46 @@ +#include "fun.h" +#include "program.h" + +void fun_init(fun* self, type* ty, struct program* prog) +{ + assert(self); + self->ty = type_new_clone(ty); + self->prog = program_new_clone(prog); +} + +void fun_free(fun* self) +{ + assert(self); + type_free(self->ty); free(self->ty); + + program_free(self->prog); + free(self->prog); + self->prog = NULL; +} + +fun* fun_new_clone(fun* self) +{ + assert(self); + fun* clone = malloc(sizeof(fun)); + fun_init(clone, self->ty, self->prog); + + return clone; +} + +size_t fun_str(fun* self, char* buffer, size_t size) +{ + assert(self); + assert(buffer); + size_t sz = 0; + + sz += snprintf(buffer + sz, size - sz, "function"); + + return sz; +} + +int fun_equals(fun* self, fun* lhs) +{ + assert(self); + assert(lhs); + return 0; // TODO to impl +} diff --git a/src/fun.h b/src/fun.h new file mode 100644 index 0000000..90a57fe --- /dev/null +++ b/src/fun.h @@ -0,0 +1,21 @@ +#ifndef FUN_H +#define FUN_H + +#include "commons.h" +#include "type.h" + +struct program; + +typedef struct { + type* ty; + struct program* prog; +} fun; + +void fun_init(fun* self, type* ty, struct program* prog); +void fun_free(fun* self); + +fun* fun_new_clone(fun* self); +size_t fun_str(fun* self, char* buffer, size_t size); +int fun_equals(fun* self, fun* lhs); + +#endif diff --git a/src/lex.l b/src/lex.l index 4e1e2fa..1fdd7cf 100644 --- a/src/lex.l +++ b/src/lex.l @@ -1,4 +1,5 @@ %{ + #include "parser.h" #include "src/utils.h" int line = 1; @@ -14,10 +15,12 @@ INTEGER -?[0-9]+ FLOAT -?[0-9]+\.[0-9]+ STRING \"[^"]*\" TYPE (int|float|bool|str|array|type) -IDENT [_A-Za-z][_A-Za-z0-9]* +IDENT [_A-Za-z][_A-Za-z0-9]* +SEP [\n]|[;] %% {COMMENT} {} +{SEP} { line++; return SEP; } "\n" { line++; } {WHITESPACES} {} @@ -26,6 +29,9 @@ IDENT [_A-Za-z][_A-Za-z0-9]* return TYPE; } +"fun" { return FUN; } +"as" { return AS; } +"return" { return RETURN; } "while" { return WHILE; } "break" { return BREAK; } "continue" { return CONTINUE; } @@ -42,6 +48,7 @@ IDENT [_A-Za-z][_A-Za-z0-9]* "<" { return LT; } ">" { return GT; } "," { return COMMA; } +":" { return COLON; } "+" { return ADD; } "-" { return SUB; } "*" { return MUL; } diff --git a/src/main.c b/src/main.c index 482f279..f9ae9c9 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #include "vm.h" #include "cstatic.h" + extern FILE* yyin; extern node* ast; extern void stack_push(node*); @@ -13,6 +14,8 @@ extern void stack_free(); int main(int argc, char** argv) { + int DEBUG = argc > 2; + if (argc > 1) { yyin = fopen(argv[1], "r"); @@ -27,6 +30,16 @@ int main(int argc, char** argv) ast = stack_pop(); stack_free(); + + if (DEBUG) + { + size_t const BUF = 1024; + char buffer[BUF]; + memset(buffer, 0, BUF); + node_str(ast, buffer, BUF); + printf("-- ast ---\n%s\n\n", buffer); + } + // Static checking symtable sym; symtable_init(&sym); @@ -52,24 +65,25 @@ int main(int argc, char** argv) compile_node(&comp, ast, &prog); + if (DEBUG) + { + size_t const BUF = 1024; + char buffer[BUF]; + memset(buffer, 0, BUF); + program_str(&prog, buffer, BUF); + printf("--- program ---\n%s\n", buffer); + } + // Execution vm v; vm_init(&v); vm_exec(&v, &prog); - if (0) // DEBUG + if (DEBUG) { size_t const BUF = 1024; char buffer[BUF]; - memset(buffer, 0, BUF); - node_str(ast, buffer, BUF); - printf("-- ast ---\n%s\n\n", buffer); - - memset(buffer, 0, BUF); - program_str(&prog, buffer, BUF); - printf("--- program ---\n%s\n", buffer); - memset(buffer, 0, BUF); vm_str(&v, buffer, BUF); printf("--- stack ---\n%s\n", buffer); diff --git a/src/node.h b/src/node.h index 9ebf9de..b04084b 100644 --- a/src/node.h +++ b/src/node.h @@ -14,7 +14,9 @@ G(NODE_GT), G(NODE_GE), G(NODE_VARDECL), G(NODE_IDENT), \ G(NODE_CONSTDECL), G(NODE_ASSIGN), G(NODE_DO), G(NODE_IS), \ G(NODE_IF), G(NODE_ELSE), G(NODE_BLOCK), \ - G(NODE_WHILE), G(NODE_CONTINUE), G(NODE_BREAK) + G(NODE_WHILE), G(NODE_CONTINUE), G(NODE_BREAK), \ + G(NODE_COLON), G(NODE_FUN_TYPE), G(NODE_RETURN), G(NODE_FUN), \ + G(NODE_FUN_PARAM), G(NODE_FUN_RET), G(NODE_CALL), G(NODE_ARGS) #include "mutils.h" diff --git a/src/opcodes.h b/src/opcodes.h index cf985f0..95c28e7 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -47,7 +47,9 @@ G(OP_ASTORE), \ G(OP_TEQ), \ G(OP_BRF), \ - G(OP_BR) + G(OP_BR), \ + G(OP_CALL), \ + G(OP_RET) enum Opcodes { diff --git a/src/parser.y b/src/parser.y index 52e962a..6fe396e 100644 --- a/src/parser.y +++ b/src/parser.y @@ -2,7 +2,6 @@ #include #include #include "src/node.h" - extern int line; void yyerror(char const*); node* ast = NULL; @@ -16,11 +15,13 @@ void stack_free(); %} +%define parse.error verbose + %union { char* str; size_t n_children; }; - +%token SEP %token IF_COND %token ELSE_COND %token WHILE @@ -29,7 +30,9 @@ %left ASSERT %token TYPE BOOLEAN INTEGER FLOAT STRING IDENT %type expr exprs prog array builtins -%type expr_list type type_list ident if +%type expr_list type type_list ident if +%type fun_rets fun_params fun_param fun +%type fun_ret_list fun_call fun_args any instr %left EQ NE %left LT GT LE GE %left AND @@ -39,11 +42,11 @@ %left POW %left NOT %token OPAR CPAR -%token OSQUARE CSQUARE COMMA +%token OSQUARE CSQUARE COMMA COLON %token LET LET_MUT ASSIGN DO END %type expr_unop block if_rec %left IS - +%token RETURN FUN AS %% prog: @@ -76,30 +79,19 @@ prog: } ; -exprs: - exprs expr { $$ = $1 + $2; } +sep: SEP | sep SEP; +optsep: | sep; - | expr { $$ = $1; } +exprs: + exprs optsep instr sep { $$ = $1 + $3; } + | optsep instr sep { $$ = $2; } ; +any: expr { $$ = $1; } | instr { $$ = $1; }; expr: - CONTINUE { - node* n = malloc(sizeof(node)); - node_init(n, NODE_CONTINUE, "", line); - stack_push(n); - $$ = 1; - } - - | BREAK { - node* n = malloc(sizeof(node)); - node_init(n, NODE_BREAK, "", line); - stack_push(n); - $$ = 1; - } - - | WHILE expr block END { + WHILE expr block END { node* n = malloc(sizeof(node)); node_init(n, NODE_WHILE, "", line); @@ -144,24 +136,6 @@ expr: $$ = 1; } - // INDEX - | expr array ASSIGN expr { - node* n = malloc(sizeof(node)); - node_init(n, NODE_ASSIGN, "", line); - - node* expr_node = stack_pop(); - node* array_node = stack_pop(); - node* ident_node = stack_pop(); - - node_add_child(n, ident_node); - node_add_child(n, array_node); - node_add_child(n, expr_node); - - stack_push(n); - - $$ = 1; - } - | expr array { node *n = malloc(sizeof(node)); node_init(n, NODE_INDEX, "", line); @@ -185,51 +159,6 @@ expr: | ident { } - | LET_MUT ident ASSIGN expr { - node* n = malloc(sizeof(node)); - node_init(n, NODE_VARDECL, "", line); - - node* rhs = stack_pop(); - node* lhs = stack_pop(); - - node_add_child(n, lhs); - node_add_child(n, rhs); - - stack_push(n); - - $$ = 1; - } - - | LET ident ASSIGN expr { - node* n = malloc(sizeof(node)); - node_init(n, NODE_CONSTDECL, "", line); - - node* rhs = stack_pop(); - node* lhs = stack_pop(); - - node_add_child(n, lhs); - node_add_child(n, rhs); - - stack_push(n); - - $$ = 1; - } - - | ident ASSIGN expr { - node* n = malloc(sizeof(node)); - node_init(n, NODE_ASSIGN, "", line); - - node* rhs = stack_pop(); - node* lhs = stack_pop(); - - node_add_child(n, lhs); - node_add_child(n, rhs); - - stack_push(n); - - $$ = 1; - } - // EXPRESSIONS | array { $$ = 1; @@ -239,16 +168,6 @@ expr: $$ = $1; } - | ASSERT expr { - node *n = malloc(sizeof(node)); - node_init(n, NODE_ASSERT, "", line); - node_add_child(n, stack_pop()); - stack_push(n); - - $$ = 1; - } - - | expr LT expr { node *n = malloc(sizeof(node)); node_init(n, NODE_LT, "", line); @@ -414,11 +333,113 @@ expr: } | expr_unop {} + + | fun_call { $$ = $1; } ; +instr: + expr + + | RETURN expr { + node* n = malloc(sizeof(node)); + node_init(n, NODE_RETURN, "", line); + + node_add_child(n, stack_pop()); + stack_push(n); + $$ = 1; + } + + | ident ASSIGN expr { + node* n = malloc(sizeof(node)); + node_init(n, NODE_ASSIGN, "", line); + + node* rhs = stack_pop(); + node* lhs = stack_pop(); + + node_add_child(n, lhs); + node_add_child(n, rhs); + + stack_push(n); + + $$ = 1; + } + + | LET_MUT ident ASSIGN expr { + node* n = malloc(sizeof(node)); + node_init(n, NODE_VARDECL, "", line); + + node* rhs = stack_pop(); + node* lhs = stack_pop(); + + node_add_child(n, lhs); + node_add_child(n, rhs); + + stack_push(n); + + $$ = 1; + } + + | LET ident ASSIGN expr { + node* n = malloc(sizeof(node)); + node_init(n, NODE_CONSTDECL, "", line); + + node* rhs = stack_pop(); + node* lhs = stack_pop(); + + node_add_child(n, lhs); + node_add_child(n, rhs); + + stack_push(n); + + $$ = 1; + } + + | CONTINUE { + node* n = malloc(sizeof(node)); + node_init(n, NODE_CONTINUE, "", line); + stack_push(n); + $$ = 1; + } + + | BREAK { + node* n = malloc(sizeof(node)); + node_init(n, NODE_BREAK, "", line); + stack_push(n); + $$ = 1; + } + + + // INDEX + | expr array ASSIGN expr { + node* n = malloc(sizeof(node)); + node_init(n, NODE_ASSIGN, "", line); + + node* expr_node = stack_pop(); + node* array_node = stack_pop(); + node* ident_node = stack_pop(); + + node_add_child(n, ident_node); + node_add_child(n, array_node); + node_add_child(n, expr_node); + + stack_push(n); + + $$ = 1; + } + + | ASSERT expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_ASSERT, "", line); + node_add_child(n, stack_pop()); + stack_push(n); + + $$ = 1; + } +; + if_rec: - ELSE_COND IF_COND expr block if_rec { + ELSE_COND IF_COND expr block_or_expr if_rec { node* n = malloc(sizeof(node)); node_init(n, NODE_IF, "", line); @@ -435,7 +456,7 @@ if_rec: $$ = 1; } - | ELSE_COND expr END { + | ELSE_COND block_or_expr END { node* n = malloc(sizeof(node)); node_init(n, NODE_IF, "", line); @@ -449,8 +470,10 @@ if_rec: } ; +block_or_expr: block | expr; + if: - IF_COND expr block if_rec { + IF_COND expr block_or_expr if_rec { node* n = malloc(sizeof(node)); node_init(n, NODE_IF, "", line); @@ -467,7 +490,7 @@ if: $$ = 1; } - | IF_COND expr block END { + | IF_COND expr block_or_expr END { node* n = malloc(sizeof(node)); node_init(n, NODE_IF, "", line); @@ -502,8 +525,168 @@ expr_unop: } ; -type_list: +fun_param: + ident AS type { + node *n = malloc(sizeof(node)); + node_init(n, NODE_FUN_PARAM, "", line); + + node* type_node = stack_pop(); + node* ident_node = stack_pop(); + + node_add_child(n, ident_node); + node_add_child(n, type_node); + + stack_push(n); + + $$ = 1; + } + +fun_param: + ident AS type +; + +fun_params: + { $$ = 0; } + | fun_param { + $$ = $1; + } + + | fun_params COMMA fun_param { + $$ = $1 + $3; + } +; + +fun_ret_list: type { $$ = $1; } + | fun_ret_list COMMA type { + $$ = $1 + $3; + } +; + +fun_rets: + { + node* n = malloc(sizeof(node)); + node_init(n, NODE_FUN_RET, "", line); + stack_push(n); + + $$ = 1; + } + + | AS fun_ret_list { + node* n = malloc(sizeof(node)); + node_init(n, NODE_FUN_RET, "", line); + + node* rets[$2]; + + for (size_t i=0; i<$2; i++) + { + rets[$2 - 1 - i] = stack_pop(); + } + + for (size_t i=0; i<$2; i++) + { + node_add_child(n, rets[i]); + } + + stack_push(n); + + $$ = 1; + } +; + +fun: + FUN OPAR fun_params CPAR fun_rets block END { + node* n = malloc(sizeof(node)); + node_init(n, NODE_FUN, "", line); + + node* block_node = stack_pop(); + assert(block_node); + + node* ret_node = stack_pop(); + assert(ret_node); + + if ($3 > 0) + { + node* params[$3]; + + for (size_t i=0; i<$3; i++) + { + params[i] = stack_pop(); + } + + for (size_t i=0; i<$3; i++) + { + node_add_child(n, params[$3 - 1 - i]); + } + } + + node_add_child(n, ret_node); + node_add_child(n, block_node); + + stack_push(n); + + $$ = 1; + } +; + +fun_args: + expr_list { + node* n = malloc(sizeof(node)); + node_init(n, NODE_ARGS, "", line); + size_t const SZ = $1; + + node* args[SZ]; + + for (size_t i=0; ipool.capacity = 0; } +program* program_new_clone(program* self) +{ + assert(self); + program* clone = malloc(sizeof(program)); + program_init(clone); + + for (size_t i=0; i < self->instrs.size; i++) + { + program_add_instr(clone, self->instrs.data[i]->opcode + , self->instrs.data[i]->param); + } + + for (size_t i=0; i < self->pool.size; i++) + { + assert(self->pool.data[i]); + program_add_pool(clone, self->pool.data[i]); + } + + return clone; +} + void program_set_instr(program* self, size_t index, int opcode, int param) { assert(self); @@ -99,12 +120,15 @@ size_t program_add_pool(program* self, value* val) self->pool.capacity *= 2; self->pool.data = realloc( self->pool.data, - sizeof(value) * self->pool.capacity + sizeof(value*) * self->pool.capacity ); } - size_t idx = self->pool.size; - self->pool.data[idx] = value_new_clone(val); + size_t idx = self->pool.size; + value* new_val = value_new_clone(val); + assert(new_val); + self->pool.data[idx] = new_val; + self->pool.size++; return idx; diff --git a/src/program.h b/src/program.h index c0bdd72..f76be52 100644 --- a/src/program.h +++ b/src/program.h @@ -29,6 +29,8 @@ typedef struct { void program_init(program* self); void program_free(program* self); +program* program_new_clone(program* self); + size_t program_add_instr(program* self, int opcode, int param); void program_set_instr(program* self, size_t index, int opcode, int param); void program_set_param(program* self, size_t index, int param); diff --git a/src/symtable.c b/src/symtable.c index bfc2776..9fc8adc 100644 --- a/src/symtable.c +++ b/src/symtable.c @@ -42,6 +42,36 @@ void symentry_free(symentry* self) free(self->ty); } +symtable* symtable_new_clone(symtable* self) +{ + symtable* clone = malloc(sizeof(symtable)); + symtable_init(clone); + + clone->entries.capacity = self->entries.capacity; + clone->entries.data = realloc(clone->entries.data, + sizeof(symentry*) * clone->entries.capacity); + + for (size_t i=0; ientries.size; i++) + { + symentry* entry = self->entries.data[i]; + symentry* clone_entry = malloc(sizeof(symentry)); + + clone_entry->id = entry->id; + clone_entry->name = str_new(entry->name); + clone_entry->is_mut = entry->is_mut; + clone_entry->scope = entry->scope; + clone_entry->ty = type_new_clone(entry->ty); + clone_entry->sym_node = node_new_clone(entry->sym_node); + clone->entries.data[i] = clone_entry; + } + + clone->entries.size = self->entries.size; + clone->id = self->id; + clone->scope = self->scope; + + return clone; +} + size_t symtable_declare_mut(symtable* self, char const* name, type* ty, node* sym_node) { assert(self); diff --git a/src/symtable.h b/src/symtable.h index 8f13d37..600ea38 100644 --- a/src/symtable.h +++ b/src/symtable.h @@ -29,6 +29,7 @@ void symtable_init(symtable* self); void symtable_free(symtable* self); void symentry_free(symentry* self); +symtable* symtable_new_clone(symtable* self); size_t symtable_declare(symtable* self, char const* name, type* ty, node* sym_node); size_t symtable_declare_mut(symtable* self, char const* name, type* ty, node* sym_node); symentry* symtable_find(symtable* self, char const* name); diff --git a/src/type.c b/src/type.c index 7e47884..e1aff9c 100644 --- a/src/type.c +++ b/src/type.c @@ -12,6 +12,103 @@ void type_init(type* self, int base_type) self->sub_types.data = malloc(sizeof(type*)); } +type* type_init_from_node(node* root) +{ + assert(root); + type* ty = malloc(sizeof(type)); + + if (root->type == NODE_FUN_TYPE) + { + type_init(ty, TY_FUNCTION); + + node* params = root->children.data[0]; + + for (size_t i=0; i< params->children.size; i++) + { + type* t = type_new_from_node(params->children.data[i]); + type_add_sub_type(ty, t); + type_free(t); free(t); + } + + node* rets = root->children.data[1]; + + for (size_t i=0; i< rets->children.size; i++) + { + type* t = type_new_from_node(rets->children.data[i]); + type_add_sub_type(ty, t); + type_free(t); free(t); + } + + return ty; + } + + else if (strcmp(root->value, "fun") == 0) + { + type_init(ty, TY_FUNCTION); + } + + else if (strcmp(root->value, "int") == 0) + { + type_init(ty, TY_INTEGER); + } + + else if (strcmp(root->value, "float") == 0) + { + type_init(ty, TY_FLOAT); + } + + else if (strcmp(root->value, "str") == 0) + { + type_init(ty, TY_STRING); + } + + else if (strcmp(root->value, "bool") == 0) + { + type_init(ty, TY_BOOLEAN); + } + + else if (strcmp(root->value, "type") == 0) + { + type_init(ty, TY_TYPE); + } + + else if (strcmp(root->value, "array") == 0) + { + type_init(ty, TY_ARRAY); + ty->kind = KIND_SEQUENTIAL; + } + else if (root->type == NODE_ADD) + { + type_init(ty, TY_TYPE); + ty->kind = KIND_DISJUNCTION; + } + else if (root->type == NODE_MUL) + { + type_init(ty, TY_TYPE); + ty->kind = KIND_CONJUNCTION; + } + else + { + char c[512]; + node_str(root, c, 512); + printf("errnode = %s\n", c); + fprintf(stderr, + "E(%d): cannot compile unknown type '%s').\n", + root->lineno, + NodeTypeStr[root->type]); + } + + for (size_t i=0; ichildren.size; i++) + { + type* sub = type_new_from_node(root->children.data[i]); + type_add_sub_type(ty, sub); + type_free(sub); + free(sub); + } + + return ty; +} + void type_init_array(type* self, type* array_type) { assert(self); @@ -35,7 +132,12 @@ void type_free(type* self) self->sub_types.size = 0; self->sub_types.capacity = 0; self->sub_types.data = NULL; +} +type* type_new_from_node(node* root) +{ + assert(root); + return type_init_from_node(root); } void type_add_sub_type(type* self, type* rhs) @@ -92,7 +194,8 @@ int type_equals(type* self, type* rhs) return 0; } - if (self->kind != KIND_SEQUENTIAL) + if (self->kind == KIND_CONJUNCTION + || self->kind == KIND_DISJUNCTION) { for (int i=0; isub_types.size; i++) { - if (i > 0) + + if ((i == 0 || self->sub_types.data[i - 1 ]->kind != KIND_RETURN) + && self->sub_types.data[i]->kind == KIND_RETURN) + { + sz += snprintf(buffer + sz, size - sz, " : "); + } + else if (i > 0) { sz += snprintf(buffer + sz, size - sz, ", "); } diff --git a/src/type.h b/src/type.h index 559f389..06215d6 100644 --- a/src/type.h +++ b/src/type.h @@ -2,6 +2,7 @@ #define TYPE_H #include "commons.h" +#include "node.h" #define TYPES(G) \ G(TY_NIL), \ @@ -11,6 +12,7 @@ G(TY_STRING), \ G(TY_ARRAY), \ G(TY_TYPE), \ + G(TY_FUNCTION), \ G(TY_TYPE_COUNT) enum Types { @@ -22,7 +24,8 @@ extern char const* TypesStr[]; enum TypeKind { KIND_DISJUNCTION, KIND_CONJUNCTION, - KIND_SEQUENTIAL + KIND_SEQUENTIAL, + KIND_RETURN }; typedef struct type { @@ -37,9 +40,11 @@ typedef struct type { } type; void type_init(type* self, int base_type); +type* type_init_from_node(node* root); void type_init_array(type* self, type* array_type); void type_free(type* self); +type* type_new_from_node(node* root); void type_add_sub_type(type* self, type* rhs); type* type_new_clone(type* self); diff --git a/src/value.c b/src/value.c index bbd42c7..7eaa4a9 100644 --- a/src/value.c +++ b/src/value.c @@ -67,6 +67,17 @@ void value_init_type(value* self, type* ty, int lineno) type_init(self->type, TY_TYPE); } +void value_init_fun(value* self, fun* f, int lineno) +{ + assert(self); + assert(f); + + self->val.fun_val = fun_new_clone(f); + + self->lineno = lineno; + self->type = type_new_clone(f->ty); +} + void value_free(value* self) { assert(self); @@ -94,6 +105,14 @@ void value_free(value* self) self->val.array_val = NULL; } + if (self->type->base_type == TY_FUNCTION + && self->val.fun_val != NULL) + { + fun_free(self->val.fun_val); + free(self->val.fun_val); + self->val.fun_val = NULL; + } + type_free(self->type); free(self->type); self->type = NULL; @@ -121,6 +140,11 @@ value* value_new_clone(value* self) clone->val.array_val = array_new_clone(self->val.array_val); } + if (self->type->base_type == TY_FUNCTION) + { + clone->val.fun_val = fun_new_clone(self->val.fun_val); + } + clone->lineno = self->lineno; return clone; @@ -167,6 +191,11 @@ int value_equals(value* self, value* rhs) return array_equals(self->val.array_val, rhs->val.array_val); } + if (self->type->base_type == TY_FUNCTION) + { + return fun_equals(self->val.fun_val, rhs->val.fun_val); + } + size_t const SZ = 512; char ty_str[SZ]; @@ -190,6 +219,12 @@ size_t value_str(value* self, char* buffer, size_t size) case TY_ARRAY: return array_str(self->val.array_val, buffer, size); + case TY_FUNCTION: + return fun_str(self->val.fun_val, buffer, size); + + case TY_FLOAT: + return snprintf(buffer, size, "%f", + self->val.real_float); case TY_STRING: return snprintf(buffer, size, "%s", self->val.string); diff --git a/src/value.h b/src/value.h index 901c2dd..37d151f 100644 --- a/src/value.h +++ b/src/value.h @@ -4,6 +4,7 @@ #include "commons.h" #include "type.h" #include "array.h" +#include "fun.h" typedef struct { type* type; @@ -15,6 +16,8 @@ typedef struct { char* string; array* array_val; type* type_val; + fun* fun_val; + } val; } value; @@ -24,6 +27,7 @@ void value_init_float(value* self, float real_float, int lineno); void value_init_string(value* self, char* string, int lineno); void value_init_array(value* self, array* arr, int lineno); void value_init_type(value* self, type* ty, int lineno); +void value_init_fun(value* self, fun* f, int lineno); void value_free(value* self); diff --git a/src/vm.c b/src/vm.c index 924be02..2de56e9 100644 --- a/src/vm.c +++ b/src/vm.c @@ -109,6 +109,9 @@ void vm_exec(vm* self, program* prog) case OP_TEQ: vm_teq(self); break; + case OP_CALL: vm_call(self, param); break; + case OP_RET: vm_ret(self); break; + default: { fprintf(stderr, "unknown opcode %s\n", OpcodesStr[opcode]); @@ -1142,3 +1145,51 @@ void vm_br(vm* self, int param) assert(self); self->pc = param; } + +void vm_call(vm* self, int param) +{ + assert(self); + + // get function and parameters + value* args[param]; + + for (int i=0; ival.fun_val; + + // create new ecxecution environment + vm v; + vm_init(&v); + + for (int i=0; iprog); + + value* ret = value_new_clone(v.stack.data[v.stack.size - 1]); + vm_push_value(self, ret); + + vm_free(&v); + + for (int i=0; ipc++; +} + +void vm_ret(vm* self) +{ + assert(self); + self->pc = self->prog->instrs.size; +} diff --git a/src/vm.h b/src/vm.h index 2457c55..41a2a77 100644 --- a/src/vm.h +++ b/src/vm.h @@ -95,4 +95,7 @@ void vm_teq(vm* self); void vm_brf(vm* self, int param); void vm_br(vm* self, int param); +void vm_call(vm* self, int param); +void vm_ret(vm* self); + #endif diff --git a/tests/err_fun.wuz b/tests/err_fun.wuz new file mode 100644 index 0000000..a5e734a --- /dev/null +++ b/tests/err_fun.wuz @@ -0,0 +1 @@ +let f = fun () as int return 1.2; end ; f diff --git a/tests/err_vars.wuz b/tests/err_vars.wuz index 7d264f7..8c5f763 100644 --- a/tests/err_vars.wuz +++ b/tests/err_vars.wuz @@ -1,8 +1,8 @@ -let x = 0 x + 1.2 -let x = false x + "false" -let x=0 let x=7 -let x=[1, 2] x[7, 9] -let x = 0 x = 3.2 -let x = 0 x = 7 -let x=[1, 2] x[0] = 3.2 -let x=[3, 4] x[0] = 7 +let x = 0 ; x + 1.2 +let x = false ; x + "false" +let x=0 ; let x=7 +let x=[1, 2] ; x[7, 9] +let x = 0 ; x = 3.2 +let x = 0 ; x = 7 +let x=[1, 2] ; x[0] = 3.2 +let x=[3, 4] ; x[0] = 7 diff --git a/tests/errors.sh b/tests/errors.sh index d3fb647..a49d7bd 100755 --- a/tests/errors.sh +++ b/tests/errors.sh @@ -9,7 +9,6 @@ echo -e "\e[34m=== Error Testing ===\e[0m" for file in $(find . -name "err_*.wuz") do LINE=1 - while read line; do echo "$line" | wuz &> /dev/null diff --git a/tests/test_fun.wuz b/tests/test_fun.wuz new file mode 100644 index 0000000..4915403 --- /dev/null +++ b/tests/test_fun.wuz @@ -0,0 +1,32 @@ +assert fun is type +assert fun is type +assert fun<:int> is type +assert fun is type +assert fun< fun<:> : int> is type + +let a = fun (x as int, y as int) as int + 0 +end + +assert a is fun +assert !(a is fun<:>) + +let b = fun (x as int) as int + return x * 7 +end + +assert 42 == b 6 + +let c = fun () as int + return 3 * 6 +end + +assert 18 == *c + +let d = fun () as int + return 43 + 27 +end + +assert 43 == *d + diff --git a/tests/test_integers.wuz b/tests/test_integers.wuz index 25af4c3..f1097b5 100644 --- a/tests/test_integers.wuz +++ b/tests/test_integers.wuz @@ -20,3 +20,4 @@ assert 64 == 2^(3+3) assert 1 == -2 - -3 assert 5 + 7 == 12 +