basic user defined functions.

main
bog 2024-04-02 22:44:34 +02:00
parent 74e5f5225d
commit 0a3d7d4aea
21 changed files with 525 additions and 7 deletions

View File

@ -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

28
features/fun.sk Normal file
View File

@ -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

View File

@ -24,6 +24,7 @@ add_library(skopy-lib SHARED
src/errors.c
src/sym.c
src/fun.c
)
file(GLOB_RECURSE

15
lib/include/fun.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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; i<args->children.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; i<args->children.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);
}

View File

@ -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(&params);
SK function = state_pop(state);
for (size_t i=0; i<param; i++)
{
SK arg = state_pop(state);
vec_push(&params, (void*) value_new_clone(state_try_get_value(state, arg)));
}
struct value* f = state_try_get_value(state, function);
state_call(state);
for (size_t i=0; i<params.size; i++)
{
size_t k = params.size - 1 - i;
struct value* val = params.data[k];
state_push(state, val->type, val->val, val->line);
state_local_store(state, i+1);
}
vec_free_elements(&params, NULL);
vec_free(&params);
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,

25
lib/src/fun.c Normal file
View File

@ -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;
}

View File

@ -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);

View File

@ -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())

View File

@ -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))

View File

@ -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; i<self->opcodes.size; i++)
{
vec_push(&clone->opcodes, self->opcodes.data[i]);
}
for (size_t i=0; i<self->params.size; i++)
{
vec_push(&clone->params, self->params.data[i]);
}
for (size_t i=0; i<self->constants.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)

View File

@ -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);
}

View File

@ -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, "<fun:%p>", self->val.fun);
} break;
case TYPE_STRING: {
str_format(dest, "%s", self->val.str);
} break;

View File

@ -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

View File

@ -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