* Sync comment with code's reality.
[dragonfly.git] / contrib / bc / dc / stack.c
1 /* 
2  * implement stack functions for dc
3  *
4  * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you can either send email to this
18  * program's author (see below) or write to:
19  *
20  *    The Free Software Foundation, Inc.
21  *    59 Temple Place, Suite 330
22  *    Boston, MA 02111 USA
23  */
24
25 /* This module is the only one that knows what stacks (both the
26  * regular evaluation stack and the named register stacks)
27  * look like.
28  */
29
30 #include "config.h"
31
32 #include <stdio.h>
33 #ifdef HAVE_STDLIB_H
34 # include <stdlib.h>
35 #endif
36 #include "dc.h"
37 #include "dc-proto.h"
38 #include "dc-regdef.h"
39
40 /* an oft-used error message: */
41 #define Empty_Stack     fprintf(stderr, "%s: stack empty\n", progname)
42
43
44 /* simple linked-list implementaion suffices: */
45 struct dc_list {
46         dc_data value;
47         struct dc_array *array; /* opaque */
48         struct dc_list *link;
49 };
50 typedef struct dc_list dc_list;
51
52 /* the anonymous evaluation stack */
53 static dc_list *dc_stack=NULL;
54
55 /* the named register stacks */
56 static dc_list *dc_register[DC_REGCOUNT];
57
58 \f
59 /* allocate a new dc_list item */
60 static dc_list *
61 dc_alloc DC_DECLVOID()
62 {
63         dc_list *result;
64
65         result = dc_malloc(sizeof *result);
66         result->value.dc_type = DC_UNINITIALIZED;
67         result->array = NULL;
68         result->link = NULL;
69         return result;
70 }
71
72 \f
73 /* check that there are two numbers on top of the stack,
74  * then call op with the popped numbers.  Construct a dc_data
75  * value from the dc_num returned by op and push it
76  * on the stack.
77  * If the op call doesn't return DC_SUCCESS, then leave the stack
78  * unmodified.
79  */
80 void
81 dc_binop DC_DECLARG((op, kscale))
82         int (*op)DC_PROTO((dc_num, dc_num, int, dc_num *)) DC_DECLSEP
83         int kscale DC_DECLEND
84 {
85         dc_data a;
86         dc_data b;
87         dc_data r;
88
89         if (!dc_stack || !dc_stack->link){
90                 Empty_Stack;
91                 return;
92         }
93         if (dc_stack->value.dc_type!=DC_NUMBER
94                         || dc_stack->link->value.dc_type!=DC_NUMBER){
95                 fprintf(stderr, "%s: non-numeric value\n", progname);
96                 return;
97         }
98         (void)dc_pop(&b);
99         (void)dc_pop(&a);
100         if ((*op)(a.v.number, b.v.number, kscale, &r.v.number) == DC_SUCCESS){
101                 r.dc_type = DC_NUMBER;
102                 dc_push(r);
103                 dc_free_num(&a.v.number);
104                 dc_free_num(&b.v.number);
105         }else{
106                 /* op failed; restore the stack */
107                 dc_push(a);
108                 dc_push(b);
109         }
110 }
111
112 /* check that there are two numbers on top of the stack,
113  * then call op with the popped numbers.  Construct two dc_data
114  * values from the dc_num's returned by op and push them
115  * on the stack.
116  * If the op call doesn't return DC_SUCCESS, then leave the stack
117  * unmodified.
118  */
119 void
120 dc_binop2 DC_DECLARG((op, kscale))
121         int (*op)DC_PROTO((dc_num, dc_num, int, dc_num *, dc_num *)) DC_DECLSEP
122         int kscale DC_DECLEND
123 {
124         dc_data a;
125         dc_data b;
126         dc_data r1;
127         dc_data r2;
128
129         if (!dc_stack || !dc_stack->link){
130                 Empty_Stack;
131                 return;
132         }
133         if (dc_stack->value.dc_type!=DC_NUMBER
134                         || dc_stack->link->value.dc_type!=DC_NUMBER){
135                 fprintf(stderr, "%s: non-numeric value\n", progname);
136                 return;
137         }
138         (void)dc_pop(&b);
139         (void)dc_pop(&a);
140         if ((*op)(a.v.number, b.v.number, kscale,
141                                                                 &r1.v.number, &r2.v.number) == DC_SUCCESS){
142                 r1.dc_type = DC_NUMBER;
143                 dc_push(r1);
144                 r2.dc_type = DC_NUMBER;
145                 dc_push(r2);
146                 dc_free_num(&a.v.number);
147                 dc_free_num(&b.v.number);
148         }else{
149                 /* op failed; restore the stack */
150                 dc_push(a);
151                 dc_push(b);
152         }
153 }
154
155 /* check that there are two numbers on top of the stack,
156  * then call dc_compare with the popped numbers.
157  * Return negative, zero, or positive based on the ordering
158  * of the two numbers.
159  */
160 int
161 dc_cmpop DC_DECLVOID()
162 {
163         int result;
164         dc_data a;
165         dc_data b;
166
167         if (!dc_stack || !dc_stack->link){
168                 Empty_Stack;
169                 return 0;
170         }
171         if (dc_stack->value.dc_type!=DC_NUMBER
172                         || dc_stack->link->value.dc_type!=DC_NUMBER){
173                 fprintf(stderr, "%s: non-numeric value\n", progname);
174                 return 0;
175         }
176         (void)dc_pop(&b);
177         (void)dc_pop(&a);
178         result = dc_compare(b.v.number, a.v.number);
179         dc_free_num(&a.v.number);
180         dc_free_num(&b.v.number);
181         return result;
182 }
183
184 /* check that there are three numbers on top of the stack,
185  * then call op with the popped numbers.  Construct a dc_data
186  * value from the dc_num returned by op and push it
187  * on the stack.
188  * If the op call doesn't return DC_SUCCESS, then leave the stack
189  * unmodified.
190  */
191 void
192 dc_triop DC_DECLARG((op, kscale))
193         int (*op)DC_PROTO((dc_num, dc_num, dc_num, int, dc_num *)) DC_DECLSEP
194         int kscale DC_DECLEND
195 {
196         dc_data a;
197         dc_data b;
198         dc_data c;
199         dc_data r;
200
201         if (!dc_stack || !dc_stack->link || !dc_stack->link->link){
202                 Empty_Stack;
203                 return;
204         }
205         if (dc_stack->value.dc_type!=DC_NUMBER
206                         || dc_stack->link->value.dc_type!=DC_NUMBER
207                         || dc_stack->link->link->value.dc_type!=DC_NUMBER){
208                 fprintf(stderr, "%s: non-numeric value\n", progname);
209                 return;
210         }
211         (void)dc_pop(&c);
212         (void)dc_pop(&b);
213         (void)dc_pop(&a);
214         if ((*op)(a.v.number, b.v.number, c.v.number,
215                                 kscale, &r.v.number) == DC_SUCCESS){
216                 r.dc_type = DC_NUMBER;
217                 dc_push(r);
218                 dc_free_num(&a.v.number);
219                 dc_free_num(&b.v.number);
220                 dc_free_num(&c.v.number);
221         }else{
222                 /* op failed; restore the stack */
223                 dc_push(a);
224                 dc_push(b);
225                 dc_push(c);
226         }
227 }
228
229 \f
230 /* initialize the register stacks to their initial values */
231 void
232 dc_register_init DC_DECLVOID()
233 {
234         int i;
235
236         for (i=0; i<DC_REGCOUNT; ++i)
237                 dc_register[i] = NULL;
238 }
239
240 /* clear the evaluation stack */
241 void
242 dc_clear_stack DC_DECLVOID()
243 {
244         dc_list *n;
245         dc_list *t;
246
247         for (n=dc_stack; n; n=t){
248                 t = n->link;
249                 if (n->value.dc_type == DC_NUMBER)
250                         dc_free_num(&n->value.v.number);
251                 else if (n->value.dc_type == DC_STRING)
252                         dc_free_str(&n->value.v.string);
253                 else
254                         dc_garbage("in stack", -1);
255                 dc_array_free(n->array);
256                 free(n);
257         }
258         dc_stack = NULL;
259 }
260
261 /* push a value onto the evaluation stack */
262 void
263 dc_push DC_DECLARG((value))
264         dc_data value DC_DECLEND
265 {
266         dc_list *n = dc_alloc();
267
268         if (value.dc_type!=DC_NUMBER && value.dc_type!=DC_STRING)
269                 dc_garbage("in data being pushed", -1);
270         n->value = value;
271         n->link = dc_stack;
272         dc_stack = n;
273 }
274
275 /* push a value onto the named register stack */
276 void
277 dc_register_push DC_DECLARG((stackid, value))
278         int stackid DC_DECLSEP
279         dc_data value DC_DECLEND
280 {
281         dc_list *n = dc_alloc();
282
283         stackid = regmap(stackid);
284         n->value = value;
285         n->link = dc_register[stackid];
286         dc_register[stackid] = n;
287 }
288
289 /* set *result to the value on the top of the evaluation stack */
290 /* The caller is responsible for duplicating the value if it
291  * is to be maintained as anything more than a transient identity.
292  *
293  * DC_FAIL is returned if the stack is empty (and *result unchanged),
294  * DC_SUCCESS is returned otherwise
295  */
296 int
297 dc_top_of_stack DC_DECLARG((result))
298         dc_data *result DC_DECLEND
299 {
300         if (!dc_stack){
301                 Empty_Stack;
302                 return DC_FAIL;
303         }
304         if (dc_stack->value.dc_type!=DC_NUMBER
305                         && dc_stack->value.dc_type!=DC_STRING)
306                 dc_garbage("at top of stack", -1);
307         *result = dc_stack->value;
308         return DC_SUCCESS;
309 }
310
311 /* set *result to a dup of the value on the top of the named register stack */
312 /*
313  * DC_FAIL is returned if the named stack is empty (and *result unchanged),
314  * DC_SUCCESS is returned otherwise
315  */
316 int
317 dc_register_get DC_DECLARG((regid, result))
318         int regid DC_DECLSEP
319         dc_data *result DC_DECLEND
320 {
321         dc_list *r;
322
323         regid = regmap(regid);
324         r = dc_register[regid];
325         if ( ! r ){
326                 fprintf(stderr, "%s: register ", progname);
327                 dc_show_id(stderr, regid, " is empty\n");
328                 return DC_FAIL;
329         }
330         *result = dc_dup(r->value);
331         return DC_SUCCESS;
332 }
333
334 /* set the top of the named register stack to the indicated value */
335 /* If the named stack is empty, craft a stack entry to enter the
336  * value into.
337  */
338 void
339 dc_register_set DC_DECLARG((regid, value))
340         int regid DC_DECLSEP
341         dc_data value DC_DECLEND
342 {
343         dc_list *r;
344
345         regid = regmap(regid);
346         r = dc_register[regid];
347         if ( ! r )
348                 dc_register[regid] = dc_alloc();
349         else if (r->value.dc_type == DC_NUMBER)
350                 dc_free_num(&r->value.v.number);
351         else if (r->value.dc_type == DC_STRING)
352                 dc_free_str(&r->value.v.string);
353         else if (r->value.dc_type == DC_UNINITIALIZED)
354                 ;
355         else
356                 dc_garbage("", regid);
357         dc_register[regid]->value = value;
358 }
359
360 /* pop from the evaluation stack
361  *
362  * DC_FAIL is returned if the stack is empty (and *result unchanged),
363  * DC_SUCCESS is returned otherwise
364  */
365 int
366 dc_pop DC_DECLARG((result))
367         dc_data *result DC_DECLEND
368 {
369         dc_list *r;
370
371         r = dc_stack;
372         if (!r){
373                 Empty_Stack;
374                 return DC_FAIL;
375         }
376         if (r->value.dc_type!=DC_NUMBER && r->value.dc_type!=DC_STRING)
377                 dc_garbage("at top of stack", -1);
378         *result = r->value;
379         dc_stack = r->link;
380         dc_array_free(r->array);
381         free(r);
382         return DC_SUCCESS;
383 }
384
385 /* pop from the named register stack
386  *
387  * DC_FAIL is returned if the named stack is empty (and *result unchanged),
388  * DC_SUCCESS is returned otherwise
389  */
390 int
391 dc_register_pop DC_DECLARG((stackid, result))
392         int stackid DC_DECLSEP
393         dc_data *result DC_DECLEND
394 {
395         dc_list *r;
396
397         stackid = regmap(stackid);
398         r = dc_register[stackid];
399         if (!r){
400                 fprintf(stderr, "%s: stack register ", progname);
401                 dc_show_id(stderr, stackid, " is empty\n");
402                 return DC_FAIL;
403         }
404         if (r->value.dc_type!=DC_NUMBER && r->value.dc_type!=DC_STRING)
405                 dc_garbage(" stack", stackid);
406         *result = r->value;
407         dc_register[stackid] = r->link;
408         dc_array_free(r->array);
409         free(r);
410         return DC_SUCCESS;
411 }
412
413 \f
414 /* tell how many entries are currently on the evaluation stack */
415 int
416 dc_tell_stackdepth DC_DECLVOID()
417 {
418         dc_list *n;
419         int depth=0;
420
421         for (n=dc_stack; n; n=n->link)
422                 ++depth;
423         return depth;
424 }
425
426
427 /* return the length of the indicated data value;
428  * if discard_p is DC_TOSS, the deallocate the value when done
429  *
430  * The definition of a datum's length is deligated to the
431  * appropriate module.
432  */
433 int
434 dc_tell_length DC_DECLARG((value, discard_p))
435         dc_data value DC_DECLSEP
436         dc_discard discard_p DC_DECLEND
437 {
438         int length;
439
440         if (value.dc_type == DC_NUMBER){
441                 length = dc_numlen(value.v.number);
442                 if (discard_p == DC_TOSS)
443                         dc_free_num(&value.v.number);
444         } else if (value.dc_type == DC_STRING) {
445                 length = dc_strlen(value.v.string);
446                 if (discard_p == DC_TOSS)
447                         dc_free_str(&value.v.string);
448         } else {
449                 dc_garbage("in tell_length", -1);
450                 /*NOTREACHED*/
451                 length = 0;     /*just to suppress spurious compiler warnings*/
452         }
453         return length;
454 }
455
456 \f
457
458 /* print out all of the values on the evaluation stack */
459 void
460 dc_printall DC_DECLARG((obase))
461         int obase DC_DECLEND
462 {
463         dc_list *n;
464
465         for (n=dc_stack; n; n=n->link)
466                 dc_print(n->value, obase, DC_WITHNL, DC_KEEP);
467 }
468
469 \f
470
471
472 /* get the current array head for the named array */
473 struct dc_array *
474 dc_get_stacked_array DC_DECLARG((array_id))
475         int array_id DC_DECLEND
476 {
477         dc_list *r = dc_register[regmap(array_id)];
478         return r ? r->array : NULL;
479 }
480
481 /* set the current array head for the named array */
482 void
483 dc_set_stacked_array DC_DECLARG((array_id, new_head))
484         int array_id DC_DECLSEP
485         struct dc_array *new_head DC_DECLEND
486 {
487         dc_list *r;
488
489         array_id = regmap(array_id);
490         r = dc_register[array_id];
491         if ( ! r )
492                 r = dc_register[array_id] = dc_alloc();
493         r->array = new_head;
494 }