%{

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>

#include "log.h"
#include "taccutil.h"
#include "sscanr.h"
#include "debug.h"
#include "mmalloc.h"

/*
     This program parses an Imp80 source file, and walks the parse tree
    to output the source with consistent case and indentation.  It is
    roughly equivalent to Peter Robertson's SOAP program, but not as clever.

    This is not yet released for prime time!  It was written solely as an
    excuse to use the Imp80 grammar and see how it worked.

    The input for this program must come from the "filter" program also
    in this directory.  Filter needs a little work too...

    Limitations:
       1) no command line options at all.  No parameterisation of indents etc.
       2) {comments} are completely lost.  Only !Comments are preserved.
       3) The parser generator used here has several opportunities for
          optimisation which have never been taken advantage of so it is
          currently slow.  Addressing those optimisations will speed up
          this program simply by rebuilding.
       4) The parser generator uses huge amounts of Ram, especially when
          formatting long declaration lists, which it does rather slowly.

    Design features: (you may call these bugs but they're deliberate)
       1) All statements are output one per line - ie all ';'s are made into
          newlines.
       2) comments are displayed at the same indentation level as code.  No
          support for block comments in column 0.
       3) Currently labels are always placed in column 0.  I'll change this
          so they're indented to the level of the current procedure/begin
          (as opposed to the current code indentation, which I tried at
          first but didn't like the look of)
       4) I want to add flags so that either names and/or keywords can
          be output in upper case.  case distinctions are lost.
       5) fancier pre-processing (a first pass with output set to /dev/null)
          would allow me to note the original space-included forms of names,
          select the most common one for any canonical name (eg a source
          file may contain 500 "open input"s and 10 "openinput"s, so 
          we would choose "open input" in that instance) and use that on
          output rather than the C-like space-removed names that we currently
          generate.  Alternatively a change to the "name" phrase to allow
          spaces in names would let them be passed through verbatim.  Case
          however is another issue.
       6) Breaking of long lines is currently not implemented and even when
          it is, it will be done poorly; arrays will not be cleverly formatted
          or aligned in columns.
       7) I left all the dummy phrases in the grammar which in a compiler
          are used to patch up forward references etc.  They're not needed
          here and just contribute overhead.  Remove them sometime.

    Plans for improvements:
       1) This code can be used to convert certain Imp80 and Imp77 features
          to the common subset that both compilers understand, i.e. it will not
          be just a formatter but a source to source translator.

    Bugs:
       1) No error recovery whatsoever if a parse fails.  Assumes both that
          this code is correct and that the input file compiles correctly
          under Imp80
       2) The grammar this is built on was intended to be used with code
          to check certain combinations which the grammar allows but the
          language does not.  For instance an "Assop" can be "=", "==",
          "<-" (jam transfer), or "->" (string assignment).  However in
          the context of the result of a function, only "=" or maybe "<-"
          might be valid; in a map only "==" is valid, and "%result ->"
          is *never* valid.  Except that it is in this program, ie the
          language it accepts includes some invalid constructs.

    Notes:
       1) Interesting grammar feature found caused by the original using
          built-in phrases versus this one doing all parsing at the top level:
             I = M'fred'
          was parsed as (name I) (assop =) (name M) <error>, i.e. the
          test for a name came before the test for a multi-constant, and
          caused a parse-fail because of the lack of lookahead by one token.
          This was corrected by adding a guard after <name> which checked for
          a quote and failed if present.

 */

/* This is the type of objects passed around as $1, $$ etc. */
#define USERTYPE char

#define RBRACE '}'
  /* Bug in tacc translation  - } skipped OK in strings but not int consts */
#define SQUOTE 39
#define DQUOTE 34


int exit_flag = FALSE;
int printing = TRUE;
int ilev = 0; /* nested begin/end for now.  Later use a stack */
int delayed_ilev = 0;
extern int _debug; /* set to true for parser diags */
int label = FALSE;

char *ProgName = "imp";
extern char **argv;
extern int argc;

int verbose = FALSE;

void indent(int ilev)
{
  while (ilev-- > 0) printf("   ");
}

char *doubleup(char *text, int c)
{
   char *s, *t;
   int ccount = 0;
   s = text;
   while (*s != '\0') if (*s++ == c) ccount += 1;
   t = s = stackmalloc(strlen(text)+ccount+1);
   if (s == NULL) {
      fprintf(stderr, "copyof: malloc fails - not enough room.\n");
      exit(EXIT_FAILURE);
   }
   for (;;) {
     if (*text == c) *s++ = c;
     if ((*s++ = *text++) == '\0') break;
   }
   return(t);
}

char *formatf(char *s, ...)
{
  /* Size the string by vfprint'ing it to /dev/null... */
  /* then heapmalloc an appropriate area                */
  char *APPROPRIATE_STRING;
  va_list ap;          

  va_start(ap, s);
  
  {
    static FILE *nullfile = NULL;
    int string_length;
    
    if (nullfile == NULL) nullfile = fopen("/dev/null", "w");
    if (nullfile == NULL) {
      fprintf(stderr, "Major error - cannot open /dev/null\n");
      fflush(stderr);
      exit(1);
    }
    string_length = vfprintf(nullfile, s, ap);
    /* fclose(nullfile); */
    APPROPRIATE_STRING = tempheapmalloc(string_length+1);
    vsprintf(APPROPRIATE_STRING, s, ap);
  }
  va_end(ap);
  return(APPROPRIATE_STRING);
}

char *strlwr(char *orig)
{
  char *s = orig;
  for (;;) {
    if (*s == '\0') break;
    if ((isalpha(*s)) && (isupper(*s))) *s = tolower(*s);
    s++;
  }
  return(orig);
}

             
%}

/* Main cheats - it invokes the parsing routines explicitly,
   in order to reduce the size of the parse tree for a whole
   file.  Also allows recovery of errors at useful boundaries */

main: ""
  {
    YYTYPE *subroot;
    void *stacktop;
    int i;

    if (strcmp(argv[argc-1], "-v") == 0) {
      argc -= 1;
      verbose = TRUE;
    }
    if (strcmp(argv[argc-1], "-d") == 0) {
      argc -= 1;
      _debug = TRUE;
    }
    if (strcmp(argv[argc-1], "-vd") == 0) {
      argc -= 1;
      _debug = TRUE;
      verbose = TRUE;
    }


    if (argc == 1) {
       yyin = fopen("test.ii", "r");
    } else if (argc != 2) {
       fprintf(stderr, "syntax: imp infile.ii\n");
       /* Let's just resume for now... */
       yyin = fopen(argv[1], "r");
    } else {
       yyin = fopen(argv[1], "r");
    }
    if (yyin == NULL) {
       fprintf(stderr, "imp: cannot open input\n");
       exit(EXIT_FAILURE);
    }

    fprintf(stderr, "%s: processing %s\n", argv[0], argv[1] == NULL ? "test.ii" : argv[1]);

    if (verbose) fprintf(stderr, "Starting\n");

    for (;;) {
      stacktop = stackmark();
      if (SS_parse(&subroot)) {
          execute_parsetree(subroot);
      } else {
          return(FALSE);
      }
      stackrelease(stacktop);
      if (exit_flag) return(TRUE);
      printf("\n");
      ilev += delayed_ilev; delayed_ilev = 0;
    }
  }
;

OPERAND:        <NAME><APP><ENAMEquote> { $$=formatf("%s%s%s", $1, $2, $3); } |
                <CONST> { $$=$1; } |
                "\("<EXPR>"\)" { $$=formatf("(%s)", $2); };

COPERAND:       <NAME><DUMMYAPP> { $$=$1; } |
                <CONST> { $$=$1; } |
                "\("<CEXPR>"\)" { $$=formatf("(%s)", $2); };

CEXPR:          <HOLE><UNARY><COPERAND><MARK><RESTOFCEXPR> {
                  $$=formatf("%s%s", $3, $5);
};

STAROREXPR:     <HOLE><UNARY><COPERAND><MARK><RESTOFCEXPR> {
                  $$=formatf("%s%s%s", $2, $3, $5);
} |
                "\*" { $$=formatf("%s", "*"); };

EXPR:           <HOLE><UNARY><OPERAND><MARK><RESTOFEXPR> {
                  $$=formatf("%s%s%s", $2, $3, $5);
};

RESTOFEXPR:     <OP><OPERAND><RESTOFEXPR> { $$=formatf(" %s %s%s", $1, $2, $3); } |
                "" { $$=formatf("%s", ""); };

RESTOFCEXPR:    <OP><COPERAND><RESTOFCEXPR> {
                  $$ = formatf(" %s %s%s", $1, $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

APP:            "\("<EXPR><RESTOFAPP>"\)" {
                  $$ = formatf("(%s%s)", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFAPP:      ","<EXPR><RESTOFAPP> {
                  $$ = formatf(", %s%s", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

PercentIU:      "if" { $$ = formatf("%s", "%if "); } |
                "unless" { $$ = formatf("%s", "%unless "); };

PercentWU:      "while"<HOLE><SC><RESTOFCOND><MARK> {
                  $$ = formatf("%s %s%s", "%while", $3, $4); 
} |
                "until"<HOLE><SC><RESTOFCOND><MARK> { 
                  $$ = formatf("%s %s%s", "%until", $3, $4); 
} |
                "for"<HOLE><NAME>"="<EXPR>","<EXPR>","<EXPR><MARK> {
                  $$ = formatf("%s %s = %s, %s, %s", "%for", $3, $5, $7, $9); 
};

ALIASquote:     "alias"<TEXTTEXT> {
                  $$ = formatf(" %s %s", "%alias", $2); 
} |
                "" { $$=formatf("%s", ""); };

NLISTquote:     ","<NAME><NLISTquote> {
                  $$ = formatf(", %s%s", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

FULLTYPE:       "integer" { $$ = formatf("%s", "%integer "); } |
                "" { $$=formatf("%s", ""); };

BTYPE:          "integer" { $$ = formatf("%s", "%integer "); } |
                "real" { $$ = formatf("%s", "%real "); } |
                "longreal" { $$ = formatf("%s", "%long %real "); };

TYPE:           "integer" { $$ = formatf("%s", "%integer "); } |
                "real" { $$ = formatf("%s", "%real "); } |
                "long"<BTYPE> { $$ = formatf("%s%s", "%long ", $2); } |
                "byte"<FULLTYPE> { $$ = formatf("%s%s", "%byte ", $2); } |
                "string"<REPFACT> { $$ = formatf("%s%s%s", "%string ", $2, (*$2 == '\0' ? "" : " ")); } |
                "half"<FULLTYPE> { $$ = formatf("%s%s", "%half ", $2); } |
                "short"<FULLTYPE> { $$ = formatf("%s%s", "%short ", $2); } |
                "record\("<RFREF>"\)" { $$ = formatf("%s (%s) ", "%record", $2); };

RT:             "routine" { $$ = formatf("%s", "%routine "); } |
                <CHTYPE><TYPE><FM> { $$ = formatf("%s%s", $2, $3); };

FM:             "fn" { $$ = formatf("%s", "%fn "); } |
                "map" { $$ = formatf("%s", "%map "); } |
                "function" { $$ = formatf("%s", "%function "); };

FPDEL:          <TYPE><PercentQNAMEquote><NAME><NLISTquote> {
                  $$ = formatf("%s%s%s%s", $1, $2, $3, $4); 
} |
                <RT><PercentNAMEquote><NAME><NLISTquote><FPP> {
                  $$ = formatf("%s%s%s%s%s", $1, $2, $3, $4, $5); 
} |
                "name"<NAME><NLISTquote> {
                  $$ = formatf("%s%s%s", "%name ", $2, $3); 
};

PercentNAMEquote:    "name" { $$ = formatf("%s", "%name "); } |
                "" { $$=formatf("%s", ""); };

PercentQNAMEquote:    "arrayname" { $$ = formatf("%s", "%array %name "); } |
                "name" { $$ = formatf("%s", "%name "); } |
                "" { $$=formatf("%s", ""); };

FPP:            "\("<HOLE><FPDEL><MARK><RESTOFFPLIST>"\)" {
                  $$ = formatf("(%s%s)", $3, $5); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFFPLIST:   <commaquote><HOLE><FPDEL><MARK><RESTOFFPLIST> {
                  $$ = formatf("%s%s%s", $1, $3, $5); 
} |
                "" { $$=formatf("%s", ""); };

ENDLIST:        "ofprogram"<UP> { $$ = formatf("%s", " %of %program"); } |
                "offile" { $$ = formatf("%s", " %of %file"); } |
                "oflist"<LISTOFF> { $$ = formatf("%s", " %of %list"); } |
                <UP> { $$=formatf("%s", ""); };

PercentFORMATquote:    "format" { $$ = formatf("%s", "%format "); } |
                "" { $$=formatf("%s", ""); };

SC:             <EXPR><COMP><EXPR><RESTOFSC> {
                  $$ = formatf("%s %s %s%s", $1, $2, $3, $4); 
} |
                "\("<SC><RESTOFCOND>"\)" { $$ = formatf("(%s%s)", $2, $3); } |
                "not"<SC> { $$ = formatf("%s", "%not ", $2); };

RESTOFSC:       <COMPtwo><EXPR> { $$ = formatf(" %s %s", $1, $2); } |
                "" { $$=formatf("%s", ""); };

RESTOFCOND:     "and"<SC><RESTOFANDC> {
                  $$ = formatf("%s%s%s", " %and ", $2, $3); 
} |
                "or"<SC><RESTOFORC> {
                  $$ = formatf("%s%s%s", " %or ", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFANDC:     "and"<SC><RESTOFANDC> {
                  $$ = formatf("%s%s%s", " %and ", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFORC:      "or"<SC><RESTOFORC> {
                  $$ = formatf("%s%s%s", " %or ", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFUI:       <ASSOP><EXPR> { $$ = formatf(" %s %s", $1, $2); } |
                ":" { label = TRUE; $$ = formatf("%s", ": "); } |
                "" { $$=formatf("%s", ""); };

PercentSPECquote:    "spec"<HOLE> { $$ = formatf("%s", "%spec "); } |
                <DOWN><HOLE> { $$=formatf("%s", ""); };

VSPECquote:    "spec" { $$ = formatf("%s", "%spec "); } |
                "" { $$=formatf("%s", ""); };

RESTOFBPLIST:   ","<EXPR>":"<EXPR><RESTOFBPLIST> {
                  $$ = formatf(", %s:%s%s", $2, $4, $5); 
} |
                "" { $$=formatf("%s", ""); };

DECLN:          <PercentQNAMEquote><NAME><NLISTquote> {
                  $$ = formatf("%s%s%s", $1, $2, $3); 
} |
                "array"<PercentFORMATquote><ADECLN> {
                  $$ = formatf("%s%s%s", "%array ", $2, $3); 
};

ADECLN:         <NAME><NLISTquote><BPAIR><RESTOFARLIST> {
                  $$ = formatf("%s%s%s%s", $1, $2, $3, $4); 
};

RESTOFARLIST:   ","<ADECLN> { $$ = formatf(", %s", $2); } |
                "" { $$=formatf("%s", ""); };

OWNDEC:         <PercentQNAMEquote><VSPECquote><HOLE><NAME>
                            <ALIASquote><CONSTquote><MARK><RESTOFOWNDEC><S> {
                  $$ = formatf("%s%s%s%s%s%s", $1, $2, $4, $5, $6, $8); 
} |
                "array"<PercentFORMATquote><VSPECquote><NAME>
                                       <ALIASquote><BPAIR><CONSTLIST> {
                  $$ = formatf("%s%s%s%s%s%s%s", "%array ", $2, $3, $4, $5, $6, $7); 
};

RESTOFOWNDEC:   ","<HOLE><NAME><ALIASquote><CONSTquote><MARK><RESTOFOWNDEC> {
                  $$ = formatf(", %s%s%s%s", $3, $4, $5, $7); 
} |
                "" { $$=formatf("%s", ""); };

XOWN:           "own" { $$ = formatf("%s", "%own "); } |
                "external" { $$ = formatf("%s", "%external "); } |
                "extrinsic" { $$ = formatf("%s", "%extrinsic "); } |
                "constant" { $$ = formatf("%s", "%constant "); } |
                "const" { $$ = formatf("%s", "%const "); };

CONSTLIST:      "="<READLINEquery><UNARY><COPERAND><RESTOFCEXPR>
                                                    <REPFACT><ROCL> {
                  $$ = formatf(" = %s%s%s%s%s", $3, $4, $5, $6, $7); 
} |
                "" { $$=formatf("%s", ""); };
ROCL:           ","<READLINEquery><UNARY><COPERAND><RESTOFCEXPR>
                                                    <REPFACT><ROCL> {
                  $$ = formatf(", %s%s%s%s%s", $3, $4, $5, $6, $7); 
} |
                "" { $$=formatf("%s", ""); };

REPFACT:        "\("<STAROREXPR>"\)" { $$ = formatf("(%s)", $2); } |
                "" { $$=formatf("%s", ""); };

RESTOFELIST:    ","<CEXPR><RESTOFELIST> {
                  $$ = formatf(", %s%s", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

PercentEVENTquote:    "event" { $$ = formatf("%s", "%event"); } |
                "" { $$=formatf("%s", ""); };

OPEXPR:         ","<EXPR> { $$ = formatf(", %s", $2); } |
                "" { $$=formatf("%s", ""); };

RESTOFSWLIST:   ","<NAME><NLISTquote>"\("<EXPR>":"<EXPR>"\)"<RESTOFSWLIST> {
                  $$ = formatf(", %s%s(%s : %s)%s", $2, $3, $5, $7, $9); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFREPEAT:   "until"<SC><RESTOFCOND> {
                  $$ = formatf("%s%s%s", " %until ", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFSS:       <COLON> { printf("{debug: label = TRUE}"); label = TRUE; $$ = $1; } |
                <S> { $$ = formatf("%s", ""); } |
                <PercentIU><SC><RESTOFCOND><S> {
                  $$ = formatf(" %s%s%s", $1, $2, $3); 
} |
                <PercentWU><S> {
                  $$ = formatf(" %s", $1); 
};

RESTOFIU:       "start"<NOTESTART> { delayed_ilev++; $$ = formatf(" %s", "%start"); } |
                "thenstart"<NOTESTART> { delayed_ilev++; $$ = formatf(" %s", "%then %start"); } |
                "then"<HOLE><UI><MARK><ELSEquote> {
                  $$ = formatf("%s%s%s", " %then ", $3, $5); 
};

AUI:            "and"<UI> { $$ = formatf("%s%s", " %and ", $2); } |
                "" { $$=formatf("%s", ""); };

ELSEquote:     "elsestart"<NOTESTART> { delayed_ilev++; $$ = formatf("%s", " %else %start"); } |
                "else"<PercentIU><HOLE><SC><RESTOFCOND><MARK><RESTOFIU> {
                  $$ = formatf("%s%s%s%s%s", " %else ", $2, $4, $5, $7); 
} |
                "else"<UI> { $$ = formatf("%s%s", " %else ", $2); } |
                "" { $$=formatf("%s", ""); };

ENAMEquote:    "_"<NAME><APP><ENAMEquote> {
                 $$ = formatf("_%s%s%s", $2, $3, $4); 
} |
                "" { $$=formatf("%s", ""); };

BPAIR:          "\("<EXPR>":"<EXPR><RESTOFBPLIST>"\)" {
                  $$ = formatf("(%s : %s%s)", $2, $4, $5); 
};

CONSTquote:    "="<UNARY><OPERAND><RESTOFCEXPR> {
                 $$ = formatf(" = %s%s%s", $2, $3, $4); 
} |
                "" { $$=formatf("%s", ""); };

PercentSEX:    "system" { $$ = formatf("%s", "%system "); } |
                "external" { $$ = formatf("%s", "%external "); } |
                "dynamic" { $$ = formatf("%s", "%dynamic "); } |
                "" { $$=formatf("%s", ""); };

CYCPARM:        <NAME>"="<EXPR>","<EXPR>","<EXPR> {
                  $$ = formatf("%s = %s, %s, %s", $1, $3, $5, $7); 
} |
                "" { $$=formatf("%s", ""); };

RESTOFRFDEC:    ","<RFDEC><RESTOFRFDEC> {
                  $$ = formatf(", %s%s", $2, $3); 
} |
                "" { $$=formatf("%s", ""); };

RFSTMNT:        "spec"<NAME> { $$ = formatf("%s%s", "%spec ", $2); } |
                <NAME>"\("<RFDEC><RESTOFRFDEC><ALTRFDEC>"\)" {
                  $$ = formatf("%s(%s%s%s)", $1, $3, $4, $5); 
};

RFREF:          <NAME> { $$ = $1; } |
                <RFDEC><RESTOFRFDEC><ALTRFDEC> {
                  $$ = formatf("%s%s%s", $1, $2, $3); 
};

RFDEC:          <TYPE><RFELMNT> {
                  $$ = formatf("%s%s", $1, $2); 
} |
                "\("<RFDEC><RESTOFRFDEC><ALTRFDEC>"\)" {
                  $$ = formatf("(%s%s%s)", $2, $3, $4); 
};

RFELMNT:        <PercentQNAMEquote><NAME><NLISTquote> {
                  $$ = formatf("%s%s%s", $1, $2, $3); 
} |
                "array"<ADECLN> { $$ = formatf("%s%s", "%array ", $2); };

ALTRFDEC:       "or"<RFDEC><RESTOFRFDEC><ALTRFDEC> {
                  $$ = formatf("%s%s%s%s", " %or ", $2, $3, $4); 
} |
                "" { $$=formatf("%s", ""); };

UCI:            "\*"<ATquote><NAME><OPTINC><S> { } |
                "PUT_"<ICONST><S> { } |
                <SETNEM><RESTOFUCI><S> { } |
                "CNOP_"<Nbyte>","<Nbyte><S> { } |
                <UCWRONG> { $$ = formatf("%s", $1); };

OPTINC:         "+"<N> { $$ = formatf("+%s", $2); } |
                "-"<N> { $$ = formatf("-%s", $2); } |
                "" { $$=formatf("%s", ""); };

ATquote:       "@" { $$ = formatf("%s", @1.text); } |
                "=" { $$ = formatf("%s", @1.text); } |
                "" { $$=formatf("%s", ""); };

RESTOFUCI:      <UCNOPS> { $$=$1; } |
                <UCUB><CEXPR> { } |
                <UCSB><CEXPR> { } |
                <UCW><CEXPR> { } |
                <UCUBUB><CEXPR>","<CEXPR> { } |
                <UCUBW><CEXPR>","<CEXPR> { };

UI:             <HOLE><NAME><APP><ENAMEquote><MARK><RESTOFUI><AUI> {
                  $$ = formatf("%s%s%s%s%s", $2, $3, $4, $6, $7); 
} |
                "->" "[1-9][0-9]*" { $$ = formatf("%sL%s", "-> ", @2.text); } |
                "->"<NAME><APP> { $$ = formatf("%s%s%s", "-> ", $2, $3); } |
                "return" { $$ = formatf("%s", "%return"); } |
                "result"<ASSOP><EXPR> { $$ = formatf("%s %s %s", "%result", $2, $3); } |
                "monitor"<AUI> { $$ = formatf("%s%s", "%monitor", $2); } |
                "stop" { $$ = formatf("%s", "%stop"); } |
                "signal"<PercentEVENTquote><CEXPR><OPEXPR> {
                  $$ = formatf("%s%s%s", "%signal %event ", $3, $4); 
} |
                "exit" { $$ = formatf("%s", "%exit"); } |
                "continue" { $$ = formatf("%s", "%continue"); };

SS:             <eof> { exit_flag = TRUE; } |
                <CHUI><HOLE><UI><MARK><RESTOFSS> {
                  if (!label) indent(ilev); label = FALSE;
                  printf("%s%s", $3, $5);
} |
                "[1-9][0-9]*:" "$" {
                   label = FALSE;
                   printf("L%s", @1.text); 
} |
                <TEXT> "$" { indent(ilev); printf("%s", $1); } |
                <PercentIU><HOLE><SC><RESTOFCOND><MARK><RESTOFIU><S> {
                  indent(ilev); printf("%s%s%s%s", $1, $3, $4, $6); 
} |
                "finish"<NOTEFINISH><ELSEquote><S> {
                  indent(--ilev); printf("%s%s", "%finish", $3); 
} |
                "cycle"<NOTECYCLE><CYCPARM><S> {
                  indent(ilev++); printf("%s %s", "%cycle", $3); 
} |
                "repeat"<NOTEREPEAT><RESTOFREPEAT><S> {
                  indent(--ilev); printf("%s%s", "%repeat", $3);
} |
                <PercentWU>"cycle"<NOTECYCLE><S> {
                  indent(ilev++); printf("%s %s", $1, "%cycle"); 
} |
                <CHTYPE><HOLE><TYPE><MARK><DECLN><S> {
                  indent(ilev); printf("%s%s", $3, $5); 
} |
                "end"<ENDLIST><S> {
                  indent(--ilev); printf("%s%s", "%end", $2); 
} |
                "recordformat"<RFSTMNT><S> {
                  indent(ilev); printf("%s%s", "%record %format ", $2); 
} |
                <HOLE><PercentSEX><RT><MARK><PercentSPECquote><NAME>
                                                    <ALIASquote><FPP><S> {
                  indent(ilev); printf("%s%s%s%s%s%s", $2, $3, $5, $6, $7, $8); 
                  if (*$5 == '\0') ilev++;
} |

                <XOWN><TYPE><OWNDEC> {
                  indent(ilev); printf("%s%s%s", $1, $2, $3); 
} |
                "include"<CONST><INCLUDE> {
                  indent(ilev); printf("%s %s", "%include", $2); 
} |
                "begin"<DOWN><HOLE><S> { indent(ilev++); printf("%s", "%begin"); } |
                "on"<TRACE><PercentEVENTquote><CEXPR><RESTOFELIST>"start"
                                                          <NOTESTART><S> {
                  indent(ilev++); printf("%s%s%s%s", "%on %event ", $4, $5, " %start"); 
} |
                "switch"<NAME><NLISTquote>"\("<EXPR>":"<EXPR>"\)"
                                                       <RESTOFSWLIST><S> {
                  indent(ilev); printf("%s%s%s(%s:%s)%s", "%switch ", $2, $3, $5, $7, $9); 
} |
                "list"<S><LISTON> { indent(ilev); printf("%s", "%list"); } |
                "else"<NOTEFINISH><DUMMYSTART><NOTESTART><S> {
                  indent(--ilev); ilev++; printf("%s", "%else"); 
} |
                "\*"<UCI> { indent(ilev); printf("*%s", $2); } |
                "trustedprogram"<S> { indent(ilev); printf("%s", "%trustedprogram"); } |
                "mainep"<NAME><S> { indent(ilev); printf("%s", "%mainep"); } |
                "control"<CONST><S> { indent(ilev); printf("%s%s", "%control ", $2); } |
                <NAME>"\(\*\):" { indent(ilev); printf("%s(*):", $1); } |
                <S> { };

ASSOP: "==" { $$=formatf("%s", @1.text); } |
       "=" { $$=formatf("%s", @1.text); } |
       "<-" { $$=formatf("%s", @1.text); } |
       "->" { $$=formatf("%s", @1.text); };

CHTYPE: "" { /* Lookahead hack to check that next item is a type */ };

CHUI: "" { };

COLON: ":" { $$=formatf("%s", @1.text); };

COMP: "==" { $$=formatf("%s", @1.text); } |
      "<=" { $$=formatf("%s", @1.text); } |
      ">=" { $$=formatf("%s", @1.text); } |
      ">" { $$=formatf("%s", @1.text); } |
      "->" { $$=formatf("%s", @1.text); } |
      "=" { $$=formatf("%s", @1.text); } |
      <notequivcomp> { $$=$1; } | <notcomp> { $$=$1; } |
      "<" { $$=formatf("%s", @1.text); };

notcomp: "#" { $$=formatf("%s", @1.text); } |
         "\\=" { $$=formatf("%s", @1.text); } |
         "<>" { $$=formatf("%s", @1.text); };

notequivcomp: "##" { $$=formatf("%s", @1.text); } |
              "\\==" { $$=formatf("%s", @1.text); };

COMPtwo: "==" { $$=formatf("%s", @1.text); } |
         "<=" { $$=formatf("%s", @1.text); } |
         ">=" { $$=formatf("%s", @1.text); } |
         ">" { $$=formatf("%s", @1.text); } |
         "->" { /* SOME OF THESE ARE INVALID IN A DOUBLE-SIDED */ $$=formatf("%s", @1.text); } |
         "=" { $$=formatf("%s", @1.text); } |
         <notequivcomp> { $$=$1; } | <notcomp> { $$=$1; } |
         "<" { $$=formatf("%s", @1.text); };

CONST: <realconst> { $$=$1; } |
       <basedconst> { $$=$1; } |
       <intconst> { $$=$1; } |
       <stringconst> { $$=$1; } |
       <lettercharconst> { $$=$1; };

realconst: "[0-9][0-9]*\.[0-9][0-9]*E[0-9][0-9]*" { $$=formatf("%s", @1.text); } |
           "[0-9][0-9]*\.[0-9][0-9]*" { $$=formatf("%s", @1.text); } |
           "[0-9][0-9]*@[0-9][0-9]*" { $$=formatf("%s", @1.text); };

basedconst: "[0-9][0-9]*_[0-9A-Z][0-9A-Z]*" { $$=formatf("%s", @1.text); };

intconst: "[0-9][0-9]*" { $$=formatf("%s", @1.text); };

stringconst: <TEXTTEXT> { $$=$1; } |
             "E" <TEXTTEXT> { $$=formatf("E%s", $2); /* EBCDIC string */ };

lettercharconst: <squote> <schar> <squote> {
                   $$=formatf("'%s'", doubleup($2, '\'')); 
} |
                 "[RHX]" <squote> <hexchars> <squote> {
                   $$=formatf("%s'%s'", @1.text, $3); 
} |
                 "B" <squote> <binchars> <squote> {
                   $$=formatf("%s'%s'", @1.text, $3); 
} |
                 "K" <squote> <octchars> <squote> {
                   $$=formatf("%s'%s'", @1.text, $3); 
} |
                 "[CMD]" <squote> <schars> <squote> {
                   /* I'm not sure what the syntax of D'...' is */
                   /* so I'll allow anything, for now           */
                   $$=formatf("%s'%s'", @1.text, doubleup($3, '\'')); 
};

squote: "'" { };

schar: <squote><squote> { $$=formatf("%s", "'"); } |
       <!squote> "." { $$=formatf("%s", @2.text); };

schars: <schar> <schars> { $$=formatf("%s%s", $1, $2); } |
        "" { $$=formatf("%s", ""); };

hexchars: "[0-9A-Fa-f][0-9A-Fa-f]*" { $$=formatf("%s", @1.text); };

octchars: "[0-7][0-7]*" { $$=formatf("%s", @1.text); };

binchars: "[01][01]*" { $$=formatf("%s", @1.text); };

DOWN: "" { /* Internal routine to push a level */ };

DUMMYAPP: "" { };

DUMMYSTART: "" { };

HOLE: "" { };

ICONST: "[0-9][0-9]*" { $$=formatf("%s", @1.text); };

INCLUDE: "" { /* called after CONST */ };

LISTOFF: "" { };

LISTON: "" { };

MARK: "" { };

NAME: "[A-Z][A-Z0-9]*" <!eitherquote> { $$=formatf("%s", strlwr(@1.text)); };

eitherquote: "['\"]" { /* Protects M'fred' or E"string" from being seen as 
                          simple names M or E */ };

NOTECYCLE: "" { };

NOTEFINISH: "" { };

NOTEREPEAT: "" { };

NOTESTART: "" { };

N: "[0-9][0-9]*" { /* 16-bit */ $$=formatf("%s", @1.text); };

Nbyte: "[0-9][0-9]*" { /* between 0 and 255 */ $$=formatf("%s", @1.text); };

OP: "+" { $$=formatf("%s", @1.text); } |
    "-" { $$=formatf("%s", @1.text); } |
    "\&" { $$=formatf("%s", @1.text); } |
    "\*\*\*\*" { $$=formatf("%s", @1.text); } |
    "\*\*" { $$=formatf("%s", @1.text); } |
    "\*" { $$=formatf("%s", @1.text); } |
    "!!" { $$=formatf("%s", @1.text); } |
    "!" { $$=formatf("%s", @1.text); } |
    "//" { $$=formatf("%s", @1.text); } |
    "/" { $$=formatf("%s", @1.text); } | 
    ">>" { $$=formatf("%s", @1.text); } |
    "<<" { $$=formatf("%s", @1.text); } |
    "\." { $$=formatf("%s", @1.text); } |
    "\\\\" { $$=formatf("%s", @1.text); } |
    "\\" { $$=formatf("%s", @1.text); };

READLINEquery: "$" { $$=formatf("%s", "\n"); } |
               "" { $$=formatf("%s", ""); };

SETNEM: "[A-Z][A-Z0-9]*" <optsub> { /* Set Mnemonic */ $$=formatf("%s_", @1.text); };

optsub: "_" { $$=formatf("%s", @1.text); } |
        "" { $$=formatf("%s", ""); };

S: "$" { /* also ';', but we convert earlier */ } |
   ";" { };

TEXTTEXT: <quote> <stringchars> <quote> {
            $$=formatf("\"%s\"", doubleup($2, DQUOTE));
};

quote: "\"" { $$=formatf("%s", "\""); };

stringchars: <quote><quote> <stringchars> { $$=formatf("%s", "\""); } |
             <!quote> ".[^\"]*" <stringchars> {
               $$=formatf("%s%s", @2.text, $3);
} |
             "$" <stringchars> {
               $$=formatf("\n%s", $2);
} |
             "" { $$=formatf("%s", ""); };

TEXT: "#.*" { $$=formatf("%s", @1.text); } |
      "!.*" { $$=formatf("%s", @1.text); } |
      "comment.*" { $$=formatf("%s%s", "%comment", @1.text+7); };

TRACE: "" { };

UCNOPS: "" { /* Opcode with no parameters */ };

UCSB: "" { /* signed byte opd */ };

UCUBUB: "" { /* two unsigned byte operands */ };

UCUBW: "" { /* unsigned byte and a word operand */ };

UCUB: "" { /* unsigned byte opd */ };

UCWRONG: ".*" {
           /* error or embedded m/c for some other processor? */
           $$=formatf("%s", @1.text); 
};

UCW: "" { /* word operand */ };

UP: "" { };

commaquote: "," { $$=formatf("%s", ", "); } |
            <!rparen> "" { /* fast error recovery */ $$=formatf("%s", ""); };

rparen: "\)" { $$=formatf("%s", @1.text); };

UNARY: "+" { $$=formatf("%s", @1.text); } |
       "-" { $$=formatf("%s", @1.text); } |
       "\~" { $$=formatf("%s", @1.text); } |
       "\\" { $$=formatf("%s", @1.text); } |
       "" { $$=formatf("%s", ""); };

%{

extern int debug(const char *fmt, ...);
extern int debug_enter(const char *fmt, ...);
extern int debug_exit(const char *fmt, ...);

extern FILE *yyin;

int eof_parse(YYTYPE **p)
{
  int c;
  c = fgetc(yyin);
  if (c == EOF) {
    return(TRUE);
  }
  ungetc(c, yyin);
  return(FALSE);
}

int setdebug_parse(YYTYPE **p)
{
  _debug = TRUE;
  return(TRUE);
}

int candebug_parse(YYTYPE **p)
{
  _debug = FALSE;
  return(TRUE);
}

%}