home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z08.c < prev    next >
Text File  |  1994-01-23  |  37KB  |  1,038 lines

  1. /*@z08.c:Object Manifest:ReplaceWithSplit()@**********************************/
  2. /*                                                                           */
  3. /*  LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05)       */
  4. /*  COPYRIGHT (C) 1993 Jeffrey H. Kingston                                   */
  5. /*                                                                           */
  6. /*  Jeffrey H. Kingston (jeff@cs.su.oz.au)                                   */
  7. /*  Basser Department of Computer Science                                    */
  8. /*  The University of Sydney 2006                                            */
  9. /*  AUSTRALIA                                                                */
  10. /*                                                                           */
  11. /*  This program is free software; you can redistribute it and/or modify     */
  12. /*  it under the terms of the GNU General Public License as published by     */
  13. /*  the Free Software Foundation; either version 1, or (at your option)      */
  14. /*  any later version.                                                       */
  15. /*                                                                           */
  16. /*  This program is distributed in the hope that it will be useful,          */
  17. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
  18. /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
  19. /*  GNU General Public License for more details.                             */
  20. /*                                                                           */
  21. /*  You should have received a copy of the GNU General Public License        */
  22. /*  along with this program; if not, write to the Free Software              */
  23. /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
  24. /*                                                                           */
  25. /*  FILE:         z08.c                                                      */
  26. /*  MODULE:       Object Manifest                                            */
  27. /*  EXTERNS:      Manifest()                                                 */
  28. /*                                                                           */
  29. /*****************************************************************************/
  30. #include "externs"
  31.  
  32. #define errorcase()                            \
  33.                                     \
  34.     y = MakeWord(WORD, STR_EMPTY, &fpos(x));                \
  35.     ReplaceNode(y, x);  DisposeObject(x);                \
  36.     x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE);    \
  37.     break;
  38.  
  39.  
  40. /*****************************************************************************/
  41. /*                                                                           */
  42. /*  static ReplaceWithSplit(x, bthr, fthr)                                   */
  43. /*                                                                           */
  44. /*  Replace object x with a SPLIT object, if threads for this object are     */
  45. /*  requested by bthr and/or fthr.                                           */
  46. /*                                                                           */
  47. /*****************************************************************************/
  48.  
  49. #define ReplaceWithSplit(x, bthr, fthr)                    \
  50.    if( bthr[ROW] || bthr[COL] || fthr[ROW] || fthr[COL] )        \
  51.     x = insert_split(x, bthr, fthr)
  52.  
  53. static OBJECT insert_split(x, bthr, fthr)
  54. OBJECT x;  OBJECT bthr[2], fthr[2];
  55. { OBJECT res, new_op;  int dim;
  56.   debug1(DOM, DD, "ReplaceWithSplit(%s, -)", EchoObject(x));
  57.   assert( type(x) != SPLIT, "ReplaceWithSplit: type(x) already SPLIT!" );
  58.   res = New(SPLIT);
  59.   FposCopy(fpos(res), fpos(x));
  60.   ReplaceNode(res, x);
  61.   for( dim = COL;  dim <= ROW;  dim++ )
  62.   { if( bthr[dim] || fthr[dim] )
  63.     { new_op = New(dim == COL ? COL_THR : ROW_THR);
  64.       thr_state(new_op) = NOTSIZED;
  65.       fwd(new_op, 1-dim) = 0;    /* will hold max frame_size */
  66.       back(new_op, 1-dim) = 0;    /* will hold max frame_origin */
  67.       FposCopy(fpos(new_op), fpos(x));
  68.       Link(res, new_op);  Link(new_op, x);
  69.       if( bthr[dim] )  Link(bthr[dim], new_op);
  70.       if( fthr[dim] )  Link(fthr[dim], new_op);
  71.     }
  72.     else Link(res, x);
  73.   }
  74.  
  75.   debug1(DOM, DD, "ReplaceWithSplit returning %s", EchoObject(res));
  76.   return res;
  77. } /* end insert_split */
  78.  
  79. /*@::ReplaceWithTidy()@*******************************************************/
  80. /*                                                                           */
  81. /*  OBJECT ReplaceWithTidy(x)                                                */
  82. /*                                                                           */
  83. /*  Replace object x with a tidier version in which juxtapositions are       */
  84. /*  folded.  If this is not possible, return the original object.            */
  85. /*                                                                           */
  86. /*****************************************************************************/
  87.  
  88. OBJECT ReplaceWithTidy(x)
  89. OBJECT x;
  90. { static FULL_CHAR    buff[MAX_LINE];        /* the growing current word */
  91.   static int        buff_len;        /* length of current word   */
  92.   static FILE_POS    buff_pos;        /* filepos of current word  */
  93.   static unsigned    buff_typ;        /* WORD or QWORD of current */
  94.   OBJECT                link, y, tmp, res;    /* temporaries              */
  95.   debug1(DOM, DD, "ReplaceWithTidy( %s )", EchoObject(x));
  96.   switch( type(x) )
  97.   {
  98.     case ACAT:
  99.     
  100.       for( link = Down(x);  link != x;  link = NextDown(link) )
  101.       {    Child(y, link);
  102.     if( type(y) == ACAT )
  103.     { tmp = Down(y);  TransferLinks(tmp, y, link);
  104.       DisposeChild(link);  link = PrevDown(tmp);
  105.     }
  106.       }
  107.       res = nil;  buff_len = 0;  buff_typ = WORD;  FposCopy(buff_pos, fpos(x));
  108.       for( link = Down(x); link != x; link = NextDown(link) )
  109.       {    Child(y, link);
  110.     if( is_word(type(y)) )
  111.     { if( buff_len + StringLength(string(y)) >= MAX_LINE )
  112.         Error(WARN, &fpos(y), "word is too long");
  113.       else
  114.       { if( buff_len == 0 )  FposCopy(buff_pos, fpos(y));
  115.         StringCopy(&buff[buff_len], string(y));
  116.         buff_len += StringLength(string(y));
  117.         if( type(y) == QWORD )  buff_typ = QWORD;
  118.       }
  119.     }
  120.     else if( type(y) == GAP_OBJ )
  121.     { if( Down(y) != y || hspace(y) + vspace(y) > 0 )
  122.       { tmp = MakeWord(buff_typ, buff, &buff_pos);
  123.         buff_len = 0;  buff_typ = WORD;
  124.         if( res == nil ) { res = New(ACAT); FposCopy(fpos(res), fpos(x)); }
  125.         Link(res, tmp);  Link(res, y);
  126.       }
  127.     }
  128.     else /* error */
  129.     { if( res != nil )  DisposeObject(res);
  130.       debug0(DOM, DD, "ReplaceWithTidy returning unchanged");
  131.       return x;
  132.     }
  133.       }
  134.       tmp = MakeWord(buff_typ, buff, &buff_pos);
  135.       if( res == nil )  res = tmp;
  136.       else Link(res, tmp);
  137.       ReplaceNode(res, x);  DisposeObject(x);
  138.       debug1(DOM, DD, "ReplaceWithTidy returning %s", EchoObject(res));
  139.       return res;
  140.  
  141.  
  142.     case WORD:
  143.     case QWORD:
  144.  
  145.       debug1(DOM, DD, "ReplaceWithTidy returning %s", EchoObject(x));
  146.       return x;
  147.  
  148.  
  149.     default:
  150.  
  151.       debug0(DOM, DD, "ReplaceWithTidy returning unchanged");
  152.       return x;
  153.   }
  154. } /* end ReplaceWithTidy */
  155.  
  156.  
  157. /*@::GetScaleFactor()@********************************************************/
  158. /*                                                                           */
  159. /*  static float GetScaleFactor(x, str)                                      */
  160. /*                                                                           */
  161. /*  Find a scale factor in object x and return it as a float, after checks.  */
  162. /*  Incorporate str in any error messages generated.                         */
  163. /*                                                                           */
  164. /*****************************************************************************/
  165.  
  166. static float GetScaleFactor(x, str)
  167. OBJECT x;  char *str;
  168. { float scale_factor;
  169.   if( !is_word(type(x)) )
  170.   { Error(WARN, &fpos(x), "replacing invalid %s by 1.0", str);
  171.     scale_factor = 1.0;
  172.   }
  173.   else if( sscanf( (char *) string(x), "%f", &scale_factor) != 1 )
  174.   { Error(WARN, &fpos(x), "replacing invalid %s %s by 1.0", str, string(x));
  175.     scale_factor = 1.0;
  176.   }
  177.   else if( scale_factor < 0.01 )
  178.   { Error(WARN, &fpos(x), "replacing undersized %s %s by 1.0", str, string(x));
  179.     scale_factor = 1.0;
  180.   }
  181.   else if( scale_factor > 100 )
  182.   { Error(WARN, &fpos(x), "replacing oversized %s %s by 1.0", str, string(x));
  183.     scale_factor = 1.0;
  184.   }
  185.   return scale_factor;
  186. } /* GetScaleFactor */
  187.  
  188.  
  189. static OBJECT nbt[2] = { nil, nil };        /* constant nil threads      */
  190. static OBJECT nft[2] = { nil, nil };        /* constant nil threads      */
  191. static OBJECT ntarget = nil;            /* constant nil target       */
  192.  
  193. /*@::Manifest()@**************************************************************/
  194. /*                                                                           */
  195. /*  OBJECT Manifest(x, env, style, bthr, fthr, target, crs, ok, need_expand) */
  196. /*                                                                           */
  197. /*  Manifest object x, interpreted in environment env and style style.       */
  198. /*  The result replaces x, and is returned also.                             */
  199. /*  The manifesting operation converts x from a pure parse tree object       */
  200. /*  containing closures and no threads, to an object ready for sizing,       */
  201. /*  with fonts propagated to the words, fill styles propagated to the        */
  202. /*  ACATs, and line spacings propagated to all interested parties.           */
  203. /*  All non-recursive, non-indefinite closures are expanded.                 */
  204. /*  Threads joining objects on a mark are constructed, and SPLIT objects     */
  205. /*  inserted, so that sizing becomes a trivial operation.                    */
  206. /*                                                                           */
  207. /*  Manifest will construct threads and pass them up as children of bthr[]   */
  208. /*  and fthr[] whenever non-nil values of these variables are passed in:     */
  209. /*                                                                           */
  210. /*      bthr[COL]            protrudes upwards from x                        */
  211. /*      fthr[COL]            protrudes downwards from x                      */
  212. /*      bthr[ROW]            protrudes leftwards from x                      */
  213. /*      fthr[ROW]            protrudes rightwards from x                     */
  214. /*                                                                           */
  215. /*  If *target != nil, Manifest will expand indefinite closures leading to   */
  216. /*  the first @Galley lying within an object of type *target.                */
  217. /*                                                                           */
  218. /*  Some objects x are not "real" in the sense that they do not give rise    */
  219. /*  to rectangles in the final printed document.  The left parameter of      */
  220. /*  @Wide and similar operators, and the gap following a concatenation       */
  221. /*  operator, are examples of such non-real objects.  The ok flag is true    */
  222. /*  when x is part of a real object.  This is needed because some things,    */
  223. /*  such as the insinuation of cross references and the breaking of          */
  224. /*  lines @Break ACAT objects, only apply to real objects.                   */
  225. /*                                                                           */
  226. /*  If *crs != nil, it points to a list of indexes to cross-references       */
  227. /*  which are to be insinuated into the manifested form of x if x is real.   */
  228. /*                                                                           */
  229. /*  If need_expand is TRUE it forces closure x to expand.                    */
  230. /*                                                                           */
  231. /*****************************************************************************/
  232.  
  233. OBJECT Manifest(x, env, style, bthr, fthr, target, crs, ok, need_expand)
  234. OBJECT x, env;  STYLE *style;
  235. OBJECT bthr[2], fthr[2]; OBJECT *target, *crs;
  236. BOOLEAN ok, need_expand;
  237. { OBJECT bt[2], ft[2], y, link, sym, tag, gaplink, g, ylink, yield, ytag, zlink;
  238.   OBJECT res, res_env, res_env2, hold_env, hold_env2, first_bt, last_ft, z;
  239.   OBJECT firsttag, firstres, prev;  float scale_factor;
  240.   int par, perp;  GAP res_gap;  unsigned res_inc;  STYLE new_style;
  241.   BOOLEAN still_backing, done, multiline, symbol_free;  FULL_CHAR ch;
  242.  
  243.   debug2(DOM, D,   "[Manifest(%s %s )", Image(type(x)), EchoObject(x));
  244.   debug1(DOM, DD,  "  environment: %s", EchoObject(env));
  245.   debug6(DOM, DD,  "  style: %s;  target: %s;  threads: %s%s%s%s",
  246.     EchoStyle(style), SymName(*target),
  247.     bthr[COL] ? " up"    : "",  fthr[COL] ? " down"  : "",
  248.     bthr[ROW] ? " left"  : "",  fthr[ROW] ? " right" : "");
  249.  
  250.   if( type(x) <= ACAT ) switch( type(x) )    /* breaks up oversize switch */
  251.   {
  252.  
  253.     case CLOSURE:
  254.     
  255.       sym = actual(x);
  256.       StyleCopy(save_style(x), *style);
  257.       debug1(DOM, DD,  "  closure; sym = %s", SymName(sym));
  258.  
  259.       /* expand parameters where possible, and find if they are all free */
  260.       symbol_free = TRUE;
  261.       for( link = Down(x);  link != x;  link = NextDown(link) )
  262.       { Child(y, link);
  263.     assert( type(y) == PAR, "Manifest/CLOSURE: type(y) != PAR!" );
  264.     Child(z, Down(y));
  265.     if( !is_word(type(z)) && !has_par(actual(y)) )
  266.     { if( is_tag(actual(y)) || is_key(actual(y)) || type(z) == NEXT )
  267.       { z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  268.         z = ReplaceWithTidy(z);
  269.       }
  270.     }
  271.     if( !is_word(type(z)) )  symbol_free = FALSE;
  272.       }
  273.  
  274.       /* if all parameters are free of symbols, optimize environment */
  275.       if( symbol_free && imports(sym) == nil && enclosing(sym) != StartSym )
  276.       {    y = SearchEnv(env, enclosing(sym));
  277.     if( y != nil && type(y) == CLOSURE )
  278.     { 
  279.       debug0(DCR, DD, "calling SetEnv from Manifest (a)");
  280.       env = SetEnv(y, nil);
  281.       hold_env2 = New(ACAT);  Link(hold_env2, env);
  282.     }
  283.     else
  284.     { Error(WARN, &fpos(x), "symbol %s used outside %s",
  285.         SymName(sym), SymName(enclosing(sym)));
  286.       hold_env2 = nil;
  287.     }
  288.       }
  289.       else hold_env2 = nil;
  290.  
  291.       if( has_target(sym) && !need_expand )
  292.       {
  293.     /* convert symbols with targets to unsized galleys */
  294.     OBJECT hd = New(HEAD);
  295.     FposCopy(fpos(hd), fpos(x));
  296.     actual(hd) = sym;
  297.     backward(hd) = TargetSymbol(x, &whereto(hd));
  298.     ready_galls(hd) = nil;
  299.     must_expand(hd) = TRUE;
  300.     sized(hd) = FALSE;
  301.     ReplaceNode(hd, x);
  302.     Link(hd, x);
  303.     AttachEnv(env, x);
  304.     x = hd;
  305.           threaded(x) = bthr[COL] != nil || fthr[COL] != nil;
  306.     ReplaceWithSplit(x, bthr, fthr);
  307.       }
  308.       else if(
  309.         *target == sym                ? (*target = nil, TRUE) :
  310.         need_expand                ? TRUE  :
  311.         uses_galley(sym) && !recursive(sym) ? TRUE  :
  312.         !indefinite(sym) && !recursive(sym) ? TRUE  :
  313.         indefinite(sym)  && *target != nil  ? SearchUses(sym, *target)
  314.                             : FALSE
  315.          )
  316.       {
  317.     /* expand the closure and manifest the result */
  318.     debug1(DOM, DD, "expanding; style: %s", EchoStyle(style));
  319.     x = ClosureExpand(x, env, TRUE, crs, &res_env);
  320.     hold_env = New(ACAT);  Link(hold_env, res_env);
  321.     debug1(DOM, DD, "recursive call; style: %s", EchoStyle(style));
  322.     x = Manifest(x, res_env, style, bthr, fthr, target, crs, ok, FALSE);
  323.     DisposeObject(hold_env);
  324.       }
  325.       else
  326.       {
  327.     /* indefinite symbol, leave unexpanded */
  328.     AttachEnv(env, x);
  329.     threaded(x) = bthr[COL] != nil || fthr[COL] != nil;
  330.     debug0(DOM, DD,  "  closure; calling ReplaceWithSplit");
  331.     ReplaceWithSplit(x, bthr, fthr);
  332.       }
  333.       if( hold_env2 != nil )  DisposeObject(hold_env2);
  334.       break;
  335.  
  336.  
  337.     case NULL_CLOS:
  338.  
  339.       StyleCopy(save_style(x), *style);
  340.       ReplaceWithSplit(x, bthr, fthr);
  341.       break;
  342.  
  343.  
  344.     case CROSS:
  345.     
  346.       assert( Down(x) != x && LastDown(x) != Down(x), "Manifest: CROSS child!");
  347.       debug0(DCR, DD, "  calling CrossExpand from Manifest/CROSS");
  348.       x = CrossExpand(x, env, style, TRUE, crs, &res_env);
  349.       assert( type(x) == CLOSURE, "Manifest/CROSS: type(x)!" );
  350.       hold_env = New(ACAT);  Link(hold_env, res_env);
  351.       /* expand here (calling Manifest immediately makes unwanted cr) */
  352.       x = ClosureExpand(x, res_env, FALSE, crs, &res_env2);
  353.       hold_env2 = New(ACAT);  Link(hold_env2, res_env2);
  354.       x = Manifest(x, res_env2, style, bthr, fthr, target, crs, ok, TRUE);
  355.       DisposeObject(hold_env);
  356.       DisposeObject(hold_env2);
  357.       break;
  358.  
  359.  
  360.     case WORD:
  361.     case QWORD:
  362.     
  363.       if( !ok || *crs == nil )
  364.       {    word_font(x) = font(*style);
  365.     ReplaceWithSplit(x, bthr, fthr);
  366.     break;
  367.       }
  368.       y = New(ACAT);
  369.       FposCopy(fpos(y), fpos(x));
  370.       ReplaceNode(y, x);
  371.       Link(y, x);  x = y;
  372.       /* NB NO BREAK! */
  373.  
  374.  
  375.     case ACAT:
  376.     
  377.       StyleCopy(save_style(x), *style);
  378.       assert(Down(x) != x, "Manifest: ACAT!" );
  379.       link = Down(x);  Child(y, link);
  380.       assert( type(y) != GAP_OBJ, "Manifest ACAT: GAP_OBJ is first!" );
  381.       multiline = FALSE;
  382.  
  383.       /* manifest first child and insert any cross references */
  384.       if( is_word(type(y)) )  word_font(y) = font(*style);
  385.       else y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE);
  386.       if( ok && *crs != nil )
  387.       {    
  388.     debug1(DCR, D, "  insinuating %s", EchoObject(*crs));
  389.     TransferLinks(Down(*crs), *crs, link);
  390.     DisposeObject(*crs);
  391.     *crs = nil;
  392.       }
  393.       prev = y;
  394.  
  395.       for( gaplink = Down(link);  gaplink != x;  gaplink = NextDown(link) )
  396.       {
  397.     Child(g, gaplink);
  398.     assert( type(g) == GAP_OBJ, "Manifest ACAT: no GAP_OBJ!" );
  399.     link = NextDown(gaplink);
  400.     assert( link != x, "Manifest ACAT: GAP_OBJ is last!" );
  401.     Child(y, link);
  402.     assert( type(y) != GAP_OBJ, "Manifest ACAT: double GAP_OBJ!" );
  403.  
  404.     /* manifest the next child */
  405.         debug1(DOM, DD, "  in ACAT (3), style = %s", EchoStyle(style));
  406.     if( is_word(type(y)) ) word_font(y) = font(*style);
  407.     else y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE);
  408.  
  409.     /* manifest the gap object */
  410.     if( Down(g) != g )
  411.     {
  412.       /* explicit & operator whose value is the child of g */
  413.       Child(z, Down(g));
  414.       z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  415.       z = ReplaceWithTidy(z);
  416.       GetGap(z, style, &gap(g), &res_inc);
  417.       vspace(g) = hspace(g) = 0;
  418.     }
  419.     else
  420.     {
  421.       /* implicit & operator */
  422.       GapCopy(gap(g), space_gap(*style));
  423.       width(gap(g)) = width(gap(g)) * (vspace(g) + hspace(g));
  424.       if( vspace(g) > 0 && is_definite(type(y)) )  multiline = TRUE;
  425.     }
  426.         debug1(DOM, DD, "  in ACAT, gap = %s", EchoLength(width(gap(g))));
  427.  
  428.     /* compress adjacent juxtaposed words of equal font */
  429.     if( is_word(type(y)) && width(gap(g)) == 0 && vspace(g)+hspace(g) == 0 )
  430.     { if( units(gap(g)) == FIXED_UNIT && mode(gap(g)) == EDGE_MODE )
  431.       { if( prev != nil && is_word(type(prev)) )
  432.         { if( !mark(gap(g)) && word_font(prev)==word_font(y) )
  433.           {    unsigned typ;
  434.         if( StringLength(string(prev)) + StringLength(string(y))
  435.               >= MAX_LINE )
  436.           Error(FATAL, &fpos(prev), "word %s%s is too long",
  437.             string(prev), string(y));
  438.         z = y;
  439.         typ = type(prev) == QWORD || type(y) == QWORD ? QWORD : WORD;
  440.         y = MakeWordTwo(typ, string(prev), string(y), &fpos(prev));
  441.         word_font(y) = word_font(prev);
  442.         MoveLink(link, y, CHILD);
  443.         DisposeObject(z);
  444.         DisposeChild(Up(prev));
  445.         DisposeChild(gaplink);
  446.           }
  447.         }
  448.       }
  449.     }
  450.     prev = y;
  451.  
  452.     /* insinuate any cross-references */
  453.     if( ok && *crs != nil )
  454.     {
  455.       debug1(DCR, D, "  insinuating %s", EchoObject(*crs));
  456.       TransferLinks(Down(*crs), *crs, link);
  457.       DisposeObject(*crs);
  458.       *crs = nil;
  459.     }
  460.  
  461.       }
  462.  
  463.       /* implement FILL_OFF break option if required */
  464.       if( ok && multiline && fill_style(*style) == FILL_UNDEF )
  465.     Error(FATAL, &fpos(x), "missing %s operator or option", KW_BREAK);
  466.       if( ok && multiline && fill_style(*style) == FILL_OFF )
  467.       {    OBJECT last_acat = x, new_acat;
  468.     x = New(VCAT);
  469.     ReplaceNode(x, last_acat);
  470.     Link(x, last_acat);
  471.     for( link = Down(last_acat); link != last_acat; link = NextDown(link) )
  472.     { Child(g, link);
  473.       if( type(g) == GAP_OBJ && mode(gap(g)) != NO_MODE && vspace(g) > 0 )
  474.       { link = PrevDown(link);
  475.         MoveLink(NextDown(link), x, PARENT);
  476.         GapCopy(gap(g), line_gap(*style));
  477.         width(gap(g)) *= vspace(g);
  478.         new_acat = New(ACAT);
  479.         if( hspace(g) > 0 )
  480.         { z = MakeWord(WORD, STR_EMPTY, &fpos(g));
  481.           Link(new_acat, z);
  482.           z = New(GAP_OBJ);
  483.           hspace(z) = hspace(g);
  484.           vspace(z) = 0;
  485.           GapCopy(gap(z), space_gap(*style));
  486.           width(gap(z)) *= hspace(z);
  487.           Link(new_acat, z);
  488.         }
  489.         TransferLinks(NextDown(link), last_acat, new_acat);
  490.         StyleCopy(save_style(new_acat), *style);
  491.         Link(x, new_acat);
  492.         last_acat = new_acat;
  493.         link = last_acat;
  494.       }
  495.     }
  496.       }
  497.  
  498.       ReplaceWithSplit(x, bthr, fthr);
  499.       break;
  500.  
  501.  
  502.     default:
  503.  
  504.       Error(INTERN, &fpos(x), "Manifest: no case for type %s", Image(type(x)));
  505.       break;
  506.  
  507.   } /* end <= ACAT */
  508.   else if( type(x) <= VCAT )  switch( type(x) )
  509.   {
  510.  
  511.     case HCAT:
  512.     case VCAT:
  513.     
  514.       par = type(x) == HCAT ? ROW : COL;
  515.       perp = 1 - par;
  516.       link = Down(x);
  517.       gaplink = NextDown(link);
  518.       assert( link!=x && gaplink!=x, "Manifest/VCAT: less than two children!" );
  519.       Child(y, link);  Child(g, gaplink);
  520.  
  521.       /* set bt and ft threads for y */
  522.       bt[perp] = bthr[perp];
  523.       ft[perp] = fthr[perp];
  524.       first_bt = bt[par] = bthr[par] ? New(THREAD) : nil;
  525.       ft[par] = join(gap(g)) ? New(THREAD) : nil;
  526.       still_backing = first_bt != nil;
  527.  
  528.       /* manifest y and insinuate any cross-references */
  529.       y = Manifest(y, env, style, bt, ft, target, crs, ok, FALSE);
  530.       if( type(x) == VCAT && ok && *crs != nil )
  531.       {
  532.     debug1(DCR, D, "  insinuating %s", EchoObject(*crs));
  533.     TransferLinks(Down(*crs), *crs, link);
  534.     DisposeObject(*crs);
  535.     *crs = nil;
  536.       }
  537.  
  538.       /* manifest the remaining children */
  539.       while( g != nil )
  540.       {    
  541.     /* manifest the gap object, store it in gap(g), add perp threads */
  542.     assert( type(g) == GAP_OBJ, "Manifest/VCAT: type(g) != GAP_OBJECT!" );
  543.     assert( Down(g) != g, "Manifest/VCAT: GAP_OBJ has no child!" );
  544.     Child(z, Down(g));
  545.     debug1(DOM, DD, "manifesting gap, style = %s", EchoStyle(style));
  546.     z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  547.     debug1(DOM, DD, "replacing with tidy, style = %s", EchoStyle(style));
  548.     z = ReplaceWithTidy(z);
  549.     debug1(DOM, DD, "calling GetGap, style = %s", EchoStyle(style));
  550.     GetGap(z, style, &gap(g), &res_inc);
  551.     if( bt[perp] )  Link(bt[perp], g);
  552.     if( ft[perp] )  Link(ft[perp], g);
  553.  
  554.     /* find the next child y, and following gap if any */
  555.     link = NextDown(gaplink);
  556.     assert( link != x, "Manifest/VCAT: GAP_OBJ is last child!" );
  557.     Child(y, link);
  558.     gaplink = NextDown(link);
  559.     if( gaplink == x )  g = nil;
  560.     else Child(g, gaplink);
  561.  
  562.     /* set bt and ft threads for y */
  563.     last_ft = ft[par];
  564.     bt[par] = ft[par] ? New(THREAD) : nil;
  565.     ft[par] = g != nil ? join(gap(g)) ? New(THREAD) : nil
  566.                : fthr[par]    ? New(THREAD) : nil;
  567.  
  568.     /* manifest y and insinuate any cross references */
  569.     y = Manifest(y, env, style, bt, ft, target, crs, ok, FALSE);
  570.     if( type(x) == VCAT && ok && *crs != nil )
  571.         {
  572.       debug1(DCR, D, "  insinuating %s", EchoObject(*crs));
  573.       TransferLinks(Down(*crs), *crs, link);
  574.       DisposeObject(*crs);
  575.       *crs = nil;
  576.         }
  577.  
  578.     if( bt[par] )    /* then thread lists last_ft and bt[par] must merge */
  579.     { OBJECT llink, rlink, lthread, rthread;  BOOLEAN goes_through;
  580.       assert( Down(bt[par]) != bt[par], "Manifest: bt[par] no children!" );
  581.       assert( last_ft!=nil && Down(last_ft)!=last_ft, "Manifest:last_ft!" );
  582.  
  583.       /* check whether marks run right through y in par direction */
  584.       goes_through = FALSE;
  585.       if( ft[par] )
  586.       { assert( Down(ft[par]) != ft[par], "Manifest: ft[par] child!" );
  587.         Child(lthread, LastDown(bt[par]));
  588.         Child(rthread, LastDown(ft[par]));
  589.         goes_through = lthread == rthread;
  590.       }
  591.  
  592.       /* merge the thread lists */
  593.       llink = Down(last_ft);  rlink = Down(bt[par]);
  594.       while( llink != last_ft && rlink != bt[par] )
  595.       { Child(lthread, llink);
  596.         Child(rthread, rlink);
  597.         assert( lthread != rthread, "Manifest: lthread == rthread!" );
  598.         MergeNode(lthread, rthread);
  599.         llink = NextDown(llink);
  600.         rlink = NextDown(rlink);
  601.       }
  602.  
  603.       /* attach leftover back threads to first_bt if required */
  604.       if( rlink != bt[par] )
  605.       { 
  606.         if( still_backing )  TransferLinks(rlink, bt[par], first_bt);
  607.       }
  608.       DisposeObject(bt[par]);
  609.  
  610.       /* attach leftover forward threads to ft[par] if required */
  611.       if( llink != last_ft )
  612.       {
  613.         if( goes_through )  TransferLinks(llink, last_ft, ft[par]);
  614.       }
  615.       DisposeObject(last_ft);
  616.  
  617.       if( !goes_through )  still_backing = FALSE;
  618.  
  619.     }
  620.     else still_backing = FALSE;
  621.  
  622.       } /* end while */
  623.  
  624.       /* export par threads */
  625.       if( fthr[par] )  MergeNode(fthr[par], ft[par]);
  626.       if( bthr[par] )  MergeNode(bthr[par], first_bt);
  627.       break;
  628.  
  629.  
  630.     default:
  631.  
  632.       Error(INTERN, &fpos(x), "Manifest: no case for type %s", Image(type(x)));
  633.       break;
  634.  
  635.   }
  636.   else switch( type(x) )
  637.   {
  638.  
  639.     case WIDE:
  640.     case HIGH:
  641.     
  642.       Child(y, Down(x));
  643.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  644.       y = ReplaceWithTidy(y);
  645.       GetGap(y, style, &res_gap, &res_inc);
  646.       if( res_inc != GAP_ABS || mode(res_gap) != EDGE_MODE ||
  647.     units(res_gap) != FIXED_UNIT )
  648.       {    Error(WARN, &fpos(y), "replacing invalid left parameter of %s by 2i",
  649.             Image(type(x)) );
  650.     units(res_gap) = FIXED_UNIT;
  651.     width(res_gap) = 2*IN;
  652.       }
  653.       SetConstraint(constraint(x), MAX_LEN, width(res_gap), MAX_LEN);
  654.       DisposeChild(Down(x));
  655.       /* NB NO BREAK! */
  656.  
  657.  
  658.     case HCONTRACT:
  659.     case VCONTRACT:
  660.     case HEXPAND:
  661.     case VEXPAND:
  662.     case PADJUST:
  663.     case HADJUST:
  664.     case VADJUST:
  665.     case ONE_COL:
  666.     case ONE_ROW:
  667.     
  668.       par = (type(x)==ONE_COL || type(x)==HEXPAND || type(x) == HCONTRACT ||
  669.          type(x)==PADJUST || type(x)==HADJUST || type(x)==WIDE) ? COL : ROW;
  670.       Child(y, Down(x));
  671.  
  672.       /* manifest the child, propagating perp threads and suppressing pars */
  673.       bt[par] = ft[par] = nil;
  674.       bt[1-par] = bthr[1-par];  ft[1-par] = fthr[1-par];
  675.       y = Manifest(y, env, style, bt, ft, target, crs, ok, FALSE);
  676.  
  677.       /* replace with split object if par threads needed */
  678.       bt[par] = bthr[par];  ft[par] = fthr[par];
  679.       bt[1-par] = ft[1-par] = nil;
  680.       ReplaceWithSplit(x, bt, ft);
  681.       break;
  682.  
  683.  
  684.     case ROTATE:
  685.  
  686.       Child(y, Down(x));
  687.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  688.       y = ReplaceWithTidy(y);
  689.       GetGap(y, style, &res_gap, &res_inc);
  690.       if( res_inc != GAP_ABS || mode(res_gap) != EDGE_MODE ||
  691.         units(res_gap) != DEG_UNIT )
  692.       {    Error(WARN, &fpos(y), "replacing invalid left parameter of %s by 0d",
  693.             Image(type(x)) );
  694.     units(res_gap) = DEG_UNIT;
  695.     width(res_gap) = 0;
  696.       }
  697.       sparec(constraint(x)) = width(res_gap);
  698.       DisposeChild(Down(x));
  699.       Child(y, Down(x));
  700.       y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE);
  701.       ReplaceWithSplit(x, bthr, fthr);
  702.       break;
  703.  
  704.  
  705.     case HSCALE:
  706.     case VSCALE:
  707.  
  708.       Child(y, Down(x));
  709.       y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE);
  710.       ReplaceWithSplit(x, bthr, fthr);
  711.       break;
  712.  
  713.  
  714.     case SCALE:
  715.  
  716.       Child(y, Down(x));
  717.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  718.       y = ReplaceWithTidy(y);
  719.       if( type(y) != ACAT )
  720.       { scale_factor = GetScaleFactor(y, "scale factor");
  721.         bc(constraint(x)) = fc(constraint(x)) = scale_factor * SF;
  722.       }
  723.       else
  724.       {
  725.     /* get horizontal scale factor */
  726.     Child(z, Down(y));
  727.     scale_factor = GetScaleFactor(z, "horizontal scale factor");
  728.         bc(constraint(x)) = scale_factor * SF;
  729.  
  730.     /* get vertical scale factor */
  731.     Child(z, LastDown(y));
  732.     scale_factor = GetScaleFactor(z, "vertical scale factor");
  733.         fc(constraint(x)) = scale_factor * SF;
  734.       }
  735.       DisposeChild(Down(x));
  736.       Child(y, LastDown(x));
  737.       y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE);
  738.       ReplaceWithSplit(x, bthr, fthr);
  739.       break;
  740.  
  741.  
  742.     case YIELD:
  743.  
  744.       Error(FATAL, &fpos(x), "%s outside of %s", KW_YIELD, KW_CASE);
  745.       break;
  746.  
  747.  
  748.     case CASE:
  749.  
  750.       /* make sure left parameter (the tag) is in order */
  751.       debug0(DOM, DD, "  manifesting CASE now");
  752.       Child(tag, Down(x));
  753.       debug1(DOM, DD, "  manifesting CASE tag %s now", EchoObject(tag));
  754.       tag = Manifest(tag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  755.       tag = ReplaceWithTidy(tag);
  756.  
  757.       /* make sure the right parameter is an ACAT */
  758.       Child(y, LastDown(x));
  759.       if( type(y) == YIELD )
  760.       {    z = New(ACAT);
  761.     MoveLink(Up(y), z, PARENT);
  762.     Link(x, z);
  763.     y = z;
  764.       }
  765.       if( type(y) != ACAT )
  766.       {    Error(WARN, &fpos(y), "%s deleted: right parameter is malformed",
  767.       KW_CASE);
  768.     errorcase();
  769.       }
  770.  
  771.       /* hunt through right parameter for res, the selected case */
  772.       res = nil;  firsttag = nil;
  773.       for( ylink = Down(y); ylink != y && res == nil; ylink = NextDown(ylink) )
  774.       {    Child(yield, ylink);
  775.     if( type(yield) == GAP_OBJ )  continue;
  776.     if( type(yield) != YIELD )
  777.     { Error(WARN, &fpos(yield), "%s contains non-%s", KW_CASE, KW_YIELD);
  778.       break;
  779.     }
  780.     Child(ytag, Down(yield));
  781.     ytag = Manifest(ytag, env, style, nbt, nft, &ntarget, crs, FALSE,FALSE);
  782.     ytag = ReplaceWithTidy(ytag);
  783.     if( is_word(type(ytag)) )
  784.     { if( firsttag == nil )
  785.       { firsttag = ytag;
  786.         Child(firstres, LastDown(yield));
  787.       }
  788.       if( (is_word(type(tag)) && StringEqual(string(ytag), string(tag))) ||
  789.           StringEqual(string(ytag), STR_ELSE)  )
  790.       { Child(res, LastDown(yield));
  791.         break;
  792.       }
  793.     }
  794.     else if( type(ytag) == ACAT )
  795.     { z = ytag;
  796.       for( zlink = Down(z);  zlink != z;  zlink = NextDown(zlink) )
  797.       { Child(ytag, zlink);
  798.         if( type(ytag) == GAP_OBJ )  continue;
  799.         if( !is_word(type(ytag)) )
  800.         { Error(WARN, &fpos(ytag), "error in left parameter of %s",
  801.         KW_YIELD);
  802.           break;
  803.         }
  804.         if( firsttag == nil )
  805.         { firsttag = ytag;
  806.           Child(firstres, LastDown(yield));
  807.         }
  808.         if( (is_word(type(tag)) && StringEqual(string(ytag), string(tag)))
  809.             || StringEqual(string(ytag), STR_ELSE) )
  810.         { Child(res, LastDown(yield));
  811.           break;
  812.         }
  813.       }
  814.     }
  815.     else Error(WARN,&fpos(ytag), "error in left parameter of %s", KW_YIELD);
  816.       }
  817.       if( res == nil )
  818.       { if( firsttag != nil )
  819.     { Error(WARN, &fpos(tag), "replacing unkown %s option %s by %s",
  820.         KW_CASE, string(tag), string(firsttag));
  821.       res = firstres;
  822.     }
  823.     else
  824.     { Error(WARN, &fpos(tag), "%s deleted: selection %s unknown",
  825.         KW_CASE, string(tag));
  826.       errorcase();
  827.     }
  828.       }
  829.  
  830.       /* now manifest the result and replace x with it */
  831.       DeleteLink(Up(res));
  832.       ReplaceNode(res, x);
  833.       DisposeObject(x);
  834.       x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE);
  835.       break;
  836.  
  837.  
  838.     case XCHAR:
  839.  
  840.       Child(y, Down(x));
  841.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  842.       y = ReplaceWithTidy(y);
  843.       if( !is_word(type(y)) )
  844.       {    Error(WARN, &fpos(x), "%s dropped: parameter is not a simple word",
  845.       KW_XCHAR);
  846.     res = MakeWord(WORD, STR_EMPTY, &fpos(x));
  847.       }
  848.       else if( word_font(y) == 0 )
  849.       {    Error(WARN, &fpos(x), "%s dropped: no current font at this point",
  850.       KW_XCHAR);
  851.     res = MakeWord(WORD, STR_EMPTY, &fpos(x));
  852.       }
  853.       else if( (ch=EvRetrieve(string(y), FontEncoding(word_font(y)))) == '\0' )
  854.       {    type(y) = QWORD;
  855.     Error(WARN, &fpos(x), "%s dropped: character %s unknown in font %s",
  856.       KW_XCHAR, StringQuotedWord(y),
  857.       FontFamilyAndFace(word_font(y)));
  858.     res = MakeWord(WORD, STR_EMPTY, &fpos(x));
  859.       }
  860.       else
  861.       {    res = MakeWord(QWORD, STR_SPACE, &fpos(x));
  862.     string(res)[0] = ch;
  863.       }
  864.       ReplaceNode(res, x);
  865.       DisposeObject(x);
  866.       x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE);
  867.       break;
  868.  
  869.  
  870.     case FONT:
  871.     case SPACE:
  872.     case BREAK:
  873.     
  874.       assert( Down(x) != x && NextDown(Down(x)) != x, "Manifest: FONT!" );
  875.       StyleCopy(new_style, *style);
  876.       Child(y, Down(x));
  877.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  878.       y = ReplaceWithTidy(y);
  879.       if( type(x) == FONT )       FontChange(&new_style, y);
  880.       else if( type(x) == SPACE ) SpaceChange(&new_style, y);
  881.       else              BreakChange(&new_style, y);
  882.       DisposeChild(Down(x));
  883.       Child(y, Down(x));
  884.       y = Manifest(y, env, &new_style, bthr, fthr, target, crs, ok, FALSE);
  885.       DeleteLink(Down(x));
  886.       MergeNode(y, x);  x = y;
  887.       break;
  888.  
  889.  
  890.     case NEXT:
  891.  
  892.       assert( Down(x) != x, "Manifest/NEXT: Down(x) == x!" );
  893.       Child(y, Down(x));
  894.       debug1(DCS, D, "  Manifesting Next( %s, 1 )", EchoObject(y));
  895.       y = Manifest(y, env, style, bthr, fthr, target, crs, FALSE, FALSE);
  896.       debug1(DCS, D, "  calling Next( %s, 1 )", EchoObject(y));
  897.       done = FALSE;
  898.       y = Next(y, 1, &done);
  899.       debug2(DCS, D, "  Next(done = %s) returning %s",
  900.             bool(done), EchoObject(y));
  901.       DeleteLink(Down(x));
  902.       MergeNode(y, x);  x = y;
  903.       break;
  904.  
  905.  
  906.     case OPEN:
  907.  
  908.       Child(y, Down(x));
  909.       Child(res, LastDown(x));
  910.       if( type(y) == CLOSURE )
  911.       { AttachEnv(env, y);
  912.     StyleCopy(save_style(y), *style);
  913.     debug0(DCR, DD, "calling SetEnv from Manifest (b)");
  914.     res_env = SetEnv(y, nil);
  915.     hold_env = New(ACAT);  Link(hold_env, res_env);
  916.     res = Manifest(res, res_env, style, bthr, fthr, target, crs, ok, FALSE);
  917.     DisposeObject(hold_env);
  918.       }
  919.       else if( type(y) == CROSS )
  920.       {    debug0(DCR, DD, "  calling CrossExpand from Manifest/OPEN");
  921.     y = CrossExpand(y, env, style, TRUE, crs, &res_env);
  922.     AttachEnv(res_env, y);
  923.     debug0(DCR, DD, "calling SetEnv from Manifest (c)");
  924.     res_env = SetEnv(y, env);
  925.     hold_env = New(ACAT);  Link(hold_env, res_env);
  926.     res = Manifest(res, res_env, style, bthr, fthr, target, crs, ok, FALSE);
  927.     DisposeObject(hold_env);
  928.       }
  929.       else
  930.       {    Error(WARN, &fpos(y), "invalid left parameter of %s", KW_OPEN);
  931.     res = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE);
  932.       }
  933.       ReplaceNode(res, x);
  934.       DisposeObject(x);
  935.       x = res;
  936.       break;
  937.  
  938.  
  939.     case TAGGED:
  940.  
  941.       /* make sure first argument is a cross-reference */
  942.       assert( Down(x) != x && NextDown(Down(x)) != x &&
  943.     NextDown(NextDown(Down(x))) == x, "Manifest TAGGED: children!" );
  944.       Child(y, Down(x));
  945.       if( type(y) != CROSS )
  946.       {    Error(WARN, &fpos(y), "left parameter of %s is not a cross-reference",
  947.       KW_TAGGED);
  948.     errorcase();
  949.       }
  950.  
  951.       /* make sure the arguments of the cross-reference are OK */
  952.       Child(z, Down(y));
  953.       if( type(z) != CLOSURE )
  954.       {    Error(WARN,&fpos(y),"left parameter of %s must be a symbol", KW_TAGGED);
  955.     errorcase();
  956.       }
  957.       if( !has_tag(actual(z)) )
  958.       {    Error(WARN, &fpos(z), "symbol %s illegal with %s since it has no %s",
  959.       SymName(actual(z)), KW_TAGGED, KW_TAG);
  960.     errorcase();
  961.       }
  962.       Child(z, NextDown(Down(y)));
  963.       z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  964.       z = ReplaceWithTidy(z);
  965.       if( is_word(type(z)) && StringEqual(string(z), KW_PRECEDING) )
  966.     cross_type(y) = CROSS_PREC;
  967.       else if( is_word(type(z)) && StringEqual(string(z), KW_FOLLOWING) )
  968.     cross_type(y) = CROSS_FOLL;
  969.       else
  970.       {    Error(WARN, &fpos(z), "%s of left parameter of %s must be %s or %s",
  971.       KW_TAG, KW_TAGGED, KW_PRECEDING, KW_FOLLOWING);
  972.     errorcase();
  973.       }
  974.  
  975.       /* make sure second argument (the new key) is ok */
  976.       Child(tag, LastDown(x));
  977.       tag = Manifest(tag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  978.       tag = ReplaceWithTidy(tag);
  979.       if( !is_word(type(tag)) )
  980.       {    Error(WARN, &fpos(tag), "right parameter of %s must be a simple word",
  981.       KW_TAGGED);
  982.     ifdebug(DOM, D, DebugObject(tag));
  983.     errorcase();
  984.       }
  985.  
  986.       /* assemble insinuated cross reference which replaces x */
  987.       ReplaceNode(tag, z);
  988.       DisposeObject(z);
  989.       ReplaceNode(y, x);
  990.       DisposeObject(x);
  991.       x = y;
  992.       ReplaceWithSplit(x, bthr, fthr);
  993.       break;
  994.  
  995.  
  996.     case GRAPHIC:
  997.  
  998.       debug1(DRS, DD, "  graphic style in Manifest = %s", EchoStyle(style));
  999.       Child(y, LastDown(x));
  1000.       y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE);
  1001.       StyleCopy(save_style(x), *style);
  1002.       Child(y, Down(x));
  1003.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  1004.       ReplaceWithSplit(x, bthr, fthr);
  1005.       break;
  1006.     
  1007.  
  1008.     case INCGRAPHIC:
  1009.     case SINCGRAPHIC:
  1010.  
  1011.       Child(y, Down(x));
  1012.       y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  1013.       y = ReplaceWithTidy(y);
  1014.       if( !is_word(type(y)) )
  1015.       { Error(WARN, &fpos(y), "%s deleted: invalid right parameter",
  1016.       type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC);
  1017.     errorcase();
  1018.       }
  1019.       ReplaceWithSplit(x, bthr, fthr);
  1020.       break;
  1021.     
  1022.  
  1023.     default:
  1024.  
  1025.       Error(INTERN, &fpos(x), "Manifest: no case for type %s", Image(type(x)));
  1026.       break;
  1027.  
  1028.   } /* end switch */
  1029.  
  1030.   debug2(DOM, D, "]Manifest returning %s %s", Image(type(x)), EchoObject(x));
  1031.   debug1(DOM, DD, "  at exit, style = %s", EchoStyle(style));
  1032.   debug1(DOM, DDD, "up:    ", EchoObject(bthr[COL]));
  1033.   debug1(DOM, DDD, "down:  ", EchoObject(fthr[COL]));
  1034.   debug1(DOM, DDD, "left:  ", EchoObject(bthr[ROW]));
  1035.   debug1(DOM, DDD, "right: ", EchoObject(fthr[ROW]));
  1036.   return x;
  1037. } /* end Manifest */
  1038.