diff options
Diffstat (limited to 'src/term.c')
-rw-r--r-- | src/term.c | 109 |
1 files changed, 89 insertions, 20 deletions
@@ -3,9 +3,11 @@ #include <stdlib.h> #include <stdio.h> +#include <assert.h> #include <log.h> #include <term.h> +#include <map.h> struct term *term_new(term_type_t type, hash_t hash, size_t depth) { @@ -19,13 +21,6 @@ struct term *term_new(term_type_t type, hash_t hash, size_t depth) return term; } -void term_refer(struct term *term, size_t depth) -{ - term->refs++; - if (depth < term->depth) // lower depths are more important - term->depth = depth; -} - void term_print(struct term *term) { switch (term->type) { @@ -49,16 +44,90 @@ void term_print(struct term *term) } } -/* void deref_term(struct term *term) */ -/* { */ -/* if (term->type == ABS) { */ -/* deref_term(term->u.abs.term); */ -/* } else if (term->type == APP) { */ -/* deref_term(term->u.app.lhs); */ -/* deref_term(term->u.app.rhs); */ -/* } */ - -/* // TODO: remove from hashmap? */ -/* if (--term->refs == 0) */ -/* free(term); */ -/* } */ +struct term *term_rehash_abs(struct term *head, struct term *term) +{ + if (head->u.abs.term->hash == term->hash) + return head; + + hash_t res = + hash((uint8_t *)&head->type, sizeof(head->type), term->hash); + + assert(res != head->hash); + + struct term *match = map_get(res); + if (match) { // already exists + term_refer(match, head->depth); + term_deref(head); + return match; + } else { // create new + struct term *new = term_new(ABS, res, head->depth); + new->u.abs.term = term; + term_refer(term, head->depth + 1); + term_deref(head); + return new; + } +} + +struct term *term_rehash_app(struct term *head, struct term *lhs, + struct term *rhs) +{ + if (head->u.app.lhs->hash == lhs->hash && + head->u.app.rhs->hash == rhs->hash) + return head; + + hash_t res = + hash((uint8_t *)&head->type, sizeof(head->type), lhs->hash); + res = hash((uint8_t *)&res, sizeof(res), rhs->hash); + + assert(res != head->hash); + + struct term *match = map_get(res); + if (match) { // already exists + term_refer(match, head->depth); + term_deref(head); + return match; + } else { // create new + struct term *new = term_new(APP, res, head->depth); + new->u.app.lhs = lhs; + new->u.app.rhs = rhs; + if (head->u.app.lhs->hash != lhs->hash) + term_refer(lhs, head->depth + 1); + if (head->u.app.rhs->hash != rhs->hash) + term_refer(rhs, head->depth + 1); + term_deref(head); + return new; + } +} + +void term_refer_head(struct term *term, size_t depth) +{ + term->refs++; + if (depth < term->depth) // lower depths are more important + term->depth = depth; +} + +void term_refer(struct term *term, size_t depth) +{ + if (term->type == ABS) { + term_refer(term->u.abs.term, depth + 1); + } else if (term->type == APP) { + term_refer(term->u.app.lhs, depth + 1); + term_refer(term->u.app.rhs, depth + 1); + } + + term_refer_head(term, depth); +} + +void term_deref(struct term *term) +{ + if (term->type == ABS) { + term_deref(term->u.abs.term); + } else if (term->type == APP) { + term_deref(term->u.app.lhs); + term_deref(term->u.app.rhs); + } + + // TODO: remove from hashmap? + if (--term->refs == 0) + free(term); +} |