{Begin SubSec Read Tables} {Title Read Tables} {Text {note Read tables and terminal tables were designed and implemented by D. C. Lewis.} {Tag ReadTables} {index *PRIMARY* Read tables} Many Interlisp input functions treat certain characters in special ways. For example, {fn READ} recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If {lisp NIL} is given as the read table, the "primary read table"{index *PRIMARY* Primary read table} is used. If {lisp T} is specified, the system terminal read table is used. Some functions will also accept the atom {atom ORIG}{index ORIG Litatom} ({it not} the {it value} of {atom ORIG}) as indicating the "original" system read table. Some output functions also take a read table argument. For example, {fn PRIN2} prints an expression so that it would be read in correctly using a given read table. The Interlisp-D system uses the following read tables: {lisp T} for input/output from terminals, the value of {var FILERDTBL}{index *PRIMARY* FILERDTBL Var} for input/output from files, the value of {var EDITRDTBL}{index EDITRDTBL Var} for input from terminals while in the tty-based editor, the value of {var DEDITRDTBL}{index DEDITRDTBL Var} for input from terminals while in the display-based editor, and the value of {var CODERDTBL}{index CODERDTBL Var} for input/output from compiled files. These five read tables are initially copies of the {lisp ORIG} read table, with changes made to some of them to provide read macros ({PageRef Term Read Macros}) that are specific to terminal input or file input. Using the functions described below, the user may further change, reset, or copy these tables. However, in the case of {var FILERDTBL} and {var CODERDTBL}, the user is cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. The user can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via {fn SETREADTABLE}, and then not specify a {arg RDTBL} argument, i.e., use {lisp NIL}. {Begin SubSec Read Table Functions} {Title Read Table Functions} {Text {FnDef {FnName READTABLEP} {FnArgs RDTBL} {Text Returns {arg RDTBL} if {arg RDTBL} is a real read table ({it not} {lisp T} or {lisp ORIG}), otherwise {lisp NIL}. }} {FnDef {FnName GETREADTABLE} {FnArgs RDTBL} {Text If {arg RDTBL}={lisp NIL}, returns the primary read table. If {arg RDTBL}={lisp T}, returns the system terminal read table. If {arg RDTBL} is a real read table, returns {arg RDTBL}. Otherwise, generates an {lisp ILLEGAL READTABLE} error.{index ILLEGAL READTABLE Error} }} {FnDef {FnName SETREADTABLE} {FnArgs RDTBL FLG} {Text Sets the primary read table to {arg RDTBL}. If {arg FLG}={lisp T}, {fn SETREADTABLE} sets the system terminal read table, {lisp T}. Note that the user can reset the other system read tables with {fn SETQ}, e.g., {lisp (SETQ FILERDTBL (GETREADTABLE))}. Generates an {lisp ILLEGAL READTABLE}{index ILLEGAL READTABLE Error} error if {arg RDTBL} is not {lisp NIL}, {lisp T}, or a real read table. Returns the previous setting of the primary read table, so {fn SETREADTABLE} is suitable for use with {fn RESETFORM} ({PageRef Fn RESETFORM}). }} {FnDef {FnName COPYREADTABLE} {FnArgs RDTBL} {Text Returns a copy of {arg RDTBL}. {arg RDTBL} can be a real read table, {lisp NIL}, {lisp T}, or {lisp ORIG} (in which case {fn COPYREADTABLE} returns a copy of the {it original} system read table), otherwise {fn COPYREADTABLE} generates an {lisp ILLEGAL READTABLE}{index ILLEGAL READTABLE Error} error. Note that {fn COPYREADTABLE} is the only function that {it creates} a read table. }} {FnDef {FnName RESETREADTABLE} {FnArgs RDTBL FROM} {Text Copies (smashes) {arg FROM} into {arg RDTBL}. {arg FROM} and {arg RDTBL} can be {lisp NIL}, {lisp T}, or a real read table. In addition, {arg FROM} can be {lisp ORIG}, meaning use the system's original read table. }} }{End SubSec Read Table Functions} {Begin SubSec Syntax Classes} {Title Syntax Classes} {Text {Tag SyntaxClass} {index *PRIMARY* Syntax classes} A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: {lisp LEFTPAREN}, {lisp RIGHTPAREN}, {lisp LEFTBRACKET}, {lisp RIGHTBRACKET}, {lisp STRINGDELIM}, {lisp ESCAPE}, {lisp BREAKCHAR}, {lisp SEPRCHAR}, and {lisp OTHER}, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: {Begin LabeledList basic syntax classes} {Name {lisp LEFTPAREN}{index LEFTPAREN (Syntax Class)}} {Text (normally left parenthesis) Begins list structure.} {Name {lisp RIGHTPAREN}{index RIGHTPAREN (Syntax Class)}} {Text (normally right parenthesis) Ends list structure.} {Name {lisp LEFTBRACKET}{index LEFTBRACKET (Syntax Class)}} {Text (normally left bracket) Begins list structure. Also matches {lisp RIGHTBRACKET} characters.} {Name {lisp RIGHTBRACKET}{index RIGHTBRACKET (Syntax Class)}} {Text (normally left bracket) Ends list structure. Can close an arbitrary numbers of {lisp LEFTPAREN} lists, back to the last {lisp LEFTBRACKET}.} {Name {lisp STRINGDELIM}{index STRINGDELIM (Syntax Class)}} {Text (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class {lisp ESCAPE} are treated as ordinary, i.e., interpreted as if they were of syntax class {lisp OTHER}. To include the string delimiter inside a string, prefix it with the {lisp ESCAPE} character.} {Name {lisp ESCAPE}{index ESCAPE (Syntax Class)}} {Text (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class {lisp OTHER}, independent of its normal syntax class.} {Name {lisp BREAKCHAR}{index BREAKCHAR (Syntax Class)}} {Text (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character.} {Name {lisp SEPRCHAR}{index SEPRCHAR (Syntax Class)}} {Text (space, carriage return, etc.) Delimits atoms, and is otherwise ignored.} {Name {lisp OTHER}{index OTHER (Syntax Class)}} {Text Characters that are not otherwise special belong to the class {lisp OTHER}.} {End LabeledList basic syntax classes} Characters of syntax class {lisp LEFTPAREN}, {lisp RIGHTPAREN}, {lisp LEFTBRACKET}, {lisp RIGHTBRACKET}, and {lisp STRINGDELIM} are all {index *PRIMARY* Break characters}{it break} characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class {lisp BREAKCHAR} serve {it only} to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by {fn RATOM}, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the {lisp ESCAPE} character. Characters of class {lisp SEPRCHAR}{index *PRIMARY* Separator characters} also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the {lisp ESCAPE} character in order to appear in an atom. For example, if {lisp $} were a break character and {lisp *} a separator character, the input stream {lisp ABC**DEF$GH*$$} would be read by 6 calls to {index RATOM FN}{fn RATOM} returning respectively {lisp ABC}, {lisp DEF}, {lisp $}, {lisp GH}, {lisp $}, {lisp $}. Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as {lisp LEFTPAREN}), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a {it syntax class}. Instead, a read table provides the {it association} between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. The functions below are used to obtain and set the syntax class of a character in a read table. {arg CH} can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates control-A, and 49 indicates the {it character} 1. Note that {arg CH} can be a full sixteen-bit {index NS Characters}NS character (see {PageRef Term NS Characters}). Note: Terminal tables, described on {PageRef Term Terminal Tables}, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. {FnDef {FnName GETSYNTAX} {FnArgs CH TABLE} {Text Returns the syntax class of {arg CH}, a character or a character code, with respect to {arg TABLE}. {arg TABLE} can be {lisp NIL}, {lisp T}, {lisp ORIG}, or a real read table or terminal table. {arg CH} can also be a syntax class, in which case {fn GETSYNTAX} returns a list of the character codes in {arg TABLE} that have that syntax class. }} {FnDef {FnName SETSYNTAX} {FnArgs CHAR CLASS TABLE} {Text Sets the syntax class of {arg CHAR}, a character or character code, in {arg TABLE}. {arg TABLE} can be either {lisp NIL}, {lisp T}, or a real read table or terminal table. {fn SETSYNTAX} returns the previous syntax class of {arg CHAR}. {arg CLASS} can be any one of the following: {begin unnumberedlist SETSYNTAX classes} {item The name of one of the basic syntax classes.} {item A list, which is interpreted as a read macro (see {PageRef Tag READMACROS}).} {item {lisp NIL}, {lisp T}, {lisp ORIG}, or a real read table or terminal table, which means to give {arg CHAR} the syntax class it has in the table indicated by {arg CLASS}. For example, {lisp (SETSYNTAX '%( 'ORIG {arg TABLE})} gives the left parenthesis character in {arg TABLE} the same syntax class that it has in the original system read table. } {item A character code or character, which means to give {arg CHAR} the same syntax class as the character {arg CHAR} in {arg TABLE}. For example, {lisp (SETSYNTAX '{lbracket} '%[ {arg TABLE})} gives the left brace character the same syntax class as the left bracket. } {end unnumberedlist SETSYNTAX classes} }} {FnDef {FnName SYNTAXP} {FnArgs CODE CLASS TABLE} {Text {arg CODE} is a character code; {arg TABLE} is {lisp NIL}, {lisp T}, or a real read table or terminal table. Returns {lisp T} if {arg CODE} has the syntax class {arg CLASS} in {arg TABLE}; {lisp NIL} otherwise. {arg CLASS} can also be a read macro type ({lisp MACRO}, {lisp SPLICE}, {lisp INFIX}), or a read macro option ({lisp FIRST}, {lisp IMMEDIATE}, etc.), in which case {fn SYNTAXP} returns {lisp T} if the syntax class is a read macro with the specified property. Note: {fn SYNTAXP} will {it not} accept a character as an argument, only a character {it code}. {note why can't it accept a character??} {note Probably because it wants to be fast, since it typically occurs in the inner loop of some scanner. Any other guesses? --bvm} }} For convenience in use with {fn SYNTAXP}, the atom {lisp BREAK}{index BREAK (Syntax Class)} may be used to refer to {it all} break characters, i.e., it is the union of {lisp LEFTPAREN}, {lisp RIGHTPAREN}, {lisp LEFTBRACKET}, {lisp RIGHTBRACKET}, {lisp STRINGDELIM}, and {lisp BREAKCHAR}. For purely symmetrical reasons, the atom {lisp SEPR}{index SEPR (Syntax Class)} corresponds to all separator characters. However, since the only separator characters are those that also appear in {lisp SEPRCHAR}, {lisp SEPR} and {lisp SEPRCHAR} are equivalent. Note that {fn GETSYNTAX} never returns {lisp BREAK} or {lisp SEPR} as a value although {fn SETSYNTAX} and {fn SYNTAXP} accept them as arguments. Instead, {fn GETSYNTAX} returns one of the disjoint basic syntax classes that comprise {lisp BREAK}. {lisp BREAK} as an argument to {fn SETSYNTAX} is interpreted to mean {lisp BREAKCHAR} if the character is not already of one of the {lisp BREAK} classes. Thus, if {lisp %(} is of class {lisp LEFTPAREN}, then {lisp (SETSYNTAX '%( 'BREAK)} doesn't do anything, since {lisp %(} is already a break character, but {lisp (SETSYNTAX '%( 'BREAKCHAR)} means make {lisp %(} be {it just} a break character, and therefore disables the {lisp LEFTPAREN} function of {lisp %(}. Similarly, if one of the format characters is disabled completely, e.g., by {lisp (SETSYNTAX '%( 'OTHER)}, then {lisp (SETSYNTAX '%( 'BREAK)} would make {lisp %(} be {it only} a break character; it would {it not} restore {lisp %(} as {lisp LEFTPAREN}. The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: {FnDef {FnName GETSEPR} {FnArgs RDTBL} {Text Returns a list of separator character codes in {arg RDTBL}. Equivalent to {lisp (GETSYNTAX 'SEPR {arg RDTBL})}. }} {FnDef {FnName GETBRK} {FnArgs RDTBL} {Text Returns a list of break character codes in {arg RDTBL}. Equivalent to {lisp (GETSYNTAX 'BREAK {arg RDTBL})}. }} {FnDef {FnName SETSEPR} {FnArgs LST FLG RDTBL} {Text Sets or removes the separator characters for {arg RDTBL}. {arg LST} is a list of charactors or character codes. {arg FLG} determines the action of {fn SETSEPR} as follows: If {arg FLG}={lisp NIL}, makes {arg RDTBL} have exactly the elements of {arg LST} as separators, discarding from {arg RDTBL} any old separator characters not in {arg LST}. If {arg FLG}=0, removes from {arg RDTBL} as separator characters all elements of {arg LST}. This provides an "{lisp UNSETSEPR}". If {arg FLG}=1, makes each of the characters in {arg LST} be a separator in {arg RDTBL}. {note what a terrible flag} If {arg LST}={lisp T}, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of {arg FLG}, i.e., {lisp (SETSEPR T)} is equivalent to {lisp (SETSEPR (GETSEPR T))}. If {arg RDTBL} is {lisp T}, then the characters are reset to those in the original system table. Returns {lisp NIL}. }} {FnDef {FnName SETBRK} {FnArgs LST FLG RDTBL} {Text Sets the break characters for {arg RDTBL}. Similar to {fn SETSEPR}. }} As with {fn SETSYNTAX} to the {lisp BREAK} class, if any of the list- or string-delimiting break characters are disabled by an appropriate {fn SETBRK} (or by making it be a separator character), its special action for {fn READ} will {it not} be restored by simply making it be a break character again with {fn SETBRK}. However, making these characters be break characters when they already {it are} will have no effect. The action of the {lisp ESCAPE} character {index %}(normally {lisp %}) is not affected by {fn SETSEPR} or {fn SETBRK}. It can be disabled by setting its syntax to the class {lisp OTHER}, and other characters can be used for escape on input by assigning them the class {lisp ESCAPE}. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as {lisp %}. That is, on output, characters of special syntax that need to be preceded by the {lisp ESCAPE} character will always be preceded by {lisp %}, independent of the syntax of {lisp %} or which, if any characters, have syntax {lisp ESCAPE}.{note How unfortunate} The following function can be used for defeating the action of the {lisp ESCAPE} character or characters: {FnDef {FnName ESCAPE} {FnArgs FLG RDTBL} {Text If {arg FLG}={lisp NIL}, makes characters of class {lisp ESCAPE} behave like characters of class {lisp OTHER} on input. Normal setting is {lisp (ESCAPE T)}. {fn ESCAPE} returns the previous setting. {note (ESCAPE NIL RDTBL) is similar to (SETSYNTAX '%% 'OTHER RDTBL), but does not rely on knowing what the escape char(s) is (are), and there seem to be some slightly bizarre differences with respect to the balancing of parentheses on typein. In any case, this function seems to be a bit of special case; is it worthy of continued life? --bvm} }} }{End SubSec Syntax Classes} {Begin SubSec Read Macros} {Title Read Macros} {Text {Tag ReadMacros} {index *PRIMARY* Read macros} Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: {lispcode ({arg TYPE} {arg OPTION{sub 1}} {ellipsis} {arg OPTION{sub N}} {arg FN})} where {arg TYPE} is one of {lisp MACRO}, {lisp SPLICE}, or {lisp INFIX}, and {arg FN} is the name of a function or a lambda expression. Whenever {fn READ} encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to {fn READ}. The interpretation of the value returned depends on the type of read macro: {Begin LabeledList type of read macro} {Label {lisp MACRO}} {Text {index MACRO (type of read macro)}This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause {lisp ~EXPR} to be read as {lisp (NOT EXPR)}, one could define {lisp ~} as the read macro: {lispcode [MACRO (LAMBDA (FL RDTBL) (LIST 'NOT (READ FL RDTBL]} } {Label {lisp SPLICE}} {Text {index SPLICE (type of read macro)}The result (which should be a list or {lisp NIL}) is spliced into the input using {fn NCONC}. For example, if {lisp $} is defined by the read macro: {lispcode (SPLICE (LAMBDA NIL (APPEND FOO)))} and the value of {lisp FOO} is {lisp (A B C)}, then when the user inputs {lisp (X $ Y)}, the result will be {lisp (X A B C Y)}. {note it is an open question about what should happen if a splice macro char is the first char typed on a line. Should it return nothing, or should it be treated like a MACRO ?? ask Ron Kaplan.} } {Label {lisp INFIX}} {Text {index INFIX (type of read macro)}The associated function is called with a third argument, which is a list, in {fn TCONC} format ({PageRef Fn TCONC}), of what has been read at the current level of list nesting. The function's value is taken as a new {fn TCONC} list which replaces the old one. For example, the infix operator {lisp +} could be defined by the read macro: {lispcode (INFIX (LAMBDA (FL RDTBL Z) (RPLACA (CDR Z) (LIST (QUOTE IPLUS) (CADR Z) (READ FL RDTBL))) Z))} {note isn't there a better example???} If an {lisp INFIX} read macro character is encountered {it not} in a list, the third argument to its associated function is {lisp NIL}. If the function returns {lisp NIL}, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a {fn TCONC} list of one element, that element is the value of the {fn READ}. If it returns a {fn TCONC} list of more than one element, the list is the value of the {fn READ}. } {End LabeledList type of read macro} The specification for a read macro character can be augmented to specify various options {arg OPTION{sub 1}} {ellipsis} {arg OPTION{sub N}}, e.g., {lisp (MACRO FIRST IMMEDIATE {arg FN})}. The following three disjoint options specify when the read macro character is to be effective: {Begin LabeledList various options} {Label {lisp ALWAYS}{index ALWAYS (type of read macro)}} {Text The default. The read macro character is always effective (except when preceded by the {lisp %} character), and is a break character, i.e., a member of {lisp (GETSYNTAX 'BREAK {arg RDTBL})}. } {Label {lisp FIRST}{index FIRST (type of read macro)}} {Text The character is interpreted as a read macro character {it only} when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class {lisp OTHER}. The read macro character is {it not} a break character. For example, the quote character is a {lisp FIRST} read macro character, so that {lisp DON'T} is read as the single atom {lisp DON'T}, rather than as {lisp DON} followed by {lisp (QUOTE T)}. } {Label {lisp ALONE}{index ALONE (type of read macro)}} {Text The read macro character is {it not} a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. } {End LabeledList various options} Making a {lisp FIRST} or {lisp ALONE} read macro character be a break character (with {fn SETBRK}) disables the read macro interpretation, i.e., converts it to syntax class {lisp BREAKCHAR}. Making an {lisp ALWAYS} read macro character be a break character is a no-op. The following two disjoint options control whether the read macro character is to be protected by the {lisp ESCAPE} character on output when a litatom containing the character is printed: {Begin LabeledList more various options} {Label {lisp ESCQUOTE} or {lisp ESC}{index ESCQUOTE (type of read macro)}{index ESC (type of read macro)}} {Text The default. When printed with {fn PRIN2}, the read macro character will be preceded by the output escape character ({lisp %}) as needed to permit the atom containing it to be read correctly. Note that for {lisp FIRST} macros, this means that the character need be quoted only when it is the first character of the atom. } {Label {lisp NOESCQUOTE} or {lisp NOESC}{index NOESCQUOTE (type of read macro)}{index NOESC (type of read macro)}} {Text The read macro character will always be printed without an escape. For example, the {lisp ?} read macro in the {lisp T} read table is a {lisp NOESCQUOTE} character. Unless you are very careful what you are doing, read macro characters in {lisp FILERDTBL} should never be {lisp NOESCQUOTE}, since symbols that happen to contain the read macro character will not read back in correctly. {note As far as I can tell, the only reason NOESCQUOTE exists is because the printer used to be too stupid to put the escape character out in front of FIRST and ALWAYS macros exactly when needed (i.e., when the character is the first or only character in a symbol, respectively). NOESCQUOTE ought to be flushed now. --bvm} } {End LabeledList more various options} The following two disjoint options control when the macro's function is actually executed: {Begin LabeledList still more various options} {Label {lisp IMMEDIATE} or {lisp IMMED}{index IMMEDIATE (type of read macro)}{index IMMED (type of read macro)}} {Text The read macro character is immediately activated, i.e., the current line is terminated, as if an {lisp EOL} had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. {lisp IMMEDIATE} read macro characters enable the user to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is {it typed}. Characters that cause action as soon as they are typed are interrupt characters (see {PageRef Term Interrupt Characters}). Note that since an {lisp IMMEDIATE} macro causes any input before it to be sent to the reader, characters typed before an {lisp IMMEDIATE} read macro character cannot be erased by control-A{index Control-A} or control-Q{index control-Q} once the {lisp IMMEDIATE} character has been typed, since they have already passed through the line buffer. However, an {lisp INFIX} read macro can still alter some of what has been typed earlier, via its third argument. } {Label {lisp NONIMMEDIATE} or {lisp NONIMMED}{index NONIMMEDIATE (type of read macro)}{index NONIMMED (type of read macro)}} {Text The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. } {End LabeledList still more various options} Making a read macro character be both {lisp ALONE} and {lisp IMMEDIATE} is a contradiction, since {lisp ALONE} requires that the next character be input in order to see if it is a break or separator character. Thus, {lisp ALONE} read macros are always {lisp NONIMMEDIATE}, regardless of whether or not {lisp IMMEDIATE} is specified. Read macro characters can be "nested". For example, if {lisp =} is defined by {lispcode (MACRO (LAMBDA (FL RDTBL) (EVAL (READ FL RDTBL))))} and {lisp !} is defined by {lispcode (SPLICE (LAMBDA (FL RDTBL) (READ FL RDTBL)))} then if the value of {lisp FOO} is {lisp (A B C)}, and {lisp (X =FOO Y)} is input, {lisp (X (A B C) Y)} will be returned. If {lisp (X !=FOO Y)} is input, {lisp (X A B C Y)} will be returned. Note: If a read macro's function calls {fn READ}, and the {fn READ} returns {lisp NIL}, the function cannot distinguish the case where a {lisp RIGHTPAREN} or {lisp RIGHTBRACKET} followed the read macro character, (e.g. "{lisp (A B ')}"), from the case where the atom {lisp NIL} (or "{lisp ()}") actually appeared. In Interlisp-D, a {lisp READ} inside of a read macro when the next input character is a {lisp RIGHTPAREN} or {lisp RIGHTBRACKET} reads the character and returns {lisp NIL}, just as if the {lisp READ} had not occurred inside a read macro. If a call to {fn READ} from within a read macro encounters an unmatched {lisp RIGHTBRACKET} {it within} a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as {lisp (A B '(C D]} works correctly. {FnDef {FnName INREADMACROP} {FnArgs} {Text Returns {lisp NIL} if currently {it not} under a read macro function, otherwise the number of unmatched left parentheses or brackets. }} {Begin Comment Interlisp-10 specific} Interlisp-10 specific: In Interlisp-10, reading a single {lisp RIGHTPAREN} or {lisp RIGHTBRACKET} via a {fn READ} inside of a read macro function is disallowed. If this occurs, the paren/bracket is put back into the input buffer, and a {lisp READ-MACRO CONTEXT ERROR}{index READ-MACRO CONTEXT ERROR Error} is generated. The following two functions are useful for avoiding this error: {FnDef {FnName SETREADMACROFLG} {FnArgs FLG} {Text In Interlisp-10, resets the "read macro" flag, i.e., the internal system flag that informs {fn READ} that it is under a read macro function, and causes it to generate a {lisp READ-MACRO CONTEXT ERROR}, if an unmatched {lisp )} or {lisp ]} is encountered. Returns the previous value of the flag. The main use for this is when debugging read macro functions: to avoid spurious {lisp READ-MACRO CONTEXT} error messages when typing into breaks, the user can put {lisp (SETREADMACROFLG)} on {var BREAKRESETFORMS}{index BREAKRESETFORMS Var} ({PageRef Var BREAKRESETFORMS}). }} The {lisp READ-MACRO CONTEXT} error does not occur {End Comment Interlisp-10 specific} {FnDef {FnName READMACROS} {FnArgs FLG RDTBL} {Text If {arg FLG}={lisp NIL}, turns off action of read macros in read table {arg RDTBL}. If {arg FLG}={lisp T}, turns them on. Returns previous setting. {note Interlisp-10 only has FLG arg, so only can turn ALL read macros on/off. Is there any way to turn off all read macros (in all RDTBLs) in Interlisp-D??} }} The following read macros are standardly defined in Interlisp in the {lisp T} and {lisp EDITRDTBL} read tables: {Begin LabeledList Standard Read Macros} {Label {lisp '} (single-quote){index *PRIMARY* ' (Read macro)}} {Text Returns the next expression, wrapped in a call to {lisp QUOTE}; e.g., {lisp 'FOO} reads as {lisp (QUOTE FOO)}. The macro is defined as a {lisp FIRST} read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. {note I see no good reason for this latter, given that you can always use %. --bvm} } {Label control-Y{index *PRIMARY* Control-Y}{Tag control-YReadMacro}} {Text Defined in {lisp T} and {lisp EDITRDTBL}. Returns the result of evaluating the next expression. For example, if the value of {lisp FOO} is {lisp (A B)}, then {lisp (LIST 1 {it control-Y}FOO 2)} is read as {lisp (LIST 1 (A B) 2)}. Note that no structure is copied; the third element of that input expression is still {lisp EQ} to the value of {lisp FOO}. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading {lisp (KEY1 {it control-Y}(ARRAY 10))} has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with {fn SETTERMCHARS} ({PageRef Fn SETTERMCHARS}). } {Label {lisp `} (backquote){index *PRIMARY* ` (backquote) (Read Macro)}{index *PRIMARY* Backquote (`) Term}{index BQUOTE Fn}{Tag BQUOTE}} {Text Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. Within the backquoted expression, the character "{lisp ,}" (comma) introduces a form to be evaluated. The value of a form preceded by "{lisp ,@}" is to be spliced in, using {lisp APPEND}. If it is permissible to destroy the list being spliced in (i.e., {fn NCONC} may be used in the translation), then "{lisp ,.}" can be used instead of "{lisp ,@}". For example, if the value of {lisp FOO} is {lisp (1 2 3 4)}, then the form {lispcode `(A ,(CAR FOO) ,@(CDDR FOO) D E)} evaluates to {lisp (A 1 3 4 D E)}; it is logically equivalent to writing {lispcode (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E))))}. Backquote is particularly useful for writing macros. For example, the body of a macro that refers to {lisp X} as the macro's argument list might be {lispcode `(COND ((FIXP ,(CAR X)) ,(CADR X)) (T .,(CDDR X)))} which is equivalent to writing {lispcode (LIST 'COND (LIST (LIST 'FIXP (CAR X)) (CADR X)) (CONS 'T (CDDR X)))} Note that comma does {it not} have any special meaning outside of a backquote context. For users without a backquote character on their keyboards, backquote can also be written as {lisp |'} (vertical-bar, quote). {Note: can be written (BQUOTE form) completely independent of read macro} } {Label {lisp ?}{index *PRIMARY* ? (Read Macro)}} {Text Implements the {lisp ?=} command for on-line help regarding the function currently being "called" in the typein (see {PageRef (in TTYIN) ?=}). } {Label {lisp |} (vertical bar){index *PRIMARY* | (Read Macro)}} {Text When followed by an end of line, tab or space, {lisp |} is ignored, i.e., treated as a separator character, enabling the editor's {var CHANGECHAR} feature ({PageRef Var CHANGECHAR}). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: {lisp '} (quote) -- A synonym for backquote. {lisp .} (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for control-Y. {lisp ,} (comma) -- Returns the evaluation of the next expression {it at load time}, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. {lisp O} or {lisp o} (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, {lisp |o12} = {lisp 10} (decimal). {lisp B} or {lisp b} -- Treats the next number as binary, i.e., reads it in radix 2. For example, {lisp |b101} = {lisp 5} (decimal). {lisp X} or {lisp x} -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The upper-case letters {lisp A} though {lisp F} are used as the digits after {lisp 9}. For example, {lisp |x1A} = {lisp 26} (decimal). {lisp R} or {lisp r} -- Reads the next number in the radix specified by the (decimal) number that appears between the {lisp |} and the {lisp R}. When inputting a number in a radix above ten, the upper-case letters {lisp A} through {lisp Z} can be used as the digits after {lisp 9} (but there is no digit above {lisp Z}, so it is not possible to type all base-99 digits). For example, {lisp |3r120} reads 120 in radix 3, returning 15. {lisp (}, {lisp {lbracket}}, {lisp ↑} -- Used internally by {fn HPRINT} and {fn HREAD} ({PageRef Fn HPRINT})to print and read unusual expressions. } {End LabeledList Standard Read Macros} The dispatching characters that are letters can appear in either upper or lower case. {note there are others, but can't go explaining them until we have more common lisp --bvm} {Begin comment Interlisp-10 specific} Interlisp-10 specific: control-W{index control-W} Defined in {lisp T} and {lisp EDITRDTBL}, Interlisp-10 only. An {lisp IMMEDIATE} read macro that deletes the previous expression. In Interlisp-D, {note and -VAX?} control-W is an editing character that deletes the previous "word". {End comment Interlisp-10 specific} }{End SubSec Read Macros} }{End SubSec Read Tables}