#ifndef _GRAMMAR_H_
#define _GRAMMAR_H_ 1

#include <wchar.h>

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

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

typedef int (*parsefn)(void);
// Max number of phrases in any Alt: 0 (Reserved), 1:10

#define LARGEST_ALT 11

#define NEGATED_PHRASE (1U<<24U)
#define GUARD_PHRASE (1U<<25U)
#define WHITESPACE_ALLOWED (1U<<26U)
#define GRAMMAR_TYPE_SHIFT 27U
#define GRAMMAR_TYPE_MASK 31U
#define BIP_TYPE (1U <<27U)
#define PHRASE_TYPE (2U <<27U)
#define SEMANTIC_TYPE (3U <<27U)
#define KEYWORD_TYPE (4U <<27U)
#define CHAR_TYPE (5U <<27U)
#define UTF32CHAR_TYPE (6U <<27U)
#define STRING_TYPE (7U <<27U)
#define UTF32STRING_TYPE (8U <<27U)
#define REGEXP_TYPE (9U <<27U)
#define OPTION_TYPE (10U <<27U)
#define COUNT_OF_ALTS (11U <<27U)
#define COUNT_OF_PHRASES (12U <<27U)
#define ALT_NUMBER (13U <<27U)
#define INDEX_MASK 0x7FFFFFU
// (We have room for types 1..31U)

#define PhraseType(idx) ((((idx)>>27U)&31U))


#define BIP_BASE 0
#define PHRASE_BASE 3
#define SEMANTIC_BASE 151
#define AST_BASE 160

#define NUM_BIPS 3
#define NUM_SIMPLE_PHRASES 148
#define NUM_SEMANTIC_PHRASES 9
#define NUM_PHRASES (NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES)

#define NUM_KEYWORDS 62
#define NUM_REGEXPS 9
#define NUM_GRAMMAR 1085

#define B_eof 0
#define B_ch 1
#define B_nl 2
#define P_PUSHDECS 3
#define P_POPDECS 4
#define P_INITDECS 5
#define P_nls 6
#define P_READLINEP 7
#define P_S 8
#define P_DOWN 9
#define P_UP 10
#define P_SS 11
#define P_BINOP 12
#define P_OP 13
#define P_MONOP 14
#define P_UOP 15
#define P_ASSOP 16
#define P_COMP1 17
#define P_COMP2 18
#define P_VARNAME 19
#define P_TAG 20
#define P_BigCharConst 21
#define P_CharConst 22
#define P_OptExponent 23
#define P_OptDecimal 24
#define P_Based 25
#define P_LITCONST 26
#define P_CONST 27
#define P_N 28
#define P_dq 29
#define P_any 30
#define P_stringchars 31
#define P_ALIASTEXT 32
#define P_semi 33
#define P_Comment 34
#define P_TEXT 35
#define P_Opt_Record_Field 36
#define P_VAR 37
#define P_OPERAND 38
#define P_CONSTVAR 39
#define P_COPERAND 40
#define P_CEXPR 41
#define P_CTORP 42
#define P_STAROREXPR 43
#define P_TORP 44
#define P_EXPR 45
#define P_RESTOFEXPR 46
#define P_RESTOFCEXPR 47
#define P_APP 48
#define P_RESTOFAPP 49
#define P_PC_IU 50
#define P_TOP_LEVEL_CONDITION 51
#define P_CYCPARM 52
#define P_PC_WUF 53
#define P_ALIAS 54
#define P_DECLNAMES 55
#define P_DECLNAME 56
#define P_FULLTYPE 57
#define P_BTYPE 58
#define P_STRLEN 59
#define P_XTYPE 60
#define P_RT 61
#define P_FM 62
#define P_FP 63
#define P_FPDEL 64
#define P_Opt_N_type 65
#define P_Opt_AN_N_type 66
#define P_FPP 67
#define P_Opt_comma 68
#define P_RESTOFFPLIST 69
#define P_ENDLIST 70
#define P_ARRAYFORMAT 71
#define P_RESTOFSC 72
#define P_SC 73
#define P_RESTOFCOND 74
#define P_RESTOFANDC 75
#define P_RESTOFORC 76
#define P_Opt_RtFnMapSpec 77
#define P_VSPECQ 78
#define P_RESTOFBPLIST 79
#define P_BPAIR 80
#define P_RESTOFARLIST 81
#define P_ADECLN 82
#define P_DECLN 83
#define P_OWNDEC 84
#define P_RESTOFOWNDEC 85
#define P_XOWN 86
#define P_Opt_Const_Init 87
#define P_REPFACT 88
#define P_CONSTLISTITEM 89
#define P_CONSTLIST 90
#define P_ROCL 91
#define P_on_event_block 92
#define P_RESTOFNLIST 93
#define P_PC_EVENTQ 94
#define P_Opt_EXPR 95
#define P_RESTOFREPEAT 96
#define P_FINISHELSEQ 97
#define P_AFTERELSE 98
#define P_ELSEQ 99
#define P_RESTOFIU 100
#define P_SEX 101
#define P_RFSTMNT 102
#define P_RFREF 103
#define P_RESTOFRFDEC 104
#define P_RFDEC 105
#define P_RFELMNT 106
#define P_ALTRFDEC 107
#define P_ASM 108
#define P_Call_or_Assign_AUI 109
#define P_SWITCHIDX 110
#define P_Goto 111
#define P_return 112
#define P_result 113
#define P_monitor_AUI 114
#define P_stop 115
#define P_signal 116
#define P_exit 117
#define P_continue 118
#define P_Unconditional_Instructions 119
#define P_UI 120
#define P_AUI 121
#define P_UI_BLOCK 122
#define P_NONFINAL_UI 123
#define P_FINAL_UI 124
#define P_More_STATEMENTS 125
#define P_STATEMENTS 126
#define P_Labels 127
#define P_If_or_Unless 128
#define P_Opt_CYCPARM 129
#define P_start_of_cycle 130
#define P_end_of_cycle 131
#define P_regular_declaration 132
#define P_proc_declaration 133
#define P_own_or_external_declaration 134
#define P_record_format_declaration 135
#define P_various_ends 136
#define P_include_file 137
#define P_begin_block 138
#define P_ONESWDECL 139
#define P_RESTOFSWLIST 140
#define P_switch_declaration 141
#define P_list_on 142
#define P_else 143
#define P_finish_opt_else 144
#define P_embedded_assembler 145
#define P_trusted 146
#define P_mainep 147
#define P_control 148
#define P_PSEMI 149
#define P_STATEMENT 150
#define S_init 0
#define S_terminate 1
#define S_whitespace 2
#define S_Imp77_stropping 3
#define S_COLON 4
#define S_UNDO_COLON 5
#define S_INCLUDE 6
#define S_LISTON 7
#define S_LISTOFF 8

extern const int bip_map[NUM_BIPS];
extern const int sequential_phrase_no_to_grammar_index[NUM_SIMPLE_PHRASES];
extern const wchar_t *phrasename[NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES];

extern const wchar_t *semantic_phrasename[NUM_SEMANTIC_PHRASES];
extern const wchar_t *semantic_code[NUM_SEMANTIC_PHRASES];
extern const wchar_t *ast_code[NUM_SIMPLE_PHRASES];
extern const wchar_t *xcomment[NUM_PHRASES];
extern const wchar_t *keyword[NUM_KEYWORDS];
extern const wchar_t *regexps[NUM_REGEXPS];

extern const int gram[NUM_GRAMMAR];
#define G_PUSHDECS 0
#define G_POPDECS 3
#define G_INITDECS 5
#define G_nls 7
#define G_READLINEP 12
#define G_S 16
#define G_DOWN 21
#define G_UP 23
#define G_SS 25
#define G_BINOP 31
#define G_OP 76
#define G_MONOP 79
#define G_UOP 89
#define G_ASSOP 92
#define G_COMP1 104
#define G_COMP2 138
#define G_VARNAME 141
#define G_TAG 145
#define G_BigCharConst 149
#define G_CharConst 152
#define G_OptExponent 159
#define G_OptDecimal 168
#define G_Based 175
#define G_LITCONST 178
#define G_CONST 201
#define G_N 204
#define G_dq 207
#define G_any 210
#define G_stringchars 213
#define G_ALIASTEXT 223
#define G_semi 228
#define G_Comment 231
#define G_TEXT 241
#define G_Opt_Record_Field 247
#define G_VAR 254
#define G_OPERAND 259
#define G_CONSTVAR 268
#define G_COPERAND 271
#define G_CEXPR 280
#define G_CTORP 283
#define G_STAROREXPR 288
#define G_TORP 295
#define G_EXPR 300
#define G_RESTOFEXPR 303
#define G_RESTOFCEXPR 309
#define G_APP 315
#define G_RESTOFAPP 322
#define G_PC_IU 329
#define G_TOP_LEVEL_CONDITION 334
#define G_CYCPARM 338
#define G_PC_WUF 347
#define G_ALIAS 357
#define G_DECLNAMES 362
#define G_DECLNAME 369
#define G_FULLTYPE 373
#define G_BTYPE 377
#define G_STRLEN 382
#define G_XTYPE 388
#define G_RT 417
#define G_FM 423
#define G_FP 430
#define G_FPDEL 435
#define G_Opt_N_type 451
#define G_Opt_AN_N_type 455
#define G_FPP 462
#define G_Opt_comma 469
#define G_RESTOFFPLIST 473
#define G_ENDLIST 480
#define G_ARRAYFORMAT 493
#define G_RESTOFSC 497
#define G_SC 502
#define G_RESTOFCOND 516
#define G_RESTOFANDC 526
#define G_RESTOFORC 532
#define G_Opt_RtFnMapSpec 538
#define G_VSPECQ 542
#define G_RESTOFBPLIST 546
#define G_BPAIR 554
#define G_RESTOFARLIST 562
#define G_ADECLN 567
#define G_DECLN 573
#define G_OWNDEC 582
#define G_RESTOFOWNDEC 597
#define G_XOWN 606
#define G_Opt_Const_Init 617
#define G_REPFACT 624
#define G_CONSTLISTITEM 630
#define G_CONSTLIST 636
#define G_ROCL 643
#define G_on_event_block 650
#define G_RESTOFNLIST 658
#define G_PC_EVENTQ 664
#define G_Opt_EXPR 668
#define G_RESTOFREPEAT 673
#define G_FINISHELSEQ 678
#define G_AFTERELSE 683
#define G_ELSEQ 692
#define G_RESTOFIU 697
#define G_SEX 707
#define G_RFSTMNT 719
#define G_RFREF 732
#define G_RESTOFRFDEC 739
#define G_RFDEC 746
#define G_RFELMNT 757
#define G_ALTRFDEC 765
#define G_ASM 772
#define G_Call_or_Assign_AUI 780
#define G_SWITCHIDX 787
#define G_Goto 793
#define G_return 799
#define G_result 802
#define G_monitor_AUI 807
#define G_stop 810
#define G_signal 813
#define G_exit 819
#define G_continue 822
#define G_Unconditional_Instructions 825
#define G_UI 838
#define G_AUI 846
#define G_UI_BLOCK 850
#define G_NONFINAL_UI 858
#define G_FINAL_UI 863
#define G_More_STATEMENTS 878
#define G_STATEMENTS 883
#define G_Labels 888
#define G_If_or_Unless 907
#define G_Opt_CYCPARM 913
#define G_start_of_cycle 917
#define G_end_of_cycle 926
#define G_regular_declaration 931
#define G_proc_declaration 937
#define G_own_or_external_declaration 947
#define G_record_format_declaration 954
#define G_various_ends 961
#define G_include_file 966
#define G_begin_block 971
#define G_ONESWDECL 976
#define G_RESTOFSWLIST 986
#define G_switch_declaration 992
#define G_list_on 998
#define G_else 1003
#define G_finish_opt_else 1007
#define G_embedded_assembler 1012
#define G_trusted 1017
#define G_mainep 1021
#define G_control 1026
#define G_PSEMI 1031
#define G_STATEMENT 1033

extern parsefn parsetime[NUM_SEMANTIC_PHRASES];
extern int parse_init(void);
extern int parse_terminate(void);
extern int parse_whitespace(void);
extern int parse_Imp77_stropping(void);
extern int parse_COLON(void);
extern int parse_UNDO_COLON(void);
extern int parse_INCLUDE(void);
extern int parse_LISTON(void);
extern int parse_LISTOFF(void);

#ifndef SUPPRESS_DATA
const wchar_t *phrasename[NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES] = {
  L"eof" ,
  L"ch" ,
  L"nl" ,
  L"PUSHDECS",
  L"POPDECS",
  L"INITDECS",
  L"nls",
  L"READLINEP",
  L"S",
  L"DOWN",
  L"UP",
  L"SS",
  L"BINOP",
  L"OP",
  L"MONOP",
  L"UOP",
  L"ASSOP",
  L"COMP1",
  L"COMP2",
  L"VARNAME",
  L"TAG",
  L"BigCharConst",
  L"CharConst",
  L"OptExponent",
  L"OptDecimal",
  L"Based",
  L"LITCONST",
  L"CONST",
  L"N",
  L"dq",
  L"any",
  L"stringchars",
  L"ALIASTEXT",
  L"semi",
  L"Comment",
  L"TEXT",
  L"Opt_Record_Field",
  L"VAR",
  L"OPERAND",
  L"CONSTVAR",
  L"COPERAND",
  L"CEXPR",
  L"CTORP",
  L"STAROREXPR",
  L"TORP",
  L"EXPR",
  L"RESTOFEXPR",
  L"RESTOFCEXPR",
  L"APP",
  L"RESTOFAPP",
  L"PC_IU",
  L"TOP_LEVEL_CONDITION",
  L"CYCPARM",
  L"PC_WUF",
  L"ALIAS",
  L"DECLNAMES",
  L"DECLNAME",
  L"FULLTYPE",
  L"BTYPE",
  L"STRLEN",
  L"XTYPE",
  L"RT",
  L"FM",
  L"FP",
  L"FPDEL",
  L"Opt_N_type",
  L"Opt_AN_N_type",
  L"FPP",
  L"Opt_comma",
  L"RESTOFFPLIST",
  L"ENDLIST",
  L"ARRAYFORMAT",
  L"RESTOFSC",
  L"SC",
  L"RESTOFCOND",
  L"RESTOFANDC",
  L"RESTOFORC",
  L"Opt_RtFnMapSpec",
  L"VSPECQ",
  L"RESTOFBPLIST",
  L"BPAIR",
  L"RESTOFARLIST",
  L"ADECLN",
  L"DECLN",
  L"OWNDEC",
  L"RESTOFOWNDEC",
  L"XOWN",
  L"Opt_Const_Init",
  L"REPFACT",
  L"CONSTLISTITEM",
  L"CONSTLIST",
  L"ROCL",
  L"on_event_block",
  L"RESTOFNLIST",
  L"PC_EVENTQ",
  L"Opt_EXPR",
  L"RESTOFREPEAT",
  L"FINISHELSEQ",
  L"AFTERELSE",
  L"ELSEQ",
  L"RESTOFIU",
  L"SEX",
  L"RFSTMNT",
  L"RFREF",
  L"RESTOFRFDEC",
  L"RFDEC",
  L"RFELMNT",
  L"ALTRFDEC",
  L"ASM",
  L"Call_or_Assign_AUI",
  L"SWITCHIDX",
  L"Goto",
  L"return",
  L"result",
  L"monitor_AUI",
  L"stop",
  L"signal",
  L"exit",
  L"continue",
  L"Unconditional_Instructions",
  L"UI",
  L"AUI",
  L"UI_BLOCK",
  L"NONFINAL_UI",
  L"FINAL_UI",
  L"More_STATEMENTS",
  L"STATEMENTS",
  L"Labels",
  L"If_or_Unless",
  L"Opt_CYCPARM",
  L"start_of_cycle",
  L"end_of_cycle",
  L"regular_declaration",
  L"proc_declaration",
  L"own_or_external_declaration",
  L"record_format_declaration",
  L"various_ends",
  L"include_file",
  L"begin_block",
  L"ONESWDECL",
  L"RESTOFSWLIST",
  L"switch_declaration",
  L"list_on",
  L"else",
  L"finish_opt_else",
  L"embedded_assembler",
  L"trusted",
  L"mainep",
  L"control",
  L"PSEMI",
  L"STATEMENT",
  L"init",
  L"terminate",
  L"whitespace",
  L"Imp77_stropping",
  L"COLON",
  L"UNDO-COLON",
  L"INCLUDE",
  L"LISTON",
  L"LISTOFF",
};
const wchar_t *phrasename_c[NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES] = {
  L"eof" ,
  L"ch" ,
  L"nl" ,
  L"PUSHDECS",
  L"POPDECS",
  L"INITDECS",
  L"nls",
  L"READLINEP",
  L"S",
  L"DOWN",
  L"UP",
  L"SS",
  L"BINOP",
  L"OP",
  L"MONOP",
  L"UOP",
  L"ASSOP",
  L"COMP1",
  L"COMP2",
  L"VARNAME",
  L"TAG",
  L"BigCharConst",
  L"CharConst",
  L"OptExponent",
  L"OptDecimal",
  L"Based",
  L"LITCONST",
  L"CONST",
  L"N",
  L"dq",
  L"any",
  L"stringchars",
  L"ALIASTEXT",
  L"semi",
  L"Comment",
  L"TEXT",
  L"Opt_Record_Field",
  L"VAR",
  L"OPERAND",
  L"CONSTVAR",
  L"COPERAND",
  L"CEXPR",
  L"CTORP",
  L"STAROREXPR",
  L"TORP",
  L"EXPR",
  L"RESTOFEXPR",
  L"RESTOFCEXPR",
  L"APP",
  L"RESTOFAPP",
  L"PC_IU",
  L"TOP_LEVEL_CONDITION",
  L"CYCPARM",
  L"PC_WUF",
  L"ALIAS",
  L"DECLNAMES",
  L"DECLNAME",
  L"FULLTYPE",
  L"BTYPE",
  L"STRLEN",
  L"XTYPE",
  L"RT",
  L"FM",
  L"FP",
  L"FPDEL",
  L"Opt_N_type",
  L"Opt_AN_N_type",
  L"FPP",
  L"Opt_comma",
  L"RESTOFFPLIST",
  L"ENDLIST",
  L"ARRAYFORMAT",
  L"RESTOFSC",
  L"SC",
  L"RESTOFCOND",
  L"RESTOFANDC",
  L"RESTOFORC",
  L"Opt_RtFnMapSpec",
  L"VSPECQ",
  L"RESTOFBPLIST",
  L"BPAIR",
  L"RESTOFARLIST",
  L"ADECLN",
  L"DECLN",
  L"OWNDEC",
  L"RESTOFOWNDEC",
  L"XOWN",
  L"Opt_Const_Init",
  L"REPFACT",
  L"CONSTLISTITEM",
  L"CONSTLIST",
  L"ROCL",
  L"on_event_block",
  L"RESTOFNLIST",
  L"PC_EVENTQ",
  L"Opt_EXPR",
  L"RESTOFREPEAT",
  L"FINISHELSEQ",
  L"AFTERELSE",
  L"ELSEQ",
  L"RESTOFIU",
  L"SEX",
  L"RFSTMNT",
  L"RFREF",
  L"RESTOFRFDEC",
  L"RFDEC",
  L"RFELMNT",
  L"ALTRFDEC",
  L"ASM",
  L"Call_or_Assign_AUI",
  L"SWITCHIDX",
  L"Goto",
  L"return",
  L"result",
  L"monitor_AUI",
  L"stop",
  L"signal",
  L"exit",
  L"continue",
  L"Unconditional_Instructions",
  L"UI",
  L"AUI",
  L"UI_BLOCK",
  L"NONFINAL_UI",
  L"FINAL_UI",
  L"More_STATEMENTS",
  L"STATEMENTS",
  L"Labels",
  L"If_or_Unless",
  L"Opt_CYCPARM",
  L"start_of_cycle",
  L"end_of_cycle",
  L"regular_declaration",
  L"proc_declaration",
  L"own_or_external_declaration",
  L"record_format_declaration",
  L"various_ends",
  L"include_file",
  L"begin_block",
  L"ONESWDECL",
  L"RESTOFSWLIST",
  L"switch_declaration",
  L"list_on",
  L"else",
  L"finish_opt_else",
  L"embedded_assembler",
  L"trusted",
  L"mainep",
  L"control",
  L"PSEMI",
  L"STATEMENT",
  L"init",
  L"terminate",
  L"whitespace",
  L"Imp77_stropping",
  L"COLON",
  L"UNDO_COLON",
  L"INCLUDE",
  L"LISTON",
  L"LISTOFF",
};
const int bip_map[NUM_BIPS] = {
  0,
  1,
  2,
};
const int sequential_phrase_no_to_grammar_index[NUM_SIMPLE_PHRASES] = {
  G_PUSHDECS,
  G_POPDECS,
  G_INITDECS,
  G_nls,
  G_READLINEP,
  G_S,
  G_DOWN,
  G_UP,
  G_SS,
  G_BINOP,
  G_OP,
  G_MONOP,
  G_UOP,
  G_ASSOP,
  G_COMP1,
  G_COMP2,
  G_VARNAME,
  G_TAG,
  G_BigCharConst,
  G_CharConst,
  G_OptExponent,
  G_OptDecimal,
  G_Based,
  G_LITCONST,
  G_CONST,
  G_N,
  G_dq,
  G_any,
  G_stringchars,
  G_ALIASTEXT,
  G_semi,
  G_Comment,
  G_TEXT,
  G_Opt_Record_Field,
  G_VAR,
  G_OPERAND,
  G_CONSTVAR,
  G_COPERAND,
  G_CEXPR,
  G_CTORP,
  G_STAROREXPR,
  G_TORP,
  G_EXPR,
  G_RESTOFEXPR,
  G_RESTOFCEXPR,
  G_APP,
  G_RESTOFAPP,
  G_PC_IU,
  G_TOP_LEVEL_CONDITION,
  G_CYCPARM,
  G_PC_WUF,
  G_ALIAS,
  G_DECLNAMES,
  G_DECLNAME,
  G_FULLTYPE,
  G_BTYPE,
  G_STRLEN,
  G_XTYPE,
  G_RT,
  G_FM,
  G_FP,
  G_FPDEL,
  G_Opt_N_type,
  G_Opt_AN_N_type,
  G_FPP,
  G_Opt_comma,
  G_RESTOFFPLIST,
  G_ENDLIST,
  G_ARRAYFORMAT,
  G_RESTOFSC,
  G_SC,
  G_RESTOFCOND,
  G_RESTOFANDC,
  G_RESTOFORC,
  G_Opt_RtFnMapSpec,
  G_VSPECQ,
  G_RESTOFBPLIST,
  G_BPAIR,
  G_RESTOFARLIST,
  G_ADECLN,
  G_DECLN,
  G_OWNDEC,
  G_RESTOFOWNDEC,
  G_XOWN,
  G_Opt_Const_Init,
  G_REPFACT,
  G_CONSTLISTITEM,
  G_CONSTLIST,
  G_ROCL,
  G_on_event_block,
  G_RESTOFNLIST,
  G_PC_EVENTQ,
  G_Opt_EXPR,
  G_RESTOFREPEAT,
  G_FINISHELSEQ,
  G_AFTERELSE,
  G_ELSEQ,
  G_RESTOFIU,
  G_SEX,
  G_RFSTMNT,
  G_RFREF,
  G_RESTOFRFDEC,
  G_RFDEC,
  G_RFELMNT,
  G_ALTRFDEC,
  G_ASM,
  G_Call_or_Assign_AUI,
  G_SWITCHIDX,
  G_Goto,
  G_return,
  G_result,
  G_monitor_AUI,
  G_stop,
  G_signal,
  G_exit,
  G_continue,
  G_Unconditional_Instructions,
  G_UI,
  G_AUI,
  G_UI_BLOCK,
  G_NONFINAL_UI,
  G_FINAL_UI,
  G_More_STATEMENTS,
  G_STATEMENTS,
  G_Labels,
  G_If_or_Unless,
  G_Opt_CYCPARM,
  G_start_of_cycle,
  G_end_of_cycle,
  G_regular_declaration,
  G_proc_declaration,
  G_own_or_external_declaration,
  G_record_format_declaration,
  G_various_ends,
  G_include_file,
  G_begin_block,
  G_ONESWDECL,
  G_RESTOFSWLIST,
  G_switch_declaration,
  G_list_on,
  G_else,
  G_finish_opt_else,
  G_embedded_assembler,
  G_trusted,
  G_mainep,
  G_control,
  G_PSEMI,
  G_STATEMENT,
};

const wchar_t *semantic_phrasename[NUM_SEMANTIC_PHRASES] = {
  L"init",
  L"terminate",
  L"whitespace",
  L"Imp77_stropping",
  L"COLON",
  L"UNDO-COLON",
  L"INCLUDE",
  L"LISTON",
  L"LISTOFF",
};

const wchar_t *semantic_code[NUM_SEMANTIC_PHRASES] = {
  L"\n"
             "#ifdef IN_PARSER // regen etc use this too but don't need a lot of what uparse.c needs.\n"
             "  // perform any initialisation required by the parse-time semantic routines.\n"
             "  // Note that for now, we have no way of declaring data outside of\n"
             "  // those procedures.  Obviously this will have to change.\n"
             "\n"
             "  // LINE RECONSTRUCTION *might* GO HERE.  But probably not.\n"
             "  \n"
             "  // initially default scope is at the level appropriate for perms\n"
             "  \n"
             "  debug_scope = 0;   // 2; // excessive debugging\n"
             "  debug_ast = 0;\n"
             "  debug_ctrl_stack = 0;\n"
             "  debug_compiler = 0;\n"
             "  debug_declarations = 0;\n"
             "  debug_torp = 0;\n"
             "  debug_ast_creation = 0;\n"
             "  \n"
             "  push_scope_level();\n"
             "  // Declare perms, prims here\n"
             "  //add_entry(\"decl\", \"NEWLINE\", 42); // param can be anything. Usually index into an array of records of the appropriate type.  During initial code creation, we'll just use random tags.\n"
             "  // initial top-level file-level scope, eg for %externalroutines\n"
             "  //push_scope_level();\n"
             "  // ready for externals\n"
             "\n"
             "  //codegen(\"\\n// Relies on using gtcpp to preprocess before cleaning up with clang-format\");\n"
             "  codegen(\"#include <imp_perms.h>\\n\");\n"
             "  codegen(\"\\n$define unless(cond) if (!(cond)) \\n$define until(cond) while(!(cond))\\n\");\n"
             "\n"
             "#ifdef NEVER // moved to <imp_perms.h>\n"
             "  codegen(\"#define goto_switch(S, N) \\\\\\n\");\n"
             "  codegen(\"  do { \\\\\\n\");\n"
             "  codegen(\"    S ## _sw = N; \\\\\\n\");\n"
             "  codegen(\"    _sw_line = __LINE__; \\\\\\n\");\n"
             "  codegen(\"    _sw_file = __FILE__; \\\\\\n\");\n"
             "  codegen(\"    if (S ## _low <= S ## _sw && S ## _sw <= S ## _high) \\\\\\n\");\n"
             "  codegen(\"      goto *S[S ## _sw - S ## _low]; \\\\\\n\");\n"
             "  codegen(\"    else \\\\\\n\");\n"
             "  codegen(\"      goto S ## _default; \\\\\\n\");\n"
             "  codegen(\"  } while (0)\\n\");\n"
             "\n"
             "  codegen(\"#define declare_switch(S, low, high) \\\\\\n\");\n"
             "  codegen(\"static int S ## _sw; \\\\\\n\");\n"
             "  codegen(\"const int S ## _low = low, S ## _high = high; \\\\\\n\");\n"
             "  codegen(\"static void **S; \\\\\\n\");\n"
             "  codegen(\"auto void init_ ## S(void); init_ ## S();\\n\");\n"
             "#endif\n"
             "\n"
             "#endif\n"
             "  return TRUE;\n",
  L"\n"
             "#ifdef IN_PARSER\n"
             "  // perform any final tidy-up required by the parse-time semantic routines.\n"
             "\n"
             "  //pop_scope_level(); // from externals back to perms\n"
             "  //pop_scope_level(); // from perms to none.\n"
             "#endif\n"
             "  return TRUE;\n",
  L"\n"
             "#ifdef IN_PARSER\n"
             "  while (source(TP).ch==' ' || source(TP).ch=='\\t' || source(TP).ch=='\\f') {\n"
             "    TP += 1;\n"
             "  }\n"
             "#endif\n"
             "  return TRUE;\n",
  L"\n"
             "#ifdef IN_PARSER\n"
             "  int debug_stropping = 0;\n"
             "\n"
             "  // TO DO: initial spaces in he file before the initial % seem to cause a failure.\n"
             "\n"
             "  // The source file has already been read trivially into source().\n"
             "  \n"
             "  // We will copy from source() into temp(), then perform line reconstruction\n"
             "  // on temp(), writing back to source().  The parser will then parse source()\n"
             "  // into atoms according to the grammar.  Initially it will only store the\n"
             "  // reconstructed characters into the atoms, but once it is working, I will\n"
             "  // modify it to also store the unreconsructed source for use in source-to-source\n"
             "  // translations, where whitespace, embedded comments, and indentation is\n"
             "  // desired in the translation, in order to mirror the original file.\n"
             "\n"
             "  // Because unfortunately underlining in Unicode is done by a *following*\n"
             "  // underline joiner character (818) rather than being a single unicode\n"
             "  // code point, it is difficult to use a single-character encoding of a\n"
             "  // stropped keyword letter - what the old Imp compilers would represent\n"
             "  // by adding 128 to the character.  However there *is* an alternive\n"
             "  // source of upper case and lower case letters in the mathematics area!\n"
             "\n"
             "  // A:Z could be encoded as 1D400:1D419 and a:z as 1D41A:1D433 :-)\n"
             "  // but for now I'm encoding keywords in lower case and variables in\n"
             "  // upper case.\n"
             "  \n"
             "  // The 1D400+ encoding looks more or less like ordinary text if it happens\n"
             "  // to be displayed (e.g. during debugging) although there should never be\n"
             "  // any need to display internally-coded keywords to users of the\n"
             "  // compilers built with this parser.\n"
             "\n"
             "  // All arrays are flex and the upper bound is a limit, not a minimum.\n"
             "  DECLARE(SYM, reconstructed, 128000000/*600000*/);\n"
             "#define _SYM(x) WRITE(x,SYM,reconstructed)\n"
             "#define  SYM(x)  READ(x,SYM,reconstructed)\n"
             "\n"
             "  int LASTP, P = 0;\n"
             "  while (source(P).ch != 0 /* WEOF */) {\n"
             "    _SYM(P).ch = source(P).ch;\n"
             "    _SYM(P).start = P; _SYM(P).end = P+1;\n"
             "    P += 1;\n"
             "  }\n"
             "  _SYM(P).ch = 0 /* WEOF */;\n"
             "  _SYM(P).start = P; _SYM(P).end = P; // no chars for EOF\n"
             "  LASTP = P;\n"
             "  \n"
             "  if (debug_stropping) {\n"
             "    int I;\n"
             "    fprintf(stderr, \"source() moved to SYM(0:%d) = \\\"\", LASTP);\n"
             "    for (I = 0; I < LASTP; I++) {\n"
             "      /*if (SYM(I).ch != 0)*/ fprintf(stderr, \"%lc\", SYM(I).ch);\n"
             "    }\n"
             "    if (SYM(LASTP).ch != 0) fprintf(stderr, \"[%d]\", SYM(LASTP).ch);\n"
             "    fprintf(stderr, \"\\\";\\n\");\n"
             "  };\n"
             "\n"
             "  int FP = 0, PP = 0; // Fetch Pointer, Put Pointer.\n"
             "\n"
             "#define DONE() \\\n"
             "        do {                                                                      \\\n"
             "            FP -= 1; /* the terminating 0*/                                       \\\n"
             "            _source(PP).ch = 0;                                                   \\\n"
             "            _source(PP).end = SYM(FP).end;                                        \\\n"
             "            if (debug_stropping) {                                                \\\n"
             "              int I;                                                              \\\n"
             "              fprintf(stderr, \"SYM(0:%d) moved to source(0:%d) = \\\"\", FP, PP);    \\\n"
             "              for (I = 0; I < PP; I++) {                                          \\\n"
             "                if (source(I).ch != 0) fprintf(stderr, \"%lc\", source(I).ch);      \\\n"
             "              }                                                                   \\\n"
             "              if (source(PP).ch != 0) fprintf(stderr, \"[%d]\", source(PP).ch);     \\\n"
             "              fprintf(stderr, \"\\\";\\n\");                                           \\\n"
             "            }                                                                     \\\n"
             "            return TRUE;                                                          \\\n"
             "        } while (0)\n"
             "\n"
             "  wint_t WC;\n"
             "\n"
             "  // NOTE THAT WITH THIS IMP77 GRAMMAR, '\\n' IS NOT WHITESPACE.  LINE ENDINGS ARE EXPLICITLY\n"
             "  // ENTERED IN THE GRAMMAR.  (See the phrases <T>, and <NL_opt>.\n"
             "  \n"
             "  // uparse.c has been modified so that its implicit whitespace skipping no longer skips '\\n'.\n"
             "  // (The algol60 parser in contrast treats all \\n's the same as spaces)\n"
             "  \n"
             "  // HOW TO HANDLE ' IN A PARSED COMMENT?\n"
             "  //\n"
             "  // %COMMENT A ' MESSES UP!\n"
             "  //\n"
             "  // because it keeps scanning until a closing quote.  However if you don't scan between quotes,\n"
             "  // line reconstruction will lose spaces within strings!\n"
             "  //\n"
             "  // You can't just end a quoted string at a newline because embedded newlines are allowed.\n"
             "  // And I checked Imp77 - it allows a single quote ch in a comment.\n"
             "\n"
             "  // If line reconstruction were being done on the fly then it could be modified if we knew we were\n"
             "  // in a comment, but since we're doing it all in advance, the only option to handle this appears\n"
             "  // to be that whenever we're in a comment, we throw away all the following line reconstruction and\n"
             "  // re-do it, with that comment handled differently.\n"
             "\n"
             "  // Or bite the bullet and work out how to do line reconstruction on the fly (which my previous\n"
             "  // imptoc did eventually manage using the 'demandload' call. So *every* fetch via TP would have\n"
             "  // to be recoded as a procedure call, with on-the-fly line reconstruction, and either a way to\n"
             "  // undo it if backtracking or simply never doing it any farther past TP and undoing it on backtracking.\n"
             "\n"
             "  // What a can of worms just to handle badly designed comments.  TO DO.\n"
             "\n"
             "#define CHECK_EOF(x) do if ((x) == 0) DONE(); else { _source(PP).end = SYM(FP-1).end; } while (0)\n"
             "\n"
             "  // PP is the 'current' slot we are writing into.\n"
             "  _source(PP).start = SYM(FP).start;\n"
             "\n"
             "  for (;;) {\n"
             "\n"
             "    _source(PP).end = SYM(FP).end; // Keep updated.\n"
             "    WC = SYM(FP++).ch; CHECK_EOF(WC);\n"
             "\n"
             "    if (WC == '%') { // We found a keyword.  It will always be read up to the last character of the keyword.\n"
             "\n"
             "      for (;;) {\n"
             "        WC = SYM(FP++).ch; CHECK_EOF(WC);\n"
             "        if (WC == '%') {\n"
             "\n"
             "        } else if (!isalpha(WC)) {\n"
             "          // It's possible to have a bunch of '%' signs and *no* keyword characters.\n"
             "          --FP; // point FP back to the non-keyword character, not as currently, the one past that.\n"
             "          break;    \n"
             "        } else { // isalpha(WC)\n"
             "          if (isupper(WC)) WC = tolower(WC);\n"
             "          _source(PP).end = SYM(FP-1).end;\n"
             "          _source(PP++).ch = WC; // | 128\n"
             "          _source(PP).start = SYM(FP).start;\n"
             "        }\n"
             "      }\n"
             "      continue;\n"
             "    }\n"
             "\n"
             "    else if (WC == '{') {\n"
             "\n"
             "      for (;;) {\n"
             "        WC = SYM(FP++).ch; CHECK_EOF(WC);\n"
             "\n"
             "        if (WC == '\\n') {\n"
             "          --FP; /* re-read the \\n as a significant character */\n"
             "          // _source(PP).end = SYM(FP-1).end; // point FP back to the newline\n"
             "          break;\n"
             "        }\n"
             "        if (WC == '}') {  // Not sure if \\n should be gobbled for {this style\n"
             "          break; // but still looking.\n"
             "        }\n"
             "      }\n"
             "      continue;\n"
             "    }\n"
             "\n"
             "    else if (WC == '\\'') {\n"
             "      _source(PP++).ch = WC;\n"
             "      for (;;) {\n"
             "        WC = SYM(FP++).ch; CHECK_EOF(WC);\n"
             "        if (WC == '\\'') {\n"
             "          // peek ahead:\n"
             "          int Peek = SYM(FP).ch; CHECK_EOF(Peek);\n"
             "          if (Peek == '\\'') { // doubled 's\n"
             "            _source(PP++).ch = WC;\n"
             "            _source(PP++).ch = Peek;\n"
             "            FP++;\n"
             "          } else {\n"
             "            _source(PP).ch = WC;\n"
             "            _source(PP).end = SYM(FP-1).end; // Leave Peek for later.\n"
             "            PP++;\n"
             "            break;\n"
             "          }\n"
             "        } else {\n"
             "          _source(PP++).ch = WC;\n"
             "        }\n"
             "      }\n"
             "      continue;\n"
             "    }\n"
             "\n"
             "    else if (WC == '\"') { // TO DO: Update ' and \" items in imp77 as well\n"
             "      _source(PP++).ch = WC;\n"
             "      for (;;) {\n"
             "        WC = SYM(FP++).ch; CHECK_EOF(WC);\n"
             "        if (WC == '\"') {\n"
             "          // peek ahead:\n"
             "          int Peek = SYM(FP).ch; CHECK_EOF(Peek);\n"
             "          if (Peek == '\"') { // doubled \"s\n"
             "            _source(PP++).ch = WC;\n"
             "            _source(PP++).ch = Peek;\n"
             "            FP++;\n"
             "          } else {\n"
             "            _source(PP).ch = WC;\n"
             "            _source(PP).end = SYM(FP-1).end; // Leave Peek for later.\n"
             "            PP++;\n"
             "            break;\n"
             "          }\n"
             "        } else {\n"
             "          _source(PP++).ch = WC;\n"
             "        }\n"
             "      }\n"
             "      continue;\n"
             "    }\n"
             "\n"
             "    else if (WC == ' ' || WC == '\\t' || WC == '\\f') {  // use iswblank(WC) instead?\n"
             "\n"
             "      continue;\n"
             "    }\n"
             "\n"
             "\n"
             "\n"
             "\n"
             "    else {\n"
             "      // everything else just returns one significant non-space character. This includes '\\n'.\n"
             "\n"
             "      if ((WC == '\\n') && ((PP>1) && (source(PP-1).ch == 'c'))) {  // BEWARE WHEN CHANGING STROPPING ENCODING: Looking for a preceding '%C' ...\n"
             "        if (PP>0) _source(PP-1).ch = ' '; // remove the '%c'\n"
             "        _source(PP++).ch = ' '; // remove the newline\n"
             "\n"
             "        // This is the only place where we gobble spaces *after* a token rather than before.\n"
             "        // It may be cleaner to set a 'continuation' flag and gobble them before the next\n"
             "        // symbol fetch rather than do it here in a lookahead.  Esp. wrt to reconstituting source\n"
             "        // from the array for the listing file etc etc.\n"
             "        // BUT FOR NOW, %C IS HANDLED BY THS HACK:\n"
             "\n"
             "        int Lookahead = FP;\n"
             "        while (SYM(Lookahead).ch == '\\n' || SYM(Lookahead).ch == ' ' || SYM(Lookahead).ch == '\\t' || SYM(Lookahead).ch == '\\f') { // Use iswblank()?\n"
             "          // No worries about  '{...}' - this behaviour seems to be identical to Imp77's\n"
             "          _SYM(Lookahead).ch = ' ';   // gobble following newlines and whitespace before next significant character.\n"
             "          Lookahead++;\n"
             "        }\n"
             "        continue;\n"
             "      }\n"
             "\n"
             "      if (iswalpha(WC) && iswlower(WC)) {\n"
             "        WC = towupper(WC);  // ALSO TEMPORARY\n"
             "      }\n"
             "      _source(PP++).ch = WC;\n"
             "      continue;\n"
             "    }\n"
             "\n"
             "\n"
             "    // Still skipping whitespace ...\n"
             "\n"
             "  }\n"
             "\n"
             "  DONE();\n"
             "  P = 0;\n"
             "  while (source(P).ch != 0) {\n"
             "    if (debug_stropping) fprintf(stderr, \"%d: ch='%lc'  start=%d:end=%d\\n\", P, source(P).ch, source(P).start, source(P).end);\n"
             "    P++;\n"
             "  }\n"
             "\n"
             "#undef DONE\n"
             "#endif\n"
             "  return TRUE;\n",
  L"\n"
             "  // Noting that we have just seen a label at parse time allows us\n"
             "  // to handle an immediately-following comment differently, i.e.\n"
             "  // by avoiding the line reconstruction (if doing so on the fly)\n"
             "  // and thus preserving the text of the comment for output by a\n"
             "  // source-to-source translator.  We're not doing that yet.\n"
             "  \n"
             "  //ColonFlag = 1; // Note that <COLON> occurs last in its list of alternatives so if it is\n"
             "                   // executed at all, the parse is going to be successful.\n"
             "                   // We should then reset ColonFlag at the point where it is tested (for the comments)\n"
             "  return TRUE;\n",
  L"\n"
             "  //ColonFlag = 0;\n"
             "  return TRUE;\n",
  L"\n"
             "  // We almost certainly need to handle include files at parse time (with a stack of\n"
             "  // input files, as I'm doing in takeon.c)\n"
             "\n"
             "  // Include files such as fred.inc should cause the generated C code to be written\n"
             "  // to fred.h, with the %include \"fred.inc\" being replaced by #include \"fred.h\"\n"
             "  // Some care should be taken when writing those files to use a temporary name\n"
             "  // such as \"fred.imp.h\" so that any header files which are being manually maintained\n"
             "  // are not overwritten by the generated files.\n"
             "\n"
             "  // For now, all output is going to the same output file.\n"
             "\n"
             "  return TRUE;\n",
  L"\n"
             "  // listing control has to be done at parse time (unless there is a *major* restructuring)\n"
             "\n"
             "  // %list / %endoflist commands may not be relevant in a C translation.\n"
             "  // We *don't*, for example, want to use %list to control whether the\n"
             "  // C code is output or not - athough it seems like a useful thing to\n"
             "  // do, it would break any existing code that used %endoflist for its original\n"
             "  // purpose.\n"
             "  \n"
             "  return TRUE;\n",
  L"\n"
             "  return TRUE;\n",
};

parsefn parsetime[NUM_SEMANTIC_PHRASES] = {
  &parse_init,
  &parse_terminate,
  &parse_whitespace,
  &parse_Imp77_stropping,
  &parse_COLON,
  &parse_UNDO_COLON,
  &parse_INCLUDE,
  &parse_LISTON,
  &parse_LISTOFF,
};
// Comments are stored so that they can be re-inserted, should
// we need to regenerate a grammar.g file from this header file.



const wchar_t *xcomment[NUM_PHRASES] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
};
const wchar_t *ast_code[NUM_SIMPLE_PHRASES] = {
  L"\n"
             "  PushDecl(42);\n"
             "  // Any time the global variables use to populate the VarDecl[] array are changed, this code should mirror them.\n"
             "  // Find them with: grep \"^  [A-Z][A-Z]\" imp80-init.g\n"
             "  PushDecl(Object);\n"
             "  PushDecl(Basetype);\n"
             "  PushDecl(Signedness);\n"
             "  PushDecl(Precision);\n"
             "  PushDecl(Proc);\n"
             "  PushDecl(NameInfo);\n"
             "  PushDecl(Spec);\n"
             "  PushDecl(IsArray);\n"
             "  PushDecl(Area);\n"
             "  PushDecl(Linkage);\n"
             "  PushDecl(ParentVarIDX);\n"
             "  PushDecl(ParentTypeIDX);\n"
             "\n"
             "  compile(P(1), depth+1); // Aha! Bug fixed.  I had forgotten to reinit after saving the previous state!\n"
             "  \n"
             "  return -1;\n"
             "  /* Should any of these be stacked too?  Specifically ParentVarIDX and ParentTypeIDX, especially if\n"
             "     compiling declarations with a recordformat or procedure FPP list.\n"
             "   */\n"
             "#ifdef NEVER\n"
             "  int ParentVarIDX = -1, ParentTypeIDX = -1;        // VarDeclIDX for procedure to which parameters are being attached.\n"
             "  int DTag = -1, VTag = -1, ATag = -1;              // Tag for declarations, VTag for use. ATag for an alias.   GLOBALS.\n"
             "  char *SDTag = NULL, /*  *SVTag = NULL, */   *SATag = NULL; // STag, VTag and ATag converted to a char *.  \n"
             "  wchar_t *wSDTag = NULL, *wSVTag = NULL, *wSATag = NULL; // STag, VTag and ATag converted to a wchar_t *.  \n"
             "#endif\n",
  L"\n"
             "  ParentTypeIDX = PopDecl();\n"
             "  ParentVarIDX = PopDecl();\n"
             "  Linkage = PopDecl();\n"
             "  Area = PopDecl();\n"
             "  IsArray = PopDecl();\n"
             "  Spec = PopDecl();\n"
             "  NameInfo = PopDecl();\n"
             "  Proc = PopDecl();\n"
             "  Precision = PopDecl();\n"
             "  Signedness = PopDecl();\n"
             "  Basetype = PopDecl();\n"
             "  Object = PopDecl();\n"
             "  {int check = PopDecl(); if (check != 42) fprintf(stderr, \"* Error: Unbalanced Decl stack\\n\"); }\n"
             "  return -1;\n",
  L"\n"
             "  ParentTypeIDX = -1;\n"
             "  ParentVarIDX = -1;\n"
             "  Object     = UNINIT_OBJECT;\n"
             "  Basetype   = UNINIT_BASETYPE;\n"
             "  Signedness = UNINIT_SIGNEDNESS;\n"
             "  Precision  = UNINIT_PRECISION;\n"
             "  Proc       = UNINIT_PROC;\n"
             "  NameInfo   = NO_NAME;           // UNINIT_AN_N\n"
             "  //IsFormat   = NO_FORMAT;       // UNINIT_ISFORMAT\n"
             "  Spec       = NO_SPEC;           // UNINIT_SPEC\n"
             "  IsArray    = SCALAR;            // UNINIT_ISARRAY\n"
             "  // TO DO: Do I need an \"IS_INITIALISED\" tag as well?  So far, only reason for\n"
             "  //        having one would be to suppress the keyword \"extern\" in declarations\n"
             "  //        such as \"%externalinteger fred = 1\" which would otherwise be declared\n"
             "  //        in C as \"extern int fred = 1;\" rather than \"int fred = 1;\" (at least\n"
             "  //        if declared at the top level.  Placing that declaration within\n"
             "  //        a nested routine is a separate issue and may be a problem. Exbedding\n"
             "  //        that declaration to the top level may not be possible due to scoping rules.)\n"
             "  // NOTE: A data declaration (e.g. %integer I) at the top level of a file of externals\n"
             "  // or before the begin/endofprogram block is an error in Imp77 and I'm guessing in Imp80.\n"
             "  Area       = (ctrl_depth() < 1 ? EXTDATA : STACK);  // UNINIT_AREA\n"
             "  // However a %routine at the same level (as opposed to an %externalroutine) appears to be accepted.\n"
             "  Linkage    = UNINIT_LINKAGE;    // Ditto? Check with ERCC compiler. Or ask Bob.\n"
             "  return -1;\n",
  L"",
  L"",
  L"\n"
             "  if (alt == 0) codegen(\"\\n\"); \n",
  L"\n"
             "  // unconditionally set in a %begin.  Could just put the 'push_scope_level()' in\n"
             "  // the code for <begin_block> but for now I'll follow the structure of the\n"
             "  // ERCC compilers and grammar.\n"
             "  push_scope_level();\n"
             "\n"
             "  // Note that it turns out we need an initial 'push_scope_level();' before we can\n"
             "  // do *anything*, even prims/perms or external data.  That has now been added to\n"
             "  // the 'init' rule called in P<SS>.  This is because C, unlike say Modula2 (or C++)\n"
             "  // has no concept of module initialisation.\n",
  L"\n"
             "  // called on %end and %endofprogram\n"
             "  pop_scope_level();\n",
  L"\n"
             "  compile(P(1), depth+1); // perform initialisation\n"
             "  compile(P(2), depth+1); // Read in the entire source and perform line reconstruction.\n"
             "  int SS = compile(P(3), depth+1); // Get the tree representing the entire program\n"
             "  compile(P(4), depth+1); // clean up at end.\n"
             "  return SS; // Pass back parse tree for generate_c() to walk and output the C code.\n",
  L"\n"
             "                     \n"
             "     // Phrase is used to convert an OP into a more useful AST phrase.\n"
             "     // (the default for char syms is separate P() items for each char, which is unweildy)\n"
             "\n"
             "     // It would be cleaner to return the OP_xxx enum rather than the string.\n"
             "     // I should do so later.  (The string is decoded in PushBinOp())\n"
             "\n"
             "     // TO DO: every P_mktuple(AST_*) operation must synthesize the type of its\n"
             "     // result from the types of the operands.\n"
             "\n"
             "     if (alt == 0)               {  //\\\\    '+',\n"
             "       t[1] = wstrtopool(L\"+\");\n"
             "       t[2] = OP_ADD;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 1)        {  //\\\\    '-',\n"
             "       t[1] = wstrtopool(L\"-\");\n"
             "       t[2] = OP_SUB;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 2)        {  //\\\\    '&',\n"
             "       t[1] = wstrtopool(L\"&\");\n"
             "       t[2] = OP_AND;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 3)        {  //\\\\    '*' '*' '*' '*',\n"
             "       t[1] = wstrtopool(L\"****\");\n"
             "       t[2] = OP_IEXP;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "       \n"
             "     } else if (alt == 4)        {  //\\\\    '*' '*',\n"
             "       t[1] = wstrtopool(L\"**\");\n"
             "       t[2] = OP_REXP;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 5)        {  //\\\\    '*',\n"
             "       t[1] = wstrtopool(L\"*\");\n"
             "       t[2] = OP_MULT;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 6)        {  //\\\\    '!' '!',\n"
             "       t[1] = wstrtopool(L\"!!\");\n"
             "       t[2] = OP_EOR;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 7)        {  //\\\\    '!',\n"
             "       t[1] = wstrtopool(L\"!\");\n"
             "       t[2] = OP_OR;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 8)        {  //\\\\    '/' '/',\n"
             "       t[1] = wstrtopool(L\"//\");\n"
             "       t[2] = OP_INTDIV;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 9)        {  //\\\\    '/',\n"
             "       // %begin\n"
             "       //   %real r\n"
             "       //   r = 3/5     ;! beware: a naive conversion to C will cause a result of 1.0 (because both operands of '/' will be ints)\n"
             "       //   print(r,5)  ;! 0.6\n"
             "       // %endofprogram\n"
             "       t[1] = wstrtopool(L\"/\");\n"
             "       t[2] = OP_REALDIV;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 10)        {  //\\\\    '>' '>',\n"
             "       t[1] = wstrtopool(L\">>\");\n"
             "       t[2] = OP_RSHIFT;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 11)        {  //\\\\    '<' '<',\n"
             "       t[1] = wstrtopool(L\"<<\");\n"
             "       t[2] = OP_LSHIFT;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 12)        {  //\\\\    '.',\n"
             "       t[1] = wstrtopool(L\".\");\n"
             "       t[2] = OP_CONCAT;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 13)        {  //\\\\    '\\' '\\',\n"
             "       t[1] = wstrtopool(L\"\\\\\\\\\");\n"
             "       t[2] = OP_IEXP;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 14)        {  //\\\\    '\\',\n"
             "       t[1] = wstrtopool(L\"\\\\\");\n"
             "       t[2] = OP_REXP;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else if (alt == 15)        {  //\\\\    '^' '^',\n"
             "       t[1] = wstrtopool(L\"^^\");                                    // NOTE: soap80 does not recognise ^^, only ****\n"
             "       t[2] = OP_IEXP;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     } else                      {  //\\\\    '^';\n"
             "       t[1] = wstrtopool(L\"^\");\n"
             "       t[2] = OP_REXP;\n"
             "       return t[0] = P_mktuple(AST_BINOP, alt, 2/*phrases*/, t);\n"
             "\n"
             "     }\n"
             "     return -1;\n",
  L"\n"
             "    PushBinOp(compile(P(1), depth+1)); // TORP\n"
             "    return -1;\n",
  L"\n"
             "     if (alt == 4) return -1;\n"
             "  \n"
             "     if (alt == 0)               {  //\\\\    '+',\n"
             "       // return -1; // simpler, but generated C would not retain the unary +\n"
             "       t[1] = wstrtopool(L\"+\");\n"
             "       t[2] = OP_POS;\n"
             "     } else if (alt == 1)        {  //\\\\    '-',\n"
             "       t[1] = wstrtopool(L\"-\");\n"
             "       t[2] = OP_NEG;\n"
             "     } else if (alt == 2)        {  //\\\\    '\\\\',\n"
             "       t[1] = wstrtopool(L\"\\\\\");\n"
             "       t[2] = OP_NOT;\n"
             "     } else if (alt == 3)        {  //\\\\    '~',\n"
             "       t[1] = wstrtopool(L\"~\");\n"
             "       t[2] = OP_NOT;\n"
             "     }\n"
             "     return t[0] = P_mktuple(AST_MONOP, alt, 2/*phrases*/, t);\n",
  L"\n"
             "  {\n"
             "     int Expr = compile(P(1), depth+1);\n"
             "     if (Expr != -1) PushMonOp(Expr); // TORP\n"
             "     return -1;\n"
             "  }\n",
  L"\n"
             "     if (alt == 0)               {\n"
             "       t[1] = wstrtopool(L\"==\");\n"
             "       t[2] = OP_ASSIGN_ADDR;\n"
             "     } else if (alt == 1)        {\n"
             "       t[1] = wstrtopool(L\"=\");\n"
             "       t[2] = OP_ASSIGN_VALUE;\n"
             "     } else if (alt == 2)        {\n"
             "       t[1] = wstrtopool(L\"<-\");\n"
             "       t[2] = OP_JAM_TRANSFER;\n"
             "     } else if (alt == 3)        {\n"
             "       t[1] = wstrtopool(L\"->\");\n"
             "       t[2] = OP_UNCOND_STR_RESOL;\n"
             "     }\n"
             "     return t[0] = P_mktuple(AST_ASSOP, alt, 2/*phrases*/, t);\n",
  L"\n"
             "  {\n"
             "    // Some work required to distinguish address comparisons and conditional resolution from simple arithmetic comparisons or string comparisons\n"
             "    const char *C_Comp[] = { \"== &\", \">=\", \">\", \"!= &\", \"!= &\", \"!=\", \"<=\", \"<\", \"-> /*STRRES*/\", \"==\", \"!=\", \"!=\" };\n"
             "    // (The '&'s above are temporary)\n"
             "    codegen(\" %s \", C_Comp[alt]);\n"
             "    return -1; // TO DO: return one of several possible AST_something tuples\n"
             "  }\n",
  L"",
  L"\n"
             "  {\n"
             "  // We want to return an AST record with the VarDecl contents\n"
             "  // (which includes the TypeDecl information)\n"
             "\n"
             "  int VTagAtom = SubPhraseIdx(Ph, 2);\n"
             "  wSVTag = Atom2WStr(VTagAtom);\n"
             "  VTag=wstrtopool(wSVTag);\n"
             "\n"
             "  // Use the scope tools to find it:\n"
             "  // (Question: are there any separate namespaces in Imp as there are in C?)\n"
             "  int VarIDX = lookup_by_strpoolidx(\"decl\", VTag); // This (currently) just returns a raw index into the VarDecl[] array. Would be nice to strongly type it.\n"
             "  if (VarIDX == -1) {\n"
             "    fprintf(stderr, \"* NAME NOT SET(#3): %ls\\n\", wSVTag); //exit(1);\n"
             "    return -1;\n"
             "  }\n"
             "  t[1] = VarIDX;\n"
             "  int rvalue = P_mktuple(AST_RVALUE, 0/*alt no*/, 1/*phrases*/, t);\n"
             "  P_TYPEINFO(rvalue) = VarDecl[VarIDX].type;\n"
             "  return t[0] = rvalue;\n"
             "  }\n",
  L"\n"
             "  {\n"
             "  // check to see which is in the most accessible format:\n"
             "  //Diagnose(\"\", SubPhraseIdx(Ph, 2),0, debug_declarations);\n"
             "  VTag = SubPhraseIdx(Ph, 2);\n"
             "  //Diagnose(\"\", VTag,0, debug_declarations);\n"
             "  \n"
             "  //if (SVTag != NULL) free(SVTag); SVTag=Atom2Str(VTag); // *short term* cache. Diags\n"
             "  if (wSVTag != NULL) free(wSVTag); wSVTag=Atom2WStr(VTag);\n"
             "  \n"
             "  t[1] = VTag;\n"
             "  return t[0] = P_mktuple(AST_TOKEN, 0/*alt no*/, 1/*phrases*/, t);\n"
             "  }\n",
  L"",
  L"",
  L"",
  L"",
  L"",
  L"\n"
             "  //  <--------------------------------------------------------------------------------------------------- FIX!  TO DO\n",
  L"  // Pushes an AST_ATOM_LIT:\n"
             "\n"
             "  t[1] = compile(P(1), depth+1);\n"
             "  t[2] = -1; // type info, TO DO.\n"
             "  t[0] = P_mktuple(AST_CONST, 0/*alt no*/, 2/*phrases*/, t);\n"
             "\n"
             "  //Diagnose(\"NEW CONSTANT: \", t[1], 0, TRUE);\n"
             "  //{int child=P_P(t[1],1); Diagnose(\"CONSTANT CHILD: \", child, 0, TRUE);}\n"
             "  \n"
             "  PushConst(t[0]);\n"
             "\n"
             "  // <--------------------------------------------------------------- FIX!  TO DO\n",
  L"",
  L"",
  L"",
  L"",
  L"",
  L" return -1; ",
  L"\n"
             "\n"
             "  // TO DO: tweaking this to suppress a later error, but would be nice to know why compile() had not tagged the result properly\n"
             "  // Add | AST_ATOM_LIT ?\n"
             "\n"
             "  t[1] = -1; /* easier if I dont care how the comment was started... */    // compile(P(1), depth+1);\n"
             "  \n"
             "  // compiling literals through the default path causes them to be printed.  This may be wanted at the initial\n"
             "  // stages of bringing up a new program but by the time it's developed as far as we are now, we generally don't\n"
             "  // want bits of text showing up at random!  I think it's anything created by wlit() and can be suppressed by\n"
             "  // redefining wlit() at the appropriate place.  By the time this code is complete there should be no drop-throughs,\n"
             "  // and any text that we want to be reserved should be passed back via AST_* tuples.  So issues such as this rule\n"
             "  // should go away.\n"
             "  // Note that for now, line reconstruction loses all spaces in parsed comments, and the path of literal text\n"
             "  // through the compiler loses embedded '{comments}' at the moment too, so comments are not really well handled\n"
             "  // for now and will require some major restructuring of the line reconstruction phase.\n"
             "\n"
             "  // The reason that spaces are stripped in \"!\" comments is that \"!\" is ambiguous with the \"OR\" operator (and\n"
             "  // in previous compilers, with !MOD!) so it is not safe to stop stripping spaces just because a \"!\" has been\n"
             "  // seen in the input.  Because \"LABEL: ! comment text\" is valid, the line reconstruction *has to* interact\n"
             "  // with the parser state, which is appalling language design and forces a specific implementation on the\n"
             "  // compiler-writer.  You cannot, for example, have a completely independent pre-processing stage, and you can't\n"
             "  // have a parser that parses first to a tree and then performs all the actions on the parsed tree - the levels\n"
             "  // are all mixed up together.  Hence why this issue has not been handled earlier. I've been trying to keep my\n"
             "  // options open as to how the parser interacts with the line reconstruction and with the decision to handle\n"
             "  // declarations and scope at parse time versus at compile time from the fully-parsed program. (i.e. statement-at-a-time\n"
             "  // compiling versus program-at-a-time compiling of the parse tree)\n"
             "  \n"
             "  t[2] = compile(P(2), depth+1);\n"
             "  t[0] = P_mktuple(AST_COMMENT, 0/*alt no*/, 2/*phrases*/, t);\n"
             "  generate_c(t[0], depth+1);\n"
             "  return t[0];\n",
  L"",
  L"\n"
             "  // NOW PUNTED TO Compile_Var() so should not get called here during parse\n"
             "  // but will be called later.\n"
             "  if (alt == 0) {\n"
             "    t[1] = -1; // reserve for parent // object\n"
             "    t[2] = compile(P(2), depth+1);   // field\n"
             "    t[3] = compile(P(3), depth+1);   // app\n"
             "    t[4] = compile(P(4), depth+1);   // subfields\n"
             "    // AST entry name yet to be determined...\n"
             "    return P_mktuple(AST_PENDING_APP_OR_FIELD, 0/*alt no*/, 4/*phrases*/, t);\n"
             "  } else {\n"
             "    return -1;\n"
             "  }\n",
  L"\n"
             "  {\n"
             "\n"
             "    int varname   = compile(P(1), depth+1); // RVALUE containing VarDecl[VarIDX]\n"
             "    int app       = compile(P(2), depth+1); // -1 or MkApp(Expr, Rest); - we don't yet know if an array or a fn call\n"
             "    int subfields = compile(P(3), depth+1); // -1 or AST_PENDING_APP_OR_FIELD\n"
             "                                          // (this will recursively create\n"
             "                                          //  tuples for any subfields)\n"
             "\n"
             "    int Var = AST_mktuple(AST_PENDING_APP_OR_FIELD, varname, -1 /*field*/, app, subfields);    \n"
             "    PushVar(Var);  // Punt to reverse-polish stack evaluation of precedence\n"
             "    return -1;\n"
             "\n"
             "  }\n",
  L"\n"
             "\n"
             "  // shunting yard algorithm.  Note need to distinguish between the use of\n"
             "  // VAR and CONST in expressions (which are getting the shunting yard treatment)\n"
             "  // and many other places in the grammar, where they are not.\n"
             "\n"
             "  // NOTE: parentheses in Imp serve two purposes: 1) overriding the precedence of\n"
             "  // operators, and 2) forcing the order of evaluation specifically to avoid\n"
             "  // overflows in intermediate results.  So although we normally output expressions\n"
             "  // using minimal brackets and only insert them when needed due to differences\n"
             "  // between Imp and C precedences, we will leave user-supplied parentheses in place\n"
             "  // when outputting the C version of an expression to explicitly preserve option (2).\n"
             "\n"
             "  if (alt == 2) {  // '('<EXPR>')'\n"
             "    // compile the expr and reduce it to a single irreducible object so it is treated\n"
             "    // like a variable or a constant and its subexpressions are not examined when\n"
             "    // handling C's operator precedence.\n"
             "\n"
             "    // Compiling <EXPR> should return an AST_EXPRESSION\n"
             "\n"
             "    // a + (b + c) was coming out as (a + b + c) - the TORP was picking up\n"
             "    // the \"a +\" that was already on the stack and adding it to the Expr.  What\n"
             "    // was needed was to seal a false bottom on the stack so that Expr is\n"
             "    // constructed independent of anything preceding it. [*** FIXED ***]\n"
             "\n"
             "    t[1] = compile(P(2), depth+1);\n"
             "    int AstTuple = P_mktuple(AST_USER_PARENS, 0, 1, t);\n"
             "               \n"
             "    PushParen(L\"(\"); PushExpr(AstTuple); PushParen(L\")\"); // AstTuple is opaque to the RP code.  It treats it like any atom.\n"
             "    return -1;\n"
             "  }\n"
             "  // otherwise, compile <VAR> or <CONST>, which will push them on the TORP stack   <--------------------------------- FIX!  TO DO\n",
  L"\n"
             "  {\n"
             "  // alt=0: symbolic constant\n"
             "  // alt=1: literal constant\n"
             "//  t[1] = compile(P(1), depth+1);\n"
             "//  PushConst(P_mktuple(AST_RVALUE, 0/*alt no*/, 1/*phrases*/, t));\n"
             "\n"
             "  int ConstVar = compile(P(1), depth+1);\n"
             "  if (P_alt(ConstVar) == 0 && P_P(ConstVar,1) == -1) {\n"
             "    fprintf(stderr, \"* NAME NOT SET(#4): %ls\\n\", wSVTag); // exit(1);\n"
             "  }\n"
             "  \n"
             "  // if (..?) {   // TO DO: Check the type and if it was a constant, mark it as so:\n"
             "  // P_ISCONST(ConstVar) = 1;\n"
             "  //\n"
             "  \n"
             "  PushConst(ConstVar);\n"
             "  return -1;\n"
             "  }\n",
  L"",
  L"\n"
             "  {\n"
             "    // CODE BELOW same as following P<EXPR> but uncommented.\n"
             "    int Expr;\n"
             "    int SaveOperBottom = OperStack_bottom; OperStack_bottom = OperStack_nextfree;\n"
             "    int SaveDataBottom = DataStack_bottom; DataStack_bottom = DataStack_nextfree;\n"
             "    t[2] = compile(P(1), depth+1);\n"
             "    t[1] = ExPop();\n"
             "    DataStack_bottom = SaveDataBottom; OperStack_bottom = SaveOperBottom;\n"
             "\n"
             "    // TO DO: Modify the tuple below to mark it as evaluating to a constant\n"
             "    // compatible with contexts in C that require a constant expression:\n"
             "    // *OR* return AST_CONSTANT_EXPRESSION tuple? (which does not yet exist)\n"
             "    Expr = P_mktuple(AST_EXPRESSION, 0, 2, t); // converts to a single object\n"
             "\n"
             "    // if (...?) {  // TO DO: Test and confirm <EXPR> evaluates to a constant...\n"
             "    //   P_ISCONST(Expr) = 1;\n"
             "    // }\n"
             "\n"
             "    //Diagnose(\"CEXPR: \", Expr, 0, debug_declarations);\n"
             "    return Expr;\n"
             "  }\n",
  L"",
  L"",
  L"",
  L"\n"
             "  {\n"
             "    // CODE BELOW same as previous P<CEXPR> but commented.\n"
             "    int Expr;\n"
             "\n"
             "    // Previously I had many calls to <EXPR> duplicating the 'false bottom'\n"
             "    // code below, until I realised I could safely use it *everywhere* and\n"
             "    // therefore placed a single instance here instead.\n"
             "    \n"
             "    int SaveOperBottom = OperStack_bottom; OperStack_bottom = OperStack_nextfree;\n"
             "    int SaveDataBottom = DataStack_bottom; DataStack_bottom = DataStack_nextfree;\n"
             "    t[2] = compile(P(1), depth+1); // walk the CST expression tree to process each atom sequentially left to right, as input to the shunting yard algorithm\n"
             "    t[1] = ExPop();               // now convert the reverse-polish stack back into an AST tree, with precedence now magically applied.\n"
             "    DataStack_bottom = SaveDataBottom;\n"
             "    OperStack_bottom = SaveOperBottom;\n"
             "\n"
             "    Expr = P_mktuple(AST_EXPRESSION, 0, 2, t); // converts to a single object\n"
             "\n"
             "    // We do not print <EXPR>s immediately because a bracketed (<EXPR>) which is a subexpression of a larger <EXPR> has to be reprocessed\n"
             "    // in order to apply operator precedence.  A consequence of this is that <EXPR>s in other contexts will have to be output via generate_c()\n"
             "    // at the point of call.\n"
             "    \n"
             "    //Diagnose(\"EXPR: \", Expr, 0, debug_declarations);\n"
             "    return Expr;\n"
             "  }\n",
  L"",
  L"",
  L"\n"
             "  if (alt == 0) {\n"
             "    int Expr = compile(P(2), depth+1);\n"
             "    int Rest = compile(P(3), depth+1);\n"
             "    return MkApp(Expr, Rest);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    int Expr = compile(P(3), depth+1);\n"
             "    int Rest = compile(P(4), depth+1);\n"
             "    return MkApp(Expr, Rest);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\"if \");\n"
             "  } else {\n"
             "    codegen(\"unless \");\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  codegen(\" (\");\n"
             "  compile(P(1), depth+1); compile(P(2), depth+1);\n"
             "  codegen(\") \");\n"
             "  return -1;\n",
  L"\n"
             "  // %for J = I, -1, 1\n"
             "  // for (J = I; J < 1; J += -1)\n"
             "  // if the step is positive use '<', if negative use '>', if unknown use '!='...\n"
             "  \n"
             "  codegen(\"for (\");\n"
             "  int Control = compile(P(1), depth+1);\n"
             "  // TO DO: check type of Control and also that it is not a literal constant! (P_alt(Control) == ?)\n"
             "  if (Control == -1 || P_P(Control,1) == -1) {\n"
             "    fprintf(stderr, \"* NAME NOT SET(#5): %ls\\n\", wSVTag); // exit(1);\n"
             "    return -1;\n"
             "  }\n"
             "  generate_c(Control, depth+1);\n"
             "\n"
             "  int Expr1 = compile(P(3), depth+1);\n"
             "  int Expr2 = compile(P(5), depth+1);\n"
             "  int Expr3 = compile(P(7), depth+1);\n"
             "\n"
             "  codegen(\" = \");\n"
             "  generate_c(Expr1, depth+1);\n"
             "  codegen(\"; \");\n"
             "  generate_c(compile(P(1), depth+1), depth+1);\n"
             "  codegen(\" != (\");\n"
             "  generate_c(Expr3, depth+1);\n"
             "  codegen(\")+(\");\n"
             "  generate_c(Expr2, depth+1);\n"
             "  codegen(\"); \");\n"
             "  generate_c(compile(P(1), depth+1), depth+1);\n"
             "  codegen(\" += \");\n"
             "  generate_c(Expr2, depth+1);\n"
             "  codegen(\") \");\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\"while \"); compile(P(2), depth+1);\n"
             "  } else if (alt == 1) {\n"
             "    // TO DO: restructure so that while is tested at end of loop rather than beginning!\n"
             "    // Note that for now compiling these causes them to also be printed on the fly.\n"
             "    codegen(\"until \"); compile(P(2), depth+1);\n"
             "    t[1] = P(2);\n"
             "    int Cond = t[0] = P_mktuple(AST_DEFER_UNCOMPILED_PHRASE, /*alt*/0, /*count*/1, t);\n"
             "    return Cond;\n"
             "  } else if (alt == 2) {\n"
             "    compile(P(2), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L" return -1; ",
  L"\n"
             "  if (alt == 0) {\n"
             "    //codegen(\", \");\n"
             "    fprintf(stderr, \"? Warning: <DECLNAMES> being compiled by default code - so no calls to Declaration() for each <DECLNAME>\\n\");\n"
             "    compile(P(3), depth+1);\n"
             "    compile(P(4), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  // check to see which is in the most accessible format:\n"
             "  DTag = SubPhraseIdx(Ph, 2); // Set a global for use by <DECLARE>\n"
             "  return -1;\n",
  L" return -1; ",
  L"\n"
             "  Basetype = (alt == 0 ? FLOAT : INTEGER);\n"
             "  return -1;\n",
  L"  //  <---------------------------------------------------------------- FIX!  TO DO\n"
             "  // TO DO.\n",
  L"\n"
             "  switch (alt) {\n"
             "  case 0: Signedness = SIGNED;   Precision = WORD;     Basetype = INTEGER;     break; // integer\n"
             "  case 1: Signedness = SIGNED;   Precision = WORD;     Basetype = FLOAT;       break; // real\n"
             "  case 2: Signedness = SIGNED;   Precision = QUADWORD;                         break; // long long (integer | real)\n"
             "  case 3: Signedness = SIGNED;   Precision = LONGWORD;                         break; // long (integer | real)\n"
             "  case 4: Signedness = SIGNED;   Precision = BYTE;     Basetype = INTEGER;     break; // byte (integer)?   (no %mite in this variant of Imp)\n"
             "  case 5: Signedness = SIGNED;   Precision = COMPOUND; Basetype = STRINGTYPE;  break; // string\n"
             "  case 6: Signedness = UNSIGNED; Precision = SHORT;    Basetype = INTEGER;     break; // half (integer)?\n"
             "  case 7: Signedness = SIGNED;   Precision = SHORT;    Basetype = INTEGER;     break; // short (integer)?\n"
             "  case 8: Signedness = SIGNED;   Precision = COMPOUND; Basetype = RECORD;\n"
             "          compile(P(3), depth+1); // compiling <RFREF> assigns <RFREF> type to global \"RecordTypeIDX\" which is picked up by Declaration()\n"
             "                                  // TO DO: Add RecordTypeIDX to the PUSH/POPDECL stack.\n"
             "                                  // (Note: inline definition of RFREF not yet handled.)\n"
             "                                                                               break; // record\n"
             "  }\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[ Sign=%d Prec=%d Type=%d ]\", Signedness, Precision, Basetype);\n"
             "  //  <----------------------------------------------------------------------------------------------  FIX!  TO DO\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    Proc = ROUTINE;\n"
             "    if (debug_declarations > 1) fprintf(stderr, \"[Proc=%d]\", Proc);\n"
             "  }\n"
             "  //  <----------------------------------------------------------------------------------------------  FIX!  TO DO\n",
  L"\n"
             "  if (alt == 1) Proc = MAP; else Proc = FN;\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[Proc=%d]\", Proc);\n"
             "  return -1;\n",
  L"",
  L"\n"
             "  // Need to check I set up Object = here correctly.  Was a bit of a rush late one night.\n"
             "  Area = PARAMETER;\n"
             "  if (alt == 0) { // a simple object\n"
             "    // <XTYPE><Opt_AN_N_type><DECLNAME><DECLNAMES>,\n"
             "    Object = VAR; Proc = NONE; Linkage = AUTO;\n"
             "    compile(P(1), depth+1); // <<XTYPE>\n"
             "    compile(P(2), depth+1); // <Opt_AN_N_type>\n"
             "    compile(P(3), depth+1); // <DECLNAME> \n"
             "    int ParamIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */); //codegen(\"/*A %s:%d*/\",__FILE__,__LINE__);\n"
             "    {\n"
             "      int MoreDecls = P(4);  // ',' <READLINEP><DECLNAME> <DECLNAMES>, ''\n"
             "      while (P_alt(MoreDecls) == 0) {\n"
             "        codegen(\", \");/*a*/\n"
             "        compile(P_P(MoreDecls, 3), depth+1); // <DECLNAME>\n"
             "        int NextParamIDX = Declaration(depth+1, TRUE /* A, B, C format allowed */); //codegen(\"/*B %s:%d*/\",__FILE__,__LINE__);\n"
             "        MoreDecls = P_P(MoreDecls, 4);\n"
             "      }\n"
             "    }\n"
             "    return -1;\n"
             "  } else if (alt == 1) {  // A procedure parameter\n"
             "    // <RT><Opt_N_type><DECLNAME><DECLNAMES><FPP>\n"
             "    Object = CODE; // TO DO: Or should this be VAR?  Not yet tested.\n"
             "    compile(P(1), depth+1); // <RT>\n"
             "    compile(P(2), depth+1); // <Opt_N_type>\n"
             "    compile(P(5), depth+1); // <FPP>           Reordered so FPP is handled before names\n"
             "    compile(P(3), depth+1); // <DECLNAME> \n"
             "    int ProcParamIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */); codegen(\"/*C %s:%d*/\",__FILE__,__LINE__);\n"
             "    {\n"
             "      int MoreDecls = P(4);  // ',' <READLINEP><DECLNAME> <DECLNAMES>, ''\n"
             "      while (P_alt(MoreDecls) == 0) {\n"
             "        codegen(\", /*b*/\");\n"
             "        compile(P_P(MoreDecls, 3), depth+1); // <DECLNAME>\n"
             "        int NextProcParamIDX = Declaration(depth+1, TRUE /* A, B, C format allowed */); codegen(\"/*D %s:%d*/\",__FILE__,__LINE__);\n"
             "        MoreDecls = P_P(MoreDecls, 4);\n"
             "      }\n"
             "    }\n"
             "    return -1;\n"
             "  } else if (alt == 2) {    // \"name\"<DECLNAME><DECLNAMES>\n"
             "    // NOTE: generated C might consider using GCC's \"__attribute((may_alias))\" to better replicate Imp semantics?\n"
             "    Object = VAR;\n"
             "    Basetype = GENERIC;\n"
             "    NameInfo = OBJECTNAME;\n"
             "    //IsFormat = UNINIT_ISFORMAT;\n"
             "    Spec = NO_SPEC;\n"
             "    //Signedness = UNINIT_SIGNEDNESS;\n"
             "    //Precision = UNINIT_PRECISION; // or maybe COMPOUND\n"
             "    Proc = NONE; // unless the %name parameter is a %fn perhaps? Haven't thought this one through yet.\n"
             "    //IsArray = UNINIT_ISARRAY;\n"
             "    //Linkage = UNINIT_LINKAGE;\n"
             "    compile(P(2), depth+1); // <DECLNAME>\n"
             "    int NameIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */); //codegen(\"/*E %s:%d*/\",__FILE__,__LINE__);\n"
             "    {\n"
             "      int MoreDecls = P(4);  // ',' <READLINEP><DECLNAME> <DECLNAMES>, ''\n"
             "      while (P_alt(MoreDecls) == 0) {\n"
             "        codegen(\", /*c*/\");\n"
             "        compile(P_P(MoreDecls, 3), depth+1); // <DECLNAME>\n"
             "        int NextNameIDX = Declaration(depth+1, TRUE /* A, B, C format allowed */); codegen(\"/*F %s:%d*/\",__FILE__,__LINE__);\n"
             "        MoreDecls = P_P(MoreDecls, 4);\n"
             "      }\n"
             "    }\n"
             "    return -1;\n"
             "  }\n",
  L" return -1; ",
  L"\n"
             "  switch (alt) {\n"
             "  case 0: NameInfo = ARRAYNAME;  break;\n"
             "  case 1: NameInfo = OBJECTNAME; break;\n"
             "  case 2: NameInfo = NO_NAME;     break;\n"
             "  }\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[NameInfo=%d]\", NameInfo);\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 1) codegen(\"void\"); // proc with no parameters\n"
             "  Area = PARAMETER;\n"
             "  //  <--------------------------------------------------------------------------------------------- FIX!  TO DO\n",
  L" return -1; ",
  L"\n"
             "  if (alt == 0) codegen(\", \");\n"
             "  //  <-------------------------------------------------------------------------------------------- FIX!  TO DO\n",
  L"\n"
             "                      \n"
             "  // Need a routine to unwind the control stack and show what was expected.\n"
             "  \n"
             "  if (alt == 0) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[end of program]\\n\");\n"
             "    if (ctrl_depth() != 1) {\n"
             "      fprintf(stderr, \"\\n* Spurious %%ENDOFPROGRAM\\n\"); exit(1);\n"
             "    }\n"
             "    int EXPECTED = pop_ctrl();\n"
             "    if (EXPECTED != ENDOFPROGRAM) {\n"
             "      fprintf(stderr, \"\\n* %%ENDOFPROGRAM not expected here (expecting %s)\\n\",\n"
             "             ctrl_debug[EXPECTED]); exit(1);\n"
             "      // A.k.a \"%FINISH missing\" or \"%REPEAT missing\" or \"%END missing\" ... also needed everywhere a pop_ctrl() doesn't match what's expected.\n"
             "    }\n"
             "    codegen(\"\\n  exit(0);\\n} /* End of program */\\n\");\n"
             "  } else if (alt == 1) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[end of perm]\\n\");\n"
             "  } else if (alt == 2) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[end of file]\\n\");\n"
             "    if (ctrl_depth() != 0) { // might not be the case for an unbalanced %include file...\n"
             "      fprintf(stderr, \"\\n* Spurious %%ENDOFFILE\\n\"); exit(1);\n"
             "    }\n"
             "  } else if (alt == 3) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[end of list]\\n\");\n"
             "  } else if (alt == 4) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[end of Rt/Fn/Map/begin block]\\n\");\n"
             "    if (ctrl_depth() < 1) {\n"
             "      fprintf(stderr, \"\\n* Spurious %%END (ctrl_depth()=%d))\\n\", ctrl_depth()); exit(1);\n"
             "    }\n"
             "    int EXPECTED = pop_ctrl();\n"
             "    if (EXPECTED != END) {\n"
             "      fprintf(stderr, \"\\n* %%END not expected here (expecting %s)\\n\",\n"
             "              ctrl_debug[EXPECTED]); exit(1);\n"
             "    }\n"
             "    codegen(\"\\n}\\n\"); // no semicolon\n"
             "  }\n"
             "  //  <-------------------------------------------------------------------------------------------- FIX!  TO DO\n",
  L"\n"
             "  if (alt == 0) Object = ARRAYFORMAT;\n"
             "  //IsFormat = (alt == 0 ? IS_FORMAT : NO_FORMAT );\n"
             "  //if (debug_declarations > 1) fprintf(stderr, \"[format=%d]\", IsFormat);\n"
             "  return -1;\n",
  L"\n"
             "  return -1; // let the parent level handle it.\n",
  L" // %not is '!' in C\n"
             "  if (alt == 0) {\n"
             "    int Expr1 = compile(P(1), depth+1);\n"
             "    generate_c(Expr1, depth+1);          // TO DO: <EXPR>s need false bottom!\n"
             "    compile(P(2), depth+1);\n"
             "    int Expr2 = compile(P(3), depth+1);\n"
             "    generate_c(Expr2, depth+1);\n"
             "    int RestofSC = P(4);\n"
             "    if (P_alt(RestofSC) == 0) {\n"
             "      int Comp2 = P_P(RestofSC, 1);\n"
             "      int Expr3 = P_P(RestofSC, 2);\n"
             "      codegen(\" && \");\n"
             "      generate_c(Expr2, depth+1);\n"
             "      compile(Comp2, depth+1);\n"
             "      Expr3 = compile(Expr3, depth+1);\n"
             "      generate_c(Expr3, depth+1);\n"
             "    }\n"
             "    compile(P(4), depth+1); // RESTOFSC\n"
             "  } else if (alt == 1) {\n"
             "    compile(P(2), depth+1); // SC\n"
             "    compile(P(3), depth+1); // RESTOFCOND\n"
             "  } else {                  // NOT SC\n"
             "    codegen(\"!\");  // not sure about priorities for booleans\n"
             "    codegen(\"(\");\n"
             "    compile(P(2), depth+1);\n"
             "    codegen(\")\");\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  // TO DO: Only one of them needs to have () added around the other, because the\n"
             "  //        operator precedences of && and || in C are not equal. (&& is higher than || so we need () around \"and\" SC's in a RESTOFORC.)\n"
             "  if (alt == 0) {\n"
             "    codegen(\" && \");\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "  } else if (alt == 1) {\n"
             "    codegen(\" || \");\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\" && \");\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\" || \");\n"
             "    compile(P(2), depth+1);     // NEED () AROUND THE <SC> IF IT CONTAINS AN \"&&\"\n"
             "    compile(P(3), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    Spec = SPEC;\n"
             "  } else if (alt == 1) {\n"
             "    // Procedures of all kinds require a %end\n"
             "    push_ctrl(END); // we will probably later distinguish between END and PROCEND etc.\n"
             "    Spec = NO_SPEC;\n"
             "  }\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[Spec=%d]\", Spec);\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) Spec = SPEC;\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[Spec=%d]\", Spec);\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "  int LowIDX = compile(P(2), depth+1);\n"
             "  int HighIDX = compile(P(4), depth+1);\n"
             "  AddDims(LowIDX, HighIDX);\n"
             "  compile(P(5), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  {\n"
             "  // TO DO: list of upper/lower bounds\n"
             "  int LowIDX = compile(P(2), depth+1);\n"
             "  int HighIDX = compile(P(4), depth+1);\n"
             "  AddDims(LowIDX, HighIDX);\n"
             "  compile(P(5), depth+1);\n"
             "  // We now have ArrayDims of pairs of Low:High on the Dims stack, ready for Declaration(depth+1, ) to use.\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) compile(P(2), depth+1);\n"
             "  return -1;\n",
  L"\n"
             "    ArrayDims = 0; compile(P(3), depth+1); // <BPAIR>\n"
             "    compile(P(1), depth+1); // <DECLNAME>\n"
             "    int ADeclIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */); // codegen(\"/*G %s:%d*/\",__FILE__,__LINE__);\n"
             "    codegen(\";\\n\");\n"
             "    {\n"
             "      int MoreDecls = P(2);  // P<DECLNAMES> = ',' <READLINEP><DECLNAME> <DECLNAMES>, ''\n"
             "      while (P_alt(MoreDecls) == 0) {\n"
             "        //codegen(\", /*e*/\");\n"
             "        compile(P_P(MoreDecls, 3), depth+1); // <DECLNAME>\n"
             "        int NextADeclIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */); //codegen(\"/*H %s:%d*/\",__FILE__,__LINE__); // for now.  Syntax of multiples is complex.\n"
             "        codegen(\";\\n\");\n"
             "        MoreDecls = P_P(MoreDecls, 4); // <DECLNAMES>\n"
             "      }\n"
             "    }\n"
             "    // more array declarations on the same line are effectively independent in terms of bounds (but not type, area etc)\n"
             "    compile(P(4), depth+1);\n"
             "    return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    compile(P(1), depth+1);\n"
             "    compile(P(2), depth+1);\n"
             "    int DeclIDX = Declaration(depth+1, TRUE /* A, B, C form allowed */); //codegen(\"/*I %s:%d*/\",__FILE__,__LINE__);\n"
             "    {\n"
             "      int MoreDecls = P(3);  // ',' <READLINEP><DECLNAME> <DECLNAMES>, ''\n"
             "      while (P_alt(MoreDecls) == 0) {\n"
             "        codegen(\"; \");/*d*/\n"
             "        compile(P_P(MoreDecls, 3), depth+1); // <DECLNAME>\n"
             "        int NextDeclIDX = Declaration(depth+1, TRUE /* A, B, C format allowed */); //codegen(\"/*J %s:%d*/\",__FILE__,__LINE__);\n"
             "        MoreDecls = P_P(MoreDecls, 4);\n"
             "      }\n"
             "    }\n"
             "    codegen(\";\\n\");\n"
             "    return -1;\n"
             "  } else if (alt == 1) {\n"
             "    IsArray = ARRAY;\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "    return -1;\n"
             "  }\n",
  L"\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[IsArray=%d]\", IsArray);\n"
             "  if (alt == 0) {\n"
             "    compile(P(1), depth+1); // <Opt_AN_N_type>\n"
             "    compile(P(2), depth+1); // <VSPECQ>\n"
             "    compile(P(3), depth+1); // <DECLNAME>\n"
             "    compile(P(4), depth+1); // <ALIAS>\n"
             "    \n"
             "    int OwnDecIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */);  //codegen(\"/*K %s:%d*/\",__FILE__,__LINE__);\n"
             "    compile(P(5), depth+1); // <Opt_Const_Init>\n"
             "    codegen(\";\\n\");\n"
             " \n"
             "    int MoreDecTuple = compile(P(6), depth+1); // <RESTOFOWNDEC>\n"
             "  } else if (alt == 1) {\n"
             "    IsArray = ARRAY; // Must be set to SCALAR somewhere for default.\n"
             "    compile(P(2), depth+1); // <ARRAYFORMAT>\n"
             "    compile(P(3), depth+1); // <DECLNAME>\n"
             "    compile(P(4), depth+1); // <ALIAS>\n"
             "    ArrayDims = 0;\n"
             "    int BPairTuple = compile(P(5), depth+1); // <BPAIR>\n"
             "    int ArrayDeclIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */);  //codegen(\"/*L %s:%d*/\",__FILE__,__LINE__);\n"
             "    int ConstListTuple = compile(P(6), depth+1); // <CONSTLIST>\n"
             "    codegen(\";\\n\");\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    // only for formatting... compile(P(2), depth+1); // <READLINEP>\n"
             "    compile(P(3), depth+1); // <DECLNAME>\n"
             "    compile(P(4), depth+1); // <ALIAS>\n"
             "    (void)Declaration(depth+1, FALSE /* A, B, C form not allowed */); //codegen(\"/*M %s:%d*/\",__FILE__,__LINE__);\n"
             "    compile(P(5), depth+1); // <Opt_Const_Init>\n"
             "    codegen(\"; \");\n"
             "    \n"
             "    compile(P(6), depth+1); //<RESTOFOWNDEC>\n"
             "  } else if (alt == 1) {\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  switch (alt) {\n"
             "  case 0: Area = OWN;                    break;\n"
             "  case 1: Area = EXTDATA;                break;\n"
             "  case 2: Area = EXTDATA;  Spec = SPEC;  break;\n"
             "  case 3: Area = CONSTANT;               break;\n"
             "  case 4: Area = CONSTANT;               break;\n"
             "  }\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[Area=%d]\", Area );\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    int SaveOperBottom = OperStack_bottom; OperStack_bottom = OperStack_nextfree;\n"
             "    int SaveDataBottom = DataStack_bottom; DataStack_bottom = DataStack_nextfree;\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "    compile(P(4), depth+1);\n"
             "    t[1] = ExPop();\n"
             "    codegen(\" = \");\n"
             "    generate_c(t[1], depth+1);\n"
             "    t[2] = -1;\n"
             "    DataStack_bottom = SaveDataBottom; OperStack_bottom = SaveOperBottom;\n"
             "    return -1; // t[0] = MkInit(AST_INITIALISER, /*alt*/0, /*count*/2, t);\n"
             "  }\n",
  L"  //  <---------------------------------------------------------------- FIX!  TO DO\n"
             "  // TO DO.\n",
  L"\n"
             "  {\n"
             "    int SaveOperBottom = OperStack_bottom; OperStack_bottom = OperStack_nextfree;\n"
             "    int SaveDataBottom = DataStack_bottom; DataStack_bottom = DataStack_nextfree;\n"
             "    compile(P(1), depth+1);\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "\n"
             "// TO DO: these are crashing with an array bound exceeded:\n"
             "\n"
             "    if (P_alt(P(4)) == 0) { // initialiser includes a repeat count:       // P<REPFACT>          = '('<STAROREXPR>')', ''\n"
             "      t[1] = ExPop();\n"
             "\n"
             "      //int Count = compile(P(4), depth+1);  // We need this as an actual number, not as a string representing an expression.\n"
             "      //t[2] = ExPop();\n"
             "      codegen(\"[ LOW ... HIGH ] = \"); generate_c(t[2], depth+1);\n"
             "\n"
             "      generate_c(t[1], depth+1);\n"
             "    } else {\n"
             "      t[1] = ExPop();\n"
             "      generate_c(t[1], depth+1);\n"
             "      t[2] = -1;\n"
             "    }\n"
             "    DataStack_bottom = SaveDataBottom; OperStack_bottom = SaveOperBottom;\n"
             "    return t[0] = -1; // MkInit(AST_INITIALISER, /*alt*/0, /*count*/2, t);\n"
             "  }\n",
  L"  //  <----------------------- FIX!  TO DO\n"
             "  if (alt == 0) {\n"
             "    codegen(\" = {\"); if (P_alt(P(2)) == 0) codegen(\"\\n\"); // copy newline if present in original\n"
             "    compile(P(3), depth+1);\n"
             "    compile(P(4), depth+1);\n"
             "    codegen(\"}\");\n"
             "    return -1;\n"
             "  } else return -1;\n",
  L"  //  <----------------------- FIX!  TO DO\n"
             "  if (alt == 0) {\n"
             "    codegen(\", \"); if (P_alt(P(2)) == 0) codegen(\"\\n\"); // copy newline if present in original\n"
             "    compile(P(3), depth+1);\n"
             "    compile(P(4), depth+1);\n"
             "    return -1;\n"
             "  } else return -1;\n",
  L"\n"
             "  // Shouldn't this allow %on %event * %start ???\n"
             "  if (debug_compiler) fprintf(stderr, \"[on event block]\\n\");\n"
             "  codegen(\"if (_imp_onevent(\");\n"
             "  generate_c(compile(P(3),depth+1), depth+1);\n"
             "  generate_c(compile(P(4),depth+1), depth+1);\n"
             "  codegen(\")) {\\n\");\n"
             "  push_ctrl(FINISH);\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\", \");\n"
             "    generate_c(compile(P(2), depth+1), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "    compile(P(4), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L" return -1; ",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\", \");\n"
             "    generate_c(compile(P(2), depth+1), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  {\n"
             "  int EXPECTED = pop_ctrl();\n"
             "  if (EXPECTED != REPEAT && EXPECTED != REPEATUNTIL && EXPECTED != REPEATPUSHEDUNTIL) {\n"
             "    fprintf(stderr, \"\\n* %%REPEAT not expected here (expecting %s)\\n\", ctrl_debug[EXPECTED]); exit(1);\n"
             "  }\n"
             "\n"
             "  if (EXPECTED == REPEATUNTIL) {\n"
             "    // if this was a %cycle ... %repeat with a possible %until after it, the\n"
             "    // %cycle could have been emitted as \"do {\" and this code must be either \"} until (cond)\" or \"} while (1)\"\n"
             "\n"
             "    if (alt == 0) {\n"
             "      if (debug_compiler) fprintf(stderr, \"[until condition]\\n\");\n"
             "      codegen(\"} until \");\n"
             "      compile(P(2), depth+1);\n"
             "      codegen(\";\");\n"
             "    } else if (alt == 1) {\n"
             "      codegen(\"} while (1);\");\n"
             "    }\n"
             "  } else if (EXPECTED == REPEATPUSHEDUNTIL) {\n"
             "    int SavedUntilCond = pop_ctrl(); // mildly dirty pushing both codes, and conditions, on the same stack...\n"
             "\n"
             "    if (alt == 0) {\n"
             "      fprintf(stderr, \"? WARNING: Having an %%until at both ends of a %%cycle is a bit dodgy, don't you think?\\n\");\n"
             "      // %until cond1 %cycle ... %repeat %until cond2?!\n"
             "      codegen(\"} while (!\");\n"
             "      compile(P(2), depth+1);  // conds include the brackets\n"
             "      codegen(\" && !\"); // or \"||\" ?\n"
             "      compile(SavedUntilCond, depth+1);\n"
             "      codegen(\");\");\n"
             "    } else if (alt == 1) {\n"
             "      // %until cond %cycle ... %repeat\n"
             "      if (debug_compiler) fprintf(stderr, \"[deferred condition from earlier until cycle]\\n\");\n"
             "      codegen(\"} while (!\");\n"
             "      compile(SavedUntilCond, depth+1);\n"
             "      codegen(\");\");\n"
             "    }\n"
             "  } else if (EXPECTED == REPEAT) {\n"
             "\n"
             "    if (alt == 0) {\n"
             "      fprintf(stderr, \"* %%UNTIL is not allowed at the end of a %%WHILE or %%FOR cycle.\\n\"); exit(1);\n"
             "    }\n"
             "    codegen(\"}\"); // no semicolon\n"
             "  }\n"
             "  return -1;\n"
             "  }\n",
  L"\n"
             "  {\n"
             "    int EXPECTED = pop_ctrl();\n"
             "\n"
             "    if (alt == 0) {\n"
             "      if (debug_compiler) fprintf(stderr, \"[FINISHELSEQ]\\n\");\n"
             "      codegen(\" else \");\n"
             "    } else codegen(\"\\n\"); // following a %finish\n"
             "  }\n"
             "  // recurse on AFTERELSE  <----------------------------------------------------------------- FIX!  TO DO\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[afterelse: start]\\n\");\n"
             "    push_ctrl(FINISH);\n"
             "    codegen(\"{\\n\");\n"
             "    return -1;\n"
             "  } else if (alt == 1) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[afterelse: if or unless <COND> start]\\n\");\n"
             "    //push_ctrl(FINISHELSE);\n"
             "    compile(P(1), depth+1);\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "    return -1;\n"
             "  } else {\n"
             "    if (debug_compiler) fprintf(stderr, \"[afterelse: <UI>]\\n\");\n"
             "    // recurse\n"
             "  }\n"
             "  // recursively handle alt 2  <------------------------------------------------------------- FIX!  TO DO\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[ELSEQ]\\n\");\n"
             "    codegen(\" else \");\n"
             "    // recurse   <--------------------------------------------------------------------------- FIX!  TO DO\n"
             "  }\n",
  L"\n"
             "  if (alt < 2) {\n"
             "    if (debug_compiler) fprintf(stderr, \"[then start]\\n\");\n"
             "    push_ctrl(FINISHELSE);\n"
             "    codegen(\" {\\n\");\n"
             "    return -1;\n"
             "  } else {\n"
             "    if (debug_compiler) fprintf(stderr, \"[then UI]\\n\");\n"
             "    // recursively handle the rest   <--------------------------------------------------------- FIX!  TO DO\n"
             "  }\n",
  L"\n"
             "  Linkage = alt+1;\n"
             "  // %routine without one of the above is auto if nested but not sure what at external level if no %external keyword.\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[Linkage=%d]\", Linkage);\n"
             "  return -1;\n",
  L"\n"
             "  {\n"
             "\n"
             "    // All that has been done prior to this is <INITDECS> and Object = RECORDFORMAT;\n"
             "\n"
             "    int RecFmtVarIDX;  // a VarDecl index\n"
             "    int RecFmtTypeIDX; // a TypeDecl index\n"
             "    if (debug_compiler) fprintf(stderr, \"[record format statement]\\n\");\n"
             "    // Object = RECORDFORMAT; // already set in <record_format_declaration>\n"
             "    if (alt == 0) {\n"
             "      // \"spec\"<DECLNAME>\n"
             "      Spec = SPEC;\n"
             "      compile(P(2), depth+1); // set up the record format name\n"
             "      RTag = DTag; // tag of record format name\n"
             "\n"
             "      // Declaration() returns a VarDecl index, not a TypeDecl index!:\n"
             "      RecFmtVarIDX  = Declaration(depth+1, FALSE /* A, B, C form not allowed */);  // RecordFormatIDX recfm\n"
             "      RecFmtTypeIDX = VarDecl[RecFmtVarIDX].type;\n"
             "      \n"
             "      if (TypeDecl[RecFmtTypeIDX].recfm != -1) {\n"
             "        fprintf(stderr, \"? %%recordformat %ls is already defined.\\n\", Atom2WStr(DTag));\n"
             "      }\n"
             "      // We do not create the anonymous table just for the %spec - the absence (indicated by recfm == -1)\n"
             "      // is how we know the spec exists.\n"
             "\n"
             "      codegen(\"typedef struct \"); PrintAtom(RTag); codegen(\" \"); PrintAtom(RTag); codegen(\";\\n\");\n"
             "      RTag = -1;\n"
             "    } else {\n"
             "      Spec = NO_SPEC;\n"
             "      // <DECLNAME>'('<PUSHDECS><RFDEC><RESTOFRFDEC><ALTRFDEC><POPDECS>')'\n"
             "      compile(P(1), depth+1); // set up the record format name\n"
             "      RTag = DTag;\n"
             "\n"
             "      // Declaration() returns a VarDecl index, not a TypeDecl index!:\n"
             "      RecFmtVarIDX  = Declaration(depth+1, FALSE /* A, B, C form not allowed */);  // RecordFormatIDX recfm\n"
             "      RecFmtTypeIDX = VarDecl[RecFmtVarIDX].type;\n"
             "      if (TypeDecl[RecFmtTypeIDX].recfm != -1) {\n"
             "        fprintf(stderr, \"* %%recordformat %ls is already defined.\\n\", Atom2WStr(DTag));\n"
             "      }\n"
             "      if (TypeDecl[RecFmtTypeIDX].recfm == -1) {\n"
             "        TypeDecl[RecFmtTypeIDX].recfm = anonymous_table(); // now prepare the container for the fields.\n"
             "        RecFmTableIDX = TypeDecl[RecFmtTypeIDX].recfm;\n"
             "      }\n"
             "\n"
             "      // We have created a holder for the record format definition, which is probably added from within P<RFDEC>\n"
             "      // Add fields with: add_entry_to_anonymous_table(RecFmTableIDX, Atom2WStr(DTag), VarIDX)\n"
             "\n"
             "\n"
             "      codegen(\"typedef struct \"); PrintAtom(RTag); codegen(\" {\\n\");\n"
             "\n"
             "      compile(P(3), depth+1); // <PUSHDECS>, also calls <INITDECS>\n"
             "\n"
             "      // Get record fields\n"
             "      compile(P(4), depth+1); // <RFDEC>           <-- this adds the field to the container\n"
             "      compile(P(5), depth+1); // <RESTOFRFDEC>\n"
             "\n"
             "      // %OR% alternative record format\n"
             "      compile(P(6), depth+1); // <ALTRFDEC>\n"
             "    \n"
             "      compile(P(7), depth+1); // <POPDECS>\n"
             "      codegen(\"} \"); PrintAtom(RTag); codegen(\";\\n\");\n"
             "      RTag = -1; // Done compiling a recordformat\n"
             "\n"
             "      // TO DO: The compiled %recordformat (currently in RecFmtTypeIDX) has to be added to scope \"decl\" by name\n"
             "      // so that it can be found when a record of this format is declared.  (or scope \"recordformat\" if they are\n"
             "      // allowed a separate namespace - I think Imp80 and Imp77 differ in this respect).   (Unless Declaration()\n"
             "      // has already done this for us?)\n"
             "    }\n"
             "  }\n"
             "  return -1;\n",
  L" // <-- looks like an RFSTMNT must be for the variation: %record (%integer A,B) fred\n"
             "  if (alt == 0) {\n"
             "    // TO DO:\n"
             "    // We set RecordTypeIDX to the VarIDX corresponding to the previously declared %recordformat '<VARNAME>' -\n"
             "    // from that we get the 'recfm' field which is the index of the anonymous symbol table entry containing the fields.\n"
             "    int VarnameTuple = compile(P(1), depth+1);\n"
             "    RecordTypeIDX = P_P(VarnameTuple, 1);\n"
             "    // (might have been preferable to invent a <FORMATNAME> rather than use <VARNAME> which returns an RVALUE tuple.)\n"
             "  } else {\n"
             "    fprintf(stderr, \"* Inline record format references have not yet been implemented. You need to use a %%recordformat.\\n\");\n"
             "    RecordTypeIDX = -1;\n"
             "  }\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[more record format declarations]\\n\");\n"
             "  //compile(P(3), depth+1);  // <RFDEC>\n"
             "  //compile(P(4), depth+1);  // <RESTOFRFDEC>\n"
             "  //return -1;\n",
  L"\n"
             "  // I think that the second form is for a nested record declaration\n"
             "  if (debug_compiler) fprintf(stderr, \"[record format declaration (RFDEC)]\\n\");\n"
             "  // <-------------------------------- DROP THROUGH AND COMPILE\n"
             "  if (alt == 0) {\n"
             "    compile(P(1), depth+1);\n"
             "    Object = VAR;\n"
             "    Area = FIELD; // whatever the parent record's area may be once used in a record declaration\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "    int FieldVarIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */);\n"
             "    if (RecFmTableIDX != -1) {\n"
             "      add_entry_to_anonymous_table(RecFmTableIDX, Atom2WStr(DTag), FieldVarIDX);\n"
             "    } else {\n"
             "      fprintf(stderr, \"* no currently open %%recordformat into which field \\\"%ls\\\" can be added.\\n\", Atom2WStr(DTag));\n"
             "    }\n"
             "    codegen(\";\\n\");\n"
             "    return FieldVarIDX;   // TO DO: both these returns may need to be wrapped in some sort of AST list.  AST_RECORDFORMAT? AST_FIELD?\n"
             "  } else {\n"
             "    int FieldVarTuple = compile(P(2), depth+1);\n"
             "    // TO DO: compile(P(3), depth+1);\n"
             "    // TO DO: compile(P(4), depth+1);\n"
             "    return FieldVarTuple; // TO DO\n"
             "  }\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[record format element]\\n\");\n"
             "  // a declaration of a record field - more restrictive in what it can be compared to regular declarations\n"
             "  // <-------------------------------- DROP THROUGH AND COMPILE\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[alternative record format declaration]\\n\");\n"
             "  // <-------------------------------- DROP THROUGH AND COMPILE\n",
  L" return -1; ",
  L"\n"
             "  // The other call to <VAR> is as an operand in an EXPR where it is pushed\n"
             "  // onto the TORP stack in order to have operator precedence applied to the\n"
             "  // EXPR as a whole.  The call to <VAR> actually just puts it on the\n"
             "  // stack and returns -1.  So to get a <VAR> here we don't look at the\n"
             "  // *result* of calling <VAR> but rather we pop the unprocessed item off\n"
             "  // the stack that was placed there by <VAR>.\n"
             "\n"
             "  compile(P(1), depth+1); // Pushes VAR\n"
             "\n"
             "  Op_or_Data TOSPop = DataStack(--DataStack_nextfree); ; // NOT ExPop(). We're not applying precedence to a tree.\n"
             "  if (TOSPop.type != 'D') { fprintf(stderr, \"** Internal error in %s at line %d\\n\", __FILE__, __LINE__); exit(1); }\n"
             "  int Var = TOSPop.idx;\n"
             "\n"
             "  //Diagnose(\"Assignment or call: \", Var,0, debug_declarations);\n"
             "\n"
             "  if (alt == 0) { // <VAR><ASSOP><EXPR>  - Assignment\n"
             "    int Assop = compile(P(2), depth+1);\n"
             "    int Expr = compile(P(3), depth+1);\n"
             "    int Assign = Mk_AST_assignment(Var, Assop, Expr, depth);\n"
             "    generate_c(Assign, depth+1);\n"
             "  } else { // <VAR>  = Call\n"
             "    generate_c(Var, depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    return compile(P(2), depth+1); // AST_EXPR tuple.\n"
             "  } else {\n"
             "    return -1;\n"
             "  }\n",
  L"  // CHANGED FROM <APP> TO NEW <SWITCHIDX> to restrict to *one* index.\n"
             "                                              // changed <VARNAME> to <TAG> to inhibit NAME NOT SET errors\n"
             "  if (debug_compiler) fprintf(stderr, \"[go to]\\n\");\n"
             "  int SWIdx = compile(P(4), depth+1);\n"
             "  if (SWIdx != -1) {\n"
             "    // switch label destination\n"
             "    codegen(\"goto_switch(\");\n"
             "    //generate_c(compile(P(3), depth+1), depth+1);\n"
             "    int SwitchName = compile(P(3), depth+1); // VARNAME\n"
             "    if (P_P(SwitchName,1) == -1) {\n"
             "      codegen(\"* ERROR: %%switch %ls NOT DECLARED\\n\", wSVTag); // exit(1);\n"
             "    }\n"
             "    codegen(\"%ls, \", wSVTag);\n"
             "    generate_c(SWIdx, depth+1);  // If the constant is < 0 we want to output \"M<const>\" instead of \"-<const>\"\n"
             "    codegen(\")\");\n"
             "    //Diagnose(\"Switch index: \", SWIdx, 0, TRUE);\n"
             "  } else {\n"
             "    // plain goto\n"
             "    codegen(\"goto \");\n"
             "    //generate_c(compile(P(3), depth+1), depth+1);\n"
             "    int Label = compile(P(3), depth+1);\n"
             "    if (P_P(Label,1) == -1) {\n"
             "      // Simple Label has not yet been defined.  Add it to the name table as a %spec so it can be faulted if missing at the end of the program.\n"
             "\n"
             "      // Since I'm getting a NAME NOT SET start turn from lunarlander.imp, I suspect the check for a name\n"
             "      // in the symtab stuff is too low-level as it should not be faulting unknown labels.\n"
             "    }\n"
             "    codegen(\"%ls\", wSVTag);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[return]\\n\");\n"
             "  codegen(\"return\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[result]\\n\");\n"
             "\n"
             "  codegen(\"return \");\n"
             "  // Assop will modify the result, possibly with a cast (for <-) or removing an indirection (for ==)\n"
             "\n"
             "  // TO DO: possibly needs a new AST_RESULT tuple similar to AST_ASSIGN\n"
             "\n"
             "  // We need to determine the result type of this function, and use it in the AST_RESULT/AST_ASSIGN tuple to\n"
             "  // cause the appropriate cast if needed.\n"
             "\n"
             "  int Expr = compile(P(3), depth+1);\n"
             "  int Type = P_TYPEINFO(Expr);   // P_TYPEINFO(X) is the hidden type field of any AST expression\n"
             "  //Describe_Type(Type);\n"
             "  \n"
             "  generate_c(Expr, depth+1);\n"
             "\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[print diagnostics]\\n\");\n"
             "  codegen(\"assert(_IMP_MONITOR_)\"); // not ideal - assert will cause an exit from the program - %monitor should not.\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[stop program]\\n\");\n"
             "  codegen(\"exit(0)\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[signal event]\\n\");\n"
             "  codegen(\"_imp_signal(\");\n"
             "  generate_c(compile(P(3), depth+1), depth+1);\n"
             "  generate_c(compile(P(4), depth+1), depth+1);\n"
             "  codegen(\")\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[exit from cycle]\\n\");\n"
             "  codegen(\"break\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[continue at end of cycle]\\n\");\n"
             "  codegen(\"continue\");\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    compile(P(1), depth+1); // <UI> must be encapsulated in a {...} block if more than 1 statement\n"
             "    compile(P(2), depth+1);\n"
             "  } else if (alt == 1) { // PC_IU is just \"if\" or \"unless\"\n"
             "    compile(P(2), depth+1);\n"
             "    compile(P(3), depth+1);\n"
             "    compile(P(1), depth+1);\n"
             "    compile(P(4), depth+1);\n"
             "  } else if (alt == 2) { // but PC_WUF is a full while/until/for condition\n"
             "    if (P_alt(P(2)) == 0) {\n"
             "      compile(P(2), depth+1); // \"while\"<TOP_LEVEL_CONDITION>,\n"
             "      compile(P(1), depth+1); // <UI_BLOCK>\n"
             "    } else if (P_alt(P(2)) == 1) {\n"
             "      codegen(\"do \");\n"
             "      compile(P(1), depth+1); // <UI_BLOCK>\n"
             "      compile(P(2), depth+1); // \"until\"<TOP_LEVEL_CONDITION>,\n"
             "    } else {\n"
             "      compile(P(2), depth+1); // \"for\"<CYCPARM> {\n"
             "      compile(P(1), depth+1); // <UI_BLOCK>\n"
             "    }\n"
             "    compile(P(3), depth+1);\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    compile(P(1), depth+1); codegen(\"; \");\n"
             "    compile(P(2), depth+1);\n"
             "    return -1;\n"
             "  } else {\n"
             "    compile(P(1), depth+1);\n"
             "    codegen(\"; \");\n"
             "  }\n"
             "  return -1;\n",
  L"",
  L"\n"
             "  if (alt == 0) {\n"
             "    codegen(\"{ \");\n"
             "    compile(P(1), depth+1); codegen(\"; \");\n"
             "    compile(P(2), depth+1);\n"
             "    codegen(\" }\");\n"
             "    return -1;\n"
             "  } else {\n"
             "    compile(P(1), depth+1); codegen(\"; \");\n"
             "  }\n"
             "  return -1;\n",
  L"",
  L"",
  L"",
  L"",
  L"\n"
             "  if (debug_compiler || debug_declarations) fprintf(stderr, \"[label]\\n\");\n"
             "\n"
             "  // MAYBE USE <VARNAME> RATHER THAN <DECLNAME> and handle declaration here rather than in Declaration(depth+1, ) since labels\n"
             "  // need to be handled a bit differently from variables?  Yeah, making that change... And indeed later changed <VARNAME> to\n"
             "  // <TAG> because varname caused a NAME NOT SET error\n"
             "\n"
             "  // Note that labels are currently *not* having a ';' added after them.  This may be OK but my previous imptoc\n"
             "  // inserted them and I suspect for a reason, though right now I can't remember what that reason might have been.\n"
             "  // I'll add the semicolons back in later if some circumstance arises where the lack of them causes problems.\n"
             "\n"
             "  int LabelTag;\n"
             "  int SwitchName = compile(P(1), depth+1); LabelTag = VTag;\n"
             "  if (alt < 2 && P_P(SwitchName, 1) == -1) {\n"
             "    fprintf(stderr, \"* ERROR: %%switch %ls NOT DECLARED\\n\", wSVTag); // exit(1);\n"
             "  //} else if (alt < 2 && P_P(SwitchName, 1) != -1) {\n"
             "  //  fprintf(stderr, \"* ERROR: SWITCH LABEL %d SET TWICE\\n\", TO DO!); // exit(1);\n"
             "  } else if (alt == 2 && P_P(SwitchName, 1) != -1) {\n"
             "    fprintf(stderr, \"* ERROR: LABEL %ls SET TWICE\\n\", wSVTag); // exit(1);\n"
             "  }\n"
             "  if (alt == 0) {\n"
             "    Object = VAR; // SWITCHDEFN;  no, SWITCHDEFN is for %switch fred(1:10)\n"
             "    Basetype = SWITCHLABEL;\n"
             "    codegen(\"%s_default:\", Atom2Str(LabelTag));\n"
             "  }  else if (alt == 1) {\n"
             "    Object = VAR; // SWITCHDEFN;\n"
             "    Basetype = SWITCHLABEL;\n"
             "    codegen(\"%s_\", Atom2Str(LabelTag));\n"
             "    Cpool_nextfree = 0; // Reset\n"
             "    Start_Divert_C_to_Cpool();\n"
             "    int len = generate_c(compile(P(3), depth+1), depth+1); // NO: needs to be a decimal constant with special handling for negative numbers.\n"
             "    End_Divert_C_to_Cpool();\n"
             "    if (Cpool(0) == '-') {\n"
             "      codegen(\"M%s\", &Cpool(1));\n"
             "    } else {\n"
             "      codegen(\"%s\", &Cpool(0));\n"
             "    }\n"
             "    Cpool_nextfree = 0; // return unused space.\n"
             "    codegen(\":\");\n"
             "  } else {\n"
             "\n"
             "    // TO DO: See lunarlander.imp - I'm getting a NAME NOT SET with \"start turn\"\n"
             "\n"
             "    Object = VAR; // LABELDEFN; no, LABELDEFN is for %label Fred\n"
             "    Basetype = JUMPLABEL;\n"
             "    codegen(\"%s: \", Atom2Str(LabelTag));  // TO DO: This outputs a ' ' after the tag.  We should suppress the space at the\n"
             "                                                 // point where it was added and add any needed spaces at the point of instantiation.\n"
             "  }\n"
             "  // PENDING! (TO DO) - the declaration code for actual labels (rather than declaration of labels) is currently\n"
             "  // non-existent and calling Declaration(depth+1, ) below outputs random crap.  I'm not 100% sure that I should even\n"
             "  // be using the 'Declaration(depth+1, )' method to handle the labels themselves.  Maybe it would be more sensible to\n"
             "  // put that code here in the grammar where the labels are instanced.\n"
             "  \n"
             "  // TO DO: (void)Declaration(depth+1, FALSE /* A, B, C form not allowed */);\n"
             "  \n"
             "  return -1;\n"
             "\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[if or unless]\\n\");\n"
             "  //  <-------------------------------------------------------------------------- FIX!  TO DO\n",
  L"\n"
             "  if (alt == 0) {\n"
             "    push_ctrl(REPEAT); // for loop\n"
             "    compile(P(1), depth+1);\n"
             "  } else if (alt == 1) {\n"
             "    push_ctrl(REPEATUNTIL); // plain %cycle\n"
             "    codegen(\"do \");\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[cycle]\\n\");\n"
             "\n"
             "  if (alt == 0) {\n"
             "\n"
             "    compile(P(2), depth+1);  // if <Opt_CYCPARM> was empty, we should push_ctrl(REPEATUNTIL);, otherwise...\n"
             "  } else {\n"
             "\n"
             "    if (P_alt(P(1)) == 1) { // %until\n"
             "      fprintf(stderr, \"? Please recode the source file so that loops with \\\"%%until <cond> %%cycle ... %%repeat\\\"\"\n"
             "                      \"are replaced by \\\"%%cycle ... %%repeat %%until <cond>\\\"\");\n"
             "      // In the meantime we just lose the condition at the top and hopefully at some point in the near\n"
             "      // future I'll have stacked it and will be able to pop it off the stack on the %repeat ...\n"
             "      codegen(\"/* Deferred: \"); // Because compile() currently outputs the condition as well as returning it as a tuple.\n"
             "      int UntilCond =  compile(P(1), depth+1);\n"
             "      codegen(\" */\");\n"
             "      codegen(\"\\ndo \");\n"
             "      push_ctrl(UntilCond);               // Save the condition for testing at the end of the loop\n"
             "      push_ctrl(REPEATPUSHEDUNTIL);       // %until not allowed after %repeat\n"
             "    } else {\n"
             "      push_ctrl(REPEAT);       // %until not allowed after %repeat\n"
             "      compile(P(1), depth+1);\n"
             "    }\n"
             "  }\n"
             "  codegen(\"{\\n\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[repeat]\\n\");\n"
             "\n"
             "  // <RESTOFREPEAT> handles the control stack and whether a %until is allowed\n"
             "  compile(P(2), depth+1);\n"
             "  compile(P(3), depth+1);\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[auto declaration]\\n\");\n"
             "  compile(P(1), depth+1); Object = VAR; Linkage = AUTO; Proc = NONE;\n"
             "  compile(P(2), depth+1);\n"
             "  compile(P(3), depth+1);\n"
             "  compile(P(4), depth+1);\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[procedure declaration]\\n\");\n"
             "\n"
             "  compile(P(1), depth+1);  Object = CODE;\n"
             "\n"
             "  compile(P(2), depth+1); // SEX\n"
             "  compile(P(3), depth+1); // RT\n"
             "  compile(P(4), depth+1); // Set up Spec and flag for <DOWN?>\n"
             "  compile(P(6), depth+1); // ALIAS\n"
             "  compile(P(5), depth+1); // DECLNAME reordered so name handled after params set up\n"
             "                          // (but work to be done to associate param list with Rt)\n"
             "\n"
             "  ParentVarIDX = Declaration(depth+1, FALSE /* A, B, C form not allowed */); //codegen(\"/*P %s:%d*/\",__FILE__,__LINE__); // BUT TO DO: NEEDS MORE WORK\n"
             "  ParentTypeIDX = VarDecl[ParentVarIDX].type;\n"
             "  \n"
             "  codegen(\"(\");\n"
             "  push_scope_level(); // aka <DOWN>.  popped at end of param list if a spec, otherwise on %end of routine. Ensures params are like locals of procedure.\n"
             "  compile(P(7), depth+1); // FPP (or void if none)\n"
             "  codegen(\")\");\n"
             "  if (Spec == SPEC) {\n"
             "    pop_scope_level(); // aka <UP>\n"
             "    codegen(\";\\n\");\n"
             "  } else {\n"
             "    // pop scope level on %end of procedure.\n"
             "    // Parameters have been declared as if within procedure body.\n"
             "    codegen(\" {\\n\");\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[own/external/const declaration]\\n\");\n"
             "\n"
             "  compile(P(1), depth+1);   Object = VAR;\n"
             "\n"
             "  compile(P(2), depth+1);\n"
             "  compile(P(3), depth+1);\n"
             "  compile(P(4), depth+1);\n"
             "  compile(P(5), depth+1);\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[record format declaration]\\n\");\n"
             "  compile(P(1), depth+1);\n"
             "  // IsFormat = IS_FORMAT; ... deprecated\n"
             "  Object = RECORDFORMAT;\n"
             "  compile(P(4), depth+1);\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[end of something]\\n\");\n"
             "  //  <-------------------------------------------------------------------------- FIX!  TO DO\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[push an include file]\\n\");\n"
             "  // TO DO: not handled in previous imptoc - it was assumed that incuded files could be translated separately\n"
             "  // but that is not correct.  Compilation of include files depends on what has gone before.  So they have to\n"
             "  // be translated in the context of the parent file, so as well as pushing the input file on the include stack,\n"
             "  // we have to switch output to the .h.tmp version of the .inc file, and switch back to the current context on EOF\n"
             "  // (the mechanics of handling include files was already worked out for takeon where .g files can contain I<...> )\n"
             "  // Note that include files will probably be handled like C and allow a search path via a -I option.\n"
             "  codegen(\"\\n#include \\\"...\\\"\\n\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[begin block]\\n\");\n"
             "  if (ctrl_depth() <= 0) {\n"
             "    push_ctrl(ENDOFPROGRAM);\n"
             "    codegen(\"\\nint _imp_mainep(int _imp_argc, char **_imp_argv) {\\n\");\n"
             "  } else {\n"
             "    push_ctrl(END); // Also pushed at the start of a rt/fn/map\n"
             "    codegen(\"\\n{ /* %%begin block */\\n\");\n"
             "  }\n"
             "  compile(P(2), depth+1); // ARGH! This was missing and nested scopes were not being created!\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_declarations > 1) fprintf(stderr, \"[one switch declaration]\");\n"
             "  // Will require *either* exbedded code at end of scope block with actual switch statement and gotos,\n"
             "  // *or* we have to backpatch this declaration with assignment of an array of labels (in the style &&labname)\n"
             "  // *or* we generate *all* labels at the point of declaration but output the unused ones at the end of\n"
             "  // the scope block with a jump to the default if given, or an error message if not. (i.e. the BADSWITCH()\n"
             "  // code from before)\n"
             "  // We do at a minimum require a hook between closing a scope and executing pending code.\n"
             "\n"
             "/*\n"
             "  This is what the last iteration of imptoc did with switch declarations:\n"
             "\n"
             "        static int Bip_sw;\n"
             "        static void *Bip[40 / *999:1038* /] = {\n"
             "            &&Bip_999,  &&Bip_1000, &&Bip_1001, &&Bip_1002, &&Bip_1003,\n"
             "            &&Bip_1004, &&Bip_1005, &&Bip_1006, &&Bip_1007, &&Bip_1008,\n"
             "            &&Bip_1009, &&Bip_1010, &&Bip_1011, &&Bip_1012, &&Bip_1013,\n"
             "            &&Bip_1014, &&Bip_1015, &&Bip_1016, &&Bip_1017, &&Bip_1018,\n"
             "            &&Bip_1019, &&Bip_1020, &&Bip_1021, &&Bip_1022, &&Bip_1023,\n"
             "            &&Bip_1024, &&Bip_1025, &&Bip_1026, &&Bip_1027, &&Bip_1028,\n"
             "            &&Bip_1029, &&Bip_1030, &&Bip_1031, &&Bip_1032, &&Bip_1033,\n"
             "            &&Bip_1034, &&Bip_1035, &&Bip_1036, &&Bip_1037, &&Bip_1038,\n"
             "        };\n"
             "\n"
             "        goto *Bip[Bip_sw = (Item)-999];\n"
             "\n"
             "or\n"
             "\n"
             "        static int S_sw;\n"
             "        static void *S[5 / *1:5* /] = {\n"
             "            &&S_1, &&S_2, &&S_3, &&S_4, &&S_default,\n"
             "        };\n"
             "        goto *S[S_sw = (A[P + 1]) - 1];\n"
             "        \n"
             "        S_4:;\n"
             "            // code for each switch entry\n"
             "\n"
             "Then if there is no S_default created due to a S(*): in Imp, add this at the foot of the procedure or block:\n"
             "\n"
             "        goto S_skip;\n"
             "        S_default:\n"
             "            fprintf(stderr, \"\\nSwitch label 'S(%d):' not set in %s\\n\", S_sw + 1,\n"
             "                    __PRETTY_FUNCTION__);\n"
             "            fflush(stderr);\n"
             "            abort();\n"
             "        S_skip:;\n"
             "\n"
             "although the goto above would be better coded as:\n"
             "\n"
             "        S_sw = (A[P + 1]) - 1;\n"
             "        if (1 <= S_sw && S_sw <= 5) goto *S[S_sw]; else goto S_default;\n"
             "\n"
             "and of course there is the saving of __LINE__ and __FILE__ to be done, using the values\n"
             "from the Imp source where the ->S(A[P + 1]) occurred.  Which could probably best be\n"
             "implemented using a macro?  e.g. goto_switch(S, A[P + 1]);\n"
             "Perhaps:\n"
             "\n"
             "#define goto_switch(S, N) \\\n"
             "  do { \\\n"
             "    S ## _sw = N; \\\n"
             "    _sw_line = __LINE__; \\\n"
             "    _sw_file = __FILE__; \\\n"
             "    if (S ## _low <= S ## _sw && S ## _sw <= S ## _high) \\\n"
             "      goto *S[S ## _sw - S ## _low]; \\\n"
             "    else \\\n"
             "      goto S ## _default; \\\n"
             "  } while (0)\n"
             "\n"
             "Initialising the switch without exbedding code is going to be tricky.  For now the best I can think of is a nested procedure call:\n"
             "\n"
             "At the switch declaration:\n"
             " \n"
             "#define declare_switch(S, low, high) static int S ## _sw; const int S ## _low = low, S ## _high = high; static void **S; auto void init_ ## S(void); init_ ## S();\n"
             "\n"
             "then at the end of the block:\n"
             "\n"
             "void init_S(void) { const static void *_S[5 / * 1:5 * /] = {\n"
             "  &&S_1, &&S_2, &&S_3, &&S_4, &&S_default,\n"
             "}; S = _S; }\n"
             "\n"
             "So to do all this we need to keep track of the switch bounds (associated with the switch name), and\n"
             "an array of whether each label is present (low:high) (plus a separate one for default:) stored in\n"
             "the var information for that name.\n"
             "\n"
             "I still need a good mechanism to trigger code generation on popping the scope at the end of a rt/fn/map/begin block.\n"
             "\n"
             "*/\n"
             "\n"
             "  // <INITDECS><DECLNAME><DECLNAMES>'('<EXPR>':'<EXPR>')'\n"
             "  \n"
             "  compile(P(1), depth+1); // <INITDECS>\n"
             "  Object = SWITCHDEFN;\n"
             "  IsArray = ARRAY;\n"
             "  Basetype = SWITCHLABEL;\n"
             "  Area = CONSTANT;\n"
             "  // NameInfo under consideration. May depend on whether the\n"
             "  // switch label is implemented as a pointer to an array or\n"
             "  // the array itself - exbedded array declaration or placed\n"
             "  // at end of the block?\n"
             "\n"
             "  // LIKE BPAIR BUT ONE-DIMENSION ONLY:\n"
             "  int LowIDX = compile(P(5), depth+1);\n"
             "  int HighIDX = compile(P(7), depth+1);\n"
             "  AddDims(LowIDX, HighIDX);\n"
             "\n"
             "  compile(P(2), depth+1); // NAME\n"
             "  int SwitchDeclIDX = Declaration(depth+1, FALSE /* switch declarations cannot be A, B, C(L:H) - they must all be handled separately. */);\n"
             "  \n"
             "  int next_name = P(3);\n"
             "  while (next_name != -1 && P_alt(next_name) == 0) {\n"
             "    // replace the name but keep the other parameters the same:\n"
             "    compile(P_P(next_name, 3), depth+1); // ',' <READLINEP><DECLNAME> <DECLNAMES>, ''\n"
             "    int MoreSwitchDeclIDX = Declaration(depth+1, FALSE /* switch declarations cannot be A, B, C(L:H) - they must all be handled separately. */);\n"
             "    next_name = P_P(next_name, 4);\n"
             "  }\n",
  L"",
  L"",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[turn on listing]\\n\");\n"
             "  return -1;\n",
  L"\n"
             "  {\n"
             "    if (debug_compiler) fprintf(stderr, \"[(finish) else (start)]\\n\");\n"
             "    int EXPECTED = pop_ctrl();\n"
             "    if (EXPECTED == ELSE || EXPECTED == FINISHELSE) {\n"
             "      push_ctrl(FINISH);\n"
             "      codegen(\"} else {\");           // NOTE: a plain \"%else\" represents \"%finish %else %start\"\n"
             "    } else {\n"
             "      // TO DO:\n"
             "      \n"
             "      // And today's exercise is... try to remember how to extract the line/column from the \"else\" symbol\n"
             "      // to report the error better :-/\n"
             "      \n"
             "      fprintf(stderr, \"\\n* Spurious %%ELSE\\n\"); exit(1);\n"
             "    }\n"
             "  }\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[finish_opt_else]\\n\");\n"
             "  {\n"
             "    if (ctrl_depth() <= 1) {\n"
             "      fprintf(stderr, \"\\n* Spurious %%FINISH\\n\"); exit(1); // at least until we get around to handling ERCC conditional compilation tests\n"
             "    }\n"
             "    int EXPECTED = pop_ctrl();\n"
             "    codegen(\"\\n}\");\n"
             "    if (EXPECTED == FINISH) {\n"
             "      push_ctrl(ELSE);\n"
             "    } else if (EXPECTED == FINISHELSE) {\n"
             "      push_ctrl(ELSE);\n"
             "    } else {\n"
             "      fprintf(stderr, \"\\n* %%FINISH not expected (expecting %s)\\n\", ctrl_debug[EXPECTED]); exit(1);\n"
             "    }\n"
             "    // recurse on FINISHELSEQ\n"
             "  }\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[embedded assembly code]\\n\");\n"
             "  codegen(\"asm(/*to do*/)\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[trusted program pragma]\\n\");\n"
             "  return -1;\n",
  L"\n"
             "  if (debug_compiler) fprintf(stderr, \"[use this routine for main()]\\n\");\n"
             "\n"
             "// TO DO: Declaration()\n"
             "  return -1;\n",
  L"\n"
             "  // pragma?\n"
             "  if (debug_compiler) fprintf(stderr, \"[%%control - fine control over compiler options]\\n\");\n",
  L"\n"
             "  // print a semicolon after certain statements. This is for C's benefit and only really an issue\n"
             "  // when generating C on the fly from compile() statements.  Once these have moved to AST_* tuples\n"
             "  // and are generated *after* parsing, there will be no need for this here.\n"
             "  codegen(\"/*5*/; \");\n"
             "  return -1;\n",
  L"\n"
             "  switch (alt) {\n"
             "  case 0: // <Labels>\n"
             "  case 1: // <Unconditional_Instructions>\n"
             "  case 2: // <Comment><S>\n"
             "  case 3: // <If_or_Unless>\n"
             "  case 4: // <start_of_cycle>\n"
             "  case 5: // <end_of_cycle>\n"
             "  case 6: // <regular_declaration>\n"
             "  case 7: // <various_ends>\n"
             "  case 8: // <record_format_declaration>\n"
             "  case 9: // <proc_declaration>\n"
             "  case 10: // <own_or_external_declaration>\n"
             "  case 11: // <include_file>\n"
             "  case 12: // <begin_block>\n"
             "  case 13: // <on_event_block>\n"
             "  case 14: // <switch_declaration>\n"
             "  case 15: // <list_on>\n"
             "  case 16: // <else>\n"
             "  case 17: // <finish_opt_else>\n"
             "  case 18: // <embedded_assembler> <PSEMI>\n"
             "  case 19: // <trusted> <PSEMI>\n"
             "  case 20: // <mainep> <PSEMI>\n"
             "  case 21: // <control> <PSEMI>\n"
             "  case 22: // <S>\n"
             "    break;\n"
             "  default:\n"
             "    fprintf(stderr, \"** Error: STATEMENT.alt = %d\\n\", alt);\n"
             "    break;\n"
             "  }\n"
             "  // drop through and return a <STATEMENT> to P<SS>\n",
};

const wchar_t *keyword[NUM_KEYWORDS] = {
  L"comment",
  L"if",
  L"unless",
  L"while",
  L"until",
  L"for",
  L"alias",
  L"integer",
  L"real",
  L"long",
  L"byte",
  L"string",
  L"half",
  L"short",
  L"record",
  L"routine",
  L"fn",
  L"map",
  L"function",
  L"name",
  L"array",
  L"ofprogram",
  L"ofperm",
  L"offile",
  L"oflist",
  L"format",
  L"not",
  L"and",
  L"or",
  L"spec",
  L"own",
  L"external",
  L"extrinsic",
  L"constant",
  L"const",
  L"on",
  L"start",
  L"event",
  L"else",
  L"then",
  L"system",
  L"dynamic",
  L"prim",
  L"perm",
  L"return",
  L"result",
  L"monitor",
  L"stop",
  L"signal",
  L"exit",
  L"continue",
  L"cycle",
  L"repeat",
  L"end",
  L"include",
  L"begin",
  L"switch",
  L"list",
  L"finish",
  L"trustedprogram",
  L"mainep",
  L"control",
};
const wchar_t *regexps[NUM_REGEXPS] = {
  L"^[A-Z][A-Z0-9]*",
  L"^'([^\']|'')*'",
  L"^''''",
  L"^''",
  L"^'.'",
  L"^[0-9A-Za-z][0-9A-Za-z]*",
  L"^[MBKXRH]",
  L"^[0-9][0-9]*",
  L"^.",
};
const int gram[NUM_GRAMMAR ] = {
// P<PUSHDECS> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_INITDECS,
// P<POPDECS> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000000,
// P<INITDECS> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000000,
// P<nls> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  BIP_TYPE | B_nl,
  PHRASE_TYPE | G_nls,
  COUNT_OF_PHRASES | 0x000000,
// P<READLINEP> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_nls,
  COUNT_OF_PHRASES | 0x000000,
// P<S> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  BIP_TYPE | B_nl,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003b,
// P<DOWN> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000000,
// P<UP> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000000,
// P<SS> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  SEMANTIC_TYPE | S_init,
  SEMANTIC_TYPE | S_Imp77_stropping,
  PHRASE_TYPE | G_STATEMENTS,
  SEMANTIC_TYPE | S_terminate,
// P<BINOP> = ...;


  COUNT_OF_ALTS | 0x000011,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002b,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000026,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000021,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000021,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000021,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002f,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002f,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002f,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002e,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005c,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005c,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005e,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005e,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005e,
// P<OP> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_BINOP,
// P<MONOP> = ...;


  COUNT_OF_ALTS | 0x000005,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002b,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005c,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00007e,
  COUNT_OF_PHRASES | 0x000000,
// P<UOP> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_MONOP,
// P<ASSOP> = ...;


  COUNT_OF_ALTS | 0x000004,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
// P<COMP1> = ...;


  COUNT_OF_ALTS | 0x00000c,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000023,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000023,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000023,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005c,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
// P<COMP2> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_COMP1,
// P<VARNAME> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | NEGATED_PHRASE | G_CONST,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000000,
// P<TAG> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | NEGATED_PHRASE | G_CONST,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000000,
// P<BigCharConst> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000001,
// P<CharConst> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000004,
// P<OptExponent> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000040,
  PHRASE_TYPE | G_N,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000040,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  PHRASE_TYPE | G_N,
  COUNT_OF_PHRASES | 0x000000,
// P<OptDecimal> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002e,
  PHRASE_TYPE | G_N,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002e,
  COUNT_OF_PHRASES | 0x000000,
// P<Based> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000005,
// P<LITCONST> = ...;


  COUNT_OF_ALTS | 0x000007,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_N,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005f,
  PHRASE_TYPE | G_Based,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_N,
  PHRASE_TYPE | G_OptDecimal,
  PHRASE_TYPE | G_OptExponent,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002e,
  PHRASE_TYPE | G_N,
  PHRASE_TYPE | G_OptExponent,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_CharConst,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_ALIASTEXT,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000045,
  PHRASE_TYPE | G_ALIASTEXT,
  COUNT_OF_PHRASES | 0x000002,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000006,
  PHRASE_TYPE | G_BigCharConst,
// P<CONST> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_LITCONST,
// P<N> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000007,
// P<dq> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000022,
// P<any> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000008,
// P<stringchars> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_dq,
  PHRASE_TYPE | G_dq,
  PHRASE_TYPE | G_stringchars,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | NEGATED_PHRASE | G_dq,
  PHRASE_TYPE | G_any,
  PHRASE_TYPE | G_stringchars,
  COUNT_OF_PHRASES | 0x000000,
// P<ALIASTEXT> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_dq,
  PHRASE_TYPE | G_stringchars,
  PHRASE_TYPE | G_dq,
// P<semi> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003b,
// P<Comment> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000021,
  PHRASE_TYPE | G_TEXT,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00007c,
  PHRASE_TYPE | G_TEXT,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000000,
  PHRASE_TYPE | G_TEXT,
// P<TEXT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | NEGATED_PHRASE | G_S,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000008,
  PHRASE_TYPE | G_TEXT,
  COUNT_OF_PHRASES | 0x000000,
// P<Opt_Record_Field> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00005f,
  PHRASE_TYPE | G_TAG,
  PHRASE_TYPE | G_APP,
  PHRASE_TYPE | G_Opt_Record_Field,
  COUNT_OF_PHRASES | 0x000000,
// P<VAR> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_VARNAME,
  PHRASE_TYPE | G_APP,
  PHRASE_TYPE | G_Opt_Record_Field,
// P<OPERAND> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_VAR,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_CONST,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<CONSTVAR> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_VARNAME,
// P<COPERAND> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_CONSTVAR,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_CONST,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_CEXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<CEXPR> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_CTORP,
// P<CTORP> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_UOP,
  PHRASE_TYPE | G_COPERAND,
  PHRASE_TYPE | G_RESTOFCEXPR,
// P<STAROREXPR> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_UOP,
  PHRASE_TYPE | G_COPERAND,
  PHRASE_TYPE | G_RESTOFCEXPR,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
// P<TORP> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_UOP,
  PHRASE_TYPE | G_OPERAND,
  PHRASE_TYPE | G_RESTOFEXPR,
// P<EXPR> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_TORP,
// P<RESTOFEXPR> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_OP,
  PHRASE_TYPE | G_OPERAND,
  PHRASE_TYPE | G_RESTOFEXPR,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFCEXPR> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_OP,
  PHRASE_TYPE | G_COPERAND,
  PHRASE_TYPE | G_RESTOFCEXPR,
  COUNT_OF_PHRASES | 0x000000,
// P<APP> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_EXPR,
  PHRASE_TYPE | G_RESTOFAPP,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFAPP> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_EXPR,
  PHRASE_TYPE | G_RESTOFAPP,
  COUNT_OF_PHRASES | 0x000000,
// P<PC_IU> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000002,
// P<TOP_LEVEL_CONDITION> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_SC,
  PHRASE_TYPE | G_RESTOFCOND,
// P<CYCPARM> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000007,
  PHRASE_TYPE | G_VARNAME,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_EXPR,
// P<PC_WUF> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000003,
  PHRASE_TYPE | G_TOP_LEVEL_CONDITION,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000004,
  PHRASE_TYPE | G_TOP_LEVEL_CONDITION,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000005,
  PHRASE_TYPE | G_CYCPARM,
// P<ALIAS> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000006,
  PHRASE_TYPE | G_ALIASTEXT,
  COUNT_OF_PHRASES | 0x000000,
// P<DECLNAMES> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  COUNT_OF_PHRASES | 0x000000,
// P<DECLNAME> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | NEGATED_PHRASE | G_CONST,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000000,
// P<FULLTYPE> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000007,
  COUNT_OF_PHRASES | 0x000000,
// P<BTYPE> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000008,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000007,
// P<STRLEN> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_STAROREXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000000,
// P<XTYPE> = ...;


  COUNT_OF_ALTS | 0x000009,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000007,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000008,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000009,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000009,
  PHRASE_TYPE | G_BTYPE,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000009,
  PHRASE_TYPE | G_BTYPE,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000a,
  PHRASE_TYPE | G_FULLTYPE,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000b,
  PHRASE_TYPE | G_STRLEN,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000c,
  PHRASE_TYPE | G_FULLTYPE,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000d,
  PHRASE_TYPE | G_FULLTYPE,
  COUNT_OF_PHRASES | 0x000004,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000e,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_RFREF,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<RT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000f,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_XTYPE,
  PHRASE_TYPE | G_FM,
// P<FM> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000010,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000011,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000012,
// P<FP> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_PUSHDECS,
  PHRASE_TYPE | G_FPDEL,
  PHRASE_TYPE | G_POPDECS,
// P<FPDEL> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_XTYPE,
  PHRASE_TYPE | G_Opt_AN_N_type,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  COUNT_OF_PHRASES | 0x000005,
  PHRASE_TYPE | G_RT,
  PHRASE_TYPE | G_Opt_N_type,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  PHRASE_TYPE | G_FPP,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000013,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
// P<Opt_N_type> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000013,
  COUNT_OF_PHRASES | 0x000000,
// P<Opt_AN_N_type> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000014,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000013,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000013,
  COUNT_OF_PHRASES | 0x000000,
// P<FPP> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_FP,
  PHRASE_TYPE | G_RESTOFFPLIST,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000000,
// P<Opt_comma> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFFPLIST> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_Opt_comma,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_FP,
  PHRASE_TYPE | G_RESTOFFPLIST,
  COUNT_OF_PHRASES | 0x000000,
// P<ENDLIST> = ...;


  COUNT_OF_ALTS | 0x000005,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000015,
  PHRASE_TYPE | G_UP,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000016,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000017,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000018,
  SEMANTIC_TYPE | S_LISTOFF,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_UP,
// P<ARRAYFORMAT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000019,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFSC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_COMP2,
  PHRASE_TYPE | G_EXPR,
  COUNT_OF_PHRASES | 0x000000,
// P<SC> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_EXPR,
  PHRASE_TYPE | G_COMP1,
  PHRASE_TYPE | G_EXPR,
  PHRASE_TYPE | G_RESTOFSC,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_SC,
  PHRASE_TYPE | G_RESTOFCOND,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001a,
  PHRASE_TYPE | G_SC,
// P<RESTOFCOND> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001b,
  PHRASE_TYPE | G_SC,
  PHRASE_TYPE | G_RESTOFANDC,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001c,
  PHRASE_TYPE | G_SC,
  PHRASE_TYPE | G_RESTOFORC,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFANDC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001b,
  PHRASE_TYPE | G_SC,
  PHRASE_TYPE | G_RESTOFANDC,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFORC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001c,
  PHRASE_TYPE | G_SC,
  PHRASE_TYPE | G_RESTOFORC,
  COUNT_OF_PHRASES | 0x000000,
// P<Opt_RtFnMapSpec> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001d,
  COUNT_OF_PHRASES | 0x000000,
// P<VSPECQ> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001d,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFBPLIST> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000005,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  PHRASE_TYPE | G_EXPR,
  PHRASE_TYPE | G_RESTOFBPLIST,
  COUNT_OF_PHRASES | 0x000000,
// P<BPAIR> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000006,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  PHRASE_TYPE | G_EXPR,
  PHRASE_TYPE | G_RESTOFBPLIST,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<RESTOFARLIST> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_ADECLN,
  COUNT_OF_PHRASES | 0x000000,
// P<ADECLN> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  PHRASE_TYPE | G_BPAIR,
  PHRASE_TYPE | G_RESTOFARLIST,
// P<DECLN> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_Opt_AN_N_type,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000014,
  PHRASE_TYPE | G_ARRAYFORMAT,
  PHRASE_TYPE | G_ADECLN,
// P<OWNDEC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000006,
  PHRASE_TYPE | G_Opt_AN_N_type,
  PHRASE_TYPE | G_VSPECQ,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_ALIAS,
  PHRASE_TYPE | G_Opt_Const_Init,
  PHRASE_TYPE | G_RESTOFOWNDEC,
  COUNT_OF_PHRASES | 0x000006,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000014,
  PHRASE_TYPE | G_ARRAYFORMAT,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_ALIAS,
  PHRASE_TYPE | G_BPAIR,
  PHRASE_TYPE | G_CONSTLIST,
// P<RESTOFOWNDEC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000006,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_ALIAS,
  PHRASE_TYPE | G_Opt_Const_Init,
  PHRASE_TYPE | G_RESTOFOWNDEC,
  COUNT_OF_PHRASES | 0x000000,
// P<XOWN> = ...;


  COUNT_OF_ALTS | 0x000005,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001e,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001f,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000020,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000021,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000022,
// P<Opt_Const_Init> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  PHRASE_TYPE | G_UOP,
  PHRASE_TYPE | G_COPERAND,
  PHRASE_TYPE | G_RESTOFCEXPR,
  COUNT_OF_PHRASES | 0x000000,
// P<REPFACT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_STAROREXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000000,
// P<CONSTLISTITEM> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_UOP,
  PHRASE_TYPE | G_COPERAND,
  PHRASE_TYPE | G_RESTOFCEXPR,
  PHRASE_TYPE | G_REPFACT,
// P<CONSTLIST> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_CONSTLISTITEM,
  PHRASE_TYPE | G_ROCL,
  COUNT_OF_PHRASES | 0x000000,
// P<ROCL> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_CONSTLISTITEM,
  PHRASE_TYPE | G_ROCL,
  COUNT_OF_PHRASES | 0x000000,
// P<on_event_block> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000006,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000023,
  PHRASE_TYPE | G_PC_EVENTQ,
  PHRASE_TYPE | G_CEXPR,
  PHRASE_TYPE | G_RESTOFNLIST,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000024,
  PHRASE_TYPE | G_S,
// P<RESTOFNLIST> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_CEXPR,
  PHRASE_TYPE | G_RESTOFNLIST,
  COUNT_OF_PHRASES | 0x000000,
// P<PC_EVENTQ> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000025,
  COUNT_OF_PHRASES | 0x000000,
// P<Opt_EXPR> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_EXPR,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFREPEAT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000004,
  PHRASE_TYPE | G_TOP_LEVEL_CONDITION,
  COUNT_OF_PHRASES | 0x000000,
// P<FINISHELSEQ> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000026,
  PHRASE_TYPE | G_AFTERELSE,
  COUNT_OF_PHRASES | 0x000000,
// P<AFTERELSE> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000024,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_PC_IU,
  PHRASE_TYPE | G_TOP_LEVEL_CONDITION,
  PHRASE_TYPE | G_RESTOFIU,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_UI_BLOCK,
// P<ELSEQ> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000026,
  PHRASE_TYPE | G_AFTERELSE,
  COUNT_OF_PHRASES | 0x000000,
// P<RESTOFIU> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000024,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000027,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000024,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000027,
  PHRASE_TYPE | G_UI_BLOCK,
  PHRASE_TYPE | G_ELSEQ,
// P<SEX> = ...;


  COUNT_OF_ALTS | 0x000006,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001f,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000028,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00002b,
  COUNT_OF_PHRASES | 0x000000,
// P<RFSTMNT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001d,
  PHRASE_TYPE | G_DECLNAME,
  COUNT_OF_PHRASES | 0x000008,
  PHRASE_TYPE | G_DECLNAME,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_PUSHDECS,
  PHRASE_TYPE | G_RFDEC,
  PHRASE_TYPE | G_RESTOFRFDEC,
  PHRASE_TYPE | G_ALTRFDEC,
  PHRASE_TYPE | G_POPDECS,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<RFREF> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_VARNAME,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_RFDEC,
  PHRASE_TYPE | G_RESTOFRFDEC,
  PHRASE_TYPE | G_ALTRFDEC,
// P<RESTOFRFDEC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_READLINEP,
  PHRASE_TYPE | G_RFDEC,
  PHRASE_TYPE | G_RESTOFRFDEC,
  COUNT_OF_PHRASES | 0x000000,
// P<RFDEC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_INITDECS,
  PHRASE_TYPE | G_XTYPE,
  PHRASE_TYPE | G_RFELMNT,
  COUNT_OF_PHRASES | 0x000005,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_RFDEC,
  PHRASE_TYPE | G_RESTOFRFDEC,
  PHRASE_TYPE | G_ALTRFDEC,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<RFELMNT> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_Opt_AN_N_type,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000014,
  PHRASE_TYPE | G_ADECLN,
// P<ALTRFDEC> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000004,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001c,
  PHRASE_TYPE | G_RFDEC,
  PHRASE_TYPE | G_RESTOFRFDEC,
  PHRASE_TYPE | G_ALTRFDEC,
  COUNT_OF_PHRASES | 0x000000,
// P<ASM> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000005,
  PHRASE_TYPE | NEGATED_PHRASE | G_semi,
  BIP_TYPE | NEGATED_PHRASE | B_nl,
  BIP_TYPE | NEGATED_PHRASE | WHITESPACE_ALLOWED | B_eof,
  REGEXP_TYPE | WHITESPACE_ALLOWED | 0x000008,
  PHRASE_TYPE | G_ASM,
  COUNT_OF_PHRASES | 0x000000,
// P<Call_or_Assign_AUI> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_VAR,
  PHRASE_TYPE | G_ASSOP,
  PHRASE_TYPE | G_EXPR,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_VAR,
// P<SWITCHIDX> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  COUNT_OF_PHRASES | 0x000000,
// P<Goto> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003e,
  PHRASE_TYPE | G_TAG,
  PHRASE_TYPE | G_SWITCHIDX,
// P<return> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00002c,
// P<result> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00002d,
  PHRASE_TYPE | G_ASSOP,
  PHRASE_TYPE | G_EXPR,
// P<monitor_AUI> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00002e,
// P<stop> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00002f,
// P<signal> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000030,
  PHRASE_TYPE | G_PC_EVENTQ,
  PHRASE_TYPE | G_CEXPR,
  PHRASE_TYPE | G_Opt_EXPR,
// P<exit> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000031,
// P<continue> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000001,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000032,
// P<Unconditional_Instructions> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_UI_BLOCK,
  PHRASE_TYPE | G_S,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_UI_BLOCK,
  PHRASE_TYPE | G_PC_IU,
  PHRASE_TYPE | G_TOP_LEVEL_CONDITION,
  PHRASE_TYPE | G_S,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_UI_BLOCK,
  PHRASE_TYPE | G_PC_WUF,
  PHRASE_TYPE | G_S,
// P<UI> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_NONFINAL_UI,
  PHRASE_TYPE | G_AUI,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_NONFINAL_UI,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_FINAL_UI,
// P<AUI> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00001b,
  PHRASE_TYPE | G_UI,
// P<UI_BLOCK> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_NONFINAL_UI,
  PHRASE_TYPE | G_AUI,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_NONFINAL_UI,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_FINAL_UI,
// P<NONFINAL_UI> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_Call_or_Assign_AUI,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_monitor_AUI,
// P<FINAL_UI> = ...;


  COUNT_OF_ALTS | 0x000007,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_Goto,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_return,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_result,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_stop,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_signal,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_exit,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_continue,
// P<More_STATEMENTS> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_STATEMENT,
  PHRASE_TYPE | G_More_STATEMENTS,
  COUNT_OF_PHRASES | 0x000000,
// P<STATEMENTS> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_STATEMENT,
  PHRASE_TYPE | G_More_STATEMENTS,
  BIP_TYPE | WHITESPACE_ALLOWED | B_eof,
// P<Labels> = ...;


  COUNT_OF_ALTS | 0x000003,
  COUNT_OF_PHRASES | 0x000006,
  PHRASE_TYPE | G_TAG,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  SEMANTIC_TYPE | S_COLON,
  COUNT_OF_PHRASES | 0x000006,
  PHRASE_TYPE | G_TAG,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  SEMANTIC_TYPE | S_COLON,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_TAG,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  SEMANTIC_TYPE | S_COLON,
// P<If_or_Unless> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_PC_IU,
  PHRASE_TYPE | G_TOP_LEVEL_CONDITION,
  PHRASE_TYPE | G_RESTOFIU,
  PHRASE_TYPE | G_S,
// P<Opt_CYCPARM> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_CYCPARM,
  COUNT_OF_PHRASES | 0x000000,
// P<start_of_cycle> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000033,
  PHRASE_TYPE | G_Opt_CYCPARM,
  PHRASE_TYPE | G_S,
  COUNT_OF_PHRASES | 0x000003,
  PHRASE_TYPE | G_PC_WUF,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000033,
  PHRASE_TYPE | G_S,
// P<end_of_cycle> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000034,
  PHRASE_TYPE | G_RESTOFREPEAT,
  PHRASE_TYPE | G_S,
// P<regular_declaration> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  PHRASE_TYPE | G_INITDECS,
  PHRASE_TYPE | G_XTYPE,
  PHRASE_TYPE | G_DECLN,
  PHRASE_TYPE | G_S,
// P<proc_declaration> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000008,
  PHRASE_TYPE | G_INITDECS,
  PHRASE_TYPE | G_SEX,
  PHRASE_TYPE | G_RT,
  PHRASE_TYPE | G_Opt_RtFnMapSpec,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_ALIAS,
  PHRASE_TYPE | G_FPP,
  PHRASE_TYPE | G_S,
// P<own_or_external_declaration> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000005,
  PHRASE_TYPE | G_INITDECS,
  PHRASE_TYPE | G_XOWN,
  PHRASE_TYPE | G_XTYPE,
  PHRASE_TYPE | G_OWNDEC,
  PHRASE_TYPE | G_S,
// P<record_format_declaration> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000005,
  PHRASE_TYPE | G_INITDECS,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00000e,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000019,
  PHRASE_TYPE | G_RFSTMNT,
  PHRASE_TYPE | G_S,
// P<various_ends> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000035,
  PHRASE_TYPE | G_ENDLIST,
  PHRASE_TYPE | G_S,
// P<include_file> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000036,
  PHRASE_TYPE | G_CONST,
  SEMANTIC_TYPE | S_INCLUDE,
// P<begin_block> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000037,
  PHRASE_TYPE | G_DOWN,
  PHRASE_TYPE | G_S,
// P<ONESWDECL> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000008,
  PHRASE_TYPE | G_INITDECS,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_DECLNAMES,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000028,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  PHRASE_TYPE | G_EXPR,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x000029,
// P<RESTOFSWLIST> = ...;


  COUNT_OF_ALTS | 0x000002,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002c,
  PHRASE_TYPE | G_ONESWDECL,
  PHRASE_TYPE | G_RESTOFSWLIST,
  COUNT_OF_PHRASES | 0x000000,
// P<switch_declaration> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000004,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000038,
  PHRASE_TYPE | G_ONESWDECL,
  PHRASE_TYPE | G_RESTOFSWLIST,
  PHRASE_TYPE | G_S,
// P<list_on> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000039,
  PHRASE_TYPE | G_S,
  SEMANTIC_TYPE | S_LISTON,
// P<else> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x000026,
  PHRASE_TYPE | G_S,
// P<finish_opt_else> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00003a,
  PHRASE_TYPE | G_FINISHELSEQ,
  PHRASE_TYPE | G_S,
// P<embedded_assembler> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  CHAR_TYPE | WHITESPACE_ALLOWED | 0x00002a,
  PHRASE_TYPE | G_ASM,
  PHRASE_TYPE | G_S,
// P<trusted> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000002,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00003b,
  PHRASE_TYPE | G_S,
// P<mainep> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00003c,
  PHRASE_TYPE | G_DECLNAME,
  PHRASE_TYPE | G_S,
// P<control> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000003,
  KEYWORD_TYPE | WHITESPACE_ALLOWED | 0x00003d,
  PHRASE_TYPE | G_CONST,
  PHRASE_TYPE | G_S,
// P<PSEMI> = ...;


  COUNT_OF_ALTS | 0x000001,
  COUNT_OF_PHRASES | 0x000000,
// P<STATEMENT> = ...;


  COUNT_OF_ALTS | 0x000017,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_Labels,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_Unconditional_Instructions,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_Comment,
  PHRASE_TYPE | G_S,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_If_or_Unless,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_start_of_cycle,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_end_of_cycle,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_regular_declaration,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_various_ends,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_record_format_declaration,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_proc_declaration,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_own_or_external_declaration,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_include_file,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_begin_block,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_on_event_block,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_switch_declaration,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_list_on,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_else,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_finish_opt_else,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_embedded_assembler,
  PHRASE_TYPE | G_PSEMI,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_trusted,
  PHRASE_TYPE | G_PSEMI,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_mainep,
  PHRASE_TYPE | G_PSEMI,
  COUNT_OF_PHRASES | 0x000002,
  PHRASE_TYPE | G_control,
  PHRASE_TYPE | G_PSEMI,
  COUNT_OF_PHRASES | 0x000001,
  PHRASE_TYPE | G_S,
};

#ifdef INITCODE
// Soon I need to move all defines and external specs to a proper header file...
// These are calls which can report where they were called from:
// Stringpool helpers






  #define PrintTag(L) PrintTag_inner(L, __FILE__, __LINE__)
  #define PrintAtom(L) PrintAtom_inner(L, __FILE__, __LINE__)
  #define Tag2Str(L) Tag2Str_inner(L, __FILE__, __LINE__)
  #define Tag2WStr(L) Tag2WStr_inner(L, __FILE__, __LINE__)
  #define Atom2Str(L) Atom2Str_inner(L,__FILE__,__LINE__)
  #define Atom2WStr(L) Atom2WStr_inner(L,__FILE__,__LINE__)
  #define pooltowstr(S) pooltowstr_inner(S, __FILE__, __LINE__)
  #define pooltostr(S) pooltostr_inner(S, __FILE__, __LINE__)
// others...


  #define TYPEOF(i) TYPEOF_inner(i, __FILE__, __LINE__)
  #ifndef compile
    #define compile(P,d) compile_inner(P,d, __FILE__, __LINE__)
    extern int compile_inner(int Ph, int depth, const char *file, const int line);
  #endif
  #define DebugVarIDX(V, d) DebugVarIDX_inner(V, d, __FILE__, __LINE__)

  #define _textof(x) #x
  #define textof(x) _textof(x)
  #define Diagnose(Mess, IDX, depth, test) Diagnose_inner(Mess, IDX, depth, test, _textof(IDX), __FILE__, __LINE__)

  #define generate_c(P, d) generate_c_inner(P, d, __FILE__, __LINE__)
  extern int generate_c_inner(int P, int depth, const char *file, const int line);
// forward ref.  generate_c is intended to *only* output its parameter in C form,
//               it should *not* attempt to compile anything.  Its parameter should
//               already have been compiled and be ready to use. (Although the lines
//               may be a little blurred during the development phase)
// Cpool is a temporary buffer area into which codegen() (which is ultimately
// called by compile() or generate_c()) can write its output, rather than the
// default where it writes to stdout.  Sometimes we may prefer to examine the
// text of a tree item rather than print it:










DECLARE(Cpool, wchar_t, 100000);
#define _Cpool(x) WRITE(x,Cpool,wchar_t)
#define Cpool(x) READ(x,Cpool,wchar_t)
int Cpool_nextfree = 0;


  typedef enum AST_CODE {
    AST_FIRST_OP = NUM_GRAMMAR,
// P(x) - anything
    AST_DEFER_UNCOMPILED_PHRASE,
// VarDeclIDX - variable, name, map call
    AST_LVALUE,
// VarDeclIDX - variable, fn call, etc
    AST_RVALUE,
// anything
    AST_INDIRECT,
// anything
    AST_ADDROF,
// object, old width, new width
    AST_ADJUST_WIDTH,
// Imp comment symbol, TEXT
    AST_COMMENT,
// Tag
    AST_TOKEN,
// Literal, Type   (not const expression, just one literal constant, with type info. Previously was RVALUE with Alt==1)
    AST_CONST,
// EXPR - replacement for P<EXPR> after applying precedence
    AST_EXPRESSION,
// TypeDeclIDX inferred type of expressions, constructed bottom-up by inference (eg int * real => real)
    AST_TYPE_INFORMATION,
// EXPR  -- for generating C source with included user brackets.
    AST_USER_PARENS,
// OP, LEFT, RIGHT
    AST_BINARY_ARITHMETIC_OPERATION,
// OP, ARG
    AST_UNARY_ARITHMETIC_OPERATION,
// OP, LEFT, RIGHT
    AST_ASSIGN,
// PROC, FPP_LIST, APP_LIST
    AST_PROC_CALL,
// EXPR, APP_LIST
    AST_APP_LIST,
// ARRAY, DIM_LIST, APP_LIST
    AST_ARRAYACCESS,
// RECORD, FIELD
    AST_FIELD,
// object, FIELD, APP_LIST, SUBFIELDS
    AST_PENDING_APP_OR_FIELD,
// OP (str), OP (code)
    AST_BINOP,
// OP (str), OP (code)
    AST_MONOP,
// OP (str), OP (code)
    AST_ASSOP,
// Do NOT add any after this item.
    AST_LAST_OP
  } AST_CODE;
// /home/gtoal/src/compilers101/new-parser/imps/tests/progs/imp80pass2.i
//%const %byte %integer %array PRECEDENCE(0:20)=0,3,3,4,5,5,4,3,3,4,4,5,5,3,5,5,
//                                              0(3),3,5
//%const %byte %integer %array OPVAL(0:20)=0,ADD,SUB,ANDL,IEXP,REXP,MULT,NONEQ,
//                  ORL,INTDIV,REALDIV,RSHIFT,LSHIFT,ADD,IEXP,REXP,0(3),LNEG,NOTL








  typedef enum Oper {
    OP_NONE=0,
    OP_ADD,
    OP_SUB,
    OP_AND,
    OP_IEXP,
    OP_REXP,
    OP_MULT,
    OP_EOR,
    OP_OR,
    OP_INTDIV,
    OP_REALDIV,
    OP_RSHIFT,
    OP_LSHIFT,
    OP_CONCAT,
    OP_NEG,
    OP_NOT,
    OP_POS,
    OP_LPAREN,
    OP_RPAREN,
// OPs above here may be used in TORP code to assign operator precedence.
// OPs below here will not be used in TORP code but may be used in the AST tree
// to handle generation of C code.
// not added to table.  May not need to as we are not handling assignments using TORP.






    OP_ASSIGN_ADDR,
    OP_ASSIGN_VALUE,
    OP_JAM_TRANSFER,
    OP_UNCOND_STR_RESOL,
// address comparisons


    OP_EQEQ,
    OP_NENE,
// Arithmetic and string comparisons


    OP_EQ,
    OP_NE,
    OP_LT,
    OP_LE,
    OP_GT,
    OP_GE,
// string-only comparisons


    OP_COND_STR_RESOL,
// Not even thinking about handling conditionals with TORP at this point.


    OP_BOOLAND,
    OP_BOOLOR,
    OP_BOOLNOT,
  } Oper;

  typedef enum Assoc {
    Left=1, Right
  } Assoc;

  typedef struct Torpedo {
    Oper Op;
    int Prec;
    int Assoc;
    int Arity;
    char *C_left, *C_mid, *C_right;
    int C_prec;
// Debugging info only
    wchar_t *Sym;
  } Torpedo;
// atoms should get C_prec of either 11 or 0. I'm not sure which yet.
// C precedences taken from https://en.cppreference.com/w/c/language/operator_precedence
// Note I carelessly numbered IMP precedences as 0 -> lowest and 6 -> highest,
// but C precedences as 11 -> lowest and 0 -> highest!  Needs to be fixed.  Fortunately
// they're handled separately so the mistake never manifests.







  #define ATOMPRIO 0

  const Torpedo OpDetails [] = {
// (I always prefer to keep 0 free to catch unassigned errors)
    { OP_NONE, 0, Left, 0, "", "*BADOP*", "", ATOMPRIO, L"*ERROR*" },
// "+"
    { OP_ADD, 3, Left, 2, "", " + ", "", 4, L"OP_ADD" },
// "-"
    { OP_SUB, 3, Left, 2, "", " - ", "", 4, L"OP_SUB" },
// "&"
    { OP_AND, 4, Left, 2, "", " & ", "", 8, L"OP_AND" },
// "****" or "\\".  \\ is the Imp77 preferred operator.  Right associative.
    { OP_IEXP, 5, Right, 2, "IEXP(", ", ", ")", ATOMPRIO, L"OP_IEXP" },
// "**" or "\"      \  in Imp77
    { OP_REXP, 5, Right, 2, "REXP(", ", ", ")", ATOMPRIO, L"OP_REXP" },
// "*"
    { OP_MULT, 4, Left, 2, "", " * ", "", 3, L"OP_MULT" },
// "!!"
    { OP_EOR, 3, Left, 2, "", " ^ ", "", 9, L"OP_EOR" },
// "!" or "|"  (suprisingly '|' is also allowed for comments! - goes *way* back!)
    { OP_OR, 3, Left, 2, "", " | ", "", 10, L"OP_OR" },
// "//"
    { OP_INTDIV, 4, Left, 2, "", " / ", "", 3, L"OP_INTDIV" },
// "/"
    { OP_REALDIV, 4, Left, 2, "", " / ", "", 3, L"OP_REALDIV" },
// ">>"    // UNSIGNED needs a size - char/short/int/long/long long
    { OP_RSHIFT, 5, Left, 2, "", " >> ", "", 5, L"OP_RSHIFT" },
// "<<"
    { OP_LSHIFT, 5, Left, 2, "", " << ", "", 5, L"OP_LSHIFT" },
// "."  // The only string operator.  All others are invalid. So prio is irrelevant.
    { OP_CONCAT, 5, Left, 2, "", ".", "", 1, L"OP_CONCAT" },
// "-"  // Precedence of '-' is surprisingly low, and responsible for the -1>>1 inanity.
    { OP_NEG, 3, Left, 1, "", "-", "", 2, L"OP_NEG" },
// "\" or "~"
    { OP_NOT, 6, Left, 1, "", "~", "", 2, L"OP_NOT" },
// "+"
    { OP_POS, 6, Left, 1, "", "+", "", 2, L"OP_POS" },
// "("  // '(' and ')' do not survive as far as an ExPop() call.
    { OP_LPAREN, 0, Left, 0, "(", "", "", ATOMPRIO, L"OP_LPAREN" },
// ")"
    { OP_RPAREN, 0, Right, 0, ")", "", "", ATOMPRIO, L"OP_RPAREN" },
// Note: Imp Modulus (really Absolute value) (originally !expr! and later |expr| ) is not supported in Imp80, and has been replaced by MOD() or IMOD().
// The C operator '%' (Modulo - no connection to Modulus) is a perm call in Imp77: REM(a,b). (or is that IREM?)


  };

  typedef struct Op_or_Data {
// 'O' or 'D' (we'll distinguish between 'C' or 'V' later - constant or variable)
    char type;
// Torpedo or AST index depending on type above.
    int idx;
  } Op_or_Data;


  typedef struct ast_defer_uncompiled_phrase {
// variable, map, name
    int phrase;
  } ast_defer_uncompiled_phrase;

  typedef struct ast_lvalue {
// variable, map, name
    int atom;
  } ast_lvalue;

  typedef struct ast_rvalue {
// variable, fn, proc, const etc - effectively an operand. but not an expr.
    int atom;
  } ast_rvalue;

  typedef struct ast_indirect {
// anything
    int rvalue;
  } ast_indirect;

  typedef struct ast_addrof {
// anything
    int lvalue;
  } ast_addrof;

  typedef struct ast_adjust_width {
    int object, old_size, new_size;
  } ast_adjust_width;

  typedef struct ast_token {
// could be anything!
    int name;
  } ast_token;

  typedef struct ast_comment {
    int sym, text;
  } ast_comment;

  typedef struct ast_constval {
// variable, fn, proc, const etc - effectively an operand. but not an expr.
    int atom;
    int type;
  } ast_constval;

  typedef struct ast_expression {
    int whatever;
  } ast_expression;

  typedef struct ast_type_information {
    int blah;
  } ast_type_information;

  typedef struct ast_user_parens {
    int Expr;
  } ast_user_parens;

  typedef struct ast_binary_arithmetic_operation {
    Oper binop;
    int left, right;
  } ast_binary_arithmetic_operation;

  typedef struct ast_unary_arithmetic_operation {
    Oper monop;
    int arg;
  } ast_unary_arithmetic_operation;

  typedef struct ast_assign {
    Oper assop;
    int LHS, RHS;
  } ast_assign;

  typedef struct ast_proc_call {
    int Proc, Types, Args;
  } ast_proc_call;

  typedef struct ast_app_list {
    int Expr, next;
  } ast_app_list;

  typedef struct ast_array_access {
    int Array, Dims, Indices;
  } ast_array_access;

  typedef struct ast_field {
    int Record, Field;
  } ast_field;

  typedef struct ast_pending_app_or_field {
    int Object, Field, App, Subfields;
  } ast_pending_app_or_field;

  typedef struct ast_binop {
    int OpStr;
    Oper OpCode;
  } ast_binop;

  typedef struct ast_monop {
    int OpStr;
    Oper OpCode;
  } ast_monop;

  typedef struct ast_assop {
    int OpStr;
    Oper OpCode;
  } ast_assop;
// Rough draft
// Although the AST is a simple array of ints, it's cleaner if we overlay a struct over the ints
// when accessing tuples, so that we don't have to remember offsets into the AST fields, which
// from previous experience with the last imptoc was the cause of several coding errors.





  typedef struct AST_struct {
// reserved field
    AST_CODE ast_code;
    int op;
    int alt;
    int phrases;
    int Hidden_fields[TUPLE_RESULT_FIELDS];
// (anonymous unions are a gcc extension)
    union {
      ast_defer_uncompiled_phrase defer_uncompiled_phrase;
      ast_lvalue lvalue;
      ast_rvalue rvalue;
      ast_indirect indirect;
      ast_addrof addrof;
      ast_adjust_width adjust_width;
      ast_comment comment;
      ast_token token;
      ast_constval constval;
      ast_expression expression;
      ast_type_information type_information;
      ast_user_parens user_parens;
      ast_binary_arithmetic_operation binary_arithmetic_operation;
      ast_unary_arithmetic_operation unary_arithmetic_operation;
      ast_assign assign;
      ast_proc_call proc_call;
      ast_app_list app_list;
      ast_array_access array_access;
      ast_field field;
      ast_pending_app_or_field pending_app_or_field;
      ast_binop binop;
      ast_monop monop;
      ast_assop assop;
    };
  } AST_struct;
// rather than have an awkward new syntax such as %prim (23) %routine BLAH, I'll use a parallel list of names
// as a lookup table to an index, and enums to index a switch table



  static const char *prim[] = {
    "**BADPRIM**",
    "SELECTINPUT",
    "SELECTOUTPUT",
    "NEWLINE",
    "SPACE",
    "SKIPSYMBOL",
    "READSTRING",
    "NEWLINES",
    "SPACES",
    "NEXTSYMBOL",
    "PRINTSYMBOL",
    "READSYMBOL",
    "READ",
    "WRITE",
    "NEWPAGE",
    "ADDR",
    "ARCSIN",
    "INT",
    "INTPT",
    "FRACPT",
    "PRINT",
    "PRINTFL",
    "REAL",
    "INTEGER",
    "MOD",
    "ARCCOS",
    "SQRT",
    "LOG",
    "SIN",
    "COS",
    "TAN",
    "EXP",
    "CLOSESTREAM",
    "BYTEINTEGER",
    "EVENTINF",
    "RADIUS",
    "ARCTAN",
    "LENGTH",
    "PRINTSTRING",
    "NL",
    "LONGREAL",
    "PRINTCH",
    "READCH",
    "STRING",
    "READITEM",
    "NEXTITEM",
    "CHARNO",
    "TOSTRING",
    "SUBSTRING",
    "RECORD",
    "ARRAY",
    "SIZEOF",
    "IMOD",
    "PI",
    "EVENTLINE",
    "LONGINTEGER",
    "LONGLONGREAL",
    "LENGTHENI",
    "LENGTHENR",
    "SHORTENI",
    "SHORTENR",
    "NEXTCH",
    "HALFINTEGER",
    "PPROFILE",
    "FLOAT",
    "LINT",
    "LINTPT",
    "SHORTINTEGER",
    "TRUNC",
  };

  typedef enum IMP_PRIM {
    PRIM_SELECTINPUT = 1,
    PRIM_SELECTOUTPUT,
    PRIM_NEWLINE,
    PRIM_SPACE,
    PRIM_SKIPSYMBOL,
    PRIM_READSTRING,
    PRIM_NEWLINES,
    PRIM_SPACES,
    PRIM_NEXTSYMBOL,
    PRIM_PRINTSYMBOL,
    PRIM_READSYMBOL,
    PRIM_READ,
    PRIM_WRITE,
    PRIM_NEWPAGE,
    PRIM_ADDR,
    PRIM_ARCSIN,
    PRIM_INT,
    PRIM_INTPT,
    PRIM_FRACPT,
    PRIM_PRINT,
    PRIM_PRINTFL,
    PRIM_REAL,
    PRIM_INTEGER,
    PRIM_MOD,
    PRIM_ARCCOS,
    PRIM_SQRT,
    PRIM_LOG,
    PRIM_SIN,
    PRIM_COS,
    PRIM_TAN,
    PRIM_EXP,
    PRIM_CLOSESTREAM,
    PRIM_BYTEINTEGER,
    PRIM_EVENTINF,
    PRIM_RADIUS,
    PRIM_ARCTAN,
    PRIM_LENGTH,
    PRIM_PRINTSTRING,
    PRIM_NL,
    PRIM_LONGREAL,
    PRIM_PRINTCH,
    PRIM_READCH,
    PRIM_STRING,
    PRIM_READITEM,
    PRIM_NEXTITEM,
    PRIM_CHARNO,
    PRIM_TOSTRING,
    PRIM_SUBSTRING,
    PRIM_RECORD,
    PRIM_ARRAY,
    PRIM_SIZEOF,
    PRIM_IMOD,
    PRIM_PI,
    PRIM_EVENTLINE,
    PRIM_LONGINTEGER,
    PRIM_LONGLONGREAL,
    PRIM_LENGTHENI,
    PRIM_LENGTHENR,
    PRIM_SHORTENI,
    PRIM_SHORTENR,
    PRIM_NEXTCH,
    PRIM_HALFINTEGER,
    PRIM_PPROFILE,
    PRIM_FLOAT,
    PRIM_LINT,
    PRIM_LINTPT,
    PRIM_SHORTINTEGER,
    PRIM_TRUNC,
  } IMP_PRIM;
// By the way, the EMAS3 "IOPT" Imp80 compiler is at http://www.gtoal.com/history.dcs.ed.ac.uk/archive/os/emas/emas2/compilers/imputils/trimp
// (see https://history.dcs.ed.ac.uk/archive/os/emas/emas3/docs/ioptnotes.html for differences from EMAS2 Imp80
// and also https://www.ancientgeek.org.uk/EMAS/ERCC_User_Notes/ERCC_User_Note_058.pdf )
// Although this code is mostly following Peter Stephens Imp to C code which is based on the same EMAS2 Imp80
// that Bob Eager's Imp80 implements.
// In the ERCC compiler, these built-in procedures are all implemented by the compiler itself rather than as externals.
// It will be easier to just parse them in source form than to pre-build a data structure with the declaration info.










  static const char *Prims[69 ] = {
    "**BADPRIM**",
    "%prim %routine %spec SELECT INPUT(%integer STREAM)",
    "%prim %routine %spec SELECT OUTPUT(%integer STREAM)",
    "%prim %routine %spec NEWLINE",
    "%prim %routine %spec SPACE",
    "%prim %routine %spec SKIP SYMBOL",
    "%prim %routine %spec READ STRING(%string %name S)",
    "%prim %routine %spec NEWLINES(%integer N)",
    "%prim %routine %spec SPACES(%integer N)",
    "%prim %integer %fn %spec NEXT SYMBOL",
    "%prim %routine %spec PRINT SYMBOL(%integer SYMBOL)",
    "%prim %routine %spec READ SYMBOL(%name SYMBOL)",
    "%prim %routine %spec READ(%name NUMBER)",
    "%prim %routine %spec WRITE(%integer VALUE,PLACES)",
    "%prim %routine %spec NEWPAGE",
    "%prim %integer %fn %spec ADDR(%name VARIABLE)",
    "%prim %long %real %fn %spec ARCSIN(%long %real X)",
    "%prim %integer %fn %spec INT(%long %real X)",
    "%prim %integer %fn %spec INTPT(%long %real X)",
    "%prim %long %real %fn %spec FRACPT(%long %real X)",
    "%prim %routine %spec PRINT(%long %real NUMBER,%integer BEFORE,AFTER)",
    "%prim %routine %spec PRINTFL(%long %real NUMBER,%integer PLACES)",
    "%prim %real %map %spec REAL(%integer VAR ADDR)",
    "%prim %integer %map %spec INTEGER(%integer VAR ADDR)",
    "%prim %long %real %fn %spec MOD(%long %real X)",
    "%prim %long %real %fn %spec ARCCOS(%long %real X)",
    "%prim %long %real %fn %spec SQRT(%long %real X)",
    "%prim %long %real %fn %spec LOG(%long %real X)",
    "%prim %long %real %fn %spec SIN(%long %real X)",
    "%prim %long %real %fn %spec COS(%long %real X)",
    "%prim %long %real %fn %spec TAN(%long %real X)",
    "%prim %long %real %fn %spec EXP(%long %real X)",
    "%prim %routine %spec CLOSE STREAM(%integer STREAM)",
    "%prim %byte %integer %map %spec BYTE INTEGER(%integer VAR ADDR)",
    "%prim %integer %fn %spec EVENTINF",
    "%prim %long %real %fn %spec RADIUS(%long %real X,Y)",
    "%prim %long %real %fn %spec ARCTAN(%long %real X,Y)",
    "%prim %byte %integer %map %spec LENGTH(%string %name  S)",
    "%prim %routine %spec PRINT STRING(%string(255) STR)",
    "%prim %integer %fn %spec NL",
    "%prim %long %real %map %spec LONG REAL(%integer VAR ADDR)",
    "%prim %routine %spec PRINT CH(%integer CHARACTER)",
    "%prim %routine %spec READ CH(%name CHARACTER)",
    "%prim %string %map %spec STRING(%integer VAR ADDR)",
    "%prim %routine %spec READ ITEM(%string %name ITEM)",
    "%prim %string (1) %fn %spec NEXT ITEM",
    "%prim %byte %integer %map %spec CHARNO(%string %name STR,%integer CHAR REQD)",
    "%prim %string (1) %fn %spec TO STRING(%integer ch)",
    "%prim %string (255) %fn %spec SUB STRING(%string(255) STR, %integer FIRST, LAST)",
    "%prim %record (*) %map %spec RECORD(%integer REC ADDR)",
    "%prim %array %map %spec ARRAY(%integer A1ADDR,%array %name FORMAT)",
    "%prim %integer %fn %spec SIZEOF(%name X)",
    "%prim %integer %fn %spec IMOD(%integer VALUE)",
    "%prim %long %real %fn %spec PI",
    "%prim %integer %fn %spec EVENTLINE",
    "%prim %long %integer %map %spec LONGINTEGER(%integer ADR)",
    "%prim %long %long %real %map %spec LONGLONGREAL(%integer ADR)",
    "%prim %long %integer %fn %spec LENGTHENI(%integer VAL)",
    "%prim %long %long %real %fn %spec LENGTHENR(%long %real VAL)",
    "%prim %integer %fn %spec SHORTENI(%long %integer VAL)",
    "%prim %long %real %fn %spec SHORTENR(%long %long %real VAL)",
    "%prim %integer %fn %spec NEXTCH",
    "%prim %half %integer %map %spec HALFINTEGER(%integer ADDR)",
    "%prim %routine %spec PPROFILE",
    "%prim %long %real %fn %spec FLOAT(%integer VALUE)",
    "%prim %long %integer %fn %spec LINT(%long %long %real X)",
    "%prim %long %integer %fn %spec LINTPT(%long %long %real X)",
    "%prim %short %integer %map %spec SHORTINTEGER(%integer N)",
    "%prim %integer %fn %spec TRUNC(%long %real X)",
  };
// For diagnostics:


  static char *TypeName[32] = {
// CST

    "*ERROR(0)*", "CST:BIP_TYPE", "CST:PHRASE_TYPE", "CST:SEMANTIC_TYPE", "CST:KEYWORD_TYPE",
    "CST:CHAR_TYPE", "CST:UTF32CHAR_TYPE", "CST:STRING_TYPE", "CST:UTF32STRING_TYPE",
    "CST:REGEXP_TYPE", "CST:OPTION_TYPE", "CST:COUNT_OF_ALTS", "CST:COUNT_OF_PHRASES",
    "CST:ALT_NUMBER", "*ERROR(14)*", "*ERROR(15)*",
// AST

    "AST:BIP", "AST:PHRASE", "AST:ATOMLIT", "AST:POOLLIT",
    "*ERROR(20)*", "*ERROR(21)*", "*ERROR(22)*", "*ERROR(23)*",
    "*ERROR(24)*", "*ERROR(25)*", "*ERROR(26)*", "*ERROR(27)*",
    "*ERROR(28)*", "*ERROR(29)*", "*ERROR(30)*", "*ERROR(31)*",
  };
// TO DO: WE NEED A PROPER DECODE PROCEDURE THAT WORKS OUT ALL TYPES OF PHRASE
// Until Diagnose is padded out a bit...




  wchar_t *Decode(int Ph) {
    static wchar_t tmp[512];
    swprintf(tmp, 511, L"%s", TypeName[(Ph>>AST_type_shift)&AST_type_mask]);
    return tmp;
  }

  void PrintTag_inner(int Literal, const char *file, const int line);
  void PrintAtom_inner(int Literal, const char *file, const int line);
  char *Tag2Str_inner(int Literal, const char *file, const int line);
  wchar_t *Tag2WStr_inner(int Literal, const char *file, const int line);
  char *Atom2Str_inner(int Literal, const char *file, const int line);
// may be unused now
  wchar_t *Atom2WStr_inner(int Literal, const char *file, const int line);
  wchar_t *pooltowstr_inner(StrpoolIDX p, const char *file, const int line);
  char *pooltostr_inner(StrpoolIDX p, const char *file, const int line);

  int codegen(char *s, ...);

  StrpoolIDX wstrtopool(wchar_t *wstr) {
    StrpoolIDX result = Stringpool_nextfree;
    for (;;) {
      wchar_t wch = *wstr++;
      _Stringpool(Stringpool_nextfree++) = wch;
      if (wch == '\0') break;
    }
// or CHAR_TYPE?
    return AST_POOL_LIT | result;
//return (STRING_TYPE << GRAMMAR_TYPE_SHIFT) | result; // or CHAR_TYPE?

  }

  StrpoolIDX strtopool(char *str) {
    StrpoolIDX result = Stringpool_nextfree;
    for (;;) {
      char ch = *str++;
      _Stringpool(Stringpool_nextfree++) = ch;
      if (ch == '\0') break;
    }
    return AST_POOL_LIT | result;
  }

  wchar_t *pooltowstr_inner(StrpoolIDX p, const char *file, const int line) {
    if (p == -1) {
      fprintf(stderr, "* Error: pooltowstr passed -1 (uninitialised string) from %s, line %d\n", file, line);
    } else if (P_AST_type(p) == 0) {
      fprintf(stderr, "* Error: pooltowstr passed an untagged index from %s, line %d\n", file, line);
    } else if (P_AST_type(p) == STRING_TYPE) {
      fprintf(stderr, "* Error: pooltowstr passed a STRING_TYPE rather than an AST_POOL_LIT from %s, line %d\n", file, line);
    } else if (P_AST_type(p) == AST_ATOM_LIT) {
      fprintf(stderr, "* Error: pooltowstr passed a AST_ATOM_LIT rather than an AST_POOL_LIT from %s, line %d\n", file, line);
    } else if (P_AST_type(p) != AST_POOL_LIT) {
      fprintf(stderr, "* Error: pooltowstr passed an unexpected tag type %d:", P_AST_type(p) >> AST_type_shift);
      fprintf(stderr, " %ls from %s, line %d\n", Decode(p), file, line);
    }
    p = p & AST_idx_mask;
    return &Stringpool(p);
  }

  char *pooltostr_inner(StrpoolIDX p, const char *file, const int line) {
    if (p == -1) {
      fprintf(stderr, "* Error: pooltostr passed -1 (uninitialised string) from %s, line %d\n", file, line);
    } else if (P_AST_type(p) == 0) {
      fprintf(stderr, "* Error: pooltostr passed an untagged index from %s, line %d\n", file, line);
    } else if (P_AST_type(p) == STRING_TYPE) {
      fprintf(stderr, "* Error: pooltostr passed a STRING_TYPE rather than an AST_POOL_LIT from %s, line %d\n", file, line);
    } else if (P_AST_type(p) == AST_ATOM_LIT) {
      fprintf(stderr, "* Error: pooltostr passed a AST_ATOM_LIT rather than an AST_POOL_LIT from %s, line %d\n", file, line);
    } else if (P_AST_type(p) != AST_POOL_LIT) {
      fprintf(stderr, "* Error: pooltostr passed an unexpected tag type %d:", P_AST_type(p) >> AST_type_shift);
      fprintf(stderr, " %ls from %s, line %d\n", Decode(p), file, line);
    }
    p = p & AST_idx_mask;
    static char tmp[256];
    int warn = 0;
    int idx = 0;
    int pidx = p;
    for (;;) {
      int ch = Stringpool(pidx); pidx += 1;
      if (ch & 0xFFFFFF00) {
        warn = 1;
      }
      if (ch == '\0') break;
      tmp[idx] = ch&0xFF;
      idx += 1;
      if (idx == 255) break;
    }
    tmp[idx] = '\0';
    if (warn) {
      fprintf(stderr, "* Error: pooltostr should not need to return a wide character string: \"%ls\"\n", &Stringpool(p));
      fprintf(stderr, "         (Called from %s, line %d)\n", file, line);
      exit(1);
    }
// &Stringpool(p);
    return tmp;
  }

  void PrintTag_inner(int Literal, const char *file, const int line) {
    int i;
    int P_ = Literal&AST_idx_mask;
    unsigned int type = (Literal>>AST_type_shift)&AST_type_mask;
    if (P_AST_type(Literal) == AST_POOL_LIT) {
      codegen("%ls", pooltowstr(P_));
    } else if (P_AST_type(Literal) == AST_ATOM_LIT) {
      fprintf(stderr, "* Internal error at %s, line %d: PrintTag was passed an AST_ATOM_LIT rather than an AST_POOL_LIT?\n",
                      file, line);
      PrintAtom(Literal);
    } else {
      fprintf(stderr, "* Internal error at %s, line %d: PrintTag expected an AST_POOL_LIT but got type %d\n",
                      file, line, type);
      exit(1);
    }
  }

  void PrintAtom_inner(int Literal, const char *file, const int line) {
    if (Literal == -1) {
      fprintf(stderr, "* Internal error at %s, line %d: Literal was -1\n", file, line); exit(1);
    }
    int i;
    int P_ = Literal&AST_idx_mask;

    if (P_AST_type(Literal) == AST_ATOM_LIT) {
      for (i = atom(P_).start; i < atom(P_).end; i++)
        codegen("%lc", source(i).ch);
    } else if (P_AST_type(Literal) == AST_POOL_LIT) {
      fprintf(stderr, "* Internal error at %s, line %d: PrintAtom was passed an AST_POOL_LIT rather than an AST_ATOM_LIT?\n",
                      file, line);
      codegen("%ls", pooltowstr(P_));
    } else {
      fprintf(stderr, "* Internal error at %s, line %d: PrintAtom expected an AST_ATOM_LIT but got type %d\n",
                      file, line, (P_AST_type(Literal)>>AST_type_shift)&AST_type_mask);
// try to print it just ic case...

      fprintf(stderr, ">>> ");
      for (i = atom(P_).start; i < atom(P_).end; i++) fprintf(stderr, "%lc", source(i).ch);
      fprintf(stderr, " <<<\n");
      exit(1);
    }
  }
// Tag2Str is a short term expedient.  Most of the places where it is used
// so far should be extracting the canonical "C" variable name corresponding
// to the Imp variable, which may have leading spaces and perhaps even
// embedded spaces or {} comments!  For now just a quick hack to remove
// spaces.







  char *Tag2Str_inner(int Literal, const char *file, const int line) {
    int i;

    int P_ = Literal&AST_idx_mask;
    unsigned int type = (Literal>>AST_type_shift)&AST_type_mask;
    if (P_AST_type(Literal) == AST_POOL_LIT) {
      codegen("%ls", pooltowstr(P_));
    } else if (P_AST_type(Literal) == AST_ATOM_LIT) {
      fprintf(stderr, "* Internal error at %s, line %d: Tag2Str was passed an AST_ATOM_LIT rather than an AST_POOL_LIT?\n",
                      file, line);
      PrintAtom(Literal);
    } else {
      fprintf(stderr, "* Internal error at %s, line %d: Tag2Str expected an AST_POOL_LIT but got type %d\n",
                      file, line, type);
      exit(1);
    }

    fprintf(stderr, "*Error: can you use Tag2WStr() instead of Tag2Str() in %s, line %d?  Change %%s to %%ls where used.\n", file, line);
// scope code needs to be modified to use wchar_t * rather than char *.

    static char charstring[1024];
    sprintf(charstring, "%ls", pooltowstr(P_));
// DANGEROUS, AND TEMPORARY.
    return charstring;
  }

  wchar_t *Tag2WStr_inner(int Literal, const char *file, const int line) {
    int i;

    int P_ = Literal&AST_idx_mask;
    unsigned int type = (Literal>>AST_type_shift)&AST_type_mask;
    if (P_AST_type(Literal) == AST_POOL_LIT) {
      return pooltowstr(P_);
    } else if (P_AST_type(Literal) == AST_ATOM_LIT) {
      fprintf(stderr, "* Internal error at %s, line %d: Tag2WStr was passed an AST_ATOM_LIT rather than an AST_POOL_LIT?\n",
                      file, line);
      return Atom2WStr(Literal);
    } else {
      fprintf(stderr, "* Internal error at %s, line %d: Tag2WStr expected an AST_POOL_LIT but got type %d\n",
                      file, line, type);
      exit(1);
    }

    return pooltowstr(P_);
  }

  char *Atom2Str_inner(int Literal, const char *file, const int line) {
    int i, idx, len;
    char c;
    int P_ = Literal&AST_idx_mask;
    unsigned int type = (Literal>>AST_type_shift)&AST_type_mask;

    if (P_AST_type(Literal) == AST_ATOM_LIT) {
//fprintf(stderr, "{len=%d}", len);
      len = atom(P_).end-atom(P_).start;
      char Str[len+1];

      idx = 0; i = atom(P_).start;
      for (;;) {
        if (i-atom(P_).start == len) break;
// temp hack, not unicode.
        c = (char)(source(i).ch & 255);
        if (c != ' ') {
          Str[idx] = c;
          idx += 1;
          Str[idx] = '\0';
        }
        i++;
      }
// :-@
      return strdup(Str);
    } else if (P_AST_type(Literal) == AST_POOL_LIT) {
      fprintf(stderr, "* Internal error at %s, line %d: Atom2Str was passed an AST_POOL_LIT rather than an AST_ATOM_LIT?\n",
                      file, line);
      return Tag2Str(Literal);
    } else {
      fprintf(stderr, "* Internal error at %s, line %d: Atom2Str expected an AST_ATOM_LIT but got type %d\n",
                      file, line, type);
      exit(1);
    }
  }
// unused?

  wchar_t *Atom2WStr_inner(int Literal, const char *file, const int line) {
    int i, idx, len;
    int P_ = Literal&AST_idx_mask;
//fprintf(stderr, "{0: type    = PhraseType(Literal) = 0x%08x}", type);
    unsigned int type = PhraseType(Literal);

    if (P_AST_type(Literal) == AST_ATOM_LIT) {
// Drop through        

    } else if (P_AST_type(Literal) == AST_POOL_LIT) {
      fprintf(stderr, "* Internal error at %s, line %d: Atom2WStr was passed an AST_POOL_LIT rather than an AST_ATOM_LIT?\n",
                      file, line);
      return Tag2WStr(Literal);
    } else {
      fprintf(stderr, "* Internal error at %s, line %d: Atom2WStr expected an AST_ATOM_LIT but got type %d\n",
                      file, line, type);
      exit(1);
    }

    len = atom(P_).end-atom(P_).start;
//fprintf(stderr, "{A: wchar_t WStr[len=%d+1]}", len);

    wchar_t wc, WStr[len+1];

    idx = 0; i = atom(P_).start;
    for (;;) {
      if (i-atom(P_).start == len) break;
//fprintf(stderr, "{WStr[idx=%d] = source(i=%d).ch = %d ('%lc')}", idx, i, source(i).ch, source(i).ch);

      wc = source(i).ch;
      if (wc != L' ') {
        WStr[idx] = wc;
        idx += 1;
        WStr[idx] = '\0';
      }
      i++;
    }
// Also :-@

    wchar_t *rslt = wcsdup(WStr);
    return rslt;
  }

  StrpoolIDX Str2Pool(char *s) {
// The expensive version of Str2Pool would first search the string pool to
// see if the string was already stored somewhere.  However the space saved
// is irrelevant and the speed would be much slow (without the added complication
// of a hash table or trie) so the *only* justification for doing so would be
// to be able to compare indexes rather than strings, but the overhead of
// locating the duplicate string when adding a new string will without doubt
// be higher than the overhead of performing an actual string comparison.







    int p = Stringpool_nextfree;
    for (;;) {
      char c = *s++;
      _Stringpool(Stringpool_nextfree++) = c;
      if (c == '\0') break;
    }
//  | AST_POOL_LIT <-- add soon TO DO
    return p;
  }
// SCOPE MANAGEMENT:





  #include "symtab.h"

  static int DOWN_flag = 0;

  typedef enum CTRL_STACK_ENUM {
    FINISHELSE = 0,
    FINISH,
    END,
    ENDOFPROGRAM,
    ELSE,
    REPEAT,
    REPEATUNTIL,
    REPEATPUSHEDUNTIL
  } CTRL_STACK_ENUM;

  const char *ctrl_debug[] = {"FINISHELSE", "FINISH", "END", "ENDOFPROGRAM", "ELSE", "REPEAT", "REPEATUNTIL", "bug"};
// These are set in <init>


  static int debug_ctrl_stack = 0;
  static int debug_compiler = 0;
  static int debug_declarations = 0;
  static int debug_ast_creation = 0;

  DECLARE(CTRL_STACK, CTRL_STACK_ENUM, 32);
  int CTRL_STACK_nextfree = 0;



  int ctrl_depth(void) {
// Testing this against 0 to determine top-level (%externalroutine) code
// may not be correct in Imp80 due to the use of %if ... %start as
// a means of conditional compilation.  A better test might be to
// use the scope stack depth - remembering that I recently added an
// extra initial scope and we may need more to handle prims + perms
// before we get to externals.  So be careful.
// Example:
//  %if ARCH = ARM %start
//    %routine DUMP
//    %end
//  %finish
// DUMP should be translated to a "static" in C but if the test used
// to determine the level was: if (ctrl_depth() <= 1) ...
// then it will look like a nested procedure and will not be typed as static.
    if (debug_ctrl_stack) {
      int i = CTRL_STACK_nextfree;
      fprintf(stderr, "Control stack: "); while (--i >= 0) fprintf(stderr, " %s", ctrl_debug[ READ(i, CTRL_STACK, CTRL_STACK_ENUM) ]); fprintf(stderr, "\n");
    }
    return CTRL_STACK_nextfree;
  }

  void push_ctrl(int Code) {
    if (debug_ctrl_stack) {
      int i = CTRL_STACK_nextfree;
      fprintf(stderr, "Control stack before push: "); while (--i >= 0) fprintf(stderr, " %s", ctrl_debug[ READ(i, CTRL_STACK, CTRL_STACK_ENUM) ]); fprintf(stderr, "\n");
    }
    WRITE(CTRL_STACK_nextfree++, CTRL_STACK, CTRL_STACK_ENUM) = Code;
    if (debug_ctrl_stack) {
      int i = CTRL_STACK_nextfree;
      fprintf(stderr, "Control stack after push: "); while (--i >= 0) fprintf(stderr, " %s", ctrl_debug[ READ(i, CTRL_STACK, CTRL_STACK_ENUM) ]); fprintf(stderr, "\n");
    }
  }

  #define pop_ctrl() pop_ctrl_inner(__LINE__,__FILE__)
  int pop_ctrl_inner(int line, char *file) {
    if (CTRL_STACK_nextfree == 0) {
      fprintf(stderr, "* Control stack empty.  Internal coding error. (detected at %s:%d)\n", file,line);
      exit(1);
    }
    if (debug_ctrl_stack) {
      int i = CTRL_STACK_nextfree;
      fprintf(stderr, "Control stack before pop: "); while (--i >= 0) fprintf(stderr, " %s", ctrl_debug[ READ(i, CTRL_STACK, CTRL_STACK_ENUM) ]); fprintf(stderr, "\n");
    }
    if (debug_ctrl_stack) {
      int i = CTRL_STACK_nextfree-1;
      fprintf(stderr, "Control stack after pop: "); while (--i >= 0) fprintf(stderr, " %s", ctrl_debug[ READ(i, CTRL_STACK, CTRL_STACK_ENUM) ]); fprintf(stderr, "\n");
    }
    return  READ(--CTRL_STACK_nextfree, CTRL_STACK, CTRL_STACK_ENUM) ;
  }
// NAME TABLES
// formal parameters, and record formats (maybe array formats too) need to suppress current <DECLNAME> handling.
// Otherwise, declarations are looking pretty good!
// Inline sections like this are copied through to the global declarations in the main program.
// If you want to *execute* code at startup, put it in the <init> phrase at the start of P<SS>










  typedef enum OBJECT {
    UNINIT_OBJECT=0, CODE, VAR, RECORDFORMAT, ARRAYFORMAT, SWITCHDEFN, LABELDEFN
  } OBJECT;
  const char *Object_name[] = {"**Uninitialised OBJECT**", "CODE", "VAR", "RECORDFORMAT", "ARRAYFORMAT", "SWITCHDEFN", "LABELDEFN"};
  OBJECT Object;

  typedef enum BASETYPE {
    UNINIT_BASETYPE=0, INTEGER, FLOAT, STRINGTYPE, RECORD, PROCADDR, GENERIC , SWITCHLABEL, JUMPLABEL
  } BASETYPE;
  const char *Basetype_name[] = {"**Uninitialised BASETYPE**", "INTEGER", "FLOAT", "STRINGTYPE", "RECORD", "PROCADDR", "GENERIC", "SWITCHLABEL", "JUMPLABEL"};
  BASETYPE Basetype;

  typedef enum SIGNEDNESS {
    UNINIT_SIGNEDNESS=0, SIGNED, UNSIGNED
  } SIGNEDNESS;
  const char *Signedness_name[] = {"**Uninitialised SIGNEDNESS**", "SIGNED", "UNSIGNED"};
  SIGNEDNESS Signedness;

  typedef enum PRECISION {
    UNINIT_PRECISION=0, COMPOUND, BYTE, SHORT, WORD, LONGWORD, QUADWORD, ADDR
  } PRECISION;
  const char *Precision_name[] = {"**Uninitialised PRECISION**", "COMPOUND", "BYTE", "SHORT", "WORD", "LONGWORD", "QUADWORD", "ADDR"};
  PRECISION Precision;

  typedef enum PROC {
    UNINIT_PROC=0, NONE, ROUTINE, FN, MAP
  } PROC;
  const char *Proc_name[] = {"**Uninitialised PROC**", "NONE", "ROUTINE", "FN", "MAP"};
  PROC Proc;

  typedef enum AN_N {
    UNINIT_AN_N=0, NO_NAME, ARRAYNAME, OBJECTNAME
  } AN_N;
  const char *NameInfo_name[] = {"**Uninitialised NAMEINFO**", "NO_NAME", "ARRAYNAME", "OBJECTNAME"};
  AN_N NameInfo;
//typedef enum ISFORMAT {
//  UNINIT_ISFORMAT=0, NO_FORMAT, IS_FORMAT
//} ISFORMAT;
//const char *IsFormat_name[] = {"**Uninitialised ISFORMAT**", "NO_FORMAT", "IS_FORMAT"};
//ISFORMAT IsFormat;







  typedef enum SPECQ {
    UNINIT_SPEC=0, NO_SPEC, SPEC
  } SPECQ;
  const char *Spec_name[] = {"**Uninitialised SPEC**", "NO_SPEC", "SPEC"};
  SPECQ Spec;

  typedef enum ISARRAY {
    UNINIT_ISARRAY=0, SCALAR, ARRAY
  } ISARRAY;
  const char *IsArray_name[] = {"**Uninitialised ISARRAY**", "SCALAR", "ARRAY"};
  ISARRAY IsArray;

  typedef enum AREA {
// extrinsic is an implied %spec
    UNINIT_AREA=0, OWN, EXTDATA, CONSTANT, STACK, PARAMETER, FIELD
  } AREA;
  const char *Area_name[] = {"**Uninitialised AREA**", "OWN", "EXTDATA", "CONSTANT", "STACK", "PARAMETER", "FIELD"};
  AREA Area;

  typedef enum LINKAGE {
    UNINIT_LINKAGE=0, EXTPROC, SYSTEM, DYNAMIC, PRIM, PERM, AUTO
  } LINKAGE;
  const char *Linkage_name[] = {"**Uninitialised LINKAGE**", "EXTPROC", "SYSTEM", "DYNAMIC", "PRIM", "PERM", "AUTO"};
  LINKAGE Linkage;
// See previous attempt for imp77 at ~/src/compilers101/new-parser/imps/datatypes.h
// structures are never used directly.  Where you would normally embed a struct,
// you must put a structIDX which is an integer into a flex array of structs.
// Using indices rather than pointers is essential to having efficient flex arrays.
// I am removing all the C style flex arrays from the structs that are ultimately
// pointed to.  If they need variable numbers of anything, I'll implement them as
// linked lists instead.
// type information - everything needed to declare and use anything
  typedef int TypeDeclIDX;
// A tag and its type information - entered into a scoped symbol table
  typedef int VarDeclIDX;
// array of bounds pairs, one for each dimension, index into Bounds[x]

  typedef int BoundsTypeIDX;
// extra info for records, arrays, strings:


  typedef int RecordFormatIDX;
  typedef int ArrayFormatIDX;
  typedef int StringFormatIDX;
// list of parameters to a procedure

  typedef int ParamFormatIDX;
  typedef int SwitchFormatIDX;
  typedef int LabelFormatIDX;

  typedef int ASTidx;
// NOT ONLY OUTPUT THE DECLARATION IN C CODE, BUT ALSO ADD DECLARATION TO SCOPED NAME TABLES.
// VarDeclIDX for procedure to which parameters are being attached.



  VarDeclIDX ParentVarIDX = -1, ParentTypeIDX = -1;
// Scope table attached to recordformat for holding record fields when creating recordformat.
  int RecFmTableIDX = -1;
// VarDeclIDX index of %recordformat entry when declaring records of that (existing) format.
  int RecordTypeIDX = -1;
// Tag for declarations, VTag for use. ATag for an alias.   GLOBALS.
  int RTag = -1, DTag = -1, VTag = -1, ATag = -1;
// STag, VTag and ATag converted to a char *.  

  char *SDTag = NULL, *SATag = NULL;
// STag, VTag and ATag converted to a wchar_t *.  
  wchar_t *wSDTag = NULL, *wSVTag = NULL, *wSATag = NULL;
// OK, here's my next problem: as well as handling declarations on the fly, I want to return the
// declaration parameters via an AST tuple.  This is fine for most of the items in the struct
// below, but we may have a problem with the IDX items which point into other arrays.  Those
// will also have to be managed by AST tuples and added to and removed from in the same order
// as during parsing.  Right now, generating C on the fly from the CST is looking like the
// more tempting option, despite some limitations.
// ERCC & PSR cram these into an int.
  typedef struct TypeDecl {
// These are all ENUMs and in the original compilers were squeezed into too few available bits in a single variable:

    OBJECT Object;
    BASETYPE Basetype;
    SIGNEDNESS Signedness;
    PRECISION Precision;
    PROC Proc;
    AN_N NameInfo;
    SPECQ Spec;
    ISARRAY IsArray;
    AREA Area;
    LINKAGE Linkage;
// The following fields of extra information depend on the type of the object:
// CODE, VAR, RECORDFORMAT, ARRAYFORMAT, SWITCHDEFN, LABELDEFN
// May later decide to add types: PARAMETERFORMAT, STRINGFORMAT instead of overloading CODE and VAR?
// A %recordarrayformat is an ARRAYFORMAT with a Basetype of VAR holding a RECORD
// WARNING: A Fn or Map needs both a recfm and a paramfn
// A hypothetical string array map might need 3 of these,
// so they *cannot* be a union!
//union {
// CODE (parameter types, when a procedure)









      ParamFormatIDX paramfm;
// VAR, if it is a string
      StringFormatIDX strfm;
// RECORDFORMAT  (fields in an anonymous_scope table) - needed by %records (CODE or VAR) and RECORDFORMATs
      RecordFormatIDX recfm;
// ARRAYFORMAT (currently includes switches)
      ArrayFormatIDX arrfm;
// SWITCHDEFN
      SwitchFormatIDX switchfm;
// LABELDEFN
      LabelFormatIDX labelfm;
//};

  } TypeDecl;
// Note that DECLARE(x) creates x.next_free_index
// which we could use instead of adding a separate x_nextfree
// but I would need to add that to the NO_FLEX path as well.





  DECLARE(TypeDeclRA, TypeDecl, 10000);
  int TypeDecl_nextfree = 0;
// forward reference for recursive pointer.



  typedef struct VarDecl VarDecl;
  typedef struct VarDecl {
// TO DO: *** WARNING *** varname/aliasname/c_name may currently be getting assigned *ATOM*s rather than Stringpool indexes.
// Fortunately most of this type of error is caught early by internal consistency checks.  When I review the code, I need to
// decide which fix is cleaner: either change the declaration below to use an atom type, or change all the variables themselves
// to be simple Stringpool indexes.
// Object name






    StrpoolIDX varname;
// %alias name or -1
    StrpoolIDX aliasname;
// mangled for use when outputting C code
    StrpoolIDX c_name;
// or -1
    TypeDeclIDX type;
// Note sure about these: partially duplicating info from TypeDecl...
// for record fields,


    VarDeclIDX Fields;
// and procedure parameter lists
    VarDeclIDX Parameters;
// and array 
    BoundsTypeIDX Bounds;

  } VarDecl;

  DECLARE(VarDeclRA, VarDecl, 10000);
  int VarDecl_nextfree = 0;




  typedef enum BOUNDTYPE {
    UNDEFINED_BOUND = 0, CONSTANT_BOUND, VARIABLE_BOUND
  } BOUNDTYPE;

  typedef struct BoundsTYPE {
// so for a 2D list, the first 'dims' would hold '2', then the next (last) '1'.
    int dims;
// lower and upper bound for this dimension (AST indices)
    int Lower, Upper;
// lower or upper or both bounds of one dimension of an array may be
    BOUNDTYPE lowerboundtype, upperboundtype;
// constant, or taken from a variable at the point of declaration,
// or unknown. (unknown is an extension for later, for Hamish's 68K Imp)
// Some types of array (e.g. in a %record) must have all constant bounds.
// Arrays with variable bounds need a dopevector where the initial
// bounds can be stored at the point where space for the array is
// allocated (most likely using alloca() to mimic Imp's stack)
// linked list of following bounds, for multidimensional arrays






    BoundsTypeIDX next;
  } BoundsTYPE;

  DECLARE(BoundsRA, BoundsTYPE, 10000);
  int Bounds_nextfree = 0;




  typedef struct recfmTYPE {
// Record format name
    StrpoolIDX formatname;
// Record format name mangled for use when outputting C code
    StrpoolIDX c_formatname;
// count of fields
    int fields;
// VarDeclIDX field[];   // (extensible)   REPLACE WITH LIST OF FIELDS, MAYBE A 'scope' pointer

  } recfmTYPE;

  DECLARE(recfmRA, recfmTYPE, 10000);
  int recfm_nextfree = 0;




  typedef struct arrfmTYPE {
// Array format name, may be -1 for none
    StrpoolIDX formatname;
// Base element type
    TypeDeclIDX type;
// #dimensions ("dims"), plus linked list of each dimension ("Dimension")
    BoundsTypeIDX bounds ;
  } arrfmTYPE;

  DECLARE(arrfmRA, arrfmTYPE, 10000);
  int arrfm_nextfree = 0;




  typedef struct switchfmTYPE {
// Switch format name, may be -1 for none
    StrpoolIDX formatname;
// Base element type
    TypeDeclIDX type;
// #dimensions ("dims"), plus linked list of each dimension ("Dimension")
    BoundsTypeIDX bounds ;
  } switchfmTYPE;

  DECLARE(switchfmRA, switchfmTYPE, 10000);
  int switchfm_nextfree = 0;





  typedef struct labelfmTYPE {
// Label format name, may be -1 for none
    StrpoolIDX formatname;
// Base element type
    TypeDeclIDX type;
// #dimensions ("dims"), plus linked list of each dimension ("Dimension")
    BoundsTypeIDX bounds ;
  } labelfmTYPE;

  DECLARE(labelfmRA, labelfmTYPE, 10000);
  int labelfm_nextfree = 0;
// May later extend but until there are languages changes, no need.
// (changes such as allowing C-style strings as opposed to Imp strings,
//  or maybe significantly increasing string length, or allowing Unicode)







  typedef struct strfmTYPE {
// maximum string length
    int maxlen;
  } strfmTYPE;

  DECLARE(strfmRA, strfmTYPE, 10000);
  int strfm_nextfree = 0;
// *********** SOME CONFUSION TO BE SORTED OUT HERE ***********
// Do I prefer a linked list of parameters attached to a TypeDecl or a VarDecl?  Or do I need both?
// I appear to have:
//    a "VarDeclIDX Parameter" in each VarDecl
// and
//    a "ParamFormatIDX paramfm" in each TypeDecl which indexes an element of paramTYPE ParamRA[]
//
// paramTYPE is now a linked list containing a VarDeclIDX pointing to a Var
//
// It looks like the Parameter in a VarDecl is used to get the *names* of the parameters
// and the paramfm in a TypeDecl is used to get the corresponding types.  The latter may be
// redundant as each VarDecl points to the appropriate TypeDecl, but having that parm entry
// does make typedecls more consistent...
// I guess it boils down to "Can a procedure's type information get separated from a specific
// procedure, in the way that a recordformat can exist separately from a record.  I don't *think*
// this is possible in Imp80 although I do have a recollection of a "%SPEC" that was essentially
// a procedure spec and I think was used for procedures being passed as parameters to other
// procedures, eg  %integerfn apply(%integer arg, %integerfn proc(%integer x)) except in some
// obscure case where the passed procedure itself took a procedure as a parameter.  I don't
// think I've ever seen any existing code that actually did that but it would be nice to
// allow for it if the language is supposed to support it.
// Parameters to a procedure do get entered into the scope tables as if they were the initial
// declarations within the procedure body and are only distinguished from locals by checking that
// their LINKAGE is "PARAMETER".  There may be some funnies related to procedure specs before
// the actual procedure body is given, where it is better to have the parameter info stored
// in a TypeDecl rather than a VarDecl...  (For example I may need to replace the list of
// formal parameters attached to the SPEC with the updated list from the body once it is known.)
// The above internal monologue has not helped me decide which to implement.  I suspect that
// at first I had better implement both and see if either one ends up not being used.
  typedef struct paramTYPE {
    int numparams_remaining;
    VarDeclIDX param;
// C-style flex array replaced with this linked list:  VarDeclIDX     param[];    // extensible
    ParamFormatIDX next;
  } paramTYPE;
  DECLARE(ParamRA, paramTYPE, 10000);
  int Param_nextfree = 0;




  void Describe_Type(TypeDeclIDX T) {

    if (T == -1) {fprintf(stderr, "NO TYPE INFORMATION\n"); return;}

    fprintf(stderr, "TypeDecl[%d].Object     = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Object, Object_name[ READ(T, TypeDeclRA, TypeDecl) .Object]);
    fprintf(stderr, "TypeDecl[%d].Basetype   = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Basetype, Basetype_name[ READ(T, TypeDeclRA, TypeDecl) .Basetype]);
    fprintf(stderr, "TypeDecl[%d].Signedness = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Signedness, Signedness_name[ READ(T, TypeDeclRA, TypeDecl) .Signedness]);
    fprintf(stderr, "TypeDecl[%d].Precision  = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Precision, Precision_name[ READ(T, TypeDeclRA, TypeDecl) .Precision]);
    fprintf(stderr, "TypeDecl[%d].Proc       = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Proc, Proc_name[ READ(T, TypeDeclRA, TypeDecl) .Proc]);
    fprintf(stderr, "TypeDecl[%d].NameInfo   = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .NameInfo, NameInfo_name[ READ(T, TypeDeclRA, TypeDecl) .NameInfo]);
    fprintf(stderr, "TypeDecl[%d].Spec       = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Spec, Spec_name[ READ(T, TypeDeclRA, TypeDecl) .Spec]);
    fprintf(stderr, "TypeDecl[%d].IsArray    = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .IsArray, IsArray_name[ READ(T, TypeDeclRA, TypeDecl) .IsArray]);
    fprintf(stderr, "TypeDecl[%d].Area       = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Area, Area_name[ READ(T, TypeDeclRA, TypeDecl) .Area]);
    fprintf(stderr, "TypeDecl[%d].Linkage    = %d  %s\n", T,  READ(T, TypeDeclRA, TypeDecl) .Linkage, Linkage_name[ READ(T, TypeDeclRA, TypeDecl) .Linkage]);
// Only print relevant items...  (However these are no longer a union)

    switch ( READ(T, TypeDeclRA, TypeDecl) .Object) {

      case ARRAYFORMAT:
        fprintf(stderr, "TypeDecl[%d].arrfm      = %d\n", T,  READ(T, TypeDeclRA, TypeDecl) .arrfm);
        break;

      case SWITCHDEFN:
        fprintf(stderr, "TypeDecl[%d].switchfm   = %d\n", T,  READ(T, TypeDeclRA, TypeDecl) .switchfm);
        break;

      case LABELDEFN:
        fprintf(stderr, "TypeDecl[%d].labelfm    = %d\n", T,  READ(T, TypeDeclRA, TypeDecl) .labelfm);
        break;
// only if a Rt/Fn/Map

      case CODE:
// WARNING: A Fn or Map needs both a recfm and a paramfm so these *cannot* be a union!

        fprintf(stderr, "TypeDecl[%d].paramfm    = %d", T,  READ(T, TypeDeclRA, TypeDecl) .paramfm);
        ParamFormatIDX arg =  READ(T, TypeDeclRA, TypeDecl) .paramfm;
        if (arg != -1) fprintf(stderr, ": ");
        while (arg != -1) {
          int numparams_remaining =  READ(arg, ParamRA, paramTYPE) .numparams_remaining;
          int param =  READ(arg, ParamRA, paramTYPE) .param;
          fprintf(stderr, " %s (%d)", Atom2Str( READ(param, VarDeclRA, VarDecl) .varname), numparams_remaining+1);
          arg =  READ(arg, ParamRA, paramTYPE) .next;
        }
        fprintf(stderr, "\n");
        if ( READ(T, TypeDeclRA, TypeDecl) .Basetype == RECORD) {
          fprintf(stderr, "TypeDecl[%d].recfm      = %d A %%record points to an anonymous scope table containing the record fields...\n", T,  READ(T, TypeDeclRA, TypeDecl) .recfm);
        } else if ( READ(T, TypeDeclRA, TypeDecl) .Basetype == STRINGTYPE) {
          fprintf(stderr, "TypeDecl[%d].strfm      = %d A %%string points to some descriptor containing the length etc///\n", T,  READ(T, TypeDeclRA, TypeDecl) .strfm);
        } else {
        }
        break;
// TO DO:


      case VAR:
      case RECORDFORMAT:
      case UNINIT_OBJECT:
        break;
    }

return;
//TypeDecl[T].Object        OBJECT     Object;
//TypeDecl[T].Basetype      BASETYPE   Basetype;
//TypeDecl[T].Signedness    SIGNEDNESS Signedness;
//TypeDecl[T].Precision     PRECISION  Precision;
//TypeDecl[T].Proc          PROC       Proc;
//TypeDecl[T].NameInfo      AN_N       NameInfo;
//TypeDecl[T].Spec          SPECQ      Spec;
//TypeDecl[T].IsArray       ISARRAY    IsArray;
//TypeDecl[T].Area          AREA       Area;
//TypeDecl[T].Linkage       LINKAGE    Linkage;   
    switch ( READ(T, TypeDeclRA, TypeDecl) .Object) {
// when it is a procedure...
    case CODE:
//TypeDecl[T].paramfm

      break;
// if it is a string...

    case VAR:
//TypeDecl[T].strfm

      break;

    case RECORDFORMAT:
//TypeDecl[T].recfm

      break;

    case ARRAYFORMAT:
//TypeDecl[T].arrfm

      break;

    case SWITCHDEFN:
//TypeDecl[T].switchfm

      break;

    case LABELDEFN:
//TypeDecl[T].labelfm

      break;

    default:
      fprintf(stderr, "* Programmer error: looks like you added a new OBJECT and forgot to update the Describe_Type() switch.\n"); exit(1);
    }

    return;
  }

  void Describe_Var(VarDeclIDX V) {

    if (V == -1) {fprintf(stderr, "NO VARIABLE INFORMATION\n"); return;}
//VarDecl[V].varname                StrpoolIDX     varname;    // Object name


    if ( READ(V, VarDeclRA, VarDecl) .varname != -1) fprintf(stderr, "Variable name: %ls\n", pooltowstr( READ(V, VarDeclRA, VarDecl) .varname));
//VarDecl[V].aliasname              StrpoolIDX     aliasname;  // %alias name or -1


    if ( READ(V, VarDeclRA, VarDecl) .aliasname != -1) fprintf(stderr, "External %%alias name: %ls\n", pooltowstr( READ(V, VarDeclRA, VarDecl) .aliasname));
//VarDecl[V].c_name                 StrpoolIDX     c_name;     // mangled for use when outputting C code


    if ( READ(V, VarDeclRA, VarDecl) .c_name != -1) fprintf(stderr, "C representation of name: %ls\n", pooltowstr( READ(V, VarDeclRA, VarDecl) .c_name));
//  TypeDeclIDX    type;       // or -1

    Describe_Type( READ(V, VarDeclRA, VarDecl) .type);
//VarDecl[V].Fields                 VarDeclIDX     Fields;     // for record fields,
//VarDecl[V].Parameters             VarDeclIDX     Parameters; // and procedure parameter lists
//VarDecl[V].Bounds                 BoundsTypeIDX  Bounds;     // and array




    return;
  }
// DebugVar() REQUIRES debug_declarations = 1




  void DebugVarIDX_inner(int VarIDX, int debug, const char *file, const int line) {
    if (VarIDX == -1) {
      fprintf(stderr, "? WARNING!  DebugVarIDX(-1, \"%s\", %d)?\n", file, line); return;
    } else {
//fprintf(stderr, "! INFO  DebugVarIDX(0x%08x, \"%s\", %d)\n", VarIDX, file, line);

    }
    if (P_AST_type(VarIDX) != 0) {
      fprintf(stderr, "* Error: DebugVarIDX(0x%08X, %d) was passed a tagged index from %s, line %d\n", VarIDX, debug, file, line);
// VarIDX should be an index not an AST entry.

      VarIDX = VarIDX & AST_idx_mask;
    }
    if (debug == 0) return;
// NO! <-- picking up an RVALUE here...
    int Tag =  READ(VarIDX, VarDeclRA, VarDecl) .varname;
    int TypeIDX =  READ(VarIDX, VarDeclRA, VarDecl) .type;
// ***IF*** this is still the correct interpretation. PROBABLY NOT.
    char *s = Atom2Str(Tag);
    fprintf(stderr, "\"%s\", line %d: Debug VAR: VarIDX %d is %s\n", file, line, VarIDX, s);

    fprintf(stderr, "VarDecl[%d].varname     = 0x%08x  \"%s\"\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .varname,  READ(VarIDX, VarDeclRA, VarDecl) .varname == -1 ?"**Unassigned VARNAME**" : Atom2Str( READ(VarIDX, VarDeclRA, VarDecl) .varname));
    fprintf(stderr, "VarDecl[%d].aliasname   = 0x%08x  \"%s\"\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .aliasname,  READ(VarIDX, VarDeclRA, VarDecl) .aliasname == -1 ?"**Unassigned ALIASNAME**" : Atom2Str( READ(VarIDX, VarDeclRA, VarDecl) .aliasname));
    fprintf(stderr, "VarDecl[%d].c_name      = 0x%08x  \"%s\"\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .c_name,  READ(VarIDX, VarDeclRA, VarDecl) .c_name == -1 ?"**Unassigned C_NAME**" : Atom2Str( READ(VarIDX, VarDeclRA, VarDecl) .c_name));
// Note sure about these: partially duplicating info from TypeDecl...
// for record fields,

    fprintf(stderr, "VarDecl[%d].Fields      = 0x%08x\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .Fields);
// and procedure parameter lists
    fprintf(stderr, "VarDecl[%d].Parameters  = 0x%08x\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .Parameters);
// and array 
    fprintf(stderr, "VarDecl[%d].Bounds      = 0x%08x\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .Bounds);

    fprintf(stderr, "VarDecl[%d].type      -> TypeDecl[%d]\n", VarIDX,  READ(VarIDX, VarDeclRA, VarDecl) .type);
    fprintf(stderr, "TypeDecl[%d].Object     = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Object, Object_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Object]);
    fprintf(stderr, "TypeDecl[%d].Basetype   = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Basetype, Basetype_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Basetype]);
    fprintf(stderr, "TypeDecl[%d].Signedness = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Signedness, Signedness_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Signedness]);
    fprintf(stderr, "TypeDecl[%d].Precision  = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Precision, Precision_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Precision]);
    fprintf(stderr, "TypeDecl[%d].Proc       = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Proc, Proc_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Proc]);
    fprintf(stderr, "TypeDecl[%d].NameInfo   = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .NameInfo, NameInfo_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .NameInfo]);
    fprintf(stderr, "TypeDecl[%d].Spec       = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Spec, Spec_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Spec]);
    fprintf(stderr, "TypeDecl[%d].IsArray    = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .IsArray, IsArray_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .IsArray]);
    fprintf(stderr, "TypeDecl[%d].Area       = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Area, Area_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Area]);
    fprintf(stderr, "TypeDecl[%d].Linkage    = %d  %s\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .Linkage, Linkage_name[ READ(TypeIDX, TypeDeclRA, TypeDecl) .Linkage]);
// Union so only one is relevant...

    switch ( READ(TypeIDX, TypeDeclRA, TypeDecl) .Object) {

      case ARRAYFORMAT:
        fprintf(stderr, "TypeDecl[%d].arrfm      = %d\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .arrfm);
        break;

      case SWITCHDEFN:
        fprintf(stderr, "TypeDecl[%d].switchfm   = %d\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .switchfm);
        break;

      case LABELDEFN:
        fprintf(stderr, "TypeDecl[%d].labelfm    = %d\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .labelfm);
        break;
// only if a Rt/Fn/Map

      case CODE:
// WARNING: A Fn or Map needs both a recfm and a paramfm so these *cannot* be a union!

        fprintf(stderr, "TypeDecl[%d].paramfm    = %d", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .paramfm);
        ParamFormatIDX arg =  READ(TypeIDX, TypeDeclRA, TypeDecl) .paramfm;
        if (arg != -1) fprintf(stderr, ": ");
        while (arg != -1) {
          int numparams_remaining =  READ(arg, ParamRA, paramTYPE) .numparams_remaining;
          int param =  READ(arg, ParamRA, paramTYPE) .param;
          fprintf(stderr, " %s (%d)", Atom2Str( READ(param, VarDeclRA, VarDecl) .varname), numparams_remaining+1);
          arg =  READ(arg, ParamRA, paramTYPE) .next;
        }
        fprintf(stderr, "\n");
        if ( READ(TypeIDX, TypeDeclRA, TypeDecl) .Basetype == RECORD) {
          fprintf(stderr, "TypeDecl[%d].recfm      = %d A %%record points to an anonymous scope table containing the record fields...\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .recfm);
        } else if ( READ(TypeIDX, TypeDeclRA, TypeDecl) .Basetype == STRINGTYPE) {
          fprintf(stderr, "TypeDecl[%d].strfm      = %d A %%string points to some descriptor containing the length etc///\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .strfm);
        } else {
        }
        break;

      case RECORDFORMAT:
        fprintf(stderr, "TypeDecl[%d].recfm      = %d  A %%recordformat points to an anonymous scope table containing the record fields...\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .recfm);
        break;
// Only if a string type

      case VAR:
        if ( READ(TypeIDX, TypeDeclRA, TypeDecl) .Basetype == RECORD) {
          fprintf(stderr, "TypeDecl[%d].recfm      = %d A %%record points to an anonymous scope table containing the record fields...\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .recfm);
        } else if ( READ(TypeIDX, TypeDeclRA, TypeDecl) .Basetype == STRINGTYPE) {
          fprintf(stderr, "TypeDecl[%d].strfm      = %d A %%string points to some descriptor containing the length etc///\n", TypeIDX,  READ(TypeIDX, TypeDeclRA, TypeDecl) .strfm);
        } else {
        }
        break;

      default:
        fprintf(stderr, "MISSING OBJECT TYPE IN DebugVarIDX()\n");
    }
  }

  void Diagnose_inner(const char *Mess, int IDX, int depth, int test, char *info, char *file, int line) {
    if (!test) return;
    if (IDX == 0) {
      fprintf(stderr, "%sDiagnose(0x%08x, \"%s\", \"%s\", %d); // Usually 0 means an implicitly unassigned variable somewhere\n", Mess, IDX, info, file, line);
      return;
    } else if (IDX == -1) {
      fprintf(stderr, "%sDiagnose(0x%08x, \"%s\", \"%s\", %d); // Usually -1 means an explicitly unassigned variable somewhere\n", Mess, IDX, info, file, line);
      return;
    } else if (IDX == 0x80808080) {
      fprintf(stderr, "%sDiagnose(0x%08x, \"%s\", \"%s\", %d); // 0x80808080 means a monitored but unassigned variable\n", Mess, IDX, info, file, line);
      return;
    }
// to do: also call DebugVarIDX() when a var found...



    int i;
    for (i = 0; i < depth; i++) fprintf(stderr, "  ");
    fprintf(stderr, "%sDiagnose(0x%08x, \"%s\", \"%s\", %d): ", Mess, IDX, info, file, line);
    switch (P_AST_type(IDX)) {
    case AST_BIP:
      fprintf(stderr, "AST_BIP"); break;
    case AST_PHRASE:
      {
      fprintf(stderr, "AST_PHRASE 0x%08X stored at AST(%d)", IDX, P_AST_index(IDX));
      int op = P_op(IDX);
      switch (op) {
      case AST_DEFER_UNCOMPILED_PHRASE: fprintf(stderr, ": AST_DEFER_UNCOMPILED_PHRASE"); break;
      case AST_LVALUE:
        {
          fprintf(stderr, ": AST_LVALUE (contains a VarDeclIDX)\n");
          DebugVarIDX_inner(P_P(IDX, 1), TRUE, file, line);
        }
        return;
      case AST_RVALUE:
        {
          fprintf(stderr, ": AST_RVALUE (contains a VarDeclIDX)\n");
          DebugVarIDX_inner(P_P(IDX, 1), TRUE, file, line);
        }
        return;
      case AST_INDIRECT: fprintf(stderr, ": AST_INDIRECT"); break;
      case AST_ADDROF: fprintf(stderr, ": AST_ADDROF"); break;
      case AST_ADJUST_WIDTH: fprintf(stderr, ": AST_ADJUST_WIDTH"); break;
      case AST_COMMENT: fprintf(stderr, ": AST_COMMENT"); break;
      case AST_TOKEN: fprintf(stderr, ": AST_TOKEN"); break;
      case AST_CONST: fprintf(stderr, ": AST_CONST"); break;
      case AST_EXPRESSION: fprintf(stderr, ": AST_EXPRESSION"); break;
      case AST_TYPE_INFORMATION: fprintf(stderr, ": AST_TYPE_INFORMATION"); break;
      case AST_USER_PARENS: fprintf(stderr, ": AST_USER_PARENS"); break;
      case AST_BINARY_ARITHMETIC_OPERATION: fprintf(stderr, ": AST_BINARY_ARITHMETIC_OPERATION"); break;
      case AST_UNARY_ARITHMETIC_OPERATION: fprintf(stderr, ": AST_UNARY_ARITHMETIC_OPERATION"); break;
      case AST_ASSIGN: fprintf(stderr, ": AST_ASSIGN"); break;
      case AST_PROC_CALL: fprintf(stderr, ": AST_PROC_CALL"); break;
      case AST_APP_LIST: fprintf(stderr, ": AST_APP_LIST"); break;
      case AST_ARRAYACCESS: fprintf(stderr, ": AST_ARRAYACCESS"); break;
      case AST_FIELD: fprintf(stderr, ": AST_FIELD"); break;
      case AST_PENDING_APP_OR_FIELD: fprintf(stderr, ": AST_PENDING_APP_OR_FIELD"); break;
      case AST_BINOP: fprintf(stderr, ": AST_BINOP"); break;
      case AST_MONOP: fprintf(stderr, ": AST_MONOP"); break;
      case AST_ASSOP: fprintf(stderr, ": AST_ASSOP"); break;

      default:
        if (op < NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES) {
          fprintf(stderr, ": P_%ls (%d)  Alt=%d (created in %s, line %d)", phrasename[op], op, P_alt(IDX), pooltostr(P_FILE(IDX)|AST_POOL_LIT), P_LINE(IDX));
        } else {
          fprintf(stderr, ": Unknown op=%d", op);
        }
      }
      fprintf(stderr, "\n");
      }
      break;
    case AST_POOL_LIT:
      fprintf(stderr, " AST_POOL_LIT"); break;
    case AST_ATOM_LIT:
      fprintf(stderr, " AST_ATOM_LIT"); break;
    case BIP_TYPE:
      fprintf(stderr, " BIP_TYPE"); break;
    case PHRASE_TYPE:
      {
// Don't want to call PHRASE - need the location of our caller instead...

      extern wchar_t *PHRASE_inner(int G_PhraseStart, char *file, int line);
      int op = P_op(IDX);
      fprintf(stderr, "PHRASE_TYPE:  Phrase@AST(%d)%s%s%s",
              P_AST_index(IDX),
              IDX&NEGATED_PHRASE ? " Negated":"",
              IDX&GUARD_PHRASE ? " Guard":"",
              IDX&WHITESPACE_ALLOWED ? " Whitesp":"");
      fprintf(stderr, " Op=%d", op);
      fprintf(stderr, " (G_%ls)", PHRASE_inner(op, file, line));
      fprintf(stderr, "\n");
      }
      return;
    case SEMANTIC_TYPE:
      fprintf(stderr, "SEMANTIC_TYPE"); break;
    case KEYWORD_TYPE:
      fprintf(stderr, "KEYWORD_TYPE"); break;
    case CHAR_TYPE:
      fprintf(stderr, "CHAR_TYPE"); break;
    case UTF32CHAR_TYPE:
      fprintf(stderr, "UTF32CHAR_TYPE"); break;
    case STRING_TYPE:
      fprintf(stderr, "STRING_TYPE"); break;
    case UTF32STRING_TYPE:
      fprintf(stderr, "UTF32STRING_TYPE"); break;
    case REGEXP_TYPE:
      fprintf(stderr, "REGEXP_TYPE"); break;
    case OPTION_TYPE:
      fprintf(stderr, "OPTION_TYPE"); break;
    case COUNT_OF_ALTS:
      fprintf(stderr, "COUNT_OF_ALTS"); break;
    case COUNT_OF_PHRASES:
      fprintf(stderr, "COUNT_OF_PHRASES"); break;
    case ALT_NUMBER:
      fprintf(stderr, "ALT_NUMBER"); break;

    default:
      fprintf(stderr, "Unknown object tag %02x (%s)", ((unsigned)IDX)>>27U, TypeName[(((unsigned)IDX)>>AST_type_shift)&31]); break;
      return;
    }

    fprintf(stderr, " Index=%d\n", P_AST_index(IDX));
// End of Diagnose()
  }
// shunting yard algorithm for operator precedence.  In my previous imptoc
// I used a precedence grammar.  I'm trying a flat grammar this time with
// the precedence being added afterwards via Dijkstra's shunting yard algorithm
// just for the experience (and because that's how the ERCC's grammar was written)
// The corresponding code in the ERCC compilers is in TORP() in their pass2.
// I'ld like to say that I learned from their example but it was easier just
// to write it from first principles (using the description in Wikipedia as
// a crib).
  static int debug_torp = 0;

#define MAX_TORP 1024
// enum acts as an index into the struct array

  DECLARE(OperStack, Oper, MAX_TORP);
  #define _OperStack(x) WRITE(x,OperStack,Oper)
  #define OperStack(x) READ(x,OperStack,Oper)
  static int OperStack_nextfree = 0;
  static int OperStack_bottom = 0;
// a.k.a 'Output' in the description below.

  DECLARE(DataStack, Op_or_Data, MAX_TORP);
  #define _DataStack(x) WRITE(x,DataStack,Op_or_Data)
  #define DataStack(x) READ(x,DataStack,Op_or_Data)
  static int DataStack_nextfree = 0;
  static int DataStack_bottom = 0;

  static void ShowRP(char *message) {
    int i;
    if (debug_torp == 0) return;
    i = OperStack_nextfree;
    fprintf(stderr, "\n%s\n  OperStack:\n", message);
    if (i == 0) fprintf(stderr, "    <empty>\n"); else
    while (i > 0) {
          Oper y;
          y = OperStack(--i);
          fprintf(stderr, "    %ls\n", OpDetails[y].Sym);
          if (i && i == OperStack_bottom) fprintf(stderr, "    ------------- false bottom\n");
    }
    fprintf(stderr, "  DataStack:\n");
    i = DataStack_nextfree;
    if (i == 0) fprintf(stderr, "    <empty>\n"); else
    while (i > 0) {
          Op_or_Data Item;
          Item = DataStack(--i);
          if (Item.type == 'O') {
            fprintf(stderr, "    %ls\n", OpDetails[Item.idx].Sym);
          } else {
            fprintf(stderr, "    %ls\n", Decode(Item.idx));
          }
          if (i && i == DataStack_bottom) fprintf(stderr, "    ------------- false bottom\n");
    }
    fprintf(stderr, "\n");
  }

  void PushParen(wchar_t *Op) {
    if (debug_torp) fprintf(stderr, "PushParen(%ls)\n", Op);
// only '(' and ')' for now!

    Oper OPER;
    if (wcscmp(Op, L"(")==0) {
      OPER = OP_LPAREN;
    } else if (wcscmp(Op, L")")==0) {
      OPER = OP_RPAREN;
    } else {
      OPER = OP_NONE; fprintf(stderr, "* Error: PushParen(\"%ls\") unknown\n", Op);
    }
// 1.3

    if (OPER == OP_LPAREN) {
      _OperStack(OperStack_nextfree++) = OPER;
// 1.4
    } else if (OPER == OP_RPAREN) {
      for (;;) {
        OPER = OperStack(--OperStack_nextfree);
// 1.4.2
        if (OPER == OP_LPAREN) break;
// 1.4.1
        _DataStack(DataStack_nextfree++) = (Op_or_Data){'O',OPER};
      }
    }
    ShowRP("PushParen:\n");
  }

  void PushMonOp(int P) {
    Oper OPER;
    wchar_t *Ops;
    Ops = pooltowstr(P_P(P,1));

#ifdef NEVER
    if (debug_torp) fprintf(stderr, "PushMonOp(%ls)\n", Decode(P));

    if (debug_torp) fprintf(stderr, "PushMonOp: Ops = \"%ls\"\n", Ops);

    if ( wcscmp(Ops, L"\\")==0 || wcscmp(Ops, L"~")==0 ) {
      OPER = OP_NOT;
    } else if (wcscmp(Ops, L"-")==0) {
      OPER = OP_NEG;
    } else {
      OPER = OP_NONE;
      fprintf(stderr, "* Error: PushMonOp(\"%ls\") unknown\n", Ops);
    }
#else
//Diagnose("", P,0, debug_torp);

    OPER = P_P(P,2);
#endif

    for (;;) {
      if (OperStack_nextfree == OperStack_bottom) break;
      Oper O2 = OperStack(OperStack_nextfree-1);
      if (O2 == OP_LPAREN) break;
// Not 100% sure this is all valid for monops. Works well for binops.
// Also somewhat dubious about the precedence levels copied from the ERCC Imp compiler.




      if ( (OpDetails[O2].Prec > OpDetails[OPER].Prec)
          || (OpDetails[OPER].Assoc == Left && (OpDetails[O2].Prec == OpDetails[OPER].Prec))) {
          O2 = OperStack(--OperStack_nextfree);
          if (debug_torp) fprintf(stderr, "A: Transferring %ls to DataStack\n", OpDetails[O2].Sym);
          _DataStack(DataStack_nextfree++) = (Op_or_Data){'O',O2};
      } else break;
    }
// <-- runtime error, don't know why. OperStack_nextfree is 0
    _OperStack(OperStack_nextfree++) = OPER;
    ShowRP("PushMonOp:\n");

  }


  void PushBinOp(int P) {
// 1.2

    Oper OPER;
    wchar_t *Ops;
    Ops = pooltowstr(P_P(P,1));
// Grammar now returns OP_* so this is no longer needed...

#ifdef NEVER
    if (debug_torp) fprintf(stderr, "PushBinOp(%ls)\n", Decode(P));
// P came from a 'wlit()' call and should be a StrpoolIDX pointer to \ or ~
// only '\' or '~', and '-' for now!




    if (debug_torp) fprintf(stderr, "PushBinOp: Ops = \"%ls\"\n", Ops);

    if (wcscmp(Ops, L"+")==0) {
      OPER = OP_ADD;
    } else if (wcscmp(Ops, L"-")==0) {
      OPER = OP_SUB;
    } else if (wcscmp(Ops, L"&")==0) {
      OPER = OP_AND;
// or \\ ...
    } else if (wcscmp(Ops, L"****")==0) {
      OPER = OP_IEXP;
// or \ ...
    } else if (wcscmp(Ops, L"**")==0) {
      OPER = OP_REXP;
    } else if (wcscmp(Ops, L"^^")==0) {
      OPER = OP_IEXP;
    } else if (wcscmp(Ops, L"^")==0) {
      OPER = OP_REXP;
    } else if (wcscmp(Ops, L"*")==0) {
      OPER = OP_MULT;
    } else if (wcscmp(Ops, L"!!")==0) {
      OPER = OP_EOR;
    } else if (wcscmp(Ops, L"!")==0) {
      OPER = OP_OR;
// avoid bug in takeon where a '//' in a string is handled as if it were a C comment.
    } else if (Ops[0] == L'/' && Ops[1] == L'/' && Ops[2] == L'\0') {
// (I need to check if that is still the case...)

      OPER = OP_INTDIV;
    } else if (wcscmp(Ops, L"/")==0) {
// %begin
//   %real r
//   r = 3/5     ;! beware: a naive conversion to C will cause a result of 1.0 (because both operands of '/' will be ints)
//   print(r,5)  ;! 0.6
// %endofprogram





      OPER = OP_REALDIV;
    } else if (wcscmp(Ops, L">>")==0) {
      OPER = OP_RSHIFT;
    } else if (wcscmp(Ops, L"<<")==0) {
      OPER = OP_LSHIFT;
    } else if (wcscmp(Ops, L".")==0) {
      OPER = OP_CONCAT;
    } else {
      fprintf(stderr, "Error: PushBinOp(\"%ls\") unknown\n", Ops);
      OPER = OP_NONE;
    }
#else
//Diagnose("", P,0, debug_torp);

    OPER = P_P(P,2);
#endif
// 1.2.1
    for (;;) {
      if (OperStack_nextfree == OperStack_bottom) break;
      Oper O2 = OperStack(OperStack_nextfree-1);
      if (O2 == OP_LPAREN) break;
      if ( (OpDetails[O2].Prec > OpDetails[OPER].Prec)
          || (OpDetails[OPER].Assoc == Left && (OpDetails[O2].Prec == OpDetails[OPER].Prec))) {
          O2 = OperStack(--OperStack_nextfree);
          if (debug_torp) fprintf(stderr, "A: Transferring %ls to DataStack\n", OpDetails[O2].Sym);
          _DataStack(DataStack_nextfree++) = (Op_or_Data){'O',O2};
      } else break;
    }
// <-- runtime error, don't know why. OperStack_nextfree is 0
    _OperStack(OperStack_nextfree++) = OPER;
    ShowRP("PushBinOp:\n");
  }

  void PushConst(int P) {
    if (debug_torp) fprintf(stderr, "PushConst(%ls)\n", Decode(P));
// 1.5 either literal constant or symbolic constant

    _DataStack(DataStack_nextfree++) = (Op_or_Data){'D',P};
    ShowRP("PushConst:\n");
  }

  void PushVar(int P) {
    if (debug_torp) fprintf(stderr, "PushVar(%ls)\n", Decode(P));
// 1.5 either literal constant or symbolic constant

    _DataStack(DataStack_nextfree++) = (Op_or_Data){'D',P};
    ShowRP("PushVar:\n");
  }

  void CHECK_CONSTANT(int AstOp) {
// If this is a constant, or a MONOP or BINOP whose arguments
// are constant, mark this tuple as being a constant as well.
// For now we just want to know if it is recognised as a constant
// expression, we don't need the actual value of the constant -
// we're going to pass that on to C.  However there are a few
// cases where we *do* need to know the value of the constant
// or constant expression, which we will implement later and
// add a pointer to another hidden field.  It will have to
// include both the value and the type. (Though I think we're
// already handling the type in its own hidden field)










    if (P_op(AstOp) == AST_UNARY_ARITHMETIC_OPERATION) {
      if (P_ISCONST(P_P(AstOp,1))) {
        P_ISCONST(AstOp) = 1;
      }
    } else if (P_op(AstOp) == AST_BINARY_ARITHMETIC_OPERATION) {
      if (P_ISCONST(P_P(AstOp,1)) && P_ISCONST(P_P(AstOp,2))) {
        P_ISCONST(AstOp) = 1;
      }
    } else if (P_op(AstOp) == AST_CONST) {
      P_ISCONST(AstOp) = 1;
    } else if (P_op(AstOp) == AST_TOKEN) {
// TO DO: Check for a const variable!
// P_ISCONST(AstOp) = 1;


    } else {
// still to handle base constants

    }
  }

  int Stack_to_Tree(void) {
    Oper op;
    int t[4];
    int Left, Right;
    if (DataStack_nextfree == DataStack_bottom) {
      fprintf(stderr, "* Error: expression stack ran out of data\n");
      return -1;
    }
    Op_or_Data TOS = DataStack(--DataStack_nextfree);
    if (TOS.type == 'O') {
      if (OpDetails[TOS.idx].Arity == 1) {
        t[1] = TOS.idx;
        t[2] = Left = Stack_to_Tree();
        int op = P_mktuple(AST_UNARY_ARITHMETIC_OPERATION, 0, 2, t);
        CHECK_CONSTANT(op);
        return op;
      } else if (OpDetails[TOS.idx].Arity == 2) {
        t[1] = TOS.idx;
        t[2] = Right = Stack_to_Tree();
        t[3] = Left = Stack_to_Tree();
        int op = P_mktuple(AST_BINARY_ARITHMETIC_OPERATION, 0, 3, t);
        CHECK_CONSTANT(op);
        return op;
      } else {
        fprintf(stderr, "* Error: bad operator on stack\n");
      }
    } else if (TOS.type == 'D') {
      return TOS.idx;
    } else {
      fprintf(stderr, "* Error: bad data type on stack\n");
    }
    return -1;
  }

  void PushExpr(int P) {
    if (debug_torp) fprintf(stderr, "PushExpr(%ls)\n", Decode(P));
    _DataStack(DataStack_nextfree++) = (Op_or_Data){'D',P};
    ShowRP("PushExpr:\n");
  }

  int ExPop(void) {
// At one point this did not handle user brackets if there was valid code on the stack below it, eg for "a + (b + c)"
// it was not possible to evaluate "(b + c)" without also picking up "a +" when the "b c +" was popped off the stack,
// which at that time consists of a b c + +.
// THIS WAS FIXED USING HAMISH'S FALSE BOTTOM TECHNIQUE (as seen in EMAS Pop2) where instead of testing the bottom of
// the stack at index 0, we compare against DataStack_bottom instead.  Then at the point of evaluating an <EXPR> that
// is within explicit parentheses, we save the old stack bottom, evaluate the expr as if in a virgin empty stack, then
// we restore the previous stack bottom.  All of which costs zero overhead in this code.  Very elegant, Hamish.










    if (debug_torp) fprintf(stderr, "\nExPop()  Oper Stack contains %d items\n", OperStack_nextfree);
    ShowRP("Expop before final transfers:");
// 2. Pop any remaining operator tokens from the stack to the output

    while (OperStack_nextfree > OperStack_bottom) {
          Oper y;
          y = OperStack(--OperStack_nextfree);
          if (debug_torp) fprintf(stderr, "B: Transferring %ls to DataStack\n", OpDetails[y].Sym);
          _DataStack(DataStack_nextfree++) = (Op_or_Data){'O',y};
    }
// TO DO: Construct AST tree here...

    ShowRP("Expop before converting to AST:");
    int Expr = Stack_to_Tree();
    if (DataStack_nextfree > DataStack_bottom) {
      fprintf(stderr, "? Warning: TORP stack is not empty (%d items remaining) after extracting an <EXPR> as an AST tree.\n", DataStack_nextfree);
// forcibly empty for now.  only during initial development.
      DataStack_nextfree = 0; OperStack_nextfree = 0;
    }
    return Expr;
  }

  DECLARE(DeclStack, int, 1024);


  static int DeclStack_nextfree = 0;

  static void PushDecl(int i) {
    WRITE(DeclStack_nextfree++,DeclStack,int) = i;
  };
  static int PopDecl(void) {
    int i =  READ(--DeclStack_nextfree,DeclStack,int) ;
    return i;
  };


  int ArrayDims = 0;
// is there something such as 'PHRASE' I could use instead of 'int'?
  DECLARE(LowBound, int, 16);
  DECLARE(HighBound, int, 16);




  void AddDims(int Low, int High) {  WRITE(ArrayDims, LowBound, int) = Low;  WRITE(ArrayDims, HighBound, int) = High; ArrayDims += 1; }
// To users of my 'uparse' parser generator - this is how you create your own type of AST tree,
// independent of what is supplied by my default code:
// Some sweet syntactic sugar here to create a varargs-type call to construct and deconstruct AST tuples with variable numbers of parameters
// (There's also code elsewhere to handle them as variant records instead, but at some point I'll want to choose between the two forms)







  static const int AST_end_marker = 0x81818181;

  #define AST_mktuple(c_op...) AST_mktuple_inner(__FILE__, __LINE__, c_op, AST_end_marker)
  int AST_mktuple_inner(const char *file, const int line, int AST_op, ...) {
    int t[LARGEST_ALT];
    va_list ap;
    int i, count = 0;

    t[0] = 0;

    va_start(ap, AST_op);
    for (;;) {
      i = va_arg(ap, int);
// Inserted by wrapper macro. Can't determine count otherwise.
      if (i == AST_end_marker) break;
      count += 1;
      if (count == LARGEST_ALT) {
        fprintf(stderr, "** Error: AST_mktuple is trying to create a tuple with more items than I can handle, in %s line %d\n", file, line); exit(1);
      }
      t[count] = i;
    }
    va_end(ap);

    int op = P_mktuple(AST_op, 0, count, t);
    CHECK_CONSTANT(op);
    return op;
  }

  #define AST_detuple(astp...) AST_detuple_inner(__FILE__, __LINE__, astp, &AST_end_marker)
  void AST_detuple_inner(const char *file, const int line, int astp, int AST_op, int *ptrs, ...) {
// The given AST_op has to match the stored one.  The expected number of parameters has to match too.
// However there is no specific check on the individual parameters.



    va_list ap;
// these may not yet have been inserted by P_whatever?()
    int count = 1, creation_line;
    char *creation_file;
    int *ptr;

    if (P_op(astp) != AST_op) {
      if (AST_op <= AST_FIRST_OP || AST_op >= AST_LAST_OP) {
        fprintf(stderr, "AST_detuple at %s, line %d: params should be astp, AST_something...\n", file, line);
      }
//if (P_op(astp) == AST_TO_DO) {
//  fault("Can't unpack - still to implement: %s from %s line %d", pool_to_str(P_P(astp, 1)), P_whatever1(astp), P_whatever2(astp));
//} else {



        fprintf(stderr, "AST at %d is a %d, not a %d. (see %s, line %d)", astp, P_op(astp), AST_op, file, line);
//}

    }
//creation_file = P_whatever1(astp);
//creation_line = P_whatever2(astp);
//if (debug_ast_creation) {
//  fprintf(stderr, "AST_tuple(%s) at %d was created at line %d and extracted at line %d\n", CAstOPName(AstOP(astp)), astp, creation_line, line);
//}








    if (ptrs) *ptrs = P_P(astp, count);
//fprintf(stderr, "%p: P_P(%d,%d) = %d\n", ptrs, astp, count, *ptrs);

    va_start(ap, ptrs);
    for (;;) {
      ptr = va_arg(ap, int *);
// Inserted by wrapper macro. Can't determine count otherwise.
      if (ptr == &AST_end_marker) break;
      count += 1;
// NULL ptrs are skipped.
      if (ptr) *ptr = P_P(astp, count);
//fprintf(stderr, "%p: P_P(%d,%d) = %d\n", ptr, astp, count, *ptr);

    }
    va_end(ap);

    if (P_count(astp) != count) {
// *Extremely* useful test!

      fprintf(stderr, "? Warning: AST at %d has %d members, not the %d given as parameters to AST_detuple().\n"
                      "(see %s, line %d)\n", astp, P_count(astp), count, file, line);
    }
  }

  TypeDeclIDX TYPEOF_inner(int ASTidx, const char *file, const int line) {
// Access hidden field of any tuple

    return P_TYPEINFO(ASTidx);
  }
// returns VarDeclIDX of new declaration

  int Declaration(int depth, int More) {
// if (More) just print the tag, not the whole declaration.
// i.e. for "int A, B;" rather than "int A; int B;".
// (This option is not properly implemented yet)




  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Object = Object ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Basetype = Basetype ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Signedness = Signedness ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Precision = Precision ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Proc = Proc ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .NameInfo = NameInfo ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Spec = Spec ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .IsArray = IsArray ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Area = Area ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .Linkage = Linkage ;
// TO DO:

  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .arrfm = -1 ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .paramfm = -1 ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .recfm = -1 ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .strfm = -1 ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .switchfm = -1 ;
  WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .labelfm = -1 ;

  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .varname = DTag;
  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .aliasname = -1;
// TEMPORARY
  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .c_name = DTag;
  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .type = TypeDecl_nextfree;

  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .Fields = -1;
  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .Parameters = -1;
  WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .Bounds = -1;
// <DECLARE> collects all the pieces that preceded it, which were set up during the walk
// of the CST (by noting information in a handful of global variables), and uses them to
// modify the scoped name tables.  It currently outputs the declarations on the fly rather
// than during a subsequent walk of the generated AST, the next iteration of improving the
// compiler design will have this procedure return AST tuples representing the declarations,
// with the output being deferred to a call to generate_c...
// When that is done, it's not clear at this point whether the AST tuples should be responsible
// for both the data structure modification *and* the C generation, or whether the updates
// to the name tables should be done here, with only the C generation being done separately
// from the AST tuples by generate_c().
// One factor in deciding which strategy to use might be the handling of initialised declarations?
// However it may be essential to create the name tables on the fly as the state of declarations
// may be needed to steer the parser, though at the moment it is not looking like that is necessary.
// If it were, and if we were to try to postpone the C code generation until the CST was fully
// converted to an AST, we would have to regenerate the name tables during that tree walk.
// Until I see a definite need to do otherwise, I'm going to assume that we can postpone the
// creation of the name tables until the AST is walked.  Note that the reason for these issues
// is that the ERCC compilers handled a statement at a time but this parser is (currently)
// parsing the entire program before the AST is interpreted.
// TO DO: when declaring a %record, create a new scope which is just for that record.
//        then when accessing a record field, look within the scope that it attached
//        to the parent object for the field name, which will point to the details for
//        that field.  This will align normal declarations and fields more consistently.
// When handling things like record formats and procedure parameters, rather than resetting
// the state variables for declarations - push them on a stack instead and *then* clear
// them down.  So that the parameters or fields can be parsed like any other declaration
// and then attached to the parent - pop the stack on the end of the record format or
// formal parameter list so that anything which follows at the parent level will preserve
// whatever context was active.
// DESCRIBE:
  if (debug_declarations) {
// This is the info we are going to use to create the declaration

    codegen("  /* ");
// Tag is a global containing the <DECLNAME>
    PrintAtom(DTag);
// that immediately preceded the <DECLARE>

    codegen(": Object=%s TypeDecl=%d VarDecl=%d ",
            Object_name[Object], TypeDecl_nextfree, VarDecl_nextfree);
    if (Object == LABELDEFN) {
    } else if (Object == CODE) {
      codegen("Proc=%s ", Proc_name[Proc]);
      codegen("Linkage=%s ", Linkage_name[Linkage]);
// FN/MAP
      if (Proc != ROUTINE) {
        if (Basetype != RECORD && Precision != COMPOUND)
          codegen("Sign=%s ", Signedness_name[Signedness]);
        codegen("Prec=%s ", Precision_name[Precision]);
        codegen("Type=%s ", Basetype_name[Basetype]);
        codegen("NameInfo=%s ", NameInfo_name[NameInfo]);
        if (Proc == FN && NameInfo == OBJECTNAME)
          codegen("(== %%map) ");
        if (Proc == MAP && NameInfo == NO_NAME)
          codegen("(== %%name %%fn) ");
      }
      codegen("Spec=%s ", Spec_name[Spec]);
    } else if (Object == VAR) {
      codegen("Area=%s ", Area_name[Area]);
      if (Proc != UNINIT_PROC) codegen("Proc=%s ", Proc_name[Proc]);
//codegen("format=%s ", IsFormat_name[IsFormat]);

      codegen("IsArray=%s ", IsArray_name[IsArray]);
      if (Basetype != RECORD && Precision != COMPOUND)
        codegen("Sign=%s ", Signedness_name[Signedness]);
      codegen("Prec=%s ", Precision_name[Precision]);
      codegen("Type=%s ", Basetype_name[Basetype]);
      codegen("NameInfo=%s ", NameInfo_name[NameInfo]);
      codegen("Spec=%s ", Spec_name[Spec]);
    } else if (Object == SWITCHDEFN) {
      codegen("Area=%s ", Area_name[Area]);
      codegen("Type=%s ", Basetype_name[Basetype]);
      codegen("NameInfo=%s ", NameInfo_name[NameInfo]);
      codegen("IsArray=%s ", IsArray_name[IsArray]);
    } else if (Object == RECORDFORMAT) {
      codegen("Type=%s ", Basetype_name[Basetype]);
      codegen("Spec=%s ", Spec_name[Spec]);
    } else if (Object == ARRAYFORMAT) {
      if (Basetype != RECORD && Precision != COMPOUND)
        codegen("Sign=%s ", Signedness_name[Signedness]);
      codegen("Prec=%s ", Precision_name[Precision]);
      codegen("Type=%s ", Basetype_name[Basetype]);
      codegen("NameInfo=%s ", NameInfo_name[NameInfo]);
      codegen("IsArray=%s ", IsArray_name[IsArray]);
      codegen("Spec=%s ", Spec_name[Spec]);
    }
// HACK! *ADJUSTED* DEPTH: for some reason depth is wrong if a %spec.
// Need to track down cause. Has to do with where push_scope() is called, I expect, and order of compile() calls.


    codegen("Depth=%d ", ctrl_depth()+(Spec==SPEC?1:0));
    codegen("*/ ");
  }
// GENERATE CODE:




  switch (Object) {
// ##################################### VARIABLES ####################################



  case VAR: {
// ####################################### AREA #######################################
// top level data declarations must be one of %external, %constant, or %own.
// All can be initialised except %external %spec (aka %extrinsic)





    if (Area == OWN) codegen("static ");
    else if (Area == EXTDATA) {
// I don't believe that these external data declarations have to come at the top level *unless* they're initialised.

      codegen("extern ");
    } else if (Area == CONSTANT) codegen("const ");
// ##################################### BASETYPE #####################################



    if (Basetype == RECORD) {
// This is the declaration of a record or record array etc of the given recordformat type
// which by this point already exists.  It is *not* the declaration of the recordformat itself.
// %recordformat index in VarDecl[]





      WRITE(VarDecl_nextfree, VarDeclRA, VarDecl) .Fields = RecordTypeIDX;

      if ( READ( READ(RecordTypeIDX, VarDeclRA, VarDecl) .type, TypeDeclRA, TypeDecl) .recfm == -1) {
        fprintf(stderr, "? The recfm field of the TypeDecl corresponding to the recordformat declaration is -1\n");
        DebugVarIDX(RecordTypeIDX, debug_declarations);
        exit(1);
      }
// anonymous scope table containing fields

      WRITE(TypeDecl_nextfree, TypeDeclRA, TypeDecl) .recfm =  READ( READ(RecordTypeIDX, VarDeclRA, VarDecl) .type, TypeDeclRA, TypeDecl) .recfm;

      if ( READ(RecordTypeIDX, VarDeclRA, VarDecl) .c_name == -1) {
        codegen("/*pending name #1*/ERROR  ");
      } else {
        codegen("%ls ", Atom2WStr( READ(RecordTypeIDX, VarDeclRA, VarDecl) .c_name));
      }

    } else if (Basetype == STRINGTYPE) {
      codegen("_imp_string ");
    } else if (Basetype == INTEGER) {
      if (Signedness == UNSIGNED) codegen("unsigned ");
      if (Precision == BYTE) codegen("char ");
      else if (Precision == SHORT) codegen("short int ");
      else if (Precision == WORD) codegen("int ");
      else if (Precision == LONGWORD) codegen("long int ");
      else if (Precision == QUADWORD) codegen("long long int ");
      else if (Precision == ADDR) codegen("void *");
    } else if (Basetype == FLOAT) {
      if (Precision == WORD) codegen("float ");
      else if (Precision == LONGWORD) codegen("double ");
      else if (Precision == QUADWORD) codegen("long double ");
      else if (Precision == ADDR) codegen("void *");
    } else if (Basetype == GENERIC) {
// %name parameter
      codegen("void *");
    }
// ##################################### NAMEINFO #####################################



    if (NameInfo == OBJECTNAME) codegen("*");
    else if (NameInfo == ARRAYNAME) codegen("**");
// ####################################### TAG ########################################



    PrintAtom(DTag);
// ###################################### ISARRAY #####################################
// May need bounds.  This should also work for %SWITCH declarations.



    if (IsArray == ARRAY) {
      int D;
// TO DO: This prints out the declaration but does not YET add it to the name/scope tables
// When we do, there is already an array BoundsRA for storing the data.  Note that LowBound
// and HighBound are just temporary communication variables, not part of the long term
// name tables.  We *could* go straight to the BoundsRA array but if/when I make that
// decision, I need to have done a lot more validation and testing.
// done elsewhere PrintAtom(DTag);








      for (D = 0; D < ArrayDims; D++) {
        int Low =  READ(D, LowBound, int) ;
        int High =  READ(D, HighBound, int) ;
// TO DO: if Low is not constant, create a variable that is initialised to the value
// of the lower bound at the point where the array is declared, and use that when
// accessing array elements via the $define macro, to protect against the value of
// the expression representing the lower bound changing when some variable in that
// expression is assigned to later.
// Also, we'll need those for each dimension, maybe saved in an array of arrayname_low[ArrayDims]
// Note that the $define to access an array element can also be used to perform
// array bound checking on array indices (although the overhead may be significant,
// but the error reporting should be better than what you would get from using
// valgrind or the gcc extensions that can perform checks)
// These macro expansions are ugly in the generated C.  It would be nicer to modify
// the indices in the compiler which would allow the tree representing the index expression
// to be output with fewer brackets and maybe with constants having been folded.
// The compromise (when using imp80 to C to translate an Imp source with a view to
// maintaining the C source in future) is to maintain the source with the embedded
// $define directives from gtcpp rather than the cleaner pure C source that is
// output by gtcpp.
        codegen("[(");
        generate_c(High, depth+1);
        codegen(")-(");
        generate_c(Low, depth+1);
        codegen(")+1]");
      }
// *X* codegen(";\n");

      codegen("\n");

      codegen("\n$define "); PrintAtom(DTag);
      for (D = 0; D < ArrayDims; D++) {
        codegen("[N%0d]", D);
      }
      codegen(" "); PrintAtom(DTag);
      for (D = 0; D < ArrayDims; D++) {
        codegen("[(N%0d)-(", D);
        int Low =  READ(D, LowBound, int) ;
        generate_c(Low, depth+1);
        codegen(")]");
      }
      codegen("\n");
    }
// ####################################### END ########################################
//if (Area == PARAMETER) {
// comma handled elsewhere
//} else {
//codegen("; "); // CANNOT OUTPUT ';' HERE IF THIS IS AN INITIALISED DECLARATION!
// this should be handled elsewhere too...
//}









    break;
// case VAR
  }
// ################################### PROCEDURES ####################################


  case CODE: {
// ##################################### LINKAGE #####################################



    if (Linkage == EXTPROC) {
      if (Spec == SPEC) {
        codegen("extern ");
      } else {
// The body of an Imp %externalroutine does not require "extern" in C.
// An external *spec* does *NOT* have to be at the top level although
// an external body does.
// declaration of an externalroutine or function etc *but* only valid if at the top level.  So check.  (TO DO)
      }
// See comment at head of ctrl_depth()   // HACK: *ADJUSTED* depth Looking for %spec-related bug.
    } else if (ctrl_depth()+(Spec==SPEC?1:0) <= 1) {
      codegen("static ");
    } else {
      if (Spec == SPEC) codegen("auto "); else codegen("/* nested auto */ ");
// GCC requires "auto" for forward references to nested procedures, but not for the procedures themselves. (but it is allowed)

    }
// ##################################### BASETYPE #####################################



    if (Proc == ROUTINE) {
      codegen("void ");
    } else if (Basetype == RECORD) {
// This is the declaration of a %recordfn or %recordmap where the format of the record
// has already been declared.  It is not the declaration of the recordformat itself.




      codegen("/*pending name #2*/recfm  ");
    } else if (Basetype == STRINGTYPE) {
      codegen("_imp_string ");
    } else {
      if (Basetype == INTEGER) {
        if (Signedness == UNSIGNED) codegen("unsigned ");
        if (Precision == BYTE) codegen("char ");
        else if (Precision == SHORT) codegen("short int ");
        else if (Precision == WORD) codegen("int ");
        else if (Precision == LONGWORD) codegen("long int ");
        else if (Precision == QUADWORD) codegen("long long int ");
        else if (Precision == ADDR) codegen("void *");
      } else if (Basetype == FLOAT) {
        if (Precision == WORD) codegen("float ");
        else if (Precision == LONGWORD) codegen("double ");
        else if (Precision == QUADWORD) codegen("long double ");
        else if (Precision == ADDR) codegen("void *");
      }
    }
    if (NameInfo == OBJECTNAME) codegen("*");
    if (Proc == MAP) codegen("*");
    PrintAtom(DTag);
    break;
// end case CODE
  }
// ################################### RECORD FORMAT ####################################


  case RECORDFORMAT: {
// Declaration() creates an empty VarDecl and TypeDecl for a recordformat declaration,
// but currently the *CALLING CODE* is responsible for populating it.
// fprintf(stderr, "? Remember to populate the recordformat declaration in the calling code.  VarDeclIDX = %d  TypeDeclIDX = %d\n", VarDecl_nextfree, TypeDecl_nextfree);



    break;
// end case RECORDFORMAT  
  }
// ################################### ARRAY FORMAT ####################################



  case ARRAYFORMAT: {
    codegen("/* TO DO: %%ARRAYFORMAT DECLARATION */\n");
    break;
// end case ARRAYFORMAT
  }
// ################################### SWITCH ####################################



  case SWITCHDEFN: {
// TO DO: check dims and warn if dims != 1

    int Low =  READ(0, LowBound, int) ;
    int High =  READ(0, HighBound, int) ;
    codegen("declare_switch(");
    PrintAtom(DTag);
    codegen(", ");
    generate_c(Low, depth+1);
    codegen(", ");
    generate_c(High, depth+1);
    codegen(");\n");
    break;
// end case SWITCHDEFN
  }
// ################################### LABEL ####################################




  case LABELDEFN: {
    codegen("/* TO DO: %%LABEL DECLARATION */\n");
    break;
// end case LABELDEFN
  }
// ################################### <ERROR> ####################################



  case UNINIT_OBJECT: {
    break;
// end case UNINIT_OBJECT
  }
// end switch

  }
// If this declaration is a formal parameter, attach it to the 'paramfm' of the parent procedure.



    if (Area == PARAMETER) {
//TypeDecl[TypeDecl_nextfree].paramfm = TypeDecl[ParentTypeIDX].paramfm; // linked list (albeit reversed)
//TypeDecl[ParentTypeIDX].paramfm = TypeDecl_nextfree; <-- NO
//fprintf(stderr, "****** I have a parameter: ATTACHING TO ParentVarIDX = %d, ParentTypeIDX = %d\n", ParentVarIDX, ParentTypeIDX);





      ParamFormatIDX This_param_idx = Param_nextfree++;
      ParamFormatIDX Param_idx;
      if (ParentTypeIDX == -1) Param_idx = -1; else Param_idx =  READ(ParentTypeIDX, TypeDeclRA, TypeDecl) .paramfm;

      WRITE(This_param_idx, ParamRA, paramTYPE) .numparams_remaining = 0;
      WRITE(This_param_idx, ParamRA, paramTYPE) .param = VarDecl_nextfree;
      WRITE(This_param_idx, ParamRA, paramTYPE) .next = -1;

      if (Param_idx == -1) {
// initial parameter

        if (ParentTypeIDX == -1) {
// error

        } else {
          READ(ParentTypeIDX, TypeDeclRA, TypeDecl) .paramfm = This_param_idx;
        }
      } else {
// Chain down to the end of the parameter list, and while doing so,
// increment the field that says how many more parameters remain
// after each parameter. (This is not strictly needed but is helpful)



        while ( READ(Param_idx, ParamRA, paramTYPE) .next != -1) {
          READ(Param_idx, ParamRA, paramTYPE) .numparams_remaining += 1;
          Param_idx =  READ(Param_idx, ParamRA, paramTYPE) .next;
        }
        READ(Param_idx, ParamRA, paramTYPE) .numparams_remaining += 1;
        READ(Param_idx, ParamRA, paramTYPE) .next = This_param_idx;
      }
// The TypeDecl for this var is now updated with all the parameters we know about so far...
// Still have something similar TO DO for the parent VarDecl


    }

    TypeDecl_nextfree += 1;
    VarDecl_nextfree += 1;
// fprintf(stderr, "Declaration(): Adding \"%ls\" to \"decl\" scope table.\n", Atom2WStr(DTag));

    add_entry("decl", Atom2WStr(DTag), VarDecl_nextfree-1);
//                ^ AST_ATOM_LIT, but would prefer a AST_POOL_LIT


    if (Area == PARAMETER && ParentTypeIDX != -1) DebugVarIDX(ParentTypeIDX, debug_declarations);

    return VarDecl_nextfree-1;
// end of Declaration()
  }
// this type parameter may not be needed if type info is properly attached to var and field

  int MkField(int Var, int Field) {
    if (Var == -1 || Field == -1) {
// return -1;
      fprintf(stderr, "* MkField(%d, %d)\n", Var, Field); exit(1);
    }

    int Type = TYPEOF(Var);
    if (Type == -1 || Type == 0x80808080) {
      fprintf(stderr, "* MkField: Var has no typeinfo attached.\n");
      Diagnose("Var: ", Var, 0, TRUE);
      exit(1);
    }

    int IsArrayName =  READ(Type, TypeDeclRA, TypeDecl) .NameInfo == ARRAYNAME;
    int IsObjectName =  READ(Type, TypeDeclRA, TypeDecl) .NameInfo == OBJECTNAME;
    int IsName = IsArrayName || IsObjectName;

    int IsRecord =  READ(Type, TypeDeclRA, TypeDecl) .Basetype == RECORD;

    if (!IsRecord) {
      fprintf(stderr, "* Error: Cannot access a field of an object (0x%08x) which is not a %%RECORD\n", Var);
      Diagnose("Var: ", Var, 0, TRUE);
      exit(1);
      return -1;
    }
// VarDecl[Var].Fields ought to be the recordformat declaration. Maybe. If Var is a VarDeclIDX.
// remember, as well as constructing a tuple that accesses the field, we need
// to return the type information of the record field as part of that tuple.






    int Fielded = AST_mktuple(AST_FIELD, Var, Field);

    P_TYPEINFO(Fielded) = P_TYPEINFO(Field);
// TypeDecl info for the field, extracted from the %recordformat


    return Fielded;
  }
// this type parameter may not be needed if type info is properly attached to Var

  int MkApp(int Expr, int More) {
    int App = AST_mktuple(AST_APP_LIST, Expr, More);
    return App;
  }

  int CopyType(int OldType) {
    int NewType = TypeDecl_nextfree++;
// Copy entire struct, not just the index.
    WRITE(NewType, TypeDeclRA, TypeDecl) =  READ(OldType, TypeDeclRA, TypeDecl) ;
    return NewType;
  }
// this type parameter may not be needed if type info is properly attached to Var

  int MkCall(int Var, int APP) {
#ifndef NEVER
    if ( READ(TYPEOF(Var), TypeDeclRA, TypeDecl) .Proc != ROUTINE &&  READ(TYPEOF(Var), TypeDeclRA, TypeDecl) .Proc != FN &&  READ(TYPEOF(Var), TypeDeclRA, TypeDecl) .Proc != MAP) {
      fprintf(stderr, "* Error: Cannot call something that is not a Rt/Fn/Map\n");
      Diagnose("Param was ", Var, 0, TRUE);
      exit(1);
    }
    int Object = AST_mktuple(AST_PROC_CALL, Var, TYPEOF(Object), APP);
    P_TYPEINFO(Object) = CopyType(TYPEOF(Var));
// eg an integerfn once called is just an int. // May have some more work to do concerning %maps TO DO
    READ(P_TYPEINFO(Object), TypeDeclRA, TypeDecl) .Object = VAR;
    return Object;
#else
    TypeDeclIDX Type;
    VarDeclIDX VarIDX;
// ISRTFNMAPQ() TRUE
// remember, as well as constructing a tuple that calls the procedure, we
// need to return the associated type information as part of that tuple.
// AST_PROC_CALL,                     // PROC, FPP_LIST, APP_LIST
// LVALUE or RVALUE






    VarIDX = P_P(Var,1);
//fprintf(stderr, "Constructing an AST_PROC_CALL tuple for VarIDX=%d...\n", VarIDX);
//DebugVarIDX(VarIDX, TRUE);



    extern int ISRTFNMAPQ_VarIDX(VarDeclIDX VarIDX);
    if (!ISRTFNMAPQ_VarIDX(VarIDX)) {
      fprintf(stderr, "* Error: VarIDX in MkCall is not a procedure but it *should* have been confirmed by now!\n");
//DebugVarIDX(VarIDX, TRUE);

      exit(0);
    }
// let's start filling these in!

    Type = P_TYPEINFO(Var);
    int Call = AST_mktuple(AST_PROC_CALL, VarIDX, Type , APP);
    P_TYPEINFO(Call) = Type;

    return Call;
#endif
  }

  int MkIndex(int Var, int APP) {
    if ( READ(TYPEOF(Var), TypeDeclRA, TypeDecl) .IsArray != ARRAY) {
      fprintf(stderr, "* Error: Cannot index something that is not an array\n");
      Diagnose("Param was ", Var, 0, TRUE);
      exit(1);
    } else fprintf(stderr, "! Creating an AST_ARRAYACCESS tuple...\n");

    int Object = AST_mktuple(AST_ARRAYACCESS, Var, -1 , APP);
    P_TYPEINFO(Object) = CopyType(TYPEOF(Var));
// eg an array of ints once indexed is just an int.
    READ(TYPEOF(Object), TypeDeclRA, TypeDecl) .IsArray = SCALAR;
    return Object;
  }
// NONE, ROUTINE, FN, MAP

  int ISRTFNMAPQ_VarIDX(VarDeclIDX VarIDX) {
    TypeDeclIDX Type;
    Type =  READ(VarIDX, VarDeclRA, VarDecl) .type;
    return (( READ(Type, TypeDeclRA, TypeDecl) .Object == CODE) && (( READ(Type, TypeDeclRA, TypeDecl) .Proc == ROUTINE) || ( READ(Type, TypeDeclRA, TypeDecl) .Proc == FN) || ( READ(Type, TypeDeclRA, TypeDecl) .Proc == MAP)));
  }
// NONE, ROUTINE, FN, MAP

  int ISRTFNMAPQ_TypeIDX(TypeDeclIDX Type) {
    return (( READ(Type, TypeDeclRA, TypeDecl) .Object == CODE) && (( READ(Type, TypeDeclRA, TypeDecl) .Proc == ROUTINE) || ( READ(Type, TypeDeclRA, TypeDecl) .Proc == FN) || ( READ(Type, TypeDeclRA, TypeDecl) .Proc == MAP)));
  }
// NONE, ROUTINE, FN, MAP

  int ISRTFNMAPQ_AST(int Var) {
    if (Var == -1) return FALSE;
    VarDeclIDX VarIDX;
    TypeDeclIDX Type;
    if (P_op(Var) != AST_LVALUE && P_op(Var) != AST_RVALUE) {
      fprintf(stderr, "Parameter to ISRTFNMAPQ_AST was neither AST_LVALUE nor AST_RVALUE\n"); Diagnose("Param was ", Var, 0, debug_declarations);
// Although type information may be in hidden field, this is not what we're looking for.

      return -1;
    }
    VarIDX = P_P(Var, 1);
    Type =  READ(VarIDX, VarDeclRA, VarDecl) .type;
    return (( READ(Type, TypeDeclRA, TypeDecl) .Object == CODE) && (( READ(Type, TypeDeclRA, TypeDecl) .Proc == ROUTINE) || ( READ(Type, TypeDeclRA, TypeDecl) .Proc == FN) || ( READ(Type, TypeDeclRA, TypeDecl) .Proc == MAP)));
  }

  wchar_t *wNAMEOF_AST(int Var) {
    if (Var == -1) return L"**ERROR**";
    VarDeclIDX VarIDX;
    TypeDeclIDX Type;
    if (P_op(Var) != AST_LVALUE && P_op(Var) != AST_RVALUE) {
      fprintf(stderr, "Parameter to wNAMEOF_AST was neither AST_LVALUE nor AST_RVALUE\n"); Diagnose("Param was ", Var, 0, debug_declarations);
// Although type information may be in hidden field, this is not what we're looking for.

      return L"**ERROR**";
    }
    VarIDX = P_P(Var, 1);
    return Atom2WStr( READ(VarIDX, VarDeclRA, VarDecl) .c_name);
  }

  int ISARRAYQ_VarIDX(VarDeclIDX VarIDX) {
    TypeDeclIDX Type;
    Type =  READ(VarIDX, VarDeclRA, VarDecl) .type;
    return ( READ(Type, TypeDeclRA, TypeDecl) .Object == VAR) && ( READ(Type, TypeDeclRA, TypeDecl) .IsArray == ARRAY);
  }

  int ISARRAYQ_TypeIDX(TypeDeclIDX Type) {
    return ( READ(Type, TypeDeclRA, TypeDecl) .Object == VAR) && ( READ(Type, TypeDeclRA, TypeDecl) .IsArray == ARRAY);
  }

  int ISARRAYQ_AST(int Var) {
    if (Var == -1) return FALSE;
    VarDeclIDX VarIDX;
    TypeDeclIDX Type;
    if (P_op(Var) != AST_LVALUE && P_op(Var) != AST_RVALUE) {
      fprintf(stderr, "Parameter to ISARRAYQ_AST was neither AST_LVALUE nor AST_RVALUE\n"); Diagnose("Param was ", Var, 0, TRUE);
// Although type information may be in hidden field, this is not what we're looking for.

      return -1;
    }
    VarIDX = P_P(Var, 1);
    Type =  READ(VarIDX, VarDeclRA, VarDecl) .type;
    return ( READ(Type, TypeDeclRA, TypeDecl) .Object == VAR) && ( READ(Type, TypeDeclRA, TypeDecl) .IsArray == ARRAY);
  }

  int Compile_Var(int Parent, int PVarname, int POpt_app, int POpt_field, int depth) {
    int Varname = -1, Opt_app = -1, APP = -1, Opt_field = -1, Field = -1, Var = -1;
//Diagnose("On entry to Compile_Var: Parent = ", Var,0, TRUE);
//Diagnose("                         PVarname = ", PVarname,0, TRUE);
//Diagnose("                         POpt_app = ", POpt_app,0, TRUE);
//Diagnose("                         POpt_field = ", POpt_field,0, TRUE);
// P<APP> = '('<EXPR><RESTOFAPP>')', ''
// P<RESTOFAPP> = ','<READLINEP><EXPR><RESTOFAPP>, ''
// P<VAR> = <VARNAME><APP><Opt_Record_Field>
// P<Opt_Record_Field> = '_'<VARNAME><APP><Opt_Record_Field>, ''
    if (Parent != -1) {
      fprintf(stderr, "* Sub-sub fields of records not yet implemented.\n");
      return -1;
    }
// ***NOTE*** Compile(<FIELDNAME>) doesn't look up varname anywhere. We still have to do that. It just returns the string.
// compile(PVarname) returns an AST_RVALUE which contains a VarDecl[VarIDX] as its first field.
//Diagnose("After compiling PVarname:Varname = ", Varname,0, TRUE);



    Varname = compile(PVarname, depth+1);
//Diagnose("After compiling POpt_app:Opt_app = ", Opt_app,0, TRUE);
    Opt_app = compile(POpt_app, depth+1);
//Diagnose("After compiling POpt_field:Opt_field = ", Opt_field,0, TRUE);
    Opt_field = compile(POpt_field, depth+1);
// Let's handle simple vars, simple array elements, and simple (one-level) record fields.  But not array elements with record fields, yet.
// end of Compile_Var()



  }



  int Mk_AST_assignment(int Var, int Assop, int Expr, int depth) {
    int t[LARGEST_ALT];
    int Source, Dest;

    if (Var == -1 || Expr == -1) {
      fprintf(stderr, "Mk_AST_assignment: Var = %d  Expr = %d\n", Var, Expr);
      return -1;
    }
// RVALUES and LVALUES hold a VarIDX, *not* any sort of expression tuple.
    if (P_op(Var) == AST_RVALUE) P_op(Var) = AST_LVALUE;
// Since they both have the same structure, this hack is acceptable.
// Actually we don't really need RVALUEs now I think of it. REMOVE THEM?!


    TypeDeclIDX TypeIDX = TYPEOF(Var);
// 1: string  2: OP

    int Op = P_P(Assop, 2);
    switch (Op) {
      case OP_ASSIGN_ADDR:
// Expr must be a var, or a namefn or a map, i.e. an LVALUE

        if (1 ) {
          Source = AST_mktuple(AST_ADDROF, Expr);
        } else if (1 ) {
          Source = Expr;
        } else {
          fprintf(stderr, "* RHS of '==' must be an LVALUE.\n");
        }
//

        if (1 ) {
          Dest = Var;
        } else {
          fprintf(stderr, "* LHS of '==' must be an LVALUE.\n");
        }
        break;

      case OP_ASSIGN_VALUE:
// if the Expr evaluates to an indirect object, need to return AST_mktuple(AST_INDIRECT, Expr)

        if (0 ) {
          Source = AST_mktuple(AST_INDIRECT, Var);
        } else {
          Source = Expr;
        }
//

        if (0 ) {
          Dest = AST_mktuple(AST_INDIRECT, Var);
        } else if (0 ) {
          Dest = AST_mktuple(AST_INDIRECT, Var);
        } else if (1 ) {
          Dest = Var;
        } else {
          fprintf(stderr, "* LHS of '==' must be an LVALUE.\n");
        }
        break;

      case OP_JAM_TRANSFER:
        if (0 ) {
//Source = AST_mktuple(AST_ADJUST_WIDTH, Expr, FROMTYPE, TOTYPE); // Plus a narrowing cast if needed.

        } else {
// Plus a narrowing cast if needed.
          Source = Expr;
        }
        if (1 ) {
          Dest = AST_mktuple(AST_INDIRECT, Var);
        } else if (1 ) {
          Dest = AST_mktuple(AST_INDIRECT, Var);
        } else if (1 ) {
          Dest = Var;
        } else {
          fprintf(stderr, "* LHS of '==' must be an LVALUE.\n");
        }
        break;

      case OP_UNCOND_STR_RESOL:
// Hopefully something earlier in the parse has converted '->' into a conditional string resolution call and we never get here.
// (The implementation will need a call to signal, if the conditional call returns FALSE.)
// S -> L.("mid").R is implemented by if (!(_imp_resolve("%Smid%S", L, R))) _imp_signal(SIG_STRFAIL, 0, 0, "String resolution fails");



        return -1;
        break;

      default:
        fprintf(stderr, "* Unhandled assignment operator: 0x%08x (%d) in %s, line %d\n", Op, Op, __FILE__, __LINE__);
        break;
    }
// OP   Actually since we've now worked out the LHS and RHS, we don't really need to pass through the actual Assop any more...

    t[1] = Op;
// LHS
    t[2] = Dest;
// RHS
    t[3] = Source;
    int Ass = P_mktuple(AST_ASSIGN, 0, 3, t);
// Will be printed by a higher level

    return Ass;
// (It will be printed by handle_ast_phrase() in imp80-compile.h,
// which itself is called by generate_c() from P<Call_or_Assign_AUI>
// in imp80.g)



  }
//int ColonFlag = 0;
//INITCODE




#endif
int parse_init(void)
{
// regen etc use this too but don't need a lot of what uparse.c needs.
#ifdef IN_PARSER
// perform any initialisation required by the parse-time semantic routines.
// Note that for now, we have no way of declaring data outside of
// those procedures.  Obviously this will have to change.
// LINE RECONSTRUCTION *might* GO HERE.  But probably not.
// initially default scope is at the level appropriate for perms
// 2; // excessive debugging








  debug_scope = 0;
  debug_ast = 0;
  debug_ctrl_stack = 0;
  debug_compiler = 0;
  debug_declarations = 0;
  debug_torp = 0;
  debug_ast_creation = 0;

  push_scope_level();
// Declare perms, prims here
//add_entry("decl", "NEWLINE", 42); // param can be anything. Usually index into an array of records of the appropriate type.  During initial code creation, we'll just use random tags.
// initial top-level file-level scope, eg for %externalroutines
//push_scope_level();
// ready for externals
//codegen("\n// Relies on using gtcpp to preprocess before cleaning up with clang-format");







  codegen("#include <imp_perms.h>\n");
  codegen("\n$define unless(cond) if (!(cond)) \n$define until(cond) while(!(cond))\n");
// moved to <imp_perms.h>

#ifdef NEVER
  codegen("#define goto_switch(S, N) \\\n");
  codegen("  do { \\\n");
  codegen("    S ## _sw = N; \\\n");
  codegen("    _sw_line = __LINE__; \\\n");
  codegen("    _sw_file = __FILE__; \\\n");
  codegen("    if (S ## _low <= S ## _sw && S ## _sw <= S ## _high) \\\n");
  codegen("      goto *S[S ## _sw - S ## _low]; \\\n");
  codegen("    else \\\n");
  codegen("      goto S ## _default; \\\n");
  codegen("  } while (0)\n");

  codegen("#define declare_switch(S, low, high) \\\n");
  codegen("static int S ## _sw; \\\n");
  codegen("const int S ## _low = low, S ## _high = high; \\\n");
  codegen("static void **S; \\\n");
  codegen("auto void init_ ## S(void); init_ ## S();\n");
#endif

#endif
  return TRUE;
}
int parse_terminate(void)
{
#ifdef IN_PARSER
// perform any final tidy-up required by the parse-time semantic routines.
//pop_scope_level(); // from externals back to perms
//pop_scope_level(); // from perms to none.




#endif
  return TRUE;
}
int parse_whitespace(void)
{
#ifdef IN_PARSER
  while (source(TP).ch==' ' || source(TP).ch=='\t' || source(TP).ch=='\f') {
    TP += 1;
  }
#endif
  return TRUE;
}
int parse_Imp77_stropping(void)
{
#ifdef IN_PARSER
  int debug_stropping = 0;
// TO DO: initial spaces in he file before the initial % seem to cause a failure.
// The source file has already been read trivially into source().
// We will copy from source() into temp(), then perform line reconstruction
// on temp(), writing back to source().  The parser will then parse source()
// into atoms according to the grammar.  Initially it will only store the
// reconstructed characters into the atoms, but once it is working, I will
// modify it to also store the unreconsructed source for use in source-to-source
// translations, where whitespace, embedded comments, and indentation is
// desired in the translation, in order to mirror the original file.
// Because unfortunately underlining in Unicode is done by a *following*
// underline joiner character (818) rather than being a single unicode
// code point, it is difficult to use a single-character encoding of a
// stropped keyword letter - what the old Imp compilers would represent
// by adding 128 to the character.  However there *is* an alternive
// source of upper case and lower case letters in the mathematics area!
// A:Z could be encoded as 1D400:1D419 and a:z as 1D41A:1D433 :-)
// but for now I'm encoding keywords in lower case and variables in
// upper case.
// The 1D400+ encoding looks more or less like ordinary text if it happens
// to be displayed (e.g. during debugging) although there should never be
// any need to display internally-coded keywords to users of the
// compilers built with this parser.
// All arrays are flex and the upper bound is a limit, not a minimum.
  DECLARE(SYM, reconstructed, 128000000 );
#define _SYM(x) WRITE(x,SYM,reconstructed)
#define SYM(x) READ(x,SYM,reconstructed)

  int LASTP, P = 0;
  while (source(P).ch != 0 ) {
    _SYM(P).ch = source(P).ch;
    _SYM(P).start = P; _SYM(P).end = P+1;
    P += 1;
  }
  _SYM(P).ch = 0 ;
// no chars for EOF
  _SYM(P).start = P; _SYM(P).end = P;
  LASTP = P;

  if (debug_stropping) {
    int I;
    fprintf(stderr, "source() moved to SYM(0:%d) = \"", LASTP);
    for (I = 0; I < LASTP; I++) {
      fprintf(stderr, "%lc", SYM(I).ch);
    }
    if (SYM(LASTP).ch != 0) fprintf(stderr, "[%d]", SYM(LASTP).ch);
    fprintf(stderr, "\";\n");
  };
// Fetch Pointer, Put Pointer.

  int FP = 0, PP = 0;
#define DONE() do { FP -= 1; _source(PP).ch = 0; _source(PP).end = SYM(FP).end; if (debug_stropping) { int I; fprintf(stderr, "SYM(0:%d) moved to source(0:%d) = \"", FP, PP); for (I = 0; I < PP; I++) { if (source(I).ch != 0) fprintf(stderr, "%lc", source(I).ch); } if (source(PP).ch != 0) fprintf(stderr, "[%d]", source(PP).ch); fprintf(stderr, "\";\n"); } return TRUE; } while (0)

  wint_t WC;
// NOTE THAT WITH THIS IMP77 GRAMMAR, '\n' IS NOT WHITESPACE.  LINE ENDINGS ARE EXPLICITLY
// ENTERED IN THE GRAMMAR.  (See the phrases <T>, and <NL_opt>.
// uparse.c has been modified so that its implicit whitespace skipping no longer skips '\n'.
// (The algol60 parser in contrast treats all \n's the same as spaces)
// HOW TO HANDLE ' IN A PARSED COMMENT?
//
// %COMMENT A ' MESSES UP!
//
// because it keeps scanning until a closing quote.  However if you don't scan between quotes,
// line reconstruction will lose spaces within strings!
//
// You can't just end a quoted string at a newline because embedded newlines are allowed.
// And I checked Imp77 - it allows a single quote ch in a comment.
// If line reconstruction were being done on the fly then it could be modified if we knew we were
// in a comment, but since we're doing it all in advance, the only option to handle this appears
// to be that whenever we're in a comment, we throw away all the following line reconstruction and
// re-do it, with that comment handled differently.
// Or bite the bullet and work out how to do line reconstruction on the fly (which my previous
// imptoc did eventually manage using the 'demandload' call. So *every* fetch via TP would have
// to be recoded as a procedure call, with on-the-fly line reconstruction, and either a way to
// undo it if backtracking or simply never doing it any farther past TP and undoing it on backtracking.
// What a can of worms just to handle badly designed comments.  TO DO.
#define CHECK_EOF(x) do if ((x) == 0) DONE(); else { _source(PP).end = SYM(FP-1).end; } while (0)
// PP is the 'current' slot we are writing into.


  _source(PP).start = SYM(FP).start;

  for (;;) {
// Keep updated.

    _source(PP).end = SYM(FP).end;
    WC = SYM(FP++).ch; CHECK_EOF(WC);
// We found a keyword.  It will always be read up to the last character of the keyword.

    if (WC == '%') {

      for (;;) {
        WC = SYM(FP++).ch; CHECK_EOF(WC);
        if (WC == '%') {

        } else if (!isalpha(WC)) {
// It's possible to have a bunch of '%' signs and *no* keyword characters.
// point FP back to the non-keyword character, not as currently, the one past that.

          --FP;
          break;
// isalpha(WC)
        } else {
          if (isupper(WC)) WC = tolower(WC);
          _source(PP).end = SYM(FP-1).end;
// | 128
          _source(PP++).ch = WC;
          _source(PP).start = SYM(FP).start;
        }
      }
      continue;
    }

    else if (WC == '{') {

      for (;;) {
        WC = SYM(FP++).ch; CHECK_EOF(WC);

        if (WC == '\n') {
          --FP;
// _source(PP).end = SYM(FP-1).end; // point FP back to the newline

          break;
        }
// Not sure if \n should be gobbled for {this style
        if (WC == '}') {
// but still looking.
          break;
        }
      }
      continue;
    }

    else if (WC == '\'') {
      _source(PP++).ch = WC;
      for (;;) {
        WC = SYM(FP++).ch; CHECK_EOF(WC);
        if (WC == '\'') {
// peek ahead:

          int Peek = SYM(FP).ch; CHECK_EOF(Peek);
// doubled 's
          if (Peek == '\'') {
            _source(PP++).ch = WC;
            _source(PP++).ch = Peek;
            FP++;
          } else {
            _source(PP).ch = WC;
// Leave Peek for later.
            _source(PP).end = SYM(FP-1).end;
            PP++;
            break;
          }
        } else {
          _source(PP++).ch = WC;
        }
      }
      continue;
    }
// TO DO: Update ' and " items in imp77 as well

    else if (WC == '"') {
      _source(PP++).ch = WC;
      for (;;) {
        WC = SYM(FP++).ch; CHECK_EOF(WC);
        if (WC == '"') {
// peek ahead:

          int Peek = SYM(FP).ch; CHECK_EOF(Peek);
// doubled "s
          if (Peek == '"') {
            _source(PP++).ch = WC;
            _source(PP++).ch = Peek;
            FP++;
          } else {
            _source(PP).ch = WC;
// Leave Peek for later.
            _source(PP).end = SYM(FP-1).end;
            PP++;
            break;
          }
        } else {
          _source(PP++).ch = WC;
        }
      }
      continue;
    }
// use iswblank(WC) instead?

    else if (WC == ' ' || WC == '\t' || WC == '\f') {

      continue;
    }




    else {
// everything else just returns one significant non-space character. This includes '\n'.
// BEWARE WHEN CHANGING STROPPING ENCODING: Looking for a preceding '%C' ...


      if ((WC == '\n') && ((PP>1) && (source(PP-1).ch == 'c'))) {
// remove the '%c'
        if (PP>0) _source(PP-1).ch = ' ';
// remove the newline
        _source(PP++).ch = ' ';
// This is the only place where we gobble spaces *after* a token rather than before.
// It may be cleaner to set a 'continuation' flag and gobble them before the next
// symbol fetch rather than do it here in a lookahead.  Esp. wrt to reconstituting source
// from the array for the listing file etc etc.
// BUT FOR NOW, %C IS HANDLED BY THS HACK:







        int Lookahead = FP;
// Use iswblank()?
        while (SYM(Lookahead).ch == '\n' || SYM(Lookahead).ch == ' ' || SYM(Lookahead).ch == '\t' || SYM(Lookahead).ch == '\f') {
// No worries about  '{...}' - this behaviour seems to be identical to Imp77's
// gobble following newlines and whitespace before next significant character.

          _SYM(Lookahead).ch = ' ';
          Lookahead++;
        }
        continue;
      }

      if (iswalpha(WC) && iswlower(WC)) {
// ALSO TEMPORARY
        WC = towupper(WC);
      }
      _source(PP++).ch = WC;
      continue;
    }
// Still skipping whitespace ...




  }

  DONE();
  P = 0;
  while (source(P).ch != 0) {
    if (debug_stropping) fprintf(stderr, "%d: ch='%lc'  start=%d:end=%d\n", P, source(P).ch, source(P).start, source(P).end);
    P++;
  }

#undef DONE
#endif
  return TRUE;
}
int parse_COLON(void)
{
// Noting that we have just seen a label at parse time allows us
// to handle an immediately-following comment differently, i.e.
// by avoiding the line reconstruction (if doing so on the fly)
// and thus preserving the text of the comment for output by a
// source-to-source translator.  We're not doing that yet.
//ColonFlag = 1; // Note that <COLON> occurs last in its list of alternatives so if it is
// executed at all, the parse is going to be successful.
// We should then reset ColonFlag at the point where it is tested (for the comments)









  return TRUE;
}
int parse_UNDO_COLON(void)
{
//ColonFlag = 0;

  return TRUE;
}
int parse_INCLUDE(void)
{
// We almost certainly need to handle include files at parse time (with a stack of
// input files, as I'm doing in takeon.c)
// Include files such as fred.inc should cause the generated C code to be written
// to fred.h, with the %include "fred.inc" being replaced by #include "fred.h"
// Some care should be taken when writing those files to use a temporary name
// such as "fred.imp.h" so that any header files which are being manually maintained
// are not overwritten by the generated files.
// For now, all output is going to the same output file.
  return TRUE;
}
int parse_LISTON(void)
{
// listing control has to be done at parse time (unless there is a *major* restructuring)
// %list / %endoflist commands may not be relevant in a C translation.
// We *don't*, for example, want to use %list to control whether the
// C code is output or not - athough it seems like a useful thing to
// do, it would break any existing code that used %endoflist for its original
// purpose.








  return TRUE;
}
int parse_LISTOFF(void)
{
  return TRUE;
}
// B<eof> = 0;
// B<ch> = 1;
// B<nl> = 2;
// E
// SUPPRESS_DATA








#endif
// _GRAMMAR_H_
#endif
