{Begin SubSec Litatoms} {Title Litatoms} {Text {index *PRIMARY* litatoms} A "litatom" (for "literal atom"){index literal atoms} is an object which conceptually consists of a print name, a value, a function definition, and a property list. In some Lisp dialects, litatoms are also known as "symbols."{index symbols} A litatom is read as any string of non-delimiting characters that cannot be interpreted as a number. The syntatic characters that delimit litatoms are called separator or break characters (see {PageRef Tag ReadTables}) and normally are space, end-of-line, line-feed, {lisp (} (left paren), {lisp )} (right paren), {lisp "} (double quote), {lisp [} (left bracket), and {lisp ]} (right bracket). However, any character may be included in a litatom by preceding it with the escape character{index escape character} {lisp %}.{index % (escape character)} Here are some examples of litatoms: {lispcode A wxyz 23SKIDDOO %] 3.1415+17 Long% Litatom% With% Embedded% Spaces} {note para about how CLISP translates A+B into something else---warning about not using +-*/ in litatoms??? note about other special chars like quote??} Litatoms are printed by {fn PRINT} and {fn PRIN2} as a sequence of characters with {lisp %}'s inserted before all delimiting characters (so that the litatom will read back in properly). Litatoms are printed by {fn PRIN1} as a sequence of characters without these extra {lisp %}'s. For example, the litatom consisting of the five characters {lisp A}, {lisp B}, {lisp C}, {lisp (}, and {lisp D} will be printed as {lisp ABC%(D} by {fn PRINT} and {lisp ABC(D} by {fn PRIN1}. Litatoms can also be constructed by {fn PACK}, {fn PACK*}, {fn SUBATOM}, {fn MKATOM}, and {fn GENSYM} (which uses {fn MKATOM}). Litatoms are unique. In other words, if two litatoms print the same, they will {it always} be {fn EQ}. Note that this is {it not} true for strings, large integers, floating point numbers, and lists; they all can print the same without being {lisp EQ}. Thus if {fn PACK} or {fn MKATOM} is given a list of characters corresponding to a litatom that already exists, they return a pointer to that litatom, and do {it not} make a new litatom. Similarly, if the read program is given as input a sequence of characters for which a litatom already exists, it returns a pointer to that litatom. Note: Interlisp is different from other Lisp dialects which allow "uninterned" litatoms. Note: Litatoms are limited to 255 characters in Interlisp-D; 127 characters in Interlisp-10. {note -VAX ???} Attempting to create a larger litatom either via {fn PACK} or by typing one in (or reading from a file) will cause an error, {lisp ATOM TOO LONG}.{index ATOM TOO LONG Error} {Begin SubSec Using Litatoms as Variables} {Title Using Litatoms as Variables} {Text {note "binding" or "value"??} Litatoms are commonly used as variables. Each litatom has a "top level" variable binding, which can be an arbitrary Interlisp object. Litatoms may also be given special variable bindings within {fn PROG}s or function calls, which only exist for the duration of the function. When a litatom is evaluated, the "current" variable binding is returned. This is the most recent special variable binding, or the top level binding if the litatom has not been rebound. {fn SETQ} is used to change the current binding. For more information on variable bindings in Interlisp, see {PageRef Tag Stack}. Note: The compiler ({PageRef Tag Compiler}) treats variables somewhat differently than the interpreter, and the user has to be aware of these differences when writing functions that will be compiled. For example, variable references in compiled code are not checked for {lisp NOBIND}, so compiled code will not generate unbound atom errors. In general, it is better to debug interpreted code, before compiling it for speed. The compiler offers some facilities to increase the efficiency of variable use in compiled functions. Global variables ({PageRef Tag GLOBALVARS}) can be defined so that the entire stack is not searched at each variable reference. Local variables ({PageRef Tag LOCALVARS}) allow compiled functions to access variable bindings which are not on the stack, which reduces variable conflicts, and also makes variable lookup faster. By convention, a litatom whose top level binding is to the litatom {atom NOBIND}{index *PRIMARY* NOBIND Litatom} is considered to have no top level binding. If a litatom has no local variable bindings, and its top level value is {atom NOBIND}, attempting to evaluate it will cause an unbound atom error.{index unbound atom}{index UNBOUND ATOM Error} The two litatoms {atom T}{index T Litatom} and {atom NIL}{index NIL Litatom} always evaluate to themselves. Attempting to change the binding of {atom T} or {atom NIL} with the functions below will generate the error {lisp ATTEMPT TO SET T}{index ATTEMPT TO SET T Error} or {lisp ATTEMPT TO SET NIL}.{index ATTEMPT TO SET NIL Error} The following functions (except {fn BOUNDP}) will also generate the error {lisp ARG NOT LITATOM},{index ARG NOT LITATOM Error} if not given a litatom. {FnDef {FnName BOUNDP} {FnArgs VAR} {Text Returns {lisp T} if {arg VAR} has a special variable binding (even if bound to {lisp NOBIND}), or if {arg VAR} has a top level value other than {atom NOBIND}; otherwise {lisp NIL}. In other words, if {arg X} is a litatom, {lisp (EVAL {arg X})} will cause an {lisp UNBOUND ATOM} error if and only if {lisp (BOUNDP {arg X})} returns {lisp NIL}.{index UNBOUND ATOM Error} {note Jerico does this differently. Vax?? ---lmm} }} {FnDef {FnName SET} {FnArgs VAR VALUE} {Text Sets the "current" variable binding of {arg VAR} to {arg VALUE}, and returns {arg VALUE}. Note that {fn SET} is a normal lambda spread function, so both {arg VAR} and {arg VALUE} are evaluated before it is called. Thus, if the value of {lisp X} is {lisp B}, and the value of {lisp Y} is {lisp C}, then {lisp (SET {lisp X} {lisp Y})} would result in {lisp B} being set to {lisp C}, and {lisp C} being returned as the value of {fn SET}. }} {FnDef {FnName SETQ} {FnArgs VAR VALUE} {Type NOSPREAD NLAMBDA} {Text Nlambda version of {fn SET}; {arg VAR} is not evaluated, {arg VALUE} is.{foot Since {fn SETQ} is an nlambda, {it neither} argument is evaluated during the calling process. However, {fn SETQ} itself calls {fn EVAL} on its second argument. Note that as a result, typing {lisp (SETQ VAR FORM)} and {lisp SETQ(VAR FORM)} to the Interlisp executive is equivalent: in both cases {lisp VAR} is not evaluated, and {lisp FORM} is. }{comment endfootnote} Thus if the value of {lisp X} is {lisp B} and the value of {lisp Y} is {lisp C}, {lisp (SETQ X Y)} would result in {lisp X} (not {lisp B}) being set to {lisp C}, and {lisp C} being returned. {note when compiled, SETQ is treated specially, so that VAR can be a local variable ---jonl} {note It appears that SETQ evaluates all of its arguments (except the first) to allow (SETQ X Y + 1) to error out to CLISP (when + is evaluated). Yuk! ---mjs} }} {FnDef {FnName SETQQ} {FnArgs VAR VALUE} {Type NLAMBDA} {Text Like {fn SETQ} except that neither argument is evaluated, e.g., {lisp (SETQQ X (A B C))} sets {lisp X} to {lisp (A B C)}. }} {Begin Note} Date: 13 June 1982 11:12 pm PDT (Sunday) From: JonL.PA Subject: Re: New FILEPKG command: INITVARS; also RPAQ? etc. Re RPAQ? (which looks equivalent to the MacLisp SETQ-IF-UNBOUND), I've often wanted yet a third version of SETQ which does the setqing only if the value is non-null (but returns the value obtained regardless). See example below. I've also heard lots of other people complain about Lisp's lack of "pronouns" a la the "IT" of lispx; SETQ-if-non-null catches some high percentages of these cases. Lack of "pronouns" means that you often see code like (IF (GET X 'FROBULATE) THEN (PRINT (GET X 'FROBULATE))) where you'd like to write (IF (GET X 'FROBULATE) THEN (PRINT it)) Example use of SETQ-IF-VALUE-NON-NULL: (COND ((SETQ.IFNN WHATTODO (FIND.MORE.WORK)) (CARRYON.ANEW WHATTODO)) ((ASKUSER ...if I should just try "polishing-up" the old some more ...) (POLISHUP.OLD WHATTODO)) (T (GO AROUNDLOOP))) {End Note} {FnDef {FnName GETTOPVAL} {FnArgs VAR} {Text Returns the top level value of {arg VAR} (even if {lisp NOBIND}), regardless of any intervening local bindings. }} {FnDef {FnName SETTOPVAL} {FnArgs VAR VALUE} {Text Sets the top level value of {arg VAR} to {arg VALUE}, regardless of any intervening bindings, and returns {arg VALUE}. }} A major difference between various Interlisp implementations is the way that variable bindings are implemented. Interlisp-10 and Interlisp-Jerico use what is called "shallow" binding. Interlisp-D and Interlisp-VAX use what is called "deep" binding. In a deep binding system,{index deep binding} a variable is bound by saving on the stack the variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding. If the variable is not found on the stack, the top level binding is retrieved from a "value cell"{index value cell} associated with the variable. In a "shallow" binding system,{index shallow binding} a variable is bound by saving on the stack the variable name and the variable's old value and putting the new value in the variable's value cell. When a variable is accessed, its value is always found in its value cell. {fn GETTOPVAL} and {fn SETTOPVAL} are less efficient in a shallow binding system, because they have to search the stack for rebindings; it is more economical to simply rebind variables. In a deep binding system, {fn GETTOPVAL} and {fn SETTOPVAL} are very efficient since they do not have to search the stack, but can simply access the value cell directly. {fn GETATOMVAL} and {fn SETATOMVAL} can be used to access a variable's value cell, in either a shallow or deep binding system. {FnDef {FnName GETATOMVAL} {FnArgs VAR} {Text Returns the value in the value cell of {arg VAR}. In a shallow binding system, this is the same as {lisp (EVAL {arg ATM})}, or simply {arg VAR}. In a deep binding system, this is the same as {lisp (GETTOPVAL {arg VAR})}. }} {FnDef {FnName SETATOMVAL} {FnArgs ATM VALUE} {Text Sets the value cell of {arg VAR} to {arg VALUE}. In a shallow binding system, this is the same as {fn SET}; in a deep binding system, this is the same as {fn SETTOPVAL}. }} }{End SubSec Using Litatoms as Variables} {Begin SubSec Function Definition Cells} {Title Function Definition Cells} {Text {index function definition cells} Each litatom has a function definition cell, which is accessed when a litatom is used as a function. The mechanism for accessing and setting the function definition cell of a litatom is described on {PageRef Tag FunctionDefinition}. {note why not described here? ---lmm} }{End SubSec Function Definition Cells} {Begin SubSec Property Lists} {Title Property Lists} {Text {index *PRIMARY* property lists} Each litatom has a property list, which allows a set of named objects to be associated with the litatom. A property list associates a name, known as a "property name"{index *PRIMARY* property name} or "property", with an abitrary object, the "property value"{index *PRIMARY* property value} or simply "value". Sometimes the phrase "to store on the property {arg X}" is used, meaning to place the indicated information on a property list under the property name {arg X}. Property names are usually litatoms or numbers, although no checks are made. However, the standard property list functions all use {fn EQ} to search for property names, so they may not work with non-atomic property names. Note that the same object can be used as both a property name and a property value. Note: Many litatoms in the system already have property lists, with properties used by the compiler, the break package, DWIM, etc. Be careful not to clobber such system properties. The variable {var SYSPROPS}{index *PRIMARY* SYSPROPS Var} is a list of property names used by the system. {note SYSPROPS NOT COMPLETE!! ---lmm} The functions below are used to manipulate the propert lists of litatoms. Except when indicated, they generate the error {lisp ARG NOT LITATOM},{index ARG NOT LITATOM Error} if given an object that is not a litatom. {FnDef {FnName GETPROP} {FnArgs ATM PROP} {Text Returns the property value for {arg PROP} from the property list of {arg ATM}. Returns {lisp NIL} if {arg ATM} is not a litatom, or {arg PROP} is not found. Note that {fn GETPROP} also returns {lisp NIL} if there is an occurrence of {arg PROP} but the corresponding property value is {lisp NIL}; this can be a source of program errors. Note: {fn GETPROP} used to be called {lisp GETP}.{index GETP (old name of GETPROP)} }} {FnDef {FnName PUTPROP} {FnArgs ATM PROP VAL} {Text Puts the property {arg PROP} with value {arg VAL} on the property list of {arg ATM}. {arg VAL} replaces any previous value for the property {arg PROP} on this property list. Returns {arg VAL}. }} {FnDef {FnName ADDPROP} {FnArgs ATM PROP NEW FLG} {Text Adds the value {arg NEW} to the list which is the value of property {arg PROP} on the property list of {arg ATM}. If {arg FLG} is {lisp T}, {arg NEW} is {fn CONS}ed onto the front of the property value of {arg PROP}, otherwise it is {fn NCONC}ed on the end (using {fn NCONC1}). If {arg ATM} does not have a property {arg PROP}, or the value is not a list, then the effect is the same as {index PUTPROP FN}{lisp (PUTPROP {arg ATM} {arg PROP} (LIST {arg NEW}))}. {fn ADDPROP} returns the (new) property value. Example: {lispcode ← (PUTPROP 'POCKET 'CONTENTS NIL) NIL ← (ADDPROP 'POCKET 'CONTENTS 'COMB) (COMB) ← (ADDPROP 'POCKET 'CONTENTS 'WALLET) (COMB WALLET)} }} {FnDef {FnName REMPROP} {FnArgs ATM PROP} {Text Removes all occurrences of the property {arg PROP} (and its value) from the property list of {arg ATM}. Returns {arg PROP} if any were found, otherwise {lisp NIL}. }} {FnDef {FnName REMPROPLIST} {FnArgs ATM PROPS} {Text Removes all occurrences of all properties on the list {arg PROPS} (and their corresponding property values) from the property list of {arg ATM}. Returns {lisp NIL}. }} {FnDef {FnName CHANGEPROP} {FnArgs X PROP1 PROP2} {Text Changes the property name of property {arg PROP1} to {arg PROP2} on the property list of {arg X}, (but does not affect the value of the property). Returns {arg X}, unless {arg PROP1} is not found, in which case it returns {lisp NIL}. }} {FnDef {FnName PROPNAMES} {FnArgs ATM} {Text Returns a list of the property names on the property list of {arg ATM}. {note includes prop names with nil values??} }} {FnDef {FnName DEFLIST} {FnArgs L PROP} {Text Used to put values under the same property name{index property name} on the property lists of several litatoms. {arg L} is a list of two-element lists. The first element of each is a litatom, and the second element is the property value{index property value} for the property {arg PROP}. Returns {lisp NIL}. For example, {lispcode (DEFLIST '( (FOO MA) (BAR CA) (BAZ RI) ) 'STATE)} puts {lisp MA} on {lisp FOO}'s {lisp STATE} property, {lisp CA} on {lisp BAR}'s {lisp STATE} property, and {lisp RI} on {lisp BAZ}'s {lisp STATE} property. }} Property lists are conventionally implemented as lists of the form {lispcode ({arg NAME{sub 1}} {arg VALUE{sub 1}} {arg NAME{sub 2}} {arg VALUE{sub 2}} {ellipsis})} although the user can store anything as the property list of a litatom. However, the functions which manipulate property lists observe this convention by searching down the property lists two {fn CDR}s at a time. Most of these functions also generate an error, {lisp ARG NOT LITATOM},{index ARG NOT LITATOM Error} if given an argument which is not a litatom, so they cannot be used directly on lists. ({lisp LISTPUT}, {lisp LISTPUT1}, {lisp LISTGET}, and {lisp LISTGET1} are functions similar to {lisp PUTPROP} and {lisp GETPROP} that work directly on lists. See {PageRef Fn LISTPUT}.) The property lists of litatoms can be directly accessed with the following functions: {FnDef {FnName GETPROPLIST} {FnArgs ATM} {Text Returns the property list of {arg ATM}. }} {FnDef {FnName SETPROPLIST} {FnArgs ATM LST} {Text If {arg ATM} is a non-{lisp NIL} litatom, sets the property list of {arg ATM} to be {arg LST}, and returns {arg LST} as its value. If {arg ATM} is {lisp NIL}, generates the error, {lisp ATTEMPT TO RPLAC NIL}{index ATTEMPT TO RPLAC NIL Error} (unless {arg LST} is also {lisp NIL}). {note ATTEMPT TO RPLAC NIL is a bad error message} }} {FnDef {FnName GETLIS} {FnArgs X PROPS} {Text Searches the property list of {arg X}, and returns the property list as of the first property on {arg PROPS} that it finds. For example, {lispcode ← (GETPROPLIST 'X) (PROP1 A PROP3 B A C) ← (GETLIS 'X '(PROP2 PROP3)) (PROP3 B A C)} Returns {lisp NIL} if no element on {arg PROPS} is found. {arg X} can also be a list itself, in which case it is searched as described above. If {arg X} is not a litatom or a list, returns {lisp NIL}. }} }{End SubSec Property Lists} {Begin SubSec Print Names} {Title Print Names} {Text {Tag PrintNames} {index *PRIMARY* print names} Each litatom has a print name, a string of characters that uniquely identifies that litatom. The term "print name" has been extended, however, to refer to the characters that are output when any object is printed. In Interlisp, all objects have print names, although only litatoms and strings have their print name explicitly stored. This section describes a set of functions which can be used to access and manipulate the print names of any object, though they are primarily used with the print names of litatoms. {note max length of print names is implementation dependent. for Interlisp-Vax, is 2↑16 characters} The print name of an object is those characters that are output when the object is printed using {fn PRIN1}, e.g., the print name of the litatom {lisp ABC%(D} consists of the five characters {lisp ABC(D}. The print name of the list {lisp (A B C)} consists of the seven characters {lisp (A B C)} (two of the characters are spaces). Sometimes we will have occasion to refer to a "{lisp PRIN2}-name."{index PRIN2-names} The {lisp PRIN2}-name of an object is those characters output when the object is printed using {lisp PRIN2}. Thus the {lisp PRIN2}-name of the litatom {lisp ABC%(D} is the {it six} characters {lisp ABC%(D}. Note that the {fn PRIN2}-name depends on what readtable is being used (see {PageRef Tag ReadTables}), since this determines where {lisp %}'s will be inserted. Many of the functions below allow either print names or {lisp PRIN2}-names to be used, as specified by {arg FLG} and {arg RDTBL} arguments. If {arg FLG} is {lisp NIL}, print names are used. Otherwise, {fn PRIN2}-names are used, computed with respect to the readtable {arg RDTBL} (or the current readtable, if {arg RDTBL} = {lisp NIL}). {note there is an inconsistancy: PRINT prints the PRIN2-name, and PRIN1 prints the print name. could be confusing.} Note: The print name of an integer depends on the setting of {fn RADIX} ({PageRef Fn RADIX}). The functions described in this section ({fn UNPACK}, {fn NCHARS}, etc.) define the print name of an integer as though the radix was 10, so that {lisp (PACK (UNPACK 'X9))} will always be {lisp X9} (and not sometimes {lisp X11}) regardless of the setting of {fn RADIX}. However, integers will still be {it printed} by {fn PRIN1} using the current radix. The user can force these functions to use print names in the current radix by changing the setting of the variable {var PRXFLG} (see {PageRef Var PRXFLG}). {FnDef {FnName MKATOM} {FnArgs X} {Text Creates and returns an atom whose print name is the same as that of the string {arg X} or, if {arg X} isn't a string, the same as that of {lisp (MKSTRING {arg X})}. Examples: {lispcode (MKATOM '(A B C)) => %(A% B% C%)} {lispcode (MKATOM "1.5") => 1.5} Note that the last example returns a number, not a litatom. It is a deeply-ingrained feature of Interlisp that no litatom can have the print name of a number. }} {FnDef {FnName SUBATOM} {FnArgs X N M} {Text Equivalent to {lisp (MKATOM (SUBSTRING {arg X} {arg N} {arg M}))}, but does not make a string pointer (see {PageRef Fn SUBSTRING}). Returns an atom made from the {arg N}th through {arg M}th characters of the print name of {arg X}. If {arg N} or {arg M} are negative, they specify positions counting backwards from the end of the print name. Examples: {lispcode (SUBATOM "FOO1.5BAR" 4 6) => 1.5} {lispcode (SUBATOM '(A B C) 2 -2) => A% B% C} }} {FnDef {FnName PACK} {FnArgs X} {Text If {arg X} is a list of atoms, {fn PACK} returns a single atom whose print name is the concatenation of the print names of the atoms in {arg X}. If the concatenated print name is the same as that of a number, {fn PACK} will return that number. For example, {lispcode (PACK '(A BC DEF G)) => ABCDEFG} {lispcode (PACK '(1 3.4)) => 13.4} {lispcode (PACK '(1 E -2)) => .01} Although {arg X} is usually a list of atoms, it can be a list of arbitrary Interlisp objects. The value of {fn PACK} is still a single atom whose print name is the concatenation of the print names of all the elements of {arg X}, e.g., {lispcode (PACK '((A B) "CD")) => %(A% B%)CD} If {arg X} is not a list or {lisp NIL}, {fn PACK} generates an error, {lisp ILLEGAL ARG}.{index ILLEGAL ARG Error} }} {FnDef {FnName PACK*} {FnArgs {arg X{sub 1}} {arg X{sub 2}} {ellipsis} {arg X{sub N}}} {Type NOSPREAD} {Text Nospread version of {fn PACK} that takes an arbitrary number of arguments, instead of a list. Examples:, {lispcode (PACK* 'A 'BC 'DEF 'G) => ABCDEFG} {lispcode (PACK* 1 3.4) => 13.4} }} {FnDef {FnName UNPACK} {FnArgs X FLG RDTBL} {Text Returns the print name of {arg X} as a list of single-characters atoms, e.g., {lispcode (UNPACK 'ABC5D) => (A B C 5 D)} {lispcode (UNPACK "ABC(D") => (A B C %( D)} If {arg FLG}={lisp T}, the {index PRIN2-names}{fn PRIN2}-name of {arg X} is used (computed with respect to {arg RDTBL}), e.g., {lispcode (UNPACK "ABC(D" T) => (%" A B C %( D %")} {lispcode (UNPACK 'ABC%(D" T) => (A B C %% %( D)} Note: {lisp (UNPACK {arg X})} performs {arg N} {fn CONS}es, where {arg N} is the number of characters in the print name of {arg X}. }} {FnDef {FnName DUNPACK} {FnArgs X SCRATCHLIST FLG RDTBL} {Text A destructive version of {fn UNPACK} that does not perform any {fn CONS}es but instead reuses the list {arg SCRATCHLIST}. If the print name is too long to fit in {arg SCRATCHLIST}, {fn DUNPACK} will extend it. If {arg SCRATCHLIST} is not a list, {fn DUNPACK} returns {lisp (UNPACK {arg X} {arg FLG} {arg RDTBL})}. {Note in Interlisp-10, gives error "DCHCON: SCRATCHLIST not a list" if SCRATCHLIST is not a list. Fix?? Doc??} }} {FnDef {FnName NCHARS} {FnArgs X FLG RDTBL} {Text Returns the number of characters in the print name of {arg X}. If {arg FLG}={lisp T}, the {index PRIN2-names}{fn PRIN2}-name is used. For example, {lispcode (NCHARS "ABC") => 3} {lispcode (NCHARS "ABC" T) => 5} }} {FnDef {FnName NTHCHAR} {FnArgs X N FLG RDTBL} {Text Returns the {arg N}th character of the print name of {arg X} as an atom. {arg N} can be negative, in which case it counts from the end of the print name, e.g., -1 refers to the last character, -2 next to last, etc. If {arg N} is greater than the number of characters in the print name, or less than minus that number, or 0, {fn NTHCHAR} returns {lisp NIL}. Examples: {lispcode (NTHCHAR 'ABC 2) => B} {lispcode (NTHCHAR 15.6 2) => 5} {lispcode (NTHCHAR 'ABC%(D -3 T) => %%} {lispcode (NTHCHAR "ABC" 2) => B} {lispcode (NTHCHAR "ABC" 2 T) => A} }} Note: {fn NTHCHAR} and {fn NCHARS} work much faster on objects that actually have an internal representation of their print name, i.e., litatoms and strings, than they do on numbers and lists, as they do not have to simulate printing. {FnDef {FnName L-CASE} {FnArgs X FLG} {Text Returns a lower case version of {arg X}.{index lower case} If {arg FLG} is {lisp T}, the first letter is capitalized. If {arg X} is a string, the value of {fn L-CASE} is also a string. If {arg X} is a list, {fn L-CASE} returns a new list in which {fn L-CASE} is computed for each corresponding element and non-{lisp NIL} tail of the original list. Examples: {lispcode (L-CASE 'FOO) => foo} {lispcode (L-CASE 'FOO T) => Foo} {lispcode (L-CASE "FILE NOT FOUND" T) => "File not found"} {lispcode (L-CASE '(JANUARY FEBRUARY (MARCH "APRIL")) T) => '(January February (March "April"))} }} {FnDef {FnName U-CASE} {FnArgs X} {Text Similar to {fn L-CASE}, except returns the upper case version of {Arg X}. }} {FnDef {FnName U-CASEP} {FnArgs X} {Text Returns {lisp T} if {arg X} contains no lower case letters; {lisp NIL} otherwise. }} {FnDef {FnName GENSYM} {FnArgs CHAR} {Text Returns a litatom of the form {lisp Xnnnn}, where {lisp X}={arg CHAR} (or {lisp A} if {arg CHAR} is {lisp NIL}) and {lisp nnnn} is an integer. Thus, the first one generated is {lisp A0001},{index A000n (gensym)} the second {lisp A0002}, etc. {fn GENSYM} provides a way of generating litatoms for various uses within the system. {note CHAR can have from 1-5 characters, but gensym always has 5 chars. (GENSYM 'X)->X0001 (GENSYM 'FOO)->FOO01 (GENSYM 'BLETCH)->error doc??} {note (GENSYM 0) => number} }} {VarDef {Name GENNUM} {Text The value of {var GENNUM}, initially {lisp 10000}, determines the next {fn GENSYM}, e.g., if {var GENNUM} is set to 10023, {lisp (GENSYM)}={lisp A0024}. }} The term "gensym" is used to indicate a litatom that was produced by the function {fn GENSYM}. Litatoms generated by {fn GENSYM} are the same as any other litatoms: they have property lists, and can be given function definitions. Note that the litatoms are not guaranteed to be new. For example, if the user has previously created {lisp A0012}, either by typing it in, or via {fn PACK} or {fn GENSYM} itself, when {var GENNUM} gets to {lisp 10011}, the next litatom returned by {fn GENSYM} will be the {lisp A0012} already in existence. {FnDef {FnName MAPATOMS} {FnArgs FN} {Text Applies {arg FN} (a function or lambda expression) to every litatom in the system. Returns {lisp NIL} For example, {lispcode (MAPATOMS (FUNCTION (LAMBDA(X) (if (GETD X) then (PRINT X)]} will print every litatom with a function definition. Note: In some implementations of Interlisp, unused litatoms may be garbage collected, which can effect the action of {fn MAPATOMS}. }} {note GC may change value in non-incremental GC system, which may throw away atoms ---lmm} }{End SubSec Print Names} {Begin SubSec Character Code Functions} {Title Character Code Functions} {Text {note perhaps this section should be put first??} {index *PRIMARY* character codes} Characters may be represented in two ways: as single-character atoms, or as integer character codes.{foot Interlisp-D uses an 8-bit character set, so the legal character codes range from 0 to 255. Interlisp-10 uses standard 7-bit ASCII, so the range is 0-127. }{comment endfootnote} In many situations, it is more efficient to use character codes, so Interlisp provides parallel functions for both representations. {Note there should be a paragraph talking about character codes. (ascii, etc.) If/When we put 16-bit char codes into Interlisp-D, we could put that info here.} {FnDef {FnName PACKC} {FnArgs X} {Text Similar to {fn PACK} except {arg X} is a list of character codes. For example, {lispcode (PACKC '(70 79 79)) => FOO} }} {FnDef {FnName CHCON} {FnArgs X FLG RDTBL} {Text Like {fn UNPACK}, except returns the print name of {arg X} as a list of character codes. If {arg FLG}={lisp T}, the {lisp PRIN2}-name{index PRIN2-names} is used. For example, {lispcode (CHCON 'FOO) => (70 79 79)} }} {FnDef {FnName DCHCON} {FnArgs X SCRATCHLIST FLG RDTBL} {Text Similar to {fn DUNPACK}. }} {FnDef {Name NTHCHARCODE} {Args X N FLG RDTBL} {Text Similar to {fn NTHCHAR}, except returns the character code of the {arg N}th character of the print name of {arg X}. If {arg N} is negative, it is interpreted as a count backwards from the end of {arg X}. If the absolute value of {arg N} is greater than the number of characters in {arg X}, or 0, then the value of {fn NTHCHARCODE} is {lisp NIL}. If {arg FLG} is {lisp T}, then the {fn PRIN2}-name of {arg X} is used, computed with respect to the readtable {arg RDTBL} }} {note move (RPLCHARCODE X N CHR) here??} {FnDef {FnName CHCON1} {FnArgs X} {Text Returns the character code of the first character of the print name of {arg X}; equal to {lisp (NTHCHARCODE {arg X} 1)}. }} {FnDef {FnName CHARACTER} {FnArgs N} {Text {arg N} is a character code. Returns the atom having the corresponding single character as its print name. {lispcode (CHARACTER 70) => F} }} {FnDef {FnName FCHARACTER} {FnArgs N} {Text Fast version of {fn CHARACTER} that compiles open. {note are there caviats? Is there any reason to ever use the other version, except for debugging purposes (tracing, advising)? no, use CHARACTER ---lmm flush??} {note in Interlisp-Vax, FCHARACTER compiles open and does not check if N is a valid character code} }} The following function makes it possible to gain the efficiency that comes from dealing with character codes without losing the symbolic advantages of character atoms: {note document COMPFLG argument??} {FnDef {Name CHARCODE} {Args C} {Type NLAMBDA} {Text Returns the character code structure specified by {arg C} (unevaluated). If {arg C} is a 1-character atom or string, the corresponding character code is simply returned. Thus, {lisp (CHARCODE A)} is 65, {lisp (CHARCODE 0)} is 48. If {arg C} is a list structure, the value is a copy of {arg C} with all the leaves replaced by the corresponding character codes. For instance, {lisp (CHARCODE (A (B C))) => (65 (66 67))} {fn CHARCODE} permits easy specification of non-printable ASCII character codes: A multi-character litatom or string whose first character is {lisp ↑} is interpreted as the control-character corresponding to its second character. Thus, {lisp (CHARCODE ↑A)} is 1, the code for control-A. Also, if a multi-character litatom or string begins with {lisp #}, this signifies a "meta-character", with a code between 128 to 255. {lisp #} and {lisp ↑} may be combined, so {lisp (CHARCODE #↑A)} is 129. (Note: Interlisp-10 cannot directly represent meta-characters as character litatoms, because it only supports 7-bit characters.) The following key litatoms are mapped into the indicated codes: {lisp CR} (13), {lisp LF} (10), {lisp SPACE} or {lisp SP} (32), {lisp ESCAPE} or {lisp ESC} (27), {lisp BELL} (7), {lisp BS} (8), {lisp TAB} (9), {lisp NULL} (0), and {lisp DEL} (127). The litatom {lisp EOL} maps into the appropriate End-Of-Line character code in the different Interlisp implementations (31 in Interlisp-10, 13 in Interlisp-D, 10 in Interlisp-VAX). {note doc TENEXEOL ? } Finally, {fn CHARCODE} maps {lisp NIL} into {lisp NIL}. This is included because some character-code producing functions sometimes return {lisp NIL} (e.g. {fn NTHCHARCODE}); a test for that value can be included in a {fn CHARCODE} list along with true character-code values. Charcode of litatomic arguments can be used wherever a structure of character codes would be appropriate. For example: {lispcode (FMEMB (NTHCHARCODE X 1) (CHARCODE (CR LF SPACE))) (EQ (BIN FOO) (CHARCODE ↑C))} There is a macro for {fn CHARCODE} which causes the character-code structure to be constructed at compile-time. Thus, the compiled code for these examples is exactly as efficient as the less readable: {lispcode (FMEMB (NTHCHARCODE X 1) (QUOTE (13 10 32))) (EQ (BIN FOO) 3)} }} {FnDef {Name SELCHARQ} {Args E CLAUSE{sub 1} {ellipsis} CLAUSE{sub N} DEFAULT} {Type NOSPREAD NLAMBDA} {Text Similar to {fn SELECTQ} ({PageRef Fn SELECTQ}), except that the selection keys are determined by applying {fn CHARCODE} (instead of {fn QUOTE}) to the key-expressions. If the value of {arg E} is a character code or {lisp NIL} and it is {fn EQ} or {fn MEMB} to the result of applying {fn CHARCODE} to the first element of a clause, the remaining forms of that clause are evaluated. Otherwise, the default is evaluated. Thus {lispcode (SELCHARQ (BIN FOO) ((SPACE TAB) (FUM)) ((↑D NIL) (BAR)) (a (BAZ)) (ZIP))} is exactly equivalent to {lispcode (SELECTQ (BIN FOO) ((32 9) (FUM)) ((4 NIL) (BAR)) (97 (BAZ)) (ZIP))} Furthermore, {fn SELCHARQ} has a macro such that it always compiles as an equivalent {fn SELECTQ}. }} }{End SubSec Character Code Functions} {Begin Note} Date: 29 SEP 1982 2001-PDT From: JONL.PA It seems that there is no advertised function which will enable a user to construct a LITATOM witht the two characters 1 and . as print name. FOO. Note that LITATOMs have properties which numbers don't, and it would be nice to be able to force a particular STRING to convert into a LITATOM, instead of always having to deal with the ambiguity. Also, % doesn't really work to escape the reader syntax, since %1%. will read in the same as "1.", which is the same as "1.0". FOO. Note that %(%) doesn't read in as the empty list; However, there is some kind of bug (probably in the line buffereing?) such that a %(%) typed at the end of a buffering will read in as %(%] Date: 1-Oct-82 12:44:02 PDT (Friday) From: Masinter.PA This has so long been considered a "feature" of Interlisp that the discussion is lost in antiquity. I am reluctant to consider the consequences of fixing it, or at what level the fix should be applied. While % is done at "read" time, what you are most interested in is the interaction with PACK/PACKC, where % has no effect (PACKC doesn't take a readtable or whatever since it always makes litatoms.) {End Note} }{End SubSec Litatoms}