diff options
author | Marvin Borner | 2023-05-06 20:19:23 +0200 |
---|---|---|
committer | Marvin Borner | 2023-05-06 20:19:23 +0200 |
commit | b31220aadc24ff137a4fe4bc39780ae63c58e11b (patch) | |
tree | 89e431ad99e85f581dbea56d61abac6794386f2a | |
parent | d19f7f5bb99ca1073edb3ce1b13e782a3f598b4a (diff) |
Start fresh
-rw-r--r-- | inc/murmur3.h | 9 | ||||
-rw-r--r-- | inc/parse.h | 11 | ||||
-rw-r--r-- | inc/reduce.h | 11 | ||||
-rw-r--r-- | inc/store.h | 250 | ||||
-rw-r--r-- | inc/term.h | 37 | ||||
m--------- | lib/bdwgc | 0 | ||||
-rw-r--r-- | makefile | 2 | ||||
-rw-r--r-- | src/main.c | 111 | ||||
-rw-r--r-- | src/murmur3.c | 40 | ||||
-rw-r--r-- | src/parse.c | 75 | ||||
-rw-r--r-- | src/reduce.c | 459 | ||||
-rw-r--r-- | src/store.c | 857 | ||||
-rw-r--r-- | src/term.c | 243 | ||||
-rw-r--r-- | src/test.c | 273 |
14 files changed, 3 insertions, 2375 deletions
diff --git a/inc/murmur3.h b/inc/murmur3.h deleted file mode 100644 index c042642..0000000 --- a/inc/murmur3.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MURMUR3_H -#define MURMUR3_H - -#include <stdint.h> -#include <stdlib.h> - -uint32_t murmur3_32(const uint8_t *key, size_t len, uint32_t seed); - -#endif diff --git a/inc/parse.h b/inc/parse.h deleted file mode 100644 index c36f296..0000000 --- a/inc/parse.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#ifndef PARSE_H -#define PARSE_H - -#include <term.h> - -struct term *parse_blc(const char *term); -struct term *parse_bruijn(const char *term); - -#endif diff --git a/inc/reduce.h b/inc/reduce.h deleted file mode 100644 index 82ea189..0000000 --- a/inc/reduce.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#ifndef REDUCER_H -#define REDUCER_H - -#include <term.h> - -struct term *reduce(struct term *term, void (*callback)(int, char, void *), - void *data); - -#endif diff --git a/inc/store.h b/inc/store.h deleted file mode 100644 index 0161b62..0000000 --- a/inc/store.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Samuel Vogelsanger <vogelsangersamuel@gmail.com> - * Copyright (c) 2023 Marvin Borner <dev@marvinborner.de> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef STORE_STORE_H -#define STORE_STORE_H - -#include <stdint.h> -#include <stddef.h> - -#ifndef STORE_VERBOSITY -#define STORE_VERBOSITY 5 -#endif - -#define DEBUG_NOTICE(fmt, ...) \ - do { \ - if (STORE_VERBOSITY >= 5) \ - fprintf(stderr, "DEBUG: store: " fmt, __VA_ARGS__); \ - } while (0) -#define DEBUG_WARN(fmt, ...) \ - do { \ - if (STORE_VERBOSITY >= 4) \ - fprintf(stderr, "DEBUG: store: " fmt, __VA_ARGS__); \ - } while (0) - -#ifndef STORE_KEY_T -#define STORE_KEY_T void * -#endif - -#ifndef STORE_VALUE_T -#define STORE_VALUE_T void * -#endif - -/** - * These are mostly for convenience - */ - -#define STORE_HASHFN_T(name) uint32_t (*name)(STORE_KEY_T) -#define STORE_EQUALSFN_T(name) int (*name)(STORE_KEY_T left, STORE_KEY_T right) -#define STORE_ASSOCFN_T(name) \ - STORE_VALUE_T(*name) \ - (STORE_KEY_T key, STORE_VALUE_T old_value, void *user_data) -#define STORE_VALUE_EQUALSFN_T(name) \ - int (*name)(STORE_VALUE_T left, STORE_VALUE_T right) - -/** - * These macros help with defining the various callbacks. Use them like so: - * @code{c} - * STORE_MAKE_EQUALSFN(equals_int, left, right) - * { - * return left == right; - * } - * @endcode - */ - -#define STORE_MAKE_HASHFN(name, arg_1) uint32_t name(STORE_KEY_T arg_1) -#define STORE_MAKE_EQUALSFN(name, arg_l, arg_r) \ - int name(STORE_KEY_T arg_l, STORE_KEY_T arg_r) -#define STORE_MAKE_ASSOCFN(name, key_arg, value_arg, user_data_arg) \ - STORE_VALUE_T name(STORE_KEY_T key_arg, STORE_VALUE_T value_arg, \ - void *user_data_arg) -#define STORE_MAKE_VALUE_EQUALSFN(name, arg_l, arg_r) \ - int name(STORE_VALUE_T arg_l, STORE_VALUE_T arg_r) - -struct store { - uint32_t ref_count; - unsigned length; - struct node *root; - - STORE_HASHFN_T(hash); - STORE_EQUALSFN_T(equals); -}; - -/** - * Creates a new map with the given hash and equals functions. This implementation is based on the assumption that if - * two keys are equal, their hashes must be equal as well. This is commonly known as the Java Hashcode contract. - * - * The reference count of a new map is zero. - * - * @param hash - * @param equals - * @return - */ -struct store *store_new(STORE_HASHFN_T(hash), STORE_EQUALSFN_T(equals)); - -/** - * Destroys a store. Doesn't clean up the stored key-value-pairs. - * - * @param old - */ -void store_destroy(struct store **store); - -/** - * Atomically increases the reference count of a map. - * - * @param store - * @return - */ -struct store *store_acquire(struct store *store); - -/** - * Atomically decreases the reference count of a map and calls store_destroy if it caused the count to drop to zero. - * - * In either case then sets the reference to NULL. - * - * @param store - */ -void store_release(struct store **store); - -/** - * Returns the number of entries in store. - * - * @param store - * @return the number of entries - */ -unsigned store_length(const struct store *store); - -/** - * Looks up key and sets *value_receiver to the associated value. Doesn't change value_receiver if key is not set. - * - * @param store - * @param key - * @param found is set to 0 if key is not set - * @return - */ -STORE_VALUE_T store_get(const struct store *store, STORE_KEY_T key, int *found); - -/** - * Returns a new map derived from store but with key set to value. - * If replaced is not NULL, sets it to indicate if the key is present in store. - * - * Reference count of the new map is zero. - * - * @param store - * @param key - * @param value - * @param replaced - * @return a new store - */ -struct store *store_set(const struct store *store, STORE_KEY_T key, - STORE_VALUE_T value, int *replaced); - -/** - * Creates a new store with the given hash and equals functions, and inserts the given keys and values. - * Only the first 'length' elements from keys and values are inserted. - * - * Reference count of the new map is zero. - * - * @param hash - * @param equals - * @param keys - * @param values - * @param length - * @return - */ -struct store *store_of(STORE_HASHFN_T(hash), STORE_EQUALSFN_T(equals), - STORE_KEY_T *keys, STORE_VALUE_T *values, size_t length); - -/** - * Returns a new map derived from store, but with key set to the return value of fn. - * fn is passed the key, the current value for key, and user_data. - * If key is not present in store, NULL is passed in place of the key and current value. - * - * Reference count of the new map is zero. - * - * @param store - * @param key - * @param fn - * @param user_data - * @return - */ -struct store *store_assoc(const struct store *store, STORE_KEY_T key, - STORE_ASSOCFN_T(fn), void *user_data); - -/** - * Compares two maps for equality. A lot of short-circuiting is done on the assumption that unequal hashes - * (for both keys and values) imply inequality. This is commonly known as the Java Hashcode contract: If two values - * are equal, their hashes must be equal as well. - * - * @param left - * @param right - * @return - */ -int store_equals(const struct store *left, const struct store *right, - STORE_VALUE_EQUALSFN_T(value_equals)); - -/** - * An iterator for store. Meant to be put on the stack. - */ -struct store_iter { - int stack_level; - unsigned element_cursor; - unsigned element_arity; - unsigned branch_cursor_stack[8]; - unsigned branch_arity_stack[8]; - void *node_stack[8]; -}; - -/** - * Initializes an iterator with a store. - * - * Example: - * @code{.c} - * struct store_iter iter; - * STORE_KEY_T key; - * STORE_VAL_T val; - * - * store_iter_init(&iter, store); - * while(store_iter_next(&iter, &key, &val)) { - * // do something with key and value - * } - * @endcode - * - * @param iter - * @param store - */ -void store_iter_init(struct store_iter *iter, const struct store *store); - -/** - * Advances iter and points key_receiver and value_receiver to the next pair. - * - * @param iter - * @param key_receiver - * @param value_receiver - * @return 0 if the end of the store has been reached - */ -int store_iter_next(struct store_iter *iter, STORE_KEY_T *key_receiver, - STORE_VALUE_T *value_receiver); - -#endif diff --git a/inc/term.h b/inc/term.h deleted file mode 100644 index 4f6f5ce..0000000 --- a/inc/term.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#ifndef TERM_H -#define TERM_H - -typedef enum { INV, ABS, APP, VAR, CLOSURE, CACHE } term_type; - -struct term { - term_type type; - union { - struct { - int name; - struct term *term; - } abs; - struct { - struct term *lhs; - struct term *rhs; - } app; - struct { - int name; - enum { BARENDREGT_VARIABLE, BRUIJN_INDEX } type; - } var; - void *other; - } u; -}; - -void to_barendregt(struct term *term); -void to_bruijn(struct term *term); -struct term *new_term(term_type type); -struct term *duplicate_term(struct term *term); -int alpha_equivalency(struct term *a, struct term *b); -void free_term(struct term *term); -void print_term(struct term *term); -void print_blc(struct term *term); -void print_scheme(struct term *term); - -#endif diff --git a/lib/bdwgc b/lib/bdwgc deleted file mode 160000 -Subproject 1c2f2c679927538fa4969f85c67fafc3b1dddb2 @@ -13,7 +13,7 @@ OBJS = $(patsubst $(SRC)/%.c, $(BUILD)/%.o, $(SRCS)) CFLAGS_DEBUG = -Wno-error -g -O0 -Wno-unused -fsanitize=address,undefined,leak CFLAGS_WARNINGS = -Wall -Wextra -Wshadow -Wpointer-arith -Wwrite-strings -Wredundant-decls -Wnested-externs -Wmissing-declarations -Wstrict-prototypes -Wmissing-prototypes -Wcast-qual -Wswitch-default -Wswitch-enum -Wunreachable-code -Wundef -Wold-style-definition -pedantic -Wno-switch-enum -CFLAGS = $(CFLAGS_WARNINGS) -std=c99 -Ofast -L$(LIB)/bdwgc/lib -lgc -I$(LIB)/bdwgc/inc -I$(INC) +CFLAGS = $(CFLAGS_WARNINGS) -std=c99 -Ofast -I$(INC) ifdef TEST # TODO: Somehow clean automagically CFLAGS += -DTEST -DNTESTS=$(TEST) @@ -1,114 +1,7 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#ifndef TEST #include <stdio.h> -#include <errno.h> -#include <string.h> -#include <time.h> -#include <stdlib.h> - -#include <reduce.h> -#include <gc.h> -#include <parse.h> - -static void callback(int i, char ch, void *data) -{ - (void)i; - (void)ch; - (void)data; - /* printf("%d: %c\n", i, ch); */ -} - -#define BUF_SIZE 1024 -static char *read_stdin(void) -{ - char buffer[BUF_SIZE]; - size_t size = 1; - char *string = malloc(sizeof(char) * BUF_SIZE); - if (!string) - return 0; - string[0] = '\0'; - while (fgets(buffer, BUF_SIZE, stdin)) { - char *old = string; - size += strlen(buffer); - string = realloc(string, size); - if (!string) { - free(old); - return 0; - } - strcat(string, buffer); - } - - if (ferror(stdin)) { - free(string); - fprintf(stderr, "Couldn't read from stdin\n"); - return 0; - } - return string; -} - -static char *read_file(const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - fprintf(stderr, "Can't open file %s: %s\n", path, - strerror(errno)); - return 0; - } - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - fseek(f, 0, SEEK_SET); - - char *string = malloc(fsize + 1); - int ret = fread(string, fsize, 1, f); - fclose(f); - - if (ret != 1) { - fprintf(stderr, "Can't read file %s: %s\n", path, - strerror(errno)); - return 0; - } - - string[fsize] = 0; - return string; -} - -int main(int argc, char **argv) +int main(int argc, char *argv[]) { - GC_INIT(); - GC_enable_incremental(); - - if (argc < 2) { - fprintf(stderr, "Invalid arguments\n"); - return 1; - } - - char *input; - if (argv[1][0] == '-') { - input = read_stdin(); - } else { - input = read_file(argv[1]); - } - - if (!input) - return 1; - - struct term *parsed = parse_bruijn(input); - - clock_t begin = clock(); - struct term *reduced = reduce(parsed, callback, 0); - clock_t end = clock(); - fprintf(stderr, "reduced in %.5fs\n", - (double)(end - begin) / CLOCKS_PER_SEC); - - to_bruijn(reduced); - print_blc(reduced); - free_term(reduced); - free_term(parsed); - free(input); + printf("hello world!"); return 0; } -#else -__attribute__((unused)) static int testing; -#endif diff --git a/src/murmur3.c b/src/murmur3.c deleted file mode 100644 index bb1f666..0000000 --- a/src/murmur3.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "murmur3.h" - -#include <string.h> - -static inline uint32_t murmur_32_scramble(uint32_t k) -{ - k *= 0xcc9e2d51; - k = (k << 15) | (k >> 17); - k *= 0x1b873593; - return k; -} - -uint32_t murmur3_32(const uint8_t *key, size_t len, uint32_t seed) -{ - uint32_t h = seed; - uint32_t k; - /* Read in groups of 4. */ - for (size_t i = len >> 2; i; i--) { - memcpy(&k, key, sizeof(uint32_t)); - key += sizeof(uint32_t); - h ^= murmur_32_scramble(k); - h = (h << 13) | (h >> 19); - h = h * 5 + 0xe6546b64; - } - /* Read the rest. */ - k = 0; - for (size_t i = len & 3; i; i--) { - k <<= 8; - k |= key[i - 1]; - } - h ^= murmur_32_scramble(k); - /* Finalize. */ - h ^= len; - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - return h; -} diff --git a/src/parse.c b/src/parse.c deleted file mode 100644 index 0b9fd82..0000000 --- a/src/parse.c +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#include <stdio.h> - -#include <parse.h> -#include <term.h> - -static struct term *rec_bruijn(const char **term) -{ - struct term *res = 0; - if (!**term) { - fprintf(stderr, "invalid parsing state!\n"); - } else if (**term == '[') { - (*term)++; - res = new_term(ABS); - res->u.abs.term = rec_bruijn(term); - } else if (**term == '(') { - (*term)++; - res = new_term(APP); - res->u.app.lhs = rec_bruijn(term); - res->u.app.rhs = rec_bruijn(term); - } else if (**term >= '0' && **term <= '9') { - res = new_term(VAR); - res->u.var.name = **term - '0'; - res->u.var.type = BRUIJN_INDEX; - (*term)++; - } else { - (*term)++; - res = rec_bruijn(term); // this is quite tolerant.. - } - return res; -} - -static struct term *rec_blc(const char **term) -{ - struct term *res = 0; - if (!**term) { - fprintf(stderr, "invalid parsing state!\n"); - } else if (**term == '0' && *(*term + 1) == '0') { - (*term) += 2; - res = new_term(ABS); - res->u.abs.term = rec_blc(term); - } else if (**term == '0' && *(*term + 1) == '1') { - (*term) += 2; - res = new_term(APP); - res->u.app.lhs = rec_blc(term); - res->u.app.rhs = rec_blc(term); - } else if (**term == '1') { - const char *cur = *term; - while (**term == '1') - (*term)++; - res = new_term(VAR); - res->u.var.name = *term - cur - 1; - res->u.var.type = BRUIJN_INDEX; - (*term)++; - } else { - (*term)++; - res = rec_blc(term); // this is quite tolerant.. - } - return res; -} - -struct term *parse_bruijn(const char *term) -{ - struct term *parsed = rec_bruijn(&term); - to_barendregt(parsed); - return parsed; -} - -struct term *parse_blc(const char *term) -{ - struct term *parsed = rec_blc(&term); - to_barendregt(parsed); - return parsed; -} diff --git a/src/reduce.c b/src/reduce.c deleted file mode 100644 index 44c4d97..0000000 --- a/src/reduce.c +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> -// based on the RKNL abstract machine - -#include <stdlib.h> -#include <assert.h> -#include <stdio.h> -#include <string.h> - -#include <reduce.h> -#include <murmur3.h> -#include <store.h> -#include <term.h> -#include <gc.h> - -struct tracked { - void *stuff; -}; - -struct stack { - void *data; - struct stack *next; -}; - -struct closure { - struct term *term; - struct store *store; -}; - -struct box { - enum { TODO, DONE } state; - struct term *term; -}; - -struct cache { - struct box *box; - struct term *term; -}; - -struct conf { - enum { ECONF, CCONF } type; - union { - struct { // closure - struct term *term; - struct store *store; - struct stack *stack; - } econf; // blue - - struct { // computed - struct stack *stack; - struct term *term; - } cconf; // green - } u; -}; - -static int name_generator(void) -{ - static int current = 0x181202; - return current++; -} - -static struct stack *stack_push(struct stack *stack, void *data) -{ - struct stack *new = GC_malloc(sizeof(*new)); - new->data = data; - new->next = stack; - return new; -} - -static struct stack *stack_next(struct stack *stack) -{ - struct stack *next = stack->next; - return next; -} - -static void econf(struct conf *conf, struct term *term, struct store *store, - struct stack *stack) -{ - conf->type = ECONF; - conf->u.econf.term = term; - conf->u.econf.store = store; - conf->u.econf.stack = stack; -} - -static void cconf(struct conf *conf, struct stack *stack, struct term *term) -{ - conf->type = CCONF; - conf->u.cconf.stack = stack; - conf->u.cconf.term = term; -} - -static int transition_1(struct term **term, struct store **store, - struct stack **stack) -{ - struct closure *closure = GC_malloc(sizeof(*closure)); - closure->term = (*term)->u.app.rhs; - closure->store = *store; - - struct term *app = new_term(APP); - app->u.app.lhs = new_term(VAR); - app->u.app.rhs = new_term(CLOSURE); - app->u.app.rhs->u.other = closure; - - *term = (*term)->u.app.lhs; - *store = *store; - *stack = stack_push(*stack, app); - - return 0; -} - -static int transition_2(struct stack **stack, struct term **term, - struct store *store) -{ - struct box *box = GC_malloc(sizeof(*box)); - box->state = TODO; - box->term = 0; - - struct closure *closure = GC_malloc(sizeof(*closure)); - closure->term = *term; - closure->store = store; - - struct cache *cache = GC_malloc(sizeof(*cache)); - cache->box = box; - cache->term = new_term(CLOSURE); - cache->term->u.other = closure; - - *stack = *stack; - *term = new_term(CACHE); - (*term)->u.other = cache; - - return 0; -} - -static int transition_3(struct term **term, struct store **store, - struct stack **stack, struct box *box) -{ - assert(box->term->type == CLOSURE); - - struct cache *cache = GC_malloc(sizeof(*cache)); - cache->box = box; - cache->term = new_term(VAR); - - struct term *cache_term = new_term(CACHE); - cache_term->u.other = cache; - - struct closure *closure = box->term->u.other; - *term = closure->term; - *store = closure->store; - *stack = stack_push(*stack, cache_term); - - return 0; -} - -static int transition_4(struct stack **stack, struct term **term, - struct box *box) -{ - *stack = *stack; - *term = box->term; - - return 0; -} - -static int transition_5(struct stack **stack, struct term **term, - struct term *peek_term) -{ - struct cache *cache = peek_term->u.other; - struct box *box = cache->box; - - box->state = DONE; - box->term = *term; - - *stack = stack_next(*stack); - *term = *term; - - return 0; -} - -static int transition_6(struct term **term, struct store **store, - struct stack **stack, struct term *peek_term, - struct closure *closure) -{ - struct box *box = GC_malloc(sizeof(*box)); - box->state = TODO; - box->term = peek_term->u.app.rhs; - - *term = closure->term->u.abs.term; - *store = store_set(closure->store, &closure->term->u.abs.name, box, 0); - *stack = stack_next(*stack); - - return 0; -} - -static int transition_7(struct term **term, struct store **store, - struct stack **stack, struct box *box, - struct closure *closure) -{ - int x = name_generator(); - - struct box *var_box = GC_malloc(sizeof(*var_box)); - var_box->state = DONE; - var_box->term = new_term(VAR); - var_box->term->u.var.name = x; - - struct cache *cache = GC_malloc(sizeof(*cache)); - cache->box = box; - cache->term = new_term(VAR); - - struct term *cache_term = new_term(CACHE); - cache_term->u.other = cache; - - struct term *abs = new_term(ABS); - abs->u.abs.name = x; - abs->u.abs.term = new_term(VAR); - - *term = closure->term->u.abs.term; - *store = store_set(closure->store, (void *)&closure->term->u.abs.name, - var_box, 0); - *stack = stack_push(*stack, cache_term); - *stack = stack_push(*stack, abs); - - return 0; -} - -static int transition_8(struct stack **stack, struct term **term, - struct box *box) -{ - *stack = *stack; - *term = box->term; - - return 0; -} - -static int transition_9(struct term **term, struct store **store, - struct stack **stack, struct term *peek_term) -{ - struct closure *closure = peek_term->u.app.rhs->u.other; - - struct term *app = new_term(APP); - app->u.app.lhs = *term; - app->u.app.rhs = new_term(VAR); - - *term = closure->term; - *store = closure->store; - *stack = stack_push(stack_next(*stack), app); - - return 0; -} - -static int transition_10(struct stack **stack, struct term **term, - struct term *peek_term) -{ - struct term *app = new_term(APP); - app->u.app.lhs = peek_term->u.app.lhs; - app->u.app.rhs = *term; - - *stack = stack_next(*stack); - *term = app; - - return 0; -} - -static int transition_11(struct stack **stack, struct term **term, - struct term *peek_term) -{ - struct term *abs = new_term(ABS); - abs->u.abs.name = peek_term->u.abs.name; - abs->u.abs.term = *term; - - *stack = stack_next(*stack); - *term = abs; - - return 0; -} - -static int transition_closure(struct conf *conf, int i, - void (*callback)(int, char, void *), void *data) -{ - struct term *term = conf->u.econf.term; - struct store *store = conf->u.econf.store; - struct stack *stack = conf->u.econf.stack; - - int ret = 1; - switch (term->type) { - case APP: // (1) - callback(i, '1', data); - ret = transition_1(&term, &store, &stack); - econf(conf, term, store, stack); - return ret; - case ABS: // (2) - callback(i, '2', data); - ret = transition_2(&stack, &term, store); - cconf(conf, stack, term); - return ret; - case VAR:; - struct box *box = store_get(store, &term->u.var.name, 0); - if (!box) { - box = GC_malloc(sizeof(*box)); - box->state = DONE; - box->term = term; - } - if (box->state == TODO) { // (3) - callback(i, '3', data); - ret = transition_3(&term, &store, &stack, box); - econf(conf, term, store, stack); - return ret; - } else if (box->state == DONE) { // (4) - callback(i, '4', data); - ret = transition_4(&stack, &term, box); - cconf(conf, stack, term); - return ret; - } - fprintf(stderr, "Invalid box state %d\n", box->state); - return 1; - default: - fprintf(stderr, "Invalid econf type %d\n", term->type); - return 1; - } -} - -static int transition_computed(struct conf *conf, int i, - void (*callback)(int, char, void *), void *data) -{ - struct stack *stack = conf->u.cconf.stack; - struct term *term = conf->u.cconf.term; - if (!stack) { - fprintf(stderr, "Invalid stack!\n"); - return 1; - } - int ret = 1; - struct term *peek_term = stack->data; - if (peek_term && peek_term->type == CACHE) { // (5) - struct cache *cache = peek_term->u.other; - struct term *cache_term = cache->term; - if (cache_term->type == VAR && !cache_term->u.var.name) { - callback(i, '5', data); - ret = transition_5(&stack, &term, peek_term); - cconf(conf, stack, term); - return ret; - } - } - if (peek_term && peek_term->type == APP && - peek_term->u.app.lhs->type == VAR && - !peek_term->u.app.lhs->u.var.name && term->type == CACHE && - ((struct cache *)term->u.other)->term->type == CLOSURE) { // (6) - struct closure *closure = - ((struct cache *)term->u.other)->term->u.other; - if (closure->term->type == ABS) { - callback(i, '6', data); - struct store *store; - ret = transition_6(&term, &store, &stack, peek_term, - closure); - econf(conf, term, store, stack); - return ret; - } - } - if (term->type == CACHE && - ((struct cache *)term->u.other)->term->type == CLOSURE) { - struct box *box = ((struct cache *)term->u.other)->box; - struct closure *closure = - ((struct cache *)term->u.other)->term->u.other; - if (closure->term->type == ABS && box->state == TODO && - !box->term) { // (7) - callback(i, '7', data); - struct store *store; - ret = transition_7(&term, &store, &stack, box, closure); - econf(conf, term, store, stack); - return ret; - } - if (closure->term->type == ABS && box->state == DONE) { // (8) - callback(i, '8', data); - ret = transition_8(&stack, &term, box); - cconf(conf, stack, term); - return ret; - } - } - if (peek_term && peek_term->type == APP && - peek_term->u.app.lhs->type == VAR && - !peek_term->u.app.lhs->u.var.name && - peek_term->u.app.rhs->type == CLOSURE) { // (9) - callback(i, '9', data); - struct store *store; - ret = transition_9(&term, &store, &stack, peek_term); - econf(conf, term, store, stack); - return ret; - } - if (peek_term && peek_term->type == APP && - peek_term->u.app.rhs->type == VAR && - !peek_term->u.app.rhs->u.var.name) { // (10) - callback(i, 'A', data); - ret = transition_10(&stack, &term, peek_term); - cconf(conf, stack, term); - return ret; - } - if (peek_term && peek_term->type == ABS && - peek_term->u.abs.term->type == VAR && - !peek_term->u.abs.term->u.var.name) { // (11) - callback(i, 'B', data); - ret = transition_11(&stack, &term, peek_term); - cconf(conf, stack, term); - return ret; - } - if (!peek_term) - return 1; - - // If implemented *correctly* it's proven that this can't happen - fprintf(stderr, "Invalid cconf transition state\n"); - return 1; -} - -static int transition(struct conf *conf, int i, - void (*callback)(int, char, void *), void *data) -{ - if (conf->type == ECONF) { - return transition_closure(conf, i, callback, data); - } else if (conf->type == CCONF) { - return transition_computed(conf, i, callback, data); - } - fprintf(stderr, "Invalid transition state %x\n", conf->type); - return 1; -} - -static struct conf *for_each_state(struct conf *conf, int i, - void (*callback)(int, char, void *), - void *data) -{ - int ret = 0; - while (!ret) - ret = transition(conf, i++, callback, data); - return conf; -} - -static int hash_var_equal(void *lhs, void *rhs) -{ - /* return memcmp(lhs, rhs, sizeof(int)); */ - return *(int *)lhs == *(int *)rhs; -} - -static uint32_t hash_var(void *key) -{ - return murmur3_32((uint8_t *)key, sizeof(int), 0); -} - -struct term *reduce(struct term *term, void (*callback)(int, char, void *), - void *data) -{ - struct stack stack = { 0 }; - struct store *store = store_new(hash_var, hash_var_equal); - struct conf conf = { - .type = ECONF, - .u.econf.term = term, - .u.econf.store = store, - .u.econf.stack = &stack, - }; - for_each_state(&conf, 0, callback, data); - assert(conf.type == CCONF); - - struct term *ret = duplicate_term(conf.u.cconf.term); - - return ret; -} diff --git a/src/store.c b/src/store.c deleted file mode 100644 index db3ccef..0000000 --- a/src/store.c +++ /dev/null @@ -1,857 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Samuel Vogelsanger <vogelsangersamuel@gmail.com> - * Copyright (c) 2023 Marvin Borner <dev@marvinborner.de> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * All the ref-counting specific code was marked with a "//reference counting" comment. If you need to modify this to - * work with your own memory policy, it is recommended to start looking at those places to understand when and where - * memory is allocated and freed. - */ - -#include <malloc.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> - -#include <store.h> -#include <gc.h> - -#define store_node_debug_fmt \ - "node{element_arity=%u, element_map=%08x, branch_arity=%u, branch_map=%08x, ref_count=%u}" -#define store_node_debug_args(node) \ - node->element_arity, node->element_map, node->branch_arity, \ - node->branch_map, node->ref_count - -#define HASH_PARTITION_WIDTH 5u -#define HASH_TOTAL_WIDTH (8 * sizeof(uint32_t)) - -/* - * Helper functions - */ - -static unsigned bitcount(uint32_t value) -{ - // taken from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - value = value - - ((value >> 1u) & 0x55555555u); // reuse input as temporary - value = (value & 0x33333333u) + ((value >> 2u) & 0x33333333u); // temp - return (((value + (value >> 4u)) & 0xF0F0F0Fu) * 0x1010101u) >> - 24u; // count -} - -static uint32_t store_mask(uint32_t hash, unsigned shift) -{ - return (hash >> shift) & ((1u << HASH_PARTITION_WIDTH) - 1); -} - -static unsigned store_index(uint32_t bitmap, uint32_t bitpos) -{ - return bitcount(bitmap & (bitpos - 1)); -} - -/* - * Data structure definitions - */ - -struct kv { - STORE_KEY_T key; - STORE_VALUE_T val; -}; - -#define STORE_NODE_ELEMENT_T struct kv -#define STORE_NODE_BRANCH_T struct node * - -struct node { - uint8_t element_arity; - uint8_t branch_arity; - uint16_t ref_count; // reference counting - uint32_t element_map; - uint32_t branch_map; - STORE_NODE_ELEMENT_T content[]; -}; - -struct collision_node { - uint8_t element_arity; // MUST SHARE LAYOUT WITH struct node - uint8_t branch_arity; // MUST SHARE LAYOUT WITH struct node - uint16_t ref_count; // MUST SHARE LAYOUT WITH struct node // reference counting - STORE_NODE_ELEMENT_T content[]; -}; - -static struct node empty_node = { - .branch_arity = 0, - .element_arity = 0, - .ref_count = 1, - .branch_map = 0, - .element_map = 0, -}; - -#define STORE_NODE_ELEMENTS(node) (node)->content -#define STORE_NODE_BRANCHES(node) \ - ((STORE_NODE_BRANCH_T *)&(node)->content[(node)->element_arity]) - -#define STORE_NODE_ELEMENTS_SIZE(length) \ - (sizeof(STORE_NODE_ELEMENT_T) * (length)) -#define STORE_NODE_BRANCHES_SIZE(length) \ - (sizeof(STORE_NODE_BRANCH_T) * (length)) - -#define STORE_NODE_ELEMENT_AT(node, bitpos) \ - STORE_NODE_ELEMENTS(node)[store_index(node->element_map, bitpos)] -#define STORE_NODE_BRANCH_AT(node, bitpos) \ - STORE_NODE_BRANCHES(node)[store_index(node->branch_map, bitpos)] - -/* - * static function declarations - */ - -// node constructor -static struct node *node_new(uint32_t element_map, uint32_t branch_map, - STORE_NODE_ELEMENT_T const *elements, - uint8_t element_arity, - STORE_NODE_BRANCH_T const *branches, - uint8_t branch_arity); - -// collision node variant -static struct collision_node * -collision_node_new(const STORE_NODE_ELEMENT_T *values, uint8_t element_arity); - -// destructor -static void node_destroy(struct node *node); - -// reference counting -static struct node *store_node_acquire(struct node *node); - -// reference counting -static void store_node_release(struct node *node); - -// top-level functions -static STORE_VALUE_T node_get(struct node *node, STORE_EQUALSFN_T(equals), - STORE_KEY_T key, uint32_t hash, unsigned shift, - int *found); - -static struct node *node_update(struct node *node, STORE_HASHFN_T(hashfn), - STORE_EQUALSFN_T(equals), STORE_KEY_T key, - STORE_VALUE_T value, uint32_t hash, - unsigned shift, int *found); - -static struct node *node_assoc(struct node *node, STORE_HASHFN_T(hashfn), - STORE_EQUALSFN_T(equals), STORE_KEY_T key, - STORE_ASSOCFN_T(fn), void *user_data, - uint32_t hash, unsigned shift, int *found); - -// collision node variants -static STORE_VALUE_T collision_node_get(struct collision_node *node, - STORE_EQUALSFN_T(equals), - STORE_KEY_T key, int *found); - -static struct collision_node * -collision_node_update(struct collision_node *node, STORE_EQUALSFN_T(equals), - STORE_KEY_T key, STORE_VALUE_T value, int *found); - -static struct collision_node *collision_node_assoc(struct collision_node *node, - STORE_EQUALSFN_T(equals), - STORE_KEY_T key, - STORE_ASSOCFN_T(fn), - void *user_data, int *found); - -// helper functions for creation of modified nodes -static struct node *node_merge(uint32_t hash_l, STORE_KEY_T key_l, - STORE_VALUE_T value_l, uint32_t hash_r, - STORE_KEY_T key_r, STORE_VALUE_T value_r, - unsigned shift); - -static struct node *node_clone_update_branch(struct node *node, uint32_t bitpos, - struct node *branch); - -static struct node *node_clone_pushdown(struct node *node, uint32_t bitpos, - struct node *branch); - -static struct node *node_clone_insert_element(struct node *node, - uint32_t bitpos, STORE_KEY_T key, - STORE_VALUE_T value); - -static struct node *node_clone_update_element(struct node *node, - uint32_t bitpos, - STORE_VALUE_T value); - -// collision node variants -static struct collision_node * -collision_node_clone_insert_element(struct collision_node *node, - STORE_KEY_T key, STORE_VALUE_T value); - -static struct collision_node * -collision_node_clone_update_element(struct collision_node *node, unsigned index, - STORE_VALUE_T value); - -// equality -static int node_equals(struct node *left, struct node *right, - STORE_EQUALSFN_T(key_equals), - STORE_VALUE_EQUALSFN_T(value_equals), unsigned shift); - -static int collision_node_equals(struct collision_node *left, - struct collision_node *right, - STORE_EQUALSFN_T(key_equals), - STORE_VALUE_EQUALSFN_T(value_equals)); - -// store private constructor -static struct store *store_from(struct node *root, unsigned length, - STORE_HASHFN_T(hash), STORE_EQUALSFN_T(equals)); - -// iterator helper functions -static void iter_push(struct store_iter *iterator, struct node *node); - -static void iter_pop(struct store_iter *iterator); - -/* - * definitions - */ - -static void node_destroy(struct node *node) -{ - DEBUG_NOTICE(" destroying " store_node_debug_fmt "@%p\n", - store_node_debug_args(node), (void *)node); - - GC_free(node); -} - -// reference counting -static struct node *store_node_acquire(struct node *node) -{ - if (node == &empty_node) - return node; - node->ref_count++; - return node; -} - -// reference counting -static void store_node_release(struct node *node) -{ - if (node == &empty_node) - return; - - if (node->ref_count-- == 1) - node_destroy(node); -} - -/** - * WARNING: all branches in <code>branches</code> are "acquired", i.e. their reference count is incremented. - * Do not pass an "almost correct" list of branches. - */ -static struct node *node_new(uint32_t element_map, uint32_t branch_map, - STORE_NODE_ELEMENT_T const *elements, - uint8_t element_arity, - STORE_NODE_BRANCH_T const *branches, - uint8_t branch_arity) -{ - const size_t content_size = STORE_NODE_ELEMENTS_SIZE(element_arity) + - STORE_NODE_BRANCHES_SIZE(branch_arity); - struct node *result = GC_malloc(sizeof(*result) + content_size); - - result->element_arity = element_arity; - result->branch_arity = branch_arity; - result->ref_count = 0; - result->element_map = element_map; - result->branch_map = branch_map; - - if (elements) { - memcpy(STORE_NODE_ELEMENTS(result), elements, - STORE_NODE_ELEMENTS_SIZE(element_arity)); - } - - STORE_NODE_BRANCH_T *branches_dest = - (STORE_NODE_BRANCH_T *)STORE_NODE_BRANCHES(result); - // reference counting - for (int i = 0; i < branch_arity; ++i) { - branches_dest[i] = store_node_acquire(branches[i]); - } - - return result; -} - -static STORE_VALUE_T collision_node_get(struct collision_node *node, - STORE_EQUALSFN_T(equals), - STORE_KEY_T key, int *found) -{ - for (unsigned i = 0; i < node->element_arity; ++i) { - struct kv kv = node->content[i]; - if (equals(kv.key, key)) { - *found = 1; - return kv.val; - } - } - - *found = 0; - return (STORE_VALUE_T)0; -} - -static STORE_VALUE_T node_get(struct node *node, STORE_EQUALSFN_T(equals), - STORE_KEY_T key, uint32_t hash, unsigned shift, - int *found) -{ - if (shift >= HASH_TOTAL_WIDTH) - return collision_node_get((struct collision_node *)node, equals, - key, found); - - const uint32_t bitpos = 1u << store_mask(hash, shift); - - if (node->branch_map & bitpos) { - return node_get(STORE_NODE_BRANCH_AT(node, bitpos), equals, key, - hash, shift + HASH_PARTITION_WIDTH, found); - - } else if (node->element_map & bitpos) { - STORE_NODE_ELEMENT_T kv = STORE_NODE_ELEMENT_AT(node, bitpos); - if (equals(kv.key, key)) { - *found = 1; - return kv.val; - } - } - - *found = 0; - return (STORE_VALUE_T)0; -} - -static struct node *node_clone_insert_element(struct node *node, - uint32_t bitpos, STORE_KEY_T key, - STORE_VALUE_T value) -{ - STORE_NODE_ELEMENT_T elements[1u << HASH_PARTITION_WIDTH]; - const unsigned index = store_index(node->element_map, bitpos); - - // copy <element_arity> chunks in total - memcpy(elements, STORE_NODE_ELEMENTS(node), - STORE_NODE_ELEMENTS_SIZE(index)); // copy first <index> chunks - elements[index].key = (STORE_KEY_T)key; - elements[index].val = (STORE_VALUE_T)value; - memcpy(&elements[index + 1], // start copying into one-past-<index> - &STORE_NODE_ELEMENTS(node)[index], // start copying from <index> - STORE_NODE_ELEMENTS_SIZE( - node->element_arity - - index) // <index> chunks already copied, <element_arity> - <index> remaining - ); - - return node_new(node->element_map | bitpos, node->branch_map, elements, - node->element_arity + 1, STORE_NODE_BRANCHES(node), - node->branch_arity); -} - -static struct node *node_clone_update_element(struct node *node, - uint32_t bitpos, - STORE_VALUE_T value) -{ - STORE_NODE_ELEMENT_T elements[1u << HASH_PARTITION_WIDTH]; - const unsigned index = store_index(node->element_map, bitpos); - - memcpy(elements, STORE_NODE_ELEMENTS(node), - STORE_NODE_ELEMENTS_SIZE(node->element_arity)); - elements[index].val = (STORE_VALUE_T)value; - return node_new(node->element_map, node->branch_map, elements, - node->element_arity, STORE_NODE_BRANCHES(node), - node->branch_arity); -} - -static struct node *node_clone_update_branch(struct node *node, uint32_t bitpos, - struct node *branch) -{ - STORE_NODE_BRANCH_T branches[1u << HASH_PARTITION_WIDTH]; - const unsigned index = store_index(node->branch_map, bitpos); - - memcpy(branches, STORE_NODE_BRANCHES(node), - STORE_NODE_BRANCHES_SIZE(node->branch_arity)); - branches[index] = branch; - return node_new(node->element_map, node->branch_map, - STORE_NODE_ELEMENTS(node), node->element_arity, - branches, node->branch_arity); -} - -static struct node *node_clone_pushdown(struct node *node, uint32_t bitpos, - struct node *branch) -{ - STORE_NODE_ELEMENT_T elements[1u << HASH_PARTITION_WIDTH]; - STORE_NODE_BRANCH_T branches[1u << HASH_PARTITION_WIDTH]; - const unsigned element_index = store_index(node->element_map, bitpos); - const unsigned branch_index = store_index(node->branch_map, bitpos); - - memcpy(elements, STORE_NODE_ELEMENTS(node), - STORE_NODE_ELEMENTS_SIZE(element_index)); - memcpy(&elements[element_index], - &STORE_NODE_ELEMENTS(node)[element_index + 1], - STORE_NODE_ELEMENTS_SIZE(node->element_arity - - (element_index + 1))); - - memcpy(branches, STORE_NODE_BRANCHES(node), - STORE_NODE_BRANCHES_SIZE(branch_index)); - memcpy(&branches[branch_index + 1], - &STORE_NODE_BRANCHES(node)[branch_index], - STORE_NODE_BRANCHES_SIZE(node->branch_arity - branch_index)); - branches[branch_index] = branch; - - return node_new(node->element_map & ~bitpos, node->branch_map | bitpos, - elements, node->element_arity - 1, branches, - node->branch_arity + 1); -} - -static struct collision_node * -collision_node_new(const STORE_NODE_ELEMENT_T *values, uint8_t element_arity) -{ - size_t content_size = sizeof(STORE_NODE_ELEMENT_T) * element_arity; - struct collision_node *result = - GC_malloc(sizeof(*result) + content_size); - - result->element_arity = element_arity; - result->branch_arity = 0; - result->ref_count = 0; - - memcpy(result->content, values, - STORE_NODE_ELEMENTS_SIZE(element_arity)); - - return result; -} - -static struct node *node_merge(uint32_t hash_l, STORE_KEY_T key_l, - STORE_VALUE_T value_l, uint32_t hash_r, - STORE_KEY_T key_r, STORE_VALUE_T value_r, - unsigned shift) -{ - uint32_t bitpos_l = 1u << store_mask(hash_l, shift); - uint32_t bitpos_r = 1u << store_mask(hash_r, shift); - - if (shift >= HASH_TOTAL_WIDTH) { - STORE_NODE_ELEMENT_T elements[2]; - elements[0].key = (STORE_KEY_T)key_l; - elements[0].val = (STORE_VALUE_T)value_l; - elements[1].key = (STORE_KEY_T)key_r; - elements[1].val = (STORE_VALUE_T)value_r; - - return (struct node *)collision_node_new(elements, 2); - - } else if (bitpos_l != bitpos_r) { - STORE_NODE_ELEMENT_T elements[2]; - - if (bitpos_l <= bitpos_r) { - elements[0].key = (STORE_KEY_T)key_l; - elements[0].val = (STORE_VALUE_T)value_l; - elements[1].key = (STORE_KEY_T)key_r; - elements[1].val = (STORE_VALUE_T)value_r; - } else { - elements[0].key = (STORE_KEY_T)key_r; - elements[0].val = (STORE_VALUE_T)value_r; - elements[1].key = (STORE_KEY_T)key_l; - elements[1].val = (STORE_VALUE_T)value_l; - } - - return node_new(bitpos_l | bitpos_r, 0u, elements, 2, NULL, 0); - - } else { - struct node *sub_node = - node_merge(hash_l, key_l, value_l, hash_r, key_r, - value_r, shift + HASH_PARTITION_WIDTH); - - return node_new(0, bitpos_l, NULL, 0, &sub_node, 1); - } -} - -static struct collision_node * -collision_node_clone_update_element(struct collision_node *node, unsigned index, - STORE_VALUE_T value) -{ - STORE_NODE_ELEMENT_T elements[node->element_arity]; - - memcpy(elements, node->content, - STORE_NODE_ELEMENTS_SIZE(node->element_arity)); - elements[index].val = (STORE_VALUE_T)value; - - return collision_node_new(elements, node->element_arity); -} - -static struct collision_node * -collision_node_clone_insert_element(struct collision_node *node, - STORE_KEY_T key, STORE_VALUE_T value) -{ - STORE_NODE_ELEMENT_T elements[node->element_arity + 1]; - - memcpy(elements, node->content, - STORE_NODE_ELEMENTS_SIZE(node->element_arity)); - elements[node->element_arity].key = (STORE_KEY_T)key; - elements[node->element_arity].val = (STORE_VALUE_T)value; - - return collision_node_new(elements, node->element_arity + 1); -} - -static struct collision_node * -collision_node_update(struct collision_node *node, STORE_EQUALSFN_T(equals), - STORE_KEY_T key, STORE_VALUE_T value, int *found) -{ - for (unsigned i = 0; i < node->element_arity; ++i) { - struct kv kv = node->content[i]; - if (equals(kv.key, key)) { - *found = 1; - - return collision_node_clone_update_element(node, i, - value); - } - } - - return collision_node_clone_insert_element(node, key, value); -} - -static struct node *node_update(struct node *node, STORE_HASHFN_T(hashfn), - STORE_EQUALSFN_T(equals), STORE_KEY_T key, - STORE_VALUE_T value, uint32_t hash, - unsigned shift, int *found) -{ - if (shift >= HASH_TOTAL_WIDTH) - return (struct node *)collision_node_update( - (struct collision_node *)node, equals, key, value, - found); - - const uint32_t bitpos = 1u << store_mask(hash, shift); - - if (node->branch_map & bitpos) { - struct node *sub_node = STORE_NODE_BRANCH_AT(node, bitpos); - struct node *new_sub_node = - node_update(sub_node, hashfn, equals, key, value, hash, - shift + HASH_PARTITION_WIDTH, found); - return node_clone_update_branch(node, bitpos, new_sub_node); - - } else if (node->element_map & bitpos) { - STORE_KEY_T current_key = - STORE_NODE_ELEMENT_AT(node, bitpos).key; - - if (equals(current_key, key)) { - *found = 1; - return node_clone_update_element(node, bitpos, value); - - } else { - STORE_VALUE_T current_value = - STORE_NODE_ELEMENT_AT(node, bitpos).val; - struct node *sub_node = - node_merge(hashfn(current_key), current_key, - current_value, hashfn(key), key, - value, shift + HASH_PARTITION_WIDTH); - return node_clone_pushdown(node, bitpos, sub_node); - } - - } else { - return node_clone_insert_element(node, bitpos, key, value); - } -} - -static struct collision_node *collision_node_assoc(struct collision_node *node, - STORE_EQUALSFN_T(equals), - STORE_KEY_T key, - STORE_ASSOCFN_T(fn), - void *user_data, int *found) -{ - STORE_VALUE_T new_value; - for (unsigned i = 0; i < node->element_arity; ++i) { - struct kv kv = node->content[i]; - if (equals(kv.key, key)) { - *found = 1; - STORE_VALUE_T old_value = kv.val; - new_value = fn(key, old_value, user_data); - return collision_node_clone_update_element(node, i, - new_value); - } - } - - new_value = fn((STORE_KEY_T)0, (STORE_VALUE_T)0, user_data); - return collision_node_clone_insert_element(node, key, new_value); -} - -static struct node *node_assoc(struct node *node, STORE_HASHFN_T(hashfn), - STORE_EQUALSFN_T(equals), STORE_KEY_T key, - STORE_ASSOCFN_T(fn), void *user_data, - uint32_t hash, unsigned shift, int *found) -{ - if (shift >= HASH_TOTAL_WIDTH) - return (struct node *)collision_node_assoc( - (struct collision_node *)node, equals, key, fn, - user_data, found); - - const uint32_t bitpos = 1u << store_mask(hash, shift); - - if (node->branch_map & bitpos) { - struct node *sub_node = STORE_NODE_BRANCH_AT(node, bitpos); - struct node *new_sub_node = - node_assoc(sub_node, hashfn, equals, key, fn, user_data, - hash, shift + HASH_PARTITION_WIDTH, found); - return node_clone_update_branch(node, bitpos, new_sub_node); - - } else if (node->element_map & bitpos) { - STORE_KEY_T current_key = - STORE_NODE_ELEMENT_AT(node, bitpos).key; - - if (equals(current_key, key)) { - *found = 1; - STORE_VALUE_T old_value = - STORE_NODE_ELEMENT_AT(node, bitpos).val; - STORE_VALUE_T new_value = fn(key, old_value, user_data); - return node_clone_update_element(node, bitpos, - new_value); - - } else { - STORE_VALUE_T current_value = - STORE_NODE_ELEMENT_AT(node, bitpos).val; - STORE_VALUE_T new_value = - fn((STORE_KEY_T)0, (STORE_VALUE_T)0, user_data); - struct node *sub_node = - node_merge(hashfn(current_key), current_key, - current_value, hash, key, new_value, - shift + HASH_PARTITION_WIDTH); - return node_clone_pushdown(node, bitpos, sub_node); - } - - } else { - STORE_VALUE_T value = - fn((STORE_KEY_T)0, (STORE_VALUE_T)0, user_data); - return node_clone_insert_element(node, bitpos, key, value); - } -} - -static int collision_node_equals(struct collision_node *left, - struct collision_node *right, - STORE_EQUALSFN_T(key_equals), - STORE_VALUE_EQUALSFN_T(value_equals)) -{ - if (left == right) - return 1; - if (left->element_arity != right->element_arity) - return 0; - - for (unsigned left_i = 0; left_i < left->element_arity; ++left_i) { - struct kv left_element = STORE_NODE_ELEMENTS(left)[left_i]; - - for (unsigned right_i = 0; right_i < right->element_arity; - ++right_i) { - struct kv right_element = - STORE_NODE_ELEMENTS(right)[right_i]; - - if (key_equals(left_element.key, right_element.key) && - value_equals(left_element.val, right_element.val)) - goto found_matching_element; - } - return 0; // compared left_element to all elements in right node, no match. - - found_matching_element: - continue; - } - return 1; // compared all elements in left node, never had an element without match. -} - -static int node_equals(struct node *left, struct node *right, - STORE_EQUALSFN_T(key_equals), - STORE_VALUE_EQUALSFN_T(value_equals), unsigned shift) -{ - if (shift >= HASH_TOTAL_WIDTH) - return collision_node_equals((struct collision_node *)left, - (struct collision_node *)right, - key_equals, value_equals); - if (left == right) - return 1; - if (left->element_map != right->element_map) - return 0; - if (left->branch_map != right->branch_map) - return 0; - for (unsigned i = 0; i < left->element_arity; ++i) { - struct kv left_element = STORE_NODE_ELEMENTS(left)[i]; - struct kv right_element = STORE_NODE_ELEMENTS(right)[i]; - if (!key_equals(left_element.key, right_element.key) || - !value_equals(left_element.val, right_element.val)) - return 0; - } - for (unsigned i = 0; i < left->branch_arity; ++i) { - struct node *left_branch = STORE_NODE_BRANCHES(left)[i]; - struct node *right_branch = STORE_NODE_BRANCHES(right)[i]; - if (!node_equals(left_branch, right_branch, key_equals, - value_equals, shift + HASH_PARTITION_WIDTH)) - return 0; - } - return 1; -} - -static struct store *store_from(struct node *root, unsigned length, - STORE_HASHFN_T(hash), STORE_EQUALSFN_T(equals)) -{ - struct store *result = GC_malloc(sizeof(*result)); - result->ref_count = 0; - result->root = root; - result->length = length; - result->hash = hash; - result->equals = equals; - return result; -} - -void store_destroy(struct store **store) -{ - DEBUG_NOTICE("destroying store@%p\n", (void *)*store); - - store_node_release((*store)->root); - GC_free(*store); - *store = NULL; -} - -struct store *store_new(STORE_HASHFN_T(hash), STORE_EQUALSFN_T(equals)) -{ - return store_from((struct node *)&empty_node, 0, hash, equals); -} - -struct store *store_acquire(struct store *store) -{ - store->ref_count++; - DEBUG_NOTICE("ACQ %p: %d\n", (void *)store, store->ref_count); - return store; -} - -void store_release(struct store **store) -{ - DEBUG_NOTICE("REL %p: %d\n", (void *)*store, (*store)->ref_count - 1); - if ((*store)->ref_count-- == 1) - store_destroy(store); -} - -struct store *store_of(STORE_HASHFN_T(hash), STORE_EQUALSFN_T(equals), - STORE_KEY_T *keys, STORE_VALUE_T *values, size_t length) -{ - struct store *result = store_new(hash, equals); - while (length--) { - struct store *tmp = - store_set(result, keys[length], values[length], NULL); - store_destroy(&result); - result = tmp; - } - return result; -} - -unsigned store_length(const struct store *store) -{ - return store->length; -} - -struct store *store_set(const struct store *store, STORE_KEY_T key, - STORE_VALUE_T value, int *replaced) -{ - const uint32_t hash = store->hash(key); - int found = 0; - int *found_p = replaced ? replaced : &found; - *found_p = 0; - struct node *new_root = store_node_acquire( - node_update(store->root, store->hash, store->equals, key, value, - hash, 0, found_p)); - return store_from(new_root, store->length + (*found_p ? 0 : 1), - store->hash, store->equals); -} - -STORE_VALUE_T store_get(const struct store *store, STORE_KEY_T key, int *found) -{ - uint32_t hash = store->hash(key); - int tmp = 0; - return node_get(store->root, store->equals, key, hash, 0, - found ? found : &tmp); -} - -struct store *store_assoc(const struct store *store, STORE_KEY_T key, - STORE_ASSOCFN_T(fn), void *user_data) -{ - const uint32_t hash = store->hash(key); - int found = 0; - struct node *new_root = store_node_acquire( - node_assoc(store->root, store->hash, store->equals, key, fn, - user_data, hash, 0, &found)); - return store_from(new_root, store->length + (found ? 0 : 1), - store->hash, store->equals); -} - -int store_equals(const struct store *left, const struct store *right, - STORE_VALUE_EQUALSFN_T(value_equals)) -{ - if (left == right) - return 1; - else if (store_length(left) != store_length(right)) - return 0; - else - return node_equals(left->root, right->root, left->equals, - value_equals, 0); -} - -void store_iter_init(struct store_iter *iterator, const struct store *store) -{ - iterator->stack_level = 0; - iterator->element_cursor = 0; - iterator->element_arity = store->root->element_arity; - iterator->branch_cursor_stack[0] = 0; - iterator->branch_arity_stack[0] = store->root->branch_arity; - iterator->node_stack[0] = store->root; -} - -static void iter_push(struct store_iter *iterator, struct node *node) -{ - iterator->stack_level += 1; - iterator->element_cursor = 0; - iterator->element_arity = node->element_arity; - iterator->branch_cursor_stack[iterator->stack_level] = 0; - iterator->branch_arity_stack[iterator->stack_level] = - node->branch_arity; - iterator->node_stack[iterator->stack_level] = node; -} - -static void iter_pop(struct store_iter *iterator) -{ - iterator->stack_level -= 1; -} - -int store_iter_next(struct store_iter *iterator, STORE_KEY_T *key, - STORE_VALUE_T *value) -{ - if (iterator->stack_level == -1) - return 0; - - struct node *current_node = iterator->node_stack[iterator->stack_level]; - unsigned *branch_cursor = - iterator->branch_cursor_stack + iterator->stack_level; - if (*branch_cursor == 0 && - iterator->element_cursor < - current_node->element_arity) { // todo: write test for this - *key = STORE_NODE_ELEMENTS( - current_node)[iterator->element_cursor] - .key; - *value = STORE_NODE_ELEMENTS( - current_node)[iterator->element_cursor] - .val; - ++iterator->element_cursor; - return 1; - - } else { - if (*branch_cursor < - iterator->branch_arity_stack[iterator->stack_level]) { - iter_push(iterator, - STORE_NODE_BRANCHES( - current_node)[*branch_cursor]); - ++*branch_cursor; - return store_iter_next(iterator, key, value); - - } else { - iter_pop(iterator); - return store_iter_next(iterator, key, value); - } - } -} diff --git a/src/term.c b/src/term.c deleted file mode 100644 index cf66f01..0000000 --- a/src/term.c +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#include <stdlib.h> -#include <assert.h> -#include <stdio.h> - -#include <term.h> -#include <gc.h> - -static int name_generator(void) -{ - static int current = 0x4242; // TODO: idk? - return current++; -} - -#define MAX_CONVERSION_VARS 256 -static void to_barendregt_helper(struct term *term, int *vars, int size) -{ - assert(size < MAX_CONVERSION_VARS); - switch (term->type) { - case ABS: - vars[size] = name_generator(); - term->u.abs.name = vars[size]; - to_barendregt_helper(term->u.abs.term, vars, size + 1); - break; - case APP: - to_barendregt_helper(term->u.app.lhs, vars, size); - to_barendregt_helper(term->u.app.rhs, vars, size); - break; - case VAR: - if (term->u.var.type == BARENDREGT_VARIABLE) - break; - int ind = size - term->u.var.name - 1; - if (ind < 0) { - fprintf(stderr, "Unbound variable %d\n", - term->u.var.name); - term->u.var.name = name_generator(); - } else { - term->u.var.name = vars[size - term->u.var.name - 1]; - } - term->u.var.type = BARENDREGT_VARIABLE; - break; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } -} - -static void to_bruijn_helper(struct term *term, int *vars, int size) -{ - assert(size < MAX_CONVERSION_VARS); - switch (term->type) { - case ABS: - vars[size] = term->u.abs.name; - to_bruijn_helper(term->u.abs.term, vars, size + 1); - term->u.abs.name = 0; - break; - case APP: - to_bruijn_helper(term->u.app.lhs, vars, size); - to_bruijn_helper(term->u.app.rhs, vars, size); - break; - case VAR: - if (term->u.var.type == BRUIJN_INDEX) - break; - int ind = -1; - for (int i = 0; i < size; i++) { - if (vars[i] == term->u.var.name) { - ind = i; - break; - } - } - if (ind < 0) { - fprintf(stderr, "Unbound variable %d\n", - term->u.var.name); - } - term->u.var.name = size - ind - 1; - term->u.var.type = BRUIJN_INDEX; - break; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } -} - -void to_barendregt(struct term *term) -{ - int vars[MAX_CONVERSION_VARS] = { 0 }; - to_barendregt_helper(term, vars, 0); -} - -void to_bruijn(struct term *term) -{ - int vars[MAX_CONVERSION_VARS] = { 0 }; - to_bruijn_helper(term, vars, 0); -} - -struct term *new_term(term_type type) -{ - struct term *term = GC_malloc(sizeof(*term)); - if (!term) { - fprintf(stderr, "Out of memory!\n"); - abort(); - } - term->type = type; - return term; -} - -struct term *duplicate_term(struct term *term) -{ - switch (term->type) { - case ABS:; - struct term *abs = new_term(ABS); - abs->u.abs.name = term->u.abs.name; - abs->u.abs.term = duplicate_term(term->u.abs.term); - return abs; - case APP:; - struct term *app = new_term(APP); - app->u.app.lhs = duplicate_term(term->u.app.lhs); - app->u.app.rhs = duplicate_term(term->u.app.rhs); - return app; - case VAR:; - struct term *var = new_term(VAR); - var->u.var.name = term->u.var.name; - var->u.var.type = term->u.var.type; - return var; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } - return term; -} - -int alpha_equivalency(struct term *a, struct term *b) -{ - if (a->type != b->type) - return 0; - - switch (a->type) { - case ABS: - assert(!a->u.abs.name); // TODO: Only bruijn right now - return a->u.abs.name == b->u.abs.name && - alpha_equivalency(a->u.abs.term, b->u.abs.term); - case APP: - return alpha_equivalency(a->u.app.lhs, b->u.app.lhs) && - alpha_equivalency(a->u.app.rhs, b->u.app.rhs); - case VAR:; - assert(a->u.var.type == BRUIJN_INDEX && - b->u.var.type == BRUIJN_INDEX); - return a->u.var.name == b->u.var.name; - default: - fprintf(stderr, "Invalid type %d\n", a->type); - } - return 0; -} - -void free_term(struct term *term) -{ - switch (term->type) { - case ABS: - free_term(term->u.abs.term); - GC_free(term); - break; - case APP: - free_term(term->u.app.lhs); - free_term(term->u.app.rhs); - GC_free(term); - break; - case VAR: - GC_free(term); - break; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } -} - -void print_term(struct term *term) -{ - switch (term->type) { - case ABS: - if (term->u.abs.name) - printf("[{%d} ", term->u.abs.name); - else - printf("["); - print_term(term->u.abs.term); - printf("]"); - break; - case APP: - printf("("); - print_term(term->u.app.lhs); - printf(" "); - print_term(term->u.app.rhs); - printf(")"); - break; - case VAR: - printf("%d", term->u.var.name); - break; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } -} - -void print_blc(struct term *term) -{ - switch (term->type) { - case ABS: - printf("00"); - print_blc(term->u.abs.term); - break; - case APP: - printf("01"); - print_blc(term->u.app.lhs); - print_blc(term->u.app.rhs); - break; - case VAR: - assert(term->u.var.type == BRUIJN_INDEX); - for (int i = 0; i <= term->u.var.name; i++) - printf("1"); - printf("0"); - break; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } -} - -void print_scheme(struct term *term) -{ - switch (term->type) { - case ABS: - printf("(*lam \"%d\" ", term->u.abs.name); - print_scheme(term->u.abs.term); - printf(")"); - break; - case APP: - printf("(*app "); - print_scheme(term->u.app.lhs); - printf(" "); - print_scheme(term->u.app.rhs); - printf(")"); - break; - case VAR: - printf("(*var \"%d\")", term->u.var.name); - break; - default: - fprintf(stderr, "Invalid type %d\n", term->type); - } -} diff --git a/src/test.c b/src/test.c deleted file mode 100644 index 9c2ecc3..0000000 --- a/src/test.c +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2023, Marvin Borner <dev@marvinborner.de> - -#ifdef TEST -#ifndef NTESTS -#define NTESTS 6 -#endif - -#ifndef STARTTEST -#define STARTTEST 0 -#endif - -#define TESTDIR "./tests/" - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <time.h> - -#include <gc.h> -#include <parse.h> -#include <term.h> -#include <reduce.h> - -struct test { - struct term *in; - struct term *res; - struct term *red; - char *trans; - struct { - int alpha; - int trans; - } equivalency; -}; - -static int name_generator(void) -{ - static int current = 0xbadbad; - return current++; -} - -static void *church(int n, void(*f(void *, int)), void *x, int name) -{ - if (n == 0) - return x; - return church(n - 1, f, f(x, name), name); -} - -static void *church_numeral_builder(void *x, int name) -{ - struct term *app = new_term(APP); - app->u.app.lhs = new_term(VAR); - app->u.app.lhs->u.var.name = name; - app->u.app.lhs->u.var.type = BARENDREGT_VARIABLE; - app->u.app.rhs = x; - return app; -} - -static struct term *church_numeral(int n) -{ - struct term *abs = new_term(ABS); - abs->u.abs.name = name_generator(); - abs->u.abs.term = new_term(ABS); - - struct term *var = new_term(VAR); - var->u.var.name = name_generator(); - var->u.var.type = BARENDREGT_VARIABLE; - - abs->u.abs.term->u.abs.name = var->u.var.name; - abs->u.abs.term->u.abs.term = - church(n, church_numeral_builder, var, abs->u.abs.name); - - return abs; -} - -static struct term *identity(void) -{ - struct term *abs = new_term(ABS); - abs->u.abs.name = name_generator(); - abs->u.abs.term = new_term(VAR); - abs->u.abs.term->u.var.name = abs->u.abs.name; - abs->u.abs.term->u.var.type = BARENDREGT_VARIABLE; - return abs; -} - -static struct term *omega(void) -{ - struct term *abs = new_term(ABS); - abs->u.abs.name = name_generator(); - abs->u.abs.term = new_term(APP); - abs->u.abs.term->u.app.lhs = new_term(VAR); - abs->u.abs.term->u.app.lhs->u.var.name = abs->u.abs.name; - abs->u.abs.term->u.app.lhs->u.var.type = BARENDREGT_VARIABLE; - abs->u.abs.term->u.app.rhs = new_term(VAR); - abs->u.abs.term->u.app.rhs->u.var.name = abs->u.abs.name; - abs->u.abs.term->u.app.rhs->u.var.type = BARENDREGT_VARIABLE; - return abs; -} - -static void counter_callback(int i, char ch, void *data) -{ - (void)ch; - *(int *)data = i; -} - -static void test_church_transitions(void) -{ - const int limit = 18; - - int deviations = 0; - double time = 0; - - for (int n = 1; n <= limit; n++) { - struct term *app = new_term(APP); - app->u.app.lhs = new_term(APP); - app->u.app.lhs->u.app.lhs = church_numeral(n); - app->u.app.lhs->u.app.rhs = church_numeral(2); - app->u.app.rhs = identity(); - int counter; - - clock_t begin = clock(); - struct term *red = reduce(app, counter_callback, &counter); - clock_t end = clock(); - time += (double)(end - begin) / CLOCKS_PER_SEC; - - free_term(red); - free_term(app); - - int expected = 10 * (2 << (n - 1)) + n * 5 + 5; - if (counter + 1 != expected) - deviations++; - } - - printf("Test church ((n 2) I) with n<=%d: %.5fs, %d transition deviations\n", - limit, time, deviations); -} - -static void test_explode(void) -{ - const int limit = 23; - - int deviations = 0; - double time = 0; - - for (int n = 1; n <= limit; n++) { - struct term *abs = new_term(ABS); - abs->u.abs.name = name_generator(); - abs->u.abs.term = new_term(APP); - abs->u.abs.term->u.app.lhs = new_term(APP); - abs->u.abs.term->u.app.lhs->u.app.lhs = church_numeral(n); - abs->u.abs.term->u.app.lhs->u.app.rhs = omega(); - abs->u.abs.term->u.app.rhs = new_term(VAR); - abs->u.abs.term->u.app.rhs->u.var.name = abs->u.abs.name; - abs->u.abs.term->u.app.rhs->u.var.type = BARENDREGT_VARIABLE; - - int counter; - - clock_t begin = clock(); - struct term *red = reduce(abs, counter_callback, &counter); - clock_t end = clock(); - time += (double)(end - begin) / CLOCKS_PER_SEC; - - free_term(red); - free_term(abs); - - int expected = 9 * n + 15; - if (counter + 1 != expected) - deviations++; - } - - printf("Test explode (λx.((n ω) x)) with n<=%d: %.5fs, %d transition deviations\n", - limit, time, deviations); -} - -static char *read_file(const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - fprintf(stderr, "Can't open file %s: %s\n", path, - strerror(errno)); - return 0; - } - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - fseek(f, 0, SEEK_SET); - - char *string = malloc(fsize + 1); - int ret = fread(string, fsize, 1, f); - fclose(f); - - if (ret != 1) { - fprintf(stderr, "Can't read file %s: %s\n", path, - strerror(errno)); - return 0; - } - - string[fsize] = 0; - return string; -} - -static void callback(int i, char ch, void *data) -{ - struct test *test = data; - if (ch != test->trans[i]) { - fprintf(stderr, "Transition deviation at index %d!\n", i); - test->equivalency.trans = 0; - } -} - -int main(void) -{ - GC_INIT(); - GC_enable_incremental(); - - struct test tests[NTESTS] = { 0 }; - - char in_template[] = TESTDIR "x.in"; - char red_template[] = TESTDIR "x.red"; - char trans_template[] = TESTDIR "x.trans"; - int offset = strlen(TESTDIR); - for (int i = 0; i < NTESTS; i++) { - char ch = '0' + i + 1 + STARTTEST; - in_template[offset] = ch; - red_template[offset] = ch; - trans_template[offset] = ch; - tests[i].trans = read_file(trans_template); - - char *in = read_file(in_template); - tests[i].in = parse_bruijn(in); - free(in); - - char *red = read_file(red_template); - tests[i].red = parse_bruijn(red); - to_bruijn(tests[i].red); - free(red); - - tests[i].equivalency.trans = 1; - } - - clock_t begin = clock(); - for (int i = 0; i < NTESTS; i++) { - tests[i].res = reduce(tests[i].in, callback, &tests[i]); - printf("Test %d done\n", i + 1 + STARTTEST); - } - clock_t end = clock(); - - for (int i = 0; i < NTESTS; i++) { - to_bruijn(tests[i].res); - tests[i].equivalency.alpha = - alpha_equivalency(tests[i].res, tests[i].red); - free_term(tests[i].res); - free_term(tests[i].red); - free(tests[i].trans); - } - - printf("\n=== REDUCTION SUMMARY ===\n"); - printf("Reduced tests in %.5fs\n", - (double)(end - begin) / CLOCKS_PER_SEC); - for (int i = 0; i < NTESTS; i++) { - if (tests[i].equivalency.alpha && tests[i].equivalency.trans) - continue; - printf("Test %d: [failed]\n\talpha-equivalency: %d\n\ttrans-equivalency: %d\n", - i + 1 + STARTTEST, tests[i].equivalency.alpha, - tests[i].equivalency.trans); - } - - printf("\n=== OTHER TESTS ===\n"); - test_church_transitions(); - test_explode(); -} -#else -__attribute__((unused)) static int no_testing; -#endif |