compile-imp80.c

    // compile: this is a skeleton that is built once from the final grammar,
    //          and modified by the programmer to do whatever the program needs to do.
    //
    //          The baseline is simply to re-output the initial input, but obviously
    //          a real implementation would do something more complex, such as indentation
    //          and formatting (e.g. like SOAP), or conversion into another language
    //          such as C, or even to create a real compiler.
    //
    //          If all you are doing is re-outputing the original source, this code is
    //          way overkill.  A 20-line version would do the same job.
    //
    //          You would typically *not* re-create this file automatically in your Makefile...
    //          Remember to edit this comment once you get to the stage where you are
    //          maintaining this procedure manually...
    //

    void compile(int astp) {
      int i;
      // These offsets are a poor man's struct:
      // Offsets in the A record are different from offsets in the AST.

      // A record offsets:
    #define A_literal_offset 3

      // AST offsets:
    #define AST_op_offset 1
    #define AST_alt_offset 2
    #define AST_count_offset 3
    // Any new fixed fields (such as a pointer to the text) would be inserted here.
    // Please do not ever write code with absolute offsets!
    #define AST_child0_offset 4

      if (astp < 0) {
        return; // -phrase denotes an optional item we don't want to print when reconstituting the source
        // Not an ideal mechanism. May change it later to add optional bits to 'op' field.
      }

      int astop = AST[astp+AST_op_offset];
      int alt = AST[astp+AST_alt_offset];
      int count = AST[astp+AST_count_offset];
      int *A_child = &AST[astp+AST_child0_offset]; // An index into a child of the AREC, *not* a child of the AST.

    // Must use these macros for children of the AST, in case new fields are added.
    #define leftchild(p) AST[(p)+AST_child0_offset+(1-1)]
    #define rightchild(p) AST[(p)+AST_child0_offset+(2-1)]
    #define nthchild(p,i) AST[(p)+AST_child0_offset+((i)-1)]

    //\\ 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;
    //\\
    //\\ };

      switch (astop) {
        //\\ #  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)
        case AST_stropped: //\\ B<stropped> = 0;
          RECONSTITUTE(A[A_child[1]+A_literal_offset], stdout);
          break;

        //\\ #
        //\\ #  single-quoted string
        case AST_sqstring: //\\ B<sqstring> = 1;
          RECONSTITUTE(A[A_child[1]+A_literal_offset], stdout);
          break;

        //\\ #
        //\\ #  double-quoted string
        case AST_dqstring: //\\ B<dqstring> = 2;
          RECONSTITUTE(A[A_child[1]+A_literal_offset], stdout);
          break;

        //\\ #
        case AST_NL: //\\ B<NL> = 3;
          RECONSTITUTE(A[A_child[1]+A_literal_offset], stdout);
          break;

        //\\ #
        case AST_char: //\\ B<char> = 4;
          RECONSTITUTE(A[A_child[1]+A_literal_offset], stdout);
          break;

        case AST_EOF:
          //\\ #
          //\\ #  All parsers need an <EOF> phrase.
          //\\ #
          break;

        //\\ #
        //\\ #  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!
        //\\ #
        case AST_LABEL_COLON:        //\\ P<LABEL-COLON> =
          if (alt == 0) {            //\\   <immediate:saved = at_startofstatement; at_startofstatement = TRUE; return TRUE;> ':',
          } else if (alt == 1) {     //\\   <immediate:at_startofstatement = saved; return FALSE;>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ # #P<LABEL-COLON> =
        //\\ # #   ':';
        //\\ #  SS is the primary entry point.
        //\\ #
        case AST_SS:                 //\\ P<SS> =
          if (alt == 0) {            //\\   <EXTERNALS> <LAST-ITEMS-IN-FILE>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #
        //\\ #  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]";
        //\\ #
        case AST_letter:             //\\ P<letter> =
          if (alt == 0) {            //\\   'A',
          } else if (alt == 1) {     //\\   'B',
          } else if (alt == 2) {     //\\   'C',
          } else if (alt == 3) {     //\\   'D',
          } else if (alt == 4) {     //\\   'E',
          } else if (alt == 5) {     //\\   'F',
          } else if (alt == 6) {     //\\   'G',
          } else if (alt == 7) {     //\\   'H',
          } else if (alt == 8) {     //\\   'I',
          } else if (alt == 9) {     //\\   'J',
          } else if (alt == 10) {     //\\   'K',
          } else if (alt == 11) {     //\\   'L',
          } else if (alt == 12) {     //\\   'M',
          } else if (alt == 13) {     //\\   'N',
          } else if (alt == 14) {     //\\   'O',
          } else if (alt == 15) {     //\\   'P',
          } else if (alt == 16) {     //\\   'Q',
          } else if (alt == 17) {     //\\   'R',
          } else if (alt == 18) {     //\\   'S',
          } else if (alt == 19) {     //\\   'T',
          } else if (alt == 20) {     //\\   'U',
          } else if (alt == 21) {     //\\   'V',
          } else if (alt == 22) {     //\\   'W',
          } else if (alt == 23) {     //\\   'X',
          } else if (alt == 24) {     //\\   'Y',
          } else if (alt == 25) {     //\\   'Z';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ # # P<digit> = "[0-9]";
        //\\ #
        case AST_digit:              //\\ P<digit> =
          if (alt == 0) {            //\\   '0',
          } else if (alt == 1) {     //\\   '1',
          } else if (alt == 2) {     //\\   '2',
          } else if (alt == 3) {     //\\   '3',
          } else if (alt == 4) {     //\\   '4',
          } else if (alt == 5) {     //\\   '5',
          } else if (alt == 6) {     //\\   '6',
          } else if (alt == 7) {     //\\   '7',
          } else if (alt == 8) {     //\\   '8',
          } else if (alt == 9) {     //\\   '9';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_alphanumeric:       //\\ P<alphanumeric> =
          if (alt == 0) {            //\\   <letter>,
          } else if (alt == 1) {     //\\   <digit>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_opt_letter_or_digit_seq://\\ P<opt-letter-or-digit-seq> =
          if (alt == 0) {            //\\   <letter> <opt-letter-or-digit-seq>,
          } else if (alt == 1) {     //\\   <digit> <opt-letter-or-digit-seq>,
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ # More than one newline (or semicolon)
        case AST_BLANKS:             //\\ P<BLANKS> =
          if (alt == 0) {            //\\   <S> <BLANKS>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  The last items in a file.
        //\\ #  the final EOF below is only for begin/endofprogram which *can* be followed by code but shouldn't
        case AST_LAST_ITEMS_IN_FILE: //\\ P<LAST-ITEMS-IN-FILE> =
          if (alt == 0) {            //\\   "end" "of" "file" <BLANKS> <EOF>,
          } else if (alt == 1) {     //\\   "end" "of" "perm" <BLANKS> <EOF>,
          } else if (alt == 2) {     //\\   "end" "of" "prim" <BLANKS> <EOF>,
          } else if (alt == 3) {     //\\   <BLANKS> <EOF>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_program:            //\\ P<program> =
          if (alt == 0) {            //\\   "programme",
          } else if (alt == 1) {     //\\   "program";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  I haven't specified any phrases to be external-only.  There might be some,
        //\\ #  but I'll leave it to compile() to reject them.
        case AST_EXTERNALS:          //\\ P<EXTERNALS> =
          if (alt == 0) {            //\\   <EXTERNAL-OR-INTERNAL> <EXTERNALS>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        case AST_INTERNALS:          //\\ P<INTERNALS> =
          if (alt == 0) {            //\\   <OPT-LABEL-DEFINITION> <INTERNAL> <INTERNALS>,
          } else if (alt == 1) {     //\\   <OPT-LABEL-DEFINITION>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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
        case AST_outer_level_end:    //\\ P<outer-level-end> =
          if (alt == 0) {            //\\   "end" "of" <program>,
          } else if (alt == 1) {     //\\   "end";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        //\\ #
        case AST_EXTERNAL_OR_INTERNAL://\\ P<EXTERNAL-OR-INTERNAL> =
          if (alt == 0) {            //\\   "begin" <S> <INTERNALS> <outer-level-end> <S>,
          } else if (alt == 1) {     //\\   <Percent-SEX> <RT> <spec> <NAME> <Opt-ALIAS> <FPP> <S>,
          } else if (alt == 2) {     //\\   <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S> <INTERNALS> <outer-level-end> <S>,
          } else if (alt == 3) {     //\\   <OPT-68K> <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S>,
          } else if (alt == 4) {     //\\   <DECLARATION-S>,
          } else if (alt == 5) {     //\\   "record" "format" <RECFMT-spec-OR-BODY> <S>,
          } else if (alt == 6) {     //\\   "switch" <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST> <S>,
          } else if (alt == 7) {     //\\   "include" <STR-CONST>,
          } else if (alt == 8) {     //\\   "option" <STR-CONST>,
          } else if (alt == 9) {     //\\   "from" <NAME> "include" <NAME> <Opt-NAME-LIST>,
          } else if (alt == 10) {     //\\   "end" "of" "list" <S>,
          } else if (alt == 11) {     //\\   "list" <S>,
          } else if (alt == 12) {     //\\   "end" "of" "mcode" <S>,
          } else if (alt == 13) {     //\\   "mcode" <S>,
          } else if (alt == 14) {     //\\   "trusted" "program" <S>,
          } else if (alt == 15) {     //\\   "main" "ep" <NAME> <S>,
          } else if (alt == 16) {     //\\   "control" <INT-CONST> <S>,
          } else if (alt == 17) {     //\\   "diagnose" <INT-CONST> <S>,
          } else if (alt == 18) {     //\\   "reals" "long",
          } else if (alt == 19) {     //\\   "reals" "normal",
          } else if (alt == 20) {     //\\   <COMMENT>,
          } else if (alt == 21) {     //\\   <S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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 }
        //\\ #
        case AST_OPT_68K:            //\\ P<OPT-68K> =
          if (alt == 0) {            //\\   '@' <68K-stuff>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_68K_stuff:          //\\ P<68K-stuff> =
          if (alt == 0) {            //\\   '-' <INT-CONST> '(' <NAME> ')',
          } else if (alt == 1) {     //\\   <INT-CONST> '(' <NAME> ')',
          } else if (alt == 2) {     //\\   <INT-CONST> '-' <INT-CONST>,
          } else if (alt == 3) {     //\\   <INT-CONST>,
          } else if (alt == 4) {     //\\   '#' <NAME>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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  ???
        //\\ #
        case AST_spec:               //\\ P<spec> =
          if (alt == 0) {            //\\   "specalias",
          } else if (alt == 1) {     //\\   "spec" <Opt-68K-code-address>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_68K_code_address://\\ P<Opt-68K-code-address> =
          if (alt == 0) {            //\\   '(' <INT-CONST> ')',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_spec:           //\\ P<OPT-spec> =
          if (alt == 0) {            //\\   <spec>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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...
        case AST_OPT_LABEL_DEFINITION://\\ P<OPT-LABEL-DEFINITION> =
          if (alt == 0) {            //\\   <NAME> '(' <CEXPR> ')' <LABEL-COLON> <OPT-LABEL-DEFINITION>,
          } else if (alt == 1) {     //\\   <NAME> '(' '*' ')' <LABEL-COLON> <OPT-LABEL-DEFINITION>,
          } else if (alt == 2) {     //\\   <NAME> <LABEL-COLON> <OPT-LABEL-DEFINITION>,
          } else if (alt == 3) {     //\\   <DIGIT-SEQ> <LABEL-COLON> <OPT-LABEL-DEFINITION>,
          } else if (alt == 4) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        case AST_FAULT_S:            //\\ P<FAULT-S> =
          if (alt == 0) {            //\\   "fault" <FAULT-ACTION> <MORE-FAULTS> <S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_FAULT_LIST:         //\\ P<FAULT-LIST> =
          if (alt == 0) {            //\\   <DIGIT-SEQ> <MORE-NUMBERS>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_MORE_NUMBERS:       //\\ P<MORE-NUMBERS> =
          if (alt == 0) {            //\\   ',' <DIGIT-SEQ> <MORE-NUMBERS>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_FAULT_ACTION:       //\\ P<FAULT-ACTION> =
          if (alt == 0) {            //\\   <FAULT-LIST> '-' '>' <LABEL>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_MORE_FAULTS:        //\\ P<MORE-FAULTS> =
          if (alt == 0) {            //\\   ',' <FAULT-ACTION> <MORE-FAULTS>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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 )
        case AST_INTERNAL:           //\\ P<INTERNAL> =
          if (alt == 0) {            //\\   <NESTED-BLOCK>,
          } else if (alt == 1) {     //\\   <UI> <REST-OF-SS1>,
          } else if (alt == 2) {     //\\   '*' <UCI>,
          } else if (alt == 3) {     //\\   <CYCLE-S>,
          } else if (alt == 4) {     //\\   <IF-S>,
          } else if (alt == 5) {     //\\   <FAULT-S>,
          } else if (alt == 6) {     //\\   "on" <OPT-event> <EVENT-LIST> <REST-OF-NAME-LIST> "start" <S> <INTERNALS> "finish" <S>,
          } else if (alt == 7) {     //\\   "spec" <NAME> <FPP> <S>,
          } else if (alt == 8) {     //\\   <EXTERNAL-OR-INTERNAL>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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..)
        //\\ #
        case AST_NESTED_BLOCK:       //\\ P<NESTED-BLOCK> =
          if (alt == 0) {            //\\   "begin" <S> <INTERNALS> "end" <S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  compound statements - blah %and blah %and blah - a short form of startfinish block
        case AST_AUI:                //\\ P<AUI> =
          if (alt == 0) {            //\\   "and" <UI>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_ASSIGN:         //\\ P<OPT-ASSIGN> =
          if (alt == 0) {            //\\   '-' '>' <STRING-RESOLUTION-EXPR>,
          } else if (alt == 1) {     //\\   <ASSOP> <EXPR>,
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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"
        case AST_UI:                 //\\ P<UI> =
          if (alt == 0) {            //\\   <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD> <OPT-ASSIGN> <AUI>,
          } else if (alt == 1) {     //\\   "print" "text" <sqstring> <AUI>,
          } else if (alt == 2) {     //\\   '-' '>' <LABEL>,
          } else if (alt == 3) {     //\\   "return",
          } else if (alt == 4) {     //\\   "true",
          } else if (alt == 5) {     //\\   "false",
          } else if (alt == 6) {     //\\   "result" <ASSOP> <EXPR>,
          } else if (alt == 7) {     //\\   "monitorstop",
          } else if (alt == 8) {     //\\   "monitor" <DIGIT-SEQ> <AUI>,
          } else if (alt == 9) {     //\\   "monitor" <AUI>,
          } else if (alt == 10) {     //\\   "stop",
          } else if (alt == 11) {     //\\   "signal" <OPT-event> <CEXPR> <OPEXPR2>,
          } else if (alt == 12) {     //\\   "exit",
          } else if (alt == 13) {     //\\   "continue";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_LABEL:              //\\ P<LABEL> =
          if (alt == 0) {            //\\   <NAME> '(' <EXPR> ')',
          } else if (alt == 1) {     //\\   <NAME>,
          } else if (alt == 2) {     //\\   <DIGIT-SEQ>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  prim and perm added by gt
        case AST_FILE_END_TYPE:      //\\ P<FILE-END-TYPE> =
          if (alt == 0) {            //\\   "file",
          } else if (alt == 1) {     //\\   "prim",
          } else if (alt == 2) {     //\\   "perm";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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>
        //\\ #
        case AST_DECLARATION_S:      //\\ P<DECLARATION-S> =
          if (alt == 0) {            //\\   <OPT-68K> <XOWN> <XTYPE> <OWNDEC> <S>,
          } else if (alt == 1) {     //\\   <OPT-68K> <XTYPE> <DECLN> <S>,
          } else if (alt == 2) {     //\\   <OPT-68K> <OPT-XOWN> "record" "array" <RADEC> <REST-OF-RADEC> <S>,
          } else if (alt == 3) {     //\\   <OPT-68K> <OPT-XOWN> "record" <RDEC> <REST-OF-RDEC> <S>,
          } else if (alt == 4) {     //\\   "const" "char" <NAME> '=' 'C' 'H' 'A' 'R' '<' <INT-CONST> '>' <S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_XOWN:           //\\ P<OPT-XOWN> =
          if (alt == 0) {            //\\   <XOWN>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  old-style record array declaration
        case AST_RADEC:              //\\ P<RADEC> =
          if (alt == 0) {            //\\   <NAME> <Opt-NAME-LIST> <BPAIR> '(' <RECFMT-REF> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_RADEC:      //\\ P<REST-OF-RADEC> =
          if (alt == 0) {            //\\   ',' <RADEC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  old-style record declaration
        case AST_RDEC:               //\\ P<RDEC> =
          if (alt == 0) {            //\\   <NAME> <Opt-NAME-LIST> '(' <RECFMT-REF> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_RDEC:       //\\ P<REST-OF-RDEC> =
          if (alt == 0) {            //\\   ',' <RDEC> <REST-OF-RDEC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  %on %event * %start or %on %event 1,2,3,etc %start
        case AST_EVENT_LIST:         //\\ P<EVENT-LIST> =
          if (alt == 0) {            //\\   '*',
          } else if (alt == 1) {     //\\   <CEXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Added old-style while/until for: %UNTIL NEXTINSYM=NL %THEN %CYCLE
        //\\ #  and %WHILE NEXTINSYM#NL %THEN SKIPINSYM
        case AST_CYCLE_S:            //\\ P<CYCLE-S> =
          if (alt == 0) {            //\\   "cycle" <Opt-CYCPARM> <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
          } else if (alt == 1) {     //\\   <Percent-WUF> "cycle" <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
          } else if (alt == 2) {     //\\   <Percent-WU> "then" "cycle" <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
          } else if (alt == 3) {     //\\   <Percent-WU> "then" <UI>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_IF_S:               //\\ P<IF-S> =
          if (alt == 0) {            //\\   <Percent-IU> <CONDITION> <THEN-S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  /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
        //\\ #
        case AST_FINISH_S:           //\\ P<FINISH-S> =
          if (alt == 0) {            //\\   "else" <Percent-IU> <CONDITION> <Opt-start> <S> <INTERNALS> <FINISH-S>,
          } else if (alt == 1) {     //\\   "else" <Opt-start> <S> <INTERNALS> "finish" <S>,
          } else if (alt == 2) {     //\\   "finish" <Opt-ELSE-S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_start:          //\\ P<Opt-start> =
          if (alt == 0) {            //\\   "start",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  see the comments on <FINISH-S> above.
        case AST_Opt_ELSE_S:         //\\ P<Opt-ELSE-S> =
          if (alt == 0) {            //\\   "else" "start" <S> <INTERNALS> "finish" <S>,
          } else if (alt == 1) {     //\\   "else" <IF-S>,
          } else if (alt == 2) {     //\\   "else" <UI> <S>,
          } else if (alt == 3) {     //\\   <S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_THEN_S:             //\\ P<THEN-S> =
          if (alt == 0) {            //\\   <OPT-then> "start" <S> <INTERNALS> <FINISH-S>,
          } else if (alt == 1) {     //\\   "then" <UI> <Opt-ELSE-S>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_then:           //\\ P<OPT-then> =
          if (alt == 0) {            //\\   "then",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_start:          //\\ P<OPT-start> =
          if (alt == 0) {            //\\   "start",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_S:                  //\\ P<S> =
          if (alt == 0) {            //\\   <NL>,
          } else if (alt == 1) {     //\\   ';';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        //\\ #
        case AST_COMMENT:            //\\ P<COMMENT> =
          if (alt == 0) {            //\\   "comment" <Parsed-OldStyle-Comment>,
          } else if (alt == 1) {     //\\   '!' <Parsed-OldStyle-Comment>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Parsed_OldStyle_Comment://\\ P<Parsed-OldStyle-Comment> =
          if (alt == 0) {            //\\   <char> <Parsed-OldStyle-Comment>,
          } else if (alt == 1) {     //\\   <dqstring> <Parsed-OldStyle-Comment>,
          } else if (alt == 2) {     //\\   <sqstring> <Parsed-OldStyle-Comment>,
          } else if (alt == 3) {     //\\   <letter> <Parsed-OldStyle-Comment>,
          } else if (alt == 4) {     //\\   <digit> <Parsed-OldStyle-Comment>,
          } else if (alt == 5) {     //\\   <stropped> <Parsed-OldStyle-Comment>,
          } else if (alt == 6) {     //\\   <NL>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_NL_or_SEMI:         //\\ P<NL-or-SEMI> =
          if (alt == 0) {            //\\   <NL>,
          } else if (alt == 1) {     //\\   <semi>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_semi:               //\\ P<semi> =
          if (alt == 0) {            //\\   ';';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <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]*"
        //\\ #
        case AST_INT_CONST:          //\\ P<INT-CONST> =
          if (alt == 0) {            //\\   <DIGIT-SEQ> '_' <alphanumeric-SEQ>,
          } else if (alt == 1) {     //\\   <DIGIT-SEQ>,
          } else if (alt == 2) {     //\\   <OLDSTYLE-BASE>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_alphanumeric_SEQ:   //\\ P<alphanumeric-SEQ> =
          if (alt == 0) {            //\\   <alphanumeric> <Opt-alphanumeric-SEQ>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_alphanumeric_SEQ://\\ P<Opt-alphanumeric-SEQ> =
          if (alt == 0) {            //\\   <alphanumeric> <Opt-alphanumeric-SEQ>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_STR_CONST:          //\\ P<STR-CONST> =
          if (alt == 0) {            //\\   <OPT-DQ-LETTER> <dqstring>,
          } else if (alt == 1) {     //\\   <sqstring>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  current line reconstruction doesn't support this. Letters come through as part of <alphanumeric>s.
        case AST_OPT_DQ_LETTER:      //\\ P<OPT-DQ-LETTER> =
          if (alt == 0) {            //\\   'E',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_NUMERIC_CONST:      //\\ P<NUMERIC-CONST> =
          if (alt == 0) {            //\\   <INT-CONST>,
          } else if (alt == 1) {     //\\   <REAL-CONST>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <CONST> is somewhat generic and should be replaced with the more specific version when possible
        case AST_CONST:              //\\ P<CONST> =
          if (alt == 0) {            //\\   <INT-CONST>,
          } else if (alt == 1) {     //\\   <REAL-CONST>,
          } else if (alt == 2) {     //\\   <STR-CONST>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <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>;
        //\\ #
        case AST_OLDSTYLE_BASE:      //\\ P<OLDSTYLE-BASE> =
          if (alt == 0) {            //\\   <letter> <sqstring>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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]*"
        //\\ #
        case AST_REAL_CONST:         //\\ P<REAL-CONST> =
          if (alt == 0) {            //\\   <DIGIT-SEQ> <OPT-FRACPT> <OPT-EXPONENT>,
          } else if (alt == 1) {     //\\   <FRACPT> <OPT-EXPONENT>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_FRACPT:             //\\ P<FRACPT> =
          if (alt == 0) {            //\\   '.' <DIGIT-SEQ>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_FRACPT:         //\\ P<OPT-FRACPT> =
          if (alt == 0) {            //\\   <FRACPT>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_Plus_Minus:     //\\ P<OPT-Plus-Minus> =
          if (alt == 0) {            //\\   '+',
          } else if (alt == 1) {     //\\   '-',
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_EXPONENT:       //\\ P<OPT-EXPONENT> =
          if (alt == 0) {            //\\   '@' <OPT-Plus-Minus> <DIGIT-SEQ>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_DIGIT_SEQ:          //\\ P<DIGIT-SEQ> =
          if (alt == 0) {            //\\   <digit> <rest-of-number>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_rest_of_number:     //\\ P<rest-of-number> =
          if (alt == 0) {            //\\   <DIGIT-SEQ>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_NAME:               //\\ P<NAME> =
          if (alt == 0) {            //\\   <letter> <!SQUO-STRING> <opt-letter-or-digit-seq>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ # # P<NAME> =
        //\\ # #    <alphanumeric> <!SQUO-STRING> <rest-of-name>;
        //\\ # # P<rest-of-name> =
        //\\ # #    <NAME>,
        //\\ # #   ;
        case AST_SQUO_STRING:        //\\ P<SQUO-STRING> =
          if (alt == 0) {            //\\   <sqstring>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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
        //\\ #
        case AST_UCI:                //\\ P<UCI> =
          if (alt == 0) {            //\\   <NL-or-SEMI>,
          } else if (alt == 1) {     //\\   <char> <UCI>,
          } else if (alt == 2) {     //\\   <dqstring> <UCI>,
          } else if (alt == 3) {     //\\   <sqstring> <UCI>,
          } else if (alt == 4) {     //\\   <stropped> <UCI>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Imp does not let you mix and/or without explicit brackets. They do not have a precedence relationship
        case AST_REST_OF_COND:       //\\ P<REST-OF-COND> =
          if (alt == 0) {            //\\   "and" <SC> <REST-OF-ANDC>,
          } else if (alt == 1) {     //\\   "or" <SC> <REST-OF-ORC>,
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_ANDC:       //\\ P<REST-OF-ANDC> =
          if (alt == 0) {            //\\   "and" <SC> <REST-OF-ANDC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_ORC:        //\\ P<REST-OF-ORC> =
          if (alt == 0) {            //\\   "or" <SC> <REST-OF-ORC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_CONDITION:          //\\ P<CONDITION> =
          if (alt == 0) {            //\\   <SC> <REST-OF-COND>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <NAME> <OPT-ACTUAL-PARAMETERS> added by gt for predicates
        //\\ #  RESTOFSC handles double-sided conditions such as '0' <= ch <= '9'
        case AST_SC:                 //\\ P<SC> =
          if (alt == 0) {            //\\   <STRING-RESOLUTION>,
          } else if (alt == 1) {     //\\   <EXPR> <COMP> <EXPR> <OPT-DOUBLE-SIDED>,
          } else if (alt == 2) {     //\\   <NAME> <OPT-ACTUAL-PARAMETERS>,
          } else if (alt == 3) {     //\\   '(' <CONDITION> ')',
          } else if (alt == 4) {     //\\   "not" <SC>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_DOUBLE_SIDED:   //\\ P<OPT-DOUBLE-SIDED> =
          if (alt == 0) {            //\\   <COMP> <EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_STRING_RESOLUTION:  //\\ P<STRING-RESOLUTION> =
          if (alt == 0) {            //\\   <STRING-EXPR> '-' '>' <STRING-RESOLUTION-EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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()
        //\\ #
        case AST_STRING_RESOLUTION_EXPR://\\ P<STRING-RESOLUTION-EXPR> =
          if (alt == 0) {            //\\   <STR-MATCH> <OPT-STR-ASSIGN>,
          } else if (alt == 1) {     //\\   <STR-ASSIGN> <OPT-STR-MATCH>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_STRING_LVALUE:      //\\ P<STRING-LVALUE> =
          if (alt == 0) {            //\\   <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_STRING_RVALUE:      //\\ P<STRING-RVALUE> =
          if (alt == 0) {            //\\   <STRING-LVALUE>,
          } else if (alt == 1) {     //\\   <STR-CONST>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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?
        //\\ #
        case AST_STRING_EXPR:        //\\ P<STRING-EXPR> =
          if (alt == 0) {            //\\   <STRING-RVALUE> <REST-OF-STRING-EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_STRING_EXPR://\\ P<REST-OF-STRING-EXPR> =
          if (alt == 0) {            //\\   '.' <STRING-RVALUE> <REST-OF-STRING-EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_STR_MATCH:          //\\ P<STR-MATCH> =
          if (alt == 0) {            //\\   '(' <STRING-EXPR> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_STR_ASSIGN:         //\\ P<STR-ASSIGN> =
          if (alt == 0) {            //\\   <STRING-LVALUE>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  ping-pong.  Can't allow -> (a).b.c.(d) or -> a.(b).(c).d
        case AST_OPT_STR_ASSIGN:     //\\ P<OPT-STR-ASSIGN> =
          if (alt == 0) {            //\\   '.' <STR-ASSIGN> <OPT-STR-MATCH>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_STR_MATCH:      //\\ P<OPT-STR-MATCH> =
          if (alt == 0) {            //\\   '.' <STR-MATCH> <OPT-STR-ASSIGN>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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 :-)
        //\\ #
        case AST_COMP:               //\\ P<COMP> =
          if (alt == 0) {            //\\   '=' '=',
          } else if (alt == 1) {     //\\   '=',
          } else if (alt == 2) {     //\\   <NOT-EQUALS-ADDRESS>,
          } else if (alt == 3) {     //\\   <NOT-EQUALS>,
          } else if (alt == 4) {     //\\   '<' '=',
          } else if (alt == 5) {     //\\   '<',
          } else if (alt == 6) {     //\\   '>' '=',
          } else if (alt == 7) {     //\\   '>';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  '<>' is a really obscure form but it definitely used to be used.  I'll
        //\\ #  post a reference as soon as I locate one.
        //\\ #
        case AST_NOT_EQUALS:         //\\ P<NOT-EQUALS> =
          if (alt == 0) {            //\\   '#',
          } else if (alt == 1) {     //\\   '\' '=',
          } else if (alt == 2) {     //\\   '<' '>';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_NOT_EQUALS_ADDRESS: //\\ P<NOT-EQUALS-ADDRESS> =
          if (alt == 0) {            //\\   '#' '#',
          } else if (alt == 1) {     //\\   '\' '=' '=';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Procedure application, i.e. actual parameters to routine or function call
        case AST_OPT_ACTUAL_PARAMETERS://\\ P<OPT-ACTUAL-PARAMETERS> =
          if (alt == 0) {            //\\   '(' <EXPR> <REST-OF-ACTUAL-PARAMETERS> ')',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_ACTUAL_PARAMETERS://\\ P<REST-OF-ACTUAL-PARAMETERS> =
          if (alt == 0) {            //\\   ',' <EXPR> <REST-OF-ACTUAL-PARAMETERS>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  handling precedence withh the grammar rather than Edinburgh-style in the AST. Tradeoff of speed for simplicity.
        //\\ #  CEXPR not handled yet
        case AST_EXPR:               //\\ P<EXPR> =
          if (alt == 0) {            //\\   <MEDIUMPREC-EXPR> <REST-OF-EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_EXPR:       //\\ P<REST-OF-EXPR> =
          if (alt == 0) {            //\\   <OP-LOW> <MEDIUMPREC-EXPR> <REST-OF-EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_MEDIUMPREC_EXPR:    //\\ P<MEDIUMPREC-EXPR> =
          if (alt == 0) {            //\\   <HIGHPREC-EXPR> <REST-OF-MEDIUMPREC-EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_MEDIUMPREC_EXPR://\\ P<REST-OF-MEDIUMPREC-EXPR> =
          if (alt == 0) {            //\\   <OP-MED> <HIGHPREC-EXPR> <REST-OF-MEDIUMPREC-EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_HIGHPREC_EXPR:      //\\ P<HIGHPREC-EXPR> =
          if (alt == 0) {            //\\   <RIGHT-ASSOC-EXPR> <REST-OF-HIGHPREC-EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_HIGHPREC_EXPR://\\ P<REST-OF-HIGHPREC-EXPR> =
          if (alt == 0) {            //\\   <OP-HIGH-LEFTASSOC> <RIGHT-ASSOC-EXPR> <REST-OF-HIGHPREC-EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_RIGHT_ASSOC_EXPR:   //\\ P<RIGHT-ASSOC-EXPR> =
          if (alt == 0) {            //\\   <REST-OF-RIGHTASSOC-EXPR> <UNARY-EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_RIGHTASSOC_EXPR://\\ P<REST-OF-RIGHTASSOC-EXPR> =
          if (alt == 0) {            //\\   <RIGHTASSOC-EXPR-with-OP> <REST-OF-RIGHTASSOC-EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_RIGHTASSOC_EXPR_with_OP://\\ P<RIGHTASSOC-EXPR-with-OP> =
          if (alt == 0) {            //\\   <UNARY-EXPR> <OP-HIGH-RIGHTASSOC>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_UNARY_EXPR:         //\\ P<UNARY-EXPR> =
          if (alt == 0) {            //\\   <Opt-UNARY-OP> <OPERAND>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        case AST_Opt_PARAMETERS_OR_POINTERINDEX://\\ P<Opt-PARAMETERS-OR-POINTERINDEX> =
          if (alt == 0) {            //\\   '[' <EXPR> ']',
          } else if (alt == 1) {     //\\   '+' '+' <EXPR>,
          } else if (alt == 2) {     //\\   '-' '-' <EXPR>,
          } else if (alt == 3) {     //\\   <OPT-ACTUAL-PARAMETERS>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_RECORDFIELD:    //\\ P<Opt-RECORDFIELD> =
          if (alt == 0) {            //\\   '_' <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_RECORDFIELD_OR_POINTERINDEX://\\ P<Opt-RECORDFIELD-OR-POINTERINDEX> =
          if (alt == 0) {            //\\   '_' <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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 '!'
        //\\ #
        case AST_OPERAND:            //\\ P<OPERAND> =
          if (alt == 0) {            //\\   <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>,
          } else if (alt == 1) {     //\\   <CONST>,
          } else if (alt == 2) {     //\\   '(' <EXPR> ')',
          } else if (alt == 3) {     //\\   '|' <EXPR> '|',
          } else if (alt == 4) {     //\\   '!' <EXPR> '!';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <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!)
        //\\ #
        case AST_OP:                 //\\ P<OP> =
          if (alt == 0) {            //\\   <OP-HIGH-LEFTASSOC>,
          } else if (alt == 1) {     //\\   <OP-HIGH-RIGHTASSOC>,
          } else if (alt == 2) {     //\\   <OP-MED>,
          } else if (alt == 3) {     //\\   <OP-LOW>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  highest precedence, left-associative
        case AST_OP_HIGH_LEFTASSOC:  //\\ P<OP-HIGH-LEFTASSOC> =
          if (alt == 0) {            //\\   '<' '<',
          } else if (alt == 1) {     //\\   '>' '>';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        //\\ #
        case AST_OP_HIGH_RIGHTASSOC: //\\ P<OP-HIGH-RIGHTASSOC> =
          if (alt == 0) {            //\\   <INTEGER-EXP>,
          } else if (alt == 1) {     //\\   <REAL-EXP>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_INTEGER_EXP:        //\\ P<INTEGER-EXP> =
          if (alt == 0) {            //\\   '*' '*' '*' '*',
          } else if (alt == 1) {     //\\   '\' '\',
          } else if (alt == 2) {     //\\   '^' '^';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REAL_EXP:           //\\ P<REAL-EXP> =
          if (alt == 0) {            //\\   '*' '*',
          } else if (alt == 1) {     //\\   '\',
          } else if (alt == 2) {     //\\   '^';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  middle-precedence operators.  All left-associative.
        case AST_OP_MED:             //\\ P<OP-MED> =
          if (alt == 0) {            //\\   '*',
          } else if (alt == 1) {     //\\   '/' '/',
          } else if (alt == 2) {     //\\   '/',
          } else if (alt == 3) {     //\\   '&';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  lowest precedence operators.  All left-associative.
        case AST_OP_LOW:             //\\ P<OP-LOW> =
          if (alt == 0) {            //\\   '+',
          } else if (alt == 1) {     //\\   '-',
          } else if (alt == 2) {     //\\   '!' '!',
          } else if (alt == 3) {     //\\   '!',
          } else if (alt == 4) {     //\\   '.';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        //\\ #
        case AST_Opt_UNARY_OP:       //\\ P<Opt-UNARY-OP> =
          if (alt == 0) {            //\\   '+',
          } else if (alt == 1) {     //\\   '-',
          } else if (alt == 2) {     //\\   <UNARY-NOT>,
          } else if (alt == 3) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  fortunately unary backslash is unambiguous with ERCC style exponentiation binary operator
        case AST_UNARY_NOT:          //\\ P<UNARY-NOT> =
          if (alt == 0) {            //\\   '\',
          } else if (alt == 1) {     //\\   '~';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Percent_IU:         //\\ P<Percent-IU> =
          if (alt == 0) {            //\\   "if",
          } else if (alt == 1) {     //\\   "unless";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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...
        case AST_REST_OF_SWLIST:     //\\ P<REST-OF-SWLIST> =
          if (alt == 0) {            //\\   ',' <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_NAME_LIST:      //\\ P<Opt-NAME-LIST> =
          if (alt == 0) {            //\\   ',' <NAME> <Opt-NAME-LIST>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_NAME_LIST:  //\\ P<REST-OF-NAME-LIST> =
          if (alt == 0) {            //\\   ',' <CEXPR> <REST-OF-NAME-LIST>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        case AST_CEXPR:              //\\ P<CEXPR> =
          if (alt == 0) {            //\\   <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_CEXPR:      //\\ P<REST-OF-CEXPR> =
          if (alt == 0) {            //\\   <OP> <COPERAND> <REST-OF-CEXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_COPERAND:           //\\ P<COPERAND> =
          if (alt == 0) {            //\\   <NAME>,
          } else if (alt == 1) {     //\\   <CONST>,
          } else if (alt == 2) {     //\\   '(' <CEXPR> ')',
          } else if (alt == 3) {     //\\   '|' <CEXPR> '|',
          } else if (alt == 4) {     //\\   '!' <CEXPR> '!';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_event:          //\\ P<OPT-event> =
          if (alt == 0) {            //\\   "event",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_FPP:                //\\ P<FPP> =
          if (alt == 0) {            //\\   '(' <FORMAL-PARAMETER-DECLARATION> <REST-OF-FORMAL-PARAMETER-LIST> ')',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_FORMAL_PARAMETER_LIST://\\ P<REST-OF-FORMAL-PARAMETER-LIST> =
          if (alt == 0) {            //\\   <Opt-Comma> <FORMAL-PARAMETER-DECLARATION> <REST-OF-FORMAL-PARAMETER-LIST>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  %routine y (%integer %name %array (1) %name b)
        //\\ #  %routine r10(%integer%array%name a(1:10),%integer k)
        case AST_FORMAL_PARAMETER_DECLARATION://\\ P<FORMAL-PARAMETER-DECLARATION> =
          if (alt == 0) {            //\\   <XTYPE> <OPT-arrayname> <NAME> <OPT-68K-Bounds> <Opt-NAME-LIST>,
          } else if (alt == 1) {     //\\   <RT> <OPT-name> <NAME> <Opt-NAME-LIST> <FPP>,
          } else if (alt == 2) {     //\\   "name" <Opt-68K-reg> <NAME> <Opt-NAME-LIST>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  More Hamish idiosyncracy!:
        //\\ #
        //\\ #  @16_1114%record(*)%map ZNEW(%name(d0) v)
        //\\ #   - APM-gdmr/I/VPERM19.imp
        //\\ #       d0 being a register
        //\\ #
        case AST_Opt_68K_reg:        //\\ P<Opt-68K-reg> =
          if (alt == 0) {            //\\   '(' <NAME> ')',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_name:           //\\ P<OPT-name> =
          if (alt == 0) {            //\\   "name",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  added predicate - gt
        case AST_RT:                 //\\ P<RT> =
          if (alt == 0) {            //\\   "routine",
          } else if (alt == 1) {     //\\   "predicate",
          } else if (alt == 2) {     //\\   <XTYPE> <FN-MAP>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  note map is equivalent to "name function"
        case AST_FN_MAP:             //\\ P<FN-MAP> =
          if (alt == 0) {            //\\   <FN>,
          } else if (alt == 1) {     //\\   "map";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_FN:                 //\\ P<FN> =
          if (alt == 0) {            //\\   "fn",
          } else if (alt == 1) {     //\\   "function";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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)
        //\\ #
        case AST_BASE_XTYPE:         //\\ P<BASE-XTYPE> =
          if (alt == 0) {            //\\   "integer",
          } else if (alt == 1) {     //\\   "real",
          } else if (alt == 2) {     //\\   "label",
          } else if (alt == 3) {     //\\   "long" <Opt-BTYPE>,
          } else if (alt == 4) {     //\\   "byte" <Opt-INTEGER>,
          } else if (alt == 5) {     //\\   "mite" <Opt-INTEGER>,
          } else if (alt == 6) {     //\\   "short" <Opt-INTEGER>,
          } else if (alt == 7) {     //\\   "half" <Opt-INTEGER>,
          } else if (alt == 8) {     //\\   "string" <REPFACT>,
          } else if (alt == 9) {     //\\   "record" <Opt-RFREF>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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
        //\\ #
        case AST_XTYPE:              //\\ P<XTYPE> =
          if (alt == 0) {            //\\   <OPT-68K-qualifier> <BASE-XTYPE> <OPT-name>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_68K_memptr:         //\\ P<68K-memptr> =
          if (alt == 0) {            //\\   "read" "only" <68K-memptr>,
          } else if (alt == 1) {     //\\   "write" "only" <68K-memptr>,
          } else if (alt == 2) {     //\\   "volatile" <68K-memptr>,
          } else if (alt == 3) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_68K_qualifier:  //\\ P<OPT-68K-qualifier> =
          if (alt == 0) {            //\\   <68K-register>,
          } else if (alt == 1) {     //\\   <68K-memptr>,
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  C-like register qualifiers (Hamish, again)
        //\\ #  %register(a2)%integer r1,r2
        //\\ #  %register(a2)%byte%name bn
        //\\ #  %register(a3)%short%name sn
        case AST_68K_register:       //\\ P<68K-register> =
          if (alt == 0) {            //\\   "register" '(' <NAME> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  '*' added by gt for recordformat(*)
        //\\ #  () moved from point of call to here to support old style "%recordname fred" without format. (Uses a formatspec instead)
        case AST_Opt_RFREF:          //\\ P<Opt-RFREF> =
          if (alt == 0) {            //\\   '(' <RECFMT-REF> ')',
          } else if (alt == 1) {     //\\   '(' '*' ')',
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  RF in the rules below is short for Record Format
        case AST_RECFMT_REF:         //\\ P<RECFMT-REF> =
          if (alt == 0) {            //\\   <NAME>,
          } else if (alt == 1) {     //\\   <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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.
        //\\ #
        case AST_ALT_RECFMT_DEC:     //\\ P<ALT-RECFMT-DEC> =
          if (alt == 0) {            //\\   "or" <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_RECFMT_DEC: //\\ P<REST-OF-RECFMT-DEC> =
          if (alt == 0) {            //\\   ',' <RECFMT-DEC> <REST-OF-RECFMT-DEC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_RECFMT_DEC:         //\\ P<RECFMT-DEC> =
          if (alt == 0) {            //\\   <XTYPE> <RECFMT-ELMNT>,
          } else if (alt == 1) {     //\\   '(' <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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>...
        //\\ #
        case AST_RECFMT_ELMNT:       //\\ P<RECFMT-ELMNT> =
          if (alt == 0) {            //\\   <OPT-arrayname> <NAME> <Opt-NAME-LIST>,
          } else if (alt == 1) {     //\\   "array" <ADECLN-in-record>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Array declaration
        case AST_ADECLN_in_record:   //\\ P<ADECLN-in-record> =
          if (alt == 0) {            //\\   <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST-in-record>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_ARLIST_in_record://\\ P<REST-OF-ARLIST-in-record> =
          if (alt == 0) {            //\\   ',' <ADECLN-in-record>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_LOWERBOUND:         //\\ P<LOWERBOUND> =
          if (alt == 0) {            //\\   <EXPR>,
          } else if (alt == 1) {     //\\   <BASE-XTYPE> <NAME>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Another Hamishism... %integer %array %name fred(1:*)
        //\\ #  which in Imp77 would be %integer %array (1) fred
        //\\ #  (reject the '*' in other contexts)
        //\\ #
        case AST_UPPERBOUND:         //\\ P<UPPERBOUND> =
          if (alt == 0) {            //\\   '*',
          } else if (alt == 1) {     //\\   <EXPR>,
          } else if (alt == 2) {     //\\   <BASE-XTYPE> <NAME>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  List of lower/upper bound pairs, for array declarations.
        case AST_BPAIR:              //\\ P<BPAIR> =
          if (alt == 0) {            //\\   '(' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_BPAIR68K:           //\\ P<BPAIR68K> =
          if (alt == 0) {            //\\   '(' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST68K> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Multi-dimensional arrays
        case AST_REST_OF_BPLIST:     //\\ P<REST-OF-BPLIST> =
          if (alt == 0) {            //\\   ',' <EXPR> ':' <EXPR> <REST-OF-BPLIST>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_BPLIST68K:  //\\ P<REST-OF-BPLIST68K> =
          if (alt == 0) {            //\\   ',' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST68K>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <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)
        //\\ #
        case AST_OPT_arrayname:      //\\ P<OPT-arrayname> =
          if (alt == 0) {            //\\   "array" <Opt-DIMENSIONS> "name",
          } else if (alt == 1) {     //\\   "array" <OPT-68K-Bounds> "name",
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  for the example from 68000 Imp,
        //\\ #  %routine y (%integer %name %array (1) %name b)
        case AST_Opt_DIMENSIONS:     //\\ P<Opt-DIMENSIONS> =
          if (alt == 0) {            //\\   '(' <INT-CONST> ')',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_INTEGER:        //\\ P<Opt-INTEGER> =
          if (alt == 0) {            //\\   "integer",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  "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?!
        case AST_REPFACT:            //\\ P<REPFACT> =
          if (alt == 0) {            //\\   '(' <Opt-STAROREXPR> ')',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_STAROREXPR:     //\\ P<Opt-STAROREXPR> =
          if (alt == 0) {            //\\   <STAROREXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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>?
        case AST_STAROREXPR:         //\\ P<STAROREXPR> =
          if (alt == 0) {            //\\   <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR>,
          } else if (alt == 1) {     //\\   '*';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  Opt- added here to allow plain "%long" as a declaration at <XTYPE>
        case AST_Opt_BTYPE:          //\\ P<Opt-BTYPE> =
          if (alt == 0) {            //\\   "real",
          } else if (alt == 1) {     //\\   "integer",
          } else if (alt == 2) {     //\\   "long" "real",
          } else if (alt == 3) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_Comma:          //\\ P<Opt-Comma> =
          if (alt == 0) {            //\\   ',',
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_ALIAS:          //\\ P<Opt-ALIAS> =
          if (alt == 0) {            //\\   "alias" <TEXTTEXT>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_TEXTTEXT:           //\\ P<TEXTTEXT> =
          if (alt == 0) {            //\\   <dqstring>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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!
        //\\ #
        case AST_Percent_SEX:        //\\ P<Percent-SEX> =
          if (alt == 0) {            //\\   "system",
          } else if (alt == 1) {     //\\   "external",
          } else if (alt == 2) {     //\\   "dynamic",
          } else if (alt == 3) {     //\\   "prim",
          } else if (alt == 4) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_RECFMT_spec_OR_BODY://\\ P<RECFMT-spec-OR-BODY> =
          if (alt == 0) {            //\\   "spec" <NAME>,
          } else if (alt == 1) {     //\\   <NAME> '(' <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC> ')';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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?
        case AST_DECLN:              //\\ P<DECLN> =
          if (alt == 0) {            //\\   <OPT-arrayname> <OPT-spec> <NAME-or-STAR-68K> <OPT-68K-Bounds> <Opt-Init-assign> <Opt-Assign-NAME-LIST>,
          } else if (alt == 1) {     //\\   "array" <OPT-spec> <OPT-format> <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>,
          } else if (alt == 2) {     //\\   "array" <BPAIR> <OPT-spec> <OPT-format> <NAME> <Opt-NAME-LIST> <REST-OF-ARLIST-68K>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OWNDEC:             //\\ P<OWNDEC> =
          if (alt == 0) {            //\\   <OPT-arrayname> <OPT-spec> <SINGLE-OWNDEC> <REST-OF-OWNDEC>,
          } else if (alt == 1) {     //\\   "array" <OPT-format> <OPT-spec> <BPAIR> <NAME> <Opt-Init-assign-array>,
          } else if (alt == 2) {     //\\   "array" <OPT-format> <OPT-spec> <NAME> <BPAIR> <Opt-Init-assign-array>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_ARLIST:     //\\ P<REST-OF-ARLIST> =
          if (alt == 0) {            //\\   ',' <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_ARLIST_68K: //\\ P<REST-OF-ARLIST-68K> =
          if (alt == 0) {            //\\   ',' <NAME> <Opt-NAME-LIST> <REST-OF-ARLIST-68K>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_68K_Bounds:     //\\ P<OPT-68K-Bounds> =
          if (alt == 0) {            //\\   <BPAIR68K>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_format:         //\\ P<OPT-format> =
          if (alt == 0) {            //\\   "format",
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #
        //\\ #  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
        //\\ #
        case AST_NAME_or_STAR_68K:   //\\ P<NAME-or-STAR-68K> =
          if (alt == 0) {            //\\   '*',
          } else if (alt == 1) {     //\\   <NAME>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  <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...
        //\\ #
        case AST_Opt_Assign_NAME_LIST://\\ P<Opt-Assign-NAME-LIST> =
          if (alt == 0) {            //\\   ',' '*' <Opt-Assign-NAME-LIST>,
          } else if (alt == 1) {     //\\   ',' <NAME> <Opt-Init-assign> <Opt-Assign-NAME-LIST>,
          } else if (alt == 2) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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...
        //\\ #
        case AST_Opt_Init_assign:    //\\ P<Opt-Init-assign> =
          if (alt == 0) {            //\\   <ASSOP> <EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  removed string resolution from assop - too much type checking
        case AST_ASSOP:              //\\ P<ASSOP> =
          if (alt == 0) {            //\\   '=' '=',
          } else if (alt == 1) {     //\\   <SCALAR-ASSIGN>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  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
        case AST_Opt_Init_assign_array://\\ P<Opt-Init-assign-array> =
          if (alt == 0) {            //\\   <ASSOP> <stupid> <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR> <REPFACT> <ROCL>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  the version without "for" is to support an old-style statement, %WHILE <cond> %THEN <ui>
        case AST_Percent_WU:         //\\ P<Percent-WU> =
          if (alt == 0) {            //\\   "while" <CONDITION>,
          } else if (alt == 1) {     //\\   "until" <CONDITION>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Percent_WUF:        //\\ P<Percent-WUF> =
          if (alt == 0) {            //\\   "while" <CONDITION>,
          } else if (alt == 1) {     //\\   "until" <CONDITION>,
          } else if (alt == 2) {     //\\   "for" <NAME> <SCALAR-ASSIGN> <EXPR> ',' <EXPR> ',' <EXPR>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_SCALAR_ASSIGN:      //\\ P<SCALAR-ASSIGN> =
          if (alt == 0) {            //\\   '=',
          } else if (alt == 1) {     //\\   '<' '-';
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_UNTIL:          //\\ P<Opt-UNTIL> =
          if (alt == 0) {            //\\   "until" <CONDITION>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_Opt_CYCPARM:        //\\ P<Opt-CYCPARM> =
          if (alt == 0) {            //\\   <NAME> <SCALAR-ASSIGN> <EXPR> ',' <EXPR> ',' <EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  gt changed signal to allow 2 exprs.  Mouses Imp77 adds an optional string!
        //\\ #  68000: %signal 5,,holeend,"HEAPPUT: heap corrupt" %if holeend>lhb_limit
        case AST_OPEXPR2:            //\\ P<OPEXPR2> =
          if (alt == 0) {            //\\   ',' <OPT-EXPR> <OPEXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_OPT_EXPR:           //\\ P<OPT-EXPR> =
          if (alt == 0) {            //\\   <EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  single optional ", expr" for use with %signal event (, subevent)?
        case AST_OPEXPR:             //\\ P<OPEXPR> =
          if (alt == 0) {            //\\   ',' <OPT-EXPR> <OPSTRING>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  %signal 3,4,position,"Set input fails" %unless position=0
        case AST_OPSTRING:           //\\ P<OPSTRING> =
          if (alt == 0) {            //\\   ',' <STRING-EXPR>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  reversed conditions and 1-line cycles
        case AST_REST_OF_SS1:        //\\ P<REST-OF-SS1> =
          if (alt == 0) {            //\\   <Percent-IU> <CONDITION> <S>,
          } else if (alt == 1) {     //\\   <Percent-WUF> <S>,
          } else if (alt == 2) {     //\\   <S>,
          } else if (alt == 3) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_REST_OF_OWNDEC:     //\\ P<REST-OF-OWNDEC> =
          if (alt == 0) {            //\\   ',' <SINGLE-OWNDEC> <REST-OF-OWNDEC>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_SINGLE_OWNDEC:      //\\ P<SINGLE-OWNDEC> =
          if (alt == 0) {            //\\   <NAME> <Opt-ALIAS> <Opt-Init-assign>;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_ROCL:               //\\ P<ROCL> =
          if (alt == 0) {            //\\   ',' <stupid> <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR> <REPFACT> <ROCL>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST_stupid:             //\\ P<stupid> =
          if (alt == 0) {            //\\   <COMMENT>,
          } else if (alt == 1) {     //\\   ;
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        //\\ #  extrinsic is an old form meaning roughly "external spec"
        case AST_XOWN:               //\\ P<XOWN> =
          if (alt == 0) {            //\\   "own",
          } else if (alt == 1) {     //\\   "external",
          } else if (alt == 2) {     //\\   "extrinsic",
          } else if (alt == 3) {     //\\   "constant",
          } else if (alt == 4) {     //\\   "const";
          }
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;

        case AST__LIT:
          RECONSTITUTE(A[A_child[1]+A_literal_offset], stdout);
          break;

        case AST__KW:
          for (i = A_child[1]; i < A_child[1]+A_child[2]; i++) {
            // fprintf(stdout, "%s", String(i));
            RECONSTITUTE(i, stdout);
          }
          break;

        default:
          for (i = 1; i <= count; i++) compile(nthchild(astp,i));
          break;
      }
    }