#
# File: parser.k3
# Grammar module parser
#
# Copyright 2005, Radboud University of Nijmegen.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# CVS ID = "$Id: parser.k3,v 1.59 2006/08/09 11:13:09 marcs Exp $"
#

#
# The number of rules is stored locally, since the compiler
# introduces synthetic rules (for groups and options).
#
MODULE parser =	ROOT_SEEN
		NR_SYNTAX_RULES
                NR_SYNTAX_ALTS
                NR_COMMITS
                NR_GROUPS
		NR_POSITIONS
                NR_OPTIONS.

DEFINES ACTION parse grammar,
        ACTION log grammar statistics,
        FUNCTION get number of positions (INT>).

USES globals, tokens, io, id, set, lexer, affix, rule,
     properties, modules, transduction, textstorage.

NR_SYNTAX_RULES,
NR_SYNTAX_ALTS,
NR_COMMITS,
NR_GROUPS,
NR_POSITIONS,
NR_OPTIONS = INT.
ROOT_SEEN = BOOL.

#
# Initialize the parser
#
PRELUDE init parser:
  [ false -> ROOT_SEEN ],
    [ 0 -> NR_SYNTAX_RULES ],
    [ 0 -> NR_SYNTAX_ALTS ],
    [ 0 -> NR_COMMITS ],
    [ 0 -> NR_GROUPS ],
    [ 0 -> NR_OPTIONS ],
    [ 0 -> NR_POSITIONS ].

#
# An AGFL grammar consists of a main grammar which should contain the root
# of the grammar together with a number (possibly 0) of subgrammars and lexicons
#
# The parser is commented by giving the relevant grammar rules
# In these rules the following abbreviations are used:
#
# NOTION option: NOTION; .
# NOTION sequence: NOTION, NOTION sequence option.
# NOTION list: NOTION, comma, NOTION list.
# NOTION pack: left parenthesis, NOTION, right parenthesis.
# 
ACTION parse grammar:
  parse main grammar module,
    read used grammar modules,
    log modules.

ACTION parse main grammar module:
  get basename (TEXT),
    add unread grammar module (TEXT),
    open grammar file (TEXT),
    write ("  reading grammar \"" + TEXT + "\"\n"),
    should be grammar head,
    should be grammar body,
    should be end of file,
    close current input file,
    mark grammar module as read (TEXT).

ACTION read used grammar modules:
  try read used grammar module,
    read used grammar modules;
  +.

PRED try read used grammar module:
  try get unread grammar module (TEXT),
    write ("  reading grammar \"" + TEXT + "\"\n"),
    open grammar file (TEXT),
    may be grammar head,
    should be grammar body,
    should be end of file,
    close current input file,
    mark grammar module as read (TEXT).

ACTION open grammar file (>TEXT):
  try open grammar file (TEXT);
  abort ("cannot open grammar file \"" + TEXT + "\"").

ACTION should be end of file:
  eof;
  get current position (POS),
    warning ("trailing garbage at end of file", POS).

#
# grammar head:
#   grammar token, grammar name, period token.
#
# As a side effect we check that the grammar name matches the file name
# This latter rule must be loosened
#
ACTION should be grammar head:
  is grammar head;
  syntax error ("\"GRAMMAR\" name specification expected").

ACTION may be grammar head:
  is grammar head;
  +.

PRED is grammar head:
  is token (grammar symbol),
    ( is module name (TEXT), verify module name (TEXT), should be token (period);
      syntax error ("grammar module name expected")
    ).

ACTION verify module name (>TEXT1):
  get current module name (TEXT2),
    ( [ TEXT1 = TEXT2 ];
        get current position (POS),
        error ("grammar name \"" + TEXT1 + "\" does not match file name \"" + TEXT2 + "\"", POS)
    ).

#
# grammar body:
#   statement sequence.
#
ACTION should be grammar body:
  is statement,
    may be statement sequence,
    display time ("grammar body"),
    should be end of file;
  syntax error ("grammar body expected").

ACTION may be statement sequence:
  is statement,
    may be statement sequence;
  +.

#
# statement:
#   lexicon module;
#   includes part;
#   root statement;
#   specification;
#   definition.
#
PRED is statement:
  get current position (POS), current pragmats (PRAGMATS),
    ( are pragmats;
      is lexicon module;
      is includes part;
      is root statement;
      is typed syntax rule or specification (PRAGMATS, POS);
      is rule or specification (POS, PRAGMATS)
    ).

#
# includes part:
#   includes token, module name list, period token.
#
PRED is includes part:
  is token (includes symbol),
    should be grammar module name list,
    should be token (period).

ACTION should be grammar module name list:
  is module name (TEXT),
    add unread grammar module (TEXT),
    rest grammar module name list;
  syntax error ("grammar module name expected").

ACTION rest grammar module name list:
  is token (comma),
    should be grammar module name list;
  +.

#
# root statement:
#   root token, root specification, period
#
# root specification:
#   syntax nonterminal, formal parameter list pack option.
#
# Although we accept a formal parameter list pack, the enter root nonterminal
# will check that only nullary nonterminals are accepted as the grammar root.
#
PRED is root statement:
  is token (root symbol),
    root specification,
    should be token (period).

ACTION root specification:
  is identifier (ID),
    get current position (POS),
    may be formal parameter list pack (FORMALS, ARITY),
    mark id as syntax nonterminal (ID, ARITY, NONTERMINAL),
    maybe enter root nonterminal (NONTERMINAL, true);
  syntax error ("root nonterminal name expected").

#
# Why not give an error for a duplicate ROOT
#
ACTION maybe enter root nonterminal (>NONTERMINAL, >BOOL):
  [ROOT_SEEN -> true],
    ([BOOL -> true],
       warning ("duplicate ROOT specification, ignoring current specification");
     +);
  [true -> ROOT_SEEN], enter root nonterminal (NONTERMINAL).

#
# definition:
#   syntax rule;
#   affix rule.
#
# affix rule:
#   affix rule head, double colon token, affix expression, period token.
#
# affix rule head:
#   affix nonterminal list.
#
# specification:
#   left hand side, period token.
#
# syntax rule:
#   left hand side, colon token, right hand side, period token
#
# left hand side:
#   syntax rule head list.
#
# syntax rule head:
#   syntax rule type, nonterminal name, formal parameter list pack option.
#
# syntax rule option:
#   rule token option;
#   option token;
#   condition token.
#
# Since every affix nonterminal is a valid syntax nonterminal, we cannot
# decide a priori whether we are parsing an affix rule, an untyped syntax
# rule or a specification.
#
# Only after encountering a '::', ':', '.' or '(' can we safely distinguish
# between affix rules and syntax rules/specifications. So we temporarily
# collect a list of all encountered identifiers and handle them later.
#
# As soon as we know that we parse a syntax rule or specification, we convert
# the identifier list into a LHS list.
# 
IDS :: ID IDS;
       nil.

PRED is rule or specification (>POS, >PRAGMATS):
  is identifier list (IDS),
    ( is token (double colon),
        should be rest affix rule (IDS, POS);
      rest syntax rule or specification (IDS, PRAGMATS, POS)).

PRED is identifier list (IDS>):
  is identifier (ID),
    ( is token (comma), should be identifier list (IDS), [ ID IDS -> IDS ];
      [ ID nil -> IDS ]
    ).

ACTION should be identifier list (IDS>):
  is identifier list (IDS);
  syntax error ("identifier list expected"),
    !.

#
# In the following rules we are certain that we handle syntax rules or specifications.
# We first collect all possible left hand sides before deciding what to do with them.
#
# Note that NP, NP (CASE): ... constitutes a valid left hand side
#
LHSS :: LHS LHSS;
        nil.

LHS :: NONTERMINAL with FORMALS at POS typed as RULE_TYPE.

PRED is typed syntax rule or specification (>PRAGMATS, >POS):
  is rule type (RULE_TYPE),
    ( is identifier (ID),
        may be formal parameter list pack (FORMALS, ARITY),
	make left hand side (ID, FORMALS, ARITY, PRAGMATS, POS, RULE_TYPE, LHS),
        rest syntax rule or specification (LHS nil, PRAGMATS, POS, RULE_TYPE);
      syntax error ("syntax rule name expected"),
        !).

PRED is rule type (RULE_TYPE>):
  is token (cond symbol), [ cond -> RULE_TYPE ];
  is token (rule symbol), [ rule -> RULE_TYPE ];
  is token (option symbol), [ option -> RULE_TYPE ].

ACTION rest syntax rule or specification (>IDS, >PRAGMATS, >POS):
  may be formal parameter list pack (FORMALS, ARITY),
    ( [ IDS -> ID IDS2 ],
        make simple left hand sides (IDS2, PRAGMATS, POS, LHSS),
	make left hand side (ID, FORMALS, ARITY, PRAGMATS, POS, unknown, LHS),
	rest syntax rule or specification (LHS LHSS, PRAGMATS, POS, unknown);
      ?
    ).

ACTION rest syntax rule or specification (>LHSS, >PRAGMATS, >POS, >RULE_TYPE):
   is token (period), enter specifications (LHSS);
   is token (comma),
     should be left hand sides (RULE_TYPE, PRAGMATS, LHSS2),
       ( is token (period),
	   enter specifications (LHSS),
	   enter specifications (LHSS2);
         should be token (colon),
           should be right hand side (ALTS),
           should be token (period),
	   enter syntax rules (LHSS, ALTS),
	   enter syntax rules (LHSS2, ALTS)
       );
   is token (colon),
     should be right hand side (ALTS),
     should be token (period),
     enter syntax rules (LHSS, ALTS);
   syntax error ("rest syntax rule or specification expected").

ACTION should be left hand sides (>RULE_TYPE, >PRAGMATS, LHSS>):
  is left hand side (RULE_TYPE, PRAGMATS, LHS),
    ( is token (comma),
	should be left hand sides (RULE_TYPE, PRAGMATS, LHSS1),
        [ LHS LHSS1 -> LHSS ];
      [ LHS nil -> LHSS ]
    );
  syntax error ("left hand side expected"), [ nil -> LHSS ].

PRED is left hand side (>RULE_TYPE, >PRAGMATS, LHS>):
  get current position (POS),
    is identifier (ID),
      may be formal parameter list pack (FORMALS, ARITY),
      make left hand side (ID, FORMALS, ARITY, PRAGMATS, POS, RULE_TYPE, LHS).

ACTION make simple left hand sides (>IDS, >PRAGMATS, >POS, LHSS>):
  [ IDS -> nil ], [ nil -> LHSS ];
  [ IDS -> ID IDS ],
    make simple left hand sides (IDS, PRAGMATS, POS, LHSS1),
    make left hand side (ID, nil, arity 0, PRAGMATS, POS, unknown, LHS),
    [ LHS LHSS1 -> LHSS ];
  ?.

ACTION make left hand side (>ID, >FORMALS, >ARITY, >PRAGMATS, >POS, >RULE_TYPE, LHS>):
   mark id as syntax nonterminal (ID, ARITY, NONTERMINAL),
   store pragmats (NONTERMINAL, PRAGMATS),
   [ NONTERMINAL with FORMALS at POS typed as RULE_TYPE -> LHS ].

ACTION enter specifications (>LHSS):
   [ LHSS -> nil ];
   [ LHSS -> LHS LHSS2 ], enter specifications (LHSS2),
     [ LHS -> NONTERMINAL with FORMALS at POS typed as RULE_TYPE ],
     enter specification (NONTERMINAL, RULE_TYPE, FORMALS, POS);
   ?.

ACTION enter syntax rules (>LHSS, >ALTS):
  [ LHSS -> nil ];
  [ LHSS -> LHS LHSS1 ], enter syntax rules (LHSS1, ALTS),
    [ LHS -> NONTERMINAL with FORMALS at POS typed as RULE_TYPE ],
     maybe enter root nonterminal (NONTERMINAL, false),
     enter syntax rule (LHS, ALTS);
  ?.

ACTION enter syntax rule (>LHS, >ALTS):
  [ NR_SYNTAX_RULES + 1 -> NR_SYNTAX_RULES ],
    [ LHS -> NONTERMINAL with FORMALS at POS typed as RULE_TYPE ],
    enter syntax rule (NONTERMINAL, RULE_TYPE, FORMALS, ALTS, POS).

#
# right hand side:
#   alternatives.
#
ACTION should be right hand side (ALTS>):
  should be syntax alternatives (ALTS).

#
# formal parameter:
#   affix expression
#
ACTION may be formal parameter list pack (FORMALS>, ARITY>):
  is formal parameter list pack (FORMALS, ARITY); 
  [ nil -> FORMALS ], [ arity 0 -> ARITY ].

PRED is formal parameter list pack (FORMALS>, ARITY>):
  is token (left parenthesis),
    should be formal parameter list (FORMALS, ARITY),
    should be token (right parenthesis).

ACTION should be formal parameter list (FORMALS>, ARITY>):
  should be formal parameter list (EXPRS, INT),
    [ EXPRS -> FORMALS ], [ arity INT -> ARITY ].

ACTION should be formal parameter list (EXPRS>, INT>):
  should be formal parameter (EXPR),
    may be rest formal parameter list (EXPRS1, INT1),
    [ EXPR EXPRS1 -> EXPRS ], [ INT1 + 1 -> INT ].

ACTION may be rest formal parameter list (EXPRS>, INT>):
  is token (comma), should be formal parameter list (EXPRS, INT);
  [nil -> EXPRS], [0 -> INT].

ACTION should be formal parameter (EXPR>):
  is formal parameter (EXPR);
  syntax error ("formal parameter expected"),
    make erroneous (EXPR).

PRED is formal parameter (EXPR>):
  is affix expression (EXPR).

FUNCTION make erroneous (EXPR>):
  [ erroneous -> EXPR ].

#
# affix expression:
#    affix variable;
#    affix terminal set;
#    quoted string;
#    number;
#
# affix terminal set:
#    affix terminal set, bar token, affix terminal
#    affix terminal.
#
PRED is affix expression (EXPR>):
  get current position (POS),
    ( is affix variable (EXPR);
      is affix terminal (ID), 
        mark id as unknown affix (ID),
        may be rest affix terminal set (ID, POS, EXPR);
      is quoted string (ID), mark id as text constant (ID), [ text const ID -> EXPR ];
      is number (INT), [ integer const INT -> EXPR ]
    ).

ACTION may be rest affix terminal set (>ID, >POS, EXPR>):
  is token (bar),
    [ ID nil -> MALTS ],
    should be affix terminal set (MALTS),
    create anonymous affix rule (MALTS, ID),
    [ set ID -> EXPR ];
  [ affix ID -> EXPR ].

ACTION should be affix terminal set (>MALTS>):
  get current position (POS),
    should be affix terminal (MALTS),
      ( is token (bar), should be affix terminal set (MALTS);
        +
      ).

ACTION should be affix terminal (>MALTS>):
  get current position (POS),
    ( is affix terminal (ID), mark id as unknown affix (ID),
	( add unique (MALTS, ID);
	  report duplicate affix in set (ID, POS)
	);
      syntax error ("affix terminal expected")
    ).

ACTION report duplicate affix in set (>ID, >POS):
  convert id to name (ID, TEXT),
    warning ("duplicate affix \"" + TEXT + "\" in expression", POS).

#
# alternatives:
#   alternative series, commit token, alternatives;
#   alternative series.
#
# alternative:
#   production, transduction part option.
#
# production:
#   frequency part option, syntax part.
#
# frequency part option,
#   sub token, number, bus token.
#
ACTION should be syntax alternatives (ALTS>):
  syntax alternative (ALT),
    rest syntax alternatives (ALTS1),
    [ALT ALTS1 -> ALTS].

ACTION rest syntax alternatives (ALTS>):
  is token (semicolon),
    should be syntax alternatives (ALTS);
  is token (commit),
    [NR_COMMITS + 1 -> NR_COMMITS],
    should be syntax alternatives (ALTS);
  [nil -> ALTS].

ACTION syntax alternative (ALT>):
  [NR_SYNTAX_ALTS + 1 -> NR_SYNTAX_ALTS],
    may be frequency part (FREQ), get current position (POS),
      ( is syntax members (MEMBERS),
          may be transduction part (SECONDPASS),
          ahead commit option (COMMITED),
 	  # get current position(POS) at this point would define the *end* of the alternative
          maybe insert position (MEMBERS, MEMBERS),
	  convert POS to short TEXT1,
	  convert NR_POSITIONS to TEXT2,
	  [ TEXT2 + ":" + TEXT1 -> TEXT ],
	  log position (TEXT),
          [ NR_POSITIONS + 1 -> NR_POSITIONS ],
          [ FREQ MEMBERS nil COMMITED SECONDPASS -> ALT ];
        may be transduction part (SECONDPASS),
          ahead commit option (COMMITED),
          [ FREQ empty COMMITED SECONDPASS -> ALT ]
      ).

FUNCTION maybe insert position (>MEMBERS1, MEMBERS2>):
  if profile option,
     [ position NR_POSITIONS -> MEMBER1 ],
     [ MEMBER1 MEMBERS1 -> MEMBERS2 ];
  [ MEMBERS1 -> MEMBERS2 ].

ACTION may be frequency part (FREQ>):
  next token is number (INT),
    is token (left square bracket),
    should be number (INT),
    should be token (right square bracket),
    [ INT -> FREQ ];
  [ 1 -> FREQ ].

FUNCTION ahead commit option (COMMITED>):
  curr token is (commit), [ true -> COMMITED ];
  [ false -> COMMITED ].

FUNCTION get number of positions (INT>):
  [ NR_POSITIONS -> INT ].

#
# syntax part:
#   member list;
#   member list, comma token, final group;
#   success operator option;
#   free order part.
#
# TODO: free word order operator (ampersand)
# Mixing free word order and sequential order in alts is not allowed!
#
PRED is syntax members (MEMBERS>):
  is syntax rule final group (MEMBER),
    [MEMBER nil -> MEMBERS];
  is syntax member (MEMBER),
    may be rest syntax members (MEMBER, MEMBERS).

ACTION may be rest syntax members (>MEMBER, MEMBERS>):
  is token (comma),
    should be syntax members (MEMBERS1),
    [MEMBER MEMBERS1 -> MEMBERS];
  is token (ampersand),
    get current position (POS),
    warning ("the free order operator has not yet been implemented correctly", POS),
    should be free word order alternative (MEMBERS1),
    [MEMBER MEMBERS1 -> MEMBERS2],
    [free word order MEMBERS2 -> MEMBER3],
    [MEMBER3 nil -> MEMBERS];
  [MEMBER nil -> MEMBERS].

ACTION should be free word order alternative (MEMBERS>):
  is syntax member (MEMBER),
    (is token (ampersand),
       should be free word order alternative (MEMBERS1),
       [MEMBER MEMBERS1 -> MEMBERS];
     [MEMBER nil -> MEMBERS]);
  syntax error ("member expected"),
    !.

ACTION should be syntax members (MEMBERS>):
  is syntax members (MEMBERS);
  syntax error ("member expected"),
    [nil -> MEMBERS].

#
# member:
#    call;
#    option;
#    guard;
#    terminal symbol.
#
# terminal symbol:
#    lexicon terminal;
#    syntax terminal;
#    quasi terminal.
#
# lexicon terminal:
#    call.
#
# syntax terminal:
#    quoted string.
#
PRED is syntax member (MEMBER>):
  is syntax rule guard (MEMBER);
  is syntax rule terminal (MEMBER);
  is syntax rule call (MEMBER);
  is syntax rule option (MEMBER);
  is syntax rule quasi terminal (MEMBER).

#
# guard:
#    left curly bracket, restriction list, right curly bracket.
#
# restriction:
#    affix variable, double colon, affix expression.
#
PRED is syntax rule guard (MEMBER>):
  is token (left curly bracket),
    should be guards (GUARDS),
    [ guard GUARDS -> MEMBER ],
    should be token (right curly bracket).

ACTION should be guards (GUARDS>):
  is guards (GUARDS);
  syntax error ("guard expected"),
    [ nil -> GUARDS ].

PRED is guards (GUARDS>):
  is guard (GUARD),
    may be rest guards (GUARDS1),
    [ GUARD GUARDS1 -> GUARDS ].

ACTION may be rest guards (GUARDS>):
  is token (comma),
    should be guards (GUARDS);
  [ nil -> GUARDS ].

PRED is guard (GUARD>):
  get current position (POS),
    is affix variable (EXPR1),
      ( is token (double colon),
          should be actual parameter (EXPR2),
          join guard domains (EXPR1, EXPR2),
          [ restrict EXPR1 to EXPR2 at POS -> GUARD ];
        syntax error ("guard operator expected"),
          make erroneous (GUARD)
      ).

FUNCTION make erroneous (GUARD>):
  [ erroneous -> GUARD ].

#
# Make sure that domains from set restrict are included in same domain.
# stn: FIXME, the above is false! expr2 can be from another domain than expr1!
# MS: Dit is sowieso fout: de parser moet zich niet met semantiek bemoeien
#
ACTION join guard domains (>EXPR1, >EXPR2):
  [ EXPR2 -> set ID2 ],
    ( [ EXPR1 -> affix ID1 ],
         join guard domains (ID1, ID2);
      [ EXPR1 -> variable ID1 instance INT ],
         join guard domains (ID1, ID2);
      ?
    );
  +.

ACTION join guard domains (>ID1, >ID2):
  add unique (ID1 nil|MALTS, ID2),
    create anonymous affix rule (MALTS, ID);
  ?.

#
# affix variable:
#    affix nonterminal, index option.
#
ACTION may be affix variable pack (EXPR>):
  is affix variable pack (EXPR);
  [ absent -> EXPR ].

PRED is affix variable pack (EXPR>):
  is token (left parenthesis),
    should be affix variable (EXPR),
    should be token (right parenthesis).

ACTION should be affix variable (EXPR>):
  is affix variable (EXPR);
  syntax error ("variable expected"),
    make erroneous (EXPR).

PRED is affix variable (EXPR>):
  get current position (POS),
    is affix nonterminal instance (ID, INT),
      try mark id as unknown affix nonterminal (ID, POS),
        ( [ INT -> -1 ], [ affix ID -> EXPR ];
          [ variable ID instance INT -> EXPR ]
        ).

# Misschien dat het laatste alternatief nooit meer getriggerd kan worden
ACTION try mark id as unknown affix nonterminal (>ID, >POS):
  get text affix (ID2), [ ID = ID2 ];
  get integer affix (ID2), [ ID = ID2 ];
  mark id as unknown affix nonterminal (ID);
  report invalid affix variable (ID, POS).

ACTION report invalid affix variable (>ID, >POS):
  convert id to name (ID, TEXT),
    error ("affix nonterminal \"" + TEXT + "\" is not a variable", POS).

#
# syntax terminal:
#   text denotation, actual parameter pack option.
#
# Note: the quoted string token is parsed as a word form to
#       prepare it for hyphen conventions
#
PRED is syntax rule terminal (MEMBER>):
  get current position (POS),
    is word form (ID),
      mark id as syntax terminal (ID),
      check not empty terminal (ID, POS),
      create system affix (EXPR1),
      may be affix variable pack (EXPR2),
      [ terminal ID using EXPR1 targeting EXPR2 at POS -> MEMBER ].

ACTION create system affix (EXPR>):
  create anonymous text affix (ID),
    [ system affix ID -> EXPR ].

ACTION check not empty terminal (>ID, >POS):
  convert id to name (ID, TEXT),
    ( [TEXT -> "\"\""],
        error ("empty terminal found", POS);
      +
    ).

#
# call:
#    syntax nonterminal, actual parameter list pack option.
#
PRED is syntax rule call (MEMBER>):
  get current position (POS),
    is identifier (ID),
      may be actual parameter list pack (ACTUALS, ARITY),
      mark id as syntax nonterminal (ID, ARITY, NONTERMINAL),
      [ call ID def NONTERMINAL with ACTUALS at POS -> MEMBER ],
      enter actual specification (NONTERMINAL, ACTUALS).

ACTION may be actual parameter list pack (ACTUALS>, ARITY>):
  is actual parameter list pack (ACTUALS, ARITY);
  [ arity 0 -> ARITY ], [ nil -> ACTUALS ].

PRED is actual parameter list pack (ACTUALS>, ARITY>):
  is token (left parenthesis),
    should be actual parameter list (ACTUALS, ARITY),
    should be token (right parenthesis).

ACTION should be actual parameter list (ACTUALS>, ARITY>):
  should be actual parameter list (EXPRS, INT),
    [ EXPRS -> ACTUALS ], [ arity INT -> ARITY ].

ACTION should be actual parameter list (EXPRS>, INT>):
  should be actual parameter (EXPR),
    may be rest actual parameter list (EXPRS1, INT1),
    [ EXPR EXPRS1 -> EXPRS ], [ INT1 + 1 -> INT ].

ACTION may be rest actual parameter list (EXPRS>, INT>):
  is token (comma),
    should be actual parameter list (EXPRS, INT);
  [ nil -> EXPRS ], [ 0 -> INT ].

#
# actual parameter:
#    affix expression.
#
ACTION should be actual parameter (EXPR>):
  is actual parameter (EXPR);
  syntax error ("actual parameter expected"),
    make erroneous (EXPR).

PRED is actual parameter (EXPR>):
  is affix expression (EXPR).

#
# final group:
#   left parenthesis, alternatives, right parenthesis.
#
PRED is syntax rule final group (MEMBER>):
  get current position (POS),
    is token (left parenthesis),
    [ NR_GROUPS + 1 -> NR_GROUPS ],
    should be syntax alternatives (ALTS),
    should be token (right parenthesis),
    create dummy nonterminal (NONTERMINAL),
    mark NONTERMINAL as anonymous group,
    enter syntax rule (NONTERMINAL, unknown, nil, ALTS, POS),
    [ final group ALTS at POS with NONTERMINAL -> MEMBER ].

#
# option:
#    left square bracket, alternatives, right square bracket
#
PRED is syntax rule option (MEMBER>):
  get current position (POS),
    is token (left square bracket),
    [ NR_OPTIONS + 1 -> NR_OPTIONS ],
    should be syntax alternatives (ALTS),
    should be token (right square bracket),
    empty (OPT_SPEC),
    [ option ALTS with OPT_SPEC at POS -> MEMBER ].

#
# The intention is to have a rule like
# quasi terminal:
#    special name, actual parameter list pack option.
#
# where the special names and their intended translation is taken
# from a special prelude, like EAG (stddefs.eag)
#
# Currently we have:
# quasi terminals:
#   panelty;	(with optional parameter)
#   match;	(with zero, one or two parameters)
#   skip;	(with zero, one or two parameters)
#   other;	(with zero or one parameter)
#   pos;	(with optional parameter)
#
PRED is syntax rule quasi terminal (MEMBER>):
  get current position (POS),
    is special name (TEXT),
      ( [ TEXT -> "PENALTY" ], may be rest penalty (MEMBER);
	[ TEXT -> "MATCH" ], may be rest match (POS, MEMBER);
	[ TEXT -> "SKIP" ],  may be rest skip (POS, MEMBER);
	[ TEXT -> "OTHER" ], may be rest other (POS, MEMBER);
	[ TEXT -> "POS" ], may be rest input pos (POS, MEMBER);
	error ("Unrecognizable special name $" + TEXT),
	  make erroneous (MEMBER)
      ).

ACTION may be rest penalty (MEMBER>):
  may be penalty pack (INT), [ penalty INT -> MEMBER ].

ACTION may be penalty pack (INT>):
  is token (left parenthesis),
    get current position (POS),
    should be number (INT),
    should be token (right parenthesis);
  [ 1 -> INT ].

ACTION may be rest skip (>POS, MEMBER>):
  may be regexp pack (ID, EXPR1, EXPR2),
    mark id as regexp skip (ID),
    [ skip regexp ID using EXPR1 targeting EXPR2 at POS -> MEMBER ].

ACTION may be rest match (>POS, MEMBER>):
  may be regexp pack (ID, EXPR1, EXPR2),
    mark id as regexp match (ID),
    [ match regexp ID using EXPR1 targeting EXPR2 at POS -> MEMBER ].

ACTION may be regexp pack (ID>, EXPR1>, EXPR2>):
  create system affix (EXPR1),
    ( is token (left parenthesis), 
        should be regexp parameters (ID, EXPR2),
        should be token (right parenthesis);
      get default regexp (ID),
        [ absent -> EXPR2 ]
    ).

ACTION should be regexp parameters (ID>, EXPR>):
  is regexp (ID),
    ( is token (comma), should be affix variable (EXPR);
      [ absent -> EXPR ]
    );
  syntax error ("regular expression expected"),
    get default regexp (ID),
    [ absent -> EXPR ].

PRED is regexp (ID>):
  get current position (POS),
    is quoted string (ID),
      convert id to name (ID, TEXT),
      strip quotes (TEXT),
        ( validate regexp (TEXT, INT);
          error ("invalid regular expression \"" + TEXT + "\"", POS)
	).

EXTERNAL TEST validate regexp (>TEXT, INT>):400

ACTION may be rest other (>POS, MEMBER>):
  create system affix (EXPR1),
    may be other pack (EXPR2),
    [ other using EXPR1 targeting EXPR2 at POS -> MEMBER ].

ACTION may be other pack (EXPR>):
  is token (left parenthesis),
    should be affix variable (EXPR),
    should be token (right parenthesis);
  [ absent -> EXPR ].

ACTION may be rest input pos (>POS, MEMBER>):
  may be affix variable pack (EXPR2),
    create system affix (EXPR1),
    [ input pos using EXPR1 targeting EXPR2 at POS -> MEMBER ].

FUNCTION make erroneous (MEMBER>):
  [ erroneous -> MEMBER ].

#
# Transduction
#
# transduction part:
#    transduction member list option.
#
ACTION may be transduction part (SECONDPASS>):
  is token (slash),
    may be transduction member list (TREMBERS),
    [ user defined TREMBERS -> SECONDPASS ];
  [ standard -> SECONDPASS ].

ACTION may be transduction member list (TREMBERS>):
  create new transduction (TREMBERS),
    ( is transduction member list (TREMBERS);
      +
    ).

PRED is transduction member list (>TREMBERS>):
  is transduction member (TREMBER),
    ( is token (comma),
        should be transduction member list (TREMBERS),
        [ TREMBER TREMBERS -> TREMBERS ];
      [ TREMBER TREMBERS -> TREMBERS ]
    ).

ACTION should be transduction member list (>TREMBERS>):
  is transduction member list (TREMBERS);
  syntax error ("transduction member expected").

ACTION should be transduction member (TREMBER>):
  is transduction member (TREMBER);
  syntax error ("transduction member expected"),
    make erroneous (TREMBER).

#
# transduction member:
#    nonterminal placeholder;
#    special name placeholder;
#    affix placeholder;
#    group placeholder;
#    option placeholder;
#    text denotation.
#
# Identification of transduction members should be checked later.
# This is an error
#
PRED is transduction member (TREMBER>):
  get current position (POS),
    ( is transduction terminal (POS, TREMBER);
      is nonterminal or affix placeholder (POS, TREMBER);
      is group or option placeholder (POS, TREMBER);
      is special name placeholder (POS, TREMBER)
    ).

PRED is transduction terminal (>POS, TREMBER>):
  is quoted string (ID),
    mark id as text constant (ID),
    check not empty terminal (ID, POS),
    [ text const ID -> TREMBER ].

PRED is nonterminal or affix placeholder (>POS, TREMBER>): 
  is identifier (ID),
    ( is dot number (INT),
        id is affix or nonterminal (ID, POS, INT, TREMBER);
      id is affix or nonterminal (ID, POS, -1, TREMBER)
    ).

FUNCTION id is affix or nonterminal (>ID, >POS, >INT, TREMBER>):
  id is user variable (ID), build affix trember (ID, POS, INT, TREMBER);
  id is affix nonterminal (ID), build affix trember (ID, POS, INT, TREMBER);
  id is anonymous affix (ID), build affix trember (ID, POS, INT, TREMBER);
  id is lexicon affix (ID), build affix trember (ID, POS, INT, TREMBER);
  id is lexicon affix terminal (ID), build affix trember (ID, POS, INT, TREMBER);
  id is set affix (ID), build affix trember (ID, POS, INT, TREMBER);
  build nonterminal trember (ID, INT, POS, TREMBER).

FUNCTION build nonterminal trember (>ID, >INT, >POS, TREMBER>):
  [ INT -> -1 ],
    [ nonterminal ID at POS -> TREMBER ];
  [ nonterminal ID instance INT at POS -> TREMBER ].

FUNCTION build affix trember (>ID, >POS, >INT, TREMBER>):
  [INT -> -1],
    [affix ID -> EXPR],
    [affix EXPR at POS -> TREMBER];
  [variable ID instance INT -> EXPR],
    [affix EXPR at POS -> TREMBER].

PRED is group or option placeholder (>POS, TREMBER>):
  is token (left parenthesis),
    should be token (right parenthesis),
    [final group -> TREMBER];
  is token (left square bracket),
    should be token (right square bracket),
      ( is dot number(INT),
          [ option INT at POS -> TREMBER ];
        [ option 1 at POS -> TREMBER ]
      ).

PRED is special name placeholder (>POS, TREMBER>):
  is special name (TEXT),
    ( [ TEXT -> "MATCH" ],
        ( is dot number (INT), [ match instance INT at POS -> TREMBER ];
          [ match at POS -> TREMBER ]
	);
      [ TEXT -> "SKIP" ],
        ( is dot number (INT), [ skip instance INT at POS -> TREMBER ];
          [ skip at POS -> TREMBER ]
	);
      [ TEXT -> "OTHER" ],
        ( is dot number (INT), [ other instance INT at POS -> TREMBER ];
          [ other at POS -> TREMBER ]
	);
      [ TEXT -> "POS" ],
	( is dot number (INT), [ input pos instance INT at POS -> TREMBER ];
	  [ input pos at POS -> TREMBER ]
	);
      error ("Special name " + TEXT + " cannot be used as a transduction placeholder"),
	make erroneous (TREMBER)
    ).

FUNCTION make erroneous (TREMBER>):
  [ empty -> TREMBER ].

#
# Affix rules
#
# affix rule rhs:
#    integer token;
#    text token;
#    affix alternatives.
#
# affix alternatives:
#    affix alternative, bar token, affix alternatives;
#    affix alternative.
#
# affix alternative:
#    affix nonterminal;
#    affix terminal.
#
ACTION should be rest affix rule (>IDS, >POS):
  verify affix nonterminals validity (IDS),
    should be affix rule rhs (IDS, POS).

ACTION verify affix nonterminals validity (>IDS):
  [ IDS -> nil ];
  [ IDS -> ID IDS2 ],
    verify affix nonterminals validity (IDS2),
    convert id to name (ID, TEXT),
      ( is valid affix nonterminal (TEXT);
	syntax error ("Illegal affix nonterminal '" + TEXT + "'")
      );
  ?.

#
# Verbouwen totdat de INT en TEXT nonterminal adequaat afgehandeld worden
# Geen twee problemen tegelijkertijd oplossen
#
ACTION should be affix rule rhs (>IDS, >POS):
  is affix nonterminal (ID),
    may be nonterminal rest affix rule rhs (ID, IDS, POS),
    should be token (period);
  is affix terminal (ID),
    may be terminal rest affix rule rhs (ID, IDS, POS),
    should be token (period);
  syntax error ("\".\" expected").

ACTION may be nonterminal rest affix rule rhs (>ID, >IDS, >POS):
  get text affix (ID2), [ ID = ID2 ], enter text affixes (IDS, POS);
  get integer affix (ID2), [ ID = ID2 ], enter integer affixes (IDS, POS);
  mark id as unknown affix nonterminal (ID),
    may be rest set alternatives (ID, IDS, POS);
  ?.

ACTION enter integer affixes (>IDS, >POS):
  [ IDS -> nil ];
  [ IDS -> ID IDS2 ],
    enter integer affixes (IDS2, POS),
    enter integer affix (ID, POS);
  ?.

ACTION enter text affixes (>IDS, >POS):
  [ IDS -> nil ];
  [ IDS -> ID IDS2 ],
    enter text affixes (IDS2, POS),
    enter text affix (ID, POS);
  ?.

ACTION may be terminal rest affix rule rhs (>ID, >IDS, >POS):
  mark id as unknown affix (ID), may be rest set alternatives (ID, IDS, POS).

ACTION may be rest set alternatives (>ID, >IDS, >POS):
  [ nil -> MALTS ],
    add unique (MALTS, ID),
      may be rest affix alternatives (MALTS),
      enter affix rules (IDS, MALTS, POS);
    ?.

ACTION enter affix rules (>IDS, >MALTS, >POS):
  [ IDS -> nil ];
  [ IDS -> ID IDS2 ],
    enter affix rules (IDS2, MALTS, POS),
    enter affix rule (ID, MALTS, POS);
  ?.

ACTION may be rest affix alternatives (>MALTS>):
  is token (bar),
    should be affix alternative (MALTS),
    may be rest affix alternatives (MALTS);
  +.

PRED is affix alternative (>MALTS>):
  get current position (POS),
    ( is affix nonterminal (ID),
        ( get text affix (ID2), [ ID = ID2 ], syntax error ("Illegal TEXT affix in rhs");
	  get integer affix (ID2), [ ID = ID2 ], syntax error ("Illegal INT affix in rhs");
          mark id as unknown affix nonterminal (ID),
	    try add unique affix alternative (MALTS, ID, POS);
          ?
        );
      is affix terminal (ID), mark id as unknown affix (ID),
	try add unique affix alternative (MALTS, ID, POS)
    ).

ACTION should be affix alternative (>MALTS>):
  is affix alternative (MALTS);
  syntax error ("affix alternative expected").

ACTION try add unique affix alternative (>MALTS>, >ID, >POS):
  add unique (MALTS, ID);
  convert id to name (ID, TEXT),
    warning ("duplicate affix alternative \"" + TEXT + "\"", POS).

#
# Logging
#
ACTION log grammar statistics:
  log ("\nParser statistics:"),

    bindec(NR_SYNTAX_RULES, TEXT),
    log ("Nr. of syntax rules: " + TEXT),

    bindec(NR_SYNTAX_ALTS, TEXT),
    log ("Nr. of syntax alternatives: " + TEXT),

    bindec(NR_GROUPS, TEXT),
    log ("Nr. of groups: " + TEXT),

    bindec(NR_OPTIONS, TEXT),
    log ("Nr. of options: " + TEXT),

    bindec(NR_COMMITS, TEXT),
    log ("Nr. of commits: " + TEXT),

    get number of lexicon nonterminals (INT),
    bindec (INT, TEXT),
    log ("Nr. of lexicon rules: " + TEXT),

    get number of syntax terminals (INT),
    bindec (INT - 1, TEXT), # do not count end-of-sentence
    log ("Nr. of terminals: " + TEXT),

    get number of regexp matches (INT),
    bindec (INT, TEXT),
    log ("Nr. of regexp matches: " + TEXT),

    get number of regexp skips (INT),
    bindec (INT, TEXT),
    log ("Nr. of regexp skips: " + TEXT),

    flush log.

#
# Lexicon
#
PRED is lexicon module:
  is lexicon module specification,
     set lexicon used,
     should be lexicon includes,
     should be token (defines symbol),
     should be lexicon defines element,
     should be rest lexicon defines elements.

PRED is lexicon module specification:
  is token (lexicon symbol).

#
# Lexicon module body:
#
SEEN_INCLUDES = BOOL.

ACTION should be rest lexicon defines elements:
  is token (period);
  is token (comma),
     should be lexicon defines element,
     should be rest lexicon defines elements;
  syntax error ("lexicon defines element expected"),
    ?. # never reached


ACTION should be lexicon defines element:
  is typed lexicon rule specification;
  is lexicon rule specification;
  is affix definition;
  syntax error ("lexicon defines element expected"),
    ?. # never reached

ACTION should be lexicon includes:
  should be lexicon module name (TEXT),
    add lexicon module (TEXT),
    may be rest lexicon includes.

ACTION may be rest lexicon includes:
  is token (comma),
    should be lexicon module name (TEXT),
    add lexicon module (TEXT),
    may be rest lexicon includes;
  +.

ACTION should be lexicon module name (TEXT>):
  is module name (TEXT);
  syntax error ("lexicon module name expected"),
    ?. # never reached

#
# Lexicon rules & affixes:
#
PRED is affix definition:
  is affix nonterminal (ID),
    should be token (double colon),
    get current position (POS),
    should be rest affix rule (ID nil, POS).

PRED is typed lexicon rule specification:
  is rule type (RULE_TYPE),
    ( is lexicon rule specification (RULE_TYPE);
      syntax error ("lexicon rule specification expected"),
       ?
    ).

PRED is lexicon rule specification:
  is lexicon rule specification (unknown).

PRED is lexicon rule specification (>RULE_TYPE):
  is identifier (ID),
    current pragmats (PRAGMATS),
    get current position (POS),
    may be formal parameter list pack (FORMALS, ARITY),
    mark id as lexicon nonterminal (ID, ARITY, NONTERMINAL),
    store pragmats (NONTERMINAL, PRAGMATS),
    enter lexicon rule (NONTERMINAL, RULE_TYPE, FORMALS, POS).
