head	1.3;
access;
symbols;
locks
	gtoal:1.3; strict;
comment	@# @;


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

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

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


desc
@imp grammar
@


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

C<immediate> = {
// WARNING: currently parse-time phrases are not being handled properly
//          by compile(). Are they set to -1 or skipped completely?
#define INCLUDE_AST_DEBUG "show_imp_ast.c"
int kw(int AST_op, int idx, int ap)
{
  int data[4];

  int i;

  char *k = keyword(idx);

  data[1] =  idx;

  data[2] = A[ap+3]; // index of c[] array element
  data[3] = strlen(k);

  return mktuple(AST_op, 1, 3, data);

}
// variables used in line_reconstruction() moved here so they are available to semantic code in grammar.
#define STROPPED_KEYWORDS 1  /* alters behaviour of parse() */
int at_startofstatement = TRUE;

int saved = TRUE;

int at_startofline = TRUE;

int in_keyword = FALSE;

int in_alphanumeric = FALSE;

};

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

##P<LABEL-COLON> =
##   ':';

# SS is the primary entry point.
#
P<SS> =
   <EXTERNALS> <LAST-ITEMS-IN-FILE>;

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

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

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

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

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

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

#More than one newline (or semicolon)
P<BLANKS> =
   <S> <BLANKS>,
  ;

# The last items in a file.
# the final EOF below is only for begin/endofprogram which *can* be followed by code but shouldn't
P<LAST-ITEMS-IN-FILE> =
   "end" "of" "file" <BLANKS> <EOF>,
   "end" "of" "perm" <BLANKS> <EOF>,
   "end" "of" "prim" <BLANKS> <EOF>,
   <BLANKS> <EOF>;

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

# I haven't specified any phrases to be external-only.  There might be some,
# but I'll leave it to compile() to reject them.
P<EXTERNALS> =
   <EXTERNAL-OR-INTERNAL> <EXTERNALS>,
  ;

# I cleaned up label handling, we can afford the overhead of backtracking over one <NAME>...
# - this isn't the 70's any more.  Labels are not valid outside blocks.
P<INTERNALS> =
   <OPT-LABEL-DEFINITION> <INTERNAL> <INTERNALS>,
   <OPT-LABEL-DEFINITION>;

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

# these are allowed at the external level and also inside procedures/main program.
# The phrases that are internal-only include these as the final alternative,
# hence why the empty statement can only occur as the last item in this list.
#
# If using this grammar as a 'SOAP' replacement, you'll want to allow syntactically
# incorrect statements also, and if so, those should be handled here after the <S> rule.
#
P<EXTERNAL-OR-INTERNAL> =
   "begin" <S> <INTERNALS> <outer-level-end> <S>,
   <Percent-SEX> <RT> <spec> <NAME> <Opt-ALIAS> <FPP> <S>,
   <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S> <INTERNALS> <outer-level-end> <S>,
   <OPT-68K> <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S>,
   <DECLARATION-S>,
   "record" "format" <RECFMT-spec-OR-BODY> <S>,
   "switch" <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST> <S>,
   "include" <STR-CONST>,
   "option" <STR-CONST>,
   "from" <NAME> "include" <NAME> <Opt-NAME-LIST>,
   "end" "of" "list" <S>,
   "list" <S>,
   "end" "of" "mcode" <S>,
   "mcode" <S>,
   "trusted" "program" <S>,
   "main" "ep" <NAME> <S>,
   "control" <INT-CONST> <S>,
   "diagnose" <INT-CONST> <S>,
   "reals" "long",
   "reals" "normal",
   <COMMENT>,
   <S>;

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

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

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

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

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

# All labels come through here.  Because of an interaction with comments,
# we use some parse-time code in phrase <LABEL-COLON> to modify how line reconstruction
# handles the comments.  btw Numeric labels are a historical form...
P<OPT-LABEL-DEFINITION> =
   <NAME> '(' <CEXPR> ')' <LABEL-COLON> <OPT-LABEL-DEFINITION>,
   <NAME> '(' '*' ')' <LABEL-COLON> <OPT-LABEL-DEFINITION>,
   <NAME> <LABEL-COLON> <OPT-LABEL-DEFINITION>,
   <DIGIT-SEQ> <LABEL-COLON> <OPT-LABEL-DEFINITION>,
  ;

# fault statements can only appear in the outer block of a program;

# they cannot appear in external routines.  This should be checked
# by the compile() procedure.
P<FAULT-S> =
   "fault" <FAULT-ACTION> <MORE-FAULTS> <S>;

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

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

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

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

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

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

# compound statements - blah %and blah %and blah - a short form of startfinish block
P<AUI> =
   "and" <UI>,
  ;

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

# the compound statement form ends in UIs which change control flow, ie
# you cannot say: %if whatever %then blah %and ->lab %and this will never happen
# tempted to add %caption here ;-)  But that may be a step too far.
# "%monitor 9" would be trapped by "%fault 9 -> lab"
P<UI> =
   <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD> <OPT-ASSIGN> <AUI>,
   "print" "text" <sqstring> <AUI>,
   '-' '>' <LABEL>,
   "return",
   "true",
   "false",
   "result" <ASSOP> <EXPR>,
   "monitorstop",
   "monitor" <DIGIT-SEQ> <AUI>,
   "monitor" <AUI>,
   "stop",
   "signal" <OPT-event> <CEXPR> <OPEXPR2>,
   "exit",
   "continue";

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

# prim and perm added by gt
P<FILE-END-TYPE> =
   "file",
   "prim",
   "perm";

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

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

# old-style record array declaration
P<RADEC> =
   <NAME> <Opt-NAME-LIST> <BPAIR> '(' <RECFMT-REF> ')';

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

# old-style record declaration
P<RDEC> =
   <NAME> <Opt-NAME-LIST> '(' <RECFMT-REF> ')';

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

# %on %event * %start or %on %event 1,2,3,etc %start
P<EVENT-LIST> =
   '*',
   <CEXPR>;

# Added old-style while/until for: %UNTIL NEXTINSYM=NL %THEN %CYCLE
# and %WHILE NEXTINSYM#NL %THEN SKIPINSYM
P<CYCLE-S> =
   "cycle" <Opt-CYCPARM> <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
   <Percent-WUF> "cycle" <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
   <Percent-WU> "then" "cycle" <S> <INTERNALS> "repeat" <Opt-UNTIL> <S>,
   <Percent-WU> "then" <UI>;

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

# /home/gtoal/gtoal.com/athome/edinburgh/APM-gdmr/I/LIB.imp
# contains:
#          %elseif 'A'<=sym<='Z' %start
#
# which is a form of Hamish's that is equivalent to
#
#  %finish %else %if 'A'<=sym<='Z' %start
#
# or the more concise
#
#  %else %if 'A'<=sym<='Z'
#
# An identical issue is: %elsestart
# in APM-gdmr/ETHER/PROFILE.imp
#
P<FINISH-S> =
   "else" <Percent-IU> <CONDITION> <Opt-start> <S> <INTERNALS> <FINISH-S>,
   "else" <Opt-start> <S> <INTERNALS> "finish" <S>,
   "finish" <Opt-ELSE-S>;

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

# see the comments on <FINISH-S> above.
P<Opt-ELSE-S> =
   "else" "start" <S> <INTERNALS> "finish" <S>,
   "else" <IF-S>,
   "else" <UI> <S>,
   <S>;

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

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

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

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

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

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

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

P<semi> =
   ';';

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

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

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

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

# current line reconstruction doesn't support this. Letters come through as part of <alphanumeric>s.
P<OPT-DQ-LETTER> =
   'E',
  ;

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

# <CONST> is somewhat generic and should be replaced with the more specific version when possible
P<CONST> =
   <INT-CONST>,
   <REAL-CONST>,
   <STR-CONST>;

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

#
P<OLDSTYLE-BASE> =
   <letter> <sqstring>;

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

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

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

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

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

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

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

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

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

## P<rest-of-name> =
##    <NAME>,
##   ;

P<SQUO-STRING> =
   <sqstring>;

# A UCI is a machine code instruction.
# (Hamish's PDP15 imp machine code syntax (which is not introduced by '*') is definitely not supported.)
#
# Machine-specific machine-code removed. May cause problems with ';' and quoted chars in machine code
#
P<UCI> =
   <NL-or-SEMI>,
   <char> <UCI>,
   <dqstring> <UCI>,
   <sqstring> <UCI>,
   <stropped> <UCI>;

# Imp does not let you mix and/or without explicit brackets. They do not have a precedence relationship
P<REST-OF-COND> =
   "and" <SC> <REST-OF-ANDC>,
   "or" <SC> <REST-OF-ORC>,
  ;

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

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

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

# <NAME> <OPT-ACTUAL-PARAMETERS> added by gt for predicates
# RESTOFSC handles double-sided conditions such as '0' <= ch <= '9'
P<SC> =
   <STRING-RESOLUTION>,
   <EXPR> <COMP> <EXPR> <OPT-DOUBLE-SIDED>,
   <NAME> <OPT-ACTUAL-PARAMETERS>,
   '(' <CONDITION> ')',
   "not" <SC>;

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

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

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

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

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

# No bracketed expressions, only '.' operator allowed, although once again,
# Hamish has an idiosyncratic extension where strings in a printstring are
# separated by semi-colons - I think it is some approximation to C's printf?
#
P<STRING-EXPR> =
   <STRING-RVALUE> <REST-OF-STRING-EXPR>;

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

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

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

# ping-pong.  Can't allow -> (a).b.c.(d) or -> a.(b).(c).d
P<OPT-STR-ASSIGN> =
   '.' <STR-ASSIGN> <OPT-STR-MATCH>,
  ;

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

# Ordering is important, eg '##' must come before '#'
#
# I really pity people who use the sort of grammar that requires
# eliminating shift-reduce conflicts before they can parse anything :-)
#
P<COMP> =
   '=' '=',
   '=',
   <NOT-EQUALS-ADDRESS>,
   <NOT-EQUALS>,
   '<' '=',
   '<',
   '>' '=',
   '>';

# '<>' is a really obscure form but it definitely used to be used.  I'll
# post a reference as soon as I locate one.
#
P<NOT-EQUALS> =
   '#',
   '\\' '=',
   '<' '>';

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

# Procedure application, i.e. actual parameters to routine or function call
P<OPT-ACTUAL-PARAMETERS> =
   '(' <EXPR> <REST-OF-ACTUAL-PARAMETERS> ')',
  ;

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

# handling precedence withh the grammar rather than Edinburgh-style in the AST. Tradeoff of speed for simplicity.
# CEXPR not handled yet
P<EXPR> =
   <MEDIUMPREC-EXPR> <REST-OF-EXPR>;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# highest precedence, left-associative
P<OP-HIGH-LEFTASSOC> =
   '<' '<',
   '>' '>';

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

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

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

# middle-precedence operators.  All left-associative.
P<OP-MED> =
   '*',
   '/' '/',
   '/',
   '&';

# lowest precedence operators.  All left-associative.
P<OP-LOW> =
   '+',
   '-',
   '!' '!',
   '!',
   '.';

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

# fortunately unary backslash is unambiguous with ERCC style exponentiation binary operator
P<UNARY-NOT> =
   '\\',
   '~';

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

# rest of %switch list
# This reminds me, isn't there an optional %label declaration in Imp?  Haven't seen an example yet,
# I need to check the manuals to make sure I'm not confusing this with C...
P<REST-OF-SWLIST> =
   ',' <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST>,
  ;

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

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

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

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

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

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

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

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

# %routine y (%integer %name %array (1) %name b)
# %routine r10(%integer%array%name a(1:10),%integer k)
P<FORMAL-PARAMETER-DECLARATION> =
   <XTYPE> <OPT-arrayname> <NAME> <OPT-68K-Bounds> <Opt-NAME-LIST>,
   <RT> <OPT-name> <NAME> <Opt-NAME-LIST> <FPP>,
   "name" <Opt-68K-reg> <NAME> <Opt-NAME-LIST>;

# More Hamish idiosyncracy!:
#
# @@16_1114%record(*)%map ZNEW(%name(d0) v)
#  - APM-gdmr/I/VPERM19.imp
#      d0 being a register
#
P<Opt-68K-reg> =
   '(' <NAME> ')',
  ;

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

# added predicate - gt
P<RT> =
   "routine",
   "predicate",
   <XTYPE> <FN-MAP>;

# note map is equivalent to "name function"
P<FN-MAP> =
   <FN>,
   "map";

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

# Confirmed that label is used.  Found one in 68000 code for labels used in embeddded machine code:
# %label f1,f2,f3,f4,f5,f6,f7,b0,b1,b2,b3,b4,b5,b6,b7,end
#
# "long long" would be added here if we supported 64 bit ints or 128bit reals...
# Okain "long" added here by making <BTYPE> optional.
# (BTYPE being integer/real/longreal)
#
P<BASE-XTYPE> =
   "integer",
   "real",
   "label",
   "long" <Opt-BTYPE>,
   "byte" <Opt-INTEGER>,
   "mite" <Opt-INTEGER>,
   "short" <Opt-INTEGER>,
   "half" <Opt-INTEGER>,
   "string" <REPFACT>,
   "record" <Opt-RFREF>;

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

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

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

# C-like register qualifiers (Hamish, again)
# %register(a2)%integer r1,r2
# %register(a2)%byte%name bn
# %register(a3)%short%name sn
P<68K-register> =
   "register" '(' <NAME> ')';

# '*' added by gt for recordformat(*)
# () moved from point of call to here to support old style "%recordname fred" without format. (Uses a formatspec instead)
P<Opt-RFREF> =
   '(' <RECFMT-REF> ')',
   '(' '*' ')',
  ;

# RF in the rules below is short for Record Format
P<RECFMT-REF> =
   <NAME>,
   <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC>;

# Imp variant record. NOT the same as C's which is per field.  This is per record.
# Still to check: a ( ... %or ... ) subgroup in a record definition, where the ...
# represents several declarations, with the whole ( ... ) group being a part
# of an enclosing record.
#
P<ALT-RECFMT-DEC> =
   "or" <RECFMT-DEC> <REST-OF-RECFMT-DEC> <ALT-RECFMT-DEC>,
  ;

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

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

# Now "name" has been removed from <OPT-arrayname-or-name>, check at the point
# of invocation, that "name" still works, now it as been added to <XTYPE>...
#
P<RECFMT-ELMNT> =
   <OPT-arrayname> <NAME> <Opt-NAME-LIST>,
   "array" <ADECLN-in-record>;

# Array declaration
P<ADECLN-in-record> =
   <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST-in-record>;

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

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

# Another Hamishism... %integer %array %name fred(1:*)
# which in Imp77 would be %integer %array (1) fred
# (reject the '*' in other contexts)
#
P<UPPERBOUND> =
   '*',
   <EXPR>,
   <BASE-XTYPE> <NAME>;

# List of lower/upper bound pairs, for array declarations.
P<BPAIR> =
   '(' <LOWERBOUND> ':' <UPPERBOUND> <REST-OF-BPLIST> ')';

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

# Multi-dimensional arrays
P<REST-OF-BPLIST> =
   ',' <EXPR> ':' <EXPR> <REST-OF-BPLIST>,
  ;

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

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

# for the example from 68000 Imp,
# %routine y (%integer %name %array (1) %name b)
P<Opt-DIMENSIONS> =
   '(' <INT-CONST> ')',
  ;

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

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

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

# Used in switch label.  (*) marks a catch-all. (like default: in C)
# Or at least SHOULD be used in switch label.  Looks like it isn't... just used in <REPFACT>?
P<STAROREXPR> =
   <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR>,
   '*';

# Opt- added here to allow plain "%long" as a declaration at <XTYPE>
P<Opt-BTYPE> =
   "real",
   "integer",
   "long" "real",
  ;

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

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

P<TEXTTEXT> =
   <dqstring>;

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

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

# 68000 Imp has a non-standard syntax for some array declarations, eg
#
# %conststring(1) %array(1:max params) parameters="B","W","L"
# %string(255) %array(0:maxfiles) list
#
# The equivalent Imp77 syntax would be
#
# %conststring(1) %array parameters(1:max params)="B","W","L"
# %string(255) %array list(0:maxfiles)
#
# Putting bounds after a %name is also from the 68000:
#
#    %integer%array%name an10(1:10)
#
# which is likely the same as
#
#    %integer%array(1)%name an10
#
#  in Imp77.  We just haven't been told the lower bound...
#
# meanwhile this broke: %string (64) %array %format sform1(0 : 1022)
#
# I'm not sure if there is any significant difference nowadays between own declarations and non-own declarations...
# the used to be distinguished by allowing initialisations... maybe worth merging the definitions now?
P<DECLN> =
   <OPT-arrayname> <OPT-spec> <NAME-or-STAR-68K> <OPT-68K-Bounds> <Opt-Init-assign> <Opt-Assign-NAME-LIST>,
   "array" <OPT-spec> <OPT-format> <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>,
   "array" <BPAIR> <OPT-spec> <OPT-format> <NAME> <Opt-NAME-LIST> <REST-OF-ARLIST-68K>;

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

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

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

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

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

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

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

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

# removed string resolution from assop - too much type checking
P<ASSOP> =
   '=' '=',
   <SCALAR-ASSIGN>;

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

# the version without "for" is to support an old-style statement, %WHILE <cond> %THEN <ui>
P<Percent-WU> =
   "while" <CONDITION>,
   "until" <CONDITION>;

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

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

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

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

# gt changed signal to allow 2 exprs.  Mouses Imp77 adds an optional string!
# 68000: %signal 5,,holeend,"HEAPPUT: heap corrupt" %if holeend>lhb_limit
P<OPEXPR2> =
   ',' <OPT-EXPR> <OPEXPR>,
  ;

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

# single optional ", expr" for use with %signal event (, subevent)?
P<OPEXPR> =
   ',' <OPT-EXPR> <OPSTRING>,
  ;

# %signal 3,4,position,"Set input fails" %unless position=0
P<OPSTRING> =
   ',' <STRING-EXPR>,
  ;

# reversed conditions and 1-line cycles
P<REST-OF-SS1> =
   <Percent-IU> <CONDITION> <S>,
   <Percent-WUF> <S>,
   <S>,
  ;

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

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

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

P<stupid> =
   <COMMENT>,
  ;

# extrinsic is an old form meaning roughly "external spec"
P<XOWN> =
   "own",
   "external",
   "extrinsic",
   "constant",
   "const";

E
@


1.2
log
@current
@
text
@a0 20
# I am considering removing <alphanumeric> and <digit> from the BIPs
# and just handling them via graamar rules (or regexps, once support is added)
# because a quick pass over the AST can consolidate a string of alphanumerics
# into a <NAME> relatively easily.  We really just need stropped letters,
# both types of string, NL, and EOF.  Everything else can be done with individual
# characters...
# stropped letter (%a %b etc)
B<stropped> = 0;
# upper or lower case letter or a digit, but only if the digit is following a letter
B<alphanumeric> = 1;
# digit
B<digit> = 2;
# single-quoted string
B<sqstring> = 3;
# double-quoted string
B<dqstring> = 4;
B<NL> = 5;
B<char> = 6;
B<EOF> = 7;

d8 80
d91 60
d156 2
a157 13
# The last items in a file. Main program has to be last, whether %begin/%endofprogram,
# or %begin/%end/%endoffile
# prim and perm files are effectively all externals followed by %endofprim or %endofperm
# a file of just externals has to end in %endoffile
#
# And to throw a spanner in the works, some old Pdp15 programs *appear* to omit the %begin
# from the begin/endofprogram, because they allow '!' comments to terminate with a ';'
# which later Imp versions doesn't allow.  For example in edit15.i15 we find:
#
# !!CS,N/CS,==1;%BEGIN; !EDIT15: COMPATIBLE CONTEXT EDITOR FOR PDP9/15
# This isn't really a problem with block structure, it's a problem with
# incompatible comment conventions which will likely require a command-line
# option to fix.
d159 8
a166 8
   "begin" <S> <INTERNALS> <ENDPROGRAM> <BLANKS> <EOF>,
   "endoffile" <BLANKS> <EOF>,
   "endofperm" <BLANKS> <EOF>,
   "endofprim" <BLANKS> <EOF>;

P<ENDPROGRAM> =
   "endofprogram",
   "end" <BLANKS> "endoffile";
d168 2
d175 1
a175 1
# - this isn't the 70's any more.
d177 13
a189 2
   <OPT-LABEL> <INTERNAL> <INTERNALS>,
   <OPT-LABEL>;
d192 6
d199 4
a202 2
   <Percent-SEX> <RT> "spec" <NAME> <Opt-ALIAS> <FPP> <S>,
   <Percent-SEX> <RT> <NAME> <Opt-ALIAS> <FPP> <S> <INTERNALS> "end" <S>,
d204 2
d207 1
d211 2
d216 3
d222 60
a281 6
# numeric labels are a historical form...
P<OPT-LABEL> =
   <NAME> '(' <CEXPR> ')' ':' <OPT-LABEL>,
   <NAME> '(' '*' ')' ':' <OPT-LABEL>,
   <NAME> ':' <OPT-LABEL>,
   <DIGIT-SEQ> ':' <OPT-LABEL>,
d286 2
a287 1
# they cannot appear in external routines.
d299 1
a299 1
   <FAULT-LIST> '-' '>' <GOTO-DESTINATION>;
d306 1
d309 3
a311 2
#   %realfn %spec f(%real x)
# the fully-qualified form is also valid but comes in via the <EXTERNAL> route.
d315 1
a315 2
# They are only allowed in the body of a procedure, and in the older compilers,
# all declarations in a block had to precede any code.
d317 1
a317 2
# There is good info re the phrase structure grammar to be found at
# http://www.ancientgeek.org.uk/EMAS/EMAS_Manuals/IMP/A_Syntactic_and_Semantic_Definition_of_the_IMP_Language.pdf
d330 1
a330 1
   "spec" <NAME> <Opt-ALIAS> <FPP> <S>,
d334 9
a345 5
# from "begin" "end" blocks
P<Opt-OFPROGRAM> =
   "of" "program" <BLANKS> <EOF>,
   <S>;

d361 1
a361 1
   <NAME> <Opt-PARAMETERS> <Opt-RECORDFIELD> <OPT-ASSIGN> <AUI>,
d363 1
a363 1
   '-' '>' <GOTO-DESTINATION>,
d376 1
a376 1
P<GOTO-DESTINATION> =
d387 1
a387 1
# added an old record declaration syntax for examples out of ai2logo.i
d396 3
a398 7
#    <XOWN> <XTYPE>         ("array name" | "name")?  ("spec")?  <SINGLE-OWNDEC>            <REST-OF-OWNDEC>,
#    <XOWN> <XTYPE> "array" ("format")?               ("spec")?  <NAME> <BPAIR> <CONSTLIST>;

#           <XTYPE>         ("array name" | "name")?  <NAME> <Opt-Const-assign> <Opt-Assign-NAME-LIST>,
#           <XTYPE> "array" ("format")?               <NAME>                    <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>;

#   <XOWN> "record" "array" <NAME> <BPAIR> '(' <RECFMT-REF> ')' <S> <XTYPE> <DECLN> <S>,
d403 20
a422 7
# To do: handle %OWNSTRING(1) STERMIN=TOSTRING(10); which is currently rejected.
# - initialisation with the result of a procedure call (or any non-const expression) should
# be OK inside a block but not at the external level.
# Though if the initialisation method is by copying from the GLAP to the GLA, maybe there
# was a reason why any %owns could not be initialised this way.  It may be that tostring
# was an exception because it was a prim function and could be treated like a constant
# by the compiler...
d424 9
a432 8
   <XOWN> <XTYPE> <OWNDEC> <S>,
   <XOWN> "record" "array" <RADEC> <REST-OF-RADEC> <S>,
   <XOWN> "record" <RDEC> <REST-OF-RDEC> <S>,
   <XTYPE> <DECLN> <S>,
   "record" "array" <RADEC> <REST-OF-RADEC> <S>,
   "record" <RDEC> <REST-OF-RDEC> <S>,
   "record" "format" <RECFMT-spec-OR-BODY> <S>,
   "switch" <NAME> <Opt-NAME-LIST> '(' <EXPR> ':' <EXPR> ')' <REST-OF-SWLIST> <S>;
d466 15
a480 4
# "else if ..." or "else" or "finish {else ...}"
# there's an ugly form of "else if ... start" that I haven't found an
# example of yet, which I'm not going to add unless I do.  If I remember
# right, it did not balance starts and finished correctly.
d482 2
a483 2
   "else" <Percent-IU> <CONDITION> <S> <INTERNALS> <FINISH-S>,
   "else" <S> <INTERNALS> "finish" <S>,
d486 5
d513 9
d523 2
a524 2
   "comment" <Rest-of-line>,
   '!' <Rest-of-line>;
d526 7
a532 9
# This is a hack that needs to be better integrated with comments and whitespace and machine code instructions...
# must end in <NL>, not <S> ...
P<Rest-of-line> =
   <char> <Rest-of-line>,
   <dqstring> <Rest-of-line>,
   <sqstring> <Rest-of-line>,
   <alphanumeric> <Rest-of-line>,
   <digit> <Rest-of-line>,
   <stropped> <Rest-of-line>,
d535 7
d549 3
d553 1
a553 3
   <DIGIT-SEQ> '_' <DIGIT-SEQ> <NAME>,
   <DIGIT-SEQ> '_' <DIGIT-SEQ>,
   <DIGIT-SEQ> '_' <NAME>,
d555 8
a562 1
   <OLDHEX>;
a582 6
# expensive hacky test to temporarily handle X'...'
# ... and it's broken :-(  <!...> currently not working properly.
# both being reported wrongly, and not generating correct code below:
# a negated phrase should not create an AST entry. Or rather, it should create a null one.
# - I think the problem is only phrases can be negated, not BIPs ... so ...
#
d585 5
a589 2
P<OLDHEX> =
   <OPT-SQ-LETTER> <sqstring>;
d591 3
a593 10
# current line reconstruction doesn't support this. Letters come through as part of <alphanumeric>s.
#P<OPT-SQ-LETTER> =
#   'M',
#   'X',
#   'B',
#  ;

P<OPT-SQ-LETTER> =
   <alphanumeric>,
  ;
d599 4
d614 1
a614 1
P<OPT-PM> =
d620 1
a620 1
   '@@' <OPT-PM> <DIGIT-SEQ>,
d631 1
a631 1
   <alphanumeric> <!SQUO-STRING> <rest-of-name>;
d633 6
a638 3
P<rest-of-name> =
   <NAME>,
  ;
d643 3
d647 1
a647 1
# Hamish's PDP15 imp machine code syntax definitely not supported.
d649 5
a653 1
   <Rest-of-line>;
a668 2
# <NAME> <Opt-PARAMETERS> added by gt for predicates
# RESTOFSC handles double-sided conditions such as '0' <= ch <= '9'
d672 2
d677 1
a677 1
   <NAME> <Opt-PARAMETERS>,
d688 2
a689 2
# Current Imp77 restricted to -> a.("test").b  with a and b being optional.
# Older Emas implementation allowed multiple parts I'm fairly sure, eg -> a.("match1").b.("match2").c
d695 2
d708 4
a711 1
# No bracketed expressions, only '.' operator allowed.
d720 1
a720 1
   '(' <STRING-RVALUE> ')';
d734 5
d740 1
d742 1
a743 2
   '=' '=',
   <NOT-EQUALS-ADDRESS>,
d749 3
d761 3
a763 3
# 'Application' i.e. parameters to routine or function call
P<Opt-PARAMETERS> =
   '(' <EXPR> <REST-OF-PARAMETERS> ')',
d766 2
a767 2
P<REST-OF-PARAMETERS> =
   ',' <EXPR> <REST-OF-PARAMETERS>,
d806 1
a806 4
#P<EXPR> =
#   <Opt-UNARY-OP> <OPERAND> <REST-OF-EXPR>;

# for Hamish's PDP9/15 Imp (old): INDP == INDEX0++P
d808 3
d815 1
a815 1
   <Opt-PARAMETERS>;
d818 1
a818 1
   '_' <NAME> <Opt-PARAMETERS> <Opt-RECORDFIELD>,
d826 14
a839 1
# Unfortunately there may be obscure cases where an ambiguous parse might result.
d847 28
a874 2
# operators all together for now until we use the grammar to handle precedence.
# regular exprs converted, cexprs not done yet.
d887 13
d929 28
a956 1
# unary operators.  Highest precedence.
a999 9
# compare re <OPERAND> =
#   <NAME> <Opt-PARAMETERS-OR-POINTERINDEX> <Opt-RECORDFIELD-OR-POINTERINDEX>,
#   <CONST>,
#   '(' <EXPR> ')',
#   '|' <EXPR> '|',
#   '!' <EXPR> '!';

# - so I'll add || to COPERAND as well...
# needs a semantic test that <NAME> is a defined const...
d1019 2
d1022 1
a1022 1
   <XTYPE> <OPT-arrayname-OR-name> <NAME> <Opt-NAME-LIST>,
d1024 11
a1034 1
   "name" <NAME> <Opt-NAME-LIST>;
d1055 3
a1057 1
# label and short added by gt as a quick hack
d1059 4
a1062 2
# Why is it record (<rfref?>) rather than record (<rfref>) - is record () ever an option?
P<XTYPE> =
d1066 5
a1070 2
   "long" <BTYPE>,
   "byte" <FULLTYPE>,
a1071 2
   "half" <FULLTYPE>,
   "short" <FULLTYPE>,
d1074 27
d1114 4
d1130 3
d1134 2
a1135 2
   <OPT-arrayname-OR-name> <NAME> <Opt-NAME-LIST>,
   "array" <ADECLN>;
d1138 2
a1139 2
P<ADECLN> =
   <NAME> <Opt-NAME-LIST> <BPAIR> <REST-OF-ARLIST>;
d1141 2
a1142 2
P<REST-OF-ARLIST> =
   ',' <ADECLN>,
d1145 13
d1160 4
a1163 1
   '(' <EXPR> ':' <EXPR> <REST-OF-BPLIST> ')';
d1170 22
a1191 3
P<OPT-arrayname-OR-name> =
   "array" "name",
   "name",
d1194 1
a1194 1
P<FULLTYPE> =
d1214 2
a1215 1
P<BTYPE> =
d1218 2
a1219 1
   "long" "real";
d1233 7
d1251 24
a1274 2
# Scalar declaration
# <Opt-Const-assign> added by gt
d1276 20
a1295 2
   <OPT-arrayname-OR-name> <NAME> <Opt-Const-assign> <Opt-Assign-NAME-LIST>,
   "array" <OPT-format> <ADECLN>;
d1301 28
a1328 1
# <Opt-Const-assign> added by gt
d1330 14
a1343 1
   ',' <NAME> <Opt-Const-assign> <Opt-Assign-NAME-LIST>,
d1346 12
a1357 4
# The <CEXPR> one may be redundant.  Had to put expr first to catch = tostring(i) because cexpr only got tostring
P<Opt-Const-assign> =
   '=' <EXPR>,
   '=' <CEXPR>,
d1368 5
a1372 1
   "for" <NAME> '=' <EXPR> ',' <EXPR> ',' <EXPR>;
d1379 1
a1379 1
   <NAME> '=' <EXPR> ',' <EXPR> ',' <EXPR>,
d1383 1
d1385 5
a1389 1
   ',' <EXPR> <OPEXPR>,
d1394 1
a1394 1
   ',' <EXPR>,
d1397 4
a1400 5
# removed string resolution from assop - too much type checking
P<ASSOP> =
   '=' '=',
   '=',
   '<' '-';
a1408 4
P<OWNDEC> =
   <OPT-arrayname-OR-name> <OPT-spec> <SINGLE-OWNDEC> <REST-OF-OWNDEC>,
   "array" <OPT-format> <OPT-spec> <NAME> <BPAIR> <CONSTLIST>;

d1414 1
a1414 8
   <NAME> <Opt-ALIAS> <Opt-Assign-CONST>;

# An initialised array declaration does not need a %C following the '=' before the list of initial values.
# this is a stupid hack to get around comments in the middle of array declarations :-(
# note that it gobbles the newline
P<CONSTLIST> =
   '=' <stupid> <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR> <REPFACT> <ROCL>,
  ;
a1421 10
  ;

P<OPT-spec> =
   "spec",
  ;

# '== 0' needed for %const %record(*) %name nil == 0 - gt  Only 0 allowed.
P<Opt-Assign-CONST> =
   '=' <Opt-UNARY-OP> <COPERAND> <REST-OF-CEXPR>,
   '=' '=' <DIGIT-SEQ>,
@


1.1
log
@Initial revision
@
text
@d1 7
d9 1
d11 1
d13 1
d15 1
d21 7
a27 2
# Extended grammar for Imp80 on Emas-2900 series, also accepts several Imp77 constructs...
# based on https://history.dcs.ed.ac.uk/archive/os/emas/emas2/compilers/imp80/imp80ps04.txt
d29 1
a29 1
   <STATEMENT-LIST> <EOF>;
d31 121
a151 2
P<STATEMENT-LIST> =
   <STATEMENT> <STATEMENT-LIST>,
d154 64
a217 4
# gt removed <TEXT>, added <COMMENT>.  Moved sw(*): up to take precedence.
# added %on %event * %start
P<STATEMENT> =
   <NAME> '(' '*' ')' ':',
d219 2
a220 6
   <UI> <RESTOFSS1>,
   <Percent-IU> <SC> <RESTOFCOND> <RESTOFIU> <S>,
   "finish" <Opt-ELSE> <S>,
   "cycle" <CYCPARM> <S>,
   "repeat" <RESTOFREPEAT> <S>,
   <Percent-WU> "cycle" <S>,
d222 50
a271 17
   "end" <ENDLIST> <S>,
   "record" "format" <RFSTMNT> <S>,
   <Percent-SEX> <RT> <Opt-PercentSPEC> <NAME> <Opt-ALIAS> <FPP> <S>,
   "include" <CONST>,
   "from" <NAME> "include" <NAME> <Opt-NLIST>,
   "begin" <S>,
   "on" <Opt-PercentEVENT> '*' <RESTOFNLIST> "start" <S>,
   "on" <Opt-PercentEVENT> <CEXPR> <RESTOFNLIST> "start" <S>,
   "switch" <NAME> <Opt-NLIST> '(' <EXPR> ':' <EXPR> ')' <RESTOFSWLIST> <S>,
   "list" <S>,
   "else" <S>,
   "else" <Percent-IU> <SC> <RESTOFCOND>,
   '*' <UCI>,
   "trusted" "program" <S>,
   "main" "ep" <NAME> <S>,
   "control" <CONST> <S>,
   <COMMENT>,
d274 12
d294 2
d305 1
a305 1
# <NUMBER> '_' <NUMBER>, and  <NUMBER> '_' <NAME>, added by gt
d307 26
a332 1
# VERY hacky stopgap just to get stuff to parse. Needs proper syntax.
d334 3
a336 7
   <NUMBER> '_' <NUMBER> <NAME>,
   <NUMBER> '_' <NUMBER>,
   <NUMBER> '_' <NAME>,
   <NUMBER>,
   <OLDHEX>,
   <sqstring>,
   <dqstring>;
d343 3
d347 36
a382 1
   <alphanumeric> <sqstring>;
d384 1
a384 1
P<NUMBER> =
d388 1
a388 1
   <NUMBER>,
d401 2
a402 1
# Machine-specific machine-code removed.
d407 3
a409 3
P<RESTOFCOND> =
   "and" <SC> <RESTOFANDC>,
   "or" <SC> <RESTOFORC>,
d412 2
a413 2
P<RESTOFORC> =
   "or" <SC> <RESTOFORC>,
d416 5
a420 1
# <NAME> <APP> added by gt for predicates
d422 3
d426 4
a429 3
   <EXPR> <COMP1> <EXPR> <RESTOFSC>,
   <NAME> <APP>,
   '(' <SC> <RESTOFCOND> ')',
d432 67
d500 2
a501 2
P<APP> =
   '(' <EXPR> <RESTOFAPP> ')',
d504 2
a505 2
P<RESTOFAPP> =
   ',' <EXPR> <RESTOFAPP>,
d508 2
a509 1
# |...| added by gt
d511 15
a525 2
   <Opt-PLUS> <OPERAND> <RESTOFEXPR>,
   '|' <EXPR> '|';
d527 2
a528 2
P<RESTOFEXPR> =
   <OP> <OPERAND> <RESTOFEXPR>,
d531 23
a553 4
P<OPERAND> =
   <NAME> <APP> <Opt-ENAME>,
   <CONST>,
   '(' <Opt-EXPR> ')';
d555 2
a556 2
P<Opt-EXPR> =
   <EXPR>,
d559 2
a560 2
P<Opt-ENAME> =
   '_' <NAME> <APP> <Opt-ENAME>,
d563 11
d575 16
a590 2
   '+',
   '-',
d592 4
d597 5
d605 6
a610 2
   '<' '<',
   '>' '>',
a612 1
   '&',
d615 2
a616 2
# unary operators
P<Opt-PLUS> =
d619 1
a619 1
   '\\',
d622 4
a625 21
P<RESTOFSC> =
   <COMP2> <EXPR>,
  ;

P<COMP2> =
   <COMP1>;

P<COMP1> =
   '=',
   '#',
   '=' '=',
   '#' '#',
   '<' '=',
   '<',
   '>' '=',
   '>',
   '-' '>';

P<RESTOFANDC> =
   "and" <SC> <RESTOFANDC>,
  ;
d632 4
a635 2
P<RESTOFSWLIST> =
   ',' <NAME> <Opt-NLIST> '(' <EXPR> ':' <EXPR> ')' <RESTOFSWLIST>,
d638 2
a639 2
P<Opt-NLIST> =
   ',' <NAME> <Opt-NLIST>,
d642 2
a643 2
P<RESTOFNLIST> =
   ',' <CEXPR> <RESTOFNLIST>,
d646 6
d653 1
a653 1
   <Opt-PLUS> <COPERAND> <RESTOFCEXPR>;
d655 2
a656 2
P<RESTOFCEXPR> =
   <OP> <COPERAND> <RESTOFCEXPR>,
d659 9
d671 3
a673 1
   '(' <Opt-CEXPR> ')';
d675 1
a675 5
P<Opt-CEXPR> =
   <CEXPR>,
  ;

P<Opt-PercentEVENT> =
d680 1
a680 1
   '(' <FPDEL> <RESTOFFPLIST> ')',
d683 2
a684 2
P<RESTOFFPLIST> =
   <Opt-Comma> <FPDEL> <RESTOFFPLIST>,
d687 4
a690 4
P<FPDEL> =
   <XTYPE> <Opt-PercentQNAME> <NAME> <Opt-NLIST>,
   <RT> <Opt-PercentNAME> <NAME> <Opt-NLIST> <FPP>,
   "name" <NAME> <Opt-NLIST>;
d692 1
a692 1
P<Opt-PercentNAME> =
d700 6
a705 1
   <XTYPE> <FM>;
d707 1
a707 1
P<FM> =
a708 1
   "map",
d712 2
d723 1
a723 1
   "record" '(' <Opt-RFREF> ')';
d726 1
d728 2
a729 2
   <RFREF>,
   '*',
d733 1
a733 1
P<RFREF> =
d735 1
a735 1
   <RFDEC> <RESTOFRFDEC> <ALTRFDEC>;
d737 3
a739 2
P<ALTRFDEC> =
   "or" <RFDEC> <RESTOFRFDEC> <ALTRFDEC>,
d742 2
a743 2
P<RESTOFRFDEC> =
   ',' <RFDEC> <RESTOFRFDEC>,
d746 3
a748 3
P<RFDEC> =
   <XTYPE> <RFELMNT>,
   '(' <RFDEC> <RESTOFRFDEC> <ALTRFDEC> ')';
d750 2
a751 2
P<RFELMNT> =
   <Opt-PercentQNAME> <NAME> <Opt-NLIST>,
d756 1
a756 1
   <NAME> <Opt-NLIST> <BPAIR> <RESTOFARLIST>;
d758 1
a758 1
P<RESTOFARLIST> =
d764 1
a764 1
   '(' <EXPR> ':' <EXPR> <RESTOFBPLIST> ')';
d767 2
a768 2
P<RESTOFBPLIST> =
   ',' <EXPR> ':' <EXPR> <RESTOFBPLIST>,
d771 1
a771 1
P<Opt-PercentQNAME> =
d781 1
d791 1
a791 1
# Or at least SHOULD be used in switch label.  Looks like it isn't... gt
d793 1
a793 1
   <Opt-PLUS> <COPERAND> <RESTOFCEXPR>,
d812 1
a812 4
P<Opt-PercentSPEC> =
   "spec",
  ;

d820 1
a820 1
P<RFSTMNT> =
d822 1
a822 10
   <NAME> '(' <RFDEC> <RESTOFRFDEC> <ALTRFDEC> ')';

# prim and perm added by gt
P<ENDLIST> =
   "of" "program",
   "of" "file",
   "of" "prim",
   "of" "perm",
   "of" "list",
  ;
d827 2
a828 2
   <Opt-PercentQNAME> <NAME> <Opt-Const-assign> <Opt-Assign-NLIST>,
   "array" <Opt-PercentFORMAT> <ADECLN>;
d830 1
a830 1
P<Opt-PercentFORMAT> =
d835 2
a836 2
P<Opt-Assign-NLIST> =
   ',' <NAME> <Opt-Const-assign> <Opt-Assign-NLIST>,
d845 1
d847 6
a852 2
   "while" <SC> <RESTOFCOND>,
   "until" <SC> <RESTOFCOND>,
d855 2
a856 2
P<RESTOFREPEAT> =
   "until" <SC> <RESTOFCOND>,
d859 1
a859 1
P<CYCPARM> =
d863 1
a863 27
P<Opt-ELSE> =
   "else" <AFTERELSE>,
  ;

P<AFTERELSE> =
   "start",
   <Percent-IU> <SC> <RESTOFCOND> <RESTOFIU>,
   <UI>;

# gt added true, false.  moved <AUI> to RESTOFUI so it doesn't follow a label
# should do something similar with <Opt-ENAME> because labels can't be record fields
# These are all UIs that cannot be followed by other UIs because they jump elsewhere.
# (although in some versions of Imp that isn't true of %monitor which can return)
P<UI> =
   <NAME> <APP> <Opt-ENAME> <RESTOFUI> <AUI>,
   '-' '>' <NAME> <APP>,
   "return",
   "true",
   "false",
   "result" <ASSOP> <EXPR>,
   "monitor" <AUI>,
   "stop",
   "signal" <Opt-PercentEVENT> <CEXPR> <OPEXPR2>,
   "exit",
   "continue";

# gt changed signal to allow 2 exprs
d873 1
a873 5
# can avoid a start/finish block by call1 %and call2 %if condition
P<AUI> =
   "and" <UI>,
  ;

a876 1
   '-' '>',
d879 5
a883 5
# gt moved COLON to end instead of front. <COLON> appears to be an action, not a character
# as the grammar has ':' <COLON> at the location of a label.  So it's a NULL term.
P<RESTOFUI> =
   <ASSOP> <EXPR>,
   ':',
d886 3
a888 4
P<RESTOFIU> =
   "start",
   "then" "start",
   "then" <UI> <Opt-ELSE>;
d890 2
a891 4
P<RESTOFSS1> =
   <S>,
   <Percent-IU> <SC> <RESTOFCOND> <S>,
   <Percent-WU> <S>,
d894 3
a897 4
P<OWNDEC> =
   <Opt-PercentQNAME> <Opt-VSPEC> <NAME> <Opt-ALIAS> <Opt-CONST> <RESTOFOWNDEC>,
   "array" <Opt-PercentFORMAT> <Opt-VSPEC> <NAME> <BPAIR> <CONSTLIST>;

d901 1
a901 1
   '=' <stupid> <Opt-PLUS> <COPERAND> <RESTOFCEXPR> <REPFACT> <ROCL>,
d905 1
a905 1
   ',' <stupid> <Opt-PLUS> <COPERAND> <RESTOFCEXPR> <REPFACT> <ROCL>,
d912 1
a912 1
P<Opt-VSPEC> =
a915 4
P<RESTOFOWNDEC> =
   ',' <NAME> <Opt-ALIAS> <Opt-CONST> <RESTOFOWNDEC>,
  ;

d917 3
a919 3
P<Opt-CONST> =
   '=' <Opt-PLUS> <COPERAND> <RESTOFCEXPR>,
   '=' '=' <NUMBER>,
d922 1
@
