-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlist.c
293 lines (265 loc) · 8.6 KB
/
list.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#include "list.h"
/* Allocate a new blank list. */
AList *list_new() {
AList *list = malloc(sizeof(AList));
list->first = NULL;
list->last = NULL;
list->length = 0;
return list;
}
/* Allocate a new list-element and set it to
* point to <val>. */
static
AListElem *create_element(AValue *val) {
AListElem *elem = malloc(sizeof(AListElem));
elem->val = val;
elem->next = NULL;
return elem;
}
/* Push an AValue* onto the front of a list,
* mutating the list. */
void list_cons(AValue *val, AList *list) {
AListElem *elem = create_element(val);
elem->prev = NULL;
elem->next = list->first;
if (list->first != NULL) {
list->first->prev = elem;
}
list->first = elem;
if (list->last == NULL) {
list->last = elem;
}
list->length ++;
}
/* Put an AValue* at the end of a list,
* mutating the list. */
void list_append(AList *list, AValue *val) {
AListElem *elem = create_element(val);
elem->prev = list->last;
if (list->last != NULL) {
list->last->next = elem;
}
list->last = elem;
if (list->first == NULL) {
list->first = elem;
}
list->length ++;
}
/* Create a new list from a proto-list. Note
* that it (for now) just takes the top element
* of the stack resulting from evaluating each
* wordseq node, even though in theory you could
* have a list structured like {1 2, 3 4, 5 6}.
* I don't even know what the semantics of that
* would be though? Maybe I'll make it do something
* useful if I can think of what would be useful.
* (Tuples? Matrices???? Too crazy?) */
/* Takes a varbuffer because, hey, there might
* be lexical variables in that list! */
AList *list_reify(AVarBuffer *buf, AProtoList *proto, unsigned int linenum) {
AWordSeqNode *current = proto->first;
AList *list = list_new();
while (current != NULL) {
/* create a new tiny stack to evaluate this on */
AStack *tmp = stack_new(2);
eval_sequence(tmp, buf, current);
/* get the top element of the stack, and issue a warning
* if there's more than one element on the stack */
if (tmp->size > 1) {
fprintf(stderr, "warning: expression ‘");
fprint_wordseq_node(stderr, current);
fprintf(stderr, "’ in list at line %d generated %d elements, "
"but only the last one will be used\n",
linenum, tmp->size);
} else if (tmp->size < 1) {
fprintf(stderr, "warning: expression ‘");
fprint_wordseq_node(stderr, current);
fprintf(stderr, "’ in list at line %d generated no elements. "
"Skipping.\n",
linenum);
current = current->next;
continue;
}
/* get a new reference to the first element */
AValue *val = stack_get(tmp, 0);
free_stack(tmp);
/* put the element onto the list */
list_append(list, val);
/* move to the next element */
current = current->next;
}
return list;
}
/* Given a value of type 'list', return the tail
* of the list. If it has no other references,
* re-uses the original list value. */
/* Note: obviously this function is partial and
* doesn't work on lists of length 0. */
AValue *tail_list_val(AValue *val) {
if (val->data.list->length == 0) {
fprintf(stderr, "error: attempt to take tail of empty list");
return NULL;
}
if (val->refs == 1) {
AListElem *oldfirst = val->data.list->first;
AListElem *newfirst = val->data.list->first->next;
AListElem *newlast = val->data.list->last;
if (newfirst == NULL) {
newlast = NULL;
}
val->data.list->first = newfirst;
if (newfirst != NULL) {
val->data.list->first->prev = NULL;
}
val->data.list->last = newlast;
val->data.list->length --;
/* don't need the old head element-holder anymore */
delete_ref(oldfirst->val);
free(oldfirst);
return ref(val);
} else {
AList *newlist = list_new();
AListElem *current = val->data.list->first->next;
while (current) {
list_append(newlist, ref(current->val));
current = current->next;
}
return ref(val_list(newlist));
}
}
/* Given a value of type 'list', return a list
* containing all but the last element of the
* original list.
* of the list. If it has no other references,
* re-uses the original list value. */
/* Also partial and returns NULL on lists
* of length 0. */
AValue *init_list_val(AValue *val) {
if (val->data.list->length == 0) {
fprintf(stderr, "error: attempt to take init of empty list");
return NULL;
}
if (val->refs == 1) {
AListElem *oldlast = val->data.list->last;
AListElem *newlast = val->data.list->last->prev;
AListElem *newfirst = val->data.list->first;
if (newlast == NULL) {
newfirst = NULL;
}
val->data.list->last = newlast;
if (newlast != NULL) {
val->data.list->last->next = NULL;
}
val->data.list->first = newfirst;
val->data.list->length --;
/* don't need the old head element-holder anymore */
delete_ref(oldlast->val);
free(oldlast);
return ref(val);
} else {
AList *newlist = list_new();
AListElem *current = val->data.list->first;
while (current->next) {
list_append(newlist, ref(current->val));
current = current->next;
}
return ref(val_list(newlist));
}
}
/* Given a value of type 'list', return the head
* of the list. (again, partial, list of length 0
* has no head) */
/* (NOTE: doesn't destroy list object; returns a
* fresh reference to head value) */
AValue *head_list_val(AValue *val) {
if (val->data.list->length == 0) {
fprintf(stderr, "error: attempt to take head of empty list");
return NULL;
}
assert(val->data.list != NULL);
assert(val->data.list->first != NULL);
AValue *hval = ref(val->data.list->first->val);
return hval;
}
/* Given a value of type 'list', return the last element
* of the list. (again, partial, list of length 0
* has no last) */
/* (NOTE: doesn't destroy list object; returns a
* fresh reference to head value) */
AValue *last_list_val(AValue *val) {
if (val->data.list->length == 0) {
fprintf(stderr, "error: attempt to take last of empty list");
return NULL;
}
assert(val->data.list != NULL);
AValue *lval = ref(val->data.list->last->val);
return lval;
}
/* Print out a list to an arbitrary filehandle. */
void fprint_list(FILE *out, AList *l) {
AListElem *current = l->first;
fprintf(out, "{ ");
while (current) {
fprint_val(out, current->val);
if (current->next != NULL) {
fprintf(out, ", ");
} else {
fprintf(out, " ");
}
current = current->next;
}
fprintf(out, "}");
}
/* Given a value and a value of type 'list', return
* the value cons'd onto the front of the list.
* Can reuse the list value if only has one reference. */
AValue *cons_list_val(AValue *val, AValue *l) {
if (l->refs == 1) {
list_cons(ref(val), l->data.list);
return ref(l);
} else {
AList *newlist = list_new();
AListElem *current = l->data.list->first;
while (current) {
assert(current->val != NULL);
list_append(newlist, ref(current->val));
current = current->next;
}
list_cons(ref(val), newlist);
return ref(val_list(newlist));
}
}
/* Given a value and a value of type 'list', return
* the value appended to the end of the list.
* Can reuse the list value if only has one reference. */
AValue *append_list_val(AValue *l, AValue *val) {
if (l->refs == 1) {
list_append(l->data.list, ref(val));
return ref(l);
} else {
AList *newlist = list_new();
AListElem *current = l->data.list->first;
while (current) {
assert(current->val != NULL);
list_append(newlist, ref(current->val));
current = current->next;
}
list_append(newlist, ref(val));
return ref(val_list(newlist));
}
}
/* Print out a list. */
void print_list(AList *l) {
fprint_list(stdout, l);
}
/* Free a list. */
void free_list(AList *l) {
AListElem *current = l->first;
while (current) {
AListElem *next = current->next;
delete_ref(current->val);
free(current);
current = next;
}
free(l);
}