head	1.5;
access;
symbols;
locks
	gtoal:1.5; strict;
comment	@ * @;


1.5
date	2021.11.09.00.34.31;	author gtoal;	state Exp;
branches;
next	1.4;

1.4
date	2021.11.08.17.51.00;	author gtoal;	state Exp;
branches;
next	1.3;

1.3
date	2021.11.08.08.40.28;	author gtoal;	state Exp;
branches;
next	1.2;

1.2
date	2021.11.07.18.00.00;	author gtoal;	state Exp;
branches;
next	1.1;

1.1
date	2021.11.07.03.23.55;	author gtoal;	state Exp;
branches;
next	;


desc
@Parser
@


1.5
log
@Incorporated cexpr into parser
@
text
@// TO DO: put spaces back in at line_reconstruction, and skip them in parser.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>

/*
  Currently we have a problem with the way whitespace is handled.  We cannot distinguish between these cases:

A=B++ + C;
A=B + ++C;
A=B+++C;

  (the latter case is dependent on the grammar)

  There are several ways this can be handled.  One is to allow spaces before any parse of a literal, but
  since we cheated in the grammar by breaking strings down into multiple single characters, that won't work
  unless literals are allowed to be multi-char.  Two ways of doing that - either handle a sequence of single
  characters in the grammar as if it were a string, or restructure the grammar to include pointers to strings -
  possibly reusing the keyword array. This option does not require a change to takeon.
  Another is to put explicit whitespace in the grammar. This should only be needed before literals, and
  could be inserted transparently by takeon. However this does mean that line reconstruction has to handle
  leaving whitespace in the filtered source text. Note that this means that comments may have to be replaced
  by a single space.
 */

#ifndef FALSE
#define FALSE (0!=0)
#define TRUE (0==0)
#endif

#define DROP_SPECIAL_BITS ((1<<14)-1)
#define NEGATED_PHRASE (1<<15)
#define OPTIONAL_PHRASE (1<<14)

#define MAX_PHRASES 1024
#define WHITESPACE (MAX_PHRASES-1)

char *progname;
static char outname[256];
FILE *outfile;

#define DEBUG_PARSER 1 // enable some extra data in header file
#define DEBUG_TOKENS 1 // debug line reconstruction
//#define DEBUG_AREC   1
int listing = 1;
#include "parser.h"              // GENERATED GRAMMAR FROM takeon.c and cexpr.g

int debug_parser = FALSE; // use -d option

void indent (int n, FILE * f) {
  fprintf (f, "%-*s", n*3, " ");
}

int bestparse = -1;             // for error reporting.
char *looking_for = "<UNKNOWN>";        // 'while looking for <PHRASENAME>' (or literal) ...

  // We use a stringpool, and strings are indexes into this pool.  This
  // is useful for the same reasons that the AST is an array indexed
  // by integers rather than a struct with pointers.  It may also
  // save space by reusing common strings.  And we get a free tag
  // to describe strings.  (Also we can compare strings just with
  // an integer tag comparison, if we ever want to)

  // not everything uses this yet.  search for malloc and realloc and strdup and free to find potential problems

#define MAXPOOL (1024*1024)
char stringpool[MAXPOOL];
int nextstring = 0;

int str_to_pool (char *s) {
  int tag;

  for (tag = 0; tag <= nextstring; tag++) {
    if (strcmp (stringpool + tag, s) == 0) {
      return tag;               /* found, one way or another */
    }
  }
}

  /* C[] is the source character token stream */

typedef struct sourceinfo {     // ATOMS for processed input stream
  char *s;                      // string contents
  int l;                        // lineno
  int col;                      // column
  int t;                        // type - tag, "string", 'charconst', or char, so far
  char *f;                      // source or includefile name
} sourceinfo;

static sourceinfo *c = NULL;
int c_nextfree = 0, c_arraysize = 0;
char *onecharstr;

/* A[] is the Analysis record.
     Contents point to a C[] structure when the item is a BIP,
     otherwise the format is [phraseno] [altno] [no-of-subphrases] [subphrases and/or BIPs...]
     for example, if A[25] contained 10, that would mean that the
     token for A[25] was stored in C[10].]
     (And the C string would be at &stringpool[c[A[25]]]?)
   */

static int *A = NULL;           /* Flex array, expanded using 'makespace' below */
int next_free_a = 0, a_size = 0;


////////////////////////////////////////////////////////////////////////////
int mktuple(int op, int alt, int count, int T[]) {
  /*  Create a tuple for an n-ary operator.  Pre-generated code
      just mirrors the Analysis record.  However we won't be
      using this - see "mk" for creating a real AST */
  int trip = -1;
  int parm;

  //  trip = nexttrip++;
  //  for (parm = 0; parm < count; parm++) {
  //    (void)T[parm]; nexttrip += 1;
  //  }
  return trip;
}

char *escape(char *s, int ch) {
  static char escaped[512];
  char *t = escaped;
  *t = '\0';
  if (s == NULL) return escaped;
  while (*s != '\0') {
    if (*s == '\n') {
      *t++ = '\\'; *t++ = 'n'; s++;
    } else if (*s == '\t') {
      *t++ = '\\'; *t++ = 't'; s++;
    } else if (*s == '\r') {
      *t++ = '\\'; *t++ = 'r'; s++;
    } else if (*s == '\\') {
      *t++ = '\\'; *t++ = '\\'; s++;
    } else if (*s == ch) {
      *t++ = '\\'; *t++ = ch; s++;
    } else *t++ = *s++;
  }
  *t = *s;
  return escaped;
}

int identifier(int ap)
{
  if (listing) fprintf(stdout, "%s", c[A[A[ap]+3]].s);
  return 0;
}

int string(int ap)
{
  if (listing) fprintf(stdout, "\"%s\"", escape(c[A[ap+4]].s, '"'));
  return 0;
}

int floating_constant(int ap)
{
  if (listing) fprintf(stdout, "%s", c[A[ap+4]].s);
  return 0;
}

int integer_constant(int ap)
{
  if (listing) fprintf(stdout, "%s", c[A[ap+4]].s);
  return 0;
}

int character_constant(int ap)
{
  if (listing) fprintf(stdout, "'%s'", escape(c[A[ap+4]].s, '\''));
  return 0;
}

int kw(int idx)
{
  if (listing) fprintf(stdout, " %s ", keyword[idx]);
  return 0;
}

int lit(int ch)
{
  if (listing) {
    fprintf(stdout, "%c", ch);
    if (ch == ';' || ch == '{') fprintf(stdout, "\n");
  }
  return 0;
}

////////////////////////////////////////////////////////////////////////////

static void *makespace_ (void *c, int nextfree, int *arraysize, int objsize) {
#define MINSIZE (1024*16)
  if ((c == NULL) || (*arraysize == 0) || (nextfree == 0)) {    // TOO MANY TESTS! Need to pick the right one!
    c = calloc (MINSIZE, objsize);
    *arraysize = MINSIZE - 1;
  } else if (nextfree >= *arraysize) {
    *arraysize = (*arraysize * 2) + 1;
    c = (void *) realloc (c, (*arraysize + 1) * objsize);       // 0:arraysize, inclusive. eg 0:15, 0:127 etc
  }
  if (c == NULL) {
    fprintf (stderr, "makespace: %s\n", strerror (errno));
    exit (errno);
  }
  return c;
}

/* variables used by line-reconstruction (lexer) */
FILE *sourcefile;
char *curfile;
int startline = TRUE, whitespace = TRUE, lineno = 1, col = 0, ch, peek;

#define makespace(c, nextfree, arraysize) c = (typeof(c))makespace_(c, nextfree, &arraysize, sizeof(c[0]))

static char xline[1024];        // make off heap instead, shorten binary
static int xi = 0, xcur_line = 1;

static void xungetc (int c, FILE * f) {
  if (xi > 0) {
    xi -= 1;
  } else if (c == '\n') {
    xcur_line -= 1;
    xi = strlen (xline);
  }
  ungetc (c, f);
}

static int last_line = -1;
static int xfgetc (FILE * f) {
  int c, ch;
 
  if ((c = fgetc (f)) == EOF)
    return EOF;
  ch = c & 255;
  if (ch == '\n') {
    xline[xi] = '\0';
    xi = 0;
    if (last_line != xcur_line) {
      strcpy (stringpool + nextstring, xline);  // (void)make_binary_tuple(LINE, xcur_line, str_to_pool(xline));
      last_line = xcur_line;
    }
    xcur_line += 1;
  } else
    xline[xi++] = ch;
  if (xi == 1023)
    xi = 1022;
  xline[xi] = '\0';
  return c;
}

void stores (char *s, int lineno, int col, int type, char *fname) {
  int tag;

  if (nextstring + strlen (s) + 1 >= MAXPOOL) {
    fprintf(stderr, "Internal error: nextstring + strlen (s) + 1 >= MAXPOOL\n");
    exit (1);
  }
  strcpy (stringpool + nextstring, s);  /* Create a backstop for when not found */
  tag = str_to_pool (s);
  if (tag == nextstring)
    nextstring += strlen (s) + 1;       /* Not found, add it */

  makespace (c, c_nextfree, c_arraysize);
  // fprintf(stderr, "c[%d] = \"%s\" type=%d\n", c_nextfree, stringpool+tag, type); // zxcv
  c[c_nextfree].s = stringpool + tag;
  c[c_nextfree].l = lineno;
  c[c_nextfree].col = col;
  c[c_nextfree].f = fname;
  c[c_nextfree].t = type;
  c_nextfree++;
}

void storec (int ch, int lineno, int col, int type, char *fname) {
  ch &= 255;
  onecharstr[ch * 2] = ch;
  onecharstr[ch * 2 + 1] = '\0';        // convert char to 1-char string before saving.
  stores (&onecharstr[ch * 2], lineno, col, type, fname);
}

void line_reconstruction (void) {
  /* Pre-process input ready for parsing.  Tokens are stored in array C[] */
  // go over this again and make sure 'col' is updated correctly at all places where chars are read.
  for (;;) {

    if ((ch = xfgetc (sourcefile)) == EOF)
      break;

    ch &= 255;                  // int, positive.

    peek = xfgetc (sourcefile);
    xungetc (peek, sourcefile);

    if (isalpha (ch)) {
      /* token or keyword */
      int token_nextfree = 0, strsize = 0, startcol = col;
      char *token = NULL;

      whitespace = FALSE;
      for (;;) {
        makespace (token, token_nextfree, strsize);
        if (isalpha (ch) || isdigit (ch) || (ch == '_')) {
          // digits and '_' allowed after 1st char.
          col++;
          token[token_nextfree++] = ch;
        } else {
          token[token_nextfree] = '\0';
          xungetc (ch, sourcefile);
          break;
        }
        ch = xfgetc (sourcefile);
      }
      stores (token, lineno, startcol, B_identifier, curfile);
      free (token);

    } else if (isdigit (ch)) {
      /* Number */
      int number_nextfree = 0, numsize = 0;
      char *number = NULL;
      int peek = xfgetc (sourcefile); xungetc (peek, sourcefile);
      if ((ch == '0') && ((peek == 'x') || (peek == 'X'))) {
        int startcol = col;
        makespace (number, number_nextfree, numsize);
        col++;
        number[number_nextfree++] = ch; // 0
        ch = xfgetc (sourcefile); // x
        col++;
        number[number_nextfree++] = ch; // x
        ch = xfgetc (sourcefile); // 0xA
        // Store as a string...
        whitespace = FALSE;
        for (;;) {
          makespace (number, number_nextfree, numsize);
          if (isxdigit (ch)) {
            col++;
            number[number_nextfree++] = ch;
          } else {
            if ((ch == 'u') || (ch == 'U')) {
              col++;
              number[number_nextfree++] = ch;
              number[number_nextfree] = '\0';
            } else {
              number[number_nextfree] = '\0';
              xungetc (ch, sourcefile);
            }
            break;
          }
          ch = xfgetc (sourcefile);
        }
        stores (number, lineno, startcol, B_integer_constant, curfile); // changed from col to startcol
        free (number);
      } else {
        int startcol = col;
        // Store as a string...
        whitespace = FALSE;
        for (;;) {
          makespace (number, number_nextfree, numsize);
          if (isdigit (ch)) {
            col++;
            number[number_nextfree++] = ch;
          } else {
            number[number_nextfree] = '\0';
            xungetc (ch, sourcefile);
            break;
          }
          ch = xfgetc (sourcefile);
        }
        stores (number, lineno, startcol, B_integer_constant, curfile); // changed from col to startcol
        free (number);
      }
    } else {
      switch (ch) {

      case '\'':               // Handle 'c' char const
      case '"':                // Handle "string"
        /* literals */
        {
          int string_nextfree = 0, strsize = 0, quotech = ch;
          char *string = NULL;

          whitespace = FALSE;
          col++;
          for (;;) {
            ch = xfgetc (sourcefile);   // Newlines are allowed
            col++;
            makespace (string, string_nextfree, strsize);
            if (ch == '\\') {
              ch = xfgetc (sourcefile);
              col++;
              if (ch == '\\') {
                string[string_nextfree++] = ch;
              } else if (ch == '\'') {
                string[string_nextfree++] = '\'';
              } else if (ch == '"') {
                string[string_nextfree++] = '"';
              } else if (ch == 'n') {
                string[string_nextfree++] = '\n';
              } else if (ch == 'r') {
                string[string_nextfree++] = '\r';
              } else if (ch == 't') {
                string[string_nextfree++] = '\t';
              } else {
                // Warn of unknown (to me) \x escape.  Probably an error.
                string[string_nextfree++] = '\\';
                string[string_nextfree++] = ch;
              }
            } else if (ch != quotech) {
              string[string_nextfree++] = ch;
            } else {
              string[string_nextfree] = '\0';
              break;
            }
          }

          if (quotech == '\'') {
            stores (string, lineno, col, B_character_constant, curfile);
            if (strlen (string) == 1) {
              // good char const
            } else if (strlen (string) <= 4) {
              // Warn that 'xx' as a 32-bit int is a non-standard extension
              fprintf(stderr, "Warning: multi-byte characters constants such as \'%s\' are not well supported.\n", escape(string, '\''));
            } else {
              // Warn that this is probably a string with the wrong type of quote.
              fprintf(stderr, "Warning: wrong kind of string quote used? - \'%s\'.\n", escape(string, '\''));
            }
          } else stores (string, lineno, col, B_string, curfile);
          free (string);
        }
        break;

      case '/':
        /* COMMENTS (or just a divide symbol) */
        col++;
        whitespace = FALSE;
        if (peek == '/') {
          // Handle line comment - not currently saved (although it could be)
          do {
            ch = xfgetc (sourcefile);
          }
          while (ch != '\n');
          lineno++;
          col = 0;
          whitespace = TRUE;
        } else if (peek == '*') {
          /* Handle potential multi-line comment - also not saved... */
          ch = xfgetc (sourcefile);     // Now we have read '/*'
          for (;;) {
            col++;
            ch = xfgetc (sourcefile);
            peek = xfgetc (sourcefile);
            if ((ch == '*') && (peek == '/'))
              break;
            xungetc (peek, sourcefile);
          }
          col += 2;
          whitespace = TRUE;
          //(void) xfgetc (sourcefile);   // Remove '/'
          // QUESTION: How does this affect # directives? 
        } else {
          storec (ch, lineno, col, B_char, curfile); // the '/'
        }
        break;

        // WHITESPACE - not stored, though it *might* be needed, e,g, for A + ++B and A++ + B which are both saved as A+++B :-(
      case '\n':
        lineno++;
      case '\r':
        startline = TRUE;
        col = 0;
        whitespace = TRUE;
        break;

      case '\t':
      case ' ':
        col++;                  // Previously did not affect whitespace.  Now does.
        whitespace = TRUE;
        // to check: startline
        break;

        // DIRECTIVES
      case '#':
        // should allow '#' as a directive if startline is true. (and not in a comment of course)
        
        // If we interpret any #-directives while lexing, we don't want to
        // do an expensive test on every token, so what we can do is set
        // a countdown timer on the introductory token (either this '#'
        // or the actual keyword such as 'ifdef') and then test that the
        // *previous* tokens match when the timer hits 0, eg
        // C[cp-3] == '#' && C[cp-2] == 'include' ... etc

        if (!whitespace) {
          // WARN: probably an error... should not be any '#' symbols in the
          // middle of a line. (This language uses "!=" for not-equal)
        }
        // Drop through

      default:
        if (whitespace) storec (' ', lineno, col, B_char, curfile);
        whitespace = FALSE;
        storec (ch, lineno, col++, B_char, curfile);
      }
    }
  }
  // set up a dummy at the end because we sometimes look ahead by 1
  // in the parsing code and don't want to hit uninitialised data.
  c[c_nextfree].t = B_EOF;
  c[c_nextfree].s = "<EOF>";
  c[c_nextfree].l = lineno;
  c[c_nextfree].col = col;
}

/* Parsing.  (Table driven top-down recursive-descent parser) */

/*  The parser used here is based on a design by Tony Brooker which was
    originally used in Atlas Autocode and the "Compiler Compiler".
    It generates a concrete syntax tree rather than the abstract
    syntax tree more popular in modern compilers.  A later phase
    converts from concrete to abstract.

    Note that the parsing procedure here is just a piece of code
    to walk a pre-built table.  There is nothing in this section
    which reflects the grammar, if that is what you are looking for.
    You'll find the grammar embedded in the 'compile()' procedure
    in the following section.
 */

int cp = 0;                     // code pointer.  Has to be global state.
int ap = 0;                     // Analysis record pointer.

int parse (int pp, int depth)   // depth is only for indentation in diags
{
  /* Main parsing procedure.  This is a table-driven parser, with the tables
     being generated from the grammar rules embedded in cexpr.c
   */
  int saved_cp, saved_ap, i, gp, alts, match;
  char saved_desc[1024*8];

  if ((pp&DROP_SPECIAL_BITS) == WHITESPACE) {
    fprintf(stderr, "Should never get here. Trying to parse <?sp> as an actual phrase rule.\n");
    i = 1/0;
    exit(1);
  }
  
  /* Initialisation */
  gp = phrase_start[pp - 512 - MAX_BIP];
  alts = gram[gp];

  /* Debugging */
  if (debug_parser) {
    fprintf (outfile, "\n%-*sPhrase %s/%d (%d alternatives) = ", depth*3, " ", phrasename[pp - 512], pp, alts);
    fflush (outfile);
  }

  gp++;                         // gp now points to first element (length) of first alt

  saved_cp = cp;
  saved_ap = ap;

  for (i = 0; i < alts; i++) {
    /* Starting with the root phrase, recursively examine each alternative */
    int each, phrases = gram[gp++], phrase_count, gap = 0;

    cp = saved_cp;
    ap = saved_ap;

    if (ap + 3 > next_free_a)
      next_free_a = ap + 3;
    makespace (A, next_free_a, a_size);

    A[ap++] = pp;               // record which phrase (could be done outside loop)
    A[ap++] = i;                // and which alt.

    // Count slots needed.  *Could* be precalculated and stored
    // in the grammar, either embedded (after the ALT) or as a
    // separate table

    for (each = 0; each < phrases; each++)
      if (gram[gp + each] >= 512)
        gap++;

    A[ap++] = gap;              // Count of alts (gap)
    // ap+gap now points to the slot after the space required, which
    // is where the first subphrase will be stored.
    ap = ap + gap;              // recursive subphrases are stored after this phrase.
    // ap is left updated if successful.

    // successfully parsed phrases are stored in A[saved_ap+3+n]

    if (saved_ap + 3 + gap > next_free_a)
      next_free_a = saved_ap + 3 + gap;
    makespace (A, next_free_a, a_size);

    if (debug_parser) {    // this loop is only for diagnostics
      char *saved_descp;

      fprintf (outfile, "\n%-*s", depth*3, " ");
      if (phrases == 0) {
        fprintf (outfile, "Alternative %d: (NULL phrase) ", i + 1);
      } else {
        fprintf (outfile, "Alternative %d: (%d phrases) ", i + 1, phrases);
      }
      saved_descp = saved_desc;
      *saved_descp = '\0';
      for (each = 0; each < phrases; each++) {
        int phrase = gram[gp + each];

        if (phrase < 256) {
          saved_descp += sprintf (saved_descp, " '%c'", phrase); // to handle white space, we want to check for it before a *sequence* of chars
        } else if (phrase < 512) {
          saved_descp += sprintf (saved_descp, " \"%s\"/%d", keyword[phrase - 256], phrase - 256); // or a string
        } else if (phrase < 512 + MAX_BIP) {
          saved_descp += sprintf (saved_descp, " {%s/BIP%d}", phrasename[phrase - 512], BIP[phrase - 512]); // or a BIP
        } else {
          saved_descp += sprintf (saved_descp, " <%s/%d>", (phrase&DROP_SPECIAL_BITS)==WHITESPACE ? "sp" : phrasename[phrase - 512], phrase);
        }
      }
      fprintf (outfile, "%s\n", saved_desc);
      fflush (outfile);
    }

    match = TRUE;               // stays true if all subphrases match
    phrase_count = 0;           // only phrases which make it into the A record,
    // i.e. excluding literals and keywords
    for (each = 0; each < phrases; each++) {
      /* Within a single grammar rule (alternative), ensure that each subphrase is present */
      int phrase = gram[gp + each];

      if (debug_parser) {
        fprintf (outfile,
                 "%-*sInput token stream = '%s' '%s' '%s' ...\n", depth*3, " ",
                 (cp < c_nextfree ? c[cp].s : "EOF"), (cp + 1 < c_nextfree ? c[cp + 1].s : "EOF"), (cp + 2 < c_nextfree ? c[cp + 2].s : "EOF"));
      }

      if (cp > bestparse) {
        static char s[1024];           // off heap instead? - binary size

        if (phrase < 256) {
          sprintf (s, "'%c'", phrase);
        } else if (phrase < 512) {
          sprintf (s, "\"%s\"", keyword[phrase - 256]);
        } else if (phrase < 512 + MAX_BIP) {
          sprintf (s, "{%s}", phrasename[phrase - 512]);
        } else {
          sprintf (s, "<%s>", (phrase&DROP_SPECIAL_BITS)==WHITESPACE ? "sp" : phrasename[phrase - 512]);
        }

        looking_for = s;
        bestparse = cp;
      }

      if (debug_parser)
        indent (depth, outfile);

      if (phrase < 256) {
        /* Literal */

        if (debug_parser)
          fprintf (outfile, "'%c'", phrase);

        if ((c[cp].t != B_char) || (c[cp].s[0] != phrase))
          match = FALSE;
        else
          cp++;
        // Don't record literals

      } else if (phrase < 512) {  /* Keyword */

        if (debug_parser)
          fprintf (outfile, "\"%s\"/%d", keyword[phrase - 256], phrase - 256);

        if ((c[cp].s == NULL) || (strcmp (keyword[phrase - 256], c[cp].s) != 0))
          match = FALSE;
        else
          cp++;
        // Don't record keywords

      } else if (phrase < (512 + MAX_BIP)) {  /* Built-in phrase */
        int where = ap;               // next phrase to be parsed will be stored at current 'ap'.

        if (debug_parser)
          fprintf (outfile, "{%s/BIP%d}", phrasename[phrase - 512], BIP[phrase - 512]);

        if (c[cp].t != BIP[phrase - 512]) {
          match = FALSE;
        } else {
          A[ap++] = phrase;
          A[ap++] = 1;
          A[ap++] = 1;
          A[ap++] = cp++;
          A[saved_ap + 3 + phrase_count++] = where;     // Record BIP
        }
      } else if ((phrase&DROP_SPECIAL_BITS)==WHITESPACE) {
        if (debug_parser) {
          fprintf (outfile, "Skip whitespace");
        }
        while ((c[cp].t == B_char) && (c[cp].s[0] == ' ')) {
          cp += 1;
          if (debug_parser) {
            fprintf (outfile, ".");
          }
        }
        match = TRUE;
      } else {
        /* Recursive call to parser for a subphrase */
        int where = ap;               // next phrase to be parsed will be stored at current 'ap'.

        
        if (debug_parser) {
          fprintf (outfile, "Try next alt: <%s/%d>", phrasename[phrase - 512], phrase);
        }

        if (!parse (phrase, depth + 1)) {
          if (debug_parser) {
            indent(depth, outfile);
            fprintf (outfile, "Alt <%s/%d> failed", phrasename[phrase - 512], phrase);
          }
          match = FALSE;
        } else {
          if (debug_parser) {
            indent(depth, outfile);
            fprintf (outfile, "Alt <%s/%d> succeeded", phrasename[phrase - 512], phrase);
          }
          // fprintf(stderr, "A[%d] = %d\n", saved_ap + 3 + phrase_count, where);
          A[saved_ap + 3 + phrase_count++] = where;
        }

      }

      if (debug_parser) {
        fprintf (outfile, "\n");
        indent (depth, outfile);
        fprintf (outfile, "Tried alternative %d: %s - result was %s\n", each + 1, saved_desc, (match ? "TRUE" : "FALSE"));
        fflush (outfile);
      }

      if (!match)
        break;

    }
    gp += phrases;              // move over all phrases, to next alt

    if (match)
      break;
    else if (debug_parser) {
      indent (depth, outfile);
      fprintf (outfile, "** Alternative %d FAILED.\n", i + 1);
    }
    // gp now points to first element (length) of next alt, or start of next phrase

  }

  return (match);

}

int build_ast_inner(int ap, int depth, int caller)
{
#define build_ast(ap, depth) build_ast_inner(ap, depth, __LINE__)
  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) {
// This is the grammar for a parser which will read the C code generated by my various
// static binary translators, and perform some source cleanups and optimisations in order
// to make the generated code more easily hand-maintainable.  It is not a decompiler
// however - the generated C will always be rather low-level.

// This code is loosely based on the grammar from:

//   https://cs.wmich.edu/~gupta/teaching/cs4850/sumII06/The%20syntax%20of%20C%20in%20Backus-Naur%20form.htm

// - unfortunately when you get down to using it, there are several errors and omissions in that grammar
// which had to be fixed.  Also, that grammar is written assuming a left-recursive style of parsing, whereas
// our parser has to be right-recursive or it will get into infinite loops.

// A few constructs (especially concerning lvalues vs rvalues) which were handled totally by
// semantic rules if using the grammar above have been handled using a slightly rewritten
// syntax in my modification of the grammar.

// Some of the ambiguities of that grammar were also fixed using the information in:

//   https://en.cppreference.com/w/c/language/operator_precedence

// Note that this grammar primarily parses C expressions - it does not handle flow control
// constructs (apart from one trivial one used in the SBT output) or declarations.
// Declarations would be needed for proper code generation, but since this will be a
// source to source filter, we can get away with a few shortcuts...

// These are built-in phrases which are handled by C code in parser.c rather than by grammatical rules:

//\\ B<char> = 0;
//\\ B<identifier> = 1;
//\\ B<integer-constant> = 2;
//\\ B<character-constant> = 3;
//\\ B<floating-constant> = 4;
//\\ B<string> = 5;
//\\ B<EOF> = 6;

// And here is the entire grammar.  The entry point is phrase "SS" (Source Statement).

// The grammar is extracted from the comments in this file using grep, and is converted
// into a set of tables using the program "takeon.c".

//\\ P<primary-expression> =
//\\    <identifier>,
//\\    <constant>,
//\\    <string>,
//\\    '(' <expression> ')';

// The code to take the parse tree and construct an AST from it was generated from the
// grammar, using the utility "create_parser.c".  The first iteration of the parse tree
// is really a "Concrete Syntax Tree" rather than an Abstract Syntax Tree, i.e. the
// generated code below initially builds an AST which reflects the raw text that was
// matched by the parser.  Eventually this code will be modified to produce a more
// realistic AST where the "A" truly does stand for "Abstract".

// At some point once the C code in this file is modified, it will no longer make sense
// to rebuild this file from the compiled grammar.

// Some work may be needed to re-order tree branches to ensure left-to-right and
// right-to-left order of precedence for some of the operations.

// The only known current issue is that standard C will lex an input stream of +'s
// by grabbing two at a time if available for '++', whereas my parser will actually
// parse "A=B+++++C;" as "A=B++ + ++C;" - standard C will see "A=B++ ++ + C;" and
// give a syntax error (because B++ is not an lvalue so cannot have the second ++ applied)
// However spaces if present are significant and disrupt the recognition of '++'...

// I don't *think* there are any circumstances where my parser would reject a valid
// input, or interpret a valid input differently from C, so I'll leave it as-is and
// allow us to accept some constructs that C would reject.  The SBTs that this is
// going to be used with would never generate such complex C code anyway.

case P_primary_expression:
  if (alt == 0)        {
    T[1] = identifier(ap+0);
    T[0] = mktuple(phrase /*P_primary_expression*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 1) {
    T[1] = build_ast(A[ap+0], depth+1); // <constant>
    T[0] = mktuple(phrase /*P_primary_expression*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 2) {
    T[1] = string(ap+0);
    T[0] = mktuple(phrase /*P_primary_expression*/, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <expression>
    T[3] = lit(')');
    T[0] = mktuple(phrase /*P_primary_expression*/, alt /*4*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  }
  return T[0];

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

case P_post_increment_op:
  if (alt == 0)        {
    T[1] = lit('+');
    T[2] = lit('+');
    T[0] = mktuple(phrase /*P_post_increment_op*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = lit('-');
    T[2] = lit('-');
    T[0] = mktuple(phrase /*P_post_increment_op*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<postfix-lvalue-expression> =
//\\    <primary-expression> <rest-of-postfix-expression>;

case P_postfix_lvalue_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <primary-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
  return mktuple(phrase /*P_postfix_lvalue_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-postfix-expression> =
//\\    '[' <expression> ']' <rest-of-postfix-expression>,
//\\    '(' <param-list> ')' <non-empty-rest-of-postfix-expression>,
//\\    '.' <identifier> <rest-of-postfix-expression>,
//\\    '->' <identifier> <rest-of-postfix-expression>,
//\\    <post-increment-op> <rest-of-postfix-expression>,
//\\   ;

case P_rest_of_postfix_expression:
  if (alt == 0)        {
    T[1] = lit('[');
    T[2] = build_ast(A[ap+0], depth+1); // <expression>
    T[3] = lit(']');
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 1) {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <param-list>
    T[3] = lit(')');
    T[4] = build_ast(A[ap+1], depth+1); // <non-empty-rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_expression*/, alt /*2*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 2) {
    T[1] = lit('.');
    T[2] = identifier(ap+0);
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_expression*/, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else if (alt == 3) {
    T[1] = lit('-');
    T[2] = lit('>');
    T[3] = identifier(ap+0);
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_expression*/, alt /*4*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 4) {
    T[1] = build_ast(A[ap+0], depth+1); // <post-increment-op>
    T[2] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_expression*/, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_postfix_expression*/, alt /*6*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<non-empty-rest-of-postfix-expression> =
//\\    '[' <expression> ']' <rest-of-postfix-expression>,
//\\    '(' <param-list> ')' <non-empty-rest-of-postfix-expression>,
//\\    '.' <identifier> <rest-of-postfix-expression>,
//\\    '->' <identifier> <rest-of-postfix-expression>,
//\\    <post-increment-op> <rest-of-postfix-expression>;

case P_non_empty_rest_of_postfix_expression:
  if (alt == 0)        {
    T[1] = lit('[');
    T[2] = build_ast(A[ap+0], depth+1); // <expression>
    T[3] = lit(']');
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_non_empty_rest_of_postfix_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 1) {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <param-list>
    T[3] = lit(')');
    T[4] = build_ast(A[ap+1], depth+1); // <non-empty-rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_non_empty_rest_of_postfix_expression*/, alt /*2*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 2) {
    T[1] = lit('.');
    T[2] = identifier(ap+0);
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_non_empty_rest_of_postfix_expression*/, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else if (alt == 3) {
    T[1] = lit('-');
    T[2] = lit('>');
    T[3] = identifier(ap+0);
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_non_empty_rest_of_postfix_expression*/, alt /*4*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <post-increment-op>
    T[2] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-expression>
    T[0] = mktuple(phrase /*P_non_empty_rest_of_postfix_expression*/, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<postfix-rvalue-expression> =
//\\    <primary-expression> <rest-of-postfix-rvalue-expression>;

case P_postfix_rvalue_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <primary-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-rvalue-expression>
  return mktuple(phrase /*P_postfix_rvalue_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-postfix-rvalue-expression> =
//\\    '[' <expression> ']' <rest-of-postfix-rvalue-expression>,
//\\    '(' <param-list> ')' <rest-of-postfix-rvalue-expression>,
//\\    '.' <identifier> <rest-of-postfix-rvalue-expression>,
//\\    '->' <identifier> <rest-of-postfix-rvalue-expression>,
//\\    <post-increment-op> <rest-of-postfix-rvalue-expression>,
//\\   ;

case P_rest_of_postfix_rvalue_expression:
  if (alt == 0)        {
    T[1] = lit('[');
    T[2] = build_ast(A[ap+0], depth+1); // <expression>
    T[3] = lit(']');
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_rvalue_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 1) {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <param-list>
    T[3] = lit(')');
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_rvalue_expression*/, alt /*2*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 2) {
    T[1] = lit('.');
    T[2] = identifier(ap+0);
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_rvalue_expression*/, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else if (alt == 3) {
    T[1] = lit('-');
    T[2] = lit('>');
    T[3] = identifier(ap+0);
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_rvalue_expression*/, alt /*4*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 4) {
    T[1] = build_ast(A[ap+0], depth+1); // <post-increment-op>
    T[2] = build_ast(A[ap+1], depth+1); // <rest-of-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_rest_of_postfix_rvalue_expression*/, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_postfix_rvalue_expression*/, alt /*6*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<signed> =
//\\    "signed",
//\\    "unsigned",
//\\   ;

case P_signed:
  if (alt == 0)        {
    T[1] = kw(/*"signed"*/ 0);
    T[0] = mktuple(phrase /*P_signed*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 1) {
    T[1] = kw(/*"unsigned"*/ 1);
    T[0] = mktuple(phrase /*P_signed*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[0] = mktuple(phrase /*P_signed*/, alt /*3*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<inttype> =
//\\    "short" <int>,
//\\    "long" "long" <int>,
//\\    "long" <int>;

case P_inttype:
  if (alt == 0)        {
    T[1] = kw(/*"short"*/ 2);
    T[2] = build_ast(A[ap+0], depth+1); // <int>
    T[0] = mktuple(phrase /*P_inttype*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 1) {
    T[1] = kw(/*"long"*/ 3);
    T[2] = kw(/*"long"*/ 3);
    T[3] = build_ast(A[ap+0], depth+1); // <int>
    T[0] = mktuple(phrase /*P_inttype*/, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[1] = kw(/*"long"*/ 3);
    T[2] = build_ast(A[ap+0], depth+1); // <int>
    T[0] = mktuple(phrase /*P_inttype*/, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<int> =
//\\    "int",
//\\   ;

case P_int:
  if (alt == 0)        {
    T[1] = kw(/*"int"*/ 4);
    T[0] = mktuple(phrase /*P_int*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[0] = mktuple(phrase /*P_int*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<type-specifier> =
//\\    "void",
//\\    <signed> "char",
//\\    <signed> <inttype>,
//\\    "float",
//\\    "double",
//\\    <typedef-name>;

case P_type_specifier:
  if (alt == 0)        {
    T[1] = kw(/*"void"*/ 5);
    T[0] = mktuple(phrase /*P_type_specifier*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 1) {
    T[1] = build_ast(A[ap+0], depth+1); // <signed>
    T[2] = kw(/*"char"*/ 6);
    T[0] = mktuple(phrase /*P_type_specifier*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 2) {
    T[1] = build_ast(A[ap+0], depth+1); // <signed>
    T[2] = build_ast(A[ap+1], depth+1); // <inttype>
    T[0] = mktuple(phrase /*P_type_specifier*/, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 3) {
    T[1] = kw(/*"float"*/ 7);
    T[0] = mktuple(phrase /*P_type_specifier*/, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 4) {
    T[1] = kw(/*"double"*/ 8);
    T[0] = mktuple(phrase /*P_type_specifier*/, alt /*5*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <typedef-name>
    T[0] = mktuple(phrase /*P_type_specifier*/, alt /*6*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<indirection-unary-ops> =
//\\    '*' <rest-of-indirection-unary-ops>;

case P_indirection_unary_ops:
  T[1] = lit('*');
  T[2] = build_ast(A[ap+0], depth+1); // <rest-of-indirection-unary-ops>
  return mktuple(phrase /*P_indirection_unary_ops*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-indirection-unary-ops> =
//\\    '*' <rest-of-indirection-unary-ops>,
//\\   ;

case P_rest_of_indirection_unary_ops:
  if (alt == 0)        {
    T[1] = lit('*');
    T[2] = build_ast(A[ap+0], depth+1); // <rest-of-indirection-unary-ops>
    T[0] = mktuple(phrase /*P_rest_of_indirection_unary_ops*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_indirection_unary_ops*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

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

case P_arithmetic_unary_op:
  if (alt == 0)        {
    T[1] = lit('+');
    T[0] = mktuple(phrase /*P_arithmetic_unary_op*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = lit('-');
    T[0] = mktuple(phrase /*P_arithmetic_unary_op*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<boolean-unary-ops> =
//\\    '!' <rest-of-boolean-unary-ops>;

case P_boolean_unary_ops:
  T[1] = lit('!');
  T[2] = build_ast(A[ap+0], depth+1); // <rest-of-boolean-unary-ops>
  return mktuple(phrase /*P_boolean_unary_ops*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-boolean-unary-ops> =
//\\    '!' <rest-of-boolean-unary-ops>,
//\\   ;

case P_rest_of_boolean_unary_ops:
  if (alt == 0)        {
    T[1] = lit('!');
    T[2] = build_ast(A[ap+0], depth+1); // <rest-of-boolean-unary-ops>
    T[0] = mktuple(phrase /*P_rest_of_boolean_unary_ops*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_boolean_unary_ops*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<bitwise-unary-ops> =
//\\    '~' <rest-of-bitwise-unary-ops>;

case P_bitwise_unary_ops:
  T[1] = lit('~');
  T[2] = build_ast(A[ap+0], depth+1); // <rest-of-bitwise-unary-ops>
  return mktuple(phrase /*P_bitwise_unary_ops*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-bitwise-unary-ops> =
//\\    '~' <rest-of-bitwise-unary-ops>,
//\\   ;

case P_rest_of_bitwise_unary_ops:
  if (alt == 0)        {
    T[1] = lit('~');
    T[2] = build_ast(A[ap+0], depth+1); // <rest-of-bitwise-unary-ops>
    T[0] = mktuple(phrase /*P_rest_of_bitwise_unary_ops*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_bitwise_unary_ops*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<address-operator> =
//\\    '&';

case P_address_operator:
  T[1] = lit('&');
  return mktuple(phrase /*P_address_operator*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);

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

case P_pre_increment_op:
  if (alt == 0)        {
    T[1] = lit('+');
    T[2] = lit('+');
    T[0] = mktuple(phrase /*P_pre_increment_op*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = lit('-');
    T[2] = lit('-');
    T[0] = mktuple(phrase /*P_pre_increment_op*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<optionally-cast-unary-rvalue-expression> =
//\\    '(' <type-specifier> ')' <optionally-cast-unary-rvalue-expression>,
//\\    <unary-rvalue-expression>;

case P_optionally_cast_unary_rvalue_expression:
  if (alt == 0)        {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <type-specifier>
    T[3] = lit(')');
    T[4] = build_ast(A[ap+1], depth+1); // <optionally-cast-unary-rvalue-expression>
    T[0] = mktuple(phrase /*P_optionally_cast_unary_rvalue_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <unary-rvalue-expression>
    T[0] = mktuple(phrase /*P_optionally_cast_unary_rvalue_expression*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<optionally-cast-postfix-rvalue-expression> =
//\\    '(' <type-specifier> ')' <optionally-cast-postfix-rvalue-expression>,
//\\    <postfix-rvalue-expression>;

case P_optionally_cast_postfix_rvalue_expression:
  if (alt == 0)        {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <type-specifier>
    T[3] = lit(')');
    T[4] = build_ast(A[ap+1], depth+1); // <optionally-cast-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_optionally_cast_postfix_rvalue_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_optionally_cast_postfix_rvalue_expression*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<unary-rvalue-expression> =
//\\    <arithmetic-unary-op> <optionally-cast-unary-rvalue-expression>,
//\\    <boolean-unary-ops> <optionally-cast-unary-rvalue-expression>,
//\\    <bitwise-unary-ops> <optionally-cast-unary-rvalue-expression>,
//\\    <indirection-unary-ops> <unary-address-expression>,
//\\    "sizeof" <unary-rvalue-expression>,
//\\    "sizeof" <unary-lvalue-expression>,
//\\    "sizeof" <type-specifier>,
//\\    <optionally-cast-postfix-rvalue-expression>;

case P_unary_rvalue_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <arithmetic-unary-op>
    T[2] = build_ast(A[ap+1], depth+1); // <optionally-cast-unary-rvalue-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 1) {
    T[1] = build_ast(A[ap+0], depth+1); // <boolean-unary-ops>
    T[2] = build_ast(A[ap+1], depth+1); // <optionally-cast-unary-rvalue-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 2) {
    T[1] = build_ast(A[ap+0], depth+1); // <bitwise-unary-ops>
    T[2] = build_ast(A[ap+1], depth+1); // <optionally-cast-unary-rvalue-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 3) {
    T[1] = build_ast(A[ap+0], depth+1); // <indirection-unary-ops>
    T[2] = build_ast(A[ap+1], depth+1); // <unary-address-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 4) {
    T[1] = kw(/*"sizeof"*/ 9);
    T[2] = build_ast(A[ap+0], depth+1); // <unary-rvalue-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 5) {
    T[1] = kw(/*"sizeof"*/ 9);
    T[2] = build_ast(A[ap+0], depth+1); // <unary-lvalue-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*6*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 6) {
    T[1] = kw(/*"sizeof"*/ 9);
    T[2] = build_ast(A[ap+0], depth+1); // <type-specifier>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*7*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <optionally-cast-postfix-rvalue-expression>
    T[0] = mktuple(phrase /*P_unary_rvalue_expression*/, alt /*8*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<optionally-cast-unary-lvalue-expression> =
//\\    '(' <type-specifier> ')' <optionally-cast-unary-lvalue-expression>,
//\\    <unary-lvalue-expression>;

case P_optionally_cast_unary_lvalue_expression:
  if (alt == 0)        {
    T[1] = lit('(');
    T[2] = build_ast(A[ap+0], depth+1); // <type-specifier>
    T[3] = lit(')');
    T[4] = build_ast(A[ap+1], depth+1); // <optionally-cast-unary-lvalue-expression>
    T[0] = mktuple(phrase /*P_optionally_cast_unary_lvalue_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <unary-lvalue-expression>
    T[0] = mktuple(phrase /*P_optionally_cast_unary_lvalue_expression*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<unary-lvalue-expression> =
//\\    <pre-increment-op> <unary-lvalue-expression>,
//\\    <indirection-unary-ops> <unary-address-expression>,
//\\    <postfix-lvalue-expression>;

case P_unary_lvalue_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <pre-increment-op>
    T[2] = build_ast(A[ap+1], depth+1); // <unary-lvalue-expression>
    T[0] = mktuple(phrase /*P_unary_lvalue_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 1) {
    T[1] = build_ast(A[ap+0], depth+1); // <indirection-unary-ops>
    T[2] = build_ast(A[ap+1], depth+1); // <unary-address-expression>
    T[0] = mktuple(phrase /*P_unary_lvalue_expression*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <postfix-lvalue-expression>
    T[0] = mktuple(phrase /*P_unary_lvalue_expression*/, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<unary-address-expression> =
//\\    <address-operator> <optionally-cast-unary-lvalue-expression>;

case P_unary_address_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <address-operator>
  T[2] = build_ast(A[ap+1], depth+1); // <optionally-cast-unary-lvalue-expression>
  return mktuple(phrase /*P_unary_address_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<multiplicative-expression> =
//\\    <unary-rvalue-expression> <rest-of-multiplicative-expression>;

case P_multiplicative_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <unary-rvalue-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-multiplicative-expression>
  return mktuple(phrase /*P_multiplicative_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-multiplicative-expression> =
//\\    <mulop> <unary-rvalue-expression> <rest-of-multiplicative-expression>,
//\\   ;

case P_rest_of_multiplicative_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <mulop>
    T[2] = build_ast(A[ap+1], depth+1); // <unary-rvalue-expression>
    T[3] = build_ast(A[ap+2], depth+1); // <rest-of-multiplicative-expression>
    T[0] = mktuple(phrase /*P_rest_of_multiplicative_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_multiplicative_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<mulop> =
//\\    '*',
//\\    '/',
//\\    '%';

case P_mulop:
  if (alt == 0)        {
    T[1] = lit('*');
    T[0] = mktuple(phrase /*P_mulop*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 1) {
    T[1] = lit('/');
    T[0] = mktuple(phrase /*P_mulop*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = lit('%');
    T[0] = mktuple(phrase /*P_mulop*/, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<additive-expression> =
//\\    <multiplicative-expression> <rest-of-additive-expression>;

case P_additive_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <multiplicative-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-additive-expression>
  return mktuple(phrase /*P_additive_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-additive-expression> =
//\\    <plusminus> <multiplicative-expression> <rest-of-additive-expression>,
//\\   ;

case P_rest_of_additive_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <plusminus>
    T[2] = build_ast(A[ap+1], depth+1); // <multiplicative-expression>
    T[3] = build_ast(A[ap+2], depth+1); // <rest-of-additive-expression>
    T[0] = mktuple(phrase /*P_rest_of_additive_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_additive_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<plusminus> =
//\\    '+',
//\\    '-';

case P_plusminus:
  if (alt == 0)        {
    T[1] = lit('+');
    T[0] = mktuple(phrase /*P_plusminus*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = lit('-');
    T[0] = mktuple(phrase /*P_plusminus*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<shift-expression> =
//\\    <additive-expression> <rest-of-shift-expression>;

case P_shift_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <additive-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-shift-expression>
  return mktuple(phrase /*P_shift_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-shift-expression> =
//\\    <shiftop> <additive-expression> <rest-of-shift-expression>,
//\\   ;

case P_rest_of_shift_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <shiftop>
    T[2] = build_ast(A[ap+1], depth+1); // <additive-expression>
    T[3] = build_ast(A[ap+2], depth+1); // <rest-of-shift-expression>
    T[0] = mktuple(phrase /*P_rest_of_shift_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_shift_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<shiftop> =
//\\    '<<',
//\\    '>>';

case P_shiftop:
  if (alt == 0)        {
    T[1] = lit('<');
    T[2] = lit('<');
    T[0] = mktuple(phrase /*P_shiftop*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = lit('>');
    T[2] = lit('>');
    T[0] = mktuple(phrase /*P_shiftop*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<relational-expression> =
//\\    <shift-expression> <rest-of-relational-expression>;

case P_relational_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <shift-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-relational-expression>
  return mktuple(phrase /*P_relational_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-relational-expression> =
//\\    <relop> <shift-expression> <rest-of-relational-expression>,
//\\   ;

case P_rest_of_relational_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <relop>
    T[2] = build_ast(A[ap+1], depth+1); // <shift-expression>
    T[3] = build_ast(A[ap+2], depth+1); // <rest-of-relational-expression>
    T[0] = mktuple(phrase /*P_rest_of_relational_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_relational_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<relop> =
//\\    '<=',
//\\    '<',
//\\    '>=',
//\\    '>';

case P_relop:
  if (alt == 0)        {
    T[1] = lit('<');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_relop*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 1) {
    T[1] = lit('<');
    T[0] = mktuple(phrase /*P_relop*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 2) {
    T[1] = lit('>');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_relop*/, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = lit('>');
    T[0] = mktuple(phrase /*P_relop*/, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<equality-expression> =
//\\    <relational-expression> <rest-of-equality-expression>;

case P_equality_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <relational-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-equality-expression>
  return mktuple(phrase /*P_equality_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-equality-expression> =
//\\    <eqop> <relational-expression> <rest-of-equality-expression>,
//\\   ;

case P_rest_of_equality_expression:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <eqop>
    T[2] = build_ast(A[ap+1], depth+1); // <relational-expression>
    T[3] = build_ast(A[ap+2], depth+1); // <rest-of-equality-expression>
    T[0] = mktuple(phrase /*P_rest_of_equality_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_equality_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<eqop> =
//\\    '==',
//\\    '!=';

case P_eqop:
  if (alt == 0)        {
    T[1] = lit('=');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_eqop*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = lit('!');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_eqop*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<bitwise-and-expression> =
//\\    <equality-expression> <rest-of-bitwise-and-expression>;

case P_bitwise_and_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <equality-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-bitwise-and-expression>
  return mktuple(phrase /*P_bitwise_and_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-bitwise-and-expression> =
//\\    '&' <equality-expression> <rest-of-bitwise-and-expression>,
//\\   ;

case P_rest_of_bitwise_and_expression:
  if (alt == 0)        {
    T[1] = lit('&');
    T[2] = build_ast(A[ap+0], depth+1); // <equality-expression>
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-bitwise-and-expression>
    T[0] = mktuple(phrase /*P_rest_of_bitwise_and_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_bitwise_and_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<exclusive-or-expression> =
//\\    <bitwise-and-expression> <rest-of-exclusive-or-expression>;

case P_exclusive_or_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <bitwise-and-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-exclusive-or-expression>
  return mktuple(phrase /*P_exclusive_or_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-exclusive-or-expression> =
//\\    '^' <bitwise-and-expression> <rest-of-exclusive-or-expression>,
//\\   ;

case P_rest_of_exclusive_or_expression:
  if (alt == 0)        {
    T[1] = lit('^');
    T[2] = build_ast(A[ap+0], depth+1); // <bitwise-and-expression>
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-exclusive-or-expression>
    T[0] = mktuple(phrase /*P_rest_of_exclusive_or_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_exclusive_or_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<inclusive-or-expression> =
//\\    <exclusive-or-expression> <rest-of-inclusive-or-expression>;

case P_inclusive_or_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <exclusive-or-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-inclusive-or-expression>
  return mktuple(phrase /*P_inclusive_or_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-inclusive-or-expression> =
//\\    '|' <exclusive-or-expression> <rest-of-inclusive-or-expression>,
//\\   ;

case P_rest_of_inclusive_or_expression:
  if (alt == 0)        {
    T[1] = lit('|');
    T[2] = build_ast(A[ap+0], depth+1); // <exclusive-or-expression>
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-inclusive-or-expression>
    T[0] = mktuple(phrase /*P_rest_of_inclusive_or_expression*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_inclusive_or_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<logical-and-expression> =
//\\    <inclusive-or-expression> <rest-of-logical-and-expression>;

case P_logical_and_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <inclusive-or-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-logical-and-expression>
  return mktuple(phrase /*P_logical_and_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-logical-and-expression> =
//\\    '&&' <inclusive-or-expression> <rest-of-logical-and-expression>,
//\\   ;

case P_rest_of_logical_and_expression:
  if (alt == 0)        {
    T[1] = lit('&');
    T[2] = lit('&');
    T[3] = build_ast(A[ap+0], depth+1); // <inclusive-or-expression>
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-logical-and-expression>
    T[0] = mktuple(phrase /*P_rest_of_logical_and_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_logical_and_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<logical-or-expression> =
//\\    <logical-and-expression> <rest-of-logical-or-expression>;

case P_logical_or_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <logical-and-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-logical-or-expression>
  return mktuple(phrase /*P_logical_or_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-logical-or-expression> =
//\\    '||' <logical-and-expression> <rest-of-logical-or-expression>,
//\\   ;

case P_rest_of_logical_or_expression:
  if (alt == 0)        {
    T[1] = lit('|');
    T[2] = lit('|');
    T[3] = build_ast(A[ap+0], depth+1); // <logical-and-expression>
    T[4] = build_ast(A[ap+1], depth+1); // <rest-of-logical-or-expression>
    T[0] = mktuple(phrase /*P_rest_of_logical_or_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_logical_or_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<conditional-expression> =
//\\    <logical-or-expression> <rest-of-conditional-expression>;

case P_conditional_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <logical-or-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-conditional-expression>
  return mktuple(phrase /*P_conditional_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-conditional-expression> =
//\\    '?' <expression> ':' <conditional-expression>,
//\\   ;

case P_rest_of_conditional_expression:
  if (alt == 0)        {
    T[1] = lit('?');
    T[2] = build_ast(A[ap+0], depth+1); // <expression>
    T[3] = lit(':');
    T[4] = build_ast(A[ap+1], depth+1); // <conditional-expression>
    T[0] = mktuple(phrase /*P_rest_of_conditional_expression*/, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_conditional_expression*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<assignment-expression> =
//\\    <opt-lvalue-assign> <conditional-expression>;

case P_assignment_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <opt-lvalue-assign>
  T[2] = build_ast(A[ap+1], depth+1); // <conditional-expression>
  return mktuple(phrase /*P_assignment_expression*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<opt-lvalue-assign> =
//\\    <unary-lvalue-expression> <assignment-operator>,
//\\   ;

case P_opt_lvalue_assign:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <unary-lvalue-expression>
    T[2] = build_ast(A[ap+1], depth+1); // <assignment-operator>
    T[0] = mktuple(phrase /*P_opt_lvalue_assign*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_opt_lvalue_assign*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

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

case P_assignment_operator:
  if (alt == 0)        {
    T[1] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 1) {
    T[1] = lit('*');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 2) {
    T[1] = lit('/');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 3) {
    T[1] = lit('%');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 4) {
    T[1] = lit('+');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 5) {
    T[1] = lit('-');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*6*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 6) {
    T[1] = lit('<');
    T[2] = lit('<');
    T[3] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*7*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else if (alt == 7) {
    T[1] = lit('>');
    T[2] = lit('>');
    T[3] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*8*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else if (alt == 8) {
    T[1] = lit('&');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*9*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else if (alt == 9) {
    T[1] = lit('^');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*10*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[1] = lit('|');
    T[2] = lit('=');
    T[0] = mktuple(phrase /*P_assignment_operator*/, alt /*11*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  }
  return T[0];

//\\ P<comma-statement> =
//\\    <assignment-expression> <rest-of-comma-statement>;

case P_comma_statement:
  T[1] = build_ast(A[ap+0], depth+1); // <assignment-expression>
  T[2] = build_ast(A[ap+1], depth+1); // <rest-of-comma-statement>
  return mktuple(phrase /*P_comma_statement*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);

//\\ P<rest-of-comma-statement> =
//\\    ',' <assignment-expression> <rest-of-comma-statement>,
//\\   ;

case P_rest_of_comma_statement:
  if (alt == 0)        {
    T[1] = lit(',');
    T[2] = build_ast(A[ap+0], depth+1); // <assignment-expression>
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-comma-statement>
    T[0] = mktuple(phrase /*P_rest_of_comma_statement*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_comma_statement*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<expression> =
//\\    <comma-statement>;

case P_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <comma-statement>
  return mktuple(phrase /*P_expression*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);

//\\ P<constant-expression> =
//\\    <conditional-expression>;

case P_constant_expression:
  T[1] = build_ast(A[ap+0], depth+1); // <conditional-expression>
  return mktuple(phrase /*P_constant_expression*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);

//\\ P<param-list> =
//\\    <assignment-expression> <rest-of-param-list>,
//\\   ;

case P_param_list:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <assignment-expression>
    T[2] = build_ast(A[ap+1], depth+1); // <rest-of-param-list>
    T[0] = mktuple(phrase /*P_param_list*/, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
  } else               {
    T[0] = mktuple(phrase /*P_param_list*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<rest-of-param-list> =
//\\    ',' <assignment-expression> <rest-of-param-list>,
//\\   ;

case P_rest_of_param_list:
  if (alt == 0)        {
    T[1] = lit(',');
    T[2] = build_ast(A[ap+0], depth+1); // <assignment-expression>
    T[3] = build_ast(A[ap+1], depth+1); // <rest-of-param-list>
    T[0] = mktuple(phrase /*P_rest_of_param_list*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_rest_of_param_list*/, alt /*2*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

//\\ P<constant> =
//\\    <integer-constant>,
//\\    <character-constant>,
//\\    <floating-constant>,
//\\    <enumeration-constant>;

case P_constant:
  if (alt == 0)        {
    T[1] = integer_constant(ap+0);
    T[0] = mktuple(phrase /*P_constant*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 1) {
    T[1] = character_constant(ap+0);
    T[0] = mktuple(phrase /*P_constant*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  } else if (alt == 2) {
    T[1] = floating_constant(ap+0);
    T[0] = mktuple(phrase /*P_constant*/, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <enumeration-constant>
    T[0] = mktuple(phrase /*P_constant*/, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
  }
  return T[0];

//\\ P<enumeration-constant> =
//\\    <identifier>;

case P_enumeration_constant:
  T[1] = identifier(ap+0);
  return mktuple(phrase /*P_enumeration_constant*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);

//\\ P<typedef-name> =
//\\    <identifier>;

case P_typedef_name:
  T[1] = identifier(ap+0);
  return mktuple(phrase /*P_typedef_name*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);

//\\ P<opt-labels> =
//\\    <label> ':' <opt-labels>,
//\\    "case" <constant-expression> ':' <opt-labels>,
//\\    "default" ':' <opt-labels>,
//\\   ;

case P_opt_labels:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <label>
    T[2] = lit(':');
    T[3] = build_ast(A[ap+1], depth+1); // <opt-labels>
    T[0] = mktuple(phrase /*P_opt_labels*/, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else if (alt == 1) {
    T[1] = kw(/*"case"*/ 10);
    T[2] = build_ast(A[ap+0], depth+1); // <constant-expression>
    T[3] = lit(':');
    T[4] = build_ast(A[ap+1], depth+1); // <opt-labels>
    T[0] = mktuple(phrase /*P_opt_labels*/, alt /*2*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
  } else if (alt == 2) {
    T[1] = kw(/*"default"*/ 11);
    T[2] = lit(':');
    T[3] = build_ast(A[ap+0], depth+1); // <opt-labels>
    T[0] = mktuple(phrase /*P_opt_labels*/, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  } else               {
    T[0] = mktuple(phrase /*P_opt_labels*/, alt /*4*/, /*phrases*/ 0, T /**/);
  }
  return T[0];

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

case P_label:
  T[1] = identifier(ap+0);
  return mktuple(phrase /*P_label*/, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);

//\\ P<SS> =
//\\    <opt-labels> "if" '(' <expression> ')' '{',
//\\    '}',
//\\    <opt-labels> <expression> ';';

case P_SS:
  if (alt == 0)        {
    T[1] = build_ast(A[ap+0], depth+1); // <opt-labels>
    T[2] = kw(/*"if"*/ 12);
    T[3] = lit('(');
    T[4] = build_ast(A[ap+1], depth+1); // <expression>
    T[5] = lit(')');
    T[6] = lit('{');
    T[0] = mktuple(phrase /*P_SS*/, alt /*1*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
  } else if (alt == 1) {
    T[1] = lit('}');
    T[0] = mktuple(phrase /*P_SS*/, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
  } else               {
    T[1] = build_ast(A[ap+0], depth+1); // <opt-labels>
    T[2] = build_ast(A[ap+1], depth+1); // <expression>
    T[3] = lit(';');
    T[0] = mktuple(phrase /*P_SS*/, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
  }
  return T[0];

//\\ E

  }
}

void compile(int p) {
}

int main (int argc, char **argv) {
  int errors, statements, lines;
  int program;
  char *s;

  /* Get clean version of executable name.  Should work on most existing systems (2006) */
  progname = strdup (argv[0]);
  if ((s = strrchr (progname, '/')) != NULL)
    progname = s + 1;           // Unix
  if ((s = strrchr (progname, '\\')) != NULL)
    progname = s + 1;           // M$
  if ((s = strrchr (progname, ']')) != NULL)
    progname = s + 1;           // Dec
  if ((s = strrchr (progname, ';')) != NULL)
    *s = '\0';                  // Version no's
  if (((s = strrchr (progname, '.')) != NULL)
      && (strcasecmp (s, ".exe") == 0))
    *s = '\0';
  if (((s = strrchr (progname, '.')) != NULL)
      && (strcasecmp (s, ".com") == 0))
    *s = '\0';

  /* Handle program arguments */

  if (argc >= 2) {
    if (strcmp(argv[1], "-d") == 0) {
      debug_parser = TRUE;
      argc -= 1; argv += 1; // shift
    }
  }
  
  if (argc != 2) {
    fprintf (stderr, "syntax: %s [-d] filename\n", progname);
    exit (1);
  }

  if ((sourcefile = fopen (argv[1], "r")) == NULL) {
    fprintf (stderr, "%s: %s - %s\n", progname, strerror (errno), argv[1]);
    exit (errno);
  }

  outfile = stdout;
  curfile = argv[1];
  startline = TRUE;
  whitespace = TRUE;
  onecharstr = (char *) malloc (512);

  line_reconstruction ();       // Effectively, lexing.

  if (debug_parser) {
    int i;                        // DEBUG ONLY
    fprintf (stderr, "\nLexical token stream:\n\n");
    for (i = 0; i < c_nextfree; i++) {
      fprintf (stderr, "C[%d] => %s, line %d, col %d: [%0d] %s\n", i, c[i].f, c[i].l, c[i].col, c[i].t, c[i].s);
    }
  }

  /* Call the parser */

  errors = 0; statements = 0;
  for (;;) {
    if (cp == c_nextfree) break; // End of file?
    ap = 0; // reset analysis record on each line. We're not parsing the entire program here.
    if (parse (P_SS, 0)) {
      int i;
#ifdef DEBUG_TOKENS
      for (i = 0; i < c_nextfree; i++) {
        fprintf(stderr, "c[%d].s = \"%s\" t=%d\n", i, c[i].s, c[i].t);
      }
#endif
#ifdef DEBUG_AREC
      while (A[i]) {
        int p, phrases = A[i+2];
        if (A[i] < 512+MAX_BIP) {
          fprintf(stderr, "A[%d]=%d B<%s>, ", i, A[i], phrasename[A[i]-512]);
          if (phrases) {
            fprintf(stderr, "choice %d: %s\n", A[i+1], c[A[i+3]].s);
          } else {
            fprintf(stderr, "Null\n");
          }
        } else {
          fprintf(stderr, "A[%d]=%d P<%s>, ", i, A[i], (A[i]&DROP_SPECIAL_BITS)==WHITESPACE ? "sp" : phrasename[A[i]-512]);
          if (phrases) {
            fprintf(stderr, "choice %d: ", A[i+1]);
            //fprintf(stderr, "A[%d]=Phrases %d ", i+2, phrases);
            for (p = i+3; p < i+3+phrases; p++) fprintf(stderr, "A[%d]=%d ", p, A[p]);
            fprintf(stderr, "\n");
          } else {
            fprintf(stderr, "Null\n");
          }
        }
        i = i + 3 + phrases;
      }
#endif
      statements += 1;
      program = build_ast(0, 0);
      compile(program);
    } else {
      errors += 1;
      break;
    }
  }

  if (errors == 0) {
    fprintf(stderr, "%d Lines, %d Statement%s compiled.\n", last_line, statements, statements == 1 ? "" : "s");
    // now do something with it :-)
  } else {
    /* Attempt to print a sensible error if the parse failed */
    if (bestparse == c_nextfree) {
      fprintf (stderr, "\"%s\", Line %d, Col %d: Premature end of file while looking for %s\n", argv[1], c[bestparse].l, c[bestparse].col + 1, looking_for);
    } else {
      int i;
      fprintf (stderr, "\"%s\", Line %d, Col %d: Syntax error while looking for %s near ", argv[1], c[bestparse].l, c[bestparse].col + 1, looking_for);
      for (i = bestparse; i < bestparse + 3; i++) {
        if (i == c_nextfree) {
          fprintf (stderr, "<End of file>");
          break;
        }
        // output built in phrases here
        fprintf (stderr, (i == (bestparse + 2) ? " ..." : " "));
      }
      fprintf (stderr, "\n");
    }

    exit (1);
  }

  exit (0);
  return (1);
}
@


1.4
log
@added comments explaining whitespace problem
@
text
@d1 2
d39 3
d47 1
a47 1
//#define DEBUG_TOKENS 1
d50 1
a50 1
#include "cexpr.h"              // GENERATED GRAMMAR FROM takeon.c and cexpr.g
d52 1
a52 1
int debug_parser = FALSE;
d457 1
d461 1
a461 1
          storec (ch, lineno, col, B_char, curfile);
d476 3
a478 1
        col++;                  // Does not affect whitespace
d483 2
d499 1
d539 6
d615 1
a615 1
          saved_descp += sprintf (saved_descp, " <%s/%d>", phrasename[phrase - 512], phrase);
d645 1
a645 1
          sprintf (s, "<%s>", phrasename[phrase - 512]);
d693 11
d708 1
d781 1144
a1924 1
#include "cexpr.c"
d2012 1
a2012 1
          fprintf(stderr, "A[%d]=%d P<%s>, ", i, A[i], phrasename[A[i]-512]);
@


1.3
log
@worked out how to extract literals for diagnostics
@
text
@d8 20
a105 6
int kw(int idx)
{
  if (listing) fprintf(stdout, " %s ", keyword[idx]);
  return 0;
}

d172 6
d592 1
a592 1
          saved_descp += sprintf (saved_descp, " '%c'", phrase);
d594 1
a594 1
          saved_descp += sprintf (saved_descp, " \"%s\"/%d", keyword[phrase - 256], phrase - 256);
d596 1
a596 1
          saved_descp += sprintf (saved_descp, " {%s/BIP%d}", phrasename[phrase - 512], BIP[phrase - 512]);
@


1.2
log
@added char constants, fixed problem with /**/ comments leaving the '/' unread.
@
text
@d13 4
d21 4
a24 1
#define DEBUG_PARSER // enable some extra data in header file
d70 1
a70 1
int nextfree = 0, arraysize = 0;
d73 96
a184 11
  /* A[] is the Analysis record.
     Contents point to a C[] structure when the item is a BIP,
     otherwise the format is [phraseno] [altno] [no-of-subphrases] [subphrases and/or BIPs...]
     for example, if A[25] contained 10, that would mean that the
     token for A[25] was stored in C[10].]
     (And the C string would be at &stringpool[c[A[25]]]?)
   */

static int *A = NULL;           /* Flex array, expanded using 'makespace' below */
int next_free_a = 0, a_size = 0;

d231 4
a234 2
  if (nextstring + strlen (s) + 1 >= MAXPOOL)
    exit (1);                   // TO DO: add message
d240 8
a247 7
  makespace (c, nextfree, arraysize);
  c[nextfree].s = stringpool + tag;
  c[nextfree].l = lineno;
  c[nextfree].col = col;
  c[nextfree].f = fname;
  c[nextfree].t = type;
  nextfree++;
d251 1
d259 1
d272 1
a272 1
      int nextfree = 0, strsize = 0, startcol = col;
d277 1
a277 1
        makespace (token, nextfree, strsize);
d281 1
a281 1
          token[nextfree++] = ch;
d283 1
a283 1
          token[nextfree] = '\0';
d294 1
a294 1
      int nextfree = 0, numsize = 0;
d296 1
a296 1
      int peek = xfgetc (sourcefile); xungetc (ch, sourcefile);
d298 8
a305 1
        ch = xfgetc (sourcefile); // 0x
d309 1
a309 1
          makespace (number, nextfree, numsize);
d312 1
a312 1
            number[nextfree++] = ch;
d314 8
a321 2
            number[nextfree] = '\0';
            xungetc (ch, sourcefile);
d326 1
a326 1
        stores (number, lineno, col, B_integer_constant, curfile);
d329 1
d333 1
a333 1
          makespace (number, nextfree, numsize);
d336 1
a336 1
            number[nextfree++] = ch;
d338 1
a338 1
            number[nextfree] = '\0';
d344 1
a344 1
        stores (number, lineno, col, B_integer_constant, curfile);
d347 1
a347 1
    } else
d354 1
a354 1
          int nextfree = 0, strsize = 0, quotech = ch;
d362 1
a362 1
            makespace (string, nextfree, strsize);
d367 1
a367 1
                string[nextfree++] = ch;
d369 1
a369 1
                string[nextfree++] = '\'';
d371 1
a371 1
                string[nextfree++] = '"';
d373 1
a373 1
                string[nextfree++] = '\n';
d375 1
a375 1
                string[nextfree++] = '\r';
d377 1
a377 1
                string[nextfree++] = '\t';
d380 2
a381 2
                string[nextfree++] = '\\';
                string[nextfree++] = ch;
d384 1
a384 1
              string[nextfree++] = ch;
d386 1
a386 1
              string[nextfree] = '\0';
d394 1
d397 1
a397 1
              fprintf(stderr, "Warning: multi-byte characters constants such as \'%s\' are not well supported.\n", string);
d400 1
a400 1
              fprintf(stderr, "Warning: wrong kind of string quote used? - \'%s\'.\n", string);
d412 1
a412 1
          // Handle line comment
d421 1
a421 1
          /* Handle potential multi-line comment */
d439 1
a439 1
        // WHITESPACE
d472 1
d476 4
a479 4
  c[nextfree].t = B_EOF;
  c[nextfree].s = "<EOF>";
  c[nextfree].l = lineno;
  c[nextfree].col = col;
d506 1
a506 1
  char saved_desc[256];
d595 1
a595 1
                 (cp < nextfree ? c[cp].s : "EOF"), (cp + 1 < nextfree ? c[cp + 1].s : "EOF"), (cp + 2 < nextfree ? c[cp + 2].s : "EOF"));
d599 1
a599 1
        static char s[128];           // off heap instead? - binary size
d641 1
a641 1
      } else if (phrase < 512 + MAX_BIP) {  /* Built-in phrase */
d647 1
a647 1
        if (c[cp].t != BIP[phrase - 512])
d649 1
a649 1
        else {
d675 1
d708 31
d741 1
d791 1
a791 1
    for (i = 0; i < nextfree; i++) {
d800 2
a801 1
    if (cp == nextfree) break;
d803 30
d834 2
d847 1
a847 1
    if (bestparse == nextfree) {
d853 1
a853 1
        if (i == nextfree) {
@


1.1
log
@Initial revision
@
text
@d38 1
a38 1
#define MAXPOOL (1024*32)
d113 1
a114 1
  static int last_line = -1;
d199 33
a231 12

      // Store as a string...
      whitespace = FALSE;
      for (;;) {
        makespace (number, nextfree, numsize);
        if (isdigit (ch)) {
          col++;
          number[nextfree++] = ch;
        } else {
          number[nextfree] = '\0';
          xungetc (ch, sourcefile);
          break;
d233 2
a234 1
        ch = xfgetc (sourcefile);
a235 3
      stores (number, lineno, col, B_integer_constant, curfile);
      free (number);

d239 1
d281 1
d285 1
d288 1
d290 1
a290 2
          }
          stores (string, lineno, col, B_string, curfile);
d320 1
a320 1
          (void) xfgetc (sourcefile);   // Remove '/'
a366 1

d522 1
a522 1
        if (strcmp (keyword[phrase - 256], c[cp].s) != 0)
d595 1
d652 13
a664 2
  if (parse (P_SS, 0)) {
    fprintf(stderr, "1 Statement compiled.\n");
@
