2021-12-10 20:22:16 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
#include "apfl.h"
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
#include "alloc.h"
|
2021-12-10 20:22:16 +00:00
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *expr)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
switch (expr->type) {
|
|
|
|
|
case APFL_EXPR_LIST:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_list_deinit(allocator, &expr->list);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_DICT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_dict_deinit(allocator, &expr->dict);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CALL:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_call_deinit(allocator, &expr->call);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
2022-04-11 20:44:04 +00:00
|
|
|
apfl_expr_body_deinit(allocator, &expr->simple_func);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_complex_func_deinit(allocator, &expr->complex_func);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNMENT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignment_deinit(allocator, &expr->assignment);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_DOT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_dot_deinit(allocator, &expr->dot);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_AT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_at_deinit(allocator, &expr->at);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONSTANT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_const_deinit(allocator, &expr->constant);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_VAR:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
apfl_string_deinit(allocator, &expr->var);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_BLANK:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *list)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_list_item_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *item)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DESTROY(allocator, item->expr, apfl_expr_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *pair)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DESTROY(allocator, pair->k, apfl_expr_deinit);
|
|
|
|
|
DESTROY(allocator, pair->v, apfl_expr_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *dict)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_CAP_LIST(allocator, dict->items, dict->len, dict->cap, apfl_expr_dict_pair_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *call)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DESTROY(allocator, call->callee, apfl_expr_deinit);
|
|
|
|
|
apfl_expr_list_deinit(allocator, &call->arguments);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-04-11 20:44:04 +00:00
|
|
|
apfl_expr_body_deinit(struct apfl_allocator allocator, struct apfl_expr_body *body)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-04-11 20:44:04 +00:00
|
|
|
DEINIT_CAP_LIST(allocator, body->items, body->len, body->cap, apfl_expr_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *constant)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
switch (constant->type) {
|
|
|
|
|
case APFL_EXPR_CONST_NIL:
|
|
|
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
|
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_STRING:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
apfl_string_deinit(allocator, &constant->string);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
#define DEINIT_GENERIC_LHS_RHS_EXPR(allocator, x, lhs_deiniter) \
|
|
|
|
|
do { \
|
|
|
|
|
DESTROY(allocator, x->lhs, lhs_deiniter); \
|
|
|
|
|
DESTROY(allocator, x->rhs, apfl_expr_deinit); \
|
2021-12-10 20:22:16 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *pred)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_param_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *params)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_CAP_LIST(allocator, params->params, params->len, params->cap, apfl_expr_params_item_deinit);
|
2022-01-07 22:08:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *item)
|
2022-01-07 22:08:25 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_param_deinit(allocator, &item->param);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *param)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
switch (param->type) {
|
|
|
|
|
case APFL_EXPR_PARAM_VAR:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
apfl_string_deinit(allocator, ¶m->var);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_const_deinit(allocator, ¶m->constant);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_param_predicate_deinit(allocator, ¶m->predicate);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_LIST:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_params_deinit(allocator, ¶m->list);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *subfunc)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_params_deinit(allocator, &subfunc->params);
|
2022-04-11 20:44:04 +00:00
|
|
|
apfl_expr_body_deinit(allocator, &subfunc->body);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *cf)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_CAP_LIST(allocator, cf->subfuncs, cf->len, cf->cap, apfl_expr_subfunc_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *pred)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_assignable_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *item)
|
2022-01-08 21:55:24 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_deinit(allocator, &item->assignable);
|
2022-01-08 21:55:24 +00:00
|
|
|
}
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *list)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_assignable_list_item_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *dot)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DESTROY(allocator, dot->lhs, apfl_expr_assignable_var_or_member_deinit);
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
apfl_string_deinit(allocator, &dot->rhs);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-07 22:08:25 +00:00
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *at)
|
2022-01-07 22:08:25 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DESTROY(allocator, at->lhs, apfl_expr_assignable_var_or_member_deinit);
|
|
|
|
|
DESTROY(allocator, at->rhs, apfl_expr_deinit);
|
2022-01-07 22:08:25 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *var_or_member)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-08 21:55:24 +00:00
|
|
|
switch (var_or_member->type) {
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
apfl_string_deinit(allocator, &var_or_member->var);
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_var_or_member_dot_deinit(allocator, &var_or_member->dot);
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_var_or_member_at_deinit(allocator, &var_or_member->at);
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *a)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
switch (a->type) {
|
2022-01-08 21:55:24 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_var_or_member_deinit(allocator, &a->var_or_member);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_const_deinit(allocator, &a->constant);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_predicate_deinit(allocator, &a->predicate);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_list_deinit(allocator, &a->list);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *a)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_assignable_deinit(allocator, &a->lhs);
|
|
|
|
|
DESTROY(allocator, a->rhs, apfl_expr_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *dot)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DESTROY(allocator, dot->lhs, apfl_expr_deinit);
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
apfl_string_deinit(allocator, &dot->rhs);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *at)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, at, apfl_expr_deinit);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move functions
|
|
|
|
|
|
|
|
|
|
struct apfl_expr
|
|
|
|
|
apfl_expr_move(struct apfl_expr *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr out = *in;
|
|
|
|
|
switch (in->type) {
|
|
|
|
|
case APFL_EXPR_LIST:
|
|
|
|
|
out.list = apfl_expr_list_move(&in->list);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_DICT:
|
|
|
|
|
out.dict = apfl_expr_dict_move(&in->dict);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CALL:
|
|
|
|
|
out.call = apfl_expr_call_move(&in->call);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
|
|
|
out.simple_func = apfl_expr_body_move(&in->simple_func);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
|
|
|
out.complex_func = apfl_expr_complex_func_move(&in->complex_func);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
|
|
|
out.assignment = apfl_expr_assignment_move(&in->assignment);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_DOT:
|
|
|
|
|
out.dot = apfl_expr_dot_move(&in->dot);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_AT:
|
|
|
|
|
out.at = apfl_expr_at_move(&in->at);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONSTANT:
|
|
|
|
|
out.constant = apfl_expr_const_move(&in->constant);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_VAR:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
out.var = apfl_string_move(&in->var);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_BLANK:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MOVE_LIST(out, in, items, len) \
|
|
|
|
|
do { \
|
|
|
|
|
MOVEPTR(out.items, in->items); \
|
|
|
|
|
out.len = in->len; \
|
2021-12-15 20:25:04 +00:00
|
|
|
in->len = 0; \
|
2021-12-10 20:22:16 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
#define MOVE_CAP_LIST(out, in, items, len, cap) \
|
|
|
|
|
do { \
|
|
|
|
|
MOVEPTR(out.items, in->items); \
|
|
|
|
|
out.len = in->len; \
|
|
|
|
|
out.cap = in->cap; \
|
|
|
|
|
in->len = 0; \
|
|
|
|
|
in->cap = 0; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
struct apfl_expr_list
|
|
|
|
|
apfl_expr_list_move(struct apfl_expr_list *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_list out;
|
|
|
|
|
MOVE_LIST(out, in, items, len);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_list_item
|
|
|
|
|
apfl_expr_list_item_move(struct apfl_expr_list_item *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_list_item out = *in;
|
|
|
|
|
in->expr = NULL;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_dict_pair
|
|
|
|
|
apfl_expr_dict_pair_move(struct apfl_expr_dict_pair *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_dict_pair out = *in;
|
|
|
|
|
in->k = NULL;
|
|
|
|
|
in->v = NULL;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_dict
|
|
|
|
|
apfl_expr_dict_move(struct apfl_expr_dict *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_dict out;
|
2022-02-08 21:53:13 +00:00
|
|
|
MOVE_CAP_LIST(out, in, items, len, cap);
|
2021-12-10 20:22:16 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_call
|
|
|
|
|
apfl_expr_call_move(struct apfl_expr_call *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_call out;
|
|
|
|
|
|
|
|
|
|
MOVEPTR(out.callee, in->callee);
|
|
|
|
|
out.arguments = apfl_expr_list_move(&in->arguments);
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-11 20:44:04 +00:00
|
|
|
struct apfl_expr_body
|
|
|
|
|
apfl_expr_body_move(struct apfl_expr_body *in)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-04-11 20:44:04 +00:00
|
|
|
struct apfl_expr_body out;
|
|
|
|
|
MOVE_CAP_LIST(out, in, items, len, cap);
|
2021-12-10 20:22:16 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_const
|
|
|
|
|
apfl_expr_const_move(struct apfl_expr_const *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_const out = *in;
|
|
|
|
|
|
|
|
|
|
switch (in->type) {
|
|
|
|
|
case APFL_EXPR_CONST_NIL:
|
|
|
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
|
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_STRING:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
out.string = apfl_string_move(&in->string);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define GENERIC_LHS_RHS_PTRS_MOVE(out, in) \
|
|
|
|
|
do { \
|
|
|
|
|
MOVEPTR(out.lhs, in->lhs); \
|
|
|
|
|
MOVEPTR(out.rhs, in->rhs); \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_param_predicate
|
|
|
|
|
apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_param_predicate out;
|
|
|
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_params
|
|
|
|
|
apfl_expr_params_move(struct apfl_expr_params *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_params out;
|
2022-02-08 21:53:13 +00:00
|
|
|
MOVE_CAP_LIST(out, in, params, len, cap);
|
2021-12-10 20:22:16 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_param
|
|
|
|
|
apfl_expr_param_move(struct apfl_expr_param *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_param out = *in;
|
|
|
|
|
switch (in->type) {
|
|
|
|
|
case APFL_EXPR_PARAM_VAR:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
out.var = apfl_string_move(&in->var);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
|
|
|
out.constant = apfl_expr_const_move(&in->constant);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
|
|
|
|
out.predicate = apfl_expr_param_predicate_move(&in->predicate);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_LIST:
|
|
|
|
|
out.list = apfl_expr_params_move(&in->list);
|
|
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_subfunc
|
|
|
|
|
apfl_expr_subfunc_move(struct apfl_expr_subfunc *in)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = apfl_expr_params_move(&in->params),
|
|
|
|
|
.body = apfl_expr_body_move(&in->body),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_complex_func
|
|
|
|
|
apfl_expr_complex_func_move(struct apfl_expr_complex_func *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_complex_func out;
|
2022-02-08 21:53:13 +00:00
|
|
|
MOVE_CAP_LIST(out, in, subfuncs, len, cap);
|
2021-12-10 20:22:16 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
struct apfl_expr_assignable_var_or_member
|
|
|
|
|
apfl_expr_assignable_var_or_member_move(struct apfl_expr_assignable_var_or_member *in)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-08 21:55:24 +00:00
|
|
|
struct apfl_expr_assignable_var_or_member out = *in;
|
|
|
|
|
switch (in->type) {
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
out.var = apfl_string_move(&in->var);
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
|
|
|
|
MOVEPTR(out.dot.lhs, in->dot.lhs);
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
out.dot.rhs = apfl_string_move(&in->dot.rhs);
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
|
|
|
MOVEPTR(out.at.lhs, in->at.lhs);
|
|
|
|
|
MOVEPTR(out.at.rhs, in->at.rhs);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
struct apfl_expr_assignable_predicate
|
|
|
|
|
apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *in)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-08 21:55:24 +00:00
|
|
|
struct apfl_expr_assignable_predicate out;
|
2021-12-10 20:22:16 +00:00
|
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_assignable_list
|
|
|
|
|
apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_assignable_list out;
|
2022-01-07 22:08:25 +00:00
|
|
|
MOVE_LIST(out, in, items, len);
|
2021-12-10 20:22:16 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_assignable
|
|
|
|
|
apfl_expr_assignable_move(struct apfl_expr_assignable *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_assignable out = *in;
|
|
|
|
|
switch (in->type) {
|
2022-01-08 21:55:24 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
|
|
|
out.var_or_member = apfl_expr_assignable_var_or_member_move(&in->var_or_member);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
|
|
|
out.constant = apfl_expr_const_move(&in->constant);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
|
|
|
|
out.predicate = apfl_expr_assignable_predicate_move(&in->predicate);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
|
|
|
|
out.list = apfl_expr_assignable_list_move(&in->list);
|
|
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
|
|
|
// nop
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_assignment
|
|
|
|
|
apfl_expr_assignment_move(struct apfl_expr_assignment *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_assignment out = *in;
|
|
|
|
|
out.lhs = apfl_expr_assignable_move(&in->lhs);
|
|
|
|
|
MOVEPTR(out.rhs, in->rhs);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_dot
|
|
|
|
|
apfl_expr_dot_move(struct apfl_expr_dot *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_dot out;
|
2022-01-08 21:55:24 +00:00
|
|
|
MOVEPTR(out.lhs, in->lhs);
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
out.rhs = apfl_string_move(&in->rhs);
|
2021-12-10 20:22:16 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_at
|
|
|
|
|
apfl_expr_at_move(struct apfl_expr_at *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_at out;
|
|
|
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define POSFMT "%d:%d"
|
|
|
|
|
#define POSARGS(p) (p).line, (p).col
|
|
|
|
|
|
|
|
|
|
static void print_expr(struct apfl_expr *expr, unsigned indent, FILE *f);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_expr_list(struct apfl_expr_list *list, unsigned indent, FILE *f)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < list->len; i++) {
|
|
|
|
|
unsigned item_indent = indent;
|
|
|
|
|
if (list->items[i].expand) {
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Expand\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
item_indent++;
|
|
|
|
|
}
|
|
|
|
|
print_expr(list->items[i].expr, item_indent, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_body(struct apfl_expr_body *body, unsigned indent, FILE *f)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < body->len; i++) {
|
|
|
|
|
print_expr(&body->items[i], indent, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-17 20:07:45 +00:00
|
|
|
static void
|
|
|
|
|
print_constant_with_pos(struct apfl_expr_const constant, struct apfl_position pos, unsigned indent, FILE *f)
|
|
|
|
|
{
|
2022-01-18 20:18:27 +00:00
|
|
|
struct apfl_string_view sv;
|
|
|
|
|
|
2021-12-17 20:07:45 +00:00
|
|
|
switch (constant.type) {
|
|
|
|
|
case APFL_EXPR_CONST_NIL:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Const (nil) @ " POSFMT "\n", POSARGS(pos));
|
2021-12-17 20:07:45 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Const (%s) @ " POSFMT "\n", constant.boolean ? "true" : "false", POSARGS(pos));
|
2021-12-17 20:07:45 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_STRING:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(constant.string);
|
|
|
|
|
apfl_print_indented(indent, f, "Const (" APFL_STR_FMT ") @ " POSFMT "\n", APFL_STR_FMT_ARGS(sv), POSARGS(pos));
|
2021-12-17 20:07:45 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_NUMBER:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Const (%f) @ " POSFMT "\n", constant.number, POSARGS(pos));
|
2021-12-17 20:07:45 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
static void
|
|
|
|
|
print_constant(struct apfl_expr_const constant, unsigned indent, FILE *f)
|
|
|
|
|
{
|
2022-01-18 20:18:27 +00:00
|
|
|
struct apfl_string_view sv;
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
switch (constant.type) {
|
|
|
|
|
case APFL_EXPR_CONST_NIL:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Const (nil)\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Const (%s)\n", constant.boolean ? "true" : "false");
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_STRING:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(constant.string);
|
|
|
|
|
apfl_print_indented(indent, f, "Const (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(sv));
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONST_NUMBER:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Const (%f)\n", constant.number);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-07 22:08:25 +00:00
|
|
|
static void print_param(struct apfl_expr_param *, unsigned, FILE *);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_params_item(struct apfl_expr_params_item *item, unsigned indent, FILE *f)
|
|
|
|
|
{
|
|
|
|
|
if (item->expand) {
|
|
|
|
|
apfl_print_indented(indent, f, "Expand\n");
|
|
|
|
|
indent++;
|
|
|
|
|
}
|
|
|
|
|
print_param(&item->param, indent, f);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
static void
|
|
|
|
|
print_param(struct apfl_expr_param *param, unsigned indent, FILE *f)
|
|
|
|
|
{
|
2022-01-18 20:18:27 +00:00
|
|
|
struct apfl_string_view sv;
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
switch (param->type) {
|
|
|
|
|
case APFL_EXPR_PARAM_VAR:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(param->var);
|
|
|
|
|
apfl_print_indented(indent, f, "Var (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(sv));
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
|
|
|
print_constant(param->constant, indent, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Predicate\n");
|
|
|
|
|
apfl_print_indented(indent+1, f, "LHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_param(param->predicate.lhs, indent+2, f);
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "RHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(param->predicate.rhs, indent+2, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_PARAM_LIST:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "List\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
for (size_t i = 0; i < param->list.len; i++) {
|
2022-01-07 22:08:25 +00:00
|
|
|
print_params_item(¶m->list.params[i], indent+1, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
|
|
|
apfl_print_indented(indent, f, "Blank (_)\n");
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
static void
|
|
|
|
|
print_assignable_var_or_member(struct apfl_expr_assignable_var_or_member var_or_member, unsigned indent, FILE *f)
|
|
|
|
|
{
|
2022-01-18 20:18:27 +00:00
|
|
|
struct apfl_string_view sv;
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
switch (var_or_member.type) {
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(var_or_member.var);
|
|
|
|
|
apfl_print_indented(indent, f, "Variable (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(sv));
|
2022-01-08 21:55:24 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(var_or_member.dot.rhs);
|
|
|
|
|
apfl_print_indented(indent, f, "Dot (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(sv));
|
2022-01-08 21:55:24 +00:00
|
|
|
print_assignable_var_or_member(*var_or_member.dot.lhs, indent+1, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
|
|
|
apfl_print_indented(indent, f, "At\n");
|
|
|
|
|
apfl_print_indented(indent+1, f, "LHS\n");
|
|
|
|
|
print_assignable_var_or_member(*var_or_member.at.lhs, indent+2, f);
|
|
|
|
|
apfl_print_indented(indent+1, f, "RHS\n");
|
|
|
|
|
print_expr(var_or_member.at.rhs, indent+2, f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
static void
|
|
|
|
|
print_assignable(struct apfl_expr_assignable assignable, unsigned indent, FILE *f)
|
|
|
|
|
{
|
|
|
|
|
switch(assignable.type) {
|
2022-01-08 21:55:24 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
|
|
|
print_assignable_var_or_member(assignable.var_or_member, indent, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
|
|
|
print_constant(assignable.constant, indent, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Predicate\n");
|
|
|
|
|
apfl_print_indented(indent+1, f, "LHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_assignable(*assignable.predicate.lhs, indent+2, f);
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "RHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(assignable.predicate.rhs, indent+2, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "List\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
for (size_t i = 0; i < assignable.list.len; i++) {
|
2022-01-07 22:08:25 +00:00
|
|
|
struct apfl_expr_assignable_list_item *item = &assignable.list.items[i];
|
|
|
|
|
unsigned indent_item = indent+1;
|
|
|
|
|
if (item->expand) {
|
|
|
|
|
apfl_print_indented(indent_item, f, "Expand\n");
|
|
|
|
|
indent_item++;
|
|
|
|
|
}
|
|
|
|
|
print_assignable(item->assignable, indent_item, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
|
|
|
apfl_print_indented(indent, f, "Blank (_)\n");
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_expr(struct apfl_expr *expr, unsigned indent, FILE *f)
|
|
|
|
|
{
|
2022-01-18 20:18:27 +00:00
|
|
|
struct apfl_string_view sv;
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
switch (expr->type) {
|
|
|
|
|
case APFL_EXPR_LIST:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "List @ " POSFMT "\n", POSARGS(expr->position));
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr_list(&expr->list, indent+1, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_DICT:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Dict @ " POSFMT "\n", POSARGS(expr->position));
|
2021-12-10 20:22:16 +00:00
|
|
|
for (size_t i = 0; i < expr->dict.len; i++) {
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "Dict item\n");
|
|
|
|
|
apfl_print_indented(indent+2, f, "Key\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->dict.items[i].k, indent+3, f);
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+2, f, "Value\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->dict.items[i].v, indent+3, f);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CALL:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Call @ " POSFMT "\n", POSARGS(expr->position));
|
|
|
|
|
apfl_print_indented(indent+1, f, "Callee\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->call.callee, indent+2, f);
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "Args\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr_list(&expr->call.arguments, indent+2, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Simple function @ " POSFMT "\n", POSARGS(expr->position));
|
2022-04-11 20:44:04 +00:00
|
|
|
print_body(&expr->simple_func, indent+1, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Complex function @ " POSFMT "\n", POSARGS(expr->position));
|
2021-12-10 20:22:16 +00:00
|
|
|
for (size_t i = 0; i < expr->complex_func.len; i++) {
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "Subfunction\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
struct apfl_expr_subfunc *sub = &expr->complex_func.subfuncs[i];
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+2, f, "Parameters\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
for (size_t j = 0; j < sub->params.len; j++) {
|
2022-01-07 22:08:25 +00:00
|
|
|
print_params_item(&sub->params.params[j], indent+3, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+2, f, "Body\n");
|
2022-04-11 20:44:04 +00:00
|
|
|
print_body(&sub->body, indent+3, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_ASSIGNMENT:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "Assignment\n");
|
|
|
|
|
apfl_print_indented(indent+1, f, "LHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_assignable(expr->assignment.lhs, indent+2, f);
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "RHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->assignment.rhs, indent+2, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_DOT:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(expr->dot.rhs);
|
|
|
|
|
apfl_print_indented(indent, f, "Dot (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(sv));
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->dot.lhs, indent+1, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_AT:
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent, f, "At\n");
|
|
|
|
|
apfl_print_indented(indent+1, f, "LHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->at.lhs, indent+2, f);
|
2022-01-02 15:53:26 +00:00
|
|
|
apfl_print_indented(indent+1, f, "RHS\n");
|
2021-12-10 20:22:16 +00:00
|
|
|
print_expr(expr->at.rhs, indent+2, f);
|
|
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_CONSTANT:
|
2021-12-17 20:07:45 +00:00
|
|
|
print_constant_with_pos(expr->constant, expr->position, indent, f);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_EXPR_VAR:
|
2022-01-18 20:18:27 +00:00
|
|
|
sv = apfl_string_view_from(expr->var);
|
|
|
|
|
apfl_print_indented(indent, f, "Var (" APFL_STR_FMT ") @ " POSFMT "\n", APFL_STR_FMT_ARGS(sv), POSARGS(expr->position));
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_BLANK:
|
|
|
|
|
apfl_print_indented(indent, f, "Blank (_)\n");
|
|
|
|
|
break;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
apfl_expr_print(struct apfl_expr expr, FILE *f)
|
|
|
|
|
{
|
|
|
|
|
print_expr(&expr, 0, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
expr_list_eq(struct apfl_expr_list a, struct apfl_expr_list b)
|
|
|
|
|
{
|
|
|
|
|
if (a.len != b.len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < a.len; i++) {
|
|
|
|
|
if (
|
|
|
|
|
a.items[i].expand != b.items[i].expand
|
|
|
|
|
|| !apfl_expr_eq(*a.items[i].expr, *b.items[i].expr)
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-04-11 20:44:04 +00:00
|
|
|
body_eq(struct apfl_expr_body a, struct apfl_expr_body b)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-04-11 20:44:04 +00:00
|
|
|
if (a.len != b.len) {
|
2022-01-22 16:16:28 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-11 20:44:04 +00:00
|
|
|
for (size_t i = 0; i < a.len; i++) {
|
|
|
|
|
if (!apfl_expr_eq(a.items[i], b.items[i])) {
|
2021-12-10 20:22:16 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool param_eq(struct apfl_expr_param, struct apfl_expr_param);
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
params_eq(struct apfl_expr_params a, struct apfl_expr_params b)
|
|
|
|
|
{
|
|
|
|
|
if (a.len != b.len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < a.len; i++) {
|
2022-01-07 22:08:25 +00:00
|
|
|
if (
|
|
|
|
|
a.params[i].expand != b.params[i].expand
|
|
|
|
|
|| !param_eq(a.params[i].param, b.params[i].param)
|
|
|
|
|
) {
|
2021-12-10 20:22:16 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
const_eq(struct apfl_expr_const a, struct apfl_expr_const b)
|
|
|
|
|
{
|
|
|
|
|
if (a.type != b.type) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a.type) {
|
|
|
|
|
case APFL_EXPR_CONST_NIL:
|
|
|
|
|
return true;
|
|
|
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
|
|
|
return a.boolean == b.boolean;
|
|
|
|
|
case APFL_EXPR_CONST_STRING:
|
2022-01-02 15:51:19 +00:00
|
|
|
return apfl_string_eq(a.string, b.string);
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
|
|
|
return a.number == b.number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
param_eq(struct apfl_expr_param a, struct apfl_expr_param b)
|
|
|
|
|
{
|
|
|
|
|
if (a.type != b.type) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a.type) {
|
|
|
|
|
case APFL_EXPR_PARAM_VAR:
|
2022-01-02 15:51:19 +00:00
|
|
|
return apfl_string_eq(a.var, b.var);
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
|
|
|
return const_eq(a.constant, b.constant);
|
|
|
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
|
|
|
|
return param_eq(*a.predicate.lhs, *b.predicate.lhs)
|
|
|
|
|
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
|
|
|
|
|
case APFL_EXPR_PARAM_LIST:
|
|
|
|
|
return params_eq(a.list, b.list);
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
|
|
|
return true;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-01-08 21:55:24 +00:00
|
|
|
assignable_var_or_member_eq(struct apfl_expr_assignable_var_or_member a, struct apfl_expr_assignable_var_or_member b)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (a.type != b.type) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a.type) {
|
2022-01-08 21:55:24 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
2022-01-02 15:51:19 +00:00
|
|
|
return apfl_string_eq(a.var, b.var);
|
2022-01-08 21:55:24 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
|
|
|
|
return assignable_var_or_member_eq(*a.dot.lhs, *b.dot.lhs)
|
|
|
|
|
&& apfl_string_eq(a.dot.rhs, b.dot.rhs);
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
|
|
|
return assignable_var_or_member_eq(*a.at.lhs, *b.at.lhs)
|
|
|
|
|
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
|
|
|
|
|
{
|
|
|
|
|
if (a.type != b.type) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a.type) {
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
|
|
|
return assignable_var_or_member_eq(a.var_or_member, b.var_or_member);
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
|
|
|
return const_eq(a.constant, b.constant);
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
|
|
|
|
return assignable_eq(*a.predicate.lhs, *b.predicate.lhs)
|
|
|
|
|
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
|
|
|
|
if (a.list.len != b.list.len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < a.list.len; i++) {
|
2022-01-07 22:08:25 +00:00
|
|
|
if (
|
|
|
|
|
a.list.items[i].expand != b.list.items[i].expand
|
|
|
|
|
|| !assignable_eq(a.list.items[i].assignable, b.list.items[i].assignable)
|
|
|
|
|
) {
|
2021-12-10 20:22:16 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
|
|
|
return true;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
apfl_expr_eq(struct apfl_expr a, struct apfl_expr b)
|
|
|
|
|
{
|
|
|
|
|
if (a.type != b.type) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 22:42:11 +00:00
|
|
|
if (!apfl_position_eq(a.position, b.position)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a.type) {
|
|
|
|
|
case APFL_EXPR_LIST:
|
|
|
|
|
return expr_list_eq(a.list, b.list);
|
|
|
|
|
case APFL_EXPR_DICT:
|
|
|
|
|
if (a.dict.len != b.dict.len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < a.dict.len; i++) {
|
|
|
|
|
if (
|
|
|
|
|
!apfl_expr_eq(*a.dict.items[i].k, *b.dict.items[i].k)
|
|
|
|
|
|| !apfl_expr_eq(*a.dict.items[i].v, *b.dict.items[i].v)
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
case APFL_EXPR_CALL:
|
|
|
|
|
return apfl_expr_eq(*a.call.callee, *b.call.callee)
|
|
|
|
|
&& expr_list_eq(a.call.arguments, b.call.arguments);
|
|
|
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
|
|
|
return body_eq(a.simple_func, b.simple_func);
|
|
|
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
|
|
|
if (a.complex_func.len != b.complex_func.len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < a.complex_func.len; i++) {
|
|
|
|
|
if (
|
|
|
|
|
!params_eq(a.complex_func.subfuncs[i].params, b.complex_func.subfuncs[i].params)
|
|
|
|
|
|| !body_eq(a.complex_func.subfuncs[i].body, b.complex_func.subfuncs[i].body)
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
|
|
|
return assignable_eq(a.assignment.lhs, b.assignment.lhs)
|
|
|
|
|
&& apfl_expr_eq(*a.assignment.rhs, *b.assignment.rhs);
|
|
|
|
|
case APFL_EXPR_DOT:
|
|
|
|
|
return apfl_expr_eq(*a.dot.lhs, *b.dot.lhs)
|
2022-01-02 15:51:19 +00:00
|
|
|
&& apfl_string_eq(a.dot.rhs, b.dot.rhs);
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_EXPR_AT:
|
|
|
|
|
return apfl_expr_eq(*a.at.lhs, *b.at.lhs)
|
|
|
|
|
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
|
|
|
|
|
case APFL_EXPR_CONSTANT:
|
|
|
|
|
return const_eq(a.constant, b.constant);
|
|
|
|
|
case APFL_EXPR_VAR:
|
2022-01-02 15:51:19 +00:00
|
|
|
return apfl_string_eq(a.var, b.var);
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_BLANK:
|
|
|
|
|
return true;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|