{Begin SubSec Output Functions} {Title Output Functions} {Text {index *PRIMARY* Output functions} 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 litatom {lisp {bracket ARRAYP}#43,2760}. Note: the term "end-of-line"{index End-of-line character} 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-D end-of-line is indicated by the character carriage-return.{index *PRIMARY* Carriage-return} Some of the functions described below have a {arg RDTBL} argument, which specifies the {index read tables}read table to be used for output (see {PageRef Term Read Tables}). If {arg RDTBL} is {lisp NIL}, the {index primary read table}primary read table is used. Most of the functions described below have an argument {arg FILE}, which specifies the stream on which the operation is to take place. If {arg FILE} is {lisp NIL}, the {index primary streams}primary output stream is used (see {PageRef Term primary streams}). {FnDef {FnName OUTPUT} {FnArgs FILE} {Text Sets {arg FILE} as the {index *PRIMARY* Primary output stream}primary output stream; returns the old primary output stream. {arg FILE} must be open for output. {lisp (OUTPUT)} returns the current primary output stream, which is not changed. Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion on {PageRef Tag StreamFileNames}. }} {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 any kind of Lisp expression, including lists, atoms, numbers, and strings. {fn PRIN1} is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. {fn PRIN1} does not print double quotes around strings, or {lisp %} in front of special characters. {fn PRIN2} is used for printing Interlisp 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 the integer output radix (as set by {index RADIX FN}{fn RADIX}, {PageRef Fn RADIX}) is not 10, {fn PRIN2} prints the integer using the {index Integer input syntax}input syntax for non-decimal integers (see {PageRef Term Integer input syntax}) but {fn PRIN1} does not (but both print the integer in the output radix). {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 {Name PRINTCCODE} {Args CHARCODE FILE} {Text Outputs a single character whose code is {arg CHARCODE} to {arg FILE}. This is similar to {lisp (PRIN1 (CHARACTER {arg CHARCODE}))}, except that numeric characters are guaranteed to print "correctly"; e.g., {Lisp (PRINTCCODE (CHARCODE 9))} always prints "{lisp 9}", independent of the setting of {fn RADIX}. Note that {fn PRINTCCODE} may actually print more than one byte on {arg FILE}, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see {fn GETFILEPTR}, {pageref fn GETFILEPTR}) during this operation. }} {FnDef {Name BOUT} {Args STREAM BYTE} {Text Outputs a single 8-bit byte to {arg STREAM}. This is similar to {fn PRINTCCODE}, but for binary streams the character position in {arg STREAM} is not updated (as with {fn PRIN3}), and end of line conventions are ignored. }} Note: {fn BOUT} is similar to {fn PRINTCCODE}, except that {fn BOUT} always writes a single byte, whereas {fn PRINTCCODE} writes a "character" that can consist of more than one byte, depending on the character and its encoding (see {index NS character I/O}{PageRef Term NS character I/O}). {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 character.{index End-of-line character} Returns {lisp NIL}. }} {FnDef {FnName FRESHLINE} {FnArgs STREAM} {Text Equivalent to {fn TERPRI}, except it does nothing if it is already at the beginning of the line. Returns {lisp T} if it prints an end-of-line, {lisp NIL} otherwise. }} {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 Term programmer's assistant}) 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. {FnDef {FnName PRINTBELLS} {FnArgs {anonarg}} {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 {Name FORCEOUTPUT} {Args STREAM WAITFORFINISH} {Text Forces any buffered output data in {arg STREAM} to be transmitted. If {arg WAITFORFINISH} is non-{lisp NIL}, this doesn't return until the data has been forced out. }} {FnDef {FnName POSITION} {FnArgs FILE N} {Text Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If {arg N} is non-{lisp NIL}, {it resets} the column number to be {arg N}. Note that resetting {fn POSITION} only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that {lisp (POSITION {arg FILE})} is {it not} the same as {lisp (GETFILEPTR {arg FILE})} which gives the position in the {it file}, not on the {it line}. }} {FnDef {FnName LINELENGTH} {FnArgs N FILE} {Text Sets the length of the print line for the output file {arg FILE} to {arg N}; returns the former setting of the line length. {arg FILE} defaults to the primary output stream. {lisp (LINELENGTH NIL {arg FILE})} returns the current setting for {arg FILE}. When a file is first opened, its line length is set to the value of the variable {var FILELINELENGTH}.{index *PRIMARY* FILELINELENGTH Var} Whenever printing an atom or string would increase a file's position {it beyond} the line length of the file, an end of line is automatically inserted first. This action can be defeated by using {fn PRIN3} and {fn PRIN4} ({PageRef Fn PRIN3}). }} {FnDef {FnName SETLINELENGTH} {FnArgs N} {Text Sets the line length for the terminal by doing {lisp (LINELENGTH {arg N} T)}. If {arg N} is {lisp NIL}, it determines {arg N} by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this 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. Note: control-P ({PageRef (Interrupt Character)control-P}) can be used to change the {fn PRINTLEVEL} setting dynamically, even while Interlisp is printing. }} 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)} 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. If the {fn CDR} printlevel is negative, then it is the same as if the {fn CDR} printlevel were infinite. Examples: {Begin Labeledlist PRINTLEVEL examples} {Label After:} {Text {lisp (A (B C (D (E F) G) H) K L)} prints as:} {Label {lisp (PRINTLEVEL 3 -1)}} {Text {lisp (A (B C (D & G) H) K L)}} {Label {lisp (PRINTLEVEL 2 -1)}} {Text {lisp (A (B C & H) K L)}} {Label {lisp (PRINTLEVEL 1 -1)}} {Text {lisp (A & K L)}} {Label {lisp (PRINTLEVEL 0 -1)}} {Text {lisp &}} {Label {lisp (PRINTLEVEL 1000 2)}} {Text {lisp (A (B --) --)}} {Label {lisp (PRINTLEVEL 1000 3)}} {Text {lisp (A (B C --) K --)}} {Label {lisp (PRINTLEVEL 1 3)}} {Text {lisp (A & K --)}} {End Labeledlist PRINTLEVEL examples} {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. }} The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. {FnDef {FnName LVLPRINT} {FnArgs X FILE CARLVL CDRLVL TAIL} {Text Performs {fn PRINT} of {arg X} to {arg FILE}, using as {lisp CAR} and {lisp CDR} print levels the values {arg CARLVL} and {arg CDRLVL}, respectively. Uses the {lisp T} read table. If {arg TAIL} is specified, and {arg X} is a tail of it, then begins its printing with "{lisp ...}", rather than on open parenthesis. }} {FnDef {FnName LVLPRIN2} {FnArgs X FILE CARLVL CDRLVL TAIL} {Text Similar to {fn LVLPRIN2}, but performs a {fn PRIN2}. }} {FnDef {FnName LVLPRIN1} {FnArgs X FILE CARLVL CDRLVL TAIL} {Text Similar to {fn LVLPRIN1}, but performs a {fn PRIN1}. }} }{End SubSec PRINTLEVEL} {Begin SubSec Printing numbers} {Title Printing numbers} {Text {index *PRIMARY* 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}. 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 are entered in a non-decimal radix with syntax such as {lisp 123Q}, {lisp |b10101}, {lisp |5r1234} (see {PageRef Term Integer input syntax}). {fn RADIX} does not affect the behavior of {fn UNPACK}, etc., unless the value of {var PRXFLG} (below) is {lisp T}. For example, if {var PRXFLG} is {lisp NIL} and the radix is set to 8 with {lisp (RADIX 8)}, the value of {lisp (UNPACK 9)} is {lisp (9)}, not {lisp (1 1)}. Using {fn PRINTNUM} ({PageRef Fn PRINTNUM}) or the {lisp PRINTOUT} command {lisp .I} ({PageRef (PRINTOUT Command) I}) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change {fn RADIX}. }} {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}. {comment Interlisp-10 specific: In Interlisp-10, {arg FORMAT} may also be a machine-dependent {lisp FLOAT} format-code as returned by {fn NUMFORMATCODE} ({PageRef Fn NUMFORMATCODE}).} Note: In Interlisp-D, {fn FLTFMT} ignores the {arg WIDTH} and {arg PAD} fields of the format (they are implemented only by {fn PRINTNUM}). }} {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 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. {comment Interlisp-10 Specific: {arg FORMAT} can also be a machine dependent format-code as returned by {fn NUMFORMATCODE} ({PageRef Fn NUMFORMATCODE}).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.} }} 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 from the function {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 on the number 9 (the vertical bars indicate the field width): {Begin Labeledlist FIX format options examples} {Label {arg FORMAT}:} {Text {lisp (PRINTNUM {arg FORMAT} 9)} prints:} {Label {lisp (FIX 2)}} {Text {lisp | 9|}} {Label {lisp (FIX 2 NIL T)}} {Text {lisp |09|}} {Label {lisp (FIX 12 8 T)}} {Text {lisp |000000000011|}} {Label {lisp (FIX 5 NIL NIL T)}} {Text {lisp |9 |}} {End Labeledlist 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. 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. {comment Interlisp-10 specific: 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}} The following examples illustrate the effects of the {lisp FLOAT} format options on the number 27.689 (the vertical bars indicate the field width): {Begin Labeledlist FLOAT format options examples} {Label {arg FORMAT}:} {Text {lisp (PRINTNUM {arg FORMAT} 27.689)} prints:} {Label {lisp (FLOAT 7 2)}} {Text {lisp | 27.69|}} {Label {lisp (FLOAT 7 2 NIL T)}} {Text {lisp |0027.69|}} {Label {lisp (FLOAT 7 2 2)}} {Text {lisp | 2.77E1|}} {Label {lisp (FLOAT 11 2 4)}} {Text {lisp | 2.77E+01|} {comment Interlisp-10 specific: 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|}.} } {Label {lisp (FLOAT 7 2 NIL NIL 1)}} {Text {lisp | 30.00|}} {Label {lisp (FLOAT 7 2 NIL NIL 2)}} {Text {lisp | 28.00|}} {End Labeledlist 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}. }} {Begin Comment} Interlisp-10 specific: 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. }} {End Comment} {index *END* PRINTNUM FN} }{End SubSec Printing numbers} {Begin SubSec User Defined Printing} {Title User Defined Printing} {Text {index *PRIMARY* User defined printing} Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {lisp {bracket datatype}} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the {lisp DATATYPE} record type, {pageRef (record type) DATATYPE}), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function {fn DEFPRINT} is used to specify the printing format of a data type. {FnDef {FnName DEFPRINT} {FnArgs TYPE FN} {Text {arg TYPE} is a type name. Whenever a printing function ({fn PRINT}, {fn PRIN1}, {fn PRIN2}, etc.) or a function requiring a print name ({fn CHCON}, {fn NCHARS}, etc.) encounters an object of the indicated type, {arg FN} is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is {lisp NIL} on calls that request the print name of an object without actually printing it. If {arg FN} returns 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} is printed using {fn PRIN2} (unless it is {lisp NIL}). No spaces are printed between the two items. Typically, {arg ITEM1} is a read macro character. If {arg FN} returns {lisp NIL}, the datum is printed in the system default manner. If {arg FN} returns {lisp T}, nothing further is printed; {arg FN} is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to {arg FN} is non-{lisp NIL}; otherwise, there is no destination for {arg FN} to do its printing, so it must return as in one of the other two cases. {Begin comment Interlisp-10 specific} Interlisp-10 specific: 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 since {fn DEFPRINT} 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}),in implementations that do not have reentrant printing code, 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. {End comment Interlisp-10 specific} }} }{End SubSec User Defined Printing} {Begin SubSec Printing Unusual Data Structures} {Title Printing Unusual Data Structures} {Text {note The {fn HPRINT} package was written by L. M. Masinter.} {index *PRIMARY* Printing unusual data structures} {index *PRIMARY* Printing 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 Term User Data Types}), 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 read table. {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} assumes 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 a random access file (and {arg UNCIRCULAR} = {lisp NIL}), a temporary file, {lisp HPRINT.SCRATCH},{index HPRINT.SCRATCH (File name)} 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}. When {fn HPRINT} is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with {fn HREAD} can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. To prevent accidental system crashes, {fn HREAD} will {it not} redefine datatypes. Instead, it will cause an error "{lisp attempt to read DATATYPE with different field specification than currently defined}".{index attempt to read DATATYPE with different field specification than currently defined Error} Continuing from this error will redefine the datatype. {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 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 read table 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 Printing Unusual Data Structures} }{End SubSec Output Functions} ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) a#rX…Üzº