XEROX BTMP 2 4 1 BTMP 1 4 By: Mike Bird Support by: Greg Wexler (Wexler.pasa@Xerox.com) INTRODUCTION BTMP is a Basic Text Macro Processor. That is, it reads a stream of characters from some source and writes another stream to some destination, where the latter stream is derived from the former in a manner determined by commands embedded in the stream. Example: If the input contained the fragment ABC[+|100|20|3]DEF, this could result in the fragment ABC123DEF appearing in the output. BTMP incorporates a number of advanced features which facilitate tailoring to handle specialised source formats. These are described in later chapters. Until then, we will assume that the default configuration is in use. Besides a large number of built-in macros, such as the "+" macro illustrated above, BTMP includes facilities for defining new macros. Some operations which might require explicit function calls in conventional programming languages are handled automatically by a text macro processor. For example, the transformation of ABC[+|100|20|3]DEF to ABC123DEF conceptually passed through a stage where the three fragments ABC, 123 and DEF were concatenated. This concatenation is automatic. Syntax The default syntax of BTMP was chosen so as to use the minimum of reserved characters. This is advantageous when manipulating arbitrary text. For those applications where more characters are free - such as certain formal languages, more characters can be reserved to the macro processor. Later, we will meet examples of this. We will use the notation "X" ===> "Y" to signify that BTMP will convert the input fragment X to the output fragment Y. This is purely an expository convenience and bears no relation to any aspect of BTMP syntax. Example: "ABC[+|100|20|3]DEF" ===> "ABC123DEF" Macro Invocation As you will no doubt have already guessed, the syntax of a macro invocation is a list of elements; delimited by square brackets and seperated by vertical bars; the first element being the macro name and the remainder being arguments. Example: [+|100|20|3] invokes the (built-in) macro named "+" with three arguments: 100, 20 and 3. Example: [*|2|[+|3|4]] invokes the "+" macro with arguments 3 and 4. This yields 7. Then the "*" (multiplication) macro is invoked with arguments 2 and 7, yielding the two character result: 14. Quotation There are situations where one would wish for a square bracket or vertical bar to appear in the output. Any sequence of characters can be enclosed in apostrophes - which protect them from interpretation. Example: "'ABC[+|100|20|3]DEF'" ===> "ABC[+|100|20|3]DEF". Example: "ABC'[+|100|20|3]'DEF" ===> "ABC[+|100|20|3]DEF". Example: "'A'B''C'[+|100|20|3]'DEF" ===> "ABC[+|100|20|3]DEF". Later, we will learn how BTMP syntax can be extended by the addition of different types of quotation characters - for example, one could define the characters {<} and {>} to quote anything which came between them. The Escape Character The character "~" performs three useful functions. Together with the "#" character it yields the number of arguments supplied to an invocation of a user-defined macro. Together with a digit character it yields the value of the corresponding argument to an invocation of a user-defined macro. Arguments are numbered from 1 upward. Argument 0 is the name of the macro being invoked. Arguments after the ninth are accessable using the built-in macro args, defined below. Finally, when followed by any other character, it yields that other character alone. Example: "~'" ===> "'". DEFINITIONS AND DIVERSIONS Definitions The built-in macro define allows one to create new macro definitions. It is normally called with two arguments - a name and a body. It yields nothing. Example: "[define|double|'[*|~1|2]']" ===> "". Notice that the body was quoted. Had it not been quoted then {double} would have been defined to be twice the value of the first argument to the current function (and therefore constant). Example: "[define|pair|'(~1 . ~1)']" ===> "". Henceforth, to save space, we will omit the result of invocation of the define macro. There is sometimes utility in defining a macro with a constant value: Example: "[define|CLICKSPERMEGAFLOP|184769]". Example: "[define|MY.PARAGRAPH.FORMAT|(LEFTMARGIN 12 RIGHTMARGIN 50)]". One can redefine a macro any number of times. This allows macros to be used as variables. Example: "[define|counter|[+|[counter]|1]]". Notice that it would have been incorrect to quote the body in this case. Example: "[define|increment|'[define|~1|[+|[~1]|1]]']". Diversions Characters which would normally be sent to the output file can be "diverted" to some holding area, whence they can later be collected or even re-processed, using the built-in macro divert. divert yields nothing. If divert's argument is absent or null (zero characters), output is redirected to the principal output stream. Example: "[divert|fred]" ===> "" diverts subsequent 'output' to the diversion named "fred". In BTMP, definitions and diversions are merely different ways to change the same underlying data structures. That is, divert causes output to be appended to the macro definition named by its argument. Example: "[divert|increment]'[define|~1|[+|[~1]|1]]'[divert]" ===> "" is equivalent to the definition of increment given above. It will be obvious that the contents of a diversion can be retrieved by invoking the macro definition, as in [fred]. However, retrieval in this fashion is subject to re-interpretation. Sometimes re-interpretation may be desirable. For those situations where it is not, the built-in macro collect can retrieve the contents of a diversion without interpretation. Example: "[collect|increment]" ===> "[define|~1|[+|[~1]|1]]". collect accepts any number of arguments and processes them sequentially. The Diversion Stack BTMP actually maintains a stack of diversions. This is particularly useful in situations where a temporary diversion is required and where the current diversion is not known. The built-in macro push is similar to divert, but adds another level to the "diversion stack". The built-in macro pop removes a level from the diversion stack. Example: To write FOO to the primary output: "[push]FOO[pop]" ===> "FOO". Example: To write FOO to the diversion named BAR: "[push|BAR]FOO[pop]" ===> "". The built-in macro discard diverts output to a dummy stream - thus discarding it. Example: "[push[discard]This is a comment?[pop]" ===> "". Remote Storage Some applications require very large diversions/definitions. For example, it may be necessary to copy the body of a document into a diversion while constructing a table-of-contents. It may be necessary to save more characters than will fit into the available memory. Therefore, BTMP permits any diversion/definition to reside on any random access device - such as a local disk. This is controlled by the optional third argument to define. If omitted or null the definition will be held somewhere within the virtual memory. Otherwise the argument is a file name which will be used to create a new file in which the diversion/definition will be stored. It is not possible to specify a file name to the divert or push macros. Example: To divert output to a new diversion on file {DSK}FOO: "[define|FOO||{DSK}FOO][divert|FOO]" ===> "". ARITHMETIC Numeric Values BTMP understands precisely the same number formats as Interlisp-D. (This should not be very surprising). Any object used in an arithmetic context which is not recognizable as a number (e.g. the empty string) is treated as an integer zero. Mixed-mode arithmetic is used throughout. Boolean Values In a boolean context, the empty string (or an omitted argument) represents "false", anything else represents "true". BTMP predicates yield the empty string or the string consisting of the digit "1" as representations of "false" and "true" respectively. Basic Arithmetic Operators The built-in macros "+" and "*" each take zero or more arguments and add or multiply them respectively. Example: "[+|1|2|3|4|5]" ===> "15". Example: "[+]" ===> "0". Example: "[*|1|2|3|4|5]" ===> "120". Example: "[*]" ===> "1". The built-in macros "-" and "/" each take precisely one or two arguments. Given two arguments they perform subtraction and division respectively. With one argument they perform the negation and reciprocal operations respectively. Example: "[-|2|3]" ===> "-1". Example: "[-|12]" ===> "-12". Example: "[/|5|2]" ===> "2". Example: "[/|5.0|2]" ===> "2.5". Example: "[/|2]" ===> "0.5". The built-in macro remainder performs an Interlisp "IREMAINDER" on precisely two arguments. Example: "[remainder|7|5]" ===> "2". Arithmetic Comparisons The six built-in macros "==", ">=", ">", "<=", "<" and "<>" test for "equal", "greater or equal", "strictly greater", "less or equal", "strictly less" and "not equal" respectively. These macros each accept any number of arguments. In particular, "<>" does check that all of its arguments have distinct arithmetic values. Example: "[>|7|4]" ===> "1". Example: "[==|0|]" ===> "1". Example: "[==|1|]" ===> "". Example: "[==|1]" ===> "1". Example: "[<>|1|2|3|2]" ===> "". Bit-wise Operators The built-in macros logand, logor, logxor and lognot perform the bit-wise operations designated by their names. lognot expects one argument but the others can handle zero or more. Example: "[lognot|-1]" ===> "0". Example: "[lognot|0]" ===> "-1". Example: "[logand|5|6|7]" ===> "4". Example: "[logand]" ===> "-1". Example: "[logor|5|6]" ===> "7". Example: "[logor]" ===> "0". Example: "[logxor|5|6|7]" ===> "4". Example: "[logxor]" ===> "0". Shift Operators The built-in macros "<<" and ">>" perform logical shifts of their first argument, left or right respectively, with the bit-count specified by their second argument. Example: "[<<|3|2]" ===> "12". Example: "[>>|22|2]" ===> "5". Example: "[<<|123]" ===> "123". Example: "[>>]" ===> "0". OTHER BUILT-IN MACROS Lack of time prevents full documentation of the remaining built-in macros at this time. Hopefully, some experimentation or examination of the sources will yield some insight. All built-in functions are accessed via the value of the global variable \BTMP.BUILTIN.FNS. Pointers to compiled user code can be added here. apply* args concatenate equal escapecharfn eval if include map* substring syntax DEBUGGING The debugging package BTMP-DEBUG (& .DCOM) defines a number of simple debugging functions. These translate the internal representations of macro definitions into user-readable form. @A Arguments to active built-in fn @B Back stack trace @D Diversion/Definition of (X) @F Forward stack trace @H Help information @P Print (X) @T Text of (N)th back stack frame Of these, only @D, @P and @T take an argument. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC) STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD CENTERED) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY MODERN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC)) (174 36 288 36) NIL) (HEADING NIL (HEADINGTYPE RUNNINGHEAD) (84 744 528 36) NIL) (TEXT NIL NIL (84 96 456 600) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC)) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD CENTERED) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY MODERN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC)) (174 36 288 36) NIL) (HEADING NIL (HEADINGTYPE RUNNINGHEAD) (84 744 528 36) NIL) (TEXT NIL NIL (84 96 456 600) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC)) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD CENTERED) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY MODERN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC)) (174 36 288 36) NIL) (HEADING NIL (HEADINGTYPE RUNNINGHEAD) (84 744 528 36) NIL) (TEXT NIL NIL (84 96 456 600) NIL)))))(È(ŠŠ8(ŠŠ8DÈÈ PAGEHEADING RUNNINGHEAD(È(È  MODERNLOGOTERMINAL MODERN MODERN MODERNMODERN €MODERN MODERN  HRULE.GETFNMODERN  HRULE.GETFNMODERN  HRULE.GETFNMODERN   HRULE.GETFNMODERN   HRULE.GETFNMODERN  /   â $$ ß8  ?6I 6` 'ê  %   2% Í 2 2 6Ö#a™lU   € %¿ %HF % ?[ #K / µf 5wM <$ l°* 5B°G+    #8 1P  5Ø(  ,. ñ*Ä9J    Ê      @ {CB     =>         t     ú5     Ž# "*&fzº