home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / MISC / REC.ZIP / RECC.DOC < prev    next >
Encoding:
Text File  |  1986-12-01  |  22.5 KB  |  545 lines

  1.  
  2.  
  3.         Chapter 4. The Workspace
  4.  
  5.     The workspace (WS) is a linear array of bytes limited by the 
  6. available memory, with which five pointers are associated: p0, p1, 
  7. p2, p3 and p4.  Pointers p0 and p1 are inclusive and pointers p2, p3 
  8. and p4 are exclusive, so that null strings may be validly delimited 
  9. by the workspace pointers.  The absolute limits of the WS are defined 
  10. by p0 (the lower limit) and p4 (the upper limit); the last address of 
  11. the WS is p4-1 and the workspace length is p4-p0.
  12.  
  13.     The workspace can be used to carry out insertions, searches 
  14. and modifications of character strings.  The text contained by the WS 
  15. begins at p0 and ends at p3-1, so that its total length is p3-p0; 
  16. clearly p3 may not exceed p4.  Pointers p1 and p2 are altered by the 
  17. text insertion, search and modification operations; p1 and p2 always 
  18. point to the lower and upper ends, respectively, of some substring of 
  19. the text delimited by p0 and p3.
  20.  
  21.     From the foregoing, it may be seen that the five workspace 
  22. pointers always satisfy the following relationship:
  23.  
  24.         p0  <  p1  <  p2  <  p3  <  p4
  25.             -       -      -     -
  26.  
  27. where p0 and p4 are the physical bounds of the WS, p0 and p3 are the 
  28. bounds of the text contained by the workspace and p1 and p2 are 
  29. auxiliary pointers acted upon by the operators and predicates to be 
  30. described in this chapter.
  31.  
  32.     The initial contents of the workspace depend on the command 
  33. line used to invoke REC's execution.  If the command line has the 
  34. form 
  35.  
  36.             rec8x Y
  37.  
  38. where x is 0 or 6 depending on the processor used and Y is absent or 
  39. is the name of a file containing a REC program to be compiled and 
  40. executed, the workspace will be initially empty, so that the 
  41. workspace pointers satisfy the relationship p0=p1=p2=p3<p4.  If the 
  42. command line has the form
  43.  
  44.             rec8x Y Z
  45.  
  46. where x is 0 or 6, Y is the name of a file and Z is a string 
  47. consisting of at least one nonblank character, the initial contents 
  48. of the workspace will be the string Z (i.e., the command line tail),
  49. with the following relationship among the pointers: p0=p1=p2<p3<p4. 
  50.  
  51.     Several of the operators and predicates which perform their 
  52. main function on the workspace require one or more arguments from the 
  53. PDL.  All of them lift from it the arguments they require; some leave 
  54. a result on the PDL.
  55.  
  56.     Next we describe the first group of predicates and operators 
  57. which act on the workspace.
  58.  
  59. Operator/Predicate        Function performed
  60.  
  61.     I        Operator. Inserts in the workspace the top 
  62.             PDL argument, if it will fit (i.e., if its 
  63.             length is less than p4-p3).  The string is 
  64.             inserted starting at pointer p2, and the text 
  65.             originally delimited by p2 and p3 is shifted 
  66.             towards p4 to make space for the insertion.  
  67.             Pointers p1 and p2 are set to delimit the 
  68.             inserted text.  If the text won't fit, the
  69.             error message "WS ovfl" is issued and program 
  70.             execution is terminated.
  71.  
  72.     Q        Operator.  Copies the text delimited by p1 
  73.             and p2 to the PDL.  Both the workspace and 
  74.             its pointers remain unaltered.
  75.  
  76.     F        Predicate, searches for text.  The text in 
  77.             the workspace is examined from left to right 
  78.             beginning at p1 to see if the object of 
  79.             comparison on the PDL can be found.  If so, 
  80.             its replica on the workspace is bracketed by
  81.             p1 and p2 and F becomes true; otherwise p1 
  82.             and p2 retain thir original values and F 
  83.             becomes false.  In both cases the top 
  84.             argument is lifted from the PDL.
  85.  
  86.     D        Operator which erases from the AT the text 
  87.             delimited by p1 and p2.  The text delimited 
  88.             by p2 and p3 is shifted towards p1 to close 
  89.             the gap generated by the deletion; when the 
  90.             operation ends p2=p1 holds.
  91.  
  92.     J        Operator.  Moves p1 to the beginning of the 
  93.             text, that is to say, assigns to p1 the value 
  94.             of p0.
  95.  
  96.     Z        Operator.  Moves p1 to the end of the text, 
  97.             i. e., assigns the value of p3 to p2.
  98.  
  99.     With these operators it becomes possible to write in REC and 
  100. execute Markov Algorithms, which involve a series of modifications to 
  101. an initially given text.  The modifications can be described by a 
  102. series of substitution rules of the form
  103.  
  104.             a1 => b1
  105.             a2 => b2 
  106.             ...
  107.  
  108. where a[i] and b[i] are (possibly null) character strings.
  109.  
  110.     Text is always searched from the left, and the substitution 
  111. list is always consulted from top to bottom.  If the chain a[i] is 
  112. found, it is replaced by b[i], and the substitution list is consulted 
  113. once again from the beginning.  If a[i] is not found, a[i+1] is 
  114. sought, until no further transformation is possible.  Markov 
  115. Algorithms also allow one or more substitutions to be declared 
  116. terminal, conventionally by placing a point after the substituion 
  117. arrow.  Many examples and a thorough dicussion may be found in 
  118. Markov's book, "Theory of Algorithms".
  119.  
  120.     A Markov Algorithm may be translated directly into REC by 
  121. writing the fragment
  122.  
  123.             J 'a' F D 'b' I :
  124.  
  125. for an ordinary substitution, and
  126.  
  127.             J 'a' F D 'b' I ;
  128.  
  129. for a terminal substitution.
  130.  
  131.     To give a specific example, consider the problem of 
  132. incrementing by one a number written out in binary form.  A 
  133. Markov algorithm to accomplish this is
  134.  
  135.             *1  =>  1*
  136.             *0  =>  0*
  137.             *   =>  +
  138.             1+  =>  +0
  139.             0+  =>. 1
  140.             +   =>. 1
  141.                 =>  *
  142.  
  143. and its operation is the following:  If neither a * nor a + is seen, 
  144. a * is inserted at the front of the word (last substitution).  It 
  145. will then travel to the end of the word (first and second 
  146. substitutions), changing places with 0's and 1's, wherupon it mutates 
  147. into a + (third substitution).  The + is the actual incrementer, 
  148. traveling backwards generating a carry if it sees a 1, writing a 1 in 
  149. place of a 0 or placing a 1 in front of the whole string.
  150.  
  151.     A REC program which reads in a binary number (as a string of 
  152. ASCII 0s and 1s) and carries out this algorithm is the following:
  153.  
  154.     [Markov Algorithm to increment a binary number]
  155.     {(2573 TL;)                [new line]
  156.      (R 13%= @L; "0"="0"T|: "1"="1"T|:
  157.         TL "< 0, 1 or 'Return'"TL @L:) T    [text]
  158.      (@L "> "TL ""@T I
  159.         (JZ QTL @L "*1" FD "1*" I:
  160.                "*0" FD "0*" I:
  161.                "*"  FD "+"  I:
  162.                "1+" FD "+0" I:
  163.                "0+" FD "1"  I;
  164.                "+"  FD "1"  I;
  165.                ""   FD "*"  I: )
  166.          JZQDT"1"=;L:)}
  167.  
  168.     It is not necessary to include J in each substitution because 
  169. p1 remains unchanged when F fails.  Z is required in order to copy 
  170. with Q the entire text to the PDL.  The program stops when a null 
  171. string or a single 0 is given in response to the prompt "> " sent to 
  172. the console at the beginning of each iteration in the main program's 
  173. outer parenthesis level.
  174.  
  175.     When this program is applied to, e.g., the string 1011, the 
  176. succesive steps in the transformation are
  177.  
  178.             1011        [initial word]
  179.             *1011
  180.             1*011
  181.             10*11
  182.             101*1
  183.             1011*
  184.             1011+
  185.             101+0
  186.             10+00
  187.             1100        [final word].
  188.  
  189.     Phrases can be created out of nothing only at the extreme 
  190. left of the text, and must be moved around very laboriously because 
  191. only one single consecutive string can be modified at a time.  For 
  192. example, to add two binary numbers, their digits first have to be 
  193. intermingled, and then an adder moved through them.  For this reason 
  194. Markov Algorithms are not very practical.  Their merit lies in their 
  195. simplicity, and the ease with which theoretical results about 
  196. computing can be established using them.
  197.  
  198.     We now introduce a second group of operators and predicates 
  199. referencing the text contained in the workspace.
  200.  
  201. Operator/Predicate        Function performed
  202.  
  203.     E        Predicate which becomes true if the WS 
  204.             contains a string which starts at p1 and is 
  205.             identical to the top PDL argument, in which 
  206.             case p2 is modified to delimit the matching 
  207.             text.  If E fails, no pointer is altered, but 
  208.             in either case the PDL argument is lifted.
  209.  
  210.     U        Predicate used to delimit an interval, given 
  211.             strings to delimit it.  Given an argument on 
  212.             the PDL, U searches in the WS for a string 
  213.             identical to the argument, beginning at p2.
  214.             If found, p1 will take the starting value of 
  215.             p2 and p2 will point to the beginning of the 
  216.             string found by U, and the predicate becomes 
  217.             true; otherwise U becomes false and the 
  218.             pointers are not moved.  The top argument is 
  219.             lifted in either case.  U differs from F in 
  220.             that its search starts at p2, and on 
  221.             succeeding the interval defined by p1 and p2 
  222.             is the one lying between the previous (p1,p2)
  223.             interval and the text found by U.  For 
  224.             instance, if the workspace contains the 
  225.             string AABBCCDDEEFFGG, the program (J'BB'F 
  226.             'F'U;) will result in pointer p1 and p2 
  227.             delimiting the substring CCDDEE.
  228.  
  229.     V        Predicate, similar to U in its operation.
  230.             Searches in AT, beginning at p2, for a string 
  231.             matching exactly the top PDL argument (which 
  232.             will be lifted regardless of the outcome of 
  233.             the search).  If found, value of p2 is 
  234.             altered so that it points to the end of the 
  235.             text found by V; p2 does not change otherwise. 
  236.             The difference between U and V is that U 
  237.             excludes the original interval and the text 
  238.             found from the interval defined by p1 and p2,
  239.             whereas V includes betwwen p1 and p2 the 
  240.             original interval and the text found.  Thus,
  241.             if the WS contains the string AABBCCDDEEFFGG,
  242.             the program (J'BB'F 'F'V;) causes p1 and p2 
  243.             to delimit the substring BBCCDDEEF.
  244.  
  245.     M        Predicate requiring two arguments from the 
  246.             PDL, which define a lexicographic interval 
  247.             whose lower bound is the lower argument and 
  248.             whose upper bound is the top argument.  M 
  249.             becomes true if the workspace contains a 
  250.             string which starts at p1 and is greater than 
  251.             or equal to the lower bound and smaller than 
  252.             or equal to the upper bound; if such a string 
  253.             exists p1 and p2 will delimit the smallest 
  254.             string lying in the interval; if M becomes 
  255.             false no pointers are moved, but in either 
  256.             case both PDL arguments are lifted.  
  257.             Lexicographic order is defined as follows: 
  258.             An n-byte string is equal to an m-byte string 
  259.             if and only if m=n and both strings consist of 
  260.             the same sequence of bytes; an n-byte string 
  261.             is greater than an m-byte string if there 
  262.             exists an integer k between 1 and the smaller 
  263.             of m and n, such that the strings coincide in 
  264.             the first k-1 bytes and the k-th byte of the 
  265.             first string is greater than the corresponding 
  266.             byte of the second string; an n-byte string is 
  267.             greater than an m-byte string if n>m and the 
  268.             first m characters of the first string are
  269.             identical to the corresponding bytes of the 
  270.             second string.  The ordering relation between 
  271.             bytes containing alphanumeric data is the one 
  272.             defined by the ASCII code, so that 0<1<...
  273.             <9<...<A<B<...<Z<...<a<b<...<z.  Notice that 
  274.             "" "xxx" M is always true regardless of the 
  275.             string xxx and causes p2 to take the value of 
  276.             p1, whereas "xxx" "" M, which would normally 
  277.             be false when xxx is nonnull, is interpreted 
  278.             in such a way that it is true only if the 
  279.             workspace contains a string that starts at 
  280.             p1 and is greater than or equal to xxx, 
  281.             effectively omitting the null string given as 
  282.             top argument from the comparison.
  283.  
  284.     j        Operator, delimits a null interval at p1, 
  285.             i.e., assigns the value of p1 to p2.
  286.  
  287.     z        Operator, delimits a null string at p2, i.e., 
  288.             it assigns the value of p2 to p1.
  289.  
  290.     A        Predicate, makes p1 advance one byte if it 
  291.             can.  If p1 is at the right end of the text 
  292.             (p1=p3) before the operation, A becomes false;
  293.             otherwise p1 advances and drags p2 with it if 
  294.             necessary (so that p2<p1 never holds).
  295.  
  296.     B        Predicate, makes p2 back up one byte if 
  297.             possible.  If p1 is at the left end of the 
  298.             text (p1=p0), B becomes false; otherwise p1 
  299.             backs up.
  300.  
  301.     q        Operator which leaves two numbers on the PDL:
  302.             the value of p1 (as lower argument) and the 
  303.             difference p2-p1 (as top argument), i.e., it 
  304.             leaves the origin and length of the interval 
  305.             defined by p1 and p2.  In the 8086 version, 
  306.             the lower argument (p1) consists of 4 bytes,
  307.             the lower two containing p1 and the upper two 
  308.             containing the base address of the memory 
  309.             segment in which the workspace resides.
  310.  
  311.     Y        Predicate requiring an argument on the PDL 
  312.             previously produced by either of the operator 
  313.             combinations qL or q|.  There are several 
  314.             possibilities, depending on the argument 
  315.             provided and the processor used.  On the 8080,
  316.             if a 2-byte argument is given (generated by 
  317.             qL), Y is always true and assigns a value to
  318.             p1, depending on the PDL value, say x, as 
  319.             follows: if x<p0, it moves p1 to p0; if x>p3,
  320.             it moves both p1 and p2 to p3; if x>p2 but 
  321.             within p0 and p3, it assigns x to both p1 and 
  322.             p2, and in the remaining case, x is assigned 
  323.             to p1 and p2 remains unchanged.  On the 8086,
  324.             given a 4 byte argument (produced by qL), Y is
  325.             false (and does not alter any of the pointers)
  326.             if the segment base contained in the PDL 
  327.             argument is different from the base of the 
  328.             segment containing the WS currently in use 
  329.             (which may be changed as will be seen later 
  330.             on), otherwise, Y assigns values to p1 (and 
  331.             possibly p2) as above.  To analyze the case 
  332.             where the PDL argument is the product of q|,
  333.             call x and y the values of p1 and p2 used by
  334.             the operator q, respectively.  Then Y will 
  335.             be false (and not alter any pointers) if x<p0 
  336.             or y>p3 (or if the segment bases are not 
  337.             equal, on the 8086), and true otherwise, in 
  338.             which case x is assigned to p1 and y to p2.
  339.  
  340.     We illustrate the use of these operators and predicates with 
  341. a program which carries out the syntactical analysis of an arithmetic 
  342. expression written out in the usual algebraic notation, in which all 
  343. operands are decimal integer constants.  The operators recognized are 
  344. +, -, * and /, no blanks are allowed nor are + or - permitted as 
  345. unary operators, but parenthesis may be used.  (We will later extend 
  346. this program to include constants containing a decimal point and 
  347. a power of ten factor, and unary + and - operators.)
  348.  
  349.     [Arithmetic expression parser]
  350.     {("0""9"Mz;) d                [digit]
  351.      (@d(@d:;);) q                [integer]
  352.      ('+'Ez; '-'Ez; '*'Ez; '/'Ez;) o    [operator]
  353.      (@q; '('Ez @e ')'Ez;) p        [operand]
  354.      (@p (qL @o@p L: Y;) ;) e        [expression]
  355.  
  356.      (R 13%=""; T@J|;) J            [read a line]
  357.  
  358.      (2573TL '> 'TL @J ""=;
  359.         JZD I @e (A) ": yes"TL: ": no"TL:)}
  360.  
  361.     The main program signals with a ">" that it is ready to 
  362. receive a string, reads a line and inserts it in the workspace after 
  363. deleting the entire previous contents.  If subroutine e recognizes an 
  364. arithmetic expression spanning the entire text, p1 will be at the 
  365. right end of the text so that (A) is true if the WS does contain an 
  366. expression (p1 cannot advance) and false otherwise.  Looking at the 
  367. definition of an expression given in subroutine e, it is seen that an 
  368. expression is a single operand or an alternating succession of 
  369. operands and operators starting and ending with an operand (the REC 
  370. operators qL and Y are used to guarantee that p1 ends up pointing to 
  371. the first character not stasifying this definition of an expression);
  372. in turn, an operand may be an integer or an expression enclosed in 
  373. parentheses, and an integer consists of one or more decimal digits 
  374. (subroutines q and d).  Subroutine q is true if there is a digit at 
  375. p1 (recognized by subroutine d) and makes p1 advance through all 
  376. digits following the first one, if any; subroutine d is true only if 
  377. there is a digit at the byte pointed to by p1, in which case the 
  378. operator z cause p1 to advance to the next character.  In a similar 
  379. fashion, subroutine o becomes true if p1 points to a byte containing 
  380. any one of the four symbols +, -, * or /.
  381.  
  382.     The last operators and predicates affecting the workspace and 
  383. its pointers are the following:
  384.  
  385.  
  386. Operator/Predicate        Function performed
  387.  
  388.     a        Predicate.  Given a two-byte integer n on the 
  389.             PDL, attempts to delimit an n-byte interval 
  390.             between p1 and p2 keeping p1 fixed; if it can,
  391.             it becomes true, lifts its argument and moves 
  392.             p2; if there are fewer than n bytes between 
  393.             p1 and p3, it becomes false, leaves p2 
  394.             unchanged and replaces its PDL argument with 
  395.             the deficit, i.e., the difference n-(p3-p1). 
  396.  
  397.     b        Predicate.  Given a two-byte integer n on the 
  398.             PDL, attempts to bracket between p1 and p2 an 
  399.             n-byte interval holding p2 fixed, if it can,
  400.             it becomes true and moves p1; if there are 
  401.             fewer than n bytes between p0 and p2, p1 
  402.             remains unchanged and the predicate becomes 
  403.             false.  The argument n is lifted in either 
  404.             case.
  405.  
  406.     e        Predicate.  Given a two-byte integer n on the 
  407.             PDL, attempts to extend the text by making 
  408.             p3 advance n bytes.  If it can, e becomes 
  409.             true, p3 advances, p1 and p2 move to delimit 
  410.             the new n-byte extent and n is lifted from 
  411.             the PDL; otherwise no pointers are moved, e 
  412.             becomes false, and n is replaced on the PDL 
  413.             by the deficit, i.e., n-(p4-p3).
  414.  
  415.     f        Predicate; it requires an argument on the 
  416.             PDL whose length we will call l for the sake 
  417.             of discussion.  f replaces the text starting 
  418.             at p1 with the PDL argument if p1+l does not 
  419.             exceed p2, in which case it becomes true, 
  420.             leaves its argument on the PDL and advances 
  421.             p1 to the end of the replaced text.  If the 
  422.             replacement would go beyond p2, f becomes 
  423.             false, no replacement takes place, p1 does 
  424.             not move and the argument is lifted from the 
  425.             PDL.  For example, "a"(f:;) would fill all 
  426.             bytes delimited by p1 and p2 with the letter 
  427.             a.
  428.  
  429.     w        Operator whose operation may take one of 
  430.             three forms, depending on the size of the 
  431.             argument it finds on the PDL.  If this 
  432.             argument is the null string, it replaces it 
  433.             with a 10-byte argument consisting of the 
  434.             concatenated values of the WS pointers, p0 
  435.             through p4.  If its argument is 10 bytes 
  436.             long, assigns from it values to p0, p1, p2, 
  437.             p3 and p4 (but it does not check that the 
  438.             above-mentioned inequalities hold; the 
  439.             10-byte argument should have been generated 
  440.             by a previous usage of w).  Finally, if the 
  441.             PDL contains two 2-byte arguments, w assumes 
  442.             the top one to be a length (say l) and the 
  443.             lower one to be an address (say x); it will 
  444.             then replace both arguments with a ten-byte 
  445.             header containing the current values of p0 
  446.             through p4 and defines a new workspace from 
  447.             x and l as follows: p0=p1=x, p2=p3=p4=x+l. 
  448.             In the 8086 version, the workspace headers 
  449.             are 12 bytes long (the two extra bytes being 
  450.             used to store the base address of the segment 
  451.             in which the WS resides) and the address 
  452.             argument of the third option may be 4 bytes 
  453.             long, the lower two containing an address 
  454.             and the upper two containing the base address 
  455.             of the segment that is to hold the new 
  456.             workspace.
  457.  
  458.     <        Operator which closes down the workspace.  
  459.             The workspace becomes restricted to the 
  460.             interval between pointers p1 and p2.  The 
  461.             reason for this could be to restrict the 
  462.             editing operations to a smaller range, or 
  463.             it could be to have absolute freedom to 
  464.             work over some material before incorporating 
  465.             it into the rest of the text.  As a practical 
  466.             matter, the text between pointers p2 and p3 
  467.             is displaced to the upper end of the 
  468.             workspace and the original values of p0 and 
  469.             p4 are recorded with it before setting up 
  470.             new values of the pointers, which are set as 
  471.             follows: p1 and p2 remain unchanged, p0 is 
  472.             set to the value of p1, p3 moves to p2 and 
  473.             p4 moves to the left of the string moved 
  474.             to the right end of the original workspace.
  475.  
  476.     >        Operator which reopens the workspace (which 
  477.             must have been closed previously by <).  The 
  478.             text lying between p0 and p3 takes the place 
  479.             of the text which lay between p1 and p2 
  480.             before < was used; p0 and p4 are restored to 
  481.             their previous scope, p1 and p2 remain 
  482.             unaltered and p3 moves to the right end of 
  483.             the text brought back from the far end of the
  484.             workspace.  If > is executed without a prior 
  485.             execution of <, no action takes place and a 
  486.             note is made of the error.
  487.  
  488.     The program shown next parses arithmetic expressions which 
  489. may contain floating point constants with power-of-ten factors of the 
  490. form En or Dn, where n is an optionally signed integer and E and D 
  491. denote single and double precision, respectively.  The operators 
  492. recognized by the program include ** and ^, which denote 
  493. exponentiation; + and - are also accepted as unary operators.
  494.  
  495.     [Arithmetic expression parser]
  496.     {("0""9"Mz;) d                [digit]
  497.      (@d:;) D                [0 or more digits]
  498.      (qL('E'Ez;'D'Ez;)(@a;;) @d@D L; Yj;) E [power of 10 factor]
  499.      (Z< (@d@D'.'Ez; J'.'Ez@d; J@d; J>)
  500.             >@D@E;) q        [number]
  501.      ('+'Ez; '-'Ez;) a            [additive operator]
  502.      ('*'Ez; '/'Ez;) m            [mult. operator]
  503.      ('**'Ez; '^'Ez;) i            [power operator]
  504.      (@i; @m; @a;) o            [operator]
  505.      (@q; '('Ez @e ')'Ez;) p        [operand]
  506.      (Z<(@p; J@a@p; J>)>
  507.         (qL @o@p L: Y;) ;) e        [expression]
  508.  
  509.      (R 13%= ""; T@J|;) J            [read a line]
  510.      (2573TL '> 'TL @J ""=;
  511.         JZD I @e (A) ": yes" TL: ": no" TL:)}
  512.  
  513.     Notice the use of < and > in subroutines q and e.  The
  514. purpose of < in both cases is to mark the beginning of the substring 
  515. being parsed when several options are possible.  In subroutine e, 
  516. the fragment Z<(@p;J@a@p;J>)> could have been coded as qL(@p;YqL@a@p;
  517. Y)L.  The advantage of using < and > is that the PDL is not altered 
  518. in any way since the action of both operators is limited exclusively 
  519. to the workspace.
  520.  
  521.     The next example shows the use of w and f to initialize to 0 
  522. all bytes in a memory block created on the PDL by the operator c:
  523.  
  524.     [creates a block and fills it with zeros]
  525.     (c m p w 0% (f:;) w n;) 0
  526.  
  527. This subroutine requires an argument on the PDL for c (the length of 
  528. the block to be created); when done, the top PDL argument is the 
  529. block's starting address.  Observe that the operator combination pw 
  530. turns the top argument into a workspace; this is one way to handle 
  531. multiple workspaces.
  532.  
  533.     The following subroutine leaves an 8 byte argument starting 
  534. from the argument provided to it; if this argument is over 8 bytes 
  535. long, it truncates it to the first 8; otherwise the predicates a, e 
  536. and f are used to pad it with blanks on the right:
  537.  
  538.     [truncate or pad with blanks to 8 bytes]
  539.     (I< (8azZD; e ' '(f:;) ;_) JQD>;) 8
  540.  
  541.     The operator _ performs an unconditional return to CP/M, the 
  542. operating system; this is included as a pretection in case e turns 
  543. out to be false (which would mean there is no space in the workspace 
  544. to carry out the operation).
  545.