/*  Convert the concrete syntax tree into an abstract syntax tree.
           (the grammar itself is also embedded in this section) */

/*
    looks like currently a bug in declaration of pointer to struct (like 'c' in this code)



    separate problem - confirmed:

    BAD:   struct sa {int a;} s;

    GOOD:  struct sa {int a;};
           struct sa s;

    the bad one doesn't generate the declaration of s.
*/
// TO DO: current outstanding bug - this constuct is not recognised:

// statements other than declarations are valid at top-level (eg compound-block)

// char line[512], *s;    -- See P_rest_of_decl:

// *++fp   (due to grammar misunderstanding)
// *--pp

// sizeof(int)

// 123L  123LL  0xff 077

// doubles & floats

// enums?  Or maybe 'left as an exercise'?

// const declarations, and using them in folding constants

// WON'T DO:

// unions

/*  This is primarily the main 'build_ast()' procedure, which is
    actually where the grammar of the language is defined.  The grammar
    is extracted from in-line comments, and converted into a table by
    the 'takeon' program which you can find in the same directory as
    this file.

    (You can view the extracted grammar in file "grammar.g")
    The style of compiler on which this design is based actually goes
    directly from concrete syntax tree to code generation - but that was
    from the days when memory was tight.  Since most modern compilers -
    and especially books about them - are AST-based, we'll take that
    extra step here in order to give our students an AST-based compiler
    to experiment with.

    *a++ = 1;  is not allowed although a++ = 1; is! - bug in lvalue?

    Another ambiguity for the lexer:   a & &b;   a && b;

    We need a tree-walker that assigns type information bottom-up
    from terminals.  Resets the type on a cast.

    vars need a type field

    we need temps

    we need to serialise the AST for SSA elimination

    Convert PUSH &x; <expr>; POPI   into   <expr>; POP x

    Deliberate language restrictions:

    no typedefs  (use #define)
    no '...' parameters (varargs)
    no procedures as parameters

    possibly will simplify expressions later.

    These would be extensions anyway, but we're still not doing them:

    no inline procedures (though may add later if easy)
    no nested procedures


 */

int build_ast_inner(int ap, int caller)
{
#define build_ast(ap) build_ast_inner(ap, __LINE__)
  /*  Main code-generation procedure.  This is called after parsing, 
     with parameters which describe the parse tree.  By jumping to the
     corresponding statement in the large 'case' below, we execute the
     actions associated with the parse-tree nodes.

     The grammar which was used to build the parse tables is extracted
     from the source below (from comments marked with "//\\") and the
     tables are built with the associated 'takeon' program. (See this
     same directory for the source.  It's quite short...)

     See http://www.difranco.net/cop2220/op-prec.htm if you need to
     convince yourself of some of the more bizarre peculiarities of
     C's operator precedence rules.

This one looks like a bug however:
    int test(void)
    {
      int j, k = 0;
      j = k++ ++;
    }
/tmp/test.c:4: error: lvalue required as increment operand

- I think that is allowed in this grammar.  Mind you better to remove
  ++ and -- altogether...  (make it a 'small c' suitable to compile
  kdf9emul.c subset)
   */

  int T[100];

  int saved_ap;
  int phrase;  // A[ap] is the phrase number. A[ap+1] is the alt.
  int alt;     // For consistency, in BIPs, the Alt should always be 1
               // although that may not be the case at the moment :-(
  int phrases; // defined subphrases

  //fprintf(stdout, "\"grammar.c\", %d: build_ast(%d);\n", caller, ap);

  saved_ap = ap;
  phrase = A[ap++] & DROP_SPECIAL_BITS;
  alt = A[ap++];
  phrases = A[ap++];

  if ((phrase < 512) || (phrase > MAX_PHRASE+512)) {
    fprintf(stderr, "* Internal error: build_ast(%d) called from line %d - phrase = %d\n",
	    ap, caller, phrase); exit(1);
  }

  switch (phrase) {

    /*  Built-in phrases */
// BIPS (Built-in Phrases) are linked to the type-code returned
// by the line-reconstruction code (aka lexer)

// These *must* come first.  These are pre-recognised by the line reconstruction code) similar
// to how lex tokenises input) so if you have any place in your source where an item could be
// ambiguous at the lexical level, do not handle it by line-reconstruction - instead have the
// parser build it up from individual characters.

// Parse-time phrase definitions.  Implemented by C code *as we parse*.  Anything done here
// on an unsuccessful parse track *must* be undone by an explicit failure item before returning
// from that track. (that will make more sense when you see an example)

// BIPS are never actually called in the current code.  (all the
// work in building them is done at line reconstruction time)

//\\ B<EOD>=0;
  case P_EOD:
    return -1;

//\\ B<TAG>=1;
  case P_TAG: 
//\\ B<PPP>=2;
  case P_PPP: 
//\\ B<MMM>=3;
  case P_MMM: 
//\\ B<CHAR>=4;
  case P_CHAR:
//\\ B<KEYWORD>=5;
  case P_KEYWORD:
//\\ B<integer-constant>=6;
  case P_integer_constant: 
//\\ B<character-constant>=7;
  case P_character_constant: 
//\\ B<string-constant>=8;
  case P_string_constant: 
    if (c[A[ap]].l > latest_line) latest_line = c[A[ap]].l;
    return A[ap];

//\\ #
//\\ # Phrase definitions.  SS is the main entry point.
//\\ #
 

// Regular phrase definitions.  SS is the main entry point.
// Note that the entire program is parsed before *any* code is executed

// <!phrase> means that the parse will continue past this item if <phrase> is *not* present
// at this point in the source.

// <?phrase> means that the parse will continue if the input at this point in the source
// does match <phrase>, *BUT* the parse pointer will not be stepped past the object and
// the input is still present to be matched by whatever follows.  This allows for quick
// context-sensitive checks (usually looking ahead quite a few tokens) to avoid a lot of
// unnecessary parsing.

// A phrase define by C<phrase> is implemented in C code and executed *at parse time*
// It may or may not match any tokens in the input stream.

// Depending on the support code for the parser, spaces and comments may or may not have already
// been removed.  Keywords in this grammar are given "thusly", and literal characters are
// matched in single quotes as in '='.  A single quote itself is matched by ''''

// We do not yet handle extended BNF constructs or regular expression (regexp) literal matching.

// inserted file:


// note fn/procedure declarations not yet included in the grammar
 
//\\ P<SS> = 
//\\    <statement> <SS>,
//\\    <EOD>;

case P_SS:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0]); // <expression>
    T[2] = build_ast(A[ap+1]); // <SS>
    return mkbinop(AST_SEQ, T[1], T[2]);
  } else               {
    return -1;
  }




//\\ P<level14> = 
//\\    "sizeof" '(' <type> <opt-pointerto-seq> ')',
//\\    <indirectthrough-seq> <opt-cast> <identifier> '(' <opt-argument-list> ')' <opt-postfix> <opt-post-ppp-or-mmm>,
//\\    <opt-cast> <identifier> '(' <opt-argument-list> ')' <opt-postfix>,
//\\    <identifier> <opt-postfix> <opt-post-ppp-or-mmm>,
//\\    <integer-constant>,
//\\    <character-constant>,
//\\    <string-constant>,
//\\    '(' <expression> ')';

case P_level14:
  // All of this is "TO DO:" fix this
  if (alt == 0)        {
    return -1;
#ifdef NEVER
    T[1] = -1;//build_ast(A[ap+0]); // <type> - currently in a state of flux!  TO DO
    T[2] = build_ast(A[ap+1]); // <opt-pointerto-seq>
    // TO DO: modify the type with the pointers if present
#endif
  } else if ((alt == 1) || (alt == 2)) {
    if (alt == 1) {
      T[1] = build_ast(A[ap+0]); // <indirectthrough-seq>
      T[2] = build_ast(A[ap+1]); // <opt-cast>
      T[3] = build_ast(A[ap+2]); // <identifier>
      T[4] = build_ast(A[ap+3]); // '(' <opt-argument-list> ')'
      T[5] = build_ast(A[ap+4]); // <opt-postfix>
      T[6] = build_ast(A[ap+5]); // <opt-post-ppp-or-mmm>
    } else {
      T[1] = -1;                 // absent <indirectthrough-seq>
      T[2] = build_ast(A[ap+0]); // <opt-cast>
      T[3] = build_ast(A[ap+1]); // <identifier>
      T[4] = build_ast(A[ap+2]); // '(' <opt-argument-list> ')'
      T[5] = build_ast(A[ap+3]); // <opt-postfix>
      T[6] = -1;                 // <opt-post-ppp-or-mmm>
    }

    // Question:   *fred(2)->j  ?   *(fred(2)->j) or (*fred(2))->j ????  Looks like the latter.  OR IS IT??? Confirm with GCC

    T[0] = mkterop(AST_Call, T[3], T[4], -1); // extra param will point to the defproc for the procedure 
    if (T[1] != -1) T[0] = mkmonop(T[1], T[0]);  // add pointers (ignore cast for now)

    if (T[5] != -1) { // opt-postfix1 first
      T[7] = T[5];
      while (leftchild(T[5]) != -1) T[5] = leftchild(T[5]);
      leftchild(T[5]) = T[0];
      T[0] = T[7];
    }

    if (T[6] != -1) { // then opt-post-ppp etc
      T[7] = T[6];
      while (leftchild(T[6]) != -1) T[6] = leftchild(T[6]);
      leftchild(T[6]) = T[0];
      T[0] = T[7];
    }

    return T[0];

  } else if (alt == 3) {
    T[1] = build_ast(A[ap+0]); // <identifier>
    T[2] = build_ast(A[ap+1]); // <opt-postfix>
    T[3] = build_ast(A[ap+2]); // <opt-post-ppp-or-mmm>
    if ((T[2] == -1) && (T[3] == -1)) return T[1]; // ident only
    if (T[2] == -1) return mkmonop(T[3], T[1]); // ident++ only
    T[0] = T[2];
    while (leftchild(T[2]) != -1) T[2] = leftchild(T[2]);
    leftchild(T[2]) = T[1];
    if (T[3] == -1) return T[0];
    return mkmonop(T[3], T[0]);
  } else if (alt == 4) {
    return integer_constant(ap+0); // build_ast(A[ap+0]); // TO DO: distinguish integer const in AST
  } else if (alt == 5) {
    return character_constant(ap+0); // build_ast(A[ap+0]); // TO DO: distinguish char const in AST
  } else if (alt == 6) {
    return string_constant(ap+0); // build_ast(A[ap+0]); // TO DO: distinguish string const in AST
  } else               {
    return build_ast(A[ap+0]); // <expression>
  }


//\\ P<level14-lv> = 
//\\    <indirectthrough-seq> <opt-cast> <identifier> '(' <opt-argument-list> ')' <opt-postfix> <opt-post-ppp-or-mmm>,
//\\    <opt-cast> <identifier> '(' <opt-argument-list> ')' <opt-postfix>,
//\\    <identifier-lv> <opt-postfix> <opt-post-ppp-or-mmm>;

  case P_level14_lv: // yuk.  duplicates code from elsewhere.  Should be common-factored.
  if (alt <= 1) {
    // <indirectthrough-seq> <opt-cast> <identifier> '(' <opt-argument-list> ')' <opt-postfix> <opt-post-ppp-or-mmm>,
    if (alt == 0) {
      T[1] = build_ast(A[ap+0]); // <indirectthrough-seq>
      T[2] = build_ast(A[ap+1]); // <opt-cast>
      T[3] = build_ast(A[ap+2]); // <identifier>
      T[4] = build_ast(A[ap+3]); // '(' <opt-argument-list> ')'
      T[5] = build_ast(A[ap+4]); // <opt-postfix>
      T[6] = build_ast(A[ap+5]); // <opt-post-ppp-or-mmm>
    } else {
      T[1] = -1;                 // absent <indirectthrough-seq>
      T[2] = build_ast(A[ap+0]); // <opt-cast>
      T[3] = build_ast(A[ap+1]); // <identifier>
      T[4] = build_ast(A[ap+2]); // '(' <opt-argument-list> ')'
      T[5] = build_ast(A[ap+3]); // <opt-postfix>
      T[6] = -1;                          // <opt-post-ppp-or-mmm>
    }

    // Question:   *fred(2)->j  ?   *(fred(2)->j) or (*fred(2))->j ????  Looks like the latter

    T[0] = mkterop(AST_Call, T[3], T[4], -1);
    if (T[1] != -1) T[0] = mkmonop(T[1], T[0]);  // add pointers (ignore cast for now)

    if (T[5] != -1) { // opt-postfix1 first
      T[7] = T[5];
      while (leftchild(T[5]) != -1) T[5] = leftchild(T[5]);
      leftchild(T[5]) = T[0];
      T[0] = T[7];
    }

    if (T[6] != -1) { // then opt-post-ppp etc
      T[7] = T[6];
      while (leftchild(T[6]) != -1) T[6] = leftchild(T[6]);
      leftchild(T[6]) = T[0];
      T[0] = T[7];
    }

    return T[0];
  } else {
    // <identifier-lv> <opt-postfix> <opt-post-ppp-or-mmm>;
    T[1] = build_ast(A[ap+0]); // <identifier>
    T[2] = build_ast(A[ap+1]); // <opt-postfix>
    T[3] = build_ast(A[ap+2]); // <opt-post-ppp-or-mmm>
    if ((T[2] == -1) && (T[3] == -1)) return T[1]; // ident only
    if (T[2] == -1) return mkmonop(T[3], T[1]); // ident++ only
    T[0] = T[2];
    while (leftchild(T[2]) != -1) T[2] = leftchild(T[2]);
    leftchild(T[2]) = T[1];
    if (T[3] == -1) return T[0];
    return mkmonop(T[3], T[0]);
  }


// TO DO: apparently, a[k]++->j.l is valid, so need to put the ++ back in the mix
// the way it was before... (and if there are any non-sensical combinations, weed
// them out after parsing)

//\\ P<opt-post-ppp-or-mmm> =
//\\    <PPP>,
//\\    <MMM>,
//\\    ;
case P_opt_post_ppp_or_mmm:
  if (alt == 0) return AST_Post_Inc;
  if (alt == 1) return AST_Post_Dec;
  return -1;


//\\ P<opt-postfix> = 
//\\    '[' <expression> ']' <opt-postfix>,
//\\    '.' <identifier> <opt-postfix>,
//\\    '-' '>' <identifier> <opt-postfix>,
//\\   ;


case P_opt_postfix:
  if (alt == 3) return -1;
  T[1] = build_ast(A[ap+0]); // <identifier> or <expression>
  T[2] = build_ast(A[ap+1]); // <opt-postfix>
  //fprintf(stderr, "opt-postfix: (alt %d) t1 = %d, t2 = %d\n", alt, T[1], T[2]);
  if (alt == 0)        {
    //fprintf(stderr, "T[1] = %d\n", T[1]);
    T[3] = mkbinop(AST_Idx, -1, T[1]);
  } else if (alt == 1) {
    T[3] = mkbinop(AST_Member, -1, T[1]);
  } else if (alt == 2) {
    T[3] = mkbinop(AST_Ptr, -1, T[1]);
  }
  if (T[2] == -1) return T[3];
  T[0] = T[2];
  while (leftchild(T[2]) != -1) T[2] = leftchild(T[2]);
  leftchild(T[2]) = T[3];
  return T[0];


//\\ P<function-call> = 
//\\    "sizeof" '(' <type> ')',
//\\    <opt-indirectthrough-seq> <opt-cast> <identifier> '(' <opt-argument-list> ')';

// also need sizeof(var) or sizeof(expr)???  does (var) become an expr?
case P_function_call:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0]); // <type>
    // AST_SizeOf - rightchild: 0 = var  1 = expr  2 = type
    return mkbinop(AST_SizeOf, T[1], 2); // TO DO: should fold to a literal, use new type mechanism
  } else               {
    // TO DO: opt-indirectthrough-seq, cast indirection
    T[1] = build_ast(A[ap+2]); // <identifier>
    T[2] = build_ast(A[ap+3]); // <opt-argument-list>
    return mkterop(AST_Call, T[1], T[2], -1);
  }
 
//\\ P<opt-argument-list> = 
//\\    <level1> <comma-opt-argument-list>,
//\\   ;

//\\ P<comma-opt-argument-list> = 
//\\    ',' <level1> <comma-opt-argument-list>,
//\\   ;

case P_opt_argument_list:
case P_comma_opt_argument_list:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0]); // <level1>
    T[2] = build_ast(A[ap+1]); // <comma-opt-argument-list>
    return mkbinop(AST_UseParam, T[1], T[2]);
  } else               {
    return mkbinop(AST_UseParam, -1, -1);
    // instead of -1, so that joe() has a parameter but that parameter is -1
  }

// TO DO: grammar needs a little tweaking to allow "*(type *)&x" as an lvalue
// (and need to check that "(type)var" is a valid lvalue too)

//\\ P<lvalue> = 
//\\    <indirectthrough-seq> <opt-cast> <level14> | <level14-lv>;

case P_lvalue:
  if (alt == 0) {
    T[1] = build_ast(A[ap+0]);
    T[2] = build_ast(A[ap+1]); // TO DO: handle casts
    T[3] = build_ast(A[ap+2]);

    T[0] = T[1];
    while (leftchild(T[1]) != -1) T[1] = leftchild(T[1]);
    leftchild(T[1]) = T[3];
    return T[0];  // TO DO: testing *fred = ...
  }
  return build_ast(A[ap+0]); // <level14-lv>
 
//\\ P<opt-cast> = <cast> | ;
case P_opt_cast:
  return -1; // TO DO:

//\\ P<level13> = 
//\\    <prefix-operator-lv> <level14-lv>,
//\\    <opt-prefix-operator> <level14>;

case P_level13:
  // these two can be folded if we don't change them again.
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0]); // <prefix-operator-lv>
    T[2] = build_ast(A[ap+1]); // <level14-lv>
    T[0] = T[1];
    while (leftchild(T[0]) != -1) T[0] = leftchild(T[0]);
    leftchild(T[0]) = T[2];
    return T[1];
  } else               {
    T[1] = build_ast(A[ap+0]); // <opt-prefix-operator>
    T[2] = build_ast(A[ap+1]); // <level14>
    if (T[1] == -1) return T[2];
    T[0] = T[1];
    while (leftchild(T[0]) != -1) T[0] = leftchild(T[0]);
    leftchild(T[0]) = T[2];
    return T[1];
  }
 
//\\ P<prefix-operator-lv> = 
//\\    <PPP>,
//\\    <MMM>,
//\\    <opt-indirectthrough-seq> <cast>,
//\\    '&',
//\\    "sizeof";

// all tuples terminate their leftchild branches with -1 for the following operand to come
case P_prefix_operator_lv:
  if (alt == 0)        {
    return mkmonop(AST_Pre_Inc, -1); // ++
  } else if (alt == 1) {
    return mkmonop(AST_Pre_Dec, -1); // --
  } else if (alt == 2) {
    T[1] = build_ast(A[ap+0]); // <opt-indirectthrough-seq>
    T[2] = build_ast(A[ap+1]); // <cast>
    if (T[1] == -1) return T[2];
    T[0] = T[1];
    while (leftchild(T[1]) != -1) T[1] = leftchild(T[1]);
    leftchild(T[1]) = T[2];
    return T[0];
  } else if (alt == 3) {
    return mkmonop(AST_AddressOf, -1); // &
  } else               {
    return mkbinop(AST_SizeOf, -1, 0); // rightchild: 0 = var  1 = expr  2 = type
  }
 
//\\ P<opt-prefix-operator> = 
//\\    <PPP>,
//\\    <MMM>,
//\\    '+',
//\\    '-',
//\\    '!',
//\\    '~',
//\\    <opt-indirectthrough-seq> <cast>,
//\\    <indirectthrough-seq>,
//\\    '&',
//\\    "sizeof",
//\\   ;


case P_opt_prefix_operator:
  if (alt == 0)        {
    return mkmonop(AST_Pre_Inc, -1); // ++
  } else if (alt == 1) {
    return mkmonop(AST_Pre_Dec, -1); // --
  } else if (alt == 2) {
    return mkmonop(AST_UPos, -1); // +
  } else if (alt == 3) {
    return mkmonop(AST_UNeg, -1); // -
  } else if (alt == 4) {
    return mkmonop(AST_UBoolNot, -1); // !    not sure if we need a UShortcutBoolNot cf BoolAnd etc?
  } else if (alt == 5) {
    return mkmonop(AST_UBitNot, -1); // ~
  } else if (alt == 6) {
    // TO DO: add <opt-indirectthrough-seq>
    // needs leftmost -1
    return build_ast(A[ap+0]); // <cast>
  } else if (alt == 7) {
    // needs leftmost -1
    return build_ast(A[ap+0]); // <indirectthrough-seq>
  } else if (alt == 8) {
    return mkmonop(AST_AddressOf, -1); // &
  } else if (alt == 9) {               // sizeof <rvalue>
    return mkbinop(AST_SizeOf, -1, 1); // rightchild: 0 = var  1 = expr  2 = type
    // shouldn't be called, or will be called as sizeof rvalue
  }
  return -1;


 
//\\ P<cast> = 
//\\    '(' <type> <opt-pointerto-seq> ')';

case P_cast:
  // TO DO: meeds work.  returned structure will be
  // indirectthough(indirectthrough(cast(var{-1},type)))

  // *** result ***MUST*** have a -1 in the terminal leftchild for a following operand

  T[1] = build_ast(A[ap+0]); // <type>  -- TO DO: make sure it is a valid AST_ object
  T[2] = build_ast(A[ap+1]); // <opt-pointerto-seq>
  if (T[2] == -1) return mkbinop(AST_Cast, -1, T[1]);
  T[0] = T[2];
  while (leftchild(T[2]) != -1) T[2] = leftchild(T[2]);
  leftchild(T[2]) = T[1];
  return mkbinop(AST_Cast, -1, T[0]);
 

  // I have split pointer-seq into pointerto-seq and indirectthrough-seq,
  // because one is used in type *definitions* and the other is used in the
  // code generator to perform an indirection through a pointer.


//\\ P<indirectthrough-seq> = 
//\\    '*' <opt-indirectthrough-seq>;

case P_indirectthrough_seq:
  // *** result ***MUST*** have a -1 in the terminal leftchild for a following operand
  return mkmonop(AST_IndirectThrough, build_ast(A[ap+0])); // <opt-indirectthrough-seq>
 
//\\ P<opt-indirectthrough-seq> = 
//\\    <indirectthrough-seq>,
//\\   ;

case P_opt_indirectthrough_seq:
  if (alt == 0) return build_ast(A[ap+0]); // <indirectthrough-seq>
  return -1;


//\\ P<pointerto-seq> = 
//\\    '*' <opt-pointerto-seq>;

case P_pointerto_seq:
  // *** result ***MUST*** have a -1 in the terminal leftchild for a following operand
  return mkmonop(AST_TYPE_PointerTo, build_ast(A[ap+0])); // <opt-pointerto-seq>
 
//\\ P<opt-pointerto-seq> = 
//\\    <pointerto-seq>,
//\\   ;

case P_opt_pointerto_seq:
  if (alt == 0) return build_ast(A[ap+0]); // <pointerto-seq>
  return -1;


//\\ P<opt-qualifier> = "const" | "volatile" | ;
case P_opt_qualifier:
  return alt; // TO DO:

//\\ P<opt-scope> = "auto" | "register" | "static" | "extern" | ;
case P_opt_scope:
  return alt; // TO DO:

// TYPE CODE
//  bottom 16 bits (15:0)  - size of data (eg ints are 4 bytes but it could be a struct - max member size 16K)
//     next 2 bits (17:16) - signed/unsigned/default
//  to come: scope/qualifier.  Maybe "inline".

//\\ P<closer> = ')';
case P_closer:
  return -1;

//\\ P<type> = 
case P_type:
  switch (alt) { // TO DO: still to handle signedness.  probably should use a better interface proc - no strings
//\\    "void",
  case 0: return TypeAtom("", "void");
//\\    "struct" <existing-structname>,
  case 1: return TypeStruct(build_ast(A[ap+0]), -1); // use this as type info.  Details will be plugged in by populate_types.c
//\\    "typeof" '(' <lvalue> ')',
  case 2: return -1; // TO DO: extract type from lvalue - AST_SizeOf and AST_TypeOf? - done before code gen?
//\\    <signed> "long" "long" "int",
  case 3: return TypeAtom(""/*build_ast(A[ap+0])*/, "long long");
//\\    <signed> "long" "long",
  case 4: return TypeAtom(""/*build_ast(A[ap+0])*/, "long long");
//\\    <signed> "long" "int",
  case 5: return TypeAtom(""/*build_ast(A[ap+0])*/, "long");
//\\    <signed> "long",
  case 6: return TypeAtom(""/*build_ast(A[ap+0])*/, "long");
//\\    <signed> "int",
  case 7: return TypeAtom(""/*build_ast(A[ap+0])*/, "int");
//\\    <signed> "short" "int",
  case 8: return TypeAtom(""/*build_ast(A[ap+0])*/, "short");
//\\    <signed> "short",
  case 9: return TypeAtom(""/*build_ast(A[ap+0])*/, "short");
//\\    <signed> "char",
  case 10: return TypeAtom(""/*build_ast(A[ap+0])*/, "char");
//\\    "FILE";
  case 11: return TypeAtom("", "void");
  default:
    return -1;
  }

#ifdef NEVER
  // TO DO: adding typeof properly, and hack for FILE (avoiding full implementation of typedefs)
  // struct size limited to 32K-1
  if (alt == 0) return 0;
  if (alt == 1) return (1<<16)-1; // TO DO: look up struct defn in name table and find size. ($1 is AST_TAG)
  if (alt != 11) T[0] = build_ast(A[ap+0]);
  if (alt == 2) return (1<<16)-1; // TO DO: typeof T[0]
  if (alt <= 4) return 8+T[0];
  if (alt == 7) return 4+T[0];
  if (alt <= 9) return 2+T[0];
  if (alt <= 10) return 1+T[0];
  return (1<<16)-1; // need to determine sizeof struct FILE.  Probably never needed.
#endif


//\\ P<signed> = "unsigned", "signed", ;
case P_signed:
  if (alt == 0) return 1<<16;
  if (alt == 1) return 2<<16;
  return 0; // let caller decide default.  (Is it always signed?  What about const char strings?)

//\\ P<level12> = 
//\\    <level13> <opt-level12>;

//\\ P<level11> = 
//\\    <level12> <opt-level11>;

//\\ P<level10> = 
//\\    <level11> <opt-level10>;

//\\ P<level9> = 
//\\    <level10> <opt-level9>;

//\\ P<level8> = 
//\\    <level9> <opt-level8>;

//\\ P<level7> = 
//\\    <level8> <opt-level7>;

//\\ P<level6> = 
//\\    <level7> <opt-level6>;

//\\ P<level5> = 
//\\    <level6> <opt-level5>;

//\\ P<level4> = 
//\\    <level5> <opt-level4>;

//\\ P<level3> = 
//\\    <level4> <opt-level3>;

// opt-conditional is a ternary operator, but it is still leftchild() that we tweak :-)
//\\ P<level2> = 
//\\    <level3> <opt-conditional>;
case P_level12:
case P_level11:
case P_level10:
case P_level9:
case P_level8: 
case P_level7:
case P_level6:
case P_level5:
case P_level4:
case P_level3:
case P_level2:
  T[1] = build_ast(A[ap+0]); // <level-N>
  T[2] = build_ast(A[ap+1]); // <opt-level-N-1>
  if (T[2] == -1) return T[1];
  T[0] = T[2];
  while (leftchild(T[0]) != -1) T[0] = leftchild(T[0]);
  leftchild(T[0]) = T[1];
  return T[2];




//\\ P<opt-level12> = 
//\\    <level12-op> <level13> <opt-level12>,
//\\   ;

//\\ P<opt-level11> = 
//\\    <level11-op> <level12> <opt-level11>,
//\\   ;

//\\ P<opt-level10> = 
//\\    <level10-op> <level11> <opt-level10>,
//\\   ;

//\\ P<opt-level9> = 
//\\    <level9-op> <level10> <opt-level9>,
//\\   ;

//\\ P<opt-level8> = 
//\\    <level8-op> <level9> <opt-level8>,
//\\   ;

//\\ P<opt-level7> = 
//\\    <level7-op> <level8> <opt-level7>,
//\\   ;

//\\ P<opt-level6> = 
//\\    <level6-op> <level7> <opt-level6>,
//\\   ;

//\\ P<opt-level5> = 
//\\    <level5-op> <level6> <opt-level5>,
//\\   ;

//\\ P<opt-level4> =
//\\    <level4-op> <level5> <opt-level4>,
//\\   ;

//\\ P<opt-level3> = 
//\\    <level3-op> <level4> <opt-level3>,
//\\   ;

case P_opt_level12:
case P_opt_level11:
case P_opt_level10:
case P_opt_level9:
case P_opt_level8:
case P_opt_level7:
case P_opt_level6:
case P_opt_level5:
case P_opt_level4:
case P_opt_level3:

  if (alt == 0)        { // also needs the parent to do the while (leftchild()==-1) trick...
                         // to get exprs like a * b / c correct.
    T[1] = build_ast(A[ap+0]); // <op>
    T[2] = build_ast(A[ap+1]); // <RHS>
    T[3] = build_ast(A[ap+2]); // <rest-of-expression>
    if (T[3] == -1) return mkbinop(T[1], -1, T[2]); // leave a hole
    T[0] = T[3];
    while (leftchild(T[3]) != -1) T[3] = leftchild(T[3]);
    leftchild(T[3]) = mkbinop(T[1], -1, T[2]); // move hole leftwards and downwards
    return T[0]; // (bug fixed: was returning T[3] !)
  }
  return -1;
 
//\\ P<level12-op> = 
//\\    '*',
//\\    '/',
//\\    '%';

case P_level12_op:
  if (alt == 0)        {
    return AST_Mul;
  } else if (alt == 1) {
    return AST_Div;
  } else               {
    return AST_Mod;
  }

//\\ P<level11-op> = 
//\\    '+',
//\\    '-';

case P_level11_op:
  if (alt == 0)        {
    return AST_Add;
  } else               {
    return AST_Sub;
  }
 
//\\ P<level10-op> = 
//\\    '<' '<',
//\\    '>' '>';

case P_level10_op:
  if (alt == 0)        {
    return AST_BitLsh;
  } else               {
    return AST_BitRsh; // 'Bit' as opposed to arithmetic shift - LSR not ASR.
  }
 
//\\ P<level9-op> = 
//\\    '<' '=',
//\\    '<',
//\\    '>' '=',
//\\    '>';

case P_level9_op:
  if (alt == 0)        {
    return AST_LE;
  } else if (alt == 1) {
    return AST_LT;
  } else if (alt == 2) {
    return AST_GE;
  } else               {
    return AST_GT;
  }

//\\ P<level8-op> = 
//\\    '=' '=',
//\\    '!' '=';

case P_level8_op:
  if (alt == 0)        {
    return AST_EQ;
  } else               {
    return AST_NE;
  }

//\\ P<level7-op> = 
//\\    <!ANDAND> '&';

case P_level7_op:
  return AST_BitAnd;
 
//\\ P<ANDAND> = '&&';
case P_ANDAND:
    return -1;

//\\ P<level6-op> = 
//\\    '^';

case P_level6_op:
  return AST_BitXor;
 
//\\ P<level5-op> = 
//\\    <!OROR> '|';

case P_level5_op:
  return AST_BitOr;

//\\ P<OROR> = '||';

case P_OROR:
  return -1;

//\\ P<level4-op> = 
//\\    '&' '&';

case P_level4_op:
  return AST_ShortcutBoolAnd; // unless overridden in a later optimisation

//\\ P<level3-op> = 
//\\    '|' '|';

case P_level3_op:
  return AST_ShortcutBoolOr; // ditto



  // BIG 'TO DO' HERE!  Each of the two results here must be of the same AST_TYPE_* so that a single
  // piece of code can assign the result of the conditional expression to something else.  This means
  // that if one item is smaller than the other, it must be widened (here) to match.

//\\ P<opt-conditional> = 
//\\    '?' <level3> ':' <level2>,
//\\   ;

case P_opt_conditional:
  if (alt == 0)        {
    T[1] = -1; // plugged in by caller
    T[2] = build_ast(A[ap+0]); // <level3>
    T[3] = build_ast(A[ap+1]); // <level2>
    return mk(AST_Cond, 3, T);
  } else               {
    return -1;
  }



//\\ P<level1> = 
//\\    <opt-assign-list> <level2>;

case P_level1:
  // need some right-associative magic for a = b = c = expr.
  // this is the only example of walking down the RHS looking for a hole.
  T[1] = build_ast(A[ap+0]); // <opt-assign-list>
  T[2] = build_ast(A[ap+1]); // <level2>
  if (T[1] == -1) return T[2];
  T[0] = T[1];
  while (rightchild(T[0]) != -1) T[0] = rightchild(T[0]);
  rightchild(T[0]) = T[2];
  //fprintf(stderr, "rightchild plugged with %d, returning %d\n", T[2], T[1]);
  return T[1];

//\\ P<equiv> = 
//\\    '=' '=';

case P_equiv:
  return -1;

//\\ P<opt-assign-list> = 
//\\    <lvalue> <!equiv> <assign-op> <opt-assign-list>,
//\\   ;

case P_opt_assign_list:
  if (alt == 0) {
    T[1] = build_ast(A[ap+0]); // <lvalue>
    T[2] = -1; // <!equiv>
    T[3] = build_ast(A[ap+2]); // <assign-op>
    T[4] = build_ast(A[ap+3]); // <opt-assign-list>
    //fprintf(stderr, "T[1] = %d, T[4] = %d\n", T[1], T[4]);
    return mkbinop(T[3], T[1], T[4]);
  } else {
    return -1;
  }



 
//\\ P<assign-op> = 
//\\    '=',
//\\    '+' '=',
//\\    '-' '=',
//\\    '*' '=',
//\\    '/' '=',
//\\    '%' '=',
//\\    '&' '=',
//\\    '^' '=',
//\\    '|' '=',
//\\    '<' '<' '=',
//\\    '>' '>' '=';

case P_assign_op:
  if (alt == 0)        { // =
    return AST_AssignTo;
  } else if (alt == 1) { // +=     TO DO: Note that code generation doesn't yet handle += etc...
    return AST_AddAss;
  } else if (alt == 2) { // -=
    return AST_SubAss;
  } else if (alt == 3) { // *=
    return AST_MulAss;
  } else if (alt == 4) { // /=
    return AST_DivAss;
  } else if (alt == 5) { // %=
    return AST_ModAss;
  } else if (alt == 6) { // &=
    return AST_BitAndAss;
  } else if (alt == 7) { // ^=
    return AST_BitXorAss;
  } else if (alt == 8) { // |=
    return AST_BitOrAss;
  } else if (alt == 9) { // <<=
    return AST_BitLshAss;
  } else               { // >>=
    return AST_BitRshAss;
  }
 
//\\ P<expression> = 
//\\    <level1> <opt-comma-level0-list>;
case P_expression:
  T[1] = build_ast(A[ap+0]); // <level1>
  T[2] = build_ast(A[ap+1]); // <level0-list>
  if (T[2] == -1) return T[1];
  return mkbinop(AST_CommaSEQ, T[1], T[2]);

//\\ P<opt-comma-level0-list> = 
//\\    ',' <level1> <opt-comma-level0-list>,
//\\   ;
case P_opt_comma_level0_list:
  if (alt == 1) return -1;
  T[1] = build_ast(A[ap+0]); // <level1>
  T[2] = build_ast(A[ap+1]); // <opt-comma-level0-list>,
  if (T[2] == -1) return T[1];
  return mkbinop(AST_CommaSEQ, T[1], T[2]);

 
//\\ P<rvalue> = 
//\\    <expression>;

case P_rvalue:
  return build_ast(A[ap+0]); // <expression>

 
//\\ P<identifier> = 
//\\    <TAG>;

case P_identifier:
  T[1] = mkmonop(AST_TAG, build_ast(A[ap+0]));
  return T[1];
  //  return lookup_identifier(T[1]); // WRONG!!!! can only lookup at codegen stage when walking the AST
  //  return mkbinop(AST_Var, T[1], 0); // 0 reflects 'value of', 1 is 'address of'

//\\ P<identifier-lv> = 
//\\    <TAG>;

case P_identifier_lv:
  T[1] = mkmonop(AST_TAG, build_ast(A[ap+0]));
  return T[1];
  // look up in name table, return an AST object that includes the name and the type info
  //  return lookup_identifier(T[1]); // WRONG!!!! can only lookup at codegen stage when walking the AST
  //  return mkbinop(AST_Var, T[1], 0); // 0 reflects 'value of', 1 is 'address of' *** TESTING A BUGFIX!!! ***


// empty statements come via the <expression-statement> rule
// reorder rules with literals and keyword choices first, minimise backtracking?

//\\ P<statement> = '{' <compound-statement> '}' |
//\\                <labeled-statement> |
//\\                <declaration> |
//\\                <expression-statement> |
//\\                <selection-statement> | 
//\\                <iteration-statement> |
//\\                <jump-statement> |
//\\                <debug-statement>;
case P_statement:
  T[1] = build_ast(A[ap+0]);
  //fprintf(stderr, "STATEMENT: alt=%d ast=%d\n", alt, T[1]);
  return T[1];


//\\ P<debug-statement> = '?' <statement>;
case P_debug_statement:
  return mkmonop(AST_SHOWTREE, build_ast(A[ap+0]));


//\\ P<labeled-statement> = <identifier> ':' <statement> |
//\\                        "case" <constant-expression> ':' <statement> |
//\\                        "default" ':' <statement>;
case P_labeled_statement:
  if (alt == 0) {
    return mkbinop(AST_SEQ, mkbinop(AST_Label, build_ast(A[ap+0]), -1),
                            build_ast(A[ap+1]));
  } else if (alt == 1) {
    T[1] = build_ast(A[ap+0]);
    T[2] = -1; // points to top of switch
    T[3] = -1; // integer holding internal label number for this jump address, used in code generation stage
    return mkbinop(AST_SEQ, mk(AST_Case, 3, T), // first -1 for enclosing switch/compound, second for label
                            build_ast(A[ap+1]));
  }
  return mkbinop(AST_SEQ, mkbinop(AST_DefaultCase, -1, -1), // -1's same as above
                          build_ast(A[ap+0]));


//\\ P<constant-expression> = <expression>;
case P_constant_expression:
  // TO DO: fold, then check that top of tree is a const.  Otherwise semantic error.
  T[1] = build_ast(A[ap+0]);
  //  if (astop(T[1] != AST_Const)) { // Can't - not folded yet!
  //    fprintf(stderr, "* Error: expression is not constant\n"); exit(1);
  //  }
  return T[1];

//\\ P<expression-statement> = <opt-expression> ';';
case P_expression_statement:
  return build_ast(A[ap+0]);

//\\ P<opt-expression> = <expression> | ;
case P_opt_expression:
  if (alt == 1) return -1;
  return build_ast(A[ap+0]);


//\\ P<compound-statement> = <opt-declaration-list> <opt-statement-list>;
  case P_compound_statement: // returns an AST_Scope containing the block
  // First, handle scope:

  // dummy for AST_Scope creation
  T[1] = -1; // this block
  T[2] = -1; // parent block
  T[3] = BLOCK_COMPOUND; // type  (Do we need to handle procedural scope here too?)

  T[4] = mk(AST_Scope, 3, T); // only tricky thing with this definition of AST_Scope is procedure params
  rightchild(T[4]) = current_scope;

  // Now handle block

  //  push_scope(T[4]);
  T[5] = build_ast(A[ap+0]); // <opt-declaration-list>
  T[6] = build_ast(A[ap+1]); // <opt-statement-list>;
  //  pop_scope();

  //if (T[5] == -1) return T[6]; // may be -1
  //if (T[6] == -1) return T[5]; // decls only, no code?  Maybe side effects in decl initialisation?
  T[7] = mkbinop(AST_SEQ, T[5], T[6]);

  leftchild(T[4]) = T[7];
  return T[4];

//\\ P<opt-declaration-list> = <declaration-list> | ;
case P_opt_declaration_list:
  if (alt == 1) return -1; // TO DO:
  return build_ast(A[ap+0]);

//\\ P<declaration-list> = <declaration> <opt-declaration-list> ;
case P_declaration_list:
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  return mkbinop(AST_SEQ, T[1], T[2]); // TO DO:



//\\ P<declaration> = <struct-decl> |
//\\                  <scalar-decl> |
                      // note these are procedures or forward declarations but not procedure variables:
//\\                  <proc-fn-decl> |
//\\                  <array-decl>;
case P_declaration:
  return build_ast(A[ap+0]);


  // no procedure variables in structs (or elsewhere)

//\\ P<struct-member-declaration> = <struct-decl> |
//\\                  <scalar-decl> |
//\\                  <array-decl>;
case P_struct_member_declaration:
  return build_ast(A[ap+0]);

  // This is a declaration of a struct type, not an actual specific struct of that type.  I think.
  // Do we need an "AST_DeclareStruct" for this, to parallel AST_Declare for variables? (we'er
  // not doing that, nor using AST_Declare.  Currently using AST_TYPE_Struct)

//\\ P<struct-member-list> = <struct-member-declaration> <struct-member-list>, ;
case P_struct_member_list:
  if (alt == 1) return -1;
  {int name, type, declp;
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);

  declp = T[1];
  for (;;) {
    if (astop(declp) == AST_SEQ) {
      declp = leftchild(declp);
    } else if (astop(declp) == AST_Declare) {
      name = leftchild(declp);
      type = rightchild(declp);
      break; // from loop
    }
  }

  T[0] = TypeStructMember(T[2], name, type); // TO DO: pull info out of declaration
  }
  return T[0];


  // TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO
  // SHIT.  Major design flaw.  DeclName & DeclType cannot look up the type info until the scope
  // structure is in place, and that is currently being done *after* the AST building. Are we going to
  // have to implement the scoping calls during this phase too???
  // TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO  TO DO


  // CURRENT MEGA-TO DO:
  // looks like we may need to restructure *ALL* declarations so that they are
  // AST_Declare name AST_TYPE_* initial, otherwise picking apart all these declarations
  // so that the relevant pieces can be put in the struct member list is going to be a nightmare...???

// P<opt-struct-decl-list> = ???
// really need to fit this into the framework of <opt-scope> <opt-qualifier> <type> <opt-pointerto-seq> <identifier> <opt-scalar-init> <opt-rest-of-scalar-decl> ';';

//\\ P<struct-decl> = "struct" <new-structname> '{' <struct-member-list> '}' ';';
// <opt-struct-decl-list>;
//       struct sa {int a;} s; is *not* a declaration of s!  It is a declaration of
//    struct sa followed by a use of the undeclared variable s.
//    So we add the declarations here and build a sequence of statements instead of one statement
case P_struct_decl:
  T[1] = build_ast(A[ap+0]);
  assert(astop(T[1]) == AST_TAG);
  T[2] = build_ast(A[ap+1]);    
  return TypeStruct(T[1]/*tag*/, T[2]/*linked list*/);


  // ALTHOUGH THERE IS A LOT OF BACKTRACKING HERE, I'M DELIBERATELY WRITING DECARATIONS LIKE THIS
  // UNTIL I GET THEM TO WORK, OTHERWISE IT IS WAY TOO CONFUSING!


//\\ P<opt-rest-of-scalar-decl> = ',' <opt-pointerto-seq> <identifier> <opt-scalar-init> <opt-rest-of-scalar-decl>, ;
case P_opt_rest_of_scalar_decl:
  if (alt == 1) return -1;
//\\ P<rest-of-scalar-decl> = <opt-pointerto-seq> <identifier> <opt-scalar-init> <opt-rest-of-scalar-decl> ';';
case P_rest_of_scalar_decl:
  T[1] = build_ast(A[ap+0]); // TO DO: if (pointers) T[1] = pointers(T[2], T[1])     
  T[2] = build_ast(A[ap+1]);    
  T[3] = build_ast(A[ap+2]); // if present, add initialisation code (GLA or explicit assign??? - TO DO student exercise)

  T[4] = build_ast(A[ap+3]);

  return mkbinop(AST_SEQ, mk(AST_Dummy, 3, T), T[4]); // or does it have to be 10 for show_ast to work???


//\\ P<scalar-decl> = <opt-scope> <opt-qualifier> <type> <rest-of-scalar-decl>;
case P_scalar_decl:
  {
    int seq = mkbinop(AST_SEQ, -1, -1);
    int topseq = seq;
    T[1] = build_ast(A[ap+0]);
    T[2] = build_ast(A[ap+1]);    
    T[3] = build_ast(A[ap+2]);    
    T[0] = build_ast(A[ap+3]); // linked list of *ident=n, 
    // T[0] is an AST_SEQ of multiple <rest-of-scalar-decl>s
    for (;;) {
      assert(astop(T[0]) == AST_SEQ);
      T[4] = leftchild(leftchild(T[0]));
      T[5] = rightchild(leftchild(T[0]));
      T[6] = nthchild(leftchild(T[0]), 2);
      // now it is as if we had a phrase:
      // <opt-scope> <opt-qualifier> <type> <opt-pointerto-seq> <identifier> <opt-scalar-init> ';';

      if (T[4] != -1) fprintf(stderr, "! Ptr to scalar declaration: %s\n", c[leftchild(T[5])].s);

      // *** handle the declaration here. ***
      leftchild(seq) = mkterop(AST_Declare, T[5], ConstructType(c[leftchild(T[5])].s, T[1], T[2], T[3], T[4], -1), T[6]);
                                               // ConstructType scope qualifier type pointers arrays
      rightchild(seq) = -1;
      T[0] = rightchild(T[0]);
      if (T[0] == -1) break;
      rightchild(seq) = mkbinop(AST_SEQ, -1, -1);
      seq = rightchild(seq);
    }
    return topseq;
  }

//\\ P<proc-fn-decl> = <opt-scope> <opt-qualifier> <type> <opt-pointerto-seq> <identifier> '(' <opt-param-list> ')' <forward-decl-or-actual-body>;
case P_proc_fn_decl:

  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);    
  T[3] = build_ast(A[ap+2]);    
  T[4] = build_ast(A[ap+3]);    
  T[5] = build_ast(A[ap+4]);    
  T[6] = build_ast(A[ap+5]);    
  T[7] = build_ast(A[ap+6]);    

  if (T[7] == -1) return -1; // For now.  TO DO: add node for forward declaration
  
                                // Body is an AST_Scope.  We unpick it and insert the
                                // parameter list before the compound-block body.

  assert(astop(T[7]) == AST_Scope);
  leftchild(T[7]) = mkbinop(AST_SEQ, DefToReceive(T[6]), leftchild(T[7]));
                                     // ^ DefParam becomes ReceiveParam (copy, not in situ) inside
                                     //           the procedure body, for local references to find.


                                // maybe need new ast type.  tie defparams to proc defn for callers,
                                // and 'receiveparam's to receive the params inside the procedure?
             // (there may not be any code generated but we do have to assign base reg/offset etc)
                                // (We already have 'useparam's at the point of call)

  T[1] = ConstructType(c[leftchild(T[5])].s, /*scope*/ T[1], /*qualifier*/ T[2], /*type*/ T[3], /*pointers*/ T[4], /*arrays*/ -1);
  T[2] = T[5]; // Name
  T[3] = T[6]; // Params
  T[4] = T[7]; // Body
  return mk(AST_DefProc, 4, T); // first node is result type, will be plugged by parent
                                // DefProc: scope/qual/type/ptr-seq, id, params, body



// currently only one array dec per statement. will generalise once this is working...

//\\ P<array-decl> = <opt-scope> <opt-qualifier> <type> <opt-pointerto-seq> <identifier> <array-bounds> <opt-array-init> ';';
case P_array_decl:
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);    
  T[3] = build_ast(A[ap+2]);    
  T[4] = build_ast(A[ap+3]);    
  T[5] = build_ast(A[ap+4]);    
  T[6] = build_ast(A[ap+5]);    
  T[7] = build_ast(A[ap+6]);    

  fprintf(stderr, "! Array declaration: %s\n", c[leftchild(T[5])].s);

  return mkterop(AST_Declare,
                /*nametag*/T[5],
		 ConstructType(c[leftchild(T[5])].s, T[1], T[2], T[3], T[4], T[6]), // ConstructType scope qualifier type pointers arrays
		 /*init*/T[7]);


//\\ P<forward-decl-or-actual-body> = ';' | '{' <compound-statement> '}';
case P_forward_decl_or_actual_body:
  if (alt == 0) return -1; // TO DO: make a nametable entry for the forward declaration,
                           //        - necessary to allow mutual recursion
  T[1] = build_ast(A[ap+0]);
  assert(astop(T[1]) == AST_Scope);
  nthchild(T[1], 2) = BLOCK_PROCFN; // override BLOCK_COMPOUND
  return T[1];

//\\ P<array-bounds> = '[' <opt-constant-expression> ']' <opt-array-bounds>;
//\\ P<opt-array-bounds> = '[' <opt-constant-expression> ']' <opt-array-bounds> | ;
case P_array_bounds:
case P_opt_array_bounds:
  if (alt == 1) return -1;
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);  // TO DO: multi-d arrays
  return TypeArrayOf(/*child*/-1, /*index*/TypeAtom("signed", "int"), /*lower*/-1, /*upper*/T[1]);
                     // child plugged by caller
                                  // index is an integer
                                               // lower is always 0
                                                            // TO DO: upper if given


  // <constant-expression> originally included a comma-list, which caused confusion
  // the constant expression list may include inline structs... would have been
  // nicer to have separated those out at a higher level rather than include them here.

  // turns out this is valid C: int a[2] = {1, 2}, b[3] = {4, 5, 6}; - need to fix initializer
  // think this means that a <constant-initializer> may be an <array-init>.

//\\ P<opt-array-init> = '=' '{' <constant-initializer-list> '}', ;
case P_opt_array_init:
  if (alt == 1) return -1; // TO DO: only partially done
  return mkbinop(AST_AssignTo, -1,  build_ast(A[ap+0]));

//\\ P<constant-initializer-list> = <constant-initializer> <rest-of-constant-initializer-list>;
//\\ P<rest-of-constant-initializer-list> = ',' <constant-initializer> <rest-of-constant-initializer-list> |
//\\                                       ',' |
//\\                                       ;
case P_constant_initializer_list:
case P_rest_of_constant_initializer_list:
  if (alt >= 1) return -1;
  return mkbinop(AST_CommaSEQ,  build_ast(A[ap+0]),  build_ast(A[ap+1])); // TO DO: only partially done

  // struct initialisation
//\\ P<constant-initializer> = <constant-expression>, '{' <constant-initializer-list> '}';
case P_constant_initializer:
  return build_ast(A[ap+0]);

//\\ P<opt-scalar-init> = '=' <level1> | ;
case P_opt_scalar_init:
  // TO DO: expression *must not* allow comma expressions.  So I've replaced <expression> with <level1>
  //        - probably should use <rvalue> but that is currently pointing to <expression>
  if (alt == 1) return -1;
  return mkbinop(AST_AssignTo, -1,  build_ast(A[ap+0]));
             // TO DO: need to add code into declaration AST.  Depends if known at compile/link time
             //                                                or needs to be computed at decl time.

//\\ P<opt-param-list> = "void" <?closer>, <param> <opt-rest-of-param-list>;
case P_opt_param_list:
  if (alt == 0) return -1;
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  nthchild(T[1], 2) = T[2];
  return T[1];

//\\ P<opt-rest-of-param-list> = ',' <param> <opt-rest-of-param-list> | ;
case P_opt_rest_of_param_list:
  if (alt == 1) return -1;
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  nthchild(T[1], 2) = T[2];
  return T[1];

// if we want things like "const int" parameters, need to add "const" here...
//\\ P<param> = <type> <opt-pointerto-seq> <identifier> <opt-empty-array-bounds>;
case P_param:
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  T[3] = build_ast(A[ap+2]);
  T[4] = build_ast(A[ap+3]);

  T[1] = T[3];
  T[2] = ConstructType(c[leftchild(T[1])].s, /*scope*/ -1, /*qualifier*/ -1, /*type*/ T[1], /*pointers*/ T[2], /*arrays*/ T[4]);
  T[3] = -1; // to be plugged by parent
  return mk(AST_DefParam, 3, T);

#ifdef NEVER
  T[1] = build_ast(A[ap+2]); // ident
  T[2] = build_ast(A[ap+0]); // type derived from <type> and <opt-pointerto-seq> and <opt-empty-array-bounds> ...
         T[4] = build_ast(A[ap+1]); // <opt-pointerto-seq>
         if (T[4] != -1) {
           // insert T[2] down left branch of T[4]?
           T[0] = T[4];
           while (leftchild(T[4]) != -1) T[4] = leftchild(T[4]);
           leftchild(T[4]) = T[2];
           T[2] = T[0];
	 } 
  // TO DO: still to add arrayof if needed
  T[3] = -1; // link to next plugged by parent
  return mk(AST_DefParam, 3, T);
#endif




//\\ P<opt-empty-array-bounds> = '[' <opt-constant-expression> ']' <opt-empty-array-bounds> | ;
case P_opt_empty_array_bounds:
  if (alt == 1) return -1;
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);  // TO DO: multi-d arrays
  return TypeArrayOf(/*child*/-1, /*index*/TypeAtom("signed", "int"), /*lower*/-1, /*upper*/T[1]);
                     // child plugged by caller
                                  // index is an integer
                                               // lower is always 0
                                                            // TO DO: upper if given

//\\ P<opt-constant-expression> = <constant-expression>, ;
case P_opt_constant_expression:
  if (alt == 1) return -1;
  return build_ast(A[ap+0]);

//\\ P<statement-list> = <statement> <opt-statement-list>;
//\\ P<opt-statement-list> = <statement> <opt-statement-list> | ;
case P_statement_list:
case P_opt_statement_list:
  if (alt == 1) return -1;
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  if (T[2] == -1) return T[1];
  return mkbinop(AST_SEQ, T[1], T[2]);

//\\ P<selection-statement> = "if" '(' <expression> ')' <statement> <opt-else-statement> |
//\\                          "switch" '(' <expression> ')' '{' <compound-statement> '}';
case P_selection_statement:
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  if (alt == 0) {
    T[3] = build_ast(A[ap+2]);
    if (T[3] == -1) return mkbinop(AST_IFTHEN, T[1], T[2]);
    return mk(AST_IFTHENELSE, 3, T); // extra fields for jump labels?
  }
  T[3] = -1; // extra hole for break label.
  T[4] = -1; // link to chain of case statements
  T[5] = -1; // link to default case
  return mk(AST_Switch, 5, T);

//\\ P<opt-else-statement> = "else" <statement> | ;
case P_opt_else_statement:
  if (alt == 1) return -1;
  T[1] = build_ast(A[ap+0]);
  //fprintf(stderr, "ELSE: %d\n", T[1]);
  return T[1];


//\\ P<iteration-statement> = "while" '(' <expression> ')' <statement> |
//\\                          "do" <statement> "while" '(' <expression> ')' ';' |
//\\                          "for" '(' <opt-expression> ';' <opt-expression> ';' <opt-expression> ')' <statement>;
case P_iteration_statement:
  // loops need jumpback, continue, and exit labels.  Store them in the tuple?
  // use ints, or generate private names and use regular 'label' AST items?
  T[1] = build_ast(A[ap+0]);
  T[2] = build_ast(A[ap+1]);
  // the labels *could* be allocated here rather than in the code generator.  May be more convenient.
  T[3] = -1; // exit_lab
  T[4] = -1; // continue_lab
  T[5] = -1; // branch_back_lab
  if (alt == 0) {
    return mk(AST_C_While, 2+3, T);
  } else if (alt == 1) {
    return mk(AST_C_DoWhile, 2+3, T);
  } else {
    if (T[2] == -1) T[2] = mkconst(TRUE); // for (init ; ; inc) ... is treated as for (init ; TRUE; inc ) ...
                                          // - we can let the code generator optimise the TRUE != 0 test away
    T[3] = build_ast(A[ap+2]);
    T[4] = build_ast(A[ap+3]);
    T[5] = -1; // ditto
    T[6] = -1;
    T[7] = -1;
    T[8] = -1; // body lab
    return mk(AST_C_ForLoop, 4+4, T);
  }

//\\ P<jump-statement> = "goto" <identifier> ';' |
//\\                     "continue" ';' |
//\\                     "break" ';' |
//\\                     "return" <opt-expression> ';';
case P_jump_statement:
  // Can we warn about an unlabelled statement after a jump statement?  eg "* Access?"
  // perhaps by putting <?unlabelled-statement> in the grammar and testing to see if it is non-null?
  // (or better, <!label>, where label of course includes case and default.
  // Nope.  Probably better done during the final (code-generating) tree-walk - combine
  // with generic dead code removal?
  if (alt == 0) {
    // goto has to look for a label within the current procedure.  If we allow nested
    // procedures, extreme care has to be taken when jumping out of a procedure.  May not
    // be allowed in C except via longjump()?

    // actually just about any label target you can see in any scope is a valid goto target - 
    // you can jump out of any lexical compound block to a parent block, and you can't jump
    // out of a procedure because you can't have statements outside of a procedure.  Any othe
    // block that has already been closed is no longer visible to you via the scoping rules.

    // slight runtime issue therefore when jumping to a parent compound-statement, when you
    // are jumping out of a block that had local declarations. OK when all your references
    // are via a single stack base, but what if you were keeping track of the stack depth
    // and indexing negatively from the (varying) stack top???

    T[1] = build_ast(A[ap+0]);
    return mkmonop(AST_Goto, T[1]);
  } else if (alt == 1) {
    // continue and break need to be within a loop, so we need a stack of blocks to chain back through. 
    return mkmonop(AST_C_Continue, -1); // -1 hooks back to enclosing loop
  } else if (alt == 2) {
    return mkmonop(AST_C_Break, -1); // -1 hooks back to enclosing loop
  } else {
    T[1] = build_ast(A[ap+0]);
    if (T[1] == -1) {
      return mkmonop(AST_Return, -1); // -1 hooks back to enclosing procedure.  warn if missing result
    } else{
      return mkbinop(AST_ReturnResult, T[1], -1); // -1 hooks back to enclosing fn. (& Check the type/spurious)
    }
  }


//\\ P<new-structname> = 
//\\    <TAG>;

case P_new_structname:
  // TO DO: vars should have a proper type field, and the struct name should be
  // treated like a type.  We need a proper name table now.
  T[1] = mkmonop(AST_TAG, build_ast(A[ap+0]));
  return T[1];

//\\ P<existing-structname> = 
//\\    <TAG>;

case P_existing_structname:
  // TO DO: vars should have a proper type field, and the struct name should be
  // treated like a type.  We need a proper name table now.

  // solution to the problem that has been bothering me...
  // return a struct type object with the name present but not the type info,
  // to be filled in on the populate_types.c pass

  T[1] = mkmonop(AST_TAG, build_ast(A[ap+0]));
  return T[1];

// end of inserted file.

default:
  if ((phrase >= 512) && (phrase < 512+MAX_PHRASE)) {
    semantic_error("* Missing case label P_%s:\n", phrasename[phrase-512]);
  } else {
    char temp[16];
    sprintf(temp, "%d", phrase);
    semantic_error("* Missing case label P_<<%s>>\n", temp);
  }
  }

//\\ E

  return -1; // RETURN ERROR TRIP. need assert here???
  
}
/*
We have a loop!

should be easy to find:

#6999 0x0804b9fc in build_ast_inner (ap=3, caller=1301) at grammar.c:200
#7000 0x0804e5b6 in build_ast_inner (ap=386, caller=1301) at grammar.c:1301
#7001 0x0804e5b6 in build_ast_inner (ap=253, caller=1292) at grammar.c:1301
#7002 0x0804e55a in build_ast_inner (ap=249, caller=1258) at grammar.c:1292
#7003 0x0804e40e in build_ast_inner (ap=79, caller=1124) at grammar.c:1258
#7004 0x0804dcb3 in build_ast_inner (ap=75, caller=1112) at grammar.c:1124
#7005 0x0804dc37 in build_ast_inner (ap=70, caller=1108) at grammar.c:1112
#7006 0x0804dc0b in build_ast_inner (ap=66, caller=1094) at grammar.c:1108
#7007 0x0804db58 in build_ast_inner (ap=61, caller=1270) at grammar.c:1094
#7008 0x0804e4a7 in build_ast_inner (ap=57, caller=1221) at grammar.c:1270
#7009 0x0804e223 in build_ast_inner (ap=16, caller=1124) at grammar.c:1221
#7010 0x0804dcb3 in build_ast_inner (ap=12, caller=1037) at grammar.c:1124
#7011 0x0804d89a in build_ast_inner (ap=8, caller=200) at grammar.c:1037
#7012 0x0804b9fc in build_ast_inner (ap=3, caller=1301) at grammar.c:200

*/
