#include #include #include #include "apfl.h" #include "alloc.h" #include "context.h" #include "gc.h" #include "hashmap.h" #include "resizable.h" #include "strings.h" #include "value.h" static struct stack * stack_new(struct gc *gc) { struct stack *stack = apfl_gc_new_stack(gc); if (stack == NULL) { return NULL; } *stack = (struct stack) { .items = NULL, .len = 0, .cap = 0, }; return stack; } void apfl_stack_deinit(struct apfl_allocator allocator, struct stack *stack) { FREE_LIST(allocator, stack->items, stack->cap); } bool apfl_stack_push(apfl_ctx ctx, struct apfl_value value) { return apfl_resizable_append( ctx->gc.allocator, sizeof(struct apfl_value), (void **)&ctx->stack->items, &ctx->stack->len, &ctx->stack->cap, &value, 1 ); } bool apfl_stack_check_index(apfl_ctx ctx, apfl_stackidx *index) { if (*index < 0) { if ((size_t)-*index > ctx->stack->len) { return false; } *index = ctx->stack->len + *index; } else if ((size_t)*index >= ctx->stack->len) { return false; } assert(0 <= *index && (size_t)*index < ctx->stack->len); return true; } static int cmp_stackidx(const void *_a, const void *_b) { const apfl_stackidx *a = _a; const apfl_stackidx *b = _b; return *a - *b; } bool apfl_stack_drop_multi(apfl_ctx ctx, size_t count, apfl_stackidx *indices) { for (size_t i = 0; i < count; i++) { if (!apfl_stack_check_index(ctx, &indices[i])) { return false; } } qsort(indices, count, sizeof(apfl_stackidx), cmp_stackidx); for (size_t i = count; i-- > 0; ) { // Will not fail, as we've already checked the indices assert(apfl_resizable_cut_without_resize( sizeof(struct apfl_value), (void **)&ctx->stack->items, &ctx->stack->len, indices[i], 1 )); } // TODO: Shrink stack return true; } bool apfl_stack_pop(apfl_ctx ctx, struct apfl_value *value, apfl_stackidx index) { if (!apfl_stack_check_index(ctx, &index)) { return false; } *value = ctx->stack->items[index]; assert(apfl_resizable_splice( ctx->gc.allocator, sizeof(struct apfl_value), (void **)ctx->stack, &ctx->stack->len, &ctx->stack->cap, index, 1, NULL, 0 )); return true; } static struct apfl_value * stack_get_pointer(apfl_ctx ctx, apfl_stackidx index) { if (!apfl_stack_check_index(ctx, &index)) { return NULL; } return &ctx->stack->items[index]; } static bool stack_get_and_adjust_index(apfl_ctx ctx, struct apfl_value *value, apfl_stackidx *index) { if (!apfl_stack_check_index(ctx, index)) { return false; } *value = ctx->stack->items[*index]; return true; } bool apfl_stack_get(apfl_ctx ctx, struct apfl_value *value, apfl_stackidx index) { return stack_get_and_adjust_index(ctx, value, &index); } struct apfl_value * apfl_stack_push_placeholder(apfl_ctx ctx) { if (!apfl_stack_push(ctx, (struct apfl_value) {.type = VALUE_NIL})) { return NULL; } return stack_get_pointer(ctx, -1); } bool apfl_stack_drop(apfl_ctx ctx, apfl_stackidx index) { struct apfl_value value; return apfl_stack_pop(ctx, &value, index); } void apfl_stack_clear(apfl_ctx ctx) { ctx->stack->len = 0; } apfl_ctx apfl_ctx_new(struct apfl_allocator base_allocator) { apfl_ctx ctx = ALLOC_OBJ(base_allocator, struct apfl_ctx_data); if (ctx == NULL) { return NULL; } if (!apfl_gc_init(&ctx->gc, base_allocator)) { FREE_OBJ(base_allocator, ctx); return NULL; } if ((ctx->scope = apfl_scope_new(&ctx->gc)) == NULL) { goto error; } if (!apfl_gc_root_add(&ctx->gc, GC_OBJECT_FROM(ctx->scope, GC_TYPE_SCOPE))) { goto error; } if ((ctx->stack = stack_new(&ctx->gc)) == NULL) { goto error; } if (!apfl_gc_root_add(&ctx->gc, GC_OBJECT_FROM(ctx->stack, GC_TYPE_STACK))) { goto error; } return ctx; error: apfl_ctx_destroy(ctx); return NULL; } void apfl_ctx_destroy(apfl_ctx ctx) { if (ctx == NULL) { return; } struct apfl_allocator base_allocator = ctx->gc.base_allocator; apfl_gc_full(&ctx->gc); apfl_gc_deinit(&ctx->gc); FREE_OBJ(base_allocator, ctx); } #define CREATE_GC_OBJECT_VALUE_ON_STACK(ctx, TYPE, MEMB, NEW) \ struct apfl_value *value = apfl_stack_push_placeholder(ctx); \ if (value == NULL) { \ return APFL_RESULT_ERR_FATAL; \ } \ \ struct apfl_value new_value = {.type = TYPE}; \ if ((new_value.MEMB = NEW) == NULL) { \ assert(apfl_stack_drop(ctx, -1)); \ return APFL_RESULT_ERR_FATAL; \ } \ \ *value = new_value; \ \ return APFL_RESULT_OK; enum apfl_result apfl_push_nil(apfl_ctx ctx) { return apfl_stack_push(ctx, (struct apfl_value) { .type = VALUE_NIL, }) ? APFL_RESULT_OK : APFL_RESULT_ERR; } enum apfl_result apfl_push_bool(apfl_ctx ctx, bool b) { return apfl_stack_push(ctx, (struct apfl_value) { .type = VALUE_BOOLEAN, .boolean = b, }) ? APFL_RESULT_OK : APFL_RESULT_ERR; } enum apfl_result apfl_push_number(apfl_ctx ctx, apfl_number num) { return apfl_stack_push(ctx, (struct apfl_value) { .type = VALUE_NUMBER, .number = num, }) ? APFL_RESULT_OK : APFL_RESULT_ERR; } static struct apfl_string * new_copied_string(struct gc *gc, struct apfl_string_view sv) { struct apfl_string s = apfl_string_blank(); if (!apfl_string_copy(gc->allocator, &s, sv)) { return NULL; } struct apfl_string *out = apfl_string_move_into_new_gc_string(gc, &s); if (out == NULL) { apfl_string_deinit(gc->allocator, &s); return NULL; } return out; } enum apfl_result apfl_push_string_view_copy(apfl_ctx ctx, struct apfl_string_view sv) { CREATE_GC_OBJECT_VALUE_ON_STACK( ctx, VALUE_STRING, string, new_copied_string(&ctx->gc, sv) ) } enum apfl_result apfl_push_const_string(apfl_ctx ctx, const char *string) { return apfl_stack_push(ctx, (struct apfl_value) { .type = VALUE_CONST_STRING, .const_string = apfl_string_view_from(string), }) ? APFL_RESULT_OK : APFL_RESULT_ERR_FATAL; } enum apfl_result apfl_list_create(apfl_ctx ctx, size_t initial_cap) { CREATE_GC_OBJECT_VALUE_ON_STACK( ctx, VALUE_LIST, list, apfl_list_new(&ctx->gc, initial_cap) ) } enum apfl_result apfl_list_append(apfl_ctx ctx, apfl_stackidx list_index, apfl_stackidx value_index) { struct apfl_value *list_val = stack_get_pointer(ctx, list_index); if (list_val == NULL) { return APFL_RESULT_ERR; } if (list_val->type != VALUE_LIST) { return APFL_RESULT_ERR; } struct apfl_value value; if (!apfl_stack_get(ctx, &value, value_index)) { return APFL_RESULT_ERR; } enum apfl_result result = apfl_list_splice( &ctx->gc, &list_val->list, list_val->list->len, 0, &value, 1 ) ? APFL_RESULT_OK : APFL_RESULT_ERR; assert(apfl_stack_drop(ctx, value_index)); return result; } enum apfl_result apfl_list_append_list(apfl_ctx ctx, apfl_stackidx dst_index, apfl_stackidx src_index) { struct apfl_value *dst_val = stack_get_pointer(ctx, dst_index); if (dst_val == NULL) { return APFL_RESULT_ERR; } if (dst_val->type != VALUE_LIST) { return APFL_RESULT_ERR; } struct apfl_value src_val; if (!apfl_stack_get(ctx, &src_val, src_index)) { return APFL_RESULT_ERR; } if (src_val.type != VALUE_LIST) { assert(apfl_stack_drop(ctx, src_index)); return APFL_RESULT_ERR; } enum apfl_result result = apfl_list_splice( &ctx->gc, &dst_val->list, dst_val->list->len, 0, src_val.list->items, src_val.list->len ) ? APFL_RESULT_OK : APFL_RESULT_ERR; assert(apfl_stack_drop(ctx, src_index)); return result; } enum apfl_result apfl_dict_create(apfl_ctx ctx) { CREATE_GC_OBJECT_VALUE_ON_STACK( ctx, VALUE_DICT, dict, apfl_dict_new(&ctx->gc) ) } enum apfl_result apfl_dict_set( apfl_ctx ctx, apfl_stackidx dict_index, apfl_stackidx k_index, apfl_stackidx v_index ) { struct apfl_value k; struct apfl_value v; struct apfl_value *dict_value; if ( !apfl_stack_get(ctx, &k, k_index) || !apfl_stack_get(ctx, &v, v_index) || (dict_value = stack_get_pointer(ctx, dict_index)) == NULL ) { return APFL_RESULT_ERR; } if (dict_value->type != VALUE_DICT) { assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index})); return APFL_RESULT_ERR; } if (!apfl_dict_set_raw(&ctx->gc, &dict_value->dict, k, v)) { assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index})); return APFL_RESULT_ERR; } assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index})); return APFL_RESULT_OK; } static enum apfl_result apfl_get_member_inner( apfl_ctx ctx, struct apfl_value container, struct apfl_value k ) { struct apfl_value *value = apfl_stack_push_placeholder(ctx); if (value == NULL) { return APFL_RESULT_ERR; } if (apfl_value_get_item(container, k, value) != GET_ITEM_OK) { assert(apfl_stack_drop(ctx, -1)); return APFL_RESULT_ERR; } return APFL_RESULT_OK; } enum apfl_result apfl_get_member( apfl_ctx ctx, apfl_stackidx container_index, apfl_stackidx k_index ) { struct apfl_value container; struct apfl_value k; if ( !stack_get_and_adjust_index(ctx, &container, &container_index) || !stack_get_and_adjust_index(ctx, &k, &k_index) ) { return APFL_RESULT_ERR; } enum apfl_result result = apfl_get_member_inner(ctx, container, k); assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, container_index})); return result; } void apfl_gc_stack_traverse(struct stack *stack, gc_visitor visitor, void *opaque) { for (size_t i = 0; i < stack->len; i++) { struct gc_object *object = apfl_value_get_gc_object(stack->items[i]); if (object != NULL) { visitor(opaque, object); } } }