// based on the RKNL abstract machine #include #include #include #include #include struct stack { void *data; struct stack *next; }; #define STORE_SIZE 128 // hashmap // TODO: Optimize? struct store { struct stack *stack; }; struct store_data { int key; void *data; }; struct closure { struct term *term; struct store (*store)[STORE_SIZE]; }; 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)[STORE_SIZE]; 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 = malloc(sizeof(*new)); new->data = data; new->next = stack; printf("Pushing %p to %p => %p\n", data, (void *)stack, (void *)new); return new; } static struct stack *stack_next(struct stack *stack) { printf("Popping %p => %p\n", (void *)stack, (void *)stack->next); return stack->next; } // no deep cloning for now // TODO: deep clone needed? static struct stack *stack_clone(struct stack *stack) { struct stack *new = 0; struct stack *iterator = stack; while (iterator) { new = stack_push(new, iterator->data); iterator = stack_next(iterator); } return new; } // no deep cloning for now // TODO: deep clone needed? static struct store (*store_clone(struct store (*store)[STORE_SIZE]))[STORE_SIZE] { struct store(*new)[STORE_SIZE] = calloc(1, STORE_SIZE * sizeof(struct store)); for (int key = 0; key < STORE_SIZE; key++) { struct store *list = &(*store)[key % STORE_SIZE]; if (list->stack) (*new)[key].stack = stack_clone(list->stack); } return new; } static int store_replace(struct store (*store)[STORE_SIZE], int key, void *data) { struct store *elem = &(*store)[key % STORE_SIZE]; struct stack *iterator = elem->stack; while (iterator) { struct store_data *keyed = iterator->data; if (keyed->key == key) { keyed->data = data; printf("Replaced %d\n", key); return 1; } iterator = stack_next(iterator); } return 0; } static struct store (*store_push(struct store (*store)[STORE_SIZE], int key, void *data))[STORE_SIZE] { printf("Storing %p with %d (%d)\n", data, key, key % STORE_SIZE); if (store_replace(store, key, data)) return store; struct store *elem = &(*store)[key % STORE_SIZE]; struct store_data *keyed = malloc(sizeof(*keyed)); keyed->key = key; keyed->data = data; elem->stack = stack_push(elem->stack, keyed); return store; } static void *store_get(struct store (*store)[STORE_SIZE], int key) { printf("Wanting %d (%d)\n", key, key % STORE_SIZE); struct store *elem = &(*store)[key % STORE_SIZE]; struct stack *iterator = elem->stack; while (iterator) { struct store_data *keyed = iterator->data; if (keyed->key == key) { printf("Got it: %p\n", keyed->data); return keyed->data; } iterator = stack_next(iterator); } printf("Couldn't find it. Anyways..\n"); return 0; // => unbound variable } static void econf(struct conf *conf, struct term *term, struct store (*store)[STORE_SIZE], 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)[STORE_SIZE], struct stack **stack) { struct closure *closure = malloc(sizeof(*closure)); closure->term = (*term)->u.app.rhs; closure->store = store_clone(*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)[STORE_SIZE]) { struct box *box = malloc(sizeof(*box)); box->state = TODO; box->term = 0; // TODO: necessary? struct term *abs = new_term(ABS); abs->u.abs.name = (*term)->u.abs.name; abs->u.abs.term = (*term)->u.abs.term; struct closure *closure = malloc(sizeof(*closure)); closure->term = abs; closure->store = store_clone(*store); struct cache *cache = 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)[STORE_SIZE], struct stack **stack, struct box *box) { assert(box->term->type == CLOSURE); struct closure *closure = box->term->u.other; print_term(closure->term); printf("\n"); struct cache *cache = malloc(sizeof(*cache)); cache->box = box; cache->term = new_term(VAR); struct term *cache_term = new_term(CACHE); cache_term->u.other = cache; *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 cache *cache) { printf("Setting %p\n", (void *)cache->box); cache->box->state = DONE; cache->box->term = *term; *stack = stack_next(*stack); *term = *term; return 0; } static int transition_6(struct term **term, struct store (**store)[STORE_SIZE], struct stack **stack, struct term *peek_term, struct closure *closure) { struct box *box = malloc(sizeof(*box)); box->state = TODO; box->term = peek_term->u.app.rhs; *term = closure->term->u.abs.term; *store = store_push(closure->store, closure->term->u.abs.name, box); *stack = stack_next(*stack); return 0; } static int transition_7(struct term **term, struct store (**store)[STORE_SIZE], struct stack **stack, struct box *box, struct closure *closure) { int x = name_generator(); struct box *var_box = malloc(sizeof(*var_box)); var_box->state = DONE; var_box->term = new_term(VAR); var_box->term->u.var.name = x; struct cache *cache = 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_push(closure->store, closure->term->u.abs.name, var_box); *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)[STORE_SIZE], 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) { struct term *term = conf->u.econf.term; struct store(*store)[STORE_SIZE] = conf->u.econf.store; struct stack *stack = conf->u.econf.stack; printf("ECONF: %p %p %p\n", (void *)term, (void *)store, (void *)stack); int ret = 1; switch (term->type) { case APP: // (1) printf("(1)\n"); ret = transition_1(&term, &store, &stack); econf(conf, term, store, stack); return ret; case ABS: // (2) printf("(2)\n"); ret = transition_2(&stack, &term, &store); cconf(conf, stack, term); return ret; case VAR:; struct box *box = store_get(store, term->u.var.name); if (!box) { box = malloc(sizeof(*box)); box->state = DONE; // TODO: Using term directly is probably fine box->term = new_term(VAR); box->term->u.var.name = term->u.var.name; } if (box->state == TODO) { // (3) printf("(3)\n"); ret = transition_3(&term, &store, &stack, box); econf(conf, term, store, stack); return ret; } else if (box->state == DONE) { // (4) printf("(4)\n"); 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) { struct stack *stack = conf->u.cconf.stack; struct term *term = conf->u.cconf.term; printf("CCONF: %p %p\n", (void *)stack, (void *)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) { printf("(5)\n"); ret = transition_5(&stack, &term, cache); 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) { printf("(6)\n"); struct store(*store)[STORE_SIZE]; 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) printf("(7)\n"); struct store(*store)[STORE_SIZE]; ret = transition_7(&term, &store, &stack, box, closure); econf(conf, term, store, stack); return ret; } if (closure->term->type == ABS && box->state == DONE) { // (8) printf("(8)\n"); 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) printf("(9)\n"); struct store(*store)[STORE_SIZE]; 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) printf("(10)\n"); 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) printf("(11)\n"); 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) { if (conf->type == ECONF) { return transition_closure(conf); } else if (conf->type == CCONF) { return transition_computed(conf); } fprintf(stderr, "Invalid transition state %x\n", conf->type); return 1; } static struct conf *for_each_state(struct conf *conf) { int ret = transition(conf); return ret ? conf : for_each_state(conf); } struct term *reduce(struct term *term) { struct stack stack = { 0 }; struct store store[STORE_SIZE] = { 0 }; struct conf conf = { .type = ECONF, .u.econf.term = term, .u.econf.store = &store, .u.econf.stack = &stack, }; for_each_state(&conf); assert(conf.type == CCONF); return duplicate_term(conf.u.cconf.term); }