/* Text buffer using doubly-linked lists * 15-122 Principles of Imperative Computation, Fall 2010 * Frank Pfenning */ /* Edit buffer interface */ typedef struct tbuf* tbuf; tbuf tbuf_new(); /* create new edit buffer */ bool tbuf_empty(tbuf B); /* is it empty? */ void insert_char(tbuf B, char c); /* insert character after point */ void delete_char(tbuf B); /* delete character before point */ void forward_chars(tbuf B, int n); /* move forward n chars */ void backward_chars(tbuf B, int n); /* move backward n chars */ string tbuf_to_string(tbuf B); /* convert edit buffer to string */ typedef char elem; /* Text buffer implementation */ /* Double-linked lists */ typedef struct dll* dll; struct dll { elem data; /* data item */ dll prev; /* prev node, where p->prev->next == p, if valid */ dll next; /* next node, where p->next->prev == p, if valid */ }; /* Text buffer */ struct tbuf { dll min; /* min buffer position, only min->next valid */ dll point; /* min <= point < max */ dll max; /* max buffer position, only max->prev valid */ int size; /* number of characters */ }; bool is_tbuf(tbuf B) { if (B == NULL) return false; /* (1) */ if (B->min == NULL) return false; /* (2) */ if (B->point == NULL) return false; /* (3), redundant with (7ab) */ if (B->max == NULL) return false; /* (4) */ if (B->min->prev != NULL) return false; /* (5), to avoid possible cycles */ { dll p = B->min; bool point_ok = false; int i = 0; while (p != B->max) //@loop_invariant p != NULL; /* (6) */ { if (p == B->point) point_ok = true; /* (7a) */ if (p->next == NULL) return false; /* (8) */ if (p->next->prev != p) return false; /* (9) */ p = p->next; i++; /* (10a) */ if (p->prev->next != p) return false; /* (11), redundant with (9) on p->prev */ } return point_ok && B->size == i-1; /* (7b) and (10b) */ } } tbuf tbuf_new() //@ensures is_tbuf(\result); //@ensures tbuf_empty(\result); { tbuf B = alloc(struct tbuf); dll min = alloc(struct dll); dll max = alloc(struct dll); min->prev = NULL; /* establishes (5) */ min->next = max; /* ests (8) */ max->prev = min; /* ests (9) on min */ /* max->next is irrelevant */ B->min = min; /* ests (2) */ B->point = min; /* ests (3) and (7a) */ B->max = max; /* ests (4) */ B->size = 0; /* ests (10b) */ return B; } bool tbuf_empty(tbuf B) //@requires is_tbuf(B); { return B->size == 0; /* or: B->min->next == B->max; */ } /* forward_chars(B, n): move forward n chars, stop at end of buffer */ void forward_chars(tbuf B, int n) //@requires is_tbuf(B); //@requires 0 <= n; //@ensures is_tbuf(B); { int i; for (i = 0; i < n && B->point->next != B->max; i++) { B->point = B->point->next; } } /* backward_chars(B, n): move backward n chars, stop at beginning of buffer */ void backward_chars(tbuf B, int n) //@requires is_tbuf(B); //@ensures is_tbuf(B); { int i; for (i = 0; i < n && B->point != B->min; i++) { B->point = B->point->prev; } } void insert_char(tbuf B, elem e) //@requires is_tbuf(B); //@ensures is_tbuf(B); { dll q = alloc(struct dll); q->data = e; q->prev = B->point; /* ok by (1) */ q->next = B->point->next; /* ok by (3) */ q->next->prev = q; /* ok by (8) and (7); ests (9) */ q->prev->next = q; /* ok by (8); ests (11) */ B->point = q; /* ests (3) and (7) */ B->size++; /* ests (10) */ } void delete_char(tbuf B) //@requires is_tbuf(B); //@ensures is_tbuf(B); { assert(B->point != B->min, "cannot delete at left end"); { dll p = B->point->prev; dll n = B->point->next; p->next = n; n->prev = p; B->point = p; B->size--; } } string tbuf_to_string(tbuf B) //@requires is_tbuf(B); //@ensures string_length(\result) == B->size+1; /* internal */ { dll p = B->min; char[] result = alloc_array(char, B->size+2); int i = 0; /* special case: we are at beginning of buffer */ if (p == B->point) { result[i] = '.'; i++; } p = p->next; /* now we are on the first character, if there is one */ while (p != B->max) { result[i] = p->data; i++; if (p == B->point) { result[i] = '.'; i++; } p = p->next; } //@assert i == B->size+1; result[i] = '\0'; /* null-terminate character array */ return string_from_chararray(result); }