// This is obj3d-indent.c

// This file and obj3d.g are the only two files that needed to be
// created manually to create the obj3d formatter - everything else
// is supplied or built automatically.  And this file was initially
// generated by gencomp, but subsequently manually edited to implement
// the indentation and symbol canonicalization:

// This module re-outputs the AST parse tree as text, but reindents the
// ALGOL 60 code. It *does not* break lines anywhere or join lines - it
// just adjusts the indentation of the existing line structure.  A 'wc'
// of the file before and after should show matching line counts.

// It also canonicalises much of the representation of the source to the
// preferred Unicode characters, except in the few cases where the appropriate
// Unicode character is not well supported at the time of writing this code.


#define INDENT "   "
#define CONTINUATION "  "
int ilev = 0; /* nested begin/end for now.  Later use a stack */
int delayed_ilev = 0;
int continuation_spaces = 0;
int suppress_spaces = TRUE;

typedef struct text_descriptor {
  int start;
  int end;
} text_descriptor;

// NOTE! With the simple addition of a flag and a test, re-indentation
// could be made into a run-time option.

static wint_t lastwch = '\n';
void Outch(wint_t wch) {
  if (suppress_spaces) {
    if (wch == ' ' || wch == '\t') return; // remove existing indentation
    if (wch != '\n') {      // then replace with calculated indentation
      // indent here by ilev then go back to 'normal' mode
      int i;
      for (i = 0; i < ilev; i++) printf(INDENT);
      if (continuation_spaces == 2) printf(CONTINUATION);
      fprintf(stdout, "%lc", wch);  lastwch = wch;
      continuation_spaces = 2;
      suppress_spaces = FALSE;
    } else {
      fprintf(stdout, "\n");  lastwch = '\n';
      ilev += delayed_ilev; delayed_ilev = 0;
      // Continue to suppress leading spaces
    }
  } else {
    if (wch == '\n') {
      fprintf(stdout, "\n");  lastwch = '\n';
      ilev += delayed_ilev; delayed_ilev = 0;
      suppress_spaces = TRUE;
    } else {
      fprintf(stdout, "%lc", wch);  lastwch = wch;
      continuation_spaces = 2;
    }
  }
}

void O(int Leading_spaces, wchar_t *Literal) {
  int i;
  // leading spaces
  for (i = atom(Leading_spaces).start; i < atom(Leading_spaces).end; i++) {
    if (source(i).ch != ' ') break;
    Outch(source(i).ch);
  }
  // replacement text
  while (*Literal != '\0') Outch(*Literal++);
  i = atom(Leading_spaces).end;
  // trailing spaces.  Uncommon but can happen from a regex
  for (;;) {
    if (i <= atom(Leading_spaces).start) break;
    --i;
    if (source(i).ch != ' ') break;
    Outch(source(i).ch);
  }
}

#define PrintIndent(x) PrintIndent_inner(x, __LINE__)
void PrintIndent_inner(int Literal, int line) {
  int leading_space = TRUE, stropped = FALSE;
  int i;
//fprintf(stderr, "PrintIndent(%d) at line %d\n", Literal, line);
  for (i = atom(Literal).start; i < atom(Literal).end; i++) {
    Outch(source(i).ch);
  }
  if (stropped) Outch('\'');
}

text_descriptor reindent(int P, int depth) {
  text_descriptor r = {0,0};
//fprintf(stderr, "*** Reindent P=%d (%08x)\n", P, P);
  if ((P == 0) || (P == -1)) return r;
  int i;
  int AST_type = (int)((unsigned int)P&(((unsigned int)AST_type_mask)<<(unsigned int)AST_type_shift)); // runtime error: left shift of 15 by 28 places cannot be represented in type 'int' (or of >= 16 << 27 ...)
  int AST_index = P&AST_idx_mask;
  int alt;
  int count;

  if ((AST_type == AST_PHRASE) || (AST_type == PHRASE_TYPE)) {
    int i;
    int op = AST(AST_index+1);
//fprintf(stderr, "AST_type == AST_PHRASE: op=%d %ls\n", op, PHRASE(op));
    alt   = AST(AST_index+2);
    count = AST(AST_index+3);
//fprintf(stderr, "P=%d AST_type = %d  AST_index = %d  alt=%d  count=%d\n", P, AST_type>>AST_type_shift, AST_index, alt, count);

    // Let's test modifications to the tree walk by intercepting some phrases.
    // If you want to intercept a subset of some phrase (eg <NAME> but only in
    // the context of a procedure name, for example) then just add a dummy level
    // of indirection such as P<PROCNAME> = <NAME>;

    // This test will capitalize all <NAME>s.
    switch (op) {
      // What in yacc etc would be $1 is referred to here as SubPhrase(P,1)

//\\ E
    default: // Everything else can use the default output code:
//fprintf(stderr, "executing defaults  count=%d\n", count);
      for (i = 1; i <= count; i++) reindent(SubPhraseIdx/*Idx*/(P,i), depth+1);
    }

  } else if (AST_type == AST_ATOM_LIT) {

//fprintf(stderr, "executing LITERAL\n");
//fprintf(stderr, "P=%d AST_type = %d  AST_index = %d\n", P, AST_type>>AST_type_shift, AST_index);
    // Will only be called for phrases not specifically listed above.
    PrintIndent(AST_index);
  } else if (AST_type == AST_BIP) {
//fprintf(stderr, "executing BIP\n");
//fprintf(stderr, "P=%d AST_type = %d  AST_index = %d\n", P, AST_type>>AST_type_shift, AST_index);
    // Will only be called for phrases not specifically listed above.
    PrintIndent(AST_index);
  } else {
    //int op = AST(AST_index+1);
    //fprintf(stderr, "P=%d AST_type = %d  AST_index = %d\n", P, AST_type>>AST_type_shift, AST_index);
  }
  return r;
}
