{Begin SubSec Output Functions} {Title Output Functions} {Text {index *BEGIN* output functions} Most of the functions described below have an argument {arg FILE}, which specifies the name of the file on which the operation is to take place. If {arg FILE} is {lisp NIL}, the primary output file is used.{index primary output file} Some of the functions have a {arg RDTBL} argument, which specifies the readtable to be used for output. If {arg RDTBL} is {lisp NIL}, the primary readtable is used.{index readtables}{index primary readtable} Unless otherwise specified by {fn DEFPRINT} ({PageRef Fn DEFPRINT}), pointers other than lists, strings, atoms, or numbers, are printed in the form {lisp {bracket {arg DATATYPE}}} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {lisp {bracket ARRAYP}#43,2760}. This printed representation is for compactness of display on the user's terminal, and will {emphasize not} read back in correctly; if the form above is read, it will produce the atom "{lisp {bracket ARRAYP}#43,2760}". Note: the term {it end-of-line}{index end-of-line} appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-10 end-of-line is indicated by the characters carriage-return{index carriage-return} and {index line-feed}line-feed in that order. {FnDef {FnName PRIN1} {FnArgs X FILE} {Text Prints {arg X} on {arg FILE}. }} {FnDef {FnName PRIN2} {FnArgs X FILE RDTBL} {Text Prints {arg X} on {arg FILE} with {lisp %}'s and {lisp "}'s inserted where required for it to read back in properly by {fn READ}, using {arg RDTBL}. }} Both {fn PRIN1} and {fn PRIN2} print lists as well as atoms and strings; {fn PRIN1} is usually used only for explicitly printing formatting characters, e.g., {lisp (PRIN1 (QUOTE %[))} might be used to print a left square bracket (the {lisp %}{index % (escape character)} would not be printed by {fn PRIN1}). {fn PRIN2} is used for printing S-expressions which can then be read back into Interlisp with {fn READ}; i.e., break and separator characters in atoms will be preceded by {lisp %}'s. For example, the atom "{lisp ()}" is printed as {lisp %(%)} by {fn PRIN2}. If {fn RADIX}=8{index RADIX FN} ({PageRef Fn RADIX}), {fn PRIN2} prints a {lisp Q}{index Q (following a number)} after integers but {fn PRIN1} does not (but both print the integer in octal).{index octal} {FnDef {FnName PRIN3} {FnArgs X FILE}} {FnDef {FnName PRIN4} {FnArgs X FILE RDTBL} {Text {fn PRIN3} and {fn PRIN4} are the same as {fn PRIN1} and {fn PRIN2} respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. }} {FnDef {FnName PRINT} {FnArgs X FILE RDTBL} {Text Prints the expression {arg X} using {fn PRIN2} followed by an end-of-line. Returns {arg X}. }} {FnDef {FnName SPACES} {FnArgs N FILE} {Text Prints {arg N} spaces. Returns {lisp NIL}. }} {FnDef {FnName TERPRI} {FnArgs FILE} {Text Prints an end-of-line. Returns {lisp NIL}. }} {FnDef {FnName TAB} {FnArgs POS MINSPACES FILE} {Text Prints the appropriate number of spaces to move to position {arg POS}. {arg MINSPACES} indicates how many spaces must be printed (if {lisp NIL}, 1 is used). If the current position plus {arg MINSPACES} is greater than {arg POS}, {fn TAB} does a {fn TERPRI} and then {lisp (SPACES {arg POS})}. If {arg MINSPACES} is {lisp T}, and the current position is greater than {arg POS}, then {fn TAB} does nothing. }} Note: A sequence of {fn PRINT}, {fn PRIN2}, {fn SPACES}, and {fn TERPRI} expressions can often be more conveniently coded with a single {lisp PRINTOUT} statement ({PageRef Tag PRINTOUT}). {FnDef {FnName SHOWPRIN2} {FnArgs X FILE RDTBL} {Text Like {fn PRIN2} except if {var SYSPRETTYFLG}={lisp T}{index SYSPRETTYFLG Var}, prettyprints {arg X} instead. Returns {arg X}. }} {FnDef {FnName SHOWPRINT} {FnArgs X FILE RDTBL} {Text Like {fn PRINT} except if {var SYSPRETTYFLG}={lisp T}{index SYSPRETTYFLG Var}, prettyprints {arg X} instead, followed by an end-of-line. Returns {arg X}. }} {fn SHOWPRINT} and {fn SHOWPRIN2} are used by the programmer's assistant ({PageRef Tag ProgAsst}) for printing the values of expressions and for printing the history list, by various commands of the break package ({PageRef Tag BreakPackage}), e.g. {BreakCom ?=} and {BreakCom BT} commands, and various other system packages. The idea is that by simply settting or binding {var SYSPRETTYFLG}{index *PRIMARY* SYSPRETTYFLG Var} to {lisp T} (initially {lisp NIL}), the user instructs the system when interacting with the user to {fn PRETTYPRINT} expressions ({PageRef Fn PRETTYPRINT}) instead of printing them.{index prettyprinting by system functions} {FnDef {FnName PRINTBELLS} {FnArgs} {Text Used by DWIM ({PageRef Tag DWIM}) to print a sequence of bells to alert the user to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. }} {FnDef {FnName DOBE} {FnArgs} {Text (Interlisp-10) {lisp D}ismiss until {lisp O}utput {lisp B}uffer is {lisp E}mpty, i.e., until all of the characters that have been printed by Interlisp functions have actually been printed on the user's terminal. For example, it is important to perform a {fn DOBE} after printing an error message before clearing the input buffers to make sure that the user has actually seen the error message. In systems that do not handle output to the display asynchronously with user computation, such as Interlisp-D, {fn DOBE} is a no-op. }} {Begin SubSec Printlevel} {Title Printlevel} {Text When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. {fn PRINTLEVEL} allows the user to specify in how much detail lists should be printed. The print functions {index PRINT FN}{fn PRINT}, {index PRIN1 FN}{fn PRIN1}, and {index PRIN2 FN}{fn PRIN2} are all affected by level parameters set by: {FnDef {FnName PRINTLEVEL} {FnArgs CARVAL CDRVAL} {Text Sets the {fn CAR} print level to {arg CARVAL}, and the {fn CDR} print level to {arg CDRVAL}. Returns a list cell whose {fn CAR} and {fn CDR} are the old settings. {fn PRINTLEVEL} is initialized with the value {lisp (1000 . -1)}. In order that {fn PRINTLEVEL} can be used with {fn RESETFORM} or {fn RESETSAVE}, if {arg CARVAL} is a list cell it is equivalent to {lisp (PRINTLEVEL (CAR {arg CARVAL}) (CDR {arg CARVAL}))}. {lisp (PRINTLEVEL {arg N} NIL)} changes the {fn CAR} printlevel without affecting the {fn CDR} printlevel. {lisp (PRINTLEVEL NIL {arg N})} changes the {fn CDR} printlevel with affecting the {fn CAR} printlevel. {lisp (PRINTLEVEL)} gives the current setting without changing either. }} The {fn CAR} printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as {lisp &}.{index & (Printed by System)} For example, suppose {arg X} = {lisp (A (B C (D (E F) G) H) K)}. If {arg CARVAL}=3, {lisp (PRINT {arg X})} would print {lisp (A (B C (D & G) H) K)}, if {arg CARVAL}=2, {lisp (A (B C & H) K)}, if {arg CARVAL}=1, {lisp (A & K)}, and if {arg CARVAL}=0, just {lisp &}. If the {fn CAR} printlevel is {it negative}, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. The {fn CDR} printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with {lisp --}.{index -- (Printed by System)} For example, if {arg CDRVAL}=2, {lisp (A B C D E)} will print as {lisp (A B --)}. For sublists, the number of list elements printed is also affected by the depth of printing in the {fn CAR} direction: Whenever the {it sum} of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the {fn CDR} printlevel, {lisp --} is printed. This gives a "triangular" effect in that less is printed the farther one goes in either {fn CAR} or {fn CDR} direction. For example, if {arg CDRVAL}=2, then {lisp (A (B C (D (E F) G) H) K L)} will print as {lisp (A (B --) --)} and if {arg CDRVAL}=3, as {lisp (A (B C --) K --)}. If the {fn CDR} printlevel is negative, then it is the same as if the {fn CDR} printlevel were infinite. The printlevel setting can be changed dynamically, even while Interlisp is printing, by typing {index *PRIMARY* control-P}control-P followed by a number, i.e., a string of digits, followed by a period or exclamation point. As soon as {index control-P}control-P is typed, Interlisp clears and saves the input buffer,{index input buffer}{index clearing input buffer} clears the output buffer,{index clearing output buffer} rings the {index bell (Printed by System)}bell indicating it has seen the control-P, and then waits for input, which is terminated by any non-number. The input buffer is then restored and the program continues. If the input was terminated by a period or an exclamation point, the {fn CAR} printlevel is immediately set to this number; otherwise, the input is ignored. Characters cleared from the output buffer{index output buffer} will have been lost in either case, and printing continues with the (possibly new) printlevel. If the print routine is currently deeper than the new level, all unfinished lists above that level will be terminated by "{lisp --})". Thus, if a circular or long list of atoms, is being printed out, typing "control-P0."{index control-P} will cause the list to be terminated immediately. If the string of digits following a control-P is terminated by a comma, another number may be typed terminated by a period or exclamation point. The {fn CAR} printlevel will then be set to the first number, the {fn CDR} printlevel to the second number. In either case, if a period is used to terminate the printlevel setting, the printlevel will be returned to its previous setting after the current printout has finished. If an exclamation point is used, the change is permanent and the printlevel is not restored (until it is changed again). {VarDef {Name PLVLFILEFLG} {Text Normally, {fn PRINTLEVEL} only affects terminal output. Output to all other files acts as though the print level is infinite. However, if {var PLVLFILEFLG} is {lisp T} (initially {lisp NIL}), then {fn PRINTLEVEL} affects output to files as well. }} }{End SubSec Printlevel} {Begin SubSec Printing numbers} {Title Printing numbers} {Text {index *BEGIN* printing numbers} {note this stuff is fairly tricky.... examine this carefully for errors} How the ordinary printing functions ({fn PRIN1}, {fn PRIN2}, etc.) print numbers can be affected in several ways. {fn RADIX} influences the printing of integers, and {fn FLTFMT} influences the printing of floating point numbers. The setting of the variable {var PRXFLG} determines how the symbol-manipulation functions handle numbers. The {fn PRINTNUM} package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. {FnDef {FnName RADIX} {FnArgs N} {Text Resets the output radix for integers to the absolute value of {arg N}. If {arg N} is negative, integers are interpreted by the print routines as unsigned numbers; i.e., the actual two's complement representation of the integer in the integer size of the particular implementation is interpreted as if it were a positive number on a machine of infinite integer size. Thus, numeric output under a negative radix varies with the implementation, and numbers printed in this way by one implementation will not read correctly in an implementation whose integers are of a different size. For example, in Interlisp-10, whose integer size is 36 bits, -9 will print as shown with the following radices{index Q (following a number)}: {Begin Table radix example} {COLUMN 20percent} {COLUMN} {First {lisp (RADIX)}} {Next {lisp (PRINT -9)}} {First {lisp 10}} {Next {lisp -9}} {First {lisp 8}} {Next {lisp -11Q}} {First {lisp -10}} {Next {lisp 68719476727} (i.e. 2{super 36}-9)} {First {lisp -8}} {Next {lisp 777777777767Q}} {End Table radix example} The value of {fn RADIX} is its previous setting. {lisp (RADIX)} gives the current setting without changing it. The initial setting is 10. Note that {fn RADIX} affects output {it only}. There is no input radix; on input, numbers are interpreted as decimal unless they end in {lisp Q}, in which case they are interpreted as octal. Thus {fn READ} and {fn PRINT} are inverses, independent of any radix setting. {fn RADIX} also does not affect the behavior of {fn UNPACK}, etc., unless the value of {var PRXFLG} (below) is {lisp T}; e.g., with {lisp (RADIX 8)}, the value of {lisp (UNPACK 9)} is {lisp (9)}, not {lisp (1 1)}. }} {FnDef {FnName FLTFMT} {FnArgs FORMAT} {Text Resets the output format for floating point numbers to the {lisp FLOAT} format {arg FORMAT} (see {fn PRINTNUM} below for a description of {lisp FLOAT} formats). {arg FORMAT}={lisp T} specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. {fn FLTFMT} returns its current setting. {lisp (FLTFMT)} returns the current setting without changing it. The initial setting is {lisp T}. In Interlisp-10, {arg FORMAT} may also be a machine-dependent {lisp FLOAT} format-code as returned by {fn NUMFORMATCODE} ({PageRef Fn NUMFORMATCODE}). {Begin Note} In Interlisp-D, {fn FLTFMT} ignores the {arg WIDTH} and {arg PAD} fields of the format (they are implemented only by {fn PRINTNUM}). {End Note} }} {note the following needs a lot of work, once I find out how it should be...} Whether print name manipulation functions ({fn UNPACK}, {fn NCHARS}, etc.) use the values of {fn RADIX} and {fn FLTFMT} is determined by the variable {var PRXFLG}: {index *PRIMARY* PRXFLG Var} {VarDef {Name PRXFLG} {Text If {var PRXFLG}={lisp NIL} (the initial setting), then the "{lisp PRIN1}" name used by {fn PACK}, {fn UNPACK}, {fn MKSTRING}, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of {fn RADIX} or {fn FLTFMT}. If {var PRXFLG}={lisp T}, then {fn RADIX} and {fn FLTFMT} do dictate the "{lisp PRIN1}" name of numbers. Note that in this case, {fn PACK} and {fn UNPACK} are {it not} inverses. Examples with {lisp (RADIX 8)}, {lisp (FLTFMT '(FLOAT 4 2))}: With {var PRXFLG}={lisp NIL}, {lispcode (UNPACK 13) => (1 3)} {lispcode (PACK '(A 9)) => A9} {lispcode (UNPACK 1.2345) => (1 %. 2 3 4 5)} With {var PRXFLG}={lisp T}, {lispcode (UNPACK 13) => (1 5)} {lispcode (PACK '(A 9)) => A11} {lispcode (UNPACK 1.2345) => (1 %. 2 3)} Note that {var PRXFLG} does not effect the radix of "{lisp PRIN2}" names, so with {lisp (RADIX 8)}, {lisp (NCHARS 9 T)}, which uses {lisp PRIN2} names, would return 3, (since 9 would print as 11Q) for either setting of {var PRXFLG}. Warning: Some system functions will not work correctly if {var PRXFLG} is not {lisp NIL}. Therefore, resetting the global value of {var PRXFLG} is not recommended. It is much better to rebind {var PRXFLG} as a {lisp SPECVAR} for that part of a program where it needs to be non-{lisp NIL}. }} {note The {fn PRINTNUM} package was designed and implemented by R. M. Kaplan.} {index *BEGIN* PRINTNUM FN} The basic function for printing numbers under format control is {fn PRINTNUM}. Its utility is considerably enhanced when used in conjunction with the {lisp PRINTOUT} package ({PageRef Tag PRINTOUT}), which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. {FnDef {FnName PRINTNUM} {FnArgs FORMAT NUMBER FILE} {Text Prints {arg NUMBER} on {arg FILE} according to the format {arg FORMAT}. {arg FORMAT} is a list structure with one of the forms described below. {arg FORMAT} can also be a machine dependent format-code as returned by {fn NUMFORMATCODE} ({PageRef Fn NUMFORMATCODE}). (Interlisp-10) If {arg NUMBER} does not fit in the field specified by {arg FORMAT}, the full print name is printed. Then a {fn TAB} is executed so that the line position of the file after {fn PRINTNUM} is always the position prior to printing plus the indicated width. {note A slightly dubious feature. Also is not true in Interlisp-D.} }} If {arg FORMAT} is a list of the form {lisp (FIX {arg WIDTH} {arg RADIX} {arg PAD0} {arg LEFTFLUSH})}, this specifies a {lisp FIX} format.{index FIX format (in PRINTNUM) Term} {arg NUMBER} is rounded to the nearest integer, and then printed in a field {arg WIDTH} characters long with radix set to {arg RADIX} (or 10 if {arg RADIX}={lisp NIL}; note that the setting of {fn RADIX} is {it not} used as the default). If {arg PAD0} and {arg LEFTFLUSH} are both {lisp NIL}, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If {arg PAD0} is {lisp T}, the character "{lisp 0}" is used for padding. If {arg LEFTFLUSH} is {lisp T}, then the number is left-justified in the field, with trailing spaces to fill out {arg WIDTH} characters. The following examples illustrate the effects of the {lisp FIX} format options (the vertical bars indicate the field width): {Begin Table FIX format options examples} {First {arg FORMAT}} {Next {arg NUMBER}} {Next {fn PRINTNUM} prints} {First {lisp (FIX 2)}} {Next {lisp 3}} {Next {lisp | 3|}} {First {lisp (FIX 2 NIL T)}} {Next {lisp 7}} {Next {lisp |07|}} {First {lisp (FIX 12 8 T)}} {Next {lisp 14}} {Next {lisp |000000000016|}} {First {lisp (FIX 5 NIL NIL T)}} {Next {lisp 2}} {Next {lisp |2 |}} {End Table FIX format options examples} If {arg FORMAT} is a list of the form {lisp (FLOAT {arg WIDTH} {arg DECPART} {arg EXPPART} {arg PAD0} {arg ROUND})}, this specifies a {lisp FLOAT} format.{index FLOAT format (in PRINTNUM) Term} {arg NUMBER} is printed as a decimal number in a field {arg WIDTH} characters wide, with {arg DECPART} digits to the right of the decimal point. If {arg EXPPART} is not {lisp 0} (or {lisp NIL}), the number is printed in exponent notation, with the exponent occupying {arg EXPPART} characters in the field. {arg EXPPART} should allow for the character {lisp E} and an optional sign to be printed before the exponent digits. {Note Although written with Interlisp-10 in mind, this description of EXPPART does not agree with the Interlisp-10 implementation! --bvm} As with {lisp FIX} format, padding on the left is with spaces, unless {arg PAD0} is {lisp T}. If {arg ROUND} is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number.{foot The interpretation of {arg WIDTH}={lisp NIL} and {arg DECPART}={lisp NIL} are not specified, and are currently a function of the implementation. Interlisp-10 prohibits {arg WIDTH}={lisp NIL}, and treats {arg DECPART}={lisp NIL} as equivalent to {arg DECPART}={lisp 0}; Interlisp-D interprets {arg WIDTH}={lisp NIL} to mean no padding, i.e., to use however much space the number needs, and interprets {arg DECPART}={lisp NIL} to mean as many decimal places as needed. {Note How's that for variation? --bvm} }{comment end footnote} {lisp FLOAT} format examples: {Begin Table FLOAT format options examples} {First {arg FORMAT}} {Next {arg NUMBER}} {Next {fn PRINTNUM} prints} {First {lisp (FLOAT 7 2)}} {Next {lisp 27.689}} {Next {lisp | 27.69|}} {First {lisp (FLOAT 7 2 NIL T)}} {Next {lisp 27.689}} {Next {lisp |0027.69|}} {First {lisp (FLOAT 7 2 2)}} {Next {lisp 27.689}} {Next {lisp | 2.77E1|}} {First {lisp (FLOAT 11 2 4)}} {Next {lisp 27.689}} {Next {lisp | 2.77E+01|}{Foot As of this writing, the Interlisp-10 implementation actually does something less intuitive with the {arg EXPPART} field: the placement of the decimal point is affected by {arg DECPART}, and padding never occurs. These two examples in Interlisp-10 would actually print as {lisp |.28E+02|} and {lisp |27.69E+0000|}. {Note surely we can fix this, unless the FLOUT Jsys really insists on doing things this way! --bvm} }{comment end footnote} } {First {lisp (FLOAT 7 2 NIL NIL 1)}} {Next {lisp 27.689}} {Next {lisp | 30.00|}} {First {lisp (FLOAT 7 2 NIL NIL 2)}} {Next {lisp 27.689}} {Next {lisp | 28.00|}} {End Table FLOAT format options examples} {VarDef {Name NILNUMPRINTFLG} {Text If {fn PRINTNUM}'s {arg NUMBER} argument is not a number and not {lisp NIL}, a {lisp NON-NUMERIC ARG} error is generated. If {arg NUMBER} is {lisp NIL}, the effect depends on the setting of the variable {var NILNUMPRINTFLG}. If {var NILNUMPRINTFLG} is {lisp NIL}, then the error occurs as usual. If it is non-{lisp NIL}, then no error occurs, and the value of {var NILNUMPRINTFLG} is printed right-justified in the field described by {arg FORMAT}. This option facilitates the printing of numbers in aggregates with missing values coded as {lisp NIL}. }} In some implementations, formatted printing of numbers receives assistance from the operating system, provided that the format is specified in some sort of special code. {fn PRINTNUM} works by converting the machine-independent format specifications described above into machine-{it dependent} codes the exact form of which may vary from implementation to implementation. This conversion process takes place on each call to {fn PRINTNUM}. For efficiency purposes, if the user is going to be performing a particular call to {fn PRINTNUM} frequently, he may wish to separate the conversion from the actual printing, performing the conversion process just once and saving the result. The function {fn NUMFORMATCODE} is available for this purpose: {fn NUMFORMATCODE} takes a format, performs the conversion and returns a machine dependent format-code, which can be given to {fn PRINTNUM} in place of a list structure format as described above. In this case, {fn PRINTNUM} will not have to perform the conversion, but can simply use the machine-dependent format code directly. {FnDef {FnName NUMFORMATCODE} {FnArgs FORMAT SMASHCODE} {Text Converts the {lisp FIX} or {lisp FLOAT} format {arg FORMAT} to a machine-dependent format-code. If {arg SMASHCODE} is recognized as a format-code data-structure, then the new format-code is smashed into that structure instead of allocating new storage. {lisp (NUMFORMATCODE)} returns an uninitialized datum that can later be smashed. In Interlisp-D, this function is a no-op, as there is no special internal representation for number formats. }} {index *END* PRINTNUM FN} {index *END* printing numbers} }{End SubSec Printing numbers} {Begin SubSec User Defined Printing} {Title User Defined Printing} {Text {index user defined printing} {FnDef {FnName DEFPRINT} {FnArgs TYPE FN} {Text {arg TYPE} is a type name (see {PageRef Fn TYPENAME}). Whenever a printing function ({fn PRINT}, {fn PRIN1}, {fn PRIN2}, etc.) encounters an object of the indicated type, {arg FN} is called with the item to be printed as its argument. If it returns {lisp NIL}, the datum is printed in the manner the system defaults; for user data types, it is printed as {lisp {Bracket datatype}#nnnnnn}. If {arg FN} wishes to specify how the datum should be printed, it should return a list of the form {lisp ({arg ITEM1} . {arg ITEM2})}. {arg ITEM1} is printed using {fn PRIN1} (unless it is {lisp NIL}), and then {arg ITEM2} printed using {fn PRIN2} with no spaces between the two items. (Typically, {arg ITEM1} is a read macro character.) In Interlisp-10, {arg TYPE} may also be a type number (see {PageRef Fn NTYP}). Note that the user can specify different action for type names {lisp ARRAYP}, {lisp HARRAYP}, {lisp TERMTABLEP}, {lisp READTABLEP}, and {lisp CCODEP}, even though they all have the same type {it number}. }} Note that {fn DEFPRINT} also affects internal calls to print from {fn PACK}, {fn CONCAT}, etc., i.e. any operation that involves obtaining a print name (see {PageRef Tag PrintNames}). A consequence of this fact is that in implementations that do not have reentrant printing code (in particular, Interlisp-10), the user's {fn DEFPRINT} function must {it not} call any print name manipulating functions itself, or the results of the whole printing operation are undefined. {Begin Note} I thought there was a whole discussion of changes to DEFPRINT in the last year to accomodate such things as giving the defprint fn a file argument, and having something to make NCHARS smart. Where is all that? -bvm {End Note} }{End SubSec User Defined Printing} {Begin SubSec Dumping Unusual Data Structures} {Title Dumping Unusual Data Structures} {Text {note The {fn HPRINT} package was written by L. M. Masinter.} {index dumping unusual data structures} {index dumping circular lists} {fn HPRINT} (for "Horrible Print") and {fn HREAD} provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. {fn HPRINT} will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. {fn HPRINT} currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. {fn HPRINT} operates by simulating the Interlisp {fn PRINT} routine for normal list structures. When it encounters a user datatype (see {PageRef Tag UserDataTypes}), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read-macro characters (see {PageRef Tag ReadMacros}). While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read-macro character is inserted before the first occurrence (by resetting the file pointer with {fn SETFILEPTR}) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, {fn HREAD} merely calls the Interlisp {fn READ} routine with the appropriate readtable. {FnDef {FnName HPRINT} {FnArgs EXPR FILE UNCIRCULAR DATATYPESEEN} {Text Prints {arg EXPR} on {arg FILE}. If {arg UNCIRCULAR} is non-{lisp NIL}, {fn HPRINT} does no checking for any circularities in {arg EXPR} (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying {arg UNCIRCULAR} as non-{lisp NIL} results in a large speed and internal-storage advantage. Normally, when {fn HPRINT} encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If {arg DATATYPESEEN} is non-{lisp NIL}, {fn HPRINT} will assume that the same data type declarations will be in force at read time as were at {fn HPRINT} time, and not output declarations. {fn HPRINT} is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If {arg FILE} is not {fn RANDOMACCESSP} (and {arg UNCIRCULAR} = {lisp NIL}), a temporary file, {lisp HPRINT.SCRATCH}, is opened, {arg EXPR} is {fn HPRINT}ed on it, and then that file is copied to the final output file and the temporary file is deleted. }} {FnDef {FnName HREAD} {FnArgs FILE} {Text Reads and returns an {fn HPRINT}-ed expression from {arg FILE}. }} {FnDef {FnName HCOPYALL} {FnArgs X} {Text Copies data structure {arg X}. {arg X} may contain circular pointers as well as arbitrary structures. }} Note: {filecom HORRIBLEVARS}{index HORRIBLEVARS FileCom} and {filecom UGLYVARS}{index UGLYVARS FileCom} ({PageRef FileCom HORRIBLEVARS}) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to {fn HPRINT} and {fn HREAD}. {Begin Note} Is this stuff true? Should it be documented in the manual? Date: 3 NOV 1974 0110-PST From: MASINTER Subject: HPRINT There is a flag HPRINTALLFLG: if on, HPRINT checks for EQ strings and large numbers (as well as lists, arrays, etc). initial value NIL (i.e. don't put strings in hash table). ---- initialization function HPINITRDTBL[chars] init's the read table. chars is the list (forwardchar forwardcdrchar bakchar fillchar finalchar). ^ used before an item which will have a back reference ^ used before an item, the tail of the list starting bakchar is used to denote both a back reference, and a hash array, (when followed by H), a regular array (when followed by A[), a datatype (when followed by [). fillchar is used to pad around back reference numbers (since it just leaves spaces a priori and goes back at the end to remark the places). finalchar is used to terminate the backref numbers (as well as after arrays, datatypes, etc). initially, these are set up: forwardchar = ^ (up arrow) forwrdcdrchar = ` (accent grave) bakchar = {lbracket} (left brace) fillchar = (ASCII 0) (usually non-printing) finalchar = {rbracket} (right brace) This means that circular lists print in a similar format to CIRCLPRINT's output. Arrays print as {bracket A[n1 n2 e1 e2 e3 ...]} (the ei's are the elements) Hash arrays print {bracket H[n1 e1 e2 e3 ...]} User datatypes as {bracket [name e1 e2 e3 ...]} except that the first time a datatype is printed, the name is followed by NWORDS and NPOINTERS. forwardchar, forwrdcdrchar, and bakchar are macros; fillchar and finalchar are just seprs (but there are redundancy checks that (EQ (READC ..) finalchar) in various places). ---- READVARS, if the last arg is a list, will initialize the readtable to those codes. HORRIBLEVARS prettydefmacro will put out the codes in the READVARS expression if they are different from those in HPDEFAULTCHARS. {End Note} }{End SubSec Dumping Unusual Data Structures} {index *END* output functions} }{End SubSec Output Functions}