// cc -c skimpcs.c -I /home/gtoal/src/compilers101/new-parser/imps

#include <perms.h>

#ifndef NDEBUG
// Although gcc supports array bounds checking for arrays based at 0, it
// does not handle arrays with non-zero lower bounds.  Indeed neither does C,
// so the non-stanard macro processor below is a hack to add Imp-style lower
// and upper bounds to C arrays.  With bounds checking!
extern int _C(int x, char *name, int lower, int upper, char *filename, int line);
#define _C(x, name, lower, upper) _C((x), name, (lower), (upper), __FILE__, __LINE__)
#else
// No array bound checking
#define _C(x, name, lower, upper) (x)
#endif

extern int A[500 /*1:500*/];
$define A[x] A[(x)-1]
extern int Condopt;
void Expr(int Exprp);
void Dump(_imp_string Opn, _imp_string Reg, _imp_string Base, int Disp);
void Filllabel(int Label);
int Fillbranch(int Label);
int Nextplabel(void);
void Fault(_imp_string Mess);
int Condflag = 0;
int Cond(int Condp, int Tlabel, int Flabel) {
  auto void Processcond(int Condp);
  auto void Test(int Ltestp);
  auto void Condrest(int Condrestp);
  auto void Store(int Testp, int Level, int Andor);
  auto void Show(_imp_string An, int A, int P);
  static const _imp_string True[6 /*1:6*/] = {
      _imp_str_literal("BZ"), _imp_str_literal("BNZ"), _imp_str_literal("BNG"),
      _imp_str_literal("BL"), _imp_str_literal("BNL"), _imp_str_literal("BG")};
$define True[x] True[(x)-1]
  static const _imp_string False[6 /*1:6*/] = {
      _imp_str_literal("BNZ"), _imp_str_literal("BZ"), _imp_str_literal("BG"),
      _imp_str_literal("BNL"), _imp_str_literal("BL"), _imp_str_literal("BNG")};
$define False[x] False[(x)-1]
  _imp_string Opn;
  static const int Index[17 /*1:17*/] = {1,  2,  3,  4,  5,  6,  7,  8, 9,
                                         10, 11, 12, 13, 14, 15, 16, 17};
$define Index[x] Index[(x)-1]
  int Testpa[16 /*1:16*/];
$define Testpa[x] Testpa[(x)-1]
  int Levela[16 /*1:16*/];
$define Levela[x] Levela[(x)-1]
  int Andora[16 /*1:16*/];
$define Andora[x] Andora[(x)-1]
  int Brancha[16 /*1:16*/];
$define Brancha[x] Brancha[(x)-1]
  int Labela[17 /*1:17*/];
$define Labela[x] Labela[(x)-1]
  int P;
  int Pp;
  int Ppp;
  int Testp;
  int Level;
  int Andor;
  int Comp;
  Level = 0;
  P = 1;
  Processcond(Condp);
  Store(Testp, -1, 1);
  Store(0, -2, 2);
  P -= 2;
  for (Pp = 1; Pp <= P; Pp++) {
    Level = Levela[Pp];
    Andor = Andora[Pp];
    for (Ppp = Pp + 1; Ppp <= P + 1; Ppp++)
      if (Levela[Ppp] < Level) {
        Level = Levela[Ppp];
        if (Andora[Ppp] != Andor) break;
      }
    Brancha[Pp] = Ppp + 1;
  }
  if (Tlabel >= 0) {
    Andora[P] = 2;
    Brancha[P] = P + 1;
    Labela[P + 1] = Tlabel;
  }
  Labela[P + 2] = Flabel;
  for (Pp = 1; Pp <= P; Pp++)
    if (Labela[Brancha[Pp]] < 0) Labela[Brancha[Pp]] = Nextplabel();
  if (Condopt == 1) {
    Newline();
    Show(_imp_str_literal("      "), *Index, P + 2);
    Show(_imp_str_literal("TESTP "), *Testpa, P);
    Show(_imp_str_literal("LEVEL "), *Levela, P + 1);
    Show(_imp_str_literal("ANDOR "), *Andora, P + 1);
    Show(_imp_str_literal("BRANCH"), *Brancha, P);
    Show(_imp_str_literal("LABEL "), *Labela, P + 2);
    Newline();
  }
  for (Pp = 1; Pp <= P; Pp++) {
    if (Labela[Pp] >= 0) Filllabel(Labela[Pp]);
    Condflag = 1;
    Expr(Testpa[Pp]);
    Comp = A[A[Testpa[Pp] + 2]];
    if (Andora[Pp] == 1)
      Opn = False[Comp];
    else
      Opn = True[Comp];
    Dump(Opn, _imp_str_literal("ACC"), _imp_str_literal(""),
         Fillbranch(Labela[Brancha[Pp]]));
  }
  if (Labela[P + 1] >= 0 && Tlabel < 0) Filllabel(Labela[P + 1]);
  if (Flabel >= 0)
    return (-1);
  else
    return (Labela[P + 2]);
  void Processcond(int Condp) {
    Test(A[Condp + 1]);
    Condrest(A[Condp + 2]);
  }
  void Test(int Ltestp) {
    if (A[Ltestp] == 1)
      Testp = Ltestp;
    else {
      Level++;
      Processcond(A[Ltestp + 1]);
      Level--;
    }
  }
  void Condrest(int Condrestp) {
    int Andor;
    Andor = A[Condrestp];
    if (Andor != 3) do {
        Store(Testp, Level, Andor);
        Test(A[Condrestp + 1]);
        Condrestp = A[Condrestp + 2];
      } while (A[Condrestp] != 2);
  }
  void Store(int Testp, int Level, int Andor) {
    if (P > 16) {
      Fault(_imp_str_literal("CONDITION TOO LONG"));
      exit(0);
    }
    Testpa[P] = Testp;
    Levela[P] = Level;
    Andora[P] = Andor;
    Labela[P] = -1;
    P++;
  }
  void Show(_imp_string An, int A, int P) {
    int Pp;
    Printstring(_imp_join(An, _imp_str_literal("  ")));
    for (Pp = 1; Pp <= P; Pp++) Write(A, 5);
    Newline();
  }
}
