{Begin SubSec Macros} {Title Macros} {Text {Tag Macros} {index *BEGIN* macros} {note Macros are a way of EXTENDING the language. Steal text from Commonlisp --- lmm I don't understand what you mean, Larry ---mjs} Macros provide an alternative way of specifying the action of a function. Whereas function definitions are evaluated with a "function call", which involves binding variables and other housekeeping tasks, macros are evaluated by {it translating} one Interlisp form into another, which is then evaluated. A litatom may have both a function definition and a macro definition. When a form is evaluated by the interpreter, if the {fn CAR} has a function definition, it is used (with a function call), otherwise if it has a macro definition, then that is used. However, when a form is compiled, the {fn CAR} is checked for a macro definition first, and only if there isn't one is the function definition compiled. This allows functions that behave differently when compiled and interpreted. For example, it is possible to define a function that, when interpreted, has a function definition that is slow and has a lot of error checks, for use when debugging a system. This function could also have a macro definition that defines a fast version of the function, which is used when the debugged system is compiled. {note it is not advised for users to have litatoms with both a function definition and a macro definition, since one can get into trouble with functions that act differently interpreted and compiled --- (from talk with lmm) mjs} Macro definitions are represented by lists that are stored on the property list of a litatom. Macros are often used for functions that should be compiled differently in different Interlisp implementations, and the exact property name a macro definition is stored under determines whether it should be used in a particular implementation. The global variable {var MACROPROPS}{index MACROPROPS Var} contains a list of all possible macro property names which should be saved by the {filecom MACROS} file package command. Typical macro property names are {prop 10MACRO}{index 10MACRO Prop} for Interlisp-10, {prop DMACRO}{index DMACRO Prop} for Interlisp-D,{foot also {prop VAXMACRO}{index VAXMACRO Prop} for Interlisp-VAX, and {prop JMACRO}{index JMACRO Prop} for Interlisp-Jerico. }{comment endfootnote} and {prop MACRO} for "implementation independent" macros. The global variable {var COMPILERMACROPROPS}{index COMPILERMACROPROPS Var} is a list of macro property names. Interlisp determines whether a litatom has a macro definition by checking these property names, in order, and using the first non-{lisp NIL} property value as the macro definition. In Interlisp-D this list contains {prop DMACRO} and {prop MACRO} in that order so that {prop DMACRO}s will override the implementation-independent {prop MACRO} properties. In general, use a {prop DMACRO} property for macros that are to be used only in Interlisp-D, use {prop 10MACRO} for macros that are to be used only in Interlisp-10, and use {prop MACRO} for macros that are to affect both systems. {note BYTEMACRO means (OR DMACRO VAXMACRO), because they wanted to share some macros} {note ALTOMACRO is obsolete synonym for DMACRO} {note example of using DECLARE to define a macro removed at Larry's request --- mjs} Macro definitions can take the following forms: {Begin LabeledList} {Max 1} {Indent 8percent} {Label {lisp (LAMBDA {ellipsis})} or {lisp (NLAMBDA {ellipsis})}} {Text A function can be made to compile open by giving it a macro definition of the form {lisp (LAMBDA {ellipsis})} or {lisp (NLAMBDA {ellipsis})}, e.g., {lisp (LAMBDA (X) (COND ((GREATERP X 0) X) (T (MINUS X))))} for {fn ABS}. The effect is as if the macro definition were written in place of the function wherever it appears in a function being compiled, i.e., it compiles as a lambda or nlambda expression. This saves the time necessary to call the function at the price of more compiled code generated in-line. } {Label {lisp (NIL {arg EXPRESSION})} or {lisp ({arg LIST} {arg EXPRESSION})}} {Text "Substitution" macro. Each argument in the form being evaluated or compiled is substituted for the corresponding atom in {arg LIST}, and the result of the substitution is used instead of the form. For example, if the macro definition of {fn ADD1} is {lisp ((X) (IPLUS X 1))}, then, {lisp (ADD1 (CAR Y))} is compiled as {lisp (IPLUS (CAR Y) 1)}. Note that {fn ABS} could be defined by the substitution macro {lisp ((X) (COND ((GREATERP X 0) X) (T (MINUS X))))}. In this case, however, {lisp (ABS (FOO X))} would compile as {lispcode (COND ((GREATERP (FOO X) 0) (FOO X)) (T (MINUS (FOO X))))} and {lisp (FOO X)} would be evaluated two times. (Code to evaluate {lisp (FOO X)} would be generated three times.) {note our optimizing compiler should be smart enough to get this one as ((LAMBDA (Y)(COND ...Y)) (FOO X)) -- Gadol} } {label {lisp (OPENLAMBDA {arg ARGS} {arg BODY})}} {Text This is a cross between substitution and {lisp LAMBDA} macros. When the compiler processes an {lisp OPENLAMBDA}, it attempts to substitute the actual arguments for the formals wherever this preserves the frequency and order of evaluation that would have resulted from a {lisp LAMBDA} expression, and produces a {lisp LAMBDA} binding only for those that require it. {note any plans for OPEN-NLAMBDA, or is that pretty much like a subst macro??} {Begin Note} is there a good, correct example we can put here?? For example, an {fn ILESSP} {lisp DMACRO} might be {lisp (OPENLAMBDA (X Y) (IGREATERP Y X))}. It cannot be a substitution macro because in {lisp (ILESSP (FOO) (FUM))}, the {lisp (FOO)} must be executed before the {lisp (FUM)}. However, for {lisp (ILESSP (FOO) 10)}, the order of execution does not matter as {lisp FOO} can be executed after the {lisp 10}. An {lisp OPENLAMBDA} would force a binding in the first case, but not the second. this last example is untrue! openquote seems to only turn into a subst macro when all arguments are constants. bug. Date: 27 DEC 1982 2236-PST From: MASINTER.PA Subject: OPENLAMBDA The current behavior is correct. The documentation is wrong. It will substitute constants, and variables from the right. The user is asserting "can substitute lexically if constant or all args litatom", i.e., OPENLAMBDA args are not specvars, and are not SETQd. {End Note} } {label {lisp T}} {Text When a macro definition is the atom {lisp T}, it means that the compiler should ignore the macro, and compile the function definition; this is a simple way of turning off other macros. For example, the user may have a function that runs in both Interlisp-D and Interlisp-10, but has a macro definition that should only be used when compiling in Interlisp-10. If the {prop MACRO} property has the macro specification, a {prop DMACRO} of {lisp T} will cause it to be ignored by the Interlisp-D compiler. Note that this {prop DMACRO} would not be necessary if the macro were specified by a {prop 10MACRO} instead of a {prop MACRO}. } {label {lisp (= . {arg OTHER-FUNCTION})}} {Text A simple way to tell the compiler to compile one function exactly as it would compile another. For example, when compiling in Interlisp-D, {fn FRPLACA}s are treated as {fn RPLACA}s. This is achieved by having {fn FRPLACA} have a {prop DMACRO} of {lisp (= . RPLACA)}. } {Label {lisp ({arg LITATOM} {arg EXPRESSION})}} {Text If a macro definition begins with a litatom other than those given above, this allows {it computation} of the Interlisp expression to be evaluated or compiled in place of the form. {arg LITATOM} is bound to the {fn CDR} of the calling form, {arg EXPRESSION} is evaluated, and the result of this evaluation is evaluated or compiled in place of the form. For example, {fn LIST} could be compiled using the computed macro: {lispcode [X (LIST 'CONS (CAR X) (AND (CDR X) (CONS 'LIST (CDR X]} This would cause {lisp (LIST X Y Z)} to compile as {lisp (CONS X (CONS Y (CONS Z NIL)))}. Note the recursion in the macro expansion. If the result of the evaluation is the litatom {index IGNOREMACRO Litatom}{atom IGNOREMACRO}, the macro is ignored and the compilation of the expression proceeds as if there were no macro definition. If the litatom in question is normally treated specially by the compiler ({fn CAR}, {fn CDR}, {fn COND}, {fn AND}, etc.), and also has a macro, if the macro expansion returns {atom IGNOREMACRO}, the litatom will still be treated specially. In Interlisp-10, if the result of the evaluation is the atom {atom INSTRUCTIONS},{index INSTRUCTIONS Litatom} no code will be generated by the compiler. It is then assumed the evaluation was done for effect and the necessary code, if any, has been added. This is a way of giving direct instructions to the compiler if you understand it. } {End LabeledList} {note there are a number of other macro forms which are used internally, and shouldn't be documented, such as MACRO = (APPLY* ...) MACRO = <atom>} Note: It is often useful, when constructing complex macro expressions, to use the {lisp BQUOTE} facility (see {PageRef Tag BQUOTE}). The following function is quite useful for debugging macro definitions: {FnDef {FnName EXPANDMACRO} {FnArgs FORM QUIETFLG {anonarg}} {Text Takes a form whose {fn CAR} has a macro definition and expands the form as it would be compiled. The result is prettyprinted, unless {arg QUIETFLG}={lisp T}, in which case the result is simply returned. }} {Begin SubSec MACROTRAN} {Title MACROTRAN} {Text {Tag Macrotran} {index *PRIMARY* MACROTRAN FN} Interpreted macros are implemented by the function {fn MACROTRAN}. When the interpreter encounters a form {fn CAR} of which is an undefined function,{foot In other words, if you have a macro on {lisp FOO}, then typing {lisp (FOO 'A 'B)} will work, but {lisp FOO(A B)} will not work.{note this should be fixed sometime} }{comment endfootnote} {fn MACROTRAN} is called. If {fn CAR} of the form has a macro definition, the macro is expanded, and the result of this expansion is evaluated in place of the original form. {fn CLISPTRAN} ({PageRef Fn CLISPTRAN}) is used to save the result of this expansion so that the expansion only has to be done once. On subsequent occasions, the translation (expansion) is retrieved from {var CLISPARRAY} the same as for other CLISP constructs; {fn MACROTRAN} never even has to be invoked. Sometimes, macros contain calls to functions that assume that the macro is being compiled. The variable {var SHOULDCOMPILEMACROATOMS}{index SHOULDCOMPILEMACROATOMS Var} is a list of functions that should be compiled to work correctly (initially {lisp (OPCODES)} in Interlisp-D, {lisp (ASSEMBLE LOC)} in Interlisp-10). {var UNSAFEMACROATOMS}{index UNSAFEMACROATOMS Var} is a list of functions which effect the operation of the compiler, so such macro forms shouldn't even be expanded except by the compiler (initially {lisp NIL} in Interlisp-D, {lisp (C2EXP STORIN CEXP COMP)} in Interlisp-10). If {fn MACROTRAN} encounters a macro containing calls to functions on these two lists, instead of the macro being expanded, a dummy function is created with the form as its definition, and the dummy function is then compiled. A form consisting of a call to this dummy function with no arguments is then evaluated in place of the original form, and {fn CLISPTRAN} is used to save the translation as described above. There are some situations for which this procedure is not amenable, e.g. a {fn GO} inside the form which is being compiled will cause the compiler to give an {lisp UNDEFINED TAG}{index UNDEFINED TAG Error} error message because it is not compiling the entire function, just a part of it. Note: {lisp MACROTRAN} is an entry on {var DWIMUSERFORMS} ({PageRef Var DWIMUSERFORMS}) and thus will not work if DWIM is not enabled. {Begin Note} Interpreter macro expansion is controlled by (MUSTCOMPILEMACROP MACRO SAFEFLG) Returns NIL if the macro body "macro" is OK to compile. Actually works by scanning the body of macro for instances of atoms on UNSAFEMACROATOMS and SHOULDCOMPILEMACROATOMS. These variables are in turn set up by COMPILEMODE. Interpreted calls to atoms with only macro definitions will automatically compile the instance of the macro expansion if the macro contains an instance of SHOULDCOMPILEMACROATOMS. {End Note} }{End SubSec MACROTRAN} {index *END* macros} }{End SubSec Macros}