/
stacks.c
319 lines (279 loc) · 9.25 KB
/
stacks.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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/* stacks.c
* Copyright: (When this is determined...it will go here)
* CVS Info
* $Id$
* Overview:
* Stack handling routines for Parrot
* Data Structure and Algorithms:
* The stack is stored as a circular, doubly-linked list of
* chunks, where each chunk has room for STACK_CHUNK_DEPTH
* entries. The invariant maintained is that there is always room
* for another entry; if a chunk is filled, a new chunk is added
* onto the list before returning.
* History:
* Notes:
* References: */
#include "parrot/parrot.h"
Stack_chunk *
new_stack(Interp *interpreter)
{
#ifdef TIDY
int i;
#endif
Stack_chunk *stack = mem_allocate_aligned(sizeof(Stack_chunk));
stack->used = 0;
stack->next = stack;
stack->prev = stack;
#ifdef TIDY
for (i = 0; i < STACK_CHUNK_DEPTH; i++)
stack->entry[i].flags = NO_STACK_ENTRY_FLAGS;
#endif
return stack;
}
/* Returns the height of the stack. The maximum "depth" is height - 1 */
size_t
stack_height(Interp *interpreter, Stack_chunk *stack_base)
{
Stack_chunk *chunk;
size_t height = stack_base->used;
for (chunk = stack_base->next; chunk != stack_base; chunk = chunk->next)
height += chunk->used;
return height;
}
/* If depth >= 0, return the entry at that depth from the top of the stack,
with 0 being the top entry. If depth < 0, then return the entry |depth|
entries from the bottom of the stack.
Returns NULL if |depth| > number of entries in stack
*/
Stack_entry *
stack_entry(Interp *interpreter, Stack_chunk *stack_base, Intval depth)
{
Stack_chunk *chunk;
Stack_entry *entry = NULL;
size_t offset = (size_t)depth;
/* For negative depths, look from the bottom of the stack up. */
if (depth < 0) {
chunk = stack_base;
offset = (size_t)-depth;
while (offset >= chunk->used && chunk != stack_base) {
offset -= chunk->used;
chunk = chunk->next;
}
if (offset < chunk->used) {
entry = &chunk->entry[offset - 1];
}
}
else {
chunk = stack_base->prev; /* Start at top */
while (offset >= chunk->used && chunk != stack_base) {
offset -= chunk->used;
chunk = chunk->prev;
}
if (offset < chunk->used) {
entry = &chunk->entry[chunk->used - offset - 1];
}
}
return entry;
}
/* Rotate the top N entries by one. If N > 0, the rotation is bubble up,
so the top most element becomes the Nth element. If N < 0, the rotation
is bubble down, so that the Nth element becomes the top most element.
*/
void
rotate_entries(Interp *interpreter, Stack_chunk *stack_base,
Intval num_entries)
{
Stack_entry temp;
Intval i;
Intval depth = num_entries - 1;
if (num_entries >= -1 && num_entries <= 1) {
return;
}
if (num_entries < 0) {
num_entries = -num_entries;
depth = num_entries - 1;
if (stack_height(interpreter, stack_base) < (size_t)num_entries) {
internal_exception(ERROR_STACK_SHALLOW, "Stack too shallow!\n");
}
temp = *stack_entry(interpreter, stack_base, depth);
for (i = depth; i > 0; i--) {
*stack_entry(interpreter, stack_base, i) =
*stack_entry(interpreter, stack_base, i - 1);
}
*stack_entry(interpreter, stack_base, 0) = temp;
}
else {
if (stack_height(interpreter, stack_base) < (size_t)num_entries) {
internal_exception(ERROR_STACK_SHALLOW, "Stack too shallow!\n");
}
temp = *stack_entry(interpreter, stack_base, 0);
for (i = 0; i < depth; i++) {
*stack_entry(interpreter, stack_base, i) =
*stack_entry(interpreter, stack_base, i + 1);
}
*stack_entry(interpreter, stack_base, depth) = temp;
}
}
/* Push something on the generic stack.
Note that the cleanup pointer, if non-NULL, points to a routine
that'll be called when the entry is removed from the stack. This is
handy for those cases where you need some sort of activity to take
place when an entry is removed, such as when you push a lexical
lock onto the call stack, or localize (or tempify, or whatever
we're calling it) variable or something
*/
void
stack_push(Interp *interpreter, Stack_chunk *stack_base,
void *thing, Stack_entry_type type, Stack_cleanup_method cleanup)
{
Stack_chunk *chunk = stack_base->prev;
Stack_entry *entry;
/* Do we need a new chunk? */
if (chunk->used == STACK_CHUNK_DEPTH) {
/* Need to add a new chunk */
Stack_chunk *new_chunk = mem_allocate_aligned(sizeof(Stack_chunk));
new_chunk->used = 0;
new_chunk->next = stack_base;
new_chunk->prev = chunk;
chunk->next = new_chunk;
stack_base->prev = new_chunk;
chunk = new_chunk;
}
entry = &chunk->entry[chunk->used];
/* Remember the type */
entry->entry_type = type;
/* If we were passed a cleanup function, mark the flag entry
* for this as needing cleanup */
entry->flags = (cleanup ? STACK_ENTRY_CLEANUP_FLAG
: NO_STACK_ENTRY_FLAGS);
/* Remember the cleanup function */
entry->cleanup = cleanup;
/* Store our thing */
switch (type) {
case STACK_ENTRY_INT:
entry->entry.int_val = *(Intval *)thing;
break;
case STACK_ENTRY_FLOAT:
entry->entry.num_val = *(Floatval *)thing;
break;
case STACK_ENTRY_PMC:
entry->entry.pmc_val = (PMC *)thing;
break;
case STACK_ENTRY_STRING:
entry->entry.string_val = (String *)thing;
break;
case STACK_ENTRY_POINTER:
entry->entry.generic_pointer = thing;
break;
case STACK_ENTRY_DESTINATION:
entry->entry.generic_pointer = thing;
break;
default:
internal_exception(ERROR_BAD_STACK_TYPE,
"Invalid stack_entry_type!\n");
break;
}
chunk->used++;
}
/* Pop off an entry and return a pointer to the contents */
void *
stack_pop(Interp *interpreter, Stack_chunk *stack_base,
void *where, Stack_entry_type type)
{
Stack_chunk *chunk = stack_base->prev;
Stack_entry *entry;
/* We may have an empty chunk at the end of the list */
if (chunk->used == 0 && chunk != stack_base) {
/* That chunk != stack check is just to allow the empty stack case
* to fall through to the following exception throwing code. */
/* Need to pop off the last entry */
stack_base->prev = chunk->prev;
stack_base->prev->next = stack_base;
/* Relying on GC feels dirty... */
chunk = stack_base->prev;
}
/* Quick sanity check */
if (chunk->used == 0) {
internal_exception(ERROR_STACK_EMPTY, "No entries on stack!\n");
}
/* Now decrement the SP */
chunk->used--;
entry = &chunk->entry[chunk->used];
/* Types of 0 mean we don't care */
if (type && entry->entry_type != type) {
internal_exception(ERROR_BAD_STACK_TYPE,
"Wrong type on top of stack!\n");
}
/* Cleanup routine? */
if (entry->flags & STACK_ENTRY_CLEANUP_FLAG) {
(*entry->cleanup)(entry);
}
/* Sometimes the caller doesn't care what the value was */
if (where == NULL) {
return NULL;
}
/* Snag the value */
switch (type) {
case STACK_ENTRY_INT:
*(Intval *)where = entry->entry.int_val;
break;
case STACK_ENTRY_FLOAT:
*(Floatval *)where = entry->entry.num_val;
break;
case STACK_ENTRY_PMC:
*(PMC **)where = entry->entry.pmc_val;
break;
case STACK_ENTRY_STRING:
*(String **)where = entry->entry.string_val;
break;
case STACK_ENTRY_POINTER:
*(void **)where = entry->entry.generic_pointer;
break;
case STACK_ENTRY_DESTINATION:
*(void **)where = entry->entry.generic_pointer;
break;
default:
internal_exception(ERROR_BAD_STACK_TYPE,
"Wrong type on top of stack!\n");
break;
}
return where;
}
/* Pop off a destination entry and return a pointer to the contents*/
void *
pop_dest(Interp *interpreter)
{
/* We don't mind the extra call, so we do this: (previous comment
* said we *do* mind, but I say let the compiler decide) */
void *dest;
(void)stack_pop(interpreter, interpreter->control_stack,
&dest, STACK_ENTRY_DESTINATION);
return dest;
}
void *
stack_peek(Interp *interpreter, Stack_chunk *stack_base,
Stack_entry_type *type)
{
Stack_entry *entry = stack_entry(interpreter, stack_base, 0);
if (entry == NULL) {
return NULL;
}
if (type != NULL) {
*type = entry->entry_type;
}
return entry->entry.generic_pointer;
}
Stack_entry_type
get_entry_type(Interp *interpreter, Stack_entry *entry)
{
return entry->entry_type;
}
/*
* Local variables:
* c-indentation-style: bsd
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/