#
#  File: transduction.k3
#  Handles transduction
#
#  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 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: transduction.k3,v 1.33 2006/08/09 11:13:09 marcs Exp $"
#
MODULE transduction.

DEFINES SECONDPASS,
        TREMBERS,
        TREMBER,
        TEST if empty (>TREMBER),
        FUNCTION create new transduction (TREMBERS>),
        FUNCTION create empty second pass (SECONDPASS>),
        FUNCTION convert >SECONDPASS to TEXT>,
        TEST get trans and tree (>SECONDPASS, TREMBERS1>, TREMBERS2>),
        TEST is user defined transduction (>SECONDPASS),
        TEST is standard transduction (>SECONDPASS),
        FUNCTION create no SECONDPASS>,
        TEST has no second pass (>SECONDPASS),

        ACTION expand final group transductions,
        ACTION expand standard transductions,

        SON,
        NR_SONS,
        FUNCTION convert >SON to INT>,
        FUNCTION convert >SON to nonterminal ID>,
        FUNCTION convert >SON to NONTERMINAL>,
        TEST if >SON is lexicon nonterminal.

USES globals, io, properties, rule, id, intermediate, textstorage.

#
# Transduction handling
#
SECONDPASS :: user defined TREMBERS;            # after parsing a user def. trans.
              standard;                         # after parsing no user def. trans.
	      trans TREMBERS and tree TREMBERS; # after expanding both the tree
                                                # and the standard transduction.
              none.                             # no second pass code (for final groups)

TREMBERS :: TREMBER TREMBERS; nil.

FUNCTION create empty second pass (SECONDPASS>):
  create new transduction (TREMBERS),
    [trans TREMBERS and tree TREMBERS -> SECONDPASS].

FUNCTION create new transduction (TREMBERS>):
  [nil -> TREMBERS].

TEST is user defined transduction (>SECONDPASS):
  [SECONDPASS -> user defined TREMBERS].

TEST is standard transduction (>SECONDPASS):
  [SECONDPASS -> standard].

TEST has no second pass (>SECONDPASS):
  [SECONDPASS -> none].

FUNCTION create no SECONDPASS>:
  [none -> SECONDPASS].

TEST get trans and tree (>SECONDPASS, TREMBERS1>, TREMBERS2>):
  [SECONDPASS -> trans TREMBERS1 and tree TREMBERS2].

FUNCTION create SECONDPASS> from >TREMBERS1 and >TREMBERS2:
  [trans TREMBERS1 and tree TREMBERS2 -> SECONDPASS].

#
# Text conversion:
#
FUNCTION convert >SECONDPASS to TEXT>:
  [SECONDPASS -> user defined TREMBERS],
    convert TREMBERS to TEXT,
    ["[* user defined: " + TEXT + " *]" -> TEXT];
  [SECONDPASS -> standard],
    ["[* standard: *]" -> TEXT];
  [SECONDPASS -> trans TREMBERS1 and tree TREMBERS2],
    convert TREMBERS1 to TEXT1,
    convert TREMBERS2 to TEXT2,
    ["[* trans: " + TEXT1 + " | tree: " + TEXT2 + " *]" -> TEXT];
  [SECONDPASS -> none],
    ["[* none *]" -> TEXT];
  ?.

FUNCTION convert >TREMBERS to TEXT>:
  [TREMBERS -> TREMBER TREMBERS],
    convert TREMBER to TEXT1,
    convert next trember to text (TREMBERS, TEXT),
    [TEXT1 + TEXT -> TEXT];
  [TREMBERS -> nil], ["" -> TEXT];
  ?.

FUNCTION convert next trember to text (>TREMBERS, TEXT>):
  [TREMBERS -> TREMBER TREMBERS],
    convert TREMBER to TEXT1,
    convert next trember to text (TREMBERS, TEXT2),
    [", " + TEXT1 + TEXT2 -> TEXT];
  [TREMBERS -> nil], ["" -> TEXT];
  ?.

#
# Transduction Members
#
TREMBER :: text const ID;
           terminal ID using EXPR;
           nonterminal ID instance INT at POS;
           nonterminal ID at POS;
           affix EXPR at POS;
           option INT at POS;
           final group;
           match instance INT at POS;
           match at POS;
           match using EXPR;
           skip instance INT at POS;
           skip at POS;
           skip using EXPR;
           other instance INT at POS;
           other at POS;
           other using EXPR;
	   input pos instance INT at POS;
	   input pos at POS;
	   input pos using EXPR;
           penalty INT;
           SON with params EXPRS;
           empty;
           erroneous.

TEST if empty (>TREMBER):
  [ TREMBER -> empty ].

#
# Text conversion:
#
FUNCTION convert >TREMBER to TEXT>:
  [ TREMBER -> text const ID ],
    convert id to name (ID, TEXT);
  [ TREMBER -> terminal ID using EXPR ],
    convert id to name (ID, TEXT1),
    convert EXPR to user TEXT2,
    [ TEXT1 + TEXT2 -> TEXT ];
  [ TREMBER -> nonterminal ID instance INT at POS ],
    convert id to name (ID, TEXT1),
    convert INT to TEXT2,
    [ TEXT1 + "." + TEXT2 -> TEXT ];
  [ TREMBER -> nonterminal ID at POS ],
    convert id to name (ID, TEXT);
  [ TREMBER -> affix EXPR at POS ],
    convert EXPR to user TEXT;
  [ TREMBER -> option INT at POS ],
    convert INT to TEXT,
    [ "[ ]." + TEXT -> TEXT];
  [ TREMBER -> final group ],
    [ "()" -> TEXT ];
  [ TREMBER -> match instance INT at POS ],
    convert INT to TEXT,
    [ "$MATCH" + TEXT -> TEXT ];
  [ TREMBER -> match at POS ],
    [ "$MATCH" -> TEXT ];
  [ TREMBER -> match using EXPR ],
    convert EXPR to user TEXT,
    [ "match using " + TEXT -> TEXT ];
  [ TREMBER -> skip instance INT at POS ],
    convert INT to TEXT,
    [ "$SKIP" + TEXT -> TEXT ];
  [ TREMBER -> skip at POS ],
    [ "$SKIP" -> TEXT ];
  [ TREMBER -> skip using EXPR ],
    convert EXPR to user TEXT,
    [ "skip using " + TEXT -> TEXT ];
  [ TREMBER -> other instance INT at POS ],
    convert INT to TEXT,
    [ "$OTHER" + TEXT -> TEXT ];
  [ TREMBER -> other at POS ],
    [ "$OTHER" -> TEXT ];
  [ TREMBER -> other using EXPR ],
    convert EXPR to user TEXT,
    [ "other using " + TEXT -> TEXT ];
  [ TREMBER -> input pos instance INT at POS ],
    convert INT to TEXT,
    [ "$POS" + TEXT -> TEXT ];
  [ TREMBER -> input pos at POS ],
    [ "$POS" -> TEXT ];
  [ TREMBER -> input pos using EXPR ],
    convert EXPR to user TEXT,
    [ "input pos using " + TEXT -> TEXT ];
  [ TREMBER -> penalty INT ],
    convert INT to TEXT,
    [ "$PENALTY(" + TEXT + ")" -> TEXT ];
  [ TREMBER -> SON with params EXPRS ],
    convert SON to TEXT;
  [ TREMBER -> empty ],
    [ "<empty>" -> TEXT ];
  [ TREMBER -> erroneous ],
    [ "erroneous" -> TEXT ];
  ?.


#####################################
#                                   #
#  Standard transduction expansion  #
#                                   #
#####################################

NUMBERED_ITEMS :: INT nonterminals INT matches INT skips INT others INT input positions.

FUNCTION create new numbered items (NUMBERED_ITEMS>):
  [0 nonterminals 0 matches 0 skips 0 others 0 input positions -> NUMBERED_ITEMS].

FUNCTION incr nonterminals (>NUMBERED_ITEMS>, INT1>):
  [NUMBERED_ITEMS -> INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions],
    [INT1 + 1 -> INT1],
    [INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions -> NUMBERED_ITEMS].

FUNCTION incr matches (>NUMBERED_ITEMS>, INT2>):
  [NUMBERED_ITEMS -> INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions],
    [INT2 + 1 -> INT2],
    [INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions -> NUMBERED_ITEMS].

FUNCTION incr skips (>NUMBERED_ITEMS>, INT3>):
  [NUMBERED_ITEMS -> INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions],
    [INT3 + 1 -> INT3],
    [INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions -> NUMBERED_ITEMS].

FUNCTION incr others (>NUMBERED_ITEMS>, INT4>):
  [NUMBERED_ITEMS -> INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions],
    [INT4 + 1 -> INT4],
    [INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions -> NUMBERED_ITEMS].

FUNCTION incr input positions (>NUMBERED_ITEMS>, INT5>):
  [NUMBERED_ITEMS -> INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions],
    [INT5 + 1 -> INT5],
    [INT1 nonterminals INT2 matches INT3 skips INT4 others INT5 input positions -> NUMBERED_ITEMS].

#
# expansion of the second pass part for those rules
# that do not contain a user defined transduction.
#
ACTION expand standard transductions:
  get number of syntax nonterminals (INT),
    expand standard transductions (INT).

ACTION expand standard transductions (>INT):
  [ INT -> 0 ];
  [ INT - 1 -> INT ],
    expand standard transductions in rule (INT),
    expand standard transductions (INT).

ACTION expand standard transductions in rule (>INT):
  convert INT to NONTERMINAL,
    ( is start nonterminal (NONTERMINAL);
      is lexicon nonterminal (NONTERMINAL);
      is anonymous group NONTERMINAL;
      expand standard transductions (NONTERMINAL)
    ).

ACTION expand standard transductions (>NONTERMINAL):
  get definitions (NONTERMINAL, DEFS),
    expand standard transductions (DEFS),
    store definitions (NONTERMINAL, DEFS).

FUNCTION expand standard transductions (>DEFS>):
  [ DEFS -> DEF DEFS ],
    expand standard transductions (DEF),
    expand standard transductions (DEFS),
    [ DEF DEFS -> DEFS ];
  [ DEFS -> nil ];
  ?.

FUNCTION expand standard transductions (>DEF>):
  [ DEF -> FORMALS at POS with ALTS ],
    create new numbered items (NUMBERED_ITEMS),
    expand standard transduction (ALTS, NUMBERED_ITEMS),
    [ FORMALS at POS with ALTS -> DEF ].

FUNCTION expand standard transduction (>ALTS>, >NUMBERED_ITEMS):
  [ ALTS -> ALT ALTS ],
    expand standard transduction (ALT, NUMBERED_ITEMS),
    expand standard transduction (ALTS, NUMBERED_ITEMS),
    [ ALT ALTS -> ALTS ];
  [ ALTS -> nil ];
  ?.

FUNCTION expand standard transduction (>ALT>, >NUMBERED_ITEMS):
  [ ALT -> FREQ MEMBERS LOCALS COMMITED SECONDPASS ],
    expand standard transduction (MEMBERS, SECONDPASS, NUMBERED_ITEMS),
    [ FREQ MEMBERS LOCALS COMMITED SECONDPASS -> ALT ];
  [ ALT -> FREQ empty COMMITED SECONDPASS],
    expand standard transduction (nil, SECONDPASS, NUMBERED_ITEMS),
    [ FREQ empty COMMITED SECONDPASS -> ALT ];
  ?.

FUNCTION expand standard transduction (>MEMBERS>, >SECONDPASS>, >NUMBERED_ITEMS>):
  [ SECONDPASS -> user defined TREMBERS1 ],
    expand standard transduction (MEMBERS, TREMBERS2, NUMBERED_ITEMS),
    [ trans TREMBERS1 and tree TREMBERS2 -> SECONDPASS ];
  [ SECONDPASS -> standard ],
    expand standard transduction (MEMBERS, TREMBERS, NUMBERED_ITEMS),
    [ trans TREMBERS and tree TREMBERS -> SECONDPASS ];
  [ SECONDPASS -> none ];
  [ SECONDPASS -> trans TREMBERS1 and tree TREMBERS2 ]; # already expanded
  ?.

FUNCTION expand standard transduction (>MEMBERS>, TREMBERS>, >NUMBERED_ITEMS>):
  where member is free word order alternative (MEMBERS, MEMBERS1),
    expand standard transduction for members (MEMBERS1, TREMBERS, NUMBERED_ITEMS),
    make free word order member (MEMBERS1, MEMBERS);
  create new transduction (TREMBERS),
    expand standard transduction for members (MEMBERS, TREMBERS, NUMBERED_ITEMS).

FUNCTION expand standard transduction for members (>MEMBERS>, TREMBERS>, >NUMBERED_ITEMS>):
  [ MEMBERS -> nil ], [ nil -> TREMBERS ];
  [ MEMBERS -> MEMBER MEMBERS ],
    expand standard transduction (MEMBER, TREMBER, NUMBERED_ITEMS),
    expand standard transduction for members (MEMBERS, TREMBERS, NUMBERED_ITEMS),
    [ TREMBER TREMBERS -> TREMBERS ],
    [ MEMBER MEMBERS -> MEMBERS ];
  ?.

FUNCTION expand standard transduction (>MEMBER>, TREMBER>, >NUMBERED_ITEMS>):
  [ MEMBER -> terminal ID using EXPR1 targeting EXPR2 at POS],
    [ terminal ID using EXPR1 -> TREMBER ];
  [ MEMBER -> skip regexp ID using EXPR1 targeting EXPR2 at POS ],
    incr skips (NUMBERED_ITEMS, INT),
    [ skip instance INT at POS -> TREMBER ];
  [ MEMBER -> match regexp ID using EXPR1 targeting EXPR2 at POS ],
    incr matches (NUMBERED_ITEMS, INT),
    [ match instance INT at POS -> TREMBER ];
  [ MEMBER -> other using EXPR1 targeting EXPR2 at POS ],
    incr others (NUMBERED_ITEMS, INT),
    [ other instance INT at POS -> TREMBER ];
  [ MEMBER -> input pos using EXPR1 targeting EXPR2 at POS ],
    incr input positions (NUMBERED_ITEMS, INT),
    [ input pos instance INT at POS -> TREMBER ];
  [ MEMBER -> call ID def NONTERMINAL with ACTUALS at POS ],
    incr nonterminals (NUMBERED_ITEMS, INT),
    [ NONTERMINAL son INT -> SON ],
    [ ACTUALS -> EXPRS ],
    [ SON with params EXPRS -> TREMBER ];
  [ MEMBER -> option ALTS with OPT_SPEC at POS ],
    incr nonterminals (NUMBERED_ITEMS, INT),
    convert OPT_SPEC to FORMALS,
    [ FORMALS -> EXPRS ],
    [ anonymous son INT -> SON ],
    [ SON with params EXPRS -> TREMBER ],
    create new numbered items (NUMBERED_ITEMS1),
    expand standard transduction (ALTS, NUMBERED_ITEMS1),
    [ option ALTS with OPT_SPEC at POS -> MEMBER ];
  [ MEMBER -> final group ALTS at POS with NONTERMINAL ],
    expand standard transduction (ALTS, NUMBERED_ITEMS),
    [ final group ALTS at POS with NONTERMINAL -> MEMBER ],
    [ final group -> TREMBER ];
  [ MEMBER -> guard GUARDS ], [ empty -> TREMBER ];
  [ MEMBER -> penalty INT ],  [ penalty INT -> TREMBER ];
  [ MEMBER -> position INT ], [ empty -> TREMBER ];
  [ MEMBER -> free word order MEMBERS ],
    write ("Free word order transduction not (yet) supported\n"),
    ?;
  write ("Unknown member kind to transduce\n"),
  ?.


#
# Final group transduction expansion
#
ACTION expand final group transductions:
  get number of syntax nonterminals (INT),
# dump ast,
    expand final group transductions (INT).
# dump ast.

ACTION expand final group transductions (>INT):
  [INT -> 0];
  [INT - 1 -> INT],
    expand final group transductions in rule (INT),
    expand final group transductions (INT).

ACTION expand final group transductions in rule (>INT):
  convert INT to NONTERMINAL,
    (is start nonterminal (NONTERMINAL);
     is lexicon nonterminal (NONTERMINAL);
     is anonymous group NONTERMINAL;
     expand final group transductions (NONTERMINAL)).

ACTION expand final group transductions (>NONTERMINAL):
  get definitions (NONTERMINAL, DEFS),
    expand final group transductions (DEFS),
    store definitions (NONTERMINAL, DEFS).

ACTION expand final group transductions (>DEFS>):
  [DEFS -> DEF DEFS],
    expand final group transductions (DEF),
    expand final group transductions (DEFS),
    [DEF DEFS -> DEFS];
  [DEFS -> nil];
  ?.

ACTION expand final group transductions (>DEF>):
  [DEF -> FORMALS at POS with ALTS],
    expand final group transductions (ALTS),
    substitute nonterminals with sons (ALTS),
    [FORMALS at POS with ALTS -> DEF].

FUNCTION expand final group transductions (>ALTS>):
  [ALTS -> nil];
  [ALTS -> ALT ALTS],
    expand final group transduction (ALT),
    expand final group transductions (ALTS),
    [ALT ALTS -> ALTS];
  ?.

FUNCTION expand final group transduction (>ALT>):
  [ALT -> FREQ MEMBERS LOCALS COMMITED SECONDPASS],
    (has no second pass (SECONDPASS);
     expand final group transductions (MEMBERS, SECONDPASS),
       [FREQ MEMBERS LOCALS COMMITED SECONDPASS -> ALT]);
  [ALT -> FREQ empty COMMITED SECONDPASS];
  ?.

FUNCTION expand final group transductions (>MEMBERS>, >SECONDPASS>):
  [MEMBERS -> MEMBER MEMBERS],
    ([MEMBER -> final group ALTS at POS with NONTERMINAL],
        substitute final group transduction (ALTS, SECONDPASS),
        expand final group transductions (ALTS),
        [final group ALTS at POS with NONTERMINAL -> MEMBER],
        [MEMBER nil -> MEMBERS],
        [none -> SECONDPASS];
     expand final group transductions (MEMBERS, SECONDPASS),
       [MEMBER MEMBERS -> MEMBERS]);
  [MEMBERS -> nil];
  ?.

FUNCTION substitute final group transduction (>ALTS>, >SECONDPASS):
  [ALTS -> nil];
  [ALTS -> ALT ALTS],
    substitute final group transduction (ALT, SECONDPASS),
    substitute final group transduction (ALTS, SECONDPASS),
    [ALT ALTS -> ALTS];
  ?.

FUNCTION substitute final group transduction (>ALT>, >SECONDPASS1):
  [ALT -> FREQ MEMBERS LOCALS COMMITED SECONDPASS2],
    substitute final group in transduction (SECONDPASS2, SECONDPASS1, SECONDPASS3),
    [FREQ MEMBERS LOCALS COMMITED SECONDPASS3 -> ALT];
  [ALT-> FREQ empty COMMITED SECONDPASS2],
    substitute final group in transduction (SECONDPASS2, SECONDPASS1, SECONDPASS3),
    [FREQ empty COMMITED SECONDPASS3 -> ALT];
  ?.

#
# substitute final group in SECONDPASS2 for SECONDPASS1 into SECONDPASS3
#
FUNCTION substitute final group in transduction (>SECONDPASS1, >SECONDPASS2, SECONDPASS3>):
  [SECONDPASS1 -> trans TREMBERS1 and tree TREMBERS2],
    ([SECONDPASS2 -> trans TREMBERS3 and tree TREMBERS4],
       substitute final group in trembers (TREMBERS1, TREMBERS3, TREMBERS5),
       substitute final group in trembers (TREMBERS2, TREMBERS4, TREMBERS6),
       [trans TREMBERS5 and tree TREMBERS6 -> SECONDPASS3];
     [SECONDPASS2 -> standard], ?;
     [SECONDPASS2 -> user defined TREMBERS], ?;
     [SECONDPASS2 -> none], ?;
     ?);
  ?.

#
# substitute final group in TREMBERS2 for TREMBERS1 into TREMBERS3
#
FUNCTION substitute final group in trembers (>TREMBERS1, >TREMBERS2, TREMBERS3>):
  [TREMBERS2 -> TREMBER2 TREMBERS2],
    ([TREMBER2 -> final group],
       substitute final group in trembers (TREMBERS1, TREMBERS2, TREMBERS3),
       append TREMBERS3 to TREMBERS1 into TREMBERS3;
     substitute final group in trembers (TREMBERS1, TREMBERS2, TREMBERS3),
       [TREMBER2 TREMBERS3 -> TREMBERS3]);
  [TREMBERS2 -> nil],
    [nil -> TREMBERS3];
  ?.

FUNCTION append >TREMBERS1 to >TREMBERS2 into TREMBERS3>:
  [TREMBERS2 -> nil],
    [TREMBERS1 -> TREMBERS3];
  [TREMBERS2 -> TREMBER2 TREMBERS2],
    append TREMBERS1 to TREMBERS2 into TREMBERS3,
    [TREMBER2 TREMBERS3 -> TREMBERS3];
  ?.


#
# Nonterminal with son substitution 
#
# A son is a member generating a procedure call in the generated code.
# The number is calculated before generating the transduction, because the
# transduction action want the son number where the last son is nr. 1.
# This is because of the way the sons are linked in the runtime system.
#
SON :: NONTERMINAL son INT;
       anonymous son INT.

FUNCTION convert >SON to TEXT>:
  [ SON -> NONTERMINAL son INT ],
    convert NONTERMINAL to TEXT1,
    convert INT to TEXT2,
    [ TEXT1 + " son " + TEXT2 -> TEXT ];
  [SON -> anonymous son INT],
    convert INT to TEXT,
    [ "anonymous son " + TEXT -> TEXT ];
  ?.

FUNCTION convert >SON to INT>:
  [ SON -> NONTERMINAL son INT ];
  [ SON -> anonymous son INT ];
  ?.

FUNCTION convert >SON to nonterminal ID>:
  [ SON -> NONTERMINAL son INT ],
    convert NONTERMINAL to ID and ARITY;
  [ SON -> anonymous son INT ],
    ?;
  ?.

FUNCTION convert >SON to NONTERMINAL>:
  [ SON -> NONTERMINAL son INT ];
  [ SON -> anonymous son INT ],
    ?;
  ?.

TEST if >SON is lexicon nonterminal:
  [ SON -> NONTERMINAL son INT ],
    is lexicon nonterminal (NONTERMINAL).

#
# The real stuff:
#
ACTION substitute nonterminals with sons (>ALTS>):
  [ALTS -> nil];
  [ALTS -> ALT ALTS],
    substitute nonterminals with sons (ALT),
    substitute nonterminals with sons (ALTS),
    [ALT ALTS -> ALTS];
  ?.

ACTION substitute nonterminals with sons (>ALT>):
  [ALT -> FREQ MEMBERS LOCALS1 COMMITED SECONDPASS],
    substitute nonterminals with sons (SECONDPASS, MEMBERS),
    [FREQ MEMBERS LOCALS1 COMMITED SECONDPASS -> ALT];
  [ALT -> FREQ empty COMMITED SECONDPASS],
    substitute nonterminals with sons (SECONDPASS, nil|MEMBERS),
    [FREQ empty COMMITED SECONDPASS -> ALT];
  ?.

ACTION substitute nonterminals with sons (>SECONDPASS>, >MEMBERS>):
  get trans and tree (SECONDPASS, TREMBERS1, TREMBERS2),
    substitute nonterminals with sons (TREMBERS1, MEMBERS),
    substitute nonterminals with sons (TREMBERS2, MEMBERS),
    create SECONDPASS from TREMBERS1 and TREMBERS2;
  has no second pass (SECONDPASS),
    # final groups, so build members path and prepare second passes seperately
    substitute nonterminals with sons (MEMBERS, nil);
  +.

ACTION substitute nonterminals with sons (>MEMBERS1>, >MEMBERS2):
  [MEMBERS1 -> nil];
  [MEMBERS1 -> MEMBER1 MEMBERS1],
    ([MEMBER1 -> final group ALTS at POS with NONTERMINAL],
       substitute nonterminals with sons (ALTS, MEMBERS2),
       [final group ALTS at POS with NONTERMINAL -> MEMBER1],
       [MEMBER1 nil -> MEMBERS1];
     [MEMBER1 -> option ALTS with OPT_SPEC at POS],
       substitute nonterminals with sons (ALTS),
       [option ALTS with OPT_SPEC at POS -> MEMBER1],
       substitute nonterminals with sons (MEMBERS1, MEMBERS2),
       [MEMBER1 MEMBERS1 -> MEMBERS1];
     append MEMBER1 to MEMBERS2,
       substitute nonterminals with sons (MEMBERS1, MEMBERS2),
       [MEMBER1 MEMBERS1 -> MEMBERS1]);
  ?.

ACTION substitute nonterminals with sons (>ALTS>, >MEMBERS):
  [ALTS -> ALT ALTS],
    substitute nonterminals with sons (ALT, MEMBERS),
    substitute nonterminals with sons (ALTS, MEMBERS),
    [ALT ALTS -> ALTS];
  [ALTS -> nil];
  ?.

ACTION substitute nonterminals with sons (>ALT>, >MEMBERS):
  [ALT -> FREQ MEMBERS1 LOCALS1 COMMITED SECONDPASS],
    substitute nonterminals with sons (SECONDPASS, MEMBERS, MEMBERS1),
    [FREQ MEMBERS1 LOCALS1 COMMITED SECONDPASS -> ALT];
  [ALT -> FREQ empty COMMITED SECONDPASS],
    substitute nonterminals with sons (SECONDPASS, MEMBERS),
    [FREQ empty COMMITED SECONDPASS -> ALT];
  ?.

ACTION substitute nonterminals with sons (>SECONDPASS>, >MEMBERS1, >MEMBERS2>):
  get trans and tree (SECONDPASS, TREMBERS1, TREMBERS2),
    [MEMBERS1 -> MEMBERS3],
    append MEMBERS2 to MEMBERS3,
    substitute nonterminals with sons (TREMBERS1, MEMBERS3),
    substitute nonterminals with sons (TREMBERS2, MEMBERS3),
    create SECONDPASS from TREMBERS1 and TREMBERS2;
  has no second pass (SECONDPASS),
    # final groups, so build members path and prepare second passes seperately
    substitute nonterminals with sons (MEMBERS2, MEMBERS1);
  +.

ACTION substitute nonterminals with sons (>TREMBERS>, >MEMBERS):
  [ TREMBERS -> TREMBER TREMBERS ],
    substitute nonterminals with sons (TREMBER, MEMBERS),
    substitute nonterminals with sons (TREMBERS, MEMBERS),
    [ TREMBER TREMBERS -> TREMBERS ];
  [ TREMBERS -> nil ];
  ?.

ACTION substitute nonterminals with sons (>TREMBER>, >MEMBERS):
  [ TREMBER -> text const ID ];
  [ TREMBER -> terminal ID using EXPR ];
  [ TREMBER -> nonterminal ID instance INT at POS ],
    convert nonterminal ID instance INT to TREMBER using MEMBERS at POS;
  [ TREMBER -> nonterminal ID at POS ],
    convert nonterminal ID instance 1 to TREMBER using MEMBERS at POS;
  [ TREMBER -> SON with params EXPRS ];
  [ TREMBER -> affix EXPR at POS ];
  [ TREMBER -> option INT at POS ],
    convert option INT to TREMBER using MEMBERS at POS;
  [ TREMBER -> final group ], ?;
  [ TREMBER -> match at POS ],
    convert match at POS to TREMBER using MEMBERS;
  [ TREMBER -> match instance INT at POS ],
    convert match instance INT at POS to TREMBER using MEMBERS;
  [ TREMBER -> skip at POS ],
    convert skip at POS to TREMBER using MEMBERS;
  [ TREMBER -> skip instance INT at POS ],
    convert skip instance INT at POS to TREMBER using MEMBERS;
  [ TREMBER -> other at POS ],
    convert other at POS to TREMBER using MEMBERS;
  [ TREMBER -> other instance INT at POS ],
    convert other instance INT at POS to TREMBER using MEMBERS;
  [ TREMBER -> input pos at POS ],
    convert input pos at POS to TREMBER using MEMBERS;
  [ TREMBER -> input pos instance INT at POS ],
    convert input pos instance INT at POS to TREMBER using MEMBERS;
  [ TREMBER -> penalty INT ];
  [ TREMBER -> empty ];
  [ TREMBER -> erroneous ], ?;
  ?.

#
# Option transformation:
#
ACTION convert option >INT to TREMBER> using >MEMBERS at >POS:
  try convert option INT to TREMBER using MEMBERS and seen 1 sons;
  convert INT to TEXT,
    error ("invalid option placeholder \"[]." + TEXT + "\" in transduction", POS),
    [ erroneous -> TREMBER ].

TEST try convert option >INT1 to TREMBER> using >MEMBERS and seen >INT2 sons:
  [MEMBERS -> MEMBER MEMBERS],
    (where member is son (MEMBER),
       (where member is option (MEMBER, NONTERMINAL, EXPRS),
          ([INT1 -> 1],
             [anonymous son INT2 -> SON],
             [SON with params EXPRS -> TREMBER];
           try convert option (INT1 - 1) to TREMBER using MEMBERS and seen (INT2 + 1) sons);
        try convert option INT1 to TREMBER using MEMBERS and seen (INT2 + 1) sons);
     try convert option INT1 to TREMBER using MEMBERS and seen INT2 sons);
  [MEMBERS -> nil],
    -;
  ?.

#
# Son counting:
#
NR_SONS = INT.

FUNCTION count sons (>NONTERMINAL, >MEMBERS, NR_SONS>):
  count sons (MEMBERS, NR_SONS).
#    ( is left recursive (NONTERMINAL),
#        [ NR_SONS + 1 -> NR_SONS ];
#      +
#    ).

FUNCTION count sons (>MEMBERS, NR_SONS>):
  [ MEMBERS -> nil ], [ 0 -> NR_SONS ];
  [ MEMBERS -> MEMBER MEMBERS1 ],
    ( where member is son (MEMBER),
        count sons (MEMBERS1, NR_SONS), [ NR_SONS + 1 -> NR_SONS ];
      count sons (MEMBERS1, NR_SONS)
    );
  ?.

#
# Nonterminal conversion:
#
ACTION convert nonterminal >ID instance >INT to TREMBER> using >MEMBERS at >POS:
  count sons (MEMBERS, NR_SONS),
    ( convert nonterminal to son (ID, INT, MEMBERS, 1, NR_SONS, SON, ACTUALS),
        [ ACTUALS -> EXPRS ],
        [ SON with params EXPRS -> TREMBER ];
      convert nonterminal with old syntax to son (ID, INT, MEMBERS, 1, NR_SONS, SON, ACTUALS, POS),
        [ ACTUALS -> EXPRS ],
        [ SON with params EXPRS -> TREMBER ];
      convert id to name (ID, TEXT), [ erroneous -> TREMBER ],
        ( [ INT -> 1 ],
            error ("invalid nonterminal placeholder \"" + TEXT + "\" or \"" + TEXT + ".1\"", POS);
          convert INT to TEXT2,
          error ("invalid nonterminal placeholder \"" + TEXT + "." + TEXT2 + "\"", POS)
        )
    ).

TEST convert nonterminal to son (>ID, >INT1, >MEMBERS, >INT2, >NR_SONS, SON>, ACTUALS>):
  [MEMBERS -> MEMBER MEMBERS1],
    (where member is son (MEMBER),
       ([MEMBER -> call ID2 def NONTERMINAL with ACTUALS at POS],
          convert NONTERMINAL to ID1 and ARITY,
          [ID = ID1],
          ([INT1 -> 1],
             [NONTERMINAL son INT2 -> SON];
           convert nonterminal to son (ID, INT1 - 1, MEMBERS1, INT2 + 1, NR_SONS, SON, ACTUALS));
        convert nonterminal to son (ID, INT1, MEMBERS1, INT2 + 1, NR_SONS, SON, ACTUALS));
     convert nonterminal to son (ID, INT1, MEMBERS1, INT2, NR_SONS, SON, ACTUALS)).

# This is here only temporarily - it should be removed in the 
# next major release.
# If "id3" fails, maybe "id.3" works.
# Unfortunately we can't see if the original denotation was "id" or "id.1",
# but we must certainly refuse if it was "id.2" or higher.
PRED convert nonterminal with old syntax to son (>ID, >INT, >MEMBERS, >INT2, >NR_SONS,
						 SON>, ACTUALS>, >POS):
  [ INT -> 1 ],
    convert id to name (ID, TEXT), split TEXT in TEXT2 and INT,
    convert INT to TEXT3, convert name to id (TEXT2, ID2),
    convert nonterminal to son (ID2, INT, MEMBERS, 1, NR_SONS, SON, ACTUALS),
    strong warning ("syntax change: old-style \"" + TEXT +
		    "\" interpreted as new-style \"" + TEXT2 + "." + TEXT3 + "\"", POS).

TEST split >TEXT in TEXT2> and INT>:
  length (TEXT, INT2), digitsuffixlength (TEXT, INT2, 0, INT3),
    extract (TEXT, INT2 - INT3, INT2, TEXT3), convert TEXT3 to INT,
    extract (TEXT, 0, INT2 - INT3, TEXT2).

FUNCTION digitsuffixlength(>TEXT, >INT, >INT2, INT3>):    
  extract(TEXT, INT - 1, INT, TEXT2),
    ( between(TEXT2, 0, "0", "9"),
        digitsuffixlength(TEXT, INT - 1, INT2 + 1, INT3);
      [ INT2 -> INT3 ]
    ).

#
# Special names
# Matches, Skips and input positions
#
# Deze kunnen we weglaten zodra er geen dubbele administratie voor de
# MEMBERS als de TREMBERS is. Het enige wat de komende regels doen
# is de goede affix expressie uit de MEMBERs peuteren om die vervolgens
# als de transductie te gebruiken.
#
ACTION convert match instance >INT at >POS to TREMBER> using >MEMBERS:
  convert match to expression (INT, MEMBERS, EXPR), [ match using EXPR -> TREMBER ];
  [ "$MATCH." -> TEXT1 ], convert INT to TEXT2,
    error ("Invalid placeholder \"" + TEXT1 + TEXT2 + "\"", POS),
    [ erroneous -> TREMBER ].

ACTION convert match at >POS to TREMBER> using >MEMBERS:
  convert match to expression (1, MEMBERS, EXPR), [ match using EXPR -> TREMBER ];
  [ "$MATCH" -> TEXT ],
    error ("Invalid placeholder \"" + TEXT + "\"", POS),
    [ erroneous -> TREMBER ].

TEST convert match to expression (>INT, >MEMBERS, EXPR>):
  [ MEMBERS -> MEMBER MEMBERS1 ],
    ( [ MEMBER -> match regexp ID using EXPR targeting EXPR2 at POS ],
        ( [ INT -> 1 ];
          convert match to expression (INT - 1, MEMBERS1, EXPR)
        );
      convert match to expression (INT, MEMBERS1, EXPR)
    ).

ACTION convert skip instance >INT at >POS to TREMBER> using >MEMBERS:
  convert skip to expression (INT, MEMBERS, EXPR), [ skip using EXPR -> TREMBER ];
  [ "$SKIP." -> TEXT1 ], convert INT to TEXT2,
    error ("Invalid placeholder \"" + TEXT1 + TEXT2 + "\"", POS),
    [ erroneous -> TREMBER ].

ACTION convert skip at >POS to TREMBER> using >MEMBERS:
  convert skip to expression (1, MEMBERS, EXPR), [ skip using EXPR -> TREMBER ];
  [ "$SKIP" -> TEXT ],
    error ("Invalid placeholder \"" + TEXT + "\"", POS),
    [ erroneous -> TREMBER ].

TEST convert skip to expression (>INT, >MEMBERS, EXPR>):
  [ MEMBERS -> MEMBER MEMBERS1 ],
    ( [ MEMBER -> skip regexp ID using EXPR targeting EXPR2 at POS ],
        ( [ INT -> 1 ];
          convert skip to expression (INT - 1, MEMBERS1, EXPR)
	);
      convert skip to expression (INT, MEMBERS1, EXPR)
    ).

ACTION convert other instance >INT at >POS to TREMBER> using >MEMBERS:
  convert other to expression (INT, MEMBERS, EXPR), [ other using EXPR -> TREMBER ];
  [ "$OTHER." -> TEXT1 ], convert INT to TEXT2,
    error ("Invalid placeholder \"" + TEXT1 + TEXT2 + "\"", POS),
    [ erroneous -> TREMBER ].

ACTION convert other at >POS to TREMBER> using >MEMBERS:
  convert other to expression (1, MEMBERS, EXPR), [ other using EXPR -> TREMBER ];
  [ "$OTHER" -> TEXT ],
    error ("Invalid placeholder \"" + TEXT + "\"", POS),
    [ erroneous -> TREMBER ].

TEST convert other to expression (>INT, >MEMBERS, EXPR>):
  [ MEMBERS -> MEMBER MEMBERS1 ],
    ( [ MEMBER -> other using EXPR targeting EXPR2 at POS ],
        ( [ INT -> 1 ];
          convert other to expression (INT - 1, MEMBERS1, EXPR)
	);
      convert other to expression (INT, MEMBERS1, EXPR)
    ).

ACTION convert input pos instance >INT at >POS to TREMBER> using >MEMBERS:
  convert input pos to expression (INT, MEMBERS, EXPR),
    [ input pos using EXPR -> TREMBER ];
  [ "$POS" -> TEXT1 ], convert INT to TEXT2,
    error ("Invalid placeholder \"" + TEXT1 + TEXT2 + "\"", POS),
    [ erroneous -> TREMBER ].

ACTION convert input pos at >POS to TREMBER> using >MEMBERS:
  convert input pos to expression (1, MEMBERS, EXPR), [ input pos using EXPR -> TREMBER ];
  [ "$POS" -> TEXT ],
    error ("Invalid placeholder \"" + TEXT + "\"", POS),
    [ erroneous -> TREMBER ].

TEST convert input pos to expression (>INT, >MEMBERS, EXPR>):
  [ MEMBERS -> MEMBER MEMBERS1 ],
    ( [ MEMBER -> input pos using EXPR targeting EXPR2 at POS],
        ( [ INT -> 1 ];
          convert input pos to expression (INT - 1, MEMBERS1, EXPR)
	);
      convert input pos to expression (INT, MEMBERS1, EXPR)
    ).
