1 // Copyright (c) 2001, 2004, 2006, 2016  Per M.A. Bothner.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.kawa.io;
5 import gnu.kawa.util.IntHashTable;
6 import java.io.*;
7 import gnu.mapping.ThreadLocation;
8 import gnu.lists.LList;
9 import gnu.lists.PrintConsumer;
10 import gnu.lists.Strings;
11 
12 /**
13  * A pretty printer.
14  *
15  * <p>
16  * This code is transcribed from pprint.lisp in Steel Bank Common Lisp,
17  * which is again based on the code in CMU Common Lisp.
18  * Modifications have been made to accommodate shared structures, as described
19  * in SRFI-38.
20  * </p>
21  * <p>
22  * The pretty printer (hereafter PP) is responsible for formatting the results
23  * of evaluating expressions so that the human user can easily read them. The PP
24  * may also be responsible for choosing where to break lines so that the output
25  * looks reasonable.
26  * </p>
27  * <p>
28  * Raw text that has not yet been written out is stored in the "buffer" array,
29  * while information about the structure of the text is stored in a queue of
30  * "formatting tokens" (see below) called queueInts. A circular queue is used
31  * when the PP is not dealing with shared structures, and a linear array is used
32  * when it is. This only effects how the formatting tokens are enqueued into the
33  * structure, and how the structure is dynamically expanded.
34  * </p>
35  * <p>
36  * Formatting tokens are simply contiguous chunks of the queue. All tokens have
37  * a type and a size. For example, there is a formatting token called
38  * {@code QITEM_NEWLINE_TYPE} whose size is specified by {@code QITEM_NEWLINE_SIZE}. The size
39  * includes these type and size bits. The tokens use the space allocated to them
40  * in different ways. For example, the tab token uses two of its chunks ({@code int}s)
41  * to specify at which column it was entered, and to which column it should
42  * expand to.
43  * </p>
44  * <p>
45  * The tokens are buffered by a display formatter. The display formatter walks
46  * the structure to be printed emitting these tokens when it finds it has to,
47  * as well as the actual characters to be printed! Remember, the formatting
48  * tokens are distinct from the actual characters written, they just control how the
49  * characters are displayed. To accommodate this dichotomy, the character buffer
50  * holds the actual characters as they appear to the display formatter, the queue
51  * holds the formatting tokens plus a mapping that specifies which character in
52  * the buffer they end at.
53  * </p>
54  * <p>
55  * Once the display formatter has finished walking the expression to be printed,
56  * the formatting queue is walked by the "destructive" methods, i.e., the ones
57  * that actually flush the buffer to the output port. As mentioned above, each
58  * token encodes the position it references in the character buffer, so we use
59  * this information to work our way through the buffer, occasionally modifying
60  * the buffer to look pretty (dictated by the formatting tokens). Examples of
61  * such modifications include changing a space character to a newline character
62  * so that the line will prettily fit on the specified line width, or more
63  * drastically, remove a shared object of the expression and replacing
64  * it will print-circle notation.
65  * </p>
66  * <p>
67  * Once everything has been flushed, the initial conditions are reset ready for
68  * the next printing
69  * </p>
70  *
71  * @author Per Bothner
72  * @author Charles Turner
73  */
74 
75 public class PrettyWriter extends PrintConsumer
76 {
77   /**
78    * Construct a PrettyWriter with {@code prettyPrintingMode = 1}
79    * @param out The output to write to
80    * @see #prettyPrintingMode
81    */
PrettyWriter(java.io.Writer out)82   public PrettyWriter(java.io.Writer out)
83   {
84     super(out);
85     setPrettyPrintingMode(1);
86   }
87   /**
88    * Construct a PrettyWriter which breaks on a given line length.
89    * If {@code lineLength} is strictly greater than one,
90    * {@code prettyPrintingMode} is set to 1, otherwise it's set to 0.
91    *
92    * @param out The output to write to
93    * @param lineLength The column width lines should break on
94    * @see #prettyPrintingMode #lineLength
95    */
PrettyWriter(java.io.Writer out, int lineLength)96   public PrettyWriter(java.io.Writer out, int lineLength)
97   {
98     super(out);
99     this.lineLength = lineLength;
100     setPrettyPrintingMode(lineLength > 1 ? 1 : 0);
101   }
102 
103   /**
104    * Construct a PrettyWriter.
105    * @param out The output port to write to
106    * @param prettyPrintingMode If {@code true} then {@code prettyPrintingMode = 1}
107    *        otherwise {@code prettyPrintingMode = 0}
108    */
PrettyWriter(java.io.Writer out, boolean prettyPrintingMode)109   public PrettyWriter(java.io.Writer out, boolean prettyPrintingMode)
110   {
111     super(out);
112     setPrettyPrintingMode(prettyPrintingMode ? 1 : 0);
113   }
114 
115   /** Line length we should format to. */
116   int lineLength = 80;
117 
118   /**
119    * If the right-hand-side margin is less than or equal to this value, the
120    * miser printing mode is entered. This is compact style of printing where
121    * line breaks are taken at every inter-token space.
122    * (1
123    *  2
124    *  3) is an example of a miser-style printing.
125    */
126   int miserWidth = 40;
127 
128     boolean isDomTerm;
129     @Override
isDomTerm()130     public boolean isDomTerm() { return isDomTerm; }
131 
132   /**
133    * This variable is used to determine how the queueInts array should be
134    * expanded. When printing with shared structure, we don't want a circular
135    * queue, since we rely on being able to reference formatting tokens in
136    * previous queue slots. When the queue has wrapped and expanded several times,
137    * keeping track of such references is a nightmare.
138    */
139   boolean sharing = false;
140 
141   /**
142    * When the display formatter has written a back-reference, we know for certain
143    * that print-circle notation has been emitted.
144    * This variable is used to avoid the expensive resolveBackReferences method
145    * from running in the case that we have a non-recursive object.
146    */
147   boolean reallySharing = false;
148 
149   public static ThreadLocation lineLengthLoc
150     = new ThreadLocation("line-length");
151   public static ThreadLocation miserWidthLoc
152     = new ThreadLocation("miser-width");
153   public static ThreadLocation indentLoc
154     = new ThreadLocation("indent");
155 
156   /**
157    * Whether to resolve shared structures is dependent on the out:print-circle
158    * command line switch being set true. The PrettyWriter uses a different
159    * data structure to resolve back-references when we're emitting print-circle
160    * notation.
161    */
162   public static ThreadLocation isSharing
163     = new ThreadLocation("print-circle");
164 
165   /** The current pretty-printing mode.
166    * See setPrettyPrintingMode for valid values. */
167   private int prettyPrintingMode;
168 
169   private IntHashTable idhash;
170 
initialiseIDHash()171     public boolean initialiseIDHash() {
172         Object share = isSharing.get(null); // FIXME ???
173         if (idhash != null)
174             return false;
175         idhash = new IntHashTable();
176         return true;
177     }
178 
clearIDHash()179   public void clearIDHash()
180   {
181     idhash.clear();
182   }
183 
finishIDHash()184     public void finishIDHash() {
185         idhash.clear();
186         writeEndOfExpression();
187         resolveBackReferences();
188         flush();
189         idhash = null;
190     }
191 
IDHashLookup(Object obj)192   public int IDHashLookup(Object obj) {
193     return idhash.lookup(obj);
194   }
195 
IDHashGetFromIndex(int index)196   public int IDHashGetFromIndex(int index) {
197     return idhash.getFromIndex(index);
198   }
199 
IDHashPutAtIndex(Object obj, int value, int index)200   public int IDHashPutAtIndex(Object obj, int value, int index) {
201     return idhash.putAtIndex(obj, value, index);
202   }
203 
IDHashRemove(Object obj)204   public int IDHashRemove(Object obj) {
205     return idhash.remove(obj);
206   }
207 
208   /** Control pretty-printing mode.
209    * @param mode the value 0 disables pretty-printing;
210    *   the value 1 enables explicit pretty-printing;
211    *   the value 2 enables pretty-printing with auto-fill, which means that
212    *   spaces are treated like enqueing NEWLINE_SPACE (essentiall a 'fill').
213    */
setPrettyPrintingMode(int mode)214     public void setPrettyPrintingMode (int mode) {
215         prettyPrintingMode = mode;
216     }
217 
setSharing(boolean sharing)218     public void setSharing (boolean sharing) {
219         this.sharing = sharing;
220     }
221 
222   /** Return pretty-printing mode.
223    * @return 0, 1, 2, as described for {@link #setPrettyPrintingMode(int)}.
224    */
getPrettyPrintingMode()225   public int getPrettyPrintingMode () { return prettyPrintingMode; }
226 
227   /** Is pretty printing enabled? */
isPrettyPrinting()228   public boolean isPrettyPrinting () { return prettyPrintingMode > 0; }
229 
230   /** Turn pretty printing on or off.
231    * Equivalent to {@code setPrettyPrintingMode(mode?1:0)}.
232    */
setPrettyPrinting(boolean mode)233   public void setPrettyPrinting (boolean mode)
234   {
235     setPrettyPrintingMode(mode ? 1 : 0);
236   }
237 
238     /** Should we write directly to out without using buffer?
239      * Currently, this is always false, but if you want to support
240      * "unbuffered mode", you need to change this.
241      */
isPassingThrough()242     private boolean isPassingThrough() {
243         return buffer.length == 0 && out != null;
244     }
245 
246     /** Holds all the text that has been output but not yet printed. */
247     private char[] buffer = new char[2048];
getBuffer()248     char[] getBuffer() { return buffer; }
249 
250     /** The index into BUFFER where more text should be put. */
251     private int bufferFillPointer;
getFillIndex()252     int getFillIndex() { return bufferFillPointer; }
setFillIndex(int index)253     void setFillIndex(int index) { bufferFillPointer = index; }
254 
255   /** Total amount of stuff that has been shifted out of the buffer.
256    * Whenever we output stuff from the buffer, we shift the remaining noise
257    * over. This makes it difficult to keep references to locations in
258    * the buffer. */
259   int bufferOffset;
260 
261   /** The column the first character in the buffer will appear in.
262    * Normally zero, but if we end up with a very long line with no breaks in it
263    * we might have to output part of it. Then this will no longer be zero.
264    * Ditto after emitting a prompt. */
265   int bufferStartColumn;
266 
267   /** The line number we are currently on. Used for *print-lines* abrevs and
268    * to tell when sections have been split across multiple lines. */
269   int lineNumber;
270 
271   // There are three different units for measuring character positions:
272   //   COLUMN - offset (in characters) from the start of the current line.
273   //   INDEX - index into the output buffer.
274   //   POSN - some position in the stream of characters cycling through
275   //          the output buffer.
276 
277   /**
278    * Return the adjusted index into the {@link #buffer}. The position is adjusted
279    * such this index will point to data that has not already been printed.
280    * @param index The index into the {@link #buffer}
281    * @return The adjusted index accounting for already written data
282    */
indexPosn(int index)283   private int indexPosn (int index)
284   {
285     return index + bufferOffset;
286   }
287 
288   /**
289    * Return the "real" index to {@link #buffer} which may index data already
290    * written. Useful for QITEM_POSN's
291    * @param posn The adjusted index into the {@link #buffer}
292    * @return  The "real" index into the {@link #buffer}
293    * @see #indexPosn(int)
294    */
posnIndex(int posn)295   private int posnIndex (int posn)
296   {
297     return posn - bufferOffset;
298   }
299 
300   /**
301    * Returns the index of a QITEM position relative to the output buffer
302    * @param posn The QITEM position to be processed
303    * @return The position of this QITEM relative to the {@link #buffer}
304    */
posnColumn(int posn)305   private int posnColumn (int posn)
306   {
307     return indexColumn(posnIndex(posn));
308   }
309 
310   /** Stack of logical blocks in effect at the buffer start.
311    * I.e. blocks for which {@code reallyStartLogicalBlock} has been called.
312    * Each block uses {@code LOGICAL_BLOCK_LENGTH} {@code int} in this array. */
313   int[] blocks = new int[10 * LOGICAL_BLOCK_LENGTH];
314   /** Number of {@code int}s used by each block in the {@code blocks} array. */
315   static final private int LOGICAL_BLOCK_LENGTH = 6;
316   static final private int BLOCK_START_COLUMN = -1;
317   static final private int BLOCK_SECTION_COLUMN = -2;
318   static final private int BLOCK_PER_LINE_PREFIX_END = -3;
319   static final private int BLOCK_PREFIX_LENGTH = -4;
320   static final private int BLOCK_SUFFIX_LENGTH = -5;
321   static final private int BLOCK_SECTION_START_LINE = -6;
322   /** The "stack pointer" in the {@code blocks} array. */
323   int blockDepth = LOGICAL_BLOCK_LENGTH;
324 
325   /** Buffer holding the per-line prefix active at the buffer start.
326    * Indentation is included in this. The length of this is stored
327    * in the logical block stack. */
328   char[] prefix = new char[128];
329 
330   /** Buffer holding the total remaining suffix active at the buffer start.
331    * The characters are right-justified in the buffer to make it easier
332    * to output the buffer. The length is stored in the logical block stack. */
333   char[] suffix = new char[128];
334 
335   static final int QUEUE_INIT_ALLOC_SIZE = 300; // FIXME
336 
337   /** A queue of pending operations.
338    * This is primarily stored in the circular buffer queueInts.  There
339    * are different kinds of operation types, and each operation can
340    * require a variable number of elements in the buffer, depending on
341    * the operation type.  Given an operation at 'index', the type
342    * operation type code is 'getQueueType(index)' (one of the
343    * QITEM_XXX_TYPE macros below), and the number of elements in the
344    * buffer is 'getQueueSize(index)' (one of the QITEM_XXX_SIZE values
345    * below).  You can think of the various QITEM_XXX_TYPEs as
346    * "sub-classes" of queued operations, but instead of creating
347    * actual Java objects, we allocate the objects' fields in the
348    * queueInts and QueueStrings arrays, to avoid expensive object
349    * allocation.  The special QITEM_NOP_TYPE is a used as a marker for
350    * when there isn't enough space in the rest of buffer, so we have
351    * to wrap around to the start.  The other QITEM_XXX macros are the
352    * offsets of the various "fields" relative to the start index. */
353   int[] queueInts = new int[QUEUE_INIT_ALLOC_SIZE];
354 
355   /** For simplicity, queueStrings is the same size as queueInts. */
356   String[] queueStrings = new String[QUEUE_INIT_ALLOC_SIZE];
357   /** Index in queueInts and queueStrings of oldest enqueued operation. */
358   int queueTail;
359   /** Number of elements (in queueInts and queueStrings) in use. */
360   private int queueSize;
361   /** If >= 0, index (into queueInts) of current unclosed begin-block node.
362    * This is a head of a linked linked of queued BLOCK_START for which
363    * we haven't seen the matching BLOCK_END  */
364   int currentBlock = -1;
365   /** Number of startLogicalBlock - number of endLogicalBlock. */
366   public int pendingBlocksCount;
367 
368   /**
369    * The queue types and respective sizes are packed into a 32-bit integer.
370    *
371    * The most-significant 16-bits of a QITEM defines the type code. This type
372    * code is defined by one of the QITEM_.+_TYPE fields below.
373    * The least significant 8-bits of QITEM defines the type size. This size
374    * is defined by one of the QITEM_.+_SIZE fields below.
375    * Bits (8, 16] are currently used to store flags for their respective queue
376    * types
377    * The only exception to this rule is QITEM_NOP_TYPE, which acts as a marker
378    * for when there isn't enough space in the rest of the buffer.
379    * All other QITEMs have in the second word (at offset QITEM_POSN)
380    * the corresponding text position as a POSN.
381    */
382 
383   /** The first int of a QITEM contains its type code and size. */
384   static final int QITEM_TYPE_AND_SIZE = 0;
getQueueType(int index)385   private int getQueueType(int index) { return queueInts[index] & 0xFF; }
getQueueSize(int index)386   private int getQueueSize(int index) { return queueInts[index] >> 16; }
387   /** Relative offset of POSN field of a QITEM. */
388   static final int QITEM_POSN = 1;
389   /** Size of "base part" of a QITEM. The type and size packing, plus the position */
390   static final int QITEM_BASE_SIZE = 2;
391 
392   /** A dummy queue item used at the high end of the queue buffer
393    * when there isn't enough space for the needed queue item. */
394   static final int QITEM_NOP_TYPE = 0;
395 
396   /** "Abstract" type for beginning of section.
397    * A section is from a block-start to a newline, from a newline to
398    * the next newline (in the same block?), or from a newline to
399    * the block end (?). */
400   /*static final int QITEM_SECTION_START_TYPE = 1;*/
401   static final int QITEM_SECTION_START_SIZE = QITEM_BASE_SIZE + 2;
402   static final int QITEM_SECTION_START_DEPTH = QITEM_BASE_SIZE;
403   static final int QITEM_SECTION_START_SECTION_END = QITEM_BASE_SIZE + 1;
404 
405   /** A newline queue item. */
406   static final int QITEM_NEWLINE_TYPE = 2;
407   static final int QITEM_NEWLINE_SIZE = QITEM_SECTION_START_SIZE + 1;
408   static final int QITEM_NEWLINE_KIND = QITEM_SECTION_START_SIZE;
409   public static final int NEWLINE_LINEAR = 'N';
410   public static final int NEWLINE_LITERAL = 'L';
411   public static final int NEWLINE_FILL = 'F';
412   /** A non-nested ' ' gets an implicit NEWLINE_SPACE.
413    * This is treated similarly to NEWLINE_FILL, but not quite. */
414   public static final int NEWLINE_SPACE = 'S';
415   public static final int NEWLINE_MISER = 'M';
416   public static final int NEWLINE_MANDATORY = 'R';  // "required"
417   /** Treat like NEWLINE_LITERAL in terms of ending sections,
418    * but don't actually print anything. */
419   public static final int NEWLINE_DUMMY = 'D';
420 
421   static final int QITEM_INDENTATION_TYPE = 3;
422   static final int QITEM_INDENTATION_SIZE = QITEM_BASE_SIZE + 2;
423   static final int QITEM_INDENTATION_KIND = QITEM_BASE_SIZE;
424   static final char QITEM_INDENTATION_BLOCK = 'B';
425   static final char QITEM_INDENTATION_CURRENT = 'C';
426   static final int QITEM_INDENTATION_AMOUNT = QITEM_BASE_SIZE + 1;
427 
428   /** A "block-start" queue item. */
429   static final int QITEM_BLOCK_START_TYPE = 4;
430   static final int QITEM_BLOCK_START_SIZE = QITEM_SECTION_START_SIZE + 3;
431   /** If the QITEM_BLOCK_START_BLOCK_END < 0, it points to
432    * the previous (outer) un-closed block-start.
433    * If QITEM_BLOCK_START_BLOCK_END > 0, it points to the
434    * corresponding block-end node.
435    * In both cases the pointers are relative to the current BLOCK_START. */
436   static final int QITEM_BLOCK_START_BLOCK_END = QITEM_SECTION_START_SIZE;
437   static final int QITEM_BLOCK_START_PREFIX = QITEM_SECTION_START_SIZE + 1;
438   static final int QITEM_BLOCK_START_SUFFIX = QITEM_SECTION_START_SIZE + 2;
439 
440   static final int QITEM_BLOCK_END_TYPE = 5;
441   static final int QITEM_BLOCK_END_SIZE = QITEM_BASE_SIZE;
442 
443   static final int QITEM_TAB_TYPE = 6;
444   static final int QITEM_TAB_SIZE = QITEM_BASE_SIZE + 3;
445   static final int QITEM_TAB_FLAGS = QITEM_BASE_SIZE;
446   static final int QITEM_TAB_IS_SECTION = 1;
447   static final int QITEM_TAB_IS_RELATIVE = 2;
448   static final int QITEM_TAB_COLNUM = QITEM_BASE_SIZE + 1;
449   static final int QITEM_TAB_COLINC = QITEM_BASE_SIZE + 2;
450 
451   /**
452    * Position markers are used to "name" shared parts of an expression.
453    * For instance in #1=(a #1# c), #1= is the position marker.
454    * The GROUP_TYPE is used as a bit mask to determine from the type+size
455    * packing whether this position marker is grouping a sub-object. See the
456    * PAIR_END_TYPE comments below for a more detailed description of this.
457    */
458   static final int QITEM_POSNMARKER_TYPE = 7;
459   static final int QITEM_POSNMARKER_GROUP_TYPE = 8;
460   static final int QITEM_POSNMARKER_SIZE = QITEM_BASE_SIZE + 1;
461   /** The datum label to use for this object, or 0 if no label.
462    * The label is initially 0, indicating no back-references.
463    * It is set to 1 when a back-reference is seen.
464    * Finally resolveBackReferences sets it to a ordinal that gets written
465    * out - i.e. the N in {@code #N#}.
466    */
467   static final int QITEM_POSNMARKER_LOC = QITEM_BASE_SIZE;
468 
469   /**
470    * Represents a back reference to a value defined by a previous QITEM_POSNMARKER
471    * @param posn The index into the {@link #queueInts} array of the position
472    * marker
473    * @return true if the position marker has been referenced.
474    */
475   static final int QITEM_BACKREF_TYPE = 8;
476   static final int QITEM_BACKREF_SIZE = QITEM_BASE_SIZE + 1;
477   static final int QITEM_BACKREF_TARGET = QITEM_BASE_SIZE;
478 
479   /**
480    * Pair end types are also linked to position markers. If the position
481    * marker is found to be grouping (i.e. that it references a non-initial
482    * sublist) the printer must emit an extra closing parenthesis so that the
483    * position marker correctly points to referenced sublist.
484    *
485    * '(a *b c d*)..
486    *
487    * For instance, if the '(b c d) sublist was found to be a reference, we would
488    * need to add extra parenthesis around the sublist like so,
489    *
490    * '(a . #N=(b c d))...
491    *
492    * These types deal with the closing parenthesis of such cases.
493    */
494   static final int QITEM_PAIR_END_TYPE = 9;
495   static final int QITEM_PAIR_END_SIZE = QITEM_BASE_SIZE + 1;
496   static final int QITEM_PAIR_END_REF = QITEM_BASE_SIZE;
497 
498   /**
499    * When performing a shared structure printing, with a prettyPrintingMode of
500    * zero, the only queueItem tokens contained in the string are newline tokens.
501    * This isn't enough for the back-reference resolver. This token is appended
502    * to the end of an s-exp so that the resolver will be able consume all of the
503    * expression.
504    */
505   static final int QITEM_EOE_TYPE = 10;
506   static final int QITEM_EOE_SIZE = QITEM_BASE_SIZE;
507 
508   /**
509    * To determine whether the position maker should group what it references.
510    * This is necessary when the CDR of a non-initial pair has been found to be
511    * referenced. We explout some unused space in the queue item
512    * type+size packing to accomodate this information.
513    * @param posn The position marker under test
514    * @return true if this position marker should group
515    */
posnMarkerIsGrouping(int posn)516   private boolean posnMarkerIsGrouping (int posn)
517   {
518     return ((queueInts[posn] >> QITEM_POSNMARKER_GROUP_TYPE) & 1) != 0;
519   }
520 
521   /**
522    * To determine whether a position marker has been referenced
523    * @param posn The index into the {@link #queueInts} array of the position
524    * marker
525    * @return true if the position marker has been referenced.
526    */
posnMarkerActive(int posn)527   private boolean posnMarkerActive (int posn)
528   {
529     return queueInts[posn + QITEM_POSNMARKER_LOC] != 0;
530   }
531 
getSectionColumn()532   private int getSectionColumn()
533   {
534     return blocks[blockDepth+BLOCK_SECTION_COLUMN];
535   }
536 
getStartColumn()537   private int getStartColumn()
538   {
539     return blocks[blockDepth+BLOCK_START_COLUMN];
540   }
541 
getPerLinePrefixEnd()542   private int getPerLinePrefixEnd()
543   {
544     return blocks[blockDepth+BLOCK_PER_LINE_PREFIX_END];
545   }
546 
getPrefixLength()547   private int getPrefixLength()
548   {
549     return blocks[blockDepth+BLOCK_PREFIX_LENGTH];
550   }
551 
getSuffixLength()552   private int getSuffixLength()
553   {
554     return blocks[blockDepth+BLOCK_SUFFIX_LENGTH];
555   }
556 
getSectionStartLine()557   private int getSectionStartLine()
558   {
559     return blocks[blockDepth+BLOCK_SECTION_START_LINE];
560   }
561 
562   boolean wordEndSeen;
563 
564   /** Note the end of a "word".  See {@link #writeWordStart}. */
writeWordEnd()565   public void writeWordEnd ()
566   {
567     wordEndSeen = true;
568   }
569 
570   /** Maybe write a word-separating space.
571    * Specifically, write a space if the previous output
572    * was {@link #writeWordEnd}.  Otherwise, do nothing.
573    */
writeWordStart()574   public void writeWordStart ()
575   {
576     if (wordEndSeen)
577       write(' ');
578     wordEndSeen = false;
579   }
580 
581     @Override
clearWordEnd()582     protected void clearWordEnd () {
583         wordEndSeen = false;
584     }
585 
586   /**
587    * Write a character to the buffer. If we're pretty printing and the character
588    * is a space character, two cases arise. If the character is a newline, then
589    * enqueue a NEWLINE_LITERAL, which has to used. If the character is a space,
590    * then we may at our discretion place a newline there if the printing
591    * requires that we do so. Note that this doesn't mean we *will* print
592    * a newline, just that we can. For other characters, we just populate the
593    * buffer.
594    * @param ch The character to write.
595    */
write(int ch)596   public void write (int ch)
597   {
598     wordEndSeen = false;
599     //log("{WRITE-ch: "+((char)ch)+"}");
600     if (isPassingThrough()) {
601         if (ch == '\n' || ch == '\r')
602             bufferStartColumn = 0;
603         writeToBase(ch);
604     }
605     else if (ch == '\n' && prettyPrintingMode > 0)
606       enqueueNewline(NEWLINE_LITERAL);
607     else
608       {
609 	ensureSpaceInBuffer(1);
610 	int fillPointer = bufferFillPointer;
611 	buffer[fillPointer] = (char) ch;
612 	bufferFillPointer = 1 + fillPointer;
613 	if (ch == ' ' && prettyPrintingMode > 1 && currentBlock < 0)
614 	  enqueueNewline(NEWLINE_SPACE);
615       }
616   }
617 
write(String str)618   public void write (String str)
619   {
620     write(str, 0, str.length());
621   }
622 
623   /**
624    * Write a (sub)string to the output buffer. Newlines and spaces are handled,
625    * and appropriate formatting token emitted in such cases.
626    *
627    * @param str The string to use
628    * @param start Where to start in the string
629    * @param count The number of character to write from the string
630    */
write(String str, int start, int count)631   public void write (String str, int start, int count)
632   {
633     wordEndSeen = false;
634     //log("{WRITE-str: "+str.substring(start, start+count)+"}");
635     if (isPassingThrough()) {
636         for (int i = count; ; ) {
637             if (--i < 0) {
638                 bufferStartColumn += count;
639                 break;
640             }
641             char ch = str.charAt(start+i);
642             if (ch == '\r' || ch == '\n') {
643                 bufferStartColumn = count - (i + 1);
644                 break;
645             }
646         }
647         writeToBase(str, start, count);
648         return;
649     }
650     while (count > 0)
651       {
652 	int cnt = count;
653 	// May allocate for space than we need (if the buffer gets fluhed).  FIXME
654 	int available = ensureSpaceInBuffer(count);
655 	if (cnt > available)
656 	  cnt = available;
657 	int fillPointer = bufferFillPointer;
658 	count -= cnt;
659 	while (--cnt >= 0)
660 	  {
661 	    char ch = str.charAt(start++);
662 	    if (ch == '\n' && prettyPrintingMode > 0)
663 	      {
664 		bufferFillPointer = fillPointer;
665 		enqueueNewline(NEWLINE_LITERAL);
666 		fillPointer = bufferFillPointer;
667 	      }
668 	    else
669 	      {
670 		buffer[fillPointer++] = (char) ch;
671 		if (ch == ' ' && prettyPrintingMode > 1 && currentBlock < 0)
672 		  {
673 		    bufferFillPointer = fillPointer;
674 		    enqueueNewline(NEWLINE_SPACE);
675 		    fillPointer = bufferFillPointer;
676 		  }
677 	      }
678 	  }
679 	bufferFillPointer = fillPointer;
680       }
681   }
682 
write(char[] str)683   public void write (char[] str)
684   {
685     write(str, 0, str.length);
686   }
687 
write(char[] str, int start, int count)688   public void write (char[] str, int start, int count)
689   {
690     wordEndSeen = false;
691     //log("{WRITE: "+new String(str, start, count)+"}");
692     if (isPassingThrough()) {
693         for (int i = count; ; ) {
694             if (--i < 0) {
695                 bufferStartColumn += count;
696                 break;
697             }
698             char ch = str[start+i];
699             if (ch == '\r' || ch == '\n') {
700                 bufferStartColumn = count - (i + 1);
701                 break;
702             }
703         }
704         writeToBase(str, start, count);
705         return;
706     }
707     int end = start + count;
708   retry:
709     while (count > 0)
710       {
711 	// Look for newline.  Should be merged with following loop.  FIXME.
712 	for (int i = start;  i < end;  i++)
713 	  {
714 	    char c;
715 	    if (prettyPrintingMode > 0
716 		&& ((c = str[i]) == '\n'
717 		    || (c == ' ' && currentBlock < 0)))
718 	      {
719 		write(str, start, i - start); // Recurse
720 		write(c);
721 		start = i + 1;
722 		count = end - start;
723 		continue retry;
724 	      }
725 	  }
726 
727 	for (;;)
728 	  {
729 	    int available = ensureSpaceInBuffer(count);
730 	    int cnt = available < count ? available : count;
731 	    int fillPointer = bufferFillPointer;
732 	    int newFillPtr = fillPointer + cnt;
733 	    for (int i = fillPointer;  i < newFillPtr;  i++)
734 	      buffer[i] = str[start++];
735 	    bufferFillPointer = newFillPtr;
736 	    count -= cnt;
737 	    if (count == 0)
738 	      break;
739 	  }
740       }
741   }
742 
743     private void writeToBase(char[] buf, int start, int count) {
744         try {
745             //log("writeToBase: "+ gnu.lists.Strings.toJson(new String(buf,start,count)));
746             out.write(buf, start, count);
747         } catch (IOException ex) {
748             throw new RuntimeException(ex);
749         }
750     }
751 
752     private void writeToBase(String str, int start, int count) {
753         try {
754             //log("writeToBase: "+ gnu.lists.Strings.toJson(str.substring(start,start+count)));
755             out.write(str, start, count);
756         } catch (IOException ex) {
757             throw new RuntimeException(ex);
758         }
759     }
760 
761     private void writeToBase(String str) {
762         writeToBase(str, 0, str.length());
763     }
764 
765     private void writeToBase(int ch) {
766         try {
767             //log("writeToBase: "+ gnu.lists.Strings.toJson(String.valueOf((char) ch))+"="+ch);
768             out.write(ch);
769         } catch (IOException ex) {
770             throw new RuntimeException(ex);
771         }
772     }
773 
774   /**
775    * A position marker is queued for every Scheme object that *could* refer
776    * to itself (or other parts of the structure).
777    * Currently, this applies only to vectors and lists. For each
778    * such object, the printer records it. All position markers are assumed
779    * inactive (i.e. not back-referenced) until proven otherwise. Some position
780    * markers are required to group new sublists, the grouping variable
781    * notes this.
782    * @return Integer The index in {@link #queueInts} where this marker resides
783    *                 it's used for back-reference lookup.
784    * @see #writeBackReference(int)
785    */
786   public int writePositionMarker (boolean grouping)
787   {
788     int result = enqueue(QITEM_POSNMARKER_TYPE, QITEM_POSNMARKER_SIZE);
789     // A zero implies we haven't assigned this position marker a count yet
790     queueInts[result + QITEM_POSNMARKER_LOC] = 0;
791     // Are we marking a non-initial sublist?
792     queueInts[result] |= (grouping ? 1 : 0) << QITEM_POSNMARKER_GROUP_TYPE;
793     // Return the index of this position marker in the queueInts
794     //log("POSNMARK: @"+result);
795     return result;
796   }
797 
798   /**
799    * Enqueue a back-reference token to the formatting queue.
800    *
801    * When the display formatter has detected that an object is referring to an
802    * "enclosing" object, a back-reference is queued. This makes the printer
803    * emit suitable notation to demonstrate the shared structure. The position
804    * marker this back-reference references is made active. If the structure
805    * to be printed was really sharing before, it is after this method.
806    * @param posn The index in the {@link #queueInts} array of the referenced
807    * position marker.
808    */
809   public void writeBackReference (int posn)
810   {
811     if (!reallySharing)
812       reallySharing = true;
813     int result = enqueue(QITEM_BACKREF_TYPE, QITEM_BACKREF_SIZE);
814     //log("BACKREF: @"+result+" references "+posn);
815     queueInts[result + QITEM_BACKREF_TARGET] = posn;
816     // The position marker this references is now active.
817     queueInts[posn + QITEM_POSNMARKER_LOC] = 1;
818   }
819 
820   /**
821    * Enqueue a pair-end token to the formatting queue.
822    *
823    * When a position marker is grouping it creates a new sublist, which of
824    * course needs a closing parenthesis that's not originally present in the
825    * structure to be printed. This token informs the back reference resolver
826    * to emit an extra closing parenthesis so that the reader will be happy.
827    *
828    * @param posn The index in the {@link #queueInts} array of the referenced
829    * position marker.
830    */
831   public void writePairEnd (Integer posn)
832   {
833     int result = enqueue(QITEM_PAIR_END_TYPE, QITEM_PAIR_END_SIZE);
834     //log("PAIREND: @"+result+" referencing: "+posn+" queueSize="+queueSize);
835     queueInts[result + QITEM_PAIR_END_REF] = posn;
836   }
837 
838   public void writeEndOfExpression ()
839   {
840     enqueue(QITEM_EOE_TYPE, QITEM_EOE_SIZE);
841   }
842 
843   private void pushLogicalBlock(int column,
844 				int perLineEnd,
845 				int prefixLength, int suffixLength,
846 				int sectionStartLine)
847   {
848     int newLength = blockDepth + LOGICAL_BLOCK_LENGTH;
849     if (newLength >= blocks.length)
850       {
851 	int[] newBlocks = new int[2 * blocks.length];
852 	System.arraycopy(blocks, 0, newBlocks, 0, blockDepth);
853 	blocks = newBlocks;
854       }
855     blockDepth = newLength;
856     blocks[blockDepth + BLOCK_START_COLUMN] = column;
857     blocks[blockDepth + BLOCK_SECTION_COLUMN] = column;
858     blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END] = perLineEnd;
859     blocks[blockDepth + BLOCK_PREFIX_LENGTH] = prefixLength;
860     blocks[blockDepth + BLOCK_SUFFIX_LENGTH] = suffixLength;
861     blocks[blockDepth + BLOCK_SECTION_START_LINE] = sectionStartLine;
862   }
863 
864   /**
865    *
866    * @param column the column of the output port where this block starts
867    * @param prefix the block's prefix
868    * @param suffix the block's suffix
869    */
870   void reallyStartLogicalBlock(int column, String prefix, String suffix)
871   {
872     int perLineEnd = getPerLinePrefixEnd();
873     int prefixLength = getPrefixLength();
874     int suffixLength = getSuffixLength();
875     pushLogicalBlock(column, perLineEnd, prefixLength, suffixLength,
876 		     lineNumber);
877     if (!isDomTerm())
878         setIndentation(column);
879     if (prefix != null)
880       {
881 	blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END] = column;
882 	int plen = prefix.length();
883 	prefix.getChars(0, plen, this.prefix, column - plen);
884       }
885     if (suffix != null)
886       {
887 	// Prepend the new suffix in front of the old suffix in this.suffix.
888 	// The suffix is stored at the "right" (high-index) end of
889 	// this.suffix to make it easier to prepend new suffixes.
890 	char[] totalSuffix = this.suffix;
891 	int totalSuffixLen = totalSuffix.length;
892 	int additional = suffix.length();
893 	int newSuffixLen = suffixLength + additional;
894 	if (newSuffixLen > totalSuffixLen)
895 	  {
896 	    int newTotalSuffixLen = enoughSpace(totalSuffixLen, additional);
897 	    this.suffix = new char[newTotalSuffixLen];
898 	    System.arraycopy(totalSuffix, totalSuffixLen - suffixLength,
899 			     this.suffix, newTotalSuffixLen - suffixLength,
900 			     suffixLength);
901 	    totalSuffixLen = newTotalSuffixLen;
902 	  }
903 	suffix.getChars(0, additional,
904 			totalSuffix, totalSuffixLen - newSuffixLen);
905 	blocks[blockDepth + BLOCK_SUFFIX_LENGTH] = newSuffixLen;
906       }
907 
908   }
909 
910   int enqueueTab (int flags, int colnum, int colinc) // DONE
911   {
912     int addr = enqueue(QITEM_TAB_TYPE, QITEM_TAB_SIZE);
913     queueInts[addr + QITEM_TAB_FLAGS] = flags;
914     queueInts[addr + QITEM_TAB_COLNUM] = colnum;
915     queueInts[addr + QITEM_TAB_COLINC] = colinc;
916     return addr;
917   }
918 
919   /** Calculate how much space to allocate for a buffer.
920    * @param current the current size of the buffer
921    * @param want how much more space is needed
922    */
923   private static int enoughSpace(int current, int want)
924   {
925     int scaled = current + (current>>1); // Multiply by 1.5
926     int enough = current + ((5 * want) >> 2);
927     return scaled > enough ? scaled : enough;
928   }
929 
setIndentation(int column)930   private void setIndentation(int column)
931   {
932     char[] prefix = this.prefix;
933     int prefixLen = prefix.length;
934     int current = getPrefixLength();
935     int minimum = getPerLinePrefixEnd();
936     if (minimum > column)
937       column = minimum;
938     if (column > prefixLen)
939       {
940 	prefix = new char[enoughSpace(prefixLen, column - prefixLen)];
941 	System.arraycopy(this.prefix, 0, prefix, 0, current);
942 	this.prefix = prefix;
943       }
944     if (column > current)
945       {
946 	for (int i = current;  i < column;  i++)
947 	  prefix[i] = ' ';
948       }
949     blocks[blockDepth + BLOCK_PREFIX_LENGTH] = column;
950   }
951 
reallyEndLogicalBlock()952   void reallyEndLogicalBlock ()
953   {
954     int oldIndent = getPrefixLength();
955     blockDepth -= LOGICAL_BLOCK_LENGTH;  // Pop
956     int newIndent = getPrefixLength();
957     if (newIndent > oldIndent)
958       {
959 	for (int i = oldIndent;  i < newIndent;  i++)
960 	  prefix[i] = ' ';
961       }
962   }
963 
964   /**
965    * Enqueue a formatting token into {@link #queueInts}.
966    *
967    * The kind and size parameters are packed into a 32-bit integer and then stored in the
968    * QITEM_TYPE_AND_SIZE offset of this token's computed base address. If the
969    * {@link #queueInts} array is not big enough to hold the new token, two cases
970    * arise. If we're not handling a shared structure, the queue is expanded, and
971    * the old formatting tokens are moved to the high end of the queue, leaving
972    * new space at the front. If we are handling shared structure, the queue
973    * size is double, and we continue to enqueue items from the old queueSize.
974    *
975    * @param kind The type of formatting token
976    * @param size The size of this formatting token
977    * @return The address of the token in {@link #queueInts}
978    */
enqueue(int kind, int size)979   public int enqueue (int kind, int size)
980   {
981     int oldLength = queueInts.length;
982     int endAvail = oldLength - queueTail - queueSize; // negative if wrapped
983     // If there are 5 int's left at the end of the queue, and we need to
984     // enqueue an item of size 7, then we don't "bend" the item around the
985     // queue, we just place a dummy formatting token that consumes the rest
986     // of this end, and start again from the beginning.
987     if (endAvail > 0 && size > endAvail && !sharing)
988       enqueue(QITEM_NOP_TYPE, endAvail);
989     if (endAvail < size && (sharing || oldLength - queueSize < size))
990       {
991         int enough = enoughSpace(oldLength, size);
992         int newLength = enough;
993 
994         int[] newInts = new int[newLength];
995         String[] newStrings = new String[newLength];
996 
997         if (sharing) // then do a linear array expansion
998           {
999             //log("sharing expand: oldLength="+oldLength+" newLength="+newLength+" queueTail="+queueTail);
1000             System.arraycopy(queueInts, 0, newInts, 0, queueInts.length);
1001             System.arraycopy(queueStrings, 0, newStrings, 0, queueStrings.length);
1002           }
1003         else // shift the old items to the top, and insert from the front of a doubled array
1004           {
1005             //log("non-sharing expand: oldLength="+oldLength+" newLength="+newLength+" queueTail="+queueTail);
1006 
1007             int queueHead = queueTail + queueSize - oldLength;
1008             if (queueHead > 0)
1009               { // Wraps around.
1010                 System.arraycopy(queueInts, 0, newInts, 0, queueHead);
1011                 System.arraycopy(queueStrings, 0, newStrings, 0, queueHead);
1012               }
1013             int part1Len = oldLength - queueTail;
1014             int deltaLength = newLength - oldLength;
1015             System.arraycopy(queueInts, queueTail,
1016                              newInts, queueTail + deltaLength,
1017                              part1Len);
1018             System.arraycopy(queueStrings, queueTail,
1019                              newStrings, queueTail + deltaLength,
1020                              part1Len);
1021 
1022             if (currentBlock >= queueTail)
1023               currentBlock += deltaLength;
1024             queueTail += deltaLength;
1025           }
1026         queueInts = newInts;
1027         queueStrings = newStrings;
1028       }
1029     int addr = queueTail + queueSize;
1030     // Wrap around if we're using a circular queue
1031     if (!sharing && addr >= queueInts.length)
1032       addr -= queueInts.length;
1033     queueInts[addr + QITEM_TYPE_AND_SIZE] = kind | (size << 16);
1034     if (size > 1) // then it's not a NOP, so needs a position
1035       queueInts[addr + QITEM_POSN] = indexPosn(bufferFillPointer);
1036     //log("enqueue "+itemKindString(kind)+" size:"+size+" at:"+queueSize+enqueueExtraLog); enqueueExtraLog = "";
1037     queueSize += size;
1038     return addr;
1039   }
1040 
1041   /**
1042    * Enqueue a newline formatting token.
1043    *
1044    * A pass is made through the formatting tokens up to this one, where section
1045    * sizes are computed. If we're not sharing, then a tentative output is done
1046    * when the current buffer cannot fit on one line.
1047    * @param kind The type of newline to enqueue
1048    */
enqueueNewline(int kind)1049   private void enqueueNewline (int kind)
1050   {
1051     wordEndSeen = false;
1052     int depth = pendingBlocksCount;
1053     //enqueueExtraLog = " kind:"+(char) kind;
1054     int newline = enqueue(QITEM_NEWLINE_TYPE, QITEM_NEWLINE_SIZE);
1055     queueInts[newline + QITEM_NEWLINE_KIND] = kind;
1056     queueInts[newline + QITEM_SECTION_START_DEPTH] = pendingBlocksCount;
1057     queueInts[newline + QITEM_SECTION_START_SECTION_END] = 0;
1058     int entry = queueTail;
1059     int todo = queueSize;
1060     while (todo > 0)
1061       {
1062 	if (entry == queueInts.length)
1063 	  entry = 0;
1064 	if (entry == newline)
1065 	  break;
1066 	int type = getQueueType(entry);
1067 	if ((type == QITEM_NEWLINE_TYPE
1068 	     || type == QITEM_BLOCK_START_TYPE)
1069 	    && queueInts[entry + QITEM_SECTION_START_SECTION_END] == 0
1070 	    && depth <= queueInts[entry + QITEM_SECTION_START_DEPTH])
1071 	  {
1072 	    int delta = newline - entry;
1073 	    if (delta < 0)
1074 	      delta += queueInts.length;
1075 	    queueInts[entry + QITEM_SECTION_START_SECTION_END] = delta;
1076 	  }
1077 	int size = getQueueSize(entry);
1078 	todo -= size;
1079 	entry += size;
1080       }
1081     if (!sharing) //!!
1082       maybeOutput (kind == NEWLINE_LITERAL || kind == NEWLINE_MANDATORY || kind == NEWLINE_DUMMY, false);
1083   }
1084 
writeBreak(int kind)1085   public final void writeBreak(int kind)
1086   {
1087     if (prettyPrintingMode > 0 || sharing) //!!
1088       enqueueNewline(kind);
1089   }
1090 
1091   /** Write a new-line iff the containing section cannot be printed
1092    * on one line.  Either all linear-style newlines in a logical
1093    * block becomes spaces (if it all fits in a line), or none
1094    * of them do. */
writeBreakLinear()1095   public void writeBreakLinear()
1096   {
1097     writeBreak(PrettyWriter.NEWLINE_LINEAR);
1098   }
1099 
enqueueIndent(char kind, int amount)1100   public int enqueueIndent (char kind, int amount)
1101   {
1102     //enqueueExtraLog = " kind:"+kind+" amount:"+amount;
1103     int result = enqueue(QITEM_INDENTATION_TYPE, QITEM_INDENTATION_SIZE);
1104     queueInts[result + QITEM_INDENTATION_KIND] = kind;
1105     queueInts[result + QITEM_INDENTATION_AMOUNT] = amount;
1106     return result;
1107   }
1108 
addIndentation(int amount, boolean current)1109   public void addIndentation(int amount, boolean current)
1110   {
1111     if (prettyPrintingMode > 0)
1112       enqueueIndent((current ? QITEM_INDENTATION_CURRENT
1113 		     : QITEM_INDENTATION_BLOCK),
1114 		    amount);
1115   }
1116 
startLogicalBlock(String prefix, boolean perLine, String suffix)1117   public void startLogicalBlock (String prefix, boolean perLine, String suffix)
1118   {
1119     // If the queue is empty, it is a good time to check if line-length etc
1120     // have been changed.
1121     if (queueSize == 0 && bufferFillPointer == 0)
1122       {
1123         Object llen = lineLengthLoc.get(null);
1124         if (llen == null)
1125           lineLength = 80;
1126         else
1127           lineLength = Integer.parseInt(llen.toString());
1128 
1129         Object mwidth = miserWidthLoc.get(null);
1130         if (mwidth == null || mwidth == Boolean.FALSE
1131             // For Common Lisp nil.  Should we use Language.isTrue() FIXME.
1132             || mwidth == LList.Empty)
1133           miserWidth = -1;
1134         else
1135           miserWidth = Integer.parseInt(mwidth.toString());
1136 
1137         Object indent = indentLoc.get(null);
1138         // if (indent == null || indent ...
1139       }
1140     if (prefix != null && ! (perLine && isDomTerm()))
1141       write(prefix);
1142     if (prettyPrintingMode == 0)
1143       return;
1144     int start = enqueue (QITEM_BLOCK_START_TYPE,
1145 			 QITEM_BLOCK_START_SIZE);
1146     queueInts[start + QITEM_SECTION_START_DEPTH] = pendingBlocksCount;
1147     queueStrings[start + QITEM_BLOCK_START_PREFIX]
1148       = perLine ? prefix : null;
1149     queueStrings[start + QITEM_BLOCK_START_SUFFIX] = suffix;
1150     pendingBlocksCount++;
1151     int outerBlock = currentBlock;
1152     if (outerBlock < 0)
1153       outerBlock = 0;
1154     else
1155       {
1156 	outerBlock -= start;
1157 	if (outerBlock > 0)
1158 	  outerBlock -= queueInts.length;
1159       }
1160     queueInts[start + QITEM_BLOCK_START_BLOCK_END] = outerBlock;
1161     queueInts[start + QITEM_SECTION_START_SECTION_END] = 0;
1162     currentBlock = start;
1163   }
1164 
startLogicalBlock(String prefix, String suffix, int indent)1165     public void startLogicalBlock(String prefix, String suffix,
1166                                   int indent) {
1167         startLogicalBlock(prefix, false, suffix);
1168         addIndentation(prefix == null ? indent
1169                        :  indent - prefix.length(),
1170                        false);
1171     }
1172 
endLogicalBlock()1173   public void endLogicalBlock ()
1174   {
1175     int end = enqueue (QITEM_BLOCK_END_TYPE, QITEM_BLOCK_END_SIZE);
1176     pendingBlocksCount--;
1177     if (currentBlock < 0)
1178       {
1179 	// reallyStartLogicalBlock has been called for the matching
1180 	// BEGIN_BLOCK, so it is no longer in the queue.  Instead it is in
1181 	// the 'blocks' stack.
1182 	int suffixLength = blocks[blockDepth+BLOCK_SUFFIX_LENGTH];
1183 	int suffixPreviousLength
1184 	  = blocks[blockDepth - LOGICAL_BLOCK_LENGTH + BLOCK_SUFFIX_LENGTH];
1185 	if (suffixLength > suffixPreviousLength)
1186 	  write(this.suffix,
1187 		this.suffix.length - suffixLength,
1188 		suffixLength - suffixPreviousLength);
1189 	currentBlock = -1;
1190 	return;
1191       }
1192     int start = currentBlock;
1193     int outerBlock = queueInts[start + QITEM_BLOCK_START_BLOCK_END];
1194     if (outerBlock == 0)
1195       currentBlock = -1;
1196     else
1197       {
1198 	int qtailFromStart = queueTail - start;
1199 	if (qtailFromStart > 0)
1200 	  qtailFromStart -= queueInts.length;
1201 	if (outerBlock < qtailFromStart)
1202 	  {
1203 	    // reallyStartLogicalBlock has been called for the outer block,
1204 	    // so there is no currentBlock.
1205 	    currentBlock = -1;
1206 	  }
1207 	else
1208 	  {
1209 	    // Make currentBlock absolute instead of relative.
1210 	    outerBlock += start;
1211 	    if (outerBlock < 0)
1212 	      outerBlock += queueInts.length;
1213 	    currentBlock = outerBlock;
1214 	  }
1215       }
1216     String suffix = queueStrings[start + QITEM_BLOCK_START_SUFFIX];
1217     if (suffix != null)
1218       write(suffix);
1219     int endFromStart = end - start;
1220     if (endFromStart < 0) // wrap-around.
1221       endFromStart += queueInts.length;
1222     queueInts[start + QITEM_BLOCK_START_BLOCK_END] = endFromStart;
1223     //log("endLogicalBlock end:"+end+" start:"+start+" rel:"+endFromStart);
1224   }
1225 
endLogicalBlock(String suffix)1226   public void endLogicalBlock (String suffix)
1227   {
1228     if (prettyPrintingMode > 0)
1229       endLogicalBlock();
1230     else if (suffix != null)
1231       write(suffix);
1232   }
1233 
1234   // Tab support
1235 
computeTabSize(int tab, int sectionStart, int column)1236   int computeTabSize (int tab, int sectionStart, int column) // DONE
1237   {
1238     int flags = queueInts[tab + QITEM_TAB_FLAGS];
1239     boolean isSection = (flags & QITEM_TAB_IS_SECTION) != 0;
1240     boolean isRelative = (flags & QITEM_TAB_IS_RELATIVE) != 0;
1241     int origin = isSection ? sectionStart : 0;
1242     int colnum = queueInts[tab + QITEM_TAB_COLNUM];
1243     int colinc = queueInts[tab + QITEM_TAB_COLINC];
1244     if (isRelative)
1245       {
1246 	if (colinc > 1)
1247 	  {
1248 	    int newposn = column + colnum;
1249 	    int rem = newposn % colinc;
1250 	    if (rem != 0)
1251 	      colnum += colinc = rem;
1252 	  }
1253 	return colnum;
1254       }
1255     else if (column <= colnum + origin)
1256       return column + origin - column;
1257     else
1258       return colinc - (column - origin) % colinc;
1259   }
1260 
indexColumn(int index)1261   int indexColumn(int index)
1262   {
1263     int column = bufferStartColumn;
1264     int sectionStart = getSectionColumn();
1265     int endPosn = indexPosn(index);
1266     int op = queueTail;
1267     int todo = queueSize;
1268     while (todo > 0)
1269       {
1270 	// If at end of queueInts, skip.
1271 	if (op >= queueInts.length)
1272 	  op = 0;
1273 	int type = getQueueType(op);
1274 	if (type != QITEM_NOP_TYPE)
1275 	  {
1276 	    int posn = queueInts[op + QITEM_POSN];
1277 	    if (posn >= endPosn)
1278 	      break;
1279 	    if (type == QITEM_TAB_TYPE)
1280 	      column += computeTabSize(op, sectionStart,
1281 				       column + posnIndex (posn));
1282 	    else if (type == QITEM_NEWLINE_TYPE
1283 		     || type == QITEM_BLOCK_START_TYPE)
1284 	      sectionStart
1285 		= column + posnIndex(queueInts[op + QITEM_POSN]);
1286 	  }
1287 	int size = getQueueSize(op);
1288 	todo -= size;
1289 	op += size;
1290       }
1291     return column + index;
1292   }
1293 
expandTabs(int through)1294   void expandTabs (int through)
1295   {
1296     int numInsertions = 0;
1297     int additional = 0;
1298     int column = bufferStartColumn;
1299     int sectionStart = getSectionColumn();
1300     int op = queueTail;
1301     int todo = queueSize;
1302     int blocksUsed = LOGICAL_BLOCK_LENGTH * pendingBlocksCount;
1303     while (todo > 0)
1304       {
1305 	if (op == queueInts.length)
1306 	  op = 0;
1307 	if (op == through)
1308 	  break;
1309 	int type = getQueueType(op);
1310 	if (type == QITEM_TAB_TYPE)
1311 	  {
1312 	    int index = posnIndex(queueInts[op + QITEM_POSN]);
1313 	    int tabsize = computeTabSize (op, sectionStart, column + index);
1314 	    if (tabsize != 0)
1315 	      {
1316 		// We use the blocks array for a temporary tab buffer.
1317 		if (blocksUsed + 2 * numInsertions + 1 >= blocks.length)
1318 		  {
1319 		    int[] newBlocks = new int[2 * blocks.length];
1320 		    System.arraycopy(blocks, 0, newBlocks, 0, blocks.length);
1321 		    blocks = newBlocks;
1322 		  }
1323 		blocks[blocksUsed + 2 * numInsertions] = index;
1324 		blocks[blocksUsed + 2 * numInsertions + 1] = tabsize;
1325 		numInsertions++;
1326 		additional += tabsize;
1327 		column += tabsize;
1328 	      }
1329 	  }
1330 	else if (op == QITEM_NEWLINE_TYPE || op == QITEM_BLOCK_START_TYPE)
1331 	  {
1332 	    sectionStart = column + posnIndex(queueInts[op + QITEM_POSN]);
1333 	  }
1334 	int size = getQueueSize(op);
1335 	todo -= size;
1336 	op += size;
1337       }
1338     if (numInsertions > 0)
1339       {
1340 	int fillPtr = bufferFillPointer;
1341 	int newFillPtr = fillPtr + additional;
1342 	char[] buffer = this.buffer;
1343 	char[] newBuffer = buffer;
1344 	int length = buffer.length;
1345 	int end = fillPtr;
1346 	if (newFillPtr > length)
1347 	  {
1348 	    int newLength = enoughSpace (fillPtr, additional);
1349 	    newBuffer = new char[newLength];
1350 	    this.buffer = newBuffer;
1351 	  }
1352 	bufferFillPointer = newFillPtr;
1353 	bufferOffset -= additional;
1354 	for (int i = numInsertions;  --i >= 0; )
1355 	  {
1356 	    int srcpos = blocks[blocksUsed + 2 * i];
1357 	    int amount = blocks[blocksUsed + 2 * i + 1];
1358 	    int dstpos = srcpos + additional;
1359 	    System.arraycopy(buffer, srcpos, newBuffer, dstpos, end - srcpos);
1360 	    for (int j = dstpos - amount;  j < dstpos;  j++)
1361 	      newBuffer[j] = ' ';
1362 	    additional -= amount;
1363 	    end = srcpos;
1364 	  }
1365 	if (newBuffer != buffer)
1366 	  System.arraycopy(buffer, 0, newBuffer, 0, end);
1367       }
1368   }
1369 
1370   // stuff to do the actual outputting
1371 
1372   /**
1373    * Make sure we can write into the buffer without overflowing it.
1374    *
1375    * If the current {@link #bufferFillPointer} is greater than the line length,
1376    * do a tentative output.
1377    * @param want How much space we need
1378    * @return The amount of space available, possible after flushes or expansion
1379    */
ensureSpaceInBuffer(int want)1380   int ensureSpaceInBuffer (int want)
1381   {
1382     char[] buffer = this.buffer;
1383     int length = buffer.length;
1384     int fillPtr = bufferFillPointer;
1385     int available = length - fillPtr;
1386     if (available > 0)
1387       return available;
1388     else if (out != null && fillPtr > lineLength && !sharing && ! isDomTerm())
1389       {
1390         if (! maybeOutput(false, false))
1391 	  outputPartialLine();
1392 	return ensureSpaceInBuffer(want);
1393       }
1394     else
1395       {
1396 	int newLength = enoughSpace(length, want);
1397 	char[] newBuffer = new char[newLength];
1398 	this.buffer = newBuffer;
1399 	for (int i = fillPtr;  --i >= 0; )
1400 	  newBuffer[i] = buffer[i];
1401 	return newLength - fillPtr;
1402       }
1403   }
1404 
1405   /**
1406    *
1407    * @param forceNewlines is true if we're printing a "literal" newline or if
1408    *                      the printer has decided that we need to force a
1409    *                      newline for a sensible formatting.
1410    * @param flushing is true when the buffer is actually written to the output
1411    *                 port. This occurs on {@link #flush()} operations for instance.
1412    * @return true if something has been output, false otherwise.
1413    */
maybeOutput(boolean forceNewlines, boolean flushing)1414   boolean maybeOutput(boolean forceNewlines, boolean flushing)
1415   {
1416     boolean outputAnything = false;
1417     //log("maybeOutput("+forceNewlines+","+flushing+"):");  dumpQueue();
1418   loop:
1419     while (queueSize > 0)
1420       {
1421 	if (queueTail >= queueInts.length)
1422 	  queueTail = 0;
1423 	int next = queueTail;
1424 	int type = getQueueType(next);
1425         if (isDomTerm()) {
1426             if (bufferFillPointer > 0 && type != QITEM_NEWLINE_TYPE && type != QITEM_NOP_TYPE) {
1427                 int count = posnIndex(queueInts[next+QITEM_POSN]);
1428                 if (count > 0) {
1429                     writeToBase(buffer, 0, count);
1430                     System.arraycopy(buffer, count, buffer, 0, bufferFillPointer-count);
1431                     bufferOffset += count;
1432                     bufferFillPointer -= count;
1433                     bufferStartColumn += count;
1434                 }
1435             }
1436 	switch (type)
1437 	  {
1438 	  case QITEM_NEWLINE_TYPE:
1439             outputAnything = true;
1440             outputLine(next, flushing);
1441 	    break;
1442 	  case QITEM_INDENTATION_TYPE:
1443             if (! isMisering())
1444 	      {
1445 		int kind = queueInts[next+QITEM_INDENTATION_KIND];
1446 		int indent = queueInts[next+QITEM_INDENTATION_AMOUNT];
1447                 boolean blockRelative = kind == QITEM_INDENTATION_BLOCK;
1448                 writeToBase("\033]"
1449                             +(blockRelative ? "113" : "112")
1450                             + ";" + indent + "\007");
1451 	      }
1452 	    break;
1453 	  case QITEM_BLOCK_START_TYPE:
1454             String prefix = queueStrings[next + QITEM_BLOCK_START_PREFIX];
1455             StringBuilder sbuf = new StringBuilder("\033]110;");
1456             if (prefix != null)
1457                 Strings.printJson(prefix, sbuf);
1458             sbuf.append("\007");
1459             writeToBase(sbuf.toString());
1460             String suffix = queueStrings[next + QITEM_BLOCK_START_SUFFIX];
1461             reallyStartLogicalBlock (posnColumn(queueInts[next + QITEM_POSN]),
1462                                      null, suffix);
1463 	    if (currentBlock == next)
1464 	      currentBlock = -1;
1465             break;
1466 	  case QITEM_BLOCK_END_TYPE:
1467               writeToBase("\033]111\007");
1468               blockDepth -= LOGICAL_BLOCK_LENGTH;  // Pop
1469               break;
1470 	  case QITEM_TAB_TYPE:
1471             // if (isDomTerm()) ??? FIXME
1472 	    expandTabs(next);
1473 	    break;
1474 	  }
1475         }
1476         else // ! isDomTerm()
1477 	switch (type)
1478 	  {
1479 	  case QITEM_NEWLINE_TYPE:
1480 	    boolean cond;
1481             int fits = -1;
1482 	    switch (queueInts[next+QITEM_NEWLINE_KIND])
1483 	      {
1484 	      default: // LINEAR, LITERAL, DUMMY, or MANDATORY:
1485 		cond = true;
1486 		break;
1487 	      case NEWLINE_MISER:
1488 		cond = isMisering();
1489 		break;
1490 	      case NEWLINE_FILL:
1491 		if (isMisering()
1492 		    || (lineNumber > getSectionStartLine()))
1493 		  {
1494 		    cond = true;
1495 		    break;
1496 		  }
1497 		/// ... fall through to ...
1498 	      case NEWLINE_SPACE:
1499 		int end = queueInts[next+QITEM_SECTION_START_SECTION_END];
1500 		if (end == 0)
1501 		  end = -1;
1502 		else
1503 		  { // convert relative->absolute.
1504 		    end = next + end;
1505 		    if (end >= queueInts.length)
1506 		      end -= queueInts.length;
1507 		  }
1508 		fits = fitsOnLine(end, forceNewlines);
1509 		if (fits > 0)
1510 		  cond = false;
1511 		else if (fits < 0 || flushing)
1512 		  cond = true;
1513 		else
1514 		  break loop;
1515 		break;
1516 	      }
1517 	    if (cond)
1518 	      {
1519 		outputAnything = true;
1520 		if (flushing && fits == 0)
1521                   outputPartialLine();
1522                 else
1523                   outputLine(next, flushing);
1524 	      }
1525 	    break;
1526 	  case QITEM_INDENTATION_TYPE:
1527             if (! isMisering())
1528 	      {
1529 		int kind = queueInts[next+QITEM_INDENTATION_KIND];
1530 		int indent = queueInts[next+QITEM_INDENTATION_AMOUNT];
1531                 if (kind == QITEM_INDENTATION_BLOCK)
1532 		  indent += getStartColumn();
1533 		else
1534 		  indent += posnColumn(queueInts[next+QITEM_POSN]);
1535 		//log("setIndent from "+next+": "+queueInts[next+QITEM_INDENTATION_AMOUNT]+" column:"+indent);
1536 		setIndentation(indent);
1537 	      }
1538 	    break;
1539 	  case QITEM_BLOCK_START_TYPE:
1540 	    int start = next;
1541 	    int end = queueInts[next + QITEM_SECTION_START_SECTION_END];
1542 	    // Convert relative offset to absolute index:
1543 	    end = end > 0 ? (end + next) % queueInts.length : -1;
1544 	    fits = fitsOnLine (end, forceNewlines);
1545 	    //log("block-start @"+next+" end:"+end+" force:"+forceNewlines+" fits:"+fits);
1546 	    if (fits > 0)
1547 	      {
1548 		// Just nuke the whole logical block and make it look
1549 		// like one nice long literal.
1550 		int endr = queueInts[next + QITEM_BLOCK_START_BLOCK_END];
1551 		// make absolute:
1552 		next = (endr + next) % queueInts.length;
1553 		expandTabs(next);
1554 		queueTail = next;
1555 		queueSize -= endr;
1556 		//log("remove block -> next:"+next+" endr:"+endr+" qSize:"+queueSize);
1557 	      }
1558 	    else if (fits < 0 || flushing)
1559 	      {
1560 		String prefix = queueStrings[next + QITEM_BLOCK_START_PREFIX];
1561 		String suffix = queueStrings[next + QITEM_BLOCK_START_SUFFIX];
1562 		//log("reallyStartLogicalBlock: "+blockDepth+" at:"+next);
1563 		reallyStartLogicalBlock (posnColumn(queueInts[next + QITEM_POSN]),
1564 					 prefix, suffix);
1565 	      }
1566 	    else // Don't know.
1567 	      break loop;
1568 	    if (currentBlock == start)
1569 	      currentBlock = -1;
1570 	    break;
1571 	  case QITEM_BLOCK_END_TYPE:
1572 	    //log("reallyEndLogicalBlock: "+blockDepth+" at:"+next);
1573             reallyEndLogicalBlock();
1574 	    break;
1575 	  case QITEM_TAB_TYPE:
1576 	    expandTabs(next);
1577 	    break;
1578 	  }
1579 	int size = getQueueSize(queueTail);
1580 	queueSize -= size;
1581 	//log("maybeWrite size: "+size+" ->"+queueSize);
1582 	queueTail = next + size;
1583       }
1584     return outputAnything;
1585   }
1586 
getMiserWidth()1587   protected int getMiserWidth () // DONE
1588   {
1589     // CommonLisp:  Use *print-miser-width*.
1590     return miserWidth;
1591   }
1592 
isMisering()1593   boolean isMisering() // DONE
1594   {
1595     int mwidth = getMiserWidth ();
1596     return (mwidth > 0
1597 	    && lineLength - getStartColumn() <= mwidth);
1598   }
1599 
getMaxLines()1600   int getMaxLines ()
1601   {
1602     // Should be value of CommonLisp *print-lines*.
1603     return -1;
1604   }
1605 
printReadably()1606   boolean printReadably()
1607   {
1608     // Should be value of CommonLisp *print-readably*.
1609     return true;
1610   }
1611 
1612   /** Return 1 if true;  -1 if false; 0 if don't know. */
fitsOnLine(int sectionEnd, boolean forceNewlines)1613   int fitsOnLine (int sectionEnd, boolean forceNewlines) // DONE
1614   {
1615     int available = lineLength;
1616     if (! printReadably() && getMaxLines() == lineNumber)
1617       {
1618 	available -= 3;  // For the " ..".
1619 	available -= getSuffixLength();
1620       }
1621     if (sectionEnd >= 0)
1622       return posnColumn(queueInts[sectionEnd + QITEM_POSN]) <= available ? 1 : -1;
1623     if (forceNewlines)
1624       return -1;
1625     if (indexColumn(bufferFillPointer) > available)
1626       return -1;
1627     return 0; // don't know.
1628   }
1629 
lineAbbreviationHappened()1630   public void lineAbbreviationHappened()
1631   {
1632     // Hook.
1633   }
1634 
writeBreak(int kind, boolean space)1635     private void writeBreak(int kind, boolean space) {
1636         String cmd = null;
1637         int op = -1;
1638         switch (kind) {
1639         case NEWLINE_FILL:
1640         case NEWLINE_SPACE:
1641             op = 115; break;
1642         case NEWLINE_LINEAR:
1643             op = 116; break;
1644         case NEWLINE_MISER:
1645             op = 117; break;
1646         case NEWLINE_LITERAL:
1647             if (blockDepth == LOGICAL_BLOCK_LENGTH) {
1648                 cmd = "\n";
1649                 break;
1650             }
1651             // else fall through
1652         default:
1653             op = 118; break;
1654         }
1655         if (op >= 0)
1656             cmd = "\033]"+op+(space ? ";\"\",\"\",\" \"\007" : "\007");
1657         writeToBase(cmd);
1658     }
1659 
1660   /** Output a new line.
1661    * @param newline index of a newline queue item
1662    */
outputLine(int newline, boolean flushing)1663   private void outputLine (int newline, boolean flushing)
1664   {
1665     //log("outputLine newline:"+newline+" flushing:"+flushing);
1666     char[] buffer = this.buffer;
1667     int kind = queueInts[newline + QITEM_NEWLINE_KIND];
1668     boolean isLiteral = kind == NEWLINE_LITERAL;
1669     int amountToConsume = posnIndex(queueInts[newline + QITEM_POSN]);
1670     int amountToPrint;
1671     if (isLiteral || kind == NEWLINE_DUMMY || isDomTerm())
1672       amountToPrint = amountToConsume;
1673     else
1674       {
1675 	// Skip trailing spaces.
1676 	for (int i = amountToConsume; ; )
1677 	  {
1678 	    if (--i < 0)
1679 	      {
1680 		amountToPrint = 0;
1681 		break;
1682 	      }
1683 	    if (buffer[i] != ' ')
1684 	      {
1685 		amountToPrint = i + 1;
1686 		break;
1687 	      }
1688 	  }
1689       }
1690     boolean spaceBreak = false;
1691     if (kind != NEWLINE_DUMMY && isDomTerm()
1692         && amountToPrint > 0 && buffer[amountToPrint-1] == ' ') {
1693         amountToPrint--;
1694         spaceBreak = true;
1695     }
1696     writeToBase(buffer, 0, amountToPrint);
1697     int lineNumber = this.lineNumber;
1698     lineNumber++;
1699     if (! printReadably())
1700       {
1701 	int maxLines = getMaxLines();
1702 	if (maxLines > 0 && lineNumber >= maxLines)
1703 	  {
1704 	    writeToBase(" ..");
1705 	    int suffixLength = getSuffixLength();
1706 	    if (suffixLength != 0)
1707 	      {
1708 		char[] suffix = this.suffix;
1709 		int len = suffix.length;
1710 		writeToBase(suffix, len - suffixLength, suffixLength);
1711 	      }
1712 	    // (throw 'line-limit-abbreviation-happened t))
1713 	    lineAbbreviationHappened();
1714 	  }
1715       }
1716     this.lineNumber = lineNumber;
1717     if (kind == NEWLINE_DUMMY) {
1718         // emit nothing
1719     } else if (isDomTerm()) {
1720         writeBreak(kind, spaceBreak);
1721     } else {
1722         writeToBase('\n');
1723     }
1724     bufferStartColumn = kind != NEWLINE_DUMMY ? 0 : getColumnNumber();
1725     int fillPtr = bufferFillPointer;
1726     int prefixLen = isLiteral ? getPerLinePrefixEnd() : getPrefixLength();
1727     int shift = amountToConsume - prefixLen;
1728     int newFillPtr = fillPtr - shift;
1729     char[] newBuffer = buffer;
1730     int bufferLength = buffer.length;
1731     if (newFillPtr > bufferLength)
1732       {
1733 	newBuffer = new char[enoughSpace(bufferLength,
1734 					 newFillPtr - bufferLength)];
1735 	this.buffer = newBuffer;
1736       }
1737     System.arraycopy(buffer, amountToConsume, newBuffer, prefixLen,
1738 		     fillPtr - amountToConsume);
1739     System.arraycopy(prefix, 0, newBuffer, 0, prefixLen);
1740     bufferFillPointer = newFillPtr;
1741     bufferOffset += shift;
1742     if (! isLiteral)
1743       {
1744 	blocks[blockDepth+BLOCK_SECTION_COLUMN] = prefixLen;
1745 	blocks[blockDepth+BLOCK_SECTION_START_LINE] = lineNumber;
1746       }
1747   }
1748 
outputPartialLine()1749   void outputPartialLine ()
1750   {
1751     //log("outputPartialLine");
1752     int tail = queueTail;
1753     while (queueSize > 0 && getQueueType(tail) == QITEM_NOP_TYPE)
1754       {
1755 	int size = getQueueSize(tail);
1756 	queueSize -= size;
1757 	tail += size;
1758 	if (tail == queueInts.length)
1759 	  tail = 0;
1760 	queueTail = tail;
1761       }
1762     int fillPtr = bufferFillPointer;
1763     int count = queueSize > 0 ? posnIndex (queueInts[tail + QITEM_POSN])
1764       : fillPtr;
1765     int newFillPtr = fillPtr - count;
1766     if (count <= 0)
1767         throw new Error("outputPartialLine called when nothing can be output.");
1768     writeToBase(buffer, 0, count);
1769     bufferFillPointer = count; // For the sake of the following:
1770     bufferStartColumn = getColumnNumber();
1771     System.arraycopy(buffer, count, buffer, 0, newFillPtr);
1772     bufferFillPointer = newFillPtr;
1773     bufferOffset += count;
1774   }
1775 
1776   /**
1777    * Walk through the queue, emitting print circle notation as described in
1778    * SRFI-38.
1779    *
1780    * For each position marker encountered, if the position marker
1781    * has been referenced, emit the #N= notation. For each back-reference, find
1782    * the corresponding position marker and emit the #N# notation. A Gap Buffer
1783    * is employed to facilitate amortised linear buffer insertion when emitting
1784    * the new print-circle notation. The method populates the rest of the buffer
1785    * contents by using the QITEM_POSN information in SRFI-38 unrelated formatting
1786    * tokens. When the print-circle notation has been emitted, future QITEM_POSN's
1787    * need to be updated, this is achieved by maintaining a delta variable that
1788    * records how many extra characters have been emitted to the output buffer.
1789    */
resolveBackReferences()1790   public void resolveBackReferences ()
1791   {
1792     if (!reallySharing)
1793       return;
1794     int posnMarkerCount = 0;
1795     GapBuffer gbuffer = new GapBuffer(buffer, buffer.length);
1796     // The delta variable records how many extra characters have been emitted
1797     // to the {@link #buffer}.
1798     int delta = 0;
1799     // Used to compute the relative delta of an SRFI-38 specific token.
1800     int oldDelta;
1801     int relativeAddress;
1802 
1803     //log ("resolveBackReferences: Starting at tail = "+queueTail);
1804     int tail = queueTail;
1805     int todo = queueSize;
1806     while (todo > 0)
1807       {
1808 	if (tail >= queueInts.length) // then wrap around
1809 	  tail = 0;
1810 
1811 	int next = tail;
1812 	int type = getQueueType(next);
1813 
1814         // The QITEM_POSN offset will return the position this QITEM occupies
1815         // in the output buffer. Since this QITEM is not SRFI-38 specific, the
1816 	// printer must output all characters up to this token into the new
1817 	// buffer.
1818         if (type != QITEM_NOP_TYPE)
1819           gbuffer.addUpTo(posnIndex(queueInts[next + QITEM_POSN]));
1820 
1821 	switch (type)
1822 	  {
1823 	  case QITEM_POSNMARKER_TYPE:
1824 	    oldDelta = delta;
1825 	    // A position marker is active if it has been back-referenced.
1826 	    if (posnMarkerActive(next))
1827 	      {
1828 		if (posnMarkerIsGrouping(next))
1829 		  {
1830 		    gbuffer.add('.');
1831 		    gbuffer.add(' ');
1832 		    delta += 2;
1833 		  }
1834 		gbuffer.add('#');
1835 		// Set this position markers reference count if it hasn't been set
1836 		// before. Note: all position marker default to a count of one.
1837 		if (queueInts[next + QITEM_POSNMARKER_LOC] == 1)
1838 		  {
1839 		    queueInts[next + QITEM_POSNMARKER_LOC] = posnMarkerCount++;
1840 		  }
1841 		// The index that the referenced position marker resides in
1842 		int reference = queueInts[next + QITEM_POSNMARKER_LOC];
1843 		// The reference could be of arbitrary magnitude, it's typically
1844 		// a unit digit however.
1845 		delta += gbuffer.add(reference);
1846 		gbuffer.add('=');
1847 		delta += 2; // For the '#' and '=' characters
1848 
1849 		if (posnMarkerIsGrouping(next))
1850 		  {
1851 		    gbuffer.add('(');
1852 		    delta++;
1853 		    // XXX Should we start a new logical block? Doesn't look great if
1854 		    // you try it...
1855 		  }
1856 
1857 		// Compute the *relative* position of this QITEM
1858 		//log("POSN-MARKER: queueInts[" + next + " + " + QITEM_POSN + "] = " + (delta - oldDelta));
1859 		queueInts[next + QITEM_POSN] += delta - oldDelta;
1860 	      }
1861 	    break;
1862 	  case QITEM_BACKREF_TYPE:
1863 	    oldDelta = delta;
1864 	    // The index that the referenced position marker resides in
1865 	    relativeAddress = queueInts[next + QITEM_BACKREF_TARGET];
1866 	    //log("RESOLVE-BACKREF: rel=" + relativeAddress + " @:" + next + "(" + (next + relativeAddress) + ")");
1867 	    // The count refers to the N digit in #N= notation. Starts at one.
1868 	    int count = queueInts[relativeAddress + QITEM_POSNMARKER_LOC];
1869 	    gbuffer.add('#');
1870 	    // The reference could be of arbitrary magnitude, it's typically
1871 	    // a unit digit however.
1872 	    delta += gbuffer.add(count);
1873 	    gbuffer.add('#');
1874 	    delta += 2; // For the '=' and '#' characters
1875 			// New characters have been added, so this items output position must
1876 			// change.
1877 	    queueInts[next + QITEM_POSN] += delta - oldDelta;
1878 	    break;
1879 	  case QITEM_PAIR_END_TYPE:
1880 	    relativeAddress = queueInts[next + QITEM_PAIR_END_REF];
1881 	    //log("RESOLVE-PAIREND: rel=" + relativeAddress + "@:" + next + "(" + (next + relativeAddress) + ")");
1882 	    if (posnMarkerActive(relativeAddress))
1883 	      {
1884 		gbuffer.add(')');
1885 		delta++;
1886 		queueInts[next + QITEM_POSN] += 1;
1887 	      }
1888 	    break;
1889 	  default:
1890 	    // Now update the token position information with the current delta.
1891 	    queueInts[next + QITEM_POSN] += delta;
1892 	    break;
1893 	  }
1894 	int size = getQueueSize(tail);
1895 	// "Dequeue" this token
1896 	todo -= size;
1897 	// Point to the next token
1898 	tail = next + size;
1899       }
1900     // The delta variable records how many extra characters have been emitted.
1901     // The bufferFillPointer points to the end of the characters to be flushed
1902     bufferFillPointer += delta;
1903     // The Gap Buffer will remove its gap and present the newly created buffer.
1904     buffer = gbuffer.restoreBuffer();
1905     posnMarkerCount = 1;
1906   }
1907 
forcePrettyOutput()1908   public void forcePrettyOutput ()
1909   {
1910     enqueueNewline(NEWLINE_DUMMY);
1911     maybeOutput(false, true);
1912     if (bufferFillPointer > 0)
1913       outputPartialLine();
1914     expandTabs(-1);
1915     bufferStartColumn = getColumnNumber();
1916     writeToBase(buffer, 0, bufferFillPointer);
1917     bufferFillPointer = 0;
1918     queueSize = queueTail = bufferOffset = 0;
1919   }
1920 
flush()1921   public void flush()
1922   {
1923     if (out == null)
1924       return;
1925     try
1926       {
1927 	forcePrettyOutput();
1928 	out.flush();
1929       }
1930     catch (IOException ex)
1931       {
1932 	throw new RuntimeException(ex.toString());
1933       }
1934   }
1935 
close()1936   public void close()
1937   {
1938     if (out != null)
1939       {
1940 	forcePrettyOutput();
1941         super.close();
1942         out = null;
1943       }
1944     buffer = null;
1945   }
1946 
1947   /** Flush and close this local Writer, but not underlying Writers. */
closeThis()1948   public void closeThis()  throws IOException
1949   {
1950     if (out != null)
1951       {
1952 	forcePrettyOutput();
1953         out = null;
1954       }
1955     buffer = null;
1956   }
1957 
atLineStart()1958     public boolean atLineStart() {
1959         // Could/should optimize.
1960         return getColumnNumber() == 0;
1961     }
1962 
1963   /** Get zero-origin column number, or -1 if unknown.
1964    * Not meaningful if {@code prettyPrintingMode > 0}. */
getColumnNumber()1965   public int getColumnNumber ()
1966   {
1967     int i = bufferFillPointer;
1968     for (;;)
1969       {
1970 	if (--i < 0)
1971 	  return bufferStartColumn + bufferFillPointer;
1972 	char ch = buffer[i];
1973 	if (ch == '\n' || ch == '\r')
1974 	  return bufferFillPointer - (i+1);
1975       }
1976   }
1977 
setColumnNumber(int column)1978   public void setColumnNumber (int column)
1979   {
1980     bufferStartColumn += column - getColumnNumber ();
1981   }
1982 
clearBuffer()1983   public void clearBuffer ()
1984   {
1985     bufferStartColumn = 0;
1986     bufferFillPointer = 0;
1987     lineNumber = 0;
1988     bufferOffset = 0;
1989     blockDepth = LOGICAL_BLOCK_LENGTH;
1990     queueTail = 0;
1991     queueSize = 0;
1992     pendingBlocksCount = 0;
1993   }
1994 
1995   private static final class GapBuffer
1996   {
1997     char[] buffer;
1998     char[] existingBuffer;
1999     int point;
2000     int existingIndex;
2001     int gapSize;
2002 
GapBuffer(char[] existing, int startSize)2003     public GapBuffer (char[] existing, int startSize)
2004     {
2005       this.buffer = new char[startSize];
2006       this.point = 0;
2007       this.existingIndex = 0;
2008       this.existingBuffer = existing;
2009     }
2010 
2011     /**
2012      * The point represents where new characters will be buffered.
2013      * @return The index that represents where new characters will be buffered.
2014      */
getPoint()2015     public int getPoint ()
2016     {
2017       return point;
2018     }
2019 
2020     /**
2021      * Add a character to the buffer.
2022      * @param ch The character to add
2023      */
add(char ch)2024     public void add (char ch)
2025     {
2026       if (point + 1 >= buffer.length)
2027 	{
2028 	  expandBuffer(1);
2029 	}
2030       buffer[point++] = ch;
2031     }
2032 
2033     /**
2034      * Add a non-negative integer to the buffer.
2035      * @param i The non-negative integer to add
2036      * @return The number of digits in the integer
2037      */
add(int i)2038     public int add (int i)
2039     {
2040       int ndigits = 1;
2041       if (i >= 10)
2042 	{
2043 	  ndigits += add(i / 10);
2044 	  i %= 10;
2045 	}
2046       add((char)('0' + i));
2047       return ndigits;
2048     }
2049 
2050     /**
2051      * Copy characters from the existing buffer to the gap buffer.
2052      * @param end the upper index of the characters to copy
2053      */
addUpTo(int end)2054     public void addUpTo(int end)
2055     {
2056       int n = end - existingIndex;
2057       if (point + n >= buffer.length)
2058 	{
2059 	  expandBuffer(n);
2060 	}
2061       while (existingIndex < end)
2062 	{
2063 	  buffer[point++] = existingBuffer[existingIndex++];
2064 	}
2065     }
2066 
2067     /**
2068      * @return The buffer with the gap removed
2069      */
restoreBuffer()2070     public char[] restoreBuffer ()
2071     {
2072       char[] retBuffer = new char[buffer.length];
2073       System.arraycopy(buffer, 0, retBuffer, 0, point);
2074       return retBuffer;
2075     }
2076 
expandBuffer(int n)2077     private void expandBuffer(int n)
2078     {
2079       int newLength = buffer.length;
2080       int minimum = newLength + n;
2081       do
2082 	{
2083 	  newLength <<= 1;
2084 	} while (newLength < minimum);
2085 
2086       char[] newBuffer = new char[newLength];
2087       System.arraycopy(buffer, 0, newBuffer, 0, point);
2088       buffer = newBuffer;
2089     }
2090   }
2091 
2092   /*
2093   public static PrintWriter log;
2094   static {
2095     try { log = new PrintWriter(new FileOutputStream("/tmp/pplog")); }
2096     catch (Throwable ex) { ex.printStackTrace(); }
2097   }
2098   public static void log(String str)
2099   {
2100     log.println(str);
2101     log.flush();
2102   }
2103   void dumpQueue()
2104   {
2105     log.println("Queue tail:"+queueTail+" size:"+queueSize
2106 		+" length:"+queueInts.length+" startCol:"+bufferStartColumn);
2107     dumpQueue(queueTail, queueSize, log);
2108   }
2109 
2110   void dumpQueue(int start, int todo, PrintWriter out)
2111   {
2112     int bufIndex = 0;
2113     while (todo > 0)
2114       {
2115 	if (start == queueInts.length)
2116 	  start = 0;
2117 	if (start < 0 || start >= queueInts.length)
2118 	  {
2119 	    out.print('@');	out.print(start);  out.print(": ");
2120 	    out.print("out of bounds - queueInts length is ");
2121 	    out.println(queueInts.length);
2122 	    break;
2123 	  }
2124 	int type = getQueueType(start);
2125 	int size = getQueueSize(start);
2126 	if (type != QITEM_NOP_TYPE)
2127 	  {
2128 	    int newIndex = posnIndex(queueInts[start+QITEM_POSN]);
2129 	    int count = newIndex - bufIndex;
2130 	    if (count > 0)
2131 	      {
2132 		out.print(count); out.print(" chars: \"");
2133 		out.write(buffer, bufIndex, count);
2134 		out.println('\"');
2135 		bufIndex = newIndex;
2136 	      }
2137 	  }
2138 	out.print('@');	out.print(start);  out.print(": ");
2139 	out.print("type:");  out.print(type);
2140 	switch (type)
2141 	  {
2142 	  case QITEM_NEWLINE_TYPE:
2143 	    out.print("(newline)");  break;
2144 	  case QITEM_INDENTATION_TYPE:
2145 	    out.print("(indentation)");  break;
2146 	  case QITEM_BLOCK_START_TYPE:
2147 	    out.print("(block-start)");  break;
2148 	  case QITEM_BLOCK_END_TYPE:
2149 	    out.print("(block-end)");  break;
2150 	  case QITEM_TAB_TYPE:  out.print("(tab)");
2151 	    break;
2152           case QITEM_POSNMARKER_TYPE:
2153             out.print("(posnmarker)");  break;
2154           case QITEM_BACKREF_TYPE:
2155             out.print("(backref)");  break;
2156 	  case QITEM_NOP_TYPE:
2157 	    out.print("(nop)");  break;
2158 	  }
2159 	out.print(" size:");  out.print(size);
2160 	out.print(";  @");  out.print(start+QITEM_POSN);
2161 	if (type != QITEM_NOP_TYPE)
2162 	  {
2163 	    out.print(": posn:");
2164 	    int posn = queueInts[start+QITEM_POSN];
2165 	    out.print(posn);
2166 	    out.print(" index:");
2167 	    out.println(posnIndex(posn));
2168 	  }
2169 	if (type == QITEM_NEWLINE_TYPE
2170 	    || type == QITEM_BLOCK_START_TYPE)
2171 
2172 	  {
2173 	    out.print('@');  out.print(start+QITEM_SECTION_START_DEPTH);
2174 	    out.print(": - depth:");
2175 	    out.print(queueInts[start+QITEM_SECTION_START_DEPTH]);
2176 	    out.print(";  @");
2177 	    out.print(start+QITEM_SECTION_START_SECTION_END);
2178 	    out.print(": section-end:");
2179 	    out.println(queueInts[start+QITEM_SECTION_START_SECTION_END]);
2180 	  }
2181 	switch (type)
2182 	  {
2183 	  case QITEM_BLOCK_START_TYPE:
2184 	    printQueueWord(start, QITEM_BLOCK_START_BLOCK_END, "block-end", out);
2185 	    printQueueStringWord(start, QITEM_BLOCK_START_PREFIX, "prefix", out);
2186 	    printQueueStringWord(start, QITEM_BLOCK_START_SUFFIX, "suffix", out);
2187 	    break;
2188 	  case QITEM_NEWLINE_TYPE:
2189 	    out.print('@');
2190 	    out.print(start+QITEM_NEWLINE_KIND);
2191 	    out.print(": - kind: ");
2192 	    int kind = queueInts[start+QITEM_NEWLINE_KIND];
2193 	    String skind = "???";
2194 	    switch (kind)
2195 	      {
2196 	      case NEWLINE_LINEAR:    skind = "linear";    break;
2197 	      case NEWLINE_LITERAL:   skind = "literal";   break;
2198 	      case NEWLINE_DUMMY:     skind = "dummy";   break;
2199 	      case NEWLINE_FILL:      skind = "fill";      break;
2200 	      case NEWLINE_SPACE:     skind = "space";      break;
2201 	      case NEWLINE_MISER:     skind = "miser";     break;
2202 	      case NEWLINE_MANDATORY: skind = "mandatory"; break;
2203 	      }
2204 	    out.print(kind);
2205 	    out.print('(');
2206 	    out.print(skind);
2207 	    out.println(')');
2208 	    break;
2209           case QITEM_INDENTATION_TYPE:
2210 	    printQueueWord(start, QITEM_INDENTATION_KIND, "kind", out);
2211 	    printQueueWord(start, QITEM_INDENTATION_AMOUNT, "amount", out);
2212 	    break;
2213 	  default:
2214 	    for (int i = 2;  i < size;  i++)
2215 	      printQueueWord(start, i, "word#"+i, out);
2216 	  }
2217 	todo -= size;
2218 	start += size;
2219       }
2220     int count = bufferFillPointer - bufIndex;
2221     if (count > 0)
2222       {
2223 	out.print(count); out.print(" chars: \"");
2224 	out.write(buffer, bufIndex, count);
2225 	out.println('\"');
2226       }
2227   }
2228 
2229   private void printQueueWord(int start, int offset,
2230 			      String fname, PrintWriter out)
2231   {
2232     out.print('@');
2233     out.print(start+offset);
2234     out.print(": - ");
2235     out.print(fname);
2236     out.print(": ");
2237     out.println(queueInts[start+offset]);
2238   }
2239 
2240   private void printQueueStringWord(int start, int offset,
2241 				    String fname, PrintWriter out)
2242   {
2243     out.print('@');
2244     out.print(start+offset);
2245     out.print(": - ");
2246     out.print(fname);
2247     out.print(": ");
2248     String str = queueStrings[start+offset];
2249     if (str == null)
2250       out.println("null");
2251     else
2252       {
2253 	out.print('\"');
2254 	out.print(str);
2255 	out.print('\"');
2256 	out.print(" length: ");
2257 	out.println(str.length());
2258       }
2259   }
2260 
2261   void check (String where)
2262   {
2263     String msg = null;
2264     if (currentBlock != -1
2265 	&& ! (currentBlock < queueInts.length
2266 	      && currentBlock >= queueTail
2267 	      ? currentBlock < queueTail + queueSize
2268 	      : currentBlock < queueTail + queueSize - queueInts.length))
2269       msg = ("currentBlock ("+currentBlock
2270 	     +") >= queue length ("+queueInts.length+")");
2271     if (msg != null)
2272       {
2273 	if (where != null)
2274 	  msg = "in "+where+": "+msg;
2275 	log.println(msg);
2276 	dumpQueue();
2277 	log.flush();
2278 	throw new Error(msg);
2279       }
2280   }
2281 
2282   String itemKindString (int kind)
2283   {
2284     switch (kind)
2285       {
2286       case QITEM_NOP_TYPE:  return "nop";
2287       case QITEM_NEWLINE_TYPE:  return "newline";
2288       case QITEM_INDENTATION_TYPE:  return "indentation";
2289       case QITEM_BLOCK_START_TYPE:  return "block-start";
2290       case QITEM_BLOCK_END_TYPE:  return "block-end";
2291       case QITEM_TAB_TYPE:  return "tab";
2292       case QITEM_POSNMARKER_TYPE: return "posn-marker";
2293       case QITEM_BACKREF_TYPE: return "backref";
2294       case QITEM_PAIR_END_TYPE: return "pair-end";
2295       case QITEM_EOE_TYPE: return "eoe";
2296       default: return "("+kind+" - unknown)";
2297       }
2298   }
2299   String enqueueExtraLog = "";
2300   */
2301 }
2302