diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 21ed6fc..cfa42c9 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -7,6 +7,7 @@ EXPR ::= | ASSIGN | BLOCK | IF +| return EXPR IF ::= if EXPR BLOCK (else (BLOCK | IF))? BLOCK ::= begin EXPR* end ASSIGN ::= ident assign EXPR @@ -26,6 +27,12 @@ LITERAL ::= | BUILTIN | opar EXPR cpar | ident +| FUN +| CALL +CALL ::= ident opar ARGS cpar +ARGS ::= (EXPR (comma EXPR)*)? +FUN ::= fun opar PARAMS cpar EXPR* end +PARAMS ::= (EXPR (comma EXPR)*)? BUILTIN ::= | int | bool diff --git a/features/fun.sk b/features/fun.sk new file mode 100644 index 0000000..88288db --- /dev/null +++ b/features/fun.sk @@ -0,0 +1,28 @@ +var a = fun (x) + return x * 2 +end + +assert a(7) eq 14 +assert a(a(7)) eq 28 + +var b = fun (u, v) + return u - v +end + +assert b(a(2), a(3)) eq -2 + +var c = fun(x, f) + return f(f(x)) +end + +assert c(7, a) eq 28 + +var d = fun(x) + var a = fun(x) + x + 1 + end + + return a(a(x)) +end + +assert d(3) eq 5 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index aff007d..6af2c95 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(skopy-lib SHARED src/errors.c src/sym.c + src/fun.c ) file(GLOB_RECURSE diff --git a/lib/include/fun.h b/lib/include/fun.h new file mode 100644 index 0000000..f8cfc9e --- /dev/null +++ b/lib/include/fun.h @@ -0,0 +1,15 @@ +#ifndef SK_FUN_H +#define SK_FUN_H + +#include "commons.h" + +struct fun +{ + struct prog* prog; +}; + +void fun_init(struct fun* self, struct prog* new_prog); +void fun_free(struct fun* self); +struct fun* fun_new_clone(struct fun* self); + +#endif diff --git a/lib/include/node.h b/lib/include/node.h index fa3f88d..2eabd45 100644 --- a/lib/include/node.h +++ b/lib/include/node.h @@ -14,7 +14,8 @@ G(NODE_NOT), G(NODE_FLOAT), G(NODE_STRING), \ G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \ G(NODE_EQUAL), G(NODE_NOT_EQUAL), G(NODE_VAR_DECL), \ G(NODE_CONST_DECL), G(NODE_IDENT), G(NODE_ASSIGN), \ -G(NODE_BLOCK), G(NODE_IF) +G(NODE_BLOCK), G(NODE_IF), G(NODE_FUN), G(NODE_PARAMS), \ +G(NODE_RETURN), G(NODE_CALL), G(NODE_ARGS) SK_ENUM_H(NodeKind, NODE_KIND); diff --git a/lib/include/parser.h b/lib/include/parser.h index d5e50f8..18dcca3 100644 --- a/lib/include/parser.h +++ b/lib/include/parser.h @@ -36,6 +36,10 @@ struct node* parser_try_usub(struct parser* self); struct node* parser_try_not(struct parser* self); struct node* parser_try_pow(struct parser* self); struct node* parser_try_literal(struct parser* self); +struct node* parser_try_call(struct parser* self); +struct node* parser_try_args(struct parser* self); +struct node* parser_try_fun(struct parser* self); +struct node* parser_try_params(struct parser* self); struct node* parser_try_builtin(struct parser* self); #endif diff --git a/lib/include/prog.h b/lib/include/prog.h index b2f720b..36cdd9e 100644 --- a/lib/include/prog.h +++ b/lib/include/prog.h @@ -12,7 +12,8 @@ G(OP_DIV), G(OP_MOD), G(OP_POW), \ G(OP_USUB), G(OP_ASSERT_EQ), \ G(OP_NOT), G(OP_AND), G(OP_OR), \ G(OP_BR), G(OP_BRF), G(OP_LT), G(OP_GT), \ -G(OP_EQUAL), G(OP_LOCAL_STORE), G(OP_LOCAL_LOAD) +G(OP_EQUAL), G(OP_LOCAL_STORE), G(OP_LOCAL_LOAD), \ +G(OP_CALL), G(OP_RET) SK_ENUM_H(Opcode, OPCODE); @@ -26,6 +27,8 @@ struct prog void prog_init(struct prog* self); void prog_free(struct prog* self); +struct prog* prog_new_clone(struct prog* self); + size_t prog_add_instr(struct prog* self, Opcode opcode, size_t param); diff --git a/lib/include/state.h b/lib/include/state.h index 15d0e6d..a74f901 100644 --- a/lib/include/state.h +++ b/lib/include/state.h @@ -46,6 +46,7 @@ void state_free(struct state* self); struct frame* state_frame(struct state* self); void state_push_frame(struct state* self); +void state_pop_frame(struct state* self); bool state_has_top(struct state* self); SK state_top(struct state* self); @@ -62,6 +63,7 @@ SK state_push_int(struct state* self, int integer, int line); SK state_push_bool(struct state* self, bool boolean, int line); SK state_push_float(struct state* self, double real, int line); SK state_push_string(struct state* self, char const* str, int line); +SK state_push_fun(struct state* self, struct fun* fun, int line); struct local* state_try_get_local(struct state* self, int id); @@ -92,4 +94,6 @@ SK state_lt(struct state* self); SK state_gt(struct state* self); SK state_eq(struct state* self); +void state_call(struct state* self); +void state_ret(struct state* self); #endif diff --git a/lib/include/token.h b/lib/include/token.h index 1f2f855..c86d97c 100644 --- a/lib/include/token.h +++ b/lib/include/token.h @@ -15,7 +15,8 @@ G(TOKEN_NOT), G(TOKEN_FLOAT), G(TOKEN_STRING), \ G(TOKEN_LT), G(TOKEN_LE), G(TOKEN_GT), G(TOKEN_GE), \ G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL), G(TOKEN_VAR), \ G(TOKEN_CONST), G(TOKEN_ASSIGN), G(TOKEN_IDENT), \ -G(TOKEN_BEGIN), G(TOKEN_END), G(TOKEN_IF), G(TOKEN_ELSE) +G(TOKEN_BEGIN), G(TOKEN_END), G(TOKEN_IF), G(TOKEN_ELSE), \ +G(TOKEN_FUN), G(TOKEN_RETURN), G(TOKEN_COMMA) SK_ENUM_H(TokenKind, TOKEN_KIND); diff --git a/lib/include/value.h b/lib/include/value.h index 50794f8..0380282 100644 --- a/lib/include/value.h +++ b/lib/include/value.h @@ -3,10 +3,11 @@ #include "commons.h" #include "node.h" +#include "fun.h" #define TYPE_KIND(G) \ G(TYPE_INT), G(TYPE_BOOL), G(TYPE_FLOAT), \ -G(TYPE_STRING) +G(TYPE_STRING), G(TYPE_FUN) SK_ENUM_H(TypeKind, TYPE_KIND); @@ -16,6 +17,7 @@ union val double real; bool boolean; char* str; + struct fun* fun; }; struct value @@ -32,6 +34,8 @@ void value_init(struct value* self, void value_free(struct value* self); +struct value* value_new_clone(struct value* self); + bool value_equals(struct value* self, struct value* rhs); void value_str(struct value* self, struct str* dest); diff --git a/lib/src/compiler.c b/lib/src/compiler.c index 3b3355c..4d23e18 100644 --- a/lib/src/compiler.c +++ b/lib/src/compiler.c @@ -40,6 +40,20 @@ void compiler_compile(struct compiler* self, sym_close_scope(sym); } break; + case NODE_CALL: { + struct node* target = node->children.data[0]; + struct node* args = node->children.data[1]; + + for (size_t i=0; ichildren.size; i++) + { + compiler_compile(self, args->children.data[i], + prog, sym); + } + + compiler_compile(self, target, prog, sym); + prog_add_instr(prog, OP_CALL, args->children.size); + } break; + case NODE_IF: { struct vec to_end; vec_init(&to_end); @@ -133,6 +147,40 @@ void compiler_compile(struct compiler* self, assert(symbol); prog_add_instr(prog, OP_LOCAL_LOAD, symbol->id); } break; + + case NODE_RETURN: { + compiler_compile(self, node->children.data[0], + prog, sym); + prog_add_instr(prog, OP_RET, SK_NO_PARAM); + } break; + + case NODE_FUN: { + struct fun* fun = malloc(sizeof(struct fun)); + struct prog* p = malloc(sizeof(struct prog)); + prog_init(p); + + struct node* args = node->children.data[0]; + struct node* body = node->children.data[1]; + + struct sym mysym; + sym_init(&mysym); + + for (size_t i=0; ichildren.size; i++) + { + struct node* child = args->children.data[i]; + char* name = child->token->value; + sym_decl_var(&mysym, name); + } + + compiler_compile(self, body, p, &mysym); + sym_free(&mysym); + fun_init(fun, p); + + union val val; + val.fun = fun; + compiler_compile_value(self, node, prog, TYPE_FUN, + val); + } break; case NODE_BOOL: { union val val; val.boolean = @@ -264,7 +312,6 @@ void compiler_compile_value(struct compiler* self, (void) self; struct value* value = malloc(sizeof(struct value)); value_init(value, type, val, node->token->line); - size_t idx = prog_add_constant(prog, value); prog_add_instr(prog, OP_PUSH, idx); } diff --git a/lib/src/exec.c b/lib/src/exec.c index 33adeec..f356ff9 100644 --- a/lib/src/exec.c +++ b/lib/src/exec.c @@ -1,4 +1,5 @@ #include "exec.h" +#include "fun.h" void exec_init(struct exec* self) { @@ -72,10 +73,65 @@ void exec_execute(struct exec* self, self->pc++; } break; + case OP_RET: { + self->pc = prog->opcodes.size; + } break; + + case OP_CALL: { + assert(self); + struct vec params; + vec_init(¶ms); + + SK function = state_pop(state); + for (size_t i=0; itype, val->val, val->line); + state_local_store(state, i+1); + } + + vec_free_elements(¶ms, NULL); + vec_free(¶ms); + + assert(f->type == TYPE_FUN); + + struct exec ex; + exec_init(&ex); + + exec_execute(&ex, state, f->val.fun->prog); + + struct value* res = value_new_clone(state_try_get_value( + state, + state_top(state) + )); + + state_ret(state); + state_push(state, res->type, res->val, res->line); + value_free(res); free(res); + self->pc++; + } break; + case OP_PUSH: { struct value* constant = prog->constants.data[param]; switch (constant->type) { + case TYPE_FUN: { + state_push_fun( + state, + fun_new_clone(constant->val.fun), + constant->line + ); + } break; + case TYPE_INT: { state_push_int( state, diff --git a/lib/src/fun.c b/lib/src/fun.c new file mode 100644 index 0000000..99bf8b4 --- /dev/null +++ b/lib/src/fun.c @@ -0,0 +1,25 @@ +#include "fun.h" +#include "prog.h" + +void fun_init(struct fun* self, struct prog* new_prog) +{ + assert(self); + self->prog = new_prog; +} + +void fun_free(struct fun* self) +{ + assert(self); + prog_free(self->prog); + free(self->prog); + self->prog = NULL; +} + +struct fun* fun_new_clone(struct fun* self) +{ + (void) self; + + struct fun* clone = malloc(sizeof(struct fun)); + fun_init(clone, prog_new_clone(self->prog)); + return clone; +} diff --git a/lib/src/lexer.c b/lib/src/lexer.c index 378f8e9..f733d6c 100644 --- a/lib/src/lexer.c +++ b/lib/src/lexer.c @@ -89,6 +89,7 @@ bool lexer_is_sep(struct lexer* self, size_t index) || c == '^' || c == '<' || c == '>' + || c == ',' || c == '='; } @@ -193,6 +194,9 @@ struct token* lexer_try_new_next(struct lexer* self) SK_SCAN_TEXT(">", TOKEN_GT); SK_SCAN_TEXT("<", TOKEN_LT); SK_SCAN_TEXT("=", TOKEN_ASSIGN); + SK_SCAN_TEXT(",", TOKEN_COMMA); + SK_SCAN_KEYWORD("fun", TOKEN_FUN, NULL); + SK_SCAN_KEYWORD("return", TOKEN_RETURN, NULL); SK_SCAN_KEYWORD("if", TOKEN_IF, NULL); SK_SCAN_KEYWORD("else", TOKEN_ELSE, NULL); SK_SCAN_KEYWORD("begin", TOKEN_BEGIN, NULL); diff --git a/lib/src/module.c b/lib/src/module.c index 9b39e5e..3d794fe 100644 --- a/lib/src/module.c +++ b/lib/src/module.c @@ -61,7 +61,6 @@ int module_compile(struct module* self) struct state state; state_init(&state); - exec_execute(&exec, &state, &self->prog); if (!errors_ok()) diff --git a/lib/src/parser.c b/lib/src/parser.c index 0b99986..5173d87 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -89,6 +89,23 @@ struct node* parser_try_root(struct parser* self) struct node* parser_try_expr(struct parser* self) { + if (lexer_next_is(&self->lexer, TOKEN_RETURN)) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_RETURN, + lexer_try_new_next(&self->lexer)); + + struct node* child = SK_TRY(parser_try_expr); + + if (!child) + { + node_free(node); free(node); + return NULL; + } + + node_push_new_child(node, child); + return node; + } if (lexer_next_is(&self->lexer, TOKEN_ASSERT)) { return SK_TRY(parser_try_assert); @@ -825,6 +842,12 @@ struct node* parser_try_literal(struct parser* self) return node; } + if (lexer_next_is(&self->lexer, TOKEN_IDENT) + && lexer_next_nth_is(&self->lexer, TOKEN_OPAR, 1)) + { + return SK_TRY(parser_try_call); + } + if (lexer_next_is(&self->lexer, TOKEN_IDENT)) { struct node* node = malloc(sizeof(struct node)); @@ -836,9 +859,161 @@ struct node* parser_try_literal(struct parser* self) return node; } + if (lexer_next_is(&self->lexer, TOKEN_FUN)) + { + return SK_TRY(parser_try_fun); + } + return SK_TRY(parser_try_builtin); } +struct node* parser_try_call(struct parser* self) +{ + assert(self); + + if (!lexer_next_is(&self->lexer, TOKEN_IDENT)) + { + return NULL; + } + + struct node* ident = malloc(sizeof(struct node)); + node_init(ident, NODE_IDENT, lexer_try_new_next(&self->lexer)); + + struct token* tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_BEGIN, "", self->lexer.context.line); + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_CALL, tok); + + node_push_new_child(node, ident); + + if (!lexer_next_is(&self->lexer, TOKEN_OPAR)) + { + node_free(node); free(node); + return NULL; + } + lexer_consume_next(&self->lexer); + + struct node* args = SK_TRY(parser_try_args); + + if (!args) + { + node_free(node); free(node); + return NULL; + } + + node_push_new_child(node, args); + if (!lexer_next_is(&self->lexer, TOKEN_CPAR)) + { + node_free(node); free(node); + return NULL; + } + lexer_consume_next(&self->lexer); + + return node; +} + +struct node* parser_try_args(struct parser* self) +{ + struct node* node = malloc(sizeof(struct node)); + struct token* tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_OPAR, "", self->lexer.context.line); + node_init(node, NODE_ARGS, tok); + + while (true) + { + struct node* expr = SK_TRY(parser_try_expr); + + if (!expr) + { + break; + } + + node_push_new_child(node, expr); + if (lexer_next_is(&self->lexer, TOKEN_COMMA)) + { + lexer_consume_next(&self->lexer); + } + else + { + break; + } + } + + return node; +} + +struct node* parser_try_fun(struct parser* self) +{ + if (!lexer_next_is(&self->lexer, TOKEN_FUN)) + { + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_FUN, lexer_try_new_next(&self->lexer)); + + if (!lexer_next_is(&self->lexer, TOKEN_OPAR)) + { + node_free(node); free(node); + return NULL; + } + lexer_consume_next(&self->lexer); + + struct node* params = SK_TRY(parser_try_params); + node_push_new_child(node, params); + + if (!lexer_next_is(&self->lexer, TOKEN_CPAR)) + { + node_free(node); free(node); + return NULL; + } + lexer_consume_next(&self->lexer); + + struct node* body = SK_TRY(parser_try_inner_block); + node_push_new_child(node, body); + + if (!lexer_next_is(&self->lexer, TOKEN_END)) + { + node_free(node); free(node); + return NULL; + } + lexer_consume_next(&self->lexer); + + + return node; +} + +struct node* parser_try_params(struct parser* self) +{ + struct node* node = malloc(sizeof(struct node)); + struct token* tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_OPAR, "", self->lexer.context.line); + node_init(node, NODE_PARAMS, tok); + + while (true) + { + struct node* expr = SK_TRY(parser_try_expr); + + if (!expr) + { + break; + } + + node_push_new_child(node, expr); + if (lexer_next_is(&self->lexer, TOKEN_COMMA)) + { + lexer_consume_next(&self->lexer); + } + else + { + break; + } + } + + return node; +} + struct node* parser_try_builtin(struct parser* self) { if (lexer_next_is(&self->lexer, TOKEN_BOOL)) diff --git a/lib/src/prog.c b/lib/src/prog.c index dcea0ff..f7d2696 100644 --- a/lib/src/prog.c +++ b/lib/src/prog.c @@ -20,6 +20,32 @@ void prog_free(struct prog* self) vec_free(&self->constants); } +struct prog* prog_new_clone(struct prog* self) +{ + assert(self); + struct prog* clone = malloc(sizeof(struct prog)); + prog_init(clone); + + for (size_t i=0; iopcodes.size; i++) + { + vec_push(&clone->opcodes, self->opcodes.data[i]); + } + + for (size_t i=0; iparams.size; i++) + { + vec_push(&clone->params, self->params.data[i]); + } + + for (size_t i=0; iconstants.size; i++) + { + vec_push(&clone->constants, value_new_clone( + self->constants.data[i]) + ); + } + + return clone; +} + size_t prog_add_instr(struct prog* self, Opcode opcode, size_t param) diff --git a/lib/src/state.c b/lib/src/state.c index 2cc43c9..ae06b9c 100644 --- a/lib/src/state.c +++ b/lib/src/state.c @@ -66,6 +66,13 @@ void state_push_frame(struct state* self) vec_push(&self->frames, frame); } +void state_pop_frame(struct state* self) +{ + assert(self); + struct frame* frame = vec_pop(&self->frames); + frame_free(frame); + free(frame); +} bool state_has_top(struct state* self) { struct frame* frame = state_frame(self); @@ -165,6 +172,14 @@ SK state_push_string(struct state* self, char const* str, int line) return state_push(self, TYPE_STRING, val ,line); } +SK state_push_fun(struct state* self, struct fun* fun, int line) +{ + assert(self); + union val val; + val.fun = fun; + return state_push(self, TYPE_FUN, val ,line); +} + struct local* state_try_get_local(struct state* self, int id) { struct frame* frame = state_frame(self); @@ -219,7 +234,8 @@ void state_local_load(struct state* self, } } - assert(0); + fprintf(stderr, "cannot load %d\n", id); + abort(); } TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs) @@ -618,3 +634,15 @@ SK state_eq(struct state* self) state_line(self, lhs) ); } + +void state_call(struct state* self) +{ + assert(self); + state_push_frame(self); +} + +void state_ret(struct state* self) +{ + assert(self); + state_pop_frame(self); +} diff --git a/lib/src/value.c b/lib/src/value.c index 93cb9e9..1ccfa90 100644 --- a/lib/src/value.c +++ b/lib/src/value.c @@ -16,10 +16,64 @@ void value_init(struct value* self, void value_free(struct value* self) { assert(self); + if (self->type == TYPE_STRING) { free(self->val.str); } + + if (self->type == TYPE_FUN) + { + fun_free(self->val.fun); + free(self->val.fun); + } +} + +struct value* value_new_clone(struct value* self) +{ + assert(self); + + struct value* clone = malloc(sizeof(struct value)); + union val val; + + switch (self->type) + { + case TYPE_INT: { + val.integer = self->val.integer; + value_init(clone, TYPE_INT, val, self->line); + return clone; + } break; + + case TYPE_FLOAT: { + val.real = self->val.real; + value_init(clone, TYPE_FLOAT, val, self->line); + return clone; + } break; + + case TYPE_BOOL: { + val.boolean = self->val.boolean; + value_init(clone, TYPE_BOOL, val, self->line); + return clone; + } break; + + case TYPE_STRING: { + val.str = self->val.str; + value_init(clone, TYPE_STRING, val, self->line); + return clone; + } break; + + case TYPE_FUN: { + val.fun = fun_new_clone(self->val.fun); + value_init(clone, TYPE_FUN, val, self->line); + return clone; + } break; + + default: { + fprintf(stderr, "cannot clone value '%s'\n", + TypeKindStr[self->type]); + abort(); + } break; + } } bool value_equals(struct value* self, struct value* rhs) @@ -47,6 +101,10 @@ void value_str(struct value* self, struct str* dest) switch (self->type) { + case TYPE_FUN: { + str_format(dest, "", self->val.fun); + } break; + case TYPE_STRING: { str_format(dest, "%s", self->val.str); } break; diff --git a/tests/lexer.h b/tests/lexer.h index 64b5beb..24e4ffd 100644 --- a/tests/lexer.h +++ b/tests/lexer.h @@ -134,6 +134,16 @@ static void test_lexer_cond() "ELSE" ); } + +static void test_lexer_function() +{ + test_lexer("fun return,", 3, + "FUN", + "RETURN", + "COMMA" + ); +} + void register_lexer() { CU_pSuite suite = CU_add_suite("Lexer", 0, 0); @@ -146,6 +156,7 @@ void register_lexer() CU_add_test(suite, "Var Declarations", test_lexer_decl); CU_add_test(suite, "Blocks", test_lexer_block); CU_add_test(suite, "Conditionnals", test_lexer_cond); + CU_add_test(suite, "Functions", test_lexer_function); } #endif diff --git a/tests/parser.h b/tests/parser.h index 666d202..a7cf199 100644 --- a/tests/parser.h +++ b/tests/parser.h @@ -127,6 +127,26 @@ static void test_parser_if() "if true 0 else if false 1 else 2 end"); } +static void test_parser_function() +{ + test_parser("ROOT(FUN(PARAMS(IDENT[x],IDENT[y])," + "BLOCK(INT[0],INT[1])))", + "fun (x, y) 0 1 end"); + + test_parser("ROOT(FUN(PARAMS," + "BLOCK(INT[0],INT[1])))", + "fun () 0 1 end"); + + test_parser("ROOT(FUN(PARAMS,BLOCK))", + "fun () end"); + + test_parser("ROOT(CALL(IDENT[hello],ARGS))", + "hello()"); + + test_parser("ROOT(CALL(IDENT[hello],ARGS(IDENT[x],INT[2])))", + "hello(x, 2)"); +} + void register_parser() { CU_pSuite suite = CU_add_suite("Parser", 0, 0); @@ -139,6 +159,7 @@ void register_parser() CU_add_test(suite, "Assignments", test_parser_assign); CU_add_test(suite, "Blocks", test_parser_block); CU_add_test(suite, "IfExpression", test_parser_if); + CU_add_test(suite, "Functions", test_parser_function); } #endif