home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / tex / texsrc2 / Src / fontutil / mft / web (.txt) < prev   
Texinfo Document  |  1990-09-23  |  75KB  |  1,710 lines

  1. % This program by D. E. Knuth is not copyrighted and can be used freely.
  2. % Version 0.0 was more-or-less debugged on June 4, 1985.
  3. % Version 0.1 improved formatting of : and added \\ (June 15, 1985).
  4. % Version 0.2 improved formatting of good, fixed @@ bug (August 4, 1985).
  5. % Version 0.3 fixed minor bug in change_file move (August 30, 1985).
  6. % Version 0.4 fixed minor bug regarding empty comments (April 8, 1989).
  7. % Version 1.0 was tuned up for the METAFONTware report (April 16, 1989).
  8. % Version 1.1 ditto, with input handled by Hosek's idea (April 27, 1989).
  9. % Version 2 has the new primitives of METAFONT 2.0 (October 16, 1989).
  10. % Here is TeX material that gets inserted after \input webmac
  11. \def\hang{\hangindent 3em\indent\ignorespaces}
  12. \font\ninerm=cmr9
  13. \let\mc=\ninerm % medium caps for names like SAIL
  14. \def\PASCAL{Pascal}
  15. \font\logo=manfnt % font used for the METAFONT logo
  16. \def\MF{{\logo META}\-{\logo FONT}}
  17. \def\pb{$\.|\ldots\.|$} % MF brackets (|...|)
  18. \def\v{\.{\char'174}} % vertical (|) in typewriter font
  19. \def\dleft{[\![} \def\dright{]\!]} % double brackets
  20. \mathchardef\RA="3221 % right arrow
  21. \mathchardef\BA="3224 % double arrow
  22. \def\({} % kludge for alphabetizing certain module names
  23. \chardef\V=`\| % vertical line in a string
  24. \def\title{MFT}
  25. \def\contentspagenumber{401}
  26. \def\topofcontents{\null
  27.   \def\titlepage{F} % include headline on the contents page
  28.   \def\rheader{\mainfont\hfil \contentspagenumber}
  29.   \vfill
  30.   \centerline{\titlefont The {\ttitlefont MFT} processor}
  31.   \vskip 15pt
  32.   \centerline{(Version 2.0, October 1989)}
  33.   \vfill}
  34. \def\botofcontents{\vfill
  35.   \centerline{\hsize 5in\baselineskip9pt
  36.     \vbox{\ninerm\noindent
  37.     The preparation of this report
  38.     was supported in part by the National Science
  39.     Foundation under grants IST-8201926, MCS-8300984, and
  40.     CCR-8610181,
  41.     and by the System Development Foundation. `\TeX' is a
  42.     trademark of the American Mathematical Society.
  43.     `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley
  44.     Publishing Company.}}}
  45. \pageno=\contentspagenumber \advance\pageno by 1
  46. @* Introduction.
  47. This program converts a \MF\ source file to a \TeX\ file. It was written
  48. by D.~E. Knuth in June, 1985; a somewhat similar {\mc SAIL} program had
  49. @^Knuth, Donald Ervin@>
  50. been developed in January, 1980.
  51. The general idea is to input a file called, say, \.{foo.mf} and to produce an
  52. output file called, say, \.{foo.tex}. The latter file, when processed by \TeX,
  53. will yield a ``prettyprinted'' representation of the input file.
  54. @^user manual@>
  55. Line breaks in the input are carried over into the output; moreover,
  56. blank spaces at the beginning of a line are converted to quads of indentation
  57. in the output. Thus, the user has full control over the indentation and line
  58. breaks. Each line of input is translated independently of the others.
  59. A slight change to \MF's comment convention allows further control.
  60. Namely, `\.{\%\%}' indicates that the remainder of an input line should be
  61. copied verbatim to the output; this interrupts the translation and forces
  62. \.{MFT} to produce a certain result.
  63. Furthermore, `\.{\%\%\%} $\langle\,$token$_1\,\rangle\ldots
  64. \langle\,$token$_n\,\rangle$'
  65. introduces a change in \.{MFT}'s formatting rules; all tokens after the first
  66. will henceforth be translated according to the current conventions for
  67. $\langle\,$token$_1\,\rangle$. The tokens must be symbolic (i.e., not
  68. numeric or string tokens). For example, the input line
  69. $$\.{\%\%\% addto fill draw filldraw}$$
  70. says that the `\.{fill}', `\.{draw}', and `\.{filldraw}' operations of
  71. plain \MF\ should be formatted as the primitive token `\.{addto}', i.e.,
  72. in boldface type. (Without such reformatting commands, \.{MFT} would treat
  73. `\.{fill}' like an ordinary tag or variable name. In fact, you need
  74. a reformatting command even to get parentheses to act like delimiters!)
  75. \MF\ comments, which follow a single \.\% sign, should be valid \TeX\
  76. input.  But \MF\ material can be included in \pb\ within a comment; this
  77. will be translated by \.{MFT} as if it were not in a comment. For example,
  78. a phrase like `\.{make} \.{\V x2r\V} \.{zero}' will be translated into
  79. `\.{make \$x\_\{2r\}\$ zero}'.
  80. The rules just stated apply to lines that contain one, two, or three \.\% signs
  81. in a row. Comments to \.{MFT} can follow `\.{\%\%\%\%}'.
  82. Five or more \.\% signs should not be used.
  83. Beside the normal input file, \.{MFT} also looks for a change file
  84. (e.g., `\.{foo.ch}'), which allows substitutions to be made in the
  85. translation. The change file follows the conventions of \.{WEB}, and
  86. it should be null if there are no changes. (Changes usually contain
  87. verbatim instructions to compensate for the fact that \.{MFT} cannot
  88. format everything in an optimum way.)
  89. There's also a third input file (e.g., `\.{plain.mft}'), which is
  90. input before the other two. This file normally contains the `\.{\%\%\%}'
  91. formatting commands that are necessary to tune \.{MFT} to a particular
  92. style of \MF\ code, so it is called the style file.
  93. The output of \.{MFT} should be accompanied by the macros in a small
  94. package called \.{mftmac.tex}.
  95. @.mftmac@>
  96. Caveat: This program is not as ``bulletproof'' as the other routines
  97. produced by Stanford's \TeX\ project. It takes care of a great deal of
  98. tedious formatting, but it can produce strange output, because \MF\ is
  99. an extremely general language. Users should proofread their output carefully.
  100. @ \.{MFT} uses a few features of the local \PASCAL\ compiler that may
  101. need to be changed in other installations:
  102. \yskip\item{1)} Case statements have a default.
  103. \item{2)} Input-output routines may need to be adapted for use with a particular
  104. character set and/or for printing messages on the user's terminal.
  105. \yskip\noindent
  106. These features are also present in the \PASCAL\ version of \TeX, where they
  107. are used in a similar (but more complex) way. System-dependent portions
  108. of \.{MFT} can be identified by looking at the entries for `system
  109. dependencies' in the index below.
  110. @!@^system dependencies@>
  111. The ``banner line'' defined here should be changed whenever \.{MFT}
  112. is modified.
  113. @d banner=='This is MFT, Version 2.0'
  114. @ The program begins with a fairly normal header, made up of pieces that
  115. @^system dependencies@>
  116. will mostly be filled in later. The \.{MF} input comes from files |mf_file|,
  117. |change_file|, and |style_file|; the \TeX\ output goes to file |tex_file|.
  118. If it is necessary to abort the job because of a fatal error, the program
  119. calls the `|jump_out|' procedure, which goes to the label |end_of_MFT|.
  120. @d end_of_MFT = 9999 {go here to wrap it up}
  121. @p @t\4@>@<Compiler directives@>@/
  122. program MFT(@!mf_file,@!change_file,@!style_file,@!tex_file);
  123. label end_of_MFT; {go here to finish}
  124. const @<Constants in the outer block@>@/
  125. type @<Types in the outer block@>@/
  126. var @<Globals in the outer block@>@/
  127. @<Error handling procedures@>@/
  128. procedure initialize;
  129.   var @<Local variables for initialization@>@/
  130.   begin @<Set initial values@>@/
  131.   end;
  132. @ The \PASCAL\ compiler used to develop this system has ``compiler
  133. directives'' that can appear in comments whose first character is a dollar sign.
  134. In our case these directives tell the compiler to detect
  135. @^system dependencies@>
  136. things that are out of range.
  137. @<Compiler directives@>=
  138. @{@&$C+,A+,D-@} {range check, catch arithmetic overflow, no debug overhead}
  139. @ Labels are given symbolic names by the following definitions. We insert
  140. the label `|exit|:' just before the `\ignorespaces|end|\unskip' of a
  141. procedure in which we have used the `|return|' statement defined below;
  142. the label `|restart|' is occasionally used at the very beginning of a
  143. procedure; and the label `|reswitch|' is occasionally used just prior to
  144. a \&{case} statement in which some cases change the conditions and we wish to
  145. branch to the newly applicable case.
  146. Loops that are set up with the \&{loop} construction defined below are
  147. commonly exited by going to `|done|' or to `|found|' or to `|not_found|',
  148. and they are sometimes repeated by going to `|continue|'.
  149. @d exit=10 {go here to leave a procedure}
  150. @d restart=20 {go here to start a procedure again}
  151. @d reswitch=21 {go here to start a case statement again}
  152. @d continue=22 {go here to resume a loop}
  153. @d done=30 {go here to exit a loop}
  154. @d found=31 {go here when you've found it}
  155. @d not_found=32 {go here when you've found something else}
  156. @ Here are some macros for common programming idioms.
  157. @d incr(#) == #:=#+1 {increase a variable by unity}
  158. @d decr(#) == #:=#-1 {decrease a variable by unity}
  159. @d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
  160. @d do_nothing == {empty statement}
  161. @d return == goto exit {terminate a procedure call}
  162. @f return == nil
  163. @f loop == xclause
  164. @ We assume that |case| statements may include a default case that applies
  165. if no matching label is found. Thus, we shall use constructions like
  166. @^system dependencies@>
  167. $$\vbox{\halign{#\hfil\cr
  168. |case x of|\cr
  169. 1: $\langle\,$code for $x=1\,\rangle$;\cr
  170. 3: $\langle\,$code for $x=3\,\rangle$;\cr
  171. |othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
  172. |endcases|\cr}}$$
  173. since most \PASCAL\ compilers have plugged this hole in the language by
  174. incorporating some sort of default mechanism. For example, the compiler
  175. used to develop \.{WEB} and \TeX\ allows `|others|:' as a default label,
  176. and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or
  177. `\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases|
  178. and |endcases| should be changed to agree with local conventions.
  179. (Of course, if no default mechanism is available, the |case| statements of
  180. this program must be extended by listing all remaining cases.)
  181. @d othercases == others: {default for cases not listed explicitly}
  182. @d endcases == @+end {follows the default case in an extended |case| statement}
  183. @f othercases == else
  184. @f endcases == end
  185. @ The following parameters are set big enough to handle the Computer
  186. Modern fonts, so they should be sufficient for most applications of \.{MFT}.
  187. @<Constants...@>=
  188. @!max_bytes=10000; {the number of bytes in tokens; must be less than 65536}
  189. @!max_names=1000; {number of tokens}
  190. @!hash_size=353; {should be prime}
  191. @!buf_size=100; {maximum length of input line}
  192. @!line_length=80; {lines of \TeX\ output have at most this many characters,
  193.   should be less than 256}
  194. @ A global variable called |history| will contain one of four values
  195. at the end of every run: |spotless| means that no unusual messages were
  196. printed; |harmless_message| means that a message of possible interest
  197. was printed but no serious errors were detected; |error_message| means that
  198. at least one error was found; |fatal_message| means that the program
  199. terminated abnormally. The value of |history| does not influence the
  200. behavior of the program; it is simply computed for the convenience
  201. of systems that might want to use such information.
  202. @d spotless=0 {|history| value for normal jobs}
  203. @d harmless_message=1 {|history| value when non-serious info was printed}
  204. @d error_message=2 {|history| value when an error was noted}
  205. @d fatal_message=3 {|history| value when we had to stop prematurely}
  206. @d mark_harmless==@t@>@+if history=spotless then history:=harmless_message
  207. @d mark_error==history:=error_message
  208. @d mark_fatal==history:=fatal_message
  209. @<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?}
  210. @ @<Set init...@>=history:=spotless;
  211. @* The character set.
  212. \.{MFT} works internally with ASCII codes, like all other programs
  213. associated with \TeX\ and \MF. The present section has been lifted
  214. almost verbatim from the \MF\ program.
  215. @^ASCII code@>
  216. @ Characters of text that have been converted to \MF's internal form
  217. are said to be of type |ASCII_code|, which is a subrange of the integers.
  218. @<Types...@>=
  219. @!ASCII_code=0..255; {eight-bit numbers}
  220. @ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
  221. character sets were common, so it did not make provision for lowercase
  222. letters. Nowadays, of course, we need to deal with both capital and small
  223. letters in a convenient way, especially in a program for font design;
  224. so the present specification of \.{MFT} has been written under the assumption
  225. that the \PASCAL\ compiler and run-time system permit the use of text files
  226. with more than 64 distinguishable characters. More precisely, we assume that
  227. the character set contains at least the letters and symbols associated
  228. with ASCII codes @'40 through @'176. If additional characters are present,
  229. \.{MFT} can be configured to work with them too.
  230. Since we are dealing with more characters than were present in the first
  231. \PASCAL\ compilers, we have to decide what to call the associated data
  232. type. Some \PASCAL s use the original name |char| for the
  233. characters in text files, even though there now are more than 64 such
  234. characters, while other \PASCAL s consider |char| to be a 64-element
  235. subrange of a larger data type that has some other name.
  236. In order to accommodate this difference, we shall use the name |text_char|
  237. to stand for the data type of the characters that are converted to and
  238. from |ASCII_code| when they are input and output. We shall also assume
  239. that |text_char| consists of the elements |chr(first_text_char)| through
  240. |chr(last_text_char)|, inclusive. The following definitions should be
  241. adjusted if necessary.
  242. @^system dependencies@>
  243. @d text_char == char {the data type of characters in text files}
  244. @d first_text_char=0 {ordinal number of the smallest element of |text_char|}
  245. @d last_text_char=255 {ordinal number of the largest element of |text_char|}
  246. @<Types...@>=
  247. @!text_file=packed file of text_char;
  248. @ @<Local variables for init...@>=
  249. @!i:0..255;
  250. @ The \.{MFT} processor converts between ASCII code and
  251. the user's external character set by means of arrays |xord| and |xchr|
  252. that are analogous to \PASCAL's |ord| and |chr| functions.
  253. @<Glob...@>=
  254. @!xord: array [text_char] of ASCII_code;
  255.   {specifies conversion of input characters}
  256. @!xchr: array [ASCII_code] of text_char;
  257.   {specifies conversion of output characters}
  258. @ Since we are assuming that our \PASCAL\ system is able to read and write the
  259. visible characters of standard ASCII (although not necessarily using the
  260. ASCII codes to represent them), the following assignment statements initialize
  261. most of the |xchr| array properly, without needing any system-dependent
  262. changes. On the other hand, it is possible to implement \.{MFT} with
  263. less complete character sets, and in such cases it will be necessary to
  264. change something here.
  265. @^system dependencies@>
  266. @<Set init...@>=
  267. xchr[@'40]:=' ';
  268. xchr[@'41]:='!';
  269. xchr[@'42]:='"';
  270. xchr[@'43]:='#';
  271. xchr[@'44]:='$';
  272. xchr[@'45]:='%';
  273. xchr[@'46]:='&';
  274. xchr[@'47]:='''';@/
  275. xchr[@'50]:='(';
  276. xchr[@'51]:=')';
  277. xchr[@'52]:='*';
  278. xchr[@'53]:='+';
  279. xchr[@'54]:=',';
  280. xchr[@'55]:='-';
  281. xchr[@'56]:='.';
  282. xchr[@'57]:='/';@/
  283. xchr[@'60]:='0';
  284. xchr[@'61]:='1';
  285. xchr[@'62]:='2';
  286. xchr[@'63]:='3';
  287. xchr[@'64]:='4';
  288. xchr[@'65]:='5';
  289. xchr[@'66]:='6';
  290. xchr[@'67]:='7';@/
  291. xchr[@'70]:='8';
  292. xchr[@'71]:='9';
  293. xchr[@'72]:=':';
  294. xchr[@'73]:=';';
  295. xchr[@'74]:='<';
  296. xchr[@'75]:='=';
  297. xchr[@'76]:='>';
  298. xchr[@'77]:='?';@/
  299. xchr[@'100]:='@@';
  300. xchr[@'101]:='A';
  301. xchr[@'102]:='B';
  302. xchr[@'103]:='C';
  303. xchr[@'104]:='D';
  304. xchr[@'105]:='E';
  305. xchr[@'106]:='F';
  306. xchr[@'107]:='G';@/
  307. xchr[@'110]:='H';
  308. xchr[@'111]:='I';
  309. xchr[@'112]:='J';
  310. xchr[@'113]:='K';
  311. xchr[@'114]:='L';
  312. xchr[@'115]:='M';
  313. xchr[@'116]:='N';
  314. xchr[@'117]:='O';@/
  315. xchr[@'120]:='P';
  316. xchr[@'121]:='Q';
  317. xchr[@'122]:='R';
  318. xchr[@'123]:='S';
  319. xchr[@'124]:='T';
  320. xchr[@'125]:='U';
  321. xchr[@'126]:='V';
  322. xchr[@'127]:='W';@/
  323. xchr[@'130]:='X';
  324. xchr[@'131]:='Y';
  325. xchr[@'132]:='Z';
  326. xchr[@'133]:='[';
  327. xchr[@'134]:='\';
  328. xchr[@'135]:=']';
  329. xchr[@'136]:='^';
  330. xchr[@'137]:='_';@/
  331. xchr[@'140]:='`';
  332. xchr[@'141]:='a';
  333. xchr[@'142]:='b';
  334. xchr[@'143]:='c';
  335. xchr[@'144]:='d';
  336. xchr[@'145]:='e';
  337. xchr[@'146]:='f';
  338. xchr[@'147]:='g';@/
  339. xchr[@'150]:='h';
  340. xchr[@'151]:='i';
  341. xchr[@'152]:='j';
  342. xchr[@'153]:='k';
  343. xchr[@'154]:='l';
  344. xchr[@'155]:='m';
  345. xchr[@'156]:='n';
  346. xchr[@'157]:='o';@/
  347. xchr[@'160]:='p';
  348. xchr[@'161]:='q';
  349. xchr[@'162]:='r';
  350. xchr[@'163]:='s';
  351. xchr[@'164]:='t';
  352. xchr[@'165]:='u';
  353. xchr[@'166]:='v';
  354. xchr[@'167]:='w';@/
  355. xchr[@'170]:='x';
  356. xchr[@'171]:='y';
  357. xchr[@'172]:='z';
  358. xchr[@'173]:='{';
  359. xchr[@'174]:='|';
  360. xchr[@'175]:='}';
  361. xchr[@'176]:='~';
  362. @ The ASCII code is ``standard'' only to a certain extent, since many
  363. computer installations have found it advantageous to have ready access
  364. to more than 94 printing characters.  If \.{MFT} is being used
  365. on a garden-variety \PASCAL\ for which only standard ASCII
  366. codes will appear in the input and output files, it doesn't really matter
  367. what codes are specified in |xchr[0..@'37]|, but the safest policy is to
  368. blank everything out by using the code shown below.
  369. However, other settings of |xchr| will make \.{MFT} more friendly on
  370. computers that have an extended character set, so that users can type things
  371. like `\.^^Z' instead of `\.{<>}', and so that \.{MFT} can echo the
  372. page breaks found in its input.  People with extended character sets can
  373. assign codes arbitrarily, giving an |xchr| equivalent to whatever
  374. characters the users of \.{MFT} are allowed to have in their input files.
  375. Appropriate changes to \.{MFT}'s |char_class| table should then be made.
  376. (Unlike \TeX, each installation of \MF\ has a fixed assignment of category
  377. codes, called the |char_class|.) Such changes make portability of programs
  378. more difficult, so they should be introduced cautiously if at all.
  379. @^character set dependencies@>
  380. @^system dependencies@>
  381. @<Set init...@>=
  382. for i:=0 to @'37 do xchr[i]:=' ';
  383. for i:=@'177 to @'377 do xchr[i]:=' ';
  384. @ The following system-independent code makes the |xord| array contain a
  385. suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
  386. where |i<j<@'177|, the value of |xord[xchr[i]]| will turn out to be
  387. |j| or more; hence, standard ASCII code numbers will be used instead of
  388. codes below @'40 in case there is a coincidence.
  389. @<Set init...@>=
  390. for i:=first_text_char to last_text_char do xord[chr(i)]:=@'177;
  391. for i:=@'200 to @'377 do xord[xchr[i]]:=i;
  392. for i:=1 to @'176 do xord[xchr[i]]:=i;
  393. @* Input and output.
  394. The I/O conventions of this program are essentially identical to those
  395. of \.{WEAVE}.  Therefore people who need to make modifications should be
  396. able to do so without too many headaches.
  397. @ Terminal output is done by writing on file |term_out|, which is assumed to
  398. consist of characters of type |text_char|:
  399. @^system dependencies@>
  400. @d print(#)==write(term_out,#) {`|print|' means write on the terminal}
  401. @d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line}
  402. @d new_line==write_ln(term_out) {start new line on the terminal}
  403. @d print_nl(#)==  {print information starting on a new line}
  404.   begin new_line; print(#);
  405.   end
  406. @<Globals...@>=
  407. @!term_out:text_file; {the terminal as an output file}
  408. @ Different systems have different ways of specifying that the output on a
  409. certain file will appear on the user's terminal. Here is one way to do this
  410. on the \PASCAL\ system that was used in \.{WEAVE}'s initial development:
  411. @^system dependencies@>
  412. @<Set init...@>=
  413. rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
  414. @ The |update_terminal| procedure is called when we want
  415. to make sure that everything we have output to the terminal so far has
  416. actually left the computer's internal buffers and been sent.
  417. @^system dependencies@>
  418. @d update_terminal == break(term_out) {empty the terminal output buffer}
  419. @ The main input comes from |mf_file|; this input may be overridden
  420. by changes in |change_file|. (If |change_file| is empty, there are no changes.)
  421. Furthermore the |style_file| is input first; it is unchangeable.
  422. @<Globals...@>=
  423. @!mf_file:text_file; {primary input}
  424. @!change_file:text_file; {updates}
  425. @!style_file:text_file; {formatting bootstrap}
  426. @ The following code opens the input files.  Since these files were listed
  427. in the program header, we assume that the \PASCAL\ runtime system has
  428. already checked that suitable file names have been given; therefore no
  429. additional error checking needs to be done.
  430. @^system dependencies@>
  431. @p procedure open_input; {prepare to read the inputs}
  432. begin reset(mf_file); reset(change_file); reset(style_file);
  433. @ The main output goes to |tex_file|.
  434. @<Globals...@>=
  435. @!tex_file: text_file;
  436. @ The following code opens |tex_file|.
  437. Since this file was listed in the program header, we assume that the
  438. \PASCAL\ runtime system has checked that a suitable external file name has
  439. been given.
  440. @^system dependencies@>
  441. @<Set init...@>=
  442. rewrite(tex_file);
  443. @ Input goes into an array called |buffer|.
  444. @<Globals...@>=@!buffer: array[0..buf_size] of ASCII_code;
  445. @ The |input_ln| procedure brings the next line of input from the specified
  446. file into the |buffer| array and returns the value |true|, unless the file has
  447. already been entirely read, in which case it returns |false|. The conventions
  448. of \TeX\ are followed; i.e., |ASCII_code| numbers representing the next line
  449. of the file are input into |buffer[0]|, |buffer[1]|, \dots,
  450. |buffer[limit-1]|; trailing blanks are ignored;
  451. and the global variable |limit| is set to the length of the
  452. @^system dependencies@>
  453. line. The value of |limit| must be strictly less than |buf_size|.
  454. @p function input_ln(var f:text_file):boolean;
  455.   {inputs a line or returns |false|}
  456. var final_limit:0..buf_size; {|limit| without trailing blanks}
  457. begin limit:=0; final_limit:=0;
  458. if eof(f) then input_ln:=false
  459. else  begin while not eoln(f) do
  460.     begin buffer[limit]:=xord[f^]; get(f);
  461.     incr(limit);
  462.     if buffer[limit-1]<>" " then final_limit:=limit;
  463.     if limit=buf_size then
  464.       begin while not eoln(f) do get(f);
  465.       decr(limit); {keep |buffer[buf_size]| empty}
  466.       if final_limit>limit then final_limit:=limit;
  467.       print_nl('! Input line too long'); loc:=0; error;
  468. @.Input line too long@>
  469.       end;
  470.     end;
  471.   read_ln(f); limit:=final_limit; input_ln:=true;
  472.   end;
  473. @* Reporting errors to the user.
  474. The command `|err_print('! Error message')|' will report a syntax error to
  475. the user, by printing the error message at the beginning of a new line and
  476. then giving an indication of where the error was spotted in the source file.
  477. Note that no period follows the error message, since the error routine
  478. will automatically supply a period.
  479. The actual error indications are provided by a procedure called |error|.
  480. @d err_print(#)==
  481.     begin new_line; print(#); error;
  482.     end
  483. @<Error handling...@>=
  484. procedure error; {prints `\..' and location of error message}
  485. var@!k,@!l: 0..buf_size; {indices into |buffer|}
  486. begin @<Print error location based on input buffer@>;
  487. update_terminal; mark_error;
  488. @ The error locations can be indicated by using the global variables
  489. |loc|, |line|, |styling|, and |changing|, which tell respectively the first
  490. unlooked-at position in |buffer|, the current line number, and whether or not
  491. the current line is from |style_file| or |change_file| or |mf_file|.
  492. This routine should be modified on systems whose standard text editor
  493. has special line-numbering conventions.
  494. @^system dependencies@>
  495. @<Print error location based on input buffer@>=
  496. begin if styling then print('. (style file ')
  497. else if changing then print('. (change file ')@+else print('. (');
  498. print_ln('l.', line:1, ')');
  499. if loc>=limit then l:=limit else l:=loc;
  500. for k:=1 to l do
  501.   print(xchr[buffer[k-1]]); {print the characters already read}
  502. new_line;
  503. for k:=1 to l do print(' '); {space out the next line}
  504. for k:=l+1 to limit do print(xchr[buffer[k-1]]); {print the part not yet read}
  505. @ The |jump_out| procedure just cuts across all active procedure levels
  506. and jumps out of the program. This is the only non-local \&{goto} statement
  507. in \.{MFT}. It is used when no recovery from a particular error has
  508. been provided.
  509. Some \PASCAL\ compilers do not implement non-local |goto| statements.
  510. @^system dependencies@>
  511. In such cases the code that appears at label |end_of_MFT| should be
  512. copied into the |jump_out| procedure, followed by a call to a system procedure
  513. that terminates the program.
  514. @d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
  515.   end
  516. @<Error handling...@>=
  517. procedure jump_out;
  518. begin goto end_of_MFT;
  519. @ Sometimes the program's behavior is far different from what it should be,
  520. and \.{MFT} prints an error message that is really for the \.{MFT}
  521. maintenance person, not the user. In such cases the program says
  522. |confusion('indication of where we are')|.
  523. @d confusion(#)==fatal_error('! This can''t happen (',#,')')
  524. @.This can't happen@>
  525. @ An overflow stop occurs if \.{MFT}'s tables aren't large enough.
  526. @d overflow(#)==fatal_error('! Sorry, ',#,' capacity exceeded')
  527. @.Sorry, x capacity exceeded@>
  528. @* Inserting the changes.
  529. Let's turn now to the low-level routine |get_line|
  530. that takes care of merging |change_file| into |mf_file|. The |get_line|
  531. procedure also updates the line numbers for error messages.
  532. (This routine was copied from \.{WEAVE}, but updated to include |styling|.)
  533. @<Globals...@>=
  534. @!line:integer; {the number of the current line in the current file}
  535. @!other_line:integer; {the number of the current line in the input file that
  536.   is not currently being read}
  537. @!temp_line:integer; {used when interchanging |line| with |other_line|}
  538. @!limit:0..buf_size; {the last character position occupied in the buffer}
  539. @!loc:0..buf_size; {the next character position to be read from the buffer}
  540. @!input_has_ended: boolean; {if |true|, there is no more input}
  541. @!changing: boolean; {if |true|, the current line is from |change_file|}
  542. @!styling: boolean; {if |true|, the current line is from |style_file|}
  543. @ As we change |changing| from |true| to |false| and back again, we must
  544. remember to swap the values of |line| and |other_line| so that the |err_print|
  545. routine will be sure to report the correct line number.
  546. @d change_changing==
  547.   changing := not changing;
  548.   temp_line:=other_line; other_line:=line; line:=temp_line
  549.     {|line @t$\null\BA\null$@> other_line|}
  550. @ When |changing| is |false|, the next line of |change_file| is kept in
  551. |change_buffer[0..change_limit]|, for purposes of comparison with the next
  552. line of |mf_file|. After the change file has been completely input, we
  553. set |change_limit:=0|, so that no further matches will be made.
  554. @<Globals...@>=
  555. @!change_buffer:array[0..buf_size] of ASCII_code;
  556. @!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
  557. @ Here's a simple function that checks if the two buffers are different.
  558. @p function lines_dont_match:boolean;
  559. label exit;
  560. var k:0..buf_size; {index into the buffers}
  561. begin lines_dont_match:=true;
  562. if change_limit<>limit then return;
  563. if limit>0 then
  564.   for k:=0 to limit-1 do if change_buffer[k]<>buffer[k] then return;
  565. lines_dont_match:=false;
  566. exit: end;
  567. @ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
  568. for the next matching operation. Since blank lines in the change file are
  569. not used for matching, we have |(change_limit=0)and not changing| if and
  570. only if the change file is exhausted. This procedure is called only
  571. when |changing| is true; hence error messages will be reported correctly.
  572. @p procedure prime_the_change_buffer;
  573. label continue, done, exit;
  574. var k:0..buf_size; {index into the buffers}
  575. begin change_limit:=0; {this value will be used if the change file ends}
  576. @<Skip over comment lines in the change file; |return| if end of file@>;
  577. @<Skip to the next nonblank line; |return| if end of file@>;
  578. @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
  579. exit: end;
  580. @ While looking for a line that begins with \.{@@x} in the change file,
  581. we allow lines that begin with \.{@@}, as long as they don't begin with
  582. \.{@@y} or \.{@@z} (which would probably indicate that the change file is
  583. fouled up).
  584. @<Skip over comment lines in the change file...@>=
  585. loop@+  begin incr(line);
  586.   if not input_ln(change_file) then return;
  587.   if limit<2 then goto continue;
  588.   if buffer[0]<>"@@" then goto continue;
  589.   if (buffer[1]>="X")and(buffer[1]<="Z") then
  590.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  591.   if buffer[1]="x" then goto done;
  592.   if (buffer[1]="y")or(buffer[1]="z") then
  593.     begin loc:=2; err_print('! Where is the matching @@x?');
  594. @.Where is the match...@>
  595.     end;
  596. continue: end;
  597. done:
  598. @ Here we are looking at lines following the \.{@@x}.
  599. @<Skip to the next nonblank line...@>=
  600. repeat incr(line);
  601.   if not input_ln(change_file) then
  602.     begin err_print('! Change file ended after @@x');
  603. @.Change file ended...@>
  604.     return;
  605.     end;
  606. until limit>0;
  607. @ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
  608. begin change_limit:=limit;
  609. if limit>0 then for k:=0 to limit-1 do change_buffer[k]:=buffer[k];
  610. @ The following procedure is used to see if the next change entry should
  611. go into effect; it is called only when |changing| is false.
  612. The idea is to test whether or not the current
  613. contents of |buffer| matches the current contents of |change_buffer|.
  614. If not, there's nothing more to do; but if so, a change is called for:
  615. All of the text down to the \.{@@y} is supposed to match. An error
  616. message is issued if any discrepancy is found. Then the procedure
  617. prepares to read the next line from |change_file|.
  618. @p procedure check_change; {switches to |change_file| if the buffers match}
  619. label exit;
  620. var n:integer; {the number of discrepancies found}
  621. @!k:0..buf_size; {index into the buffers}
  622. begin if lines_dont_match then return;
  623. n:=0;
  624. loop@+  begin change_changing; {now it's |true|}
  625.   incr(line);
  626.   if not input_ln(change_file) then
  627.     begin err_print('! Change file ended before @@y');
  628. @.Change file ended...@>
  629.     change_limit:=0;  change_changing; {|false| again}
  630.     return;
  631.     end;
  632.   @<If the current line starts with \.{@@y},
  633.     report any discrepancies and |return|@>;
  634.   @<Move |buffer| and |limit|...@>;
  635.   change_changing; {now it's |false|}
  636.   incr(line);
  637.   if not input_ln(mf_file) then
  638.     begin err_print('! MF file ended during a change');
  639. @.MF file ended...@>
  640.     input_has_ended:=true; return;
  641.     end;
  642.   if lines_dont_match then incr(n);
  643.   end;
  644. exit: end;
  645. @ @<If the current line starts with \.{@@y}...@>=
  646. if limit>1 then if buffer[0]="@@" then
  647.   begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  648.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  649.   if (buffer[1]="x")or(buffer[1]="z") then
  650.     begin loc:=2; err_print('! Where is the matching @@y?');
  651. @.Where is the match...@>
  652.     end
  653.   else if buffer[1]="y" then
  654.     begin if n>0 then
  655.       begin loc:=2; err_print('! Hmm... ',n:1,
  656.         ' of the preceding lines failed to match');
  657. @.Hmm... n of the preceding...@>
  658.       end;
  659.     return;
  660.     end;
  661.   end
  662. @ Here's what we do to get the input rolling.
  663. @<Initialize the input system@>=
  664. begin open_input; line:=0; other_line:=0;@/
  665. changing:=true; prime_the_change_buffer; change_changing;@/
  666. styling:=true; limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
  667. @ The |get_line| procedure is called when |loc>limit|; it puts the next
  668. line of merged input into the buffer and updates the other variables
  669. appropriately.
  670. @p procedure get_line; {inputs the next line}
  671. label restart;
  672. begin restart: if styling then
  673.   @<Read from |style_file| and maybe turn off |styling|@>;
  674. if not styling then
  675.   begin if changing then
  676.     @<Read from |change_file| and maybe turn off |changing|@>;
  677.   if not changing then
  678.     begin @<Read from |mf_file| and maybe turn on |changing|@>;
  679.     if changing then goto restart;
  680.     end;
  681.   end;
  682. @ @<Read from |mf_file|...@>=
  683. begin incr(line);
  684. if not input_ln(mf_file) then input_has_ended:=true
  685. else if limit=change_limit then
  686.   if buffer[0]=change_buffer[0] then
  687.     if change_limit>0 then check_change;
  688. @ @<Read from |style_file|...@>=
  689. begin incr(line);
  690. if not input_ln(style_file) then
  691.   begin styling:=false; line:=0;
  692.   end;
  693. @ @<Read from |change_file|...@>=
  694. begin incr(line);
  695. if not input_ln(change_file) then
  696.   begin err_print('! Change file ended without @@z');
  697. @.Change file ended...@>
  698.   buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
  699.   end;
  700. if limit>1 then {check if the change has ended}
  701.   if buffer[0]="@@" then
  702.     begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  703.       buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  704.     if (buffer[1]="x")or(buffer[1]="y") then
  705.       begin loc:=2; err_print('! Where is the matching @@z?');
  706. @.Where is the match...@>
  707.       end
  708.     else if buffer[1]="z" then
  709.       begin prime_the_change_buffer; change_changing;
  710.       end;
  711.     end;
  712. @ At the end of the program, we will tell the user if the change file
  713. had a line that didn't match any relevant line in |mf_file|.
  714. @<Check that all changes have been read@>=
  715. if change_limit<>0 then {|changing| is false}
  716.   begin for loc:=0 to change_limit do buffer[loc]:=change_buffer[loc];
  717.   limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
  718.   err_print('! Change file entry did not match');
  719. @.Change file entry did not match@>
  720.   end
  721. @* Data structures.
  722. \.{MFT} puts token names
  723. into the large |byte_mem| array, which is packed with eight-bit integers.
  724. Allocation is sequential, since names are never deleted.
  725. An auxiliary array |byte_start| is used as a directory for |byte_mem|;
  726. the |link| and |ilk| arrays give further information about names.
  727. These auxiliary arrays consist of sixteen-bit items.
  728. @<Types...@>=
  729. @!eight_bits=0..255; {unsigned one-byte quantity}
  730. @!sixteen_bits=0..65535; {unsigned two-byte quantity}
  731. @ \.{MFT} has been designed to avoid the need for indices that are more
  732. than sixteen bits wide, so that it can be used on most computers.
  733. @<Globals...@>=
  734. @!byte_mem: packed array [0..max_bytes] of ASCII_code; {characters of names}
  735. @!byte_start: array [0..max_names] of sixteen_bits; {directory into |byte_mem|}
  736. @!link: array [0..max_names] of sixteen_bits; {hash table links}
  737. @!ilk: array [0..max_names] of sixteen_bits; {type codes}
  738. @ The names of tokens are found by computing a hash address |h| and
  739. then looking at strings of bytes signified by |hash[h]|, |link[hash[h]]|,
  740. |link[link[hash[h]]]|, \dots, until either finding the desired name
  741. or encountering a zero.
  742. A `|name_pointer|' variable, which signifies a name, is an index into
  743. |byte_start|. The actual sequence of characters in the name pointed to by
  744. |p| appears in positions |byte_start[p]| to |byte_start[p+1]-1|, inclusive,
  745. of |byte_mem|.
  746. We usually have |byte_start[name_ptr]=byte_ptr|, which is
  747. the starting position for the next name to be stored in |byte_mem|.
  748. @d length(#)==byte_start[#+1]-byte_start[#] {the length of a name}
  749. @<Types...@>=
  750. @!name_pointer=0..max_names; {identifies a name}
  751. @ @<Global...@>=
  752. @!name_ptr:name_pointer; {first unused position in |byte_start|}
  753. @!byte_ptr:0..max_bytes; {first unused position in |byte_mem|}
  754. @ @<Set init...@>=
  755. byte_start[0]:=0; byte_ptr:=0;
  756. byte_start[1]:=0; {this makes name 0 of length zero}
  757. name_ptr:=1;
  758. @ The hash table described above is updated by the |lookup| procedure,
  759. which finds a given name and returns a pointer to its index in
  760. |byte_start|. The token is supposed to match character by character.
  761. If it was not already present, it is inserted into the table.
  762. Because of the way \.{MFT}'s scanning mechanism works, it is most convenient
  763. to let |lookup| search for a token that is present in the |buffer|
  764. array. Two other global variables specify its position in the buffer: the
  765. first character is |buffer[id_first]|, and the last is |buffer[id_loc-1]|.
  766. @<Glob...@>=
  767. @!id_first:0..buf_size; {where the current token begins in the buffer}
  768. @!id_loc:0..buf_size; {just after the current token in the buffer}
  769. @!hash:array [0..hash_size] of sixteen_bits; {heads of hash lists}
  770. @ Initially all the hash lists are empty.
  771. @<Local variables for init...@>=
  772. @!h:0..hash_size; {index into hash-head array}
  773. @ @<Set init...@>=
  774. for h:=0 to hash_size-1 do hash[h]:=0;
  775. @ Here now is the main procedure for finding tokens.
  776. @p function lookup:name_pointer; {finds current token}
  777. label found;
  778. var i:0..buf_size; {index into |buffer|}
  779. @!h:0..hash_size; {hash code}
  780. @!k:0..max_bytes; {index into |byte_mem|}
  781. @!l:0..buf_size; {length of the given token}
  782. @!p:name_pointer; {where the token is being sought}
  783. begin l:=id_loc-id_first; {compute the length}
  784. @<Compute the hash code |h|@>;
  785. @<Compute the name location |p|@>;
  786. if p=name_ptr then @<Enter a new name into the table at position |p|@>;
  787. lookup:=p;
  788. @ A simple hash code is used: If the sequence of
  789. ASCII codes is $c_1c_2\ldots c_m$, its hash value will be
  790. $$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
  791. @<Compute the hash...@>=
  792. h:=buffer[id_first]; i:=id_first+1;
  793. while i<id_loc do
  794.   begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
  795.   end
  796. @ If the token is new, it will be placed in position |p=name_ptr|,
  797. otherwise |p| will point to its existing location.
  798. @<Compute the name location...@>=
  799. p:=hash[h];
  800. while p<>0 do
  801.   begin if length(p)=l then
  802.     @<Compare name |p| with current token,
  803.       |goto found| if equal@>;
  804.   p:=link[p];
  805.   end;
  806. p:=name_ptr; {the current token is new}
  807. link[p]:=hash[h]; hash[h]:=p; {insert |p| at beginning of hash list}
  808. found:
  809. @ @<Compare name |p|...@>=
  810. begin i:=id_first; k:=byte_start[p];
  811. while (i<id_loc)and(buffer[i]=byte_mem[k]) do
  812.   begin incr(i); incr(k);
  813.   end;
  814. if i=id_loc then goto found; {all characters agree}
  815. @ When we begin the following segment of the program, |p=name_ptr|.
  816. @<Enter a new name...@>=
  817. begin if byte_ptr+l>max_bytes then overflow('byte memory');
  818. if name_ptr+1>max_names then overflow('name');
  819. i:=id_first; {get ready to move the token into |byte_mem|}
  820. while i<id_loc do
  821.   begin byte_mem[byte_ptr]:=buffer[i]; incr(byte_ptr); incr(i);
  822.   end;
  823. incr(name_ptr); byte_start[name_ptr]:=byte_ptr;
  824. @<Assign the default value to |ilk[p]|@>;
  825. @* Initializing the primitive tokens.
  826. Each token read by \.{MFT} is recognized as belonging to one of the
  827. following ``types'':
  828. @d indentation=0 {internal code for space at beginning of a line}
  829. @d end_of_line=1 {internal code for hypothetical token at end of a line}
  830. @d end_of_file=2 {internal code for hypothetical token at end of the input}
  831. @d verbatim=3 {internal code for the token `\.{\%\%}'}
  832. @d set_format=4 {internal code for the token `\.{\%\%\%}'}
  833. @d mft_comment=5 {internal code for the token `\.{\%\%\%\%}'}
  834. @d min_action_type=6 {smallest code for tokens that produce ``real'' output}
  835. @d numeric_token=6 {internal code for tokens like `\.{3.14159}'}
  836. @d string_token=7 {internal code for tokens like `|"pie"|'}
  837. @d min_symbolic_token=8 {smallest internal code for a symbolic token}
  838. @d op=8 {internal code for tokens like `\.{sqrt}'}
  839. @d command=9 {internal code for tokens like `\.{addto}'}
  840. @d endit=10 {internal code for tokens like `\.{fi}'}
  841. @d binary=11 {internal code for tokens like `\.{and}'}
  842. @d abinary=12 {internal code for tokens like `\.{+}'}
  843. @d bbinary=13 {internal code for tokens like `\.{step}'}
  844. @d ampersand=14 {internal code for the token `\.{\char`\&}'}
  845. @d pyth_sub=15 {internal code for the token `\.{+-+}'}
  846. @d as_is=16 {internal code for tokens like `\.{]}'}
  847. @d bold=17 {internal code for tokens like `\.{nullpen}'}
  848. @d type_name=18 {internal code for tokens like `\.{numeric}'}
  849. @d path_join=19 {internal code for the token `\.{..}'}
  850. @d colon=20 {internal code for the token `\.:'}
  851. @d semicolon=21 {internal code for the token `\.;'}
  852. @d backslash=22 {internal code for the token `\.{\\}'}
  853. @d double_back=23 {internal code for the token `\.{\\\\}'}
  854. @d less_or_equal=24 {internal code for the token `\.{<=}'}
  855. @d greater_or_equal=25 {internal code for the token `\.{>=}'}
  856. @d not_equal=26 {internal code for the token `\.{<>}'}
  857. @d sharp=27 {internal code for the token `\.{\char`\#}'}
  858. @d comment=28 {internal code for the token `\.{\char`\%}'}
  859. @d recomment=29 {internal code used to resume a comment after `\pb'}
  860. @d min_suffix=30 {smallest code for symbolic tokens in suffixes}
  861. @d internal=30 {internal code for tokens like `\.{pausing}'}
  862. @d input_command=31 {internal code for tokens like `\.{input}'}
  863. @d special_tag=32 {internal code for tags that take at most one subscript}
  864. @d tag=33 {internal code for nonprimitive tokens}
  865. @<Assign the default value to |ilk[p]|@>=ilk[p]:=tag
  866. @ We have to get \MF's primitives into the hash table, and the
  867. simplest way to do this is to insert them every time \.{MFT} is run.
  868. A few macros permit us to do the initialization with a compact program.
  869. We use the fact that the longest primitive is \.{intersectiontimes},
  870. which is 17 letters long.
  871. @d spr17(#)==buffer[17]:=#;cur_tok:=lookup;ilk[cur_tok]:=
  872. @d spr16(#)==buffer[16]:=#;spr17
  873. @d spr15(#)==buffer[15]:=#;spr16
  874. @d spr14(#)==buffer[14]:=#;spr15
  875. @d spr13(#)==buffer[13]:=#;spr14
  876. @d spr12(#)==buffer[12]:=#;spr13
  877. @d spr11(#)==buffer[11]:=#;spr12
  878. @d spr10(#)==buffer[10]:=#;spr11
  879. @d spr9(#)==buffer[9]:=#;spr10
  880. @d spr8(#)==buffer[8]:=#;spr9
  881. @d spr7(#)==buffer[7]:=#;spr8
  882. @d spr6(#)==buffer[6]:=#;spr7
  883. @d spr5(#)==buffer[5]:=#;spr6
  884. @d spr4(#)==buffer[4]:=#;spr5
  885. @d spr3(#)==buffer[3]:=#;spr4
  886. @d spr2(#)==buffer[2]:=#;spr3
  887. @d spr1(#)==buffer[1]:=#;spr2
  888. @d pr1==id_first:=17; spr17
  889. @d pr2==id_first:=16; spr16
  890. @d pr3==id_first:=15; spr15
  891. @d pr4==id_first:=14; spr14
  892. @d pr5==id_first:=13; spr13
  893. @d pr6==id_first:=12; spr12
  894. @d pr7==id_first:=11; spr11
  895. @d pr8==id_first:=10; spr10
  896. @d pr9==id_first:=9; spr9
  897. @d pr10==id_first:=8; spr8
  898. @d pr11==id_first:=7; spr7
  899. @d pr12==id_first:=6; spr6
  900. @d pr13==id_first:=5; spr5
  901. @d pr14==id_first:=4; spr4
  902. @d pr15==id_first:=3; spr3
  903. @d pr16==id_first:=2; spr2
  904. @d pr17==id_first:=1; spr1
  905. @ The intended use of the macros above might not be immediately obvious,
  906. but the riddle is answered by the following:
  907. @<Store all the primitives@>=
  908. id_loc:=18;@/
  909. pr2(".")(".")(path_join);@/
  910. pr1("[")(as_is);@/
  911. pr1("]")(as_is);@/
  912. pr1("}")(as_is);@/
  913. pr1("{")(as_is);@/
  914. pr1(":")(colon);@/
  915. pr2(":")(":")(colon);@/
  916. pr3("|")("|")(":")(colon);@/
  917. pr2(":")("=")(as_is);@/
  918. pr1(",")(as_is);@/
  919. pr1(";")(semicolon);@/
  920. pr1("\")(backslash);@/
  921. pr2("\")("\")(double_back);@/
  922. pr5("a")("d")("d")("t")("o")(command);@/
  923. pr2("a")("t")(bbinary);@/
  924. pr7("a")("t")("l")("e")("a")("s")("t")(op);@/
  925. pr10("b")("e")("g")("i")("n")("g")("r")("o")("u")("p")(command);
  926. pr8("c")("o")("n")("t")("r")("o")("l")("s")(op);@/
  927. pr4("c")("u")("l")("l")(command);@/
  928. pr4("c")("u")("r")("l")(op);@/
  929. pr10("d")("e")("l")("i")("m")("i")("t")("e")("r")("s")(command);@/
  930. pr7("d")("i")("s")("p")("l")("a")("y")(command);@/
  931. pr8("e")("n")("d")("g")("r")("o")("u")("p")(endit);@/
  932. pr8("e")("v")("e")("r")("y")("j")("o")("b")(command);@/
  933. pr6("e")("x")("i")("t")("i")("f")(command);@/
  934. pr11("e")("x")("p")("a")("n")("d")("a")("f")("t")("e")("r")(command);@/
  935. pr4("f")("r")("o")("m")(bbinary);@/
  936. pr8("i")("n")("w")("i")("n")("d")("o")("w")(bbinary);@/
  937. pr7("i")("n")("t")("e")("r")("i")("m")(command);@/
  938. pr3("l")("e")("t")(command);@/
  939. pr11("n")("e")("w")("i")("n")("t")("e")("r")("n")("a")("l")(command);@/
  940. pr2("o")("f")(command);@/
  941. pr10("o")("p")("e")("n")("w")("i")("n")("d")("o")("w")(command);@/
  942. pr10("r")("a")("n")("d")("o")("m")("s")("e")("e")("d")(command);@/
  943. pr4("s")("a")("v")("e")(command);@/
  944. pr10("s")("c")("a")("n")("t")("o")("k")("e")("n")("s")(command);@/
  945. pr7("s")("h")("i")("p")("o")("u")("t")(command);@/
  946. pr4("s")("t")("e")("p")(bbinary);@/
  947. pr3("s")("t")("r")(command);@/
  948. pr7("t")("e")("n")("s")("i")("o")("n")(op);@/
  949. pr2("t")("o")(bbinary);@/
  950. pr5("u")("n")("t")("i")("l")(bbinary);@/
  951. pr3("d")("e")("f")(command);@/
  952. pr6("v")("a")("r")("d")("e")("f")(command);@/
  953. @ (There are so many primitives, it's necessary to break this long
  954. initialization code up into pieces so as not to overflow \.{WEAVE}'s capacity.)
  955. @<Store all the primitives@>=
  956. pr10("p")("r")("i")("m")("a")("r")("y")("d")("e")("f")(command);@/
  957. pr12("s")("e")("c")("o")("n")("d")("a")("r")("y")("d")("e")("f")(command);@/
  958. pr11("t")("e")("r")("t")("i")("a")("r")("y")("d")("e")("f")(command);@/
  959. pr6("e")("n")("d")("d")("e")("f")(endit);@/
  960. pr3("f")("o")("r")(command);@/
  961. pr11("f")("o")("r")("s")("u")("f")("f")("i")("x")("e")("s")(command);@/
  962. pr7("f")("o")("r")("e")("v")("e")("r")(command);@/
  963. pr6("e")("n")("d")("f")("o")("r")(endit);@/
  964. pr5("q")("u")("o")("t")("e")(command);@/
  965. pr4("e")("x")("p")("r")(command);@/
  966. pr6("s")("u")("f")("f")("i")("x")(command);@/
  967. pr4("t")("e")("x")("t")(command);@/
  968. pr7("p")("r")("i")("m")("a")("r")("y")(command);@/
  969. pr9("s")("e")("c")("o")("n")("d")("a")("r")("y")(command);@/
  970. pr8("t")("e")("r")("t")("i")("a")("r")("y")(command);@/
  971. pr5("i")("n")("p")("u")("t")(input_command);@/
  972. pr8("e")("n")("d")("i")("n")("p")("u")("t")(bold);@/
  973. pr2("i")("f")(command);@/
  974. pr2("f")("i")(endit);@/
  975. pr4("e")("l")("s")("e")(command);@/
  976. pr6("e")("l")("s")("e")("i")("f")(command);@/
  977. pr4("t")("r")("u")("e")(bold);@/
  978. pr5("f")("a")("l")("s")("e")(bold);@/
  979. pr11("n")("u")("l")("l")("p")("i")("c")("t")("u")("r")("e")(bold);@/
  980. pr7("n")("u")("l")("l")("p")("e")("n")(bold);@/
  981. pr7("j")("o")("b")("n")("a")("m")("e")(bold);@/
  982. pr10("r")("e")("a")("d")("s")("t")("r")("i")("n")("g")(bold);@/
  983. pr9("p")("e")("n")("c")("i")("r")("c")("l")("e")(bold);@/
  984. pr4("g")("o")("o")("d")(special_tag);@/
  985. pr2("=")(":")(as_is);@/
  986. pr3("=")(":")("|")(as_is);@/
  987. pr4("=")(":")("|")(">")(as_is);@/
  988. pr3("|")("=")(":")(as_is);@/
  989. pr4("|")("=")(":")(">")(as_is);@/
  990. pr4("|")("=")(":")("|")(as_is);@/
  991. pr5("|")("=")(":")("|")(">")(as_is);@/
  992. pr6("|")("=")(":")("|")(">")(">")(as_is);@/
  993. pr4("k")("e")("r")("n")(binary);
  994. pr6("s")("k")("i")("p")("t")("o")(command);@/
  995. @ (Does anybody out there remember the commercials that went \.{LS-MFT}?)
  996. @<Store all the prim...@>=
  997. pr13("n")("o")("r")("m")("a")("l")("d")("e")("v")("i")("a")("t")("e")(op);@/
  998. pr3("o")("d")("d")(op);@/
  999. pr5("k")("n")("o")("w")("n")(op);@/
  1000. pr7("u")("n")("k")("n")("o")("w")("n")(op);@/
  1001. pr3("n")("o")("t")(op);@/
  1002. pr7("d")("e")("c")("i")("m")("a")("l")(op);@/
  1003. pr7("r")("e")("v")("e")("r")("s")("e")(op);@/
  1004. pr8("m")("a")("k")("e")("p")("a")("t")("h")(op);@/
  1005. pr7("m")("a")("k")("e")("p")("e")("n")(op);@/
  1006. pr11("t")("o")("t")("a")("l")("w")("e")("i")("g")("h")("t")(op);@/
  1007. pr3("o")("c")("t")(op);@/
  1008. pr3("h")("e")("x")(op);@/
  1009. pr5("A")("S")("C")("I")("I")(op);@/
  1010. pr4("c")("h")("a")("r")(op);@/
  1011. pr6("l")("e")("n")("g")("t")("h")(op);@/
  1012. pr13("t")("u")("r")("n")("i")("n")("g")("n")("u")("m")("b")("e")("r")(op);@/
  1013. pr5("x")("p")("a")("r")("t")(op);@/
  1014. pr5("y")("p")("a")("r")("t")(op);@/
  1015. pr6("x")("x")("p")("a")("r")("t")(op);@/
  1016. pr6("x")("y")("p")("a")("r")("t")(op);@/
  1017. pr6("y")("x")("p")("a")("r")("t")(op);@/
  1018. pr6("y")("y")("p")("a")("r")("t")(op);@/
  1019. pr4("s")("q")("r")("t")(op);@/
  1020. pr4("m")("e")("x")("p")(op);@/
  1021. pr4("m")("l")("o")("g")(op);@/
  1022. pr4("s")("i")("n")("d")(op);@/
  1023. pr4("c")("o")("s")("d")(op);@/
  1024. pr5("f")("l")("o")("o")("r")(op);@/
  1025. pr14("u")("n")("i")("f")("o")("r")("m")("d")("e")("v")("i")("a")("t")("e")(op);
  1026. pr10("c")("h")("a")("r")("e")("x")("i")("s")("t")("s")(op);@/
  1027. pr5("a")("n")("g")("l")("e")(op);@/
  1028. pr5("c")("y")("c")("l")("e")(op);@/
  1029. @ (If you think this \.{WEB} code is ugly, you should see the Pascal code
  1030. it produces.)
  1031. @<Store all the primitives@>=
  1032. pr13("t")("r")("a")("c")("i")("n")("g")
  1033.  ("t")("i")("t")("l")("e")("s")(internal);@/
  1034. pr16("t")("r")("a")("c")("i")("n")("g")
  1035.  ("e")("q")("u")("a")("t")("i")("o")("n")("s")(internal);@/
  1036. pr15("t")("r")("a")("c")("i")("n")("g")
  1037.  ("c")("a")("p")("s")("u")("l")("e")("s")(internal);@/
  1038. pr14("t")("r")("a")("c")("i")("n")("g")
  1039.  ("c")("h")("o")("i")("c")("e")("s")(internal);@/
  1040. pr12("t")("r")("a")("c")("i")("n")("g")
  1041.  ("s")("p")("e")("c")("s")(internal);@/
  1042. pr11("t")("r")("a")("c")("i")("n")("g")
  1043.  ("p")("e")("n")("s")(internal);@/
  1044. pr15("t")("r")("a")("c")("i")("n")("g")
  1045.  ("c")("o")("m")("m")("a")("n")("d")("s")(internal);@/
  1046. pr13("t")("r")("a")("c")("i")("n")("g")
  1047.  ("m")("a")("c")("r")("o")("s")(internal);@/
  1048. pr12("t")("r")("a")("c")("i")("n")("g")
  1049.  ("e")("d")("g")("e")("s")(internal);@/
  1050. pr13("t")("r")("a")("c")("i")("n")("g")
  1051.  ("o")("u")("t")("p")("u")("t")(internal);@/
  1052. pr12("t")("r")("a")("c")("i")("n")("g")
  1053.  ("s")("t")("a")("t")("s")(internal);@/
  1054. pr13("t")("r")("a")("c")("i")("n")("g")
  1055.  ("o")("n")("l")("i")("n")("e")(internal);@/
  1056. @ @<Store all the primitives@>=
  1057. pr4("y")("e")("a")("r")(internal);@/
  1058. pr5("m")("o")("n")("t")("h")(internal);@/
  1059. pr3("d")("a")("y")(internal);@/
  1060. pr4("t")("i")("m")("e")(internal);@/
  1061. pr8("c")("h")("a")("r")("c")("o")("d")("e")(internal);@/
  1062. pr7("c")("h")("a")("r")("f")("a")("m")(internal);@/
  1063. pr6("c")("h")("a")("r")("w")("d")(internal);@/
  1064. pr6("c")("h")("a")("r")("h")("t")(internal);@/
  1065. pr6("c")("h")("a")("r")("d")("p")(internal);@/
  1066. pr6("c")("h")("a")("r")("i")("c")(internal);@/
  1067. pr6("c")("h")("a")("r")("d")("x")(internal);@/
  1068. pr6("c")("h")("a")("r")("d")("y")(internal);@/
  1069. pr10("d")("e")("s")("i")("g")("n")("s")("i")("z")("e")(internal);@/
  1070. pr4("h")("p")("p")("p")(internal);@/
  1071. pr4("v")("p")("p")("p")(internal);@/
  1072. pr7("x")("o")("f")("f")("s")("e")("t")(internal);@/
  1073. pr7("y")("o")("f")("f")("s")("e")("t")(internal);@/
  1074. pr7("p")("a")("u")("s")("i")("n")("g")(internal);@/
  1075. pr12("s")("h")("o")("w")
  1076.  ("s")("t")("o")("p")("p")("i")("n")("g")(internal);@/
  1077. pr10("f")("o")("n")("t")("m")("a")("k")("i")("n")("g")(internal);@/
  1078. pr8("p")("r")("o")("o")("f")("i")("n")("g")(internal);@/
  1079. pr9("s")("m")("o")("o")("t")("h")("i")("n")("g")(internal);@/
  1080. pr12("a")("u")("t")("o")("r")("o")("u")("n")("d")("i")("n")("g")(internal);@/
  1081. pr11("g")("r")("a")("n")("u")("l")("a")("r")("i")("t")("y")(internal);@/
  1082. pr6("f")("i")("l")("l")("i")("n")(internal);@/
  1083. pr12("t")("u")("r")("n")("i")("n")("g")("c")("h")("e")("c")("k")(internal);@/
  1084. pr12("w")("a")("r")("n")("i")("n")("g")("c")("h")("e")("c")("k")(internal);@/
  1085. pr12("b")("o")("u")("n")("d")("a")("r")("y")("c")("h")("a")("r")(internal);@/
  1086. @ Still more.
  1087. @<Store all the prim...@>=
  1088. pr1("+")(abinary);@/
  1089. pr1("-")(abinary);@/
  1090. pr1("*")(abinary);@/
  1091. pr1("/")(as_is);@/
  1092. pr2("+")("+")(binary);@/
  1093. pr3("+")("-")("+")(pyth_sub);@/
  1094. pr3("a")("n")("d")(binary);@/
  1095. pr2("o")("r")(binary);@/
  1096. pr1("<")(as_is);@/
  1097. pr2("<")("=")(less_or_equal);@/
  1098. pr1(">")(as_is);@/
  1099. pr2(">")("=")(greater_or_equal);@/
  1100. pr1("=")(as_is);@/
  1101. pr2("<")(">")(not_equal);@/
  1102. pr9("s")("u")("b")("s")("t")("r")("i")("n")("g")(command);@/
  1103. pr7("s")("u")("b")("p")("a")("t")("h")(command);@/
  1104. pr13("d")("i")("r")("e")("c")("t")("i")("o")("n")@|
  1105.  ("t")("i")("m")("e")(command);@/
  1106. pr5("p")("o")("i")("n")("t")(command);@/
  1107. pr10("p")("r")("e")("c")("o")("n")("t")("r")("o")("l")(command);@/
  1108. pr11("p")("o")("s")("t")("c")("o")("n")("t")("r")("o")("l")(command);@/
  1109. pr9("p")("e")("n")("o")("f")("f")("s")("e")("t")(command);@/
  1110. pr1("&")(ampersand);@/
  1111. pr7("r")("o")("t")("a")("t")("e")("d")(binary);@/
  1112. pr7("s")("l")("a")("n")("t")("e")("d")(binary);@/
  1113. pr6("s")("c")("a")("l")("e")("d")(binary);@/
  1114. pr7("s")("h")("i")("f")("t")("e")("d")(binary);@/
  1115. pr11("t")("r")("a")("n")("s")("f")("o")("r")("m")("e")("d")(binary);@/
  1116. pr7("x")("s")("c")("a")("l")("e")("d")(binary);@/
  1117. pr7("y")("s")("c")("a")("l")("e")("d")(binary);@/
  1118. pr7("z")("s")("c")("a")("l")("e")("d")(binary);@/
  1119. pr17("i")("n")("t")("e")("r")("s")("e")("c")("t")("i")("o")("n")@|
  1120.  ("t")("i")("m")("e")("s")(binary);@/
  1121. pr7("n")("u")("m")("e")("r")("i")("c")(type_name);@/
  1122. pr6("s")("t")("r")("i")("n")("g")(type_name);@/
  1123. pr7("b")("o")("o")("l")("e")("a")("n")(type_name);@/
  1124. pr4("p")("a")("t")("h")(type_name);@/
  1125. pr3("p")("e")("n")(type_name);@/
  1126. pr7("p")("i")("c")("t")("u")("r")("e")(type_name);@/
  1127. pr9("t")("r")("a")("n")("s")("f")("o")("r")("m")(type_name);@/
  1128. pr4("p")("a")("i")("r")(type_name);@/
  1129. @ At last we are done with the tedious initialization of primitives.
  1130. @<Store all the prim...@>=
  1131. pr3("e")("n")("d")(endit);@/
  1132. pr4("d")("u")("m")("p")(endit);@/
  1133. pr9("b")("a")("t")("c")("h")("m")("o")("d")("e")(bold);
  1134. pr11("n")("o")("n")("s")("t")("o")("p")("m")("o")("d")("e")(bold);
  1135. pr10("s")("c")("r")("o")("l")("l")("m")("o")("d")("e")(bold);
  1136. pr13("e")("r")("r")("o")("r")("s")("t")("o")("p")@|
  1137.  ("m")("o")("d")("e")(bold);
  1138. pr5("i")("n")("n")("e")("r")(command);@/
  1139. pr5("o")("u")("t")("e")("r")(command);@/
  1140. pr9("s")("h")("o")("w")("t")("o")("k")("e")("n")(command);@/
  1141. pr9("s")("h")("o")("w")("s")("t")("a")("t")("s")(bold);@/
  1142. pr4("s")("h")("o")("w")(command);@/
  1143. pr12("s")("h")("o")("w")("v")("a")("r")("i")("a")("b")("l")("e")(command);@/
  1144. pr16("s")("h")("o")("w")@|
  1145.  ("d")("e")("p")("e")("n")("d")("e")("n")("c")("i")("e")("s")(bold);@/
  1146. pr7("c")("o")("n")("t")("o")("u")("r")(command);@/
  1147. pr10("d")("o")("u")("b")("l")("e")("p")("a")("t")("h")(command);@/
  1148. pr4("a")("l")("s")("o")(command);@/
  1149. pr7("w")("i")("t")("h")("p")("e")("n")(command);@/
  1150. pr10("w")("i")("t")("h")("w")("e")("i")("g")("h")("t")(command);@/
  1151. pr8("d")("r")("o")("p")("p")("i")("n")("g")(command);@/
  1152. pr7("k")("e")("e")("p")("i")("n")("g")(command);@/
  1153. pr7("m")("e")("s")("s")("a")("g")("e")(command);@/
  1154. pr10("e")("r")("r")("m")("e")("s")("s")("a")("g")("e")(command);@/
  1155. pr7("e")("r")("r")("h")("e")("l")("p")(command);@/
  1156. pr8("c")("h")("a")("r")("l")("i")("s")("t")(command);@/
  1157. pr8("l")("i")("g")("t")("a")("b")("l")("e")(command);@/
  1158. pr10("e")("x")("t")("e")("n")("s")("i")("b")("l")("e")(command);@/
  1159. pr10("h")("e")("a")("d")("e")("r")("b")("y")("t")("e")(command);@/
  1160. pr9("f")("o")("n")("t")("d")("i")("m")("e")("n")(command);@/
  1161. pr7("s")("p")("e")("c")("i")("a")("l")(command);@/
  1162. pr10("n")("u")("m")("s")("p")("e")("c")("i")("a")("l")(command);@/
  1163. pr1("%")(comment);@/
  1164. pr2("%")("%")(verbatim);@/
  1165. pr3("%")("%")("%")(set_format);@/
  1166. pr4("%")("%")("%")("%")(mft_comment);@/
  1167. pr1("#")(sharp);@/
  1168. @ We also want to store a few other strings of characters that are
  1169. used in \.{MFT}'s translation to \TeX\ code.
  1170. @d ttr1(#)==byte_mem[byte_ptr-1]:=#; cur_tok:=name_ptr;
  1171.   incr(name_ptr); byte_start[name_ptr]:=byte_ptr
  1172. @d ttr2(#)==byte_mem[byte_ptr-2]:=#; ttr1
  1173. @d ttr3(#)==byte_mem[byte_ptr-3]:=#; ttr2
  1174. @d ttr4(#)==byte_mem[byte_ptr-4]:=#; ttr3
  1175. @d ttr5(#)==byte_mem[byte_ptr-5]:=#; ttr4
  1176. @d tr1==incr(byte_ptr); ttr1
  1177. @d tr2==byte_ptr:=byte_ptr+2; ttr2
  1178. @d tr3==byte_ptr:=byte_ptr+3; ttr3
  1179. @d tr4==byte_ptr:=byte_ptr+4; ttr4
  1180. @d tr5==byte_ptr:=byte_ptr+5; ttr5
  1181. @<Glob...@>=
  1182. @!translation:array[ASCII_code] of name_pointer;
  1183. @!i:ASCII_code; {index into |translation|}
  1184. @ @<Store all the translations@>=
  1185. for i:=0 to 255 do translation[i]:=0;
  1186. tr2("\")("$"); translation["$"]:=cur_tok;@/
  1187. tr2("\")("#"); translation["#"]:=cur_tok;@/
  1188. tr2("\")("&"); translation["&"]:=cur_tok;@/
  1189. tr2("\")("{"); translation["{"]:=cur_tok;@/
  1190. tr2("\")("}"); translation["}"]:=cur_tok;@/
  1191. tr2("\")("_"); translation["_"]:=cur_tok;@/
  1192. tr2("\")("%"); translation["%"]:=cur_tok;@/
  1193. tr4("\")("B")("S")(" "); translation["\"]:=cur_tok;@/
  1194. tr4("\")("H")("A")(" "); translation["^"]:=cur_tok;@/
  1195. tr4("\")("T")("I")(" "); translation["~"]:=cur_tok;@/
  1196. tr5("\")("a")("s")("t")(" "); translation["*"]:=cur_tok;@/
  1197. tr4("\")("A")("M")(" "); tr_amp:=cur_tok;@/
  1198. @.\\AM, etc@>
  1199. tr4("\")("B")("L")(" "); tr_skip:=cur_tok;@/
  1200. tr4("\")("S")("H")(" "); tr_sharp:=cur_tok;@/
  1201. tr4("\")("P")("S")(" "); tr_ps:=cur_tok;@/
  1202. tr4("\")("l")("e")(" "); tr_le:=cur_tok;@/
  1203. tr4("\")("g")("e")(" "); tr_ge:=cur_tok;@/
  1204. tr4("\")("n")("e")(" "); tr_ne:=cur_tok;@/
  1205. tr5("\")("q")("u")("a")("d"); tr_quad:=cur_tok;@/
  1206. @ @<Glob...@>=
  1207. @!tr_le,@!tr_ge,@!tr_ne,@!tr_amp,@!tr_sharp,@!tr_skip,@!tr_ps,
  1208.  @!tr_quad:name_pointer; {special translations}
  1209. @* Inputting the next token.
  1210. \.{MFT}'s lexical scanning routine is called |get_next|. This procedure
  1211. inputs the next token of \MF\ input and puts its encoded meaning into
  1212. two global variables, |cur_type| and |cur_tok|.
  1213. @<Glob...@>=
  1214. @!cur_type:eight_bits; {type of token just scanned}
  1215. @!cur_tok:integer; {hash table or buffer location}
  1216. @!prev_type:eight_bits; {previous value of |cur_type|}
  1217. @!prev_tok:integer; {previous value of |cur_tok|}
  1218. @ @<Set init...@>=
  1219. cur_type:=end_of_line; cur_tok:=0;
  1220. @ Two global state variables affect the behavior of |get_next|: A space
  1221. will be considered significant when |start_of_line| is |true|,
  1222. and the buffer will be considered devoid of information when |empty_buffer|
  1223. is |true|.
  1224. @<Glob...@>=
  1225. @!start_of_line:boolean; {has the current line had nothing but spaces so far?}
  1226. @!empty_buffer:boolean; {is it time to input a new line?}
  1227. @ The 256 |ASCII_code| characters are grouped into classes by means of
  1228. the |char_class| table. Individual class numbers have no semantic
  1229. or syntactic significance, expect in a few instances defined here.
  1230. There's also |max_class|, which can be used as a basis for additional
  1231. class numbers in nonstandard extensions of \MF.
  1232. @d digit_class=0 {the class number of \.{0123456789}}
  1233. @d period_class=1 {the class number of `\..'}
  1234. @d space_class=2 {the class number of spaces and nonstandard characters}
  1235. @d percent_class=3 {the class number of `\.\%'}
  1236. @d string_class=4 {the class number of `\."'}
  1237. @d right_paren_class=8 {the class number of `\.)'}
  1238. @d isolated_classes==5,6,7,8 {characters that make length-one tokens only}
  1239. @d letter_class=9 {letters and the underline character}
  1240. @d left_bracket_class=17 {`\.['}
  1241. @d right_bracket_class=18 {`\.]'}
  1242. @d invalid_class=20 {bad character in the input}
  1243. @d end_line_class=21 {end of an input line (\.{MFT} only)}
  1244. @d max_class=21 {the largest class number}
  1245. @<Glob...@>=
  1246. @!char_class:array[ASCII_code] of 0..max_class; {the class numbers}
  1247. @ If changes are made to accommodate non-ASCII character sets, they should be
  1248. essentially the same in \.{MFT} as in \MF. However, \.{MFT} has an additional
  1249. class number, the |end_line_class|, which is used only for the special
  1250. character |carriage_return| that is placed at the end of the input buffer.
  1251. @^character set dependencies@>
  1252. @^system dependencies@>
  1253. @d carriage_return=@'15 {special code placed in |buffer[limit]|}
  1254. @<Set init...@>=
  1255. for i:="0" to "9" do char_class[i]:=digit_class;
  1256. char_class["."]:=period_class;
  1257. char_class[" "]:=space_class;
  1258. char_class["%"]:=percent_class;
  1259. char_class[""""]:=string_class;@/
  1260. char_class[","]:=5;
  1261. char_class[";"]:=6;
  1262. char_class["("]:=7;
  1263. char_class[")"]:=right_paren_class;
  1264. for i:="A" to "Z" do char_class[i]:=letter_class;
  1265. for i:="a" to "z" do char_class[i]:=letter_class;
  1266. char_class["_"]:=letter_class;@/
  1267. char_class["<"]:=10;
  1268. char_class["="]:=10;
  1269. char_class[">"]:=10;
  1270. char_class[":"]:=10;
  1271. char_class["|"]:=10;@/
  1272. char_class["`"]:=11;
  1273. char_class["'"]:=11;@/
  1274. char_class["+"]:=12;
  1275. char_class["-"]:=12;@/
  1276. char_class["/"]:=13;
  1277. char_class["*"]:=13;
  1278. char_class["\"]:=13;@/
  1279. char_class["!"]:=14;
  1280. char_class["?"]:=14;@/
  1281. char_class["#"]:=15;
  1282. char_class["&"]:=15;
  1283. char_class["@@"]:=15;
  1284. char_class["$"]:=15;@/
  1285. char_class["^"]:=16;
  1286. char_class["~"]:=16;@/
  1287. char_class["["]:=left_bracket_class;
  1288. char_class["]"]:=right_bracket_class;@/
  1289. char_class["{"]:=19;
  1290. char_class["}"]:=19;@/
  1291. for i:=0 to " "-1 do char_class[i]:=invalid_class;
  1292. char_class[carriage_return]:=end_line_class;@/
  1293. for i:=127 to 255 do char_class[i]:=invalid_class;
  1294. @ And now we're ready to take the plunge into |get_next| itself.
  1295. @d switch=25 {a label in |get_next|}
  1296. @d pass_digits=85 {another}
  1297. @d pass_fraction=86 {and still another, although |goto| is considered harmful}
  1298. @p procedure get_next; {sets |cur_type| and |cur_tok| to next token}
  1299. label switch,pass_digits,pass_fraction,done,found,exit;
  1300. var @!c:ASCII_code; {the current character in the buffer}
  1301. @!class:ASCII_code; {its class number}
  1302. begin prev_type:=cur_type; prev_tok:=cur_tok;
  1303. if empty_buffer then
  1304.   @<Bring in a new line of input; |return| if the file has ended@>;
  1305. switch: c:=buffer[loc]; id_first:=loc; incr(loc); class:=char_class[c];
  1306. @<Branch on the |class|, scan the token; |return| directly if the
  1307.   token is special, or |goto found| if it needs to be looked up@>;
  1308. found:id_loc:=loc; cur_tok:=lookup; cur_type:=ilk[cur_tok];
  1309. exit:end;
  1310. @ @d emit(#)==@t@>@+begin cur_type:=#; cur_tok:=id_first; return;@+end
  1311. @<Branch on the |class|...@>=
  1312. case class of
  1313. digit_class:goto pass_digits;
  1314. period_class:begin class:=char_class[buffer[loc]];
  1315.   if class>period_class then goto switch {ignore isolated `\..'}
  1316.   else if class<period_class then goto pass_fraction; {|class=digit_class|}
  1317.   end;
  1318. space_class:if start_of_line then emit(indentation)
  1319.   else goto switch;
  1320. end_line_class: emit(end_of_line);
  1321. string_class:@<Get a string token and |return|@>;
  1322. isolated_classes: goto found;
  1323. invalid_class:@<Decry the invalid character and |goto switch|@>;
  1324. othercases do_nothing {letters, etc.}
  1325. endcases;@/
  1326. while char_class[buffer[loc]]=class do incr(loc);
  1327. goto found;
  1328. pass_digits: while char_class[buffer[loc]]=digit_class do incr(loc);
  1329. if buffer[loc]<>"." then goto done;
  1330. if char_class[buffer[loc+1]]<>digit_class then goto done;
  1331. incr(loc);
  1332. pass_fraction:repeat incr(loc);
  1333. until char_class[buffer[loc]]<>digit_class;
  1334. done:emit(numeric_token)
  1335. @ @<Get a string token and |return|@>=
  1336. loop@+begin if buffer[loc]="""" then
  1337.     begin incr(loc); emit(string_token);
  1338.     end;
  1339.   if loc=limit then @<Decry the missing string delimiter and |goto switch|@>;
  1340.   incr(loc);
  1341.   end
  1342. @ @<Decry the missing string delimiter and |goto switch|@>=
  1343. begin err_print('! Incomplete string will be ignored'); goto switch;
  1344. @.Incomplete string...@>
  1345. @ @<Decry the invalid character and |goto switch|@>=
  1346. begin err_print('! Invalid character will be ignored'); goto switch;
  1347. @.Invalid character...@>
  1348. @ @<Bring in a new line of input; |return| if the file has ended@>=
  1349. begin get_line;
  1350. if input_has_ended then emit(end_of_file);
  1351. buffer[limit]:=carriage_return; loc:=0; start_of_line:=true;
  1352. empty_buffer:=false;
  1353. @* Low-level output routines.
  1354. The \TeX\ output is supposed to appear in lines at most |line_length|
  1355. characters long, so we place it into an output buffer. During the output
  1356. process, |out_line| will hold the current line number of the line about to
  1357. be output.
  1358. @<Glo...@>=
  1359. @!out_buf:array[0..line_length] of ASCII_code; {assembled characters}
  1360. @!out_ptr:0..line_length; {number of characters in |out_buf|}
  1361. @!out_line: integer; {coordinates of next line to be output}
  1362. @ The |flush_buffer| routine empties the buffer up to a given breakpoint,
  1363. and moves any remaining characters to the beginning of the next line.
  1364. If the |per_cent| parameter is |true|, a |"%"| is appended to the line
  1365. that is being output; in this case the breakpoint |b| should be strictly
  1366. less than |line_length|. If the |per_cent| parameter is |false|,
  1367. trailing blanks are suppressed.
  1368. The characters emptied from the buffer form a new line of output.
  1369. @p procedure flush_buffer(@!b:eight_bits;@!per_cent:boolean);
  1370.   {outputs |out_buf[1..b]|, where |b<=out_ptr|}
  1371. label done;
  1372. var j,@!k:0..line_length;
  1373. begin j:=b;
  1374. if not per_cent then {remove trailing blanks}
  1375.   loop@+  begin if j=0 then goto done;
  1376.     if out_buf[j]<>" " then goto done;
  1377.     decr(j);
  1378.     end;
  1379. done: for k:=1 to j do write(tex_file,xchr[out_buf[k]]);
  1380. if per_cent then write(tex_file,xchr["%"]);
  1381. write_ln(tex_file); incr(out_line);
  1382. if b<out_ptr then for k:=b+1 to out_ptr do out_buf[k-b]:=out_buf[k];
  1383. out_ptr:=out_ptr-b;
  1384. @ \.{MFT} calls |flush_buffer(out_ptr,false)| before it has input
  1385. anything. We initialize the output variables
  1386. so that the first line of the output file will be `\.{\\input mftmac}'.
  1387. @.\\input mftmac@>
  1388. @.mftmac@>
  1389. @<Set init...@>=
  1390. out_ptr:=1; out_buf[1]:=" "; out_line:=1; write(tex_file,'\input mftmac');
  1391. @ When we wish to append the character |c| to the output buffer, we write
  1392. `$|out|(c)$'; this will cause the buffer to be emptied if it was already
  1393. full. Similarly, `$|out2|(c_1)(c_2)$' appends a pair of characters.
  1394. A line break will occur at a space or after a single-nonletter
  1395. \TeX\ control sequence.
  1396. @d oot(#)==@;@/
  1397.   if out_ptr=line_length then break_out;
  1398.   incr(out_ptr); out_buf[out_ptr]:=#;
  1399. @d oot1(#)==oot(#)@+end
  1400. @d oot2(#)==oot(#)@,oot1
  1401. @d oot3(#)==oot(#)@,oot2
  1402. @d oot4(#)==oot(#)@,oot3
  1403. @d oot5(#)==oot(#)@,oot4
  1404. @d out==@+begin oot1
  1405. @d out2==@+begin oot2
  1406. @d out3==@+begin oot3
  1407. @d out4==@+begin oot4
  1408. @d out5==@+begin oot5
  1409. @ The |break_out| routine is called just before the output buffer is about
  1410. to overflow. To make this routine a little faster, we initialize position
  1411. 0 of the output buffer to `\.\\'; this character isn't really output.
  1412. @<Set init...@>=
  1413. out_buf[0]:="\";
  1414. @ A long line is broken at a blank space or just before a backslash that isn't
  1415. preceded by another backslash. In the latter case, a |"%"| is output at
  1416. the break. (This policy has a known bug, in the rare situation that the
  1417. backslash was in a string constant that's being output ``verbatim.'')
  1418. @p procedure break_out; {finds a way to break the output line}
  1419. label exit;
  1420. var k:0..line_length; {index into |out_buf|}
  1421. @!d:ASCII_code; {character from the buffer}
  1422. begin k:=out_ptr;
  1423. loop@+  begin if k=0 then
  1424.     @<Print warning message, break the line, |return|@>;
  1425.   d:=out_buf[k];
  1426.   if d=" " then
  1427.     begin flush_buffer(k,false); return;
  1428.     end;
  1429.   if (d="\")and(out_buf[k-1]<>"\") then {in this case |k>1|}
  1430.     begin flush_buffer(k-1,true); return;
  1431.     end;
  1432.   decr(k);
  1433.   end;
  1434. exit:end;
  1435. @ We get to this module only in unusual cases that the entire output line
  1436. consists of a string of backslashes followed by a string of nonblank
  1437. non-backslashes. In such cases it is almost always safe to break the
  1438. line by putting a |"%"| just before the last character.
  1439. @<Print warning message...@>=
  1440. begin print_nl('! Line had to be broken (output l.',out_line:1);
  1441. @.Line had to be broken@>
  1442. print_ln('):');
  1443. for k:=1 to out_ptr-1 do print(xchr[out_buf[k]]);
  1444. new_line; mark_harmless;
  1445. flush_buffer(out_ptr-1,true); return;
  1446. @ To output a string of bytes from |byte_mem|, we call |out_str|.
  1447. @p procedure out_str(@!p:name_pointer); {outputs a string}
  1448. var @!k:0..max_bytes; {index into |byte_mem|}
  1449. begin for k:=byte_start[p] to byte_start[p+1]-1 do out(byte_mem[k]);
  1450. @ The |out_name| subroutine is used to output a symbolic token.
  1451. Unusual characters are translated into forms that won't screw up.
  1452. @p procedure out_name(@!p:name_pointer); {outputs a name}
  1453. var @!k:0..max_bytes; {index into |byte_mem|}
  1454. @!t:name_pointer; {translation of character being output, if any}
  1455. begin for k:=byte_start[p] to byte_start[p+1]-1 do
  1456.   begin t:=translation[byte_mem[k]];
  1457.         if t=0 then out(byte_mem[k])
  1458.   else out_str(t);
  1459.   end;
  1460. @ We often want to output a name after calling a numeric macro
  1461. (e.g., `\.{\\1\{foo\}}').
  1462. @p procedure out_mac_and_name(@!n:ASCII_code; @!p:name_pointer);
  1463. begin out("\"); out(n);
  1464. if length(p)=1 then out_name(p)
  1465. else  begin out("{"); out_name(p); out("}");
  1466.   end;
  1467. @ Here's a routine that simply copies from the input buffer to the output
  1468. buffer.
  1469. @p procedure copy(@!first_loc:integer); {output |buffer[first_loc..loc-1]|}
  1470. var @!k:0..buf_size; {|buffer| location being copied}
  1471. begin for k:=first_loc to loc-1 do out(buffer[k]);
  1472. @* Translation.
  1473. The main work of \.{MFT} is accomplished by a routine that translates
  1474. the tokens, one by one, with a limited amount of lookahead/lookbehind.
  1475. Automata theorists might loosely call this a ``finite state transducer,''
  1476. because the flow of control is comparatively simple.
  1477. @p procedure do_the_translation;
  1478. label restart,reswitch,done,exit;
  1479. var @!k:0..buf_size; {looks ahead in the buffer}
  1480. @!t:integer; {type that spreads to new tokens}
  1481. begin restart:if out_ptr>0 then flush_buffer(out_ptr,false);
  1482. empty_buffer:=true;
  1483. loop@+  begin get_next;
  1484.   if start_of_line then @<Do special actions at the start of a line@>;
  1485.   reswitch:case cur_type of
  1486.   numeric_token:@<Translate a numeric token or a fraction@>;
  1487.   string_token:@<Translate a string token@>;
  1488.   indentation:out_str(tr_quad);
  1489.   end_of_line,mft_comment:@<Wind up a line of translation and |goto restart|,
  1490.     or finish a \pb\ segment and |goto reswitch|@>;
  1491.   end_of_file:return;
  1492. @t\4@>  @<Cases that translate primitive tokens@>@;
  1493.   comment,recomment:@<Translate a comment and |goto restart|,
  1494.     unless there's a \pb\ segment@>;
  1495.   verbatim:@<Copy the rest of the current input line to the output,
  1496.     then |goto restart|@>;
  1497.   set_format:@<Change the translation format of tokens,
  1498.     and |goto restart| or |reswitch|@>;
  1499.   internal,special_tag,tag:@<Translate a tag and possible subscript@>;
  1500.   end;  {all cases have been listed}
  1501.   end;
  1502. exit:end;
  1503. @ @<Do special actions at the start of a line@>=
  1504. if cur_type>=min_action_type then
  1505.   begin out("$"); start_of_line:=false;
  1506.   case cur_type of
  1507.   endit:out2("\")("!");
  1508. @.\\!@>
  1509.   binary,abinary,bbinary,ampersand,pyth_sub:out2("{")("}");
  1510. @.\{\}@>
  1511.   othercases do_nothing
  1512.   endcases;
  1513.   end
  1514. else if cur_type=end_of_line then
  1515.   begin out_str(tr_skip); goto restart;
  1516.   end
  1517. else if cur_type=mft_comment then goto restart
  1518. @ Let's start with some of the easier translations, so that the harder
  1519. ones will also be easy when we get to them. A string like |"cat"|
  1520. comes out `\.{\\7"cat"}'.
  1521. @<Translate a string token@>=
  1522. begin out2("\")("7"); copy(cur_tok);
  1523. @.\\7@>
  1524. @ Similarly, the translation of `\.{sqrt}' is `\.{\\1\{sqrt\}}'.
  1525. @<Cases that translate primitive tokens@>=
  1526. op: out_mac_and_name("1",cur_tok);
  1527. @.\\1@>
  1528. command: out_mac_and_name("2",cur_tok);
  1529. @.\\2@>
  1530. type_name: if prev_type=command then out_mac_and_name("1",cur_tok)
  1531.   else out_mac_and_name("2",cur_tok);
  1532. endit: out_mac_and_name("3",cur_tok);
  1533. @.\\3@>
  1534. bbinary: out_mac_and_name("4",cur_tok);
  1535. @.\\4@>
  1536. bold: out_mac_and_name("5",cur_tok);
  1537. @.\\5@>
  1538. binary: out_mac_and_name("6",cur_tok);
  1539. @.\\6@>
  1540. path_join: out_mac_and_name("8",cur_tok);
  1541. @.\\8@>
  1542. colon: out_mac_and_name("?",cur_tok);
  1543. @.\\?@>
  1544. @ Here are a few more easy cases.
  1545. @<Cases that translate primitive tokens@>=
  1546. as_is,sharp,abinary: out_name(cur_tok);
  1547. double_back: out2("\")(";");
  1548. @.\\;@>
  1549. semicolon: begin out_name(cur_tok); get_next;
  1550.   if cur_type<>end_of_line then if cur_type<>endit then out2("\")(" ");
  1551. @.\\\char32@>
  1552.   goto reswitch;
  1553.   end;
  1554. @ Some of the primitives have a fixed output (independent of |cur_tok|):
  1555. @<Cases that translate primitive tokens@>=
  1556. backslash:out_str(translation["\"]);
  1557. pyth_sub:out_str(tr_ps);
  1558. less_or_equal:out_str(tr_le);
  1559. greater_or_equal:out_str(tr_ge);
  1560. not_equal:out_str(tr_ne);
  1561. ampersand:out_str(tr_amp);
  1562. @ The remaining primitive is slightly special.
  1563. @<Cases that translate primitive tokens@>=
  1564. input_command: begin out_mac_and_name("2",cur_tok);
  1565.   out5("\")("h")("b")("o")("x");
  1566.   @<Scan the file name and output it in \.{typewriter type}@>;
  1567.   end;
  1568. @ File names have different formats on different computers, so we don't scan
  1569. them with |get_next|. Here we use
  1570. a rule that probably covers most cases satisfactorily: We ignore leading
  1571. blanks, then consider the file name to consist of all subsequent characters
  1572. up to the first blank, semicolon, comment, or end-of-line.
  1573. (A |carriage_return| appears at the end of the line.)
  1574. @<Scan the file name and output it in \.{typewriter type}@>=
  1575. while buffer[loc]=" " do incr(loc);
  1576. out5("{")("\")("t")("t")(" ");
  1577. while (buffer[loc]<>" ")and(buffer[loc]<>"%")and(buffer[loc]<>";")
  1578.   and(loc<limit) do
  1579.   begin out(buffer[loc]); incr(loc);
  1580.   end;
  1581. out("}")
  1582. @ @<Translate a numeric token or a fraction@>=
  1583. if buffer[loc]="/" then
  1584.   if char_class[buffer[loc+1]]=digit_class then {it's a fraction}
  1585.     begin out5("\")("f")("r")("a")("c"); copy(cur_tok); get_next;
  1586. @.\\frac@>
  1587.     out2("/")("{"); get_next; copy(cur_tok); out("}");
  1588.     end
  1589.   else copy(cur_tok)
  1590. else copy(cur_tok)
  1591. @ @<Translate a tag and possible subscript@>=
  1592. begin if length(cur_tok)=1 then out_name(cur_tok)
  1593. else out_mac_and_name("\",cur_tok);
  1594. @.\\\\@>
  1595. get_next;
  1596. if byte_mem[byte_start[prev_tok]]="'" then goto reswitch;
  1597. case prev_type of
  1598. internal:begin if (cur_type=numeric_token)or(cur_type>=min_suffix) then
  1599.     out2("\")(",");
  1600. @.\\,@>
  1601.   goto reswitch;
  1602.   end;
  1603. special_tag:if cur_type<min_suffix then goto reswitch
  1604.   else  begin out("."); cur_type:=internal; goto reswitch;
  1605. @..@>
  1606.     end;
  1607. tag:begin if cur_type=tag then if byte_mem[byte_start[cur_tok]]="'" then
  1608.     goto reswitch; {a sequence of primes goes on the main line}
  1609.   if (cur_type=numeric_token)or(cur_type>=min_suffix) then
  1610.     @<Translate a subscript@>
  1611.   else if cur_type=sharp then out_str(tr_sharp)
  1612.   else goto reswitch;
  1613.   end;
  1614. end; {there are no other cases}
  1615. @ @<Translate a subscript@>=
  1616. begin out2("_")("{");
  1617. loop@+  begin if cur_type>=min_suffix then out_name(cur_tok)
  1618.   else copy(cur_tok);
  1619.   if prev_type=special_tag then
  1620.     begin get_next; goto done;
  1621.     end;
  1622.   get_next;
  1623.   if cur_type<min_suffix then if cur_type<>numeric_token then goto done;
  1624.   if cur_type=prev_type then
  1625.     if cur_type=numeric_token then out2("\")(",")
  1626. @.\\,@>
  1627.     else if char_class[byte_mem[byte_start[cur_tok]]]=@|
  1628.      char_class[byte_mem[byte_start[prev_tok]]] then
  1629.       if byte_mem[byte_start[prev_tok]]<>"." then out(".")
  1630.       else out2("\")(",");
  1631.   end;
  1632. done: out("}"); goto reswitch;
  1633. @ The tricky thing about comments is that they might contain \pb.
  1634. We scan ahead for this, and replace the second `\.{\char'174}'
  1635. by a |carriage_return|.
  1636. @<Translate a comment and |goto restart|...@>=
  1637. begin if cur_type=comment then out2("\")("9");
  1638. @.\\9@>
  1639. id_first:=loc;
  1640. while (loc<limit)and(buffer[loc]<>"|") do incr(loc);
  1641. copy(id_first);
  1642. if loc<limit then
  1643.   begin start_of_line:=true; incr(loc); k:=loc;
  1644.   while (k<limit)and(buffer[k]<>"|") do incr(k);
  1645.   buffer[k]:=carriage_return;
  1646.   end
  1647. else  begin if out_buf[out_ptr]="\" then out(" ");
  1648.   out4("\")("p")("a")("r"); goto restart;
  1649. @.\\par@>
  1650.   end;
  1651. @ @<Copy the rest of the current input line to the output...@>=
  1652. begin id_first:=loc; loc:=limit; copy(id_first);
  1653. if out_ptr=0 then
  1654.   begin out_ptr:=1; out_buf[1]:=" ";
  1655.   end;
  1656. goto restart;
  1657. @ @<Wind up a line of translation...@>=
  1658. begin out("$");
  1659. if (loc<limit)and(cur_type=end_of_line) then
  1660.   begin cur_type:=recomment; goto reswitch;
  1661.   end
  1662. else  begin out4("\")("p")("a")("r"); goto restart;
  1663. @.\\par@>
  1664.   end;
  1665. @ @<Change the translation format...@>=
  1666. begin start_of_line:=false; get_next; t:=cur_type;
  1667. while cur_type>=min_symbolic_token do
  1668.   begin get_next;
  1669.   if cur_type>=min_symbolic_token then ilk[cur_tok]:=t;
  1670.   end;
  1671. if cur_type<>end_of_line then if cur_type<>mft_comment then
  1672.   begin err_print('! Only symbolic tokens should appear after %%%');
  1673. @.Only symbolic tokens...@>
  1674.   goto reswitch;
  1675.   end;
  1676. empty_buffer:=true; goto restart;
  1677. @* The main program.
  1678. Let's put it all together now: \.{MFT} starts and ends here.
  1679. @^system dependencies@>
  1680. @p begin initialize; {beginning of the main program}
  1681. print_ln(banner); {print a ``banner line''}
  1682. @<Store all the primitives@>;
  1683. @<Store all the translations@>;
  1684. @<Initialize the input...@>;
  1685. do_the_translation;
  1686. @<Check that all changes have been read@>;
  1687. end_of_MFT:{here files should be closed if the operating system requires it}
  1688. @<Print the job |history|@>;
  1689. @ Some implementations may wish to pass the |history| value to the
  1690. operating system so that it can be used to govern whether or not other
  1691. programs are started. Here we simply report the history to the user.
  1692. @^system dependencies@>
  1693. @<Print the job |history|@>=
  1694. case history of
  1695. spotless: print_nl('(No errors were found.)');
  1696. harmless_message: print_nl('(Did you see the warning message above?)');
  1697. error_message: print_nl('(Pardon me, but I think I spotted something wrong.)');
  1698. fatal_message: print_nl('(That was a fatal error, my friend.)');
  1699. end {there are no other cases}
  1700. @* System-dependent changes.
  1701. This module should be replaced, if necessary, by changes to the program
  1702. that are necessary to make \.{MFT} work at a particular installation.
  1703. It is usually best to design your change file so that all changes to
  1704. previous modules preserve the module numbering; then everybody's version
  1705. will be consistent with the printed program. More extensive changes,
  1706. which introduce new modules, can be inserted here; then only the index
  1707. itself will get a new module number.
  1708. @^system dependencies@>
  1709. @* Index.
  1710.