#include <stdio.h>

typedef long PTR;

#define number_tag	0
#define immed_tag	1
#define pair_tag	2
#define string_tag	3
#define symbol_tag	4
#define vector_tag	5
#define proc_tag	6

#define mask		7

#define tag_len		3

#define bool_tag	0x01 
#define null_tag	0x09
#define char_tag	0x11

#define imm_tag_len	8

#define imm_mask	0xFF

#define TAG(x) ((x) & mask)
#define UNTAG(x) ((x) & (~mask))
#define IMMTAG(x) ((x) & imm_mask)

#define STRINGLENGTH(x) (*((PTR *)UNTAG(x)))
#define STRINGDATA(x) ((char *)UNTAG(x) + sizeof(PTR))
#define CAR(x) (*((PTR *)UNTAG(x)))
#define CDR(x) (*((PTR *)UNTAG(x) + 1))
#define SYMBOLNAME(x) (*((PTR *)UNTAG(x)))
#define VECTORLENGTH(x) (*((PTR *)UNTAG(x)))
#define VECTORDATA(x) ((PTR *)UNTAG(x) + 1)

#define default_heap_size 10000
#define default_stack_size 10000

extern PTR call_scheme ();

usage_error(who) char *who; {
    extern void exit();

    (void) fprintf(stderr, "usage: %s [-h <heap size>] \n",
                           who);
    (void) fprintf(stderr, "    specify sizes in words (base 10)\n");
    (void) exit(1);
}

main(argc, argv) int argc; char *argv[]; {
    unsigned heap_size = default_heap_size;
    unsigned stack_size = default_stack_size;
    int n;
    void *malloc();

    for (n = 1; n < argc; n++)
        if (*argv[n] == '-')
            switch (*(argv[n]+1)) {
                case 'h': /* heap size option */
                    argv[n] = (char *)NULL;
                    if (++n == argc) usage_error(argv[0]);
                    heap_size = atoi(argv[n]);
                    break;
		default:
                    usage_error(argv[0]);
            }
        else
            usage_error(argv[0]);

    print(call_scheme((PTR)malloc(4*stack_size),(PTR)malloc(4*heap_size)));
    
    (void) printf("\n");
    return 0;
}

print(x) PTR x; {
  switch (TAG(x)) {
  case number_tag: {
    (void) printf("%ld", x/(mask+1)); break; }
  case immed_tag: {
    switch (IMMTAG(x)) {
    case bool_tag:
      (void) printf((x>>imm_tag_len) ? "#t" : "#f"); break;
    case null_tag:
      (void) printf("()"); break;
    case char_tag:
      switch (x>>imm_tag_len) {
      case '\n':
	(void) printf("#\\newline"); break;
      case ' ':
	(void) printf("#\\space"); break;
      case 9:
	(void) printf("#\\tab"); break;
      default: 
	(void) printf("#\\%c", x>>imm_tag_len); break;
      } 
      break;
    }
    break; 
  }
  case pair_tag:  {
    (void) printf("(");
    print(CAR(x));
    x = CDR(x);
    while (TAG(x) == pair_tag) {
      (void) printf(" ");
      print(CAR(x));
      x = CDR(x);
    }
    if (IMMTAG(x) != null_tag) {
      (void) printf(" . ");
      print(x);
    }
    (void) printf(")");
    break;
  }
  case string_tag: {
    int n;
    char *s;
    n = STRINGLENGTH(x);
    s = STRINGDATA(x);
    (void) printf("\"");
    while (n--) {
      if (*s == '"' || *s == '\\') (void) printf("\\");
      (void) printf("%c", *s++);
    }
    (void) printf("\"");
    break;
  }
  case symbol_tag: {
    int n;
    char *s;
    n = STRINGLENGTH(SYMBOLNAME(x));
    s = STRINGDATA(SYMBOLNAME(x));
    (void) printf("|");			/* changed */
    while (n--) {
      if (*s == '"' || *s == '\\') (void) printf("\\");
      (void) printf("%c", *s++);
    }
    (void) printf("|");
    break;
  }
  case vector_tag: {
    int n;
    PTR *p;
    (void) printf("#(");
    n = VECTORLENGTH(x);
    p = VECTORDATA(x);
    if (n != 0) {
      print(*p);
      while (--n) {
	(void) printf(" ");
	print(*++p);
      }
    }
    (void) printf(")");
    break;
  }
  case proc_tag: {
    (void) printf("<procedure>", x);	/* changed */
    break;
  }
  default: {
    (void) printf("#<garbage %x>", x);
    break;
  }
  }
}
