{Begin SubSec Readtables}
{Title Readtables}
{Text

{note Readtables and terminal tables were designed and implemented by D. C. Lewis.}

{Tag ReadTables}

{index *BEGIN* readtables}
{index *PRIMARY* readtables}


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 readtables.  Readtables are objects that specify the syntactic properties of characters for input routines.  Since the input routines parse character sequences into objects, the readtable in use determines which sequences are recognized as literal atoms, strings, list structures, etc.

Most Interlisp input functions take an optional readtable argument, which specifies the readtable to use when reading an expression.  If {lisp NIL} is given as the readtable, the "primary readtable"{index *PRIMARY* primary readtable} is used.  If {lisp T} is specified, the system terminal readtable 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 readtable.  Some output functions also take a readtable argument.  For example, {fn PRIN2} prints an expression so that it would be read in correctly using a given readtable.


The Interlisp system uses three readtables:  {lisp T} for input/output from terminals, the value of {var FILERDTBL}{index *PRIMARY* FILERDTBL Var} for input/output from files, and the value of {var EDITRDTBL}{index EDITRDTBL Var} for input from terminals while in the editor.  These three tables are initially copies of the {lisp ORIG} readtable, with changes made to some of them to provide read macros ({PageRef Tag ReadMacros}) that are specific to terminal input or file input.  Using the functions described below, the user may further change, reset, or copy these tables.  The user can also create new readtables, and either explicitly pass them to input/output functions as arguments, or install them as the primary readtable, via {fn SETREADTABLE}, and then not specify a {arg RDTBL} argument, i.e., use {lisp NIL}.



{Begin SubSec Readtable Functions}
{Title Readtable Functions}
{Text

{FnDef {FnName READTABLEP} {FnArgs RDTBL}
{Text
Returns {arg RDTBL} if {arg RDTBL} is a real readtable ({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 readtable.  If {arg RDTBL} is a real readtable, returns {arg RDTBL}. Otherwise, generates an {lisp ILLEGAL READTABLE} error.{index ILLEGAL READTABLE Error}
}}


{FnDef {FnName SETREADTABLE} {FnArgs RDTBL FLG}
{Text
Sets the primary readtable to {arg RDTBL}.  If {arg FLG}={lisp T}, {fn SETREADTABLE} sets the system terminal readtable, {lisp T}.  Note that the user can reset the other system readtables 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 readtable.  Returns the previous setting of the primary readtable, 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 readtable, {lisp NIL}, {lisp T}, or {lisp ORIG} (in which case {fn COPYREADTABLE} returns a copy of the {it original} system readtable), 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 readtable.
}}


{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 readtable.  In addition, {arg FROM} can be {lisp ORIG}, meaning use the system's original readtable.
}}


}{End SubSec Readtable Functions}



{Begin SubSec Syntax Classes}
{Title Syntax Classes}
{Text

{Tag SyntaxClass}


{index *PRIMARY* syntax classes}

A readtable 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 {it break} characters{index *PRIMARY* 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 readtable 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 readtable provides the {it association} between a character and its syntax class, and the input/output routines enforce the abstraction by using readtables to drive the parsing.

{note Special thanks to J. Strother Moore for this lucid explanation taken from {lisp [MOO]}.}



The  functions below are used to obtain and set the syntax class of a character in a readtable.  {arg CH} can either be a character code (a number), or a character (a single-character atom); those Interlisp objects that happen to be both, viz., one-digit numbers, are interpreted as character codes.  For example, in Interlisp-10, 1 indicates control-A, and 49 indicates the {it character} 1.


Note: Terminal tables, described in {PageRef Tag TerminalTables}, also associate characters with syntax classes, and they can also be manipulated with the functions below.  The set of readtable 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 readtable 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 readtable 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{index read-macro characters} (see {PageRef Tag READMACROS}).}

{item
{lisp NIL}, {lisp T}, {lisp ORIG}, or a real readtable 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 readtable.
}

{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 readtable 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 atom (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 readtable:


{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 readtable 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 (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{index % (escape character)}:


{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 *BEGIN* 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 file and readtable 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

{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:

{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, {lisp +} could be defined by:

{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:

{index read-macro options}

{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 escape 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.  For example, {lisp *} is an {lisp ALONE} read-macro character in order to implement the comment pointer feature (see {PageRef Tag COMMENTPOINTER}).
}

{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:

{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 %}).
}

{Label {lisp NOESCQUOTE} or {lisp NOESC}{index NOESCQUOTE (type of read-macro)}{index NOESC (type of read-macro)}}
{Text
The read-macro character will be printed without an escape, e.g., {lisp '} 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 is 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).  This should be fixed, and NOESCQUOTE flushed.  --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 Tag InterruptChars}).

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 or 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.


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.  Therefore, 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 INREADMACROP} {FnArgs}
{Text
Returns {lisp NIL} if currently {it not} under a read-macro function, otherwise the number of unmatched left parentheses or brackets.
}}


{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 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 READMACROS} {FnArgs FLG RDTBL}
{Text
If {arg FLG}={lisp NIL}, turns off action of read-macros.  If {arg FLG}={lisp T},
turns them on.  Returns previous setting.

In Interlisp-D, turns off/on action of read-macros in readtable {arg RDTBL}.

{note Interlisp-10 only has FLG arg.}
{note 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:

{Begin LabeledList Standard Read Macros}

{Label {lisp '} (single-quote)}
{Text
Currently defined only in {lisp T} and {lisp EDITRDTBL}.  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 {lisp control-Y}{index 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 (1 (A B) 2)}, but note that no structure is copied; the {lisp CADR} 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 `} (back-quote){index ` (back-quote)}{index back-quote}{Tag BQUOTE}}
{Text
Back-quote makes it easier to write programs to construct complex data structures.  Back-quote is like quote, except that within the back-quoted expression, forms can be evaluated.  The general idea is that the back-quoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something.

Within the back-quoted expression, the character "{lisp ,}" (comma) introduces a form to be evaluated.  A form preceded by "{lisp .,}" is to be spliced in, using {lisp APPEND}.  Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the back-quoted expression is evaluated.  That is, the back-quote macro returns an expression which, when evaluated, produces the desired structure.

For example, if the value of {lisp FOO} is {lisp (1 2 3 4)}, then the form {lisp `(A ,(CAR FOO) .,(CDDR FOO) D E)} evaluates to {lisp (A 1 3 4 D E)}; it is logically equivalent to writing {lisp (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E))))}.  Back-quote is particularly useful for writing macros.  For example,

{lispCode
`(COND
   ((FIXP ,(CAR X))
      ,(CADR X))
   (T .,(CDDR X)))}

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 back-quote context.

For users without a back-quote character on their keyboards, back-quote can also be written as {lisp |'} (vertical-bar, quote).  On Xerox 1100's, back-quote can be typed as shift-linefeed.

{Note: can be written (BQUOTE form) completely independent of read-macro}
}


{Label {lisp ?}{index ? (Read Macro)}}
{Text
Defined in {lisp T} and {lisp EDITRDTBL}.  Implements the {lisp ?=} command for on-line help regarding the function currently being "called" in the typein (see {PageRef PAcom ?=}).
}

{Label {lisp *}}
{Text
Defined in {lisp FILERDTBL} only.  Implements the comment pointer feature for saving space by keeping the text of comments outside memory ({PageRef Tag COMMENTPOINTER}).
}

{Label {lisp control-W}{index control-W}}
{Text
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". 
}

{Label {lisp |} (vertical bar){index | (vertical bar) Term}}
{Text
When followed by {lisp '} (quote), is a synonym for back-quote; followed by certain other characters, it is used by {fn HPRINT} and {fn HREAD} to print and read in unusual expressions; otherwise is ignored, i.e., treated as a separator character, enabling the editor's {var CHANGECHAR} feature ({PageRef Var CHANGECHAR}). 
}



{End  LabeledList Standard Read Macros}



{index *END* read-macros}


}{End SubSec Read-Macros}



{index *END* readtables}



}{End SubSec Readtables}