imp80.c

    //\\ # This is an extended grammar which now accepts constructs from multiple versions of Imp
    //\\ # - it was primarily based on a correct Imp80 grammar, then modified to add Imp77 constructs
    //\\ # followed by features from Pdp15 Imp, old Emas "Imp9", and Hamish's 68000 extensions, etc
    //\\ # The original base grammar was:
    //\\ #   https://history.dcs.ed.ac.uk/archive/os/emas/emas2/compilers/imp80/imp80ps04.txt
    //\\ #
    //\\ # Since this accepts multiple dialects, I'm tempted to call it "PImp", short for "Polyglot Imp"...
    //\\ #
    //\\ # There is good info re the phrase structure grammar to be found at
    //\\ # http://www.ancientgeek.org.uk/EMAS/EMAS_Manuals/IMP/A_Syntactic_and_Semantic_Definition_of_the_IMP_Language.pdf
    //\\ #
    //\\ # Built-in Phrases used by the line reconstruction phase:
    //\\ #
    //\\ # stropped letter (%a %b etc)
    //\\ B<stropped> = 0;
    //\\ #
    //\\ # single-quoted string
    //\\ B<sqstring> = 1;
    //\\ #
    //\\ # double-quoted string
    //\\ B<dqstring> = 2;
    //\\ #
    //\\ B<NL> = 3;
    //\\ #
    //\\ B<char> = 4;
    //\\ #
    //\\ # All parsers need an <EOF> phrase.
    //\\ #
    //\\ B<EOF> = 5;

    //\\ C<immediate> = {
    //\\ // WARNING: currently parse-time phrases are not being handled properly
    //\\ //          by compile(). Are they set to -1 or skipped completely?
    //\\ #define INCLUDE_AST_DEBUG "show_imp_ast.c"
    //\\ int kw(int AST_op, int idx, int ap)
    //\\ {
    //\\   int data[4];
    //\\
    //\\   int i;
    //\\
    //\\   char *k = keyword(idx);
    //\\
    //\\   data[1] =  idx;
    //\\
    //\\   data[2] = A[ap+3]; // index of c[] array element
    //\\   data[3] = strlen(k);
    //\\
    //\\   return mktuple(AST_op, 1, 3, data);
    //\\
    //\\ }
    //\\ // variables used in line_reconstruction() moved here so they are available to semantic code in grammar.
    //\\ #define STROPPED_KEYWORDS 1  /* alters behaviour of parse() */
    //\\ int at_startofstatement = TRUE;
    //\\
    //\\ int saved = TRUE;
    //\\
    //\\ int at_startofline = TRUE;
    //\\
    //\\ int in_keyword = FALSE;
    //\\
    //\\ int in_alphanumeric = FALSE;
    //\\
    //\\ };

    //\\ #
    //\\ # We have one semantic phrase in this grammar:
    //\\ #
    //\\ # OK, this is slightly trickier than you might expect... you can't place the <COLON> immediate code after the ':'
    //\\ # because the parser pre-fetches the next token in order to compare against <COLON> even though <COLON> doesn't
    //\\ # actually examine any text.  So... we put the test *before* the ':', and *UNDO* its actions in the following
    //\\ # alternative if the ':' was not matched.
    //\\ #
    //\\ # Anyway, the outcome is that if a <COLON> is followed by a '!' comment, that comment is handled by the
    //\\ # line reconstruction code and is *not* passed back to the parser a character at a time as it was previously doing!
    //\\ #
    //\\ # This in turn solves the problem of line reconstruction finding a ' or a " in the comment and gobbling up characters
    //\\ # - possibly over multiple line - until it hits a matching quote.  PHEW.  It took a long time to get here!
    //\\ #

    //\\ P<LABEL-COLON> =
    //\\    <immediate:saved = at_startofstatement; at_startofstatement = TRUE; return TRUE;> ':',
    //\\    <immediate:at_startofstatement = saved; return FALSE;>;

    case P_LABEL_COLON:
      if (alt == 0)        {
        T[1] = -1; // successful semantic call
        T[2] = lit(AST__LIT, ':', A[ap+1]);
        T[0] = mktuple(AST_LABEL_COLON, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = -1; // successful semantic call
        T[0] = mktuple(AST_LABEL_COLON, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ ##P<LABEL-COLON> =
    //\\ ##   ':';
    //\\ # SS is the primary entry point.
    //\\ #

    //\\ P<SS> =
    //\\    <EXTERNALS> <LAST-ITEMS-IN-FILE>;

    case P_SS:
      T[1] = build_ast(A[ap+0], depth+1); // <EXTERNALS>
      T[2] = build_ast(A[ap+1], depth+1); // <LAST-ITEMS-IN-FILE>
      return mktuple(AST_SS, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ #
    //\\ # I'm taking the long way to handle alphanumerics and variable names, for now
    //\\ # At some point (soon) I hope to add regular expression matching, to shorten
    //\\ # these rules and speed up parsing.  But this works, quite reliably.
    //\\ #
    //\\ ## P<letter> = "[A-Z]";
    //\\ #

    //\\ P<letter> =
    //\\    'A',
    //\\    'B',
    //\\    'C',
    //\\    'D',
    //\\    'E',
    //\\    'F',
    //\\    'G',
    //\\    'H',
    //\\    'I',
    //\\    'J',
    //\\    'K',
    //\\    'L',
    //\\    'M',
    //\\    'N',
    //\\    'O',
    //\\    'P',
    //\\    'Q',
    //\\    'R',
    //\\    'S',
    //\\    'T',
    //\\    'U',
    //\\    'V',
    //\\    'W',
    //\\    'X',
    //\\    'Y',
    //\\    'Z';

    case P_letter:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, 'A', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, 'B', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, 'C', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 3) {
        T[1] = lit(AST__LIT, 'D', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 4) {
        T[1] = lit(AST__LIT, 'E', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 5) {
        T[1] = lit(AST__LIT, 'F', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*5*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 6) {
        T[1] = lit(AST__LIT, 'G', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*6*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 7) {
        T[1] = lit(AST__LIT, 'H', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*7*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 8) {
        T[1] = lit(AST__LIT, 'I', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*8*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 9) {
        T[1] = lit(AST__LIT, 'J', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*9*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 10) {
        T[1] = lit(AST__LIT, 'K', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*10*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 11) {
        T[1] = lit(AST__LIT, 'L', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*11*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 12) {
        T[1] = lit(AST__LIT, 'M', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*12*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 13) {
        T[1] = lit(AST__LIT, 'N', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*13*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 14) {
        T[1] = lit(AST__LIT, 'O', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*14*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 15) {
        T[1] = lit(AST__LIT, 'P', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*15*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 16) {
        T[1] = lit(AST__LIT, 'Q', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*16*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 17) {
        T[1] = lit(AST__LIT, 'R', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*17*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 18) {
        T[1] = lit(AST__LIT, 'S', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*18*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 19) {
        T[1] = lit(AST__LIT, 'T', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*19*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 20) {
        T[1] = lit(AST__LIT, 'U', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*20*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 21) {
        T[1] = lit(AST__LIT, 'V', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*21*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 22) {
        T[1] = lit(AST__LIT, 'W', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*22*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 23) {
        T[1] = lit(AST__LIT, 'X', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*23*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 24) {
        T[1] = lit(AST__LIT, 'Y', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*24*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, 'Z', A[ap+0]);
        T[0] = mktuple(AST_letter, alt /*25*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ ## P<digit> = "[0-9]";
    //\\ #

    //\\ P<digit> =
    //\\    '0',
    //\\    '1',
    //\\    '2',
    //\\    '3',
    //\\    '4',
    //\\    '5',
    //\\    '6',
    //\\    '7',
    //\\    '8',
    //\\    '9';

    case P_digit:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '0', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '1', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '2', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 3) {
        T[1] = lit(AST__LIT, '3', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 4) {
        T[1] = lit(AST__LIT, '4', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 5) {
        T[1] = lit(AST__LIT, '5', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*5*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 6) {
        T[1] = lit(AST__LIT, '6', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*6*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 7) {
        T[1] = lit(AST__LIT, '7', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*7*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 8) {
        T[1] = lit(AST__LIT, '8', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*8*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '9', A[ap+0]);
        T[0] = mktuple(AST_digit, alt /*9*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<alphanumeric> =
    //\\    <letter>,
    //\\    <digit>;

    case P_alphanumeric:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <letter>
        T[0] = mktuple(AST_alphanumeric, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <digit>
        T[0] = mktuple(AST_alphanumeric, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<opt-letter-or-digit-seq> =
    //\\    <letter> <opt-letter-or-digit-seq>,
    //\\    <digit> <opt-letter-or-digit-seq>,
    //\\   ;

    case P_opt_letter_or_digit_seq:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <letter>
        T[2] = build_ast(A[ap+1], depth+1); // <opt-letter-or-digit-seq>
        T[0] = mktuple(AST_opt_letter_or_digit_seq, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <digit>
        T[2] = build_ast(A[ap+1], depth+1); // <opt-letter-or-digit-seq>
        T[0] = mktuple(AST_opt_letter_or_digit_seq, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_opt_letter_or_digit_seq, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ #More than one newline (or semicolon)

    //\\ P<BLANKS> =
    //\\    <S> <BLANKS>,
    //\\   ;

    case P_BLANKS:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <S>
        T[2] = build_ast(A[ap+1], depth+1); // <BLANKS>
        T[0] = mktuple(AST_BLANKS, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_BLANKS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # The last items in a file.
    //\\ # the final EOF below is only for begin/endofprogram which *can* be followed by code but shouldn't

    //\\ P<LAST-ITEMS-IN-FILE> =
    //\\    "end" "of" "file" <BLANKS> <EOF>,
    //\\    "end" "of" "perm" <BLANKS> <EOF>,
    //\\    "end" "of" "prim" <BLANKS> <EOF>,
    //\\    <BLANKS> <EOF>;

    case P_LAST_ITEMS_IN_FILE:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[2] = kw(AST__KW, KW_of, A[ap+1]);
        T[3] = kw(AST__KW, KW_file, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <BLANKS>
        T[5] = bip(AST_EOF, A[ap+4]);
        T[0] = mktuple(AST_LAST_ITEMS_IN_FILE, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[2] = kw(AST__KW, KW_of, A[ap+1]);
        T[3] = kw(AST__KW, KW_perm, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <BLANKS>
        T[5] = bip(AST_EOF, A[ap+4]);
        T[0] = mktuple(AST_LAST_ITEMS_IN_FILE, alt /*1*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[2] = kw(AST__KW, KW_of, A[ap+1]);
        T[3] = kw(AST__KW, KW_prim, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <BLANKS>
        T[5] = bip(AST_EOF, A[ap+4]);
        T[0] = mktuple(AST_LAST_ITEMS_IN_FILE, alt /*2*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <BLANKS>
        T[2] = bip(AST_EOF, A[ap+1]);
        T[0] = mktuple(AST_LAST_ITEMS_IN_FILE, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<program> =
    //\\    "programme",
    //\\    "program";

    case P_program:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_programme, A[ap+0]);
        T[0] = mktuple(AST_program, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_program, A[ap+0]);
        T[0] = mktuple(AST_program, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # I haven't specified any phrases to be external-only.  There might be some,
    //\\ # but I'll leave it to compile() to reject them.

    //\\ P<EXTERNALS> =
    //\\    <EXTERNAL-OR-INTERNAL> <EXTERNALS>,
    //\\   ;

    case P_EXTERNALS:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <EXTERNAL-OR-INTERNAL>
        T[2] = build_ast(A[ap+1], depth+1); // <EXTERNALS>
        T[0] = mktuple(AST_EXTERNALS, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_EXTERNALS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # I cleaned up label handling, we can afford the overhead of backtracking over one <NAME>...
    //\\ # - this isn't the 70's any more.  Labels are not valid outside blocks.

    //\\ P<INTERNALS> =
    //\\    <OPT-LABEL-DEFINITION> <INTERNAL> <INTERNALS>,
    //\\    <OPT-LABEL-DEFINITION>;

    case P_INTERNALS:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-LABEL-DEFINITION>
        T[2] = build_ast(A[ap+1], depth+1); // <INTERNAL>
        T[3] = build_ast(A[ap+2], depth+1); // <INTERNALS>
        T[0] = mktuple(AST_INTERNALS, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-LABEL-DEFINITION>
        T[0] = mktuple(AST_INTERNALS, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # top-level begin/end block can either be %begin/%endofprogram,
    //\\ # or %begin/%end/%endoffile.  However in some cases things can
    //\\ # be placed between the %end and the %endoffile.
    //\\ #
    //\\ # I've also seen %routine xxx/%endofprogram in FMACS/BOOT.imp
    //\\ #
    //\\ # There's a %begin/%end without an %endoffile in FMACS/FIRST4.imp

    //\\ P<outer-level-end> =
    //\\    "end" "of" <program>,
    //\\    "end";

    case P_outer_level_end:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[2] = kw(AST__KW, KW_of, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <program>
        T[0] = mktuple(AST_outer_level_end, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[0] = mktuple(AST_outer_level_end, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # these are allowed at the external level and also inside procedures/main program.
    //\\ # The phrases that are internal-only include these as the final alternative,
    //\\ # hence why the empty statement can only occur as the last item in this list.
    //\\ #
    //\\ # If using this grammar as a 'SOAP' replacement, you'll want to allow syntactically
    //\\ # incorrect statements also, and if so, those should be handled here after the <S> rule.
    //\\ #

    //\\ P<EXTERNAL-OR-INTERNAL> =
    //\\    "begin" <S> <INTERNALS> <outer-level-end> <S>,
    //\\    <Percent-SEX> <RT> <spec> <NAME> <Opt-ALIAS> <FPP> <S>,
    //\\    <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S> <INTERNALS> <outer-level-end> <S>,
    //\\    <OPT-68K> <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S>,
    //\\    <DECLARATION-S>,
    //\\    "record" "format" <RECFMT-spec-OR-BODY> <S>,
    //\\    "switch" <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST> <S>,
    //\\    "include" <STR-CONST>,
    //\\    "option" <STR-CONST>,
    //\\    "from" <NAME> "include" <NAME> <Opt-NAME-LIST>,
    //\\    "end" "of" "list" <S>,
    //\\    "list" <S>,
    //\\    "end" "of" "mcode" <S>,
    //\\    "mcode" <S>,
    //\\    "trusted" "program" <S>,
    //\\    "main" "ep" <NAME> <S>,
    //\\    "control" <INT-CONST> <S>,
    //\\    "diagnose" <INT-CONST> <S>,
    //\\    "reals" "long",
    //\\    "reals" "normal",
    //\\    <COMMENT>,
    //\\    <S>;

    case P_EXTERNAL_OR_INTERNAL:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_begin, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <S>
        T[3] = build_ast(A[ap+2], depth+1); // <INTERNALS>
        T[4] = build_ast(A[ap+3], depth+1); // <outer-level-end>
        T[5] = build_ast(A[ap+4], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-SEX>
        T[2] = build_ast(A[ap+1], depth+1); // <RT>
        T[3] = build_ast(A[ap+2], depth+1); // <spec>
        T[4] = build_ast(A[ap+3], depth+1); // <NAME>
        T[5] = build_ast(A[ap+4], depth+1); // <Opt-ALIAS>
        T[6] = build_ast(A[ap+5], depth+1); // <FPP>
        T[7] = build_ast(A[ap+6], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*1*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-SEX>
        T[2] = build_ast(A[ap+1], depth+1); // <RT>
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-ALIAS>
        T[5] = build_ast(A[ap+4], depth+1); // <FPP>
        T[6] = build_ast(A[ap+5], depth+1); // <S>
        T[7] = build_ast(A[ap+6], depth+1); // <INTERNALS>
        T[8] = build_ast(A[ap+7], depth+1); // <outer-level-end>
        T[9] = build_ast(A[ap+8], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*2*/, /*phrases*/ 9, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-68K>
        T[2] = build_ast(A[ap+1], depth+1); // <Percent-SEX>
        T[3] = build_ast(A[ap+2], depth+1); // <RT>
        T[4] = build_ast(A[ap+3], depth+1); // <NAME>
        T[5] = build_ast(A[ap+4], depth+1); // <Opt-ALIAS>
        T[6] = build_ast(A[ap+5], depth+1); // <FPP>
        T[7] = build_ast(A[ap+6], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*3*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else if (alt == 4) {
        T[1] = build_ast(A[ap+0], depth+1); // <DECLARATION-S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 5) {
        T[1] = kw(AST__KW, KW_record, A[ap+0]);
        T[2] = kw(AST__KW, KW_format, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <RECFMT-spec-OR-BODY>
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*5*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 6) {
        T[1] = kw(AST__KW, KW_switch, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-NAME-LIST>
        T[4] = lit(AST__LIT, '(', A[ap+3]);
        T[5] = build_ast(A[ap+4], depth+1); // <EXPR>
        T[6] = lit(AST__LIT, ':', A[ap+5]);
        T[7] = build_ast(A[ap+6], depth+1); // <EXPR>
        T[8] = lit(AST__LIT, ')', A[ap+7]);
        T[9] = build_ast(A[ap+8], depth+1); // <REST-OF-SWLIST>
        T[10] = build_ast(A[ap+9], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*6*/, /*phrases*/ 10, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9], T[10]*/);
      } else if (alt == 7) {
        T[1] = kw(AST__KW, KW_include, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <STR-CONST>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*7*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 8) {
        T[1] = kw(AST__KW, KW_option, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <STR-CONST>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*8*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 9) {
        T[1] = kw(AST__KW, KW_from, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = kw(AST__KW, KW_include, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <NAME>
        T[5] = build_ast(A[ap+4], depth+1); // <Opt-NAME-LIST>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*9*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 10) {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[2] = kw(AST__KW, KW_of, A[ap+1]);
        T[3] = kw(AST__KW, KW_list, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*10*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 11) {
        T[1] = kw(AST__KW, KW_list, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*11*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 12) {
        T[1] = kw(AST__KW, KW_end, A[ap+0]);
        T[2] = kw(AST__KW, KW_of, A[ap+1]);
        T[3] = kw(AST__KW, KW_mcode, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*12*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 13) {
        T[1] = kw(AST__KW, KW_mcode, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*13*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 14) {
        T[1] = kw(AST__KW, KW_trusted, A[ap+0]);
        T[2] = kw(AST__KW, KW_program, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*14*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 15) {
        T[1] = kw(AST__KW, KW_main, A[ap+0]);
        T[2] = kw(AST__KW, KW_ep, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*15*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 16) {
        T[1] = kw(AST__KW, KW_control, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <INT-CONST>
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*16*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 17) {
        T[1] = kw(AST__KW, KW_diagnose, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <INT-CONST>
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*17*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 18) {
        T[1] = kw(AST__KW, KW_reals, A[ap+0]);
        T[2] = kw(AST__KW, KW_long, A[ap+1]);
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*18*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 19) {
        T[1] = kw(AST__KW, KW_reals, A[ap+0]);
        T[2] = kw(AST__KW, KW_normal, A[ap+1]);
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*19*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 20) {
        T[1] = build_ast(A[ap+0], depth+1); // <COMMENT>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*20*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <S>
        T[0] = mktuple(AST_EXTERNAL_OR_INTERNAL, alt /*21*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # These are all extensions used by 68000 Imp.
    //\\ #
    //\\ # %begin
    //\\ # %recordformat f(%byte ldte,lsap,rdte,rsap)
    //\\ # @#ldte %record(f)cur
    //\\ # ( I have no idea what "@#ldte " means. )
    //\\ #
    //\\ # @400(a5) %record(*)%namearray in(0:7)
    //\\ # @16_3F00-192 %routine         closeinput
    //\\ # @724(a5) %integername heapfront
    //\\ #
    //\\ # '@' <68K-code-address> before a routine name is an external spec in 68000 Imp, and
    //\\ # <Opt-68K-code-address> after an external %spec is a perm routine spec in 68000 Imp.
    //\\ # as in: %integerfnspec(16_1124) REM(%integer a,b)  { found in APM-gdmr/I/GGPERM.imp }
    //\\ #

    //\\ P<OPT-68K> =
    //\\    '@' <68K-stuff>,
    //\\   ;

    case P_OPT_68K:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '@', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <68K-stuff>
        T[0] = mktuple(AST_OPT_68K, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_OPT_68K, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<68K-stuff> =
    //\\    '-' <INT-CONST> '(' <NAME> ')',
    //\\    <INT-CONST> '(' <NAME> ')',
    //\\    <INT-CONST> '-' <INT-CONST>,
    //\\    <INT-CONST>,
    //\\    '#' <NAME>;

    case P_68K_stuff:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <INT-CONST>
        T[3] = lit(AST__LIT, '(', A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <NAME>
        T[5] = lit(AST__LIT, ')', A[ap+4]);
        T[0] = mktuple(AST_68K_stuff, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <INT-CONST>
        T[2] = lit(AST__LIT, '(', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = lit(AST__LIT, ')', A[ap+3]);
        T[0] = mktuple(AST_68K_stuff, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <INT-CONST>
        T[2] = lit(AST__LIT, '-', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <INT-CONST>
        T[0] = mktuple(AST_68K_stuff, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <INT-CONST>
        T[0] = mktuple(AST_68K_stuff, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '#', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[0] = mktuple(AST_68K_stuff, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # this is for 68000 Imp:
    //\\ #
    //\\ # %externalrealfnspecalias READ %alias "readreal"
    //\\ # %externalintegerfnspecalias READ
    //\\ # I have no idea what a "specalias" is :-(
    //\\ #
    //\\ # %integerarrayspec(16_C00000) frame(0:32767)
    //\\ # %integerfnspec(16_1124) REM(%integer a,b)
    //\\ # %bytespec(16_7fffc)status
    //\\ #
    //\\ # I think these extensions are also covered with a *different* extension in the same compiler :-(
    //\\ # i.e.
    //\\ #   @16_7fffc %byte status  ???
    //\\ #

    //\\ P<spec> =
    //\\    "specalias",
    //\\    "spec" <Opt-68K-code-address>;

    case P_spec:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_specalias, A[ap+0]);
        T[0] = mktuple(AST_spec, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_spec, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-68K-code-address>
        T[0] = mktuple(AST_spec, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<Opt-68K-code-address> =
    //\\    '(' <INT-CONST> ')',
    //\\   ;

    case P_Opt_68K_code_address:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <INT-CONST>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_Opt_68K_code_address, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_Opt_68K_code_address, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-spec> =
    //\\    <spec>,
    //\\   ;

    case P_OPT_spec:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <spec>
        T[0] = mktuple(AST_OPT_spec, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_spec, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # All labels come through here.  Because of an interaction with comments,
    //\\ # we use some parse-time code in phrase <LABEL-COLON> to modify how line reconstruction
    //\\ # handles the comments.  btw Numeric labels are a historical form...

    //\\ P<OPT-LABEL-DEFINITION> =
    //\\    <NAME> '(' <CEXPR> ')' <LABEL-COLON> <OPT-LABEL-DEFINITION>,
    //\\    <NAME> '(' '*' ')' <LABEL-COLON> <OPT-LABEL-DEFINITION>,
    //\\    <NAME> <LABEL-COLON> <OPT-LABEL-DEFINITION>,
    //\\    <DIGIT-SEQ> <LABEL-COLON> <OPT-LABEL-DEFINITION>,
    //\\   ;

    case P_OPT_LABEL_DEFINITION:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = lit(AST__LIT, '(', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <CEXPR>
        T[4] = lit(AST__LIT, ')', A[ap+3]);
        T[5] = build_ast(A[ap+4], depth+1); // <LABEL-COLON>
        T[6] = build_ast(A[ap+5], depth+1); // <OPT-LABEL-DEFINITION>
        T[0] = mktuple(AST_OPT_LABEL_DEFINITION, alt /*0*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = lit(AST__LIT, '(', A[ap+1]);
        T[3] = lit(AST__LIT, '*', A[ap+2]);
        T[4] = lit(AST__LIT, ')', A[ap+3]);
        T[5] = build_ast(A[ap+4], depth+1); // <LABEL-COLON>
        T[6] = build_ast(A[ap+5], depth+1); // <OPT-LABEL-DEFINITION>
        T[0] = mktuple(AST_OPT_LABEL_DEFINITION, alt /*1*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = build_ast(A[ap+1], depth+1); // <LABEL-COLON>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-LABEL-DEFINITION>
        T[0] = mktuple(AST_OPT_LABEL_DEFINITION, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
        T[2] = build_ast(A[ap+1], depth+1); // <LABEL-COLON>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-LABEL-DEFINITION>
        T[0] = mktuple(AST_OPT_LABEL_DEFINITION, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPT_LABEL_DEFINITION, alt /*4*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # fault statements can only appear in the outer block of a program;
    //\\ # they cannot appear in external routines.  This should be checked
    //\\ # by the compile() procedure.

    //\\ P<FAULT-S> =
    //\\    "fault" <FAULT-ACTION> <MORE-FAULTS> <S>;

    case P_FAULT_S:
      T[1] = kw(AST__KW, KW_fault, A[ap+0]);
      T[2] = build_ast(A[ap+1], depth+1); // <FAULT-ACTION>
      T[3] = build_ast(A[ap+2], depth+1); // <MORE-FAULTS>
      T[4] = build_ast(A[ap+3], depth+1); // <S>
      return mktuple(AST_FAULT_S, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);

    //\\ P<FAULT-LIST> =
    //\\    <DIGIT-SEQ> <MORE-NUMBERS>;

    case P_FAULT_LIST:
      T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
      T[2] = build_ast(A[ap+1], depth+1); // <MORE-NUMBERS>
      return mktuple(AST_FAULT_LIST, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<MORE-NUMBERS> =
    //\\    ',' <DIGIT-SEQ> <MORE-NUMBERS>,
    //\\   ;

    case P_MORE_NUMBERS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <DIGIT-SEQ>
        T[3] = build_ast(A[ap+2], depth+1); // <MORE-NUMBERS>
        T[0] = mktuple(AST_MORE_NUMBERS, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_MORE_NUMBERS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<FAULT-ACTION> =
    //\\    <FAULT-LIST> '-' '>' <LABEL>;

    case P_FAULT_ACTION:
      T[1] = build_ast(A[ap+0], depth+1); // <FAULT-LIST>
      T[2] = lit(AST__LIT, '-', A[ap+1]);
      T[3] = lit(AST__LIT, '>', A[ap+2]);
      T[4] = build_ast(A[ap+3], depth+1); // <LABEL>
      return mktuple(AST_FAULT_ACTION, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);

    //\\ P<MORE-FAULTS> =
    //\\    ',' <FAULT-ACTION> <MORE-FAULTS>,
    //\\   ;

    case P_MORE_FAULTS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <FAULT-ACTION>
        T[3] = build_ast(A[ap+2], depth+1); // <MORE-FAULTS>
        T[0] = mktuple(AST_MORE_FAULTS, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_MORE_FAULTS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # The "spec" form below is for old EMAS Imp procedure parameters:
    //\\ #
    //\\ # %routine integrate(%realname y, %real a,b, %integer n, %c
    //\\ #                    %realfn f)
    //\\ #   %spec f(%real x)
    //\\ #
    //\\ # the fully-qualified form (%realfnspec f(%real x)) is also valid but comes in via the <EXTERNAL> route.
    //\\ #
    //\\ # There are other types of %spec related to parameters to procedures as well -
    //\\ # %recordspec for sure.  Not so sure if there is an arrayspec.
    //\\ # They are only allowed in the body of a procedure.
    //\\ #
    //\\ # (Btw in the older compilers, all declarations in a block had to precede any code.)
    //\\ #
    //\\ # strictly speaking the <UI> <REST-OF-SS1> form, <UI> "while" <SC>, should only
    //\\ # allow a single <UI>, not the <UI> <AUI> form. (EMAS Imp)
    //\\ # (page 4.4 of https://history.dcs.ed.ac.uk/archive/docs/Edinburgh_IMP_Language_Manual.pdf )

    //\\ P<INTERNAL> =
    //\\    <NESTED-BLOCK>,
    //\\    <UI> <REST-OF-SS1>,
    //\\    '*' <UCI>,
    //\\    <CYCLE-S>,
    //\\    <IF-S>,
    //\\    <FAULT-S>,
    //\\    "on" <OPT-event> <EVENT-LIST> <REST-OF-NAME-LIST> "start" <S> <INTERNALS> "finish" <S>,
    //\\    "spec" <NAME> <FPP> <S>,
    //\\    <EXTERNAL-OR-INTERNAL>;

    case P_INTERNAL:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NESTED-BLOCK>
        T[0] = mktuple(AST_INTERNAL, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <UI>
        T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-SS1>
        T[0] = mktuple(AST_INTERNAL, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UCI>
        T[0] = mktuple(AST_INTERNAL, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <CYCLE-S>
        T[0] = mktuple(AST_INTERNAL, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 4) {
        T[1] = build_ast(A[ap+0], depth+1); // <IF-S>
        T[0] = mktuple(AST_INTERNAL, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 5) {
        T[1] = build_ast(A[ap+0], depth+1); // <FAULT-S>
        T[0] = mktuple(AST_INTERNAL, alt /*5*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 6) {
        T[1] = kw(AST__KW, KW_on, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-event>
        T[3] = build_ast(A[ap+2], depth+1); // <EVENT-LIST>
        T[4] = build_ast(A[ap+3], depth+1); // <REST-OF-NAME-LIST>
        T[5] = kw(AST__KW, KW_start, A[ap+4]);
        T[6] = build_ast(A[ap+5], depth+1); // <S>
        T[7] = build_ast(A[ap+6], depth+1); // <INTERNALS>
        T[8] = kw(AST__KW, KW_finish, A[ap+7]);
        T[9] = build_ast(A[ap+8], depth+1); // <S>
        T[0] = mktuple(AST_INTERNAL, alt /*6*/, /*phrases*/ 9, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]*/);
      } else if (alt == 7) {
        T[1] = kw(AST__KW, KW_spec, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <FPP>
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[0] = mktuple(AST_INTERNAL, alt /*7*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <EXTERNAL-OR-INTERNAL>
        T[0] = mktuple(AST_INTERNAL, alt /*8*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # unlike with old-style parsers, the whole of the block content ends up in an AST item.
    //\\ #
    //\\ # Unfortunately there is a problem caused by 68000 Imp: some files contain only
    //\\ # begin/end with no endoffile.  Some are begin/end;endoffile instead of begin/endofprogram
    //\\ # A few are routine blah/endofprogram !
    //\\ # One had begin/endofprogram followed by more routines.
    //\\ # (and see elsewhere for 68000's external routine specs which are of the form
    //\\ #   @address %routine blah
    //\\ #  - with no %spec keyword, but no routine body either..)
    //\\ #

    //\\ P<NESTED-BLOCK> =
    //\\    "begin" <S> <INTERNALS> "end" <S>;

    case P_NESTED_BLOCK:
      T[1] = kw(AST__KW, KW_begin, A[ap+0]);
      T[2] = build_ast(A[ap+1], depth+1); // <S>
      T[3] = build_ast(A[ap+2], depth+1); // <INTERNALS>
      T[4] = kw(AST__KW, KW_end, A[ap+3]);
      T[5] = build_ast(A[ap+4], depth+1); // <S>
      return mktuple(AST_NESTED_BLOCK, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);

    //\\ # compound statements - blah %and blah %and blah - a short form of startfinish block

    //\\ P<AUI> =
    //\\    "and" <UI>,
    //\\   ;

    case P_AUI:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_and, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UI>
        T[0] = mktuple(AST_AUI, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_AUI, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-ASSIGN> =
    //\\    '-' '>' <STRING-RESOLUTION-EXPR>,
    //\\    <ASSOP> <EXPR>,
    //\\   ;

    case P_OPT_ASSIGN:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[2] = lit(AST__LIT, '>', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <STRING-RESOLUTION-EXPR>
        T[0] = mktuple(AST_OPT_ASSIGN, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <ASSOP>
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[0] = mktuple(AST_OPT_ASSIGN, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_OPT_ASSIGN, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # the compound statement form ends in UIs which change control flow, ie
    //\\ # you cannot say: %if whatever %then blah %and ->lab %and this will never happen
    //\\ # tempted to add %caption here ;-)  But that may be a step too far.
    //\\ # "%monitor 9" would be trapped by "%fault 9 -> lab"

    //\\ P<UI> =
    //\\    <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD> <OPT-ASSIGN> <AUI>,
    //\\    "print" "text" <sqstring> <AUI>,
    //\\    '-' '>' <LABEL>,
    //\\    "return",
    //\\    "true",
    //\\    "false",
    //\\    "result" <ASSOP> <EXPR>,
    //\\    "monitorstop",
    //\\    "monitor" <DIGIT-SEQ> <AUI>,
    //\\    "monitor" <AUI>,
    //\\    "stop",
    //\\    "signal" <OPT-event> <CEXPR> <OPEXPR2>,
    //\\    "exit",
    //\\    "continue";

    case P_UI:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-PARAMETERS-OR-POINTERINDEX>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-RECORDFIELD>
        T[4] = build_ast(A[ap+3], depth+1); // <OPT-ASSIGN>
        T[5] = build_ast(A[ap+4], depth+1); // <AUI>
        T[0] = mktuple(AST_UI, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_print, A[ap+0]);
        T[2] = kw(AST__KW, KW_text, A[ap+1]);
        T[3] = bip(AST_sqstring, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <AUI>
        T[0] = mktuple(AST_UI, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[2] = lit(AST__LIT, '>', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <LABEL>
        T[0] = mktuple(AST_UI, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 3) {
        T[1] = kw(AST__KW, KW_return, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 4) {
        T[1] = kw(AST__KW, KW_true, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 5) {
        T[1] = kw(AST__KW, KW_false, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*5*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 6) {
        T[1] = kw(AST__KW, KW_result, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <ASSOP>
        T[3] = build_ast(A[ap+2], depth+1); // <EXPR>
        T[0] = mktuple(AST_UI, alt /*6*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 7) {
        T[1] = kw(AST__KW, KW_monitorstop, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*7*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 8) {
        T[1] = kw(AST__KW, KW_monitor, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <DIGIT-SEQ>
        T[3] = build_ast(A[ap+2], depth+1); // <AUI>
        T[0] = mktuple(AST_UI, alt /*8*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 9) {
        T[1] = kw(AST__KW, KW_monitor, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <AUI>
        T[0] = mktuple(AST_UI, alt /*9*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 10) {
        T[1] = kw(AST__KW, KW_stop, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*10*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 11) {
        T[1] = kw(AST__KW, KW_signal, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-event>
        T[3] = build_ast(A[ap+2], depth+1); // <CEXPR>
        T[4] = build_ast(A[ap+3], depth+1); // <OPEXPR2>
        T[0] = mktuple(AST_UI, alt /*11*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 12) {
        T[1] = kw(AST__KW, KW_exit, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*12*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_continue, A[ap+0]);
        T[0] = mktuple(AST_UI, alt /*13*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<LABEL> =
    //\\    <NAME> '(' <EXPR> ')',
    //\\    <NAME>,
    //\\    <DIGIT-SEQ>;

    case P_LABEL:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = lit(AST__LIT, '(', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <EXPR>
        T[4] = lit(AST__LIT, ')', A[ap+3]);
        T[0] = mktuple(AST_LABEL, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[0] = mktuple(AST_LABEL, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
        T[0] = mktuple(AST_LABEL, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # prim and perm added by gt

    //\\ P<FILE-END-TYPE> =
    //\\    "file",
    //\\    "prim",
    //\\    "perm";

    case P_FILE_END_TYPE:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_file, A[ap+0]);
        T[0] = mktuple(AST_FILE_END_TYPE, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_prim, A[ap+0]);
        T[0] = mktuple(AST_FILE_END_TYPE, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_perm, A[ap+0]);
        T[0] = mktuple(AST_FILE_END_TYPE, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # I added an old record declaration syntax for examples out of ai2logo.i
    //\\ #
    //\\ # %OWNRECORDARRAY INDEX42( 0:1022)           (PIC DIR)
    //\\ #
    //\\ # %SYSTEMROUTINESPEC FINFO(%STRING(15) S,%INTEGER LEV,%C
    //\\ #    %RECORDNAME R, %INTEGERNAME FLAG)
    //\\ #
    //\\ # %RECORD R(F)
    //\\ #
    //\\ # I'm not sure where/if this is handled:
    //\\ #
    //\\ # %record(f)%array portinfo(1:31)
    //\\ #
    //\\ # I need to get the old Imp manuals out and check a couple of things, in particular
    //\\ #   %recordformatspec and %arrayformatspec ...
    //\\ #
    //\\ # The grammar below allows some initialisations that the compilers don't support
    //\\ # but it's cleaner to reject them in compile()
    //\\ #
    //\\ # I had to add initialisations with pointers for these examples from
    //\\ # 68000 Imp: %bytename b == length(s)
    //\\ # %ownrecord(i tc f)%namearray tc refs(1:maxtc) == nil(*)
    //\\ #
    //\\ # These are 68000 syntax which is rejected by Imp77:
    //\\ # %conststring(1) %array(1:max params) parameters="B","W","L"
    //\\ # %string(255) %array(0:maxfiles) list
    //\\ #
    //\\ # The equivalent Imp77 syntax would be
    //\\ #
    //\\ # %conststring(1) %array parameters(1:max params)="B","W","L"
    //\\ # %string(255) %array list(0:maxfiles)
    //\\ #
    //\\ # Finally here's a one-off hack for a construct I've only seen *once*
    //\\ # in APM-gdmr/I/PRIM22.imp ...
    //\\ # %constchar                    snl=char<10>
    //\\ #

    //\\ P<DECLARATION-S> =
    //\\    <OPT-68K> <XOWN> <XTYPE> <OWNDEC> <S>,
    //\\    <OPT-68K> <XTYPE> <DECLN> <S>,
    //\\    <OPT-68K> <OPT-XOWN> "record" "array" <RADEC> <REST-OF-RADEC> <S>,
    //\\    <OPT-68K> <OPT-XOWN> "record" <RDEC> <REST-OF-RDEC> <S>,
    //\\    "const" "char" <NAME> '=' 'C' 'H' 'A' 'R' '<' <INT-CONST> '>' <S>;

    case P_DECLARATION_S:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-68K>
        T[2] = build_ast(A[ap+1], depth+1); // <XOWN>
        T[3] = build_ast(A[ap+2], depth+1); // <XTYPE>
        T[4] = build_ast(A[ap+3], depth+1); // <OWNDEC>
        T[5] = build_ast(A[ap+4], depth+1); // <S>
        T[0] = mktuple(AST_DECLARATION_S, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-68K>
        T[2] = build_ast(A[ap+1], depth+1); // <XTYPE>
        T[3] = build_ast(A[ap+2], depth+1); // <DECLN>
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[0] = mktuple(AST_DECLARATION_S, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-68K>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-XOWN>
        T[3] = kw(AST__KW, KW_record, A[ap+2]);
        T[4] = kw(AST__KW, KW_array, A[ap+3]);
        T[5] = build_ast(A[ap+4], depth+1); // <RADEC>
        T[6] = build_ast(A[ap+5], depth+1); // <REST-OF-RADEC>
        T[7] = build_ast(A[ap+6], depth+1); // <S>
        T[0] = mktuple(AST_DECLARATION_S, alt /*2*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-68K>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-XOWN>
        T[3] = kw(AST__KW, KW_record, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <RDEC>
        T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-RDEC>
        T[6] = build_ast(A[ap+5], depth+1); // <S>
        T[0] = mktuple(AST_DECLARATION_S, alt /*3*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else               {
        T[1] = kw(AST__KW, KW_const, A[ap+0]);
        T[2] = kw(AST__KW, KW_char, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = lit(AST__LIT, '=', A[ap+3]);
        T[5] = lit(AST__LIT, 'C', A[ap+4]);
        T[6] = lit(AST__LIT, 'H', A[ap+5]);
        T[7] = lit(AST__LIT, 'A', A[ap+6]);
        T[8] = lit(AST__LIT, 'R', A[ap+7]);
        T[9] = lit(AST__LIT, '<', A[ap+8]);
        T[10] = build_ast(A[ap+9], depth+1); // <INT-CONST>
        T[11] = lit(AST__LIT, '>', A[ap+10]);
        T[12] = build_ast(A[ap+11], depth+1); // <S>
        T[0] = mktuple(AST_DECLARATION_S, alt /*4*/, /*phrases*/ 12, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9], T[10], T[11], T[12]*/);
      }
      return T[0];

    //\\ P<OPT-XOWN> =
    //\\    <XOWN>,
    //\\   ;

    case P_OPT_XOWN:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <XOWN>
        T[0] = mktuple(AST_OPT_XOWN, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_XOWN, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # old-style record array declaration

    //\\ P<RADEC> =
    //\\    <NAME> <Opt-NAME-LIST> <BPAIR> '(' <RECFMT-REF> ')';

    case P_RADEC:
      T[1] = build_ast(A[ap+0], depth+1); // <NAME>
      T[2] = build_ast(A[ap+1], depth+1); // <Opt-NAME-LIST>
      T[3] = build_ast(A[ap+2], depth+1); // <BPAIR>
      T[4] = lit(AST__LIT, '(', A[ap+3]);
      T[5] = build_ast(A[ap+4], depth+1); // <RECFMT-REF>
      T[6] = lit(AST__LIT, ')', A[ap+5]);
      return mktuple(AST_RADEC, alt /*0*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);

    //\\ P<REST-OF-RADEC> =
    //\\    ',' <RADEC>,
    //\\   ;

    case P_REST_OF_RADEC:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <RADEC>
        T[0] = mktuple(AST_REST_OF_RADEC, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_RADEC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # old-style record declaration

    //\\ P<RDEC> =
    //\\    <NAME> <Opt-NAME-LIST> '(' <RECFMT-REF> ')';

    case P_RDEC:
      T[1] = build_ast(A[ap+0], depth+1); // <NAME>
      T[2] = build_ast(A[ap+1], depth+1); // <Opt-NAME-LIST>
      T[3] = lit(AST__LIT, '(', A[ap+2]);
      T[4] = build_ast(A[ap+3], depth+1); // <RECFMT-REF>
      T[5] = lit(AST__LIT, ')', A[ap+4]);
      return mktuple(AST_RDEC, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);

    //\\ P<REST-OF-RDEC> =
    //\\    ',' <RDEC> <REST-OF-RDEC>,
    //\\   ;

    case P_REST_OF_RDEC:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <RDEC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-RDEC>
        T[0] = mktuple(AST_REST_OF_RDEC, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_RDEC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # %on %event * %start or %on %event 1,2,3,etc %start

    //\\ P<EVENT-LIST> =
    //\\    '*',
    //\\    <CEXPR>;

    case P_EVENT_LIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[0] = mktuple(AST_EVENT_LIST, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <CEXPR>
        T[0] = mktuple(AST_EVENT_LIST, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # Added old-style while/until for: %UNTIL NEXTINSYM=NL %THEN %CYCLE
    //\\ # and %WHILE NEXTINSYM#NL %THEN SKIPINSYM

    //\\ P<CYCLE-S> =
    //\\    "cycle" <Opt-CYCPARM> <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
    //\\    <Percent-WUF> "cycle" <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
    //\\    <Percent-WU> "then" "cycle" <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
    //\\    <Percent-WU> "then" <UI>;

    case P_CYCLE_S:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_cycle, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-CYCPARM>
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[4] = build_ast(A[ap+3], depth+1); // <INTERNALS>
        T[5] = kw(AST__KW, KW_repeat, A[ap+4]);
        T[6] = build_ast(A[ap+5], depth+1); // <Opt-UNTIL>
        T[7] = build_ast(A[ap+6], depth+1); // <S>
        T[0] = mktuple(AST_CYCLE_S, alt /*0*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-WUF>
        T[2] = kw(AST__KW, KW_cycle, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[4] = build_ast(A[ap+3], depth+1); // <INTERNALS>
        T[5] = kw(AST__KW, KW_repeat, A[ap+4]);
        T[6] = build_ast(A[ap+5], depth+1); // <Opt-UNTIL>
        T[7] = build_ast(A[ap+6], depth+1); // <S>
        T[0] = mktuple(AST_CYCLE_S, alt /*1*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-WU>
        T[2] = kw(AST__KW, KW_then, A[ap+1]);
        T[3] = kw(AST__KW, KW_cycle, A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <S>
        T[5] = build_ast(A[ap+4], depth+1); // <INTERNALS>
        T[6] = kw(AST__KW, KW_repeat, A[ap+5]);
        T[7] = build_ast(A[ap+6], depth+1); // <Opt-UNTIL>
        T[8] = build_ast(A[ap+7], depth+1); // <S>
        T[0] = mktuple(AST_CYCLE_S, alt /*2*/, /*phrases*/ 8, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-WU>
        T[2] = kw(AST__KW, KW_then, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <UI>
        T[0] = mktuple(AST_CYCLE_S, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      }
      return T[0];

    //\\ P<IF-S> =
    //\\    <Percent-IU> <CONDITION> <THEN-S>;

    case P_IF_S:
      T[1] = build_ast(A[ap+0], depth+1); // <Percent-IU>
      T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
      T[3] = build_ast(A[ap+2], depth+1); // <THEN-S>
      return mktuple(AST_IF_S, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ # /home/gtoal/gtoal.com/athome/edinburgh/APM-gdmr/I/LIB.imp
    //\\ # contains:
    //\\ #          %elseif 'A'<=sym<='Z' %start
    //\\ #
    //\\ # which is a form of Hamish's that is equivalent to
    //\\ #
    //\\ #  %finish %else %if 'A'<=sym<='Z' %start
    //\\ #
    //\\ # or the more concise
    //\\ #
    //\\ #  %else %if 'A'<=sym<='Z'
    //\\ #
    //\\ # An identical issue is: %elsestart
    //\\ # in APM-gdmr/ETHER/PROFILE.imp
    //\\ #

    //\\ P<FINISH-S> =
    //\\    "else" <Percent-IU> <CONDITION> <Opt-start> <S> <INTERNALS> <FINISH-S>,
    //\\    "else" <Opt-start> <S> <INTERNALS> "finish" <S>,
    //\\    "finish" <Opt-ELSE-S>;

    case P_FINISH_S:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_else, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Percent-IU>
        T[3] = build_ast(A[ap+2], depth+1); // <CONDITION>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-start>
        T[5] = build_ast(A[ap+4], depth+1); // <S>
        T[6] = build_ast(A[ap+5], depth+1); // <INTERNALS>
        T[7] = build_ast(A[ap+6], depth+1); // <FINISH-S>
        T[0] = mktuple(AST_FINISH_S, alt /*0*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_else, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-start>
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[4] = build_ast(A[ap+3], depth+1); // <INTERNALS>
        T[5] = kw(AST__KW, KW_finish, A[ap+4]);
        T[6] = build_ast(A[ap+5], depth+1); // <S>
        T[0] = mktuple(AST_FINISH_S, alt /*1*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else               {
        T[1] = kw(AST__KW, KW_finish, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-ELSE-S>
        T[0] = mktuple(AST_FINISH_S, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<Opt-start> =
    //\\    "start",
    //\\   ;

    case P_Opt_start:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_start, A[ap+0]);
        T[0] = mktuple(AST_Opt_start, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_Opt_start, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # see the comments on <FINISH-S> above.

    //\\ P<Opt-ELSE-S> =
    //\\    "else" "start" <S> <INTERNALS> "finish" <S>,
    //\\    "else" <IF-S>,
    //\\    "else" <UI> <S>,
    //\\    <S>;

    case P_Opt_ELSE_S:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_else, A[ap+0]);
        T[2] = kw(AST__KW, KW_start, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[4] = build_ast(A[ap+3], depth+1); // <INTERNALS>
        T[5] = kw(AST__KW, KW_finish, A[ap+4]);
        T[6] = build_ast(A[ap+5], depth+1); // <S>
        T[0] = mktuple(AST_Opt_ELSE_S, alt /*0*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_else, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <IF-S>
        T[0] = mktuple(AST_Opt_ELSE_S, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_else, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UI>
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[0] = mktuple(AST_Opt_ELSE_S, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <S>
        T[0] = mktuple(AST_Opt_ELSE_S, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<THEN-S> =
    //\\    <OPT-then> "start" <S> <INTERNALS> <FINISH-S>,
    //\\    "then" <UI> <Opt-ELSE-S>;

    case P_THEN_S:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-then>
        T[2] = kw(AST__KW, KW_start, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[4] = build_ast(A[ap+3], depth+1); // <INTERNALS>
        T[5] = build_ast(A[ap+4], depth+1); // <FINISH-S>
        T[0] = mktuple(AST_THEN_S, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else               {
        T[1] = kw(AST__KW, KW_then, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UI>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-ELSE-S>
        T[0] = mktuple(AST_THEN_S, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      }
      return T[0];

    //\\ P<OPT-then> =
    //\\    "then",
    //\\   ;

    case P_OPT_then:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_then, A[ap+0]);
        T[0] = mktuple(AST_OPT_then, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_then, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-start> =
    //\\    "start",
    //\\   ;

    case P_OPT_start:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_start, A[ap+0]);
        T[0] = mktuple(AST_OPT_start, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_start, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<S> =
    //\\    <NL>,
    //\\    ';';

    case P_S:
      if (alt == 0)        {
        T[1] = bip(AST_NL, A[ap+0]);
        T[0] = mktuple(AST_S, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, ';', A[ap+0]);
        T[0] = mktuple(AST_S, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # Most comments are handled in the line reconstruction phase.
    //\\ # Comments starting with %COMMENT are an exception.
    //\\ # Some older compilers treated '!' comments like %COMMENT,
    //\\ # with "%c" at the end of a comment causing continuation!
    //\\ # This phrase does not allow ';' in a comment to terminate
    //\\ # the comment, but some versions of Imp (I think PDP15)
    //\\ # did, so that will have to be handled with a command-line
    //\\ # option and a parse-time phrase if it ever is needed.
    //\\ #

    //\\ P<COMMENT> =
    //\\    "comment" <Parsed-OldStyle-Comment>,
    //\\    '!' <Parsed-OldStyle-Comment>;

    case P_COMMENT:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_comment, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_COMMENT, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '!', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_COMMENT, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<Parsed-OldStyle-Comment> =
    //\\    <char> <Parsed-OldStyle-Comment>,
    //\\    <dqstring> <Parsed-OldStyle-Comment>,
    //\\    <sqstring> <Parsed-OldStyle-Comment>,
    //\\    <letter> <Parsed-OldStyle-Comment>,
    //\\    <digit> <Parsed-OldStyle-Comment>,
    //\\    <stropped> <Parsed-OldStyle-Comment>,
    //\\    <NL>;

    case P_Parsed_OldStyle_Comment:
      if (alt == 0)        {
        T[1] = bip(AST_char, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 1) {
        T[1] = bip(AST_dqstring, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 2) {
        T[1] = bip(AST_sqstring, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <letter>
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 4) {
        T[1] = build_ast(A[ap+0], depth+1); // <digit>
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 5) {
        T[1] = bip(AST_stropped, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Parsed-OldStyle-Comment>
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = bip(AST_NL, A[ap+0]);
        T[0] = mktuple(AST_Parsed_OldStyle_Comment, alt /*6*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<NL-or-SEMI> =
    //\\    <NL>,
    //\\    <semi>;

    case P_NL_or_SEMI:
      if (alt == 0)        {
        T[1] = bip(AST_NL, A[ap+0]);
        T[0] = mktuple(AST_NL_or_SEMI, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <semi>
        T[0] = mktuple(AST_NL_or_SEMI, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

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

    case P_semi:
      T[1] = lit(AST__LIT, ';', A[ap+0]);
      return mktuple(AST_semi, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);

    //\\ # <DIGIT-SEQ> '_' <DIGIT-SEQ>, and  <DIGIT-SEQ> '_' <NAME>, added by gt
    //\\ # <NAME> <sqstring> is really X'....' but type checking stops me matching 'X'
    //\\ # VERY hacky stopgap just to get stuff to parse. Needs proper syntax
    //\\ # which needs the ability to recognise individual digits and letters
    //\\ # rather than the current typed 'alphanumeric' hack in line_reconstruction ...
    //\\ # Doesn't handle %real well, and indeed not at all if containing an '@' sign.
    //\\ # E"ebcdic" etc also not handled.  Hamish's 40_XXX comes in here.
    //\\ #
    //\\ ## P<basedconst> =  "[0-9][0-9]*_[0-9A-Z][0-9A-Z]*"
    //\\ #

    //\\ P<INT-CONST> =
    //\\    <DIGIT-SEQ> '_' <alphanumeric-SEQ>,
    //\\    <DIGIT-SEQ>,
    //\\    <OLDSTYLE-BASE>;

    case P_INT_CONST:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
        T[2] = lit(AST__LIT, '_', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <alphanumeric-SEQ>
        T[0] = mktuple(AST_INT_CONST, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
        T[0] = mktuple(AST_INT_CONST, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <OLDSTYLE-BASE>
        T[0] = mktuple(AST_INT_CONST, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<alphanumeric-SEQ> =
    //\\    <alphanumeric> <Opt-alphanumeric-SEQ>;

    case P_alphanumeric_SEQ:
      T[1] = build_ast(A[ap+0], depth+1); // <alphanumeric>
      T[2] = build_ast(A[ap+1], depth+1); // <Opt-alphanumeric-SEQ>
      return mktuple(AST_alphanumeric_SEQ, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<Opt-alphanumeric-SEQ> =
    //\\    <alphanumeric> <Opt-alphanumeric-SEQ>,
    //\\   ;

    case P_Opt_alphanumeric_SEQ:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <alphanumeric>
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-alphanumeric-SEQ>
        T[0] = mktuple(AST_Opt_alphanumeric_SEQ, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_Opt_alphanumeric_SEQ, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<STR-CONST> =
    //\\    <OPT-DQ-LETTER> <dqstring>,
    //\\    <sqstring>;

    case P_STR_CONST:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-DQ-LETTER>
        T[2] = bip(AST_dqstring, A[ap+1]);
        T[0] = mktuple(AST_STR_CONST, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = bip(AST_sqstring, A[ap+0]);
        T[0] = mktuple(AST_STR_CONST, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # current line reconstruction doesn't support this. Letters come through as part of <alphanumeric>s.

    //\\ P<OPT-DQ-LETTER> =
    //\\    'E',
    //\\   ;

    case P_OPT_DQ_LETTER:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, 'E', A[ap+0]);
        T[0] = mktuple(AST_OPT_DQ_LETTER, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_DQ_LETTER, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<NUMERIC-CONST> =
    //\\    <INT-CONST>,
    //\\    <REAL-CONST>;

    case P_NUMERIC_CONST:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <INT-CONST>
        T[0] = mktuple(AST_NUMERIC_CONST, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <REAL-CONST>
        T[0] = mktuple(AST_NUMERIC_CONST, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # <CONST> is somewhat generic and should be replaced with the more specific version when possible

    //\\ P<CONST> =
    //\\    <INT-CONST>,
    //\\    <REAL-CONST>,
    //\\    <STR-CONST>;

    case P_CONST:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <INT-CONST>
        T[0] = mktuple(AST_CONST, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <REAL-CONST>
        T[0] = mktuple(AST_CONST, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <STR-CONST>
        T[0] = mktuple(AST_CONST, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # <sqstring> has to be checked for length and content depending on prefix.
    //\\ #
    //\\ ## P<lettercharconst> = <squote> <schar> <squote> |
    //\\ ##   "[RHX]" <squote> <hexchars> <squote> |
    //\\ ##   "B" <squote> <binchars> <squote> |
    //\\ ##   "K" <squote> <octchars> <squote> |
    //\\ ##   "[CMD]" <squote> <mchars> <squote>;
    //\\ #

    //\\ P<OLDSTYLE-BASE> =
    //\\    <letter> <sqstring>;

    case P_OLDSTYLE_BASE:
      T[1] = build_ast(A[ap+0], depth+1); // <letter>
      T[2] = bip(AST_sqstring, A[ap+1]);
      return mktuple(AST_OLDSTYLE_BASE, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ # Note: a <DIGIT-SEQ> can be both an integer constant and a real constant.
    //\\ # Imp77 supports constants like this: %constlongreal PI = 16_3.243F6A89
    //\\ # which are not handled here yet.
    //\\ #
    //\\ ## P<realconst> = "[0-9][0-9]*\.[0-9][0-9]*E[0-9][0-9]*" |
    //\\ ##                "[0-9][0-9]*\.[0-9][0-9]*" |
    //\\ ##                "[0-9][0-9]*@[0-9][0-9]*"
    //\\ #

    //\\ P<REAL-CONST> =
    //\\    <DIGIT-SEQ> <OPT-FRACPT> <OPT-EXPONENT>,
    //\\    <FRACPT> <OPT-EXPONENT>;

    case P_REAL_CONST:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-FRACPT>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-EXPONENT>
        T[0] = mktuple(AST_REAL_CONST, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <FRACPT>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-EXPONENT>
        T[0] = mktuple(AST_REAL_CONST, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<FRACPT> =
    //\\    '.' <DIGIT-SEQ>;

    case P_FRACPT:
      T[1] = lit(AST__LIT, '.', A[ap+0]);
      T[2] = build_ast(A[ap+1], depth+1); // <DIGIT-SEQ>
      return mktuple(AST_FRACPT, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<OPT-FRACPT> =
    //\\    <FRACPT>,
    //\\   ;

    case P_OPT_FRACPT:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <FRACPT>
        T[0] = mktuple(AST_OPT_FRACPT, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_FRACPT, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-Plus-Minus> =
    //\\    '+',
    //\\    '-',
    //\\   ;

    case P_OPT_Plus_Minus:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '+', A[ap+0]);
        T[0] = mktuple(AST_OPT_Plus_Minus, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[0] = mktuple(AST_OPT_Plus_Minus, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_Plus_Minus, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-EXPONENT> =
    //\\    '@' <OPT-Plus-Minus> <DIGIT-SEQ>,
    //\\   ;

    case P_OPT_EXPONENT:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '@', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-Plus-Minus>
        T[3] = build_ast(A[ap+2], depth+1); // <DIGIT-SEQ>
        T[0] = mktuple(AST_OPT_EXPONENT, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPT_EXPONENT, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<DIGIT-SEQ> =
    //\\    <digit> <rest-of-number>;

    case P_DIGIT_SEQ:
      T[1] = build_ast(A[ap+0], depth+1); // <digit>
      T[2] = build_ast(A[ap+1], depth+1); // <rest-of-number>
      return mktuple(AST_DIGIT_SEQ, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<rest-of-number> =
    //\\    <DIGIT-SEQ>,
    //\\   ;

    case P_rest_of_number:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <DIGIT-SEQ>
        T[0] = mktuple(AST_rest_of_number, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_rest_of_number, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<NAME> =
    //\\    <letter> <!SQUO-STRING> <opt-letter-or-digit-seq>;

    case P_NAME:
      T[1] = build_ast(A[ap+0], depth+1); // <letter>
      T[2] = -1; // <!SQUO-STRING>
      T[3] = build_ast(A[ap+2], depth+1); // <opt-letter-or-digit-seq>
      return mktuple(AST_NAME, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ ## P<NAME> =
    //\\ ##    <alphanumeric> <!SQUO-STRING> <rest-of-name>;
    //\\ ## P<rest-of-name> =
    //\\ ##    <NAME>,
    //\\ ##   ;

    //\\ P<SQUO-STRING> =
    //\\    <sqstring>;

    case P_SQUO_STRING:
      T[1] = bip(AST_sqstring, A[ap+0]);
      return mktuple(AST_SQUO_STRING, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);

    //\\ # A UCI is a machine code instruction.
    //\\ # (Hamish's PDP15 imp machine code syntax (which is not introduced by '*') is definitely not supported.)
    //\\ #
    //\\ # Machine-specific machine-code removed. May cause problems with ';' and quoted chars in machine code
    //\\ #

    //\\ P<UCI> =
    //\\    <NL-or-SEMI>,
    //\\    <char> <UCI>,
    //\\    <dqstring> <UCI>,
    //\\    <sqstring> <UCI>,
    //\\    <stropped> <UCI>;

    case P_UCI:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NL-or-SEMI>
        T[0] = mktuple(AST_UCI, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = bip(AST_char, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UCI>
        T[0] = mktuple(AST_UCI, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 2) {
        T[1] = bip(AST_dqstring, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UCI>
        T[0] = mktuple(AST_UCI, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 3) {
        T[1] = bip(AST_sqstring, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UCI>
        T[0] = mktuple(AST_UCI, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = bip(AST_stropped, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <UCI>
        T[0] = mktuple(AST_UCI, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # Imp does not let you mix and/or without explicit brackets. They do not have a precedence relationship

    //\\ P<REST-OF-COND> =
    //\\    "and" <SC> <REST-OF-ANDC>,
    //\\    "or" <SC> <REST-OF-ORC>,
    //\\   ;

    case P_REST_OF_COND:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_and, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <SC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-ANDC>
        T[0] = mktuple(AST_REST_OF_COND, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_or, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <SC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-ORC>
        T[0] = mktuple(AST_REST_OF_COND, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_COND, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-ANDC> =
    //\\    "and" <SC> <REST-OF-ANDC>,
    //\\   ;

    case P_REST_OF_ANDC:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_and, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <SC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-ANDC>
        T[0] = mktuple(AST_REST_OF_ANDC, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_ANDC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-ORC> =
    //\\    "or" <SC> <REST-OF-ORC>,
    //\\   ;

    case P_REST_OF_ORC:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_or, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <SC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-ORC>
        T[0] = mktuple(AST_REST_OF_ORC, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_ORC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<CONDITION> =
    //\\    <SC> <REST-OF-COND>;

    case P_CONDITION:
      T[1] = build_ast(A[ap+0], depth+1); // <SC>
      T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-COND>
      return mktuple(AST_CONDITION, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ # <NAME> <OPT-ACTUAL-PARAMETERS> added by gt for predicates
    //\\ # RESTOFSC handles double-sided conditions such as '0' <= ch <= '9'

    //\\ P<SC> =
    //\\    <STRING-RESOLUTION>,
    //\\    <EXPR> <COMP> <EXPR> <OPT-DOUBLE-SIDED>,
    //\\    <NAME> <OPT-ACTUAL-PARAMETERS>,
    //\\    '(' <CONDITION> ')',
    //\\    "not" <SC>;

    case P_SC:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <STRING-RESOLUTION>
        T[0] = mktuple(AST_SC, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <EXPR>
        T[2] = build_ast(A[ap+1], depth+1); // <COMP>
        T[3] = build_ast(A[ap+2], depth+1); // <EXPR>
        T[4] = build_ast(A[ap+3], depth+1); // <OPT-DOUBLE-SIDED>
        T[0] = mktuple(AST_SC, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-ACTUAL-PARAMETERS>
        T[0] = mktuple(AST_SC, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 3) {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_SC, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = kw(AST__KW, KW_not, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <SC>
        T[0] = mktuple(AST_SC, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<OPT-DOUBLE-SIDED> =
    //\\    <COMP> <EXPR>,
    //\\   ;

    case P_OPT_DOUBLE_SIDED:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <COMP>
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[0] = mktuple(AST_OPT_DOUBLE_SIDED, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_OPT_DOUBLE_SIDED, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<STRING-RESOLUTION> =
    //\\    <STRING-EXPR> '-' '>' <STRING-RESOLUTION-EXPR>;

    case P_STRING_RESOLUTION:
      T[1] = build_ast(A[ap+0], depth+1); // <STRING-EXPR>
      T[2] = lit(AST__LIT, '-', A[ap+1]);
      T[3] = lit(AST__LIT, '>', A[ap+2]);
      T[4] = build_ast(A[ap+3], depth+1); // <STRING-RESOLUTION-EXPR>
      return mktuple(AST_STRING_RESOLUTION, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);

    //\\ # Current Imp77 restricts string resolution to -> a.("test").b  with a and b being optional.
    //\\ # Older Emas implementations allowed multiple parts I'm fairly sure, eg -> a.("match1").b.("match2").c
    //\\ #
    //\\ # Also, behaviour changed re initial ("match") with no assignment variable before it -
    //\\ # used to be that "match" had to start the string, but now there can be text before
    //\\ # it, and a null match has to be coded as -> a.("match").b %and a = ""
    //\\ #
    //\\ # So if we need to distinguish between these variations, do it in compile()
    //\\ #

    //\\ P<STRING-RESOLUTION-EXPR> =
    //\\    <STR-MATCH> <OPT-STR-ASSIGN>,
    //\\    <STR-ASSIGN> <OPT-STR-MATCH>;

    case P_STRING_RESOLUTION_EXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <STR-MATCH>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-STR-ASSIGN>
        T[0] = mktuple(AST_STRING_RESOLUTION_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <STR-ASSIGN>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-STR-MATCH>
        T[0] = mktuple(AST_STRING_RESOLUTION_EXPR, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<STRING-LVALUE> =
    //\\    <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>;

    case P_STRING_LVALUE:
      T[1] = build_ast(A[ap+0], depth+1); // <NAME>
      T[2] = build_ast(A[ap+1], depth+1); // <Opt-PARAMETERS-OR-POINTERINDEX>
      T[3] = build_ast(A[ap+2], depth+1); // <Opt-RECORDFIELD-OR-POINTERINDEX>
      return mktuple(AST_STRING_LVALUE, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ P<STRING-RVALUE> =
    //\\    <STRING-LVALUE>,
    //\\    <STR-CONST>;

    case P_STRING_RVALUE:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <STRING-LVALUE>
        T[0] = mktuple(AST_STRING_RVALUE, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <STR-CONST>
        T[0] = mktuple(AST_STRING_RVALUE, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # No bracketed expressions, only '.' operator allowed, although once again,
    //\\ # Hamish has an idiosyncratic extension where strings in a printstring are
    //\\ # separated by semi-colons - I think it is some approximation to C's printf?
    //\\ #

    //\\ P<STRING-EXPR> =
    //\\    <STRING-RVALUE> <REST-OF-STRING-EXPR>;

    case P_STRING_EXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <STRING-RVALUE>
      T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-STRING-EXPR>
      return mktuple(AST_STRING_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<REST-OF-STRING-EXPR> =
    //\\    '.' <STRING-RVALUE> <REST-OF-STRING-EXPR>,
    //\\   ;

    case P_REST_OF_STRING_EXPR:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '.', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <STRING-RVALUE>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-STRING-EXPR>
        T[0] = mktuple(AST_REST_OF_STRING_EXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_STRING_EXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<STR-MATCH> =
    //\\    '(' <STRING-EXPR> ')';

    case P_STR_MATCH:
      T[1] = lit(AST__LIT, '(', A[ap+0]);
      T[2] = build_ast(A[ap+1], depth+1); // <STRING-EXPR>
      T[3] = lit(AST__LIT, ')', A[ap+2]);
      return mktuple(AST_STR_MATCH, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ P<STR-ASSIGN> =
    //\\    <STRING-LVALUE>;

    case P_STR_ASSIGN:
      T[1] = build_ast(A[ap+0], depth+1); // <STRING-LVALUE>
      return mktuple(AST_STR_ASSIGN, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);

    //\\ # ping-pong.  Can't allow -> (a).b.c.(d) or -> a.(b).(c).d

    //\\ P<OPT-STR-ASSIGN> =
    //\\    '.' <STR-ASSIGN> <OPT-STR-MATCH>,
    //\\   ;

    case P_OPT_STR_ASSIGN:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '.', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <STR-ASSIGN>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-STR-MATCH>
        T[0] = mktuple(AST_OPT_STR_ASSIGN, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPT_STR_ASSIGN, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-STR-MATCH> =
    //\\    '.' <STR-MATCH> <OPT-STR-ASSIGN>,
    //\\   ;

    case P_OPT_STR_MATCH:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '.', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <STR-MATCH>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-STR-ASSIGN>
        T[0] = mktuple(AST_OPT_STR_MATCH, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPT_STR_MATCH, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # Ordering is important, eg '##' must come before '#'
    //\\ #
    //\\ # I really pity people who use the sort of grammar that requires
    //\\ # eliminating shift-reduce conflicts before they can parse anything :-)
    //\\ #

    //\\ P<COMP> =
    //\\    '=' '=',
    //\\    '=',
    //\\    <NOT-EQUALS-ADDRESS>,
    //\\    <NOT-EQUALS>,
    //\\    '<' '=',
    //\\    '<',
    //\\    '>' '=',
    //\\    '>';

    case P_COMP:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '=', A[ap+0]);
        T[2] = lit(AST__LIT, '=', A[ap+1]);
        T[0] = mktuple(AST_COMP, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '=', A[ap+0]);
        T[0] = mktuple(AST_COMP, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <NOT-EQUALS-ADDRESS>
        T[0] = mktuple(AST_COMP, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 3) {
        T[1] = build_ast(A[ap+0], depth+1); // <NOT-EQUALS>
        T[0] = mktuple(AST_COMP, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 4) {
        T[1] = lit(AST__LIT, '<', A[ap+0]);
        T[2] = lit(AST__LIT, '=', A[ap+1]);
        T[0] = mktuple(AST_COMP, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 5) {
        T[1] = lit(AST__LIT, '<', A[ap+0]);
        T[0] = mktuple(AST_COMP, alt /*5*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 6) {
        T[1] = lit(AST__LIT, '>', A[ap+0]);
        T[2] = lit(AST__LIT, '=', A[ap+1]);
        T[0] = mktuple(AST_COMP, alt /*6*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '>', A[ap+0]);
        T[0] = mktuple(AST_COMP, alt /*7*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # '<>' is a really obscure form but it definitely used to be used.  I'll
    //\\ # post a reference as soon as I locate one.
    //\\ #

    //\\ P<NOT-EQUALS> =
    //\\    '#',
    //\\    '\\' '=',
    //\\    '<' '>';

    case P_NOT_EQUALS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '#', A[ap+0]);
        T[0] = mktuple(AST_NOT_EQUALS, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '\\', A[ap+0]);
        T[2] = lit(AST__LIT, '=', A[ap+1]);
        T[0] = mktuple(AST_NOT_EQUALS, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '<', A[ap+0]);
        T[2] = lit(AST__LIT, '>', A[ap+1]);
        T[0] = mktuple(AST_NOT_EQUALS, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<NOT-EQUALS-ADDRESS> =
    //\\    '#' '#',
    //\\    '\\' '=' '=';

    case P_NOT_EQUALS_ADDRESS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '#', A[ap+0]);
        T[2] = lit(AST__LIT, '#', A[ap+1]);
        T[0] = mktuple(AST_NOT_EQUALS_ADDRESS, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '\\', A[ap+0]);
        T[2] = lit(AST__LIT, '=', A[ap+1]);
        T[3] = lit(AST__LIT, '=', A[ap+2]);
        T[0] = mktuple(AST_NOT_EQUALS_ADDRESS, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      }
      return T[0];

    //\\ # Procedure application, i.e. actual parameters to routine or function call

    //\\ P<OPT-ACTUAL-PARAMETERS> =
    //\\    '(' <EXPR> <REST-OF-ACTUAL-PARAMETERS> ')',
    //\\   ;

    case P_OPT_ACTUAL_PARAMETERS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-ACTUAL-PARAMETERS>
        T[4] = lit(AST__LIT, ')', A[ap+3]);
        T[0] = mktuple(AST_OPT_ACTUAL_PARAMETERS, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_OPT_ACTUAL_PARAMETERS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-ACTUAL-PARAMETERS> =
    //\\    ',' <EXPR> <REST-OF-ACTUAL-PARAMETERS>,
    //\\   ;

    case P_REST_OF_ACTUAL_PARAMETERS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-ACTUAL-PARAMETERS>
        T[0] = mktuple(AST_REST_OF_ACTUAL_PARAMETERS, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_ACTUAL_PARAMETERS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # handling precedence withh the grammar rather than Edinburgh-style in the AST. Tradeoff of speed for simplicity.
    //\\ # CEXPR not handled yet

    //\\ P<EXPR> =
    //\\    <MEDIUMPREC-EXPR> <REST-OF-EXPR>;

    case P_EXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <MEDIUMPREC-EXPR>
      T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-EXPR>
      return mktuple(AST_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<REST-OF-EXPR> =
    //\\    <OP-LOW> <MEDIUMPREC-EXPR> <REST-OF-EXPR>,
    //\\   ;

    case P_REST_OF_EXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-LOW>
        T[2] = build_ast(A[ap+1], depth+1); // <MEDIUMPREC-EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-EXPR>
        T[0] = mktuple(AST_REST_OF_EXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_EXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<MEDIUMPREC-EXPR> =
    //\\    <HIGHPREC-EXPR> <REST-OF-MEDIUMPREC-EXPR>;

    case P_MEDIUMPREC_EXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <HIGHPREC-EXPR>
      T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-MEDIUMPREC-EXPR>
      return mktuple(AST_MEDIUMPREC_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<REST-OF-MEDIUMPREC-EXPR> =
    //\\    <OP-MED> <HIGHPREC-EXPR> <REST-OF-MEDIUMPREC-EXPR>,
    //\\   ;

    case P_REST_OF_MEDIUMPREC_EXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-MED>
        T[2] = build_ast(A[ap+1], depth+1); // <HIGHPREC-EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-MEDIUMPREC-EXPR>
        T[0] = mktuple(AST_REST_OF_MEDIUMPREC_EXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_MEDIUMPREC_EXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<HIGHPREC-EXPR> =
    //\\    <RIGHT-ASSOC-EXPR> <REST-OF-HIGHPREC-EXPR>;

    case P_HIGHPREC_EXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <RIGHT-ASSOC-EXPR>
      T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-HIGHPREC-EXPR>
      return mktuple(AST_HIGHPREC_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<REST-OF-HIGHPREC-EXPR> =
    //\\    <OP-HIGH-LEFTASSOC> <RIGHT-ASSOC-EXPR> <REST-OF-HIGHPREC-EXPR>,
    //\\   ;

    case P_REST_OF_HIGHPREC_EXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-HIGH-LEFTASSOC>
        T[2] = build_ast(A[ap+1], depth+1); // <RIGHT-ASSOC-EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-HIGHPREC-EXPR>
        T[0] = mktuple(AST_REST_OF_HIGHPREC_EXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_HIGHPREC_EXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<RIGHT-ASSOC-EXPR> =
    //\\    <REST-OF-RIGHTASSOC-EXPR> <UNARY-EXPR>;

    case P_RIGHT_ASSOC_EXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <REST-OF-RIGHTASSOC-EXPR>
      T[2] = build_ast(A[ap+1], depth+1); // <UNARY-EXPR>
      return mktuple(AST_RIGHT_ASSOC_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<REST-OF-RIGHTASSOC-EXPR> =
    //\\    <RIGHTASSOC-EXPR-with-OP> <REST-OF-RIGHTASSOC-EXPR>,
    //\\   ;

    case P_REST_OF_RIGHTASSOC_EXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <RIGHTASSOC-EXPR-with-OP>
        T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-RIGHTASSOC-EXPR>
        T[0] = mktuple(AST_REST_OF_RIGHTASSOC_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_RIGHTASSOC_EXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<RIGHTASSOC-EXPR-with-OP> =
    //\\    <UNARY-EXPR> <OP-HIGH-RIGHTASSOC>;

    case P_RIGHTASSOC_EXPR_with_OP:
      T[1] = build_ast(A[ap+0], depth+1); // <UNARY-EXPR>
      T[2] = build_ast(A[ap+1], depth+1); // <OP-HIGH-RIGHTASSOC>
      return mktuple(AST_RIGHTASSOC_EXPR_with_OP, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ P<UNARY-EXPR> =
    //\\    <Opt-UNARY-OP> <OPERAND>;

    case P_UNARY_EXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <Opt-UNARY-OP>
      T[2] = build_ast(A[ap+1], depth+1); // <OPERAND>
      return mktuple(AST_UNARY_EXPR, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);

    //\\ # These pointer arithmetic elements are for Hamish's PDP9/15 Imp (old): INDP == INDEX0++P
    //\\ # and for Hamish's 68000 Imp:     %if tp[1]_mode # litmode %then lo = minint %else lo = tp[1]_val
    //\\ # I hope this grammar definition causes the right precedence level...
    //\\ #
    //\\ # They can also be used in LVALUEs.

    //\\ P<Opt-PARAMETERS-OR-POINTERINDEX> =
    //\\    '[' <EXPR> ']',
    //\\    '+' '+' <EXPR>,
    //\\    '-' '-' <EXPR>,
    //\\    <OPT-ACTUAL-PARAMETERS>;

    case P_Opt_PARAMETERS_OR_POINTERINDEX:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '[', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = lit(AST__LIT, ']', A[ap+2]);
        T[0] = mktuple(AST_Opt_PARAMETERS_OR_POINTERINDEX, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '+', A[ap+0]);
        T[2] = lit(AST__LIT, '+', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <EXPR>
        T[0] = mktuple(AST_Opt_PARAMETERS_OR_POINTERINDEX, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[2] = lit(AST__LIT, '-', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <EXPR>
        T[0] = mktuple(AST_Opt_PARAMETERS_OR_POINTERINDEX, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-ACTUAL-PARAMETERS>
        T[0] = mktuple(AST_Opt_PARAMETERS_OR_POINTERINDEX, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<Opt-RECORDFIELD> =
    //\\    '_' <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD>,
    //\\   ;

    case P_Opt_RECORDFIELD:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '_', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-PARAMETERS-OR-POINTERINDEX>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-RECORDFIELD>
        T[0] = mktuple(AST_Opt_RECORDFIELD, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_Opt_RECORDFIELD, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-RECORDFIELD-OR-POINTERINDEX> =
    //\\    '_' <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>,
    //\\   ;

    case P_Opt_RECORDFIELD_OR_POINTERINDEX:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '_', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-PARAMETERS-OR-POINTERINDEX>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-RECORDFIELD-OR-POINTERINDEX>
        T[0] = mktuple(AST_Opt_RECORDFIELD_OR_POINTERINDEX, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_Opt_RECORDFIELD_OR_POINTERINDEX, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # The old form of modulus (!X!) is for really ancient compilers such as Hamish's Pdp15 Imp.
    //\\ # Unfortunately there are obscure cases where an ambiguous parse results:
    //\\ #
    //\\ #  putact(condop!!polarity!!1+polarity<<7,item,0)
    //\\ #
    //\\ # Does that mean
    //\\ #   putact(condop !!  polarity   !!  1 + polarity<<7, item, 0)
    //\\ # or
    //\\ #   putact(condop !  !polarity!  !   1 + polarity<<7, item, 0)
    //\\ #
    //\\ # I need to check but I *hope* that the grammar here causes the first of the above to be selected.
    //\\ # If not, we'll need a command-line option here to force the version of Imp being used.
    //\\ #
    //\\ # I just hope that there's no version of Imp that ever supported factorials with a postfix '!'
    //\\ #

    //\\ P<OPERAND> =
    //\\    <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>,
    //\\    <CONST>,
    //\\    '(' <EXPR> ')',
    //\\    '|' <EXPR> '|',
    //\\    '!' <EXPR> '!';

    case P_OPERAND:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-PARAMETERS-OR-POINTERINDEX>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-RECORDFIELD-OR-POINTERINDEX>
        T[0] = mktuple(AST_OPERAND, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <CONST>
        T[0] = mktuple(AST_OPERAND, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_OPERAND, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 3) {
        T[1] = lit(AST__LIT, '|', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = lit(AST__LIT, '|', A[ap+2]);
        T[0] = mktuple(AST_OPERAND, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = lit(AST__LIT, '!', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = lit(AST__LIT, '!', A[ap+2]);
        T[0] = mktuple(AST_OPERAND, alt /*4*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      }
      return T[0];

    //\\ # <OP> is only used in <CEXPR>s now and will be removed entirely
    //\\ # once CEXPRs are recoded to use the same grammar as EXPRs.
    //\\ #
    //\\ # *However* I am considering doing away with CEXPRs altogether,
    //\\ # and just using <EXPR> everywhere, in conjunction with a parse-time
    //\\ # test to check whether any of the terms were in fact *not* constants.
    //\\ #
    //\\ # Also... it's not obvious if we could in fact use EXPRs everywhere *without*
    //\\ # the overhead of having to organise the code/name tables so that they
    //\\ # can be checked for being constants at parse time, and merely reject
    //\\ # them from the compile() procedure instead?  This would be possible
    //\\ # as long as there are no parse ambiguities caused by checking after
    //\\ # parsing. I know that's a problem in C but Imp's simpler syntax
    //\\ # might let us get away with it!
    //\\ #
    //\\ # Note that none of these options eliminates the possibility of implementing
    //\\ # conditional compilation the EMAS Imp80 way using
    //\\ #
    //\\ # %if <CEXPR> %start
    //\\ #   ! conditional code
    //\\ # %finish
    //\\ #
    //\\ # (Though I've always had questions about that on EMAS if there's a
    //\\ #  label inside the block - the ability to use that construct for
    //\\ #  conditional compilation depends on whether that label is ever
    //\\ #  jumped to from code outside the block! - doing it properly requires
    //\\ #  full flow control analysis and a transitive closure on the flow graph!)
    //\\ #

    //\\ P<OP> =
    //\\    <OP-HIGH-LEFTASSOC>,
    //\\    <OP-HIGH-RIGHTASSOC>,
    //\\    <OP-MED>,
    //\\    <OP-LOW>;

    case P_OP:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-HIGH-LEFTASSOC>
        T[0] = mktuple(AST_OP, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-HIGH-RIGHTASSOC>
        T[0] = mktuple(AST_OP, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-MED>
        T[0] = mktuple(AST_OP, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <OP-LOW>
        T[0] = mktuple(AST_OP, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # highest precedence, left-associative

    //\\ P<OP-HIGH-LEFTASSOC> =
    //\\    '<' '<',
    //\\    '>' '>';

    case P_OP_HIGH_LEFTASSOC:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '<', A[ap+0]);
        T[2] = lit(AST__LIT, '<', A[ap+1]);
        T[0] = mktuple(AST_OP_HIGH_LEFTASSOC, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '>', A[ap+0]);
        T[2] = lit(AST__LIT, '>', A[ap+1]);
        T[0] = mktuple(AST_OP_HIGH_LEFTASSOC, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # highest precedence, right-associative (only exponentiation)
    //\\ #
    //\\ # I found this expression the 68000 Imp compiler:
    //\\ #
    //\\ #           y = |r|+0.5/10.0\{^}m;  !modulus, rounded
    //\\ #
    //\\ # which I parse as:
    //\\ #
    //\\ #           y = |r|+( 0.5 / (10.0\{^}m));  !modulus, rounded
    //\\ #
    //\\ # I presume this is a clever way to round the last digit of a decimal,
    //\\ # but damn, how it works is beyond me.
    //\\ # Typical Hamish code! Brilliant (I presume) but obscure and uncommented.
    //\\ #

    //\\ P<OP-HIGH-RIGHTASSOC> =
    //\\    <INTEGER-EXP>,
    //\\    <REAL-EXP>;

    case P_OP_HIGH_RIGHTASSOC:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <INTEGER-EXP>
        T[0] = mktuple(AST_OP_HIGH_RIGHTASSOC, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <REAL-EXP>
        T[0] = mktuple(AST_OP_HIGH_RIGHTASSOC, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<INTEGER-EXP> =
    //\\    '*' '*' '*' '*',
    //\\    '\\' '\\',
    //\\    '^' '^';

    case P_INTEGER_EXP:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[2] = lit(AST__LIT, '*', A[ap+1]);
        T[3] = lit(AST__LIT, '*', A[ap+2]);
        T[4] = lit(AST__LIT, '*', A[ap+3]);
        T[0] = mktuple(AST_INTEGER_EXP, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '\\', A[ap+0]);
        T[2] = lit(AST__LIT, '\\', A[ap+1]);
        T[0] = mktuple(AST_INTEGER_EXP, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '^', A[ap+0]);
        T[2] = lit(AST__LIT, '^', A[ap+1]);
        T[0] = mktuple(AST_INTEGER_EXP, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<REAL-EXP> =
    //\\    '*' '*',
    //\\    '\\',
    //\\    '^';

    case P_REAL_EXP:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[2] = lit(AST__LIT, '*', A[ap+1]);
        T[0] = mktuple(AST_REAL_EXP, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '\\', A[ap+0]);
        T[0] = mktuple(AST_REAL_EXP, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '^', A[ap+0]);
        T[0] = mktuple(AST_REAL_EXP, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # middle-precedence operators.  All left-associative.

    //\\ P<OP-MED> =
    //\\    '*',
    //\\    '/' '/',
    //\\    '/',
    //\\    '&';

    case P_OP_MED:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[0] = mktuple(AST_OP_MED, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '/', A[ap+0]);
        T[2] = lit(AST__LIT, '/', A[ap+1]);
        T[0] = mktuple(AST_OP_MED, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '/', A[ap+0]);
        T[0] = mktuple(AST_OP_MED, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '&', A[ap+0]);
        T[0] = mktuple(AST_OP_MED, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # lowest precedence operators.  All left-associative.

    //\\ P<OP-LOW> =
    //\\    '+',
    //\\    '-',
    //\\    '!' '!',
    //\\    '!',
    //\\    '.';

    case P_OP_LOW:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '+', A[ap+0]);
        T[0] = mktuple(AST_OP_LOW, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[0] = mktuple(AST_OP_LOW, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '!', A[ap+0]);
        T[2] = lit(AST__LIT, '!', A[ap+1]);
        T[0] = mktuple(AST_OP_LOW, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 3) {
        T[1] = lit(AST__LIT, '!', A[ap+0]);
        T[0] = mktuple(AST_OP_LOW, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '.', A[ap+0]);
        T[0] = mktuple(AST_OP_LOW, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # You would think that unary operators always have the highest precedence,
    //\\ # but not so - here's a tricky one...
    //\\ #
    //\\ # I = -1 >> 1
    //\\ #
    //\\ # This always has the value 0, regardless of integer size, because a unary minus
    //\\ # in front of a constant is *NOT* interpreted as a negative constant, but rather
    //\\ # the positive constant preceded by a unary negation operator.
    //\\ #
    //\\ # It gets worse... unary negation is implemented as binary subtraction with
    //\\ # an inserted '0' left operand, i.e.
    //\\ #
    //\\ # 0 - 1 >> 1
    //\\ #
    //\\ # Shifts have higher precedence than subtraction, so this becomes:
    //\\ #
    //\\ # 0 - (1 >> 1)
    //\\ #
    //\\ # which evaluates to 0.
    //\\ #
    //\\ # The 68000 Imp compiler includes this statement which I hope is correct! dict(firstpos)_val = -cad>>1
    //\\ #
    //\\ # Some versions of Imp (I think Imp80) forbid a unary '+' before constants,
    //\\ # so A + +3 would be illegal (although A + -3 would not)
    //\\ #
    //\\ # and none of this applies to binary-not (\) so \3 really is higher
    //\\ # priority than '>>' and \3 >> 1 does what you would expect.  I hope.
    //\\ #

    //\\ P<Opt-UNARY-OP> =
    //\\    '+',
    //\\    '-',
    //\\    <UNARY-NOT>,
    //\\   ;

    case P_Opt_UNARY_OP:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '+', A[ap+0]);
        T[0] = mktuple(AST_Opt_UNARY_OP, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '-', A[ap+0]);
        T[0] = mktuple(AST_Opt_UNARY_OP, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <UNARY-NOT>
        T[0] = mktuple(AST_Opt_UNARY_OP, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_Opt_UNARY_OP, alt /*3*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # fortunately unary backslash is unambiguous with ERCC style exponentiation binary operator

    //\\ P<UNARY-NOT> =
    //\\    '\\',
    //\\    '~';

    case P_UNARY_NOT:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '\\', A[ap+0]);
        T[0] = mktuple(AST_UNARY_NOT, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '~', A[ap+0]);
        T[0] = mktuple(AST_UNARY_NOT, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<Percent-IU> =
    //\\    "if",
    //\\    "unless";

    case P_Percent_IU:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_if, A[ap+0]);
        T[0] = mktuple(AST_Percent_IU, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_unless, A[ap+0]);
        T[0] = mktuple(AST_Percent_IU, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # rest of %switch list
    //\\ # This reminds me, isn't there an optional %label declaration in Imp?  Haven't seen an example yet,
    //\\ # I need to check the manuals to make sure I'm not confusing this with C...

    //\\ P<REST-OF-SWLIST> =
    //\\    ',' <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST>,
    //\\   ;

    case P_REST_OF_SWLIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-NAME-LIST>
        T[4] = lit(AST__LIT, '(', A[ap+3]);
        T[5] = build_ast(A[ap+4], depth+1); // <EXPR>
        T[6] = lit(AST__LIT, ':', A[ap+5]);
        T[7] = build_ast(A[ap+6], depth+1); // <EXPR>
        T[8] = lit(AST__LIT, ')', A[ap+7]);
        T[9] = build_ast(A[ap+8], depth+1); // <REST-OF-SWLIST>
        T[0] = mktuple(AST_REST_OF_SWLIST, alt /*0*/, /*phrases*/ 9, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_SWLIST, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-NAME-LIST> =
    //\\    ',' <NAME> <Opt-NAME-LIST>,
    //\\   ;

    case P_Opt_NAME_LIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-NAME-LIST>
        T[0] = mktuple(AST_Opt_NAME_LIST, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_Opt_NAME_LIST, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-NAME-LIST> =
    //\\    ',' <CEXPR> <REST-OF-NAME-LIST>,
    //\\   ;

    case P_REST_OF_NAME_LIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CEXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-NAME-LIST>
        T[0] = mktuple(AST_REST_OF_NAME_LIST, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_NAME_LIST, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # Still to handle operator precedence etc for <CEXPR>s the same way as for <EXPR>s.
    //\\ # However it is such a major duplication of effort that it might be much easier to
    //\\ # just use <EXPR>s and have a semantic test for constantcy.
    //\\ # The problem is the ambiguity of <NAMES>.  And you don't want any of the decorated
    //\\ # forms of a name (records, function calls, pointers, maybe even rejecting const
    //\\ # array elements?) - just simple literals and const integer operands.

    //\\ P<CEXPR> =
    //\\    <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR>;

    case P_CEXPR:
      T[1] = build_ast(A[ap+0], depth+1); // <Opt-UNARY-OP>
      T[2] = build_ast(A[ap+1], depth+1); // <COPERAND>
      T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-CEXPR>
      return mktuple(AST_CEXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ P<REST-OF-CEXPR> =
    //\\    <OP> <COPERAND> <REST-OF-CEXPR>,
    //\\   ;

    case P_REST_OF_CEXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OP>
        T[2] = build_ast(A[ap+1], depth+1); // <COPERAND>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-CEXPR>
        T[0] = mktuple(AST_REST_OF_CEXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_CEXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<COPERAND> =
    //\\    <NAME>,
    //\\    <CONST>,
    //\\    '(' <CEXPR> ')',
    //\\    '|' <CEXPR> '|',
    //\\    '!' <CEXPR> '!';

    case P_COPERAND:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[0] = mktuple(AST_COPERAND, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <CONST>
        T[0] = mktuple(AST_COPERAND, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CEXPR>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_COPERAND, alt /*2*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 3) {
        T[1] = lit(AST__LIT, '|', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CEXPR>
        T[3] = lit(AST__LIT, '|', A[ap+2]);
        T[0] = mktuple(AST_COPERAND, alt /*3*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = lit(AST__LIT, '!', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CEXPR>
        T[3] = lit(AST__LIT, '!', A[ap+2]);
        T[0] = mktuple(AST_COPERAND, alt /*4*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      }
      return T[0];

    //\\ P<OPT-event> =
    //\\    "event",
    //\\   ;

    case P_OPT_event:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_event, A[ap+0]);
        T[0] = mktuple(AST_OPT_event, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_event, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<FPP> =
    //\\    '(' <FORMAL-PARAMETER-DECLARATION> <REST-OF-FORMAL-PARAMETER-LIST> ')',
    //\\   ;

    case P_FPP:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <FORMAL-PARAMETER-DECLARATION>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-FORMAL-PARAMETER-LIST>
        T[4] = lit(AST__LIT, ')', A[ap+3]);
        T[0] = mktuple(AST_FPP, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_FPP, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-FORMAL-PARAMETER-LIST> =
    //\\    <Opt-Comma> <FORMAL-PARAMETER-DECLARATION> <REST-OF-FORMAL-PARAMETER-LIST>,
    //\\   ;

    case P_REST_OF_FORMAL_PARAMETER_LIST:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <Opt-Comma>
        T[2] = build_ast(A[ap+1], depth+1); // <FORMAL-PARAMETER-DECLARATION>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-FORMAL-PARAMETER-LIST>
        T[0] = mktuple(AST_REST_OF_FORMAL_PARAMETER_LIST, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_FORMAL_PARAMETER_LIST, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # %routine y (%integer %name %array (1) %name b)
    //\\ # %routine r10(%integer%array%name a(1:10),%integer k)

    //\\ P<FORMAL-PARAMETER-DECLARATION> =
    //\\    <XTYPE> <OPT-arrayname> <NAME> <OPT-68K-Bounds> <Opt-NAME-LIST>,
    //\\    <RT> <OPT-name> <NAME> <Opt-NAME-LIST> <FPP>,
    //\\    "name" <Opt-68K-reg> <NAME> <Opt-NAME-LIST>;

    case P_FORMAL_PARAMETER_DECLARATION:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <XTYPE>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-arrayname>
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = build_ast(A[ap+3], depth+1); // <OPT-68K-Bounds>
        T[5] = build_ast(A[ap+4], depth+1); // <Opt-NAME-LIST>
        T[0] = mktuple(AST_FORMAL_PARAMETER_DECLARATION, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <RT>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-name>
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-NAME-LIST>
        T[5] = build_ast(A[ap+4], depth+1); // <FPP>
        T[0] = mktuple(AST_FORMAL_PARAMETER_DECLARATION, alt /*1*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else               {
        T[1] = kw(AST__KW, KW_name, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-68K-reg>
        T[3] = build_ast(A[ap+2], depth+1); // <NAME>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-NAME-LIST>
        T[0] = mktuple(AST_FORMAL_PARAMETER_DECLARATION, alt /*2*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      }
      return T[0];

    //\\ # More Hamish idiosyncracy!:
    //\\ #
    //\\ # @16_1114%record(*)%map ZNEW(%name(d0) v)
    //\\ #  - APM-gdmr/I/VPERM19.imp
    //\\ #      d0 being a register
    //\\ #

    //\\ P<Opt-68K-reg> =
    //\\    '(' <NAME> ')',
    //\\   ;

    case P_Opt_68K_reg:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_Opt_68K_reg, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_Opt_68K_reg, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-name> =
    //\\    "name",
    //\\   ;

    case P_OPT_name:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_name, A[ap+0]);
        T[0] = mktuple(AST_OPT_name, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_name, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # added predicate - gt

    //\\ P<RT> =
    //\\    "routine",
    //\\    "predicate",
    //\\    <XTYPE> <FN-MAP>;

    case P_RT:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_routine, A[ap+0]);
        T[0] = mktuple(AST_RT, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_predicate, A[ap+0]);
        T[0] = mktuple(AST_RT, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <XTYPE>
        T[2] = build_ast(A[ap+1], depth+1); // <FN-MAP>
        T[0] = mktuple(AST_RT, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # note map is equivalent to "name function"

    //\\ P<FN-MAP> =
    //\\    <FN>,
    //\\    "map";

    case P_FN_MAP:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <FN>
        T[0] = mktuple(AST_FN_MAP, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_map, A[ap+0]);
        T[0] = mktuple(AST_FN_MAP, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ P<FN> =
    //\\    "fn",
    //\\    "function";

    case P_FN:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_fn, A[ap+0]);
        T[0] = mktuple(AST_FN, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_function, A[ap+0]);
        T[0] = mktuple(AST_FN, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # Confirmed that label is used.  Found one in 68000 code for labels used in embeddded machine code:
    //\\ # %label f1,f2,f3,f4,f5,f6,f7,b0,b1,b2,b3,b4,b5,b6,b7,end
    //\\ #
    //\\ # "long long" would be added here if we supported 64 bit ints or 128bit reals...
    //\\ # Okain "long" added here by making <BTYPE> optional.
    //\\ # (BTYPE being integer/real/longreal)
    //\\ #

    //\\ P<BASE-XTYPE> =
    //\\    "integer",
    //\\    "real",
    //\\    "label",
    //\\    "long" <Opt-BTYPE>,
    //\\    "byte" <Opt-INTEGER>,
    //\\    "mite" <Opt-INTEGER>,
    //\\    "short" <Opt-INTEGER>,
    //\\    "half" <Opt-INTEGER>,
    //\\    "string" <REPFACT>,
    //\\    "record" <Opt-RFREF>;

    case P_BASE_XTYPE:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_integer, A[ap+0]);
        T[0] = mktuple(AST_BASE_XTYPE, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_real, A[ap+0]);
        T[0] = mktuple(AST_BASE_XTYPE, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_label, A[ap+0]);
        T[0] = mktuple(AST_BASE_XTYPE, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 3) {
        T[1] = kw(AST__KW, KW_long, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-BTYPE>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*3*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 4) {
        T[1] = kw(AST__KW, KW_byte, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-INTEGER>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*4*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 5) {
        T[1] = kw(AST__KW, KW_mite, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-INTEGER>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*5*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 6) {
        T[1] = kw(AST__KW, KW_short, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-INTEGER>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*6*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 7) {
        T[1] = kw(AST__KW, KW_half, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-INTEGER>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*7*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 8) {
        T[1] = kw(AST__KW, KW_string, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <REPFACT>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*8*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = kw(AST__KW, KW_record, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-RFREF>
        T[0] = mktuple(AST_BASE_XTYPE, alt /*9*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # does %record(i tc f)%name match <XTYPE>?
    //\\ #
    //\\ # Courtesy of Hamish, two more language extensions:
    //\\ #
    //\\ # Two <OPT-68K-qualifier>s to allow one or both of %writeonly %volatile %byte %integer device
    //\\ #

    //\\ P<XTYPE> =
    //\\    <OPT-68K-qualifier> <BASE-XTYPE> <OPT-name>;

    case P_XTYPE:
      T[1] = build_ast(A[ap+0], depth+1); // <OPT-68K-qualifier>
      T[2] = build_ast(A[ap+1], depth+1); // <BASE-XTYPE>
      T[3] = build_ast(A[ap+2], depth+1); // <OPT-name>
      return mktuple(AST_XTYPE, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ P<68K-memptr> =
    //\\    "read" "only" <68K-memptr>,
    //\\    "write" "only" <68K-memptr>,
    //\\    "volatile" <68K-memptr>,
    //\\   ;

    case P_68K_memptr:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_read, A[ap+0]);
        T[2] = kw(AST__KW, KW_only, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <68K-memptr>
        T[0] = mktuple(AST_68K_memptr, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_write, A[ap+0]);
        T[2] = kw(AST__KW, KW_only, A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <68K-memptr>
        T[0] = mktuple(AST_68K_memptr, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_volatile, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <68K-memptr>
        T[0] = mktuple(AST_68K_memptr, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_68K_memptr, alt /*3*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-68K-qualifier> =
    //\\    <68K-register>,
    //\\    <68K-memptr>,
    //\\   ;

    case P_OPT_68K_qualifier:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <68K-register>
        T[0] = mktuple(AST_OPT_68K_qualifier, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <68K-memptr>
        T[0] = mktuple(AST_OPT_68K_qualifier, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_68K_qualifier, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # C-like register qualifiers (Hamish, again)
    //\\ # %register(a2)%integer r1,r2
    //\\ # %register(a2)%byte%name bn
    //\\ # %register(a3)%short%name sn

    //\\ P<68K-register> =
    //\\    "register" '(' <NAME> ')';

    case P_68K_register:
      T[1] = kw(AST__KW, KW_register, A[ap+0]);
      T[2] = lit(AST__LIT, '(', A[ap+1]);
      T[3] = build_ast(A[ap+2], depth+1); // <NAME>
      T[4] = lit(AST__LIT, ')', A[ap+3]);
      return mktuple(AST_68K_register, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);

    //\\ # '*' added by gt for recordformat(*)
    //\\ # () moved from point of call to here to support old style "%recordname fred" without format. (Uses a formatspec instead)

    //\\ P<Opt-RFREF> =
    //\\    '(' <RECFMT-REF> ')',
    //\\    '(' '*' ')',
    //\\   ;

    case P_Opt_RFREF:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <RECFMT-REF>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_Opt_RFREF, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = lit(AST__LIT, '*', A[ap+1]);
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_Opt_RFREF, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_Opt_RFREF, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # RF in the rules below is short for Record Format

    //\\ P<RECFMT-REF> =
    //\\    <NAME>,
    //\\    <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC>;

    case P_RECFMT_REF:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[0] = mktuple(AST_RECFMT_REF, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <RECFMT-DEC>
        T[2] = build_ast(A[ap+1], depth+1); // <REST-OF-RECFMT-DEC>
        T[3] = build_ast(A[ap+2], depth+1); // <ALT-RECFMT-DEC>
        T[0] = mktuple(AST_RECFMT_REF, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      }
      return T[0];

    //\\ # Imp variant record. NOT the same as C's which is per field.  This is per record.
    //\\ # Still to check: a ( ... %or ... ) subgroup in a record definition, where the ...
    //\\ # represents several declarations, with the whole ( ... ) group being a part
    //\\ # of an enclosing record.
    //\\ #

    //\\ P<ALT-RECFMT-DEC> =
    //\\    "or" <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC>,
    //\\   ;

    case P_ALT_RECFMT_DEC:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_or, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <RECFMT-DEC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-RECFMT-DEC>
        T[4] = build_ast(A[ap+3], depth+1); // <ALT-RECFMT-DEC>
        T[0] = mktuple(AST_ALT_RECFMT_DEC, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_ALT_RECFMT_DEC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-RECFMT-DEC> =
    //\\    ',' <RECFMT-DEC> <REST-OF-RECFMT-DEC>,
    //\\   ;

    case P_REST_OF_RECFMT_DEC:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <RECFMT-DEC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-RECFMT-DEC>
        T[0] = mktuple(AST_REST_OF_RECFMT_DEC, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_RECFMT_DEC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<RECFMT-DEC> =
    //\\    <XTYPE> <RECFMT-ELMNT>,
    //\\    '(' <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC> ')';

    case P_RECFMT_DEC:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <XTYPE>
        T[2] = build_ast(A[ap+1], depth+1); // <RECFMT-ELMNT>
        T[0] = mktuple(AST_RECFMT_DEC, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <RECFMT-DEC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-RECFMT-DEC>
        T[4] = build_ast(A[ap+3], depth+1); // <ALT-RECFMT-DEC>
        T[5] = lit(AST__LIT, ')', A[ap+4]);
        T[0] = mktuple(AST_RECFMT_DEC, alt /*1*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      }
      return T[0];

    //\\ # Now "name" has been removed from <OPT-arrayname-or-name>, check at the point
    //\\ # of invocation, that "name" still works, now it as been added to <XTYPE>...
    //\\ #

    //\\ P<RECFMT-ELMNT> =
    //\\    <OPT-arrayname> <NAME> <Opt-NAME-LIST>,
    //\\    "array" <ADECLN-in-record>;

    case P_RECFMT_ELMNT:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-arrayname>
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-NAME-LIST>
        T[0] = mktuple(AST_RECFMT_ELMNT, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <ADECLN-in-record>
        T[0] = mktuple(AST_RECFMT_ELMNT, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # Array declaration

    //\\ P<ADECLN-in-record> =
    //\\    <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST-in-record>;

    case P_ADECLN_in_record:
      T[1] = build_ast(A[ap+0], depth+1); // <NAME>
      T[2] = build_ast(A[ap+1], depth+1); // <Opt-NAME-LIST>
      T[3] = build_ast(A[ap+2], depth+1); // <BPAIR>
      T[4] = build_ast(A[ap+3], depth+1); // <REST-OF-ARLIST-in-record>
      return mktuple(AST_ADECLN_in_record, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);

    //\\ P<REST-OF-ARLIST-in-record> =
    //\\    ',' <ADECLN-in-record>,
    //\\   ;

    case P_REST_OF_ARLIST_in_record:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <ADECLN-in-record>
        T[0] = mktuple(AST_REST_OF_ARLIST_in_record, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_ARLIST_in_record, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<LOWERBOUND> =
    //\\    <EXPR>,
    //\\    <BASE-XTYPE> <NAME>;

    case P_LOWERBOUND:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <EXPR>
        T[0] = mktuple(AST_LOWERBOUND, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <BASE-XTYPE>
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[0] = mktuple(AST_LOWERBOUND, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # Another Hamishism... %integer %array %name fred(1:*)
    //\\ # which in Imp77 would be %integer %array (1) fred
    //\\ # (reject the '*' in other contexts)
    //\\ #

    //\\ P<UPPERBOUND> =
    //\\    '*',
    //\\    <EXPR>,
    //\\    <BASE-XTYPE> <NAME>;

    case P_UPPERBOUND:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[0] = mktuple(AST_UPPERBOUND, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <EXPR>
        T[0] = mktuple(AST_UPPERBOUND, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <BASE-XTYPE>
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[0] = mktuple(AST_UPPERBOUND, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ # List of lower/upper bound pairs, for array declarations.

    //\\ P<BPAIR> =
    //\\    '(' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST> ')';

    case P_BPAIR:
      T[1] = lit(AST__LIT, '(', A[ap+0]);
      T[2] = build_ast(A[ap+1], depth+1); // <LOWERBOUND>
      T[3] = lit(AST__LIT, ':', A[ap+2]);
      T[4] = build_ast(A[ap+3], depth+1); // <UPPERBOUND>
      T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-BPLIST>
      T[6] = lit(AST__LIT, ')', A[ap+5]);
      return mktuple(AST_BPAIR, alt /*0*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);

    //\\ P<BPAIR68K> =
    //\\    '(' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST68K> ')';

    case P_BPAIR68K:
      T[1] = lit(AST__LIT, '(', A[ap+0]);
      T[2] = build_ast(A[ap+1], depth+1); // <LOWERBOUND>
      T[3] = lit(AST__LIT, ':', A[ap+2]);
      T[4] = build_ast(A[ap+3], depth+1); // <UPPERBOUND>
      T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-BPLIST68K>
      T[6] = lit(AST__LIT, ')', A[ap+5]);
      return mktuple(AST_BPAIR68K, alt /*0*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);

    //\\ # Multi-dimensional arrays

    //\\ P<REST-OF-BPLIST> =
    //\\    ',' <EXPR> ':' <EXPR> <REST-OF-BPLIST>,
    //\\   ;

    case P_REST_OF_BPLIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[3] = lit(AST__LIT, ':', A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <EXPR>
        T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-BPLIST>
        T[0] = mktuple(AST_REST_OF_BPLIST, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_BPLIST, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-BPLIST68K> =
    //\\    ',' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST68K>,
    //\\   ;

    case P_REST_OF_BPLIST68K:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <LOWERBOUND>
        T[3] = lit(AST__LIT, ':', A[ap+2]);
        T[4] = build_ast(A[ap+3], depth+1); // <UPPERBOUND>
        T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-BPLIST68K>
        T[0] = mktuple(AST_REST_OF_BPLIST68K, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_BPLIST68K, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # <Opt-DIMENSIONS> added to handle:
    //\\ #
    //\\ # %routine y (%integer %name %array (1) %name b)
    //\\ #
    //\\ # and <OPT-68K-Bounds> added to handle:
    //\\ #
    //\\ # %routine extract params(%string(255) %name in,out,
    //\\ #    %integer %array(1:max params) %name params)
    //\\ #

    //\\ P<OPT-arrayname> =
    //\\    "array" <Opt-DIMENSIONS> "name",
    //\\    "array" <OPT-68K-Bounds> "name",
    //\\   ;

    case P_OPT_arrayname:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-DIMENSIONS>
        T[3] = kw(AST__KW, KW_name, A[ap+2]);
        T[0] = mktuple(AST_OPT_arrayname, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-68K-Bounds>
        T[3] = kw(AST__KW, KW_name, A[ap+2]);
        T[0] = mktuple(AST_OPT_arrayname, alt /*1*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPT_arrayname, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # for the example from 68000 Imp,
    //\\ # %routine y (%integer %name %array (1) %name b)

    //\\ P<Opt-DIMENSIONS> =
    //\\    '(' <INT-CONST> ')',
    //\\   ;

    case P_Opt_DIMENSIONS:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <INT-CONST>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_Opt_DIMENSIONS, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_Opt_DIMENSIONS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-INTEGER> =
    //\\    "integer",
    //\\   ;

    case P_Opt_INTEGER:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_integer, A[ap+0]);
        T[0] = mktuple(AST_Opt_INTEGER, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_Opt_INTEGER, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # "42(2)" is equivalent to "42, 42".  (*) means 'as many more are needed to fill the array' so must come last.
    //\\ # However it looks like <REPFACT> is also used as the string length indicator?!

    //\\ P<REPFACT> =
    //\\    '(' <Opt-STAROREXPR> ')',
    //\\   ;

    case P_REPFACT:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '(', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <Opt-STAROREXPR>
        T[3] = lit(AST__LIT, ')', A[ap+2]);
        T[0] = mktuple(AST_REPFACT, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REPFACT, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-STAROREXPR> =
    //\\    <STAROREXPR>,
    //\\   ;

    case P_Opt_STAROREXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <STAROREXPR>
        T[0] = mktuple(AST_Opt_STAROREXPR, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_Opt_STAROREXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # Used in switch label.  (*) marks a catch-all. (like default: in C)
    //\\ # Or at least SHOULD be used in switch label.  Looks like it isn't... just used in <REPFACT>?

    //\\ P<STAROREXPR> =
    //\\    <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR>,
    //\\    '*';

    case P_STAROREXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <Opt-UNARY-OP>
        T[2] = build_ast(A[ap+1], depth+1); // <COPERAND>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-CEXPR>
        T[0] = mktuple(AST_STAROREXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[0] = mktuple(AST_STAROREXPR, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # Opt- added here to allow plain "%long" as a declaration at <XTYPE>

    //\\ P<Opt-BTYPE> =
    //\\    "real",
    //\\    "integer",
    //\\    "long" "real",
    //\\   ;

    case P_Opt_BTYPE:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_real, A[ap+0]);
        T[0] = mktuple(AST_Opt_BTYPE, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_integer, A[ap+0]);
        T[0] = mktuple(AST_Opt_BTYPE, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_long, A[ap+0]);
        T[2] = kw(AST__KW, KW_real, A[ap+1]);
        T[0] = mktuple(AST_Opt_BTYPE, alt /*2*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_Opt_BTYPE, alt /*3*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-Comma> =
    //\\    ',',
    //\\   ;

    case P_Opt_Comma:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[0] = mktuple(AST_Opt_Comma, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_Opt_Comma, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-ALIAS> =
    //\\    "alias" <TEXTTEXT>,
    //\\   ;

    case P_Opt_ALIAS:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_alias, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <TEXTTEXT>
        T[0] = mktuple(AST_Opt_ALIAS, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_Opt_ALIAS, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<TEXTTEXT> =
    //\\    <dqstring>;

    case P_TEXTTEXT:
      T[1] = bip(AST_dqstring, A[ap+0]);
      return mktuple(AST_TEXTTEXT, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);

    //\\ # if we add a "C" type (to reverse the order of parameter evaluation) it would be done here.
    //\\ # I don't think you would write %permroutine since they are built in to the compiler, but
    //\\ # depending on how the compiler is implemented, you might, and if so they would be added here.
    //\\ # I do have a vague recollection of perm routines being matched up with internal code using
    //\\ # a file or declarations containing magic numbers, eg something like:
    //\\ # %perm (5) %routine write(%integer n, sp)
    //\\ # - but that may be a manufactured memory!
    //\\ #

    //\\ P<Percent-SEX> =
    //\\    "system",
    //\\    "external",
    //\\    "dynamic",
    //\\    "prim",
    //\\   ;

    case P_Percent_SEX:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_system, A[ap+0]);
        T[0] = mktuple(AST_Percent_SEX, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_external, A[ap+0]);
        T[0] = mktuple(AST_Percent_SEX, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_dynamic, A[ap+0]);
        T[0] = mktuple(AST_Percent_SEX, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 3) {
        T[1] = kw(AST__KW, KW_prim, A[ap+0]);
        T[0] = mktuple(AST_Percent_SEX, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_Percent_SEX, alt /*4*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<RECFMT-spec-OR-BODY> =
    //\\    "spec" <NAME>,
    //\\    <NAME> '(' <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC> ')';

    case P_RECFMT_spec_OR_BODY:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_spec, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[0] = mktuple(AST_RECFMT_spec_OR_BODY, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = lit(AST__LIT, '(', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <RECFMT-DEC>
        T[4] = build_ast(A[ap+3], depth+1); // <REST-OF-RECFMT-DEC>
        T[5] = build_ast(A[ap+4], depth+1); // <ALT-RECFMT-DEC>
        T[6] = lit(AST__LIT, ')', A[ap+5]);
        T[0] = mktuple(AST_RECFMT_spec_OR_BODY, alt /*1*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      }
      return T[0];

    //\\ # 68000 Imp has a non-standard syntax for some array declarations, eg
    //\\ #
    //\\ # %conststring(1) %array(1:max params) parameters="B","W","L"
    //\\ # %string(255) %array(0:maxfiles) list
    //\\ #
    //\\ # The equivalent Imp77 syntax would be
    //\\ #
    //\\ # %conststring(1) %array parameters(1:max params)="B","W","L"
    //\\ # %string(255) %array list(0:maxfiles)
    //\\ #
    //\\ # Putting bounds after a %name is also from the 68000:
    //\\ #
    //\\ #    %integer%array%name an10(1:10)
    //\\ #
    //\\ # which is likely the same as
    //\\ #
    //\\ #    %integer%array(1)%name an10
    //\\ #
    //\\ #  in Imp77.  We just haven't been told the lower bound...
    //\\ #
    //\\ # meanwhile this broke: %string (64) %array %format sform1(0 : 1022)
    //\\ #
    //\\ # I'm not sure if there is any significant difference nowadays between own declarations and non-own declarations...
    //\\ # the used to be distinguished by allowing initialisations... maybe worth merging the definitions now?

    //\\ P<DECLN> =
    //\\    <OPT-arrayname> <OPT-spec> <NAME-or-STAR-68K> <OPT-68K-Bounds> <Opt-Init-assign> <Opt-Assign-NAME-LIST>,
    //\\    "array" <OPT-spec> <OPT-format> <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>,
    //\\    "array" <BPAIR> <OPT-spec> <OPT-format> <NAME> <Opt-NAME-LIST> <REST-OF-ARLIST-68K>;

    case P_DECLN:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-arrayname>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-spec>
        T[3] = build_ast(A[ap+2], depth+1); // <NAME-or-STAR-68K>
        T[4] = build_ast(A[ap+3], depth+1); // <OPT-68K-Bounds>
        T[5] = build_ast(A[ap+4], depth+1); // <Opt-Init-assign>
        T[6] = build_ast(A[ap+5], depth+1); // <Opt-Assign-NAME-LIST>
        T[0] = mktuple(AST_DECLN, alt /*0*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-spec>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-format>
        T[4] = build_ast(A[ap+3], depth+1); // <NAME>
        T[5] = build_ast(A[ap+4], depth+1); // <Opt-NAME-LIST>
        T[6] = build_ast(A[ap+5], depth+1); // <BPAIR>
        T[7] = build_ast(A[ap+6], depth+1); // <REST-OF-ARLIST>
        T[0] = mktuple(AST_DECLN, alt /*1*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else               {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <BPAIR>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-spec>
        T[4] = build_ast(A[ap+3], depth+1); // <OPT-format>
        T[5] = build_ast(A[ap+4], depth+1); // <NAME>
        T[6] = build_ast(A[ap+5], depth+1); // <Opt-NAME-LIST>
        T[7] = build_ast(A[ap+6], depth+1); // <REST-OF-ARLIST-68K>
        T[0] = mktuple(AST_DECLN, alt /*2*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      }
      return T[0];

    //\\ P<OWNDEC> =
    //\\    <OPT-arrayname> <OPT-spec> <SINGLE-OWNDEC> <REST-OF-OWNDEC>,
    //\\    "array" <OPT-format> <OPT-spec> <BPAIR> <NAME> <Opt-Init-assign-array>,
    //\\    "array" <OPT-format> <OPT-spec> <NAME> <BPAIR> <Opt-Init-assign-array>;

    case P_OWNDEC:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <OPT-arrayname>
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-spec>
        T[3] = build_ast(A[ap+2], depth+1); // <SINGLE-OWNDEC>
        T[4] = build_ast(A[ap+3], depth+1); // <REST-OF-OWNDEC>
        T[0] = mktuple(AST_OWNDEC, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-format>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-spec>
        T[4] = build_ast(A[ap+3], depth+1); // <BPAIR>
        T[5] = build_ast(A[ap+4], depth+1); // <NAME>
        T[6] = build_ast(A[ap+5], depth+1); // <Opt-Init-assign-array>
        T[0] = mktuple(AST_OWNDEC, alt /*1*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      } else               {
        T[1] = kw(AST__KW, KW_array, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-format>
        T[3] = build_ast(A[ap+2], depth+1); // <OPT-spec>
        T[4] = build_ast(A[ap+3], depth+1); // <NAME>
        T[5] = build_ast(A[ap+4], depth+1); // <BPAIR>
        T[6] = build_ast(A[ap+5], depth+1); // <Opt-Init-assign-array>
        T[0] = mktuple(AST_OWNDEC, alt /*2*/, /*phrases*/ 6, T /*T[1], T[2], T[3], T[4], T[5], T[6]*/);
      }
      return T[0];

    //\\ P<REST-OF-ARLIST> =
    //\\    ',' <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>,
    //\\   ;

    case P_REST_OF_ARLIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-NAME-LIST>
        T[4] = build_ast(A[ap+3], depth+1); // <BPAIR>
        T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-ARLIST>
        T[0] = mktuple(AST_REST_OF_ARLIST, alt /*0*/, /*phrases*/ 5, T /*T[1], T[2], T[3], T[4], T[5]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_ARLIST, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-ARLIST-68K> =
    //\\    ',' <NAME> <Opt-NAME-LIST> <REST-OF-ARLIST-68K>,
    //\\   ;

    case P_REST_OF_ARLIST_68K:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-NAME-LIST>
        T[4] = build_ast(A[ap+3], depth+1); // <REST-OF-ARLIST-68K>
        T[0] = mktuple(AST_REST_OF_ARLIST_68K, alt /*0*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_ARLIST_68K, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-68K-Bounds> =
    //\\    <BPAIR68K>,
    //\\   ;

    case P_OPT_68K_Bounds:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <BPAIR68K>
        T[0] = mktuple(AST_OPT_68K_Bounds, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_68K_Bounds, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-format> =
    //\\    "format",
    //\\   ;

    case P_OPT_format:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_format, A[ap+0]);
        T[0] = mktuple(AST_OPT_format, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_format, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ #
    //\\ # This is the same ugly hack as <Opt-Assign-NAME-LIST> below
    //\\ # The AST handling has to check for and reject the '*' in all contexts except for
    //\\ # 68000-style declarations such as "@16_4000c0 %byte *,acia s,*,acia d"
    //\\ #
    //\\ # Below is a note from inside the 68K compiler that may be relevant:
    //\\ #
    //\\ #! %record(*) or %string(*) needs %name (or @)
    //\\ #! %string alone allowed only for %const%string
    //\\ #

    //\\ P<NAME-or-STAR-68K> =
    //\\    '*',
    //\\    <NAME>;

    case P_NAME_or_STAR_68K:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '*', A[ap+0]);
        T[0] = mktuple(AST_NAME_or_STAR_68K, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[0] = mktuple(AST_NAME_or_STAR_68K, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # <Opt-Init-assign> added by gt
    //\\ # This hideous hack with '*' for an anonymous entry is yet another of Hamish's idiosyncrasies:
    //\\ #
    //\\ # @16_7fffc %byte status,data,*,control
    //\\ #                            ^
    //\\ #  "/home/gtoal/gtoal.com/athome/edinburgh/APM-gdmr/ETHER/SPY.imp"
    //\\ #
    //\\ # @16_4000c0 %byte *,acia s,*,acia d
    //\\ #
    //\\ # and would appear to just be a lazy way to skip over an address when assigning multiple
    //\\ # individual scalars to addresses...
    //\\ #
    //\\ # Obviously it should not be allowed in any other context - some semantic checking needed here...
    //\\ #

    //\\ P<Opt-Assign-NAME-LIST> =
    //\\    ',' '*' <Opt-Assign-NAME-LIST>,
    //\\    ',' <NAME> <Opt-Init-assign> <Opt-Assign-NAME-LIST>,
    //\\   ;

    case P_Opt_Assign_NAME_LIST:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = lit(AST__LIT, '*', A[ap+1]);
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-Assign-NAME-LIST>
        T[0] = mktuple(AST_Opt_Assign_NAME_LIST, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-Init-assign>
        T[4] = build_ast(A[ap+3], depth+1); // <Opt-Assign-NAME-LIST>
        T[0] = mktuple(AST_Opt_Assign_NAME_LIST, alt /*1*/, /*phrases*/ 4, T /*T[1], T[2], T[3], T[4]*/);
      } else               {
        T[0] = mktuple(AST_Opt_Assign_NAME_LIST, alt /*2*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # Need <EXPR> instead of <CEXPR> to catch = tostring(i) because cexpr only got tostring
    //\\ # Need '== <EXPR>' instead of '== <LVALUE>' to catch '== length(s)', i.e. a %map although it looks like a fn call.
    //\\ # - the latter being more of a failing of LVALUE that here.
    //\\ # Removed:
    //\\ #   '==' <LVALUE>,
    //\\ #   '=' <CEXPR>,
    //\\ # These will be improved once we have semantic checks...
    //\\ #

    //\\ P<Opt-Init-assign> =
    //\\    <ASSOP> <EXPR>,
    //\\   ;

    case P_Opt_Init_assign:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <ASSOP>
        T[2] = build_ast(A[ap+1], depth+1); // <EXPR>
        T[0] = mktuple(AST_Opt_Init_assign, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_Opt_Init_assign, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # removed string resolution from assop - too much type checking

    //\\ P<ASSOP> =
    //\\    '=' '=',
    //\\    <SCALAR-ASSIGN>;

    case P_ASSOP:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '=', A[ap+0]);
        T[2] = lit(AST__LIT, '=', A[ap+1]);
        T[0] = mktuple(AST_ASSOP, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = build_ast(A[ap+0], depth+1); // <SCALAR-ASSIGN>
        T[0] = mktuple(AST_ASSOP, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ # An initialised array declaration does not need a %C following the '=' before the list of initial values.
    //\\ # this is a stupid hack to get around comments in the middle of array declarations :-(
    //\\ # note that it gobbles the newline
    //\\ # This has to handle: "== nil(*)" from %ownrecord(i tc f)%namearray tc refs(1:maxtc) == nil(*)
    //\\ # Turns out <SCALAR-ASSIGN> didn't include '==' so switched to <ASSOP> instead

    //\\ P<Opt-Init-assign-array> =
    //\\    <ASSOP> <stupid> <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR> <REPFACT> <ROCL>,
    //\\   ;

    case P_Opt_Init_assign_array:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <ASSOP>
        T[2] = build_ast(A[ap+1], depth+1); // <stupid>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-UNARY-OP>
        T[4] = build_ast(A[ap+3], depth+1); // <COPERAND>
        T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-CEXPR>
        T[6] = build_ast(A[ap+5], depth+1); // <REPFACT>
        T[7] = build_ast(A[ap+6], depth+1); // <ROCL>
        T[0] = mktuple(AST_Opt_Init_assign_array, alt /*0*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else               {
        T[0] = mktuple(AST_Opt_Init_assign_array, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # the version without "for" is to support an old-style statement, %WHILE <cond> %THEN <ui>

    //\\ P<Percent-WU> =
    //\\    "while" <CONDITION>,
    //\\    "until" <CONDITION>;

    case P_Percent_WU:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_while, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[0] = mktuple(AST_Percent_WU, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = kw(AST__KW, KW_until, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[0] = mktuple(AST_Percent_WU, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<Percent-WUF> =
    //\\    "while" <CONDITION>,
    //\\    "until" <CONDITION>,
    //\\    "for" <NAME> <SCALAR-ASSIGN> <EXPR> ',' <EXPR> ',' <EXPR>;

    case P_Percent_WUF:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_while, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[0] = mktuple(AST_Percent_WUF, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_until, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[0] = mktuple(AST_Percent_WUF, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[1] = kw(AST__KW, KW_for, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <NAME>
        T[3] = build_ast(A[ap+2], depth+1); // <SCALAR-ASSIGN>
        T[4] = build_ast(A[ap+3], depth+1); // <EXPR>
        T[5] = lit(AST__LIT, ',', A[ap+4]);
        T[6] = build_ast(A[ap+5], depth+1); // <EXPR>
        T[7] = lit(AST__LIT, ',', A[ap+6]);
        T[8] = build_ast(A[ap+7], depth+1); // <EXPR>
        T[0] = mktuple(AST_Percent_WUF, alt /*2*/, /*phrases*/ 8, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8]*/);
      }
      return T[0];

    //\\ P<SCALAR-ASSIGN> =
    //\\    '=',
    //\\    '<' '-';

    case P_SCALAR_ASSIGN:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, '=', A[ap+0]);
        T[0] = mktuple(AST_SCALAR_ASSIGN, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = lit(AST__LIT, '<', A[ap+0]);
        T[2] = lit(AST__LIT, '-', A[ap+1]);
        T[0] = mktuple(AST_SCALAR_ASSIGN, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      }
      return T[0];

    //\\ P<Opt-UNTIL> =
    //\\    "until" <CONDITION>,
    //\\   ;

    case P_Opt_UNTIL:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_until, A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[0] = mktuple(AST_Opt_UNTIL, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_Opt_UNTIL, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<Opt-CYCPARM> =
    //\\    <NAME> <SCALAR-ASSIGN> <EXPR> ',' <EXPR> ',' <EXPR>,
    //\\   ;

    case P_Opt_CYCPARM:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <NAME>
        T[2] = build_ast(A[ap+1], depth+1); // <SCALAR-ASSIGN>
        T[3] = build_ast(A[ap+2], depth+1); // <EXPR>
        T[4] = lit(AST__LIT, ',', A[ap+3]);
        T[5] = build_ast(A[ap+4], depth+1); // <EXPR>
        T[6] = lit(AST__LIT, ',', A[ap+5]);
        T[7] = build_ast(A[ap+6], depth+1); // <EXPR>
        T[0] = mktuple(AST_Opt_CYCPARM, alt /*0*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else               {
        T[0] = mktuple(AST_Opt_CYCPARM, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # gt changed signal to allow 2 exprs.  Mouses Imp77 adds an optional string!
    //\\ # 68000: %signal 5,,holeend,"HEAPPUT: heap corrupt" %if holeend>lhb_limit

    //\\ P<OPEXPR2> =
    //\\    ',' <OPT-EXPR> <OPEXPR>,
    //\\   ;

    case P_OPEXPR2:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <OPEXPR>
        T[0] = mktuple(AST_OPEXPR2, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPEXPR2, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<OPT-EXPR> =
    //\\    <EXPR>,
    //\\   ;

    case P_OPT_EXPR:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <EXPR>
        T[0] = mktuple(AST_OPT_EXPR, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_OPT_EXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # single optional ", expr" for use with %signal event (, subevent)?

    //\\ P<OPEXPR> =
    //\\    ',' <OPT-EXPR> <OPSTRING>,
    //\\   ;

    case P_OPEXPR:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <OPT-EXPR>
        T[3] = build_ast(A[ap+2], depth+1); // <OPSTRING>
        T[0] = mktuple(AST_OPEXPR, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_OPEXPR, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # %signal 3,4,position,"Set input fails" %unless position=0

    //\\ P<OPSTRING> =
    //\\    ',' <STRING-EXPR>,
    //\\   ;

    case P_OPSTRING:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <STRING-EXPR>
        T[0] = mktuple(AST_OPSTRING, alt /*0*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else               {
        T[0] = mktuple(AST_OPSTRING, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # reversed conditions and 1-line cycles

    //\\ P<REST-OF-SS1> =
    //\\    <Percent-IU> <CONDITION> <S>,
    //\\    <Percent-WUF> <S>,
    //\\    <S>,
    //\\   ;

    case P_REST_OF_SS1:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-IU>
        T[2] = build_ast(A[ap+1], depth+1); // <CONDITION>
        T[3] = build_ast(A[ap+2], depth+1); // <S>
        T[0] = mktuple(AST_REST_OF_SS1, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else if (alt == 1) {
        T[1] = build_ast(A[ap+0], depth+1); // <Percent-WUF>
        T[2] = build_ast(A[ap+1], depth+1); // <S>
        T[0] = mktuple(AST_REST_OF_SS1, alt /*1*/, /*phrases*/ 2, T /*T[1], T[2]*/);
      } else if (alt == 2) {
        T[1] = build_ast(A[ap+0], depth+1); // <S>
        T[0] = mktuple(AST_REST_OF_SS1, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_SS1, alt /*3*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<REST-OF-OWNDEC> =
    //\\    ',' <SINGLE-OWNDEC> <REST-OF-OWNDEC>,
    //\\   ;

    case P_REST_OF_OWNDEC:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <SINGLE-OWNDEC>
        T[3] = build_ast(A[ap+2], depth+1); // <REST-OF-OWNDEC>
        T[0] = mktuple(AST_REST_OF_OWNDEC, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);
      } else               {
        T[0] = mktuple(AST_REST_OF_OWNDEC, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<SINGLE-OWNDEC> =
    //\\    <NAME> <Opt-ALIAS> <Opt-Init-assign>;

    case P_SINGLE_OWNDEC:
      T[1] = build_ast(A[ap+0], depth+1); // <NAME>
      T[2] = build_ast(A[ap+1], depth+1); // <Opt-ALIAS>
      T[3] = build_ast(A[ap+2], depth+1); // <Opt-Init-assign>
      return mktuple(AST_SINGLE_OWNDEC, alt /*0*/, /*phrases*/ 3, T /*T[1], T[2], T[3]*/);

    //\\ P<ROCL> =
    //\\    ',' <stupid> <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR> <REPFACT> <ROCL>,
    //\\   ;

    case P_ROCL:
      if (alt == 0)        {
        T[1] = lit(AST__LIT, ',', A[ap+0]);
        T[2] = build_ast(A[ap+1], depth+1); // <stupid>
        T[3] = build_ast(A[ap+2], depth+1); // <Opt-UNARY-OP>
        T[4] = build_ast(A[ap+3], depth+1); // <COPERAND>
        T[5] = build_ast(A[ap+4], depth+1); // <REST-OF-CEXPR>
        T[6] = build_ast(A[ap+5], depth+1); // <REPFACT>
        T[7] = build_ast(A[ap+6], depth+1); // <ROCL>
        T[0] = mktuple(AST_ROCL, alt /*0*/, /*phrases*/ 7, T /*T[1], T[2], T[3], T[4], T[5], T[6], T[7]*/);
      } else               {
        T[0] = mktuple(AST_ROCL, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ P<stupid> =
    //\\    <COMMENT>,
    //\\   ;

    case P_stupid:
      if (alt == 0)        {
        T[1] = build_ast(A[ap+0], depth+1); // <COMMENT>
        T[0] = mktuple(AST_stupid, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[0] = mktuple(AST_stupid, alt /*1*/, /*phrases*/ 0, T /**/);
      }
      return T[0];

    //\\ # extrinsic is an old form meaning roughly "external spec"

    //\\ P<XOWN> =
    //\\    "own",
    //\\    "external",
    //\\    "extrinsic",
    //\\    "constant",
    //\\    "const";

    case P_XOWN:
      if (alt == 0)        {
        T[1] = kw(AST__KW, KW_own, A[ap+0]);
        T[0] = mktuple(AST_XOWN, alt /*0*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 1) {
        T[1] = kw(AST__KW, KW_external, A[ap+0]);
        T[0] = mktuple(AST_XOWN, alt /*1*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 2) {
        T[1] = kw(AST__KW, KW_extrinsic, A[ap+0]);
        T[0] = mktuple(AST_XOWN, alt /*2*/, /*phrases*/ 1, T /*T[1]*/);
      } else if (alt == 3) {
        T[1] = kw(AST__KW, KW_constant, A[ap+0]);
        T[0] = mktuple(AST_XOWN, alt /*3*/, /*phrases*/ 1, T /*T[1]*/);
      } else               {
        T[1] = kw(AST__KW, KW_const, A[ap+0]);
        T[0] = mktuple(AST_XOWN, alt /*4*/, /*phrases*/ 1, T /*T[1]*/);
      }
      return T[0];

    //\\ E