[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / CONTRIB / pphs / pphs.c
diff --git a/ghc/CONTRIB/pphs/pphs.c b/ghc/CONTRIB/pphs/pphs.c
new file mode 100644 (file)
index 0000000..aa31a3e
--- /dev/null
@@ -0,0 +1,1030 @@
+                               /* pphs - a pretty printer for Haskell code */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define MAXLINELENGTH 256
+
+enum face {KW, ID, IS, SU, ST, CO, NU, MA, SP, LC, RC, CR, BF, FQ, EQ, DQ, QD, EE, DC, DP, CP, LE, GE, LA, RA, RR, TI, BE};
+                               /* Possible values of typeface */
+
+int widecolons = 0;            /* User may want space between double colons */
+int subscripts = 0;            /* User may want subscripts after '_' in identifiers */
+int tablength = 8;             /* User's input file tablength */
+
+typedef struct ElementType_Tag { /* Basic storage unit */
+  char chars[MAXLINELENGTH];   /* Characters */
+  enum face typeface[MAXLINELENGTH]; /* Typefaces */
+  int indentation, length, col;        /* Indentation level, non-empty length, column level */
+} ElementType;
+
+typedef struct StackNodeType_Tag *Link; /* Stack-related types */
+typedef struct StackNodeType_Tag {
+  ElementType Element;         /* Stack item */
+  Link Next;                   /* Link to next node */
+} StackNodeType;
+typedef StackNodeType *StackNodePtr;
+typedef StackNodePtr StackType;
+
+typedef int QueueSizeType;     /* Queue-related types */
+typedef struct QueueNodeType_Tag *Connection;
+typedef struct QueueNodeType_Tag {
+  ElementType Element;         /* Queue item */
+  Connection Next;             /* Link to next node */
+} QueueNodeType;
+typedef QueueNodeType *QueueNodePtr;
+typedef struct QueueType_Tag {
+  QueueNodePtr Front, Rear;
+  QueueSizeType Length;
+} QueueType;
+
+FILE *ifptr;                   /* input file pointer */
+
+                               /* * * STACK FUNCTIONS * * */
+StackType
+  CreateStack()                        /* Returns an empty stack */
+{
+  return(NULL);
+}
+
+int
+  IsEmptyStack(s)              /* Returns 1 if s is empty, 0 otherwise */
+StackType s;
+{
+  return(s == NULL);
+}
+
+StackType
+  Push(s, x)                   /* Returns stack with x pushed onto s */
+StackType s;
+ElementType x;
+{
+  StackType p;
+
+  p = (StackNodeType *) malloc(sizeof(StackNodeType));
+  if (p == NULL) {
+    fprintf(stderr, "pphs: Stack is too big\n");
+    exit(3);
+  }
+  else {
+    (*p).Element = x;
+    (*p).Next = s;
+    return(p);
+  }
+}
+
+ElementType
+  Top(s)                       /* Returns value of top element in s */
+StackType s;
+{
+  return((*s).Element);
+}
+
+StackType
+  Pop(s)                       /* Returns stack with top element of s popped off */
+StackType s;
+{
+  StackType t;
+
+  t = (*s).Next;
+  free(s);
+  return(t);
+}
+
+StackType
+  PopSym(s)          /* Returns stack with top element of s popped off without freeing */
+StackType s;
+{
+  StackType t;
+
+  t = (*s).Next;
+/* free(s); As PopSym is called within a function, free would free space needed later */
+  return(t);
+}
+                               /* * * QUEUE FUNCTIONS * * */
+QueueType
+  CreateQueue()                        /* Returns an empty queue */
+{
+  QueueType q;
+
+  q.Front = NULL;
+  q.Rear = NULL;
+  q.Length = 0;
+  return(q);
+}
+
+int
+  IsEmptyQueue(q)              /* Returns 1 if q is empty, 0 otherwise */
+QueueType q;
+{
+  return(q.Front == NULL);
+}
+
+int
+  LengthOfQueue(q)             /* Returns length of q */
+QueueType q;
+{
+  return(q.Length);
+}
+
+QueueNodePtr
+  FrontOfQueue(q)              /* Returns pointer to front of q */
+QueueType q;
+{
+  return(q.Front);
+}
+
+QueueNodePtr
+  RearOfQueue(q)               /* Returns pointer to rear of q */
+QueueType q;
+{
+  return(q.Rear);
+}
+
+QueueType
+  AddToQueue(q, x)             /* Adds item x to rear of queue q */
+QueueType q;
+ElementType x;
+{
+  QueueNodePtr p;
+
+  p = (QueueNodeType *) malloc(sizeof(QueueNodeType));
+  if (p == NULL) {
+    fprintf(stderr, "pphs: Queue is too big\n");
+    exit(4);
+  }
+  else {
+    (*p).Element = x;
+    (*p).Next = NULL;
+    if (q.Front == NULL)
+      q.Front = p;
+    else
+      (*(q.Rear)).Next = p;
+    q.Rear = p;
+    q.Length++;
+    return(q);
+  }
+}
+
+QueueType
+  TakeFromQueue(q)             /* Removes front item from queue */
+QueueType q;
+{
+  QueueNodePtr p;
+
+  if (q.Front == NULL) {
+    fprintf(stderr, "pphs: Stack underflow\n");
+    exit(5);
+  }
+  else {
+    p = q.Front;
+    q.Front = (*(q.Front)).Next;
+    if (q.Front == NULL)
+      q.Rear = NULL;
+    q.Length--;
+    free(p);
+    return(q);
+  }
+}
+                               /* * * TYPEFACE FUNCTIONS * * */
+int
+  IsMathsChar(c)               /* Returns 1 if c is a character to be in maths */
+char c;
+{
+  return((c == '[') || (c == ']') || (c == '/') || (c == ',') || (c == '!')
+        || (c == ':') || (c == ';') || (c == '(') || (c == ')') || (c == '&')
+        || (c == '#') || (c == '+') || (c == '-') || (c == '<') || (c == '>')
+        || (c == '{') || (c == '}') || (c == '=') || (c == '|') || (c == '\'')
+        || (c == '^'));         
+}
+
+ElementType
+  ChangeTypeface(store, length, finish, tf) /* Changes the typeface to tf in store
+                                              for length until finish */
+ElementType store;
+int length, finish;
+enum face tf;
+{
+  int counter;
+
+  for (counter = (finish - length); counter < finish; counter++)
+    store.typeface[counter] = tf;
+  return(store);
+}
+
+ElementType
+  CheckForDoubleChar(store, position) /* Checks for double character
+                                        in store.chars[position - 2..position - 1],
+                                        if found alters typeface */
+ElementType store;
+int position;
+{
+  if ((position >= 2) && (store.typeface[position - 2] != DC)) {
+    if ((store.chars[position - 2] == '-') && (store.chars[position - 1] == '-')) {
+      store.typeface[position - 2] = LC; /* Haskell "--" line comment */
+      store.typeface[position - 1] = LC;
+    }
+    else if ((store.chars[position - 2] == '{') && (store.chars[position - 1] == '-')) {
+      store.typeface[position - 2] = RC; /* Haskell "{-" regional comment begin */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '-') && (store.chars[position - 1] == '}')) {
+      store.typeface[position - 2] = CR; /* Haskell "-}" regional comment end */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '+') && (store.chars[position - 1] == '+')) {
+      store.typeface[position - 2] = DP; /* Double plus */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == ':') && (store.chars[position - 1] == '+')) {
+      store.typeface[position - 2] = CP; /* Colon plus */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '<') && (store.chars[position - 1] == '=')) {
+      store.typeface[position - 2] = LE; /* Less than or equal to */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '>') && (store.chars[position - 1] == '=')) {
+      store.typeface[position - 2] = GE; /* Greater than or equal to */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '<') && (store.chars[position - 1] == '-')) {
+      store.typeface[position - 2] = LA; /* Leftarrow */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '-') && (store.chars[position - 1] == '>')) {
+      store.typeface[position - 2] = RA; /* Rightarrow */
+      store.typeface[position - 1] = DC;
+    }
+    else if ((store.chars[position - 2] == '=') && (store.chars[position - 1] == '>')) {
+      store.typeface[position - 2] = RR; /* Double rightarrow */
+      store.typeface[position - 1] = DC;
+    }
+    else if (((store.chars[position - 2] == '*') && (store.chars[position - 1] == '*'))
+            || ((store.chars[position - 2] == '^') && (store.chars[position - 1] == '^'))) {
+      store.typeface[position - 2] = MA; /* Exponent, ie not Times */
+      store.typeface[position - 1] = MA;
+    }
+  }
+  return(store);
+}
+
+int
+  IsHaskellPunc(c)     /* Returns 1 if c is a punctuation mark not part of identifier */
+char c;
+{
+  return((c == ' ') || (c == ',') || (c == '@') || (c == '#') || (c == '$')
+        || (c == '%') || (c == '&') || (c == '*') || (c == '(') || (c == ')')
+        || (c == '-') || (c == '+') || (c == '=') || (c == '\\') || (c == '|')
+        || (c == '[') || (c == ']') || (c == '{') || (c == '}') || (c == ':')
+        || (c == ';') || (c == '"') || (c == '~') || (c == '?') || (c == '/')
+        || (c == '<') || (c == '>') || (c == '^'));
+}
+
+int
+  IsKeyWord(str)               /* Returns 1 if str is a keyword to be in keyword font */
+char str[MAXLINELENGTH];
+{
+  return((!(strcmp(str, "case"))) || (!(strcmp(str, "class")))
+        || (!(strcmp(str, "data"))) || (!(strcmp(str, "default")))
+        || (!(strcmp(str, "deriving"))) || (!(strcmp(str, "else")))
+        || (!(strcmp(str, "hiding"))) || (!(strcmp(str, "if")))
+        || (!(strcmp(str, "import"))) || (!(strcmp(str, "in")))
+        || (!(strcmp(str, "infix"))) || (!(strcmp(str, "infixl")))
+        || (!(strcmp(str, "infixr"))) || (!(strcmp(str, "instance")))
+        || (!(strcmp(str, "interface"))) || (!(strcmp(str, "let")))
+        || (!(strcmp(str, "module"))) || (!(strcmp(str, "of")))
+        || (!(strcmp(str, "renaming"))) || (!(strcmp(str, "then")))
+        || (!(strcmp(str, "to"))) || (!(strcmp(str, "type")))
+        || (!(strcmp(str, "where"))));
+}
+
+int
+  KeyWord(c, store, position) /* Returns length of keyword if a keyword ends
+                                      at store.chars[position - 1] */
+char c;
+ElementType store;
+int position;
+{
+  int counter, start, end = position - 1, keywordlen = 0;
+  char str[MAXLINELENGTH];
+  
+  if ((!isalpha(c)) && (c != '_') && (c != '\'') && (position)) {
+    for (counter = end; (counter >= 0) && ((isalpha(store.chars[counter]))
+                                          || (c == '_') || (c == '\''))
+                                      && (counter >= store.indentation); counter--) {
+      ;         /* Just count letters */
+    }
+    start = ++counter;
+    for (counter = 0; counter + start <= end; counter++) {
+      str[counter] = store.chars[counter + start]; /* Copy letters into str */
+    }
+    str[counter] = '\0'; /* Add null character to end */
+    if (IsKeyWord(str))                /* Checks word in str is keyword */
+      keywordlen = strlen(str);        /* and measures it */
+  }
+  return(keywordlen);
+}
+
+ElementType
+  CheckForKeyword(c, store, position) /* Returns store with any possible keyword
+                                        ending at store.chars[position - 1]
+                                        identified as such in store.typeface */
+char c;
+ElementType store;
+int position;
+{
+  if (KeyWord(c, store, position))
+    store = ChangeTypeface(store, KeyWord(c, store, position), position, KW);
+  return(store);
+}
+
+int
+  IsNumber(c, store, position, statesok) /* Returns 1 if c forms part of a number */
+char c;
+ElementType store;
+int position, statesok;
+{
+  int counter, foundident = 0, foundpunc = 0;
+
+  if (((isdigit(c)) || (c == 'e') || (c == 'E') || (c == '|') || (c == '.'))
+      && (statesok)) {
+    counter = position - 1;
+    while ((isdigit(store.chars[counter])) && (counter >= 0))
+      counter--;
+    if (((store.chars[counter] == '+') || (store.chars[counter] == '-'))
+       && ((store.chars[counter - 1] == 'e') || (store.chars[counter - 1] == 'E'))
+       && (counter > 2))
+      counter -= 2;
+    else if (((store.chars[counter] == 'e') || (store.chars[counter] == 'E'))
+            && (counter > 1))
+      counter--;
+    while ((isdigit(store.chars[counter])) && (counter >= 0))
+      counter--;
+    if ((store.chars[counter] == '.') && (counter > 1))
+      counter--;
+    while ((isdigit(store.chars[counter])) && (counter >= 0))
+      counter--;
+    if ((isalpha(store.chars[counter])) && (counter >= 0))
+      foundident = 1;          /* ie not number */
+    else if ((IsHaskellPunc(store.chars[counter])) || (counter < 0))
+      foundpunc = 1; /* ie is number */
+  }
+  return(foundpunc);
+}
+                               /* * * LINE SELECTION FUNCTIONS * * */
+ElementType
+  SelectSkipLine(s, store, linecounter)        /* Returns store containing line for skipover */
+StackType s;
+ElementType store;
+int linecounter;
+{
+  ElementType temp;
+  int counter;
+  
+  if (!(IsEmptyStack(s))) {
+    while (((Top(s)).length <= linecounter) || ((Top(s)).indentation >= linecounter)) {
+      temp = Top(s);
+      s = PopSym(s);
+      if (IsEmptyStack(s)) {
+       counter = temp.length;
+       while (counter < linecounter) {
+         temp.chars[counter] = ' ';
+         temp.typeface[counter++] = SP;
+       }
+       temp.chars[counter] = '\0'; /* Add null character to end */
+       s = Push(s, temp);
+       break;
+      }
+    }
+    store = Top(s);
+  }
+  else {                       /* Stack is empty */
+    counter = store.length;
+    while (counter < linecounter) {
+      store.chars[counter] = ' ';
+      store.typeface[counter++] = SP;
+    }
+    store.chars[counter] = '\0'; /* Add null character to end */
+  }
+  return(store);
+}
+                               /* * * STORING FUNCTIONS * * */
+ElementType
+  CreateStore()                        /* Returns an empty store */
+{
+  ElementType store;
+
+  strcpy(store.chars, "");
+  store.length = 0;
+  store.indentation = 0;
+  store.col = 0;
+  return(store);
+}
+
+ElementType
+  StoreSpace(store, position) /* Stores a space in the store at current position */
+ElementType store;
+int position;
+{
+  store.chars[position] = ' ';
+  store.typeface[position] = SP;
+  return(store);
+}
+                               /* * * WRITING FUNCTIONS * * */
+void
+  WriteStartFace(tf)           /* Writes LaTeX typeface commands for start of section */
+enum face tf;
+{
+  if (tf == KW)                        /* Keywords */
+    printf("{\\keyword ");
+  else if ((tf == ID) || (tf == IS)) /* Identifiers */
+    printf("{\\iden ");
+  else if (tf == ST)           /* Strings */
+    printf("{\\stri ");
+  else if (tf == CO)           /* Comments */
+    printf("{\\com ");
+  else if (tf == NU)           /* Numbers */
+    printf("{\\numb ");
+  else if ((tf == MA) || (tf == TI)) /* Various maths */
+    printf("$");
+}
+
+void
+  WriteFinishFace(tf)          /* Writes LaTeX typeface commands for end of section */
+enum face tf;
+{
+  if ((tf == KW) || (tf == ID) || (tf == ST) || (tf == CO)
+      || (tf == NU)) /* Keywords, identifiers, strings, comments or numbers */
+    printf("\\/}");
+  else if ((tf == MA) || (tf == TI)) /* Various maths */
+    printf("$");
+  else if (tf == IS) /* Subscripts in identifiers */
+    printf("\\/}$");
+}
+
+int
+  WriteSpaces(store, counter, finish) /* Writes consecutive spaces,
+                                        returning new counter value */
+ElementType store;
+int counter, finish;
+{
+  int spaces = 0;              /* The number of spaces found */
+  
+  for (; (store.typeface[counter] == SP) && (counter < finish); counter++)
+    spaces++;
+  printf("\\xspa{%d}", spaces);
+  return(--counter);
+}
+
+int
+  WriteChar(store, counter, finish)    /* Writes charater, returning new counter value */
+ElementType store;
+int counter, finish;
+{
+  if (store.typeface[counter] == SP) /* Space */
+    printf("\\xspa1"); /* Redundant */
+  else if (store.typeface[counter] == BE) /* Bar under equals sign */
+    printf("\\bareq");
+  else if (store.typeface[counter] == DP) { /* Double plus */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("\\plusplus");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == CP) { /* Colon plus */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("{:}{+}");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == LE) { /* Less than or equal to */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("$\\leq$");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == GE) { /* Greater than or equal to */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("$\\geq$");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == LA) { /* Leftarrow */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("$\\leftarrow$");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == RA) { /* Rightarrow */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("$\\rightarrow$");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == RR) { /* Double rightarrow */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("$\\Rightarrow$");
+      counter++;
+    }
+  }
+  else if (store.typeface[counter] == RC) { /* Regional comment begin */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("{\\com \\{-\\/}");
+      counter++;
+    }
+    else
+      printf("{\\com \\{\\/}");
+  }
+  else if (store.typeface[counter] == CR) { /* Regional comment end */
+    if ((counter < finish - 1) && (store.typeface[counter + 1] == DC)) {
+      printf("{\\com -\\}\\/}");
+      counter++;
+    }
+    else
+      printf("{\\com -\\/}");
+  }
+  else if ((store.typeface[counter] == LC) && (store.chars[counter] == '-'))
+    printf("{\\rm -}");        /* Comment - problem: "--" becomes "-" in LaTeX so fix done */
+  else if (store.chars[counter] == '\\')
+    printf("\\hbox{$\\setminus$}"); /* Backslash */
+  else if (store.chars[counter] == '*') {
+    if (store.typeface[counter] == TI)
+      printf("\\times ");      /* Multiplication */
+    else
+      printf("*");             /* Other star symbols, eg Exponent */
+  }
+  else if ((store.chars[counter] == '_') && (store.typeface[counter] == SU)) {
+      if ((counter < finish - 1) && (store.typeface[counter + 1] == IS))
+       printf("$_");           /* Subscript character */
+  }
+  else if (store.chars[counter] == '^')
+    printf("\\char'136 ");     /* Up-arrow */
+  else if (store.chars[counter] == '~')
+    printf("\\char'176 ");     /* Tilda */
+  else if ((store.chars[counter] == ':') && (store.chars[counter - 1] == ':')
+          && (widecolons))
+    printf("\\,:");    /* Double colon */
+  else if (store.chars[counter] == '"') {
+    if ((counter) && ((store.chars[counter - 1] == '"')
+                     || (store.chars[counter - 1] == '\'')))
+      printf("\\,");   /* If previous character was a quote, leave a little space */
+    if (store.typeface[counter] == DQ)
+      printf("{\\rm ``}");     /* Open doublequote */
+    else if (store.typeface[counter] == QD)
+      printf("{\\rm \"}");     /* Close doublequote */
+    else
+      printf("{\\rm \\char'175}"); /* Escape doublequote in string */
+  }
+  else if (store.chars[counter] == '\'') {
+    if ((counter) && ((store.chars[counter - 1] == '"')
+                     || ((store.chars[counter - 1] == '\'')
+                         && ((store.typeface[counter - 1] != MA)
+                             || (store.typeface[counter] != MA)))))
+      printf("\\,");    /* If previous character was a quote, leave a little space
+                          except when it's a double prime */
+    if (store.typeface[counter] == FQ)
+      printf("\\forquo ");     /* Forward single quote */
+    else if (store.typeface[counter] == EQ)
+      printf("\\escquo ");     /* Escape single quote */
+    else if (store.typeface[counter] == BF) {
+      if ((counter + 1 < store.length) && (store.typeface[counter + 1] == BF)
+         && (counter + 1 != store.indentation)) {
+       printf("{\\com \'\'\\/}"); /* Closing LaTeX style quote */
+       counter++;
+      }
+      else
+       printf("{\\com \'\\/}"); /* Single quote following backquote in comment */
+    }
+    else
+      printf("\'");            /* Prime */
+  }
+  else if (store.chars[counter] == '{')
+    printf("\\hbox{$\\cal \\char'146$}"); /* Open curly bracket */
+  else if (store.chars[counter] == '}')
+    printf("\\hbox{$\\cal \\char'147$}"); /* Close curly bracket */
+  else if ((counter) && (store.chars[counter - 1] == '[') && (store.chars[counter] == ']'))
+    printf("\\,]");            /* Leave small gap between adjacent square brackets */
+  else if ((store.chars[counter] == '$') || (store.chars[counter] == '%')
+          || (store.chars[counter] == '_') || (store.chars[counter] == '#')
+          || (store.chars[counter] == '&')) /* Various characters needing '\' for LaTeX */
+    printf("\\%c", store.chars[counter]);
+  else                         /* Other characters */
+    printf("%c", store.chars[counter]);
+  return(counter);
+}
+
+void
+  WriteSkipover(store) /* Writes the skipover portion of line in store */
+ElementType store;
+{
+  int counter = 0;
+
+  printf("\\skipover{");       /* Write opening LaTeX skipover command */
+  WriteStartFace(store.typeface[counter]); /* Write opening LaTeX typeface command */
+  if (store.typeface[counter] == SP)
+    counter = WriteSpaces(store, counter, store.indentation); /* Write spaces */
+  else
+    counter = WriteChar(store, counter, store.indentation); /* Write character */
+  for (counter++; counter < store.indentation; counter++){ /* until end of skipover */
+    if (store.typeface[counter - 1] != store.typeface[counter]) { /* If typeface change */
+      WriteFinishFace(store.typeface[counter - 1]); /* write closing typeface command */
+      WriteStartFace(store.typeface[counter]); /* write opening LaTeX typeface command */
+    }      
+    if (store.typeface[counter] == SP)
+      counter = WriteSpaces(store, counter, store.indentation); /* Write spaces */
+    else
+      counter = WriteChar(store, counter, store.indentation); /* Write character */
+  }
+  if (store.typeface[counter - 1] == SU)
+    ;          /* If indentation is under subscript don't open math section */
+  else
+    WriteFinishFace(store.typeface[counter - 1]); /* Write closing LaTeX typeface command */
+  printf("}");                 /* Write closing LaTeX skipover command */
+}
+
+void
+  WriteWords(store)            /* Writes rest of line, starting at indentation level */
+ElementType store;
+{
+  int counter = store.indentation;
+  int intabular = 0;          /* Boolean: is in tabular section for internal alignment */
+
+  WriteStartFace(store.typeface[counter]); /* Write opening LaTeX typeface command */
+  if (store.typeface[counter] == SP)
+    counter = WriteSpaces(store, counter, store.length); /* Write spaces */
+  else
+    counter = WriteChar(store, counter, store.length); /* Write character */
+  for (counter++; counter < store.length; counter++){ /* until end of word */
+    if ((store.col) && (store.col == counter)) {
+      printf(" & ");
+      if (store.chars[counter - 1] == ':')
+       printf("$:");
+      intabular = 1;
+    }
+    if (store.typeface[counter - 1] != store.typeface[counter]) /* If typeface change */
+      WriteFinishFace(store.typeface[counter - 1]); /* Write closing typeface command */
+    if ((store.typeface[counter] == SP) && (intabular)) {
+      printf(" & ");
+      intabular = 0;
+    }
+    if ((store.typeface[counter - 1] != store.typeface[counter]) /* If typeface change */
+       && ((store.chars[counter] != ':') || (store.col != counter + 1)))
+      WriteStartFace(store.typeface[counter]); /* Write opening LaTeX typeface command */
+    if (store.typeface[counter] == SP)
+      counter = WriteSpaces(store, counter, store.length); /* Write spaces */
+    else if ((store.chars[counter] != ':') || (!store.col) || (store.col != counter + 1))
+      counter = WriteChar(store, counter, store.length); /* Write character */
+  }
+  WriteFinishFace(store.typeface[counter - 1]); /* Write closing LaTeX typeface command */
+}
+
+void
+  WriteLine(store, needed)     /* Writes the line in store,
+                                  only writing LaTeX newline if needed */
+ElementType store;
+int needed;
+{
+  if (store.indentation)
+    WriteSkipover(store);
+  if (store.indentation < store.length)
+    WriteWords(store);
+  if (needed)
+    printf("\\\\");            /* LaTeX newline character */
+  printf("\n");
+}
+
+QueueType
+  WriteQueue(q)                        /* Writes lines, removing them from queue,
+                                  leaves last line in queue if not in tabular section */
+QueueType q;
+{
+  int intabular = 0;
+
+  if ((!(IsEmptyQueue(q))) && ((*(FrontOfQueue(q))).Element.col)) {
+    printf("\\begin{tabular}{@{}l@{\\xspa1}c@{}l}\n");
+    intabular = 1;
+  }
+  while (LengthOfQueue(q) > !intabular) {
+    WriteLine((*(FrontOfQueue(q))).Element, 1);        /* LaTeX newline character is needed */
+    q = TakeFromQueue(q);
+  }
+  if (intabular)
+    printf("\\end{tabular}\\\\\n");
+  return(q);
+}
+
+QueueType
+  WriteRestOfQueue(q)          /* Writes all lines, removing them from queue,
+                                  doesn't have LaTeX newline after last line */
+QueueType q;
+{
+  int intabular = 0;
+
+  if ((!(IsEmptyQueue(q))) && ((*(FrontOfQueue(q))).Element.col)) {
+    printf("\\begin{tabular}{@{}l@{\\xspa1}c@{}l}\n");
+    intabular = 1;
+  }
+  while (!(IsEmptyQueue(q))) {
+    WriteLine((*(FrontOfQueue(q))).Element, (LengthOfQueue(q) > 1)); /* Last line doesn't
+                                                          need LaTeX newline character */
+    q = TakeFromQueue(q);
+  }
+  if (intabular) {
+    printf("\\end{tabular}");
+    if (!IsEmptyQueue(q))      /* Last line doesn't need LaTeX newline character */
+      printf("\\\\");
+    printf("\n");
+  }
+  return(q);
+}
+
+int
+main (argc, argv)              /* * * MAIN PROGRAM * * */
+     int argc;
+     char *argv[];
+{
+  int tripped = 1, instring = 0, instringincomment = 0, inlinecomment = 0;
+  int incharquote = 0, incharquoteincomment = 0, inbackquoteincomment = 0;
+  int insub = 0;
+  /* Booleans - just taken new line, in string, in string inside comment, in line comment,
+     in character quote, in character quote inside comment, in backquote inside comment,
+     in subscript */
+  int linecounter = 0, indentcounter = 0, inregcomment = 0, pos;
+  /* Counters: current position on line, indentation of current line,
+     nesting level of regional comments, position marker */
+  char c;                      /* Character */
+  StackType s;                 /* Stack of previous longest lines */
+  QueueType q;                 /* Queue of lines waiting to be printed */
+  ElementType store;           /* Store of letters, typefaces and non-empty length */
+
+  if ((argc == 3) && (argv[1][0] == '-')) { /* If options specified with call */
+    if (strstr(argv[1], "s"))  /* if -s option, subscripts in identifiers wanted */
+      subscripts = 1;
+    if (strstr(argv[1], "t")) {        /* if -tX option, tab characters are X spaces */
+      for (pos = 1; (argv[1][pos] != 't'); pos++) /* find 't' */
+       ;
+      for (pos++, tablength = 0; isdigit(argv[1][pos]); pos++) /* read number */
+       tablength = (tablength * 10) + (argv[1][pos] - '0');
+    }
+    if (strstr(argv[1], "w"))  /* if -w option called, wide double colons wanted */
+      widecolons = 1;
+  }
+  else if (argc == 2)          /* If no options */
+    ;
+  else {                       /* If not called with pphs and a filename */
+    fprintf(stderr, "pphs: Call with one file name\n");
+    exit(1);
+  }
+  
+  if ((strcspn(argv[argc - 1], ".") == strlen(argv[argc - 1])) /* If filename has no extention */
+      && ((ifptr = fopen(argv[argc - 1], "r")) == NULL)) /* and no plain file of that name */
+    strcat(argv[argc - 1], ".hs"); /* add a ".hs" extention */
+  if ((ifptr = fopen(argv[argc - 1], "r")) == NULL) { /* Open input file */
+    fprintf(stderr, "pphs: File could not be opened\n"); /* eg isn't there */
+    exit(2);
+  }
+  else {
+    
+    printf("\\begin{tabbing}\n"); /* Start of Haskell program */
+    
+    store = CreateStore();     /* an empty one */
+    s = CreateStack();         /* an empty one */
+    q = CreateQueue();         /* an empty one */
+    
+    fscanf(ifptr, "%c", &c);   /* Read character */
+    while (!feof(ifptr)) {     /* While not at end of input file */
+      while ((isspace(c)) && (!(feof(ifptr)))) { /* Read blank characters */
+       if (c == ' ') {
+         if (tripped)
+           linecounter++;      /* Count leading spaces */
+         else {                /* or */
+           store = StoreSpace(store, linecounter++); /* Store intermediate
+                                                        or trailing space */
+           if (store.length < linecounter)
+             store.chars[linecounter] = '\0'; /* Add null character to end */
+         }
+         fscanf(ifptr, "%c", &c); /* Read next character */
+       }
+       else if (c == '\t') {
+         if (tripped)
+           linecounter += (tablength - (linecounter % tablength));
+         else {
+           store = StoreSpace(store, linecounter++);
+           for (; linecounter % tablength; linecounter++)
+             store = StoreSpace(store, linecounter);
+           if (store.length < linecounter)
+             store.chars[linecounter] = '\0'; /* Add null character to end */
+         }
+         fscanf(ifptr, "%c", &c); /* Read next character */
+       }
+       else if (c == '\n') {
+         tripped = 1;          /* Just taken a new line */
+         inlinecomment = 0;
+         if (!(IsEmptyStack(s)))
+           while (((Top(s)).length <= store.length)
+                  && ((Top(s)).indentation >= store.length)) {
+             s = Pop(s);
+             if (IsEmptyStack(s))
+               break;
+           }
+         if (store.length > 0) { /* Push non-empty line onto indentation stack */
+           store.indentation = indentcounter;
+           s = Push(s, store);
+         }
+         if (!(IsEmptyQueue(q))) {
+           if ((store.col != (*(FrontOfQueue(q))).Element.col)
+               || (!(*(FrontOfQueue(q))).Element.col))
+             q = WriteQueue(q); /* If internal alignment changes or there is none
+                                   write out lines */
+         }
+         q = AddToQueue(q, store); /* Add to writing queue */
+         linecounter = 0;      /* Get ready to count leading spaces */
+         store.length = linecounter;
+         fscanf(ifptr, "%c", &c); /* Read next character */
+       }
+       else break;
+      }
+      if (tripped) {
+       indentcounter = linecounter;
+       store.indentation = linecounter;
+       store.col = 0;
+      }
+      if ((tripped) && (linecounter)) { /* Skipover necessary for indentation */
+       store = SelectSkipLine(s, store, linecounter);
+       store.indentation = linecounter;
+       store.col = 0;
+      }
+      if (!feof(ifptr))
+       tripped = 0;            /* No longer just taken new line */
+      while ((!(isspace(c))) && (!(feof(ifptr)))) { /* Read word */
+       if ((linecounter > 1) && (!IsEmptyQueue(q))
+           && ((*(RearOfQueue(q))).Element.length >= linecounter)
+           && (linecounter > store.indentation)
+           && (linecounter > (*(RearOfQueue(q))).Element.indentation)
+           && (store.chars[linecounter - 1] == ' ')
+           && ((((*(RearOfQueue(q))).Element.chars[linecounter - 1] == ' ')
+                && ((c == (*(RearOfQueue(q))).Element.chars[linecounter])
+                    || ((c == '=')
+                        && ((*(RearOfQueue(q))).Element.chars[linecounter] == ':')
+                        && ((*(RearOfQueue(q))).Element.chars[linecounter + 1] == ':'))))
+               || (((*(RearOfQueue(q))).Element.chars[linecounter - 1] == ':')
+                   && ((*(RearOfQueue(q))).Element.chars[linecounter] == ':')
+                   && (c == '=')))
+           && ((store.chars[linecounter - 2] == ' ')
+               || ((*(RearOfQueue(q))).Element.chars[linecounter - 2] == ' '))
+           && (((*(RearOfQueue(q))).Element.col == 0)
+               || ((*(RearOfQueue(q))).Element.col == linecounter))) {
+         store.col = linecounter; /* Identify any internal alignment */
+         (*(RearOfQueue(q))).Element.col = linecounter;
+       }
+       if ((c == '"') && (!incharquote) /* String outside comments */
+           && (!inregcomment) && (!inlinecomment)) {
+         if (((linecounter) && (store.chars[linecounter - 1] != '\\'))
+             || (!linecounter))
+           instring = !instring;
+       }
+       else if ((c == '"') && (!incharquoteincomment) /* String inside comment */
+                && (!inbackquoteincomment)
+                && ((inregcomment) || (inlinecomment))) {
+         if (((linecounter) && (store.chars[linecounter - 1] != '\\'))
+             || (!linecounter))
+           instringincomment = !instringincomment;
+       }
+       else if ((c == '`') && ((inlinecomment) || (inregcomment))) {
+         if ((linecounter) && (store.chars[linecounter - 1] == '`'))
+           inbackquoteincomment = 2; /* Opening LaTeX style quote in comment */
+         else
+           inbackquoteincomment = !inbackquoteincomment; /* Backquote in comment */
+       }
+       else if ((linecounter) && (!inlinecomment) && (!instring)) {
+         if ((store.chars[linecounter - 1] == '{') && (c == '-'))
+           inregcomment++;     /* Haskell "{-" regional comment begin */
+         else if ((store.chars[linecounter - 1] == '-') && (c == '}')) {
+           inregcomment--;     /* Haskell "-}" regional comment end */
+           instringincomment = 0;
+           incharquoteincomment = 0;
+           inbackquoteincomment = 0;
+         }
+       }
+       if (c == '|') {
+         if ((!IsEmptyQueue(q))
+             && ((((*(RearOfQueue(q))).Element.chars[linecounter] == '=')
+                  && (linecounter == store.indentation))
+                 || ((*(RearOfQueue(q))).Element.typeface[linecounter] == BE)))
+           store.typeface[linecounter] = BE;
+         else
+           store.typeface[linecounter] = MA;
+       }
+       else if ((c == '\'') && (linecounter) && (store.chars[linecounter - 1] == '\\'))
+         store.typeface[linecounter] = EQ; /* Escape character quote */
+       else if ((c == '\'') && (!instring) && (!inregcomment) && (!inlinecomment)) {
+         if (((linecounter) && (store.chars[linecounter - 1] != '\\')
+              && ((IsHaskellPunc(store.chars[linecounter - 1])) || (incharquote)))
+             || (!linecounter)) {
+           incharquote = !incharquote;
+           store.typeface[linecounter] = FQ; /* Character quote */
+         }
+         else
+           store.typeface[linecounter] = MA; /* Prime */
+       }
+       else if ((c == '\'') && (!instringincomment)
+                && ((inregcomment) || (inlinecomment))) {
+         if (((linecounter) && (store.chars[linecounter - 1] != '\\')
+              && ((IsHaskellPunc(store.chars[linecounter - 1]))
+                  || (incharquoteincomment)))
+             || (!linecounter)) {
+           incharquoteincomment = !incharquoteincomment;
+           store.typeface[linecounter] = FQ; /* Character quote in comment */
+         }
+         else if (inbackquoteincomment) {
+           inbackquoteincomment--;
+           store.typeface[linecounter] = BF; /* `x' character quote in comment */
+         }
+         else
+           store.typeface[linecounter] = MA; /* Prime */
+       }
+       else if (c == '"') {
+         if ((!incharquote) && (!incharquoteincomment) && (!inbackquoteincomment)
+             && ((instring) || (instringincomment))) {
+           if (((linecounter) && (store.chars[linecounter - 1] != '\\'))
+               || (!linecounter))
+             store.typeface[linecounter] = DQ; /* Open doublequote */
+           else if (store.chars[linecounter - 1] == '\\')
+             store.typeface[linecounter] = EE; /* Escape doublequote */
+         }
+         else if ((!incharquote) && (!incharquoteincomment) && (!inbackquoteincomment)) {
+           if (((linecounter) && (store.chars[linecounter - 1] != '\\'))
+               || (!linecounter))
+             store.typeface[linecounter] = QD; /* Close doublequote */
+           else if (store.chars[linecounter - 1] == '\\')
+             store.typeface[linecounter] = EE; /* Escape doublequote */
+         }
+         else
+           store.typeface[linecounter] = EE; /* Character quote of doublequote */
+       }
+       else if (c == '`') {
+         if ((inlinecomment) || (inregcomment))
+           store.typeface[linecounter] = CO;
+         else
+           store.typeface[linecounter] = MA;
+       }
+       else if ((linecounter) && (subscripts) && (c == '_')
+                && (store.typeface[linecounter - 1] == ID))
+         store.typeface[linecounter] = SU; /* Subscript in identifier */
+       else if (c == '*')
+         store.typeface[linecounter] = TI; /* Times - may be changed by double char */
+       else if (IsMathsChar(c))
+         store.typeface[linecounter] = MA; /* Maths characters */
+       else if (IsNumber(c, store, linecounter,
+                         ((!inregcomment) && (!instring) && (!inlinecomment))))
+         store.typeface[linecounter] = NU; /* Numbers */
+       else if ((instring) || (incharquote))
+         store.typeface[linecounter] = ST; /* Characters in strings */
+       else if ((inlinecomment) || (inregcomment))
+         store.typeface[linecounter] = CO; /* Characters in comments */
+       else {
+         if (insub)
+           store.typeface[linecounter] = IS; /* Subscript identifiers */
+         else
+           store.typeface[linecounter] = ID; /* Others */
+       }
+       if (linecounter)
+         if ((store.typeface[linecounter - 1] == IS)
+             && (store.typeface[linecounter] != IS))
+           insub = 0;          /* End of subscript identifier */
+       store.chars[linecounter++] = c; /* Place character in store */
+       if (linecounter > store.indentation + 1)
+         store = CheckForDoubleChar(store, linecounter);
+       if ((store.typeface[linecounter - 1] == LC) && (!inregcomment)
+           && (!instring) && (!incharquote)) {
+         instringincomment = 0;
+         incharquoteincomment = 0;
+         inbackquoteincomment = 0;
+         inlinecomment = 1;
+       }
+       else if ((store.typeface[linecounter - 1] == SU)
+                && (linecounter != store.indentation))
+         insub = 1;
+       fscanf(ifptr, "%c", &c); /* Read next character */
+       if (feof(ifptr))
+         c = ' ';
+       if ((!inregcomment) && (!inlinecomment) && (!instring))
+         store = CheckForKeyword(c, store, linecounter); /* Keywords not in comments or
+                                                            strings to be in keyword typeface */
+      }
+      insub = 0;
+      store.chars[linecounter] = '\0'; /* String terminating null character */
+      store.length = linecounter;
+    }
+    if ((!tripped) && (!store.col)) /* If last line not in internal alignment */
+      q = WriteQueue(q);             /*   write previous lines which might */
+    if (!tripped)              /* Put final line in queue if non-empty */
+      q = AddToQueue(q, store);
+    if (feof(ifptr))           /* Write remaining lines */
+      q = WriteRestOfQueue(q);
+    
+    printf("\\end{tabbing}\n"); /* End of Haskell program */
+    
+    exit(0);
+  }
+}