home *** CD-ROM | disk | FTP | other *** search
/ Mega Top 1 / os2_top1.zip / os2_top1 / APPS / TEKST / SPIDER / MASTER / SPIDER.WEB < prev    next >
Encoding:
Text File  |  1990-01-23  |  85.2 KB  |  2,577 lines

  1. % Copyright 1989 by Norman Ramsey, Odyssey Research Associates
  2. % To be used for research purposes only
  3. % For more information, see file COPYRIGHT in the parent directory
  4.  
  5. \message{OK, entering \string\batchmode...}
  6. \batchmode
  7.  
  8. \let\RA\rightarrow
  9.  
  10. \def\vert{{\tt\char'174}}
  11. \def\pb{$\.|\ldots\.|$} % C brackets (|...|)
  12.  
  13. \def\title{SPIDER}
  14.  
  15. \def\topofcontents{\null\vfill
  16.   \titlefalse % include headline on the contents page
  17.   \def\rheader{\hfil}
  18.   \centerline{\titlefont The {\ttitlefont SPIDER} processor}
  19.   \vfill}
  20.  
  21.  
  22. \def\syntax##1{\leavevmode\hbox{$\langle\hbox{\sl ##1\/}\rangle$}}
  23. \def\produces{\leavevmode\hbox{${}::={}$}}
  24. \def\opt##1{$[$##1$]$}
  25.  
  26. #*={\tt SPIDER} proper.
  27. #*Introduction.
  28. This is an AWK program designed to read a description of a programming
  29. language and to write out the language-dependent parts of WEB.
  30. In the main,
  31. the description of a programming language is 
  32.  a list of all the tokens of the language 
  33. (together with various facts about them) and a grammar for prettyprinting
  34. code fragments written in that language.
  35. The ``Spider User's Guide'' describes how to use {\tt SPIDER} to construct 
  36. a {\tt WEB} system for the ALGOL-like language of your choice.
  37. ({\tt SPIDER} may be able to handle LISP and Miranda and other strange 
  38. languages; the experiment hasn't been tried.
  39. The unusual lexical requirements of FORTRAN are probably beyond it, at 
  40. least until the lexical analysis is modernized.)
  41.  
  42. # The outline of the program is fairly straightforward.
  43. We use |exitcode| throughout to monitor error status.
  44. If we were more Knuthlike, we would have a |history| variable with values of 
  45. |spotless|, and so on.
  46. This will have to wait until we get macros back into \.{TANGLE}.
  47.  
  48. We put the pattern-action statement for productions last, because in
  49. case of a conflict like \.{token~-->~...}, we want the interpretation
  50. as {\tt token} to win out over
  51. the interpretation as a prodution.
  52.  
  53. #u#1
  54. BEGIN {
  55. #<Set initial values#>
  56. exitcode=0
  57. }
  58. #@
  59. #<Ignore comments and blank lines#>
  60. #<Pattern-action statements#>
  61. #<Production pattern-action statement#>
  62. #<Default action for patterns we don't recognize#>
  63. #@
  64. END {
  65. #<Write out all of the WEB code#>
  66. print "Writing out lists" > logfile
  67. #<Write lists of everything#>
  68. #<Write statistics for this \.{SPIDER}#>
  69. #<Check for errors at the very end#>
  70. if (exitcode != 0) {
  71.     exit exitcode
  72.     }
  73. }
  74.  
  75. # There are a couple of actions we may want to perform with just
  76. about any command.
  77. If a command fails, we move on to the next, but we remember the fault
  78. so we can complain at the end.
  79. #<Punt this command#>=
  80.     exitcode=-1
  81.     next
  82.  
  83.  
  84. # Throughout \.{SPIDER} we always use the variable |i| to step through the 
  85. fields of a  command, so that |$i| is always the next field of interest.
  86. When we thinik we have finished a command, 
  87. we will always want to check to make sure there are no unexamined
  88. fields left over.
  89. #<Check that we used everything#>=
  90.     if (i<=NF) {
  91.         print "Error: leftover fields", $i, "... on line", NR
  92.         #<Punt...#>
  93.         }
  94.  
  95.  
  96. # To \.{SPIDER}, any line beginning with |"## "| is a comment.
  97. \.{SPIDER} also ignores blank lines.
  98. #<Ignore comments...#>=
  99. #=/^##|^ *$/#> {
  100.     ## comments, blank lines
  101.     print $0 > logfile
  102.     next
  103.     }
  104.  
  105. # But, if \.{SPIDER} encounters a line we don't recognize, it complains.
  106. #<Default act...#>=
  107.     {
  108.     print "Warning: I don't know what to do with this line:"
  109.     print "        ", $0
  110.     print "Warning: I don't know what to do with this line:" > logfile
  111.     print "        ", $0 > logfile
  112. }
  113.  
  114.  
  115. #*1Files written by {\tt SPIDER}.
  116.  {\tt SPIDER} writes output to a number of files.
  117. Because 4.3~BSD AWK is limited in the number of files it can write at
  118. one time, there is substantial overlap.
  119. Here is a table:
  120. \noindent\halign{\vrule height10pt depth3.5pt width0pt
  121. \it##\hfil\tabskip=1em&\tt##\hfil&\tabskip=0pt
  122.     \hsize=4in\vtop{\noindent##\strut\par}\cr
  123. \noalign{\medskip}
  124. \bf Internal Name&\bf External Name&\omit\bf Description\hfil\cr
  125. \noalign{\smallskip}
  126. categoryfile&names.unsorted&
  127. names of categories, to be checked for duplicates by {\tt nodups.awk}
  128. \cr
  129. cycles&cycle.test&
  130. potential cycles, to be checked by {\tt cycle.web}
  131. \cr
  132. grammarfile&grammar.web&
  133. grammar; included in {\tt weave.web}
  134. \cr
  135. ilkfile&names.unsorted&
  136. names of ilks, to be checked for duplicates by {\tt nodups.awk}
  137. \cr
  138. logfile&spider.slog&
  139. log file, to be consulted when things go wrong
  140. \cr
  141. macrofile&*web.tex&
  142. language specific macro file, {\tt\string\input} by all \TeX{} 
  143.     files created by {\tt weave.web}
  144. \cr
  145. productions&productions.list&
  146. list of the productions (numbered) used in debugging \.{WEAVE}
  147. \cr
  148. reserved&scraps.web&
  149. code for converting the reserved word to scraps.
  150. {\tt scraps.web} is included by {\tt weave.web}
  151. \cr
  152. scrapfile&scraps.web&
  153. code for converting tokens to scraps.
  154. {\tt scraps.web} is included by {\tt weave.web}
  155. \cr
  156. tlang&outtoks.web&
  157. Information about what language we're webbing.
  158. {\tt outtoks.web} is included by {\tt tangle.web}.
  159. \cr
  160. tokennamefile&names.unsorted&
  161. list of names of all the tokens, to be checked by {\tt nodups.awk}
  162. \cr
  163. translationfile&trans\_keys.unsorted&
  164. list of names of all the translation keywords.
  165. Checked for duplicates by {\tt nodups.awk}, and also for recognizability
  166. by {\tt transcheck.awk}.
  167. \cr
  168. ttokfile&outtoks.web&
  169. This is the tokenization code for {\tt TANGLE}.
  170. \cr
  171. wlang&scraps.web&
  172. Information about what language we're webbing,
  173. {\tt scraps.web} is included by {\tt weave.web}.
  174. \cr
  175. }
  176.  
  177. # Every action writes information to a log file.
  178. This log file can be used to check up on what happened.
  179. #<Set initial...#>=
  180.     logfile = "spider.slog"
  181.  
  182. # Here we write the names of the key words used in translations.
  183. #<Set initi...#>=
  184.     translationfile = "trans_keys.unsorted"
  185.  
  186. #  We write tokens out to two files: |scrapfile| for \.{WEAVE}, and
  187. |ttokfile| for \.{TANGLE}.
  188. #<Set init...#>=
  189.     scrapfile = "scraps.web"
  190.     print "@*Scrap code generated by {\\tt SPIDER}." > scrapfile
  191.     ttokfile = "outtoks.web"
  192.     print "@*Token code generated by {\\tt SPIDER}." > scrapfile
  193. # The reserved word stuff gets a file of its own, or it would in an ideal 
  194. world.
  195. #<Set init...#>=
  196.     reserved = "scraps.web" ## use same file; not enough files
  197.  
  198. # We'll also end up writing a list of token names, for name checking
  199. purposes.
  200. #<Set initial...#>=
  201.     tokennamefile = "names.unsorted" ## cut down on number of output files
  202. # We also write out every ilk, so we'll be able to look for name
  203. clashes with translations and so on.
  204. #<Set init...#>=
  205.     ilkfile = "names.unsorted" ## cut down  on number of output files
  206. # We also write all the category names to a separate file, so we can
  207. check for duplicates later.
  208. #<Set init...#>=
  209.     categoryfile = "names.unsorted" ## cut down  on number of output files
  210. # We use a special file to write grammar information:
  211. #<Set init...#>=
  212.     grammarfile = "grammar.web"
  213.     print "@*Grammar code generated by {\\tt SPIDER}." > grammarfile
  214. # We use the language information to write banners and macro information.
  215. We combine this with other  stuff because AWK can't handle more than
  216. 10 output files.
  217. #<Set initial...#>=
  218.     tlang = "outtoks.web" ## same as ttokfile
  219.     wlang = "scraps.web" ## same as scrapfile
  220.  
  221. # We will write a list of the successfully parsed productions to a
  222. separate file.
  223. #<Set init...#>=
  224.     productions = "productions.list"
  225.  
  226. # These productions will get fed to {\tt cycle.awk}, which looks for cycles.
  227. #<Set initial...#>=
  228.     cycles = "cycle.test"
  229.  
  230.  
  231.  
  232. #*Processing translations.
  233. Translations tell \.{WEAVE} or \.{TANGLE} what to write out in
  234. particular circumstances (e.g.~after scanning a particular token, or 
  235. when firing some production).
  236. They are described at some length in the ``\.{SPIDER} User's Guide.''
  237. Translations are enclosed in angle brackets and separated by dashes.
  238. They can contain key words, digits, the self marker~`{\tt*}',
  239. or quoted strings.
  240. Since we can't put a space or dash into strings, we allow the use of
  241. key words |space| and |dash| to stand for those symbols.
  242. #^space#>
  243. #^dash#>
  244.  
  245. Other key words are interpreted by \.{WEAVE} as prettyprinting instructions:
  246.  
  247. \yskip\hang |break_space| denotes an optional line break or an en space;
  248.  
  249. \yskip\hang |force| denotes a line break;
  250.  
  251. \yskip\hang |big_force| denotes a line break with additional vertical space;
  252.  
  253. \yskip\hang |opt| denotes an optional line break (with the continuation
  254. line indented two ems with respect to the normal starting position)---this
  255. code is followed by an integer |n|, and the break will occur with penalty
  256. $10n$;
  257.  
  258. \yskip\hang |backup| denotes a backspace of one em;
  259.  
  260. \yskip\hang |cancel| obliterates any |break_space| or |force| or |big_force|
  261. tokens that immediately precede or follow it and also cancels any
  262. |backup| tokens that follow it;
  263.  
  264. \yskip\hang |indent| causes future lines to be indented one more em;
  265.  
  266. \yskip\hang |outdent| causes future lines to be indented one less em.
  267.  
  268. \yskip\hang |math_rel|, |math_bin|, and |math_op| will be translated into
  269. \.{\\mathrel\{}, \.{\\mathbin\{}, and  \.{\\mathop\{}, respectively.
  270.  
  271.  
  272. \yskip\noindent All of these tokens are removed from the \TeX\ output that
  273. comes from programming language text between \pb\ signs; |break_space| 
  274. and |force| and 
  275. |big_force| become single spaces in this mode. 
  276. %The translation of other
  277. %program texts results in \TeX\ 
  278. %control sequences \.{\\1}, \.{\\2},
  279. %\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, 
  280. %\.{\\7} corresponding respectively to
  281. %|indent|, |outdent|, |opt|, 
  282. %|backup|, |break_space|, |force|, and
  283. %|big_force|. However, 
  284. A sequence of consecutive `\.\ ', |break_space|,
  285. |force|, and/or |big_force| tokens is first replaced by a single token
  286. (the maximum of the given ones).
  287.  
  288. %Some Other control sequences in the \TeX\ output will be
  289. %`\.{\\\\\{}$\,\ldots\,$\.\}' 
  290. %surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding
  291. %reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings,
  292. %`\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and
  293. %`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding module names, where
  294. %|n| is the module number.
  295.  
  296. # We write out the names of all the key words used translations, 
  297. so we can check that
  298. \.{WEAVE} can be expected to recognize them.
  299. This helps us catch the problem early if a translation given is
  300. not one of the above
  301. (as opposed to, say, having the C~compiler fail to compile \.{WEAVE}).
  302. #<Write lists...#>=
  303.     for (t in translation_keywords) {
  304.         print t > translationfile
  305.         }
  306.  
  307. # #<Write stat...#>=
  308. for (t in translation_keywords) {
  309.     num_of_translation_keywords++
  310.     }
  311. printf "You used %d translation keywords.\n", \
  312.     num_of_translation_keywords > logfile
  313. printf "You used %d translation keywords.\n", num_of_translation_keywords
  314.  
  315. # If the macro facility worked right, 
  316. we would use the following patterns to recognize items as they occur:
  317. #d cat_pattern = #=/[a-zA-Z][a-zA-Z_]*/#>
  318. #d trans_pattern = #=/<(([0-9]|[a-zA-Z][a-zA-Z_]*|"([^"]*\\")*[^"]*"|\*)-)*#>#&
  319.                       #=([0-9]|[a-zA-Z][a-zA-Z_]*|"([^"]*\\")*[^"]*"|\*)>/#>
  320.  
  321. # Here's where we swallow a translation and spit out the \.{WEAVE} code
  322. to handle that translation.
  323. Since AWK has no functions, we define this as a module.
  324.  
  325. When we're appending a key word {\it in the process of creating a 
  326. scrap from a token}, we use |small_app| in preference to |app|, 
  327. because |app|'s cleverness about mathness and dollar signs only works when 
  328. reducing existing scraps, not when creating scraps from tokens.
  329. We'll expect the variable |append_keyword| to be set to either 
  330. |"small_app"| or |"app"|.
  331.  
  332.  
  333. #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
  334. |outstring|, using |selfstring| as translation of |"<*>"|#>=
  335. temp = substr(transstring,2,length(transstring)-2) ## kills awk bug
  336. trcnt = split(temp,trtok,"-")
  337. outstring = ""
  338. for (tridx=1;tridx<=trcnt;tridx++) {
  339.     alternate=trtok[tridx]
  340.     #<Convert |"space"| and |"dash"|#>
  341.     if (alternate ~ #=/^[0-9]$/#>) { ## digit
  342.         temp = sprintf("\tapp_str(\"%s\");\n",alternate)
  343.         outstring=outstring temp
  344.     } else if (alternate ~ #=/^[a-zA-Z_]+$/#>) { ## key word
  345.         translation_keywords[alternate]=1 ## remember
  346.         temp = sprintf("\t%s(%s);\n",append_keyword,alternate) 
  347. ##Call |app| or |small_app| depending whether we're reducing or creating scraps
  348.         outstring=outstring temp
  349.     } else if (alternate ~ #=/^\"([^"]*\\\")*[^"]*\"$/#>) { ## string
  350.         temp = sprintf("\tapp_str(%s);\n",alternate)
  351.         outstring=outstring temp
  352.     } else if (alternate ~ #=/^\*$/#>) { ## self marker
  353.         #<If |selfstring==""|, complain loudly#>
  354.         outstring=outstring selfstring
  355.     } else { 
  356.         print "Bogus translation", wherestring
  357.         exitcode = -1
  358.     }
  359. }
  360.  
  361. # Here we convert the key words |space| and |dash| to strings.
  362. We quote the strings, to be sure that they are handled by the string
  363. mechanism.
  364. #<Convert |"space"|...#>=
  365.     if (alternate=="space") {
  366.         alternate="\" \""
  367.     } else if (alternate=="dash") {
  368.         alternate="\"-\""
  369.     }
  370.  
  371.  
  372. # There are some places (notably in productions) where the translation
  373. |"<*>"| makes no sense.
  374. In this case the caller sets |selfstring=""|, and we complain.
  375. #<If |selfstring==""|, complain...#>=
  376. if (selfstring=="") {
  377.     print "Translation \"<*>\" makes no sense", wherestring
  378.     exitcode = -1
  379.     }
  380.  
  381. # There are times when we may want to convert a translation directly
  382. into a quoted string, usually for \.{TANGLE}'s benefit.
  383. Here, the only things allowed are quoted strings and |space| and |dash|.
  384. We peel off quote marks and concatenate things together, and then we
  385. put the quote marks back on at the end.
  386. #<Convert restricted translation in |transstring| 
  387.     to quoted string in |outstring|#>=
  388. temp = substr(transstring,2,length(transstring)-2) ## kills awk bug
  389. trcnt = split(temp,trtok,"-")
  390. outstring = ""
  391. for (tridx=1;tridx<=trcnt;tridx++) {
  392.     alternate=trtok[tridx]
  393.     #<Convert |"space"| and |"dash"|#>
  394.     if (alternate ~ #=/^[0-9]$/#>) { ## digit
  395.         print "Digit not allowed in restricted translation", wherestring
  396.         exitcode = -1
  397.     } else if (alternate ~ #=/^[a-zA-Z_]+$/#>) { ## key word
  398.         print "Key word not allowed in restricted translation", wherestring
  399.         exitcode = -1
  400.     } else if (alternate ~ #=/^\"([^"]*\\\")*[^"]*\"$/#>) { ## string
  401.         temp = substr(alternate,2,length(alternate)-2) ## strip quotes
  402.         outstring=outstring temp
  403.     } else if (alternate ~ #=/^\*$/#>) { ## self marker
  404.         print "<*> not allowed in restricted translation", wherestring
  405.         exitcode = -1
  406.     } else { 
  407.         print "Bogus restricted translation", wherestring
  408.         exitcode = -1
  409.     }
  410. }
  411. outstring = "\"" outstring "\"" ## put quotes back on |outstring|
  412.  
  413.  
  414.  
  415. #*Tokens.
  416.  
  417. Tokens are pretty complicated.
  418. Each token has a string by which we recognize it in the input.
  419. This string is what immediately follows the |token| command.
  420. Then, there's another string that tells \.{TANGLE} how to write out 
  421. the token.
  422. Finally, it has a category and a translation (so we can make a scrap out
  423. of it), and a mathness (to tell us whether it has to be in math
  424. mode, horizontal mode, or either).
  425. The 
  426. \.{translation} and \.{mathness} have defaults.
  427.  
  428. #*2Scanning for token descriptions.
  429. This module is used everywhere we must scan a line for token descriptions.
  430. #<Scan this line from |start_place| to finish, looking for
  431.         \.{translation}$\ldots$ and putting results in
  432.         |this_translation|$\ldots$#>=
  433. for (i=start_place;i<NF;) {
  434.      if ($i=="tangleto") { ## for \.{TANGLE}
  435.         i++
  436.         this_tangleto=$i
  437.         i++
  438.     } else if ($i=="translation") { ## for \.{WEAVE}
  439.         i++
  440.         this_translation=$i
  441.         i++
  442.     } else if ($i=="mathness") { ## for \.{WEAVE}
  443.         i++
  444.         this_mathness=$i
  445.         i++
  446.     } else if ($i=="category") { ## for \.{WEAVE}
  447.         i++
  448.         this_category=$i
  449.         categories[$i]=1
  450.         i++
  451.     } else if ($i=="name") { ## for debugging
  452.         i++
  453.         this_name="SP_" $i ##OK, so it's hacking...
  454.         i++
  455.     } else {
  456.         print "Error: unrecognized token description", $i, "on line", NR
  457.         #<Punt...#>
  458.     }
  459. }
  460. #<Check that we used everything#>
  461.  
  462. # We check for the presence or absence of certain empty strings after
  463. scanning.
  464. #<Make sure |this_name| is empty#>=
  465. if (this_name != "") {
  466.     print "Error: name doesn't apply on line", NR
  467.     #<Punt...#>
  468.     }
  469. # #<Make sure |this_tangleto| is empty#>=
  470. if (this_tangleto != "") {
  471.     print "Error: tangleto doesn't apply on line", NR
  472.     #<Punt...#>
  473.     }
  474. # #<Make sure |this_category| is empty#>=
  475. if (this_category != "") {
  476.     print "Error: category doesn't apply on line", NR
  477.     #<Punt...#>
  478.     }
  479. # #<Make sure |this_translation| is empty#>=
  480. if (this_translation != "") {
  481.     print "Error: translation doesn't apply on line", NR
  482.     #<Punt...#>
  483.     }
  484. # #<Make sure |this_category| is not empty#>=
  485. if (this_category == "") {
  486.     print "Error: you must give a category on line", NR
  487.     #<Punt...#>
  488.     }
  489.  
  490. #*1Setting the default token descriptions.
  491. \.{SPIDER} maintains default information about {\em mathness}
  492. and {\em translation}, so these can be omitted from token descriptions.
  493. We can change the operative defaults at any time by using a
  494. |"default"| command.
  495. It too, scans for keywords, using the standard scanning module
  496. #<Pattern-action...#>=
  497. #=/^default /#> {
  498.     print "Setting defaults..." > logfile
  499.     start_place=2
  500.     #<Set |this_mathness| etcetera to the defaults and those with no
  501.         defaults to |""|#>
  502.     #<Scan this line from |start_place| to finish, looking for
  503.         \.{translation}$\ldots$ and putting results in
  504.         |this_translation|$\ldots$#> 
  505.     #<Make sure |this_name| is empty#>
  506.     #<Make sure |this_category| is empty#>
  507.     default_translation=this_translation
  508.     default_mathness=this_mathness
  509. #@    print "\tdefault translation is", default_translation > logfile
  510.     print "\tdefault mathness is", default_mathness > logfile
  511. #@    next    
  512. }
  513.  
  514. # Normally, we will set all quantities to the defaults before scanning:
  515. #<Set |this_mathness| etcetera to the defaults and those with no
  516. defaults to |""|#>=
  517.     this_translation=default_translation
  518.     this_mathness=default_mathness
  519.     this_name=""
  520.     this_category=""
  521.     this_tangleto=""
  522. # When \.{SPIDER} starts up, the defaults are already set:
  523. #<Set initi...#>=
  524.     default_translation="<*>"
  525.     default_mathness="maybe"
  526.  
  527.  
  528. #*1Recognizing token designators.
  529. Let's begin by discussing the way \.{WEAVE} and \.{TANGLE} represent
  530. tokens internally.
  531.  
  532. \.{WEAVE} and \.{TANGLE} process tokens in a two-step process.
  533. Both read the token from the input using |get_next|, which returns a
  534. unique eight-bit number representing the token.
  535. Generally printable ASCII characters represent themselves, and other
  536. tokens get numbers in the unprintable range.
  537. \.{TANGLE} assigns ranges to some tokens ahead of time: |string| is 2,
  538. |identifier| is #'202, and so on.
  539. Tokens that we introduce to \.{TANGLE} must have numbers between 
  540. #'13 and #'37 inclusive.
  541.  
  542. Rather than work with eight-bit numbers themselves, we use names for
  543. the tokens.
  544. This makes \.{WEAVE} and \.{TANGLE} easier to debug when things go wrong.
  545.  
  546. In \.{WEAVE}, the category, mathness, and translation are all
  547. attached to a scrap based on the eight-bit number returned by
  548. |get_next|, and this is done at a later time.
  549.  
  550. In \.{TANGLE}, characters are written to the output file(s) based on
  551. the token code, which can be either eight bits for simple tokens or
  552. sixteen for identifiers and things.
  553.  
  554. Our mission in this section will be to read in all the token
  555. information from the {\tt token} command, 
  556. and to create the names and numbers used by \.{WEAVE} and \.{TANGLE}
  557. to represent the tokens.
  558. In the next section we will
  559. write the code that processes the tokens for both \.{WEAVE} and
  560. \.{TANGLE} (lexical analysis in 
  561. |get_next|, and subsequent processing elsewhere).
  562. You will pardon us if things get a bit tedious.
  563.  
  564. # The {\tt token} command is used to specify tokens that are not
  565. reserved words.
  566. Reserved word tokens get special treatment all their own.
  567.  
  568. #<Pattern...#>=
  569. #=/^token /#> {
  570.     print "Token", $2 > logfile
  571.     if ($2=="identifier") {
  572.         #<Process identifier token description#>
  573.     } else    if ($2=="number") {
  574.         #<Process numeric token description#>
  575.     } else    if ($2=="newline") {
  576.         #<Process newline token description#>
  577.     } else    if ($2=="pseudo_semi") {
  578.         #<Process |pseudo_semi| token description#>
  579.     } else if ($2 ~ #=/[a-zA-ZA-Z0-9]+/#>) { 
  580.         ## we recognize no other names
  581.         print "Error: unknown token species:", $2
  582.         #<Punt this command#>
  583.     } else { 
  584.         #<Process a non-alphanumeric token description#>
  585.     }
  586.     categories[this_category]=1 ## is this right?
  587. #^questions#>
  588.     next
  589. }
  590.  
  591. # Identifiers, numbers (and string literals), newlines, and the special
  592. token \.{pseduo\_semi} are predefined.
  593. #<Process identifier token description#>=
  594.     #<Set |this_mathness| etcetera to the defaults and those with no
  595.         defaults to |""| #>
  596.     this_translation=""
  597.     start_place=3
  598.     #<Scan this line from |start_place| to finish, looking for
  599.         \.{translation}$\ldots$ and putting results in
  600.         |this_translation|$\ldots$#> 
  601.     #<Make sure |this_name| is empty#>
  602.     #<Make sure |this_tangleto| is empty#>
  603.     #<Make sure |this_category| is not empty#>
  604.     #<Make sure |this_translation| is empty#>
  605.     id_category=this_category
  606.     id_mathness=this_mathness
  607.  
  608. # We have yet to implement a separate procedure for numerics and strings!
  609. #<Process numeric token description#>=
  610.     print "Warning: numeric constants and strings are",\
  611.         "identified in this WEAVE."
  612.     print "Warning: numeric constants and strings are",\
  613.         "identified in this WEAVE." > logfile
  614.     #<Set |this_mathness| etcetera to the defaults and those with no
  615.         defaults to |""| #>
  616.     this_translation=""
  617.     start_place=3
  618.     #<Scan this line from |start_place| to finish, looking for
  619.         \.{translation}$\ldots$ and putting results in
  620.         |this_translation|$\ldots$#> 
  621.     #<Make sure |this_name| is empty#>
  622.     #<Make sure |this_tangleto| is empty#>
  623.     #<Make sure |this_category| is not empty#>
  624.     #<Make sure |this_translation| is empty#>
  625.     number_category=this_category
  626.     number_mathness=this_mathness
  627.  
  628. #
  629. #<Process newline token description#>=
  630.     #<Set |this_mathness| etcetera to the defaults and those with no
  631.         defaults to |""| #>
  632.     start_place=3
  633.     #<Scan this line from |start_place| to finish, looking for
  634.         \.{translation}$\ldots$ and putting results in
  635.         |this_translation|$\ldots$#> 
  636.     #<Make sure |this_name| is empty#>
  637.     #<Make sure |this_tangleto| is empty#>
  638.     #<Make sure |this_category| is not empty#>
  639.     newline_category=this_category
  640.     newline_mathness=this_mathness
  641.     newline_translation=this_translation
  642.  
  643. #
  644. #<Process |pseudo_semi| token description#>=
  645.     #<Set |this_mathness| etcetera to the defaults and those with no
  646.         defaults to |""| #>
  647.     start_place=3
  648.     #<Scan this line from |start_place| to finish, looking for
  649.         \.{translation}$\ldots$ and putting results in
  650.         |this_translation|$\ldots$#> 
  651.     #<Make sure |this_name| is empty#>
  652.     #<Make sure |this_tangleto| is empty#>
  653.     #<Make sure |this_category| is not empty#>
  654.     pseudo_semi_category=this_category
  655.     pseudo_semi_mathness=this_mathness
  656.     pseudo_semi_translation=this_translation
  657.  
  658. # Here is where things get a bit more interesting; we have to
  659. consider all the other (non-reserved-word) tokens, and find a way to
  660. convert them to \.{WEAVE} and \.{TANGLE}'s internal form.
  661. We take single characters straight, except for those that must be
  662. escaped one way or another.
  663. For multicharacter tokens, we have to invent a name and a number, 
  664. which process we will describe below.
  665.  
  666.  
  667. Tokens have a zillion attributes:  not just category, translation, and
  668. their friends, but things like internal representations, the length of
  669. the input string, you name it.
  670.  
  671. We remember the length of the longest token in the system, 
  672. because when we go to
  673. recognize tokens we will look for the longest first and then on down.
  674. We maintain that length at the very end here.
  675. #<Process a non-alphanumeric token description#>=
  676.     this_string=$2
  677.     #<Translate |"{space}"| to |" "| in |this_string|#>
  678.     $2 = this_string
  679.     #<Set |tokenname[$2]|, |tokenlength[$2]|, and, for long
  680.         tokens, set |tokentest[$2]| and |tokennumbers[$2]|#>
  681.     if (tokens[$2]!="") {
  682.         print "Warning: token", $2, "defined twice"
  683.         }
  684.     tokens[$2]=1 ## remember this token
  685.     #<Set attributes of token |$2|#>
  686.     #<Make sure token |$2| has a number if it needs one#>
  687.     #<Update record of maximum token length#>
  688.  
  689.  
  690. # This code represents and undocumented feature.
  691. We should replace it by allowing restricted translations
  692. in |$2|, the then documenting it.
  693. When doing this, we'll have to match the full |trans_pattern|
  694. in all its glory; A mere |#=/<.*>/#>| won't do.
  695.  
  696. #<Translate |"{space}"| to |" "| in |this_string|#>=
  697. old_string = this_string
  698. this_string = ""
  699. ## Invariant: |this_string old_string| corresponds to result, and 
  700. ## |"{space}"| is translated in |this_string| but not |old_string|
  701. idx = index(old_string,"{space}")
  702. while (idx != 0) {
  703.     temp =substr(old_string,1,idx-1)
  704.     this_string = this_string temp " "
  705.     old_string = substr(old_string,idx+7)
  706.     idx = index(old_string,"{space}")
  707. }
  708. this_string = this_string old_string
  709.  
  710. # Tokens need an internal eight-bit representation.
  711. For single characters (which are assumed to be printable), we use
  712. the ASCII code as the internal representation.
  713. Multicharacter tokens will be assigned a name and a number.
  714. (The names may be specified by the user or generated by \.{SPIDER}.)
  715. Unfortunately the numbers for \.{WEAVE} and \.{TANGLE} have to be
  716. different (the reasons will only depress you).
  717. We assign \.{WEAVE} numbers by starting numbering from |highesttoken|, and
  718. working our way down.
  719. At the moment |hisghesttoken==200|, and I can't remember whether 200 is
  720. a ``magic number'' or not, so you'd better assume that it is.
  721. We get the tpoken numbers for \.{TANGLE} by subtracting an offset,
  722. as you'll see later.
  723. #<Set initial...#>=
  724.     highesttoken=200 ## highest numbered token
  725.     tokennumber=highesttoken
  726.  
  727. # At the end we check to make sure we haven't used up too many numbers
  728. for tokens.
  729. \.{WEAVE} token numbers must be |>=127|.
  730. #<Check for errors at the...#>=
  731. if (tokennumber<127) {
  732.     print "Error: too many token names for WEAVE --- over by",\
  733.         127-tokennumber
  734.     exitcode=-1
  735.     }
  736. # \.{TANGLE} tokens must be between #'13 and #'37 inclusive.
  737. We add three to the number because \.{TANGLE} has special definitions for
  738. the three tokens taken off the top.
  739. #<Check for errors...#>=
  740. if (highesttoken-tokennumber > #'37-(#'13-1)+3) { \
  741.         ## number of tokens in |#'13|--|#'37|, plus 3
  742.     print "Error: too many token names for TANGLE --- over by",\
  743.         highesttoken-tokennumber - (#'37-(#'13-1)+3)
  744.     exitcode=-1
  745.     }
  746.     
  747.  
  748.  
  749. # The token name is what \.{WEAVE} and \.{TANGLE} will use internally
  750. to refer to the token's internal representation as an eight-bit code.
  751. We use names instead of using the numbers directly in the vague hope that
  752. it will make \.{WEAVE} and \.{TANGLE} easier to debug when something goes
  753. wrong.
  754. For multi-character tokens, the name will be a \.{WEB} macro that is defined
  755. to be equal to the token's eight-bit code.
  756. If the token is a single character, its ``name'' will be that character,
  757. quoted with single quotes.
  758. The single-character tokens \.{@}, \.{\\}, and \.{'} require special
  759. handling, since they have to be escaped in some way to be quoted.
  760.  
  761. Once we've computed the name, we put it in  |tokenname[$2]|.
  762. #<Set |tokenname[$2]|, |tokenlength[$2]|, and, for long
  763.     tokens, set |tokentest[$2]| and |tokennumbers[$2]|#>=
  764.     if ($2=="@") {
  765.         $2="@@"
  766.         tokenname[$2]="'@@'"
  767.         tokenlength[$2]=1
  768.     } else if ($2=="'" || $2 == "\\") {
  769.         $2="\\" $2
  770.         tokenname[$2]="'" $2 "'"
  771.         tokenlength[$2]=1
  772.     } else if (length($2)>1) {
  773.         #<Handle multicharacter tokens#>
  774.     } else {
  775.         temp = sprintf("'%s'", $2)
  776.         tokenname[$2] = temp
  777.         tokenlength[$2]=1
  778.     }
  779.  
  780. # For the long tokens, we generate a name by which we'll refer to the
  781. token.
  782. That name will actually be defined to be a number, which we'll take to
  783. be the current value of |tokennumber|.
  784. We'll write in |tokentest[$2]| the C~code used to recognize that token,
  785. and in |tokenlength[$2]| we'll leave that token's length.
  786. (The length is used both to find long tokens before short ones, and
  787. to avoid finding long ``tokens'' that 
  788. actually go beyond the end of the line.)
  789. #<Handle multicharacter tokens#>=
  790.     tokenname[$2]="SP_gen_token_" tokennumber
  791.     tokennumbers[$2]=tokennumber
  792.     tokennumber--
  793.     ## figure out how to recognize the token
  794.     temp = sprintf( "strncmp(\"%s\",loc-1,%d)==0", $2, length($2))
  795.     tokentest[$2]=temp
  796.     tokenlength[$2]=length($2)
  797.  
  798.  
  799. # The setting of attributes is as for all tokens:
  800. #<Set attributes of token |$2|#>=
  801.     #<Set |this_mathness| etcetera to the defaults and those with no
  802.         defaults to |""| #>
  803.     this_name=tokenname[$2]
  804.     start_place=3
  805.     #<Scan this line from |start_place| to finish, looking for
  806.         \.{translation}$\ldots$ and putting results in
  807.         |this_translation|$\ldots$#> 
  808.     #<Make sure |this_category| is not empty#>
  809.     tokencategory[$2]=this_category
  810.     tokenmathness[$2]=this_mathness
  811.     tokentranslation[$2]=this_translation
  812.     tokenname[$2]=this_name
  813.     tokentangleto[$2]=this_tangleto
  814.  
  815. # We have to remember the length of the longest token so we can
  816. recognize long tokens before short ones.
  817. #<Update record of maximum token length#>=
  818.     temp = tokenlength[$2]
  819.     if (temp > maxtokenlength) {
  820.         maxtokenlength=temp
  821.         }
  822.  
  823. # We're paranoid.
  824. #<Make sure token |$2| has a number if it needs one#>=
  825.     if (tokenlength[$2]>1 && tokennumbers[$2]=="") {
  826.         print "This can't happen: token", $2, "is long", \
  827.             "but has no number"
  828.         exitcode = -1
  829.     }
  830.  
  831.  
  832. #*1Writing {\tt WEB}'s lexical analysis code.
  833. The token recognition problem is the same for \.{WEAVE} and
  834. \.{TANGLE}. 
  835. Both have routines called |get_next| that recognize the tokens on
  836. input.
  837. Most of |get_next| is prefabricated 
  838. (and the same in both \.{WEAVE} and \.{TANGLE}),
  839. but we have to put in the part that recognizes multi-character
  840. non-alphanumeric tokens.
  841.  
  842. We write the same code to both \.{WEAVE} and \.{TANGLE}.
  843. #<Write out...#>=
  844.     tempfile = scrapfile
  845.     #<Write token recognition code to |tempfile|#>
  846.     tempfile = ttokfile
  847.     #<Write token recognition code to |tempfile|#>
  848.     
  849. # This is how we do it.
  850. #<Write token recognition code to |tempfile|#>=
  851. print "@ Here we input tokens of more than one character" > tempfile
  852. print "@<Compress two-symbol operator@>=" > tempfile
  853. #<Look for multicharacter tokens, starting with the longest,
  854.     and working down#>
  855.  
  856. # We look for long tokens, then shorter, and so on.
  857. We have to make sure we don't look beyond the end of a line.
  858. #<Look for multicharacter tokens, starting with the longest,
  859.         and working down#>=
  860.     for (len=maxtokenlength; len>=2; len--) {
  861.         printf "if (loc+%d<=limit) {\n", len-1 > tempfile
  862.         #<Check for tokens in |tokentest| of length |len|#>
  863.         printf "\t}\n" > tempfile
  864.         }
  865.     #<Make sure there are no tokens of length 1 in |tokentest|#> 
  866.  
  867. # #<Check for tokens in |tokentest| of length |len|#>=
  868. notfirst=0
  869. for (t in tokentest) {
  870.     if (tokenlength[t]==len) {
  871.         printf "\t" > tempfile
  872.         if (notfirst==1) {
  873.             printf "else " > tempfile
  874.             }
  875.         notfirst=1
  876.         printf "if (%s) {\n", tokentest[t] > tempfile
  877.         printf "\t\tloc += %d;\n", len-1 > tempfile
  878.         printf "\t\treturn %s;\n\t\t}\n", tokenname[t] > tempfile
  879.         }
  880.     }
  881.  
  882.  
  883. # #<Make sure there are no tokens of length 1 in |tokentest|#>=
  884. for (t in tokentest) {
  885.     if (tokenlength[t]==1) {
  886.         print "This can't happen: token", t, "is of length 1 but", \
  887.                 "it has a test"
  888.         exitcode=-1
  889.     }
  890. }
  891.  
  892.  
  893.  
  894. #*1Writing out {\tt WEAVE}'s token-to-scrap code.
  895. Here is where we write the code that converts an already-recognized
  896. token (from |get_next|) into a scrap.
  897. There are several different kinds of tokens, and each requires a
  898. slightly different treatment.
  899. Will write out the code for the different species one at a time.
  900. #<Write out all...#>=
  901.     print "Writing out predefined scraps" > logfile
  902.     #<Write code for identifier scrap#>
  903.     #<Write code for string or constant scrap#>
  904.     #<Write code for newline scrap#>
  905.     #<Write code for |pseudo_semi| scrap#>
  906.     print "Writing out token scraps" > logfile
  907.     #<Write code for ordinary token scraps#>
  908.  
  909.  
  910.  
  911.  
  912. # This is how we write out the information for the identifier.
  913. #<Write code for identifier scrap#>=
  914.     if (id_category != "") {
  915.     print "@ @<Append an identifier scrap@>=" > scrapfile
  916.     print "p=id_lookup(id_first, id_loc,normal);" > scrapfile
  917.     print "if (p->ilk==normal) {" > scrapfile
  918.     print "  small_app(id_flag+p-name_dir);" > scrapfile
  919.     printf "  app_scrap(SP_%s,%s_math);", \
  920.             id_category, id_mathness > scrapfile
  921.     appended[id_category]=1
  922.     print " /* not a reserved word */" > scrapfile
  923.     print "}" > scrapfile
  924.     print "else if reserved(p) {" > scrapfile
  925.     print "@<Decide on reserved word scraps@>;" > scrapfile
  926.     print "}" > scrapfile
  927.     print "else {" > scrapfile
  928.     print " err_print(\"! Identifier with unmentioned ilk\");" > scrapfile
  929.     print "@.Identifier with unmentioned ilk@>" > scrapfile
  930.     print "}" > scrapfile
  931.     } else {
  932.         print "Error: I don't know what to do with an identifier"
  933.         print "       Please give me a \"token identifier ...\""
  934.         exitcode = -1
  935.         }
  936.  
  937. # We hold the name |"identifier"|, and we reserve a number for
  938. identifiers.
  939. #<Set initial...#>=
  940.     tokennumbers["identifier"]=tokennumber; tokennumber--
  941.     tokenname["identifier"]="identifier"
  942.  
  943. # This is how we write out the string or constant scrap, at the end.
  944. #<Write code for string or constant scrap#>=
  945. print "Warning: TeX strings have the same category as ", \
  946.     "numeric constants in this WEAVE."
  947. print "Warning: TeX strings have the same category as ", \
  948.     "numeric constants in this WEAVE." > logfile
  949. if (number_category != "") {
  950. print "@ For some reason strings, constants,",\
  951.     " and \TeX\ strings are identified." > scrapfile
  952. print "That has to be fixed." > scrapfile
  953. print "@<Do the |app_scrap| for a string or constant@>=" > scrapfile
  954. printf "app_scrap(SP_%s,%s_math);\n", number_category,\
  955.     number_mathness > scrapfile
  956. appended[number_category]=1
  957. } else {
  958.     print "Error: I don't know what to do with a numeric constant"
  959.     print "       Please give me a \"token number ...\""
  960.     exitcode = -1
  961.     }
  962.  
  963.  
  964. # We hold names and numbers for constants and strings, as well as identifiers.
  965. #<Set initial...#>=
  966.     tokennumbers["constant"]=tokennumber; tokennumber--
  967.     tokenname["constant"]="constant"
  968.     tokennumbers["string"]=tokennumber; tokennumber--
  969.     tokenname["string"]="string"
  970.  
  971.  
  972. #<Write code for newline scrap#>=
  973. if (newline_category != "") {
  974.     print "@ @<Append a newline scrap@>=" > scrapfile
  975.     transstring=newline_translation
  976.     selfstring="small_app(next_control);"
  977.     wherestring="in translation of token newline"
  978.     append_keyword="small_app"
  979.     #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
  980.         |outstring|, using |selfstring| as translation of |"<*>"|#>
  981.     print outstring > scrapfile
  982.     printf "  app_scrap(SP_%s,%s_math);\n", newline_category,\
  983.         newline_mathness > scrapfile
  984.     appended[newline_category]=1
  985. } else {
  986.     print "Error: I don't know what to do with a newline"
  987.     print "       Please give me a \"token newline ...\""
  988.     exitcode = -1
  989.     }
  990.  
  991. #<Write code for |pseudo_semi| scrap#>=
  992. if (pseudo_semi_category != "") {
  993.     print "@ @<Append a |pseudo_semi| scrap@>=" > scrapfile
  994.     transstring=pseudo_semi_translation
  995.     selfstring="small_app(next_control);"
  996.     wherestring="in translation of token pseudo_semi"
  997.     append_keyword="small_app"
  998.     #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
  999.         |outstring|, using |selfstring| as translation of |"<*>"|#>
  1000.     print outstring > scrapfile
  1001.     printf "  app_scrap(SP_%s,%s_math);\n", pseudo_semi_category,\
  1002.         pseudo_semi_mathness > scrapfile
  1003.     appended[pseudo_semi_category]=1
  1004. } else {
  1005.     printf "Error: I don't know what to do with a pseudo_semi (%s;)",\
  1006.             substr(at_sign,1,1)
  1007.     print "       Please give me a \"token pseudo_semi ...\""
  1008.     exitcode = -1
  1009.     }
  1010.  
  1011. # Here is how we write out the code that converts ordinary tokens to scraps:
  1012. #<Write code for ordinary token scraps#>=
  1013. print "@ @<Cases for ordinary tokens@>=" > scrapfile
  1014. for (t in tokens) {
  1015.     temp = tokenname[t]
  1016.     printf "case %s:\n", temp > scrapfile
  1017.     transstring=tokentranslation[t]
  1018.     selfstring="small_app(next_control);"
  1019.     wherestring= sprintf ("in translation of token %s", t)
  1020.     append_keyword="small_app"
  1021.     #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
  1022.         |outstring|, using |selfstring| as translation of |"<*>"|#>
  1023.     print outstring > scrapfile
  1024.     printf "\tapp_scrap(SP_%s,%s_math);\n", tokencategory[t], \
  1025.         tokenmathness[t] > scrapfile
  1026.     temp = tokencategory[t]
  1027.     appended[temp]=1
  1028. #^append check#>
  1029.     print "\tbreak;" > scrapfile
  1030.     }
  1031.  
  1032. #*3{\tt TANGLE}'s token-to-output conversion.
  1033. We have to write special cases for things appearing in |tokennumbers|.
  1034. The output conventions for |string|, |constant| and |identifier| are
  1035. fixed by \.{TANGLE}.
  1036.  
  1037. One day we have to improve \.{TANGLE}'s treatment of spacing in the output;
  1038. at the moment it just makes sure there are spaces between adjacent identifiers
  1039. or numbers.
  1040. #^future enhancements#>
  1041. #<Write out...#>=
  1042. print "@ @<Cases for tokens to be output@>=" > ttokfile
  1043. for (t in tokennumbers) {
  1044.        #<If |t| is |"string"|, |"constant"|, or |"identifier"|, just |continue|#>
  1045.     printf "case %s:\n", tokenname[t] > ttokfile
  1046.     this_tangleto = tokentangleto[t]
  1047.     if (this_tangleto=="") {
  1048.         printf "\tC_printf(\"%%s\",\"%s\");\n",t > ttokfile
  1049.     } else {
  1050.         printf "\tif (out_state==verbatim) {\n" > ttokfile
  1051.         printf "\t\tC_printf(\"%%s\",\"%s\");\n",t > ttokfile
  1052.         printf "\t} else {\n" > ttokfile
  1053.         #<Write code to print |this_tangleto| onto |ttokfile|#>
  1054.         printf "\t}\n" > ttokfile
  1055.     }
  1056.     print "\tif (out_state!=verbatim) out_state=misc;" > ttokfile
  1057.     print "break;" > ttokfile
  1058.     }
  1059.  
  1060. # We also have to write something for the tokens that aren't in |tokennumbers|
  1061. but which have a nonnull |tokentangleto| anyway.
  1062. #<Write out...#>=
  1063. print "@ @<Cases for tokens to be output@>=" > ttokfile
  1064. for (t in tokentangleto) {
  1065.        #<If |t| is |"string"|, |"constant"|, or |"identifier"|, just |continue|#>
  1066.     if (tokennumbers[t]!="" || tokentangleto[t]=="")
  1067.         continue
  1068.     if (t=="@") {
  1069.         thistangletokname = "@@"
  1070.     } else if (t=="\\" || t=="'") {
  1071.         thistangletokname = "\\" t
  1072.     } else {
  1073.         thistangletokname = t
  1074.     }
  1075.     printf "case '%s':\n", thistangletokname > ttokfile
  1076.     this_tangleto = tokentangleto[t]
  1077.     if (this_tangleto=="") {
  1078.         print "This can't happen -- null tangleto for", t, wherestring
  1079.         exitcode = -1
  1080.     } else {
  1081.         printf "\tif (out_state==verbatim) {\n" > ttokfile
  1082.         printf "\t\tC_printf(\"%%s\",\"%s\");\n",t > ttokfile
  1083.         printf "\t} else {\n" > ttokfile
  1084.         #<Write code to print |this_tangleto| onto |ttokfile|#>
  1085.         printf "\t}\n" > ttokfile
  1086.     }
  1087.     print "\tif (out_state!=verbatim) out_state=misc;" > ttokfile
  1088.     print "break;" > ttokfile
  1089. }
  1090. # The tokens for |string|, |constant|, and |identifier| are treated
  1091. specially by \.{TANGLE}; code to handle them already lives in \.{TANGLE}.web.
  1092. Therefore, we don't gum up the works with our scheming.
  1093. #<If |t| is |"string"|, |"constant"|, or |"identifier"|, just |continue|#>=
  1094. if (t=="string"||t=="constant"||t=="identifier")
  1095.     continue
  1096.  
  1097.  
  1098.  
  1099. # This is somewhat like the translation code, but tuned for \.{TANGLE}
  1100. #<Write code to print |this_tangleto| onto |ttokfile|#>=
  1101. oldwherestring = wherestring
  1102. wherestring = "for tangleto " wherestring
  1103. #@
  1104. transstring=this_tangleto
  1105. #<Convert restricted translation in |transstring| 
  1106.     to quoted string in |outstring|#>
  1107. printf "\tC_printf(\"%%s\",%s);\n",outstring > ttokfile
  1108. #@
  1109. wherestring=oldwherestring
  1110.  
  1111.  
  1112.  
  1113. #*3Defining the token names.
  1114.  At some point we'll have to define all these names, for both
  1115. \.{TANGLE} and \.{WEAVE}. We may as well
  1116. show how we do that now.
  1117. #<Write out...#>=
  1118.     tempfile = scrapfile
  1119.     #<Write the definitions of the token names to |tempfile|#>
  1120.     tempfile = ttokfile
  1121.     #<Write the definitions of the token names to |tempfile|#>
  1122.  
  1123. # We use an ugly trick to get the token numbers different for 
  1124. \.{WEAVE} and \.{TANGLE}:
  1125. #<Write the definitions of the token names to |tempfile|#>=
  1126.     print "@ Here are the definitions of the token names" > tempfile
  1127.     for (t in tokennumbers) {
  1128.         temp = tokennumbers[t]
  1129.         if (temp==0)
  1130.             continue  ## don't know why we need this!!
  1131.         if (tempfile==ttokfile) { ## output to \.{TANGLE}
  1132.             #<If |t| is |"string"|, |"constant"|, or |"identifier"|, 
  1133.                just |continue|#> ## already defined in \.{TANGLE}
  1134.             temp = temp + #'37 + 3 - highesttoken ## hackety hack!
  1135.                   ## +3 because three highest are already defined!
  1136.             }
  1137.         printf "@d %s = %s\n", tokenname[t], temp > tempfile
  1138.         }
  1139.  
  1140.  
  1141. # Some token names are just characters quoted with |'|. We write out
  1142. all the others.
  1143. #<Write lists...#>=
  1144.     for (t in tokenname) {
  1145.         temp = tokenname[t]
  1146.         if (substr(temp,1,1) != "'") {
  1147.             #<Strip opening |"SP_"| from |temp|, if it is there#>
  1148.             print temp > tokennamefile
  1149.             }
  1150.         }
  1151.  
  1152. # #<Strip opening |"SP_"| from |temp|, if it is there#>=
  1153. tempa=substr(temp,1,3)
  1154. if (tempa=="SP_") {
  1155.     temp = substr(temp,4) ## remove |"SP_"|
  1156.     }
  1157.  
  1158.  
  1159. #*Reserved words and ilks.
  1160. \.{TANGLE} doesn't even need the {\it idea} of
  1161.  reserved words; it treats them like
  1162. all other identifiers.
  1163. \.{WEAVE}, however, needs to be able to recognize reserved words to do
  1164. prettyprinting. 
  1165. \.{WEAVE} uses a two-tiered system for coping with reserved words.
  1166. I think this system was really designed to make it easier to code
  1167. \.{WEAVE} by hand, and is therefore not of much interest for
  1168. \.{SPIDER}, but we retain it as a matter of least resistance.
  1169.  
  1170. Every reserved word belongs to an ilk, and it is the ilks that, like
  1171. tokens, have translations, categories, and so on.
  1172.  
  1173. I have made a bewildering array of defaults that is probably full of
  1174. bugs.
  1175. We use a special convention to initialize the |this_| family.
  1176.  
  1177. #<Pattern-act...#>=
  1178. #=/^ilk /#> {
  1179.     print "Ilk", $2 > logfile
  1180.     #<Set |this_mathness| etcetera to the defaults and those with no
  1181.         defaults to |""| #>
  1182.     #<If no category is specified, invent a default if you can#>
  1183.     this_name=""
  1184.     start_place=3
  1185.     #<Scan this line from |start_place| to finish, looking for
  1186.         \.{translation}$\ldots$ and putting results in
  1187.         |this_translation|$\ldots$#> 
  1188.     #<Make sure |this_category| is not empty#>
  1189.     #<Make sure |this_name| is empty#>
  1190.     ilk_category[$2]=this_category
  1191.     ilk_mathness[$2]=this_mathness
  1192.     ilk_translation[$2]=this_translation
  1193.     next
  1194. }
  1195.  
  1196.  
  1197. # The pernicious option here is to be able to leave off the category, so
  1198. that an item of ilk |fish_like| will get category |fish|.
  1199.  
  1200. #<If no category is specified, invent a default if you can#>=
  1201.     if ($2 ~ #=/^[a-zA-Z_]+_like$/#> && $0 !~ #=/ category /#>) { 
  1202.         ## give default category
  1203.         this_category = substr($2,1,length($2)-5)
  1204.         categories[this_category]=1
  1205.         }
  1206.  
  1207. # For the reserved words, our only option is to set an ilk.
  1208. We go through wild and assuredly ill-advised gyrations attempting to
  1209. set all the default properties of that ilk.
  1210. If the ilk is omitted, we make a new ilk by attaching the string
  1211. |"_like"| to the name of the reserved word.
  1212. {\bf Don't use this feature; it embarrasses the author.}
  1213. #^ill-advised#>
  1214. #<Pattern-action...#>=
  1215. #=/^reserved /#> {
  1216.     print "Reserved word", $2 > logfile
  1217.     if ($0 !~ #=/ ilk /#>) {
  1218.         #<Attempt to make up an ilk, with all its defaults#>
  1219.     }
  1220.     for (i=3; i<=NF;) {
  1221.         if ($i == "ilk") {
  1222.             i++
  1223.             reservedilk[$2]=$i
  1224.             has_reserved[$i]=1 ## remember that ilk has some reserved word
  1225.             i++
  1226.         } else {
  1227.             print "Error: bad reserved word attribute:", $i, \
  1228.                 "on line", NR
  1229.             #<Punt...#>
  1230.         }
  1231.     }
  1232.     #<Check that we used everything#>
  1233.     next
  1234. }
  1235.  
  1236. # Here is our feeble attempt to make up an ilk for a reserved word for
  1237. which no ilk is given.
  1238. The default ilk for |"with"| is |"with_like"|, and so on.
  1239. {\bf Please, please don't do this.}
  1240. #<Attempt to make up an ilk, with all its defaults#>=
  1241.     temp = $2 "_like"
  1242.     reservedilk[$2]=temp
  1243.     if (ilk_translation[temp]=="") {
  1244.         ilk_translation[temp]=default_translation
  1245.     }
  1246.     has_reserved[temp]=1
  1247.     if (ilk_mathness[temp]=="") {
  1248.         ilk_mathness[temp]=default_mathness
  1249.     }
  1250.     ## and default category for that ilk is the resword itself
  1251.     if (ilk_category[temp]=="") {
  1252.         ilk_category[temp]=$2
  1253.         categories[$2]=1
  1254.     }
  1255.     ilk_is_made_up[temp]=1 ## we really should do something with this
  1256. #^mistakes#>
  1257.  
  1258.  
  1259. #*1Telling {\tt WEAVE} how to recognize reserved words.
  1260. At the end, we'll write out definitions for the ilk names, and we'll
  1261. write translations of all the ilks.
  1262. #<Write out all...#>=
  1263. print "Writing out reserved words and ilks" > logfile
  1264. ilkno=64
  1265. print "@ Here is a list of all the ilks" > reserved
  1266. for (i in ilk_translation) {
  1267.     printf "@d SP_%s = %d\n", i, ilkno > reserved
  1268.     ilkno++
  1269.     }
  1270.  
  1271. # Here is where we write the code that converts reserved word tokens
  1272. into scraps.
  1273. #<Write out all...#>=
  1274. print " " > reserved
  1275. print "@ Here are the scraps we get from the reserved words" > reserved
  1276. print "@d the_word = res_flag+p-name_dir" > reserved
  1277. print "@<Decide on reserved word scraps@>=" > reserved
  1278. print "switch (p->ilk) {" > reserved
  1279. for (t in ilk_translation) {
  1280.     printf "\tcase SP_%s: \n\t\t", t > reserved
  1281.     transstring=ilk_translation[t]
  1282.     selfstring="small_app(the_word);"
  1283.     wherestring= sprintf ("in translation of ilk %s", t)
  1284.     append_keyword="small_app"
  1285.     #<Take translation from |transstring| and 
  1286.         write corresponding \.{WEAVE} code to
  1287.         |outstring|, using |selfstring| as translation of |"<*>"|#>
  1288.     if (trcnt>0) ## at least one text in the translation
  1289.         has_translation[t]=1
  1290.     print outstring > reserved
  1291.     printf "\tapp_scrap(SP_%s,%s_math);\n", ilk_category[t], \
  1292.     ilk_mathness[t] > reserved
  1293.     temp=ilk_category[t]
  1294.     appended[temp]=1
  1295. #^append check#>
  1296.     printf "\t\tbreak;\n" > reserved
  1297.     }
  1298. print "}" > reserved
  1299.         
  1300.  
  1301. # At the end, we'll have to enter each reserved word in the identifier
  1302. table, along with its ilk.
  1303. #<Write out all...#>=
  1304. print "@ @<Store all the reserved words@>=" > reserved
  1305. for (i in reservedilk) {
  1306.     printf "id_lookup(\"%s\",NULL,SP_%s);\n", i, reservedilk[i] > reserved
  1307. }
  1308.  
  1309. # At the very end, we'll make sure every ilk has both a reserved word
  1310. and some translation.
  1311. {\bf Perhaps this could be cleaned up a bit?}
  1312. #<Check for errors at...#>=
  1313.     for (i in ilk_translation) {
  1314.         if (has_reserved[i] != 1) {
  1315.             print "Error: there is no reserved word of ilk", i
  1316.             exitcode=-1
  1317.             }
  1318.         if (has_translation[i] != 1) {
  1319.             print "Error: ilk", i, "has no translation"
  1320.             exitcode=-1
  1321.             }
  1322.         }
  1323.  
  1324. # #<Write lists...#>=
  1325.     for (i in ilk_translation) {
  1326.         print i > ilkfile
  1327.         }
  1328.  
  1329. # #<Write stat...#>=
  1330. for (i in ilk_translation) number_of_ilks++
  1331. for (i in reservedilk) number_of_reserved_words++
  1332. printf "You defined %d reserved words of %d ilks.\n", \
  1333.     number_of_reserved_words, number_of_ilks
  1334. printf "You defined %d reserved words of %d ilks.\n", \
  1335.     number_of_reserved_words, number_of_ilks > logfile
  1336.  
  1337. #*The prettyprinting grammar.
  1338. The most intricate part of \.{WEAVE} is its mechanism for converting
  1339. programming language code into \TeX\ code.
  1340. A ``bottom up'' approach is used to parse the
  1341. programming language material, since \.{WEAVE} must deal with fragmentary
  1342. constructions whose overall ``part of speech'' is not known.
  1343.  
  1344. At the lowest level, the input is represented as a sequence of entities
  1345. that we shall call {\it scraps}, where each scrap of information consists
  1346. of two parts, its {\it category} and its {\it translation}. The category
  1347. is essentially a syntactic class, and the translation is a token list that
  1348. represents \TeX\ code. Rules of syntax and semantics tell us how to
  1349. combine adjacent scraps into larger ones, and if we are lucky an entire
  1350. program text that starts out as hundreds of small scraps will join
  1351. together into one gigantic scrap whose translation is the desired \TeX\
  1352. code. If we are unlucky, we will be left with several scraps that don't
  1353. combine; their translations will simply be output, one by one.
  1354.  
  1355. The combination rules are given as context-sensitive productions that are
  1356. applied from left to right. Suppose that we are currently working on the
  1357. sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest
  1358. production that applies to an initial substring $s_1\,s_2\ldots\,$; but if
  1359. no such productions exist, we find to find the longest production
  1360. applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we
  1361. try to match $s_3\,s_4\ldots\,$, etc.
  1362.  
  1363. A production applies if the category codes have a given pattern. For
  1364. example, one of the productions is
  1365. $$\hbox{\.{open [ math semi <\.{"\\\\,"}-opt-5> ] -->
  1366. open math}}$$ 
  1367. and it means that three consecutive scraps whose respective categories are
  1368. |open|, |math|, and |semi| are con\-verted to two scraps whose categories
  1369. are |open| and |math|. 
  1370.  The |open| scrap has not changed, while the string \.{<"\\\\,"-opt-5>} 
  1371. indicates that the new |math| scrap
  1372. has a translation composed of the translation of the original
  1373. |math| scrap followed by the translation of the |semi| scrap followed
  1374. by `\.{\\,}' followed by `|opt|' followed by `\.5'. (In the \TeX\ file,
  1375. this will specify an additional thin space after the semicolon, followed
  1376. by an optional line break with penalty 50.) 
  1377.  
  1378. Their is an extensive discussion of the grammar, with examples, in the
  1379. ``Spider User's Guide.''
  1380. Y'oughta read it.
  1381.  
  1382. #*1Scanning a production.
  1383.  A production in the grammar is written as a sequence of category
  1384. names and translations, followed by a right arrow (\.{-->}), followed
  1385. by a category name.
  1386. When \.{WEAVE} is scanning the sequence of scraps that makes up a
  1387. module, it checks to see whether the categories of those scraps match
  1388. the categories given on the left side of the production.
  1389. If so, the production fires, and the scraps and translations on the
  1390. left side of the arrow are combined into a single, new scrap, and the
  1391. new scrap is given the category from the right side of the arrow.
  1392. The scraps which are combined are called the firing scraps,
  1393. #^firing scraps#>
  1394. and the category given to the combination is called the target category.
  1395.  
  1396. Instead of a category name, e.g.~``\.{math},'' one can write a list of
  1397. category names, e.g.~``\.{(open\vert lsquare)}'' instead.
  1398. A scrap matches the list if and only if its category is one of the
  1399. names listed.
  1400. One can also use the wildcard ``\.?'', which any scrap matches.
  1401.  
  1402. On the right-hand side, one can write a \## followed by a number in
  1403. place of the target category name.
  1404. If we specify the target category as ``\.{\##2}'', for example, it
  1405. means ``give the new scrap the same category as the second scrap that
  1406. matched the left side of the production.''
  1407.  
  1408. # Here is the whole syntax as quoted from the ``Spider User's Guide''
  1409.  
  1410. \begingroup\def\\{\par\noindent\ignorespaces}\tt
  1411. \noindent\syntax{production} \produces\\\quad
  1412. \syntax{left context} [ \syntax{firing instructions} ] \syntax{right context}
  1413. --> \syntax{left context} \syntax{target category} \syntax{right
  1414. context}\\
  1415. \syntax{left context} \produces~\syntax{scrap designators}\\
  1416. \syntax{right context} \produces~\syntax{scrap designators}\\
  1417. \syntax{firing instruction} \produces \syntax{scrap designator}\\
  1418. \syntax{firing instruction} \produces \syntax{translation}\\
  1419. \syntax{scrap designator} \produces~?\\
  1420. \syntax{scrap designator} \produces~\opt{!}\syntax{marked category}\\
  1421. \syntax{scrap designator} \produces~\opt{!}\syntax{category alternatives}\\
  1422. \syntax{category alternatives} \produces~(\syntax{optional
  1423. alternatives}\syntax{marked category})\\
  1424. \syntax{optional alternative} \produces~\syntax{marked category}\vert\\
  1425. \syntax{marked category} \produces~\syntax{category name}\opt{*}\\
  1426. \syntax{target category} \produces~\#\syntax{integer}\\
  1427. \syntax{target category} \produces~\syntax{category name}\\
  1428. \endgroup
  1429.  
  1430. # Here is the pattern that reads productions.
  1431. In most of the  modules below, we read through some of the fields of the
  1432. production.
  1433. We use |i| to remember what field we are about to examine.
  1434. When a module terminates, |$i| is left pointing to the first field of
  1435. interest to the next module.
  1436. #<Production patt...#>=
  1437. #=/-->/#>    {
  1438.     #<Set up to parse this production#>
  1439.     #<Read through the fields of the production, up to the arrow#>
  1440.     #<Set |lowpos|, |highpos|, and |arrowpos| to their proper values#>
  1441.     #<Update |highestposoverall|#>
  1442.     #<Update |highestunknownpos|#>
  1443.     #<Check to see that left context matches#>
  1444.     #<Process scrap to which we are translating#>
  1445.     #<Check to see that right context matches#>
  1446.     #<Check to make sure we used all the fields of the production#>
  1447.     #<Compute the appropriate test for this production and put it
  1448.         in |prodtest[prodnum]|#>
  1449.     #<Compute the code to write the new translation, should this
  1450.         production fire, and put it in |prodtrans[prodnum]|#>
  1451.     #<Write the start token in |ppstart[prodnum]| and the number
  1452.         of tokens reduced in |tokensreduced[prodnum]|#>
  1453.     #<If we only reduced one token, write the reduction
  1454.          out to file |cycles| for later testing#>
  1455.     next
  1456. } ## \.{/-->/}
  1457.  
  1458. # Each scrap in the production will be given a position |pos|,
  1459. beginning with 1. (Using 1 and not 0 lets us make good use of the fact
  1460. that uninitialized AWK variables will have value zero.)
  1461. We will remember the positions  of the scraps that get reduced; they
  1462. will be from |lowpos| to |highpos-1|.
  1463. We keep track of the production number in |prodnum|, and we save a
  1464. copy of the input line in |inputline[prodnum]|.
  1465. #<Set up to parse this production#>=
  1466.     lowpos=0; highpos=0; pos=1
  1467.     prodnum=prodnum+1
  1468.     inputline[prodnum]=$0
  1469.     print "Parsing production", prodnum, $0 > logfile
  1470.  
  1471.  
  1472. # This is the guts of the parsing. We have to read each field in the
  1473. production, determine whether it is category or translation
  1474. information, and act accordingly.
  1475. Each scrap will be given a position |pos|.
  1476. We will write in |test[pos]| the code needed to decide whether a
  1477. particular scrap matches the pattern given in the production.
  1478. Scraps can match a single category by name, a list of categories, or
  1479. |"?"|, which every scrap matches.
  1480. Categories can be starred, in which case we underline the index entry
  1481. of the first identifier in the scrap's translation.
  1482.  
  1483. We also write in |trans[pos]| the code necessary to produce the
  1484. translations preceding the scrap at |pos|.
  1485.  
  1486. #<Read through the fields...#>=
  1487. trans[pos]=""
  1488. for (i=1; i<=NF; i++) {
  1489.     if ($i ~ #=/<.*>/#>) { ##  should be |trans_pattern|
  1490.         #<Process a translation in |$i|#>
  1491.     } else if ($i ~ #=/^!?[a-zA-Z_]+(\*\*?)?$/#>) { ## |cat_pattern|
  1492.         #<Process a single category#>
  1493.     } else if ($i ~ #=/^!?\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/#>){
  1494.         #<Process a list of alternative categories#>
  1495.     } else if ($i == "?") {
  1496.         #<Process a category wild card#>
  1497.     } else if ($i == "[") {
  1498.         lowpos=pos
  1499.     } else if ($i == "]") {
  1500.         highpos=pos
  1501.     } else if ($i=="-->") {
  1502.         break
  1503.     } else { ## we don't recognize the field
  1504.         print "Error: bad field is", $i, "in production on line", NR
  1505.         #<Forget this production#>
  1506.     }
  1507. }
  1508. i++
  1509.  
  1510.  
  1511. # When we find a mistake, we just abandon the current production.
  1512. Decrementing |prodnum| will make it as if this production never happened.
  1513. #<Forget this production#>=
  1514.     prodnum--
  1515.     #<Punt this...#>
  1516.  
  1517. # We process the translation and add the result to the current
  1518. translation for |pos|.
  1519. #<Process a translation...#>=
  1520. transstring=$i
  1521. selfstring="" ## senseless for productions
  1522. wherestring= sprintf ("in production on line %d", NR)
  1523. append_keyword="app"
  1524. #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
  1525. |outstring|, using |selfstring| as translation of |"<*>"|#>
  1526. trans[pos]=trans[pos] outstring
  1527.  
  1528. # Here we'll set |test[pos]|.
  1529. The phrase |test[pos]| will be a single C conjunct; if the test for
  1530. each scrap is true, the whole production will fire.
  1531. If we're called upon to make a scrap underlined or reserved, we'll add
  1532. to |trans[pos]|.
  1533.  
  1534. If a category is negated we add an extra clause to make
  1535. sure nothing matches the zero category, since {\tt WEAVE} assumes
  1536. no production ever matches a scrap with category zero.
  1537. #<Process a single category#>=
  1538.     field[pos]=$i ## save this field to compare RHS
  1539.     #<Set |negation|, and remove leading |"!"| from |$i| if necessary#>
  1540.     #<Strip stars from |$i| (if any) and add appropriate
  1541.         translations to |trans[pos]|#>
  1542.     cat = $i
  1543.     categories[cat]=1 ## remember |cat| is a category
  1544.     if (negation==0) {
  1545.         test[pos]=sprintf("(pp+%d)->cat==SP_%s",pos-1,cat)
  1546.     } else {
  1547.         test[pos]=sprintf("((pp+%d)->cat!=SP_%s && (pp+%d)->cat != 0)",\
  1548.             pos-1,cat,pos-1)
  1549.     }
  1550.     #<Update the record of the rightmost occurrence of category |cat|#>
  1551.     #<Advance |pos|, making the new |trans[pos]| empty#>
  1552.  
  1553. # The list of categories is enclosed in parentheses and the individual
  1554. categories are separated by vertical bars.
  1555. We have to make the test for these things a disjunction, but
  1556. processing is more or less like the processing for a single category.
  1557.  
  1558. If a list of alternatives is negated we add an extra clause to make
  1559. sure nothing matches the zero category, since {\tt WEAVE} assumes
  1560. no production ever matches a scrap with category zero.
  1561. #<Process a list of alternative categories#>=
  1562.     field[pos]=$i ## save this field to compare RHS
  1563.     #<Set |negation|, and remove leading |"!"| from |$i| if necessary#>
  1564.     if (negation==0) {
  1565.         test[pos]="(" ## open for a list of good alternatives
  1566.     } else {
  1567.         temp=sprintf("(pp+%d)->cat==0",pos-1)
  1568.         test[pos]="!(" temp "||" ## open for a list of bad alternatives
  1569.     }
  1570.     #<Strip stars from |$i| (if any) and add appropriate
  1571.         translations to |trans[pos]|#>
  1572.     temp = substr($i,2,length($i)-2) ## throw out parens
  1573.     m = split(temp,tok,"|")
  1574.     for (j=1;j<=m;j++) {
  1575.         cat = tok[j]
  1576.         categories[cat]=1 ## remember it's a category
  1577.         #<Update the record of the rightmost occurrence of
  1578.             category |cat|#>
  1579.         temp=sprintf("(pp+%d)->cat==SP_%s",pos-1,cat)
  1580.         test[pos]=test[pos] temp ## add alternative to test
  1581.         if (j!=m) 
  1582.             test[pos]=test[pos] "||\n" ## line too long errors
  1583.     }
  1584.     test[pos]= test[pos] ")"
  1585.     #<Advance |pos|, making the new |trans[pos]| empty#>
  1586.  
  1587.  
  1588. # We keep track of the rightmost occurrence of each category. 
  1589. This enables us to backtrack by exactly the right amount when a
  1590. production fires and creates a new scrap.
  1591. #<Update the record of the rightmost occurrence of category |cat|#>=
  1592.     if (pos > highestpos[cat]) {
  1593.         highestpos[cat]=pos
  1594.         }
  1595.  
  1596. # If a category or lsit of alternatives is preceded by an exclamation
  1597. point (|"!"|), we set |negation|, and we will test for scraps that are
  1598. {\it not} of that category or are {\it not} of one of the categories
  1599. listed.
  1600. #<Set |negation|...#>=
  1601. temp = substr($i,1,1)
  1602. if (temp=="!") {
  1603.     negation = 1
  1604.     $i = substr($i,2)
  1605. } else {
  1606.     negation = 0
  1607. }
  1608.  
  1609. # Since both translations and tokens can add to |trans[pos]| we must
  1610. make sure it is empty whenever we get a new |pos|. 
  1611. This device makes that easy.
  1612.  
  1613. #<Advance |pos|, making the new |trans[pos]| empty#>=
  1614.     pos=pos+1
  1615.     trans[pos]=""
  1616.  
  1617. # If a category is single-starred, we take this construct to be the
  1618. {\it definition} of that item, and we underline the index entry for
  1619. this module.
  1620. The |make_underlined| routine finds the first identifier in the
  1621. translation of the starred scrap, and underlines the index entry for
  1622. that identifier in this module.
  1623.  
  1624. If a category is double-starred, we used to try to change the ilk of the
  1625. appropriate identifier to make it a reserved word.
  1626. The only use this ever had was in handling C typedefs, and it should
  1627. probably be removed.
  1628. #^mistakes#>
  1629. In the meanwhile, double starring is like single starring.
  1630.  
  1631. #<Strip stars from |$i| (if any) and add appropriate
  1632.     translations to |trans[pos]|#>=
  1633. if ($i ~ #=/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*\*$/#>) { ## it's double-starred
  1634.     temp = sprintf("\tmake_underlined(pp+%d);\n",pos-1)
  1635.     trans[pos] = trans[pos] temp
  1636.     $i = substr($i,1,length($i)-2)
  1637. } else if ($i ~ #=/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*$/#>) { ## it's starred
  1638.     temp = sprintf("\tmake_underlined(pp+%d);\n",pos-1)
  1639.     trans[pos] = trans[pos] temp
  1640.     $i = substr($i,1,length($i)-1)
  1641. } else if ($i ~ #=/\*$/#>) { ## a bad star?
  1642.     print "Error: can't remove stars in production on line", NR
  1643.     #<Forget this production#>
  1644. }
  1645.  
  1646. # Wild cards are easy to process, but we do have to remember that
  1647. not even a wild card matches a scrap of category zero.
  1648. #<Process a category wild card#>=
  1649.     field[pos]=$i ## save this field to compare RHS
  1650.         test[pos]=sprintf("(pp+%d)->cat!=0",pos-1) ## anything nonzero matches
  1651.     highwildcard=pos ## we don't really need this?
  1652.     #<Advance |pos|, making the new |trans[pos]| empty#>
  1653.  
  1654.  
  1655.  
  1656. #
  1657. We reach this point in the program after we will have read the arrow
  1658. into |$i|.
  1659.  
  1660. This module establishes in what ranges of |pos| the contexts fall:
  1661. $$\vbox{\halign{##\hfil\tabskip1em&\hfil##\hfil\cr
  1662. \bf Items&\bf Range\cr
  1663. \noalign{\vskip2pt}
  1664. left context&|1..lowpos-1|\cr
  1665. firing instructions&|lowpos..highpos-1|\cr
  1666. right context&|highpos..arrowpos-1|\cr
  1667. }}$$
  1668. If |lowpos| and |highpos| haven't been set by the appearance of square
  1669. brackets, we set them to make the contexts empty.
  1670. None or both should be set.
  1671.  
  1672.  
  1673. #<Set |lowpos|, |highpos|, and |arrowpos| to their proper values#>=
  1674.     arrowpos=pos
  1675.     if (lowpos==0 && highpos==0) {
  1676.         lowpos=1 ## first transform position
  1677.         highpos=arrowpos     ## first token not reduced 
  1678.                     ## (or one beyond last token position)
  1679.     } else if (lowpos==0 || highpos==0) {
  1680.         print "Error: square brackets don't balance in", \
  1681.             "production on line", NR
  1682.         #<Forget this production#>
  1683.     }
  1684.  
  1685. # Here is the efficient place to update the rightmost (highest)
  1686. position of {\it any} category.
  1687. #<Update |highestposoverall|#>=
  1688.     if (arrowpos-1 > highestposoverall) { 
  1689.             highestposoverall=arrowpos-1
  1690.             }
  1691.  
  1692. # Dealing with grammars in which categories can be unnamed (using
  1693. wildcards or negation) can be a pain  in the ass.
  1694. What we have to do, when reducing after firing a production, is move 
  1695. backwards enough so that we don't miss any earlier productions that
  1696. are supposed to fire.
  1697. This means we have to move back at least far enough so that the new
  1698. scrap will match any unnamed category.
  1699. {\bf But} we don't have to worry about wildcards (|"?"|) at the end of
  1700. a production, because they would have matched anyway, even before the
  1701. current production fired. Hence:
  1702. #<Update |highestunknownpos|#>=
  1703. for (hup=arrowpos-1; field[hup]=="?";) {
  1704.     hup--
  1705.     }
  1706. for (;hup>highestunknownpos;hup--) {
  1707.     temp=field[hup]
  1708.     temp=substr(temp,1,1)
  1709.     if (temp=="?" || temp =="!") {
  1710.         highestunknownpos=hup ## we know |hup>highestunknownpos|
  1711.         break ## redundant, since test will fail
  1712.     }
  1713. }
  1714.  
  1715. # Here is the error checking for context sensitive productions.
  1716. #<Check to see that left context matches#>=
  1717.     for (pos=1;pos<lowpos;pos++) {
  1718.         #<Check |$i| against |field[pos]|#>
  1719.         i++
  1720.     }
  1721.  
  1722. # #<Check to see that right context matches#>=
  1723.     for (pos=highpos;pos<arrowpos;pos++) {
  1724.         #<Check |$i| against |field[pos]|#>
  1725.         i++
  1726.     }
  1727. # #<Check |$i| against |field[pos]|#>=
  1728.         if (i>NF || $i != field[pos]) {
  1729.             print "Error: token mismatch is: found", $i, \
  1730.                 "sought", field[pos], "on line", NR
  1731.             #<Forget this...#>
  1732.             }
  1733.  
  1734. # We process our target scrap in between checking the left and right
  1735. contexts.
  1736. This scrap can be the name of a category, or it can be ``$\##nnn$'',
  1737. where $nnn$ refers to the number of a category on the left side of the
  1738. arrow.
  1739. In this way it is possible to match wildcards and lists of alternatives.
  1740. #<Process scrap to which we are translating#>=
  1741.     ## i points to the target category
  1742.     if (i>NF) {
  1743.         print "Error: no target category in production on line", NR
  1744.         #<Forget this...#>
  1745.         }
  1746.     if ($i ~ #=/##[0-9]+/#>) { ## a number
  1747.         $i = substr($i,2) ## peel off the \##
  1748.         #<Make sure |1 <= $i < arrowpos|#>
  1749.         targetcategory[prodnum]="Unnamed category"
  1750.         temp = sprintf("(pp+%d)->cat", $i-1)
  1751.         unnamed_cat[prodnum]=temp
  1752.     } else if ($i ~ #=/[a-zA-Z][a-zA-Z_]*/#>) { ## a category
  1753.         targetcategory[prodnum]=$i
  1754.         categories[$i]=1 ## remember this is a category
  1755.     } else {
  1756.         print "Error: unrecognizable target token", $i, \
  1757.             "in production on line", NR
  1758.         #<Forget this...#>
  1759.     }
  1760.     i++
  1761.  
  1762. # We call this at the end to make sure there aren't unused fields left over
  1763. #<Check to make sure we used all the fields of the production#>=
  1764.     if (i<=NF) {
  1765.         print "Error: used only " i-1 " of " NF " tokens", \
  1766.             "in production on line", NR
  1767.         #<Forget this...#>
  1768.         }
  1769.  
  1770. # After having vetted the whole production, we combine the tests and
  1771. translations for each |pos|.
  1772. #<Compute the appropriate test for this production and put it
  1773.         in |prodtest[prodnum]|#>=
  1774.     prodtest[prodnum]=""
  1775.     for (pos=1;pos<arrowpos;pos++) {
  1776.         if (pos>1) {
  1777.             prodtest[prodnum]=prodtest[prodnum] " &&\n\t\t"
  1778.             }
  1779.         prodtest[prodnum]=prodtest[prodnum] test[pos]
  1780.         }
  1781.  
  1782. # #<Compute the code to write the new translation, should this
  1783.         production fire, and put it in |prodtrans[prodnum]|#>=
  1784.     prodtrans[prodnum]=""
  1785.     for (pos=lowpos;pos<highpos;pos++) {
  1786.         prodtrans[prodnum]=prodtrans[prodnum] trans[pos] 
  1787.         ## add code to append this scrap
  1788.         temp = sprintf("\tapp1(pp+%d);\n",pos-1)
  1789.         prodtrans[prodnum]=prodtrans[prodnum] temp
  1790.         #<If not negated, record the fact that a token of
  1791.             category satisfying |test[pos]| could have
  1792.             been reduced#>
  1793.         }
  1794.     prodtrans[prodnum]=prodtrans[prodnum] trans[highpos]
  1795.  
  1796. # #<Write the start token in |ppstart[prodnum]| and the number
  1797.         of tokens reduced in |tokensreduced[prodnum]|#>=
  1798.     ppstart[prodnum]=lowpos-1
  1799.     tokensreduced[prodnum]=highpos-lowpos
  1800.  
  1801. # #<If we only reduced one token, write the reduction
  1802.          out to file |cycles| for later testing#>=
  1803.     if (highpos-lowpos==1) {
  1804.         printf "%d: %s --> %s\n", prodnum, field[lowpos], \
  1805.             targetcategory[prodnum] > cycles
  1806.         wrotecycles = 1
  1807.         }
  1808.  
  1809. # If we never even had the possibility of a cycle, we still have to write
  1810. out a dummy file so the cycle checker in the Makefile won't barf.
  1811. # #<Write lists of everything#>=
  1812. if(wrotecycles==0) {
  1813.     print "0: dummy --> nodummy" > cycles
  1814.     }
  1815.  
  1816. # For error checking, we keep track of categories that get reduced in
  1817. productions. 
  1818. We can't do this while scanning the production, because we don't know
  1819. at the beginning what |lowpos| will be, since we might or might not
  1820. ever see a left square bracket.
  1821.  
  1822. If a particular category is never reduced, that merits a warning later on.
  1823. #<If not negated, record the fact that a token of category satisfying
  1824.             |test[pos]| could have been reduced#>=
  1825. temp = field[pos]
  1826. tempa = substr(temp,1,1)
  1827. if (tempa != "!") {    
  1828.     if (temp ~ #=/^\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/#>) {
  1829.         ## list of alternatives
  1830.         #<Remove trailing stars from |temp|#>
  1831.         temp = substr(temp,2,length(temp)-2)
  1832.         m = split(temp,tok,"|")
  1833.         for (j=1;j<=m;j++) {
  1834.             alternate = tok[j]
  1835.             reduced[alternate]=1
  1836.             }
  1837.     } else if (temp ~ #=/^[a-zA-Z_]+(\*\*?)?$/#>) {
  1838.         #<Remove trailing stars from |temp|#>
  1839.         reduced[temp]=1
  1840.     } else if (temp != "?") {
  1841.         print "Confusion: unintelligible field[pos]:", temp, \
  1842.             "in production on line", NR
  1843.         #<Forget this...#>
  1844.     }
  1845. }
  1846.  
  1847. # #<Remove trailing...#>=
  1848. while (temp ~ #=/\*$/#>) {
  1849.     temp = substr(temp,1,length(temp)-1)
  1850. }
  1851.  
  1852. # #<Check for err...#>=
  1853. for (c in categories) {
  1854.     if (reduced[c] != 1) {
  1855.         print "Warning: category", c, "never reduced"
  1856.     }
  1857. }
  1858.  
  1859.  
  1860. # Here's a check for the target token number
  1861. #<Make sure |1 <= $i < arrowpos|#>=
  1862. if ((0+$i)<1 || (0+$i)>=0+arrowpos) {
  1863.     print "Error: can't take token number", $i, "of", arrowpos-1, \
  1864.         "tokens", "in production on line", NR
  1865.     #<Forget this...#>
  1866.     }
  1867.  
  1868. #*1Writing the scrap reduction code.
  1869. Before writing the grammar, we want to define all of the category codes.
  1870. #<Write out...#>=
  1871.     print "Writing out category codes" > logfile
  1872.     print "@ Here is a list of category codes scraps can have" > grammarfile
  1873.     i=1
  1874.     for (t in categories) {
  1875.         printf "@d SP_%s = %d\n",t,i > grammarfile
  1876.         i++
  1877.     }
  1878.     print "@c" > grammarfile
  1879. # We also want to make sure we can print the names of categories in
  1880. case we need to debug.
  1881. #<Write out...#>=
  1882.     print "##ifdef DEBUG" > grammarfile
  1883.     print "##define PRINT_CAT(A,B) case A: printf(B); break" > grammarfile
  1884.     print "print_cat(c) /* symbolic printout of a category */" > grammarfile
  1885.     print "eight_bits c;" > grammarfile
  1886.     print "{" > grammarfile
  1887.     print "  switch(c) {" > grammarfile
  1888.     for (t in categories) {
  1889.         printf "PRINT_CAT(SP_%s,\"%s\");\n",t,t > grammarfile
  1890.         }
  1891.     print "    case 0: printf(\"zero\"); break;" > grammarfile
  1892.     print "    default: printf(\"UNKNOWN\"); break;" > grammarfile
  1893.     print "  }" > grammarfile
  1894.     print "}" > grammarfile
  1895.     print "##endif DEBUG" > grammarfile
  1896.     print " " > grammarfile
  1897.  
  1898. # And there goes the list...
  1899. #<Write lists...#>=
  1900.     for (c in categories) {
  1901.         print c > categoryfile
  1902.         }
  1903.  
  1904. # #<Write stat...#>=
  1905. for (c in categories) {
  1906.     number_of_categories++
  1907. }
  1908. printf "You used %d different categories in %d productions.\n", \ 
  1909.     number_of_categories, prodnum
  1910. printf "You used %d different categories in %d productions.\n", \ 
  1911.     number_of_categories, prodnum > logfile
  1912. printf "The biggest production had %d scraps on its left-hand side.\n", \
  1913.     highestposoverall
  1914. printf "The biggest production had %d scraps on its left-hand side.\n", \
  1915.     highestposoverall > logfile
  1916.  
  1917.  
  1918. # We will write a list of the successfully parsed productions to a
  1919. separate file.
  1920. The list will include
  1921.  production numbers, to which the user can refer
  1922. when debugging.
  1923. #<Write lists...#>=
  1924. for (n=1; n<= prodnum; n++) {
  1925.     printf "%2d: %s\n",n,inputline[n] > productions
  1926.     }
  1927.  
  1928. # Finally, we write out the code for all of the productions.
  1929. Here is our first view of category checking: we want to make sure that
  1930. each category can be appended, either by |app_scrap| or by |reduce|.
  1931. We also want to make sure each category can be reduced by firing some
  1932. production.
  1933. We track these things using the arrays |appended| and |reduced|.
  1934.  
  1935. We write the definition of |highestposoverall|, for safety.
  1936.  
  1937. We used to write this code as a very deeply nested if-then-else,
  1938. but that caused a yacc overflow in the generated code for C~{\tt WEAVE}.
  1939. So now we write 
  1940. {\tt if (...) \LB...; goto end\_prods;\RB}
  1941. #<Write out...#>=
  1942. print "Writing out grammar" > logfile
  1943. print "@ Here is where we define |highestposoverall| and where we" > grammarfile
  1944. print "check the productions." > grammarfile
  1945. print "@d highestposoverall =", highestposoverall > grammarfile
  1946. print "@<Test for all of the productions@>=" > grammarfile
  1947. for (n=1; n<=prodnum; n++) {
  1948.     if (n%5==0)
  1949.         print "@ @<Test for all of the productions@>=" \
  1950.             > grammarfile ## avoids overflowing \.{WEAVE} of \.{WEAVE}
  1951.     #<Change \vert,\_, and {\tt \##} in |inputline[n]|; put results in |this_string|#>
  1952.     #<Make |this_string| no more than 60 characters wide#>
  1953.     printf "if (%s) {\n\t/* %d: {\\tt %s} */\n%s",\
  1954.         prodtest[n],n,this_string,prodtrans[n] > grammarfile
  1955.     #<Write the |reduce| call, taking note of whether the
  1956.         category is named#>
  1957.     print "\tgoto end_prods;" > grammarfile
  1958.     printf "} " > grammarfile
  1959.     }
  1960. printf "\n" > grammarfile
  1961. print "end_prods:" > grammarfile
  1962.  
  1963. # We do different things for a category that is unnamed.
  1964. #<Write the |reduce| call, taking note of whether the category is named#>=
  1965.     ttk=targetcategory[n]
  1966.     if (ttk == "Unnamed category") {
  1967. #^append check#>
  1968.         printf "\treduce(pp+%d,%d,%s,%d,%d);\n",ppstart[n],\
  1969.             tokensreduced[n],unnamed_cat[n],\
  1970.             1-highestposoverall,n > grammarfile
  1971.     } else {
  1972.         appended[ttk]=1 ## remember we appended this token
  1973. #^append check#>
  1974.         reduction=highestpos[ttk]
  1975.         if (reduction<highestunknownpos) {
  1976.             reduction = highestunknownpos
  1977.             }
  1978.         printf "\treduce(pp+%d,%d,SP_%s,%d,%d);\n",ppstart[n],\
  1979.             tokensreduced[n],targetcategory[n],\
  1980.             1-reduction,n > grammarfile
  1981.     }
  1982.  
  1983. # This is the place we check for errors.
  1984. #^append check#>
  1985. #^reduce check#>
  1986. #<Check for errors...#>=
  1987. for (c in categories) {
  1988.     if (appended[c] != 1) {
  1989.         if (c=="ignore_scrap") { ## appended by \.{WEAVE}
  1990.             print "Warning: category", c, "never appended"
  1991.         } else {
  1992.             print "Error: category", c, "never appended"
  1993.             exitcode=-1
  1994.             }
  1995.         }
  1996.     }
  1997.  
  1998.  
  1999.  
  2000. # It's desirable to put the production in a comment, but we have to
  2001. get rid of the confusing \vert, or \.{WEAVE} will think it introduces
  2002. code.
  2003. We also have to escape underscores and sharp signs, otherwise \TeX\ will 
  2004. think we want math mode.
  2005. #<Change \vert,\_, and {\tt \##} in |inputline[n]|; put results in |this_string|#>=
  2006.     this_string = inputline[n]
  2007.     tempi = index(this_string,"|")
  2008.     while (tempi != 0) {
  2009.         tempa = substr(this_string,1,tempi-1)
  2010.         tempb = substr(this_string,tempi+1)
  2011.         this_string = tempa "\\vert " tempb
  2012.         tempi = index(this_string,"|")
  2013.         }
  2014.         templ = ""; tempr = this_string
  2015.     tempi = index(tempr,"_")
  2016.     while (tempi != 0) {
  2017.         tempa = substr(tempr,1,tempi-1)
  2018.         tempr = substr(tempr,tempi+1)
  2019.         templ = templ tempa "\\_"
  2020.         tempi = index(tempr,"_")
  2021.         }
  2022.         this_string = templ tempr
  2023.         templ = ""; tempr = this_string
  2024.     tempi = index(tempr,"##")
  2025.     while (tempi != 0) {
  2026.         tempa = substr(tempr,1,tempi-1)
  2027.         tempr = substr(tempr,tempi+1)
  2028.         templ = templ tempa "\\##"
  2029.         tempi = index(tempr,"##")
  2030.         }
  2031.         this_string = templ tempr
  2032.  
  2033.  
  2034. # We have to keep these productions from making an input line too long.
  2035. #<Make |this_string| no more than 60 characters wide#>=
  2036. toolong=this_string; this_string=""
  2037. while (length(toolong)>60) {
  2038.     idx=59
  2039.     idchar = substr(toolong,idx,1)
  2040.     while (idx>1 && idchar!=" ") {
  2041.         idx--
  2042.         idchar = substr(toolong,idx,1)
  2043.     }
  2044.     if (idx==1) 
  2045.         idx=59
  2046.     temp = substr(toolong,1,idx-1)
  2047.     toolong = substr(toolong,idx+1)
  2048.     this_string = this_string temp "\n"
  2049. }
  2050. this_string = this_string toolong
  2051.  
  2052.  
  2053.  
  2054. #*The rest of {\tt SPIDER}.
  2055. We present the remaining features of \.{SPIDER} in them order we
  2056. used in the ``\.{SPIDER} User's Guide.''
  2057. #*2 Naming the target language.
  2058. \.{SPIDER} is designed to help you build a \.{WEB} system for any
  2059. programming language.
  2060. We need to know the name of the language, and what extension to 
  2061. use when writing the tangled unnamed module.
  2062. We use this information to pick a name for the file that will hold
  2063. this \.{WEB}'s special \TeX{} macros, and we write |"\\input webkernel"|
  2064. on that file.
  2065. #<Patt...#>=
  2066. #=/^language /#>    {
  2067.     language = $2
  2068.     extension=language
  2069.     for (i=3; i<NF; ) {
  2070.         if ($i=="extension") {
  2071.             i++
  2072.             extension=$i
  2073.             i++
  2074.         } else if ($i=="version") {
  2075.             i++
  2076.             version=$i
  2077.             i++
  2078.         } else {
  2079.             print "Error: unknown language property", $i,\
  2080.                 "on line", NR
  2081.             #<Punt...#>
  2082.         }
  2083.     }
  2084.     #<Check that we used everything#>
  2085.     #<Write the first line of the macro file#>
  2086.     next
  2087. }
  2088.  
  2089. # #<Write out...#>=
  2090. if (language != "") {
  2091.     print "@ Here is the language-dependent stuff" > tlang
  2092.     if (version!="")
  2093.         version = ", Version " version
  2094.     printf "@d banner = \"This is %s TANGLE%s %s\\n\"\n", language, \
  2095.         version, date > tlang
  2096.     printf "@<Global...@>=char C_file_extension[]=\"%s\";\n", extension \
  2097.         > tlang
  2098. #@
  2099.     print "@ Here is the language-dependent stuff" > wlang
  2100.     if (version!="")
  2101.         version = ", Version " version
  2102.     printf "@d banner = \"This is %s WEAVE%s %s\\n\"\n", language, \
  2103.         version, date > wlang
  2104.     print "@<Set |out_ptr| and do a |tex_printf| to read the macros@>=" \
  2105.         > wlang
  2106.     printf "*out_ptr='x'; tex_printf(\"\\\\input %sweb.te\");\n", \
  2107.         extension > wlang
  2108.     printf "@ @<Global...@>=char C_file_extension[]=\"%s\";\n", extension \
  2109.         > wlang
  2110. } else {
  2111.     print "Error: you haven't given me any \"language\" information"
  2112.     exitcode=-1
  2113. }
  2114.     
  2115. #*1Defining {\TeX} macros.
  2116. The first thing we do after getting the language is write a line to
  2117. the macro file.
  2118. This makes sure the kernel \.{WEB} macros will be available.
  2119. #<Write the first line of the macro file#>=
  2120.     macrofile = extension "web.tex"
  2121.     print "\\input webkernel.tex" > macrofile
  2122.  
  2123.  
  2124. # Processing macros is straightforward: everything between \.{macros
  2125. begin} and \.{macros end} gets copied into the macro file.
  2126. #<Patt...#>=
  2127. #=/^macros begin$/,/^macros end$/#> {
  2128.     if (begunmacs==0) {
  2129.         begunmacs=1
  2130.         next
  2131.     }
  2132.     if ($0 ~ #=/^macros end$/#>) {
  2133.         begunmacs=0
  2134.         next
  2135.     }
  2136.     if (macrofile=="") {
  2137.         if (complained==0) {
  2138.             print "Error: you must give \"language\"",\
  2139.                 "before \"macros\""
  2140.             complained=1
  2141.             #<Punt...#>
  2142.         } 
  2143.     } else {
  2144.         print $0 > macrofile
  2145.     }
  2146.     next
  2147. }
  2148.  
  2149.  
  2150.  
  2151. #*1Handling modules.
  2152. We need to give module names a category, both when we define modules
  2153. and when we use them in other modules.
  2154.  
  2155. We might conceivably fool around with mathness, but we don't 
  2156. really intend to do so.
  2157. #<Pattern-action...#>=
  2158. #=/^module /#> {
  2159.     for (i=2;i<NF;) {
  2160.         if ($i=="definition") {
  2161.             i++
  2162.             mod_def_cat=$i
  2163.             categories[$i]=1
  2164.             print "Module definition category set to", $i > logfile
  2165.             i++
  2166.         } else if ($i=="use") {
  2167.             i++
  2168.             mod_use_cat=$i
  2169.             categories[$i]=1
  2170.             print "Module use category set to", $i > logfile
  2171.             i++
  2172.         } else {
  2173.             print "Error: unknown module property", $i, \
  2174.                 "on line", NR
  2175.             #<Punt...#>
  2176.         }
  2177.     }
  2178.     #<Check that we used everything#>
  2179.     next
  2180. }
  2181.  
  2182. # Here's how we rig it:
  2183. #<Write out...#>=
  2184. if (mod_def_cat!="") {
  2185.     print "@ @<Call |app_scrap| for a module definition@>=" > scrapfile
  2186.     printf "app_scrap(SP_%s,no_math);\n", mod_def_cat > scrapfile
  2187.     appended[mod_def_cat]=1
  2188. } else {
  2189.     print "Error: I don't know what to do with a module definition"
  2190.     print "       Give me a \"module definition ...\""
  2191.     exitcode=-1
  2192. }
  2193. if (mod_use_cat!="") {
  2194.     print "@ @<Call |app_scrap| for a module use@>=" > scrapfile
  2195.     printf "app_scrap(SP_%s,maybe_math);\n", mod_use_cat > scrapfile
  2196.     appended[mod_use_cat]=1
  2197. } else {
  2198.     print "Error: I don't know what to do with a module use"
  2199.     print "       Give me a \"module use ...\""
  2200.     exitcode=-1
  2201. }
  2202.  
  2203.  
  2204. #*1At sign.
  2205. With \.{SPIDER}, we can designate any character we like as the 
  2206. ``magic at sign.''
  2207. #<Pattern-act...#>=
  2208. #=/^at_sign /#> {
  2209.     if (NF==2 && length($2)==1) {
  2210.         if ($2=="@") {
  2211.             at_sign="@@"
  2212.         } else {
  2213.             at_sign=$2
  2214.             }
  2215.     } else {
  2216.         print "Error: I can't understand", $0
  2217.         print "       Give me an at sign of length 1"
  2218.         #<Punt...#>
  2219.     }
  2220.     next
  2221. }
  2222.     
  2223. # We write the at sign out to the grammar file and to \.{TANGLE}'s token file
  2224. #<Write out all...#>=
  2225.     tempfile = grammarfile
  2226.     #<Write |at_sign| definion to |tempfile|#>
  2227.     tempfile = ttokfile
  2228.     #<Write |at_sign| definion to |tempfile|#>
  2229.  
  2230. # It's trivially done
  2231. #<Write |at_sign| definion to |tempfile|#>=
  2232.     print "@ Here is the |at_sign| for the new web" > tempfile
  2233.     printf "@d at_sign = @`%s'\n", at_sign > tempfile
  2234.     print " " > tempfile
  2235.     print "@ Here is |the_at_sign| left for common" > tempfile
  2236.     print "@<Global...@>=char the_at_sign = at_sign;" > tempfile
  2237.     print " " > tempfile
  2238.  
  2239. # We provide a default at sign:
  2240. #<Set init...#>=
  2241.     at_sign="@@"
  2242.  
  2243.  
  2244. #*1Comments.
  2245. We have to explain how our programming language supports comments.
  2246. We give the strings that initiate and terminate a comment.
  2247. We can say comments are terminated by ``newline'' if that's the case.
  2248. #<Pattern-act...#>=
  2249. #=/^comment /#> {
  2250.     print $0 > logfile
  2251.     for (i=2; i<NF;) {
  2252.         if ($i=="begin") {
  2253.             i++
  2254.         if ($i ~ #=/^<.*>$/#>) {
  2255.                 transstring = $i
  2256.         wherestring = "in \"comment begin\" on line " NR
  2257.                 #<Convert restricted translation in |transstring| 
  2258.                     to quoted string in |outstring|#>
  2259.                 begin_comment_string = outstring
  2260.                 i++
  2261.         } else {
  2262.         print "Error: \"comment begin\" must have a restricted translation"
  2263.         #<Punt...#>
  2264.         }
  2265.         } else if ($i=="end") {
  2266.             i++
  2267.             if ($i=="newline") {
  2268.                 comments_end_with_newline = 1
  2269.                 end_comment_string = "\"\\n\""
  2270.             } else if ($i ~ #=/^<.*>$/#>){
  2271.                 comments_end_with_newline = 0
  2272.                 transstring = $i
  2273.         wherestring = "in \"comment end\" on line " NR
  2274.                 #<Convert restricted translation in 
  2275.                     |transstring| 
  2276.                     to quoted string in |outstring|#>
  2277.                 end_comment_string = outstring
  2278.         } else {
  2279.         print "Error: \"comment end\" must have a restricted translation"
  2280.         #<Punt...#>
  2281.         }
  2282.             i++
  2283.         } else {
  2284.             print "Error: bad comment attribute:", $i
  2285.             #<Punt...#>
  2286.         } 
  2287.     }
  2288.     #<Check that we used everything#>
  2289.     #<Write the comment definitions to the macro file#>
  2290.     next
  2291. }
  2292.  
  2293. # \.{WEAVE} and \.{TANGLE} must be able to recognize comments.
  2294. Here we give \.{TANGLE}
  2295.  quoted strings that show the beginning and end of a
  2296. comment.
  2297.  
  2298. #<Write out...#>=
  2299. print "@ Here we recognize the comment start seqence" > ttokfile
  2300. print "@<See a comment starting at |loc| and skip it@>=" > ttokfile
  2301.    printf "{int len; len=strlen(%s);\n", begin_comment_string > ttokfile
  2302.    printf "if (loc+len<=limit && !strncmp(loc,%s,len)) {\n",\
  2303.      begin_comment_string > ttokfile
  2304.    print "\tloc += len; /* a new thing */" > ttokfile
  2305.    print "\tskip_comment(); /* scan to end of comment or newline */" > ttokfile
  2306.    print "\tif (comment_continues || comments_end_with_newline)" > ttokfile
  2307.    print "\t\treturn('\\n');" > ttokfile
  2308.    print "\telse continue;\n}\n}" > ttokfile
  2309.  
  2310.  
  2311. # Now this is \.{WEAVE} finding the start of a comment
  2312. #<Write out...#>=
  2313. print "@ @<See a comment starting at |loc-1| and return |begin_comment|@>=" \
  2314.         > scrapfile
  2315.    printf "{int len; len=strlen(%s);\n", begin_comment_string > scrapfile
  2316.    printf "if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
  2317.      begin_comment_string > scrapfile
  2318.    print "\tloc += len-1;" > scrapfile
  2319.    print "\t return (begin_comment); /* scan to end of comment or newline */" > scrapfile
  2320.    print "}\n}" > scrapfile
  2321.  
  2322.  
  2323.  
  2324.  
  2325. # Here \.{TANGLE} spots the end of a comment
  2326. #<Write out...#>=
  2327. print "@ Here we deal with recognizing the end of comments" > ttokfile
  2328. printf "@d comments_end_with_newline = %d\n", comments_end_with_newline >ttokfile
  2329. print "@<Recognize comment end starting at |loc-1|@>=" > ttokfile
  2330. if (comments_end_with_newline != 1) {
  2331.    printf "{int len; len=strlen(%s);\n", end_comment_string > ttokfile
  2332.    printf "if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
  2333.      end_comment_string > ttokfile
  2334.    print "loc += len-1; return(comment_continues=0); }}" > ttokfile
  2335. } else {
  2336.    print "/* This code will never be executed */ " > ttokfile
  2337. }
  2338.  
  2339. # Now here is \.{WEAVE}.
  2340. \.{WEAVE} copes elsewhere with the situation when
  2341. |comments_end_with_newline| holds, so we don't need to consider it here.
  2342. #<Write out...#>=
  2343. printf "@ Here we recognize end of comments" > scrapfile
  2344. printf "@d comments_end_with_newline = %d\n",comments_end_with_newline >scrapfile
  2345. print "@<Check for end of comment@>=" > scrapfile
  2346.    printf "{int len; len=strlen(%s);\n", end_comment_string > scrapfile
  2347.    printf "if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
  2348.      end_comment_string > scrapfile
  2349. print " loc++; if(bal==1) {if (phase==2) app_tok('}'); return(0);}" > scrapfile
  2350. print "  else {" > scrapfile
  2351. print "    err_print(\"! Braces don't balance in comment\");" > scrapfile
  2352. print "@.Braces don't balance in comment@>" > scrapfile
  2353. print "    @<Clear |bal| and |return|@>;" > scrapfile
  2354. print "  }" > scrapfile
  2355. print "}" > scrapfile
  2356. print "}" > scrapfile
  2357.  
  2358.  
  2359. # We have to give \.{TANGLE} the beginning and ending comment strings, so
  2360. it can use thing in writing its own comments.
  2361. #<Write out...#>=
  2362.     print "@ Important tokens:" > ttokfile
  2363.     printf "@d begin_comment_string = %s\n", begin_comment_string > ttokfile
  2364.     printf "@d end_comment_string = %s\n", end_comment_string > ttokfile
  2365.  
  2366. # We also have to write out the starting and ending comment strings to
  2367. the macro file. 
  2368. We do this at the time of parsing |#=/^comment /#>|, so the user has a
  2369. chance to override.
  2370. #<Write the comment definitions to the macro file#>=
  2371. if (macrofile!="") {
  2372.    this_string=substr(begin_comment_string,2,length(begin_comment_string)-2)
  2373.    #<Write |this_string| into |tex_string|, escaping \TeX's specials#>
  2374.    printf "\\def\\commentbegin{%s}\n", tex_string > macrofile
  2375.    if (comments_end_with_newline==0) {
  2376.       this_string=substr(end_comment_string,2,length(end_comment_string)-2)
  2377.       #<Write |this_string| into |tex_string|, escaping \TeX's specials#>
  2378.       printf "\\def\\commentend{%s}\n", tex_string > macrofile
  2379.    } else {
  2380.       print "\\def\\commentend{\\relax}" > macrofile
  2381.    }
  2382. } else {
  2383.     print "Error: I can't write comment info to the macro file---"
  2384.     print "       you haven't given me any \"language\" information"
  2385.     #<Punt...#>
  2386.     }
  2387.  
  2388.  
  2389.  
  2390. # Escaping \TeX's specials is pretty easy:
  2391. #<Set initial...#>=
  2392. texof["\\"]="\\BS"
  2393. texof["{"]="\\{"
  2394. texof["}"]="\\{"
  2395. texof["$"]="\\$"
  2396. texof["&"]="\\amp"
  2397. texof["##"]="\\##"
  2398. texof["^"]="\\H"
  2399. texof["_"]="\\_"
  2400. texof["~"]="\\TI"
  2401. texof["%"]="\\%"
  2402.  
  2403. #<Write |this_string| into |tex_string|, escaping \TeX's specials#>=
  2404. tex_string=""
  2405. while (length(this_string)>0) {
  2406.     c = substr(this_string,1,1)
  2407.     this_string = substr(this_string,2)
  2408.     cprime = texof[c]
  2409.     if (cprime=="") {
  2410.         tex_string = tex_string c
  2411.     } else {
  2412.         tex_string = tex_string cprime
  2413.     } 
  2414. }
  2415.  
  2416. #*1Controlling line numbering.
  2417. Here we fart around with line numbering for \.{TANGLE}.
  2418. This lets \.{TANGLE} write an indication of the locations of things in
  2419. the \.{WEB} source.
  2420. The C preprocessor accepts these things as \.{\##line} directives.
  2421. #<Pattern-act...#>=
  2422. #=/^line /#> {
  2423.     print $0 > logfile
  2424.     for (i=2; i<NF;) {
  2425.         if ($i=="begin") {
  2426.             i++
  2427.         if ($i ~ #=/^<.*>$/#>) {
  2428.                 transstring = $i
  2429.         wherestring = "in \"line begin\" on line " NR
  2430.                 #<Convert restricted translation in |transstring| 
  2431.                     to quoted string in |outstring|#>
  2432.                 sharp_line_open = outstring
  2433.                 i++
  2434.         } else {
  2435.         print "Error: \"line begin\" must have a restricted translation"
  2436.         #<Punt...#>
  2437.         }
  2438.         } else if ($i=="end") {
  2439.             i++
  2440.             if ($i ~ #=/^<.*>$/#>){
  2441.                 transstring = $i
  2442.         wherestring = "in \"line end\" on line " NR
  2443.                 #<Convert restricted translation in 
  2444.                     |transstring| 
  2445.                     to quoted string in |outstring|#>
  2446.                 sharp_line_close = outstring
  2447.         } else {
  2448.         print "Error: \"line end\" must have a restricted translation"
  2449.         #<Punt...#>
  2450.         }
  2451.             i++
  2452.     } else {
  2453.         print "Error: bad line attribute:", $i, "on line", NR
  2454.         #<Punt...#>
  2455.     } 
  2456.     } ## |for|
  2457.     #<Check that we used everything#>
  2458.     next
  2459. }
  2460.  
  2461. # We have to give \.{TANGLE} the strings for \&{\##line} commands.
  2462. #<Write out...#>=
  2463.     print "@ Important tokens:" > ttokfile
  2464.     printf "@d sharp_line_open = %s\n", sharp_line_open > ttokfile
  2465.     printf "@d sharp_line_close = %s\n", sharp_line_close > ttokfile
  2466.  
  2467. # We'll choose some innocuous defaults
  2468. #<Set init...#>=
  2469. sharp_line_open = "\"##line\""
  2470. sharp_line_close = "\"\""
  2471.  
  2472. #*1Tracking the generation date.
  2473. We want to be able to note the date on which we generate files.
  2474. #<Patt...#>=
  2475. #=/^date /#>    {
  2476.     ## date returned as ``Fri Dec 11 11:31:18 EST 1987''
  2477.     mo = month[$3]
  2478.     day = $4
  2479.     year = $7
  2480.     time = $5
  2481.     #<Set |hour|, |minute|, and |ampm| from |time|#>
  2482.     date = sprintf ("(generated at %d:%s %s on %s %d, %d)",\
  2483.         hour, minute, ampm, mo, day, year)
  2484.     next
  2485. }
  2486.  
  2487. # We want the months to have their full names
  2488. #<Set init...#>=
  2489. month["Jan"]="January"
  2490. month["Feb"]="February"
  2491. month["Mar"]="March"
  2492. month["Apr"]="April"
  2493. month["May"]="May"
  2494. month["Jun"]="June"
  2495. month["Jul"]="July"
  2496. month["Aug"]="August"
  2497. month["Sep"]="September"
  2498. month["Oct"]="October"
  2499. month["Nov"]="November"
  2500. month["Dec"]="December"
  2501.  
  2502. # We make a ``friendly'' time from |time=="hh:mm:ss"|.
  2503. #<Set |hour|, |minute|, and |ampm| from |time|#>=
  2504.     hour = substr(time,1,2)
  2505.     if (hour >=12)
  2506.         ampm = "PM"
  2507.     else
  2508.         ampm="AM"
  2509.     
  2510.     if (hour==0) {
  2511.         hour =12
  2512.     } else if (hour>12) {
  2513.         hour = hour -12
  2514.     }
  2515.     minute = substr(time,4,2)
  2516.  
  2517.  
  2518. #*=The {\tt SPIDER} tools.
  2519. #i cycle.web
  2520. #*Flagging duplicate names.
  2521. Detects duplicate names in a sorted list.
  2522. #(nodups.awk#>=
  2523. {    if ($0==last) {
  2524.         print "Error: duplicate name", $0, "on lines", NR-1"-"NR
  2525.         exit -1
  2526.         }
  2527.     last = $0
  2528. }
  2529. #*Checking translation keywords for validity.
  2530. #(transcheck.awk#>=
  2531. #=/^good translations$/#>,#=/^test translations$/#> {
  2532.     if ($0 !~ #=/^good translations$|^test translations$/#>) {
  2533.         istranslation[$0]=1
  2534.         }
  2535.     next
  2536.     }
  2537.  
  2538. {    if (istranslation[$0]!=1) {
  2539.         print "Error:", $0, "is not a valid translation"
  2540.         exitcode = -1
  2541.         }
  2542. }
  2543.  
  2544. END {
  2545.     exit exitcode
  2546.     }
  2547. # This is a copy of {\tt transcheck.list}, which should be the first
  2548. part of the input to {\tt transcheck.awk}.
  2549. Since \.{TANGLE} will insert its own stuff, we can't use it.
  2550. {\tt transcheck.awk} {\em could} be updated to work with the
  2551. tangled output, though, if it seemed desirable.
  2552. #(junk.list#>=
  2553. good translations
  2554. break_space
  2555. force
  2556. big_force
  2557. opt
  2558. backup
  2559. big_cancel
  2560. cancel
  2561. indent
  2562. outdent
  2563. math_rel
  2564. math_bin
  2565. math_op
  2566. test translations
  2567.  
  2568. #*=Index.
  2569. This is a combined index to {\tt SPIDER} and the {\tt SPIDER} tools.
  2570. Since the {\tt SPIDER} tools are nearly trivial, it's really just
  2571. {\tt SPIDER}.
  2572.  
  2573.  
  2574.