B<char> = 0;
B<identifier> = 1;
B<integer-constant> = 2;
B<character-constant> = 3;
B<floating-constant> = 4;
B<string> = 5;
B<keyword> = 6;
B<pp> = 7;
B<mm> = 8;
B<andand> = 9;
B<oror> = 10;
B<eqeq> = 11;
B<EOF> = 12;

C<immediate> = {
extern int *A;

extern int cp;                     // code pointer.  Has to be global state.
extern int ap;                     // Analysis record pointer.
int typedef_table[123];// for now
int push(int val, int *table);

int check(int Aval, int *table);

};

# top-level statement
P<ignored> =
   <immediate:return FALSE;> ';';

P<SS> =
   <opt-external-declaration-list>;

P<opt-external-declaration-list> =
   <external-declaration> <opt-external-declaration-list>,
   <EOF>;

P<external-declaration> =
   <typedef-declaration> ';',
   <proc-fn-decl>,
   <possibly-initialised-scalar-or-array-decl>,
   <enum-specifier>,
   <struct-or-union-specifier>,
   ';';

# Oops - these can be initialised.  Not yet done so.
#
P<struct-or-union-specifier> =
   <struct-or-union> <identifier> '{' <struct-field-declarations> '}',
   <struct-or-union> '{' <struct-field-declarations> '}',
   <struct-or-union> <identifier>;

P<struct-or-union> =
   "struct",
   "union";

P<struct-field-declarations> =
   <struct-field-declaration> <rest-of-struct-field-declarations>;

P<rest-of-struct-field-declarations> =
   <struct-field-declaration> <rest-of-struct-field-declarations>,
  ;

P<struct-field-declaration> =
   <possibly-initialised-scalar-or-array-decl>;

# I originally thought you could only initialise an array after a declaration
# that had '[]' in it.  But not the case - a typedef'd declaration may have
# the array part in the typedef rather than the instance.  Ugly language.
#
# Also we can have initialised structs :-/
#
P<possibly-initialised-scalar-or-array-decl> =
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <type> <rest-of-scalar-or-array-decl> ';';

# The <opt-scalar-init> part of function pointers is usually going to be "= &procname"... any other type of scalar is dubious...
P<rest-of-scalar-or-array-decl> =
   <opt-indirection-decl> <identifier> <array-bounds> <opt-array-init> <opt-rest-of-scalar-or-array-decl>,
   <opt-indirection-decl> '(' <opt-indirection-decl> <identifier> ')' '(' <opt-param-list> ')' <opt-scalar-init> <opt-rest-of-scalar-or-array-decl>,
   <opt-indirection-decl> <identifier> <opt-scalar-init> <opt-rest-of-scalar-or-array-decl>;

P<opt-rest-of-scalar-or-array-decl> =
   ',' <rest-of-scalar-or-array-decl>,
  ;

P<opt-scalar-init> =
   '=' <assignment-expression>,
  ;

P<assignment-expression> =
   <opt-lvalue-assign> <conditional-expression> <opt-rest-of-assignment-expression>;

P<opt-rest-of-assignment-expression> =
   <assignment-operator> <conditional-expression> <opt-rest-of-assignment-expression>,
  ;

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

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

P<expression> =
   <comma-statement>;

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

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

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

P<rest-of-logical-or-expression> =
   <oror> <logical-and-expression> <rest-of-logical-or-expression>,
  ;

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

P<rest-of-logical-and-expression> =
   <andand> <inclusive-or-expression> <rest-of-logical-and-expression>,
  ;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# my reading of the spec is that "sizeof" <type-specifier> is valid but "sizeof" '(' <type-specifier> ')' is not,
# however the latter appears to be accepted by gcc.
# Should we merge <type-specifier> with <opt-indirection-unary-ops>? Is <type-specifier> ever used without an optional '*' following?
P<unary-rvalue-expression> =
   <arithmetic-unary-op> <optionally-cast-unary-rvalue-expression>,
   <boolean-unary-ops> <optionally-cast-unary-rvalue-expression>,
   <bitwise-unary-ops> <optionally-cast-unary-rvalue-expression>,
   <indirection-unary-ops> <unary-address-expression>,
   "sizeof" <unary-rvalue-expression>,
   "sizeof" <unary-lvalue-expression>,
   "sizeof" <type-specifier> <opt-indirection-unary-ops>,
   "sizeof" '(' <type-specifier> <opt-indirection-unary-ops> ')',
   <optionally-cast-postfix-rvalue-expression>;

P<optionally-cast-postfix-rvalue-expression> =
   <cast> <optionally-cast-postfix-rvalue-expression>,
   <postfix-rvalue-expression>;

P<postfix-rvalue-expression> =
   <primary-expression> <rest-of-postfix-rvalue-expression>,
   <unary-lvalue-expression> <rest-of-postfix-rvalue-expression>;

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

P<post-increment-op> =
   <pp>,
   <mm>;

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

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

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

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

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

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

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

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

P<enumeration-constant> =
   <identifier>;

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

P<optionally-cast-unary-lvalue-expression> =
   <cast> <optionally-cast-unary-lvalue-expression>,
   <unary-lvalue-expression>;

P<cast> =
   '(' <type-specifier> <opt-indirection-unary-ops> ')';

# <opt-indirection-decl> looks just like <opt-indirection-unary-ops> except that it only
# happens in a declaration, not a data access.  The significant difference is that the '*'
# can be preceded by a "const" (and maybe "volatile") keyword, eg "int const *fred" which
# is a constant pointer to an int.
#
P<opt-indirection-decl> =
   <opt-const-or-volatile-type-qualifier> '*' <opt-indirection-decl>,
  ;

P<indirection-unary-ops> =
   '*' <opt-indirection-unary-ops>;

P<opt-indirection-unary-ops> =
   '*' <opt-indirection-unary-ops>,
  ;

P<type-specifier> =
   <basic-type-specifier>,
   <typedef-name>;

P<typedef-name> =
   <identifier>;

# I think struct or union or enum specifier below can be an actual declaration using '{}'s
# rather than just a struct or enum name.
#
P<basic-type-specifier> =
   "void",
   "double",
   "long" "double",
   <opt-signed> "char",
   <opt-signed-inttype>,
   "float",
   <struct-or-union-specifier>,
   <enum-specifier>;

P<enum-specifier> =
   "enum" '{' <enumerator-list> '}',
   "enum" <identifier> '{' <enumerator-list> '}',
   "enum" <identifier>;

P<enumerator-list> =
   <enumerator> <rest-of-enumerator-list>;

P<rest-of-enumerator-list> =
   ',' <enumerator> <rest-of-enumerator-list>,
   ',',
  ;

P<enumerator> =
   <identifier> '=' <constant-expression>,
   <identifier>;

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

P<opt-signed-inttype> =
   <opt-signed> "short" <opt-int>,
   <opt-signed> "long" "long" <opt-int>,
   <opt-signed> "long" <opt-int>,
   "int",
   "signed",
   "unsigned";

P<opt-int> =
   "int",
  ;

P<opt-signed> =
   "signed",
   "unsigned",
  ;

P<address-operator> =
   '&';

P<pre-increment-op> =
   <pp>,
   <mm>;

P<optionally-cast-unary-rvalue-expression> =
   <cast> <optionally-cast-unary-rvalue-expression>,
   <unary-rvalue-expression>;

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

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

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

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

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

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

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

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

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

P<eqop> =
   <eqeq>,
   '!' '=';

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

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

P<opt-array-init> =
   '=' '{' <constant-initializer-list> '}',
   '=' <string>,
  ;

P<constant-initializer-list> =
   <constant-initializer> <rest-of-constant-initializer-list>;

P<rest-of-constant-initializer-list> =
   ',' <constant-initializer> <rest-of-constant-initializer-list>,
   ',',
  ;

P<constant-initializer> =
   <constant-expression>,
   '{' <constant-initializer-list> '}';

P<array-bounds> =
   '[' <opt-constant-expression> ']' <opt-array-bounds>;

P<opt-array-bounds> =
   '[' <opt-constant-expression> ']' <opt-array-bounds>,
  ;

P<opt-constant-expression> =
   <constant-expression>,
  ;

P<type> =
   "void",
   <enum-specifier>,
   <struct-or-union-specifier>,
   "typeof" '(' <unary-lvalue-expression> ')',
   <basic-type-specifier>,
   <typedef-name>;

P<opt-const-or-volatile-type-qualifier> =
   <const-or-volatile-type-qualifier>,
  ;

P<const-or-volatile-type-qualifier> =
   "const",
   "volatile";

P<opt-auto-reg-static-ext> =
   "auto",
   "register",
   "static",
   "extern",
  ;

P<identifier-list> =
   <identifier> <rest-of-identifier-list>;

P<rest-of-identifier-list> =
   ',' <identifier-list>,
  ;

P<more-forward-decls-or-actual-body> =
   <opt-extern-proc-name-and-params-list> ';',
   <compound-statement>;

P<extern-proc-spec> =
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <opt-basic-type-specifier-or-typedef-name>;

P<opt-basic-type-specifier-or-typedef-name> =
   <opt-basic-type-specifier>,
   <typedef-name>;

P<extern-proc-name-and-params> =
   <opt-indirection-decl> <identifier> '(' <opt-param-list> ')';

P<opt-extern-proc-name-and-params-list> =
   ',' <extern-proc-name-and-params> <opt-extern-proc-name-and-params-list>,
  ;

P<proc-fn-decl> =
   <extern-proc-spec> <extern-proc-name-and-params> <more-forward-decls-or-actual-body>,
   <extern-proc-spec> <opt-indirection-decl> <identifier> '(' <identifier-list> ')' <opt-oldstyle-param-list> ';' <compound-statement>;

P<compound-statement> =
   '{' '}',
   '{' <statement-list> '}';

P<statement-list> =
   <opt-labels> <statement> <rest-of-statement-list>;

P<rest-of-statement-list> =
   <opt-labels> <statement> <rest-of-statement-list>,
  ;

P<statement> =
   ';',
   <compound-statement>,
   <selection-statement>,
   <iteration-statement>,
   <jump-statement>,
   <expression> ';',
   <in-proc-data-declaration>;

P<in-proc-data-declaration> =
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <type> <decl-list>,
   <struct-decl>;

P<struct-decl> =
   "struct" <new-structname> '{' <struct-member-list> '}';

P<struct-member-list> =
   <struct-member-declaration> <struct-member-list>,
  ;

P<struct-member-declaration> =
   <struct-decl>,
   <possibly-initialised-scalar-decl>,
   <possibly-initialised-array-decl>;

P<possibly-initialised-array-decl> =
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <type> <opt-indirection-decl> <identifier> <array-bounds> <opt-array-init> ';';

P<possibly-initialised-scalar-decl> =
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <type> <rest-of-scalar-decl>;

P<rest-of-scalar-decl> =
   <opt-indirection-decl> <identifier> <opt-scalar-init> <opt-rest-of-scalar-decl> ';';

P<opt-rest-of-scalar-decl> =
   ',' <opt-indirection-decl> <identifier> <opt-scalar-init> <opt-rest-of-scalar-decl>,
  ;

P<new-structname> =
   <identifier>;

P<decl-list> =
   <decl> <rest-of-decl-list>;

P<rest-of-decl-list> =
   ',' <decl-list>,
  ;

P<decl> =
   <opt-indirection-decl> <identifier> <possibly-empty-array-bounds-list> <opt-array-init>,
   <opt-indirection-decl> <identifier> <opt-scalar-init>;

P<possibly-empty-array-bounds-list> =
   '[' <opt-constant-expression> ']' <optional-possibly-empty-array-bounds-list>;

P<optional-possibly-empty-array-bounds-list> =
   '[' <opt-constant-expression> ']' <optional-possibly-empty-array-bounds-list>,
  ;

P<jump-statement> =
   "goto" <label> ';',
   "continue" ';',
   "break" ';',
   "return" <opt-expression> ';';

P<opt-expression> =
   <expression>,
  ;

P<label> =
   <identifier>;

P<iteration-statement> =
   "while" '(' <expression> ')' <statement>,
   "do" <statement> "while" '(' <expression> ')' ';',
   "for" '(' <opt-expression> ';' <opt-expression> ';' <opt-expression> ')' <statement>;

P<selection-statement> =
   "if" '(' <expression> ')' <statement> <opt-else>,
   "switch" '(' <expression> ')' <statement>;

P<opt-else> =
   "else" <statement>,
  ;

# guard to give better error on "label: }" which should be "label: ; }"
P<missing-semicolon-after-label-at-end-of-block> =
   '}';

P<opt-labels> =
   <label> ':' <opt-labels> <!missing-semicolon-after-label-at-end-of-block>,
   "case" <constant-expression> ':' <opt-labels> <!missing-semicolon-after-label-at-end-of-block>,
   "default" ':' <opt-labels> <!missing-semicolon-after-label-at-end-of-block>,
  ;

# formal parameter list can also be just a list of types (but can still end in "...")
# normal current style however is to have named parameters.
#
P<opt-param-list> =
   "void",
   <formal-param> <opt-rest-of-param-list>,
  ;

P<opt-oldstyle-param-list> =
   <oldstyle-param-list>,
  ;

P<oldstyle-param-list> =
   "void",
   <oldstyle-formal-param> <opt-rest-of-oldstyle-param-list>;

P<opt-rest-of-oldstyle-param-list> =
   ';' <oldstyle-param-list>,
  ;

P<oldstyle-formal-param> =
   <opt-const-or-volatile-type-qualifier> <type> <oldstyle-parameter-list>;

P<oldstyle-parameter-list> =
   <whatever> <rest-of-oldstyle-parameter-list>;

P<rest-of-oldstyle-parameter-list> =
   ',' <oldstyle-parameter-list>,
  ;

P<whatever> =
   <opt-indirection-decl> <opt-identifier> <optional-possibly-empty-array-bounds-list>;

P<opt-rest-of-param-list> =
   ',' '.' '.' '.',
   ',' <formal-param> <opt-rest-of-param-list>,
  ;

P<formal-param> =
   <procedure-as-parameter>,
   <opt-const-or-volatile-type-qualifier> <type> <opt-indirection-decl> <opt-identifier> <optional-possibly-empty-array-bounds-list>;

P<opt-identifier> =
   <identifier>,
  ;

P<procedure-as-parameter> =
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <opt-basic-type-specifier> '(' <opt-indirection-decl> <identifier> ')' '(' <opt-param-list> ')',
   <opt-auto-reg-static-ext> <opt-const-or-volatile-type-qualifier> <typedef-name> '(' <opt-indirection-decl> <identifier> ')' '(' <opt-param-list> ')';

P<opt-basic-type-specifier> =
   <basic-type-specifier>,
  ;

# Wonder if this would do it? <opt-indirection-decl> '(' <opt-indirection-decl> <identifier> ')' '(' <opt-param-list> ')' <opt-scalar-init> <opt-rest-of-scalar-or-array-decl>,
P<typedef-declaration> =
   "typedef" <type-specifier> <opt-indirection-decl> '(' <opt-indirection-decl> <identifier> ')' '(' <opt-param-list> ')',
   "typedef" <struct-decl> <maybe-indirect-typedef-name-list>,
   "typedef" <type-specifier> <maybe-indirect-typedef-name-list>;

P<maybe-indirect-typedef-name-list> =
   <opt-indirection-decl> <typedef-name> <rest-of-maybe-indirect-typedef-name-list>;

P<rest-of-maybe-indirect-typedef-name-list> =
   ',' <maybe-indirect-typedef-name-list>,
  ;

E
