#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "types.h"


/* Define the built-in types. 
   (Note that union initializers always set first element of union.) */
struct TyperepS _void_type = {Base,{"VOID"}};
Typerep void_type = &(_void_type);
struct TyperepS _int_type = {Base,{"INTEGER"}};
Typerep int_type = &(_int_type);
struct TyperepS _bool_type = {Base,{"BOOLEAN"}};
Typerep bool_type = &(_bool_type);
struct TyperepS _string_type = {Base,{"STRING"}};
Typerep string_type = &(_string_type);
struct TyperepS _real_type = {Base,{"REAL"}};
Typerep real_type = &(_real_type);
struct TyperepS _nil_type = {Base,{"RECORD"}};
Typerep nil_type = &(_nil_type);

/* type - allocate a type structure for type operator op */
Typerep mk_BaseTyperep(char *name) {
  Typerep p = (Typerep) malloc(sizeof *p);
  assert(p);
  p->kind = Base;
  p->u.base.name = name;
  return p;
}

Typerep mk_ArrayTyperep(Typerep etype) {
  Typerep p = (Typerep) malloc(sizeof *p);
  assert(p);
  p->kind = Array;
  p->u.array.etype = etype;
  return p;
}


Typerep mk_ProcTyperep(Typerep rtype,Product args) {
  Typerep p = (Typerep) malloc(sizeof *p);
  assert(p);
  p->kind = Procedure;
  p->u.proc.rtype = rtype;
  p->u.proc.args = args;
  return p;
}

Typerep mk_RecordTyperep(Product fields) {
  Typerep p = (Typerep) malloc(sizeof *p);
  assert(p);
  p->kind = Record;
  p->u.record.fields = fields;
  return p;
}

/* product - allocate an unlinked  product structure for name x type */
Product mk_Product(char *name, Typerep type, Product link) {
  Product p = (Product) malloc(sizeof *p);
  assert(p);
  p->name = name;
  p->type = type;
  p->link = link;
  return p;
}

/* printtype - print type on fp */
void printtype(Typerep type, FILE *fp) {
  assert(type);
  switch (type->kind) {
  case Base: 
    fprintf(fp, "%s", type->u.base.name);
    break;
  case Array:
    fprintf(fp, "ARRAY OF ");
    printtype(type->u.array.etype, fp);
    break;
  case Record:
    {Product p;
     fprintf(fp, "RECORD ");
     for (p = type->u.record.fields; p; p = p->link) {
       fprintf(fp,"%s:",p->name);
       fprintf(fp,":");
       printtype(p->type,fp);
       fprintf(fp,"; ");
     };
     fprintf(fp, "END");
     break;}
  case Procedure:
    fprintf(fp, "PROCEDURE");
    if (type->u.proc.args) {
      Product p = type->u.proc.args;
      fprintf(fp,"(%s: ",p->name);
      printtype(p->type, fp);
      while (p = p->link) {
	fprintf(fp, "; %s: ",p->name);
	printtype(p->type, fp);
      }
      fprintf(fp, ")");
    } else 
      fprintf(fp, "()");
    fprintf(fp, ": ");
    printtype(type->u.proc.rtype, fp);
    break;
  default: assert(0); /* invalid kind */
  }
}
