✨ basic user defined functions.
parent
74e5f5225d
commit
0a3d7d4aea
|
@ -7,6 +7,7 @@ EXPR ::=
|
||||||
| ASSIGN
|
| ASSIGN
|
||||||
| BLOCK
|
| BLOCK
|
||||||
| IF
|
| IF
|
||||||
|
| return EXPR
|
||||||
IF ::= if EXPR BLOCK (else (BLOCK | IF))?
|
IF ::= if EXPR BLOCK (else (BLOCK | IF))?
|
||||||
BLOCK ::= begin EXPR* end
|
BLOCK ::= begin EXPR* end
|
||||||
ASSIGN ::= ident assign EXPR
|
ASSIGN ::= ident assign EXPR
|
||||||
|
@ -26,6 +27,12 @@ LITERAL ::=
|
||||||
| BUILTIN
|
| BUILTIN
|
||||||
| opar EXPR cpar
|
| opar EXPR cpar
|
||||||
| ident
|
| ident
|
||||||
|
| FUN
|
||||||
|
| CALL
|
||||||
|
CALL ::= ident opar ARGS cpar
|
||||||
|
ARGS ::= (EXPR (comma EXPR)*)?
|
||||||
|
FUN ::= fun opar PARAMS cpar EXPR* end
|
||||||
|
PARAMS ::= (EXPR (comma EXPR)*)?
|
||||||
BUILTIN ::=
|
BUILTIN ::=
|
||||||
| int
|
| int
|
||||||
| bool
|
| 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/errors.c
|
||||||
|
|
||||||
src/sym.c
|
src/sym.c
|
||||||
|
src/fun.c
|
||||||
)
|
)
|
||||||
|
|
||||||
file(GLOB_RECURSE
|
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_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \
|
||||||
G(NODE_EQUAL), G(NODE_NOT_EQUAL), G(NODE_VAR_DECL), \
|
G(NODE_EQUAL), G(NODE_NOT_EQUAL), G(NODE_VAR_DECL), \
|
||||||
G(NODE_CONST_DECL), G(NODE_IDENT), G(NODE_ASSIGN), \
|
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);
|
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_not(struct parser* self);
|
||||||
struct node* parser_try_pow(struct parser* self);
|
struct node* parser_try_pow(struct parser* self);
|
||||||
struct node* parser_try_literal(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);
|
struct node* parser_try_builtin(struct parser* self);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,7 +12,8 @@ G(OP_DIV), G(OP_MOD), G(OP_POW), \
|
||||||
G(OP_USUB), G(OP_ASSERT_EQ), \
|
G(OP_USUB), G(OP_ASSERT_EQ), \
|
||||||
G(OP_NOT), G(OP_AND), G(OP_OR), \
|
G(OP_NOT), G(OP_AND), G(OP_OR), \
|
||||||
G(OP_BR), G(OP_BRF), G(OP_LT), G(OP_GT), \
|
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);
|
SK_ENUM_H(Opcode, OPCODE);
|
||||||
|
|
||||||
|
@ -26,6 +27,8 @@ struct prog
|
||||||
void prog_init(struct prog* self);
|
void prog_init(struct prog* self);
|
||||||
void prog_free(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,
|
size_t prog_add_instr(struct prog* self,
|
||||||
Opcode opcode,
|
Opcode opcode,
|
||||||
size_t param);
|
size_t param);
|
||||||
|
|
|
@ -46,6 +46,7 @@ void state_free(struct state* self);
|
||||||
|
|
||||||
struct frame* state_frame(struct state* self);
|
struct frame* state_frame(struct state* self);
|
||||||
void state_push_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);
|
bool state_has_top(struct state* self);
|
||||||
SK state_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_bool(struct state* self, bool boolean, int line);
|
||||||
SK state_push_float(struct state* self, double real, 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_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);
|
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_gt(struct state* self);
|
||||||
SK state_eq(struct state* self);
|
SK state_eq(struct state* self);
|
||||||
|
|
||||||
|
void state_call(struct state* self);
|
||||||
|
void state_ret(struct state* self);
|
||||||
#endif
|
#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_LT), G(TOKEN_LE), G(TOKEN_GT), G(TOKEN_GE), \
|
||||||
G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL), G(TOKEN_VAR), \
|
G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL), G(TOKEN_VAR), \
|
||||||
G(TOKEN_CONST), G(TOKEN_ASSIGN), G(TOKEN_IDENT), \
|
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);
|
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
|
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
#include "fun.h"
|
||||||
|
|
||||||
#define TYPE_KIND(G) \
|
#define TYPE_KIND(G) \
|
||||||
G(TYPE_INT), G(TYPE_BOOL), G(TYPE_FLOAT), \
|
G(TYPE_INT), G(TYPE_BOOL), G(TYPE_FLOAT), \
|
||||||
G(TYPE_STRING)
|
G(TYPE_STRING), G(TYPE_FUN)
|
||||||
|
|
||||||
SK_ENUM_H(TypeKind, TYPE_KIND);
|
SK_ENUM_H(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ union val
|
||||||
double real;
|
double real;
|
||||||
bool boolean;
|
bool boolean;
|
||||||
char* str;
|
char* str;
|
||||||
|
struct fun* fun;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct value
|
struct value
|
||||||
|
@ -32,6 +34,8 @@ void value_init(struct value* self,
|
||||||
|
|
||||||
void value_free(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);
|
bool value_equals(struct value* self, struct value* rhs);
|
||||||
|
|
||||||
void value_str(struct value* self, struct str* dest);
|
void value_str(struct value* self, struct str* dest);
|
||||||
|
|
|
@ -40,6 +40,20 @@ void compiler_compile(struct compiler* self,
|
||||||
sym_close_scope(sym);
|
sym_close_scope(sym);
|
||||||
} break;
|
} 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: {
|
case NODE_IF: {
|
||||||
struct vec to_end;
|
struct vec to_end;
|
||||||
vec_init(&to_end);
|
vec_init(&to_end);
|
||||||
|
@ -133,6 +147,40 @@ void compiler_compile(struct compiler* self,
|
||||||
assert(symbol);
|
assert(symbol);
|
||||||
prog_add_instr(prog, OP_LOCAL_LOAD, symbol->id);
|
prog_add_instr(prog, OP_LOCAL_LOAD, symbol->id);
|
||||||
} break;
|
} 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: {
|
case NODE_BOOL: {
|
||||||
union val val;
|
union val val;
|
||||||
val.boolean =
|
val.boolean =
|
||||||
|
@ -264,7 +312,6 @@ void compiler_compile_value(struct compiler* self,
|
||||||
(void) self;
|
(void) self;
|
||||||
struct value* value = malloc(sizeof(struct value));
|
struct value* value = malloc(sizeof(struct value));
|
||||||
value_init(value, type, val, node->token->line);
|
value_init(value, type, val, node->token->line);
|
||||||
|
|
||||||
size_t idx = prog_add_constant(prog, value);
|
size_t idx = prog_add_constant(prog, value);
|
||||||
prog_add_instr(prog, OP_PUSH, idx);
|
prog_add_instr(prog, OP_PUSH, idx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "exec.h"
|
#include "exec.h"
|
||||||
|
#include "fun.h"
|
||||||
|
|
||||||
void exec_init(struct exec* self)
|
void exec_init(struct exec* self)
|
||||||
{
|
{
|
||||||
|
@ -72,10 +73,65 @@ void exec_execute(struct exec* self,
|
||||||
self->pc++;
|
self->pc++;
|
||||||
} break;
|
} 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: {
|
case OP_PUSH: {
|
||||||
struct value* constant = prog->constants.data[param];
|
struct value* constant = prog->constants.data[param];
|
||||||
switch (constant->type)
|
switch (constant->type)
|
||||||
{
|
{
|
||||||
|
case TYPE_FUN: {
|
||||||
|
state_push_fun(
|
||||||
|
state,
|
||||||
|
fun_new_clone(constant->val.fun),
|
||||||
|
constant->line
|
||||||
|
);
|
||||||
|
} break;
|
||||||
|
|
||||||
case TYPE_INT: {
|
case TYPE_INT: {
|
||||||
state_push_int(
|
state_push_int(
|
||||||
state,
|
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 == '>'
|
|| 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_GT);
|
||||||
SK_SCAN_TEXT("<", TOKEN_LT);
|
SK_SCAN_TEXT("<", TOKEN_LT);
|
||||||
SK_SCAN_TEXT("=", TOKEN_ASSIGN);
|
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("if", TOKEN_IF, NULL);
|
||||||
SK_SCAN_KEYWORD("else", TOKEN_ELSE, NULL);
|
SK_SCAN_KEYWORD("else", TOKEN_ELSE, NULL);
|
||||||
SK_SCAN_KEYWORD("begin", TOKEN_BEGIN, NULL);
|
SK_SCAN_KEYWORD("begin", TOKEN_BEGIN, NULL);
|
||||||
|
|
|
@ -61,7 +61,6 @@ int module_compile(struct module* self)
|
||||||
|
|
||||||
struct state state;
|
struct state state;
|
||||||
state_init(&state);
|
state_init(&state);
|
||||||
|
|
||||||
exec_execute(&exec, &state, &self->prog);
|
exec_execute(&exec, &state, &self->prog);
|
||||||
|
|
||||||
if (!errors_ok())
|
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)
|
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))
|
if (lexer_next_is(&self->lexer, TOKEN_ASSERT))
|
||||||
{
|
{
|
||||||
return SK_TRY(parser_try_assert);
|
return SK_TRY(parser_try_assert);
|
||||||
|
@ -825,6 +842,12 @@ struct node* parser_try_literal(struct parser* self)
|
||||||
return node;
|
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))
|
if (lexer_next_is(&self->lexer, TOKEN_IDENT))
|
||||||
{
|
{
|
||||||
struct node* node = malloc(sizeof(struct node));
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
@ -836,9 +859,161 @@ struct node* parser_try_literal(struct parser* self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lexer_next_is(&self->lexer, TOKEN_FUN))
|
||||||
|
{
|
||||||
|
return SK_TRY(parser_try_fun);
|
||||||
|
}
|
||||||
|
|
||||||
return SK_TRY(parser_try_builtin);
|
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)
|
struct node* parser_try_builtin(struct parser* self)
|
||||||
{
|
{
|
||||||
if (lexer_next_is(&self->lexer, TOKEN_BOOL))
|
if (lexer_next_is(&self->lexer, TOKEN_BOOL))
|
||||||
|
|
|
@ -20,6 +20,32 @@ void prog_free(struct prog* self)
|
||||||
vec_free(&self->constants);
|
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,
|
size_t prog_add_instr(struct prog* self,
|
||||||
Opcode opcode,
|
Opcode opcode,
|
||||||
size_t param)
|
size_t param)
|
||||||
|
|
|
@ -66,6 +66,13 @@ void state_push_frame(struct state* self)
|
||||||
vec_push(&self->frames, frame);
|
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)
|
bool state_has_top(struct state* self)
|
||||||
{
|
{
|
||||||
struct frame* frame = state_frame(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);
|
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 local* state_try_get_local(struct state* self, int id)
|
||||||
{
|
{
|
||||||
struct frame* frame = state_frame(self);
|
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)
|
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)
|
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)
|
void value_free(struct value* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
if (self->type == TYPE_STRING)
|
if (self->type == TYPE_STRING)
|
||||||
{
|
{
|
||||||
free(self->val.str);
|
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)
|
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)
|
switch (self->type)
|
||||||
{
|
{
|
||||||
|
case TYPE_FUN: {
|
||||||
|
str_format(dest, "<fun:%p>", self->val.fun);
|
||||||
|
} break;
|
||||||
|
|
||||||
case TYPE_STRING: {
|
case TYPE_STRING: {
|
||||||
str_format(dest, "%s", self->val.str);
|
str_format(dest, "%s", self->val.str);
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -134,6 +134,16 @@ static void test_lexer_cond()
|
||||||
"ELSE"
|
"ELSE"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_lexer_function()
|
||||||
|
{
|
||||||
|
test_lexer("fun return,", 3,
|
||||||
|
"FUN",
|
||||||
|
"RETURN",
|
||||||
|
"COMMA"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void register_lexer()
|
void register_lexer()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
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, "Var Declarations", test_lexer_decl);
|
||||||
CU_add_test(suite, "Blocks", test_lexer_block);
|
CU_add_test(suite, "Blocks", test_lexer_block);
|
||||||
CU_add_test(suite, "Conditionnals", test_lexer_cond);
|
CU_add_test(suite, "Conditionnals", test_lexer_cond);
|
||||||
|
CU_add_test(suite, "Functions", test_lexer_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -127,6 +127,26 @@ static void test_parser_if()
|
||||||
"if true 0 else if false 1 else 2 end");
|
"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()
|
void register_parser()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
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, "Assignments", test_parser_assign);
|
||||||
CU_add_test(suite, "Blocks", test_parser_block);
|
CU_add_test(suite, "Blocks", test_parser_block);
|
||||||
CU_add_test(suite, "IfExpression", test_parser_if);
|
CU_add_test(suite, "IfExpression", test_parser_if);
|
||||||
|
CU_add_test(suite, "Functions", test_parser_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue