✨ basic user defined functions.
parent
74e5f5225d
commit
0a3d7d4aea
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -24,6 +24,7 @@ add_library(skopy-lib SHARED
|
|||
src/errors.c
|
||||
|
||||
src/sym.c
|
||||
src/fun.c
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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; i<param; i++)
|
||||
{
|
||||
SK arg = state_pop(state);
|
||||
vec_push(¶ms, (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(¶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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
|
|
175
lib/src/parser.c
175
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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue