/[cvs]/stack/stack.c
ViewVC logotype

Contents of /stack/stack.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.46 - (show annotations)
Thu Feb 7 03:56:39 2002 UTC (22 years, 2 months ago) by masse
Branch: MAIN
Changes since 1.45: +45 -38 lines
File MIME type: text/plain
Cosmetic changes.
(eval) Added possibility to eval strings.
(environment) Now contains non_eval_flag.

1 /* printf */
2 #include <stdio.h>
3 /* EXIT_SUCCESS */
4 #include <stdlib.h>
5 /* NULL */
6 #include <stddef.h>
7 /* dlopen, dlsym, dlerror */
8 #include <dlfcn.h>
9 /* assert */
10 #include <assert.h>
11
12 #define HASHTBLSIZE 65536
13
14 /* First, define some types. */
15
16 /* A value of some type */
17 typedef struct {
18 enum {
19 integer,
20 string,
21 func, /* Function pointer */
22 symb,
23 list
24 } type; /* Type of stack element */
25
26 union {
27 void *ptr; /* Pointer to the content */
28 int val; /* ...or an integer */
29 } content; /* Stores a pointer or an integer */
30
31 int refcount; /* Reference counter */
32
33 } value;
34
35 /* A symbol with a name and possible value */
36 /* (These do not need reference counters, they are kept unique by
37 hashing.) */
38 typedef struct symbol_struct {
39 char *id; /* Symbol name */
40 value *val; /* The value (if any) bound to it */
41 struct symbol_struct *next; /* In case of hashing conflicts, a */
42 } symbol; /* symbol is a kind of stack item. */
43
44 /* A type for a hash table for symbols */
45 typedef symbol *hashtbl[HASHTBLSIZE]; /* Hash table declaration */
46
47 /* An item (value) on a stack */
48 typedef struct stackitem_struct
49 {
50 value *item; /* The value on the stack */
51 struct stackitem_struct *next; /* Next item */
52 } stackitem;
53
54 /* An environment; gives access to the stack and a hash table of
55 defined symbols */
56 typedef struct {
57 stackitem *head; /* Head of the stack */
58 hashtbl symbols; /* Hash table of all variable bindings */
59 int err; /* Error flag */
60 int non_eval_flag;
61 } environment;
62
63 /* A type for pointers to external functions */
64 typedef void (*funcp)(environment *); /* funcp is a pointer to a void
65 function (environment *) */
66
67 /* Initialize a newly created environment */
68 void init_env(environment *env)
69 {
70 int i;
71
72 env->err= 0;
73 env->non_eval_flag= 0;
74 for(i= 0; i<HASHTBLSIZE; i++)
75 env->symbols[i]= NULL;
76 }
77
78 /* Returns a pointer to a pointer to an element in the hash table. */
79 symbol **hash(hashtbl in_hashtbl, const char *in_string)
80 {
81 int i= 0;
82 unsigned int out_hash= 0;
83 char key= '\0';
84 symbol **position;
85
86 while(1){ /* Hash in_string */
87 key= in_string[i++];
88 if(key=='\0')
89 break;
90 out_hash= out_hash*32+key;
91 }
92
93 out_hash= out_hash%HASHTBLSIZE;
94 position= &(in_hashtbl[out_hash]);
95
96 while(1){
97 if(*position==NULL) /* If empty */
98 return position;
99
100 if(strcmp(in_string, (*position)->id)==0) /* If match */
101 return position;
102
103 position= &((*position)->next); /* Try next */
104 }
105 }
106
107 /* Generic push function. */
108 void push(stackitem** stack_head, stackitem* in_item)
109 {
110 in_item->next= *stack_head;
111 *stack_head= in_item;
112 }
113
114 /* Push a value onto the stack */
115 void push_val(stackitem **stack_head, value *val)
116 {
117 stackitem *new_item= malloc(sizeof(stackitem));
118 new_item->item= val;
119 val->refcount++;
120 push(stack_head, new_item);
121 }
122
123 /* Push an integer onto the stack. */
124 void push_int(stackitem **stack_head, int in_val)
125 {
126 value *new_value= malloc(sizeof(value));
127 stackitem *new_item= malloc(sizeof(stackitem));
128 new_item->item= new_value;
129
130 new_value->content.val= in_val;
131 new_value->type= integer;
132 new_value->refcount=1;
133
134 push(stack_head, new_item);
135 }
136
137 /* Copy a string onto the stack. */
138 void push_cstring(stackitem **stack_head, const char *in_string)
139 {
140 value *new_value= malloc(sizeof(value));
141 stackitem *new_item= malloc(sizeof(stackitem));
142 new_item->item=new_value;
143
144 new_value->content.ptr= malloc(strlen(in_string)+1);
145 strcpy(new_value->content.ptr, in_string);
146 new_value->type= string;
147 new_value->refcount=1;
148
149 push(stack_head, new_item);
150 }
151
152 /* Push a symbol onto the stack. */
153 void push_sym(environment *env, const char *in_string)
154 {
155 stackitem *new_item; /* The new stack item */
156 /* ...which will contain... */
157 value *new_value; /* A new symbol value */
158 /* ...which might point to... */
159 symbol **new_symbol; /* (if needed) A new actual symbol */
160 /* ...which, if possible, will be bound to... */
161 value *new_fvalue; /* (if needed) A new function value */
162 /* ...which will point to... */
163 void *funcptr; /* A function pointer */
164
165 static void *handle= NULL; /* Dynamic linker handle */
166
167 /* Create a new stack item containing a new value */
168 new_item= malloc(sizeof(stackitem));
169 new_value= malloc(sizeof(value));
170 new_item->item=new_value;
171
172 /* The new value is a symbol */
173 new_value->type= symb;
174 new_value->refcount= 1;
175
176 /* Look up the symbol name in the hash table */
177 new_symbol= hash(env->symbols, in_string);
178 new_value->content.ptr= *new_symbol;
179
180 if(*new_symbol==NULL) { /* If symbol was undefined */
181
182 /* Create a new symbol */
183 (*new_symbol)= malloc(sizeof(symbol));
184 (*new_symbol)->val= NULL; /* undefined value */
185 (*new_symbol)->next= NULL;
186 (*new_symbol)->id= malloc(strlen(in_string)+1);
187 strcpy((*new_symbol)->id, in_string);
188
189 /* Intern the new symbol in the hash table */
190 new_value->content.ptr= *new_symbol;
191
192 /* Try to load the symbol name as an external function, to see if
193 we should bind the symbol to a new function pointer value */
194 if(handle==NULL) /* If no handle */
195 handle= dlopen(NULL, RTLD_LAZY);
196
197 funcptr= dlsym(handle, in_string); /* Get function pointer */
198 if(dlerror()==NULL) { /* If a function was found */
199 new_fvalue= malloc(sizeof(value)); /* Create a new value */
200 new_fvalue->type=func; /* The new value is a function pointer */
201 new_fvalue->content.ptr=funcptr; /* Store function pointer */
202 (*new_symbol)->val= new_fvalue; /* Bind the symbol to the new
203 function value */
204 new_fvalue->refcount= 1;
205 }
206 }
207 push(&(env->head), new_item);
208 }
209
210 void printerr(const char* in_string) {
211 fprintf(stderr, "Err: %s\n", in_string);
212 }
213
214 /* Throw away a value */
215 void free_val(value *val){
216 stackitem *item, *temp;
217
218 val->refcount--; /* Decrease the reference count */
219 if(val->refcount == 0){
220 switch (val->type){ /* and free the contents if necessary */
221 case string:
222 free(val->content.ptr);
223 break;
224 case list: /* lists needs to be freed recursively */
225 item=val->content.ptr;
226 while(item != NULL) { /* for all stack items */
227 free_val(item->item); /* free the value */
228 temp=item->next; /* save next ptr */
229 free(item); /* free the stackitem */
230 item=temp; /* go to next stackitem */
231 }
232 free(val); /* Free the actual list value */
233 break;
234 default:
235 break;
236 }
237 }
238 }
239
240 /* Discard the top element of the stack. */
241 extern void toss(environment *env)
242 {
243 stackitem *temp= env->head;
244
245 if((env->head)==NULL) {
246 printerr("Too Few Arguments");
247 env->err=1;
248 return;
249 }
250
251 free_val(env->head->item); /* Free the value */
252 env->head= env->head->next; /* Remove the top stack item */
253 free(temp); /* Free the old top stack item */
254 }
255
256 /* Print newline. */
257 extern void nl()
258 {
259 printf("\n");
260 }
261
262 /* Gets the type of a value */
263 extern void type(environment *env){
264 int typenum;
265
266 if((env->head)==NULL) {
267 printerr("Too Few Arguments");
268 env->err=1;
269 return;
270 }
271 typenum=env->head->item->type;
272 toss(env);
273 switch(typenum){
274 case integer:
275 push_sym(env, "integer");
276 break;
277 case string:
278 push_sym(env, "string");
279 break;
280 case symb:
281 push_sym(env, "symbol");
282 break;
283 case func:
284 push_sym(env, "function");
285 break;
286 case list:
287 push_sym(env, "list");
288 break;
289 default:
290 push_sym(env, "unknown");
291 break;
292 }
293 }
294
295 /* Prints the top element of the stack. */
296 void print_h(stackitem *stack_head)
297 {
298 switch(stack_head->item->type) {
299 case integer:
300 printf("%d", stack_head->item->content.val);
301 break;
302 case string:
303 printf("\"%s\"", (char*)stack_head->item->content.ptr);
304 break;
305 case symb:
306 printf("%s", ((symbol *)(stack_head->item->content.ptr))->id);
307 break;
308 case func:
309 printf("#<function %p>", (funcp)(stack_head->item->content.ptr));
310 break;
311 case list:
312 /* A list is just a stack, so make stack_head point to it */
313 stack_head=(stackitem *)(stack_head->item->content.ptr);
314 printf("[ ");
315 while(stack_head != NULL) {
316 print_h(stack_head);
317 printf(" ");
318 stack_head=stack_head->next;
319 }
320 printf("]");
321 break;
322 default:
323 printf("#<unknown %p>", (stack_head->item->content.ptr));
324 break;
325 }
326 }
327
328 extern void print_(environment *env) {
329 if(env->head==NULL) {
330 printerr("Too Few Arguments");
331 env->err=1;
332 return;
333 }
334 print_h(env->head);
335 }
336
337 /* Prints the top element of the stack and then discards it. */
338 extern void print(environment *env)
339 {
340 print_(env);
341 if(env->err) return;
342 toss(env);
343 }
344
345 /* Only to be called by function printstack. */
346 void print_st(stackitem *stack_head, long counter)
347 {
348 if(stack_head->next != NULL)
349 print_st(stack_head->next, counter+1);
350 printf("%ld: ", counter);
351 print_h(stack_head);
352 nl();
353 }
354
355 /* Prints the stack. */
356 extern void printstack(environment *env)
357 {
358 if(env->head == NULL) {
359 return;
360 }
361 print_st(env->head, 1);
362 nl();
363 }
364
365 /* Swap the two top elements on the stack. */
366 extern void swap(environment *env)
367 {
368 stackitem *temp= env->head;
369
370 if(env->head==NULL || env->head->next==NULL) {
371 printerr("Too Few Arguments");
372 env->err=1;
373 return;
374 }
375
376 env->head= env->head->next;
377 temp->next= env->head->next;
378 env->head->next= temp;
379 }
380
381 /* Recall a value from a symbol, if bound */
382 extern void rcl(environment *env)
383 {
384 value *val;
385
386 if(env->head == NULL) {
387 printerr("Too Few Arguments");
388 env->err=1;
389 return;
390 }
391
392 if(env->head->item->type!=symb) {
393 printerr("Bad Argument Type");
394 env->err=2;
395 return;
396 }
397
398 val=((symbol *)(env->head->item->content.ptr))->val;
399 if(val == NULL){
400 printerr("Unbound Variable");
401 env->err=3;
402 return;
403 }
404 toss(env); /* toss the symbol */
405 if(env->err) return;
406 push_val(&(env->head), val); /* Return its bound value */
407 }
408
409 extern void pack(environment*);
410 void stack_read(environment*, char*);
411
412
413 /* If the top element is a symbol, determine if it's bound to a
414 function value, and if it is, toss the symbol and execute the
415 function. */
416 extern void eval(environment *env)
417 {
418 funcp in_func;
419 value* temp_val;
420 stackitem* iterator;
421
422 if(env->head==NULL) {
423 printerr("Too Few Arguments");
424 env->err=1;
425 return;
426 }
427
428 switch(env->head->item->type) {
429 /* if it's a symbol */
430 case symb:
431 rcl(env); /* get its contents */
432 if(env->err) return;
433 if(env->head->item->type!=symb){ /* don't recurse symbols */
434 eval(env); /* evaluate the value */
435 return;
436 }
437 break;
438
439 /* If it's a lone function value, run it */
440 case func:
441 in_func= (funcp)(env->head->item->content.ptr);
442 toss(env);
443 if(env->err) return;
444 (*in_func)(env);
445 break;
446
447 /* If it's a list */
448 case list:
449 temp_val= env->head->item;
450 env->head->item->refcount++;
451 toss(env);
452 if(env->err) return;
453 iterator= (stackitem*)temp_val->content.ptr;
454 while(iterator!=NULL && iterator->item!=NULL) {
455 push_val(&(env->head), iterator->item);
456 if(env->head->item->type==symb
457 && strcmp(";", ((symbol*)(env->head->item->content.ptr))->id)==0) {
458 toss(env);
459 if(env->err) return;
460 eval(env);
461 if(env->err) return;
462 }
463 iterator= iterator->next;
464 }
465 free_val(temp_val);
466 break;
467
468 /* If it's a string */
469 case string:
470 temp_val= env->head->item;
471 env->head->item->refcount++;
472 toss(env);
473 if(env->err) return;
474 push_sym(env, "[");
475 env->non_eval_flag++;
476 stack_read(env, (char*)temp_val->content.ptr);
477 env->non_eval_flag--;
478 push_sym(env, "["); pack(env);
479 if(env->err) return;
480 eval(env);
481 if(env->err) return;
482 free_val(temp_val);
483 break;
484
485 default:
486 }
487 }
488
489 /* Reverse (flip) a list */
490 extern void rev(environment *env){
491 stackitem *old_head, *new_head, *item;
492
493 if((env->head)==NULL) {
494 printerr("Too Few Arguments");
495 env->err=1;
496 return;
497 }
498
499 if(env->head->item->type!=list) {
500 printerr("Bad Argument Type");
501 env->err=2;
502 return;
503 }
504
505 old_head=(stackitem *)(env->head->item->content.ptr);
506 new_head=NULL;
507 while(old_head != NULL){
508 item=old_head;
509 old_head=old_head->next;
510 item->next=new_head;
511 new_head=item;
512 }
513 env->head->item->content.ptr=new_head;
514 }
515
516 /* Make a list. */
517 extern void pack(environment *env)
518 {
519 void* delimiter;
520 stackitem *iterator, *temp;
521 value *pack;
522
523 delimiter= env->head->item->content.ptr; /* Get delimiter */
524 toss(env);
525
526 iterator= env->head;
527
528 if(iterator==NULL || iterator->item->content.ptr==delimiter) {
529 temp= NULL;
530 toss(env);
531 } else {
532 /* Search for first delimiter */
533 while(iterator->next!=NULL
534 && iterator->next->item->content.ptr!=delimiter)
535 iterator= iterator->next;
536
537 /* Extract list */
538 temp= env->head;
539 env->head= iterator->next;
540 iterator->next= NULL;
541
542 if(env->head!=NULL)
543 toss(env);
544 }
545
546 /* Push list */
547 pack= malloc(sizeof(value));
548 pack->type= list;
549 pack->content.ptr= temp;
550 pack->refcount= 1;
551
552 temp= malloc(sizeof(stackitem));
553 temp->item= pack;
554
555 push(&(env->head), temp);
556 rev(env);
557 }
558
559 /* Parse input. */
560 void stack_read(environment *env, char *in_line)
561 {
562 char *temp, *rest;
563 int itemp;
564 size_t inlength= strlen(in_line)+1;
565 int convert= 0;
566
567 temp= malloc(inlength);
568 rest= malloc(inlength);
569
570 do {
571 /* If comment */
572 if((convert= sscanf(in_line, "#%[^\n\r]", rest))) {
573 free(temp); free(rest);
574 return;
575 }
576
577 /* If string */
578 if((convert= sscanf(in_line, "\"%[^\"\n\r]\" %[^\n\r]", temp, rest))) {
579 push_cstring(&(env->head), temp);
580 break;
581 }
582 /* If integer */
583 if((convert= sscanf(in_line, "%d %[^\n\r]", &itemp, rest))) {
584 push_int(&(env->head), itemp);
585 break;
586 }
587 /* Escape ';' with '\' */
588 if((convert= sscanf(in_line, "\\%c%[^\n\r]", temp, rest))) {
589 temp[1]= '\0';
590 push_sym(env, temp);
591 break;
592 }
593 /* If symbol */
594 if((convert= sscanf(in_line, "%[^][ ;\n\r]%[^\n\r]", temp, rest))) {
595 push_sym(env, temp);
596 break;
597 }
598 /* If single char */
599 if((convert= sscanf(in_line, "%c%[^\n\r]", temp, rest))) {
600 if(*temp==';') {
601 if(!env->non_eval_flag) {
602 eval(env); /* Evaluate top element */
603 break;
604 }
605
606 push_sym(env, ";");
607 break;
608 }
609
610 if(*temp==']') {
611 push_sym(env, "[");
612 pack(env);
613 if(env->non_eval_flag)
614 env->non_eval_flag--;
615 break;
616 }
617
618 if(*temp=='[') {
619 push_sym(env, "[");
620 env->non_eval_flag++;
621 break;
622 }
623 }
624 } while(0);
625
626 free(temp);
627
628 if(convert<2) {
629 free(rest);
630 return;
631 }
632
633 stack_read(env, rest);
634
635 free(rest);
636 }
637
638 /* Relocate elements of the list on the stack. */
639 extern void expand(environment *env)
640 {
641 stackitem *temp, *new_head;
642
643 /* Is top element a list? */
644 if(env->head==NULL) {
645 printerr("Too Few Arguments");
646 env->err=1;
647 return;
648 }
649 if(env->head->item->type!=list) {
650 printerr("Bad Argument Type");
651 env->err=2;
652 return;
653 }
654
655 rev(env);
656
657 if(env->err)
658 return;
659
660 /* The first list element is the new stack head */
661 new_head= temp= env->head->item->content.ptr;
662
663 env->head->item->refcount++;
664 toss(env);
665
666 /* Find the end of the list */
667 while(temp->next!=NULL)
668 temp= temp->next;
669
670 /* Connect the tail of the list with the old stack head */
671 temp->next= env->head;
672 env->head= new_head; /* ...and voila! */
673
674 }
675
676 /* Compares two elements by reference. */
677 extern void eq(environment *env)
678 {
679 void *left, *right;
680 int result;
681
682 if((env->head)==NULL || env->head->next==NULL) {
683 printerr("Too Few Arguments");
684 env->err=1;
685 return;
686 }
687
688 left= env->head->item->content.ptr;
689 swap(env);
690 right= env->head->item->content.ptr;
691 result= (left==right);
692
693 toss(env); toss(env);
694 push_int(&(env->head), result);
695 }
696
697 /* Negates the top element on the stack. */
698 extern void not(environment *env)
699 {
700 int val;
701
702 if((env->head)==NULL) {
703 printerr("Too Few Arguments");
704 env->err=1;
705 return;
706 }
707
708 if(env->head->item->type!=integer) {
709 printerr("Bad Argument Type");
710 env->err=2;
711 return;
712 }
713
714 val= env->head->item->content.val;
715 toss(env);
716 push_int(&(env->head), !val);
717 }
718
719 /* Compares the two top elements on the stack and return 0 if they're the
720 same. */
721 extern void neq(environment *env)
722 {
723 eq(env);
724 not(env);
725 }
726
727 /* Give a symbol some content. */
728 extern void def(environment *env)
729 {
730 symbol *sym;
731
732 /* Needs two values on the stack, the top one must be a symbol */
733 if(env->head==NULL || env->head->next==NULL) {
734 printerr("Too Few Arguments");
735 env->err=1;
736 return;
737 }
738
739 if(env->head->item->type!=symb) {
740 printerr("Bad Argument Type");
741 env->err=2;
742 return;
743 }
744
745 /* long names are a pain */
746 sym=env->head->item->content.ptr;
747
748 /* if the symbol was bound to something else, throw it away */
749 if(sym->val != NULL)
750 free_val(sym->val);
751
752 /* Bind the symbol to the value */
753 sym->val= env->head->next->item;
754 sym->val->refcount++; /* Increase the reference counter */
755
756 toss(env); toss(env);
757 }
758
759 /* Quit stack. */
760 extern void quit(environment *env)
761 {
762 exit(EXIT_SUCCESS);
763 }
764
765 /* Clear stack */
766 extern void clear(environment *env)
767 {
768 while(env->head!=NULL)
769 toss(env);
770 }
771
772 /* List all defined words */
773 extern void words(environment *env)
774 {
775 symbol *temp;
776 int i;
777
778 for(i= 0; i<HASHTBLSIZE; i++) {
779 temp= env->symbols[i];
780 while(temp!=NULL) {
781 printf("%s\n", temp->id);
782 temp= temp->next;
783 }
784 }
785 }
786
787 /* Forgets a symbol (remove it from the hash table) */
788 extern void forget(environment *env)
789 {
790 char* sym_id;
791 stackitem *stack_head= env->head;
792 symbol **hash_entry, *temp;
793
794 if(stack_head==NULL) {
795 printerr("Too Few Arguments");
796 env->err=1;
797 return;
798 }
799
800 if(stack_head->item->type!=symb) {
801 printerr("Bad Argument Type");
802 env->err=2;
803 return;
804 }
805
806 sym_id= ((symbol*)(stack_head->item->content.ptr))->id;
807 toss(env);
808
809 hash_entry= hash(env->symbols, sym_id);
810 temp= *hash_entry;
811 *hash_entry= (*hash_entry)->next;
812
813 if(temp->val!=NULL) {
814 free_val(temp->val);
815 }
816 free(temp->id);
817 free(temp);
818 }
819
820 /* Returns the current error number to the stack */
821 extern void errn(environment *env){
822 push_int(&(env->head), env->err);
823 }
824
825 int main()
826 {
827 environment myenv;
828 char in_string[100];
829
830 init_env(&myenv);
831
832 printf("okidok\n ");
833
834 while(fgets(in_string, 100, stdin) != NULL) {
835 stack_read(&myenv, in_string);
836 if(myenv.err) {
837 printf("(error %d) ", myenv.err);
838 myenv.err=0;
839 }
840 printf("okidok\n ");
841 }
842 quit(&myenv);
843 return EXIT_FAILURE;
844 }

root@recompile.se
ViewVC Help
Powered by ViewVC 1.1.26