Introduce allocator abstraction
We now no longer call malloc/free/... directly, but use an allocator object that is passed around. This was mainly done as a preparation for a garbage collector: The collector will need to know, how much memory we're using, introducing the collector abstraction will allow the GC to hook into the memory allocation and observe the memory usage. This has other potential applications: - We could now be embedded into applications that can't use the libc allocator. - There could be an allocator that limits the total amount of used memory, e.g. for sandboxing purposes. - In our tests we could use this to simulate out of memory conditions (implement an allocator that fails at the n-th allocation, increase n by one and restart the test until there are no more faked OOM conditions). The function signature of the allocator is basically exactly the same as the one Lua uses.
This commit is contained in:
parent
ad80ff8f85
commit
ebf3fc89ff
25 changed files with 1264 additions and 717 deletions
|
|
@ -3,6 +3,7 @@ AM_CFLAGS=--std=c11 -Wall -Werror -Wextra -pedantic
|
|||
lib_LIBRARIES = libapfl.a
|
||||
|
||||
libapfl_a_SOURCES =
|
||||
libapfl_a_SOURCES += alloc.c
|
||||
libapfl_a_SOURCES += error.c
|
||||
libapfl_a_SOURCES += eval.c
|
||||
libapfl_a_SOURCES += expr.c
|
||||
|
|
@ -18,7 +19,6 @@ libapfl_a_SOURCES += source_readers.c
|
|||
libapfl_a_SOURCES += value.c
|
||||
|
||||
apfl_internal_headers =
|
||||
apfl_internal_headers += common.h
|
||||
apfl_internal_headers += hashmap.c
|
||||
apfl_internal_headers += internal.h
|
||||
apfl_internal_headers += resizable.h
|
||||
|
|
|
|||
72
src/alloc.c
Normal file
72
src/alloc.c
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "apfl.h"
|
||||
#include "alloc.h"
|
||||
|
||||
#define ALLOC_DEBUG 1
|
||||
|
||||
static void *
|
||||
libc_alloc(void *opaque, void *oldptr, size_t oldsize, size_t newsize)
|
||||
{
|
||||
(void)opaque;
|
||||
(void)oldsize;
|
||||
|
||||
if (newsize == 0) {
|
||||
free(oldptr);
|
||||
return NULL;
|
||||
} else {
|
||||
return realloc(oldptr, newsize);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
verifying_alloc(void *opaque, void *oldptr, size_t oldsize, size_t newsize)
|
||||
{
|
||||
struct apfl_allocator *wrapped = opaque;
|
||||
|
||||
size_t *oldptr_actual = NULL;
|
||||
if (oldptr != NULL) {
|
||||
oldptr_actual = (size_t *)((char *)oldptr - sizeof(size_t));
|
||||
|
||||
// This checks, if the oldsize was correct
|
||||
assert(*oldptr_actual == oldsize);
|
||||
}
|
||||
|
||||
if (newsize == 0) {
|
||||
void *newptr = ALLOCATOR_CALL(*wrapped, oldptr_actual, oldsize + sizeof(size_t), 0);
|
||||
assert(newptr == NULL);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
size_t *newptr = ALLOCATOR_CALL(*wrapped, oldptr_actual, oldsize + sizeof(size_t), newsize + sizeof(size_t));
|
||||
if (newptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*newptr = newsize;
|
||||
return newptr + 1;
|
||||
}
|
||||
|
||||
static struct apfl_allocator libc_allocator = {
|
||||
.opaque = NULL,
|
||||
.alloc = libc_alloc,
|
||||
};
|
||||
|
||||
struct apfl_allocator
|
||||
apfl_allocator_wrap_verifying(struct apfl_allocator *wrapped)
|
||||
{
|
||||
return (struct apfl_allocator) {
|
||||
.opaque = wrapped,
|
||||
.alloc = verifying_alloc,
|
||||
};
|
||||
}
|
||||
|
||||
struct apfl_allocator
|
||||
apfl_allocator_default(void)
|
||||
{
|
||||
return ALLOC_DEBUG
|
||||
? apfl_allocator_wrap_verifying(&libc_allocator)
|
||||
: libc_allocator;
|
||||
}
|
||||
29
src/alloc.h
Normal file
29
src/alloc.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef APFL_ALLOC_H
|
||||
#define APFL_ALLOC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define ALLOCATOR_CALL(a, oldptr, oldsize, newsize) \
|
||||
((a).alloc((a).opaque, (oldptr), (oldsize), (newsize)))
|
||||
|
||||
#define ALLOC_BYTES(a, n) (((n) == 0) ? NULL : ALLOCATOR_CALL(a, NULL, 0, (n)))
|
||||
#define REALLOC_BYTES(a, ptr, old, new) ALLOCATOR_CALL(a, ptr, old, new)
|
||||
#define FREE_BYTES(a, ptr, n) ALLOCATOR_CALL(a, ptr, n, 0)
|
||||
|
||||
#define REALLOC_LIST(a, ptr, old, new) REALLOC_BYTES(a, ptr, sizeof(*ptr) * (old), sizeof(*ptr) * (new))
|
||||
#define ALLOC_LIST(a, T, len) (T *)ALLOC_BYTES(a, sizeof(T) * (len))
|
||||
#define FREE_LIST(a, ptr, len) FREE_BYTES(a, ptr, sizeof(*ptr) * (len))
|
||||
|
||||
#define ALLOC_OBJ(a, T) ALLOC_LIST(a, T, 1)
|
||||
#define FREE_OBJ(a, ptr) FREE_LIST(a, ptr, 1)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
109
src/apfl.h
109
src/apfl.h
|
|
@ -9,6 +9,33 @@ extern "C" {
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Allocator
|
||||
|
||||
// apfl_allocator_cb is called to (re)allocate and free memory.
|
||||
//
|
||||
// If called with newsize = 0, the memory of oldsize bytes pointed to by
|
||||
// oldptr should be freed. If the NULL-pointer is freed, nothing should
|
||||
// happen. A freeing must return the NULL-pointer and is not allowed to
|
||||
// fail.
|
||||
//
|
||||
// If oldptr = NULL and oldsize = 0, memory of size newsize should be
|
||||
// allocated and the pointer to it returned. If the allocation fails, NULL
|
||||
// should be returned.
|
||||
//
|
||||
// Reallocation is done by calling with the old pointer, old size and new
|
||||
// size. On success the new pointer should be returned and the old pointer
|
||||
// (unless they're the same) no longer used. On failure, NULL should be
|
||||
// returned and the oldptr should stay valid.
|
||||
typedef void * (*apfl_allocator_cb) (void *opaque, void *oldptr, size_t oldsize, size_t newsize);
|
||||
|
||||
struct apfl_allocator {
|
||||
void *opaque;
|
||||
apfl_allocator_cb alloc;
|
||||
};
|
||||
|
||||
struct apfl_allocator apfl_allocator_default(void);
|
||||
struct apfl_allocator apfl_allocator_wrap_verifying(struct apfl_allocator *wrapped);
|
||||
|
||||
typedef double apfl_number;
|
||||
|
||||
struct apfl_position {
|
||||
|
|
@ -28,6 +55,11 @@ struct apfl_string_view {
|
|||
struct apfl_string {
|
||||
char *bytes;
|
||||
size_t len;
|
||||
|
||||
// TODO: Not sure, if it's a good idea to expose this. We now need the actual
|
||||
// size of the underlying allocation tough. Maybe we should better
|
||||
// shrink the cap to len wen we're done building a string?
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct apfl_refcounted_string_data;
|
||||
|
|
@ -57,7 +89,7 @@ int apfl_string_view_cmp(struct apfl_string_view, struct apfl_string_view);
|
|||
#define apfl_string_eq(a, b) (apfl_string_cmp((a), (b)) == 0)
|
||||
|
||||
struct apfl_string apfl_string_blank(void);
|
||||
void apfl_string_deinit(struct apfl_string *);
|
||||
void apfl_string_deinit(struct apfl_allocator allocator, struct apfl_string *);
|
||||
struct apfl_string apfl_string_move(struct apfl_string *src);
|
||||
|
||||
/**
|
||||
|
|
@ -65,15 +97,16 @@ struct apfl_string apfl_string_move(struct apfl_string *src);
|
|||
* Returns true on success, false otherwise (if the necessary memory could not
|
||||
* be allocated).
|
||||
*/
|
||||
bool apfl_string_copy(struct apfl_string *dst, struct apfl_string_view src);
|
||||
bool apfl_string_copy(struct apfl_allocator allocator, struct apfl_string *dst, struct apfl_string_view src);
|
||||
|
||||
struct apfl_string_builder {
|
||||
struct apfl_allocator allocator;
|
||||
char *bytes;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
void apfl_string_builder_init(struct apfl_string_builder *);
|
||||
void apfl_string_builder_init(struct apfl_allocator allocator, struct apfl_string_builder *);
|
||||
void apfl_string_builder_deinit(struct apfl_string_builder *);
|
||||
bool apfl_string_builder_append(struct apfl_string_builder *, struct apfl_string_view);
|
||||
bool apfl_string_builder_append_byte(struct apfl_string_builder *, char byte);
|
||||
|
|
@ -81,9 +114,9 @@ struct apfl_string apfl_string_builder_move_string(struct apfl_string_builder *)
|
|||
|
||||
#define apfl_string_builder_append_cstr(builder, cstr) (apfl_string_builder_append((builder), apfl_string_view_from_cstr((cstr))))
|
||||
|
||||
apfl_refcounted_string apfl_string_copy_into_new_refcounted(struct apfl_string_view);
|
||||
apfl_refcounted_string apfl_string_copy_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string_view);
|
||||
|
||||
apfl_refcounted_string apfl_string_move_into_new_refcounted(struct apfl_string *);
|
||||
apfl_refcounted_string apfl_string_move_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string *);
|
||||
|
||||
/* Increases the reference of the refcounted string.
|
||||
* Returns the same apfl_refcounted_string value.
|
||||
|
|
@ -93,12 +126,12 @@ apfl_refcounted_string apfl_refcounted_string_incref(apfl_refcounted_string);
|
|||
/* Unrefs the refcounted string. It is no longer allowed to use the pointer
|
||||
* after calling this function with it!
|
||||
*/
|
||||
void apfl_refcounted_string_unref(apfl_refcounted_string );
|
||||
void apfl_refcounted_string_unref(struct apfl_allocator allocator, apfl_refcounted_string );
|
||||
|
||||
/* Like apfl_refcounted_string_unref, but accepts a pointer to a refcounted_string.
|
||||
* The pointed to value will be set to NULL.
|
||||
*/
|
||||
void apfl_refcounted_string_unref_ptr(apfl_refcounted_string *);
|
||||
void apfl_refcounted_string_unref_ptr(struct apfl_allocator allocator, apfl_refcounted_string *);
|
||||
|
||||
// Tokens
|
||||
|
||||
|
|
@ -136,7 +169,7 @@ struct apfl_token {
|
|||
};
|
||||
};
|
||||
|
||||
void apfl_token_deinit(struct apfl_token *);
|
||||
void apfl_token_deinit(struct apfl_allocator allocator, struct apfl_token *);
|
||||
|
||||
const char *apfl_token_type_name(enum apfl_token_type);
|
||||
|
||||
|
|
@ -221,6 +254,7 @@ struct apfl_expr_dict_pair {
|
|||
struct apfl_expr_dict {
|
||||
struct apfl_expr_dict_pair *items;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct apfl_expr_call {
|
||||
|
|
@ -231,6 +265,7 @@ struct apfl_expr_call {
|
|||
struct apfl_expr_body {
|
||||
struct apfl_expr *items;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
unsigned refcount; // TODO: Probably better to not expose refcount
|
||||
};
|
||||
|
||||
|
|
@ -268,6 +303,7 @@ enum apfl_expr_param_type {
|
|||
struct apfl_expr_params {
|
||||
struct apfl_expr_params_item *params;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct apfl_expr_param {
|
||||
|
|
@ -294,6 +330,7 @@ struct apfl_expr_subfunc {
|
|||
struct apfl_expr_complex_func {
|
||||
struct apfl_expr_subfunc *subfuncs;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
enum apfl_expr_assignable_type {
|
||||
|
|
@ -397,30 +434,30 @@ bool apfl_expr_eq(struct apfl_expr, struct apfl_expr);
|
|||
|
||||
// Begin deinit functions
|
||||
|
||||
void apfl_expr_deinit(struct apfl_expr *);
|
||||
void apfl_expr_list_deinit(struct apfl_expr_list *);
|
||||
void apfl_expr_list_item_deinit(struct apfl_expr_list_item *);
|
||||
void apfl_expr_dict_pair_deinit(struct apfl_expr_dict_pair *);
|
||||
void apfl_expr_dict_deinit(struct apfl_expr_dict *);
|
||||
void apfl_expr_call_deinit(struct apfl_expr_call *);
|
||||
void apfl_expr_body_unref(struct apfl_expr_body *);
|
||||
void apfl_expr_const_deinit(struct apfl_expr_const *);
|
||||
void apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *);
|
||||
void apfl_expr_params_deinit(struct apfl_expr_params *);
|
||||
void apfl_expr_params_item_deinit(struct apfl_expr_params_item *);
|
||||
void apfl_expr_param_deinit(struct apfl_expr_param *);
|
||||
void apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *);
|
||||
void apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *);
|
||||
void apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *);
|
||||
void apfl_expr_assignable_list_item_deinit(struct apfl_expr_assignable_list_item *);
|
||||
void apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *);
|
||||
void apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_expr_assignable_var_or_member_dot *);
|
||||
void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_expr_assignable_var_or_member_at *);
|
||||
void apfl_expr_assignable_var_or_member_deinit(struct apfl_expr_assignable_var_or_member *);
|
||||
void apfl_expr_assignable_deinit(struct apfl_expr_assignable *);
|
||||
void apfl_expr_assignment_deinit(struct apfl_expr_assignment *);
|
||||
void apfl_expr_dot_deinit(struct apfl_expr_dot *);
|
||||
void apfl_expr_at_deinit(struct apfl_expr_at *);
|
||||
void apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *);
|
||||
void apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *);
|
||||
void apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *);
|
||||
void apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *);
|
||||
void apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *);
|
||||
void apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *);
|
||||
void apfl_expr_body_unref(struct apfl_allocator allocator, struct apfl_expr_body *);
|
||||
void apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *);
|
||||
void apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *);
|
||||
void apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *);
|
||||
void apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *);
|
||||
void apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *);
|
||||
void apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *);
|
||||
void apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *);
|
||||
void apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *);
|
||||
void apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *);
|
||||
void apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *);
|
||||
void apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *);
|
||||
void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *);
|
||||
void apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *);
|
||||
void apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *);
|
||||
void apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *);
|
||||
void apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *);
|
||||
void apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *);
|
||||
|
||||
// End deinit functions
|
||||
|
||||
|
|
@ -463,7 +500,7 @@ typedef struct apfl_tokenizer *apfl_tokenizer_ptr;
|
|||
|
||||
typedef bool (*apfl_source_reader_cb)(void *context, char *buf, size_t *len, bool need);
|
||||
|
||||
apfl_tokenizer_ptr apfl_tokenizer_new(apfl_source_reader_cb, void *context);
|
||||
apfl_tokenizer_ptr apfl_tokenizer_new(struct apfl_allocator allocator, apfl_source_reader_cb, void *context);
|
||||
void apfl_tokenizer_destroy(apfl_tokenizer_ptr);
|
||||
|
||||
enum apfl_parse_result apfl_tokenizer_next(apfl_tokenizer_ptr, bool need);
|
||||
|
|
@ -486,7 +523,7 @@ struct apfl_error apfl_tokenizer_get_error(apfl_tokenizer_ptr);
|
|||
*/
|
||||
bool apfl_string_source_reader(void *, char *, size_t *len, bool);
|
||||
|
||||
void *apfl_string_source_reader_new(struct apfl_string_view);
|
||||
void *apfl_string_source_reader_new(struct apfl_allocator allocator, struct apfl_string_view);
|
||||
void apfl_string_source_reader_destroy(void *);
|
||||
|
||||
|
||||
|
|
@ -503,7 +540,7 @@ struct apfl_parser;
|
|||
|
||||
typedef struct apfl_parser *apfl_parser_ptr;
|
||||
|
||||
apfl_parser_ptr apfl_parser_new(struct apfl_parser_token_source);
|
||||
apfl_parser_ptr apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source);
|
||||
|
||||
/* Destroys the parser.
|
||||
* Note that if the token source needs it's own destruction, you'll have to do
|
||||
|
|
@ -538,7 +575,7 @@ enum apfl_result {
|
|||
APFL_RESULT_ERR_FATAL,
|
||||
};
|
||||
|
||||
apfl_ctx apfl_ctx_new(void);
|
||||
apfl_ctx apfl_ctx_new(struct apfl_allocator);
|
||||
|
||||
void apfl_ctx_destroy(apfl_ctx);
|
||||
|
||||
|
|
|
|||
27
src/common.h
27
src/common.h
|
|
@ -1,27 +0,0 @@
|
|||
#ifndef APFL_COMMON_H
|
||||
#define APFL_COMMON_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// APFL_DESTROY destroys a dynamically allocated value.
|
||||
// It will first deinit the value using deiniter,
|
||||
// free the memory and then set the variable to NULL.
|
||||
// It is always allowed to destroy an already destroyed
|
||||
// or deinited value.
|
||||
#define APFL_DESTROY(var, deiniter) \
|
||||
do { \
|
||||
if ((var) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
deiniter(var); \
|
||||
free(var); \
|
||||
(var) = NULL; \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
249
src/eval.c
249
src/eval.c
|
|
@ -1,6 +1,8 @@
|
|||
#include <assert.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
#include "alloc.h"
|
||||
#include "hashmap.h"
|
||||
#include "internal.h"
|
||||
#include "resizable.h"
|
||||
|
|
@ -15,6 +17,8 @@
|
|||
} while (0)
|
||||
|
||||
struct apfl_ctx_data {
|
||||
struct apfl_allocator allocator;
|
||||
|
||||
struct apfl_hashmap scope;
|
||||
|
||||
struct apfl_value *stack;
|
||||
|
|
@ -49,6 +53,7 @@ struct match_pattern_value {
|
|||
struct apfl_value value;
|
||||
struct apfl_value *member_keys;
|
||||
size_t member_keys_len;
|
||||
size_t member_keys_cap;
|
||||
};
|
||||
|
||||
struct match_pattern_list {
|
||||
|
|
@ -76,15 +81,16 @@ struct match_pattern {
|
|||
};
|
||||
struct match_pattern_constraint *constraints;
|
||||
size_t constraints_len;
|
||||
size_t constraints_cap;
|
||||
};
|
||||
|
||||
static enum apfl_result evaluate(apfl_ctx, struct apfl_expr *);
|
||||
static variable variable_new(void);
|
||||
static variable variable_new(apfl_ctx);
|
||||
static variable variable_incref(variable var);
|
||||
static void variable_unref(variable var);
|
||||
static void variable_unref(apfl_ctx, variable var);
|
||||
static enum match_result match_pattern_from_assignable(apfl_ctx ctx, struct apfl_expr_assignable *, struct match_pattern *);
|
||||
static void match_pattern_deinit(struct match_pattern *);
|
||||
static enum match_result match_pattern_match(struct match_pattern *, struct apfl_value);
|
||||
static void match_pattern_deinit(apfl_ctx, struct match_pattern *);
|
||||
static enum match_result match_pattern_match(struct apfl_allocator, struct match_pattern *, struct apfl_value value);
|
||||
|
||||
static bool
|
||||
scope_keys_eq(void *opaque, const void *_a, const void *_b)
|
||||
|
|
@ -108,18 +114,18 @@ scope_calc_hash(void *opaque, const void *_key)
|
|||
static void
|
||||
scope_destroy_key(void *opaque, void *_key)
|
||||
{
|
||||
(void)opaque;
|
||||
apfl_ctx ctx = opaque;
|
||||
apfl_refcounted_string *key = _key;
|
||||
|
||||
apfl_refcounted_string_unref(*key);
|
||||
apfl_refcounted_string_unref(ctx->allocator, *key);
|
||||
}
|
||||
|
||||
static void
|
||||
scope_destroy_value(void *opaque, void *_value)
|
||||
{
|
||||
(void)opaque;
|
||||
apfl_ctx ctx = opaque;
|
||||
variable *value = _value;
|
||||
variable_unref(*value);
|
||||
variable_unref(ctx, *value);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -140,31 +146,30 @@ scope_copy_value(void *opaque, void *_dest, void *_src)
|
|||
*dest = variable_incref(*src);
|
||||
}
|
||||
|
||||
static const struct apfl_hashmap_callbacks scope_hashmap_callbacks = {
|
||||
.opaque = NULL,
|
||||
.keys_eq = scope_keys_eq,
|
||||
.calc_hash = scope_calc_hash,
|
||||
.destroy_key = scope_destroy_key,
|
||||
.destroy_value = scope_destroy_value,
|
||||
.copy_key = scope_copy_key,
|
||||
.copy_value = scope_copy_value,
|
||||
};
|
||||
|
||||
bool
|
||||
scope_init(struct apfl_hashmap *map)
|
||||
scope_init(apfl_ctx ctx, struct apfl_hashmap *map)
|
||||
{
|
||||
return apfl_hashmap_init(
|
||||
map,
|
||||
scope_hashmap_callbacks,
|
||||
ctx->allocator,
|
||||
(struct apfl_hashmap_callbacks) {
|
||||
.opaque = ctx,
|
||||
.keys_eq = scope_keys_eq,
|
||||
.calc_hash = scope_calc_hash,
|
||||
.destroy_key = scope_destroy_key,
|
||||
.destroy_value = scope_destroy_value,
|
||||
.copy_key = scope_copy_key,
|
||||
.copy_value = scope_copy_value,
|
||||
},
|
||||
sizeof(apfl_refcounted_string),
|
||||
sizeof(variable)
|
||||
);
|
||||
}
|
||||
|
||||
static variable
|
||||
variable_new(void)
|
||||
variable_new(apfl_ctx ctx)
|
||||
{
|
||||
variable var = ALLOC(struct variable_data);
|
||||
variable var = ALLOC_OBJ(ctx->allocator, struct variable_data);
|
||||
if (var == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -185,22 +190,22 @@ variable_incref(variable var)
|
|||
}
|
||||
|
||||
static void
|
||||
variable_unref(variable var)
|
||||
variable_unref(apfl_ctx ctx, variable var)
|
||||
{
|
||||
if (var != NULL && apfl_refcount_dec(&var->refcount)) {
|
||||
apfl_value_deinit(&var->value);
|
||||
free(var);
|
||||
apfl_value_deinit(ctx->allocator, &var->value);
|
||||
FREE_OBJ(ctx->allocator, var);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
variable_set(variable var, struct apfl_value value)
|
||||
variable_set(apfl_ctx ctx, variable var, struct apfl_value value)
|
||||
{
|
||||
if (var == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
apfl_value_deinit(&var->value);
|
||||
apfl_value_deinit(ctx->allocator, &var->value);
|
||||
var->value = apfl_value_move(&value);
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +213,7 @@ static bool
|
|||
stack_push(apfl_ctx ctx, struct apfl_value value)
|
||||
{
|
||||
bool ok = apfl_resizable_append(
|
||||
ctx->allocator,
|
||||
sizeof(struct apfl_value),
|
||||
(void **)&ctx->stack,
|
||||
&ctx->stack_len,
|
||||
|
|
@ -217,7 +223,7 @@ stack_push(apfl_ctx ctx, struct apfl_value value)
|
|||
);
|
||||
|
||||
if (!ok) {
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(ctx->allocator, &value);
|
||||
}
|
||||
|
||||
return ok;
|
||||
|
|
@ -250,6 +256,7 @@ stack_pop(apfl_ctx ctx, struct apfl_value *value, int index)
|
|||
*value = ctx->stack[index];
|
||||
|
||||
assert(apfl_resizable_splice(
|
||||
ctx->allocator,
|
||||
sizeof(struct apfl_value),
|
||||
(void **)ctx->stack,
|
||||
&ctx->stack_len,
|
||||
|
|
@ -298,7 +305,7 @@ stack_drop(apfl_ctx ctx, int index)
|
|||
if (!stack_pop(ctx, &value, index)) {
|
||||
return false;
|
||||
}
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(ctx->allocator, &value);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -309,22 +316,25 @@ stack_must_drop(apfl_ctx ctx, int index)
|
|||
}
|
||||
|
||||
apfl_ctx
|
||||
apfl_ctx_new(void)
|
||||
apfl_ctx_new(struct apfl_allocator allocator)
|
||||
{
|
||||
apfl_ctx ctx = ALLOC(struct apfl_ctx_data);
|
||||
apfl_ctx ctx = ALLOC_OBJ(allocator, struct apfl_ctx_data);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!scope_init(&ctx->scope)) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->allocator = allocator;
|
||||
ctx->stack = NULL;
|
||||
ctx->stack_len = 0;
|
||||
ctx->stack_cap = 0;
|
||||
|
||||
if (!scope_init(ctx, &ctx->scope)) {
|
||||
FREE_OBJ(allocator, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
@ -339,9 +349,8 @@ apfl_ctx_destroy(apfl_ctx ctx)
|
|||
while (ctx->stack_len > 0) {
|
||||
stack_must_drop(ctx, -1);
|
||||
}
|
||||
free(ctx->stack);
|
||||
|
||||
free(ctx);
|
||||
FREE_OBJ(ctx->allocator, ctx->stack);
|
||||
FREE_OBJ(ctx->allocator, ctx);
|
||||
}
|
||||
|
||||
static variable
|
||||
|
|
@ -352,12 +361,12 @@ ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_stri
|
|||
return var;
|
||||
}
|
||||
|
||||
if ((var = variable_new()) == NULL) {
|
||||
if ((var = variable_new(ctx)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!apfl_hashmap_set(&ctx->scope, &name, &var)) {
|
||||
variable_unref(var);
|
||||
variable_unref(ctx, var);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -368,7 +377,7 @@ static variable
|
|||
ctx_get_var_for_assignment(apfl_ctx ctx, apfl_refcounted_string name)
|
||||
{
|
||||
variable var = ctx_get_var_for_assignment_inner(ctx, name);
|
||||
apfl_refcounted_string_unref(name);
|
||||
apfl_refcounted_string_unref(ctx->allocator, name);
|
||||
return var;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +386,7 @@ ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name)
|
|||
{
|
||||
variable var;
|
||||
bool ok = apfl_hashmap_get(&ctx->scope, &name, &var);
|
||||
apfl_refcounted_string_unref(name);
|
||||
apfl_refcounted_string_unref(ctx->allocator, name);
|
||||
return ok ? var : NULL;
|
||||
}
|
||||
|
||||
|
|
@ -410,8 +419,8 @@ constant_to_value(struct apfl_expr_const *constant)
|
|||
|
||||
static bool
|
||||
match_pattern_add_constraint(
|
||||
struct apfl_allocator allocator,
|
||||
struct match_pattern *pattern,
|
||||
size_t *constraints_cap,
|
||||
enum match_pattern_constraint_type type,
|
||||
struct apfl_value value
|
||||
) {
|
||||
|
|
@ -421,14 +430,15 @@ match_pattern_add_constraint(
|
|||
};
|
||||
|
||||
if (!apfl_resizable_append(
|
||||
allocator,
|
||||
sizeof(struct match_pattern_constraint),
|
||||
(void **)&pattern->constraints,
|
||||
&pattern->constraints_len,
|
||||
constraints_cap,
|
||||
&pattern->constraints_cap,
|
||||
&constraint,
|
||||
1
|
||||
)) {
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(allocator, &value);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -454,6 +464,7 @@ match_pattern_from_assignable_list(
|
|||
}
|
||||
|
||||
if ((pattern_list->subpatterns = ALLOC_LIST(
|
||||
ctx->allocator,
|
||||
struct match_pattern,
|
||||
assignable_list->len
|
||||
)) == NULL) {
|
||||
|
|
@ -479,10 +490,13 @@ match_pattern_from_assignable_list(
|
|||
);
|
||||
|
||||
if (result != MATCH_OK) {
|
||||
DEINIT_LIST(
|
||||
DEINIT_CAP_LIST_WITH_ARGS(
|
||||
ctx->allocator,
|
||||
pattern_list->subpatterns,
|
||||
pattern_list->subpatterns_len,
|
||||
match_pattern_deinit
|
||||
assignable_list->len,
|
||||
match_pattern_deinit,
|
||||
ctx
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -506,8 +520,8 @@ match_pattern_from_var_or_member(
|
|||
.value = {.type = APFL_VALUE_NIL},
|
||||
.member_keys = NULL,
|
||||
.member_keys_len = 0,
|
||||
.member_keys_cap = 0,
|
||||
};
|
||||
size_t member_keys_cap = 0;
|
||||
|
||||
enum match_result result;
|
||||
enum apfl_result eval_result;
|
||||
|
|
@ -518,6 +532,7 @@ next:
|
|||
switch (var_or_member->type) {
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
||||
if ((pattern->value.varname = apfl_string_copy_into_new_refcounted(
|
||||
ctx->allocator,
|
||||
apfl_string_view_from(var_or_member->var)
|
||||
)) == NULL) {
|
||||
result = MATCH_FATAL_ERROR;
|
||||
|
|
@ -527,6 +542,7 @@ next:
|
|||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
||||
str_value.type = APFL_VALUE_STRING;
|
||||
if ((str_value.string = apfl_string_copy_into_new_refcounted(
|
||||
ctx->allocator,
|
||||
apfl_string_view_from(var_or_member->dot.rhs)
|
||||
)) == NULL) {
|
||||
result = MATCH_FATAL_ERROR;
|
||||
|
|
@ -534,14 +550,15 @@ next:
|
|||
}
|
||||
|
||||
if (!apfl_resizable_append(
|
||||
ctx->allocator,
|
||||
sizeof(struct apfl_value),
|
||||
(void **)&pattern->value.member_keys,
|
||||
&pattern->value.member_keys_len,
|
||||
&member_keys_cap,
|
||||
&pattern->value.member_keys_cap,
|
||||
&str_value,
|
||||
1
|
||||
)) {
|
||||
apfl_value_deinit(&str_value);
|
||||
apfl_value_deinit(ctx->allocator, &str_value);
|
||||
result = MATCH_FATAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -564,14 +581,15 @@ next:
|
|||
rhs_value = stack_must_pop(ctx, -1);
|
||||
|
||||
if (!apfl_resizable_append(
|
||||
ctx->allocator,
|
||||
sizeof(struct apfl_value),
|
||||
(void **)&pattern->value.member_keys,
|
||||
&pattern->value.member_keys_len,
|
||||
&member_keys_cap,
|
||||
&pattern->value.member_keys_cap,
|
||||
&rhs_value,
|
||||
1
|
||||
)) {
|
||||
apfl_value_deinit(&rhs_value);
|
||||
apfl_value_deinit(ctx->allocator, &rhs_value);
|
||||
result = MATCH_FATAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -581,7 +599,12 @@ next:
|
|||
}
|
||||
|
||||
fail:
|
||||
DEINIT_LIST(pattern->value.member_keys, pattern->value.member_keys_len, apfl_value_deinit);
|
||||
DEINIT_LIST(
|
||||
ctx->allocator,
|
||||
pattern->value.member_keys,
|
||||
pattern->value.member_keys_cap,
|
||||
apfl_value_deinit
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -597,8 +620,7 @@ match_pattern_from_assignable_inner(
|
|||
pattern->type = MPATTERN_BLANK;
|
||||
pattern->constraints = NULL;
|
||||
pattern->constraints_len = 0;
|
||||
|
||||
size_t constraints_cap = 0;
|
||||
pattern->constraints_cap = 0;
|
||||
|
||||
next:
|
||||
switch (assignable->type) {
|
||||
|
|
@ -606,8 +628,8 @@ next:
|
|||
return match_pattern_from_var_or_member(ctx, &assignable->var_or_member, pattern);
|
||||
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
||||
if (!match_pattern_add_constraint(
|
||||
ctx->allocator,
|
||||
pattern,
|
||||
&constraints_cap,
|
||||
MPATTERN_CONSTRAINT_EQUALS,
|
||||
constant_to_value(&assignable->constant)
|
||||
)) {
|
||||
|
|
@ -630,8 +652,8 @@ next:
|
|||
}
|
||||
|
||||
if (!match_pattern_add_constraint(
|
||||
ctx->allocator,
|
||||
pattern,
|
||||
&constraints_cap,
|
||||
MPATTERN_CONSTRAINT_PREDICATE,
|
||||
apfl_value_move(&value)
|
||||
)) {
|
||||
|
|
@ -659,7 +681,7 @@ match_pattern_from_assignable(
|
|||
) {
|
||||
enum match_result result = match_pattern_from_assignable_inner(ctx, assignable, pattern);
|
||||
if (result != MATCH_OK) {
|
||||
match_pattern_deinit(pattern);
|
||||
match_pattern_deinit(ctx, pattern);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -697,17 +719,17 @@ match_pattern_create_vars(apfl_ctx ctx, struct match_pattern *pattern)
|
|||
}
|
||||
|
||||
static void
|
||||
match_pattern_constraint_deinit(struct match_pattern_constraint *constraint)
|
||||
match_pattern_constraint_deinit(struct apfl_allocator allocator, struct match_pattern_constraint *constraint)
|
||||
{
|
||||
if (constraint == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
apfl_value_deinit(&constraint->value);
|
||||
apfl_value_deinit(allocator, &constraint->value);
|
||||
}
|
||||
|
||||
static void
|
||||
match_pattern_deinit(struct match_pattern *pattern)
|
||||
match_pattern_deinit(apfl_ctx ctx, struct match_pattern *pattern)
|
||||
{
|
||||
if (pattern == NULL) {
|
||||
return;
|
||||
|
|
@ -717,27 +739,31 @@ match_pattern_deinit(struct match_pattern *pattern)
|
|||
case MPATTERN_BLANK:
|
||||
break;
|
||||
case MPATTERN_VALUE:
|
||||
apfl_refcounted_string_unref(pattern->value.varname);
|
||||
variable_unref(pattern->value.var);
|
||||
apfl_value_deinit(&pattern->value.value);
|
||||
apfl_refcounted_string_unref(ctx->allocator, pattern->value.varname);
|
||||
variable_unref(ctx, pattern->value.var);
|
||||
apfl_value_deinit(ctx->allocator, &pattern->value.value);
|
||||
DEINIT_LIST(
|
||||
ctx->allocator,
|
||||
pattern->value.member_keys,
|
||||
pattern->value.member_keys_len,
|
||||
pattern->value.member_keys_cap,
|
||||
apfl_value_deinit
|
||||
);
|
||||
break;
|
||||
case MPATTERN_LIST:
|
||||
DEINIT_LIST(
|
||||
DEINIT_LIST_WITH_ARGS(
|
||||
ctx->allocator,
|
||||
pattern->list.subpatterns,
|
||||
pattern->list.subpatterns_len,
|
||||
match_pattern_deinit
|
||||
match_pattern_deinit,
|
||||
ctx
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
DEINIT_LIST(
|
||||
ctx->allocator,
|
||||
pattern->constraints,
|
||||
pattern->constraints_len,
|
||||
pattern->constraints_cap,
|
||||
match_pattern_constraint_deinit
|
||||
);
|
||||
}
|
||||
|
|
@ -763,6 +789,7 @@ match_pattern_check_constraint(
|
|||
|
||||
static enum match_result
|
||||
match_pattern_match_list_inner(
|
||||
struct apfl_allocator allocator,
|
||||
struct match_pattern_list *pattern_list,
|
||||
/*borrowed*/ apfl_list list
|
||||
) {
|
||||
|
|
@ -783,12 +810,14 @@ match_pattern_match_list_inner(
|
|||
struct apfl_value list_item;
|
||||
// Will not fail, as i is a valid index for the list
|
||||
assert(apfl_list_get_item(
|
||||
allocator,
|
||||
apfl_list_incref(list),
|
||||
i,
|
||||
&list_item
|
||||
));
|
||||
|
||||
enum match_result result = match_pattern_match(
|
||||
allocator,
|
||||
&pattern_list->subpatterns[i],
|
||||
apfl_value_move(&list_item)
|
||||
);
|
||||
|
|
@ -810,12 +839,14 @@ match_pattern_match_list_inner(
|
|||
struct apfl_value list_item;
|
||||
// Will not fail, as list_index is a valid index for the list
|
||||
assert(apfl_list_get_item(
|
||||
allocator,
|
||||
apfl_list_incref(list),
|
||||
list_index,
|
||||
&list_item
|
||||
));
|
||||
|
||||
enum match_result result = match_pattern_match(
|
||||
allocator,
|
||||
&pattern_list->subpatterns[subpattern_index],
|
||||
apfl_value_move(&list_item)
|
||||
);
|
||||
|
|
@ -824,7 +855,7 @@ match_pattern_match_list_inner(
|
|||
}
|
||||
}
|
||||
|
||||
apfl_editable_list mid_builder = apfl_editable_list_new();
|
||||
apfl_editable_list mid_builder = apfl_editable_list_new(allocator);
|
||||
if (mid_builder == NULL) {
|
||||
return MATCH_ERROR;
|
||||
}
|
||||
|
|
@ -833,6 +864,7 @@ match_pattern_match_list_inner(
|
|||
struct apfl_value list_item;
|
||||
// Will not fail, as i is a valid index for the list
|
||||
assert(apfl_list_get_item(
|
||||
allocator,
|
||||
apfl_list_incref(list),
|
||||
i,
|
||||
&list_item
|
||||
|
|
@ -852,6 +884,7 @@ match_pattern_match_list_inner(
|
|||
}
|
||||
|
||||
return match_pattern_match(
|
||||
allocator,
|
||||
&pattern_list->subpatterns[pattern_list->expand_index],
|
||||
(struct apfl_value) {
|
||||
.type = APFL_VALUE_LIST,
|
||||
|
|
@ -862,23 +895,24 @@ match_pattern_match_list_inner(
|
|||
|
||||
static enum match_result
|
||||
match_pattern_match_list(
|
||||
struct apfl_allocator allocator,
|
||||
struct match_pattern_list *pattern_list,
|
||||
struct apfl_value value
|
||||
) {
|
||||
if (value.type != APFL_VALUE_LIST) {
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(allocator, &value);
|
||||
return MATCH_DOESNT_MATCH;
|
||||
}
|
||||
apfl_list list = apfl_list_incref(value.list);
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(allocator, &value);
|
||||
|
||||
enum match_result result = match_pattern_match_list_inner(pattern_list, list);
|
||||
apfl_list_unref(list);
|
||||
enum match_result result = match_pattern_match_list_inner(allocator, pattern_list, list);
|
||||
apfl_list_unref(allocator, list);
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum match_result
|
||||
match_pattern_match(struct match_pattern *pattern, struct apfl_value value)
|
||||
match_pattern_match(struct apfl_allocator allocator, struct match_pattern *pattern, struct apfl_value value)
|
||||
{
|
||||
// We put the contraints into the list from the outside in, so we need
|
||||
// to iterate in reverse order.
|
||||
|
|
@ -888,20 +922,20 @@ match_pattern_match(struct match_pattern *pattern, struct apfl_value value)
|
|||
&value
|
||||
);
|
||||
if (result != MATCH_OK) {
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(allocator, &value);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
switch (pattern->type) {
|
||||
case MPATTERN_BLANK:
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(allocator, &value);
|
||||
return MATCH_OK;
|
||||
case MPATTERN_VALUE:
|
||||
pattern->value.value = apfl_value_move(&value);
|
||||
return MATCH_OK;
|
||||
case MPATTERN_LIST:
|
||||
return match_pattern_match_list(&pattern->list, apfl_value_move(&value));
|
||||
return match_pattern_match_list(allocator, &pattern->list, apfl_value_move(&value));
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
|
@ -910,6 +944,7 @@ match_pattern_match(struct match_pattern *pattern, struct apfl_value value)
|
|||
|
||||
static enum match_result
|
||||
match_pattern_apply_value_keys(
|
||||
struct apfl_allocator allocator,
|
||||
struct match_pattern_value *pattern_value,
|
||||
apfl_editable_dict ed,
|
||||
size_t i
|
||||
|
|
@ -936,14 +971,14 @@ match_pattern_apply_value_keys(
|
|||
&value
|
||||
)) {
|
||||
if (value.type != APFL_VALUE_DICT) {
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(allocator, &value);
|
||||
return MATCH_ERROR;
|
||||
}
|
||||
|
||||
next_ed = apfl_editable_dict_new_from_dict(value.dict);
|
||||
next_ed = apfl_editable_dict_new_from_dict(allocator, value.dict);
|
||||
} else {
|
||||
// Be nice and create the missing dictionary
|
||||
next_ed = apfl_editable_dict_new();
|
||||
next_ed = apfl_editable_dict_new(allocator);
|
||||
}
|
||||
|
||||
if (next_ed == NULL) {
|
||||
|
|
@ -951,6 +986,7 @@ match_pattern_apply_value_keys(
|
|||
}
|
||||
|
||||
enum match_result result = match_pattern_apply_value_keys(
|
||||
allocator,
|
||||
pattern_value,
|
||||
next_ed,
|
||||
i - 1
|
||||
|
|
@ -980,14 +1016,14 @@ match_pattern_apply_value_keys(
|
|||
}
|
||||
|
||||
static enum match_result
|
||||
match_pattern_apply_value(struct match_pattern_value *pattern_value)
|
||||
match_pattern_apply_value(apfl_ctx ctx, struct match_pattern_value *pattern_value)
|
||||
{
|
||||
if (pattern_value->var == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pattern_value->member_keys_len == 0) {
|
||||
variable_set(pattern_value->var, apfl_value_move(&pattern_value->value));
|
||||
variable_set(ctx, pattern_value->var, apfl_value_move(&pattern_value->value));
|
||||
return MATCH_OK;
|
||||
}
|
||||
|
||||
|
|
@ -996,6 +1032,7 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value)
|
|||
}
|
||||
|
||||
apfl_editable_dict ed = apfl_editable_dict_new_from_dict(
|
||||
ctx->allocator,
|
||||
apfl_dict_incref(pattern_value->var->value.dict)
|
||||
);
|
||||
if (ed == NULL) {
|
||||
|
|
@ -1003,6 +1040,7 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value)
|
|||
}
|
||||
|
||||
enum match_result result = match_pattern_apply_value_keys(
|
||||
ctx->allocator,
|
||||
pattern_value,
|
||||
ed,
|
||||
pattern_value->member_keys_len - 1
|
||||
|
|
@ -1017,7 +1055,7 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value)
|
|||
return MATCH_FATAL_ERROR;
|
||||
}
|
||||
|
||||
variable_set(pattern_value->var, (struct apfl_value) {
|
||||
variable_set(ctx, pattern_value->var, (struct apfl_value) {
|
||||
.type = APFL_VALUE_DICT,
|
||||
.dict = d,
|
||||
});
|
||||
|
|
@ -1026,16 +1064,17 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value)
|
|||
}
|
||||
|
||||
static enum match_result
|
||||
match_pattern_apply(struct match_pattern *pattern)
|
||||
match_pattern_apply(apfl_ctx ctx, struct match_pattern *pattern)
|
||||
{
|
||||
switch (pattern->type) {
|
||||
case MPATTERN_BLANK:
|
||||
return MATCH_OK;
|
||||
case MPATTERN_VALUE:
|
||||
return match_pattern_apply_value(&pattern->value);
|
||||
return match_pattern_apply_value(ctx, &pattern->value);
|
||||
case MPATTERN_LIST:
|
||||
for (size_t i = 0; i < pattern->list.subpatterns_len; i++) {
|
||||
enum match_result result = match_pattern_apply(
|
||||
ctx,
|
||||
&pattern->list.subpatterns[i]
|
||||
);
|
||||
if (result != MATCH_OK) {
|
||||
|
|
@ -1071,12 +1110,12 @@ evaluate_list_expr_to_list(apfl_ctx ctx, struct apfl_expr *expr, apfl_editable_l
|
|||
}
|
||||
|
||||
if (value.type != APFL_VALUE_LIST) {
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(ctx->allocator, &value);
|
||||
return APFL_RESULT_ERR;
|
||||
}
|
||||
|
||||
apfl_list list = apfl_list_incref(value.list);
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(ctx->allocator, &value);
|
||||
|
||||
if (!apfl_editable_list_append_list(elist, list)) {
|
||||
return APFL_RESULT_ERR_FATAL;
|
||||
|
|
@ -1107,7 +1146,7 @@ evaluate_expr_list_into_list(apfl_ctx ctx, struct apfl_expr_list *list, apfl_edi
|
|||
static enum apfl_result
|
||||
evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list)
|
||||
{
|
||||
apfl_editable_list elist = apfl_editable_list_new();
|
||||
apfl_editable_list elist = apfl_editable_list_new(ctx->allocator);
|
||||
if (elist == NULL) {
|
||||
return APFL_RESULT_ERR_FATAL;
|
||||
}
|
||||
|
|
@ -1127,7 +1166,7 @@ evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list)
|
|||
.type = APFL_VALUE_LIST,
|
||||
.list = out,
|
||||
})) {
|
||||
apfl_list_unref(out);
|
||||
apfl_list_unref(ctx->allocator, out);
|
||||
return APFL_RESULT_ERR_FATAL;
|
||||
}
|
||||
|
||||
|
|
@ -1162,7 +1201,7 @@ evaluate_dict_into_editable(
|
|||
static enum apfl_result
|
||||
evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict)
|
||||
{
|
||||
apfl_editable_dict ed = apfl_editable_dict_new();
|
||||
apfl_editable_dict ed = apfl_editable_dict_new(ctx->allocator);
|
||||
|
||||
if (ed == NULL) {
|
||||
return APFL_RESULT_ERR_FATAL;
|
||||
|
|
@ -1182,7 +1221,7 @@ evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict)
|
|||
.type = APFL_VALUE_DICT,
|
||||
.dict = out,
|
||||
})) {
|
||||
apfl_dict_unref(out);
|
||||
apfl_dict_unref(ctx->allocator, out);
|
||||
return APFL_RESULT_ERR_FATAL;
|
||||
}
|
||||
|
||||
|
|
@ -1201,7 +1240,7 @@ evaluate_dot(apfl_ctx ctx, struct apfl_expr_dot *dot)
|
|||
};
|
||||
|
||||
struct apfl_value out;
|
||||
if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&key), &out) != APFL_VALUE_GET_ITEM_OK) {
|
||||
if (apfl_value_get_item(ctx->allocator, apfl_value_move(&lhs), apfl_value_move(&key), &out) != APFL_VALUE_GET_ITEM_OK) {
|
||||
return APFL_RESULT_ERR; // TODO: Describe error
|
||||
}
|
||||
|
||||
|
|
@ -1221,7 +1260,7 @@ evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at)
|
|||
struct apfl_value lhs = stack_must_pop(ctx, -1);
|
||||
|
||||
struct apfl_value out;
|
||||
if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&rhs), &out) != APFL_VALUE_GET_ITEM_OK) {
|
||||
if (apfl_value_get_item(ctx->allocator, apfl_value_move(&lhs), apfl_value_move(&rhs), &out) != APFL_VALUE_GET_ITEM_OK) {
|
||||
return APFL_RESULT_ERR; // TODO: Describe error
|
||||
}
|
||||
|
||||
|
|
@ -1256,7 +1295,7 @@ evaluate_assignment_setup(
|
|||
}
|
||||
|
||||
if (!match_pattern_create_vars(ctx, pattern)) {
|
||||
match_pattern_deinit(pattern);
|
||||
match_pattern_deinit(ctx, pattern);
|
||||
return MATCH_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -1265,10 +1304,12 @@ evaluate_assignment_setup(
|
|||
|
||||
static enum match_result
|
||||
evaluate_assignment_finish(
|
||||
apfl_ctx ctx,
|
||||
struct match_pattern *pattern,
|
||||
struct apfl_value value
|
||||
) {
|
||||
enum match_result match_result = match_pattern_match(
|
||||
ctx->allocator,
|
||||
pattern,
|
||||
apfl_value_move(&value)
|
||||
);
|
||||
|
|
@ -1277,7 +1318,7 @@ evaluate_assignment_finish(
|
|||
return match_result;
|
||||
}
|
||||
|
||||
return match_pattern_apply(pattern);
|
||||
return match_pattern_apply(ctx, pattern);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1297,17 +1338,18 @@ evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment)
|
|||
|
||||
enum apfl_result result = evaluate(ctx, assignment->rhs);
|
||||
if (result != APFL_RESULT_OK) {
|
||||
match_pattern_deinit(&pattern);
|
||||
match_pattern_deinit(ctx, &pattern);
|
||||
return result;
|
||||
}
|
||||
|
||||
match_result = evaluate_assignment_finish(
|
||||
ctx,
|
||||
&pattern,
|
||||
stack_must_get(ctx, -1) // stack_must_get instead of stack_must_pop,
|
||||
// so the value is stull on the stack on return.
|
||||
);
|
||||
|
||||
match_pattern_deinit(&pattern);
|
||||
match_pattern_deinit(ctx, &pattern);
|
||||
if (match_result != MATCH_OK) {
|
||||
stack_must_drop(ctx, -1);
|
||||
return failing_match_result_to_apfl_result(match_result);
|
||||
|
|
@ -1325,7 +1367,7 @@ evaluate_var(apfl_ctx ctx, apfl_refcounted_string varname)
|
|||
}
|
||||
|
||||
bool ok = stack_push(ctx, apfl_value_incref(var->value));
|
||||
variable_unref(var);
|
||||
variable_unref(ctx, var);
|
||||
return ok
|
||||
? APFL_RESULT_OK
|
||||
: APFL_RESULT_ERR_FATAL;
|
||||
|
|
@ -1368,7 +1410,12 @@ enum apfl_result
|
|||
apfl_eval(apfl_ctx ctx, struct apfl_expr expr)
|
||||
{
|
||||
enum apfl_result result = evaluate(ctx, &expr);
|
||||
apfl_expr_deinit(&expr);
|
||||
|
||||
// TODO: expr might have been allocated with another allocator. The apfl_ctx
|
||||
// should probably also handle parsing and no longer accept
|
||||
// expressions directly.
|
||||
apfl_expr_deinit(ctx->allocator, &expr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1382,5 +1429,5 @@ apfl_debug_print_val(apfl_ctx ctx, int index, FILE *f)
|
|||
}
|
||||
|
||||
apfl_value_print(value, f);
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(ctx->allocator, &value);
|
||||
}
|
||||
|
|
|
|||
172
src/expr.c
172
src/expr.c
|
|
@ -4,43 +4,43 @@
|
|||
|
||||
#include "apfl.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "alloc.h"
|
||||
#include "internal.h"
|
||||
|
||||
void
|
||||
apfl_expr_deinit(struct apfl_expr *expr)
|
||||
apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *expr)
|
||||
{
|
||||
switch (expr->type) {
|
||||
case APFL_EXPR_LIST:
|
||||
apfl_expr_list_deinit(&expr->list);
|
||||
apfl_expr_list_deinit(allocator, &expr->list);
|
||||
break;
|
||||
case APFL_EXPR_DICT:
|
||||
apfl_expr_dict_deinit(&expr->dict);
|
||||
apfl_expr_dict_deinit(allocator, &expr->dict);
|
||||
break;
|
||||
case APFL_EXPR_CALL:
|
||||
apfl_expr_call_deinit(&expr->call);
|
||||
apfl_expr_call_deinit(allocator, &expr->call);
|
||||
break;
|
||||
case APFL_EXPR_SIMPLE_FUNC:
|
||||
apfl_expr_body_unref(expr->simple_func);
|
||||
apfl_expr_body_unref(allocator, expr->simple_func);
|
||||
expr->simple_func = NULL;
|
||||
break;
|
||||
case APFL_EXPR_COMPLEX_FUNC:
|
||||
apfl_expr_complex_func_deinit(&expr->complex_func);
|
||||
apfl_expr_complex_func_deinit(allocator, &expr->complex_func);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNMENT:
|
||||
apfl_expr_assignment_deinit(&expr->assignment);
|
||||
apfl_expr_assignment_deinit(allocator, &expr->assignment);
|
||||
break;
|
||||
case APFL_EXPR_DOT:
|
||||
apfl_expr_dot_deinit(&expr->dot);
|
||||
apfl_expr_dot_deinit(allocator, &expr->dot);
|
||||
break;
|
||||
case APFL_EXPR_AT:
|
||||
apfl_expr_at_deinit(&expr->at);
|
||||
apfl_expr_at_deinit(allocator, &expr->at);
|
||||
break;
|
||||
case APFL_EXPR_CONSTANT:
|
||||
apfl_expr_const_deinit(&expr->constant);
|
||||
apfl_expr_const_deinit(allocator, &expr->constant);
|
||||
break;
|
||||
case APFL_EXPR_VAR:
|
||||
apfl_refcounted_string_unref(expr->var);
|
||||
apfl_refcounted_string_unref(allocator, expr->var);
|
||||
expr->var = NULL;
|
||||
break;
|
||||
case APFL_EXPR_BLANK:
|
||||
|
|
@ -50,48 +50,48 @@ apfl_expr_deinit(struct apfl_expr *expr)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_expr_list_deinit(struct apfl_expr_list *list)
|
||||
apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *list)
|
||||
{
|
||||
DEINIT_LIST(list->items, list->len, apfl_expr_list_item_deinit);
|
||||
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_list_item_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_list_item_deinit(struct apfl_expr_list_item *item)
|
||||
apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *item)
|
||||
{
|
||||
DESTROY(item->expr, apfl_expr_deinit);
|
||||
DESTROY(allocator, item->expr, apfl_expr_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_dict_pair_deinit(struct apfl_expr_dict_pair *pair)
|
||||
apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *pair)
|
||||
{
|
||||
DESTROY(pair->k, apfl_expr_deinit);
|
||||
DESTROY(pair->v, apfl_expr_deinit);
|
||||
DESTROY(allocator, pair->k, apfl_expr_deinit);
|
||||
DESTROY(allocator, pair->v, apfl_expr_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_dict_deinit(struct apfl_expr_dict *dict)
|
||||
apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *dict)
|
||||
{
|
||||
DEINIT_LIST(dict->items, dict->len, apfl_expr_dict_pair_deinit);
|
||||
DEINIT_CAP_LIST(allocator, dict->items, dict->len, dict->cap, apfl_expr_dict_pair_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_call_deinit(struct apfl_expr_call *call)
|
||||
apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *call)
|
||||
{
|
||||
DESTROY(call->callee, apfl_expr_deinit);
|
||||
apfl_expr_list_deinit(&call->arguments);
|
||||
DESTROY(allocator, call->callee, apfl_expr_deinit);
|
||||
apfl_expr_list_deinit(allocator, &call->arguments);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_body_unref(struct apfl_expr_body *body)
|
||||
apfl_expr_body_unref(struct apfl_allocator allocator, struct apfl_expr_body *body)
|
||||
{
|
||||
if (body != NULL && apfl_refcount_dec(&body->refcount)) {
|
||||
DEINIT_LIST(body->items, body->len, apfl_expr_deinit);
|
||||
free(body);
|
||||
DEINIT_CAP_LIST(allocator, body->items, body->len, body->cap, apfl_expr_deinit);
|
||||
FREE_OBJ(allocator, body);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_const_deinit(struct apfl_expr_const *constant)
|
||||
apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *constant)
|
||||
{
|
||||
switch (constant->type) {
|
||||
case APFL_EXPR_CONST_NIL:
|
||||
|
|
@ -100,52 +100,52 @@ apfl_expr_const_deinit(struct apfl_expr_const *constant)
|
|||
// nop
|
||||
break;
|
||||
case APFL_EXPR_CONST_STRING:
|
||||
apfl_refcounted_string_unref(constant->string);
|
||||
apfl_refcounted_string_unref(allocator, constant->string);
|
||||
constant->string = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEINIT_GENERIC_LHS_RHS_EXPR(x, lhs_deiniter) \
|
||||
do { \
|
||||
DESTROY(x->lhs, lhs_deiniter); \
|
||||
DESTROY(x->rhs, apfl_expr_deinit); \
|
||||
#define DEINIT_GENERIC_LHS_RHS_EXPR(allocator, x, lhs_deiniter) \
|
||||
do { \
|
||||
DESTROY(allocator, x->lhs, lhs_deiniter); \
|
||||
DESTROY(allocator, x->rhs, apfl_expr_deinit); \
|
||||
} while (0)
|
||||
|
||||
void
|
||||
apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *pred)
|
||||
apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *pred)
|
||||
{
|
||||
DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_param_deinit);
|
||||
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_param_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_params_deinit(struct apfl_expr_params *params)
|
||||
apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *params)
|
||||
{
|
||||
DEINIT_LIST(params->params, params->len, apfl_expr_params_item_deinit);
|
||||
DEINIT_CAP_LIST(allocator, params->params, params->len, params->cap, apfl_expr_params_item_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_params_item_deinit(struct apfl_expr_params_item *item)
|
||||
apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *item)
|
||||
{
|
||||
apfl_expr_param_deinit(&item->param);
|
||||
apfl_expr_param_deinit(allocator, &item->param);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_param_deinit(struct apfl_expr_param *param)
|
||||
apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *param)
|
||||
{
|
||||
switch (param->type) {
|
||||
case APFL_EXPR_PARAM_VAR:
|
||||
apfl_refcounted_string_unref(param->var);
|
||||
apfl_refcounted_string_unref(allocator, param->var);
|
||||
param->var = NULL;
|
||||
break;
|
||||
case APFL_EXPR_PARAM_CONSTANT:
|
||||
apfl_expr_const_deinit(¶m->constant);
|
||||
apfl_expr_const_deinit(allocator, ¶m->constant);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_PREDICATE:
|
||||
apfl_expr_param_predicate_deinit(¶m->predicate);
|
||||
apfl_expr_param_predicate_deinit(allocator, ¶m->predicate);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_LIST:
|
||||
apfl_expr_params_deinit(¶m->list);
|
||||
apfl_expr_params_deinit(allocator, ¶m->list);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_BLANK:
|
||||
// nop
|
||||
|
|
@ -154,85 +154,84 @@ apfl_expr_param_deinit(struct apfl_expr_param *param)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *subfunc)
|
||||
apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *subfunc)
|
||||
{
|
||||
apfl_expr_params_deinit(&subfunc->params);
|
||||
apfl_expr_body_unref(subfunc->body);
|
||||
apfl_expr_params_deinit(allocator, &subfunc->params);
|
||||
apfl_expr_body_unref(allocator, subfunc->body);
|
||||
subfunc->body = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *cf)
|
||||
apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *cf)
|
||||
{
|
||||
DEINIT_LIST(cf->subfuncs, cf->len, apfl_expr_subfunc_deinit);
|
||||
DEINIT_CAP_LIST(allocator, cf->subfuncs, cf->len, cf->cap, apfl_expr_subfunc_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *pred)
|
||||
apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *pred)
|
||||
{
|
||||
DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_assignable_deinit);
|
||||
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_assignable_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_list_item_deinit(struct apfl_expr_assignable_list_item *item)
|
||||
apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *item)
|
||||
{
|
||||
apfl_expr_assignable_deinit(&item->assignable);
|
||||
apfl_expr_assignable_deinit(allocator, &item->assignable);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *list)
|
||||
apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *list)
|
||||
{
|
||||
DEINIT_LIST(list->items, list->len, apfl_expr_assignable_list_item_deinit);
|
||||
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_assignable_list_item_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_expr_assignable_var_or_member_dot *dot)
|
||||
apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *dot)
|
||||
{
|
||||
DESTROY(dot->lhs, apfl_expr_assignable_var_or_member_deinit);
|
||||
apfl_refcounted_string_unref(dot->rhs);
|
||||
DESTROY(allocator, dot->lhs, apfl_expr_assignable_var_or_member_deinit);
|
||||
apfl_refcounted_string_unref(allocator, dot->rhs);
|
||||
dot->rhs = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_var_or_member_at_deinit(struct apfl_expr_assignable_var_or_member_at *at)
|
||||
apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *at)
|
||||
{
|
||||
DESTROY(at->lhs, apfl_expr_assignable_var_or_member_deinit);
|
||||
DESTROY(at->rhs, apfl_expr_deinit);
|
||||
DESTROY(allocator, at->lhs, apfl_expr_assignable_var_or_member_deinit);
|
||||
DESTROY(allocator, at->rhs, apfl_expr_deinit);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
apfl_expr_assignable_var_or_member_deinit(struct apfl_expr_assignable_var_or_member *var_or_member)
|
||||
apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *var_or_member)
|
||||
{
|
||||
switch (var_or_member->type) {
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
||||
apfl_refcounted_string_unref(var_or_member->var);
|
||||
apfl_refcounted_string_unref(allocator, var_or_member->var);
|
||||
var_or_member->var = NULL;
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
||||
apfl_expr_assignable_var_or_member_dot_deinit(&var_or_member->dot);
|
||||
apfl_expr_assignable_var_or_member_dot_deinit(allocator, &var_or_member->dot);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
||||
apfl_expr_assignable_var_or_member_at_deinit(&var_or_member->at);
|
||||
apfl_expr_assignable_var_or_member_at_deinit(allocator, &var_or_member->at);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_deinit(struct apfl_expr_assignable *a)
|
||||
apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *a)
|
||||
{
|
||||
switch (a->type) {
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
||||
apfl_expr_assignable_var_or_member_deinit(&a->var_or_member);
|
||||
apfl_expr_assignable_var_or_member_deinit(allocator, &a->var_or_member);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
||||
apfl_expr_const_deinit(&a->constant);
|
||||
apfl_expr_const_deinit(allocator, &a->constant);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
||||
apfl_expr_assignable_predicate_deinit(&a->predicate);
|
||||
apfl_expr_assignable_predicate_deinit(allocator, &a->predicate);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_LIST:
|
||||
apfl_expr_assignable_list_deinit(&a->list);
|
||||
apfl_expr_assignable_list_deinit(allocator, &a->list);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_BLANK:
|
||||
// nop
|
||||
|
|
@ -241,24 +240,24 @@ apfl_expr_assignable_deinit(struct apfl_expr_assignable *a)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignment_deinit(struct apfl_expr_assignment *a)
|
||||
apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *a)
|
||||
{
|
||||
apfl_expr_assignable_deinit(&a->lhs);
|
||||
DESTROY(a->rhs, apfl_expr_deinit);
|
||||
apfl_expr_assignable_deinit(allocator, &a->lhs);
|
||||
DESTROY(allocator, a->rhs, apfl_expr_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_dot_deinit(struct apfl_expr_dot *dot)
|
||||
apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *dot)
|
||||
{
|
||||
DESTROY(dot->lhs, apfl_expr_deinit);
|
||||
apfl_refcounted_string_unref(dot->rhs);
|
||||
DESTROY(allocator, dot->lhs, apfl_expr_deinit);
|
||||
apfl_refcounted_string_unref(allocator, dot->rhs);
|
||||
dot->rhs = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_at_deinit(struct apfl_expr_at *at)
|
||||
apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *at)
|
||||
{
|
||||
DEINIT_GENERIC_LHS_RHS_EXPR(at, apfl_expr_deinit);
|
||||
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, at, apfl_expr_deinit);
|
||||
}
|
||||
|
||||
// Move functions
|
||||
|
|
@ -312,6 +311,15 @@ apfl_expr_move(struct apfl_expr *in)
|
|||
in->len = 0; \
|
||||
} while (0)
|
||||
|
||||
#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)
|
||||
|
||||
struct apfl_expr_list
|
||||
apfl_expr_list_move(struct apfl_expr_list *in)
|
||||
{
|
||||
|
|
@ -341,7 +349,7 @@ struct apfl_expr_dict
|
|||
apfl_expr_dict_move(struct apfl_expr_dict *in)
|
||||
{
|
||||
struct apfl_expr_dict out;
|
||||
MOVE_LIST(out, in, items, len);
|
||||
MOVE_CAP_LIST(out, in, items, len, cap);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -400,7 +408,7 @@ struct apfl_expr_params
|
|||
apfl_expr_params_move(struct apfl_expr_params *in)
|
||||
{
|
||||
struct apfl_expr_params out;
|
||||
MOVE_LIST(out, in, params, len);
|
||||
MOVE_CAP_LIST(out, in, params, len, cap);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +449,7 @@ struct apfl_expr_complex_func
|
|||
apfl_expr_complex_func_move(struct apfl_expr_complex_func *in)
|
||||
{
|
||||
struct apfl_expr_complex_func out;
|
||||
MOVE_LIST(out, in, subfuncs, len);
|
||||
MOVE_CAP_LIST(out, in, subfuncs, len, cap);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
|||
141
src/hashmap.c
141
src/hashmap.c
|
|
@ -2,14 +2,17 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alloc.h"
|
||||
#include "hashmap.h"
|
||||
#include "resizable.h"
|
||||
|
||||
struct apfl_hashmap_bucket {
|
||||
void *keys;
|
||||
void *values;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
|
||||
// These will be the same, unless a memory reallocation failed
|
||||
size_t keys_cap;
|
||||
size_t values_cap;
|
||||
};
|
||||
|
||||
#define FNV_PRIME 1099511628211U
|
||||
|
|
@ -32,6 +35,7 @@ apfl_hash_fnv1a(const void *data, size_t len)
|
|||
}
|
||||
|
||||
#define HAS_CALLBACK(map, cb) ((map).callbacks.cb != NULL)
|
||||
#define INVOKE_CALLBACK_NOARGS(map, cb) (map).callbacks.cb((map).callbacks.opaque)
|
||||
#define INVOKE_CALLBACK(map, cb, ...) (map).callbacks.cb((map).callbacks.opaque, __VA_ARGS__)
|
||||
|
||||
static bool
|
||||
|
|
@ -90,6 +94,14 @@ copy_value(const struct apfl_hashmap map, void *dest, void *src)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_deinit(const struct apfl_hashmap map)
|
||||
{
|
||||
if (HAS_CALLBACK(map, on_deinit)) {
|
||||
INVOKE_CALLBACK_NOARGS(map, on_deinit);
|
||||
}
|
||||
}
|
||||
|
||||
#define CAP_GROW 5
|
||||
static_assert(CAP_GROW >= 1, "CAP_GROW must be at least 1");
|
||||
|
||||
|
|
@ -131,24 +143,40 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void
|
|||
return true;
|
||||
}
|
||||
|
||||
if (bucket->len <= bucket->cap) {
|
||||
size_t new_cap = calc_new_cap(bucket->cap);
|
||||
if (bucket->keys_cap != bucket->values_cap) {
|
||||
assert(false); // A set operation was called on the hashmap,
|
||||
// after a previous set operation failed.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bucket->len <= bucket->keys_cap) {
|
||||
size_t new_cap = calc_new_cap(bucket->keys_cap);
|
||||
|
||||
void *newmem;
|
||||
|
||||
newmem = realloc(bucket->keys, new_cap * keysize);
|
||||
newmem = REALLOC_BYTES(
|
||||
map.allocator,
|
||||
bucket->keys,
|
||||
bucket->keys_cap * keysize,
|
||||
new_cap * keysize
|
||||
);
|
||||
if (newmem == NULL) {
|
||||
return false;
|
||||
}
|
||||
bucket->keys = newmem;
|
||||
bucket->keys_cap = new_cap;
|
||||
|
||||
newmem = realloc(bucket->values, new_cap * valsize);
|
||||
newmem = REALLOC_BYTES(
|
||||
map.allocator,
|
||||
bucket->values,
|
||||
bucket->values_cap * valsize,
|
||||
new_cap * valsize
|
||||
);
|
||||
if (newmem == NULL) {
|
||||
return false;
|
||||
}
|
||||
bucket->values = newmem;
|
||||
|
||||
bucket->cap = new_cap;
|
||||
bucket->values_cap = new_cap;
|
||||
}
|
||||
|
||||
copy_key(map, KVADDR(bucket->keys, keysize, bucket->len), key);
|
||||
|
|
@ -160,16 +188,32 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void
|
|||
}
|
||||
|
||||
static bool
|
||||
get_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void *value)
|
||||
peek_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void **value_ptr)
|
||||
{
|
||||
size_t i;
|
||||
if (!find_key_in_bucket(map, bucket, key, &i)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
if (value_ptr != NULL) {
|
||||
size_t valsize = map.valsize;
|
||||
copy_value(map, value, KVADDR(bucket->values, valsize, i));
|
||||
*value_ptr = KVADDR(bucket->values, valsize, i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
get_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void *value)
|
||||
{
|
||||
void *value_ptr;
|
||||
|
||||
if (!peek_in_bucket(map, bucket, key, &value_ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
copy_value(map, value, value_ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -217,25 +261,28 @@ delete_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, co
|
|||
static bool
|
||||
hashmap_init(
|
||||
struct apfl_hashmap *map,
|
||||
struct apfl_allocator allocator,
|
||||
struct apfl_hashmap_callbacks callbacks,
|
||||
size_t nbuckets,
|
||||
size_t keysize,
|
||||
size_t valsize
|
||||
) {
|
||||
map->callbacks = callbacks;
|
||||
map->allocator = allocator;
|
||||
map->keysize = keysize;
|
||||
map->valsize = valsize;
|
||||
map->nbuckets = nbuckets;
|
||||
if ((map->buckets = malloc(sizeof(struct apfl_hashmap_bucket) * nbuckets)) == NULL) {
|
||||
if ((map->buckets = ALLOC_LIST(allocator, struct apfl_hashmap_bucket, nbuckets)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nbuckets; i++) {
|
||||
map->buckets[i] = (struct apfl_hashmap_bucket) {
|
||||
.keys = NULL,
|
||||
.values = NULL,
|
||||
.len = 0,
|
||||
.cap = 0,
|
||||
.keys = NULL,
|
||||
.values = NULL,
|
||||
.len = 0,
|
||||
.keys_cap = 0,
|
||||
.values_cap = 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -245,11 +292,12 @@ hashmap_init(
|
|||
bool
|
||||
apfl_hashmap_init(
|
||||
struct apfl_hashmap *map,
|
||||
struct apfl_allocator allocator,
|
||||
struct apfl_hashmap_callbacks callbacks,
|
||||
size_t keysize,
|
||||
size_t valsize
|
||||
) {
|
||||
return hashmap_init(map, callbacks, INITIAL_NBUCKETS, keysize, valsize);
|
||||
return hashmap_init(map, allocator, callbacks, INITIAL_NBUCKETS, keysize, valsize);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -258,6 +306,12 @@ apfl_hashmap_delete(struct apfl_hashmap *map, const void *key)
|
|||
delete_in_bucket(*map, bucket_by_key(*map, key), key);
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_hashmap_peek(const struct apfl_hashmap *map, const void *key, void **value_ptr)
|
||||
{
|
||||
return get_in_bucket(*map, bucket_by_key(*map, key), key, value_ptr);
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_hashmap_get(const struct apfl_hashmap *map, const void *key, void *value)
|
||||
{
|
||||
|
|
@ -277,10 +331,11 @@ destroy_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket
|
|||
destroy_key(map, KVADDR(bucket->keys, map.keysize, i));
|
||||
destroy_value(map, KVADDR(bucket->values, map.valsize, i));
|
||||
}
|
||||
free(bucket->keys);
|
||||
free(bucket->values);
|
||||
FREE_BYTES(map.allocator, bucket->keys, bucket->keys_cap * map.keysize);
|
||||
FREE_BYTES(map.allocator, bucket->values, bucket->values_cap * map.valsize);
|
||||
bucket->len = 0;
|
||||
bucket->cap = 0;
|
||||
bucket->keys_cap = 0;
|
||||
bucket->values_cap = 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
|
@ -304,8 +359,11 @@ apfl_hashmap_deinit(struct apfl_hashmap *map)
|
|||
for (size_t i = 0; i < map->nbuckets; i++) {
|
||||
destroy_bucket(*map, &map->buckets[i]);
|
||||
}
|
||||
free(map->buckets);
|
||||
FREE_LIST(map->allocator, map->buckets, map->nbuckets);
|
||||
map->buckets = NULL;
|
||||
}
|
||||
|
||||
on_deinit(*map);
|
||||
}
|
||||
|
||||
struct apfl_hashmap
|
||||
|
|
@ -323,7 +381,7 @@ apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src)
|
|||
size_t keysize = src.keysize;
|
||||
size_t valsize = src.valsize;
|
||||
|
||||
if (!hashmap_init(dst, src.callbacks, src.nbuckets, keysize, valsize)) {
|
||||
if (!hashmap_init(dst, src.allocator, src.callbacks, src.nbuckets, keysize, valsize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -336,18 +394,19 @@ apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src)
|
|||
continue;
|
||||
}
|
||||
|
||||
dstbucket->keys = malloc(keysize * len);
|
||||
dstbucket->values = malloc(valsize * len);
|
||||
dstbucket->keys = ALLOC_BYTES(src.allocator, keysize * len);
|
||||
dstbucket->values = ALLOC_BYTES(src.allocator, valsize * len);
|
||||
if (dstbucket->keys == NULL || dstbucket->values == NULL) {
|
||||
free(dstbucket->keys);
|
||||
FREE_BYTES(src.allocator, dstbucket->keys, keysize * len);
|
||||
dstbucket->keys = NULL;
|
||||
free(dstbucket->values);
|
||||
FREE_BYTES(src.allocator, dstbucket->values, valsize * len);
|
||||
dstbucket->values = NULL;
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dstbucket->cap = len;
|
||||
dstbucket->keys_cap = len;
|
||||
dstbucket->values_cap = len;
|
||||
|
||||
for (size_t j = 0; j < len; j++) {
|
||||
copy_key(
|
||||
|
|
@ -428,7 +487,7 @@ apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *cursor)
|
|||
cursor_skip_empty_buckets(cursor);
|
||||
}
|
||||
|
||||
#define CURSOR_GET(cursor, out, bucketmemb, sizememb, copy) \
|
||||
#define CURSOR_PEEK(cursor, out, bucketmemb, sizememb) \
|
||||
struct apfl_hashmap_bucket *bucket = cursor_get_bucket(cursor); \
|
||||
\
|
||||
if (bucket == NULL) { \
|
||||
|
|
@ -441,20 +500,32 @@ apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *cursor)
|
|||
\
|
||||
size_t size = cursor.map->sizememb; \
|
||||
\
|
||||
copy( \
|
||||
*cursor.map, \
|
||||
out, \
|
||||
KVADDR(bucket->bucketmemb, size, cursor.i) \
|
||||
); \
|
||||
out = KVADDR(bucket->bucketmemb, size, cursor.i);
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_peek_key(struct apfl_hashmap_cursor cursor, void **key_ptr)
|
||||
{
|
||||
CURSOR_PEEK(cursor, *key_ptr, keys, keysize)
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_peek_value(struct apfl_hashmap_cursor cursor, void **value_ptr)
|
||||
{
|
||||
CURSOR_PEEK(cursor, *value_ptr, values, valsize)
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor cursor, void *key)
|
||||
{
|
||||
CURSOR_GET(cursor, key, keys, keysize, copy_key)
|
||||
void *key_ptr;
|
||||
apfl_hashmap_cursor_peek_key(cursor, &key_ptr);
|
||||
copy_key(*cursor.map, key, key_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor cursor, void *value)
|
||||
{
|
||||
CURSOR_GET(cursor, value, values, valsize, copy_value)
|
||||
void *value_ptr;
|
||||
apfl_hashmap_cursor_peek_value(cursor, &value_ptr);
|
||||
copy_value(*cursor.map, value, value_ptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ extern "C" {
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
#define APFL_HASH_FNV1A_INIT 14695981039346656037U // offset_basis for 64bit FNV-1(a)
|
||||
|
||||
typedef uint64_t apfl_hash;
|
||||
|
|
@ -42,10 +44,14 @@ struct apfl_hashmap_callbacks {
|
|||
// Copies a value. Returns true on success, false on failure.
|
||||
// If not provided, the bytes will be copied with memcpy.
|
||||
void (*copy_value) (void *opaque, void *dest, void *src);
|
||||
|
||||
// Called on deinitialization of the hashmap, if provided.
|
||||
void (*on_deinit) (void *opaque);
|
||||
};
|
||||
|
||||
struct apfl_hashmap {
|
||||
struct apfl_hashmap_callbacks callbacks;
|
||||
struct apfl_allocator allocator;
|
||||
size_t keysize;
|
||||
size_t valsize;
|
||||
size_t nbuckets;
|
||||
|
|
@ -55,8 +61,15 @@ struct apfl_hashmap {
|
|||
apfl_hash apfl_hash_fnv1a_add(const void *, size_t len, apfl_hash);
|
||||
apfl_hash apfl_hash_fnv1a(const void *, size_t len);
|
||||
|
||||
bool apfl_hashmap_init(struct apfl_hashmap *, struct apfl_hashmap_callbacks, size_t keysize, size_t valsize);
|
||||
bool apfl_hashmap_init(
|
||||
struct apfl_hashmap *,
|
||||
struct apfl_allocator,
|
||||
struct apfl_hashmap_callbacks,
|
||||
size_t keysize,
|
||||
size_t valsize
|
||||
);
|
||||
void apfl_hashmap_delete(struct apfl_hashmap *, const void *key);
|
||||
bool apfl_hashmap_peek(const struct apfl_hashmap *, const void *key, void **value_ptr);
|
||||
bool apfl_hashmap_get(const struct apfl_hashmap *, const void *key, void *value);
|
||||
bool apfl_hashmap_set(struct apfl_hashmap *, void *key, void *value);
|
||||
size_t apfl_hashmap_count(const struct apfl_hashmap);
|
||||
|
|
@ -70,6 +83,8 @@ bool apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src);
|
|||
struct apfl_hashmap_cursor apfl_hashmap_get_cursor(struct apfl_hashmap *);
|
||||
bool apfl_hashmap_cursor_is_end(struct apfl_hashmap_cursor);
|
||||
void apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *);
|
||||
void apfl_hashmap_cursor_peek_key(struct apfl_hashmap_cursor, void **key_ptr);
|
||||
void apfl_hashmap_cursor_peek_value(struct apfl_hashmap_cursor, void **value_ptr);
|
||||
void apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor, void *key);
|
||||
void apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor, void *value);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ init_int2int(testctx t)
|
|||
struct apfl_hashmap map;
|
||||
if (!apfl_hashmap_init(
|
||||
&map,
|
||||
test_allocator(t),
|
||||
(struct apfl_hashmap_callbacks) { .opaque = NULL },
|
||||
sizeof(int),
|
||||
sizeof(int)
|
||||
|
|
@ -169,6 +170,7 @@ init_str2str(testctx t)
|
|||
struct apfl_hashmap map;
|
||||
if (!apfl_hashmap_init(
|
||||
&map,
|
||||
test_allocator(t),
|
||||
(struct apfl_hashmap_callbacks) {
|
||||
.opaque = t,
|
||||
.keys_eq = str2str_keys_eq,
|
||||
|
|
|
|||
|
|
@ -8,19 +8,58 @@ extern "C" {
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
#define DEINIT_CAP_LIST(allocator, items, len, cap, item_deinit) \
|
||||
do { \
|
||||
if ((items) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
for (size_t i = 0; i < (len); i++) { \
|
||||
item_deinit(allocator, &((items)[i])); \
|
||||
} \
|
||||
FREE_LIST(allocator, items, cap); \
|
||||
len = 0; \
|
||||
cap = 0; \
|
||||
(items) = NULL; \
|
||||
} while(0)
|
||||
|
||||
#define DEINIT_LIST(items, len, item_deinit) \
|
||||
do { \
|
||||
if ((items) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
for (size_t i = 0; i < (len); i++) { \
|
||||
item_deinit(&((items)[i])); \
|
||||
} \
|
||||
len = 0; \
|
||||
free(items); \
|
||||
(items) = NULL; \
|
||||
#define DEINIT_LIST(allocator, items, len, item_deinit) \
|
||||
do { \
|
||||
if ((items) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
for (size_t i = 0; i < (len); i++) { \
|
||||
item_deinit(allocator, &((items)[i])); \
|
||||
} \
|
||||
FREE_LIST(allocator, items, len); \
|
||||
len = 0; \
|
||||
(items) = NULL; \
|
||||
} while(0)
|
||||
|
||||
#define DEINIT_LIST_WITH_ARGS(allocator, items, len, item_deinit, ...) \
|
||||
do { \
|
||||
if ((items) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
for (size_t i = 0; i < (len); i++) { \
|
||||
item_deinit(__VA_ARGS__, &((items)[i])); \
|
||||
} \
|
||||
FREE_LIST(allocator, items, len); \
|
||||
len = 0; \
|
||||
(items) = NULL; \
|
||||
} while(0)
|
||||
|
||||
#define DEINIT_CAP_LIST_WITH_ARGS(allocator, items, len, cap, item_deinit, ...) \
|
||||
do { \
|
||||
if ((items) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
for (size_t i = 0; i < (len); i++) { \
|
||||
item_deinit(__VA_ARGS__, &((items)[i])); \
|
||||
} \
|
||||
FREE_LIST(allocator, items, cap); \
|
||||
len = 0; \
|
||||
cap = 0; \
|
||||
(items) = NULL; \
|
||||
} while(0)
|
||||
|
||||
#define MOVEPTR(out, in) \
|
||||
|
|
@ -29,18 +68,20 @@ extern "C" {
|
|||
in = NULL; \
|
||||
} while(0)
|
||||
|
||||
// ALLOC_LIST allocates memory for a list of n values of type T.
|
||||
// n == 0 will always result in NULL (not guaranteed by calloc()) and the
|
||||
// result will be cast into a pointer to T (this way the compiler can warn us,
|
||||
// if we try to allocate memory for a wrong type). Also if we always use
|
||||
// calloc(), the allocated memory is in a defined state.
|
||||
#define ALLOC_LIST(T, n) (T *)((n) == 0 ? NULL : calloc((n), sizeof(T)))
|
||||
|
||||
#define ALLOC(T) ALLOC_LIST(T, 1)
|
||||
|
||||
// Aliases to commonly used functions / macros
|
||||
|
||||
#define DESTROY APFL_DESTROY
|
||||
// DESTROY destroys a dynamically allocated value.
|
||||
// It will first deinit the value using deiniter,
|
||||
// free the memory and then set the variable to NULL.
|
||||
// It is always allowed to destroy an already destroyed
|
||||
// or deinited value.
|
||||
#define DESTROY(allocator, var, deiniter) \
|
||||
do { \
|
||||
if ((var) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
deiniter(allocator, var); \
|
||||
FREE_OBJ(allocator, var); \
|
||||
(var) = NULL; \
|
||||
} while(0)
|
||||
|
||||
// Internal use only functions
|
||||
|
||||
|
|
|
|||
19
src/main.c
19
src/main.c
|
|
@ -35,7 +35,7 @@ repl_source_reader(void *context, char *buf, size_t *len, bool need)
|
|||
}
|
||||
|
||||
static int
|
||||
repl_tokenizer(apfl_tokenizer_ptr tokenizer)
|
||||
repl_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer)
|
||||
{
|
||||
while (true) {
|
||||
struct apfl_error err;
|
||||
|
|
@ -45,7 +45,7 @@ repl_tokenizer(apfl_tokenizer_ptr tokenizer)
|
|||
case APFL_PARSE_OK:
|
||||
token = apfl_tokenizer_get_token(tokenizer);
|
||||
apfl_token_print(token, stdout);
|
||||
apfl_token_deinit(&token);
|
||||
apfl_token_deinit(allocator, &token);
|
||||
|
||||
break;
|
||||
case APFL_PARSE_EOF:
|
||||
|
|
@ -89,13 +89,13 @@ repl_parser_generic(apfl_parser_ptr parser, struct apfl_expr *expr, int *rv)
|
|||
}
|
||||
|
||||
static int
|
||||
repl_parser(apfl_parser_ptr parser)
|
||||
repl_parser(struct apfl_allocator allocator, apfl_parser_ptr parser)
|
||||
{
|
||||
struct apfl_expr expr;
|
||||
int rv;
|
||||
while (repl_parser_generic(parser, &expr, &rv)) {
|
||||
apfl_expr_print(expr, stdout);
|
||||
apfl_expr_deinit(&expr);
|
||||
apfl_expr_deinit(allocator, &expr);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -141,18 +141,19 @@ main(int argc, const char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
struct apfl_allocator allocator = apfl_allocator_default();
|
||||
apfl_tokenizer_ptr tokenizer = NULL;
|
||||
apfl_parser_ptr parser = NULL;
|
||||
apfl_ctx ctx = NULL;
|
||||
|
||||
if ((tokenizer = apfl_tokenizer_new(repl_source_reader, NULL)) == NULL) {
|
||||
if ((tokenizer = apfl_tokenizer_new(allocator, repl_source_reader, NULL)) == NULL) {
|
||||
fprintf(stderr, "Failed initializing tokenizer\n");
|
||||
rv = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (mode >= REPL_PARSER) {
|
||||
if ((parser = apfl_parser_new(apfl_tokenizer_as_token_source(tokenizer))) == NULL) {
|
||||
if ((parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(tokenizer))) == NULL) {
|
||||
fprintf(stderr, "Failed initializing parser\n");
|
||||
rv = 1;
|
||||
goto exit;
|
||||
|
|
@ -160,7 +161,7 @@ main(int argc, const char **argv)
|
|||
}
|
||||
|
||||
if (mode >= REPL_EVAL) {
|
||||
if ((ctx = apfl_ctx_new()) == NULL) {
|
||||
if ((ctx = apfl_ctx_new(allocator)) == NULL) {
|
||||
fprintf(stderr, "Failed initializing context\n");
|
||||
rv = 1;
|
||||
goto exit;
|
||||
|
|
@ -169,10 +170,10 @@ main(int argc, const char **argv)
|
|||
|
||||
switch (mode) {
|
||||
case REPL_TOKENIZER:
|
||||
rv = repl_tokenizer(tokenizer);
|
||||
rv = repl_tokenizer(allocator, tokenizer);
|
||||
break;
|
||||
case REPL_PARSER:
|
||||
rv = repl_parser(parser);
|
||||
rv = repl_parser(allocator, parser);
|
||||
break;
|
||||
case REPL_EVAL:
|
||||
rv = repl_eval(parser, ctx);
|
||||
|
|
|
|||
492
src/parser.c
492
src/parser.c
File diff suppressed because it is too large
Load diff
|
|
@ -3,10 +3,12 @@
|
|||
#include "apfl.h"
|
||||
#include "resizable.h"
|
||||
|
||||
#include "alloc.h"
|
||||
#include "test.h"
|
||||
|
||||
struct parser_test {
|
||||
testctx t;
|
||||
struct apfl_allocator allocator;
|
||||
void *source_reader_ctx;
|
||||
apfl_tokenizer_ptr tokenizer;
|
||||
apfl_parser_ptr parser;
|
||||
|
|
@ -15,17 +17,21 @@ struct parser_test {
|
|||
static struct parser_test *
|
||||
new_parser_test(testctx t, const char *source)
|
||||
{
|
||||
struct apfl_allocator allocator = test_allocator(t);
|
||||
|
||||
struct parser_test *pt = must_alloc(t, sizeof(struct parser_test));
|
||||
pt->t = t;
|
||||
if ((pt->source_reader_ctx = apfl_string_source_reader_new(apfl_string_view_from(source))) == NULL) {
|
||||
pt->allocator = allocator;
|
||||
|
||||
if ((pt->source_reader_ctx = apfl_string_source_reader_new(allocator, apfl_string_view_from(source))) == NULL) {
|
||||
test_fatalf(t, "Failed initializing source reader");
|
||||
}
|
||||
|
||||
if ((pt->tokenizer = apfl_tokenizer_new(apfl_string_source_reader, pt->source_reader_ctx)) == NULL) {
|
||||
if ((pt->tokenizer = apfl_tokenizer_new(allocator, apfl_string_source_reader, pt->source_reader_ctx)) == NULL) {
|
||||
test_fatalf(t, "Failed initializing the tokenizer");
|
||||
}
|
||||
|
||||
if ((pt->parser = apfl_parser_new(apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) {
|
||||
if ((pt->parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) {
|
||||
test_fatalf(t, "Failed initializing the parser");
|
||||
}
|
||||
|
||||
|
|
@ -73,8 +79,8 @@ expect_expr(struct parser_test *pt, struct apfl_expr expected)
|
|||
test_failf(pt->t, "Have:");
|
||||
apfl_expr_print(expr, stderr);
|
||||
}
|
||||
apfl_expr_deinit(&expr);
|
||||
apfl_expr_deinit(&expected);
|
||||
apfl_expr_deinit(pt->allocator, &expr);
|
||||
apfl_expr_deinit(pt->allocator, &expected);
|
||||
break;
|
||||
case APFL_PARSE_EOF:
|
||||
test_fatalf(pt->t, "Extected an expression but got EOF");
|
||||
|
|
@ -111,7 +117,7 @@ expect_error_of_type(struct parser_test *pt, enum apfl_error_type want)
|
|||
static apfl_refcounted_string
|
||||
new_string(struct parser_test *pt, const char *in)
|
||||
{
|
||||
apfl_refcounted_string out = apfl_string_copy_into_new_refcounted(apfl_string_view_from(in));
|
||||
apfl_refcounted_string out = apfl_string_copy_into_new_refcounted(pt->allocator, apfl_string_view_from(in));
|
||||
if (out == NULL) {
|
||||
test_fatalf(pt->t, "Failed copying string in new_string");
|
||||
}
|
||||
|
|
@ -121,7 +127,7 @@ new_string(struct parser_test *pt, const char *in)
|
|||
static void *
|
||||
new_helper(struct parser_test *pt, size_t size, void *data)
|
||||
{
|
||||
void *out = must_alloc(pt->t, size);
|
||||
void *out = ALLOC_BYTES(pt->allocator, size);
|
||||
memcpy(out, data, size);
|
||||
return out;
|
||||
}
|
||||
|
|
@ -161,9 +167,10 @@ struct test_pre_body {
|
|||
static struct apfl_expr_body *
|
||||
create_body(struct parser_test *pt, struct test_pre_body pre_body)
|
||||
{
|
||||
struct apfl_expr_body *body = must_alloc(pt->t, sizeof(struct apfl_expr_body));
|
||||
struct apfl_expr_body *body = ALLOC_OBJ(pt->allocator, struct apfl_expr_body);
|
||||
body->items = pre_body.items;
|
||||
body->len = pre_body.len;
|
||||
body->cap = pre_body.len;
|
||||
body->refcount = 1;
|
||||
return body;
|
||||
}
|
||||
|
|
@ -179,48 +186,69 @@ enum listbuilder_cmd {
|
|||
|
||||
#define LISTBUILDER_NAME(n) listbuilder_##n
|
||||
#define LISTBUILDER_ITEM_NAME(n) listbuilder_item_##n
|
||||
#define MKLISTBUILDER(name, listtype, itemtype, items_memb, len_memb) \
|
||||
struct LISTBUILDER_ITEM_NAME(name) { \
|
||||
bool has_item; \
|
||||
itemtype item; \
|
||||
}; \
|
||||
listtype \
|
||||
LISTBUILDER_NAME(name)( \
|
||||
struct parser_test *pt, \
|
||||
struct LISTBUILDER_ITEM_NAME(name) items[] \
|
||||
) { \
|
||||
listtype body; \
|
||||
size_t cap = 0; \
|
||||
#define LISTBUILDER_FILL_LIST(pt, itemtype, items, list_items, list_len, cap) \
|
||||
apfl_resizable_init((void **)&list_items, &list_len, &cap); \
|
||||
\
|
||||
apfl_resizable_init((void **)&body.items_memb, &body.len_memb, &cap); \
|
||||
\
|
||||
for (items++; items->has_item; items++) { \
|
||||
if (!apfl_resizable_append( \
|
||||
sizeof(itemtype), \
|
||||
(void **)&body.items_memb, \
|
||||
&body.len, \
|
||||
&cap, \
|
||||
&items->item, \
|
||||
1 \
|
||||
)) { \
|
||||
test_fatalf(pt->t, "Failed appending"); \
|
||||
assert(false); \
|
||||
} \
|
||||
for (items++; items->has_item; items++) { \
|
||||
if (!apfl_resizable_append( \
|
||||
pt->allocator, \
|
||||
sizeof(itemtype), \
|
||||
(void **)&list_items, \
|
||||
&list_len, \
|
||||
&cap, \
|
||||
&items->item, \
|
||||
1 \
|
||||
)) { \
|
||||
test_fatalf(pt->t, "Failed appending"); \
|
||||
assert(false); \
|
||||
} \
|
||||
\
|
||||
return body; \
|
||||
} \
|
||||
|
||||
#define MKLISTBUILDER(name, listtype, itemtype, items_memb) \
|
||||
struct LISTBUILDER_ITEM_NAME(name) { \
|
||||
bool has_item; \
|
||||
itemtype item; \
|
||||
}; \
|
||||
listtype \
|
||||
LISTBUILDER_NAME(name)( \
|
||||
struct parser_test *pt, \
|
||||
struct LISTBUILDER_ITEM_NAME(name) items[] \
|
||||
) { \
|
||||
listtype list; \
|
||||
size_t cap = 0; \
|
||||
\
|
||||
LISTBUILDER_FILL_LIST(pt, itemtype, items, list.items_memb, list.len, cap) \
|
||||
\
|
||||
return list; \
|
||||
} \
|
||||
|
||||
#define MKCAPLISTBUILDER(name, listtype, itemtype, items_memb) \
|
||||
struct LISTBUILDER_ITEM_NAME(name) { \
|
||||
bool has_item; \
|
||||
itemtype item; \
|
||||
}; \
|
||||
listtype \
|
||||
LISTBUILDER_NAME(name)( \
|
||||
struct parser_test *pt, \
|
||||
struct LISTBUILDER_ITEM_NAME(name) items[] \
|
||||
) { \
|
||||
listtype list; \
|
||||
\
|
||||
LISTBUILDER_FILL_LIST(pt, itemtype, items, list.items_memb, list.len, list.cap) \
|
||||
\
|
||||
return list; \
|
||||
} \
|
||||
|
||||
#define LIST_BEGIN(pt, builder) (LISTBUILDER_NAME(builder)(pt, (struct LISTBUILDER_ITEM_NAME(builder)[]) {{.has_item=false
|
||||
#define LIST_END }, {.has_item = false}}))
|
||||
#define LIST_ADD }, {.has_item = true, .item =
|
||||
|
||||
MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items, len)
|
||||
MKLISTBUILDER(body, struct test_pre_body, struct apfl_expr, items, len)
|
||||
MKLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items, len)
|
||||
MKLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs, len)
|
||||
MKLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_params_item, params, len)
|
||||
MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable_list_item, items, len)
|
||||
MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items)
|
||||
MKLISTBUILDER(body, struct test_pre_body, struct apfl_expr, items)
|
||||
MKCAPLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items)
|
||||
MKCAPLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs)
|
||||
MKCAPLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_params_item, params)
|
||||
MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable_list_item, items)
|
||||
|
||||
#define POS(l, c) (struct apfl_position) { .line = l, .col = c }
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alloc.h"
|
||||
#include "resizable.h"
|
||||
|
||||
void
|
||||
|
|
@ -15,8 +16,14 @@ apfl_resizable_init(void **mem, size_t *len, size_t *cap)
|
|||
}
|
||||
|
||||
bool
|
||||
apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t newlen)
|
||||
{
|
||||
apfl_resizable_resize(
|
||||
struct apfl_allocator allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *len,
|
||||
size_t *cap,
|
||||
size_t newlen
|
||||
) {
|
||||
// TODO: We're wasteful here by never actually shrinking the memory.
|
||||
if (newlen <= *len || newlen < *cap) {
|
||||
*len = newlen;
|
||||
|
|
@ -25,7 +32,7 @@ apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, si
|
|||
|
||||
assert(newlen >= *cap);
|
||||
|
||||
if (!apfl_resizable_ensure_cap(elem_size, mem, cap, newlen)) {
|
||||
if (!apfl_resizable_ensure_cap(allocator, elem_size, mem, cap, newlen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -34,8 +41,13 @@ apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, si
|
|||
}
|
||||
|
||||
bool
|
||||
apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want_cap)
|
||||
{
|
||||
apfl_resizable_ensure_cap(
|
||||
struct apfl_allocator allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *cap,
|
||||
size_t want_cap
|
||||
) {
|
||||
if (want_cap <= *cap) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -43,7 +55,7 @@ apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want
|
|||
// TODO: We currently simply grow the memory to have space for exactly
|
||||
// want_cap elements. It would probably be smarter to grow the memory
|
||||
// a bit larger to reduce calls to realloc.
|
||||
void *newmem = realloc(*mem, want_cap * elem_size);
|
||||
void *newmem = REALLOC_BYTES(allocator, *mem, *cap * elem_size, want_cap * elem_size);
|
||||
if (newmem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -54,12 +66,19 @@ apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want
|
|||
}
|
||||
|
||||
bool
|
||||
apfl_resizable_ensure_cap_for_more_elements(size_t elem_size, void **mem, size_t len, size_t *cap, size_t more_elements)
|
||||
{
|
||||
return apfl_resizable_ensure_cap(elem_size, mem, cap, len + more_elements); // TODO: What if len + more_elements overflows?
|
||||
apfl_resizable_ensure_cap_for_more_elements(
|
||||
struct apfl_allocator allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t len,
|
||||
size_t *cap,
|
||||
size_t more_elements
|
||||
) {
|
||||
return apfl_resizable_ensure_cap(allocator, elem_size, mem, cap, len + more_elements); // TODO: What if len + more_elements overflows?
|
||||
}
|
||||
|
||||
bool apfl_resizable_splice(
|
||||
struct apfl_allocator allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *len,
|
||||
|
|
@ -75,6 +94,7 @@ bool apfl_resizable_splice(
|
|||
|
||||
if (other_len > cut_len) {
|
||||
if (!apfl_resizable_ensure_cap_for_more_elements(
|
||||
allocator,
|
||||
elem_size,
|
||||
mem,
|
||||
*len,
|
||||
|
|
@ -108,9 +128,10 @@ bool apfl_resizable_splice(
|
|||
}
|
||||
|
||||
bool
|
||||
apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len)
|
||||
apfl_resizable_append(struct apfl_allocator allocator, size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len)
|
||||
{
|
||||
return apfl_resizable_splice(
|
||||
allocator,
|
||||
elem_size,
|
||||
mem,
|
||||
len,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
#define APFL_RESIZABLE_TRAIT(T, N) \
|
||||
T* N; \
|
||||
size_t len; \
|
||||
|
|
@ -13,12 +15,33 @@
|
|||
|
||||
void apfl_resizable_init(void **mem, size_t *len, size_t *cap);
|
||||
|
||||
bool apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t newlen);
|
||||
bool apfl_resizable_resize(
|
||||
struct apfl_allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *len,
|
||||
size_t *cap,
|
||||
size_t newlen
|
||||
);
|
||||
|
||||
bool apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want_cap);
|
||||
bool apfl_resizable_ensure_cap_for_more_elements(size_t elem_size, void **mem, size_t len, size_t *cap, size_t more_elements);
|
||||
bool apfl_resizable_ensure_cap(
|
||||
struct apfl_allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *cap,
|
||||
size_t want_cap
|
||||
);
|
||||
bool apfl_resizable_ensure_cap_for_more_elements(
|
||||
struct apfl_allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t len,
|
||||
size_t *cap,
|
||||
size_t more_elements
|
||||
);
|
||||
|
||||
bool apfl_resizable_splice(
|
||||
struct apfl_allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *len,
|
||||
|
|
@ -29,7 +52,15 @@ bool apfl_resizable_splice(
|
|||
size_t other_len
|
||||
);
|
||||
|
||||
bool apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len);
|
||||
bool apfl_resizable_append(
|
||||
struct apfl_allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *len,
|
||||
size_t *cap,
|
||||
const void *other_mem,
|
||||
size_t other_len
|
||||
);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "test.h"
|
||||
|
||||
#include "alloc.h"
|
||||
#include "resizable.h"
|
||||
|
||||
struct splice_test_data {
|
||||
|
|
@ -25,14 +26,17 @@ getlen(const int *data)
|
|||
static void
|
||||
run_splice_test(testctx t, struct splice_test_data st)
|
||||
{
|
||||
struct apfl_allocator allocator = test_allocator(t);
|
||||
|
||||
size_t len = getlen(st.old);
|
||||
int *mem = must_alloc(t, sizeof(int) * len);
|
||||
int *mem = ALLOC_LIST(allocator, int, len);
|
||||
memcpy(mem, st.old, sizeof(int) * len);
|
||||
size_t cap = len;
|
||||
|
||||
size_t other_len = getlen(st.other);
|
||||
|
||||
if (!apfl_resizable_splice(
|
||||
allocator,
|
||||
sizeof(int),
|
||||
(void **)&mem,
|
||||
&len,
|
||||
|
|
@ -63,7 +67,7 @@ run_splice_test(testctx t, struct splice_test_data st)
|
|||
}
|
||||
}
|
||||
|
||||
free(mem);
|
||||
FREE_LIST(allocator, mem, cap);
|
||||
}
|
||||
|
||||
TEST(splice_empty, t) {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "apfl.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include "alloc.h"
|
||||
|
||||
struct string_source_reader {
|
||||
struct apfl_allocator allocator;
|
||||
struct apfl_string_view sv;
|
||||
size_t off;
|
||||
};
|
||||
|
|
@ -27,13 +29,14 @@ apfl_string_source_reader(void *opaque, char *buf, size_t *len, bool need)
|
|||
}
|
||||
|
||||
void *
|
||||
apfl_string_source_reader_new(struct apfl_string_view sv)
|
||||
apfl_string_source_reader_new(struct apfl_allocator allocator, struct apfl_string_view sv)
|
||||
{
|
||||
struct string_source_reader *ctx = ALLOC(struct string_source_reader);
|
||||
struct string_source_reader *ctx = ALLOC_OBJ(allocator, struct string_source_reader);
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->allocator = allocator;
|
||||
ctx->sv = sv;
|
||||
ctx->off = 0;
|
||||
|
||||
|
|
@ -43,5 +46,10 @@ apfl_string_source_reader_new(struct apfl_string_view sv)
|
|||
void
|
||||
apfl_string_source_reader_destroy(void *opaque)
|
||||
{
|
||||
free(opaque);
|
||||
struct string_source_reader *ctx = opaque;
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
FREE_OBJ(ctx->allocator, ctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "apfl.h"
|
||||
|
||||
#include "alloc.h"
|
||||
#include "internal.h"
|
||||
#include "resizable.h"
|
||||
|
||||
|
|
@ -66,26 +67,28 @@ apfl_string_blank(void)
|
|||
return (struct apfl_string) {
|
||||
.bytes = NULL,
|
||||
.len = 0,
|
||||
.cap = 0
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
apfl_string_deinit(struct apfl_string *string)
|
||||
apfl_string_deinit(struct apfl_allocator allocator, struct apfl_string *string)
|
||||
{
|
||||
free(string->bytes);
|
||||
FREE_BYTES(allocator, string->bytes, string->cap);
|
||||
*string = apfl_string_blank();
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_string_copy(struct apfl_string *dst, struct apfl_string_view src)
|
||||
apfl_string_copy(struct apfl_allocator allocator, struct apfl_string *dst, struct apfl_string_view src)
|
||||
{
|
||||
apfl_string_deinit(dst);
|
||||
if ((dst->bytes = malloc(src.len)) == NULL) {
|
||||
apfl_string_deinit(allocator, dst);
|
||||
if ((dst->bytes = ALLOC_BYTES(allocator, src.len)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(dst->bytes, src.bytes, src.len);
|
||||
dst->len = src.len;
|
||||
dst->cap = src.len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -98,23 +101,31 @@ apfl_string_move(struct apfl_string *src)
|
|||
return out;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_string_builder_init(struct apfl_string_builder *builder)
|
||||
static void
|
||||
builder_reset(struct apfl_string_builder *builder)
|
||||
{
|
||||
apfl_resizable_init((void **)&(builder->bytes), &(builder->len), &(builder->cap));
|
||||
}
|
||||
|
||||
void
|
||||
apfl_string_builder_init(struct apfl_allocator allocator, struct apfl_string_builder *builder)
|
||||
{
|
||||
builder->allocator = allocator;
|
||||
builder_reset(builder);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_string_builder_deinit(struct apfl_string_builder *builder)
|
||||
{
|
||||
free(builder->bytes);
|
||||
apfl_string_builder_init(builder);
|
||||
FREE_BYTES(builder->allocator, builder->bytes, builder->cap);
|
||||
builder_reset(builder);
|
||||
}
|
||||
|
||||
static bool
|
||||
append_bytes(struct apfl_string_builder *builder, const char *bytes, size_t len)
|
||||
{
|
||||
return apfl_resizable_append(
|
||||
builder->allocator,
|
||||
sizeof(char),
|
||||
APFL_RESIZABLE_ARGS(*builder, bytes),
|
||||
bytes,
|
||||
|
|
@ -141,8 +152,9 @@ apfl_string_builder_move_string(struct apfl_string_builder *builder)
|
|||
|
||||
str.bytes = builder->bytes;
|
||||
str.len = builder->len;
|
||||
str.cap = builder->cap;
|
||||
|
||||
apfl_string_builder_init(builder);
|
||||
builder_reset(builder);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
@ -158,16 +170,16 @@ apfl_string_view_from_refcounted_string(apfl_refcounted_string rcstring)
|
|||
}
|
||||
|
||||
apfl_refcounted_string
|
||||
apfl_string_copy_into_new_refcounted(struct apfl_string_view sv)
|
||||
apfl_string_copy_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string_view sv)
|
||||
{
|
||||
struct apfl_string str = apfl_string_blank();
|
||||
if (!apfl_string_copy(&str, sv)) {
|
||||
if (!apfl_string_copy(allocator, &str, sv)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(&str);
|
||||
apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(allocator, &str);
|
||||
if (rcstring == NULL) {
|
||||
apfl_string_deinit(&str);
|
||||
apfl_string_deinit(allocator, &str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -175,9 +187,12 @@ apfl_string_copy_into_new_refcounted(struct apfl_string_view sv)
|
|||
}
|
||||
|
||||
apfl_refcounted_string
|
||||
apfl_string_move_into_new_refcounted(struct apfl_string *src)
|
||||
apfl_string_move_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string *src)
|
||||
{
|
||||
apfl_refcounted_string dst = ALLOC(struct apfl_refcounted_string_data);
|
||||
apfl_refcounted_string dst = ALLOC_OBJ(
|
||||
allocator,
|
||||
struct apfl_refcounted_string_data
|
||||
);
|
||||
if (dst == NULL) {
|
||||
// TODO: Or should we free src here?
|
||||
return NULL;
|
||||
|
|
@ -201,17 +216,17 @@ apfl_refcounted_string_incref(apfl_refcounted_string rcstring)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_refcounted_string_unref(apfl_refcounted_string rcstring)
|
||||
apfl_refcounted_string_unref(struct apfl_allocator allocator, apfl_refcounted_string rcstring)
|
||||
{
|
||||
if (rcstring != NULL && apfl_refcount_dec(&rcstring->refcount)) {
|
||||
apfl_string_deinit(&rcstring->string);
|
||||
free(rcstring);
|
||||
apfl_string_deinit(allocator, &rcstring->string);
|
||||
FREE_OBJ(allocator, rcstring);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apfl_refcounted_string_unref_ptr(apfl_refcounted_string *rcstring_ptr)
|
||||
apfl_refcounted_string_unref_ptr(struct apfl_allocator allocator, apfl_refcounted_string *rcstring_ptr)
|
||||
{
|
||||
apfl_refcounted_string_unref(*rcstring_ptr);
|
||||
apfl_refcounted_string_unref(allocator, *rcstring_ptr);
|
||||
*rcstring_ptr = NULL;
|
||||
}
|
||||
|
|
|
|||
32
src/test.h
32
src/test.h
|
|
@ -13,10 +13,13 @@ extern "C" {
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
struct testctx_struct {
|
||||
jmp_buf *fataljmp;
|
||||
const char *name;
|
||||
const char *prefix;
|
||||
struct apfl_allocator allocator_unverified;
|
||||
bool ok;
|
||||
};
|
||||
|
||||
|
|
@ -71,6 +74,30 @@ test_fatalf(testctx t, const char* fmt, ...) {
|
|||
test_fatal(t);
|
||||
}
|
||||
|
||||
static void *
|
||||
test_must_allocator_cb(void *opaque, void *oldptr, size_t oldsize, size_t newsize)
|
||||
{
|
||||
(void)oldsize;
|
||||
testctx t = opaque;
|
||||
|
||||
if (newsize == 0) {
|
||||
free(oldptr);
|
||||
return NULL;
|
||||
} else {
|
||||
void *mem = realloc(oldptr, newsize);
|
||||
if (mem == NULL) {
|
||||
test_fatalf(t, "Failed to allocate memory");
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
struct apfl_allocator
|
||||
test_allocator(testctx t)
|
||||
{
|
||||
return apfl_allocator_wrap_verifying(&t->allocator_unverified);
|
||||
}
|
||||
|
||||
#define TESTPREFIXSIZE 1024
|
||||
|
||||
bool
|
||||
|
|
@ -87,6 +114,11 @@ test_run_test(struct testdef test)
|
|||
return false;
|
||||
}
|
||||
|
||||
t->allocator_unverified = (struct apfl_allocator) {
|
||||
.opaque = t,
|
||||
.alloc = test_must_allocator_cb,
|
||||
};
|
||||
|
||||
snprintf(prefix, TESTPREFIXSIZE, "%s: ", test.name);
|
||||
|
||||
if(setjmp(*here) == 0) {
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ has_numeric_data(enum apfl_token_type type)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_token_deinit(struct apfl_token *token)
|
||||
apfl_token_deinit(struct apfl_allocator allocator, struct apfl_token *token)
|
||||
{
|
||||
if (has_text_data(token->type)) {
|
||||
apfl_refcounted_string_unref_ptr(&token->text);
|
||||
apfl_refcounted_string_unref_ptr(allocator, &token->text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@
|
|||
|
||||
#include "apfl.h"
|
||||
|
||||
#include "alloc.h"
|
||||
|
||||
#define BUFSIZE 4096
|
||||
typedef int buf_offset;
|
||||
static_assert(INT_MAX >= BUFSIZE, "BUFSIZE is too large for type buf_offset");
|
||||
|
||||
struct apfl_tokenizer {
|
||||
struct apfl_allocator allocator;
|
||||
apfl_source_reader_cb source_reader;
|
||||
void *source_reader_context;
|
||||
char *buf;
|
||||
|
|
@ -39,18 +42,19 @@ struct apfl_tokenizer {
|
|||
};
|
||||
|
||||
apfl_tokenizer_ptr
|
||||
apfl_tokenizer_new(apfl_source_reader_cb source_reader, void *context)
|
||||
apfl_tokenizer_new(struct apfl_allocator allocator, apfl_source_reader_cb source_reader, void *context)
|
||||
{
|
||||
apfl_tokenizer_ptr tokenizer = malloc(sizeof(struct apfl_tokenizer));
|
||||
apfl_tokenizer_ptr tokenizer = ALLOC_OBJ(allocator, struct apfl_tokenizer);
|
||||
if (tokenizer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tokenizer->allocator = allocator;
|
||||
tokenizer->source_reader = source_reader;
|
||||
tokenizer->source_reader_context = context;
|
||||
|
||||
if ((tokenizer->buf = malloc(BUFSIZE)) == NULL) {
|
||||
free(tokenizer);
|
||||
if ((tokenizer->buf = ALLOC_BYTES(allocator, BUFSIZE)) == NULL) {
|
||||
FREE_OBJ(allocator, tokenizer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +79,8 @@ apfl_tokenizer_destroy(apfl_tokenizer_ptr tokenizer)
|
|||
return;
|
||||
}
|
||||
|
||||
free(tokenizer->buf);
|
||||
free(tokenizer);
|
||||
FREE_BYTES(tokenizer->allocator, tokenizer->buf, BUFSIZE);
|
||||
FREE_OBJ(tokenizer->allocator, tokenizer);
|
||||
}
|
||||
|
||||
struct apfl_token
|
||||
|
|
@ -260,9 +264,12 @@ static apfl_refcounted_string
|
|||
rcstring_from_string_builder(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder *builder)
|
||||
{
|
||||
struct apfl_string string = apfl_string_builder_move_string(builder);
|
||||
apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(&string);
|
||||
apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(
|
||||
tokenizer->allocator,
|
||||
&string
|
||||
);
|
||||
if (rcstring == NULL) {
|
||||
apfl_string_deinit(&string);
|
||||
apfl_string_deinit(tokenizer->allocator, &string);
|
||||
tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
|
||||
}
|
||||
return rcstring;
|
||||
|
|
@ -277,7 +284,7 @@ comment(apfl_tokenizer_ptr tokenizer)
|
|||
struct apfl_position last_pos;
|
||||
|
||||
struct apfl_string_builder text;
|
||||
apfl_string_builder_init(&text);
|
||||
apfl_string_builder_init(tokenizer->allocator, &text);
|
||||
|
||||
for (;;) {
|
||||
apfl_refcounted_string rcstring;
|
||||
|
|
@ -621,7 +628,7 @@ static enum apfl_parse_result
|
|||
string(apfl_tokenizer_ptr tokenizer)
|
||||
{
|
||||
struct apfl_string_builder text;
|
||||
apfl_string_builder_init(&text);
|
||||
apfl_string_builder_init(tokenizer->allocator, &text);
|
||||
|
||||
enum apfl_parse_result out = inner_string(tokenizer, &text);
|
||||
|
||||
|
|
@ -772,7 +779,7 @@ static enum apfl_parse_result
|
|||
maybe_name(apfl_tokenizer_ptr tokenizer, bool need, char first_byte)
|
||||
{
|
||||
struct apfl_string_builder text;
|
||||
apfl_string_builder_init(&text);
|
||||
apfl_string_builder_init(tokenizer->allocator, &text);
|
||||
|
||||
enum apfl_parse_result out = maybe_name_inner(tokenizer, need, first_byte, &text);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
struct tokenizer_test {
|
||||
testctx t;
|
||||
struct apfl_allocator allocator;
|
||||
apfl_tokenizer_ptr tokenizer;
|
||||
void *ctx;
|
||||
};
|
||||
|
|
@ -13,12 +14,14 @@ struct tokenizer_test {
|
|||
static struct tokenizer_test *
|
||||
new_tokenizer_test_sv(testctx t, struct apfl_string_view text)
|
||||
{
|
||||
void *ctx = apfl_string_source_reader_new(text);
|
||||
struct apfl_allocator allocator = apfl_allocator_default();
|
||||
|
||||
void *ctx = apfl_string_source_reader_new(allocator, text);
|
||||
if (ctx == NULL) {
|
||||
test_fatalf(t, "Failed to initialize the source reader");
|
||||
}
|
||||
|
||||
apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(apfl_string_source_reader, ctx);
|
||||
apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(allocator, apfl_string_source_reader, ctx);
|
||||
if (tokenizer == NULL) {
|
||||
test_fatalf(t, "Failed to initialize the tokenizer");
|
||||
}
|
||||
|
|
@ -27,6 +30,7 @@ new_tokenizer_test_sv(testctx t, struct apfl_string_view text)
|
|||
|
||||
*tt = (struct tokenizer_test) {
|
||||
.t = t,
|
||||
.allocator = allocator,
|
||||
.tokenizer = tokenizer,
|
||||
.ctx = ctx,
|
||||
};
|
||||
|
|
@ -90,7 +94,7 @@ expect_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_type
|
|||
apfl_token_type_name(tok->type),
|
||||
apfl_token_type_name(type)
|
||||
);
|
||||
apfl_token_deinit(tok);
|
||||
apfl_token_deinit(tt->allocator, tok);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -107,7 +111,7 @@ expect_simple_token(struct tokenizer_test *tt, int line, int col, enum apfl_toke
|
|||
{
|
||||
struct apfl_token tok;
|
||||
if (expect_token(tt, line, col, type, &tok)) {
|
||||
apfl_token_deinit(&tok);
|
||||
apfl_token_deinit(tt->allocator, &tok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +124,7 @@ expect_text_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_
|
|||
struct apfl_string_view sv = apfl_string_view_from(tok.text);
|
||||
test_failf(tt->t, "Token has wrong content. have=\"" APFL_STR_FMT "\", want=\"%s\"", APFL_STR_FMT_ARGS(sv), text);
|
||||
}
|
||||
apfl_token_deinit(&tok);
|
||||
apfl_token_deinit(tt->allocator, &tok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +137,7 @@ expect_text_token_sv(struct tokenizer_test *tt, int line, int col, enum apfl_tok
|
|||
struct apfl_string_view sv = apfl_string_view_from(tok.text);
|
||||
test_failf(tt->t, "Token has wrong content. have=\"" APFL_STR_FMT "\", want=\"%s\"", APFL_STR_FMT_ARGS(sv), text);
|
||||
}
|
||||
apfl_token_deinit(&tok);
|
||||
apfl_token_deinit(tt->allocator, &tok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +149,7 @@ expect_number_token(struct tokenizer_test *tt, int line, int col, enum apfl_toke
|
|||
if (tok.number != num) {
|
||||
test_failf(tt->t, "Token has wrong content. have=%f, want=%f", tok.number, num);
|
||||
}
|
||||
apfl_token_deinit(&tok);
|
||||
apfl_token_deinit(tt->allocator, &tok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +162,7 @@ expect_error(struct tokenizer_test *tt, enum apfl_error_type want)
|
|||
case APFL_PARSE_OK:
|
||||
tok = apfl_tokenizer_get_token(tt->tokenizer);
|
||||
test_failf(tt->t, "Expected error, got token of type %s instead", apfl_token_type_name(tok.type));
|
||||
apfl_token_deinit(&tok);
|
||||
apfl_token_deinit(tt->allocator, &tok);
|
||||
return;
|
||||
case APFL_PARSE_EOF:
|
||||
test_fatalf(tt->t, "Got an EOF instead of a token");
|
||||
|
|
|
|||
185
src/value.c
185
src/value.c
|
|
@ -1,6 +1,8 @@
|
|||
#include <assert.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
#include "alloc.h"
|
||||
#include "internal.h"
|
||||
#include "resizable.h"
|
||||
#include "hashmap.h"
|
||||
|
|
@ -10,6 +12,7 @@ struct apfl_list_data {
|
|||
unsigned refcount;
|
||||
struct apfl_value *items;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct apfl_dict_data {
|
||||
|
|
@ -36,18 +39,18 @@ apfl_list_len(apfl_list list)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_list_unref(apfl_list list)
|
||||
apfl_list_unref(struct apfl_allocator allocator, apfl_list list)
|
||||
{
|
||||
if (list == NULL || !apfl_refcount_dec(&list->refcount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < list->len; i++) {
|
||||
apfl_value_deinit(&list->items[i]);
|
||||
apfl_value_deinit(allocator, &list->items[i]);
|
||||
}
|
||||
|
||||
free(list->items);
|
||||
free(list);
|
||||
FREE_LIST(allocator, list->items, list->cap);
|
||||
FREE_OBJ(allocator, list);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -67,8 +70,8 @@ dict_calc_hash(void *opaque, const void *key)
|
|||
static void
|
||||
dict_destroy_key_or_value(void *opaque, void *kv)
|
||||
{
|
||||
(void)opaque;
|
||||
apfl_value_deinit(kv);
|
||||
struct apfl_allocator *allocator = opaque;
|
||||
apfl_value_deinit(*allocator, kv);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -82,25 +85,44 @@ dict_copy_key_or_value(void *opaque, void *_dest, void *_src)
|
|||
*dest = apfl_value_incref(*src);
|
||||
}
|
||||
|
||||
static const struct apfl_hashmap_callbacks dict_hashmap_callbacks = {
|
||||
.opaque = NULL,
|
||||
.keys_eq = dict_keys_eq,
|
||||
.calc_hash = dict_calc_hash,
|
||||
.destroy_key = dict_destroy_key_or_value,
|
||||
.destroy_value = dict_destroy_key_or_value,
|
||||
.copy_key = dict_copy_key_or_value,
|
||||
.copy_value = dict_copy_key_or_value,
|
||||
};
|
||||
static void
|
||||
dict_on_deinit(void *opaque)
|
||||
{
|
||||
struct apfl_allocator *allocator = opaque;
|
||||
FREE_OBJ(*allocator, allocator);
|
||||
}
|
||||
|
||||
static bool
|
||||
init_hashmap_for_dict(struct apfl_hashmap *map)
|
||||
init_hashmap_for_dict(struct apfl_allocator allocator, struct apfl_hashmap *map)
|
||||
{
|
||||
return apfl_hashmap_init(
|
||||
struct apfl_allocator *allocator_ptr = ALLOC_OBJ(allocator, struct apfl_allocator);
|
||||
if (allocator_ptr == NULL) {
|
||||
return false;
|
||||
}
|
||||
*allocator_ptr = allocator;
|
||||
|
||||
bool ok = apfl_hashmap_init(
|
||||
map,
|
||||
dict_hashmap_callbacks,
|
||||
allocator,
|
||||
(struct apfl_hashmap_callbacks) {
|
||||
.opaque = allocator_ptr,
|
||||
.keys_eq = dict_keys_eq,
|
||||
.calc_hash = dict_calc_hash,
|
||||
.destroy_key = dict_destroy_key_or_value,
|
||||
.destroy_value = dict_destroy_key_or_value,
|
||||
.copy_key = dict_copy_key_or_value,
|
||||
.copy_value = dict_copy_key_or_value,
|
||||
.on_deinit = dict_on_deinit,
|
||||
},
|
||||
sizeof(struct apfl_value),
|
||||
sizeof(struct apfl_value)
|
||||
);
|
||||
|
||||
if (!ok) {
|
||||
FREE_OBJ(allocator, allocator_ptr);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
apfl_dict
|
||||
|
|
@ -114,14 +136,14 @@ apfl_dict_incref(apfl_dict dict)
|
|||
}
|
||||
|
||||
void
|
||||
apfl_dict_unref(apfl_dict dict)
|
||||
apfl_dict_unref(struct apfl_allocator allocator, apfl_dict dict)
|
||||
{
|
||||
if (dict == NULL || !apfl_refcount_dec(&dict->refcount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
apfl_hashmap_deinit(&dict->map);
|
||||
free(dict);
|
||||
FREE_OBJ(allocator, dict);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -166,15 +188,14 @@ print(unsigned indent, FILE *out, struct apfl_value value, bool skip_first_inden
|
|||
|
||||
apfl_print_indented(first_indent, out, "[\n");
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(&cur)) {
|
||||
struct apfl_value k, v;
|
||||
apfl_hashmap_cursor_get_key(cur, &k);
|
||||
apfl_hashmap_cursor_get_value(cur, &v);
|
||||
struct apfl_value *k;
|
||||
struct apfl_value *v;
|
||||
apfl_hashmap_cursor_peek_key(cur, (void **)&k);
|
||||
apfl_hashmap_cursor_peek_value(cur, (void **)&v);
|
||||
|
||||
print(indent+1, out, k, false);
|
||||
apfl_value_deinit(&k);
|
||||
print(indent+1, out, *k, false);
|
||||
fprintf(out, " -> ");
|
||||
print(indent+1, out, v, true);
|
||||
apfl_value_deinit(&v);
|
||||
print(indent+1, out, *v, true);
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
apfl_print_indented(indent, out, "]");
|
||||
|
|
@ -253,23 +274,18 @@ dict_eq(const apfl_dict a, const apfl_dict b)
|
|||
struct apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(&a->map);
|
||||
size_t total = 0;
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(&cur)) {
|
||||
struct apfl_value key;
|
||||
struct apfl_value val;
|
||||
struct apfl_value *key;
|
||||
struct apfl_value *val;
|
||||
|
||||
apfl_hashmap_cursor_get_key(cur, &key);
|
||||
apfl_hashmap_cursor_get_value(cur, &val);
|
||||
apfl_hashmap_cursor_peek_key(cur, (void **)&key);
|
||||
apfl_hashmap_cursor_peek_value(cur, (void **)&val);
|
||||
|
||||
struct apfl_value other_val;
|
||||
if (!apfl_hashmap_get(&b->map, &key, &other_val)) {
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(&val);
|
||||
struct apfl_value *other_val;
|
||||
if (!apfl_hashmap_peek(&b->map, &key, (void **)&other_val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool eq = apfl_value_eq(val, other_val);
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(&val);
|
||||
apfl_value_deinit(&other_val);
|
||||
bool eq = apfl_value_eq(*val, *other_val);
|
||||
|
||||
if (!eq) {
|
||||
return false;
|
||||
|
|
@ -307,19 +323,21 @@ apfl_value_eq(const struct apfl_value a, const struct apfl_value b)
|
|||
}
|
||||
|
||||
struct apfl_editable_list_data {
|
||||
struct apfl_allocator allocator;
|
||||
struct apfl_value *items;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
apfl_editable_list
|
||||
apfl_editable_list_new(void)
|
||||
apfl_editable_list_new(struct apfl_allocator allocator)
|
||||
{
|
||||
apfl_editable_list elist = ALLOC(struct apfl_editable_list_data);
|
||||
apfl_editable_list elist = ALLOC_OBJ(allocator, struct apfl_editable_list_data);
|
||||
if (elist == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
elist->allocator = allocator;
|
||||
elist->items = NULL;
|
||||
elist->len = 0;
|
||||
elist->cap = 0;
|
||||
|
|
@ -331,6 +349,7 @@ static bool
|
|||
editable_list_append_values(apfl_editable_list elist, struct apfl_value *values, size_t len)
|
||||
{
|
||||
if (!apfl_resizable_ensure_cap_for_more_elements(
|
||||
elist->allocator,
|
||||
sizeof(struct apfl_value),
|
||||
(void **)&elist->items,
|
||||
elist->len,
|
||||
|
|
@ -352,7 +371,7 @@ bool
|
|||
apfl_editable_list_append(apfl_editable_list elist, struct apfl_value value)
|
||||
{
|
||||
bool ok = editable_list_append_values(elist, &value, 1);
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(elist->allocator, &value);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +379,7 @@ bool
|
|||
apfl_editable_list_append_list(apfl_editable_list elist, apfl_list list)
|
||||
{
|
||||
bool ok = editable_list_append_values(elist, list->items, list->len);
|
||||
apfl_list_unref(list);
|
||||
apfl_list_unref(elist->allocator, list);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -373,18 +392,18 @@ apfl_editable_list_destroy(apfl_editable_list elist)
|
|||
|
||||
if (elist->items != NULL) {
|
||||
for (size_t i = 0; i < elist->len; i++) {
|
||||
apfl_value_deinit(&elist->items[i]);
|
||||
apfl_value_deinit(elist->allocator, &elist->items[i]);
|
||||
}
|
||||
free(elist->items);
|
||||
FREE_LIST(elist->allocator, elist->items, elist->cap);
|
||||
}
|
||||
|
||||
free(elist);
|
||||
FREE_OBJ(elist->allocator, elist);
|
||||
}
|
||||
|
||||
apfl_list
|
||||
apfl_editable_list_finalize(apfl_editable_list elist)
|
||||
{
|
||||
apfl_list list = ALLOC(struct apfl_list_data);
|
||||
apfl_list list = ALLOC_OBJ(elist->allocator, struct apfl_list_data);
|
||||
if (list == NULL) {
|
||||
apfl_editable_list_destroy(elist);
|
||||
return NULL;
|
||||
|
|
@ -393,26 +412,29 @@ apfl_editable_list_finalize(apfl_editable_list elist)
|
|||
list->refcount = 1;
|
||||
list->items = elist->items; // TODO: Maybe shrink memory with realloc?
|
||||
list->len = elist->len;
|
||||
list->cap = elist->cap;
|
||||
|
||||
free(elist);
|
||||
FREE_OBJ(elist->allocator, elist);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
struct apfl_editable_dict_data {
|
||||
struct apfl_allocator allocator;
|
||||
struct apfl_hashmap map;
|
||||
};
|
||||
|
||||
apfl_editable_dict
|
||||
apfl_editable_dict_new(void)
|
||||
apfl_editable_dict_new(struct apfl_allocator allocator)
|
||||
{
|
||||
apfl_editable_dict ed = ALLOC(struct apfl_editable_dict_data);
|
||||
apfl_editable_dict ed = ALLOC_OBJ(allocator, struct apfl_editable_dict_data);
|
||||
if (ed == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!init_hashmap_for_dict(&ed->map)) {
|
||||
free(ed);
|
||||
ed->allocator = allocator;
|
||||
if (!init_hashmap_for_dict(allocator, &ed->map)) {
|
||||
FREE_OBJ(allocator, ed);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -420,31 +442,32 @@ apfl_editable_dict_new(void)
|
|||
}
|
||||
|
||||
apfl_editable_dict
|
||||
apfl_editable_dict_new_from_dict(apfl_dict dict)
|
||||
apfl_editable_dict_new_from_dict(struct apfl_allocator allocator, apfl_dict dict)
|
||||
{
|
||||
if (dict->refcount == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
apfl_editable_dict ed = ALLOC(struct apfl_editable_dict_data);
|
||||
apfl_editable_dict ed = ALLOC_OBJ(allocator, struct apfl_editable_dict_data);
|
||||
if (ed == NULL) {
|
||||
apfl_dict_unref(dict);
|
||||
apfl_dict_unref(allocator, dict);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ed->allocator = allocator;
|
||||
if (dict->refcount == 1) {
|
||||
// We're the only one having a reference to the dict. We can directly use it's hashmap!
|
||||
ed->map = apfl_hashmap_move(&dict->map);
|
||||
} else {
|
||||
// There are other places referencing the dict, we need to create a shallow copy first.
|
||||
if (!apfl_hashmap_copy(&ed->map, dict->map)) {
|
||||
free(ed);
|
||||
apfl_dict_unref(dict);
|
||||
FREE_OBJ(allocator, ed);
|
||||
apfl_dict_unref(allocator, dict);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
apfl_dict_unref(dict);
|
||||
apfl_dict_unref(allocator, dict);
|
||||
|
||||
return ed;
|
||||
}
|
||||
|
|
@ -456,7 +479,7 @@ apfl_editable_dict_get(
|
|||
struct apfl_value *out
|
||||
) {
|
||||
bool ok = apfl_hashmap_get(&ed->map, &key, out);
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(ed->allocator, &key);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -468,8 +491,8 @@ apfl_editable_dict_set(apfl_editable_dict ed, struct apfl_value key, struct apfl
|
|||
}
|
||||
|
||||
bool ok = apfl_hashmap_set(&ed->map, &key, &value);
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(&value);
|
||||
apfl_value_deinit(ed->allocator, &key);
|
||||
apfl_value_deinit(ed->allocator, &value);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +504,7 @@ apfl_editable_dict_delete(apfl_editable_dict ed, struct apfl_value key)
|
|||
}
|
||||
|
||||
apfl_hashmap_delete(&ed->map, &key);
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(ed->allocator, &key);
|
||||
}
|
||||
|
||||
apfl_dict
|
||||
|
|
@ -491,7 +514,7 @@ apfl_editable_dict_finalize(apfl_editable_dict ed)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
apfl_dict dict = ALLOC(struct apfl_dict_data);
|
||||
apfl_dict dict = ALLOC_OBJ(ed->allocator, struct apfl_dict_data);
|
||||
if (dict == NULL) {
|
||||
apfl_editable_dict_destroy(ed);
|
||||
return NULL;
|
||||
|
|
@ -500,7 +523,7 @@ apfl_editable_dict_finalize(apfl_editable_dict ed)
|
|||
dict->refcount = 1;
|
||||
dict->map = apfl_hashmap_move(&ed->map);
|
||||
|
||||
free(ed);
|
||||
FREE_OBJ(ed->allocator, ed);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
|
@ -513,11 +536,11 @@ apfl_editable_dict_destroy(apfl_editable_dict ed)
|
|||
}
|
||||
|
||||
apfl_hashmap_deinit(&ed->map);
|
||||
free(ed);
|
||||
FREE_OBJ(ed->allocator, ed);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_value_deinit(struct apfl_value *value)
|
||||
apfl_value_deinit(struct apfl_allocator allocator, struct apfl_value *value)
|
||||
{
|
||||
switch (value->type) {
|
||||
case APFL_VALUE_NIL:
|
||||
|
|
@ -525,15 +548,15 @@ apfl_value_deinit(struct apfl_value *value)
|
|||
case APFL_VALUE_NUMBER:
|
||||
goto ok;
|
||||
case APFL_VALUE_STRING:
|
||||
apfl_refcounted_string_unref(value->string);
|
||||
apfl_refcounted_string_unref(allocator, value->string);
|
||||
value->string = NULL;
|
||||
goto ok;
|
||||
case APFL_VALUE_LIST:
|
||||
apfl_list_unref(value->list);
|
||||
apfl_list_unref(allocator, value->list);
|
||||
value->list = NULL;
|
||||
goto ok;
|
||||
case APFL_VALUE_DICT:
|
||||
apfl_dict_unref(value->dict);
|
||||
apfl_dict_unref(allocator, value->dict);
|
||||
value->dict = NULL;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -545,29 +568,29 @@ ok:
|
|||
}
|
||||
|
||||
bool
|
||||
apfl_list_get_item(apfl_list list, size_t index, struct apfl_value *out)
|
||||
apfl_list_get_item(struct apfl_allocator allocator, apfl_list list, size_t index, struct apfl_value *out)
|
||||
{
|
||||
if (index >= list->len) {
|
||||
apfl_list_unref(list);
|
||||
apfl_list_unref(allocator, list);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = apfl_value_incref(list->items[index]);
|
||||
apfl_list_unref(list);
|
||||
apfl_list_unref(allocator, list);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_dict_get_item(apfl_dict dict, struct apfl_value key, struct apfl_value *out)
|
||||
apfl_dict_get_item(struct apfl_allocator allocator, apfl_dict dict, struct apfl_value key, struct apfl_value *out)
|
||||
{
|
||||
bool ok = apfl_hashmap_get(&dict->map, &key, out);
|
||||
apfl_dict_unref(dict);
|
||||
apfl_value_deinit(&key);
|
||||
apfl_dict_unref(allocator, dict);
|
||||
apfl_value_deinit(allocator, &key);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static enum apfl_value_get_item_result
|
||||
get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_value key, struct apfl_value *out)
|
||||
get_item(struct apfl_allocator allocator, /*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_value key, struct apfl_value *out)
|
||||
{
|
||||
if (container.type == APFL_VALUE_LIST) {
|
||||
if (key.type != APFL_VALUE_NUMBER) {
|
||||
|
|
@ -575,6 +598,7 @@ get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_valu
|
|||
}
|
||||
|
||||
return apfl_list_get_item(
|
||||
allocator,
|
||||
apfl_list_incref(container.list),
|
||||
(size_t)key.number,
|
||||
out
|
||||
|
|
@ -583,6 +607,7 @@ get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_valu
|
|||
: APFL_VALUE_GET_ITEM_KEY_DOESNT_EXIST;
|
||||
} else if (container.type == APFL_VALUE_DICT) {
|
||||
return apfl_dict_get_item(
|
||||
allocator,
|
||||
apfl_dict_incref(container.dict),
|
||||
apfl_value_incref(key),
|
||||
out
|
||||
|
|
@ -595,11 +620,11 @@ get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_valu
|
|||
}
|
||||
|
||||
enum apfl_value_get_item_result
|
||||
apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out)
|
||||
apfl_value_get_item(struct apfl_allocator allocator, struct apfl_value container, struct apfl_value key, struct apfl_value *out)
|
||||
{
|
||||
enum apfl_value_get_item_result result = get_item(container, key, out);
|
||||
apfl_value_deinit(&container);
|
||||
apfl_value_deinit(&key);
|
||||
enum apfl_value_get_item_result result = get_item(allocator, container, key, out);
|
||||
apfl_value_deinit(allocator, &container);
|
||||
apfl_value_deinit(allocator, &key);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
18
src/value.h
18
src/value.h
|
|
@ -42,7 +42,7 @@ bool apfl_value_eq(const struct apfl_value, const struct apfl_value);
|
|||
struct apfl_value apfl_value_move(struct apfl_value *src);
|
||||
struct apfl_value apfl_value_incref(struct apfl_value);
|
||||
void apfl_value_print(struct apfl_value, FILE *);
|
||||
void apfl_value_deinit(struct apfl_value *);
|
||||
void apfl_value_deinit(struct apfl_allocator, struct apfl_value *);
|
||||
|
||||
enum apfl_value_get_item_result {
|
||||
APFL_VALUE_GET_ITEM_OK,
|
||||
|
|
@ -51,21 +51,21 @@ enum apfl_value_get_item_result {
|
|||
APFL_VALUE_GET_ITEM_WRONG_KEY_TYPE,
|
||||
};
|
||||
|
||||
enum apfl_value_get_item_result apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out);
|
||||
enum apfl_value_get_item_result apfl_value_get_item(struct apfl_allocator allocator, struct apfl_value container, struct apfl_value key, struct apfl_value *out);
|
||||
|
||||
apfl_list apfl_list_incref(apfl_list);
|
||||
size_t apfl_list_len(apfl_list);
|
||||
bool apfl_list_get_item(apfl_list, size_t index, struct apfl_value *out);
|
||||
void apfl_list_unref(apfl_list);
|
||||
bool apfl_list_get_item(struct apfl_allocator, apfl_list, size_t index, struct apfl_value *out);
|
||||
void apfl_list_unref(struct apfl_allocator, apfl_list);
|
||||
|
||||
apfl_dict apfl_dict_incref(apfl_dict);
|
||||
bool apfl_dict_get_item(apfl_dict, struct apfl_value key, struct apfl_value *out);
|
||||
void apfl_dict_unref(apfl_dict);
|
||||
bool apfl_dict_get_item(struct apfl_allocator, apfl_dict, struct apfl_value key, struct apfl_value *out);
|
||||
void apfl_dict_unref(struct apfl_allocator, apfl_dict);
|
||||
|
||||
struct apfl_editable_list_data;
|
||||
typedef struct apfl_editable_list_data *apfl_editable_list;
|
||||
|
||||
apfl_editable_list apfl_editable_list_new(void);
|
||||
apfl_editable_list apfl_editable_list_new(struct apfl_allocator);
|
||||
bool apfl_editable_list_append(apfl_editable_list, struct apfl_value);
|
||||
bool apfl_editable_list_append_list(apfl_editable_list, apfl_list);
|
||||
void apfl_editable_list_destroy(apfl_editable_list);
|
||||
|
|
@ -79,8 +79,8 @@ apfl_list apfl_editable_list_finalize(apfl_editable_list);
|
|||
struct apfl_editable_dict_data;
|
||||
typedef struct apfl_editable_dict_data *apfl_editable_dict;
|
||||
|
||||
apfl_editable_dict apfl_editable_dict_new(void);
|
||||
apfl_editable_dict apfl_editable_dict_new_from_dict(apfl_dict);
|
||||
apfl_editable_dict apfl_editable_dict_new(struct apfl_allocator);
|
||||
apfl_editable_dict apfl_editable_dict_new_from_dict(struct apfl_allocator, apfl_dict);
|
||||
bool apfl_editable_dict_get(apfl_editable_dict, struct apfl_value key, struct apfl_value *out);
|
||||
bool apfl_editable_dict_set(apfl_editable_dict, struct apfl_value key, struct apfl_value value);
|
||||
void apfl_editable_dict_delete(apfl_editable_dict, struct apfl_value key);
|
||||
|
|
|
|||
Loading…
Reference in a new issue