diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/noweb |
Initial commit
Diffstat (limited to 'web/noweb')
386 files changed, 74430 insertions, 0 deletions
diff --git a/web/noweb/CHANGES b/web/noweb/CHANGES new file mode 100644 index 0000000000..9f01a042d2 --- /dev/null +++ b/web/noweb/CHANGES @@ -0,0 +1,394 @@ +CHANGES FOR VERSION 2.12 +l2h and sl2h now support rules from booktabs package +l2h and sl2h now deal with \suspend and \resume from package mdwlist +l2h and sl2h now deal with \verbatiminput package verbatim +note to FAQ about \usepackage[T1]{fontenc} +root Makefile now has a default target that complains bitterly (Allan Adler) +get xchunks to track source locations +fix bogon in Hacker's Guide: @index nl takes no argument +totex didn't handle @line +improved German text from Pascal Schmitt +l2h and sl2h now make -- – and --- — +brace fix for 2.11 broke [[}]]; now fixed +improved German text from Johannes Wiedersich (closes Debian #418892) +\usepackage{vmargin} not \usepackage{vpage} (Hubert Chan; closes Debian #420614) +added an optional argument to Emacs noweb-mode to support desktop-read (NR) +added an option to l2h/sl2h to convert LaTeX double-quote ligatures to HTML 4 entities +NR has a new address +changes to noweb-mode.el to make font-lock work better (Christian-Lynbeck) +C code now compiles with gcc -Wall -Werror, though tricks with rcsid make me blush +awkname script needed to update roff scripts and lib/pipedocs (Chloe Lopez) +Partial support for Icelandic (Johann Oskarsson <johann@myrkraverk.com>) +added Lee Wittenberg's custom-code filter to contrib/leew +the noweb-mode command now sets noweb-chunk-vector when run +fix nountangle script to support multiple filters (Anon Sricharoenchai) +noscript shell-script trick by Greyson Fischer (added to contrib) +'getline' renamed (now part of POSIX standard) (Brad Larsen) +added missing control sequences to nwmac.tex (Sergio Spina) +Aharon Robbins is now Arnold Robbins <arnold@skeeve.com> +Point to Jim Pettigrew's installation instructions for Windows 7 +noweb & other 25 yo scripts now quote shell variables correctly (Francky Leyn) +eliminated deprecated tmpnam() in favor of tmpfile(3) +Makefile now uses mkdir -p (to keep GNU Make from bleating) +added BSD-2 license (H/T Jonas Stein, Gentoo project) +Scripts no longer use insecure names in /tmp/; instead using mktemp + +CHANGES FOR VERSION 2.11b - released 12 Jun 2006 +Make noroff use -mm by default (Debian 218050) +Fixed flagrant errors in noweave -x -troff (but bad formatting remains) + +CHANGES FOR VERSION 2.11a - released 20 May 2006 +Clarify instructions for $PATH and $MANPATH (B Pierce) +Create examples/Makefile.awk (B Pierce) +Clarify documentation: unescaped << and >> not OK in docs chunk (R Hemmecke) +Add -lua option to docs2comments filter + +CHANGES FOR VERSION 2.11 - released 6 April 2006 +Braces did not render correctly in [[<<$\{a_i\}$>>]] (Albert Reiner) +From Kevin Quick, Windows hints and a new NOTANGLE.BAT +Generate postscript with -P cmz for better PDF (Francky Leyn) +Clarify for noweave -delay that the first doco chunk is before the first @ +extended xchunks with -summary argument +Line in a code chunk opening with unclosed, unescaped << is OK (Ralf Hemmecke) + +UNDO ME! Make docs2comments filter tolerate embedded @xref stuff.. +Added Windows XP help to FAQ (David Lemmon) +Noidx now calculates labels for a code chunk using the number of preceding + definitions from the same @file, not the overall number +Change wording for \noweboptions{german} (Christian Lindig) +Need to insert \leavevmode in \sublabel so that new \@bsphack will work right +From Yann Dirson, new filters in contrib/ydirson. +From Fr'ed'eric Lin'e, some improvements in French translation (apologies to Moli`ere) + +CHANGES FOR VERSION 2.10c - released 17 Jul 2002 + +Bug in noidx.nw caused tag NWD1 etc to be used in multiple files. +Cleaned up alignment tags in tables emitted by l2h.nw (Federico Di Gregorio) +Fixed some bugs in the HTML markup for the hacker's guide +Goes back to iconc, except for noidx.nw, which can't be compiled because + of the 32-bit polynomial arithmetic + +CHANGES FOR VERSION 2.10b - released 15 Jul 2002 + +Bug in xchunks.nw, around line 100 -- db never initialized (Stephen Edwards) +In local identifier cross-reference, enable breaks between pages + of multiply defined identifiers (option breakdefs) +Create a secret \noweboptions{breakcode} for those who want their + code chunks broken across page boundaries +German is `Benutzt' not `Benutztt' (Johannes Huesing) +No longer suggests iconc; compiling with icont is now the default. + +CHANGES FOR VERSION 2.10a - released 30 Mar 2001 + +Fixed problem in src/Makefile -- FAQ should be built by `source' not `all' + +CHANGES FOR VERSION 2.10 - released 16 Jun 2000, but not officially + +Added -matlab to nountangle (Uwe Brauer) +Added \nwblindhyperanchor to \weblabel (Andrew Swann) +Changed one more 'char c' to 'unsigned char c' in recognize.nw (Gilles ANDRE) +Change noweb.sty to support more than 26 code chunks on a page (F. Leyn) +l2h skips optional arguments with \includegraphics +fixed bugs in htmltoc having to do with nested anchors on one line +Added capacity for two more filters to noweave script +Fixed bug in autodefs.sml (including spaces in symbol chars) +Added autodefs.mmix +Added autodefs.asdl +Scripts for Plan 9 rc (Russ Cox) +Tinkered with noidx sortkey +Added \mbox{} to \nwnewline, so that spaces won't be eaten (John Cummings) +Added xchunks filter, per request of Mike Smith & Glenn Holloway +Replaced FORTH uniqueid heuristic with 32-bit CRC +Added pipedocs filter to pipe documentation chunks through commands +Added h2a (convert html to ascii) for use with pipedoces +Added docs2comments filter, with pipedocs and h2a, to replace nountangle +Added nwmtime filter; helps derive Zephyr web pages +Moved the official FAQ to html +Added multiple-language support to noweb.sty (Miguel Filgueiras) +notangle -L initial newline is a bug and will never be fixed (Harald Soleng) +Added \noweboptions{noxref}, intended for temporary use inside documents. + +CHANGES FOR VERSION 2.9a - released 16 Feb 1999 + +Fixed bug in htmltoc that left multiple toc anchors + + +CHANGES FOR VERSION 2.9 + +Good support for hypertext using the `hyperref' package +noweave now finds uses in quoted code, the better to exploit hypertext +added support for emacs font-lock highlighting as comments to noweb-mode (Adnan Yaqub) +Document common -L options on notangle man page (Brett Presnell) +Fixed incompatibility with verbatim package (Rafael Laboissiere) +added contrib/leyn from Francky Leyn +noweb-mode.el no longer treats @x as chunk end for non-white x (Chris Milner) +Don't use signed char to index array of 256 in recognize.nw (Michael Herrmann) +Added explanation for WNT to FAQ. +Support \ quoting in argument to elide +Support multiple -R options on the same notangle command line. +Change tohtml so that noweave -latex+html doesn't protect anchor texts + in a rawhtml environment (???) +Added to /usr/doc/noweb for Slackware binary distribution +Slackware binary distribution now includes emacs noweb mode. +Updated internal docs in support.nw to explain the great \nwlbrace trouble +Use gcc -ansi -pedantic in Makefiles. +Remove multicols environment from examples/wc.nw (Yotam Medini) +Change noweave and totex to support subscripting identifiers used in quoted code +Fixed bug in l2h (bad conversion of tables). +Make l2h of \section use CPS (for newline between \section and {) +Added letenv to l2h, and changed verbatim to use environment name as terminator + + +CHANGES FOR VERSION 2.8a + +noweave complains if you use -x with -index or -indexfrom +installation now includes noweb-mode.el +noroots no longer treats uses in quoted code as real uses (David Bruce) +file INSTALL now notes that 'awkname' is always needed (Robert Solovay) +file INSTALL now notes that texhash(1) may be needed (Robert Solovay) +mnt (noweb script) now complains about chunks named <<*>> (Robert Solovay) +lots of rcs identifiers and names everywhere +all man pages should carry version numbers +Use Stephen Parker's htmltoc, with many bug fixes and improvements. + + +CHANGES FOR VERSION 2.8 + +fixed unbalanced brace in support.nw for nwmac.tex (Hartmut Henkel) +changed notangle script so notangle -L preserves tabs (Saul London, NR) +changed uniqueid in noidx to avoid everything except letters, digits, + and a few known special chars. fixes the Norwegian bug (Hans + Husum) and also the non-Posix regexp (Christian Wagner) +update recognizer.nw to use 128-node recognizer for larger character sets + as per Christian Wagner +$arkopts -> $markopts for noweave -v +noweave now uses \documentclass for headers +fixed a bug in tohtml ``impossible character to skip'' +hacked \catcode \other for " in \setupcode, + which is better for german.sty even though \setupmodname is still wrong +added support for @fatal to all pipeline stages; + noweave and notangle now detect pipeline errors and exit nonzero +added Lee Wittenberg's changes to support.nw and totex.nw to support + printing of index identifiers in different fonts +updated documentation to be consisten with latex2e, e.g., talk about + the noweb 'package' not the 'document-style option'. + Kept nowebstyle(1) for backward compatability. +added \nextchunklabel to support dotNoweb (replaces its \chunklabel) +massive improvements to l2h, including support for tables +moved nowebstyle and nowebfilters man pages to section 7 +noweave -v shows @header and @trailer + + +CHANGES FOR VERSION 2.7d + +noweb.sty steals \{ and \} in chunk names. Bad dog! No biscuit! +Forgot to include [noweb] style in numarkup.nw +changed <- to <- in tohtml and l2h, and similarly for -> +HTML output of noweb tools is now weblint-clean +didn't properly skip <!-- ...> in tohtml +bug fix for bad indentation when chunk starts at beginning of line +added many more control sequences to l2h +added % l2h closure, makes nr's \secn, \fig, and the like possible. + + + +CHANGES FOR VERSION 2.7c + +Fixed bug in l2h, wasn't treating quotes in @use correctly. +Added -autodefs pascal (a hack) +Fixed bug in tohtml, with noweave -option longxref was showing uses twice +Fixed ``black magic'' claim in man page to refer to -option longxref... +Fixed tohtml to make placement of anchors nest properly when possible +Underscores in filenames are now in FAQ +Added the htmltoc command + + +CHANGES FOR VERSION 2.7b + +Changed module->chunk in error message 'undefined module name' +Added touch to `make source' in a forlorn attempt to eliminate once + and for all the problem of sources and derived sources with + identical timestamps +Added `make touch' in case CTAN screws up timestamps, and updated FAQ. +Disabled assertions in lexer (markmain.nw) +Added pointer to noweb home page to README +Mention sharpline in the man page entry for notangle -L +@@ in column 1 now stands for a single @ sign. +Change my address from Bellcore to Purdue. +Preliminary support for hypertext with hyper.sty. + +CHANGES FOR VERSION 2.7a + +Move @xrev prevdef and @xref nextdef to appear before @defn, to + support navigational aids as in Fraser and Hanson's book. + Requires changes in totex and tohtml to delay xref output. + Support.nw now makes long vs short a noweb option. +Hack noweave to pass -shortxref to backend on seeing `-option shortxref' +Offer shortxref support in tohtml. +Changed noidx to avoid putting anchor labels in empty documentation chunks. +Fixed bug in l2h; was treating saved ignore flag as text fun after copying bracket. +tohtml dumped core if it tried to warn of bad tag with null arg +Botched local definitions in autodefs files +SHELL=/bin/sh in Makefiles +Improvements in l2h, including multiple citations. +Fixed Makefile in xdoc to build techrep with wc.tex now that examples have moved +Fix bugs in awkname; thanks to Steven Ooms for the bug report and the fix. +In INSTALL, noted special awk problems for HP-UX users. +Change noweb.sty to use \textwidth, not \hsize, to place use/def markup +Fixed bug in -autodefs.c (confusion around static storage class) +Quoted code no longer makes l2h forget what mode it was in ($[[x]]+1$) +Uses in quoted code (e.g., [[<<exported functions>>]]) are now + formatted sensibly and with proper cross-reference. As a side effect, + lexical analysis is faster. + +CHANGES FOR VERSION 2.7 + +New directory structure, new Makefiles. +Distributing gzipped tar file instead of compressed shell archive. +Added l2h filter to convert latex to HTML. +Added -markup option to tools +contrib/norman now has a numarkup, which is markup for nuweb +Fixed unmarkup to handle nuweb chunks, which don't end in newlines. +The awk versions of filters are even more inferior to the Icon versions, + and they are barely tested. I will fix bugs as they are reported. + +CHANGES FOR VERSION 2.6d (which never left Bellcore) + +New copyright, intended to make it possible to sell CD-ROMs containing noweb +Revisions to the Noweb Hacker's Guide +Added 'elide' filter to icon directory. +Changed totex to turn a reference to ??? into one to nw@notdef +Commented out references to \code and \edoc in support.nw + (too chicken to eliminate them completely) +Changed lib/tohtml not to use the awk 'length' variable --- mawk barfs + (thanks to Bill Trost for bug report) +Changed tohtml not to plant anchor on < or & because that + could start an SGML tag (thanks to Bill Trost for bug report) +Awk version of tohtml used && in patterns; some awks couldn't handle it. + (thanks to Mark Smucker for bug report) +Changed \filename to \nwfilename to avoid conflicts with some latex2e packages +Help for the most frequent problems now in INSTALL. +Fixed a bug that bit noweave -index when there are multiple definitions + of an identifier; tag of first defn was repeated instead of + getting all tags (thanks to Michael Norrish for bug report). +noweave -html -x was producing local index stuff, but it shouldn't. +Decided -html shouldn't disable -noindex in noweave script +Improved tohtml so it needn't insert * for an anchor so often. +Changed notangle internally so it doesn't complain + if a chunk doesn't end with a newline (possible with nufront) +changed noweave so -index no longer forces a latex wrapper +Changed noidx so it won't use HTML specials in unique ids +Changes tohtml not to quote HTML in chunk names + + +CHANGES FOR VERSION 2.6c + +Added -v option to noweave to show the pipeline. +A preliminary Noweb Hacker's Guide. + +CHANGES FOR VERSION 2.6b + +Added -autodefs c + +Changed installation procedure so that source is no longer distributed +with contributed software --- you now must install noweb, then build +contributed software. + +Bug fixes: + + - Makefile didn't create .../man/man1 + - noweave botched -x option, emitted index info anyway + - markup complained, incorrectly, about [[<<]] in documentation. + - noidx died if it tripped over an identifier used only in quoted code + - tohtml wasn't inserting doc anchor into above_defns, so some xrefs + that should have read `above' were coming out `below' + - when one identifier was a prefix of another, as in + Class and Class::member, finduses duplicated the prefix. + + +CHANGES FOR VERSION 2.6a + +Fix bug in finduses that bit on the HP snake. + +CHANGES FOR VERSION 2.6 + +For the HTML support + - Invented lots of new @xref markup to support cross-referencing and + indexing in a formatter-independent way. + - Rewrote noidx and totex to push cross-reference and index support into + the back end. + - Adapted an HTML back end contributed by Bill Trost +Dave Love provided the bug fixes for latex2e. + +CHANGES FOR VERSION 2.5: + +Improved Source Code + +Several parts of the source code have been rewritten in noweb. I +especially call your attention to the contents of the shell, lib, and +tex directories. Reading tex/support.nw will help you tinker with +formatting. + + +Enhanced Latex Support + +Includes INCOMPATIBLE changes to options accepted by noweave (and +noweave's default behavior). noweb now supports an index of +identifiers and identifier cross-reference in a language-independent +way. For more information, see the man page for noweave, especially +the -index option. The man page for noindex tells how to build +indexes for large, multi-file documents. + +I have added a `noweb' command that acts much like Preston Briggs's +`nuweb'; it extracts all the source programs and also writes a LaTeX +document with full cross-referencing. See the man page for details. + +latex support has been improved by changes to noweave, the noweave +cross-referencer, and the noweb document-style option. Dave Love and +Jon Krom provided impetus for some of these changes in the best +possible way; they sent code. Improvements include: + -- noweave now generates latex headers by default. + -- Code chunks are broken across pages only when they are more than + a page long. (Thanks to Dave Love.) + -- Thanks to Dave Love, cross-referenced definitions that appear on + the same page are now numbered 7a, 7b, 7c, etc. (optionally 7.1, + 7.2, 7.3, or just plain 7; see the noweave man page). + -- There is a `noweb' pagestyle, which puts source-file names and + dates on the header line. It is the default page style for noweave. + -- noweave inserts no extra newlines, so the line numbers are the + same in the .tex and .nw files (Dave Love's suggestion; a major win). + -- By using noweave -delay, you can put a latex preamble in the + first documentation chunk, and noweave will delay \filename and + \begindocs until after the preamble, which becomes a WEB-like + ``limbo'' area (suggested by Dave Love, Jon Krom, and others). + -- noweave now has a -filter option that lets you name a + cross-referencer or prettyprinter on the command line. + You can try Jon Krom's cross-referencer by using + noweave -filter $LIB/noxref.krom + -- notangle has a -filter option that you can use to implement + slightly different semantics, such as Knuth's ``...'' + abbreviation convention. + -- The awk version of the cross-referencer is now almost as good as + the Icon version; the sole remaining difference is that the Icon + version ignores case when sorting; the awk version does not. + -- Bug fixes in noweave's handling of special characters in chunk names. + -- Many of the shell and awk scripts are now noweb files. + -- \chunklist is now \nowebchunks, and it need not appear on a line + by itself. \nowebindex provides an index. + -- The TeX support is now somewhat documented; see tex/support.nw + -- Support for latex's New Font Selection Scheme (whatever that is), + thanks again to Dave Love. + +Finally, Kostas Oikonomou has contributed a simple prettyprinter for +Icon programs, and Conrado Martinez-Parra has contributed a +prettyprinter for Dijkstra's language of guarded commands. I have +made both suitable for use with noweave -filter. + + +Bug Fix + +The code that was supposed to prevent a chunk from including itself +didn't work at all. So +<<*>>= +Forever <<*>> +@ made notangle very unhappy. diff --git a/web/noweb/COPYRIGHT b/web/noweb/COPYRIGHT new file mode 100644 index 0000000000..61fae52cca --- /dev/null +++ b/web/noweb/COPYRIGHT @@ -0,0 +1,24 @@ +Noweb is copyright 1989-2018 by Norman Ramsey. All rights reserved. + +Noweb is protected by copyright. It is not public-domain +software or shareware, and it is not protected by a ``copyleft'' +agreement like the one used by the Free Software Foundation. +It may be used and/or redistributed using *either* the terms +stated here *or* by the 2-clause BSD license found in file LICENSE. + +Noweb is available free for any use in any field of endeavor. You may +redistribute noweb in whole or in part provided you acknowledge its +source and include this COPYRIGHT file. You may modify noweb and +create derived works, provided you retain this copyright notice, but +the result may not be called noweb without my written consent. + +You may sell noweb if you wish. For example, you may sell a CD-ROM +including noweb. + +You may sell a derived work, provided that all source code for your +derived work is available, at no additional charge, to anyone who buys +your derived work in any form. You must give permisson for said +source code to be used and modified under the terms of this license. +You must state clearly that your work uses or is based on noweb and +that noweb is available free of change. You must also request that +bug reports on your work be reported to you. diff --git a/web/noweb/CTAN b/web/noweb/CTAN new file mode 100644 index 0000000000..f034f1b9fc --- /dev/null +++ b/web/noweb/CTAN @@ -0,0 +1,28 @@ +Administrative notes for CTAN upload: + + - The distribution contains 7 .bbl files, which are there to enable + users to build documentation or examples. + + - The distribution contains many files with duplicate basenames, + e.g., noidx.nw, noindex.nw, noroots.nw, and so on. These + different files have identical basenames because they provide + identical functionality on different platforms, or because they + provide documentation of that functionality. For example, + + src/awk/noindex.nw Index building on Unix using Awk + src/icon/noindex.nw Index building on Unix using Icon + contrib/rsc/rc/noindex.nw Index building using the Plan 9 shell RC + xdoc/noindex.nw Man page for index building + + None of these files is ever exposed to kpathsea or to TeX, so name + clashes should not be a problem. + + - The distribution includes TeX source for two papers about Noweb: a + 1991 technical report and a 1993 submission to IEEE Software. + Both .tex files refer to information that is no longer current, + including the obsolete FTP server ftp.tex.ac.uk. Because the + intent of the distribution is to provide interested readers with + access to these documents as they were published, these references + will not be changed. + + diff --git a/web/noweb/DATE b/web/noweb/DATE new file mode 100644 index 0000000000..97856accd9 --- /dev/null +++ b/web/noweb/DATE @@ -0,0 +1 @@ +Version 2.12 of Wed Jun 20 12:21:27 EDT 2018 diff --git a/web/noweb/DEBIAN b/web/noweb/DEBIAN new file mode 100644 index 0000000000..fd1f2a91d7 --- /dev/null +++ b/web/noweb/DEBIAN @@ -0,0 +1,19 @@ +Under noweb's original licence, anyone may modify noweb and create +derived works, provided he or she retains this copyright notice, but +the result may not be called noweb without my written consent. I +hereby provide my written consent to the Debian project to call their +derived work 'noweb'. I do so under the assumption that Debian's +modifications are normally intended to fix bugs, close security holes, +and bring the software into compliance with Debian policy. I have a +great deal of trust in and respect for the Debian project, so I am in +fact willing for the Debian project to make any modification they deem +necessary and still call the result 'noweb'. In particular, there is +no need for the Debian project to consult me about any contemplated +modification, either now or in the future. I do reserve the right to +revoke my consent in the unlikely event that, in my opinion, future +Debian modifications will have made it misleading to call the package +'noweb'. + +Under the BSD license, anyone may create a derived work and to call it +noweb without my written consent. + diff --git a/web/noweb/DEBIAN.asc b/web/noweb/DEBIAN.asc new file mode 100644 index 0000000000..0788abba25 --- /dev/null +++ b/web/noweb/DEBIAN.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEmrzhALHbgoRRr4rgZfoIXblrJmkFAlsr5Q8ACgkQZfoIXblr +JmmjChAAi23ohCcof9Z8rf6bshdr8gxYPvFuep5YTIJRmi4aw6nMMMCmrEZC9cvk +C7TjDeg8TY9z/t0VelcMSepxwjTnJ/r4rm33xIkyojPp277YfU4bmdbR+LavvE5o +g9z745KAUkemAVBSKH4aieJt+XJ6EMKh/AjPjYzZsGZAfcDrmjCQo4gMM781RSq5 +wBTTxmqHstZ9gRiibu9Pxks1zE+zmf/lpKhP8bYNRCRnYzEuVbDzNesVimFnSZkC +erxGnIGjmVaUBc0B1LYAfOj/vTYx0h/mxXpeq/1oUpEsO0mslrGxKnB/WPjDVRg7 +/R68gFg6+ptk6IIlwzk9eCIXMcXia1ax10S2RysARmHhYwcGaEkfKFfVcwlK0xlU +0dhcJ8pSZ2aw+/NP5FLqgBfxt2Fg/YVvwOII37azmkxb3BmUO6UpuxObIsMDmOun +8fPum5+bzyWGUIevWHSt/cw37BLEWKoS+SKV8MLDZjP2qp5vYlIQNGdW0/pypSHC +qUEghZpTOxTFU7A3qtVO3ksOqTrxIDqc72Qe6f3i8KBT8Nw1xplKygDesYmTJ85t +aftWYTizQ4QQHk2GzW5UPzxoVzUQYYIANTOu5sESdOjf4a99FvHc6lu9pIWMSqvk +0nqbxMjODpNaYbHRF3LxZx2Qv1nHgj5n+M4le5zzM2PBpgAo1GE= +=y3B3 +-----END PGP SIGNATURE----- diff --git a/web/noweb/LICENSE b/web/noweb/LICENSE new file mode 100644 index 0000000000..3a62e12067 --- /dev/null +++ b/web/noweb/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright © 2018, Norman Ramsey + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/web/noweb/Makefile b/web/noweb/Makefile new file mode 100644 index 0000000000..f2fd5753a6 --- /dev/null +++ b/web/noweb/Makefile @@ -0,0 +1,66 @@ +# Copyright 1995-2006 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# +# Don't edit this file; you should be editing the Makefiles in the +# src and contrib directories. + +VERSION=2.12 +SHELL=/bin/sh +CINAME=-Nv`echo $(VERSION) | tr . _` +CIMSG=-f -m'standard checkin preparing to export version $(VERSION)' + +all: + @echo "You have no business running 'make' here; please look at the README file" + @exit 1 +source: ; for i in src; do (cd $$i; make source); done +www: ; for i in src/xdoc examples; do (cd $$i; make www); done + +clean: + for i in src examples contrib; do (cd $$i; make clean); done + rm -f nwsrcfilter *~ */*~ +clobber: clean + for i in src examples contrib; do (cd $$i; make clobber); done + +DATE: + (./echo -n "Version $(VERSION) of "; date) > DATE + +versioncheck: + @if [ -z "$(UPPERVERSION)" ]; then echo "run 'make versioncheck' in the parent directory, not here" 1>&2; exit 1; fi + @if [ "$(VERSION)" = "$(UPPERVERSION)" ]; then echo "Version $(VERSION) OK in Makefile"; else echo "Version mismatch in Makefile: upper $(UPPERVERSION) lower $(VERSION)"; exit 1; fi + @if [ "$(VERSION)" = "`awk '{print $$2}' DATE`" ]; then echo "Version $(VERSION) OK in DATE"; else ./echo -n "Version mismatch in DATE: "; cat DATE; exit 1; fi + @if fgrep -s "This is version $(VERSION) of " README; then echo "Version $(VERSION) OK in README"; else echo "Version mismatch in README"; exit 1; fi + @if fgrep -s "CHANGES FOR VERSION $(VERSION)" CHANGES; then echo "Version $(VERSION) OK in CHANGES"; else echo "Version mismatch in CHANGES"; exit 1; fi + @if fgrep -s " for version $(VERSION) of" src/README; then echo "Version $(VERSION) OK in src/README"; else echo "Version mismatch in src/README"; exit 1; fi + @if fgrep -s "version $(VERSION)" src/xdoc/notangle.txt; then echo "Version $(VERSION) OK in src/xdoc/notangle.txt"; else echo "Version mismatch in src/xdoc/notangle.txt"; exit 1; fi + +nwsrcfilter: nwsrcfilter.icn + icont nwsrcfilter + +tarnames: clean source nwsrcfilter DATE + find . -not -type d -not -name FAQ.old -print | ./nwsrcfilter + +tar: clean source nwsrcfilter DATE emacscheck + chmod +w src/Makefile + rm -rf /tmp/noweb-$(VERSION) + mkdir /tmp/noweb-$(VERSION) + tar cvf - `find . ! -type d -not -name FAQ.old -print | ./nwsrcfilter` | (cd /tmp/noweb-$(VERSION) ; tar xf - ) + (cd /tmp; tar cf - noweb-$(VERSION) ) | gzip -v > ../noweb-$(VERSION).tgz + rm -f ../noweb.tgz + (cd .. ; ln -s noweb-$(VERSION).tgz noweb.tgz) + chmod -w src/Makefile + +ctan: clean source nwsrcfilter DATE emacscheck + chmod +w src/Makefile + (cd src; make boot) + rm -f ../noweb-$(VERSION)-ctan.zip + find ./* ! -type d -not -name FAQ.old -not -name '.git*' -print | ./nwsrcfilter | sed 's@^@noweb/@' | ( ln -s . noweb; zip ../noweb-$(VERSION)-ctan.zip -@; rm -f noweb ) + chmod -w src/Makefile + +emacscheck: + -echo "Checking to ensure distribution matches personal emacs mode" 1>&2 + diff src/elisp/noweb-mode.el $(HOME)/emacs/noweb-mode.el + +checkin: + (cd src; make "CINAME=$(CINAME)" "CIMSG=$(CIMSG)" checkin) + + diff --git a/web/noweb/README b/web/noweb/README new file mode 100644 index 0000000000..0ba30b32cb --- /dev/null +++ b/web/noweb/README @@ -0,0 +1,244 @@ +This is version 2.12 of ``noweb'', a simple, extensible literate- +programming tool. noweb is available from the Github repository +https://github.com/nrnrnr/noweb, and it is mirrored by the +Comprehensive TeX Archive Network. It is distributed under the BSD-2 +open-souce license. Other helpful information can be found on noweb's +home page at http://www.cs.tufts.edu/~nr/noweb. + +The most convenient way to get noweb is to install it from a package; +the procedure for building noweb from source dates from 1989, and it is +both baroque and brittle. If you must build noweb from source, change +to the `src' directory and follow the instructions in the file INSTALL. +(Windows users, look for http://www.jim-pettigrew.com/noweb/install.) + + +WHAT'S NEW IN VERSION 2.12 + +Version 2.12 now has a dual license: in addition to noweb's original +license, it is licensed under the BSD 2-clause license. + +Version 2.12 no longer defines a getline() function, which conflicted +with a new POSIX standard. And it no longer uses the deprecated +tempnam(3). + +Version 2.12 contains a number of enhancements to the sl2h program and +l2h filter, which convert from LaTeX to HTML. They now deal with +\suspend and \resume from \usepackage{mdwlist}, and they also convert +some TeX ligatures to HTML entities, most notably the -- and --- +ligatures. Thanks to Christian Lynbeck, 2.12 also has new Emacs Lisp +code that better supports font-lock. Other than that, 2.12 fixes a +number of bugs, the most notable of which is that the emacs noweb-mode +command is now compatible with Emacs 25 desktop-read and desktop-save. + + +INTRODUCTION --- WHAT IS NOWEB, ANYWAY? + +noweb is designed to meet the needs of literate programmers while +remaining as simple as possible. Its primary advantages are +simplicity, extensibility, and language-independence. noweb uses 5 +control sequences to WEB's 27. The noweb manual is only 3 pages; an +additional page explains how to customize its LaTeX output. noweb +works ``out of the box'' with any programming language, and supports +TeX, latex, and HTML back ends. A back end to support full hypertext +or indexing takes about 250 lines; a simpler one can be written in 40 +lines of awk. Noweb does not prettyprint natively, but supports +prettyprinting through such third-party filters as pretzel and dpp. + +noweb has been distributed free of charge for thirty years, and it is +one of the world's most widely used literate-programming tools. It +has been used for hundreds of thousands of lines of code in such +languages as awk, C, C++, Haskell, Icon, Lua, Modula-3, Objective +Caml, PAL, perl, Promela, and Standard ML. + +If you already know you want to use noweb, you need only install it +and read the manual page. If you're just curious about noweb, read +the paper that appeared in the September 1994 issue of IEEE Software. +(If you can't get Software, send me a postcard and I'll send you a +reprint.) A nice, brief tutorial appeared in the October 1997 issue +of Linux Journal (but beware that chunk syntax is <<name>> and not <<name>). + +If you're brand new to literate programming, check out the FAQ for the +USENET newsgroup comp.programming.literate. There are also some +resources available through the noweb home page: + http://www.cs.tufts.edu/~nr/noweb + + +WHAT YOU GET IN THIS DISTRIBUTION + +This distribution contains the following directories: + binaries Pre-built distributions containing binaries and documentation + contrib software contributed by noweb users + examples sample noweb filters and programs in different languages + src Source code and documentation (including FAQ) for noweb +Where appropriate, these directories have README files of their own. +BINARY DISTRIBUTIONS MAY NOT ALWAYS BE UP-TO-DATE, especially DOS +distributions. + + +EXTENSIBILITY + +noweb provides extensibility by using the Unix toolkit philosophy. +The ``noweb,'' ``notangle,'' and ``noweave'' commands are built from +pieces, which are then assembled in pipelines using shell scripts. +The pieces include: + + markup convert noweb file from human syntax to tool syntax + unmarkup inverse of markup + totex convert from tool syntax to TeX/latex markup + tohtml convert from tool syntax to HTML markup + nt `tangle' the tool form of the noweb file + mnt discover roots, then act like nt + noidx insert indexing and cross-reference information + finduses finds uses of identifiers + +These pieces are combined by the scripts in the src/shell directory to +provide more than just weaving and tangling: + + noweb analog of nuweb + notangle analog of TANGLE + noweave analog of WEAVE + nountangle tangle, but keep interleaved documentation in comments + noroots print names of all root chunks in a noweb file + nocount count number of lines of code and documentation. + nodefs extract defined identifiers for noweave -indexfrom + noindex build an external index for multi-file documents + + +WEAVING --- A TAR PIT + +The worst aspect of literate programming is the enormous amount of +time wasted wrangling over what ``woven'' output should look like. +Although noweb does no prettyprinting, it is not entirely immune--- +several people have complained about noweave's output or have sent me +changes that add more options to noweave. I resisted for years, but +with version 2.5 I finally succumbed. I let the number of options to +noweave double, and I have provided for too many options and hooks for +customizing the latex output. I won't let it happen again. + +noweb doesn't try to be all things to all programmers, but it is very +easy to change. If you don't like noweave's formatting, you can read +tex/support.nw to learn how to customize it; look for the words +``style hook.'' (Reading noweb.sty directly is not recommended.) For +simple formatting, it might be easier to throw away noweave and make +your own. To help you get started, the shell directory contains +noweave.simple, a simplified version of noweave that Dave Hanson +created for use with C programs (it can't handle code with @ signs). +The Noweb Hacker's Guide (xdoc/guide.tex) explains the intermediate +language that noweb uses to represent literate programs. + +The intermediate language makes it possible to extend noweave with a +language-dependent prettyprinter, as shown by contributions of an Icon +prettyprinter by Kostas Oikonomou and a guarded-command prettyprinter +by Conrado Martinez-Parra. (I haven't written a prettyprinter myself +because my experience with Spider left me thinking that prettyprinting +is far more trouble than it's worth.) Further contributions of +prettyprinters are welcome. + +noweb comes with two cross-referencers for use with noweave. The +standard one is written in Icon, because Icon offers excellent +functionality and performance. Because Icon is not available on all +platforms, I profide an alternative, but inferior cross-referencer +written in awk. See the INSTALL file for details. + +Cross-referencing makes formatting even more of a tar pit; the +cross-referencer itself takes about 300 lines, and extensive LaTeX +support is also required. I haven't made the attempt to write +cross-reference code for plain TeX. Anyone who has ideas for reducing +the number of options or for other ways to restore sanity to the +situation is urged to write to nr@cs.tufts.edu. + + +INDEX AND IDENTIFIER CROSS-REFERENCE + +Noweb creates identifier cross-references so that you can click on an +identifier and jump to its definition (if you're using printed LaTeX +output, a footnote gives you the page number of the definition). +To noweb, any string of nonwhite characters can be an identifier. +A human being or a language-dependent tool must mark definitions of +identifiers; noweb finds the uses using a language-independent +algorithm. The algorithm relies on an idea taken from the lexical +conventions of Standard ML. Characters are divided into three +classes: alphanumerics, symbols, and delimiters. If an identifier +begins with an alphanumeric, it must be delimited on the left by a +symbol or a delimiter. If it begins with a symbol, it must be +delimited on the left by an alphanumeric or a delimiter. If it begins +with a delimiter, there are no restrictions on the character +immediately to the left. Similar rules apply on the right-hand side. +The default classifications are chosen to make sense for commonly used +programming languages, so that noweb will not recognize `zip' when it +sees `zippy', or `++' when it sees `++:='. This trick works +surprisingly well, but it does not prevent noweb from spotting +identifiers in comments or string literals. + +The basic assumption in noweb is that a language-dependent filter will +identify definitions automatically. Filters for Icon, TeX, and yacc +all take about 30 lines of Icon code and are included in the noweb +distribution; if you write a filter of your own, you can put it in the +$LIB directory with a name like `autodefs.pascal'. Try `noweave +-showautodefs' for a complete list of such filters. + +If the automatic filter does not work well for you, or if there is no +filter available for your language, I recommend that you mark +definitions using backticks (`) in your source code, and use +`-filter btdefn' with both noweave and notangle. + +noweave -index works well for short programs, but nodefs, noindex, and +noweave -indexfrom are there for large multi-file programs. See the +noindex man page for details. + + +NOTES + + src/xdoc/techrep.* contains an early, almost unrecognizable version + of a paper describing noweb that appeared in IEEE Software in + September, 1994. You are probably better off writing me for a + reprint of the Software paper. (Send a postcard!) + + The Noweb Hacker's Guide, which appears in src/xdoc/guide.*, + documents the representation of noweb files that is used by the + noweb tools, in case you want to write any tools of your own. + Simple tools (e.g. count the number of lines of interleaved + documentation) are trivial. If you write any tools, or you want + tools written (e.g. prettyprinters, index generators), let me know. + + The icon directory contains Icon versions of most pipeline + stages. If you want to adapt noweb to work with a text processor + other than TeX or latex, they might provide a better starting point. + Perhaps the whole system should have been written in Icon from the + beginning, but I'm not going to do it over. Icon is available by + anonymous ftp from cs.arizona.edu. + + + Thanks to Preston Briggs for the Aho-Corasick recognizer, and for + helpful discussions. + Thanks to Dave Hanson for cpif. + Thanks to Dave Love for LaTeX wizardry. + Thanks to Joseph Reynolds for prodding me to fix [[...]]. + Thanks to Bill Trost for the original HTML back end. + Thanks to Phil Bewig for the troff support. + Thanks to Philip Miller and Lee Wittenberg for DOS binaries. + Thanks to Paolo Ciccone for the Win32 binaries. + Thanks to Hubert Chathi for picking up the Debian distribution. + +Send comments or questions to nr@cs.tufts.edu. + +I enjoy hearing from noweb users; if you have enjoyed noweb, why not +send me a local postcard for my collection? My address is: + + Norman Ramsey + Department of Computer Science + Tufts University + 161 College Avenue + Medford, MA 02155 + USA + + +COPYRIGHT + +Noweb is copyright 1989-2018 by Norman Ramsey. All rights reserved. + +You may use and distribute noweb for any purpose, for free. You may +modify noweb and create derived works, provided you retain the +copyright notice, but the result may not be called noweb without my +written consent. You may not sell noweb itself, but you may do +anything you like with programs created with noweb. diff --git a/web/noweb/contrib/Makefile b/web/noweb/contrib/Makefile new file mode 100644 index 0000000000..0e048da8ea --- /dev/null +++ b/web/noweb/contrib/Makefile @@ -0,0 +1,13 @@ +SHELL=/bin/sh +LIB=/dev/null # to be overridden +ICONC=icont # to be overridden +DIRS=davelove jonkrom leew norman + +# don't do kostas; it requires gnu make (ugh) + +all: ; for i in $(DIRS); do (cd $$i; make ICONC=$(ICONC) all); done +install: ; for i in $(DIRS); do (cd $$i; make LIB=$(LIB) BIN=$(BIN) install); done +source: ; for i in $(DIRS); do (cd $$i; make source); done +clean: ; for i in $(DIRS); do (cd $$i; make clean); done +clobber: clean + diff --git a/web/noweb/contrib/README b/web/noweb/contrib/README new file mode 100644 index 0000000000..7b5d7c97ed --- /dev/null +++ b/web/noweb/contrib/README @@ -0,0 +1,9 @@ +These directories contain software that was contributed by users of +noweb. Each directory contains a file called `email' that has the +email address of the contributor. I don't pretend to have copyright +rights, and I don't provide any warranty or any support. If you find +something useful here, we're both pleased. + +Some contributed software contains binaries or is too big to be +included in the standard noweb distribution. In such cases, the +directories here may contain only pointers to anonymous ftp sites. diff --git a/web/noweb/contrib/avs/email b/web/noweb/contrib/avs/email new file mode 100644 index 0000000000..9c280ceffc --- /dev/null +++ b/web/noweb/contrib/avs/email @@ -0,0 +1,2 @@ +avs@daimi.aau.dk (Alexandre Valente Sousa) +If this doesn't work (in a couple of years it won't) try avs@monet.inescn.pt diff --git a/web/noweb/contrib/avs/filelist.txt b/web/noweb/contrib/avs/filelist.txt new file mode 100644 index 0000000000..73ee43dd75 --- /dev/null +++ b/web/noweb/contrib/avs/filelist.txt @@ -0,0 +1,59 @@ +File list for noweb/contrib/avs +This is not in Noweb 2.7a but it has been proposed to be in the contrib dir +Thus these files are from ftp.daimi.aau.dk:/pub/empl/avs/avs386_noweb27a.tar.gz + +Although the target is Dos I use .tar.gz format instead of .arj or .zip because +anyhow you need DJGPP (which has gzip) and MKS (which has tar) to use this. For +your convenience just in case (i.e. you only have MKS yet and will be using +my instructions to get DJGPP) I supply the file +ftp.daimi.aau.dk:/pub/empl/avs/gzip386.exe' (you should rename it to gzip.exe) + + CHANGE THIS: +myenv.ksh --> edit this file for your environment (Korn shell script) + + DOCUMENTATION: +readme --> brief description +email --> my contact info +norman1.txt --> mail sent to Norman Ramsey describing this Dos HOWTO +filelist.txt --> this file +howto386.txt --> documentation/troubleshooting/explanation/Dos recipe +ftpsites.txt --> where to get additional software that might be required + + ADDITIONAL DOCUMENTATION: +icon.1 --> NROFF processed man file for Icon (because we are using it) +jrtex12a.avs --> excellent J. Refling's PC386 LaTeX2e HOWTO annotated by me +mks42bug.0d --> text file with my personal list of MKS 4.2 bugs +report1.bug --> bug report for noweb 2.7a + + BUILD & INSTALL SCRIPTS (used by myenv.ksh): +mksfixes.ksh --> fix noweb.ksh and cpif.ksh for the MKS Toolkit 4.2 +generate.ksh --> generates 'automate.bat' (the build/install script) +nwicon.bat --> Make Icon code (the '\\\\' hack) +nw_c.bat --> Patch DJGPP bug & Make C code & avoid out of memory errors +nwinst.ksh --> install Noweb (just to separate install from build) +make_ico.awk --> patches noweb/src/icon/makefile for Ms-Dos +make_src.awk --> patches noweb/src/makefile for Ms-Dos +make_xdo.awk --> patches noweb/src/xdoc/makefile for Ms-Dos +make_lib.awk --> patches noweb/src/lib/makefile for Ms-Dos + + 2 17 122 email + 59 353 2735 filelist.txt + 63 412 2978 ftpsites.txt + 114 687 4883 generate.ksh + 254 2270 13918 howto386.txt + 330 1243 10462 icon.1 + 1054 5692 43470 jrtex12a.avs + 50 297 1694 make_ico.awk + 12 80 498 make_lib.awk + 72 509 2832 make_src.awk + 14 104 593 make_xdo.awk + 128 1219 7347 mks42bug.0d + 14 110 714 mksfixes.ksh + 38 321 1923 myenv.ksh + 136 1409 8168 norman1.txt + 62 288 2077 nw_c.bat + 43 201 1411 nwicon.bat + 9 33 253 nwinst.ksh + 39 302 1807 readme + 97 796 4554 report1.bug + 2590 16343 112439 total diff --git a/web/noweb/contrib/avs/ftpsites.txt b/web/noweb/contrib/avs/ftpsites.txt new file mode 100644 index 0000000000..8abcbd4bdc --- /dev/null +++ b/web/noweb/contrib/avs/ftpsites.txt @@ -0,0 +1,65 @@ +NOTE: The information below is from May 30, 1995. It is likely to be +obsolete. And to appease the maintainers of CTAN, references to ftp +dot tex dot ac dot uk have been removed. +---------------------------------------------------------------- +These are the anonymous ftp sites that I prefer (I am in Europe and these are +the fastest sites for me) + +Noweb 2.7a (only in this site in 29-May-95) +ftp.shsu.edu:/pub/tex/web + +Noweb 2.7 +(No longer available. Try https://github.com/nrnrnr/noweb, tag v2_7, +or better yet, a version that is up to date.) + +How to build Noweb 2.7a for Dos + PC386 + MKS toolkit +ftp.daimi.aau.dk:/pub/empl/avs/avs386_noweb27a.tar.gz +(my site, just for completeness, you might also want to look there for a newer +version in case some bug in my scripts has been reported and fixed) + +emTeX 386 3.1415 [3c-beta12], LaTeX2e (1-Dec-94 patch level 1), Dvips 5.54 +(several ftp sites must be used, see the file 'jrtex12a.avs') + +Chicago LaTeX package (required by noweb/src/xdoc/guide.tex) +CTAN +Assuming that you followed the instructions from the file 'jrtex12a.avs' put +the 3 files (chicago.bst, chicago.sty, chicagoa.bst) in +?:/emtex/texinputs/local) + +DJGPP (GNU software Dos port for PC 386) +ftp.funet.fi:/pub/mirrors/oak.oakland.edu/Simtel/msdos/djgpp (empty in +21-Mar-95 due to disk crash) +or +omnigate.clarkson.edu:/pub/msdos/djgpp +or +oak.oakland.edu:/pub/msdos/djgpp +The easiest way is to get the README's and FAQ (readme.1st, readme.dj, +djgpp.faq), to read them, and then to get the installation program +(install.exe, install.dat) and the remaining binaries and docs (the minimum set +of files that you need is the first FAQ entry, you will also need the Make +module, and if you have a 386 without a coprocessor the 80387 emulator) + +Binaries for Icon 9.0 for MsDos 386/486: +cs.arizona.edu:/icon/packages/msdos/de-386.lzh +(if you don't have lha.exe to unpack the archive you should also get it from +there) + +The MKS Toolkit for Dos is commercial software (Sorry) +Don't know which is the last version, I use 4.2 (Oct-93) +Tel: (519) 884-2251 Mortice Kern Systems Inc. +Fax: (519) 884-8861 35 King Street North, +Technical Advice: (519) 884-2270 Waterloo, Ontario, +Internet: inquiry@mks.com N2J 2W9 +CompuServe User ID: 73260,1043 CANADA +BIX User Name: mks + +GhostScript 3.12 +ftp.funet.fi:/gnu/ghostscript3/aladdin +If you don't have a PostScript printer you might want to use GhostScript which +can take PostScript and translate it to your printer (presuming your printer is +somehow supported). See 'jrtex12a.avs' for a list of the devices in the gs.exe, +gs386.exe, gswin.exe, and gswin32s.exe binaries. Notice that gs.exe crawls when +processing noweave output (after intermediate processing by latex and dvips), +while gs386.exe is about 40 times faster (about 1 page/second in a 486 +DX2-80). This speed difference does not apply to normal PostScript code it is +caused by something in the noweb code diff --git a/web/noweb/contrib/avs/generate.ksh b/web/noweb/contrib/avs/generate.ksh new file mode 100644 index 0000000000..8bf6050166 --- /dev/null +++ b/web/noweb/contrib/avs/generate.ksh @@ -0,0 +1,114 @@ +# do not use directly, it is better to edit the file 'myenv.ksh' + +if [ -e ../../contrib/avs/$0.ksh -o -e ../../contrib/avs/$0 ] +then +else + echo Wrong dir, must run \'$0\' from noweb/contrib/avs dir + exit 1 +fi + +if [ -z "$7" ] +then + echo Usage: $0 BIN LIB MAN TEXINPUTS GMAKEPATH TMP ICONTRANSLATORPATH + echo If your environment is OK installs noweb27 in your PC386 + echo "(icont.exe, iconx.exe, ixhdr.exe in dir e:\\b), e.g." + echo " $0 i:/b g:/usr/local/lib/noweb g:/man h:/emtex/texinputs/local j:/djgpp/bin/make.exe d:/tmp e:/b/icont.exe" + echo "(the '.exe' in make.exe and icont.exe is not necessary)" + exit 1 +fi + +cd ../.. +# now one is at ./noweb + +echo "Renaming src/makefile src/icon/makefile src/install src/xdoc/makefile src/lib/makefile src/awkname to *.old:" +for f in src/makefile src/icon/makefile src/install src/xdoc/makefile src/lib/makefile src/awkname +do + if [ -e $f.old ] + then + echo File \'$f.old\' already exists, skipped + else + mv $f $f.old + fi +done +echo Done! + +echo "Adding Dos specific makefiles and scripts for the combination MKS/DJGPP/ICONT:" +awk -f contrib/avs/make_ico.awk src/icon/makefile.old > src/icon/makefile +awk -f contrib/avs/make_src.awk src/makefile.old > src/makefile +awk -f contrib/avs/make_xdo.awk src/xdoc/makefile.old > src/xdoc/makefile +awk -f contrib/avs/make_lib.awk src/lib/makefile.old > src/lib/makefile +cp -p contrib/avs/nw_c.bat src +cp -p contrib/avs/nwinst.ksh src +cp -p contrib/avs/nwicon.bat src +echo Done! + +cd src +echo "Adapting awkname for Dos and changing the awk name to 'awk' in all scripts:" +sed "s@new=/tmp/\$\$.new; old=/tmp/\$\$.old@new=$6/\$\$.new; old=$6/\$\$.old@" <awkname.old >awkname +# because the script has no '.ksh' extension one has to use 'awkname.' +sh -c "./awkname. awk" +echo Done! + +echo "Touch'ing all *.nw source code to avoid potential date/time problems:" +echo "(all of it gets dummy date 12:00 23-Feb-95)" +find . -name "*.nw" -exec touch -t 9502231200 "{}" \; +echo Done! + +echo "Touch'ing all *.1 man pages to avoid potential date/time problems:" +echo "(all of it gets dummy date 12:00 24-Feb-95)" +find xdoc -name "*.1" -exec touch -t 9502241200 "{}" \; +echo Done! + +cd ../contrib/avs +echo "Generating 'contrib/avs/automate.bat' (to avoid out of memory errors):" +echo "@echo off" >automate.bat +echo "REM This file was generated by $0" >>automate.bat +echo "cd ..\\\\..\\\\src" >> automate.bat + +echo "echo ***" >>automate.bat +echo "echo *** Make icon code" >>automate.bat +echo "echo ***" >>automate.bat + +# Trying to get something like: +# call nwicon i:/b g:/usr/local/lib/noweb j:\djgpp\bin\make.exe e:\\\\b\\\\icont.exe +# The '\c' arg to echo means not to add a \n at the end +# Careful not to allow echo to interpret e.g. djgpp\bin as having an embedded backspace (\b) +# 'sed' adds a \n line to its output, that's why tr was needed +echo call nwicon $1 $2 \\c >>automate.bat +echo $5 \\c | sed 's#/#\\#g' | tr -d '\015\012' >>automate.bat +echo $7 | sed 's#/#\\\\\\\\#g' >>automate.bat + +echo if errorlevel 1 goto FAILURE >>automate.bat +echo "echo ***" >>automate.bat +echo "echo *** Make C code" >>automate.bat +echo "echo ***" >>automate.bat +echo set DJGPPMAKE=$5 >>automate.bat +echo call nw_c $5 | sed 's@/@\\@g' >>automate.bat +echo if errorlevel 1 goto FAILURE >>automate.bat +echo "echo ***" >>automate.bat +echo "echo *** Installing noweb" >>automate.bat +echo "echo ***" >>automate.bat +echo sh -c \"./nwinst.ksh $1 $2 $3 $4\" >>automate.bat +echo if errorlevel 1 goto FAILURE >>automate.bat +echo "echo ***" >>automate.bat +echo "echo *** Fixing $1/cpif.ksh and $1/noweb.ksh as documented in 'howto386.txt'" >>automate.bat +echo "echo ***" >>automate.bat +echo "cd ..\\\\contrib\\\\avs" >>automate.bat +echo sh -c \"./mksfixes.ksh $1 $6\" >>automate.bat +echo if errorlevel 1 goto FAILURE >>automate.bat +echo "echo Success, noweb 2.7a built & installed! Now use 'man noweb'" >>automate.bat +echo 'echo Noweb 2.7| banner -c n | sed' "'s/[ ]$//'" >>automate.bat +echo goto THEEND >>automate.bat +echo :FAILURE >>automate.bat +echo "echo Previous command failed (non 0 exit code), out of memory?" >>automate.bat +echo echo Aborting... sorry you have to manually fix the problem >>automate.bat +echo "echo (and after that go to noweb/contrib/avs and rerun automate.bat)" >>automate.bat +echo :THEEND >>automate.bat + +echo "****" +echo "The file 'automate.bat' has been generated, now LEAVE the Korn shell (to avoid" +echo "out of memory errors) and run it. If 'automate.bat' fails at some point, e.g." +echo "out of memory, then you may try to fix the problem by hand e.g. calling the C" +echo "compiler directly by looking at the previous output from Make, and then to" +echo "rerun 'automate'. You can run 'automate.bat' as many times as you need until" +echo "you reach the end with success" diff --git a/web/noweb/contrib/avs/howto386.txt b/web/noweb/contrib/avs/howto386.txt new file mode 100644 index 0000000000..49dedea19d --- /dev/null +++ b/web/noweb/contrib/avs/howto386.txt @@ -0,0 +1,254 @@ +** Recipe for building and installing noweb 2.7a in a PC386 running Dos +** Recipe version 0.3 (30-May-95), report problems to avs@daimi.aau.dk + +This recipe assumes its support files are at './noweb/contrib/avs'. If they are +not (as is the case with the noweb 2.7a distribution) then just unpack +noweb27a.tar.gz (official distribution) and avs386_noweb27a.tar.gz (my +contribution) from the same base dir + +I'm only doing a minimum patch of the noweb source files and installation +scripts to get a successful install (binaries plus support files plus man +pages). I do not patch things that I am not likely to need like support for +'make clean' or changing the '.nw' files in the sources dir. Also I didn't try +to install the contributed software from the contrib dir. After noweb is +successfully installed just remove the distribution files from your system (it +is better to archive it somewhere, there are some docs, examples and contribs +that might be useful later) + +Bugs/problems with noweb 2.7a: see the file 'report1.bug' + +This recipe supports Awk but that option is not thoroughly tested. Why should I +use Awk if the Noweb distribution explicitly says that the Awk versions of the +tools are untested and so probably have bugs? Compiling Icon for Ms-Dos is not +easy but one can ftp the binaries (only 512 KB) + +** History: + +0.3 recipe for noweb 2.7 (30-May-95) +0.2 recipe for noweb 2.7 (26-Mar-95), internal use only +0.1 recipe for noweb 2.6c (12-Dec-94), internal use only + +**** Software **** + +Requires: +- MKS Toolkit 4.2 for Dos (older versions of MKS might not work) +- the DJGPP port of GNU gcc, GNU make, and GNU gzip +- version 9.0 of Icon for MsDos 386/486, +- LaTeX (LaTeX 2.09 or LaTeX2e) + +LaTeX: I use emTeX 386, the installation is everything except straightforward, +if you don't already have LaTeX in your PC the best way to get it up and +running is by using John Refling's jrtex12a.txt ("How I installed emtex, +latex2e, mf, dvips, on a 386 with postscript or hplaser"). I supply an +annotated copy of that document with some corrections and some additions +made by me in the file 'jrtex12a.avs' + +Because MKS does not have nroff, a nroff processed man page for Icon is +provided here. (I also provide mks42bug.0d which has a list of all the MKS 4.2 +bugs that I am aware of, some of them required a fix in this recipe) + +See the file 'ftpsites.txt' for download info for the additional software + +**** Install **** + +NOTE: my MKS installation allows me to switch easily between using the MKS +Korn-shell (sh.exe) and the Ms-Dos command interpreter (command.com), i.e. I +boot with command.com and then I run login to get the Korn shell and by logging +out I am back in command.com. You need the ability to switch between using +command.com and sh.exe because some software has problems with the MKS pathname +separator '/'. This is why the steps normally specify if they are to be run +under command.com or sh.exe. If nothing is said it would work under +either. Also command.com uses much less memory than sh.exe, for instance I am +unable to compile the C code when running under sh.exe (this has to do with the +640KB MsDos limit, I have lots of extended memory and a good memory manager) + +Change to some temporary directory (if '.' is that dir then all files will be +under './noweb') and extract the distribution (the extension .tar.gz was +changed to .tgz to comply with Ms-Dos filenames, 386avs27.tgz is +avs386_noweb27.tar.gz): + +cd ... +gzip -dc noweb27.tgz | tar xvf - +gzip -dc 386avs27.tgz | tar xvf - + (my files are not part of noweb 2.7) +cd ./noweb/contrib/avs + +All my files are at ./noweb/contrib/avs, see the file 'filelist.txt' + +If you feel lucky edit the site specific line of the file 'myenv.ksh' +(i.e. specify the paths where things are, and where things will go) and run +it. Then run the generated file 'automate.bat'. If things work as they ought +to, you can stop reading. + +**** Troubleshooting **** + +Print this file and go through it step by step (I'm explaining what 'myenv.ksh', +'automate.bat' and the scripts called by these are doing) + +a) change to ./noweb/src directory and edit 'awkname' + (this script uses '/tmp', if your tmp dir is not in the same drive as noweb, + replace in line 8 + new=/tmp/$$.new; old=/tmp/$$.old + with e.g. + new=c:/tmp/$$.new; old=c:/tmp/$$.old + (Notice that although one is using Icon instead of Awk line 26 of + noweb.ksh, line 7 of noroots.ksh and line 32 of nountang.ksh use Awk + anyway, so it's easier to update the awk name everywhere) + +b) run the shell script 'awkname' under the Korn shell with 'awk' as argument + (because the Korn shell awk is named awk). Notice that because the awkname + script does not have a .ksh extension one has to explicitly supply a '.' as + the extension name, otherwise the shell won't find it, e.g. + cd ./noweb/src; ./awkname. awk + +c) rename src/makefile src/icon/makefile src/install src/xdoc/makefile + src/awkname to *.old. And then run the awk scripts to patch the makefiles + (each action in each script has a comment explaining what it is doing and + why). The file 'install' had to be renamed to avoid confusing MKS make when + it tries to run 'make install'. + awk -f contrib/avs/make_ico.awk src/icon/makefile.old > src/icon/makefile + awk -f contrib/avs/make_src.awk src/makefile.old > src/makefile + awk -f contrib/avs/make_xdo.awk src/xdoc/makefile.old > src/xdoc/makefile + +d) copy the scripts to the /noweb/src dir (you need to run them from there) + cp -p contrib/avs/nw_c.bat src + cp -p contrib/avs/nwinst.ksh src + cp -p contrib/avs/nwicon.bat src + +e) run nwicon.bat under command.com, run it 1st without args to get an usage + screen, and then provide the correct args (careful with the number and use + of slashes and backslashes). This compiles the icon code + (Concerning the location of your icon binaries: if it is d:/bin/icont.exe + and d:/bin/ixhdr.exe then use d:\\\\bin\\\\icont, '.exe' extension not + necessary. Sorry about the \\\\, but believe me that's the only way I + managed for it to work, see below for an explanation. Give also the + location of your DJGPP make, and the locations where later on you want to + install the noweb LIB and the noweb BIN) + +f) run nw_c.bat under command.com without args to get the usage screen, then + use the 'set' command and then rerun nw_c.bat. This compiles the C code + (if I try to run under the shell instead of command.com and the makefile + tries to compile a C program I get out of memory errors, i.e. this is the + place where you need more free Ram, 600000 bytes free is enough) + (the full pathname of DJGPP Make is given twice, the one with Unix style + slashes goes in the environment var and the one with MsDos style + backslashes is given as arg. Notice that an environment variable called + DJGPPMAKE is set, make sure that you have enough space for it, otherwise + increase the value used in the /E:### option of the SHELL command in + your config.sys) + (nw_c.bat also patches src/c/finduses.c because of a problem with DJGPP + tmpfile() in libc.a, the screen will show what is being changed. This + might not be necessary under a newer version of DJGPP (I did not yet + install the last DJGPP maintenance patches) but it is better to do it + anyway because tempnam() uses the environment var TMPDIR, while + tmpfile() doesn't) + +g) run nwinst.ksh under the Korn shell (sh.exe), there is an usage screen. You + can also run it from command.com using 'sh -c "./nwinst.ksh ..."'. This + installs the icon based version of noweb (some files will need to be patched + afterwards, see below). + (because an Unix style shell and MKS make are needed) + (make sure that MKS make is the 1st make in your path, see Technical + Notes below) + (if you prefer to use Awk instead of Icon update the scripts yourself. + Notice that although Awk is slower than Icon, MKS has a 32 bits version + of Awk which might be good enough, and MKS has also an Awk compiler + (awkc, see 'man awkc'), thus by removing from the shell scripts the + awk code, puting it into a file, compiling it, and updating the shell + scripts to use that executable you might get good performance, I didn't + try to use Awk thus I don't know) + (in noweb27.tar.gz in the file "install.dos" there are some references + to an MKS awk bug in handling backslashes in gsub(), I was unable to find + the code that it refered to in the noweb distribution (did it only apply + to an older version of noweb?), but anyhow you should check it out if you + are going to use awk) + +h) fix cpif.ksh and noweb.ksh by running mksfixes.ksh (from noweb/contrib/avs), + there is an usage screen. This edits the noweb.ksh in the BIN location + specified to nwinst.ksh and removes twice (in lines 20 and 25) the + PATH="$PATH:$LIB" + which is not necessary and causes the error 'cannot execute go32' at run-time + (go32 is the Dos extender used by DJGPP). It also edits cpif.ksh (also in the + BIN location) and: + - removes the PATH statement in line 8 (because 'PATH=/bin:/usr/bin' is + probably wrong for your system) + - adds a full pathname (with a drive letter) to "new=/tmp/$$" in line 20 + (e.g. "new=d:/tmp/$$"), because otherwise the script will fail when run + from a drive which has no tmp dir + - because of a bug in MKS 4.2 changes line 28 from + -eq0|-ne1|*2) cp $new $i + to + -eq0|-ne1|*2|*3) cp $new $i + Maybe you would like to check out if that bug applies to your system, + in MKS 4.2 the program cmp ("cmp.exe") gives an exit code of 3 if one of + the files to be compared cannot be opened or doesn't exist, this is in + contrast with the MKS man page which says that an error code of 2 is + given (to fix this bug is very important otherwise e.g. the command noweb + will never create the TeX file) + +i) copy './noweb/contrib/avs/icon.1' to '.../yourman/cat1' and 'mks42bug.0d' to + '.../yourman/cat0' and add '.../yourman' to your MANPATH. + +j) test the installation by running noweb/examples/... + (you must run noweb under the Korn shell. I have enough free RAM to run + everything including the Dos extenders of Icon, DJGPP Emacs, emTeX, etc. + Probably this happens because 'mem' run from the Korn Shell reports + 590272 free bytes (of the 640 KB), and I have enough free EMS. I use + QEMM 7.5 as the memory manager) + +**** Technical Notes: **** + +- DJGPP Make can be in your path but it can't be 1st otherwise install.ksh does +not work +- noweb/install renamed to noweb/install.old to avoid confusing MKS Make when + trying to execute 'make install' +- it is assumed that MKS Make is the 1st Make in your path +- nw_c.bat does not run coff2exe, thus you CANNOT test the binaries BEFORE + you install them, i.e. you don't get the .exe files. This avoids the need to + change the src/c/makefile + When you run install.ksh, coff2exe will be run for all binaries (i.e. a stub + will be added to them which will call the Dos extender go32.exe) +- if you have problems with the 127 chars PATH limit do: + Command.com 6.0 can take a PATH longer than 127 chars by setting it in the + config.sys (but it is not possible to change it afterwards from e.g. + autoexec.bat or the command line), also some programs might crash with this + extra long path and one can only see the 1st 122 chars (although all are used + in a search) + The Ndos shareware command interpreter can have a PATH 256 chars long. + +- main changes made to the Unix noweb/src/makefile to create the Dos Makefile: + (see also make_src.awk) +a) in some places of the makefile quotes had to be removed (e.g. instead of + "CFLAGS=$(CFLAGS)" one has to use CFLAGS=$(CFLAGS), this is caused by the + use of 'command.com' as the shell while building noweb). Of course if using + more than one CFLAGS (i.e. embedded blanks) this won't work (the easiest and + dirty fix is to add them directly to the makefile in that case) +b) MKS strip cannot be used on the DJGPP .exe, otherwise go32 would fail +c) it is an hybrid makefile, 'make all' only works with DJGPP Make and + 'command.com', while 'make install' only works with MKS Make and the MKS + Korn Shell +d) 'SHELL=/bin/sh' had to be disabled (e.g. if MKS $ROOTDIR is not '/') +e) links (used in the man pages) not supported under MsDos, copy was used +f) there is also an external 'cd.exe' in the MKS Toolkit (don't ask me what + it can be used for, it is just a source of problems). This gives problems + that the MKS make will use it instead of the internal Korn-shell cd command + unless the line to be executed is such that it requires the Korn-shell to + interpret it. This is why: + cd c; coff2exe nt markup mnt finduses + won't work, but + cd "c"; coff2exe nt markup mnt finduses + works fine + +- main changes made to the noweb/src/icon/makefile (see also make_ico.awk): +a) cannot run under the Korn shell otherwise icont.exe becomes confused about + the current directory +b) when executing icont.exe if argv[0] has slashes (instead of backslashes) + icont.exe runs OK but at the end it is unable to run ixhdr.exe thus it fails +c) I got several spurious CPU locks when calling make from make while executing + icont.exe, that's why only one make is used +d) icont must be replaced with the full path (with backslashes) to itself, +e) the option '-I' to the 'icont.exe' is not documented in the Icon man page + (see section 3 of document IPD248a from the Icon project), it produces a + non-executable icode file (.icx) which can be run by iconx.exe + +**** The End **** diff --git a/web/noweb/contrib/avs/icon.1 b/web/noweb/contrib/avs/icon.1 new file mode 100644 index 0000000000..04c5465e6d --- /dev/null +++ b/web/noweb/contrib/avs/icon.1 @@ -0,0 +1,330 @@ +} + + + IIIICCCCOOOONNNN((((1111)))) IIIICCCCOOOONNNN((((1111)))) + 11113333 MMMMaaaarrrrcccchhhh 1111999999993333 IIIIPPPPDDDD222211119999 + + + + NNNNAAAAMMMMEEEE + icon - interpret or compile Icon programs + + SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS + icont [ option ... ] file ... [ -x arg ... ] + iconc [ option ... ] file ... [ -x arg ... ] + + DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN + icont and iconc each convert an Icon source program into executable + form. icont translates quickly and provides interpretive execution. + iconc takes longer to compile but produces programs that execute + faster. icont and iconc for the most part can be used + interchangeably. + + This manual page describes both icont and iconc. Where there there are + differences in usage between icont and iconc, these are noted. + + FFFFiiiilllleeee NNNNaaaammmmeeeessss:::: Files whose names end in .icn are assumed to be Icon + source files. The .icn suffix may be omitted; if it is not present, it + is supplied. The character - can be used to indicate an Icon source + file given in standard input. Several source files can be given on + the same command line; if so, they are combined to produce a single + program. + + The name of the executable file is the base name of the first input + file, formed by deleting the suffix, if present. stdin is used for + source programs given in standard input. + + PPPPrrrroooocccceeeessssssssiiiinnnngggg:::: As noted in the synopsis above, icont and iconc accept + options followed by file names, optionally followed by -x and + arguments. If -x is given, the program is executed automatically and + any following arguments are passed to it. + + icont: The processing performed by icont consists of two phases: + _t_r_a_n_s_l_a_t_i_o_n and _l_i_n_k_i_n_g. During translation, each Icon source file is + translated into an intermediate language called _u_c_o_d_e. Two ucode files + are produced for each source file, with base names from the source + file and suffixes .u1 and .u2. During linking, the one or more pairs + of ucode files are combined to produce a single _i_c_o_d_e file. The ucode + files are deleted after the icode file is created. + + Processing by icont can be terminated after translation by the -c + option. In this case, the ucode files are not deleted. The names of + .u1 files from previous translations can be given on the icont command + line. These files and the corresponding .u2 files are included in the + linking phase after the translation of any source files. The suffix + .u can be used in place of .u1; in this case the 1 is supplied + automatically. Ucode files that are explicitly named are not deleted. + + iconc: The processing performed by iconc consists of two phases: _c_o_d_e + _g_e_n_e_r_a_t_i_o_n and _c_o_m_p_i_l_a_t_i_o_n _a_n_d _l_i_n_k_i_n_g. The code generation phase + + + + - 1 - Formatted: December 9, 1994 + + + + + + + IIIICCCCOOOONNNN((((1111)))) IIIICCCCOOOONNNN((((1111)))) + 11113333 MMMMaaaarrrrcccchhhh 1111999999993333 IIIIPPPPDDDD222211119999 + + + + produces C code, consisting of a .c and a .h file, with the base name + of the first source file. These files are then compiled and linked to + produce an executable binary file. The C files normally are deleted + after compilation and linking. + + Processing by iconc can be terminated after code generation by the -c + option. In this case, the C files are not deleted. + + OOOOPPPPTTTTIIIIOOOONNNNSSSS + The following options are recognized by icont and iconc: + + -c Stop after producing intermediate files and do not delete them. + + -e _f_i_l_e + Redirect standard error output to _f_i_l_e. + + -m Preprocess each .icn source file with the _m_4(_1) macro processor. + + -o _n_a_m_e + Name the output file _n_a_m_e. + + -s Suppress informative messages. Normally, both informative + messages and error messages are sent to standard error output. + + -t Arrange for &trace to have an initial value of -1 when the program + is executed and for iconc enable debugging features. + + -u Issue warning messages for undeclared identifiers in the program. + + -E Direct the results of preprocessing to standard output and inhibit + further processing. + + The following additional options are recognized by iconc: + + -f _s_t_r_i_n_g + Enable features as indicated by the letters in _s_t_r_i_n_g: + + a all, equivalent to delns + + d enable debugging features: display(), name(), variable(), + error trace back, and the effect of -f n (see below) + + e enable error conversion + + l enable large-integer arithmetic + + n produce code that keeps track of line numbers and file names + in the source code + + s enable full string invocation + + + + + - 2 - Formatted: December 9, 1994 + + + + + + + IIIICCCCOOOONNNN((((1111)))) IIIICCCCOOOONNNN((((1111)))) + 11113333 MMMMaaaarrrrcccchhhh 1111999999993333 IIIIPPPPDDDD222211119999 + + + + -n _s_t_r_i_n_g + Disable specific optimizations. These are indicated by the letters + in _s_t_r_i_n_g: + + a all, equivalent to cest + + c control flow optimizations other than switch statement + optimizations + + e expand operations in-line when reasonable (keywords are always + put in-line) + + s optimize switch statements associated with operation + invocations + + t type inference + + -p _a_r_g + Pass _a_r_g on to the C compiler used by iconc + + -r _p_a_t_h + Use the run-time system at _p_a_t_h, which must end with a slash. + + -v _i + Set verbosity level of informative messages to _i + + -_C _p_r_g + Have iconc use the C compiler given by _p_r_g + + EEEENNNNVVVVIIIIRRRROOOONNNNMMMMEEEENNNNTTTT VVVVAAAARRRRIIIIAAAABBBBLLLLEEEESSSS + When an Icon program is executed, several environment variables are + examined to determine certain execution parameters. Values in + parentheses are the default values. + + BLKSIZE (65000) + The initial size of the allocated block region, in bytes. + + COEXPSIZE (2000) + The size, in words, of each co-expression block. + + DBLIST + The location of data bases for iconc to search before the standard + one. The value of DBLIST should be a blank-separated string of + the form _p_1 _p_2 ... _p_n where the _p_i name directories. + + ICONCORE + If set, a core dump is produced for error termination. + + ICONX + The location of iconx, the executor for icode files, is built into + an icode file when it is produced. This location can be overridden + + + + - 3 - Formatted: December 9, 1994 + + + + + + + IIIICCCCOOOONNNN((((1111)))) IIIICCCCOOOONNNN((((1111)))) + 11113333 MMMMaaaarrrrcccchhhh 1111999999993333 IIIIPPPPDDDD222211119999 + + + + by setting the environment variable ICONX. If ICONX is not set + and iconx is not found on the built-in path, PATH is searched for + it. If this environment variable is set, it specifies the + location of iconx to use to execute an icode file. + + IPATH + The location of ucode files specified in link declarations for + icont. IPATH is a blank-separated list of directories. The + current directory is always searched first, regardless of the + value of IPATH. + + LPATH + The location of source files specified in link declarations for + iconc. LPATH is otherwise similar to IPATH. + + MSTKSIZE (10000) + The size, in words, of the main interpreter stack for icont. + + NOERRBUF + By default, &errout is buffered. If this variable is set, &errout + is not buffered. + + QLSIZE (5000) + The size, in bytes, of the region used for pointers to strings + during garbage collection. + + STRSIZE (65000) + The initial size of the string space, in bytes. + + TRACE + The initial value of &trace. If this variable has a value, it + overrides the translation-time -t option. + + FFFFIIIILLLLEEEESSSS + icont Icon translator + iconc Icon compiler + iconx Icon executor + + SSSSEEEEEEEE AAAALLLLSSSSOOOO + _T_h_e _I_c_o_n _P_r_o_g_r_a_m_m_i_n_g _L_a_n_g_u_a_g_e, Ralph E. Griswold and Madge T. + Griswold, Prentice-Hall Inc., Englewood Cliffs, New Jersey, Second + Edition, 1990. _V_e_r_s_i_o_n _8._1_0 _o_f _I_c_o_n, Ralph E. Griswold, Clinton L. + Jeffery, and Gregg M. Townsend, IPD212, Department of Computer + Science, The University of Arizona, 1993. _U_s_i_n_g _V_e_r_s_i_o_n _8._1_0 _o_f _t_h_e + _I_c_o_n _C_o_m_p_i_l_e_r, Ralph E. Griswold, IPD214, Department of Computer + Science, The University of Arizona, 1993. m4(1), icon_vt(1) + + LLLLIIIIMMMMIIIITTTTAAAATTTTIIIIOOOONNNNSSSS AAAANNNNDDDD BBBBUUUUGGGGSSSS + The icode files for the interpreter do not stand alone; the Icon run- + time system (iconx) must be present. Stack overflow is checked using + a heuristic that is not always effective. If the -m option is used, + + + + - 4 - Formatted: December 9, 1994 + + + + + + + IIIICCCCOOOONNNN((((1111)))) IIIICCCCOOOONNNN((((1111)))) + 11113333 MMMMaaaarrrrcccchhhh 1111999999993333 IIIIPPPPDDDD222211119999 + + + + line numbers reported in error messages and tracing messages are from + the file after, not before, preprocessing. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 5 - Formatted: December 9, 1994 + + + diff --git a/web/noweb/contrib/avs/jrtex12a.avs b/web/noweb/contrib/avs/jrtex12a.avs new file mode 100644 index 0000000000..a9d7131aee --- /dev/null +++ b/web/noweb/contrib/avs/jrtex12a.avs @@ -0,0 +1,1054 @@ +**** NOTE: this is the superb document 'jrtex12a' from John Refling. I just +**** fixed a couple of bugs and added a few comments (for my own use). The +**** changes are marked with '****'. The biggest problem was that his setup +**** didn't contemplate installing emTeX in another drive than C, nor did it +**** contemplate putting the fonts generated on demand on yet another drive +**** (handy if one wants emTeX to be in a READONLY drive) +**** +**** If you only have a 386 without coprocessor be careful with some +**** delete instructions (which assume you have a 387 or a 486DX) +**** +**** Known problems: +**** If running under the MKS Toolkit shell, font generation on demand may +**** fail. I don't have this problem any more (don't know what fixed it, +**** the new version of emTeX?) but I had it in the past. Just do the font +**** generation under 'command.com' and then go back to the Korn shell +**** avs@daimi.aau.dk (18-Mar-95) + +How I installed emtex, latex2e, mf, dvips, on a 386 w/ postscript or hplaser +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + John Refling, University of California + jprefling@lbl.gov [DO NOT SEND REQUESTS HERE] + date: 13 January 1995, Version: 12a + +Archivists +~~~~~~~~~~ +This file should be stored on your machine with a basename of 'jrtex12a'. +in a place related to tex. A one line description of this guide might be: +"Describes install of emtex, latex2e, hplj & PS on PC" + +How to get latest +~~~~~~~~~~~~~~~~~ +This will be available at simtel mirrors such as wuarchive.wustl.edu +and oak.oakland.edu in/or near the /pub/msdos/tex directory, and perhaps +CTAN machines. A similar file which describes an older +version of emtex installation (pre latex-2e) has basename jrhelp11. + +What is this +~~~~~~~~~~~~ +This is a short account of my experiences installing emtex on a few different +computers, with a few different printers. + +Hardware you NEED to follow this guide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a 386 or greater computer with reasonable (4MB) ram about 35 meg free space + peak for the install (for easy installation, although you can get by with + much less if you are willing to really work at it), reduced to ~10 MB + disk space permanently (less w/o postscript support and the docs), + plus fonts (about 5 MB max). + +a reasonable dos version (best if you can recall and edit commands) + +a laser printer (postscript or pcl hplaserjet or hplaserjet iv, are tested). + +What you get +~~~~~~~~~~~~ +emtex system (tex, latex2e, mf, screen previewer, postscript support) +dynamically generated fonts if you wish (that's right, I don't generate them + until I use `em) +The emtex system is a great tex / latex system. + +A note on the version numbers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The base name of this document is jrtex which takes 5 characters out of 8 +possible. The next two digits correspond to the betatest version of emtex, +currently 12. Unfortunately, other pieces of the emtex package change without +notice and so it is hard to pin a version number on the entire package, which +I could carry over to this document, plus I might have revisions. The final +character in this help file's name will serve as a minor revision indicator. + +Even so, there might be minor discrepancies between what you see here +and what is on the net. Perhaps one of the best ways to check for +revisions on the net would be to check the file's size and/or it's date. +However, I just don't see any good way to build this info into the name +of this document. I have included the file size of files obtained from +the net. When the size indicated is different from the size you received, +you need to be cautious... most likely a new version has been added to the +distribution, and you need to take that into account. Sometimes in the +betatest subdirectory there are multiple versions of the same thing... use +the latest... even if I don't in this document! + +How to do it +~~~~~~~~~~~~ +Print out a copy of this file, and follow along with it. CROSS OFF STEPS +WHEN COMPLETED. You need to be able to transfer files from the internet +to your PC. You also need pkunzip, and know how to use it. If you use +unzip, the options are slightly different. + +What you don't get +~~~~~~~~~~~~~~~~~~ +This worked for me, but I can't guarantee it for you. You take all +responsibility for implementing this. You are assumed to know enough +about what is going on to not do something dangerous even if it says to +do so here. Besides, someone could have changed this before you got it. +ALSO, I DON'T COVER MIGRATION FROM THE 2.09 VERSION OF LATEX TO THE NEW +2E VERSION. I ASSUME A NEW INSTALLATION! Also, this is only how I did +it! and is not an official guideline, or recommendation. It is possible +that something in here may guide you contrary to some of the published +or yet to be published standards. This is provided in the hope that it +may be useful, in the event that you are having trouble with your +installation. THAT'S IT. You have been warned! + +Introduction +~~~~~~~~~~~~ +The emtex distribution was a little confusing to me at first, since I am +used to obtaining one compressed distribution archive file and working with +that. When a new version is distributed, one goes back to the ftp site and +gets the compressed file with the next higher version number. + +With emtex, things are spread across directories, not only in terms of package +components, but also in terms of revision levels. The most serious drawback +of this method is that it is difficult to determine the revision level of +programs, or where a particular revision level is. The simpliest way to +install it is just to install ALL of the archives in order, and let +them overwrite [hopefully] older versions. Then, you will have the latest +versions, and you delete what you don't want. + +1. OBTAIN THE DISTRIBUTION AND EXTRACT IT AND ARCHIVE IT FOR LATER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a. cd \ + +b. use ftp to connect to a distribution machine, ftp.dante.de, + ftp.shsu.edu, wuarchive.wustl.edu, etc, and get to the emtex directory + something like /tex-archive/systems/msdos/emtex. We wish to grab all the + files in the disk1, disk2, disk3, disk4, disk5, betatest subdirs. + MAKE SURE YOU SET BINARY MODE BEFORE THE TRANSFER OF BINARY FILES. + One way to do it all is as follows (assuming that the remote ftp server + has on-the-fly compression): + + ftp ftp.shsu.edu +**** ftp.dante.de is too slow + cd /tex-archive/systems/msdos + bin + hash + get emtex.zip [15,173,126] +**** get emtex.tar.gz [11,859,740] + + This will recursively get everything. Last time I tried it, it ended up + being a 14,992,564 byte file, and took 60 minutes via ftp. + + Then, get the latex2e stuff: + + cd /tex-archive/macros/latex + get base.zip [771,461] + cd packages + get tools.zip [221,517] +**** get graphics.tar.gz [80,020] +**** 'graphics' has epsfig (allows including Encapsulated PostScript files in LaTeX docs) + quit + +c. Then on the pc: + + mkdir \emtmp + cd \emtmp + pkunzip \emtex.zip [or unzip -j \emtex.zip] + cd \ + + You will need 15 + 15 = 30 MB disk space for this process. Read the + readme.tst and note the date in the upper right-hand corner. For my + installation it was 12-Dec-94. After success, you may delete \emtex.zip + and get 15 MB disk space back. Then goto step d. +**** version of 03-Feb-95 (includes web.zip with weave, tangle and pooltype) + + If this does not work [because you don't have enough temporary disk space + for the one huge file, or if the ftp server can't compress into one huge + file], you have to get all the files individually. The ones we are most + interested in are listed in item 5 below with their locations in []. + +d. Do the following in order [if you use unzip, do not use the -d]: + + pkunzip -d \emtmp\tex1.zip [disk1, 375854] + pkunzip -d \emtmp\tex2.zip [disk1, 268240] + pkunzip -d \emtmp\latex1.zip [disk2, 248823] + pkunzip -d \emtmp\latex2.zip [disk2, 238131] + pkunzip -d \emtmp\blatex.zip [disk2, 231677] + pkunzip -d \emtmp\makeindx.zip [disk2, 52717] + pkunzip -d \emtmp\bmf1.zip [disk3, 266527] + pkunzip -d \emtmp\bmf2.zip [disk5, 271791] + pkunzip -d \emtmp\texcad.zip [disk3, 120547] + pkunzip -d \emtmp\mf1.zip [disk4, 249916] + pkunzip -d \emtmp\mf2.zip [disk4, 344463] + pkunzip -d \emtmp\mf3.zip [disk4, 278545] + pkunzip -d \emtmp\mfware1.zip [disk4, 327215] + pkunzip -d \emtmp\mfware2.zip [disk5, 140361] + pkunzip -d \emtmp\btex1.zip [disk5, 265159] + pkunzip -d \emtmp\btex2.zip [disk5, 274600] + pkunzip -d \emtmp\misc_mf.zip [disk5, 36349] + pkunzip -d -o \emtmp\mfb5.zip [betatest, 1044415] + pkunzip -d -o \emtmp\mfjob11n.zip [betatest, ?] + pkunzip -d -o \emtmp\texb12.zip [betatest, 1120943] + pkunzip -d -o \emtmp\bibtexb1.zip [betatest, 167593] + pkunzip -d -o \emtmp\fontl12a.zip [betatest, 88321] + pkunzip -d -o \emtmp\dvid15g1.zip [betatest, 1213323] + pkunzip -d -o \emtmp\dvid15g2.zip [betatest, 727077] + + Answer yes to any overwrites. + + move \emtmp\readme.tst \emtex\doc + move \emtmp\readme.eng \emtex\doc\english + + Extracting these files generates another 18 MB of files. If this is + successful, you may now delete everything in \emtmp, as well as \emtex.zip. + + If you run out of space in the process, you may delete each .zip you + have extracted successfully. After extracting ALL the .zips, you may + delete \emtex.zip. + +e. Get rid of stuff (I save only the 386 stuff): + cd \emtex + del *.cmd [os/2 stuff] + del bmf*.* [big mf] + del blatex.bat [big latex] + del btex*.* [big tex] + del mf.exe [small mf] + del mf186.exe [small mf] + del mf286.exe [small mf] + del tex.exe [small tex] + del tex186.exe [small tex] + del tex286.exe [small tex] + del texp.exe [os/2 stuff] + del texfmts [small tex] + rmdir texfmts [small tex] + del mfbases [small tex] + rmdir mfbases [small tex] +**** + del remove [intnl files] + rmdir remove [intnl files] +**** Might not be a good idea to remove, tells from where a given file came + del mfp*.* [os/2 stuff] + del dvipm*.* [os/2 stuff] + del mfjob1.ovl [small mfjob] + del mfjob2.ovl [small mfjob] + del ask.exe + del bibtex.exe [small bibt] +**** + del book + rmdir book +**** book has subdirs (skip these steps, german subdir will be deleted below) + + IF YOU REALLY ARE GOING TO USE A DOT MATRIX PRINTER, YOU MIGHT KEEP THESE: + del dvidot*.* [dot matrix] + del gh.* + del prtfx*.* + del prtitoh.* + del prtlq*.* + del prtp6*.* + del prtsty.bat + del pcx*.* + del prtaiw.bat + del makedot.exe + + I DON'T HAVE LIMITED MEMORY, SO DELETE + del vs.bat [ltd memory vers] + del dviscrs.exe [ltd memory vers] + + OLD FORMATS FOR FONTS, IF YOU ARE STARTING NEW, YOU DON'T WANT THEM: + del chtopx.exe + del gftopxl.exe + del gftype.exe + del pktopx.exe + del pxtoch.exe + del pxtopk.exe + + I HAVE A COPROCESSOR SO DON'T NEED THE COPROCESSOR-LESS VERSIONS + del dviscr.exe + del dvihplj.exe + + I DON'T HAVE A VIKING DISPLAY + del dvivik.exe + + SAVE SOME MORE SPACE since I regenerate the bases & formats later + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + cd \emtex\bmfbases + del *.cmd + del *.log + del *.bas + edit bas.bat and change "bmf" to "\emtex\mf386" in all cases (2) +**** use only 'mf386' (i.e. ?:/emtex will be in the PATH) + + cd \emtex\btexfmts + del *.cmd + del *.log + del *.fmt + edit fmt.bat and change "btex" to "\emtex\tex386" in all cases (2) + edit slifmt.bat and change "btex" to "\emtex\tex386" in all cases (1) +**** use only 'tex386' (i.e. ?:/emtex will be in the PATH) + + I delete some dot-matrix printer configuration files. Actually, all I + keep in the \emtex\data\ subdirectory is: + dvidrv.err fax.cnf fontlist lj.cnf ljh.cnf newfonts.sub oldfonts.sub + + I choose to get rid of a few of the doc files in German to save space: + del \emtex\doc\german + rmdir \emtex\doc\german + del \emtex\book\german + rmdir \emtex\book\german + + and get rid of some more o/s-2 stuff: + del \emtex\help + rmdir \emtex\help + +f. FINALLY, ARCHIVE IT: + cd \ + pkzip -rP em941212 \emtex\*.* [941212 = date in readme.tst] + copy em941212.zip [your archive place] + copy base.zip [your archive place] + copy tools.zip [your archive place] + + These should fit on two 3" floppies. This is what I use to archive the + distribution. The em941212.zip will have to be split across the disks. I + picked 941212 as the version number since that is the date in the readme.tst + file. When the date changes, change the number. + +g. This will give you the following software and versions: + + bibtex32.exe 0.99c [3c-beta1, have to dig thru exe to find vers] + dvidrv ? + dvihplj 1.5g +**** dvihplj7 1.5g + dviscr 1.5g +**** dviscr 1.5a + emx.exe 0.8h (rev 16) [have to dig thru exe to find vers] +**** emx.exe 0.8h (rev 18) + fontlib 1.2a + gftodvi.exe 3.0 [1g] + gftopk.exe 3.2 [1j] +**** gftopk.exe 2.3 [1j] + makeindx.exe 2.11 + maketcp.exe 1.1c + mf386.exe 2.71 [3c-beta5] + mfjob.exe 1.1n + mft.exe 2.0 [1e] + pktogf.exe 1.0 [1c] + pktype.exe 2.3 [1c] + tex386.exe 3.1415 [3c-beta12] + texcad.exe 2.8 + + +2. RESTORING THE ARCHIVE +~~~~~~~~~~~~~~~~~~~~~~~~ +If you are continuing from above skip to the next section. If you are +restoring em941212 from the archive you made earlier, do that now, and: + +pkunzip -d em941212.zip + +3. COMPLETING THE INSTALLATION OF LATEX2e +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Make sure that the following is in \config.sys: (the numbers can be larger) + +shell=command.com /e:1024 /p +buffers=20 +files=20 + +if tex complains that it can't open an output file, increase the files. + +Edit \autoexec.bat and append the following to the path statement: +;\emtex + +also add the following lines + +set texinput=c:\emtex\texinput! +set dvidrvfonts=c:\texfonts +set dviscr=-s2 +set mfjobopt=-3 + +You may insert the contents of \emtex\set-tex.bat into your autoexec.bat, +although it is not necessary, since the default paths are hard coded into +the .exe files. However, if you change any paths later, you will have to +edit \emtex\set-tex.bat to reflect them, and execute \emtex\set-tex.bat +each time you boot, or automatic subdirectory searching will not work. +Make sure that the ! follows the `texinput' as in the first line above. +Also, the last two are not in set-tex.bat, so you have to add them anyway. + +You should add the following line to your autoexec.bat, with appropriate +number for the lpt port. This prevents prthplj & prthpljh from aborting when +you take too long to refill the printer's paper: +mode lpt1 retry=r + +create \emtex\tex.bat in path: +tex386 &plain %1 %2 %3 %4 %5 %6 %7 %8 %9 + +create \emtex\latex.bat in path: +tex386 &latex %1 %2 %3 %4 %5 %6 %7 %8 %9 + +create \emtex\slitex.bat in path: +tex386 &splain %1 %2 %3 %4 %5 %6 %7 %8 %9 + +or add them to a ced-like alias file. If you don't know what that is, don't +ask. You will now type tex, latex, slitex followed by the filename to +process a document. If the extension on the filename is .tex, it is optional. + +reboot now! + +EXTRACT LATEX2E +~~~~~~~~~~~~~~~ +cd \ +pkunzip -d \base +cd \base +\emtex\tex386 /i unpack.ins + +replace any existing files in the following (to override them): +move *.ltx \emtex\btexfmts +move ltxcheck.tex \emtex\texinput +move testpage.tex \emtex\texinput +move docstrip.tex \emtex\texinput +move *.cls \emtex\texinput +move *.clo \emtex\texinput +move *.sty \emtex\texinput +move *.fd \emtex\texinput +move *.def \emtex\texinput +move *.cfg \emtex\texinput + +mkdir \emtex\makeindx +move *.ist \emtex\makeindx + +mkdir \emtex\doc\base +move *.tex \emtex\doc\base [save and read these docs] +move *.txt \emtex\doc\base [save and read these docs] +**** move *.err \emtex\doc\base [LaTeX companion & LaTeX book errata] + +mkdir \emtex\texinput\local +**** For local files, will be used by automatic 1 subdir level search ('!') + +cd \ +del base.zip +del base +rmdir base + +BUILD THE CM BASES +~~~~~~~~~~~~~~~~~~ +cd \emtex\bmfbases +bas + +BUILD THE TEX AND LATEX2e FORMATS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +cd \emtex\btexfmts +fmt +slifmt +del lplain.* [this is the old 2.09 format replaced later on] +\emtex\tex386 /i latex.ltx [this is the new latex 2e format file] + +cd \ +latex ltxcheck [all should be ok] +**** Complains about missing AMS fonts (this was to be expected) + + +EXTRACT THE MAINZ TOOLS +~~~~~~~~~~~~~~~~~~~~~~~ +cd \ +pkunzip -d \tools +cd tools +latex tools.ins +del temp.tex +mkdir \emtex\texinput\tools +move *.sty \emtex\texinput\tools +move *.tex \emtex\texinput\tools +**** move *.txt \emtex\doc\tools + +mkdir \emtex\doc\tools +move *.dtx \emtex\doc\tools [these are all the docs for tools] + +cd \ +del tools.zip +del tools +rmdir tools + +TESTING THE LATEX INSTALLATION +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +mkdir \test +cd \test + +Make a small file called cm.tex containing: + +\documentclass{article} +\begin{document} +Hello world...........$\sum$ +\end{document} + +and latex it by typing `latex cm'. It should not complain. + + +4. VIEWING THE RESULTS +~~~~~~~~~~~~~~~~~~~~~~ +By latexing the above test document we have only used brief font info that +is stored in the TFM files (in \emtex\tfm). You cannot print or view the +results until you put fonts on your computer and/or printer. The only +exception to this is postscript printers or viewers which have their fonts +built in. More on that later. + +If you have a postscipt printer, you may use its internal fonts, or you may +download fonts (e.g. CM [computer modern]), or do a combination of the two). +You may also choose to use a postscript previewer for screen previewing (e.g. +ghostscript). OR, you may choose to use the CM fonts that come with latex for +screen previewing and use a dvi previewer (e.g. dviscr). + +If you have a reasonably recent HP pcl laser printer (any of the laser jets +excluding the laser jet 1 [the laser jet 1+ is ok, although limited memory) +or a laser printer that emulates an HP laser printer, you will want to use +the CM fonts. + +The new HPLJ IV has its own scaleable fonts, so perhaps you will be able to +use those internal fonts much like with postscript printers. You would need +the TFM files describing the fonts inside the printer. I haven't done much +work with the HP LJ IV so it is possible this has already been done. I don't +discuss it here. + +Moving on in the choices, if you choose to use CM for either printing or +display, then you need to obtain the actual fonts. You can either get +them from the net, generate them all at once, or generate them on demand +and the save them for future use. I do the latter. On a 486-33 MHz +it only takes a minute or so to generate a font in a particular size. +All these options are described next. + +Pick one, and skip to a), b), c), d), e), or f) below. +**** Best choice is step c), generate fonts on demand + +a. CM fonts from the net, for screen viewing & 300 dpi hplj printing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + mkdir \texfonts + cd \texfonts + ftp ftp.shsu.edu [or other site] + cd tex-archive/systems/msdos/emtex-fonts + bin + hash + get lj_fonts.zip [this will be about 5 MB] + quit + + pkunzip lj_fonts *.fli [or unzip -j lj_fonts *.fli] + + Archive lj_fonts.zip somewhere for future use. + + To display the document on your screen, use v <filename>. v.bat is a batch + file in \emtex which calls dviscr. If you have a math coprocessor and + deleted dviscr.exe and kept dviscr7.exe, edit v.bat to call dviscr7.exe + instead of dviscr.exe. Example: + + cd \test + v test + + I add a /pt to \emtex\data\lj.cnf to prevent all the .dlg files. + + You can also put the one line contents of v.bat into a ced-like program. If + you don't know what that is, skip it. + + Read the documentation on dvi viewers in \emtex\doc\english\dvidrv.doc + +b. CM fonts from the net, for screen viewing & 600 dpi hplj IV printing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + I haven't tried this, since I couldn't find the .fli files for the HPLJ 4 on + the net. They would be rather large (up to 4x larger than the 300dpi---twice + in each dimension, 2 dimensions). If you do find them, edit + \emtex\v.bat to use @ljh.cnf and follow along similarly to the previous + section for 300dpi fonts, but edit \emtex\ljh.cnf, instead. + + Or, You may want to dynamically generate the fonts (below)... + + +c. Dynamically generating the CM fonts on demand, 300 dpi for hplj & screen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + This is how I do it. Append the following line to \emtex\data\lj.cnf + /fb + + I also add a /pt to the file to prevent all the .dlg files. + + You may wish to comment out the /pl line with a %, since you will not be + using the font libraries, as was done in 4a. + + Make sure that `set mfjobopt=-3' is in your autoexec.bat, or that it is + set before trying to generate the fonts. This tells mfjob to use mf386.exe + instead of mf.exe. + + cd \test + v test + + should generate 2 fonts and then display the dvi file on your screen. The + next time you do `v test' the fonts will already be generated. v.bat is a + batch file in \emtex which calls dviscr. If you have a math coprocessor, + edit v.bat to call dviscr7.exe. You can also put the one line contents of + v.bat into a ced-like line. The fonts go to \texfonts\pixel.lj\300dpi. + + +d. Dynamically generating the CM fonts on demand, 600 dpi for hplj IV & screen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Append the following lines to \emtex\data\ljh.cnf + /fb + + & comment out the /pl line with a %. Also edit v.bat to use ljh.cfg instead + of lj.cnf. v.bat is a batch file in \emtex which calls dviscr. If you + have a math coprocessor, edit v.bat to call dviscr7.exe. I also add a /pt + to the config file to prevent all the .dlg files. + + Make sure that `set mfjobopt=-3' is in your autoexec.bat, or that it is + set before trying to generate the fonts. This tells mfjob to use mf386.exe + instead of mf.exe. + + cd \test + v test + + should generate 2 fonts and display the dvi file on your screen. The fonts + go to \texfonts\pixel.ljh\600dpi. Next time, it won't have to generate + these fonts. + +e. Locally generating all 300dpi fonts for HP +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + cd \emtex\mfjob + mfjob all [wait several hours] + cd \ + del \newfonts\tfm + rmdir \newfonts\tfm + del \newfonts\log + rmdir \newfonts\log + move newfonts texfonts + +f. Locally generating all 600dpi fonts for HP IV +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + cd \emtex\mfjob + edit modes.mfj and change `def m=[lj]' to `def m=[ljh]' at end + mfjob all [wait several hours] + cd \ + del \newfonts\tfm + rmdir \newfonts\tfm + del \newfonts\log + rmdir \newfonts\log + move newfonts texfonts + + +5. Printing using CM fonts and HPLJ or HPLJ IV +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +use prthplj.bat and prthpljh.bat respectively. You may want to edit +them and change the output file /po=lpt1:, etc. If you have a math coproc, +edit prthplj.bat and prthpljh.bat to call dvihplj7.exe. If you are using +the HPLJ IV, add /og=600 to \emtex\prthpljh.bat on the same line. + +These routines use the same fonts and config files as the screen previewer, +so once the fonts are generated by the screen viewer, they are generated for +the printer too. + +IF you have an hp printer you are now done with the installation. + +6. PSNFSS & DVIPS for postscript printers (skip if you don't have a PS printer) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a. Using CM fonts only, and not the internal PS fonts of your printer + + if all you want to do is to use the CM fonts (and not the fonts in your + postscript printer), you may stop here. However this is kind of a waste + because the extra money you paid for the postscript printer gives you a + lot of advanced features and fonts. However, with those features comes + more installation difficulties, ie, the rest of this document. So no + one would blame you for stopping here. + + If you want to stop here and generate CM fonts and download them to the + printer as a bit-map each time you print, do the following: + + cd \ + ftp wuarchive.wustl.edu +**** 4.8 KB/s + cd /mirrors/msdos/postscript +**** cd /systems/ibmpc/simtel/msdos/postscrp + bin + hash + get dvips554.zip [592,893 bytes, or latest] + quit + + pkzip -d \dvips554.zip *.tfm [delete the .tfm's from the zip] + pkzip -d \dvips554.zip *.vf [delete the .vf's from the zip] + pkunzip -d \dvips554.zip + + cd \emtex\texinput\dvips + del times.sty + del palatino.sty + del bookman.sty + del chancery.sty + del avantgar.sty + del lucida.sty + del ncs.sty + del psfonts.sty + del psgreek.sty + + The above represent old style files incompatible with latex2e and psnfss2e. + + mkdir \emtex\doc\dvips + move dvi*.tex \emtex\doc\dvips + + The fonts for the Apple Laserwriters are the same as for the HP LJ, since + the engines are the same [write black]. Choosing dvihplj or dvips formats + the bit maps properly in each printer's language. This is where the + difference is. Thus CM fonts obtained in 4a, 4b, 4e, or 4f are suitable + for dvips. Follow those instructions if you haven't already for the + screen fonts. If you have already obtained or generated or set up + dynamic (on the fly generation of fonts) for the screen you are already + to go: +**** The problem is that the Dvips config files assume drive C unless +**** told otherwise (dvips won't find 'tex.pro', see page 37 Dvips manual) +**** Assuming emTeX in drive D, fonts in drive E: +**** set texconfig=d:\emtex\ps +**** set emtexdir=d:\emtex +**** set dvipsheader=d:\emtex\ps +**** set texfonts=e:\texfonts +**** set texpks=e:\texfonts\pixel.lj\%bdpi\%f.pk +**** (put these in a batch file, remember to use '%%' to mean '%' in the +**** texpks value) + + cd \test + dvips cm + copy cm.ps lpt1: [or where ever your printer is] + + You may need an iteration of `mfjob dvips' to generate fonts. You may + stop here. Delete dvips.mfj after doing mfjob. + + +b. Using the internal PS fonts of your printer, and CM for occasional math + + IF you wish to take advantage of the native fonts in + your postscript printer, continue on. + + Do the steps in a) above. + + To use use native fonts, you will + insert one command \usepackage{font} in your latex document after the + \documentclass{} command. font=one of times, palatino, helvet, avant, + newcent, or bookman. To get these psnfss2e packages: + + cd \ + ftp ftp.shsu.edu [or other site] + cd tex-archive/macros/latex/packages + bin + hash + get psnfss.zip [724,648] + quit + + pkunzip -d \psnfss.zip + cd \psnfss + latex psfonts.ins [installation] +**** answer y to "do you have the 36 common PS fonts or do you intend to install them" + mkdir \emtex\texinput\ps + move *.sty \emtex\texinput\ps + mkdir \emtex\doc\ps + move psnfss2e.tex \emtex\doc\ps + move psfonts.dtx \emtex\dec\ps + cd \ + del \psnfss + rmdir \psnfss + + get the latex2e tfm, vf, fd and other associated files for adobe ps fonts: + + ftp ftp.shsu.edu [or other site] + cd tex-archive/fonts/metrics + bin + hash + get adobe.zip [1,895,291 bytes] + quit + + cd \ + pkunzip -d \adobe.zip + + edit \emtex\ps\config.ps and uncomment (delete the *) & change the line: + T c:\emtex\tfm;c:\emtex\tfm\ps;c:\emtex\tfm\local +**** Change all the following paths (V, P, L, S, H) also because they assume drive C + + there are 6 packages (times, palatino, helvet, avant, newcent, bookman) which + you use to select the primary postscript fonts for your document. These in + turn call upon the following 7 native postscipt fonts in the printer: + Helvetica, AvantGarde, Times, Palatino, NewCenturySchoolbook, Bookman, and + Courier. Your printer and screen previewer should have these to work properly + with the 6 packages (they are part of the standard 35 fonts). This info came + from Table 1 of psnfess2e.tex (page 4). The file adobe.zip that you just + obtained contains fd, tfm, and vf data for these and many other postscript + fonts. I will only cover the 7 fonts used by the 6 packages. + + move \adobe\helvetic\fd\*.fd \emtex\texinput\ps + move \adobe\avantgar\fd\*.fd \emtex\texinput\ps +**** md emtex\tfm\ps +**** This requires adding this path to the environment var TEXTFM + move \adobe\times\fd\*.fd \emtex\texinput\ps + move \adobe\palatino\fd\*.fd \emtex\texinput\ps + move \adobe\newcentu\fd\*.fd \emtex\texinput\ps + move \adobe\bookman\fd\*.fd \emtex\texinput\ps + move \adobe\courier\fd\*.fd \emtex\texinput\ps + + mkdir \texfonts\vf + move \adobe\helvetic\vf\*.vf \texfonts\vf + move \adobe\avantgar\vf\*.vf \texfonts\vf + move \adobe\times\vf\*.vf \texfonts\vf + move \adobe\palatino\vf\*.vf \texfonts\vf + move \adobe\newcentu\vf\*.vf \texfonts\vf + move \adobe\bookman\vf\*.vf \texfonts\vf + move \adobe\courier\vf\*.vf \texfonts\vf + + move \adobe\helvetic\tfm\*.tfm \emtex\tfm\ps + move \adobe\avantgar\tfm\*.tfm \emtex\tfm\ps + move \adobe\times\tfm\*.tfm \emtex\tfm\ps + move \adobe\palatino\tfm\*.tfm \emtex\tfm\ps + move \adobe\newcentu\tfm\*.tfm \emtex\tfm\ps + move \adobe\bookman\tfm\*.tfm \emtex\tfm\ps + move \adobe\courier\tfm\*.tfm \emtex\tfm\ps + + move \adobe\helvetic\phv.map \emtex\ps + move \adobe\avantgar\pag.map \emtex\ps + move \adobe\times\ptm.map \emtex\ps + move \adobe\palatino\ppl.map \emtex\ps + move \adobe\newcentu\pnc.map \emtex\ps + move \adobe\bookman\pbk.map \emtex\ps + move \adobe\courier\pcr.map \emtex\ps + + cd \ + del adobe.zip + del adobe + rmdir adobe + + cd \emtex\ps + del psfonts.map + ren *.map *.x + copy phv.x + pag.x + ptm.x + ppl.x + pnc.x + pbk.x + pcr.x psfonts.map + del *.x + + cd \test and create a file called ps.tex + \documentclass{article} + \usepackage{times} + \begin{document} + Hello world...........$\sum$ + \end{document} + + dvips ps + + this should work. The math needs to use the old CM fonts, a small subset + of which may need to be generated on the fly or downloaded. dvips should + have created an instruction file for mfjob to create the missing math + fonts. If all is well, run + + mfjob dvips + dvips ps + copy ps.ps to lpt1: [or whatever is your printer port] + + and you should have the complete document. I create everything on demand, + when dvips complains, using mfjob dvips as above. + + cd \ + del adobe + rmdir adobe + + Note, as I mentioned before, you can't view postscript on your screen. + The missing fonts appear as boxes on the screen, but print fine on a + postscript printer. + + You can look at the document on the screen using ghostscript or another + postscript previewer, which allows you to look at any included post- + script figures too, but involves a few more hours of install time (if you've + gotten this far then you can handle anything!). + + Check out gs386.exe on wuarchive.wustl.edu. + + +7. CUSTOM FONTS +~~~~~~~~~~~~~~~ +to use a custom font, say APL, get the .mf file off the net, and +put it in a \emtex\mfinput\local subdirectory. As an example, use cmapl10.mf +from the CTAN archive in /tex-archive/fonts/apl: + +cd \emtex\mfinput\local +mf386 \mode=hplaser; \nonstopmode; mag=1.0; input cmapl10 + +move cmapl10.tfm \emtex\tfm\local + +to look at it, for example, do: + +tex testfont +and reply: +cmapl10 +\table +\end + +then +v testfont + +and let it generate the fonts and look at them! To use them in a document, +read the texbook. + +you can print out the result on an hp printer: +dvihplj testfont + +you can print out the result on an hp IV printer: +dvihpljh testfont + +you can print out the result on a postscipt printer: +dvips testfont + +(c) 1995 John P. Refling, All rights reserved. + + + # - # - # + + + + + printing + /|\ +/- / | \ +| / | \ +| / | \ +|printer hp / | hpIV \ ps +| / | \ +| / | \ +\- / | \ + / | \ +/- / / \ / \ +|font CM / CM / \ HP CM / \ PS /w CM +|- / / ? / \ + / / / \ + prthplj prthpljh dvips1 dvips2 + 300 600 300 300 +/- |\ /| |\ /| +| | \ LIB OTF / | | \ LIB OTF / | +| OTF | \ / | LIB OTF | \ / | +| | \ / | | \ / | +|source | \ | | \ | +| | / \ | | / \ | +| | / \ | | / \ | +| | / \ | | / \ | +\- |/ \| |/ \| + + 4c,4d download=4a or 4b 6a+(4c or 4d) download=6b+(4a or 4b) + genall=4e or 4f genall=6b+(4e or 4f) + + +Type of printer +~~~~~~~~~~~~~~~ +hp = PCL (hplaserjet 1+ through BUT not including the HP Laserjet IV) +hpIV = PJL (hplaserjet IV, w/o postscript) +ps = any postscript printer (including hplaserjet IV w/postscript) + +Choice of font +~~~~~~~~~~~~~~ +CM = Computer Modern fonts, freely available, and included with emtex + (either generated locally with metafont, or downloaded off the net, + see source of CM font below) +HP = scaleable fonts inside the HP laserjet IV printer +PS = scaleable postscript fonts inside a postscript printer, however, when + you are using math, it will revert to CM for the math fonts. See source + of CM fonts below. + +Source of CM fonts (for CM or CM for postscript math) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +OTF = On the fly font generation (initially slower, small space rqmt) +LIB = downloaded libraries of all the fonts (faster, large space rqmt ~5 MB) + + + +What to use +~~~~~~~~~~~ +prthplj = from emtex +prthpljh = from emtex +dvips1 = with OTF CM generation from MakeTeXPK, or PK libraries +dvips2 = mostly internal postscript fonts, but some of OTF CM generation + for fonts missing from postscript. +? = no solution that I am aware of, but I didn't look very hard + +(c) 1995 John P. Refling. All rights reserved. + +**** All lines below this one represent my setup, they are not from John Refling: +**** (they are not all marked with '****' to avoid cluttering) + +**** Encapsulated PostScript support: +To be able to use the epsfig package expand 'graphics.tar.gz' somewhere, +read the '00readme.txt' and then run: +('?' is your drive letter, .tgz because .tar.gz is invalid in Ms-Dos) + +gzip -dc graphics.tgz | tar xvf - +latex graphics.ins +md ?:\emtex\texinputs\graphics +copy *.def ?:\emtex\texinputs\graphics +copy *.sty ?:\emtex\texinputs\graphics +copy 00readme.txt ?:\emtex\texinputs\graphics +echo \ExecuteOptions{dvips} > ?:\emtex\texinputs\graphics\color.cfg +echo \ExecuteOptions{dvips} > ?:\emtex\texinputs\graphics\graphics.cfg + +**** I put this in my 'autoexec.bat': + +rem emTeX, don't change, just update DVIDRVFONTS, EMTEXDRIVE, PATH and edit the +rem file '?:\emtex\ps\config.ps' and fix its T, V, P, L, S, H paths +set DVIDRVFONTS=G:\texfonts +set EMTEXDRIVE=H +echo Calling %EMTEXDRIVE%:\emtex\set-tex.bat +if exist %EMTEXDRIVE%:\emtex\set-tex.bat goto SETTEX +echo File not found! +pause +:SETTEX +call %EMTEXDRIVE%:\emtex\set-tex.bat +SET MFINPUT=%MFINPUT%;g:\usr\local\lib\tex\mfinput + +**** And I put this in the 'set-tex.bat' file in the emtex dir: + +rem emTeX env vars setup, see also jrtex12a.txt. +rem If changing location update: +rem a) env vars 'EMTEXDRIVE' (emTeX drive) and 'DVIDRVFONTS' (TeX fonts path) +rem b) edit the file 'emtex\ps\config.ps' and fix the paths T, V, P, L, S, H +if %DVIDRVFONTS%.==. goto NOENVVAR +if %emtexdrive%.==. goto NOENVVAR +rem Append `!' to a directory name to get one-level subdirectory search +rem Append `!!' to a directory name to let emTeX search all subdirectories +SET TEXINPUT=%emtexdrive%:\EMTEX\TEXINPUT! +SET TEXFMT=%emtexdrive%:\EMTEX\TEXFMTS +SET BTEXFMT=%emtexdrive%:\EMTEX\BTEXFMTS +SET TEXTFM=%emtexdrive%:\EMTEX\TFM;%emtexdrive%:\EMTEX\TFM\PS +SET MFINPUT=%emtexdrive%:\EMTEX\MFINPUT +SET MFBAS=%emtexdrive%:\EMTEX\MFBASES +SET BMFBAS=%emtexdrive%:\EMTEX\BMFBASES +SET MFJOB=%emtexdrive%:\EMTEX\MFJOB +SET BIBINPUT=%emtexdrive%:\EMTEX\BIBINPUT +SET DVIDRVINPUT=%emtexdrive%:\MYTEX;%emtexdrive%:\EMTEX\DOC +rem SET DVIDRVGRAPH=C:\MYTEX;%emtexdrive%:\EMTEX\DOC\GR$r +SET DVIDRVGRAPH=%emtexdrive%:\EMTEX\DOC\GR$r +set dviscr=-s2 +set mfjobopt=-3 +rem DVIPS 5.54 (also possible to only use TEXCONFIG and to set rest in cfg file) +set texconfig=%emtexdrive%:\emtex\ps +set emtexdir=%emtexdrive%:\emtex +set dvipsheaders=%emtexdrive%:\emtex\ps +set texfonts=%emtexdrive%:\emtex\tfm;%emtexdrive%:\emtex\tfm\ps +set texpks=%DVIDRVFONTS%\pixel.lj\%%bdpi\%%f.pk +goto THEEND +:NOENVVAR +echo %0: ERROR, env vars DVIDRVFONTS and EMTEXDRIVE not set! +pause +:THEEND + +**** GhostScript (PostScript level 2 interpreter/previewer) +**** The current version is 3.12 (Oct-94) +**** ftp.funet.fi:/pub/gnu/ghostscript3/aladdin +There are several Dos/Win binaries: + 1) gs.exe was compiled for the following devices: + vga ega svga16 vesa atiw tseng tvga deskjet + djet500 laserjet ljetplus ljet2p ljet3 ljet4 cdeskjet cdjcolor + cdjmono cdj550 pj pjxl bj10e bj200 epson eps9high + ibmpro gifmono gif8 pcxmono pcxgray pcx16 pcx256 pcx24b + 2) gs386.exe was compiled for the following devices: + vga ega svga16 atiw tseng tvga deskjet djet500 + laserjet ljetplus ljet2p ljet3 ljet4 cdeskjet cdjcolor cdjmono + cdj550 paintjet pjetxl epson eps9high ibmpro bj10e bj200 + gifmono gif8 tiffg3 faxg3 pcxmono pcxgray pcx16 pcx256 + pcx24b pbm pbmraw pgm pgmraw ppm ppmraw bitcmyk + 3) don't know what gswin.exe was compiled for (use the PostScript command + 'devicenames ==' to find out, using the option '-h' won't work here) + 4) gswin32s.exe was compiled (not by Aladdin) for the following devices: + mswin mswinprn deskjet djet500 laserjet ljetplus ljet2p + ljet3 ljet4 cdeskjet cdjcolor cdjmono cdj550 paintjet pjetxl epson + eps9high ibmpro st800 bj10e bj200 gifmono gif8 tiffg3 + tiffg4 bmpmono bmp16 bmp256 bmp16m pcxmono + pcx16 pcx256 pbm pbmraw pgm pgmraw ppm ppmraw +If you need something that is not in here you will have to compile GhostScript +yourself (if the device code already exists just add it to the makefile, +otherwise you'll have to write it yourself, there is documentation on how to do +that) or if your printer is supported by Windows to use gswin?.exe with the +device 'mswinprn') + +**** end of this file diff --git a/web/noweb/contrib/avs/make_ico.awk b/web/noweb/contrib/avs/make_ico.awk new file mode 100644 index 0000000000..f422176173 --- /dev/null +++ b/web/noweb/contrib/avs/make_ico.awk @@ -0,0 +1,50 @@ +# edits noweb/src/icon/makefile for Ms-Dos + PC386 + Icon 386 9.0 + DJGPP + MKS 4.2 +# tested with noweb 2.7a + +BEGIN { print "# generated MsDos makefile, original in makefile.old" } + +/SHELL=/ { $0 = "# " $0 } # disable SHELL def + +/BINEXECS=/ { # add .exe extension + s = ""; + for (k = 1; k <= NF; ++k) + s = s sprintf("%s.exe ", $k); + $0 = s +} + +function splitLineTooLong() { # appends to strings s1 & s2 (does not initialize them) + for (k = 1; k <= NF; ++k) + if (match($k, "\\.")) + s1 = s1 $k " "; + else + s2 = s2 $k ".exe "; + } +/LIBEXECS=/ { # split in 2 parts (to avoid 128 chars command.com overflow) and add .exe if no extension is provided + s1 = ""; s2 = ""; + if ($NF == "\\") { # tackles problem of a '\' meaning continue in next line + $NF = ""; + NF = NF - 1; + splitLineTooLong(); + getline; # read next line due to '\' continuation char + } + splitLineTooLong(); + printf("LIBEXECS2=%s\n", s1); + $0 = s2; +} + +/^EXECS=/ { $0 = $0 " $(LIBEXECS2)" } # because now LIBEXECS is split into LIBEXECS and LIBEXECS2 + +/cp \$\(LIBEXECS\)/ { printf("\tcp $(LIBEXECS2) $(LIB)\n"); } # the new LIBEXECS2 also need to be copied + +/\/bin\/rm/ { $1 = "\trm" } # rm might not be at "/bin/rm", remember to add the tab \t + +/\$\(ICON.\) -o/ { + if (!match($3, "\\.")) { # if no extension add .exe + sub(/[a-z0-9]+/, "&.exe", $3); + $1 = "\t" $1 " -I" # add -I option to icon translator (see Icon 386 9.0 Ms-Dos docs) + } + } + +/^[a-z0-9]+: [a-z0-9]+\.icn/ && NF == 2 { sub(/[a-z0-9]+/, "&.exe", $1) } # add .exe + +{ print $0 } # prints the line (which might have been changed) diff --git a/web/noweb/contrib/avs/make_lib.awk b/web/noweb/contrib/avs/make_lib.awk new file mode 100644 index 0000000000..84e2c15bd6 --- /dev/null +++ b/web/noweb/contrib/avs/make_lib.awk @@ -0,0 +1,12 @@ +# edits noweb/src/lib/makefile for Ms-Dos + PC386 + Icon 386 9.0 + DJGPP + MKS 4.2 +# tested with noweb 2.7a + +BEGIN { print "# generated MsDos makefile, original in makefile.old"; } + +/SHELL\=/ { $0 = "# " $0 } # disable SHELL def + +/cp unmarkup emptydefn toascii \$/ { # add an extension .ksh) when copying + $0 = "\tcp unmarkup $(LIB)/unmarkup.ksh\n\tcp emptydefn $(LIB)/emptydefn.ksh\n\tcp toascii $(LIB)/toascii.ksh" +} + +{ print $0 } # prints the line (which might have been changed) diff --git a/web/noweb/contrib/avs/make_src.awk b/web/noweb/contrib/avs/make_src.awk new file mode 100644 index 0000000000..d9dec16af4 --- /dev/null +++ b/web/noweb/contrib/avs/make_src.awk @@ -0,0 +1,72 @@ +# edits noweb/src/makefile for Ms-Dos + PC386 + Icon 386 9.0 + DJGPP + MKS 4.2 +# tested with noweb 2.7a + +BEGIN { print "# generated MsDos makefile, original in makefile.old" } + +/SHELL\=/ { $0 = "# " $0 } # disable SHELL def + +/for i in shell lib xdoc tex;/ { # new fix for noweb 2.7a (not needed in 2.7) + $0 = "\tcd shell\n\tmake all\n\tcd ..\n\tcd lib\n\tmake all\n\tcd ..\n\tcd xdoc\n\tmake all\n\tcd ..\n\tcd tex\n\tmake all\n\tcd ..\n" +} + +/cd ([a-z]+)|(\$\(LIBSRC\)); make/ { # fix problems with quotes and 'cd' and explode into 3 lines + if ($NF == "all") { + for (k = 1; k <= NF; ++k) gsub("\"", "", $k); # remove quotes + sub(/;/, "", $2); # remove semicolon + s = ""; for (k = 4; k <= NF; ++k) s = s " " $k; # group 'Make' args in a single string (for the sprintf) + $0 = sprintf("cd %s\n\t$(DJGPPMAKE) %s\n\tcd ..", $2, s); + } else + if ($NF == "install") + sub("^[^;]+", " \"&\"", $2); # add quotes (which need a shell) to force use of shell internal 'cd' instead of bin/cd.exe + $0 = "\t" $0; +} + +/\/dev\/null/ { sub(/\/dev\/null/, "NUL") } # Ms-Dos uses NUL to mean /dev/null + +/strip/ { + sub(/strip/, "# strip"); # remove the strip, MKS strip would ruin the binaries + $0 = $0 "\n\tcd \"c\"; coff2exe nt markup mnt finduses"; # and in next line add the coff2exe command (see DJGPP docs) +} + +/chmod \+x/ { $0 = "\t# " $0 } # disable chmod (not necessary and sometimes tries to 'chmod +x foo' instead of 'chmod +x foo.ksh') + +/cp / { # add an eventual extension (.exe or .ksh) when copying + if (match($2, "^c/")) { + $1 = "\t" $1; + for (k = 2; k < NF; ++k) # NB: 1st & last field not processed + $k = $k ".exe"; + } else { + if ($NF == "$(LIB)") { # add .ksh and split in several lines + s = ""; + for (k = 2; k < NF; ++k) { + baseName = substr($k, 1 + match($k, "/")); + s = s sprintf("\tcp %s %s/%s.ksh", $k, $NF, baseName); + if (k != NF-1) + s = s "\n"; + } + $0 = s; + } + } +} + +/install: install-code install-man install-tex/ { $3 = "install-preformat-man" } # MKS has no NROFF + +/sed / { # all files processed by sed require something to be fixed for MsDos + if (match($0, "\$\(BIN\)")) + $NF = $NF ".ksh" # add .ksh extension + else { + if (match($0, "gzip")) { # remove gzip of man pages, MKS supports compressed man pages but in .dbz not in .gz format + sub("\| gzip", ""); # remove call to gzip + sub("\.gz$", "", $NF); # remove .gz extension + } else + $0 = "#" $0; # Disable because MKS does not support NROFF + } + $0 = "\t" $0; +} + +/; ln / { # no links in MsDos + sub(/; ln /, "; cp -p "); # replace link (ln) with copy (cp) + gsub(/\.gz/, ""); # gzip compressed man pages not used +} + +{ print $0 } # prints the line (which might have been changed) diff --git a/web/noweb/contrib/avs/make_xdo.awk b/web/noweb/contrib/avs/make_xdo.awk new file mode 100644 index 0000000000..b59ccefb5d --- /dev/null +++ b/web/noweb/contrib/avs/make_xdo.awk @@ -0,0 +1,14 @@ +# edits noweb/src/xdoc/makefile for Ms-Dos + PC386 + Icon 386 9.0 + DJGPP + MKS 4.2 +# tested with noweb 2.7a + +BEGIN { print "# generated MsDos makefile, original in makefile.old"; } + +/SHELL\=/ { $0 = "# " $0 } # disable SHELL def + +/WWW=/ { $0 = "WWW=../.." } # Put World Wide Web files in noweb because the dir $(HOME)/www/noweb might not exist + +/\/bin\/rm/ { sub(/\/bin\/rm/, "rm") } # rm might not be at "/bin/rm" + +/\.ps\.gz/ { sub(/\.ps\.gz/, ".pgz"); } # MsDos limitation, use extension .pgz instead of .ps.gz + +{ print $0 } # prints the line (which might have been changed) diff --git a/web/noweb/contrib/avs/mks42bug.0d b/web/noweb/contrib/avs/mks42bug.0d new file mode 100644 index 0000000000..f6ba43e2dd --- /dev/null +++ b/web/noweb/contrib/avs/mks42bug.0d @@ -0,0 +1,128 @@ + MKS Toolkit 4.2 Dos Bugs + +Environment: 486DX2-80 VLbus, 32 MB RAM, 2 EIDE HDs, ATI Mach64 GPT 2048K VLbus +Sblaster 16 ASP, Logitech serial Mouseman, Ms-Dos6.22, WfW3.11 (32 bits disk +access/32 bits file access), QEMM7.5, Stacker4.0 for OS/2 & Dos, +OS/2 boot manager (Dos/Win, Warp, Linux) + +1- bin32/diff.exe doesn't do correctly wild card expansion under command.com + (i.e. doesn't use glob.exe correctly). It works fine under the Korn shell. + No problems with bin/diff.exe + (as it can be much faster than the 16 bits diff, e.g. 5s against 18s, I + just renamed it to diff32.exe) + BUT: it seems that in some cases with QEMM 7.5 diff32 works sometimes with + wildcards expansion, and it is diff.exe that crashes when wild card expansion + is used and at the same time there are differences (QEMM says invalid ...). + Notice that it seems that if the 2 files are identical no problem! Under + a clean boot no crash, but again no one can check if invalid statements + are used. See the 2 files MOD_FBUS.LST and LIXO for an example of a crash! + +2- bin/find.exe with the option -exec doesn't always work correctly under + command.com (Ms-Dos 6.2 or Ms-Dos 6.22). The 1st exec works, the 2nd gives a + memory allocation error (under Ms-Dos 6.2) or an unspecified error (under + Ms-Dos 6.22). + E.g.: + find . -type f -exec ls -l {} ; + always works, but in + find . -name "*.zip" -exec ls -l {} ; + only the first exec works, the 2nd gives the error message + find: cannot execute "ls": not a directory or path not found + and find aborts. + The same behaviour occurs with executing non MKS commands. + No problems if executing under the Korn shell. + The find.exe from version 2.3 of the MKS toolkit works fine under + command.com, thus this is a new bug. + +3- the Korn shell crashes if one presses ctrl-BREAK (even ctrl-alt-del doesn't + work). Under windows it "violates system integrity" and windows terminates + it and asks one to reboot the machine because it should be unstable. Under + Qemm, Qemm also complains in the same way. The setting of 'break' in + config.sys/autoexec.bat (i.e. ON/OFF) is not relevant here + +4- there are several bugs in bin/pax.exe + a) -i never works (i.e. whether one writes a name, presses '.' or + presses return it always is unable to create that file) + b) -s only works partially, most of the time (using s/.../.../p to see + what happens) it does erroneous substitutions. It seems to work if + all ocurrences of '.' or '/' on the original name are matched by + using a '.' (e.g. never use a regular expression that matches both the + basename and the extension) + c) using '-f path' if path has a backslash anywhere then it crashes + (e.g. using 'pax -f g:\users\foo.tar' from the DOS prompt). The + crash looks like an infinite loop. If running in a Dos window then it + is possible to kill that window without crashing the system + d) this is not really a bug but a thing that would come handy. It would be + good if pax discarded the sufix ',v' from the filenames, this because + Unix RCS adds that kind of sufix and when extracting it creates an + invalid filename. In the current version 'RCS/filename.txt,v' extracts + correctly (because it already has a 3 char extension thus the ',v' is + discarded) but RCS/filename.c,v doesn't extract at all (and as there + are bugs in the option -s it is hard to extract those filenames) + (a good thing with tar/pax is that when creating a tar file if one + specifies a longer filename or a filename with a mixture of upper/lower + case the file will be stored in the tar file with that name, this + simplifies going back and forward between Ms-Dos/Unix if after truncation + and case convertion no filenames are equal. Thus it would be also good + if it did accept ',v' when creating a tar file under Ms-Dos) +# +# How to extract RCS files from a tar file if they have ',v' on their name +# +# Warning: pax has bugs, -i doesn't work, -s only works if all ocurrences of +# either '.' or '/' in the expression to match are matched by using '.' +# Assuming filenames of the type 'cpnauto/RCS/*.*,v', extracts to 'rcs/*.*' +# BUG: a filename without an extension (e.g. cpnauto/RCS/makefile,v') loses +# the last char (i.e. becomes 'rcs/makefil') +# +# Don't try to automate (e.g. to use args, the bugs in the handling of -s +# will probably defeat any attempts) +# +pax -f /cpnauto.tar >cpnauto.lst +pax -krvf /cpnauto.tar -s '/cpnauto\(.\)RCS.\([a-z0-9_]*\).\([a-z]*\),v/rcs\1\2.\3/p' `grep RCS cpnauto.lst` + +5- bin/tar.exe crashes (infinite loop?) when given wildcards, e.g. + 'tar tvf *.tar' from the Dos prompt (command.com) and there is a single + file with extension .tar in that dir, no problem if using the full + filename [check if it is the same as 4-c)]. It also crashes if a backslash + is given in a pathname (infinite loop) + +6- there is no online man page for dircmp(1) although 'man tkerrat' says there + is. Anyhow dircmp(1) works + + Usage: dircmp [-ds] dir1 dir2 + + -d -> works only for text files, shows differences in diff format + -s -> silent mode (non verbose, only the differences) + +7- 'make -f makefile' doesn't work, it always tries to use makefile.mak as + the makefile. Probably because 'whence -v makefile' says that makefile + is a function. Not checked any further + +8- man who(1) says that login(1) only uses the file $ROOTDIR/etc/utmp if + it already exists, this is not true, it is created if it does not exist. + Anyhow one can still install MKS in a READONLY drive because if etc/utmp is + set to READONLY it is not changed and there is no error message (it is not + enough to only have the drive but not the file write protected, in that case + one gets an error message: Write protect error writing drive ?) + +9- bin/cmp.exe gives an exit code of 3 if one of the files to be compared cannot + be open or doesn't exist, this is in contrast with the MKS man page (and with + Unix) which says that an error code of 2 is to be given. If only 1 filename + or more than 2 filenames are given, an exit code of 2 occurs, if one of the + files does not exist (the 1st or the 2nd) an exit code code of 3 occurs + +10- the date of the '.' and '..' entries given by 'ls -al' is wrong, it's + always 31-Dec-79 (i.e. 0, because the PC time starts at 1-Jan-80). In other + words if current dir is 'foo/bar' then to get the creation date of dir + 'bar' one has to do 'ls -ld ../bar' instead of just 'ls -ld .' + +11- 'vi -x pathname' where pathname is not from the current dir + or has dir names (e.g. ./filename.txt) + Under command.com do not use '/' but '\' otherwise one gets + 0 lines 0 chars (even if the file exists). Under the Korn shell + do not use '\\' but use '/' otherwise 0 lines 0 chars. + This has something to do with the implicit call to crypt (caused by + the -x). + This bug is not very serious because usually one uses '\' under + command.com and '/' under the korn shell, still all the MKS toolkit + commands are supposed to accept both (and I usually use '/' even + under command.com) diff --git a/web/noweb/contrib/avs/mksfixes.ksh b/web/noweb/contrib/avs/mksfixes.ksh new file mode 100644 index 0000000000..6ca89997de --- /dev/null +++ b/web/noweb/contrib/avs/mksfixes.ksh @@ -0,0 +1,15 @@ +if [ -z "$1" -o -z "$2" ] +then + echo Usage $0 BIN TMP >&2 + echo Fixes "'BIN/cpif.ksh'" for use with MKS Toolkit "(see 'man mks42bug', cmp entry)" >&2 + echo "Fixes 'BIN/noweb.ksh' for use with MKS toolkit (the PATH problem, see howto386.txt)" >&2 + echo TMP is for later use by cpif.ksh i.e. at run-time >&2 + echo "Changes only line 8 (if it has 'PATH='), line 20 (if it has 'new=') and line 28 (if it has '-eq0')" >&2 + exit 1 +fi + +cat $1/cpif.ksh | sed -e '8s/\(PATH=.*\)/#\1/' -e '20s@\(new=.*\)@new='$2'/$$@' -e '28s/-eq0.*/ -eq0|-ne1|*2|*3) cp $new $i/' > $2/cpif.tmp +mv $2/cpif.tmp $1/cpif.ksh + +cat $1/noweb.ksh | sed '21,26s/PATH="$PATH:$LIB"//' > $2/noweb.tmp +mv $2/noweb.tmp $1/noweb.ksh
\ No newline at end of file diff --git a/web/noweb/contrib/avs/myenv.ksh b/web/noweb/contrib/avs/myenv.ksh new file mode 100644 index 0000000000..d390df0c59 --- /dev/null +++ b/web/noweb/contrib/avs/myenv.ksh @@ -0,0 +1,38 @@ +# You should only edit the line ' myargs=...', see below + +echo Builds and installs Noweb 2.7 from source code +echo "Full documentation in 'howto386.txt'" +echo Assumptions: +echo "1- There is free environment space (between 30 and 40 bytes)" +echo "2- DJGPP is installed (gcc, go32, coff2exe, make)" +echo "3- MKS Toolkit 4.2 (or above?) for Dos is installed" +echo "4- The MKS Toolkit Make is the 1st make in your PATH env var" +echo "5- Icon 9.0 translator binaries are installed" +echo "6- You have enough free Ram (around 600000 bytes)" +echo "7- Your paths are not too long to break some script" +echo " (i.e. 128 bytes Dos command line limit)" +echo "8- To fully use Noweb, LaTeX2e already is or WILL be installed" +echo "9- You are running a not too old Dos version (e.g. 'call batchfile')" + +# Edit the 'myArgs=...' line to adapt for your environment: +# Use always FULLPATHS, i.e. with DRIVE LETTER +# Use only slashes '/' in pathnames, backslashes won't work +# BIN is where the noweb binaries will be installed +# LIB is where the noweb support files will be installed +# MAN is where to put the man pages (MANPATH env var or Mks ROOTDIR/etc) +# DJGPPmake is the fullpath to Gnu make (does not have to be in your PATH) +# TMP is a temporary directory to be used by Noweb at run-time +# ICON is the fullpath to icont (the Icon translator) +# (in both DJGPPmake and ICON the '.exe' is not necessary, +# remove it if you have problems with names too long) + +theArgs="BIN LIB MAN TEXINPUTS DJGPPmake TMP ICON" + myArgs="i:/b g:/usr/local/lib/noweb g:/man h:/emtex/texinputs/local j:/djgpp/bin/make.exe d:/tmp e:/b/icont.exe" + +echo "generate $theArgs" +echo "generate $myArgs" +read f?"Check if the line above is OK for your machine, continue (y/n)? " +if [ "$f" = "y" -o "$f" = "Y" -o "$f" = "yes" -o "$f" = "YES" ] +then + ./generate.ksh $myArgs +fi diff --git a/web/noweb/contrib/avs/norman1.txt b/web/noweb/contrib/avs/norman1.txt new file mode 100644 index 0000000000..5db826cb87 --- /dev/null +++ b/web/noweb/contrib/avs/norman1.txt @@ -0,0 +1,136 @@ +Hi Norman + +I'm one of your noweb fans (one of these days you'll get a postcard from +Denmark) since version 2.6c. I use it in several Unix systems (Sun4, Solaris, +HPUX, Linux) and in a PC running Dos. I am able to use Noweb in the same way +under both Dos and Unix. This means Dos+Icon+LaTeX2e in all platforms. + +When I downloaded Noweb 2.6c I had problems compiling it for Dos. In a way the +information on how to do it was there (e.g. the 'install.dos' file from Lee +Wittenburg), but it just gave general guidelines and it used Awk instead of +Icon. To be able to compile and use Icon took one day's work and several +machine crashes. So I wrote a kind of HOWTO for my own use when the next +version came around. + +Noweb 2.7 should have been easy, but it took a couple of hours because the +directory hierarchy had changed. Now there are Dos binaries on the +distribution, but they have a couple of problems (besides being from 2.6c). + +Today I just downloaded Noweb 2.7a. It has binaries for both 2.6c and 2.7, but +that version 2.7 if for the Watcom compiler (I have it but it is not installed) +and it uses Perl, which is nice but is risky if you change your code. +After half an hour I had updated my scripts to cope with version 2.7a and +after that I just compiled the source and got 2.7a Dos binaries. + +So I cleaned up my HOWTO and I'm asking if you would be interested in putting +it in the next noweb distribution. Here is why you might be interested: + +1- no binaries, only text files i.e. reduced space usage. You already have Dos +binaries in your distribution and you wouldn't want more. For those users that +don't manage to build it, I offer to supply ftp access to the binaries (all +they have to do is ask). Another advantage is that my scripts might work +without changes in the next Noweb version (if you don't change the directory +hierarchy nor the part of the makefiles that I'm patching nor add anything new +that requires patching). Gzip compressed the files take about 40 KB, expanded +about 112 KB. Notice that the scripts have comments and check for errors trying +to fail gracefully, otherwise they would be one fourth as big. Also more than +two thirds is documentation files + +2- the noweb/src/install.dos doesn't say enough (in my case it required 1 day's +work), noweb/binaries/dos-2.6c.zip is already two versions behind and +although it uses Icon it does not use Icon for a PC 386 (or above) thus memory +problems are to be expected. Also only some of the shell scripts were +translated to C++, thus using noweb at work (Unix) and home (Dos) wouldn't be +exactly the same. The same applies to the Dos binaries for version 2.7, using +Perl might make a difference + +3- minimum hardware/software requirements are a PC386 and the MKS Toolkit +(commercial software). Any user that is thinking about using Noweb in an +e.g. PC 286 is not doing serious work (or is brave). Thus asking for a 386 as a +minimum is reasonable. It is a pity that one has to rely on commercial software +but although there are some shareware Unix shells, I have no experience with +them thus I don't know how good they are. The MKS toolkit is very popular and +the price tag is quite reasonable (I think around $250). It closely follows +Posix, there are versions for Dos, OS/2 and Win NT. It offers a Korn shell and +all the standard Unix utilities, e.g. find, tar, man, awk, sed, vi, make. The +only thing missing is multitasking (I manage to get a kind of multitasking by +unning several Dos boxes under Windows, but that is another story). One can +even use '/' instead of '\'. + +4- if the user has the above then the missing parts can be downloaded. I supply +ftp sites and directories where to find DJGPP binaries (the PC386 Dos port of +gcc, gmake, gzip, etc), Icon PC386 binaries, and LaTeX2e PC386 binaries (I +supply an original document of John Refling annotated by me because the +installation for a normal user is a nightmare, with that document it is +straightforward, my annotations only fix some small bugs in that document and +explain a few more points). As an option GhostScript can also be used + +5- after the user has MKS, DJGPP, Icon, and LaTeX2e (latex2e can be installed +after noweb) properly installed, to get noweb 2.7a up and running all he/she +has to do is to download it and to edit a one line script (in the same fashion +as your noweb/src/nwmake) to specify the paths. + +6- that one line script works as follows: it supplies the args to another +script which generates a third script. If the user is lucky running that script +is enough (it works for me). Otherwise he/she has to look at that script and +compare it with my document which explains ALL the steps required for fixing +Noweb to run under Dos limitations. The generated script begins by renaming +some of your makefiles and replacing them with equivalent makefiles (patched +with awk scripts to circunvent Dos problems), then it uses GNU Make and Icon to +compile the Icon sources, GNU Make and gcc to compile the C sources, and MKS +Make and the Korn shell to install noweb. It might seem contrived but it avoids +out of memory errors and I want to use your makefiles (i.e. slightly patched +versions of them), not to supply my own makefiles which would be more sensitive +to changes in your source code + +7- for troubleshooting I supply a document that explains all the steps for +building Noweb, and I also supply a list of the MKS bugs that I discovered (one +of them while compiling Noweb). I also discovered a couple of bugs/problems in +your code, all of them trivial except one (the missing chunk problem, this is +easy to reproduce in Dos but tough in Unix). See the file +noweb/contrib/avs/report1.bug + +8- notice that the user gets EVERYTHING, e.g. 'cd noweb/src/xdoc; latex guide; +dvips guide' works (any missing METAFONT fonts will be generated on the +fly). Then if he/she does not have a PostScript printer he/she can get +GhostScript (mentioned in the ftpsites.txt) to translate for the printer at +hand. The same applies to all Noweb commands (they work as in Unix). This +allows using Noweb for doing serious literate programming on a Dos PC all the +way down to the printing process. + +9- I assume that the next version won't change the directory hierarchy thus my +scripts might work without change or will need half an hour work to update them +(I had to update my 2.7 scripts to 2.7a because you changed sensitive parts of +your makefiles) + +10- I don't supply makefiles for commercial C compilers. As noweb is free I'll +rather use as much free software as possible. MKS is the unfortunate exception. + +The tar file (39 KB) ftp.daimi.aau.dk:/pub/empl/avs/avs386_noweb27a.tar.gz +has files that expand into noweb/contrib/avs (I copied the style of the other +contributions, e.g. readme, email). By uncompressing noweb27a.tar.gz and +avs386_noweb27a.tar.gz from the same directory and by editing a single line of +my script 'noweb/contrib/avs/myenv.ksh' one can compile Noweb 2.7a for Dos. +Here is what that line looks like in my case: + generate i:/bin g:/usr/local/lib/noweb g:/man h:/emtex/texinputs/local j:/djgpp/bin/make.exe d:/tmp e:/bin/icont.exe + +If you think this is interesting you might +a) mention it in comp.programming.literate +b) add it to the noweb 2.7b distribution, after all now this allows me to build + and install Noweb 2.7a from the 2 tar files in just under 3 minutes ;-) + +Regards +/avs + +P.S: myself I switched to Linux but up to now Noweb under Dos helped me a +lot. Although I won't be using Noweb under Dos so much in the future I'm still +willing to support it (i.e. any eventual updates to my recipe). And if you ever +give me a prerelease of a new Noweb (with a week's notice) I will supply you +with either the Dos binaries for incorporating into the official distribution +or better still with updated patches/scripts + +P.S.: if you want the noweb 2.7a Dos binaries just ask + +P.S.: remember that the files in avs386_noweb27a.tar.gz were Dos manipulated. + To see any of those files under Unix (e.g. report1.bug) remember to do + tr -d '\015' < infile > outfile diff --git a/web/noweb/contrib/avs/nw_c.bat b/web/noweb/contrib/avs/nw_c.bat new file mode 100644 index 0000000000..d62641151e --- /dev/null +++ b/web/noweb/contrib/avs/nw_c.bat @@ -0,0 +1,63 @@ +@echo off +if %1.==. goto USAGE +if %DJGPPMAKE%.==. goto NOENVVAR +goto DOIT + +:NOENVVAR +echo Aborting, environment var DJGPPMAKE not set +goto THEEND +:USAGE +echo %0 DJGPPmakePath +echo ** Use backslash path in arg & slash path in DJGPPMAKE env var, e.g. +echo set DJGPPMAKE=j:/djgpp/bin/make +echo %0 j:\djgpp\bin\make +goto THEEND + +:FAILURE0 +echo Failed to patch src/c/finduses.c (probably the source lines to patch are +echo not any more at lines 44 and 65) +goto THEEND +:FAILURE2 +echo Failed to patch src/c/finduses.c, reason unknown +goto THEEND + +:DOIT +rem Requires DJGPP port of GNU gcc (gcc, make) +rem Beware of the Ms-Dos command line 128 chars limit! +rem MKS make won't do, use DJGPP make! + +rem Use UNIX style pathnames in DJGPPMAKE path, otherwise DJGPP make chokes! +rem This is used for make to launch submakes assuming that you might have 2 +rem different makes in your path, the MKS make (which we don't want to use) +rem and the DJGPP make (which we want to use) + +rem Avoid using broken tmpfile() function from DJGPP 'libc.a' +if not exist c\finduses.old cp c/finduses.c c/finduses.old +if errorlevel 1 goto THEEND +echo Patching lines 44 and 65 of src/c/finduses.c (DJGPP tmpfile() broken!) +sed '44s/FILE \*tmp = tmpfile()/char *tmpName;FILE*tmp=fopen(tmpName=tempnam(".",NULL),"w+")/' c/finduses.old>c\finduses.tmp +diff c/finduses.old c/finduses.tmp +if errorlevel 2 goto FAILURE2 +if errorlevel 1 goto STEP2 +if errorlevel 0 goto FAILURE0 +:STEP2 +sed '65s/add_use_markers(tmp, stdout);/add_use_markers(tmp, stdout); remove(tmpName);/' c/finduses.tmp > c\finduses.new +diff c/finduses.tmp c/finduses.new +if errorlevel 2 goto FAILURE2 +if errorlevel 1 goto STEP3 +if errorlevel 0 goto FAILURE0 +:STEP3 +rm c/finduses.tmp +if errorlevel 1 goto THEEND +cmp -s c/finduses.c c/finduses.new +if errorlevel 1 goto DIFFERENT +rm c/finduses.new +goto THEMAKE +:DIFFERENT +mv c/finduses.new c/finduses.c + +:THEMAKE +rem Use Ms-Dos style pathnames here, otherwise command.com chokes! +@echo on +%1 CC=gcc +:THEEND diff --git a/web/noweb/contrib/avs/nwicon.bat b/web/noweb/contrib/avs/nwicon.bat new file mode 100644 index 0000000000..d416013c2c --- /dev/null +++ b/web/noweb/contrib/avs/nwicon.bat @@ -0,0 +1,43 @@ +@echo off +if %1.==. goto USAGE +if %2.==. goto USAGE +if %3.==. goto USAGE +if %4.==. goto USAGE +if not %5.==. goto USAGE + +if exist ..\..\noweb\src\%0 goto DOIT +if exist ..\..\noweb\src\%0.bat goto DOIT +echo '..\..\noweb\src\%0' not found! +cd +echo Bad startup dir? Aborting. +echo Change to ./noweb/src and run the file installed by msdosfix.bat in there +echo (you cannot use the original in noweb\contrib\avs\nwicon.bat) +goto THEEND +:DOIT + +rem This represents 'make iconlib', but only like this I could put it to work +cd icon +rem j:\djgpp\bin\make ICONC=e:\\\\b\\\\icont ICONT=e:\\\\b\\\\icont +%3 ICONC=%4 ICONT=%4 +if errorlevel 1 goto THEEND +cp -p totex.exe ../lib +if errorlevel 1 goto THEEND +cp -p tohtml.exe ../lib +if errorlevel 1 goto THEEND +cp -p noidx.exe ../lib +if errorlevel 1 goto THEEND +cp -p noindex.exe ../shell +if errorlevel 1 goto THEEND +rem j:\djgpp\bin\make ICONC=e:\\\\b\\\\icont ICONT=e:\\\\b\\\\icont LIB=g:/usr/local/lib/noweb BIN=i:/b install +%3 ICONC=%4 ICONT=%4 LIB=%2 BIN=%1 install +if errorlevel 1 goto THEEND +cd .. +goto THEEND + +:USAGE +if not %1.==. echo Wrong usage: %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 +echo Usage: %0 BIN LIB DJGPPmakeBackslashPath 4BackslashedIconTranslatorPath +echo e.g. %0 i:/b g:/usr/local/lib/noweb j:\djgpp\bin\make.exe e:\\\\b\\\\icont.exe +echo If you are running this as part of another script abort with CTRL-C +pause +:THEEND diff --git a/web/noweb/contrib/avs/nwinst.ksh b/web/noweb/contrib/avs/nwinst.ksh new file mode 100644 index 0000000000..6e11001f5a --- /dev/null +++ b/web/noweb/contrib/avs/nwinst.ksh @@ -0,0 +1,9 @@ +export SHELL=${SHELL:=$ROOTDIR/bin/sh.exe} +if [ -z "$1" ] +then + echo "Usage: $0 BIN LIB MAN TEXINPUTS" + echo "-- Installs noweb using icon" + exit 1 +fi + +make CC="gcc" BIN=$1 LIB=$2 MAN=$3 TEXINPUTS=$4 LIBSRC=icon install diff --git a/web/noweb/contrib/avs/readme b/web/noweb/contrib/avs/readme new file mode 100644 index 0000000000..f284d69d52 --- /dev/null +++ b/web/noweb/contrib/avs/readme @@ -0,0 +1,39 @@ +version 0.3 (30-May-95) + +How to install noweb 2.7a in a PC386 or above running Dos if you have Mortice +Kern Systems' MKS Toolkit 4.2 for Dos. The rest of the software can be obtained +by ftp (GNU DJGPP, Icon binaries, LaTeX2e, GhostScript). Complete details on +how to get and install everything are provided + + a) Look at filelist.txt, and if you need something look at ftpsites.txt + b) To install noweb if you are lucky you just have to edit 'myenv.ksh' and + to run it, e.g. from the Ms-Dos command.com prompt do: + cd noweb\contrib\avs + edit myenv.ksh + sh -c ./myenv.ksh + automate + c) Test the installation by doing (from the Korn shell prompt): + cd noweb/src/tex + noweb support.nw + latex support + latex support + latex support + v support + (assuming your dvi viewer is a batch file called v.bat) + dvips -o support.ps support + d) See if you have the same problem as I do. If section 2.2 is missing + from page 7, chunk 9b from page 9 and chunk 28b from page 28 then + you also have that problem that some code/doc chunks come out as white + space. The current fix is to add a newline at the start/end of the chunk + to make a paragraph. See report1.bug + e) Further technical details on howto386.txt + +If using this recipe you still run into trouble contact me. As a last resort +I'm willing to supply you by anonymous ftp with everything you need to run +noweb 2.7a (except the MKS toolkit which is commercial software, i.e. I can +supply binaries for LaTeX2e, Icon 9.0 and Noweb 2.7 that fit in 4 * 1.44 MB +floppies) + +My recipe has only been tested in my machine (which is heavily loaded, dozens +of TSR's and has recent versions of most software). I will appreciate receiving +comments that allow it to run smoothly in other environments diff --git a/web/noweb/contrib/avs/report1.bug b/web/noweb/contrib/avs/report1.bug new file mode 100644 index 0000000000..c19c2abcd4 --- /dev/null +++ b/web/noweb/contrib/avs/report1.bug @@ -0,0 +1,97 @@ +Noweb 2.7a bug report (avs@daimi.aau.dk, 29-May-95) + +***************** +"strip problem in hpux8" + +In noweb/src/Makefile the strip command in an HPUX8 machine causes the make to +fail. Commenting out the 'strip' fixes the problem + +***************** +\n problem in -option output of 'noweave -h' + +The 'Add \noweboptions{opt} to ...' should be 'Add noweboptions{opt} to ...' + +***************** +Dates problem in source code distribution (as of 29-May-95) + +I used ftp get with on the fly compression of the noweb dir to a .tar.gz file +thus I got the original dates (I didn't cause the dates problem) + +The .nw files are not always older than the generated files and the same +applies to the man page files (regarding the nroff processed man pages). The +problem seems to be that the dates are identical instead of say 1 second older. +A simple fix is: + +cd noweb/src +find . -name "*.nw" -exec touch -t 9502231200 "{}" \; +find xdoc -name "*.1" -exec touch -t 9502241200 "{}" \; + +***************** +Style hook in 'src/tex/support.nw' page 21 + +The margin note style hook in page 21 is wrong handed (should have come on the +right instead of on the left) + +***************** +There is a problem with noweb 2.7a/LaTeX: some chunks might disappear from the +LaTeX output, i.e. they are in the output .tex file but in the .dvi file they +come out as white space (sometimes this is noticed because it seems strange +that a page has a big block of white space in the middle, but if it is in the +end of the page the disappearance might go unnoticed) + +A similar problem was found with noweb 2.6c and 2.7 (I didn't report it +before). I was once able under Unix (HPUX8) to see the problem, but +unfortunately I'm unable to reproduce it (sorry!). I can see it under Dos with +e.g in 2.7a + cd noweb/src/tex + noweb support.nw + latex support + latex support + latex support + v support + (v is the Dos equivalent of xdvi) +The section 2.2 in page 7 and the chunks 9b and 28b come out as a white +rectangle in the middle of the page. If you want I can send you the .dvi files +(Dos generated) that show the disappearance. This is a subtle problem + +The problem can always be solved by adding an empty newline (to make a +paragraph). In my noweb source file the problem causes a chunk of documentation +to disappear. A doc chunk comes after a code chunk and if I started with '@ +...', i.e. a '@' followed by a blank followed by the text the chunk came out as +white space, but if I added a newline to the '@' (i.e. started the text in the +next line) everything was OK (the lost chunk could be seen in the .tex file, it +just didn't make to the .dvi file). This didn't happen everywhere or always +only in some doc or code chunks and only in some particular circunstances, +i.e. adding a couple of lines several pages before (which changed the page +layout in the problematic page) made the problem disappear + +Using diff to compare the .tex outputs I noticed that the only difference is +that the \n makes its way to the .tex output thus the problem does not seem to +be with the noweb scripts but with the noweb LaTeX support code (nwmac.tex or +noweb.sty) + +Or maybe it's a LaTeX bug (it is NOT only a bug in the emTeX Dos port because +the first time I saw this it was in an Unix machine running Latex 2.09) + +Other symptoms: + a) using the .dvi file (Dos generated) and seeing it under Linux + caused a crash (floating point exception) when viewing the affected + pages (7, 9, 28). No problem if those pages were skipped (i.e. never + displayed on the screen). When with a now lost .nw file + I saw chunks disappearing under HPUX8 I also got floating point + exceptions and core dumps (until I made a slight change in the page + layout which caused the core dumps to disappear, still the missing + chunks hapenned still later on). Seeing the .dvi file under Dos does + not cause any crashes, only a white rectangle in the affected area + b) using the .tex file (Dos generated) caused no problem under Linux + +My tentative diagnostic: + - it is not a LaTeX2e specific problem (I saw it under LaTeX) + - it is not a Dos specific problem (I saw it under HPUX8) + - it is not a noweb/noweave problem (if the .tex output is manipulated + under another machine it works fine) + - it is either in the LaTeX support file noweb.sty or in LaTeX, still + it is a machine dependent problem rare under Unix frequent under Dos + +If I manage to pinpoint the problem, or reproduce it under Unix I'll let you +know diff --git a/web/noweb/contrib/conrado/Makefile b/web/noweb/contrib/conrado/Makefile new file mode 100644 index 0000000000..f7a9ee59e2 --- /dev/null +++ b/web/noweb/contrib/conrado/Makefile @@ -0,0 +1,15 @@ +LIB=/dev/null # to be overridden by install + +.SUFFIXES: .nw .icn +.nw.icn: ; notangle -L'#line %-1L "%F"%N' $*.nw | cpif $*.icn + +all: d2tex +source: d2tex +install: + cp d2tex $(LIB)/dijkstra.filter + +# TeX files. +hospital.tex: hospital.nw d2tex + noweave -delay -filter ./d2tex hospital.nw > hospital.tex +clean: + /bin/rm -f hospital.tex *.dvi *.aux *.log *.blg *.bbl *~ diff --git a/web/noweb/contrib/conrado/README b/web/noweb/contrib/conrado/README new file mode 100644 index 0000000000..c21ed85589 --- /dev/null +++ b/web/noweb/contrib/conrado/README @@ -0,0 +1,38 @@ +This is the README file for the collection of files related to d2tex. +These files are: + +- algoritmos.sty: +defines the 'code' environment and several other +related environments (code*, algorithm, ...); the implementation is +not documented and can be difficult to understand, but I have +documented the main features of the environments. +- d2tex: +an noweb filter (written in awk) that prettyprints; for instance, + /* this is a comment */ is replaced by \COMMENT{this is a comment} + <= is replaced by \le + PROCEDURE initialize is replaced by \PROCEDURE |initialize| + +this script does not parse the input; it only uses pattern-matching, +but produces reasonable output if some thumb rules are followed. +d2tex has a list of keywords built in. + +- keywords.tex: + +contains TeX macro definitions for producing keywords in boldface, +special simbols (as the boxes between guarded commands), etc.; most of +the macros include indentation macros \tab and \untab; \tab is sets a +tab and moves the margin tab to the right, while \untab removes a tab +stop and moves to the left the margin tab. Since the 'code' +environment is based upon the tabbing environment, it is useful for +the keyword macros (such as \WHILE) to include these tab/untab +macros. \tab and \untab can be used inside the 'code' environment when +needed, for instance, to override the default indentation or to +correct minor mistakes. + +- hospital.nw: +a weird example of the type of code that d2tex is 'able' to +prettyprint; the documentation is not written in english (several +paragraphs and comments in the code are written in catalan) and the +code is not complete. + + diff --git a/web/noweb/contrib/conrado/algoritmos.sty b/web/noweb/contrib/conrado/algoritmos.sty new file mode 100644 index 0000000000..23bbc711f3 --- /dev/null +++ b/web/noweb/contrib/conrado/algoritmos.sty @@ -0,0 +1,169 @@ +\catcode`@=11 +% +% \listofalgorithms +% +% this macro is like \listoffigures or \listoftables; it prepares a +% list of algorithms/programs using an auxiliary file (.lop) +% CUSTOMIZATION: the banner of the list is a string defined by \listalgorithmsname; +% the default value is ``List of Algorithms'' + +\def\listofalgorithms{\@restonecolfalse\if@twocolumn\@restonecoltrue\onecolumn + \fi\chapter*{\listalgorithmsname\@mkboth + {\uppercase{\listalgorithmsname}}{\uppercase{\listalgorithmsname}}} + \addcontentsline{toc}{chapter}{\listalgorithmsname} + {\ssp\@starttoc{lop}}\if@restonecol + \twocolumn\fi} +\let\l@algorithm\l@figure + +\def\listalgorithmsname{List of Algorithms} + +% \begin{algorithm} ... \end{algorithm} +% +% the 'algorithm' environment encloses a float object (like figure or table) +% algorithm's get their own numbering; captions begin with +% ``Algorithm'', then the number, and then the text; +% and there is an entry in the .lop file for each 'algorithm' +% CUSTOMIZATION: the first word in a caption is given by \algorithmname + +\newcounter{algorithm} +\def\thealgorithm{\@arabic\c@algorithm} +\def\fps@algorithm{tbp} +\def\ftype@algorithm{4} +\def\ext@algorithm{lop} +\def\fnum@algorithm{\algorithmname\ \thealgorithm} +\def\algorithm{\@float{algorithm}} +\def\endalgorithm{\end@float} +\@namedef{algorithm*}{\@dblfloat{algorithm}} +\@namedef{endalgorithm*}{\end@dblalgorithm} + +\def\algorithmname{Algorithm} + +% \begin{code} ... \end{code} +% +% this environment is a slight modification of the tabbing environment. +% the specific characteristics are: +% 0) it has a parameter from xref information +% 1) there is a small skip between the paragraph above and the first +% line of the code. +% 2) a rule is drawn at the beginning and at the end of the code. +% 3) all lines are typeset in math mode +% 4) |<something>| prints <something> in \rm if issued inside math +% mode (for instance, inside this environment); +% and in \sl if issued outside math mode +% 5) _ can be used instead of \_ inside the code environments and in +% text; it produces subscripts in math mode as usual +% 6) <return>'s are obeyed; if you want to break lines in the source +% but not in the final document terminate the line with a comment char % +% 7) you can leave a blank line using \\ +% +% it also introduces a command \numberlines that has no effect in this +% environment, but it has in the *-form of code +% CUST: \algosep and \algoruleheight are the length parameters that +% give the amount of skip between the previous paragraph and the +% beginning of code, and the width of the rule. +% Both have default value 0pt. \algofontsize allows changing the font size +% of the text written inside a code environment. The default is \footnotesize + + +\def\numberlines{} +\newlength{\algosep} +\newlength{\algoruleheight} +\def\rulecode{\rule{\textwidth}{\algoruleheight}} +\def\sepcode{\vspace{\algosep}} +\def\algofontsize{\footnotesize} +\newif\ifinsidecode +\insidecodefalse + +\def\@programcr{$\@tabcr$} + +{\catcode`\;=\active \gdef;{\semicolon\;}} +\mathchardef\semicolon="603B + +\catcode`\_=\active \gdef_{\ifinsidecode\_\else\ifmmode\sb\else\_\fi\fi} + +\catcode`\|=12\relax +\def\origbar{|} +\catcode`\|=\active + +\def|{\ifx\@sharp\relax\origbar\else\@dovar\fi} +\def\@dovar#1#2|{#1\ifmmode{\mbox{\rm #2}}\else{\sl #2}\fi} + + +\def\@tabcommandson{% activate tabbing commands: + % \tab sets a tab stop and adds one to the margin tab: + \def\tab{$\=\+$} + % Must finish current field before testing if at margin: + \def\@finishfield{\@stopfield\@addfield\@startfield} + % \untab removes one tab stop and moves left if at the beginning of a line: + \def\untab{$\@finishfield\@ifatmargin\@ltab\else\relax\fi\-$} + % \@marginspace adds an extra space unless there is no text on the line: + \def\@marginspace{$\@finishfield$\@ifatmargin\relax\else\ \fi}} + +\def\@tabcommandsoff{% deactivate tabbing commands: + \let\tab=\relax + \let\@finishfield=\relax + \let\untab=\relax + \let\@marginspace=\ % never at margin thus always a space +} +\@tabcommandsoff + +\def\code#1{\par\sepcode +\noindent\rulecode\algofontsize\numberlines\insidecodetrue +\@tabcommandson \obeycr +\lineskip \z@\let\>\@rtab\let\<\@ltab\let\=\@settab + \let\+\@tabplus\let\-\@tabminus\let\t\tab\let\u\untab +\let\\=\@programcr +\global\@hightab\@firsttab \global\@nxttabmar\@firsttab +\dimen\@firsttab\@totalleftmargin \global\@tabpush0 +\global\@rjfieldfalse \trivlist +\item[]\if@minipage\else\vskip\parskip\fi +\setbox\@tabfbox\hbox{\rlap{\indent\hskip\@totalleftmargin +\the\everypar}}\def\@itemfudge{\box\@tabfbox}\@startline\ignorespaces +$\@gobblecr +} + +\def\endcode{$\@stopline\ifnum\@tabpush > 0 \@badpoptabs +\fi\endtrivlist\noindent\@tabcommandsoff +\rulecode +\sepcode +} + +% \begin{code*} ... \end{code*} +% +% the *-form of code numbers sequentially each line of the code (lines +% are separated by \\'s) +% CUST: the label of each line can be easily changed, by redefining +% the macro \codelinelabel and/or the macro \thecodeline; the counter +% is codeline. + +\newcounter{codeline} +\def\thecodeline{\arabic{codeline}} +\def\codelinelabel{\thecodeline.\ \ } + +\@namedef{code*}{\def\numberlines{\setcounter{codeline}{0} +\def\@stopline{\addtocounter{codeline}{1} +\unskip\@stopfield\if@rjfield \global\@rjfieldfalse + \@tempdima\@totalleftmargin \advance\@tempdima\linewidth +\hbox to\@tempdima{\@itemfudge\codelinelabel\hskip\dimen\@curtabmar + \box\@curline\hfil\box\@curfield}\else\@addfield + \hbox{\@itemfudge\codelinelabel\hskip\dimen\@curtabmar\box\@curline}\fi} +}\code} +\@namedef{endcode*}{\endcode} + +\catcode`@=12 + +% \begin{cntcode} ... \end{cntcode} +% +% a slight variation of the code environment, cntcode centers its +% contents; it should be used only if there is no page break in +% the middle of the code; that means that its usefulness is limited to +% small chunks of code, specifications, short declarations, ... +\newlength{\codewidth} +\setlength{\codewidth}{0.7\textwidth} +\newenvironment{cntcode}{\def\sepcode{}\def\rulecode{}\centering +\begin{minipage}[t]{\codewidth} +\begin{code}}{\end{code}\end{minipage} +\par} + + + diff --git a/web/noweb/contrib/conrado/d2tex b/web/noweb/contrib/conrado/d2tex new file mode 100755 index 0000000000..5b807b7ce0 --- /dev/null +++ b/web/noweb/contrib/conrado/d2tex @@ -0,0 +1,144 @@ +#! /bin/sh +KEYGEN=$(mktemp) +trap "rm -f $KEYGEN; exit 1" 1 2 3 15 +cat > $KEYGEN <<END_OF_FILE +COMMENT#1 +ASSERT#1 +PROGRAM +ENDPROGRAM +USES +ENDUSES +MODULE +ENDMODULE +SPECIFICATION +ENDSPECIFICATION +IMPLEMENTATION +ENDIMPLEMENTATION +IMPORT +ENDIMPORT +TYPE +ENDTYPE +VAR +ENDVAR +CONST +ENDCONST +ARRAY +RECORD +ENDRECORD +BEGIN +IF +ELSE +ENDIF +THEN +SKIP +WHILE +ENDWHILE +DO +DDO +ENDDO +FORALL +FOR +ENDFOR +ENDFORALL +PARALLEL +REPEAT +UNTIL +AND +OR +NOT +CAT +OF +IN +DIV +MOD +PROCEDURE +ENDPROCEDURE +FUNCTION +ENDFUNCTION +RETURNS +INP +OUTP +INOUTP +PRIVATE +@@@ +END_OF_FILE + +nawk ' +BEGIN { code=0 ; quoting=0 ; inside_comm=0 + while ((getline kw < "'"$KEYGEN"'") >0) + keywords[kw]++ + } +/^@begin code/ { + printf "@literal \\begin{code}{%s}\\let\\maybehbox=\\hbox\n", substr($0, 13) + code=1; next +} +/^@end code/ { code=0 ; printf "@literal \\end{code}\n"; next} +/^@quote$/ { quoting = 1 } +/^@endquote$/ { quoting = 0 } +/^@text / { if (code) { print_code(substr($0, 7)) } + else { print } + next + } +{print} +function print_code(line) { + temp = line; + comm = ""; + if (temp ~ /\/\*/) + { r = index(temp, "/*"); + inside_comm = 1; comm = "\\COMMENT{" substr(temp, r+3); + if (comm ~ /\*\//) { inside_comm = 0; comm = comm "}"; temp = ""; } + } + if (temp ~ /\*\//) + { comm = temp "}"; temp = ""; inside_comm = 0; } + + sub(/\*\//,"",comm); + sub(/\/\*.*/,"",temp); + + if (temp != "" && inside_comm == 0) + { + temp = operators(temp); + temp = substitute_keywords(temp); + temp = mark_vars_types(temp); + } + line = temp comm; + print "@literal " line; +} + +function substitute_keywords(s, kw) { + for (kw in keywords) + if (s ~ kw) + { + gsub("(^|[ \\t]+)" kw "($|[ \\t]+)", " \\" kw " ", s); + gsub("(^|[ \\t]+)" kw ";", " \\" kw ";", s); + gsub("(^|[ \\t]+)" kw "\\.", " \\" kw ".", s); + gsub("[(]" kw, "(\\" kw, s); + gsub("," kw, ",\\" kw, s); + } + return s +} + +function mark_vars_types(s ) { + gsub(/\\OF[ \t\n]*[a-zA-Z_]+/,"|&|", s); + gsub(/\\RETURN[ \t\n]*[a-zA-Z_]+/,"|&|", s); + gsub(/[a-zA-Z_]+[ \t\n]*=[ \t\n](\\RECORD|\\ARRAY)/,"|&|", s); + gsub(/[a-zA-Z_]+[ \t\n]*=[ \t\n]\\{/,"|&|",s); + gsub(/:[\t\n ]*[a-zA-Z_]+/, "|&|" ,s); + gsub(/[a-zA-Z_]+[ \\t\\n]*\(/,"|&|",s); + gsub(/\(\|/, "|(", s); + gsub(/\|:[ \t\n]*/, ":|", s); + gsub(/\|\\de[ \t\n]*/, "\\de |", s); + gsub(/\|\\RETURN[ \t\n]*/, "\\RETURN |", s); + gsub(/\=[\t\n ]*\\ARRAY\|/, "| = \\ARRAY", s); + gsub(/\=[\t\n ]*\\RECORD\|/, "| = \\RECORD", s); + gsub(/\=[\t\n ]*\\{\|/, "| = \\{", s); + return s +} + +function operators(s ) { + gsub(/ >= /," \\ge ", s); + gsub(/ <= /, " \\le ", s); + gsub(/ != /, " \\not= ", s); + return s; +} +' "$@" +rm -f $KEYGEN diff --git a/web/noweb/contrib/conrado/email b/web/noweb/contrib/conrado/email new file mode 100644 index 0000000000..ef96d5bb44 --- /dev/null +++ b/web/noweb/contrib/conrado/email @@ -0,0 +1 @@ +conrado@moon.upc.es (Conrado Martinez-Parra) diff --git a/web/noweb/contrib/conrado/hospital.nw b/web/noweb/contrib/conrado/hospital.nw new file mode 100644 index 0000000000..46b5d9fe1f --- /dev/null +++ b/web/noweb/contrib/conrado/hospital.nw @@ -0,0 +1,165 @@ +\documentstyle[noweb,algoritmos]{article} +\input{keywords} +\title{Hospital General} +\author{Conrado Mart\'{\i}nez-Parra} +\pagestyle{plain} +\begin{document} +\maketitle + +@ The problem. + + +An hospital has $np$ floors ($1\le np\le |MAX_PISOS|$), +the $i$-th floor has $nh_i$ rooms +($1\le nh_i\le |MAX_HAB|$) and the $j$-th room of the $i$-th floor +has $nll_{i,j}$ beds ($1\le nll_{i,j}\le |MAX_LLITS|$). The number of +beds that are occupied by patients in the $j$-th room of the $i$-th +floor in a given moment is denoted by $oc_{i,j}$. + +\smallskip + +The patients are distributed in the hospital according to one of the +following three cathegories: {\tt child}, {\tt men}, {\tt women}. +All the patients in a given room must belong to the same cathegory. + +Consider the following type definitions: +<<definicio de tipus>>= +TYPE operation_class = { ingress, dismin, null }; + cathegory = { child, women, men }; + + id_llit = RECORD + pis, hab, llit : integer; +/* any bed can be identified by a tuple +(floor number, room number, bed number) */ + ENDRECORD; + + operation = RECORD + class : operation_class; + cat_ingress_patient : categhory; +/* used only in ingress operations */ + vacant_bed : id_llit; +/* used only for dismin operations */ + ENDRECORD; + +ENDTYPE +@ + + +@ The solution. +<<definicio de tipus>>= +TYPE hospital = RECORD + nr_pisos : integer; + pisos : ARRAY [1..MAX_PISOS] OF pis; + /* com que el tipus 'taula [...] de pis' es + totalment accesori, no ho definim per separat; fem el + mateix en la definicio del tipus 'pis' */ + ENDRECORD; + pis = RECORD + nr_habitacions : integer; + habs : ARRAY [1..MAX_HABIT] OF pis; + ENDRECORD; + +ENDTYPE + +@ +Pel que fa a cadascuna de les habitacions, ens caldr\`a saber: 1) +quants llits hi ha; 2) quants llits estan ocupats; 3) la categoria +dels pacients (buida, si no n'hi ha cap); 4) l'estat de cadascun dels +llits. Nom\'es ens caldr\`a saber la categoria d'un pacient, ja que +tots els pacients dins una habitaci\'o tenen la mateixa categoria. +Aix\'{\i} doncs, proposem la seg\"uent definici\'o: + +<<inicialitzar hospital>>= +PROCEDURE inicialitzar(OUTP H : hospital) +VAR i,j,k : enter +ENDVAR + + llegir(H.nr_pisos); + + /* hem obtingut el nombre de pisos del hospital */ + + i:= 1; + + WHILE i <= H.nr_pisos DO + + llegir(H.pisos[i].nr_nabitacions); + i:= i + 1 + + ENDWHILE; + + /* hem obtingut el nombre d'habitacions per a cada pis */ + + i:= 1; + + WHILE i <= H.nr_pisos DO + + j:= 1; + WHILE j <= H.pisos[i].nr_habitacions DO + + llegir(H.pisos[i].habs[j].nr_llits; + /* hem obtingut el nombre de llits per a cada habitacio */ + H.pisos[i].habs[j].nr_ocupats:= 0; + H.pisos[i].habs[j].cat_pacients:= buida; + + k:= 1; + WHILE k <= H.pisos[i].habs[j].nr_llits DO + + H.pisos[i].habs[j].ocupat[k]:= false; + k:= k + 1 + + ENDWHILE; + + /* hem fet les definicions que indiquen que l'habitacio es + buida */ + j:= j + 1 + + ENDWHILE; + + i:= i + 1 + + ENDWHILE + +ENDPROCEDURE +@ + +<<*>>= +PROGRAM Hospital + +<<definicio de tipus>> + +VAR op : operacio; + H : hospital; +ENDVAR + + inicialitzar_hospital(H); + + llegir_operacio(op); + + WHILE op.classe != nul DO + + tractar_operacio(op,H); + llegir_operacio(op) + + ENDWHILE + +ENDPROGRAM. + +@ + +<<tractar l'operacio en curs $op$, modificant l'estat de l'hospital $H$>>= +PROCEDURE tractar_operacio(INP op : operacio; INOUTP H : hospital) +VAR ll_asgn: id_llit; +ENDVAR + + IF op.classe = ingres THEN ingres(op,cat_pacient_ingressat, H, ll_asgn); + escriure("Ingres:",ll_asgn.pis,ll_asgn.hab, + ll_asgn.llit); + + ELSE op.classe = baixa THEN donar_baixa(op.llit_abandonat,H); + + ENDIF + +ENDPROCEDURE; + +@ +\end{document} diff --git a/web/noweb/contrib/conrado/keywords.tex b/web/noweb/contrib/conrado/keywords.tex new file mode 100644 index 0000000000..7dc7c2968c --- /dev/null +++ b/web/noweb/contrib/conrado/keywords.tex @@ -0,0 +1,59 @@ +\def\COMMENT#1{\{\parbox[t]{\codewidth}{\rm #1\}}} +\def\ASSERT#1{\{\mbox{\rm #1}\}} +\def\PROGRAM{{\bf pro}\tab{\bf gram\ }} +\def\ENDPROGRAM{\untab{\bf end}} +\def\USES{{\bf uses\ }\tab} +\def\ENDUSES{\untab{\bf end}} +\def\MODULE{{\bf mod}\tab{\bf ule\ }} +\def\ENDMODULE{\untab{\bf end}} +\def\SPECIFICATION{{\bf spe}\tab{\bf cification\ }} +\def\ENDSPECIFICATION{\untab{\bf end}} +\def\IMPLEMENTATION{{\bf imp}\tab{\bf lementation\ }} +\def\ENDIMPLEMENTATION{\untab{\bf end}} +\def\IMPORT{{\bf imp}\tab{\bf orts\ }} +\def\ENDIMPORT{\untab{\bf end}} +\def\TYPE{{\bf type\ }\tab} +\def\ENDTYPE{\untab} +\def\VAR{{\bf var\ }\tab} +\def\ENDVAR{\untab} +\def\CONST{{\bf const\ }\tab} +\def\ENDCONST{\untab} +\def\ARRAY{{\bf array\ }} +\def\RECORD{\tab{\bf rec}\tab{\bf ord\ }} +\def\ENDRECORD{\untab{\bf end}$\-$} +\def\BEGIN{} +\def\IF{{\bf if }\tab\ \ \tab} +\def\ELSE{\untab\untab [\!]$\>$} +\def\ENDIF{\untab\untab {\bf fi}} +\def\THEN{\ \longrightarrow\ \ \tab} +\def\SKIP{\emptyset} +\def\WHILE{{\bf whi}\tab{\bf le\ }} +\def\ENDWHILE{\untab{\bf end}} +\def\DO{{\bf\ do\ }} +\def\DDO{{\bf do\ }\tab} +\def\ENDDO{\untab{\bf end}} +\def\FORALL{{\bf for}\tab{\bf\ all\ }} +\def\FOR{{\bf for}\tab\ } +\def\ENDFOR{\untab{\bf end}} +\def\ENDFORALL{\untab{\bf end}} +\def\PARALLEL{{\bf\ parallel\ }} +\def\REPEAT{{\bf rep}\tab{\bf eat\ }} +\def\UNTIL{\untab{\bf until\ }} +\def\AND{\mathbin{\hbox{\bf and}}} +\def\OR{\mathbin{\hbox{\bf or}}} +\def\NOT{\mathop{\hbox{\bf not}}} +\def\CAT{\mathbin{\&}} +\def\OF{{\bf\ of\ }} +\def\IN{{\bf\ in\ }} +\def\DIV{\mathbin{\hbox{\bf div}}} +\def\MOD{\mathbin{\hbox{\bf mod}}} +\def\PROCEDURE{{\bf pro}\tab{\bf cedure\ }} +\def\ENDPROCEDURE{\untab{\bf end}} +\def\FUNCTION{{\bf fun}\tab{\bf ction\ }} +\def\ENDFUNCTION{\untab{\bf end}} +\def\RETURNS{{\bf return\ }} +\def\INP{{\bf in\ }} +\def\OUTP{{\bf out\ }} +\def\INOUTP{{\bf in/out\ }} +\def\PRIVATE{{\bf private\ }} + diff --git a/web/noweb/contrib/davelove/Makefile b/web/noweb/contrib/davelove/Makefile new file mode 100644 index 0000000000..5a349d23c1 --- /dev/null +++ b/web/noweb/contrib/davelove/Makefile @@ -0,0 +1,6 @@ +SHELL=/bin/sh +all: +source: +install: +clean: + /bin/rm -f *.dvi *.log *.aux diff --git a/web/noweb/contrib/davelove/README b/web/noweb/contrib/davelove/README new file mode 100644 index 0000000000..2ca66a6bac --- /dev/null +++ b/web/noweb/contrib/davelove/README @@ -0,0 +1,2 @@ +subref.doc is a literate version of the code that noxref uses to +number definitions 7a, 7b, 7c, and so on. diff --git a/web/noweb/contrib/davelove/email b/web/noweb/contrib/davelove/email new file mode 100644 index 0000000000..eeaf4fb95f --- /dev/null +++ b/web/noweb/contrib/davelove/email @@ -0,0 +1 @@ +d.love@daresbury.ac.uk diff --git a/web/noweb/contrib/davelove/subref.doc b/web/noweb/contrib/davelove/subref.doc new file mode 100644 index 0000000000..93a5c48d8d --- /dev/null +++ b/web/noweb/contrib/davelove/subref.doc @@ -0,0 +1,235 @@ +%<*x> ^^A -*-latex-*- +% [Standard D. Carlisle boilerplate.] +% This file may be used without modification as a style (.sty) file. +% +% If you have Mittelbach's doc.sty, this file may be formatted with a +% command like: +% latex subref.sty +% +% If you have the Mittelbach/Duchier/Braams docstrip utility, you may +% produce a faster loading .sty file. +% Rename this file to: subref.doc +% Then run this file through *plain* TeX: +% tex subref.doc +% This should produce the file subref.sty. +% If you do not have plain TeX on your system, you can trick LaTeX into +% doing the work as follows: +% latex \def\fmtname{plain} \input subref.doc +% Note that you may need to quote the arguments here to stop your +% operating system treating the \ characters incorrectly. +% +% latex subref.doc +% Will produce a typeset version of the documentation, as above. +% +% [Although this is a fairly trivial style, it is for a literate +% programming task, so it better be written literately, i.e. with the +% `doc' option.] +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\def\plain{plain}\ifx\fmtname\plain\csname fi\endcsname + \def\batchfile{subref.doc} + \input docstrip + \preamble + + Copyright D.Love, SERC Daresbury Laboratory, 1993 + The `doc' version of this style is re-distributable and usable + under conditions of the GNU copyleft, but please mark any changes, + list them here and report any major enhancements to the author. + Do not distribute the stripped version of this file. + + \endpreamble + \generateFile{subref.sty}{t}{\from{subref.doc}{}} + \endinput +\fi +% +\ifcat a\noexpand @\let\next\relax\else\def\next{% + \documentstyle[doc%,a4 + ,subref]{article}\MakePercentIgnore}\fi\next +% +%\def\eatmodule<#1>{}\eatmodule +%</x> +% \def\fileversion{1.0} +% \def\filedate{7/7/93} +% \def\docdate {7/7/93} +% \CheckSum{113} +%% \CharacterTable +%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +%% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \textwidth=355pt ^^A Allow macrocode text with 72 columns. +% \CodelineIndex ^^A Code lines numbered. +% \DisableCrossrefs ^^A No Cross references. +% \MakeShortVerb{\"} ^^A "\foo" works like \verb+\foo+ +% +% \title{{\tt subref.sty}:\\Counting references on pages\thanks{This +% file has version number \fileversion{} dated \filedate{}. The +% documentation was last revised on \docdate.}} +% \author{Dave Love} +% \date{} +% \begin{document} +% \maketitle +% \begin{abstract} +% \noindent This \LaTeX{} style option +% provides a mechanism for defining `page +% sub-references' using "\sublabel{foo}" referenced with +% "\subpageref{foo}". Sub-references will be numbered like these real +% examples: \subpageref{ref:foo}, \subpageref{ref:bar}, +% \subpageref{ref:baz}\sublabel{ref:foo}\sublabel{ref:bar}\sublabel{ref:baz} +% etc.\ unless there is only one on the page, in which case the letter +% will be dropped like this: \subpageref{ref:fred}. +% \end{abstract} +% +% \subsection*{Usage} +% +% For use in "noweb", Norman Ramsey requires:\DescribeMacro{\subpageref} +% \begin{quote} +% What's wanted is a latex macro "\subpageref{quux}" that produces +% either a page number (for a page containing only one definition) or +% a page number followed by a, b, c, etc\dots +% \end{quote} +% To be able to use "\subpageref" we must define the label with +% "\sublabel"\DescribeMacro{\sublabel}, used like label. (Using +% "\ref" \DescribeMacro{\ref} with a label defined by "\sublabel" will +% produce the sub-reference number, by the way, and "\pageref" +% \DescribeMacro{\pageref} works as expected.) Note that +% "\subpageref" is robust and "\ref" and "\pageref" are defined to be +% robust also, as they will be in future \LaTeX{} releases. +% Incidentally, these expand to the relevant text plus "\null"---you +% might want to strip this off, e.g.\ for sorting lists. +% +% \StopEventually +% +% \subsection*{Code} +% +% There are various ways we could attack this task (which is made +% non-trivial by the well-known asynchrony of (La)\TeX's output +% routine). There are various ways we might tackle the problem, but +% they all must depend on hacks in the ".aux" file or a similar one. +% Joachim Schrod's "fnpag.sty" does the same sort of thing differently +% to this \LaTeX-specific approach. See "latex.tex" for enlightenment +% on the cross-referencing mechanism and the \LaTeX{} internals used +% below. +% \begin{macro}{\subpageref} +% The "\subpageref" macro first does a normal "\pageref". If the +% reference is actually defined, it then goes on to check whether the +% control sequence "2on"\meta{page referenced} is defined and sets the +% "\ref" value to get "a" etc.\ if so. The magic, of course, is in +% defining the "2on" bit appropriately. +% \begin{macrocode} +\newcommand{\subpageref}[1]{% + \pageref{#1}% + \@ifundefined{r@#1}% + {}% + {\@ifundefined{2on\@pageref{#1}}% + {}% + {\ref{#1}}}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@pageref} +% This is like "\pageref", but expands to "\relax" without a warning +% if the reference is undefined. +% \begin{macrocode} +\def\@pageref#1{\expandafter\expandafter\expandafter + \@cdr\csname r@#1\endcsname\@nil} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\sublabel} +% This is like the usual "\label" command, except that it writes +% "\newsublabel" onto the ".aux" file rather than "\newlabel". +% \begin{macrocode} +\newcommand{\sublabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\newsublabel} +% This is the macro that does the important work. It is called with the +% same sort of arguments as "\newlabel": the first argument is the +% label name and the second is "{"\meta{ref value}"}{"\meta{page +% number}"}". (Note that the only definition here which needs to be +% global is the one which is, and that "\global" is redefined by +% "\enddocument", which will bite you if you use it\dots.) +% +% First we extract the page number into "\this@page". +% \begin{macrocode} +\newcommand{\newsublabel}[2]{% + \edef\this@page{\@cdr#2\@nil}% +% \end{macrocode} +% Then we see whether it's greater than the value of "\last@page" +% which was stashed away by the last "\newsublabel" (or is zero if +% this is the first one). If the page has changed, we reset the +% counter "\sub@page" telling us how many sub-labels there have been +% on the page. +% \begin{macrocode} + \ifnum\this@page>\last@page + \sub@page=0\relax + \fi + \last@page=\this@page + \advance\sub@page by 1 +% \end{macrocode} +% If we've had at least two on the page, we define the "2on"\meta{page +% no.} macro to indicate the fact. +% \begin{macrocode} + \ifnum\sub@page=2 + \global\@namedef{2on\this@page}{}% + \fi +% \end{macrocode} +% Then we write a normal "\newlabel" with the sub-reference as the +% normal reference value in the second argument. +% \begin{macrocode} + \edef\@tempa{\noexpand\newlabel{#1}% + {{\@alph{\number\sub@page}}{\this@page}}}% + \@tempa} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\last@page} +% \begin{macro}{\sub@page} +% We need to define these counters. "\last@page" could be a +% suitably-initialised macro instead. +% \begin{macrocode} +\newcount\last@page +\newcount\sub@page +% \end{macrocode} +% \end{macro} +% \end{macro} +% \begin{macro}{\pageref} +% \begin{macro}{\ref} +% Let's use Rainer's new expandable definitions of "\ref" and +% "\pageref" to minimise the risk of nasty surprises. +% \begin{macrocode} +%% RmS 92/08/14: made \ref and \pageref robust +\def\ref#1{\@ifundefined{r@#1}{{\reset@font\bf ??}\@warning + {Reference `#1' on page \thepage \space + undefined}}{\expandafter\expandafter\expandafter + \@car\csname r@#1\endcsname + \@nil\null}} +\def\pageref#1{\@ifundefined{r@#1}{{\reset@font\bf ??}\@warning + {Reference `#1' on page \thepage \space + undefined}}{\expandafter\expandafter\expandafter + \@cdr\csname r@#1\endcsname + \@nil\null}} +% \end{macrocode} +% \end{macro} +% \end{macro}\sublabel{ref:fred} +% \Finale +% \end{document} +% +\endinput diff --git a/web/noweb/contrib/fischer/README b/web/noweb/contrib/fischer/README new file mode 100644 index 0000000000..12150874a3 --- /dev/null +++ b/web/noweb/contrib/fischer/README @@ -0,0 +1,40 @@ +Date: Wed, 23 Feb 2011 23:02:00 -0500 +From: Greyson Fischer <greyson@foosoft.us> +To: nr@cs.tufts.edu +Subject: noweb + interpreter line = noscript + + +Dear Norman, + +I have been a literate programmer for a few years now. Although I must +limit my use of it most of the time due to corporate pressure, it comes +in extremely handy for particularly new or challenging tasks. Of course +I use noweb for many of my literate programs, preferring it over even +cweb for C and C++ (sure, 'int' isn't bold, but at least it's indented +the way I like to read). + +I found myself repeating a pattern when it came to writing in +interpreted languages; specifically those that use a shebang to specify +their interpreter. I write the literate script, tangle it, and then copy +the result off to be used. The problem with this approach comes when I +want, or need, to edit the script again later. Although I know it came +from noweb (usually because of the complete lack of comments) I couldn't +always track down the original source in a timely manner, leading me to +make changes to the derived script, rather than the source. + +So, I came up with a simple fix. noscript tangles a document on the fly +(assuming the first line has %!) and executes it inline with the +specified interpreter. + +To use it, take a noweb document (for example: myscript.nw) which +tangles <<*>> into, for example, a shell script. Add a "%!/bin/sh" at +the top of the file. Run 'noscript myscript.nw'. Done, the script has +been executed. + +I've attached my version 0.1 in case your interested (along with some +trivial test documents). It's made it quite a bit easier for me to keep +the document and the script together. Perhaps you, or one of your users, +might find it of use. + +Cheers, +Greyson Fischer diff --git a/web/noweb/contrib/fischer/noscript-0.1/noscript b/web/noweb/contrib/fischer/noscript-0.1/noscript new file mode 100755 index 0000000000..7b2a559005 --- /dev/null +++ b/web/noweb/contrib/fischer/noscript-0.1/noscript @@ -0,0 +1,15 @@ +#!/bin/sh + +SCRIPT="$1"; shift +INTERP=`sed -ne ' + s!/bin/sh!/bin/sh -s!; + 1s/^%!//p + ' "$SCRIPT"` + +case "$INTERP" in + '') + echo 'No interpreter line (%!) found!' >&2 + exit 1 + ;; + *) notangle "$SCRIPT" | exec $INTERP - "$@" ;; +esac diff --git a/web/noweb/contrib/fischer/noscript-0.1/test-none.nw b/web/noweb/contrib/fischer/noscript-0.1/test-none.nw new file mode 100644 index 0000000000..eb54b3864a --- /dev/null +++ b/web/noweb/contrib/fischer/noscript-0.1/test-none.nw @@ -0,0 +1,8 @@ +@ This is a test script which displays the ability of my new noweb-script to +actually execute a script which is embedded in a noweb styled file. This test +case ensures that nothing is run when there is no interpreter specified. + +<<*>>= +/bin/false -- nothing here. + +@ And that should do it. diff --git a/web/noweb/contrib/fischer/noscript-0.1/test-py.nw b/web/noweb/contrib/fischer/noscript-0.1/test-py.nw new file mode 100644 index 0000000000..113b5a8e0f --- /dev/null +++ b/web/noweb/contrib/fischer/noscript-0.1/test-py.nw @@ -0,0 +1,23 @@ +%!/usr/bin/env python + +@ This is a test script which displays the ability of my new noweb-script to +actually execute a script which is embedded in a noweb styled file. + +<<*>>= +<<imports>> + +print "Hello Python World!" +print "Args:", sys.argv[1:] +for n in xrange( 0, 10 ): + <<Do something with numbers>> +print + +@ And that should do it, now we just need to do something with the numbers: + +<<Do something with numbers>>= +print n, + +@ Oh yeah, I need to import [[sys]] as well + +<<imports>>= +import sys diff --git a/web/noweb/contrib/fischer/noscript-0.1/test-sh.nw b/web/noweb/contrib/fischer/noscript-0.1/test-sh.nw new file mode 100644 index 0000000000..18f29b2efd --- /dev/null +++ b/web/noweb/contrib/fischer/noscript-0.1/test-sh.nw @@ -0,0 +1,10 @@ +%!/bin/sh + +@ This is a test script which displays the ability of my new noweb-script to +actually execute a script which is embedded in a noweb styled file. + +<<*>>= +echo "Hello Bourne World!" +echo "Args: $*" + +@ And that should do it. diff --git a/web/noweb/contrib/gregory/README b/web/noweb/contrib/gregory/README new file mode 100644 index 0000000000..ae55232fb7 --- /dev/null +++ b/web/noweb/contrib/gregory/README @@ -0,0 +1,2 @@ +dots.nw enables the use of trailing dots in chunk names. It does the same +job as the example program `disambiguate.nw', but it is written in perl. diff --git a/web/noweb/contrib/gregory/dots.nw b/web/noweb/contrib/gregory/dots.nw new file mode 100644 index 0000000000..b7d1f650dc --- /dev/null +++ b/web/noweb/contrib/gregory/dots.nw @@ -0,0 +1,154 @@ +\section*{Resolving trailing dots\ldots} +Gregory Tucker-Kellogg\\ +gtk@walsh.med.harvard.edu +\subsection*{Introduction} + +Unlike \verb|WEB|, \verb|noweb| does not allow the use of trailing +dots in chunk (section) names. \verb|Dots| corrects for this. It is +similar but not identical to \verb|disambiguate|, an \verb|Icon| +program to accomplish the same task. \verb|Dots| is written in +\verb|perl|. + +Before it does much else, \verb|noweb| creates a markup description of +a source file. That markup description is passed along (in both +\verb|noweave| and \verb|notangle|) to other programs in the pipeline +(\verb|totex| for \verb|noweave| and \verb|nt| for \verb|notangle|). +\verb|Dots| intervenes after the markup stage as a filter. The chunk +name references are passed in the form described in Ramsey's paper, +i.e., +\begin{quote} +\leavevmode\rlap{\begin{tabular}{ll} +\tt @defn {\rm\it name}&The code chunk named {\rm\it name} is being defined\\ +\tt @use {\rm\it name}&A reference to code chunk named {\rm\it name}\\ +\end{tabular}} +\end{quote} +If trailing dots are used in a chunk name, they will be passed along +at the markup stage verbatim without any attempt at resolution. +That's where \verb|dots| comes in. + +We require two passes over the noweb code as passed through +\verb|markup|. The first pass picks out all of the unambigious chunk +names and stores them in associative arrays. In between the passes, +we expand the ambigious names and do some simple error checking. The +second pass does a simple replace on incomplete names and writes +output to the next stage of the pipeline. + +The choices for handling the input stream seems to be between sucking +the whole markup into memory at once (as \verb|disambiguate| does) or storing +the markup as a temporary file between the passes. The second is +slower but will not break as the file gets bigger. We'll choose the +first for now. + +\subsection*{Program outline} +<<*>>= +#!/usr/local/bin/perl +while (<>) { # the first pass takes the input from STDIN + <<create lists of identifiers>> +} +<<resolve ambiguities in identifier names>> +<<printout while replacing those with trailing dots>> +@ + +\subsection*{Representation} + +What's the best structure for the list of chunk names? It could just be a +normal array, except we would have to check if a given name is already +defined before adding it too the list. We could make an associative +array, except we really don't have a key to associate. On the other +hand, we could make a single associative array of names with +associations ``complete'' and ``incomplete'' depending on the +presence of dots. This would require no checking on predefinitions, +and a key sorted list brings up each full chunk name as the {\em next} +member of the list for which [[$completion{$identifier}=$complete]]. + +<<create lists of identifiers>>= +if (/^@(defn|use)\s(.*)$/) { # we've found a name of some sort + if (($truncated = $2) =~ s/\.\.\.$//) { # this one ends in dots. + $completion{$truncated} ="incomplete"; + $truncations{$.-1} = $truncated; + $usage_type{$.-1} = $1; + } + else {$completion{$2} ="complete";} + } + push(lines,$_); + +@ + +\subsection*{Chunkname resolution} +The associative array [[%completion]] contains all of the names. The +associative array [[%truncation_table]] contains the line numbers of the +names with trailing dots. We can change the values of [[%completion]] +from ``complete'' and ``incomplete'' to a number representing the +index of the appropriate completion. If there is more than one, we +can print out a warning but still resolve on the closest name. + +<<resolve ambiguities in identifier names>>= +@namelist = sort(keys(%completion)); +$j = $i = 0; +while ($i < $#namelist) { #collect all the ambiguities in a row + while ($completion{$namelist[$j]} eq "incomplete") { + $ambiguity_found = 1; + $j = $i + 1; + } + <<check for remaining ambiguity>> + foreach $name (@namelist[$i..$j]) { + $completion{$name} = $namelist[$j]; + } + $j=$i=$j + 1; + undef($ambiguity_found); +} +@ + + +After we've gotten the expansions of abbreviated chunk names, we still +might run into a problem. First, if no correct expansion was +established, we might just missassign the abbreviation. The expansion +might still be ambiguous if more than one complete expansion can give +the same abbreviation. The first case is a fatal error. The second +can be resolved by seeing if a complete chunkname immediately +following the first completion is a solution. If so, we take the +first completion anyway but print a warning for the user. + +<<check for remaining ambiguity>>= +if (defined $ambiguity_found) { + $suggested = $namelist[$j]; + $nextchance = $namelist[$j+1]; + foreach $name (@namelist[$i..$j-1]) { + if (substr($suggested,0,length($name)) ne $name) { + die "FATAL ERROR: can't resolve @<<$name...>>\n" + } + } + if ($completion{$nextchance} eq "complete") { + foreach $name (@namelist[$i..$j-1]) { + if (substr($nextchance,0,length($name)) eq $name) { + print STDERR "WARNING--Ambiguous chunkname:\n"; + print STDERR "\t<<${name}...@>> could be either\n"; + print STDERR "\t<<$suggested@>> or\n\t<<$nextchance@>>\n"; + print STDERR "I will use <<$suggested@>>\n" + } + } + } +} +@ + + +\subsection*{Printout} +Finally, the [[%truncations]] and [[%usage_type]] arrays are put to +work. We use the line numbers (as [[keys()]]) to pull up the +truncations, and then associate truncations with completed names. +Since we found everything on the first pass we don't have to scan +each line for a [[@defn]] or [[@use]] statement. Note: this part of +the program, analogous to {\em pass2} in \verb|disambiguate|, is +different from \verb|disambiguiate|, which went through a search on +the second pass. If we decided to store the markup in a temporary +file after the first pass to save memory, we would change this section +for blockwise printout. We still would not be forced to scan each +line. + +<<printout while replacing those with trailing dots>>= +foreach $trunc_line (sort(keys(%truncations))) { + $lines[$trunc_line] = + "\@$usage_type{$trunc_line} $completion{$truncations{$trunc_line}}\n"; +} +print @lines; +@ diff --git a/web/noweb/contrib/gregory/email b/web/noweb/contrib/gregory/email new file mode 100644 index 0000000000..04b61f60a9 --- /dev/null +++ b/web/noweb/contrib/gregory/email @@ -0,0 +1 @@ +gtk@walsh.med.harvard.edu (Gregory Tucker-Kellogg) diff --git a/web/noweb/contrib/jobling/Makefile b/web/noweb/contrib/jobling/Makefile new file mode 100644 index 0000000000..5f91558927 --- /dev/null +++ b/web/noweb/contrib/jobling/Makefile @@ -0,0 +1,34 @@ +PROG = correct-refs +DOCSRC = $(PROG).tex +PROGSRC = $(PROG).csh +SCRIPTS = list-anchors.awk awk-scripts.awk + +all: correct-refs.tex correct-refs.csh all-scripts + +correct-refs.tex: correct-refs.nw + noweave -delay -index $< > $@ + +correct-refs.csh: correct-refs.nw + notangle -Rcorrect-refs.csh $< | cpif $@ + chmod +x $@ + +all-scripts: correct-refs.nw + notangle -Rlist-anchors.awk $< | cpif list-anchors.awk + notangle -Rawk-scripts.awk $< | cpif awk-scripts.awk + touch all-scripts + +install: + cp correct-refs.csh $(HOME)/bin + cp *.awk $(HOME)/lib + +tidy: + -rm *~ *% *.bak *.log *.blg + +clean: tidy + -rm *.ps *.dvi *.toc *.aux *.bbl *.dep $(PROG).shar + +realclean: clean + -rm $(DOCSRC) $(PROGSRC) $(SCRIPTS) + +shar: + shar README Makefile $(PROG).nw > $(PROG).shar diff --git a/web/noweb/contrib/jobling/README b/web/noweb/contrib/jobling/README new file mode 100644 index 0000000000..c522c48295 --- /dev/null +++ b/web/noweb/contrib/jobling/README @@ -0,0 +1,21 @@ +Correct-refs: +============ + +A set of awk scripts and to correct the internal anchors and links +produced by noweave -latex+html followed by latex2html. + +This is necessary because the design of the html back-ends for +noweave assume that the resulting HTML file will be the single +document produced by latex2html -split 0 but this is not such +a good idea if the document is large (e.g. the nuweb example). +By default latex2html will split documents into nodes at +section boundaries --- but if the document contains links, +the names have to be changed from "#name" to "noden.html#name". + +The full documentation is contained in correct-refs.nw. Comments +to + +Chris P. Jobling, University of Wales, Swansea +C.P.Jobling@Swansea.ac.uk + + diff --git a/web/noweb/contrib/jobling/correct-refs.bbl b/web/noweb/contrib/jobling/correct-refs.bbl new file mode 100644 index 0000000000..dbeac6d068 --- /dev/null +++ b/web/noweb/contrib/jobling/correct-refs.bbl @@ -0,0 +1,36 @@ +\begin{thebibliography}{1} + +\bibitem{awk-book} +Alfred~V. Aho, Brain~W. Kernighan, and Peter~J. Weinberger. +\newblock {\em The {AWK} Programming Language}. +\newblock Addison Wesley, Reading, MA, USA, 1988. + +\bibitem{gawk-manual} +Diane~Barlow Close, Arnold~D. Robbins, Paul~H. Rubin, and Richard Stallman. +\newblock {\em The {GAWK} Manual}. +\newblock Free Software Foundation, 675 Massachusetts Avenue, Cambridge, MA + 02139, USA, version 0.11 beta edition, October 1989. + +\bibitem{nutshell-unix} +Daniel Gilly. +\newblock {\em UNIX in a Nutsell}. +\newblock O'Reilly \& Associates, Sebastopol, CA, USA, {System V} edition, + 1992. + +\bibitem{ramsey94} +Norman Ramsey. +\newblock Literate programming simplified. +\newblock {\em IEEE Software}, pages 97--105, September 1994. + +\bibitem{perl-llama} +Randal~L. Schwartz. +\newblock {\em Learning perl}. +\newblock O'Reilly \& Associates, Sebastopol, CA, USA, 1993. + + +\bibitem{perl-camel} +Larry Wall and Randal~L. Schwartz. +\newblock {\em Programming perl}. +\newblock O'Reilly \& Associates, Sebastopol, CA, USA, 1991. + +\end{thebibliography} diff --git a/web/noweb/contrib/jobling/correct-refs.nw b/web/noweb/contrib/jobling/correct-refs.nw new file mode 100644 index 0000000000..6ab051e9a1 --- /dev/null +++ b/web/noweb/contrib/jobling/correct-refs.nw @@ -0,0 +1,391 @@ +%========================================================================% +% @noweb-file{ +% author = "C.P. Jobling", +% version = "$Revision: 1.5 $", +% date = "$Date: 1995/04/01 13:54:43 $, +% filename = "correct-refs.nw", +% address = "Control and computer aided engineering +% Department of Electrical and Electronic Engineering +% University of Wales, Swansea +% Singleton Park +% Swansea SA2 8PP +% Wales, UK", +% telephone = "+44-792-295580", +% FAX = "+44-792-295686", +% checksum = "", +% email = "C.P.Jobling@Swansea.ac.uk", +% codetable = "ISO/ASCII", +% keywords = "", +% supported = "yes", +% abstract = "Postprocessing routines to correct link +% errors introduced by \verb|rawhtml| when +% anchors are moved into sub-nodes of the +% main HTML document.", +% docstring = "The checksum field above contains a CRC-16 +% checksum as the first value, followed by the +% equivalent of the standard UNIX wc (word +% count) utility output of lines, words, and +% characters. This is produced by Robert +% Solovay's checksum utility.", +% } +%======================================================================== +\documentclass[a4paper]{article} +\usepackage{noweb,html,multicol} + +\newcommand{\noweb}{\texttt{noweb}} +\newcommand{\command}{\texttt{correct-xref}} + +\title{\command \\ + Correct HTML In-Document Anchors and Hyperlinks} +\author{C.P. Jobling \\ +University of Wales, Swansea \\ +(C.P.Jobling@Swansea.ac.uk)} +\date{$Date: 1995/04/01 13:54:43 $ \\ +Version $Revision: 1.5 $} + +\begin{document} +\maketitle +\begin{abstract} +Postprocessing routines to correct link +errors introduced by \verb|rawhtml| when +anchors are moved into sub-nodes of the +main HTML document. +\end{abstract} + +\tableofcontents +<<Copyright>>= +# This file is part of the correct-refs.nw package which is +# Copyright (C) C.P. Jobling, 1995 +# +# The code may be freely used for any purpose whatever +# provided that it distributed with +# the noweb source correct-refs.nw and that this copyright +# notice is kept intact. +# +# Please report any problems, errors or suggestions to +# Chris Jobling, University of Wales, Swansea +# email: C.P.Jobling@Swansea.ac.uk +# www-home page: http://faith.swan.ac.uk/chris.html/cpj.html +# +# $Id: correct-refs.nw,v 1.5 1995/04/01 13:54:43 eechris Exp $ +@ + +\section{Purpose} +When a \LaTeX $+$ HTML document is processed by \LaTeX 2HTML the +resulting HTML document consists of a set of smaller documents, called +``nodes''. Cross-referencing between nodes, works well if \LaTeX{} +\verb|\label| and \verb|\ref| commands have been used to establish +them. However, if the \verb|rawhtml| environment is used to create +HTML anchors, e.g. +\begin{verbatim} +I want +\begin{rawhtml} +<a name="anchor">this</a> +\end{rawhtml} +to be an anchor. + : + : +And now I want to go back to the anchor: +follow this +\begin{rawhtml} +<a href="#anchor">link</a> +\end{rawhtml} +\end{verbatim} +will only work if the link and the anchor happen to be in the same +document. This can only be guaranteed when the \verb|-split 0| option +is used. + +Why is this a problem? The answer is that I am interested in +``literate programming'' using \noweb{} \cite{ramsey94} which has an +option to create a \LaTeX{} file for its support of fancy mathematics, tables +and figures with code-chunks, code and variable crossreferences +formatted using \verb|rawhtml|. And the cross-references are exactly +as described above. For largeish programs, particularly those that make +heavy use of mathematics, tables and figures in the documentation sections +(exactly the kind that I write!), it is inconvenient to have a single +HTML file so I want to have the document split into pieces. Hence, I +have to postprocess the resulting HTML files changing links so that +the node-name of the anchor is included. + +For the previous example, say +\begin{verbatim} +I want +\begin{rawhtml} +<a name="anchor">this</a> +\end{rawhtml} +to be an anchor. +\end{verbatim} +ends up as: +\begin{verbatim} +I want +<a name="anchor">this</a> +to be an anchor. +\end{verbatim} +in \texttt{node1.html} + +Then any links to this anchor which are of the form +\begin{verbatim} +And now I want to go back to the anchor: +follow this +<a href="#anchor">link</a> +\end{verbatim} +Have to be changed to: +\begin{verbatim} +And now I want to go back to the anchor: +follow this +<a href="node1.html#anchor">link</a> +\end{verbatim} +in every node (except node1.html itself). + +In best UNIX, and \noweb{} tradition, the tools to achieve this are +written in a mixture of \texttt{csh} \cite[Chapter 5]{nutshell-unix}, +\texttt{awk} \cite{awk-book,gawk-manual} and \texttt{awk} +\cite[Chapter 10]{nutshell-unix} although one day I might redo them in +\texttt{perl} \cite{perl-llama,perl-camel} for better portability. + +\section{Usage} +The command to correct the cross-references is +\begin{quote} +\command{} {\it html-dir} +\end{quote} +where {\it html-dir} is the document directory created by \LaTeX 2HTML +from {\it html-dir.tex}. + +\section{The Code} + +\subsection{Finding the anchors} +On examining the HTML created by \LaTeX 2HTML, I noticed that all +anchors created from \LaTeX{} \verb|\ref|, \verb|\tableofcontents|, +\verb|\index| and by the cross-referencing done by \LaTeX 2HTML itself +are of the form +\begin{verbatim} +<a name=sometext>blah, blah</a> +\end{verbatim} +That is the label name is not enclosed in double quotes. So, providing +that the anchors that you create in your \verb|rawhtml| environments +are of the form +\begin{verbatim} +<a name="sometext">Blah, blah</a> +\end{verbatim} +\command{} will be able to distinguish between anchors and links +created by \LaTeX 2HTML and anchors and links created by \noweb{} or +by the author of the original \LaTeX{} document. + +Here is an \texttt{awk} script to extract all the anchors from a +series of documents and to output them in a list in the form +\begin{verbatim} +filename:anchor-name1 +filename:anchor-name2 +\end{verbatim} + +<<list-anchors.awk>>= +# list-anchors.awk --- process a set of .html files and list +# anchors in form filename:anchor-name +# usage: [gn]awk -f list-anchors.awk *.html +# +<<Copyright>> +{ <<Throw away [[<meta name=""]] stuff>> + for (i = 1; i <= NF; i++) + <<Find and print anchors>> +} +@ + +\LaTeX 2HTML adds \verb|<meta name="">| tags to the head of all nodes. +These could confuse the anchor finder, so we have to throw them away. + +<<Throw away [[<meta name=""]] stuff>>= +if ($1 != "<meta") +@ + + +This code just compares each field in the line with the pattern +\verb|^name=".*"$| and when it finds one, strips it to leave just the +anchor name and writes the filename and anchor name on {\it stdout}. +<<Find and print anchors>>= +if ($i ~ /^(name|NAME)\=\".*\".*$/) { + anchor = $i + sub(/^(name|NAME)\=\"/,"",anchor) + sub(/\".*$/,"",anchor) + printf("%s:%s\n",FILENAME,anchor) +} +@ + +To use this program to create the anchor list: +<<Create the list of anchors>>= +cd $DOC +echo Creating list of anchors +gawk -f $LIB/list-anchors.awk *.html | sort -u >! anchor-list +@ + +\subsection{Generating link editing scripts} + +In order to correct the links in the HTML files, we now use the {\it + anchor-list} to control the creation of a set of \texttt{awk} +scripts, one per html file, which will edit the links and replace +\begin{verbatim} +<a href="#anchor">link</a> +\end{verbatim} +by +\begin{verbatim} +<a href="node.html#anchor">link</a> +\end{verbatim} +unless the anchor and link happen to be in the same file. + +The processing is again done with a \texttt{[gn]awk} script. + +<<awk-scripts.awk>>= +# awk-scripts.awk --- process a file conatining node:anchor +# information to create a set of awk +# scripts to correct the links in the nodes. +# usage: [gn]awk -f awk-scripts.awk anchor-list +# +<<Copyright>> +BEGIN {FS = ":"} +{ + <<Collect file names and anchors>> +} +END { + <<produce \texttt{awk} scripts>> +} +@ + +The first part of this script just reads the entry in the {\it + anchor-list} and stores the information in arrays for use later. + +<<Collect file names and anchors>>= +if (! ($1 in files)) { + files[$1] = NR +} +prefix[NR] = $1 +anchor[NR] = $2 +@ + +To produce the \texttt{awk} scripts we loop over each unique file name +encountered: +<<produce \texttt{awk} scripts>>= +for (file in files) { + <<Open new \texttt{awk} script>> + <<Write \texttt{awk} commands for each anchor>> +} +@ + +To create a new \texttt{awk} script: +<<Open new \texttt{awk} script>>= +<<Create file name for \texttt{awk} script>> +<<Open file>> +@ + +The first thing we need to is +to get the root of the HTML file and append awk. + +<<Create file name for \texttt{awk} script>>= +filename = file +sub(/\..*$/,".awk",filename) +@ + +Next we open the file using redirection +<<Open file>>= +printf("# awk script to correct HTML links in %s\n",file) > filename +@ + +To create the \texttt{awk} code, we have to loop over each item in the +list of anchor names. We throw away any that have the same filename as +the currently open file. It will be ok to leave these as +\begin{verbatim} +<a href="#anchor">link</a> +\end{verbatim} +because the anchor is in the same file as the link. The rest get the +file-name prepended onto each use of the anchor name in all links. +When this is run, the output will be: +\begin{verbatim} +{ gsub(/href=\"#anchor-1\"/,"href=\"node1.html#anchor-1\"") } +{ gsub(/href=\"#anchor-2\"/,"href=\"node1.html#anchor-2\"") } + : + : +{ gsub(/href=\"#anchor-n\"/,"href=\"nodem.html#anchor-n\"") } +{ print $0 } +\end{verbatim} +The actual string printing command doesn't look much like this because +of all the escaping that has to be done. + +<<Write \texttt{awk} commands for each anchor>>= +for (i=1; i < NR; i++) { + <<Reject links to anchors in current file>> + printf("{ gsub(/href=\\\"#%s\\\"/,\"href=\\\"%s#%s\\\"\") }\n", + anchor[i],prefix[i],anchor[i]) >> filename +} +printf("{ print $0 }") >> filename +close(filename) +@ + +<<Reject links to anchors in current file>>= +if (prefix[i] != file) +@ + +To create the \texttt{awk} scripts +<<Create \texttt{awk} scripts for HTML files>>= +echo Creating awk scripts +gawk -f $LIB/awk-scripts.awk anchor-list +@ + +<<Use the \texttt{awk} scripts to correct HTML nodes>>= +echo Processing HTML nodes +foreach f (*.awk) + set root=$f:r + set tmpfile=`mktemp --suffix=.html` + echo -n Processing $root.html + gawk -f $f < $root.html >! $tmpfile + echo "..." Done + cp $root.html $root.html.bak + cp $tmpfile $root.html +end + +@ + +\subsection{\texttt{csh} Script to do anchor correction} +<<correct-refs.csh>>= +#!/usr/bin/csh +# correct-refs.csh - CSH script file to correct HTML links and anchors +# usage: correct-refs HTMLDIR +<<Copyright>> +unalias rm +set LIB = $HOME/lib +set DOC = $argv[1] +echo Correcting anchors in HTML dir $DOC +<<Create the list of anchors>> +<<Create \texttt{awk} scripts for HTML files>> +<<Use the \texttt{awk} scripts to correct HTML nodes>> +<<Clean up>> +@ + +<<Clean up>>= +rm *.awk +rm anchor-list +echo Done! +@ + +\bibliographystyle{plain} +\bibliography{refs} + +\section*{Code Chunks} +\nowebchunks + +\section*{Revision History} + +\begin{description} +\item[1.3 to 1.4] The {\tt sed} script didn't work for \noweb{} + generated long anchor and link names. The substitute command string + was too long apparently! I changed the {\tt sed} scripts to {\tt awk + scripts}. Also tidied a few bits of the documentation. +\end{description} +\end{document} + + + + + + + + + + diff --git a/web/noweb/contrib/jobling/email b/web/noweb/contrib/jobling/email new file mode 100644 index 0000000000..4d70f5cc4b --- /dev/null +++ b/web/noweb/contrib/jobling/email @@ -0,0 +1 @@ +C.P.Jobling@Swansea.ac.uk diff --git a/web/noweb/contrib/jonkrom/Makefile b/web/noweb/contrib/jonkrom/Makefile new file mode 100644 index 0000000000..12485906bc --- /dev/null +++ b/web/noweb/contrib/jonkrom/Makefile @@ -0,0 +1,15 @@ +LIB=/dev/null # override for installation +SHELL=/bin/sh +all: noxref.krom + chmod +x noxref.krom + +install: + cp noxref.krom $(LIB) + +source: noxref.krom + +noxref.krom: noxref.nw + notangle -Rnoxref noxref.nw > noxref.krom + +clean: + /bin/rm -f *.tex *.dvi *.ilg *.idx *.aux *.log *.blg *.bbl *~ *.ind noxref.krom diff --git a/web/noweb/contrib/jonkrom/README b/web/noweb/contrib/jonkrom/README new file mode 100644 index 0000000000..8d294db2b9 --- /dev/null +++ b/web/noweb/contrib/jonkrom/README @@ -0,0 +1,4 @@ +An altered version of noxref (written in awk) that claims to support +the same \chunklist semantics as the Icon version, plus boilerplate +for supporting DOS. + diff --git a/web/noweb/contrib/jonkrom/email b/web/noweb/contrib/jonkrom/email new file mode 100644 index 0000000000..1b7699c64c --- /dev/null +++ b/web/noweb/contrib/jonkrom/email @@ -0,0 +1 @@ +jgk@jet.uk diff --git a/web/noweb/contrib/jonkrom/noxref.nw b/web/noweb/contrib/jonkrom/noxref.nw new file mode 100644 index 0000000000..06480117a8 --- /dev/null +++ b/web/noweb/contrib/jonkrom/noxref.nw @@ -0,0 +1,491 @@ +%============================================================================= +% ---------------------------------------------------------------------------- +% Noxref, the cross referencing program for Noweb + +\documentstyle[noweb]{article} +%\RCSdef $Id: noxref.nw,v 1.2 1993/05/06 18:15:40 jgk Exp $ + +% ---------------------------------------------------------------------------- +%\title{{\tt Noxref\thanks{\RCSId},} +\title{{\tt Noxref\thanks{Id: noxref.nw,v 1.2 1993/05/06 18:15:40 jgk Exp},} + the cross referencing program for {\tt Noweb}} +\author{\setcounter{footnote}{6}% + JG Krom\thanks{JET Joint Undertaking, e-mail address: jgk@jet.uk}} + +\date{Printed: \today} + +% ---------------------------------------------------------------------------- +\makeindex +\begin{document} +\maketitle + +% ---------------------------------------------------------------------------- +% A bit of jargon: +\newcommand{\noweb}{{\tt noweb}} +\newcommand{\noweave}{{\tt noweave}} +\newcommand{\notangle}{{\tt notangle}} +\newcommand{\noxref}{{\tt noxref}} \newcommand{\Noxref}{{\tt Noxref}} +\newcommand{\markup}{{\tt markup}} +\newcommand{\awk}{{\rm awk}} +\newcommand{\unix}{{\sc unix}} +\newcommand{\dos}{{\sc msdos}} +\newcommand{\chklist}{{$\setminus$\tt nowebchunks}} +%---------------------------------------------------------------------------- +\section{Introduction} + +N Ramsey presents in [2] a clean, language independent system for +literate programming, called \noweb. One of the components of +\noweave, the ``weave'' program for this system, is the \noxref\ program. + +This system has been been ported to \dos\ by L Wittenberg. + +The author of this paper customised this \noxref\ program. The purpose +of this paper is to describe these customisations. In order to +implement these cutomisations in a ``literate Programming'' style, the +codes written by the above mentioned authors are included in this +document. + +% ---------------------------------------------------------------------------- +\section{Problem Definition} + +The \noxref\ program consists of an \awk\ [1] program, driven by an +\unix\ shell script or, as appropriate, by an \dos\ batch file. This +\noxref\ program adds page number references to the usage and +definitions of the code chunks in a ``woven'' printing of a literate +program. + +A feature that is available in other implementations of the \noxref\ +program, the alphabetical chunk list, was missing from the \awk\ +implementation of this program. As this feature seemed useful, it is +implemented as an addition to the existing \noxref\ \awk\ program. + +This noxref program is the proper place to create this chunk list, +since all information required for this list is already collected by +this program. + +The chunk list should take a form, similar to a table of contents: +chunk names, in the ``\LA{chunk name}\RA{}'' format, on the left hand +side of the page, a list of page numbers on the right hand side of the +page and leaders between the two. The list of page numbers should first +contain the pages on which the chunk is defined and then the pages on +which the chunk is used. Root chunks are to be indicated with the word +``Root''. Chunks that are used, but not defined, are marked with the +word ``Undefined''. + +This whole chunk list, formatted using \LaTeX\ commands, replaces any +line, in the original source, containing only the word ``\chklist''. + +\Noxref\ was, and still is, intended as a stage in the \noweave\ +pipeline. This means that it will receive input in the ``marked-up'' +format generated by the \markup\ program. The output of \noxref\ +should also be in this format. + +% ---------------------------------------------------------------------------- +\pagebreak +\section{Web Structure} +This document describes three different files for two different environments: +\begin{enumerate} +\item [[noxref]] The executable shell script for use under \unix. + This file includes the awk script. +\item [[noxref.bat]] The executable shell script for use under \dos. + This file calls upon the following file. +\item [[noxref.awk]] The awk source code used or included by the + files above. +\end{enumerate} +Each of these can be generated by specifying the required filename as +the root chunk name when executing the \notangle\ program on this web. +To obtain the \dos\ batch file the following command should be executed: +\begin{center} +[[notangle -Rnoxref.bat noxref.nw > noxref.bat]] +\end{center} +Users of these shell scripts might have to adapt the [[awk]] program +name in these scripts to match their local system configuration. +<<noxref>>= +#!/bin/sh +# $Id: noxref.nw,v 1.2 1993/05/06 18:15:40 jgk Exp $ +nawk '<<noxref.awk>>' +<<noxref.bat>>= +@echo off +REM # $Id: noxref.nw,v 1.2 1993/05/06 18:15:40 jgk Exp $ +REM The NOWEB environment variable must be set to the directory +REM where NOXREF.AWK is. It must end in '/' or '\' as required +REM by the AWK interpreter in use. +awk -f %NOWEB%noxref.awk +<<noxref.awk>>= +# $Id: noxref.nw,v 1.2 1993/05/06 18:15:40 jgk Exp $ +<<Noxref awk source>> +<<Noxref awk chunk list additions>> +@ +% ---------------------------------------------------------------------------- +\section{The AWK Source Code} +This is mostly Ramsey's original code. The fragment that has been +changed is included as the chunk: \LA{Find and process the \chklist\ +request}\RA. Module label generation has been upgraded to the +algorithm used in N~Ramsey's last release of the \noweb\ system. +<<Noxref awk source>>= +BEGIN { defns[0] = 0 ; uses[0] = 0 ; dcounts[0] = 0 ; firstdef[0] = 0; + ucounts[0] = 0 ; idtable[0] = 0 ; keycounts[0] = 0 ; + firstdefnout[0] = 0; filetable[0] = 0 } +{ lines[nextline++] = $0 } +/^@defn / { logname("DEFN", defns, dcounts, substr($0, 7)) } +/^@use / { logname("USE", uses, ucounts, substr($0, 6)) } +/^@file / { curfile = modid(substr($0, 7) substr($0, 10, 3)) } +<<Noxref awk source>>= +function logname(which, tbl, counts, name, id) { + counts[name] = counts[name] + 1 + id = which curfile "-" modid(name) "-" counts[name] + tbl[name] = tbl[name] id " " + lines[nextline++] = "@literal \\label{" id "}" + if (which == "DEFN" && firstdef[name] == "") firstdef[name] = id +} +<<Noxref awk source>>= +function modid(name, key) { + if (idtable[name] == "") { + key = name + gsub(/[\[\]\\{} -]/, "*", key) + if (length(key) > 6) key = substr(key,1,3) substr(key, length(key)-2, 3) + keycounts[key] = keycounts[key] + 1 + idtable[name] = key "-" keycounts[key] + } + return idtable[name] +} +<<Noxref awk source>>= +END { + for (i=0; i < nextline; i++) { + name = substr(lines[i], 2) + name = substr(name, 1, index(name, " ")-1) + arg = substr(lines[i], length(name)+3) + if (name == "defn") { + thischunk = arg + printf "@defn %s~{\\footnotesize\\rm\\pageref{%s}}\n", arg, firstdef[arg] + } else if (name == "use") { + if (firstdef[arg] != "") + printf "@use %s~{\\footnotesize\\rm\\pageref{%s}}\n", arg, firstdef[arg] + else + printf "@use %s~{\\footnotesize\\rm (never defined)}\n", arg + } else if (name == "end") { + if (substr(arg, 1, 4) == "code" && firstdefnout[thischunk] == 0) { + firstdefnout[thischunk] = 1 + n = split(defns[thischunk], a) + if (n > 1) { + printf "@literal \\nwalsodefined{" + for (j = 2; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n@nl\n" + } + if (uses[thischunk] != "") { + printf "@literal \\nwused{" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n@nl\n" + } else + printf "@literal \\nwnotused\n@nl\n" + } + print lines[i] + } + <<Find and process the \chklist\ request>> + else + print lines[i] + delete lines[i] + } +} +@ Finding the \chklist\ command is straight forward, it must be on a +\verb+@text+ line. The unclean way of using a chunk to insert an +[[else]]~[[if]] clause in a larger [[if -- else if -- else]] +structure should be noted. +<<Find and process the \chklist\ request>>= +else if (name == "text") { + if (arg == "\\nowebchunks") + printChunkList() + else + print lines[i] +} +@ If the keyword has been found the function [[printChunkList()]] is +called to do the actual printing. +% ---------------------------------------------------------------------------- +\section{The Chunk List Additions} +These additions consist, except for the chunk \LA{Find and process the +\chklist\ request}\RA\ mentioned above, of two functions: one to sort +the list of chunks, and one to print this list. + +<<Noxref awk chunk list additions>>= +<<Sort chunk list>> +<<Print chunk list>> +@ +% ---------------------------------------------------------------------------- +\subsection{The Sorting Routine} +This function implements essentially a simple insertion sort. If +performance becomes a problem, some effort could be invested to use a +better algorithm, but that seems unnecessary at the moment. + +\subsubsection{The Sorting Function Framework} +Two global variables, [[nextFreeIdx]] and [[sortedNames]], carry the +results of this function. + +The [[sortedNames]] array is empty to start with, except for the +first element, which contains the null string as a sentinel; no +string compares to less than the null string. The invariant on this +array is that it will always contain chunk names in sorted order, +with the lowest (according to the awk comparison rules) coming first. + +The invariant on [[nextFreeIdx]] is that it always contains the index +number of the next free element in the array. +<<Sort chunk list>>= +function sortChunkNames( <<Sort --- Local Variables>>) +{ + sortedNames[0] = "" + nextFreeIdx=1; # The next index to use (range 1--N) + <<Run through the [[chunkname]]s as stored in [[defns]] array>> + <<Run through the [[chunkname]]s as stored in [[uses]] array>> +} +@ +\subsubsection{Scan the Arrays} +All chunk names have been stored in the [[defns]] array when they were +defined. Using the ``{\tt for \em xyz \tt in \em arrayname}'' feature of +awk, it is possible to step through all elements of the array. The +zero element in the arrays would confuse the sorting algorithm, so these +elements have to be discarded. +<<Run through the [[chunkname]]s as stored in [[defns]] array>>= +for (chunkname in defns) { + if (chunkname != 0) { + <<Insert in proper place in sorted array>> + } +} +<<Sort --- Local Variables>>= +chunkname, +@ All names that have been used are stored in the array [[uses]]. This +array has to be scanned for chunk names that might have been used, but +that were not defined. Such chunks should also be included in the chunk +list, so they are inserted in the [[sortedNames]] array. +<<Run through the [[chunkname]]s as stored in [[uses]] array>>= +for (chunkname in uses) { + if ((chunkname != 0) && !(chunkname in defns)) { + <<Insert in proper place in sorted array>> + } +} +@ +\subsubsection{Insert into the Sorted Array} +The proper place for the insertion is found by scanning the sorted +array from the end to the beginning. The local variable [[idx]] is +used for this scan, it will always point at a possible insertion +location. [[nextFreeIdx]] is incremented, since it is now known that +there is another element which will be inserted. + +If the element before the current scan location is greater than the +chunkname to be inserted, then the chunkname will be inserted before +that element. The scanned element should be moved one position up (to +the current insertion location) at this point. + +Otherwise, the chunk name should come after the element before the +scan location, ie. it should be inserted at the current position. +[[idx]] is pushed to the end condition, to stop the scan over the +sorted array and to get a new chunk name. +<<Insert in proper place in sorted array>>= +for ( idx = nextFreeIdx++ ; idx>0 ; idx-- ) { + if ( sortedNames[idx-1] > chunkname ) + sortedNames[idx] = sortedNames[idx-1] ; + else { + sortedNames[idx] = chunkname ; + idx = -1 ; + } +} +<<Sort --- Local Variables>>= +idx +@ +% ---------------------------------------------------------------------------- +\subsection{Print the Chunk List} +The function to print the chunk list, first calls upon the sorting +function to get the names in order. It then inserts, if required, +some heading material and lastly prints the names. +<<Print chunk list>>= +function printChunkList( <<Print --- Local Variables>> ) +{ + sortChunkNames() ; + <<Optional Header material>> + <<Header material>> + <<Print Loop>> + <<Closing Material>> +} +@ +% ---------------------------------------------------------------------------- +\subsubsection{The Printing Loop} +This loops steps through the indices of the sorted names array, up to +the next free index number. It prints each name, using the \markup\ +\verb+@use+ directive, followed by a row of dots. The printing of the +page numbers, root markers etc. is delegated to the chunk \LA{print page +numbers etc.}\RA. +<<Print Loop>>= +for (idx=1; idx<nextFreeIdx; idx++) { + print "@use " sortedNames[idx] ; + print "@literal \\dotfill" ; + <<Print page numbers etc.>> + print "@nl" ; +} +<<Print --- Local Variables>>= +idx, +@ When printing the page references, the following cases should be considered. +\begin{itemize} +\item If a name does not appear in the [[uses]] array, it must have been + in the [[defns]] arrray. It is therefore a root chunk. +\item If a name does not appear in the [[defns]] array, it is undefined in + the source file currently processed. +\item If a name is defined, print the page references of the definitions. +\item If a name is used, print the usage page references. +\end{itemize} +<<Print page numbers etc.>>= +if (uses[sortedNames[idx]] == "") { + print "@literal { \\rm Root}," ; +} +if (defns[sortedNames[idx]] == "") { + print "@literal { \\rm Undefined}" ; +} +else { + <<Print definition page numbers>> +} +if (uses[sortedNames[idx]] != "") { + <<Print usage page numbers>> +} +@ +\paragraph{Definition References.} +Definition page references are derived from the [[defns]] arrays, by +splitting them into fields with the [[split]] function. The first one +should not be preceded by a ``,'' (comma character), but all subsequent +numbers (if any) should have a comma in front of them. Page references +for the definitions are printed underlined. +<<Print definition page numbers>>= +n = split(defns[sortedNames[idx]], a) +print "@literal { \\underline{\\pageref{" a[1] "}}}"; +if (2 <= n) + for (j = 2; j <= n; j++) + print "@literal {, \\underline{\\pageref{" a[j] "}}}"; +<<Print --- Local Variables>>= +n, a, j +@ +\paragraph{Usage References.} +The page references for the places where the chunks are used are +obtained from the [[uses]] array. These always have +preceding text (definition page references or the word ``Undefined''), +so these should always have a ``,'' in front of them. +<<Print usage page numbers>>= +n = split(uses[sortedNames[idx]], a) +for (j = 1; j <= n; j++) + print "@literal {\\rm, \\pageref{" a[j] "}}"; +@ +\paragraph{A Small Test.} +Both chunk names should appear in the chunk list: one +marked as ``Root'', the other as ``Undefined''. +<<An unused (therefore root) chunk>>= +<<An undefined chunk>> +@ +\pagebreak +\subsubsection{List Opening and Closing Definitions} +If required, some commands could be included to generate a chapter or +section heading above the chunk list. However, the author of this +code prefers to have such sectioning commands under the control of the +final document source file. + +Users who prefer to have these section commands automatically +generated (like the Icon implementation of the \noxref\ program does) +can redefine \LA{Optional Header material}\RA\ to be equal to the +current definition of \LA{Not used header material}\RA. +<<Optional Header material>>= +<<Not used header material>>= +print "@literal \\ifx\\chapter\\undefined\\section*" +print "@literal {Alphabetical List of Chunk Names}" ; +print "@literal \\else\\chapter" +print "@literal {Alphabetical List of Chunk Names}" ; +print "@literal \\fi" +print "@nl" ; +@ The following header material is required, it sets up the +environment for the list. +<<Header material>>= +print "@literal {\\obeylines" ; +print "@literal \\setlength{\\parindent}{0mm}" ; +print "@literal \\setlength{\\parskip}{1.4ex}" ; +print "@nl" ; +<<Closing Material>>= +print "@literal }" ; +@ +% ---------------------------------------------------------------------------- +\newpage +\section{References} +% This is faked (ie, not a real LaTeX bibliography), since this file +% is likely to get included in other files, with other bibliographies. +{ +\begin{description} +\sfcode`\.=1000\relax + +\item[{\rm [1]~~~}] +Aho AV., Kernighan BW., Weinberger PJ. 1988, +{\sl The AWK Programming Language,} +Addison-Wesley. + +\item[{\rm [2]~~~}] +Ramsey N. 1992--1993, +``Literate Programming Tools Need Not Be Complex,'' +To be published in {\sl IEEE Software.} 1993. + +\end{description} +} + + +% ---------------------------------------------------------------------------- +% This should go in RCS.sty! +\newenvironment{RCSlog}% +{\begin{trivlist} \item[]% +\setlength{\parindent}{0mm}% +\setlength{\parskip}{3ex}% +\catcode`\$=12% +\hbadness=10000\ignorespaces\obeycr}% +{\end{trivlist}} + +\section{RCS Maintained Log} +\begin{RCSlog} +$Log: noxref.nw,v $ +Revision 1.2 1993/05/06 18:15:40 jgk +Moved from using bold to underlining for the page references +of definitions. (On advice of Lee Wittenberg.) +A few linguistic improvements. RCS ID strings included in progs. + +Revision 1.1 1993/05/01 21:08:21 JG~Krom +Initial revision + +A version with the same code, but some errors and typos +in the documentation text was known as: +Revision 1.1 1993/04/28 17:03:23 jgk +Initial revision + +This file was derived from: +``NOXREF.BAT'' by L~Wittenberg and ``noxref'' By N~Ramsey. +(No change log was available for these files.) + +And from: +Log: noxref.awk +Revision 1.5 1993/04/23 12:52:16 JG~Krom +On advice of Lee Wittenberg, used the new way of label generation. + +Revision 1.4 1993/04/20 22:41:44 JG~Krom +Improved layout of chunklist. + +Revision 1.3 1993/04/11 17:47:38 JG~Krom +Indicate root chunks in the chunklist. + +Revision 1.2 1993/04/11 15:52:53 JG~Krom +First stab at the chunklist command. + +Revision 1.1 1992/10/21 17:00:00 LEEW +checked in with -k by JG~Krom at 1993/04/10 16:53:28 + +Which in turn was also derived from: ``noxref'' By N~Ramsey. +\end{RCSlog} +% ---------------------------------------------------------------------------- +\newpage +\section{Alphabetical List of Chunk Names} +\nowebchunks +% ---------------------------------------------------------------------------- +\input{noxref.ind} +% ---------------------------------------------------------------------------- +\end{document} +% End of noweb code +%============================================================================= diff --git a/web/noweb/contrib/kaelin/README b/web/noweb/contrib/kaelin/README new file mode 100644 index 0000000000..f8be279ff9 --- /dev/null +++ b/web/noweb/contrib/kaelin/README @@ -0,0 +1,19 @@ +Prettyprinters for Icon and C++ based on Kostas's work, but using Computer Modern fonts. +Prettyprinters for Icon and C++ based on Kostas's work, but using Computer Mode +rn fonts. + +Theres is no Makefile +Type +> noweave -x -delay pp.nw > pp.tex < /* creates pp.tex */ +> latex pp.tex < /* creates pp.dvi and warnings */ +> latex pp.tex < /* creates final pp.dvi */ +to get documentation and +> noweb -t pp.nw < /* creates cnw.icn and inw.icn */ +> iconc cnw.icn < /* creates cnw */ +> iconc inw.icn < /* creates inw */ +to get filters named cnw and inw. +(Maybe you have to use full path-names for noweave, noweb, latex and iconc) + +For installation: Look at the other Makefiles in contrib/* + +Cleaning up is trivial: Remove pp.aux, pp.log, pp.tex, inw.icn and cnw.icn diff --git a/web/noweb/contrib/kaelin/email b/web/noweb/contrib/kaelin/email new file mode 100644 index 0000000000..8790d4fd1f --- /dev/null +++ b/web/noweb/contrib/kaelin/email @@ -0,0 +1 @@ +Kaelin Colclasure <kaelin@bridge.com> diff --git a/web/noweb/contrib/kaelin/pp.nw b/web/noweb/contrib/kaelin/pp.nw new file mode 100644 index 0000000000..dd06757340 --- /dev/null +++ b/web/noweb/contrib/kaelin/pp.nw @@ -0,0 +1,616 @@ +% For LaTeX input: noweave -x -delay filename.nw > filename.tex +% For source code: noweb -t filename.nw +\documentstyle[noweb,11pt]{article} +\pagestyle{noweb} +\noweboptions{longchunks,smallcode} + +\newcommand{\CEE}{{\sc C\spacefactor1000}} +\newcommand{\CPP}{{\sc C\PP\spacefactor1000}} +\newcommand{\ICON}{{\sc Icon\spacefactor1000}} +\newcommand{\PP}{\kern.5pt\raisebox{.4ex} + {$\scriptscriptstyle+\kern-1pt+$}\kern.5pt} + +\begin{document} +\title{Beyond the {\tt \symbol{"5C}tt} Font\\{\large A {\tt noweb} Extension}} +\author{Kaelin L. Colclasure \\ {\tt kaelin@bridge.com}} +\date{Preliminary Draft} +\maketitle + +\section{Introduction} +This document contains two filters for {\tt noweave}, written in \ICON, +which add basic pretty-printing for \CPP\ and \ICON\ code to {\tt noweb}'s +repetoir of functionality. The bulk of the code herein is derived from +\cite{kostas}, and at least a nodding familiarity with that work is assumed +by this documentation. A working knowledge of \ICON\ is, of course, a must. +Refer to \cite{griswold} as necessary. + +\subsection{Design philosophy} +While I relish the idea of inflicting my own code formatting +preferences on the unsuspecting masses, I was less enthusiastic +towards the prospect of writing a scanner for each target language. +Besides (I adeptly rationalized), so doing would violate one of the +fundamental tenets of {\tt noweb}-- that the programmer maintain +control over the formatting of code. + +Thus, this implementaion does not address indentation or line-breaking +within code chunks. Like its predecessor, it is limited bolding target +language keywords, operator substitution and other such in-line markup +generation. However, even these restricted transformations can have a +marked effect on the clarity of exposition of the source code.\footnote{This +is particularly true for languages like Icon which have a plethora of +multi-character operators.} + +\subsection{Why a new implementation?} +While \cite{kostas} provides an easily-extensible implementation, it also +has one unfortunate limitation-- the technique used to distinguish between +operators and comment delimiters relies upon those categories consisting of +disjoint character sets\footnote{More precisely, it relies upon the {\em +start sets} of those two categories of tokens being disjoint.} in the target +language. While true for the target languages Kostas implemented, this +assertion does not hold for a large class of commonly used languages. The +\CEE\ lagnuage, for instance, uses \verb|/*| \ldots \verb|*/| to bracket +comments when both \verb|/| and \verb|*| are operators as well. + +While obviously a problem for me, since my first target language was \CPP, +this could undoubtably have been redressed with relatively minor surgery to +\cite{kostas}. However, once I'd familiarized myself with Kostas' code, I +felt compelled to add some additional tweaks and features of my own. The +principle enhancements provided are as follows: +\begin{itemize} +\item A more versatile scheme for handling token recognition. This not only +addresses the problem outlined above, but makes it possible to do target +language dependent processing during scanning as well as during +initialization. +\item More robust handling of string constants. Kostas did not deal with +escaped embedded quoting characters at all. +\item Pretty-printing of [[[[quoted]]]] code as well as code in {\tt noweb} +chunks. +\item Use of \ICON s conditional compilation facilities to allow all the code +for different targets to reside in one file.\footnote{Well, {\em I} like it +better this way\ldots} +\end{itemize} +Along the way, there have been some steps backwards as well: +\begin{itemize} +\item I make no provisions for multi-line comments at all. I +personally use them only for commenting out code regions, and in that +context special treatment is undesirable.\footnote{This does not +preclude other target language implementations based upon this work +from using target language dependent code to deal with multi-line +comments.} +\item Font utilization has been restricted to the Computer Modern fonts +provided with all implementations of \TeX\ (primarily because my +site has only those fonts). +\item Due to my ignorance of ``vanilla'' \TeX, the markup code generated +is \LaTeX\ dependent (I was tempted to add this as an item to the list +above, but decided to refrain from provoking any nasty EMail). +\end{itemize} +No doubt I have introduced some new bugs as well, but discovering those is +left as an excercise for the reader. + +\section{Implementation} +Like Kostas, I have split the implementation of each target filter into a +common part and a target dependent part. However, in contrast with +\cite{kostas}, I have kept all the pieces together in one {\tt noweb} input +file. Readers interested only in adding a new target should read \S +\ref{reqs} and then skim \S \ref{c} and \S \ref{icon} for complete examples. + +\subsection{Target requirements\label{reqs}} +\paragraph{Minimal implementation:} +Each target implementation must provide at least the following:\footnote{In +point of fact, the list of keywords could be left null. It merely provides +a convenient way to populate the [[translation]] table with the +language's reserved words, which will likely all receive the same markup +treatment. However, here again, Icon is an exception\ldots} +\begin{itemize} +\item A root chunk which generates the \ICON\ source file for the target +language filter. +\item A table containing all of the target languages ``interesting'' +character sequences and either their typographic translation {\em or an \ICON\ +procedure for deriving it}. +\item A list of keywords. +\item A list of operators. For our purposes here, comment-delimiting +punctuation marks are considered operators. +\end{itemize} +Variables to hold the required table and lists exist at global scope. +<<Global declarations>>= +global translation # Table of typographic translations +global keyword_list, operator_list # List of keywords, operators +@ + +\paragraph{The [[translation]] table:} The table of typographic substitutions +is built by the following code chunk during program initialization: +<<Define the [[translation]] table>>= +translation := table(); +<<Keywords>> +<<Operators>> +@ + +\paragraph{Initial character sets:} +The {\em start sets} for the two classes of tokens are defined globally in +the following two chunks. Note that [[op_chars]] is automatically derived +from the list of operators already defined above. However, some target +languages will require adjustments to the definition of +[[id_chars]].\footnote{Note that the Icon implementation does {\em not} +add $\&$ to this list even though it prefixes some identifiers. We treat +it always as an operator instead.} +<<Global declarations>>= +global id_chars, op_chars +@ +<<Define initial character sets>>= +id_chars := &letters ++ &digits ++ '_' +op_chars := '' +every op := !operator_list do op_chars ++:= cset(op[1]) +@ + +\paragraph{The [[begin_token]] character set:} +The next two chunks globally define the {\em start set} for {\bf all} +interesting tokens. In general this will be \mbox{[[id_chars]] $\cup$ +[[op_chars]]}, but in some cases it may be desirable to add additional +elements to this set. +<<Global declarations>>= +global begin_token +@ +<<Define the [[begin_token]] character set>>= +begin_token := id_chars ++ op_chars +@ + +\paragraph{Procedures:} +\ICON\ procedures referenced in the [[translation]] table must, of +course, be defined by the target implementation. When invoked, such a +procedure will receive one argument-- the token which caused it to be +invoked. + +Additionally, the [[TeXify]] procedure's [[case]] statement includes a +target language dependent chunk which may be used to implement anything I've +forgotten or neglected (like multi-line comments). The \CEE/\CPP +implementation provides a skeletal example of how this might be used to +process \CEE preprocessor directives.\footnote{It takes the trouble to find +them, but then merely writes them verbatim to the output with no special +formatting. This could have been more easily accomplished with a [[procedure]] +entry in the [[translation]] table.} + +\subsection{Target independent code} + +\subsubsection{The [[main]] procedure} + +<<Procedure [[main]]>>= +procedure main() + <<Define the [[translation]] table>> + <<Define initial character sets>> + <<Define the [[begin_token]] character set>> + + <<Emit special \LaTeX\ definitions>> + <<Process each input line through the [[filter]] procedure>> + return +end +@ +<<Emit special \LaTeX\ definitions>>= +write("@literal \\def\\begcom{\\begingroup\\rm" || + "\\catcode`\\$=3\\catcode`\\^=7\\catcode`\\_=8{}}") +write("@literal \\def\\endcom{\\endgroup}") +@ +<<Process each input line through the [[filter]] procedure>>= +while line := read() do + line ? (="@" & filter(tab(upto(' ') | 0), if =" " then tab(0) else &null)) +@ + +\subsubsection{The [[filter]] procedure} + +<<Procedure [[filter]]>>= +procedure filter(name, arg) +static kind +static whitespace +static code_in_line + initial { + whitespace := ' \t' + } + case name of { + "begin": { + arg ? kind := tab(many(&letters)) + copyline(name, arg) + } + "defn" | "literal" | "use": { + code_in_line := 1 + copyline(name, arg) + } + "endquote": { + kind := "docs" + copyline(name, arg) + } + "nl": { + if \kind == "code" & /code_in_line then + write("@literal \\smallskip\\eatline") + copyline(name, arg) + code_in_line := &null + } + "quote": { + kind := "code" + copyline(name, arg) + } + "text": { + if \kind == "code" then { + if *(cset(arg) -- whitespace) > 0 then code_in_line := 1 + TeXify(arg) + } + else copyline(name, arg) + } + default: copyline(name, arg) + } + return +end +@ +<<Procedure [[copyline]]>>= +procedure copyline(name, arg) + return write("@", name, (" " || \arg) | "") +end +@ + +\subsubsection{The [[TeXify]] procedure} + +<<Procedure [[TeXify]]>>= +procedure TeXify(arg) + writes("@literal ") + arg ? { + while writes(preTeX(tab(upto(begin_token)))) do case &pos + 1 of { + <<Language-dependent \TeX ify chunk>> + any(id_chars): <<Identifier or numeric constant>> + any(op_chars): <<Operator>> + default: stop("\n** Error at input pos ", &pos) + } + writes(preTeX(tab(0))) + } + write() + return rval +end +@ +<<Identifier or numeric constant>>= +{ + token := tab(many(id_chars)) + <<Write [[token]] or its typographic translation>> +} +@ +<<Operator>>= +{ + token := tab(match(!operator_list) | &pos + 1) + <<Write [[token]] or its typographic translation>> +} +@ +<<Write [[token]] or its typographic translation>>= +trans := translation[token] +case type(trans) of { + "procedure": writes(trans(token)) + "null": writes(preTeX(token)) + default: writes("\\mbox{" || trans || "}") +} +@ +<<Procedure [[preTeX]]>>= +procedure preTeX(arg) +static TeX, hex + initial { + TeX := '\\${}&#^_%' + hex := table(); + hex["\\"] := "5C"; hex["$"] := "24"; hex["{"] := "7B"; hex["}"] := "7D" + hex["&"] := "26"; hex["#"] := "23"; hex["^"] := "5E"; hex["_"] := "5F" + hex["%"] := "25" + } + str := "" + every c := !arg do + str ||:= if *(cset(c) ** TeX) > 0 then "\\symbol{\"" || hex[c] || "}" + else c + return str +end +@ + +\subsubsection{Target utility procedures} + +<<Procedure [[comment_eol]]>>= +procedure comment_eol(arg) + return "\\begcom" || arg || tab(0) || "\\endcom" +end +@ + +\paragraph{The [[quoted_c_string]] procedure:} +This procedure matches a \CEE/\CPP-style +string constant which may contain embedded quotes escaped by a backslash +(\verb|\|) character. We want string constants to be typeset with +\verb|\verb*| (ala {\tt CWEB}). The result looks like +\verb*|"a string constant"| which makes counting multiple embedded spaces +{\em much} easier. +<<Procedure [[quoted_c_string]]>>= +procedure quoted_c_string(arg) +local c, str + c := cset(arg) + str := tab(upto(c)) + if \str then while str[-1] == "\\" & (*str < 2 | str[-2] ~== "\\") do + str ||:= tab(&pos + 1) || tab(upto(c)) + else str := "" + str ||:= tab(&pos + 1) # Pick up closing quote + return "\\verb*\^K" || arg || str || "\^K" +end +@ Note the use of ASCII {\tt VT} control characters to bracket the +\verb|\verb*| environment. Any target implementation which utilizes this +procedure must include [[write("@literal \\catcode`^^K=3")]] in the \LaTeX\ +special definitions chunk. This is a gross hack, but it's made necessary by +the fact that a string literal could conceiveably contain {bf all} of the +printable ASCII characters. We therefore arbitrarily choose a control +character which we deem unlikely to be found in a string +literal.\footnote{Incidently, my original choice was NUL, but this proved +problematic for {\tt vi} users because that editor stripped the NULs away +when the {\tt .tex} file was edited by hand.} + +\subsection{\protect\CEE/\protect\CPP\ code markup\label{c}} + +<<cnw.icn>>= +$define LANG_CPLUSPLUS +<<Global declarations>> +<<Procedure [[main]]>> +<<Procedure [[filter]]>> +<<Procedure [[copyline]]>> +<<Procedure [[preTeX]]>> +<<Procedure [[TeXify]]>> + +<<Procedure [[comment_eol]]>> +<<Procedure [[quoted_c_string]]>> +@ +<<Keywords>>= +$ifdef LANG_CPLUSPLUS +keyword_list := [ + "asm","auto","break","case","catch","char","class","const", + "continue","default","delete","do","double","else","enum","extern", + "float","for","friend","goto","if","inline","int","long", + "new","operator","private","protected","public","register","return","short", + "signed","sizeof","static","struct","switch","template","this","throw", + "try","typedef","union","unsigned","virtual","void","volatile","while" +] +every key := !keyword_list do translation[key] := "{\\bf{}" || key || "}" +$endif +@ +<<Operators>>= +$ifdef LANG_CPLUSPLUS +operator_list := [ + "<\<=",">>=","->","++","--","<\<",">>","<=",">=","==","!=","&&","||", + "*=","/=","%=","+=","-=","&=","^=","|=","()","[]","//","/*","*/", + "!","%","^","&","*","(",")","-","+","=","{","}","|","~","[","]","<",">", + "?","/","'","\"" +] +translation["<\<="] := "\\protect\\OPASSIGN{\\ll}" +translation[">>="] := "\\protect\\OPASSIGN{\\gg}" +translation["->"] := "\^K\\rightharpoonup\^K" +translation["++"] := "\\protect\\PP" +translation["--"] := "\\protect\\MM" +translation["<\<"] := "\^K\\ll\^K" +translation[">>"] := "\^K\\gg\^K" +translation["<="] := "\^K\\leq\^K" +translation[">="] := "\^K\\geq\^K" +translation["=="] := "\^K\\equiv\^K" +translation["!="] := "\^K\\neq\^K" +translation["&&"] := "\^K\\wedge\^K" +translation["||"] := "\^K\\vee\^K" +translation["*="] := "\\protect\\OPASSIGN{\\ast}" +translation["/="] := "\\protect\\OPASSIGN{\\div}" +translation["%="] := "\\protect\\OPASSIGN{" || preTeX("%") || "}" +translation["+="] := "\\protect\\OPASSIGN{+}" +translation["-="] := "\\protect\\OPASSIGN{-}" +translation["&="] := "\\protect\\OPASSIGN{" || preTeX("&") || "}" +translation["^="] := "\\protect\\OPASSIGN{\\oplus}" +translation["|="] := "\\protect\\OPASSIGN{\\mid}" +translation["()"] := "\^K(\\;)\^K" +translation["[]"] := "\^K[\\;]\^K" +translation["//"] := comment_eol + +translation["!"] := "\^K\\neg\^K" +translation["%"] := "\^K" || preTeX("%") || "\^K" +translation["^"] := "\^K\\oplus\^K" +translation["&"] := "\^K" || preTeX("&") || "\^K" +translation["*"] := "\^K\\ast\^K" +translation["="] := "\^K\\leftarrow\^K" +translation["{"] := "\\boldmath\^K\\{\^K" +translation["}"] := "\\boldmath\^K\\}\^K" +translation["|"] := "\^K\\mid\^K" +translation["~"] := "\^K\\sim\^K" +translation["/"] := "\^K\\div\^K" +translation["'"] := quoted_c_string +translation["\""] := quoted_c_string + +every op := !operator_list do /translation[op] := "\^K" || op || "\^K" +$endif +@ +<<Language-dependent \TeX ify chunk>>= +$ifdef LANG_CPLUSPLUS +any(cpp_mark): writes(preTeX(tab(0))) +$endif +@ +<<Global declarations>>= +$ifdef LANG_CPLUSPLUS +global cpp_mark +$endif +@ +<<Define initial character sets>>= +$ifdef LANG_CPLUSPLUS +cpp_mark := '#' +$endif +@ +<<Define the [[begin_token]] character set>>= +$ifdef LANG_CPLUSPLUS +begin_token ++:= cpp_mark +$endif +@ +<<Emit special \LaTeX\ definitions>>= +$ifdef LANG_CPLUSPLUS +write("@literal \\catcode`^^K=3") +write("@literal \\newcommand{\\MM}{\\kern.5pt\\raisebox{.4ex}" || + "{\^K\\scriptscriptstyle-\\kern-1pt-\^K}\\kern.5pt}") +write("@literal \\newcommand{\\PP}{\\kern.5pt\\raisebox{.4ex}" || + "{\^K\\scriptscriptstyle+\\kern-1pt+\^K}\\kern.5pt}") +write("@literal \\newcommand{\\OPASSIGN}[1]{\\raisebox{-.4ex}" || + "{\^K\\stackrel{\\scriptscriptstyle\\,#1}{\\leftarrow}\^K}}") +$endif +@ + +\subsection{\protect\ICON\ code markup\label{icon}} + +<<inw.icn>>= +$define LANG_ICON +<<Global declarations>> +<<Procedure [[main]]>> +<<Procedure [[filter]]>> +<<Procedure [[copyline]]>> +<<Procedure [[preTeX]]>> +<<Procedure [[TeXify]]>> + +<<Procedure [[comment_eol]]>> +<<Procedure [[quoted_c_string]]>> +<<\ICON\ [[prefixed_keyword_check]] procedure>> +@ +<<Global declarations>>= +$ifdef LANG_ICON +global an_word_list, ss_word_list +$endif +@ +<<Keywords>>= +$ifdef LANG_ICON +keyword_list := [ + "by","break","case","create","default","do","else","end", + "every","fail","global","if","initial","link","local","next", + "not","of","procedure","record","repeat","return","static","suspend", + "to","then","while","until" +] +an_word_list := [ + "ascii","clock","collections","cset","current","date","dateline","digits", + "error","errornumber","errortext","errorvalue","errout","fail","features", + "file","host","input","lcase","letters","level","line","main","null", + "output","pos","random","regions","source","storage","subject","time", + "trace","ucase","version", +# Added in Version 8.10 + "allocated","e","phi","pi","progname", +# Added by X interface + "col","control","interval","ldrag","lpress","lrelease","mdrag","meta", + "mpress","mrelease","resize","rdrag","row","rpress","rrelease","shift", + "window","x","y" +] +ss_word_list := [ +# Translator directives for Version 8.10 + "define","else","endif","ifdef","ifndef","include","line","undef", +] +every key := !keyword_list do translation[key] := "{\\bf{}" || key || "}" +$endif +@ + +<<Operators>>= +$ifdef LANG_ICON +operator_list := [ + "#","~===:=","<\<=:=",">>=:=","~==:=","|||:=","===:=","~===","<=:=", + ">=:=","~=:=","++:=","--:=","**:=","||:=","<\<:=","==:=",">>:=", + "<\<=",">>=","~==","|||",":=:","<->","===","+:=","-:=","*:=","/:=","%:=", + "^:=","<:=","=:=",">:=","@:=","&:=","?:=","()","[]", + "<=",">=","~=","++","--","**","||","<\<","==",">>",":=","<-","{","}","|", + "+","-","?","~","=","!","@","^","*",".","/","\\","%","<","&","$","'","\"" +] + +translation["#"] := comment_eol +translation["~===:="] := "\\protect\\OPASSIGN{\\not\\equiv}" +translation["<\<=:="] := "\\protect\\OPASSIGN{\\preceq}" +translation[">>=:="] := "\\protect\\OPASSIGN{\\succeq}" +translation["~==:="] := "\\protect\\OPASSIGN{\\not\\approx}" +translation["|||:="] := "\\protect\\LONGOPASSIGN{[\\:]\\Join}" +translation["===:="] := "\\protect\\OPASSIGN{\\equiv}" +translation["~==="] := "\^K\\not\\equiv\^K" +translation["<=:="] := "\\protect\\OPASSIGN{\\leq}" +translation[">=:="] := "\\protect\\OPASSIGN{\\geq}" +translation["~=:="] := "\\protect\\OPASSIGN{\\neq}" +translation["++:="] := "\\protect\\OPASSIGN{\\uplus}" +translation["--:="] := "\\protect\\OPASSIGN{\\ni}" +translation["**:="] := "\\protect\\OPASSIGN{\\in}" +translation["||:="] := "\\protect\\OPASSIGN{\\Join}" +translation["<\<:="] := "\\protect\\OPASSIGN{\\prec}" +translation["==:="] := "\\protect\\OPASSIGN{\\approx}" +translation[">>:="] := "\\protect\\OPASSIGN{\\succ}" + +translation["<\<="] := "\^K\\preceq\^K" +translation[">>="] := "\^K\\succeq\^K" +translation["~=="] := "\^K\\not\\approx\^K" +translation["|||"] := "\\protect\\OPSTACK{[\\:]}{\\Join}" +translation[":=:"] := "\^K\\leftrightarrow\^K" +translation["<->"] := "\^K\\Leftrightarrow\^K" +translation["==="] := "\^K\\equiv\^K" +translation["+:="] := "\\protect\\OPASSIGN{+}" +translation["-:="] := "\\protect\\OPASSIGN{-}" +translation["*:="] := "\\protect\\OPASSIGN{\\ast}" +translation["/:="] := "\\protect\\OPASSIGN{\\div}" +translation["%:="] := "\\protect\\OPASSIGN{" || preTeX("%") || "}" +translation["^:="] := "\\protect\\OPASSIGN{\\uparrow}" +translation["<:="] := "\\protect\\OPASSIGN{<}" +translation["=:="] := "\\protect\\OPASSIGN{=}" +translation[">:="] := "\\protect\\OPASSIGN{>}" +translation["@:="] := "\\protect\\OPASSIGN{\\partial}" +translation["&:="] := "\\protect\\OPASSIGN{\\wedge}" +translation["?:="] := "\\protect\\OPASSIGN{\\wr}" +translation["()"] := "\^K(\\;)\^K" +translation["[]"] := "\^K[\\;]\^K" +translation["<="] := "\^K\\leq\^K" +translation[">="] := "\^K\\geq\^K" +translation["~="] := "\^K\\neq\^K" +translation["++"] := "\^K\\uplus\^K" +translation["--"] := "\^K\\ni\^K" +translation["**"] := "\^K\\in\^K" +translation["||"] := "\^K\\Join\^K" +translation["<\<"] := "\^K\\prec\^K" +translation["=="] := "\^K\\approx\^K" +translation[">>"] := "\^K\\succ\^K" +translation[":="] := "\^K\\leftarrow\^K" +translation["<-"] := "\^K\\Leftarrow\^K" +translation["{"] := "\\boldmath\^K\\{\^K" +translation["}"] := "\\boldmath\^K\\}\^K" +translation["|"] := "\^K\\vee\^K" +translation["?"] := "\^K\\wr\^K" +translation["~"] := "\^K\\ni\^K" +translation["!"] := "\^K\\forall\^K" +translation["^"] := "\^K\\uparrow\^K" +translation["*"] := "\^K\\ast\^K" +translation["\\"] := "\^K\\exists\^K" +translation["%"] := "\^K" || preTeX("%") || "\^K" +translation["&"] := prefixed_keyword_check +translation["$"] := prefixed_keyword_check +translation["'"] := quoted_c_string +translation["\""] := quoted_c_string + +every op := !operator_list do /translation[op] := "\^K" || op || "\^K" +$endif +@ +<<\ICON\ [[prefixed_keyword_check]] procedure>>= +procedure prefixed_keyword_check(arg) +local keyword_list, keyword, result + keyword_list := (arg == "&", an_word_list) | ss_word_list + keyword := tab(match(!keyword_list)) | &null + if \keyword then result := "{\\bf{}" || preTeX(arg) || keyword || "}" + else result := "\^K" || ((arg == "&", "\\wedge") | preTeX(arg)) || "\^K" + return result +end +@ +<<Emit special \LaTeX\ definitions>>= +$ifdef LANG_ICON +write("@literal \\catcode`^^K=3") +write("@literal \\newcommand{\\LONGOPASSIGN}[1]{\\raisebox{-.4ex}" || + "{\^K\\stackrel{\\scriptscriptstyle\\,#1}{\\longleftarrow}\^K}}") +write("@literal \\newcommand{\\OPASSIGN}[1]{\\raisebox{-.4ex}" || + "{\^K\\stackrel{\\scriptscriptstyle\\,#1}{\\leftarrow}\^K}}") +write("@literal \\newcommand{\\OPSTACK}[2]{\\raisebox{-.4ex}" || + "{\^K\\stackrel{\\scriptscriptstyle#1}{#2}\^K}}") +$endif +@ + +\section{Chunks} +\nowebchunks + +\begin{thebibliography}{Mmoo} +\bibitem[Gris]{griswold}Ralph E. Griswold and Madge T. Griswold. {\em +The Icon Programming Language}. Prentice-Hall, Englewood Cliffs, New +Jersey, 1983. +\bibitem[Oiko]{kostas}Kostas N. Oikonomou. {\em Extending Noweb With +Some Typesetting}. Unpublished. Included in {\tt contrib} directory +of the standard {\tt noweb} distribution. +\end{thebibliography} +\end{document} +% Local Variables: +% outline-regexp: "\\([\\\]\\(sub\\)*sec\\)\\|\\(<[^>]+>>=\\)" +% End: diff --git a/web/noweb/contrib/kostas/C++_translation_table b/web/noweb/contrib/kostas/C++_translation_table new file mode 100644 index 0000000000..1653f51057 --- /dev/null +++ b/web/noweb/contrib/kostas/C++_translation_table @@ -0,0 +1,64 @@ +# This file defines translations into \TeX\ code for keywords of C++. It also defines +# translations for special tokens, such as <=. + +# Initialize the translation table to contain nulls. +translation := table() + +# Reserved words. +translation["asm"] := "{\\ttb{}asm}" +translation["auto"] := "{\\ttb{}auto}" +translation["break"] := "{\\ttb{}break}" +translation["case"] := "{\\ttb{}case}" +translation["char"] := "{\\ttb{}char}" +translation["class"] := "{\\ttb{}class}" +translation["const"] := "{\\ttb{}const}" +translation["continue"] := "{\\ttb{}continue}" +translation["default"] := "{\\ttb{}default}" +translation["delete"] := "{\\ttb{}delete}" +translation["do"] := "{\\ttb{}do}" +translation["double"] := "{\\ttb{}double}" +translation["else"] := "{\\ttb{}else}" +translation["enum"] := "{\\ttb{}enum}" +translation["extern"] := "{\\ttb{}extern}" +translation["float"] := "{\\ttb{}float}" +translation["for"] := "{\\ttb{}for}" +translation["friend"] := "{\\ttb{}friend}" +translation["goto"] := "{\\ttb{}goto}" +translation["if"] := "{\\ttb{}if}" +translation["inline"] := "{\\ttb{}inline}" +translation["int"] := "{\\ttb{}int}" +translation["long"] := "{\\ttb{}long}" +translation["new"] := "{\\ttb{}new}" +translation["operator"] := "{\\ttb{}operator}" +translation["overload"] := "{\\ttb{}overload}" +translation["private"] := "{\\ttb{}private}" +translation["protected"] := "{\\ttb{}protected}" +translation["public"] := "{\\ttb{}public}" +translation["register"] := "{\\ttb{}register}" +translation["return"] := "{\\ttb{}return}" +translation["short"] := "{\\ttb{}short}" +translation["sizeof"] := "{\\ttb{}sizeof}" +translation["static"] := "{\\ttb{}static}" +translation["struct"] := "{\\ttb{}struct}" +translation["switch"] := "{\\ttb{}switch}" +translation["this"] := "{\\ttb{}this}" +translation["typedef"] := "{\\ttb{}typedef}" +translation["union"] := "{\\ttb{}union}" +translation["unsigned"] := "{\\ttb{}unsigned}" +translation["virtual"] := "{\\ttb{}virtual}" +translation["void"] := "{\\ttb{}void}" +translation["while"] := "{\\ttb{}while}" + +# Translations for operators. +translation["{"] := "\\{" +translation["}"] := "\\}" +translation["<"] := "\\(<\\)" +translation[">"] := "\\(>\\)" +translation["<<"] := "\\(\\ll\\)" +translation[">>"] := "\\(\\gg\\)" +translation["!="] := "\\(\\neq\\)" +translation["&&"] := "\\(\\land\\)" +translation["||"] := "\\(\\lor\\)" +translation["<="] := "\\(\\le\\)" +translation[">="] := "\\(\\ge\\)" +translation["->"] := "\\(\\to\\)" diff --git a/web/noweb/contrib/kostas/C_translation_table b/web/noweb/contrib/kostas/C_translation_table new file mode 100644 index 0000000000..a283eb89ce --- /dev/null +++ b/web/noweb/contrib/kostas/C_translation_table @@ -0,0 +1,55 @@ +# This file defines translations into \TeX\ code for keywords of C. It also defines +# translations for special tokens, such as <=. + +# Initialize the translation table to contain nulls. +translation := table() + +# Reserved words. +translation["auto"] := "{\\ttb{}auto}" +translation["break"] := "{\\ttb{}break}" +translation["case"] := "{\\ttb{}case}" +translation["char"] := "{\\ttb{}char}" +translation["continue"] := "{\\ttb{}continue}" +translation["default"] := "{\\ttb{}default}" +translation["do"] := "{\\ttb{}do}" +translation["double"] := "{\\ttb{}double}" +translation["else"] := "{\\ttb{}else}" +translation["enum"] := "{\\ttb{}enum}" +translation["extern"] := "{\\ttb{}extern}" +translation["float"] := "{\\ttb{}float}" +translation["for"] := "{\\ttb{}for}" +translation["goto"] := "{\\ttb{}goto}" +translation["if"] := "{\\ttb{}if}" +translation["int"] := "{\\ttb{}int}" +translation["long"] := "{\\ttb{}long}" +translation["register"] := "{\\ttb{}register}" +translation["return"] := "{\\ttb{}return}" +translation["short"] := "{\\ttb{}short}" +translation["sizeof"] := "{\\ttb{}sizeof}" +translation["static"] := "{\\ttb{}static}" +translation["struct"] := "{\\ttb{}struct}" +translation["switch"] := "{\\ttb{}switch}" +translation["typedef"] := "{\\ttb{}typedef}" +translation["union"] := "{\\ttb{}union}" +translation["unsigned"] := "{\\ttb{}unsigned}" +translation["void"] := "{\\ttb{}void}" +translation["while"] := "{\\ttb{}while}" + +# Pre-processor directives +translation["#define"] := "{#\\ttb{}define}" +translation["#include"] := "{#\\ttb{}include}" + +# Translations for operators. +translation["{"] := "\\{" +translation["}"] := "\\}" +translation["<"] := "\\(<\\)" +translation[">"] := "\\(>\\)" +translation["<<"] := "\\(\\ll\\)" +translation[">>"] := "\\(\\gg\\)" +translation["!="] := "\\(\\neq\\)" +translation["&&"] := "\\(\\land\\)" +translation["||"] := "\\(\\lor\\)" +translation["<="] := "\\(\\le\\)" +translation[">="] := "\\(\\ge\\)" +translation["->"] := "\\(\\to\\)" + diff --git a/web/noweb/contrib/kostas/Makefile b/web/noweb/contrib/kostas/Makefile new file mode 100644 index 0000000000..27bbffad71 --- /dev/null +++ b/web/noweb/contrib/kostas/Makefile @@ -0,0 +1,75 @@ +# Only works with Gnu make. + +LIB=/opt/noweb/lib +ICONC=icont +# This is supposed to be the defns.nw file in the icon directory of the distribution. +defns=defns.nw +TANGLE=notangle +WEAVE=noweave -delay -filter icon.filter -index + +.SUFFIXES: .nw .icn .tex .dvi + + +all: C.filter C++.filter icon.filter oot.filter math.filter\ + autodefs.oot autodefs.math + +install: + mv *.filter $(LIB) + mv autodefs.* $(LIB) + + +# TeX files. +%.tex : %.nw + $(WEAVE) $< > $@ +pp.tex: pp.nw + noweave -delay -autodefs icon -filter icon.filter -index pp.nw > pp.tex +%.dvi : %.tex + latex $< +# Don't delete the intermediate .tex file. +.PRECIOUS : %.tex + + +# Icon files. +C.icn: pp.nw C_translation_table + $(TANGLE) -R"C" pp.nw > $@ +C++.icn: pp.nw C++_translation_table + $(TANGLE) -R"C++" pp.nw > $@ +icon.icn: pp.nw icon_translation_table + $(TANGLE) -R"Icon" pp.nw > $@ +oot.icn: pp.nw oot_translation_table + $(TANGLE) -R"OOT" pp.nw > $@ +math.icn: pp.nw math_translation_table + $(TANGLE) -R"Mathematica" pp.nw > $@ + +ootdefs.icn: ootdefs.nw + $(TANGLE) $< $(defns) > $@ +mathdefs.icn: mathdefs.nw + $(TANGLE) $< $(defns) > $@ + + +# Executables: filters. +%.filter : %.icn + $(ICONC) -o $@ $< + +# Executables: autodefs. +autodefs.oot: ootdefs.icn + $(ICONC) -o autodefs.oot ootdefs.icn +autodefs.math: mathdefs.icn + $(ICONC) -o autodefs.math mathdefs.icn + + +# Cleaning: remove all files that can be recreated from noweb sources. +nowebs := $(wildcard *.nw) +rem := $(nowebs:.nw=.icn) +rem := $(rem) $(nowebs:.nw=.tex) +rem := $(rem) $(nowebs:.nw=.log) +rem := $(rem) $(nowebs:.nw=.aux) +rem := $(rem) $(nowebs:.nw=.toc) + + +# Also remove the Icon files for the filters. +clean: + -rm -f $(rem) C.icn C++.icn icon.icn oot.icn math.icn *.filter autodefs.* + + + diff --git a/web/noweb/contrib/kostas/Makefile.gnu b/web/noweb/contrib/kostas/Makefile.gnu new file mode 100644 index 0000000000..27bbffad71 --- /dev/null +++ b/web/noweb/contrib/kostas/Makefile.gnu @@ -0,0 +1,75 @@ +# Only works with Gnu make. + +LIB=/opt/noweb/lib +ICONC=icont +# This is supposed to be the defns.nw file in the icon directory of the distribution. +defns=defns.nw +TANGLE=notangle +WEAVE=noweave -delay -filter icon.filter -index + +.SUFFIXES: .nw .icn .tex .dvi + + +all: C.filter C++.filter icon.filter oot.filter math.filter\ + autodefs.oot autodefs.math + +install: + mv *.filter $(LIB) + mv autodefs.* $(LIB) + + +# TeX files. +%.tex : %.nw + $(WEAVE) $< > $@ +pp.tex: pp.nw + noweave -delay -autodefs icon -filter icon.filter -index pp.nw > pp.tex +%.dvi : %.tex + latex $< +# Don't delete the intermediate .tex file. +.PRECIOUS : %.tex + + +# Icon files. +C.icn: pp.nw C_translation_table + $(TANGLE) -R"C" pp.nw > $@ +C++.icn: pp.nw C++_translation_table + $(TANGLE) -R"C++" pp.nw > $@ +icon.icn: pp.nw icon_translation_table + $(TANGLE) -R"Icon" pp.nw > $@ +oot.icn: pp.nw oot_translation_table + $(TANGLE) -R"OOT" pp.nw > $@ +math.icn: pp.nw math_translation_table + $(TANGLE) -R"Mathematica" pp.nw > $@ + +ootdefs.icn: ootdefs.nw + $(TANGLE) $< $(defns) > $@ +mathdefs.icn: mathdefs.nw + $(TANGLE) $< $(defns) > $@ + + +# Executables: filters. +%.filter : %.icn + $(ICONC) -o $@ $< + +# Executables: autodefs. +autodefs.oot: ootdefs.icn + $(ICONC) -o autodefs.oot ootdefs.icn +autodefs.math: mathdefs.icn + $(ICONC) -o autodefs.math mathdefs.icn + + +# Cleaning: remove all files that can be recreated from noweb sources. +nowebs := $(wildcard *.nw) +rem := $(nowebs:.nw=.icn) +rem := $(rem) $(nowebs:.nw=.tex) +rem := $(rem) $(nowebs:.nw=.log) +rem := $(rem) $(nowebs:.nw=.aux) +rem := $(rem) $(nowebs:.nw=.toc) + + +# Also remove the Icon files for the filters. +clean: + -rm -f $(rem) C.icn C++.icn icon.icn oot.icn math.icn *.filter autodefs.* + + + diff --git a/web/noweb/contrib/kostas/Makefile.make b/web/noweb/contrib/kostas/Makefile.make new file mode 100644 index 0000000000..27bbffad71 --- /dev/null +++ b/web/noweb/contrib/kostas/Makefile.make @@ -0,0 +1,75 @@ +# Only works with Gnu make. + +LIB=/opt/noweb/lib +ICONC=icont +# This is supposed to be the defns.nw file in the icon directory of the distribution. +defns=defns.nw +TANGLE=notangle +WEAVE=noweave -delay -filter icon.filter -index + +.SUFFIXES: .nw .icn .tex .dvi + + +all: C.filter C++.filter icon.filter oot.filter math.filter\ + autodefs.oot autodefs.math + +install: + mv *.filter $(LIB) + mv autodefs.* $(LIB) + + +# TeX files. +%.tex : %.nw + $(WEAVE) $< > $@ +pp.tex: pp.nw + noweave -delay -autodefs icon -filter icon.filter -index pp.nw > pp.tex +%.dvi : %.tex + latex $< +# Don't delete the intermediate .tex file. +.PRECIOUS : %.tex + + +# Icon files. +C.icn: pp.nw C_translation_table + $(TANGLE) -R"C" pp.nw > $@ +C++.icn: pp.nw C++_translation_table + $(TANGLE) -R"C++" pp.nw > $@ +icon.icn: pp.nw icon_translation_table + $(TANGLE) -R"Icon" pp.nw > $@ +oot.icn: pp.nw oot_translation_table + $(TANGLE) -R"OOT" pp.nw > $@ +math.icn: pp.nw math_translation_table + $(TANGLE) -R"Mathematica" pp.nw > $@ + +ootdefs.icn: ootdefs.nw + $(TANGLE) $< $(defns) > $@ +mathdefs.icn: mathdefs.nw + $(TANGLE) $< $(defns) > $@ + + +# Executables: filters. +%.filter : %.icn + $(ICONC) -o $@ $< + +# Executables: autodefs. +autodefs.oot: ootdefs.icn + $(ICONC) -o autodefs.oot ootdefs.icn +autodefs.math: mathdefs.icn + $(ICONC) -o autodefs.math mathdefs.icn + + +# Cleaning: remove all files that can be recreated from noweb sources. +nowebs := $(wildcard *.nw) +rem := $(nowebs:.nw=.icn) +rem := $(rem) $(nowebs:.nw=.tex) +rem := $(rem) $(nowebs:.nw=.log) +rem := $(rem) $(nowebs:.nw=.aux) +rem := $(rem) $(nowebs:.nw=.toc) + + +# Also remove the Icon files for the filters. +clean: + -rm -f $(rem) C.icn C++.icn icon.icn oot.icn math.icn *.filter autodefs.* + + + diff --git a/web/noweb/contrib/kostas/README b/web/noweb/contrib/kostas/README new file mode 100644 index 0000000000..d26e9c78b5 --- /dev/null +++ b/web/noweb/contrib/kostas/README @@ -0,0 +1,14 @@ +This directory contains noweb programs (written in Icon) that extend the basic noweb +with some pretty-printing capabilities. By this we don't mean formatting, but just +printing keywords in bold, comments in roman, mathematical operators using special +symbols, etc. The pretty-printers work as noweb filters. + +There are 4 pretty-printers here, all generated from the same file, pp.nw. The +pretty-printers are for C/C++, Icon, Turing (OOT), and Mathematica. pp.nw contains +full documentation, and is written in a way that adding another language should be +easy. (Let me know if you find it isn't so.) + +The pretty-printers work only with LaTeX2e, and you must have the cmttb10 font. + +There are also filters that write noweb index entries (autodefs) for Turing and +Mathematica. diff --git a/web/noweb/contrib/kostas/WHATS_NEW b/web/noweb/contrib/kostas/WHATS_NEW new file mode 100644 index 0000000000..e08b859d56 --- /dev/null +++ b/web/noweb/contrib/kostas/WHATS_NEW @@ -0,0 +1,46 @@ +WHAT'S NEW: + +1) Indexing: the pretty-printers now fix up some of the deficiencies of noweb's +indexing mechanism. + +"finduses" generates spurious references to identifiers when it sees a string +matching an identifier inside a comment or string literal. For example, try + + noweave -autodefs c -index t.nw > t.tex + latex t + latex t + +on the file + +<<Section A>>= +int num; +<<Section B>>= +/* num is the number ... */ +<<Section C>>= +printf("num is the number of things\n"); +@ +\nowebindex + +noweave (finduses) will generate a reference to num in each of sections B and C. + +Now try + + noweave -autodefs c -filter C.filter -index t.nw > t.tex + latex t + latex t + +(NOTE: -filter first, -index next!) +You get a pretty-printed version without these references. All pretty-printer +filters suppress these spurious references. + + +2) A bug in pretty-printing operators has been fixed. For example, if ``=='' is +meant to be typeset as ``\equiv'', say, but ``==='' is supposed to be left alone, the +old version would typeset ``==='' as ``\equiv=''. + + +3) For those who know about OOT: the autodefs.oot now does not index identifiers +declared in subprograms. This reduces clutter. However, it has the capability to +index such identifiers selectively if the user supplies an argument to the filter, +the name of a file listing subprogram names one per line. (See ootdefs.nw for +details.) diff --git a/web/noweb/contrib/kostas/defns.nw b/web/noweb/contrib/kostas/defns.nw new file mode 100644 index 0000000000..0d3326c446 --- /dev/null +++ b/web/noweb/contrib/kostas/defns.nw @@ -0,0 +1,33 @@ +<<*>>= +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +@ +[[indextext]] is a hack to introduce suitable ``[[@index nl]],'' but it +messes up the line counts! +<<*>>= +procedure writedefn(defn) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & <<flush index>> + else { + if *indextext + *defn > 65 then <<flush index>> + write("@index defn ", defn) + indextext ||:= " " || defn + } + return +end +<<flush index>>= +{ # write("@index nl") # don't! + indextext := "" +} diff --git a/web/noweb/contrib/kostas/email b/web/noweb/contrib/kostas/email new file mode 100644 index 0000000000..fec98b1e32 --- /dev/null +++ b/web/noweb/contrib/kostas/email @@ -0,0 +1 @@ +ko@surya.ho.att.com diff --git a/web/noweb/contrib/kostas/icon_translation_table b/web/noweb/contrib/kostas/icon_translation_table new file mode 100644 index 0000000000..d3b8099666 --- /dev/null +++ b/web/noweb/contrib/kostas/icon_translation_table @@ -0,0 +1,140 @@ +# This file defines translations into \TeX\ code for reserved words, and keywords of Icon. +# It also defines translations for special tokens, such as <=. + + +# Initialize the translation table to contain nulls. +translation := table() + + +# Reserved words. +translation["by"] := "{\\ttb{}by}" +translation["break"] := "{\\ttb{}break}" +translation["case"] := "{\\ttb{}case}" +translation["create"] := "{\\ttb{}create}" +translation["default"] := "{\\ttb{}default}" +translation["do"] := "{\\ttb{}do}" +translation["else"] := "{\\ttb{}else}" +translation["end"] := "{\\ttb{}end}" +translation["every"] := "{\\ttb{}every}" +translation["global"] := "{\\ttb{}global}" +translation["fail"] := "{\\ttb{}fail}" +translation["if"] := "{\\ttb{}if}" +translation["initial"] := "{\\ttb{}initial}" +translation["link"] := "{\\ttb{}link}" +translation["local"] := "{\\ttb{}local}" +translation["next"] := "{\\ttb{}next}" +translation["not"] := "{\\ttb{}not}" +translation["of"] := "{\\ttb{}of}" +translation["procedure"] := "{\\ttb{}procedure}" +translation["record"] := "{\\ttb{}record}" +translation["repeat"] := "{\\ttb{}repeat}" +translation["return"] := "{\\ttb{}return}" +translation["static"] := "{\\ttb{}static}" +translation["suspend"] := "{\\ttb{}suspend}" +translation["to"] := "{\\ttb{}to}" +translation["then"] := "{\\ttb{}then}" +translation["while"] := "{\\ttb{}while}" +translation["until"] := "{\\ttb{}until}" + +# Icon keywords. +translation["&ascii"] := "{\\ttb{}&ascii}" +translation["&clock"] := "{\\ttb{}\&clock}" +translation["&collections"] := "{\\ttb{}\&collections}" +translation["&cset"] := "{\\ttb{}\&cset}" +translation["¤t"] := "{\\ttb{}\¤t}" +translation["&date"] := "{\\ttb{}\&date}" +translation["&dateline"] := "{\\ttb{}\&dateline}" +translation["&digits"] := "{\\ttb{}\&digits}" +translation["&error"] := "{\\ttb{}\&error}" +translation["&errornumber"] := "{\\ttb{}\&errornumber}" +translation["&errortext"] := "{\\ttb{}\&errortext}" +translation["&errorvalue"] := "{\\ttb{}\&errorvalue}" +translation["&errout"] := "{\\ttb{}\&errout}" +translation["&fail"] := "{\\ttb{}\&fail}" +translation["&features"] := "{\\ttb{}\&features}" +translation["&file"] := "{\\ttb{}\&file}" +translation["&host"] := "{\\ttb{}\&host}" +translation["&input"] := "{\\ttb{}\&input}" +translation["&lcase"] := "{\\ttb{}\&lcase}" +translation["&letters"] := "{\\ttb{}\&letters}" +translation["&level"] := "{\\ttb{}\&level}" +translation["&line"] := "{\\ttb{}\&line}" +translation["&main"] := "{\\ttb{}\&main}" +translation["&null"] := "{\\ttb{}\&null}" +translation["&output"] := "{\\ttb{}\&output}" +translation["&pos"] := "{\\ttb{}\&pos}" +translation["&random"] := "{\\ttb{}\&random}" +translation["®ions"] := "{\\ttb{}\®ions}" +translation["&source"] := "{\\ttb{}\&source}" +translation["&storage"] := "{\\ttb{}\&storage}" +translation["&subject"] := "{\\ttb{}\&subject}" +translation["&time"] := "{\\ttb{}\&time}" +translation["&trace"] := "{\\ttb{}\&trace}" +translation["&ucase"] := "{\\ttb{}\&ucase}" +translation["&version"] := "{\\ttb{}\&version}" + +# Added in Version 8.10. +translation["&allocated"] := "{\\ttb{}\&allocated}" +translation["&e"] := "{\\ttb{}\&e}" +translation["&phi"] := "{\\ttb{}\&phi}" +translation["&pi"] := "{\\ttb{}\&pi}" +translation["&progname"] := "{\\ttb{}\&progname}" + +# Added by the X interface. +#translation["&col"] := "{\\ttb{}\&col}" +#translation["&control"] := "{\\ttb{}\&control}" +#translation["&interval"] := "{\\ttb{}\&interval}" +#translation["&ldrag"] := "{\\ttb{}\&ldrag}" +#translation["&lpress"] := "{\\ttb{}\&lpress}" +#translation["&lrelease"] := "{\\ttb{}\&lrelease}" +#translation["&mdrag"] := "{\\ttb{}\&mdrag}" +#translation["&meta"] := "{\\ttb{}\&meta}" +#translation["&mpress"] := "{\\ttb{}\&mpress}" +#translation["&mrelease"] := "{\\ttb{}\&mrelease}" +#translation["&resize"] := "{\\ttb{}\&resize}" +#translation["&rdrag"] := "{\\ttb{}\&rdrag}" +#translation["&row"] := "{\\ttb{}\&row}" +#translation["&rpress"] := "{\\ttb{}\&rpress}" +#translation["&rrelease"] := "{\\ttb{}\&rrelease}" +#translation["&shift"] := "{\\ttb{}\&shift}" +#translation["&window"] := "{\\ttb{}\&window}" +#translation["&x"] := "{\\ttb{}\&x}" +#translation["&y"] := "{\\ttb{}\&y}" + + +# Translator directives (V8.10). +translation["$include"] := "{\\ttb{}\\$include}" +translation["$line"] := "{\\ttb\\$line}" +translation["$define"] := "{\\ttb\\$define}" +translation["$undef"] := "{\\ttb\\$undef}" +translation["$ifdef"] := "{\\ttb\\$ifdef}" +translation["$ifndef"] := "{\\ttb\\$ifndef}" +translation["$else"] := "{\\ttb\\$else}" +translation["$endif"] := "{\\ttb\\$endif}" + + +# Translations for operators, and other good stuff. +translation["{"] := "\\{" +translation["}"] := "\\}" +translation["\\"] := "\\verb|\\|" +translation["<"] := "\\(<\\)" +translation[">"] := "\\(>\\)" +translation["<="] := "\\(\\le\\)" +translation[">="] := "\\(\\ge\\)" +translation["~="] := "\\(\\neq\\)" +translation["++"] := "\\(\\cup\\)" +translation["**"] := "\\(\\cap\\)" +translation["--"] := "\\(\\setminus\\)" +translation["&"] := "\\(\\land\\)" # Conjunction +translation["|"] := "\\(\\lor\\)" # Alternation +translation[">>"] := "\\(\\succ\\)" +translation["<<"] := "\\(\\prec\\)" +translation["||"] := "\\(\\Vert\\)" +translation[">>="] := "\\(\\succeq\\)" +translation["<<="] := "\\(\\preceq\\)" +#translation["=="] := ? +#translation["~=="] := ? +translation["==="] := "\\(\\equiv\\)" +translation["~==="] := "\\(\\not\\equiv\\)" +translation[":=:"] := "\\(\\leftrightarrow\\)" +translation["<->"] := "\\(\\leftrightarrow\\)" diff --git a/web/noweb/contrib/kostas/math_translation_table b/web/noweb/contrib/kostas/math_translation_table new file mode 100644 index 0000000000..cc57c4e995 --- /dev/null +++ b/web/noweb/contrib/kostas/math_translation_table @@ -0,0 +1,68 @@ +# This file defines translations into \TeX\ code for some of the most common Mathematica +# keywords. Not all of them, because there are too many. +# It also defines translations for special tokens, such as <=. + + +# Initialize the translation table to contain nulls. +translation := table() + +# Keywords. +translation["Abort"] := "{\\ttb{}Abort}" +translation["And"] := "{\\ttb{}And}" +translation["Append"] := "{\\ttb{}Append}" +translation["AppendTo"] := "{\\ttb{}AppendTo}" +translation["Apply"] := "{\\ttb{}Apply}" +translation["Array"] := "{\\ttb{}Array}" +translation["Assert"] := "{\\ttb{}Assert}" # This is mine. +translation["Begin"] := "{\\ttb{}Begin}" +translation["BeginPackage"] := "{\\ttb{}BeginPackage}" +translation["Block"] := "{\\ttb{}Block}" +translation["Break"] := "{\\ttb{}Break}" +translation["Chop"] := "{\\ttb{}Chop}" +translation["Continue"] := "{\\ttb{}Continue}" +translation["Do"] := "{\\ttb{}Do}" +translation["End"] := "{\\ttb{}End}" +translation["EndPackage"] := "{\\ttb{}EndPackage}" +# My addition: +translation["ExitWhen"] := "{\\ttb{}ExitWhen}" +translation["False"] := "{\\ttb{}False}" +translation["For"] := "{\\ttb{}For}" +translation["Function"] := "{\\ttb{}Function}" +translation["If"] := "{\\ttb{}If}" +translation["Join"] := "{\\ttb{}Join}" +translation["Length"] := "{\\ttb{}Length}" +# My addition: +translation["Loop"] := "{\\ttb{}Loop}" +translation["Map"] := "{\\ttb{}Map}" +translation["Module"] := "{\\ttb{}Module}" +translation["Needs"] := "{\\ttb{}Needs}" +translation["Not"] := "{\\ttb{}Not}" +translation["Part"] := "{\\ttb{}Part}" +translation["Prepend"] := "{\\ttb{}Prepend}" +translation["Print"] := "{\\ttb{}Print}" +translation["Return"] := "{\\ttb{}Return}" +translation["Scan"] := "{\\ttb{}Scan}" +translation["Switch"] := "{\\ttb{}Switch}" +translation["Table"] := "{\\ttb{}Table}" +translation["Take"] := "{\\ttb{}Take}" +translation["True"] := "{\\ttb{}True}" +translation["Union"] := "{\\ttb{}Union}" +translation["Which"] := "{\\ttb{}Which}" +translation["While"] := "{\\ttb{}While}" + + +# Translations for operators, etc. +translation["{"] := "\\{" +translation["}"] := "\\}" +translation["<"] := "\\(<\\)" +translation[">"] := "\\(>\\)" +translation["!="] := "\\(\\neq\\)" +translation["=="] := "\\(\\equiv\\)" +translation["<="] := "\\(\\le\\)" +translation[">="] := "\\(\\ge\\)" +translation["->"] := "\\(\\rightarrow\\)" +translation["&&"] := "\\(\\land\\)" +translation["||"] := "\\(\\lor\\)" +translation["**"] := "\\(\\otimes\\)" +translation["<>"] := "\\(\\bowtie\\)" + diff --git a/web/noweb/contrib/kostas/mathdefs.nw b/web/noweb/contrib/kostas/mathdefs.nw new file mode 100644 index 0000000000..e77f7d3845 --- /dev/null +++ b/web/noweb/contrib/kostas/mathdefs.nw @@ -0,0 +1,24 @@ +\section{Finding \textsl{Mathematica} definitions} + +This will simply recognize definitions made with ``:=''. +<<*>>= +procedure main(args) + go() +end +<<*>>= +procedure postpass(name, arg) + static kind, id + initial {kind := "bogus"; id := &letters ++ &digits} + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then + arg ? if s := tab(find(":=")) then + {s ? {tab(many(' ')); writedefn(tab(many(id)))} + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end diff --git a/web/noweb/contrib/kostas/oot_translation_table b/web/noweb/contrib/kostas/oot_translation_table new file mode 100644 index 0000000000..0ca9078412 --- /dev/null +++ b/web/noweb/contrib/kostas/oot_translation_table @@ -0,0 +1,137 @@ +# This file defines translations into \TeX\ code for keywords of OOT (Object-Oriented +# Turing). It also defines translations for special tokens, such as <=. + +# Initialize the translation table to contain nulls. +translation := table() + +# Reserved words. +translation["addressint"] := "{\\ttb{}addressint}" +translation["all"] := "{\\ttb{}all}" +translation["and"] := "\\(\\land\\)" +translation["anyclass"] := "{\\ttb{}anyclass}" +translation["array"] := "{\\ttb{}array}" +translation["assert"] := "{\\ttb{}assert}" +translation["begin"] := "{\\ttb{}begin}" +translation["bind"] := "{\\ttb{}bind}" +translation["body"] := "{\\ttb{}body}" +translation["boolean"] := "{\\ttb{}boolean}" +translation["by"] := "{\\ttb{}by}" +translation["case"] := "{\\ttb{}case}" +translation["char"] := "{\\ttb{}char}" +translation["cheat"] := "{\\ttb{}cheat}" +translation["checked"] := "{\\ttb{}checked}" +translation["class"] := "{\\ttb{}class}" +translation["close"] := "{\\ttb{}close}" +translation["collection"] := "{\\ttb{}collection}" +translation["const"] := "{\\ttb{}const}" +translation["decreasing"] := "{\\ttb{}decreasing}" +translation["deferred"] := "{\\ttb{}deferred}" +translation["div"] := "{\\ttb{}div}" +translation["else"] := "{\\ttb{}else}" +translation["elsif"] := "{\\ttb{}elsif}" +translation["end"] := "{\\ttb{}end}" +translation["enum"] := "{\\ttb{}enum}" +translation["exit"] := "{\\ttb{}exit}" +translation["export"] := "{\\ttb{}export}" +translation["external"] := "{\\ttb{}external}" +translation["false"] := "{\\ttb{}false}" +translation["flexible"] := "{\\ttb{}flexible}" +translation["for"] := "{\\ttb{}for}" +translation["fork"] := "{\\ttb{}fork}" +translation["forward"] := "{\\ttb{}forward}" +translation["free"] := "{\\ttb{}free}" +translation["function"] := "{\\ttb{}function}" +translation["get"] := "{\\ttb{}get}" +translation["if"] := "{\\ttb{}if}" +translation["implement"] := "{\\ttb{}implement}" +translation["import"] := "{\\ttb{}import}" +translation["in"] := "\\(\\in\\)" +translation["include"] := "{\\ttb{}include}" +translation["inherit"] := "{\\ttb{}inherit}" +translation["init"] := "{\\ttb{}init}" +translation["int"] := "{\\ttb{}int}" +translation["int1"] := "{\\ttb{}int1}" +translation["int2"] := "{\\ttb{}int2}" +translation["int4"] := "{\\ttb{}int4}" +translation["invariant"] := "{\\ttb{}invariant}" +translation["label"] := "{\\ttb{}label}" +translation["loop"] := "{\\ttb{}loop}" +translation["mod"] := "{\\ttb{}mod}" +translation["module"] := "{\\ttb{}module}" +translation["monitor"] := "{\\ttb{}monitor}" +translation["nat"] := "{\\ttb{}nat}" +translation["nat1"] := "{\\ttb{}nat1}" +translation["nat2"] := "{\\ttb{}nat2}" +translation["nat4"] := "{\\ttb{}nat4}" +translation["new"] := "{\\ttb{}new}" +translation["nil"] := "{\\ttb{}nil}" +translation["not"] := "\\(\\neg\\)\\kern-0.3em" +translation["of"] := "{\\ttb{}of}" +translation["opaque"] := "{\\ttb{}opaque}" +translation["open"] := "{\\ttb{}open}" +translation["or"] := "\\(\\lor\\)" +translation["pause"] := "{\\ttb{}pause}" +translation["pervasive"] := "{\\ttb{}pervasive}" +translation["pointer"] := "{\\ttb{}pointer}" +translation["post"] := "{\\ttb{}post}" +translation["pre"] := "{\\ttb{}pre}" +translation["procedure"] := "{\\ttb{}procedure}" +translation["process"] := "{\\ttb{}process}" +translation["put"] := "{\\ttb{}put}" +translation["quit"] := "{\\ttb{}quit}" +translation["read"] := "{\\ttb{}read}" +translation["real"] := "{\\ttb{}real}" +translation["real4"] := "{\\ttb{}real4}" +translation["real8"] := "{\\ttb{}real8}" +translation["record"] := "{\\ttb{}record}" +translation["register"] := "{\\ttb{}register}" +translation["result"] := "{\\ttb{}result}" +translation["return"] := "{\\ttb{}return}" +translation["seek"] := "{\\ttb{}seek}" +translation["set"] := "{\\ttb{}set}" +#translation["shl"] := "\\(\\triangleleft\\)" +translation["shl"] := "{\\ttb{}shl}" +#translation["shr"] := "\\(\\triangleright\\)" +translation["shr"] := "{\\ttb{}shr}" +translation["signal"] := "{\\ttb{}signal}" +translation["skip"] := "{\\ttb{}skip}" +translation["string"] := "{\\ttb{}string}" +translation["tag"] := "{\\ttb{}tag}" +translation["tell"] := "{\\ttb{}tell}" +translation["then"] := "{\\ttb{}then}" +translation["to"] := "{\\ttb{}to}" +translation["true"] := "{\\ttb{}true}" +translation["type"] := "{\\ttb{}type}" +translation["unchecked"] := "{\\ttb{}unchecked}" +translation["union"] := "{\\ttb{}union}" +translation["unit"] := "{\\ttb{}unit}" +translation["unqualified"] := "{\\ttb{}unqualified}" +translation["var"] := "{\\ttb{}var}" +translation["wait"] := "{\\ttb{}wait}" +translation["when"] := "{\\ttb{}when}" +translation["write"] := "{\\ttb{}write}" +translation["xor"] := "\\(\\oplus\\)" + +# Translations for operators. +translation["<"] := "\\(<\\)" +translation[">"] := "\\(>\\)" +translation["<="] := "\\(\\le\\)" +translation[">="] := "\\(\\ge\\)" +translation["=>"] := "\\(\\Rightarrow\\)" +translation["**"] := "\\^{}" +translation["~="] := "\\(\\neq\\)" +translation["->"] := "\\(\\triangleright\\)" + + +# Pre-processor directives +translation["#else"] := "{#\\ttb{}else}" +translation["#elsif"] := "{#\\ttb{}elsif}" +translation["#end"] := "{#\\ttb{}end}" +translation["#if"] := "{#\\ttb{}if}" +translation["#macro"] := "{#\\ttb{}macro}" + + +# Turing Plus +#translation["child"] := "{\\ttb{}child}" +#translation["stub"] := "{\\ttb{}stub}" + diff --git a/web/noweb/contrib/kostas/ootdefs.nw b/web/noweb/contrib/kostas/ootdefs.nw new file mode 100644 index 0000000000..dcbf8aedea --- /dev/null +++ b/web/noweb/contrib/kostas/ootdefs.nw @@ -0,0 +1,198 @@ +% -*-lang : icon-*- + +\documentclass [11pt] {article} +\usepackage {noweb} +\usepackage {fullpage} +\pagestyle {noweb} + +\title {Automatic \texttt{noweb} Indexing for OOT Identifiers} +\author{Kostas N. Oikonomou \\ \textsf{ko@surya.ho.att.com}} + +\begin {document} +\maketitle + +@ This code builds a [[noweb]] filter which produces automatic index entries for OOT +identifiers. It is used with the generic file [[icon/defns.nw]]. (The Makefile takes +care of this.)\\ +\textsc{Note}: There are some good examples here of how someone used to programming +in Turing can screw up in Icon. They have to do with assignment (Icon may succeed or +fail), and with booleans. +<<*>>= +<<Finding OOT definitions>> +<<Other routines needed by [[defns.nw]]>> +@ + + +@ +\section {Finding OOT definitions} + +[[begin_decl]] is a set of reserved words that signal the beginning of a declaration. +Ideally, after encountering a token in [[begin_decl]], we would find the identifier +and write an index entry. However, some declarations are tricky: +\begin {enumerate} + \item {\ttb{}deferred procedure} P + \item {\ttb{}const pervasive} k := 3 + \item {\ttb{}external} \texttt{"}f\texttt{"} {\ttb{}function} F + \item {\ttb{}var} x, y, z : {\ttb{}real} + \item {\ttb{}import} a, b, {\ttb{}var} u \% No declarations here! + \item We normally don't index identifiers declared inside procedures or functions. + This can be changed, if desired, by making [[ie_list]] empty. However, it is + also possible to invoke the filter with an argument \texttt{\itshape filename}, + where \texttt{\itshape filename\/} contains subprogram names one per line. Then + the local declarations in these subprograms {\em will\/} be indexed\footnote {For + this to work, invoke [[noweave]] as \texttt{noweave -autodefs "oot {\itshape + filename\/}" -filter oot.filter -index}}. +\end {enumerate} +Handling these cases makes life a bit more difficult. We do it by setting up various +categories of tokens (Icon lists). +@ + + +<<Set up token categories>>= +id_chars := &letters ++ &digits ++ '_' +begin_decl := ["class", "const", "function", "module", "monitor", "procedure", + "process", "type", "var"] +qualifier1 := ["deferred", "external", "forward"] # Cases 1, 3 +qualifier2 := ["pervasive"] # Case 2 +ie_list := ["import", "export"] +begin_subprogram := ["procedure", "function"] +@ + +@ + + +@ +<<Finding OOT definitions>>= +procedure postpass(name, arg) + static kind, id_chars, begin_decl, qualifier1, qualifier2, ie_list, + begin_subprogram, in_ie_list, in_subprogram, sub_name + local token + initial { + <<Set up token categories>> + <<Initialize static variables>> + } + if name == "begin" then + arg ? kind := tab(upto(' ')|0) + else + if name == "text" & kind == "code" then + arg ? {<<Go for the identifiers>>} + return +end +@ + +@ +<<Initialize static variables>>= +kind := "bogus" +in_ie_list := &null +in_subprogram := &null +@ + +@ +<<Go for the identifiers>>= +tab(many(' ')) +token := tab(many(id_chars)) # May be null. +if /in_ie_list & /in_subprogram then # ``/'' is Turings' \textbf{not}. + case token of { + !ie_list : in_ie_list := "true" + !qualifier1 : { + <<If \textbf{external}, skip the quoted string>> + <<Find the identifiers and write index entries>>} + !begin_subprogram : <<Index [[sub_name]] and remember it>> + !begin_decl : { + tab(match(!qualifier2)); tab(many(' ')) # Case 2 + <<Find the identifiers and write index entries>>} + } +else { + <<Check for end of \textbf{import/export} list>> + <<Check for end of subprogram>>} +@ + + +@ This is case 3. A quoted string may or may not be there! +<<If \textbf{external}, skip the quoted string>>= +if token == "external" then { + tab(many(' ')) + # Icon note: the next expression fails if one of the sub-expressions fails. + tab(match('"')) & tab(many(id_chars)) & tab(match('"')) + tab(many(' ')) +} +@ + +@ This is case 6. [[sub_name]] is the subprogram's identifier. +<<Index [[sub_name]] and remember it>>= +{tab(many(' ')) + sub_name := tab(many(id_chars)) + writedefn(sub_name) # Defined in \texttt{defns.nw} + # \texttt{sub\_name ~== !subs\_to\_index} doesn't work. Why does it work in \texttt{case}? + if not member(set(subs_to_index), sub_name) then in_subprogram:= "true"} +@ + +@ This is case 4. +Multiple identifiers can occur only in \textbf{var} or \textbf{const} declarations. +<<Find the identifiers and write index entries>>= +repeat { + writedefn(tab(many(id_chars))) # Defined in \texttt{defns.nw} + tab(many(' ')) + if not tab(match(",")) then break + tab(many(' ')) +} +@ + + +@ We assume that every line of a multi-line \textbf{import/export} lists ends with a +comma, unless it is the last line. So if [[in_ie_list]] is set, we check the last +non-blank character of the line. If it is not a ``,'' we set [[in_ie_list]] to null. +Clearly, the Turing compiler will accept multi-line lists even though their lines do +not end with a comma, so this method of detecting the end of the list is not +foolproof. It is, however, simple. +<<Check for end of \textbf{import/export} list>>= +# This is not Turing! \texttt{in\_list := trim(arg)[-1] == ","} is wrong! +if \in_ie_list then + if arg == "" | trim(arg)[-1] ~== "," then in_ie_list := &null +@ + +@ +<<Check for end of subprogram>>= +if \in_subprogram then + if \token == "end" then + {tab(many(' ')); if tab(match(sub_name)) then in_subprogram := &null} +@ + + + +@ +\section {Procedures [[main]] and [[prepass]]} + +This is copied (modified) from [[icon/icondefs.nw]]. +<<Other routines needed by [[defns.nw]]>>= +global subs_to_index +procedure main(args) + local name, f + <<Initialize [[subs_to_index]]>> + go() +end +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # Force newline. +end +@ + +@ +<<Initialize [[subs_to_index]]>>= +subs_to_index := [] +f := open(args[1], "r") +if \f then { + every put(subs_to_index, !f) # Neat! See p. 132 of the Icon book. + writes(&errout, "(Indexing subprograms") + every writes(&errout, " ", !subs_to_index) + write(&errout, ")") + close(f) +} +@ + + + +%@ +%\section {Index} +%\nowebindex + +\end {document}
\ No newline at end of file diff --git a/web/noweb/contrib/kostas/pp.nw b/web/noweb/contrib/kostas/pp.nw new file mode 100644 index 0000000000..05e6c18254 --- /dev/null +++ b/web/noweb/contrib/kostas/pp.nw @@ -0,0 +1,541 @@ +% -*-lang : icon-*- + +\documentclass [11pt] {article} +\usepackage {noweb} +\usepackage {fullpage} +\pagestyle {noweb} + +\title {Extending Noweb With Some Typesetting} +\author{Kostas N. Oikonomou \\ \textsf{ko@surya.ho.att.com}} + +\begin {document} +\maketitle +\tableofcontents + +@ +\section {Introduction} + +This is a pretty-printer, written in Icon, for the [[noweb]] system. The capabilities +of the prettyprinter are to typeset reserved words, comments, quoted strings, and +special (e.g.\ mathematical) symbols of the target language in an almost arbitrary +way\footnote{It is also possible to typeset identifiers arbitrarily.}. This +generality is achieved by the brute-force method of looking up the translation of +(\TeX\ code for) a token in a table. See \S\ref{sec:lang} for the languages that can +be handled. Adding a new language entails making additions \emph{only} to +\S\ref{sec:lang}. All the material in \S\ref{sec:ind} is language-independent, and +should not be touched. + +The pretty-printer's design is based on the following two premises: +\begin {itemize} + \item It should be as independent of the target language as possible, and + \item We don't want to write a full-blown scanner for the target language. +\end {itemize} +Strings of characters of the target language which we want to typeset specially are +called ``interesting tokens''. Having had some experience with Web and SpiderWeb, we +define three categories of interesting tokens: +\begin {enumerate} + \item Reserved words of the target language: we want to typeset them in bold, say. + \item Other strings that we want to typeset specially: e.g. $\le$ for [[<=]]. + \item Comment and quoting tokens (characters): we want what follows them or what is + enclosed by them to be typeset literally. +\end {enumerate} +In addition, comments are typeset in roman font, and math mode is active in comments. + +A table [[translation]] defines a translation into \TeX\ code for every interesting +token in the target language. Here is an excerpt from the translation table for +Object-Oriented Turing: +\begin {center} + \begin {tabular}{l} + [[translation["addressint"] := "{\\ttb{}addressint}"]] \\ + [[translation["all"] := "{\\ttb{}all}"]] \\ + [[translation["and"] := "\\(\\land\\)"]] \\ + [[translation["anyclass"] := "{\\ttb{}anyclass}"]] \\ + [[translation["array"] := "{\\ttb{}array}"]] \\ + [[translation["~="] := "\\(\\neq\\)"]] + \end {tabular} +\end {center} +(Here the control sequence \verb+\ttb+ selects the bold typewriter font +cmttb10\footnote{The empty group \{\} serves to separate the control sequence from +its argument without introducing an extra space.}.) We use four sets of strings to +define the tokens in categories 2 and 3: +\begin {center} + [[special]], [[comment1]], [[comment2]], [[quote2]]. +\end {center} +[[comment1]] is for unbalanced comment strings (e.g.\ the character [[%]] in Turing +and [[#]] in Icon), [[comment2]] is for balanced comment strings (e.g.\ [[/*]] and +[[*/]]), and [[quote2]] is for literal quotes, such as [["]], which we assume to be +balanced. + +Our approach to recognizing the interesting tokens while scanning a line, is to have +a set of characters [[interesting]] (an Icon cset), containing all the characters by +which an interesting token may begin. [[interesting]] is the union of +\begin {itemize} + \item the cset defining the characters which may begin a reserved word, and + \item the cset containing the initial characters of all strings in the special, + comment, and quote sets. +\end {itemize} +The basic idea is this: given a line of text, we scan up to a character in +[[interesting]], and, depending on what this character is, we may try to complete the +token by further scanning. If we succeed, we look up the token in the +[[translation]] table, and if the token is found, we output its translation, +otherwise we output the token itself unchanged. When comment or quote tokens are +recognized, further processing of the line may stop altogether, or temporarily, until +a matching token is found. + + +\section {Languages} +\label{sec:lang} +The languages handled at the moment are C, C++, Icon, Object-Oriented Turing (OOT), +and Mathematica. Looking at the structure that follows should make it clear that +adding another language should be easy. Only this section has to be touched. +<<C>>= +<<[[main]] for C>> +<<Language-independent procedures>> +@ +<<C++>>= +<<[[main]] for C++>> +<<Language-independent procedures>> +@ +<<OOT>>= +<<[[main]] for OOT>> +<<Language-independent procedures>> +@ +<<Icon>>= +<<[[main]] for Icon>> +<<Language-independent procedures>> +@ +<<Mathematica>>= +<<[[main]] for Mathematica>> +<<Language-independent procedures>> +@ + + +@ +\subsection {Main Procedures} + +<<[[main]] for C>>= +procedure main(args) + <<Local variables>> + $include "C_translation_table" + <<C interesting tokens>> + <<Emit special {\TeX} definitions>> + while line := read() do filter(line) +end +@ +@ +<<[[main]] for C++>>= +procedure main(args) + <<Local variables>> + $include "C++_translation_table" + <<C interesting tokens>> + <<Emit special {\TeX} definitions>> + while line := read() do filter(line) +end +@ +@ +<<[[main]] for OOT>>= +procedure main(args) + <<Local variables>> + $include "oot_translation_table" + <<OOT interesting tokens>> + <<Emit special {\TeX} definitions>> + while line := read() do filter(line) +end +@ +@ +<<[[main]] for Icon>>= +procedure main(args) + <<Local variables>> + $include "icon_translation_table" + <<Icon interesting tokens>> + <<Emit special {\TeX} definitions>> + while line := read() do filter(line) +end +@ +@ +<<[[main]] for Mathematica>>= +procedure main(args) + <<Local variables>> + $include "math_translation_table" + <<Mathematica interesting tokens>> + <<Emit special {\TeX} definitions>> + while line := read() do filter(line) +end +@ + + +@ +\subsection {Definition of the Interesting Tokens} +\label{sec:int} + +\textsc{Note}: all of the lists [[special]] must be arranged so that longest tokens +come first! +<<OOT interesting tokens>>= +res_word_chars := &letters ++ '#' +id_chars := res_word_chars ++ &digits ++ '_' +comment1 := ["%"] # Unbalanced comment +comment2 := [["/*", "*/"]] # Balanced comment. This is a set of \emph{pairs}. +quote2 := [["\"","\""]] # Balanced quote +# The special tokens must be sorted so that longest strings come first! +special := ["~=", "**", ">=", "<=", "=>", "->", ">", "<"] +<<Detecting the beginning of a token>> +@ +@ Icon presents an interesting problem, unresolved at this time. See \S\ref{sec:todo}. +<<Icon interesting tokens>>= +res_word_chars := &letters ++ '&$' +id_chars := res_word_chars ++ &digits ++ '_' +comment1 := ["#"] +comment2 := [[]] +quote2 := [["\"","\""], ["\'","\'"]] +special := ["\\", "||", ">=", "<=", "=>", "~=", "++", "**", "--", "{", "}", "<", ">"] +<<Detecting the beginning of a token>> +@ +<<Mathematica interesting tokens>>= +res_word_chars := &letters ++ '&$' +id_chars := res_word_chars ++ &digits +comment1 := [] +comment2 := [["(*", "*)"]] +quote2 := [["\"", "\""]] +special := ["!=", "==", ">=", "<=", "->", "||", "&&", "<>", "**", "{", "}", "<", ">"] +<<Detecting the beginning of a token>> +@ +<<C interesting tokens>>= +res_word_chars := &letters ++ '#' +id_chars := res_word_chars ++ &digits ++ '_' +comment1 := [] +comment2 := [["/*", "*/"]] +quote2 := [["\"","\""],["\'","\'"]] +special := ["!=", ">=", "<=", "@<<", "@>>", "||", "&&", "->", "{", "}", "<", ">"] +<<Detecting the beginning of a token>> +@ + + + +@ +\section {Language-Independent Pretty-Printing} +\label{sec:ind} +<<Language-independent procedures>>= +<<Global variables>> +<<Procedure [[filter]]>> +<<Procedure [[TeXify]]>> +@ + +@ +\subsection {Detecting the Beginning of a Token} + +For each interesting category define a cset containing the characters by which a +token in that category may begin. +<<Detecting the beginning of a token>>= +begin_comment1 := begin_comment2 := begin_quote2 := begin_special := '' +every e := !comment1 do begin_comment1 ++:= cset(e[1]) +every e := !comment2 do begin_comment2 ++:= cset(e[1][1]) +every e := !quote2 do begin_quote2 ++:= cset(e[1]) +every e := !special do begin_special ++:= cset(e[1]) +<<Check that these sets are disjoint>> +interesting := res_word_chars ++ begin_comment1 ++ begin_comment2 ++ + begin_quote2 ++ begin_special +@ +@ The token recognition method used in procedure [[TeXify]] is based on the +assumption that the above sets are mutually disjoint, and that they also do not +intersect the set [[id_chars]]. If this assumption does not hold, the results are +unpredictable. +<<Check that these sets are disjoint>>= +I := begin_comment1 ** begin_comment2 ** begin_quote2 ** begin_special ** id_chars +if *I ~= 0 then stop ("** Pretty-printer problem: the characters in the set ", + image(I), "\n may begin tokens in more than one category!") +@ + +@ Local and global variables for the [[main]]'s. +\enlargethispage*{1cm} +<<Local variables>>= +local line, special_set +<<Global variables>>= +global translation, res_word_chars, id_chars, special, comment1, comment2, quote2 +global interesting, begin_special, begin_comment1, begin_comment2, begin_quote2 +@ + + +@ +\subsection {The procedure TeXify} + +This procedure formats [[@text]] lines in the [[noweb]] file. It is called by +procedure [[filter]]. Note that every \TeX{}ified line is a ``literal'' in +[[noweb]]'s sense. +<<Procedure [[TeXify]]>>= +procedure TeXify(line, p0) + <<Local variables for [[TeXify]]>> + writes("@literal ") + line ? + {if \in_comment1 then + <<Write unbalanced comment text>> + else if \in_comment2 then + <<Write balanced comment text>> + else if \in_quote then + <<Write quoted text>> + else + <<Not inside a comment or quote>> + # Write the remainder of the line, if any. + writes(tab(0)) + } + write() +end +@ +@ +<<Not inside a comment or quote>>= +while writes(tab(upto(interesting))) do + case &pos+1 of { + # To understand the \texttt{&pos+1}, look at Icon's semantics of \texttt{case} and of \texttt{any}. + any(id_chars) : <<Identifier or reserved word>> + any(begin_special) : <<Possible ``special'' token>> + any(begin_comment1) : <<Possible unbalanced comment>> + any(begin_comment2) : <<Possible balanced comment>> + any(begin_quote2) : <<Possible quote>> + default : <<Internal error!>> + } +@ +@ Well, if we got here there's something wrong in the scanning algorithm. [[p0]] is +the position in the line of the source file where the argument [[line]] of [[TeXify]] +begins. +<<Internal error!>>= +stop("\n** Error in pretty-printer procedure TeXify:\n input line ", line_num, + ", column ", p0+&pos-2) +# Note: this is the column in the Emacs sense, i.e.\ the first character is in column 0. +@ + + +@ +\subsubsection {Handling the interesting tokens} + +All identifiers will be matched (and some non-identifiers, such as explicit numeric +constants), but the [[translation]] table defines \TeX\ code for reserved words +only. Matching this larger set allows one to include translations for some +identifiers too. + +As an exercise in understanding Icon's semantics, spend some time figuring out why +saying {\ttfamily writes(if t := translation[token] then t else token)} will +\emph{not} work here\footnote{Hint: consider the case in which no translation is +defined for the token.}. +<<Identifier or reserved word>>= +{token := tab(many(id_chars)) + t := translation[token] + writes(if \t then t else token)} +@ + +@ There are two issues here. Suppose our set [[special]] contains both [[=]] and +[[==]], and it does not contain [[=-]]. What happens when we encounter [[=]]? +First, we have to be sure that this is not the string [[==]]. So (a) we must match +the {\em longest\/} token in [[special]], in case a special token is a prefix of +another special token. Second, we must check that we do not have the string [[=-]], +since this is not a special token. So (b) we must check that a token in [[special]] +is followed by a proper terminating character. + +To ensure (a), [[match(!special)]] will match the longest token if the list +[[special]] is arranged so that longest tokens come first, as noted in +\S\ref{sec:int}. To ensure (b) define the cset [[not_special]]: +<<Global variables>>= +global not_special +<<Detecting the beginning of a token>>= +special_set := '' +every e := !special do special_set ++:= cset(e) +not_special := ~special_set +@ + +@ We will {\em assume\/} that if a token in [[special]] is followed by a character in +[[not_special]] (or the end of the line), then it is a legitimate special token. So +<<Possible ``special'' token>>= +if (token := tab(match(!special)) & (any(not_special) | pos(0))) then + writes(translation[token]) +else + writes(move(1)) +@ + + +@ +\subsubsection {Comments and quotes} + +Procedures [[filter]] and [[TeXify]] interact via the variables [[in_comment]] and +[[in_quote]] in handling comments and quotes. This is because the [[finduses]] and +[[noidx]] filters are language-independent, and so can insert spurious [[@index]] and +[[@xref]] lines in the middle of commented or quoted text of the target language. +While this is merely an annoyance with balanced quotes and comments, it causes a real +problem with unbalanced comments, in that [[TeXify]] cannot detect the end of an +\emph{unbalanced} comment. This must be done by [[filter]], when it encounters a +[[@nl]] line. See \S\ref{sec:filter} for some more details. +<<Global variables>>= +global in_comment1, in_comment2, in_quote +@ + + +@ If we match a token in [[comment1]], we output it and the rest of the line as is, +but in [[\rm]] font. Within a comment, characters special to \TeX\ are active, +e.g. \verb+$x^2$+ will produce $x^2$. A problem with this is that if you comment out +the (C) line \verb+printf("Hi there!\n")+, \TeX\ will complain that [[\n]] is an +undefined control sequence. +<<Possible unbalanced comment>>= +if writes(tab(match(!comment1))) then + {in_comment1 := "yes" + writes("\\begcom{}" || tab(0)) + break} # We let \texttt{filter} detect the end of the comment. +else + writes(move(1)) # The character wasn't the beginning of a comment token. +@ + + +@ If we are at this point, it is not necessarily true that we have found a comment. +For example, in \textsl{Mathematica} comments begin with a [[(]], which may also +appear in [[x+(y+z)]]. The additional complexity comes from the fact the we have to +handle comments extending over many lines. +<<Possible balanced comment>>= +{every c := !comment2 do + # The conjunction is needed here! + {writes(c_open := tab(match(c[1]))) & c_close := c[2] & break} + if \c_open then + {in_comment2 := "yes" + writes("\\begcom{}") + <<Write balanced comment text>>} + else + writes(move(1)) # The character wasn't the beginning of a comment after all. +} +@ +@ Quoted strings may extend over multiple lines. Except for the formatting, we handle +them like balanced comments. +<<Possible quote>>= +{every q := !quote2 do + {writes(q_open := tab(match(q[1]))) & q_close := q[2] & break} + if \q_open then + {in_quote := "yes" + <<Write quoted text>>} + else + writes(move(1)) # The character wasn't the beginning of a quoting token. +} +@ +@ +<<Write unbalanced comment text>>= +writes(tab(0)) +@ +@ +<<Write balanced comment text>>= +{if writes(tab(find(c_close))) then # Comment ends here + {writes("\\endcom{}" || move(*c_close)) + in_comment2 := &null} + else # Comment doesn't close on this line + writes(tab(0)) +} +@ +@ After encountering a quote we write literally, except that we precede every +character special to \TeX\ by a backslash and follow it by an empty group. (This is +necessary for the characters ``\~{}'' and ``\^{}''.) +<<Write quoted text>>= +{q := tab(find(q_close)|0) # $q$ doesn't include the closing quote. + q ? {while writes(tab(upto(TeXspecial))) do writes("\\" || move(1) || "{}") + writes(tab(0))} + if writes(tab(match(q_close))) then # Quote ends on this line + in_quote := &null +} +@ +@ +<<Local variables for [[TeXify]]>>= +local token, c, t, q, c_open +static c_close, q_close, TeXspecial +initial {TeXspecial := '\\${}&#^_%~'} # The cset of characters treated specially by \TeX. +@ + + + +@ +\subsection {Filtering the Input} +\label{sec:filter} + +First we set up the typewriter bold font [[\ttb]], corresponding to cmttb10. Then we +define the macros [[\begcom]] (begin comment) and [[\endcom]]. [[\begcom]] +\begin {itemize} + \item switches to [[\rmfamily]], + \item activates [[$]] by changing its catcode to 3, + \item makes the characters ``\texttt{\^{}}'' and ``[[_]]'' active for superscripts + and subscripts, + \item changes the catcode of the space character to 10. This way comments will be + typeset normally, and not as if [[\obeyspaces]] were active. +\end {itemize} +<<Emit special {\TeX} definitions>>= +write("@literal \\DeclareFontShape{OT1}{cmtt}{bx}{n}{ <-> cmttb10 }{}") +write("@nl") +write("@literal \\def\\ttb{\\bfseries}") +write("@nl") +write("@literal \\def\\begcom{\\begingroup\\rmfamily \\catcode`\\$=3_ + \\catcode`\\^=7 \\catcode`\\_=8 \\catcode`\\ =10}") +write("@nl") +write("@literal \\def\\endcom{\\endgroup}") +write("@nl") +@ + + +@ Procedure [[filter]] is straightforward, except that it interacts with [[TeXify]] +when it comes to comments and quotes. [[line_num]] is used by both. +<<Global variables>>= +global line_num +<<Emit special {\TeX} definitions>>= +line_num := 0 +<<Procedure [[filter]]>>= +procedure filter(line) + static kind # Local and static. + local keyword, rest, p0 + line_num := line_num + 1 # line no. in the input file; used by \texttt{TeXify}. + line ? (keyword := tab(upto(' ')|0) & + rest := if tab(match(" ")) then {p0 := &pos; tab(0)} else &null) + case keyword of { + "@begin" : {rest ? kind := tab(many(&letters)) + write(line)} + "@text" : if \kind == "code" then TeXify(rest,p0) else write(line) + "@nl" : {if \in_comment1 then # This must be an unbalanced comment. + {write("@literal \\endcom{}"); in_comment1 := &null} + write(line)} + "@index" | "@xref" : <<Not if in comment or quote!>> + default : write(line) + } + return +end +@ + +@ Don't output spurious [[@index]] or [[@xref]] lines when in a comment or quote. +([[@index]] is produced by [[finduses]] and [[@xref]] by [[noidx]].) +This works only if the language filter is run {\em before\/} [[noidx]]. +<<Not if in comment or quote!>>= +if /in_comment1 & /in_comment2 & /in_quote then write(line) +@ + + + +@ +\section {To do} +\label{sec:todo} + +We have the following unresolved issue, exemplified by Icon. The current filter +translates the symbol ``\&''as ``$\land$'', even though ``\&'' is {\em not\/} in +Icon's [[special]]. This happens because ``\&'' is in Icon's [[res_word_chars]], and +a translation for it is defined in [[icon_translation_table]]. So when [[TeXify]] +encounters it, it recognizes it as an Icon reserved word, and uses the translation +defined for it. Now if this translation is not wanted, remove ``\&'' from +[[icon_translation_table]] and don't bother me any more. However, if this +translation is ok, we have an inconsistency, in that ``\&'' is not in [[special]]. + +While this is not a real problem, achieving consistency (which may be needed in a +more general case) is not so easy. If we add ``\&'' to [[special]], the check in +$\langle$\textit{Check that these sets are disjoint\/}$\rangle$ will fail. To fix +this, we could +\begin {enumerate} + \item Add a constraint to the recognition of a reserved word: it has to be + a token of length $>1$. + \item Revise the [[case]] structure in $\langle$\textit{Not inside a comment or + quote\/}$\rangle$, as it will no longer work. +\end {enumerate} +We could also consider having a separate translation table for special tokens. +@ + + +@ +\appendix +\section {Index} +\nowebindex + + +\end {document}
\ No newline at end of file diff --git a/web/noweb/contrib/leew/Makefile b/web/noweb/contrib/leew/Makefile new file mode 100644 index 0000000000..975bf88864 --- /dev/null +++ b/web/noweb/contrib/leew/Makefile @@ -0,0 +1,6 @@ +SHELL=/bin/sh +all: +install: +source: +clean: + /bin/rm -f nocond *.dvi *.log *.aux *.toc *.tex *.tex nocond.1 diff --git a/web/noweb/contrib/leew/README b/web/noweb/contrib/leew/README new file mode 100644 index 0000000000..337fb068c9 --- /dev/null +++ b/web/noweb/contrib/leew/README @@ -0,0 +1,12 @@ +Lee Wittenberg has kindly provided his port of noweb to DOS, which +includes executable binaries. It once appeared in the DOS +subdirectory of the standard noweb distribution, but it got superseded +by somebody else's port later on. + +nocond is a slick noweb filter that supports conditional code in a +nicer way than ugly old #ifdef's. + +nobrace looks for unbalanced braces in code chunks. + +pretty-comment uses LaTeX to typeset inline comments... +and it's written in SNOBOL! diff --git a/web/noweb/contrib/leew/custom-code/README.custom-code b/web/noweb/contrib/leew/custom-code/README.custom-code new file mode 100644 index 0000000000..06367f1ceb --- /dev/null +++ b/web/noweb/contrib/leew/custom-code/README.custom-code @@ -0,0 +1,17 @@ +The "custom-code" script is a simple noweb filter that allows for some +very simple typesetting of code chunks. It simply inserts a + + \bgroup\nwcustomcode ... \egroup + +wrapper around everything in code chunks and quoted code. The user +simply defines the \nwcustomcode macro to typeset the code +appropriately. This is primarily useful for programming languages like +Neliac (and, perhaps, APL), which use specialized character sets (and +don't look quite right when rendered in ``standard'' ASCII characters. + +The "example.nw" file demonstrates "custom-code" in action. Simply + + noweave -t4 -delay -index example.nw -filter custom-code >example.tex + +and run the result through LaTeX for a sample of the same code both +with and without the custom typesetting. diff --git a/web/noweb/contrib/leew/custom-code/custom-code b/web/noweb/contrib/leew/custom-code/custom-code new file mode 100755 index 0000000000..4d9ce45104 --- /dev/null +++ b/web/noweb/contrib/leew/custom-code/custom-code @@ -0,0 +1,12 @@ +#!/bin/awk -f +BEGIN { needcontrol = 0; } +/^@begin code / { needcontrol = 1 } +/^@quote$/ { needcontrol = 1 } +/^@text / { if (needcontrol) { + print "@literal \\bgroup\\nwcustomcode{}" + } + needcontrol = 0 + } +/^@end code / { print "@literal \\egroup{}" } +/^@endquote$/ { print "@literal \\egroup{}" } + { print } diff --git a/web/noweb/contrib/leew/custom-code/example.nw b/web/noweb/contrib/leew/custom-code/example.nw new file mode 100644 index 0000000000..c6a0b252f5 --- /dev/null +++ b/web/noweb/contrib/leew/custom-code/example.nw @@ -0,0 +1,101 @@ +% to weave: +% noweave -t4 -delay -index example.nw -filter custom-code >example.tex +% +\documentclass{article} +\usepackage{noweb} +\noweboptions{smallcode} +\topmargin=0pt +\textheight=9in +\def\sub#1{$_{#1}$} +\def\minus{-} +\def\plus{+} +\def\equals{=} +\def\lt{<} +\def\gt{>} +\def\neliac{% + \catcode`_=\active% + \catcode`|=\active% + \catcode`&=\active% + \catcode`*=\active% + \catcode`'=\active% + \catcode`^=\active% + \catcode`-=\active% + \catcode`+=\active% + \catcode`==\active% + \catcode`<=\active% + \catcode`>=\active% + \catcode`/=\active% +} +\bgroup\neliac +\global\def\nwcustomcode{\neliac% + \global\def_##1{\sub{##1}}% + \global\def|{$\cup$}% + \global\def&{$\cap$}% + \global\def*{$\times$}% + \global\def'{$\vert$}% + \global\def^{$\uparrow$}% + \global\def+{$\plus$}% + \global\def={$\equals$}% + \global\def/##1{\(\ifx##1=\ne\else\slash##1\fi\)}% + \global\def<##1{\(\ifx##1=\le\else\lt##1\fi\)}% + \global\def>##1{\(\ifx##1=\ge\else\gt##1\fi\)}% + \global\def-##1{\(\ifx##1>\to\else\minus##1\fi\)}% +} +\egroup + +\begin{document} +Here is some actual code from the original Neliac compiler for the +Univac M-460 ``Countess'' computer (written in Neliac, of course): +<<*>>= +DEBUG SCAN: +i = 0: standard compiling location -> i; ; +j = 0: obj prog std last address -> j; ; +i = i(1)j{ [i] = straight jump function | [i] = return jump function: + fault 9. ; [i](15 -> 29) = 61000_8 & [i](0 -> 14) - bias -> k /= 0: + { [k] = 0 | [k] = straight jump function: fault 10. ; }; ; +l'oop exit: }. check key sets, turn off flex, clear indices, +key[2] /= 0: dump name lists and stop. exit. +F'AULT 9: +start flex, carriage return upper case, 69 -> lower loop limit, +72 -> upper loop limit, dump a title, +n = 177_8(1)0{ undefined name location[n] = i: + write undefined name, continue. ; }, +C'ONTINUE: write address, loop exit. +F'AULT 10: +start flex, carriage return upper case, +77 -> lower loop limit, 82 -> upper loop limit, dump a title, +n = 777_8(1)0{ name address[n] - bias = k: write name, go on. ; }, +k -> upper dump buffer[1], dump five number, +G'O ON: write address, loop exit. +W'RITE ADDRESS: +{ 73 -> lower loop limit, 76 -> upper loop limit, dump a title, +i -> upper dump buffer[1], dump five numbers, }. e'xit: . . +@ +And here is the very same code without the \verb"custom-code" +typesetting:\let\nwcustomcode=\relax +<<*>>= +DEBUG SCAN: +i = 0: standard compiling location -> i; ; +j = 0: obj prog std last address -> j; ; +i = i(1)j{ [i] = straight jump function | [i] = return jump function: + fault 9. ; [i](15 -> 29) = 61000_8 & [i](0 -> 14) - bias -> k /= 0: + { [k] = 0 | [k] = straight jump function: fault 10. ; }; ; +l'oop exit: }. check key sets, turn off flex, clear indices, +key[2] /= 0: dump name lists and stop. exit. +F'AULT 9: +start flex, carriage return upper case, 69 -> lower loop limit, +72 -> upper loop limit, dump a title, +n = 177_8(1)0{ undefined name location[n] = i: + write undefined name, continue. ; }, +C'ONTINUE: write address, loop exit. +F'AULT 10: +start flex, carriage return upper case, +77 -> lower loop limit, 82 -> upper loop limit, dump a title, +n = 777_8(1)0{ name address[n] - bias = k: write name, go on. ; }, +k -> upper dump buffer[1], dump five number, +G'O ON: write address, loop exit. +W'RITE ADDRESS: +{ 73 -> lower loop limit, 76 -> upper loop limit, dump a title, +i -> upper dump buffer[1], dump five numbers, }. e'xit: . . +@ \relax +\end{document} diff --git a/web/noweb/contrib/leew/custom-code/example.pdf b/web/noweb/contrib/leew/custom-code/example.pdf Binary files differnew file mode 100644 index 0000000000..51145950df --- /dev/null +++ b/web/noweb/contrib/leew/custom-code/example.pdf diff --git a/web/noweb/contrib/leew/custom-code/example.tex b/web/noweb/contrib/leew/custom-code/example.tex new file mode 100644 index 0000000000..b039a96f3f --- /dev/null +++ b/web/noweb/contrib/leew/custom-code/example.tex @@ -0,0 +1,105 @@ +% to weave:% ===> this file was generated automatically by noweave --- better not edit it +% noweave -t4 -delay -index example.nw -filter custom-code >example.tex +% +\documentclass{article} +\usepackage{noweb} +\noweboptions{smallcode} +\topmargin=0pt +\textheight=9in +\def\sub#1{$_{#1}$} +\def\minus{-} +\def\plus{+} +\def\equals{=} +\def\lt{<} +\def\gt{>} +\def\neliac{% + \catcode`_=\active% + \catcode`|=\active% + \catcode`&=\active% + \catcode`*=\active% + \catcode`'=\active% + \catcode`^=\active% + \catcode`-=\active% + \catcode`+=\active% + \catcode`==\active% + \catcode`<=\active% + \catcode`>=\active% + \catcode`/=\active% +} +\bgroup\neliac +\global\def\nwcustomcode{\neliac% + \global\def_##1{\sub{##1}}% + \global\def|{$\cup$}% + \global\def&{$\cap$}% + \global\def*{$\times$}% + \global\def'{$\vert$}% + \global\def^{$\uparrow$}% + \global\def+{$\plus$}% + \global\def={$\equals$}% + \global\def/##1{\(\ifx##1=\ne\else\slash##1\fi\)}% + \global\def<##1{\(\ifx##1=\le\else\lt##1\fi\)}% + \global\def>##1{\(\ifx##1=\ge\else\gt##1\fi\)}% + \global\def-##1{\(\ifx##1>\to\else\minus##1\fi\)}% +} +\egroup + +\begin{document} +Here is some actual code from the original Neliac compiler for the +Univac M-460 ``Countess'' computer (written in Neliac, of course): +\nwfilename{example.nw}\nwbegincode{1}\sublabel{NW36h5rr-1p0Y9w-1}\nwmargintag{{\nwtagstyle{}\subpageref{NW36h5rr-1p0Y9w-1}}}\moddef{*~{\nwtagstyle{}\subpageref{NW36h5rr-1p0Y9w-1}}}\endmoddef\nwstartdeflinemarkup\nwprevnextdefs{\relax}{NW36h5rr-1p0Y9w-2}\nwenddeflinemarkup +\bgroup\nwcustomcode{}DEBUG SCAN: +i = 0: standard compiling location -> i; ; +j = 0: obj prog std last address -> j; ; +i = i(1)j\{ [i] = straight jump function | [i] = return jump function: + fault 9. ; [i](15 -> 29) = 61000_8 & [i](0 -> 14) - bias -> k /= 0: + \{ [k] = 0 | [k] = straight jump function: fault 10. ; \}; ; +l'oop exit: \}. check key sets, turn off flex, clear indices, +key[2] /= 0: dump name lists and stop. exit. +F'AULT 9: +start flex, carriage return upper case, 69 -> lower loop limit, +72 -> upper loop limit, dump a title, +n = 177_8(1)0\{ undefined name location[n] = i: + write undefined name, continue. ; \}, +C'ONTINUE: write address, loop exit. +F'AULT 10: +start flex, carriage return upper case, +77 -> lower loop limit, 82 -> upper loop limit, dump a title, +n = 777_8(1)0\{ name address[n] - bias = k: write name, go on. ; \}, +k -> upper dump buffer[1], dump five number, +G'O ON: write address, loop exit. +W'RITE ADDRESS: +\{ 73 -> lower loop limit, 76 -> upper loop limit, dump a title, +i -> upper dump buffer[1], dump five numbers, \}. e'xit: . . +\egroup{}\nwalsodefined{\\{NW36h5rr-1p0Y9w-2}}\nwnotused{*}\nwendcode{}\nwbegindocs{2}\nwdocspar +And here is the very same code without the \verb"custom-code" +typesetting:\let\nwcustomcode=\relax +\nwenddocs{}\nwbegincode{3}\sublabel{NW36h5rr-1p0Y9w-2}\nwmargintag{{\nwtagstyle{}\subpageref{NW36h5rr-1p0Y9w-2}}}\moddef{*~{\nwtagstyle{}\subpageref{NW36h5rr-1p0Y9w-1}}}\plusendmoddef\nwstartdeflinemarkup\nwprevnextdefs{NW36h5rr-1p0Y9w-1}{\relax}\nwenddeflinemarkup +\bgroup\nwcustomcode{}DEBUG SCAN: +i = 0: standard compiling location -> i; ; +j = 0: obj prog std last address -> j; ; +i = i(1)j\{ [i] = straight jump function | [i] = return jump function: + fault 9. ; [i](15 -> 29) = 61000_8 & [i](0 -> 14) - bias -> k /= 0: + \{ [k] = 0 | [k] = straight jump function: fault 10. ; \}; ; +l'oop exit: \}. check key sets, turn off flex, clear indices, +key[2] /= 0: dump name lists and stop. exit. +F'AULT 9: +start flex, carriage return upper case, 69 -> lower loop limit, +72 -> upper loop limit, dump a title, +n = 177_8(1)0\{ undefined name location[n] = i: + write undefined name, continue. ; \}, +C'ONTINUE: write address, loop exit. +F'AULT 10: +start flex, carriage return upper case, +77 -> lower loop limit, 82 -> upper loop limit, dump a title, +n = 777_8(1)0\{ name address[n] - bias = k: write name, go on. ; \}, +k -> upper dump buffer[1], dump five number, +G'O ON: write address, loop exit. +W'RITE ADDRESS: +\{ 73 -> lower loop limit, 76 -> upper loop limit, dump a title, +i -> upper dump buffer[1], dump five numbers, \}. e'xit: . . +\egroup{}\nwendcode{} + +\nwixlogsorted{c}{{*}{NW36h5rr-1p0Y9w-1}{\nwixd{NW36h5rr-1p0Y9w-1}\nwixd{NW36h5rr-1p0Y9w-2}}}% +\nwbegindocs{4}\relax +\end{document} +\nwenddocs{} diff --git a/web/noweb/contrib/leew/custom-code/n b/web/noweb/contrib/leew/custom-code/n new file mode 100644 index 0000000000..b2016f1622 --- /dev/null +++ b/web/noweb/contrib/leew/custom-code/n @@ -0,0 +1,20 @@ +Date: Wed, 13 May 2009 19:32:13 -0400 +From: Alec <alec@deviant-logic.net> +To: Norman Ramsey <nr@cs.tufts.edu> +Subject: Re: A couple links + +Actually, the pain and suffering from the python article +turns out to be found at his followup post on tail calls on +his regular blog: +http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html + +It's truly thrilling. + +-A + +On Wed, May 6, 2009 at 9:44 PM, Norman Ramsey +<nr@cs.tufts.edu> wrote: +> Thanks! +> +> N +> diff --git a/web/noweb/contrib/leew/email b/web/noweb/contrib/leew/email new file mode 100644 index 0000000000..e6186cf003 --- /dev/null +++ b/web/noweb/contrib/leew/email @@ -0,0 +1,2 @@ +Lee Wittenberg <leew@alumni.stanford.edu> + diff --git a/web/noweb/contrib/leew/nobrace.nw b/web/noweb/contrib/leew/nobrace.nw new file mode 100644 index 0000000000..1190f8a291 --- /dev/null +++ b/web/noweb/contrib/leew/nobrace.nw @@ -0,0 +1,321 @@ +% +% to tangle: +% notangle -t4 -L nobrace.nw > nobrace.icn +% to weave: +% noweave -t4 -delay -autodefs icon -index nobrace.nw > nobrace.tex +% to create the manpage: +% notangle -Rnobrace.1 nobrace.nw > nobrace.1 +% +\documentclass{article} + +\usepackage{noweb,multicol} +\noweboptions{longchunks} % noweave -option longchunks would be + % better, but won't work with -delay, and + % we need stuff before \begin{document} + +% show spaces in string constants +\global\let\xsetup=\setupcode +\bgroup + \catcode`\"=\active\gdef\setupcode{\xsetup + \catcode`\"=\active\def"##1"{\char`\"\xxx{##1}\char`\"}}% +\egroup +\bgroup + \catcode`\ =\active\gdef\xxx#1{{\catcode`\ =\active\chardef ='40#1}}% +\egroup + +\def\noweb/{\texttt{noweb}} +\def\nobrace/{\texttt{nobrace}} +\def\notangle/{\texttt{notangle}} +\def\noweave/{\texttt{noweave}} + +\title {A Filter For Matching Braces in \noweb/ Programs% + \thanks{Copyright \copyright~1996 by Lee Wittenberg. + Although this software is freely distributable, it is not in + the public domain. It is provided ``as is'' and without any + express or implied warranties, including, without limitation, + the implied warranties of merchantability and fitness for a + particular purpose.}} +\author {Lee Wittenberg\\\tt leew@pilot.njin.net} + +\pagestyle{noweb} +\begin{document} +\maketitle +@ \iffalse +% +% We don't want a troff man page woven by TeX, do we? +% +<<nobrace.1>>= +.TH NOBRACE 1 "local 4/9/96" +.SH NAME +nobrace \- check noweb chunks for brace mismatches +.SH SYNOPSIS +.B nobrace +[ brace-pair ... ] +.SH DESCRIPTION +.I nobrace +is a filter designed to work with +.I notangle(1) +or +.I noweave(1) +to ensure that the braces in each code chunk are balanced. +.I nobrace +generates warning messages on the standard error stream for each +chunk with unbalanced braces. + +If no brace pairs are specified on the command line, +.I nobrace +will check parentheses, square brackets, and curly braces. +.SH BUGS +.I nobrace +is naive about braces in string constants, comments, etc. +.PP +No provision is made for multiple character braces, so C-style +comments cannot be checked (nor can Algol-like \fBbegin\fP's and +\fBend\fP's). +.PP +This manual page would be better if its author knew more about troff +and the -man macros. +.SH SEE ALSO +.I notangle(1), noweave(1) +.SH AUTHOR +Lee Wittenberg. Internet address: \fBleew@pilot.njin.net\fP +@ \fi +@ +\section{Introduction} +Many literate programming authorities +consider it good practice for each code chunk definition to be syntactically +and semantically complete in itself, with each chunk use representing +a complete entity (statement, expression, etc.). To be complete, the +braces in a chunk must be balanced. This web provides a +\noweb/ filter that warns the user about mismatched braces in chunks. +@ +\section{The Program} +The main program reads and echoes each +line of the standard input (so it will be invisible in the pipeline), +processing only relevant markup lines. +<<*>>= +procedure main(args) + <<Initialization>> + while inputline := read() do { + write(inputline) + inputline ? { + <<Process relevant markup lines>> + {} # for final else + } + } +end +@ +Each command-line +argument is taken to be a pair of braces, the open brace first. We +construct two tables, [[pair]], and [[delta]], which are used to keep +the brace balancing straight and separate (we don't want `[[{]]' +matching `[[)]]'). We use the [[braces]] cset for scanning the text +lines in code chunks. + +If no command line arguments are specified, we assume `\verb"() [] {}"'. +<<Initialization>>= +pair := table("") +delta := table(0) +braces := '' +if *args = 0 then + args := ["()", "[]", "{}"] +every p := !args do { + braces ++:= p + every pair[!p] := p + delta[p[1]] := +1 + delta[p[2]] := -1 +} +@ %def pair delta braces +@ +\section{Relevant Markup} +Our \emph{raison d'\^etre} is to match braces in code chunks. +Each [[@text]] line in a code chunk is scanned for braces, which we +attempt to balance. +<<Process relevant markup lines>>= +if ="@text " then { + if \code then { + line := tab(0) + every p := upto(braces, line) do { + b := line[p] + <<Balance brace [[b]] at [[p]]>> + } + } +} else +@ +Whenever we enter a code chunk, we need to set our [[code]] flag, +<<Process relevant markup lines>>= +if ="@begin code " then { + code := 1 +} else +@ \noindent +and reset it whenever we leave. Whenever a code chunk ends (thus +ending a definition), we also need to check for any remaining umatched +braces in that chunk. +<<Process relevant markup lines>>= +if ="@end code " then { + code := &null + <<Check for unmatched braces>> +} else +@ +All webs start with a text chunk, not code. +<<Initialization>>= +code := &null +@ %def code +@ +The variables [[curr_chunkname]], [[curr_filename]], and [[curr_line]] +help keep track of where mismatches are found. +@ \noindent +We initialize them to ``safe'' values, just in case. +<<Initialization>>= +curr_line := 1 +curr_filename := "Standard Input" +curr_chunkname := "***Unknown Chunk***" +@ %def curr_filename curr_line curr_chunkname +@ +Newlines simply increase the [[curr_line]] count. +<<Process relevant markup lines>>= +if ="@nl" & pos(0) then { + curr_line +:= 1 +} else +@ +Whenever we get a new file name, we make note of it. +<<Process relevant markup lines>>= +if ="@file " then { + curr_filename := tab(0) + curr_line := 1 +} else +@ +The [[@line]] directive can be used to adjust the current line +number in a source file. We hear and obey. +<<Process relevant markup lines>>= +if ="@line " then { + curr_line := integer(tab(0)) +} else +@ +New chunk definitions give us a new [[curr_chunkname]]. +``[[<<Check for unmatched braces>>]]'' +is here because it is not illegal for a single code chunk to have more +than one definition. None of the standard tools produce anything like +that, but we allow for this (remote) possibility in the interests of +defensive programming. +<<Process relevant markup lines>>= +if ="@defn " then { + <<Check for unmatched braces>> + curr_chunkname := tab(0) +} else +@ +\section{Dealing With Braces} +Whenever we see an opening brace, we push its location +on the appropriate stack, +and when we see a closing brace, we pop the +corresponding opening brace's location +(we use [[pull]] instead of [[pop]] to keep the list sorted; we use +list concatenation instead of [[put]] because of Icon's table +initialization semantics). + +If there is no opening brace to pop, +we've found a mismatched closing brace. +We add each line containing a brace to the [[error_line]] table +because it may be needed for a warning message. +<<Balance brace [[b]] at [[p]]>>= +if delta[b] > 0 then { +# put(bstack[pair[b]], loc(curr_line, p)) + bstack[pair[b]] |||:= [loc(curr_line, p)] +} else { + pull(bstack[pair[b]]) | + {<<Note brace error at [[curr_line]], [[p]]>>} +} +error_line[curr_line] := line +@ +A brace's location is a line number and a column position in that +line. +<<*>>= +record loc(line,col) +@ +We keep all brace errors in a sorted list, [[error_list]]. +<<Note brace error at [[curr_line]], [[p]]>>= +put(error_list, loc(curr_line, p)) +@ +The brace stacks are initially empty, as is the error list. +<<Initialization>>= +bstack := table([]) +error_list := [] +error_line := table("") +@ %def bstack error_list error_line +@ +If either the error list or any of the brace stacks are not empty, we +have mismatched braces +<<Check for unmatched braces>>= +if (*error_list | *!bstack) ~= 0 then { + <<Generate warning messages>> +} +@ +We merge [[error_list]] with all the brace stacks to create a single +(sorted) list of mismatched brace locations. We write a single +warning message for the chunk, followed by all the lines with +mismatched braces. When we're finished, we clear the error list and +the brace stacks for the next chunk definition. +<<Generate warning messages>>= +every error_list := merge(!bstack, error_list) +write(&errout, "Warning: Mismatched braces in @<<", + curr_chunkname, ">>", + if curr_filename ~== "" + then " (" || curr_filename || ")" + else "", + ":") +<<Display all relevant lines with mismatched braces marked>> +error_list := [] +bstack := table([]) +@ +For each line represented in the error list, we print the line with a +marker line under it. We use the `\verb"^"' character to mark the +position of each mismatched brace. Each line is prefixed with its +line number. +<<Display all relevant lines with mismatched braces marked>>= +lineno := 0; +every e := !error_list do { + if e.line ~= lineno then { + if lineno ~=0 then + write(&errout, marker) + lineno := e.line + write(&errout, right(e.line || ": ", 10), + error_line[e.line]) + marker := repl(" ", 10) + } + marker := left(marker, e.col+10-1) || "^" +} +write(&errout, marker) +@ +The [[merge]] procedure merges two sorted lists of [[loc]]'s. We plow +through both lists more or less in parallel, adding the earliest +brace location to the result list. When [[a]] is exhausted, +the remaining elements of [[b]] are concatenated to the result. Thus, +the longer list should always be passed as the second parameter if +possible. +<<*>>= +procedure merge(a, b) + local i, j, result + result := [] + i := j := 1 + while a[i] do { + if a[i].line > b[j].line | + (a[i].line = b[j].line & a[i].col > b[j].col) then { + put(result, b[j]) + j +:= 1 + } else { + put(result, a[i]) + i +:= 1 + } + } + return result ||| b[j:0] +end +@ +\appendix +\section{Chunk Index} +\nowebchunks +\section{Identifier Index} +\begin{multicols}{2} +\nowebindex +\end{multicols} +@ +\end{document} diff --git a/web/noweb/contrib/leew/nocond.nw b/web/noweb/contrib/leew/nocond.nw new file mode 100644 index 0000000000..50eb35bc64 --- /dev/null +++ b/web/noweb/contrib/leew/nocond.nw @@ -0,0 +1,375 @@ +% +% $Header: d:/noweb/work/RCS/nocond.nw%v 1.4 1995/07/29 17:14:49 LEEW Exp LEEW $ +% $Workfile$ +% +% to tangle the sed script +% notangle -t4 -R"sed script" nocond.nw > nocond +% to tangle the shell script: +% notangle -t4 -R"shell script" nocond.nw > nocond +% to tangle the awk program +% notangle -t4 -Rnocond.awk nocond.nw > nocond.awk +% (use -filter "nocond MKS AWKC" if necessary) +% to weave: +% noweave -t4 -delay -x nocond.nw > nocond.tex +% +\documentstyle[noweb,twoside]{article} +\noweboptions{longchunks} +\let\nwnotused=\nwoutput +\oddsidemargin=63pt % standard LaTeX margins don't work well for 2-sided webs +\evensidemargin=63pt + +\ifx\LaTeXe\undefined\def\LaTeXe{\LaTeX2e}\fi % for old installations +\def\noweb/{{\tt noweb}} +\def\nocond/{{\tt nocond}} +\def\notangle/{{\tt notangle}} +\def\noweave/{{\tt noweave}} + +\title {A Filter For Conditional Tangling in \noweb/% + \thanks{Copyright \copyright~1994, 1995 by Lee Wittenberg. + Although this software is freely distributable, it is not in + the public domain. It is provided ``as is'' and without any + express or implied warranties, including, without limitation, + the implied warranties of merchantability and fitness for a + particular purpose.}} +\author {Lee Wittenberg\\\tt leew@pilot.njin.net} + +%\input{nocondmac.tex} +\bgroup +\catcode`\@=11 +\global\let\nc@LA=\LA +\gdef\nocondmark#1)){\/{\bf[\negthinspace[#1]\negthinspace]}}% +\global\let\nc@notused=\nwnotused +\global\let\nc@output=\nwoutput +\gdef\nc@rootchunk#1{\nwcodecomment{\nocondrootnote}% + \global\let\nwnotused=\nc@notused + \global\let\nwoutput=\nc@output +}% +\gdef\nocondrootnote{Conditional definition.}% +% for noweb 2.6 (bug, since fixed?) +\global\@namedef{r@???}{{0}{\nocondxref}} +% for noweb 2.5: +\global\@namedef{r@nw@notdef}{{0}{\nocondxref}} +\gdef\nocondxref{(conditional)} +\global\let\nc@nwixlog=\nwixlogsorted +\gdef\nwixlogsorted#1#2{ +\ifx#1c% + \immediate\write\@auxout{\string\bgroup\string\catcode`\string\(=\string\active} +\fi + \nc@nwixlog{#1}{#2} +\ifx#1c% + \immediate\write\@auxout{\string\egroup} +\fi}% +\catcode`\(=\active +\gdef\LA{\nc@LA + \catcode`\(=\active + \def(##1{\ifx##1(\global\let\nwnotused=\nc@rootchunk + \global\let\nwoutput=\nc@rootchunk + \nocondmark + \else\char`\(##1\fi}% +}% +\egroup +\pagestyle{noweb} +\begin{document} +\maketitle +@ \iffalse +% +% We don't want a troff man page woven by TeX, do we? +% +<<nocond.1>>= +.TH NOCOND 1 "local 8/1/94" +.SH NAME +nocond \- provide noweb with conditional tangling +.SH SYNOPSIS +.B nocond +version +.br +\fBawk -f nocond.awk\fP version +.SH DESCRIPTION +.I nocond +is a filter designed to work with +.I notangle(1) +to provide it with a simple +conditional capability. Chunk definitions may be +marked as conditional by including a version name +wrapped in double parentheses as part of the chunk name. +.PP +.I nocond +concatenates its command line arguments +(with a single space between each argument) to form +a version name, and removes matching conditional marks +from chunk definition names so +.I notangle(1) +will include the chunks as part of the appropriate +definition. +.PP +.I nocond +also provides a file of TeX macros, \fInocondmac.tex\fP, which +will nicely typeset conditional chunk names. +.SH EXAMPLE +Suppose that a Pascal web (\fIpgm.nw\fP) uses the chunk +.IP +\fB@<<\fPOpen the output file\fB@>>\fP +.PP +The author can provide multiple definitions of this chunk: +.IP +\fB@<<\fPOpen the output file ((UCSD Pascal))\fB@>>=\fP +.nf +REWRITE(outfile, 'XYZ.DAT'); +\fB@<<\fPOpen the output file ((Turbo Pascal))\fB@>>=\fP +ASSIGN(outfile, 'XYZ.DAT'); +REWRITE(outfile); +.fi +.PP +To tangle the UCSD Pascal version, the command line +.IP +notangle -filter "nocond UCSD Pascal" pgm.nw > pgm.pas +.PP +will suffice. The Turbo Pascal version can be tangled +similarly. +.SH SEE ALSO +.I notangle(1) +.SH AUTHOR +Lee Wittenberg. Internet address: \fBleew@pilot.njin.net\fP +@ \fi +@ +\section{Introduction} +This program is a very simple filter that provides \notangle/ +with a simple conditional capability. It should be +written in {\tt sed}, but non-Unix versions +of that venerable utility are not as readily available as they +should be, so we are using Awk instead (however, see +section~\ref{sed script}). + +\section{The Awk Program} +The Awk program simply passes all its input lines to the +standard output. However, when it encounters a chunk +definition name, it first removes any conditional marks that +match the version specified on the command line. +<<nocond.awk>>= +<<Version control info>> +BEGIN{ + <<System-dependent initialization>> + <<Initialization>> +} +<<Remove desired conditional marks from any chunk definition names>> +{print} +<<Version control info>> +@ +Chunk definition names are prefixed with the markup code +`\verb*"@defn "', and [[gsub]] is just made for this kind of +work. +<<Remove desired conditional marks from any chunk definition names>>= +/^@defn / { gsub(pattern, "", $0) } +@ +We want to remove marks surrounded by `\verb"(("' and +`\verb"))"'. We need the backslashes in the pattern so +Awk doesn't treat the parentheses as grouping symbols. +<<Initialization>>= +<<Use [[ARGV]] to determine the [[version]] desired>> +pattern = " *\(\(" version "\)\)" +@ +Some command processors are not very friendly about dealing +with command line arguments containing spaces, so rather than +require the version name to be supplied as a single argument, +we treat all the arguments as a single, multi\-word one (with +single spaces between the words). We then set [[ARGC]] to~1 to +prevent Awk from trying to re-use the arguments as filenames. +<<Use [[ARGV]] to determine the [[version]] desired>>= +version = ARGV[1] +for (i = 2; i < ARGC; i++) { + version = version " " ARGV[i] +} +ARGC = 1 +@ +\subsection{System Dependencies} +The MKS Awk compiler tends to get confused about command line +arguments, even though the interpreter has no problems. The +following kludge seems to take care of it (don't ask): +<<System-dependent initialization ((MKS AWKC))>>= +ARGV[1] +@ +It's likely that other system-dependencies will arise as +\nocond/ is tried with other versions of Awk. A bit +depressing, but that's what the tool was designed for, and it's +kind of nice to use it to implement itself. Since every Awk +won't require special initialization, we provide a null chunk +to avoid ``undefined chunk'' complaints from \notangle/. +<<System-dependent initialization>>= +@ +\section{The Shell Script} +Unix users can use the following shell script as a \notangle/ +filter: +<<shell script>>= +nawk '<<nocond.awk>>' +@ +\section{A {\tt sed} Script} +\label{sed script}. +{\sc Gnu} Awk, running under Linux, doesn't seem amenable to any +patches that will make the above Awk program work correctly (the +[[gsub]] function seems to be a sore spot in many Awk implementations). +A {\tt sed} script, therefore, seems to be a necessity. The following +does the trick. +<<sed script>>= +<<Version control info>> +sed "/^@defn/s/ *(($*))//" +@ +\section{Weaving a Conditional Web} +Some people think the double parentheses don't look very +good in woven output, and that the version name should stand +out a bit from the chunk name. We provide the macro file +\verb"nocondmac.tex" for those with such beliefs. These macros +should be usable both in plain \TeX\ and \LaTeX, but have only +been tested with the latter. They seem to work okay in \LaTeXe{} +(in both native and compatibility modes), as well. + +We simply redefine the meaning of \noweb/'s [[\LA]] macro to +make `\verb"("' an active character that typesets stuff in +{\tt ((~$\ldots$~))} nicely and leaves other parentheses alone. +As long as [[\LA]] exists and contains a \verb"\bgroup" this +ought to work. +<<nocondmac.tex>>= +\bgroup +\catcode`\@=11 +\global\let\nc@LA=\LA +<<Useful macros>> +<<Make `[[(]]' active>> +\gdef\LA{\nc@LA + <<Make `[[(]]' active>> + \def(##1{\ifx##1(<<Adjust root chunk footnote>>\nocondmark + \else\char`\(##1\fi}% +}% +\egroup +<<Make `[[(]]' active>>= +\catcode`\(=\active +@ +The real work will be done by [[\nocondmark]]. This is the +only macro that should be changed if you want to adjust the way +conditionals are typeset. +<<Useful macros>>= +\gdef\nocondmark#1)){\/{\bf[\negthinspace[#1]\negthinspace]}}% +@ +In \LaTeX\ webs, +the cross-reference footnotes for root chunks are generated by +[[\nwnotused]] or [[\nwoutput]], depending on whether the woven +output was generated with the \notangle/ or \noweb/ command. +We note the original definitions, but change them to +print `Conditional definition.' when a chunk name includes +{\tt ((~$\ldots$~))}. +<<Useful macros>>= +\ifx\nwnotused\undefined\else + \global\let\nc@notused=\nwnotused +\fi +\ifx\nwoutput\undefined\else + \global\let\nc@output=\nwoutput +\fi +<<Adjust root chunk footnote>>= +\ifx\nc@rootchunk\undefined\else +\global\let\nwnotused=\nc@rootchunk +\global\let\nwoutput=\nc@rootchunk +\fi +@ +The macro [[\nc@rootchunk]] is defined so that it resets +[[\nwnotused]] and [[\nwoutput]] when it's finished, so that +real root chunks will have the proper footnote. We use +[[\nocondrootnote]] so that the conditional footnote can +easily be customized. +<<Useful macros>>= +\ifx\nwnotused\undefined\else + \ifx\nwoutput\undefined\else + \gdef\nc@rootchunk#1{\nwcodecomment{\nocondrootnote}% + \global\let\nwnotused=\nc@notused + \global\let\nwoutput=\nc@output + }% + \gdef\nocondrootnote{Conditional definition.}% +\fi\fi +@ +In a web with conditional definitions, chunks that appear to be +undefined are actually conditionally defined, so we change the +`never defined' message to a more meaningful `conditional'. +<<Useful macros>>= +\ifx\documentstyle\undefined\else % LaTeX only +% noweb 2.5: +\global\@namedef{r@nw@notdef}{{0}{\nocondxref}} +% noweb 2.6: (bug, since fixed?) +\global\@namedef{r@???}{{0}{\nocondxref}} +\gdef\nocondxref{(conditional)} +\fi +@ +The chunk index is a bit of a problem because \TeX\ assigns +catcodes when a token is first read, but the chunk index is +read in as part of the \verb".aux" file, when `\verb"("' is not +an active character. We fix [[\nwixlogsorted]] so it will +change the catcode of `\verb"("' temporarily for chunk index +info in the \verb".aux" file. +<<Useful macros>>= +\ifx\nwixlogsorted\undefined\else + \global\let\nc@nwixlog=\nwixlogsorted + \gdef\nwixlogsorted#1#2{ + \ifx#1c + \immediate\write\@auxout{\string\bgroup + \string\catcode`\string\(=\string\active}% + \fi + \nc@nwixlog{#1}{#2} + \ifx#1c + \immediate\write\@auxout{\string\egroup}% + \fi + }% +\fi +@ +On the other hand, all we need do for +[[\nowebchunks@external]] is to set the catcode. Note +that the \verb"externalindex" option must be set {\em after\/} +executing \verb"\input nocondmac.tex" for things to work +properly. +<<Useful macros>>= +\ifx\nowebchunks@external\undefined\else + \global\let\nc@chunks@external=\nowebchunks@external + \gdef\nowebchunks@external{% + \bgroup + <<Make `[[(]]' active>> + \nc@chunks@external + \egroup + }% +\fi +@ +\appendix +\section {Language and Version Control Tools} +This puts revision information in the program so we can make +sure things don't get ``out of sync.'' +<<Version control info>>= +# $Header: d:/noweb/work/RCS/nocond.nw%v 1.4 1995/07/29 17:14:49 LEEW Exp LEEW $ +@ +\section {Chunk Index} +\nowebchunks +%\twocolumn[\section{Identifier Index}] % no point, really, for this web +%\nowebindex +@ +\end{document} +% $Log: nocond.nw%v $ +% Revision 1.4 1995/07/29 17:14:49 LEEW +% Added sed script; minor cosmetic changes +% +% Revision 1.3 1994/09/11 18:06:26 LEEW +% Fixed macros for noweb 2.6c +% +% Revision 1.2 1994/08/05 20:46:48 LEEW +% Added macros to typeset chunk index correctly. +% +% Revision 1.1 1994/08/01 14:05:33 LEEW +% Changed manpage to troff format. +% Spiffed up nocondmac macros. +% Removed non-standard macro packages. +% +% Revision 1.0 1994/06/20 17:47:55 LEEW +% First public version. +% +% Revision 0.3 1994/06/20 17:31:49 LEEW +% Added TeX macros +% +% Revision 0.2 1994/06/20 16:36:59 LEEW +% Awk script complete +% +% Revision 0.1 1994/06/20 14:54:49 LEEW +% Manpage only. +% +% $Header: d:/noweb/work/RCS/nocond.nw%v 1.4 1995/07/29 17:14:49 LEEW Exp LEEW $ diff --git a/web/noweb/contrib/leew/strhack.nw b/web/noweb/contrib/leew/strhack.nw new file mode 100644 index 0000000000..4b0e3fff9d --- /dev/null +++ b/web/noweb/contrib/leew/strhack.nw @@ -0,0 +1,32 @@ +\documentstyle[11pt,noweb]{article} +\noweboptions{longchunks,noidentxref,smallcode} +\pagestyle{noweb} + +\def\noweb/{{\tt noweb}} + +\title{A Hack for Typesetting Strings in \noweb/\thanks{This +code is hereby placed in the public domain.}} +\author{Lee Wittenberg\\Kean College of New Jersey\\Union, NJ +07083\\\tt leew@pilot.njin.net} + +\begin{document} +\maketitle + +The following macros adjust things so that \noweb/ will use +``visible spaces'' in double-quoted strings within code chunks. +The same effect can be +achieved for single-quoted strings by replacing each occurrence +of `[["]]', below, with `[[']]'. +It doesn't work within \verb"[["~\ldots~\verb"]]" (although I +can't figure out why). +<<*>>= +\global\let\xsetup=\setupcode +\bgroup + \catcode`\"=\active\gdef\setupcode{\xsetup + \catcode`\"=\active\def"##1"{\char`\"\xxx{##1}\char`\"}}% +\egroup +\bgroup + \catcode`\ =\active\gdef\xxx#1{{\catcode`\ =\active\chardef ='40#1}}% +\egroup +@ +\end{document} diff --git a/web/noweb/contrib/leyn/README b/web/noweb/contrib/leyn/README new file mode 100644 index 0000000000..79d24b3a3b --- /dev/null +++ b/web/noweb/contrib/leyn/README @@ -0,0 +1,23 @@ +ttroots +======= + +-Allows underscores in root chunks that are written to disk. +-All root chunks are printed out in the LaTeX document as + upright verbatim names. + + +notangleall +=========== + +-creates all required directories for the code chunks +-tangles all root chunks (as noweb -t) +-makes all scripts mared executable. The marking is done + as follows: + %unx chmod +x chunk_name + <<chunk_name>>= + ... + @ + + Can also be used for other UNIX commands that should be + executed after the tangling. + diff --git a/web/noweb/contrib/leyn/email b/web/noweb/contrib/leyn/email new file mode 100644 index 0000000000..f330ba5499 --- /dev/null +++ b/web/noweb/contrib/leyn/email @@ -0,0 +1 @@ +Francky.Leyn@esat.kuleuven.ac.be diff --git a/web/noweb/contrib/leyn/notangleall b/web/noweb/contrib/leyn/notangleall new file mode 100755 index 0000000000..9ab9f75e8d --- /dev/null +++ b/web/noweb/contrib/leyn/notangleall @@ -0,0 +1,27 @@ +#! /bin/sh +# +# notangleall +# +# -creates all required directories for the code chunks +# -tangles all root chunks (as noweb -t) +# -makes all scripts mared executable. The marking is done +# as follows: +# %unx chmod +x chunk_name +# <<chunk_name>>= +# ... +# @ +# +# Can also be used for other UNIX commands that should be +# executed after the tangling. + + +echo 'making required subdirs' +noroots $1 | \ + sed '/<<[^/]*>>/d ; s/<<\(.*\)\/[^\/]*>>/\1/' | \ + sort | uniq | sed 's/\(.*\)/mkdir -p \1 ;/' | sh + +echo 'extracting code chunks' +noweb -t $1 + +echo 'making scripts executable' +egrep '^%unix' $1 | sed 's/%unix //; s/.*/& ;/' | sh diff --git a/web/noweb/contrib/leyn/ttroots b/web/noweb/contrib/leyn/ttroots new file mode 100755 index 0000000000..b03bc91922 --- /dev/null +++ b/web/noweb/contrib/leyn/ttroots @@ -0,0 +1,41 @@ +#! /bin/sh +# +# ttroots +# +# -Allows underscores in root chunks that are written to disk. +# -All root chunks are printed out in the LaTeX document as +# upright verbatim names. + +gawk ' + { line[NR] = $0 ; } +# a root chunk name can not contain spaces +$1 == "@use" && NF == 2 { used[$2] = 1 ; next ; } +$1 == "@defn" && NF == 2 { defined[$2] = 1 ; } + +END { + # determine root chunks + for (i in defined) + if (!(i in used)) + root_chunks[i] = 1 ; + + # root chunk substitutions + # Root chunk names can be used in 3 contexts: + # @defn name + # @xref notused name + # @xref chunkbegin label name + for (i=1; i<=NR; i++) { + if (line[i] ~ /^(@xref notused|@xref chunkbegin|@defn)/) { + nr = split(line[i], array, " ") ; + stat = array[1] ; + name = array[nr] ; + if ((stat == "@xref" || (stat == "@defn" && nr == 2)) && (name in root_ch +unks)) { + replace = " \\textup{\\texttt{"name"}}" ; + gsub("_", "\\_", replace) ; + gsub(" "name, replace, line[i]) ; + } ; + } ; + print line[i] ; + } +} +' diff --git a/web/noweb/contrib/norman/Makefile b/web/noweb/contrib/norman/Makefile new file mode 100644 index 0000000000..471f71e82b --- /dev/null +++ b/web/noweb/contrib/norman/Makefile @@ -0,0 +1,10 @@ +LIB=/dev/null # to be overridden +DIRS=numarkup + +all: ; for i in $(DIRS); do (cd $$i; make ICONC=$(ICONC) ICONT=$(ICONT) all); done +install: ; for i in $(DIRS); do (cd $$i; make LIB=$(LIB) BIN=$(BIN) install); done +source: ; for i in $(DIRS); do (cd $$i; make source); done +clean: ; for i in $(DIRS); do (cd $$i; make clean); done +iconlib: # cheap hack for slackmake + true + diff --git a/web/noweb/contrib/norman/README b/web/noweb/contrib/norman/README new file mode 100644 index 0000000000..722d895409 --- /dev/null +++ b/web/noweb/contrib/norman/README @@ -0,0 +1,6 @@ +cleanchunks.nw escape special characters in chunk names +moddate.nw use file modification time as date (POSIX) +numarkup contains a substitute for markup that works with nuweb files +pp contains a very simple prettyprinter +scopehack.icn A hack for scoping files; see the FAQ +generate-to A hack for renumbering lines in tools like yacc diff --git a/web/noweb/contrib/norman/cleanchunks.nw b/web/noweb/contrib/norman/cleanchunks.nw new file mode 100644 index 0000000000..9334c11225 --- /dev/null +++ b/web/noweb/contrib/norman/cleanchunks.nw @@ -0,0 +1,42 @@ +\section{Code to clean up special characters in chunk names} + +Some people want to use {\tt noweb} to browse big pieces of legacy +code without having to touch any of it by hand. Since legacy code +sometimes uses underscores and other special characters in chunk +names, it makes sense to have a filter to escape special characters in +chunk names. +<<*>>= +procedure main() + local tag, line + while line := read() do + line ? + if tag := =("@defn " | "@use ") then + write(tag, TeXliteral(tab(0))) + else + write(line) +end +@ +<<'\\{}$&#^_ ~%'>>= +<<*>>= +procedure TeXliteral(arg) + static nospace, code, TeXspecials + initial { codes := ["\\", 92, "{", 123, "}", 125, "$", 36, "&", 38, "#", 35, "^", 94, + "_", 95, "%", 37, "~", 126] + code := table() + TeXspecials := '\\{}$&#^_~%' + while (c := get(codes), n := get(codes)) do code[c] := string(n) + if c := !TeXspecials & c ~== " " & not member(code, c) then + stop("internal error, character-code mismatch, report a bug!") + } + s := "" + arg ? { + while s ||:= tab(upto(TeXspecials)) do { + c := move(1) + if member(code, c) then + s ||:= "{\\tt\\char" || code[c] || "}" + else + s ||:= "\\" || c + } + return s || tab(0) + } +end diff --git a/web/noweb/contrib/norman/email b/web/noweb/contrib/norman/email new file mode 100644 index 0000000000..d87018308d --- /dev/null +++ b/web/noweb/contrib/norman/email @@ -0,0 +1 @@ +nr@eecs.harvard.edu diff --git a/web/noweb/contrib/norman/generate-to b/web/noweb/contrib/norman/generate-to new file mode 100755 index 0000000000..9bc8f65e6e --- /dev/null +++ b/web/noweb/contrib/norman/generate-to @@ -0,0 +1,24 @@ +#!/usr/bin/env lua5.1 + +-- Usage: $0 filename +-- Reads from stdin and writes to filename, renumbering directives +-- that say "generated code" + +assert(#arg == 1, 'Usage: $0 outfilename') +local filename = assert(arg[1]) + +local f = assert(io.open(filename, 'w')) + +local n = 0 -- how many lines have already been written to f +local function rewrite() + return string.format('#line %d "%s"', n, filename) +end +for l in io.lines() do + n = n + 1 + l = l:gsub('%#line%s+%d+%s*"generated code"', rewrite, 1) + f:write(l, '\n') +end + +f:close() + +
\ No newline at end of file diff --git a/web/noweb/contrib/norman/htmlgif/htmlgif.icn b/web/noweb/contrib/norman/htmlgif/htmlgif.icn new file mode 100644 index 0000000000..1a681be801 --- /dev/null +++ b/web/noweb/contrib/norman/htmlgif/htmlgif.icn @@ -0,0 +1,54 @@ +# htmlgif -- convert eps references to gifs + +procedure newer(target, prereq) + return system("newer " || target || " " || prereq) == 0 +end + +procedure main(args) + if *args > 0 then every cvt(open(!args)) + else cvt(&input) + return +end + +procedure cvt(file) + while line := read(file) do + line ? + if (pre := tab(find("<a ")), optwhite(), ="<a href=", optwhite(), + ps := tab(upto(' \t>')), optwhite(), =">", + tab(lookfor(s := "PostScript figure " || (ps|strip_quotes(ps)))), + =s, any(' ]<>"'), tab(find("</a>")), post := tab(0)) then + { + ps := strip_quotes(ps) + gif := suffex(ps) || ".gif" + write(pre, "<img inline src=", quote(gif), + " alt=", quote("[GIF derived from PostScript figure " || ps || "]"), ">", + post) + if not newer(gif, ps) then + system("pstopbm " || ps || " | ppmtogif | giftool -rgb white > " || gif) + } else { + write(line) + } + return +end + +procedure lookfor(s) + suspend find(s) +end + +procedure strip_quotes(s) + return if s[1] == s[-1] == "\"" then s[2:-1] else s +end + +procedure suffex(s) + static nodot + initial nodot := ~ '.' + s ? return if (b := tab(upto('.')), =".", tab(many(nodot)), pos(0)) then b else s +end + +procedure quote(s) + return "\"" || s || "\"" +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end diff --git a/web/noweb/contrib/norman/htmlgif/newer.c b/web/noweb/contrib/norman/htmlgif/newer.c new file mode 100644 index 0000000000..9b741bd6f9 --- /dev/null +++ b/web/noweb/contrib/norman/htmlgif/newer.c @@ -0,0 +1,19 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> + +main(int argc, char *argv[]) { + struct stat b1, b2; + + if (argc != 3) { + fprintf(stderr, "Usage: %s file1 file2\n", argv[0]); + exit (-1); + } else if (stat(argv[1],&b1) < 0) { + perror(argv[1]); + exit(-2); + } else if (stat(argv[2],&b2) < 0) { + perror(argv[2]); + exit(-2); + } else exit(b1.st_mtime > b2.st_mtime ? 0 : 1); +} diff --git a/web/noweb/contrib/norman/htmlgif/pstopbm b/web/noweb/contrib/norman/htmlgif/pstopbm new file mode 100755 index 0000000000..eeb65ff385 --- /dev/null +++ b/web/noweb/contrib/norman/htmlgif/pstopbm @@ -0,0 +1,86 @@ +#!/bin/sh +# exec gs -sDEVICE=djet500 -sOutputFile=- -q -dNOPAUSE "$@" + +device=pbm +case $0 in + *pbm) device=pbm ;; + *ppm) device=ppm ;; +esac + +border=10 +leftborder=$border +rightborder=$border +upperborder=$border +lowerborder=$border +translate=yes +showpage=showpage + +while [ $# -gt 0 ]; do + case $1 in + -border) border="$2" + leftborder=$border + rightborder=$border + upperborder=$border + lowerborder=$border + shift ;; + -upper) upperborder="$2" ; shift ;; + -lower) lowerborder="$2" ; shift ;; + -left) leftborder="$2" ; shift ;; + -right) rightborder="$2" ; shift ;; + -notrans) translate= ;; + -showpage) showpage=showpage ;; + -noshowpage) showpage= ;; + -*) echo "Unknown option $1" 1>&2 ; exit 1;; + *) break ;; + esac + shift +done + +tmp=$(mktemp) +tmpa=$(mktemp --suffix=.a) +if [ $# -eq 0 ]; then cat > $tmp; else cat "$@" > $tmp; fi + +if echo "$@" | fgrep .eps > /dev/null; then + echo showpage >> $tmp +fi + +set foo `psbb $tmp` +shift + +if [ -n "$translate" -a $# -eq 4 ]; then + + llx="$1" lly="$2" urx="$3" ury="$4" + llx=`expr $llx - $leftborder` + lly=`expr $lly - $lowerborder` + urx=`expr $urx + $rightborder` + ury=`expr $ury + $upperborder` + width=`expr $urx - $llx` + height=`expr $ury - $lly` + + awk '{print} + /^%%EndComments/ { printf "%d neg %d neg translate\n", '"$llx, $lly"' } + ' $tmp > $tmpa + echo "$showpage" | + gs -q -g"${width}x$height" -sDEVICE=$device -sOutputFile=- -dNOPAUSE $tmpa - +else + echo "$showpage" | + gs -q -sDEVICE=$device -sOutputFile=- -dNOPAUSE $tmp - +fi + +# rm -f $tmp $tmpa + +exit 0 + +#### old version + + + +if [ $# -eq 0 ]; then + tmp=$(mktemp) + cat > $tmp + gs -q -sDEVICE=$device -sOutputFile=- -dNOPAUSE -dMAGSTEP=1.0 $tmp +else + gs -q -sDEVICE=$device -sOutputFile=- -dNOPAUSE -dMAGSTEP=1.0 "$@" +fi +rm -rf $tmp + diff --git a/web/noweb/contrib/norman/moddate.nw b/web/noweb/contrib/norman/moddate.nw new file mode 100644 index 0000000000..bf43e8fe4f --- /dev/null +++ b/web/noweb/contrib/norman/moddate.nw @@ -0,0 +1,79 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode -*- +\section{Using file modification dates} + +\date{October 31, 1996} + +This \texttt{noweb} filter +sets the date to the modification date of the file being woven. +It relies on the convention that +\begin{quote} +\verb+\date{+\emph{mumble}\verb+}+ +\end{quote} +appears on a line by itself. \emph{Mumble} stands for any string. +The filter replaces \emph{Mumble} with the modifcation date and time +of the file as announced by \verb+@file+. + +The filter uses POSIX functions, and it uses the \texttt{noweb} input +stuff, so it has to be linked with +\texttt{getline.o} \texttt{columns.o}, and \texttt{errors.o} from the +\texttt{noweb} distribution. +<<moddate.c*>>= +<<includes>> +<<local procs>> +main() { + char *s; + char *file = NULL; + while (s = getline(stdin)) + if (match("@file ", s)) { + printf("%s", s); + file = get_file_name(s); + } else if (matches_date(s) && file != NULL) + print_modification_time(file); + else + printf("%s", s); +} +@ +Matching ideas are stolen from Icon. +<<local procs>>= +match(char *pattern, char *s) { + return !strncmp(pattern, s, strlen(pattern)); +} +<<local procs>>= +matches_date(char *s) { + return match("@text \\date{", s) && s[strlen(s)-2] == '}'; +} +@ +Allocate space for a file name and strip the trailing newline. +<<local procs>>= +char *get_file_name(char *s) { + char *p = (char *)malloc(strlen(s)); /* wastes some characters */ + assert(p); + strcpy(p, s + strlen("@file ")); + p[strlen(p)-1] = 0; /* trim newline */ + return p; +} +@ +To get a nicer format for the time, I would replace the call to +[[asctime]] with something else. +<<local procs>>= +void print_modification_time(char *file) { + struct stat buf; + int n = stat(file, &buf); + char *time; + if (n) {fprintf(stderr, "could not stat %s\n", file); exit(1); } + time = asctime(localtime(&buf.st_mtime)); + if (time[strlen(time)-1] == '\n') + time[strlen(time)-1] = 0; + printf("@text \\date{%s}\\def\\today{%s}\n", time, time); +} +@ +<<includes>>= +#include <time.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.h> +#include "getline.h" + diff --git a/web/noweb/contrib/norman/numarkup/Makefile b/web/noweb/contrib/norman/numarkup/Makefile new file mode 100644 index 0000000000..9165670393 --- /dev/null +++ b/web/noweb/contrib/norman/numarkup/Makefile @@ -0,0 +1,37 @@ +LIB=/dev/null # to be overridden +CC = cc +CFLAGS = -O + +TARGET = numarkup +OBJS = main.o pass1.o latex.o input.o scraps.o names.o arena.o global.o + +.SUFFIXES: .nw +.nw.c: ; notangle -R"$@"'*' -L $< | cpif $@ +.nw.h: ; notangle -R"$@" $< | cpif $@ + +all: + noweb -t numarkup.nw + make $(TARGET) + +install: + noweb -t numarkup.nw + make $(TARGET) + strip $(TARGET) + cp $(TARGET) $(LIB) + +source: main.c pass1.c latex.c input.c scraps.c names.c arena.c global.c + +clean: + rm -f *.o *.c *.h *.tex *.log *.dvi *~ *.blg $(TARGET) *.html *~ + +$(OBJS): global.h + +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) + +numarkup.html: numarkup.nw + noweave -filter l2h -html -index numarkup.nw > numarkup.html + +numarkup.tex: numarkup.nw + noweb -o numarkup.nw + diff --git a/web/noweb/contrib/norman/numarkup/numarkup.bbl b/web/noweb/contrib/norman/numarkup/numarkup.bbl new file mode 100644 index 0000000000..224d2050b3 --- /dev/null +++ b/web/noweb/contrib/norman/numarkup/numarkup.bbl @@ -0,0 +1,58 @@ +\begin{thebibliography}{10} + +\bibitem{aho:75} +Alfred~V. Aho and Margaret~J. Corasick. +\newblock Efficient string matching: An aid to bibliographic search. +\newblock {\em Communications of the ACM}, 18(6):333--340, June 1975. + +\bibitem{hanson:90} +David~R. Hanson. +\newblock Fast allocation and deallocation of memory based on object lifetimes. +\newblock {\em Software -- Practice and Experience}, 20(1):5--12, January 1990. + +\bibitem{knuth:84} +Donald~E. Knuth. +\newblock Literate programming. +\newblock {\em The Computer Journal}, 27(2):97--111, May 1984. + +\bibitem{metafont:program} +Donald~E. Knuth. +\newblock {\em {{\small\sf METAFONT:}} The Program}. +\newblock Computers \& Typesetting. Addison-Wesley, 1986. + +\bibitem{tex:program} +Donald~E. Knuth. +\newblock {\em {{\TeX}}: The Program}. +\newblock Computers \& Typesetting. Addison-Wesley, 1986. + +\bibitem{texbook} +Donald~E. Knuth. +\newblock {\em The {{\TeX}}book}. +\newblock Computers \& Typesetting. Addison-Wesley, 1986. + +\bibitem{latex} +Leslie Lamport. +\newblock {\em {{\LaTeX:}} A Document Preparation System}. +\newblock Addison-Wesley, 1986. + +\bibitem{levy:90} +Silvio Levy and Donald~E. Knuth. +\newblock {{\tt CWEB}} user manual: The {{\small CWEB}} system of structured + documentation. +\newblock Technical Report {\small STAN}-{\small CS}-83-977, Stanford + University, October 1990. +\newblock Available for anonymous ftp from {\tt labrea.stanford.edu} in + directory {\tt pub/cweb}. + +\bibitem{noweb} +Norman Ramsey. +\newblock Literate-programming tools need not be complex. +\newblock Submitted to IEEE Software, August 1992. + +\bibitem{funnelweb} +Ross~N. Williams. +\newblock {FunnelWeb} user's manual, May 1992. +\newblock Available for anonymous ftp from {\tt sirius.itd.adelaide.edu.au} in + directory {\tt pub/funnelweb}. + +\end{thebibliography} diff --git a/web/noweb/contrib/norman/numarkup/numarkup.nw b/web/noweb/contrib/norman/numarkup/numarkup.nw new file mode 100644 index 0000000000..e47c5a356c --- /dev/null +++ b/web/noweb/contrib/norman/numarkup/numarkup.nw @@ -0,0 +1,1264 @@ +\documentstyle[noweb]{report} + +\title{Nuweb front end for noweb} +\author{Norman Ramsey\\(from code by Preston Briggs)} + +\begin{document} +\pagenumbering{roman} +\maketitle +\tableofcontents + +\chapter{Introduction} +\pagenumbering{arabic} + +This code reads one or more nuweb files and produces noweb intermediate code + (as described in the {\em Noweb Hackers' Guide}) on +standard output. +It was created by modifying version 0.87 of nuweb. + + + +\chapter{The Overall Structure} + +Processing a web requires two major steps: +\begin{enumerate} +\item Read the source, accumulating file names, macro names, scraps, +and lists of cross-references. +This pass is needed so we can disambiguated scrap names. +\item Reread the source, transforming it to noweb form on standard output. +\end{enumerate} + + +\section{Files} + +I have divided the program into several files for quicker +recompilation during development. +<<global.h>>= +<<Include files>> +<<Type declarations>> +<<Global variable declarations>> +<<Function prototypes>> +@ +We'll need at least three of the standard system include files. +<<Include files>>= +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +@ %def FILE stderr exit fprintf fputs fopen fclose getc putc strlen toupper isupper islower isgraph isspace tempnam remove malloc size_t +@ +\newpage +\noindent +I also like to use [[TRUE]] and [[FALSE]] in my code. +I'd use an [[enum]] here, except that some systems seem to provide +definitions of [[TRUE]] and [[FALSE]] be default. The following +code seems to work on all the local systems. +<<Type declarations>>= +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!0) +#endif +@ +\subsection{The Main Files} + +The code is divided into four main files (introduced here) and five +support files (introduced in the next section). +The file [[main.c]] will contain the driver for the whole program +(see Section~\ref{main-routine}). +<<main.c*>>= +#include "global.h" +@ +The first pass over the source file is contained in [[pass1.c]]. +It handles collection of all the file names, macros names, and scraps +(see Section~\ref{pass-one}). +<<pass1.c*>>= +#include "global.h" +@ +The [[.tex]] file is created during a second pass over the source +file. The file [[latex.c]] contains the code controlling the +construction of the [[.tex]] file +(see Section~\ref{latex-file}). +<<latex.c*>>= +#include "global.h" +@ +\subsection{Support Files} + +The support files contain a variety of support routines used to define +and manipulate the major data abstractions. +The file [[input.c]] holds all the routines used for referring to +source files (see Section~\ref{source-files}). +<<input.c*>>= +#include "global.h" +@ +Creation and lookup of scraps is handled by routines in [[scraps.c]] +(see Section~\ref{scraps}). +<<scraps.c*>>= +#include "global.h" +@ +The handling of file names and macro names is detailed in [[names.c]] +(see Section~\ref{names}). +<<names.c*>>= +#include "global.h" +@ +Memory allocation and deallocation is handled by routines in [[arena.c]] +(see Section~\ref{memory-management}). +<<arena.c*>>= +#include "global.h" +@ +Finally, for best portability, I seem to need a file containing +(useless!) definitions of all the global variables. +<<global.c*>>= +#include "global.h" +<<Global variable definitions>> +@ +\section{The Main Routine} \label{main-routine} + +The main routine is quite simple in structure. +It wades through the optional command-line arguments, +then handles any files listed on the command line. +<<main.c*>>= +void main(argc, argv) + int argc; + char **argv; +{ + int arg = 1; + <<Interpret command-line arguments>> + <<Process the remaining arguments (file names)>> + exit(0); +} +@ +\subsection{Command-Line Arguments} + +There is one possible command-line argument: +\begin{description} +\item[\tt -v] The verbose flag. Forces output of progress reports. +\end{description} + +Global flags are declared for each of the arguments. +<<Global variable declarations>>= +extern int verbose_flag; /* if TRUE, write progress information */ +@ +The flags are all initialized for correct default behavior. + +<<Global variable definitions>>= +int verbose_flag = FALSE; +@ +We save the invocation name of the command in a global variable +[[command_name]] for use in error messages. +<<Global variable declarations>>= +extern char *command_name; +<<Global variable definitions>>= +char *command_name = NULL; +@ +The invocation name is conventionally passed in [[argv[0]]]. +<<Interpret command-line arguments>>= +command_name = argv[0]; +@ +We need to examine the remaining entries in [[argv]], looking for +command-line arguments. +<<Interpret command-line arguments>>= +while (arg < argc) { + char *s = argv[arg]; + if (*s++ == '-') { + <<Interpret the argument string [[s]]>> + arg++; + } + else break; +} +@ +Several flags can be stacked behind a single minus sign; therefore, +we've got to loop through the string, handling them all. +<<Interpret the argument string [[s]]>>= +{ + char c = *s++; + while (c) { + switch (c) { + case 'v': verbose_flag = TRUE; + break; + default: fprintf(stderr, "%s: unexpected argument ignored. ", + command_name); + fprintf(stderr, "Usage is: %s [-v] file...\n", + command_name); + break; + } + c = *s++; + } +} +@ +\subsection{File Names} + +We expect at least one file name. While a missing file name might be +ignored without causing any problems, we take the opportunity to report +the usage convention. +<<Process the remaining arguments (file names)>>= +{ + if (arg >= argc) { + fprintf(stderr, "%s: expected a file name. ", command_name); + fprintf(stderr, "Usage is: %s [-v] file-name...\n", command_name); + exit(-1); + } + do { + <<Handle the file name in [[argv[arg]]]>> + arg++; + } while (arg < argc); +} +@ +The code to handle a particular file name is rather more tedious than +the actual processing of the file. A file name may be an arbitrarily +complicated path name, with an optional extension. If no extension is +present, we add [[.w]] as a default. The extended path name will be +kept in a local variable [[source_name]]. +<<Handle the file name in [[argv[arg]]]>>= +{ + char source_name[100]; + <<Build [[source_name]]>> + <<Process a file>> +} +@ +I bump the pointer [[p]] through all the characters in [[argv[arg]]], +copying all the characters into [[source_name]] (via the pointer +[[q]]). + +At each slash, I update [[trim]] to point just past the +slash in [[source_name]]. The effect is that [[trim]] will point +at the file name without any leading directory specifications. + +The pointer [[dot]] is made to point at the file name extension, if +present. If there is no extension, we add [[.w]] to the source name. +In any case, we create the [[tex_name]] from [[trim]], taking +care to get the correct extension. +<<Build [[source_name]]>>= +{ + char *p = argv[arg]; + char *q = source_name; + char *dot = NULL; + char c = *p++; + while (c) { + *q++ = c; + if (c == '/') { + dot = NULL; + } + else if (c == '.') + dot = q - 1; + c = *p++; + } + *q = '\0'; + if (!dot) { + *q++ = '.'; + *q++ = 'w'; + *q = '\0'; + } +} +@ +Now that we're finally ready to process a file, it's not really too +complex. We bundle most of the work into routines [[pass1]] +(see Section~\ref{pass-one}) and [[write_tex]] (see +Section~\ref{latex-file}). After we're finished with a +particular file, we must remember to release its storage (see +Section~\ref{memory-management}). +<<Process a file>>= +{ + pass1(source_name); + write_tex(source_name, "bogus"); + arena_free(); +} +@ +\section{Pass One} \label{pass-one} + +During the first pass, we scan the file, saving names so we'll be able to +disambiguated them later. +<<Function prototypes>>= +extern void pass1(); +@ +The routine [[pass1]] takes a single argument, the name of the +source file. It opens the file, then initializes the scrap structures +(see Section~\ref{scraps}) and the roots of the file-name tree, the +macro-name tree, and the tree of user-specified index entries (see +Section~\ref{names}). After completing all the +necessary preparation, we make a pass over the file, filling in all +our data structures. Next, we seach all the scraps for references to +the user-specified index entries. Finally, we must reverse all the +cross-reference lists accumulated while scanning the scraps. +<<pass1.c*>>= +void pass1(file_name) + char *file_name; +{ + if (verbose_flag) + fprintf(stderr, "reading %s\n", file_name); + source_open(file_name); + macro_names = NULL; + file_names = NULL; + <<Scan the source file, looking for at-sequences>> +} +@ +The only thing we look for in the first pass are the command +sequences. All ordinary text is skipped entirely. +<<Scan the source file, looking for at-sequences>>= +{ + int c = source_get(); + while (c != EOF) { + if (c == '@') + <<Scan at-sequence>> + c = source_get(); + } +} +@ +Only four of the at-sequences are interesting during the first pass. +We skip past others immediately; warning if unexpected sequences are +discovered. +<<Scan at-sequence>>= +{ + c = source_get(); + switch (c) { + case 'O': + case 'o': <<Build output file definition>> + break; + case 'D': + case 'd': <<Build macro definition>> + break; + case '@': + case 'u': + case 'm': + case 'f': /* ignore during this pass */ + break; + default: fprintf(stderr, + "%s: unexpected @ sequence ignored (%s, line %d)\n", + command_name, source_name, source_line); + break; + } +} +@ +\subsection{Accumulating Definitions} + +There are two steps required to handle a definition: +\begin{enumerate} +\item Build an entry for the name so we can look it up later. +\item Skip past the scrap. +\end{enumerate} +We go through the same steps for both file names and macro names. +<<Build output file definition>>= +{ + collect_file_name(); + collect_scrap(); +} +<<Build macro definition>>= +{ + collect_macro_name(); + collect_scrap(); +} +@ +\section{Writing the Latex File} \label{latex-file} + +The second pass (invoked via a call to [[write_tex]]) copies most of +the text from the source file straight into a [[.tex]] file. +Definitions are formatted slightly and cross-reference information is +printed out. + +Note that all the formatting is handled in this section. +If you don't like the format of definitions or indices or whatever, +it'll be in this section somewhere. Similarly, if someone wanted to +modify nuweb to work with a different typesetting system, this would +be the place to look. + +<<Function prototypes>>= +extern void write_tex(); +@ +We need a few local function declarations before we get into the body +of [[write_tex]]. +<<latex.c*>>= +static void copy_scrap(); /* formats the body of a scrap */ +@ +The routine [[write_tex]] takes two file names as parameters: the +name of the web source file and the name of the [[.tex]] output file. +<<latex.c*>>= +void write_tex(file_name, tex_name) + char *file_name; + char *tex_name; +{ + if (verbose_flag) + fprintf(stderr, "writing %s\n", "standard output"); + source_open(file_name); + printf("@file %s\n", file_name); + <<Copy [[source_file]] into standard output, transforming to noweb>> +} +@ +We make our second (and final) pass through the source web, this time +copying characters straight into the [[.tex]] file. However, we keep +an eye peeled for [[@]]~characters, which signal a command sequence. + +We keep track of state. + +<<Copy [[source_file]] into standard output, transforming to noweb>>= +{ + int scraps = 1; + int c = source_get(); + int docs_begun = 0; + while (c != EOF) { + if (c == '@') { + <<Interpret at-sequence>> + } else { + <<begin documentation chunk>> + if (c == '\n') + printf("\n@nl\n@text "); + else + putchar(c); + c = source_get(); + } + } +} +<<begin documentation chunk>>= +if (!docs_begun) { + docs_begun = 1; + printf("@begin docs %d\n", ++chunk_count); + printf("@text "); +} +@ + +Counting chunks needs a global variable. +<<Global variable declarations>>= + +extern int chunk_count; +<<Global variable definitions>>= + +int chunk_count = 0; +<<end documentation chunk>>= + +if (docs_begun) { + printf("\n@end docs %d\n", chunk_count); + docs_begun = 0; +} +<<Interpret at-sequence>>= +{ + int big_definition = FALSE; + c = source_get(); + switch (c) { + case 'O': big_definition = TRUE; + case 'o': <<end documentation chunk>> + <<Write output file definition>> + break; + case 'D': big_definition = TRUE; + case 'd': <<end documentation chunk>> + <<Write macro definition>> + break; + case 'f': <<Write index of file names>> + break; + case 'm': <<Write index of macro names>> + break; + case 'u': <<Write index of user-specified names>> + break; + case '@': <<begin documentation chunk>> putchar(c); /* fall through */ + default: c = source_get(); + break; + } +} +@ +Macro and file definitions are formatted nearly identically. +<<Write output file definition>>= +{ + Name *name = collect_file_name(); + printf("@begin code %d\n", ++chunk_count); + printf("@defn %s%s\n@nl\n", name->spelling, name->debug_flag ? "*" : ""); + copy_scrap(); + <<Finish the scrap environment>> +} +<<Write macro definition>>= +{ + Name *name = collect_macro_name(); + printf("@begin code %d\n", ++chunk_count); + printf("@defn %s\n@nl\n", name->spelling); + copy_scrap(); + <<Finish the scrap environment>> +} +<<Finish the scrap environment>>= +{ + printf("\n@end code %d\n", chunk_count); + do + c = source_get(); + while (isspace(c)); /* may not be appropriate for noweb */ +} +@ +\subsubsection{Formatting a Scrap} +<<latex.c*>>= +static void copy_scrap() +{ + int c = source_get(); + int indent = 0; + printf("@text "); + while (1) { + switch (c) { + case '@': <<Check at-sequence for end-of-scrap>> + break; + case '\n': printf("\n@nl\n@text "); + indent = 0; + break; + case '\t': <<Expand tab into spaces>> + break; + default: putchar(c); + indent++; + break; + } + c = source_get(); + } +} +<<Expand tab into spaces>>= +{ + int delta = 8 - (indent % 8); + indent += delta; + while (delta > 0) { + putchar(' '); + delta--; + } +} +<<Check at-sequence for end-of-scrap>>= +{ + c = source_get(); + switch (c) { + case '@': putchar('@'); + break; + case '|': printf("\n"); + <<print out index entries>> /* fall through */ + case '}': return; + case '<': <<Format macro name>> + break; + default : fprintf(stderr, "%s: unexpected @%c in scrap (%s, %d)\n", + command_name, c, source_name, source_line); + exit(-1); + } +} +<<print out index entries>>= +{ + do { + char new_name[100]; + char *p = new_name; + do + c = source_get(); + while (isspace(c)); + if (c != '@') { + Name *name; + do { + *p++ = c; + c = source_get(); + } while (c != '@' && !isspace(c)); + *p = '\0'; + printf("@index defn %s\n", new_name); + } + } while (c != '@'); + printf("@text "); /* maintain invariant, even though no more text is coming */ + c = source_get(); + if (c != '}') { + fprintf(stderr, "%s: unexpected @%c in scrap (%s, %d)\n", + command_name, c, source_name, source_line); + exit(-1); + } +} +<<Format macro name>>= +{ + Name *name = collect_scrap_name(); + printf("\n@use %s\n@text ", name->spelling); +} +@ +\subsection{Generating the Indices} + +<<Write index of file names>>= +{ + /* noweb doesn't do files; they're all macros */ + c = source_get(); +} +<<Write index of macro names>>= +{ + <<begin documentation chunk>> + printf("\\nowebchunks "); + c = source_get(); +} +<<Write index of user-specified names>>= +{ + <<begin documentation chunk>> + printf("\\nowebindex "); + c = source_get(); +} +@ +\chapter{The Support Routines} + +\section{Source Files} \label{source-files} + +\subsection{Global Declarations} + +We need two routines to handle reading the source files. +<<Function prototypes>>= +extern void source_open(); /* pass in the name of the source file */ +extern int source_get(); /* no args; returns the next char or EOF */ +@ +There are also two global variables maintained for use in error +messages and such. +<<Global variable declarations>>= +extern char *source_name; /* name of the current file */ +extern int source_line; /* current line in the source file */ +<<Global variable definitions>>= +char *source_name = NULL; +int source_line = 0; +@ +\subsection{Local Declarations} + + +<<input.c*>>= +static FILE *source_file; /* the current input file */ +static int source_peek; +static int double_at; +static int include_depth; +<<input.c*>>= +struct { + FILE *file; + char *name; + int line; +} stack[10]; +@ +\subsection{Reading a File} + +The routine [[source_get]] returns the next character from the +current source file. It notices newlines and keeps the line counter +[[source_line]] up to date. It also catches [[EOF]] and watches +for [[@]]~characters. All other characters are immediately returned. +<<input.c*>>= +int source_get() +{ + int c = source_peek; + switch (c) { + case EOF: <<Handle [[EOF]]>> + return c; + case '@': <<Handle an ``at'' character>> + return c; + case '\n': source_line++; + default: source_peek = getc(source_file); + return c; + } +} +@ +This whole [[@]]~character handling mess is pretty annoying. +I want to recognize [[@i]] so I can handle include files correctly. +At the same time, it makes sense to recognize illegal [[@]]~sequences +and complain; this avoids ever having to check anywhere else. +Unfortunately, I need to avoid tripping over the [[@@]]~sequence; +hence this whole unsatisfactory [[double_at]] business. +<<Handle an ``at'' character>>= +{ + c = getc(source_file); + if (double_at) { + source_peek = c; + double_at = FALSE; + c = '@'; + } + else + switch (c) { + case 'i': <<Open an include file>> + break; + case 'f': case 'm': case 'u': + case 'd': case 'o': case 'D': case 'O': + case '{': case '}': case '<': case '>': case '|': + source_peek = c; + c = '@'; + break; + case '@': source_peek = c; + double_at = TRUE; + break; + default: fprintf(stderr, "%s: bad @ sequence (%s, line %d)\n", + command_name, source_name, source_line); + exit(-1); + } +} +<<Open an include file>>= +{ + char name[100]; + if (include_depth >= 10) { + fprintf(stderr, "%s: include nesting too deep (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + <<Collect include-file name>> + stack[include_depth].name = source_name; + stack[include_depth].file = source_file; + stack[include_depth].line = source_line + 1; + include_depth++; + source_line = 1; + source_name = save_string(name); + source_file = fopen(source_name, "r"); + if (!source_file) { + fprintf(stderr, "%s: can't open include file %s\n", + command_name, source_name); + exit(-1); + } + source_peek = getc(source_file); + c = source_get(); +} +<<Collect include-file name>>= +{ + char *p = name; + do + c = getc(source_file); + while (c == ' ' || c == '\t'); + while (isgraph(c)) { + *p++ = c; + c = getc(source_file); + } + *p = '\0'; + if (c != '\n') { + fprintf(stderr, "%s: unexpected characters after file name (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } +} +@ +If an [[EOF]] is discovered, the current file must be closed and +input from the next stacked file must be resumed. If no more files are +on the stack, the [[EOF]] is returned. +<<Handle [[EOF]]>>= +{ + fclose(source_file); + if (include_depth) { + include_depth--; + source_file = stack[include_depth].file; + source_line = stack[include_depth].line; + source_name = stack[include_depth].name; + source_peek = getc(source_file); + c = source_get(); + } +} +@ +\subsection{Opening a File} + +The routine [[source_open]] takes a file name and tries to open the +file. If unsuccessful, it complains and halts. Otherwise, it sets +[[source_name]], [[source_line]], and [[double_at]]. +<<input.c*>>= +void source_open(name) + char *name; +{ + source_file = fopen(name, "r"); + if (!source_file) { + fprintf(stderr, "%s: couldn't open %s\n", command_name, name); + exit(-1); + } + source_name = name; + source_line = 1; + source_peek = getc(source_file); + double_at = FALSE; + include_depth = 0; +} +@ +\section{Scraps} \label{scraps} +<<scraps.c*>>= +void collect_scrap() +{ + char *source = save_string(source_name); + int line = source_line; + <<Accumulate scrap and return>> +} +<<Accumulate scrap and return>>= +{ + int c = source_get(); + while (1) { + switch (c) { + case EOF: fprintf(stderr, "%s: unexpected EOF in scrap (%s, %d)\n", + command_name, source, line); + exit(-1); + case '@': <<Handle at-sign during scrap accumulation>> + break; + default: c = source_get(); + break; + } + } +} +<<Handle at-sign during scrap accumulation>>= +{ + c = source_get(); + switch (c) { + case '@': c = source_get(); + break; + case '|': <<skip user-specified index entries>> + case '}': return; + case '<': <<Handle macro invocation in scrap>> + break; + default : fprintf(stderr, "%s: unexpected @%c in scrap (%s, %d)\n", + command_name, c, source_name, source_line); + exit(-1); + } +} +<<skip user-specified index entries>>= +{ + do { + do + c = source_get(); + while (c != '@'); + c = source_get(); + } while (c == '@'); + if (c != '}') { + fprintf(stderr, "%s: unexpected @%c in scrap (%s, %d)\n", + command_name, c, source_name, source_line); + exit(-1); + } +} +<<Handle macro invocation in scrap>>= +{ + (void) collect_scrap_name(); + c = source_get(); +} +<<Function prototypes>>= +extern void collect_scrap(); +@ +\section{Names} \label{names} +<<Type declarations>>= +typedef struct name { + char *spelling; + struct name *llink; + struct name *rlink; + int mark; + char tab_flag; + char indent_flag; + char debug_flag; +} Name; +<<Global variable declarations>>= +extern Name *file_names; +extern Name *macro_names; +<<Global variable definitions>>= +Name *file_names = NULL; +Name *macro_names = NULL; +<<Function prototypes>>= +extern Name *collect_file_name(); +extern Name *collect_macro_name(); +extern Name *collect_scrap_name(); +extern Name *name_add(); +extern Name *prefix_add(); +extern char *save_string(); +<<names.c*>>= +enum { LESS, GREATER, EQUAL, PREFIX, EXTENSION }; + +static int compare(x, y) + char *x; + char *y; +{ + int len, result; + int xl = strlen(x); + int yl = strlen(y); + int xp = x[xl - 1] == ' '; + int yp = y[yl - 1] == ' '; + if (xp) xl--; + if (yp) yl--; + len = xl < yl ? xl : yl; + result = strncmp(x, y, len); + if (result < 0) return GREATER; + else if (result > 0) return LESS; + else if (xl < yl) { + if (xp) return EXTENSION; + else return LESS; + } + else if (xl > yl) { + if (yp) return PREFIX; + else return GREATER; + } + else return EQUAL; +} +@ %def compare LESS GREATER EQUAL PREFIX EXTENSION +<<names.c*>>= +char *save_string(s) + char *s; +{ + char *new = (char *) arena_getmem((strlen(s) + 1) * sizeof(char)); + strcpy(new, s); + return new; +} +@ %def save_string +<<names.c*>>= +static int ambiguous_prefix(); + +Name *prefix_add(root, spelling) + Name **root; + char *spelling; +{ + Name *node = *root; + while (node) { + switch (compare(node->spelling, spelling)) { + case GREATER: root = &node->rlink; + break; + case LESS: root = &node->llink; + break; + case EQUAL: return node; + case EXTENSION: node->spelling = save_string(spelling); + return node; + case PREFIX: <<Check for ambiguous prefix>> + return node; + } + node = *root; + } + <<Create new name entry>> +} +@ %def prefix_add +@ +Since a very short prefix might match more than one macro name, I need +to check for other matches to avoid mistakes. Basically, I simply +continue the search down {\em both\/} branches of the tree. + +<<Check for ambiguous prefix>>= +{ + if (ambiguous_prefix(node->llink, spelling) || + ambiguous_prefix(node->rlink, spelling)) + fprintf(stderr, + "%s: ambiguous prefix @<%s...@> (%s, line %d)\n", + command_name, spelling, source_name, source_line); +} +<<names.c*>>= +static int ambiguous_prefix(node, spelling) + Name *node; + char *spelling; +{ + while (node) { + switch (compare(node->spelling, spelling)) { + case GREATER: node = node->rlink; + break; + case LESS: node = node->llink; + break; + case EQUAL: + case EXTENSION: + case PREFIX: return TRUE; + } + } + return FALSE; +} +@ %def ambiguous_prefix +<<names.c*>>= +Name *name_add(root, spelling) + Name **root; + char *spelling; +{ + Name *node = *root; + while (node) { + int result = strcmp(node->spelling, spelling); + if (result > 0) + root = &node->llink; + else if (result < 0) + root = &node->rlink; + else + return node; + node = *root; + } + <<Create new name entry>> +} +@ %def name_add +<<Create new name entry>>= +{ + node = (Name *) arena_getmem(sizeof(Name)); + node->spelling = save_string(spelling); + node->mark = FALSE; + node->llink = NULL; + node->rlink = NULL; + node->tab_flag = TRUE; + node->indent_flag = TRUE; + node->debug_flag = FALSE; + *root = node; + return node; +} +@ +Name terminated by whitespace. Also check for ``per-file'' flags. Keep +skipping white space until we reach scrap. +<<names.c*>>= +Name *collect_file_name() +{ + Name *new_name; + char name[100]; + char *p = name; + int start_line = source_line; + int c = source_get(); + while (isspace(c)) + c = source_get(); + while (isgraph(c)) { + *p++ = c; + c = source_get(); + } + if (p == name) { + fprintf(stderr, "%s: expected file name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + *p = '\0'; + new_name = name_add(&file_names, name); + <<Handle optional per-file flags>> + if (c != '@' || source_get() != '{') { + fprintf(stderr, "%s: expected @{ after file name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + return new_name; +} +@ %def collect_file_name +<<Handle optional per-file flags>>= +{ + while (1) { + while (isspace(c)) + c = source_get(); + if (c == '-') { + c = source_get(); + do { + switch (c) { + case 't': new_name->tab_flag = FALSE; + break; + case 'd': new_name->debug_flag = TRUE; + break; + case 'i': new_name->indent_flag = FALSE; + break; + default : fprintf(stderr, "%s: unexpected per-file flag (%s, %d)\n", + command_name, source_name, source_line); + break; + } + c = source_get(); + } while (!isspace(c)); + } + else break; + } +} +@ +Name terminated by \verb+\n+ or \verb+@{+; but keep skipping until \verb+@{+ +<<names.c*>>= +Name *collect_macro_name() +{ + char name[100]; + char *p = name; + int start_line = source_line; + int c = source_get(); + while (isspace(c)) + c = source_get(); + while (c != EOF) { + switch (c) { + case '@': <<Check for terminating at-sequence and return name>> + break; + case '\t': + case ' ': *p++ = ' '; + do + c = source_get(); + while (c == ' ' || c == '\t'); + break; + case '\n': <<Skip until scrap begins, then return name>> + default: *p++ = c; + c = source_get(); + break; + } + } + fprintf(stderr, "%s: expected macro name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + return NULL; /* unreachable return to avoid warnings on some compilers */ +} +@ %def collect_macro_name +<<Check for terminating at-sequence and return name>>= +{ + c = source_get(); + switch (c) { + case '@': *p++ = c; + break; + case '{': <<Cleanup and install name>> + default: fprintf(stderr, + "%s: unexpected @%c in macro name (%s, %d)\n", + command_name, c, source_name, start_line); + exit(-1); + } +} +<<Cleanup and install name>>= +{ + if (p > name && p[-1] == ' ') + p--; + if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') { + p[-3] = ' '; + p -= 2; + } + if (p == name || name[0] == ' ') { + fprintf(stderr, "%s: empty scrap name (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + *p = '\0'; + return prefix_add(¯o_names, name); +} +<<Skip until scrap begins, then return name>>= +{ + do + c = source_get(); + while (isspace(c)); + if (c != '@' || source_get() != '{') { + fprintf(stderr, "%s: expected @{ after macro name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + <<Cleanup and install name>> +} +@ +Terminated by \verb+@>+ +<<names.c*>>= +Name *collect_scrap_name() +{ + char name[100]; + char *p = name; + int c = source_get(); + while (c == ' ' || c == '\t') + c = source_get(); + while (c != EOF) { + switch (c) { + case '@': <<Look for end of scrap name and return>> + break; + case '\t': + case ' ': *p++ = ' '; + do + c = source_get(); + while (c == ' ' || c == '\t'); + break; + default: if (!isgraph(c)) { + fprintf(stderr, + "%s: unexpected character in macro name (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + *p++ = c; + c = source_get(); + break; + } + } + fprintf(stderr, "%s: unexpected end of file (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + return NULL; /* unreachable return to avoid warnings on some compilers */ +} +@ %def collect_scrap_name +<<Look for end of scrap name and return>>= +{ + c = source_get(); + switch (c) { + case '@': *p++ = c; + c = source_get(); + break; + case '>': <<Cleanup and install name>> + default: fprintf(stderr, + "%s: unexpected @%c in macro name (%s, %d)\n", + command_name, c, source_name, source_line); + exit(-1); + } +} +@ +\section{Memory Management} \label{memory-management} + +I manage memory using a simple scheme inspired by Hanson's idea of +{\em arenas\/}~\cite{hanson:90}. +Basically, I allocate all the storage required when processing a +source file (primarily for names and scraps) using calls to +[[arena_getmem(n)]], where [[n]] specifies the number of bytes to +be allocated. When the storage is no longer required, the entire arena +is freed with a single call to [[arena_free()]]. Both operations +are quite fast. +<<Function prototypes>>= +extern void *arena_getmem(); +extern void arena_free(); +<<arena.c*>>= +typedef struct chunk { + struct chunk *next; + char *limit; + char *avail; +} Chunk; +@ %def Chunk +@ +We define an empty chunk called [[first]]. The variable [[arena]] points +at the current chunk of memory; it's initially pointed at [[first]]. +As soon as some storage is required, a ``real'' chunk of memory will +be allocated and attached to [[first->next]]; storage will be +allocated from the new chunk (and later chunks if necessary). +<<arena.c*>>= +static Chunk first = { NULL, NULL, NULL }; +static Chunk *arena = &first; +@ %def first arena +@ +\subsection{Allocating Memory} + +The routine [[arena_getmem(n)]] returns a pointer to (at least) +[[n]] bytes of memory. Note that [[n]] is rounded up to ensure +that returned pointers are always aligned. We align to the nearest +8~byte segment, since that'll satisfy the more common 2-byte and +4-byte alignment restrictions too. + +<<arena.c*>>= +void *arena_getmem(n) + size_t n; +{ + char *q; + char *p = arena->avail; + n = (n + 7) & ~7; /* ensuring alignment to 8 bytes */ + q = p + n; + if (q <= arena->limit) { + arena->avail = q; + return p; + } + <<Find a new chunk of memory>> +} +@ %def arena_getmem +@ +If the current chunk doesn't have adequate space (at least [[n]] +bytes) we examine the rest of the list of chunks (starting at +[[arena->next]]) looking for a chunk with adequate space. If [[n]] +is very large, we may not find it right away or we may not find a +suitable chunk at all. +<<Find a new chunk of memory>>= +{ + Chunk *ap = arena; + Chunk *np = ap->next; + while (np) { + char *v = sizeof(Chunk) + (char *) np; + if (v + n <= np->limit) { + np->avail = v + n; + arena = np; + return v; + } + ap = np; + np = ap->next; + } + <<Allocate a new chunk of memory>> +} +@ +If there isn't a suitable chunk of memory on the free list, then we +need to allocate a new one. +<<Allocate a new chunk of memory>>= +{ + size_t m = n + 10000; + np = (Chunk *) malloc(m); + np->limit = m + (char *) np; + np->avail = n + sizeof(Chunk) + (char *) np; + np->next = NULL; + ap->next = np; + arena = np; + return sizeof(Chunk) + (char *) np; +} +@ +\subsection{Freeing Memory} + +To free all the memory in the arena, we need only point [[arena]] +back to the first empty chunk. +<<arena.c*>>= +void arena_free() +{ + arena = &first; +} +@ %def arena_free +@ +\chapter{Indices} \label{indices} + +\section{Chunks} + +\nowebchunks + +\section{Identifiers} + +Knuth prints his index of indentifiers in a two-column format. +I could force this automatically by emitting the [[\twocolumn]] +command; but this has the side effect of forcing a new page. +Therefore, it seems better to leave it this up to the user. + +\nowebindex + +\bibliographystyle{plain} +\bibliography{literate} + +\end{document} diff --git a/web/noweb/contrib/norman/pp/mkfile b/web/noweb/contrib/norman/pp/mkfile new file mode 100644 index 0000000000..894316536f --- /dev/null +++ b/web/noweb/contrib/norman/pp/mkfile @@ -0,0 +1,24 @@ +ICONT=icont + +%: %.icn + $ICONT $target + +%.icn: %.nw + notangle -L'#line %-1L "%F"%N' $prereq > $target + +all:V: pp pp.ps pp.html + +$BIN/nwpp: pp.icn + $ICONT -o $target $prereq + +pp.tex: pp.nw + noweave -delay -autodefs icon -index $prereq > $target + +pp.html: pp.nw + noweave -filter l2h -delay -autodefs icon -index -html $prereq > $target + +clean:V: + rm -f *~ *.tex *.dvi *.logf *.icn *.u1 *.u2 *.aux *.toc *.log + +clobber:V: clean + rm -f *.ps *.dvi *.html pp diff --git a/web/noweb/contrib/norman/pp/pp.nw b/web/noweb/contrib/norman/pp/pp.nw new file mode 100644 index 0000000000..b8260d226b --- /dev/null +++ b/web/noweb/contrib/norman/pp/pp.nw @@ -0,0 +1,314 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- + +\documentclass {article} +\usepackage {noweb} + +\title {Simple prettyprinting with Noweb} +\author{Norman Ramsey\\ \texttt{nr@eecs.harvard.edu}} + +\begin {document} +@ +\maketitle + +\section {Introduction} + +This is a pretty-printer, written as a filter for the noweb +literate-programming tool. +The prettyprinter does not touch indentation and line breaks; what it +does is break each code line into tokens, then reformat the tokens. +Some of the prettyprinter's capabilities are specified in a +translation table. +This table is written in a file, which must be named as the first argument. +The prettyprinter will: +\begin{itemize} +\item format special tokens as specified in the parameter file +\item keep track of which tokens need to be in math mode, and take +care of it +\item change underscores to subscripts in the names of identifiers +\end{itemize} +The prettyprinter doesn't do a great job with quoted strings, and it +doesn't do anything intelligent with comments. +Users are invited to improve these aspects. + +Using the prettyprinter requires changing the {\TeX} code that noweb +runs at the start of a code chunk. This may do the job: +\begin{verbatim} +\usepackage{noweb} +\let\originalprime=' +\def\setupcode{\catcode`\ =10 \catcode`\'=13 \regressprime} +{\catcode`\'=\active + \makeatletter + \gdef\regressprime{\def'{^\bgroup\prim@s}}} +\let\Tt\relax +\end{verbatim} + +The prettyprinter uses the ``finduses'' model of symbols, alphanumerics, and +delimiters. +A token is +\begin{itemize} +\item whitespace, +\item a maximal string of symbols, +\item a maximal string of alphanumerics +\item a single delimiter, or +\item a string that begins with a delimiter and appears in the +translation table. +\end{itemize} +<<*>>= +global alphanum, symbols # anything else is a delimiter +@ The defaults are as in ``finduses.'' +<<initialization>>= +alphanum := &letters ++ &digits ++ '_\'@#' +symbols := '!%^&*-+:=|~<>./?`' +@ +All tokens become {\TeX} strings, and we track three kinds. +<<*>>= +record space(string) # white space +record math(string) # string to appear in math mode +record nonmath(string) # string to appear outside of math mode +@ Space between two math tokens goes in math mode; space adjacent to a +nonmath token goes in nonmath mode. +@ +Sometimes we have to convert something to math mode. +<<*>>= +procedure mathcvt(s) + return case type(s) of { + "math" | "space" : s + "nonmath" : math("\\mbox{" || s.string || "}") + } + stop("bad math conversion of ", image(s)) +end +procedure mathstring(s) + return mathcvt(s).string +end +@ +A table [[translation]] defines a translation into \TeX\ code for every interesting +token in the target language. +The table is a sequence of lines of the form +\begin{quote} +\begin{tabular}{ll} +\verb+$+\emph{token} \emph{translation}&A math-mode token\\ +\verb+-+\emph{token} \emph{translation}&A non-math token\\ +\verb+A+\emph{chars}&List of all characters to be considered alphanumerics\\ +\verb+S+\emph{chars}&List of all characters to be considered symbols\\ +\end{tabular} +\end{quote} +Tokens, including identifiers and symbols, are considered to be +math-mode tokens unless the translation table specifies otherwise. +<<*>>= +procedure read_translation(fname) + local f, line, k, v, t + f := open(fname) | stop("Cannot open file ", fname) + t := table() + while line := read(f) do + line ? + case move(1) of { + "$" : { tab(many(' \t')); k := tab(upto(' \t')); tab(many(' \t')); v := tab(0) + t[k] := math(v) } + "-" : { tab(many(' \t')); k := tab(upto(' \t')); tab(many(' \t')); v := tab(0) + t[k] := nonmath(v) } + "A" : alphanum := cset(tab(0)) + "S" : symbols := cset(tab(0)) + default : stop("Table entry must begin with $ or - or A or S") + } + close(f) + return t +end +@ +The rest is uninteresting Icon code, which surely could be better documented. +<<*>>= +global trans +procedure main(args) + local curline, curmath + <<initialization>> + trans := read_translation(get(args)) | stop("Must specify translation table") + <<add \TeX\ specials to [[trans]]>> + dtrans := table() + every k := key(trans) & not any(symbols, k) & not any(alphanum, k) do + dtrans[k] := trans[k] + curline := [] + code := &null + while line := read() do + line ? { <<consume input>> } +end +@ +Instead of escaping the {\TeX} specials, I just put them in the +translation table if they aren't already. +<<add \TeX\ specials to [[trans]]>>= +every c := !"{}#$%^&_" do /trans[c] := math("\\" || c) +/trans["\\"] := math("\\backslash ") +@ +We accumulate tokens into [[curline]], then emit them when we reach +the end of a line or the end of code. +<<consume input>>= +="@" | stop("Malformed line in noweb pipeline") +keyword := tab(upto(' ')|0) +value := if pos(0) then &null else (=" ", tab(0)) +case keyword of { + "begin" : {if match("code", value) then code := 1 else code := &null + write(line)} + "end" : { <<drain accumulation>>; code := &null; write(line) } + "quote" : {code := 1; write(line)} + "endquote" : {<<drain accumulation>>; code := &null; write(line)} + "text" : if \code then {<<accumulate [[value]]>>} else write(line) + "nl" | "use" : { <<drain accumulation>>; write(line) } + default : write(line) +} +@ +Converting text to tokens is the heart of the algorithm. +This code looks at the first character and finds maximal sequences. +Digit sequences are treated specially +Strings with single or double quotes are hacked in. +<<accumulate [[value]]>>= +value ? + while not pos(0) do + if any(' \t') then put(curline, space(tab(many(' \t')))) + else if any(alphanum) then { # maximal alphanumeric string + id := tab(many(alphanum)) + put(curline, xform_alphanum(id)) + } else if any(symbols) then { # maximal symbol string + id := tab(many(symbols)) + put(curline, xform_symbols(id)) + } else if delim := =("\"" | "'") then { + put(curline, xform_literal(delim || tab(find(delim)) || =delim)) + } else if =(id := key(dtrans)) then { # if delimiter starts table string, xlate + put(curline, dtrans[id]) + } else { # single delimiter character + put(curline, math(move(1))) + } +@ +Underscores become subscripts, initial hats become hats, and we wrap +long strings in \verb+\mathit+ unless they are strings of digits. +Leading underscores are botched. +<<*>>= +procedure xform_alphanum(id) + local base + if \trans[id] then return trans[id] + if id[1] == "^" then # scope is to end of symbol + return math("\\nwpphat{" || mathstring(xform_alphanum(id[2:0])) || "}") + id ? + if *(base := tab(upto('_'))) > 0 & move(1) & not pos(0) then + return math(mathstring(xform_alphanum(base)) || "_" || + mathstring(xform_alphanum(tab(0)))) + else + return math(mathwrap(tab(0))) +end +procedure mathwrap(s) + if *s = 1 then return s + else if s ? (tab(upto('\'') == 2), tab(many('\'')), pos(0)) then + return "{" || s || "}" + else if upto(~&digits, s) then return "{\\mathit{" || s || "}}" + else return s # numbers don't get italic +end +@ +Symbols don't get any of this massaging. +<<*>>= +procedure xform_symbols(id) + if \trans[id] then return trans[id] + return math(id) +end +@ +I haven't tested any of this literal jazz. +<<*>>= +procedure xform_literal(id) + static chars + initial chars := "=|+-@!$#" || &letters || &digits + if c := !chars & not(find(c, id)) then + return nonmath("\\verb" || c || id || c) + else + return nonmath("\\texttt{" || id || "}") +end +@ +To emit tokens, I track mathness, and I turn it on and off +appropriately. +I also make sure to get space outside of math mode wherever +appropriate, so it will show up. +<<drain accumulation>>= +if *curline > 0 then { + writes("@literal ") + curmath := &null + while t := get(curline) do + case type(t) of { + "math" : { <<ensure math>>; writes(t.string) } + "nonmath" : { <<ensure non-math>>; writes(t.string) } + "space" : { if /curmath then writes(repl("{\\ }", *t.string)) + else if type(curline[1]) == "math" then writes(t.string) + else { <<ensure non-math>>; writes(repl("{\\ }", *t.string)) } + } + default : stop("This can't happen --- bad token ", image(t)) + } + <<ensure non-math>> + write() +} +<<ensure math>>= +/curmath := 1 & writes("\\(") +<<ensure non-math>>= +\curmath := &null & writes("\\)") +@ +\section{Example} +Here's a fragment of source code I used in a paper: +\begin{verbatim} +fun simple () = + let (b_I --> PC := target_I | I_c) = tgt[PC] + in if [[b_I]] then + PC := [[target_I]] | [[I_c]] + else + PC := succ(PC) | [[I_c]] + fi + ; simple() + end +\end{verbatim} +Here's the corresponding output\ifhtml +, which looks pretty stupid in HTML because it's intended for {\TeX}\fi: +\begin{trivlist} +\item \obeylines +\textbf{fun}\ \({\mathit{simple}} () \equiv \) +\ \ \textbf{let}\ \((b_I \mathbin{\rightarrow} {\mathit{PC}} \mathrel{:=} {\mathit{target}}_I \mathrel{|} I_c) \equiv {\mathit{tgt}}[{\mathit{PC}}]\) +\ \ \textbf{in}\ \ \textbf{if}\ \([\![b_I]\!]\)\ \textbf{then} +\ \ \ \ \ \ \ \ \({\mathit{PC}} \mathrel{:=} [\![{\mathit{target}}_I]\!] \mathrel{|} [\![I_c]\!]\) +\ \ \ \ \ \ \textbf{else} +\ \ \ \ \ \ \ \ \({\mathit{PC}} \mathrel{:=} {\mathit{succ}}({\mathit{PC}}) \mathrel{|} [\![I_c]\!]\) +\ \ \ \ \ \ \textbf{fi} +\ \ \ \ \ \ \(; {\mathit{simple}}()\) +\ \ \textbf{end} +\end{trivlist} +@ +And finally, +here's the translation table I used: +{\small +\begin{verbatim} +A^_'@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789# +S!%&*-+:=|~<>./?` +$true \textbf{true} +$false \textbf{false} +-if \textbf{if} +-then \textbf{then} +-else \textbf{else} +-fi \textbf{fi} +-fun \textbf{fun} +-let \textbf{let} +-in \textbf{in} +-end \textbf{end} +$@[[ [\![ +$@]] ]\!] +$:= \mathrel{:=} +$andalso \land +$--> \mathbin{\rightarrow} +$= \equiv +$== = +$| \mathrel{|} +$~ \mathord{-} +$not \lnot +$!= \ne +$<= \le +$>= \ge +$... \bullet +\end{verbatim} +\par} + + +\appendix +\section {Index} +\nowebindex +\nowebchunks +@ +\end {document} diff --git a/web/noweb/contrib/norman/scopehack.icn b/web/noweb/contrib/norman/scopehack.icn new file mode 100644 index 0000000000..d837a2ff61 --- /dev/null +++ b/web/noweb/contrib/norman/scopehack.icn @@ -0,0 +1,44 @@ +# scopehack; a replacement for totex but splitting output into multiple files + +global totex + +procedure main(args) + totex := "PATH=/usr/local/noweb/lib:/usr/lib/noweb:$PATH totex" + every totex ||:= " '" || !args || "'" + + lines := [] + file + + while line := read() do { + line ? + if ="@fatal" then exit(1) + else if ="@file " then + if =\file & pos(0) then # no change + &null + else { + flush(file, lines) + file := tab(0) + } + put(lines, line) + } + flush(file, lines) +end + +procedure flush(file, lines) + if /file & *lines > 0 then + stop("First line is not @file") + else if *lines = 0 then + return + else { + outfile := suffex(file) || ".tex" + p := open(totex || " > " || outfile, "wp") | stop ("cannot run ", totex) + while write(p, get(lines)) + close(p) + return + } +end + +procedure suffex(s) + return reverse (reverse(s) ? {tab(upto('.')) & ="."; tab(0)}) +end + diff --git a/web/noweb/contrib/partingr/README b/web/noweb/contrib/partingr/README new file mode 100644 index 0000000000..a0f06fecb0 --- /dev/null +++ b/web/noweb/contrib/partingr/README @@ -0,0 +1,148 @@ +This is plain TeX indexing and cross-reference support for noweb. + +total 38 +-rwxr-x--- 1 partingr 959 Aug 2 14:10 TeXthings +-rwxr-x--- 1 partingr 5544 Aug 2 14:19 addscore.nw +-rwxr-xr-x 1 partingr 4672 Aug 2 13:35 mm2mx63 +-rwxr-xr-x 1 partingr 5033 Aug 2 13:35 mm2mx64 +-rwxr-xr-x 1 partingr 5180 Aug 2 13:35 mm2mx65 +-rwxr-xr-x 1 partingr 1445 Aug 2 13:37 mm2tex +-rwxr-xr-x 1 partingr 4109 Aug 2 13:35 mx2tex31 +-rwxr-xr-x 1 partingr 4055 Aug 2 13:35 nwindex.tex +-rwxr-x--- 1 partingr 67 Aug 2 14:14 nwnweave +-rwxr-xr-x 1 partingr 35 Aug 2 13:35 nwtangle +-rwxr-xr-x 1 partingr 64 Aug 2 13:35 nwweave +-rwxr-xr-x 1 partingr 524 Aug 2 13:58 xpand + +This is what each file does/is: + +TeXthings perl 'header' file. needs to be somewhere + perl will find it when it executes mm2mx63 + or mx2tex31 or mm2tex. + +mm2mx63 version 6.3 of mm2mx. converts mm files + (those created by markup) into mx files + (which is my modified markup file). + sectionref macros are aaa,aab,aac,aad + reads from STDIN and outputs to STDOUT + see below for cli options. + +mm2mx64 version 6.4 of mm2mx. virtually the same + as mm2mx63, but the sectionref macros that + get created have different names. default + is za,zc,zd etc. (I have lots of 2 character + macros for TeX so I don't use this so much, + but it might be useful...) + +mm2mx65 same as mm2mx63, but converts @<name@> in + documentation chunks into a @use reference + so you get the reference style in the + documentation, if you see what I mean. + EG. "see @<main@>" will become + "see <main 1a>" if main is defined in 1a + (with the proper typesetting for the module + name of course). + +mx2tex31 converts mx files into TeX. see below for + cli options. + +mm2tex weaver for normal markup files. tends to be + faster than awk because perl semi-compiles + it's programs before execution. + +nwindex.tex TeX macros for indexing. + +nwnweave shell script to weave a file with certain + cli options. + +nwweave shell script to weave a file with certain + cli options. + +xpand expands ...>> references in markup files. + !only *after* a full name has been seen! + +CLI OPTIONS: + +mm2mx63 -i create an identifier index from @ %def lines + -m create a module index + -n case insensitive module name matching + (nb all module names come out in lower case) + +mx2tex31 -i create the indexes (from the mx file) + -n name set the name of the output files. input is + from STDIN, output is to name.tex and + name.texnique. + -s hack for 'only first definition gets the full + list of defining chunks'. + (look at output to see the difference) + -f name takes the markup file from name.markup and + outputs to name.tex and name.texnique + -h help message + -q no output to terminal at all + +NWINDEX.MAC + +When mx2tex31 creates a TeX file, it inserts 'hooks' into the code +so that the chunk references can be printed out according to the +user's preferences. + +For all the hooks, \list contains a list of the defining chunks +of the named chunk and \ulist contains a list of the chunks the +named chunk is used in. +(These lists are from Appendix D of The TeXBook. Items are +seperated by \\ and contained in braces, so a list containing 1,2,3 +would be defined as \def\list{\\{1}\\{2}\\{3}} + +For a chunk defined in 1a and 1b, and used in 1c +\list={\\{1a}\\{1b}} +\ulist={\\{1c}} + +3 hooks are provided... + +\inmodname is called just before the right-angle of the chunk name +\beforecode is called just after the chunk name (and angles) have + been set +\aftercode is called just before \nwendcode{} + +The default definitions: +\inmodname = put the *first* defining chunk xref in chunk names +\beforecode = empty +\aftercode = 'This definition is continued in section...' if the name + is defined in more than one chunk and + 'This code is used in section...' + +[look at the output to see what happens] +This gives output like the LaTeX cross-referencing (and like CWEB). + +There are macros in nwindex.tex for printing out lists neatly +(ie with commas and 'and' at the end) and for putting +'section' or 'sections' at the front of the list. + +[if you need more info, either email me or wait for the documentation + which should be finished this week] + +INDEXES + +if asked to, mx2tex31 creates two files: name.ids and name.mods +ids - identifier index +mods - module index + +It *doesn't* include these by default in the TeX file. you have +to ask for them by putting \printindex{ids} or \printindex{mods} + +you have to ask for them because (IMHO) indexes aren't vitally +important while a program is being developed or for small +programs. + +the indexes are created in seperate files to allow other tools +to create them independently of mx2tex31. for example, a +c program could create name.ids by parsing the code chunks +for identifiers etc. (like CWEB/WEB do) which would be a +better way of indexing than @ %def lines... + + +BTW I apologise for this documentation. I haven't had a +chance to get a proper version written yet. + +Do people want a TeXinfo version of the documentation or +just plain TeX file? diff --git a/web/noweb/contrib/partingr/TeXthings b/web/noweb/contrib/partingr/TeXthings new file mode 100755 index 0000000000..35f537a93f --- /dev/null +++ b/web/noweb/contrib/partingr/TeXthings @@ -0,0 +1,52 @@ +sub convquotes +{ + local($line)=@_; + local($pre,$mid,$end,$obrace,$cbrace); + + $obrace=index($line,'[[',0); + while($obrace!=-1) + { + $pre=substr($line,0,$obrace); + $cbrace=index($line,']]',$obrace); + while(substr($line,$cbrace+2,1) eq ']') + { $cbrace++; + } + $mid=substr($line,$obrace+2,$cbrace-$obrace-2); + $end=substr($line,$cbrace+2); + $line=$pre . "\\code{}" . &TeXliteral($mid) . "\\edoc{}" . $end; + $obrace=index($line,'[[',0); + } + return $line; +} + +sub escapebslash +{ + local($line)=@_; + + $line=~s/([\\\{\}])/\n\1/g; + $line=~s/\n/\\/g; + return $line; +} + +sub TeXliteral +{ + local($_)=@_; + + s/\\/<\\char92>/g; + s/\}/<\\char125}/g; + s/\{/{\\char123}/g; + s/<\\char/{\\char/g; + s/\{\\char92>/{\\char92}/g; + s/\$/{\\char36}/g; + s/&/{\\char38}/g; + s/#/{\\char35}/g; + s/\^/{\\char94}/g; + s/_/{\\char95}/g; + s/%/{\\char37}/g; + s/~/{\\char126}/g; + s/ /\\ /g; + return $_; +} + +1; + diff --git a/web/noweb/contrib/partingr/addscore.nw b/web/noweb/contrib/partingr/addscore.nw new file mode 100644 index 0000000000..e0e5d9eb79 --- /dev/null +++ b/web/noweb/contrib/partingr/addscore.nw @@ -0,0 +1,183 @@ +\def\musecs{$\mu$secs} +\itemwidth=.25in +@ +\section{Archery Database: {\tt AddScore}} +This perl script adds a score (or scores) to the archery database. +It provides only a basic user interface. + +<<addscore>>= +<<setup>> +<<main program loop>> +<<subroutines>> +@ + +\section{Setting up the database and script} +Because much of the code is shared between this family of scripts, +some of the code is in subroutine files. We [[require]] these. + +<<setup>>= +require "custom" || die "can't open custom routines library"; +require "archsubs" || die "can't open subroutines library"; +@ %def &readrounds &init + +Early versions of this script allowed you to abort an entry by not +typing anything at a prompt. However this became cumbersome when only +the third prompt would let you do this. So we now trap [[SIGINT]] +and point it to the same abort routine. + +<<setup>>= +$SIG{'INT'}='abort'; +@ %def $SIG + +We need know what date it is today (for the default database and +date) so we get this from the operating system via the [[gmtime()]] +call. + +<<setup>>= +$start=time; +($ts,$tmi,$th,$tmd,$tmo,$ty,@junk)=gmtime($start); +@ %def $start $ts $tmi $th $tmd $tmo $ty @junk | $start gmtime() + +Now we read in the rounds database, and open the relevant dbm file for +the archery database. [[&init]] handles the command line options that +relate to the person and year that are asked for. + +<<setup>>= +&readrounds; +do &init; +@ %def | &readrounds &init +\section{The main body of the program} +The main part of the program justs loops asking the user for +a score to be entered and then prompting for another loop. + +Unfortunately, we still treat dates as strings (due to problems in the +conversion of dates to \musecs\ and back) so all this data cannot +be accessed by date order. + +\subsection{Outline of main loop} +<<main...>>= +while(1) +{ + <<date entry and validation>> + <<round entry and validation>> + <<score entry and validation>> + <<create and store dbm entry>> + <<prompt user for another go>> +} +@ + +We close the dbm database explicitly here, just to be on the safe side. +<<main...>>= +dbmclose(scores); +@ +\subsection{Entering the date} +<<date...>>= + dateloop: + while(1) + { + print "Enter date ($tmd-$tmo-$year) - "; + $date=scalar(<STDIN>); + chop $date; + $date=join('-',$tmd,$tmo) unless $date; + if($date=~/(\d{1,2})-(\d{1,2})/) + { + $dt_ntrd=1; + $dindex=sprintf("%02d:%02d:%04d",$1,$2,$year); + $usecs=&retime($1,$2,$year); + + ($xts,$xtmi,$xth,$xtmd,$xtmo,$xty,@junk)=gmtime($usecs); + printf "%02d-%02d-%04d\n",$xtmd,$xtmo,$xty; + + last dateloop; + } + else { print "invalid date\n"; } + } +@ %def dateloop: $date $dt_ntrd $dindex $usecs $xts $xtmi $xth $xtmd $xtmo $xty @junk | STDIN &retime() gmtime() +\subsection{Entering the round} +<<round...>>= + $round=&getround; + ($rtype,@rdists)=split(/,/,@rounds{$round}); + $mult=($rtype='y'?9:10); +@ %def $round @rdists $mult | $rtype @rounds{} &getround +\subsection{Entering the score} +<<score...>>= + until(defined($sc_ntrd)) + { + print 'Enter scores (100,100,100) - '; + $sclist=scalar(<STDIN>); + chop $sclist; + &abort unless $sclist; + @rscores=split(/,/,$sclist); + if(2*$#rscores==$#rdists-1) {$sc_ntrd=1;} + else { print "wrong number of scores\n"; } + for($i=0;$i<=$#rscores;$i++) + { + $ms=@rdists[2*$i]*$mult; + if(@rscores[$i]>$ms) + { printf "invalid: %d>possible ($ms)\n",@rscores[$i]; + $sc_ntrd=$undefined; + } + } + } +@ %def $sclist @rscores $ms $sc_ntrd | STDIN $mult @rdists $undefined +\subsection{Storing the data} +The dbm entries are created in a fairly naive fashion at the moment. +The round name and scores are turned into a CSV string. Future versions +will almost certainly use templates and packing to reduce the amount +of information that is stored. + +<<create...>>= + $entry=join(',',$round,@rscores); + @scores{$dindex}=$entry; +@ %def $entry @scores | $round @rscores $dindex +\subsection{Prompt the user} +Now we prompt the user to see if they want to enter another score. +The default answer (ie just pressing [[RETURN]]) is yes on the +assumption that more than one score will be entered at a time. It +will be changed so that a score entered with today's date will +change the default to no on the assumption that the rest of the +database is up to date. + +<<prompt...>>= + $loopagain=&yesno("Another score",'y'); + last unless $loopagain; + undef $sc_ntrd; +@ %def $loopagain | &yesno() $sc_ntrd +\section{Subroutines} +\subsection{[[retime]]} +This subroutine is supposed to convert a date in dd-mm-yyyy form into +the number of \musecs\ since 1-1-1970, but it sometimes gets it +mysteriously wrong, so we don't use it for indexing yet. + +<<Subroutines>>= +sub retime +{ local($d,$m,$y)=@_; + local($wm,$md,$yd); + + $wm=$m-3; + if($wm<0) { $wm+=12; $y--; } # modified month number + $md=int(30.6*$wm+.5); # month day + $yd=int(365.25*($y-1970)+$md+$d)+60; # actual day number + 86400*$yd; +} +@ %def $d $m $y $wm $md $yd +\section{Things to do} +\itemize +\item +The way the rounds are stored and accessed needs to be changed. +`Compiling' the rounds into a more compact form would save space, +and possibly make things a little faster. + +\item +A better user interface would also be a good idea. Possibly making +the program run under a different screen mode for larger text. + +\item +Adding a confirm just before the data is inserted into the dbm database. + +\enditemize + +\chapter{Variables} +\printindex{ids} +\chapter{Modules} +\printindex{mods} diff --git a/web/noweb/contrib/partingr/email b/web/noweb/contrib/partingr/email new file mode 100644 index 0000000000..d80033763c --- /dev/null +++ b/web/noweb/contrib/partingr/email @@ -0,0 +1,2 @@ +Robert Partington <rjp@browser.org> + diff --git a/web/noweb/contrib/partingr/mm2mx63 b/web/noweb/contrib/partingr/mm2mx63 new file mode 100755 index 0000000000..41e7f3ca67 --- /dev/null +++ b/web/noweb/contrib/partingr/mm2mx63 @@ -0,0 +1,160 @@ +#!/usr/common/bin/perl +do "getopts.pl" || die "$!"; +do Getopts('imnd:l:'); + +do "TeXthings"; + +$sectionref='aaa'; $i=0; +if($opt_l) + { open(LOGFILE,">$opt_l") || die "$!"; } + +while(<>) +{ +# s/\n$//; + s/^@//; +# expand wildcard references here, then process as normal + if(/^(use|defn) (.*)\.{3}$/) + { print LOGFILE "Wildcard `$2...', expands to " if $opt_l; + @matches=grep(/^$2.*/i,split(/¤/,$known)); + if($#matches>0) + { + print LOGFILE "[",join('][',@matches),"] " if $opt_l; + print STDERR "Ambiguous module name `$mod...', line $i\n"; + print STDERR "Matches: [",join('][',@matches),"]\n"; + print STDERR "Using `",@matches[0],"'\n"; + #die "\nAmbiguous module name `$mod...', line $i"; + } + elsif($#matches==-1) + { + die "\nNo match for name `$mod...', line $i"; + } + $mn=@matches[0]; + if($mn=~/\[\[/) { $mn=&convquotes($mn); } + print LOGFILE $mn,"\n" if $opt_l; + $_="$1 $mn"; + } + +# process a <<defn>>= + if(/^defn (.*)$/) + { print LOGFILE "Defining chunk `$1' with macro $sectionref\n" if $opt_l; + $md=$1; $mn=$1; if($opt_n) { $md=~tr/A-Z/a-z/; } + $mt=$md; + $mt=~s/([*+.?{}()])/\\\1/g; + if($known!~/¤$mt/) { $known=$known . "¤$md"; } + @names{$md}=1; $currentmod=$md; + @defines{$md}=@defines{$md} . "\\\\{\\xp\\$sectionref}"; + #if($mn=~/\[\[/) { $mn=&convquotes($mn); } + @lines[$i]="defn $sectionref $mn\n"; $oldref=$sectionref; + $sectionref++; + push(@uses,$i); + $indxing=0; + } +# process a <<use>> + elsif(/^use (.*)$/) + { print LOGFILE "Using chunk `$1' in chunk $oldref\n" if $opt_l; + $md=$1; $mn=$1; if($opt_n) { $md=~tr/A-Z/a-z/; } + $mt=$md; + $mt=~s/([*+.?{}()])/\\\1/g; + if($known!~/¤$mt/) { $known=$known . "¤$md"; } + @reference{$md}=@reference{$md} . "\\\\{\\xp\\$oldref}"; + #if($mn=~/\[\[/) { $mn=&convquotes($mn); } + @lines[$i]="use $oldref $mn\n"; + push(@uses,$i); + $indxing=0; + } +# process identifier information + elsif(/^index (nl|defn |use )(.*)/) + { + if($2 eq '|') { $indxing=1; } + else + { if($2) + { print LOGFILE "Identifier `$2' indexed as " if $opt_l; + if($1 eq "defn " && !$indxing) + { print LOGFILE "defined [$oldref]\n" if $opt_l; + $style=""; + } + else + { print LOGFILE "used [$oldref]\n" if $opt_l; + $style="\\it"; + } + @variables{$2}=@variables{$2} . ",\\thinspace{$style\\xp\\$oldref}"; + } + } + $i--; # don't put this line in the file here + } +# stick the line in the array + else + { @lines[$i]=$_; } + $i++; +} + +if($opt_l) + { + print LOGFILE "\n\nList of modules currently defined\n"; + print LOGFILE join("\n",sort keys(defines)); + print LOGFILE "\n\nList of modules currently referenced\n"; + print LOGFILE join("\n",sort keys(reference)); + print LOGFILE "\n\n"; + } + +foreach(keys(reference)) + { @longlist{$_}=@reference{$_}; } + +foreach(keys(defines)) + { @longlist{$_}=@longlist{$_} . ('%' . @defines{$_}); } + +foreach(@uses) + { + $ref=@lines[$_]; + $ref=~/^(use|defn) (...) (.*)/; + $mn=$3; $dr=$2; $ac=$1; + $defns=@defines{$mn}; + print LOGFILE "Module $mn " if $opt_l; + if($ac eq 'defn') + { $uses=@reference{$mn}; $uses="{$uses}|"; + print LOGFILE "defined at line $_, $uses\n" if $opt_l; + } + else + { $uses=''; + print LOGFILE "referenced at line $_\n" if $opt_l; + } + print LOGFILE "Line $_ modified to `$ac $dr {$defns} $uses$mn'\n" if $opt_l; + $mn=&convquotes($mn); + @lines[$_]="$ac $dr|{$defns}|$uses $mn\n"; + } + +print STDOUT "header tex \n",@lines; + +# now @longlist{MOD} contains a list of all the references to <MOD> +# sort them to make them look pretty + +if($opt_m) + { + print LOGFILE "Making module index...\n" if $opt_l; + print "index mods\n"; + foreach(sort keys(longlist)) + { + $defns=@defines{$_}; + $defns=~s/^,\\\\thinspace//; + print LOGFILE "Module <$_ $defns> ",@reference{$_},"\n" if $opt_l; + # first we print the module name and defining numbers + print "entry {\\LA ",&convquotes($_),"\\ \\xwp{$defns}\\RA}\\quad"; + # now we print the bit after that : assume foot=cmr8 + print "{\\foot\\xtc{",@reference{$_},"}}\n"; + } + print "end index\n"; + } + +if($opt_i) + { + print "index ids\n"; + foreach(sort keys(variables)) + { + $vars=@variables{$_}; + $vars=~s/^,\\thinspace//; + print "entry {\\code ",&TeXliteral($_),"\\edoc} :\\quad",$vars,"\n"; + } + print "end index\n"; + } + +print STDOUT "trailer tex\n"; diff --git a/web/noweb/contrib/partingr/mm2mx64 b/web/noweb/contrib/partingr/mm2mx64 new file mode 100755 index 0000000000..58f26356b5 --- /dev/null +++ b/web/noweb/contrib/partingr/mm2mx64 @@ -0,0 +1,173 @@ +#!/usr/common/bin/perl +do "getopts.pl" || die "$!"; +do Getopts('imnd:l:'); + +do "TeXthings"; + +$sectionref='a'; $i=0; +if($opt_l) + { open(LOGFILE,">$opt_l") || die "$!"; } + +while(<>) +{ +# s/\n$//; + s/^@//; +# expand wildcard references here, then process as normal + if(/^(use|defn) (.*)\.{3}$/) + { print LOGFILE "Wildcard `$2...', expands to " if $opt_l; + @matches=grep(/^$2.*/i,split(/¤/,$known)); + if($#matches>0) + { + print LOGFILE "[",join('][',@matches),"] " if $opt_l; + print STDERR "Ambiguous module name `$mod...', line $i\n"; + print STDERR "Matches: [",join('][',@matches),"]\n"; + print STDERR "Using `",@matches[0],"'\n"; + #die "\nAmbiguous module name `$mod...', line $i"; + } + elsif($#matches==-1) + { + die "\nNo match for name `$mod...', line $i"; + } + $mn=@matches[0]; + if($mn=~/\[\[/) { $mn=&convquotes($mn); } + print LOGFILE $mn,"\n" if $opt_l; + $_="$1 $mn"; + } + +# process a <<defn>>= + if(/^defn (.*)$/) + { print LOGFILE "Defining chunk `$1' with macro $sectionref\n" if $opt_l; + $md=$1; $mn=$1; if($opt_n) { $md=~tr/A-Z/a-z/; } + $mt=$md; + $mt=~s/([*+.?{}()])/\\\1/g; + if($known!~/¤$mt/) { $known=$known . "¤$md"; } + @names{$md}=1; $currentmod=$md; + @defines{$md}=@defines{$md} . "\\\\{\\xp\\z$sectionref}"; + #if($mn=~/\[\[/) { $mn=&convquotes($mn); } + @lines[$i]="defn z$sectionref $mn\n"; $oldref=$sectionref; + $sectionref++; + push(@uses,$i); + $indxing=0; + } +# process a <<use>> + elsif(/^use (.*)$/) + { print LOGFILE "Using chunk `$1' in chunk $oldref\n" if $opt_l; + $md=$1; $mn=$1; if($opt_n) { $md=~tr/A-Z/a-z/; } + $mt=$md; + $mt=~s/([*+.?{}()])/\\\1/g; + if($known!~/¤$mt/) { $known=$known . "¤$md"; } + @reference{$md}=@reference{$md} . "\\\\{\\xp\\z$oldref}"; + #if($mn=~/\[\[/) { $mn=&convquotes($mn); } + @lines[$i]="use z$oldref $mn\n"; + push(@uses,$i); + $indxing=0; + } +# process identifier information + elsif(/^index (nl|defn |use )(.*)/) + { + if($2 eq '|') { $indxing=1; } + else + { if($2) + { print LOGFILE "Identifier `$2' indexed as " if $opt_l; + if($1 eq "defn " && !$indxing) + { print LOGFILE "defined [$oldref]\n" if $opt_l; + $style=""; + } + else + { print LOGFILE "used [$oldref]\n" if $opt_l; + $style="\\it"; + } + @variables{$2}=@variables{$2} . ",\\thinspace{$style\\xp\\z$oldref}"; + } + } + $i--; # don't put this line in the file here + } +# stick the line in the array + else { @lines[$i]=$_; } + $i++; +} + +if($opt_l) + { + print LOGFILE "\n\nList of modules currently defined\n"; + print LOGFILE join("\n",sort keys(defines)); + print LOGFILE "\n\nList of modules currently referenced\n"; + print LOGFILE join("\n",sort keys(reference)); + print LOGFILE "\n\n"; + } + +foreach(keys(reference)) + { @longlist{$_}=@reference{$_}; } + +foreach(keys(defines)) + { @longlist{$_}=@longlist{$_} . ('%' . @defines{$_}); } + +foreach(@uses) + { + $ref=@lines[$_]; + $ref=~/^(use|defn) (z[a-z]+) (.*)/; + $mn=$3; $dr=$2; $ac=$1; + $defns=@defines{$mn}; + print LOGFILE "Module $mn " if $opt_l; + if($ac eq 'defn') + { $uses=@reference{$mn}; $uses="{$uses}|"; + print LOGFILE "defined at line $_, $uses\n" if $opt_l; + } + else + { $uses=''; + print LOGFILE "referenced at line $_\n" if $opt_l; + } + print LOGFILE "Line $_ modified to `$ac $dr {$defns} $uses$mn'\n" if $opt_l; + $mn=&convquotes($mn); + @lines[$_]="$ac $dr|{$defns}|$uses $mn\n"; + } + +print STDOUT "header tex \n",@lines; + +# now @longlist{MOD} contains a list of all the references to <MOD> +# sort them to make them look pretty + +if($opt_m) + { + print LOGFILE "Making module index...\n" if $opt_l; + print "index mods\n"; + foreach(sort keys(longlist)) + { + $defns=@defines{$_}; + $defns=~s/^,\\\\thinspace//; + print LOGFILE "Module <$_ $defns> ",@reference{$_},"\n" if $opt_l; + # first we print the module name and defining numbers + print "entry {\\LA ",&convquotes($_),"\\ \\xwp{$defns}\\RA}\\quad"; + # now we print the bit after that : assume foot=cmr8 + print "{\\foot\\xtc{",@reference{$_},"}}\n"; + } + print "end index\n"; + } + +if($opt_i) + { + print "index ids\n"; + foreach(sort keys(variables)) + { + $vars=@variables{$_}; + $vars=~s/^,\\thinspace//; + print "entry {\\code ",&TeXliteral($_),"\\edoc} :\\quad",$vars,"\n"; + } + print "end index\n"; + } + +print STDOUT "trailer tex\n"; + +sub usage_info +{ + local($line)=@_; + @ixrefs=sort(split(/%/,@reference{$line})); + if($#ixrefs==-1) + { return "This code is never referenced. It may be a root module.";} + elsif($#ixrefs==0) + { return "This code is used in section ",@ixrefs[0]; } + else + { $lastref=pop(@ixrefs); + return "This code is used in sections ",join(",\\,",@ixrefs)," and $lastref"; + } +} diff --git a/web/noweb/contrib/partingr/mm2mx65 b/web/noweb/contrib/partingr/mm2mx65 new file mode 100755 index 0000000000..7c7809b4b8 --- /dev/null +++ b/web/noweb/contrib/partingr/mm2mx65 @@ -0,0 +1,179 @@ +#!/usr/common/bin/perl +do "getopts.pl" || die "$!"; +do Getopts('imnd:l:'); + +do "TeXthings"; + +$sectionref='aaa'; $i=0; +if($opt_l) + { open(LOGFILE,">$opt_l") || die "$!"; } + +while(<>) +{ +# s/\n$//; + s/^@//; + if(/@</) + { ($pre,$mid,$end)=&convxrefs($_); + @lines[$i]=$pre; $i++; + $_=$mid; # need to expand the module name here + } +# expand wildcard references here, then process as normal + if(/^(use|defn) (.*)\.{3}$/) + { print LOGFILE "Wildcard `$2...', expands to " if $opt_l; + @matches=grep(/^$2.*/i,split(/¤/,$known)); + if($#matches>0) + { + print LOGFILE "[",join('][',@matches),"] " if $opt_l; + print STDERR "Ambiguous module name `$mod...', line $i\n"; + print STDERR "Matches: [",join('][',@matches),"]\n"; + print STDERR "Using `",@matches[0],"'\n"; + #die "\nAmbiguous module name `$mod...', line $i"; + } + elsif($#matches==-1) + { + die "\nNo match for name `$mod...', line $i"; + } + $mn=@matches[0]; + if($mn=~/\[\[/) { $mn=&convquotes($mn); } + print LOGFILE $mn,"\n" if $opt_l; + $_="$1 $mn"; + } + +# process a <<defn>>= + if(/^defn (.*)$/) + { print LOGFILE "Defining chunk `$1' with macro $sectionref\n" if $opt_l; + $md=$1; $mn=$1; if($opt_n) { $md=~tr/A-Z/a-z/; } + $mt=$md; + $mt=~s/([*+.?{}()])/\\\1/g; + if($known!~/¤$mt/) { $known=$known . "¤$md"; } + @names{$md}=1; $currentmod=$md; + @defines{$md}=@defines{$md} . "\\\\{\\xp\\$sectionref}"; + #if($mn=~/\[\[/) { $mn=&convquotes($mn); } + @lines[$i]="defn $sectionref $mn\n"; $oldref=$sectionref; + $sectionref++; + push(@uses,$i); + $indxing=0; + } +# process a <<use>> + elsif(/^use (.*)$/) + { print LOGFILE "Using chunk `$1' in chunk $oldref\n" if $opt_l; + $md=$1; $mn=$1; if($opt_n) { $md=~tr/A-Z/a-z/; } + $mt=$md; + $mt=~s/([*+.?{}()])/\\\1/g; + if($known!~/¤$mt/) { $known=$known . "¤$md"; } + @reference{$md}=@reference{$md} . "\\\\{\\xp\\$oldref}"; + #if($mn=~/\[\[/) { $mn=&convquotes($mn); } + @lines[$i]="use $oldref $mn\n"; + push(@uses,$i); + $indxing=0; + } +# process identifier information + elsif(/^index (nl|defn |use )(.*)/) + { + if($2 eq '|') { $indxing=1; } + else + { if($2) + { print LOGFILE "Identifier `$2' indexed as " if $opt_l; + if($1 eq "defn " && !$indxing) + { print LOGFILE "defined [$oldref]\n" if $opt_l; + $style=""; + } + else + { print LOGFILE "used [$oldref]\n" if $opt_l; + $style="\\it"; + } + @variables{$2}=@variables{$2} . ",\\thinspace{$style\\xp\\$oldref}"; + } + } + $i--; # don't put this line in the file here + } +# stick the line in the array + else + { @lines[$i]=$_; } + $i++; + if($end) { @lines[$i]=$end; $i++; undef $end; } +} + +if($opt_l) + { + print LOGFILE "\n\nList of modules currently defined\n"; + print LOGFILE join("\n",sort keys(defines)); + print LOGFILE "\n\nList of modules currently referenced\n"; + print LOGFILE join("\n",sort keys(reference)); + print LOGFILE "\n\n"; + } + +foreach(keys(reference)) + { @longlist{$_}=@reference{$_}; } + +foreach(keys(defines)) + { @longlist{$_}=@longlist{$_} . ('%' . @defines{$_}); } + +foreach(@uses) + { + $ref=@lines[$_]; + $ref=~/^(use|defn) (...) (.*)/; + $mn=$3; $dr=$2; $ac=$1; + $defns=@defines{$mn}; + print LOGFILE "Module $mn " if $opt_l; + if($ac eq 'defn') + { $uses=@reference{$mn}; $uses="{$uses}|"; + print LOGFILE "defined at line $_, $uses\n" if $opt_l; + } + else + { $uses=''; + print LOGFILE "referenced at line $_\n" if $opt_l; + } + print LOGFILE "Line $_ modified to `$ac $dr {$defns} $uses$mn'\n" if $opt_l; + $mn=&convquotes($mn); + @lines[$_]="$ac $dr|{$defns}|$uses $mn\n"; + } + +print STDOUT "header tex \n",@lines; + +# now @longlist{MOD} contains a list of all the references to <MOD> +# sort them to make them look pretty + +if($opt_m) + { + print LOGFILE "Making module index...\n" if $opt_l; + print "index mods\n" if $opt_l; + foreach(sort keys(longlist)) + { + $defns=@defines{$_}; + $defns=~s/^,\\\\thinspace//; + print LOGFILE "Module <$_ $defns> ",@reference{$_},"\n" if $opt_l; + # first we print the module name and defining numbers + print "entry {\\LA ",&convquotes($_),"\\ \\xwp{$defns}\\RA}\\quad"; + # now we print the bit after that : assume foot=cmr8 + print "{\\foot\\xtc{",@reference{$_},"}}\n"; + } + print "end index\n"; + } + +if($opt_i) + { + print "index ids\n"; + foreach(sort keys(variables)) + { + $vars=@variables{$_}; + $vars=~s/^,\\thinspace//; + print "entry {\\code ",&TeXliteral($_),"\\edoc} :\\quad",$vars,"\n"; + } + print "end index\n"; + } + +print STDOUT "trailer tex\n"; + +sub convxrefs + { + local($l)=@_; + local($found,$output); + $found=index($l,'@<'); + $lost=index($l,'@>',$found); + $pre=substr($l,0,$found) . "\n"; # before the use + $mid="use " . substr($l,$found+2,$lost-$found-2) . "\n"; + $end="text " . substr($l,$lost+2); + substr($l,$found,$lost+2-$found)=""; + return $pre,$mid,$end; + } diff --git a/web/noweb/contrib/partingr/mm2tex b/web/noweb/contrib/partingr/mm2tex new file mode 100755 index 0000000000..79fd7a9c3d --- /dev/null +++ b/web/noweb/contrib/partingr/mm2tex @@ -0,0 +1,41 @@ +#!/usr/common/bin/perl +do "TeXthings" || die "$!"; +print "\\input nwmac "; +while(<>) +{ + if(/^@begin code (.*)$/) { print "\\nwbegincode{$1}"; $code=1; $text=5; } + elsif(/^@end code/) { print "\\nwendcode{}\\filbreak$defing"; $code=0; } + elsif(/^@begin docs (.*)$/) { print "\\nwbegindocs{$1}"; $text=0; $textmode=0; } + elsif(/^@end docs/) { print "\\nwenddocs{}"; } + elsif(/^@text (.*)$/) + { $text+=length $1; + if($code==1) { print &escapebslash($1); } + elsif($quoting==1) { print &TeXliteral($1); } + else { print $1; } + $textmode=1 if $text>0; + } + elsif(/^@nl$/) + { if($code==0) + { if($text==0) + { if($textmode==1) { print "\\nwdocspar\\noindent\n"; } + else { print "\n"; } + $textmode=1; $text=1; + } + else { print "\n"; } + } + elsif($quoting) { print "\\nwnewline"; } + else { if($text>0) { print "\n"; } } + } + elsif(/^@defn (.*)$/) + { $name=$1; + print "\\moddef{",&convquotes($name),"}\\",@defns{$name},"endmoddef"; + @defns{$name}='plus'; + } + elsif(/^@use (.*)$/) + { print "\\LA{}",&convquotes($1),"\\RA{}"; } + elsif(/^@quote$/) { $quoting=1; print "{\\tt "; } + elsif(/^@endquote$/) { $quoting=0; print "}"; $textmode=0; } + elsif(/^@file (.*)$/) { $filename=$1; print "\\filename{$filename}"; } + elsif(/^@literal (.*)$/) { print "$1"; } +} +print "\\bye\n"; diff --git a/web/noweb/contrib/partingr/mx2tex31 b/web/noweb/contrib/partingr/mx2tex31 new file mode 100755 index 0000000000..e7e3ef674d --- /dev/null +++ b/web/noweb/contrib/partingr/mx2tex31 @@ -0,0 +1,130 @@ +#!/usr/common/bin/perl +do "TeXthings"; +do "getopts.pl" || die "$!"; +do Getopts('n:idf:hqst'); + +if($opt_h) + { + print STDERR <<ENDOFHELP; + +Usage: mx2tex [options] + +mx2tex takes a file created by mm2mx and converts it into TeX. + +Options: d debugging information + f <name> use this file for the job (from <name>.mx to <name>.tex/texnique) + h this help text + i create the indexes + n <name> use this as the name for the output files + default: woven + q operate quietly (no output) + s only have full list of defines for first chunk +ENDOFHELP + exit; + } + +unless($opt_q) + { + print STDERR "mx2tex version 3, 1994 by Rob Partington\n"; + if($opt_d) { push(@options,"debugging"); } + if($opt_f) { push(@options,"file:$opt_f"); } + if($opt_i) { push(@options,"indexes"); } + if($opt_n) { push(@options,"name:$opt_n"); } + if($opt_s) { push(@options,"first define"); } + if($opt_t) { push(@options,"force write"); } + print STDERR "Options:",sort(join(' + ',@options)),"\n"; + undef @options; + } + +$macrofile="nwindex"; if($opt_d) { $macrofile="nwidxmac"; } + +$filename=$opt_f; if($opt_n) { $filename=$opt_n; } + +open(TEX,">$filename.texnique") || die "$!"; + +if($opt_f) { push(@ARGV,"$filename.markup"); } + +unless(-e "$filename.tex" && !$opt_t) + { + open(TEXCNTL,">$filename.tex") || die "$!"; + print TEXCNTL <<EOTEX; +\\input $macrofile +\\def\\defined{}\\init\\output={\\plainoutput\\global\\subpageref=97} +{\\def\\shipout{\\message{[p\\the\\pageno]}\\setbox0} +\\input \\jobname.texnique \\vfill\\supereject} +\\init{\\gdef\\passtwo{}\\input \\jobname.texnique } +\\end +EOTEX + close(TEXCNTL); + } + + +$code=0; $text=1; $ignore=0; +whileloop: +while(<>) +{ + if(/^begin code (.*)$/) { $delayed="\\nwbegincode{$1}"; $code=1; $text=5; } + elsif(/^end code/) { print TEX "\\nwendcode{}\\filbreak$defing"; $code=0; } + elsif(/^begin docs (.*)$/) { print TEX "\\nwbegindocs{$1}"; $text=0; $textmode=0; } + elsif(/^end docs/) { print TEX "\\nwenddocs{}"; } + elsif(/^text (.*)$/) + { $text+=length $1; + if($code==1) { print TEX &escapebslash($1); } + elsif($quoting==1) { print TEX &TeXliteral($1); } + else { print TEX $1; } + $textmode=1 if $text>0; + } + elsif(/^nl$/) + { if($code==0) + { if($text==0) + { if($textmode==1) { print TEX "\\nwdocspar\\noindent\n"; } + else { print TEX "\n"; } + $textmode=1; $text=1; + } + else { print TEX "\n"; } + } + elsif($quoting) { print TEX "\\nwnewline"; } + else { if($text>0) { print TEX "\n"; } } + } + elsif(/^defn ([a-z]+)\|(.*)\|(.*)\| (.*)$/) + { $xref=$1; $name=$2; + $defing="\\makeref{$1}"; + $deflist=$2; $uselist=$3; + if($opt_s && @defns{$name} eq 'plus') + { + if(($firstr=index($deflist,'}\\'))!=-1) + { $deflist=substr($deflist,0,$firstr) . '}}'; } + if(($firstr=index($uselist,'}\\'))!=-1) + { $uselist=substr($uselist,0,$firstr) . '}}'; } + } + + print TEX "$delayed\\def\\list$deflist\\def\\ulist$uselist", + "\\moddef{\\xp{\\$1}}{",&convquotes($4),"}", + "\\inmodname\\",@defns{$name},"endmoddef"; + $ignore=1; + @defns{$name}='plus'; + } + elsif(/^use ([a-z]+)\|(.*)\| (.*)$/) + { + $deflist=$2; + if($opt_s) + { $deflist=~s/^({\\\\\{.*\}).*\}$/\1\}/;} + print TEX "\\LA{}",&convquotes($3),"\\def\\list",$deflist,"\\inmodname\\RA{}"; + } + elsif(/^quote$/) { $quoting=1; print TEX "{\\tt "; } + elsif(/^endquote$/) { $quoting=0; print TEX "}"; $textmode=0; } + elsif(/^file (.*)$/) { print TEX "\\filename{$1}"; } + elsif(/^literal (.*)$/) { print TEX "$1"; } + elsif(/^entry (.*)$/ && $opt_i) { print INDEX "$1\n"; } + elsif(/^index (.*)$/ && $opt_i) + { print STDERR "creating index `$filename.$1'\n" unless $opt_q; + open(INDEX,">$filename.$1"); + print INDEX "\\begingroup\\parindent=0pt\\obeylines%\n"; + } + elsif(/^end index$/ && $opt_i) + { + print INDEX "\\endgroup\n"; + close(INDEX); + } +} +print TEX "\\passfin\n"; diff --git a/web/noweb/contrib/partingr/nwindex.tex b/web/noweb/contrib/partingr/nwindex.tex new file mode 100644 index 0000000000..bff95acd7b --- /dev/null +++ b/web/noweb/contrib/partingr/nwindex.tex @@ -0,0 +1,134 @@ +\ifx\NwInDeXLoaded\undefined\relax\else\endinput\fi +\def\NwInDeXLoaded{} +\ifx\moddef\undefined\input nwmac \fi + +% last minute panic comments + +% font for cross references +\font\foot=cmr8 + +% \init re-initialises stuff for each pass +\def\initnwindex{\global\pageno=1\global\subpageref=97} +\let\init=\initnwindex + +% macros to place information in the left margin +% from The TeXBook +\def\strutdepth{\dp\strutbox} +\def\marginalnote#1{\strut\vadjust{\kern-\strutdepth\specialstar{#1}}} +\def\specialstar#1{\vtop to \strutdepth{ + \baselineskip\strutdepth + \vss\llap{#1\quad}\null}} + +% redefine moddef to take an argument (this section's code) +\def\moddef#1{\vskip3pt\leavevmode\marginalnote{{\bf#1}}\kern-\codemargin \LA} + +% make the contents file immediately open +\immediate\openout\cont=\contentsfile +\immediate\write\cont{\string\catcode`\string\@=11}% a hack to make contents + % take stuff in plain.tex + +% redefine \nwendcode to provide the \aftercode hook +\def\nwendcode{\aftercode{}\endgroup} + +% \subpageref is the letter part of the code +\newcount\subpageref \subpageref=97 + +% an entry in an index (AFAIK this is unused) +\def\index#1#2{\line{\hskip.5in{\vbox{{\ignorespaces#1}\hskip4pt #2.}\hss}}} + +% advance the \subpageref, going to 'A' if 'z' was the last one +\def\nextref{\global\advance\subpageref by 1\ifnum\subpageref=123\subpageref=65\fi} + +% \xp expands to #1 if #1 is defined or \relax if it is not +\def\xp#1{\ifx#1\undefined\relax\else#1\fi} + +% AFAIK this is unused +\let\ag=\aftergroup + +% \xref is called each time a code is defined - sort of a hook +\def\xref#1{} + +% \defined is a defined macro! +\def\defined{} + +% AFAIK this is unused +\def\killpage{\setbox0=\box255\deadcycles=0 \global\subpageref=97\global\advance\pageno by 1} + +% a big macro for the end of a pass +\def\passfin{% + \ifx\passtwo\defined + \write\cont{}% ensure that the contents file isn't empty after pass2 + \closeout\cont + \vfil\eject\pageno=-1 % new page causes contents to be really closed + \topofcontents\readcontents\botofcontents + \else + \vfill\supereject + \fi +} + +% define a reference on pass one only +\def\makeref#1{\ifx\passtwo\undefined + \edef\next{\gdef\csname#1\endcsname{\folio\char\the\subpageref}} + \next\xref{\csname#1\endcsname}\nextref\fi} + +% list macros from appendix D, The TeXBook +\toksdef\ta=0 \toksdef\tb=2 % +\long\def\leftappenditem#1\to#2{\ta={\\{#1}}\tb=\expandafter{#2}% + \edef#2{\the\ta\the\tb}} + +\def\lop#1\to#2{\expandafter\lopoff#1\lopoff#1#2} + +\long\def\lopoff\\#1#2\lopoff#3#4{\def#4{#1}\def#3{#2}} + +\def\cardinality#1\to#2{#2=0 \long\def\\##1{\global\advance#2 by 1}#1} +\def\list{} +\def\ulist{} +\newcount\listlength + +% write out a nicely formatted list +\def\writeoutlist#1#2#3{\cardinality#1\to\listlength +\def\\##1{\advance\listlength by -1\relax##1\ifnum\listlength>1 #2% +\else\ifnum\listlength=1 #3\fi\fi}#1} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \prettylist{list}{one}{many}{between}{end} +% 1 -> {one}E1 +% 2 -> {many}E1{end}E2 +% 3 -> {many}E1{between}E2{end}E3 +% etc. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\prettylist#1#2#3#4#5{% +\cardinality#1\to\listlength\ifnum\listlength>1 #3\else#2\fi +\writeoutlist{#1}{#4}{#5}} + +\def\writeplain#1{\prettylist#1{}{}{,\thinspace{}}{,\thinspace{}}} +\def\writelist#1{\prettylist#1{}{}{,\thinspace{}}{ and }} +\def\writesections#1{\prettylist#1{section }{sections }{,\thinspace }{\ and }} + +\def\usage#1{This code is used in \writesections#1.} +\def\defs#1{\cardinality#1\to\listlength\ifnum\listlength>1{% +{\lop#1\to\hello This definition is continued in \writesections#1.}} +\fi} + +\def\first#1{\ifx\list\defined\else\ {{\lop#1\to\hello \hello}}\fi} +\def\thiscode#1{\ifx#1\defined Root module (never referenced in this document)% +\else\usage#1\fi} + +% \thiscode and \writeplain can't take direct lists as parameters, so +% fake it with these two +\def\xtc#1{\def\list{#1}\thiscode\list} +\def\xwp#1{\def\list{#1}\writeplain\list} + +% hooks for cross-references +\def\inmodname{\ifx\list\defined\else\thinspace{\foot\first\list}\fi} +\def\beforecode{} +\def\aftercode{\vbox{\kern3pt\hbox{{\foot\defs\list}}\kern-2pt\hbox{{\foot\thiscode\ulist}}\kern3pt}} + +% \printindex - check for file existing before \input'ing it +\newwrite\filecheck +\def\printindex#1{\openin\filecheck=\jobname.#1 +\ifeof\filecheck\message{[#1 file missing]}{\tt noweb} has no index `#1'\else +\closein\filecheck\input \jobname.#1 +\fi +} diff --git a/web/noweb/contrib/partingr/nwnweave b/web/noweb/contrib/partingr/nwnweave new file mode 100755 index 0000000000..d78a2af3af --- /dev/null +++ b/web/noweb/contrib/partingr/nwnweave @@ -0,0 +1,2 @@ +#!/bin/csh +markup $1.nw | mm2mx63 -n -i -m | mx2tex31 -i -n $1 -s diff --git a/web/noweb/contrib/partingr/nwtangle b/web/noweb/contrib/partingr/nwtangle new file mode 100755 index 0000000000..b9b115bd01 --- /dev/null +++ b/web/noweb/contrib/partingr/nwtangle @@ -0,0 +1,2 @@ +#!/bin/csh +markup $1.nw | nt -R'$2' diff --git a/web/noweb/contrib/partingr/nwweave b/web/noweb/contrib/partingr/nwweave new file mode 100755 index 0000000000..0108ccf4aa --- /dev/null +++ b/web/noweb/contrib/partingr/nwweave @@ -0,0 +1,2 @@ +#!/bin/csh +markup $1.nw | mm2mx63 -i -m | mx2tex31 -i -n $1 -s diff --git a/web/noweb/contrib/partingr/xpand b/web/noweb/contrib/partingr/xpand new file mode 100755 index 0000000000..772375ff54 --- /dev/null +++ b/web/noweb/contrib/partingr/xpand @@ -0,0 +1,20 @@ +#!/usr/common/bin/perl +while(<>) +{ +# expand wildcard references here, then process as normal + if(/^@(use|defn) (.*)$/) + { + $action=$1; + if($2=~/\.{3}$/) + { @matches=grep(/^$2.*/i,split(/¤/,$known)); + if($#matches>0) + { die "\nAmbiguous module name `$mod...'"; } + elsif($#matches==-1) + { die "\nNo match for name `$mod...',line $i "; } + else + { $mn=@matches[0]; $_="@$action $mn\n"; } + } + else { $known=$known . "¤$2"; } + } + print STDOUT $_; +} diff --git a/web/noweb/contrib/rsc/README b/web/noweb/contrib/rsc/README new file mode 100644 index 0000000000..76e57bbd0c --- /dev/null +++ b/web/noweb/contrib/rsc/README @@ -0,0 +1 @@ +These scripts support use of Noweb under Plan 9. diff --git a/web/noweb/contrib/rsc/email b/web/noweb/contrib/rsc/email new file mode 100644 index 0000000000..2e53c38912 --- /dev/null +++ b/web/noweb/contrib/rsc/email @@ -0,0 +1 @@ +Russ Cox <rsc@plan9.bell-labs.com> diff --git a/web/noweb/contrib/rsc/rc/cpif.nw b/web/noweb/contrib/rsc/rc/cpif.nw new file mode 100644 index 0000000000..e4bb15e512 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/cpif.nw @@ -0,0 +1,47 @@ +<<cpif>>= +#!/bin/rc +# +# Based on shell script by Norman Ramsey +# Translated from sh to rc by Russ Cox +# +# see /sys/src/cmd/noweb/COPYRIGHT +# +# cpif [ -eq -ne ] file... +# copy standard input to each of the named files +# if new * old is true or old doesn't exist; +# * defaults to -ne + +rfork en + +# set -x +op=-ne +if(~ $1 -eq -ne){ + op=$1 + shift +} +if(~ $1 -* || ~ $#* 0) { + echo 'Usage: '$0' [-eq -ne] file...' >[1=2] + exit usage +} + +new=$(mktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } + +# trap 'rm -f $new; exit 1' 1 2 15 # clean up files + +cat >$new +for(i) { + cmp -s $new $i + + switch($op^$status) { + # differed but we wanted same + case -eq*differ + ; + # didn't differ but we wanted different + case -ne + ; + # got what we wanted or perhaps an error + case * + cp $new $i + } +} +rm -f $new diff --git a/web/noweb/contrib/rsc/rc/emptydefn.nw b/web/noweb/contrib/rsc/rc/emptydefn.nw new file mode 100644 index 0000000000..33901008d0 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/emptydefn.nw @@ -0,0 +1,10 @@ +<<emptydefn>>= +#!/bin/rc +# +# notangle filter that makes the definition of an empty chunk @<<>>= +# stand for a continuation of the previous chunk definition. + +awk 'BEGIN { lastdefn = "@defn " } +/^@defn $/ { print lastdefn; next } +/^@defn / { lastdefn = $0 } +{ print }' $* diff --git a/web/noweb/contrib/rsc/rc/mkfile b/web/noweb/contrib/rsc/rc/mkfile new file mode 100644 index 0000000000..a2165adb42 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/mkfile @@ -0,0 +1,28 @@ + +RCTARG=cpif noroots noweave notangle nountangle +RCLIBTARG=emptydefn noidx noindex toascii tohtml totex unmarkup noweb +AWKTARG=noidx.awk noindex.awk toascii1.awk toascii2.awk tohtml.awk + +%: %.nw + notangle -R$stem $stem.nw >$stem + +%2.awk: %.nw + notangle -R$target $stem.nw >$target +%1.awk: %.nw + notangle -R$target $stem.nw >$target +%.awk: %.nw + notangle -R$target $stem.nw >$target + +TARG=$RCTARG $RCLIBTARG $AWKTARG + +default:V: $TARG + +clean:V: + rm $TARG + +install:V: + cp $RCTARG /rc/bin + cp $RCLIBTARG /sys/lib/noweb/bin/rc + chmod 775 /rc/bin/^($RCTARG) + chmod 775 /sys/lib/noweb/bin/rc/^($RCLIBTARG) + cp $AWKTARG /sys/lib/noweb diff --git a/web/noweb/contrib/rsc/rc/noidx.nw b/web/noweb/contrib/rsc/rc/noidx.nw new file mode 100644 index 0000000000..672badb84f --- /dev/null +++ b/web/noweb/contrib/rsc/rc/noidx.nw @@ -0,0 +1,432 @@ +\documentstyle[noweb]{article} +\pagestyle{noweb} +\begin{document} +@ +\section{Cross-reference and index support} +Here is is. +<<noidx>>= +#!/bin/rc +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com + +delay=0 +anchordist=0 +while(! ~ $#* 0) { + switch($1){ + case -delay + delay=1 + case -docanchor + anchordist=$2 + shift + case * + echo 'cannot happen -- '^$1^' passed to noidx' >[1=2] + exit cannothappen + } + shift +} +awk -f /sys/lib/noweb/noidx.awk -v 'delay='$delay -v 'anchordist='$anchordist +@ +<<noidx.awk>>= +<<functions>> +BEGIN { <<initialization>> nextline = 0 } +<<first pass>> +{ lines[nextline] = $0; nextline++ } +END { + for (i = 0; i < nextline; i ++) { + <<second pass over [[lines[i]]]>> + delete lines[i] + } + if (!delay) <<write trailers>> +} +@ %def lines nextline +<<initialization>>= +curfile = "standard input?" +lastchunkbegin = "never any chunks?" ; +<<initialization>>= +allchunks[0] = 0 ; allidents[0] = 0 ; indexlabels[0] = 0 +defanchors[0] = 0 ; uses[0] = 0 ; anchorlabel[0] = 0 ; indexanchorlabel[0] = 0 +@ %def allchunks allidents indexlabels defanchors uses anchorlabel indexancho +label +<<first pass>>= +/^@file / { curfile = uniqueid(substr($0, 7)) } +/^@begin / { lastchunkbegin = $0 } +/^@end docs / { if (anchordist > 0) <<insert and set [[lastanchorlabel]]>> } +/^@end code / { lastanchorlabel = "" } +@ %def curfile lastchunkbegin lastanchorlabel +<<first pass>>= +/^@defn / { arg = substr($0, 7) + allchunks[arg] = 1 + lastdefnlabel = newdefnlabel(arg) + slipin("@xref label " lastdefnlabel) + if (lastanchorlabel == "") lastanchorlabel = lastdefnlabel + if (anchorlabel[arg] == "") anchorlabel[arg] = lastanchorlabel + addlabel(defanchors, arg, lastanchorlabel) + addud(chunkud, "defn", arg, lastanchorlabel) + thisusecount = 0 + } +/^@use / { if (lastchunkbegin ~ /^@begin code /) { + arg = substr($0, 6) + allchunks[arg] = 1 + slipin("@xref label " lastdefnlabel "-u" (++thisusecount)) + addlabel(uses, arg, lastanchorlabel) + addud(chunkud, "use", arg, lastanchorlabel) + } + } +@ %def allchunks lastdefnlabel +<<first pass>>= +/^@index use / { arg = substr($0, 12) + allidents[arg] = 1 + if (lastanchorlabel != "") addud(indexud, "use", arg, lastan +horlabel) + } +/^@index defn / { arg = substr($0, 13) + <<handle index definition of [[arg]]>> + } +/^@index localdefn / { arg = substr($0, 18) + <<handle index definition of [[arg]]>> + } +<<handle index definition of [[arg]]>>= +allidents[arg] = 1 +if (lastanchorlabel != "") { + l = lastanchorlabel +} else { + l = newdocslabel() + slipin("@xref label " l) +} +addud(indexud, "defn", arg, l) +if (indexanchorlabel[arg] == "") indexanchorlabel[arg] = l +slipin("@xref ref " l) # bug fix +@ %def allidents indexanchorlabel +The bug fix\label{multi-def-bug} +alluded to above occurs when there are multiple definitions of an identifier. +In this case, we can't just use [[indexanchorlabel[id]]], because that refers +nly to +the first definition. In the {\TeX} back end, that leads to bogus +tags like \hbox{\it x \small\rm 7b 7b 7b} instead of \hbox{\it x +\small\rm 7b 9 12a}; the first tag is repeated again and again. +Because the tag for the current [[@defn]] is lost by the time pass~2 +rolls around, we have to slip it in on pass~1. +@ +<<insert and set [[lastanchorlabel]]>>= +{ n = anchordist + lastanchorlabel = newdocslabel() + for(i = nextline - 1; i >= 0; i--) { + if (n == 0 || lines[i] ~ /^@begin docs /) { + insertafter(i, "@xref label " lastanchorlabel) + i = -1 # cause loop to terminate + } else if (lines[i] == "@nl") { + n-- + } + } +} +<<functions>>= +function insertafter(i, s, n) { + for(n = nextline++; n - 1 > i; n--) lines[n] = lines[n-1] + lines[n] = s +} +@ +In the awk version, [[slipin]] is called {\em before} the current line is +added to [[lines]]. +<<functions>>= +function slipin(s) { + lines[nextline++] = s +} +<<initialization>>= +thesedefns[0] = 0; theseuses[0] = 0 ; +<<second pass over [[lines[i]]]>>= +line = lines[i] +if (line ~ /^@begin /) { + if (delay && lastchunkbegin == line) <<write trailers>> + print line + for (x in thesedefns) delete thesedefns[x] + for (x in theseuses) delete theseuses[x] + thischunk = "" +} else if (line ~ /^@defn /) { + thischunk = substr(line, 7) + printf "@xref ref %s\n", anchorlabel[thischunk] + print line +} else if (line ~ /^@use /) { + arg = substr(line, 6) + printf "@xref ref %s\n", (anchorlabel[arg] == "" ? "nw@notdef" : anchorlab +l[arg]) + print line +} else if (line ~ /^@index defn /) { + arg = substr(line, 13) + thesedefns[arg] = 1 + # no xref ref because of bug fix + # if (indexanchorlabel[arg] != "") + # printf "@xref ref %s\n", indexanchorlabel[arg] + print line +} else if (line ~ /^@index localdefn /) { + arg = substr(line, 18) + thesedefns[arg] = 1 + # no xref ref because of bug fix + # if (indexanchorlabel[arg] != "") + # printf "@xref ref %s\n", indexanchorlabel[arg] + print line +} else if (line ~ /^@index use /) { + arg = substr(line, 12) + theseuses[arg] = 1 + if (indexanchorlabel[arg] != "") + printf "@xref ref %s\n", indexanchorlabel[arg] + print line +} else if (line ~ /^@end code/) { + <<write cross-reference>> + print line +} else if (line ~ /^@text /) { + # grotesque hack to get indexes in HTML + if (thischunk == "") { # docs mode + arg = substr(line, 7) + if (arg == "<nowebchunks>") lognowebchunks() + else if (arg == "<nowebindex>") lognowebindex() + else print line + } else { + print line + } +} else { + print line +} +@ %def thesedefns theseuses +The case of the [[@index defn]] is the one case where we don't emit a +reference, because the reference has to go in earlier. See +page~\pageref{multi-def-bug} for an explanation. +<<write cross-reference>>= +defout[thischunk]++ +<<write index cross-reference>> +if (defout[thischunk] == 1) {<<write chunk cross-reference>>} +if (defout[thischunk] > 1) + printf "@xref prevdef %s\n", listget(defanchors[thischunk], defout[thischunk +-1) +if (defout[thischunk] < defcount[thischunk]) + printf "@xref nextdef %s\n", listget(defanchors[thischunk], defout[thischunk ++1) +<<write index cross-reference>>= +for (x in thesedefns) + delete theseuses[x] +delete thesedefns[0] +n = alphasort(thesedefns) +if (n > 0) { + print "@index begindefs" + for (j = 0; j < n; j++) { + m = split(indexud[sorted[j]], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@index isused %s\n", substr(a[k], 5, length(a[k])-5) + printf "@index defitem %s\n", sorted[j] + delete sorted[j] + } + print "@index enddefs" +} +<<write index cross-reference>>= +delete theseuses[0] +n = alphasort(theseuses) +if (n > 0) { + print "@index beginuses" + for (j = 0; j < n; j++) { + m = split(indexud[sorted[j]], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^defn/) + printf "@index isdefined %s\n", substr(a[k], 6, length(a[k])-6) + printf "@index useitem %s\n", sorted[j] + delete sorted[j] + } + print "@index enduses" +} +<<write chunk cross-reference>>= +if (defcount[thischunk] > 1) { + print "@xref begindefs" + n = split(defanchors[thischunk], a) + for (j = 2; j <= n; j++) printf "@xref defitem %s\n", a[j] + print "@xref enddefs" + +} +if (uses[thischunk] != "") { + print "@xref beginuses" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) printf "@xref useitem %s\n", a[j] + print "@xref enduses" +} else { + printf "@xref notused %s\n", thischunk +} +<<functions>>= +function newdefnlabel(arg, label) { + defcount[arg] = defcount[arg] + 1 + label = "NW" curfile "-" uniqueid(arg) "-" alphacode(defcount[arg]) + return label +} +@ %def newdefnlabel +<<initialization>>= +defcount[0] = 0 ; +<<functions>>= +function newdocslabel() { + newdocslabelcount++ + return "NWD" alphacode(newdocslabelcount) +} +@ %def newdocslabel +<<functions>>= +function addlabel(tbl, arg, label, marker) { + marker = " " label + if (!tailmatch(tbl[arg], marker)) + tbl[arg] = tbl[arg] marker + return label +} +@ %def addlabel +<<functions>>= +function tailmatch(string, tail, pos) { + pos = length(string) - length(tail) + 1 + if (pos > 0 && substr(string, pos) == tail) + return 1 + else + return 0 +} +@ %def tailmatch +<<functions>>= +function addud(udlist, name, arg, label, s) { + s = " " name "{" label "}" + if (!tailmatch(udlist[arg], s)) + udlist[arg] = udlist[arg] s +} +@ %def addud +<<functions>>= +function listget(l, i, n, a) { + n = split(l, a) + return a[i] +} +@ %def listget +<<initialization>>= +udlist[0] = 0 ; +@ +[[uniqueid]] eliminates both {\TeX} and HTML specials. +Escaping the [[/]] in the character class in the regexp pattern works +around a bug in many awks. +Unpalatable, but what can one do? +<<functions>>= +function uniqueid(name, key) { + if (uidtable[name] == "") { + key = make_key(name) + # gsub(/[\]\[ \\{}`#%&~_^<>"-]/, "*", key) # old + gsub(/[^a-zA-Z0-9!$()*+,.\/:;=?@|]/, "*", key) + keycounts[key] = keycounts[key] + 1 + uidtable[name] = key + if (keycounts[key] > 1) + uidtable[name] = uidtable[name] "." alphacode(keycounts[key]) + } + return uidtable[name] +} +@ %def uniqueid +<<functions>>= +function make_key(name, key, l) { + l = length(name) + sub(/^.*\//, "", name) + key = substr(name, 1, 3) + if (l >= 3) key = key alphacode(l) + return key +} +<<initialization>>= +uidtable[0] = 0 +keycounts[0] = 0 ; +<<write trailers>>= +{ print "@nl" + print "@nl" + lognowebchunks() + lognowebindex() +} +@ +Now, a special hack, so we can write this stuff in the right place on pass 2. +<<functions>>= +function lognowebchunks(l, j, n, x) { + if (loggednowebchunks > 0) return + loggednowebchunks = 1 + delete allchunks[0] + n = alphasort(allchunks) + print "@xref beginchunks" + for (j = 0; j < n; j++) { + name = sorted[j]; delete sorted[j] + printf "@xref chunkbegin %s %s\n", + (anchorlabel[name] != "" ? anchorlabel[name] : "nw@notdef"), name + m = split(chunkud[name], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@xref chunkuse %s\n", substr(a[k], 5, length(a[k])-5) + else if (a[k] ~ /^defn/) + printf "@xref chunkdefn %s\n", substr(a[k], 6, length(a[k])-6) + print "@xref chunkend" + } + print "@xref endchunks" +} +@ %def lognowebchunks +<<functions>>= +function lognowebindex(l, j, n, x) { + if (loggednowebindex > 0) return + loggednowebindex = 1 + delete allidents[0] + n = alphasort(allidents) + print "@index beginindex" + for (j = 0; j < n; j++) { + name = sorted[j]; delete sorted[j] + printf "@index entrybegin %s %s\n", + (indexanchorlabel[name] != "" ? indexanchorlabel[name] : "nw@notdef"), +ame + m = split(indexud[name], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@index entryuse %s\n", substr(a[k], 5, length(a[k])-5) + else if (a[k] ~ /^defn/) + printf "@index entrydefn %s\n", substr(a[k], 6, length(a[k])-6) + print "@index entryend" + } + print "@index endindex" +} +@ %def lognowebindex +<<functions>>= +function alphasort(a, x, n) { + n = 0 + for (x in a) + n = insertitem(x, n) + return n +} +function insertitem(x, n, i, tmp) { + sorted[n] = x + sortkeys[n] = sortkey(x) + i = n + while (i > 0 && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i-1])) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [i-1]; sortkeys [i-1] = tmp + tmp = sorted[i]; sorted[i] = sorted[i-1]; sorted[i-1] = tmp + i = i - 1 + } + return n + 1 +} +@ %def alphasort insertitem +<<initialization>>= +sorted[0] = 0; sortkeys[0] = 0; +<<functions>>= +function sortkey(name, s) { + s = name + gsub(/[^a-zA-Z ]/, "", s) + return s +} +@ %def sortkey +<<functions>>= +function alphacode(n) { + if (n < 0) + return "-" alphacode(-n) + else if (n >= alphacodelen) + return alphacode(n / alphacodelen) alphacode(n % alphacodelen) + else + return substr(alphacodes, n+1, 1) +} +@ %def alphacode +<<initialization>>= +alphacodes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +alphacodelen = length(alphacodes) ; +@ +\section{List of chunks} +\nowebchunks + +\twocolumn +\section{Index} +\nowebindex +@ +\end{document} diff --git a/web/noweb/contrib/rsc/rc/noindex.nw b/web/noweb/contrib/rsc/rc/noindex.nw new file mode 100644 index 0000000000..193f2c6429 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/noindex.nw @@ -0,0 +1,194 @@ +This program is similar to [[makeindex]] in that it grovels through [[.aux]] +files looking for index information, which it writes to a [[.nwi]] file. +It's used when [[noweave -indexfrom]] is used on many files separately; +it combines the separate indices into a single, correctly sorted index. +That index file is read by [[\nowebindex*]]. +<<noindex>>= +#!/bin/rc +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com + +if(! ~ $#* 1) { + echo 'Usage: '^`{basename $0}^' file[.tex]' >[1=2] + exit usage +} + +awk -f /sys/lib/noweb/noindex.awk >[1=2] + +<<noindex.awk>>= +BEGIN { infile="'"$1"'" + <<main code>> + exit +} +<<functions>> +<<main code>>= +if (infile ~ /\.tex$/) { + infile = substr(infile, 1, length(infile)-4) ".aux" +} else if (infile !~ /\.aux$/) { + infile = infile ".aux" +} +idx[0] = "" +delete idx[0] +gobble(infile) +alphasort(idx) +outname = substr(infile, 1, length(infile)-4) ".nwi" +last = "" +for (i = 0; i < count; i++) { + out = stripcount(sorted[i]) + if (last != out) { + print out > outname + last = out +# <show sort key [[i]]> + } +} +<<show sort key [[i]]>>= +key = sortkeys[i] +sub(/^\n+/, "", key) +sub(/\n.*$/, "", key) +print "% " key > outname +<<functions>>= +function gobble(name, infile, rc, tag) { + for (rc = (getline line < name); rc > 0; rc = (getline line < name)) { + if (line ~ /^\\@input{[^}]*}$/) + gobble(substr(line, 9, length(line)-9)) + else if (line ~ /^\\nwixadds{/) { + count++ + tag = "000000" count + tag = substr(tag, length(tag)-6+1) + idx[count] = tag " " substr(line, 11) + } + } + if (rc < 0) print "No file " name "." + else close(name) + return +} +<<functions>>= +function stripcount(s) { + sub(/^[0-9]+/, "", s) + sub(/ +/, "", s) + return "\\nwixaddsx{" s +} +<<functions>>= +function alphasort(a, x, n) { + n = 0 + for (x in a) + n = insertitem(a[x], n) + finish_sorting(n) + return n +} +function insertitem(x, n, i, tmp) { + sorted[n] = x + sortkeys[n] = sortkey(x) + return n+1 +} +<<functions>>= +function finish_sorting(n) { + firstwork = nextwork = 0 + addquick(0, n) + while(nextwork > firstwork) + qsort() +} +<<functions>>= +function addquick(l, r) { + workq[nextwork++] = l + workq[nextwork++] = r +} +<<get [[l]] and [[r]] out of work queue>>= +l = workq[firstwork] +delete workq[firstwork] +firstwork++ +r = workq[firstwork] +delete workq[firstwork] +firstwork++ +<<functions>>= +function qsort(l, r, mid, i, last) { + <<get [[l]] and [[r]] out of work queue>> + if (r - l < 10) + isort(l, r) + else { + mid = l + int((r - l) * rand()) + swap(l, mid) + last = l + for (i = l+1; i < r; i++) + if (sortkeys[i] < sortkeys[l] || + sortkeys[i] == sortkeys[l] && sorted[i] < sorted[l]) + swap(++last, i) + swap(l, last) + addquick(l, last) + addquick(last+1, r) + } +} +<<functions>>= +function isort(l, r, n) { + for (n = l + 1; n < r; n++) + for (i = n; i > l && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i +1]); i--) + swap(i, i-1) +} +<<functions>>= +function swap(i, j, tmp) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [j]; sortkeys [j] = tmp + tmp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = tmp +} +<<functions>>= +function sortkey(s, count) { + match(s, /^[0-9]+/) + count = substr(s, RSTART, RLENGTH) + sub(/^[0-9]+ */, "", s) + if (s ~ /c}/) { + return firstkey(substr(s, 3)) "\n" count + } else if (s ~ /i}/) { + return firstkey(substr(s, 3)) "\n" count + } else { + print "sortkey handed non-chunk and non-index: " s + exit 1 + } +} +<<functions>>= +function firstkey(s, r, openbrace) { + if (s !~ /^{{/) { + <<complain about format and exit>> + } + sub (/^{{/, "", s) + gsub(/\\([a-zA-Z]+|.) */, "", s) # kill control sequences + openbrace = 1 + r = "" + while (openbrace > 0) + if (match(s, /^[^{}]*/) <= 0) + openbrace-- + else { + r = r substr(s, RSTART, RLENGTH) + c = substr(s, RSTART+RLENGTH, 1) + s = substr(s, RSTART+RLENGTH+1) + if (c == "}") openbrace-- + else openbrace++ + if (openbrace > 0) r = r c + } + return alphabet(r) "\n" r +} +<<complain about format and exit>>= +print "key \"" substr(s, 1, 6) "...\" is ill-formatted" +exit 1 +<<functions>>= +function alphabet(s, r) { + r = "" + while (match(s, /[a-zA-Z \t]/) > 0) { + s = substr(s, RSTART) + c = substr(s, 1, 1) + if (c == " " || c == "\t") { + r = r " " + sub(/^[ \t]+/, "", s) + } else { + match(s, /^[a-zA-Z]+/) + r = r substr(s, RSTART, RLENGTH) + s = substr(s, RSTART+RLENGTH) + } + } + sub(/^ +/, "", r) + return r +} +@ +\section{Index} +\nowebindex diff --git a/web/noweb/contrib/rsc/rc/noroots.nw b/web/noweb/contrib/rsc/rc/noroots.nw new file mode 100644 index 0000000000..74f4a792a1 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/noroots.nw @@ -0,0 +1,16 @@ +<<noroots>>= +#!/bin/rc +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# set -x +/sys/lib/noweb/bin/$objtype/markup $* | awk ' +/^@quote$/,/^@endquote$/ { next } +/^@defn / { chunk=substr($0,7) ; defined[chunk]=1 } +/^@use / { chunk=substr($0,6) ; used[chunk]=1 } +END { + for (chunk in defined) { + if (defined[chunk]==1 && used[chunk]==0) printf "@<<%s>>\n", chunk + } +}' diff --git a/web/noweb/contrib/rsc/rc/notangle.nw b/web/noweb/contrib/rsc/rc/notangle.nw new file mode 100644 index 0000000000..476534ca27 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/notangle.nw @@ -0,0 +1,51 @@ +<<notangle>>= +#!/bin/rc +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# Translated from sh to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com +# + +rfork en +bind -b /sys/lib/texmf/bin/$objtype /bin +bind -b /sys/lib/texmf/bin/rc /bin + +LIB=/sys/lib/texmf/noweb +markup=markup +opt=() +arg=() +markopt=() +filters=() + +while(! ~ $#* 0) { + switch($1) { + case -m -m3 -awk -icn -icon -pascal -c -c++ -f77 -f90 -tex -w[0-9][0-9] + + ; + case -t + ; + case -t* + markopt=($markopt -t) + opt=($opt $1) + case -filter + filters=($filters $2) + shift + case -markup + markup=$2 + shift + case - + arg=($arg $1) + case -L* + opt=($opt -t $1) + markopt=($markopt -t) + case -* + opt=($opt $1) + case * + arg=($arg $1) + } + shift +} + +$markup $markopt $arg | $filters nt $opt +exit $status diff --git a/web/noweb/contrib/rsc/rc/nountangle.nw b/web/noweb/contrib/rsc/rc/nountangle.nw new file mode 100644 index 0000000000..207eb5e08c --- /dev/null +++ b/web/noweb/contrib/rsc/rc/nountangle.nw @@ -0,0 +1,93 @@ +<<nountangle>>= +#!/bin/rc +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com + +# set -x +rfork en +bind -b /sys/lib/noweb/bin/$objtype /bin +bind -b /sys/lib/noweb/bin/rc /bin + +markup=markup +opt='' +arg='' +filters='' +width=72 +subst='gsub("\\*/", "* /", s)' +format='/* %%-%ds */' + +while(! ~ $#* 0) { + switch($1) { + case -ml -m3 + format='(* %%-%ds *)' + subst='gsub("\\*\\)", "* )", s) gsub("\\(\\*", "( *", s)' + case -awk -icn -icon + format='# %%-%ds' ; subst=' ' + case -lisp -scm + format=';;; %%-%ds' ; subst=' ' + case -c++ + format='// %%-%ds' ; subst=' ' + case -c + format='/* %%-%ds */' subst='gsub("\\*/", "* /", s)' + case -pascal + format='{ %%-%ds }' ; subst='gsub("[{}]", "-", s)' + case -f77 + format='C %%-%ds' ; subst=' ' ;; + case -f90 + format='! %%-%ds' ; subst=' ' ;; + case -tex + format='%%%% %%-%ds' ; subst=' ' ;; + case -L* + # deliberately ignore requests for #line + case -w[0-9][0-9]*; width=`{echo $1 | sed 's/^-w//'} ;; + case -filter; filters=' | '$filters' '$2 ; shift ;; + case -markup; markup=$2; shift ;; + case -; arg=$arg' '$1;; + case -*; opt=$opt' '$1 ;; + case *; arg=$arg' '$1 ;; + } + shift +} + +eval $markup^' '^$arg^' '^$filters | +awk 'BEGIN { line = 0; capture = 0; format=sprintf("'$format'",'$width') } + +function comment(s) { + '$subst' + return sprintf(format,s) +} + +function grab(s) { + if (capture==0) print + else holding[line] = holding[line] s +} + +/^@end doc/ { capture = 0; holding[++line] = "" ; next } +/^@begin doc/ { capture = 1; next } + +/^@text / { grab(substr($0,7)); next} +/^@quote$/ { grab("[[") ; next} +/^@endquote$/ { grab("]]") ; next} + +/^@nl$/ { if (capture !=0 ) { + holding[++line] = "" + } else if (defn_pending != 0) { + print "@nl" + for (i=0; i<=line && holding[i] ~ /^ *$/; i++) i=i + for (; i<=line; i++) printf "@text %s\n@nl\n", comment(holding[i]) + line = 0; holding[0] = "" + defn_pending = 0 + } else print + next + } + +/^@defn / { holding[line] = holding[line] "<"substr($0,7)">=" # (line should b + blank) + print ; defn_pending = 1 ; next } +{ print }' | +eval nt^' '^$opt + diff --git a/web/noweb/contrib/rsc/rc/noweave.nw b/web/noweb/contrib/rsc/rc/noweave.nw new file mode 100644 index 0000000000..46b49ae81e --- /dev/null +++ b/web/noweb/contrib/rsc/rc/noweave.nw @@ -0,0 +1,594 @@ +\section{Weaving a {\tt noweb} file into a \TeX file} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991-1997 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# $Id: noweave.nw,v 1.6 1998/08/17 00:10:34 nr Exp nr $ +# +# Translated from sh to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com +@ +Here's the organization of the source: +<<noweave>>= +#!/bin/rc +<<copyright notice>> +rfork en +<<initialization>> +<<set up /bin union>> +<<scan options and arguments>> +{ + <<emit markup on standard output>> + status='' +} | { + args=(`{echo $noindex $delay $shortxref}) + eval $backend $args +} +exit $status +<<if verbose, show back end>>= +if(! ~ $verbose '') + echo $backend $noindex $delay $shortxref >[1=2] +@ +The first item of initialization is to locate the {\tt noweb} library. +<<initialization>>= +LIB=/sys/lib/noweb +@ +We need to add the {\tt noweb} bin directories to the union mount on {\tt /bin +. +<<set up /bin union>>= +bind -b $LIB/bin/$objtype /bin +bind -b $LIB/bin/rc /bin +@ +We continue with initialization. +We use strings throughout rather than {\tt rc} lists, +since we're just going to echo it anyway, and it makes +keeping the filterlist easy. +<<initialization>>= +markup=markup +backend=totex +wrapper='' +delay='' +args='' +markopts='' +noweboptions='' +autodefs='' +verbose='' +shortxref='' +noquote=-noquote +docanchor='' +noindex=-noindex +filterlist=() +# following supported by change in totex back end +noquote='' +@ +I make two passes over the arguments so as not to require that options +be given in a certain order +<<scan options and arguments>>= +saveargs=($*) +arg='' +while(! ~ $#* 0) { + switch($1) { + <<first pass {\tt noweave} options>> + case - + arg=$arg^' '$1 + case -* + echo $0': unrecognized option '$1 >[1=2] + <<show usage>> + exit usage + case * + arg=$arg^' '$1 + } + shift +} +<<insist first-pass options are self-consistent>> +if(~ $wrapper '') + wrapper=latex + +*=$saveargs +shift + +while(! ~ $#* 0) { + switch($1) { + <<second pass {\tt noweave} options>> + } + shift +} + +<<add [[$newopt]] to [[noweboptions]]>>= +if(~ $noweboptions '') + noweboptions=$newopt +if not + noweboptions=$noweboptions','$newopt + +<<first pass {\tt noweave} options>>= +case -latex + if(! ~ $wrapper none) + wrapper=latex +case -tex + wrapper=tex +case -html + if(! ~ $wrapper none) + wrapper=html + backend='tohtml -localindex' + noquote='' + docanchor='-docanchor 10' +case -latex+html + if(! ~ $wrapper none) + wrapper=latex + backend='tohtml -localindex -raw' + noquote='' + docanchor='-docanchor 10' +case -ascii + wrapper=none + backend=toascii +case -troff + backend=toroff +case -n + wrapper=none +case -backend + backend=$2 + shift +case -markup + markup=$2 + shift +@ +Note some versions of echo break on [[echo "-n..."]], echoing nothing +at all. The leading space is claimed to prevent this problem. +<<option printout for usage>>= +echo '-latex Emit LaTeX with headers and trailers (default).' >[1=2] +echo '-tex Emit plain TeX with headers and trailers.' >[1=2] +echo '-html Emit HTML with headers and trailers.' >[1=2] +echo '-latex+html Assume LaTeX in documentation, but use HTML in code.' > +1=2] +# echo '-ascii Emit ASCII.' >[1=2] +echo '-troff Emit troff (actually GNU groff).' >[1=2] +echo ' -n Don''t use any header or trailer.' >[1=2] +echo '-markup frontend Parse input with frontend (e.g., numarkup).' >[1=2] +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-latex +Emit LaTeX, including wrapper in +.B article +style with the +.B noweb +package and page style. (Default) +.TP +.B \-tex +Emit plain TeX, including wrapper with +.B nwmac +macros. +.TP +.B \-html +Emit HTML, using HTML wrapper. +The output is uninteresting without \fB-index\fP or \fB-x\fP. +The tags \fB<nowebchunks>\fP and \fB<nowebindex>\fP, on lines by themselves, +produce a list of chunks and an index of identifiers, respectively. +If these tags are not present, the list and index are placed at the end of the +file. +.TP +.B \-latex+html +Assume documentation chunks are LaTeX, but generate HTML for code chunks, +suitably marked so conversion with +.I latex2html(1) +yields reasonable output. +A LaTeX wrapper is implied, but can be turned off with \fB-n\fP. +.I Use of this option is +.B deprecated; +use +.B \-html +with +.B "\-filter l2h" +instead. +<<noweave man page option table>>= +.TP +.B \-troff +Emit +.IR troff (1) +markup (with no wrapper). +The result should be processed with +.IR noroff (1). +Bug reports for +.B \-troff +to Arnold Robbins +.B <arnold@skeeve.com>. +<<bogus noweave man page option table>>= +.TP +.B \-ascii +Emit ASCII (with no wrapper). +Bug reports for +.B \-ascii +to Phil Bewig +.B <pbewig@netcom.com>. +<<noweave man page option table>>= +.TP +.B \-n +Don't use any wrapper (header or trailer). +This option is useful when \fInoweave\fP's output will be +a part of a larger document. +See also +.B \-delay. +@ \fi +A common bug seems to be using both [[-x]] and [[-index]] on the same +command line, so I complain about it. +<<insist first-pass options are self-consistent>>= +if(! ~ $using_xref '' && ! ~ $using_index '') { + echo $0': you may not use -x with -index or -indexfrom (drop the -x)' > +1=2] + exit -x-index +} +<<initialization>>= +using_index='' +using_xref='' +@ +<<first pass {\tt noweave} options>>= +case -filter + shift +case -x + using_xref=1 +case -index + noindex='' + using_index=1 +case -indexfrom + shift + noindex='' + using_index=1 +<<second pass {\tt noweave} options>>= +case -filter + newfilter=$2 + shift + <<add [[$newfilter]]>> +case -x + newfilter='finduses noquote' + <<add [[$newfilter]]>> +case -index + newfilter='finduses '^$noquote + <<add [[$newfilter]]>> + newfilter='noidx '^$docanchor^' '^$delay + <<add [[$newfilter]]>> +case -indexfrom + newfilter='finduses '^$noquote^' '^$2 + <<add [[$newfilter]]>> + newfilter='noidx '^$docanchor^' '^$delay + <<add [[$newfilter]]>> + shift +<<option printout for usage>>= +echo '-x Use the default cross-referencer (needs LaTeX or HTML). + >[1=2] +echo '-index Create index using identifiers defined in input files.' + >[1=2] +echo '-indexfrom defs Create index of identifers listed in file defs.' >[1=2 + +echo '-filter cmd Filter through ''cmd'' before weaving; cmd could pretty +rint' >[1=2] +echo ' or perform other functions.' >[1=2] +@ \iffalse +<<noweave man page indexing options>>= +.TP +.B \-x +For +.I LaTeX, +add a page number to each chunk name identifying the location of that +chunk's definition, and emit cross-reference information relating definitions +nd uses. +For +.I HTML, +create hypertext links between uses and definitions of chunks. +When +.B noweave -x +is used with +.I LaTeX, +the control sequence +.B "\\\\nowebchunks" +expands to a sorted list of all code chunks. +.TP +.B \-index +Build cross-reference information (or hypertext links) for identifiers defined +by +.br +.B "@ %def" +.I identifiers +.br +Definitions are those found in input files. +Requires +.I LaTeX +or +.I HTML. +.B \-index +implies +.B \-x; +including both will generate strange-looking output. +.I noweave +does not generate +cross-references to identifiers that appear in quoted code (\fB@[[\fP...\fB@]] +fP), +but it does generate hypertext links. +When +.B noweave -index +is used with +.I LaTeX, +the control sequence +.B "\\\\nowebindex" +expands to an index of identifiers. +.TP +.B \-indexfrom \fIindex\fP +Like +.B \-index, +but the identifiers to be indexed are taken from file \fIindex\fP. +See +.I noindex(1). +<<noweave man page option table>>= +.TP +.B \-filter \fIcmd\fP +Filters the +.I noweb +source through +.I cmd +after converting it to tool form and before converting to +.I TeX. +.I noweave +looks for +.I cmd +first on the user's +.B PATH, +then in +.B |LIBDIR|. +Such filters +can be used to add features to +.I noweave; +for an example, see +.B |LIBDIR|/noxref.krom. +.I Noweave +supports up to four filters; one can get more by shell trickery, +for example, \fB-filter "icon.filter | noidx"\fP. +The \fB-autodefs\fP, +\fB-x\fP, \fB-index\fP, and \fB-indexfrom\fP options are implemented as filter +. +Filters are executed with the shell's +.B eval +command, so +.I cmd +should be quoted accordingly. +<<description of -markup option>> +@ \fi +Note that it would be appropriate to look for autodefs +using [[[ -x $newfilter ]]], +but that stupid DEC Ultrix doesn't support [[test -x]], so the best I can +do in a portable way is [[test -r]]. +<<first pass {\tt noweave} options>>= +case -autodefs + newfilter='autodefs.'^$2 + if(test -r $newfilter) { + <<add [[$newfilter]]>> + } + if not { + echo $0^': don''t know how to find definitions for '^$2 + exit defns + } + shift + +case -showautodefs + <<print all legal [[-autodefs]] or complain>> + exit complain +<<option printout for usage>>= +echo '-autodefs lang Source is in language ''lang''; find definitions automa +ically.' >[1=2] +echo '-showautodefs List languages that can be used with -autodefs' >[1=2] +@ \iffalse +<<noweave man page indexing options>>= +.TP +.B \-autodefs \fIlang\fP +Discover identifier definitions automatically. +Code in chunks must be in language \fIlang\fP. +Permissible \fIlang\fPs vary but may include +.B tex +or +.B icon. +Useless without +.B \-index, +which it must precede. +.TP +.B \-showautodefs +Show values of \fIlang\fP usable with \fB-autodefs\fP. +@ \fi +Same note as above regarding [[test -x]] vs [[test -r]]. +<<print all legal [[-autodefs]] or complain>>= +foundautodefs=no +for(i in $LIB/autodefs.*) { + if(test -r $i) { + echo 'Supports -autodefs '^$i | sed 's!$LIB/autodefs\.!!' >[1=2 + + foundautodefs=yes + } +} +if(~ $foundautodefs no) + echo 'Does not support -autodefs' >[1=2] +@ +Here's an embarrassing hack --- if we spot \verb+-option shortxref+ +or \verb+-option longxref+ on the +command line, we pass something suitable to the back end, in case we're doing +HTML. +<<first pass {\tt noweave} options>>= +case -option + newopt=$2 + shift + if(~ $newopt shortxref) + shortxref=-shortxref + if(~ $newopt longxref) + shortxref=-longxref + <<add [[$newopt]] to [[noweboptions]]>> +<<option printout for usage>>= +echo '-option opt Add \noweboptions{opt} to header (latex only)' >[1=2] +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-option \fIopt\fP +Adds \fB\enoweboptions{\fP\fIopt\fP\fB}\fP to the +.I LaTeX +header. +See +.I nowebstyle(1) +for values of +.I opt. +Normally useful only with the +.B \-latex +option, but +.B "\-option longxref" +works black magic with +.B \-html. +@ \fi +<<first pass {\tt noweave} options>>= +# case -nodelay +# delay='' +case -delay + delay=-delay + wrapper=none +<<option printout for usage>>= +echo '-delay Delay markup until after first documentation chunk.' >[ +=2] +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-delay +By default, +.I noweave +puts file-name and other information into the output before the first chunk +of the program. +.B \-delay +delays that information until after the first documentation chunk, making +act a little bit like the +.I WEB +``limbo.'' +The option is typically used to enable a user to put a specialized +.I LaTeX +.B "\\\\documentclass" +command and other preamble material in the first documentation chunk. +This option also forces trailing cross-referencing information to be emitted +just before the final chunk, instead of at the end of the document; +the final chunk is expected to contain +.B "\\\\end{document}." +The +.B \-delay +option implies the +.B \-n +option. +@ \fi +% .TP +% .B \-nodelay +% Don't delay, put file-name and other information right after header. (Defaul +) +% @ \fi +<<first pass {\tt noweave} options>>= +case -t* + markopts=$markopts' '$1 +<<option printout for usage>>= +echo '-tk Expand tab stops every k columns' >[1=2] +echo '-t Copy tabs to the output' >[1=2] +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-t\fIk\fP +Expand tabs with stops every \fIk\fP columns. +(Default is to expand every 8 columns.) +.TP +.B \-t +Copy tabs to the output. +@ \fi +<<first pass {\tt noweave} options>>= +case -v + echo 'RCS id $Id: noweave.nw,v 1.6 1998/08/17 00:10:34 nr Exp nr $' >[1 +2] + verbose=1 +<<option printout for usage>>= +echo '-v Print pipeline and RCS info on standard error' >[1=2] +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-v +Print the pipeline and RCS info on standard error. +@ \fi +\iffalse +<<man page: WEAVING section>>= +Output from \fInoweave\fP can +be used in \fITeX\fP documents that +.B "\\\\input nwmac," +in \fILaTeX\fP documents that use the +.B noweb +package (see \fInowebstyle(1)\fP), +and in \fIHTML\fP documents to be browsed with +.I Mosaic(1). +.I Noweave +treats code chunks somewhat like +.I LaTeX list environments. +If the ``\fB@ \fP'' that terminates a code chunk is followed immediately by te +t, +that text follows the code chunk without a paragraph break. +If the rest of the line is blank, +.I noweave +puts +.I TeX +into ``vertical mode,'' and later text starts a fresh, indented paragraph. +.PP +No page breaks occur in the middle of code chunks unless necessary to avoid +an overfull vbox. +The documentation chunk immediately preceding a code chunk appears on +the same page as that code chunk unless doing so would violate the previous ru +e. +.PP +.I Noweave +inserts no extra newlines in its \fITeX\fP output, so the line numbers given +in +.I TeX +error messages are the same as those in the input file. +.PP +.I noweave +has +options that dictate choice of +formatter +and that support different formatting idioms and tools. +Basic options are described here; options related to index +and cross-reference information are described in the +INDEXING AND CROSS-REFERENCE section. +<<noweave man page option table>> +@ +<<man page: INDEXING AND CROSS-REFERENCE section>>= + +@ \fi +<<add [[$newfilter]]>>= +filterlist=($filterlist $newfilter) +<<show usage>>= +echo 'Usage: '$0' [options] [files]' >[1=2] +echo 'Options recognized are:' >[1=2] +<<option printout for usage>> +@ +To avoid inserting any extra newlines into the output, +I use [[@literal]]to insert headers and trailers. +<<emit markup on standard output>>= +header='' +# whatis wrapper +# whatis arg +switch($wrapper) { +case none + ; +case latex + header='@header '^$wrapper^' '^$noweboptions + trailer='@trailer '^$wrapper +case * + header='@header '^$wrapper^$arg +} +if(! ~ $header '') + echo $header +<<if verbose, make noise about pipeline>> +<<if verbose, show back end>> +if(~ $#filterlist 0) + filterlist=cat +pipeline='| '^$filterlist +pipeline=cat^$"pipeline + +# whatis pipeline +argx=(`{echo $markopts $arg}) +# whatis argx +$markup $argx | eval $pipeline +if(! ~ $trailer '') + echo $trailer +<<if verbose, make noise about pipeline>>= diff --git a/web/noweb/contrib/rsc/rc/noweave.simple b/web/noweb/contrib/rsc/rc/noweave.simple new file mode 100644 index 0000000000..2f2bb84a3e --- /dev/null +++ b/web/noweb/contrib/rsc/rc/noweave.simple @@ -0,0 +1,56 @@ +#!/bin/rc + +rfork en + +LIB=/sys/lib/noweb + +bind -b /sys/lib/noweb/bin/$objtype /bin +bind -b /sys/lib/noweb/bin/rc /bin + +files=() +for(i) { + switch($i) { + case -* + ; + case * + files=($files $i) + } +} + +markup $files | awk ' +BEGIN { code=0 + print "\\documentstyle{article}" + print "\\newcommand{\\fragment}[1]{{\\sl$\\langle$#1\\/$\\ran +le$}}" + print "\\begin{document}" + } +END { if (code) print "\\end{trivlist}" + print "\\end{document}" + } +/^@quote$/ { printf "\\verb@"} +/^@endquote$/ { printf "@" } +/^@begin code/ { if (!code) print "\\begin{trivlist}\\raggedright\\obeylines\\ +eftskip=2em\\small\\item[]%"; code=1 } +/^@end code/ { } +/^@begin docs/ { if (code) print "\\end{trivlist}"; code=0 } +/^@end docs/ { } +/^@defn / { gsub(/\[\[/, "\\verb@"); gsub(/]]/, "@") + name = substr($0,7) + printf "\\hspace{-2em}" + printf "\\fragment{%s}", name + defs[name] += 1 + if (defs[name] > 1) + printf "$+\\!\\!\\equiv$" + else + printf "$\\equiv$" + printf "\\index{\\fragment{%s}}", name + } +/^@use / { gsub(/\[\[/, "\\verb@"); gsub(/]]/, "@") + name = substr($0,6) + printf "\\fragment{%s}", name + printf "\\index{\\fragment{%s}}", name + } +/^@literal / { printf "%s", substr($0, 10) } +/^@nl$/ { print ""} +/^@text / { if (code) printf "\\verb@%s@", substr($0,7) + else printf "%s", substr($0,7) }' diff --git a/web/noweb/contrib/rsc/rc/noweb.nw b/web/noweb/contrib/rsc/rc/noweb.nw new file mode 100644 index 0000000000..e790c47e42 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/noweb.nw @@ -0,0 +1,63 @@ +<<noweb>>= +#!/bin/rc +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com + +rfork en +bind -b /sys/lib/noweb/bin/$objtype /bin +bind -b /sys/lib/noweb/bin/rc /bin + +markup=markup +mntopt=-L +status=0 +tex=1 +output=1 + +break=no +while(! ~ $#* 0 && ~ $break no) { + switch($1) { + case -to -ot + tex='' + output='' + shift + case -t + tex='' + shift + case -o + output='' + shift + case -L* + mntopt=$1 + shift + case -markup + markup=$2 + shift + shift + case -* + echo unrecognized option $1 >[1=2] + exit usage + case * + break=yes + } +} + +for(source) { + if(test -n $output) { + eval $markup' -t '$source' | mnt -t8 '$mntopt' -all' || status= + + } + if(test -n $tex) { + texname=`{echo $source | sed '/\./s/\.[^.]*$//'} + texname=$texname.tex + eval $markup' '$source | finduses -noquote | noidx -delay | + awk '{print} + /^@defn [^ ]*$/ { print "@literal \\let\\nwnotused=\\nw +utput{}" }' | + totex -delay | cpif $texname || status=1 + } +} + +exit $status diff --git a/web/noweb/contrib/rsc/rc/toascii.nw b/web/noweb/contrib/rsc/rc/toascii.nw new file mode 100644 index 0000000000..3805fa4bf2 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/toascii.nw @@ -0,0 +1,279 @@ +[[Toascii]] is a [[noweb]] back end for formatting text as a plain ascii file. +It was written by Phil Bewig (pbewig@netcom.com) on March 31, 1995, and +contributed to Norman Ramsey's [[noweb]] literate programming system. +@ +The main program is shown below. Option [[-delay]] is processed, for +compatibility with other back ends, but ignored; since the initial document +chunk used with [[-delay]] normally contains only [[TeX]] formatting commands +in limbo, and since those commands will be deleted before formatting, there is +no need to handle [[-delay]]. +<<toascii>>= +#!/bin/rc +# +# Based on shell script by Phil Bewig +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-lab.scom +# +delay=0 +noindex=0 +for(i) { + switch($i){ + case -delay + delay=1 + case -noindex + noindex=1 + case * + echo 'cannot happen' >[1=2] + exit coredump + } +} +<<arrange temporary files>> +<<invoke first pass using parameter>> +<<invoke second pass using file>> +exit $status +@ %def delay noindex +[[Toascii]] uses two temporary files, one for storing the text between passes +and one for communicating the conversion of labels to tags. The files are +named here, and disposal of the file on exit from [[toascii]] is arranged. +Also arranged here is a temporary file for storage of the awk program on an +ugly system, as discussed below. +<<arrange temporary files>>= +awkfile=$(mktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +textfile=$(mktemp ) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +tagsfile=$(mktemp ) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +@ %def textfile tagsfile awkfile +The actual formatting of the text, code, and index entries is done by various +unix text processing commands in pipelines. There are four formatting pipes: +tfmt, which formats text, cfmt, which formats code, xfmt, which formats index +entries within the running text, and zfmt, which formats the lists of chunks +and identifiers at the end of the text. The formatters established below are +only suggestions, and may be modified to suit local taste (and the presence of +various text processing commands on the local machine!); in particular, [[c]] +programmers may want to format code with cb or indent. The sed patterns below +insert four blank spaces at the beginning of the line. +<<initialize formatters>>= +tfmt"detex | fmt -l 79" +cfmt="cat" +xfmt="cat" +zfmt="cat" +@ %def tfmt cfmt xfmt zfmt +Forgiving systems allow the awk program to be specified as a parameter to the +awk interpreter; ugly systems require that it be placed in a temporary file. +The chunks below implement both options. +<<invoke first pass using parameter>>= +awk '<<first pass>>' +<<invoke second pass using parameter>>= +awk '<<second pass>>' -v 'noindex='$noindex $textfile +<<toascii1.awk>>= +<<first pass>> +<<invoke first pass using file>>= +awk -f toascii1.awk +<<toascii2.awk>>= +<<second pass>> +<<invoke second pass using file>>= +awk -f /sys/lib/noweb/toascii2.awk -v 'noindex='$noindex $textfile +@ +The first pass is responsible for extracting [[label]]s and assigning them +section numbers, which are used for cross-referencing in the second pass. The +first pass also removes from the input file those lines which are not used by +the second pass. +The environment is gotten from the associative array [[ENVIRON]]. +<<initialize environment>>= +textfile=ENVIRON["textfile"] +tagsfile=ENVIRON["tagsfile"] +<<first pass>>= +BEGIN { <<initialize environment>> } +/^@begin code/ { ++secno } +/^@xref label/ { print $3, secno >tagsfile } +/^@((begin|end) (docs|code))/ { print >textfile } +/^@(text|nl|defn|use)/ { print >textfile } +/^@xref (ref|notused)/ { print >textfile } +/^@xref (begin|end)(defs|uses)/ { print >textfile } +/^@xref (def|use)item/ { print >textfile} +/^@xref ((begin|end)chunks)|(chunk(begin|use|defn|end))/ { print >textfile } +/^@index (begin|end)(defs|uses)/ { print >textfile } +/^@index (is(us|defin)ed)|((def|use)item)/ { print >textfile } +/^@index ((begin|end)index)|(entry(begin|use|defn|end))/ { print >textfile } +@ +The second pass performs formatting. After looking up the temp file names and +formatters in the environment and reading the [[tagsfile]] created in the firs + +pass, the second pass processes each input command in the body of the awk +[[pattern-action]] processing loop. +<<second pass>>= +BEGIN { + <<initialize environment>> + <<initialize formatters>> + while (getline <tagsfile > 0) + tag[$1] = $2 + close(tagsfile) +} +<<process [[noweb]] commands>> +/^@fatal / { exit 1 } +END { + close(out) +} +<<functions>> +@ %def tag +The rest of the program consists of a series of awk [[pattern-action]] +statements which each process a particular type of [[noweb]] pipeline command. +They are discussed in related groups, and all collected in a single chunk. We +begin with the commands that process the text of the document and code chunks. +The basic strategy is always write text to [[out]] and open and close various +pipes as needed. Variable [[code]] is true only within code chunks, and +[[secno]] numbers the sections as they appear. Function [[endcode()]] closes +the code pipeline at the end of a code section or whenever the first indexing +command appears. +<<process [[noweb]] commands>>= +/^@begin docs/ { out = tfmt } +/^@end docs/ { close(out) } +/^@begin code/ { out = cfmt; code = 1; ++secno } +/^@end code/ { endcode(); close(out); printf "\n" } +/^@text/ { printf "%s", substr($0, 7) | out } +/^@nl/ { # printf "(->%s)", formatname(out) | out ; + printf "\n" | out } +@ %def out secno code +<<functions>>= +function endcode() { + if (code == 1) { + code = 0 + close(out) + out = xfmt + printf "\n" | out } } +@ %def endcode +Definitions and uses of code chunks are handled below. Variable [[defn[name]] + +is set to a plus sign after a definition is printed, so that continuations of +the definition are properly identified. Variable [[lastxrefref]] is the tag +associated with the most-recently-seen cross-reference label, and refers to th + +section number of the original definition of the code chunk. Definition lines +are printed directly, without passing through any of the formatters defined +above. +<<process [[noweb]] commands>>= +/^@xref ref/ { lastxrefref = tag[substr($0, 11)] } +/^@defn/ { name = convquote(substr($0, 7)) + printf "\n### %d ### %s%s=", + secno, chunkname(name, lastxrefref), defn[name] + defn[name] = "+" } +/^@use/ { name = convquote(substr($0, 6)) + printf "%s", chunkname(name, lastxrefref) | out } +@ %def lastxref name defn +There are three messages related to the definition and use of code chunks whic + +may appear in the output: "This definition continued in ...", "This code used +in ...", and "This code not used in this document." These messages are printe + +by the following code. +<<process [[noweb]] commands>>= +/^@xref begindefs/ { endcode() + printf "This definition continued in" | out } +/^@xref beginuses/ { endcode() + printf "This code used in" | out } +/^@xref notused/ { endcode() + print "This code not used in this document." | out } +/^@xref (def|use)item/ { addlist(tag[$3]) } +/^@xref end(defs|uses)/ { printlist() } +@ +Processing of the [[noweb]] commands which produce the identifier definition +message "Defines: ... used in ..." is performed by the following code. The +[[if]] in [[@index isused]] prevents index definitions from pointing to +themselves. +<<process [[noweb]] commands>>= +$0 ~ /^@index begindefs/ && !noindex { + endcode() + print "Defines:" | out } + +$0 ~ /^@index isused/ && !noindex { + if (tag[$3] != lastxrefref) addlist(tag[$3]) } + +$0 ~ /^@index defitem/ && !noindex { + printf " %s,", $3 | out + if (nlist == 0) printf " not used in this document.\n" | out + else { printf " used in" | out; printlist() } } +@ +Processing of the [[noweb]] commands which produce the identifier usage messag + +"Uses ..." is performed by the following code. +<<process [[noweb]] commands>>= +$0 ~ /^@index beginuses/ && !noindex { endcode(); printf "Uses" | out } +$0 ~ /^@index isdefined/ && !noindex { lastuse = tag[$3] } +$0 ~ /^@index useitem/ && !noindex { addlist(sprintf("%s %s", $3, lastuse)) + +$0 ~ /^@index enduses/ && !noindex { printlist() } +@ %def lastuse +The [[noweb]] commands which print the list of chunks at the end of the +document are processed by the following code. +<<process [[noweb]] commands>>= +/^@xref beginchunks/ { close(out); out = zfmt + print "List of code chunks\n" | out } +/^@xref chunkbegin/ { name = convquote(substr($0, length($3) + 19)) + printf "%s\n", chunkname(name, tag[$3]) | out } +/^@xref chunkuse/ { addlist(tag[$3]) } +/^@xref chunkdefn/ { } +/^@xref chunkend/ { if (nlist == 0) + print " Not used in this document." | out + else { printf " Used in" | out; printlist() } } +/^@xref endchunks/ { } +@ +The [[noweb]] commands which print the list of identifiers at the end of the +document are processed by the following code. +<<process [[noweb]] commands>>= +$0 ~ /^@index beginindex/ && !noindex { print "\nList of identifiers (defini" + + "tion in parentheses)\n" | out } +$0 ~ /^@index entrybegin/ && !noindex { name = substr($0, length($3 + 19)) + lastdefn = tag[$3] + printf "%s: ", $4 | out } +$0 ~ /^@index entryuse/ && !noindex { addlist(tag[$3]) } +$0 ~ /^@index entrydefn/ && !noindex { } +$0 ~ /^@index entryend/ && !noindex { for (i = 1; i <= nlist; i++) + if (list[i] == lastdefn) + sub(/.*/, "(&)", list[i]) + if (nlist == 0) + print "Not used." | out + else printlist() } +$0 ~ /^@index endindex/ && !noindex { } +@ +Several of the cross-reference and indexing commands use the [[addlist(s)]] an + +[[printlist()]] functions to manage the printing of lists of code sections and +variable names: [[addlist(s)]] adds string [[s]] to a queued [[list]] waiting +to be printed and [[printlist()]] prints the [[list]], appropriately formatted +with commas. These two functions are described below. +<<functions>>= +function addlist(s, i) { + for (i = 1; i <= nlist; i++) + if (s == list[i]) return + list[++nlist] = s } + +function printlist( i) { + if (nlist == 1) printf " %s.\n", list[1] | out + else if (nlist == 2) printf " %s and %s.\n", list[1], list[2] | out + else { + for (i = 1; i < nlist; i++) + printf " %s,", list[i] | out + printf " and %s.\n", list[nlist] | out } + for (i in list) delete list[i] + nlist = 0 } +@ %def list nlist addlist printlist +Chunk names which appear in definitions and uses of chunks consist of text +which may contain quoted code embedded between double square brackets. Quoted +code in text chunks are handled by the [[@quote ... @endquote]] mechanism, but +quoted code in chunk names must be handled explicitly by the back end. The +function below does what is needed. +<<functions>>= +function convquote(s) { gsub(/\[\[|\]\]/, "", s); return s } +@ %def convquote + +<<functions>>= +function chunkname(name, number) { + if (number == 0) + return sprintf("<%s>", name) + else + return sprintf("<%s %d>", name, number) +} +@ %def chunkname +@ +<nowebchunks> +<nowebindex> diff --git a/web/noweb/contrib/rsc/rc/tohtml.nw b/web/noweb/contrib/rsc/rc/tohtml.nw new file mode 100644 index 0000000000..46b1bc3e3b --- /dev/null +++ b/web/noweb/contrib/rsc/rc/tohtml.nw @@ -0,0 +1,362 @@ +\documentstyle[noweb]{article} +\pagestyle{noweb} +\begin{document} +\section{Converting {\tt noweb} markup to {\tt HTML}} +This copyright applies both to the {\tt noweb} source and to the +generated shell script. +Thanks to Bill Trost for getting me started with an early version. +<<copyright notice>>= +# Copyright 1994 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com + +<<tohtml>>= +#!/bin/rc +<<copyright notice>> +# Do not try to understand this file! Look at lib/tohtml.nw in the noweb sour +e! + +delay=0 +raw=0 +localindex=0 +noindex=0 +for(i) { + switch($i) { + case -delay + delay=1 + case -raw + raw=1 + case -localindex + if(~ $noindex 0) + localindex=1 + case -noindex + localindex=0 + noindex=1 + } +} + +awk -f /sys/lib/noweb/tohtml.awk \ + -v 'delay='$delay -v 'raw='$raw -v 'localindex='$localindex -v 'noindex +'$noindex +<<tohtml.awk>>= +<<awk program for conversion to {\tt HTML}>> +@ +The [[-raw]] option brackets HTML with [[\begin{rawhtml}]] and +[[\end{rawhtml}]]; the purpose is to embed HTML in a {\LaTeX} +document before converting the document with {\tt latex2html}. +[[braw]] and [[eraw]] hold those delimiters (or else empty strings). +<<awk program for conversion to {\tt HTML}>>= +<<functions>> +BEGIN { <<initialization>> } +!doneraw { # do not do in BEGIN because not all awks assign variables yet + if (raw) { braw = "\\begin{rawhtml}"; eraw = "\\end{rawhtml}" } + else braw = eraw = "" + doneraw = 1 +} +<<patterns>> +END { print "" } +@ +[[ecode]] is the marker used at the end of the current code chunk. +If there is no cross-reference stuff at the end, we just use [[</pre>]]; +otherwise we terminate whatever environment is used for the cross-reference st +ff. +<<patterns>>= +/^@begin code / { code = 1; printf "%s<pre>", braw; ecode = "</pre>" } +/^@end code / { code = 0; previscode = 1; <<dump pending cross-reference inf +>> + printf "%s%s", ecode, eraw + } +@ +We want to try to avoid emitting paragraph elements when the +preceding chunk is a code chunk, as tracked by [[previscode]]. +Also, if we do slip in a paragraph, we may use the {\LaTeX} style. +<<patterns>>= +/^@begin docs / { if (previscode) printf "%s", (raw ? "\\par" : "<p>") + previscode = text = 0 + } +@ +Sometimes it happens that a document-chunk anchor is put in a document chunk t +at +contains no text. In that case, we put in a phony anchor at the end of the ch +nk so +we won't lose the cross-reference. +<<patterns>>= +/^@end docs / { if (lastxreflabel != "") + printf "%s%s%s\n", braw, linklabel(lastxreflabel, "*"), er +w + lastxreflabel = "" + } +@ +Normally, if there's a pending anchor, we put it on the first available text l +ne. +<<patterns>>= +/^@text / { line = substr($0, 7); text += length(line) + if (code) { + if (lastindexref != "" && line ~ /[^ \t]/) { + printf "%s", linkto(lastindexref, line) + lastindexref = "" + } else { + printf "%s", escapeSpecials(line) + } + } else if (quoting) { + if (line ~ /[^ \t]/) { + printf "%s", linklabelto(lastxreflabel, lastindexref, + escapeSpecials(line)) + lastindexref = lastxreflabel = "" + } else { + printf "%s", escapeSpecials(line) + } + } else { + if (lastxreflabel != "" && line ~ /[^ \t]/) { + <<print docs anchor>> + lastxreflabel = "" + } else { + printf "%s", line + } + } + } +@ +We anchor on the first nonblank character of the line, unless that's +a \TeX\ control sequence or an SGML tag. +In that case we insert a {\tt*} to anchor to. +None of this crap would be necessary if HTML could anchor to empty text. +<<print docs anchor>>= +match(line, /^[ \t]*/) +blanks = substr(line, RSTART, RLENGTH) +line = substr(line, RSTART+RLENGTH) +if (line ~ /^[{}\\<&]/) { + char = "*" +} else { + char = substr(line, 1, 1) + line = substr(line, 2) +} +printf "%s%s%s%s%s", braw, blanks, linklabel(lastxreflabel, char), eraw, line +if (lastxreflabel != "") defns_above[lastxreflabel] = 1 +<<patterns>>= +/^@nl$/ { print "" } +/^@defn / { thischunk = name = substr($0, 7) + if (lastxreflabel != "") defns_above[lastxreflabel] = 1 + writechunk(lastxreflabel, lastxrefref, "dfn", name, defns[name] "= +) + <<clear [[lastxref*]]>> + defns[name] = "+" + } +<<initialization>>= +defns[0] = 0 +defns_above[0] = 0 +<<patterns>>= +/^@use / { writechunk(lastxreflabel, lastxrefref, "i", substr($0, 6), "") } +@ +Writing a chunk involves creating an anchor for it. +<<functions>>= +function writechunk(label, ref, tag, name, suffix) { + printf "%s", + linklabelto(label, ref, sgmlwrap(tag, "<" convquotes(name) ">" suffi +)) +} +@ +<<patterns>>= +/^@quote$/ { quoting = 1 ; printf "%s<code>", braw } +/^@endquote$/ { quoting = 0 ; printf "</code>%s", eraw } +/^@file / { filename = substr($0, 7); <<clear [[lastxref*]]>> } +/^@literal / { printf "%s", substr($0, 10) } +/^@header html / { <<write HTML header>> } +/^@trailer html$/ { <<write HTML trailer>> } +@ +<<write HTML header>>= +printf "<html><head><title>%s</title></head><body>", substr($0, 14) + +<<write HTML trailer>>= +print "</body></html>" +@ +<<patterns>>= +/^@xref label / { lastxreflabel = substr($0, 13) } +/^@xref ref / { lastxrefref = substr($0, 11) } +/^@xref prevdef/ { pendingprev = substr($0, 15) } +/^@xref nextdef/ { pendingnext = substr($0, 15) } +/^@xref beginuses/ { useitems = "" } +/^@xref useitem / { useitems = useitems " " substr($0, 15) } +/^@xref enduses/ { useitemstab[thischunk] = useitems } +/^@xref notused / { <<code-to-blockquote>> + printf "This code is written to a file (or else not used +.<p>" + } +<<initialization>>= +useitemstab[0] = 0 +<<clear [[lastxref*]]>>= +lastxreflabel = lastxrefref = "" +<<dump pending cross-reference info>>= +useitemscount = split(useitemstab[thischunk], a) +if (pendingprev != "" || pendingnext != "" || useitemscount > 0) { + <<code-to-blockquote>> + <<write out uses with links>> + if (useitemscount > 0 && (pendingprev != "" || pendingnext != "")) + printf "; " + p = useitemscount > 0 ? "previous" : "Previous" + n = useitemscount > 0 ? "next" : "Next" + if (pendingprev != "") + if (pendingnext != "") + printf "%s and %s definitions", linkto(pendingprev, p), linkto(pendingne +t, "next") + else + printf "%s definition", linkto(pendingprev, p) + else + if (pendingnext != "") + printf "%s definition", linkto(pendingnext, n) + pendingprev = pendingnext = "" + useitems = "" + print ".<p>" +} +<<write out uses with links>>= +useprefix = "Used " +for (j = 1; j <= useitemscount; j++) { + if (defns_above[a[j]] > 0) + usedir = "above" + else + usedir = "below" + printf "%s%s", useprefix, linkto(a[j], usedir (useitemscount > 1 ? " (" j ") + : "")) + useprefix = ", " +} +@ +The hack here is to put the supplementary information in a blockquote area +after the code. +<<code-to-blockquote>>= +if (ecode == "</pre>") { + printf "</pre><blockquote>" + ecode = "</blockquote>" +} +@ +The HTML back end ignores [[@xref begindefs]], [[@xref defitem]], and +[[@xref enddefs]]; it uses the [[nextdef]] and [[prevdef]] links instead. +<<patterns>>= +/^@xref (begindefs|defitem|enddefs)/ { } +/^@xref beginchunks$/ { printf "%s<ul>\n", braw } +/^@xref chunkbegin / { label = $3; name = substr($0, 19 + length(label)) + printf "<li>"; comma = ": "; count = 0 + writechunk("", label, "i", name, "") + } +/^@xref chunkuse / { printf "%s%s", comma, linkto(substr($0, 16), "U" ++cou +t) + comma = ", " + } +/^@xref chunkdefn / { printf "%s%s", comma, linkto(substr($0, 17), "D" ++cou +t) + comma = ", " + } +/^@xref chunkend$/ { print "" } +/^@xref endchunks$/ { printf "</ul>%s\n", eraw } +<<patterns>>= +/^@index beginindex$/ { if (!noindex) { printf "%s<ul>\n", braw } } +/^@index entrybegin / { if (!noindex) { + label = $3; name = substr($0, 20 + length(label)) + printf "<li>"; comma = ": "; count = 0 + printf "%s", + linklabelto("NWI-" escapeSpecials(name), label, na +e) + + } } +/^@index entryuse / { if (!noindex) { + printf "%s%s", comma, linkto(substr($0, 17), "U" ++co +nt) + comma = ", " + } } +/^@index entrydefn / { if (!noindex) { + printf "%s%s", comma, linkto(substr($0, 18), "D" ++ +ount) + comma = ", " + } } +/^@index entryend$/ { if (!noindex) { print "" } } +/^@index endindex$/ { if (!noindex) { printf "</ul>%s\n", eraw } } +@ +The local identifier cross-reference doesn't show each use; it just shows +the identifiers that are defined, with links to the full index. +<<patterns>>= +/^@index use/ { lastindexref = lastxrefref; lastxrefref = "" } +/^@index defn/ { <<clear [[lastxref*]]>> } +/^@index localdefn/ { <<clear [[lastxref*]]>> } +/^@index nl/ { } # do nothing -- destroys line numbering +/^@index begindefs/ { if (localindex) { + <<code-to-blockquote>>; printf "Defines"; comma = " " +} } +/^@index isused / { } +/^@index defitem / { if (localindex) { + arg = substr($0, 16) + printf "%s%s", comma, + linkto("NWI-" escapeSpecials(arg), sgmlwrap("code", escapeSpecials(arg) +) + comma = ", " +} } +/^@index enddefs/ { if (localindex) { printf " (links are to index).<p>\n" } +} +/^@index (beginuses|isdefined|useitem|enduses)/ { } # use local links +@ +\subsection{Support functions} +Here's all our anchor support goo. +<<functions>>= +function linklabelto(label, ref, contents, s) { + s = label != "" || ref != "" ? "<a" : "" + if (label != "") s = s " name=" image(label) + if (ref != "") s = s " href=" image("#" ref) + s = s (label != "" || ref != "" ? ">" : "") + s = s contents + s = s (label != "" || ref != "" ? "</a>" : "") + return s +} + +function linkto(ref, contents) { + return linklabelto("", ref, contents) +} + +function linklabel(label, contents) { + return linklabelto(label, "", contents) +} +@ +Another support function is used for wrapping tags around text: +<<functions>>= +function sgmlwrap(tag, s) { + return "<" tag ">" s "</" tag ">" +} +<<functions>>= +function image(s) { + gsub(/"/, "\\\"", s) + return "\"" s "\"" +} +@ +Lucky for us, {\tt HTML} has few special characters. Unlucky for us, +we have to deal with each one seperately. Nothing much to whine +about, really. +<<functions>>= +function escapeSpecials (l) { + gsub(/&/, "\\&", l) + gsub(/</, "\\<", l) + gsub(/>/, "\\>", l) + gsub(/"/, "\\"", l) + return l +} +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<functions>>= +function convquotes(s, r, i, line) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "<code>" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + line = substr(s, 1, i-1+RLENGTH-2) + # line = escapeSpecials(line) # destroys internal markup --- do not cal + + r = r line "</code>" + s = substr(s, i+RLENGTH) + } else { + r = r s "</code>" + s = "" + } + } + return r s +} +@ +\end{document} diff --git a/web/noweb/contrib/rsc/rc/totex.nw b/web/noweb/contrib/rsc/rc/totex.nw new file mode 100644 index 0000000000..160a364341 --- /dev/null +++ b/web/noweb/contrib/rsc/rc/totex.nw @@ -0,0 +1,312 @@ +\section{Converting {\tt noweb} markup to {\TeX} markup} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com + +<<totex>>= +#!/bin/rc +<<copyright notice>> +# Don't try to understand this file! Look at lib/totex.nw in the noweb source + +delay=0 +noindex=0 +for(i) { + switch($i) { + case -delay + delay=1 + case -noindex + noindex=1 + case * + echo 'This can''t happen -- '$i' passed to totex' >[1=2] + exit cannothappen + } +} +<<invoke awk program using file>> +@ +On a forgiving system, we make the awk program an argument: +<<invoke awk program using parameter>>= +awk '<<awk program for conversion to {\TeX}>>' -v 'delay='$delay -v 'noindex=' +noindex +@ +On an ugly system, we have to put it in a file. +<<invoke awk program using file>>= +awk -f /sys/lib/noweb/totex.awk -v 'delay='$delay -v 'noindex='$noindex +<<totex.awk>>= +<<awk program for conversion to {\TeX}>> +@ +The markup carefully adds no newlines not already present in the input, +so that the line numbers of the {\TeX} file will be the same as the +numbers of the corresponding {\tt noweb} file. +The variables are: +\begin{description} +\item[\tt code] Nonzero if converting a code chunk. +\item[\tt quoting] Nonzero if quoting code in documentation. +\item[\tt text] Number of characters written since start of + documentation chunk. +\end{description} +[[text]] is used to write [[\par]] if a newline appears at the +beginning of a documentation chunk without any intervening text. +This subtle trick preserves new-paragraph semantics without requiring +the insertion of a blank line that would throw off the line count. +<<awk program for conversion to {\TeX}>>= +BEGIN { code=0 ; quoting=0 ; text=1; <<initialization>> } +/^@begin code/ { code=1 ; printf "\\nwbegincode{%s}", substr($0, 13) } +/^@end code/ { code=0 ; printf "\\nwendcode{}"; lastdefnlabel = "" } +<<special patterns for document chunk 0>> +/^@begin docs/ { text=0 ; printf "\\nwbegindocs{%s}", substr($0, 13 + } +/^@end docs/ { printf "\\nwenddocs{}" } +/^@text / { line = substr($0, 7) ; text += length - 6 + if (code) printf "%s", escape_brace_bslash(line) + else if (quoting) printf "%s", TeXliteral(line) + else printf "%s", line + } +/^@nl$/ { if (!code) {<<print [[\nwdocspar]] if no text>>} + if (quoting) printf "\\nwnewline" + printf "\n" + } +/^@defn / { name = substr($0, 7); <<defn of [[name]], with cross-refe +ence>> } +/^@use / { printf "\\LA{}%s%s\\RA{}", + convquotes(substr($0, 6)), <<optional ref tag>> + } +/^@quote$/ { quoting = 1 ; printf "{\\tt{}" } +/^@endquote$/ { quoting = 0 ; printf "}" } +/^@file / { filename = substr($0, 7); <<clear [[lastxref*]]>> + if (!delay) printf "\\nwfilename{%s}", filename + } +/^@literal / { printf "%s", substr($0, 10) } +/^@header latex / { <<write {\LaTeX} header>> } +/^@header tex / { printf "\\input nwmac " } +/^@trailer latex$/ { print "\\end{document}" } +/^@trailer tex$/ { print "\\bye" } +<<xref patterns>> +<<index patterns>> +END { printf "\n" } +<<functions>> +@ +<<print [[\nwdocspar]] if no text>>= +if (text==0) printf "\\nwdocspar" +text=1 +@ +Delaying markup is handled by special patterns for the first document chunk. +Because several {\tt noweb} files can be marked up at once, there can be +several document chunks numbered 0. +The later ones are given no special treatment by the simple expedient of +turning [[delay]] off after the first one. +<<special patterns for document chunk 0>>= +/^@begin docs 0$/ { if (delay) next } +/^@end docs 0$/ { if (delay) { + printf "\\nwfilename{%s}", filename; delay=0; next + } } +@ +<<defn of [[name]], with cross-reference>>= +if (lastxreflabel != "") { + printf "\\sublabel{%s}", lastxreflabel + printf "\\nwmargintag{%s}", label2tag(lastxreflabel) +} +printf "\\moddef{%s%s}\\%sendmoddef", convquotes(name), <<optional ref tag>>, +efns[name] +lastdefnlabel = lastxreflabel +<<clear [[lastxref*]]>> +defns[name] = "plus" +<<optional ref tag>>= +(lastxrefref != "" ? ("~" label2tag(lastxrefref)) : "") +<<functions>>= +function label2tag(label) { + return "{\\nwtagstyle{}\\subpageref{" label "}}" +} +<<initialization>>= +defns[0] = 0 +@ +<<write {\LaTeX} header>>= +printf "\\documentclass{article}\\usepackage{noweb}\\pagestyle{noweb}\\nowebop +ions{%s}%s", + substr($0, 15), "\\begin{document}" +@ +\subsection{Cross-reference and index support} +<<xref patterns>>= +/^@xref label / { lastxreflabel = substr($0, 13) } +/^@xref ref / { lastxrefref = substr($0, 11) } +/^@xref begindefs$/ { printf "\\nwalsodefined{" } +/^@xref defitem / { printf "\\\\{%s}", substr($0, 15) } +/^@xref enddefs$/ { printf "}" } +/^@xref beginuses$/ { printf "\\nwused{" } +/^@xref useitem / { printf "\\\\{%s}", substr($0, 15) } +/^@xref enduses$/ { printf "}" } +/^@xref notused / { printf "\\nwnotused{%s}", TeXliteral(substr($0, 15)) } +/^@xref nextdef / { } +/^@xref prevdef / { } +<<clear [[lastxref*]]>>= +lastxreflabel = lastxrefref = "" +<<index patterns>>= +/^@index nl$/ { print (code ? "\\eatline" : "%") } +/^@index defn / { + if (!noindex) { arg = substr($0, 13); <<handle index defn of [[arg]]> + } } +/^@index localdefn / { + if (!noindex) { arg = substr($0, 18); <<handle index defn of [[arg]]> + } } +/^@index use / { + if (!noindex) { arg = substr($0, 12); <<handle index use of [[arg]]>> +} } +@ +Nothing is involved in handling definitions and uses unless there are cross-re +erence +labels pending. +An index definition or use has its own [[@xref label]] only if it's in documen +ation; +if it's in code, we use the anchor label of the definition. +(You don't have to know that to understand what happens here, but I thought yo + +might like to.) +<<handle index defn of [[arg]]>>= +if (lastxreflabel != "") printf "\\nosublabel{%s}", lastxreflabel +if (lastxrefref != "") + printf "\\nwindexdefn{%s}{%s}{%s}", TeXliteral(arg), indexlabel(arg), lastxr +fref +<<clear [[lastxref*]]>> +@ +The {\LaTeX} back end ignores uses in code; they get bundled up by a previous +ilter +(the cross-referencer) and handled elsewhere. +<<handle index use of [[arg]]>>= +if (!code) { + if (lastxreflabel != "") printf "\\protect\\nosublabel{%s}", lastxreflabel + if (lastxrefref != "") + printf "\\protect\\nwindexuse{%s}{%s}{%s}", + TeXliteral(arg), indexlabel(arg), lastxrefref +} +<<clear [[lastxref*]]>> +@ +Here's the local identifier cross-reference that appears at the end of a code +hunk. +We guard everything with \LA{}SI\RA, as before. +<<index patterns>>= +/^@index begindefs$/ { if (!noindex) { printf "\\nwidentdefs{" } } +/^@index isused / { if (!noindex) { } } # handled by latex +/^@index defitem / { if (!noindex) { i = substr($0,16); <<write [[i]] with [ +\\]]>> } } +/^@index enddefs$/ { if (!noindex) { printf "}" } } +/^@index beginuses$/ { if (!noindex) { printf "\\nwidentuses{"; ucount = 0 } } +/^@index isdefined / { if (!noindex) { } } # latex finds the definitions +/^@index useitem / { if (!noindex) { i = substr($0, 16); <<write [[i]] with +[\\]]>> + ulist[ucount++] = i + } } +/^@index enduses$/ { if (!noindex) { printf "}"; <<write [[ulist]]>> } } +<<initialization>>= +ulist[0] = 0 +<<write [[i]] with [[\\]]>>= +printf "\\\\{{%s}{%s}}", TeXliteral(i), indexlabel(i) +<<write [[ulist]]>>= +if (lastdefnlabel != "") { + for (j = 0; j < ucount; j++) + printf "\\nwindexuse{%s}{%s}{%s}", + TeXliteral(ulist[j]), indexlabel(ulist[j]), lastdefnlabel +} +@ +\subsubsection{The list of chunks and the index} +The treatments of the list of chunks and the index are similar. +Both use [[\nwixlogsorted]], which writes magic goo into the {\tt .aux} file. +The real cross-referencing is done by the underlying {\LaTeX} code. +<<xref patterns>>= +/^@xref beginchunks$/ { } +/^@xref chunkbegin / { label = $3; name = substr($0, 19 + length( +abel)) + printf "\\nwixlogsorted{c}{{%s}{%s}{", + convquotes(name), label + } +/^@xref chunkuse / { printf "\\nwixu{%s}", substr($0, 16) } +/^@xref chunkdefn / { printf "\\nwixd{%s}", substr($0, 17) } +/^@xref chunkend$/ { print "}}%" } +/^@xref endchunks$/ { } +<<index patterns>>= +/^@index beginindex$/ { if (!noindex) { } } +/^@index entrybegin / { if (!noindex) { label = $3; name = substr($0, 20 + len +th(label)) + printf "\\nwixlogsorted{i}{{%s}{%s}}%%\n", + + TeXliteral(name), indexlabel(name) + } } +/^@index entryuse / { if (!noindex) { } } # handled by latex +/^@index entrydefn / { if (!noindex) { } } # handled by latex +/^@index entryend$/ { if (!noindex) { } } +/^@index endindex$/ { if (!noindex) { } } + +@ +\subsection{Miscellany} +I first insert a newline before the special characters, then change the +newline to a backslash. I can't do the backslash directly because +[[\&]] means a literal ampersand. +<<functions>>= +function escape_brace_bslash(line) { + gsub(/[\\{}]/, "\n&", line) + gsub(/\n/, "\\", line) + return line +} +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<functions>>= +function convquotes(s, r, i) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "\\code{}" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + r = r TeXliteral(substr(s, 1, i-1+RLENGTH-2)) "\\edoc{}" + s = substr(s, i+RLENGTH) + } else { + r = r s "\\edoc{}" + s = "" + } + } + return r s +} +<<functions>>= +function indexlabel(ident, l) { + l = ident + gsub(/:/, ":col", l) # must be first (colon) + gsub(/ /, ":sp", l) # space + gsub(/#/, ":has", l) # hash + gsub(/\$/, ":do", l) # dollar + gsub(/%/, ":pe", l) # percent + gsub(/&/, ":am", l) # ampersand + gsub(/,/, ":com", l) # commad + gsub(/\\/, ":bs", l) # backslash + gsub(/\^/, ":hat", l) # hat + gsub(/_/, ":un", l) # underscore + gsub(/{/, ":lb", l) # left brace + gsub(/}/, ":rb", l) # right brace + gsub(/~/, ":ti", l) # tilde + return l +} +@ %def indexlabel +@ +Because latex2e uses [[`]] as an active character, I have to use +decimal character codes for the specials. +<<functions>>= +function TeXliteral(arg) { + gsub(/\\/, "<\\char92>", arg) + gsub(/}/, "<\\char125}", arg) + gsub(/{/, "{\\char123}", arg) + gsub(/<\\char/, "{\\char", arg) + gsub(/{\\char92>/, "{\\char92}", arg) + gsub(/\$/, "{\\char36}", arg) + gsub(/&/, "{\\char38}", arg) + gsub(/#/, "{\\char35}", arg) + gsub(/\^/, "{\\char94}", arg) + gsub(/_/, "{\\char95}", arg) + gsub(/%/, "{\\char37}", arg) + gsub(/~/, "{\\char126}", arg) + gsub(/ /, "\\ ", arg) + return arg +} +@ %def TeXliteral + diff --git a/web/noweb/contrib/rsc/rc/unmarkup.nw b/web/noweb/contrib/rsc/rc/unmarkup.nw new file mode 100644 index 0000000000..9d04b3389d --- /dev/null +++ b/web/noweb/contrib/rsc/rc/unmarkup.nw @@ -0,0 +1,53 @@ +<<unmarkup>>= +#!/bin/rc +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file /sys/src/cmd/noweb/COPYRIGHT for more information. +# +# Translated to rc by Russ Cox +# bugs -> rsc@plan9.bell-labs.com +# + +awk ' +BEGIN { + rcsid = "$Id: unmarkup,v 1.5 1999/02/16 21:11:54 nr Exp nr $" + rcsname = "$Name: v2_9a $" +} +/^@begin docs 0$/ { next } +/^@begin docs / { printf "@ " } +/^@begin code / { code = 1 } +/^@end [cd]o[dc][es] / { + code = 0 + if (dangling_text) printf "\n" + dangling_text = 0 + printf "%s", deflines + if (defline != "") printf "%s\n", defline + deflines = "" ; defline = "" + } +/^@defn / { printf "@<<%s>>=", substr($0,7) } +/^@text $/ {next} +/^@text / { + gsub("@<<", "@@<<"); + gsub("@>>", "@@>>"); + if (!(code || quoting)) { + gsub(/\[\[/, "@[["); + gsub(/\]\]/, "@]]"); + } + printf "%s", substr($0,7) + dangling_text = 1 +} +/^@quote$/ { printf("[["); dangling_text = 1; quoting = 1 } +/^@endquote$/ { printf("]]"); dangling_text = 1; quoting = 0 } +/^@nl$/ { printf "\n"; dangling_text = 0} + +/^@index defn / { + if (defline == "") defline = "@ %def" + defline = defline " " substr($0, 13) +} +/^@index nl$/ { + deflines = deflines defline "\n" + defline = "" +} +/^@use / { printf "@<<%s>>", substr($0,6) + dangling_text = 1 + }' $* | sed 's/^@ $/@/' diff --git a/web/noweb/contrib/ydirson/Makefile b/web/noweb/contrib/ydirson/Makefile new file mode 100644 index 0000000000..a6a6dadc99 --- /dev/null +++ b/web/noweb/contrib/ydirson/Makefile @@ -0,0 +1,11 @@ +LIB=/dev/null # to be overridden + +FILTERS = guesslang inheritlang enscript-html + +# nothing to tangle or weave +all: +source: +clean: + +install: + cp -p $(FILTERS) $(LIB) diff --git a/web/noweb/contrib/ydirson/README b/web/noweb/contrib/ydirson/README new file mode 100644 index 0000000000..54b5b43c71 --- /dev/null +++ b/web/noweb/contrib/ydirson/README @@ -0,0 +1,32 @@ +guesslang <list of root chunks> + Attempts to set the @language of given root chunks. + Note: Currently only inspects '#!' lines, not filename. +inheritlang + Propagates @language to non-root chunks. +enscript-html <enscript flags> + Uses enscript(1) to pretty-print chunks in HTML according to @language. + Most useful enscript flags include --color and --style=... + + Note: Should ultimately work with all languages + supported by enscript, but needs extra info about how + to mangle @use clauses. If it complains "Don't know + how to mangle @use" for your language, you can edit + mangle_use() and demangle_use() to turn the @use + clause into a meaningfull language clause, and then + convert back in @use form. + + Note: Supports all highlighting styles of enscript + 1.6.4, but the regexp in demangle_use() may need to be + adapted in the future. + +Typical use lokks like: + noweave -html \ + -filter "guesslang ${NOWEBOUTSRC} | inheritlang | enscript-html" \ + -x <your.nw> + +Be sure to specify -x or possibly other filters *after* the -filter, +since the pretty-printer does not preserve the position of remaining +directives (esp. @xref) within code chunks. + +Sample output is viewable at +http://ydirson.free.fr/en/software/noweb/dh-kpatches.html diff --git a/web/noweb/contrib/ydirson/email b/web/noweb/contrib/ydirson/email new file mode 100644 index 0000000000..4d569e5f14 --- /dev/null +++ b/web/noweb/contrib/ydirson/email @@ -0,0 +1 @@ +ydirson@altern.org diff --git a/web/noweb/contrib/ydirson/enscript-html b/web/noweb/contrib/ydirson/enscript-html new file mode 100755 index 0000000000..1f179bef8a --- /dev/null +++ b/web/noweb/contrib/ydirson/enscript-html @@ -0,0 +1,150 @@ +#!/usr/bin/perl -w + +# Noweb filter which calls enscript to prettyprint according to +# @language directives (see guesslang and inheritlang filters to have +# those directive automatically generated). + +# Copyright (c) 2003 by Yann Dirson <ydirson@altern.org> + +# Distribute under the terms of the GNU General Public Licence, +# version 2. + +# FIXME: +# - @use in code chunks is not supported for all @language's yet +# => find a way to plug external data ? +# - when a perl chunk ends with comment lines, we get enscript +# trailers in woven output + +use strict; +use File::Temp qw(tempfile); + +my $mangledID='__NOWEB__mangled__use__'; +sub mangle_use { + my ($usedchunk, $lang) = @_; + + if (grep { $lang eq $_ } ('perl', 'c', 'c++') ) { + return "$mangledID (\"$usedchunk\")\n"; + } else { + die "Don't know how to mangle \@use for language $lang"; + } +} + +sub demangle_use { + my ($mangled, $lang) = @_; + + if (grep { $lang eq $_ } ('perl', 'c', 'c++') ) { + $mangled =~ m|^(.*)$mangledID \((?:<B>)?(?:<FONT.*>)?\"(.*)\"(?:</FONT>)?(?:</B>)?\)(.*)$|; + return ($1, $2, $3); + } else { + die "Don't know how to demangle \@use for language $lang"; + } +} + + +# Find out languages supported by the available version of enscript + +my @knownlangs; +open (LANGS, 'enscript --help-highlight | grep ^Name: |') or + die "enscript --help-highlight failed: $!"; +while (<LANGS>) { + chomp; + @_ = split /\s+/; + push @knownlangs, $_[1]; +} + + +while (<STDIN>) { + if (m/^\@begin code/) { + + # we found a code chunk, now bufferize its contents until + # @language, or until @end if no @language is there. Store in + # $event which of these 2 events just occured + + my (@buffer, $event); + push @buffer, $_; + while (defined($_ = <STDIN>) and + not ((m/^\@end code / and $event = [1]) or + (m/^\@language (.*)/ and $event = [2, $1])) ) { + push @buffer, $_; + } + die "$0 hit EOF before seing \@end code or \@language" unless defined $event; + + if ($event->[0] == 1) { + # we got @end first, everything read goes through unmodified + + push @buffer, $_; # the @end line + # no declared language: dump @buffer + foreach (@buffer) { print; } + } else { + # we found @language... + + # check that language is supported + my $lang = $event->[1]; + if (grep { $_ eq $lang } @knownlangs ) { + # language is supported + + # (implicitely) drop @language from output, read remainder + my $chunknum; + while (defined($_ = <STDIN>) and not (m/^\@end code (.*)/ and $chunknum = $1)) { + push @buffer, $_; + } + # we don't want "@end code" in the buffer, delay its output + my $endcode = $_; + + # transform the code chunk to be accepted by enscript, and + # store it into an auto-unlinked temporary file + + my $tmp = new File::Temp(); + # demangle @-directives into something suitable for enscript + foreach (@buffer) { + if (m/^\@text (.*)/ ) { + print $tmp $1; + } elsif (m/^\@nl$/) { + print $tmp "\n"; + } elsif (m/^\@use (.*)/) { + print $tmp mangle_use ($1, $lang); + } else { + print; + } + } + + # pipe, remangle + open PRETTY, "enscript --highlight=$lang --language=html " . + join (' ', @ARGV) . + " --silent -o - $tmp |" or + die "enscript failed: $!"; + { + my $started = undef; + while (<PRETTY>) { + if (m|^<PRE>$|) { + $started = 1; + next; + } + if (m|^</PRE>$|) { + last; + } + + if (m/$mangledID/) { + my ($prefix, $use, $suffix) = demangle_use ($_, $lang); + print "\@literal $prefix\n" if $prefix ne ''; + print "\@use $use\n" ; + print "\@literal $suffix\n" if $suffix ne ''; + next; + } + print "\@literal $_\@nl\n" if defined $started; + } + } + close PRETTY; + close $tmp; # auto-unlinked + + print $endcode; + } else { + push @buffer, $_; # the @language line + # unsupported language: dump @buffer + foreach (@buffer) { print; } + } + } + } else { + print $_; + } +} diff --git a/web/noweb/contrib/ydirson/guesslang b/web/noweb/contrib/ydirson/guesslang new file mode 100755 index 0000000000..d659e57ee2 --- /dev/null +++ b/web/noweb/contrib/ydirson/guesslang @@ -0,0 +1,57 @@ +#!/usr/bin/perl -w + +# Noweb filter which attempt to add @language directives to root +# chunks named on command-line. + +# Copyright (c) 2003 by Yann Dirson <ydirson@altern.org> + +# Distribute under the terms of the GNU General Public Licence, +# version 2. + +# TODO: Currently only look at the 1st line of the chunk, expecting to +# find a standard UN*X "#!" declaration. Still has to look at file +# names/suffixes as well. + +use strict; +use File::Basename; + +my @roots = @ARGV; + +my %interpreters = ( + '[gn]awk' => 'awk', + 'perl[0-9.]*' => 'perl', + 'python[0-9.]*' => 'python', + '(wish|tclsh)[0-9.]*' => 'tcl', + '(|k|ba|z)sh' => 'sh', + ); + +while (<STDIN>) { + if (m/^\@defn (.*)$/ and grep (/^$1$/, @roots)) { + my $language; + my $defn = $_; + + # FIXME: should lookup a filename-based hash first (or second ?) + + # memorize all lines until we can guess the language + my @buffer; + while (defined($_ = <STDIN>) and not m/^\@text/) { + push @buffer, $_; + } + # we have found the 1st @text line in the file, go see if it's a hash-bang + if (m/^\@text #!\s*(\/\S+)/) { + my $interp = basename ($1); + + # lookup in our knowledge base + foreach my $re (keys %interpreters) { + $language=$interpreters{$re} if $interp =~ m/^$re$/; + } + # default to interpreter's name (FIXME: ugly, should be optional) + $language = $interp unless defined $language; + } + # + print $defn; + print "\@language $language\n" if defined $language; + print @buffer; + } + print $_; +} diff --git a/web/noweb/contrib/ydirson/inheritlang b/web/noweb/contrib/ydirson/inheritlang new file mode 100755 index 0000000000..f71cce0a61 --- /dev/null +++ b/web/noweb/contrib/ydirson/inheritlang @@ -0,0 +1,76 @@ +#!/usr/bin/perl -w + +# Noweb filter to propagate @language directive from a chunk to used +# chunks. Assumes that root chunks already have a @language directive +# (see guesslang filter). Takes no argument. + +# Copyright (c) 2003 by Yann Dirson <ydirson@altern.org> + +# Distribute under the terms of the GNU General Public Licence, +# version 2. + +use strict; + +my (%chunklangs, %chunkchildren); + +# FIXME: we could bufferize as needed, if we want to grow more complex +my @data = <STDIN>; + +# register the chunk hierarchy +{ + my $thischunk = undef; + foreach (@data) { + if (m/^\@end code/) { # this one first to limit to code chunks + $thischunk = undef; + } elsif (m/^\@use (.*)$/) { + push @{$chunkchildren{$thischunk}}, $1 if defined $thischunk; + } elsif (m/^\@defn (.*)$/) { + $thischunk = $1; + } elsif (m/^\@language (.*)$/) { + die "\@language without a \@defn: $_" unless defined $thischunk; + $chunklangs{$thischunk} = $1; + } + } +} + +# propagate to argument's children +sub propagate { + my ($thischunk) = @_; + if (defined $chunklangs{$thischunk}) { + foreach my $child (@{$chunkchildren{$thischunk}}) { + if (defined $chunklangs{$child}) { + if ($chunklangs{$child} eq $chunklangs{$thischunk}) { + print STDERR "Notice: chunk used more than once: \`$child'\n"; + } else { + die "Chunk cannot inherits languages \`$chunklangs{$child}' and " . + "\`$chunklangs{$thischunk}': \`$child'\n"; + } + } else { + $chunklangs{$child} = $chunklangs{$thischunk}; + } + + # recurse + propagate($child); + } + } else { + print STDERR "Warning: could not infer language for \`$thischunk'\n"; + } +} + +# propagate from all known chunks +foreach my $chunk (keys %chunklangs) { + propagate($chunk); +} + +# output +foreach (@data) { + if (m/^\@defn (.*)$/) { + print $_; + print "\@language $chunklangs{$1}\n" if (defined $chunklangs{$1}) + } elsif (m/^\@language /) { + # Do not output twice. Since we already asserted consistency we can + # simply ignore this one. + } else { + print $_; + } +} diff --git a/web/noweb/echo b/web/noweb/echo new file mode 100755 index 0000000000..447731d216 --- /dev/null +++ b/web/noweb/echo @@ -0,0 +1,27 @@ +#!/bin/sh + +# version of echo that does -n + +if [ "x$1" = "x-n" ]; then + shift + cat << echoEOF | tr -d '\012' +$* +echoEOF + +else + cat << echoEOF +$* +echoEOF + +fi + +exit 0 + + +if [ -x /bin/echo ]; then + exec /bin/echo "$@" +elif [ -x /usr/bin/echo ]; then + exec /usr/bin/echo "$@" +else + echo "$@" +fi diff --git a/web/noweb/examples/Makefile b/web/noweb/examples/Makefile new file mode 100644 index 0000000000..9295cf54fc --- /dev/null +++ b/web/noweb/examples/Makefile @@ -0,0 +1,55 @@ +WWW=$(HOME)/www/noweb/examples +SHELL=/bin/sh +NOTANGLE=nountangle -m3 +NOWEAVE=noweave + +.SUFFIXES: .i3 .m3 .nw .tex .dvi .html +.nw.html: ; $(NOWEAVE) -filter l2h -filter btdefn -index -html $*.nw > $*.html +.nw.tex: ; $(NOWEAVE) -index -filter btdefn $*.nw > $*.tex +.nw.i3: ; $(NOTANGLE) -Rinterface -L'<* LINE %L "%F" *>%N' $*.nw > $*.i3 +.nw.m3: ; $(NOTANGLE) -L'<* LINE %L "%F" *>%N' $*.nw > $*.m3 +.tex.dvi: ; latex '\scrollmode \input '"$*"; while grep -s 'Rerun to get cross-references right' $*.log; do latex '\scrollmode \input '"$*"; done + +HTML=breakmodel.html compress.html dag.html graphs.html mipscoder.html primes.html \ + scanner.html test.html tree.html wc.html wcni.html + + +www: $(HTML) + copy -v $(HTML) $(WWW) + copy -v README.h $(WWW)/index.html + +clean: + rm -f *~ *.aux *.tex *.dvi *.log *.html *.toc + +clobber: clean + +compress.html: compress.nw + $(NOWEAVE) -filter l2h -index -autodefs c -html compress.nw > compress.html +compress.tex: compress.nw + $(NOWEAVE) -index -autodefs c compress.nw > compress.tex +dag.html: dag.nw + $(NOWEAVE) -filter l2h -index -autodefs icon -html dag.nw > dag.html +dag.tex: dag.nw + $(NOWEAVE) -index -autodefs icon dag.nw > dag.tex +mipscoder.html: mipscoder.nw + $(NOWEAVE) -filter l2h -index -autodefs sml -html mipscoder.nw > mipscoder.html +mipscoder.tex: mipscoder.nw + $(NOWEAVE) -autodefs sml -index mipscoder.nw > mipscoder.tex +scanner.html: scanner.nw + $(NOWEAVE) -filter l2h -index -autodefs c -autodefs yacc -html scanner.nw > scanner.html +scanner.tex: scanner.nw + $(NOWEAVE) -index -autodefs c -autodefs yacc scanner.nw > scanner.tex +tree.html: tree.nw + $(NOWEAVE) -filter l2h -index -autodefs icon -html tree.nw > tree.html +tree.tex: tree.nw + $(NOWEAVE) -index -autodefs icon tree.nw > tree.tex +test.html: test.nw + $(NOWEAVE) -filter l2h -html test.nw > test.html +test.tex: test.nw + $(NOWEAVE) test.nw > test.tex +wc.html: wc.nw2html + $(NOWEAVE) -autodefs c -filter btdefn -index -html -n wc.nw2html > wc.html +wcni.html: wc.nw2html + $(NOWEAVE) -filter btdefn -x -html -n wc.nw2html > wcni.html +wc.tex: wc.nw + $(NOWEAVE) -autodefs c -filter btdefn -index wc.nw > wc.tex diff --git a/web/noweb/examples/Makefile.awk b/web/noweb/examples/Makefile.awk new file mode 100644 index 0000000000..304fe88baa --- /dev/null +++ b/web/noweb/examples/Makefile.awk @@ -0,0 +1,58 @@ +WWW=$(HOME)/www/noweb/examples +SHELL=/bin/sh +NOTANGLE=nountangle -m3 +NOWEAVE=noweave + +.SUFFIXES: .i3 .m3 .nw .tex .dvi .html +.nw.html: ; $(NOWEAVE) -filter btdefn -index -html $*.nw > $*.html +.nw.tex: ; $(NOWEAVE) -index -filter btdefn $*.nw > $*.tex +.nw.i3: ; $(NOTANGLE) -Rinterface -L'<* LINE %L "%F" *>%N' $*.nw > $*.i3 +.nw.m3: ; $(NOTANGLE) -L'<* LINE %L "%F" *>%N' $*.nw > $*.m3 +.tex.dvi: ; latex '\scrollmode \input '"$*"; while grep -s 'Rerun to get cross-references right' $*.log; do latex '\scrollmode \input '"$*"; done + +HTML=breakmodel.html compress.html dag.html graphs.html mipscoder.html primes.html \ + scanner.html test.html tree.html wc.html wcni.html +DVI=compress.dvi dag.dvi mipscoder.dvi scanner.dvi tree.dvi test.dvi wc.dvi + + +texonly: $(DVI) + +www: $(HTML) + copy -v $(HTML) $(WWW) + copy -v README.h $(WWW)/index.html + +clean: + rm -f *~ *.aux *.tex *.dvi *.log *.html *.toc + +clobber: clean + +compress.html: compress.nw + $(NOWEAVE) -index -html compress.nw > compress.html +compress.tex: compress.nw + $(NOWEAVE) -index compress.nw > compress.tex +dag.html: dag.nw + $(NOWEAVE) -index -html dag.nw > dag.html +dag.tex: dag.nw + $(NOWEAVE) -index dag.nw > dag.tex +mipscoder.html: mipscoder.nw + $(NOWEAVE) -index -html mipscoder.nw > mipscoder.html +mipscoder.tex: mipscoder.nw + $(NOWEAVE) -index mipscoder.nw > mipscoder.tex +scanner.html: scanner.nw + $(NOWEAVE) -index -html scanner.nw > scanner.html +scanner.tex: scanner.nw + $(NOWEAVE) -index scanner.nw > scanner.tex +tree.html: tree.nw + $(NOWEAVE) -index -html tree.nw > tree.html +tree.tex: tree.nw + $(NOWEAVE) -index tree.nw > tree.tex +test.html: test.nw + $(NOWEAVE) -html test.nw > test.html +test.tex: test.nw + $(NOWEAVE) test.nw > test.tex +wc.html: wc.nw2html + $(NOWEAVE) -filter btdefn -index -html -n wc.nw2html > wc.html +wcni.html: wc.nw2html + $(NOWEAVE) -filter btdefn -x -html -n wc.nw2html > wcni.html +wc.tex: wc.nw + $(NOWEAVE) -filter btdefn -index wc.nw > wc.tex diff --git a/web/noweb/examples/README b/web/noweb/examples/README new file mode 100644 index 0000000000..3cf8e981a4 --- /dev/null +++ b/web/noweb/examples/README @@ -0,0 +1,49 @@ +All these examples have been set up to work with latex, although I +often prefer plain TeX because latex's page breaking is so bad and so +hard to control. breakmodel.nw is the only one that requires +cross-referencing (noweave -x). I have included the nonstandard +`multicol' style file, although it is readily available as part of the +Mainz distribution. + +Except for breakmodel.nw, each example file is a fragment of a larger +program. noweb itself is not a good example of literate programming. +It is harder than most people realize to write a truly literate +program, and noweb wasn't worth the effort. + +If you have installed noweb with LIBSRC=awk instead of LIBSRC=icon, +you need to use Makefile.awk instead of Makefile. + +The files are: + + Makefile Shows some sample default rules for use + in a Makefile. Try `make tree.tex tree.dvi'. + Makefile.awk Like Makefile but doesn't require Icon. + None of the HTML examples will be readable + without Icon. + breakmodel.nw A formal model of breakpoints using the + Promela modeling language. + dag.nw Fragment of an Icon program that compiles + patterns into decision-tree pattern-matching + code. tree.nw builds the decision trees; + dag.nw turns them into dags. + graphs.nw Several graphs written in jgraph (write + plank@cs.utk.edu for information about jgraph). + mipscoder.nw Part of the original MIPS code generator from + Standard ML of New Jersey. Writting in Standard ML. + primes.nw noweb version of DEK's original prime-number program. + scanner.nw Part of a student compiler project. Includes + C code and lex and yacc specifications, all in + a single file. + sharpline A noweb filter that makes noweb `see' #line directives + in a noweb file. + test.nw A simple test file. + tree.nw Fragment of an Icon program that compiles + patterns into decision-tree pattern-matching + code. tree.nw builds the decision trees; + dag.nw turns them into dags. + wc.nw A re-implementation in noweb of the CWEB + example from DEK's book. This example is + intended to be as exact a copy as possible; no + attempt was made to improve the code. + whitespace A noweb filter that makes whitespace insignificant + within chunk names. diff --git a/web/noweb/examples/README.h b/web/noweb/examples/README.h new file mode 100644 index 0000000000..0ff13d92e8 --- /dev/null +++ b/web/noweb/examples/README.h @@ -0,0 +1,72 @@ +<html> +<head> +<title>Examples programs written in <tt>noweb</tt></title> +</head> +<body> +<h1>Noweb example programs</h1> +All but one of +these examples have had their documentation parts converted automatically +from LaTeX to HTML. +(Can you tell which one?) +Except for <a href=breakmodel.html>breakmodel</a>, +each example file is a fragment of a larger +program. +<p> +The typical command line used to create one of these documents is: +<pre> +noweave -filter l2h -index -autodefs c -html compress.nw > compress.html +</pre> +for various values of <tt>c</tt> :-) +<p> +The example programs are: +<dl> +<dt><a href=breakmodel.html>breakmodel</a><dd> + A formal model of breakpoints using the + Promela modeling language. +<dt><a href=compress.html>compress</a><dd> + A library that modifies the <t>open</t>, <t>close</t>, <t>read</t>, + and <t>write</t> system calls (along with some others) to + transparently read and write files in Unix <t>compress</t> format. + Graciously contributed by + <a href=http://www.cs.princeton.edu/~blume>Matthias Blume</a> +<dt><a href=dag.html>dag</a><dd> + Fragment of an <a href=http://www.cs.arizona.edu/icon/>Icon</a> + program that compiles + patterns into decision-tree pattern-matching + code. tree.nw builds the decision trees; + dag.nw turns them into dags. +<dt><a href=graphs.html>graphs</a><dd> + Several graphs written in + <a href=http://www.cs.utk.edu/~plank/plank/jgraph/jgraph.html>jgraph</a>. +<dt><a href=mipscoder.html>mipscoder</a><dd> + Part of the original MIPS code generator from + Standard ML of New Jersey. Written in Standard ML. +<dt><a href=primes.html>primes</a><dd> + noweb version of DEK's original prime-number program. + I got tired of typing and never entered all the text. +<dt><a href=scanner.html>scanner</a><dd> + Part of a student compiler project. Includes + C code and lex and yacc specifications, all in + a single file. +<dt><a href=solver.html>solver</a><dd> +An equation solver, published in <em>Software---Practice & Experience</em>. +The <A href=/~nr/pubs/solver.ps>PostScript</a> is probably easier to read. +<dt><a href=test.html>test</a><dd> + A simple test file. +<dt><a href=tree.html>tree</a><dd> + Fragment of an <a href=http://www.cs.arizona.edu/icon/>Icon</a> + program that compiles + patterns into decision-tree pattern-matching + code. tree.nw builds the decision trees; + dag.nw turns them into dags. +<dt><a href=wc.html>wc</a><dd> + An re-implementation of the example word-count program from Don + Knuth's book on literate programming. + This example is as exact a copy as possible; no + attempt was made to improve the code. + <a href="wcni.html">Here</a> is a version without identifier + cross-reference, which gives it a cleaner look. + <a href="wc.nw.html">Here</a> you can see a plain-text + rendering of the <tt>noweb</tt> source. +</dl> +</body> diff --git a/web/noweb/examples/breakmodel.nw b/web/noweb/examples/breakmodel.nw new file mode 100644 index 0000000000..284ad01719 --- /dev/null +++ b/web/noweb/examples/breakmodel.nw @@ -0,0 +1,459 @@ +% \global\def\begindocs#1{\relax} +% \global\let\enddocs=\relax + +{\def\semifilbreak#1{\vskip0pt plus #1\penalty-200\vskip0pt plus -#1} +\def\single{\def\baselinestretch{1.0}\small\normalsize} + +% l2h ignore semifilbreak { +% l2h ignore single + +% l2h substitution LA < +% l2h substitution RA > + +\section{A Formal Model of Breakpoints} +\label{appendix:breakpoint-model} +This appendix provides a formal model of {\tt ldb}'s follow-set breakpoints. +The model takes the form of a PROMELA program~\cite{holzmann:design}. +PROMELA programs define several threads of control that communicate by +passing messages. +Each thread of control runs a program written in a guarded-command +language with a C-like syntax. +Programs may be nondeterministic. +PROMELA can simulate the execution of a program and +search its state space for states violating assertions embedded in the +program. +The simulator also searches for states with no successors, i.e., +deadlocks. + +The PROMELA code in this appendix models {\tt ldb}'s implementation of +breakpoints. +Although {\tt ldb} does not work with multithreaded programs, the +model uses multiple threads because a procedure call from {\tt ldb} to +a target process effectively creates a new thread. +The assertions embedded in the model specify that the debugger +takes a breakpoint action just before any thread's +successful execution of the instruction at the breakpoint. +Breakpoints may be implemented either in the operating system or in +the debugger itself; the choice does not affect the model used here. +The model assumes it can plant trap instructions in +the instruction stream of the target program, and that +it will be notified when the target program encounters a trap. +The model also suits a machine with a ``trace mode'' + that causes a trap after the execution of every instruction. + + +The model has a single +breakpoint. +To keep the state space small, the model has only two threads, so that + a single bit can represent thread [[id]]s.\label{noweb-sample-page-number} +<<declarations>>= +#define NTHREADS 2 +#define threadid bit +@ \noindent +The {\footnotesize\pageref{noweb-sample-page-number}} in +\LA{}declarations~\footnotesize\pageref{noweb-sample-page-number}\RA{} +is the page number on which the definition appears. + +\section{Modeling the program counter and execution} +To keep things simple, I partition the possible values of the program +counter into three sets: +\begin{quote} +\begin{tabular}{ll} + [[Break]]&the breakpoint itself,\\ + [[Follow]]&the instruction(s) following the breakpoint,\\ + [[Outside]]&outside the breakpoint.\\ +\end{tabular} +\end{quote} +\semifilbreak{2\baselineskip} +\noindent The three sets are modeled by the following constants. +<<declarations>>= +#define NPCS 3 +#define Break 0 /* pc at the breakpoint */ +#define Follow 1 /* pc in breakpoint's follow set */ +#define Outside 2 /* all other pc's */ +@ + +The ability to plant traps is modeled by the array [[trapped]], which +records whether a trap instruction has been +stored at a particular location: +<<declarations>>= +bool trapped[NPCS]; +@ + +The model has five active components: two threads, a CPU that +executes one thread at a time, the breakpoint, and the rest of the debugger. +Here are the channels that are used for communication between the +threads, the CPU, the breakpoint, and the debugger. +Taking a breakpoint action is modeled by sending a message on the channel +[[breakaction]]. +<<declarations>>= +chan execute[NTHREADS] = [0] of {bit}; /* try to execute instruction */ +chan cont[NTHREADS] = [0] of {bit}; /* instruction executed */ +chan trap = [0] of {byte}; /* CPU trapped on id! */ +chan resume = [0] of {bit}; /* debugger resumed after trap */ +chan breakaction = [0] of {byte}; /* deliver breakpoint to debugger */ +@ \noindent [[[0]]] indicates that the channels are synchronous; + senders block until a receiver is ready and vice~versa.{\hfuzz=0.9pt\par} + +The communication structure is: +\begin{center} +\setlength{\unitlength}{0.01in}% +\footnotesize +\begin{picture}(580,180)( 50,-90) +\thicklines +\put(585, 0){\oval(90,60)} +\put(402, 0){\oval(90,62)} +\put(252, 0){\oval(90,60)} +\put( 95, 60){\oval(90,60)} +\put( 95,-60){\oval(90,60)} +\put(295, 10){\vector( 1, 0){ 60}} +\put(355,-10){\vector(-1, 0){ 60}} +\put(450, 0){\vector( 1, 0){ 90}} +\put(140, 55){\vector( 2,-1){ 70}} +\put(205, 10){\vector(-2, 1){ 70}} +\put(135,-45){\vector( 2, 1){ 70}} +\put(210,-20){\vector(-2,-1){ 70}} +\put(585, 0){\makebox(0,0){{\tt debugger()}}} +\put(402, 0){\makebox(0,0){{\tt breakpoint()}}} +\put(255, 0){\makebox(0,0){{\tt CPU()}}} +\put( 95,-65){\makebox(0,0)[b]{{\tt thread(1)}}} +\put( 95, 55){\makebox(0,0)[b]{{\tt thread(0)}}} +\put(165,-21){\makebox(0,0)[rb]{{\tt execute[1]}}} +\put(165, 16){\makebox(0,0)[rb]{{\tt cont[0]}}} +\put(170,-50){\makebox(0,0)[lb]{{\tt cont[1]}}} +\put(165, 50){\makebox(0,0)[lb]{{\tt execute[0]}}} +\put(330,-20){\makebox(0,0)[b]{{\tt resume}}} +\put(330, 15){\makebox(0,0)[b]{{\tt trap!id}}} +\put(493, 5){\makebox(0,0)[b]{{\tt breakaction!id}}} +\end{picture} +\end{center} +@ + +\noindent The CPU repeats the following steps. +\begin{enumerate}\single +\item +Wait for a thread to attempt to execute the instruction at [[pc]]. +\item +If the instruction is a trap, notify the debugger. +When the debugger tells the CPU to resume, [[pc]] is unchanged. +\item +If the instruction is not a trap, advance [[pc]]. +\item +Ask the thread to continue executing. +\end{enumerate} +There is only one debugger, but there are multiple threads, and each +one has its own [[pc]] and its own communication with the CPU. +When the CPU notifies the debugger of a trap, it identifies the +trapping thread. +Other messages are used only for synchronization, so they send and +receive the nonsense variable [[x]]. +<<declarations>>= +bit x; /* junk variable for sending messages */ +@ +A [[proctype]] is a procedure that a thread can execute; this one +models the CPU. + [[c?x]] receives the value [[x]] on channel [[c]]; [[c!x]] sends. +Arrows ([[->]]) separate guards from commands. +<<proctypes>>= +proctype CPU(byte count) { + threadid id = 0; + do + :: execute[id]?x -> + if + :: trapped[pc[id]] -> trap!id ; resume?x + :: !trapped[pc[id]] -> <<advance [[pc[id]]]>> + fi; + cont[id]!x; + <<possible context switch (change of [[id]])>> + od +} +@ Context switching is discussed below. +@ +\semifilbreak{1in} % page tuning +@ +Since the program counter is an abstraction, advancing it does not +mean incrementing it. A successful execution at [[Break]] is +guaranteed to be followed by an attempt to execute [[Follow]]; +aside from that, any instruction can follow any other. +<<advance [[pc[id]]]>>= +if +:: pc[id] == Break -> pc[id] = Follow +:: pc[id] != Break -> /* any instruction can be next */ + if + :: pc[id] = Outside + :: pc[id] = Break + :: pc[id] = Follow + fi +fi +@ \noindent +The second [[if]] statement has no guards, so an alternative is +chosen nondeterministically. + +All threads begin execution outside the breakpoint. +<<declarations>>= +byte pc[NTHREADS]; +<<initialize data for thread [[id]]>>= +pc[id] = Outside; + +@ +\section{Counting events} +The correctness criterion for the breakpoint implementation is that +one breakpoint action must be taken for every +successful execution of an instruction at [[Break]]. +[[threadcount[id]]] counts how many times thread~[[id]] +has executed the breakpoint, and [[actioncount[id]]] counts how many +breakpoint actions have been taken on behalf of thread~[[id]]. +<<declarations>>= +byte threadcount[NTHREADS]; +byte actioncount[NTHREADS]; +<<initialize data for thread [[id]]>>= +threadcount[id] = 0; +actioncount[id] = 0; +@ +\semifilbreak{0.75in} % page tuning +@ +Here is the model of a thread, including the assertion that the thread +and debugger counts are the same: +<<proctypes>>= +proctype thread(threadid id) { + do + :: if + :: pc[id] == Break -> execute[id]!x; cont[id]?x; + <<if successfully executed [[Break]], increment [[threadcount[id]]]>> + :: pc[id] != Break -> execute[id]!x; cont[id]?x + fi; + assert(pc[id] != Outside || threadcount[id] == actioncount[id]) + od +} +@ +The corresponding model of the debugger is +<<proctypes>>= +proctype debugger() { + threadid id; + do + :: atomic { breakaction?id -> <<increment [[actioncount[id]]]>> } + od +} +@ \noindent +[[atomic]] groups statements into a single atomic action. +When the debugger takes a breakpoint action, it atomically increments +[[actioncount[id]]]. +Without [[atomic]], it might delay incrementing the counter and +invalidate the assertion above. + +A thread knows it has successfully executed [[Break]] if the [[pc]] +has changed: +<<if successfully executed [[Break]], increment [[threadcount[id]]]>>= +if +:: pc[id] != Break -> <<increment [[threadcount[id]]]>> +:: pc[id] == Break -> skip +fi +@ + To keep the state space small, I restrict the values of the +counters to be in the range [[0..3]]. +<<increment [[threadcount[id]]]>>= +threadcount[id] = (threadcount[id] + 1) % 4 +<<increment [[actioncount[id]]]>>= +actioncount[id] = (actioncount[id] + 1) % 4 +@ +\section{Implementing the breakpoint} +There is a long tradition of implementing breakpoints using traps +and single stepping. To set a breakpoint at $I$, plant a trap +at $I$. When the target program hits the trap, that's a breakpoint +event. +To resume execution after the breakpoint, + restore the original instruction to $I$, + single step the machine to execute just the instruction at $I$, +and once again plant a trap at $I$ and continue execution. +Not all machines have a single-step mode in hardware, but +single stepping can be simulated in software by using more trap +instructions. +In my model, I eliminate single stepping entirely, working directly +with trap instructions and a follow set +(modeled by [[Follow]]). + +The simpler model does not preclude the use of hardware single stepping. +One of the operations in the model is planting traps at the locations +in the follow set of an instruction. +This operation can be implemented either by computing the follow set +and planting actual traps, or by setting a trace bit on a machine with +hardware single stepping. + +\semifilbreak{3\baselineskip} + +An active breakpoint is trapped either on the instruction of the +breakpoint itself or on that instruction's follow set. +The breakpoint keeps track of which state it is in, with the following +invariant. +\begin{verbatim} + breakstate == Break && trapped[Break] = 1 && trapped[Follow] = 0 +|| breakstate == Follow && trapped[Break] = 0 && trapped[Follow] = 1 +\end{verbatim} +<<declarations>>= +byte breakstate = Break; +<<initialization>>= +trapped[Break] = 1; +@ Changing the state preserves the invariant.\label{move-traps-page} +<<move traps to [[Break]]>>= +atomic { breakstate = Break; trapped[Break] = 1; trapped[Follow] = 0 } +<<move traps to [[Follow]]>>= +atomic { breakstate = Follow; trapped[Break] = 0; trapped[Follow] = 1 } +@ + +It's necessary to keep track of the state of each thread with respect +to the breakpoint. A thread is ``in the breakpoint'' if it has +trapped at [[Break]], and it does not ``leave the breakpoint'' until +it traps at [[Follow]]. Threads are initially outside the breakpoint. +<<declarations>>= +bit inbreak[NTHREADS]; +<<initialize data for thread [[id]]>>= +inbreak[id] = 0; +@ +\semifilbreak{2in} % page tuning +@ +One possible implementation just keeps track of the various states and +delivers a breakpoint event at the right time: +<<candidate breakpoint implementation>>= +proctype breakpoint() { + threadid id; + + do + :: trap?id -> + if + :: breakstate == Break -> + if + :: !inbreak[id] -> breakaction!id ; inbreak[id] = 1 + :: inbreak[id] -> skip /* no event */ + fi; + <<move traps to [[Follow]]>> + :: breakstate == Follow -> + if + :: inbreak[id] -> inbreak[id] = 0 + :: !inbreak[id] -> skip + fi; + <<move traps to [[Break]]>> + fi; + resume!x + od +} +@ +This implementation works fine for a single thread. +With two threads, the PROMELA state-space search finds the +following erroneous execution sequence +(attempted executions that trap are marked with a {*}): +\begin{quote}\single +\begin{tabular}{llcc} +breakpoint (debugger)& CPU & thread 0 & thread 1\\ +& & [[Outside]]\\ +& & [[Break]]\rlap{*}\\ +%\multicolumn{3}{l} +{\LA{}take breakpoint action\RA{}}\\ +{\LA{}move traps to [[Follow]]~\footnotesize\pageref{move-traps-page}\RA{}}\\ +resume\\ +&context switch\\ + &&& [[Outside]]\\ + &&& [[Break]]\\ + &&& [[Follow]]\rlap{*}\\ +%\multicolumn{3}{l} +{\LA{}take no action\RA{}}\\ +{\LA{}move traps to [[Break]]~\footnotesize\pageref{move-traps-page}\RA{}}\\ +resume\\ + &&& [[Outside]]\\ +&context switch\\ + && [[Follow]]\\ + && [[Outside]]\\ +\end{tabular} +\end{quote} +In this execution sequence, thread~1 goes through the breakpoint +without triggering a breakpoint action. +In an earlier version of {\tt ldb}, this sequence could be provoked by +executing a procedure call after the user's program hit a breakpoint; +the user's program was thread~0, and the procedure call was thread~1. + +%%%% \semifilbreak{3in} % page tuning +To prevent such an occurrence, the CPU must not be permitted to change +contexts when a thread is in the middle of a breakpoint. +If the CPU can change contexts only when [[noswitch == 0]], then the +following breakpoint implementation works correctly. +<<proctypes>>= +proctype breakpoint() { + threadid id; + + do + :: trap?id -> + if + :: breakstate == Break -> + if + :: !inbreak[id] -> breakaction!id ; inbreak[id] = 1 + :: inbreak[id] -> assert(0) + fi; + noswitch = noswitch + 1; + <<move traps to [[Follow]]>> + :: breakstate == Follow -> + if + :: inbreak[id] -> inbreak[id] = 0 + :: !inbreak[id] -> assert(0) + fi; + noswitch = noswitch - 1; + <<move traps to [[Break]]>> + fi; + resume!x + od +} +@ \noindent The ban on context switching makes it possible to strengthen + [[skip]] to [[assert(0)]]. + +[[noswitch]] is declared to be a counter, not a bit, because that +implementation generalizes to multiple breakpoints. +<<declarations>>= +byte noswitch = 0; +@ +\semifilbreak{1.6in} % page tuning +@ +The CPU code to do the context switching correctly is: +<<possible context switch (change of [[id]])>>= +if +:: noswitch == 0 -> <<set [[id]] randomly>> +:: noswitch > 0 -> skip +fi +<<set [[id]] randomly>>= +atomic { + if + :: id = 0 + :: id = 1 + fi +} +@ +\section{Completing the model} +The boilerplate needed to turn the model into +a complete PROMELA specification is: +<<*>>= +<<declarations>> +<<proctypes>> +init { + threadid id; + atomic { + <<initialization>> + <<for $0 \le \tt id < NTHREADS$, initialize data for thread [[id]]>>; + run thread(0); + run thread(1); + run debugger(); + run breakpoint(); + run CPU (2) + } +} +@ \semifilbreak{1in} +<<for $0 \le \tt id < NTHREADS$, initialize data for thread [[id]]>>= +id = 0; +do +:: id < NTHREADS -> <<initialize data for thread [[id]]>> + if + :: id == NTHREADS - 1 -> break + :: id < NTHREADS - 1 -> id = id + 1 + fi +od +@ +} +@ +\section{List of chunks} +\nowebchunks diff --git a/web/noweb/examples/compress.nw b/web/noweb/examples/compress.nw new file mode 100644 index 0000000000..9c24df13fe --- /dev/null +++ b/web/noweb/examples/compress.nw @@ -0,0 +1,1637 @@ +\title{Transparent on-the-fly data compression {\tt COS595}} +\author{Matthias Blume\\ +Dept.\ of Computer Science, Princeton University,\\ +Princeton, NJ 08544} + +\maketitle + +\tableofcontents + +\section{Introduction} + +The C library on UNIX provides functions for calling the operating +system. +Replacing those functions can provide a different program functionality +without the need to make changes to the program text. + +Replacing the functions [[open]], [[creat]], [[close]], [[read]] and +[[write]] along with a couple of other routines will change the file +system interface. +Since on today's computer systems most information are stored with a +high redundancy, it seems to be useful to apply data compression +algorithms to them. +If the compression and uncompression is done by the file system +interface itself, then it will become transparent to the programs +using it. + +Although a truly general solution can only be done in the file system +itself, it is nevertheless possible to approximate this to a high +degree within the C library. +I will show a sample implementation, which does this. + +\section{System call substitutes} + +Since I am going to replace the functions [[open]], [[read]], +[[write]] etc. by my own versions, I will not be able to use them to do +actual input and output. +Therefore it is necessary to write new versions of the original C +library function. +The only things to change are the {\em names} of those routines. + +The following program text gives the implementation for MIPS-based +machines. +By using the {\em m4} macro processor, which is available on +most UNIX systems, I take advantage of the fact, that the sequences of +instructions needed for any of the functions follow a +common pattern. + +<<mips-asm.m>>= +# include <sys/syscall.h> +# include <regdef.h> + + .text + + .globl _cerror + +define(sc,` + .globl _sys_$1 + .ent _sys_$1 +_sys_$1: + li v0, SYS_$1 + syscall + beq a3, zero, 9f + j _cerror +9:$2 + j ra + .end') + +sc(open) +sc(creat) +sc(close) +sc(read) +sc(write) +sc(dup) +sc(lseek) +sc(pipe,` sw v0, 0 (a0) + sw v1, 4 (a0)') +@ +The Second argument of the [[sc]] macro is necessary to implement the +[[pipe]] system call, because it has to store two file descriptors +into locations given as the argument of the call. + +@ +\section{Overall structure} + +My new versions of the file system interface functions are implemented +in the file [[compress.c]]. +The structure of the file can be described as follows: + +<<compress.c>>= +<<include files>> +<<constant definitions>> +<<type definitions>> +<<external prototypes>> +<<static prototypes>> +<<static definitions>> +<<initialization>> +<<write compressed>> +<<read compressed>> +<<other io-substitutes>> +<<replaced system calls>> +@ +The necessary system header files are: + +<<include files>>= +# include <stdlib.h> +# include <stdio.h> +# include <string.h> +# include <assert.h> +# include <fcntl.h> +# include <errno.h> +@ + +\section{Replacing system calls} + +In this implementation I will give substitutes for the following +functions: +\begin{itemize} +\item +[[creat]] - creates a new file or truncates it to 0 bytes; opens for +writing +\item +[[open]] - opens an existing file for reading or writing +\item +[[close]] - releases the association between a file descriptor and the +corresponding file +\item +[[read]] - reads a number of bytes from an open file into a given buffer +\item +[[write]] - writes a number of bytes from a given buffer to an open file +\item +[[dup]] - duplicates a file descriptor +\item +[[lseek]] - provides random access to arbitrary positions within an +open file (if supported) +\item +[[tell]] - this is not a true system call and could be simulated by +[[lseek]]; it returns the current position in the file +\item +[[pipe]] - opens an inter-process communication channel known as a +{\em pipe}; returns two file descriptors, one for reading from the +pipe, one for writing into the pipe +\end{itemize} + +The real file system interface will be driven by the external +functions: +<<external prototypes>>= +extern _sys_open (const char *, int); +extern _sys_creat (const char *, int); +extern int _sys_close (int); +extern int _sys_read (int, char *, int); +extern int _sys_write (int, const char *, int); +extern int _sys_dup (int fd); +extern long _sys_lseek (int fd, long offset, int whence); +extern int _sys_pipe (int [2]); +@ +and the replaced system call functions are: +<<replaced system calls>>= +<<replaced creat>> +<<replaced open>> +<<replaced close>> +<<replaced read>> +<<replaced write>> +<<replaced dup>> +<<replaced tell>> +<<replaced lseek>> +<<replaced pipe>> +@ +Data compression and uncompression will be done by the Lempel-Ziv +algorithm. +It is necessary to maintain several independent compression- or +un\-com\-pres\-sion-``engines'', because there can be many files open. +There is no fixed relationship between the number of characters read +from or written to the real file system and the number of characters +seen by the program. +This means, both compression and uncompression must be able to stop +``in the middle of the operation''. +All the relevant variables, which constitute the ``state'' of the +``engine'' have to be saved in a data structure, which in turn has to +be associated with the file descriptor. + +The existence of the [[dup]] system call introduces some further requirements +on the implementation. +Basically this means, that the same ``engine'' can be associated with +more than one file descriptor. + +Currently I restrict myself to at most [[MAXFILES]] open files. +This can easily be changed by using [[getdtablesize]] to find out the +maximum number of open files allowed by the operating system. + +<<constant definitions>>= +# define MAXFILES 256 +@ +The states of ``engines'' are stored in structures of type [[struct cfd]]. + +<<type definitions>>= +typedef struct cfd *cfd; + +struct cfd { + struct methods *methods; + int nbits; + int shared; + <<other cfd members>> +}; +@ +One crucial idea to deal with the complexity of the problem is to +adopt some ``object-oriented'' techniques. +I use the member [[methods]] to point to a collection of function +pointers. +Depending on whether a file is read or written, compressed or plain, I +need different algorithms to access the file. +Using the table of [[methods]] allows to do this without complicated +{\em if-then-else} chains all over the place. + +<<type definitions>>= +struct methods { + int (* read) (int, unsigned char *, int); + int (* write) (int, unsigned char *, int); + int (* close) (int); + long (* seek) (int, long, int); +}; +@ +There will be a global file table [[filetab]], indexed by file +descriptors, which contains pointers to structures of type [[struct cfd]]. +The contents of this table at a given position depends on the mode of +operation used with the file descriptor under question. +Possible modes are: +\begin{itemize} +\item +the file is a plain file (no compression or uncompression) +\item +the file is written using compression +\item +the file is read using uncompression +\item +the file is opened for reading, but it is still unclear, whether it is +compressed or not +\item +the file is opened for reading; the ``engine'' found, that the file is +not compressed by reading the first few characters of the file; +the program has not asked yet for all of those pre-read characters +\item +the file is unknown to the interface module---this usually happens, if +the file descriptor is inherited from a parent process (e.g. the +shell) +\end{itemize} + +Descriptors for plain files and unknown file descriptors are +passed directly to the real system calls. +Therefore, these two modes are represented the same: by a [[NULL]] +pointer in the corresponding position of the file table. + +We need two different method tables for reading or writing in +compressed mode. +A third table is necessary to deal with the remaining modes of operation. +A compressed file is recognized by the first three characters in the +file, which are known as the [[compress_prefix]]. + +<<static definitions>>= +<<file table>> +<<method tables>> +<<compress prefix>> +@ + +<<compress prefix>>= +static unsigned char compress_prefix [] = { 0x1f, 0x9d, 0x10 }; +@ + +<<file table>>= +static cfd filetab [MAXFILES] = { NULL, }; +@ +The three collections of methods are: + +<<method tables>>= +<<write-compressed table>> +<<read-compressed table>> +<<initial read table>> +@ +For writing: + +<<write-compressed table>>= +static struct methods cw_m = { + refuse_io, + write_compressed, + close_compressed_write, + refuse_seek, +}; +@ +For reading: + +<<read-compressed table>>= +static struct methods cr_m = { + read_compressed, + refuse_io, + close_compressed_read, + refuse_seek, +}; +@ +For deciding, whether to read a compressed or a plain file: + +<<initial read table>>= +static struct methods ir_m = { + read_prefix, + refuse_io, + close_prefix_read, + prefix_seek, +}; +@ +To define the above tables, I need the following prototype definitions: + +<<static prototypes>>= +static int read_compressed (int fd, unsigned char *buf, int n); +static int read_prefix (int fd, unsigned char *buf, int n); +static int write_compressed (int fd, unsigned char *buf, int n); +static int refuse_io (int fd, unsigned char *buf, int n); + +static int close_compressed_read (int fd); +static int close_prefix_read (int fd); +static int close_compressed_write (int fd); + +static long refuse_seek (int fd, long offset, int whence); +static long prefix_seek (int fd, long offset, int whence); +@ +In order not to confuse other programs started by a combination of +[[fork]] and [[exec]], I always use the real system call along with +the replaced one. +Therefore, the indices into [[filetab]] are real file descriptors +provided by the operating system. + +[[creat]] always sets the mode of operation to ``write with +compression'' by using the methods [[cw_m]]. + +As a general rule, I set the member [[nbits]] to zero when opening a +file. +This signals, that the data structures have not been fully initialized yet. + +The member [[shared]] counts the number of [[filetab]] entries, which +point to the same data structure. +The routines for closing a file will use this to determine, +whether the last connection to the file will be closed. +Only in this case I can perform cleanup operations like freeing the +data structures associated with the file. + +<<replaced creat>>= +int creat (const char *path, int mode) +{ + int fd; + + init (); + + fd = _sys_creat (path, mode); + + if (fd < 0 || fd > MAXFILES || + (filetab [fd] = malloc (sizeof (struct cfd))) == NULL) + return fd; + + filetab [fd]->nbits = 0; + filetab [fd]->methods = &cw_m; + + filetab [fd]->shared = 1; + + return fd; +} +@ +Opening a file for writing using [[open]] assumes, that the file +already exists. +Therefore it is not useful to write with compression, because the +compressed data will interfere with what has already been in the file. +As a consequence I leave the entry in [[filetab]] unchanged. + +It turns out, that new versions of the C library don't use [[creat]], +but call [[open]] with some additional flags and parameters as +specified by [[POSIX]]. +This means, that I have to simulate the desired behavior by calling +[[creat]] from within [[open]] if necessary. + +Opening a file for reading is the most complex case. +At some time I need to read the first few characters of the file to +decide, whether the file is compressed or not. +The most natural place to do this seems to be the [[open]] routine. +Unfortunately, this would violate the semantics of [[open]]. +(Imagine opening a terminal file for reading!) +The decision has to be delayed until the first call to [[read]] will +be executed. +[[open]] sets the mode of operation to ``unclear, whether to use +decompression'' by using the method table [[ir_m]]. + +<<replaced open>>= +int open (const char *path, int how, int mode) +{ + int fd; + + if (how == (O_WRONLY | O_CREAT | O_TRUNC)) + return creat (path, mode); + + init (); + + fd = _sys_open (path, how); + if (fd < 0 || fd > MAXFILES || how != 0) + return fd; + + if ((filetab [fd] = malloc (sizeof (struct cfd))) == NULL) + return fd; + + filetab [fd]->nbits = 0; + filetab [fd]->methods = &ir_m; + filetab [fd]->shared = 1; + + return fd; +} +@ +Most of the remaining substitutes for system call functions follow a +common pattern: +\begin{itemize} +\item +look, if the file descriptor points to non-[[NULL]] in [[filetab]] +\item +if not, then simply use the original system call +\item +otherwise call the appropriate function from the methods table +\end{itemize} + +<<replaced close>>= +int close (int fd) +{ + return (fd < 0 || fd > MAXFILES || filetab [fd] == NULL) + ? _sys_close (fd) + : (* filetab [fd]->methods->close) (fd); +} +@ + +<<replaced read>>= +int read (int fd, char *buf, int n) +{ + return (fd < 0 || fd > MAXFILES || filetab [fd] == NULL) + ? _sys_read (fd, buf, n) + : (* filetab [fd]->methods->read) (fd, (unsigned char *) buf, n); +} +@ + +<<replaced write>>= +int write (int fd, const char *buf, int n) +{ + return (fd < 0 || fd > MAXFILES || filetab [fd] == NULL) + ? _sys_write (fd, buf, n) + : (* filetab [fd]->methods->write) (fd, (unsigned char *) buf, n); +} +@ +[[dup]] simply copies, what is in [[filetab]] at a given place to +another place. +The [[cfd]]-member [[shared]] must be incremented. + +<<replaced dup>>= +int dup (int fd) +{ + int res = _sys_dup (fd); + + if (fd < 0 || fd > MAXFILES || filetab [fd] == NULL) + return res; + assert (res < MAXFILES); + assert (filetab [res] == NULL); + filetab [res] = filetab [fd]; + filetab [res]->shared++; + return res; +} +@ +[[lseek]], again, follows the general pattern. +There is one minor variation: if the arguments indicate, that no +actual repositioning is asked for, [[tell]] gets called. + +<<replaced lseek>>= +long lseek (int fd, long offset, int whence) +{ + if (offset == 0 && whence == 1) + return tell (fd); + return (fd < 0 || fd > MAXFILES || filetab [fd] == NULL) + ? _sys_lseek (fd, offset, whence) + : (* filetab [fd]->methods->seek) (fd, offset, whence); +} +@ +[[tell]] is not a real system call. +I simulate it using [[lseek]] if necessary. +For files in compressed mode I keep track of the file position myself. +Note, that [[filepos]] is not initialized until the first [[read]] from +or [[write]] to the file has been executed. + +<<other cfd members>>= +long filepos; +@ + +<<replaced tell>>= +long tell (int fd) +{ + return (fd < 0 || fd > MAXFILES || filetab [fd] == NULL) + ? _sys_lseek (fd, 0L, 1) + : (filetab [fd]->nbits < MINBITS) + ? 0 + : filetab [fd]->filepos; +} +@ +Since [[pipe]] creates two file descriptors, I have to deal with two +entries in [[filetab]]. +Writing to the pipe will be performed in compressed mode. +This seems to imply, that reading has to use compressed mode as well, +but this is not the case. +The most common case of using pipes is in the context of [[fork]] and +[[exec]]. +It is very likely, that the pipe will be written by another program, +and I have to check, whether this program uses compression or not. + +<<replaced pipe>>= +int pipe (int fd [2]) +{ + cfd p0, p1; + + init (); + + if (_sys_pipe (fd) < 0) + return -1; + + if (fd [0] > MAXFILES || fd [1] > MAXFILES) + return 0; + p0 = malloc (sizeof (struct cfd)); + if (p0 == NULL) + return 0; + p1 = malloc (sizeof (struct cfd)); + if (p1 == NULL) { + free (p0); + return 0; + } + + p0->nbits = p1->nbits = 0; + p0->methods = &ir_m; + p1->methods = &cw_m; + p0->shared = p1->shared = 1; + + filetab [fd [0]] = p0; + filetab [fd [1]] = p1; + + return 0; +} +@ + +\section{Initialization} + +The library function [[atexit]] provides a way to register a function, +which will be called, when the program exits (i.e. when it calls [[exit]]). +I use this to register a function, which closes all the open files +found in [[filetab]]. + +As you might have noticed, [[init]] will be called from any of the +functions, which create non-[[NULL]] entries in [[filetab]]. + +<<initialization>>= +static void cleanup (void) +{ + int i; + + for (i = 0; i < MAXFILES; i++) + if (filetab [i] != NULL) + (* filetab [i]->methods->close) (i); +} +@ +The registration of [[cleanup]] will be done exactly once. + +<<initialization>>= +static void init (void) +{ + static int initialized = 0; + + if (initialized == 0) { + atexit (cleanup); + initialized = 1; + } +} +@ + +\section{Compression} + +Compression employs the adaptive Lempel-Ziv algorithm. +Tables are constructed as data are written. +Every sequence of characters ever seen by the algorithm (which uses a +greedy heuristic to construct these sequences) is associated with a +unique code. +The [[cfd]]-member [[nextcode]] always holds the next available code +to be associated with the next sequence. +Because the algorithm writes data, which are not always aligned to +byte-boundaries, I have to use a buffer, the size of which is a +multiple of the current code size [[nbits]] and a multiple of eight. +Since the maximum code size is sixteen, a buffer of at most 16 bytes +is required. + +<<constant definitions>>= +# define TABSIZE 8192 +# define MAXBITS 16 +# define MINBITS 9 +# define FIRSTCODE 256 +@ + +I use [[struct cfd]] for both compress and uncompress. +Some of the members in [[struct cfd]] are only used for either +compression or uncompression, and not for both. +In order to save some space, these members are placed into a union. + +<<other cfd members>>= +unsigned long nextcode; +unsigned char buf [MAXBITS]; +int bitpos; +union { + struct { + <<compress-only members>> + } c; + struct { + <<uncompress-only members>> + } d; +} u; +@ +[[lastcode]] holds the code of the character sequence seen so far. +[[codes]] is a hashtable, which is used to describe the mapping of +strings to codes. + +<<compress-only members>>= +struct centry **codes; +unsigned long lastcode; +@ +The hashtable used by this algorithm has fixed size and uses chaining +to deal with collisions. +The data structure for the chaining is described by: + +<<type definitions>>= +struct centry { + unsigned short w; + unsigned char c; + unsigned short code; + struct centry *next; +}; +@ +Here, [[w]] is the code for the string without the last character, +[[c]] is the last character, [[code]] is the code for this sequence +and [[next]] holds the next entry of the chain. + +<<write compressed>>= +<<hashtable management>> +<<writing bits>> +<<writing character arrays>> +<<finish writing>> +@ +This is the hashtable management: + +<<hashtable management>>= +<<hash function>> +<<hashtable lookup>> +@ + +<<hash function>>= +# define hash(x,y) (((x)<<8|(y))%TABSIZE) +@ +There is not very much to say about hashtable lookup. +The only important thing to note is, that I use a ``move-to-front'' +heuristic to speed things up. + +<<hashtable lookup>>= +static struct centry *lookup (cfd fd, unsigned char c) +{ + unsigned lc = fd->u.c.lastcode; + struct centry **start = + &fd->u.c.codes [hash(lc, c)]; + struct centry **cur = start; + struct centry *tmp; + + while (*cur != NULL && ((*cur)->w != lc || (*cur)->c != c)) + cur = & (*cur)->next; + if (*cur == NULL) + return NULL; + else { + tmp = *cur; + *cur = tmp->next; + tmp->next = *start; + *start = tmp; + return tmp; + } +} +@ +In order to write a number of bits it is necessary to use the [[buf]] +member of the [[cfd]] structure, because I cannot write fractions of a +byte. +It is just a matter of shifting and masking bits correctly... + +I give the description of [[invmask]] here, although it is used only +later for reading bits. + +<<writing bits>>= +# define mask(x,n) ((x) & (~(~0 << (n)))) +# define invmask(x,n) ((x) & (~0 << (n))) +@ + +<<writing bits>>= +static int output (cfd fd, int ifd) +{ + unsigned char *byte = fd->buf + fd->bitpos / 8; + int bit = fd->bitpos % 8; + unsigned code = fd->u.c.lastcode; + + *byte = mask (*byte, bit) | code << bit; + byte++; + code >>= 8 - bit; + if (fd->nbits + bit > 16) { + *byte++ = code; + code >>= 8; + } + *byte = code; + fd->bitpos += fd->nbits; + if (fd->bitpos == 8 * fd->nbits) { + if (_sys_write (ifd, (char *) fd->buf, fd->nbits) < 0) + return -1; + fd->bitpos = 0; + } + return fd->bitpos; +} +@ +To be able to write arbitrary arrays of characters I need to suspend +compression not after a certain amount of characters {\em written}, but +after any amount of characters {\em seen}. +This means, that character sequences, which are collapsed into one +code, may extend across multiple calls to [[write]]. + +[[write]] checks first, whether this is the very first call to +[[write]] for this file and initializes the data structures. +Remember, that [[creat]] and [[pipe]] set [[nbits]] to zero to +indicate this situation. + +After using all possible codes no further entries to the hashtable are +made---[[write]] has to live with what is in the table. + +<<writing character arrays>>= +static int write_compressed (int ifd, unsigned char *buf, int n) +{ + cfd fd = filetab [ifd]; + int i, h; + struct centry *tmp; + unsigned char c; + + if (n == 0) + return 0; + + <<write initialization>> + + while (i-- > 0) { + c = *buf++; + + if ((tmp = lookup (fd, c)) == NULL) { + + <<output code for prefix>> + <<add code to table if necessary>> + + fd->nextcode++; + fd->u.c.lastcode = c; + + } else + fd->u.c.lastcode = tmp->code; + } + + fd->filepos += n; + + return n; +} +@ +A value of zero in [[nbits]] indicates, that the data structures have to be +initialized. + +<<write initialization>>= +if (fd->nbits == 0) { + if (_sys_write (ifd, (char *) compress_prefix, + sizeof (compress_prefix)) < 0) + return -1; + fd->nbits = MINBITS; + fd->nextcode = FIRSTCODE; + fd->bitpos = 0; + fd->u.c.codes = malloc (TABSIZE * sizeof (struct centry *)); + if (fd->u.c.codes == NULL) { + errno = ENOMEM; + return -1; + } + for (i = 0; i < TABSIZE; i++) + fd->u.c.codes [i] = NULL; + fd->filepos = 0; + fd->u.c.lastcode = *buf++; + i = n - 1; +} else + i = n; +@ +<<output code for prefix>>= +if (output (fd, ifd) < 0) + return -1; +@ +As long as not all the possible codes have been used, codes for new +sequences have to be introduces. + +<<add code to table if necessary>>= +if (fd->nextcode < (1L << MAXBITS)) { + if ((tmp = malloc (sizeof (struct centry))) == NULL) { + errno = ENOMEM; + return -1; + } + tmp->w = fd->u.c.lastcode; + tmp->c = c; + tmp->code = fd->nextcode; + + if (fd->nextcode == (1L << fd->nbits)) { + if (fd->bitpos > 0) { + if (_sys_write (ifd, (char *) fd->buf, fd->nbits) < 0) + return -1; + fd->bitpos = 0; + } + fd->nbits++; + } + + h = hash (fd->u.c.lastcode, c); + tmp->next = fd->u.c.codes [h]; + fd->u.c.codes [h] = tmp; +} +@ + +An important thing to note, is that I cannot simply close a file using +the operating system call. +It may be the case (in fact, it is always the case) that there is +still some accumulated code in [[lastcode]] that wants to be written +out. +I have to make sure, that I only really close the file, if the last +reference to this file is going to be abandoned. + +Unlike during a switch from [[nbits]] to [[nbits]]+1, where I always +flush the {\em entire} buffer [[buf]] (up to [[nbits]] bytes), I write +only those parts of [[buf]] which really contain written bits when +closing the file. +This provides the necessary information about the end of the file to +the uncompression algorithm. + +<<finish writing>>= +static int close_compressed_write (int ifd) +{ + cfd fd = filetab [ifd]; + int res, i; + struct centry *run, *next; + + if (--fd->shared == 0 && fd->nbits > 0) { + for (i = 0; i < TABSIZE; i++) + if ((run = fd->u.c.codes [i]) != NULL) + do { + next = run->next; + free (run); + run = next; + } while (run != NULL); + free (fd->u.c.codes); + if (output (fd, ifd) < 0) + return -1; + if (fd->bitpos > 0 && + _sys_write (ifd, + (char *) fd->buf, + (fd->bitpos + 7) / 8) + < 0) + return -1; + } + res = _sys_close (ifd); + if (fd->shared == 0) + free (fd); + filetab [ifd] = NULL; + return res; +} +@ + +\section{Uncompression} + +The algorithm to uncompress compressed files looks a little bit more +complicated. +First, I need a stack (described by the members [[stack]], +[[stacktop]] and [[stacksize]]) to reverse the sequence of characters, +which I obtain from a code. +The stack is realized as a rubber-band array, which is automatically +expanded when necessary. +Furthermore, [[buflen]] keeps track of the number of characters, which have +actually been read from the file system---fewer characters than +[[nbits]] indicate the end of the file. +[[oldcode]] holds the last code that has been read and [[finchar]] is the +final character produced from the last code. +This is necessary to deal correctly with the {\em +``AwAwA''}-phenomenon, where a code can be read, which is not in the +table yet. + +The ``hashtable'' [[htab]] is a rubber-band array which contains for +each code the associated prefix (i.e. the code for the string without +the last character) together with that last character. +Since entries are made in a sequential order, it is not +necessary to use a hash function. +The entry for a code {\em k} is at position {\em k-256}, because the +first codes 0-255 stand for themselves and don't need to be stored +into the table. + +Here are the missing members of [[struct cfd]]: + +<<uncompress-only members>>= +long tabsize; +struct { unsigned w; unsigned char c; } *htab; +unsigned stacksize; +unsigned stacktop; +unsigned char *stack; +int buflen; +unsigned short oldcode; +unsigned char finchar; +@ +Uncompression is split into the following tasks: + +<<read compressed>>= +<<stack management>> +<<reading bits>> +<<reading the first few bytes>> +<<reading compressed files>> +<<finish reading>> +@ +The stack management maintains a rubber band array. +The array can only expand, therefore ``popping'' items from the stack +can be ``in-lined''. +[[push]] is more complicated and gets its own function: + +<<constant definitions>>= +# define STACKGROWTH 64 +@ + +<<stack management>>= +static int push (cfd fd, unsigned char c) +{ + if (fd->u.d.stacktop >= fd->u.d.stacksize) { + fd->u.d.stacksize += STACKGROWTH; + if ((fd->u.d.stack = + realloc (fd->u.d.stack, fd->u.d.stacksize)) + == NULL) { + errno = ENOMEM; + return -1; + } + } + fd->u.d.stack [fd->u.d.stacktop++] = c; + return 0; +} +@ +Reading the bits into the buffer is a little bit more trickier than +writing. +Consider a pipe: If the pipe contains fewer characters than required, +then only those bytes are delivered. +The system call blocks for empty pipes only. +Therefore [[refill_buffer]] repeats the call to [[_sys_read]] until +either the buffer is completely filled or [[_sys_read]] signals +end-of-file or an error condition. + +<<reading bits>>= +static int refill_buffer (cfd fd, int ifd) +{ + int n, r; + + n = 0; + r = 0; + while (n < fd->nbits && + (r = _sys_read (ifd, + (char *) (fd->buf + n), + fd->nbits - n)) + > 0) + n += r; + if (r < 0) + return -1; + fd->u.d.buflen = 8 * n; + fd->bitpos = 0; + return 0; +} +@ +The [[input]]-function is very much like [[output]], except that it +has to return an end-of-file condition when the end of the file has +been reached. +I use -1 to signal the end of the file and -2 to signal an error. +[[buflen]] is used to describe, what is really in the buffer (the +number of bits). + +<<reading bits>>= +static int input (cfd fd, int ifd) +{ + unsigned char *byte; + int bit; + unsigned code; + + if (fd->bitpos + fd->nbits > fd->u.d.buflen) { + if (refill_buffer (fd, ifd)) + return -2; + if (fd->u.d.buflen == 0) + return -1; + } + byte = fd->buf + fd->bitpos / 8; + bit = fd->bitpos % 8; + code = invmask (*byte, bit) >> bit; + byte++; + bit = 8 - bit; + if (fd->nbits - bit >= 8) { + code |= *byte++ << bit; + bit += 8; + } + code |= mask (*byte, fd->nbits - bit) << bit; + fd->bitpos += fd->nbits; + + return code; +} +@ +There is still the problem, that [[read]] has to check first, whether +the contents of the file is compressed or not. +If [[read]] detects, that the file is not compressed, than it has to +arrange for {\em all} file descriptors associated with the file, that +it is read using the plain operating system call. +This is done using the function [[mark_uncompressed]]. + +<<reading the first few bytes>>= +static void mark_uncompressed (cfd fd) +{ + int i; + + for (i = 0; i < MAXFILES; i++) + if (filetab [i] == fd) + filetab [i] = NULL; + free (fd); +} +@ +The first attempt to read a file will be used to check, whether the +file is compressed. +To do this, I try to read the first three characters in the file and +compare them with [[compress_prefix]]. +If I don't get three characters or if those characters do not coincide +with [[compress_prefix]], then the file is considered to be not +compressed. +Otherwise I simply change the method table to be [[cr_m]] and call +[[read_compressed]]. + +In the case that the file is not compressed, the characters read are +part of the data and have to be placed into the buffer, which is the +argument to read. +After doing so, the file has to be marked being uncompressed. +If [[read]] has asked for more than three characters, then +[[_sys_read]] will try to get those. + +Difficulties arise, if [[read]] had asked for fewer characters than +received by the first [[_sys_read]]. +These characters have to be kept for further calls to [[read]]. +(To reset the file pointer to the beginning might be impossible, +because the file can be a terminal device or a pipe.) +I set [[nbits]] to 1 to indicate, that the file has already proven to +be in uncompressed state. +In this case [[read_prefix]] fetches the characters from the buffer +instead of reading them from the file system. +When all the characters are ``eaten up'' I mark the file as being +uncompressed. + +<<reading the first few bytes>>= +static int read_prefix (int ifd, unsigned char *buf, int n) +{ + cfd fd = filetab [ifd]; + int l; + + if (n == 0) + return 0; + + if (fd->nbits == 0) { + if ((l = _sys_read (ifd, + (char *) fd->buf, + sizeof compress_prefix)) + < 0) + return -1; + if (l == sizeof (compress_prefix) && + memcmp (fd->buf, + compress_prefix, + sizeof compress_prefix) + == 0) { + fd->methods = &cr_m; + return read_compressed (ifd, buf, n); + } + fd->nbits = 1; + fd->filepos = 0; + fd->bitpos = l; + } + + if (n < fd->bitpos - fd->filepos) { + memcpy (buf, fd->buf + fd->filepos, n); + fd->filepos += n; + return n; + } + + memcpy (buf, fd->buf + fd->filepos, fd->bitpos - fd->filepos); + if (n > fd->bitpos - fd->filepos) { + l = _sys_read (ifd, (char *) (buf + fd->bitpos - fd->filepos), + n - (fd->bitpos - fd->filepos)); + if (l < 0) + l = 0; + n = l + fd->bitpos - fd->filepos; + } + + mark_uncompressed (fd); + + return n; +} +@ +This is the code for reading compressed files. +Note, that the function initialized all the relevant data structures +if [[nbits]] equals zero. +Later it reads codes, constructs the corresponding character strings using the +stack and places those characters into the buffer. +Usually there will remain some characters, which have to be kept until +the next call to [[read]]. +The table of codes is again constructed as the algorithm goes. +The uncompress algorithm lags always one step behind, so it may +happen, that a code is not yet in the table. +In this case, the sequence of characters can be reconstructed using +[[finchar]] and [[oldcode]]. + +<<reading compressed files>>= +static int read_compressed (int ifd, unsigned char *buf, int n) +{ + cfd fd = filetab [ifd]; + int i = n; + unsigned incode, c; + int cin; + + <<read initialization>> + + for (;;) { + <<empty the stack>> + <<return or read next code>> + + c = incode = cin; + + <<special AwAwA case handling>> + <<analyse code and put bytes onto stack>> + <<enter code to table htab>> + + fd->u.d.oldcode = incode; + } +} +@ +The first time [[read]] gets called for a compressed file, the following code +will be executed: + +<<read initialization>>= +if (fd->nbits == 0) { + if (n == 0) + return 0; + + fd->nbits = MINBITS; + fd->nextcode = FIRSTCODE; + fd->bitpos = 8 * MINBITS; + fd->filepos = 0; + fd->u.d.tabsize = TABSIZE; + if ((fd->u.d.htab = + malloc (TABSIZE * sizeof (*fd->u.d.htab))) + == NULL) { + errno = ENOMEM; + return -1; + } + fd->u.d.stacksize = STACKGROWTH; + fd->u.d.stacktop = 0; + if ((fd->u.d.stack = malloc (STACKGROWTH)) == NULL) { + errno = ENOMEM; + return -1; + } + fd->u.d.buflen = 8 * MINBITS; + cin = input (fd, ifd); + if (cin < 0) + return cin == -1 ? 0 : -1; + *buf++ = cin; + --i; + fd->u.d.oldcode = cin; + fd->u.d.finchar = cin; +} +@ +First, [[read]] has to empty the stack: + +<<empty the stack>>= +while (fd->u.d.stacktop > 0 && i > 0) { + *buf++ = fd->u.d.stack [--fd->u.d.stacktop]; + --i; +} +@ +Then it can try to get another code, if necessary: + +<<return or read next code>>= +if (i == 0 || (cin = input (fd, ifd)) == -1) { + fd->filepos += n - i; + return n - i; +} + +if (cin < -1) + return -1; +@ +It may happen, that a code is not in the table yet. +[[oldcode]] and [[finchar]] contain enough information to deduce, what has to +be in the table: + +<<special AwAwA case handling>>= +if (c >= fd->nextcode) { + if (c > fd->nextcode) { + errno = EIO; + return -1; + } + if (push (fd, fd->u.d.finchar)) + return -1; + c = fd->u.d.oldcode; +} +@ +A code is analyzed from right to left. +Therefore, I need to use the stack to reverse the order of the characters: + +<<analyse code and put bytes onto stack>>= +while (c >= FIRSTCODE) { + if (push (fd, fd->u.d.htab [c - FIRSTCODE].c)) + return -1; + c = fd->u.d.htab [c - FIRSTCODE].w; +} +fd->u.d.finchar = c; +if (push (fd, c)) + return -1; +@ +Unless all possible codes are already used, I have to insert the a new code +into the table. + +<<enter code to table htab>>= +if (fd->nextcode < (1L << MAXBITS)) { + if (fd->nextcode - FIRSTCODE >= fd->u.d.tabsize) { + fd->u.d.tabsize += TABSIZE; + if ((fd->u.d.htab = + realloc (fd->u.d.htab, + fd->u.d.tabsize * sizeof (*fd->u.d.htab))) + == NULL) { + errno = ENOMEM; + return -1; + } + } + fd->u.d.htab [fd->nextcode - FIRSTCODE].c = c; + fd->u.d.htab [fd->nextcode - FIRSTCODE].w = fd->u.d.oldcode; + + if (fd->nextcode == (1L << fd->nbits) - 1 && + fd->nbits < MAXBITS) { + fd->nbits++; + if (refill_buffer (fd, ifd)) + return -1; + } + + fd->nextcode++; +} +@ + +Closing a file, which has been read from is not as difficult as +closing a file which has been written to, because no buffers have to +be flushed. +Nevertheless, it has to take care of freeing the data structures. +I have two different routines for closing, one for [[cr_m]], the other +for [[ir_m]]. + +<<finish reading>>= +static int close_compressed_read (int ifd) +{ + cfd fd = filetab [ifd]; + + if (--fd->shared == 0) { + if (fd->nbits > 0) { + free (fd->u.d.htab); + free (fd->u.d.stack); + } + free (fd); + } + filetab [ifd] = NULL; + return _sys_close (ifd); +} +@ + +<<finish reading>>= +static int close_prefix_read (int ifd) +{ + cfd fd = filetab [ifd]; + + if (--fd->shared == 0) + free (fd); + filetab [ifd] = NULL; + return _sys_close (ifd); +} +@ + +\section{Miscellaneous IO-substitutes} + +Reading from a file, that has been opened for writing or vice versa is +not allowed. + +<<other io-substitutes>>= +static int refuse_io (int fd, unsigned char *buf, int n) +{ + errno = EINVAL; + return -1; +} +@ +As far as [[lseek]] is concerned, a compressed file (regardless +whether read or written) behaves like a pipe, i.e. [[lseek]] returns -1. + +<<other io-substitutes>>= +static long refuse_seek (int fd, long offset, int whence) +{ + errno = ESPIPE; + return -1; +} +@ +An attempt to [[lseek]] a file, that is opened for reading and has not +(yet) proven to contain compressed data, will force to treat the file +as a plain file. + +<<other io-substitutes>>= +static long prefix_seek (int ifd, long offset, int whence) +{ + cfd fd = filetab [ifd]; + + mark_uncompressed (fd); + return _sys_lseek (ifd, offset, whence); +} +@ + +\section{Examples} + +The remainder of the text gives a collection of sample code, which +provides some evidence, that the implementation is correct. + +I start with a simple copy program. +The program takes one or two command line arguments the first of +which is the input file, while the second names the output file and +defaults to the standard output. + +The most interesting property of this program is, that is can be used +both as a substitute for [[compress]] and as a replacement for +[[uncompress]]. +Further, it will also work on plain files. +Consider: +\begin{itemize} +\item +{\tt t plain-input compressed-output} +\item +{\tt t compressed-input compressed-output} +\item +{\tt t plain-input >plain-output} +\item +{\tt t compressed-input >plain-output} +\end{itemize} + +<<t.c>>= +# include <stdio.h> +# include <errno.h> +# include <string.h> + +int main (int argc, char **argv) +{ + FILE *in, *out; + int c; + + if (argc != 3 && argc != 2) { + fprintf (stderr, "Usage: %s infile [ outfile ]\n", argv [0]); + exit (1); + } + + if ((in = fopen (argv [1], "r")) == NULL) { + fprintf (stderr, "Cannot open file %s for reading: %s\n", + argv [1], strerror (errno)); + exit (1); + } + if (argc == 2) + out = stdout; + else if ((out = fopen (argv [2], "w")) == NULL) { + fprintf (stderr, "Cannot open file %s for writing: %s\n", + argv [2], strerror (errno)); + exit (1); + } + + while ((c = getc (in)) != EOF) + putc (c, out); + + fclose (in); + fclose (out); + + return 0; +} +@ +The next example does the same thing while not using the +standard library. +Instead, it calls [[open]], [[read]] etc. directly. + +<<v.c>>= +# include <stdio.h> +# include <errno.h> +# include <string.h> + +int main (int argc, char **argv) +{ + int ifd, ofd; + int n; + char buf [4096]; + + if (argc != 3 && argc != 2) { + fprintf (stderr, "Usage: %s infile [ outfile ]\n", argv [0]); + exit (1); + } + + if ((ifd = open (argv [1], 0)) < 0) { + fprintf (stderr, "Cannot open file %s for reading: %s\n", + argv [1], strerror (errno)); + exit (1); + } + if (argc == 2) + ofd = 1; + else if ((ofd = creat (argv [2], 0666)) < 0) { + fprintf (stderr, "Cannot open file %s for writing: %s\n", + argv [2], strerror (errno)); + exit (1); + } + + while ((n = read (ifd, buf, 512)) > 0) + write (ofd, buf, n); + + close (ifd); + close (ofd); + + return 0; +} +@ +The next example uses [[dup]] and performs interleaved writes to both +file descriptors. +Of course, this isn't necessary, but it shows, that [[dup]] works as +expected. + +<<u.c>>= +# include <stdio.h> +# include <errno.h> +# include <string.h> + +int main (int argc, char **argv) +{ + int ifd, oofd, dofd; + int n; + char buf [512]; + + if (argc != 3 && argc != 2) { + fprintf (stderr, "Usage: %s infile [ outfile ]\n", argv [0]); + exit (1); + } + + if ((ifd = open (argv [1], 0)) < 0) { + fprintf (stderr, "Cannot open file %s for reading: %s\n", + argv [1], strerror (errno)); + exit (1); + } + if (argc == 2) + oofd = 1; + else if ((oofd = creat (argv [2], 0666)) < 0) { + fprintf (stderr, "Cannot open file %s for writing: %s\n", + argv [2], strerror (errno)); + exit (1); + } + + dofd = dup (oofd); + + while ((n = read (ifd, buf, 512)) > 0) + write (oofd, buf, n / 2), + write (dofd, buf + n / 2, n - n / 2); + + close (ifd); + close (oofd); + close (dofd); + + return 0; +} +@ +The following program is the most complex example. +It shows the use of pipes in the framework of [[fork]] and [[exec]]. +By two times executing [[fork]] I create a sequential arrangement of +three processes, which are connected by two pipes. +Pipe [[p1]] connects the {\em child} with the {\em parent}, while [[p2]] +provides a channel from the {\em grandchild} to the {\em child}. + +The {\em child} starts another program by calling either [[execl]] or +[[execvp]], depending on what command line arguments are given. +The idea is to pipe compressed data to a {\em filter} and read it +back. +Simple ``filters'' like [[cat]] or [[tee]] don't change the data. +Therefore, the {\em parent} should see, what the {\em grandchild} +wrote in this case. +It is worth trying to put {\tt uncompress~-C} into the place of the filter. +(The behavior should not change, because [[read]] automatically +detects, that the data in [[p1]] are not in compressed format.) + +Note, that all calls to [[fork]] and [[exec]] have to be executed {\em +before any actual input or output takes place}. + +<<w.c>>= +# include <stdio.h> +# include <assert.h> +# include <errno.h> +# include <string.h> + +# define check(x) \ + ((x)<0?(fprintf(stderr,"%s(%d): (%s) < 0 (%s)\n", \ + __FILE__, __LINE__, #x, strerror(errno)), \ + exit(1)):0) + +int main (int argc, char **argv) +{ + char buf [512]; + int n; + int p1 [2], p2 [2]; + int f; + + check (pipe (p1)); + + check (f = fork ()); + if (f > 0) { + check (close (p1 [1])); + while (check (n = read (p1 [0], buf, 512)), n > 0) + check (write (1, buf, n)); + putc ('\n', stderr); + check (close (p1 [0])); + check (wait (NULL)); + exit (0); + } else { + check (close (1)); + check (dup (p1 [1])); + check (close (p1 [0])); + check (close (p1 [1])); + + check (pipe (p2)); + + check (f = fork ()); + + if (f > 0) { + check (close (p2 [1])); + check (close (0)); + check (dup (p2 [0])); + check (close (p2 [0])); + + if (argc == 1) + check (execl ("/bin/cat", "cat", NULL)); + else + check (execvp (argv [1], argv + 1)); + } else { + check (close (p2 [0])); + while (check (n = read (0, buf, 512)), n > 0) + check (write (p2 [1], buf, n)); + check (close (p2 [1])); + exit (0); + } + } +} +@ +A small program shows the use of pipes through the [[popen]]-interface: + +<<x.c>>= +# include <stdio.h> + +# include <assert.h> + +int main (int argc, char **argv) +{ + FILE *fp; + int c; + + assert (argc == 2); + fp = popen (argv [1], "r"); + assert (fp != NULL); + while ((c = getc (fp)) != EOF) + putchar (c); + pclose (fp); + return 0; +} +@ +A final test case makes sure, that [[read_prefix]] works correctly, +even in the case, that the number of characters asked for is less than +the length of the [[compress_prefix]]. +The program always reads only one character at a time. +Try to use it with a plain file and remember, how [[read]] is implemented. + +<<y.c>>= +# include <assert.h> + +int main (int argc, char **argv) +{ + char c; + int fd; + + assert (argc == 2); + fd = open (argv [1], 0); + assert (fd >= 0); + while (read (fd, &c, 1) == 1) + write (1, &c, 1); + close (fd); + return 0; +} +@ + +\section{Conclusions} + +The examples in the previous section show, that the new file system +interface really hides the details of data compression, thereby +providing this service in a fashion transparent to the programs that +use it. +However, everything works only as long as a single program has +complete control over an open file. +In a multi-tasking environment like UNIX, this is generally not true. +This puts some severe constraints onto the usage of the new interface. +The implementation presented in this paper assumes, that files opened +for writing by [[open]] are not compressed. +What, if the data in that file actually {\em are} compressed? + +Recently, there are some efforts made to integrate data compression with +file system implementations themselves. +Only here one has the opportunity to know about {\em every} access to a file, +and things can be synchronized properly. + +Nevertheless, the given program text can be useful in many +circumstances. +I, for instance, tried to integrate it with [[VSCM]], which is my +implementation of {\em Scheme}. +Memory dumps written by [[VSCM]] are less than half as big as before, +and everything still works fine. +Probably, the fact that files containing symbolic expressions are +written in compressed mode as well is a little bit surprising and annoying. +(This leads to the demand for a switch to turn off compression, but +this would expose details of the implementation, which is what I +wanted to avoid in the first place.) + +\section{Indexes} + +\subsection{Code Chunks} +\nowebchunks + +\subsection{Identifiers} +\nowebindex diff --git a/web/noweb/examples/dag.nw b/web/noweb/examples/dag.nw new file mode 100644 index 0000000000..06ba17dbb8 --- /dev/null +++ b/web/noweb/examples/dag.nw @@ -0,0 +1,163 @@ +\section{Converting trees to dags} +The problem with the trees generated in the previous section is that +there's a different edge, and therefore a different child, for each +possible interval of the field tested, even if those children both +execute exactly the same ``original'' arm of the case statement. +The code in this section converts the trees to dags, and as part of +the process it combines edges pointing to the same node. +This can reduce the size of the tree by huge factors. + +To make the transformation work, I have to represent a {\em set of +intervals} on each edge, not just a single interval. Because no two intervals +overlap, I can use a wonderful dirty trick, detailed below. +I also {\em may} convert a node's name string to a [[namearray]] mapping +field values to strings. The goal is for children of the same +parent to share a single name array; that way the edges can be merged and +the name operator can be implemented with an array reference. +If I don't convert a node's name, the only penalty is that the tree +might be bigger. +(Code generation will be different for the two cases.) +@ +Now, the dirty representation trick: +I can represent a set of numbers $S$ (a union of intervals) as two +sets, $lo$ and $hi$, such that +\begin{itemize} +% l2h substitution cap <b>intersect</b># +% l2h substitution cup <b>union</b># +% l2h substitution emptyset <b>empty#set</b> +\item[] $lo \cap hi = \emptyset$ +\item[] if ${\tt sort}(lo \cup hi) = a, b, c, d, \ldots$, then + $S = [a,b-1] \cup [c,d-1] \cup \ldots$. +\end{itemize} +The procedure [[addinterval]] adds a new interval to such a set $S$, +relying on the fact that no two intervals overlap. +<<*>>= +procedure addinterval(loset, hiset, lonum, hinum) + if member(loset, hinum) then delete(loset, hinum) else insert(hiset, hinum) + if member(hiset, lonum) then delete(hiset, lonum) else insert(loset, lonum) + return +end +@ +To convert trees to dags I need to be able to compare two nodes +for structural identity, and the easiest way is to compute a canonical +representation as a string:\par\noindent [[ + node : [fname:patimage(list of edges)] + | <NOMATCH> + | (image(node.name):image(node.cs.arms[1].original)) + edge : patimage(list of sort(loset ++ hiset)):node +]] +<<*>>= +procedure nodetostring(n, depth) + static cache + initial cache := table() + /depth := 0 + if /cache[n] then + if *n.children > 0 then { + result := "[" || n.field.name || ":" + every result ||:= edgetostring(!n.children, depth+2) + cache[n] := result || "]" + } else if *n.cs.arms = 0 then + cache[n] := "<NOMATCH>" + else + cache[n] := "(" || image(n.name) || ":" || image(n.cs.arms[1].original) ||")" + return \cache[n] +end + +procedure edgetostring(e,depth) + return left("\n", depth) || + "{" || patimage(sort(e.lo ++ e.hi)) || ":" || nodetostring(e.node,depth) || "}" +end +@ +Conversion to dag is the usual bottom-up hashing; here I compute the +string and then use the string to index into a table. +The real work of merging edges is done by [[combinechildren]]. +If edge merging results in a single each, the node is replaced by +its child, provided the edge really covers all possible values +of the field. +<<*>>= +procedure tree2dag(n, nodetable, depth) + /nodetable := table() + /depth := 0 + if *n.children > 0 then + combinechildren(n, nodetable, depth+2) # converts edges to set form + if *n.children = 1 then { + e := n.children[1] + if covers(n.children[1], n.field.hi - n.field.lo) then + n := n.children[1].node # all roads to child: hoist it + else + warning("node with one child doesn't match all cases") + } + s := nodetostring(n, depth) + /nodetable[s] := n + return nodetable[s] +end +@ +Here's where I check coverage. +Only success or failure of [[covers]] is meaningful, not +the value returned. +<<*>>= +procedure covers(e, width) + l := sort(e.lo ++ e.hi) + return *l = 2 & l[1] = 0 & l[2] = 2^width +end +@ +The complicated stuff here is identifying a name array. +At each node, either all edges go in an exiting name array +or a new name array is used. +If not, I create a new one. +<<*>>= +record namearray(field, tbl, hi, codename) + # field used as index, table[integer] of name, bound on table, name of this array +global natable +<<*>>= +procedure arraycandidates(n) + initial MAXRANGE := 32 + suspend e := !n.children & type(e.node.name) == "string" & + e.hi - e.lo <= MAXRANGE & e +end + +procedure combinechildren(n, nodetable, depth) + initial natable := table() + + if arraycandidates(n).node.name ~== arraycandidates(n).node.name then { + <<change names of children from strings to namearrays when possible>> + } + + lotable := table() + hitable := table() + every e := !n.children & child := tree2dag(e.node, nodetable, depth) do { + /lotable[child] := set() + /hitable[child] := set() + addinterval(lotable[child], hitable[child], e.lo, e.hi) + } + n.children := [] + every child := key(lotable) do + put(n.children, edge(child, lotable[child], hitable[child])) + return +end +<<change names of children from strings to namearrays when possible>>= +mightuse := set() # name arrays we might use must have right field +every na := !\natable[n.field] do + insert(mightuse, na) +every e := arraycandidates(n) & na := !mightuse do + if \na.tbl[e.lo to e.hi - 1] ~== e.node.name then # slot used with wrong name + delete(mightuse, na) +if *mightuse > 0 then + willuse := ?mightuse +else { + /natable[n.field] := set() + insert(natable[n.field], willuse := namearray(n.field, table(), 0)) +} +every e := arraycandidates(n) & + e.lo - willuse.hi <= MAXRANGE do { + every willuse.tbl[e.lo to e.hi - 1] := e.node.name; + e.node.name := willuse + willuse.hi <:= e.hi + } +<<*>>= +procedure namesused(n, result) + /result := set() + if type(n.name) == "namearray" then insert(result, n.name) + every namesused((!n.children).node, result) + return result +end diff --git a/web/noweb/examples/graphs.nw b/web/noweb/examples/graphs.nw new file mode 100644 index 0000000000..a5f7b8cdc3 --- /dev/null +++ b/web/noweb/examples/graphs.nw @@ -0,0 +1,244 @@ +This is a noweb file for doing all the graphs in the paper. It sets +up generic chunks for setting up the axis sizes, etc, and gets all of +the graph's data from data.nw. + +@ This first chunk is to be used by all graphs. It sets up the axis +sizes, as well as the font sizes for labels. +<<Standard Defaults>>= + yaxis size 1.9 hash_labels fontsize 7 label fontsize 8 draw + xaxis size 2.14 hash_labels fontsize 7 label fontsize 8 draw + title fontsize 10 +@ +The following chunks define the curves for the four different +types of checkpointing algorithms. +<<Sequential Curve>>= + newcurve marktype circle fill 1 linetype solid +<<Concurrent Curve>>= + newcurve marktype diamond fill 0 linetype solid +<<Low Latency Curve>>= + newcurve marktype box fill 1 linetype solid +<<Low Latency Large Curve>>= + newcurve marktype box fill 0 linetype solid +@ +Finally, this chunk is for the split legends of graphs 1-4. +<<Legend>>= +newgraph + inherit_axes + xaxis min 0 max 10 nodraw + yaxis min 0 max 10 nodraw + <<Sequential Curve>> label fontsize 7 : Sequential + <<Concurrent Curve>> label fontsize 7 : Concurrent + legend x 0 y 11.5 +newgraph + inherit_axes + <<Low Latency Curve>> label fontsize 7 : Low Latency, Conc. + <<Low Latency Large Curve>> label fontsize 7 : Low Lat., Conc., Large + legend x 4.7 y 11.5 +@ +---- THE GRAPHS ---- +@ +GRAPHS 1 \& 2 +<<Graphs 1n2>>= +Y 3 +newgraph x_translate -1.37 + <<Graph 1>> +newgraph inherit_axes x_translate 1.7 + <<Graph 2>> +@ +Graph \#1: Checkpoint time. This is the wall-clock time to take one +checkpoint of a merge-sort program. +<<Graph 1>>= + <<Standard Defaults>> + title : Graph 1: Checkpoint Time. + xaxis min 0 max 10 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max 70 hash 10 mhash 1 label : Time (sec) + <<Sequential Curve>> + pts shell : notangle -R"Ckp Time" data.nw | head -6 | awk '{print $1" "$2}' + <<Concurrent Curve>> + pts shell : notangle -R"Ckp Time" data.nw | head -6 | awk '{print $1" "$3}' + <<Low Latency Curve>> + pts shell : notangle -R"Ckp Time" data.nw | head -6 | awk '{print $1" "$4}' + <<Low Latency Large Curve>> + pts shell : notangle -R"Ckp Time" data.nw | head -6 | awk '{print $1" "$5}' + <<Legend>> + +@ +Graph \#2: Checkpoint overhead. This is the extra time that it took +the program to complete after taking one checkpoint. +<<Graph 2>>= + <<Standard Defaults>> + title : Graph 2: Checkpoint Overhead. + xaxis min 0 max 10 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max 35 hash 10 mhash 1 label : Time (sec) + <<Sequential Curve>> + pts shell : notangle -R"Overhead" data.nw | awk '{print $1" "$2}' + <<Concurrent Curve>> + pts shell : notangle -R"Overhead" data.nw | awk '{print $1" "$3}' + <<Low Latency Curve>> + pts shell : notangle -R"Overhead" data.nw | awk '{print $1" "$4}' + <<Low Latency Large Curve>> + pts shell : notangle -R"Overhead" data.nw | awk '{print $1" "$5}' + <<Legend>> +@ +GRAPHS 3 \& 4 +<<Graphs 3n4>>= + Y 3 + newgraph x_translate -1.37 + <<Graph 3>> + newgraph inherit_axes x_translate 1.7 + <<Graph 4>> +@ +Graph \#3: Checkpoint Overhead Percentage. This is the percentage +of the checkpointing time which is overhead incurred on the target +program +<<Graph 3>>= + <<Standard Defaults>> + title : Graph 3: Checkpoint Overhead Percentage + xaxis min 0 max 10 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max 100 hash 10 mhash 1 label : % Overhead + <<Sequential Curve>> + pts shell : notangle -R"Ckp %age" data.nw | awk '{print $1" "$2}' + <<Concurrent Curve>> + pts shell : notangle -R"Ckp %age" data.nw | awk '{print $1" "$3}' + <<Low Latency Curve>> + pts shell : notangle -R"Ckp %age" data.nw | awk '{print $1" "$4}' + <<Low Latency Large Curve>> + pts shell : notangle -R"Ckp %age" data.nw | awk '{print $1" "$5}' + <<Legend>> +@ +Graph \#4: This displays what happens when the heaps get big +<<Graph 4>>= + <<Standard Defaults>> + title : Graph 4: Checkpoint Time for Large Heaps. + xaxis min 0 max 13 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max 700 hash 100 mhash 1 label : Time (sec) + <<Sequential Curve>> + pts shell : notangle -R"Ckp Time" data.nw | awk '{print $1" "$2}' + <<Concurrent Curve>> + pts shell : notangle -R"Ckp Time" data.nw | awk '{print $1" "$3}' + <<Low Latency Curve>> + pts shell : notangle -R"Ckp Time" data.nw | awk '{print $1" "$4}' + <<Low Latency Large Curve>> + pts shell : notangle -R"Ckp Time" data.nw | awk '{print $1" "$5}' + <<Legend>> +@ +Graph \#5: Latency Data +<<Graph 5>>= + Y 3 + newgraph + <<Standard Defaults>> + title : Graph 5: Latency Data. + xaxis min 0 max 13 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max .351 hash .1 mhash 1 label : Time(sec) + newcurve marktype circle fill 1 linetype solid + pts shell : notangle -R"Latency" data.nw | awk '{print $1" "$2}' + label : Initial Stop Time + newcurve marktype circle fill 0 linetype solid + pts shell : notangle -R"Latency" data.nw | awk '{print $1" "$3}' + label : Maximum Trap Time + legend right +@ +GRAPHS 6 \& 7 +<<Graphs 6n7>>= + Y 3 + newgraph x_translate -1.37 + <<Graph 6>> + newgraph inherit_axes x_translate 1.7 + <<Graph 7>> +@ +Graph \#6: Number of traps vs Segment Size +<<Graph 6>>= + <<Standard Defaults>> + title : Graph 6: Number of Traps vs. Segment Size. + xaxis min 0 max 128 hash 32 mhash 1 label : Segment Size (KBytes) + yaxis min 0 max 1000 hash 200 mhash 1 label : Number of Traps + newcurve marktype circle fill 1 linetype solid + pts shell : notangle -R"Num Traps" data.nw +@ +Graph \#7: Number of traps vs Segment Size +<<Graph 7>>= + <<Standard Defaults>> + title : Graph 7: Maximum Trap Time vs. Segment Size. + xaxis min 0 max 128 hash 32 mhash 1 label : Segment Size (KBytes) + yaxis min 0 max 1.4 hash .2 mhash 1 label : Maximum Trap Time (Sec) + newcurve marktype circle fill 1 linetype solid + pts shell : notangle -R"Trap Time" data.nw +@ +Graph \#8: Frequency of Traps +<<Graph 8>>= + Y 3 + newgraph + <<Standard Defaults>> + title : Graph \#8: Frequency of Traps. + xaxis min 0 max 2.6001 hash .5 mhash 4 + label : Starting Time of 0.1 second interval (sec) + yaxis min 0 max 10 hash 1 mhash 0 + label : Traps per 0.1 second Interval + newcurve marktype circle fill 0 pts + shell : notangle -R"Freq" data.nw + newstring hjr vjc fontsize 7 x 2.6 y 10 : Average Trap Time = 0.015 seconds +@ +GRAPHS 9 \& 10 +<<Graphs 9n10>>= + Y 3 + newgraph x_translate -1.37 + <<Graph 9>> + newgraph inherit_axes x_translate 1.7 + <<Graph 10>> +@ +Graph 9 \& 10 have bizarre legends as well: +<<Merge>>= + newcurve marktype circle fill 0 linetype solid +<<Trs>>= + newcurve marktype triangle fill 0 linetype none +<<Pattern>>= + newcurve marktype diamond fill 1 linetype none +<<Bubble>>= + newcurve marktype cross fill 1 linetype none +<<Matrix>>= + newcurve marktype box fill 1 linetype none +<<9n10 Legend>>= + newgraph inherit_axes + xaxis min 0 max 10 nodraw + yaxis min 0 max 10 nodraw + <<Merge>> + label fontsize 7 : Merge Sort + <<Trs>> + label fontsize 7 : Travelling Salesman + <<Pattern>> + label fontsize 7 : Pattern Match + legend x 0 y 12 + newgraph inherit_axes + <<Bubble>> + label fontsize 7 : Bubble Sort + <<Matrix>> + label fontsize 7 : Matrix Multiplication + legend x 5.6 y 12 +@ +Graph \#9: +<<Graph 9>>= + <<Standard Defaults>> + title : Graph 9: Checkpoint Time of Other Benchmarks. + xaxis min 0 max 10 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max 40 hash 10 mhash 1 label : Time (sec) + <<Merge>> + pts shell : notangle -R"Ckp Time" data.nw | head -6 | awk '{print $1" "$4}' + <<Trs>> pts .064 2.359 + <<Pattern>> pts 1.233 6.374 + <<Bubble>> pts .513 3.91 + <<Matrix>> pts 3.000 14.71 + <<9n10 Legend>> +@ +Graph \#10: +<<Graph 10>>= + <<Standard Defaults>> + title : Graph 10: Checkpoint Overhead of Other Benchmarks. + xaxis min 0 max 10 hash 2 mhash 1 label : Heap Size (MBytes) + yaxis min 0 max 5 hash 1 mhash 1 label : Time (sec) + <<Merge>> + pts shell : notangle -R"Overhead" data.nw | awk '{print $1" "$4}' + <<Trs>> pts .064 0.17 + <<Pattern>> pts 1.233 0.07 + <<Bubble>> pts .513 0.07 + <<Matrix>> pts 3.000 0.75 + <<9n10 Legend>> diff --git a/web/noweb/examples/mipscoder.nw b/web/noweb/examples/mipscoder.nw new file mode 100644 index 0000000000..a97f30774e --- /dev/null +++ b/web/noweb/examples/mipscoder.nw @@ -0,0 +1,1118 @@ +\section{A small assembler for the MIPS} +This is part of the code generator for Standard ML of New Jersey. +We generate code in several stages. +This is nearly the lowest stage; it is like an assembler. +The user can call any function in the [[MIPSCODER]] signature. +Each one corresponds to an assembler pseudo-instruction. +Most correspond to single MIPS instructions. +The assembler remembers all the instructions that have been +requested, and when [[codegen]] is called it generates MIPS +code for them. + +Some other structure will be able to use the MIPS structure to implement +a [[CMACHINE]], which is the abstract machine that ML thinks it is running +on. +(What really happens is a functor maps some structure +implementing [[MIPSCODER]] to a different structure implementing +[[CMACHINE]].) + +{\em Any function using a structure of this signature must avoid +touching registers 1~and~31. +Those registers are reserved for use by the assembler.} + +@ Here is the signature of the assembler, [[MIPSCODER]]. +It can be extracted from this file by +$$\hbox{\tt notangle mipsinstr.nw -Rsignature}.$$ +<<signature>>= +signature MIPSCODER = sig + +(* Assembler for the MIPS chip *) + +eqtype Label +datatype Register = Reg of int + (* Registers 1 and 31 are reserved for use by this assembler *) +datatype EA = Direct of Register | Immed of int | Immedlab of Label + (* effective address *) + +structure M : sig + + (* Emit various constants into the code *) + + val emitstring : string -> unit (* put a literal string into the + code (null-terminated?) and + extend with nulls to 4-byte + boundary. Just chars, no + descriptor or length *) + exception BadReal of string + val low_order_offset : int (* does the low-order word of a + floating point literal come + first (0) or second (1) *) + val realconst : string -> unit (* emit a floating pt literal *) + val emitlong : int -> unit (* emit a 4-byte integer literal *) + + + (* Label bindings and emissions *) + + val newlabel : unit -> Label (* new, unbound label *) + val define : Label -> unit (* cause the label to be bound to + the code about to be generated *) + val emitlab : int * Label -> unit (* L3: emitlab(k,L2) is equivalent to + L3: emitlong(k+L2-L3) *) + + (* Control flow instructions *) + + val slt : Register * EA * Register -> unit + (* (operand1, operand2, result) *) + (* set less than family *) + val beq : bool * Register * Register * Label -> unit + (* (beq or bne, operand1, operand2, branch address) *) + (* branch equal/not equal family *) + + val jump : Register -> unit (* jump register instruction *) + + val slt_double : Register * Register -> unit + (* floating pt set less than *) + val seq_double : Register * Register -> unit + (* floating pt set equal *) + val bcop1 : bool * Label -> unit (* floating pt conditional branch *) + + + (* Arithmetic instructions *) + (* arguments are (operand1, operand2, result) *) + + val add : Register * EA * Register -> unit + val and' : Register * EA * Register -> unit + val or : Register * EA * Register -> unit + val xor : Register * EA * Register -> unit + val sub : Register * Register * Register -> unit + val div : Register * Register * Register -> unit + (* first arg is some register + guaranteed to overflow when + added to itself. Used to + detect divide by zero. *) + val mult : Register * Register * Register -> unit + val mfhi : Register -> unit (* high word of 64-bit multiply *) + + (* Floating point arithmetic *) + + val neg_double : Register * Register -> unit + val mul_double : Register * Register * Register -> unit + val div_double : Register * Register * Register -> unit + val add_double : Register * Register * Register -> unit + val sub_double : Register * Register * Register -> unit + + (* Move pseudo-instruction : move(src,dest) *) + + val move : EA * Register -> unit + + (* Load and store instructions *) + (* arguments are (destination, source address, offset) *) + + val lbu : Register * EA * int -> unit (* bytes *) + val sb : Register * EA * int -> unit + val lw : Register * EA * int -> unit (* words *) + val sw : Register * EA * int -> unit + val lwc1: Register * EA * int -> unit (* floating point coprocessor *) + val swc1: Register * EA * int -> unit + val lui : Register * int -> unit + + (* Shift instructions *) + (* arguments are (shamt, operand, result) *) + (* shamt as Immedlab _ is senseless *) + + val sll : EA * Register * Register -> unit + val sra : EA * Register * Register -> unit + + + (* Miscellany *) + + val align : unit -> unit (* cause next data to be emitted on + a 4-byte boundary *) + val mark : unit -> unit (* emit a back pointer, + also called mark *) + + val comment : string -> unit + + end (* signature of structure M *) + + val codegen : unit->unit + + val codestats : outstream -> unit (* write statistics on stream *) + +end (* signature MIPSCODER *) +@ The basic strategy of the implementation is to hold on, via the [[kept]] +pointer, to the list of instructions generated so far. +We use [[instr]] for the type of an instruction, so +[[kept]] has type [[instr list ref]]. + +The instructions will be executed in the following order: the +instruction at the head of the [[!kept]] is executed last. +This enables us to accept calls in the order of execution but +add the new instruction(s) to the list in constant time. + + +@ +We structure the instruction stream a little bit by factoring +out the different load and store instructions that can +occur: we have load byte, load word, and load to coprocessor (floating point). +<<types auxiliary to [[instr]]>>= +datatype size = Byte | Word | Floating +@ +Here are the instructions that exist. +We list them in more or less the order of the MIPSCODER signature. +<<definition of [[instr]]>>= +<<types auxiliary to [[instr]]>> + +datatype instr = + STRINGCONST of string (* constants *) + | EMITLONG of int + + | DEFINE of Label (* labels *) + | EMITLAB of int * Label + + | SLT of Register * EA * Register (* control flow *) + | BEQ of bool * Register * Register * Label + | JUMP of Register + | SLT_D of Register * Register + | SEQ_D of Register * Register + | BCOP1 of bool * Label + + | NOP (* no-op for delay slot *) + + | ADD of Register * EA * Register (* arithmetic *) + | AND of Register * EA * Register + | OR of Register * EA * Register + | XOR of Register * EA * Register + | SUB of Register * Register * Register + | MULT of Register * Register + | DIV of Register * Register + | MFLO of Register (* mflo instruction used with + 64-bit multiply and divide *) + | MFHI of Register + + | NEG_D of Register * Register + | MUL_D of Register * Register * Register + | DIV_D of Register * Register * Register + | ADD_D of Register * Register * Register + | SUB_D of Register * Register * Register + + | MOVE of EA * Register (* put something into a register *) + | LDI_32 of int * Register (* load in a big immediate constant (>16 bits) *) + | LUI of Register * int (* Mips lui instruction *) + + | LOAD of size * Register * EA * int (* load and store *) + | STORE of size * Register * EA * int + + | SLL of EA * Register * Register (* shift *) + | SRA of EA * Register * Register + + | COMMENT of string (* generates nothing *) + | MARK (* a backpointer *) + + | BREAK of int (* break instruction *) +@ +Here is the code that handles the generated stream, [[kept]]. +It begins life as [[nil]] and returns to [[nil]] every time code is +generated. +The function [[keep]] is a convenient way of adding a single [[instr]] to +the list; it's very terse. +Sometimes we have to add multiple [[instr]]s; then we use [[keeplist]]. +We also define a function [[delay]] that is just like a [[keep]] but +it adds a NOP in the delay slot. +<<instruction stream and its functions>>= + val kept = ref nil : instr list ref + fun keep f a = kept := f a :: !kept + fun delay f a = kept := NOP :: f a :: !kept + fun keeplist l = kept := l @ !kept +<<reinitialize [[kept]]>>= + kept := nil +@ +\subsection{Exporting functions for {\tt MIPSCODER}} +We now know enough to implement most of the functions called for in +[[MIPSCODER]]. +We still haven't decided on an implementation of labels, +and there is one subtlety in multiplication and division, +but the rest is set. +<<[[MIPSCODER]] functions>>= + val emitstring = keep STRINGCONST (* literals *) + exception BadReal = IEEEReal.BadReal + val low_order_offset = Emitter.low_order_offset + val realconst = keep (STRINGCONST o order_real o IEEEReal.realconst) + val emitlong = keep EMITLONG + + <<label functions>> (* labels *) + + val slt = keep SLT (* control flow *) + val beq = delay BEQ + val jump = delay JUMP + val slt_double = delay SLT_D + val seq_double = delay SEQ_D + val bcop1 = delay BCOP1 + + val add = keep ADD (* arithmetic *) + val and' = keep AND + val or = keep OR + val xor = keep XOR + val op sub = keep SUB + <<multiplication and division functions>> + + val neg_double = keep NEG_D + val mul_double = keep MUL_D + val div_double = keep DIV_D + val add_double = keep ADD_D + val sub_double = keep SUB_D + + val move = keep MOVE + + fun lbu (a,b,c) = delay LOAD (Byte,a,b,c) (* load and store *) + fun lw (a,b,c) = delay LOAD (Word,a,b,c) + fun lwc1 (a,b,c) = delay LOAD (Floating,a,b,c) + fun sb (a,b,c) = keep STORE (Byte,a,b,c) + fun sw (a,b,c) = keep STORE (Word,a,b,c) + fun swc1 (a,b,c) = delay STORE (Floating,a,b,c) + val lui = keep LUI + + val sll = keep SLL (* shift *) + val sra = keep SRA + + fun align() = () (* never need to align on MIPS *) + val mark = keep (fn () => MARK) + val comment = keep COMMENT +@ +Multiplication has a minor complication; the +result has to be fetched from the LO register. +<<multiplication and division functions>>= +fun mult (op1, op2, result) = keeplist [MFLO result, MULT (op1, op2)] +val mfhi = keep MFHI +@ +Division has a major complication; I must test for divide by zero since +the hardware does not. +If the divisor is zero, I cause an overflow exception by +adding [[limitreg]] to itself. +<<multiplication and division functions>>= +fun op div (op1, op2, result) = + let val next = newlabel() + in keeplist [ + MFLO result, (* get the result *) + DEFINE next, (* skip to here if nonzero *) + BREAK 7, (* signals zerodivide *) + DIV (op1, op2), (* divide in delay slot *) + BEQ (false, Reg 0, op2, next) (* skip if divisor nonzero *) + ] + end +@ +For now, labels are just pointers to integers. +During code generation, those integers will be set to positions +in the instruction stream, and then they'll be useful as addresses +relative to the program counter pointer (to be held in [[Reg pcreg]]). +<<definition of [[Label]]>>= + type Label = int ref +<<label functions>>= + fun newlabel () = ref 0 + val define = keep DEFINE + val emitlab = keep EMITLAB +@ +Here's the overall plan of this structure: +<<*>>= +functor MipsCoder(Emitter: EMITTER) : MIPSCODER = struct + + open Emitter + <<definition of [[Label]]>> + + datatype Register = Reg of int + + datatype EA = Direct of Register + | Immed of int + | Immedlab of Label + + <<definition of [[instr]]>> + + <<instruction stream and its functions>> + + structure M = struct + <<[[MIPSCODER]] functions>> + end + + open M + + <<functions that assemble [[instr]]s into code>> + + <<statistics>> + +end (* MipsInstr *) +@ \subsection{Sizes of {\tt instr}s} +Now let's consider the correspondence between our [[instr]] type and the +actual MIPS instructions we intend to emit. +One important problem to solve is figuring out how big things are, +so that we know what addresses to generate for the various labels. +We will also want to know what address is currently stored in the program +counter regsiter ([[pcreg]]), +because we'll need to know when something is close +enough that we can use a sixteen-bit address relative to that register. +The kind of address we can use will determine how big things are. + +We'll rearrange the code so that we have a list of [[ref int * instr]] pairs, +where the [[ref int]] stores the position in the list. +(Positions start at zero.) +Since in the MIPS all instructions are the same size, we measure +position as number of instructions. +While we're at it, we reverse the list so that the head will execute first, +then the rest of the list. + +We begin with each position set to zero, and make a pass over the list +trying to set the value of each position. +We do this by estimating the size of (number of MIPS instructions +generated for) each [[instr]]. +Since there are forward references, we may not have all the distances right +the first time, so we have to make a second pass. +But during this second pass we could find that something is further +away than we thought, and we have to switch from using a pc-relative mode to +something else (or maybe grab the new pc?), which changes the size again, +and moves things even further away. +Because we can't control this process, we just keep making passes over the +list until the process quiesces (we get the same size twice). + +In order to guarantee termination, we have to make sure later passes only +increase the sizes of things. +This is sufficient since there is a maximum number of MIPS instructions +we can generate for each [[instr]]. + + +While we're at it, we might want to complicate things by making the function +that does the passes also emit code. +For a single pass we hand an optional triple of emitters, the initial position, +an [[int option]] for the program counter pointer (if known), and the +instructions. + + + +I'm not sure what explains the use of the [[ref int]] to track the position, +instead of just an [[int]]---it might be a desire to avoid the +overhead of creating a bunch of new objects, or it might be really hard +to do the passes cheaply. +It should think a variation on [[map]] would do the job, but maybe I'm +missing something. + +@ +[[emit : int * int -> unit]] emits one instruction, +and [[emit_string : int -> string -> unit]] emits a string constant. +[[emit_string]] could be specified as a function of [[emit]], +but the nature of the function would depend on whether the target +machine was little-endian or big-endian, and we don't want to have +that dependency built in. + + [[instrs]] is the +list of instructions (in execute-head-last order). + +The second argument to [[pass]] indicates for what instructions code +is to be generated. +It is a record (position of next instruction, program counter pointer if any, +remaining instructions to generate [with positions]). + +\indent [[prepare]] produces two results: the instruction stream with +size pointers added, and the total size of code to be generated. +We add the total size because that is the only way to find the number +of [[bltzal]]s, which are implicit in the instruction stream. + +<<assembler>>= +fun prepare instrs = + let fun add_positions(done, inst::rest) = + add_positions( (ref 0, inst) :: done, rest) + | add_positions(done, nil) = done + + val instrs' = add_positions(nil, instrs) (* reverse and add [[ref int]]s*) + + fun passes(oldsize) = + (* make passes with no emission until size is stable*) + let val size = pass false (0,NONE,instrs') + in if size=oldsize then size + else passes size + end + in {size = passes 0, stream = instrs'} + end + +fun assemble instrs = + pass true (0,NONE,#stream (prepare instrs)) + +<<functions that assemble [[instr]]s into code>>= +fun get (SOME x) = x + | get NONE = ErrorMsg.impossible "missing pcptr in mipscoder" + +<<[[pcptr]] functions>> +<<single pass>> +<<assembler>> + +fun codegen () = ( + assemble (!kept); + <<reinitialize [[kept]]>> + ) +@ +The program counter pointer is a device that enables us to to addressing +relative to the pcp register, register 31. +The need for it arises when we want to access a data element which we know +only by its label. +The labels give us addresses relative to the beginning of the function, +but we can only use addresses relative to some register. +The answer is to set register~31 with a [[bltzal]] instruction, +then use that for addressing. + +The function [[needs_a_pcptr]] determines when it is necessary +to have a known value in register~31. +That is, we need the program counter pointer +\begin{itemize} +\item +at [[NOP]] for a reason to be named later? +\item +at any operation that uses an effective address that refers to a label +(since all labels have to be relative to the program counter). +\item +BEQ's and BCOP1's to very far away, +since we have to compute the address for a JUMP +knowing the value of the program counter pointer. +\end{itemize} +<<[[pcptr]] functions>>= +fun needs_a_pcptr(_,SLT(_,Immedlab _,_)) = true + | needs_a_pcptr(_,ADD(_,Immedlab _,_)) = true + | needs_a_pcptr(_,AND(_,Immedlab _,_)) = true + | needs_a_pcptr(_,OR(_,Immedlab _,_)) = true + | needs_a_pcptr(_,XOR(_,Immedlab _,_)) = true + | needs_a_pcptr(_,MOVE(Immedlab _,_)) = true + | needs_a_pcptr(_,LOAD(_,_,Immedlab _,_)) = true + | needs_a_pcptr(_,STORE(_,_,Immedlab _,_)) = true + | needs_a_pcptr(_,SLL(Immedlab _,_,_)) = true + | needs_a_pcptr(_,SRA(Immedlab _,_,_)) = true + | needs_a_pcptr(1, BEQ _) = false (* small BEQ's dont need pcptr *) + | needs_a_pcptr(_, BEQ _) = true (* but large ones do *) + | needs_a_pcptr(1, BCOP1 _) = false (* small BCOP1's dont need pcptr *) + | needs_a_pcptr(_, BCOP1 _) = true (* but large ones do *) + | needs_a_pcptr _ = false +@ +Creating the program counter pointer once, with a [[bltzal]], is not +enough; we have to invalidate the program counter pointer at every +label, since control could arrive at the label from God knows where, and +therefore we don't know what the program counter pointer is. + +We use the function [[makepcptr]] to create a new program counter pointer +``on the fly'' while generating code for other [[instrs]]. +(I chose not to create a special [[instr]] for [[bltzal]], which I +could have inserted at appropriate points in the instruction stream.) +To try and find an odd bug, I'm adding no-ops after each [[bltzal]]. +I don't really believe they're necessary. + +The function [[gen]], which generates the instructions (or computes +their size), takes three arguments. +Third: the list of instructions to be generated (paired with pointers +to their sizes); first: the position (in words) at which to generate +those instructions; second: the current value of the program counter +pointer (register~31), if known. + +The mutual recursion between [[gen]] and [[makepcptr]] maintains +the program counter pointer. +[[gen]] invalidates it at labels, and calls [[makepcptr]] to create a valid +one when necessary (as determined by [[needs_a_pcptr]]). +<<single pass>>= +fun pass emit_now = +let fun makepcptr(i,x) = + (* may need to emit NOP for delay slot if next instr is branch *) + let val size = case x of ((_,BEQ _)::rest) => 2 + | ((_,BCOP1 _)::rest) => 2 + | _ => 1 + in if emit_now then (emit(Opcodes.bltzal(0,0)); + if size=2 then emit(Opcodes.add(0,0,0)) else ()) + else (); + gen(i+size, SOME (i+2), x) + end +and gen(i,_,nil) = i + | gen(i, _, (_,DEFINE lab) :: rest) = (lab := i; gen(i,NONE, rest)) + (* invalidate the pc pointer at labels *) + (* may want to do special fiddling with NOPs *) + | gen(pos, pcptr, x as ((sizeref as ref size, inst) :: rest)) = + if (pcptr=NONE andalso needs_a_pcptr(size, inst)) then makepcptr(pos,x) + else if emit_now + then + <<emit MIPS instructions>> + else + <<compute positions>> +in gen +end + +@ \subsection{Generating the instructions} +Now we need to consider the nitty-gritty details of just what instructions +are generated for each [[instr]]. +In early passes, we'll just need to know how many instructions are +required (and that number may change from pass to pass, so it must be +recomputed). +In the last pass, the sizes are stable (by definition), so we can look +at the sizes to see what instructions to generate. + +We'll consider the [[instrs]] in groups, but first, here's the +way we will structure things: +<<compute positions>>= +let <<functions for computing sizes>> + val newsize = case inst of + <<cases for sizes to be computed>> +in if newsize > size then sizeref := newsize else (); + gen(pos+(!sizeref) (* BUGS -- was pos+size*),pcptr,rest) +end +<<emit MIPS instructions>>= +let fun gen1() = gen(pos+size,pcptr,rest) + (* generate the rest of the [[instr]]s *) + open Bits + open Opcodes + <<declare reserved registers [[tempreg]] and [[pcreg]]>> + <<functions for emitting instructions>> +in case inst of + <<cases of instructions to be emitted>> +end +@ When we get around to generating code, we may need to use a temporary +register. +For example, if we want to load into a register +an immediate constant that won't fit +into 16~bits, we will have to load the high-order part of the constant +with [[lui]], then use [[addi]] to add then the low-order part. +The MIPS assembler has a similar problem, and on page D-2 of +the MIPS book we notice that register~1 is reserved for the use of the +assembler. +So we do the same. + +We need to reserve a second register for use in pointing to the program +counter. +We will use register 31 because the [[bltzal]] instruction automatically +sets register 31 to the PC. +<<declare reserved registers [[tempreg]] and [[pcreg]]>>= +val tempreg = 1 +val pcreg = 31 +@ +Before showing the code for the actual instructions, we should +point out that +we have two different ways of emitting a long word. +[[emitlong]] just splits the bits into two pieces for those cases +when it's desirable to put a word into the memory image. +[[split]] gives something that will load correctly +when the high-order piece is loaded into a high-order halfword +(using [[lui]]), +and the low-order piece is sign-extended and then added to the +high-order piece. +This is the way we load immediate constants of more than sixteen bits. +It is also useful for generating load or store instructions with +offsets of more than sixteen bits: we [[lui]] the [[hi]] part and +add it to the base regsiter, then use the [[lo]] part as an offset. +<<functions for emitting instructions>>= +fun emitlong i = emit(rshift(i,16), andb(i,65535)) + (* emit one long word (no sign fiddling) *) +fun split i = let val hi = rshift(i,16) and lo = andb(i,65535) + in if lo<32768 then (hi,lo) else (hi+1, lo-65536) + end + +@ We begin implementing [[instrs]] by considering those that emit constants. +String constants are padded with nulls out to a word boundary. +Integer constants are just emitted with [[emitlong]]. +<<cases for sizes to be computed>>= + STRINGCONST s => Integer.div(String.length(s)+3,4) +| EMITLONG _ => 1 +<<cases of instructions to be emitted>>= + STRINGCONST s => + let val s' = s ^ "\000\000\000\000" + in gen1(emit_string (4*size) s') + (* doesn't know Big vs Little-Endian *) + end +| EMITLONG i => gen1(emitlong i) +@ +Next consider the labels. +A [[DEFINE]] should never reach this far, and [[EMITLAB]] is almost like +an [[EMITLONG]]. +<<cases for sizes to be computed>>= +| DEFINE _ => ErrorMsg.impossible "generate code for DEFINE in mipscoder" +| EMITLAB _ => 1 +<<cases of instructions to be emitted>>= +| DEFINE _ => gen1(ErrorMsg.impossible "generate code for DEFINE in mipscoder") +| EMITLAB(i, ref d) => gen1(emitlong((d-pos)*4+i)) +@ +Now we have to start worrying about instructions with [[EA]] in them. +The real difficulty these things present is that they may have an +immediate operand that won't fit in 16~bits. +So we'll need to get this large immediate operand into a register, +sixteen bits at a time, and then do the operation on the register. + +Since all of the arithmetic instructions have this difficulty, and since +we can use them to implement the others, we'll start with those and +catch up with the control-flow instructions later. +@ [[SUB]], [[MULT]], [[DIV]], and [[MFLO]] all use registers only, +so they are easy. +The other arithmetic operations get treated exactly the same, so we'll +use a function to compute the size. +{\bf move this to follow the definition of [[arith]]?} +<<cases for sizes to be computed>>= +| ADD(_, ea, _) => easize ea +| AND(_, ea, _) => easize ea +| OR (_, ea, _) => easize ea +| XOR(_, ea, _) => easize ea +| SUB _ => 1 +| DIV (_,_) => 1 +| MULT (_,_) => 1 +| MFLO _ => 1 +| MFHI _ => 1 +@ Register operations take one instruction. +Immediate operations take one instruction for 16~bit constants, +and 3 for larger constants (since it costs two instructions to load +a big immediate constant into a register). +An immediate instruction with [[Immedlab l]] means that the operand +is intended to be the machine address associated with that label. +To compute that address, we need to add +[[4*(l-pcptr)]] to the contents of +register~[[pcreg]] (which holds [[4*pcptr]]), +put the results in a register, and operate on that register. + +This tells us enough to compute the sizes. +<<functions for computing sizes>>= +fun easize (Direct _) = 1 + | easize (Immed i) = if abs(i)<32768 then 1 else 3 + | easize (Immedlab(ref lab)) = 1 + easize(Immed (4*(lab-(get pcptr)))) +@ +As we have seen, +to implement any arithmetic operation, we need to know the register +form and the sixteen-bit immediate form. +We will also want the operator from [[instr]], since we do the +large immediate via a recursive call. +We'll set up a function, [[arith]], that does the job. +<<functions for emitting instructions>>= +fun arith (opr, rform, iform) = + let fun ar (Reg op1, Direct (Reg op2), Reg result) = + gen1(emit(rform(result,op1,op2))) + | ar (Reg op1, Immed op2, Reg result) = + (case size of + 1 (* 16 bits *) => gen1(emit(iform(result,op1,op2))) + | 3 (* 32 bits *) => + gen(pos,pcptr, + (ref 2, LDI_32(op2, Reg tempreg)):: + (ref 1, opr(Reg op1, Direct(Reg tempreg), Reg result)):: + rest) + | _ => gen(ErrorMsg.impossible + "bad size in arith Immed in mipscoder") + ) + | ar (Reg op1, Immedlab (ref op2), Reg result) = + gen(pos, pcptr, + (ref (size-1), + ADD(Reg pcreg,Immed(4*(op2-(get pcptr))), Reg tempreg)):: + (ref 1, opr(Reg op1, Direct(Reg tempreg), Reg result)):: + rest) + in ar + end +@ +The generation itself may be a bit anticlimactic. +The MIPS has no ``subtract immediate'' instruction, and [[SUB]] has +a different type than the others, so we emit it directly. +<<cases of instructions to be emitted>>= +| ADD stuff => arith (ADD,add,addi) stuff +| AND stuff => arith (AND,and',andi) stuff +| OR stuff => arith (OR,or,ori) stuff +| XOR stuff => arith (XOR,xor,xori) stuff +| SUB (Reg op1, Reg op2, Reg result) => gen1(emit(sub(result,op1,op2))) +| DIV (Reg op1, Reg op2) => gen1(emit(div(op1,op2))) +| MULT(Reg op1, Reg op2) => gen1(emit(mult(op1,op2))) +| MFLO(Reg result) => gen1(emit(mflo(result))) +| MFHI(Reg result) => gen1(emit(mfhi(result))) +@ Floating point arithmetic is pretty easy because we always do it in +registers. +We also support only one format, double precision. +<<cases for sizes to be computed>>= +| NEG_D _ => 1 +| MUL_D _ => 1 +| DIV_D _ => 1 +| ADD_D _ => 1 +| SUB_D _ => 1 +@ When emitting instructions we have to remember the Mips instructions +use result on the left, but the [[MIPSCODER]] signature requires result +on the right. +<<cases of instructions to be emitted>>= +| NEG_D (Reg op1,Reg result) => gen1(emit(neg_fmt(D_fmt,result,op1))) +<<functions for emitting instructions>>= +fun float3double instruction (Reg op1,Reg op2,Reg result) = + gen1(emit(instruction(D_fmt,result,op1,op2))) +<<cases of instructions to be emitted>>= +| MUL_D x => float3double mul_fmt x +| DIV_D x => float3double div_fmt x +| ADD_D x => float3double add_fmt x +| SUB_D x => float3double sub_fmt x + + +@ We offer a separate [[MOVE]] instruction because of large immediate +constants. +It is always possible to do [[move(src,dest)]] by doing +[[add(Reg 0,src,dest)]], but the general form [[add(Reg i, Immed c, dest)]] +takes three instructions when [[c]] is a large constant (more than 16 bits). +Rather than clutter up the code for [[add]] (and [[or]] and [[xor]]) by +trying to recognize register~0, we provide [[move]] explicitly. + +\indent [[LDI_32]] takes care of the particular case in which we are +loading a 32-bit immediate constant into a register. +It dates from the bad old days before [[MOVE]], and it might be a good idea +to remove it sometime. +<<functions for emitting instructions>>= +fun domove (Direct (Reg src), Reg dest) = gen1(emit(add(dest,src,0))) + | domove (Immed src, Reg dest) = + (case size of + 1 (* 16 bits *) => gen1(emit(addi(dest,0,src))) + | 2 (* 32 bits *) => + gen(pos,pcptr,(ref 2, LDI_32(src, Reg dest))::rest) + | _ => gen(ErrorMsg.impossible "bad size in domove Immed in mipscoder") + ) + | domove (Immedlab (ref src), Reg dest) = + gen(pos, pcptr, + (ref size, + ADD(Reg pcreg,Immed(4*(src-(get pcptr))), Reg dest))::rest) +@ Notice we use [[easize]] and not [[movesize]] in the third clause +because when we reach this point the treatment of a [[MOVE]] is the same +as that of an [[ADD]]. +<<functions for computing sizes>>= +fun movesize (Direct _) = 1 + | movesize (Immed i) = if abs(i)<32768 then 1 else 2 + | movesize (Immedlab(ref lab)) = easize(Immed (4*(lab-(get pcptr)))) + +<<cases for sizes to be computed>>= +| MOVE (src,_) => movesize src +| LDI_32 _ => 2 +| LUI _ => 1 +<<cases of instructions to be emitted>>= +| MOVE stuff => domove stuff +| LDI_32 (immedconst, Reg dest) => + let val (hi,lo) = split immedconst + in gen1(emit(lui(dest,hi));emit(addi(dest,dest,lo))) + end +| LUI (Reg dest,immed16) => gen1(emit(lui(dest,immed16))) + +@ +Now that we've done arithmetic, we can see how to do control flow without +too much trouble. +[[SLT]] can be treated just like an arithmetic operator. +[[BEQ]] is simple if the address to which we branch is close enough. +Otherwise we use the following sequence for [[BEQ(Reg op1, Reg op2, ref dest)]]: +\begin{verbatim} + bne op1,op2,L + ADD (Reg pcreg, Immed (4*(dest-pcptr)), Reg tempreg) + jr tempreg + L: ... +\end{verbatim} +Notice we don't have to put a [[NOP]] in the delay slot of the [[bne]]. +We don't need one after the jump unless we needed one after the +original [[BEQ]], in which case one will be there. +If the branch is taken, we're doing as well as we can. +If the branch is not taken, we will have executed an [[add]] or [[lui]] in the +delay slot of the [[bne]], but the results just get thrown away. +<<cases for sizes to be computed>>= +| SLT(_, ea, _) => easize ea +| BEQ(_,_,_,ref dest) => + if abs((pos+1)-dest) < 32768 then 1 (* single instruction *) + else 2+easize (Immed (4*(dest-(get pcptr)))) +| JUMP _ => 1 +| SLT_D _ => 1 +| SEQ_D _ => 1 +| BCOP1(_,ref dest) => + if abs((pos+1)-dest) < 32768 then 1 (* single instruction *) + else 2+easize (Immed (4*(dest-(get pcptr)))) +| NOP => 1 +@ The implementation is as described, except we use a +non-standard [[nop]]. +There are many Mips instructions that have no effect, and the standard +one is the word with all zeroes ([[sll 0,0,0]]). +We use [[add]], adding 0 to 0 and store the result in 0, because it +will be easy to distinguish from a data word that happens to be zero. +<<cases of instructions to be emitted>>= +| SLT stuff => arith (SLT,slt,slti) stuff +| BEQ(b, Reg op1, Reg op2, ref dest) => + if size = 1 then + gen1(emit((if b then beq else bne)(op1,op2,dest-(pos+1)))) + else gen(pos,pcptr, + (ref 1, BEQ(not b, Reg op1, Reg op2, ref(pos+size))) + ::(ref (size-2), + ADD(Reg pcreg, Immed(4*(dest-(get pcptr))), Reg tempreg)) + ::(ref 1, JUMP(Reg tempreg)) + ::rest) +| JUMP(Reg dest) => gen1(emit(jr(dest))) +| SLT_D (Reg op1, Reg op2) => + gen1(emit(c_lt(D_fmt,op1,op2))) +| SEQ_D (Reg op1, Reg op2) => + gen1(emit(c_seq(D_fmt,op1,op2))) +| BCOP1(b, ref dest) => + let fun bc1f offset = cop1(8,0,offset) + fun bc1t offset = cop1(8,1,offset) + in if size = 1 then + gen1(emit((if b then bc1t else bc1f)(dest-(pos+1)))) + else gen(pos,pcptr, + (ref 1, BCOP1(not b, ref(pos+size))) + ::(ref (size-2), + ADD(Reg pcreg, Immed(4*(dest-(get pcptr))), Reg tempreg)) + ::(ref 1, JUMP(Reg tempreg)) + ::rest) + end +| NOP => gen1(emit(add(0,0,0))) (* one of the many MIPS no-ops *) +@ +Our next problem is to tackle load and store. +The major difficulty is if the offset is too large to fit in +sixteen bits; if so, we have to create a new base register. +If we have [[Immedlab]], we do it as an offset from [[pcreg]]. +<<functions for emitting instructions>>= +fun memop(rform,Reg dest, Direct (Reg base), offset) = + (case size + of 1 => gen1(emit(rform(dest,offset,base))) + | 3 => let val (hi,lo) = split offset + in gen1(emit(lui(tempreg,hi)); (* tempreg = hi @<< 16 *) + emit(add(tempreg,base,tempreg));(* tempreg += base *) + emit(rform(dest,lo,tempreg)) (* load dest,lo(tempreg) *) + ) + end + | _ => gen1(ErrorMsg.impossible "bad size in memop Direct in mipscoder") + ) + | memop(rform,Reg dest, Immed address, offset) = + (case size + of 1 => gen1(emit(rform(dest,offset+address,0))) + | 2 => let val (hi,lo) = split (offset+address) + in gen1(emit(lui(tempreg,hi)); + emit(rform(dest,lo,tempreg)) + ) + end + | _ => gen1(ErrorMsg.impossible "bad size in memop Immed in mipscoder") + ) + | memop(rform,Reg dest, Immedlab (ref lab), offset) = + memop(rform, Reg dest, Direct (Reg pcreg), offset+4*(lab - get pcptr)) +@ The actual registers don't matter for computing sizes, and in fact +the value of [[pcreg]] is not visible here, so we use an arbitrary +register ([[Reg 0]]) to compute the size. +<<functions for computing sizes>>= +fun adrsize(_, Reg _, Direct _, offset) = + if abs(offset)<32768 then 1 else 3 + | adrsize(_, Reg _, Immed address, offset) = + if abs(address+offset) < 32768 then 1 else 2 + | adrsize(x, Reg dest, Immedlab (ref lab), offset) = + adrsize(x, Reg dest, Direct (Reg 0 (* pcreg in code *) ), + offset+4*(lab-(get pcptr))) +<<cases for sizes to be computed>>= +| LOAD x => adrsize x +| STORE x => adrsize x +<<cases of instructions to be emitted>>= +| LOAD (Byte,dest,address,offset) => memop(lbu,dest,address,offset) +| LOAD (Word,dest,address,offset) => memop(lw,dest,address,offset) +| LOAD (Floating,dest,address,offset) => memop(lwc1,dest,address,offset) +| STORE (Byte,dest,address,offset) => memop(sb,dest,address,offset) +| STORE (Word,dest,address,offset) => memop(sw,dest,address,offset) +| STORE (Floating,dest,address,offset) => memop(swc1,dest,address,offset) +@ +For the shift instructions, only register and immediate operands +make sense. +Immediate operands make sense if and only if they are representable +in five bits. +If everything is right, these are single instructions. +<<cases for sizes to be computed>>= +| SLL _ => 1 +| SRA _ => 1 +<<cases of instructions to be emitted>>= +| SLL (Immed shamt, Reg op1, Reg result) => gen1( + if (shamt >= 0 andalso shamt < 32) then emit(sll(result,op1,shamt)) + else ErrorMsg.impossible ("bad sll shamt " + ^ (Integer.makestring shamt) ^ " in mipscoder")) +| SLL (Direct(Reg shamt), Reg op1, Reg result) => + gen1(emit(sllv(result,op1,shamt))) +| SLL (Immedlab _,_,_) => ErrorMsg.impossible "sll shamt is Immedlab in mipscoder" +| SRA (Immed shamt, Reg op1, Reg result) => gen1( + if (shamt >= 0 andalso shamt < 32) then emit(sra(result,op1,shamt)) + else ErrorMsg.impossible ("bad sra shamt " + ^ (Integer.makestring shamt) ^ " in mipscoder")) +| SRA (Direct(Reg shamt), Reg op1, Reg result) => + gen1(emit(srav(result,op1,shamt))) +| SRA (Immedlab _,_,_) => ErrorMsg.impossible "sra shamt is Immedlab in mipscoder" +@ +Finally, comments are ignored, and marks (backpointers) are written into the +instruction stream. + +Comments are used by the front end to give diagnostics. +In the bad old days we would have had two different [[MIPSCODER]]s, one +which generated machine code (and ignored comments), and one which +wrote out assembly code (and copied comments). +Today we have just one, which means the rerouting of comments takes place +at a much higher level. Look in [[cps/mipsglue.nw]]. +<<cases for sizes to be computed>>= +| COMMENT _ => 0 +| MARK => 1 (* backpointer takes one word *) +| BREAK _ => 1 (* break instruction *) +@ Just for the record, here's the description of what a mark (backpointer) +is. +``Take the byte address at which the mark resides and add 4, giving +the byte address of the object following the mark. +(That object is the marked object.) +Subtract the byte address of the initial word that marks the +start of this instruction stream. +Now divide by 4, giving the distance in words between the +beginning of the block and the marked object. +Take that quantity and shift it left by multiplying by [[power_tags]], +and indicate the result is a mark by adding the tag bits [[tag_backptr]] +into the low order part.'' + [[pos+1]] is exactly the required distance in words. +<<cases of instructions to be emitted>>= +| COMMENT _ => gen1() +| MARK => gen1( + let open System.Tags + in emitlong((pos+1) * power_tags + tag_backptr) + end) +| BREAK n => gen1( + if n < 0 orelse n > 32 then ErrorMsg.impossible "bad break code" + else emit(break n)) +@ +\subsection{Optimization} +The first step towards optimization is to take statistics. +We will count: [[instrs]], Mips words, [[NOP]]s in load and branch delays, +and [[bltzal]]s. +In the current implementation the [[bltzal]]s are implicit, so there +is no way to count them or optimize them. +<<statistics>>= +fun printstats stream + {inst : int, code : int, data : int, + load : int, branch : int, compare : int, size : int} = + let val print = output stream + val nop = load+branch+compare + val bltzal = size - (code + data) + val code = code + bltzal + <<definition of [[sprintf]]>> + fun P x = substring(makestring(100.0 * x),0,4) (* percent *) + fun printf f d = print (sprintf f d) + in printf ["Counted "," instrs in "," words (", + " code, "," data)\n" ^ + "Used "," NOPs ("," load, "," branch,"," compare) and "," bltzals\n" ^ + "","% of code words were NOPs; ","% were bltzals\n" ^ + "","% of all words were code; ","% of all words were NOPs\n"] + [I inst, I size, I code, I data, + I nop, I load, I branch, I compare, I bltzal, + P (real nop / real code), P (real bltzal / real code), + P (real code / real size), P (real nop / real size)] + handle Overflow => print "[Overflow in computing Mips stats]\n" + end + +<<statistics>>= +<<definition of [[iscode]]>> +fun addstats (counts as {inst,code,data,load,branch,compare}) = + fn nil => counts + | (sizeref,first)::(_,NOP)::rest => addstats + {inst=inst+2, code=code+(!sizeref)+1, data=data, + load=load+ (case first of LOAD _ => 1 | _ => 0), + branch=branch +(case first of BEQ _ => 1 | JUMP _ => 1 + | BCOP1 _ => 1 | _ => 0), + compare=compare+(case first of SLT_D _ => 1 | SEQ_D _ => 1 + | _ => 0) + } rest + | (sizeref,first)::rest => addstats + {inst=inst+1, + code = code + if iscode(first) then !sizeref else 0, + data = data + if not (iscode first) then !sizeref else 0, + load=load, + branch=branch, + compare=compare + } rest + + +fun codestats outfile = + let val {size,stream=instrs} = prepare (!kept) + val zero = {inst=0, code=0, data=0, load=0, branch=0, compare=0} + val counts as {inst,code,data,load,branch,compare} = + addstats zero instrs + in printstats outfile + {inst=inst,code=code,data=data, + load=load,branch=branch,compare=compare,size=size} + end + +<<definition of [[iscode]]>>= +val iscode = fn + STRINGCONST _ => false + | EMITLONG _ => false + | DEFINE _ => false + | EMITLAB _ => false + + | SLT _ => true + | BEQ _ => true + | JUMP _ => true + | NOP => true + | SLT_D _ => true + | SEQ_D _ => true + | BCOP1 _ => true + + | ADD _ => true + | AND _ => true + | OR _ => true + | XOR _ => true + | SUB _ => true + | MULT _ => true + | DIV _ => true + | MFLO _ => true + | MFHI _ => true + + | NEG_D _ => true + | MUL_D _ => true + | DIV_D _ => true + | ADD_D _ => true + | SUB_D _ => true + + | MOVE _ => true + | LDI_32 _ => true + | LUI _ => true + + | LOAD _ => true + | STORE _ => true + + | SLL _ => true + | SRA _ => true + + | COMMENT _ => false + | MARK => false + | BREAK _ => true + +<<definition of [[sprintf]]>>= +val I = Integer.makestring +val R = Real.makestring +exception Printf +fun sprintf format values = + let fun merge([x],nil) = [x] + | merge(nil,nil) = nil + | merge(x::y,z::w) = x::z:: merge(y,w) + | merge _ = raise Printf + in implode(merge(format,values)) + end + +@ +At the moment these functions are meaningless junk. +<<functions that remove pipeline bubbles>>= +val rec squeeze = + + fn (x as LOAD(_,Reg d, m, i))::NOP::instr::rest => + if use(instr,d) then ?? + else squeeze(x::instr::rest) + | (x as STORE _)::(y as LOAD _)::rest => + x :: squeeze(y::rest) + | instr::(x as LOAD(_,Reg d, Direct(Reg s), i))::NOP::rest => + if use(instr, d) orelse gen(instr, s) then ?? + else squeeze(x::instr::rest) + | instr::(x as LOAD(_,Reg d, _, i))::NOP::rest => + if use(instr,d) then ?? + else squeeze(x::instr::rest) + | (x as MFLO _):: (y as MULDIV _) :: rest => + x :: squeeze (y::rest) + | (x as MFLO(Reg d))::instr::rest => + if (use(instr,d) orelse gen(instr,d) then ?? + else squeeze(instr::x::rest) + | instr :: (x as MULDIV(Reg a, Reg b)) :: rest => + if gen(instr,a) orelse gen(instr,b) then ?? + else squeeze(x::instr::rest) + +val rec final = + fn + | instr::(x as LOAD(_,Reg d, Direct(Reg s), i))::NOP::rest => + if gen(instr, s) then instr::final(x::NOP::rest) + else x::instr::(final rest) + | instr :: (x as JUMP _) :: NOP :: rest => + x :: instr :: final rest + | instr :: (x as BEQ(_,Reg a, Reg b, _)) :: NOP :: rest => + if gen(instr,a) orelse gen(instr,b) then instr::x::NOP::(final rest) + else x::instr::(final rest) +@ +\section{Indices} +\subsection{Chunks} +\nowebchunks +\subsection{Identifiers} +\nowebindex diff --git a/web/noweb/examples/multicol.sty b/web/noweb/examples/multicol.sty new file mode 100644 index 0000000000..bc1b703031 --- /dev/null +++ b/web/noweb/examples/multicol.sty @@ -0,0 +1,525 @@ +%% +%% This is file `multicol.sty', generated +%% on <1993/10/28> with the docstrip utility (2.1e). +%% +%% The original source files were: +%% +%% multicol.doc (with options: `style,check,marktrace') +%% +%% Copyright (C) 1989-1992 by Frank Mittelbach. All rights reserved. +%% +%% This file is part of the multicol package. +%% +%% IMPORTANT NOTICE: +%% +%% You are not allowed to change this file. You may however copy +%% this file to a file with a different name and then change the copy +%% if you obey the restrictions on file changes described in +%% readme.mz. +%% +%% You are NOT ALLOWED to distribute this file alone. You are NOT +%% ALLOWED to take money for the distribution or use of this file (or +%% a changed version) except for a nominal charge for copying etc. +%% +%% You are allowed to distribute this file under the condition that +%% it is distributed together with all files mentioned in readme.mz1. +%% +%% If you receive only some of these files from someone, complain! +%% +%% However, if these files are distributed by established suppliers +%% as part of a complete TeX distribution, and the structure of the +%% distribution would make it difficult to distribute the whole set +%% of files, *those parties* are allowed to distribute only some of +%% the files provided that it is made clear that the user will get a +%% complete distribution-set upon request to that supplier (not me). +%% Notice that this permission is not granted to the end user. +%% +%% +%% For error reports in case of UNCHANGED versions see readme.mz +%% +%% +\def\fileversion{v1.4m} +\def\filedate{92/09/04} +\def\docdate {92/09/04} + +%% \CheckSum{1101} +%% \CharacterTable +%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +%% +%% Style-option `multicol' to use with LaTeX v2.09 +%% Copyright (C) 1989-1992 Frank Mittelbach, all rights reserved. +\@ifundefined{mult@cols}{}{\endinput} +\typeout{Style option: `multicol' + \fileversion\space <\filedate> (FMi)} +\typeout{English documentation + \@spaces\@spaces\space<\docdate> (FMi)} +\def\multicols#1{\col@number#1\relax + \ifnum\col@number<\tw@ + \@warning{Using `\number\col@number' + columns doesn't seem a good idea.^^J + I therefore use two columns instead}% + \col@number\tw@ \fi + \ifnum\col@number>5 + \@warning{Current implementation doesn't + support more than five columns.^^J + I therefore use five columns instead}% + \col@number5 \fi + \@ifnextchar[\mult@cols{\mult@cols[]}} +\def\mult@cols[#1]{\@ifnextchar[% + {\mult@@cols{#1}}% + {\mult@@cols{#1}[\premulticols]}} +\def\mult@@cols#1[#2]{% + \par + \ifinner \@boxedmulticolstrue + \else + \ifnum \doublecol@number>\z@ + \@boxedmulticolstrue + \fi + \fi + \ifnum\c@tracingmulticols>\z@ + \typeout{^^J^^JStarting multicolumn + output with \the\col@number + \space columns% + \if@boxedmulticols\space + (boxed mode)\fi + \on@line:^^J}\fi + \enough@room{#2}% + {#1\par}\addvspace\multicolsep + \begingroup + \prepare@multicols + \if@boxedmulticols + \setbox\mult@box\vbox\bgroup + \penalty-\@M + \fi + \ignorespaces} +\newif\if@boxedmulticols \@boxedmulticolsfalse +\newbox\mult@box +\def\enough@room#1{% + \if@boxedmulticols\else + \par + \bgroup\@nobreakfalse\addpenalty\z@\egroup + \page@free \pagegoal + \advance \page@free -\pagetotal + \ifnum \c@tracingmulticols>\z@ + \typeout{Current page:}% + \message{\@spaces goal height=% + \the\pagegoal: used \the\pagetotal + \space -> free=\the\page@free}% + \typeout{\@spaces needed \the#1 + (for \string#1)}\fi + \ifdim \page@free <#1\newpage \fi + \fi} +\def\prepare@multicols{% + \multicol@leftmargin\@totalleftmargin + \@totalleftmargin\z@ + \parshape\z@ + \doublecol@number\col@number + \multiply\doublecol@number\tw@ + \if@boxedmulticols + \let\l@kept@firstmark\kept@firstmark + \let\l@kept@botmark\kept@botmark + \global\let\kept@firstmark\@empty + \global\let\kept@botmark\@empty + \else + \nointerlineskip \null + \output{% + \global\setbox\partial@page\vbox + {% + \ifvoid\partial@page\else + \@latexerr{Error saving partial + page\on@line}% + {The part of the page before + the multicols environment was + nearly full with the result + that starting the environment + will produce an overfull + page. Some text may be lost! + Please increase \premulticols + either generally or for this + environment by specifying a + suitable value in the second + optional argument to the + multicols environment.} + \unvbox\partial@page + \box\last@line + \fi + \unvbox\@cclv + \global\setbox\last@line\lastbox + }% + \prep@keptmarks + \global\let\kept@topmark\firstmark + }\eject + \advance\@colroom-\ht\partial@page + \vsize\col@number\@colroom + \advance\vsize\c@collectmore\baselineskip + \output{\multi@column@out}% + \multiply\count\footins\col@number + \multiply\skip \footins\col@number + \reinsert@footnotes + \fi + \vbadness10001 \hbadness5000 + \tolerance\multicoltolerance + \pretolerance\multicolpretolerance + \setemergencystretch\col@number\hsize + \set@floatcmds + \advance\baselineskip\multicolbaselineskip + \hsize\linewidth \advance\hsize\columnsep + \advance\hsize-\col@number\columnsep + \divide\hsize\col@number + \full@width\linewidth + \linewidth\hsize +} +\newdimen\multicol@leftmargin +\def\endmulticols{\par + \if@boxedmulticols + \egroup + \setbox\@tempboxa\vsplit\mult@box to\z@ + \balance@columns + \return@nonemptymark{first}\kept@firstmark + \return@nonemptymark{bot}\kept@botmark + \page@sofar + \global\let\kept@firstmark\l@kept@firstmark + \global\let\kept@botmark\l@kept@botmark + \ifnum\c@tracingmulticols>\tw@ + \typeout{Restore kept marks to% + ^^J first: \meaning\kept@firstmark% + ^^J bot\space\space: + \meaning\kept@botmark }% + \fi + \else + \bgroup\@nobreakfalse\addpenalty\z@\egroup + \output{\balance@columns@out}\eject + \ifvbox\partial@page\unvbox\partial@page\fi + \global\let\kept@firstmark\@empty + \global\let\kept@botmark\@empty + \ifnum\c@tracingmulticols>\tw@ + \typeout{Make kept marks empty}% + \fi + \fi + \@checkend{multicols}% + \endgroup + \if@boxedmulticols\else + \reinsert@footnotes + \fi + \global\c@unbalance\z@ + \enough@room\postmulticols + \addvspace\multicolsep + \ifnum\c@tracingmulticols>\z@ + \typeout{^^JEnding multicolumn + output% + \if@boxedmulticols + \space(boxed mode)\fi + .^^J^^J}\fi} +\newcount\c@unbalance +\newcount\c@collectmore +\newcount\c@tracingmulticols +\newcount\col@number +\newcount\doublecol@number +\newcount\multicoltolerance +\newcount\multicolpretolerance +\newdimen\full@width +\newdimen\page@free +\newdimen\premulticols +\newdimen\postmulticols +\newskip\multicolsep +\newskip\multicolbaselineskip +\newbox\partial@page +\newbox\last@line +\c@unbalance = 0 +\c@collectmore = 0 +\c@tracingmulticols = 0 +\col@number = 1 +\multicoltolerance = 9999 +\multicolpretolerance = -1 +\premulticols = 50pt +\postmulticols= 20pt +\multicolsep = 12pt plus 4pt minus 3pt +\multicolbaselineskip=0pt +\def\process@cols#1#2{\count@#1\relax + \loop #2% + \advance\count@\tw@ + \ifnum\count@<\doublecol@number + \repeat} +\def\page@sofar{% + \process@cols\z@{\wd\count@\hsize}% + \ifnum\c@tracingmulticols>\z@ + \typeout{Column spec: + (\the\multicol@leftmargin\space --> + \the\full@width\space = \the\hsize + \space x \the\col@number)^^J}% + \fi +\ifvmode\else\errmessage{Multicol Error}\fi + \moveright\multicol@leftmargin + \hbox to\full@width{% + \process@cols\tw@{\box\count@ + \hss\vrule\@width\columnseprule\hss}% + \box\z@ + \rlap{\phantom p}% +}% +} +\def\reinsert@footnotes{\ifvoid\footins\else + \insert\footins{}\fi} +\def\multi@column@out{% + \ifnum\outputpenalty <-\@Mi + \speci@ls \else + \splittopskip\topskip + \splitmaxdepth\maxdepth + \dimen@\@colroom + \divide\skip\footins\col@number + \ifvoid\footins \else + \advance\dimen@-\skip\footins + \advance\dimen@-\ht\footins \fi + \process@cols\tw@{\setbox\count@ + \vsplit\@cclv to\dimen@ + \set@keptmarks + \ifshr@nking + \setbox\count@\vbox to\dimen@ + {\unvbox\count@\vfill}% + \fi + }% + \setbox\z@\vsplit\@cclv to\dimen@ + \set@keptmarks + \ifshr@nking + \setbox\z@\vbox to\dimen@ + {\unvbox\z@\vfill}% + \fi + \ifvoid\@cclv \else + \unvbox\@cclv + \penalty\outputpenalty + \ifvoid\footins\else + \@warning{I moved some lines to + the next page.^^J + \@spaces Footnotes on page + \thepage\space might be wrong}% + \fi + \ifnum \c@tracingmulticols>\thr@@ + \hrule\allowbreak \fi + \fi + \ifx\@empty\kept@firstmark + \let\firstmark\kept@topmark + \let\botmark\kept@topmark + \else + \let\firstmark\kept@firstmark + \let\botmark\kept@botmark + \fi + \ifnum\c@tracingmulticols>\tw@ + \typeout{Use kept top + \space\space mark: + `\meaning\kept@topmark'% + ^^JUse kept first mark: + `\meaning\kept@firstmark'% + ^^JUse kept bot + \space\space mark: + `\meaning\kept@botmark'% + ^^JProduce first mark: + `\meaning\firstmark'% + ^^JProduce bot + \space\space mark: + `\meaning\botmark'}% + \fi + \setbox\@cclv\vbox{\unvbox\partial@page + \page@sofar}% + \@makecol\@outputpage + \global\let\kept@topmark\botmark + \global\let\kept@firstmark\@empty + \global\let\kept@botmark\@empty + \ifnum\c@tracingmulticols>\tw@ + \typeout{(Re)Init top + \space\space mark: + `\meaning\kept@topmark'}% + \fi + \global\@colroom\@colht + \process@deferreds + \@whilesw\if@fcolmade\fi{\@outputpage + \global\@colroom\@colht + \process@deferreds}% + \ifnum\c@tracingmulticols>\@ne + \typeout{Colroom: \the\@colht\space + after float space removed + = \the\@colroom }\fi + \global\vsize\col@number\@colroom + \global\advance\vsize + \c@collectmore\baselineskip + \fi} +\def\speci@ls{% + \typeout{Floats and marginpars not + allowed inside `multicols' + environment!}% + \unvbox\@cclv\reinsert@footnotes + \xdef\@freelist{\@freelist\@currlist}% + \gdef\@currlist{}} +\def\process@deferreds{% + \@floatplacement + \@tryfcolumn\@deferlist + \if@fcolmade\else + \begingroup + \let\@tempb\@deferlist + \gdef\@deferlist{}% + \let\@elt\@scolelt + \@tempb \endgroup + \fi} +\newif\ifshr@nking +\def\raggedcolumns{% + \@bsphack\shr@nkingtrue\@esphack} +\def\flushcolumns{% + \@bsphack\shr@nkingfalse\@esphack} +\def\balance@columns@out{% + \setbox\mult@box\vbox{\unvbox\@cclv}% + \balance@columns + \global\vsize\@colroom + \global\advance\vsize\ht\partial@page + \unvbox\partial@page + \return@nonemptymark{first}\kept@firstmark + \return@nonemptymark{bot}\kept@botmark + \page@sofar} +\def\balance@columns{% + \setbox\mult@box\vbox to\ht\mult@box{% + \unvbox\mult@box + \remove@discardable@items + }% + \get@keptmarks\mult@box + \splittopskip\topskip + \splitmaxdepth\maxdepth + \@tempdima\ht\mult@box + \advance\@tempdima\dp\mult@box + \divide\@tempdima\col@number + \count@\@tempdima + \divide\count@\baselineskip + \dimen@\count@\baselineskip + \advance\dimen@\topskip + \ifdim \dimen@ >\@tempdima + \advance\dimen@-\baselineskip + \fi + \advance\dimen@\c@unbalance\baselineskip + \ifnum\c@tracingmulticols>\@ne + \typeout{Balance columns\on@line: + \ifnum\c@unbalance=\z@\else + (off balance=\number\c@unbalance)\fi}% + \fi + \ifnum\dimen@<\topskip + \ifnum\c@tracingmulticols>\@ne + \typeout{Start value + \the\dimen@ \space -> + \the\topskip \space (corrected)}% + \fi + \dimen@\topskip + \fi + \vbadness\@M \loop + {\process@cols\@ne{\global\setbox\count@ + \box\voidb@x}}% + \global\setbox\@ne\copy\mult@box + {\process@cols\thr@@{% + \global\setbox\count@ + \vsplit\@ne to\dimen@ + }}% + \boxmaxdepth\maxdepth + \global\setbox\@ne\vbox{\unvbox\@ne}% + \ifnum\c@tracingmulticols>\@ne + \message{\@spaces First column + = \the\ht\thr@@}\fi + \ifshr@nking \global\setbox\thr@@ + \vbox{\unvbox\thr@@}% + \ifnum\c@tracingmulticols>\@ne + \message{ after shrinking + \the\ht\thr@@}\fi\fi + \ifnum\c@tracingmulticols>\@ne + \message{<> last column = + \the\ht\@ne^^J}% + \fi + \ifdim\ht\@ne >\ht\thr@@ + \advance\dimen@\p@ + \repeat + \dimen@\ht\thr@@ + \ifdim\dimen@>\@colroom + \dimen@\@colroom + \fi + \process@cols\z@{\@tempcnta\count@ + \advance\@tempcnta\@ne + \setbox\count@\vbox to\dimen@ + {\unvbox\@tempcnta + \ifshr@nking\vfill\fi}}% +} +\@ifundefined{emergencystretch} + {\newdimen\emergencystretch}{} +\def\setemergencystretch#1#2{% + \emergencystretch 4pt + \multiply\emergencystretch#1} + +\def\set@floatcmds{% + \let\@dblfloat\@dbflt + \def\end@dblfloat{\par + \vskip\z@\egroup + \ifnum\@floatpenalty<\z@ + \@cons\@deferlist\@currbox + \fi + \ifnum\@floatpenalty=-\@Mii + \@esphack + \fi}} +\def\kept@topmark{{}{}} +\let\kept@firstmark\@empty +\let\kept@botmark\@empty +\def\return@nonemptymark#1#2{% + \ifx#2\@empty + \else + \ifnum\c@tracingmulticols>\tw@ + \typeout{Returned #1 mark: \meaning#2}% + \nobreak + \fi + \mark{#2}% + \nobreak + \fi} +\def\get@keptmarks#1{% + \begingroup + \vbadness\@M + \setbox#1\copy#1% + \setbox#1\vsplit#1to\maxdimen + \set@keptmarks + \endgroup +} +\def\set@keptmarks{% + \ifx\kept@firstmark\@empty + \expandafter\gdef\expandafter + \kept@firstmark + \expandafter{\splitfirstmark}% + \ifnum\c@tracingmulticols>\tw@ + \ifx\kept@firstmark\@empty\else + \typeout{Set kept first mark: + `\meaning\kept@firstmark'}% + \fi + \fi + \fi + \expandafter\def\expandafter\@tempa + \expandafter{\splitbotmark}% + \ifx\@tempa\@empty\else + \global\let\kept@botmark\@tempa + \ifnum\c@tracingmulticols>\tw@ + \typeout{Set kept bot mark: + `\meaning\kept@botmark'}% + \fi + \fi}% +\def\prep@keptmarks{% + \if@boxedmulticols \else + \get@keptmarks\partial@page + \fi} +\def\remove@discardable@items{% + \unskip\unpenalty\unkern + \unskip\unpenalty\unkern + \unskip\unpenalty\unkern + \unskip\unpenalty\unkern +} +\endinput +%% +%% End of file `multicol.sty'. diff --git a/web/noweb/examples/primes.bbl b/web/noweb/examples/primes.bbl new file mode 100644 index 0000000000..6fbb9884a5 --- /dev/null +++ b/web/noweb/examples/primes.bbl @@ -0,0 +1,13 @@ +\begin{thebibliography}{1} + +\bibitem{knuth:literate} +Donald~E. Knuth. +\newblock Literate programming. +\newblock {\em The Computer Journal}, 27(2):97--111, 1984. + +\bibitem{dahl:structured} +Ole-Johan Dahl, Edsger~W. Dijkstra, and C.~A.~R. Hoare. +\newblock {\em Structured Programming}. +\newblock Academic Press, London and New York, 1972. + +\end{thebibliography} diff --git a/web/noweb/examples/primes.nw b/web/noweb/examples/primes.nw new file mode 100644 index 0000000000..d265b81b91 --- /dev/null +++ b/web/noweb/examples/primes.nw @@ -0,0 +1,186 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. + +% l2h substitution nw <tt>noweb</tt> +% some insanity is needed to avoid getting a double square bracket +% l2h substitution [ <b>[</b><b>[</b> +% l2h substitution ] <b>]</b><b>]</b> +\def\nw{{\tt noweb}} +\def\[{\ifhmode\ \fi$[\mkern-2mu[$} +\def\]{$]\mkern-2mu]$} + +\title{Printing Primes: An example of \nw} + +\section{Printing Primes: An example of \nw} +The following program is essentially the program that appears in +Reference~\cite{knuth:literate}, the first article on literate programming, +but rendered using \nw. +An important differents is the {\tt WEB} has macros, but \nw\ does not. +Knuth's program is itself essentially the same as Edsger Dijkstra's +``first example of step-wise program composition.''~\cite[pages +26--39]{dahl:structured}. + +Dijkstra's program prints a table of the first thousand prime numbers. +We shall begin as he did, by reducing the entire program to its +top-level description. + +<<*>>= +<<program to print the first thousand prime numbers>> +@ +\[Double brackets will be used in what follows to enclose comments +relating to \nw\ itself. +This definition of the root module could have been eliminated by +choosing to use +\begin{quote} +\tt notangle -R'program to print the first thousand prime numbers' +\end{quote} +to extract the program.\] +@ This program has no input, because we want to keep it simple. +The result of the program will be to produce a list of the first +thousand prime numbers, and this list will appear on the [[output]] +file. + +Since there is no input, we declare the value [[m = 1000]] as a +compile-time constant. +The program itself is capable of generating the first [[m]] prime +numbers for any positive [[m]], as long as the computer's finite +limitations are not exceeded. +<<program to print the first thousand prime numbers>>= +program `print_primes(output); + const m = 1000; + <<other constants of the program>> + var <<variables of the program>> + begin <<print the first [[m]] prime numbers>> + end. +@ + +\section{Plan of the program} +We shall proceed to fill out the rest of the program by making +whatever decisions seem easiest at each step; the idea will be to +strive for simplicity first and efficiency later, in order to see +where this leads us. +The final program may not be optimum, but we want it to be reliable, +well motivated, and reasonably fast. + +Let us decide at this point to maintain a table that includes all of +the prime numebrs that will be generated, and to sepaerate the +genreation problem from the printing problem. +<<print the first [[m]] prime numbers>>= +<<fill table [[p]] with the first [[m]] prime numbers>>; +<<print table [[p]]>> +@ +How should table [[p]] be represented? +Two possibilities suggest themselves: We could construct a +sifficiently large aray of boolean values in whith the $k$th entry is +[[true]] if and only if the number $k$ is prime; or we could build an +array of integers in which the $k$th entry is the $k$th prime number. +Let us choose the latter alternatice, by introducing an intereger +array called [[p[1..m]]]. +In the documentation below, the notation [[p[k]]] will refer to the +[[k]]th element of the array [[p]], while $p_k$ will refer to the +$k$th prime number. +If the program is correct [[p[k]]] will equal $p_k$ or it will not yet +have nbeen asigned any value. +<<variables of the program>>= +p: array [1..m] of integer; + { the first m prime numbers, in increasing order } +@ +\section{The output phase} +<<other constants of the program>>= +rr = 50; +cc = 4; +ww = 10; +<<variables of the program>>= +`page_number: integer; +`page_offset: integer; +`row_offset: integer; +c: 0..cc; +@ +<<print table [[p]]>>= +begin page_number := 1; page_offset := 1; +while page_offset <= m do + begin <<output a page of answers>>; + page_number := page_number + 1; + page_offset := page_poffset + rr * cc; + end; +end +<<output a page of answers>>= +begin write('The First '); +write(m:1); +write(' Prime Numbers --- Page '); +write(page_number:1); write_ln; write_ln; + { there's a blank line after the heading } +for row_offset := pages_offset to page_offset + rr - 1 + do <<output a line of answers>>; +page; +end +<<output a line of answers>>= +begin for c := 0 to cc - 1 do + if for_offset + c * rr <= m then + write(p[row_offset + c * rr]); +writeln; +end +@ +\section{Generating the primes} +<<fill table [[p]] with the first [[m]] prime numbers>>= +<<initialize the data structures>> +while k < m do + begin <<increase [[j]] until it is the next prime number>> + k := k + 1; p[k] := j; + end +<<variables of the program>>= +j: integer; { all primes <= j are in table p } +k: 0..m; { this many primes are in table p } +<<increase [[j]] until it is the next prime number>>= +repeat j := j + 2; + <<update variables that depend on [[j]]>>; + <<give to [[j_prime]] the meaning: [[j]] is a prime number>> +until j_prime +<<variables of the program>>= +`j_prime: boolean; +<<initialize the data structures>>= +j := 1; k := 1; p[1] := 2; +<<variables of the program>>= +`ord: 2..ord_max; + { the smallest index >= 2 such that p_ord squared > j } +`square: integer; + { square = p_ord squared } +<<initialize the data structures>>= +ord := 2; square := 9; +<<other constants of the program>>= +ord_max = 30; { p_ord_max squared must exceed p_m } +<<update variables that depend on [[j]]>>= +if j = square then + begin ord := ord + 1; + <<update variables that depend on [[ord]]>> + end +<<update variables that depend on [[ord]]>>= +square := p[ord] * p[ord]; { at this point ord <= k } +@ +\section{The inner loop} +<<give to [[j_prime]] the meaning: [[j]] is a prime number>>= +n := 2; j_prime := true; +while (n < ord) and j_prime do + begin <<if [[p[n]]] is a factor of [[j]], set [[j_prime := false]]>>; + n := n + 1; + end +<<variables of the program>>= +n: 2..ord_max; { runs from 2 to ord when testing divisibility } +<<variables of the program>>= +`mult: array [2..ord_max] of integer; + { runs through multiples of primes } +<<update variables that depend on [[ord]]>>= +mult[ord-1] := j; +<<if [[p[n]]] is a factor of [[j]], set [[j_prime := false]]>>= +while mult[n] < j do + mult[n] := mult[n] + p[n] + p[n]; +if mult[n] = j then j_prime := false; +@ +\section{Index} +\subsection{Chunks} +\nowebchunks +\subsection{Identifiers} +\nowebindex + +\bibliographystyle{unsrt} +\bibliography{web,cs,ramsey} diff --git a/web/noweb/examples/scanner.nw b/web/noweb/examples/scanner.nw new file mode 100644 index 0000000000..cb6372f8a4 --- /dev/null +++ b/web/noweb/examples/scanner.nw @@ -0,0 +1,444 @@ +% Norman Ramsey (nr@notecnirp) --- CS 320 + +\def\sizedboxit#1#2{\vtop{\vbox{\hrule\hbox{\vrule\kern #2% + \vtop{\vbox{\kern #2\hbox{#1}}\kern #2}\kern #2\vrule}}\hrule}} +\def\boxit#1{\sizedboxit{#1}{1pt}} +\def\token#1{\boxit{\strut\tt #1}} + +\setcounter{secnumdepth}{0} + +% l2h ignore boxit +% l2h argblock token tt +@ +In this assignment I print intermediate code. +<<grammatical C functions>>= +void print6 (Exp e) { + extern int yylineno; + printf("line %d:\n",yylineno); + if (e.type==error_type) + printf("# ERROR\n"); + else + printTree(e.tree,stdout); +} +<<grammatical C declarations>>= +void print6(Exp e); +@ +\section{The {\tt yacc} value stack} +Here are all the objects I use as synthesized attributes: +<<grammatical declarations>>= +%union { + char *string; + Type type; + Product product; + Symbol symbol; + Exp exp; + Explist explist; + int bit; +} +@ +\section{3. Vocabulary and representation} +\subsection{Representation of terminal symbols} +{\tt yacc} and {\tt lex} use integers to represent terminal +symbols (tokens). +Single-character tokens are represented by their ASCII codes. +Longer tokens are declared using {\tt yacc}'s [[%token]] declaration; +{\tt yacc} chooses an integer representation of each such token. +These representations are made available to the lexer via [[y.tab.h]]. +I use the standard trick from Kernighan and Pike ([[x.tab.h]] instead of +[[y.tab.h]]) to avoid remaking the lexer unecessarily. +<<lexical include files>>= +#include "x.tab.h" +@ +\subsection{1. Identifiers} +Since {\tt lex} is notoriously slow at using patterns to recognize + reserved words, I look up every identifier in a table of reserved words. +[[idcategory(id)]] returns the category of the reserved word [[id]] +when [[id]] is in fact a reserved word. +When [[id]] is not a reserved word, [[idcategory(id)]] returns [[IDENT]]. +<<lexical definitions>>= +letter [A-Za-z] +digit [0-9] +@ +The offensive [[<INITIAL>]] below has to do with handling comments (q.v.). +Both identifiers and reserved words +are saved in the string table and put on the {\tt yacc} value stack. +<<lexical rules>>= +<INITIAL>{letter}({letter}|{digit})* { yylval.string = strsave(yytext); + return idcategory(yytext); } +<<lexical C declarations>>= +extern char *strsave(char *s); +<<grammatical declarations>>= +%token IDENT +%type <string> IDENT +<<grammatical rules>>= +ident : IDENT + ; +<<grammatical declarations>>= +%type <string> ident +<<grammatical C declarations>>= +extern char yytext[]; +@ +\subsection{2. Numbers} +I have to use two different [[ScaleFactor]] definitions +so I can tell the difference between long and short reals. +Notice that the scale factor is {\em not} optional for the long real. +<<lexical rules>>= +<INITIAL>{digit}+|{digit}({hexDigit}*)[Hh] { yylval.string = strsave(yytext); + return INTEGER; } +<INITIAL>{digit}+"."{digit}*{EScaleFactor}? { yylval.string = strsave(yytext); + return REAL; } +<INITIAL>{digit}+"."{digit}*{DScaleFactor} { yylval.string = strsave(yytext); + return LONGREAL; } +<<grammatical declarations>>= +%token <string> INTEGER REAL LONGREAL +@ +I permit lower case where Wirth insists on upper case. +This will be convenient later on. +Besides, Hanson does it. + +I need parentheses around things like [[EScaleFactor]] because I'n not +really defining a regular expression---I'm using a macro facility. +<<lexical definitions>>= +hexDigit [0-9A-Fa-f] +EScaleFactor ([eE][+\-]?{digit}+) +DScaleFactor ([dD][+\-]?{digit}+) +@ +\subsection{3,4. Strings and character constants} +Single character strings [["x"]] become character constants, +not strings, thanks to the {\tt lex} disambiguation rules. +<<lexical rules>>= +<INITIAL>"\'"{nonquote}"\'" { yylval.string=strsave(yytext); return CHARCONST; } +<INITIAL>{digit}{hexDigit}*[Xx] { yylval.string=strsave(yytext); return CHARCONST; } +<INITIAL>"\""{nondoublequote}*"\"" { yylval.string=strsave(yytext); return STRING; } +<<grammatical declarations>>= +%token <string> CHARCONST STRING +@ +The character set for string literals is awkward, +because I want to include backslash escapes. +I use the ANSI standard backslash escapes from section A2.5.2 of +the second edition of Kernighan and Ritchie. +Because the lexical analyzer is probably not the right place to +handle illegal backslash escapes, I allow any reasonable +character to follow the backslash. +I define [[nonoctalx]] to be those characters +that can't start an octal or hexadecimal constant (when following +a backslash). +Then I can recognize octal and hexdecimal character constants like [["\012"]]. +I {\em don't} insist that at least one [[hexDigit]] follow [[\x]], +because again that should be handled downstream of the lexical analyzer. +<<lexical definitions>>= +plainnonquote [ \t\]\"!#$%&()*+,\-./0-9:;<=>?@A-Z[^_`a-z{|}~] +plainnondoublequote [ \t\]\'!#$%&()*+,\-./0-9:;<=>?@A-Z[^_`a-z{|}~] +escapedchar (\\({nonoctalx}|{octal}{octal}?{octal}?|x{hexDigit}*)) +nonoctalx [ \]\'\"!#$%&()*+,\-./89:;<=>?@A-Z[\\^_`a-wyz{|}~] +nonquote ({plainnonquote}|{escapedchar}) +nondoublequote ({plainnondoublequote}|{escapedchar}) +octal [0-7] +@ +I also need to handle strings that don't terminate. +When I see one, I gobble up the whole line on which it sits---that should +make it easier for the parser to recover. +(The alternative is trying to tokenize the characters following the open +quote.) +<<lexical rules>>= +<INITIAL>("\""{nondoublequote}*|"\'"{nonquote}*) <<complain; return bad string>> +@ +I print the first few characters of a nonterminated string, +followed by an ellipsis. +I return the string anyway because that way there's a chance that the parser +can just ignore the error. +<<complain; return bad string>>= +{ yyerror("Unterminated string %.8s%s",yytext,yyleng>8?"...":""); + yylval.string=strsave(""); + return STRING; +} +@ +I include prototypes for the string functions, to keep {\tt lcc -A} +from complaining about missing prototypes. +<<common include files>>= +#include <string.h> +@ +\subsection{5. Operators and delimiters} +Here are [[%token]] declarations for all the multicharacter tokens, +including the reserved words. +I use [[yyBEGIN]] because [[BEGIN]] means something special to {\tt lex}. +<<grammatical declarations>>= +%token ARROW INC DEC LSHIFT RSHIFT LEQ GEQ EQ NEQ AND OR + /* -> ++ -- @<< @>> <= >= == != && || */ +@ +I make sure the lexer always returns strings for identifiers +and reserved words. +@ +Recognizing the operators and delimiters is straightforward: +<<lexical rules>>= +<INITIAL>"->" return ARROW; +<INITIAL>"++" return INC; +<INITIAL>"--" return DEC; +<INITIAL>"<<" return LSHIFT; +<INITIAL>">>" return RSHIFT; +<INITIAL>"<=" return LEQ; +<INITIAL>">=" return GEQ; +<INITIAL>"==" return EQ; +<INITIAL>"!=" return NEQ; +<INITIAL>"&&" return AND; +<INITIAL>"||" return OR; +<INITIAL>[\]+!\-*/~&.,;|()[{}^=#<>:] return *yytext; +@ +\paragraph{Reserved word search} +Recall that, instead of having the {\tt lex}-generated automaton +recognize reserved words, I wanted to look up each identifier to see +if it is a reserved word. +I put the reserved words into an array and use binary search to find their +categories. +A word that isn't in the list has category [[IDENT]]. + +The list itself is excruciating to read. +I use a trick I got from Dave Hanson---I put the list in a header +file as calls to the [[kw]] (keyword) macro. +Then I include the header with appropriate macros wherever I need it. +<<list of reserved words>>= +kw(INT, "int") +@ +A binary search table of reserved words: +<<reserved word data structures>>= +static +struct reserved {char *s; int category;} +reservedwords[] = { +#define kw(VAL,STRING) {STRING,VAL}, +<<list of reserved words>> +#undef kw +}; +static int numreservedwords = (sizeof(reservedwords)/sizeof(struct reserved)); +@ +[[idcategory]] is just binary search. +<<lexical C functions>>= +<<reserved word data structures>> +static int idcategory (char *id) { + int low=0, high=numreservedwords-1, mid; + int compare; + while (low <= high) { + /* Invariant: if id is in the initial range low...high, + it is in the current range low...high */ + mid = (low+high)/2; /* note low <= mid <= high */ + compare = strcmp(id,reservedwords[mid].s); + if (compare>0) low = mid + 1; + else if (compare<0) high = mid - 1; + else return reservedwords[mid].category; + } + return IDENT; /* id is not a reserved word */ +} +<<lexical C declarations>>= +static int idcategory(char *); +@ +\subsection{Comments} +I use the standard trick of defining a special state just for comments. +A begin comment sends the lexer into state [[<COMMENT>]], and an +end comment returns it to state [[<INITIAL>]]. +All tokens that aren't comments are recognized only in state [[<INITIAL>]], +which explains the offensive [[<INITIAL>]] prepended to every rule. + +<<lexical definitions>>= +%S COMMENT +<<lexical rules>>= +<INITIAL>"/*" BEGIN COMMENT; +<COMMENT>"*/" BEGIN INITIAL; +<COMMENT>"\n" ; +<COMMENT>. ; +@ +\subsection{Whitespace and bad characters} +<<lexical rules>>= +<INITIAL>[ \t\n]+ ; /* ignore whitespace */ +<INITIAL>. <<complain about a bad character>> +@ +The error message we print is different for printable and nonprintable +characters. +<<complain about a bad character>>= +{ if (*yytext >= ' ' && *yytext < 0177) + yyerror("bad character `%c'", *yytext); + else yyerror("bad character `\\%03o'", *yytext); +} +@ +\section{8. Expressions} +\subsection{8.1 Operands (designators and constants)} +There are no qualified identifiers, so this simplifies the parsing +of designators. +It is a bit awkward to distinguish variables and parameters from +constant identifiers. +There is also an awkwardness with rvalues---boolean expressions +have to be converted from ``test'' to values using [[BOOL]]. +Following a suggestion of Hanson's, I use +three nonterminals: [[exp]] is an expression (possibly a test); +[[rvalue]] is an rvalue (never a test), and +[[lvalue]] is an lvalue. +I introduce [[complex_lvalue]] because I need to distinguish identifiers from +all other lvalues (otherwise I get a reduce-reduce conflict when converting +lvalues to expressions). +@ +<<grammatical declarations>>= +%type <exp> arraydes lvalue complex_lvalue rvalue exp +@ +Here are productions for all the C literals. +I use [[make_constval(type,string)]] to convert a string to a value of +the type desired. + +I issue warnings for long reals, since they aren't supported in Oberon/320. +<<grammatical rules>>= +exp : INTEGER { $$ = make_constval(integer_type,$1); } + | REAL { $$ = make_constval(real_type,$1); } + | LONGREAL { warning("Long reals not supported (replaced with zero)"); + $$ = make_constval(real_type,strsave("0.0")); } + | CHARCONST { $$ = make_constval(char_type,$1); } + | STRING { $$ = make_constval(build_array(0,stringchar_type),$1); } + ; +@ +\subsection{8.2 Operators} +I use {\tt yacc} precedence declarations. + +These declarations define precedence. +My task is much simplified because unary and binary [[-]] have +exactly the same precedence. +<<grammatical declarations>>= +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQ NEQ +%left '<' '>' LEQ GEQ +%left LSHIFT RSHIFT +%left '+' '-' +%left '*' '/' '%' +%right '!' '~' INC DEC /* unary operators */ +%left ARROW '.' +@ +[[binop]] checks types and generates intermediate code. +Consult the chapter on typechecking for the description of the +correct operation of [[binop]] and the meanings of +various permissions [[p_xxx]]. +<<grammatical rules>>= +complex_lvalue : lvalue '.' ident { $$ = find_field($1,$3); } + | exp ARROW ident { $$ = find_field(deref($1),$3) } + | '*' rvalue { $$ = deref($1); } + | arraydes ']' { $$ = $1; } + ; + +arraydes : rvalue '[' exp { $$ = subscript($1,$3); } + ; + +lvalue : complex_lvalue { $$ = $1; } + | ident { $$ = lookup_lvalue($1); } + ; + +exp : complex_lvalue { $$.type=$1.type; + $$.tree=tMEM($1.type->size,$1.tree); } + | ident { $$ = lookup_exp($1); } + ; + +rvalue : exp { $$ = boolval($1); } + ; +<<grammatical rules>>= +exp : exp EQ exp { $$ = binop(OSeq, $1,$3,boolean_type,p_equality); } + | exp NEQ exp { $$ = binop(OSneq,$1,$3,boolean_type,p_equality); } + | exp '<' exp { $$ = binop(OSlt, $1,$3,boolean_type,p_relational); } + | exp LEQ exp { $$ = binop(OSleq,$1,$3,boolean_type,p_relational); } + | exp '>' exp { $$ = binop(OSgt, $1,$3,boolean_type,p_relational); } + | exp GEQ exp { $$ = binop(OSgeq,$1,$3,boolean_type,p_relational); } + ; +exp : exp '+' exp { $$ = binop(OSplus, $1,$3,0,p_numeric); } + | exp '-' exp { $$ = binop(OSminus,$1,$3,0,p_numeric); } + | exp OR exp { $$ = binop(OSor, $1,$3,0,p_boolean); } + ; +exp : exp '*' exp { $$ = binop(OSmul, $1,$3,0,p_numeric); } + | exp AND exp { $$ = binop(OSand,$1,$3,0,p_boolean); } + ; +exp : '(' exp ')' { $$ = $2; } + ; +@ +\subsection{Function calls} +Calls to functions (procedures having a return type) +may occur {\em only} as factors in the production given below. +The +bottom of p.~678 of the Oberon report makes it clear that the~[[()]] are +required even if the function has no parameters. +<<not yet grammatical rules>>= +exp : ident ActualParameters { $$ = fcall($1,$2); } + ; + +ActualParameters: '(' ExpList ')' { $$ = $2; } + | '(' ')' { $$ = 0; } + ; + +ExpList : rvalue ',' ExpList { $$ = explist($1,$3); } + | rvalue { $$ = explist($1,0); } + ; +<<not yet grammatical declarations>>= +%type <explist> ActualParameters ExpList +@ +\section{11. Compilation units} +<<grammatical rules>>= +module : exp { compile($1); } +<<grammatical declarations>>= +%start module +@ +\section{Error recovery} +Here are some simple error productions that might help the parser continue. +The first four gobble up mangled declarations. +The last two are stabs in the dark; I hope they give the parser a chance +to recover from errors in statements and expressions. +<<grammatical rules>>= +exp : error { $$.type = error_type; } +@ +\section{Putting it all together} +Here are the necessary {\tt\#include} files: +<<common include files>>= +#include <assert.h> +#include <stdio.h> +#include "types.h" +#include "predef.h" +#include "tree.h" +#include "typecheck.h" +#include "codegen.h" +#include "symbol.h" +#include "declarations.h" +#include "constants.h" +#include "errors.h" +@ +There are no include files used exclusively by the parser. +This is because the lexer sees {\tt y.tab.h}, and so has to know everything +the parser knows. +<<grammatical include files>>= + +@ +This is boilerplate for every {\tt lex} specification ever written: +<<lexer>>= +%{ +<<common include files>> +<<lexical include files>> +<<lexical C declarations>> +%} +<<lexical definitions>> +%% +<<lexical rules>> +%% +<<lexical C functions>> +@ +And this is boilerplate for every {\tt yacc} specification ever written: +<<parser>>= +%{ +<<common include files>> +<<grammatical include files>> +<<grammatical C declarations>> +extern int yylex(void); +%} +<<grammatical declarations>> +%% +<<grammatical rules>> +%% +#define lint /* keep lcc from barking about no reference to yaccpar_sccsid */ +<<grammatical C functions>> +@ +\section{Indices} +\subsection{Chunks} +\nowebchunks +\subsection{Identifiers} +\nowebindex diff --git a/web/noweb/examples/sharpline b/web/noweb/examples/sharpline new file mode 100755 index 0000000000..5682c942b8 --- /dev/null +++ b/web/noweb/examples/sharpline @@ -0,0 +1,31 @@ +#!/bin/sh +# +# noweb filter to handle #line directives in noweb input +# +# sample usage: +# +# preprocessor files | notangle -L -filter sharpline - > result.c +# + +nawk ' +BEGIN { linestart = 1 } +/^(@nl|@index nl)$/ { linestart = 1; print; next } +/^@text #line +[0-9]+$/ { + if (linestart) + printf "@line %d\n", $3 + else + print + linestart = 0; next +} +/^@text #line +[0-9]+ +".*"$/ { + if (linestart) { + thisline = $3 + sub(/^@text #line +[0-9]+ +/, "") + thisfile = substr($0, 2, length($0) - 2) + printf "@file %s\n@line %d\n", thisfile, thisline + } else + print + linestart = 0; next +} +/^@(text |quote )/ { linestart = 0 } +{ print }' "$@" diff --git a/web/noweb/examples/test.nw b/web/noweb/examples/test.nw new file mode 100644 index 0000000000..c69da5a0ce --- /dev/null +++ b/web/noweb/examples/test.nw @@ -0,0 +1,17 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +<<*>>= +one <<two>> <<three>> # uses two and three +@ %def one +<<two>>= +first of two +second of two +third of two +@ %def fish fowl duck +@ %def two +<<three>>= +first of three + second of three + third of three +@ %def three + diff --git a/web/noweb/examples/tree.nw b/web/noweb/examples/tree.nw new file mode 100644 index 0000000000..43613fb431 --- /dev/null +++ b/web/noweb/examples/tree.nw @@ -0,0 +1,326 @@ +\section{Generating decision trees} + +The crux of the problem is to transform a {\em case statement} into a +{\em decision tree}. A case statement has a {\em value}, a sequence +of {\em arms}, and a {\em trailer}. +Each arm has a pattern, and code to be executed. +When the case statement is executed, it chooses the first arm whose +pattern matches the value, then executes the corresponding code, then +executes the trailer. +I generate a {\em decision tree} to do the job. +Each internal node of the decision tree tests a field of the value. +It then chooses an edge (child) based on that value, and continues +testing fields until it reaches a leaf, at which time it executes the +code associated with that leaf. + +The goal of tree generation is not to generate just any tree, but the +tree with the fewest nodes. This problem is NP-complete, so I apply +a few heuristics. The results, at least for the machine descriptions +I use, seem to be as good as what I would come up with by hand. +@ +The arms of the case statement have some extra information. +The file and line number help with error message and make it possible +to generate [[#line]] statements that identify the source of the code. +The original arm gives the arm from which the current arm is derived, +and is useful for many of the heuristics. +<<*>>= +record caserec(arms,valcode,trailer) + # case arms, code to compute value, trailing code +record arm(file, line, pattern, code, original) + # pattern and code are the content + # line, file, original(pattern) are used for error reporting +@ +Each node of the decision tree is associated with a particular case +statement. +Internal nodes have children, and a [[field]] which says which field +we decided to test on. The edges that point to the children record +the interval of values for the particular child. +Leaf nodes have a [[name]] that records the name of the pattern known +to match at that leaf node. +<<*>>= +record node(cs, children, field, name) + # case statement, list of edges to children, field chosen, pattern name + # (name field used to support name operator, assigned only to leaves) +record edge(node, lo, hi) + # node pointed to and lo and hi interval of field for this edge +@ +To create a decision tree, I begin with a node containing the full, +original case statement. I then use a ``work queue'' approach to check +each node and see if it needs to be split. +If no pattern matches the node, or if the first pattern always matches +(with a unique name), no further splitting needs to be done, and I +assign a name to the leaf.\footnote{If the name isn't used, I assign +the name [["-unused-"]], because that will make it easier to combine +nodes in the dagging phase.} +Otherwise, I split the node. +<<*>>= +procedure needs_splitting(n) + if *n.cs.arms = 0 then fail + p := n.cs.arms[1].pattern + name := \p.disjuncts[1].name | p.name + every d := !p.disjuncts do { + n := \d.name | p.name + if n ~=== name then return # different names, needs splitting + else if *d.constraints = 0 then fail # always matches, needn't split + } + return # pattern doesn't always match -> split +end + +procedure tree(cs) + static heuristics + initial heuristics := [leafarms, childarms, nomatch, childdisjuncts, branchfactor] + + root := node(cs) + work := [edge(root)] # work queue of edges (nodes) to be expanded + while n := get(work).node do + if needs_splitting(n) then { + <<split node [[n]] and add children to work queue>> + } else { + if *n.cs.arms = 0 then + n.name := "-NOMATCH-" + else if n.cs.arms[1].code ? find_id("name") then { + p := n.cs.arms[1].pattern + n.name := \p.disjuncts[1].name | \p.name | "-unnamed-" + } else + n.name := &null + if \mapnames then n.name := map(\n.name) + } + return root +end +@ +Splitting a node involves choosing a field, finding out which intervals +of values of that field are interesting, and creating a child node for +each such interval of values. The patterns in the case statement of the +child node reflect the knowledge of the value interval of the tested +field. + +I make the decision by splitting the node on {\em each} field +mentioned in the case statement. I then compute some heuristic +functions of the children from each splitting and use the +best-scoring field. + +Some debugging information may be written to [[hdebug]] or [[sdebug]]. +<<split node [[n]] and add children to work queue>>= +fields := mentions(n.cs) +*fields > 0 | impossible("internal node mentions no fields") +candidates := table() +every f := !fields do + candidates[f] := split(n, f) +<<if debugging, split all and report>> +*fields > 1 & write(\hdebug, "Choosing one of ", patimage(fields)) +every h := !heuristics do { + if *fields = 1 then break + fields := findmaxima(h, candidates, fields) +write(\hdebug, image(h), " chose ", patimage(fields)) +} +*fields > 0 | impossible("no fields") +*fields = 1 | write(\hdebug, "tie among fields", patimage(fields), " near ", + image(n.cs.arms[1].original.file), ", line ", + n.cs.arms[1].original.line) +work |||:= n.children := candidates[n.field := ?fields] +<<if debugging, split all and report>>= +if \tryall & \hdebug & *fields > 1 then { + write(\hdebug, repl("=",10), " Splitting ", repl("=", 10)) + every findmaxima(!heuristics, candidates, fields) do write(\hdebug) + write(\hdebug, repl("=", 30), "\n") +} +@ +To split a node, I look at each interval of values that might be +interesting. I apply that interval to the case statement, and if there +can be any match, I create and add a new child node. +<<*>>= +procedure split(n, f) + local vals,v,d,val,c,p,j,i,newd,cst,child,newp + + patterns := [] + children := [] + every put(patterns, (!n.cs.arms).pattern) + r := intervals(patterns, f) + <<if debugging, write about splitting this node>> + + every i := 1 to *r - 1 do + put(children, edge(node(apply(n.cs, f, r[i], r[i+1]),[]), r[i], r[i+1])) + + write(\sdebug, "Done splitting.\n") + return children +end +<<if debugging, write about splitting this node>>= +writes(\sdebug, "Splitting ") +outpattern(\sdebug, patterns[1]) +every i := 2 to *patterns do { writes(\sdebug, " | "); outpattern(\sdebug, patterns[i])} +write(\sdebug, " on ", f.name) +@ +So, what is the new case statement that results from applying +$\tt lo \le f < hi$ to [[cs]]? +For each arm, I match the pattern against the interval. +If it succeeds, I create a new arm for the new case statement, +containing the reduced pattern. +<<*>>= +procedure apply(cs, f, lo, hi) + result := copy(cs) + result.arms := [] + write(\sdebug, " Applying ", stringininterval(f.name, lo, hi)) + every a := !cs.arms do + put(result.arms, + arm(a.file, a.line, pmatch(a.pattern, f, lo, hi), a.code, a.original)) + if alwaysmatches(result.arms[1].pattern) then + result.arms := [result.arms[1]] + return result +end + +# if lo <= f < hi and p matches, return the new p + +procedure pmatch(p, f, lo, hi) + result := pattern([], p.name) + every d := !p.disjuncts do + if c := !d.constraints & c.field === f then # disjunct mentions f + if c.lo <= lo & hi <= c.hi then { # this constraint is matched + newd := disjunct([], d.name) + every c := !d.constraints & c.field ~=== f do + put(newd.constraints, c) + put(result.disjuncts, newd) + } else + c.hi <= lo | c.lo >= hi | impossible("bad intervals") + else # disjunct does not mention f + put(result.disjuncts, d) + <<if debugging, write about results of [[pmatch]]>> + if *result.disjuncts > 0 then return result +end + +<<if debugging, write about results of [[pmatch]]>>= +if *result.disjuncts > 0 then writes(\sdebug, " ===> ") & outpattern(\sdebug, p) +# else writes(\sdebug, " ") & outpattern(\sdebug, p) + +if *result.disjuncts > 0 then write(\sdebug, " matches") +# else write(\sdebug, " does not match") +@ +\subsection{Tree-minimization heuristics} +First, the boilerplate that takes a heuristic [[h]], candidate +splittings, and a set of fields, and returns the set of fields with +the largest score on [[h]]. +<<*>>= +procedure findmaxima(h, candidates, fields) + local max + S := [] + every f := !fields do { + score := h(candidates[f], f) + write(\hdebug,"Field ", f.name, " scores ", score, " on ", image(h)) + /max := score - 1 + if score > max then { + max := score + S := [f] + } else if score = max then + put(S, f) + } + return set(S) +end +@ +Here's a big pile of heuristics. +I'm not sure I've ever needed more than the first two, but they're +amusing and easy enough to write. +<<*>>= +# leafarms: prefer candidate with most arms that appear at leaf +# nodes. Each original arm counted only once. +# Not matching is also counted as an arm. + +procedure leafarms(children, f) + arms := set() + every n := (!children).node & *n.cs.arms > 0 do + if not needs_splitting(n) then + insert(arms, n.cs.arms[1].original) + return *arms + if *(!children).node.cs.arms = 0 then 1 else 0 +end + +# childarms: prefer the candidate with the fewest arms in children + +procedure childarms(children, f) + sum := 0 + every sum -:= *(!children).node.cs.arms + return sum +end + +# nomatch: if tied on leafarms and childarms, take candidate +# with real leaf in preference to nomatch leaf + +procedure nomatch(children, f) + return if *(!children).node.cs.arms = 0 then -1 else 0 +end + +# childdisjuncts: prefer the candidate with the fewest disjuncts in children + +procedure childdisjuncts(children, f) + sum := 0 + every sum -:= *(!(!children).node.cs.arms).pattern.disjuncts + return sum +end + +# branchfactor: prefer the candidate with the fewest children + +procedure branchfactor(children, f) + return - *children +end +@ +\subsection{Utility functions} +<<*>>= +# If f is to be used to split patterns, what intervals need to be considered? + +procedure intervals(patterns, f) + cuts := set([0, 2^(f.hi - f.lo)]) + every p := !patterns & d := !p.disjuncts & c := !d.constraints & c.field === f do + every insert(cuts, c.lo | c.hi) + return sort(cuts) +end + +# what fields are mentioned in a case statement? + +procedure mentions(cs) + result := set() + every a := !cs.arms & d := !a.pattern.disjuncts & c := !d.constraints do + insert(result, c.field) + return result +end + +# find_id: tab to and past identifier id, returning its position +# ignores quotes, comment brackets + +procedure find_id(id) + static notlnum + initial notlnum := ~ (&letters ++ &digits ++ '_') + tab(p := find(id)) & p = 1 | (move(-1) & any(notlnum) & move(1)) & + =id & pos(0) | any(notlnum) & suspend p +end +@ +\subsection{Tree checking} +Once the tree is generated, it's useful to check it for redundant arms +and for arms that never match. These checks will help users catch +mistakes in their specifications. Note that I must check the +``original'' arms; that's why they're there. +<<*>>= +procedure checktree(n) + originals := set() + every insert(originals, (!n.cs.arms).original) + deletematching(n, originals) + every a := !originals do + warning("No word matches pattern at ", image(a.file), ", line ", a.line) + if hasnomatch(n) then + warning("Case statement at ", image(n.cs.arms[1].file), ", line ", + n.cs.arms[1].line - 1, " doesn't cover all cases") + return n +end + +procedure deletematching(n, originals) + if *originals = 0 then return + else if *n.children > 0 then every deletematching((!n.children).node, originals) + else every delete(originals, (!n.cs.arms).original) +end + +procedure hasnomatch(n) + if *n.children > 0 then return hasnomatch((!n.children).node) + else if *n.cs.arms = 0 then return # found it +end +@ +\section{Indices} +\subsection{Chunks} +\nowebchunks +\subsection{Identifiers} +\nowebindex diff --git a/web/noweb/examples/wc.bbl b/web/noweb/examples/wc.bbl new file mode 100644 index 0000000000..363d36fbe7 --- /dev/null +++ b/web/noweb/examples/wc.bbl @@ -0,0 +1,14 @@ +\begin{thebibliography}{1} + +\bibitem{guntermann:cweb} +Klaus Guntermann and Joachim Schrod. +\newblock {{\tt WEB}} adapted to {C}. +\newblock {\em {TUG}boat}, 7(3):134--137, October 1986. + +\bibitem{knuth:literate:book} +Donald~E. Knuth. +\newblock {\em Literate Programming}, volume~27 of {\em Center for the Study of + Language and Information Lecture Notes}. +\newblock Leland Stanford Junior University, Stanford, California, 1992. + +\end{thebibliography} diff --git a/web/noweb/examples/wc.nw b/web/noweb/examples/wc.nw new file mode 100644 index 0000000000..16ec82e7bf --- /dev/null +++ b/web/noweb/examples/wc.nw @@ -0,0 +1,372 @@ +\makeatletter +\def\idxexample#1{\nwix@id@uses#1} +\makeatother + +\section{An example of {\tt noweb}} +The following short program illustrates the use of {\tt noweb}, +a low-tech tool for literate programming. +The purpose of the program is to provide a basis for +comparing {\tt WEB} and {\tt noweb}, so I have used a program that has +been published before; +the text, code, and presentation are taken +from~\cite[Chapter~12]{knuth:literate:book}. +The notable differences are: +\begin{itemize} +\item +When displaying source code, +{\tt noweb} uses different typography. +In particular, {\tt WEB} makes good use of multiple fonts +and the ablity to typeset mathematics, and it may use + mathematical symbols in place of C~symbols (e.g. +``$\land$'' for ``{\tt \&\&}''). +{\tt noweb} uses a single fixed-width font for code. +\item +{\tt noweb} can work with {\LaTeX}, and I have used {\LaTeX} in this example. +\item +{\tt noweb} has no numbered ``sections.'' +\ifx\wbn\undefined +When numbers are needed for cross-referencing, {\tt noweb} uses page +numbers. +\else +When numbers are needed for cross-referencing, {\tt noweb} ordinarily uses page +numbers. +\fi +If two or more chunks appear on a page, for example, page~24, they are +distinguished by appending a letter to the page number, for example, +24a or 24b. +\ifx\wbn\undefined\else +{\LaTeX} computes these page numbers, but since {\LaTeX} is not used +in the production of {\it IEEE Software}, this example uses +consecutive ``chunk numbers'' instead of page numbers. +\fi +\item +{\tt noweb} has no special support for macros. +In the sample program, I have used the chunk +``\LA{}Definitions~{\nwtagstyle{}\subpageref{NWwc.5-DefB-1}}\RA{}'' to hold +macro definitions. +\item +{\tt noweb} does not recognize C~identifier definitions automatically, +so I had to add a list of defined identifiers to each code chunk. +Because {\tt noweb} is language-independent, it must use a heuristic +to find uses of identifiers. +This heuristic can be fooled into finding false ``uses'' +in comments or string literals, such as the use of +{\tt status} in chunk~\subpageref{NWwc.5-DefB-1}. +\item +The {\tt CWEB} version of this program has semicolons following most uses +of \LA$\cdots$\RA{}. +{\tt WEB} needs the semicolon or its equivalent to make +its prettyprinting come out right. +Because it does not attempt prettyprinting, {\tt noweb} needs no semicolons. +\item +Both {\tt WEB} and {\tt noweb} write chunk cross-reference information in +{\footnotesize footnote} font below each code chunk, for example, +``{\footnotesize\let\nwcodecomment=\relax\nwused{\\{NWwc.5-TheG-1}}}'' +Unlike {\tt WEB}, {\tt noweb} also includes cross-reference +information for identifiers, for example, +``{\footnotesize Defines +{\let\nwcodecomment=\relax\idxexample{{file\_count}{file:uncount}}}}'' +This information is generated using the {\tt @~\%def} markings in the +{\tt noweb} source. +\end{itemize} + +\subsection{Counting words} +This example, based on a program by Klaus Guntermann and +Joachim Schrod~\cite{guntermann:cweb} and a program by Silvio Levy and +D. E. Knuth~\cite[Chapter~12]{knuth:literate:book}, presents the ``word count'' +program from {\sc Unix}, rewritten in {\tt noweb} to demonstrate +literate programming using {\tt noweb}. +The level of detail in this document is intentionally high, for +didactic purposes; many of the things spelled out here don't need to +be explained in other programs. + +The purpose of {\tt wc} is to count lines, words, and/or characters in +a list of files. +The number of lines in a file is the number of newline characters it +contains. +The number of characters is the file length in bytes. +A ``word'' is a maximal sequence of consecutive characters other than +newline, space, or tab, containing at least one visible ASCII code. +(We assume that the standard ASCII code is in use.) + +\bigskip +@ +Most literate C programs share a common structure. +It's probably a good idea to state the overall structure explicitly at +the outset, even though the various parts could all be introduced in +chunks named \LA{*}\RA{} if we wanted to add them piecemeal. + +Here, then, is an overview of the file {\tt wc.c} that is defined by +the {\tt noweb} program {\tt wc.nw}: +<<*>>= +<<Header files to include>> +<<Definitions>> +<<Global variables>> +<<Functions>> +<<The main program>> +@ +We must include the standard I/O definitions, since we want to send +formatted output to [[stdout]] and [[stderr]]. +<<Header files to include>>= +#include <stdio.h> +@ + +The [[status]] variable will tell the operating system if the run was +successful or not, and [[prog_name]] is used in case there's an error +message to be printed. +<<Definitions>>= +#define OK 0 + /* status code for successful run */ +#define usage_error 1 + /* status code for improper syntax */ +#define cannot_open_file 2 + /* status code for file access error */ +<<Global variables>>= +int status = OK; + /* exit status of command, initially OK */ +char *prog_name; + /* who we are */ +@ + +Now we come to the general layout of the [[main]] +function. +<<The main program>>= +main(`argc, `argv) + int argc; + /* number of arguments on UNIX command line */ + char **argv; + /* the arguments, an array of strings */ +{ + <<Variables local to [[main]]>> + prog_name = argv[0]; + <<Set up option selection>> + <<Process all the files>> + <<Print the grand totals if there were multiple files>> + exit(status); +} +@ + +If the first argument begins with a `{\tt-}', the +user is choosing the desired counts and specifying +the order in which they should be displayed. +Each selection is given by the +initial character (lines, words, or characters). +For example, `{\tt-cl}' would cause just the +number of characters and the number of lines to +be printed, in that order. + +We do not process this string now; we simply remember where it is. +It will be used to control the formatting at output time. +<<Variables local to [[main]]>>= +int file_count; + /* how many files there are */ +char *which; + /* which counts to print */ +<<Set up option selection>>= +which = "lwc"; + /* if no option is given, print 3 values */ +if (argc > 1 && *argv[1] == '-') { + which = argv[1] + 1; + argc--; + argv++; +} +file_count = argc - 1; +@ + +Now we scan the remaining arguments and try to open a file, if possible. +The file is processed and its statistics are given. +We use a [[do ... while]] loop because we should read from the standard +input if no file name is given. +<<Process all the files>>= +argc--; +do { + <<If a file is given, try to open [[*(++argv)]]; [[continue]] if unsuccessful>> + <<Initialize pointers and counters>> + <<Scan file>> + <<Write statistics for file>> + <<Close file>> + <<Update grand totals>> + /* even if there is only one file */ +} while (--argc > 0); +@ + +Here's the code to open the file. A special trick allows us to handle +input from [[stdin]] when no name is given. +Recall that the file descriptor to [[stdin]] is~0; that's what we use +as the default initial value. +<<Variables local to [[main]]>>= +int `fd = 0; + /* file descriptor, initialized to stdin */ +<<Definitions>>= +#define READ_ONLY 0 + /* read access code for system open */ +<<If a file is given, try to open [[*(++argv)]]; [[continue]] if unsuccessful>>= +if (file_count > 0 +&& (fd = open(*(++argv), READ_ONLY)) < 0) { + fprintf(stderr, + "%s: cannot open file %s\n", + prog_name, *argv); + status |= cannot_open_file; + file_count--; + continue; +} +<<Close file>>= +close(fd); +@ +We will do some homemade buffering in order to speed things up: +Characters will be read into the [[buffer]] array before we process +them. +To do this we set up appropriate pointers and counters. +<<Definitions>>= +#define buf_size BUFSIZ + /* stdio.h BUFSIZ chosen for efficiency */ +<<Variables local to [[main]]>>= +char buffer[buf_size]; + /* we read the input into this array */ +register char *ptr; + /* first unprocessed character in buffer */ +register char *buf_end; + /* the first unused position in buffer */ +register int c; + /* current char, or # of chars just read */ +int in_word; + /* are we within a word? */ +long word_count, line_count, char_count; + /* # of words, lines, and chars so far */ +<<Initialize pointers and counters>>= +ptr = buf_end = buffer; +line_count = word_count = char_count = 0; +in_word = 0; +@ + +The grand totals must be initialized to zero at the beginning of the +program. +If we made these variables local to [[main]], we would have to do this +initialization explicitly; however, C's globals are automatically +zeroed. (Or rather, ``statically zeroed.'') (Get it?) +<<Global variables>>= +long tot_word_count, tot_line_count, + tot_char_count; + /* total number of words, lines, chars */ +@ + +\vskip0pt plus3in\penalty-500\vskip0pt plus-3in +The present chunk, which does the counting that is {\tt wc}'s {\em +raison d'\^etre}, was actually one of the simplest to write. +We look at each character and change state if it begins or ends a word. +<<Scan file>>= +while (1) { + <<Fill [[buffer]] if it is empty; [[break]] at end of file>> + c = *ptr++; + if (c > ' ' && c < 0177) { + /* visible ASCII codes */ + if (!in_word) { + word_count++; + in_word = 1; + } + continue; + } + if (c == '\n') line_count++; + else if (c != ' ' && c != '\t') continue; + in_word = 0; + /* c is newline, space, or tab */ +} +@ + +Buffered I/O allows us to count the number of characters almost for +free. +<<Fill [[buffer]] if it is empty; [[break]] at end of file>>= +if (ptr >= buf_end) { + ptr = buffer; + c = read(fd, ptr, buf_size); + if (c <= 0) break; + char_count += c; + buf_end = buffer + c; +} +@ + +It's convenient to output the statistics by defining a new function +[[wc_print]]; then the same function can be used for the totals. +Additionally we must decide here if we know the name of the file we have +processed or if it was just [[stdin]].{\hfuzz=11.1pt\par} +<<Write statistics for file>>= +wc_print(which, char_count, word_count, + line_count); +if (file_count) + printf(" %s\n", *argv); /* not stdin */ +else + printf("\n"); /* stdin */ +@ +<<Update grand totals>>= +tot_line_count += line_count; +tot_word_count += word_count; +tot_char_count += char_count; +@ + +We might as well improve a bit on {\sc Unix}'s {\tt wc} by displaying +the number of files too. +<<Print the grand totals if there were multiple files>>= +if (file_count > 1) { + wc_print(which, tot_char_count, + tot_word_count, tot_line_count); + printf(" total in %d files\n", file_count); +} +@ + +Here now is the function that prints the values according to the +specified options. +The calling routine is supposed to supply a newline. +If an invalid option character is found we inform the user about proper +usage of the command. +Counts are printed in 8-digit fields so that they will line up in +columns. +<<Definitions>>= +#define print_count(n) printf("%8ld", n) +<<Functions>>= +wc_print(which, char_count, word_count, line_count) + char *which; /* which counts to print */ + long char_count, word_count, line_count; + /* given totals */ +{ + while (*which) + switch (*which++) { + case 'l': print_count(line_count); + break; + case 'w': print_count(word_count); + break; + case 'c': print_count(char_count); + break; + default: + if ((status & usage_error) == 0) { + fprintf(stderr, + "\nUsage: %s [-lwc] [filename ...]\n", + prog_name); + status |= usage_error; + } + } +} +@ + +Incidentally, a test of this program against the system {\tt wc} +command on a SPARCstation showed that the ``official'' {\tt wc} was +slightly slower. +Furthermore, although that {\tt wc} gave an appropriate error message +for the options `{\tt-abc}', it made no complaints about the options +`{\tt-labc}'! +Dare we suggest that the system routine might have been better if its +programmer had used a more literate approach? +@ + +\section*{List of code chunks} +This list is generated automatically. +The numeral is that of the first definition of the chunk. +\nowebchunks +%\begin{multicols}{2}[ +\section*{Index} +Here is a list of the identifiers used, and where they appear. +Underlined entries indicate the place of definition. +This index is generated automatically. +%] +\nowebindex +%\end{multicols} + diff --git a/web/noweb/examples/wc.nw2html b/web/noweb/examples/wc.nw2html new file mode 100644 index 0000000000..6067d7768c --- /dev/null +++ b/web/noweb/examples/wc.nw2html @@ -0,0 +1,370 @@ +<html> +<HEAD> +<title>An Example of noweb</title></HEAD> +<BODY> + +<H1>An Example of noweb</H1> +<address>Norman Ramsey<br> +Dept. of Computer Science, Princeton University<br> +Princeton, NJ 08544 +</address> + +<h2><a name="contents">Contents</a></h2> +<ul> +<li><a href="#intro">Introduction</a> +<li><a href="#counting">Counting words</a> +<li><a href="#index">Index</a> +</ul> + +<h2><a name="intro">Introduction</a></h2> + +The following short program illustrates the use of <tt>noweb</tt>, +a low-tech tool for literate programming. +The purpose of the program is to provide a basis for +comparing <tt>WEB</tt> and <tt>noweb</tt>, so I have used a program that has +been published before; +the text, code, and presentation are taken +from Chapter 12 of D. E. Knuth, +<cite>Literate Programming</cite> +(volume 27 of <cite>Center for the Study of +Language and Information Lecture Notes</cite>, +Stanford Univ., 1992).<p> + +The notable differences are: +<ul> +<li> +When displaying source code, +<tt>noweb</tt> uses different typography. +In particular, <tt>WEB</tt> makes good use of multiple fonts +and the ablity to typeset mathematics, and it may use + mathematical symbols in place of C symbols (e.g. +a logical ``and'' symbol for ``[[&&]]''). +<tt>noweb</tt> uses a single fixed-width font for code. +<li> +<tt>noweb</tt> can work with HTML, and I have used HTML in this example. +<li> +<tt>noweb</tt> has no numbered ``sections.'' +When cross-referencing is needed, <tt>noweb</tt> uses hypertext links or page +numbers. +<li> +<tt>noweb</tt> has no special support for macros. +In the sample program, I have used +a ``Definitions'' chunk to hold +macro definitions. +<li> +<tt>noweb</tt>'s index of identifiers is less accurate than <tt>WEB</tt>'s, +because it uses a language-independent heuristic to find identifiers. +This heuristic may erroneously find ``uses'' of identifiers +in string literals or comments. +Although <tt>noweb</tt> does have a language-dependent algorithm for finding +definitions of identifiers, that algorithm is less reliable than <tt>CWEB</tt>'s, +because <tt>noweb</tt> does not really parse C code. +<li> +The <tt>CWEB</tt> version of this program has semicolons following most uses +of <...>. +<tt>WEB</tt> needs the semicolon or its equivalent to make +its prettyprinting come out right. +Because it does not attempt prettyprinting, <tt>noweb</tt> needs no semicolons. +</ul> + +<h2><a name="counting">Counting words</a></h2> + +This example, based on a program by Klaus Guntermann and +Joachim Schrod (`<tt>WEB</tt> adapted to C.' +<cite>TUGboat</cite> <b>7</b>(3):134-7, Oct. 1986) +and a program by Silvio Levy and +D. E. Knuth (Ch. 12 of <cite>Literate Programming</cite>), +presents the ``word count'' +program from Unix, rewritten in <tt>noweb</tt> to demonstrate +literate programming using <tt>noweb</tt>. +The level of detail in this document is intentionally high, for +didactic purposes; many of the things spelled out here don't need to +be explained in other programs.<p> + +The purpose of <tt>wc</tt> is to count lines, words, and/or characters in +a list of files. +The number of lines in a file is the number of newline characters it +contains. +The number of characters is the file length in bytes. +A ``word'' is a maximal sequence of consecutive characters other than +newline, space, or tab, containing at least one visible ASCII code. +(We assume that the standard ASCII code is in use.)<p> + +Most literate C programs share a common structure. +It's probably a good idea to state the overall structure explicitly at +the outset, even though the various parts could all be introduced in +chunks named <*> if we wanted to add them piecemeal.<p> + +Here, then, is an overview of the file <tt>wc.c</tt> that is defined by +the <tt>noweb</tt> program <tt>wc.nw</tt>: +<<*>>= +<<Header files to include>> +<<Definitions>> +<<Global variables>> +<<Functions>> +<<The main program>> +@ +We must include the standard I/O definitions, since we want to send +formatted output to [[stdout]] and [[stderr]]. +<<Header files to include>>= +#include <stdio.h> +@ + +The [[status]] variable will tell the operating system if the run was +successful or not, and [[prog_name]] is used in case there's an error +message to be printed. +<<Definitions>>= +#define OK 0 + /* status code for successful run */ +#define usage_error 1 + /* status code for improper syntax */ +#define cannot_open_file 2 + /* status code for file access error */ +@ %def OK usage_error cannot_open_file +<<Global variables>>= +int status = OK; + /* exit status of command, initially OK */ +char *prog_name; + /* who we are */ +@ %def status prog_name + +Now we come to the general layout of the [[main]] +function. +<<The main program>>= +main(argc, argv) + int argc; + /* number of arguments on UNIX command line */ + char **argv; + /* the arguments, an array of strings */ +{ + <<Variables local to [[main]]>> + prog_name = argv[0]; + <<Set up option selection>> + <<Process all the files>> + <<Print the grand totals if there were multiple files>> + exit(status); +} +@ %def main argc argv + +If the first argument begins with a `[[-]]', the +user is choosing the desired counts and specifying +the order in which they should be displayed. +Each selection is given by the +initial character (lines, words, or characters). +For example, `[[-cl]]' would cause just the +number of characters and the number of lines to +be printed, in that order.<p> + +We do not process this string now; we simply remember where it is. +It will be used to control the formatting at output time. +<<Variables local to [[main]]>>= +int file_count; + /* how many files there are */ +char *which; + /* which counts to print */ +@ %def file_count which +<<Set up option selection>>= +which = "lwc"; + /* if no option is given, print 3 values */ +if (argc > 1 && *argv[1] == '-') { + which = argv[1] + 1; + argc--; + argv++; +} +file_count = argc - 1; +@ + +Now we scan the remaining arguments and try to open a file, if possible. +The file is processed and its statistics are given. +We use a [[do ... while]] loop because we should read from the standard +input if no file name is given. +<<Process all the files>>= +argc--; +do { + <<If a file is given, try to open [[*(++argv)]]; [[continue]] if unsuccessful>> + <<Initialize pointers and counters>> + <<Scan file>> + <<Write statistics for file>> + <<Close file>> + <<Update grand totals>> + /* even if there is only one file */ +} while (--argc > 0); +@ + +Here's the code to open the file. A special trick allows us to handle +input from [[stdin]] when no name is given. +Recall that the file descriptor to [[stdin]] is 0; that's what we use +as the default initial value. +<<Variables local to [[main]]>>= +int fd = 0; + /* file descriptor, initialized to stdin */ +@ %def fd +<<Definitions>>= +#define READ_ONLY 0 + /* read access code for system open */ +@ %def READ_ONLY +<<If a file is given, try to open [[*(++argv)]]; [[continue]] if unsuccessful>>= +if (file_count > 0 +&& (fd = open(*(++argv), READ_ONLY)) < 0) { + fprintf(stderr, + "%s: cannot open file %s\n", + prog_name, *argv); + status |= cannot_open_file; + file_count--; + continue; +} +<<Close file>>= +close(fd); +@ +We will do some homemade buffering in order to speed things up: +Characters will be read into the [[buffer]] array before we process +them. +To do this we set up appropriate pointers and counters. +<<Definitions>>= +#define buf_size BUFSIZ + /* stdio.h BUFSIZ chosen for efficiency */ +@ %def buf_size +<<Variables local to [[main]]>>= +char buffer[buf_size]; + /* we read the input into this array */ +register char *ptr; + /* first unprocessed character in buffer */ +register char *buf_end; + /* the first unused position in buffer */ +register int c; + /* current char, or # of chars just read */ +int in_word; + /* are we within a word? */ +long word_count, line_count, char_count; + /* # of words, lines, and chars so far */ +@ %def buffer ptr buf_end in_word word_count line_count char_count +<<Initialize pointers and counters>>= +ptr = buf_end = buffer; +line_count = word_count = char_count = 0; +in_word = 0; +@ + +The grand totals must be initialized to zero at the beginning of the +program. +If we made these variables local to [[main]], we would have to do this +initialization explicitly; however, C's globals are automatically +zeroed. (Or rather, ``statically zeroed.'') (Get it?) +<<Global variables>>= +long tot_word_count, tot_line_count, + tot_char_count; + /* total number of words, lines, chars */ +@ + +The present chunk, which does the counting that is <tt>wc</tt>'s +<i>raison d'etre</i>, was actually one of the simplest to write. +We look at each character and change state if it begins or ends a word. +<<Scan file>>= +while (1) { + <<Fill [[buffer]] if it is empty; [[break]] at end of file>> + c = *ptr++; + if (c > ' ' && c < 0177) { + /* visible ASCII codes */ + if (!in_word) { + word_count++; + in_word = 1; + } + continue; + } + if (c == '\n') line_count++; + else if (c != ' ' && c != '\t') continue; + in_word = 0; + /* c is newline, space, or tab */ +} +@ + +Buffered I/O allows us to count the number of characters almost for +free. +<<Fill [[buffer]] if it is empty; [[break]] at end of file>>= +if (ptr >= buf_end) { + ptr = buffer; + c = read(fd, ptr, buf_size); + if (c <= 0) break; + char_count += c; + buf_end = buffer + c; +} +@ + +It's convenient to output the statistics by defining a new function +[[wc_print]]; then the same function can be used for the totals. +Additionally we must decide here if we know the name of the file we have +processed or if it was just [[stdin]]. +<<Write statistics for file>>= +wc_print(which, char_count, word_count, + line_count); +if (file_count) + printf(" %s\n", *argv); /* not stdin */ +else + printf("\n"); /* stdin */ +@ +<<Update grand totals>>= +tot_line_count += line_count; +tot_word_count += word_count; +tot_char_count += char_count; +@ + +We might as well improve a bit on Unix's <tt>wc</tt> by displaying +the number of files too. +<<Print the grand totals if there were multiple files>>= +if (file_count > 1) { + wc_print(which, tot_char_count, + tot_word_count, tot_line_count); + printf(" total in %d files\n", file_count); +} +@ + +Here now is the function that prints the values according to the +specified options. +The calling routine is supposed to supply a newline. +If an invalid option character is found we inform the user about proper +usage of the command. +Counts are printed in 8-digit fields so that they will line up in +columns. +<<Definitions>>= +#define print_count(n) printf("%8ld", n) +@ %def print_count +<<Functions>>= +wc_print(which, char_count, word_count, line_count) + char *which; /* which counts to print */ + long char_count, word_count, line_count; + /* given totals */ +{ + while (*which) + switch (*which++) { + case 'l': print_count(line_count); + break; + case 'w': print_count(word_count); + break; + case 'c': print_count(char_count); + break; + default: + if ((status & usage_error) == 0) { + fprintf(stderr, + "\nUsage: %s [-lwc] [filename ...]\n", + prog_name); + status |= usage_error; + } + } +} +@ %def wc_print + +Incidentally, a test of this program against the system <tt>wc</tt> +command on a SPARCstation showed that the ``official'' <tt>wc</tt> was +slightly slower. +Furthermore, although that <tt>wc</tt> gave an appropriate error message +for the options `[[-abc]]', it made no complaints about the options +`[[-labc]]'! +Dare we suggest that the system routine might have been better if its +programmer had used a more literate approach? + +<h2><a name=index>Index</a></h2> +<h3>Chunks +<nowebchunks> +<h3>Indifiers +<nowebindex> +</body> +</html> diff --git a/web/noweb/examples/whitespace b/web/noweb/examples/whitespace new file mode 100644 index 0000000000..5ac6c1d0f9 --- /dev/null +++ b/web/noweb/examples/whitespace @@ -0,0 +1,10 @@ + +# whitespace -- noweb filter to make multiple whitespace +# characters equivalent to a single space, so that +# << Hello World>>, <<Hello World>>, +# and <<Hello World >> all refer to the chunk +# <<Hello World>> + +sed -e '/^@use /s/[ \t][ \t]*/ /g' -e '/^@defn /s/[ \t][ \t]*/ /g' \ + -e '/^@use /s/[ \t]*$//g' -e '/^@defn /s/[ \t]*$//g' + diff --git a/web/noweb/nwsrcfilter.icn b/web/noweb/nwsrcfilter.icn new file mode 100644 index 0000000000..b1b1c0491b --- /dev/null +++ b/web/noweb/nwsrcfilter.icn @@ -0,0 +1,60 @@ +# nwsrcfilter -- filter out irrelevant files, put .nw files first + +procedure main(args) + while line := read() do + if not (line[-1] == "~" | line == "." | find("/RCS", line) | + (line ? (tab(find("./binaries/")), move(1))) | + line[3] == line[-1] == "#" | line == "./nwsrcfilter") then + line ? (="./" & save_file(tab(0))) + dump_files() +end + +global root + +global files_in_input + +procedure save_file(name) + initial {root := table(); files_in_input := set()} + insert(files_in_input, name) + name ? do_save(root) + return +end + +procedure do_save(root) + if key := tab(upto('/')) & tab(many('/')) then { + /root[key] := table() + do_save(root[key]) + } else + /root[tab(0)] := table() +end + +procedure dump_files(prefix, r) + /prefix := "" + /r := root + if *r = 0 then return + l := sort(r) + bubblenw(l) + every p := !l do + if *p[2] > 0 then { + dump_files(prefix || p[1] || "/", p[2]) + if member(files_in_input, prefix || p[1]) then write(prefix, p[1]) + # only dump directories if they were given explicitly + } else { + write(prefix, p[1]) + } +end + +procedure bubblenw(l) + lo := 1 # first non-nw + while l[lo][1][-3:0] == ".nw" do lo +:= 1 + hi := lo +1 # first succeeding nw + while l[hi][1][-3:0] ~== ".nw" do hi +:= 1 + while hi <= *l do { + i := hi + while i > lo do {l[i] :=: l[i-1]; i -:= 1} + lo +:= 1 + hi +:= 1 + while l[hi][1][-3:0] ~== ".nw" do hi +:= 1 + } + return l +end diff --git a/web/noweb/src/COPYRIGHT b/web/noweb/src/COPYRIGHT new file mode 100644 index 0000000000..48c98d1f17 --- /dev/null +++ b/web/noweb/src/COPYRIGHT @@ -0,0 +1,21 @@ +Noweb is copyright 1989-2000 by Norman Ramsey. All rights reserved. + +Noweb is protected by copyright. It is not public-domain +software or shareware, and it is not protected by a ``copyleft'' +agreement like the one used by the Free Software Foundation. + +You may use and distribute noweb for any purpose, for free. You may +redistribute noweb in whole or in part provided you acknowledge its +source and include this COPYRIGHT file. You may modify noweb and +create derived works, provided you retain this copyright notice, but +the result may not be called noweb without my written consent. + +You may sell noweb if you wish. For example, you may sell a CD-ROM +including noweb. + +You may use noweb in products as long as you charge for only those +components that are entirely your own and you acknowledge the use of +noweb clearly in all product documentation and distribution media. You +must state clearly that your product uses or is based on noweb and +that noweb is available free of change. You must also request that +bug reports on your product be reported to you. diff --git a/web/noweb/src/FAQ b/web/noweb/src/FAQ new file mode 100644 index 0000000000..33304a1503 --- /dev/null +++ b/web/noweb/src/FAQ @@ -0,0 +1,501 @@ +Noweb Frequently Asked Questions + + • Problems Building Noweb + • Indentation of code, especially FORTRAN + • Formatting the printed documentation + • Microsoft Madness + • Noweb and Emacs + • Other Questions + +Problems Building Noweb + +Q. The build fails because it can't find notangle. What am I doing wrong? + + A. Your source files have the wrong timestamps. The noweb files are + carefully distributed so that the C code is more recent than the literate + source code. Retrieving that noweb distribution as a compressed tar file + preserves these stamps. The CTAN sites will create a compressed tar file on + the fly for any directory. Your best bet is to get the tar file, unpack it, + and try again, because your abortive attempt will probably have stomped on + a C file that you need. + + I have reports that the former CTAN site ftp.shsu.edu fouled the + timestamps. Don't use the site! If you get bad timestamps, you can try + `make touch' as soon as you unpack the distribution, which should touch all + of the derived files. This trick won't work on NeXT systems, which have a + strange idea of 'touch'. + +Q. The system builds OK, but when I try to run noweave, I get an error in /usr/ +public/pkg/noweb/lib/totex (or some similar place) complaining about being out +of environment space or something equally unpleasant. + + A. The awk versions of the noweb scripts pass the awk programs as arguments + on the command line. Some shells or systems are too broken to handle a real + awk program on the command line. This problem is one of many reasons that + the awk versions are officially deprecated. The Icon versions will make you + happy, but if you prefer, you can edit the offending scripts so that the + awk programs reside in separate files and the scripts call awk -f. + +Q. I'm unable to run noweb on Windows NT 4.0. The problem is that the Icon +programs use a DOS extender which doesn't work in an NT console. + + A. Clint Jeffery, the Icon Master for Windows, says to use the NT console + binaries (nticont.exe and nticonx.exe) rather than wiconx.exe with its faux + console. Only applications that use the graphics facilities require + wiconx.exe and its kin. The nticonx* binaries should run fine on Win95 + also. + +Q. I'm having trouble getting noweb to work on Windows XP. + + A. Bad news. There used to be a distribution from literateprogramming.org, + but as of January 2010, that domain is no longer registered, and I don't + know where the distribution went. + +Indentation of code, especially FORTRAN + +Q. Has anyone devised a clean way of handling FORTRAN 77 using a litprog tool +such as noweb? If I have the following: + +<<Block>>= + <<Nicely Indented Chunk Title>> +@ +<<Nicely Indented Chunk Title>>= +C A comment in a chunk! + Z = X * + + Y +@ + +I'll get the "C" in column 7, and the continuation character "+" in column 12, +neither of which is desirable. + + A. As noted on the man page, notangle's -L option suppresses the usual + behavior with respect to indentation. Thus, a command like + + notangle -L'%N' pgm.nw > pgm.f + + should do the job. + +Q. I noticed the -L option to notangle turns off the usual indentation. Is +there an easy way to get both -L and indentation? + + A. No. It was my design decision that -L should preserve the column numbers + in the original source code. This behavior is extremely useful in cases + where a compiler reports an error in the source by line and column number. + Of course, this decision precludes changing the indentation of the input. + +Formatting the printed documentation + +Q. There's too much white space at the bottom of the pages, especially if a +code chunk is followed by a section. How do I fix it? + + A. Noweb is set up never to break chunks across pages. You can get better + results by relaxing this constraint, e.g., by + + \def\nwendcode{\endtrivlist \endgroup \vfil\penalty10\vfilneg} + \let\nwdocspar=\smallbreak + + or the even less liberal + + \def\nwendcode{\endtrivlist \endgroup} + \let\nwdocspar=\par + +Q. In a noweb source file, what is the difference between a line beginning with +"@" followed by a space and some text versus a line with nothing but "@"? + + A. A line with nothing but @ begins the `documentation chunk' with a blank + line. TeX thinks blank lines are new paragraphs and good places to put page + breaks. By default, TeX thinks that if a new documentation chunk starts + with a new paragraph, this is an especially good place for a page break. + +Q. I know how to switch between code and documentation via <<*>>= and @, but +sometimes, after a <<*>>= section, automatically a new page is started, even if +I do not want this but prefer to go on with commenting directly after the +inserted programm code. How can I avoid this? + + A. The @ sign you use to switch back to documentation should not appear on + a line by itself, but should be followed by a blank space and some text. + You should then put a bare @ sign on a line by itself at the next place + that is suitable for a page break. + +Q. I don't quite understand noweb's default page breaking. + +Here is some text +that leads up to a chunk +<<chunk>>= + a = b; + c = d; +@ and here is some more text. +Sometimes this appears on a new page even though +there is plenty of room on this page. Can I fix it by adding +the following line (which I haven't been doing) ? +@ +New paragraph here. + + A. Sad but true. Perhaps a future version of noweb will provide better + facilities for controlling page breaking. + +Q. I ran into a problem when trying to use several files in a single noweb +project. In my project, I have a LaTeX master file (say, doc.tex) that \ +include's three others: say, a.tex, b.tex, and copyright.tex. These later files +are generated from a.nw, b.nw, and copyright.nw, respectively, using the +command "noweave -n -index" I really want to keep these noweb files separated. +In the code chunks appearing in both a.nw and b.nw a copyright notice chunk +(defined in copyright.nw) is included. + +Tangling works fine, provided that copyright.nw appears in the notangle +command. Problems arise with noweave. Copyright chunks appear as "(never +defined)" in a.tex and b.tex, what is obvious, because I cannot use +copyright.nw for weaving a.nw or b.nw (otherwise the copyright notice chunk +would appear several times in my printed document). + +Does someone know how to solve this problem? + + A. This is a form of question that arises repeatedly, which is to say, + ``what is the scope of chunk names?'' In Noweb, chunk names are in the same + scope if the files in which the chunks appear are used on the same command + line. Thus, the normal rule is one Noweb file per command line, and + therefore each file is its own scope. + + You want to have chunks in the same scope for purposes of tangling, but not + to weave files together. If you want files a.nw and b.nw in separate + scopes, the situation is hopeless, because ``being in the same scope'' has + to be a transitive property, and if a.nw and b.nw are both in the same + scope as copyright.nw, they must be in the same scope as each other. + + However, if you want all files to be in the same scope, there is a + solution, although it is a bit ugly. The solution is to weave all the files + together, then use a special back end to split them into separate .tex + files. This back end, called scopehack, could be used as follows: + + noweave -n -index -backend scopehack *.nw + + and this results in multiple *.tex files as required. + +Q. I have problems using LaTeX \ifthenelse with noweb--- code chunks inside an +\ifthenelse are not typeset correctly and a %def directive causes (La)TeX +groups to be unbalanced. + + A. Noweb code chunks are ``moving arguments'' (like \verb) and can't be + arguments to macros. I suggest you combine \ifthenelse with the noweb + 'elide' filter to remove the offending code chunks. + +Q. I embed the build instructions for a noweb-based project in a code chunk +called build-script that I don't wish to have show up in the printed +documentation. So, I surround the section with LaTeX \iffalse ... \fi. If I do +this at the same time as using noweb's index, the \fi shows up in the generated +TeX file at the end of the index information, resulting in an empty index. + + A. Make sure that the \fi appears before the last documentation chunk, by + creating an empty documentation chunk (with @) if necessary. Then use + noweave -delay to insert the index information. + + Incidentally, it is recommended that, in addition to using \iffalse ... \ + fi, you remove unwanted code chunks by using the elide filter provided with + noweb. Latex can get confused if chunk cross-reference anchors are hidden + away inside \iffalse ... \fi. + +Q. When I write C code and use the decrement operator --, it appears in the +resulting document as a single -. This unpleasantness seems to be a side effect +of \usepackage[T1]{fontenc}. + + A. Indeed. We don't understand this behavior, but as an ill-understood + workaround, you can add + + \catcode`\-=\active + + to \def\setupcode, and things may work better. + +Microsoft Madness + +Q. Using the precompiled MS-DOS and Windows binaries for noweb, many of the +programs throw their output to the screen and not to whatever receiving program +/file they ought to go to. A typical example: I go to /noweb/examples and type + + notangle wc.nw > wc.c + +I see the tangled program scroll by on the screen, but it isn't piped to the +file wc.c as requested. Any solutions? + + A. This is a known COMMAND.COM problem. Try: + + command /c notangle wc.nw > wc.c + + or get a COMMAND.COM replacement such as 4DOS. Better yet, help with the + noweb 3 port. + +Q. For Windows 95/NT systems: Why do I get the error message "This program +cannot be run in DOS mode." when I run some of the programs (including +noweave.bat)? + + A. [Thanks to Chris Harris.] The problem is that some of the files used in + the noweb system (including totex.exe and tohtml.exe used by noweave.bat) + are written using an old version of icon that uses a DOS extender which is + incompatible with 95/NT. The solution is to rebuild all of these files that + were built using icon source: + 1. Go get the console ICON executables for use on 95/NT. They are the + files nticont.exe, nticonx.exe, noop.bat. Put them in your noweb/bin + directory. + 1. The other two were available on the noweb ftp site, from the FAQ, + but noop.bat wasn't... it's availbe via FTP with the icon + distribution, but it's pretty easy to build yourself, it's contents + are the single line: + + @rem this file prevents further batch processing after launching nticonx + + 2. So to build noop.bat, just type at the prompt "copy con noop.bat" + and then paste in the above line (starting with the @) hit enter + and ^Z and you're good to go. Note: This file could really be + empty, the above line is a dos batch file comment, but you may want + to put this in there just so you remember in 2 years why you + haven't deleted this file! :) + 2. Get the noweb source distribution and extract just the icon files (i.e. + those located in the icon/ directory with a .icn extention) + 3. Build a batch file for each one using "nticont .icn" + 4. Copy all of the .bat files built in step #2 into your noweb/bin/ + directory + 5. For each new .bat file you have in the bin directory, delete the + corresponding .exe file + Additionally, do NOT rename nticont to icont or nticonx to iconx. The batch + files created with nticont REQUIRE nticonx to exist. So if you rename it, + it'll get very confused. + +Noweb and Emacs + +Q. Emacs noweb-mode is driving me crazy. For example, I want 4-space tabs +instead of the usual 8. I put this line in my file: + +% -*- mode: Noweb; noweb-code-mode: c-mode; tab-width: 4 -*- + +Or equivalently I do M-x set-variable tab-width 4. Everything is fine until the +cursor moves from one chunk to another---then whammo! Tabs are 8 spaces again. +What can I do?? + + A. If you have GNU emacs 20.4, or perhaps some other version, an + undocumented internal function of files.el will do what you want. Try + adding this to your ~/.emacs file: + + (add-hook 'noweb-select-mode-hook + '(lambda () (hack-local-variables-prop-line))) + + Thanks to Glenn Holloway for this discovery. + +Q. Emacs noweb-mode is driving me crazy. I want to use Tab or Ctl-J to handle +indentation, but when I'm in the middle of a code chunk, the indentation is +wrong, and the cursor jumps to a strange place. How can I make emacs behave? + + A. These symptoms indicate that perhaps emacs has lost track of the + boundaries between noweb chunks, perhaps material in documentation chunks + is confusing the auto-indenter, or perhaps both. As of Emacs 21 in 2008, + you can now easily afford to fix these problems using scary Emacs macros: + + (defun my-newline-and-indent () + "Newline and indent that works better with noweb mode" + (interactive "*") + (if (and (boundp 'noweb-mode) noweb-mode) + (save-restriction + (noweb-update-chunk-vector) + (noweb-narrow-to-chunk) + (newline-and-indent)) + (newline-and-indent))) + + (defun my-tab-key () + "Tab that always tabs in Fundamental mode" (interactive) + (if (string= "Fundamental" mode-name) + (insert "\t") + (if (and (boundp 'noweb-mode) noweb-mode) + (save-restriction + (noweb-update-chunk-vector) + (noweb-narrow-to-chunk) + (indent-for-tab-command)) + (indent-for-tab-command)))) + + (defun use-my-indent-keys () + "Locally bind tab and ^J to keys that indent better" (interactive) + (local-set-key "\t" 'my-tab-key) + (local-set-key "\C-j" 'my-newline-and-indent)) + + (add-hook 'noweb-select-code-mode-hook 'use-my-indent-keys) + + Add these to your .emacs file and perhaps the problem will go away. Isn't + Moore's law grand? + +Other Questions + +Q. What about noweb 3? + + A. I use the alpha version, which is alpha only because the new Hackers' + Guide is not finished. But don't look for a release before I retire. + Noweb 3 is definitely an improvement over noweb 2, but for most people, + it's not enough of an improvement for me to go to the trouble of making a + real release. Noweb 3 was started in 1996(!) in response to situations that + no longer obtain, and by 2000 it was substantially complete—the last + significant revisions being done when I was first drafting my textbook. + + Today performance is no longer an issue, and with Darwin and mingw/cygwin, + portability is less of a concern as well. And with Lua 5.1 available, no + sane person wants to use Lua 2.5. I believe that noweb 3 no longer fills a + need. + +Q. I compiled and installed, and everything looks OK, but when I ran the +examples, two things do not work. + + • Noweave can not find definitions for 'C': + + noweave -index -autodefs c compress.nw > compress.tex + noweave: don't know how to find definitions for 'c' + + • Noweave can't find l2h for the -filter. + + A. The awk library doesn't include all the filters that the Icon library + includes. To get these extra features, you'll have to get Icon from the + University of Arizona and re-install noweb using the Icon library. + +Q. I've been playing with LP, using noweb. I put related header, source and +test files into a single .nw file. But that means that if I make a change in +the source component, the header file is re-extracted, with the result that +many files which haven't actually changed are recompiled. Any suggestions on +simplifying the process? + + A. + + notangle -Rheader foo.nw | cpif foo.h + + To understand why this works, see the cpif man page. + +Q. What are these weird Makefile targets like install-preformat-man? Why is +Makefile built from a .nw file? Why is the Makefile so ugly? + + A. Pay no attention to the man behind the curtain. That's all trickery I + use to build a Slackware-style package for Linux. + +Q. We often get an error from TeX asking us to have a wizard enlarge it, due to +a line buffer overflow (the buffer is at 3000 bytes now). This is truly +annoying, since our wizard refuses to enlarge TeX, insisting there is a problem +in our LaTeX macros or the Icon programs that generate them. Unfortunately, +that all comes straight from noweave. + + A. Starting with web2c version 7, you can increase the size up to 30000 in + texmf.cnf. teTeX changed to a 30,000-byte line buffer starting in November, + 1997, so if you encounter this problem you should upgrade your TeX. teTeX + and web2c are both available from the TeX User's Group. + + Noweave emits very long lines on purpose, to preserve the desirable + property that the source and TeX files have identical line numbers. This + property guarantees that the line numbers in error messages from TeX refer + to the lines of the source file, so that, for example, if TeX complains of + an unknown control sequence on line 632, you can just refer to line 632 of + your noweb file. + + Most very long lines are caused by long code chunks containing lots of + identifier definitions, so if you cannot get a new TeX, you can fix the + problem by adding more <<...>>= lines, which breaks up the big chunk into + smaller chunks. For example, instead of using a single chunk definition to + define a large header file, use several: + + <<alinfo.h>>= + #define ... + #define ... + ... + <<alinfo.h>>= + #define ... + #define ... + ... + +Q. I am trying to process multiple *.nw files independently of each other with +noweb, only merging the *.tex files for the final documentation (using noindex +and nodefs to produce cross references). It seems that noweb uses the same +labels (for TeX references) in different files. (The file names and the +identifiers defined in these files are quite similar). + + A. Noweb uses the heuristic made famous by FORTH: names are reduced to the + first three characters plus the length. Changing your file names should be + sufficient. If you don't like that, you could use -filter with a sed script + that adds a unique prefix to all the relevant labels (see the Hacker's + Guide for info about where labels appear). + +Q. I have a problem with noweave's -delay option. It does not seem to prevent +the program from inserting its information before the first chunk and after the +last one. + + A. Put your first @ sign after the \begin{document} instead of at the + beginning of the file, and your last @ sign just before the \end{document}. + +Q. How do I get an output file (root chunk) with an underscore in its name? +LaTeX complains about the underscore. + + A. All chunk names are set in ordinary TeX mode. This means that underscore + is a subscript character. Rather than use special hacks, I just avoid + underscores in file names, since I prefer to use hyphens anyway. However, + you can write a simple sed script to filter the underscores for use by + noweave: + + noweave -filter 'sed "/^@use /s/_/\\\\_/g;/^@defn /s/_/\\\\_/g"' + + and the filter will change _ to \_ before TeX sees it. + +Q. Having used nuweb2noweb on a nuweb file which uses @i to include other files +I find that the resulting noweb file is enormous because the included files are +written directly into the noweb file. How do I emulate nuweb's @i with noweb? + + A. The short answer is it can't be done. (Some would say that it doesn't + need to be done, because notangle and noweave can accept multiple files on + the command line.) The long answer is that you can usually use LaTeX's \ + include or \input commands and keep the noweb files separate. If you + actually need to tangle all those files together (as opposed to just + weaving them), you can usually mention all the names on the command line: + + notangle foo.nw bar.nw quux.nw > big.out + +Q. I have a problem with typesetting Mathematica's double-bracketed +subscripting command using [[...]] in my documentation chunks. noweb considers +[[a[[#]]&]] to mean the symbols a[[# in typewriter and then latex gets stuck on +the offending & character. How can I get around this problem? + + A. Split the quoted code into two pieces, which you write consecutively, + thus: + + [[a[[#]]]][[&]] + +Q. Why is the noweb command so slow? + + A. It's expensive to create a TeX file, so + + noweb foo + + is probably at least 5 times slower than + + noweb -t foo + + and in an edit/compile/debug cycle you may not need the docs and can use + the faster form. + +Q. What is the origin of the ``no'' prefix in noweb ? + + A. I wrote noweb at a time when language-dependent webs were proliferating, + e.g., adaweb, cweb, mweb, etc. So the "no" in noweb is a triple pun: + â–¡ A language-independent web, not limited to one programming language, + hence web for no particular language or `noweb'. + â–¡ A contraction of `Norman's web' + â–¡ The third part of the pun is not suitable for publication. + +Q. Norman, what made you choose Lua as the implementation language for Noweb 3, +rather than, say, Python? + + A. Lua is small and simple. I wanted not to be vulnerable to future changes + in language definitions or implementations, which meant I would have to + clone and maintain the implementation forever. When I was making this + decision in 1997, Lua 2.5 was small enough that I could feel comfortable + doing this. + +Q. Are there noweb comments? It would be very useful to comment parts of a +noweb file that wouldn't be parsed as either TeX or code. + + A. I recommend that you put comments in specially named chunks and use the + elide filter that comes with the distribution. For example, if the name of + each comment chunk begins with comment:, you could run + + noweave -filter elide comment:* ... + + (Because chunks that are not used are ignored, there would be no need to + elide when tangling.) + diff --git a/web/noweb/src/FAQ.html b/web/noweb/src/FAQ.html new file mode 100644 index 0000000000..f492e5c5ca --- /dev/null +++ b/web/noweb/src/FAQ.html @@ -0,0 +1,676 @@ +<html><head><title> +Noweb Frequently Asked Questions</title></head><body> + +<h2><a name=toc1>Noweb Frequently Asked Questions</a></h2> + +<tableofcontents> +<ul compact> +<li><a href="#toc2">Problems Building Noweb</a></li> +<li><a href="#toc3">Indentation of code, especially FORTRAN</a></li> +<li><a href="#toc4">Formatting the printed documentation</a></li> +<li><a href="#toc5">Microsoft Madness</a></li> +<li><a href="#toc6">Noweb and Emacs</a></li> +<li><a href="#toc7">Other Questions</a></li> +</ul> +</tableofcontents> + +<h3><a name=toc2>Problems Building Noweb</a></h3> + +<b>Q.</b> The build fails because it can't find notangle. What am I doing wrong? + +<p><blockquote><b>A.</b> Your source files have the wrong timestamps. The noweb files are + carefully distributed so that the C code is more recent than the + literate source code. Retrieving that noweb distribution as a + compressed tar file preserves these stamps. The CTAN sites will + create a compressed tar file on the fly for any directory. Your + best bet is to get the tar file, unpack it, and try again, because + your abortive attempt will probably have stomped on a C file that + you need. +<p> + I have reports that the former CTAN site <tt>ftp.shsu.edu</tt> +fouled the timestamps. Don't use the site! +If you get bad timestamps, + you can try + `make touch' as soon as you unpack the distribution, which should + touch all of the derived files. This trick won't work on NeXT + systems, which have a strange idea of 'touch'. + +</blockquote><p><b>Q.</b> The system builds OK, but when I try to run noweave, I get an + error in /usr/public/pkg/noweb/lib/totex (or some similar place) + complaining about being out of environment space or something + equally unpleasant. + +<p><blockquote><b>A.</b> The awk versions of the noweb scripts pass the awk programs as + arguments on the command line. Some shells or systems are too + broken to handle a real awk program on the command line. This + problem is one of many reasons that the awk versions are + officially deprecated. The Icon versions will make you happy, but + if you prefer, you can edit the offending scripts so that the awk + programs reside in separate files and the scripts call awk -f. +</blockquote> + +<p><b>Q.</b> + I'm unable to run noweb on Windows NT 4.0. The problem is that the + Icon programs use a DOS extender which doesn't work in an NT console. +<p><blockquote><b>A.</b> + Clint Jeffery, the Icon Master for Windows, says to + use the <a href=ftp://ringer.cs.utsa.edu/pub/icon/nt/console>NT +console binaries</a> + (<tt>nticont.exe</tt> and +<tt>nticonx.exe</tt>) rather than +<tt>wiconx.exe</tt> with its faux console. Only applications that use +the graphics +facilities require <tt>wiconx.exe</tt> and its kin. The +<tt>nticonx*</tt> binaries should run fine +on Win95 also. +</blockquote> + + +<p><b>Q.</b> + I'm having trouble getting noweb to work on Windows XP. +<p><blockquote><b>A.</b> +Bad news. There used to be a distribution from +<tt>literateprogramming.org</tt>, but as of January 2010, that domain + is no longer registered, and I don't know where the distribution + went. +<!-- +Some users are very satisfied with a distribution from +<a + href=http://www.literateprogramming.org><tt>http://www.literateprogramming.org</tt></a>. +This distribution requires + Cygwin (in particular, Cygwin's bash shell) to get the pipes and so on +working. + The installation instructions are at +<A href=http://www.literateprogramming.com/noweb/nowebinstall.html><tt>http://www.literateprogramming.com/noweb/nowebinstall.html</tt></a>. +--> +</blockquote> + +<h3><a name=toc3>Indentation of code, especially FORTRAN</a></h3> + +<b>Q.</b> + +Has anyone devised a clean way of handling FORTRAN 77 using a litprog tool +such as noweb? If I have the following: +<pre> +<<Block>>= + <<Nicely Indented Chunk Title>> +@ +<<Nicely Indented Chunk Title>>= +C A comment in a chunk! + Z = X * + + Y +@ +</pre> +I'll get the "C" in column 7, and the continuation character "+" in column +12, neither of which is desirable. +<p><blockquote><b>A.</b> +As noted on the man page, notangle's <tt>-L</tt> option suppresses the +usual behavior with respect to indentation. +Thus, a command like +<blockquote><tt>notangle -L'%N' pgm.nw > pgm.f</tt></blockquote> +should do the job. +</blockquote> + + +<p><b>Q.</b> +I noticed the <tt>-L</tt> option to notangle turns off the usual indentation. +Is there an easy way to get both <tt>-L</tt> and indentation? +<p><blockquote><b>A.</b> +No. It was my design decision that <tt>-L</tt> should preserve the column numbers +in the original source code. This behavior is extremely useful in +cases where a compiler reports an error in the source by line and +<em>column</em> number. Of course, this decision precludes changing the +indentation of the input. +</blockquote> + + + + +<h3><a name=toc4>Formatting the printed documentation</a></h3> + +<b>Q.</b> There's too much white space at the bottom of the pages, +especially if a code chunk is followed by a section. How do I fix it? + +<p><blockquote><b>A.</b> +Noweb is set up never to break chunks across pages. +You can get better results by relaxing this constraint, e.g., by +<pre> +\def\nwendcode{\endtrivlist \endgroup \vfil\penalty10\vfilneg} +\let\nwdocspar=\smallbreak +</pre> +or the even less liberal +<pre> +\def\nwendcode{\endtrivlist \endgroup} +\let\nwdocspar=\par +</pre> +</blockquote> + +<p> +<b>Q.</b> +In a noweb source file, what is the difference between a +line beginning with "@" followed by a space and some text +versus a line with nothing but "@"? +<p><blockquote><b>A.</b> +A line with nothing but @ begins the `documentation chunk' with a +blank line. TeX thinks blank lines are new paragraphs and good places +to put page breaks. By default, TeX thinks that if a new +documentation chunk starts with a new paragraph, this is an especially +good place for a page break. +</blockquote> +<p> +<b>Q.</b> +I know how to switch between code and documentation via +<tt><<*>>=</tt> and <tt>@</tt>, +but sometimes, after a <tt><<*>>=</tt> section, automatically a new page is +started, even if I do not want this but prefer to go on with commenting +directly after the inserted programm code. How can I avoid this? +<p><blockquote><b>A.</b> +The <tt>@</tt> sign you use to switch back to documentation should not +appear on a line by itself, but should be followed by a blank space +and some text. +You should then put a bare <tt>@</tt> sign on a line by itself at the +next place that is suitable for a page break. +</blockquote> +<p> +<b>Q.</b> +I don't quite understand noweb's default page breaking. +<pre> +Here is some text +that leads up to a chunk +<<chunk>>= + a = b; + c = d; +@ and here is some more text. +Sometimes this appears on a new page even though +there is plenty of room on this page. Can I fix it by adding +the following line (which I haven't been doing) ? +@ +New paragraph here. +</pre> +<p> +<blockquote> +<b>A.</b> +Sad but true. +Perhaps a future version of noweb will provide better facilities for +controlling page breaking. +</blockquote> + + + + +<p> +<b>Q.</b> I ran into a problem when trying to use several files in a single noweb +project. In my project, I have a LaTeX master file (say, doc.tex) that +\include's three others: say, a.tex, b.tex, and copyright.tex. These +later files are generated from a.nw, b.nw, and copyright.nw, +respectively, using the command "noweave -n -index" I <em>really</em> want to +keep these noweb files separated. In the code chunks appearing in both +a.nw and b.nw a copyright notice chunk (defined in copyright.nw) is +included. +<p> +Tangling works fine, provided that copyright.nw appears in the notangle +command. Problems arise with noweave. Copyright chunks appear as +"(never defined)" in a.tex and b.tex, what is obvious, because I cannot +use copyright.nw for weaving a.nw or b.nw (otherwise the copyright +notice chunk would appear several times in my printed document). +<p> +Does someone know how to solve this problem? + +<p><blockquote><b>A.</b> +This is a form of question that arises repeatedly, which is to say, +``what is the scope of chunk names?'' +In Noweb, chunk names are in the same scope if the files in which the +chunks appear are used on the same command line. +Thus, the normal rule is one Noweb file per command line, and +therefore each file is its own scope. +<p> +You want to have chunks in the same scope for purposes of tangling, +but not to weave files together. +If you want files a.nw and b.nw in separate scopes, the situation is +hopeless, because ``being in the same scope'' has to be a transitive +property, and if a.nw and b.nw are both in the same scope as +copyright.nw, they must be in the same scope as each other. +<p> +However, if you want all files to be in the same scope, there is a +solution, although it is a bit ugly. The solution is to weave all the +files together, then use a special back end to split them into +separate .tex files. +This back end, called <a href=scopehack.icn><tt>scopehack</tt></a>, could +be used as follows: +<blockquote> +<pre> +noweave -n -index -backend scopehack *.nw +</pre> +</blockquote> +and this results in multiple *.tex files as required. +</blockquote> + +<p> +<b>Q.</b> +I have problems using LaTeX <code>\ifthenelse</code> with noweb--- +code chunks inside an <code>\ifthenelse</code> are not typeset correctly and +a <code>%def</code> directive causes (La)TeX groups to be unbalanced. + +<p><blockquote><b>A.</b> +Noweb code chunks are ``moving arguments'' (like <code>\verb</code>) and can't be +arguments to macros. I suggest you combine <code>\ifthenelse</code> with the noweb +'elide' filter to remove the offending code chunks. +</blockquote> + +<p> +<b>Q.</b> +I embed the build instructions for a noweb-based project in a code chunk +called <tt>build-script</tt> that I don't +wish to have show up in the printed documentation. So, I surround the +section with LaTeX <code>\iffalse ... \fi</code>. +If I do this at the same time as using noweb's index, the <code>\fi</code> shows up in +the generated TeX file +at the end of the index information, resulting in an empty index. +<p> +<blockquote><b>A.</b> +Make sure that the <code>\fi</code> appears <em>before</em> the last +documentation chunk, by creating an empty documentation chunk +(with @) if necessary. +Then use <code>noweave -delay</code> to insert the index information. +<p> +Incidentally, it is recommended that, in addition to using +<code>\iffalse ... \fi</code>, you remove unwanted code chunks by +using the <tt>elide</tt> filter provided with noweb. +Latex can get confused if chunk cross-reference anchors are hidden +away inside <code>\iffalse ... \fi</code>. +</blockquote> + +<b>Q.</b> When I write C code and use the decrement +operator <tt>--</tt>, it appears in the resulting document as a single +<tt>-</tt>. +This unpleasantness seems to be a side effect of +<tt>\usepackage[T1]{fontenc}</tt>. +<p><blockquote><b>A.</b> +Indeed. We don't understand this behavior, but as an ill-understood +workaround, you can add +<pre> +\catcode`\-=\active +</pre> +to <tt>\def\setupcode</tt>, and things may work better. +</blockquote> + + + + + +<h3><a name=toc5>Microsoft Madness</a></h3> +<b>Q.</b> Using the precompiled MS-DOS and Windows +binaries for noweb, many of the programs throw their +output to the screen and not to whatever receiving program/file they +ought to go to. A typical example: I go to /noweb/examples and type +<pre> + notangle wc.nw > wc.c +</pre> +I see the tangled program scroll by on the screen, but it isn't piped to +the file wc.c as requested. +Any solutions? + +<p><blockquote><b>A.</b> +This is a known COMMAND.COM problem. Try: +<pre> + command /c notangle wc.nw > wc.c +</pre> +or get a COMMAND.COM replacement such as 4DOS. +Better yet, help with the <a href=todo3.html>noweb 3 port</a>. +</blockquote> + +<p><b>Q.</b> +For Windows 95/NT systems: +Why do I get the error message "This program cannot be run in DOS mode." +when I run some of the programs (including noweave.bat)? +<p><blockquote><b>A.</b> +[Thanks to Chris Harris.] +The problem is that some of the files used in the noweb system (including +totex.exe and tohtml.exe used by noweave.bat) are written using an old +version of icon that uses a DOS extender which is incompatible with 95/NT. +The solution is to rebuild all of these files that were built using icon +source: +<ol> +<li> +Go get the console ICON executables for use on 95/NT. They are the files +nticont.exe, nticonx.exe, noop.bat. Put them in your noweb/bin directory. +<ol> +<li>The other two were available on the noweb ftp site, from the +FAQ, but noop.bat wasn't... it's availbe via FTP with the icon distribution, +but it's pretty easy to build yourself, it's contents are the single line: +<pre> +@rem this file prevents further batch processing after launching nticonx +</pre> +<li>So to build noop.bat, just type at the prompt "copy con +noop.bat<enter>" and then paste in the above line (starting with the @) hit +enter and ^Z and you're good to go. Note: This file could really be empty, +the above line is a dos batch file comment, but you may want to put this in +there just so you remember in 2 years why you haven't deleted this file! :) +</ol> +<li>Get the noweb source distribution and extract just the icon files (i.e. +those located in the icon/ directory with a .icn extention) +<li>Build a batch file for each one using "nticont <filename>.icn" + +<li>Copy all of the .bat files built in step #2 into your noweb/bin/ +directory + +<li>For each new .bat file you have in the bin directory, delete the +corresponding .exe file +</ol> +Additionally, do NOT rename nticont to icont or nticonx to iconx. The batch +files created with nticont REQUIRE nticonx to exist. So if you rename it, +it'll get very confused. +</blockquote> + + + +<h3><a name="toc6">Noweb and Emacs</a></h3> + +<p><b>Q.</b> +Emacs noweb-mode is driving me crazy. For example, I want 4-space tabs instead +of the usual 8. I put this line in my file: +<pre> +% -*- mode: Noweb; noweb-code-mode: c-mode; tab-width: 4 -*- +</pre> +Or equivalently I do M-x set-variable tab-width 4. +Everything is fine until the cursor moves from one chunk to +another---then whammo! Tabs are 8 spaces again. +What can I do?? + +<p><blockquote><b>A.</b> +If you have GNU emacs 20.4, or perhaps some other version, an +undocumented internal function of files.el will do what you want. +Try adding this to your ~/.emacs file: +<pre> + (add-hook 'noweb-select-mode-hook + '(lambda () (hack-local-variables-prop-line))) +</pre> +Thanks to Glenn Holloway for this discovery. +</blockquote> + + +<p><b>Q.</b> +Emacs noweb-mode is driving me crazy. +I want to use Tab or Ctl-J to handle indentation, but when I'm in the +middle of a code chunk, the indentation is wrong, and the cursor jumps +to a strange place. +How can I make emacs behave? +<p><blockquote><b>A.</b> +These symptoms indicate that perhaps emacs has lost track of the boundaries +between noweb chunks, perhaps material in documentation chunks is +confusing the auto-indenter, or perhaps both. +As of Emacs 21 in 2008, you can now easily afford to fix these +problems using scary Emacs macros: +<pre> + (defun my-newline-and-indent () + "Newline and indent that works better with noweb mode" + (interactive "*") + (if (and (boundp 'noweb-mode) noweb-mode) + (save-restriction + (noweb-update-chunk-vector) + (noweb-narrow-to-chunk) + (newline-and-indent)) + (newline-and-indent))) + + (defun my-tab-key () + "Tab that always tabs in Fundamental mode" (interactive) + (if (string= "Fundamental" mode-name) + (insert "\t") + (if (and (boundp 'noweb-mode) noweb-mode) + (save-restriction + (noweb-update-chunk-vector) + (noweb-narrow-to-chunk) + (indent-for-tab-command)) + (indent-for-tab-command)))) + + (defun use-my-indent-keys () + "Locally bind tab and ^J to keys that indent better" (interactive) + (local-set-key "\t" 'my-tab-key) + (local-set-key "\C-j" 'my-newline-and-indent)) + + (add-hook 'noweb-select-code-mode-hook 'use-my-indent-keys) +</pre> +Add these to your <tt>.emacs</tt> file and perhaps the problem will go away. +Isn't Moore's law grand? +</blockquote> + + + + +<h3><a name=toc7>Other Questions</a></h3> + +<b>Q.</b> +What about noweb 3? + +<p><blockquote><b>A.</b> + I use the <a href=nwalphacur.zip>alpha version</a>, which is alpha + only because the new Hackers' Guide is not finished. + But <strong>don't look for a release before I retire</strong>. + Noweb 3 is definitely an improvement over noweb 2, but + for most people, it's not <em>enough</em> of an improvement for me + to go to the trouble of making a real release. + Noweb 3 + was started in 1996(!) in response to situations that no longer + obtain, and by 2000 it was substantially complete—the last + significant revisions being done when I was first drafting my + textbook. + <p> + Today performance is no longer an issue, and with Darwin and + mingw/cygwin, portability is less of a concern as well. + And with Lua 5.1 available, no sane person wants to use Lua 2.5. + I believe that noweb 3 no longer fills a need. +</blockquote><p> + + + +<b>Q.</b> +I compiled +and installed, and everything looks +OK, but when I ran the examples, two things do not work. +<ul> +<li>Noweave can not find definitions for 'C': +<pre> +noweave -index -autodefs c compress.nw > compress.tex +noweave: don't know how to find definitions for 'c' +</pre> +<li>Noweave can't find <tt>l2h</tt> for the <tt>-filter</tt>. +</ul> + +<p><blockquote><b>A.</b> +The awk library doesn't include all the filters that the Icon library +includes. To get these extra features, you'll have to get <a +href=http://www.cs.arizona.edu/icon/>Icon</a> from +the University of Arizona and re-install noweb using the Icon library. +</blockquote><p> + +<b>Q.</b> + +I've been playing with LP, using noweb. I put related header, source and +test files into a single .nw file. But that means that if I make a change +in the source component, the header file is re-extracted, with the result +that many files which haven't actually changed are recompiled. +Any suggestions on simplifying the process? + +<p><blockquote><b>A.</b> +<pre> +notangle -Rheader foo.nw | cpif foo.h +</pre> +To understand why this works, see the <tt>cpif</tt> man page. +</blockquote><p> + +<b>Q.</b> What are these weird Makefile targets like install-preformat-man? + Why is Makefile built from a .nw file? Why is the Makefile so ugly? + +<p><blockquote><b>A.</b> Pay no attention to the man behind the curtain. That's all + trickery I use to build a Slackware-style package for Linux. + +</blockquote><p><b>Q.</b> We often get an error from TeX asking us to have a wizard enlarge + it, due to a line buffer overflow (the buffer is at 3000 bytes + now). This is truly annoying, since our wizard refuses to enlarge + TeX, insisting there is a problem in our LaTeX macros or the Icon + programs that generate them. Unfortunately, that all comes straight + from noweave. + +<p><blockquote><b>A.</b> +Starting with web2c version 7, you can increase the size up to 30000 +in <tt>texmf.cnf</tt>. +teTeX changed to a 30,000-byte line buffer starting in November, 1997, +so if you encounter this problem you should upgrade your TeX. +teTeX and web2c are both available from the <a +href=http://www.tug.org/>TeX User's Group</a>. +<p> + + Noweave emits very long lines on purpose, to preserve the desirable + property that the source and TeX files have identical line numbers. + This property guarantees that the line numbers in error messages + from TeX refer to the lines of the source file, so that, for + example, if TeX complains of an unknown control sequence on line 632, + you can just refer to line 632 of your noweb file. +<p> + Most very long lines are caused by long + code chunks + containing lots of identifier definitions, so + if you cannot get a new TeX, you can + fix the problem by adding more <<...>>= lines, which breaks up + the big chunk into smaller chunks. + For example, instead of using a + single chunk definition to define a large header file, use several: +<pre> + <<alinfo.h>>= + #define ... + #define ... + ... + <<alinfo.h>>= + #define ... + #define ... + ... +</pre> + +</blockquote><p><b>Q.</b> I am trying to process multiple *.nw files independently of each + other with noweb, only merging the *.tex files for the final + documentation (using noindex and nodefs to produce cross + references). It seems that noweb uses the same labels (for TeX + references) in different files. (The file names and the identifiers + defined in these files are quite similar). + +<p><blockquote><b>A.</b> Noweb uses the heuristic made famous by FORTH: names are reduced to + the first three characters plus the length. Changing your file names + should be sufficient. If you don't like that, you could use -filter + with a sed script that adds a unique prefix to all the relevant labels + (see the <a href=guide.html>Hacker's Guide</a> for info about where labels appear). + +</blockquote><p><b>Q.</b> +I have a problem with noweave's <code>-delay</code> option. It does not seem to prevent + the program from inserting its information before the first chunk and after + the last one. + +<p><blockquote><b>A.</b> +Put your first @ sign after the <code>\begin{document}</code> instead of at the +beginning of the file, and your last @ sign just before the +<code>\end{document}</code>. + + +</blockquote><p><b>Q.</b> How do I get an output file (root chunk) with an underscore in its name? + LaTeX complains about the underscore. + +<p><blockquote><b>A.</b> All chunk names are set in ordinary TeX mode. This means that + underscore is a subscript character. Rather than use special + hacks, I just avoid underscores in file names, since I prefer to + use hyphens anyway. However, you can write a simple sed script to + filter the underscores for use by noweave: +<pre> + noweave -filter 'sed "/^@use /s/_/\\\\_/g;/^@defn /s/_/\\\\_/g"' +</pre> + and the filter will change _ to \_ before TeX sees it. + + +</blockquote><p><b>Q.</b> Having used nuweb2noweb on a nuweb file which uses @i to include + other files I find that the resulting noweb file is enormous + because the included files are written directly into the noweb + file. How do I emulate nuweb's @i with noweb? + +<p><blockquote><b>A.</b> The short answer is it can't be done. (Some would say that it + doesn't <em>need</em> to be done, because notangle and noweave can accept + multiple files on the command line.) The long answer is that you + can usually use LaTeX's \include or \input commands and keep the + noweb files separate. If you actually need to tangle all those + files together (as opposed to just weaving them), you can usually + mention all the names on the command line: +<pre> + notangle foo.nw bar.nw quux.nw > big.out +</pre> + +</blockquote><p><b>Q.</b> I have a problem with typesetting Mathematica's double-bracketed + subscripting command using <code>[[</code>...<code>]]</code> + in my documentation chunks. +noweb considers <code>[[a[[#]]&]]</code> to mean the symbols <code>a[[#</code> in +typewriter and then latex gets stuck on the offending <code>&</code> character. +How can I get around this problem? + +<p><blockquote><b>A.</b> +Split the quoted code into two +pieces, which you write consecutively, thus: +<blockquote> + <code>[[a[[#]]]][[&]]</code> +</blockquote> + + + + +</blockquote><p><b>Q.</b> Why is the <tt>noweb</tt> command so slow? + + +<p><blockquote><b>A.</b> It's expensive to create a TeX file, so +<pre>noweb foo</pre> +is probably at least 5 times slower than +<pre>noweb -t foo</pre> +and in an edit/compile/debug cycle you may not need the docs and can + use the faster form. +</blockquote> + +<p><b>Q.</b> What is the origin of the ``no'' prefix in <tt>noweb</tt> ? + +<p><blockquote><b>A.</b> +I wrote <tt>noweb</tt> at a time when language-dependent webs were +proliferating, e.g., adaweb, cweb, mweb, etc. So the "no" in <tt>noweb</tt> is +a triple pun: +<ul> +<li>A language-independent web, not limited to one programming + language, hence web for no particular language or `<tt>noweb</tt>'. +<li>A contraction of `Norman's web' +<li>The third part of the pun is not suitable for publication. +</ul> +</blockquote> + +<p><b>Q.</b> +Norman, what made you choose <a href=http://www.lua.org/>Lua</a> as the +implementation language for Noweb 3, rather +than, say, Python? +<p><blockquote><b>A.</b> +Lua is small and simple. I wanted not to be vulnerable to future +changes in language definitions or implementations, which meant I +would have to clone and maintain the implementation forever. When I +was making this decision in 1997, Lua 2.5 was small enough that I +could feel comfortable doing this. +</blockquote> + + +<p><b>Q.</b> + Are there noweb comments? + It would be very useful to comment parts of a noweb file that wouldn't + be parsed as either TeX or code. +<p><blockquote><b>A.</b> +I recommend that you put comments in specially named chunks and use + the <tt>elide</tt> filter that comes with the distribution. +For example, if the name of each comment chunk begins with +<tt>comment:</tt>, you could run +<blockquote> +<pre> +noweave -filter elide comment:* ... +</pre> +</blockquote> +(Because chunks that are not used are ignored, there would be no need + to <tt>elide</tt> when tangling.) +</blockquote> + +</body></html> diff --git a/web/noweb/src/HPUX b/web/noweb/src/HPUX new file mode 100644 index 0000000000..ee65c1462b --- /dev/null +++ b/web/noweb/src/HPUX @@ -0,0 +1,29 @@ +HPUX awk has bugs. I recommend that you use Brian Kernighan's +implementation of awk, which is available for free from AT&T. +Try http://cm.bell-labs.com/who/bwk/. + +If you must use HPUX awk, Steven Ooms kindly provided the following +changes to the awk script "totex". The problem lines are all situated +in the function "indexlabel( ident, l )": + +---------------start included text--------------- +totex: gsub(/{/, ":lb", l) # left brace +... +totex: gsub(/{/, "{\\char123}", arg) +... +totex: gsub(/{\\char92>/, "{\\char92}", arg) +---------------end included text--------------- + +The corresponding new lines look as follows: + +---------------start included text--------------- +totex: gsub(/\{/, ":lb", l) # left brace +... +totex: gsub(/\{/, "{\\char123}", arg) +... +totex: gsub(/\{\\char92>/, "{\\char92}", arg) +---------------end included text--------------- + +Also, I'm told that in src/Makefile the strip command in an HPUX8 +machine causes the make to fail. Commenting out the 'strip' fixes the +problem. diff --git a/web/noweb/src/INSTALL b/web/noweb/src/INSTALL new file mode 100644 index 0000000000..a56b88aedc --- /dev/null +++ b/web/noweb/src/INSTALL @@ -0,0 +1,92 @@ +These installation instructions are for Unix systems. +For Plan 9 systems, Russ Cox has kindly provided scripts in the contrib +directory, but for installation instructions, you are on your own. +If you use Microsoft Windows, Jim Pettigrew has written installation +instructions for Windows 7 at http://www.jim-pettigrew.com/noweb/install/. + +Crucial parts of noweb are written in both Icon and Awk. You can use +either Icon or Awk, but Icon is better: + + - If you have Icon, use the Icon version of the noweb library; this + code speeds up noweave by a factor of 3. The Icon versions have + fewer bugs and provide more filters (e.g., convert LaTeX to HTML + in documentation chunks). Not all of the examples will work with + the awk library. If you have an Icon compiler and believe it + works, set `ICONC=iconc -f l' in the Makefile. (Icon is freely + available from the University of Arizona, but the compiler is no + longer maintained.) + + - As of version 2.12, the Awk code for noweb's indexing and + cross-reference relies on the bit-manipulation primitives found + in GNU Awk. If you have neither Icon nor GNU Awk, you will not + be able to index or cross-reference literate programs. + (This breaking change was put in to enable the Awk and Icon codes + to generate *identical* outputs, for regression testing.) + + +To build noweb: + + 1) First point to compilers to be used to built the tools and + library by setting these variables in the Makefile: + CC the name of an ANSI C compiler + LIBSRC 'awk' or 'icon', depending on which library you want + + Now choose locations for the noweb files, and set the appropriate + variables in the Makefile: + BIN on $PATH, will hold notangle, noweave, noroots, ... + LIB a directory to store markup, nt, noxref, ... + MAN man pages will go in $MAN/man1 + TEXINPUTS a directory TeX will look for nwmac.tex and noweb.sty + ELISP a directory for noweb-mode.el, for emacs 19 and up + All of these files are *output* files, where noweb will place + binaries and man pages that are customized for your installation. + Therefore you should: + - make sure you have permission to write to these directories + - refrain from making them point into the noweb source distribution + - arrange for BIN to be on your $PATH and MAN to be on your $MANPATH + If you don't use emacs, just leave ELISP set to /dev/null. + + 2) If your modern awk is called something other than `nawk', run the + `awkname' script to change all references to nawk. For example, + `awkname gawk'. You need to do this even if you have chosen + LIBSRC=icon, because there are a few scripts here and there that + are only written in awk. If you have an HPUX system and you're + using the vendor-supplied awk, you have a problem---see file HPUX + for details. + + Note: the `awkname' script relies on mktemp(1) to create + temporary files. If your system lacks this program, you'll need + to substitute tempfile(1) or some other means of creating a + temporary file. + + 3) Type `make boot'. This command updates timestamps so that noweb + does not try to build itself using noweb. + + 4) Fasten your seat belt and type `make all install'. If you want a + nonstandard noweave, you can use the WEAVE variable in the + Makefile; for example, `make WEAVE=noweave.simple'. In contrast + with previous versions, the standard noweave should work with + HPUX and SGI Irix. + + 5) If you want to install contributed software, you need the + `contrib' hierarchy. Get it, set the LIB variable in the + Makefile, and type 'make all install'. If you just want the + nuweb parser, try + (cd ../contrib/norman; make LIB=$LIB all install) + where $LIB is the noweb LIB directory. + + 6) On some installations, (e.g., Unix teTeX), you may have to run + texhash(1) before TeX or latex will see files added to TEXINPUTS. + + 7) You can clean up by typing `make clean'. If you wish, you can + remove the pre-built sources by typing `make clobber', but then + you will be unable to build noweb unless you already have noweb. + +Instead of editing the Makefile, you can create a shell script that +invokes make with the proper variables. `nwmake' is an example of +such a script; I use it to install noweb at Princeton using the Icon +cross-referencer and our local C compiler. If you make your own +script, call it something else so it won't get overwritten by the +default nwmake when you unbundle a new distribution. + +If you encounter a problem building noweb, try the FAQ file. diff --git a/web/noweb/src/INSTALL.DOS b/web/noweb/src/INSTALL.DOS new file mode 100644 index 0000000000..4428acc559 --- /dev/null +++ b/web/noweb/src/INSTALL.DOS @@ -0,0 +1,43 @@ +Lee Wittenburg kindly provided this account of his efforts in getting +noweb working on a DOS machine using the MKS toolkit. + + 1. All the shell scripts without an extension required a .KSH. The + makefile, of course needed to be changed accordingly. Also, the + normal .o to .obj and <no extension> to .exe was necessary in the + makefile. No other changes were necessary, except minor ones due to + bugs in MKS make. + + 2. Rename INSTALL to INSTALL.NIX to keep make from getting confused. + + 3. I changed all references to "nawk" to "awk". + + 4. There is a bug in MKS Awk that requires a couple of changes in + NOWEAVE. There's a problem with backslashes in the 2nd argument to + gsub() that requires a couple of changes to NOWEAVE. The lines + + gsub("\\\\", "\\\\", line) + gsub("{", "\\{", line); gsub("}", "\\}", line) + + need to be + + gsub("\\\\", "\\\\\\", line) + gsub("{", "\\\{", line); gsub("}", "\\\}", line) + + I've reported this bug to MKS (when I first found it in version + 3.2), but it still hasn't been fixed. It's not a NOWEB bug, but + MKS Awk NOWEB users need to know about it. + + 5. MKS Awk is POSIX compiliant with regard to regular expression + syntax; Unix Awk isn't; GNU Awk swings both ways (I believe); other + Awks I couldn't say about. This causes a problem ONLY in NOXREF. + I had to change the line + + gsub(/[\[\]\\{} -]/, "*", key) + + to + + gsub(/[[\{} -]|\]/, "*", key) + + which probably won't work on a Unix Awk. There's likely to be + a Unix/POSIX discrepency in Awks for quite a while. + diff --git a/web/noweb/src/Makefile b/web/noweb/src/Makefile new file mode 100644 index 0000000000..339df3b9b4 --- /dev/null +++ b/web/noweb/src/Makefile @@ -0,0 +1,222 @@ +# Copyright 1995-2018 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# +# Adjust these two lines for your ANSI C compiler +CC=gcc -ansi -pedantic -O -Wall -Werror +CFLAGS= +# If you have Icon, you should use the Icon versions of the noweb pipeline. +# Set LIBSRC=icon +LIBSRC=awk +# If you have no Icon compiler, but do have icont, make ICONC=icont +ICONC=iconc +ICONT=icont + +# BIN is where the commands (notangle, noweave, nountangle, noroots) land +# LIB is where the pieces of the pipes (nt, markup, unmarkup) are stored +# MAN is the root of your local man pages directory +# MANEXT is the extension for your commands' man pages (usually 1 or l) +# MAN7EXT is the extension for the nowebstyle man page (usually 7) +# TEXINPUTS is the directory for TeX macro files +# ELISP is the directory for emacs lisp files, or /dev/null not to install +BIN=/usr/local/noweb +LIB=/usr/local/noweb/lib +MAN=/usr/local/noweb/man +MANEXT=1 +MAN7EXT=7 +TEXINPUTS=/usr/local/tex/inputs +ELISP=/dev/null + +# change WEAVE if you want a different version of noweave to be installed +WEAVE=noweave + +# Stop editing. No user-serviceable parts below. +SHELL=/bin/sh +CINAME= +CIMSG= +MANDIR=$(MAN)/man$(MANEXT) +MAN7DIR=$(MAN)/man$(MAN7EXT) +CATDIR=$(MAN)/cat$(MANEXT) +CAT7DIR=$(MAN)/cat$(MAN7EXT) +LIBNAME=$(LIB) +TEXNAME=$(TEXINPUTS) + +all: + cd c; make "CC=$(CC)" "CFLAGS=$(CFLAGS)" all + for i in shell lib xdoc tex; do (cd $$i; make all); done + cd $(LIBSRC); make "ICONT=$(ICONT)" "ICONC=$(ICONC)" all + +FAQ: FAQ.html + sleep 1; html2ascii FAQ.html > FAQ + +FAQ.html: $(HOME)/www/noweb/FAQ.html + rm -f FAQ.html + /bin/cp $(HOME)/www/noweb/FAQ.html FAQ.html + chmod -w FAQ.html + +install: install-code install-man install-tex install-elisp +uninstall: uninstall-code uninstall-man uninstall-tex uninstall-elisp + -rmdir $(BIN) $(LIB) 2>/dev/null || true + +install-shell: + mkdir -p $(BIN) $(LIB) + sed "s@|LIBDIR|@$(LIBNAME)@" shell/noweb > $(BIN)/noweb + chmod +x $(BIN)/noweb + sed "s@|LIBDIR|@$(LIBNAME)@" shell/notangle > $(BIN)/notangle + chmod +x $(BIN)/notangle + sed "s@|LIBDIR|@$(LIBNAME)@" shell/$(WEAVE) > $(BIN)/$(WEAVE) + chmod +x $(BIN)/$(WEAVE) + sed "s@|LIBDIR|@$(LIBNAME)@" shell/nountangle > $(BIN)/nountangle + chmod +x $(BIN)/nountangle + sed "s@|LIBDIR|@$(LIBNAME)@" shell/nodefs > $(BIN)/nodefs + chmod +x $(BIN)/nodefs + sed "s@|LIBDIR|@$(LIBNAME)@" shell/noroots > $(BIN)/noroots + chmod +x $(BIN)/noroots + sed "s@|LIBDIR|@$(LIBNAME)@" shell/nuweb2noweb > $(BIN)/nuweb2noweb + chmod +x $(BIN)/nuweb2noweb + sed "s@|LIBDIR|@$(LIBNAME)@" shell/cpif > $(BIN)/cpif + chmod +x $(BIN)/cpif + sed "s@|LIBDIR|@$(LIBNAME)@" shell/htmltoc > $(BIN)/htmltoc + chmod +x $(BIN)/htmltoc + sed "s@|LIBDIR|@$(LIBNAME)@" shell/noroff > $(BIN)/noroff + chmod +x $(BIN)/noroff + sed "s@|LIBDIR|@$(LIBNAME)@" shell/toroff > $(LIB)/toroff + chmod +x $(LIB)/toroff + cp shell/tmac.w $(LIB) + +uninstall-shell: + rm -f $(BIN)/noweb + rm -f $(BIN)/notangle + rm -f $(BIN)/$(WEAVE) + rm -f $(BIN)/nountangle + rm -f $(BIN)/nodefs + rm -f $(BIN)/noroots + rm -f $(BIN)/nuweb2noweb + rm -f $(BIN)/cpif + rm -f $(BIN)/htmltoc + rm -f $(BIN)/noroff + rm -f $(LIB)/toroff + rm -f $(LIB)/tmac.w + +install-code: install-shell + mkdir -p $(BIN) $(LIB) + strip c/nt c/markup c/mnt c/finduses c/nwmktemp + cp c/nt c/markup c/mnt c/finduses c/nwmktemp $(LIB) + cd $(LIBSRC); make ICONT=$(ICONT) ICONC=$(ICONC) LIB=$(LIB) BIN=$(BIN) install + cd lib; make LIB=$(LIB) install + +uninstall-code: uninstall-shell + rm -f $(LIB)/nt $(LIB)/markup $(LIB)/mnt $(LIB)/finduses + cd $(LIBSRC); make ICONT=$(ICONT) ICONC=$(ICONC) LIB=$(LIB) BIN=$(BIN) uninstall + cd lib; make LIB=$(LIB) uninstall +install-man: + mkdir -p $(MAN) $(MANDIR) $(MAN7DIR) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/cpif.1 > $(MANDIR)/cpif.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nodefs.1 > $(MANDIR)/nodefs.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noroots.1 > $(MANDIR)/noroots.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noweb.1 > $(MANDIR)/noweb.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noindex.1 > $(MANDIR)/noindex.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nuweb2noweb.1 > $(MANDIR)/nuweb2noweb.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/notangle.1 > $(MANDIR)/notangle.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noroff.1 > $(MANDIR)/noroff.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/sl2h.1 > $(MANDIR)/sl2h.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/htmltoc.1 > $(MANDIR)/htmltoc.$(MANEXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nowebstyle.7 > $(MAN7DIR)/nowebstyle.$(MAN7EXT) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nowebfilters.7 > $(MAN7DIR)/nowebfilters.$(MAN7EXT) + rm -f $(MANDIR)/noweave.$(MANEXT) + (cd $(MANDIR); ln notangle.$(MANEXT) noweave.$(MANEXT)) + rm -f $(MANDIR)/nountangle.$(MANEXT) + (cd $(MANDIR); ln notangle.$(MANEXT) nountangle.$(MANEXT)) +uninstall-man: + rm -f $(MANDIR)/cpif.$(MANEXT) + rm -f $(MANDIR)/nodefs.$(MANEXT) + rm -f $(MANDIR)/noroots.$(MANEXT) + rm -f $(MANDIR)/noweb.$(MANEXT) + rm -f $(MANDIR)/noindex.$(MANEXT) + rm -f $(MANDIR)/nuweb2noweb.$(MANEXT) + rm -f $(MANDIR)/notangle.$(MANEXT) + rm -f $(MANDIR)/noroff.$(MANEXT) + rm -f $(MANDIR)/sl2h.$(MANEXT) + rm -f $(MANDIR)/htmltoc.$(MANEXT) + rm -f $(MAN7DIR)/nowebstyle.$(MAN7EXT) + rm -f $(MAN7DIR)/nowebfilters.$(MAN7EXT) + rm -f $(MANDIR)/noweave.$(MANEXT) + rm -f $(MANDIR)/nountangle.$(MANEXT) + -rmdir $(MANDIR) $(MAN7DIR) $(MAN) 2>/dev/null || true +install-gzipped-man: + mkdir -p $(MAN) $(MANDIR) $(MAN7DIR) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/cpif.1 | gzip -9 > $(MANDIR)/cpif.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nodefs.1 | gzip -9 > $(MANDIR)/nodefs.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noroots.1 | gzip -9 > $(MANDIR)/noroots.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noweb.1 | gzip -9 > $(MANDIR)/noweb.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noindex.1 | gzip -9 > $(MANDIR)/noindex.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nuweb2noweb.1 | gzip -9 > $(MANDIR)/nuweb2noweb.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/notangle.1 | gzip -9 > $(MANDIR)/notangle.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noroff.1 | gzip -9 > $(MANDIR)/noroff.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/sl2h.1 | gzip -9 > $(MANDIR)/sl2h.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/htmltoc.1 | gzip -9 > $(MANDIR)/htmltoc.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nowebstyle.7 | gzip -9 > $(MAN7DIR)/nowebstyle.$(MAN7EXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nowebfilters.7 | gzip -9 > $(MAN7DIR)/nowebfilters.$(MAN7EXT).gz + rm -f $(MANDIR)/noweave.$(MANEXT).gz + (cd $(MANDIR); ln notangle.$(MANEXT).gz noweave.$(MANEXT).gz) + rm -f $(MANDIR)/nountangle.$(MANEXT).gz + (cd $(MANDIR); ln notangle.$(MANEXT).gz nountangle.$(MANEXT).gz) +install-preformat-man: + -echo "Warning: install-preformat-man is obsolete, even on Slackware systems" 1>&2 + mkdir -p $(MAN) $(CATDIR) $(CAT7DIR) + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/cpif.txt | gzip > $(CATDIR)/cpif.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nodefs.txt | gzip > $(CATDIR)/nodefs.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noroots.txt | gzip > $(CATDIR)/noroots.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noweb.txt | gzip > $(CATDIR)/noweb.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noindex.txt | gzip > $(CATDIR)/noindex.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nuweb2noweb.txt | gzip > $(CATDIR)/nuweb2noweb.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/notangle.txt | gzip > $(CATDIR)/notangle.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/noroff.txt | gzip > $(CATDIR)/noroff.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/sl2h.txt | gzip > $(CATDIR)/sl2h.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/htmltoc.txt | gzip > $(CATDIR)/htmltoc.$(MANEXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nowebstyle.txt | gzip > $(CAT7DIR)/nowebstyle.$(MAN7EXT).gz + sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@" xdoc/nowebfilters.txt | gzip > $(CAT7DIR)/nowebfilters.$(MAN7EXT).gz + rm -f $(CATDIR)/noweave.$(MANEXT).gz + (cd $(CATDIR); ln notangle.$(MANEXT).gz noweave.$(MANEXT).gz) + rm -f $(CATDIR)/nountangle.$(MANEXT).gz + (cd $(CATDIR); ln notangle.$(MANEXT).gz nountangle.$(MANEXT).gz) +install-tex: + mkdir -p $(TEXINPUTS) + cp tex/nwmac.tex tex/noweb.sty $(TEXINPUTS) + -texhash || echo "Program texhash not found or failed" + +uninstall-tex: + rm -f $(TEXINPUTS)/nwmac.tex $(TEXINPUTS)/noweb.sty || true + +install-elisp: + if [ "/dev/null" != "$(ELISP)" ]; then mkdir -p $(ELISP); fi + cp elisp/noweb-mode.el $(ELISP) + +uninstall-elisp: + rm -f $(ELISP)/noweb-mode.el || true +checkin: + for i in lib tex xdoc; do (cd $$i; make "CINAME=$(CINAME)" "CIMSG=$(CIMSG)" checkin); done + for i in c icon awk; do (cd $$i; ci -l $(CINAME) $(CIMSG) *.nw Makefile); done + (cd elisp; ci -l $(CINAME) $(CIMSG) *.el) + ci -l $(CINAME) $(CIMSG) Makefile.nw INSTALL INSTALL.DOS README FAQ COPYRIGHT nwmake *.nw + +source: FAQ + for i in c shell lib xdoc icon awk tex; do (cd $$i; make CPIF=">" source); done + sleep 1 + for i in c shell lib xdoc icon awk tex; do (cd $$i; make touch); done +touch: + touch FAQ + for i in c shell lib xdoc icon awk tex; do (cd $$i; make touch); done +boot: + for i in c shell lib xdoc icon awk tex; do (cd $$i; make boot); done +clean: + for i in c shell lib xdoc icon awk tex; do (cd $$i; make clean); done + rm -f nwsrcfilter *~ */*~ + +clobber: clean + for i in c shell lib xdoc icon awk tex; do (cd $$i; make clobber); done + +Makefile: Makefile.nw + chmod +w Makefile + notangle -R'script' Makefile.nw | sh > Makefile + chmod -w Makefile + diff --git a/web/noweb/src/Makefile.nw b/web/noweb/src/Makefile.nw new file mode 100644 index 0000000000..079a844b09 --- /dev/null +++ b/web/noweb/src/Makefile.nw @@ -0,0 +1,228 @@ +I got sick and tired of copying goo for multiple files. +This nonsense is the result. (Too bad mk isn't everywhere.) +<<script>>= +(cat Makefile.nw; echo ""; notangle -t8 -R'generate chunks' Makefile.nw | sh) | notangle -t8 - +<<*>>= +# Copyright 1995-2018 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# +# Adjust these two lines for your ANSI C compiler +CC=gcc -ansi -pedantic -O -Wall -Werror +CFLAGS= +# If you have Icon, you should use the Icon versions of the noweb pipeline. +# Set LIBSRC=icon +LIBSRC=awk +# If you have no Icon compiler, but do have icont, make ICONC=icont +ICONC=iconc +ICONT=icont + +# BIN is where the commands (notangle, noweave, nountangle, noroots) land +# LIB is where the pieces of the pipes (nt, markup, unmarkup) are stored +# MAN is the root of your local man pages directory +# MANEXT is the extension for your commands' man pages (usually 1 or l) +# MAN7EXT is the extension for the nowebstyle man page (usually 7) +# TEXINPUTS is the directory for TeX macro files +# ELISP is the directory for emacs lisp files, or /dev/null not to install +BIN=/usr/local/noweb +LIB=/usr/local/noweb/lib +MAN=/usr/local/noweb/man +MANEXT=1 +MAN7EXT=7 +TEXINPUTS=/usr/local/tex/inputs +ELISP=/dev/null + +# change WEAVE if you want a different version of noweave to be installed +WEAVE=noweave + +# Stop editing. No user-serviceable parts below. +SHELL=/bin/sh +CINAME= +CIMSG= +MANDIR=$(MAN)/man$(MANEXT) +MAN7DIR=$(MAN)/man$(MAN7EXT) +CATDIR=$(MAN)/cat$(MANEXT) +CAT7DIR=$(MAN)/cat$(MAN7EXT) +LIBNAME=$(LIB) +TEXNAME=$(TEXINPUTS) + +all: + cd c; make "CC=$(CC)" "CFLAGS=$(CFLAGS)" all + for i in shell lib xdoc tex; do (cd $$i; make all); done + cd $(LIBSRC); make "ICONT=$(ICONT)" "ICONC=$(ICONC)" all + +FAQ: FAQ.html + sleep 1; html2ascii FAQ.html > FAQ + +FAQ.html: $(HOME)/www/noweb/FAQ.html + rm -f FAQ.html + /bin/cp $(HOME)/www/noweb/FAQ.html FAQ.html + chmod -w FAQ.html + +install: install-code install-man install-tex install-elisp +uninstall: uninstall-code uninstall-man uninstall-tex uninstall-elisp + -rmdir $(BIN) $(LIB) 2>/dev/null || true + +install-shell: + mkdir -p $(BIN) $(LIB) + <<shell binaries>> + cp shell/tmac.w $(LIB) + +uninstall-shell: + <<uninstall shell binaries>> + rm -f $(LIB)/tmac.w + +install-code: install-shell + mkdir -p $(BIN) $(LIB) + strip c/nt c/markup c/mnt c/finduses c/nwmktemp + cp c/nt c/markup c/mnt c/finduses c/nwmktemp $(LIB) + cd $(LIBSRC); make ICONT=$(ICONT) ICONC=$(ICONC) LIB=$(LIB) BIN=$(BIN) install + cd lib; make LIB=$(LIB) install + +uninstall-code: uninstall-shell + rm -f $(LIB)/nt $(LIB)/markup $(LIB)/mnt $(LIB)/finduses + cd $(LIBSRC); make ICONT=$(ICONT) ICONC=$(ICONC) LIB=$(LIB) BIN=$(BIN) uninstall + cd lib; make LIB=$(LIB) uninstall +@ I do the [[<<shell binaries>>]] before [[$(LIBSRC)]] so that the +Icon version of [[htmltoc]], if present, will overwrite the Perl +version. +@ +<<generate chunks>>= +for i in noweb notangle '$(WEAVE)' nountangle nodefs noroots nuweb2noweb cpif htmltoc \ + noroff +do + echo '@<<shell binaries@>>=' + echo 'sed "s@|LIBDIR|@$(LIBNAME)@"' "shell/$i" '> $(BIN)/'"$i" + echo 'chmod +x $(BIN)/'"$i" + echo '@<<uninstall shell binaries@>>=' + echo 'rm -f $(BIN)/'"$i" +done +for i in toroff +do + echo '@<<shell binaries@>>=' + echo 'sed "s@|LIBDIR|@$(LIBNAME)@"' "shell/$i" '> $(LIB)/'"$i" + echo 'chmod +x $(LIB)/'"$i" + echo '@<<uninstall shell binaries@>>=' + echo 'rm -f $(LIB)/'"$i" +done +<<*>>= +install-man: + mkdir -p $(MAN) $(MANDIR) $(MAN7DIR) + <<ordinary pages>> +uninstall-man: + <<uninstall ordinary pages>> + -rmdir $(MANDIR) $(MAN7DIR) $(MAN) 2>/dev/null || true +@ +Slackware no longer uses preformatted compressed pages, just +compressed pages. +<<*>>= +install-gzipped-man: + mkdir -p $(MAN) $(MANDIR) $(MAN7DIR) + <<compressed pages>> +<<*>>= +install-preformat-man: + -echo "Warning: install-preformat-man is obsolete, even on Slackware systems" 1>&2 + mkdir -p $(MAN) $(CATDIR) $(CAT7DIR) + <<preformatted compressed pages>> +<<generate chunks>>= +NORMALPAGES="cpif nodefs noroots noweb noindex nuweb2noweb notangle noroff sl2h htmltoc" +NORMAL7PAGES="nowebstyle nowebfilters" +LINKPAGES="noweave nountangle" + + +for i in $NORMALPAGES; do + echo '@<<ordinary pages@>>=' + echo 'sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@"' \ + "xdoc/$i.1" '> $(MANDIR)/'"$i"'.$(MANEXT)' + echo '@<<uninstall ordinary pages@>>=' + echo 'rm -f $(MANDIR)/'"$i"'.$(MANEXT)' +done +for i in $NORMAL7PAGES; do + echo '@<<ordinary pages@>>=' + echo 'sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@"' \ + "xdoc/$i.7" '> $(MAN7DIR)/'"$i"'.$(MAN7EXT)' + echo '@<<uninstall ordinary pages@>>=' + echo 'rm -f $(MAN7DIR)/'"$i"'.$(MAN7EXT)' +done + +for i in $LINKPAGES; do + echo '@<<ordinary pages@>>=' + echo 'rm -f $(MANDIR)/'"$i"'.$(MANEXT)' + echo '(cd $(MANDIR); ln notangle.$(MANEXT)' "$i"'.$(MANEXT))' + echo '@<<uninstall ordinary pages@>>=' + echo 'rm -f $(MANDIR)/'"$i"'.$(MANEXT)' +done + +echo '@<<compressed pages@>>=' + +for i in $NORMALPAGES; do + echo 'sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@"' \ + "xdoc/$i.1" ' | gzip -9 > $(MANDIR)/'"$i"'.$(MANEXT).gz' +done +for i in $NORMAL7PAGES; do + echo 'sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@"' \ + "xdoc/$i.7" ' | gzip -9 > $(MAN7DIR)/'"$i"'.$(MAN7EXT).gz' +done + +for i in $LINKPAGES; do + echo 'rm -f $(MANDIR)/'"$i"'.$(MANEXT).gz' + echo '(cd $(MANDIR); ln notangle.$(MANEXT).gz' "$i"'.$(MANEXT).gz)' +done + +echo '@<<preformatted compressed pages@>>=' + +for i in $NORMALPAGES; do + echo 'sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@"' \ + "xdoc/$i.txt" ' | gzip > $(CATDIR)/'"$i"'.$(MANEXT).gz' +done +for i in $NORMAL7PAGES; do + echo 'sed -e "s@|LIBDIR|@$(LIBNAME)@" -e "s@|TEXINPUTS|@$(TEXNAME)@"' \ + "xdoc/$i.txt" ' | gzip > $(CAT7DIR)/'"$i"'.$(MAN7EXT).gz' +done + +for i in $LINKPAGES; do + echo 'rm -f $(CATDIR)/'"$i"'.$(MANEXT).gz' + echo '(cd $(CATDIR); ln notangle.$(MANEXT).gz' "$i"'.$(MANEXT).gz)' +done +<<*>>= +install-tex: + mkdir -p $(TEXINPUTS) + cp tex/nwmac.tex tex/noweb.sty $(TEXINPUTS) + -texhash || echo "Program texhash not found or failed" + +uninstall-tex: + rm -f $(TEXINPUTS)/nwmac.tex $(TEXINPUTS)/noweb.sty || true + +install-elisp: + if [ "/dev/null" != "$(ELISP)" ]; then mkdir -p $(ELISP); fi + cp elisp/noweb-mode.el $(ELISP) + +uninstall-elisp: + rm -f $(ELISP)/noweb-mode.el || true +@ +<<*>>= +checkin: + for i in lib tex xdoc; do (cd $$i; make "CINAME=$(CINAME)" "CIMSG=$(CIMSG)" checkin); done + for i in c icon awk; do (cd $$i; ci -l $(CINAME) $(CIMSG) *.nw Makefile); done + (cd elisp; ci -l $(CINAME) $(CIMSG) *.el) + ci -l $(CINAME) $(CIMSG) Makefile.nw INSTALL INSTALL.DOS README FAQ COPYRIGHT nwmake *.nw + +source: FAQ + for i in c shell lib xdoc icon awk tex; do (cd $$i; make CPIF=">" source); done + sleep 1 + for i in c shell lib xdoc icon awk tex; do (cd $$i; make touch); done +touch: + touch FAQ + for i in c shell lib xdoc icon awk tex; do (cd $$i; make touch); done +boot: + for i in c shell lib xdoc icon awk tex; do (cd $$i; make boot); done +clean: + for i in c shell lib xdoc icon awk tex; do (cd $$i; make clean); done + rm -f nwsrcfilter *~ */*~ + +clobber: clean + for i in c shell lib xdoc icon awk tex; do (cd $$i; make clobber); done + +Makefile: Makefile.nw + chmod +w Makefile + notangle -R'script' Makefile.nw | sh > Makefile + chmod -w Makefile diff --git a/web/noweb/src/README b/web/noweb/src/README new file mode 100644 index 0000000000..5e25dc05e0 --- /dev/null +++ b/web/noweb/src/README @@ -0,0 +1,20 @@ +These are the base sources and documents for version 2.12 of +``noweb'', a simple, extensible literate-programming tool. noweb is +available from https://github.com/nrnrnr/noweb. The file INSTALL +tells how to build noweb. The file FAQ answers some frequently asked +questions about building and using noweb. + + +STRUCTURE OF THE BASE SOURCES + +This part of the distribution contains the following directories: + + awk pipeline stages and back ends in awk and sh + src C source code for nt, markup, and finduses + icon pipeline stages and back ends in Icon + lib other pipeline stages and back ends, not available in Icon + rc replacements for shell scripts; useful on Plan 9 + shell the shell scripts that make up the actual commands + tex supporting tex code for /usr/local/lib/tex/macros + xdoc man pages, hacker's guide (named to be unpacked last) + diff --git a/web/noweb/src/VERSIONS b/web/noweb/src/VERSIONS new file mode 100644 index 0000000000..273eec39d4 --- /dev/null +++ b/web/noweb/src/VERSIONS @@ -0,0 +1,11 @@ +2.11: Bug fix in parsing; a dozen small repairs +2.10: nountangle becomes a filter; multilanguage support; ... +2.9: Support for hyperref; many small repairs +2.8: LaTeX2e, support for HTML tables +2.7: Reads nuweb files, converts latex to HTML +2.6: HTML support, necessitating a rewrite of cross-referencer +2.5: Index of identifiers and identifier cross-reference +2.4: Icon versions of the major pipeline stages +2.0: Fully detailed pipeline representation +1: Pipelined architecture +0: Monolithic notangle; noweave as unholy awk script diff --git a/web/noweb/src/awk/Makefile b/web/noweb/src/awk/Makefile new file mode 100644 index 0000000000..f090674347 --- /dev/null +++ b/web/noweb/src/awk/Makefile @@ -0,0 +1,47 @@ +# Copyright 1993 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +LIB=/dev/null # must be overridden +BIN=/dev/null # must be overridden +SHELL=/bin/sh + +LIBEXECSBARE=noidx tohtml +LIBEXECSDEP=totex +LIBEXECS=$(LIBEXECSBARE) $(LIBEXECSDEP) +BINEXECS=noindex +EXECS=$(BINEXECS) $(LIBEXECS) + +all: $(EXECS) + chmod +x $(EXECS) +sources: $(EXECS) +touch: $(EXECS) + touch $(EXECS) +boot: + touch $(EXECS) + +install: all + cp $(LIBEXECSBARE) $(LIB) + for i in $(LIBEXECSDEP); do sed "s@|LIBDIR|@$(LIB)@" "$$i" > $(LIB)/"$$i"; chmod +x $(LIB)/$$i; done + cp $(BINEXECS) $(BIN) + +uninstall: + for i in $(LIBEXECS); do rm -f $(LIB)/$$i; done + for i in $(BINEXECS); do rm -f $(BIN)/$$i; done + +source: $(EXECS) + +totex: totex.nw + notangle -Rtotex totex.nw > totex + +noidx: noidx.nw + notangle noidx.nw > noidx + +tohtml: tohtml.nw + notangle tohtml.nw > tohtml + +noindex: noindex.nw + notangle -Rnoindex noindex.nw > noindex + +clean: ; rm -f *.log *.blg *.dvi *.toc *.aux *.tex *~ *.html +clobber: clean + rm -f totex noidx tohtml noindex diff --git a/web/noweb/src/awk/noidx b/web/noweb/src/awk/noidx new file mode 100755 index 0000000000..59f35046af --- /dev/null +++ b/web/noweb/src/awk/noidx @@ -0,0 +1,362 @@ +#!/bin/sh +delay=0 anchordist=0 +while [ $# -gt 0 ]; do + case $1 in + -delay) delay=1 ;; + -docanchor) anchordist="$2"; shift ;; + *) echo "This can't happen -- $1 passed to noidx" 1>&2 ; exit 1 ;; + esac + shift +done +nawk 'function insertafter(i, s, n) { + for(n = nextline++; n - 1 > i; n--) lines[n] = lines[n-1] + lines[n] = s + } + function slipin(s) { + lines[nextline++] = s + } + function fill_charcode_table(i) { + if (charcode_table[64]) + return + for (i = 0; i < 256; i++) { + charcode_table[sprintf("%c", i)] = i + } + } + + function charcode(s, i) { + fill_charcode_table() + return charcode_table[substr(s, i, 1)] + } + function fill_crc_table(POLY, sum, i, j) { + POLY = 245 * 4096 * 4096 + if (crc_table[0]) { + return + } + for (i = 0; i < 256; i ++) { + sum = 0 + for (j = 7; j >= 0; j -= 1) { + if (and(i, lshift(1, j)) != 0) { + sum = xor(sum, rshift(POLY, j)) + } + } + crc_table[i] = sum + } + } + + function crc(s, sum, i) { + fill_crc_table() + sum = 0 + for (i = 1; i <= length(s); i++) { + sum = xor(rshift(sum, 8), crc_table[and(xor(sum, charcode(s, i)), 255)]) + } + return sum + } + function newdefnlabel(arg, label) { + defcount[arg] = defcount[arg] + 1 + label = "NW" curfile "-" uniqueid(arg) "-" alphacode(defcount[arg]) + return label + } + function newdocslabel() { + newdocslabelcount++ + return "NWD" alphacode(newdocslabelcount) + } + function addlabel(tbl, arg, label, marker) { + marker = " " label + if (!tailmatch(tbl[arg], marker)) + tbl[arg] = tbl[arg] marker + return label + } + function tailmatch(string, tail, pos) { + pos = length(string) - length(tail) + 1 + if (pos > 0 && substr(string, pos) == tail) + return 1 + else + return 0 + } + function addud(udlist, name, arg, label, s) { + s = " " name "{" label "}" + if (!tailmatch(udlist[arg], s)) + udlist[arg] = udlist[arg] s + } + function listget(l, i, n, a) { + n = split(l, a) + return a[i] + } + function uniqueid(name, key) { + if (uidtable[name] == "") { + key = make_key(name) + # gsub(/[\]\[ \\{}`#%&~_^<>"-]/, "*", key) # old + gsub(/[^a-zA-Z0-9!$()*+,.\/:;=?@|]/, "*", key) + keycounts[key] = keycounts[key] + 1 + uidtable[name] = key + if (keycounts[key] > 1) + uidtable[name] = uidtable[name] "." alphacode(keycounts[key]) + } + return uidtable[name] + } + function old_make_key(name, key, l) { + l = length(name) + sub(/^.*\//, "", name) + key = substr(name, 1, 3) + if (l >= 3) key = key alphacode(l) + return key + } + function make_key(name) { + return alphacode(crc(name)) + } + function lognowebchunks(l, j, n, x) { + if (loggednowebchunks > 0) return + loggednowebchunks = 1 + delete allchunks[0] + n = alphasort(allchunks) + print "@xref beginchunks" + for (j = 0; j < n; j++) { + name = sorted[j]; delete sorted[j] + printf "@xref chunkbegin %s %s\n", + (anchorlabel[name] != "" ? anchorlabel[name] : "nw@notdef"), name + m = split(chunkud[name], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@xref chunkuse %s\n", substr(a[k], 5, length(a[k])-5) + else if (a[k] ~ /^defn/) + printf "@xref chunkdefn %s\n", substr(a[k], 6, length(a[k])-6) + print "@xref chunkend" + } + print "@xref endchunks" + } + function lognowebindex(l, j, n, x) { + if (loggednowebindex > 0) return + loggednowebindex = 1 + delete allidents[0] + n = alphasort(allidents) + print "@index beginindex" + for (j = 0; j < n; j++) { + name = sorted[j]; delete sorted[j] + printf "@index entrybegin %s %s\n", + (indexanchorlabel[name] != "" ? indexanchorlabel[name] : "nw@notdef"), name + m = split(indexud[name], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@index entryuse %s\n", substr(a[k], 5, length(a[k])-5) + else if (a[k] ~ /^defn/) + printf "@index entrydefn %s\n", substr(a[k], 6, length(a[k])-6) + print "@index entryend" + } + print "@index endindex" + } + function alphasort(a, x, n) { + n = 0 + for (x in a) + n = insertitem(x, n) + return n + } + function insertitem(x, n, i, tmp) { + sorted[n] = x + sortkeys[n] = sortkey(x) + i = n + while (i > 0 && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i-1])) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [i-1]; sortkeys [i-1] = tmp + tmp = sorted[i]; sorted[i] = sorted[i-1]; sorted[i-1] = tmp + i = i - 1 + } + return n + 1 + } + function sortkey(s) { + return tolower(s) "\n" s # relies on \n sorting lower than other characters + } + function alphacode(n) { + if (n < 0) + return "-" alphacode(-n) + else if (n >= alphacodelen) + return alphacode(n / alphacodelen) alphacode(n % alphacodelen) + else + return substr(alphacodes, n+1, 1) + } + BEGIN { curfile = "standard input?" + lastchunkbegin = "never any chunks?" ; + allchunks[0] = 0 ; allidents[0] = 0 ; indexlabels[0] = 0 + defanchors[0] = 0 ; uses[0] = 0 ; anchorlabel[0] = 0 ; indexanchorlabel[0] = 0 + thesedefns[0] = 0; theseuses[0] = 0 ; + defcount[0] = 0 ; + udlist[0] = 0 ; + uidtable[0] = 0 + keycounts[0] = 0 ; + sorted[0] = 0; sortkeys[0] = 0; + alphacodes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + alphacodelen = length(alphacodes) ; nextline = 0 } + /^@file / { curfile = uniqueid(substr($0, 7)) } + /^@begin / { lastchunkbegin = $0 } + /^@end docs / { if (anchordist > 0) { n = anchordist + lastanchorlabel = newdocslabel() + for(i = nextline - 1; i >= 0; i--) { + if (n == 0 || lines[i] ~ /^@begin docs /) { + insertafter(i, "@xref label " lastanchorlabel) + i = -1 # cause loop to terminate + } else if (lines[i] == "@nl") { + n-- + } + } + } } + /^@end code / { lastanchorlabel = "" } + /^@defn / { arg = substr($0, 7) + allchunks[arg] = 1 + lastdefnlabel = newdefnlabel(arg) + slipin("@xref label " lastdefnlabel) + if (lastanchorlabel == "") lastanchorlabel = lastdefnlabel + if (anchorlabel[arg] == "") anchorlabel[arg] = lastanchorlabel + addlabel(defanchors, arg, lastanchorlabel) + addud(chunkud, "defn", arg, lastanchorlabel) + thisusecount = 0 + } + /^@use / { if (lastchunkbegin ~ /^@begin code /) { + arg = substr($0, 6) + allchunks[arg] = 1 + slipin("@xref label " lastdefnlabel "-u" (++thisusecount)) + addlabel(uses, arg, lastanchorlabel) + addud(chunkud, "use", arg, lastanchorlabel) + } + } + /^@index use / { arg = substr($0, 12) + allidents[arg] = 1 + if (lastanchorlabel != "") addud(indexud, "use", arg, lastanchorlabel) + } + /^@index defn / { arg = substr($0, 13) + allidents[arg] = 1 + if (lastanchorlabel != "") { + l = lastanchorlabel + } else { + l = newdocslabel() + slipin("@xref label " l) + } + addud(indexud, "defn", arg, l) + if (indexanchorlabel[arg] == "") indexanchorlabel[arg] = l + slipin("@xref ref " l) # bug fix + } + /^@index localdefn / { arg = substr($0, 18) + allidents[arg] = 1 + if (lastanchorlabel != "") { + l = lastanchorlabel + } else { + l = newdocslabel() + slipin("@xref label " l) + } + addud(indexud, "defn", arg, l) + if (indexanchorlabel[arg] == "") indexanchorlabel[arg] = l + slipin("@xref ref " l) # bug fix + } + { lines[nextline] = $0; nextline++ } + END { + for (i = 0; i < nextline; i ++) { + line = lines[i] + if (line ~ /^@begin /) { + if (delay && lastchunkbegin == line) { print "@nl" + print "@nl" + lognowebchunks() + lognowebindex() + } + print line + for (x in thesedefns) delete thesedefns[x] + for (x in theseuses) delete theseuses[x] + thischunk = "" + } else if (line ~ /^@defn /) { + thischunk = substr(line, 7) + printf "@xref ref %s\n", anchorlabel[thischunk] + print line + defout[thischunk]++ + if (defout[thischunk] > 1) + printf "@xref prevdef %s\n", listget(defanchors[thischunk], defout[thischunk]-1) + if (defout[thischunk] < defcount[thischunk]) + printf "@xref nextdef %s\n", listget(defanchors[thischunk], defout[thischunk]+1) + if (defout[thischunk] == 1) {if (defcount[thischunk] > 1) { + print "@xref begindefs" + n = split(defanchors[thischunk], a) + for (j = 2; j <= n; j++) printf "@xref defitem %s\n", a[j] + print "@xref enddefs" + + } + if (uses[thischunk] != "") { + print "@xref beginuses" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) printf "@xref useitem %s\n", a[j] + print "@xref enduses" + } else { + printf "@xref notused %s\n", thischunk + }} + } else if (line ~ /^@use /) { + arg = substr(line, 6) + printf "@xref ref %s\n", (anchorlabel[arg] == "" ? "nw@notdef" : anchorlabel[arg]) + print line + } else if (line ~ /^@index defn /) { + arg = substr(line, 13) + thesedefns[arg] = 1 + # no xref ref because of bug fix + # if (indexanchorlabel[arg] != "") + # printf "@xref ref %s\n", indexanchorlabel[arg] + print line + } else if (line ~ /^@index localdefn /) { + arg = substr(line, 18) + thesedefns[arg] = 1 + # no xref ref because of bug fix + # if (indexanchorlabel[arg] != "") + # printf "@xref ref %s\n", indexanchorlabel[arg] + print line + } else if (line ~ /^@index use /) { + arg = substr(line, 12) + theseuses[arg] = 1 + if (indexanchorlabel[arg] != "") + printf "@xref ref %s\n", indexanchorlabel[arg] + print line + } else if (line ~ /^@end code/) { + for (x in thesedefns) + delete theseuses[x] + delete thesedefns[0] + n = alphasort(thesedefns) + if (n > 0) { + print "@index begindefs" + for (j = 0; j < n; j++) { + m = split(indexud[sorted[j]], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@index isused %s\n", substr(a[k], 5, length(a[k])-5) + printf "@index defitem %s\n", sorted[j] + delete sorted[j] + } + print "@index enddefs" + } + delete theseuses[0] + n = alphasort(theseuses) + if (n > 0) { + print "@index beginuses" + for (j = 0; j < n; j++) { + m = split(indexud[sorted[j]], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^defn/) + printf "@index isdefined %s\n", substr(a[k], 6, length(a[k])-6) + printf "@index useitem %s\n", sorted[j] + delete sorted[j] + } + print "@index enduses" + } + print line + } else if (line ~ /^@text /) { + # grotesque hack to get indexes in HTML + if (thischunk == "") { # docs mode + arg = substr(line, 7) + if (arg == "<nowebchunks>") lognowebchunks() + else if (arg == "<nowebindex>") lognowebindex() + else print line + } else { + print line + } + } else { + print line + } + delete lines[i] + } + if (!delay) { print "@nl" + print "@nl" + lognowebchunks() + lognowebindex() + } + }' delay=$delay anchordist=$anchordist diff --git a/web/noweb/src/awk/noidx.nw b/web/noweb/src/awk/noidx.nw new file mode 100644 index 0000000000..77b4430027 --- /dev/null +++ b/web/noweb/src/awk/noidx.nw @@ -0,0 +1,477 @@ +\documentstyle[noweb]{article} +\pagestyle{noweb} +\begin{document} +@ +\section{Cross-reference and index support} +Here is is. +<<*>>= +#!/bin/sh +delay=0 anchordist=0 +while [ $# -gt 0 ]; do + case $1 in + -delay) delay=1 ;; + -docanchor) anchordist="$2"; shift ;; + *) echo "This can't happen -- $1 passed to noidx" 1>&2 ; exit 1 ;; + esac + shift +done +nawk '<<awk program for indexing>>' delay=$delay anchordist=$anchordist +@ +<<awk program for indexing>>= +<<functions>> +BEGIN { <<initialization>> nextline = 0 } +<<first pass>> +{ lines[nextline] = $0; nextline++ } +END { + for (i = 0; i < nextline; i ++) { + <<second pass over [[lines[i]]]>> + delete lines[i] + } + if (!delay) <<write trailers>> +} +@ %def lines nextline +<<initialization>>= +curfile = "standard input?" +lastchunkbegin = "never any chunks?" ; +<<initialization>>= +allchunks[0] = 0 ; allidents[0] = 0 ; indexlabels[0] = 0 +defanchors[0] = 0 ; uses[0] = 0 ; anchorlabel[0] = 0 ; indexanchorlabel[0] = 0 +@ %def allchunks allidents indexlabels defanchors uses anchorlabel indexanchorlabel +<<first pass>>= +/^@file / { curfile = uniqueid(substr($0, 7)) } +/^@begin / { lastchunkbegin = $0 } +/^@end docs / { if (anchordist > 0) <<insert and set [[lastanchorlabel]]>> } +/^@end code / { lastanchorlabel = "" } +@ %def curfile lastchunkbegin lastanchorlabel +<<first pass>>= +/^@defn / { arg = substr($0, 7) + allchunks[arg] = 1 + lastdefnlabel = newdefnlabel(arg) + slipin("@xref label " lastdefnlabel) + if (lastanchorlabel == "") lastanchorlabel = lastdefnlabel + if (anchorlabel[arg] == "") anchorlabel[arg] = lastanchorlabel + addlabel(defanchors, arg, lastanchorlabel) + addud(chunkud, "defn", arg, lastanchorlabel) + thisusecount = 0 + } +/^@use / { if (lastchunkbegin ~ /^@begin code /) { + arg = substr($0, 6) + allchunks[arg] = 1 + slipin("@xref label " lastdefnlabel "-u" (++thisusecount)) + addlabel(uses, arg, lastanchorlabel) + addud(chunkud, "use", arg, lastanchorlabel) + } + } +@ %def allchunks lastdefnlabel +<<first pass>>= +/^@index use / { arg = substr($0, 12) + allidents[arg] = 1 + if (lastanchorlabel != "") addud(indexud, "use", arg, lastanchorlabel) + } +/^@index defn / { arg = substr($0, 13) + <<handle index definition of [[arg]]>> + } +/^@index localdefn / { arg = substr($0, 18) + <<handle index definition of [[arg]]>> + } +<<handle index definition of [[arg]]>>= +allidents[arg] = 1 +if (lastanchorlabel != "") { + l = lastanchorlabel +} else { + l = newdocslabel() + slipin("@xref label " l) +} +addud(indexud, "defn", arg, l) +if (indexanchorlabel[arg] == "") indexanchorlabel[arg] = l +slipin("@xref ref " l) # bug fix +@ %def allidents indexanchorlabel +The bug fix\label{multi-def-bug} +alluded to above occurs when there are multiple definitions of an identifier. +In this case, we can't just use [[indexanchorlabel[id]]], because that refers only to +the first definition. In the {\TeX} back end, that leads to bogus +tags like \hbox{\it x \small\rm 7b 7b 7b} instead of \hbox{\it x +\small\rm 7b 9 12a}; the first tag is repeated again and again. +Because the tag for the current [[@defn]] is lost by the time pass~2 +rolls around, we have to slip it in on pass~1. +@ +<<insert and set [[lastanchorlabel]]>>= +{ n = anchordist + lastanchorlabel = newdocslabel() + for(i = nextline - 1; i >= 0; i--) { + if (n == 0 || lines[i] ~ /^@begin docs /) { + insertafter(i, "@xref label " lastanchorlabel) + i = -1 # cause loop to terminate + } else if (lines[i] == "@nl") { + n-- + } + } +} +<<functions>>= +function insertafter(i, s, n) { + for(n = nextline++; n - 1 > i; n--) lines[n] = lines[n-1] + lines[n] = s +} +@ +In the awk version, [[slipin]] is called {\em before} the current line is +added to [[lines]]. +<<functions>>= +function slipin(s) { + lines[nextline++] = s +} +<<initialization>>= +thesedefns[0] = 0; theseuses[0] = 0 ; +<<second pass over [[lines[i]]]>>= +line = lines[i] +if (line ~ /^@begin /) { + if (delay && lastchunkbegin == line) <<write trailers>> + print line + for (x in thesedefns) delete thesedefns[x] + for (x in theseuses) delete theseuses[x] + thischunk = "" +} else if (line ~ /^@defn /) { + thischunk = substr(line, 7) + printf "@xref ref %s\n", anchorlabel[thischunk] + print line + <<write chunk cross-reference>> +} else if (line ~ /^@use /) { + arg = substr(line, 6) + printf "@xref ref %s\n", (anchorlabel[arg] == "" ? "nw@notdef" : anchorlabel[arg]) + print line +} else if (line ~ /^@index defn /) { + arg = substr(line, 13) + thesedefns[arg] = 1 + # no xref ref because of bug fix + # if (indexanchorlabel[arg] != "") + # printf "@xref ref %s\n", indexanchorlabel[arg] + print line +} else if (line ~ /^@index localdefn /) { + arg = substr(line, 18) + thesedefns[arg] = 1 + # no xref ref because of bug fix + # if (indexanchorlabel[arg] != "") + # printf "@xref ref %s\n", indexanchorlabel[arg] + print line +} else if (line ~ /^@index use /) { + arg = substr(line, 12) + theseuses[arg] = 1 + if (indexanchorlabel[arg] != "") + printf "@xref ref %s\n", indexanchorlabel[arg] + print line +} else if (line ~ /^@end code/) { + <<write index cross-reference>> + print line +} else if (line ~ /^@text /) { + # grotesque hack to get indexes in HTML + if (thischunk == "") { # docs mode + arg = substr(line, 7) + if (arg == "<nowebchunks>") lognowebchunks() + else if (arg == "<nowebindex>") lognowebindex() + else print line + } else { + print line + } +} else { + print line +} +@ %def thesedefns theseuses +The case of the [[@index defn]] is the one case where we don't emit a +reference, because the reference has to go in earlier. See +page~\pageref{multi-def-bug} for an explanation. +<<write chunk cross-reference>>= +defout[thischunk]++ +if (defout[thischunk] > 1) + printf "@xref prevdef %s\n", listget(defanchors[thischunk], defout[thischunk]-1) +if (defout[thischunk] < defcount[thischunk]) + printf "@xref nextdef %s\n", listget(defanchors[thischunk], defout[thischunk]+1) +if (defout[thischunk] == 1) {<<write use and def summary for chunk cross-reference>>} +<<write index cross-reference>>= +for (x in thesedefns) + delete theseuses[x] +delete thesedefns[0] +n = alphasort(thesedefns) +if (n > 0) { + print "@index begindefs" + for (j = 0; j < n; j++) { + m = split(indexud[sorted[j]], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@index isused %s\n", substr(a[k], 5, length(a[k])-5) + printf "@index defitem %s\n", sorted[j] + delete sorted[j] + } + print "@index enddefs" +} +<<write index cross-reference>>= +delete theseuses[0] +n = alphasort(theseuses) +if (n > 0) { + print "@index beginuses" + for (j = 0; j < n; j++) { + m = split(indexud[sorted[j]], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^defn/) + printf "@index isdefined %s\n", substr(a[k], 6, length(a[k])-6) + printf "@index useitem %s\n", sorted[j] + delete sorted[j] + } + print "@index enduses" +} +<<write use and def summary for chunk cross-reference>>= +if (defcount[thischunk] > 1) { + print "@xref begindefs" + n = split(defanchors[thischunk], a) + for (j = 2; j <= n; j++) printf "@xref defitem %s\n", a[j] + print "@xref enddefs" + +} +if (uses[thischunk] != "") { + print "@xref beginuses" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) printf "@xref useitem %s\n", a[j] + print "@xref enduses" +} else { + printf "@xref notused %s\n", thischunk +} +<<functions>>= +function fill_charcode_table(i) { + if (charcode_table[64]) + return + for (i = 0; i < 256; i++) { + charcode_table[sprintf("%c", i)] = i + } +} + +function charcode(s, i) { + fill_charcode_table() + return charcode_table[substr(s, i, 1)] +} +@ +32-bit Cyclic Redundancy Code implemented by A. Appel 1986 + +This works only if [[POLY]] is a prime polynomial in the field +of integers modulo~2, of order~32. Since the representation of this polynomial +will not fit in a 32-bit word, the high-order bit is implicit. +\textbf{It must also be the case} that the coefficients of orders~31 down to~25 +are zero. Fortunately, we have a candidate, from +E. J. Watson, "Primitive Polynomials (Mod 2)", Math. Comp 16 (1962): +It is: $x^{32} + x^7 + x^5 + x^3 + x^2 + x^1 + x^0$. + +Now we reverse the bits to get: +\begin{verbatim} + 111101010000000000000000000000001 in binary (but drop the last 1) + f 5 0 0 0 0 0 0 in hex +\end{verbatim} +<<functions>>= +function fill_crc_table(POLY, sum, i, j) { + POLY = 245 * 4096 * 4096 + if (crc_table[0]) { + return + } + for (i = 0; i < 256; i ++) { + sum = 0 + for (j = 7; j >= 0; j -= 1) { + if (and(i, lshift(1, j)) != 0) { + sum = xor(sum, rshift(POLY, j)) + } + } + crc_table[i] = sum + } +} + +function crc(s, sum, i) { + fill_crc_table() + sum = 0 + for (i = 1; i <= length(s); i++) { + sum = xor(rshift(sum, 8), crc_table[and(xor(sum, charcode(s, i)), 255)]) + } + return sum +} +<<functions>>= +function newdefnlabel(arg, label) { + defcount[arg] = defcount[arg] + 1 + label = "NW" curfile "-" uniqueid(arg) "-" alphacode(defcount[arg]) + return label +} +@ %def newdefnlabel +<<initialization>>= +defcount[0] = 0 ; +<<functions>>= +function newdocslabel() { + newdocslabelcount++ + return "NWD" alphacode(newdocslabelcount) +} +@ %def newdocslabel +<<functions>>= +function addlabel(tbl, arg, label, marker) { + marker = " " label + if (!tailmatch(tbl[arg], marker)) + tbl[arg] = tbl[arg] marker + return label +} +@ %def addlabel +<<functions>>= +function tailmatch(string, tail, pos) { + pos = length(string) - length(tail) + 1 + if (pos > 0 && substr(string, pos) == tail) + return 1 + else + return 0 +} +@ %def tailmatch +<<functions>>= +function addud(udlist, name, arg, label, s) { + s = " " name "{" label "}" + if (!tailmatch(udlist[arg], s)) + udlist[arg] = udlist[arg] s +} +@ %def addud +<<functions>>= +function listget(l, i, n, a) { + n = split(l, a) + return a[i] +} +@ %def listget +<<initialization>>= +udlist[0] = 0 ; +@ +[[uniqueid]] eliminates both {\TeX} and HTML specials. +Escaping the [[/]] in the character class in the regexp pattern works +around a bug in many awks. +Unpalatable, but what can one do? +<<functions>>= +function uniqueid(name, key) { + if (uidtable[name] == "") { + key = make_key(name) + # gsub(/[\]\[ \\{}`#%&~_^<>"-]/, "*", key) # old + gsub(/[^a-zA-Z0-9!$()*+,.\/:;=?@|]/, "*", key) + keycounts[key] = keycounts[key] + 1 + uidtable[name] = key + if (keycounts[key] > 1) + uidtable[name] = uidtable[name] "." alphacode(keycounts[key]) + } + return uidtable[name] +} +@ %def uniqueid +<<functions>>= +function old_make_key(name, key, l) { + l = length(name) + sub(/^.*\//, "", name) + key = substr(name, 1, 3) + if (l >= 3) key = key alphacode(l) + return key +} +<<functions>>= +function make_key(name) { + return alphacode(crc(name)) +} +<<initialization>>= +uidtable[0] = 0 +keycounts[0] = 0 ; +<<write trailers>>= +{ print "@nl" + print "@nl" + lognowebchunks() + lognowebindex() +} +@ +Now, a special hack, so we can write this stuff in the right place on pass 2. +<<functions>>= +function lognowebchunks(l, j, n, x) { + if (loggednowebchunks > 0) return + loggednowebchunks = 1 + delete allchunks[0] + n = alphasort(allchunks) + print "@xref beginchunks" + for (j = 0; j < n; j++) { + name = sorted[j]; delete sorted[j] + printf "@xref chunkbegin %s %s\n", + (anchorlabel[name] != "" ? anchorlabel[name] : "nw@notdef"), name + m = split(chunkud[name], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@xref chunkuse %s\n", substr(a[k], 5, length(a[k])-5) + else if (a[k] ~ /^defn/) + printf "@xref chunkdefn %s\n", substr(a[k], 6, length(a[k])-6) + print "@xref chunkend" + } + print "@xref endchunks" +} +@ %def lognowebchunks +<<functions>>= +function lognowebindex(l, j, n, x) { + if (loggednowebindex > 0) return + loggednowebindex = 1 + delete allidents[0] + n = alphasort(allidents) + print "@index beginindex" + for (j = 0; j < n; j++) { + name = sorted[j]; delete sorted[j] + printf "@index entrybegin %s %s\n", + (indexanchorlabel[name] != "" ? indexanchorlabel[name] : "nw@notdef"), name + m = split(indexud[name], a) + for (k = 1; k <= m; k++) + if (a[k] ~ /^use/) + printf "@index entryuse %s\n", substr(a[k], 5, length(a[k])-5) + else if (a[k] ~ /^defn/) + printf "@index entrydefn %s\n", substr(a[k], 6, length(a[k])-6) + print "@index entryend" + } + print "@index endindex" +} +@ %def lognowebindex +<<functions>>= +function alphasort(a, x, n) { + n = 0 + for (x in a) + n = insertitem(x, n) + return n +} +function insertitem(x, n, i, tmp) { + sorted[n] = x + sortkeys[n] = sortkey(x) + i = n + while (i > 0 && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i-1])) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [i-1]; sortkeys [i-1] = tmp + tmp = sorted[i]; sorted[i] = sorted[i-1]; sorted[i-1] = tmp + i = i - 1 + } + return n + 1 +} +@ %def alphasort insertitem +<<initialization>>= +sorted[0] = 0; sortkeys[0] = 0; +<<old, evil code>>= +function sortkey(name, s) { + s = name + gsub(/[^a-zA-Z ]/, "", s) + return s "\n" name # relies on \n sorting lower than other characters +} +<<functions>>= +function sortkey(s) { + return tolower(s) "\n" s # relies on \n sorting lower than other characters +} +@ %def sortkey +<<functions>>= +function alphacode(n) { + if (n < 0) + return "-" alphacode(-n) + else if (n >= alphacodelen) + return alphacode(n / alphacodelen) alphacode(n % alphacodelen) + else + return substr(alphacodes, n+1, 1) +} +@ %def alphacode +<<initialization>>= +alphacodes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +alphacodelen = length(alphacodes) ; +@ +\section{List of chunks} +\nowebchunks + +\twocolumn +\section{Index} +\nowebindex +@ +\end{document} diff --git a/web/noweb/src/awk/noindex b/web/noweb/src/awk/noindex new file mode 100755 index 0000000000..ba530f5c5c --- /dev/null +++ b/web/noweb/src/awk/noindex @@ -0,0 +1,155 @@ +#!/bin/sh + +if [ $# -ne 1 ]; then + echo "Usage: `basename $0` file[.tex]" 1>&2 + exit 1 +fi + +nawk 'BEGIN { infile="'"$1"'" + if (infile ~ /\.tex$/) { + infile = substr(infile, 1, length(infile)-4) ".aux" + } else if (infile !~ /\.aux$/) { + infile = infile ".aux" + } + idx[0] = "" + delete idx[0] + gobble(infile) + alphasort(idx) + outname = substr(infile, 1, length(infile)-4) ".nwi" + last = "" + for (i = 0; i < count; i++) { + out = stripcount(sorted[i]) + if (last != out) { + print out > outname + last = out + # <show sort key [[i]]> + } + } + exit + } +function gobble(name, infile, rc, tag) { + for (rc = (getline line < name); rc > 0; rc = (getline line < name)) { + if (line ~ /^\\@input{[^}]*}$/) + gobble(substr(line, 9, length(line)-9)) + else if (line ~ /^\\nwixadds{/) { + count++ + tag = "000000" count + tag = substr(tag, length(tag)-6+1) + idx[count] = tag " " substr(line, 11) + } + } + if (rc < 0) print "No file " name "." + else close(name) + return +} +function stripcount(s) { + sub(/^[0-9]+/, "", s) + sub(/ +/, "", s) + return "\\nwixaddsx{" s +} +function alphasort(a, x, n) { + n = 0 + for (x in a) + n = insertitem(a[x], n) + finish_sorting(n) + return n +} +function insertitem(x, n, i, tmp) { + sorted[n] = x + sortkeys[n] = sortkey(x) + return n+1 +} +function finish_sorting(n) { + firstwork = nextwork = 0 + addquick(0, n) + while(nextwork > firstwork) + qsort() +} +function addquick(l, r) { + workq[nextwork++] = l + workq[nextwork++] = r +} +function qsort(l, r, mid, i, last) { + l = workq[firstwork] + delete workq[firstwork] + firstwork++ + r = workq[firstwork] + delete workq[firstwork] + firstwork++ + if (r - l < 10) + isort(l, r) + else { + mid = l + int((r - l) * rand()) + swap(l, mid) + last = l + for (i = l+1; i < r; i++) + if (sortkeys[i] < sortkeys[l] || + sortkeys[i] == sortkeys[l] && sorted[i] < sorted[l]) + swap(++last, i) + swap(l, last) + addquick(l, last) + addquick(last+1, r) + } +} +function isort(l, r, n) { + for (n = l + 1; n < r; n++) + for (i = n; i > l && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i-1]); i--) + swap(i, i-1) +} +function swap(i, j, tmp) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [j]; sortkeys [j] = tmp + tmp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = tmp +} +function sortkey(s, count) { + match(s, /^[0-9]+/) + count = substr(s, RSTART, RLENGTH) + sub(/^[0-9]+ */, "", s) + if (s ~ /c}/) { + return firstkey(substr(s, 3)) "\n" count + } else if (s ~ /i}/) { + return firstkey(substr(s, 3)) "\n" count + } else { + print "sortkey handed non-chunk and non-index: " s + exit 1 + } +} +function firstkey(s, r, openbrace) { + if (s !~ /^{{/) { + print "key \"" substr(s, 1, 6) "...\" is ill-formatted" + exit 1 + } + sub (/^{{/, "", s) + gsub(/\\([a-zA-Z]+|.) */, "", s) # kill control sequences + openbrace = 1 + r = "" + while (openbrace > 0) + if (match(s, /^[^{}]*/) <= 0) + openbrace-- + else { + r = r substr(s, RSTART, RLENGTH) + c = substr(s, RSTART+RLENGTH, 1) + s = substr(s, RSTART+RLENGTH+1) + if (c == "}") openbrace-- + else openbrace++ + if (openbrace > 0) r = r c + } + return alphabet(r) "\n" r +} +function alphabet(s, r) { + r = "" + while (match(s, /[a-zA-Z \t]/) > 0) { + s = substr(s, RSTART) + c = substr(s, 1, 1) + if (c == " " || c == "\t") { + r = r " " + sub(/^[ \t]+/, "", s) + } else { + match(s, /^[a-zA-Z]+/) + r = r substr(s, RSTART, RLENGTH) + s = substr(s, RSTART+RLENGTH) + } + } + sub(/^ +/, "", r) + return r +}' 1>&2 diff --git a/web/noweb/src/awk/noindex.nw b/web/noweb/src/awk/noindex.nw new file mode 100644 index 0000000000..ba57f6e234 --- /dev/null +++ b/web/noweb/src/awk/noindex.nw @@ -0,0 +1,187 @@ +This program is similar to [[makeindex]] in that it grovels through [[.aux]] +files looking for index information, which it writes to a [[.nwi]] file. +It's used when [[noweave -indexfrom]] is used on many files separately; +it combines the separate indices into a single, correctly sorted index. +That index file is read by [[\nowebindex*]]. +<<noindex>>= +#!/bin/sh + +if [ $# -ne 1 ]; then + echo "Usage: `basename $0` file[.tex]" 1>&2 + exit 1 +fi + +nawk 'BEGIN { infile="'"$1"'" + <<main code>> + exit + } +<<functions>>' 1>&2 +<<main code>>= +if (infile ~ /\.tex$/) { + infile = substr(infile, 1, length(infile)-4) ".aux" +} else if (infile !~ /\.aux$/) { + infile = infile ".aux" +} +idx[0] = "" +delete idx[0] +gobble(infile) +alphasort(idx) +outname = substr(infile, 1, length(infile)-4) ".nwi" +last = "" +for (i = 0; i < count; i++) { + out = stripcount(sorted[i]) + if (last != out) { + print out > outname + last = out +# <show sort key [[i]]> + } +} +<<show sort key [[i]]>>= +key = sortkeys[i] +sub(/^\n+/, "", key) +sub(/\n.*$/, "", key) +print "% " key > outname +<<functions>>= +function gobble(name, infile, rc, tag) { + for (rc = (getline line < name); rc > 0; rc = (getline line < name)) { + if (line ~ /^\\@input{[^}]*}$/) + gobble(substr(line, 9, length(line)-9)) + else if (line ~ /^\\nwixadds{/) { + count++ + tag = "000000" count + tag = substr(tag, length(tag)-6+1) + idx[count] = tag " " substr(line, 11) + } + } + if (rc < 0) print "No file " name "." + else close(name) + return +} +<<functions>>= +function stripcount(s) { + sub(/^[0-9]+/, "", s) + sub(/ +/, "", s) + return "\\nwixaddsx{" s +} +<<functions>>= +function alphasort(a, x, n) { + n = 0 + for (x in a) + n = insertitem(a[x], n) + finish_sorting(n) + return n +} +function insertitem(x, n, i, tmp) { + sorted[n] = x + sortkeys[n] = sortkey(x) + return n+1 +} +<<functions>>= +function finish_sorting(n) { + firstwork = nextwork = 0 + addquick(0, n) + while(nextwork > firstwork) + qsort() +} +<<functions>>= +function addquick(l, r) { + workq[nextwork++] = l + workq[nextwork++] = r +} +<<get [[l]] and [[r]] out of work queue>>= +l = workq[firstwork] +delete workq[firstwork] +firstwork++ +r = workq[firstwork] +delete workq[firstwork] +firstwork++ +<<functions>>= +function qsort(l, r, mid, i, last) { + <<get [[l]] and [[r]] out of work queue>> + if (r - l < 10) + isort(l, r) + else { + mid = l + int((r - l) * rand()) + swap(l, mid) + last = l + for (i = l+1; i < r; i++) + if (sortkeys[i] < sortkeys[l] || + sortkeys[i] == sortkeys[l] && sorted[i] < sorted[l]) + swap(++last, i) + swap(l, last) + addquick(l, last) + addquick(last+1, r) + } +} +<<functions>>= +function isort(l, r, n) { + for (n = l + 1; n < r; n++) + for (i = n; i > l && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i-1]); i--) + swap(i, i-1) +} +<<functions>>= +function swap(i, j, tmp) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [j]; sortkeys [j] = tmp + tmp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = tmp +} +<<functions>>= +function sortkey(s, count) { + match(s, /^[0-9]+/) + count = substr(s, RSTART, RLENGTH) + sub(/^[0-9]+ */, "", s) + if (s ~ /c}/) { + return firstkey(substr(s, 3)) "\n" count + } else if (s ~ /i}/) { + return firstkey(substr(s, 3)) "\n" count + } else { + print "sortkey handed non-chunk and non-index: " s + exit 1 + } +} +<<functions>>= +function firstkey(s, r, openbrace) { + if (s !~ /^{{/) { + <<complain about format and exit>> + } + sub (/^{{/, "", s) + gsub(/\\([a-zA-Z]+|.) */, "", s) # kill control sequences + openbrace = 1 + r = "" + while (openbrace > 0) + if (match(s, /^[^{}]*/) <= 0) + openbrace-- + else { + r = r substr(s, RSTART, RLENGTH) + c = substr(s, RSTART+RLENGTH, 1) + s = substr(s, RSTART+RLENGTH+1) + if (c == "}") openbrace-- + else openbrace++ + if (openbrace > 0) r = r c + } + return alphabet(r) "\n" r +} +<<complain about format and exit>>= +print "key \"" substr(s, 1, 6) "...\" is ill-formatted" +exit 1 +<<functions>>= +function alphabet(s, r) { + r = "" + while (match(s, /[a-zA-Z \t]/) > 0) { + s = substr(s, RSTART) + c = substr(s, 1, 1) + if (c == " " || c == "\t") { + r = r " " + sub(/^[ \t]+/, "", s) + } else { + match(s, /^[a-zA-Z]+/) + r = r substr(s, RSTART, RLENGTH) + s = substr(s, RSTART+RLENGTH) + } + } + sub(/^ +/, "", r) + return r +} +@ +\section{Index} +\nowebindex diff --git a/web/noweb/src/awk/tohtml b/web/noweb/src/awk/tohtml new file mode 100755 index 0000000000..f4a0439c41 --- /dev/null +++ b/web/noweb/src/awk/tohtml @@ -0,0 +1,237 @@ +#!/bin/sh +# Copyright 1994-2018 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# Do not try to understand this file! Look at lib/tohtml.nw in the noweb source! + +delay=0 raw=0 localindex=0 noindex=0 nocomment=0 +for i do + case $i in + -delay) delay=1 ;; + -raw) raw=1 ;; + -localindex) if [ $noindex -eq 0 ]; then localindex=1; fi;; + -noindex) localindex=0; noindex=1 ;; + -no-gen-comment) nocomment=1 ;; + esac +done +nawk 'function writechunk(label, ref, tag, name, suffix) { + printf "%s", + linklabelto(label, ref, sgmlwrap(tag, "<" convquotes(name) ">" suffix)) + } + function linklabelto(label, ref, contents, s) { + s = label != "" || ref != "" ? "<a" : "" + if (label != "") s = s " name=" image(label) + if (ref != "") s = s " href=" image("#" ref) + s = s (label != "" || ref != "" ? ">" : "") + s = s contents + s = s (label != "" || ref != "" ? "</a>" : "") + return s + } + + function linkto(ref, contents) { + return linklabelto("", ref, contents) + } + + function linklabel(label, contents) { + return linklabelto(label, "", contents) + } + function sgmlwrap(tag, s) { + return "<" tag ">" s "</" tag ">" + } + function image(s) { + gsub(/"/, "\\\"", s) + return "\"" s "\"" + } + function escapeSpecials (l) { + gsub(/&/, "\\&", l) + gsub(/</, "\\<", l) + gsub(/>/, "\\>", l) + gsub(/"/, "\\"", l) + return l + } + function convquotes(s, r, i, line) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "<code>" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + line = substr(s, 1, i-1+RLENGTH-2) + # line = escapeSpecials(line) # destroys internal markup --- do not call + r = r line "</code>" + s = substr(s, i+RLENGTH) + } else { + r = r s "</code>" + s = "" + } + } + return r s + } + BEGIN { + defns[0] = 0 + defns_above[0] = 0 + useitemstab[0] = 0 + } + !doneraw { # do not do in BEGIN because not all awks assign variables yet + if (raw) { braw = "\\begin{rawhtml}"; eraw = "\\end{rawhtml}" } + else braw = eraw = "" + doneraw = 1 + if (!nocomment) { + print braw "<!-- this file was generated automatically by noweave;" " better not edit it-->" eraw + } + } + /^@begin code / { code = 1; printf "%s<pre>", braw; ecode = "</pre>" } + /^@end code / { code = 0; previscode = 1; useitemscount = split(useitemstab[thischunk], a) + if (pendingprev != "" || pendingnext != "" || useitemscount > 0) { + if (ecode == "</pre>") { + printf "</pre><blockquote>" + ecode = "</blockquote>" + } + useprefix = "Used " + for (j = 1; j <= useitemscount; j++) { + if (defns_above[a[j]] > 0) + usedir = "above" + else + usedir = "below" + printf "%s%s", useprefix, linkto(a[j], usedir (useitemscount > 1 ? " (" j ")" : "")) + useprefix = ", " + } + if (useitemscount > 0 && (pendingprev != "" || pendingnext != "")) + printf "; " + p = useitemscount > 0 ? "previous" : "Previous" + n = useitemscount > 0 ? "next" : "Next" + if (pendingprev != "") + if (pendingnext != "") + printf "%s and %s definitions", linkto(pendingprev, p), linkto(pendingnext, "next") + else + printf "%s definition", linkto(pendingprev, p) + else + if (pendingnext != "") + printf "%s definition", linkto(pendingnext, n) + pendingprev = pendingnext = "" + useitems = "" + print ".<p>" + } + printf "%s%s", ecode, eraw + } + /^@begin docs / { if (previscode) printf "%s", (raw ? "\\par" : "<p>") + previscode = text = 0 + } + /^@end docs / { if (lastxreflabel != "") + printf "%s%s%s\n", braw, linklabel(lastxreflabel, "*"), eraw + lastxreflabel = "" + } + /^@text / { line = substr($0, 7); text += length(line) + if (code) { + if (lastindexref != "" && line ~ /[^ \t]/) { + printf "%s", linkto(lastindexref, line) + lastindexref = "" + } else { + printf "%s", escapeSpecials(line) + } + } else if (quoting) { + if (line ~ /[^ \t]/) { + printf "%s", linklabelto(lastxreflabel, lastindexref, + escapeSpecials(line)) + lastindexref = lastxreflabel = "" + } else { + printf "%s", escapeSpecials(line) + } + } else { + if (lastxreflabel != "" && line ~ /[^ \t]/) { + match(line, /^[ \t]*/) + blanks = substr(line, RSTART, RLENGTH) + line = substr(line, RSTART+RLENGTH) + if (line ~ /^[{}\\<&]/) { + char = "*" + } else { + char = substr(line, 1, 1) + line = substr(line, 2) + } + printf "%s%s%s%s%s", braw, blanks, linklabel(lastxreflabel, char), eraw, line + if (lastxreflabel != "") defns_above[lastxreflabel] = 1 + lastxreflabel = "" + } else { + printf "%s", line + } + } + } + /^@nl$/ { print "" } + /^@defn / { thischunk = name = substr($0, 7) + if (lastxreflabel != "") defns_above[lastxreflabel] = 1 + writechunk(lastxreflabel, lastxrefref, "dfn", name, defns[name] "=") + lastxreflabel = lastxrefref = "" + defns[name] = "+" + } + /^@use / { writechunk(lastxreflabel, lastxrefref, "i", substr($0, 6), "") } + /^@quote$/ { quoting = 1 ; printf "%s<code>", braw } + /^@endquote$/ { quoting = 0 ; printf "</code>%s", eraw } + /^@file / { filename = substr($0, 7); lastxreflabel = lastxrefref = "" } + /^@literal / { printf "%s", substr($0, 10) } + /^@header html / { printf "<html><head><title>%s</title></head><body>", substr($0, 14) + } + /^@trailer html$/ { print "</body></html>" } + /^@xref label / { lastxreflabel = substr($0, 13) } + /^@xref ref / { lastxrefref = substr($0, 11) } + /^@xref prevdef/ { pendingprev = substr($0, 15) } + /^@xref nextdef/ { pendingnext = substr($0, 15) } + /^@xref beginuses/ { useitems = "" } + /^@xref useitem / { useitems = useitems " " substr($0, 15) } + /^@xref enduses/ { useitemstab[thischunk] = useitems } + /^@xref notused / { if (ecode == "</pre>") { + printf "</pre><blockquote>" + ecode = "</blockquote>" + } + printf "This code is written to a file (or else not used).<p>" + } + /^@xref (begindefs|defitem|enddefs)/ { } + /^@xref beginchunks$/ { printf "%s<ul>\n", braw } + /^@xref chunkbegin / { label = $3; name = substr($0, 19 + length(label)) + printf "<li>"; comma = ": "; count = 0 + writechunk("", label, "i", name, "") + } + /^@xref chunkuse / { printf "%s%s", comma, linkto(substr($0, 16), "U" ++count) + comma = ", " + } + /^@xref chunkdefn / { printf "%s%s", comma, linkto(substr($0, 17), "D" ++count) + comma = ", " + } + /^@xref chunkend$/ { print "" } + /^@xref endchunks$/ { printf "</ul>%s\n", eraw } + /^@index beginindex$/ { if (!noindex) { printf "%s<ul>\n", braw } } + /^@index entrybegin / { if (!noindex) { + label = $3; name = substr($0, 20 + length(label)) + printf "<li>"; comma = ": "; count = 0 + printf "%s", + linklabelto("NWI-" escapeSpecials(name), label, name) + + } } + /^@index entryuse / { if (!noindex) { + printf "%s%s", comma, linkto(substr($0, 17), "U" ++count) + comma = ", " + } } + /^@index entrydefn / { if (!noindex) { + printf "%s%s", comma, linkto(substr($0, 18), "D" ++count) + comma = ", " + } } + /^@index entryend$/ { if (!noindex) { print "" } } + /^@index endindex$/ { if (!noindex) { printf "</ul>%s\n", eraw } } + /^@index use/ { lastindexref = lastxrefref; lastxrefref = "" } + /^@index defn/ { lastxreflabel = lastxrefref = "" } + /^@index localdefn/ { lastxreflabel = lastxrefref = "" } + /^@index nl/ { } # do nothing -- destroys line numbering + /^@index begindefs/ { if (localindex) { + if (ecode == "</pre>") { + printf "</pre><blockquote>" + ecode = "</blockquote>" + }; printf "Defines"; comma = " " + } } + /^@index isused / { } + /^@index defitem / { if (localindex) { + arg = substr($0, 16) + printf "%s%s", comma, + linkto("NWI-" escapeSpecials(arg), sgmlwrap("code", escapeSpecials(arg))) + comma = ", " + } } + /^@index enddefs/ { if (localindex) { printf " (links are to index).<p>\n" } } + /^@index (beginuses|isdefined|useitem|enduses)/ { } # use local links + END { print "" }' \ + delay=$delay raw=$raw localindex=$localindex noindex=$noindex nocomment=$nocomment diff --git a/web/noweb/src/awk/tohtml.nw b/web/noweb/src/awk/tohtml.nw new file mode 100644 index 0000000000..807c59cf30 --- /dev/null +++ b/web/noweb/src/awk/tohtml.nw @@ -0,0 +1,331 @@ +\documentstyle[noweb]{article} +\pagestyle{noweb} +\begin{document} +\section{Converting {\tt noweb} markup to {\tt HTML}} +This copyright applies both to the {\tt noweb} source and to the +generated shell script. +Thanks to Bill Trost for getting me started with an early version. +<<copyright notice>>= +# Copyright 1994-2018 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +<<*>>= +#!/bin/sh +<<copyright notice>> +# Do not try to understand this file! Look at lib/tohtml.nw in the noweb source! + +delay=0 raw=0 localindex=0 noindex=0 nocomment=0 +for i do + case $i in + -delay) delay=1 ;; + -raw) raw=1 ;; + -localindex) if [ $noindex -eq 0 ]; then localindex=1; fi;; + -noindex) localindex=0; noindex=1 ;; + -no-gen-comment) nocomment=1 ;; + esac +done +nawk '<<awk program for conversion to {\tt HTML}>>' \ + delay=$delay raw=$raw localindex=$localindex noindex=$noindex nocomment=$nocomment +@ +The [[-raw]] option brackets HTML with [[\begin{rawhtml}]] and +[[\end{rawhtml}]]; the purpose is to embed HTML in a {\LaTeX} +document before converting the document with {\tt latex2html}. +[[braw]] and [[eraw]] hold those delimiters (or else empty strings). +<<awk program for conversion to {\tt HTML}>>= +<<functions>> +BEGIN { + <<initialization>> +} +!doneraw { # do not do in BEGIN because not all awks assign variables yet + if (raw) { braw = "\\begin{rawhtml}"; eraw = "\\end{rawhtml}" } + else braw = eraw = "" + doneraw = 1 + if (!nocomment) { + print braw "<!-- this file was generated automatically by noweave;" " better not edit it-->" eraw + } +} +<<patterns>> +END { print "" } +@ +[[ecode]] is the marker used at the end of the current code chunk. +If there is no cross-reference stuff at the end, we just use [[</pre>]]; +otherwise we terminate whatever environment is used for the cross-reference stuff. +<<patterns>>= +/^@begin code / { code = 1; printf "%s<pre>", braw; ecode = "</pre>" } +/^@end code / { code = 0; previscode = 1; <<dump pending cross-reference info>> + printf "%s%s", ecode, eraw + } +@ +We want to try to avoid emitting paragraph elements when the +preceding chunk is a code chunk, as tracked by [[previscode]]. +Also, if we do slip in a paragraph, we may use the {\LaTeX} style. +<<patterns>>= +/^@begin docs / { if (previscode) printf "%s", (raw ? "\\par" : "<p>") + previscode = text = 0 + } +@ +Sometimes it happens that a document-chunk anchor is put in a document chunk that +contains no text. In that case, we put in a phony anchor at the end of the chunk so +we won't lose the cross-reference. +<<patterns>>= +/^@end docs / { if (lastxreflabel != "") + printf "%s%s%s\n", braw, linklabel(lastxreflabel, "*"), eraw + lastxreflabel = "" + } +@ +Normally, if there's a pending anchor, we put it on the first available text line. +<<patterns>>= +/^@text / { line = substr($0, 7); text += length(line) + if (code) { + if (lastindexref != "" && line ~ /[^ \t]/) { + printf "%s", linkto(lastindexref, line) + lastindexref = "" + } else { + printf "%s", escapeSpecials(line) + } + } else if (quoting) { + if (line ~ /[^ \t]/) { + printf "%s", linklabelto(lastxreflabel, lastindexref, + escapeSpecials(line)) + lastindexref = lastxreflabel = "" + } else { + printf "%s", escapeSpecials(line) + } + } else { + if (lastxreflabel != "" && line ~ /[^ \t]/) { + <<print docs anchor>> + lastxreflabel = "" + } else { + printf "%s", line + } + } + } +@ +We anchor on the first nonblank character of the line, unless that's +a \TeX\ control sequence or an SGML tag. +In that case we insert a {\tt*} to anchor to. +None of this crap would be necessary if HTML could anchor to empty text. +<<print docs anchor>>= +match(line, /^[ \t]*/) +blanks = substr(line, RSTART, RLENGTH) +line = substr(line, RSTART+RLENGTH) +if (line ~ /^[{}\\<&]/) { + char = "*" +} else { + char = substr(line, 1, 1) + line = substr(line, 2) +} +printf "%s%s%s%s%s", braw, blanks, linklabel(lastxreflabel, char), eraw, line +if (lastxreflabel != "") defns_above[lastxreflabel] = 1 +<<patterns>>= +/^@nl$/ { print "" } +/^@defn / { thischunk = name = substr($0, 7) + if (lastxreflabel != "") defns_above[lastxreflabel] = 1 + writechunk(lastxreflabel, lastxrefref, "dfn", name, defns[name] "=") + <<clear [[lastxref*]]>> + defns[name] = "+" + } +<<initialization>>= +defns[0] = 0 +defns_above[0] = 0 +<<patterns>>= +/^@use / { writechunk(lastxreflabel, lastxrefref, "i", substr($0, 6), "") } +@ +Writing a chunk involves creating an anchor for it. +<<functions>>= +function writechunk(label, ref, tag, name, suffix) { + printf "%s", + linklabelto(label, ref, sgmlwrap(tag, "<" convquotes(name) ">" suffix)) +} +@ +<<patterns>>= +/^@quote$/ { quoting = 1 ; printf "%s<code>", braw } +/^@endquote$/ { quoting = 0 ; printf "</code>%s", eraw } +/^@file / { filename = substr($0, 7); <<clear [[lastxref*]]>> } +/^@literal / { printf "%s", substr($0, 10) } +/^@header html / { <<write HTML header>> } +/^@trailer html$/ { <<write HTML trailer>> } +@ +<<write HTML header>>= +printf "<html><head><title>%s</title></head><body>", substr($0, 14) + +<<write HTML trailer>>= +print "</body></html>" +@ +<<patterns>>= +/^@xref label / { lastxreflabel = substr($0, 13) } +/^@xref ref / { lastxrefref = substr($0, 11) } +/^@xref prevdef/ { pendingprev = substr($0, 15) } +/^@xref nextdef/ { pendingnext = substr($0, 15) } +/^@xref beginuses/ { useitems = "" } +/^@xref useitem / { useitems = useitems " " substr($0, 15) } +/^@xref enduses/ { useitemstab[thischunk] = useitems } +/^@xref notused / { <<code-to-blockquote>> + printf "This code is written to a file (or else not used).<p>" + } +<<initialization>>= +useitemstab[0] = 0 +<<clear [[lastxref*]]>>= +lastxreflabel = lastxrefref = "" +<<dump pending cross-reference info>>= +useitemscount = split(useitemstab[thischunk], a) +if (pendingprev != "" || pendingnext != "" || useitemscount > 0) { + <<code-to-blockquote>> + <<write out uses with links>> + if (useitemscount > 0 && (pendingprev != "" || pendingnext != "")) + printf "; " + p = useitemscount > 0 ? "previous" : "Previous" + n = useitemscount > 0 ? "next" : "Next" + if (pendingprev != "") + if (pendingnext != "") + printf "%s and %s definitions", linkto(pendingprev, p), linkto(pendingnext, "next") + else + printf "%s definition", linkto(pendingprev, p) + else + if (pendingnext != "") + printf "%s definition", linkto(pendingnext, n) + pendingprev = pendingnext = "" + useitems = "" + print ".<p>" +} +<<write out uses with links>>= +useprefix = "Used " +for (j = 1; j <= useitemscount; j++) { + if (defns_above[a[j]] > 0) + usedir = "above" + else + usedir = "below" + printf "%s%s", useprefix, linkto(a[j], usedir (useitemscount > 1 ? " (" j ")" : "")) + useprefix = ", " +} +@ +The hack here is to put the supplementary information in a blockquote area +after the code. +<<code-to-blockquote>>= +if (ecode == "</pre>") { + printf "</pre><blockquote>" + ecode = "</blockquote>" +} +@ +The HTML back end ignores [[@xref begindefs]], [[@xref defitem]], and +[[@xref enddefs]]; it uses the [[nextdef]] and [[prevdef]] links instead. +<<patterns>>= +/^@xref (begindefs|defitem|enddefs)/ { } +/^@xref beginchunks$/ { printf "%s<ul>\n", braw } +/^@xref chunkbegin / { label = $3; name = substr($0, 19 + length(label)) + printf "<li>"; comma = ": "; count = 0 + writechunk("", label, "i", name, "") + } +/^@xref chunkuse / { printf "%s%s", comma, linkto(substr($0, 16), "U" ++count) + comma = ", " + } +/^@xref chunkdefn / { printf "%s%s", comma, linkto(substr($0, 17), "D" ++count) + comma = ", " + } +/^@xref chunkend$/ { print "" } +/^@xref endchunks$/ { printf "</ul>%s\n", eraw } +<<patterns>>= +/^@index beginindex$/ { if (!noindex) { printf "%s<ul>\n", braw } } +/^@index entrybegin / { if (!noindex) { + label = $3; name = substr($0, 20 + length(label)) + printf "<li>"; comma = ": "; count = 0 + printf "%s", + linklabelto("NWI-" escapeSpecials(name), label, name) + + } } +/^@index entryuse / { if (!noindex) { + printf "%s%s", comma, linkto(substr($0, 17), "U" ++count) + comma = ", " + } } +/^@index entrydefn / { if (!noindex) { + printf "%s%s", comma, linkto(substr($0, 18), "D" ++count) + comma = ", " + } } +/^@index entryend$/ { if (!noindex) { print "" } } +/^@index endindex$/ { if (!noindex) { printf "</ul>%s\n", eraw } } +@ +The local identifier cross-reference doesn't show each use; it just shows +the identifiers that are defined, with links to the full index. +<<patterns>>= +/^@index use/ { lastindexref = lastxrefref; lastxrefref = "" } +/^@index defn/ { <<clear [[lastxref*]]>> } +/^@index localdefn/ { <<clear [[lastxref*]]>> } +/^@index nl/ { } # do nothing -- destroys line numbering +/^@index begindefs/ { if (localindex) { + <<code-to-blockquote>>; printf "Defines"; comma = " " +} } +/^@index isused / { } +/^@index defitem / { if (localindex) { + arg = substr($0, 16) + printf "%s%s", comma, + linkto("NWI-" escapeSpecials(arg), sgmlwrap("code", escapeSpecials(arg))) + comma = ", " +} } +/^@index enddefs/ { if (localindex) { printf " (links are to index).<p>\n" } } +/^@index (beginuses|isdefined|useitem|enduses)/ { } # use local links +@ +\subsection{Support functions} +Here's all our anchor support goo. +<<functions>>= +function linklabelto(label, ref, contents, s) { + s = label != "" || ref != "" ? "<a" : "" + if (label != "") s = s " name=" image(label) + if (ref != "") s = s " href=" image("#" ref) + s = s (label != "" || ref != "" ? ">" : "") + s = s contents + s = s (label != "" || ref != "" ? "</a>" : "") + return s +} + +function linkto(ref, contents) { + return linklabelto("", ref, contents) +} + +function linklabel(label, contents) { + return linklabelto(label, "", contents) +} +@ +Another support function is used for wrapping tags around text: +<<functions>>= +function sgmlwrap(tag, s) { + return "<" tag ">" s "</" tag ">" +} +<<functions>>= +function image(s) { + gsub(/"/, "\\\"", s) + return "\"" s "\"" +} +@ +Lucky for us, {\tt HTML} has few special characters. Unlucky for us, +we have to deal with each one seperately. Nothing much to whine +about, really. +<<functions>>= +function escapeSpecials (l) { + gsub(/&/, "\\&", l) + gsub(/</, "\\<", l) + gsub(/>/, "\\>", l) + gsub(/"/, "\\"", l) + return l +} +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<functions>>= +function convquotes(s, r, i, line) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "<code>" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + line = substr(s, 1, i-1+RLENGTH-2) + # line = escapeSpecials(line) # destroys internal markup --- do not call + r = r line "</code>" + s = substr(s, i+RLENGTH) + } else { + r = r s "</code>" + s = "" + } + } + return r s +} +@ +\end{document} diff --git a/web/noweb/src/awk/totex b/web/noweb/src/awk/totex new file mode 100755 index 0000000000..ac74b82695 --- /dev/null +++ b/web/noweb/src/awk/totex @@ -0,0 +1,175 @@ +#!/bin/sh +LIB=|LIBDIR| +# Copyright 1991-2018 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# Don't try to understand this file! Look at lib/totex.nw in the noweb source! +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to totex" 1>&2 ; exit 1 ;; + esac +done +nawk 'BEGIN { code=0 ; quoting=0 ; text=1; defns[0] = 0 + ulist[0] = 0 } + /^@begin code/ { code=1 ; printf "\\nwbegincode{%s}", substr($0, 13) } + /^@end code/ { code=0 ; printf "\\nwendcode{}"; lastdefnlabel = "" } + /^@begin docs 0$/ { if (delay) next } + /^@end docs 0$/ { if (delay) { + printf "\\nwfilename{%s}", filename; delay=0; next + } } + /^@begin docs/ { text=0 ; printf "\\nwbegindocs{%s}", substr($0, 13) } + /^@end docs/ { printf "\\nwenddocs{}" } + /^@text / { line = substr($0, 7) ; text += length - 6 + if (code) printf "%s", escape_brace_bslash(line) + else if (quoting) printf "%s", TeXliteral(line) + else printf "%s", line + } + /^@nl$/ { if (!code) {if (text==0) printf "\\nwdocspar" + text=1} + if (quoting) printf "\\nwnewline" + printf "\n" + } + /^@defn / { name = substr($0, 7); if (lastxreflabel != "") { + printf "\\sublabel{%s}", lastxreflabel + printf "\\nwmargintag{%s}", label2tag(lastxreflabel) + } + printf "\\moddef{%s%s}\\%sendmoddef", convquotes(name), (lastxrefref != "" ? ("~" label2tag(lastxrefref)) : ""), defns[name] + lastdefnlabel = lastxreflabel + lastxreflabel = lastxrefref = "" + defns[name] = "plus" } + /^@use / { printf "\\LA{}%s%s\\RA{}", + convquotes(substr($0, 6)), (lastxrefref != "" ? ("~" label2tag(lastxrefref)) : "") + } + /^@quote$/ { quoting = 1 ; printf "{\\tt{}" } + /^@endquote$/ { quoting = 0 ; printf "}" } + /^@file / { filename = substr($0, 7); lastxreflabel = lastxrefref = "" + if (!delay) printf "\\nwfilename{%s}", filename + } + /^@literal / { printf "%s", substr($0, 10) } + /^@header latex / { printf "\\documentclass{article}\\usepackage{noweb}\\pagestyle{noweb}\\noweboptions{%s}%s", + substr($0, 15), "\\begin{document}" } + /^@header tex / { printf "\\input nwmac " } + /^@trailer latex$/ { print "\\end{document}" } + /^@trailer tex$/ { print "\\bye" } + /^@xref label / { lastxreflabel = substr($0, 13) } + /^@xref ref / { lastxrefref = substr($0, 11) } + /^@xref begindefs$/ { printf "\\nwalsodefined{" } + /^@xref defitem / { printf "\\\\{%s}", substr($0, 15) } + /^@xref enddefs$/ { printf "}" } + /^@xref beginuses$/ { printf "\\nwused{" } + /^@xref useitem / { printf "\\\\{%s}", substr($0, 15) } + /^@xref enduses$/ { printf "}" } + /^@xref notused / { printf "\\nwnotused{%s}", TeXliteral(substr($0, 15)) } + /^@xref nextdef / { } + /^@xref prevdef / { } + /^@xref beginchunks$/ { } + /^@xref chunkbegin / { label = $3; name = substr($0, 19 + length(label)) + printf "\\nwixlogsorted{c}{{%s}{%s}{", + convquotes(name), label + } + /^@xref chunkuse / { printf "\\nwixu{%s}", substr($0, 16) } + /^@xref chunkdefn / { printf "\\nwixd{%s}", substr($0, 17) } + /^@xref chunkend$/ { print "}}%" } + /^@xref endchunks$/ { } + /^@index nl$/ { print (code ? "\\eatline" : "%") } + /^@index defn / { + if (!noindex) { arg = substr($0, 13); if (lastxreflabel != "") printf "\\nosublabel{%s}", lastxreflabel + if (lastxrefref != "") + printf "\\nwindexdefn{%s}{%s}{%s}", TeXliteral(arg), indexlabel(arg), lastxrefref + lastxreflabel = lastxrefref = "" } } + /^@index localdefn / { + if (!noindex) { arg = substr($0, 18); if (lastxreflabel != "") printf "\\nosublabel{%s}", lastxreflabel + if (lastxrefref != "") + printf "\\nwindexdefn{%s}{%s}{%s}", TeXliteral(arg), indexlabel(arg), lastxrefref + lastxreflabel = lastxrefref = "" } } + /^@index use / { + if (!noindex) { arg = substr($0, 12); if (!code) { + if (lastxreflabel != "") printf "\\protect\\nosublabel{%s}", lastxreflabel + if (lastxrefref != "") + printf "\\protect\\nwindexuse{%s}{%s}{%s}", + TeXliteral(arg), indexlabel(arg), lastxrefref + } + lastxreflabel = lastxrefref = "" } } + /^@index begindefs$/ { if (!noindex) { printf "\\nwidentdefs{" } } + /^@index isused / { if (!noindex) { } } # handled by latex + /^@index defitem / { if (!noindex) { i = substr($0,16); printf "\\\\{{%s}{%s}}", TeXliteral(i), indexlabel(i) } } + /^@index enddefs$/ { if (!noindex) { printf "}" } } + /^@index beginuses$/ { if (!noindex) { printf "\\nwidentuses{"; ucount = 0 } } + /^@index isdefined / { if (!noindex) { } } # latex finds the definitions + /^@index useitem / { if (!noindex) { i = substr($0, 16); printf "\\\\{{%s}{%s}}", TeXliteral(i), indexlabel(i) + ulist[ucount++] = i + } } + /^@index enduses$/ { if (!noindex) { printf "}"; if (lastdefnlabel != "") { + for (j = 0; j < ucount; j++) + printf "\\nwindexuse{%s}{%s}{%s}", + TeXliteral(ulist[j]), indexlabel(ulist[j]), lastdefnlabel + } } } + /^@index beginindex$/ { if (!noindex) { } } + /^@index entrybegin / { if (!noindex) { label = $3; name = substr($0, 20 + length(label)) + printf "\\nwixlogsorted{i}{{%s}{%s}}%%\n", + TeXliteral(name), indexlabel(name) + } } + /^@index entryuse / { if (!noindex) { } } # handled by latex + /^@index entrydefn / { if (!noindex) { } } # handled by latex + /^@index entryend$/ { if (!noindex) { } } + /^@index endindex$/ { if (!noindex) { } } + + END { printf "\n" } + function label2tag(label) { + return "{\\nwtagstyle{}\\subpageref{" label "}}" + } + function escape_brace_bslash(line) { + gsub(/[\\{}]/, "\n&", line) + gsub(/\n/, "\\", line) + return line + } + function convquotes(s, r, i) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "\\code{}" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + r = r TeXliteral(substr(s, 1, i-1+RLENGTH-2)) "\\edoc{}" + s = substr(s, i+RLENGTH) + } else { + r = r s "\\edoc{}" + s = "" + } + } + return r s + } + function indexlabel(ident, l) { + l = ident + gsub(/:/, ":col", l) # must be first (colon) + gsub(/ /, ":sp", l) # space + gsub(/#/, ":has", l) # hash + gsub(/\$/, ":do", l) # dollar + gsub(/%/, ":pe", l) # percent + gsub(/&/, ":am", l) # ampersand + gsub(/,/, ":com", l) # commad + gsub(/\\/, ":bs", l) # backslash + gsub(/\^/, ":hat", l) # hat + gsub(/_/, ":un", l) # underscore + gsub(/{/, ":lb", l) # left brace + gsub(/}/, ":rb", l) # right brace + gsub(/~/, ":ti", l) # tilde + return l + } + function TeXliteral(arg) { + gsub(/\\/, "<\\char92>", arg) + gsub(/}/, "<\\char125}", arg) + gsub(/{/, "{\\char123}", arg) + gsub(/<\\char/, "{\\char", arg) + gsub(/{\\char92>/, "{\\char92}", arg) + gsub(/\$/, "{\\char36}", arg) + gsub(/&/, "{\\char38}", arg) + gsub(/#/, "{\\char35}", arg) + gsub(/\^/, "{\\char94}", arg) + gsub(/_/, "{\\char95}", arg) + gsub(/%/, "{\\char37}", arg) + gsub(/~/, "{\\char126}", arg) + gsub(/ /, "\\ ", arg) + return arg + }' delay=$delay noindex=$noindex diff --git a/web/noweb/src/awk/totex.nw b/web/noweb/src/awk/totex.nw new file mode 100644 index 0000000000..f0ccd3f23e --- /dev/null +++ b/web/noweb/src/awk/totex.nw @@ -0,0 +1,287 @@ +\section{Converting {\tt noweb} markup to {\TeX} markup} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991-2018 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +<<totex>>= +#!/bin/sh +LIB=|LIBDIR| +<<copyright notice>> +# Don't try to understand this file! Look at lib/totex.nw in the noweb source! +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to totex" 1>&2 ; exit 1 ;; + esac +done +<<invoke awk program using parameter>> +@ +On a forgiving system, we make the awk program an argument: +<<invoke awk program using parameter>>= +nawk '<<awk program for conversion to {\TeX}>>' delay=$delay noindex=$noindex +@ +On an ugly system, we have to put it in a file. +<<invoke awk program using file>>= +awkfile=$($LIB/nwmktemp) +trap 'rm -f $awkfile; exit 1' 0 1 2 15 # clean up files +cat > $awkfile << 'EOF' +<<awk program for conversion to {\TeX}>> +EOF +nawk -f $awkfile delay=$delay noindex=$noindex +@ +The markup carefully adds no newlines not already present in the input, +so that the line numbers of the {\TeX} file will be the same as the +numbers of the corresponding {\tt noweb} file. +The variables are: +\begin{description} +\item[\tt code] Nonzero if converting a code chunk. +\item[\tt quoting] Nonzero if quoting code in documentation. +\item[\tt text] Number of characters written since start of + documentation chunk. +\end{description} +[[text]] is used to write [[\par]] if a newline appears at the +beginning of a documentation chunk without any intervening text. +This subtle trick preserves new-paragraph semantics without requiring +the insertion of a blank line that would throw off the line count. +<<awk program for conversion to {\TeX}>>= +BEGIN { code=0 ; quoting=0 ; text=1; <<initialization>> } +/^@begin code/ { code=1 ; printf "\\nwbegincode{%s}", substr($0, 13) } +/^@end code/ { code=0 ; printf "\\nwendcode{}"; lastdefnlabel = "" } +<<special patterns for document chunk 0>> +/^@begin docs/ { text=0 ; printf "\\nwbegindocs{%s}", substr($0, 13) } +/^@end docs/ { printf "\\nwenddocs{}" } +/^@text / { line = substr($0, 7) ; text += length - 6 + if (code) printf "%s", escape_brace_bslash(line) + else if (quoting) printf "%s", TeXliteral(line) + else printf "%s", line + } +/^@nl$/ { if (!code) {<<print [[\nwdocspar]] if no text>>} + if (quoting) printf "\\nwnewline" + printf "\n" + } +/^@defn / { name = substr($0, 7); <<defn of [[name]], with cross-reference>> } +/^@use / { printf "\\LA{}%s%s\\RA{}", + convquotes(substr($0, 6)), <<optional ref tag>> + } +/^@quote$/ { quoting = 1 ; printf "{\\tt{}" } +/^@endquote$/ { quoting = 0 ; printf "}" } +/^@file / { filename = substr($0, 7); <<clear [[lastxref*]]>> + if (!delay) printf "\\nwfilename{%s}", filename + } +/^@literal / { printf "%s", substr($0, 10) } +/^@header latex / { <<write {\LaTeX} header>> } +/^@header tex / { printf "\\input nwmac " } +/^@trailer latex$/ { print "\\end{document}" } +/^@trailer tex$/ { print "\\bye" } +<<xref patterns>> +<<index patterns>> +END { printf "\n" } +<<functions>> +@ +<<print [[\nwdocspar]] if no text>>= +if (text==0) printf "\\nwdocspar" +text=1 +@ +Delaying markup is handled by special patterns for the first document chunk. +Because several {\tt noweb} files can be marked up at once, there can be +several document chunks numbered 0. +The later ones are given no special treatment by the simple expedient of +turning [[delay]] off after the first one. +<<special patterns for document chunk 0>>= +/^@begin docs 0$/ { if (delay) next } +/^@end docs 0$/ { if (delay) { + printf "\\nwfilename{%s}", filename; delay=0; next + } } +@ +<<defn of [[name]], with cross-reference>>= +if (lastxreflabel != "") { + printf "\\sublabel{%s}", lastxreflabel + printf "\\nwmargintag{%s}", label2tag(lastxreflabel) +} +printf "\\moddef{%s%s}\\%sendmoddef", convquotes(name), <<optional ref tag>>, defns[name] +lastdefnlabel = lastxreflabel +<<clear [[lastxref*]]>> +defns[name] = "plus" +<<optional ref tag>>= +(lastxrefref != "" ? ("~" label2tag(lastxrefref)) : "") +<<functions>>= +function label2tag(label) { + return "{\\nwtagstyle{}\\subpageref{" label "}}" +} +<<initialization>>= +defns[0] = 0 +@ +<<write {\LaTeX} header>>= +printf "\\documentclass{article}\\usepackage{noweb}\\pagestyle{noweb}\\noweboptions{%s}%s", + substr($0, 15), "\\begin{document}" +@ +\subsection{Cross-reference and index support} +<<xref patterns>>= +/^@xref label / { lastxreflabel = substr($0, 13) } +/^@xref ref / { lastxrefref = substr($0, 11) } +/^@xref begindefs$/ { printf "\\nwalsodefined{" } +/^@xref defitem / { printf "\\\\{%s}", substr($0, 15) } +/^@xref enddefs$/ { printf "}" } +/^@xref beginuses$/ { printf "\\nwused{" } +/^@xref useitem / { printf "\\\\{%s}", substr($0, 15) } +/^@xref enduses$/ { printf "}" } +/^@xref notused / { printf "\\nwnotused{%s}", TeXliteral(substr($0, 15)) } +/^@xref nextdef / { } +/^@xref prevdef / { } +<<clear [[lastxref*]]>>= +lastxreflabel = lastxrefref = "" +<<index patterns>>= +/^@index nl$/ { print (code ? "\\eatline" : "%") } +/^@index defn / { + if (!noindex) { arg = substr($0, 13); <<handle index defn of [[arg]]>> } } +/^@index localdefn / { + if (!noindex) { arg = substr($0, 18); <<handle index defn of [[arg]]>> } } +/^@index use / { + if (!noindex) { arg = substr($0, 12); <<handle index use of [[arg]]>> } } +@ +Nothing is involved in handling definitions and uses unless there are cross-reference +labels pending. +An index definition or use has its own [[@xref label]] only if it's in documentation; +if it's in code, we use the anchor label of the definition. +(You don't have to know that to understand what happens here, but I thought you +might like to.) +<<handle index defn of [[arg]]>>= +if (lastxreflabel != "") printf "\\nosublabel{%s}", lastxreflabel +if (lastxrefref != "") + printf "\\nwindexdefn{%s}{%s}{%s}", TeXliteral(arg), indexlabel(arg), lastxrefref +<<clear [[lastxref*]]>> +@ +The {\LaTeX} back end ignores uses in code; they get bundled up by a previous filter +(the cross-referencer) and handled elsewhere. +<<handle index use of [[arg]]>>= +if (!code) { + if (lastxreflabel != "") printf "\\protect\\nosublabel{%s}", lastxreflabel + if (lastxrefref != "") + printf "\\protect\\nwindexuse{%s}{%s}{%s}", + TeXliteral(arg), indexlabel(arg), lastxrefref +} +<<clear [[lastxref*]]>> +@ +Here's the local identifier cross-reference that appears at the end of a code chunk. +We guard everything with \LA{}SI\RA, as before. +<<index patterns>>= +/^@index begindefs$/ { if (!noindex) { printf "\\nwidentdefs{" } } +/^@index isused / { if (!noindex) { } } # handled by latex +/^@index defitem / { if (!noindex) { i = substr($0,16); <<write [[i]] with [[\\]]>> } } +/^@index enddefs$/ { if (!noindex) { printf "}" } } +/^@index beginuses$/ { if (!noindex) { printf "\\nwidentuses{"; ucount = 0 } } +/^@index isdefined / { if (!noindex) { } } # latex finds the definitions +/^@index useitem / { if (!noindex) { i = substr($0, 16); <<write [[i]] with [[\\]]>> + ulist[ucount++] = i + } } +/^@index enduses$/ { if (!noindex) { printf "}"; <<write [[ulist]]>> } } +<<initialization>>= +ulist[0] = 0 +<<write [[i]] with [[\\]]>>= +printf "\\\\{{%s}{%s}}", TeXliteral(i), indexlabel(i) +<<write [[ulist]]>>= +if (lastdefnlabel != "") { + for (j = 0; j < ucount; j++) + printf "\\nwindexuse{%s}{%s}{%s}", + TeXliteral(ulist[j]), indexlabel(ulist[j]), lastdefnlabel +} +@ +\subsubsection{The list of chunks and the index} +The treatments of the list of chunks and the index are similar. +Both use [[\nwixlogsorted]], which writes magic goo into the {\tt .aux} file. +The real cross-referencing is done by the underlying {\LaTeX} code. +<<xref patterns>>= +/^@xref beginchunks$/ { } +/^@xref chunkbegin / { label = $3; name = substr($0, 19 + length(label)) + printf "\\nwixlogsorted{c}{{%s}{%s}{", + convquotes(name), label + } +/^@xref chunkuse / { printf "\\nwixu{%s}", substr($0, 16) } +/^@xref chunkdefn / { printf "\\nwixd{%s}", substr($0, 17) } +/^@xref chunkend$/ { print "}}%" } +/^@xref endchunks$/ { } +<<index patterns>>= +/^@index beginindex$/ { if (!noindex) { } } +/^@index entrybegin / { if (!noindex) { label = $3; name = substr($0, 20 + length(label)) + printf "\\nwixlogsorted{i}{{%s}{%s}}%%\n", + TeXliteral(name), indexlabel(name) + } } +/^@index entryuse / { if (!noindex) { } } # handled by latex +/^@index entrydefn / { if (!noindex) { } } # handled by latex +/^@index entryend$/ { if (!noindex) { } } +/^@index endindex$/ { if (!noindex) { } } + +@ +\subsection{Miscellany} +I first insert a newline before the special characters, then change the +newline to a backslash. I can't do the backslash directly because +[[\&]] means a literal ampersand. +<<functions>>= +function escape_brace_bslash(line) { + gsub(/[\\{}]/, "\n&", line) + gsub(/\n/, "\\", line) + return line +} +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<functions>>= +function convquotes(s, r, i) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "\\code{}" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + r = r TeXliteral(substr(s, 1, i-1+RLENGTH-2)) "\\edoc{}" + s = substr(s, i+RLENGTH) + } else { + r = r s "\\edoc{}" + s = "" + } + } + return r s +} +<<functions>>= +function indexlabel(ident, l) { + l = ident + gsub(/:/, ":col", l) # must be first (colon) + gsub(/ /, ":sp", l) # space + gsub(/#/, ":has", l) # hash + gsub(/\$/, ":do", l) # dollar + gsub(/%/, ":pe", l) # percent + gsub(/&/, ":am", l) # ampersand + gsub(/,/, ":com", l) # commad + gsub(/\\/, ":bs", l) # backslash + gsub(/\^/, ":hat", l) # hat + gsub(/_/, ":un", l) # underscore + gsub(/{/, ":lb", l) # left brace + gsub(/}/, ":rb", l) # right brace + gsub(/~/, ":ti", l) # tilde + return l +} +@ %def indexlabel +@ +Because latex2e uses [[`]] as an active character, I have to use +decimal character codes for the specials. +<<functions>>= +function TeXliteral(arg) { + gsub(/\\/, "<\\char92>", arg) + gsub(/}/, "<\\char125}", arg) + gsub(/{/, "{\\char123}", arg) + gsub(/<\\char/, "{\\char", arg) + gsub(/{\\char92>/, "{\\char92}", arg) + gsub(/\$/, "{\\char36}", arg) + gsub(/&/, "{\\char38}", arg) + gsub(/#/, "{\\char35}", arg) + gsub(/\^/, "{\\char94}", arg) + gsub(/_/, "{\\char95}", arg) + gsub(/%/, "{\\char37}", arg) + gsub(/~/, "{\\char126}", arg) + gsub(/ /, "\\ ", arg) + return arg +} +@ %def TeXliteral + diff --git a/web/noweb/src/awk/totex.nw.bad b/web/noweb/src/awk/totex.nw.bad new file mode 100644 index 0000000000..aa99ff290c --- /dev/null +++ b/web/noweb/src/awk/totex.nw.bad @@ -0,0 +1,292 @@ +\section{Converting {\tt noweb} markup to {\TeX} markup} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +<<totex>>= +#!/bin/sh +<<copyright notice>> +# Don't try to understand this file! Look at lib/totex.nw in the noweb source! +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to totex" 1>&2 ; exit 1 ;; + esac +done +<<invoke awk program using parameter>> +@ +On a forgiving system, we make the awk program an argument: +<<invoke awk program using parameter>>= +nawk '<<awk program for conversion to {\TeX}>>' delay=$delay noindex=$noindex +@ +On an ugly system, we have to put it in a file. +<<invoke awk program using file>>= +awkfile=/tmp/totex$$.awk +trap 'rm -f $awkfile; exit 1' 0 1 2 15 # clean up files +cat > $awkfile << 'EOF' +<<awk program for conversion to {\TeX}>> +EOF +nawk -f $awkfile delay=$delay noindex=$noindex +@ +The markup carefully adds no newlines not already present in the input, +so that the line numbers of the {\TeX} file will be the same as the +numbers of the corresponding {\tt noweb} file. +The variables are: +\begin{description} +\item[\tt code] Nonzero if converting a code chunk. +\item[\tt quoting] Nonzero if quoting code in documentation. +\item[\tt text] Number of characters written since start of + documentation chunk. +\end{description} +[[text]] is used to write [[\par]] if a newline appears at the +beginning of a documentation chunk without any intervening text. +This subtle trick preserves new-paragraph semantics without requiring +the insertion of a blank line that would throw off the line count. +<<awk program for conversion to {\TeX}>>= +BEGIN { code=0 ; quoting=0 ; text=1; <<initialization>> } +/^@begin code/ { code=1 ; printf "\\nwbegincode{%s}", substr($0, 13) } +/^@end code/ { code=0 ; printf "\\nwendcode{}"; lastdefnlabel = "" } +<<special patterns for document chunk 0>> +/^@begin docs/ { text=0 ; printf "\\nwbegindocs{%s}", substr($0, 13) } +/^@end docs/ { printf "\\nwenddocs{}" } +/^@text / { line = substr($0, 7) ; text += length - 6 + if (code) printf "%s", escape_brace_bslash(line) + else if (quoting) printf "%s", TeXliteral(line) + else printf "%s", line + } +/^@nl$/ { if (!code) {<<print [[\nwdocspar]] if no text>>} + if (quoting) printf "\\nwnewline" + printf "\n" + } +/^@defn / { name = substr($0, 7); <<defn of [[name]], with cross-reference>> } +/^@use / { <<set [[optreftag]]>> + printf "\\LA{}%s%s\\RA{}", \ + convquotes(substr($0, 6)), optreftag + } +/^@quote$/ { quoting = 1 ; printf "{\\tt{}" } +/^@endquote$/ { quoting = 0 ; printf "}" } +/^@file / { filename = substr($0, 7); <<clear [[lastxref*]]>> + if (!delay) printf "\\nwfilename{%s}", filename + } +/^@literal / { printf "%s", substr($0, 10) } +/^@header latex / { <<write {\LaTeX} header>> } +/^@header tex / { printf "\\input nwmac " } +/^@trailer latex$/ { print "\\end{document}" } +/^@trailer tex$/ { print "\\bye" } +<<xref patterns>> +<<index patterns>> +END { printf "\n" } +<<functions>> +@ +<<print [[\nwdocspar]] if no text>>= +if (text==0) printf "\\nwdocspar" +text=1 +@ +Delaying markup is handled by special patterns for the first document chunk. +Because several {\tt noweb} files can be marked up at once, there can be +several document chunks numbered 0. +The later ones are given no special treatment by the simple expedient of +turning [[delay]] off after the first one. +<<special patterns for document chunk 0>>= +/^@begin docs 0$/ { if (delay) next } +/^@end docs 0$/ { if (delay) { + printf "\\nwfilename{%s}", filename; delay=0; next + } } +@ +<<defn of [[name]], with cross-reference>>= +if (lastxreflabel != "") { + printf "\\sublabel{%s}", lastxreflabel + printf "\\nwmargintag{%s}", label2tag(lastxreflabel) +} + +<<set [[optreftag]]>> +printf "\\moddef{%s%s}\\%sendmoddef", convquotes(name), optreftag, defns[name] +lastdefnlabel = lastxreflabel +<<clear [[lastxref*]]>> +defns[name] = "plus" +<<set [[optreftag]]>>= +if (lastxrefref != "") + optreftag = "~" label2tag(lastxrefref) +else + optreftag = "" +<<functions>>= +function label2tag(label) { + return "{\\nwtagstyle{}\\subpageref{" label "}}" +} +<<initialization>>= +defns[0] = 0 +@ +<<write {\LaTeX} header>>= +printf "\\documentclass{article}\\usepackage{noweb}\\pagestyle{noweb}\\noweboptions{%s}%s", substr($0, 15), "\\begin{document}" +@ +\subsection{Cross-reference and index support} +<<xref patterns>>= +/^@xref label / { lastxreflabel = substr($0, 13) } +/^@xref ref / { lastxrefref = substr($0, 11) } +/^@xref begindefs$/ { printf "\\nwalsodefined{" } +/^@xref defitem / { printf "\\\\{%s}", substr($0, 15) } +/^@xref enddefs$/ { printf "}" } +/^@xref beginuses$/ { printf "\\nwused{" } +/^@xref useitem / { printf "\\\\{%s}", substr($0, 15) } +/^@xref enduses$/ { printf "}" } +/^@xref notused / { printf "\\nwnotused{%s}", TeXliteral(substr($0, 15)) } +/^@xref nextdef / { } +/^@xref prevdef / { } +<<clear [[lastxref*]]>>= +lastxreflabel = lastxrefref = "" +<<index patterns>>= +/^@index nl$/ { if (code) print "\\eatline" + else print "%" } +/^@index defn / { + if (!noindex) { arg = substr($0, 13); <<handle index defn of [[arg]]>> } } +/^@index localdefn / { + if (!noindex) { arg = substr($0, 18); <<handle index defn of [[arg]]>> } } +/^@index use / { + if (!noindex) { arg = substr($0, 12); <<handle index use of [[arg]]>> } } +@ +Nothing is involved in handling definitions and uses unless there are cross-reference +labels pending. +An index definition or use has its own [[@xref label]] only if it's in documentation; +if it's in code, we use the anchor label of the definition. +(You don't have to know that to understand what happens here, but I thought you +might like to.) +<<handle index defn of [[arg]]>>= +if (lastxreflabel != "") printf "\\nosublabel{%s}", lastxreflabel +if (lastxrefref != "") + printf "\\nwindexdefn{%s}{%s}{%s}", TeXliteral(arg), indexlabel(arg), lastxrefref +<<clear [[lastxref*]]>> +@ +The {\LaTeX} back end ignores uses in code; they get bundled up by a previous filter +(the cross-referencer) and handled elsewhere. +<<handle index use of [[arg]]>>= +if (!code) { + if (lastxreflabel != "") printf "\\protect\\nosublabel{%s}", lastxreflabel + if (lastxrefref != "") + printf "\\protect\\nwindexuse{%s}{%s}{%s}", \ + TeXliteral(arg), indexlabel(arg), lastxrefref +} +<<clear [[lastxref*]]>> +@ +Here's the local identifier cross-reference that appears at the end of a code chunk. +We guard everything with \LA{}SI\RA, as before. +<<index patterns>>= +/^@index begindefs$/ { if (!noindex) { printf "\\nwidentdefs{" } } +/^@index isused / { if (!noindex) { } } # handled by latex +/^@index defitem / { if (!noindex) { i = substr($0,16); <<write [[i]] with [[\\]]>> } } +/^@index enddefs$/ { if (!noindex) { printf "}" } } +/^@index beginuses$/ { if (!noindex) { printf "\\nwidentuses{"; ucount = 0 } } +/^@index isdefined / { if (!noindex) { } } # latex finds the definitions +/^@index useitem / { if (!noindex) { i = substr($0, 16); <<write [[i]] with [[\\]]>> + ulist[ucount++] = i + } } +/^@index enduses$/ { if (!noindex) { printf "}"; <<write [[ulist]]>> } } +<<initialization>>= +ulist[0] = 0 +<<write [[i]] with [[\\]]>>= +printf "\\\\{{%s}{%s}}", TeXliteral(i), indexlabel(i) +<<write [[ulist]]>>= +if (lastdefnlabel != "") { + for (j = 0; j < ucount; j++) + printf "\\nwindexuse{%s}{%s}{%s}", \ + TeXliteral(ulist[j]), indexlabel(ulist[j]), lastdefnlabel +} +@ +\subsubsection{The list of chunks and the index} +The treatments of the list of chunks and the index are similar. +Both use [[\nwixlogsorted]], which writes magic goo into the {\tt .aux} file. +The real cross-referencing is done by the underlying {\LaTeX} code. +<<xref patterns>>= +/^@xref beginchunks$/ { } +/^@xref chunkbegin / { label = $3; name = substr($0, 19 + length(label)) + printf "\\nwixlogsorted{c}{{%s}{%s}{", \ + convquotes(name), label + } +/^@xref chunkuse / { printf "\\nwixu{%s}", substr($0, 16) } +/^@xref chunkdefn / { printf "\\nwixd{%s}", substr($0, 17) } +/^@xref chunkend$/ { print "}}%" } +/^@xref endchunks$/ { } +<<index patterns>>= +/^@index beginindex$/ { if (!noindex) { } } +/^@index entrybegin / { if (!noindex) { label = $3; name = substr($0, 20 + length(label)) + printf "\\nwixlogsorted{i}{{%s}{%s}}%%\n", \ + TeXliteral(name), indexlabel(name) + } } +/^@index entryuse / { if (!noindex) { } } # handled by latex +/^@index entrydefn / { if (!noindex) { } } # handled by latex +/^@index entryend$/ { if (!noindex) { } } +/^@index endindex$/ { if (!noindex) { } } + +@ +\subsection{Miscellany} +I first insert a newline before the special characters, then change the +newline to a backslash. I can't do the backslash directly because +[[\&]] means a literal ampersand. +<<functions>>= +function escape_brace_bslash(line) { + gsub(/[\\{}]/, "\n&", line) + gsub(/\n/, "\\", line) + return line +} +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<functions>>= +function convquotes(s, r, i) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "\\code{}" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + r = r TeXliteral(substr(s, 1, i-1+RLENGTH-2)) "\\edoc{}" + s = substr(s, i+RLENGTH) + } else { + r = r s "\\edoc{}" + s = "" + } + } + return r s +} +<<functions>>= +function indexlabel(ident, l) { + l = ident + gsub(/:/, ":col", l) # must be first (colon) + gsub(/ /, ":sp", l) # space + gsub(/#/, ":has", l) # hash + gsub(/\$/, ":do", l) # dollar + gsub(/%/, ":pe", l) # percent + gsub(/&/, ":am", l) # ampersand + gsub(/,/, ":com", l) # commad + gsub(/\\/, ":bs", l) # backslash + gsub(/\^/, ":hat", l) # hat + gsub(/_/, ":un", l) # underscore + gsub(/{/, ":lb", l) # left brace + gsub(/}/, ":rb", l) # right brace + gsub(/~/, ":ti", l) # tilde + return l +} +@ %def indexlabel +@ +Because latex2e uses [[`]] as an active character, I have to use +decimal character codes for the specials. +<<functions>>= +function TeXliteral(arg) { + gsub(/\\/, "<\\char92>", arg) + gsub(/}/, "<\\char125}", arg) + gsub(/{/, "{\\char123}", arg) + gsub(/<\\char/, "{\\char", arg) + gsub(/{\\char92>/, "{\\char92}", arg) + gsub(/\$/, "{\\char36}", arg) + gsub(/&/, "{\\char38}", arg) + gsub(/#/, "{\\char35}", arg) + gsub(/\^/, "{\\char94}", arg) + gsub(/_/, "{\\char95}", arg) + gsub(/%/, "{\\char37}", arg) + gsub(/~/, "{\\char126}", arg) + gsub(/ /, "\\ ", arg) + return arg +} +@ %def TeXliteral + diff --git a/web/noweb/src/awkmake b/web/noweb/src/awkmake new file mode 100755 index 0000000000..5c710ce0f8 --- /dev/null +++ b/web/noweb/src/awkmake @@ -0,0 +1,10 @@ +#!/bin/sh + +WHICH=${LIBSRC:-awk} +DIR=/usr/local/noweb-$WHICH + +mkdir -p $DIR + + +if [ $# -eq 0 ]; then set all install; fi # "$@" breaks make for empty args +/usr/bin/make ICONT=icont CC="gcc -ansi -pedantic -O -Wall -Werror" LIBSRC=$WHICH BIN=$DIR/bin LIB=$DIR/lib MAN=$DIR/man TEXINPUTS=/usr/local/share/texmf/tex/plain/misc ELISP=/dev/null "$@" diff --git a/web/noweb/src/awkname b/web/noweb/src/awkname new file mode 100755 index 0000000000..157843b903 --- /dev/null +++ b/web/noweb/src/awkname @@ -0,0 +1,30 @@ +#!/bin/sh + +case $# in + 1) ;; + *) echo "Usage: `basename $0` [awk|nawk|gawk]" 1>&2; exit 1 +esac + +rc=0 +new=$(mktemp); old=$(mktemp) + +for file in lib/emptydefn lib/unmarkup lib/toascii lib/btdefn \ + awk/noidx awk/totex awk/tohtml awk/noindex \ + shell/noroff shell/toroff lib/pipedocs \ + shell/nocount shell/nountangle shell/noweb shell/noroots +do + trap 'rm -f $new $old; exit 1' 1 2 15 # clean up files + if sed -e "s/nawk '/$1 '/" -e "s/nawk -f/$1 -f/" -e "s/AWK=nawk/AWK=$1/" <$file >$new + then + cp $file $old # save original file + trap 'trap "" 1 2 15; cp $old $file # ignore signals + rm -f $new $old; exit 1' 1 2 15 # during restore + cp $new $file + else + echo "awkname: failed to change $file" 1>&2 + rc=1 + fi +done +rm -f $new $old +exit $rc + diff --git a/web/noweb/src/c/Makefile b/web/noweb/src/c/Makefile new file mode 100644 index 0000000000..52bf4e2c74 --- /dev/null +++ b/web/noweb/src/c/Makefile @@ -0,0 +1,112 @@ +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# +# Adjust these two lines for your ANSI C compiler +CC=gcc -ansi -pedantic -O -Wall -Werror +CFLAGS= + +# after installation, make doc.dvi for literate version + +FINDUSESOBJS=columns.o errors.o finduses.o match.o getline.o recognize.o +MNTOBJS=mnt.o getline.o match.o modules.o modtrees.o notangle.o \ + strsave.o errors.o columns.o +TANGLEOBJS=notangle.o getline.o match.o modules.o modtrees.o strsave.o \ + main.o errors.o columns.o +MARKUPOBJS=markmain.o strsave.o markup.o errors.o getline.o columns.o +FILES=markmain.nw markup.nw \ + main.nw notangle.nw match.nw mnt.nw modules.nw modtrees.nw \ + finduses.nw recognize.nw \ + getline.nw columns.nw errors.nw strsave.nw + +SRCS=columns.h errors.h getline.h markup.h match.h modtrees.h \ + modules.h notangle.h recognize.h strsave.h \ + columns.c errors.c getline.c finduses.c main.c markmain.c markup.c match.c \ + mnt.c modtrees.c modules.c notangle.c nwmktemp.c recognize.c \ + strsave.c markup.ps +NAME='name of RCS version' + +NOTANGLE=notangle +SHELL=/bin/sh +# set this for CPIF and then distribute tools with bad timestamps... +#CPIF=| cpif +CPIF=> + +.SUFFIXES: .nw .tex .dvi .h +.nw.tex: ; noweave $*.nw >$*.tex +.nw.c: ; $(NOTANGLE) -L $*.nw >$*.c +.nw.o: ; $(NOTANGLE) -L $*.nw >$*.c + $(CC) $(CFLAGS) -c $*.c +.nw.h: ; $(NOTANGLE) -Rheader $*.nw $(CPIF) $*.h + +all: nt markup mnt finduses nwmktemp + +nt: $(TANGLEOBJS) + $(CC) $(CFLAGS) -o nt $(TANGLEOBJS) + +nwmktemp: nwmktemp.o + $(CC) $(CFLAGS) -o nwmktemp nwmktemp.o + +finduses: $(FINDUSESOBJS) + $(CC) $(CFLAGS) -o finduses $(FINDUSESOBJS) + +markup: $(MARKUPOBJS) + $(CC) $(CFLAGS) -o markup $(MARKUPOBJS) + +mnt: $(MNTOBJS) + $(CC) $(CFLAGS) -o mnt $(MNTOBJS) + +source: $(SRCS) +touch: $(SRCS) + touch $(SRCS) + +boot: ; touch $(SRCS) + +clean: ; rm -f nt markup mnt finduses fakepretty + rm -f core *.makelog *.tex *.log *.blg *.dvi *.o *.toc *~ + rm -f *.atac *.trace *.html + +clobber: clean + rm -f $(SRCS) fakepretty.c pretty.[ch] + +checkin: $(FILES) doc.nw + ci $(CINAME) $(CIMSG) -l $(FILES) doc.nw + +doc.tex: doc.nw + cp doc.nw doc.tex + +allcode.tex: $(FILES) + noweave -n -index $(FILES) > allcode.tex + +doc.dvi: doc.tex allcode.tex + latex doc; latex doc; latex doc + +doc.ps: doc.dvi + dvips doc + +markup.ps: markmain.nw + notangle -Rmarkup.dot markmain.nw | dot -Tps > markup.ps + +FPOBJS=fakepretty.o pretty.o errors.o getline.o match.o strsave.o columns.o + +fakepretty.o: pretty.h + +fakepretty: $(FPOBJS) + $(CC) $(CFLAGS) -o fakepretty $(FPOBJS) + + +columns.o: columns.h +errors.o: errors.h +finduses.o: errors.h match.h getline.h recognize.h +getline.o: columns.h errors.h getline.h +main.o: notangle.h errors.h columns.h modules.h modtrees.h +markmain.o: errors.h markup.h getline.h columns.h +markup.o: markup.h strsave.h errors.h +match.o: match.h +mnt.o: modules.h modtrees.h notangle.h errors.h columns.h strsave.h +modtrees.o: strsave.h modules.h modtrees.h errors.h +modules.o: modules.h modtrees.h errors.h columns.h strsave.h +notangle.o: strsave.h getline.h modules.h modtrees.h errors.h match.h notangle.h +nwmktemp.o: +pretty.o: getline.h match.h errors.h pretty.h strsave.h +recognize.o: +strsave.o: strsave.h errors.h diff --git a/web/noweb/src/c/columns.c b/web/noweb/src/c/columns.c new file mode 100644 index 0000000000..a34b2ebff6 --- /dev/null +++ b/web/noweb/src/c/columns.c @@ -0,0 +1,37 @@ +#line 18 "columns.nw" +static char rcsid[] = "$Id: columns.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include "columns.h" + +int tabsize = 8; +#line 25 "columns.nw" +int columnwidth (char *s) { /* width of a string in columns */ + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return limitcolumn(s, 0); +} +#line 31 "columns.nw" +int limitcolumn (char *s, int col) { + while (*s) { + col++; + if (*s=='\t' && tabsize > 0) while (col % tabsize != 0) col++; + s++; + } + return col; +} +#line 40 "columns.nw" +void indent_for (int width, FILE *fp) { + /* write whitespace [[width]] columns wide */ +/*fprintf(fp,"<%2d>",width); if (width>4) {fprintf(fp," "); width -= 4;}*/ + if (tabsize > 1) + while (width >= tabsize) { + putc('\t', fp); + width -= tabsize; + } + while (width > 0) { + putc(' ', fp); + width--; + } +} + diff --git a/web/noweb/src/c/columns.h b/web/noweb/src/c/columns.h new file mode 100644 index 0000000000..83762f5988 --- /dev/null +++ b/web/noweb/src/c/columns.h @@ -0,0 +1,6 @@ +extern int tabsize; +extern int columnwidth (char *s); /* number of columns string occupies */ +extern int limitcolumn (char *s, int startcolumn); + /* width of startcolumn blanks plus s */ +extern void indent_for (int width, FILE *fp); + /* indent to width; next char -> width+1 */ diff --git a/web/noweb/src/c/columns.nw b/web/noweb/src/c/columns.nw new file mode 100644 index 0000000000..ce35ed568b --- /dev/null +++ b/web/noweb/src/c/columns.nw @@ -0,0 +1,53 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +\subsection{Managing indentation and columns} +These functions are used to help accumulate indentations across module +boundaries by measuring the widths of various strings +in columns. +The size of things depends on [[tabsize]] as defined in columns.h. +If [[tabsize == 0]], tabs shouldn't be touched on input and won't be +generated on output. +<<header>>= +extern int tabsize; +extern int columnwidth (char *s); /* number of columns string occupies */ +extern int limitcolumn (char *s, int startcolumn); + /* width of startcolumn blanks plus s */ +extern void indent_for (int width, FILE *fp); + /* indent to width; next char -> width+1 */ +<<*>>= +static char rcsid[] = "$Id: columns.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include "columns.h" + +int tabsize = 8; +<<*>>= +int columnwidth (char *s) { /* width of a string in columns */ + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return limitcolumn(s, 0); +} +<<*>>= +int limitcolumn (char *s, int col) { + while (*s) { + col++; + if (*s=='\t' && tabsize > 0) while (col % tabsize != 0) col++; + s++; + } + return col; +} +<<*>>= +void indent_for (int width, FILE *fp) { + /* write whitespace [[width]] columns wide */ +/*fprintf(fp,"<%2d>",width); if (width>4) {fprintf(fp," "); width -= 4;}*/ + if (tabsize > 1) + while (width >= tabsize) { + putc('\t', fp); + width -= tabsize; + } + while (width > 0) { + putc(' ', fp); + width--; + } +} + diff --git a/web/noweb/src/c/doc.bbl b/web/noweb/src/c/doc.bbl new file mode 100644 index 0000000000..e72476f0c4 --- /dev/null +++ b/web/noweb/src/c/doc.bbl @@ -0,0 +1,8 @@ +\begin{thebibliography}{1} + +\bibitem{aho:efficient} +Alfred~V. Aho and Margaret~J. Corasick. +\newblock Efficient string matching: An aid to bibliographic search. +\newblock {\em Communications of the ACM}, 18(6):333--340, June 1975. + +\end{thebibliography} diff --git a/web/noweb/src/c/doc.nw b/web/noweb/src/c/doc.nw new file mode 100644 index 0000000000..d32e6cfc55 --- /dev/null +++ b/web/noweb/src/c/doc.nw @@ -0,0 +1,49 @@ +% $Id: doc.nw,v 1.21 2008/10/06 01:03:05 nr Exp nr $ + +\documentclass[twoside]{article} +\usepackage{noweb,multicol} +\makeatletter +\newenvironment{fields}{\@fields}{\crcr\egroup\par\end{quote}} +\def\@fields{\@ifnextchar[{\@@fields}{\@@fields[4in]}} +\def\@@fields[#1]{\begin{quote}\catcode`\_=\other + \tabskip=\leftmargin + \halign\bgroup\tt##\hfil\tabskip=2em& + {\hsize=#1\vtop{\noindent\raggedright\let\\=\cr + \strut##\strut\par}}\tabskip=0pt\cr} +\makeatother +\pagestyle{noweb}\raggedbottom\noweboptions{shift} + +\title{{\tt noweb} Source Code\thanks{{\tt noweb} is copyright 1989--2000 by Norman Ramsey. +Although it is freely available, it is not in the public domain.}} +\author{Norman Ramsey\thanks{Section~\ref{preston} is the +generous contribution of Preston Briggs}\\{\tt nr@eecs.harvard.edu}} +\begin{document} +\maketitle +\section*{Preface} +This isn't a real literate program---there's just enough documentation +here to get the job done. +One of my observations is that the cost of creating a high-quality, well-documented +literate program adds 1--3 times the amount of effort it took to create the program +in the first place. +Most programs I write aren't worth that kind of effort, and {\tt noweb} is +no exception. +Don't expect too much. + +\tableofcontents + +\input{allcode.tex} + +\bibliographystyle{plain} +\bibliography{web} + +\section{List of all chunks from all files} + +\nowebchunks + +\begin{multicols}{2}[\section{Index}] +\nowebindex +\end{multicols} + +\end{document} + + diff --git a/web/noweb/src/c/errors.c b/web/noweb/src/c/errors.c new file mode 100644 index 0000000000..c47a092ab2 --- /dev/null +++ b/web/noweb/src/c/errors.c @@ -0,0 +1,53 @@ +#line 17 "errors.nw" +static char rcsid[] = "$Id: errors.nw,v 2.26 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "errors.h" + +#line 34 "errors.nw" +enum errorlevel errorlevel = Normal; +int finalstage = 0; +char *progname = NULL; +#line 44 "errors.nw" +void nowebexit(char *msg) { + if (!finalstage && errorlevel > Normal) + printf("@fatal %s %s\n", progname != NULL ? progname : "a-noweb-filter", + msg != NULL ? msg : "an unspecified error occurred"); + exit(errorlevel); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ +} +#line 60 "errors.nw" +void errormsg(enum errorlevel level, char *s,...) { + va_list args; /* see K&R, page 174 */ + va_start(args,s); + +#line 81 "errors.nw" +if (level > errorlevel) + errorlevel = level; +vfprintf(stderr, s, args); +fprintf(stderr,"\n"); +#line 64 "errors.nw" + va_end(args); + if (level >= Fatal) + nowebexit(s); +} +#line 71 "errors.nw" +void errorat(char *filename, int lineno, enum errorlevel level, char *s, ...) { + va_list args; /* see K&R, page 174 */ + va_start(args,s); + fprintf(stderr, "%s:%d: ", filename, lineno); + +#line 81 "errors.nw" +if (level > errorlevel) + errorlevel = level; +vfprintf(stderr, s, args); +fprintf(stderr,"\n"); +#line 76 "errors.nw" + va_end(args); + if (level >= Fatal) + nowebexit(s); +} diff --git a/web/noweb/src/c/errors.h b/web/noweb/src/c/errors.h new file mode 100644 index 0000000000..72cc81ec9e --- /dev/null +++ b/web/noweb/src/c/errors.h @@ -0,0 +1,10 @@ +enum errorlevel { Normal=0, Warning, Error, Fatal, Impossible }; +extern enum errorlevel errorlevel; +extern int finalstage; /* set nonzero if this main() is a final stage */ +extern char *progname; /* set to name of program if main() is a filter */ +extern void nowebexit(char *optional_msg); +void errormsg(enum errorlevel level, char *s, ...); +#define overflow(S) errormsg(Fatal,"Capacity exceeded: %s", S) +#define impossible(S) errormsg(Impossible, "This can't happen: %s", S) +#define checkptr(P) do { if (!(P)) overflow("memory"); } while (0) +void errorat(char *filename, int lineno, enum errorlevel level, char *s, ...); diff --git a/web/noweb/src/c/errors.nw b/web/noweb/src/c/errors.nw new file mode 100644 index 0000000000..c3c59829d2 --- /dev/null +++ b/web/noweb/src/c/errors.nw @@ -0,0 +1,94 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode; c-indent-level: 4 -*- +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +\subsection{Error messages} +Here is a big set of functions that other places can call to issue +error messages. + +It might be worth implementing these as macros so they could indicate +the file name and line number in the source of where they were called. + +[[errorlevel]] is an integer that says how bad the worst error so far +has been. +[[newerrorlevel]] will update it conditionally. +Some day we should hide it and give the world a function call that +queries it. +<<*>>= +static char rcsid[] = "$Id: errors.nw,v 2.26 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "errors.h" + +<<C functions>> +@ +I track degrees of error as they occur. +<<header>>= +enum errorlevel { Normal=0, Warning, Error, Fatal, Impossible }; +extern enum errorlevel errorlevel; +extern int finalstage; /* set nonzero if this main() is a final stage */ +extern char *progname; /* set to name of program if main() is a filter */ +<<C functions>>= +enum errorlevel errorlevel = Normal; +int finalstage = 0; +char *progname = NULL; +@ +If an error occurred, one can exit with the proper notice on the +filter: +<<header>>= +extern void nowebexit(char *optional_msg); +@ +<<C functions>>= +void nowebexit(char *msg) { + if (!finalstage && errorlevel > Normal) + printf("@fatal %s %s\n", progname != NULL ? progname : "a-noweb-filter", + msg != NULL ? msg : "an unspecified error occurred"); + exit(errorlevel); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ +} +@ +The rest is just varying flavors of error messages. +<<header>>= +void errormsg(enum errorlevel level, char *s, ...); +#define overflow(S) errormsg(Fatal,"Capacity exceeded: %s", S) +#define impossible(S) errormsg(Impossible, "This can't happen: %s", S) +#define checkptr(P) do { if (!(P)) overflow("memory"); } while (0) +<<C functions>>= +void errormsg(enum errorlevel level, char *s,...) { + va_list args; /* see K&R, page 174 */ + va_start(args,s); + <<set [[errorlevel]] and print [[s]] with [[args]]>> + va_end(args); + if (level >= Fatal) + nowebexit(s); +} +<<header>>= +void errorat(char *filename, int lineno, enum errorlevel level, char *s, ...); +<<C functions>>= +void errorat(char *filename, int lineno, enum errorlevel level, char *s, ...) { + va_list args; /* see K&R, page 174 */ + va_start(args,s); + fprintf(stderr, "%s:%d: ", filename, lineno); + <<set [[errorlevel]] and print [[s]] with [[args]]>> + va_end(args); + if (level >= Fatal) + nowebexit(s); +} +<<set [[errorlevel]] and print [[s]] with [[args]]>>= +if (level > errorlevel) + errorlevel = level; +vfprintf(stderr, s, args); +fprintf(stderr,"\n"); +@ +Some older machines don't do [[vfprintf]]; here's an implementation: +<<crock for non-ANSI machines>>= +extern int _doprnt(const char *format, va_list args, FILE *stream); + +int vfprintf(FILE *stream, const char *format, va_list arg) { + return _doprnt(format,arg,stream); +} + + diff --git a/web/noweb/src/c/fakepretty.nw b/web/noweb/src/c/fakepretty.nw new file mode 100644 index 0000000000..0149413a1c --- /dev/null +++ b/web/noweb/src/c/fakepretty.nw @@ -0,0 +1,85 @@ +\section{A fake prettyprinter} +This ``prettyprinter'' builds a code brick. +<<*>>= +static char rcsid[] = "$Id: fakepretty.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <stdlib.h> +#include "pretty.h" + +struct prettyprinter { + int column; + int trailswhite; + PrettyPrinterCallback callback; + void *cb; +}; +<<*>>= +static void addwhite(PrettyPrinter pp) { + if (!pp->trailswhite) { + pp->callback(pp->cb, STRING, " "); + pp->column++; + pp->trailswhite = 1; + } +} +<<*>>= +static void flushout(PrettyPrinter pp) { + if (pp->column > 0) { + pp->callback(pp->cb, NEWLINE, (char *)0); + pp->column = 0; + pp->trailswhite = 1; + } +} +<<*>>= +static void addthing(PrettyPrinter pp, Parttype type, char *contents) { + addwhite(pp); + if (pp->column + strlen(contents) > 70) flushout(pp); + pp->callback(pp->cb, type, contents); + pp->column += strlen(contents); + pp->trailswhite = 0; +} +#define addtoken(PP,T) addthing(pp, STRING, T) +#define adduse(PP,C) addthing(pp, USE_CHUNK, C) +@ +<<*>>= +void prettyprint(PrettyPrinter pp, Parttype type, char *contents) { + switch(type) { + case START_OF_CHUNK: break; + case END_OF_CHUNK: if (pp->column > 0) flushout(pp); break; + case STRING: <<write tokens>> break; + case USE_CHUNK: adduse(pp, contents); break; + case NEWLINE: addwhite(pp); break; + case WHATSIT: pp->callback(pp->cb, type, contents); break; + default: assert(0); + } +} + +PrettyPrinter new_prettyprinter(PrettyPrinterCallback callback, void *closure) { + PrettyPrinter pp = (PrettyPrinter) malloc(sizeof *pp); + pp->callback = callback; + pp->cb = closure; + pp->column = 0; + pp->trailswhite = 0; + return pp; +} +@ +<<write tokens>>= +{ char *p = contents; + char *q; + + while (*p && isspace(*p)) p++; + while (*p) { + for (q = p; *q && !isspace(*q); q++); + { char c = *q; + *q = 0; + addtoken(pp, p); + *q = c; + } + p = q; + while (*p && isspace(*p)) p++; + } +} +@ + + diff --git a/web/noweb/src/c/finduses.c b/web/noweb/src/c/finduses.c new file mode 100644 index 0000000000..096f18f211 --- /dev/null +++ b/web/noweb/src/c/finduses.c @@ -0,0 +1,137 @@ +#line 5 "finduses.nw" +static char rcsid[] = "$Id: finduses.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "errors.h" +#include "match.h" +#include "getline.h" +#include "recognize.h" +#line 23 "finduses.nw" +static Recognizer nwindex; +#define ALPHANUM "_'@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#" +#define SYMBOLS "!%^&*-+:=|~<>./?`" +/* note $ and \ both delimiters */ +#line 31 "finduses.nw" +static int showquotes = 1; +#line 97 "finduses.nw" +typedef struct line_and_outfile { + char *line; + FILE *out; +} LineOut; +#line 66 "finduses.nw" +static void read_ids(FILE *in); +#line 102 "finduses.nw" +static void add_use_markers(FILE *in, FILE *out); +#line 154 "finduses.nw" +static void write_index_use(void *closure, char *id, char *instance); +static char *emit_up_to(FILE *f, char *s, char *limit); +#line 35 "finduses.nw" +int main(int argc, char **argv) { + FILE *fp; + int i; + + progname = argv[0]; + for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != 0; i++) + if (!strcmp(argv[i], "-noquote")) + showquotes = 0; + else + errormsg(Error, "%s: unknown option %s\n", progname, argv[i]); + nwindex = new_recognizer(ALPHANUM, SYMBOLS); + if (i == argc) { + +#line 77 "finduses.nw" +{ FILE *tmp = tmpfile(); + char *line; + if (tmp == NULL) +#line 157 "finduses.nw" +errormsg(Fatal, "%s: couldn't open temporary file\n", progname); +#line 80 "finduses.nw" + while ((line = getline_nw(stdin)) != NULL) { + if (fputs(line, tmp) == EOF) +#line 159 "finduses.nw" +errormsg(Fatal, "%s: error writing temporary file\n", progname); +#line 82 "finduses.nw" + if (is_index(line, "defn")) { + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; + add_ident(nwindex, line+1+5+1+4+1); + } else if (is_index(line, "localdefn")) { + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; + add_ident(nwindex, line+1+5+1+9+1); + } else if (is_keyword(line, "fatal")) { + exit(1); + } + } + rewind(tmp); + stop_adding(nwindex); + add_use_markers(tmp, stdout); +} +#line 48 "finduses.nw" + } else { + +#line 58 "finduses.nw" +for (; i < argc; i++) + if ((fp=fopen(argv[i],"r"))==NULL) + errormsg(Error, "%s: couldn't open file %s\n", progname, argv[i]); + else { + read_ids(fp); + fclose(fp); + } +#line 50 "finduses.nw" + stop_adding(nwindex); + add_use_markers(stdin, stdout); + } + nowebexit(NULL); + return errorlevel; /* slay warning */ +} +#line 68 "finduses.nw" +static void read_ids(FILE *in) { + char *line; + while ((line = getline_nw(in)) != NULL) { + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; + add_ident(nwindex, line); + } +} +#line 104 "finduses.nw" +static void add_use_markers(FILE *in, FILE *out) { + char *line; + int incode = 0; + LineOut info; info.line = (char *)0; info.out = out; + + while ((line = getline_nw(in)) != NULL) { + if (is_begin(line, "code") || (showquotes && is_keyword(line, "quote"))) + incode = 1; + else if (is_end(line, "code") || is_keyword(line, "endquote")) + incode = 0; + if (is_keyword(line, "text") && incode) { + info.line = line + 6; /* skip "@text " */ + search_for_ident(nwindex, line, write_index_use, &info); + if (*info.line && *info.line != '\n') + fprintf(out, "@text %s", info.line); /* has newline */ + } else + fprintf(out, "%s", line); + } +} +#line 135 "finduses.nw" +static void write_index_use(void *closure, char *id, char *instance) { + LineOut *info = (LineOut *) closure; + info->line = emit_up_to(info->out, info->line, instance); + fprintf(info->out, "@index use %s\n", id); + info->line = emit_up_to(info->out, info->line, instance + strlen(id)); +} +#line 142 "finduses.nw" +static char *emit_up_to(FILE *f, char *s, char *limit) { + if (s < limit) { + char saved = *limit; + *limit = 0; + fprintf(f, "@text %s\n", s); + *limit = saved; + return limit; + } else { + return s; + } +} diff --git a/web/noweb/src/c/finduses.nw b/web/noweb/src/c/finduses.nw new file mode 100644 index 0000000000..ba070a6501 --- /dev/null +++ b/web/noweb/src/c/finduses.nw @@ -0,0 +1,159 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode; c-indent-level: 4 -*- +\section{Scanning for uses of identifiers} +\subsection{Main program} +<<*>>= +static char rcsid[] = "$Id: finduses.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "errors.h" +#include "match.h" +#include "getline.h" +#include "recognize.h" +@ +These choices of alphanumerics and symbols seem to work for most languages. +Making [[@]] alphanumeric helps {\LaTeX}, and making [[#]] +alphanumeric helps avoid false hits on C preprocessor directives like +[[#define]] and [[#include]]. +<<*>>= +static Recognizer nwindex; +#define ALPHANUM "_'@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#" +#define SYMBOLS "!%^&*-+:=|~<>./?`" +/* note $ and \ both delimiters */ +@ + +By default, find uses within quoted code ([[[[...]]]]). +<<*>>= +static int showquotes = 1; +<<typedefs>> +<<local prototypes>> +<<*>>= +int main(int argc, char **argv) { + FILE *fp; + int i; + + progname = argv[0]; + for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != 0; i++) + if (!strcmp(argv[i], "-noquote")) + showquotes = 0; + else + errormsg(Error, "%s: unknown option %s\n", progname, argv[i]); + nwindex = new_recognizer(ALPHANUM, SYMBOLS); + if (i == argc) { + <<add uses to stdin, grabbing defns from stdin>> + } else { + <<read identifiers to be defined from files named in [[argv]]>> + stop_adding(nwindex); + add_use_markers(stdin, stdout); + } + nowebexit(NULL); + return errorlevel; /* slay warning */ +} +@ +<<read identifiers to be defined from files named in [[argv]]>>= +for (; i < argc; i++) + if ((fp=fopen(argv[i],"r"))==NULL) + errormsg(Error, "%s: couldn't open file %s\n", progname, argv[i]); + else { + read_ids(fp); + fclose(fp); + } +<<local prototypes>>= +static void read_ids(FILE *in); +<<*>>= +static void read_ids(FILE *in) { + char *line; + while ((line = getline_nw(in)) != NULL) { + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; + add_ident(nwindex, line); + } +} +@ +<<add uses to stdin, grabbing defns from stdin>>= +{ FILE *tmp = tmpfile(); + char *line; + if (tmp == NULL) <<complain about opening temp file and exit>> + while ((line = getline_nw(stdin)) != NULL) { + if (fputs(line, tmp) == EOF) <<complain about writing temp file and exit>> + if (is_index(line, "defn")) { + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; + add_ident(nwindex, line+1+5+1+4+1); + } else if (is_index(line, "localdefn")) { + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; + add_ident(nwindex, line+1+5+1+9+1); + } else if (is_keyword(line, "fatal")) { + exit(1); + } + } + rewind(tmp); + stop_adding(nwindex); + add_use_markers(tmp, stdout); +} +<<typedefs>>= +typedef struct line_and_outfile { + char *line; + FILE *out; +} LineOut; +<<local prototypes>>= +static void add_use_markers(FILE *in, FILE *out); +<<*>>= +static void add_use_markers(FILE *in, FILE *out) { + char *line; + int incode = 0; + LineOut info; info.line = (char *)0; info.out = out; + + while ((line = getline_nw(in)) != NULL) { + if (is_begin(line, "code") || (showquotes && is_keyword(line, "quote"))) + incode = 1; + else if (is_end(line, "code") || is_keyword(line, "endquote")) + incode = 0; + if (is_keyword(line, "text") && incode) { + info.line = line + 6; /* skip "@text " */ + search_for_ident(nwindex, line, write_index_use, &info); + if (*info.line && *info.line != '\n') + fprintf(out, "@text %s", info.line); /* has newline */ + } else + fprintf(out, "%s", line); + } +} +@ + +We gradually cut out the uses, and the tail of the line is left in [[info.line]], +to be printed by the code above. +There's a tricky bug lurking here---if one identifier is a prefix of another, +but both are recognized (as with the C$++$ [[::]] separator), we have to avoid +writing them both out in full, because that would duplicate text unnecessarily. +As a result, we always emit the line in pieces. +The function [[emit_up_to(f, s, limit)]] emits the piece of the string [[s]] up to +but not including [[limit]], if any. +It returns [[limit]] or [[s]], whichever is greater. +<<*>>= +static void write_index_use(void *closure, char *id, char *instance) { + LineOut *info = (LineOut *) closure; + info->line = emit_up_to(info->out, info->line, instance); + fprintf(info->out, "@index use %s\n", id); + info->line = emit_up_to(info->out, info->line, instance + strlen(id)); +} +<<*>>= +static char *emit_up_to(FILE *f, char *s, char *limit) { + if (s < limit) { + char saved = *limit; + *limit = 0; + fprintf(f, "@text %s\n", s); + *limit = saved; + return limit; + } else { + return s; + } +} +<<local prototypes>>= +static void write_index_use(void *closure, char *id, char *instance); +static char *emit_up_to(FILE *f, char *s, char *limit); +<<complain about opening temp file and exit>>= +errormsg(Fatal, "%s: couldn't open temporary file\n", progname); +<<complain about writing temp file and exit>>= +errormsg(Fatal, "%s: error writing temporary file\n", progname); diff --git a/web/noweb/src/c/getline.c b/web/noweb/src/c/getline.c new file mode 100644 index 0000000000..5d14367c9e --- /dev/null +++ b/web/noweb/src/c/getline.c @@ -0,0 +1,69 @@ +#line 17 "getline.nw" +static char rcsid[] = "$Id: getline.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#define START_SIZE 128 /* initial buffer size */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "columns.h" +#include "errors.h" +#include "getline.h" + +static char *buf1 = NULL, *buf2 = NULL; /* lines without, with tabs expanded */ +static int buf_size = START_SIZE; /* size of both buffers if non-NULL */ + +#line 44 "getline.nw" +void new_buffers(void) { + checkptr(buf1 = (char *) realloc(buf1, buf_size)); + checkptr(buf2 = (char *) realloc(buf2, buf_size)); +} +#line 49 "getline.nw" +char *getline_nw (FILE *fp) { + + +#line 86 "getline.nw" +if (buf1==NULL) { + checkptr(buf1 = (char *) malloc (buf_size)); + checkptr(buf2 = (char *) malloc (buf_size)); +} +#line 52 "getline.nw" + + buf1=fgets(buf1, buf_size, fp); + if (buf1==NULL) return buf1; /* end of file */ + while (buf1[strlen(buf1)-1] != '\n') { /* failed to get whole line */ + buf_size *= 2; + new_buffers(); + if (fgets(buf1+strlen(buf1),buf_size-strlen(buf1),fp)==NULL) + return buf1; /* end of file */ + } + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return buf1; +} +#line 66 "getline.nw" +char *getline_expand (FILE *fp) { + char *s, *t; + int width; + + if (getline_nw(fp)==NULL) return NULL; + +#line 91 "getline.nw" +if (columnwidth(buf1) > buf_size - 1) { + while (columnwidth(buf1) > buf_size - 1) buf_size *= 2; + new_buffers(); +} +#line 72 "getline.nw" + s = buf1; t = buf2; width=0; + while (*s) + if (*s=='\t' && tabsize > 0) { + do { + *t++ = ' '; width++; + } while (width % tabsize != 0); + s++; + } else { + *t++ = *s++; width++; + } + *t='\0'; + return buf2; +} diff --git a/web/noweb/src/c/getline.h b/web/noweb/src/c/getline.h new file mode 100644 index 0000000000..e560dfaf49 --- /dev/null +++ b/web/noweb/src/c/getline.h @@ -0,0 +1,6 @@ +char *getline_expand (FILE *fp); + /* grab a line in buffer, return new buffer or NULL for eof + tabs in line are expanded according to tabsize */ +char *getline_nw (FILE *fp); + /* grab a line in the buffer, return a new buffer or NULL for eof + no expansion of tabs */ diff --git a/web/noweb/src/c/getline.nw b/web/noweb/src/c/getline.nw new file mode 100644 index 0000000000..9ba4c6160d --- /dev/null +++ b/web/noweb/src/c/getline.nw @@ -0,0 +1,94 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +@ +\section{Utility functions} + +\subsection{Line-oriented input} +[[getline_nw]] is reads in the next line, no matter +how long it is, expanding tabs. +I allocate more and more space for the line, until I've +read a whole line or until I run out of space, whichever comes first. +The buffer grows in size exponentially, so the cost in overhead is roughly the logarithm +of the length of the longest line. +@ +[[getline_nw]] reuses the same internal buffers over and over, so subsequent +calls destroy the results of previous calls. +<<*>>= +static char rcsid[] = "$Id: getline.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#define START_SIZE 128 /* initial buffer size */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "columns.h" +#include "errors.h" +#include "getline.h" + +static char *buf1 = NULL, *buf2 = NULL; /* lines without, with tabs expanded */ +static int buf_size = START_SIZE; /* size of both buffers if non-NULL */ + +<<C functions>> +@ +<<header>>= +char *getline_expand (FILE *fp); + /* grab a line in buffer, return new buffer or NULL for eof + tabs in line are expanded according to tabsize */ +char *getline_nw (FILE *fp); + /* grab a line in the buffer, return a new buffer or NULL for eof + no expansion of tabs */ +@ N.B. In 2008, [[getline]] became part of a POSIX standard, +so Noweb's version (which is 20 years old) is now [[getline_nw]]. +@ +<<C functions>>= +void new_buffers(void) { + checkptr(buf1 = (char *) realloc(buf1, buf_size)); + checkptr(buf2 = (char *) realloc(buf2, buf_size)); +} +<<C functions>>= +char *getline_nw (FILE *fp) { + + <<if [[buf1==NULL]], allocate initial buffers>> + + buf1=fgets(buf1, buf_size, fp); + if (buf1==NULL) return buf1; /* end of file */ + while (buf1[strlen(buf1)-1] != '\n') { /* failed to get whole line */ + buf_size *= 2; + new_buffers(); + if (fgets(buf1+strlen(buf1),buf_size-strlen(buf1),fp)==NULL) + return buf1; /* end of file */ + } + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return buf1; +} +<<C functions>>= +char *getline_expand (FILE *fp) { + char *s, *t; + int width; + + if (getline_nw(fp)==NULL) return NULL; + <<if [[columnwidth(buf1) > buf_size - 1]], allocate new buffers>> + s = buf1; t = buf2; width=0; + while (*s) + if (*s=='\t' && tabsize > 0) { + do { + *t++ = ' '; width++; + } while (width % tabsize != 0); + s++; + } else { + *t++ = *s++; width++; + } + *t='\0'; + return buf2; +} +<<if [[buf1==NULL]], allocate initial buffers>>= +if (buf1==NULL) { + checkptr(buf1 = (char *) malloc (buf_size)); + checkptr(buf2 = (char *) malloc (buf_size)); +} +<<if [[columnwidth(buf1) > buf_size - 1]], allocate new buffers>>= +if (columnwidth(buf1) > buf_size - 1) { + while (columnwidth(buf1) > buf_size - 1) buf_size *= 2; + new_buffers(); +} diff --git a/web/noweb/src/c/main.c b/web/noweb/src/c/main.c new file mode 100644 index 0000000000..f120c207fe --- /dev/null +++ b/web/noweb/src/c/main.c @@ -0,0 +1,75 @@ +#line 7 "main.nw" +static char rcsid[] = "$Id: main.nw,v 2.25 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "notangle.h" +#include "errors.h" +#include "columns.h" +#include "modules.h" +#include "modtrees.h" + +#line 25 "main.nw" +int main(int argc, char **argv) { + int i; + char *locformat = ""; + char *Clocformat = "#line %L \"%F\"%N"; + int root_options_seen = 0; + + tabsize = 0; /* default for nt is not to use tabs */ + progname = argv[0]; + finalstage = 1; + + for (i=1; i<argc; i++) { + +#line 55 "main.nw" +if (*argv[i]=='-') { + +#line 71 "main.nw" + switch (argv[i][1]) { + case 't': /* set tab size or turn off */ + if (isdigit(argv[i][2])) + tabsize = atoi(argv[i]+2); + else if (argv[i][2]==0) + tabsize = 0; /* no tabs */ + else + errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]); + break; + case 'R': /* change name of root module */ + root_options_seen++; + break; + case 'L': /* have a #line number format */ + locformat = argv[i]+2; + if (!*locformat) locformat = Clocformat; + break; + default: + errormsg(Warning, "Ignoring unknown option -%s", argv[i]); + } +#line 57 "main.nw" +} else { + +#line 92 "main.nw" +errormsg(Warning, + "I can't handle arguments yet, so I'll just ignore `%s'",argv[i]); + +#line 59 "main.nw" +} +#line 37 "main.nw" + } + + read_defs(stdin); /* read all the definitions */ + apply_each_module(remove_final_newline); /* pretty up the module texts */ + if (root_options_seen == 0) + emit_module_named(stdout, "*", locformat); + else + for (i=1; i<argc; i++) + if (argv[i][0] == '-' && argv[i][1] == 'R') + emit_module_named(stdout, argv[i]+2, locformat); + + nowebexit(NULL); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return errorlevel; /* slay warning */ +} diff --git a/web/noweb/src/c/main.nw b/web/noweb/src/c/main.nw new file mode 100644 index 0000000000..bebe9092eb --- /dev/null +++ b/web/noweb/src/c/main.nw @@ -0,0 +1,94 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode -*- +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +\section{Tangling} +\subsection{Main program} +<<*>>= +static char rcsid[] = "$Id: main.nw,v 2.25 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "notangle.h" +#include "errors.h" +#include "columns.h" +#include "modules.h" +#include "modtrees.h" + +@ +The main program makes one pass through the arguments, using options +to set global state, and counting any roots it sees. +It then makes a second pass to emit all the roots, or if there are no +roots on the command line, it emits the module (chunk) named [[<<*>>]]. +<<*>>= +int main(int argc, char **argv) { + int i; + char *locformat = ""; + char *Clocformat = "#line %L \"%F\"%N"; + int root_options_seen = 0; + + tabsize = 0; /* default for nt is not to use tabs */ + progname = argv[0]; + finalstage = 1; + + for (i=1; i<argc; i++) { + <<process argument [[i]]---first pass>> + } + + read_defs(stdin); /* read all the definitions */ + apply_each_module(remove_final_newline); /* pretty up the module texts */ + if (root_options_seen == 0) + emit_module_named(stdout, "*", locformat); + else + for (i=1; i<argc; i++) + if (argv[i][0] == '-' && argv[i][1] == 'R') + emit_module_named(stdout, argv[i]+2, locformat); + + nowebexit(NULL); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return errorlevel; /* slay warning */ +} +@ +<<process argument [[i]]---first pass>>= +if (*argv[i]=='-') { + <<handle option in argument [[i]]>> +} else { + <<handle argument in argument [[i]]>> +} +@ +The following options are good on the command line: +\begin{fields} +-Rname& make name the name of the root module. + Default is [[@<<*@>>]].\\ +-L[format]& Use the given format to write {\tt\#line} numbers. + If -L is given alone, use ANSI C format. + If not given, write no {\tt\#line} numbers.\\ +-t[tabsize]& Fiddle with tabs +\end{fields} +<<handle option in argument [[i]]>>= + switch (argv[i][1]) { + case 't': /* set tab size or turn off */ + if (isdigit(argv[i][2])) + tabsize = atoi(argv[i]+2); + else if (argv[i][2]==0) + tabsize = 0; /* no tabs */ + else + errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]); + break; + case 'R': /* change name of root module */ + root_options_seen++; + break; + case 'L': /* have a #line number format */ + locformat = argv[i]+2; + if (!*locformat) locformat = Clocformat; + break; + default: + errormsg(Warning, "Ignoring unknown option -%s", argv[i]); + } +@ +<<handle argument in argument [[i]]>>= +errormsg(Warning, + "I can't handle arguments yet, so I'll just ignore `%s'",argv[i]); + diff --git a/web/noweb/src/c/markmain.c b/web/noweb/src/c/markmain.c new file mode 100644 index 0000000000..5e63bba06e --- /dev/null +++ b/web/noweb/src/c/markmain.c @@ -0,0 +1,406 @@ +#line 66 "markmain.nw" +static char rcsid[] = "$Id: markmain.nw,v 2.29 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include "errors.h" +#include "markup.h" +#include "getline.h" +#include "columns.h" + +#line 35 "markmain.nw" +typedef enum state {Code=1, Docs=2, CodeIndex=3} State; +typedef enum mark {Begin=1, End=2} Mark; +typedef enum index {Defn=1, Use=2, Newline=3} Index; + +static char *states[] = { "bad state", "code", "docs", "code" }; +static char *marks[] = { "bad mark", "begin", "end" }; +static char *indices[] = { "bad index", "defn", "use", "nl" }; +static char low_at_sign = '@'; + +static void print_state(FILE *out, Mark be, State state, int count) { + fprintf(out, "%c%s %s %d\n", low_at_sign, marks[be], states[state], count); +} + +static void print_index(FILE *out, Index idx, char *arg) { + if (arg) + fprintf(out, "%cindex %s %s\n", low_at_sign, indices[idx], arg); + else + fprintf(out, "%cindex %s\n", low_at_sign, indices[idx]); +} + +static void print_pair(FILE *out, char *name, char *value) { + if (value) { + int last=strlen(value)-1; + if (last>=0 && value[last]=='\n') + fprintf(out, "%c%s %s%cnl\n", low_at_sign, name, value, low_at_sign); + else + fprintf(out, "%c%s %s\n", low_at_sign, name, value); + } else + fprintf(out, "%c%s\n", low_at_sign, name); +} + +#line 80 "markmain.nw" +void markup (FILE* in, FILE *out, char *filename) { + State state = Docs; /* what we are reading */ + int quoting = 0; /* currently quoting code? */ + int count = 0; /* number of current chunk, from 0 */ + int missing_newline = 0; /* was last line missing a trailing \n? */ + int lineno = 0; /* number of lines read */ + int last_open_quote = -1; /* line number of last unmatched open quote */ + + char *line; /* part of line up to mark (or end) */ +#define MAX_MODNAME 255 + char modname[MAX_MODNAME+1] = ""; /* name of module currently being read, + [[""]] if no module is being read */ + + +#line 120 "markmain.nw" +print_pair(out, "file", filename); +print_state(out, Begin, state, count); +while ((line = getline_expand(in)) != NULL) { + lineno++; + +#line 171 "markmain.nw" +missing_newline = line[strlen(line)-1] != '\n'; +#line 125 "markmain.nw" + if (starts_code(line, filename, lineno)) { + +#line 541 "markmain.nw" +if (quoting) { + errorat(filename, last_open_quote, Warning, "open quote `[[' never closed"); + quoting = 0; +} +#line 127 "markmain.nw" + print_state(out, End, state, count); + count++; + state = Code; + print_state(out, Begin, state, count); + getmodname(modname,MAX_MODNAME,line); + print_pair(out,"defn",modname); + print_pair(out,"nl",0); /* could be implicit but this is better */ + } else if (is_def(line)) { + +#line 527 "markmain.nw" +line = remove_def_marker(line); +while (*line && isspace(*line)) line++; +while (*line) { + char tmp; + char *s = line+1; + while (*s && !isspace(*s)) s++; + tmp = *s; *s = 0; + print_index(out, Defn, line); + *s = tmp; + while (isspace(*s)) s++; + line = s; +} +print_index(out, Newline, 0); +#line 136 "markmain.nw" + if (state == Code) + state = CodeIndex; + } else { + if (starts_doc(line) || state == CodeIndex) { + +#line 541 "markmain.nw" +if (quoting) { + errorat(filename, last_open_quote, Warning, "open quote `[[' never closed"); + quoting = 0; +} +#line 141 "markmain.nw" + print_state(out, End, state, count); + count++; + state = Docs; /* always reading docs after a stop */ + print_state(out, Begin, state, count); + if (starts_doc(line)) + line = first_doc_line(line); + } + { +#line 180 "markmain.nw" +char *p, *s, *t, c; +static char *buf; +static int buflen = 0; +#line 149 "markmain.nw" + +#line 258 "markmain.nw" +#define LA1 '<' +#define LA2 '<' +#define RA1 '>' +#define RA2 '>' +#define LS1 '[' +#define LS2 '[' +#define RS1 ']' +#define RS2 ']' +#define ESC '@' +#line 271 "markmain.nw" +#define next(L) do { c = *t++; goto L; } while (0) +#define copy(L) do { *s++ = c; c = *t++; goto L; } while (0) +#line 277 "markmain.nw" +#define lexassert(E) ((void)0) +#line 150 "markmain.nw" + +#line 504 "markmain.nw" +if (buf == NULL) + checkptr(buf = (char *) malloc(buflen = 128)); +if (buflen < strlen(line) + 1 + 2) { + while (buflen < strlen(line) + 1 + 2) + buflen *= 2; + checkptr(buf = (char *) realloc(buf, buflen)); +} +#line 185 "markmain.nw" +p = s = buf+2; +t = line; +c = *t++; +#line 151 "markmain.nw" + +#line 205 "markmain.nw" +if (c == ESC && *t == ESC) { *s++ = ESC; c = *++t; ++t; } +#line 152 "markmain.nw" + if (state == Code || quoting) + goto convert_code; + else + goto convert_docs; + +#line 295 "markmain.nw" +convert_docs: +t: lexassert(state == Docs && !quoting); + if (c == ESC) next(at); + if (c == LA1) next(la); + if (c == LS1) next(ls); + +#line 241 "markmain.nw" +if (c == '\0') { + +#line 512 "markmain.nw" +if (s > p) { + *s = 0; + print_pair(out, "text", p); +} +s = p = buf + 2; +#line 243 "markmain.nw" + goto done_converting; +} +#line 301 "markmain.nw" + copy(t); +#line 309 "markmain.nw" +at: if (c == LA1) next(atla); + if (c == LS1) next(atls); + if (c == RA1) next(atra); + if (c == RS1) next(atrs); + *s++ = ESC; goto t; +#line 318 "markmain.nw" +atls: if (c == LS2) { *s++ = LS1; *s++ = LS2; next(t); } + *s++ = ESC; *s++ = LS1; goto t; +#line 324 "markmain.nw" +atla: if (c == LA2) { *s++ = LA1; *s++ = LA2; next(t); } + *s++ = ESC; *s++ = LA1; goto t; +#line 330 "markmain.nw" +atrs: if (c == RS2) { *s++ = RS1; *s++ = RS2; next(t); } + *s++ = ESC; *s++ = RS1; goto t; +#line 336 "markmain.nw" +atra: if (c == RA2) { *s++ = RA1; *s++ = RA2; next(t); } + *s++ = ESC; *s++ = RA1; goto t; +#line 342 "markmain.nw" +la: if (c == LA2) { + +#line 524 "markmain.nw" +errorat(filename, lineno, Error, "unescaped << in documentation chunk"); +#line 344 "markmain.nw" + *s++ = LA1; *s++ = LA2; next(t); + } + *s++ = LA1; goto t; +#line 354 "markmain.nw" +ls: lexassert(state == Docs); + if (c == LS2) { + +#line 512 "markmain.nw" +if (s > p) { + *s = 0; + print_pair(out, "text", p); +} +s = p = buf + 2; +#line 357 "markmain.nw" + quoting = 1; last_open_quote = lineno; print_pair(out, "quote", 0); + next(c); + } + *s++ = LS1; + goto t; +#line 371 "markmain.nw" +convert_code: +c: lexassert(state == Code || quoting); + if (c == RS1 && quoting) next(crs); + if (c == LA1) next(cla); + if (c == ESC) next(cat); + +#line 241 "markmain.nw" +if (c == '\0') { + +#line 512 "markmain.nw" +if (s > p) { + *s = 0; + print_pair(out, "text", p); +} +s = p = buf + 2; +#line 243 "markmain.nw" + goto done_converting; +} +#line 377 "markmain.nw" + copy(c); +#line 385 "markmain.nw" +cat: if (c == LA1) next(catla); + if (c == RA1) next(catra); + *s++ = ESC; goto c; +#line 392 "markmain.nw" +catla: if (c == LA2) { *s++ = LA1; *s++ = LA2; next(c); } + *s++ = ESC; *s++ = LA1; goto c; +#line 398 "markmain.nw" +catra: if (c == RA2) { *s++ = RA1; *s++ = RA2; next(c); } + *s++ = ESC; *s++ = RA1; goto c; +#line 406 "markmain.nw" +crs: if (c == RS2) next(ce); + *s++ = RS1; goto c; +#line 412 "markmain.nw" +ce: lexassert(quoting); + if (c == RS2) copy(ce); + quoting = 0; +#line 512 "markmain.nw" +if (s > p) { + *s = 0; + print_pair(out, "text", p); +} +s = p = buf + 2; +#line 414 "markmain.nw" + print_pair(out, "endquote", 0); + goto t; +#line 422 "markmain.nw" +cla: if (c == LA2) { +#line 512 "markmain.nw" +if (s > p) { + *s = 0; + print_pair(out, "text", p); +} +s = p = buf + 2; +#line 422 "markmain.nw" + next(u); } + *s++ = LA1; goto c; +#line 433 "markmain.nw" +u: lexassert(state == Code || quoting); + if (c == LS1) copy(uls); + if (c == RS1 && quoting) next(urs); + if (c == RA1) next(ura); + if (c == '\0') { /* premature end --- it's not a use after all */ + +#line 499 "markmain.nw" +lexassert(p == buf + 2); +p -= 2; +p[0] = LA1; +p[1] = LA2; +#line 439 "markmain.nw" + goto c; + } + copy(u); +#line 446 "markmain.nw" +urs: lexassert(quoting); + if (c == RS2) { /* premature end --- it's not a use after all */ + +#line 499 "markmain.nw" +lexassert(p == buf + 2); +p -= 2; +p[0] = LA1; +p[1] = LA2; +#line 449 "markmain.nw" + next(ce); + } + *s++ = RS1; goto u; +#line 456 "markmain.nw" +uls: if (c == LS2) copy(uc); + goto u; +#line 463 "markmain.nw" +uc: lexassert(quoting || state == Code); + if (c == RS1) copy(ucrs); + if (c == '\0') { + +#line 499 "markmain.nw" +lexassert(p == buf + 2); +p -= 2; +p[0] = LA1; +p[1] = LA2; +#line 467 "markmain.nw" + goto c; + } + copy(uc); +#line 474 "markmain.nw" +ucrs: if (c == RS2) copy(uce); + goto uc; +#line 480 "markmain.nw" +uce: if (c == RS2) copy(uce); + goto u; +#line 486 "markmain.nw" +ura: if (c == RA2) { +#line 519 "markmain.nw" +*s = 0; +print_pair(out, "use", p); +s = p = buf + 2; +#line 486 "markmain.nw" + next(c); } + *s++ = RA1; goto u; +#line 157 "markmain.nw" + done_converting: + (void)0; + } + } +} +#line 541 "markmain.nw" +if (quoting) { + errorat(filename, last_open_quote, Warning, "open quote `[[' never closed"); + quoting = 0; +} +#line 173 "markmain.nw" +if (missing_newline) print_pair(out, "nl",0); +#line 164 "markmain.nw" +print_state(out, End, state, count); +#line 94 "markmain.nw" +} +#line 550 "markmain.nw" +int main(int argc, char **argv) { + FILE *fp; + int i; + + progname = argv[0]; + for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != 0; i++) + switch(argv[i][1]) { + case 't': +#line 583 "markmain.nw" +if (isdigit(argv[i][2])) + tabsize = atoi(argv[i]+2); +else if (argv[i][2]==0) + tabsize = 0; /* no tabs */ +else + errormsg(Error, "%s: ill-formed option %s", progname, argv[i]); +#line 557 "markmain.nw" + break; + default : errormsg(Error, "%s: unknown option -%c", progname, argv[i][1]); + break; + } + if (i < argc) + for (; i < argc; i++) { + if (!strcmp(argv[i], "-")) { + markup(stdin,stdout,""); + } else + if ((fp=fopen(argv[i],"r"))==NULL) { + errormsg(Error, "%s: couldn't open file %s", progname, argv[i]); + fprintf(stdout, "@fatal couldn't open file %s\n", argv[i]); + } else { + markup(fp,stdout,argv[i]); + fclose(fp); + } + } + else + markup(stdin,stdout,""); + nowebexit(NULL); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return errorlevel; /* slay warning */ +} diff --git a/web/noweb/src/c/markmain.nw b/web/noweb/src/c/markmain.nw new file mode 100644 index 0000000000..1542eff182 --- /dev/null +++ b/web/noweb/src/c/markmain.nw @@ -0,0 +1,588 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode -*- +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +\section{Converting a {\tt noweb} file to tool markup} +\subsection{Main program} +This is the initial filter in the pipeline. +Syntax from {\tt markup.ow}. +Output may contain [[ + @[begin|end] [code|docs] nnn ... + @text <line of text> + @quote + @endquote + @defn <module name> + @use <module name> + @nl + @file <filename> + @line nnn + @index [defn|use] <indexed string> + @index nl + @literal <literal text for weave or other tool> +]] +Here's the code that writes the output. +We have one function to print the state, which takes care of +[[@begin]] and [[@end]] and remembering what kind of chunk we have. +Another function prints [[@name value]] pairs. +If the [[value]] has a trailing newline, the pair is followed by [[@nl]]. + +If the only way index entries were created was by tools, life would be easy. +Permitting index definitions in the input causes two complications. +First, it introduces newlines that don't belong to any chunk, but that must be counted. +Second, it introduces a special state, [[CodeIndex]] +in which a code chunk is about to be terminated +but won't be until there are no more index definitions. +<<state and printing definitions>>= +typedef enum state {Code=1, Docs=2, CodeIndex=3} State; +typedef enum mark {Begin=1, End=2} Mark; +typedef enum index {Defn=1, Use=2, Newline=3} Index; + +static char *states[] = { "bad state", "code", "docs", "code" }; +static char *marks[] = { "bad mark", "begin", "end" }; +static char *indices[] = { "bad index", "defn", "use", "nl" }; +static char low_at_sign = '@'; + +static void print_state(FILE *out, Mark be, State state, int count) { + fprintf(out, "%c%s %s %d\n", low_at_sign, marks[be], states[state], count); +} + +static void print_index(FILE *out, Index idx, char *arg) { + if (arg) + fprintf(out, "%cindex %s %s\n", low_at_sign, indices[idx], arg); + else + fprintf(out, "%cindex %s\n", low_at_sign, indices[idx]); +} + +static void print_pair(FILE *out, char *name, char *value) { + if (value) { + int last=strlen(value)-1; + if (last>=0 && value[last]=='\n') + fprintf(out, "%c%s %s%cnl\n", low_at_sign, name, value, low_at_sign); + else + fprintf(out, "%c%s %s\n", low_at_sign, name, value); + } else + fprintf(out, "%c%s\n", low_at_sign, name); +} +<<*>>= +static char rcsid[] = "$Id: markmain.nw,v 2.29 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include "errors.h" +#include "markup.h" +#include "getline.h" +#include "columns.h" + +<<state and printing definitions>> + +void markup (FILE* in, FILE *out, char *filename) { + State state = Docs; /* what we are reading */ + int quoting = 0; /* currently quoting code? */ + int count = 0; /* number of current chunk, from 0 */ + int missing_newline = 0; /* was last line missing a trailing \n? */ + int lineno = 0; /* number of lines read */ + int last_open_quote = -1; /* line number of last unmatched open quote */ + + char *line; /* part of line up to mark (or end) */ +#define MAX_MODNAME 255 + char modname[MAX_MODNAME+1] = ""; /* name of module currently being read, + [[""]] if no module is being read */ + + <<copy all lines from [[in]] to [[out]], adding markup>> +} +@ +A {\em noweb } file consists of a sequence of +{\em chunks,} which may appear in any order. +{\em noweb} supports two kinds of chunks: documentation chunks and code chunks. +Documentation chunks begin with an at sign (@) at the beginning of a line, +followed by a space +(or with an at sign on a line by itself). +They have no names. +Code chunks begin with +$$\hbox{\tt@<<{\em chunk name\/}@>>=}$$ +on a line by itself. +The left double angle bracket ({\tt @<<}) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not signal the beginning of a documentation +or code chunk, the first chunk is assumed to be a documentation chunk. + +We implement this by just looping through the document, looking for the +beginnings of chunks. +We use [[state]] to keep track of what's ending. +We add a little extra markup for file name and module names +Starting code and starting documentation are notably different: +code is started by a module definition, which must appear on a line by itself. +Documentation is started by a character sequence, +and arbitrary documentation text can follow on the same line. +<<copy all lines from [[in]] to [[out]], adding markup>>= +print_pair(out, "file", filename); +print_state(out, Begin, state, count); +while ((line = getline_expand(in)) != NULL) { + lineno++; + <<remember if a newline was missing from the last line>> + if (starts_code(line, filename, lineno)) { + <<complain and correct unmatched open quote, if any>> + print_state(out, End, state, count); + count++; + state = Code; + print_state(out, Begin, state, count); + getmodname(modname,MAX_MODNAME,line); + print_pair(out,"defn",modname); + print_pair(out,"nl",0); /* could be implicit but this is better */ + } else if (is_def(line)) { + <<emit index definitions and index newline>> + if (state == Code) + state = CodeIndex; + } else { + if (starts_doc(line) || state == CodeIndex) { + <<complain and correct unmatched open quote, if any>> + print_state(out, End, state, count); + count++; + state = Docs; /* always reading docs after a stop */ + print_state(out, Begin, state, count); + if (starts_doc(line)) + line = first_doc_line(line); + } + { <<declarations for conversion engine>> + <<macro definitions for conversion engine>> + <<initialize conversion engine>> + <<if the line begins with two [[ESC]]s, append one>> + if (state == Code || quoting) + goto convert_code; + else + goto convert_docs; + <<conversion state machine>> + done_converting: + (void)0; + } + } +} +<<complain and correct unmatched open quote, if any>> +<<add a trailing newline if one was missing>> +print_state(out, End, state, count); +@ +We make it safe to assume henceforth that every chunk ends with newline, +even if the file didn't end with newline. +If the file didn't end with newline, we add a newline to the last line +before printing the final [[@end]]. +<<remember if a newline was missing from the last line>>= +missing_newline = line[strlen(line)-1] != '\n'; +<<add a trailing newline if one was missing>>= +if (missing_newline) print_pair(out, "nl",0); +@ +The conversion engine copies the input [[line]] to the output [[buf]], +using the pointers [[s]] on the left for the destination and [[t]] on the +right for the source. [[c]] holds the current input character, and [[p]] +points to the current piece of text about to be emitted. +<<declarations for conversion engine>>= +char *p, *s, *t, c; +static char *buf; +static int buflen = 0; +<<initialize conversion engine>>= +<<make [[buf]] big enough to hold all of [[line]], plus two more characters>> +p = s = buf+2; +t = line; +c = *t++; +@ +Using [[@@]] in the first column to escape an at sign was added after +the state machine was designed, in response to a plea from Dirk +Verworner: +\begin{quote} +We at the Humboldt-University of Berlin use your +{\tt noweb} programs for our project. +We've a simple description language, which sometimes an at sign +on a line by itself requires. +So I've a problem. +\end{quote} +It might be that I ought to add a special beginning-of-line state to +the state machine, so that we could recognize with there, but I'd have +to replicate it for both code and documentation. +Because I only pay the cost once per input line, I don't mind using +the following Band-Aid: +<<if the line begins with two [[ESC]]s, append one>>= +if (c == ESC && *t == ESC) { *s++ = ESC; c = *++t; ++t; } +@ +To convert documentation lines, we have to track the quoting brackets. +Within quoting brackets, we use the same engine as for within code lines +(except for our treatment of square brackets). +We have the state machine from hell. We can have ordinary text, quoted text in +[[[[...]]]], or a use in [[[[<<...>>]]]]. +Ordinary text and quoted text can span lines, but uses can't. +So, the four main states of our lexer are: +\begin{quote} +\begin{tabular}{ll} +\tt t&Ordinary text\\ +\tt c&Code text\\ +\tt u&A use\\ +\tt uc&Quoted code within a use\\ +\end{tabular} +\end{quote} +That last state covers the case where we have [[[[<<...[[...]]...>>]]]]. +BTW, it's no coincidence that if we've just consumed a newline (i.e., we're at the +start of a line), we must be in one of those four states. +Moreover, since a newline in states [[u]] and [[uc]] means it's not really a use% +\footnote{Because uses may not span lines.}, +those states fall back to state [[c]] (while prepending the opening [[<<]] that +appeared to signal a use). +So [[c]] and [[t]] are the major states. When scanning documentation, +they correspond to [[quoting]] and [[!quoting]]. +@ +% l2h substitution chi X +By the way, one reason for writing a state machine by hand instead of using a +lexer generator is so I can test the machine using the wonderful $\chi$ATAC tool +developed by Bob Horgan, Saul London, and Hira Agrawal. For more information +send email to {\tt jrh@bellcore.com}. +@ +We don't have to treat a newline specially, because [[print_pair]] inserts +[[@nl]] if needed. +<<emit text and goto [[done_converting]] if at end of line>>= +if (c == '\0') { + <<emit [[p]] to [[s]] as text>> + goto done_converting; +} +@ +Markup is defined as follows: +\begin{quote} +\begin{tabular}{ll} +\tt LA1 LA2&open a chunk\\ +\tt RA1 RA2&close a chunk\\ +\tt LS1 LS2&open a quote\\ +\tt RS1 RS2&close a quote\\ +\tt ESC"es the sequences above\\ +\end{tabular} +\end{quote} +By default we use angles for chunks and squares for quotes, at sign for escape: +<<macro definitions for conversion engine>>= +#define LA1 '<' +#define LA2 '<' +#define RA1 '>' +#define RA2 '>' +#define LS1 '[' +#define LS2 '[' +#define RS1 ']' +#define RS2 ']' +#define ESC '@' +@ +Usually when we change state we advance the input pointer. +Sometimes we also copy the current character. +<<macro definitions for conversion engine>>= +#define next(L) do { c = *t++; goto L; } while (0) +#define copy(L) do { *s++ = c; c = *t++; goto L; } while (0) +@ +Normally we don't check assertions in the lexer. +To check them, we would [[#define lexassert assert]]. +<<macro definitions for conversion engine>>= +#define lexassert(E) ((void)0) +@ +From here on I intercalate the code that implements the state machine with the +dot code that draws a picture of it. +The picture can be found in file {\tt markup.ps}; it's too big to be useful as +part of this document. +The arcs in the picture are labelled with {\it char}/{\it output}, but the +output may be omitted. +In the code, we see +[[goto]] when the picture has a [[-]] sign (denoting character not +consumed), [[next]] when we're emitting some literal, and [[copy]] when +we emit [[c]] (the next character). +<<dot arcs>>= +t -> at [label="@"] +t -> la [label="<"] +t -> ls [label="["] +t -> t [label="c/c"] +<<conversion state machine>>= +convert_docs: +t: lexassert(state == Docs && !quoting); + if (c == ESC) next(at); + if (c == LA1) next(la); + if (c == LS1) next(ls); + <<emit text and goto [[done_converting]] if at end of line>> + copy(t); +<<dot arcs>>= +at -> atls [label="["] +at -> atla [label="<"] +at -> atrs [label="]"] +at -> atra [label=">"] +at -> t [label="c-/@"] +<<conversion state machine>>= +at: if (c == LA1) next(atla); + if (c == LS1) next(atls); + if (c == RA1) next(atra); + if (c == RS1) next(atrs); + *s++ = ESC; goto t; +<<dot arcs>>= +atls -> t [label="[/[["] +atls -> t [label="c-/@["] +<<conversion state machine>>= +atls: if (c == LS2) { *s++ = LS1; *s++ = LS2; next(t); } + *s++ = ESC; *s++ = LS1; goto t; +<<dot arcs>>= +atla -> t [label="</<<"] +atla -> t [label="c-/@<"] +<<conversion state machine>>= +atla: if (c == LA2) { *s++ = LA1; *s++ = LA2; next(t); } + *s++ = ESC; *s++ = LA1; goto t; +<<dot arcs>>= +atrs -> t [label="]/]]"] +atrs -> t [label="c-/@]"] +<<conversion state machine>>= +atrs: if (c == RS2) { *s++ = RS1; *s++ = RS2; next(t); } + *s++ = ESC; *s++ = RS1; goto t; +<<dot arcs>>= +atra -> t [label=">/>>"] +atra -> t [label="c-/@>"] +<<conversion state machine>>= +atra: if (c == RA2) { *s++ = RA1; *s++ = RA2; next(t); } + *s++ = ESC; *s++ = RA1; goto t; +<<dot arcs>>= +la -> t [label="</<< (error)"] +la -> t [label="c-/<"] +<<conversion state machine>>= +la: if (c == LA2) { + <<complain of unescaped @<< in documentation>> + *s++ = LA1; *s++ = LA2; next(t); + } + *s++ = LA1; goto t; +@ +When we've seen [[[[]], we don't yet know whether it's the start of +a quote or the start of a use---but we can emit the text so far. +<<dot arcs>>= +ls -> c [label="[ (emit text; quote)"] +ls -> t [label="c-/["] +<<conversion state machine>>= +ls: lexassert(state == Docs); + if (c == LS2) { + <<emit [[p]] to [[s]] as text>> + quoting = 1; last_open_quote = lineno; print_pair(out, "quote", 0); + next(c); + } + *s++ = LS1; + goto t; +@ +Here's the ordinary quote state. +<<dot arcs>>= +/*{ rank=same cat catla catra }*/ +c -> crs [label="] (docs only)"] +c -> cla [label="<"] +c -> cat [label="@"] +c -> c [label="c/c"] +<<conversion state machine>>= +convert_code: +c: lexassert(state == Code || quoting); + if (c == RS1 && quoting) next(crs); + if (c == LA1) next(cla); + if (c == ESC) next(cat); + <<emit text and goto [[done_converting]] if at end of line>> + copy(c); +@ +In code, quoting square brackets is neither necessary nor permitted. +<<dot arcs>>= +cat -> catla [label="<"] +cat -> catra [label=">"] +cat -> c [label="c-/@"] +<<conversion state machine>>= +cat: if (c == LA1) next(catla); + if (c == RA1) next(catra); + *s++ = ESC; goto c; +<<dot arcs>>= +catla -> c [label="</<<"] +catla -> c [label="c-/@<"] +<<conversion state machine>>= +catla: if (c == LA2) { *s++ = LA1; *s++ = LA2; next(c); } + *s++ = ESC; *s++ = LA1; goto c; +<<dot arcs>>= +catra -> c [label=">/>>"] +catra -> c [label="c-/@>"] +<<conversion state machine>>= +catra: if (c == RA2) { *s++ = RA1; *s++ = RA2; next(c); } + *s++ = ESC; *s++ = RA1; goto c; +@ +We can't end the quote until we've consumed all the closing [[]]]'s. +<<dot arcs>>= +crs -> ce [label="]"] +crs -> c [label="c-/]"] +<<conversion state machine>>= +crs: if (c == RS2) next(ce); + *s++ = RS1; goto c; +<<dot arcs>>= +ce -> ce [label="]/]"] +ce -> t [label="c- (emit text; endquote)"] +<<conversion state machine>>= +ce: lexassert(quoting); + if (c == RS2) copy(ce); + quoting = 0; <<emit [[p]] to [[s]] as text>> print_pair(out, "endquote", 0); + goto t; +@ +Now's here where we might be seeing a use. +<<dot arcs>>= +cla -> u [label="< (emit text)"] +cla -> c [label="c-/<"] +<<conversion state machine>>= +cla: if (c == LA2) { <<emit [[p]] to [[s]] as text>> next(u); } + *s++ = LA1; goto c; +@ +When processing a use, we make sure [[[[...]]]] hide any closing angle brackets. +<<dot arcs>>= +u -> uls [label="[/["] +u -> urs [label="] (docs only)"] +u -> ura [label=">"] +u -> c [label="eol- (prepend <<)"] +u -> u [label="c/c"] +<<conversion state machine>>= +u: lexassert(state == Code || quoting); + if (c == LS1) copy(uls); + if (c == RS1 && quoting) next(urs); + if (c == RA1) next(ura); + if (c == '\0') { /* premature end --- it's not a use after all */ + <<prepend opening angles to [[p]]>> + goto c; + } + copy(u); +<<dot arcs>>= +urs -> ce [label="] (not use; prepend <<)"] +urs -> u [label="c-/]"] +<<conversion state machine>>= +urs: lexassert(quoting); + if (c == RS2) { /* premature end --- it's not a use after all */ + <<prepend opening angles to [[p]]>> + next(ce); + } + *s++ = RS1; goto u; +<<dot arcs>>= +uls -> uc [label="[/["] +uls -> u [label="c-"] +<<conversion state machine>>= +uls: if (c == LS2) copy(uc); + goto u; +<<dot arcs>>= +uc -> ucrs [label="]/]"] +uc -> c [label="eol- (prepend <<)"] +uc -> uc [label="c/c"] +<<conversion state machine>>= +uc: lexassert(quoting || state == Code); + if (c == RS1) copy(ucrs); + if (c == '\0') { + <<prepend opening angles to [[p]]>> + goto c; + } + copy(uc); +<<dot arcs>>= +ucrs -> uce [label="]/]"] +ucrs -> uc [label="c-"] +<<conversion state machine>>= +ucrs: if (c == RS2) copy(uce); + goto uc; +<<dot arcs>>= +uce -> uce [label="]/]"] +uce -> u [label="c-"] +<<conversion state machine>>= +uce: if (c == RS2) copy(uce); + goto u; +<<dot arcs>>= +ura -> c [label="> (emit use)"] +ura -> u [label="c-/>"] +<<conversion state machine>>= +ura: if (c == RA2) { <<emit [[p]] to [[s]] as use>> next(c); } + *s++ = RA1; goto u; +<<markup.dot>>= +digraph lexer { + rotate=90 + size="10,7.5" + ratio=fill + rankdir=LR + node [shape=circle] + <<dot arcs>> +} +@ +<<prepend opening angles to [[p]]>>= +lexassert(p == buf + 2); +p -= 2; +p[0] = LA1; +p[1] = LA2; +<<make [[buf]] big enough to hold all of [[line]], plus two more characters>>= +if (buf == NULL) + checkptr(buf = (char *) malloc(buflen = 128)); +if (buflen < strlen(line) + 1 + 2) { + while (buflen < strlen(line) + 1 + 2) + buflen *= 2; + checkptr(buf = (char *) realloc(buf, buflen)); +} +<<emit [[p]] to [[s]] as text>>= +if (s > p) { + *s = 0; + print_pair(out, "text", p); +} +s = p = buf + 2; +@ emit the empty use regardless +<<emit [[p]] to [[s]] as use>>= +*s = 0; +print_pair(out, "use", p); +s = p = buf + 2; +@ +<<complain of unescaped @<< in documentation>>= +errorat(filename, lineno, Error, "unescaped @<< in documentation chunk"); +@ +<<emit index definitions and index newline>>= +line = remove_def_marker(line); +while (*line && isspace(*line)) line++; +while (*line) { + char tmp; + char *s = line+1; + while (*s && !isspace(*s)) s++; + tmp = *s; *s = 0; + print_index(out, Defn, line); + *s = tmp; + while (isspace(*s)) s++; + line = s; +} +print_index(out, Newline, 0); +<<complain and correct unmatched open quote, if any>>= +if (quoting) { + errorat(filename, last_open_quote, Warning, "open quote `[[' never closed"); + quoting = 0; +} +@ +Finally, we give the main program, which opens each file and passes it +to [[markup]]. +Like many main programs, it's based on K \& R [[cat]]. +<<*>>= +int main(int argc, char **argv) { + FILE *fp; + int i; + + progname = argv[0]; + for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != 0; i++) + switch(argv[i][1]) { + case 't': <<handle [[-t]]>> break; + default : errormsg(Error, "%s: unknown option -%c", progname, argv[i][1]); + break; + } + if (i < argc) + for (; i < argc; i++) { + if (!strcmp(argv[i], "-")) { + markup(stdin,stdout,""); + } else + if ((fp=fopen(argv[i],"r"))==NULL) { + errormsg(Error, "%s: couldn't open file %s", progname, argv[i]); + fprintf(stdout, "@fatal couldn't open file %s\n", argv[i]); + } else { + markup(fp,stdout,argv[i]); + fclose(fp); + } + } + else + markup(stdin,stdout,""); + nowebexit(NULL); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return errorlevel; /* slay warning */ +} +@ +<<handle [[-t]]>>= +if (isdigit(argv[i][2])) + tabsize = atoi(argv[i]+2); +else if (argv[i][2]==0) + tabsize = 0; /* no tabs */ +else + errormsg(Error, "%s: ill-formed option %s", progname, argv[i]); diff --git a/web/noweb/src/c/markup.c b/web/noweb/src/c/markup.c new file mode 100644 index 0000000000..b982febd87 --- /dev/null +++ b/web/noweb/src/c/markup.c @@ -0,0 +1,123 @@ +#line 24 "markup.nw" +static char rcsid[] = "$Id: markup.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include "markup.h" +#include "strsave.h" +#include "errors.h" + +#line 64 "markup.nw" +char at_sign = '@'; /* should be the only place '@' is mentioned */ +#line 88 "markup.nw" +static char def_marker[] = " %def "; +#define def_length (6) + +#line 74 "markup.nw" +int starts_doc(char *line) { + return (*line==at_sign && (line[1]=='\0' || isspace(line[1]))); +} + +char *first_doc_line(char *line) { + if (line[1]!='\0' && line[1] !='\n') return line+2; + else return line+1; +} +#line 91 "markup.nw" +int is_def(char *line) { + int answer; + static int complained; + +#line 104 "markup.nw" +{ static int checked = 0; + if (!checked) { + assert(strlen(def_marker) == def_length); + checked = 1; + } +} +#line 95 "markup.nw" + answer = (*line==at_sign && !strncmp(line+1, def_marker, def_length)); + +#line 114 "markup.nw" +if (answer && !complained) { + complained = 1; +/* + fprintf(stderr, "Warning: @ %%def is deprecated. Use `-filter autodefs.xxx'\n" + " or `-filter btdefn'\n"); +*/ +} +#line 97 "markup.nw" + return answer; +} + +char *remove_def_marker(char *line) { + return line+1+def_length; +} +#line 144 "markup.nw" +char *mod_start(char *s, int mark) { + return find_escaped(s,"<<","@<<", mark); +} +char *mod_end(char *s, int mark) { + return find_escaped(s,">>","@>>", mark); +} +#line 163 "markup.nw" +int starts_code (char *line, char *filename, int lineno) { + char *tail; + if (mod_start(line,0) != line+2) return 0; + tail = mod_end(line+2,0); + if (tail == NULL || *tail++ != '=') return 0; + while (isspace(*tail)) tail++; + return (*tail == '\0'); +} + +void getmodname(char *dest, int size, char *source) { + /* excess characters in the module name are ignored */ + char *p = strsave(source); + char *q = mod_start(p,1); + + if (q==NULL) +#line 184 "markup.nw" +{ + free(p); + impossible + ("I couldn't manage to extract a module name, but I'm sure I saw one"); +} +#line 178 "markup.nw" + if (mod_end(q,1)==NULL) +#line 184 "markup.nw" +{ + free(p); + impossible + ("I couldn't manage to extract a module name, but I'm sure I saw one"); +} +#line 179 "markup.nw" + strncpy(dest,q,size-1); + dest[size-1] = '\0'; + free(p); +} +#line 207 "markup.nw" +char *find_escaped(register char *s, char *search, char *escape, int mark) { + register char first = *search; + register char first_escape = (escape != NULL ? *escape : '\0'); + int searchlen = strlen(search); + int escapelen = (escape != NULL ? strlen(escape) : 0); + + do { + while (*s && *s != first && *s != first_escape) s++; + if (!*s) break; + if (first_escape && !strncmp(s,escape,escapelen)) { + s += escapelen; + continue; + } + if (!strncmp(s,search,searchlen)) break; + s++; + } while (*s != '\0'); + /* here either s is empty or it points to the first unescaped [[search]] */ + if (*s == '\0') return NULL; + if (mark) *s = '\0'; + return s+searchlen; +} diff --git a/web/noweb/src/c/markup.h b/web/noweb/src/c/markup.h new file mode 100644 index 0000000000..e47229eef7 --- /dev/null +++ b/web/noweb/src/c/markup.h @@ -0,0 +1,13 @@ +extern char at_sign; /* the at sign */ +int starts_doc(char *); +char *first_doc_line(char *); +int is_def(char *); +char *remove_def_marker(char *); +char *mod_start (char *s, int mark); /* find the first module name */ +char *mod_end (char *s, int mark); /* find the end of module name */ +int starts_code (char *line, char *filename, int lineno); + /* does this line start module defn? */ +void getmodname(char *dest, int size, char *source); + /* extract module name and put in dest */ +char *find_escaped(register char *s, char *search, char *escape, int mark); + /* find escaped strings. See markup.nw for details */ diff --git a/web/noweb/src/c/markup.nw b/web/noweb/src/c/markup.nw new file mode 100644 index 0000000000..bce1840830 --- /dev/null +++ b/web/noweb/src/c/markup.nw @@ -0,0 +1,227 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +\subsection{Identifying the special {\tt noweb} control sequences} +This file is supposed to contain all the definitions of how different +things are distinguished in the source text. +@ +Documentation is introduced only by an at sign (@) on a line by itself +(optionally followed by spaces). +It continues up to new documentation or a module definition. +A module definition is a module name, followed by one equals sign (=), +possibly followed by white space, on a line by itself. +A module name is any test enclosed in double angle brackets ([[@<<...@>>]]). +The ``unnamed'' module is called * (that is, [[@<<*@>>]]). + +Double angle brackets may be escaped in source by preceding them with +the at sign. +No other character, not even the at sign, needs to be escaped. +This makes for more readable source, but makes it impossible to put an +at sign immediately before a module name. +If we liked we could try to hack this by creating a predefined module that +expands to an at sign, but I won't bother. + +<<*>>= +static char rcsid[] = "$Id: markup.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include "markup.h" +#include "strsave.h" +#include "errors.h" + +<<data definitions>> + +<<C functions>> +@ +To quote from the man page: +\begin{quote} +A +{\em noweb } +file consists of a sequence of +{\em chunks,} +which may appear in any order. +{\em noweb} +supports two kinds of chunks: documentation chunks and code chunks. +Documentation chunks begin with an at sign (@) at the beginning of a line, +followed by a space +(or with an at sign on a line by itself). +They have no names. +Code chunks begin with +$$\hbox{\tt@<<{\em chunk name\/}@>>=}$$ +on a line by itself. +The left double angle bracket ({\tt @<<}) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not signal the beginning of a documentation +or code chunk, the first chunk is assumed to be a documentation chunk. +\end{quote} +@ +<<data definitions>>= +char at_sign = '@'; /* should be the only place '@' is mentioned */ +<<header>>= +extern char at_sign; /* the at sign */ +@ +Here's how we recognize the start of a documentation chunk, and +extract the first line of documentation from what follows the @ sign. +<<header>>= +int starts_doc(char *); +char *first_doc_line(char *); +<<C functions>>= +int starts_doc(char *line) { + return (*line==at_sign && (line[1]=='\0' || isspace(line[1]))); +} + +char *first_doc_line(char *line) { + if (line[1]!='\0' && line[1] !='\n') return line+2; + else return line+1; +} +@ +An index line is a special case of starting documentation. +<<header>>= +int is_def(char *); +char *remove_def_marker(char *); +<<data definitions>>= +static char def_marker[] = " %def "; +#define def_length (6) +<<C functions>>= +int is_def(char *line) { + int answer; + static int complained; + <<first time through, assert we got the length right>> + answer = (*line==at_sign && !strncmp(line+1, def_marker, def_length)); + <<if [[answer && !complained]], complain>> + return answer; +} + +char *remove_def_marker(char *line) { + return line+1+def_length; +} +<<first time through, assert we got the length right>>= +{ static int checked = 0; + if (!checked) { + assert(strlen(def_marker) == def_length); + checked = 1; + } +} +@ +Explicit deprecation is just too painful. I think the users won't +stand for it. +<<if [[answer && !complained]], complain>>= +if (answer && !complained) { + complained = 1; +/* + fprintf(stderr, "Warning: @ %%def is deprecated. Use `-filter autodefs.xxx'\n" + " or `-filter btdefn'\n"); +*/ +} +@ +Recognizing module names (which introduce code chunks) +and extracting them is the mission of the [[mod_start]] and [[mod_end]] +functions. + +The basic idea is to find an instance of [[@<<]], while skipping every +instance of [[@@<<]]. +That's the start of a module name. +We find the end the same way. +A function called [[find_escaped]] does the dirty work. +If it succeeds, it returns a pointer to the first character after +the special sequence, in this case the first character of the +module name. +(If it fails, it returns a null pointer.) +If its [[mark]] paramter is set, it will put a [['\0']] in place +of the special sequence, splitting the string into pieces. +These two properties make it perfect for dissecting lines with module names. + +[[mod_start]] and [[mod_end]] just store the special sequences. +<<header>>= +char *mod_start (char *s, int mark); /* find the first module name */ +char *mod_end (char *s, int mark); /* find the end of module name */ +<<C functions>>= +char *mod_start(char *s, int mark) { + return find_escaped(s,"@<<","@@<<", mark); +} +char *mod_end(char *s, int mark) { + return find_escaped(s,"@>>","@@>>", mark); +} +@ +Once we know how to [[mod_start]] and [[mod_end]], recognizing the +beginning of a code chunk is easy. The only tricky bit is that if we +see an unclosed, unescaped [[<<]] in a code chunk, this should be acceptable. +Because in a documentation chunk, an unclosed, unescaped [[<<]] will +already be caught with a ``module name doesn't end'' message, the +right thing to do here is silently to let an unclosed [[<<]] pass. +<<header>>= +int starts_code (char *line, char *filename, int lineno); + /* does this line start module defn? */ +void getmodname(char *dest, int size, char *source); + /* extract module name and put in dest */ +<<C functions>>= +int starts_code (char *line, char *filename, int lineno) { + char *tail; + if (mod_start(line,0) != line+2) return 0; + tail = mod_end(line+2,0); + if (tail == NULL || *tail++ != '=') return 0; + while (isspace(*tail)) tail++; + return (*tail == '\0'); +} + +void getmodname(char *dest, int size, char *source) { + /* excess characters in the module name are ignored */ + char *p = strsave(source); + char *q = mod_start(p,1); + + if (q==NULL) <<Error exit from getmodname>> + if (mod_end(q,1)==NULL) <<Error exit from getmodname>> + strncpy(dest,q,size-1); + dest[size-1] = '\0'; + free(p); +} +<<Error exit from getmodname>>= +{ + free(p); + impossible + ("I couldn't manage to extract a module name, but I'm sure I saw one"); +} +@ +Here we do the magic [[find_escaped]]. +Find an arbitrary character seqence, skipping an escaped sequence (if any), +replacing the beginning with [['\0']] if [[mark]] is set. + + If no [[search]] sequence in [[s]], returns [[NULL]]. + Otherwise, returns a pointer to the first character after the + [[search]] sequence, and sets the first character to [['\0']], so + that [[s]] will point to the part of the string before the [[search]] + sequence. The [[escape]] sequence (if any) is always skipped, + even if the [[search]] sequence is a substring of it. + +[[escape]] will be ignored if it is either the [[NULL]] pointer +or the null string ([[""]]). +<<header>>= +char *find_escaped(register char *s, char *search, char *escape, int mark); + /* find escaped strings. See markup.nw for details */ +<<C functions>>= +char *find_escaped(register char *s, char *search, char *escape, int mark) { + register char first = *search; + register char first_escape = (escape != NULL ? *escape : '\0'); + int searchlen = strlen(search); + int escapelen = (escape != NULL ? strlen(escape) : 0); + + do { + while (*s && *s != first && *s != first_escape) s++; + if (!*s) break; + if (first_escape && !strncmp(s,escape,escapelen)) { + s += escapelen; + continue; + } + if (!strncmp(s,search,searchlen)) break; + s++; + } while (*s != '\0'); + /* here either s is empty or it points to the first unescaped [[search]] */ + if (*s == '\0') return NULL; + if (mark) *s = '\0'; + return s+searchlen; +} diff --git a/web/noweb/src/c/markup.ps b/web/noweb/src/c/markup.ps new file mode 100644 index 0000000000..ae0a4c7d58 --- /dev/null +++ b/web/noweb/src/c/markup.ps @@ -0,0 +1,1748 @@ +%!PS-Adobe-3.0 +%%Creator: graphviz version 2.26.3 (20100126.1600) +%%Title: lexer +%%Pages: (atend) +%%BoundingBox: (atend) +%%EndComments +save +%%BeginProlog +/DotDict 200 dict def +DotDict begin + +/setupLatin1 { +mark +/EncodingVector 256 array def + EncodingVector 0 + +ISOLatin1Encoding 0 255 getinterval putinterval +EncodingVector 45 /hyphen put + +% Set up ISO Latin 1 character encoding +/starnetISO { + dup dup findfont dup length dict begin + { 1 index /FID ne { def }{ pop pop } ifelse + } forall + /Encoding EncodingVector def + currentdict end definefont +} def +/Times-Roman starnetISO def +/Times-Italic starnetISO def +/Times-Bold starnetISO def +/Times-BoldItalic starnetISO def +/Helvetica starnetISO def +/Helvetica-Oblique starnetISO def +/Helvetica-Bold starnetISO def +/Helvetica-BoldOblique starnetISO def +/Courier starnetISO def +/Courier-Oblique starnetISO def +/Courier-Bold starnetISO def +/Courier-BoldOblique starnetISO def +cleartomark +} bind def + +%%BeginResource: procset graphviz 0 0 +/coord-font-family /Times-Roman def +/default-font-family /Times-Roman def +/coordfont coord-font-family findfont 8 scalefont def + +/InvScaleFactor 1.0 def +/set_scale { + dup 1 exch div /InvScaleFactor exch def + scale +} bind def + +% styles +/solid { [] 0 setdash } bind def +/dashed { [9 InvScaleFactor mul dup ] 0 setdash } bind def +/dotted { [1 InvScaleFactor mul 6 InvScaleFactor mul] 0 setdash } bind def +/invis {/fill {newpath} def /stroke {newpath} def /show {pop newpath} def} bind def +/bold { 2 setlinewidth } bind def +/filled { } bind def +/unfilled { } bind def +/rounded { } bind def +/diagonals { } bind def + +% hooks for setting color +/nodecolor { sethsbcolor } bind def +/edgecolor { sethsbcolor } bind def +/graphcolor { sethsbcolor } bind def +/nopcolor {pop pop pop} bind def + +/beginpage { % i j npages + /npages exch def + /j exch def + /i exch def + /str 10 string def + npages 1 gt { + gsave + coordfont setfont + 0 0 moveto + (\() show i str cvs show (,) show j str cvs show (\)) show + grestore + } if +} bind def + +/set_font { + findfont exch + scalefont setfont +} def + +% draw text fitted to its expected width +/alignedtext { % width text + /text exch def + /width exch def + gsave + width 0 gt { + [] 0 setdash + text stringwidth pop width exch sub text length div 0 text ashow + } if + grestore +} def + +/boxprim { % xcorner ycorner xsize ysize + 4 2 roll + moveto + 2 copy + exch 0 rlineto + 0 exch rlineto + pop neg 0 rlineto + closepath +} bind def + +/ellipse_path { + /ry exch def + /rx exch def + /y exch def + /x exch def + matrix currentmatrix + newpath + x y translate + rx ry scale + 0 0 1 0 360 arc + setmatrix +} bind def + +/endpage { showpage } bind def +/showpage { } def + +/layercolorseq + [ % layer color sequence - darkest to lightest + [0 0 0] + [.2 .8 .8] + [.4 .8 .8] + [.6 .8 .8] + [.8 .8 .8] + ] +def + +/layerlen layercolorseq length def + +/setlayer {/maxlayer exch def /curlayer exch def + layercolorseq curlayer 1 sub layerlen mod get + aload pop sethsbcolor + /nodecolor {nopcolor} def + /edgecolor {nopcolor} def + /graphcolor {nopcolor} def +} bind def + +/onlayer { curlayer ne {invis} if } def + +/onlayers { + /myupper exch def + /mylower exch def + curlayer mylower lt + curlayer myupper gt + or + {invis} if +} def + +/curlayer 0 def + +%%EndResource +%%EndProlog +%%BeginSetup +14 default-font-family set_font +1 setmiterlimit +% /arrowlength 10 def +% /arrowwidth 5 def + +% make sure pdfmark is harmless for PS-interpreters other than Distiller +/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse +% make '<<' and '>>' safe on PS Level 1 devices +/languagelevel where {pop languagelevel}{1} ifelse +2 lt { + userdict (<<) cvn ([) cvn load put + userdict (>>) cvn ([) cvn load put +} if + +%%EndSetup +setupLatin1 +%%Page: 1 1 +%%PageBoundingBox: 36 36 576 682 +%%PageOrientation: Landscape +0 0 1 beginpage +gsave +36 36 540 646 boxprim clip newpath +0.393586 0.393586 set_scale 90 rotate 95.4667 -1459.47 translate +% t +gsave +1 setlinewidth +0 0 0 nodecolor +18 612 18 18 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +14.5 607.9 moveto 7 (t) alignedtext +grestore +% t->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 5.16 624.92 moveto +-.69 636.13 3.59 648 18 648 curveto +28.13 648 33.26 642.13 33.37 634.75 curveto +stroke +0 0 0 edgecolor +newpath 36.72 633.73 moveto +30.84 624.92 lineto +29.95 635.48 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 36.72 633.73 moveto +30.84 624.92 lineto +29.95 635.48 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +8.5 652.4 moveto 19 (c/c) alignedtext +grestore +% at +gsave +1 setlinewidth +0 0 0 nodecolor +186 463 21.04 21.54 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +178.5 458.9 moveto 15 (at) alignedtext +grestore +% t->at +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 19.99 593.94 moveto +24.18 559.54 35.11 486.61 54 471 curveto +81.9 447.94 125.49 450.25 154.87 455.42 curveto +stroke +0 0 0 edgecolor +newpath 154.51 458.92 moveto +165 457.42 lineto +155.86 452.05 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 154.51 458.92 moveto +165 457.42 lineto +155.86 452.05 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +92.5 475.4 moveto 15 (@) alignedtext +grestore +% la +gsave +1 setlinewidth +0 0 0 nodecolor +186 943 20.15 20.15 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +179.5 938.9 moveto 13 (la) alignedtext +grestore +% t->la +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 19.66 630.11 moveto +23.87 673.16 35.9 780.31 54 811 curveto +80.8 856.44 110.06 847.38 146 886 curveto +154.79 895.45 163.2 906.92 170.03 917.1 curveto +stroke +0 0 0 edgecolor +newpath 167.19 919.15 moveto +175.58 925.62 lineto +173.05 915.33 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 167.19 919.15 moveto +175.58 925.62 lineto +173.05 915.33 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +93.5 890.4 moveto 13 (<) alignedtext +grestore +% ls +gsave +1 setlinewidth +0 0 0 nodecolor +186 1066 18.76 18.76 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +180.5 1061.9 moveto 11 (ls) alignedtext +grestore +% t->ls +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 18.69 630.23 moveto +22.03 715.45 36.92 1071.17 54 1087 curveto +82.87 1113.76 131.44 1095.83 160.93 1080.69 curveto +stroke +0 0 0 edgecolor +newpath 162.63 1083.75 moveto +169.78 1075.94 lineto +159.31 1077.59 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 162.63 1083.75 moveto +169.78 1075.94 lineto +159.31 1077.59 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +97 1104.4 moveto 6 ([) alignedtext +grestore +% at->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 167.86 474.75 moveto +161.07 479.04 153.25 483.85 146 488 curveto +105.97 510.91 85.98 502.77 54 536 curveto +40.88 549.64 31.82 568.94 26.07 584.69 curveto +stroke +0 0 0 edgecolor +newpath 22.66 583.83 moveto +22.76 594.42 lineto +29.29 586.08 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 22.66 583.83 moveto +22.76 594.42 lineto +29.29 586.08 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +84.5 540.4 moveto 31 (c-/@) alignedtext +grestore +% atls +gsave +1 setlinewidth +0 0 0 nodecolor +410 753 27.99 28.49 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +397.5 748.9 moveto 25 (atls) alignedtext +grestore +% at->atls +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 194.93 482.47 moveto +202.4 498.01 213.77 520.08 226 538 curveto +275.33 610.28 345.1 686.07 382.97 725.48 curveto +stroke +0 0 0 edgecolor +newpath 380.64 728.11 moveto +390.11 732.87 lineto +385.68 723.25 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 380.64 728.11 moveto +390.11 732.87 lineto +385.68 723.25 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +290 702.4 moveto 6 ([) alignedtext +grestore +% atla +gsave +1 setlinewidth +0 0 0 nodecolor +410 616 29.88 29.88 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +396.5 611.9 moveto 27 (atla) alignedtext +grestore +% at->atla +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 207.5 465 moveto +243.07 469.35 314.73 482.56 360 521 curveto +377.38 535.76 389.57 558.11 397.55 577.42 curveto +stroke +0 0 0 edgecolor +newpath 394.36 578.85 moveto +401.25 586.9 lineto +400.88 576.31 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 394.36 578.85 moveto +401.25 586.9 lineto +400.88 576.31 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +286.5 525.4 moveto 13 (<) alignedtext +grestore +% atrs +gsave +1 setlinewidth +0 0 0 nodecolor +410 361 30.07 30.57 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +396 356.9 moveto 28 (atrs) alignedtext +grestore +% at->atrs +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 205.49 454.13 moveto +242.81 437.13 324.92 399.74 372.98 377.86 curveto +stroke +0 0 0 edgecolor +newpath 374.46 381.03 moveto +382.11 373.7 lineto +371.56 374.66 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 374.46 381.03 moveto +382.11 373.7 lineto +371.56 374.66 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +290 447.4 moveto 6 (]) alignedtext +grestore +% atra +gsave +1 setlinewidth +0 0 0 nodecolor +410 175 30.77 31.27 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +395.5 170.9 moveto 29 (atra) alignedtext +grestore +% at->atra +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 191.75 442.76 moveto +198.17 422.15 209.82 389.77 226 365 curveto +269.22 298.81 337.51 235.73 377.61 201.5 curveto +stroke +0 0 0 edgecolor +newpath 380.11 203.98 moveto +385.48 194.85 lineto +375.59 198.63 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 380.11 203.98 moveto +385.48 194.85 lineto +375.59 198.63 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +286.5 369.4 moveto 13 (>) alignedtext +grestore +% la->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 167.07 950.59 moveto +138.66 960.6 85.04 973.87 54 946 curveto +31.41 925.71 21.75 717.39 18.93 640.4 curveto +stroke +0 0 0 edgecolor +newpath 22.42 639.9 moveto +18.57 630.03 lineto +15.42 640.15 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 22.42 639.9 moveto +18.57 630.03 lineto +15.42 640.15 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +54.5 966.4 moveto 91 (</<< \(error\)) alignedtext +grestore +% la->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 167.97 934.02 moveto +161.19 930.88 153.35 927.51 146 925 curveto +106.21 911.42 82.02 934.34 54 903 curveto +36.65 883.59 24.04 709.4 19.67 640.18 curveto +stroke +0 0 0 edgecolor +newpath 23.16 639.77 moveto +19.05 630.01 lineto +16.17 640.21 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 23.16 639.77 moveto +19.05 630.01 lineto +16.17 640.21 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +85.5 929.4 moveto 29 (c-/<) alignedtext +grestore +% ls->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 167.72 1060.39 moveto +138.15 1050.25 80.45 1025.84 54 983 curveto +36.11 954.02 23.29 721.76 19.31 640.19 curveto +stroke +0 0 0 edgecolor +newpath 22.8 639.97 moveto +18.83 630.15 lineto +15.81 640.31 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 22.8 639.97 moveto +18.83 630.15 lineto +15.81 640.31 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +89 1055.4 moveto 22 (c-/[) alignedtext +grestore +% c +gsave +1 setlinewidth +0 0 0 nodecolor +410 925 18 18 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +406.5 920.9 moveto 7 (c) alignedtext +grestore +% ls->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 202.11 1055.86 moveto +240.96 1031.4 340 969.06 385.93 940.15 curveto +stroke +0 0 0 edgecolor +newpath 388.03 942.96 moveto +394.63 934.67 lineto +384.3 937.04 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 388.03 942.96 moveto +394.63 934.67 lineto +384.3 937.04 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +226.5 1043.4 moveto 133 ([ \(emit text; quote\)) alignedtext +grestore +% atls->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 381.47 754.8 moveto +334.73 756.83 239.46 757.06 164 732 curveto +113.21 715.14 92.78 719.88 54 683 curveto +41.39 671.01 32.47 653.63 26.67 639.07 curveto +stroke +0 0 0 edgecolor +newpath 29.89 637.7 moveto +23.14 629.54 lineto +23.33 640.13 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 29.89 637.7 moveto +23.14 629.54 lineto +23.33 640.13 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +167.5 747.4 moveto 37 (c-/@[) alignedtext +grestore +% atls->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 381.31 749.44 moveto +318.21 740.62 164.24 713.5 54 648 curveto +47.67 644.24 41.65 639.11 36.4 633.91 curveto +stroke +0 0 0 edgecolor +newpath 38.73 631.27 moveto +29.33 626.38 lineto +33.62 636.06 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 38.73 631.27 moveto +29.33 626.38 lineto +33.62 636.06 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +175.5 715.4 moveto 21 ([/[[) alignedtext +grestore +% atla->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 379.88 615.69 moveto +306.23 614.94 117.81 613.02 46.19 612.29 curveto +stroke +0 0 0 edgecolor +newpath 46.16 608.79 moveto +36.12 612.18 lineto +46.08 615.79 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 46.16 608.79 moveto +36.12 612.18 lineto +46.08 615.79 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +165.5 617.4 moveto 41 (</<<) alignedtext +grestore +% atla->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 380.84 624.01 moveto +374.05 625.6 366.81 627.06 360 628 curveto +225.28 646.64 186.67 655.91 54 626 curveto +50.73 625.26 47.36 624.28 44.06 623.17 curveto +stroke +0 0 0 edgecolor +newpath 45.12 619.83 moveto +34.52 619.62 lineto +42.67 626.39 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 45.12 619.83 moveto +34.52 619.62 lineto +42.67 626.39 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +164.5 649.4 moveto 43 (c-/@<) alignedtext +grestore +% atrs->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 403.99 331.16 moveto +397.5 307.59 384.6 276.2 360 261 curveto +239.83 186.73 136.21 232.11 54 347 curveto +27.23 384.41 20.33 522.69 18.58 583.26 curveto +stroke +0 0 0 edgecolor +newpath 15.07 583.54 moveto +18.31 593.63 lineto +22.07 583.72 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 15.07 583.54 moveto +18.31 593.63 lineto +22.07 583.72 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +175.5 249.4 moveto 21 (]/]]) alignedtext +grestore +% atrs->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 379.57 357.91 moveto +309.72 352.71 137.49 350.92 54 448 curveto +36.69 468.13 25.79 542.64 20.94 583.95 curveto +stroke +0 0 0 edgecolor +newpath 17.46 583.6 moveto +19.81 593.93 lineto +24.41 584.39 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 17.46 583.6 moveto +19.81 593.93 lineto +24.41 584.39 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +167.5 382.4 moveto 37 (c-/@]) alignedtext +grestore +% atra->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 378.84 170.32 moveto +307.05 161.53 130.32 151.78 54 254 curveto +34.41 280.23 22.76 503.58 19.19 583.61 curveto +stroke +0 0 0 edgecolor +newpath 15.68 583.77 moveto +18.74 593.92 lineto +22.67 584.08 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 15.68 583.77 moveto +18.74 593.92 lineto +22.67 584.08 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +165.5 186.4 moveto 41 (>/>>) alignedtext +grestore +% atra->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 380.66 163.64 moveto +310.44 138.59 132.7 88.21 54 186 curveto +29.1 216.94 20.67 493.96 18.58 583.92 curveto +stroke +0 0 0 edgecolor +newpath 15.08 583.86 moveto +18.35 593.94 lineto +22.08 584.02 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 15.08 583.86 moveto +18.35 593.94 lineto +22.08 584.02 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +164.5 135.4 moveto 43 (c-/@>) alignedtext +grestore +% c->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 396.29 936.8 moveto +388.72 948.33 393.29 961 410 961 curveto +422.14 961 427.87 954.31 427.2 946.23 curveto +stroke +0 0 0 edgecolor +newpath 430.46 944.96 moveto +423.71 936.8 lineto +423.89 947.39 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 430.46 944.96 moveto +423.71 936.8 lineto +423.89 947.39 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +400.5 965.4 moveto 19 (c/c) alignedtext +grestore +% crs +gsave +1 setlinewidth +0 0 0 nodecolor +647 472 25.02 25.02 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +637 467.9 moveto 20 (crs) alignedtext +grestore +% c->crs +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 415.11 907.71 moveto +422.42 882.21 435.66 832.89 442 790 curveto +446.82 757.4 437.37 517.95 460 494 curveto +498.69 453.07 570 457.27 612.5 464.39 curveto +stroke +0 0 0 edgecolor +newpath 611.93 467.84 moveto +622.4 466.19 lineto +613.18 460.96 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 611.93 467.84 moveto +622.4 466.19 lineto +613.18 460.96 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +460.5 498.4 moveto 85 (] \(docs only\)) alignedtext +grestore +% cla +gsave +1 setlinewidth +0 0 0 nodecolor +647 1256 23.82 24.32 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +637.5 1251.9 moveto 19 (cla) alignedtext +grestore +% c->cla +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 410.25 943.35 moveto +411.47 1003.13 418.59 1190.77 460 1231 curveto +500.39 1270.24 570.88 1268.05 612.81 1262.38 curveto +stroke +0 0 0 edgecolor +newpath 613.45 1265.82 moveto +622.82 1260.88 lineto +612.42 1258.9 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 613.45 1265.82 moveto +622.82 1260.88 lineto +612.42 1258.9 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +496.5 1268.4 moveto 13 (<) alignedtext +grestore +% cat +gsave +1 setlinewidth +0 0 0 nodecolor +647 790 25.91 26.41 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +636 785.9 moveto 22 (cat) alignedtext +grestore +% c->cat +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 413.94 907.26 moveto +421.74 873.87 440.15 803.81 460 790 curveto +504.83 758.8 571.5 768.61 611.95 779 curveto +stroke +0 0 0 edgecolor +newpath 611.07 782.39 moveto +621.63 781.63 lineto +612.9 775.63 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 611.07 782.39 moveto +621.63 781.63 lineto +612.9 775.63 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +495.5 794.4 moveto 15 (@) alignedtext +grestore +% crs->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 623.38 481.3 moveto +575.38 500.42 470.06 543.56 460 558 curveto +430.44 600.43 449.99 738.91 442 790 curveto +436.09 827.82 425.19 870.7 417.77 897.79 curveto +stroke +0 0 0 edgecolor +newpath 414.32 897.12 moveto +415.02 907.7 lineto +421.06 899 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 414.32 897.12 moveto +415.02 907.7 lineto +421.06 899 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +492 562.4 moveto 22 (c-/]) alignedtext +grestore +% ce +gsave +1 setlinewidth +0 0 0 nodecolor +1386 472 22.24 22.24 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1378 467.9 moveto 16 (ce) alignedtext +grestore +% crs->ce +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 672.01 472 moveto +782.87 472 1229.87 472 1353.93 472 curveto +stroke +0 0 0 edgecolor +newpath 1353.94 475.5 moveto +1363.94 472 lineto +1353.94 468.5 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1353.94 475.5 moveto +1363.94 472 lineto +1353.94 468.5 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1042 476.4 moveto 6 (]) alignedtext +grestore +% cla->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 624.5 1245.82 moveto +576.01 1223.78 465.53 1173.08 460 1166 curveto +434.23 1133.03 418.62 1009.42 412.71 953.18 curveto +stroke +0 0 0 edgecolor +newpath 416.19 952.78 moveto +411.69 943.19 lineto +409.23 953.49 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 416.19 952.78 moveto +411.69 943.19 lineto +409.23 953.49 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +488.5 1213.4 moveto 29 (c-/<) alignedtext +grestore +% u +gsave +1 setlinewidth +0 0 0 nodecolor +921 1142 18.07 18.07 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +916 1137.9 moveto 10 (u) alignedtext +grestore +% cla->u +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 669.89 1246.48 moveto +720.4 1225.46 841.14 1175.22 894.68 1152.95 curveto +stroke +0 0 0 edgecolor +newpath 896.26 1156.08 moveto +904.15 1149.01 lineto +893.57 1149.62 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 896.26 1156.08 moveto +904.15 1149.01 lineto +893.57 1149.62 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +748.5 1216.4 moveto 91 (< \(emit text\)) alignedtext +grestore +% cat->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 620.73 793.72 moveto +582.19 800.26 509.55 816.84 460 854 curveto +444.02 865.99 431.19 884.58 422.52 899.74 curveto +stroke +0 0 0 edgecolor +newpath 419.39 898.18 moveto +417.68 908.64 lineto +425.53 901.53 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 419.39 898.18 moveto +417.68 908.64 lineto +425.53 901.53 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +487.5 858.4 moveto 31 (c-/@) alignedtext +grestore +% catla +gsave +1 setlinewidth +0 0 0 nodecolor +921 1011 34.24 34.74 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +904 1006.9 moveto 34 (catla) alignedtext +grestore +% cat->catla +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 662.44 811.45 moveto +680.91 836.18 713.69 877.1 748 906 curveto +789.74 941.16 844.52 972.31 881.15 991.37 curveto +stroke +0 0 0 edgecolor +newpath 879.56 994.49 moveto +890.05 995.95 lineto +882.76 988.26 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 879.56 994.49 moveto +890.05 995.95 lineto +882.76 988.26 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +787.5 971.4 moveto 13 (<) alignedtext +grestore +% catra +gsave +1 setlinewidth +0 0 0 nodecolor +921 683 36.13 36.13 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +903 678.9 moveto 36 (catra) alignedtext +grestore +% cat->catra +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 671.8 780.32 moveto +718.37 762.13 819.45 722.66 877.55 699.97 curveto +stroke +0 0 0 edgecolor +newpath 879.14 703.1 moveto +887.19 696.2 lineto +876.6 696.58 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 879.14 703.1 moveto +887.19 696.2 lineto +876.6 696.58 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +787.5 753.4 moveto 13 (>) alignedtext +grestore +% catla->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 886.88 1018.23 moveto +872.48 1020.94 855.5 1023.67 840 1025 curveto +799.26 1028.49 788.77 1028.17 748 1025 curveto +618.84 1014.97 574.06 1041.43 460 980 curveto +446.33 972.64 434.46 960.05 425.73 948.77 curveto +stroke +0 0 0 edgecolor +newpath 428.41 946.5 moveto +419.68 940.5 lineto +422.76 950.64 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 428.41 946.5 moveto +419.68 940.5 lineto +422.76 950.64 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +626.5 1027.4 moveto 41 (</<<) alignedtext +grestore +% catla->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 886.46 1006.95 moveto +851.48 1002.71 795.87 995.64 748 988 curveto +632.3 969.54 495.32 942.35 437.71 930.67 curveto +stroke +0 0 0 edgecolor +newpath 438.39 927.24 moveto +427.9 928.68 lineto +437 934.1 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 438.39 927.24 moveto +427.9 928.68 lineto +437 934.1 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +625.5 988.4 moveto 43 (c-/@<) alignedtext +grestore +% catra->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 885.32 677.41 moveto +799.51 666.14 579.87 650.68 460 764 curveto +440.63 782.31 423.51 856.07 415.25 897.1 curveto +stroke +0 0 0 edgecolor +newpath 411.79 896.53 moveto +413.29 907.02 lineto +418.66 897.88 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 411.79 896.53 moveto +413.29 907.02 lineto +418.66 897.88 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +626.5 704.4 moveto 41 (>/>>) alignedtext +grestore +% catra->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 886.66 671.97 moveto +822.29 653 679.54 618.96 564 649 curveto +512.78 662.32 491.51 664.48 460 707 curveto +437.52 737.33 450.35 753.19 442 790 curveto +433.58 827.12 423.36 869.85 416.77 897.12 curveto +stroke +0 0 0 edgecolor +newpath 413.3 896.58 moveto +414.35 907.12 lineto +420.11 898.22 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 413.3 896.58 moveto +414.35 907.12 lineto +420.11 898.22 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +625.5 653.4 moveto 43 (c-/@>) alignedtext +grestore +% ce->t +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1384.8 449.87 moveto +1378.46 356.55 1340.69 0 1134 0 curveto +186 0 186 0 186 0 curveto +103.66 0 88.61 55.29 54 130 curveto +34.94 171.15 22.4 486.48 18.96 583.61 curveto +stroke +0 0 0 edgecolor +newpath 15.45 583.73 moveto +18.6 593.84 lineto +22.45 583.97 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 15.45 583.73 moveto +18.6 593.84 lineto +22.45 583.97 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +564 4.4 moveto 166 (c- \(emit text; endquote\)) alignedtext +grestore +% ce->ce +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1377.27 492.58 moveto +1376.06 502.84 1378.97 512 1386 512 curveto +1390.5 512 1393.32 508.24 1394.44 502.84 curveto +stroke +0 0 0 edgecolor +newpath 1397.95 502.68 moveto +1394.73 492.58 lineto +1390.95 502.48 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1397.95 502.68 moveto +1394.73 492.58 lineto +1390.95 502.48 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1378 516.4 moveto 16 (]/]) alignedtext +grestore +% u->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 902.9 1141.7 moveto +836.31 1139.68 601.53 1124.45 460 1011 curveto +440.84 995.64 427.53 970.8 419.5 951.76 curveto +stroke +0 0 0 edgecolor +newpath 422.72 950.41 moveto +415.77 942.41 lineto +416.22 953 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 422.72 950.41 moveto +415.77 942.41 lineto +416.22 953 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +584 1125.4 moveto 126 (eol- \(prepend <<\)) alignedtext +grestore +% u->u +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 905.11 1151.22 moveto +891.48 1163.35 896.78 1178 921 1178 curveto +939.92 1178 947.29 1169.06 943.12 1159.36 curveto +stroke +0 0 0 edgecolor +newpath 945.75 1157.04 moveto +936.89 1151.22 lineto +940.19 1161.29 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 945.75 1157.04 moveto +936.89 1151.22 lineto +940.19 1161.29 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +911.5 1182.4 moveto 19 (c/c) alignedtext +grestore +% uls +gsave +1 setlinewidth +0 0 0 nodecolor +1134 1245 25.02 25.02 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1124 1240.9 moveto 20 (uls) alignedtext +grestore +% u->uls +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 937.36 1149.91 moveto +972.56 1166.93 1056 1207.28 1102.07 1229.56 curveto +stroke +0 0 0 edgecolor +newpath 1100.78 1232.82 moveto +1111.3 1234.02 lineto +1103.82 1226.52 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1100.78 1232.82 moveto +1111.3 1234.02 lineto +1103.82 1226.52 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1037 1226.4 moveto 16 ([/[) alignedtext +grestore +% urs +gsave +1 setlinewidth +0 0 0 nodecolor +1134 829 25.91 26.41 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1123 824.9 moveto 22 (urs) alignedtext +grestore +% u->urs +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 933.61 1128.95 moveto +948.39 1112.79 972.35 1083.74 984 1054 curveto +1007.36 994.35 967.43 964.93 1002 911 curveto +1024.6 875.75 1068.31 853.1 1099.23 840.73 curveto +stroke +0 0 0 edgecolor +newpath 1100.69 843.92 moveto +1108.78 837.07 lineto +1098.19 837.38 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1100.69 843.92 moveto +1108.78 837.07 lineto +1098.19 837.38 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1002.5 915.4 moveto 85 (] \(docs only\)) alignedtext +grestore +% ura +gsave +1 setlinewidth +0 0 0 nodecolor +1134 1112 27.8 27.8 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1122 1107.9 moveto 24 (ura) alignedtext +grestore +% u->ura +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 939.16 1139.44 moveto +973.77 1134.57 1049.82 1123.86 1096.01 1117.35 curveto +stroke +0 0 0 edgecolor +newpath 1096.53 1120.81 moveto +1105.94 1115.95 lineto +1095.55 1113.88 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1096.53 1120.81 moveto +1105.94 1115.95 lineto +1095.55 1113.88 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1038.5 1134.4 moveto 13 (>) alignedtext +grestore +% uls->u +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1109.2 1250.63 moveto +1081.53 1255.6 1036.18 1259.72 1002 1243 curveto +969.92 1227.3 946.45 1192.18 933.27 1167.79 curveto +stroke +0 0 0 edgecolor +newpath 936.28 1165.99 moveto +928.57 1158.72 lineto +930.06 1169.21 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 936.28 1165.99 moveto +928.57 1158.72 lineto +930.06 1169.21 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1038.5 1258.4 moveto 13 (c-) alignedtext +grestore +% uc +gsave +1 setlinewidth +0 0 0 nodecolor +1386 1245 22.24 22.24 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1378 1240.9 moveto 16 (uc) alignedtext +grestore +% uls->uc +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1159.08 1245 moveto +1205.11 1245 1302.98 1245 1353.83 1245 curveto +stroke +0 0 0 edgecolor +newpath 1353.93 1248.5 moveto +1363.93 1245 lineto +1353.93 1241.5 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1353.93 1248.5 moveto +1363.93 1245 lineto +1353.93 1241.5 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1255 1249.4 moveto 16 ([/[) alignedtext +grestore +% urs->ce +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1149.2 807.47 moveto +1192.99 745.44 1319.36 566.4 1367.28 498.52 curveto +stroke +0 0 0 edgecolor +newpath 1370.34 500.26 moveto +1373.24 490.07 lineto +1364.62 496.22 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1370.34 500.26 moveto +1373.24 490.07 lineto +1364.62 496.22 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1180 764.4 moveto 166 (] \(not use; prepend <<\)) alignedtext +grestore +% urs->u +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1126.82 854.04 moveto +1119.69 875.54 1107.05 906.39 1088 928 curveto +1058.15 961.86 1028.5 945.47 1002 982 curveto +982.63 1008.7 997.84 1024.06 984 1054 curveto +972.55 1078.77 953.81 1103.76 939.74 1120.74 curveto +stroke +0 0 0 edgecolor +newpath 937.05 1118.51 moveto +933.26 1128.4 lineto +942.39 1123.03 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 937.05 1118.51 moveto +933.26 1128.4 lineto +942.39 1123.03 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1034 986.4 moveto 22 (c-/]) alignedtext +grestore +% ura->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1130.95 1084.15 moveto +1125.23 1037.23 1111.31 945.43 1088 924 curveto +991.99 835.73 554.16 901.03 438.23 920.17 curveto +stroke +0 0 0 edgecolor +newpath 437.33 916.77 moveto +428.04 921.87 lineto +438.48 923.68 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 437.33 916.77 moveto +428.04 921.87 lineto +438.48 923.68 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +750.5 887.4 moveto 87 (> \(emit use\)) alignedtext +grestore +% ura->u +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1107.59 1101.82 moveto +1101.27 1099.82 1094.47 1098.03 1088 1097 curveto +1050.25 1091 1039 1087.41 1002 1097 curveto +980.91 1102.46 959.47 1114.74 943.95 1125.09 curveto +stroke +0 0 0 edgecolor +newpath 941.78 1122.34 moveto +935.54 1130.9 lineto +945.76 1128.1 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 941.78 1122.34 moveto +935.54 1130.9 lineto +945.76 1128.1 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1030.5 1101.4 moveto 29 (c-/>) alignedtext +grestore +% uc->c +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1368.05 1257.92 moveto +1328.1 1285.37 1227.92 1347 1134 1347 curveto +647 1347 647 1347 647 1347 curveto +559.44 1347 516.03 1352.29 460 1285 curveto +438.84 1259.59 418.81 1034.38 412.24 953.64 curveto +stroke +0 0 0 edgecolor +newpath 415.7 952.93 moveto +411.41 943.25 lineto +408.72 953.49 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 415.7 952.93 moveto +411.41 943.25 lineto +408.72 953.49 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +858 1351.4 moveto 126 (eol- \(prepend <<\)) alignedtext +grestore +% uc->uc +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1377.27 1265.58 moveto +1376.06 1275.84 1378.97 1285 1386 1285 curveto +1390.5 1285 1393.32 1281.24 1394.44 1275.84 curveto +stroke +0 0 0 edgecolor +newpath 1397.95 1275.68 moveto +1394.73 1265.58 lineto +1390.95 1275.48 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1397.95 1275.68 moveto +1394.73 1265.58 lineto +1390.95 1275.48 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1376.5 1289.4 moveto 19 (c/c) alignedtext +grestore +% ucrs +gsave +1 setlinewidth +0 0 0 nodecolor +1492 1175 30.77 31.27 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1477.5 1170.9 moveto 29 (ucrs) alignedtext +grestore +% uc->ucrs +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1404.46 1232.81 moveto +1419.04 1223.18 1439.8 1209.47 1457.44 1197.82 curveto +stroke +0 0 0 edgecolor +newpath 1459.39 1200.73 moveto +1465.81 1192.3 lineto +1455.53 1194.89 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1459.39 1200.73 moveto +1465.81 1192.3 lineto +1455.53 1194.89 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1426 1222.4 moveto 16 (]/]) alignedtext +grestore +% ucrs->uc +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1460.27 1176.94 moveto +1448.75 1178.83 1436.09 1182.39 1426 1189 curveto +1415.64 1195.78 1407.11 1206.25 1400.63 1216.31 curveto +stroke +0 0 0 edgecolor +newpath 1397.6 1214.56 moveto +1395.47 1224.94 lineto +1403.61 1218.16 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1397.6 1214.56 moveto +1395.47 1224.94 lineto +1403.61 1218.16 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1427.5 1193.4 moveto 13 (c-) alignedtext +grestore +% uce +gsave +1 setlinewidth +0 0 0 nodecolor +1605 1068 27.99 28.49 ellipse_path stroke +0 0 0 nodecolor +14 /Times-Roman set_font +1592.5 1063.9 moveto 25 (uce) alignedtext +grestore +% ucrs->uce +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1514.87 1153.35 moveto +1532.86 1136.31 1558 1112.5 1577.16 1094.36 curveto +stroke +0 0 0 edgecolor +newpath 1579.59 1096.88 moveto +1584.45 1087.46 lineto +1574.78 1091.8 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1579.59 1096.88 moveto +1584.45 1087.46 lineto +1574.78 1091.8 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1542 1128.4 moveto 16 (]/]) alignedtext +grestore +% uce->u +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1583.16 1049.99 moveto +1561.44 1033.95 1526.47 1013 1492 1013 curveto +1134 1013 1134 1013 1134 1013 curveto +1052.01 1013 974.6 1083.8 939.98 1120.5 curveto +stroke +0 0 0 edgecolor +newpath 937.09 1118.47 moveto +932.88 1128.19 lineto +942.23 1123.22 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 937.09 1118.47 moveto +932.88 1128.19 lineto +942.23 1123.22 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1379.5 1017.4 moveto 13 (c-) alignedtext +grestore +% uce->uce +gsave +1 setlinewidth +0 0 0 edgecolor +newpath 1594.94 1094.47 moveto +1594.37 1105.09 1597.72 1114 1605 1114 curveto +1609.78 1114 1612.86 1110.16 1614.26 1104.52 curveto +stroke +0 0 0 edgecolor +newpath 1617.75 1104.72 moveto +1615.06 1094.47 lineto +1610.78 1104.16 lineto +closepath fill +1 setlinewidth +solid +0 0 0 edgecolor +newpath 1617.75 1104.72 moveto +1615.06 1094.47 lineto +1610.78 1104.16 lineto +closepath stroke +0 0 0 edgecolor +14 /Times-Roman set_font +1597 1118.4 moveto 16 (]/]) alignedtext +grestore +endpage +showpage +grestore +%%PageTrailer +%%EndPage: 1 +%%Trailer +%%Pages: 1 +%%BoundingBox: 36 36 576 682 +end +restore +%%EOF diff --git a/web/noweb/src/c/match.c b/web/noweb/src/c/match.c new file mode 100644 index 0000000000..ced38e2316 --- /dev/null +++ b/web/noweb/src/c/match.c @@ -0,0 +1,26 @@ +#line 10 "match.nw" +static char rcsid[] = "$Id: match.nw,v 1.21 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <string.h> +#include "match.h" +static int matches(char *line, char *search) { + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return !strncmp(line,search,strlen(search)); +} +#line 24 "match.nw" +int is_keyword(char *line, char *keyword) { + char low_at_sign = '@'; + return *line==low_at_sign && matches(line+1,keyword) && + (line[strlen(keyword)+1]==' ' || line[strlen(keyword)+1]=='\n' || + line[strlen(keyword)+1]=='\0'); +} +int is_begin(char *line, char *type) { + return is_keyword(line,"begin") && matches(line+1+6,type); +} +int is_end(char *line, char *type) { + return is_keyword(line,"end") && matches (line+1+4,type); +} +int is_index(char *line, char *type) { + return is_keyword(line,"index") && matches(line+1+6,type); +} diff --git a/web/noweb/src/c/match.h b/web/noweb/src/c/match.h new file mode 100644 index 0000000000..a45efb1bbc --- /dev/null +++ b/web/noweb/src/c/match.h @@ -0,0 +1,4 @@ +extern int is_keyword(char *line, char *keyword); +extern int is_begin(char *line, char *type); +extern int is_end(char *line, char *type); +extern int is_index(char *line, char *type); diff --git a/web/noweb/src/c/match.nw b/web/noweb/src/c/match.nw new file mode 100644 index 0000000000..2d015a8a13 --- /dev/null +++ b/web/noweb/src/c/match.nw @@ -0,0 +1,38 @@ +\subsection{Code to recognize [[noweb]] markup on input} +We recognize the input using matching functions, which can find [[@begin]], +[[@end]], and other useful information. +<<header>>= +extern int is_keyword(char *line, char *keyword); +extern int is_begin(char *line, char *type); +extern int is_end(char *line, char *type); +extern int is_index(char *line, char *type); +<<*>>= +static char rcsid[] = "$Id: match.nw,v 1.21 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <string.h> +#include "match.h" +static int matches(char *line, char *search) { + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return !strncmp(line,search,strlen(search)); +} +@ +On 24 March 1996, I changed this to accept zero as a terminator +because I strip newlines in the prettyprinter. +Too bad if we just got worse at detecting bogus filters. +<<*>>= +int is_keyword(char *line, char *keyword) { + char low_at_sign = '@'; + return *line==low_at_sign && matches(line+1,keyword) && + (line[strlen(keyword)+1]==' ' || line[strlen(keyword)+1]=='\n' || + line[strlen(keyword)+1]=='\0'); +} +int is_begin(char *line, char *type) { + return is_keyword(line,"begin") && matches(line+1+6,type); +} +int is_end(char *line, char *type) { + return is_keyword(line,"end") && matches (line+1+4,type); +} +int is_index(char *line, char *type) { + return is_keyword(line,"index") && matches(line+1+6,type); +} diff --git a/web/noweb/src/c/mnt.c b/web/noweb/src/c/mnt.c new file mode 100644 index 0000000000..c4d2aeb351 --- /dev/null +++ b/web/noweb/src/c/mnt.c @@ -0,0 +1,172 @@ +#line 9 "mnt.nw" +static char rcsid[] = "$Id: mnt.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#define _POSIX_C_SOURCE 200809L +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> +#include "modules.h" +#include "modtrees.h" +#include "notangle.h" +#include "errors.h" +#include "columns.h" +#include "strsave.h" + +#line 55 "mnt.nw" +void add_uses_to_usecounts(Module mp); +void emit_if_unused_and_conforming(Module mp); +#line 91 "mnt.nw" +static void emitfile(char *modname); + +#line 28 "mnt.nw" +#define Clocformat "#line %L \"%F\"%N" +static char *locformat = Clocformat; + +int main(int argc, char **argv) { + int i; + + tabsize = 0; /* default for nt is not to use tabs */ + + progname = argv[0]; + finalstage = 1; + +#line 49 "mnt.nw" +read_defs(stdin); +apply_each_module(remove_final_newline); +#line 39 "mnt.nw" + for (i=1; i<argc; i++) + switch (*argv[i]) { + case '-': +#line 150 "mnt.nw" + switch (*++argv[i]) { + case 'a': + if (strcmp(argv[i], "all")) + errormsg(Warning, "Ignoring unknown option -%s", argv[i]); + else { +#line 52 "mnt.nw" +apply_each_module(add_uses_to_usecounts); +apply_each_module(emit_if_unused_and_conforming); +#line 154 "mnt.nw" + } + break; + case 't': /* set tab size or turn off */ + if (isdigit(argv[i][1])) + tabsize = atoi(argv[i]+1); + else if (argv[i][1]==0) + tabsize = 0; /* no tabs */ + else + errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]); + break; + case 'L': /* have a #line number format */ + locformat = argv[i] + 1; + if (!*locformat) locformat = Clocformat; + break; + default: + errormsg(Warning, "Ignoring unknown option -%s", argv[i]); + } +#line 41 "mnt.nw" + break; + default: emitfile(argv[i]); break; + } + nowebexit(NULL); + return errorlevel; /* slay warning */ +} +#line 58 "mnt.nw" +void add_uses_to_usecounts(Module mp) { + Module used; + struct modpart *p; + for (p=mp->head; p!=NULL; p=p->next) + if (p->ptype == MODULE) { + used = lookup(p->contents); + if (used != NULL) + used->usecount++; + } +} +#line 74 "mnt.nw" +void emit_if_unused_and_conforming(Module mp) { + char *index; + if (mp->usecount == 0 && strpbrk(mp->name, " \n\t\v\r\f") == NULL) { + if (index = strpbrk(mp->name, "[](){}!$&<>*?;|^`'\\\""), + index == NULL || (index[0] == '*' && index[1] == 0)) { + if (index == mp->name) + errormsg(Error, "<<*>> is not a good chunk name for noweb; " + "use notangle instead"); + else + emitfile(mp->name); + } else { + errormsg(Error, "<<%s>> cannot be an output chunk; " + "it contains a metacharacter", mp->name); + } + } +} +#line 93 "mnt.nw" +static void emitfile(char *modname) { + Module root = lookup(modname); + FILE *fp = tmpfile(); + char *lfmt, *filename; + +#line 111 "mnt.nw" +{ int n = strlen(modname) - 1; + if (n >= 0 && modname[n] == '*') { + lfmt = locformat; + filename = strsave(modname); + filename[n] = 0; + } else { + lfmt = ""; + filename = modname; + } +} +#line 98 "mnt.nw" + +#line 145 "mnt.nw" +if (root == NULL) { + errormsg(Error, "Chunk <<%s>> is undefined", filename); + return; +} +#line 99 "mnt.nw" + if (fp == NULL) errormsg(Fatal, "Calling tmpfile() failed"); + +#line 122 "mnt.nw" +resetloc(); +(void) expand(root, 0, 0, 0, lfmt, fp); +putc('\n', fp); +#line 101 "mnt.nw" + rewind(fp); + +#line 127 "mnt.nw" +{ FILE *dest = fopen(filename, "r"); + if (dest != NULL) { + int x, y; + do { + x = getc(fp); + y = getc(dest); + } while (x == y && x != EOF); + fclose(dest); + if (x == y) { + fclose(fp); + return; + } + } +} +#line 103 "mnt.nw" + remove(filename); + fclose(fp); + fp = fopen(filename, "w"); + if (fp == NULL) { +#line 142 "mnt.nw" +errormsg(Error, "Can't open output file %s", filename); +return; +#line 106 "mnt.nw" + } + +#line 122 "mnt.nw" +resetloc(); +(void) expand(root, 0, 0, 0, lfmt, fp); +putc('\n', fp); +#line 108 "mnt.nw" + fclose(fp); +} diff --git a/web/noweb/src/c/mnt.nw b/web/noweb/src/c/mnt.nw new file mode 100644 index 0000000000..f18f6c5a30 --- /dev/null +++ b/web/noweb/src/c/mnt.nw @@ -0,0 +1,171 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode -*- +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +\subsection{Expanding multiple files from a single source} +This main program is used to make the monolithic {\tt noweb} +script efficient. +Tips of the hat to Ross Williams and Preston Briggs. +<<*>>= +static char rcsid[] = "$Id: mnt.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#define _POSIX_C_SOURCE 200809L +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> +#include "modules.h" +#include "modtrees.h" +#include "notangle.h" +#include "errors.h" +#include "columns.h" +#include "strsave.h" + +<<local prototypes>> + +#define Clocformat "#line %L \"%F\"%N" +static char *locformat = Clocformat; + +int main(int argc, char **argv) { + int i; + + tabsize = 0; /* default for nt is not to use tabs */ + + progname = argv[0]; + finalstage = 1; + <<read standard input into tree>> + for (i=1; i<argc; i++) + switch (*argv[i]) { + case '-': <<handle option in [[argv[i]]]>> break; + default: emitfile(argv[i]); break; + } + nowebexit(NULL); + return errorlevel; /* slay warning */ +} +@ +<<read standard input into tree>>= +read_defs(stdin); +apply_each_module(remove_final_newline); +<<write out all conforming roots>>= +apply_each_module(add_uses_to_usecounts); +apply_each_module(emit_if_unused_and_conforming); +<<local prototypes>>= +void add_uses_to_usecounts(Module mp); +void emit_if_unused_and_conforming(Module mp); +<<*>>= +void add_uses_to_usecounts(Module mp) { + Module used; + struct modpart *p; + for (p=mp->head; p!=NULL; p=p->next) + if (p->ptype == MODULE) { + used = lookup(p->contents); + if (used != NULL) + used->usecount++; + } +} +@ + +A conforming output chunk name has no spaces and no metacharacters. +Names with spaces are silently ignored, but names that are otherwise +conforming but that contain metacharacters cause complaints. +<<*>>= +void emit_if_unused_and_conforming(Module mp) { + char *index; + if (mp->usecount == 0 && strpbrk(mp->name, " \n\t\v\r\f") == NULL) { + if (index = strpbrk(mp->name, "[](){}!$&<>*?;|^`'\\\""), + index == NULL || (index[0] == '*' && index[1] == 0)) { + if (index == mp->name) + errormsg(Error, "@<<*@>> is not a good chunk name for noweb; " + "use notangle instead"); + else + emitfile(mp->name); + } else { + errormsg(Error, "@<<%s@>> cannot be an output chunk; " + "it contains a metacharacter", mp->name); + } + } +} +<<local prototypes>>= +static void emitfile(char *modname); +<<*>>= +static void emitfile(char *modname) { + Module root = lookup(modname); + FILE *fp = tmpfile(); + char *lfmt, *filename; + <<set [[lfmt]] and [[filename]] from [[modname]]>> + <<complain and [[return]] if [[root == NULL]]>> + if (fp == NULL) errormsg(Fatal, "Calling tmpfile() failed"); + <<expand [[root]] onto [[fp]]>> + rewind(fp); + <<if file [[filename]] has the same contents as [[fp]], close [[fp]] and [[return]]>> + remove(filename); + fclose(fp); + fp = fopen(filename, "w"); + if (fp == NULL) {<<complain about [[filename]] and [[return]]>>} + <<expand [[root]] onto [[fp]]>> + fclose(fp); +} +<<set [[lfmt]] and [[filename]] from [[modname]]>>= +{ int n = strlen(modname) - 1; + if (n >= 0 && modname[n] == '*') { + lfmt = locformat; + filename = strsave(modname); + filename[n] = 0; + } else { + lfmt = ""; + filename = modname; + } +} +<<expand [[root]] onto [[fp]]>>= +resetloc(); +(void) expand(root, 0, 0, 0, lfmt, fp); +putc('\n', fp); +@ +<<if file [[filename]] has the same contents as [[fp]], close [[fp]] and [[return]]>>= +{ FILE *dest = fopen(filename, "r"); + if (dest != NULL) { + int x, y; + do { + x = getc(fp); + y = getc(dest); + } while (x == y && x != EOF); + fclose(dest); + if (x == y) { + fclose(fp); + return; + } + } +} +<<complain about [[filename]] and [[return]]>>= +errormsg(Error, "Can't open output file %s", filename); +return; +<<complain and [[return]] if [[root == NULL]]>>= +if (root == NULL) { + errormsg(Error, "Chunk @<<%s@>> is undefined", filename); + return; +} +<<handle option in [[argv[i]]]>>= + switch (*++argv[i]) { + case 'a': + if (strcmp(argv[i], "all")) + errormsg(Warning, "Ignoring unknown option -%s", argv[i]); + else {<<write out all conforming roots>>} + break; + case 't': /* set tab size or turn off */ + if (isdigit(argv[i][1])) + tabsize = atoi(argv[i]+1); + else if (argv[i][1]==0) + tabsize = 0; /* no tabs */ + else + errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]); + break; + case 'L': /* have a #line number format */ + locformat = argv[i] + 1; + if (!*locformat) locformat = Clocformat; + break; + default: + errormsg(Warning, "Ignoring unknown option -%s", argv[i]); + } +@ diff --git a/web/noweb/src/c/modtrees.c b/web/noweb/src/c/modtrees.c new file mode 100644 index 0000000000..b0b044cda3 --- /dev/null +++ b/web/noweb/src/c/modtrees.c @@ -0,0 +1,77 @@ +#line 14 "modtrees.nw" +static char rcsid[] = "$Id: modtrees.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "strsave.h" +#include "modules.h" +#include "modtrees.h" +#include "errors.h" + +typedef struct tnode { /* tree node */ + struct tnode *left, *right; + Module data; +} TNODE; + +static struct tnode *root=NULL; + +#line 62 "modtrees.nw" +static Module insert_tree(TNODE **rootptr, char *modname); +static Module lookup_tree(TNODE **rootptr, char *modname); +#line 33 "modtrees.nw" +Module insert (char *modname) { + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return insert_tree (&root, modname); +} + +static Module +insert_tree(TNODE **rootptr, char *modname) { + if (*rootptr==NULL) { + +#line 58 "modtrees.nw" + checkptr(*rootptr=(struct tnode *) malloc (sizeof(struct tnode))); + (*rootptr)->left = (*rootptr)->right = NULL; +#line 43 "modtrees.nw" + return (*rootptr)->data = newmodule(modname); + } + if (strcmp((*rootptr)->data->name,modname)==0) { + return (*rootptr)->data; + } else if (strcmp((*rootptr)->data->name,modname)<0) { + return insert_tree(&((*rootptr)->left),modname); + } else /* >0 */ { + return insert_tree(&((*rootptr)->right),modname); + } + +} +#line 66 "modtrees.nw" +Module lookup (char *modname) { + return lookup_tree (&root, modname); +} + +static Module +lookup_tree(TNODE **rootptr, char *modname) { + if (*rootptr==NULL) { + return NULL; + } + if (strcmp((*rootptr)->data->name,modname)==0) { + return (*rootptr)->data; + } else if (strcmp((*rootptr)->data->name,modname)<0) { + return lookup_tree(&((*rootptr)->left),modname); + } else /* >0 */ { + return lookup_tree(&((*rootptr)->right),modname); + } +} +#line 90 "modtrees.nw" +static +void apply_to_tree(TNODE *p, void (*fun)(Module)) { + if (p != NULL) { + apply_to_tree(p->left,fun); + (*fun)(p->data); + apply_to_tree(p->right,fun); + } +} +void apply_each_module(void (*fun)(Module)) { + apply_to_tree(root,fun); +} diff --git a/web/noweb/src/c/modtrees.h b/web/noweb/src/c/modtrees.h new file mode 100644 index 0000000000..94b3b7357a --- /dev/null +++ b/web/noweb/src/c/modtrees.h @@ -0,0 +1,4 @@ +Module insert (char *modname); /* add a module to the world */ +Module lookup (char *modname); /* return ptr to module or NULL */ +void apply_each_module(void (*fun)(Module)); + /* apply [[fun]] to each module in the tree */ diff --git a/web/noweb/src/c/modtrees.nw b/web/noweb/src/c/modtrees.nw new file mode 100644 index 0000000000..1d5220c3cd --- /dev/null +++ b/web/noweb/src/c/modtrees.nw @@ -0,0 +1,100 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +@ +\subsection{Support for looking up modules by name} +Trees of modules. +A table of modules with insert and lookup, but no delete. +The key is the module name. +<<header>>= +Module insert (char *modname); /* add a module to the world */ +Module lookup (char *modname); /* return ptr to module or NULL */ +void apply_each_module(void (*fun)(Module)); + /* apply [[fun]] to each module in the tree */ +<<*>>= +static char rcsid[] = "$Id: modtrees.nw,v 2.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "strsave.h" +#include "modules.h" +#include "modtrees.h" +#include "errors.h" + +typedef struct tnode { /* tree node */ + struct tnode *left, *right; + Module data; +} TNODE; + +static struct tnode *root=NULL; + +<<Function declarations>> +<<*>>= +Module insert (char *modname) { + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return insert_tree (&root, modname); +} + +static Module +insert_tree(TNODE **rootptr, char *modname) { + if (*rootptr==NULL) { + <<Allocate new tnode in **rootptr>> + return (*rootptr)->data = newmodule(modname); + } + if (strcmp((*rootptr)->data->name,modname)==0) { + return (*rootptr)->data; + } else if (strcmp((*rootptr)->data->name,modname)<0) { + return insert_tree(&((*rootptr)->left),modname); + } else /* >0 */ { + return insert_tree(&((*rootptr)->right),modname); + } + +} +@ + +Node allocation is perfectly standard. +<<Allocate new tnode in **rootptr>>= + checkptr(*rootptr=(struct tnode *) malloc (sizeof(struct tnode))); + (*rootptr)->left = (*rootptr)->right = NULL; +@ +<<Function declarations>>= +static Module insert_tree(TNODE **rootptr, char *modname); +static Module lookup_tree(TNODE **rootptr, char *modname); +@ +<<*>>= +Module lookup (char *modname) { + return lookup_tree (&root, modname); +} + +static Module +lookup_tree(TNODE **rootptr, char *modname) { + if (*rootptr==NULL) { + return NULL; + } + if (strcmp((*rootptr)->data->name,modname)==0) { + return (*rootptr)->data; + } else if (strcmp((*rootptr)->data->name,modname)<0) { + return lookup_tree(&((*rootptr)->left),modname); + } else /* >0 */ { + return lookup_tree(&((*rootptr)->right),modname); + } +} +@ + +[[apply_each_module]] takes as argument a function and applies +it to each module in the tree. +This makes it easy to, for example, remove trailing newlines +from each module. +<<*>>= +static +void apply_to_tree(TNODE *p, void (*fun)(Module)) { + if (p != NULL) { + apply_to_tree(p->left,fun); + (*fun)(p->data); + apply_to_tree(p->right,fun); + } +} +void apply_each_module(void (*fun)(Module)) { + apply_to_tree(root,fun); +} diff --git a/web/noweb/src/c/modules.c b/web/noweb/src/c/modules.c new file mode 100644 index 0000000000..2229bf0419 --- /dev/null +++ b/web/noweb/src/c/modules.c @@ -0,0 +1,226 @@ +#line 50 "modules.nw" +static char rcsid[] = "$Id: modules.nw,v 2.26 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "modules.h" +#include "modtrees.h" +#include "errors.h" +#include "columns.h" +#include "strsave.h" + +#line 112 "modules.nw" +static struct modpart * +newmodpart(int type, char *s, Location *loc); /* create a new module part */ + +static +void append(Module mp, struct modpart *p); +#line 229 "modules.nw" +static int seekcycle(Module mp, Parent parent); +#line 134 "modules.nw" +static char *lastfilename = 0; +static int lastlineno = -1; +#line 41 "modules.nw" +Module newmodule (char *modname) { + Module p = (Module) malloc (sizeof (struct module)); + checkptr(p); + p->name = strsave(modname); + p->usecount = 0; + p->head = p->tail = NULL; + return p; +} +#line 74 "modules.nw" +void add_part (Module mp, char *s, Parttype type, Location *loc) { + struct modpart *p = newmodpart(type,s,loc); + append (mp,p); +} +#line 79 "modules.nw" +static struct modpart * +newmodpart(int type, char *s, Location *loc) { + struct modpart *p = (struct modpart *) malloc (sizeof (struct modpart)); + checkptr(p); + p->ptype = type; + if (s) { + p->contents = strsave(s); + +#line 106 "modules.nw" +{ int k = strlen(p->contents)-1; + if (p->contents[k] == '\n') p->contents[k] = '\0'; + else impossible("input line doesn't end with newline"); +} +#line 87 "modules.nw" + } + if (loc) p->loc = *loc; + p->next = NULL; + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return p; +} +#line 95 "modules.nw" +static +void append(Module mp, struct modpart *p) { + /* append p to mp's list of modparts */ + if (mp->head == NULL) { + mp->head = mp->tail = p; + } else { + mp->tail->next = p; + mp->tail = p; + } +} +#line 139 "modules.nw" +void resetloc(void) { + lastfilename = 0; + lastlineno = -1; +} +#line 147 "modules.nw" +int expand (Module mp, int indent, int partial_distance, Parent parent, + char *locformat, FILE *out) { + struct modpart *p; + Module newmod; + int error=Normal; + struct parent thismodule; /* the value only matters when we're expanding a module */ + + +#line 221 "modules.nw" +thismodule.this = mp; +thismodule.parent = parent; +#line 155 "modules.nw" + +#line 224 "modules.nw" +if (seekcycle(mp, parent)) { + errormsg(Error, "<<%s>>", mp->name); + return Error; +} + +#line 157 "modules.nw" + for (p=mp->head; p!=NULL; p=p->next) { + switch (p->ptype) { + case STRING: +#line 178 "modules.nw" +if (*(p->contents) != '\0') { + if (*locformat) { + if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head)) + indent_for(partial_distance, out); + } else if (partial_distance == 0) { + indent_for(indent, out); + partial_distance = indent; + } + fprintf(out,"%s",p->contents); + partial_distance = limitcolumn(p->contents, partial_distance); +} +#line 159 "modules.nw" + ; break; + case MODULE: +#line 203 "modules.nw" +newmod = lookup(p->contents); +if (newmod==NULL) { + errormsg (Error, "undefined chunk name: <<%s>>", p->contents); + error=Error; +} else { + int retcode; + if (*locformat == 0 && partial_distance == 0) { + indent_for(indent, out); + partial_distance = indent; + } + retcode = expand (newmod, partial_distance, partial_distance, + &thismodule, locformat, out); + if (retcode > error) error = retcode; +} +partial_distance = limitcolumn(p->contents, partial_distance + 2) + 2; + /* account for surrounding brackets */ +#line 160 "modules.nw" + ; break; + case NEWLINE: +#line 190 "modules.nw" +partial_distance = 0; +putc('\n', out); +lastlineno++; +#line 161 "modules.nw" + ; break; + default: impossible("bad part type"); + } + } + return error; +} +#line 231 "modules.nw" +static int seekcycle(Module mp, Parent parent) { + if (parent == NULL) { + return 0; + } else if (parent->this == mp || seekcycle(mp, parent->parent)) { + if (parent->this == mp) + fprintf(stderr, "Cyclic code chunks: "); + fprintf(stderr, "<<%s>> -> ", parent->this->name); + return 1; + } else { + return 0; + } +} +#line 253 "modules.nw" +int printloc(FILE *fp, char *fmt, Location loc, int partial) { + char *p; + if (*fmt + && (loc.filename!=lastfilename || lastlineno != loc.lineno)) { + if (partial) putc('\n',fp); + +#line 265 "modules.nw" +for (p = fmt; *p; p++) { + if (*p == '%') { + switch (*++p) { + case '%': putc('%', fp); break; + case 'N': putc('\n', fp); break; + case 'F': fprintf(fp, "%s", loc.filename); break; + case 'L': fprintf(fp, "%d", loc.lineno); break; + case '-': case '+': + if (isdigit(p[1]) && p[2] == 'L') { + fprintf(fp, "%d", *p == '+' ? loc.lineno + (p[1] - '0') + : loc.lineno - (p[1] - '0')); + p += 2; + } else + +#line 285 "modules.nw" +{ static int complained = 0; + if (!complained) { + errormsg(Error,"Bad format sequence ``%%%c'' in -L%s",*p,fmt); + complained = 1; + } +} +#line 279 "modules.nw" + break; + default: +#line 285 "modules.nw" +{ static int complained = 0; + if (!complained) { + errormsg(Error,"Bad format sequence ``%%%c'' in -L%s",*p,fmt); + complained = 1; + } +} +#line 280 "modules.nw" + break; + } + } else putc(*p, fp); +} +#line 259 "modules.nw" + lastfilename = loc.filename; + lastlineno = loc.lineno; + return 1; + } else return 0; +} +#line 311 "modules.nw" +void remove_final_newline (Module mp) { + /* remove trailing newline that must be in module */ + if (mp->tail==NULL) /* module has no text */ + return; + if (mp->tail->ptype != NEWLINE) + /* do nothing --- this is possible with nuweb front end + formerly: impossible("Module doesn't end with newline"); */ + ; + else if (mp->tail == mp->head) + mp->head = mp->tail = NULL; + else { + struct modpart *p = mp->head; + while (p->next != mp->tail) p = p->next; + p->next = NULL; + } +} diff --git a/web/noweb/src/c/modules.h b/web/noweb/src/c/modules.h new file mode 100644 index 0000000000..f6c8b50956 --- /dev/null +++ b/web/noweb/src/c/modules.h @@ -0,0 +1,37 @@ +typedef struct location { /* identify lines of source */ + char *filename; + int lineno; +} Location; + +typedef enum parttype {STRING=1, MODULE, NEWLINE} Parttype; + +struct modpart { + Parttype ptype; /* type of fragment: STRING, MODULE, NEWLINE */ + char *contents; + Location loc; /* for String, where's it from ? */ + struct modpart *next; +}; +typedef struct module { + char *name; + int usecount; + struct modpart *head, *tail; +} *Module; +Module newmodule(char *modname); /* create a new, blank module */ +#define addstring(MP,S,L) add_part(MP,S,STRING,&L) + /* add a string to a module definition (stripping final newline) */ +#define addmodule(MP,S) add_part(MP,S,MODULE,0) + /* add a module reference to a module definition (stripping final newline) */ +#define addnewline(MP) add_part(MP,0,NEWLINE,0) +void add_part (Module mp, char *s, Parttype type, Location *loc); +typedef struct parent { + Module this; + struct parent *parent; +} *Parent; + +int expand (Module mp, int indent, int partial_distance, Parent parent, + char *locformat, FILE *out); + /* expand a module, writing to file out */ +void resetloc(void); +int printloc(FILE *fp, char *fmt, Location loc, int partial); +void remove_final_newline (Module mp); + /* remove trailing newline that must be in module */ diff --git a/web/noweb/src/c/modules.nw b/web/noweb/src/c/modules.nw new file mode 100644 index 0000000000..a3c5f0069c --- /dev/null +++ b/web/noweb/src/c/modules.nw @@ -0,0 +1,326 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +@ +\subsection{The internal structure of modules (chunks)} +This code is so old, it dates back to when I used to call ``chunks'' ``modules.'' +@ +\subsubsection{Assembling modules from parts} +Modules are linked lists. +Each element of the list is either a string or a reference to another +module name. +During expansion of a module, strings are printed and module references +are expanded. +The structure of module references must be a dag or else [[expand]] will +complain about a cycle. +<<header>>= +typedef struct location { /* identify lines of source */ + char *filename; + int lineno; +} Location; + +typedef enum parttype {STRING=1, MODULE, NEWLINE} Parttype; + +struct modpart { + Parttype ptype; /* type of fragment: STRING, MODULE, NEWLINE */ + char *contents; + Location loc; /* for String, where's it from ? */ + struct modpart *next; +}; +@ +In order to facilitate expansion of all roots (and to identify roots), +I keep a use counter in each module. +<<header>>= +typedef struct module { + char *name; + int usecount; + struct modpart *head, *tail; +} *Module; +<<header>>= +Module newmodule(char *modname); /* create a new, blank module */ +<<C functions>>= +Module newmodule (char *modname) { + Module p = (Module) malloc (sizeof (struct module)); + checkptr(p); + p->name = strsave(modname); + p->usecount = 0; + p->head = p->tail = NULL; + return p; +} +<<*>>= +static char rcsid[] = "$Id: modules.nw,v 2.26 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "modules.h" +#include "modtrees.h" +#include "errors.h" +#include "columns.h" +#include "strsave.h" + +<<Function declarations>> +<<local data>> +<<C functions>> +@ +<<header>>= +#define addstring(MP,S,L) add_part(MP,S,STRING,&L) + /* add a string to a module definition (stripping final newline) */ +#define addmodule(MP,S) add_part(MP,S,MODULE,0) + /* add a module reference to a module definition (stripping final newline) */ +#define addnewline(MP) add_part(MP,0,NEWLINE,0) +void add_part (Module mp, char *s, Parttype type, Location *loc); +<<C functions>>= +void add_part (Module mp, char *s, Parttype type, Location *loc) { + struct modpart *p = newmodpart(type,s,loc); + append (mp,p); +} +<<C functions>>= +static struct modpart * +newmodpart(int type, char *s, Location *loc) { + struct modpart *p = (struct modpart *) malloc (sizeof (struct modpart)); + checkptr(p); + p->ptype = type; + if (s) { + p->contents = strsave(s); + <<strip final newline from p->contents>> + } + if (loc) p->loc = *loc; + p->next = NULL; + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return p; +} +<<C functions>>= +static +void append(Module mp, struct modpart *p) { + /* append p to mp's list of modparts */ + if (mp->head == NULL) { + mp->head = mp->tail = p; + } else { + mp->tail->next = p; + mp->tail = p; + } +} +<<strip final newline from p->contents>>= +{ int k = strlen(p->contents)-1; + if (p->contents[k] == '\n') p->contents[k] = '\0'; + else impossible("input line doesn't end with newline"); +} +@ +<<Function declarations>>= +static struct modpart * +newmodpart(int type, char *s, Location *loc); /* create a new module part */ + +static +void append(Module mp, struct modpart *p); +@ +\subsubsection{Expanding modules} +[[expand]] returns 0 if all is well, nonzero if error. +A [[struct parent]] chain lists modules currently being expanded, +so I can detect cycles. +[[partial_distance]] is the size of the line already written out when +[[expand]] is called. +<<header>>= +typedef struct parent { + Module this; + struct parent *parent; +} *Parent; + +int expand (Module mp, int indent, int partial_distance, Parent parent, + char *locformat, FILE *out); + /* expand a module, writing to file out */ +<<local data>>= +static char *lastfilename = 0; +static int lastlineno = -1; +<<header>>= +void resetloc(void); +<<C functions>>= +void resetloc(void) { + lastfilename = 0; + lastlineno = -1; +} +@ +[[indent]] is the amount by which this chunk should be indented. +[[partial_distance]] is the width of what has already been written to the current line. +<<C functions>>= +int expand (Module mp, int indent, int partial_distance, Parent parent, + char *locformat, FILE *out) { + struct modpart *p; + Module newmod; + int error=Normal; + struct parent thismodule; /* the value only matters when we're expanding a module */ + + <<set [[thismodule = {mp, parent}]]>> + <<if [[mp]] is an ancestor, complain and return>> + + for (p=mp->head; p!=NULL; p=p->next) { + switch (p->ptype) { + case STRING: <<print a string>>; break; + case MODULE: <<expand a module>>; break; + case NEWLINE: <<print a newline>>; break; + default: impossible("bad part type"); + } + } + return error; +} +@ +If the previous line was not partial, this string starts a fresh line +and it is necessary to indent. +When emitting [[#line]] directives, don't indent---instead try to +preserve the original column of the information in the source, as +well as the line number. +This means indenting by [[partial_distance]] if a [[#line]] directive +is emitted, {\em except} when it's the first line of a module (hack! hack!). +[[printloc]] returns nonzero when [[#line]] is actually emitted. + +<<print a string>>= +if (*(p->contents) != '\0') { + if (*locformat) { + if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head)) + indent_for(partial_distance, out); + } else if (partial_distance == 0) { + indent_for(indent, out); + partial_distance = indent; + } + fprintf(out,"%s",p->contents); + partial_distance = limitcolumn(p->contents, partial_distance); +} +<<print a newline>>= +partial_distance = 0; +putc('\n', out); +lastlineno++; +@ +Previously, when we expanded a module, the partial distance became the +new indentation level. This was clearly wrong when we were at the +beginning of the line, so I just hacked a special case for no +[[locformat]] and zero [[partial_distance]]. It's essentially what +would be done above for a string of length zero if that weren't hacked +out as a special case. +This change probably blows the case where the module being expanded is +empty. +<<expand a module>>= +newmod = lookup(p->contents); +if (newmod==NULL) { + errormsg (Error, "undefined chunk name: @<<%s@>>", p->contents); + error=Error; +} else { + int retcode; + if (*locformat == 0 && partial_distance == 0) { + indent_for(indent, out); + partial_distance = indent; + } + retcode = expand (newmod, partial_distance, partial_distance, + &thismodule, locformat, out); + if (retcode > error) error = retcode; +} +partial_distance = limitcolumn(p->contents, partial_distance + 2) + 2; + /* account for surrounding brackets */ +@ +<<set [[thismodule = {mp, parent}]]>>= +thismodule.this = mp; +thismodule.parent = parent; +<<if [[mp]] is an ancestor, complain and return>>= +if (seekcycle(mp, parent)) { + errormsg(Error, "@<<%s@>>", mp->name); + return Error; +} +<<Function declarations>>= +static int seekcycle(Module mp, Parent parent); +<<C functions>>= +static int seekcycle(Module mp, Parent parent) { + if (parent == NULL) { + return 0; + } else if (parent->this == mp || seekcycle(mp, parent->parent)) { + if (parent->this == mp) + fprintf(stderr, "Cyclic code chunks: "); + fprintf(stderr, "@<<%s@>> -> ", parent->this->name); + return 1; + } else { + return 0; + } +} +@ + +Printing locations means emitting [[#line]] numbers. +[[printloc]] emits a [[#line]] only if the line emitted is out of sequence. +It returns nonzero when [[#line]] is emitted; zero otherwise. +The flag [[partial]] tells whether the preceding line was partial. +If so, then a newline is necessary before [[#line]] can be emitted. +<<header>>= +int printloc(FILE *fp, char *fmt, Location loc, int partial); +<<C functions>>= +int printloc(FILE *fp, char *fmt, Location loc, int partial) { + char *p; + if (*fmt + && (loc.filename!=lastfilename || lastlineno != loc.lineno)) { + if (partial) putc('\n',fp); + <<write out the line number pragma>> + lastfilename = loc.filename; + lastlineno = loc.lineno; + return 1; + } else return 0; +} +<<write out the line number pragma>>= +for (p = fmt; *p; p++) { + if (*p == '%') { + switch (*++p) { + case '%': putc('%', fp); break; + case 'N': putc('\n', fp); break; + case 'F': fprintf(fp, "%s", loc.filename); break; + case 'L': fprintf(fp, "%d", loc.lineno); break; + case '-': case '+': + if (isdigit(p[1]) && p[2] == 'L') { + fprintf(fp, "%d", *p == '+' ? loc.lineno + (p[1] - '0') + : loc.lineno - (p[1] - '0')); + p += 2; + } else + <<complain about bad format character>> + break; + default: <<complain about bad format character>> break; + } + } else putc(*p, fp); +} +<<complain about bad format character>>= +{ static int complained = 0; + if (!complained) { + errormsg(Error,"Bad format sequence ``%%%c'' in -L%s",*p,fmt); + complained = 1; + } +} +@ + +\subsubsection{Other operations on completed modules} +For more elegant-looking output, we provide a primitive that removes +the final newline from a module. + +We will use [[apply_each_module]] to remove trailing newlines +from each module. +This serves two purposes: first, our output looks nicer, and, second, +it makes it possible to define a module that doesn't end with newline. + +As they are read in, all modules end with newline, by definition.% +\footnote{Except of course if this input came from an uncorrected nuweb file, +in which case modules needn't end with newlines. Eventually one hopes +I'll fix numarkup to insert exactly the newlines that would be removed here.} +We remove all of the trailing newlines. +<<header>>= +void remove_final_newline (Module mp); + /* remove trailing newline that must be in module */ +<<C functions>>= +void remove_final_newline (Module mp) { + /* remove trailing newline that must be in module */ + if (mp->tail==NULL) /* module has no text */ + return; + if (mp->tail->ptype != NEWLINE) + /* do nothing --- this is possible with nuweb front end + formerly: impossible("Module doesn't end with newline"); */ + ; + else if (mp->tail == mp->head) + mp->head = mp->tail = NULL; + else { + struct modpart *p = mp->head; + while (p->next != mp->tail) p = p->next; + p->next = NULL; + } +} diff --git a/web/noweb/src/c/notangle.c b/web/noweb/src/c/notangle.c new file mode 100644 index 0000000000..ac5c3599ab --- /dev/null +++ b/web/noweb/src/c/notangle.c @@ -0,0 +1,189 @@ +#line 18 "notangle.nw" +static char rcsid[] = "$Id: notangle.nw,v 2.25 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#define MAX_MODNAME 255 +#line 23 "notangle.nw" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "strsave.h" +#include "getline.h" +#include "modules.h" +#include "modtrees.h" +#include "errors.h" +#include "match.h" +#include "notangle.h" + +#line 163 "notangle.nw" +static void warn_dots(char *modname); /* warn about names ending in ... */ + +#line 182 "notangle.nw" +void insist(char *line, char *keyword, char *msg); + +#line 37 "notangle.nw" +void emit_module_named (FILE *out, char *rootname, char *locformat) { + Module root = NULL; /* ptr to root module */ + + root = lookup(rootname); + +#line 167 "notangle.nw" +if (root==NULL) { + errormsg(Fatal, "The root module <<%s>> was not defined.", rootname); + return; +} +#line 42 "notangle.nw" + (void) expand(root,0,0,0,locformat,out); + putc('\n',out); /* make output end with newline */ + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ +} +#line 58 "notangle.nw" +void read_defs(FILE *in) { + char modname[MAX_MODNAME+1] = ""; /* name of module currently being read, + [[""]] if no module is being read */ + Module modptr = NULL; /* ptr to current module, or NULL */ + char *line = NULL; /* buffer for input */ + Location loc; + + while ((line = getline_nw(in)) != NULL) { + if (is_keyword(line, "fatal")) exit(1); + +#line 103 "notangle.nw" +if (is_keyword(line, "nl") || is_index(line, "nl")) { + loc.lineno++; +#line 115 "notangle.nw" +} else if (is_keyword(line,"file")) { + +#line 128 "notangle.nw" +{ char temp[MAX_MODNAME+1]; + if (strlen(line) >= MAX_MODNAME + strlen("@file ")) + overflow("file name size"); + strcpy(temp,line+strlen("@file ")); + temp[strlen(temp)-1]='\0'; + loc.filename = strsave(temp); +} +#line 117 "notangle.nw" + loc.lineno = 1; +} else if (is_keyword(line, "line")) { + +#line 136 "notangle.nw" +{ char temp[MAX_MODNAME+1]; + if (strlen(line) >= MAX_MODNAME + strlen("@line ")) + overflow("file name size"); + strcpy(temp,line+strlen("@line ")); + temp[strlen(temp)-1]='\0'; + +#line 145 "notangle.nw" +{ char *p; + for (p = temp; *p; p++) + if (!isdigit(*p)) + errormsg(Error, "non-numeric line number in `@line %s'", temp); +} +#line 142 "notangle.nw" + loc.lineno = atoi(temp); +} +#line 120 "notangle.nw" + loc.lineno--; +#line 106 "notangle.nw" +} +if (!is_begin(line, "code")) + continue; +#line 68 "notangle.nw" + +#line 99 "notangle.nw" +do { line = getline_nw(in); +} while (line != NULL && !is_keyword(line,"defn") && !is_keyword(line,"text")); +#line 69 "notangle.nw" + insist(line,"defn","code chunk had no definition line"); + +#line 125 "notangle.nw" +strcpy(modname,line+strlen("@defn ")); +modname[strlen(modname)-1]='\0'; +#line 71 "notangle.nw" + warn_dots(modname); /* names ending in ... aren't like web */ + modptr = insert(modname); /* find or add module in table */ + + line = getline_nw(in); + insist(line,"nl","definition line not followed by newline"); + loc.lineno++; + line = getline_nw(in); + while (line != NULL && !is_end(line,"code")) { + if (is_keyword(line,"nl")) { + addnewline(modptr); + loc.lineno++; + } else if (is_keyword(line,"text")) { + addstring(modptr,line+1+4+1,loc); + } else if (is_keyword(line,"use")) { + warn_dots(line+1+3+5); + addmodule(modptr,line+1+3+1); + } else if (is_index(line, "nl")) { + loc.lineno++; + +#line 115 "notangle.nw" +} else if (is_keyword(line,"file")) { + +#line 128 "notangle.nw" +{ char temp[MAX_MODNAME+1]; + if (strlen(line) >= MAX_MODNAME + strlen("@file ")) + overflow("file name size"); + strcpy(temp,line+strlen("@file ")); + temp[strlen(temp)-1]='\0'; + loc.filename = strsave(temp); +} +#line 117 "notangle.nw" + loc.lineno = 1; +} else if (is_keyword(line, "line")) { + +#line 136 "notangle.nw" +{ char temp[MAX_MODNAME+1]; + if (strlen(line) >= MAX_MODNAME + strlen("@line ")) + overflow("file name size"); + strcpy(temp,line+strlen("@line ")); + temp[strlen(temp)-1]='\0'; + +#line 145 "notangle.nw" +{ char *p; + for (p = temp; *p; p++) + if (!isdigit(*p)) + errormsg(Error, "non-numeric line number in `@line %s'", temp); +} +#line 142 "notangle.nw" + loc.lineno = atoi(temp); +} +#line 120 "notangle.nw" + loc.lineno--; +#line 90 "notangle.nw" + } else if (!is_keyword(line, "index")) + +#line 184 "notangle.nw" +errorat(loc.filename, loc.lineno, Error, "botched code chunk `%s'", line); +#line 92 "notangle.nw" + line = getline_nw(in); + } + +#line 178 "notangle.nw" +if (line==NULL) { + impossible("End of file occurred in mid-module"); +} +#line 95 "notangle.nw" + } +} +#line 156 "notangle.nw" +static +void warn_dots(char *modname) { + if (!strcmp(modname+strlen(modname)-3,"...")) + errormsg(Warning, "Module name <<%s>> isn't completed as in web", + modname); +} +#line 172 "notangle.nw" +void insist(char *line, char *keyword, char *msg) { + +#line 178 "notangle.nw" +if (line==NULL) { + impossible("End of file occurred in mid-module"); +} +#line 174 "notangle.nw" + if (!is_keyword(line,keyword)) + impossible(msg); +} diff --git a/web/noweb/src/c/notangle.h b/web/noweb/src/c/notangle.h new file mode 100644 index 0000000000..359c15150a --- /dev/null +++ b/web/noweb/src/c/notangle.h @@ -0,0 +1,2 @@ +void emit_module_named (FILE *out, char *rootname, char *locformat); +void read_defs(FILE *in); /* read module definitions */ diff --git a/web/noweb/src/c/notangle.nw b/web/noweb/src/c/notangle.nw new file mode 100644 index 0000000000..c40c3f3684 --- /dev/null +++ b/web/noweb/src/c/notangle.nw @@ -0,0 +1,184 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +@ +\subsection{Tangling a single file} +A cheap imitation of tangle. +The idea is a very cheap implementation of tangle. +The markup of the file is described in file markup.ow. +@ +The structure of the program is as follows: first we accumulate all of +the module definitions, then we write out the definition of the +root module (normally [["*"]]). +The module definition information will be stored statically +in a table supplied by [[modtrees.h]]; we'll cover the +details later. +<<header>>= +void emit_module_named (FILE *out, char *rootname, char *locformat); +<<*>>= +static char rcsid[] = "$Id: notangle.nw,v 2.25 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#define MAX_MODNAME 255 +@ +<<*>>= +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "strsave.h" +#include "getline.h" +#include "modules.h" +#include "modtrees.h" +#include "errors.h" +#include "match.h" +#include "notangle.h" + +<<Function declarations>> + +void emit_module_named (FILE *out, char *rootname, char *locformat) { + Module root = NULL; /* ptr to root module */ + + root = lookup(rootname); + <<quit if we couldn't find the root>> + (void) expand(root,0,0,0,locformat,out); + putc('\n',out); /* make output end with newline */ + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ +} +@ +We loop looking for the start of a code chunk. +When we find one, we get the name of the module in which the code +is to appear. +Then we just keep adding lines to that module until +we see a terminator. +After we see the terminator we start all over again looking for another code +chunk. +<<header>>= +void read_defs(FILE *in); /* read module definitions */ +<<*>>= +void read_defs(FILE *in) { + char modname[MAX_MODNAME+1] = ""; /* name of module currently being read, + [[""]] if no module is being read */ + Module modptr = NULL; /* ptr to current module, or NULL */ + char *line = NULL; /* buffer for input */ + Location loc; + + while ((line = getline_nw(in)) != NULL) { + if (is_keyword(line, "fatal")) exit(1); + <<track line numbers, then [[continue]] unless [[line]] is [[@begin code]]>> + <<repeat [[line = getline_nw(in);]] until EOF, [[@defn]], or [[@text]]>> + insist(line,"defn","code chunk had no definition line"); + <<copy module name into [[modname]]>> + warn_dots(modname); /* names ending in ... aren't like web */ + modptr = insert(modname); /* find or add module in table */ + + line = getline_nw(in); + insist(line,"nl","definition line not followed by newline"); + loc.lineno++; + line = getline_nw(in); + while (line != NULL && !is_end(line,"code")) { + if (is_keyword(line,"nl")) { + addnewline(modptr); + loc.lineno++; + } else if (is_keyword(line,"text")) { + addstring(modptr,line+1+4+1,loc); + } else if (is_keyword(line,"use")) { + warn_dots(line+1+3+5); + addmodule(modptr,line+1+3+1); + } else if (is_index(line, "nl")) { + loc.lineno++; + <<[[} else if (line]] contains [[@file]] or [[@line) {]] adjust [[loc]]>> + } else if (!is_keyword(line, "index")) + <<complain of botched code chunk>> + line = getline_nw(in); + } + <<if [[line==NULL]] die of premature end of file>> + } +} +@ +<<repeat [[line = getline_nw(in);]] until EOF, [[@defn]], or [[@text]]>>= +do { line = getline_nw(in); +} while (line != NULL && !is_keyword(line,"defn") && !is_keyword(line,"text")); +@ +<<track line numbers, then [[continue]] unless [[line]] is [[@begin code]]>>= +if (is_keyword(line, "nl") || is_index(line, "nl")) { + loc.lineno++; +<<[[} else if (line]] contains [[@file]] or [[@line) {]] adjust [[loc]]>> +} +if (!is_begin(line, "code")) + continue; +@ +The only tricky bit with the line numbers is to note that [[@line]] gives +the line number of the {\em following} line, not of the line on which the +[[@line]] appears. That means [[loc.lineno]] must be decremented after it is +set, so that the next newline will increment it to the correct value. +<<[[} else if (line]] contains [[@file]] or [[@line) {]] adjust [[loc]]>>= +} else if (is_keyword(line,"file")) { + <<save name from [[line]] ([[@file ...]]) into [[loc.filename]]>> + loc.lineno = 1; +} else if (is_keyword(line, "line")) { + <<save line number from [[line]] ([[@line ...]]) into [[loc.lineno]]>> + loc.lineno--; +@ +When copying the module name or a file name, +we have to strip the trailing newline. +<<copy module name into [[modname]]>>= +strcpy(modname,line+strlen("@defn ")); +modname[strlen(modname)-1]='\0'; +<<save name from [[line]] ([[@file ...]]) into [[loc.filename]]>>= +{ char temp[MAX_MODNAME+1]; + if (strlen(line) >= MAX_MODNAME + strlen("@file ")) + overflow("file name size"); + strcpy(temp,line+strlen("@file ")); + temp[strlen(temp)-1]='\0'; + loc.filename = strsave(temp); +} +<<save line number from [[line]] ([[@line ...]]) into [[loc.lineno]]>>= +{ char temp[MAX_MODNAME+1]; + if (strlen(line) >= MAX_MODNAME + strlen("@line ")) + overflow("file name size"); + strcpy(temp,line+strlen("@line ")); + temp[strlen(temp)-1]='\0'; + <<fail if string [[temp]] contains a non-digit>> + loc.lineno = atoi(temp); +} +<<fail if string [[temp]] contains a non-digit>>= +{ char *p; + for (p = temp; *p; p++) + if (!isdigit(*p)) + errormsg(Error, "non-numeric line number in `@line %s'", temp); +} +@ +In {\tt WEB}, module names ending in ``...'' may be prefixes +for other names. +We don't do anything like that here, but we do warn the user about +``...'' in case he's got a file converted from {\tt WEB}. +<<*>>= +static +void warn_dots(char *modname) { + if (!strcmp(modname+strlen(modname)-3,"...")) + errormsg(Warning, "Module name @<<%s@>> isn't completed as in web", + modname); +} +<<Function declarations>>= +static void warn_dots(char *modname); /* warn about names ending in ... */ + +@ Error checking is perenially dull. +<<quit if we couldn't find the root>>= +if (root==NULL) { + errormsg(Fatal, "The root module @<<%s@>> was not defined.", rootname); + return; +} +<<*>>= +void insist(char *line, char *keyword, char *msg) { + <<if [[line==NULL]] die of premature end of file>> + if (!is_keyword(line,keyword)) + impossible(msg); +} +<<if [[line==NULL]] die of premature end of file>>= +if (line==NULL) { + impossible("End of file occurred in mid-module"); +} +<<Function declarations>>= +void insist(char *line, char *keyword, char *msg); +<<complain of botched code chunk>>= +errorat(loc.filename, loc.lineno, Error, "botched code chunk `%s'", line); diff --git a/web/noweb/src/c/nwmktemp b/web/noweb/src/c/nwmktemp Binary files differnew file mode 100755 index 0000000000..e179bd8944 --- /dev/null +++ b/web/noweb/src/c/nwmktemp diff --git a/web/noweb/src/c/nwmktemp.c b/web/noweb/src/c/nwmktemp.c new file mode 100644 index 0000000000..31b213065a --- /dev/null +++ b/web/noweb/src/c/nwmktemp.c @@ -0,0 +1,43 @@ +#line 10 "nwmktemp.nw" +#define _POSIX_C_SOURCE 200809L + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +#line 21 "nwmktemp.nw" +static const char *tmpdir(void) { + char *tmpdir = getenv("TMPDIR"); + if (tmpdir && *tmpdir) + return tmpdir; + else + return "/tmp"; +} +#line 29 "nwmktemp.nw" +int main(int argc, char **argv) { + char template[] = "nwtemp.XXXXXXXXXX"; + const char *tmp = tmpdir(); + char *path = malloc(strlen(tmp) + strlen(template) + 3); /* slash, newline, \0 */ + int fd; + + assert(argc == 1); + + assert(path); + strcpy(path, tmp); + path[strlen(tmp)] = '/'; + strcpy(path+strlen(tmp)+1, template); + + fd = mkstemp(path); + if (fd < 0) { + perror(argv[0]); + free(path); + return 1; + } else { + printf("%s\n", path); + free(path); + close(fd); + return 0; + } +} diff --git a/web/noweb/src/c/nwmktemp.nw b/web/noweb/src/c/nwmktemp.nw new file mode 100644 index 0000000000..10c4e20205 --- /dev/null +++ b/web/noweb/src/c/nwmktemp.nw @@ -0,0 +1,54 @@ +% -*- mode: Noweb; noweb-code-mode: c-mode -*- +% Copyright 2018 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. + +\section{Temporary files} + +Noweb's shell scripts need a way to create a temporary file, securely. +This program is a thin wrapper around [[mkstemp]]. +<<*>>= +#define _POSIX_C_SOURCE 200809L + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +@ +Find a temporary directory. +<<*>>= +static const char *tmpdir(void) { + char *tmpdir = getenv("TMPDIR"); + if (tmpdir && *tmpdir) + return tmpdir; + else + return "/tmp"; +} +<<*>>= +int main(int argc, char **argv) { + char template[] = "nwtemp.XXXXXXXXXX"; + const char *tmp = tmpdir(); + char *path = malloc(strlen(tmp) + strlen(template) + 3); /* slash, newline, \0 */ + int fd; + + assert(argc == 1); + + assert(path); + strcpy(path, tmp); + path[strlen(tmp)] = '/'; + strcpy(path+strlen(tmp)+1, template); + + fd = mkstemp(path); + if (fd < 0) { + perror(argv[0]); + free(path); + return 1; + } else { + printf("%s\n", path); + free(path); + close(fd); + return 0; + } +} +@ diff --git a/web/noweb/src/c/pretty.nw b/web/noweb/src/c/pretty.nw new file mode 100644 index 0000000000..0a5ac01f4c --- /dev/null +++ b/web/noweb/src/c/pretty.nw @@ -0,0 +1,259 @@ +\section{Prettyprinting} + +\subsection{API for prettyprinting} + +Here is a suitable API for a prettyprinter for noweb. +Input to be prettyprinted is a sequence of strings, newlines, and +chunks used. A [[STRING]] is always free of newlines. +<<API for prettyprinter>>= +typedef enum parttype { + START_OF_CHUNK=1, END_OF_CHUNK, STRING, USE_CHUNK, NEWLINE, + LITERAL, DEFINITION, WHATSIT +} Parttype; +@ The prettyprinter calls back into the application as follows: +<<API for prettyprinter>>= +typedef void (*PrettyPrinterCallback) (void *closure, Parttype type, char *contents); +@ The [[contents]] must be +\begin{quote} +\begin{tabular}{ll} +[[START_OF_CHUNK]]&The name of the chunk being prettyprinted\\ +[[END_OF_CHUNK]]&[[NULL]]\\ +[[STRING]]&A string with no newlines\\ +[[LITERAL]]${}^*$&A string with no newlines, to get no further formatting\\ +[[USE_CHUNK]]&The name of the chunk referred to\\ +[[NEWLINE]]&[[NULL]]\\ +[[DEFINITION]]${}^*$&An identifier whose definition is in this chunk.\\ +[[WHATSIT]]&An uninterpreted string passed to the callback function +\end{tabular} +\end{quote} +Items marked with a star ($*$) are legal only when the prettyprinter +is calling back into the application. +The identifier given in [[DEFINITION]] isn't printed, so if the +prettyprinter wants to print it, it has to send it twice. +@ +The prettyprinter exports two calls to the application. +<<API for prettyprinter>>= +typedef struct prettyprinter *PrettyPrinter; + +extern PrettyPrinter new_prettyprinter(PrettyPrinterCallback callback, void *closure); +extern void prettyprint(PrettyPrinter pp, Parttype type, char *contents); +@ The prettyprinter accepts calls to the [[prettyprint]] procedure. +Any call to [[prettyprint]] may result in one or more callbacks to the +procedure registered when [[new_prettyprinter]] is called. +For any given [[PrettyPrinter]], the application must call +[[prettyprint]] with [[type==END_OF_CHUNK]] exactly once, and that +call must be the last call. + +<<header>>= +<<API for prettyprinter>> +@ +\subsection{Calling the prettyprinter} + +The assumption here is that we have zero or more prettyprinters +satisfying the interface above---what are the mechanics of getting +something prettyprinted. +We select a prettyprinter based on +the name of the root chunk. + +Because we want to support either a two-pass or one-pass algorithm, +we don't say how lines will come in or go out, but we do say that we +will strip all the trailing newlines. +<<definitions>>= +void pretty (char *getline(void *in), void *in, + void putline(void *out, char *line), void *out, + PrettyPrinter make_pp(void *cl, char *name), void *cl) +{ + char *line = NULL; /* buffer for input */ + PrettyPrinter pp; + Location loc; + + while ((line = getline(in)) != NULL) { + <<adjust [[loc]] if needed>> + <<if not [[@begin code]], copy line and [[continue]]>> + putline(out, line); + + for (line = getline(in); + line && !is_keyword(line,"defn") && !is_keyword(line,"text"); + line = getline(in) + ) putline(out, line); + insist(line,"defn","code chunk had no definition line"); + putline(out, line); + pp = make_pp(cl, line+6); /* use chunk name */ + if (!pp) { <<copy code chunk to [[out]]>>; continue; } + + for (; line && !is_keyword(line, "nl") ;) { + line = getline(in); putline(out, line); + } + insist(line,"nl","definition line not followed by newline"); + loc.lineno++; + for (line = getline(in); + line != NULL && !is_end(line,"code"); + line = getline(in)) + { + <<adjust [[loc]] if needed>> + if (is_keyword(line,"nl")) { + prettyprint(pp, NEWLINE, NULL); + } else if (is_keyword(line,"text")) { + prettyprint(pp, STRING, line+1+4+1); + } else if (is_keyword(line,"use")) { + prettyprint(pp, USE_CHUNK, line+1+3+1); + } else { + prettyprint(pp, WHATSIT, line); + } + } + prettyprint(pp, END_OF_CHUNK, NULL); + <<if [[line==NULL]] die of premature end of file>> + putline(out, line); + } +} +<<if not [[@begin code]], copy line and [[continue]]>>= +if (!is_begin(line, "code")) { + putline(out, line); + continue; +} +<<copy code chunk to [[out]]>>= +while ((line = getline(in)) != NULL) { + putline(out, line); + if (is_end(line, "code")) break; +} +@ +<<adjust [[loc]] if needed>>= +{ if (is_keyword(line, "nl") || is_index(line, "nl")) { + loc.lineno++; + } else if (is_keyword(line,"file")) { + loc.filename = strsave(line + 6); + loc.lineno = 1; + } else if (is_keyword(line, "line")) { + <<save line number from [[line]] ([[@line ...]]) into [[loc.lineno]]>> + loc.lineno--; + } +} +@ +<<save line number from [[line]] ([[@line ...]]) into [[loc.lineno]]>>= +{ char *temp = line+6; + <<fail if string [[temp]] contains a non-digit>> + loc.lineno = atoi(temp); +} +<<fail if string [[temp]] contains a non-digit>>= +{ char *p; + for (p = temp; *p; p++) + if (!isdigit(*p)) + errormsg(Error, "non-numeric line number in `@line %s'", temp); +} +<<definitions>>= +void insist(char *line, char *keyword, char *msg) { + <<if [[line==NULL]] die of premature end of file>> + if (!is_keyword(line,keyword)) + impossible(msg); +} +<<if [[line==NULL]] die of premature end of file>>= +if (line==NULL) { + impossible("End of file occurred in mid-module"); +} +<<declarations>>= +void insist(char *line, char *keyword, char *msg); +@ +\subsection{Simple callback procedure} +We've got a callback to do I/O +<<definitions>>= +typedef struct io { + FILE *out; + void (*putline)(void *out, char *line); +} *IO; + +static void write_pretty(void *closure, Parttype type, char *contents) { + IO io = (IO) closure; + switch(type) { + case START_OF_CHUNK: break; + case END_OF_CHUNK: break; + case STRING: fprintf(io->out, "@text %s\n", contents); break; + case LITERAL: fprintf(io->out, "@literal %s\n", contents); break; + case USE_CHUNK: fprintf(io->out, "@use %s\n", contents); break; + case NEWLINE: io->putline(io->out, "@nl"); break; + case DEFINITION: fprintf(io->out, "@index defn %s\n", contents); break; + case WHATSIT: io->putline(io->out, contents); break; + default: assert(0); + } +} +@ + +\subsection{Sketch for a two-pass driver} +The best plan would be to use a two-pass driver. +On the first pass it would +\begin{itemize} +\item +Read in all lines and builds a def-use tree. +\item +Decide on a prettyprinter for each root, either using the root name +or a suffix (and probably based on info from the command line as well +as defaults). +\item +Propagate info from parents to children. +Inconsistent children (distinct prettyprinters) become new roots. +\end{itemize} +Then on the second pass it could use the propagated info to select a +prettyprinter for each chunk. + +\subsection{One-pass driver} +This just uses a single prettyprinter. +This means get and put are just I/O. +<<one-pass definitions>>= +static char *get_stripped(void *in) { + FILE *fp = (FILE *)in; + char *line = getline_nw(fp); + if (line) { + int i = strlen(line) - 1; + if (i >= 0 && line[i] == '\n') line[i] = 0; + } + return line; +} + +static void put_stripped(void *out, char *line) { + FILE *fp = (FILE *)out; + fputs(line, fp); + fputc('\n', fp); +} +@ +If we only ever use a single prettyprinter, we make the closure the +I/O closure. +As a trick, we return no prettyprinter if the chunk name has a blank. +<<one-pass definitions>>= +static PrettyPrinter make_pp(void *cl, char *name) { + if (strchr(name, ' ')) return NULL; + return new_prettyprinter(write_pretty, cl); +} +@ +The program is always a filter, so it uses stdin and stdout. +<<one-pass definitions>>= +main (int argc, char *argv[]) { + struct io io; + io.out = stdout; + io.putline = put_stripped; + pretty(get_stripped, stdin, put_stripped, stdout, make_pp, &io); + return 0; +} +@ +\subsection{Boilerplate} +<<*>>= +static char rcsid[] = "$Id: pretty.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include "strsave.h" +#include "getline.h" +#include "match.h" +#include "errors.h" +#include "pretty.h" + +typedef struct location { /* identify lines of source */ + char *filename; + int lineno; +} Location; + +<<declarations>> +<<definitions>> +<<one-pass definitions>> + diff --git a/web/noweb/src/c/readme.nw b/web/noweb/src/c/readme.nw new file mode 100644 index 0000000000..970878a664 --- /dev/null +++ b/web/noweb/src/c/readme.nw @@ -0,0 +1,61 @@ +% Copyright 1991-2018 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +% $Id: readme.nw,v 2.19 2008/10/06 01:03:05 nr Exp nr $ + +Guidebook. + +Here's a breakdown of files by function. + +columns.nw Used to help accumulate indentations across module + boundaries by measuring the widths of various strings + in columns. Depends on your notion of tabs. + +errors.nw Functions that issue error messages. + +getline.nw Used to read text a line at a time, no matter how + long lines might be. + +main.nw Process options and so on before calling notangle. + +markup.nw Encapsulates all the definitions of how things are + labeled in the text: what is a module definition, + what terminates it, and so on. + +modtrees.nw Provides a table of modules with insert and lookup + (no delete). Also can apply an arbitrary function + to each module in the table. + +modules.nw Defines the abstraction of a module and the operations + on it. + +notangle.nw Reads a text on one file and writes a program on another. + Works by reading module definitions, then expanding + the module ``*''. + +strsave.nw Return a pointer to a fresh copy of a string. + + +@ History of features. + + 2 Feb 89: improve the visual quality of the output by removing the trailing + newline from every module and by tracking indentation. + + 9 Feb 89: parameterize notangle by the name of the root module, and + make it a command line option. This will help in writing + multiple outputs from a single input. + +21 Mar 89: Make markup front end for noweave and notangle. + This breaks passing of options for notangle. + Tough. + + Much improved cleanliness of implementation. + +?? ??? 90: Track string locations for emitting \#line pragmas + +Summer 91: Version 2: second syntax for tools, added noroots. + + +@ Bugs. +There's no distinction between errors and warnings. + +Also see the man page. diff --git a/web/noweb/src/c/recognize.c b/web/noweb/src/c/recognize.c new file mode 100644 index 0000000000..24469f07d8 --- /dev/null +++ b/web/noweb/src/c/recognize.c @@ -0,0 +1,215 @@ +#line 24 "recognize.nw" +static char rcsid[] = "$Id: recognize.nw,v 1.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +#line 39 "recognize.nw" +#include <string.h> +#include <stdlib.h> +#line 54 "recognize.nw" +typedef struct recognizer *Recognizer; +#line 69 "recognize.nw" +typedef void (*Callback) (void *closure, char *id, char *instance); +#line 52 "recognize.nw" +Recognizer new_recognizer(char *alphanum, char *symbols); +#line 60 "recognize.nw" +void add_ident(Recognizer r, char *id); +void stop_adding(Recognizer r); +#line 63 "recognize.nw" +void search_for_ident(Recognizer r, char *input, Callback f, void *closure); +#line 76 "recognize.nw" +typedef struct goto_node Goto_Node; +typedef struct move_node Move_Node; +#line 79 "recognize.nw" +typedef struct name_node { + struct name_node *next; /* points to the next name on the output list */ + char *name; +} Name_Node; +#line 84 "recognize.nw" +struct move_node { + Move_Node *next; /* points to the next node on the move list */ + Goto_Node *state; /* the next state for this character */ + unsigned char c; +}; +#line 90 "recognize.nw" +struct goto_node { + Name_Node *output; /* list of words ending in this state */ + Move_Node *moves; /* list of possible moves */ + Goto_Node *fail; /* and where to go when no move fits */ + Goto_Node *next; /* next goto node with same depth */ +}; +#line 97 "recognize.nw" +struct recognizer { + Goto_Node *root[256]; /* might want 128, depending on the character set */ + char *alphas; + char *syms; + int max_depth; + Goto_Node **depths; /* an array, max_depth long, of lists of goto_nodes, + created while adding ids, used while building + the failure functions */ +}; +#line 322 "recognize.nw" +int reject_match(Recognizer r, char *id, char *input, char *current); +#line 114 "recognize.nw" +static Goto_Node *goto_lookup(unsigned char c, Goto_Node *g) +{ + Move_Node *m = g->moves; + while (m && m->c != c) + m = m->next; + return m ? m->state : NULL; +} +#line 127 "recognize.nw" +Recognizer new_recognizer(char *alphanum, char *symbols) +{ + Recognizer r = (Recognizer) calloc(1, sizeof(struct recognizer)); + r->alphas = alphanum; + r->syms = symbols; + r->max_depth = 10; + r->depths = (Goto_Node **) calloc(r->max_depth, sizeof(Goto_Node *)); + return r; +} +#line 142 "recognize.nw" +void add_ident(Recognizer r, char *id) +{ + int depth = 2; + char *p = id; + unsigned char c = *p++; + Goto_Node *q = r->root[c]; + if (!q) + +#line 162 "recognize.nw" +{ + q = (Goto_Node *) calloc(1, sizeof(Goto_Node)); + r->root[c] = q; + q->next = r->depths[1]; + r->depths[1] = q; +} +#line 150 "recognize.nw" + c = *p++; + while (c) { + Goto_Node *new = goto_lookup(c, q); + if (!new) + +#line 169 "recognize.nw" +{ + Move_Node *new_move = (Move_Node *) malloc(sizeof(Move_Node)); + new = (Goto_Node *) calloc(1, sizeof(Goto_Node)); + new_move->state = new; + new_move->c = c; + new_move->next = q->moves; + q->moves = new_move; + if (depth == r->max_depth) + +#line 182 "recognize.nw" +{ + int i; + Goto_Node **new_depths = (Goto_Node **) calloc(2*depth, sizeof(Goto_Node *)); + r->max_depth = 2 * depth; + for (i=0; i<depth; i++) + new_depths[i] = r->depths[i]; + free(r->depths); + r->depths = new_depths; +} +#line 178 "recognize.nw" + new->next = r->depths[depth]; + r->depths[depth] = new; +} +#line 155 "recognize.nw" + q = new; + depth++; + c = *p++; + } + +#line 192 "recognize.nw" +if (!q->output) { + char *copy = malloc(strlen(id) + 1); + strcpy(copy, id); + q->output = (Name_Node *) malloc(sizeof(Name_Node)); + q->output->next = NULL; + q->output->name = copy; +} +#line 160 "recognize.nw" +} +#line 208 "recognize.nw" +void stop_adding(Recognizer r) +{ + int depth; + for (depth=1; depth<r->max_depth; depth++) { + Goto_Node *g = r->depths[depth]; + while (g) { + Move_Node *m = g->moves; + while (m) { + unsigned char a = m->c; + Goto_Node *s = m->state; + Goto_Node *state = g->fail; + while (state && !goto_lookup(a, state)) + state = state->fail; + if (state) + s->fail = goto_lookup(a, state); + else + s->fail = r->root[a]; + if (s->fail) { + Name_Node *p = s->fail->output; + while (p) { + Name_Node *q = (Name_Node *) malloc(sizeof(Name_Node)); + q->name = p->name; /* depending on memory deallocation + strategy, we may need to copy this */ + q->next = s->output; + s->output = q; + p = p->next; + } + } + m = m->next; + } + g = g->next; + } + } +} +#line 247 "recognize.nw" +void search_for_ident(Recognizer r, char *input, Callback f, void *closure) +{ + Goto_Node *state = NULL; + char *current = input; + unsigned char c = (unsigned char) *current++; + while (c) { + +#line 263 "recognize.nw" +{ + while (state && !goto_lookup(c, state)) + state = state->fail; + state = state ? goto_lookup(c, state) : r->root[c]; +} +#line 254 "recognize.nw" + +#line 273 "recognize.nw" +{ + if (state) { + Name_Node *p = state->output; + while (p) { + if (!reject_match(r, p->name, input, current)) + f(closure, p->name, current - strlen(p->name)); + p = p->next; + } + } +} +#line 255 "recognize.nw" + c = *current++; + } +} +#line 297 "recognize.nw" +int reject_match(Recognizer r, char *id, char *input, char *current) +{ + int len = strlen(id); + char first = id[0]; + char last = id[len - 1]; + char next = *current; + char prev = '\0'; + current = current - len - 1; + if (input <= current) + prev = *current; + if (prev && strchr(r->alphas, first) && strchr(r->alphas, prev)) return 1; + if (next && strchr(r->alphas, last ) && strchr(r->alphas, next)) return 1; + if (prev && strchr(r->syms, first) && strchr(r->syms, prev)) return 1; + if (next && strchr(r->syms, last ) && strchr(r->syms, next)) return 1; + return 0; +} diff --git a/web/noweb/src/c/recognize.h b/web/noweb/src/c/recognize.h new file mode 100644 index 0000000000..c2196365e7 --- /dev/null +++ b/web/noweb/src/c/recognize.h @@ -0,0 +1,6 @@ +typedef struct recognizer *Recognizer; +typedef void (*Callback) (void *closure, char *id, char *instance); +Recognizer new_recognizer(char *alphanum, char *symbols); +void add_ident(Recognizer r, char *id); +void stop_adding(Recognizer r); +void search_for_ident(Recognizer r, char *input, Callback f, void *closure); diff --git a/web/noweb/src/c/recognize.nw b/web/noweb/src/c/recognize.nw new file mode 100644 index 0000000000..38b6bbb733 --- /dev/null +++ b/web/noweb/src/c/recognize.nw @@ -0,0 +1,322 @@ +% \documentstyle[noweb]{article} +% +% \setlength{\oddsidemargin}{0in} +% \setlength{\evensidemargin}{0in} +% \setlength{\topmargin}{0in} +% \addtolength{\topmargin}{-\headheight} +% \addtolength{\topmargin}{-\headsep} +% \setlength{\textheight}{8.9in} +% \setlength{\textwidth}{6.5in} +% \setlength{\marginparwidth}{0.5in} + +\subsection{An Efficient String Matcher (by Preston Briggs)} +\label{preston} + +\subsubsection{Introduction} + +The obvious approach to this problem would be quite expensive for +large documents; however, there is an interesting paper describing an +efficient solution~\cite{aho:efficient}. + +\paragraph{Boilerplate} \indent\null\par + +<<*>>= +static char rcsid[] = "$Id: recognize.nw,v 1.24 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +static struct keepalive { char *s; struct keepalive *p; } keepalive[] = + { {rcsid, keepalive}, {rcsname, keepalive} }; /* avoid warnings */ +<<Include files>> +<<header>> +<<Type definitions>> +<<Prototypes>> +<<Function definitions>> +@ +<<header>>= +<<Exported type definitions>> +<<Exported prototypes>> +@ +<<Include files>>= +#include <string.h> +#include <stdlib.h> +@ + +\paragraph{External Interface} + +The externally visible interface was designed by Norman Ramsey. + +We assume that [[alphanum]] and [[symbols]] point to constant +strings; {\sl i.e.,} we don't bother to copy them into separately +allocated space. + +<<Exported prototypes>>= +Recognizer new_recognizer(char *alphanum, char *symbols); +<<Exported type definitions>>= +typedef struct recognizer *Recognizer; +@ +A copy is made of the string pointed to by [[id]]. +It won't hurt to add the same identifier multiple times to a given +recognizer. +<<Exported prototypes>>= +void add_ident(Recognizer r, char *id); +void stop_adding(Recognizer r); +<<Exported prototypes>>= +void search_for_ident(Recognizer r, char *input, Callback f, void *closure); +@ + +[[instance]] is a pointer to the place within [[input]] that we +saw the identifier. +<<Exported type definitions>>= +typedef void (*Callback) (void *closure, char *id, char *instance); +@ + +\subsubsection{Defining the Automata} + + +<<Type definitions>>= +typedef struct goto_node Goto_Node; +typedef struct move_node Move_Node; +<<Type definitions>>= +typedef struct name_node { + struct name_node *next; /* points to the next name on the output list */ + char *name; +} Name_Node; +<<Type definitions>>= +struct move_node { + Move_Node *next; /* points to the next node on the move list */ + Goto_Node *state; /* the next state for this character */ + unsigned char c; +}; +<<Type definitions>>= +struct goto_node { + Name_Node *output; /* list of words ending in this state */ + Move_Node *moves; /* list of possible moves */ + Goto_Node *fail; /* and where to go when no move fits */ + Goto_Node *next; /* next goto node with same depth */ +}; +<<Type definitions>>= +struct recognizer { + Goto_Node *root[256]; /* might want 128, depending on the character set */ + char *alphas; + char *syms; + int max_depth; + Goto_Node **depths; /* an array, max_depth long, of lists of goto_nodes, + created while adding ids, used while building + the failure functions */ +}; +@ + +\paragraph{A Utility Function} + +We need a function that, given the current state and a character, will +return the next state as directed by the ``goto table.'' If there is +no defined entry in the table, the function returns [[NULL]]. +<<Function definitions>>= +static Goto_Node *goto_lookup(unsigned char c, Goto_Node *g) +{ + Move_Node *m = g->moves; + while (m && m->c != c) + m = m->next; + return m ? m->state : NULL; +} +@ + +\subsubsection{Building the Automata} + +The [[max_depth]] should be initialized to be at least 2. +<<Function definitions>>= +Recognizer new_recognizer(char *alphanum, char *symbols) +{ + Recognizer r = (Recognizer) calloc(1, sizeof(struct recognizer)); + r->alphas = alphanum; + r->syms = symbols; + r->max_depth = 10; + r->depths = (Goto_Node **) calloc(r->max_depth, sizeof(Goto_Node *)); + return r; +} +@ + +\paragraph{Building the Goto Table} + +We assume [[id]] is at least 1 character long. +<<Function definitions>>= +void add_ident(Recognizer r, char *id) +{ + int depth = 2; + char *p = id; + unsigned char c = *p++; + Goto_Node *q = r->root[c]; + if (!q) + <<Create an entry for [[root[c]]]>> + c = *p++; + while (c) { + Goto_Node *new = goto_lookup(c, q); + if (!new) + <<Create a new goto entry and attach to [[q]]'s move list>> + q = new; + depth++; + c = *p++; + } + <<Set [[q->output]] to [[id]] (if not already present)>> +} +<<Create an entry for [[root[c]]]>>= +{ + q = (Goto_Node *) calloc(1, sizeof(Goto_Node)); + r->root[c] = q; + q->next = r->depths[1]; + r->depths[1] = q; +} +<<Create a new goto entry and attach to [[q]]'s move list>>= +{ + Move_Node *new_move = (Move_Node *) malloc(sizeof(Move_Node)); + new = (Goto_Node *) calloc(1, sizeof(Goto_Node)); + new_move->state = new; + new_move->c = c; + new_move->next = q->moves; + q->moves = new_move; + if (depth == r->max_depth) + <<Double the size of the [[depths]] array>> + new->next = r->depths[depth]; + r->depths[depth] = new; +} +<<Double the size of the [[depths]] array>>= +{ + int i; + Goto_Node **new_depths = (Goto_Node **) calloc(2*depth, sizeof(Goto_Node *)); + r->max_depth = 2 * depth; + for (i=0; i<depth; i++) + new_depths[i] = r->depths[i]; + free(r->depths); + r->depths = new_depths; +} +<<Set [[q->output]] to [[id]] (if not already present)>>= +if (!q->output) { + char *copy = malloc(strlen(id) + 1); + strcpy(copy, id); + q->output = (Name_Node *) malloc(sizeof(Name_Node)); + q->output->next = NULL; + q->output->name = copy; +} +@ + +%\newpage +\paragraph{Building the Failure Functions} + +After all the strings have been added to the goto table, we can +construct the failure functions. It's going to be hard to explain +this one. +<<Function definitions>>= +void stop_adding(Recognizer r) +{ + int depth; + for (depth=1; depth<r->max_depth; depth++) { + Goto_Node *g = r->depths[depth]; + while (g) { + Move_Node *m = g->moves; + while (m) { + unsigned char a = m->c; + Goto_Node *s = m->state; + Goto_Node *state = g->fail; + while (state && !goto_lookup(a, state)) + state = state->fail; + if (state) + s->fail = goto_lookup(a, state); + else + s->fail = r->root[a]; + if (s->fail) { + Name_Node *p = s->fail->output; + while (p) { + Name_Node *q = (Name_Node *) malloc(sizeof(Name_Node)); + q->name = p->name; /* depending on memory deallocation + strategy, we may need to copy this */ + q->next = s->output; + s->output = q; + p = p->next; + } + } + m = m->next; + } + g = g->next; + } + } +} +@ + +\subsubsection{Using the Automata} + +<<Function definitions>>= +void search_for_ident(Recognizer r, char *input, Callback f, void *closure) +{ + Goto_Node *state = NULL; + char *current = input; + unsigned char c = (unsigned char) *current++; + while (c) { + <<Goto the next state>> + <<Perform the callback for any outputs>> + c = *current++; + } +} +@ +This is all complicated by my use of [[NULL]] to indicate the +initial state. However, we get a nice speedup by using the [[root]] +array instead of walking down the move list for every character. +<<Goto the next state>>= +{ + while (state && !goto_lookup(c, state)) + state = state->fail; + state = state ? goto_lookup(c, state) : r->root[c]; +} +@ + +We walk down the output list, calling [[f]] with each name that is +not rejected (see the next section). +<<Perform the callback for any outputs>>= +{ + if (state) { + Name_Node *p = state->output; + while (p) { + if (!reject_match(r, p->name, input, current)) + f(closure, p->name, current - strlen(p->name)); + p = p->next; + } + } +} +@ + +% \newpage +\paragraph{Rejecting Matches} + +A problem with simple substring matching is that the string ``he'' +would match longer strings like ``she'' and ``her.'' Norman Ramsey +suggested examining the characters occuring immediately before and +after a match and rejecting the match if it appears to be part of a +longer token. Of course, the concept of {\sl token\/} is +language-dependent, so we may be occasionally mistaken. +For the present, we'll consider the mechanism an experiment. + +<<Function definitions>>= +int reject_match(Recognizer r, char *id, char *input, char *current) +{ + int len = strlen(id); + char first = id[0]; + char last = id[len - 1]; + char next = *current; + char prev = '\0'; + current = current - len - 1; + if (input <= current) + prev = *current; + if (prev && strchr(r->alphas, first) && strchr(r->alphas, prev)) return 1; + if (next && strchr(r->alphas, last ) && strchr(r->alphas, next)) return 1; + if (prev && strchr(r->syms, first) && strchr(r->syms, prev)) return 1; + if (next && strchr(r->syms, last ) && strchr(r->syms, next)) return 1; + return 0; +} +@ Note we never reject a zero [[prev]] or [[next]], since some +implementations of [[strchr]] always return true when searching for a +zero character. +@ + +We need a prototype for [[reject_match]], since it's referenced +before its definition. + +<<Prototypes>>= +int reject_match(Recognizer r, char *id, char *input, char *current); diff --git a/web/noweb/src/c/strsave.c b/web/noweb/src/c/strsave.c new file mode 100644 index 0000000000..d27b5b60e1 --- /dev/null +++ b/web/noweb/src/c/strsave.c @@ -0,0 +1,17 @@ +#line 6 "strsave.nw" +static char rcsid[] = "$Id: strsave.nw,v 2.21 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "strsave.h" +#include "errors.h" + +char *strsave (char *s) { + char *t = malloc (strlen(s)+1); + checkptr(t); + strcpy(t,s); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return t; +} diff --git a/web/noweb/src/c/strsave.h b/web/noweb/src/c/strsave.h new file mode 100644 index 0000000000..e389136964 --- /dev/null +++ b/web/noweb/src/c/strsave.h @@ -0,0 +1 @@ +char *strsave (char *s); /* returns a pointer to a fresh copy of s */ diff --git a/web/noweb/src/c/strsave.nw b/web/noweb/src/c/strsave.nw new file mode 100644 index 0000000000..658e58857c --- /dev/null +++ b/web/noweb/src/c/strsave.nw @@ -0,0 +1,21 @@ +% Copyright 1991 by Norman Ramsey. All rights reserved. +% See file COPYRIGHT for more information. +<<header>>= +char *strsave (char *s); /* returns a pointer to a fresh copy of s */ +<<*>>= +static char rcsid[] = "$Id: strsave.nw,v 2.21 2008/10/06 01:03:05 nr Exp nr $"; +static char rcsname[] = "$Name: v2_12 $"; +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "strsave.h" +#include "errors.h" + +char *strsave (char *s) { + char *t = malloc (strlen(s)+1); + checkptr(t); + strcpy(t,s); + (void)rcsid; /* avoid a warning */ + (void)rcsname; /* avoid a warning */ + return t; +} diff --git a/web/noweb/src/csmake b/web/noweb/src/csmake new file mode 100755 index 0000000000..7c658591e4 --- /dev/null +++ b/web/noweb/src/csmake @@ -0,0 +1,3 @@ +#!/bin/sh +if [ $# -eq 0 ]; then set all install; fi # "$@" breaks make for empty args +make CC=gcc BIN=/homes/nr/pkg/noweb/bin LIB=/homes/nr/pkg/noweb/lib MAN=/homes/nr/pkg/noweb/man TEXINPUTS=/homes/nr/pkg/tex/inputs "$@" diff --git a/web/noweb/src/elisp/noweb-mode.el b/web/noweb/src/elisp/noweb-mode.el new file mode 100644 index 0000000000..2371e6afda --- /dev/null +++ b/web/noweb/src/elisp/noweb-mode.el @@ -0,0 +1,995 @@ +;; noweb-mode.el - edit noweb files with GNU Emacs +;; Copyright (C) 1995 by Thorsten.Ohl @ Physik.TH-Darmstadt.de +;; with a little help from Norman Ramsey <norman@bellcore.com> +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;; +;; See bottom of this file for information on language-dependent highlighting +;; +;; $Id: noweb-mode.el,v 1.19 2008/10/06 01:03:05 nr Exp nr $ +;; $Name: v2_12 $ +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; THIS IS UNRELEASED CODE: IT IS MISSING FUNCTIONALITY AND IT NEEDS CLEANUP ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Put this into your ~/.emacs to use this mode automagically. +;; +;; (autoload 'noweb-mode "noweb-mode" "Editing noweb files." t) +;; (setq auto-mode-alist (append (list (cons "\\.nw$" 'noweb-mode)) +;; auto-mode-alist)) + +;; NEWS: +;; +;; Version 2.12 +;; +;; From Christan Lynbeck <christian@defun.dk>, a small fix to make +;; font-lock work with nowbe-mode. It is quite simple, narrow to the +;; current chunk before calling the appropriate mode function. If one +;; also sets `font-lock-dont-widen' to true, the effect is that +;; highlighting only is done inside each chunk. +;; +;; Earlier versions: +;; +;; * [tho] M-n q, aka: M-x noweb-fill-chunk +;; +;; * [tho] M-n TAB, aka: M-x noweb-complete-chunk +;; +;; * [tho] noweb-occur +;; +;; * [nr] use `M-n' instead of `C-c n' as default command prefix +;; +;; * [nr] don't be fooled by +;; +;; @ +;; <<foo>>= +;; int foo; +;; @ %def foo +;; Here starts a new documentation chunk! +;; <<bar>>= +;; int bar; +;; +;; * [nr] switch mode changing commands off during isearch-mode +;; +;; * [tho] noweb-goto-chunk proposes a default +;; + +;; TODO: +;; +;; * replace obscure hacks like `(stringp (car (noweb-find-chunk)))' +;; by something more reasonable like `(noweb-code-chunkp)'. +;; +;; * _maybe_ replace our `noweb-chunk-vector' by text properties. We +;; could then use highlighting to jazz up the visual appearance. +;; +;; * wrapped `noweb-goto-next' and `noweb-goto-previous' +;; +;; * more range checks and error exits +;; +;; * commands for tangling, weaving, etc. +;; +;; * ... +;; + +;;; Variables + +(defconst noweb-mode-RCS-Id + "$Id: noweb-mode.el,v 1.19 2008/10/06 01:03:05 nr Exp nr $") + +(defconst noweb-mode-RCS-Name + "$Name: v2_12 $") + +(defvar noweb-mode-prefix "\M-n" + "*Prefix key to use for noweb mode commands. +The value of this variable is checked as part of loading noweb mode. +After that, changing the prefix key requires manipulating keymaps.") + +(defvar noweb-mode-load-hook nil + "Hook that is run after noweb mode is loaded.") + +(defvar noweb-mode-hook nil + "Hook that is run after entering noweb mode.") + +(defvar noweb-select-code-mode-hook nil + "Hook that is run after the code mode is selected. +This is the place to overwrite keybindings of the NOWEB-CODE-MODE.") + +(defvar noweb-select-doc-mode-hook nil + "Hook that is run after the documentation mode is selected. +This is the place to overwrite keybindings of the NOWEB-DOC-MODE.") + +(defvar noweb-select-mode-hook nil + "Hook that is run after the documentation or the code mode is selected. +This is the place to overwrite keybindings of the other modes.") + +(defvar noweb-doc-mode 'latex-mode + "Major mode for editing documentation chunks.") + +(defvar noweb-code-mode 'fundamental-mode + "Major mode for editing code chunks. This is set to FUNDAMENTAL-MODE +by default, but you might want to change this in the Local Variables +section of your file to something more appropriate, like C-MODE, +FORTRAN-MODE, or even INDENTED-TEXT-MODE.") + +(defvar noweb-chunk-vector nil + "Vector of the chunks in this buffer.") + +(defvar noweb-narrowing nil + "If not NIL, the display will always be narrowed to the +current chunk pair.") + +(defvar noweb-electric-@-and-< t + "If not nil, the keys `@' and `<' will be bound to NOWEB-ELECTRIC-@ +and NOWEB-ELECTRIC-<, respectively.") + + +;;; Setup +(defvar noweb-mode-prefix-map nil + "Keymap for noweb mode commands.") + +(defvar noweb-mode-menu-map nil + "Keymap for noweb mode menu commands.") + +(defvar noweb-mode nil + "Buffer local variable, T iff this buffer is edited in noweb mode.") + +(if (not (assq 'noweb-mode minor-mode-alist)) + (setq minor-mode-alist (append minor-mode-alist + (list '(noweb-mode " Noweb"))))) + +(defun noweb-minor-mode () + "Minor meta mode for editing noweb files. See NOWEB-MODE." + (interactive) + (noweb-mode)) + +(defun noweb-mode (&optional arg-for-desktop-read) + "Minor meta mode for editing noweb files. +`Meta' refers to the fact that this minor mode is switching major +modes depending on the location of point. + +The following special keystrokes are available in noweb mode: + +Movement: +\\[noweb-next-chunk] \tgoto the next chunk +\\[noweb-previous-chunk] \tgoto the previous chunk +\\[noweb-goto-previous] \tgoto the previous chunk of the same name +\\[noweb-goto-next] \tgoto the next chunk of the same name +\\[noweb-goto-chunk] \tgoto a chunk +\\[noweb-next-code-chunk] \tgoto the next code chunk +\\[noweb-previous-code-chunk] \tgoto the previous code chunk +\\[noweb-next-doc-chunk] \tgoto the next documentation chunk +\\[noweb-previous-doc-chunk] \tgoto the previous documentation chunk + +Copying/Killing/Marking/Narrowing: +\\[noweb-copy-chunk-as-kill] \tcopy the chunk the point is in into the kill ring +\\[noweb-copy-chunk-pair-as-kill] \tcopy the pair of doc/code chunks the point is in +\\[noweb-kill-chunk] \tkill the chunk the point is in +\\[noweb-kill-chunk-pair] \tkill the pair of doc/code chunks the point is in +\\[noweb-mark-chunk] \tmark the chunk the point is in +\\[noweb-mark-chunk-pair] \tmark the pair of doc/code chunks the point is in +\\[noweb-narrow-to-chunk] \tnarrow to the chunk the point is in +\\[noweb-narrow-to-chunk-pair] \tnarrow to the pair of doc/code chunks the point is in +\\[widen] \twiden +\\[noweb-toggle-narrowing] \ttoggle auto narrowing + +Filling: +\\[noweb-fill-chunk] \tfill the chunk at point according to mode. +\\[noweb-fill-paragraph-chunk] \tfill the paragraph at point, restricted to chunk. + +Insertion: +\\[noweb-insert-mode-line] \tinsert a line to set this file's code mode +\\[noweb-new-chunk] \tinsert a new chunk at point +\\[noweb-complete-chunk] \tcomplete the chunk name before point. +\\[noweb-electric-@] \tinsert a `@' or start a new doc chunk. +\\[noweb-electric-<] \tinsert a `<' or start a new code chunk. + +Modes: +\\[noweb-set-doc-mode] \tchange the major mode for editing documentation chunks +\\[noweb-set-code-mode] \tchange the major mode for editing code chunks + +Misc: +\\[noweb-occur] \tfind all occurrences of the current chunk +\\[noweb-update-chunk-vector] \tupdate the markers for chunks +\\[noweb-describe-mode] \tdescribe noweb-mode +\\[noweb-mode-version] \tshow noweb-mode's version in the minibuffer +" + (interactive) + (mapcar 'noweb-make-variable-permanent-local + '(noweb-mode + noweb-narrowing + noweb-chunk-vector + post-command-hook + isearch-mode-hook + isearch-mode-end-hook + noweb-doc-mode + noweb-code-mode)) + (setq noweb-mode t) + (noweb-setup-keymap) + (noweb-update-chunk-vector) + (add-hook 'post-command-hook 'noweb-post-command-hook) + (add-hook 'noweb-select-doc-mode-hook 'noweb-auto-fill-doc-mode) + (add-hook 'noweb-select-code-mode-hook 'noweb-auto-fill-code-mode) + (add-hook 'isearch-mode-hook 'noweb-note-isearch-mode) + (add-hook 'isearch-mode-end-hook 'noweb-note-isearch-mode-end) + (run-hooks 'noweb-mode-hook) + (message "noweb mode: use `M-x noweb-describe-mode' for further information")) + +(defun noweb-setup-keymap () + "Setup the noweb-mode keymap. This function is rerun every time the +major modes changes, because it might have grabbed the keys." + (if noweb-mode-prefix-map + nil + (setq noweb-mode-prefix-map (make-sparse-keymap)) + (noweb-bind-keys)) + (if noweb-mode-menu-map + nil + (setq noweb-mode-menu-map (make-sparse-keymap "Noweb")) + (noweb-bind-menu)) + (if noweb-electric-@-and-< + (progn + (local-set-key "@" 'noweb-electric-@) + (local-set-key "<" 'noweb-electric-<))) + (local-set-key "\M-q" 'noweb-fill-paragraph-chunk) + (local-set-key noweb-mode-prefix noweb-mode-prefix-map) + (local-set-key [menu-bar noweb] (cons "Noweb" noweb-mode-menu-map))) + + (defun noweb-bind-keys () + "Establish noweb mode key bindings." + (define-key noweb-mode-prefix-map "\C-n" 'noweb-next-chunk) + (define-key noweb-mode-prefix-map "\C-p" 'noweb-previous-chunk) + (define-key noweb-mode-prefix-map "\M-n" 'noweb-goto-next) + (define-key noweb-mode-prefix-map "\M-m" 'noweb-insert-mode-line) + (define-key noweb-mode-prefix-map "\M-p" 'noweb-goto-previous) + (define-key noweb-mode-prefix-map "c" 'noweb-next-code-chunk) + (define-key noweb-mode-prefix-map "C" 'noweb-previous-code-chunk) + (define-key noweb-mode-prefix-map "d" 'noweb-next-doc-chunk) + (define-key noweb-mode-prefix-map "D" 'noweb-previous-doc-chunk) + (define-key noweb-mode-prefix-map "g" 'noweb-goto-chunk) + (define-key noweb-mode-prefix-map "\C-l" 'noweb-update-chunk-vector) + (define-key noweb-mode-prefix-map "\M-l" 'noweb-update-chunk-vector) + (define-key noweb-mode-prefix-map "w" 'noweb-copy-chunk-as-kill) + (define-key noweb-mode-prefix-map "W" 'noweb-copy-chunk-pair-as-kill) + (define-key noweb-mode-prefix-map "k" 'noweb-kill-chunk) + (define-key noweb-mode-prefix-map "K" 'noweb-kill-chunk-pair) + (define-key noweb-mode-prefix-map "m" 'noweb-mark-chunk) + (define-key noweb-mode-prefix-map "M" 'noweb-mark-chunk-pair) + (define-key noweb-mode-prefix-map "n" 'noweb-narrow-to-chunk) + (define-key noweb-mode-prefix-map "N" 'noweb-narrow-to-chunk-pair) + (define-key noweb-mode-prefix-map "t" 'noweb-toggle-narrowing) + (define-key noweb-mode-prefix-map "\t" 'noweb-complete-chunk) + (define-key noweb-mode-prefix-map "q" 'noweb-fill-chunk) + (define-key noweb-mode-prefix-map "i" 'noweb-new-chunk) + (define-key noweb-mode-prefix-map "o" 'noweb-occur) + (define-key noweb-mode-prefix-map "v" 'noweb-mode-version) + (define-key noweb-mode-prefix-map "h" 'noweb-describe-mode) + (define-key noweb-mode-prefix-map "\C-h" 'noweb-describe-mode)) + + (defun noweb-bind-menu () + "Establish noweb mode menu bindings." + (define-key noweb-mode-menu-map [noweb-mode-version] + '("Version" . noweb-mode-version)) + (define-key noweb-mode-menu-map [noweb-describe-mode] + '("Help" . noweb-describe-mode)) + (define-key noweb-mode-menu-map [separator-noweb-help] '("--")) + (define-key noweb-mode-menu-map [noweb-occur] + '("Chunk occurrences" . noweb-occur)) + (define-key noweb-mode-menu-map [noweb-update-chunk-vector] + '("Update the chunk vector" . noweb-update-chunk-vector)) + (define-key noweb-mode-menu-map [noweb-new-chunk] + '("Insert new chunk" . noweb-new-chunk)) + (define-key noweb-mode-menu-map [noweb-fill-chunk] + '("Fill current chunk" . noweb-fill-chunk)) + (define-key noweb-mode-menu-map [noweb-complete-chunk] + '("Complete chunk name" . noweb-complete-chunk)) + (define-key noweb-mode-menu-map [separator-noweb-chunks] '("--")) + (define-key noweb-mode-menu-map [noweb-toggle-narrowing] + '("Toggle auto narrowing" . noweb-toggle-narrowing)) + (define-key noweb-mode-menu-map [noweb-narrow-to-chunk-pair] + '("Narrow to chunk pair" . noweb-narrow-to-chunk-pair)) + (define-key noweb-mode-menu-map [noweb-narrow-to-chunk] + '("Narrow to chunk" . noweb-narrow-to-chunk)) + (define-key noweb-mode-menu-map [noweb-mark-chunk-pair] + '("Mark chunk pair" . noweb-mark-chunk-pair)) + (define-key noweb-mode-menu-map [noweb-mark-chunk] + '("Mark chunk" . noweb-mark-chunk)) + (define-key noweb-mode-menu-map [noweb-kill-chunk-pair] + '("Kill chunk pair" . noweb-kill-chunk-pair)) + (define-key noweb-mode-menu-map [noweb-kill-chunk] + '("Kill chunk" . noweb-kill-chunk)) + (define-key noweb-mode-menu-map [noweb-copy-chunk-pair-as-kill] + '("Copy chunk pair" . noweb-copy-chunk-pair-as-kill)) + (define-key noweb-mode-menu-map [noweb-copy-chunk-as-kill] + '("Copy chunk" . noweb-copy-chunk-as-kill)) + (define-key noweb-mode-menu-map [separator-noweb-move] '("--")) + (define-key noweb-mode-menu-map [noweb-next-doc-chunk] + '("Next documentation chunk" . noweb-next-doc-chunk)) + (define-key noweb-mode-menu-map [noweb-previous-doc-chunk] + '("Previous documentation chunk" . noweb-previous-doc-chunk)) + (define-key noweb-mode-menu-map [noweb-next-code-chunk] + '("Next code chunk" . noweb-next-code-chunk)) + (define-key noweb-mode-menu-map [noweb-previous-code-chunk] + '("Previous code chunk" . noweb-previous-code-chunk)) + (define-key noweb-mode-menu-map [noweb-goto-chunk] + '("Goto chunk" . noweb-goto-chunk)) + (define-key noweb-mode-menu-map [noweb-goto-next] + '("Next chunk of same name" . noweb-goto-next)) + (define-key noweb-mode-menu-map [noweb-goto-previous] + '("Previous chunk of same name" . noweb-goto-previous)) + (define-key noweb-mode-menu-map [noweb-next-chunk] + '("Next chunk" . noweb-next-chunk)) + (define-key noweb-mode-menu-map [noweb-previous-chunk] + '("Previous chunk" . noweb-previous-chunk))) + +(defun noweb-make-variable-permanent-local (var) + "Declare VAR buffer local, but protect it from beeing killed +by major mode changes." + (make-variable-buffer-local var) + (put var 'permanent-local 't)) + +(defun noweb-note-isearch-mode () + "Take note of an incremental search in progress" + (remove-hook 'post-command-hook 'noweb-post-command-hook)) + +(defun noweb-note-isearch-mode-end () + "Take note of an incremental search having ended" + (add-hook 'post-command-hook 'noweb-post-command-hook)) + +(defun noweb-post-command-hook () + "The hook being run after each command in noweb mode." + (noweb-select-mode) + ;; reinstall our keymap if the major mode screwed it up: + (noweb-setup-keymap)) + + +;;; Chunks + +(defvar *noweb-chunk-regexp* + "^\\(@\\( \\|$\\|\\( %def\\)\\)\\|<<\\(.*\\)>>=\\)" + "Regular expression usuable for finding chunk boundaries.") + +(defun noweb-update-chunk-vector () + "Scan the whole buffer and place a marker at each \"^@\" and \"^<<\". +Record them in NOWEB-CHUNK-VECTOR." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((chunk-list (list (cons 'doc (point-marker))))) + (while (re-search-forward *noweb-chunk-regexp* nil t) + (goto-char (match-beginning 0)) + ;; If the 3rd subexpression matched @ %def, we're still in a code + ;; chunk (sort of), so don't place a marker here. + (if (not (match-beginning 3)) + (setq chunk-list + ;; If the 4th subexpression matched inside <<...>>, we're seeing + ;; a new code chunk. + (cons (cons (if (match-beginning 4) + (buffer-substring (match-beginning 4) (match-end 4)) + 'doc) + (point-marker)) + chunk-list)) + ;; Scan forward either to !/^@ %def/, which will start a docs chunk, + ;; or to /^<<.*>>=$/, which will start a code chunk. + (progn + (next-line 1) + (while (looking-at "@ %def") + (next-line 1)) + (setq chunk-list + ;; Now we can tell code vs docs + (cons (cons (if (looking-at "<<\\(.*\\)>>=") + (buffer-substring (match-beginning 1) (match-end 1)) + 'doc) + (point-marker)) + chunk-list)))) + (next-line 1)) + (setq chunk-list (cons (cons 'doc (point-max-marker)) chunk-list)) + (setq noweb-chunk-vector (vconcat (reverse chunk-list)))))) + +(defun noweb-find-chunk () + "Return a pair consisting of the name (or 'DOC) and the +marker of the current chunk." + (if (not noweb-chunk-vector) + (noweb-update-chunk-vector)) + (aref noweb-chunk-vector (noweb-find-chunk-index-buffer))) + +(defun noweb-find-chunk-index-buffer () + "Return the index of the current chunk in NOWEB-CHUNK-VECTOR." + (noweb-find-chunk-index 0 (1- (length noweb-chunk-vector)))) + +(defun noweb-find-chunk-index (low hi) + (if (= hi (1+ low)) + low + (let ((med (/ (+ low hi) 2))) + (if (<= (point) (cdr (aref noweb-chunk-vector med))) + (noweb-find-chunk-index low med) + (noweb-find-chunk-index med hi))))) + +(defun noweb-chunk-region () + "Return a pair consisting of the beginning and end of the current chunk." + (interactive) + (let ((start (noweb-find-chunk-index-buffer))) + (cons (marker-position (cdr (aref noweb-chunk-vector start))) + (marker-position (cdr (aref noweb-chunk-vector (1+ start))))))) + +(defun noweb-chunk-pair-region () + "Return a pair consisting of the beginning and end of the current pair of +documentation and code chunks." + (interactive) + (let* ((start (noweb-find-chunk-index-buffer)) + (end (1+ start))) + (if (stringp (car (aref noweb-chunk-vector start))) + (cons (marker-position (cdr (aref noweb-chunk-vector (1- start)))) + (marker-position (cdr (aref noweb-chunk-vector end)))) + (while (not (stringp (car (aref noweb-chunk-vector end)))) + (setq end (1+ end))) + (cons (marker-position (cdr (aref noweb-chunk-vector start))) + (marker-position (cdr (aref noweb-chunk-vector (1+ end)))))))) + +(defun noweb-chunk-vector-aref (i) + (if (< i 0) + (error "Before first chunk.")) + (if (>= i (length noweb-chunk-vector)) + (error "Beyond last chunk.")) + (aref noweb-chunk-vector i)) + +(defun noweb-complete-chunk () + "Complete the chunk name before point, if any." + (interactive) + (if (stringp (car (aref noweb-chunk-vector + (noweb-find-chunk-index-buffer)))) + (let ((end (point)) + (beg (save-excursion + (if (re-search-backward "<<" + (save-excursion + (beginning-of-line) + (point)) + t) + (match-end 0) + nil)))) + (if beg + (let* ((pattern (buffer-substring beg end)) + (alist (noweb-build-chunk-alist)) + (completion (try-completion pattern alist))) + (cond ((eq completion t)) + ((null completion) + (message "Can't find completion for \"%s\"" pattern) + (ding)) + ((not (string= pattern completion)) + (delete-region beg end) + (insert completion) + (if (not (looking-at ">>")) + (insert ">>"))) + (t + (message "Making completion list...") + (with-output-to-temp-buffer "*Completions*" + (display-completion-list (all-completions pattern alist))) + (message "Making completion list...%s" "done")))) + (message "Not at chunk name..."))) + (message "Not in code chunk..."))) + + +;;; Filling, etc + +(defun noweb-hide-code-quotes () + "Replace all non blank characters in [[...]] code quotes +in the current buffer (you might want to narrow to the interesting +region first) by `*'. Return a list of pairs with the position and +value of the original strings." + (save-excursion + (let ((quote-list nil)) + (goto-char (point-min)) + (while (re-search-forward "\\[\\[" nil 'move) + (let ((beg (match-end 0)) + (end (if (re-search-forward "\\]\\]" nil t) + (match-beginning 0) + (point-max)))) + (goto-char beg) + (while (< (point) end) + ;; Move on to the next word: + (let ((b (progn + (skip-chars-forward " \t\n" end) + (point))) + (e (progn + (skip-chars-forward "^ \t\n" end) + (point)))) + (if (> e b) + ;; Save the string and a marker to the end of the + ;; replacement text. A marker to the beginning is + ;; useless. See NOWEB-RESTORE-CODE-QUOTES. + (save-excursion + (setq quote-list (cons (cons (copy-marker e) + (buffer-substring b e)) + quote-list)) + (goto-char b) + (insert-char ?* (- e b) t) + (delete-char (- e b)))))))) + (reverse quote-list)))) + +(defun noweb-restore-code-quotes (quote-list) + "Reinsert the strings modified by `noweb-hide-code-quotes'." + (save-excursion + (mapcar '(lambda (q) + (let* ((e (marker-position (car q))) + ;; Slightly inefficient, but correct way to find + ;; the beginning of the word to be replaced. + ;; Using the marker at the beginning will loose + ;; if whitespace has been rearranged + (b (save-excursion + (goto-char e) + (skip-chars-backward "*") + (point)))) + (delete-region b e) + (goto-char b) + (insert (cdr q)))) + quote-list))) + +(defun noweb-fill-chunk () + "Fill the current chunk according to mode. +Run `fill-region' on documentation chunks and `indent-region' on code +chunks." + (interactive) + (save-restriction + (noweb-narrow-to-chunk) + (if (stringp (car (noweb-find-chunk))) + (progn + ;; Narrow to the code section proper; w/o the first and any + ;; index declaration lines. + (narrow-to-region (progn + (goto-char (point-min)) + (forward-line 1) + (point)) + (progn + (goto-char (point-max)) + (forward-line -1) + (while (looking-at "@") + (forward-line -1)) + (forward-line 1) + (point))) + (if (or indent-region-function indent-line-function) + (indent-region (point-min) (point-max) nil) + (error "No indentation functions defined in %s!" major-mode))) + (let ((quote-list (noweb-hide-code-quotes))) + (fill-region (point-min) (point-max)) + (noweb-restore-code-quotes quote-list))))) + +(defun noweb-fill-paragraph-chunk (&optional justify) + "Fill a paragraph in the current chunk." + (interactive "P") + (noweb-update-chunk-vector) + (save-restriction + (noweb-narrow-to-chunk) + (if (stringp (car (noweb-find-chunk))) + (progn + ;; Narrow to the code section proper; w/o the first and any + ;; index declaration lines. + (narrow-to-region (progn + (goto-char (point-min)) + (forward-line 1) + (point)) + (progn + (goto-char (point-max)) + (forward-line -1) + (while (looking-at "@") + (forward-line -1)) + (forward-line 1) + (point))) + (fill-paragraph justify)) + (let ((quote-list (noweb-hide-code-quotes))) + (fill-paragraph justify) + (noweb-restore-code-quotes quote-list))))) + +(defun noweb-auto-fill-doc-chunk () + "Replacement for `do-auto-fill'." + (save-restriction + (narrow-to-region (car (noweb-chunk-region)) + (save-excursion + (end-of-line) + (point))) + (let ((quote-list (noweb-hide-code-quotes))) + (do-auto-fill) + (noweb-restore-code-quotes quote-list)))) + +(defun noweb-auto-fill-doc-mode () + "Install the improved auto fill function, iff necessary." + (if auto-fill-function + (setq auto-fill-function 'noweb-auto-fill-doc-chunk))) + +(defun noweb-auto-fill-code-mode () + "Install the default auto fill function, iff necessary." + (if auto-fill-function + (setq auto-fill-function 'do-auto-fill))) + +;;; Marking + +(defun noweb-mark-chunk () + "Mark the current chunk." + (interactive) + (let ((r (noweb-chunk-region))) + (goto-char (car r)) + (push-mark (cdr r) nil t))) + +(defun noweb-mark-chunk-pair () + "Mark the current pair of documentation and code chunks." + (interactive) + (let ((r (noweb-chunk-pair-region))) + (goto-char (car r)) + (push-mark (cdr r) nil t))) + + +;;; Narrowing + +(defun noweb-toggle-narrowing (&optional arg) + "Toggle if we should narrow the display to the current pair of +documentation and code chunks after each movement. With argument: +switch narrowing on." + (interactive "P") + (if (or arg (not noweb-narrowing)) + (progn + (setq noweb-narrowing t) + (noweb-narrow-to-chunk-pair)) + (setq noweb-narrowing nil) + (widen))) + +(defun noweb-narrow-to-chunk () + "Narrow the display to the current chunk." + (interactive) + (let ((r (noweb-chunk-region))) + (narrow-to-region (car r) (cdr r)))) + +(defun noweb-narrow-to-chunk-pair () + "Narrow the display to the current pair of documentation and code chunks." + (interactive) + (let ((r (noweb-chunk-pair-region))) + (narrow-to-region (car r) (cdr r)))) + + +;;; Killing + +(defun noweb-kill-chunk () + "Kill the current chunk." + (interactive) + (let ((r (noweb-chunk-region))) + (kill-region (car r) (cdr r)))) + +(defun noweb-kill-chunk-pair () + "Kill the current pair of chunks." + (interactive) + (let ((r (noweb-chunk-pair-region))) + (kill-region (car r) (cdr r)))) + +(defun noweb-copy-chunk-as-kill () + "Place the current chunk on the kill ring." + (interactive) + (let ((r (noweb-chunk-region))) + (copy-region-as-kill (car r) (cdr r)))) + +(defun noweb-copy-chunk-pair-as-kill () + "Place the current pair of chunks on the kill ring." + (interactive) + (let ((r (noweb-chunk-pair-region))) + (copy-region-as-kill (car r) (cdr r)))) + + +;;; Movement + +(defun noweb-sign (n) + "Return the sign of N." + (if (< n 0) -1 1)) + +(defun noweb-next-doc-chunk (&optional cnt) + "Goto to the Nth documentation chunk from point." + (interactive "p") + (widen) + (let ((start (noweb-find-chunk-index-buffer)) + (i 1)) + (while (<= i (abs cnt)) + (setq start (+ (noweb-sign cnt) start)) + (while (stringp (car (noweb-chunk-vector-aref start))) + (setq start (+ (noweb-sign cnt) start))) + (setq i (1+ i))) + (goto-char (marker-position (cdr (noweb-chunk-vector-aref start)))) + (forward-char 1)) + (if noweb-narrowing + (noweb-narrow-to-chunk-pair))) + +(defun noweb-previous-doc-chunk (&optional n) + "Goto to the -Nth documentation chunk from point." + (interactive "p") + (noweb-next-doc-chunk (- n))) + +(defun noweb-next-code-chunk (&optional cnt) + "Goto to the Nth code chunk from point." + (interactive "p") + (widen) + (let ((start (noweb-find-chunk-index-buffer)) + (i 1)) + (while (<= i (abs cnt)) + (setq start (+ (noweb-sign cnt) start)) + (while (not (stringp (car (noweb-chunk-vector-aref start)))) + (setq start (+ (noweb-sign cnt) start))) + (setq i (1+ i))) + (goto-char (marker-position (cdr (noweb-chunk-vector-aref start)))) + (next-line 1)) + (if noweb-narrowing + (noweb-narrow-to-chunk-pair))) + +(defun noweb-previous-code-chunk (&optional n) + "Goto to the -Nth code chunk from point." + (interactive "p") + (noweb-next-code-chunk (- n))) + +(defun noweb-next-chunk (&optional n) + "If in a documentation chunk, goto to the Nth documentation +chunk from point, else goto to the Nth code chunk from point." + (interactive "p") + (if (stringp (car (aref noweb-chunk-vector + (noweb-find-chunk-index-buffer)))) + (noweb-next-code-chunk n) + (noweb-next-doc-chunk n))) + +(defun noweb-previous-chunk (&optional n) + "If in a documentation chunk, goto to the -Nth documentation +chunk from point, else goto to the -Nth code chunk from point." + (interactive "p") + (noweb-next-chunk (- n))) + +(defvar noweb-chunk-history nil + "") + +(defun noweb-goto-chunk () + "Goto the named chunk." + (interactive) + (widen) + (let* ((completion-ignore-case t) + (alist (noweb-build-chunk-alist)) + (chunk (completing-read + "Chunk: " alist nil t + (noweb-goto-chunk-default) + noweb-chunk-history))) + (goto-char (cdr (assoc chunk alist)))) + (if noweb-narrowing + (noweb-narrow-to-chunk-pair))) + +(defun noweb-goto-chunk-default () + (save-excursion + (if (re-search-backward "<<" + (save-excursion + (beginning-of-line) + (point)) + 'move) + (goto-char (match-beginning 0))) + (if (re-search-forward "<<\\(.*\\)>>" + (save-excursion + (end-of-line) + (point)) + t) + (buffer-substring (match-beginning 1) (match-end 1)) + nil))) + +(defun noweb-build-chunk-alist () + (if (not noweb-chunk-vector) + (noweb-update-chunk-vector)) + ;; The naive recursive solution will exceed MAX-LISP-EVAL-DEPTH in + ;; buffers w/ many chunks. Maybe there is a tail recursivce solution, + ;; but iterative solutions should be acceptable for dealing with vectors. + (let ((alist nil) + (i (1- (length noweb-chunk-vector)))) + (while (>= i 0) + (let* ((chunk (aref noweb-chunk-vector i)) + (name (car chunk)) + (marker (cdr chunk))) + (if (and (stringp name) + (not (assoc name alist))) + (setq alist (cons (cons name marker) alist)))) + (setq i (1- i))) + alist)) + +(defun noweb-goto-next (&optional cnt) + "Goto the continuation of the current chunk." + (interactive "p") + (widen) + (if (not noweb-chunk-vector) + (noweb-update-chunk-vector)) + (let ((start (noweb-find-chunk-index-buffer))) + (if (not (stringp (car (aref noweb-chunk-vector start)))) + (setq start (1+ start))) + (if (stringp (car (noweb-chunk-vector-aref start))) + (let ((name (car (noweb-chunk-vector-aref start))) + (i 1)) + (while (<= i (abs cnt)) + (setq start (+ (noweb-sign cnt) start)) + (while (not (equal (car (noweb-chunk-vector-aref start)) + name)) + (setq start (+ (noweb-sign cnt) start))) + (setq i (1+ i))) + (goto-char (marker-position + (cdr (noweb-chunk-vector-aref start)))) + (next-line 1)))) + (if noweb-narrowing + (noweb-narrow-to-chunk-pair))) + +(defun noweb-goto-previous (&optional cnt) + "Goto the previous chunk." + (interactive "p") + (noweb-goto-next (- cnt))) + +(defun noweb-occur (arg) + "Find all occurences of the current chunk. +This function simply runns OCCUR on \"<<NAME>>\"." + (interactive "P") + (let ((n (if (and arg + (numberp arg)) + arg + 0)) + (idx (noweb-find-chunk-index-buffer))) + (if (stringp (car (aref noweb-chunk-vector idx))) + (occur (regexp-quote (concat "<<" + (car (aref noweb-chunk-vector idx)) + ">>")) + n) + (setq idx (1+ idx)) + (while (not (stringp (car (aref noweb-chunk-vector idx)))) + (setq idx (1+ idx))) + (occur (regexp-quote (concat "<<" + (car (aref noweb-chunk-vector idx)) + ">>")) + n)))) + + +;;; Insertion + +(defun noweb-new-chunk (name) + "Insert a new chunk." + (interactive "sChunk name: ") + (insert "@ \n<<" name ">>=\n") + (save-excursion + (insert "@ %def \n")) + (noweb-update-chunk-vector)) + +(defun noweb-at-beginning-of-line () + (equal (save-excursion + (beginning-of-line) + (point)) + (point))) + +(defun noweb-electric-@ (arg) + "Smart incarnation of `@', starting a new documentation chunk, maybe. +If given an numerical argument, it will act just like the dumb `@'. +Otherwise and if at the beginning of a line in a code chunk: +insert \"@ \" and update the chunk vector." + (interactive "P") + (if arg + (self-insert-command (if (numberp arg) arg 1)) + (if (and (noweb-at-beginning-of-line) + (stringp (car (noweb-find-chunk)))) + (progn + (insert "@ ") + (noweb-update-chunk-vector)) + (self-insert-command 1)))) + +(defun noweb-electric-< (arg) + "Smart incarnation of `<', starting a new code chunk, maybe. +If given an numerical argument, it will act just like the dumb `<'. +Otherwise and if at the beginning of a line in a documentation chunk: +insert \"<<>>=\" and a newline if necessary. Leave point in the middle +and and update the chunk vector." + (interactive "P") + (if arg + (self-insert-command (if (numberp arg) arg 1)) + (if (and (noweb-at-beginning-of-line) + (not (stringp (car (noweb-find-chunk))))) + (progn + (insert "<<") + (save-excursion + (insert ">>=") + (if (not (looking-at "\\s *$")) + (newline))) + (noweb-update-chunk-vector)) + (self-insert-command 1)))) + + +;;; Modes + +(defun noweb-chunk-boundary-backward () + "Find beginning of current chunk." + (save-match-data + (save-excursion + (when (re-search-backward *noweb-chunk-regexp* nil 'move) + (goto-char (match-beginning 0))) + (point)))) + +(defun noweb-chunk-boundary-forward () + "Find beginning of next chunk." + (save-match-data + (save-excursion + (when (re-search-forward *noweb-chunk-regexp* nil 'move) + (goto-char (match-beginning 0))) + (point)))) + + +(defun noweb-select-mode () + "Select NOWEB-DOC-MODE or NOWEB-CODE-MODE, as appropriate." + (interactive) + (let ((beg (noweb-chunk-boundary-backward)) + (end (noweb-chunk-boundary-forward))) + (save-restriction + (narrow-to-region beg end) + (if (= (char-after beg) ?<) + ;; Inside a code chunk + (if (equal major-mode noweb-code-mode) + nil + (funcall noweb-code-mode) + (run-hooks 'noweb-select-code-mode-hook) + (run-hooks 'noweb-select-mode-hook)) + ;; Inside a documentation chunk + (if (equal major-mode noweb-doc-mode) + nil + (funcall noweb-doc-mode) + (run-hooks 'noweb-select-doc-mode-hook) + (run-hooks 'noweb-select-mode-hook)))))) + +(defun noweb-set-doc-mode (mode) + "Change the major mode for editing documentation chunks." + (interactive "CNew major mode for documentation chunks: ") + (setq noweb-doc-mode mode)) + +(defun noweb-set-code-mode (mode) + "Change the major mode for editing code chunks." + (interactive "CNew major mode for code chunks: ") + (setq noweb-code-mode mode)) + + +;;; Misc + +(defun noweb-mode-version () + "Echo the RCS identification of noweb mode." + (interactive) + (message "Thorsten's noweb-mode (PRERELEASE). RCS: %s" + noweb-mode-RCS-Id)) + +(defun noweb-describe-mode () + "Describe noweb mode." + (interactive) + (describe-function 'noweb-mode)) + +(defun noweb-insert-mode-line (arg) + "Insert line that will set the noweb mode of this file in emacs" + (interactive "CNoweb code mode for this file: ") + (save-excursion + (goto-char (point-min)) + (insert "% -*- mode: Noweb; noweb-code-mode: " (symbol-name arg) " -*-\n"))) + + +;;; Debugging + +(defun noweb-log (s) + (let ((b (current-buffer))) + (switch-to-buffer (get-buffer-create "*noweb-log*")) + (goto-char (point-max)) + (setq buffer-read-only nil) + (insert s) + (setq buffer-read-only t) + (switch-to-buffer b))) + + +;;; Finale + +(run-hooks 'noweb-mode-load-hook) +(provide 'noweb-mode) + + +;; Local Variables: +;; mode:emacs-lisp +;; End: + diff --git a/web/noweb/src/icon/Makefile b/web/noweb/src/icon/Makefile new file mode 100644 index 0000000000..7d9b8db1eb --- /dev/null +++ b/web/noweb/src/icon/Makefile @@ -0,0 +1,151 @@ +SHELL=/bin/sh +LIB=/dev/null # to be overridden +BIN=/dev/null # to be overridden +ICONC=iconc -p -L/usr/X11R6/lib # override to icont if no iconc on your system +ICONT=icont # override to iconc if all filters should be compiled +CPIF=| cpif # change to ">" to insure all sources always made + +LIBEXECS=totex disambiguate noidx tohtml elide l2h docs2comments \ + autodefs.tex autodefs.icon autodefs.yacc autodefs.sml autodefs.pascal \ + autodefs.promela autodefs.lrtl autodefs.asdl autodefs.mmix xchunks pipedocs +LIBSPECIAL=autodefs.cee +BINEXECS=noindex sl2h htmltoc +EXECS=$(LIBEXECS) $(BINEXECS) $(LIBSPECIAL) pdcached +SRCS=totex.icn disambiguate.icn noidx.icn texdefs.icn icondefs.icn \ + yaccdefs.icn noindex.icn smldefs.icn tohtml.icn cdefs.icn elide.icn \ + l2h.icn sl2h.icn pascaldefs.icn promeladefs.icn lrtldefs.icn asdldefs.icn \ + mmixdefs.icn htmltoc.icn xchunks.icn docs2comments.icn pipedocs.icn pdcached.icn + +.SUFFIXES: .nw .icn .html .tex .dvi +.nw.icn: + notangle -L'#line %-1L "%F"%N' $*.nw $(CPIF) $*.icn +.nw.html: + noweave -filter l2h -autodefs icon -html -index $*.nw | htmltoc > $*.html +.nw.tex: + noweave -delay -autodefs icon -index $*.nw > $*.tex +.tex.dvi: + latex $*; while grep -s 'Rerun to get cross' $*.log; do latex $*; done + +all: $(EXECS) +source: $(SRCS) +touch: $(SRCS) + touch $(SRCS) + +boot: + touch $(SRCS) + +install: $(EXECS) + cp $(LIBEXECS) $(LIB) + cp $(BINEXECS) $(BIN) + cp $(LIBSPECIAL) $(LIB)/autodefs.c + +uninstall: + for i in $(LIBEXECS); do rm -f $(LIB)/$$i; done + for i in $(BINEXECS); do rm -f $(BIN)/$$i; done + rm -f $(LIB)/autodefs.c + +clean: + /bin/rm -f *.tex *.dvi *.aux *.log *.blg *.bbl *~ *.toc *.html *.u1 *.u2 + /bin/rm -f *.[ch] *.ps *.gz + /bin/rm -f $(EXECS) + /bin/rm -f sl2h + +clobber: clean + /bin/rm -f *.icn + +texdefs.icn: texdefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' texdefs.nw defns.nw $(CPIF) texdefs.icn +icondefs.icn: icondefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' icondefs.nw defns.nw $(CPIF) icondefs.icn +yaccdefs.icn: yaccdefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' yaccdefs.nw defns.nw $(CPIF) yaccdefs.icn +smldefs.icn: smldefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' smldefs.nw defns.nw $(CPIF) smldefs.icn +cdefs.icn: cdefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' cdefs.nw defns.nw $(CPIF) cdefs.icn +pascaldefs.icn: pascaldefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' pascaldefs.nw defns.nw $(CPIF) pascaldefs.icn +promeladefs.icn: promeladefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' promeladefs.nw defns.nw $(CPIF) promeladefs.icn +lrtldefs.icn: lrtldefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' lrtldefs.nw defns.nw $(CPIF) lrtldefs.icn +asdldefs.icn: asdldefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' asdldefs.nw defns.nw $(CPIF) asdldefs.icn +mmixdefs.icn: mmixdefs.nw defns.nw + notangle -L'#line %-1L "%F"%N' mmixdefs.nw defns.nw $(CPIF) mmixdefs.icn + +autodefs.tex: texdefs.icn + $(ICONT) -o autodefs.tex texdefs.icn + +autodefs.icon: icondefs.icn + $(ICONC) -o autodefs.icon icondefs.icn + +autodefs.yacc: yaccdefs.icn + $(ICONT) -o autodefs.yacc yaccdefs.icn + +autodefs.sml: smldefs.icn + $(ICONT) -o autodefs.sml smldefs.icn + +autodefs.cee: cdefs.icn + $(ICONT) -o autodefs.cee cdefs.icn + +autodefs.pascal: pascaldefs.icn + $(ICONT) -o autodefs.pascal pascaldefs.icn + +autodefs.promela: promeladefs.icn + $(ICONT) -o autodefs.promela promeladefs.icn + +autodefs.lrtl: lrtldefs.icn + $(ICONT) -o autodefs.lrtl lrtldefs.icn + +autodefs.asdl: asdldefs.icn + $(ICONT) -o autodefs.asdl asdldefs.icn + +autodefs.mmix: mmixdefs.icn + $(ICONT) -o autodefs.mmix mmixdefs.icn + +totex: totex.icn + $(ICONC) totex.icn + +totexnr: totexnr.icn + $(ICONT) totexnr.icn + +tohtml: tohtml.icn + $(ICONC) tohtml.icn + +docs2comments: docs2comments.icn + $(ICONT) docs2comments.icn # no longer compiles with iconc + +xchunks: xchunks.icn + $(ICONT) xchunks.icn + +l2h.icn: l2h.nw + notangle -L'#line %-1L "%F"%N' -R'l2h.icn' l2h.nw $(CPIF) l2h.icn +l2h: l2h.icn + $(ICONT) -o l2h l2h.icn # no longer compiles with iconc + +sl2h.icn: l2h.nw + notangle -L'#line %-1L "%F"%N' -R'sl2h.icn' l2h.nw $(CPIF) sl2h.icn +sl2h: sl2h.icn + $(ICONT) -o sl2h sl2h.icn # no longer compiles with iconc + +noindex: noindex.icn + $(ICONT) noindex.icn + +noidx: noidx.icn + $(ICONT) noidx.icn # no longer compiles with iconc + +elide: elide.icn + $(ICONT) elide.icn + +pipedocs: pipedocs.icn + $(ICONT) pipedocs.icn + +pdcached: pdcached.icn + $(ICONT) pdcached.icn + +disambiguate: disambiguate.icn + $(ICONT) disambiguate.icn + +htmltoc: htmltoc.icn + $(ICONT) htmltoc.icn diff --git a/web/noweb/src/icon/asdldefs.icn b/web/noweb/src/icon/asdldefs.icn new file mode 100644 index 0000000000..a1d2a88dbd --- /dev/null +++ b/web/noweb/src/icon/asdldefs.icn @@ -0,0 +1,85 @@ +#line 3 "asdldefs.nw" +procedure main(args) + go() +end +#line 7 "asdldefs.nw" +global idchars +procedure postpass(name, arg) + static kind + initial { idchars := &letters ++ &digits ++ '_.' + kind := "bogus" + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then (arg || "--") ? (tab(find("--") \ 1) ? { + if (optwhite(), any(&lcase), t := tab(many(idchars)), optwhite(), ="=") + then + undotted(t) + while tab(upto(idchars)) do + if (="imports", any(' \t')) then + (tab(upto(idchars)), tab(many(idchars))) + else if any(&ucase) then undotted(tab(many(idchars))) + else tab(many(idchars)) + }) + } + return +end + +procedure undotted(s) + return if upto('.', s) then &null else writedefn(s) +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end +#line 41 "asdldefs.nw" +procedure rcsinfo () + return "$Id: asdldefs.nw,v 1.15 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/asdldefs.nw b/web/noweb/src/icon/asdldefs.nw new file mode 100644 index 0000000000..b7ff2febc8 --- /dev/null +++ b/web/noweb/src/icon/asdldefs.nw @@ -0,0 +1,46 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\section{Finding definitions in $\lambda$-RTL} +<<*>>= +procedure main(args) + go() +end +<<*>>= +global idchars +procedure postpass(name, arg) + static kind + initial { idchars := &letters ++ &digits ++ '_.' + kind := "bogus" + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then (arg || "--") ? (tab(find("--") \ 1) ? { + if (optwhite(), any(&lcase), t := tab(many(idchars)), optwhite(), ="=") + then + undotted(t) + while tab(upto(idchars)) do + if (="imports", any(' \t')) then + (tab(upto(idchars)), tab(many(idchars))) + else if any(&ucase) then undotted(tab(many(idchars))) + else tab(many(idchars)) + }) + } + return +end + +procedure undotted(s) + return if upto('.', s) then &null else writedefn(s) +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end +<<*>>= +procedure rcsinfo () + return "$Id: asdldefs.nw,v 1.15 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/cdefs.icn b/web/noweb/src/icon/cdefs.icn new file mode 100644 index 0000000000..a74d46fc5d --- /dev/null +++ b/web/noweb/src/icon/cdefs.icn @@ -0,0 +1,180 @@ +#line 2 "cdefs.nw" +global decls_ok +procedure main(args) + decls_ok := !args == "-decl" + go() +end +#line 8 "cdefs.nw" +global typespecs, storespecs, typequals, otherspecs, structlike +procedure postpass(name, arg) + local tok, stat + static kind + initial { + kind := "bogus" + storespecs := set(["typedef", "extern", "static", "auto", "register"]) + typespecs := set(["void", "char", "float", "short", "signed", + "int", "double", "long", "unsigned"]) + typequals := set(["const", "volatile"]) + otherspecs := storespecs ++ typequals + structlike := set(["struct", "union", "enum"]) + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : arg ? { + if kind ~== "code" then return + if any(' \t') then return + if (="#define", tab(many(' \t'))) then return wd(c_ident_token()) + if find("dclproto") then return + if ="}" then { + if c_ident_token() == "while" then fail # don't get tripped by do { ... } while + +#line 61 "cdefs.nw" +while x := declarator() do { + wd(x, stat) + if c_token() == "," then &null else return +} +#line 31 "cdefs.nw" + } else { + specifier_count := 0 + stat := &null # haven't seen static yet + seenType := &null + while ( + (/decls_ok & c_ident_token() == "extern", return) | + (member(typespecs, c_ident_token()), seenType := 1) | # swallow type + (member(otherspecs, tok := c_ident_token()), + stat := look_for_static(stat, tok)) | # swallow a specifier + (member(structlike, c_ident_token()), # struct with optional tag + c_ident_token() | &null) | + (/seenType, any(&ucase, c_ident_token()), seenType := 1) + # assume a typedef'd type, skip + ) do + specifier_count +:= 1 +## write(&errout, "skipped ", specifier_count, " specifiers") + if f := scan_function() then + if /decls_ok & c_token() == (";"|",") then fail else { +## write(&errout, "function ", image(f), " followed by ", +## image(c_token()) | "<EOL>") + wd(f, stat) + return + } + if specifier_count > 0 then { +#line 61 "cdefs.nw" +while x := declarator() do { + wd(x, stat) + if c_token() == "," then &null else return +} +#line 54 "cdefs.nw" + } + } + } + } + return +end +#line 66 "cdefs.nw" +procedure wd(x, stat) + static reserved + initial reserved := set([ + "asm", "auto", "break", "case", "char", "const", "continue", + "default", "double", "do", "else", "enum", "extern", "float", "for", + "goto", "if", "int", "long", "register", "return", "short", + "signed", "sizeof", "static", "struct", "switch", "typedef", "union", + "unsigned", "void", "volatile", "while"]) + if member(reserved, x) then return + return writedefn(x, stat) +end + +procedure prepass(name, arg) + return +end + +procedure c_ident_token() + static alpha, alnum + initial { alpha := '_' ++ &letters; alnum := alpha ++ &digits } + tab(many(' \t')) + if any(alpha) then suspend tab(many(alnum)) +end + +procedure c_token() + static alpha, alnum + initial { alpha := '_' ++ &letters; alnum := alpha ++ &digits } + tab(many(' \t')) + if any(alpha) then suspend tab(many(alnum)) else suspend move(1) +end + +procedure scan_function() + (f := c_ident_token(), +#write(&errout, " try function ", image(f)), + tab(many(' \t')) | &null, + any('('), + *tab(bal()) > 0 | + (="(", *tab(bal(',', '[{(', ')}]')) > 0, =",", optwhite(), pos(0)), +#write(&errout, " caught function trailing ", image(&subject[&pos:0])), + suspend f + ) +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end + +procedure declarator() + while c_token() == "*" do + while member(typequals, c_token()) + (c_token() == "(", d := declarator(), c_token() == ")") | + (d := c_ident_token()) | + fail + while c_token() == ("[", "(") do + (tab(bal('])','[(',')]')), c_token()) + return d +end +#line 123 "cdefs.nw" +procedure look_for_static(stat, tok) + return (tok == "static") | stat +end +#line 127 "cdefs.nw" +procedure rcsinfo () + return "$Id: cdefs.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/cdefs.nw b/web/noweb/src/icon/cdefs.nw new file mode 100644 index 0000000000..186f47d3b3 --- /dev/null +++ b/web/noweb/src/icon/cdefs.nw @@ -0,0 +1,132 @@ +\section{Finding definitions in ANSI C} +<<*>>= +global decls_ok +procedure main(args) + decls_ok := !args == "-decl" + go() +end +<<*>>= +global typespecs, storespecs, typequals, otherspecs, structlike +procedure postpass(name, arg) + local tok, stat + static kind + initial { + kind := "bogus" + storespecs := set(["typedef", "extern", "static", "auto", "register"]) + typespecs := set(["void", "char", "float", "short", "signed", + "int", "double", "long", "unsigned"]) + typequals := set(["const", "volatile"]) + otherspecs := storespecs ++ typequals + structlike := set(["struct", "union", "enum"]) + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : arg ? { + if kind ~== "code" then return + if any(' \t') then return + if (="#define", tab(many(' \t'))) then return wd(c_ident_token()) + if find("dclproto") then return + if ="}" then { + if c_ident_token() == "while" then fail # don't get tripped by do { ... } while + <<eat declarators>> + } else { + specifier_count := 0 + stat := &null # haven't seen static yet + seenType := &null + while ( + (/decls_ok & c_ident_token() == "extern", return) | + (member(typespecs, c_ident_token()), seenType := 1) | # swallow type + (member(otherspecs, tok := c_ident_token()), + stat := look_for_static(stat, tok)) | # swallow a specifier + (member(structlike, c_ident_token()), # struct with optional tag + c_ident_token() | &null) | + (/seenType, any(&ucase, c_ident_token()), seenType := 1) + # assume a typedef'd type, skip + ) do + specifier_count +:= 1 +## write(&errout, "skipped ", specifier_count, " specifiers") + if f := scan_function() then + if /decls_ok & c_token() == (";"|",") then fail else { +## write(&errout, "function ", image(f), " followed by ", +## image(c_token()) | "<EOL>") + wd(f, stat) + return + } + if specifier_count > 0 then {<<eat declarators>>} + } + } + } + return +end +<<eat declarators>>= +while x := declarator() do { + wd(x, stat) + if c_token() == "," then &null else return +} +<<*>>= +procedure wd(x, stat) + static reserved + initial reserved := set([ + "asm", "auto", "break", "case", "char", "const", "continue", + "default", "double", "do", "else", "enum", "extern", "float", "for", + "goto", "if", "int", "long", "register", "return", "short", + "signed", "sizeof", "static", "struct", "switch", "typedef", "union", + "unsigned", "void", "volatile", "while"]) + if member(reserved, x) then return + return writedefn(x, stat) +end + +procedure prepass(name, arg) + return +end + +procedure c_ident_token() + static alpha, alnum + initial { alpha := '_' ++ &letters; alnum := alpha ++ &digits } + tab(many(' \t')) + if any(alpha) then suspend tab(many(alnum)) +end + +procedure c_token() + static alpha, alnum + initial { alpha := '_' ++ &letters; alnum := alpha ++ &digits } + tab(many(' \t')) + if any(alpha) then suspend tab(many(alnum)) else suspend move(1) +end + +procedure scan_function() + (f := c_ident_token(), +#write(&errout, " try function ", image(f)), + tab(many(' \t')) | &null, + any('('), + *tab(bal()) > 0 | + (="(", *tab(bal(',', '[{(', ')}]')) > 0, =",", optwhite(), pos(0)), +#write(&errout, " caught function trailing ", image(&subject[&pos:0])), + suspend f + ) +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end + +procedure declarator() + while c_token() == "*" do + while member(typequals, c_token()) + (c_token() == "(", d := declarator(), c_token() == ")") | + (d := c_ident_token()) | + fail + while c_token() == ("[", "(") do + (tab(bal('])','[(',')]')), c_token()) + return d +end +<<*>>= +procedure look_for_static(stat, tok) + return (tok == "static") | stat +end +<<*>>= +procedure rcsinfo () + return "$Id: cdefs.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/defns.nw b/web/noweb/src/icon/defns.nw new file mode 100644 index 0000000000..f168d25b29 --- /dev/null +++ b/web/noweb/src/icon/defns.nw @@ -0,0 +1,40 @@ +<<*>>= +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +@ +[[indextext]] is a hack to introduce suitable ``[[@index nl]],'' but it +messes up the line counts! +<<*>>= +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & <<flush index>> + else { + if *indextext + *defn > 65 then <<flush index>> + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +<<flush index>>= +{ # write("@index nl") # don't! + indextext := "" +} +<<*>>= +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/disambiguate.icn b/web/noweb/src/icon/disambiguate.icn new file mode 100644 index 0000000000..da547d37d6 --- /dev/null +++ b/web/noweb/src/icon/disambiguate.icn @@ -0,0 +1,67 @@ +#line 7 "disambiguate.nw" +global chunknames + +procedure remember(name) + initial chunknames := table() + if *name = 0 then fail + /chunknames[name[1]] := set() + insert(chunknames[name[1]], name) + return +end +#line 20 "disambiguate.nw" +procedure disambiguate(arg) + local s + if *arg = 0 then fail + search := chunknames[arg[1]] + if /search then fatal("Can't resolve ", arg, "...") + every match(arg, n := !search) do + if /s then s := n + else fatal("Ambiguous abbreviation: <<", arg, "...>> could denote\n\t<<", + s, ">> or\n\t<<", n, ">>") + if *s = 0 then fatal("Can't resolve ", arg, "...") + return s +end +#line 35 "disambiguate.nw" +global lines, defns, uses, names, firstdefnout + +procedure main(args) + lines := [] + while put(lines, line := read()) do + apply(pass1, line) + every apply(pass2, !lines) +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end + +procedure pass1(name, chunkname) + initial chunknames := set() + case name of { + "defn" | "use" : + if chunkname[0-:3] ~== "..." then remember(chunkname) + } + return +end + +procedure pass2(name, arg) + case name of { + "defn" | "use" : + if arg[0:-3] == "..." then arg := disambiguate(arg[1:-3]) + } + write("@", name, (" " || \arg) | "") + if name == "fatal" then exit(1) + return +end +#line 68 "disambiguate.nw" +procedure fatal(L[]) + write!(["@fatal disambiguate "] ||| L) + write!([&errout, "noweb error in disambiguate: "] ||| L) + exit(1) +end + +#line 75 "disambiguate.nw" +procedure rcsinfo () + return "$Id: disambiguate.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/disambiguate.nw b/web/noweb/src/icon/disambiguate.nw new file mode 100644 index 0000000000..8f23d515b1 --- /dev/null +++ b/web/noweb/src/icon/disambiguate.nw @@ -0,0 +1,80 @@ +\section{Disambiguating abbreviated chunk names} +This code disambiguates chunk names ending in [[...]]. +If used, it belongs right after [[markup]] in the pipeline. + +The first pass calls [[remember]] on each name {\em not} ending in dots. +For efficiency, I keep a table of sets indexed by the first character of the name. +<<*>>= +global chunknames + +procedure remember(name) + initial chunknames := table() + if *name = 0 then fail + /chunknames[name[1]] := set() + insert(chunknames[name[1]], name) + return +end +@ + +The second pass calls [[disambiguate]] on each name that {\em does} end in dots. +<<*>>= +procedure disambiguate(arg) + local s + if *arg = 0 then fail + search := chunknames[arg[1]] + if /search then fatal("Can't resolve ", arg, "...") + every match(arg, n := !search) do + if /s then s := n + else fatal("Ambiguous abbreviation: @<<", arg, "...@>> could denote\n\t@<<", + s, "@>> or\n\t@<<", n, "@>>") + if *s = 0 then fatal("Can't resolve ", arg, "...") + return s +end +@ +The +<<*>>= +global lines, defns, uses, names, firstdefnout + +procedure main(args) + lines := [] + while put(lines, line := read()) do + apply(pass1, line) + every apply(pass2, !lines) +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end + +procedure pass1(name, chunkname) + initial chunknames := set() + case name of { + "defn" | "use" : + if chunkname[0-:3] ~== "..." then remember(chunkname) + } + return +end + +procedure pass2(name, arg) + case name of { + "defn" | "use" : + if arg[0:-3] == "..." then arg := disambiguate(arg[1:-3]) + } + write("@", name, (" " || \arg) | "") + if name == "fatal" then exit(1) + return +end +@ +<<*>>= +procedure fatal(L[]) + write!(["@fatal disambiguate "] ||| L) + write!([&errout, "noweb error in disambiguate: "] ||| L) + exit(1) +end + +<<*>>= +procedure rcsinfo () + return "$Id: disambiguate.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/docs2comments.icn b/web/noweb/src/icon/docs2comments.icn new file mode 100644 index 0000000000..0f8e914378 --- /dev/null +++ b/web/noweb/src/icon/docs2comments.icn @@ -0,0 +1,180 @@ +#line 8 "docs2comments.nw" +global format, subst, firstline, lastline, allchunks, chunks, width +record wrap(l, r) # put fore and aft of line +record breakup(strings) # split up these strings with spaces +record tr(old, new) # translate old chars to new +global left_quote, right_quote + +procedure main(args) + format := wrap("/* ", " */") + subst := breakup(["/*","*/"]) + width := 80 + left_quote := "[[" + right_quote := "]]" + while a := get(args) do + case a of { + "-ml" | "-m3" : (format := wrap("(* ", " *)"), subst := breakup(["(*", "*)"])) + "-awk" | "-icn" | "-icon" : format := wrap("# ") + "-lisp" | "-scm" : format := wrap(";;; ") + "-c++" : format := wrap("// ") + "-c" : { format := wrap(" * "); firstline := "/*"; lastline := " */" + subst := breakup(["/*", "*/"]) + } + "-ocamlweb" : { format := wrap(" "); firstline := "(*"; lastline := " *)" + subst := breakup(["(*", "*)"]) + left_quote := "["; right_quote := "]" + } + "-pascal" : { format := wrap("{ ", " }"); subst := tr("{}", "--") } + "-f77" : format := wrap("C ") + "-f90" : format := wrap("! ") + "-tex" : format := wrap("%% ") + "-lua" : format := wrap("-- ") + + "-all" : allchunks := 1 + "-one" : allchunks := &null + default : + a ? + if (="-w", width <- integer(tab(many(&digits))), pos(0)) then + &null + else if ="-L" then + &null # deliberately ignore requests for #line + else { + write("@fatal unknown argument '", a, "' to docs2comments") + exit(1) + } + } + +#line 96 "docs2comments.nw" +chunks := [] +while line := read() do + line ? + if ="@end docs" | ="@end code" then { + if (\holding)[-1] == "" then pull (holding) + put(chunks, code(preceding_defn, \defn_pending, holding) | docs(holding)) +## if type(chunks[-1]) == "code" then +## every write("##### " || ( +## "preceding defn is " || image(preceding_defn) | +## "defn pending is " || image(defn_pending) | +## "Holding chunk with these lines:" | image(!holding))) + defn_pending := &null + holding := &null + chunks := drain(chunks) + } else if ="@begin docs" then { + indocs := 1 + holding := [""] + } else if ="@begin code" then { + indocs := &null + holding := [""] + } else if \indocs then { + if ="@text " then { + grab(tab(0), holding) + } else if ="@quote" & pos(0) then { + grab(left_quote, holding) + } else if ="@endquote" & pos(0) then { + grab(right_quote, holding) + } else if ="@nl" & pos(0) then { + put(\holding, "") + } + } else if ="@defn " then { + defn_pending := tab(0) + if holding[-1] == "" then pull (holding) + preceding_defn := holding + holding := [] + } else { + if match("@xref ") & (\holding)[-1] == "" then pull(holding) + put(\holding, line) + } +drain(put(chunks, docs([]))) +#line 53 "docs2comments.nw" + return +end +#line 57 "docs2comments.nw" +procedure insert_blanks(s, bad) + s ? { + r := "" + while r ||:= tab(find(bad)) do { + r ||:= bad[1] + every i := 2 to *bad do r ||:= " " || bad[i] + =bad + } + return r || tab(0) + } +end +#line 70 "docs2comments.nw" +procedure comment(s) + case type(subst) of { + "breakup" : every s := insert_blanks(s, !subst.strings) + "tr" : s := map(s, subst.old, subst.new) + "null" : &null + default : stop("unknown substitution of type ", type(subst)) + } + if /format.r then + return format.l || s + else { + if *s < width then s ||:= repl(" ", width - *s) + return format.l || s || format.r + } +end +#line 88 "docs2comments.nw" +procedure grab(s, holding) + return (\holding)[-1] := holding[-1] || s +end + +record docs(lines) +record code(preceding, defn, lines) + +#line 138 "docs2comments.nw" +procedure emit_as_comments(c) + type(c) == "docs" | runerr(1, "Unexpected code chunk") + every write("@text ", comment(trailing_nonblank(\c.lines)), "\n@nl") + return +end + +procedure trailing_nonblank(l) + local suspended + every s := !l do + if \suspended | *s > 0 then { + suspended := 1 + suspend s + } +end + + +procedure drain(chunks) + static n + initial n := -1 + if type(c := chunks[-2]) == "code" then { + write("@begin code ", n +:= 1) + every write(!c.preceding | ("@defn " || c.defn) | "@nl") + write("@text ", \firstline, "\n@nl") + every i := (if \allchunks then 1 else *chunks -2) to *chunks - 2 do + emit_as_comments(chunks[i]) + write("@text ", comment("<" || c.defn || ">="), "\n@nl") + write("@text ", \lastline, "\n@nl") + copyxreflines(\c.lines) + bad := &null + (bad := get(\c.lines)) == "@nl" | + { write(&errout, "expected newline after definition; got ", image(bad)); + runerr(1, bad) } + every write(!c.lines) + c := chunks[-1] + if type(c) == "docs" & upto(\c.lines[1], ~' \n\t') then { + write("@text ", \firstline, "\n@nl") + emit_as_comments(c) + write("@text ", \lastline, "\n@nl") + write("@nl") + chunks := [] + } else + chunks := [c] + write("@end code ", n) + } + return chunks +end + +procedure copyxreflines(l) + while match("@xref ", l[1]) do { + #write(&errout, "COPYING ", image(l[1])) + get(l) #write(get(l)) + } + return +end diff --git a/web/noweb/src/icon/docs2comments.nw b/web/noweb/src/icon/docs2comments.nw new file mode 100644 index 0000000000..1a6935e457 --- /dev/null +++ b/web/noweb/src/icon/docs2comments.nw @@ -0,0 +1,192 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- + +\section{Convert documentation chunks to comments} + +Options indicate what language and comment style is preferred, +plus whether to attach one or all nearby doc chunks to code. +One is the default. +<<*>>= +global format, subst, firstline, lastline, allchunks, chunks, width +record wrap(l, r) # put fore and aft of line +record breakup(strings) # split up these strings with spaces +record tr(old, new) # translate old chars to new +global left_quote, right_quote + +procedure main(args) + format := wrap("/* ", " */") + subst := breakup(["/*","*/"]) + width := 80 + left_quote := "[[" + right_quote := "]]" + while a := get(args) do + case a of { + "-ml" | "-m3" : (format := wrap("(* ", " *)"), subst := breakup(["(*", "*)"])) + "-awk" | "-icn" | "-icon" : format := wrap("# ") + "-lisp" | "-scm" : format := wrap(";;; ") + "-c++" : format := wrap("// ") + "-c" : { format := wrap(" * "); firstline := "/*"; lastline := " */" + subst := breakup(["/*", "*/"]) + } + "-ocamlweb" : { format := wrap(" "); firstline := "(*"; lastline := " *)" + subst := breakup(["(*", "*)"]) + left_quote := "["; right_quote := "]" + } + "-pascal" : { format := wrap("{ ", " }"); subst := tr("{}", "--") } + "-f77" : format := wrap("C ") + "-f90" : format := wrap("! ") + "-tex" : format := wrap("%% ") + "-lua" : format := wrap("-- ") + + "-all" : allchunks := 1 + "-one" : allchunks := &null + default : + a ? + if (="-w", width <- integer(tab(many(&digits))), pos(0)) then + &null + else if ="-L" then + &null # deliberately ignore requests for #line + else { + write("@fatal unknown argument '", a, "' to docs2comments") + exit(1) + } + } + <<transform [[&input]]>> + return +end +@ +<<*>>= +procedure insert_blanks(s, bad) + s ? { + r := "" + while r ||:= tab(find(bad)) do { + r ||:= bad[1] + every i := 2 to *bad do r ||:= " " || bad[i] + =bad + } + return r || tab(0) + } +end +@ +<<*>>= +procedure comment(s) + case type(subst) of { + "breakup" : every s := insert_blanks(s, !subst.strings) + "tr" : s := map(s, subst.old, subst.new) + "null" : &null + default : stop("unknown substitution of type ", type(subst)) + } + if /format.r then + return format.l || s + else { + if *s < width then s ||:= repl(" ", width - *s) + return format.l || s || format.r + } +end +@ +[[grab]] concatenates a string [[s]] onto the last line being held in +list [[holding]]. +<<*>>= +procedure grab(s, holding) + return (\holding)[-1] := holding[-1] || s +end + +record docs(lines) +record code(preceding, defn, lines) + +<<transform [[&input]]>>= +chunks := [] +while line := read() do + line ? + if ="@end docs" | ="@end code" then { + if (\holding)[-1] == "" then pull (holding) + put(chunks, code(preceding_defn, \defn_pending, holding) | docs(holding)) +## if type(chunks[-1]) == "code" then +## every write("##### " || ( +## "preceding defn is " || image(preceding_defn) | +## "defn pending is " || image(defn_pending) | +## "Holding chunk with these lines:" | image(!holding))) + defn_pending := &null + holding := &null + chunks := drain(chunks) + } else if ="@begin docs" then { + indocs := 1 + holding := [""] + } else if ="@begin code" then { + indocs := &null + holding := [""] + } else if \indocs then { + if ="@text " then { + grab(tab(0), holding) + } else if ="@quote" & pos(0) then { + grab(left_quote, holding) + } else if ="@endquote" & pos(0) then { + grab(right_quote, holding) + } else if ="@nl" & pos(0) then { + put(\holding, "") + } + } else if ="@defn " then { + defn_pending := tab(0) + if holding[-1] == "" then pull (holding) + preceding_defn := holding + holding := [] + } else { + if match("@xref ") & (\holding)[-1] == "" then pull(holding) + put(\holding, line) + } +drain(put(chunks, docs([]))) +@ +<<*>>= +procedure emit_as_comments(c) + type(c) == "docs" | runerr(1, "Unexpected code chunk") + every write("@text ", comment(trailing_nonblank(\c.lines)), "\n@nl") + return +end + +procedure trailing_nonblank(l) + local suspended + every s := !l do + if \suspended | *s > 0 then { + suspended := 1 + suspend s + } +end + + +procedure drain(chunks) + static n + initial n := -1 + if type(c := chunks[-2]) == "code" then { + write("@begin code ", n +:= 1) + every write(!c.preceding | ("@defn " || c.defn) | "@nl") + write("@text ", \firstline, "\n@nl") + every i := (if \allchunks then 1 else *chunks -2) to *chunks - 2 do + emit_as_comments(chunks[i]) + write("@text ", comment("<" || c.defn || ">="), "\n@nl") + write("@text ", \lastline, "\n@nl") + copyxreflines(\c.lines) + bad := &null + (bad := get(\c.lines)) == "@nl" | + { write(&errout, "expected newline after definition; got ", image(bad)); + runerr(1, bad) } + every write(!c.lines) + c := chunks[-1] + if type(c) == "docs" & upto(\c.lines[1], ~' \n\t') then { + write("@text ", \firstline, "\n@nl") + emit_as_comments(c) + write("@text ", \lastline, "\n@nl") + write("@nl") + chunks := [] + } else + chunks := [c] + write("@end code ", n) + } + return chunks +end + +procedure copyxreflines(l) + while match("@xref ", l[1]) do { + #write(&errout, "COPYING ", image(l[1])) + get(l) #write(get(l)) + } + return +end diff --git a/web/noweb/src/icon/elide.icn b/web/noweb/src/icon/elide.icn new file mode 100644 index 0000000000..0c5e809407 --- /dev/null +++ b/web/noweb/src/icon/elide.icn @@ -0,0 +1,71 @@ +#line 17 "elide.nw" +procedure main(args) + elide := [] + every put(elide, globpat(!args)) + while line := read() do + if match("@begin code ", line) then + { +#line 35 "elide.nw" +accum := [line] +while line := read() & not match("@defn ", line) do + put(accum, line) +line ? { + ="@defn " | fatal("This can't happen") + if globmatch(!elide) then { + while line := get(accum) | read() & not match("@end code ", line) do # drain + if line == "@nl" then write("@index nl") # grotesque, but may keep lines right + } else + every write(!accum | line) +} +#line 22 "elide.nw" + } + else { + write(line) + if match("@fatal ", line) then exit(1) + } + return +end +#line 61 "elide.nw" +procedure globpat(s) + static specials + initial specials := '?*\\' + l := [] + s ? + while not pos(0) do + put(l, + if not any(specials) then + tab(upto(specials)|0) + else case move(1) of { + "?" : &cset + "*" : &null + "\\" : move(1) + }) + return l +end +#line 82 "elide.nw" +procedure doglobmatch(pat, i) + local p + suspend if p := pat[i] then + case type(p) of { + "string" : =p + "cset" : tab(any(p)) + "null" : tab(&pos to *&subject+1) + } || doglobmatch(pat, i+1) + else (pos(0), "") +end +#line 96 "elide.nw" +procedure globmatch(pattern, s) + return if \s then s ? doglobmatch(pattern, 1) + else doglobmatch(pattern, 1) +end +#line 101 "elide.nw" +procedure fatal(L[]) + write!(["@fatal elide "] ||| L) + write!([&errout, "noweb error in elide: "] ||| L) + exit(1) +end +#line 107 "elide.nw" +procedure rcsinfo () + return "$Id: elide.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/elide.nw b/web/noweb/src/icon/elide.nw new file mode 100644 index 0000000000..f135274447 --- /dev/null +++ b/web/noweb/src/icon/elide.nw @@ -0,0 +1,112 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\section{Eliding unwanted code chunks} + +This filter removes from the [[markup]] token stream any code chunk + whose name matches any of the ``globbing'' patterns given it as + arguments. These are the familiar file name globbing patterns. For + example: [[elide 'RefMan_Ch*']] will prevent weaving a chunk + defined as ``\LA{}RefMan\_Chapter\_14\RA{}''. + +You could also use this filter when tangling, but it's not clear why you'd want to. + +@ +\subsection{Eliding} +The main loop simply copies the input to the output. +We stop only if we see [[@begin code]], because that means we might +need to elide. +<<*>>= +procedure main(args) + elide := [] + every put(elide, globpat(!args)) + while line := read() do + if match("@begin code ", line) then + {<<see about eliding this chunk>>} + else { + write(line) + if match("@fatal ", line) then exit(1) + } + return +end +@ +To see about eliding, we accumulate lines until we see [[@defn]]. +At that point we can either keep the chunk, which means printing the +accumulated lines and returning to the main loop, or elide it, which +means draining the input until the next [[@end code]]. +<<see about eliding this chunk>>= +accum := [line] +while line := read() & not match("@defn ", line) do + put(accum, line) +line ? { + ="@defn " | fatal("This can't happen") + if globmatch(!elide) then { + while line := get(accum) | read() & not match("@end code ", line) do # drain + if line == "@nl" then write("@index nl") # grotesque, but may keep lines right + } else + every write(!accum | line) +} +@ +\subsection{Globbing} + +I do globbing in two stages: first I turn the globbing string into +a pattern. A pattern is a list of the following kinds of data items: +\begin{quote} +\begin{tabular}{ll} +string&match that string\\ +cset&match one character in that cset\\ +nil&match anything\\ +\end{tabular} +\end{quote} +[[globpat]] turns a globbing string into a pattern. +It doesn't handle the {\tt [} metacharacter, but it might one day. +<<*>>= +procedure globpat(s) + static specials + initial specials := '?*\\' + l := [] + s ? + while not pos(0) do + put(l, + if not any(specials) then + tab(upto(specials)|0) + else case move(1) of { + "?" : &cset + "*" : &null + "\\" : move(1) + }) + return l +end +@ +Giving a globbing pattern [[pat]] and an index [[i]], [[doglobmatch]] +returns the string (if any) matched by [[pat[i:0]]]. +[[doglobmatch]] scans the current subject. +<<*>>= +procedure doglobmatch(pat, i) + local p + suspend if p := pat[i] then + case type(p) of { + "string" : =p + "cset" : tab(any(p)) + "null" : tab(&pos to *&subject+1) + } || doglobmatch(pat, i+1) + else (pos(0), "") +end +@ +[[globmatch(p)]] matches the pattern [[p]] against the current +subject, or a subject can be supplied as the second argument. +<<*>>= +procedure globmatch(pattern, s) + return if \s then s ? doglobmatch(pattern, 1) + else doglobmatch(pattern, 1) +end +<<*>>= +procedure fatal(L[]) + write!(["@fatal elide "] ||| L) + write!([&errout, "noweb error in elide: "] ||| L) + exit(1) +end +<<*>>= +procedure rcsinfo () + return "$Id: elide.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/htmltoc.icn b/web/noweb/src/icon/htmltoc.icn new file mode 100644 index 0000000000..fd29051070 --- /dev/null +++ b/web/noweb/src/icon/htmltoc.icn @@ -0,0 +1,218 @@ +#line 37 "htmltoc.nw" +$define sentinel "<tableofcontents>" +$define sentinel_end "</tableofcontents>" +#line 44 "htmltoc.nw" +record ioc(number, level, text) +#line 46 "htmltoc.nw" +global contents +#line 52 "htmltoc.nw" +procedure readfile(f) + local old_contents + intoc := &null + contents := [] + old_contents := [] + no := 0 + + file := list() + while l := read(f) do l ? { +#line 71 "htmltoc.nw" +if =sentinel then { + intoc := "in table of contents" + put(file, l) +} else if =sentinel_end then { + intoc := &null + put(file, l) +} else if \intoc then { + put(old_contents, l) +} else if /intoc then { + if +#line 88 "htmltoc.nw" +(pre := tab(upto('<')), ="<", skip(), tab(any('hH')), n := tab(many(&digits)), + skip(), =">") +#line 80 "htmltoc.nw" + then { + +#line 102 "htmltoc.nw" +if (c := tab(upto('<')), ="</", skip(), tab(any('hH')), =n, skip(), =">") then { + no +:= 1 + c ? c := strip_toc_anchors() || tab(0) + addcontents(no, n, c) + +#line 112 "htmltoc.nw" +put(file, pre || "<h" || n || "><a name=toc" || no || ">" || c || + "</a></h" || n || ">" || tab(0)) +#line 107 "htmltoc.nw" +} else + write(&errout, "Unterminated <h", n, ">", tab(0), "...") +#line 82 "htmltoc.nw" + } else + put(file, l) +} +#line 60 "htmltoc.nw" + } + if \intoc then { + push(old_contents, sentinel_end) + every l := !copy(old_contents) do l ? { +#line 71 "htmltoc.nw" +if =sentinel then { + intoc := "in table of contents" + put(file, l) +} else if =sentinel_end then { + intoc := &null + put(file, l) +} else if \intoc then { + put(old_contents, l) +} else if /intoc then { + if +#line 88 "htmltoc.nw" +(pre := tab(upto('<')), ="<", skip(), tab(any('hH')), n := tab(many(&digits)), + skip(), =">") +#line 80 "htmltoc.nw" + then { + +#line 102 "htmltoc.nw" +if (c := tab(upto('<')), ="</", skip(), tab(any('hH')), =n, skip(), =">") then { + no +:= 1 + c ? c := strip_toc_anchors() || tab(0) + addcontents(no, n, c) + +#line 112 "htmltoc.nw" +put(file, pre || "<h" || n || "><a name=toc" || no || ">" || c || + "</a></h" || n || ">" || tab(0)) +#line 107 "htmltoc.nw" +} else + write(&errout, "Unterminated <h", n, ">", tab(0), "...") +#line 82 "htmltoc.nw" + } else + put(file, l) +} +#line 63 "htmltoc.nw" + } + } + + return file +end +#line 92 "htmltoc.nw" +procedure skip() + suspend tab(many(' \t')) | "" +end +#line 117 "htmltoc.nw" +procedure strip_toc_anchors() + local s, p + if (="<a name=toc", tab(many(&digits)), =">", s := strip_toc_anchors(), ="</a>") then + return s + else { + s := to_next_anchor_or_end_anchor() | runerr(1, "never found an anchor") + if pos(0) | at_end_anchor() then + return s + else + return s || tab_past_anchor_start() || strip_toc_anchors() || + tab_past_anchor_end() + } +end +#line 132 "htmltoc.nw" +procedure at_end_anchor() + &subject[&pos:0] ? + return tab_past_anchor_end() +end + +procedure at_start_anchor() + &subject[&pos:0] ? + return tab_past_anchor_start() +end + +procedure tab_past_anchor_end() + suspend ="<" || optwhite() || ="/" || optwhite() || =("a"|"A") || optwhite() || =">" +end + +procedure tab_past_anchor_start() + suspend ="<" || optwhite() || =("a"|"A") || white() || tab(upto('>')) || =">" +end + +procedure to_next_anchor_or_end_anchor() + return (1(tab(upto('<')), at_start_anchor() | at_end_anchor()) | tab(0)) \ 1 +end + +procedure white() + suspend tab(many(' \t')) +end +procedure optwhite() + suspend white() | "" +end +#line 171 "htmltoc.nw" +procedure addcontents(no, n, s) + if map(s) ~== "contents" & find(n, toclevels) then + put(contents, ioc(no, n, strip_tags(s))) + return +end + +procedure strip_tags(s) + s ? { + r := "" + while r ||:= tab(upto('<')) do (tab(upto('>')), =">") + return r || tab(0) + } +end +#line 191 "htmltoc.nw" +global minlevel + +procedure tocindent(level) + return repl(" ", level-minlevel) +end + +procedure writetoc() + local level + minlevel := 99 + every minlevel >:= (!contents).level + level := minlevel - 1 # triggers opening list + + every l := !contents do { + +#line 215 "htmltoc.nw" +while level > l.level do { + write(tocindent(level), "</ul>") + level -:= 1 +} +while level < l.level do { + level +:= 1 + write(tocindent(level), "<ul compact>") +} +#line 205 "htmltoc.nw" + write(tocindent(level), + "<li><a href=\"#toc", l.number, "\">", l.text, "</a></li>") + } + +#line 226 "htmltoc.nw" +while level >= minlevel do { + write(tocindent(level), "</ul>") + level -:= 1 +} +#line 209 "htmltoc.nw" +end +#line 235 "htmltoc.nw" +procedure add_contents(f) + file := readfile(f) + + every l := !file do { + write(l) + if match(sentinel, l) then writetoc() + } +end +#line 246 "htmltoc.nw" +global toclevels +procedure main(args) + if args[1] ? (="-", toclevels := tab(many(&digits)), pos(0)) then get(args) + else toclevels := "2345" + if *args = 0 then add_contents(&input) + else every add_contents(openfile(!args)) + return +end + +procedure openfile(f) + return open(f) | stop("Cannot open file ", f) +end +#line 259 "htmltoc.nw" +procedure rcsinfo () + return "$Id: htmltoc.nw,v 1.20 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/htmltoc.nw b/web/noweb/src/icon/htmltoc.nw new file mode 100644 index 0000000000..cf80e3ffc5 --- /dev/null +++ b/web/noweb/src/icon/htmltoc.nw @@ -0,0 +1,264 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\title{htmltoc -- A Table of Contents Generater for HTML} +\author{stephen parker\thanks{Revisions by Norman Ramsey}\\ + \texttt{stephen@stephen.uk.eu.org}} + +\tableofcontents + +\section{Introduction} +\texttt{htmltoc} reads an HTML document on standard input and writes the +same document, but optionally including a table of contents on output. +The motivation for this program was Norman Ramsey's \texttt{htmltoc} +program included with the \texttt{noweb} distribution which this program +has no advantages over other than not being written in perl. + +% noweave mangles the references in rawhtml blocks if you're not +% careful. don't ask me why. +\begin{rawhtml} +This html document is created from a noweb +<a href="http://lila.york.ac.uk/htmltoc/htmltoc.nw">source</a> +document which also produces the notangled +<a href="http://lila.york.ac.uk/htmltoc/htmltoc.icn">icon source</a>. +The <a href="http://lila.york.ac.uk/htmltoc/mkfile">mkfile</a> to control +this is also available; if you haven't got mk then it shouldn't be too +hard to turn it into a Makefile. +\end{rawhtml} + +\section{Implementation} +The Icon source follows the usual pattern. +<<*>>= +<<Preprocessor constants>> +<<Data structures>> +<<Global Variables>> +<<Procedures>> +@ Any text between the marks [["<tableofcontents>"]] and +[["</tableofcontents>"]] is replaced on output with a table +of contents for the document. +<<Preprocessor constants>>= +$define sentinel "<tableofcontents>" +$define sentinel_end "</tableofcontents>" +@ The TOC is created from the header items in the text. These +are stored in an [[ioc]] record. The headers are sequentially +numbered and the level of the heading and the heading text are +stored in a list of [[ioc]]s named [[contents]]. +<<Data structures>>= +record ioc(number, level, text) +<<Global Variables>>= +global contents +@ HTMLtoc must make two passes through the file in order to +generate a TOC before the actual material. Since we'd like +HTMLtoc to run in a pipeline we actually store the entire +input in a list named [[file]]. +<<Procedures>>= +procedure readfile(f) + local old_contents + intoc := &null + contents := [] + old_contents := [] + no := 0 + + file := list() + while l := read(f) do l ? { <<handle line>> } + if \intoc then { + push(old_contents, sentinel_end) + every l := !copy(old_contents) do l ? { <<handle line>> } + } + + return file +end +@ The variable [[intoc]] is [[1]] if we are between the TOC markers +on input. [[no]] is incremented for each header read. +<<handle line>>= +if =sentinel then { + intoc := "in table of contents" + put(file, l) +} else if =sentinel_end then { + intoc := &null + put(file, l) +} else if \intoc then { + put(old_contents, l) +} else if /intoc then { + if <<[[l]] includes a header>> then { + <<add header line to [[contents]] and [[file]]>> + } else + put(file, l) +} +@ We look for a string of the form \verb|<[Hh][0-9]>| which +signifies the beginning of a header. This isn't nice Icon. +<<[[l]] includes a header>>= +(pre := tab(upto('<')), ="<", skip(), tab(any('hH')), n := tab(many(&digits)), + skip(), =">") +@ [[skip]] skips white space, if present. +<<Procedures>>= +procedure skip() + suspend tab(many(' \t')) | "" +end +@ The scanning environment contains a header, the +\verb|<[Hh][0-9]>| has already been consumed, so +the text of the header can by found by scanning for the close of header. +The header number, +[[no]] is incremented and then [[addcontents()]] is +called to add the item to the [[contents]] list. +<<add header line to [[contents]] and [[file]]>>= +if (c := tab(upto('<')), ="</", skip(), tab(any('hH')), =n, skip(), =">") then { + no +:= 1 + c ? c := strip_toc_anchors() || tab(0) + addcontents(no, n, c) + <<append modified line to [[file]]>> +} else + write(&errout, "Unterminated <h", n, ">", tab(0), "...") +@ We also need to label the heading in the text, so +we can reference it from the TOC. +<<append modified line to [[file]]>>= +put(file, pre || "<h" || n || "><a name=toc" || no || ">" || c || + "</a></h" || n || ">" || tab(0)) +@ When stripping these, we rely on their having been put there by this +program. +<<Procedures>>= +procedure strip_toc_anchors() + local s, p + if (="<a name=toc", tab(many(&digits)), =">", s := strip_toc_anchors(), ="</a>") then + return s + else { + s := to_next_anchor_or_end_anchor() | runerr(1, "never found an anchor") + if pos(0) | at_end_anchor() then + return s + else + return s || tab_past_anchor_start() || strip_toc_anchors() || + tab_past_anchor_end() + } +end +@ +<<Procedures>>= +procedure at_end_anchor() + &subject[&pos:0] ? + return tab_past_anchor_end() +end + +procedure at_start_anchor() + &subject[&pos:0] ? + return tab_past_anchor_start() +end + +procedure tab_past_anchor_end() + suspend ="<" || optwhite() || ="/" || optwhite() || =("a"|"A") || optwhite() || =">" +end + +procedure tab_past_anchor_start() + suspend ="<" || optwhite() || =("a"|"A") || white() || tab(upto('>')) || =">" +end + +procedure to_next_anchor_or_end_anchor() + return (1(tab(upto('<')), at_start_anchor() | at_end_anchor()) | tab(0)) \ 1 +end + +procedure white() + suspend tab(many(' \t')) +end +procedure optwhite() + suspend white() | "" +end +@ [[addcontents()]] should simply append the [[ioc]] +record for the header to the [[contents]] list. But +one problem is that the text in the header may not +be simple -- it might contain markup. One case where +we don't want to copy this markup is if it is +a \verb|<a name="|... token, since then there would be +multiple targets for this name. Any markup of the +form \verb|<| ... \verb|>| is stripped from the header -- +this harsh rule may be relaxed in future. +We also avoid adding the line ``Contents'' to the contents. +<<Procedures>>= +procedure addcontents(no, n, s) + if map(s) ~== "contents" & find(n, toclevels) then + put(contents, ioc(no, n, strip_tags(s))) + return +end + +procedure strip_tags(s) + s ? { + r := "" + while r ||:= tab(upto('<')) do (tab(upto('>')), =">") + return r || tab(0) + } +end +@ +The procedure [[writetoc()]] writes the TOC on +output using the [[contents]] list. We keep a current +header level in the integer variable [[level]]. As +we iterate through the [[contents]] list we insert \verb|<ul compact>|s and +\verb|</ul>|s as required for formatting. +<<Procedures>>= +global minlevel + +procedure tocindent(level) + return repl(" ", level-minlevel) +end + +procedure writetoc() + local level + minlevel := 99 + every minlevel >:= (!contents).level + level := minlevel - 1 # triggers opening list + + every l := !contents do { + <<adjust [[level]] to [[l.level]]>> + write(tocindent(level), + "<li><a href=\"#toc", l.number, "\">", l.text, "</a></li>") + } + <<close any lists>> +end +@ We write out a \verb|</ul>| for each level we are beyond the +current item, and a \verb|<ul compact>| for each level we are below +the current. The call to [[repl()]] merely writes the correct +number of tabs so as to make the contents look ok. +<<adjust [[level]] to [[l.level]]>>= +while level > l.level do { + write(tocindent(level), "</ul>") + level -:= 1 +} +while level < l.level do { + level +:= 1 + write(tocindent(level), "<ul compact>") +} +@ At the end of the contents we need to write as many +\verb|</ul>|s as are required. +<<close any lists>>= +while level >= minlevel do { + write(tocindent(level), "</ul>") + level -:= 1 +} +@ The [[add_contents()]] procedure simply reads the input using +[[readfile()]] then writes it out again. If the +sentinel line is found then the TOC is written at +that point. +<<Procedures>>= +procedure add_contents(f) + file := readfile(f) + + every l := !file do { + write(l) + if match(sentinel, l) then writetoc() + } +end +@ +[[main]] makes the program read its arguments or act as a filter. +<<Procedures>>= +global toclevels +procedure main(args) + if args[1] ? (="-", toclevels := tab(many(&digits)), pos(0)) then get(args) + else toclevels := "2345" + if *args = 0 then add_contents(&input) + else every add_contents(openfile(!args)) + return +end + +procedure openfile(f) + return open(f) | stop("Cannot open file ", f) +end +<<*>>= +procedure rcsinfo () + return "$Id: htmltoc.nw,v 1.20 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/icondefs.icn b/web/noweb/src/icon/icondefs.icn new file mode 100644 index 0000000000..2a36826fb6 --- /dev/null +++ b/web/noweb/src/icon/icondefs.icn @@ -0,0 +1,77 @@ +#line 2 "icondefs.nw" +global showlocal +procedure main(args) + showlocal := !args == "-local" + go() +end +#line 8 "icondefs.nw" +procedure postpass(name, arg) + static kind, definers + initial { kind := "bogus" + definers := ["global"] + if \showlocal then every put(definers, "local" | "static") + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if =("procedure "|"record ") then { + tab(many(' ')) + writedefn(tab(upto(' ()'))) + } else if (tab(many(' ')) | "", =!definers, tab(many(' '))) then { + while (not any('#')) & writedefn(tab(upto(', #'))) do tab(many(', ')) + if not any('#') then writedefn(tab(0)) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end +#line 32 "icondefs.nw" +procedure rcsinfo () + return "$Id: icondefs.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/icondefs.nw b/web/noweb/src/icon/icondefs.nw new file mode 100644 index 0000000000..f843011489 --- /dev/null +++ b/web/noweb/src/icon/icondefs.nw @@ -0,0 +1,37 @@ +\section{Finding Icon definitions} +<<*>>= +global showlocal +procedure main(args) + showlocal := !args == "-local" + go() +end +<<*>>= +procedure postpass(name, arg) + static kind, definers + initial { kind := "bogus" + definers := ["global"] + if \showlocal then every put(definers, "local" | "static") + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if =("procedure "|"record ") then { + tab(many(' ')) + writedefn(tab(upto(' ()'))) + } else if (tab(many(' ')) | "", =!definers, tab(many(' '))) then { + while (not any('#')) & writedefn(tab(upto(', #'))) do tab(many(', ')) + if not any('#') then writedefn(tab(0)) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end +<<*>>= +procedure rcsinfo () + return "$Id: icondefs.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/l2h.icn b/web/noweb/src/icon/l2h.icn new file mode 100644 index 0000000000..46f3143733 --- /dev/null +++ b/web/noweb/src/icon/l2h.icn @@ -0,0 +1,2237 @@ +#line 217 "l2h.nw" +procedure star(cs) + suspend cs | (cs || "*") +end +#line 596 "l2h.nw" +procedure asciiCharCode(S) + return emit_text(S, char(2(optwhite(), TeXnumber(), optwhite()))) +end +procedure TeXnumber() + return integer( + (="'", "8r" || tab(many(&digits))) | + (="\"", "16r" || tab(many(&digits))) | + tab(many(&digits))) +end +#line 671 "l2h.nw" +procedure convert(S, optstring) + static specials + initial { + +#line 819 "l2h.nw" +activetab := table(unknown_cs) +activeclosure := table() +#line 969 "l2h.nw" +cstab := table(unknown_cs) +csclosure := table() +#line 987 "l2h.nw" +unknown_set := set() +#line 1015 "l2h.nw" +every begintab | endtab := table(unknown_env) +every begincl | endcl := table() +#line 1028 "l2h.nw" +unknown_envs := set() +#line 1381 "l2h.nw" +liststack := [] +# write(&errout, "Initialized liststack") +suspendstack := [] +#line 1961 "l2h.nw" +countertab := table() +#line 1969 "l2h.nw" +accent_name := table() +accent_valid := table('') +accent_name ["`"] := "grave" +accent_valid["`"] := 'AEIOUaeiou' +accent_name ["'"] := "acute" +accent_valid["'"] := 'AEIOUYaeiouy' +accent_name ["^"] := "circ" +accent_valid["^"] := 'AEIOUaeiou' +accent_name ["hat"] := "circ" +accent_valid["hat"] := 'AEIOUaeiou' +accent_name ["\""] := "uml" +accent_valid["\""] := 'AEIOUaeiouy' +accent_name ["~"] := "tilde" +accent_valid["~"] := 'ANOano' +accent_name ["="] := "bar" +accent_name ["."] := "dot" +accent_name ["u"] := "u" +accent_name ["v"] := "v" +accent_name ["H"] := "H" +accent_name ["t"] := "t" +accent_name ["c"] := "cedil" +accent_valid["c"] := 'Cc' +accent_name ["d"] := "underdot" +accent_name ["b"] := "underbar" +#line 2675 "l2h.nw" +csfunctions := table() +#line 675 "l2h.nw" + +#line 114 "l2h.nw" +substitution(",", " ") +substitution(" ", " ") +substitution("\n", "\n") +substitution("\t", " ") +ignore("raggedright") +ignore("@") +ignore("/") # no italic correction +substitution("", "\n") # \<newline> treated as request for newline +every c := !"$%#{}_" do + substitution(c, c) +substitution("&", "&") +argblock("emph", "em") +every argblock("footnote" | "footnotetext" | "thanks", + " <b>[</b>", "<b>]</b> ") # put footnotes in bold brackets +substitution("today", &date) +#line 132 "l2h.nw" +ignore("textrm") # html can't switch to default font! +argblock("textit", "i") +argblock("textbf", "b") +argblock("textsl", "i") +ignore("textsc") +argblock("texttt", "tt") +ignore("textsf") +ignore("boldmath") +# \boldmath could be done by introducing S.mathfont, but I don't want to! +#line 142 "l2h.nw" +ignore("mathrm") # html can't switch to default font! +argblock("mathit", "i") +argblock("mathbf", "b") +argblock("mathtt", "tt") +ignore("mathsf") +argblock("mathcal", "i") +every ignore("scriptstyle"|"displaystyle"|"scriptscriptstyle") +every ignore("mathord"|"mathbin"|"mathrel") +#line 152 "l2h.nw" +every ignore("tiny" | "scriptsize" | "footnotesize" | "small" | "normalsize" | + "large" | "Large" | "LARGE" | "huge" | "Huge") +#line 160 "l2h.nw" +every accent(key(accent_name)) +every ignore("dag" | "ddag") +substitution("S", "§") +substitution("P", "¶") +substitution("copyright", "©") +substitution("pounds", "£") +substitution("o", "ø") +substitution("O", "Ø") +substitution("aa", "å") +substitution("AA", "Å") +substitution("ae", "æ") +substitution("AE", "&Aelig;") +#line 175 "l2h.nw" +argblockv("part", "h1", &null, "*[") +argblockv("chapter", "h1", &null, "*[") +argblockv("section", "h2", &null, "*[") +argblockv("subsection", "h3", &null, "*[") +argblockv("subsubsection", "h4", &null, "*[") +argblockv("paragraph", "h5", &null, "*[") +argblockv("subparagraph", "h6", &null, "*[") +ignore("appendix") +auxfile("tableofcontents", "toc", + "<p>\n<tableofcontents>\n<b>[Table of contents]</b>\n</tableofcontents>\n<p>", + "<h2>Table of Contents</h2>\n<tableofcontents>\n", "\n</tableofcontents>\n") +cstab["tableofcontents"] := Ctableofcontents # override to call set_toclevel +ignore("listoftables") +ignore("listoffigures") +ignore("addtocontents", "{{") +ignoreenv("filecontents", "{") +#line 198 "l2h.nw" +cstab["("] := Cmath +cstab[")"] := Cmath_end +cstab["["] := Cdisplaymath +cstab["]"] := Cdisplaymath_end +ignoreenv("equation") +every table_env(star("eqnarray"), 0, " ", "<blockquote><i>", "</i></blockquote>") + # also lame +substitution("frac", "<b>frac</b>") +substitution("sqrt", "<b>frac</b>") +every substitution("ldots" | "cdots" | "vdots", "...") +ignore("left") +ignore("right") +ignore("overline") +substitution(":", " ") +substitution(";", " ") +ignore("!") +#line 223 "l2h.nw" +substitution("Diamond", "<>") +substitution("langle", "<") +substitution("rangle", ">") +substitution("le", "<=") +substitution("ne", "!=") +substitution("ge", ">=") +substitution("times", "×") +substitution("divide", "÷") +substitution("bmod", "</i>mod<i>") # better hook in with math +substitution("equiv", "===") +every x := "arccos" | "arcsin" | "arctan" | "arg" | "cos" | "cosh" | + "cot" | "coth" | "csc" | "deg" | "det" | "dim" | "exp" | + "gcd" | "hom" | "lim" | "liminf" | "limsup" | "ln" | "log" | + "max" | "min" | "sec" | "sin" | "sinh" | "sup" | "tan" | "tanh" +do + substitution(x, "</i>" || x || "<i>") +substitution("liminf", "</i>lim inf<i>") +substitution("limsup", "</i>lim sup<i>") +every x := "alpha" | "beta" | "gamma" | "delta" | "epsilon" | "zeta" | "eta" | + "theta" | "iota" | "kappa" | "lambda" | "nu" | "xi" | "pi" | + "rho" | "sigma" | "tau" | "upsilon" | "phi" | "chi" | "psi" | "omega" | + "in" +do + substitution(x, "<b>" || x || "</b>") +substitution("mu", "µ") +every x := "Gamma" | "Delta" | "Theta" | "Lambda" | "Xi" | "Pi" | "Sigma" | + "Upsilon" | "Phi" | "Psi" | "Omega" +do + substitution(x, "<b>" || x || "</b>") +#line 255 "l2h.nw" +substitution("lfloor", "</i>|_<i>") +substitution("rfloor", "</i>_|<i>") +substitution("leq", "<=") +substitution("geq", ">=") +substitution("ll", "«") +substitution("gg", "»") +substitution("Rightarrow", "==>") +substitution("rightarrow", "-->") +substitution("approx", "<u>~</u>") +#line 268 "l2h.nw" +envblock("quote", "blockquote") +envblock("quotation", "blockquote") +envblock("center", "blockquote") +envblock("flushleft", "blockquote") +envblock("flushright", "blockquote") +envblock("verse", "blockquote") +verbatim("verbatim", escape_HTML_specials, "pre") +cstab["verb"] := Cverb +cstab["verb*"] := Cverb +csclosure["verb*"] := 1 +cstab["verbatiminput"] := Cverbatiminput +csclosure["verbatiminput"] := verbatim_cl(escape_HTML_specials) +#line 284 "l2h.nw" +cstab["item"] := Citem +csclosure["item"] := [item_cl("<li>", "", "<li>")] +listenv("itemize", "ul") +listenv("enumerate", "ol") +listenv("description", "dl") +#line 292 "l2h.nw" +ignore("documentstyle", "[{") +ignore("documentclass", "[{") +ignore("usepackage", "[{") +ignore("pagestyle", "{") +ignore("thispagestyle", "{") +ignore("pagenumbering", "{") +ignore("newcounter", "{") +ignore("global") +ignore("etalchar") # used in the .bbl files: \newcommand{\etalchar}[1]{$^{#1}$} +#line 307 "l2h.nw" +argblockv("title", "h1") +argblockv("author","address") +argblockv("date", "b") +substitution("maketitle", "<!--title goes here-->") +ignoreenv("titlepage") +envblock("abstract", "<h2>Abstract</h2><blockquote>", "</blockquote>") +#line 322 "l2h.nw" +cstab["label"] := Clabel +cstab["ref"] := Cref +cstab["pageref"] := Cref +cstab["subpageref"] := Cref +cstab["chunklabel"] := Clabel +#line 332 "l2h.nw" +ignore("bibliographystyle", "{") +auxfile("bibliography", "bbl", "<b>[BibTeX bibliography]</b>", &null, &null, "{") +envblock("thebibliography", "<h2>References</h2>", "", "{") +every cstab["cite" | "citeN" | "opencite" | "openciteN" | "citeyear"] := Ccite +cstab["bibitem"] := Cbibitem +ignore("newblock") +ignore("nocite", "{") +#line 343 "l2h.nw" +every ignore("input" | "include" | "includeonly", "{") +# filecontents not done yet +#line 348 "l2h.nw" +cstab["\\"] := Cbackback +substitution("linebreak", "<br>") +ignore("-") +ignoreenv("sloppypar") +ignore("sloppy") +#line 357 "l2h.nw" +substitution("pagebreak", "<hr>") +substitution("newpage", "<hr>") +substitution("clearpage", "<hr>") +ignore("enlargethispage", "*{") +#line 364 "l2h.nw" +ignore("mbox") +ignore("makebox", "([[") # ( comes from picture area +ignore("fbox") +ignore("framebox", "[[") # could insert horizontal rules, but why? +ignore("newsavebox", 1) +ignore("sbox", 2) +ignore("savebox", "{[[{") +ignore("usebox", 1) +envblock("minipage", "blockquote", &null, "[{") +argblock("parbox", "blockquote", &null, "[{") +#line 377 "l2h.nw" +ignore("hspace", "*{") +ignore("hfil") +ignore("hfill") +ignore("vspace", "*{") +ignore("vfil") +ignore("vfill") +#line 386 "l2h.nw" +ignore("newlength", "{") +ignore("setlength", "{{") +ignore("addtolength", "{{") +#line 392 "l2h.nw" +envblock("picture", "<b>[picture]</b>", "", "((") +ignore("put", "({") +ignore("multiput", "(({{") +ignore("dashbox", "{([") +ignore("line", "({") +ignore("vector", "({") +ignore("shortstack", "[") +ignore("circle", "*{") +ignore("oval", "([") +ignore("frame") +ignore("thinlines") +ignore("thicklines") +#line 408 "l2h.nw" +every envblock(star("figure"), "<hr>", "<hr>", "[") +every envblock(star("table"), "<hr>", "<hr>", "[") +argblock("caption", "b") # captions in bold +#line 415 "l2h.nw" +envblock("tabbing", "blockquote") +# \= is accent +ignore(">") +ignore("+") +ignore("kill") +#line 423 "l2h.nw" +tabular("array", "[") +tabular("tabular", "[") +tabular("tabularx", "{[") +tabular("tabular*", "{[") +every cstab["multicolumn"|"multispan"] := Cmulticolumn +cstab["span"] := Cspan +cstab["noalign"] := Cnoalign +cstab["hline"] := Chline +cstab["toprule"] := Chline +cstab["midrule"] := Chline +cstab["bottomrule"] := Chline +ignore("cline", "{") +ignore("newcolumntype", "{{") +#line 439 "l2h.nw" +ignore("newcommand", "A[[{") +ignore("renewcommand", "A[[{") +ignore("providecommand", "A[[{") +ignore("newenvironment", "{[{{") +ignore("renewenvironment", "{[{{") +ignore("newtheorem", "{{") +#line 450 "l2h.nw" +cstab["setcounter"] := Csetcounter +ignore("addtocounter", "{{") +#line 455 "l2h.nw" +cstab["makeatletter"] := Cmakeatletter +cstab["makeatother"] := Cmakeatother +#line 460 "l2h.nw" +every fontchange("tt" | "ttfamily", "tt") +every fontchange("bf" | "bfseries", "b") +every fontchange("it" | "itshape", "i") +fontchange("sl", "i") +fontchange("em", "em") +ignore("rm") # html can't switch to default font! +ignore("sf") +ignore("sc") +#line 470 "l2h.nw" +ignore("rmfamily") +ignore("normalfont") +#line 473 "l2h.nw" +ignoreenv("document") +#line 475 "l2h.nw" +substitution("LaTeX", "LaTeX") +#line 477 "l2h.nw" +ignore("numberline", "{") +ignore("protect") +ignore("onecolumn") +ignore("twocolumn", "C") +ignore("typeout", "[{") +ignore("closedbib") +#line 484 "l2h.nw" +every ignore("leftmargini" | "leftmarginii" | "labelsep" | "fboxsep", "=") +every ignore("tabcolsep", "=") +every ignore("evensidemargin" | "marginparsep" | "marginparwidth" | + "oddsidemargin" | "textheight" | "textwidth" | "topmargin", "=") +#line 489 "l2h.nw" +ignore("DeclareMathVersion", "{") +ignore("mathversion", "{") +ignore("setpapersize", "{") +ignore("setmarginsrb", "{{{{{{{{") +ignore("marginparwidth", "=") +ignore("marginparsep", "=") +#line 498 "l2h.nw" +ignoreenv("multicols", "{C") +cstab["citeN"] := Ccite +ignore("afterpage", "{") +every cstab["psfig"|"epsfig"] := Cepsfig +cstab["includegraphics"] := Cincludegraphics +ignore("newcolumntype", "{{") +#line 507 "l2h.nw" +cstab["kill"] := Ckill +#line 511 "l2h.nw" +activesubst("~", " ") +argblock("centerline", "<br>", "<br>") +substitution("cr", "<br>") +substitution("hrule", "<hr>") +substitution("vrule", "|") +substitution("hrulefill", "------") +ignore("hyphenation", "{") +ignore("hbox") +ignore("rlap") +ignore("llap") +ignore("vbox") +ignore("vtop") +ignore("hidewidth") +ignore("message", "{") +ignore("relax") +ignore("null") +ignore("offinterlineskip") +ignore("omit") +ignore("newdimen", "{") +ignore("nobreak") +#line 532 "l2h.nw" +cstab["par"] := implicit_paragraph +cstab["smallskip"] := implicit_paragraph +cstab["medskip"] := implicit_paragraph +cstab["bigskip"] := implicit_paragraph +cstab["vskip"] := implicit_paragraph +csclosure["vskip"] := "=" +#line 544 "l2h.nw" +every ignore("begingroup" | "endgroup" | "bgroup" | "egroup") +#line 546 "l2h.nw" +cstab["newif"] := Cnewif +cstab["iffalse"] := Ciffalse +cstab["iftrue"] := Ciftrue +cstab["ifhtml"] := Ciftrue # false in LaTeX, but true when converting! +cstab["else"] := Celse +cstab["fi"] := Cfi +cstab["ifx"] := cstab["if"] := cstab["ifnum"] := Ciffalse +#line 556 "l2h.nw" +ignore("let", "A=") +every ignore("hfuzz" | "parindent" | "parskip" | "baselineskip", "=") +every ignore("hbadness" | "hsize" | "vsize" | "overfullrule" | "tabskip", "=") +every ignore("extrarowheight" | "codemargin", "=") +every ignore("looseness", "=") +substitution("hskip", " ", "=") +ignore("setbox", "{=") +every ignore("box" | "unhbox" | "unvbox", "{") +#line 565 "l2h.nw" +ignore("unskip") +ignore("hss") +ignore("phantom", "{") +every ignore("kern" | "lower" | "spacefactor", "=") # a cheat, but works +every ignore("clubpenalty" | "widowpenalty", "=") +#line 578 "l2h.nw" +every ignore("expandafter" | "indent" | "noindent" | "leavevmode" | "strut") +ignore("def", 1) +#line 581 "l2h.nw" +substitution("TeX", "TeX") +substitution("BibTeX", "BibTeX") +substitution("MF", "METAFONT") +#line 594 "l2h.nw" +cstab["char"] := asciiCharCode +#line 608 "l2h.nw" +macro("nwanchorto", 2, "<a href=\"#$1\">#2</a>") +macro("nwanchorname", 2, "<a name=\"#$1\">#2</a>") +ignore("nwaddbox", "{") +verbatim("latexonly", do_nothing) +verbatim("rawhtml", emit_text) +#line 617 "l2h.nw" +substitution("quad", " ") +#line 624 "l2h.nw" +ignore("noweboptions", 1) +#line 630 "l2h.nw" +substitution("nowebindex", "<nowebindex>") +substitution("nowebchunks", "<nowebchunks>") +ignore("nowebsize") +#line 634 "l2h.nw" +envblock("fields", "blockquote", &null, "[") # lame; could try to <tt> 1st col +envblock("fields*", "blockquote", &null, "{") # lame; could try to <tt> 1st col +ignore("citeauthoryear", "{{{") +ignore("authoryear", "{{") +substitution("bibrule", "--------") +let("bibskip", "par") +every cstab["anoncite"|"authorcite"] := Ccite +#line 644 "l2h.nw" +ignore("pssilent") +ignore("psnoisy") +#line 1012 "l2h.nw" +cstab["begin"] := do_begin +cstab["end"] := do_end +#line 1385 "l2h.nw" +cstab["suspend"] := Csuspend +cstab["resume"] := Cresume +#line 1603 "l2h.nw" +cstab["uppercase"] := Cuppercase +#line 1899 "l2h.nw" +cstab["contentsline"] := Ccontentsline +#line 676 "l2h.nw" + +#line 2677 "l2h.nw" +csfunctions["argblock"] := argblock +csfunctions["argblockv"] := argblockv +csfunctions["envblock"] := envblock +csfunctions["fontchange"] := fontchange +csfunctions["ignore"] := ignore +csfunctions["ignoreenv"] := ignoreenv +csfunctions["let"] := let +csfunctions["letenv"] := letenv +csfunctions["listenv"] := listenv +csfunctions["substitution"] := substitution +csfunctions["activesubst"] := activesubst +csfunctions["closure"] := let_closure +csfunctions["let_closure"] := let_closure +csfunctions["newcommand"] := l2h_macro +csfunctions["macro"] := l2h_macro +csfunctions["environment"] := l2h_environment +csfunctions["tabular"] := tabular +#line 677 "l2h.nw" + specials := '\\{}<>-\'`"%$&~\n\0\1' + } + + if \optstring then return optstring ? convert(S) + else { + +#line 867 "l2h.nw" +l := S.newtext +S.newtext := [] +while c := get(l) do + c.proc!c.args +#line 700 "l2h.nw" +if S.mode == "V" & any(~'\\{}<>%\n\t ') then S.mode := "H" +emit_text(S, tab(upto(specials) | 0)) +while not pos(0) do + if S.mode == "Q" then { # quoting + emit_text(S, tab(upto('\1') | 0)) + if ="\1" then { + emit_text(S, "\1") + S.mode := S.quotemode + } + } else { + if any(S.activechars) then + do_activechar(S, move(1)) + else + case move(1) of { + "\\" : { +#line 941 "l2h.nw" +cs := if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) +if /S.ignoring | cs == ("else"|"fi") | cstab[cs] === (Ciffalse|Ciftrue) then + do_cs(S, cs) +else + &null # error("### Ignoring \\", cs) +#line 714 "l2h.nw" + } + "{" : { +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 715 "l2h.nw" + } + "}" : { +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 716 "l2h.nw" + } + "%" : { +#line 1066 "l2h.nw" +parse_dynamic_add(S) +delay_newtext(S) +eat_comment(S) +#line 717 "l2h.nw" + } + "\n" : { +#line 1313 "l2h.nw" +emit_text(S, "\n") +if /S.ignoring then Cnewline(S) +#line 718 "l2h.nw" + } + "$" : { +#line 1301 "l2h.nw" +if /S.ignoring then + if ="$" then + if S.mode == "M" then { Cdisplaymath_end(S); S.mode := "V" } + else { Cdisplaymath(S); S.mode := "M" } + else + if S.mode == "M" then { Cmath_end(S); S.mode := "H" } + else { Cmath(S); S.mode := "M" } +#line 719 "l2h.nw" + } + "&" : ampersand(S) + "\0" : {S.quotemode := S.mode; S.mode := "Q"; emit_text(S, "\0")} + # remaining cases simply escape HTML specials + "<" : emit_text(S, "<") + ">" : emit_text(S, ">") + "\"" : emit_text(S, """) + "-" : { +#line 735 "l2h.nw" +if S.mode == "V" then S.mode := "H" +if S.mode == "H" then + if ="--" then emit_text(S, "—") + else if ="-" then emit_text(S, "–") + else emit_text(S, "-") +else + emit_text(S, "-") +#line 726 "l2h.nw" + } + "`" : { +#line 746 "l2h.nw" +if S.mode == "V" then S.mode := "H" +if \html_quotes & S.mode == "H" then + if ="`" then emit_text(S, "“") + else emit_text(S, "‘") +else + emit_text(S, "`") +#line 727 "l2h.nw" + } + "'" : { +#line 753 "l2h.nw" +if S.mode == "V" then S.mode := "H" +if \html_quotes & S.mode == "H" & ="'" then emit_text(S, "”") +else emit_text(S, "'") +#line 728 "l2h.nw" + } + } + if S.mode == "V" & any(~'\\{}<>%\n\t ') then S.mode := "H" + emit_text(S, tab(upto(specials++S.activechars) | 0)) + } +return 1(. S.the_text, S.the_text := "") # what's been converted +#line 683 "l2h.nw" + } +end +#line 744 "l2h.nw" +global html_quotes # render quotes as HTML 4 entities +#line 760 "l2h.nw" +record state(mode, quotemode +#line 788 "l2h.nw" +, text, the_text +#line 804 "l2h.nw" +, ignoring +#line 822 "l2h.nw" +, activechars +#line 844 "l2h.nw" +, newtext +#line 887 "l2h.nw" +, delayed_newtext +#line 897 "l2h.nw" +, open, closes +#line 949 "l2h.nw" +, csletters +#line 1082 "l2h.nw" +, comment +#line 1521 "l2h.nw" +, ifstack +#line 1628 "l2h.nw" +, tables +#line 760 "l2h.nw" + ) + # mode is Q, H, V, or M + # quotemode is saved mode: H, V, or M +#line 766 "l2h.nw" +procedure converter(mode) + /mode := "V" + return state(mode, mode +#line 790 "l2h.nw" +, accumulate_text, "" +#line 806 "l2h.nw" +, &null +#line 824 "l2h.nw" +, '~' +#line 846 "l2h.nw" +, [] +#line 889 "l2h.nw" +, [] +#line 899 "l2h.nw" +, [], [] +#line 951 "l2h.nw" +, &letters +#line 1084 "l2h.nw" +, "" +#line 1523 "l2h.nw" +, [] +#line 1630 "l2h.nw" +, [] +#line 768 "l2h.nw" + ) +end +#line 774 "l2h.nw" +procedure reset(S) + +#line 792 "l2h.nw" +S.text := accumulate_text +S.the_text := "" +#line 808 "l2h.nw" +S.ignoring := &null +#line 826 "l2h.nw" +S.activechars := '~' +#line 848 "l2h.nw" +S.newtext := [] +#line 891 "l2h.nw" +S.delayed_newtext := [] +#line 901 "l2h.nw" +every S.open | S.closes := [] +#line 953 "l2h.nw" +S.csletters := &letters +#line 1086 "l2h.nw" +S.comment := "" +#line 1525 "l2h.nw" +if *S.ifstack > 0 then S.ifstack := [] # keeps GC down +#line 1632 "l2h.nw" +S.tables := [] +#line 776 "l2h.nw" + return S +end +#line 783 "l2h.nw" +procedure accumulate_text(S, text) + S.the_text ||:= text + return +end +#line 800 "l2h.nw" +procedure emit_text(S, text) + return if \S.ignoring then "" else S.text(S, text) +end +#line 813 "l2h.nw" +global activetab, activeclosure +procedure do_activechar(S, c) + (activetab[c])(S, c, activeclosure[c]) + return +end +#line 852 "l2h.nw" +record closure(proc, args) +#line 857 "l2h.nw" +procedure before_next_newtext(S, proc, args) + push(S.newtext, closure(proc, args)) +end + +procedure after_next_newtext(S, proc, args) + put(S.newtext, closure(proc, args)) +end +#line 876 "l2h.nw" +procedure delay_newtext(S) + push(S.delayed_newtext, S.newtext) + S.newtext := [] + return +end + +procedure undelay_newtext(S) + S.newtext := \pop(S.delayed_newtext) | + {write(&errout, "This can't happen: no delayed_newtext"); &null[0]} +end +#line 903 "l2h.nw" +procedure after_next_open(S, proc, args) + return put(S.open, closure(proc, args)) +end + +procedure before_next_close(S, proc, args) + return push(S.closes[1], closure(proc, args)) # lost at top level +end + +procedure after_next_close(S, proc, args) + return put(S.closes[1], closure(proc, args)) # lost at top level +end +#line 960 "l2h.nw" +global cstab, csclosure + +procedure do_cs(S, cs) + tab(many(' \t')) # skip white space following CS + if pos(0) | any('\n') then before_next_newtext(S, skipblanks, [S]) + (cstab[cs])(S, cs, csclosure[cs]) + return +end +#line 976 "l2h.nw" +global show_unknowns +procedure unknown_cs(S, cs, cl) +# if S.text === ignore_text then return # a bit of a hack -- should no longer be needed + if \show_unknowns then S.text(S, "<b>\\" || cs || "</b>") + if not member(unknown_set, cs) then { + write(\unknown_file, "Warning: unknown control sequence \\", cs) + insert(unknown_set, cs) + } + return +end +#line 989 "l2h.nw" +global cstab, csclosure, unknown_set +#line 995 "l2h.nw" +global begintab, endtab, begincl, endcl + +procedure do_begin(S, cs, cl) + (="{", env := tab(upto('}')), ="}") | error("botched \\begin{...}") + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 1000 "l2h.nw" + (begintab[env])(S, env, begincl[env]) + return +end + +procedure do_end(S, cs, cl) + (="{", env := tab(upto('}')), ="}") | error("botched \\end{...}") + # write(&errout, "calling ", image(endtab[env]), " for \\end{", env, "}") + (endtab[env])(S, env, endcl[env]) + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1009 "l2h.nw" + return +end +#line 1018 "l2h.nw" +procedure unknown_env(S, env, cl) +### if S.text === ignore_text then return # a bit of a hack # no longer needed + if \show_unknowns then S.text(S, "<b>{" || env || "}</b>") + if not member(unknown_envs, env) then { + write(\unknown_file, "Warning: unknown environment {", env, "}") + insert(unknown_envs, env) + } + return +end +#line 1030 "l2h.nw" +global unknown_envs +#line 1034 "l2h.nw" +procedure warn_unknown(s, type, mark, rmark) + if *s > 0 then { + pushout("Unknown " || type || ": ") + every pushout(((\mark | "")\1) || !sort(s) || ((\rmark | "")\1) || " ") + pushout("\n") + } +end +#line 1042 "l2h.nw" +procedure pushout(s) + static col + initial col := 0 + if find("\n", s) then + s ? { + pushout(tab(upto('\n'))) + while ="\n" do {col := 0; write(&errout)} + pushout(tab(0)) + } + else { + col +:= *s + if col >= 79 then {writes(&errout, "\n "); col := *s + 2} + writes(&errout, s) + } + return +end +#line 1070 "l2h.nw" +procedure eat_comment(S) + S.comment ||:= tab(upto('\n') | 0) + if pos(0) then + before_next_newtext(S, eat_comment, [S]) + else { + undelay_newtext(S) + Ccomment(S) + S.comment := "" + } + return +end +#line 1106 "l2h.nw" +record verbatim_cl(output, terminator, html, translate_blank) + +procedure verbatim(name, output, html) + begintab[name] := Cverbatim + begincl [name] := verbatim_cl(output, &null, html) + return +end + +procedure Cverbatim(S, cs, cl) + if cl === begincl[cs] & /cl.terminator then + cl := begincl[cs] := + verbatim_cl(cl.output, "\\end{" || cs || "}", cl.html, cl.translate_blank) + emit_text(S, tag(\cl.html)) + delay_newtext(S) + do_verbatim(S, cl) + return +end +#line 1128 "l2h.nw" +procedure do_verbatim(S, cl) + if cl.output(S, tab(find(cl.terminator)), cl) then { + =cl.terminator + emit_text(S, endtag(\cl.html)) + undelay_newtext(S) + } else { + cl.output(S, tab(0), cl) + before_next_newtext(S, do_verbatim, [S, cl]) + } + return +end +#line 1142 "l2h.nw" +procedure escape_HTML_specials(S, s, cl) + s ? { + while emit_text(S, tab(upto('&<>" '))) do + case move(1) of { + "\"" : emit_text(S, """) + "&" : emit_text(S, "&") + "<" : emit_text(S, "<") + ">" : emit_text(S, ">") + " " : emit_text(S, if \cl.translate_blank then "·" else " ") + } + emit_text(S, tab(0)) + } + return +end +#line 1160 "l2h.nw" +procedure Cverb(S, cs, cl) + Cverbatim(S, cs, verbatim_cl(escape_HTML_specials, move(1), "tt", cl)) + return +end +#line 1166 "l2h.nw" +procedure Cverbatiminput(S, cs, cl) + local filename, f, line + if ="{" & filename := tab(upto('}')) & ="}" then { + f := \open(filename) | { emit_text(S, "<strong>Could not read file</strong> <tt>") + escape_HTML_specials(S, filename, cl) + emit_text(S, "</tt>") + return + } + emit_text(S, "\n<pre>\n") + while line := read(f) do { + escape_HTML_specials(S, line, cl) + emit_text(S, "\n") + } + emit_text(S, "</pre>\n") + close(f) + } + return +end +#line 1190 "l2h.nw" +procedure csarg(S) + return 2(="{", tab(bal('}', '{', '}')), ="}") | + (optwhite(), + if ="\\" then + "\\" || (tab(many(S.csletters)) | move(1)) + else + move(1)) +end +#line 1225 "l2h.nw" +procedure apply_args(S, p, as, args_wanted) + delay_newtext(S) + do_apply_args(S, closure(p, as), args_wanted, [], "", 0) + return +end + +procedure do_apply_args(S, cl, wanted_count, args_seen, current_arg, brace_depth) + local open_comment + # invariant : we have an open brace +# pushtrace("APPLY") + while *args_seen < wanted_count & not pos(0) do { + while *args_seen < wanted_count & brace_depth = 0 & not pos(0) do { + tab(many(' \t\n')) + case c := move(1) of { + "\\" : put(args_seen, "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1)) + "{" : { current_arg := "" ; brace_depth := 1 } + "}" : { error("Insufficient arguments to macro ", macro.name) } + "%" : if tab(upto('\n')) then ="\n" else open_comment := tab(0) + default : put(args_seen, c) + } + } + while brace_depth > 0 & not pos(0) do { + current_arg ||:= tab(upto('\\{}%') | 0) + case move(1) of { + "%" : if tab(upto('\n')) then ="\n" else open_comment := tab(0) + "\\" : current_arg ||:= "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) + "{" : { current_arg ||:= "{" ; brace_depth +:= 1 } + "}" : { brace_depth -:= 1 + if brace_depth > 0 then + current_arg ||:= "}" + else { + put(args_seen, current_arg) + current_arg := "" + } + } + } + } + } + if *args_seen = wanted_count then { + undelay_newtext(S) + cl.proc ! (cl.args ||| args_seen) + } else if \open_comment then { + delay_newtext(S) + before_next_newtext(S, skip_comment_and_continue, + [S, closure(do_apply_args, [S, cl, wanted_count, args_seen, + current_arg, brace_depth])]) + } else + before_next_newtext(S, do_apply_args, [S, cl, wanted_count, args_seen, + current_arg, brace_depth]) +# poptrace() + return +end +#line 1284 "l2h.nw" +procedure skip_comment_and_continue(S, cl) + tab(upto('\n') | 0) + if pos(0) then + before_next_newtext(S, skip_comment_and_continue, [S, cl]) + else { + ="\n" + undelay_newtext(S) +# <take actions appropriate to new text>> + } + return +end +#line 1316 "l2h.nw" +procedure Cnewline(S) + tab(many(' \t')) + if match("\n") then implicit_paragraph(S) + if pos(0) then before_next_newtext(S, Cnewline, [S]) +end +#line 1325 "l2h.nw" +procedure skipblanks(S) + tab(many(' \t')) + if ="\n" then Cnewline(S) + else if pos(0) then before_next_newtext(S, skipblanks, [S]) +end +#line 1334 "l2h.nw" +procedure implicit_paragraph(S, cs, cl) + if S.mode ~== "V" then { + S.mode := "V" + Cparagraph(S) + } + cs_ignore(S, cs, \cl) +end +#line 1347 "l2h.nw" +procedure stop_skipping(S) + while S.newtext[1].proc === (Cnewline|skipblanks) do pop(S.newtext) +end +#line 1357 "l2h.nw" +record item_cl(before, after, ifnone) + +procedure Citem(S, cs, cl) + if pos(0) then + after_next_newtext(S, Citem, [S, cs, cl]) + else if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", convert_bracketed, cl) + } else { + skipblanks(S) + emit_text(S, cl[1].ifnone) + \liststack[1] := \liststack[1] + 1 + } +end +#line 1372 "l2h.nw" +procedure convert_bracketed(S, contents, cl) + emit_text(S, cl[1].before || + convert(converter("H"), "{" || contents || "}") || + cl[1].after) + optwhite() +end +#line 1379 "l2h.nw" +global liststack, suspendstack +#line 1388 "l2h.nw" +procedure listenv(env, html) + begintab[env] := Clist + begincl[env] := html + endtab[env] := Clist_end + endcl[env] := html +end + +procedure push_item_closure(cs) + push(csclosure["item"], + if cs == "description" then item_cl("<dt>", "<dd>", "<dt><dd>") + else item_cl("<li>", "--", "<li>")) +end + +procedure Clist(S, cs, cl) + push(liststack, 1) + push_item_closure(cs) + emit_text(S, tag(cl)) +end + +procedure Cresume(S, cs, cl) + local n, env + (="{", env := tab(upto('}')), ="}") | error("botched \\resume{...}") + n := pop(suspendstack) | error("\\resume not balanced by corresponding \\suspend") + push(liststack, n) + push_item_closure(env) + if env == "enumerate" then + emit_text(S, tag(begincl[env] || " start=\"" || n || "\"")) + else + emit_text(S, tag(begincl[env])) +end + +procedure Clist_end(S, cs, cl) + emit_text(S, endtag(cl)) + pop(csclosure["item"]) + pop(liststack) +end + +procedure Csuspend(S, cs, cl) + local env + (="{", env := tab(upto('}')), ="}") | error("botched \\suspend{...}") + emit_text(S, endtag(endcl[env])) + pop(csclosure["item"]) + push(suspendstack, pop(liststack)) +end +#line 1437 "l2h.nw" +global labels_seen + +procedure Clabel(S, cs, cl) + initial /labels_seen := set() + insert(labels_seen, l := csarg(S)) | fail + emit_text(S, "<a name=\"" || l || "\"><b>[*]</b></a>") +end + +procedure Cref(S, cs, cl) + local prefix, prefix_tag + initial /labels_seen := set() + prefix_tag := (\cl)[1] | "" + prefix := (\cl)[2] | "" + l := prefix || csarg(S) | fail + emit_text(S, prefix_tag || "<a href=\"#" || l || "\">[" || + (if member(labels_seen, l) then "<-" else "->") || "]</a>") +end +#line 1463 "l2h.nw" +procedure Ccite(S, cs, cl, bracketed_text) + if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", do_cite, cl) + } else + do_cite(S, &null, cl) +end + +procedure do_cite(S, commentary, cl) + local key + if \commentary then + optwhite() + if pos(0) then before_next_newtext(S, do_cite, [S, commentary, cl]) + else { + key := csarg(S) + \commentary := convert(converter("H"), "{" || \commentary || "}") + emit_text(S, "<b>[cite ") + key ? { + while k := tab(upto(",")) & ="," do + emit_text(S, "<a href=\"#NWcite-" || k || "\">" || k || "</a>, ") + if k := tab(0) then + emit_text(S, "<a href=\"#NWcite-" || k || "\">" || k || "</a>") + } + emit_text(S, ", <i>" || \commentary || "</i>") + emit_text(S, "]</b>") + } +end +#line 1491 "l2h.nw" +procedure Cbibitem(S, cs, cl) + local label, key + static counter + initial counter := 0 + if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", finish_bibitem, []) + } else { + label := "<b>[" || (counter +:= 1) || "]</b>" + apply_args(S, do_bibitem_key, [S, label], 1) + } +end + +procedure do_bibitem_key(S, label, key) + return emit_text(S, "<a name=\"NWcite-" || key || "\">" || label || "</a> ") +end + +procedure finish_bibitem(S, contents, args) + local key, label + optwhite() + label := convert(converter("H"), "{" || contents || "}") + key := apply_args(S, do_bibitem_key, [S, label], 1) +end +#line 1529 "l2h.nw" +record ifrec(on_else, on_fi) +#line 1538 "l2h.nw" +procedure Ciffalse(S, cs, cl) +#error("### \\", cs, " -> false (S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}, ")") + push(S.ifstack, ifrec(S.ignoring, S.ignoring)) + S.ignoring := 1 +end + +procedure Ciftrue(S, cs, cl) +#error("### \\", cs, " -> true (S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}, ")") + push(S.ifstack, ifrec(1, S.ignoring)) +end + +procedure Celse(S, cs, cl) + S.ignoring := S.ifstack[1].on_else +#error("### \\else -> S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}) +end + +procedure Cfi(S, cs, cl) + S.ignoring := S.ifstack[1].on_fi +#error("### \\fi -> S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}) + pop(S.ifstack) +end +#line 1563 "l2h.nw" +procedure Cnewif(S, cs, cl) + local newif, newcs + tab(many(' \t\n')) + if pos(0) then + after_next_newtext(S, Cnewif, [S, cs, cl]) + else { + newif := csarg(S) + newif ? + if ="\\if" & newcs := tab(many(S.csletters)) & pos(0) then { + +#line 1580 "l2h.nw" +cstab[newcs || "false"] := Csetif +cstab[newcs || "true"] := Csetif +cstab["if" || newcs] := Ciffalse +#line 1573 "l2h.nw" + } else + error("\\newif argument botch: " || newif) + } +end +#line 1584 "l2h.nw" +procedure Csetif(S, cs, cl) + local base, tag + if cs ? (base := tab(find("true"|"false")), tag := =("true"|"false"), pos(0)) then { + cstab["if" || base] := if tag == "true" then Ciftrue else Ciffalse + } else { + error("This can't happen --- setif botch (not urgent)") + } +end +#line 1598 "l2h.nw" +procedure Cuppercase(S, cs, cl) + l := map(csarg(S), &lcase, &ucase) | fail + emit_text(S, l) +end +#line 1613 "l2h.nw" +record table_info(index, # number of this cell in the row + alignment, # the alignment of this cell + width, # how many columns this cell will span + alignments, # default alignments for this table + brace_depth, # size of S.closes after start of cell + cell_text) # value of S.text to use to scan this cell +#line 1637 "l2h.nw" +procedure start_table_cell(S, text) + local this, attributes + + text ? { + tab(many(' \t\n')) + if pos(0) then return + } +# write(&errout, "starting cell with ", image(text)) + + this := S.tables[1] | fatal("starting cell with no current table") + S.text := this.cell_text + +if /(\this).brace_depth then write(&errout, "starting table cell, ", image(this), " has null brace depth") + # use this to start the current cell + if this.index = 1 then emit_text(S, "<tr>") + attributes := \this.alignment | aligneq("top") + if this.width > 1 then attributes ||:= " colspan=" || this.width + emit_text(S, "<td" || attributes || ">") + + # now update state for the next cell + this.index +:= this.width # advance to next cell + this.alignment := this.alignments[this.index] | &null + this.width := 1 + # can't set cell_text until we hit & + + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 1663 "l2h.nw" + emit_text(S, text) + return +end +#line 1670 "l2h.nw" +procedure ampersand(S) + local this + this := S.tables[1] + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1674 "l2h.nw" + if /this then + emit_text(S, " --- ") + else { + emit_text(S, "") # be sure cell gets started, even if empty + emit_text(S, "</td>") + if S.text ~=== start_table_cell then + this.cell_text := S.text + S.text := start_table_cell + this.brace_depth := *S.closes + 1 # will open at start of cell +#write(&errout, "set brace depth for ", image(this)) + } + tab(many(' \t\n')) +## write(&errout, " past &, text = ", image(S.text), ", next = ", +## image(&subject[&pos:0])) + return +end +#line 1699 "l2h.nw" +procedure Cbackback(S, cs, cl) + local this + this := S.tables[1] + cs_ignore(S, cs, "[", Cbackback_continue, [S, this]) +end + +procedure Cbackback_continue(S, this) +#if /(\this).brace_depth then write(&errout, image(this), " has null brace depth") + if /this | *S.closes > this.brace_depth then { # ordinary \\ + S.text(S, "<br>") + } else { # row terminator +## write(&errout, "ending row with ", image(&subject[&pos:0])) + emit_text(S, "") # be sure cell gets started, even if empty + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1713 "l2h.nw" + emit_text(S, "</td></tr>\n") + tab(many(' \t\n')) + if S.text ~=== start_table_cell then + this.cell_text := S.text + this.index := 1 + this.alignment := this.alignments[this.index] | &null + this.width := 1 + S.text := start_table_cell + this.brace_depth := *S.closes + 1 # about to open + } +end +#line 1727 "l2h.nw" +procedure Chline(S, cs, cl) + if \S.tables[1] then return + else emit_text(S, "<hr>") + return +end +#line 1737 "l2h.nw" +procedure Ctabular_end(S, cs, cl) + local this + if S.text ~=== start_table_cell | S.tables[1].index > 1 then {# row in progress + emit_text(S, "") # be sure cell gets started, even if empty + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1742 "l2h.nw" + emit_text(S, "</td></tr>") + } + if S.text === start_table_cell then # abort it + S.text := S.tables[1].cell_text + emit_text(S, "</table>") + xxx := pop(S.tables) +#write(&errout, "popped ", image(xxx)) + return +end +#line 1754 "l2h.nw" +procedure Ctabular(S, cs, cl) + cs_ignore(S, cs, cl, Ctabular_continue, [S]) + return +end + +procedure Ctabular_continue(S) + a := csarg() # alignment +#write(&errout, "Alignment ", a) + emit_text(S, if upto('|', a) then "<table border>" else "<table>") + emit_text(S, "<!-- alignment is " || a || "-->") + a := alignments(a) + emit_text(S, "<!-- " || *a || " columns-->") + push(S.tables, table_info(1, a[1] | "l", 1, a, *S.closes+1, S.text)) +#write(&errout, "pushed ", image(S.tables[1])) + S.text := start_table_cell + optwhite() + return +end +#line 1777 "l2h.nw" +procedure tabular(env, ignore) + begintab[env] := Ctabular + begincl[env] := ignore + endtab[env] := Ctabular_end + endcl[env] := ignore +end +#line 1786 "l2h.nw" +procedure aligneq(a) + return " align=\"" || a || "\"" +end + +procedure valigneq(a) + return " valign=\"" || a || "\"" +end + +procedure alignments(s) + a := [] + s ? { + while not pos(0) do + case move(1) of { + "l" | "X" | "Y" | "p" : + { put(a, aligneq("left") || valigneq("top")); skip_bracket() } + "c" : put(a, aligneq("center")) + "r" : put(a, aligneq("right")) + "m" : { put(a, aligneq("left") || valigneq("center")); skip_bracket() } + "b" : { put(a, aligneq("left") || valigneq("bottom")); skip_bracket() } + "@" | "<" | ">" | "!" : skip_bracket() + "|" : &null + default : &null # unrecognized... + } + } + return a +end +#line 1813 "l2h.nw" +procedure skip_bracket() + if ="{" then { + n := 1 + while n > 0 & not pos(0) do { + tab(upto('{}\\') | 0) + case move(1) of { + "{" : n +:= 1 + "}" : n -:= 1 + "\\" : move(1) + } + } + } + return +end +#line 1831 "l2h.nw" +procedure Cmulticolumn(S, cs, cl) + local this + this := S.tables[1] + n := integer(csarg()) | error("\\multicolumn or \\multispan not followed by integer") + if cs == "multicolumn" then a := alignments(csarg()) +# write(&errout, "\\", cs, " n = ", n, ", a = ", (\a)[1] | "???", +# ", text = ", image(S.text)) + if /this then return # \multicolumn without table? + this.width := n + this.alignment := (\a)[1] + return +end + +procedure Cspan(S, cs, cl) + (\S.tables[1]).width +:= 1 + return +end +#line 1850 "l2h.nw" +procedure Cnoalign(S, cs, cl) + apply_args(S, finish_noalign, [S], 1) + return +end + +procedure finish_noalign(S, arg) + return if \S.ignoring then "" else + accumulate_text(S, "<br>" || convert(converter("V"), "{" || arg || "}") || "<br>") +end +#line 1862 "l2h.nw" +procedure auxfile(cs, ext, placeholder, header, trailer, ignore) + cstab[cs] := Cauxfile + csclosure[cs] := aux_cl(ext, placeholder, header, trailer, \ignore | "") +end +#line 1869 "l2h.nw" +record aux_cl(ext, placeholder, header, trailer, ignore) + +procedure Cauxfile(S, cs, cl) + local auxfile, T + if auxfile := open(basename(\curfile) || "." || cl.ext) then { + T := converter("V") + Cmakeatletter(T) + S.text(S, \cl.header) + while line := read(auxfile) do + S.text(S, convert(T, line || "\n")) + close(auxfile) + S.text(S, \cl.trailer) + } else { + S.text(S, \cl.placeholder) + } + cs_ignore(S, cs, cl.ignore) + if \auxfile then return +end +#line 1888 "l2h.nw" +procedure basename(name) + reverse(name) ? { + tab(upto('.')) & ="." + return reverse(tab(0)) + } +end +#line 1901 "l2h.nw" +procedure Ctableofcontents(S, cs, cl) + S.mode := "V" + Cauxfile(S, cs, cl) + set_toclevel(S) +end +#line 1910 "l2h.nw" +procedure set_toclevel(S, l) + static toclevel, initiallevel + if /initiallevel := \l then + S.text(S, "<ul compact>") + if /l := \initiallevel then + S.text(S, "</ul>") + if /l then return # never set a level + /toclevel := l + while toclevel < l do { + S.text(S, "<ul compact>") + toclevel +:= 1 + } + while toclevel > l do { + S.text(S, "</ul>") + toclevel -:= 1 + } + return +end +#line 1931 "l2h.nw" +procedure Ccontentsline(S, cs, cl) + local type, level + static leveltab + initial { +#line 1945 "l2h.nw" +l := ["part", "chapter", "section", "subsection", "subsubsection", + "paragraph", "subparagraph"] +leveltab := table() +every i := 1 to *l do + leveltab[l[i]] := i - 2 # making section level 1 +#line 1934 "l2h.nw" + } + l := \leveltab[csarg()] | fail + if l > \countertab["tocdepth"] then + cs_ignore(S, cs, "{{") # skip this one + else { + set_toclevel(S, l) + S.text(S, "<li>") + after_next_open(S, after_next_close, [S, cs_ignore, [S, cs, "{"]]) + } +end +#line 1953 "l2h.nw" +global countertab + +procedure Csetcounter(S, cs, cl) + local counter + (counter := csarg(), countertab[counter] := integer(csarg())) | + cs_ignore(S, cs, "{{") +end +#line 1967 "l2h.nw" +global accent_name, accent_valid +#line 1998 "l2h.nw" +procedure accent(cs) + cstab[cs] := Caccent +end + +procedure Caccent(S, cs, cl) + static warned + initial warned := table() + arg := csarg(S) | return + if arg == "\\i" then arg := "i" + if arg == "\\j" then arg := "j" + if *arg = 1 & any(accent_valid[cs], arg) then + S.text(S, "&" || arg || accent_name[cs] || ";") + else { + +#line 2016 "l2h.nw" +/warned[cs] := set() +if not member(warned[cs], arg) then { + write(&errout, "Warning: Can't handle \\", cs, " with arg `", arg, "'") + insert(warned[cs], arg) +} +#line 2012 "l2h.nw" + S.text(S, arg) + } +end +#line 2026 "l2h.nw" +procedure fontchange(tex, html) + cstab[tex] := Cfontchange + csclosure[tex] := html +end +#line 2031 "l2h.nw" +procedure Cfontchange(S, tex, html) + S.text(S, tag(html)) + before_next_close(S, emit_text, [S, endtag(html)]) +end +#line 2051 "l2h.nw" +procedure ignore(cs, template) + /template := "" + cstab[cs] := cs_ignore + csclosure[cs] := template +end + +procedure ignoreenv(env, template) + /template := "" + begintab[env] := cs_ignore + begincl[env] := template + endtab[env] := do_nothing +end +#line 2070 "l2h.nw" +procedure cs_ignore(S, cs, template, proc, args) + local saved_ignore + saved_ignore := S.ignoring + S.ignoring := 1 + if type(template) == "integer" then template := repl("{", template) + return do_ignore(S, template, saved_ignore, proc, args) +end +#line 2084 "l2h.nw" +procedure do_ignore(S, template, saved_ignore, proc, args) + if *template > 0 then + if optwhite() & pos(0) then + after_next_newtext(S, do_ignore, [S, template, saved_ignore, proc, args]) + else + case template[1] of { + "{" : { S.ignoring := 1 + after_next_open(S, ignore_til_close, + [S, template[2:0], saved_ignore, proc, args]) + } + "A" : { csarg(S) # had better be in one input + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + "[" : if optwhite() & ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", ignore_bracket_plus, + [S, template[2:0], saved_ignore, proc, args]) + } else + do_ignore(S, template[2:0], saved_ignore, proc, args) + "C" : # a total cheat, means ``copy optional arg'' + if optwhite() & ="[" then { + S.ignoring := &null + delay_newtext(S) + with_upto_bracket(S, "", copy_bracket_plus, + [S, template[2:0], saved_ignore, proc, args]) + } else + do_ignore(S, template[2:0], saved_ignore, proc, args) + "=" : { delay_newtext(S) + eat_assignment(S, do_ignore, [S, template[2:0], saved_ignore, proc,args]) + } + "*" : { (="*", optwhite()) + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + "(" : { (="(", tab(upto(')')), =")", optwhite()) + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + } + else { + S.ignoring := saved_ignore + (\proc)!(\args) + } +end + +procedure ignore_til_close(S, template, saved_ignore, proc, args) + before_next_close(S, do_ignore, [S, template, saved_ignore, proc, args]) +end +#line 2133 "l2h.nw" +procedure do_nothing(S, cs, cl) + return +end +#line 2144 "l2h.nw" +procedure with_upto_bracket(S, bracketed_text, proc, args) + bracketed_text ||:= tab(upto(']') | 0) + if pos(0) then + before_next_newtext(S, with_upto_bracket, [S, bracketed_text, proc, args]) + else { + ="]" + undelay_newtext(S) + (\proc)(S, bracketed_text, args) + } + return +end +#line 2158 "l2h.nw" +procedure ignore_bracket_plus(S, contents, args) + # contents are ignored + do_ignore!args +end +#line 2164 "l2h.nw" +procedure copy_bracket_plus(S, contents, args) + local should_ignore + should_ignore := args[3] | fail # saved_ignore arg to do_ignore + if /should_ignore then + S.text(S, convert(converter("H"), "{" || contents || "}")) + do_ignore!args +end +#line 2177 "l2h.nw" +procedure eat_assignment(S, proc, args) + static decimal_chars, hex_chars, oct_chars + initial { + decimal_chars := &digits ++ '.,+-' + hex_chars := &digits ++ 'abcdefABCDEF' + oct_chars := '0124567' + } + optwhite() + ="=" # so what if we swallow multiple = signs + optwhite() + if pos(0) then { + before_next_newtext(S, eat_assignment, [S, proc, args]) + return + } else if glue() then { # finished + } else if any(decimal_chars) then { + tab(many(decimal_chars)) + optwhite() + if ="\\" then + tab(many(S.csletters)) | move(1) + # else assume assignment of the form \hangafter=2 + } else if ="\"" then { + tab(many(hex_chars)) & optwhite() + } else if ="\'" then { + tab(many(oct_chars)) & optwhite() + } else if =("\\"|"`\\") then + tab(many(S.csletters)) | move(1) + undelay_newtext(S) + (\proc)!args +end +#line 2207 "l2h.nw" +procedure dimen() + static decimal_chars + initial decimal_chars := &digits ++ '.,' + suspend (optwhite(), + if any('+-') then (move(1), optwhite()) else "", + tab(many(decimal_chars)), optwhite(), + (="true", optwhite()) | &null, + =("em"|"ex"|"pt"|"pc"|"in"|"bp"|"cm"|"mm"|"dd"|"cc"|"sp"|"mu")) +end +#line 2217 "l2h.nw" +procedure glue() + suspend (dimen(), + (optwhite(), ="plus", dimen()) | "", + (optwhite(), ="minus", dimen()) | "") +end +#line 2231 "l2h.nw" +procedure substitution(tex, html, ignore_template) + # ignore mode for now + cstab[tex] := Cemit_ig + csclosure[tex] := emit_ig_cl(html, \ignore_template | "") +end +#line 2239 "l2h.nw" +record emit_ig_cl(html, template) + +procedure Cemit_ig(S, cs, cl) + emit_text(S, cl.html) + if *cl.template > 0 then + cs_ignore(S, cs, cl.template) +end +#line 2249 "l2h.nw" +procedure activesubst(char, html, ignore_template) + local S + # ignore mode for now + activetab[char] := Cemit_ig + activeclosure[char] := emit_ig_cl(html, \ignore_template | "") + S := \dynamic_add_hack | return + if upto(S.activechars, char) then return # already active + if S.activechars ++:= cset(char) then { + before_next_close(S, delete_active_char, [S, char]) + } else impossible("ugh") + return +end + +procedure delete_active_char(S, char) + S.activechars --:= char + return +end +#line 2282 "l2h.nw" +procedure envblock(env, left, right, ignore_template) + /ignore_template := "" + begintab[env] := Cemit_ig + begincl[env] := emit_ig_cl(if /right then tag(left) else left, ignore_template) + endtab[env] := Cemit + endcl[env] := if /right then endtag(left) else right +end +#line 2291 "l2h.nw" +procedure Cemit(S, cs, cl) + S.text(S, cl) +end +#line 2303 "l2h.nw" +record blockpair(left, right, ignore) + +procedure argblock(tex, html, right, ignore) + # called as is envblock + /ignore := "" + cstab[tex] := Cblock + csclosure[tex] := + if /right then blockpair (tag(html), endtag(html), ignore) + else blockpair (html, right, ignore) +end +#line 2316 "l2h.nw" +procedure argblockv(tex, html, right, ignore) + argblock(tex, html, right, ignore) + cstab[tex] := CblockV +end +#line 2321 "l2h.nw" +procedure Cblock(S, cs, cl, done_ignoring) + if /done_ignoring & *cl.ignore > 0 then { + cs_ignore(S, cs, cl.ignore, Cblock, [S, cs, cl, 1]) + } else if pos(0) then { + after_next_newtext(S, do_cs, [S, cs, cl]) + } else if match("{") then { + S.text(S, cl.left) + after_next_open(S, before_next_close, [S, emit_text, [S, cl.right]]) + } else { +# S.text(S, cl.left || csarg(S) || cl.right) + apply_args(S, Cblock_continue, [S, cl], 1) + } + return +end + +procedure Cblock_continue(S, cl, title) + S.text (S, cl.left || title || cl.right) + return +end +#line 2341 "l2h.nw" +procedure CblockV(S, cs, cl) + S.mode := "V" + Cblock(S, cs, cl) + return +end +#line 2353 "l2h.nw" +record macro_defn(name, arg_count, body, mode) +#line 2357 "l2h.nw" +record raw_arg(number) +record converted_arg(number, mode) +#line 2361 "l2h.nw" +procedure expand_macro(S, macro, args) + every a := !macro.body do + case type(a) of { + "string" : emit_text(S, a) + "raw_arg" : emit_text(S, args[a.number]) | impossible("missing arg") + "converted_arg" : S.text(S, convert(S, "{" || args[a.number] || "}")) + } +# poptrace() + return +end +#link pushtrace +#line 2375 "l2h.nw" +procedure do_macro(S, macro, args_seen, current_arg, brace_depth) + # invariant : we have an open brace +# write(&errout, "scanning args for macro ", macro.name) +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count) + while *args_seen < macro.arg_count & not pos(0) do { + while *args_seen < macro.arg_count & brace_depth = 0 & not pos(0) do { +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + tab(many(' \t\n')) + case c := move(1) of { + "\\" : put(args_seen, "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1)) + "{" : { current_arg := "" ; brace_depth := 1 } + "}" : { error("Insufficient arguments to macro ", macro.name) } + default : put(args_seen, c) + } + } + while brace_depth > 0 & not pos(0) do { +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + current_arg ||:= tab(upto('\\{}') | 0) + case move(1) of { + "\\" : current_arg ||:= "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) + "{" : { current_arg ||:= "{" ; brace_depth +:= 1 } + "}" : { brace_depth -:= 1 + if brace_depth > 0 then + current_arg ||:= "}" + else { + put(args_seen, current_arg) + current_arg := "" + } + } + } + } + } +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + if *args_seen = macro.arg_count then { +# write(&errout, "Arguments for macro ", macro.name, ":") +# every write(&errout, "\t", image(!args_seen)) + expand_macro(S, macro, args_seen) + undelay_newtext(S) + } else + before_next_newtext(S, do_macro, [S, macro, args_seen, current_arg, brace_depth]) + return +end +#line 2428 "l2h.nw" +procedure Cmacro(S, cs, cl) +# pushtrace("MACRO") + delay_newtext(S) +# apply_args(S, closure(expand_macro, [cl]), cl.arg_count) + do_macro(S, cl, [], "", 0) + return +end +#line 2439 "l2h.nw" +procedure macro(name, arg_count, body, mode) + m := macro_defn(name, arg_count, parse_body(body), mode) + cstab[name] := Cmacro + csclosure[name] := m + return +end + +procedure begin_macro(env, arg_count, body, mode) + m := macro_defn(env, arg_count, parse_body(body), mode) + begintab[env] := Cmacro + begincl[env] := m + return +end + +procedure parse_body(body) + b := [] + body ? { + put(b, tab(upto('#') | 0)) + while ="#" do { + put(b, ="#" | (="$", raw_arg(argnum())) | converted_arg(argnum())) | + error("malformed macro arg #", tab(0)) + put(b, tab(upto('#') | 0)) + } + } + return b +end + +procedure argnum() + if any(&digits) then return integer(move(1)) else fail +end +#line 2472 "l2h.nw" +procedure l2h_macro(name, count, body[]) + count := integer(count) | + return error("must give # of arguments to l2h macro ", name) + s := "" + every s ||:= " " || (1(b := !body, type(b) == "string")) + s := s[2:0] # strip leading space if any + return macro(name, count, s) +end +#line 2482 "l2h.nw" +procedure l2h_environment(env, count, body[]) + count := integer(count) | + return error("must give # of arguments to l2h environment ", env) + s := "" + every s ||:= " " || (1(b := !body, type(b) == "string")) + s := s[2:0] # strip leading space if any + return begin_macro(env, count, s) +end +#line 2497 "l2h.nw" +record table_closure(args, amp, open, close) + +procedure table_env(env, args, amp, open, close) + begintab[env] := Ctable + begincl[env] := table_closure(args, amp, + if /close then tag(\open) | &null else open, + if /close then endtag(\open) | &null else close) + endtab[env] := Ctable_end + endcl[env] := [] +end +#line 2508 "l2h.nw" +procedure Ctable(S, env, cl) + local amp +## amp := S.ampersand +## S.ampersand := cl.amp + S.text(S, \cl.open) + push(endcl[env], amp) + cs_ignore(S, env, cl.args) +end + +procedure Ctable_end(S, env, cl) +# S.ampersand := pop(cl) + S.text(S, \begincl[env].close) +end +#line 2524 "l2h.nw" +procedure Cepsfig(S, cs, cl) + apply_args(S, do_epsfig, [S], 1) +end + +procedure do_epsfig(S, arg) + local args + args := [] + arg ? + while not pos(0) do { + tab(many(' \t\n')) + put(args, eqsplit(tab(upto(',') | 0))) + } + if a := !args & a.name == ("file"|"figure") then + emit_text(S, "<a href=" || image(a.value) || "><b>[</b>PostScript figure " || + a.value || "<b>]</b></a>") + else + emit_text(S, "<b>[</b>Ill-understood PostScript figure<b>]</b>") +end + +record apair(name, value) + +procedure eqsplit(s) + p := apair() + s ? (p.name := tab(upto('=')), ="=", p.value := tab(0)) + return p +end +#line 2552 "l2h.nw" +procedure Cincludegraphics(S, cs, cl) + local saved_ignore + saved_ignore := S.ignoring + S.ignoring := 1 + do_ignore(S, "[", saved_ignore, apply_args, [S, do_includegraphics, [S], 1]) +end + +procedure do_includegraphics(S, arg) + local base, ext + if arg ? (base := tab(find(ext := ".ps" | ".eps" | ".epsi")), =ext, pos(0)) then + emit_text(S, "<a href=" || image(arg) || "><b>[</b>PostScript figure " || + arg || "<b>]</b></a>") + else if arg ? (base := tab(find(ext := ".png" | ".gif" | ".jpg")), =ext, pos(0)) then + emit_text(S, "<img src=" || image(arg) || ">") + else + emit_text(S, "<b>[</b>Ill-understood graphics<b>]</b>") +end +#line 2574 "l2h.nw" +procedure let(lhs, rhs) + cstab[lhs] := cstab[rhs] + csclosure[lhs] := csclosure[rhs] +end + +procedure let_closure(lhs, cl[]) + csclosure[lhs] := if *cl = 1 then cl[1] else cl +end + +procedure letenv(lhs, rhs) + begintab[lhs] := begintab[rhs] + endtab[lhs] := endtab[rhs] + begincl[lhs] := begincl[rhs] + endcl[lhs] := endcl[rhs] +end +#line 2595 "l2h.nw" +procedure tag(html) + return "<" || html || ">" +end + +procedure endtag(html) + return "</" || html || ">" +end +#line 2605 "l2h.nw" +procedure Ccomment(S) + if *S.comment > 0 then { + S.text(S, "<!--") + S.comment ? { + while S.text(S, tab(find("--"))) do { + move(2) + S.text(S, "- - ") + } + S.text(S, tab(0)) + } + S.text(S, "-->") + } + S.comment := "" + return +end +#line 2621 "l2h.nw" +procedure Cparagraph(S) + S.text(S, "<p>") +end +#line 2625 "l2h.nw" +procedure Cmath(S) + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 2627 "l2h.nw" + S.text(S, "<i>") +end + +procedure Cmath_end(S) + S.text(S, "</i>") + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 2633 "l2h.nw" +end +#line 2635 "l2h.nw" +procedure Cdisplaymath(S) + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 2637 "l2h.nw" + S.text(S, "<blockquote><i>") +end + +procedure Cdisplaymath_end(S) + S.text(S, "</i></blockquote>") + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 2643 "l2h.nw" +end +#line 2645 "l2h.nw" +procedure Cmakeatletter(S) + S.csletters ++:= '@' +end + +procedure Cmakeatother(S) + S.csletters --:= '@' +end +#line 2655 "l2h.nw" +procedure Ckill(S, cs, cl) + S.the_text := "" +end +#line 2673 "l2h.nw" +global csfunctions +#line 2700 "l2h.nw" +global dynamic_add_hack +procedure parse_dynamic_add(S) + if (optwhite(), =("l2h"|"sl2h"), skipwhite(), + p := tab(upto(' \t')), +#line 2717 "l2h.nw" +((p := \csfunctions[p]) | +{ dynamic_warn(p); fail }) +#line 2703 "l2h.nw" + , + skipwhite(), any(~'\n')) then { + a := [] + while (any(~'\n'), l := tab(upto(' \t\n') | 0)) do { + put(a, if p === (l2h_macro|l2h_environment) then l else map(l, "#", " ")) + skipwhite() + } + dynamic_add_hack := S + p!a + dynamic_add_hack := &null + return + } +end +#line 2720 "l2h.nw" +procedure dynamic_warn(p) + static badprocs + initial badprocs := set() + if not member(badprocs, p) then { + write(&errout, "Warning: % l2h ", p, " not recognized -- ignored") + insert(badprocs, p) + } +end +#line 2732 "l2h.nw" +procedure optwhite() + suspend tab(many(' \t')) | "" +end +#line 2737 "l2h.nw" +procedure skipwhite() + suspend tab(many(' \t')) +end +#line 2829 "l2h.nw" +global errstatus +procedure error(args[]) + errstatus := 1 + return write!([&errout, (\curfile || ":") | "line ", curline, ": "] ||| args) +end +#line 2878 "l2h.nw" +procedure fatal(L[]) + write!(["@fatal l2h "] ||| L) + write!([&errout, "noweb error in l2h: "] ||| L) + exit(1) +end +#line 2885 "l2h.nw" +procedure rcsinfo () + return "$Id: l2h.nw,v 1.24 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 2745 "l2h.nw" +procedure main(args) + local line + errstatus := 0 + every arg := !args do + case arg of { + "-show-unknowns" : show_unknowns := 1 + "-html-quotes" : html_quotes := 1 + default : fatal("unknown arg ", image(arg)) + } + while line := read() do + apply(filter, line) + warn_unknown(\unknown_set, "control sequences", "\\") + warn_unknown(\unknown_envs, "environments", "{", "}") + if errstatus > 0 then + write("@fatal l2h Error occurred in l2h conversion") + exit(errstatus) +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 2771 "l2h.nw" +global curfile, curline + +procedure filter(name, arg) + static S, code + initial S := converter("V") +### write(" mode ", S.mode) + case name of { + "begin" : { +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2778 "l2h.nw" + ; if match("code ", arg) then code := 1} + "end" : {if match("docs ", arg) then +#line 2795 "l2h.nw" +if S.mode ~== "V" then write("@text <p>") +#line 2780 "l2h.nw" + +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2780 "l2h.nw" + ; code := &null; S.mode := "V"} + "quote" : { outtext("\0" ? convert(S)) } + "endquote" : { outtext("\1" ? convert(S)) } + "file" : { +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2783 "l2h.nw" + ; curfile := arg; curline := 1} + "line" : { +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2784 "l2h.nw" + ; curline := integer(arg)} + "defn" : { write("@", name, " ", convert_use_or_def(arg)) } + "use" : { write("@", name, " ", convert_use_or_def(arg)) } + "text" : {if \code then +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2787 "l2h.nw" + else outtext(arg ? convert(S)) } + "nl" : {if \code then +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2788 "l2h.nw" + else outtext("\n" ? convert(S)); curline +:= 1} + "fatal" : { +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2789 "l2h.nw" + ; exit(1)} + default : { +#line 2817 "l2h.nw" +write("@", name, (" " || \arg) | "") +#line 2790 "l2h.nw" + } + } + return +end +#line 2799 "l2h.nw" +procedure convert_use_or_def(s) + r := "" + s ? { + while r ||:= quickconv(tab(find("[["))) do { + (r ||:= ="[[") | fatal("impossible missing [[") + (r ||:= tab(find("]]")) || tab(many(']'))) | + fatal("impossible missing ]] in ", image(s)) + } + return r || quickconv(tab(0)) + } +end + +procedure quickconv(s) + static C + initial C := converter("H") + return 1(("{" || s || "}" ? convert(C)), reset(C)) +end +#line 2819 "l2h.nw" +procedure outtext(s) + s ? + while not pos(0) do + if ="\n" then write("@nl") + else if ="\0" then write("@quote") + else if ="\1" then write("@endquote") + else write("@text ", tab(upto('\n\0\1') | 0)) + return +end diff --git a/web/noweb/src/icon/l2h.nw b/web/noweb/src/icon/l2h.nw new file mode 100644 index 0000000000..268039c558 --- /dev/null +++ b/web/noweb/src/icon/l2h.nw @@ -0,0 +1,2898 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\documentclass{article} +\usepackage{noweb,multicol} + +\title{Converting {\LaTeX} to HTML} +\author{Norman Ramsey\\{\tt nr@eecs.harvard.edu}} + +\noweboptions{smallcode} +\setcounter{secnumdepth}{1} + +\begin{document} +@ +\pagenumbering{roman} +\maketitle +\tableofcontents +\pagenumbering{arabic} +@ +\section{Introduction} +This program provides an infrastructure for converting {\LaTeX} to +HTML. +That infrastructure can be used to make a {\tt noweb} filter or to +make a standalone conversion program. +The program is roughly divided into three parts. +Section~\ref{cs-decls} assigns a meaning (treatment) to each control +sequence. +The first part is roughly declarative, and in principle it could be + replaced by a data file. +The declarations can be augmented dynamically by putting formal +comments in a {\LaTeX} or noweb file---the so-called +``nifty \verb+% l2h+ escape hatch,'' which is utterly necessary and utterly +undocumented. +Section~\ref{engine} describes the engine used to do the conversion, +and +Sections \ref{imp-decl}~and~\ref{html-format} gives the procedures +that do the individual conversions. + +This program was motivated by my dissatisfaction with +\texttt{latex2html}. +In particular, \texttt{latex2html} is slow, and its output is full of +chartjunk. +Design decisions are driven by two goals: +\begin{itemize} +\item +Translate input eagerly as it is read in, in one pass whenever +possible. +For example, I take care to translate the body of [[\textbf{...}]] +without reading the entire argument. +Instead, I open the macro by emitting [[<b>]], and I note that a +corresponding [[</b>]] should be emitted when the matching close brace +is reached. +Most environments, including tables, are treated similarly. +The result is that \texttt{l2h} is \emph{fast}; it can comfortably +handle documents of hundreds of pages, whereas \texttt{latex2html} +starts bogging down at around 50~pages.% +\footnote{Measurement taken late in 1994} +\item +Use simple, natural translations to HTML whenever possible, and limit +functionality to simple translation. +For example, I do \emph{not} offer options to split documents up into +sections and to create hyperlinks between sections---that sort of +thing can and should be done by pure HTML tools. +Also, I make no real attempt to do anything fancy with equations or +pictures. +These things have no natural equivalents in HTML. +If you want to run {\LaTeX}, produce images, and embed them, look +elsewhere. +\end{itemize} +Finally, the program has to be able to translate a fragment at a time, +because it needs to work selectively on parts of the noweb pipeline. +For example, the argument to a {\LaTeX} control sequence might be +split into three pieces, one of which could be noweb quoted code. +Passing pieces in and getting results back incrementally +would be completely straightforward in a language with +message-passing concurrency, but we don't have that in Icon. +If I understood co-expressions, I might be able to use them, but +instead I use a continuation-passing, closure-passing style, +constantly creating actions to be executed on future input events. +Significant events include open and close braces as well as the simple +arrival of new text. +This style makes sense if one has some familiarity with continuations +\cite{appel:continuation,appel:compiling}, but for Icon programmers +it's bizarre. + + +{\bf Disclaimers}: +This program may be the worst I have ever written. +It started as a weekend hack, then took on a life of its own. +As I gradually assimilated the weirdness of writing +continuation-passing code in Icon, I came up with better and better +ways of doing things, but all the old ways are still here. +I really ought to clean things up and document them, but that would +not help me get tenure. +Please excuse the weak or absent documentation. + + + +@ +\section{Descriptions of control sequences} +\label{cs-decls} + +This section defines behavior for each control sequence we know how to +convert. +The definitions have a declarative flavor, since most are done by +procedure calls. +These calls initialize the machinery descriped in Section~\ref{cs-tables}. + +{\LaTeX} control sequences come first, using + the same organization as the quick reference card from the +second edition of the {\LaTeX} manual. +Other control sequences follow. +@ +\subsection{{\LaTeX} control sequences} +\subsubsection{Sentences and paragraphs} +<<control-sequence assignments>>= +substitution(",", " ") +substitution(" ", " ") +substitution("\n", "\n") +substitution("\t", " ") +ignore("raggedright") +ignore("@") +ignore("/") # no italic correction +substitution("", "\n") # \<newline> treated as request for newline +every c := !"$%#{}_" do + substitution(c, c) +substitution("&", "&") +argblock("emph", "em") +every argblock("footnote" | "footnotetext" | "thanks", + " <b>[</b>", "<b>]</b> ") # put footnotes in bold brackets +substitution("today", &date) +@ +\subsubsection{Type style} +<<control-sequence assignments>>= +ignore("textrm") # html can't switch to default font! +argblock("textit", "i") +argblock("textbf", "b") +argblock("textsl", "i") +ignore("textsc") +argblock("texttt", "tt") +ignore("textsf") +ignore("boldmath") +# \boldmath could be done by introducing S.mathfont, but I don't want to! +<<control-sequence assignments>>= +ignore("mathrm") # html can't switch to default font! +argblock("mathit", "i") +argblock("mathbf", "b") +argblock("mathtt", "tt") +ignore("mathsf") +argblock("mathcal", "i") +every ignore("scriptstyle"|"displaystyle"|"scriptscriptstyle") +every ignore("mathord"|"mathbin"|"mathrel") +@ HTML has only one size. +<<control-sequence assignments>>= +every ignore("tiny" | "scriptsize" | "footnotesize" | "small" | "normalsize" | + "large" | "Large" | "LARGE" | "huge" | "Huge") +@ +\subsubsection{Accents and symbols} +I've used a document from the W3~consortium to identify escapes for +symbols. +Older browsers won't support these symbols. +<<control-sequence assignments>>= +every accent(key(accent_name)) +every ignore("dag" | "ddag") +substitution("S", "§") +substitution("P", "¶") +substitution("copyright", "©") +substitution("pounds", "£") +substitution("o", "ø") +substitution("O", "Ø") +substitution("aa", "å") +substitution("AA", "Å") +substitution("ae", "æ") +substitution("AE", "&Aelig;") +@ +\subsubsection{Sectioning and table of contents} +<<control-sequence assignments>>= +argblockv("part", "h1", &null, "*[") +argblockv("chapter", "h1", &null, "*[") +argblockv("section", "h2", &null, "*[") +argblockv("subsection", "h3", &null, "*[") +argblockv("subsubsection", "h4", &null, "*[") +argblockv("paragraph", "h5", &null, "*[") +argblockv("subparagraph", "h6", &null, "*[") +ignore("appendix") +auxfile("tableofcontents", "toc", + "<p>\n<tableofcontents>\n<b>[Table of contents]</b>\n</tableofcontents>\n<p>", + "<h2>Table of Contents</h2>\n<tableofcontents>\n", "\n</tableofcontents>\n") +cstab["tableofcontents"] := Ctableofcontents # override to call set_toclevel +ignore("listoftables") +ignore("listoffigures") +ignore("addtocontents", "{{") +ignoreenv("filecontents", "{") +@ +\subsubsection{Mathematical formulas} +Here we see our first assignments to [[cstab]], which is the real +technology underlying these seemingly declarative calls. +I'll assign to [[cstab]] directly when some really special behavior is +called for. In this case, it's going in and out of math mode. +<<control-sequence assignments>>= +cstab["("] := Cmath +cstab[")"] := Cmath_end +cstab["["] := Cdisplaymath +cstab["]"] := Cdisplaymath_end +ignoreenv("equation") +every table_env(star("eqnarray"), 0, " ", "<blockquote><i>", "</i></blockquote>") + # also lame +substitution("frac", "<b>frac</b>") +substitution("sqrt", "<b>frac</b>") +every substitution("ldots" | "cdots" | "vdots", "...") +ignore("left") +ignore("right") +ignore("overline") +substitution(":", " ") +substitution(";", " ") +ignore("!") +@ The [[star]] procedure lets us define \verb+eqnarray+ and +\verb+eqnarray*+ in one fell swoop. +<<*>>= +procedure star(cs) + suspend cs | (cs || "*") +end +@ +There are a gazillion symbols. I'll add them on demand. +<<control-sequence assignments>>= +substitution("Diamond", "<>") +substitution("langle", "<") +substitution("rangle", ">") +substitution("le", "<=") +substitution("ne", "!=") +substitution("ge", ">=") +substitution("times", "×") +substitution("divide", "÷") +substitution("bmod", "</i>mod<i>") # better hook in with math +substitution("equiv", "===") +every x := "arccos" | "arcsin" | "arctan" | "arg" | "cos" | "cosh" | + "cot" | "coth" | "csc" | "deg" | "det" | "dim" | "exp" | + "gcd" | "hom" | "lim" | "liminf" | "limsup" | "ln" | "log" | + "max" | "min" | "sec" | "sin" | "sinh" | "sup" | "tan" | "tanh" +do + substitution(x, "</i>" || x || "<i>") +substitution("liminf", "</i>lim inf<i>") +substitution("limsup", "</i>lim sup<i>") +every x := "alpha" | "beta" | "gamma" | "delta" | "epsilon" | "zeta" | "eta" | + "theta" | "iota" | "kappa" | "lambda" | "nu" | "xi" | "pi" | + "rho" | "sigma" | "tau" | "upsilon" | "phi" | "chi" | "psi" | "omega" | + "in" +do + substitution(x, "<b>" || x || "</b>") +substitution("mu", "µ") +every x := "Gamma" | "Delta" | "Theta" | "Lambda" | "Xi" | "Pi" | "Sigma" | + "Upsilon" | "Phi" | "Psi" | "Omega" +do + substitution(x, "<b>" || x || "</b>") +@ +Here are some lonesome math symbols. +<<control-sequence assignments>>= +substitution("lfloor", "</i>|_<i>") +substitution("rfloor", "</i>_|<i>") +substitution("leq", "<=") +substitution("geq", ">=") +substitution("ll", "«") +substitution("gg", "»") +substitution("Rightarrow", "==>") +substitution("rightarrow", "-->") +substitution("approx", "<u>~</u>") +@ +\subsubsection{Displayed paragraphs} +HTML really has only one kind of displayed paragraph---the block quotation. +<<control-sequence assignments>>= +envblock("quote", "blockquote") +envblock("quotation", "blockquote") +envblock("center", "blockquote") +envblock("flushleft", "blockquote") +envblock("flushright", "blockquote") +envblock("verse", "blockquote") +verbatim("verbatim", escape_HTML_specials, "pre") +cstab["verb"] := Cverb +cstab["verb*"] := Cverb +csclosure["verb*"] := 1 +cstab["verbatiminput"] := Cverbatiminput +csclosure["verbatiminput"] := verbatim_cl(escape_HTML_specials) +@ \verb*+\verb* uses visible blanks+. +@ +\subsubsection{Lists} +<<control-sequence assignments>>= +cstab["item"] := Citem +csclosure["item"] := [item_cl("<li>", "", "<li>")] +listenv("itemize", "ul") +listenv("enumerate", "ol") +listenv("description", "dl") +@ +\subsubsection{Global or ignorable} +<<control-sequence assignments>>= +ignore("documentstyle", "[{") +ignore("documentclass", "[{") +ignore("usepackage", "[{") +ignore("pagestyle", "{") +ignore("thispagestyle", "{") +ignore("pagenumbering", "{") +ignore("newcounter", "{") +ignore("global") +ignore("etalchar") # used in the .bbl files: \newcommand{\etalchar}[1]{$^{#1}$} +@ +\subsubsection{Title page and abstract} +I could be clever and have \verb+\title+ have a side effect +that sticks in the right boilerplate when we see \verb+\begin{document}+, +but for now it's not worth the hassle. +<<control-sequence assignments>>= +argblockv("title", "h1") +argblockv("author","address") +argblockv("date", "b") +substitution("maketitle", "<!--title goes here-->") +ignoreenv("titlepage") +envblock("abstract", "<h2>Abstract</h2><blockquote>", "</blockquote>") +@ +\subsubsection{Cross-reference} +A more ambitious scheme would make labels anchor at preceding +sectioning commands, but it's hard to see how to do that in one pass. +Instead, I just use some conventional glyphs. +I use special procedures for the cross-references so I can have an +arrow pointing either forward or backward, depending on the direction +of the reference. +<<control-sequence assignments>>= +cstab["label"] := Clabel +cstab["ref"] := Cref +cstab["pageref"] := Cref +cstab["subpageref"] := Cref +cstab["chunklabel"] := Clabel +@ +\subsubsection{Bibliography and citation} +For the bibliography, I actually go grubbing for a {\tt .bbl} file if +I can find one. +<<control-sequence assignments>>= +ignore("bibliographystyle", "{") +auxfile("bibliography", "bbl", "<b>[BibTeX bibliography]</b>", &null, &null, "{") +envblock("thebibliography", "<h2>References</h2>", "", "{") +every cstab["cite" | "citeN" | "opencite" | "openciteN" | "citeyear"] := Ccite +cstab["bibitem"] := Cbibitem +ignore("newblock") +ignore("nocite", "{") +@ +\subsubsection{Splitting the input} +All input is ignored. Those things are in their own files. +<<control-sequence assignments>>= +every ignore("input" | "include" | "includeonly", "{") +# filecontents not done yet +@ +\subsubsection{Line breaking} +<<control-sequence assignments>>= +cstab["\\"] := Cbackback +substitution("linebreak", "<br>") +ignore("-") +ignoreenv("sloppypar") +ignore("sloppy") +@ +\subsubsection{Page breaking} +I simulate forced page breaks by horizontal rules. +<<control-sequence assignments>>= +substitution("pagebreak", "<hr>") +substitution("newpage", "<hr>") +substitution("clearpage", "<hr>") +ignore("enlargethispage", "*{") +@ +\subsubsection{Boxes} +<<control-sequence assignments>>= +ignore("mbox") +ignore("makebox", "([[") # ( comes from picture area +ignore("fbox") +ignore("framebox", "[[") # could insert horizontal rules, but why? +ignore("newsavebox", 1) +ignore("sbox", 2) +ignore("savebox", "{[[{") +ignore("usebox", 1) +envblock("minipage", "blockquote", &null, "[{") +argblock("parbox", "blockquote", &null, "[{") +@ +\subsubsection{Space} +<<control-sequence assignments>>= +ignore("hspace", "*{") +ignore("hfil") +ignore("hfill") +ignore("vspace", "*{") +ignore("vfil") +ignore("vfill") +@ +\subsubsection{Length} +<<control-sequence assignments>>= +ignore("newlength", "{") +ignore("setlength", "{{") +ignore("addtolength", "{{") +@ +\subsubsection{Pictures} +<<control-sequence assignments>>= +envblock("picture", "<b>[picture]</b>", "", "((") +ignore("put", "({") +ignore("multiput", "(({{") +ignore("dashbox", "{([") +ignore("line", "({") +ignore("vector", "({") +ignore("shortstack", "[") +ignore("circle", "*{") +ignore("oval", "([") +ignore("frame") +ignore("thinlines") +ignore("thicklines") +@ +\subsubsection{Figures and Tables} +I surround figures and tables with horizontal rules. +<<control-sequence assignments>>= +every envblock(star("figure"), "<hr>", "<hr>", "[") +every envblock(star("table"), "<hr>", "<hr>", "[") +argblock("caption", "b") # captions in bold +@ +\subsubsection{{\tt tabbing} environment} +I can't see how to do anything sensible with {\tt tabbing}. +<<control-sequence assignments>>= +envblock("tabbing", "blockquote") +# \= is accent +ignore(">") +ignore("+") +ignore("kill") +@ +\subsubsection{{\tt array} and {\tt tabular} environment} +<<control-sequence assignments>>= +tabular("array", "[") +tabular("tabular", "[") +tabular("tabularx", "{[") +tabular("tabular*", "{[") +every cstab["multicolumn"|"multispan"] := Cmulticolumn +cstab["span"] := Cspan +cstab["noalign"] := Cnoalign +cstab["hline"] := Chline +cstab["toprule"] := Chline +cstab["midrule"] := Chline +cstab["bottomrule"] := Chline +ignore("cline", "{") +ignore("newcolumntype", "{{") +@ +\subsubsection{Definitions} +<<control-sequence assignments>>= +ignore("newcommand", "A[[{") +ignore("renewcommand", "A[[{") +ignore("providecommand", "A[[{") +ignore("newenvironment", "{[{{") +ignore("renewenvironment", "{[{{") +ignore("newtheorem", "{{") +@ +\subsubsection{Numbering} +We have to have a special [[setcounter]] so we can ignore the right stuff +in the table of contents. +<<control-sequence assignments>>= +cstab["setcounter"] := Csetcounter +ignore("addtocounter", "{{") +@ +\subsubsection{Other {\LaTeX} control sequences} +<<control-sequence assignments>>= +cstab["makeatletter"] := Cmakeatletter +cstab["makeatother"] := Cmakeatother +@ +Here are all the old-style font changes. +<<control-sequence assignments>>= +every fontchange("tt" | "ttfamily", "tt") +every fontchange("bf" | "bfseries", "b") +every fontchange("it" | "itshape", "i") +fontchange("sl", "i") +fontchange("em", "em") +ignore("rm") # html can't switch to default font! +ignore("sf") +ignore("sc") +@ And some new ones +<<control-sequence assignments>>= +ignore("rmfamily") +ignore("normalfont") +<<control-sequence assignments>>= +ignoreenv("document") +<<control-sequence assignments>>= +substitution("LaTeX", "LaTeX") +<<control-sequence assignments>>= +ignore("numberline", "{") +ignore("protect") +ignore("onecolumn") +ignore("twocolumn", "C") +ignore("typeout", "[{") +ignore("closedbib") +<<control-sequence assignments>>= +every ignore("leftmargini" | "leftmarginii" | "labelsep" | "fboxsep", "=") +every ignore("tabcolsep", "=") +every ignore("evensidemargin" | "marginparsep" | "marginparwidth" | + "oddsidemargin" | "textheight" | "textwidth" | "topmargin", "=") +<<control-sequence assignments>>= +ignore("DeclareMathVersion", "{") +ignore("mathversion", "{") +ignore("setpapersize", "{") +ignore("setmarginsrb", "{{{{{{{{") +ignore("marginparwidth", "=") +ignore("marginparsep", "=") +@ +\subsection{Control sequences from various {\LaTeX} packages} +<<control-sequence assignments>>= +ignoreenv("multicols", "{C") +cstab["citeN"] := Ccite +ignore("afterpage", "{") +every cstab["psfig"|"epsfig"] := Cepsfig +cstab["includegraphics"] := Cincludegraphics +ignore("newcolumntype", "{{") +@ +A (perhaps vain) attempt to implement \verb+\kill+. +<<control-sequence assignments>>= +cstab["kill"] := Ckill +@ +\subsection{Plain {\TeX} control sequences} +<<control-sequence assignments>>= +activesubst("~", " ") +argblock("centerline", "<br>", "<br>") +substitution("cr", "<br>") +substitution("hrule", "<hr>") +substitution("vrule", "|") +substitution("hrulefill", "------") +ignore("hyphenation", "{") +ignore("hbox") +ignore("rlap") +ignore("llap") +ignore("vbox") +ignore("vtop") +ignore("hidewidth") +ignore("message", "{") +ignore("relax") +ignore("null") +ignore("offinterlineskip") +ignore("omit") +ignore("newdimen", "{") +ignore("nobreak") +<<control-sequence assignments>>= +cstab["par"] := implicit_paragraph +cstab["smallskip"] := implicit_paragraph +cstab["medskip"] := implicit_paragraph +cstab["bigskip"] := implicit_paragraph +cstab["vskip"] := implicit_paragraph +csclosure["vskip"] := "=" +@ +We can't give the grouping control sequences their real meaning, because +that would blow our brace balance when ignoring definitions and the like. +The proper solution would be to distinguish between grouping and braces, +but that would require much more sophistication than we've got just now. +<<control-sequence assignments>>= +every ignore("begingroup" | "endgroup" | "bgroup" | "egroup") +<<control-sequence assignments>>= +cstab["newif"] := Cnewif +cstab["iffalse"] := Ciffalse +cstab["iftrue"] := Ciftrue +cstab["ifhtml"] := Ciftrue # false in LaTeX, but true when converting! +cstab["else"] := Celse +cstab["fi"] := Cfi +cstab["ifx"] := cstab["if"] := cstab["ifnum"] := Ciffalse +@ +Lots of assignable things: +<<control-sequence assignments>>= +ignore("let", "A=") +every ignore("hfuzz" | "parindent" | "parskip" | "baselineskip", "=") +every ignore("hbadness" | "hsize" | "vsize" | "overfullrule" | "tabskip", "=") +every ignore("extrarowheight" | "codemargin", "=") +every ignore("looseness", "=") +substitution("hskip", " ", "=") +ignore("setbox", "{=") +every ignore("box" | "unhbox" | "unvbox", "{") +<<control-sequence assignments>>= +ignore("unskip") +ignore("hss") +ignore("phantom", "{") +every ignore("kern" | "lower" | "spacefactor", "=") # a cheat, but works +every ignore("clubpenalty" | "widowpenalty", "=") +@ +Backslashes and delimiters. +<<new l2h.nw init declarative statements>>= +substitution("backslash", "\\") +ignore("delimiter", "=") +@ +@ Other stuff to be ignored: +<<control-sequence assignments>>= +every ignore("expandafter" | "indent" | "noindent" | "leavevmode" | "strut") +ignore("def", 1) +<<control-sequence assignments>>= +substitution("TeX", "TeX") +substitution("BibTeX", "BibTeX") +substitution("MF", "METAFONT") +@ +\subsubsection{HTML support for \TeX\ [[\char]]} + +Process the numeric argument of a \TeX\ [[\char]] command that is of the form +`[[\char123]]' or `[[\char 123]]' (the [[\char]] has already been scanned, it +is no longer in the input) +and return the character with that code, which we hope will be ASCII +for reasonable implementations of Icon. +Also gobble any trailing whitespace. +<<control-sequence assignments>>= +cstab["char"] := asciiCharCode +<<*>>= +procedure asciiCharCode(S) + return emit_text(S, char(2(optwhite(), TeXnumber(), optwhite()))) +end +procedure TeXnumber() + return integer( + (="'", "8r" || tab(many(&digits))) | + (="\"", "16r" || tab(many(&digits))) | + tab(many(&digits))) +end +@ +\subsection{HTML support} +<<control-sequence assignments>>= +macro("nwanchorto", 2, "<a href=\"#$1\">#2</a>") +macro("nwanchorname", 2, "<a name=\"#$1\">#2</a>") +ignore("nwaddbox", "{") +verbatim("latexonly", do_nothing) +verbatim("rawhtml", emit_text) +@ +\subsection{Other control sequences} +Here's some stuff that might be plain {\TeX}. +<<control-sequence assignments>>= +substitution("quad", " ") +@ +I get to include my favorite {\TeX} hacks. +We define ignoring loosely; the count denotes the number of balanced-brace pairs. +We also ignore everything before an ignored balanced-brace pair, which means +it works for \verb+\def+. +<<control-sequence assignments>>= +ignore("noweboptions", 1) +@ +Now, here are a couple of righteous hacks! +The idea is that most views will ignore this stuff, but the indexer might +use it to get clever about dumping chunks and all in the right places. +<<control-sequence assignments>>= +substitution("nowebindex", "<nowebindex>") +substitution("nowebchunks", "<nowebchunks>") +ignore("nowebsize") +<<control-sequence assignments>>= +envblock("fields", "blockquote", &null, "[") # lame; could try to <tt> 1st col +envblock("fields*", "blockquote", &null, "{") # lame; could try to <tt> 1st col +ignore("citeauthoryear", "{{{") +ignore("authoryear", "{{") +substitution("bibrule", "--------") +let("bibskip", "par") +every cstab["anoncite"|"authorcite"] := Ccite +@ +This will always have to be patched by hand, but it may be worth it. +<<control-sequence assignments>>= +ignore("pssilent") +ignore("psnoisy") +@ + + +\section{The conversion engine} +\label{engine} + +The converter doesn't have the luxury of working on the whole text at +once; instead it has to accept and convert a piece at a time. +If I really understood co-expressions, I would surely make them sit up +and beg. +Since I don't, I keep some state around, and I pass continuations and +closures like there's no tomorrow. + + +@ +\subsection{Basic conversion} +Here's the basic engine, which works by string scanning. +The initial boilerplate sets up the second argument (if any) as +[[&subject]]. +The characters [[-]], [[']], and~[[`]] are special because they start +{\TeX} ligature. +We also have the odd specials [["\0"]] and [["\1"]], which are +used to delimit quoted code in noweb. +Woe betide the hapless user who has real nulls or 1s in his {\LaTeX} file. +<<*>>= +procedure convert(S, optstring) + static specials + initial { + <<initialization>> + <<control-sequence assignments>> + <<assign to dynamic-add table>> + specials := '\\{}<>-\'`"%$&~\n\0\1' + } + + if \optstring then return optstring ? convert(S) + else { + <<scan, convert, and return result>> + } +end +@ +If I were a good dog, I would make a state diagram. +Since I'm not, I'll just say that we either +accumulate text using the function [[S.text]], which exists for that +purpose, or else we do something special upon encountering a special character. + +The [[<<take actions appropriate to new text>>]] +chunk may do something special with the text in +case we're not in the default state (for example, we may be scanning +for the end of a comment). + +Encountering a non-threatening character throws the converter into +horizontal mode. +<<scan, convert, and return result>>= +<<take actions appropriate to new text>> +if S.mode == "V" & any(~'\\{}<>%\n\t ') then S.mode := "H" +emit_text(S, tab(upto(specials) | 0)) +while not pos(0) do + if S.mode == "Q" then { # quoting + emit_text(S, tab(upto('\1') | 0)) + if ="\1" then { + emit_text(S, "\1") + S.mode := S.quotemode + } + } else { + if any(S.activechars) then + do_activechar(S, move(1)) + else + case move(1) of { + "\\" : {<<control sequence>>} + "{" : {<<take open-group actions>>} + "}" : {<<take close-group actions>>} + "%" : {<<comment>>} + "\n" : {<<newline>>} + "$" : {<<dollar sign>>} + "&" : ampersand(S) + "\0" : {S.quotemode := S.mode; S.mode := "Q"; emit_text(S, "\0")} + # remaining cases simply escape HTML specials + "<" : emit_text(S, "<") + ">" : emit_text(S, ">") + "\"" : emit_text(S, """) + "-" : {<<deal with dash ligatures>>} + "`" : {<<deal with open quotes>>} + "'" : {<<deal with close quotes>>} + } + if S.mode == "V" & any(~'\\{}<>%\n\t ') then S.mode := "H" + emit_text(S, tab(upto(specials++S.activechars) | 0)) + } +return 1(. S.the_text, S.the_text := "") # what's been converted +<<deal with dash ligatures>>= +if S.mode == "V" then S.mode := "H" +if S.mode == "H" then + if ="--" then emit_text(S, "—") + else if ="-" then emit_text(S, "–") + else emit_text(S, "-") +else + emit_text(S, "-") +@ +<<*>>= +global html_quotes # render quotes as HTML 4 entities +<<deal with open quotes>>= +if S.mode == "V" then S.mode := "H" +if \html_quotes & S.mode == "H" then + if ="`" then emit_text(S, "“") + else emit_text(S, "‘") +else + emit_text(S, "`") +<<deal with close quotes>>= +if S.mode == "V" then S.mode := "H" +if \html_quotes & S.mode == "H" & ="'" then emit_text(S, "”") +else emit_text(S, "'") +@ +The definition of a converter's state is distributed. +We've already seen the use of [[mode]]. +<<*>>= +record state(mode, quotemode <<other fields of state>>) + # mode is Q, H, V, or M + # quotemode is saved mode: H, V, or M +@ +To create a new state, the default mode is vertical +<<*>>= +procedure converter(mode) + /mode := "V" + return state(mode, mode <<initial values for other fields of state>>) +end +@ +To avoid repeated memory allocation, we provide a routine to reset a +converter to its initial state. +<<*>>= +procedure reset(S) + <<code to reset [[S]]>> + return S +end +@ +The basic action performed by the +[[S.text]] function is to accumulate converted text in [[S.the_text]]. +[[S.text]] is usually [[accumulate_text]]. +<<*>>= +procedure accumulate_text(S, text) + S.the_text ||:= text + return +end +<<other fields of state>>= +, text, the_text +<<initial values for other fields of state>>= +, accumulate_text, "" +<<code to reset [[S]]>>= +S.text := accumulate_text +S.the_text := "" +@ +[[emit_text]] just uses the current value of [[S.text]], provided we aren't +currently ignoring tokens. +Its primary use is to appear in closures, when we don't know what +[[S.text]] will be when the closure is executed. +<<*>>= +procedure emit_text(S, text) + return if \S.ignoring then "" else S.text(S, text) +end +<<other fields of state>>= +, ignoring +<<initial values for other fields of state>>= +, &null +<<code to reset [[S]]>>= +S.ignoring := &null +@ +Active characters are like control sequences. +The only one active by default is the~[[~]]. +<<*>>= +global activetab, activeclosure +procedure do_activechar(S, c) + (activetab[c])(S, c, activeclosure[c]) + return +end +<<initialization>>= +activetab := table(unknown_cs) +activeclosure := table() +<<other fields of state>>= +, activechars +<<initial values for other fields of state>>= +, '~' +<<code to reset [[S]]>>= +S.activechars := '~' +@ +\subsection{Action and continuation hooks} +We provide hooks so that actions can be taken at various points. +The major ones are: +\begin{description} +\item[\tt newtext] +When the next string is passed in for conversion. +\item[open brace] +After the next open brace or begin environment. +\item[close brace] +Before the next close brace or end environment. +\end{description} +@ +\subsubsection{{\tt newtext}} +[[newtext]] is a list of closures to be executed (actions to take) + when the next input comes. +<<other fields of state>>= +, newtext +<<initial values for other fields of state>>= +, [] +<<code to reset [[S]]>>= +S.newtext := [] +@ +A closure is simply a procedure with arguments. +<<*>>= +record closure(proc, args) +@ +[[before_next_newtext]] and [[after_next_newtext]] +add to the list of actions to be taken (at the left and right, respectively). +<<*>>= +procedure before_next_newtext(S, proc, args) + push(S.newtext, closure(proc, args)) +end + +procedure after_next_newtext(S, proc, args) + put(S.newtext, closure(proc, args)) +end +@ +When taking the actions, be careful to avoid infinite loop, e.g., on empty lines. +<<take actions appropriate to new text>>= +l := S.newtext +S.newtext := [] +while c := get(l) do + c.proc!c.args +@ +Some control sequences temporarily override all actions to be taken on +a new input, using [[delay_newtext]]. + [[undelay_newtext]] restores actions. +<<*>>= +procedure delay_newtext(S) + push(S.delayed_newtext, S.newtext) + S.newtext := [] + return +end + +procedure undelay_newtext(S) + S.newtext := \pop(S.delayed_newtext) | + {write(&errout, "This can't happen: no delayed_newtext"); &null[0]} +end +<<other fields of state>>= +, delayed_newtext +<<initial values for other fields of state>>= +, [] +<<code to reset [[S]]>>= +S.delayed_newtext := [] +@ +\subsubsection{Opening and closing groups} +There's only one list of actions to be taken at the next open, +but there's a whole stack of lists of actions to be taken at closes. +<<other fields of state>>= +, open, closes +<<initial values for other fields of state>>= +, [], [] +<<code to reset [[S]]>>= +every S.open | S.closes := [] +<<*>>= +procedure after_next_open(S, proc, args) + return put(S.open, closure(proc, args)) +end + +procedure before_next_close(S, proc, args) + return push(S.closes[1], closure(proc, args)) # lost at top level +end + +procedure after_next_close(S, proc, args) + return put(S.closes[1], closure(proc, args)) # lost at top level +end +<<take open-group actions>>= +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +<<take close-group actions>>= +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +<<old>>= +procedure Cbegingroup(S, cs, cl) + <<take open-group actions>> +end +<<old>>= +procedure Cendgroup(S, cs, cl) + <<take close-group actions>> +end +<<old control-sequence assignments>>= +cstab["begingroup"] := Cbegingroup +cstab["endgroup"] := Cendgroup +cstab["bgroup"] := Cbegingroup +cstab["egroup"] := Cendgroup +@ +\subsection{Handling control sequences and environments} +OK, to eat a control sequence, first scan it, then execute it using [[do_cs]]. +[[S.csletters]] records the current set of ``letters'' for control +sequences (so we can interpret \verb+\makeatletter+). +<<control sequence>>= +cs := if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) +if /S.ignoring | cs == ("else"|"fi") | cstab[cs] === (Ciffalse|Ciftrue) then + do_cs(S, cs) +else + &null # error("### Ignoring \\", cs) +<<other fields of state>>= +, csletters +<<initial values for other fields of state>>= +, &letters +<<code to reset [[S]]>>= +S.csletters := &letters +@ +To execute a control sequence, look up its procedure in [[cstab]], +and pass in the name of the control sequence, plus the closure +argument from [[csclosure]]. +\label{cs-tables} +<<*>>= +global cstab, csclosure + +procedure do_cs(S, cs) + tab(many(' \t')) # skip white space following CS + if pos(0) | any('\n') then before_next_newtext(S, skipblanks, [S]) + (cstab[cs])(S, cs, csclosure[cs]) + return +end +<<initialization>>= +cstab := table(unknown_cs) +csclosure := table() +@ +The default action for an unknown control sequence is [[unknown_cs]]. +If the global [[show_unknowns]] is set we dump the control sequence into the +output in bold. We save the unknown sequences for later warning messages. +<<*>>= +global show_unknowns +procedure unknown_cs(S, cs, cl) +# if S.text === ignore_text then return # a bit of a hack -- should no longer be needed + if \show_unknowns then S.text(S, "<b>\\" || cs || "</b>") + if not member(unknown_set, cs) then { + write(\unknown_file, "Warning: unknown control sequence \\", cs) + insert(unknown_set, cs) + } + return +end +<<initialization>>= +unknown_set := set() +<<*>>= +global cstab, csclosure, unknown_set +@ +The control sequences \verb+\begin+ and \verb+\end+ are treated +specially, +so we can have a similar machinery for environments. +<<*>>= +global begintab, endtab, begincl, endcl + +procedure do_begin(S, cs, cl) + (="{", env := tab(upto('}')), ="}") | error("botched \\begin{...}") + <<take open-group actions>> + (begintab[env])(S, env, begincl[env]) + return +end + +procedure do_end(S, cs, cl) + (="{", env := tab(upto('}')), ="}") | error("botched \\end{...}") + # write(&errout, "calling ", image(endtab[env]), " for \\end{", env, "}") + (endtab[env])(S, env, endcl[env]) + <<take close-group actions>> + return +end +<<control-sequence assignments>>= +cstab["begin"] := do_begin +cstab["end"] := do_end +<<initialization>>= +every begintab | endtab := table(unknown_env) +every begincl | endcl := table() +<<*>>= +procedure unknown_env(S, env, cl) +### if S.text === ignore_text then return # a bit of a hack # no longer needed + if \show_unknowns then S.text(S, "<b>{" || env || "}</b>") + if not member(unknown_envs, env) then { + write(\unknown_file, "Warning: unknown environment {", env, "}") + insert(unknown_envs, env) + } + return +end +<<initialization>>= +unknown_envs := set() +<<*>>= +global unknown_envs +@ +\subsection{Issuing warnings about unknown control sequences and environments} +<<*>>= +procedure warn_unknown(s, type, mark, rmark) + if *s > 0 then { + pushout("Unknown " || type || ": ") + every pushout(((\mark | "")\1) || !sort(s) || ((\rmark | "")\1) || " ") + pushout("\n") + } +end +<<*>>= +procedure pushout(s) + static col + initial col := 0 + if find("\n", s) then + s ? { + pushout(tab(upto('\n'))) + while ="\n" do {col := 0; write(&errout)} + pushout(tab(0)) + } + else { + col +:= *s + if col >= 79 then {writes(&errout, "\n "); col := *s + 2} + writes(&errout, s) + } + return +end +@ +\subsection{Procedures related to parsing {\TeX}} +\subsubsection{Comment-skipping} +This logic gobbles text into [[S.comment]] +until a newline is encountered, at which point it calls +[[Ccomment]] to format the comment. +All other new-text actions go on hold until the comment is over. +<<comment>>= +parse_dynamic_add(S) +delay_newtext(S) +eat_comment(S) +<<*>>= +procedure eat_comment(S) + S.comment ||:= tab(upto('\n') | 0) + if pos(0) then + before_next_newtext(S, eat_comment, [S]) + else { + undelay_newtext(S) + Ccomment(S) + S.comment := "" + } + return +end +<<other fields of state>>= +, comment +<<initial values for other fields of state>>= +, "" +<<code to reset [[S]]>>= +S.comment := "" +@ +Verbatim text is a little bit like comment text---we keep swallowing +under special rules until we find a terminator. +There are at least three classes of rules: +\begin{itemize} +\item +Copy text, but escape the HTML specials. +This corresponds to an ordinary {\LaTeX} \texttt{verbatim} +environment. +\item +Copy text while changing nothing. +This correspondes to a \texttt{rawhtml} environment. +\item +Throw everything on the floor. +This corresponds to a \texttt{latexonly} environment. +\end{itemize} +We store an output method, a string that terminates the environment, +and possibly tag for an HTML wrapper around the environment. +<<*>>= +record verbatim_cl(output, terminator, html, translate_blank) + +procedure verbatim(name, output, html) + begintab[name] := Cverbatim + begincl [name] := verbatim_cl(output, &null, html) + return +end + +procedure Cverbatim(S, cs, cl) + if cl === begincl[cs] & /cl.terminator then + cl := begincl[cs] := + verbatim_cl(cl.output, "\\end{" || cs || "}", cl.html, cl.translate_blank) + emit_text(S, tag(\cl.html)) + delay_newtext(S) + do_verbatim(S, cl) + return +end +@ +If we find the terminator, we're finished. +Otherwise, we swallow the whole input and make sure our action on next +input is to continue scanning. +<<*>>= +procedure do_verbatim(S, cl) + if cl.output(S, tab(find(cl.terminator)), cl) then { + =cl.terminator + emit_text(S, endtag(\cl.html)) + undelay_newtext(S) + } else { + cl.output(S, tab(0), cl) + before_next_newtext(S, do_verbatim, [S, cl]) + } + return +end +@ +When writing verbatim text, we still have to convert HTML specials. +<<*>>= +procedure escape_HTML_specials(S, s, cl) + s ? { + while emit_text(S, tab(upto('&<>" '))) do + case move(1) of { + "\"" : emit_text(S, """) + "&" : emit_text(S, "&") + "<" : emit_text(S, "<") + ">" : emit_text(S, ">") + " " : emit_text(S, if \cl.translate_blank then "·" else " ") + } + emit_text(S, tab(0)) + } + return +end +@ +The \verb+\verb+ control sequence's terminator is the first character +following \verb+\verb+. +<<*>>= +procedure Cverb(S, cs, cl) + Cverbatim(S, cs, verbatim_cl(escape_HTML_specials, move(1), "tt", cl)) + return +end +@ +<<*>>= +procedure Cverbatiminput(S, cs, cl) + local filename, f, line + if ="{" & filename := tab(upto('}')) & ="}" then { + f := \open(filename) | { emit_text(S, "<strong>Could not read file</strong> <tt>") + escape_HTML_specials(S, filename, cl) + emit_text(S, "</tt>") + return + } + emit_text(S, "\n<pre>\n") + while line := read(f) do { + escape_HTML_specials(S, line, cl) + emit_text(S, "\n") + } + emit_text(S, "</pre>\n") + close(f) + } + return +end +@ +\subsubsection{Arguments} +It's occasionally necessary to collect the argument of a control +sequence. +[[csarg]] does the job. +<<*>>= +procedure csarg(S) + return 2(="{", tab(bal('}', '{', '}')), ="}") | + (optwhite(), + if ="\\" then + "\\" || (tab(many(S.csletters)) | move(1)) + else + move(1)) +end +@ +[[csarg()]] works only if the whole +argument is in the same line; otherwise it only returns the opening curly +brace, `[[{]]'. Another problem with [[csarg()]] is that it does not cope with +[[%]] or [[\]] in the input (due to the use of the Icon function [[bal()]] to +balance curly brackets), as such a escaped or commented out curly brace is +handled incorrectly. + +The solution provided here is not trivial. The problem is that if we have +`[[\foo{bar]]' in a line and the `[[baz}]]' is in another line then due to the +way how l2h works the Icon command associated with [[foo]] will have to +terminate before the `[[baz}]]' gets read, and as such cannot do anything +useful except register a callback to finish the job. + +@ + +[[apply_arg(S, cl)]] scans an argument (preceded by optional +whitespace), then invokes the closure on that argument. +Its use should subsume [[csarg]], but that may take a while yet. +<<*>>= +@ +[[apply_args(S, p, as, n]] scans [[n]] arguments from the +input, puts them in a list [[args]], then calls [[p!(as ||| args)]]. +This is a bit weak, because we really want to turn off comment +skipping for some arguments. +N.B. the arguments are \emph{not} converted. +<<*>>= +procedure apply_args(S, p, as, args_wanted) + delay_newtext(S) + do_apply_args(S, closure(p, as), args_wanted, [], "", 0) + return +end + +procedure do_apply_args(S, cl, wanted_count, args_seen, current_arg, brace_depth) + local open_comment + # invariant : we have an open brace +# pushtrace("APPLY") + while *args_seen < wanted_count & not pos(0) do { + while *args_seen < wanted_count & brace_depth = 0 & not pos(0) do { + tab(many(' \t\n')) + case c := move(1) of { + "\\" : put(args_seen, "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1)) + "{" : { current_arg := "" ; brace_depth := 1 } + "}" : { error("Insufficient arguments to macro ", macro.name) } + "%" : if tab(upto('\n')) then ="\n" else open_comment := tab(0) + default : put(args_seen, c) + } + } + while brace_depth > 0 & not pos(0) do { + current_arg ||:= tab(upto('\\{}%') | 0) + case move(1) of { + "%" : if tab(upto('\n')) then ="\n" else open_comment := tab(0) + "\\" : current_arg ||:= "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) + "{" : { current_arg ||:= "{" ; brace_depth +:= 1 } + "}" : { brace_depth -:= 1 + if brace_depth > 0 then + current_arg ||:= "}" + else { + put(args_seen, current_arg) + current_arg := "" + } + } + } + } + } + if *args_seen = wanted_count then { + undelay_newtext(S) + cl.proc ! (cl.args ||| args_seen) + } else if \open_comment then { + delay_newtext(S) + before_next_newtext(S, skip_comment_and_continue, + [S, closure(do_apply_args, [S, cl, wanted_count, args_seen, + current_arg, brace_depth])]) + } else + before_next_newtext(S, do_apply_args, [S, cl, wanted_count, args_seen, + current_arg, brace_depth]) +# poptrace() + return +end +<<*>>= +procedure skip_comment_and_continue(S, cl) + tab(upto('\n') | 0) + if pos(0) then + before_next_newtext(S, skip_comment_and_continue, [S, cl]) + else { + ="\n" + undelay_newtext(S) +# <take actions appropriate to new text>> + } + return +end +@ +\subsubsection{Misc specials} +Ampersands are covered in the table section (\ref{tabular}). +@ +The dollar sign is for entering and exiting math mode: +<<dollar sign>>= +if /S.ignoring then + if ="$" then + if S.mode == "M" then { Cdisplaymath_end(S); S.mode := "V" } + else { Cdisplaymath(S); S.mode := "M" } + else + if S.mode == "M" then { Cmath_end(S); S.mode := "H" } + else { Cmath(S); S.mode := "M" } +@ +Newlines emit themselves, plus start skipping blanks until they get to +some nonblank text. +We have to identify a blank line so we can insert a paragraph marker. +<<newline>>= +emit_text(S, "\n") +if /S.ignoring then Cnewline(S) +<<*>>= +procedure Cnewline(S) + tab(many(' \t')) + if match("\n") then implicit_paragraph(S) + if pos(0) then before_next_newtext(S, Cnewline, [S]) +end +@ +Other procedures might want to skip white space, which includes +newlines, but we don't want to miss a paragraph. +<<*>>= +procedure skipblanks(S) + tab(many(' \t')) + if ="\n" then Cnewline(S) + else if pos(0) then before_next_newtext(S, skipblanks, [S]) +end +@ +Paragraphs count only in horizontal or math mode (and they better not +happen in math mode!). +<<*>>= +procedure implicit_paragraph(S, cs, cl) + if S.mode ~== "V" then { + S.mode := "V" + Cparagraph(S) + } + cs_ignore(S, cs, \cl) +end +@ +Here's a real hack. I use it to stop skipping blanks when the noweb +filter sees text quoted by [[[[...]]]]. +That text is never converted, but we don't want to skip blanks that +follow it. +<<*>>= +procedure stop_skipping(S) + while S.newtext[1].proc === (Cnewline|skipblanks) do pop(S.newtext) +end +@ +\subsubsection{Items} +For items, we actually want to do something with the optional arguments, +namely, convert them. +We wrap them in braces so that any font changes and so on will be +appropriately limited in their effects. +<<*>>= +record item_cl(before, after, ifnone) + +procedure Citem(S, cs, cl) + if pos(0) then + after_next_newtext(S, Citem, [S, cs, cl]) + else if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", convert_bracketed, cl) + } else { + skipblanks(S) + emit_text(S, cl[1].ifnone) + \liststack[1] := \liststack[1] + 1 + } +end +<<*>>= +procedure convert_bracketed(S, contents, cl) + emit_text(S, cl[1].before || + convert(converter("H"), "{" || contents || "}") || + cl[1].after) + optwhite() +end +<<*>>= +global liststack, suspendstack +<<initialization>>= +liststack := [] +# write(&errout, "Initialized liststack") +suspendstack := [] +<<control-sequence assignments>>= +cstab["suspend"] := Csuspend +cstab["resume"] := Cresume +<<*>>= +procedure listenv(env, html) + begintab[env] := Clist + begincl[env] := html + endtab[env] := Clist_end + endcl[env] := html +end + +procedure push_item_closure(cs) + push(csclosure["item"], + if cs == "description" then item_cl("<dt>", "<dd>", "<dt><dd>") + else item_cl("<li>", "--", "<li>")) +end + +procedure Clist(S, cs, cl) + push(liststack, 1) + push_item_closure(cs) + emit_text(S, tag(cl)) +end + +procedure Cresume(S, cs, cl) + local n, env + (="{", env := tab(upto('}')), ="}") | error("botched \\resume{...}") + n := pop(suspendstack) | error("\\resume not balanced by corresponding \\suspend") + push(liststack, n) + push_item_closure(env) + if env == "enumerate" then + emit_text(S, tag(begincl[env] || " start=\"" || n || "\"")) + else + emit_text(S, tag(begincl[env])) +end + +procedure Clist_end(S, cs, cl) + emit_text(S, endtag(cl)) + pop(csclosure["item"]) + pop(liststack) +end + +procedure Csuspend(S, cs, cl) + local env + (="{", env := tab(upto('}')), ="}") | error("botched \\suspend{...}") + emit_text(S, endtag(endcl[env])) + pop(csclosure["item"]) + push(suspendstack, pop(liststack)) +end +@ +\subsubsection{Labels and references} +These could be done by [[argblock]], except I want to make it possible to have +different text depending on whether the references point forward or backward. +<<*>>= +global labels_seen + +procedure Clabel(S, cs, cl) + initial /labels_seen := set() + insert(labels_seen, l := csarg(S)) | fail + emit_text(S, "<a name=\"" || l || "\"><b>[*]</b></a>") +end + +procedure Cref(S, cs, cl) + local prefix, prefix_tag + initial /labels_seen := set() + prefix_tag := (\cl)[1] | "" + prefix := (\cl)[2] | "" + l := prefix || csarg(S) | fail + emit_text(S, prefix_tag || "<a href=\"#" || l || "\">[" || + (if member(labels_seen, l) then "<-" else "->") || "]</a>") +end +@ +\subsubsection{Citations} +The important thing about a citation key is that it makes a hot line +to the appropriate item in the bibliography. +[[Ccite]] and [[Cbibitem]] work together to make it happen. + +Optional arg might contain blanks, so it might be split, but + I assume the citation key isn't split between inputs. +<<*>>= +procedure Ccite(S, cs, cl, bracketed_text) + if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", do_cite, cl) + } else + do_cite(S, &null, cl) +end + +procedure do_cite(S, commentary, cl) + local key + if \commentary then + optwhite() + if pos(0) then before_next_newtext(S, do_cite, [S, commentary, cl]) + else { + key := csarg(S) + \commentary := convert(converter("H"), "{" || \commentary || "}") + emit_text(S, "<b>[cite ") + key ? { + while k := tab(upto(",")) & ="," do + emit_text(S, "<a href=\"#NWcite-" || k || "\">" || k || "</a>, ") + if k := tab(0) then + emit_text(S, "<a href=\"#NWcite-" || k || "\">" || k || "</a>") + } + emit_text(S, ", <i>" || \commentary || "</i>") + emit_text(S, "]</b>") + } +end +<<*>>= +procedure Cbibitem(S, cs, cl) + local label, key + static counter + initial counter := 0 + if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", finish_bibitem, []) + } else { + label := "<b>[" || (counter +:= 1) || "]</b>" + apply_args(S, do_bibitem_key, [S, label], 1) + } +end + +procedure do_bibitem_key(S, label, key) + return emit_text(S, "<a name=\"NWcite-" || key || "\">" || label || "</a> ") +end + +procedure finish_bibitem(S, contents, args) + local key, label + optwhite() + label := convert(converter("H"), "{" || contents || "}") + key := apply_args(S, do_bibitem_key, [S, label], 1) +end +@ +\subsubsection{Conditionals} +The idea here is that an \verb+\if+$\cdots$ control sequence will conditionally +ignore text, and that \verb+\fi+ restores the previous state. +To keep track of state, we have an ``if stack'' that records what +[[S.text]] should be upon encountering \verb+\else+ and \verb+\fi+. +<<other fields of state>>= +, ifstack +<<initial values for other fields of state>>= +, [] +<<code to reset [[S]]>>= +if *S.ifstack > 0 then S.ifstack := [] # keeps GC down +@ +What's on the ifstack is +<<*>>= +record ifrec(on_else, on_fi) +@ It's possible that one day this code will need to be updated to delay +new-text actions (and to do God knows what if +new-text actions have already been delayed). +@ +Every \verb+\if+$\cdots$ is equivalent either to \verb+\iffalse+ +of \verb+\iftrue+, so we begin by defining those, as well as \verb+\else+ +and \verb+\fi+ +<<*>>= +procedure Ciffalse(S, cs, cl) +#error("### \\", cs, " -> false (S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}, ")") + push(S.ifstack, ifrec(S.ignoring, S.ignoring)) + S.ignoring := 1 +end + +procedure Ciftrue(S, cs, cl) +#error("### \\", cs, " -> true (S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}, ")") + push(S.ifstack, ifrec(1, S.ignoring)) +end + +procedure Celse(S, cs, cl) + S.ignoring := S.ifstack[1].on_else +#error("### \\else -> S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}) +end + +procedure Cfi(S, cs, cl) + S.ignoring := S.ifstack[1].on_fi +#error("### \\fi -> S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}) + pop(S.ifstack) +end +@ +Now, all that's left is to handle \verb+\newif+. +This part is all boilerplate. +<<*>>= +procedure Cnewif(S, cs, cl) + local newif, newcs + tab(many(' \t\n')) + if pos(0) then + after_next_newtext(S, Cnewif, [S, cs, cl]) + else { + newif := csarg(S) + newif ? + if ="\\if" & newcs := tab(many(S.csletters)) & pos(0) then { + <<make [[newcs]] a new \verb+\if+-like thing>> + } else + error("\\newif argument botch: " || newif) + } +end +@ +And here we do the real work: +<<make [[newcs]] a new \verb+\if+-like thing>>= +cstab[newcs || "false"] := Csetif +cstab[newcs || "true"] := Csetif +cstab["if" || newcs] := Ciffalse +<<*>>= +procedure Csetif(S, cs, cl) + local base, tag + if cs ? (base := tab(find("true"|"false")), tag := =("true"|"false"), pos(0)) then { + cstab["if" || base] := if tag == "true" then Ciftrue else Ciffalse + } else { + error("This can't happen --- setif botch (not urgent)") + } +end +@ +\subsection{Upper case} + +This is a very simple implementation of [[\uppercase]]: it requires to +have all of its argument immediately. +<<*>>= +procedure Cuppercase(S, cs, cl) + l := map(csarg(S), &lcase, &ucase) | fail + emit_text(S, l) +end +<<control-sequence assignments>>= +cstab["uppercase"] := Cuppercase +@ +\subsection{HTML support for array and tabular environments} +We handle tables by using [[S.text]] to implement a little state machine. +There are only two states: waiting to start a new cell, and the +ordinary state of converting text. + +The rest of the state information is held in a list of [[table_info]] +records that tell us what to expect for the next cell. +<<*>>= +record table_info(index, # number of this cell in the row + alignment, # the alignment of this cell + width, # how many columns this cell will span + alignments, # default alignments for this table + brace_depth, # size of S.closes after start of cell + cell_text) # value of S.text to use to scan this cell +@ This state could conceivably be extended to include pre- and +post-content for each cell, \`a la plain {\TeX}'s [[\halign]] or the +{\LaTeX} [[<{}]] and [[>{}]] directives, but for now I won't bother. + +I should probably also add a [[rows_taken]] field and use it to +implement [[multirow]] support. +@ +Here's a stack that keeps track of all currently active tabular environments. +<<other fields of state>>= +, tables +<<initial values for other fields of state>>= +, [] +<<code to reset [[S]]>>= +S.tables := [] +@ +Accumulating text forces the transition between states. +While I'm at it, I update the state for the next cell. +<<*>>= +procedure start_table_cell(S, text) + local this, attributes + + text ? { + tab(many(' \t\n')) + if pos(0) then return + } +# write(&errout, "starting cell with ", image(text)) + + this := S.tables[1] | fatal("starting cell with no current table") + S.text := this.cell_text + +if /(\this).brace_depth then write(&errout, "starting table cell, ", image(this), " has null brace depth") + # use this to start the current cell + if this.index = 1 then emit_text(S, "<tr>") + attributes := \this.alignment | aligneq("top") + if this.width > 1 then attributes ||:= " colspan=" || this.width + emit_text(S, "<td" || attributes || ">") + + # now update state for the next cell + this.index +:= this.width # advance to next cell + this.alignment := this.alignments[this.index] | &null + this.width := 1 + # can't set cell_text until we hit & + + <<take open-group actions>> + emit_text(S, text) + return +end +@ +Hitting an ampersand closes and opens groups, and it advances to the +next cell. +<<*>>= +procedure ampersand(S) + local this + this := S.tables[1] + <<take close-group actions>> + if /this then + emit_text(S, " --- ") + else { + emit_text(S, "") # be sure cell gets started, even if empty + emit_text(S, "</td>") + if S.text ~=== start_table_cell then + this.cell_text := S.text + S.text := start_table_cell + this.brace_depth := *S.closes + 1 # will open at start of cell +#write(&errout, "set brace depth for ", image(this)) + } + tab(many(' \t\n')) +## write(&errout, " past &, text = ", image(S.text), ", next = ", +## image(&subject[&pos:0])) + return +end +@ +The double backslash is the end of a row, unless it's buried in braces +or there's no table. +We have to be careful about ignoring a square bracket, because if the +[[\\]] is at the end of a line, we won't know until we see the newline +that it's not a bracket, and we don't see the newline until we get the +next text. +We therefore must use a continuation-passing style for this ignore. +<<*>>= +procedure Cbackback(S, cs, cl) + local this + this := S.tables[1] + cs_ignore(S, cs, "[", Cbackback_continue, [S, this]) +end + +procedure Cbackback_continue(S, this) +#if /(\this).brace_depth then write(&errout, image(this), " has null brace depth") + if /this | *S.closes > this.brace_depth then { # ordinary \\ + S.text(S, "<br>") + } else { # row terminator +## write(&errout, "ending row with ", image(&subject[&pos:0])) + emit_text(S, "") # be sure cell gets started, even if empty + <<take close-group actions>> + emit_text(S, "</td></tr>\n") + tab(many(' \t\n')) + if S.text ~=== start_table_cell then + this.cell_text := S.text + this.index := 1 + this.alignment := this.alignments[this.index] | &null + this.width := 1 + S.text := start_table_cell + this.brace_depth := *S.closes + 1 # about to open + } +end +@ +A horizontal line disappears if it's in a table. +<<*>>= +procedure Chline(S, cs, cl) + if \S.tables[1] then return + else emit_text(S, "<hr>") + return +end +@ +An [[\end{tabular}]] terminates the whole affair. +If we're at the beginning of a row, things are easy. +Otherwise, we terminate the current row first. +<<*>>= +procedure Ctabular_end(S, cs, cl) + local this + if S.text ~=== start_table_cell | S.tables[1].index > 1 then {# row in progress + emit_text(S, "") # be sure cell gets started, even if empty + <<take close-group actions>> + emit_text(S, "</td></tr>") + } + if S.text === start_table_cell then # abort it + S.text := S.tables[1].cell_text + emit_text(S, "</table>") + xxx := pop(S.tables) +#write(&errout, "popped ", image(xxx)) + return +end +@ +Finally, the setup of the table itself: +<<*>>= +procedure Ctabular(S, cs, cl) + cs_ignore(S, cs, cl, Ctabular_continue, [S]) + return +end + +procedure Ctabular_continue(S) + a := csarg() # alignment +#write(&errout, "Alignment ", a) + emit_text(S, if upto('|', a) then "<table border>" else "<table>") + emit_text(S, "<!-- alignment is " || a || "-->") + a := alignments(a) + emit_text(S, "<!-- " || *a || " columns-->") + push(S.tables, table_info(1, a[1] | "l", 1, a, *S.closes+1, S.text)) +#write(&errout, "pushed ", image(S.tables[1])) + S.text := start_table_cell + optwhite() + return +end +@ Earlier, the initial value of [[S.tables[1].brace_depth]] was +[[&null]], but when we had alignment of \verb+{c}+, it was never +getting set, so I'm setting it on startup, even though I'm not sure if +that's really right. +<<*>>= +procedure tabular(env, ignore) + begintab[env] := Ctabular + begincl[env] := ignore + endtab[env] := Ctabular_end + endcl[env] := ignore +end +@ +We figure alignments using the tricks in the {\LaTeX} book. +<<*>>= +procedure aligneq(a) + return " align=\"" || a || "\"" +end + +procedure valigneq(a) + return " valign=\"" || a || "\"" +end + +procedure alignments(s) + a := [] + s ? { + while not pos(0) do + case move(1) of { + "l" | "X" | "Y" | "p" : + { put(a, aligneq("left") || valigneq("top")); skip_bracket() } + "c" : put(a, aligneq("center")) + "r" : put(a, aligneq("right")) + "m" : { put(a, aligneq("left") || valigneq("center")); skip_bracket() } + "b" : { put(a, aligneq("left") || valigneq("bottom")); skip_bracket() } + "@" | "<" | ">" | "!" : skip_bracket() + "|" : &null + default : &null # unrecognized... + } + } + return a +end +<<*>>= +procedure skip_bracket() + if ="{" then { + n := 1 + while n > 0 & not pos(0) do { + tab(upto('{}\\') | 0) + case move(1) of { + "{" : n +:= 1 + "}" : n -:= 1 + "\\" : move(1) + } + } + } + return +end +@ +[[\multicolumn]] changes the width and alignment of the current cell. +[[\multispan]] changes only the width. +<<*>>= +procedure Cmulticolumn(S, cs, cl) + local this + this := S.tables[1] + n := integer(csarg()) | error("\\multicolumn or \\multispan not followed by integer") + if cs == "multicolumn" then a := alignments(csarg()) +# write(&errout, "\\", cs, " n = ", n, ", a = ", (\a)[1] | "???", +# ", text = ", image(S.text)) + if /this then return # \multicolumn without table? + this.width := n + this.alignment := (\a)[1] + return +end + +procedure Cspan(S, cs, cl) + (\S.tables[1]).width +:= 1 + return +end +@ +<<*>>= +procedure Cnoalign(S, cs, cl) + apply_args(S, finish_noalign, [S], 1) + return +end + +procedure finish_noalign(S, arg) + return if \S.ignoring then "" else + accumulate_text(S, "<br>" || convert(converter("V"), "{" || arg || "}") || "<br>") +end +@ +\subsection{Reading and converting auxiliary {\LaTeX} files} +<<*>>= +procedure auxfile(cs, ext, placeholder, header, trailer, ignore) + cstab[cs] := Cauxfile + csclosure[cs] := aux_cl(ext, placeholder, header, trailer, \ignore | "") +end +@ +[[Cauxfile]] succeeds if it finds a file, fails otherwise. +<<*>>= +record aux_cl(ext, placeholder, header, trailer, ignore) + +procedure Cauxfile(S, cs, cl) + local auxfile, T + if auxfile := open(basename(\curfile) || "." || cl.ext) then { + T := converter("V") + Cmakeatletter(T) + S.text(S, \cl.header) + while line := read(auxfile) do + S.text(S, convert(T, line || "\n")) + close(auxfile) + S.text(S, \cl.trailer) + } else { + S.text(S, \cl.placeholder) + } + cs_ignore(S, cs, cl.ignore) + if \auxfile then return +end +<<*>>= +procedure basename(name) + reverse(name) ? { + tab(upto('.')) & ="." + return reverse(tab(0)) + } +end +@ +\subsubsection{Table of contents} +We can build a table of contents by reading the .toc file. +Sadly, I haven't figured out how to get hot links yet. +<<control-sequence assignments>>= +cstab["contentsline"] := Ccontentsline +<<*>>= +procedure Ctableofcontents(S, cs, cl) + S.mode := "V" + Cauxfile(S, cs, cl) + set_toclevel(S) +end +@ +[[set_toclevel]] manages the starting and ending of lists. +With no level argument, it resets the toc to the initial level. +<<*>>= +procedure set_toclevel(S, l) + static toclevel, initiallevel + if /initiallevel := \l then + S.text(S, "<ul compact>") + if /l := \initiallevel then + S.text(S, "</ul>") + if /l then return # never set a level + /toclevel := l + while toclevel < l do { + S.text(S, "<ul compact>") + toclevel +:= 1 + } + while toclevel > l do { + S.text(S, "</ul>") + toclevel -:= 1 + } + return +end +@ +Assume one table of contents per converted document. +<<*>>= +procedure Ccontentsline(S, cs, cl) + local type, level + static leveltab + initial { <<assign numbers of sections in leveltab>> } + l := \leveltab[csarg()] | fail + if l > \countertab["tocdepth"] then + cs_ignore(S, cs, "{{") # skip this one + else { + set_toclevel(S, l) + S.text(S, "<li>") + after_next_open(S, after_next_close, [S, cs_ignore, [S, cs, "{"]]) + } +end +<<assign numbers of sections in leveltab>>= +l := ["part", "chapter", "section", "subsection", "subsubsection", + "paragraph", "subparagraph"] +leveltab := table() +every i := 1 to *l do + leveltab[l[i]] := i - 2 # making section level 1 +@ +\subsubsection{Counters} +<<*>>= +global countertab + +procedure Csetcounter(S, cs, cl) + local counter + (counter := csarg(), countertab[counter] := integer(csarg())) | + cs_ignore(S, cs, "{{") +end +<<initialization>>= +countertab := table() +@ +\subsubsection{Accents} +This info is taken from the HTML RFC, section entitled +``ISO Latin~1 character entities.'' +<<*>>= +global accent_name, accent_valid +<<initialization>>= +accent_name := table() +accent_valid := table('') +accent_name ["`"] := "grave" +accent_valid["`"] := 'AEIOUaeiou' +accent_name ["'"] := "acute" +accent_valid["'"] := 'AEIOUYaeiouy' +accent_name ["^"] := "circ" +accent_valid["^"] := 'AEIOUaeiou' +accent_name ["hat"] := "circ" +accent_valid["hat"] := 'AEIOUaeiou' +accent_name ["\""] := "uml" +accent_valid["\""] := 'AEIOUaeiouy' +accent_name ["~"] := "tilde" +accent_valid["~"] := 'ANOano' +accent_name ["="] := "bar" +accent_name ["."] := "dot" +accent_name ["u"] := "u" +accent_name ["v"] := "v" +accent_name ["H"] := "H" +accent_name ["t"] := "t" +accent_name ["c"] := "cedil" +accent_valid["c"] := 'Cc' +accent_name ["d"] := "underdot" +accent_name ["b"] := "underbar" +@ +Initialization calls [[accent]] to indicate that a control +sequence represents an accent. +In fact, [[accent]] is called on all keys of [[accent_name]]. +<<*>>= +procedure accent(cs) + cstab[cs] := Caccent +end + +procedure Caccent(S, cs, cl) + static warned + initial warned := table() + arg := csarg(S) | return + if arg == "\\i" then arg := "i" + if arg == "\\j" then arg := "j" + if *arg = 1 & any(accent_valid[cs], arg) then + S.text(S, "&" || arg || accent_name[cs] || ";") + else { + <<warn about [[cs]] with [[arg]]>> + S.text(S, arg) + } +end +<<warn about [[cs]] with [[arg]]>>= +/warned[cs] := set() +if not member(warned[cs], arg) then { + write(&errout, "Warning: Can't handle \\", cs, " with arg `", arg, "'") + insert(warned[cs], arg) +} +@ +\subsection{Font changes} +A font change changes the font until the next close, when we need to emit +the appropriate end tag. +<<*>>= +procedure fontchange(tex, html) + cstab[tex] := Cfontchange + csclosure[tex] := html +end +<<*>>= +procedure Cfontchange(S, tex, html) + S.text(S, tag(html)) + before_next_close(S, emit_text, [S, endtag(html)]) +end +@ +\section{Implementations of declaratives} +\label{imp-decl} + +\subsection{Ignoring stuff} +There are several different kinds of things that can be ignored: +ordinary arguments, +balanced-brace arguments, optional arguments, assignments (which may +include dimensions), stars, and parenthesized coordinates. +We ignore a sequence of these things by supplying a template to +[[ignore]], in which each character stands for something to be ignored. +We've already seen examples of these things in Section~\ref{cs-decls}. + +We can ignore arguments of control sequences or environments. +In either case, [[cs_ignore]] does the work. +<<*>>= +procedure ignore(cs, template) + /template := "" + cstab[cs] := cs_ignore + csclosure[cs] := template +end + +procedure ignoreenv(env, template) + /template := "" + begintab[env] := cs_ignore + begincl[env] := template + endtab[env] := do_nothing +end +@ +Because ignoring may span many inputs, all [[cs_ignore]] does is set things +up to call [[do_ignore]]. +The major setup is replacing [[S.text]] with a function that does nothing. +Oh, and it converts an integer template +into that many arguments, for historical reasons. +<<*>>= +procedure cs_ignore(S, cs, template, proc, args) + local saved_ignore + saved_ignore := S.ignoring + S.ignoring := 1 + if type(template) == "integer" then template := repl("{", template) + return do_ignore(S, template, saved_ignore, proc, args) +end +@ +Some things are easily ignored (partly because we assume they don't +span inputs). For others, we have special procedures. +The brace-ignoring stuff uses the open and close hooks, because braces +can be nested deeply. +If non-null, [[proc]] is applied to [[args]] after everything is ignored. +<<*>>= +procedure do_ignore(S, template, saved_ignore, proc, args) + if *template > 0 then + if optwhite() & pos(0) then + after_next_newtext(S, do_ignore, [S, template, saved_ignore, proc, args]) + else + case template[1] of { + "{" : { S.ignoring := 1 + after_next_open(S, ignore_til_close, + [S, template[2:0], saved_ignore, proc, args]) + } + "A" : { csarg(S) # had better be in one input + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + "[" : if optwhite() & ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", ignore_bracket_plus, + [S, template[2:0], saved_ignore, proc, args]) + } else + do_ignore(S, template[2:0], saved_ignore, proc, args) + "C" : # a total cheat, means ``copy optional arg'' + if optwhite() & ="[" then { + S.ignoring := &null + delay_newtext(S) + with_upto_bracket(S, "", copy_bracket_plus, + [S, template[2:0], saved_ignore, proc, args]) + } else + do_ignore(S, template[2:0], saved_ignore, proc, args) + "=" : { delay_newtext(S) + eat_assignment(S, do_ignore, [S, template[2:0], saved_ignore, proc,args]) + } + "*" : { (="*", optwhite()) + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + "(" : { (="(", tab(upto(')')), =")", optwhite()) + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + } + else { + S.ignoring := saved_ignore + (\proc)!(\args) + } +end + +procedure ignore_til_close(S, template, saved_ignore, proc, args) + before_next_close(S, do_ignore, [S, template, saved_ignore, proc, args]) +end +@ +Finally, at the end of an ignored environment, do nothing. +<<*>>= +procedure do_nothing(S, cs, cl) + return +end +@ +\subsubsection{Parsing bracketed (optional) arguments} +We may have to deal with optional arguments that are split across lines. +We pass in a continuation for the bracket. +This is a lot like gobbling to a newline, which we had to do with a comment. +As in the other case, we do something stupid if the bracket is +protected (e.g. by a backslash or comment char). +<<*>>= +procedure with_upto_bracket(S, bracketed_text, proc, args) + bracketed_text ||:= tab(upto(']') | 0) + if pos(0) then + before_next_newtext(S, with_upto_bracket, [S, bracketed_text, proc, args]) + else { + ="]" + undelay_newtext(S) + (\proc)(S, bracketed_text, args) + } + return +end +@ +To ignore brackets: +<<*>>= +procedure ignore_bracket_plus(S, contents, args) + # contents are ignored + do_ignore!args +end +@ and to copy them +<<*>>= +procedure copy_bracket_plus(S, contents, args) + local should_ignore + should_ignore := args[3] | fail # saved_ignore arg to do_ignore + if /should_ignore then + S.text(S, convert(converter("H"), "{" || contents || "}")) + do_ignore!args +end +@ +\subsubsection{Ignoring assignments} +Assignments are tricky because they might involve numbers, control +sequences, dimensions, or even glue. +We approximate the syntax from page 275 in the \TeX book. +<<*>>= +procedure eat_assignment(S, proc, args) + static decimal_chars, hex_chars, oct_chars + initial { + decimal_chars := &digits ++ '.,+-' + hex_chars := &digits ++ 'abcdefABCDEF' + oct_chars := '0124567' + } + optwhite() + ="=" # so what if we swallow multiple = signs + optwhite() + if pos(0) then { + before_next_newtext(S, eat_assignment, [S, proc, args]) + return + } else if glue() then { # finished + } else if any(decimal_chars) then { + tab(many(decimal_chars)) + optwhite() + if ="\\" then + tab(many(S.csletters)) | move(1) + # else assume assignment of the form \hangafter=2 + } else if ="\"" then { + tab(many(hex_chars)) & optwhite() + } else if ="\'" then { + tab(many(oct_chars)) & optwhite() + } else if =("\\"|"`\\") then + tab(many(S.csletters)) | move(1) + undelay_newtext(S) + (\proc)!args +end +<<*>>= +procedure dimen() + static decimal_chars + initial decimal_chars := &digits ++ '.,' + suspend (optwhite(), + if any('+-') then (move(1), optwhite()) else "", + tab(many(decimal_chars)), optwhite(), + (="true", optwhite()) | &null, + =("em"|"ex"|"pt"|"pc"|"in"|"bp"|"cm"|"mm"|"dd"|"cc"|"sp"|"mu")) +end +<<*>>= +procedure glue() + suspend (dimen(), + (optwhite(), ="plus", dimen()) | "", + (optwhite(), ="minus", dimen()) | "") +end +@ +\subsection{Substitution} + +\subsubsection{Simple substitution for a single control sequence} + +Even simple substitution isn't so simple, because in addition to the +HTML that we substitute for the {\TeX}, we can also supply a template +of stuff to be ignored (like the optional argument to \verb+\\+). +<<*>>= +procedure substitution(tex, html, ignore_template) + # ignore mode for now + cstab[tex] := Cemit_ig + csclosure[tex] := emit_ig_cl(html, \ignore_template | "") +end +@ +The closure contains HTML to be written and a template to be ignored. +<<*>>= +record emit_ig_cl(html, template) + +procedure Cemit_ig(S, cs, cl) + emit_text(S, cl.html) + if *cl.template > 0 then + cs_ignore(S, cs, cl.template) +end +@ +\subsubsection{Substitution for active characters} +<<*>>= +procedure activesubst(char, html, ignore_template) + local S + # ignore mode for now + activetab[char] := Cemit_ig + activeclosure[char] := emit_ig_cl(html, \ignore_template | "") + S := \dynamic_add_hack | return + if upto(S.activechars, char) then return # already active + if S.activechars ++:= cset(char) then { + before_next_close(S, delete_active_char, [S, char]) + } else impossible("ugh") + return +end + +procedure delete_active_char(S, char) + S.activechars --:= char + return +end +@ +\subsubsection{Substitution for environments} +The [[envblock]] procedure has two forms: +\begin{itemize} +\item +{}[[envblock(]]{\it environment}, {\it tag}[[)]] simply uses +begin- and end-{\it tag} in place of the environment. +\item +{}[[envblock(]]{\it environment}, {\it left}, {\it right}, {\it +ignore}[[)]] +puts the {\it left} text at the beginning of the environment, the {\it +right} text at the end, plus at the beginning of the environment it +ignores the arguments described by {\it ignore}. +\end{itemize} +It's easier to implement than to describe. +<<*>>= +procedure envblock(env, left, right, ignore_template) + /ignore_template := "" + begintab[env] := Cemit_ig + begincl[env] := emit_ig_cl(if /right then tag(left) else left, ignore_template) + endtab[env] := Cemit + endcl[env] := if /right then endtag(left) else right +end +@ [[Cemit]] emits text with nothing to ignore. +<<*>>= +procedure Cemit(S, cs, cl) + S.text(S, cl) +end +@ +\subsubsection{Substitution around arguments of control sequences} +These substitutions place tags at the beginning and end of arguments +to control sequences, instead of surrounding the contents of an +environment. +For example, they specify how to convert [[\section{...}]] to +[[<h1>...</h1>]] and so forth. +The calling convention is as for [[envblock]]. +<<*>>= +record blockpair(left, right, ignore) + +procedure argblock(tex, html, right, ignore) + # called as is envblock + /ignore := "" + cstab[tex] := Cblock + csclosure[tex] := + if /right then blockpair (tag(html), endtag(html), ignore) + else blockpair (html, right, ignore) +end +@ There is a fine point; control sequences labelled with [[argblockv]] +should put the converter into vertical mode. +<<*>>= +procedure argblockv(tex, html, right, ignore) + argblock(tex, html, right, ignore) + cstab[tex] := CblockV +end +<<*>>= +procedure Cblock(S, cs, cl, done_ignoring) + if /done_ignoring & *cl.ignore > 0 then { + cs_ignore(S, cs, cl.ignore, Cblock, [S, cs, cl, 1]) + } else if pos(0) then { + after_next_newtext(S, do_cs, [S, cs, cl]) + } else if match("{") then { + S.text(S, cl.left) + after_next_open(S, before_next_close, [S, emit_text, [S, cl.right]]) + } else { +# S.text(S, cl.left || csarg(S) || cl.right) + apply_args(S, Cblock_continue, [S, cl], 1) + } + return +end + +procedure Cblock_continue(S, cl, title) + S.text (S, cl.left || title || cl.right) + return +end +<<*>>= +procedure CblockV(S, cs, cl) + S.mode := "V" + Cblock(S, cs, cl) + return +end +@ +\subsubsection{Macro substitution} +I'm taking the plunge and describing a ghastly macro language. +Macros have arguments, a body, and an optional terminal mode. +The final mode, if non-null, is the mode to which the conversion +engine should be set. +<<*>>= +record macro_defn(name, arg_count, body, mode) +@ The body is a list of items, where an item may be a raw argument, a +converted argument, or a string. +<<*>>= +record raw_arg(number) +record converted_arg(number, mode) +@ +<<*>>= +procedure expand_macro(S, macro, args) + every a := !macro.body do + case type(a) of { + "string" : emit_text(S, a) + "raw_arg" : emit_text(S, args[a.number]) | impossible("missing arg") + "converted_arg" : S.text(S, convert(S, "{" || args[a.number] || "}")) + } +# poptrace() + return +end +#link pushtrace +@ +Scan arguments and hope comments in arguments just work out. Ha ha. +<<*>>= +procedure do_macro(S, macro, args_seen, current_arg, brace_depth) + # invariant : we have an open brace +# write(&errout, "scanning args for macro ", macro.name) +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count) + while *args_seen < macro.arg_count & not pos(0) do { + while *args_seen < macro.arg_count & brace_depth = 0 & not pos(0) do { +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + tab(many(' \t\n')) + case c := move(1) of { + "\\" : put(args_seen, "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1)) + "{" : { current_arg := "" ; brace_depth := 1 } + "}" : { error("Insufficient arguments to macro ", macro.name) } + default : put(args_seen, c) + } + } + while brace_depth > 0 & not pos(0) do { +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + current_arg ||:= tab(upto('\\{}') | 0) + case move(1) of { + "\\" : current_arg ||:= "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) + "{" : { current_arg ||:= "{" ; brace_depth +:= 1 } + "}" : { brace_depth -:= 1 + if brace_depth > 0 then + current_arg ||:= "}" + else { + put(args_seen, current_arg) + current_arg := "" + } + } + } + } + } +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + if *args_seen = macro.arg_count then { +# write(&errout, "Arguments for macro ", macro.name, ":") +# every write(&errout, "\t", image(!args_seen)) + expand_macro(S, macro, args_seen) + undelay_newtext(S) + } else + before_next_newtext(S, do_macro, [S, macro, args_seen, current_arg, brace_depth]) + return +end +@ +<<*>>= +procedure Cmacro(S, cs, cl) +# pushtrace("MACRO") + delay_newtext(S) +# apply_args(S, closure(expand_macro, [cl]), cl.arg_count) + do_macro(S, cl, [], "", 0) + return +end +@ +Now, a {\TeX}-like macro facility in which [[#]] is used for converted +parameters and [[#$]] for raw ones. +<<*>>= +procedure macro(name, arg_count, body, mode) + m := macro_defn(name, arg_count, parse_body(body), mode) + cstab[name] := Cmacro + csclosure[name] := m + return +end + +procedure begin_macro(env, arg_count, body, mode) + m := macro_defn(env, arg_count, parse_body(body), mode) + begintab[env] := Cmacro + begincl[env] := m + return +end + +procedure parse_body(body) + b := [] + body ? { + put(b, tab(upto('#') | 0)) + while ="#" do { + put(b, ="#" | (="$", raw_arg(argnum())) | converted_arg(argnum())) | + error("malformed macro arg #", tab(0)) + put(b, tab(upto('#') | 0)) + } + } + return b +end + +procedure argnum() + if any(&digits) then return integer(move(1)) else fail +end +@ +And the dynamic version\ldots +<<*>>= +procedure l2h_macro(name, count, body[]) + count := integer(count) | + return error("must give # of arguments to l2h macro ", name) + s := "" + every s ||:= " " || (1(b := !body, type(b) == "string")) + s := s[2:0] # strip leading space if any + return macro(name, count, s) +end +@ +<<*>>= +procedure l2h_environment(env, count, body[]) + count := integer(count) | + return error("must give # of arguments to l2h environment ", env) + s := "" + every s ||:= " " || (1(b := !body, type(b) == "string")) + s := s[2:0] # strip leading space if any + return begin_macro(env, count, s) +end +@ +\subsection{Table environments} +For tables, we not only have an HTML tag, we also supply some text +for the ampersand. +[[args]] is a template describing the arguments to the environment, +which are ignored. +<<*>>= +record table_closure(args, amp, open, close) + +procedure table_env(env, args, amp, open, close) + begintab[env] := Ctable + begincl[env] := table_closure(args, amp, + if /close then tag(\open) | &null else open, + if /close then endtag(\open) | &null else close) + endtab[env] := Ctable_end + endcl[env] := [] +end +<<*>>= +procedure Ctable(S, env, cl) + local amp +## amp := S.ampersand +## S.ampersand := cl.amp + S.text(S, \cl.open) + push(endcl[env], amp) + cs_ignore(S, env, cl.args) +end + +procedure Ctable_end(S, env, cl) +# S.ampersand := pop(cl) + S.text(S, \begincl[env].close) +end +@ +\subsection{Postscript} +<<*>>= +procedure Cepsfig(S, cs, cl) + apply_args(S, do_epsfig, [S], 1) +end + +procedure do_epsfig(S, arg) + local args + args := [] + arg ? + while not pos(0) do { + tab(many(' \t\n')) + put(args, eqsplit(tab(upto(',') | 0))) + } + if a := !args & a.name == ("file"|"figure") then + emit_text(S, "<a href=" || image(a.value) || "><b>[</b>PostScript figure " || + a.value || "<b>]</b></a>") + else + emit_text(S, "<b>[</b>Ill-understood PostScript figure<b>]</b>") +end + +record apair(name, value) + +procedure eqsplit(s) + p := apair() + s ? (p.name := tab(upto('=')), ="=", p.value := tab(0)) + return p +end +@ +<<*>>= +procedure Cincludegraphics(S, cs, cl) + local saved_ignore + saved_ignore := S.ignoring + S.ignoring := 1 + do_ignore(S, "[", saved_ignore, apply_args, [S, do_includegraphics, [S], 1]) +end + +procedure do_includegraphics(S, arg) + local base, ext + if arg ? (base := tab(find(ext := ".ps" | ".eps" | ".epsi")), =ext, pos(0)) then + emit_text(S, "<a href=" || image(arg) || "><b>[</b>PostScript figure " || + arg || "<b>]</b></a>") + else if arg ? (base := tab(find(ext := ".png" | ".gif" | ".jpg")), =ext, pos(0)) then + emit_text(S, "<img src=" || image(arg) || ">") + else + emit_text(S, "<b>[</b>Ill-understood graphics<b>]</b>") +end +@ +\subsection{Control-sequence assignment} +This procedure is available to be used for dynamic assignment. +One day we might use it to parse \verb+\let+ as well. +<<*>>= +procedure let(lhs, rhs) + cstab[lhs] := cstab[rhs] + csclosure[lhs] := csclosure[rhs] +end + +procedure let_closure(lhs, cl[]) + csclosure[lhs] := if *cl = 1 then cl[1] else cl +end + +procedure letenv(lhs, rhs) + begintab[lhs] := begintab[rhs] + endtab[lhs] := endtab[rhs] + begincl[lhs] := begincl[rhs] + endcl[lhs] := endcl[rhs] +end +@ +\section{HTML formatting} +\label{html-format} + +First, generic procedures used to create beginning and ending tags. +<<*>>= +procedure tag(html) + return "<" || html || ">" +end + +procedure endtag(html) + return "</" || html || ">" +end +@ +Next, a gazillion formatting procedures. +<<*>>= +procedure Ccomment(S) + if *S.comment > 0 then { + S.text(S, "<!--") + S.comment ? { + while S.text(S, tab(find("--"))) do { + move(2) + S.text(S, "- - ") + } + S.text(S, tab(0)) + } + S.text(S, "-->") + } + S.comment := "" + return +end +<<*>>= +procedure Cparagraph(S) + S.text(S, "<p>") +end +<<*>>= +procedure Cmath(S) + <<take open-group actions>> + S.text(S, "<i>") +end + +procedure Cmath_end(S) + S.text(S, "</i>") + <<take close-group actions>> +end +<<*>>= +procedure Cdisplaymath(S) + <<take open-group actions>> + S.text(S, "<blockquote><i>") +end + +procedure Cdisplaymath_end(S) + S.text(S, "</i></blockquote>") + <<take close-group actions>> +end +<<*>>= +procedure Cmakeatletter(S) + S.csletters ++:= '@' +end + +procedure Cmakeatother(S) + S.csletters --:= '@' +end +@ +Approximate \verb+\kill+ by eliminating text. +<<*>>= +procedure Ckill(S, cs, cl) + S.the_text := "" +end +@ +\section{Support for adding control sequences dynamically} + +The idea is to use formal comments of the form: +\begin{quote} +\verb+% l2h function arg arg ...+ +\end{quote} +These comments have the same effect as the procedure calls in +the chunk [[<<control-sequence assignments>>]]. +@ +Our first step is to create a table with the names of the functions we +recognize. +Ordinarly this table would be distributed, but I created it after the +fact with a little quick Unix pipeline. +<<*>>= +global csfunctions +<<initialization>>= +csfunctions := table() +<<assign to dynamic-add table>>= +csfunctions["argblock"] := argblock +csfunctions["argblockv"] := argblockv +csfunctions["envblock"] := envblock +csfunctions["fontchange"] := fontchange +csfunctions["ignore"] := ignore +csfunctions["ignoreenv"] := ignoreenv +csfunctions["let"] := let +csfunctions["letenv"] := letenv +csfunctions["listenv"] := listenv +csfunctions["substitution"] := substitution +csfunctions["activesubst"] := activesubst +csfunctions["closure"] := let_closure +csfunctions["let_closure"] := let_closure +csfunctions["newcommand"] := l2h_macro +csfunctions["macro"] := l2h_macro +csfunctions["environment"] := l2h_environment +csfunctions["tabular"] := tabular +@ +Now, the tough issue is how to parse arguments. I'm going to try the +following initial strategy: arguments are separated by spaces. +To put a space within an argument, use \verb+#+. There is no way to +put a \verb+#+ within an argument. +<<*>>= +global dynamic_add_hack +procedure parse_dynamic_add(S) + if (optwhite(), =("l2h"|"sl2h"), skipwhite(), + p := tab(upto(' \t')), <<make [[p]] a good function or warn and [[fail]]>>, + skipwhite(), any(~'\n')) then { + a := [] + while (any(~'\n'), l := tab(upto(' \t\n') | 0)) do { + put(a, if p === (l2h_macro|l2h_environment) then l else map(l, "#", " ")) + skipwhite() + } + dynamic_add_hack := S + p!a + dynamic_add_hack := &null + return + } +end +<<make [[p]] a good function or warn and [[fail]]>>= +((p := \csfunctions[p]) | +{ dynamic_warn(p); fail }) +<<*>>= +procedure dynamic_warn(p) + static badprocs + initial badprocs := set() + if not member(badprocs, p) then { + write(&errout, "Warning: % l2h ", p, " not recognized -- ignored") + insert(badprocs, p) + } +end +@ +\section{Miscellanous utilities} +[[optwhite]] skips and returns optional white space. +<<*>>= +procedure optwhite() + suspend tab(many(' \t')) | "" +end +@ [[skipwhite]] insists that there must be some white space. +<<*>>= +procedure skipwhite() + suspend tab(many(' \t')) +end +@ +\section{Main program for a noweb filter} +First, this is how we use the converter as a noweb filter. +<<l2h.icn>>= +<<*>> +procedure main(args) + local line + errstatus := 0 + every arg := !args do + case arg of { + "-show-unknowns" : show_unknowns := 1 + "-html-quotes" : html_quotes := 1 + default : fatal("unknown arg ", image(arg)) + } + while line := read() do + apply(filter, line) + warn_unknown(\unknown_set, "control sequences", "\\") + warn_unknown(\unknown_envs, "environments", "{", "}") + if errstatus > 0 then + write("@fatal l2h Error occurred in l2h conversion") + exit(errstatus) +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +@ +This is noweb filter machinery. I really ought to coordinate quoted text +with the converter (so it always shows up in the right place), +but so far I'm too lazy. +<<l2h.icn>>= +global curfile, curline + +procedure filter(name, arg) + static S, code + initial S := converter("V") +### write(" mode ", S.mode) + case name of { + "begin" : {<<out>>; if match("code ", arg) then code := 1} + "end" : {if match("docs ", arg) then <<possible paragraph>> + <<out>>; code := &null; S.mode := "V"} + "quote" : { outtext("\0" ? convert(S)) } + "endquote" : { outtext("\1" ? convert(S)) } + "file" : {<<out>>; curfile := arg; curline := 1} + "line" : {<<out>>; curline := integer(arg)} + "defn" : { write("@", name, " ", convert_use_or_def(arg)) } + "use" : { write("@", name, " ", convert_use_or_def(arg)) } + "text" : {if \code then <<out>> else outtext(arg ? convert(S)) } + "nl" : {if \code then <<out>> else outtext("\n" ? convert(S)); curline +:= 1} + "fatal" : {<<out>>; exit(1)} + default : {<<out>>} + } + return +end +<<possible paragraph>>= +if S.mode ~== "V" then write("@text <p>") +@ A special function is needed to implement {\tt noweb}'s quoting +convention within chunk names. +<<l2h.icn>>= +procedure convert_use_or_def(s) + r := "" + s ? { + while r ||:= quickconv(tab(find("[["))) do { + (r ||:= ="[[") | fatal("impossible missing [[") + (r ||:= tab(find("]]")) || tab(many(']'))) | + fatal("impossible missing ]] in ", image(s)) + } + return r || quickconv(tab(0)) + } +end + +procedure quickconv(s) + static C + initial C := converter("H") + return 1(("{" || s || "}" ? convert(C)), reset(C)) +end +<<out>>= +write("@", name, (" " || \arg) | "") +<<l2h.icn>>= +procedure outtext(s) + s ? + while not pos(0) do + if ="\n" then write("@nl") + else if ="\0" then write("@quote") + else if ="\1" then write("@endquote") + else write("@text ", tab(upto('\n\0\1') | 0)) + return +end +<<*>>= +global errstatus +procedure error(args[]) + errstatus := 1 + return write!([&errout, (\curfile || ":") | "line ", curline, ": "] ||| args) +end +@ +\section{Main program for a simple converter} +<<sl2h.icn>>= +<<*>> +global curfile, curline + +procedure convert_file(f) + static S + initial S := converter("V") + curline := 0 + while line := read(f) do { + curline +:= 1 + writes(convert(S, line || "\n")) + } + return +end + +procedure main(args) + local warn + warn := 1 + errstatus := 0 + every arg := !args do + if arg[1] == "-" then + case arg of { + "-show-unknowns" : show_unknowns := 1 + "-nowarn" : warn := &null + "-html-quotes" : html_quotes := 1 + "-" : { curfile := arg; convert_file(&input) } + default : write(&errout, "Warning: unrecognized option ", arg) + } + else if f := open(curfile <- arg) then + convert_file(f) + else + write(&errout, "Error: Can't open file ", arg) + if /curfile then + convert_file(&input) + if \warn then { + warn_unknown(\unknown_set, "control sequences", "\\") + warn_unknown(\unknown_envs, "environments", "{", "}") + } + exit(errstatus) +end +@ +<<*>>= +procedure fatal(L[]) + write!(["@fatal l2h "] ||| L) + write!([&errout, "noweb error in l2h: "] ||| L) + exit(1) +end +@ +<<*>>= +procedure rcsinfo () + return "$Id: l2h.nw,v 1.24 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ +\section{Chunks} +\nowebchunks + +\begin{multicols}{2}[\section{Index}] +\nowebindex +\end{multicols} +@ +\end{document} diff --git a/web/noweb/src/icon/lrtldefs.icn b/web/noweb/src/icon/lrtldefs.icn new file mode 100644 index 0000000000..6faec732b7 --- /dev/null +++ b/web/noweb/src/icon/lrtldefs.icn @@ -0,0 +1,99 @@ +#line 3 "lrtldefs.nw" +procedure main(args) + go() +end +#line 7 "lrtldefs.nw" +procedure postpass(name, arg) + static kind, definers + initial { kind := "bogus" + definers := ["fun", "val", "structure", "signature", + "type", "rtlop"] + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if =(""|" "|" ") & =!definers & tab(many(' ')) then { + skiptyvars(); optwhite() + if ="[" then + while define_id() + else + define_id() + } + } + return +end + +procedure define_id() + static id1, id2, reserved + initial { id1 := &letters ++ &digits ++ '\'_' + id2 := '!%&$+-/:<=>?@\\~^|#*`' + reserved := set() + every insert(reserved, !"*|:=#_" | "->" | "=>") + } + optwhite() + if id := tab(many(id1 | id2)) then { + member(reserved, id) | writedefn(id) + return id + } + fail +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure skiptyvars() + suspend =("'"|"#") || tab(many(&letters ++ &digits ++ '\'_')) | + ="(" || bal(')', '(', ')') || =")" +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end +#line 55 "lrtldefs.nw" +procedure rcsinfo () + return "$Id: lrtldefs.nw,v 1.17 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/lrtldefs.nw b/web/noweb/src/icon/lrtldefs.nw new file mode 100644 index 0000000000..a928f761ff --- /dev/null +++ b/web/noweb/src/icon/lrtldefs.nw @@ -0,0 +1,60 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\section{Finding definitions in $\lambda$-RTL} +<<*>>= +procedure main(args) + go() +end +<<*>>= +procedure postpass(name, arg) + static kind, definers + initial { kind := "bogus" + definers := ["fun", "val", "structure", "signature", + "type", "rtlop"] + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if =(""|" "|" ") & =!definers & tab(many(' ')) then { + skiptyvars(); optwhite() + if ="[" then + while define_id() + else + define_id() + } + } + return +end + +procedure define_id() + static id1, id2, reserved + initial { id1 := &letters ++ &digits ++ '\'_' + id2 := '!%&$+-/:<=>?@\\~^|#*`' + reserved := set() + every insert(reserved, !"*|:=#_" | "->" | "=>") + } + optwhite() + if id := tab(many(id1 | id2)) then { + member(reserved, id) | writedefn(id) + return id + } + fail +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure skiptyvars() + suspend =("'"|"#") || tab(many(&letters ++ &digits ++ '\'_')) | + ="(" || bal(')', '(', ')') || =")" +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end +<<*>>= +procedure rcsinfo () + return "$Id: lrtldefs.nw,v 1.17 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/mmixdefs.icn b/web/noweb/src/icon/mmixdefs.icn new file mode 100644 index 0000000000..cd9bf7ffca --- /dev/null +++ b/web/noweb/src/icon/mmixdefs.icn @@ -0,0 +1,71 @@ +#line 3 "mmixdefs.nw" +procedure main(args) + go() +end +#line 7 "mmixdefs.nw" +global idchars +procedure postpass(name, arg) + static kind, lets + initial { idchars := &letters ++ &digits ++ '_:' + lets := &letters ++ '_' + kind := "bogus" + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if (any(lets), t := tab(many(idchars)), any('\t ')) then + writedefn(t) + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end +#line 27 "mmixdefs.nw" +procedure rcsinfo () + return "$Id: mmixdefs.nw,v 1.15 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/mmixdefs.nw b/web/noweb/src/icon/mmixdefs.nw new file mode 100644 index 0000000000..abb7ceda31 --- /dev/null +++ b/web/noweb/src/icon/mmixdefs.nw @@ -0,0 +1,32 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\section{Finding definitions in MMIX} +<<*>>= +procedure main(args) + go() +end +<<*>>= +global idchars +procedure postpass(name, arg) + static kind, lets + initial { idchars := &letters ++ &digits ++ '_:' + lets := &letters ++ '_' + kind := "bogus" + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if (any(lets), t := tab(many(idchars)), any('\t ')) then + writedefn(t) + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end +<<*>>= +procedure rcsinfo () + return "$Id: mmixdefs.nw,v 1.15 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/noidx.icn b/web/noweb/src/icon/noidx.icn new file mode 100644 index 0000000000..3a829eaa9c --- /dev/null +++ b/web/noweb/src/icon/noidx.icn @@ -0,0 +1,465 @@ +#line 9 "noidx.nw" +global lines # all the input lines +global delay # non-null if markup should be delayed past the first chunk +global anchordist # max distance before code chunk to place defining @xref label + +procedure main(args) + lines := [] + +#line 38 "noidx.nw" +curfile := "standard input?" +lastbeginarg := "never any chunks?" +#line 50 "noidx.nw" +every allchunks | allidents := set() +every indexlabels | defanchors | uses | anchorlabel | indexanchorlabel := table() +#line 284 "noidx.nw" +every chunkud | indexud := table(); +#line 16 "noidx.nw" + delay := &null + +#line 28 "noidx.nw" +while case(a := get(args)) of { + "-delay" : delay := 1 + "-docanchor" : anchordist := integer(get(args)) + default : fatal("unknown argument ", image(a)) +} +#line 18 "noidx.nw" + while put(lines, line := read()) do + apply(pass1, line) + every apply(pass2, !lines) + +#line 340 "noidx.nw" +(if /wrote_trailers then { + wrote_trailers := 1 + write("@nl") + write("@nl") + lognowebchunks() + lognowebindex() +} else &null) +#line 22 "noidx.nw" +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 35 "noidx.nw" +global curfile # unique id of the current @file +global lastbeginarg # identifies last chunk (for -delay) +#line 47 "noidx.nw" +global allchunks, allidents +global indexlabels, defanchors, uses, anchorlabel, indexanchorlabel +#line 57 "noidx.nw" +procedure pass1(name, arg) + static lastdefnlabel, thisusecount, lastanchorlabel, nonblank, textnonblank + initial nonblank := ~' \t' + case name of { + "file" : curfile := uniqueid(arg) + "begin" : { lastbeginarg := arg; textnonblank := &null } + "end" : if match("docs ", arg) & \anchordist & \textnonblank then + { +#line 116 "noidx.nw" +t := []; n := anchordist +lastanchorlabel := newdocslabel() +while lines[-1] ? + if n = 0 | ="@begin docs" then + { put(lines, "@xref label " || lastanchorlabel); lines |||:= t; break } + else + { if ="@nl" & pos(0) then n -:= 1; push(t, pull(lines)) } +#line 64 "noidx.nw" + } + else + lastanchorlabel := &null + "text" : /textnonblank := upto(nonblank, arg) + "defn" : { + insert(allchunks, arg) + slipin("@xref label " || (lastdefnlabel := newdefnlabel(arg))) + /lastanchorlabel := lastdefnlabel + /anchorlabel[arg] := lastanchorlabel + addlabel(defanchors, arg, lastanchorlabel) + addud(chunkud, "defn", arg, lastanchorlabel) + thisusecount := 0 + } + "use" : + if match ("code ", lastbeginarg) then { + insert(allchunks, arg) + slipin("@xref label " || lastdefnlabel || "-u" || (thisusecount +:= 1)) + addlabel(uses, arg, lastanchorlabel) + addud(chunkud, "use", arg, lastanchorlabel) + } else { + /textnonblank := upto(nonblank, arg) # could appear in quoted code + } + "index" : { + +#line 234 "noidx.nw" +{ arg ? { name := tab(upto(' ')|0); arg := if =" " then tab(0) else &null } } +#line 88 "noidx.nw" + case name of { + "use" : { insert(allidents, arg) + addud(indexud, "use", arg, \lastanchorlabel) + } + "defn" | "localdefn" : { + insert(allidents, arg) + (l := \lastanchorlabel) | + slipin("@xref label " || (l := newdocslabel())) + addud(indexud, "defn", arg, l) + /indexanchorlabel[arg] := l + slipin("@xref ref " || l) # bug fix + } + } + } + } + return +end +#line 127 "noidx.nw" +procedure slipin(s) + local last + last := pull(lines) + every put(lines, s | last) + return s +end +#line 134 "noidx.nw" +procedure pass2(name, arg) + static thesedefns, theseuses + static thischunk # null for docs chunk, arg of @defn for code chunk + static defout # number of definitions emitted for each chunk + initial defout := table(0) + case name of { + "begin" : { + if \delay & lastbeginarg == arg then +#line 340 "noidx.nw" +(if /wrote_trailers then { + wrote_trailers := 1 + write("@nl") + write("@nl") + lognowebchunks() + lognowebindex() +} else &null) +#line 142 "noidx.nw" + +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 143 "noidx.nw" + every thesedefns | theseuses := set() + thischunk := &null + } + "defn" : { thischunk := arg; + write("@xref ref " || anchorlabel[arg]) + +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 148 "noidx.nw" + + +#line 195 "noidx.nw" +defout[thischunk] +:= 1 +write("@xref prevdef ", defanchors[thischunk][defout[thischunk] - 1]) # fail if first +write("@xref nextdef ", defanchors[thischunk][defout[thischunk] + 1]) # faif if last +if defout[thischunk] = 1 then { +#line 221 "noidx.nw" +if *defanchors[thischunk] > 1 then { + write("@xref begindefs") + every write("@xref defitem ", defanchors[thischunk][2 to *defanchors[thischunk]]) + write("@xref enddefs") +} +if \uses[thischunk] then { + write("@xref beginuses") + every write("@xref useitem ", !uses[thischunk]) + write("@xref enduses") +} else + write("@xref notused ", thischunk) +#line 198 "noidx.nw" + } +#line 150 "noidx.nw" + } +# must postpone index cross-reference to end of chunk because +# the info is accumulated in [[thesdefns]] and [[theseuses]] on this pass + "end" : { if match("code", arg) then { +#line 200 "noidx.nw" +theseuses --:= thesedefns +if *thesedefns > 0 then { + write("@index begindefs") + every i := !alphasort(thesedefns) do { + every u := !\indexud[i] do u ? if ="use " then write("@index isused ", tab(0)) + write("@index defitem ", i) + } + write("@index enddefs") +} +if *theseuses > 0 then { + l := alphasort(theseuses) + write("@index beginuses") + every i := !alphasort(theseuses) do { + every d := !\indexud[i] do d ? if ="defn " then write("@index isdefined ", tab(0)) + write("@index useitem ", i) + } + write("@index enduses") +} +# every write("@literal \\nwindexuse{", TeXliteral(ident := !l), "}", +# "{", indexlabels[ident], "}{", thislabel, "}") +#line 153 "noidx.nw" + } + +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 154 "noidx.nw" + + } + "use" : { write("@xref ref " || (\anchorlabel[arg] | "nw@notdef")) # was "???" + +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 157 "noidx.nw" + + } + "index" : { + +#line 234 "noidx.nw" +{ arg ? { name := tab(upto(' ')|0); arg := if =" " then tab(0) else &null } } +#line 161 "noidx.nw" + case name of { + "defn" | "localdefn" : { + insert(thesedefns, arg) + # no xref ref because of bug fix + } + "use" : { insert(theseuses, arg) + write("@xref ref ", \indexanchorlabel[arg]) + } + } + name := "index " || name + +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 172 "noidx.nw" + } + "text" : # grotesque hack! for chunks and index in HTML + if /thischunk then # docs mode + case arg of { + "<nowebchunks>" : lognowebchunks() + "<nowebindex>" : lognowebindex() + default : +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 179 "noidx.nw" + } + else +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 181 "noidx.nw" + "fatal" : { +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 181 "noidx.nw" + ; exit(1) } + "trailer" : { +#line 340 "noidx.nw" +(if /wrote_trailers then { + wrote_trailers := 1 + write("@nl") + write("@nl") + lognowebchunks() + lognowebindex() +} else &null) +#line 183 "noidx.nw" + +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 184 "noidx.nw" + } + default : +#line 236 "noidx.nw" +write("@", name, (" " || \arg) | "") +#line 186 "noidx.nw" + } + return +end +#line 238 "noidx.nw" +procedure lualistimage(l) + p := "{ " + s := "" + every i := 1 to *l do { + s ||:= p || "[" || i || "] = " || image(l[i]) + p := ", " + } + return s || " }" +end +#line 255 "noidx.nw" +procedure newdefnlabel(arg) + static defcounts + initial defcounts := table(0) + /defanchors[arg] := [] + k := curfile || "-" || uniqueid(arg) + defcounts[k] +:= 1 + return "NW" || k || "-" || alphacode(defcounts[k]) +end +#line 264 "noidx.nw" +procedure newdocslabel() + static count + initial count := 0 + return "NWD" || curfile || "-" || alphacode(count +:= 1) +end +#line 270 "noidx.nw" +procedure addlabel(tbl, arg, label) + /tbl[arg] := [] + if tbl[arg][-1] == label then &null + else put(tbl[arg], label) + return label +end +#line 277 "noidx.nw" +global chunkud, indexud +procedure addud(udlist, name, arg, label) + /udlist[arg] := [] + s := name || " " || label + return udlist[arg][-1] == s | put(udlist[arg], s) +end +#line 289 "noidx.nw" +procedure strip_extra_uses(udlista) + local old, new, item + old := copy(udlista) + new := [] + while item := get(old) do + item ? + if ="defn " then + +#line 305 "noidx.nw" +{ new[-1] == item | put(new, item) } +#line 297 "noidx.nw" + else if ="use " then + if "defn " || tab(0) == !(old|new) then + &null # write(&errout, "Stripped extra ", item) + else + +#line 305 "noidx.nw" +{ new[-1] == item | put(new, item) } +#line 302 "noidx.nw" + return new +end +#line 309 "noidx.nw" +procedure uniqueid(name) + local key + static idtable, keycounts, badchars, badstars + initial { idtable := table() ; keycounts := table(0) + badchars := ~ (&letters ++ &digits ++ '!$()*+,./:;=?@|') + badstars := repl("*", *badchars) + } + + if not member(idtable, name) then { + # key := make_key(map(name, badchars, badstars)) # deprecated + key := alphacode(crc(name)) + keycounts[key] +:= 1 + idtable[name] := key + if keycounts[key] > 1 then idtable[name] ||:= "." || alphacode(keycounts[key]) + } + return idtable[name] +end +#line 330 "noidx.nw" +procedure make_key(key, len) + static noslash + initial noslash := ~ '/' + key ? return make_key(3(tab(upto('/')), ="/", tab(many(noslash)), pos(0)), *key) | + key[1+:3] || alphacode(\len | *key) | + key || (alphacode(\len) | "") +end +#line 338 "noidx.nw" +global wrote_trailers +#line 350 "noidx.nw" +procedure lognowebchunks(noheader) + static called + if \called then return + else called := 1 + + l := alphasort(allchunks) + write("@xref beginchunks") + while name := get(l) do { + write("@xref chunkbegin ", (\anchorlabel[name] | "nw@notdef"), " ", name) + every write("@xref chunk", !\chunkud[name]) + write("@xref chunkend") + } + write("@xref endchunks") +end +#line 365 "noidx.nw" +procedure lognowebindex() + static called + if \called then return + else called := 1 + + l := alphasort(allidents) + write("@index beginindex") + while name := get(l) do { + write("@index entrybegin ", (\indexanchorlabel[name] | "nw@notdef"), " ", name) + every write("@index entry", !strip_extra_uses(\indexud[name])) + write("@index entryend") + } + write("@index endindex") +end +#line 380 "noidx.nw" +procedure alphasort(x) + t := table() + every s := !x do t[s] := sortkey(s) + t := sort(t, 2) + l := [] + every put(l, (!t)[1]) + return l +end +#line 405 "noidx.nw" +procedure sortkey(s) + return map(s) || "\n" || s # relies on \n sorting lower than other chars +end +#line 409 "noidx.nw" +procedure alphacode(n) + static codes + initial codes := &digits || &letters + return if n < 0 then + "-" || alphacode(-n) + else if n >= *codes then + alphacode(n / *codes) || alphacode(n % *codes) + else + codes[n+1] +end +#line 421 "noidx.nw" +# /* 32-bit Cyclic Redundancy Code implemented by A. Appel 1986 */ +# +# this works only if POLY is a prime polynomial in the field +# of integers modulo 2, of order 32. Since the representation of this +# won't fit in a 32-bit word, the high-order bit is implicit. +# IT MUST ALSO BE THE CASE that the coefficients of orders 31 down to 25 +# are zero. Fortunately, we have a candidate, from +# E. J. Watson, "Primitive Polynomials (Mod 2)", Math. Comp 16 (1962). +# It is: x^32 + x^7 + x^5 + x^3 + x^2 + x^1 + x^0 +# +# Now we reverse the bits to get: +# 111101010000000000000000000000001 in binary (but drop the last 1) +# f 5 0 0 0 0 0 0 in hex +# */ + +procedure crc_table() + local POLY, sum + POLY := 16rf5000000 + t := table() + every i := 0 to 255 do { + sum := 0 + every j := 7 to 0 by -1 do + if iand(i, ishift(1, j)) ~= 0 then + sum := ixor(sum, ishift(POLY, -j)) + t[i] := sum + } + return t +end + +procedure crc(s) + local sum + static table + initial table := crc_table() + sum := 0 + s ? while sum := ixor(ishift(sum, -8), table[iand(ixor(sum, ord(move(1))), 255)]) + return sum +end +#line 460 "noidx.nw" +procedure fatal(L[]) + write!(["@fatal noidx "] ||| L) + write!([&errout, "noweb error in noidx: "] ||| L) + exit(1) +end +#line 466 "noidx.nw" +procedure rcsinfo () + return "$Id: noidx.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/noidx.nw b/web/noweb/src/icon/noidx.nw new file mode 100644 index 0000000000..62fd9176d1 --- /dev/null +++ b/web/noweb/src/icon/noidx.nw @@ -0,0 +1,479 @@ +\documentstyle[noweb]{article} +\pagestyle{noweb}\noweboptions{shortxref} +\begin{document} +Missing ``{\tt @xref tag} {\it label tag},'' where {\it label} is the name of +a label and {\it tag} is a chunk number, page number, sub-page number, or +other identifying mark. +@ +\section{Cross-reference and index support} +<<*>>= +global lines # all the input lines +global delay # non-null if markup should be delayed past the first chunk +global anchordist # max distance before code chunk to place defining @xref label + +procedure main(args) + lines := [] + <<initialization>> + delay := &null + <<swallow [[args]]>> + while put(lines, line := read()) do + apply(pass1, line) + every apply(pass2, !lines) + <<write trailers if not already written>> +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +<<swallow [[args]]>>= +while case(a := get(args)) of { + "-delay" : delay := 1 + "-docanchor" : anchordist := integer(get(args)) + default : fatal("unknown argument ", image(a)) +} +@ +<<*>>= +global curfile # unique id of the current @file +global lastbeginarg # identifies last chunk (for -delay) +<<initialization>>= +curfile := "standard input?" +lastbeginarg := "never any chunks?" +@ +[[anchorlabel]] is the label that references to a code chunk refer to. +The {\LaTeX} back end typically uses the chunk itself, but the HTML +back end will use a spot in the preceding documentation chunk (if +any), as requested by [[-docanchor]] (and non-null [[anchordist]]) +and recorded in [[anchorlabel]]. +<<*>>= +global allchunks, allidents +global indexlabels, defanchors, uses, anchorlabel, indexanchorlabel +<<initialization>>= +every allchunks | allidents := set() +every indexlabels | defanchors | uses | anchorlabel | indexanchorlabel := table() +@ +I set [[textnonblank]] non-null if some text line in the current chunk +contains a non-blank character. +This trick lets me avoid planting anchors in empty documentation chunks. +<<*>>= +procedure pass1(name, arg) + static lastdefnlabel, thisusecount, lastanchorlabel, nonblank, textnonblank + initial nonblank := ~' \t' + case name of { + "file" : curfile := uniqueid(arg) + "begin" : { lastbeginarg := arg; textnonblank := &null } + "end" : if match("docs ", arg) & \anchordist & \textnonblank then + {<<insert and set [[lastanchorlabel]]>>} + else + lastanchorlabel := &null + "text" : /textnonblank := upto(nonblank, arg) + "defn" : { + insert(allchunks, arg) + slipin("@xref label " || (lastdefnlabel := newdefnlabel(arg))) + /lastanchorlabel := lastdefnlabel + /anchorlabel[arg] := lastanchorlabel + addlabel(defanchors, arg, lastanchorlabel) + addud(chunkud, "defn", arg, lastanchorlabel) + thisusecount := 0 + } + "use" : + if match ("code ", lastbeginarg) then { + insert(allchunks, arg) + slipin("@xref label " || lastdefnlabel || "-u" || (thisusecount +:= 1)) + addlabel(uses, arg, lastanchorlabel) + addud(chunkud, "use", arg, lastanchorlabel) + } else { + /textnonblank := upto(nonblank, arg) # could appear in quoted code + } + "index" : { + <<shift [[name]] and [[arg]]>> + case name of { + "use" : { insert(allidents, arg) + addud(indexud, "use", arg, \lastanchorlabel) + } + "defn" | "localdefn" : { + insert(allidents, arg) + (l := \lastanchorlabel) | + slipin("@xref label " || (l := newdocslabel())) + addud(indexud, "defn", arg, l) + /indexanchorlabel[arg] := l + slipin("@xref ref " || l) # bug fix + } + } + } + } + return +end +@ +The bug fix\label{multi-def-bug} +alluded to above occurs when there are multiple definitions of an identifier. +In this case, we can't just use [[indexanchorlabel[id]]], because that refers only to +the first definition. In the {\TeX} back end, that leads to bogus +tags like \hbox{\it x \small\rm 7b 7b 7b} instead of \hbox{\it x +\small\rm 7b 9 12a}; the first tag is repeated again and again. +Because the tag for the current [[@defn]] is lost by the time pass~2 +rolls around, we have to slip it in on pass~1. +@ +<<insert and set [[lastanchorlabel]]>>= +t := []; n := anchordist +lastanchorlabel := newdocslabel() +while lines[-1] ? + if n = 0 | ="@begin docs" then + { put(lines, "@xref label " || lastanchorlabel); lines |||:= t; break } + else + { if ="@nl" & pos(0) then n -:= 1; push(t, pull(lines)) } +@ +[[slipin]] sticks something into [[lines]] just before the current (last) line. +It makes sense only on the first pass. +<<*>>= +procedure slipin(s) + local last + last := pull(lines) + every put(lines, s | last) + return s +end +<<*>>= +procedure pass2(name, arg) + static thesedefns, theseuses + static thischunk # null for docs chunk, arg of @defn for code chunk + static defout # number of definitions emitted for each chunk + initial defout := table(0) + case name of { + "begin" : { + if \delay & lastbeginarg == arg then <<write trailers if not already written>> + <<copy [[name]] and [[arg]] to output>> + every thesedefns | theseuses := set() + thischunk := &null + } + "defn" : { thischunk := arg; + write("@xref ref " || anchorlabel[arg]) + <<copy [[name]] and [[arg]] to output>> + <<write chunk cross-reference>> + } +# must postpone index cross-reference to end of chunk because +# the info is accumulated in [[thesdefns]] and [[theseuses]] on this pass + "end" : { if match("code", arg) then {<<write index cross-reference>>} + <<copy [[name]] and [[arg]] to output>> + } + "use" : { write("@xref ref " || (\anchorlabel[arg] | "nw@notdef")) # was "???" + <<copy [[name]] and [[arg]] to output>> + } + "index" : { + <<shift [[name]] and [[arg]]>> + case name of { + "defn" | "localdefn" : { + insert(thesedefns, arg) + # no xref ref because of bug fix + } + "use" : { insert(theseuses, arg) + write("@xref ref ", \indexanchorlabel[arg]) + } + } + name := "index " || name + <<copy [[name]] and [[arg]] to output>> + } + "text" : # grotesque hack! for chunks and index in HTML + if /thischunk then # docs mode + case arg of { + "<nowebchunks>" : lognowebchunks() + "<nowebindex>" : lognowebindex() + default : <<copy [[name]] and [[arg]] to output>> + } + else <<copy [[name]] and [[arg]] to output>> + "fatal" : { <<copy [[name]] and [[arg]] to output>>; exit(1) } + "trailer" : { <<write trailers if not already written>> + <<copy [[name]] and [[arg]] to output>> + } + default : <<copy [[name]] and [[arg]] to output>> + } + return +end +@ +The case of the [[@index defn]] is the one case where we don't emit a +reference, because the reference has to go in earlier. See +page~\pageref{multi-def-bug} for an explanation. +@ +<<write chunk cross-reference>>= +defout[thischunk] +:= 1 +write("@xref prevdef ", defanchors[thischunk][defout[thischunk] - 1]) # fail if first +write("@xref nextdef ", defanchors[thischunk][defout[thischunk] + 1]) # faif if last +if defout[thischunk] = 1 then {<<write use and def summary for chunk cross-reference>>} +<<write index cross-reference>>= +theseuses --:= thesedefns +if *thesedefns > 0 then { + write("@index begindefs") + every i := !alphasort(thesedefns) do { + every u := !\indexud[i] do u ? if ="use " then write("@index isused ", tab(0)) + write("@index defitem ", i) + } + write("@index enddefs") +} +if *theseuses > 0 then { + l := alphasort(theseuses) + write("@index beginuses") + every i := !alphasort(theseuses) do { + every d := !\indexud[i] do d ? if ="defn " then write("@index isdefined ", tab(0)) + write("@index useitem ", i) + } + write("@index enduses") +} +# every write("@literal \\nwindexuse{", TeXliteral(ident := !l), "}", +# "{", indexlabels[ident], "}{", thislabel, "}") +<<write use and def summary for chunk cross-reference>>= +if *defanchors[thischunk] > 1 then { + write("@xref begindefs") + every write("@xref defitem ", defanchors[thischunk][2 to *defanchors[thischunk]]) + write("@xref enddefs") +} +if \uses[thischunk] then { + write("@xref beginuses") + every write("@xref useitem ", !uses[thischunk]) + write("@xref enduses") +} else + write("@xref notused ", thischunk) +@ +<<shift [[name]] and [[arg]]>>= +{ arg ? { name := tab(upto(' ')|0); arg := if =" " then tab(0) else &null } } +<<copy [[name]] and [[arg]] to output>>= +write("@", name, (" " || \arg) | "") +<<*>>= +procedure lualistimage(l) + p := "{ " + s := "" + every i := 1 to *l do { + s ||:= p || "[" || i || "] = " || image(l[i]) + p := ", " + } + return s || " }" +end +@ +The following code is a change first effective in version~2.10d. +It ensures that labels are the same whether files are marked up one at +a time or in a group. +This change helps ``book page definition marks'' for the 152 text, +which is processed one chapter at a time for weaving but several +chapters in a group for tangling. +<<*>>= +procedure newdefnlabel(arg) + static defcounts + initial defcounts := table(0) + /defanchors[arg] := [] + k := curfile || "-" || uniqueid(arg) + defcounts[k] +:= 1 + return "NW" || k || "-" || alphacode(defcounts[k]) +end +<<*>>= +procedure newdocslabel() + static count + initial count := 0 + return "NWD" || curfile || "-" || alphacode(count +:= 1) +end +<<*>>= +procedure addlabel(tbl, arg, label) + /tbl[arg] := [] + if tbl[arg][-1] == label then &null + else put(tbl[arg], label) + return label +end +<<*>>= +global chunkud, indexud +procedure addud(udlist, name, arg, label) + /udlist[arg] := [] + s := name || " " || label + return udlist[arg][-1] == s | put(udlist[arg], s) +end +<<initialization>>= +every chunkud | indexud := table(); +@ +Because it's silly to have both a use and a definition point to the same chunk, +I strip out uses that list the same chunk as their definitions. +<<*>>= +procedure strip_extra_uses(udlista) + local old, new, item + old := copy(udlista) + new := [] + while item := get(old) do + item ? + if ="defn " then + <<add item if needed>> + else if ="use " then + if "defn " || tab(0) == !(old|new) then + &null # write(&errout, "Stripped extra ", item) + else + <<add item if needed>> + return new +end +<<add item if needed>>= +{ new[-1] == item | put(new, item) } +@ +[[uniqueid]] eliminates both {\TeX} and HTML specials. +<<*>>= +procedure uniqueid(name) + local key + static idtable, keycounts, badchars, badstars + initial { idtable := table() ; keycounts := table(0) + badchars := ~ (&letters ++ &digits ++ '!$()*+,./:;=?@|') + badstars := repl("*", *badchars) + } + + if not member(idtable, name) then { + # key := make_key(map(name, badchars, badstars)) # deprecated + key := alphacode(crc(name)) + keycounts[key] +:= 1 + idtable[name] := key + if keycounts[key] > 1 then idtable[name] ||:= "." || alphacode(keycounts[key]) + } + return idtable[name] +end +@ +The original key routine used the FORTH heuristic on the basename: +first three letters plus the length. This produced too many collisions. +<<*>>= +procedure make_key(key, len) + static noslash + initial noslash := ~ '/' + key ? return make_key(3(tab(upto('/')), ="/", tab(many(noslash)), pos(0)), *key) | + key[1+:3] || alphacode(\len | *key) | + key || (alphacode(\len) | "") +end +<<*>>= +global wrote_trailers +<<write trailers if not already written>>= +(if /wrote_trailers then { + wrote_trailers := 1 + write("@nl") + write("@nl") + lognowebchunks() + lognowebindex() +} else &null) +@ +Now, a special hack, so we can write this stuff in the right place on pass 2. +<<*>>= +procedure lognowebchunks(noheader) + static called + if \called then return + else called := 1 + + l := alphasort(allchunks) + write("@xref beginchunks") + while name := get(l) do { + write("@xref chunkbegin ", (\anchorlabel[name] | "nw@notdef"), " ", name) + every write("@xref chunk", !\chunkud[name]) + write("@xref chunkend") + } + write("@xref endchunks") +end +<<*>>= +procedure lognowebindex() + static called + if \called then return + else called := 1 + + l := alphasort(allidents) + write("@index beginindex") + while name := get(l) do { + write("@index entrybegin ", (\indexanchorlabel[name] | "nw@notdef"), " ", name) + every write("@index entry", !strip_extra_uses(\indexud[name])) + write("@index entryend") + } + write("@index endindex") +end +<<*>>= +procedure alphasort(x) + t := table() + every s := !x do t[s] := sortkey(s) + t := sort(t, 2) + l := [] + every put(l, (!t)[1]) + return l +end +@ +I tried to ignore special characters when sorting. +This turned out to be too clever by half. +Sample: it would sort [[BALANCER_DEBUGGING]] before +[[BALANCE_TRANSFORMATION]], because the underscore didn't count. +<<old, evil code>>= +procedure sortkey(s) + static keep + initial keep := &lcase ++ ' ' + s := map(s) + r := "" + s ? while tab(upto(keep)) do r ||:= move(1) + return r || "\n" || s # relies on \n sorting lower than other chars +end +@ My solution is to move to a much simpler scheme in which the only +thing we do is change case. +<<*>>= +procedure sortkey(s) + return map(s) || "\n" || s # relies on \n sorting lower than other chars +end +<<*>>= +procedure alphacode(n) + static codes + initial codes := &digits || &letters + return if n < 0 then + "-" || alphacode(-n) + else if n >= *codes then + alphacode(n / *codes) || alphacode(n % *codes) + else + codes[n+1] +end +@ +<<*>>= +# /* 32-bit Cyclic Redundancy Code implemented by A. Appel 1986 */ +# +# this works only if POLY is a prime polynomial in the field +# of integers modulo 2, of order 32. Since the representation of this +# won't fit in a 32-bit word, the high-order bit is implicit. +# IT MUST ALSO BE THE CASE that the coefficients of orders 31 down to 25 +# are zero. Fortunately, we have a candidate, from +# E. J. Watson, "Primitive Polynomials (Mod 2)", Math. Comp 16 (1962). +# It is: x^32 + x^7 + x^5 + x^3 + x^2 + x^1 + x^0 +# +# Now we reverse the bits to get: +# 111101010000000000000000000000001 in binary (but drop the last 1) +# f 5 0 0 0 0 0 0 in hex +# */ + +procedure crc_table() + local POLY, sum + POLY := 16rf5000000 + t := table() + every i := 0 to 255 do { + sum := 0 + every j := 7 to 0 by -1 do + if iand(i, ishift(1, j)) ~= 0 then + sum := ixor(sum, ishift(POLY, -j)) + t[i] := sum + } + return t +end + +procedure crc(s) + local sum + static table + initial table := crc_table() + sum := 0 + s ? while sum := ixor(ishift(sum, -8), table[iand(ixor(sum, ord(move(1))), 255)]) + return sum +end +@ +<<*>>= +procedure fatal(L[]) + write!(["@fatal noidx "] ||| L) + write!([&errout, "noweb error in noidx: "] ||| L) + exit(1) +end +<<*>>= +procedure rcsinfo () + return "$Id: noidx.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ +\section{List of chunks} +\nowebchunks + +\twocolumn +\section{Index} +\nowebindex +@ +\end{document} diff --git a/web/noweb/src/icon/noindex.icn b/web/noweb/src/icon/noindex.icn new file mode 100644 index 0000000000..3ba307170b --- /dev/null +++ b/web/noweb/src/icon/noindex.icn @@ -0,0 +1,98 @@ +#line 6 "noindex.nw" +procedure main(args) + if *args ~= 1 then + write(&errout, "Usage: noindex basename") + else { + in := get(args) + if in[-4:0] == ".tex" then in[-4:0] := ".aux" + else if not (in[-4:0] == ".aux") then in ||:= ".aux" + gobble(in, idx := []) + f := open(in[1:-4] || ".nwi", "w") | fatal("Cannot write ", in[1:-4] || ".nwi") + last := "" + every out := stripcount(!alphasort(idx)) do + write(f, last ~== out) & last := out + close(f) + } + return +end +#line 23 "noindex.nw" +procedure gobble(name, idx) + static count + initial count := 0 + in := open(name) | +#line 40 "noindex.nw" +{ write(&errout, "No file ", name, "."); return } +#line 27 "noindex.nw" + while line := read(in) do + line ? + if ="\\@input{" & name := tab(upto('}')) & move(1) & pos(0) then + gobble(name, idx) + else if ="\\nwixadds{" then + put(idx, right(count +:= 1, 6, "0") || " " || tab(0)) + return +end +#line 36 "noindex.nw" +procedure stripcount(s) + s ? { tab(many(&digits)); tab(many(' ')); return "\\nwixaddsx{" || tab(0) } +end +#line 42 "noindex.nw" +procedure alphasort(x) + t := table() + every s := !x do t[s] := sortkey(s) + t := sort(t, 2) + l := [] + every put(l, (!t)[1]) + return l +end +#line 51 "noindex.nw" +procedure sortkey(s) + local count + map(s) ? { + count := tab(many(&digits)) + tab(many(' ')) + if ="c}" then return firstkey() || "\n" || count + else if ="i}" then return firstkey() || "\n" || count + else fatal("not chunk or index") + } +end +#line 62 "noindex.nw" +procedure firstkey() + ="{{" | fatal("bad index or chunk format") + s := (remove_control_sequences() ? tab(bal('}', '{', '}') | 0)) + return alphabet(s) || "\n" || s +end +#line 68 "noindex.nw" +procedure alphabet(s) + static keep + initial keep := &lcase ++ ' \t' + r := "" + s ? { + while tab(upto(keep)) do + if tab(many(' \t')) then + r ||:= " " + else + r ||:= tab(many(&lcase)) | fatal("This can't happen") + } + r ? { tab(many(' ')) ; return tab(0) } # strip leading blanks +end +#line 82 "noindex.nw" +procedure remove_control_sequences() + s := "" + while s ||:= tab(upto('\\')) do { + ="\\" + tab(many(&letters)) | move(1) # skip + tab(many(' \t')) + } + return s || tab(0) +end +#line 93 "noindex.nw" +procedure fatal(L[]) + write!(["@fatal noindex "] ||| L) + write!([&errout, "noweb error in noindex: "] ||| L) + exit(1) +end +#line 99 "noindex.nw" +procedure rcsinfo () + return "$Id: noindex.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/noindex.nw b/web/noweb/src/icon/noindex.nw new file mode 100644 index 0000000000..cad4369b8a --- /dev/null +++ b/web/noweb/src/icon/noindex.nw @@ -0,0 +1,106 @@ +This program is similar to [[makeindex]] in that it grovels through [[.aux]] +files looking for index information, which it writes to a [[.nwi]] file. +It's used when [[noweave -indexfrom]] is used on many files separately; +it combines the separate indices into a single, correctly sorted index. +That index file is read by [[\nowebindex*]]. +<<*>>= +procedure main(args) + if *args ~= 1 then + write(&errout, "Usage: noindex basename") + else { + in := get(args) + if in[-4:0] == ".tex" then in[-4:0] := ".aux" + else if not (in[-4:0] == ".aux") then in ||:= ".aux" + gobble(in, idx := []) + f := open(in[1:-4] || ".nwi", "w") | fatal("Cannot write ", in[1:-4] || ".nwi") + last := "" + every out := stripcount(!alphasort(idx)) do + write(f, last ~== out) & last := out + close(f) + } + return +end +<<*>>= +procedure gobble(name, idx) + static count + initial count := 0 + in := open(name) | <<can't open; return>> + while line := read(in) do + line ? + if ="\\@input{" & name := tab(upto('}')) & move(1) & pos(0) then + gobble(name, idx) + else if ="\\nwixadds{" then + put(idx, right(count +:= 1, 6, "0") || " " || tab(0)) + return +end +<<*>>= +procedure stripcount(s) + s ? { tab(many(&digits)); tab(many(' ')); return "\\nwixaddsx{" || tab(0) } +end +<<can't open; return>>= +{ write(&errout, "No file ", name, "."); return } +<<*>>= +procedure alphasort(x) + t := table() + every s := !x do t[s] := sortkey(s) + t := sort(t, 2) + l := [] + every put(l, (!t)[1]) + return l +end +<<*>>= +procedure sortkey(s) + local count + map(s) ? { + count := tab(many(&digits)) + tab(many(' ')) + if ="c}" then return firstkey() || "\n" || count + else if ="i}" then return firstkey() || "\n" || count + else fatal("not chunk or index") + } +end +<<*>>= +procedure firstkey() + ="{{" | fatal("bad index or chunk format") + s := (remove_control_sequences() ? tab(bal('}', '{', '}') | 0)) + return alphabet(s) || "\n" || s +end +<<*>>= +procedure alphabet(s) + static keep + initial keep := &lcase ++ ' \t' + r := "" + s ? { + while tab(upto(keep)) do + if tab(many(' \t')) then + r ||:= " " + else + r ||:= tab(many(&lcase)) | fatal("This can't happen") + } + r ? { tab(many(' ')) ; return tab(0) } # strip leading blanks +end +<<*>>= +procedure remove_control_sequences() + s := "" + while s ||:= tab(upto('\\')) do { + ="\\" + tab(many(&letters)) | move(1) # skip + tab(many(' \t')) + } + return s || tab(0) +end +@ +<<*>>= +procedure fatal(L[]) + write!(["@fatal noindex "] ||| L) + write!([&errout, "noweb error in noindex: "] ||| L) + exit(1) +end +<<*>>= +procedure rcsinfo () + return "$Id: noindex.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ +\section{Index} +\nowebindex diff --git a/web/noweb/src/icon/noxref.nw b/web/noweb/src/icon/noxref.nw new file mode 100644 index 0000000000..4b0eaa1836 --- /dev/null +++ b/web/noweb/src/icon/noxref.nw @@ -0,0 +1,119 @@ +\section{Cross-reference for noweb/latex} +<<*>>= +global lines, defns, uses, names, firstdefnout + +procedure main(args) + lines := [] + defns := table() + uses := table() + names := table() + while put(lines, line := read()) do + apply(pass1, line) + firstdefnout := set() + every apply(pass2, !lines) +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +<<*>>= +procedure pass1(name, chunkname) + static curfile, defined + initial {curfile := "????"; defined := set()} + case name of { + "file" : curfile := modid(chunkname || (chunkname[4+:3] | chunkname[4:0] | "")) + "defn" | "use" : { + if name == "defn" then tbl := defns else tbl := uses + if name == "defn" & not member(defined, chunkname) then { + label := "sublabel" + insert(defined, chunkname) + } else + label := "label" + /tbl[chunkname] := [] + id := map(name, &lcase, &ucase) || ":" || curfile || ":" || + modid(chunkname) || "-" || (1+*tbl[chunkname]) + put(tbl[chunkname], id) + put(lines, "@literal \\" || label || "{" || id || "}") + names[chunkname] := lettersonly(chunkname) + } + } + return +end +<<*>>= +procedure modid(name) + local key + static idtable, keycounts + initial { idtable := table() ; keycounts := table(0) } + + if not member(idtable, name) then { + key := map(name, " \\{}-[]`#%&~_^", "**************") + if *key > 6 then key := key[1:4] || key[0:-3] + keycounts[key] +:= 1 + idtable[name] := key || if keycounts[key] > 1 then "-" || keycounts[key] else "" + } + return idtable[name] +end +<<*>>= +global thischunk + +procedure pass2(name, arg) + case name of { + "defn" : { + thischunk := arg + write("@defn ", arg, "~{\\footnotesize\\Rm\\subpageref{", defns[arg][1], "}}") + } + "use" : + write("@use ", arg, "~{\\footnotesize\\Rm\\subpageref{", (\defns[arg])[1], "}}") | + write("@use ", arg, "~{\\footnotesize\\Rm (never defined)}") + "end" : { + if match("code", arg) & not member(firstdefnout, thischunk) then { + insert(firstdefnout, thischunk) + if *defns[thischunk] > 1 then { + writes("@literal \\alsodefined{") + every i := 2 to *defns[thischunk] do + writes("\\\\{", defns[thischunk][i], "}") + write("}") + } + if \uses[thischunk] then { + writes("@literal \\used{") + every i := 1 to *uses[thischunk] do + writes("\\\\{", uses[thischunk][i], "}") + write("}") + } else write("@literal \\notused{", escapeTeXspecials(thischunk), "}") + } + write("@", name, " ", arg) + } + "text" : if \arg == "\\nowebchunks" then nowebchunks() + else write("@text", (" " || \arg) | "") + default : write("@", name, (" " || \arg) | "") + } + return +end +<<*>>= +procedure nowebchunks() + l := sort(names,2) + while name := get(l)[1] do { + write("@literal {") + write("@use ", name, "~{\\footnotesize\\Rm\\subpageref{", (\defns[name])[1], "}}") | + write("@use ", name, "~{\\footnotesize\\Rm (never defined)}") + write("@literal }") + if *l > 0 then write("@literal \\\\") + } +end +<<*>>= +procedure lettersonly(s) + static keep + initial keep := &lcase ++ ' ' + s := map(s) + r := "" + s ? while tab(upto(keep)) do r ||:= move(1) + return r +end +<<*>>= +procedure escapeTeXspecials(name) + name ? { + s := "" + while s ||:= tab(upto('{}$&%#^_~\\')) do s ||:= "\\" || move(1) + return s || tab(0) + } +end diff --git a/web/noweb/src/icon/nwl2h.nw b/web/noweb/src/icon/nwl2h.nw new file mode 100644 index 0000000000..3dbc2e58ed --- /dev/null +++ b/web/noweb/src/icon/nwl2h.nw @@ -0,0 +1,79 @@ +<<*>>= +global show_unknowns + +procedure main(args) + local line + every arg := !args do + case arg of { + "-show-unknowns" : show_unknowns := 1 + default : write(&errout, "l2h filter: unknown arg ", image(arg)) + } + while line := read() do + apply(filter, line) + warn_unknown(unknown_set, "control sequences", "\\") + warn_unknown(unknown_envs, "environments", "{", "}") +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +<<*>>= +global curfile, curline + +procedure filter(name, arg) + static S, C, code + initial { S := converter("V"); C := converter("H") } + case name of { + "begin" : {<<out>>; if match("code ", arg) then code := 1} + "end" : {<<out>>; code := &null; S.mode := "V"} + "quote" : {<<out>>; code := 1; stop_skipping(S)} + "endquote" : {<<out>>; code := &null} + "file" : {<<out>>; curfile := arg; curline := 1} + "line" : {<<out>>; curline := integer(arg)} + "defn" : { write("@", name, " ", "{" || arg || "}" ? convert(C)); reset(C) } + "use" : { write("@", name, " ", "{" || arg || "}" ? convert(C)); reset(C) } + "text" : {if \code then <<out>> else outtext(arg ? convert(S))} + "nl" : {if \code then <<out>> else outtext("\n" ? convert(S)); curline +:= 1} + default : {<<out>>} + } + return +end +<<out>>= +write("@", name, (" " || \arg) | "") +<<*>>= +procedure outtext(s) + s ? + while not pos(0) do + if ="\n" then write("@nl") + else write("@text ", tab(upto('\n') | 0)) + return +end +<<*>>= +procedure warn_unknown(s, type, mark, rmark) + if *s > 0 then { + pushout("Unknown " || type || ": ") + every pushout(((\mark | "")\1) || !sort(s) || ((\rmark | "")\1) || " ") + pushout("\n") + } +end +<<*>>= +procedure pushout(s) + static col + initial col := 0 + if find("\n", s) then + s ? { + pushout(tab(upto('\n'))) + while ="\n" do {col := 0; write(&errout)} + pushout(tab(0)) + } + else { + col +:= *s + if col >= 79 then {writes(&errout, "\n "); col := *s + 2} + writes(&errout, s) + } + return +end +<<*>>= +procedure error(args[]) + return write!([&errout, (\curfile || ", ") | "", "line ", curline, ": "] ||| args) +end diff --git a/web/noweb/src/icon/pascaldefs.icn b/web/noweb/src/icon/pascaldefs.icn new file mode 100644 index 0000000000..ccdd434ca2 --- /dev/null +++ b/web/noweb/src/icon/pascaldefs.icn @@ -0,0 +1,96 @@ +#line 2 "pascaldefs.nw" +global showlocal +procedure main(args) + showlocal := !args == "-local" + go() +end +#line 8 "pascaldefs.nw" +procedure postpass(name, arg) + static kind + initial { kind := "bogus" } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if (optwhite(), =("function "|"procedure "), not hasword(arg, "forward")) then{ + tab(many(' ')) + writedefn(tab(upto(' ():;'))) + } else if (optwhite(), x := allupper(), optwhite(), ="=") then { + writedefn(x) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure words() + static alnum + initial alnum := &letters ++ &digits ++ '_' + suspend if any(alnum) then tab(many(alnum)) + else if pos(0) then fail + else if tab(upto(alnum)) then words() +end + +procedure hasword(s, w) + suspend (tab(0) ? words()) == w +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end + +procedure allupper() + static uppers + initial uppers := &ucase ++ &digits ++ '_' + suspend tab(many(uppers)) +end + + +procedure rcsinfo () + return "$Id: pascaldefs.nw,v 1.17 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/pascaldefs.nw b/web/noweb/src/icon/pascaldefs.nw new file mode 100644 index 0000000000..04637bc3bf --- /dev/null +++ b/web/noweb/src/icon/pascaldefs.nw @@ -0,0 +1,55 @@ +\section{Finding Pascal definitions} +<<*>>= +global showlocal +procedure main(args) + showlocal := !args == "-local" + go() +end +<<*>>= +procedure postpass(name, arg) + static kind + initial { kind := "bogus" } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if (optwhite(), =("function "|"procedure "), not hasword(arg, "forward")) then{ + tab(many(' ')) + writedefn(tab(upto(' ():;'))) + } else if (optwhite(), x := allupper(), optwhite(), ="=") then { + writedefn(x) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure words() + static alnum + initial alnum := &letters ++ &digits ++ '_' + suspend if any(alnum) then tab(many(alnum)) + else if pos(0) then fail + else if tab(upto(alnum)) then words() +end + +procedure hasword(s, w) + suspend (tab(0) ? words()) == w +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end + +procedure allupper() + static uppers + initial uppers := &ucase ++ &digits ++ '_' + suspend tab(many(uppers)) +end + + +procedure rcsinfo () + return "$Id: pascaldefs.nw,v 1.17 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/pdcached.icn b/web/noweb/src/icon/pdcached.icn new file mode 100644 index 0000000000..e24102bce3 --- /dev/null +++ b/web/noweb/src/icon/pdcached.icn @@ -0,0 +1,238 @@ +#line 5 "pdcached.nw" +procedure main(args) + cachename := get(args) | stop("pdcached: no cache") + if args[1] == "-nearcode" then + nearcode := get(args) + cmd := get(args) | stop("pdcached: no command") + + cmd := "sed '/^@begin docs /s/[0-9][0-9]*/0/' | unmarkup | " || cmd || + " | markup | grep -v '^@file $'" + + last := &null + loadcache(cachename) + + while line := read() do + {## write("====> process ", image(line), "; last = ", type(last), + ## "(", *\last | "?", "), lastcode = ", image(lastcode)) + if match("@begin code ", line) then { + if type(last) == "list" then + pipeout(last, cmd) + last := "code" + until match("@end code ", line) do { + write(line) + line := read() + } + write(line) +#write("******until completed with ", line) + } else if match("@begin docs ", line) then { + l := [line] + until match("@end docs ", line) do + put(l, line := read()) +#write("******until completed with ", line) + put(l, line) + if /nearcode then { + pipeout(l, cmd) + last := &null + } else if last === "code" & notblank(l) then { + pipeout(l, cmd) + every write("@begin docs 0" | "@nl" | "@end docs 0") + last := &null + } else { + last := l + } + } else { + write(line) + } + } + savecache(cachename) + return +end + +#line 55 "pdcached.nw" +procedure pipeout(lines, cmd) + local cached + static tmp + initial { tmp := "/tmp/pdcached.out" + hitcount := misscount := 0 + } + cached := find_in_cache(lines) + if /cached then { + misscount +:= 1 + f := open(cmd || " > " || tmp, "wp") + every write(f, !lines) + close(f) + f := open(tmp, "r") + cached := [] + while put(cached, read(f)) + close(f) + adjustblanks(lines, cached) + save_in_cache(lines, cached) + } else { + adjustblanks(lines, cached) + hitcount +:= 1 + } + every write(!cached) + return +end +#line 84 "pdcached.nw" +procedure adjustblanks(in, out) + if &fail & find("Applying a primitive", !in) then { + write(&errout, "For ", image(in[1]), "/", image(out[1]), ", notblank(in) = ", + image(notblank(in)) | "<failed>", + "; blankstring(out[1]) = ", image(blankstring(out[1])) | "<failed>") + every i := 1 to *in do write(&errout, " in[", i, "] = ", image( in[i])) + every i := 1 to *out do write(&errout, "out[", i, "] = ", image(out[i])) + } + if notblank(in) then + if notblank(out) then + &null # this is OK, so do nothing + else + remove_initial_blanks(out) + else # input begins with blanks + if notblank(out) then + insert_initial_newline(out) + else + &null # this is OK, so do nothing + return +end + +procedure blankstring(s) + s ? { tab(many(' \t')); return pos(0) } +end +#line 110 "pdcached.nw" +global cache, hitcount, misscount + +procedure strip_chunk_number(s) + s ? + if =("@" || ("begin" | "end") || " " || ("docs" | "code") || " ") then + return tab(1) + else + return s +end + +procedure find_in_cache(lines) + c := cache + every l := strip_chunk_number(!lines) do + c := \c[l] | return &null + return c[&null] +end + +procedure save_in_cache(lines, cached) + c := cache + i := 1 + while l := strip_chunk_number(lines[i]) do { + /c[l] := table() + c := c[l] + i := i + 1 + } + c[&null] := cached + return +end + +link xcode + +procedure loadcache(cachename) + cache := xdecode(c := open(cachename)) | table() + close(\c) + return +end + +link numbers + +procedure savecache(cachename) + write(&errout, "Cache misses ", + frn(real(misscount) * 100.0 / real(misscount + hitcount), 0, 1), "% of ", + misscount+hitcount, " queries") + if misscount = 0 then return + system("lockfile " || cachename || ".lock") + xencoden(cache, cachename || ".new") + rename(cachename || ".new", cachename) | stop("Could not update cache") + remove(cachename || ".lock") + return # forget the other! + + c := open(cachename || ".fast", "w") | stop("cannot open fast cache") + wval(c, cache) + close(c) +end +#line 165 "pdcached.nw" +procedure readval(f) + local stack + stack := [] + while l := read(f) do + l ? case move(1) of { + "s" : push(stack, tab(0)) + "l" : push(stack, []) + "t" : push(stack, table()) + "n" : push(stack, &null) + "a" : { v := pop(stack); put(stack[1], v) } + "k" : { v := pop(stack); k := pop(stack); stack[1][k] := v } + } + return stack[1] +end + +procedure wval(f, v) + return case type(v) of { + "string" : write(f, "s", v) + "list" : wlis (f, v) + "table" : wtab (f, v) + "null" : write(f, "n") + default : stop("writing unknown type") + } +end + +procedure wlis(f, l) + write(f, "l") + every v := !l do { + wval(f, v); write(f, "a") + } + return +end + +procedure wtab(f, t) + write(f, "t") + every k := key(t) do { + wval(f, k); wval(f, t[k]); write(f, "k") + } + return +end +#line 208 "pdcached.nw" +procedure notblank(l) + every x := !l do + x ? + if ="@nl" & pos(0) then + fail + else if ="@text " & { tab(many(' \t')); not(pos(0)) } then + return x +end +#line 217 "pdcached.nw" +procedure remove_initial_blanks(l) + p := [] + while c := get(l) do + c ? + if ="@nl" | (="@text " & { tab(many(' \t')); pos(0) }) then { + &null # skip this baby + } else if =("@text" | "@end docs") then { + while push(l, pop(p)) + return l + } else { + push(p, c) + } + while push(l, pop(p)) + return l +end +#line 233 "pdcached.nw" +procedure insert_initial_newline(l) + p := [] + while c := get(l) do + if match("@text "|"@end docs", c) then { + push(l, c) + push(l, "@nl") + while push(l, pop(p)) + return l + } else { + push(p, c) + } + push(l, "@nl") + while push(l, pop(p)) + return l +end diff --git a/web/noweb/src/icon/pdcached.nw b/web/noweb/src/icon/pdcached.nw new file mode 100644 index 0000000000..b85d3ff9aa --- /dev/null +++ b/web/noweb/src/icon/pdcached.nw @@ -0,0 +1,248 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +This code pipes documentation chunks through a command. +If given option [[-nearcode]], it pipes only documentation chunks that +are next to code chunks; it drops all other documentation chunks. +<<*>>= +procedure main(args) + cachename := get(args) | stop("pdcached: no cache") + if args[1] == "-nearcode" then + nearcode := get(args) + cmd := get(args) | stop("pdcached: no command") + + cmd := "sed '/^@begin docs /s/[0-9][0-9]*/0/' | unmarkup | " || cmd || + " | markup | grep -v '^@file $'" + + last := &null + loadcache(cachename) + + while line := read() do + {## write("====> process ", image(line), "; last = ", type(last), + ## "(", *\last | "?", "), lastcode = ", image(lastcode)) + if match("@begin code ", line) then { + if type(last) == "list" then + pipeout(last, cmd) + last := "code" + until match("@end code ", line) do { + write(line) + line := read() + } + write(line) +#write("******until completed with ", line) + } else if match("@begin docs ", line) then { + l := [line] + until match("@end docs ", line) do + put(l, line := read()) +#write("******until completed with ", line) + put(l, line) + if /nearcode then { + pipeout(l, cmd) + last := &null + } else if last === "code" & notblank(l) then { + pipeout(l, cmd) + every write("@begin docs 0" | "@nl" | "@end docs 0") + last := &null + } else { + last := l + } + } else { + write(line) + } + } + savecache(cachename) + return +end + +<<*>>= +procedure pipeout(lines, cmd) + local cached + static tmp + initial { tmp := "/tmp/pdcached.out" + hitcount := misscount := 0 + } + cached := find_in_cache(lines) + if /cached then { + misscount +:= 1 + f := open(cmd || " > " || tmp, "wp") + every write(f, !lines) + close(f) + f := open(tmp, "r") + cached := [] + while put(cached, read(f)) + close(f) + adjustblanks(lines, cached) + save_in_cache(lines, cached) + } else { + adjustblanks(lines, cached) + hitcount +:= 1 + } + every write(!cached) + return +end +@ +Make sure if it begins blank before processing, it begins blank after +processing. +<<*>>= +procedure adjustblanks(in, out) + if &fail & find("Applying a primitive", !in) then { + write(&errout, "For ", image(in[1]), "/", image(out[1]), ", notblank(in) = ", + image(notblank(in)) | "<failed>", + "; blankstring(out[1]) = ", image(blankstring(out[1])) | "<failed>") + every i := 1 to *in do write(&errout, " in[", i, "] = ", image( in[i])) + every i := 1 to *out do write(&errout, "out[", i, "] = ", image(out[i])) + } + if notblank(in) then + if notblank(out) then + &null # this is OK, so do nothing + else + remove_initial_blanks(out) + else # input begins with blanks + if notblank(out) then + insert_initial_newline(out) + else + &null # this is OK, so do nothing + return +end + +procedure blankstring(s) + s ? { tab(many(' \t')); return pos(0) } +end +@ +<<*>>= +global cache, hitcount, misscount + +procedure strip_chunk_number(s) + s ? + if =("@" || ("begin" | "end") || " " || ("docs" | "code") || " ") then + return tab(1) + else + return s +end + +procedure find_in_cache(lines) + c := cache + every l := strip_chunk_number(!lines) do + c := \c[l] | return &null + return c[&null] +end + +procedure save_in_cache(lines, cached) + c := cache + i := 1 + while l := strip_chunk_number(lines[i]) do { + /c[l] := table() + c := c[l] + i := i + 1 + } + c[&null] := cached + return +end + +link xcode + +procedure loadcache(cachename) + cache := xdecode(c := open(cachename)) | table() + close(\c) + return +end + +link numbers + +procedure savecache(cachename) + write(&errout, "Cache misses ", + frn(real(misscount) * 100.0 / real(misscount + hitcount), 0, 1), "% of ", + misscount+hitcount, " queries") + if misscount = 0 then return + system("lockfile " || cachename || ".lock") + xencoden(cache, cachename || ".new") + rename(cachename || ".new", cachename) | stop("Could not update cache") + remove(cachename || ".lock") + return # forget the other! + + c := open(cachename || ".fast", "w") | stop("cannot open fast cache") + wval(c, cache) + close(c) +end +<<*>>= +procedure readval(f) + local stack + stack := [] + while l := read(f) do + l ? case move(1) of { + "s" : push(stack, tab(0)) + "l" : push(stack, []) + "t" : push(stack, table()) + "n" : push(stack, &null) + "a" : { v := pop(stack); put(stack[1], v) } + "k" : { v := pop(stack); k := pop(stack); stack[1][k] := v } + } + return stack[1] +end + +procedure wval(f, v) + return case type(v) of { + "string" : write(f, "s", v) + "list" : wlis (f, v) + "table" : wtab (f, v) + "null" : write(f, "n") + default : stop("writing unknown type") + } +end + +procedure wlis(f, l) + write(f, "l") + every v := !l do { + wval(f, v); write(f, "a") + } + return +end + +procedure wtab(f, t) + write(f, "t") + every k := key(t) do { + wval(f, k); wval(f, t[k]); write(f, "k") + } + return +end +@ +Manipulating docs chunks. +<<*>>= +procedure notblank(l) + every x := !l do + x ? + if ="@nl" & pos(0) then + fail + else if ="@text " & { tab(many(' \t')); not(pos(0)) } then + return x +end +<<*>>= +procedure remove_initial_blanks(l) + p := [] + while c := get(l) do + c ? + if ="@nl" | (="@text " & { tab(many(' \t')); pos(0) }) then { + &null # skip this baby + } else if =("@text" | "@end docs") then { + while push(l, pop(p)) + return l + } else { + push(p, c) + } + while push(l, pop(p)) + return l +end +<<*>>= +procedure insert_initial_newline(l) + p := [] + while c := get(l) do + if match("@text "|"@end docs", c) then { + push(l, c) + push(l, "@nl") + while push(l, pop(p)) + return l + } else { + push(p, c) + } + push(l, "@nl") + while push(l, pop(p)) + return l +end diff --git a/web/noweb/src/icon/pipedocs.icn b/web/noweb/src/icon/pipedocs.icn new file mode 100644 index 0000000000..fc86e7a03b --- /dev/null +++ b/web/noweb/src/icon/pipedocs.icn @@ -0,0 +1,65 @@ +#line 5 "pipedocs.nw" +procedure main(args) + if args[1] == "-nearcode" then + nearcode := get(args) + cmd := get(args) | stop("pipedocs: no command") + + cmd := "sed '/^@begin docs /s/[0-9][0-9]*/0/' | unmarkup | " || cmd || + " | awk ' NR == 1 && /^[ \t]*$/ {next}\n{print}\n' | markup | grep -v '^@file $'" + + last := &null + + while line := read() do + {## write("====> process ", image(line), "; last = ", type(last), + ## "(", *\last | "?", "), lastcode = ", image(lastcode)) + if match("@begin code ", line) then { + if type(last) == "list" then + pipeout(last, cmd) + last := "code" + until match("@end code ", line) do { + write(line) + line := read() + } + write(line) +#write("******until completed with ", line) + } else if match("@begin docs ", line) then { + l := [line] + until match("@end docs ", line) do + put(l, line := read()) +#write("******until completed with ", line) + put(l, line) + if /nearcode then { + pipeout(l, cmd) + last := &null + } else if last === "code" & notblank(l) then { + pipeout(l, cmd) + every write("@begin docs 0" | "@nl" | "@end docs 0") + last := &null + } else { + last := l + } + } else { + write(line) + } + } + return +end + +#line 52 "pipedocs.nw" +procedure pipeout(lines, cmd) + f := open(cmd, "wp") + every write(f, !lines) + return close(f) +end + +#line 59 "pipedocs.nw" +procedure notblank(l) + every x := !l do + x ? + if ="@nl" & pos(0) then + fail + else if ="@text " & { tab(many(' \t')); not(pos(0)) } then + return x +end + + diff --git a/web/noweb/src/icon/pipedocs.nw b/web/noweb/src/icon/pipedocs.nw new file mode 100644 index 0000000000..bc2ac8df5d --- /dev/null +++ b/web/noweb/src/icon/pipedocs.nw @@ -0,0 +1,69 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +This code pipes documentation chunks through a command. +If given option [[-nearcode]], it pipes only documentation chunks that +are next to code chunks; it drops all other documentation chunks. +<<*>>= +procedure main(args) + if args[1] == "-nearcode" then + nearcode := get(args) + cmd := get(args) | stop("pipedocs: no command") + + cmd := "sed '/^@begin docs /s/[0-9][0-9]*/0/' | unmarkup | " || cmd || + " | awk ' NR == 1 && /^[ \t]*$/ {next}\n{print}\n' | markup | grep -v '^@file $'" + + last := &null + + while line := read() do + {## write("====> process ", image(line), "; last = ", type(last), + ## "(", *\last | "?", "), lastcode = ", image(lastcode)) + if match("@begin code ", line) then { + if type(last) == "list" then + pipeout(last, cmd) + last := "code" + until match("@end code ", line) do { + write(line) + line := read() + } + write(line) +#write("******until completed with ", line) + } else if match("@begin docs ", line) then { + l := [line] + until match("@end docs ", line) do + put(l, line := read()) +#write("******until completed with ", line) + put(l, line) + if /nearcode then { + pipeout(l, cmd) + last := &null + } else if last === "code" & notblank(l) then { + pipeout(l, cmd) + every write("@begin docs 0" | "@nl" | "@end docs 0") + last := &null + } else { + last := l + } + } else { + write(line) + } + } + return +end + +<<*>>= +procedure pipeout(lines, cmd) + f := open(cmd, "wp") + every write(f, !lines) + return close(f) +end + +<<*>>= +procedure notblank(l) + every x := !l do + x ? + if ="@nl" & pos(0) then + fail + else if ="@text " & { tab(many(' \t')); not(pos(0)) } then + return x +end + + diff --git a/web/noweb/src/icon/promeladefs.icn b/web/noweb/src/icon/promeladefs.icn new file mode 100644 index 0000000000..183e53d16b --- /dev/null +++ b/web/noweb/src/icon/promeladefs.icn @@ -0,0 +1,97 @@ +#line 2 "promeladefs.nw" +global showlocal +procedure rcsinfo () + return "$Id: promeladefs.nw,v 1.17 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + showlocal := !args == "-local" + go() +end +#line 12 "promeladefs.nw" +procedure postpass(name, arg) + static kind, mtype + initial { kind := "bogus" } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if \mtype then + mtype := write_mtypes() + else if (optwhite(), =("proctype "|"chan ")) then { + tab(many(' ')) + writedefn(tab(upto(' ():;'))) + } else if (optwhite(), ="mtype", optwhite(), ="{") then + mtype := write_mtypes() + } + return +end + +procedure write_mtypes() + static start, alnum + initial { start := &letters ++ '/,}'; alnum := &letters ++ &digits ++ '_' } + optwhite() + while any(start) do { + if ="/*" then tab(upto("*/") | 0) + else if ="," then &null + else if ="}" then return &null + else if any(alnum) then writedefn(tab(many(alnum))) + else if pos(0) then return 1 + else if ="/" then return &null + else { write(&errout, "bad mtype ", tab(0)); return &null } + optwhite(); + } + return if pos(0) then 1 else &null +end + + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end + + +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/promeladefs.nw b/web/noweb/src/icon/promeladefs.nw new file mode 100644 index 0000000000..cda5d81336 --- /dev/null +++ b/web/noweb/src/icon/promeladefs.nw @@ -0,0 +1,56 @@ +\section{Finding Promela definitions} +<<*>>= +global showlocal +procedure rcsinfo () + return "$Id: promeladefs.nw,v 1.17 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + showlocal := !args == "-local" + go() +end +<<*>>= +procedure postpass(name, arg) + static kind, mtype + initial { kind := "bogus" } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if \mtype then + mtype := write_mtypes() + else if (optwhite(), =("proctype "|"chan ")) then { + tab(many(' ')) + writedefn(tab(upto(' ():;'))) + } else if (optwhite(), ="mtype", optwhite(), ="{") then + mtype := write_mtypes() + } + return +end + +procedure write_mtypes() + static start, alnum + initial { start := &letters ++ '/,}'; alnum := &letters ++ &digits ++ '_' } + optwhite() + while any(start) do { + if ="/*" then tab(upto("*/") | 0) + else if ="," then &null + else if ="}" then return &null + else if any(alnum) then writedefn(tab(many(alnum))) + else if pos(0) then return 1 + else if ="/" then return &null + else { write(&errout, "bad mtype ", tab(0)); return &null } + optwhite(); + } + return if pos(0) then 1 else &null +end + + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end + + diff --git a/web/noweb/src/icon/sl2h.icn b/web/noweb/src/icon/sl2h.icn new file mode 100644 index 0000000000..22a571c534 --- /dev/null +++ b/web/noweb/src/icon/sl2h.icn @@ -0,0 +1,2167 @@ +#line 217 "l2h.nw" +procedure star(cs) + suspend cs | (cs || "*") +end +#line 596 "l2h.nw" +procedure asciiCharCode(S) + return emit_text(S, char(2(optwhite(), TeXnumber(), optwhite()))) +end +procedure TeXnumber() + return integer( + (="'", "8r" || tab(many(&digits))) | + (="\"", "16r" || tab(many(&digits))) | + tab(many(&digits))) +end +#line 671 "l2h.nw" +procedure convert(S, optstring) + static specials + initial { + +#line 819 "l2h.nw" +activetab := table(unknown_cs) +activeclosure := table() +#line 969 "l2h.nw" +cstab := table(unknown_cs) +csclosure := table() +#line 987 "l2h.nw" +unknown_set := set() +#line 1015 "l2h.nw" +every begintab | endtab := table(unknown_env) +every begincl | endcl := table() +#line 1028 "l2h.nw" +unknown_envs := set() +#line 1381 "l2h.nw" +liststack := [] +# write(&errout, "Initialized liststack") +suspendstack := [] +#line 1961 "l2h.nw" +countertab := table() +#line 1969 "l2h.nw" +accent_name := table() +accent_valid := table('') +accent_name ["`"] := "grave" +accent_valid["`"] := 'AEIOUaeiou' +accent_name ["'"] := "acute" +accent_valid["'"] := 'AEIOUYaeiouy' +accent_name ["^"] := "circ" +accent_valid["^"] := 'AEIOUaeiou' +accent_name ["hat"] := "circ" +accent_valid["hat"] := 'AEIOUaeiou' +accent_name ["\""] := "uml" +accent_valid["\""] := 'AEIOUaeiouy' +accent_name ["~"] := "tilde" +accent_valid["~"] := 'ANOano' +accent_name ["="] := "bar" +accent_name ["."] := "dot" +accent_name ["u"] := "u" +accent_name ["v"] := "v" +accent_name ["H"] := "H" +accent_name ["t"] := "t" +accent_name ["c"] := "cedil" +accent_valid["c"] := 'Cc' +accent_name ["d"] := "underdot" +accent_name ["b"] := "underbar" +#line 2675 "l2h.nw" +csfunctions := table() +#line 675 "l2h.nw" + +#line 114 "l2h.nw" +substitution(",", " ") +substitution(" ", " ") +substitution("\n", "\n") +substitution("\t", " ") +ignore("raggedright") +ignore("@") +ignore("/") # no italic correction +substitution("", "\n") # \<newline> treated as request for newline +every c := !"$%#{}_" do + substitution(c, c) +substitution("&", "&") +argblock("emph", "em") +every argblock("footnote" | "footnotetext" | "thanks", + " <b>[</b>", "<b>]</b> ") # put footnotes in bold brackets +substitution("today", &date) +#line 132 "l2h.nw" +ignore("textrm") # html can't switch to default font! +argblock("textit", "i") +argblock("textbf", "b") +argblock("textsl", "i") +ignore("textsc") +argblock("texttt", "tt") +ignore("textsf") +ignore("boldmath") +# \boldmath could be done by introducing S.mathfont, but I don't want to! +#line 142 "l2h.nw" +ignore("mathrm") # html can't switch to default font! +argblock("mathit", "i") +argblock("mathbf", "b") +argblock("mathtt", "tt") +ignore("mathsf") +argblock("mathcal", "i") +every ignore("scriptstyle"|"displaystyle"|"scriptscriptstyle") +every ignore("mathord"|"mathbin"|"mathrel") +#line 152 "l2h.nw" +every ignore("tiny" | "scriptsize" | "footnotesize" | "small" | "normalsize" | + "large" | "Large" | "LARGE" | "huge" | "Huge") +#line 160 "l2h.nw" +every accent(key(accent_name)) +every ignore("dag" | "ddag") +substitution("S", "§") +substitution("P", "¶") +substitution("copyright", "©") +substitution("pounds", "£") +substitution("o", "ø") +substitution("O", "Ø") +substitution("aa", "å") +substitution("AA", "Å") +substitution("ae", "æ") +substitution("AE", "&Aelig;") +#line 175 "l2h.nw" +argblockv("part", "h1", &null, "*[") +argblockv("chapter", "h1", &null, "*[") +argblockv("section", "h2", &null, "*[") +argblockv("subsection", "h3", &null, "*[") +argblockv("subsubsection", "h4", &null, "*[") +argblockv("paragraph", "h5", &null, "*[") +argblockv("subparagraph", "h6", &null, "*[") +ignore("appendix") +auxfile("tableofcontents", "toc", + "<p>\n<tableofcontents>\n<b>[Table of contents]</b>\n</tableofcontents>\n<p>", + "<h2>Table of Contents</h2>\n<tableofcontents>\n", "\n</tableofcontents>\n") +cstab["tableofcontents"] := Ctableofcontents # override to call set_toclevel +ignore("listoftables") +ignore("listoffigures") +ignore("addtocontents", "{{") +ignoreenv("filecontents", "{") +#line 198 "l2h.nw" +cstab["("] := Cmath +cstab[")"] := Cmath_end +cstab["["] := Cdisplaymath +cstab["]"] := Cdisplaymath_end +ignoreenv("equation") +every table_env(star("eqnarray"), 0, " ", "<blockquote><i>", "</i></blockquote>") + # also lame +substitution("frac", "<b>frac</b>") +substitution("sqrt", "<b>frac</b>") +every substitution("ldots" | "cdots" | "vdots", "...") +ignore("left") +ignore("right") +ignore("overline") +substitution(":", " ") +substitution(";", " ") +ignore("!") +#line 223 "l2h.nw" +substitution("Diamond", "<>") +substitution("langle", "<") +substitution("rangle", ">") +substitution("le", "<=") +substitution("ne", "!=") +substitution("ge", ">=") +substitution("times", "×") +substitution("divide", "÷") +substitution("bmod", "</i>mod<i>") # better hook in with math +substitution("equiv", "===") +every x := "arccos" | "arcsin" | "arctan" | "arg" | "cos" | "cosh" | + "cot" | "coth" | "csc" | "deg" | "det" | "dim" | "exp" | + "gcd" | "hom" | "lim" | "liminf" | "limsup" | "ln" | "log" | + "max" | "min" | "sec" | "sin" | "sinh" | "sup" | "tan" | "tanh" +do + substitution(x, "</i>" || x || "<i>") +substitution("liminf", "</i>lim inf<i>") +substitution("limsup", "</i>lim sup<i>") +every x := "alpha" | "beta" | "gamma" | "delta" | "epsilon" | "zeta" | "eta" | + "theta" | "iota" | "kappa" | "lambda" | "nu" | "xi" | "pi" | + "rho" | "sigma" | "tau" | "upsilon" | "phi" | "chi" | "psi" | "omega" | + "in" +do + substitution(x, "<b>" || x || "</b>") +substitution("mu", "µ") +every x := "Gamma" | "Delta" | "Theta" | "Lambda" | "Xi" | "Pi" | "Sigma" | + "Upsilon" | "Phi" | "Psi" | "Omega" +do + substitution(x, "<b>" || x || "</b>") +#line 255 "l2h.nw" +substitution("lfloor", "</i>|_<i>") +substitution("rfloor", "</i>_|<i>") +substitution("leq", "<=") +substitution("geq", ">=") +substitution("ll", "«") +substitution("gg", "»") +substitution("Rightarrow", "==>") +substitution("rightarrow", "-->") +substitution("approx", "<u>~</u>") +#line 268 "l2h.nw" +envblock("quote", "blockquote") +envblock("quotation", "blockquote") +envblock("center", "blockquote") +envblock("flushleft", "blockquote") +envblock("flushright", "blockquote") +envblock("verse", "blockquote") +verbatim("verbatim", escape_HTML_specials, "pre") +cstab["verb"] := Cverb +cstab["verb*"] := Cverb +csclosure["verb*"] := 1 +cstab["verbatiminput"] := Cverbatiminput +csclosure["verbatiminput"] := verbatim_cl(escape_HTML_specials) +#line 284 "l2h.nw" +cstab["item"] := Citem +csclosure["item"] := [item_cl("<li>", "", "<li>")] +listenv("itemize", "ul") +listenv("enumerate", "ol") +listenv("description", "dl") +#line 292 "l2h.nw" +ignore("documentstyle", "[{") +ignore("documentclass", "[{") +ignore("usepackage", "[{") +ignore("pagestyle", "{") +ignore("thispagestyle", "{") +ignore("pagenumbering", "{") +ignore("newcounter", "{") +ignore("global") +ignore("etalchar") # used in the .bbl files: \newcommand{\etalchar}[1]{$^{#1}$} +#line 307 "l2h.nw" +argblockv("title", "h1") +argblockv("author","address") +argblockv("date", "b") +substitution("maketitle", "<!--title goes here-->") +ignoreenv("titlepage") +envblock("abstract", "<h2>Abstract</h2><blockquote>", "</blockquote>") +#line 322 "l2h.nw" +cstab["label"] := Clabel +cstab["ref"] := Cref +cstab["pageref"] := Cref +cstab["subpageref"] := Cref +cstab["chunklabel"] := Clabel +#line 332 "l2h.nw" +ignore("bibliographystyle", "{") +auxfile("bibliography", "bbl", "<b>[BibTeX bibliography]</b>", &null, &null, "{") +envblock("thebibliography", "<h2>References</h2>", "", "{") +every cstab["cite" | "citeN" | "opencite" | "openciteN" | "citeyear"] := Ccite +cstab["bibitem"] := Cbibitem +ignore("newblock") +ignore("nocite", "{") +#line 343 "l2h.nw" +every ignore("input" | "include" | "includeonly", "{") +# filecontents not done yet +#line 348 "l2h.nw" +cstab["\\"] := Cbackback +substitution("linebreak", "<br>") +ignore("-") +ignoreenv("sloppypar") +ignore("sloppy") +#line 357 "l2h.nw" +substitution("pagebreak", "<hr>") +substitution("newpage", "<hr>") +substitution("clearpage", "<hr>") +ignore("enlargethispage", "*{") +#line 364 "l2h.nw" +ignore("mbox") +ignore("makebox", "([[") # ( comes from picture area +ignore("fbox") +ignore("framebox", "[[") # could insert horizontal rules, but why? +ignore("newsavebox", 1) +ignore("sbox", 2) +ignore("savebox", "{[[{") +ignore("usebox", 1) +envblock("minipage", "blockquote", &null, "[{") +argblock("parbox", "blockquote", &null, "[{") +#line 377 "l2h.nw" +ignore("hspace", "*{") +ignore("hfil") +ignore("hfill") +ignore("vspace", "*{") +ignore("vfil") +ignore("vfill") +#line 386 "l2h.nw" +ignore("newlength", "{") +ignore("setlength", "{{") +ignore("addtolength", "{{") +#line 392 "l2h.nw" +envblock("picture", "<b>[picture]</b>", "", "((") +ignore("put", "({") +ignore("multiput", "(({{") +ignore("dashbox", "{([") +ignore("line", "({") +ignore("vector", "({") +ignore("shortstack", "[") +ignore("circle", "*{") +ignore("oval", "([") +ignore("frame") +ignore("thinlines") +ignore("thicklines") +#line 408 "l2h.nw" +every envblock(star("figure"), "<hr>", "<hr>", "[") +every envblock(star("table"), "<hr>", "<hr>", "[") +argblock("caption", "b") # captions in bold +#line 415 "l2h.nw" +envblock("tabbing", "blockquote") +# \= is accent +ignore(">") +ignore("+") +ignore("kill") +#line 423 "l2h.nw" +tabular("array", "[") +tabular("tabular", "[") +tabular("tabularx", "{[") +tabular("tabular*", "{[") +every cstab["multicolumn"|"multispan"] := Cmulticolumn +cstab["span"] := Cspan +cstab["noalign"] := Cnoalign +cstab["hline"] := Chline +cstab["toprule"] := Chline +cstab["midrule"] := Chline +cstab["bottomrule"] := Chline +ignore("cline", "{") +ignore("newcolumntype", "{{") +#line 439 "l2h.nw" +ignore("newcommand", "A[[{") +ignore("renewcommand", "A[[{") +ignore("providecommand", "A[[{") +ignore("newenvironment", "{[{{") +ignore("renewenvironment", "{[{{") +ignore("newtheorem", "{{") +#line 450 "l2h.nw" +cstab["setcounter"] := Csetcounter +ignore("addtocounter", "{{") +#line 455 "l2h.nw" +cstab["makeatletter"] := Cmakeatletter +cstab["makeatother"] := Cmakeatother +#line 460 "l2h.nw" +every fontchange("tt" | "ttfamily", "tt") +every fontchange("bf" | "bfseries", "b") +every fontchange("it" | "itshape", "i") +fontchange("sl", "i") +fontchange("em", "em") +ignore("rm") # html can't switch to default font! +ignore("sf") +ignore("sc") +#line 470 "l2h.nw" +ignore("rmfamily") +ignore("normalfont") +#line 473 "l2h.nw" +ignoreenv("document") +#line 475 "l2h.nw" +substitution("LaTeX", "LaTeX") +#line 477 "l2h.nw" +ignore("numberline", "{") +ignore("protect") +ignore("onecolumn") +ignore("twocolumn", "C") +ignore("typeout", "[{") +ignore("closedbib") +#line 484 "l2h.nw" +every ignore("leftmargini" | "leftmarginii" | "labelsep" | "fboxsep", "=") +every ignore("tabcolsep", "=") +every ignore("evensidemargin" | "marginparsep" | "marginparwidth" | + "oddsidemargin" | "textheight" | "textwidth" | "topmargin", "=") +#line 489 "l2h.nw" +ignore("DeclareMathVersion", "{") +ignore("mathversion", "{") +ignore("setpapersize", "{") +ignore("setmarginsrb", "{{{{{{{{") +ignore("marginparwidth", "=") +ignore("marginparsep", "=") +#line 498 "l2h.nw" +ignoreenv("multicols", "{C") +cstab["citeN"] := Ccite +ignore("afterpage", "{") +every cstab["psfig"|"epsfig"] := Cepsfig +cstab["includegraphics"] := Cincludegraphics +ignore("newcolumntype", "{{") +#line 507 "l2h.nw" +cstab["kill"] := Ckill +#line 511 "l2h.nw" +activesubst("~", " ") +argblock("centerline", "<br>", "<br>") +substitution("cr", "<br>") +substitution("hrule", "<hr>") +substitution("vrule", "|") +substitution("hrulefill", "------") +ignore("hyphenation", "{") +ignore("hbox") +ignore("rlap") +ignore("llap") +ignore("vbox") +ignore("vtop") +ignore("hidewidth") +ignore("message", "{") +ignore("relax") +ignore("null") +ignore("offinterlineskip") +ignore("omit") +ignore("newdimen", "{") +ignore("nobreak") +#line 532 "l2h.nw" +cstab["par"] := implicit_paragraph +cstab["smallskip"] := implicit_paragraph +cstab["medskip"] := implicit_paragraph +cstab["bigskip"] := implicit_paragraph +cstab["vskip"] := implicit_paragraph +csclosure["vskip"] := "=" +#line 544 "l2h.nw" +every ignore("begingroup" | "endgroup" | "bgroup" | "egroup") +#line 546 "l2h.nw" +cstab["newif"] := Cnewif +cstab["iffalse"] := Ciffalse +cstab["iftrue"] := Ciftrue +cstab["ifhtml"] := Ciftrue # false in LaTeX, but true when converting! +cstab["else"] := Celse +cstab["fi"] := Cfi +cstab["ifx"] := cstab["if"] := cstab["ifnum"] := Ciffalse +#line 556 "l2h.nw" +ignore("let", "A=") +every ignore("hfuzz" | "parindent" | "parskip" | "baselineskip", "=") +every ignore("hbadness" | "hsize" | "vsize" | "overfullrule" | "tabskip", "=") +every ignore("extrarowheight" | "codemargin", "=") +every ignore("looseness", "=") +substitution("hskip", " ", "=") +ignore("setbox", "{=") +every ignore("box" | "unhbox" | "unvbox", "{") +#line 565 "l2h.nw" +ignore("unskip") +ignore("hss") +ignore("phantom", "{") +every ignore("kern" | "lower" | "spacefactor", "=") # a cheat, but works +every ignore("clubpenalty" | "widowpenalty", "=") +#line 578 "l2h.nw" +every ignore("expandafter" | "indent" | "noindent" | "leavevmode" | "strut") +ignore("def", 1) +#line 581 "l2h.nw" +substitution("TeX", "TeX") +substitution("BibTeX", "BibTeX") +substitution("MF", "METAFONT") +#line 594 "l2h.nw" +cstab["char"] := asciiCharCode +#line 608 "l2h.nw" +macro("nwanchorto", 2, "<a href=\"#$1\">#2</a>") +macro("nwanchorname", 2, "<a name=\"#$1\">#2</a>") +ignore("nwaddbox", "{") +verbatim("latexonly", do_nothing) +verbatim("rawhtml", emit_text) +#line 617 "l2h.nw" +substitution("quad", " ") +#line 624 "l2h.nw" +ignore("noweboptions", 1) +#line 630 "l2h.nw" +substitution("nowebindex", "<nowebindex>") +substitution("nowebchunks", "<nowebchunks>") +ignore("nowebsize") +#line 634 "l2h.nw" +envblock("fields", "blockquote", &null, "[") # lame; could try to <tt> 1st col +envblock("fields*", "blockquote", &null, "{") # lame; could try to <tt> 1st col +ignore("citeauthoryear", "{{{") +ignore("authoryear", "{{") +substitution("bibrule", "--------") +let("bibskip", "par") +every cstab["anoncite"|"authorcite"] := Ccite +#line 644 "l2h.nw" +ignore("pssilent") +ignore("psnoisy") +#line 1012 "l2h.nw" +cstab["begin"] := do_begin +cstab["end"] := do_end +#line 1385 "l2h.nw" +cstab["suspend"] := Csuspend +cstab["resume"] := Cresume +#line 1603 "l2h.nw" +cstab["uppercase"] := Cuppercase +#line 1899 "l2h.nw" +cstab["contentsline"] := Ccontentsline +#line 676 "l2h.nw" + +#line 2677 "l2h.nw" +csfunctions["argblock"] := argblock +csfunctions["argblockv"] := argblockv +csfunctions["envblock"] := envblock +csfunctions["fontchange"] := fontchange +csfunctions["ignore"] := ignore +csfunctions["ignoreenv"] := ignoreenv +csfunctions["let"] := let +csfunctions["letenv"] := letenv +csfunctions["listenv"] := listenv +csfunctions["substitution"] := substitution +csfunctions["activesubst"] := activesubst +csfunctions["closure"] := let_closure +csfunctions["let_closure"] := let_closure +csfunctions["newcommand"] := l2h_macro +csfunctions["macro"] := l2h_macro +csfunctions["environment"] := l2h_environment +csfunctions["tabular"] := tabular +#line 677 "l2h.nw" + specials := '\\{}<>-\'`"%$&~\n\0\1' + } + + if \optstring then return optstring ? convert(S) + else { + +#line 867 "l2h.nw" +l := S.newtext +S.newtext := [] +while c := get(l) do + c.proc!c.args +#line 700 "l2h.nw" +if S.mode == "V" & any(~'\\{}<>%\n\t ') then S.mode := "H" +emit_text(S, tab(upto(specials) | 0)) +while not pos(0) do + if S.mode == "Q" then { # quoting + emit_text(S, tab(upto('\1') | 0)) + if ="\1" then { + emit_text(S, "\1") + S.mode := S.quotemode + } + } else { + if any(S.activechars) then + do_activechar(S, move(1)) + else + case move(1) of { + "\\" : { +#line 941 "l2h.nw" +cs := if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) +if /S.ignoring | cs == ("else"|"fi") | cstab[cs] === (Ciffalse|Ciftrue) then + do_cs(S, cs) +else + &null # error("### Ignoring \\", cs) +#line 714 "l2h.nw" + } + "{" : { +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 715 "l2h.nw" + } + "}" : { +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 716 "l2h.nw" + } + "%" : { +#line 1066 "l2h.nw" +parse_dynamic_add(S) +delay_newtext(S) +eat_comment(S) +#line 717 "l2h.nw" + } + "\n" : { +#line 1313 "l2h.nw" +emit_text(S, "\n") +if /S.ignoring then Cnewline(S) +#line 718 "l2h.nw" + } + "$" : { +#line 1301 "l2h.nw" +if /S.ignoring then + if ="$" then + if S.mode == "M" then { Cdisplaymath_end(S); S.mode := "V" } + else { Cdisplaymath(S); S.mode := "M" } + else + if S.mode == "M" then { Cmath_end(S); S.mode := "H" } + else { Cmath(S); S.mode := "M" } +#line 719 "l2h.nw" + } + "&" : ampersand(S) + "\0" : {S.quotemode := S.mode; S.mode := "Q"; emit_text(S, "\0")} + # remaining cases simply escape HTML specials + "<" : emit_text(S, "<") + ">" : emit_text(S, ">") + "\"" : emit_text(S, """) + "-" : { +#line 735 "l2h.nw" +if S.mode == "V" then S.mode := "H" +if S.mode == "H" then + if ="--" then emit_text(S, "—") + else if ="-" then emit_text(S, "–") + else emit_text(S, "-") +else + emit_text(S, "-") +#line 726 "l2h.nw" + } + "`" : { +#line 746 "l2h.nw" +if S.mode == "V" then S.mode := "H" +if \html_quotes & S.mode == "H" then + if ="`" then emit_text(S, "“") + else emit_text(S, "‘") +else + emit_text(S, "`") +#line 727 "l2h.nw" + } + "'" : { +#line 753 "l2h.nw" +if S.mode == "V" then S.mode := "H" +if \html_quotes & S.mode == "H" & ="'" then emit_text(S, "”") +else emit_text(S, "'") +#line 728 "l2h.nw" + } + } + if S.mode == "V" & any(~'\\{}<>%\n\t ') then S.mode := "H" + emit_text(S, tab(upto(specials++S.activechars) | 0)) + } +return 1(. S.the_text, S.the_text := "") # what's been converted +#line 683 "l2h.nw" + } +end +#line 744 "l2h.nw" +global html_quotes # render quotes as HTML 4 entities +#line 760 "l2h.nw" +record state(mode, quotemode +#line 788 "l2h.nw" +, text, the_text +#line 804 "l2h.nw" +, ignoring +#line 822 "l2h.nw" +, activechars +#line 844 "l2h.nw" +, newtext +#line 887 "l2h.nw" +, delayed_newtext +#line 897 "l2h.nw" +, open, closes +#line 949 "l2h.nw" +, csletters +#line 1082 "l2h.nw" +, comment +#line 1521 "l2h.nw" +, ifstack +#line 1628 "l2h.nw" +, tables +#line 760 "l2h.nw" + ) + # mode is Q, H, V, or M + # quotemode is saved mode: H, V, or M +#line 766 "l2h.nw" +procedure converter(mode) + /mode := "V" + return state(mode, mode +#line 790 "l2h.nw" +, accumulate_text, "" +#line 806 "l2h.nw" +, &null +#line 824 "l2h.nw" +, '~' +#line 846 "l2h.nw" +, [] +#line 889 "l2h.nw" +, [] +#line 899 "l2h.nw" +, [], [] +#line 951 "l2h.nw" +, &letters +#line 1084 "l2h.nw" +, "" +#line 1523 "l2h.nw" +, [] +#line 1630 "l2h.nw" +, [] +#line 768 "l2h.nw" + ) +end +#line 774 "l2h.nw" +procedure reset(S) + +#line 792 "l2h.nw" +S.text := accumulate_text +S.the_text := "" +#line 808 "l2h.nw" +S.ignoring := &null +#line 826 "l2h.nw" +S.activechars := '~' +#line 848 "l2h.nw" +S.newtext := [] +#line 891 "l2h.nw" +S.delayed_newtext := [] +#line 901 "l2h.nw" +every S.open | S.closes := [] +#line 953 "l2h.nw" +S.csletters := &letters +#line 1086 "l2h.nw" +S.comment := "" +#line 1525 "l2h.nw" +if *S.ifstack > 0 then S.ifstack := [] # keeps GC down +#line 1632 "l2h.nw" +S.tables := [] +#line 776 "l2h.nw" + return S +end +#line 783 "l2h.nw" +procedure accumulate_text(S, text) + S.the_text ||:= text + return +end +#line 800 "l2h.nw" +procedure emit_text(S, text) + return if \S.ignoring then "" else S.text(S, text) +end +#line 813 "l2h.nw" +global activetab, activeclosure +procedure do_activechar(S, c) + (activetab[c])(S, c, activeclosure[c]) + return +end +#line 852 "l2h.nw" +record closure(proc, args) +#line 857 "l2h.nw" +procedure before_next_newtext(S, proc, args) + push(S.newtext, closure(proc, args)) +end + +procedure after_next_newtext(S, proc, args) + put(S.newtext, closure(proc, args)) +end +#line 876 "l2h.nw" +procedure delay_newtext(S) + push(S.delayed_newtext, S.newtext) + S.newtext := [] + return +end + +procedure undelay_newtext(S) + S.newtext := \pop(S.delayed_newtext) | + {write(&errout, "This can't happen: no delayed_newtext"); &null[0]} +end +#line 903 "l2h.nw" +procedure after_next_open(S, proc, args) + return put(S.open, closure(proc, args)) +end + +procedure before_next_close(S, proc, args) + return push(S.closes[1], closure(proc, args)) # lost at top level +end + +procedure after_next_close(S, proc, args) + return put(S.closes[1], closure(proc, args)) # lost at top level +end +#line 960 "l2h.nw" +global cstab, csclosure + +procedure do_cs(S, cs) + tab(many(' \t')) # skip white space following CS + if pos(0) | any('\n') then before_next_newtext(S, skipblanks, [S]) + (cstab[cs])(S, cs, csclosure[cs]) + return +end +#line 976 "l2h.nw" +global show_unknowns +procedure unknown_cs(S, cs, cl) +# if S.text === ignore_text then return # a bit of a hack -- should no longer be needed + if \show_unknowns then S.text(S, "<b>\\" || cs || "</b>") + if not member(unknown_set, cs) then { + write(\unknown_file, "Warning: unknown control sequence \\", cs) + insert(unknown_set, cs) + } + return +end +#line 989 "l2h.nw" +global cstab, csclosure, unknown_set +#line 995 "l2h.nw" +global begintab, endtab, begincl, endcl + +procedure do_begin(S, cs, cl) + (="{", env := tab(upto('}')), ="}") | error("botched \\begin{...}") + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 1000 "l2h.nw" + (begintab[env])(S, env, begincl[env]) + return +end + +procedure do_end(S, cs, cl) + (="{", env := tab(upto('}')), ="}") | error("botched \\end{...}") + # write(&errout, "calling ", image(endtab[env]), " for \\end{", env, "}") + (endtab[env])(S, env, endcl[env]) + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1009 "l2h.nw" + return +end +#line 1018 "l2h.nw" +procedure unknown_env(S, env, cl) +### if S.text === ignore_text then return # a bit of a hack # no longer needed + if \show_unknowns then S.text(S, "<b>{" || env || "}</b>") + if not member(unknown_envs, env) then { + write(\unknown_file, "Warning: unknown environment {", env, "}") + insert(unknown_envs, env) + } + return +end +#line 1030 "l2h.nw" +global unknown_envs +#line 1034 "l2h.nw" +procedure warn_unknown(s, type, mark, rmark) + if *s > 0 then { + pushout("Unknown " || type || ": ") + every pushout(((\mark | "")\1) || !sort(s) || ((\rmark | "")\1) || " ") + pushout("\n") + } +end +#line 1042 "l2h.nw" +procedure pushout(s) + static col + initial col := 0 + if find("\n", s) then + s ? { + pushout(tab(upto('\n'))) + while ="\n" do {col := 0; write(&errout)} + pushout(tab(0)) + } + else { + col +:= *s + if col >= 79 then {writes(&errout, "\n "); col := *s + 2} + writes(&errout, s) + } + return +end +#line 1070 "l2h.nw" +procedure eat_comment(S) + S.comment ||:= tab(upto('\n') | 0) + if pos(0) then + before_next_newtext(S, eat_comment, [S]) + else { + undelay_newtext(S) + Ccomment(S) + S.comment := "" + } + return +end +#line 1106 "l2h.nw" +record verbatim_cl(output, terminator, html, translate_blank) + +procedure verbatim(name, output, html) + begintab[name] := Cverbatim + begincl [name] := verbatim_cl(output, &null, html) + return +end + +procedure Cverbatim(S, cs, cl) + if cl === begincl[cs] & /cl.terminator then + cl := begincl[cs] := + verbatim_cl(cl.output, "\\end{" || cs || "}", cl.html, cl.translate_blank) + emit_text(S, tag(\cl.html)) + delay_newtext(S) + do_verbatim(S, cl) + return +end +#line 1128 "l2h.nw" +procedure do_verbatim(S, cl) + if cl.output(S, tab(find(cl.terminator)), cl) then { + =cl.terminator + emit_text(S, endtag(\cl.html)) + undelay_newtext(S) + } else { + cl.output(S, tab(0), cl) + before_next_newtext(S, do_verbatim, [S, cl]) + } + return +end +#line 1142 "l2h.nw" +procedure escape_HTML_specials(S, s, cl) + s ? { + while emit_text(S, tab(upto('&<>" '))) do + case move(1) of { + "\"" : emit_text(S, """) + "&" : emit_text(S, "&") + "<" : emit_text(S, "<") + ">" : emit_text(S, ">") + " " : emit_text(S, if \cl.translate_blank then "·" else " ") + } + emit_text(S, tab(0)) + } + return +end +#line 1160 "l2h.nw" +procedure Cverb(S, cs, cl) + Cverbatim(S, cs, verbatim_cl(escape_HTML_specials, move(1), "tt", cl)) + return +end +#line 1166 "l2h.nw" +procedure Cverbatiminput(S, cs, cl) + local filename, f, line + if ="{" & filename := tab(upto('}')) & ="}" then { + f := \open(filename) | { emit_text(S, "<strong>Could not read file</strong> <tt>") + escape_HTML_specials(S, filename, cl) + emit_text(S, "</tt>") + return + } + emit_text(S, "\n<pre>\n") + while line := read(f) do { + escape_HTML_specials(S, line, cl) + emit_text(S, "\n") + } + emit_text(S, "</pre>\n") + close(f) + } + return +end +#line 1190 "l2h.nw" +procedure csarg(S) + return 2(="{", tab(bal('}', '{', '}')), ="}") | + (optwhite(), + if ="\\" then + "\\" || (tab(many(S.csletters)) | move(1)) + else + move(1)) +end +#line 1225 "l2h.nw" +procedure apply_args(S, p, as, args_wanted) + delay_newtext(S) + do_apply_args(S, closure(p, as), args_wanted, [], "", 0) + return +end + +procedure do_apply_args(S, cl, wanted_count, args_seen, current_arg, brace_depth) + local open_comment + # invariant : we have an open brace +# pushtrace("APPLY") + while *args_seen < wanted_count & not pos(0) do { + while *args_seen < wanted_count & brace_depth = 0 & not pos(0) do { + tab(many(' \t\n')) + case c := move(1) of { + "\\" : put(args_seen, "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1)) + "{" : { current_arg := "" ; brace_depth := 1 } + "}" : { error("Insufficient arguments to macro ", macro.name) } + "%" : if tab(upto('\n')) then ="\n" else open_comment := tab(0) + default : put(args_seen, c) + } + } + while brace_depth > 0 & not pos(0) do { + current_arg ||:= tab(upto('\\{}%') | 0) + case move(1) of { + "%" : if tab(upto('\n')) then ="\n" else open_comment := tab(0) + "\\" : current_arg ||:= "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) + "{" : { current_arg ||:= "{" ; brace_depth +:= 1 } + "}" : { brace_depth -:= 1 + if brace_depth > 0 then + current_arg ||:= "}" + else { + put(args_seen, current_arg) + current_arg := "" + } + } + } + } + } + if *args_seen = wanted_count then { + undelay_newtext(S) + cl.proc ! (cl.args ||| args_seen) + } else if \open_comment then { + delay_newtext(S) + before_next_newtext(S, skip_comment_and_continue, + [S, closure(do_apply_args, [S, cl, wanted_count, args_seen, + current_arg, brace_depth])]) + } else + before_next_newtext(S, do_apply_args, [S, cl, wanted_count, args_seen, + current_arg, brace_depth]) +# poptrace() + return +end +#line 1284 "l2h.nw" +procedure skip_comment_and_continue(S, cl) + tab(upto('\n') | 0) + if pos(0) then + before_next_newtext(S, skip_comment_and_continue, [S, cl]) + else { + ="\n" + undelay_newtext(S) +# <take actions appropriate to new text>> + } + return +end +#line 1316 "l2h.nw" +procedure Cnewline(S) + tab(many(' \t')) + if match("\n") then implicit_paragraph(S) + if pos(0) then before_next_newtext(S, Cnewline, [S]) +end +#line 1325 "l2h.nw" +procedure skipblanks(S) + tab(many(' \t')) + if ="\n" then Cnewline(S) + else if pos(0) then before_next_newtext(S, skipblanks, [S]) +end +#line 1334 "l2h.nw" +procedure implicit_paragraph(S, cs, cl) + if S.mode ~== "V" then { + S.mode := "V" + Cparagraph(S) + } + cs_ignore(S, cs, \cl) +end +#line 1347 "l2h.nw" +procedure stop_skipping(S) + while S.newtext[1].proc === (Cnewline|skipblanks) do pop(S.newtext) +end +#line 1357 "l2h.nw" +record item_cl(before, after, ifnone) + +procedure Citem(S, cs, cl) + if pos(0) then + after_next_newtext(S, Citem, [S, cs, cl]) + else if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", convert_bracketed, cl) + } else { + skipblanks(S) + emit_text(S, cl[1].ifnone) + \liststack[1] := \liststack[1] + 1 + } +end +#line 1372 "l2h.nw" +procedure convert_bracketed(S, contents, cl) + emit_text(S, cl[1].before || + convert(converter("H"), "{" || contents || "}") || + cl[1].after) + optwhite() +end +#line 1379 "l2h.nw" +global liststack, suspendstack +#line 1388 "l2h.nw" +procedure listenv(env, html) + begintab[env] := Clist + begincl[env] := html + endtab[env] := Clist_end + endcl[env] := html +end + +procedure push_item_closure(cs) + push(csclosure["item"], + if cs == "description" then item_cl("<dt>", "<dd>", "<dt><dd>") + else item_cl("<li>", "--", "<li>")) +end + +procedure Clist(S, cs, cl) + push(liststack, 1) + push_item_closure(cs) + emit_text(S, tag(cl)) +end + +procedure Cresume(S, cs, cl) + local n, env + (="{", env := tab(upto('}')), ="}") | error("botched \\resume{...}") + n := pop(suspendstack) | error("\\resume not balanced by corresponding \\suspend") + push(liststack, n) + push_item_closure(env) + if env == "enumerate" then + emit_text(S, tag(begincl[env] || " start=\"" || n || "\"")) + else + emit_text(S, tag(begincl[env])) +end + +procedure Clist_end(S, cs, cl) + emit_text(S, endtag(cl)) + pop(csclosure["item"]) + pop(liststack) +end + +procedure Csuspend(S, cs, cl) + local env + (="{", env := tab(upto('}')), ="}") | error("botched \\suspend{...}") + emit_text(S, endtag(endcl[env])) + pop(csclosure["item"]) + push(suspendstack, pop(liststack)) +end +#line 1437 "l2h.nw" +global labels_seen + +procedure Clabel(S, cs, cl) + initial /labels_seen := set() + insert(labels_seen, l := csarg(S)) | fail + emit_text(S, "<a name=\"" || l || "\"><b>[*]</b></a>") +end + +procedure Cref(S, cs, cl) + local prefix, prefix_tag + initial /labels_seen := set() + prefix_tag := (\cl)[1] | "" + prefix := (\cl)[2] | "" + l := prefix || csarg(S) | fail + emit_text(S, prefix_tag || "<a href=\"#" || l || "\">[" || + (if member(labels_seen, l) then "<-" else "->") || "]</a>") +end +#line 1463 "l2h.nw" +procedure Ccite(S, cs, cl, bracketed_text) + if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", do_cite, cl) + } else + do_cite(S, &null, cl) +end + +procedure do_cite(S, commentary, cl) + local key + if \commentary then + optwhite() + if pos(0) then before_next_newtext(S, do_cite, [S, commentary, cl]) + else { + key := csarg(S) + \commentary := convert(converter("H"), "{" || \commentary || "}") + emit_text(S, "<b>[cite ") + key ? { + while k := tab(upto(",")) & ="," do + emit_text(S, "<a href=\"#NWcite-" || k || "\">" || k || "</a>, ") + if k := tab(0) then + emit_text(S, "<a href=\"#NWcite-" || k || "\">" || k || "</a>") + } + emit_text(S, ", <i>" || \commentary || "</i>") + emit_text(S, "]</b>") + } +end +#line 1491 "l2h.nw" +procedure Cbibitem(S, cs, cl) + local label, key + static counter + initial counter := 0 + if ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", finish_bibitem, []) + } else { + label := "<b>[" || (counter +:= 1) || "]</b>" + apply_args(S, do_bibitem_key, [S, label], 1) + } +end + +procedure do_bibitem_key(S, label, key) + return emit_text(S, "<a name=\"NWcite-" || key || "\">" || label || "</a> ") +end + +procedure finish_bibitem(S, contents, args) + local key, label + optwhite() + label := convert(converter("H"), "{" || contents || "}") + key := apply_args(S, do_bibitem_key, [S, label], 1) +end +#line 1529 "l2h.nw" +record ifrec(on_else, on_fi) +#line 1538 "l2h.nw" +procedure Ciffalse(S, cs, cl) +#error("### \\", cs, " -> false (S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}, ")") + push(S.ifstack, ifrec(S.ignoring, S.ignoring)) + S.ignoring := 1 +end + +procedure Ciftrue(S, cs, cl) +#error("### \\", cs, " -> true (S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}, ")") + push(S.ifstack, ifrec(1, S.ignoring)) +end + +procedure Celse(S, cs, cl) + S.ignoring := S.ifstack[1].on_else +#error("### \\else -> S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}) +end + +procedure Cfi(S, cs, cl) + S.ignoring := S.ifstack[1].on_fi +#error("### \\fi -> S.ignoring === ", image(S.ignoring) ? {="procedure "; tab(0)}) + pop(S.ifstack) +end +#line 1563 "l2h.nw" +procedure Cnewif(S, cs, cl) + local newif, newcs + tab(many(' \t\n')) + if pos(0) then + after_next_newtext(S, Cnewif, [S, cs, cl]) + else { + newif := csarg(S) + newif ? + if ="\\if" & newcs := tab(many(S.csletters)) & pos(0) then { + +#line 1580 "l2h.nw" +cstab[newcs || "false"] := Csetif +cstab[newcs || "true"] := Csetif +cstab["if" || newcs] := Ciffalse +#line 1573 "l2h.nw" + } else + error("\\newif argument botch: " || newif) + } +end +#line 1584 "l2h.nw" +procedure Csetif(S, cs, cl) + local base, tag + if cs ? (base := tab(find("true"|"false")), tag := =("true"|"false"), pos(0)) then { + cstab["if" || base] := if tag == "true" then Ciftrue else Ciffalse + } else { + error("This can't happen --- setif botch (not urgent)") + } +end +#line 1598 "l2h.nw" +procedure Cuppercase(S, cs, cl) + l := map(csarg(S), &lcase, &ucase) | fail + emit_text(S, l) +end +#line 1613 "l2h.nw" +record table_info(index, # number of this cell in the row + alignment, # the alignment of this cell + width, # how many columns this cell will span + alignments, # default alignments for this table + brace_depth, # size of S.closes after start of cell + cell_text) # value of S.text to use to scan this cell +#line 1637 "l2h.nw" +procedure start_table_cell(S, text) + local this, attributes + + text ? { + tab(many(' \t\n')) + if pos(0) then return + } +# write(&errout, "starting cell with ", image(text)) + + this := S.tables[1] | fatal("starting cell with no current table") + S.text := this.cell_text + +if /(\this).brace_depth then write(&errout, "starting table cell, ", image(this), " has null brace depth") + # use this to start the current cell + if this.index = 1 then emit_text(S, "<tr>") + attributes := \this.alignment | aligneq("top") + if this.width > 1 then attributes ||:= " colspan=" || this.width + emit_text(S, "<td" || attributes || ">") + + # now update state for the next cell + this.index +:= this.width # advance to next cell + this.alignment := this.alignments[this.index] | &null + this.width := 1 + # can't set cell_text until we hit & + + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 1663 "l2h.nw" + emit_text(S, text) + return +end +#line 1670 "l2h.nw" +procedure ampersand(S) + local this + this := S.tables[1] + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1674 "l2h.nw" + if /this then + emit_text(S, " --- ") + else { + emit_text(S, "") # be sure cell gets started, even if empty + emit_text(S, "</td>") + if S.text ~=== start_table_cell then + this.cell_text := S.text + S.text := start_table_cell + this.brace_depth := *S.closes + 1 # will open at start of cell +#write(&errout, "set brace depth for ", image(this)) + } + tab(many(' \t\n')) +## write(&errout, " past &, text = ", image(S.text), ", next = ", +## image(&subject[&pos:0])) + return +end +#line 1699 "l2h.nw" +procedure Cbackback(S, cs, cl) + local this + this := S.tables[1] + cs_ignore(S, cs, "[", Cbackback_continue, [S, this]) +end + +procedure Cbackback_continue(S, this) +#if /(\this).brace_depth then write(&errout, image(this), " has null brace depth") + if /this | *S.closes > this.brace_depth then { # ordinary \\ + S.text(S, "<br>") + } else { # row terminator +## write(&errout, "ending row with ", image(&subject[&pos:0])) + emit_text(S, "") # be sure cell gets started, even if empty + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1713 "l2h.nw" + emit_text(S, "</td></tr>\n") + tab(many(' \t\n')) + if S.text ~=== start_table_cell then + this.cell_text := S.text + this.index := 1 + this.alignment := this.alignments[this.index] | &null + this.width := 1 + S.text := start_table_cell + this.brace_depth := *S.closes + 1 # about to open + } +end +#line 1727 "l2h.nw" +procedure Chline(S, cs, cl) + if \S.tables[1] then return + else emit_text(S, "<hr>") + return +end +#line 1737 "l2h.nw" +procedure Ctabular_end(S, cs, cl) + local this + if S.text ~=== start_table_cell | S.tables[1].index > 1 then {# row in progress + emit_text(S, "") # be sure cell gets started, even if empty + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 1742 "l2h.nw" + emit_text(S, "</td></tr>") + } + if S.text === start_table_cell then # abort it + S.text := S.tables[1].cell_text + emit_text(S, "</table>") + xxx := pop(S.tables) +#write(&errout, "popped ", image(xxx)) + return +end +#line 1754 "l2h.nw" +procedure Ctabular(S, cs, cl) + cs_ignore(S, cs, cl, Ctabular_continue, [S]) + return +end + +procedure Ctabular_continue(S) + a := csarg() # alignment +#write(&errout, "Alignment ", a) + emit_text(S, if upto('|', a) then "<table border>" else "<table>") + emit_text(S, "<!-- alignment is " || a || "-->") + a := alignments(a) + emit_text(S, "<!-- " || *a || " columns-->") + push(S.tables, table_info(1, a[1] | "l", 1, a, *S.closes+1, S.text)) +#write(&errout, "pushed ", image(S.tables[1])) + S.text := start_table_cell + optwhite() + return +end +#line 1777 "l2h.nw" +procedure tabular(env, ignore) + begintab[env] := Ctabular + begincl[env] := ignore + endtab[env] := Ctabular_end + endcl[env] := ignore +end +#line 1786 "l2h.nw" +procedure aligneq(a) + return " align=\"" || a || "\"" +end + +procedure valigneq(a) + return " valign=\"" || a || "\"" +end + +procedure alignments(s) + a := [] + s ? { + while not pos(0) do + case move(1) of { + "l" | "X" | "Y" | "p" : + { put(a, aligneq("left") || valigneq("top")); skip_bracket() } + "c" : put(a, aligneq("center")) + "r" : put(a, aligneq("right")) + "m" : { put(a, aligneq("left") || valigneq("center")); skip_bracket() } + "b" : { put(a, aligneq("left") || valigneq("bottom")); skip_bracket() } + "@" | "<" | ">" | "!" : skip_bracket() + "|" : &null + default : &null # unrecognized... + } + } + return a +end +#line 1813 "l2h.nw" +procedure skip_bracket() + if ="{" then { + n := 1 + while n > 0 & not pos(0) do { + tab(upto('{}\\') | 0) + case move(1) of { + "{" : n +:= 1 + "}" : n -:= 1 + "\\" : move(1) + } + } + } + return +end +#line 1831 "l2h.nw" +procedure Cmulticolumn(S, cs, cl) + local this + this := S.tables[1] + n := integer(csarg()) | error("\\multicolumn or \\multispan not followed by integer") + if cs == "multicolumn" then a := alignments(csarg()) +# write(&errout, "\\", cs, " n = ", n, ", a = ", (\a)[1] | "???", +# ", text = ", image(S.text)) + if /this then return # \multicolumn without table? + this.width := n + this.alignment := (\a)[1] + return +end + +procedure Cspan(S, cs, cl) + (\S.tables[1]).width +:= 1 + return +end +#line 1850 "l2h.nw" +procedure Cnoalign(S, cs, cl) + apply_args(S, finish_noalign, [S], 1) + return +end + +procedure finish_noalign(S, arg) + return if \S.ignoring then "" else + accumulate_text(S, "<br>" || convert(converter("V"), "{" || arg || "}") || "<br>") +end +#line 1862 "l2h.nw" +procedure auxfile(cs, ext, placeholder, header, trailer, ignore) + cstab[cs] := Cauxfile + csclosure[cs] := aux_cl(ext, placeholder, header, trailer, \ignore | "") +end +#line 1869 "l2h.nw" +record aux_cl(ext, placeholder, header, trailer, ignore) + +procedure Cauxfile(S, cs, cl) + local auxfile, T + if auxfile := open(basename(\curfile) || "." || cl.ext) then { + T := converter("V") + Cmakeatletter(T) + S.text(S, \cl.header) + while line := read(auxfile) do + S.text(S, convert(T, line || "\n")) + close(auxfile) + S.text(S, \cl.trailer) + } else { + S.text(S, \cl.placeholder) + } + cs_ignore(S, cs, cl.ignore) + if \auxfile then return +end +#line 1888 "l2h.nw" +procedure basename(name) + reverse(name) ? { + tab(upto('.')) & ="." + return reverse(tab(0)) + } +end +#line 1901 "l2h.nw" +procedure Ctableofcontents(S, cs, cl) + S.mode := "V" + Cauxfile(S, cs, cl) + set_toclevel(S) +end +#line 1910 "l2h.nw" +procedure set_toclevel(S, l) + static toclevel, initiallevel + if /initiallevel := \l then + S.text(S, "<ul compact>") + if /l := \initiallevel then + S.text(S, "</ul>") + if /l then return # never set a level + /toclevel := l + while toclevel < l do { + S.text(S, "<ul compact>") + toclevel +:= 1 + } + while toclevel > l do { + S.text(S, "</ul>") + toclevel -:= 1 + } + return +end +#line 1931 "l2h.nw" +procedure Ccontentsline(S, cs, cl) + local type, level + static leveltab + initial { +#line 1945 "l2h.nw" +l := ["part", "chapter", "section", "subsection", "subsubsection", + "paragraph", "subparagraph"] +leveltab := table() +every i := 1 to *l do + leveltab[l[i]] := i - 2 # making section level 1 +#line 1934 "l2h.nw" + } + l := \leveltab[csarg()] | fail + if l > \countertab["tocdepth"] then + cs_ignore(S, cs, "{{") # skip this one + else { + set_toclevel(S, l) + S.text(S, "<li>") + after_next_open(S, after_next_close, [S, cs_ignore, [S, cs, "{"]]) + } +end +#line 1953 "l2h.nw" +global countertab + +procedure Csetcounter(S, cs, cl) + local counter + (counter := csarg(), countertab[counter] := integer(csarg())) | + cs_ignore(S, cs, "{{") +end +#line 1967 "l2h.nw" +global accent_name, accent_valid +#line 1998 "l2h.nw" +procedure accent(cs) + cstab[cs] := Caccent +end + +procedure Caccent(S, cs, cl) + static warned + initial warned := table() + arg := csarg(S) | return + if arg == "\\i" then arg := "i" + if arg == "\\j" then arg := "j" + if *arg = 1 & any(accent_valid[cs], arg) then + S.text(S, "&" || arg || accent_name[cs] || ";") + else { + +#line 2016 "l2h.nw" +/warned[cs] := set() +if not member(warned[cs], arg) then { + write(&errout, "Warning: Can't handle \\", cs, " with arg `", arg, "'") + insert(warned[cs], arg) +} +#line 2012 "l2h.nw" + S.text(S, arg) + } +end +#line 2026 "l2h.nw" +procedure fontchange(tex, html) + cstab[tex] := Cfontchange + csclosure[tex] := html +end +#line 2031 "l2h.nw" +procedure Cfontchange(S, tex, html) + S.text(S, tag(html)) + before_next_close(S, emit_text, [S, endtag(html)]) +end +#line 2051 "l2h.nw" +procedure ignore(cs, template) + /template := "" + cstab[cs] := cs_ignore + csclosure[cs] := template +end + +procedure ignoreenv(env, template) + /template := "" + begintab[env] := cs_ignore + begincl[env] := template + endtab[env] := do_nothing +end +#line 2070 "l2h.nw" +procedure cs_ignore(S, cs, template, proc, args) + local saved_ignore + saved_ignore := S.ignoring + S.ignoring := 1 + if type(template) == "integer" then template := repl("{", template) + return do_ignore(S, template, saved_ignore, proc, args) +end +#line 2084 "l2h.nw" +procedure do_ignore(S, template, saved_ignore, proc, args) + if *template > 0 then + if optwhite() & pos(0) then + after_next_newtext(S, do_ignore, [S, template, saved_ignore, proc, args]) + else + case template[1] of { + "{" : { S.ignoring := 1 + after_next_open(S, ignore_til_close, + [S, template[2:0], saved_ignore, proc, args]) + } + "A" : { csarg(S) # had better be in one input + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + "[" : if optwhite() & ="[" then { + delay_newtext(S) + with_upto_bracket(S, "", ignore_bracket_plus, + [S, template[2:0], saved_ignore, proc, args]) + } else + do_ignore(S, template[2:0], saved_ignore, proc, args) + "C" : # a total cheat, means ``copy optional arg'' + if optwhite() & ="[" then { + S.ignoring := &null + delay_newtext(S) + with_upto_bracket(S, "", copy_bracket_plus, + [S, template[2:0], saved_ignore, proc, args]) + } else + do_ignore(S, template[2:0], saved_ignore, proc, args) + "=" : { delay_newtext(S) + eat_assignment(S, do_ignore, [S, template[2:0], saved_ignore, proc,args]) + } + "*" : { (="*", optwhite()) + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + "(" : { (="(", tab(upto(')')), =")", optwhite()) + do_ignore(S, template[2:0], saved_ignore, proc, args) + } + } + else { + S.ignoring := saved_ignore + (\proc)!(\args) + } +end + +procedure ignore_til_close(S, template, saved_ignore, proc, args) + before_next_close(S, do_ignore, [S, template, saved_ignore, proc, args]) +end +#line 2133 "l2h.nw" +procedure do_nothing(S, cs, cl) + return +end +#line 2144 "l2h.nw" +procedure with_upto_bracket(S, bracketed_text, proc, args) + bracketed_text ||:= tab(upto(']') | 0) + if pos(0) then + before_next_newtext(S, with_upto_bracket, [S, bracketed_text, proc, args]) + else { + ="]" + undelay_newtext(S) + (\proc)(S, bracketed_text, args) + } + return +end +#line 2158 "l2h.nw" +procedure ignore_bracket_plus(S, contents, args) + # contents are ignored + do_ignore!args +end +#line 2164 "l2h.nw" +procedure copy_bracket_plus(S, contents, args) + local should_ignore + should_ignore := args[3] | fail # saved_ignore arg to do_ignore + if /should_ignore then + S.text(S, convert(converter("H"), "{" || contents || "}")) + do_ignore!args +end +#line 2177 "l2h.nw" +procedure eat_assignment(S, proc, args) + static decimal_chars, hex_chars, oct_chars + initial { + decimal_chars := &digits ++ '.,+-' + hex_chars := &digits ++ 'abcdefABCDEF' + oct_chars := '0124567' + } + optwhite() + ="=" # so what if we swallow multiple = signs + optwhite() + if pos(0) then { + before_next_newtext(S, eat_assignment, [S, proc, args]) + return + } else if glue() then { # finished + } else if any(decimal_chars) then { + tab(many(decimal_chars)) + optwhite() + if ="\\" then + tab(many(S.csletters)) | move(1) + # else assume assignment of the form \hangafter=2 + } else if ="\"" then { + tab(many(hex_chars)) & optwhite() + } else if ="\'" then { + tab(many(oct_chars)) & optwhite() + } else if =("\\"|"`\\") then + tab(many(S.csletters)) | move(1) + undelay_newtext(S) + (\proc)!args +end +#line 2207 "l2h.nw" +procedure dimen() + static decimal_chars + initial decimal_chars := &digits ++ '.,' + suspend (optwhite(), + if any('+-') then (move(1), optwhite()) else "", + tab(many(decimal_chars)), optwhite(), + (="true", optwhite()) | &null, + =("em"|"ex"|"pt"|"pc"|"in"|"bp"|"cm"|"mm"|"dd"|"cc"|"sp"|"mu")) +end +#line 2217 "l2h.nw" +procedure glue() + suspend (dimen(), + (optwhite(), ="plus", dimen()) | "", + (optwhite(), ="minus", dimen()) | "") +end +#line 2231 "l2h.nw" +procedure substitution(tex, html, ignore_template) + # ignore mode for now + cstab[tex] := Cemit_ig + csclosure[tex] := emit_ig_cl(html, \ignore_template | "") +end +#line 2239 "l2h.nw" +record emit_ig_cl(html, template) + +procedure Cemit_ig(S, cs, cl) + emit_text(S, cl.html) + if *cl.template > 0 then + cs_ignore(S, cs, cl.template) +end +#line 2249 "l2h.nw" +procedure activesubst(char, html, ignore_template) + local S + # ignore mode for now + activetab[char] := Cemit_ig + activeclosure[char] := emit_ig_cl(html, \ignore_template | "") + S := \dynamic_add_hack | return + if upto(S.activechars, char) then return # already active + if S.activechars ++:= cset(char) then { + before_next_close(S, delete_active_char, [S, char]) + } else impossible("ugh") + return +end + +procedure delete_active_char(S, char) + S.activechars --:= char + return +end +#line 2282 "l2h.nw" +procedure envblock(env, left, right, ignore_template) + /ignore_template := "" + begintab[env] := Cemit_ig + begincl[env] := emit_ig_cl(if /right then tag(left) else left, ignore_template) + endtab[env] := Cemit + endcl[env] := if /right then endtag(left) else right +end +#line 2291 "l2h.nw" +procedure Cemit(S, cs, cl) + S.text(S, cl) +end +#line 2303 "l2h.nw" +record blockpair(left, right, ignore) + +procedure argblock(tex, html, right, ignore) + # called as is envblock + /ignore := "" + cstab[tex] := Cblock + csclosure[tex] := + if /right then blockpair (tag(html), endtag(html), ignore) + else blockpair (html, right, ignore) +end +#line 2316 "l2h.nw" +procedure argblockv(tex, html, right, ignore) + argblock(tex, html, right, ignore) + cstab[tex] := CblockV +end +#line 2321 "l2h.nw" +procedure Cblock(S, cs, cl, done_ignoring) + if /done_ignoring & *cl.ignore > 0 then { + cs_ignore(S, cs, cl.ignore, Cblock, [S, cs, cl, 1]) + } else if pos(0) then { + after_next_newtext(S, do_cs, [S, cs, cl]) + } else if match("{") then { + S.text(S, cl.left) + after_next_open(S, before_next_close, [S, emit_text, [S, cl.right]]) + } else { +# S.text(S, cl.left || csarg(S) || cl.right) + apply_args(S, Cblock_continue, [S, cl], 1) + } + return +end + +procedure Cblock_continue(S, cl, title) + S.text (S, cl.left || title || cl.right) + return +end +#line 2341 "l2h.nw" +procedure CblockV(S, cs, cl) + S.mode := "V" + Cblock(S, cs, cl) + return +end +#line 2353 "l2h.nw" +record macro_defn(name, arg_count, body, mode) +#line 2357 "l2h.nw" +record raw_arg(number) +record converted_arg(number, mode) +#line 2361 "l2h.nw" +procedure expand_macro(S, macro, args) + every a := !macro.body do + case type(a) of { + "string" : emit_text(S, a) + "raw_arg" : emit_text(S, args[a.number]) | impossible("missing arg") + "converted_arg" : S.text(S, convert(S, "{" || args[a.number] || "}")) + } +# poptrace() + return +end +#link pushtrace +#line 2375 "l2h.nw" +procedure do_macro(S, macro, args_seen, current_arg, brace_depth) + # invariant : we have an open brace +# write(&errout, "scanning args for macro ", macro.name) +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count) + while *args_seen < macro.arg_count & not pos(0) do { + while *args_seen < macro.arg_count & brace_depth = 0 & not pos(0) do { +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + tab(many(' \t\n')) + case c := move(1) of { + "\\" : put(args_seen, "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1)) + "{" : { current_arg := "" ; brace_depth := 1 } + "}" : { error("Insufficient arguments to macro ", macro.name) } + default : put(args_seen, c) + } + } + while brace_depth > 0 & not pos(0) do { +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + current_arg ||:= tab(upto('\\{}') | 0) + case move(1) of { + "\\" : current_arg ||:= "\\" || + if pos(0) then "" + else if any(S.csletters) then tab(many(S.csletters)) + else move(1) + "{" : { current_arg ||:= "{" ; brace_depth +:= 1 } + "}" : { brace_depth -:= 1 + if brace_depth > 0 then + current_arg ||:= "}" + else { + put(args_seen, current_arg) + current_arg := "" + } + } + } + } + } +# write(&errout, "seen ", *args_seen, " want ", macro.arg_count, +# " current ", image(current_arg), " braces ", brace_depth) + if *args_seen = macro.arg_count then { +# write(&errout, "Arguments for macro ", macro.name, ":") +# every write(&errout, "\t", image(!args_seen)) + expand_macro(S, macro, args_seen) + undelay_newtext(S) + } else + before_next_newtext(S, do_macro, [S, macro, args_seen, current_arg, brace_depth]) + return +end +#line 2428 "l2h.nw" +procedure Cmacro(S, cs, cl) +# pushtrace("MACRO") + delay_newtext(S) +# apply_args(S, closure(expand_macro, [cl]), cl.arg_count) + do_macro(S, cl, [], "", 0) + return +end +#line 2439 "l2h.nw" +procedure macro(name, arg_count, body, mode) + m := macro_defn(name, arg_count, parse_body(body), mode) + cstab[name] := Cmacro + csclosure[name] := m + return +end + +procedure begin_macro(env, arg_count, body, mode) + m := macro_defn(env, arg_count, parse_body(body), mode) + begintab[env] := Cmacro + begincl[env] := m + return +end + +procedure parse_body(body) + b := [] + body ? { + put(b, tab(upto('#') | 0)) + while ="#" do { + put(b, ="#" | (="$", raw_arg(argnum())) | converted_arg(argnum())) | + error("malformed macro arg #", tab(0)) + put(b, tab(upto('#') | 0)) + } + } + return b +end + +procedure argnum() + if any(&digits) then return integer(move(1)) else fail +end +#line 2472 "l2h.nw" +procedure l2h_macro(name, count, body[]) + count := integer(count) | + return error("must give # of arguments to l2h macro ", name) + s := "" + every s ||:= " " || (1(b := !body, type(b) == "string")) + s := s[2:0] # strip leading space if any + return macro(name, count, s) +end +#line 2482 "l2h.nw" +procedure l2h_environment(env, count, body[]) + count := integer(count) | + return error("must give # of arguments to l2h environment ", env) + s := "" + every s ||:= " " || (1(b := !body, type(b) == "string")) + s := s[2:0] # strip leading space if any + return begin_macro(env, count, s) +end +#line 2497 "l2h.nw" +record table_closure(args, amp, open, close) + +procedure table_env(env, args, amp, open, close) + begintab[env] := Ctable + begincl[env] := table_closure(args, amp, + if /close then tag(\open) | &null else open, + if /close then endtag(\open) | &null else close) + endtab[env] := Ctable_end + endcl[env] := [] +end +#line 2508 "l2h.nw" +procedure Ctable(S, env, cl) + local amp +## amp := S.ampersand +## S.ampersand := cl.amp + S.text(S, \cl.open) + push(endcl[env], amp) + cs_ignore(S, env, cl.args) +end + +procedure Ctable_end(S, env, cl) +# S.ampersand := pop(cl) + S.text(S, \begincl[env].close) +end +#line 2524 "l2h.nw" +procedure Cepsfig(S, cs, cl) + apply_args(S, do_epsfig, [S], 1) +end + +procedure do_epsfig(S, arg) + local args + args := [] + arg ? + while not pos(0) do { + tab(many(' \t\n')) + put(args, eqsplit(tab(upto(',') | 0))) + } + if a := !args & a.name == ("file"|"figure") then + emit_text(S, "<a href=" || image(a.value) || "><b>[</b>PostScript figure " || + a.value || "<b>]</b></a>") + else + emit_text(S, "<b>[</b>Ill-understood PostScript figure<b>]</b>") +end + +record apair(name, value) + +procedure eqsplit(s) + p := apair() + s ? (p.name := tab(upto('=')), ="=", p.value := tab(0)) + return p +end +#line 2552 "l2h.nw" +procedure Cincludegraphics(S, cs, cl) + local saved_ignore + saved_ignore := S.ignoring + S.ignoring := 1 + do_ignore(S, "[", saved_ignore, apply_args, [S, do_includegraphics, [S], 1]) +end + +procedure do_includegraphics(S, arg) + local base, ext + if arg ? (base := tab(find(ext := ".ps" | ".eps" | ".epsi")), =ext, pos(0)) then + emit_text(S, "<a href=" || image(arg) || "><b>[</b>PostScript figure " || + arg || "<b>]</b></a>") + else if arg ? (base := tab(find(ext := ".png" | ".gif" | ".jpg")), =ext, pos(0)) then + emit_text(S, "<img src=" || image(arg) || ">") + else + emit_text(S, "<b>[</b>Ill-understood graphics<b>]</b>") +end +#line 2574 "l2h.nw" +procedure let(lhs, rhs) + cstab[lhs] := cstab[rhs] + csclosure[lhs] := csclosure[rhs] +end + +procedure let_closure(lhs, cl[]) + csclosure[lhs] := if *cl = 1 then cl[1] else cl +end + +procedure letenv(lhs, rhs) + begintab[lhs] := begintab[rhs] + endtab[lhs] := endtab[rhs] + begincl[lhs] := begincl[rhs] + endcl[lhs] := endcl[rhs] +end +#line 2595 "l2h.nw" +procedure tag(html) + return "<" || html || ">" +end + +procedure endtag(html) + return "</" || html || ">" +end +#line 2605 "l2h.nw" +procedure Ccomment(S) + if *S.comment > 0 then { + S.text(S, "<!--") + S.comment ? { + while S.text(S, tab(find("--"))) do { + move(2) + S.text(S, "- - ") + } + S.text(S, tab(0)) + } + S.text(S, "-->") + } + S.comment := "" + return +end +#line 2621 "l2h.nw" +procedure Cparagraph(S) + S.text(S, "<p>") +end +#line 2625 "l2h.nw" +procedure Cmath(S) + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 2627 "l2h.nw" + S.text(S, "<i>") +end + +procedure Cmath_end(S) + S.text(S, "</i>") + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 2633 "l2h.nw" +end +#line 2635 "l2h.nw" +procedure Cdisplaymath(S) + +#line 915 "l2h.nw" +push(S.closes, []) # fresh set of closing tasks +while c := get(S.open) do + c.proc!c.args +#line 2637 "l2h.nw" + S.text(S, "<blockquote><i>") +end + +procedure Cdisplaymath_end(S) + S.text(S, "</i></blockquote>") + +#line 919 "l2h.nw" +while c := get(S.closes[1]) do + c.proc!c.args +pop(S.closes) +#line 2643 "l2h.nw" +end +#line 2645 "l2h.nw" +procedure Cmakeatletter(S) + S.csletters ++:= '@' +end + +procedure Cmakeatother(S) + S.csletters --:= '@' +end +#line 2655 "l2h.nw" +procedure Ckill(S, cs, cl) + S.the_text := "" +end +#line 2673 "l2h.nw" +global csfunctions +#line 2700 "l2h.nw" +global dynamic_add_hack +procedure parse_dynamic_add(S) + if (optwhite(), =("l2h"|"sl2h"), skipwhite(), + p := tab(upto(' \t')), +#line 2717 "l2h.nw" +((p := \csfunctions[p]) | +{ dynamic_warn(p); fail }) +#line 2703 "l2h.nw" + , + skipwhite(), any(~'\n')) then { + a := [] + while (any(~'\n'), l := tab(upto(' \t\n') | 0)) do { + put(a, if p === (l2h_macro|l2h_environment) then l else map(l, "#", " ")) + skipwhite() + } + dynamic_add_hack := S + p!a + dynamic_add_hack := &null + return + } +end +#line 2720 "l2h.nw" +procedure dynamic_warn(p) + static badprocs + initial badprocs := set() + if not member(badprocs, p) then { + write(&errout, "Warning: % l2h ", p, " not recognized -- ignored") + insert(badprocs, p) + } +end +#line 2732 "l2h.nw" +procedure optwhite() + suspend tab(many(' \t')) | "" +end +#line 2737 "l2h.nw" +procedure skipwhite() + suspend tab(many(' \t')) +end +#line 2829 "l2h.nw" +global errstatus +procedure error(args[]) + errstatus := 1 + return write!([&errout, (\curfile || ":") | "line ", curline, ": "] ||| args) +end +#line 2878 "l2h.nw" +procedure fatal(L[]) + write!(["@fatal l2h "] ||| L) + write!([&errout, "noweb error in l2h: "] ||| L) + exit(1) +end +#line 2885 "l2h.nw" +procedure rcsinfo () + return "$Id: l2h.nw,v 1.24 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +#line 2838 "l2h.nw" +global curfile, curline + +procedure convert_file(f) + static S + initial S := converter("V") + curline := 0 + while line := read(f) do { + curline +:= 1 + writes(convert(S, line || "\n")) + } + return +end + +procedure main(args) + local warn + warn := 1 + errstatus := 0 + every arg := !args do + if arg[1] == "-" then + case arg of { + "-show-unknowns" : show_unknowns := 1 + "-nowarn" : warn := &null + "-html-quotes" : html_quotes := 1 + "-" : { curfile := arg; convert_file(&input) } + default : write(&errout, "Warning: unrecognized option ", arg) + } + else if f := open(curfile <- arg) then + convert_file(f) + else + write(&errout, "Error: Can't open file ", arg) + if /curfile then + convert_file(&input) + if \warn then { + warn_unknown(\unknown_set, "control sequences", "\\") + warn_unknown(\unknown_envs, "environments", "{", "}") + } + exit(errstatus) +end diff --git a/web/noweb/src/icon/smldefs.icn b/web/noweb/src/icon/smldefs.icn new file mode 100644 index 0000000000..43f29ab1b1 --- /dev/null +++ b/web/noweb/src/icon/smldefs.icn @@ -0,0 +1,84 @@ +#line 3 "smldefs.nw" +procedure rcsinfo () + return "$Id: smldefs.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + go() +end +#line 11 "smldefs.nw" +procedure postpass(name, arg) + static kind, definers, id1, id2, reserved + initial { kind := "bogus" + definers := ["fun", "val", "structure", "signature", "and", "functor", + "type", "eqtype", "datatype", "withtype", "exception"] + id1 := &letters ++ &digits ++ '\'_' + id2 := '!%&$+-/:<=>?@\\~^|#*`' + reserved := set() + every insert(reserved, !"*|:=#_" | "->" | "=>") + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if =(""|" "|" ") & =!definers & tab(many(' ')) then { + skiptyvars(); optwhite() + writedefn(1(id := tab(many(id1 | id2)), not member(reserved, id))) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure skiptyvars() + suspend ="'" || tab(many(&letters ++ &digits ++ '\'_')) | + ="(" || tab(bal(')', '(', ')')) || =")" +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/smldefs.nw b/web/noweb/src/icon/smldefs.nw new file mode 100644 index 0000000000..1a715ced4a --- /dev/null +++ b/web/noweb/src/icon/smldefs.nw @@ -0,0 +1,44 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\section{Finding definitions in Standard ML} +<<*>>= +procedure rcsinfo () + return "$Id: smldefs.nw,v 1.19 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + go() +end +<<*>>= +procedure postpass(name, arg) + static kind, definers, id1, id2, reserved + initial { kind := "bogus" + definers := ["fun", "val", "structure", "signature", "and", "functor", + "type", "eqtype", "datatype", "withtype", "exception"] + id1 := &letters ++ &digits ++ '\'_' + id2 := '!%&$+-/:<=>?@\\~^|#*`' + reserved := set() + every insert(reserved, !"*|:=#_" | "->" | "=>") + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if =(""|" "|" ") & =!definers & tab(many(' ')) then { + skiptyvars(); optwhite() + writedefn(1(id := tab(many(id1 | id2)), not member(reserved, id))) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end + +procedure skiptyvars() + suspend ="'" || tab(many(&letters ++ &digits ++ '\'_')) | + ="(" || tab(bal(')', '(', ')')) || =")" +end + +procedure optwhite() + suspend tab(many(' \t')) | "" +end diff --git a/web/noweb/src/icon/texdefs.icn b/web/noweb/src/icon/texdefs.icn new file mode 100644 index 0000000000..13ddd4b12a --- /dev/null +++ b/web/noweb/src/icon/texdefs.icn @@ -0,0 +1,73 @@ +#line 2 "texdefs.nw" +procedure rcsinfo () + return "$Id: texdefs.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + go() +end +#line 10 "texdefs.nw" +procedure postpass(name, arg) + static kind, idchars, definers, newcommand + initial { kind := "bogus"; idchars := &letters ++ '@' + definers := ["def", "newdimen", "newif", "newcount", "newwrite", + "newbox", "global\\def", "long\\def", "gdef" ] + newcommand := ["newcommand", "renewcommand"] + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? { + if =("\\" || !definers || "\\") then + writedefn("\\" || (tab(many(idchars)) | move(1))) + else if =("\\" || !newcommand || "{") then + writedefn(tab(upto('}'))) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/texdefs.nw b/web/noweb/src/icon/texdefs.nw new file mode 100644 index 0000000000..c530120662 --- /dev/null +++ b/web/noweb/src/icon/texdefs.nw @@ -0,0 +1,32 @@ +\section{Finding {\TeX} definitions} +<<*>>= +procedure rcsinfo () + return "$Id: texdefs.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + go() +end +<<*>>= +procedure postpass(name, arg) + static kind, idchars, definers, newcommand + initial { kind := "bogus"; idchars := &letters ++ '@' + definers := ["def", "newdimen", "newif", "newcount", "newwrite", + "newbox", "global\\def", "long\\def", "gdef" ] + newcommand := ["newcommand", "renewcommand"] + } + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? { + if =("\\" || !definers || "\\") then + writedefn("\\" || (tab(many(idchars)) | move(1))) + else if =("\\" || !newcommand || "{") then + writedefn(tab(upto('}'))) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end diff --git a/web/noweb/src/icon/tohtml.icn b/web/noweb/src/icon/tohtml.icn new file mode 100644 index 0000000000..dfea222088 --- /dev/null +++ b/web/noweb/src/icon/tohtml.icn @@ -0,0 +1,489 @@ +#line 10 "tohtml.nw" +# Copyright 1994 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +#line 19 "tohtml.nw" +# Don't try to understand this file! Look at icon/tohtml.nw in the noweb source! + +global braw, eraw +procedure main(args) + local delay, raw, where, localindex, noindex, shortxref + +#line 218 "tohtml.nw" +useitemstab := table() +defns := table("") +defns_above := set() # keep track of defining chunks we've seen +#line 25 "tohtml.nw" + every braw | eraw := "" + delay := !args == "-delay" + noindex := !args == "-noindex" + shortxref := if !args == "-longxref" then &null else 1 + localindex := (/noindex, !args == "-localindex") # produce local identifier xref? + raw := !args == "-raw" + if \raw then {braw := "\\begin{rawhtml}"; eraw := "\\end{rawhtml}"} + if !args == "-no-gen-comment" then &null + else + write(braw, "<!-- this file was generated automatically by noweave;", + " better not edit it-->", eraw) + while inputline := read() do inputline ? { + +#line 90 "tohtml.nw" +if ="@text " then { text +:= *(line := tab(0)) + if \code then + writes(( +#line 98 "tohtml.nw" +2(line ? {tab(many(' \t')); not pos(0)}, + linkto(\lastindexref, escapeSpecials(line)), + lastindexref := &null) +#line 92 "tohtml.nw" + ) | escapeSpecials(line)) + else if \quoting then + writes(( +#line 123 "tohtml.nw" +2(line ? {tab(many(' \t')); not pos(0)}, + linklabelto(lastxreflabel, lastindexref, escapeSpecials(line), "bare"), + lastxreflabel := lastindexref := &null) +#line 94 "tohtml.nw" + ) | escapeSpecials(line)) + else + writes(( +#line 107 "tohtml.nw" +1(line ? skip_white_tags() || + linklabel(\lastxreflabel, skip_chars()) || tab(0), + insert(defns_above, \lastxreflabel), + lastxreflabel := &null) +#line 96 "tohtml.nw" + ) | line) } else +#line 207 "tohtml.nw" +if ="@nl" & pos(0) then { +#line 292 "tohtml.nw" +if \shortxref & (\pendinguses | \pendingprev | \pendingnext | \notused) then { + +#line 297 "tohtml.nw" +if \pendinguses & *\useitems > 0 then { + writes(" <b>(") + every i := 1 to *\useitems do { + usedir := if member(defns_above, useitems[i]) then "<-U" else "U->" + writes(if i > 1 then " " else "", linkto(useitems[i], usedir)) + } + writes(")</b>") +} +if \pendingprev | \pendingnext then { + writes(" <b>[") + writes(linkto(\pendingprev, "<-")) + writes("D") + writes(linkto(\pendingnext, "->")) + writes("]</b>") +} +#line 294 "tohtml.nw" + pendinguses := pendingprev := pendingnext := notused := &null +} +#line 208 "tohtml.nw" + write() } else +#line 62 "tohtml.nw" +if ="@begin code " then { code := 1 ; thischunk := &null + +#line 350 "tohtml.nw" +useitems := [] +notused := &null +#line 64 "tohtml.nw" + writes(braw, "<pre>"); ecode := "</pre>" } else +if ="@end code " then { +#line 313 "tohtml.nw" +if /shortxref then { + useitems := useitemstab[\thischunk] + if \pendingprev | \pendingnext | (\pendinguses, (*\useitems > 0 | \notused)) then { + +#line 356 "tohtml.nw" +if ecode == "</pre>" then { + writes("</pre><blockquote>") + ecode := "</blockquote>" +} +#line 317 "tohtml.nw" + +#line 342 "tohtml.nw" +useprefix := "Used " +every i := 1 to *\useitems do { + usedir := if member(defns_above, useitems[i]) then "above" else "below" + usesuffix := if *\useitems > 1 then " (" || i || ")" else "" + writes(useprefix, linkto(useitems[i], usedir || usesuffix)) + useprefix := ", " +} +#line 318 "tohtml.nw" + useitemstab[\thischunk] + if *\useitems > 0 & (\pendingprev | \pendingnext) then + writes("; ") + p := if *\useitems > 0 then "previous" else "Previous" + n := if *\useitems > 0 then "next" else "Next" + if \pendingprev then + if \pendingnext then + writes(linkto(pendingprev, p), " and ", + linkto(pendingnext, "next"), " definitions") + else + writes(linkto(pendingprev, p), " definition") + else + if \pendingnext then + writes(linkto(pendingnext, n), " definition") + if \pendingprev | \pendingnext | *\useitems > 0 then + writes(".") + if \notused then + writes("<br>\nThis code is written to a file (or else not used).") + write("<p>") + pendinguses := pendingprev := pendingnext := useitems := notused := &null + } +} else + &null +#line 66 "tohtml.nw" + code := nil ; previscode := 1 + writes(ecode, eraw) } else +#line 210 "tohtml.nw" +if ="@defn " then { writechunk(lastxreflabel, lastxrefref, "dfn", + thischunk := tab(0), defns[thischunk] || "=") + insert(defns_above, \lastxreflabel) + useitems := \useitemstab[thischunk] + pendinguses := 1 + +#line 274 "tohtml.nw" +every lastxreflabel | lastxrefref := &null +#line 216 "tohtml.nw" + defns[thischunk] := "+" } else +#line 74 "tohtml.nw" +if ="@begin docs " then { if \previscode then writes(if /raw then "<p>" else "\\par") + previscode := &null + text := 0 } else +#line 82 "tohtml.nw" +if ="@end docs " then { write(linklabel(\lastxreflabel, "*")) + lastxreflabel := &null } else +#line 222 "tohtml.nw" +if ="@use " then { writechunk(lastxreflabel, lastxrefref, "i", tab(0)) + +#line 274 "tohtml.nw" +every lastxreflabel | lastxrefref := &null +#line 223 "tohtml.nw" + } else +#line 251 "tohtml.nw" +if ="@xref " then { + if fun := tab(upto(' ')) then {move(1); arg := tab(0)} + else { fun := tab(0); arg := &null } + case fun of { + +#line 265 "tohtml.nw" +"label" : { +#line 276 "tohtml.nw" +warn_unused_xref("label", \lastxreflabel) +#line 265 "tohtml.nw" + ; lastxreflabel := arg } +"ref" : { +#line 278 "tohtml.nw" +warn_unused_xref("ref", \lastxrefref) +#line 266 "tohtml.nw" + ; lastxrefref := arg } +"prevdef" : pendingprev := arg +"nextdef" : pendingnext := arg +"beginuses" : useitems := [] +"useitem" : put(useitems, arg) +"enduses" : useitemstab[\thischunk] := useitems +"notused" : notused := arg +#line 364 "tohtml.nw" +"begindefs" | "defitem" | "enddefs" : &null +#line 367 "tohtml.nw" +"beginchunks" : { write(braw, "<ul>") } +"chunkbegin" : { writes("<li>"); comma := ": "; count := 0 + arg ? { ref := tab(upto(' ')); =" "; name := tab(0) } + writechunk(&null, ref, "i", name) } +"chunkuse" : { writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", " } +"chunkdefn" : { writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", " } +"chunkend" : write() +"endchunks" : write("</ul>", eraw) +#line 255 "tohtml.nw" + + default : (\arg|"") ? warn_unknown("xref " || fun) + } +} else +#line 385 "tohtml.nw" +if ="@index " then { + if /noindex then { + if fun := tab(upto(' ')) then {move(1); arg := tab(0)} + else { fun := tab(0); arg := &null } + case fun of { + +#line 376 "tohtml.nw" +"beginindex" : { write(braw, "<ul>") } +"entrybegin" : { writes("<li>"); comma := ": "; count := 0 + arg ? { ref := tab(upto(' ')); =" "; name := tab(0) } + writes(linklabelto("NWI-" || escapeSpecials(name), ref, name, "b")) } +"entryuse" : { writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", " } +"entrydefn" : { writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", " } +"entryend" : write() +"endindex" : write("</ul>", eraw) +#line 400 "tohtml.nw" +"use" : { lastindexref := lastxrefref; lastxrefref := &null } +"defn" : { +#line 274 "tohtml.nw" +every lastxreflabel | lastxrefref := &null +#line 401 "tohtml.nw" + } +"localdefn" : { +#line 274 "tohtml.nw" +every lastxreflabel | lastxrefref := &null +#line 402 "tohtml.nw" + } +"nl" : &null # do nothing -- no hope of keeping line numbering +#line 408 "tohtml.nw" +"begindefs" : if \localindex then { + +#line 313 "tohtml.nw" +if /shortxref then { + useitems := useitemstab[\thischunk] + if \pendingprev | \pendingnext | (\pendinguses, (*\useitems > 0 | \notused)) then { + +#line 356 "tohtml.nw" +if ecode == "</pre>" then { + writes("</pre><blockquote>") + ecode := "</blockquote>" +} +#line 317 "tohtml.nw" + +#line 342 "tohtml.nw" +useprefix := "Used " +every i := 1 to *\useitems do { + usedir := if member(defns_above, useitems[i]) then "above" else "below" + usesuffix := if *\useitems > 1 then " (" || i || ")" else "" + writes(useprefix, linkto(useitems[i], usedir || usesuffix)) + useprefix := ", " +} +#line 318 "tohtml.nw" + useitemstab[\thischunk] + if *\useitems > 0 & (\pendingprev | \pendingnext) then + writes("; ") + p := if *\useitems > 0 then "previous" else "Previous" + n := if *\useitems > 0 then "next" else "Next" + if \pendingprev then + if \pendingnext then + writes(linkto(pendingprev, p), " and ", + linkto(pendingnext, "next"), " definitions") + else + writes(linkto(pendingprev, p), " definition") + else + if \pendingnext then + writes(linkto(pendingnext, n), " definition") + if \pendingprev | \pendingnext | *\useitems > 0 then + writes(".") + if \notused then + writes("<br>\nThis code is written to a file (or else not used).") + write("<p>") + pendinguses := pendingprev := pendingnext := useitems := notused := &null + } +} else + &null +#line 410 "tohtml.nw" + if /uglyindex | /shortxref then { + +#line 356 "tohtml.nw" +if ecode == "</pre>" then { + writes("</pre><blockquote>") + ecode := "</blockquote>" +} +#line 412 "tohtml.nw" + writes("Defines ") + } else { + writes("<hr><b>[</b>") + } + comma := "" + } +"isused" : &null +"defitem" : if \localindex then { + writes(comma, linkto("NWI-" || escapeSpecials(arg), + sgmlwrap("code", escapeSpecials(arg)))) + comma := if /uglyindex | /shortxref then ", " else "<b>,</b> " + } +"enddefs" : if \localindex then + write(if /uglyindex | /shortxref then " (links are to index).<p>" + else "<b>]</b>") +"beginuses" | "isdefined" | "useitem" | "enduses" : &null # use local links +#line 390 "tohtml.nw" + + default : (\arg|"") ? warn_unknown("index " || fun) + } + } + # don't get any warnings if not doing indexing +} else +#line 237 "tohtml.nw" +if ="@quote" & pos(0) then { quoting := 1 ; writes(braw, "<code>") } else +if ="@endquote" & pos(0) then { quoting := nil ; writes("</code>", eraw) } else +#line 240 "tohtml.nw" +if ="@file " then { filename := tab(0); +#line 274 "tohtml.nw" +every lastxreflabel | lastxrefref := &null +#line 240 "tohtml.nw" + } else +if ="@literal " then { writes(tab(0)) } else +if ="@header html " then { +#line 246 "tohtml.nw" +writes("<html><head><title>", tab(0), "</title></head><body>") +#line 242 "tohtml.nw" + } else +if ="@trailer html" & pos(0) then { +#line 248 "tohtml.nw" +write("</body></html>") +#line 243 "tohtml.nw" + } else +#line 51 "tohtml.nw" +if match("@fatal ") then { # follows last else + exit(1) +} else if ="@" then + warn_unknown(1(tab(upto(' ')|0), pos(0) | move(1))) +else + fatal("Botched line in noweb pipeline: ", tab(0)) +#line 38 "tohtml.nw" + } + write() +end +#line 131 "tohtml.nw" +procedure skip_white_tags() + suspend thewhite() || + ((="<" || thewhite() || (="/" || thewhite() | "") || + (tab(many(&letters)) | ="!--") || + tab(upto(">")) || =">" || skip_white_tags() | + "") \ 1) +end +#line 139 "tohtml.nw" +procedure skip_chars() + if not any('<') & upto('<') then + suspend tab(upto('<')) + else if any('&') & upto(';') then + suspend (tab(upto(';')) || =";") + else if (any('<') & upto('>')) | any(' \t') then + (write(&errout, "broken skip_chars, report to nr@eecs.harvard.edu: ", image(tab(0))), + &fail) + else if any('<') then + (write(&errout, "warning: can't find end of HTML tag: ", image(tab(0)), + "\n\tquoted code embedded in comment?"), &fail) + else if pos(0) then + fail + else + suspend tab(0) +end +procedure optwhite() + suspend tab(many(' \t')) | "" +end +procedure thewhite() + return optwhite() \ 1 +end +#line 162 "tohtml.nw" +procedure skip_tags_and_char(count) + local tag + /count := 0 + if any('&') & upto(';') then + suspend(tab(upto(';')) || =";") + else if any('<') then + suspend ="<" || (tab(many(' \t')) | "") || + (="/" | (tag := tab(many(&letters)))) || + tab(upto(">")) || =">" || + (if map(tag) == "a" then + tab(upto("<")) || ="<" || optwhite() || ="/" || optwhite() || + (("A"|"a") == tab(many(&letters))) || optwhite || =">" + else "") || + skip_tags_and_char(count+1) + else + suspend (tab(many(' \t')) | "") || + ((move(1) | if count > 0 then "")\1) # succeed at end if count > 0 + +end +#line 228 "tohtml.nw" +procedure writechunk(label, ref, tag, name, suffix) + /suffix := "" + writes(linklabelto(label, ref, + sgmlwrap(tag, "<" || convquotes(name) || ">" || suffix), + "bare")) + return +end +#line 280 "tohtml.nw" +procedure warn_unused_xref(tag, label) + static warned + initial warned := set() + if not member(warned, tag) then { + insert(warned, tag) + write(&errout, "Warning: internal inconsistency in noweb (not urgent)---") + write(&errout, "\tnever used @xref ", tag, " ", label) + } + return +end +#line 436 "tohtml.nw" +procedure linklabelto(label, ref, contents, bare) + local s + s := if \bare then "" else braw + s ||:= if \label | \ref then "<a" else "" + s ||:= " name=" || image(\label) + s ||:= " href=" || image("#" || \ref) + s ||:= if \label | \ref then ">" else "" + s ||:= if \bare then "" else eraw + s ||:= contents + s ||:= if \bare then "" else braw + s ||:= if \label | \ref then "</a>" else "" + s ||:= if \bare then "" else eraw + return s +end +#line 453 "tohtml.nw" +procedure linklabel(label, contents, bare) + return linklabelto(label, &null, contents, bare) +end + +procedure linkto(ref, contents) + return linklabelto(&null, ref, contents, "bare") +end +#line 467 "tohtml.nw" +procedure sgmlwrap(tag, s) + return "<" || tag || ">" || s || "</" || tag || ">" +end +#line 475 "tohtml.nw" +procedure escapeSpecials (l) + static escapes, specials + initial {escapes := table(); + escapes["&"] := "&" + escapes["<"] := "<" + escapes[">"] := ">" + escapes["\""] := """ + specials := '' + every specials ++:= key(escapes) + } + s := "" + l ? { + while s ||:= tab(upto(specials)) do + s ||:= escapes[move(1)] + return s || tab(0) + } +end +#line 496 "tohtml.nw" +procedure convquotes(s) + r := "" + s ? { # don't call escapeSpecials on s; it destroys internal markup + while r ||:= tab(find("[[")) do { + ="[[" | fatal("impossible missing [[") + r ||:= "<code>" || tab(find("]]")) + r ||:= tab(many(']')-2) + ="]]" | fatal("impossible missing ]] in ", image(s)) + r ||:= "</code>" + } + return r || tab(0) + } +end +#line 511 "tohtml.nw" +procedure warn_unknown(tag) + static warned + initial warned := set() + if not member(warned, tag) then { + write(&errout, "Warning: unrecognized escape @", tag, " ", tab(0)) + insert(warned, tag) + } + return +end +#line 522 "tohtml.nw" +procedure fatal(L[]) + write!([&errout, "noweb error in tohtml: "] ||| L) + exit(1) +end +#line 529 "tohtml.nw" +procedure rcsinfo () + return "$Id: tohtml.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/tohtml.nw b/web/noweb/src/icon/tohtml.nw new file mode 100644 index 0000000000..9eb904836c --- /dev/null +++ b/web/noweb/src/icon/tohtml.nw @@ -0,0 +1,534 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\documentstyle[noweb]{article} +\pagestyle{noweb} +\begin{document} +@ +\section{Converting {\tt noweb} markup to {\tt HTML}} +This copyright applies both to the {\tt noweb} source and to the +generated code. +Thanks to Bill Trost for getting me started with an early version. +<<copyright notice>>= +# Copyright 1994 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +@ +The [[-raw]] option brackets HTML with [[\begin{rawhtml}]] and +[[\end{rawhtml}]]; the purpose is to embed HTML in a {\LaTeX} +document before converting the document with {\tt latex2html}. +[[braw]] and [[eraw]] hold those delimiters (or else empty strings). +<<*>>= +<<copyright notice>> +# Don't try to understand this file! Look at icon/tohtml.nw in the noweb source! + +global braw, eraw +procedure main(args) + local delay, raw, where, localindex, noindex, shortxref + <<initialization>> + every braw | eraw := "" + delay := !args == "-delay" + noindex := !args == "-noindex" + shortxref := if !args == "-longxref" then &null else 1 + localindex := (/noindex, !args == "-localindex") # produce local identifier xref? + raw := !args == "-raw" + if \raw then {braw := "\\begin{rawhtml}"; eraw := "\\end{rawhtml}"} + if !args == "-no-gen-comment" then &null + else + write(braw, "<!-- this file was generated automatically by noweave;", + " better not edit it-->", eraw) + while inputline := read() do inputline ? { + <<scan and convert>> + } + write() +end +<<scan and convert>>= +<<@text>> +<<@nl>> +<<code chunks>> +<<@defn>> +<<docs chunks>> +<<@use>> +<<@xref>> +<<@index>> +<<others>> +if match("@fatal ") then { # follows last else + exit(1) +} else if ="@" then + warn_unknown(1(tab(upto(' ')|0), pos(0) | move(1))) +else + fatal("Botched line in noweb pipeline: ", tab(0)) +@ +[[ecode]] is the marker used at the end of the current code chunk. +If there is no cross-reference stuff at the end, we just use [[</pre>]]; +otherwise we terminate whatever environment is used for the cross-reference stuff. +<<code chunks>>= +if ="@begin code " then { code := 1 ; thischunk := &null + <<reset cross-reference info>> + writes(braw, "<pre>"); ecode := "</pre>" } else +if ="@end code " then { <<dump pending cross-reference info (long form)>> + code := nil ; previscode := 1 + writes(ecode, eraw) } else +@ We maintain [[thischunk]] null until we've seen [[@defn]]. + +We want to try to avoid emitting paragraph elements when the +preceding chunk is a code chunk, as tracked by [[previscode]]. +Also, if we do slip in a paragraph, we may use the {\LaTeX} style. +<<docs chunks>>= +if ="@begin docs " then { if \previscode then writes(if /raw then "<p>" else "\\par") + previscode := &null + text := 0 } else +@ +Sometimes it happens that a document-chunk anchor is put in a document chunk that +contains no text. In that case, we put in a phony anchor at the end of the chunk so +we won't lose the cross-reference. +<<docs chunks>>= +if ="@end docs " then { write(linklabel(\lastxreflabel, "*")) + lastxreflabel := &null } else +@ +Normally, if there's a pending anchor, we put it on the first available text line. +There's a bit of a fine point that crops up if the very first piece of text +is quoted code. In that case we have to attach both the label for the +{\it docs} anchor and the ref for the {\it index} anchor. +<<@text>>= +if ="@text " then { text +:= *(line := tab(0)) + if \code then + writes((<<index anchor>>) | escapeSpecials(line)) + else if \quoting then + writes((<<index and docs anchor>>) | escapeSpecials(line)) + else + writes((<<docs anchor>>) | line) } else +<<index anchor>>= +2(line ? {tab(many(' \t')); not pos(0)}, + linkto(\lastindexref, escapeSpecials(line)), + lastindexref := &null) +@ +We anchor on the first nonblank character of the line, unless that's +an SGML tag, in which case we have to +skip past. +None of this crap would be necessary if HTML could anchor to empty text. +<<docs anchor>>= +1(line ? skip_white_tags() || + linklabel(\lastxreflabel, skip_chars()) || tab(0), + insert(defns_above, \lastxreflabel), + lastxreflabel := &null) +<<old docs anchor>>= +2(line ? {tab(many(' \t')); not pos(0)}, + line ? linklabel(\lastxreflabel, + (tab(many(' \t')) | "") || + (skip_tags_and_char() | "*")) || + tab(0), + insert(defns_above, \lastxreflabel), + lastxreflabel := &null) +@ +An indexed identifier is not empty and contains no tags, so we don't +have to worry. +<<index and docs anchor>>= +2(line ? {tab(many(' \t')); not pos(0)}, + linklabelto(lastxreflabel, lastindexref, escapeSpecials(line), "bare"), + lastxreflabel := lastindexref := &null) +@ +Skip as many tags as possible. +Argument is number of tags already skipped; if nonzero, we're willing to succeed +at the end of the string. +<<*>>= +procedure skip_white_tags() + suspend thewhite() || + ((="<" || thewhite() || (="/" || thewhite() | "") || + (tab(many(&letters)) | ="!--") || + tab(upto(">")) || =">" || skip_white_tags() | + "") \ 1) +end +<<*>>= +procedure skip_chars() + if not any('<') & upto('<') then + suspend tab(upto('<')) + else if any('&') & upto(';') then + suspend (tab(upto(';')) || =";") + else if (any('<') & upto('>')) | any(' \t') then + (write(&errout, "broken skip_chars, report to nr@eecs.harvard.edu: ", image(tab(0))), + &fail) + else if any('<') then + (write(&errout, "warning: can't find end of HTML tag: ", image(tab(0)), + "\n\tquoted code embedded in comment?"), &fail) + else if pos(0) then + fail + else + suspend tab(0) +end +procedure optwhite() + suspend tab(many(' \t')) | "" +end +procedure thewhite() + return optwhite() \ 1 +end +<<*>>= +procedure skip_tags_and_char(count) + local tag + /count := 0 + if any('&') & upto(';') then + suspend(tab(upto(';')) || =";") + else if any('<') then + suspend ="<" || (tab(many(' \t')) | "") || + (="/" | (tag := tab(many(&letters)))) || + tab(upto(">")) || =">" || + (if map(tag) == "a" then + tab(upto("<")) || ="<" || optwhite() || ="/" || optwhite() || + (("A"|"a") == tab(many(&letters))) || optwhite || =">" + else "") || + skip_tags_and_char(count+1) + else + suspend (tab(many(' \t')) | "") || + ((move(1) | if count > 0 then "")\1) # succeed at end if count > 0 + +end +@ +The preceding scheme sometimes wraps an anchor around a tag. +If that turns out to be bad, we could try the following alternative: +<<funky new docs anchor>>= +2(line ? {tab(many(' \t')); not pos(0)}, + line ? scan_initial_tags() || + linklabel(\lastxreflabel, + (tab(many(' \t')) | "") || (scan_past_char() | "*")) || + tab(0), + insert(defns_above, \lastxreflabel), + lastxreflabel := &null) +<<procedures to support new docs anchor>>= +procedure scan_initial_tags() + suspend (="<" || (tab(many(' \t')) | "") || + (="/" | (tag := tab(many(&letters)), map(tag) ~== "a", tag)) || + tab(upto(">")) || =">" || scan_initial_tags()) | + "" +end + +procedure scan_past_char() + if any('&') & upto(';') then + suspend(tab(upto(';')) || =";") + else + suspend (tab(many(' \t')) | "") || tab(any(~'<')) +end +<<@nl>>= +if ="@nl" & pos(0) then { <<show definitions and uses on definition line---once>> + write() } else +<<@defn>>= +if ="@defn " then { writechunk(lastxreflabel, lastxrefref, "dfn", + thischunk := tab(0), defns[thischunk] || "=") + insert(defns_above, \lastxreflabel) + useitems := \useitemstab[thischunk] + pendinguses := 1 + <<clear [[lastxref*]]>> + defns[thischunk] := "+" } else +<<initialization>>= +useitemstab := table() +defns := table("") +defns_above := set() # keep track of defining chunks we've seen +<<@use>>= +if ="@use " then { writechunk(lastxreflabel, lastxrefref, "i", tab(0)) + <<clear [[lastxref*]]>> } else +@ +Writing a chunk involves creating an anchor for it. +The anchor is ``bare'' because it's always in an HTML-containing place. +<<*>>= +procedure writechunk(label, ref, tag, name, suffix) + /suffix := "" + writes(linklabelto(label, ref, + sgmlwrap(tag, "<" || convquotes(name) || ">" || suffix), + "bare")) + return +end +@ +<<others>>= +if ="@quote" & pos(0) then { quoting := 1 ; writes(braw, "<code>") } else +if ="@endquote" & pos(0) then { quoting := nil ; writes("</code>", eraw) } else +<<others>>= +if ="@file " then { filename := tab(0); <<clear [[lastxref*]]>> } else +if ="@literal " then { writes(tab(0)) } else +if ="@header html " then { <<write HTML header>> } else +if ="@trailer html" & pos(0) then { <<write HTML trailer>> } else +@ +<<write HTML header>>= +writes("<html><head><title>", tab(0), "</title></head><body>") +<<write HTML trailer>>= +write("</body></html>") +@ +<<@xref>>= +if ="@xref " then { + if fun := tab(upto(' ')) then {move(1); arg := tab(0)} + else { fun := tab(0); arg := &null } + case fun of { + <<cases for @xref>> + default : (\arg|"") ? warn_unknown("xref " || fun) + } +} else +@ +[[useitemstab]] seems to exist to enable us to repeat the +uses for each chunk, even though the [[noidx]] filter only provides them once. +I think it may prove better to provide use information for each instance +of a chunk. +<<cases for @xref>>= +"label" : { <<warn if unused [[lastxreflabel]]>>; lastxreflabel := arg } +"ref" : { <<warn if unused [[lastxrefref]]>>; lastxrefref := arg } +"prevdef" : pendingprev := arg +"nextdef" : pendingnext := arg +"beginuses" : useitems := [] +"useitem" : put(useitems, arg) +"enduses" : useitemstab[\thischunk] := useitems +"notused" : notused := arg +<<clear [[lastxref*]]>>= +every lastxreflabel | lastxrefref := &null +<<warn if unused [[lastxreflabel]]>>= +warn_unused_xref("label", \lastxreflabel) +<<warn if unused [[lastxrefref]]>>= +warn_unused_xref("ref", \lastxrefref) +<<*>>= +procedure warn_unused_xref(tag, label) + static warned + initial warned := set() + if not member(warned, tag) then { + insert(warned, tag) + write(&errout, "Warning: internal inconsistency in noweb (not urgent)---") + write(&errout, "\tnever used @xref ", tag, " ", label) + } + return +end +@ +<<show definitions and uses on definition line---once>>= +if \shortxref & (\pendinguses | \pendingprev | \pendingnext | \notused) then { + <<write out definition-line markup>> + pendinguses := pendingprev := pendingnext := notused := &null +} +<<write out definition-line markup>>= +if \pendinguses & *\useitems > 0 then { + writes(" <b>(") + every i := 1 to *\useitems do { + usedir := if member(defns_above, useitems[i]) then "<-U" else "U->" + writes(if i > 1 then " " else "", linkto(useitems[i], usedir)) + } + writes(")</b>") +} +if \pendingprev | \pendingnext then { + writes(" <b>[") + writes(linkto(\pendingprev, "<-")) + writes("D") + writes(linkto(\pendingnext, "->")) + writes("]</b>") +} +<<dump pending cross-reference info (long form)>>= +if /shortxref then { + useitems := useitemstab[\thischunk] + if \pendingprev | \pendingnext | (\pendinguses, (*\useitems > 0 | \notused)) then { + <<code-to-blockquote>> + <<write out uses with links>> + useitemstab[\thischunk] + if *\useitems > 0 & (\pendingprev | \pendingnext) then + writes("; ") + p := if *\useitems > 0 then "previous" else "Previous" + n := if *\useitems > 0 then "next" else "Next" + if \pendingprev then + if \pendingnext then + writes(linkto(pendingprev, p), " and ", + linkto(pendingnext, "next"), " definitions") + else + writes(linkto(pendingprev, p), " definition") + else + if \pendingnext then + writes(linkto(pendingnext, n), " definition") + if \pendingprev | \pendingnext | *\useitems > 0 then + writes(".") + if \notused then + writes("<br>\nThis code is written to a file (or else not used).") + write("<p>") + pendinguses := pendingprev := pendingnext := useitems := notused := &null + } +} else + &null +<<write out uses with links>>= +useprefix := "Used " +every i := 1 to *\useitems do { + usedir := if member(defns_above, useitems[i]) then "above" else "below" + usesuffix := if *\useitems > 1 then " (" || i || ")" else "" + writes(useprefix, linkto(useitems[i], usedir || usesuffix)) + useprefix := ", " +} +<<reset cross-reference info>>= +useitems := [] +notused := &null +@ +The hack here is to put the supplementary information in a blockquote area +after the code. +<<code-to-blockquote>>= +if ecode == "</pre>" then { + writes("</pre><blockquote>") + ecode := "</blockquote>" +} +@ +The HTML back end ignores [[@xref begindefs]], [[@xref defitem]], and +[[@xref enddefs]]; it uses the [[nextdef]] and [[prevdef]] links instead. +<<cases for @xref>>= +"begindefs" | "defitem" | "enddefs" : &null +@ +<<cases for @xref>>= +"beginchunks" : { write(braw, "<ul>") } +"chunkbegin" : { writes("<li>"); comma := ": "; count := 0 + arg ? { ref := tab(upto(' ')); =" "; name := tab(0) } + writechunk(&null, ref, "i", name) } +"chunkuse" : { writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", " } +"chunkdefn" : { writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", " } +"chunkend" : write() +"endchunks" : write("</ul>", eraw) +<<cases for @index>>= +"beginindex" : { write(braw, "<ul>") } +"entrybegin" : { writes("<li>"); comma := ": "; count := 0 + arg ? { ref := tab(upto(' ')); =" "; name := tab(0) } + writes(linklabelto("NWI-" || escapeSpecials(name), ref, name, "b")) } +"entryuse" : { writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", " } +"entrydefn" : { writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", " } +"entryend" : write() +"endindex" : write("</ul>", eraw) +<<@index>>= +if ="@index " then { + if /noindex then { + if fun := tab(upto(' ')) then {move(1); arg := tab(0)} + else { fun := tab(0); arg := &null } + case fun of { + <<cases for @index>> + default : (\arg|"") ? warn_unknown("index " || fun) + } + } + # don't get any warnings if not doing indexing +} else +@ +The local identifier cross-reference doesn't show each use; it just shows +the identifiers that are defined, with links to the full index. +<<cases for @index>>= +"use" : { lastindexref := lastxrefref; lastxrefref := &null } +"defn" : { <<clear [[lastxref*]]>> } +"localdefn" : { <<clear [[lastxref*]]>> } +"nl" : &null # do nothing -- no hope of keeping line numbering +@ +I tried an abbreviated index format at the end of the chunk, but it looks +so bad that I've punted on it (for now). Hence the [[\uglyindex]]. +<<cases for @index>>= +"begindefs" : if \localindex then { + <<dump pending cross-reference info (long form)>> + if /uglyindex | /shortxref then { + <<code-to-blockquote>> + writes("Defines ") + } else { + writes("<hr><b>[</b>") + } + comma := "" + } +"isused" : &null +"defitem" : if \localindex then { + writes(comma, linkto("NWI-" || escapeSpecials(arg), + sgmlwrap("code", escapeSpecials(arg)))) + comma := if /uglyindex | /shortxref then ", " else "<b>,</b> " + } +"enddefs" : if \localindex then + write(if /uglyindex | /shortxref then " (links are to index).<p>" + else "<b>]</b>") +"beginuses" | "isdefined" | "useitem" | "enduses" : &null # use local links +@ +\subsection{Support functions} +Here's all our anchor support goo. +Unless the anchor is deemed ``bare,'' the beginning and ending tags +are protected with [[braw]] and [[eraw]]. +This step is necessary to make \verb=noweave -latex+html= work without +trapping {\LaTeX} markup in a \texttt{rawhtml} environment. +<<*>>= +procedure linklabelto(label, ref, contents, bare) + local s + s := if \bare then "" else braw + s ||:= if \label | \ref then "<a" else "" + s ||:= " name=" || image(\label) + s ||:= " href=" || image("#" || \ref) + s ||:= if \label | \ref then ">" else "" + s ||:= if \bare then "" else eraw + s ||:= contents + s ||:= if \bare then "" else braw + s ||:= if \label | \ref then "</a>" else "" + s ||:= if \bare then "" else eraw + return s +end +@ +Here are two abbreviations. +<<*>>= +procedure linklabel(label, contents, bare) + return linklabelto(label, &null, contents, bare) +end + +procedure linkto(ref, contents) + return linklabelto(&null, ref, contents, "bare") +end +@ [[linkto]] is always used ``bare,'' and I've decided that the +inconsistency in calling conversions is worth the savings from not +having to write a meaningless argument everywhere. +Reasonable people could disagree. +@ +Another support function is used for wrapping tags around text: +<<*>>= +procedure sgmlwrap(tag, s) + return "<" || tag || ">" || s || "</" || tag || ">" +end +@ +Lucky for us, {\tt HTML} has few special characters. Unlucky for us, +we have to deal with each one seperately. Nothing much to whine +about, really. +<<*>>= +procedure escapeSpecials (l) + static escapes, specials + initial {escapes := table(); + escapes["&"] := "&" + escapes["<"] := "<" + escapes[">"] := ">" + escapes["\""] := """ + specials := '' + every specials ++:= key(escapes) + } + s := "" + l ? { + while s ||:= tab(upto(specials)) do + s ||:= escapes[move(1)] + return s || tab(0) + } +end +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<*>>= +procedure convquotes(s) + r := "" + s ? { # don't call escapeSpecials on s; it destroys internal markup + while r ||:= tab(find("[[")) do { + ="[[" | fatal("impossible missing [[") + r ||:= "<code>" || tab(find("]]")) + r ||:= tab(many(']')-2) + ="]]" | fatal("impossible missing ]] in ", image(s)) + r ||:= "</code>" + } + return r || tab(0) + } +end +@ +<<*>>= +procedure warn_unknown(tag) + static warned + initial warned := set() + if not member(warned, tag) then { + write(&errout, "Warning: unrecognized escape @", tag, " ", tab(0)) + insert(warned, tag) + } + return +end +@ +<<*>>= +procedure fatal(L[]) + write!([&errout, "noweb error in tohtml: "] ||| L) + exit(1) +end +@ +\end{document} +<<*>>= +procedure rcsinfo () + return "$Id: tohtml.nw,v 1.22 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +@ diff --git a/web/noweb/src/icon/totex.icn b/web/noweb/src/icon/totex.icn new file mode 100644 index 0000000000..353f695656 --- /dev/null +++ b/web/noweb/src/icon/totex.icn @@ -0,0 +1,456 @@ +#line 5 "totex.nw" +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +#line 11 "totex.nw" +procedure rcsinfo () + return "$Id: totex.nw,v 1.21 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +global headercomment, realwrite +procedure main(args) + local delay, name + +#line 46 "totex.nw" +text := 1 +#line 135 "totex.nw" +useitemstab := table() +#line 149 "totex.nw" +defns := table("") +#line 336 "totex.nw" +TeXspecials := '\\{}$&#^_ ~%' +#line 19 "totex.nw" + delay := !args == "-delay" + noindex := !args == "-noindex" + if !args == "-no-gen-comment" then &null + else {realwrite := write; write := firstwrite} + while inputline := read() do inputline ? { + +#line 82 "totex.nw" +if ="@text " then { text +:= *(line := tab(0)) + writes(if \quoting then +#line 88 "totex.nw" +if /noindex & \lastindexref then + "\\nwlinkedidentq{" || TeXliteral(line) || "}{" || lastindexref || "}" +else + TeXliteral(line) +#line 84 "totex.nw" + else if \code then +#line 93 "totex.nw" +if /noindex & \lastindexref then + "\\nwlinkedidentc{" || escape(line, '{}\\') || "}{" || lastindexref || "}" +else + escape(line, '{}\\') + +#line 85 "totex.nw" + else line) + lastindexref := &null } else +#line 99 "totex.nw" +if ="@nl" & pos(0) then { if /code then { +#line 115 "totex.nw" +if text = 0 then writes("\\nwdocspar") +text := 1 +#line 99 "totex.nw" + } + if \quoting then writes("\\nwnewline") + +#line 201 "totex.nw" +if \pendinguses | \pendingprev | \pendingnext then { + writes("\\nwstartdeflinemarkup") + +#line 208 "totex.nw" +if \pendinguses then { + dumpitems(useitems, "nwusesondefline") +} +if \pendingprev | \pendingnext then { + writes("\\nwprevnextdefs{", \pendingprev | "\\relax", "}{", + \pendingnext | "\\relax", "}") +} +#line 204 "totex.nw" + writes("\\nwenddeflinemarkup") + pendinguses := pendingprev := pendingnext := &null +} +#line 102 "totex.nw" + write() } else +#line 71 "totex.nw" +if ="@begin code " then { code := 1 ; writes("\\nwbegincode{", tab(0), "}") + +#line 177 "totex.nw" +every defitems | useitems := [] +notused := &null +#line 72 "totex.nw" + } else +if ="@end code " then { +#line 184 "totex.nw" +dumpitems(defitems, "nwalsodefined") +dumpitems(useitems, "nwused") +writes("\\nwnotused{", \notused, "}") +#line 177 "totex.nw" +every defitems | useitems := [] +notused := &null +#line 74 "totex.nw" + code := &null ; writes("\\nwendcode{}") + lastdefnlabel := pendingprev := pendingnext := &null } else +#line 122 "totex.nw" +if ="@defn " then { writes("\\sublabel{", \lastxreflabel, "}") + writes("\\nwmargintag{", label2tag(\lastxreflabel), "}") + writes("\\moddef{", convquotes(thischunk := tab(0)), + ("~" || label2tag(\lastxrefref)) | "", + "}\\", defns[thischunk], "endmoddef") + useitems := \useitemstab[thischunk] + pendinguses := 1 + lastdefnlabel := lastxreflabel + +#line 216 "totex.nw" +every lastxreflabel | lastxrefref := &null +#line 131 "totex.nw" + defns[thischunk] := "plus" } else +#line 77 "totex.nw" +if ="@begin docs " then { if \delay & match(0) then +#line 110 "totex.nw" +&null +#line 77 "totex.nw" + + else {text := 0; writes("\\nwbegindocs{",tab(0),"}")} } else +if ="@end docs " then { if \delay & match(0) then +#line 112 "totex.nw" +{ writes("\\nwfilename{", filename, "}"); delay := &null } +#line 79 "totex.nw" + + else writes("\\nwenddocs{}") } else +#line 137 "totex.nw" +if ="@use " then { writes("\\LA{}", convquotes(name := tab(0)), + ("~" || label2tag(\lastxrefref)) | "", + "\\RA{}") + +#line 216 "totex.nw" +every lastxreflabel | lastxrefref := &null +#line 140 "totex.nw" + } else +#line 175 "totex.nw" +if ="@xref " then { +#line 189 "totex.nw" +if ="label " then { lastxreflabel := tab(0) } else +if ="ref " then { lastxrefref := tab(0) } else +if ="begindefs" & pos(0) then { } else +if ="defitem " then { put(defitems, tab(0)) } else +if ="enddefs" & pos(0) then { } else +if ="beginuses" & pos(0) then { useitems := [] } else +if ="useitem " then { put(useitems, tab(0)) } else +if ="enduses" & pos(0) then { useitemstab[thischunk] := useitems } else +if ="notused " then { notused := tab(0) } else +if ="nextdef " then { pendingnext := tab(0) } else +if ="prevdef " then { pendingprev := tab(0) } else +#line 294 "totex.nw" +if ="beginchunks" & pos(0) then { } else +if ="chunkbegin " then { label := tab(upto(' ')); =" " + writes("\\nwixlogsorted{c}{{", convquotes(tab(0)), + "}{", label, "}{") } else +if ="chunkuse " then { writes("\\nwixu{", tab(0), "}") } else +if ="chunkdefn " then { writes("\\nwixd{", tab(0), "}") } else +if ="chunkend" & pos(0) then { write("}}%") } else +if ="endchunks" & pos(0) then { } else +#line 175 "totex.nw" + +#line 218 "totex.nw" +warn_unknown("xref " || tab(upto(' \t') | 0)) +#line 175 "totex.nw" + } else +#line 232 "totex.nw" +if ="@index " then { +#line 240 "totex.nw" +if ="nl" & pos(0) then { write(if \code then "\\eatline" else "%")}else +if =("defn "|"localdefn ") then +#line 244 "totex.nw" +/noindex & +#line 241 "totex.nw" + { +#line 253 "totex.nw" +writes("\\nosublabel{", \lastxreflabel, "}") +writes("\\nwindexdefn{\\nwixident{", TeXliteral(name := tab(0)), "}}{", indexlabel(name), "}{", + \lastxrefref, "}") +#line 216 "totex.nw" +every lastxreflabel | lastxrefref := &null +#line 241 "totex.nw" + } else +if ="use " then +#line 244 "totex.nw" +/noindex & +#line 242 "totex.nw" + { +#line 261 "totex.nw" +if /code & /quoting then { + writes("\\protect\\nosublabel{", \lastxreflabel, "}") + writes("\\protect\\nwindexuse{\\nwixident{", TeXliteral(name := tab(0)), "}}{", + indexlabel(name), "}{", \lastxrefref, "}") +} +lastindexref := lastxrefref +#line 216 "totex.nw" +every lastxreflabel | lastxrefref := &null +#line 242 "totex.nw" + } else +#line 272 "totex.nw" +if ="begindefs" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 272 "totex.nw" + { +#line 184 "totex.nw" +dumpitems(defitems, "nwalsodefined") +dumpitems(useitems, "nwused") +writes("\\nwnotused{", \notused, "}") +#line 177 "totex.nw" +every defitems | useitems := [] +notused := &null +#line 273 "totex.nw" + writes("\\nwidentdefs{") } else +if ="isused " then +#line 244 "totex.nw" +/noindex & +#line 274 "totex.nw" + { "handled by latex" } else +if ="defitem " then +#line 244 "totex.nw" +/noindex & +#line 275 "totex.nw" + { i := tab(0); +#line 284 "totex.nw" +writes("\\\\{{\\nwixident{", TeXliteral(i), "}}{", indexlabel(i), "}}") +#line 275 "totex.nw" + } else +if ="enddefs" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 276 "totex.nw" + { writes("}") } else +if ="beginuses" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 277 "totex.nw" + { +#line 184 "totex.nw" +dumpitems(defitems, "nwalsodefined") +dumpitems(useitems, "nwused") +writes("\\nwnotused{", \notused, "}") +#line 177 "totex.nw" +every defitems | useitems := [] +notused := &null +#line 278 "totex.nw" + writes("\\nwidentuses{"); ulist := [] } else +if ="isdefined " then +#line 244 "totex.nw" +/noindex & +#line 279 "totex.nw" + { "latex finds the definitions" } else +if ="useitem " then +#line 244 "totex.nw" +/noindex & +#line 280 "totex.nw" + { i := tab(0); +#line 284 "totex.nw" +writes("\\\\{{\\nwixident{", TeXliteral(i), "}}{", indexlabel(i), "}}") +#line 280 "totex.nw" + + put(ulist, i); } else +if ="enduses" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 282 "totex.nw" + { writes("}"); +#line 286 "totex.nw" +every i := !ulist do + writes("\\nwindexuse{\\nwixident{", TeXliteral(i), "}}{", indexlabel(i), "}{", \lastdefnlabel, "}") +#line 282 "totex.nw" + } else +#line 303 "totex.nw" +if ="beginindex" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 303 "totex.nw" + { } else +if ="entrybegin " then +#line 244 "totex.nw" +/noindex & +#line 304 "totex.nw" + { label := tab(upto(' ')); =" "; name := tab(0) + write("\\nwixlogsorted{i}{{\\nwixident{", TeXliteral(name), + "}}{", indexlabel(name), "}}%") } else +if ="entryuse " then +#line 244 "totex.nw" +/noindex & +#line 307 "totex.nw" + { "handled by latex" } else +if ="entrydefn " then +#line 244 "totex.nw" +/noindex & +#line 308 "totex.nw" + { "handled by latex" } else +if ="entryend" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 309 "totex.nw" + { } else +if ="endindex" & pos(0) then +#line 244 "totex.nw" +/noindex & +#line 310 "totex.nw" + { } else +#line 232 "totex.nw" + +#line 234 "totex.nw" +warn_unknown("index " || tab(upto(' \t') | 0)) +#line 232 "totex.nw" + } else +#line 153 "totex.nw" +if ="@quote" & pos(0) then { quoting := 1 ; writes("{\\Tt{}") } else +if ="@endquote" & pos(0) then { quoting := &null ; writes("\\nwendquote}") } else +if ="@file " then { filename := tab(0); +#line 216 "totex.nw" +every lastxreflabel | lastxrefref := &null +#line 156 "totex.nw" + \delay | writes("\\nwfilename{", filename, "}") + } else +if ="@line " then { } else +if ="@literal " then { writes(tab(0)) } else +if ="@header latex " then { +#line 165 "totex.nw" +writes("\\documentclass{article}\\usepackage{noweb}\\pagestyle{noweb}\\noweboptions{", tab(0), + "}\\begin{document}") +#line 160 "totex.nw" + } else +if ="@header tex " then { writes("\\input nwmac ") } else +if ="@trailer latex" & pos(0) then { write("\\end{document}") } else +if ="@trailer tex" & pos(0) then { write("\\bye") } else +#line 61 "totex.nw" +if ="@fatal " then { # follows last else + # write(&errout, "Noweb error in stage ", tab(upto(' ')), ":", tab(0)) + exit(1) +} else if ="@" then + warn_unknown(1(tab(upto(' ')|0), pos(0) | move(1))) +else + write(&errout, "Botched line in noweb pipeline: ", tab(0)) +#line 25 "totex.nw" + } + write() +end +#line 142 "totex.nw" +procedure label2tag(label) + return "{\\nwtagstyle{}\\subpageref{" || label || "}}" +end +#line 220 "totex.nw" +procedure dumpitems(items, cs) + if *\items > 0 then { + writes("\\", cs, "{") + every writes("\\\\{", !items, "}") + writes("}") + return + } else + fail +end +#line 316 "totex.nw" +procedure firstwrite(L[]) + write := realwrite + put(L, + "% ===> this file was generated automatically by noweave --- better not edit it") + return write!L +end +#line 325 "totex.nw" +procedure escape(line, chars, prefix) + /prefix := "\\" + line ? { + s := "" + while s ||:= tab(upto(chars)) do s ||:= prefix || move(1) + return s || tab(0) + } +end +#line 334 "totex.nw" +global TeXspecials +#line 357 "totex.nw" +procedure TeXliteral(arg) + static nospace, code + initial { codes := ["\\", "nwbackslash", + "{", "nwlbrace", + "}", "nwrbrace", + "$", "$", + "&", "&", + "#", "#", + "^", "char94", + "_", "_", + "%", "%", + "~", "char126"] + code := table() + while (c := get(codes), n := get(codes)) do code[c] := string(n) + if c := !TeXspecials & c ~== " " & not member(code, c) then + fatal("internal error, character-code mismatch, report a bug!") + } + s := "" + arg ? { + while s ||:= tab(upto(TeXspecials)) do { + c := move(1) + if member(code, c) then + s ||:= "{\\" || code[c] || "}" + else + s ||:= "\\" || c + } + return s || tab(0) + } +end +#line 390 "totex.nw" +procedure convquotes(s) + r := "" + s ? { + while r ||:= tab(find("[[")) do { + ="[[" | fatal("impossible missing [[") + r ||:= "\\code{}" || TeXliteral(tab(find("]]"))) + r ||:= tab(many(']')-2) + ="]]" | fatal("impossible missing ]]") + r ||:= "\\edoc{}" + } + return r || tab(0) + } +end +#line 404 "totex.nw" +procedure warn_unknown(tag) + static warned + initial warned := set() + if not member(warned, tag) then { + write(&errout, "Warning: unrecognized escape @", tag, " ", tab(0)) + insert(warned, tag) + } + return +end +#line 416 "totex.nw" +procedure indexlabel(ident) + static badset, trans + initial { + +#line 430 "totex.nw" +trans := table() +trans[" "] := "sp" # space +trans["#"] := "has" # hash +trans["$"] := "do" # dollar +trans["%"] := "pe" # percent +trans["&"] := "am" # ampersand +trans[","] := "com" # commad +trans[":"] := "col" # colon +trans["\\"] := "bs" # backslash +trans["^"] := "hat" # hat +trans["_"] := "un" # underscore +trans["{"] := "lb" # left brace +trans["}"] := "rb" # right brace +trans["~"] := "ti" # tilde +#line 420 "totex.nw" + badset := '' + every badset ++:= key(trans) + } + ident ? { + s := "" + while s ||:= tab(upto(badset)) do s ||:= ":" || trans[move(1)] + return s || tab(0) + } +end +#line 445 "totex.nw" +procedure fatal(L[]) + write!([&errout, "noweb error in tohtml: "] ||| L) + exit(1) +end + diff --git a/web/noweb/src/icon/totex.nw b/web/noweb/src/icon/totex.nw new file mode 100644 index 0000000000..0b3200aae7 --- /dev/null +++ b/web/noweb/src/icon/totex.nw @@ -0,0 +1,450 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- +\section{Converting {\tt noweb} markup to {\TeX} markup (Icon version)} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +@ +Here's the organization of the source: +<<*>>= +<<copyright notice>> +procedure rcsinfo () + return "$Id: totex.nw,v 1.21 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +global headercomment, realwrite +procedure main(args) + local delay, name + <<initialization>> + delay := !args == "-delay" + noindex := !args == "-noindex" + if !args == "-no-gen-comment" then &null + else {realwrite := write; write := firstwrite} + while inputline := read() do inputline ? { + <<scan and convert>> + } + write() +end +@ The markup carefully adds no newlines not already present in the input, +so that the line numbers of the {\TeX} file will be the same as the +numbers of the corresponding {\tt noweb} file. +The variables are: +\begin{description} +\item[\tt code] Nonnull if converting a code chunk. +\item[\tt quoting] Nonnull if quoting code in documentation. +\item[\tt text] Number of characters written since start of + documentation chunk. +\end{description} +[[text]] is used to write [[\nwdocspar]] if a newline appears at the +beginning of a documentation chunk without any intervening text. +This subtle trick preserves new-paragraph semantics without requiring +the insertion of a blank line that would throw off the line count. +The special control sequences makes a page break at that spot especially likely, +so that small documentation chunks will appear on the some page as the code +chunks they precede. +<<initialization>>= +text := 1 +@ %def text + +Ordering helps improve speed, so I write things in a funny order. +This whole code is a giant [[if ... then ... else if ...]]. +<<scan and convert>>= +<<@text>> +<<@nl>> +<<code chunks>> +<<@defn>> +<<docs chunks>> +<<@use>> +<<@xref>> +<<@index>> +<<others>> +if ="@fatal " then { # follows last else + # write(&errout, "Noweb error in stage ", tab(upto(' ')), ":", tab(0)) + exit(1) +} else if ="@" then + warn_unknown(1(tab(upto(' ')|0), pos(0) | move(1))) +else + write(&errout, "Botched line in noweb pipeline: ", tab(0)) +@ +\subsection{Basic text and chunk boundaries} +<<code chunks>>= +if ="@begin code " then { code := 1 ; writes("\\nwbegincode{", tab(0), "}") + <<reset cross-reference info>> } else +if ="@end code " then { <<dump pending cross-reference info>> + code := &null ; writes("\\nwendcode{}") + lastdefnlabel := pendingprev := pendingnext := &null } else +<<docs chunks>>= +if ="@begin docs " then { if \delay & match(0) then <<delay for [[@begin docs]]>> + else {text := 0; writes("\\nwbegindocs{",tab(0),"}")} } else +if ="@end docs " then { if \delay & match(0) then <<delay for [[@end docs]]>> + else writes("\\nwenddocs{}") } else +<<@text>>= +if ="@text " then { text +:= *(line := tab(0)) + writes(if \quoting then <<possibly indexed quoted [[line]]>> + else if \code then <<possibly indexed code [[line]]>> + else line) + lastindexref := &null } else +<<possibly indexed quoted [[line]]>>= +if /noindex & \lastindexref then + "\\nwlinkedidentq{" || TeXliteral(line) || "}{" || lastindexref || "}" +else + TeXliteral(line) +<<possibly indexed code [[line]]>>= +if /noindex & \lastindexref then + "\\nwlinkedidentc{" || escape(line, '{}\\') || "}{" || lastindexref || "}" +else + escape(line, '{}\\') + +<<@nl>>= +if ="@nl" & pos(0) then { if /code then {<<print [[\nwdocspar]] if no [[text]]>>} + if \quoting then writes("\\nwnewline") + <<show definitions and uses on definition line---once>> + write() } else +@ +Delaying markup is handled by special patterns for the first document chunk. +Because several {\tt noweb} files can be marked up at once, there can be +several document chunks numbered 0. +The later ones are given no special treatment by the simple expedient of +turning [[delay]] off after the first one. +<<delay for [[@begin docs]]>>= +&null +<<delay for [[@end docs]]>>= +{ writes("\\nwfilename{", filename, "}"); delay := &null } +@ +<<print [[\nwdocspar]] if no [[text]]>>= +if text = 0 then writes("\\nwdocspar") +text := 1 +@ +\subsection{Chunk definitions and uses, with possible cross-reference} +Here we start to see the cross-reference markup, driven by [[lastxreflabel]] +and [[lastxrefref]]. +<<@defn>>= +if ="@defn " then { writes("\\sublabel{", \lastxreflabel, "}") + writes("\\nwmargintag{", label2tag(\lastxreflabel), "}") + writes("\\moddef{", convquotes(thischunk := tab(0)), + ("~" || label2tag(\lastxrefref)) | "", + "}\\", defns[thischunk], "endmoddef") + useitems := \useitemstab[thischunk] + pendinguses := 1 + lastdefnlabel := lastxreflabel + <<clear [[lastxref*]]>> + defns[thischunk] := "plus" } else +@ [[useitemstab]] enables us to show uses even on later instances of a chunk, +although the index filter only provides uses with the first chunk. +<<initialization>>= +useitemstab := table() +<<@use>>= +if ="@use " then { writes("\\LA{}", convquotes(name := tab(0)), + ("~" || label2tag(\lastxrefref)) | "", + "\\RA{}") + <<clear [[lastxref*]]>> } else +<<*>>= +procedure label2tag(label) + return "{\\nwtagstyle{}\\subpageref{" || label || "}}" +end +@ +[[defns]] serves only to give the proper distinction between +[[\endmoddef]] and [[\plusendmoddef]]. +<<initialization>>= +defns := table("") +@ +\subsection{Quoting, headers, trailers, \& miscellany} +<<others>>= +if ="@quote" & pos(0) then { quoting := 1 ; writes("{\\Tt{}") } else +if ="@endquote" & pos(0) then { quoting := &null ; writes("\\nwendquote}") } else +if ="@file " then { filename := tab(0); <<clear [[lastxref*]]>> + \delay | writes("\\nwfilename{", filename, "}") + } else +if ="@line " then { } else +if ="@literal " then { writes(tab(0)) } else +if ="@header latex " then { <<write {\LaTeX} header>> } else +if ="@header tex " then { writes("\\input nwmac ") } else +if ="@trailer latex" & pos(0) then { write("\\end{document}") } else +if ="@trailer tex" & pos(0) then { write("\\bye") } else +<<write {\LaTeX} header>>= +writes("\\documentclass{article}\\usepackage{noweb}\\pagestyle{noweb}\\noweboptions{", tab(0), + "}\\begin{document}") +@ +\subsection{Cross-reference and index support} + +\subsubsection{Chunk cross-reference} +We begin with basic cross-reference [[@xref label]] and [[@xref ref]], +then show the chunk cross-reference that comes at the end of a code chunk. +The {\LaTeX} back end ignores [[@xref nextdef]] and [[@xref prevdef]]. +<<@xref>>= +if ="@xref " then { <<@xref tests>> <<bad @xref>> } else +<<reset cross-reference info>>= +every defitems | useitems := [] +notused := &null +@ +By resetting the cross-reference info after dumping, +we make it possible to dump both before index stuff and before +end of chunk, without having to do any checking. +<<dump pending cross-reference info>>= +dumpitems(defitems, "nwalsodefined") +dumpitems(useitems, "nwused") +writes("\\nwnotused{", \notused, "}") +<<reset cross-reference info>> +<<@xref tests>>= +if ="label " then { lastxreflabel := tab(0) } else +if ="ref " then { lastxrefref := tab(0) } else +if ="begindefs" & pos(0) then { } else +if ="defitem " then { put(defitems, tab(0)) } else +if ="enddefs" & pos(0) then { } else +if ="beginuses" & pos(0) then { useitems := [] } else +if ="useitem " then { put(useitems, tab(0)) } else +if ="enduses" & pos(0) then { useitemstab[thischunk] := useitems } else +if ="notused " then { notused := tab(0) } else +if ="nextdef " then { pendingnext := tab(0) } else +if ="prevdef " then { pendingprev := tab(0) } else +<<show definitions and uses on definition line---once>>= +if \pendinguses | \pendingprev | \pendingnext then { + writes("\\nwstartdeflinemarkup") + <<write out definition-line markup>> + writes("\\nwenddeflinemarkup") + pendinguses := pendingprev := pendingnext := &null +} +<<write out definition-line markup>>= +if \pendinguses then { + dumpitems(useitems, "nwusesondefline") +} +if \pendingprev | \pendingnext then { + writes("\\nwprevnextdefs{", \pendingprev | "\\relax", "}{", + \pendingnext | "\\relax", "}") +} +<<clear [[lastxref*]]>>= +every lastxreflabel | lastxrefref := &null +<<bad @xref>>= +warn_unknown("xref " || tab(upto(' \t') | 0)) +<<*>>= +procedure dumpitems(items, cs) + if *\items > 0 then { + writes("\\", cs, "{") + every writes("\\\\{", !items, "}") + writes("}") + return + } else + fail +end +@ +\subsubsection{Identifier cross-reference, i.e.\ index} +<<@index>>= +if ="@index " then { <<@index tests>> <<bad @index>> } else +<<bad @index>>= +warn_unknown("index " || tab(upto(' \t') | 0)) +@ +This first round of stuff just handles the basics: definitions, uses, and newlines. +Unless it's OK to show the index (by \LA{}SI\RA), we handle nothing but +\hbox{[[@index nl]]}. +<<@index tests>>= +if ="nl" & pos(0) then { write(if \code then "\\eatline" else "%")}else +if =("defn "|"localdefn ") then <<SI>> { <<handle index defn>> } else +if ="use " then <<SI>> { <<handle index use>> } else +<<SI>>= +/noindex & +@ +Nothing is involved in handling definitions and uses unless there are cross-reference +labels pending. +An index definition or use has its own [[@xref label]] only if it's in documentation; +if it's in code, we use the anchor label of the definition. +(You don't have to know that to understand what happens here, but I thought you +might like to.) +<<handle index defn>>= +writes("\\nosublabel{", \lastxreflabel, "}") +writes("\\nwindexdefn{\\nwixident{", TeXliteral(name := tab(0)), "}}{", indexlabel(name), "}{", + \lastxrefref, "}") +<<clear [[lastxref*]]>> +@ +The {\LaTeX} back end ignores uses in code; they get bundled up by a previous filter +(the cross-referencer) and handled elsewhere. +<<handle index use>>= +if /code & /quoting then { + writes("\\protect\\nosublabel{", \lastxreflabel, "}") + writes("\\protect\\nwindexuse{\\nwixident{", TeXliteral(name := tab(0)), "}}{", + indexlabel(name), "}{", \lastxrefref, "}") +} +lastindexref := lastxrefref +<<clear [[lastxref*]]>> +@ +Here's the local identifier cross-reference that appears at the end of a code chunk. +We guard everything with \LA{}SI\RA, as before. +<<@index tests>>= +if ="begindefs" & pos(0) then <<SI>> { <<dump pending cross-reference info>> + writes("\\nwidentdefs{") } else +if ="isused " then <<SI>> { "handled by latex" } else +if ="defitem " then <<SI>> { i := tab(0); <<write [[i]] with [[\\]]>> } else +if ="enddefs" & pos(0) then <<SI>> { writes("}") } else +if ="beginuses" & pos(0) then <<SI>> { <<dump pending cross-reference info>> + writes("\\nwidentuses{"); ulist := [] } else +if ="isdefined " then <<SI>> { "latex finds the definitions" } else +if ="useitem " then <<SI>> { i := tab(0); <<write [[i]] with [[\\]]>> + put(ulist, i); } else +if ="enduses" & pos(0) then <<SI>> { writes("}"); <<write [[ulist]]>> } else +<<write [[i]] with [[\\]]>>= +writes("\\\\{{\\nwixident{", TeXliteral(i), "}}{", indexlabel(i), "}}") +<<write [[ulist]]>>= +every i := !ulist do + writes("\\nwindexuse{\\nwixident{", TeXliteral(i), "}}{", indexlabel(i), "}{", \lastdefnlabel, "}") +@ +\subsubsection{The list of chunks and the index} +The treatments of the list of chunks and the index are similar. +Both use [[\nwixlogsorted]], which writes magic goo into the {\tt .aux} file. +The real cross-referencing is done by the underlying {\LaTeX} code. +<<@xref tests>>= +if ="beginchunks" & pos(0) then { } else +if ="chunkbegin " then { label := tab(upto(' ')); =" " + writes("\\nwixlogsorted{c}{{", convquotes(tab(0)), + "}{", label, "}{") } else +if ="chunkuse " then { writes("\\nwixu{", tab(0), "}") } else +if ="chunkdefn " then { writes("\\nwixd{", tab(0), "}") } else +if ="chunkend" & pos(0) then { write("}}%") } else +if ="endchunks" & pos(0) then { } else +<<@index tests>>= +if ="beginindex" & pos(0) then <<SI>> { } else +if ="entrybegin " then <<SI>> { label := tab(upto(' ')); =" "; name := tab(0) + write("\\nwixlogsorted{i}{{\\nwixident{", TeXliteral(name), + "}}{", indexlabel(name), "}}%") } else +if ="entryuse " then <<SI>> { "handled by latex" } else +if ="entrydefn " then <<SI>> { "handled by latex" } else +if ="entryend" & pos(0) then <<SI>> { } else +if ="endindex" & pos(0) then <<SI>> { } else +@ +\subsection{HEader comments} +This godawful hack slips in a comment without messing up our line +numbers. +<<*>>= +procedure firstwrite(L[]) + write := realwrite + put(L, + "% ===> this file was generated automatically by noweave --- better not edit it") + return write!L +end +@ +\subsection{Utility procedures} +<<*>>= +procedure escape(line, chars, prefix) + /prefix := "\\" + line ? { + s := "" + while s ||:= tab(upto(chars)) do s ||:= prefix || move(1) + return s || tab(0) + } +end +<<*>>= +global TeXspecials +<<initialization>>= +TeXspecials := '\\{}$&#^_ ~%' +@ +I can't use [[\\char`\%]] and similar sequences with latex2e, because +[[`]] is an active character that suppresses ligatures. So I got TeX to print out the +actual character codes for me. +This string ([['\\{}$&#^_ ~%']]) should serve as a test. + +Unfortunately, the character codes for these characters are not the +same across fonts. While this is not a problem when everything is in +a typewriter font, it can cause major strangenesses when another font +is used (Icon, for example, is often typeset in a sans-serif font). +Codes for `[[$]]', `[[&]]', `[[#]]', `[[^]]', `[[_]]', `[[~]]' and +`[[%]]' use +font-independent macros. Since there is no font-independent way to +typeset `[[\]]', `[[{]]' and `[[}]]',\footnote{Although both plain +\TeX\ and \LaTeX\ provide `[[\{]]' and `[[\}]], plain \TeX\ only +allows their use in math mode.} +we generate special macros defined in \verb"noweb.sty" and +\verb"nwmac.tex". The default definitions are geared to typewriter +fonts, but they can be redefined as necessary. +<<*>>= +procedure TeXliteral(arg) + static nospace, code + initial { codes := ["\\", "nwbackslash", + "{", "nwlbrace", + "}", "nwrbrace", + "$", "$", + "&", "&", + "#", "#", + "^", "char94", + "_", "_", + "%", "%", + "~", "char126"] + code := table() + while (c := get(codes), n := get(codes)) do code[c] := string(n) + if c := !TeXspecials & c ~== " " & not member(code, c) then + fatal("internal error, character-code mismatch, report a bug!") + } + s := "" + arg ? { + while s ||:= tab(upto(TeXspecials)) do { + c := move(1) + if member(code, c) then + s ||:= "{\\" || code[c] || "}" + else + s ||:= "\\" || c + } + return s || tab(0) + } +end +@ +A special function is used to implement {\tt noweb}'s quoting +convention within chunk names. +<<*>>= +procedure convquotes(s) + r := "" + s ? { + while r ||:= tab(find("[[")) do { + ="[[" | fatal("impossible missing [[") + r ||:= "\\code{}" || TeXliteral(tab(find("]]"))) + r ||:= tab(many(']')-2) + ="]]" | fatal("impossible missing ]]") + r ||:= "\\edoc{}" + } + return r || tab(0) + } +end +<<*>>= +procedure warn_unknown(tag) + static warned + initial warned := set() + if not member(warned, tag) then { + write(&errout, "Warning: unrecognized escape @", tag, " ", tab(0)) + insert(warned, tag) + } + return +end +@ +This gets special characters out of the labels used by identifiers. +<<*>>= +procedure indexlabel(ident) + static badset, trans + initial { + <<initialize [[trans]]>> + badset := '' + every badset ++:= key(trans) + } + ident ? { + s := "" + while s ||:= tab(upto(badset)) do s ||:= ":" || trans[move(1)] + return s || tab(0) + } +end +<<initialize [[trans]]>>= +trans := table() +trans[" "] := "sp" # space +trans["#"] := "has" # hash +trans["$"] := "do" # dollar +trans["%"] := "pe" # percent +trans["&"] := "am" # ampersand +trans[","] := "com" # commad +trans[":"] := "col" # colon +trans["\\"] := "bs" # backslash +trans["^"] := "hat" # hat +trans["_"] := "un" # underscore +trans["{"] := "lb" # left brace +trans["}"] := "rb" # right brace +trans["~"] := "ti" # tilde +<<*>>= +procedure fatal(L[]) + write!([&errout, "noweb error in tohtml: "] ||| L) + exit(1) +end + diff --git a/web/noweb/src/icon/xchunks.icn b/web/noweb/src/icon/xchunks.icn new file mode 100644 index 0000000000..cf1a74b20e --- /dev/null +++ b/web/noweb/src/icon/xchunks.icn @@ -0,0 +1,142 @@ +#line 31 "xchunks.nw" +record chunk(lines, type) + # lines is all the lines of the chunk + # type is "code" or "docs" or "unknown" + +procedure main(args) + local merge, summary + while args[1][1] == "-" do + case args[1] of { + "-merge" : merge := get(args) + "-summary" : { get(args); summary := " (summary)" } + default : break + } + *args > 0 | stop("xchunks needs an external database") + + +#line 136 "xchunks.nw" +db := "markup" +every db ||:= " " || !args +db := open(db, "rp") | stop("cannot pipe from ", image(db)) + +#line 47 "xchunks.nw" + dbchunks := readchunks(db) + defns := defnstable(dbchunks) + # defns maps name to list of code chunks defining that name + if \merge then + every merge_chunks(!defns) + if \summary then + every add_defn_string(!defns, summary) + + thisdoc := readchunks(&input) + every c := !thisdoc do + if c.type ~== "code" | notEmpty(c) | /defns[chunkName(c)] then + every write(!c.lines) + else { + write("@crc_prefix on") + every write(!(!defns[chunkName(c)]).lines) + write("@crc_prefix off") + } +end +#line 69 "xchunks.nw" +procedure readchunks(infile) + local chunks, c + local file, lnum, lines, startline + chunks := [] + lnum := 1 + while line := read(infile) do + line ? + if ="@file " then { + file := tab(0) + lnum := 1 + } else if ="@line " then { + lnum := integer(tab(0)) + } else if ="@begin " then { + lines := [] + put(lines, "@file " || \file) + put(lines, "@line " || lnum) + put(lines, line) + startline := line + c := chunk(lines, tab(upto(' ')|0)) + line := read(infile) | stop("unmatched ", startline) + while line ? not ="@end " do { + put(c.lines, line) + line := read(infile) | stop("unmatched ", startline) + line ? + if ="@file " then { file := tab(0); lnum := 1 } + else if ="@line " then { lnum := integer(tab(0)) } + else if =("@nl"|"@index nl") & pos(0) then { lnum := lnum + 1 } + } + put(c.lines, line) + put(chunks, c) + } else { + put(chunks, chunk([line], "unknown")) + line ? + if ="@file " then { file := tab(0); lnum := 1 } + else if ="@line " then { lnum := integer(tab(0)) } + else if =("@nl"|"@index nl") & pos(0) then { lnum := lnum + 1 } + } + return chunks +end +#line 111 "xchunks.nw" +procedure defnstable(chunks) + t := table() + every c := !chunks & c.type == "code" do { + /t[chunkName(c)] := [] + put(t[chunkName(c)], c) + } + return t +end +#line 124 "xchunks.nw" +procedure chunkName(c) + return (!c.lines ? (="@defn " & tab(0))) | stop("no @defn in code chunk") +end + +procedure notEmpty(c) + static nonwhite + initial nonwhite := ~' \t' + return !c.lines ? ="@use " | (="@text ", upto(nonwhite)) +end +#line 142 "xchunks.nw" +procedure merge_chunks(chunks) + while *chunks > 1 do { + c := get(chunks) + strip_code_tail(c) + strip_code_head(chunks[1]) + every put(c.lines, !chunks[1].lines) + chunks[1] := c + } + return chunks +end +#line 154 "xchunks.nw" +procedure strip_code_tail(c) + if c.lines[-1] ? match("@end code") then + pull(c.lines) + else + stop("Unexpected line") + return c +end + +procedure strip_code_head(c) + local pfx + pfx := [] + if match("@begin code", c.lines[1]) then + get(c.lines) + else + stop("bad line " || c.lines[1]) + while not match("@defn ", c.lines[1]) do + push(pfx, get(c.lines)) + get(c.lines) | stop("this can't happen") + while c.lines[1] ~== "@nl" do + push(pfx, get(c.lines)) + get(c.lines) | stop("this can't happen") + every push(c.lines, !pfx) + return c +end +#line 180 "xchunks.nw" +procedure add_defn_string(chunks, s) + every c := !chunks & i := 1 to *c.lines do + if match("@defn ", c.lines[i]) then + c.lines[i] ||:= s + return chunks +end diff --git a/web/noweb/src/icon/xchunks.nw b/web/noweb/src/icon/xchunks.nw new file mode 100644 index 0000000000..b3ef8fd98d --- /dev/null +++ b/web/noweb/src/icon/xchunks.nw @@ -0,0 +1,186 @@ +% -*- mode: Noweb; noweb-code-mode: icon-mode -*- + +\section{Getting chunks from an external ``dictionary''} + +Mike Smith wants to do an unweblike thing: keep his code and +documentation in separate source files. +I~think he wants to do this so he can enable the \emph{same} code +chunks to appear in \emph{multiple} documents, with guaranteed +consistency, but I~suppose the reasons don't matter. +In any case, he wants syntax for referring to a code chunk from an +external database, and for displaying the definition(s) of that chunk +exactly where he specifies. + +I~suggested solving the problem with a Noweb filter. +He can refer to chunks using the approved source syntax: +\texttt{[{}[<{}<}\emph{chunk name}\texttt{>{}>]{}]}. +To pull in the definitions from the external database, I suggested he +provide an empty definition. +Since such a definition can be written in one line (two lines if the +definition is followed by a documentation chunk), he can pull in many +definitions concisely. + +This program is the noweb filter that does the job. +Named ``\texttt{xchunks},'' short for ``external chunks,'' it pulls in +definitions from an external database. +The external database is itself simply a list of Noweb files. +It's a bit more liberal than defined above; a chunk is considered +empty if its definition contains at most whitespace. + +A late-breaking bug: we need to resynchronize line numbers. +<<*>>= +record chunk(lines, type) + # lines is all the lines of the chunk + # type is "code" or "docs" or "unknown" + +procedure main(args) + local merge, summary + while args[1][1] == "-" do + case args[1] of { + "-merge" : merge := get(args) + "-summary" : { get(args); summary := " (summary)" } + default : break + } + *args > 0 | stop("xchunks needs an external database") + + <<let [[db]] be an open file giving the database pipeline from [[args]]>> + + dbchunks := readchunks(db) + defns := defnstable(dbchunks) + # defns maps name to list of code chunks defining that name + if \merge then + every merge_chunks(!defns) + if \summary then + every add_defn_string(!defns, summary) + + thisdoc := readchunks(&input) + every c := !thisdoc do + if c.type ~== "code" | notEmpty(c) | /defns[chunkName(c)] then + every write(!c.lines) + else { + write("@crc_prefix on") + every write(!(!defns[chunkName(c)]).lines) + write("@crc_prefix off") + } +end +@ +Reading chunks is fairly straightforward; we look for begin and end. +The loop logic is a little less pleasant than I would like. +<<*>>= +procedure readchunks(infile) + local chunks, c + local file, lnum, lines, startline + chunks := [] + lnum := 1 + while line := read(infile) do + line ? + if ="@file " then { + file := tab(0) + lnum := 1 + } else if ="@line " then { + lnum := integer(tab(0)) + } else if ="@begin " then { + lines := [] + put(lines, "@file " || \file) + put(lines, "@line " || lnum) + put(lines, line) + startline := line + c := chunk(lines, tab(upto(' ')|0)) + line := read(infile) | stop("unmatched ", startline) + while line ? not ="@end " do { + put(c.lines, line) + line := read(infile) | stop("unmatched ", startline) + line ? + if ="@file " then { file := tab(0); lnum := 1 } + else if ="@line " then { lnum := integer(tab(0)) } + else if =("@nl"|"@index nl") & pos(0) then { lnum := lnum + 1 } + } + put(c.lines, line) + put(chunks, c) + } else { + put(chunks, chunk([line], "unknown")) + line ? + if ="@file " then { file := tab(0); lnum := 1 } + else if ="@line " then { lnum := integer(tab(0)) } + else if =("@nl"|"@index nl") & pos(0) then { lnum := lnum + 1 } + } + return chunks +end +@ +Building the definitions table is easy. +<<*>>= +procedure defnstable(chunks) + t := table() + every c := !chunks & c.type == "code" do { + /t[chunkName(c)] := [] + put(t[chunkName(c)], c) + } + return t +end +@ +The basic functions [[chunkName]] and [[notEmpty]] require search. +You could imagine caching these, but since they're called twice and at +most once per chunk, respectively, I doubt it's worth it. +<<*>>= +procedure chunkName(c) + return (!c.lines ? (="@defn " & tab(0))) | stop("no @defn in code chunk") +end + +procedure notEmpty(c) + static nonwhite + initial nonwhite := ~' \t' + return !c.lines ? ="@use " | (="@text ", upto(nonwhite)) +end +@ +Finally, here's how we get the database. +<<let [[db]] be an open file giving the database pipeline from [[args]]>>= +db := "markup" +every db ||:= " " || !args +db := open(db, "rp") | stop("cannot pipe from ", image(db)) +@ +To merge adjacent definitions, we strip leading and trailing markup. +<<*>>= +procedure merge_chunks(chunks) + while *chunks > 1 do { + c := get(chunks) + strip_code_tail(c) + strip_code_head(chunks[1]) + every put(c.lines, !chunks[1].lines) + chunks[1] := c + } + return chunks +end +@ +<<*>>= +procedure strip_code_tail(c) + if c.lines[-1] ? match("@end code") then + pull(c.lines) + else + stop("Unexpected line") + return c +end + +procedure strip_code_head(c) + local pfx + pfx := [] + if match("@begin code", c.lines[1]) then + get(c.lines) + else + stop("bad line " || c.lines[1]) + while not match("@defn ", c.lines[1]) do + push(pfx, get(c.lines)) + get(c.lines) | stop("this can't happen") + while c.lines[1] ~== "@nl" do + push(pfx, get(c.lines)) + get(c.lines) | stop("this can't happen") + every push(c.lines, !pfx) + return c +end +@ +<<*>>= +procedure add_defn_string(chunks, s) + every c := !chunks & i := 1 to *c.lines do + if match("@defn ", c.lines[i]) then + c.lines[i] ||:= s + return chunks +end diff --git a/web/noweb/src/icon/yaccdefs.icn b/web/noweb/src/icon/yaccdefs.icn new file mode 100644 index 0000000000..e0313651e8 --- /dev/null +++ b/web/noweb/src/icon/yaccdefs.icn @@ -0,0 +1,67 @@ +#line 2 "yaccdefs.nw" +procedure rcsinfo () + return "$Id: yaccdefs.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + go() +end + +procedure postpass(name, arg) + static kind + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if id := tab(many(&letters)) then { + tab(many(' \t')) + if =":" & any(&letters ++ ' ([{') then writedefn(id) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end +#line 1 "defns.nw" +procedure go() + local line + while line := read() do { + apply(prepass, line) + write(line) + if match("@fatal ", line) then exit(1) + apply(postpass, line) + } +end + +procedure apply(pass, line) + line ? (="@" & pass(tab(upto(' ')|0), if =" " then tab(0) else &null)) +end +#line 18 "defns.nw" +procedure writedefn(defn, locl) + static indextext + initial indextext := "" + if /defn then + *indextext > 0 & +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 23 "defns.nw" + else { + if *indextext + *defn > 65 then +#line 31 "defns.nw" +{ # write("@index nl") # don't! + indextext := "" +} +#line 25 "defns.nw" + write(if \locl then "@index localdefn " else "@index defn ", defn) + indextext ||:= " " || defn + } + return +end +#line 35 "defns.nw" +procedure rcsinfo_too () + return "$Id: defns.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end diff --git a/web/noweb/src/icon/yaccdefs.nw b/web/noweb/src/icon/yaccdefs.nw new file mode 100644 index 0000000000..596090cff5 --- /dev/null +++ b/web/noweb/src/icon/yaccdefs.nw @@ -0,0 +1,26 @@ +\section{Finding {\tt yacc} definitions} +<<*>>= +procedure rcsinfo () + return "$Id: yaccdefs.nw,v 1.18 2008/10/06 01:03:05 nr Exp nr $" || + "$Name: v2_12 $" +end +procedure main(args) + go() +end + +procedure postpass(name, arg) + static kind + case name of { + "begin" : arg ? kind := tab(upto(' ')|0) + "text" : if kind == "code" then arg ? + if id := tab(many(&letters)) then { + tab(many(' \t')) + if =":" & any(&letters ++ ' ([{') then writedefn(id) + } + } + return +end + +procedure prepass(name, arg) + if name == "end" then writedefn(&null) # force newline +end diff --git a/web/noweb/src/lib/Makefile b/web/noweb/src/lib/Makefile new file mode 100644 index 0000000000..47cb6f40fe --- /dev/null +++ b/web/noweb/src/lib/Makefile @@ -0,0 +1,38 @@ +# Copyright 1993 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +LIB=/dev/null +SHELL=/bin/sh +CINAME= +CIMSG= +RCSFILES=emptydefn toascii.nw unmarkup Makefile nwmtime pipedocs h2a btdefn + +all: toascii + chmod +x unmarkup emptydefn toascii nwmtime pipedocs h2a btdefn + +install: all + cp unmarkup emptydefn nwmtime btdefn $(LIB) + sed 's@|LIBDIR|@$(LIB)@g' pipedocs > $(LIB)/pipedocs + sed 's@|LIBDIR|@$(LIB)@g' toascii > $(LIB)/toascii + sed 's@|LIBDIR|@$(LIB)@g' h2a > $(LIB)/h2a + chmod +x $(LIB)/pipedocs $(LIB)/toascii $(LIB)/h2a + +uninstall: + for i in unmarkup emptydefn toascii nwmtime h2a btdefn; do rm -f $(LIB)/$$i; done + rm -f $(LIB)/pipedocs + +checkin: + ci -l $(CINAME) $(CIMSG) $(RCSFILES) + +source: toascii +touch: toascii + touch toascii +boot: + touch toascii + +toascii: toascii.nw + notangle -Rtoascii toascii.nw > toascii + +clean: ; rm -f *.log *.blg *.dvi *.toc *.aux *.tex *~ +clobber: clean + rm -f toascii diff --git a/web/noweb/src/lib/btdefn b/web/noweb/src/lib/btdefn new file mode 100755 index 0000000000..a7b2d1678d --- /dev/null +++ b/web/noweb/src/lib/btdefn @@ -0,0 +1,16 @@ +#!/bin/sh + +# turn backticks into definitions + +nawk ' +/^@begin code / { code = 1 } +/^@end code / { code = 0 } +code && /^@text .*".*`.*"/ { print; next } +code && /^@text .*`/ { + while (i = match($0, /`[a-zA-Z_][a-zA-Z0-9_]*/)) { + id = substr($0, i+1, RLENGTH-1) + sub(/`[a-zA-Z_][a-zA-Z0-9_]*/, id) + print "@index defn " id + } +} +{print}' diff --git a/web/noweb/src/lib/emptydefn b/web/noweb/src/lib/emptydefn new file mode 100755 index 0000000000..29499b147b --- /dev/null +++ b/web/noweb/src/lib/emptydefn @@ -0,0 +1,9 @@ +#!/bin/sh +# +# notangle filter that makes the definition of an empty chunk <<>>= +# stand for a continuation of the previous chunk definition. + +nawk 'BEGIN { lastdefn = "@defn " } +/^@defn $/ { print lastdefn; next } +/^@defn / { lastdefn = $0 } +{ print }' "$@" diff --git a/web/noweb/src/lib/h2a b/web/noweb/src/lib/h2a new file mode 100755 index 0000000000..9aec5dc416 --- /dev/null +++ b/web/noweb/src/lib/h2a @@ -0,0 +1,17 @@ +#!/bin/sh + +LIB=|LIBDIR| +opts="-nolist" + +case $1 in + -[0-9]*) opts="$opts -width=`expr 0 - $1`" ; shift ;; +esac + +html=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +trap 'rm -f $html; exit 1' 1 2 15 # clean up files + +awk ' +/^ *$/ { if (NR == 1) { print ; next } } +{ print > "'"$html"'" }' +if [ -r $html ]; then lynx -dump $opts $html | sed '1d'; fi +rm -f $html diff --git a/web/noweb/src/lib/noxref.nw b/web/noweb/src/lib/noxref.nw new file mode 100644 index 0000000000..3eec706bc7 --- /dev/null +++ b/web/noweb/src/lib/noxref.nw @@ -0,0 +1,215 @@ +\section{Cross-referencing tool} +This filter adds labels and cross-references to chunk definitions and uses, +and it converts the line [[\nowebchunks]] into an alphabetical list of chunks. + +Following the lead of J. G. Krom, I make it possible to generate the +file [[noxref]] for the Unix environment, and the files [[noxref.bat]] +and [[noxref.awk]] for the MS-DOS environment. + +<<*>>= +#!/bin/sh +nawk '<<cross-reference awk program>>' +<<noxref.awk>>= +<<cross-reference awk program>> +<<noxref.bat>>= +@echo off +REM The NOWEB environment variable must be set to the directory +REM where NOXREF.AWK is. It must end in '/' or '\' as required +REM by the AWK interpreter in use. +awk -f %NOWEB%noxref.awk +@ +The function [[modid]] turns a chunk name into a unique key acceptable +for use as a {\LaTeX} label. +The first step maps the name into a 6-character key; the second step +appends an integer (from [[keycounts]]) used to distinguish different +chunk names that map to the same key. +[[idtable]] is used to cache the results so the computation is +performed just once per chunk name. +<<function definitions>>= +function modid(name, key) { + if (idtable[name] == "") { + key = name + gsub(/[\[\]\\{} `#%&~_^-]/, "*", key) # ditch special chars + if (length(key) > 6) key = substr(key,1,3) substr(key, length(key)-2, 3) + keycounts[key] = keycounts[key] + 1 + if (keycounts[key] > 1) + idtable[name] = key "-" keycounts[key] + else + idtable[name] = key + } + return idtable[name] +} +<<initialization>>= +idtable[0] = 0 ; keycounts[0] = 0 +@ +The complete program runs in two passes. +The first pass saves all the input and builds up list of definitions +and uses. +The second pass writes out all the lines with suitable +cross-referencing markup. +The first pass saves information in the following tables, which are +indexed by chunk name. +\begin{itemize} +\item[\tt defns] Lists labels of all defining instances. +\item[\tt firstdef] Label of the first defining instance. +\item[\tt dcounts] Counts the number of defining instances. +\item[\tt uses] Lists labels of all uses. +\item[\tt dcounts] Counts the number of uses. +\end{itemize} +<<initialization>>= +defns[0] = 0 ; firstdef[0] = 0 ; dcounts[0] = 0 ; uses[0] = 0 ; ucounts[0] = 0 +@ The first pass logs both definitions ([[which == "DEFN"]]) and uses +([[which == "USE"]]) by calling [[logname]]. +<<function definitions>>= +function logname(which, tbl, counts, name, id, label) { + counts[name] = counts[name] + 1 + id = which ":" curfile ":" modid(name) "-" counts[name] + tbl[name] = tbl[name] id " " + if (which == "DEFN" && counts[name] == 1) label = "sublabel" + else label = "label" + lines[nextline++] = "@literal \\" label "{" id "}" + if (which == "DEFN" && firstdef[name] == "") firstdef[name] = id +} +@ +The structure of the full program, showing both passes, is: +<<cross-reference awk program>>= +<<function definitions>> +BEGIN { <<initialization>> } +{ lines[nextline++] = $0 } +/^@defn / { logname("DEFN", defns, dcounts, substr($0, 7)) } +/^@use / { logname("USE", uses, ucounts, substr($0, 6)) } +/^@file / { curfile = modid(substr($0, 7) substr($0, 10, 3)) } +END { <<write out the program with cross-reference info>> } +@ +The second pass just copies lines to its output, except for +definitions, uses and the [[\nowebchunks]] control sequence. +It also puts a bunch of cross-reference information at the end of each +code chunk. +<<write out the program with cross-reference info>>= +for (i=0; i < nextline; i++) { + name = substr(lines[i], 2) + if (index(name, " ")) { + name = substr(name, 1, index(name, " ")-1) + arg = substr(lines[i], length(name)+3) + } else { + arg = "" + } + if (name == "defn") { + thischunk = arg + <<emit definition>> + } else if (name == "use") { + <<emit use>> + } else if (name == "begin") { + chunktype = substr(arg, 1, 4) + print lines[i] + } else if (name == "quote") { + quote = 1 + print lines[i] + } else if (name == "endquote") { + quote = 0 + print lines[i] + } else if (name == "end") { + if (chunktype == "code") { + <<emit cross-reference info for [[thischunk]]>> + } + print lines[i] + } else if (name == "text" && arg == "\\nowebchunks" && chunktype == "docs" && quote == 0) { + <<emit sorted list of chunks>> + } else { + print lines[i] + } + delete lines[i] +} +@ +\maybreak{1in} +<<emit definition>>= +printf "@defn %s~{\\footnotesize\\Rm\\subpageref{%s}}\n", arg, firstdef[arg] +<<emit use>>= +if (firstdef[arg] != "") + printf "@use %s~{\\footnotesize\\Rm\\subpageref{%s}}\n", arg, firstdef[arg] +else + printf "@use %s~{\\footnotesize\\Rm (never defined)}\n", arg +@ +Cross reference information is emitted only at the first definition of each +chunk, as tracked by [[firstdefnout]]. +<<initialization>>= +firstdefnout[0] = 0 +<<emit cross-reference info for [[thischunk]]>>= +if (firstdefnout[thischunk] == 0) { + firstdefnout[thischunk] = 1 + n = split(defns[thischunk], a) + if (n > 1) { + printf "@literal \\nwalsodefined{" + for (j = 2; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n" + } + if (uses[thischunk] != "") { + printf "@literal \\nwused{" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n" + } else + printf "@literal \\nwnotused{%s}\n", escapeTeXspecials(thischunk) +} +@ \subsection{Sorting} +Each chunk name is mapped to a sorting key. +Ideally such a key would contain only lower-case letters and spaces, +and it would discount \TeX{} control sequences, and more. +Such computations are so excruciating in awk that I content myself +with eliminating nonletters. +<<function definitions>>= +function sortkey(name, s) { + s = name + gsub(/[^a-zA-Z ]/, "", s) + return s +} +@ I maintain a two parallel lists, one containing sort keys and the other +containing chunk names. +<<function definitions>>= +function insertchunk(name, i, tmp) { + sortcount = sortcount + 1 + sortnames[sortcount] = name + sortkeys[sortcount] = sortkey(name) + i = sortcount + while (i > 1 && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sortnames[i] < sortnames[i-1])) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [i-1]; sortkeys [i-1] = tmp + tmp = sortnames[i]; sortnames[i] = sortnames[i-1]; sortnames[i-1] = tmp + i = i - 1 + } +} +@ +That's enough machinery to do the job. +<<emit sorted list of chunks>>= +if (sortcount == 0) { + <<insert all chunk names into [[sortnames]]>> +} +for (j = 1; j <= sortcount; j++) { + if (firstdef[sortnames[j]] != "") + print "@use " sortnames[j] "~{\\footnotesize\\Rm\\subpageref{" firstdef[sortnames[j]] "}}" + else + print "@use " sortnames[j] "~{\\footnotesize\\Rm (never defined)}" + if (j < sortcount) + print "@literal \\\\" +} +<<insert all chunk names into [[sortnames]]>>= +delete defns[0] +for (c in defns) insertchunk(c) +delete uses[0] +for (c in uses) if (!(c in defns)) insertchunk(c) +@ +<<function definitions>>= +function escapeTeXspecials(name, l, r) { + r = name + l = "" + while (match(r, /[{}$%&#^_~\\]/) > 0) { + l = l substr(r, 1, RSTART-1) "\\" substr(r, RSTART, 1) + r = substr(r, RSTART+1) + } + return l r +} +@ +\subsection{List of chunk names} +\nowebchunks diff --git a/web/noweb/src/lib/noxref.sh b/web/noweb/src/lib/noxref.sh new file mode 100644 index 0000000000..da09c46a55 --- /dev/null +++ b/web/noweb/src/lib/noxref.sh @@ -0,0 +1,65 @@ +#!/bin/sh +nawk 'BEGIN { defns[0] = 0 ; uses[0] = 0 ; dcounts[0] = 0 ; firstdef[0] = 0; + ucounts[0] = 0 ; idtable[0] = 0 ; keycounts[0] = 0 ; firstdefnout[0] = 0; + filetable[0] = 0 } +{ lines[nextline++] = $0 } +/^@defn / { logname("DEFN", defns, dcounts, substr($0, 7)) } +/^@use / { logname("USE", uses, ucounts, substr($0, 6)) } +/^@file / { curfile = modid(substr($0, 7)) } + +function logname(which, tbl, counts, name, id) { + counts[name] = counts[name] + 1 + id = which curfile "-" modid(name) "-" counts[name] + tbl[name] = tbl[name] id " " + lines[nextline++] = "@literal \\label{" id "}" + if (which == "DEFN" && firstdef[name] == "") firstdef[name] = id +} + +function modid(name, key) { + if (idtable[name] == "") { + key = name + gsub(/[\[\]\\{} -]/, "*", key) + if (length(key) > 6) key = substr(key,1,3) substr(key, length(key)-2, 3) + keycounts[key] = keycounts[key] + 1 + idtable[name] = key "-" keycounts[key] + } + return idtable[name] +} + +END { + for (i=0; i < nextline; i++) { + name = substr(lines[i], 2) + name = substr(name, 1, index(name, " ")-1) + arg = substr(lines[i], length(name)+3) + if (name == "defn") { + thischunk = arg + printf "@defn %s~{\\footnotesize\\rm\\pageref{%s}}\n", arg, firstdef[arg] + } else if (name == "use") { + if (firstdef[arg] != "") + printf "@use %s~{\\footnotesize\\rm\\pageref{%s}}\n", arg, firstdef[arg] + else + printf "@use %s~{\\footnotesize\\rm (never defined)}\n", arg + } else if (name == "end") { + if (substr(arg, 1, 4) == "code" && firstdefnout[thischunk] == 0) { + firstdefnout[thischunk] = 1 + n = split(defns[thischunk], a) + if (n > 1) { + printf "@literal \\alsodefined{" + for (j = 2; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n@nl\n" + } + if (uses[thischunk] != "") { + printf "@literal \\used{" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n@nl\n" + } else + printf "@literal \\notused\n@nl\n" + } + print lines[i] + } else + print lines[i] + } +}' diff --git a/web/noweb/src/lib/nwmtime b/web/noweb/src/lib/nwmtime new file mode 100755 index 0000000000..0d65b8a4b6 --- /dev/null +++ b/web/noweb/src/lib/nwmtime @@ -0,0 +1,32 @@ +#!/bin/sh +# +# nwmtime -- emit defn of modification time of first file +# + +eval 'exec perl -S $0 ${1+"$@"}' + if $running_under_some_shell; + +$first = 1; +while(<>) { + print; + if ($first && /\@file\s*(\S+)/) { + $mtime_file = $1; + $first = 0; + } +} + +if (!$first) { + my $filename = $mtime_file; + my $mtime; + + # localtime if you really want local time... + $mtime = gmtime((stat $filename)[9]); + print <<EOF; +\@begin code 000 +\@defn modification time of first \@file +\@nl +\@text $mtime +\@nl +\@end code 000 +EOF +} diff --git a/web/noweb/src/lib/pipedocs b/web/noweb/src/lib/pipedocs new file mode 100755 index 0000000000..4205475a4f --- /dev/null +++ b/web/noweb/src/lib/pipedocs @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +nawk ' +BEGIN { + rcsid = "$I$" + rcsname = "$Name $" + cmd = "sed '"'"'/^@begin docs /s/[0-9][0-9]*/0/'"'"' | |LIBDIR|/unmarkup | '"$*"' | |LIBDIR|/markup | grep -v '"'"'^@file $'"'"'" +} +/^@begin docs / { piping = 1 } +{ if (piping) + print | cmd + else + print +} +/^@end docs / { + piping = 0 + close(cmd) +}' + + diff --git a/web/noweb/src/lib/toascii b/web/noweb/src/lib/toascii new file mode 100755 index 0000000000..7df1ee32fb --- /dev/null +++ b/web/noweb/src/lib/toascii @@ -0,0 +1,131 @@ +#!/bin/sh +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toascii" 1>&2 ; exit 1 ;; + esac +done +awkfile=$(mktemp) +textfile=$(mktemp) +tagsfile=$(mktemp) +export awkfile textfile tagsfile +trap 'rm -f $awkfile $textfile $tagsfile' 0 1 2 10 14 15 +nawk 'BEGIN { textfile=ENVIRON["textfile"] + tagsfile=ENVIRON["tagsfile"] } + /^@begin code/ { ++secno } + /^@xref label/ { print $3, secno >tagsfile } + /^@((begin|end) (docs|code))/ { print >textfile } + /^@(text|nl|defn|use)/ { print >textfile } + /^@xref (ref|notused)/ { print >textfile } + /^@xref (begin|end)(defs|uses)/ { print >textfile } + /^@xref (def|use)item/ { print >textfile} + /^@xref ((begin|end)chunks)|(chunk(begin|use|defn|end))/ { print >textfile } + /^@index (begin|end)(defs|uses)/ { print >textfile } + /^@index (is(us|defin)ed)|((def|use)item)/ { print >textfile } + /^@index ((begin|end)index)|(entry(begin|use|defn|end))/ { print >textfile }' +nawk 'BEGIN { + textfile = ENVIRON["textfile"] + tagsfile = ENVIRON["tagsfile"] + tfmt="detex | fmt -79" + cfmt="expand -4 | fold -75 | sed \"s/^/ /\"" + xfmt="fold -75 | sed \"s/^/ /\"" + zfmt="cat" + while (getline <tagsfile > 0) + tag[$1] = $2 + close(tagsfile) + } + /^@begin docs/ { out = tfmt } + /^@end docs/ { close(out) } + /^@begin code/ { out = cfmt; code = 1; ++secno } + /^@end code/ { endcode(); close(out); printf "\n" } + /^@text/ { printf "%s", substr($0, 7) | out } + /^@nl/ { # printf "(->%s)", formatname(out) | out ; + printf "\n" | out } + /^@xref ref/ { lastxrefref = tag[substr($0, 11)] } + /^@defn/ { name = convquote(substr($0, 7)) + printf "\n### %d ### %s%s=", + secno, chunkname(name, lastxrefref), defn[name] + defn[name] = "+" } + /^@use/ { name = convquote(substr($0, 6)) + printf "%s", chunkname(name, lastxrefref) | out } + /^@xref begindefs/ { endcode() + printf "This definition continued in" | out } + /^@xref beginuses/ { endcode() + printf "This code used in" | out } + /^@xref notused/ { endcode() + print "This code not used in this document." | out } + /^@xref (def|use)item/ { addlist(tag[$3]) } + /^@xref end(defs|uses)/ { printlist() } + $0 ~ /^@index begindefs/ && !noindex { + endcode() + print "Defines:" | out } + + $0 ~ /^@index isused/ && !noindex { + if (tag[$3] != lastxrefref) addlist(tag[$3]) } + + $0 ~ /^@index defitem/ && !noindex { + printf " %s,", $3 | out + if (nlist == 0) printf " not used in this document.\n" | out + else { printf " used in" | out; printlist() } } + $0 ~ /^@index beginuses/ && !noindex { endcode(); printf "Uses" | out } + $0 ~ /^@index isdefined/ && !noindex { lastuse = tag[$3] } + $0 ~ /^@index useitem/ && !noindex { addlist(sprintf("%s %s", $3, lastuse)) } + $0 ~ /^@index enduses/ && !noindex { printlist() } + /^@xref beginchunks/ { close(out); out = zfmt + print "List of code chunks\n" | out } + /^@xref chunkbegin/ { name = convquote(substr($0, length($3) + 19)) + printf "%s\n", chunkname(name, tag[$3]) | out } + /^@xref chunkuse/ { addlist(tag[$3]) } + /^@xref chunkdefn/ { } + /^@xref chunkend/ { if (nlist == 0) + print " Not used in this document." | out + else { printf " Used in" | out; printlist() } } + /^@xref endchunks/ { } + $0 ~ /^@index beginindex/ && !noindex { print "\nList of identifiers (defini" \ + "tion in parentheses)\n" | out } + $0 ~ /^@index entrybegin/ && !noindex { name = substr($0, length($3 + 19)) + lastdefn = tag[$3] + printf "%s: ", $4 | out } + $0 ~ /^@index entryuse/ && !noindex { addlist(tag[$3]) } + $0 ~ /^@index entrydefn/ && !noindex { } + $0 ~ /^@index entryend/ && !noindex { for (i = 1; i <= nlist; i++) + if (list[i] == lastdefn) + sub(/.*/, "(&)", list[i]) + if (nlist == 0) + print "Not used." | out + else printlist() } + $0 ~ /^@index endindex/ && !noindex { } + /^@fatal / { exit 1 } + END { + close(out) + } + function endcode() { + if (code == 1) { + code = 0 + close(out) + out = xfmt + printf "\n" | out } } + function addlist(s, i) { + for (i = 1; i <= nlist; i++) + if (s == list[i]) return + list[++nlist] = s } + + function printlist( i) { + if (nlist == 1) printf " %s.\n", list[1] | out + else if (nlist == 2) printf " %s and %s.\n", list[1], list[2] | out + else { + for (i = 1; i < nlist; i++) + printf " %s,", list[i] | out + printf " and %s.\n", list[nlist] | out } + for (i in list) delete list[i] + nlist = 0 } + function convquote(s) { gsub(/\[\[|\]\]/, "", s); return s } + function chunkname(name, number) { + if (number == 0) + return sprintf("<%s>", name) + else + return sprintf("<%s %d>", name, number) + }' noindex=$noindex $textfile +exit $? diff --git a/web/noweb/src/lib/toascii.nw b/web/noweb/src/lib/toascii.nw new file mode 100644 index 0000000000..c76a7fd2ff --- /dev/null +++ b/web/noweb/src/lib/toascii.nw @@ -0,0 +1,262 @@ +[[Toascii]] is a [[noweb]] back end for formatting text as a plain ascii file. +It was written by Phil Bewig (pbewig@netcom.com) on March 31, 1995, and +contributed to Norman Ramsey's [[noweb]] literate programming system. +@ +The main program is shown below. Option [[-delay]] is processed, for +compatibility with other back ends, but ignored; since the initial document +chunk used with [[-delay]] normally contains only [[TeX]] formatting commands +in limbo, and since those commands will be deleted before formatting, there is +no need to handle [[-delay]]. +<<toascii>>= +#!/bin/sh +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toascii" 1>&2 ; exit 1 ;; + esac +done +<<arrange temporary files>> +<<invoke first pass using parameter>> +<<invoke second pass using parameter>> +exit $? +@ %def delay noindex +[[Toascii]] uses two temporary files, one for storing the text between passes +and one for communicating the conversion of labels to tags. The files are +named here, and disposal of the file on exit from [[toascii]] is arranged. +Also arranged here is a temporary file for storage of the awk program on an +ugly system, as discussed below. +<<arrange temporary files>>= +awkfile=$(mktemp) +textfile=$(mktemp) +tagsfile=$(mktemp) +export awkfile textfile tagsfile +trap 'rm -f $awkfile $textfile $tagsfile' 0 1 2 10 14 15 +@ %def textfile tagsfile awkfile +The actual formatting of the text, code, and index entries is done by various +unix text processing commands in pipelines. There are four formatting pipes: +tfmt, which formats text, cfmt, which formats code, xfmt, which formats index +entries within the running text, and zfmt, which formats the lists of chunks +and identifiers at the end of the text. The formatters established below are +only suggestions, and may be modified to suit local taste (and the presence of +various text processing commands on the local machine!); in particular, [[c]] +programmers may want to format code with cb or indent. The sed patterns below +insert four blank spaces at the beginning of the line. +<<initialize formatters>>= +tfmt="detex | fmt -79" +cfmt="expand -4 | fold -75 | sed \"s/^/ /\"" +xfmt="fold -75 | sed \"s/^/ /\"" +zfmt="cat" +@ %def tfmt cfmt xfmt zfmt +Forgiving systems allow the awk program to be specified as a parameter to the +awk interpreter; ugly systems require that it be placed in a temporary file. +The chunks below implement both options. +<<invoke first pass using parameter>>= +nawk '<<first pass>>' +<<invoke second pass using parameter>>= +nawk '<<second pass>>' noindex=$noindex $textfile +<<invoke first pass using file>>= +cat > $awkfile << 'EOF' +<<first pass>> +EOF +nawk -f $awkfile +<<invoke second pass using file>>= +cat > $awkfile << 'EOF' +<<second pass>> +EOF +nawk -f $awkfile noindex=$noindex $textfile +@ +The first pass is responsible for extracting [[label]]s and assigning them +section numbers, which are used for cross-referencing in the second pass. The +first pass also removes from the input file those lines which are not used by +the second pass. +<<first pass>>= +BEGIN { textfile=ENVIRON["textfile"] + tagsfile=ENVIRON["tagsfile"] } +/^@begin code/ { ++secno } +/^@xref label/ { print $3, secno >tagsfile } +/^@((begin|end) (docs|code))/ { print >textfile } +/^@(text|nl|defn|use)/ { print >textfile } +/^@xref (ref|notused)/ { print >textfile } +/^@xref (begin|end)(defs|uses)/ { print >textfile } +/^@xref (def|use)item/ { print >textfile} +/^@xref ((begin|end)chunks)|(chunk(begin|use|defn|end))/ { print >textfile } +/^@index (begin|end)(defs|uses)/ { print >textfile } +/^@index (is(us|defin)ed)|((def|use)item)/ { print >textfile } +/^@index ((begin|end)index)|(entry(begin|use|defn|end))/ { print >textfile } +@ +The second pass performs formatting. After looking up the temp file names and +formatters in the environment and reading the [[tagsfile]] created in the first +pass, the second pass processes each input command in the body of the awk +[[pattern-action]] processing loop. +<<second pass>>= +BEGIN { + textfile = ENVIRON["textfile"] + tagsfile = ENVIRON["tagsfile"] + <<initialize formatters>> + while (getline <tagsfile > 0) + tag[$1] = $2 + close(tagsfile) +} +<<process [[noweb]] commands>> +/^@fatal / { exit 1 } +END { + close(out) +} +<<functions>> +@ %def tag +The rest of the program consists of a series of awk [[pattern-action]] +statements which each process a particular type of [[noweb]] pipeline command. +They are discussed in related groups, and all collected in a single chunk. We +begin with the commands that process the text of the document and code chunks. +The basic strategy is always write text to [[out]] and open and close various +pipes as needed. Variable [[code]] is true only within code chunks, and +[[secno]] numbers the sections as they appear. Function [[endcode()]] closes +the code pipeline at the end of a code section or whenever the first indexing +command appears. +<<process [[noweb]] commands>>= +/^@begin docs/ { out = tfmt } +/^@end docs/ { close(out) } +/^@begin code/ { out = cfmt; code = 1; ++secno } +/^@end code/ { endcode(); close(out); printf "\n" } +/^@text/ { printf "%s", substr($0, 7) | out } +/^@nl/ { # printf "(->%s)", formatname(out) | out ; + printf "\n" | out } +@ %def out secno code +<<functions>>= +function endcode() { + if (code == 1) { + code = 0 + close(out) + out = xfmt + printf "\n" | out } } +@ %def endcode +Definitions and uses of code chunks are handled below. Variable [[defn[name]]] +is set to a plus sign after a definition is printed, so that continuations of +the definition are properly identified. Variable [[lastxrefref]] is the tag +associated with the most-recently-seen cross-reference label, and refers to the +section number of the original definition of the code chunk. Definition lines +are printed directly, without passing through any of the formatters defined +above. +<<process [[noweb]] commands>>= +/^@xref ref/ { lastxrefref = tag[substr($0, 11)] } +/^@defn/ { name = convquote(substr($0, 7)) + printf "\n### %d ### %s%s=", + secno, chunkname(name, lastxrefref), defn[name] + defn[name] = "+" } +/^@use/ { name = convquote(substr($0, 6)) + printf "%s", chunkname(name, lastxrefref) | out } +@ %def lastxref name defn +There are three messages related to the definition and use of code chunks which +may appear in the output: "This definition continued in ...", "This code used +in ...", and "This code not used in this document." These messages are printed +by the following code. +<<process [[noweb]] commands>>= +/^@xref begindefs/ { endcode() + printf "This definition continued in" | out } +/^@xref beginuses/ { endcode() + printf "This code used in" | out } +/^@xref notused/ { endcode() + print "This code not used in this document." | out } +/^@xref (def|use)item/ { addlist(tag[$3]) } +/^@xref end(defs|uses)/ { printlist() } +@ +Processing of the [[noweb]] commands which produce the identifier definition +message "Defines: ... used in ..." is performed by the following code. The +[[if]] in [[@index isused]] prevents index definitions from pointing to +themselves. +<<process [[noweb]] commands>>= +$0 ~ /^@index begindefs/ && !noindex { + endcode() + print "Defines:" | out } + +$0 ~ /^@index isused/ && !noindex { + if (tag[$3] != lastxrefref) addlist(tag[$3]) } + +$0 ~ /^@index defitem/ && !noindex { + printf " %s,", $3 | out + if (nlist == 0) printf " not used in this document.\n" | out + else { printf " used in" | out; printlist() } } +@ +Processing of the [[noweb]] commands which produce the identifier usage message +"Uses ..." is performed by the following code. +<<process [[noweb]] commands>>= +$0 ~ /^@index beginuses/ && !noindex { endcode(); printf "Uses" | out } +$0 ~ /^@index isdefined/ && !noindex { lastuse = tag[$3] } +$0 ~ /^@index useitem/ && !noindex { addlist(sprintf("%s %s", $3, lastuse)) } +$0 ~ /^@index enduses/ && !noindex { printlist() } +@ %def lastuse +The [[noweb]] commands which print the list of chunks at the end of the +document are processed by the following code. +<<process [[noweb]] commands>>= +/^@xref beginchunks/ { close(out); out = zfmt + print "List of code chunks\n" | out } +/^@xref chunkbegin/ { name = convquote(substr($0, length($3) + 19)) + printf "%s\n", chunkname(name, tag[$3]) | out } +/^@xref chunkuse/ { addlist(tag[$3]) } +/^@xref chunkdefn/ { } +/^@xref chunkend/ { if (nlist == 0) + print " Not used in this document." | out + else { printf " Used in" | out; printlist() } } +/^@xref endchunks/ { } +@ +The [[noweb]] commands which print the list of identifiers at the end of the +document are processed by the following code. +<<process [[noweb]] commands>>= +$0 ~ /^@index beginindex/ && !noindex { print "\nList of identifiers (defini" \ + "tion in parentheses)\n" | out } +$0 ~ /^@index entrybegin/ && !noindex { name = substr($0, length($3 + 19)) + lastdefn = tag[$3] + printf "%s: ", $4 | out } +$0 ~ /^@index entryuse/ && !noindex { addlist(tag[$3]) } +$0 ~ /^@index entrydefn/ && !noindex { } +$0 ~ /^@index entryend/ && !noindex { for (i = 1; i <= nlist; i++) + if (list[i] == lastdefn) + sub(/.*/, "(&)", list[i]) + if (nlist == 0) + print "Not used." | out + else printlist() } +$0 ~ /^@index endindex/ && !noindex { } +@ +Several of the cross-reference and indexing commands use the [[addlist(s)]] and +[[printlist()]] functions to manage the printing of lists of code sections and +variable names: [[addlist(s)]] adds string [[s]] to a queued [[list]] waiting +to be printed and [[printlist()]] prints the [[list]], appropriately formatted +with commas. These two functions are described below. +<<functions>>= +function addlist(s, i) { + for (i = 1; i <= nlist; i++) + if (s == list[i]) return + list[++nlist] = s } + +function printlist( i) { + if (nlist == 1) printf " %s.\n", list[1] | out + else if (nlist == 2) printf " %s and %s.\n", list[1], list[2] | out + else { + for (i = 1; i < nlist; i++) + printf " %s,", list[i] | out + printf " and %s.\n", list[nlist] | out } + for (i in list) delete list[i] + nlist = 0 } +@ %def list nlist addlist printlist +Chunk names which appear in definitions and uses of chunks consist of text +which may contain quoted code embedded between double square brackets. Quoted +code in text chunks are handled by the [[@quote ... @endquote]] mechanism, but +quoted code in chunk names must be handled explicitly by the back end. The +function below does what is needed. +<<functions>>= +function convquote(s) { gsub(/\[\[|\]\]/, "", s); return s } +@ %def convquote + +<<functions>>= +function chunkname(name, number) { + if (number == 0) + return sprintf("<%s>", name) + else + return sprintf("<%s %d>", name, number) +} +@ %def chunkname +@ +<nowebchunks> +<nowebindex> diff --git a/web/noweb/src/lib/unmarkup b/web/noweb/src/lib/unmarkup new file mode 100755 index 0000000000..761fc14897 --- /dev/null +++ b/web/noweb/src/lib/unmarkup @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +nawk ' +BEGIN { + rcsid = "$Id: unmarkup,v 1.20 2008/10/06 01:03:05 nr Exp $" + rcsname = "$Name: $" + firstdoc = 1 +} +/^@begin docs / { if (!firstdoc) printf "@ "; firstdoc = 0 } +/^@begin code / { code = 1 } +/^@end [cd]o[dc][es] / { + code = 0 + if (dangling_text) printf "\n" + dangling_text = 0 + printf "%s", deflines + if (defline != "") printf "%s\n", defline + deflines = "" ; defline = "" + } +/^@defn / { printf "<<%s>>=", substr($0,7) } +/^@text $/ {next} +/^@text / { + gsub("<<", "@<<"); + gsub(">>", "@>>"); + if (!(code || quoting)) { + gsub(/\[\[/, "@[["); + gsub(/\]\]/, "@]]"); + } + printf "%s", substr($0,7) + dangling_text = 1 +} +/^@quote$/ { printf("[["); dangling_text = 1; quoting = 1 } +/^@endquote$/ { printf("]]"); dangling_text = 1; quoting = 0 } +/^@nl$/ { printf "\n"; dangling_text = 0} + +/^@index defn / { + if (defline == "") defline = "@ %def" + defline = defline " " substr($0, 13) +} +/^@index nl$/ { + deflines = deflines defline "\n" + defline = "" +} +/^@use / { printf "<<%s>>", substr($0,6) + dangling_text = 1 + }' "$@" | sed 's/^@ $/@/' diff --git a/web/noweb/src/limake b/web/noweb/src/limake new file mode 100755 index 0000000000..150840afc9 --- /dev/null +++ b/web/noweb/src/limake @@ -0,0 +1,3 @@ +#!/bin/sh +if [ $# -eq 0 ]; then set all install; fi # "$@" breaks make for empty args +/usr/bin/make CC="gcc -ansi -pedantic -O -Wall -Werror" LIBSRC=icon BIN=/usr/local/noweb/bin LIB=/usr/local/noweb/lib MAN=/usr/local/noweb/man TEXINPUTS=/usr/share/texmf/tex/plain/misc ELISP=$HOME/emacs "$@" diff --git a/web/noweb/src/nwmake b/web/noweb/src/nwmake new file mode 100644 index 0000000000..eb9e71ea61 --- /dev/null +++ b/web/noweb/src/nwmake @@ -0,0 +1,3 @@ +#!/bin/sh +if [ $# -eq 0 ]; then set iconlib all install; fi # "$@" breaks make for empty args +/bin/make CC="lcc -A -A" BIN=/usr/local/bin LIB=/usr/local/lib/noweb MAN=/usr/local/man TEXINPUTS=/usr/local/lib/tex/inputs "$@" diff --git a/web/noweb/src/rhmake b/web/noweb/src/rhmake new file mode 100755 index 0000000000..4694987aff --- /dev/null +++ b/web/noweb/src/rhmake @@ -0,0 +1,3 @@ +#!/bin/sh +if [ $# -eq 0 ]; then set all install; fi # "$@" breaks make for empty args +/usr/bin/make CC="lcc -A" LIBSRC=icon BIN=/usr/local/noweb/bin LIB=/usr/local/noweb/lib MAN=/usr/local/noweb/man TEXINPUTS=/usr/share/texmf/tex/latex/local ELISP=$HOME/emacs "$@" diff --git a/web/noweb/src/shell/Makefile b/web/noweb/src/shell/Makefile new file mode 100644 index 0000000000..e28d7b5659 --- /dev/null +++ b/web/noweb/src/shell/Makefile @@ -0,0 +1,35 @@ +# Copyright 1993 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +SHELL=/bin/sh +LIB=/usr/public/pkg/noweb/lib + +EXEC=noweave noweb notangle noroff toroff +SRCS=$(EXEC) tmac.w +RCSFILES=noweave.nw notangle.nw noweb.nw noroff.nw noroots nocount Makefile +CINAME= +CIMSG= + +all: $(SRCS) +source: $(SRCS) +touch: $(SRCS) + touch $(SRCS) +boot: + touch $(SRCS) + +checkin: + ci -l $(CINAME) $(CIMSG) $(RCSFILES) + +noweave: noweave.nw + notangle -Rnoweave noweave.nw > noweave + +notangle: notangle.nw + notangle -Rnotangle notangle.nw > notangle + +noweb: noweb.nw + notangle -Rnoweb noweb.nw > noweb + +noroff toroff tmac.w: roff.nw + noweb -t roff.nw + +clean: ; rm -f *.log *.blg *.dvi *.toc *.aux *.tex *~ *.nwt roff.mm +clobber: clean ; rm -f $(SRCS) diff --git a/web/noweb/src/shell/cpif b/web/noweb/src/shell/cpif new file mode 100644 index 0000000000..d304e204f5 --- /dev/null +++ b/web/noweb/src/shell/cpif @@ -0,0 +1,32 @@ +#!/bin/sh + +# cpif [ -eq -ne ] file... +# copy standard input to each of the named files +# if new * old is true or old doesn't exist; +# * defaults to -ne + +PATH=/bin:/usr/bin +LIB=|LIBDIR| + +# set -x +op=-ne +case "$1" in +-eq|-ne) op=$1; shift ;; +-*) echo 'Usage: '`basename $0`' [ -eq -ne ] file...' 1>&2; exit 2 +esac +case $# in +0) echo 'Usage: '`basename $0`' [ -eq -ne ] file...' 1>&2; exit 2 +esac + +new=$($LIB/nwmktemp) +trap 'rm -f $new; exit 1' 1 2 15 # clean up files + +cat >$new +for i +do + cmp -s $new $i + case $op$? in + -eq0|-ne1|*2) cp $new $i || { rm -f $new; exit 1; } + esac +done +rm -f $new diff --git a/web/noweb/src/shell/detangle b/web/noweb/src/shell/detangle new file mode 100644 index 0000000000..741afd4654 --- /dev/null +++ b/web/noweb/src/shell/detangle @@ -0,0 +1,73 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# set -x +LIB=|LIBDIR| +markup=$LIB/markup opt= arg= filters= +width=72 subst='gsub("\\*/", "* /", s)' format='/* %%-%ds */' + +while [ $# -gt 0 ]; do + case $1 in + -ml|-m3) format='(* %%-%ds *)' ; subst='gsub("\\*\\)", "* )", s)' ;; + -awk|-icn|-icon) format='# %%-%ds' ; subst=' ' ;; + -lisp|-scm) format=';;; %%-%ds' ; subst=' ' ;; + -c++) format='// %%-%ds' ; subst=' ' ;; + -c) format='/* %%-%ds */' subst='gsub("\\*/", "* /", s)' ;; + -pascal) format='{ %%-%ds }' ; subst='gsub("[{}]", "-", s)' ;; + -f77) format='C %%-%ds' ; subst=' ' ;; + -f90) format='! %%-%ds' ; subst=' ' ;; + -tex) format='%%%% %%-%ds' ; subst=' ' ;; + -L*) ;; # deliberately ignore requests for #line + -w[0-9][0-9]*) width=`echo $1 | sed 's/^-w//'` ;; + -filter) filters=" | $filters $2 " ; shift ;; + -markup) markup="$2" ; shift ;; + -) arg="$arg '$1'" ;; + -*) opt="$opt '$1'" ;; + *) arg="$arg '$1'" ;; + esac + shift +done + +PATH="$PATH:$LIB" eval "$markup $arg $filters" | +nawk 'BEGIN { line = 0; capture = 0; aftercode=0 + format=sprintf("'"$format"'",'"$width"') } + +function comment(s) { + '"$subst"' + return sprintf(format,s) +} + +function grab(s) { + if (capture==0) print + else holding[line] = holding[line] s +} + +/^@end doc/ { capture = 0; holding[++line] = "" ; next } +/^@begin doc/ { capture = 1; next } + +/^@end code/ { aftercode = 1; seenchars = 0; next } + +/^@text [ ]*$/ { grab(substr($0,7)); next} +/^@text / { grab(substr($0,7)); seenchars = 1; next} +/^@quote$/ { grab("[[") ; next} +/^@endquote$/ { grab("]]") ; next} + +/^@nl$/ { if (aftercode != 0) { + if (seenchars == 0) { + if (capture !=0 ) { + holding[++line] = "" + } else if (defn_pending != 0) { + print "@nl" + for (i=0; i<=line && holding[i] ~ /^ *$/; i++) i=i + for (; i<=line; i++) printf "@text %s\n@nl\n", comment(holding[i]) + line = 0; holding[0] = "" + defn_pending = 0 + } else print + next + } + +/^@defn / { holding[line] = holding[line] "<"substr($0,7)">=" # (line should be blank) + print ; defn_pending = 1 ; next } +{ print }' | +eval "$LIB/nt $opt" diff --git a/web/noweb/src/shell/htmltoc b/web/noweb/src/shell/htmltoc new file mode 100755 index 0000000000..db2606e6a0 --- /dev/null +++ b/web/noweb/src/shell/htmltoc @@ -0,0 +1,66 @@ +eval 'exec perl -S $0 ${1+"$@"}' + if 0; + +$sentinel = "<tableofcontents>"; +$sentinel_end = "</tableofcontents>"; + +while (<>) { + push(@file, $_) unless (/^$sentinel/ .. /^$sentinel_end/); + push(@file, $_) if (/^$sentinel/ || /^$sentinel_end/); +} + +$intro = -1; + +$outerlevel = 1; + +$curlevel = $outerlevel; +foreach $i (0..$#file) { + + if ($file[$i] =~ m|$sentinel|) {$intro = $i;} + + next unless $file[$i] =~ m|<[hH]([23])>\s*((.*\S)*)\s*</[hH]\1>|; + $nowlevel = $1; + $label = $heading = $2; + + while ($nowlevel > $curlevel) { + push(@toc, "<ul compact>\n"); + $curlevel++; + } + while ($nowlevel < $curlevel) { + push(@toc, "</ul>\n"); + $curlevel--; + } + if ($file[$i] =~ m/name="([^"]+)"/i) { + $label = $1; + $heading =~ s/name="/href="#/i; + push(@toc, "<li>$heading\n"); + } else { + $label =~ s/\W//g; + $label =~ tr/A-Z/a-z/; + $label = substr($label, 0, 6) . "00"; + $label++ while (defined $labels{$label}); +# $file[$i] =~ s|\Q$heading\E|<a name="$label">$&</a>|; + $file[$i] =~ s|$heading|<a name="$label">$&</a>|; + push(@toc, qq{<li><a href="#$label">$heading</a>\n}); + } + $labels{$label}++; +} +$nowlevel = $outerlevel; +while ($nowlevel > $curlevel) { + push(@toc, "<ul compact>\n"); + $curlevel++; +} +while ($nowlevel < $curlevel) { + push(@toc, "</ul>\n"); + $curlevel--; +} + +if ($intro >= 0) { + print @file[0..$intro]; + print @toc; + print @file[$intro+1 .. $#file]; +} else { + print @file; +} + + diff --git a/web/noweb/src/shell/luddite b/web/noweb/src/shell/luddite new file mode 100644 index 0000000000..5a38673946 --- /dev/null +++ b/web/noweb/src/shell/luddite @@ -0,0 +1,46 @@ +#!/bin/sh +# set -x +LIB=+LIBDIR+ +opt= arg= +comment='function comment(s) { + gsub("\\*/", "* /", s) + return sprintf("/* %-72s */",s) +}' + +for i do + case $i in + -ml|-m3) comment='function comment(s) { + gsub("\\*\\)", "* )", s) + return sprintf("(* %-72s *)",s) +}' ;; + -awk) comment='function comment(s) { + + return sprintf("# %s",s) +}' ;; + -c) comment='function comment(s) { + gsub("\\*/", "* /", s) + return sprintf("/* %-72s */",s) +}' ;; + -L*) ;; # deliberately ignore requests for #line + -*) opt="$opt '$i'" ;; + *) arg="$arg '$i'" ;; + esac +done + +eval "$LIB/markup $arg" | expand | +nawk 'BEGIN { line = 0; capture = 0 } +'"$comment"' + +/^@end doc/ { capture = 0; holding[line++] = "" ; next } +/^@begin doc/ { capture = 1; next } + +capture == 1 { holding[line++] = comment($0); next } + +/@name / { print + print comment("<"substr($0,7)">=") # may want to omit this line + for (i=0; i<line; i++) print holding[i] + line = 0 + next +} +{ print }' | +eval "$LIB/nt $opt" diff --git a/web/noweb/src/shell/nocount b/web/noweb/src/shell/nocount new file mode 100755 index 0000000000..6ca632a089 --- /dev/null +++ b/web/noweb/src/shell/nocount @@ -0,0 +1,24 @@ +#!/bin/sh + +LIB=|LIBDIR| + +$LIB/markup "$@" | nawk ' +BEGIN { bogus = "this is total bogosity"; codecount[bogus] = -1; docscount[bogus] = -1 } +/^@file / { thisfile = $2 ; files[thisfile] = 0 } +/^@begin code/ { code = 1 } +/^@begin docs/ { code = 0 } +/^@nl/ { + if (code == 0) + docscount[thisfile]++ + else + codecount[thisfile]++ +} +END { + printf " Code Docs Both File\n" + for (file in files) { + printf "%5d %5d %5d %s\n", codecount[file], docscount[file], codecount[file]+docscount[file], file + totalcode += codecount[file] + totaldocs += docscount[file] + } + printf "%5d %5d %5d %s\n", totalcode, totaldocs, totalcode+totaldocs, "Total" +}' diff --git a/web/noweb/src/shell/nodefs b/web/noweb/src/shell/nodefs new file mode 100644 index 0000000000..5396e105db --- /dev/null +++ b/web/noweb/src/shell/nodefs @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 1993 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +LIB=|LIBDIR| +case $1 in + -auto|-autodefs) + newfilter="$LIB/autodefs.$2" ; shift ; shift + $LIB/markup "$@" | $newfilter | sed -n 's/^@index defn //p' + ;; + -showauto|-showautodefs) + foundautodefs= + for i in $LIB/autodefs.*; do + if [ -r $i ]; then + echo "This `basename $0` supports -autodefs $i" | + sed "s@$LIB/autodefs\.@@" 1>&2 + foundautodefs=$i + fi + done + if [ -z "$foundautodefs" ]; then + echo "This `basename $0` does not support -autodefs" + fi + ;; + *) + $LIB/markup "$@" | sed -n 's/^@index defn //p' + ;; +esac +exit $? diff --git a/web/noweb/src/shell/nonu b/web/noweb/src/shell/nonu new file mode 100755 index 0000000000..612dadede9 --- /dev/null +++ b/web/noweb/src/shell/nonu @@ -0,0 +1,41 @@ +#!/bin/sh +LIB=/usr/public/pkg/noweb/lib +# attempt to convert nuweb to noweb using sam + +tmp=$($LIB/nwmktemp) +trap '/bin/rm -f $tmp; exit 1' 1 2 15 # clean up files +cp $1 $tmp || exit 1 + +notat='([^@]|\n)' +white='[ \n]' +sam -d $tmp << EOF +,x/\@\@/c/{{{the magic at sign}}}/ +,x/<</c/{{{double left brackets}}}/ +,x/>>/c/{{{double right brackets}}}/ +,x/@m/c/\\nowebchunks/ +,x/@u/c/\\nowebindex/ +,x/\@[oO][^@]*$/x/ -[dit][a-zA-Z]*/v/..../d +,x/\@[dDoO|}]/i/\n/ +,x/\n\n\@[dDoO|}]/s/\n\n/\n/ +,x/\@[dDoO]${notat}*\@{/s/\n/ /g +,x/\@[dDoO]${notat}*\@{/{ +s/\@[dDoO] */<</ +s/\@{/>>=\n/ +} +,s/\@</<</g +,s/\@>/>>/g +,x/<<[^>]*(>[^>][^>]*)*>>/s/[ ][ ]*/ /g +,x/<< | >>/s/ // +,x/\@\|$notat*\@}/x/\n/a/@ %def / +,x/\@ %def \@}/c/@ / +,x/\@\|/c/@ %def / +,s/\@}/@ /g +,x/\n\@ %def.*\n\@ *\n/x/\n\@ *\n/c/\n/ +,x/^\@ %def.*$/s/[ ]+/ /g +,x/{{{the magic at sign}}}/c/@/ +,x/{{{double left brackets}}}/c/@<</ +,x/{{{double right brackets}}}/c/@>>/ +w +EOF +$LIB/markup $tmp | $LIB/disambiguate | $LIB/unmarkup +rm $tmp diff --git a/web/noweb/src/shell/noroff b/web/noweb/src/shell/noroff new file mode 100644 index 0000000000..fbe2b2a86a --- /dev/null +++ b/web/noweb/src/shell/noroff @@ -0,0 +1,98 @@ +#!/bin/sh +# +# noroff -- run troff using tags file trick + +ROFF="groff" +AWK=nawk +macrodir=|LIBDIR| +LIB=|LIBDIR| + +opts= + +if [ $# -eq 0 ]; then + echo "Usage: noroff [groff-arguments] files" 1>&2 + exit 1 +fi + +while [ $# -gt 0 ] +do + case $1 in + -*) opts="$opts $1" + shift + ;; + *) # end of options + break; + ;; + esac +done + +if [ "$opts" = "" ] +then + # no options, default to -mm + # groff already defaults to -Tps + opts="-mm" +fi +# otherwise assume user passed in all the arguments they want + +base="`basename $1 | sed '/\./s/\.[^.]*$//'`" +tagsfile="$base.nwt" +(echo ".so $macrodir/tmac.w" +if [ -r "$tagsfile" ]; then + tagstemp=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } + cp $tagsfile $tagstemp + $AWK '{ + if (sub(/^###TAG### / , "")) tags[$1] = $2 + else if (sub(/^###BEGINCHUNKS###/, "")) printf ".de CLIST\n.CLISTBEGIN\n" + else if (sub(/^###CHUNKBEGIN### /, "")) { name = convquote($0) + chunkuse = chunkdefn = "" } + else if (sub(/^###CHUNKUSE### / , "")) chunkuse = chunkuse " " tag($0) + else if (sub(/^###CHUNKDEFN### / , "")) chunkdefn = chunkdefn " " tag($0) + else if (sub(/^###CHUNKEND###/ , "")) + printf ".CITEM \"%s\" \"%s\" \"%s\"\n", name, chunkdefn, chunkuse + else if (sub(/^###ENDCHUNKS###/ , "")) printf ".CLISTEND\n..\n" + else if (sub(/^###BEGININDEX###/ , "")) printf ".de ILIST\n.ILISTBEGIN\n" + else if (sub(/^###ENTRYBEGIN### /, "")) { name = convquote($0) + entryuse = entrydefn = "" } + else if (sub(/^###ENTRYUSE### / , "")) entryuse = entryuse " " tag($0) + else if (sub(/^###ENTRYDEFN### / , "")) entrydefn = entrydefn " " tag($0) + else if (sub(/^###ENTRYEND###/ , "")) { + for (i = 1; i <= split(entrydefn, entryarray); i++) + sub(entryarray[i], "\\*[BEGINDEFN]&\\*[ENDDEFN]", entryuse) + printf ".IITEM \"%s\" \"%s\"\n", name, entryuse } + else if (sub(/^###ENDINDEX###/ , "")) printf ".ILISTEND\n..\n" + } + function convquote(s, out, front, mid, tail) { + gsub(/\[\[/, "\\*[BEGINCONVQUOTE]", s) + # gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s) + out = "" + mid = "\\*[ENDCONVQUOTE]" + while (match(s, /\]\]+/) != 0) { + # RLENGTH is length of match, want to remove last two chars + # RSTART is where sequence of ]s begins + tail = substr(s, RSTART + RLENGTH) + if (RLENGTH == 2) # easy + front = substr(s, 1, RSTART - 1) + else + front = substr(s, 1, RSTART - 1 + RLENGTH - 2) + out = out front mid + s = tail + } + out = out s + return out } + # my test program for the revised function - ADR + # BEGIN { str = "abc[[foo[i]]]]]]]]junk" + # print str + # print convquote(str) + # str2 = "nothing here" + # print str2 + # print convquote(str2) + # str3 = "abc[[foo[i]]]]]]]]junk[[bar[i]]more stuff[[baz]]" + # print str3 + # print convquote(str3) + # } + function tag(s) { if (s in tags) return tags[s]; else return "???" }' $tagstemp + rm -f $tagstemp + fi + cat "$@") | +($ROFF $opts 2>$tagsfile) +sed '/^###[A-Z][A-Z]*###/d' $tagsfile >&2 diff --git a/web/noweb/src/shell/noroots b/web/noweb/src/shell/noroots new file mode 100644 index 0000000000..0dcf6c06b5 --- /dev/null +++ b/web/noweb/src/shell/noroots @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# set -x +LIB=|LIBDIR| +$LIB/markup "$@" | nawk ' +/^@quote$/,/^@endquote$/ { next } +/^@defn / { chunk=substr($0,7) ; defined[chunk]=1 } +/^@use / { chunk=substr($0,6) ; used[chunk]=1 } +END { + for (chunk in defined) { + if (defined[chunk]==1 && used[chunk]==0) printf "<<%s>>\n", chunk + } +}' diff --git a/web/noweb/src/shell/notangle b/web/noweb/src/shell/notangle new file mode 100644 index 0000000000..ba3174e4be --- /dev/null +++ b/web/noweb/src/shell/notangle @@ -0,0 +1,27 @@ +#!/bin/sh +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# $Id: notangle.nw,v 1.2 1997/09/13 20:29:56 nr Exp $ +# $Name: $ +LIB=|LIBDIR| +markup=$LIB/markup opt= arg= markopt= filters= +while [ $# -gt 0 ]; do + case $1 in + -ml|-m3|-awk|-icn|-icon|-pascal|-c|-c++|-f77|-f90|-tex|-w[0-9][0-9]*) ;; + # deliberately ignore nountangle args + -t) ;; # this is default + -t*) markopt="$markopt -t" ; opt="$opt '$1'" ;; + # copy tabs at markup, use width given in notangle + -filter) filters="$filters $2 | " ; shift ;; + -markup) markup="$2" ; shift ;; + -) arg="$arg '$1'" ;; + -L*) opt="$opt -t '$1'" ; markopt="$markopt -t" ;; + -*) opt="$opt '$1'" ;; + *) arg="$arg '$1'" ;; + esac + shift +done +PATH="$PATH:$LIB" +export PATH +eval "$markup $markopt $arg | $filters $LIB/nt $opt "'; rc=$?' +exit $rc diff --git a/web/noweb/src/shell/notangle.nw b/web/noweb/src/shell/notangle.nw new file mode 100644 index 0000000000..0cfa484344 --- /dev/null +++ b/web/noweb/src/shell/notangle.nw @@ -0,0 +1,30 @@ +<<copyright notice>>= +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +<<notangle>>= +#!/bin/sh +<<copyright notice>> +# $Id: notangle.nw,v 1.2 1997/09/13 20:29:56 nr Exp $ +# $Name: $ +LIB=|LIBDIR| +markup=$LIB/markup opt= arg= markopt= filters= +while [ $# -gt 0 ]; do + case $1 in + -ml|-m3|-awk|-icn|-icon|-pascal|-c|-c++|-f77|-f90|-tex|-w[0-9][0-9]*) ;; + # deliberately ignore nountangle args + -t) ;; # this is default + -t*) markopt="$markopt -t" ; opt="$opt '$1'" ;; + # copy tabs at markup, use width given in notangle + -filter) filters="$filters $2 | " ; shift ;; + -markup) markup="$2" ; shift ;; + -) arg="$arg '$1'" ;; + -L*) opt="$opt -t '$1'" ; markopt="$markopt -t" ;; + -*) opt="$opt '$1'" ;; + *) arg="$arg '$1'" ;; + esac + shift +done +PATH="$PATH:$LIB" +export PATH +eval "$markup $markopt $arg | $filters $LIB/nt $opt "'; rc=$?' +exit $rc diff --git a/web/noweb/src/shell/nountangle b/web/noweb/src/shell/nountangle new file mode 100644 index 0000000000..4853c33262 --- /dev/null +++ b/web/noweb/src/shell/nountangle @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# set -x +LIB=|LIBDIR| +markup=$LIB/markup opt= arg= filters= +width=72 subst='gsub("\\*/", "* /", s)' format='/* %%-%ds */' + +while [ $# -gt 0 ]; do + case $1 in + -ml|-m3) format='(* %%-%ds *)' ; + subst='gsub("\\*\\)", "* )", s); gsub("\\(\\*", "( *", s)' ;; + -awk|-icn|-icon) format='# %%-%ds' ; subst=' ' ;; + -lisp|-scm) format=';;; %%-%ds' ; subst=' ' ;; + -matlab) format='%%%% %%-%ds' ; subst=' ' ;; + -c++) format='// %%-%ds' ; subst=' ' ;; + -c) format='/* %%-%ds */' subst='gsub("\\*/", "* /", s)' ;; + -pascal) format='{ %%-%ds }' ; subst='gsub("[{}]", "-", s)' ;; + -f77) format='C %%-%ds' ; subst=' ' ;; + -f90) format='! %%-%ds' ; subst=' ' ;; + -tex) format='%%%% %%-%ds' ; subst=' ' ;; + -L*) ;; # deliberately ignore requests for #line + -w[0-9][0-9]*) width=`echo $1 | sed 's/^-w//'` ;; + -filter) filters=" $filters | $2 " ; shift ;; + -markup) markup="$2" ; shift ;; + -) arg="$arg '$1'" ;; + -*) opt="$opt '$1'" ;; + *) arg="$arg '$1'" ;; + esac + shift +done + +PATH="$PATH:$LIB" eval "$markup $arg $filters" | +nawk 'BEGIN { line = 0; capture = 0; format=sprintf("'"$format"'",'"$width"') } + +function comment(s) { + '"$subst"' + return sprintf(format,s) +} + +function grab(s) { + if (capture==0) print + else holding[line] = holding[line] s +} + +/^@end doc/ { capture = 0; holding[++line] = "" ; next } +/^@begin doc/ { capture = 1; next } + +/^@text / { grab(substr($0,7)); next} +/^@quote$/ { grab("[[") ; next} +/^@endquote$/ { grab("]]") ; next} + +/^@nl$/ { if (capture !=0 ) { + holding[++line] = "" + } else if (defn_pending != 0) { + print "@nl" + for (i=0; i<=line && holding[i] ~ /^ *$/; i++) i=i + for (; i<=line; i++) printf "@text %s\n@nl\n", comment(holding[i]) + line = 0; holding[0] = "" + defn_pending = 0 + } else print + next + } + +/^@defn / { holding[line] = holding[line] "<"substr($0,7)">=" # (line should be blank) + print ; defn_pending = 1 ; next } +{ print }' | +eval "$LIB/nt $opt" diff --git a/web/noweb/src/shell/noweave b/web/noweb/src/shell/noweave new file mode 100644 index 0000000000..dad8b023c5 --- /dev/null +++ b/web/noweb/src/shell/noweave @@ -0,0 +1,292 @@ +#!/bin/sh +# Copyright 1991-1997 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# $Id: noweave.nw,v 1.7 2008/10/06 01:03:24 nr Exp $ +LIB=|LIBDIR| +markup=$LIB/markup backend=$LIB/totex +wrapper= delay= args= markopts= noweboptions= autodefs= verbose= shortxref= +noquote="-noquote" docanchor= noindex="-noindex" +filtera= filterb= filterc= filterd= filtere= filterf= filterg= +# following supported by change in totex back end +noquote= +using_index= using_xref= +pass2setcmd="set ignore" +for i do + pass2setcmd="$pass2setcmd '$i'" +done +arg= +while [ $# -gt 0 ]; do + case $1 in + -latex) if [ "$wrapper" != "none" ]; then wrapper=latex; fi ;; + -tex) wrapper=tex ;; + -html) if [ "$wrapper" != "none" ]; then wrapper=html; fi; + backend="$LIB/tohtml -localindex" + noquote=""; docanchor="-docanchor 10" ;; + -latex+html) if [ "$wrapper" != "none" ]; then wrapper=latex; fi; + backend="$LIB/tohtml -localindex -raw" + noquote=""; docanchor="-docanchor 10" ;; + -ascii) wrapper=none + backend="$LIB/toascii" ;; + -troff) wrapper=mm + backend="$LIB/toroff" ;; + -n) wrapper=none ;; + -backend) backend="$2" ; shift ;; + -markup) markup="$2" ; shift ;; + -filter) shift ;; + -x) using_xref=1 ;; + -index) noindex= ; using_index=1 ;; + -indexfrom) shift; noindex= ; using_index=1 ;; + -autodefs) newfilter="$LIB/autodefs.$2" + if [ -r $newfilter ]; then if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi + else echo "$0: don't know how to find definitions for '$2'" 1>&2; exit 1 + fi + shift + ;; + -showautodefs) foundautodefs= + for i in $LIB/autodefs.*; do + if [ -r $i ]; then + echo "This `basename $0` supports -autodefs $i" | + sed "s@$LIB/autodefs\.@@" 1>&2 + foundautodefs=$i + fi + done + if [ -z "$foundautodefs" ]; then + echo "This `basename $0` does not support -autodefs" + fi; exit 1 ;; + -option) newopt="$2"; shift + if [ "X$newopt" = "Xshortxref" ]; then shortxref="-shortxref"; fi + if [ "X$newopt" = "Xlongxref" ]; then shortxref="-longxref"; fi + if [ -z "$noweboptions" ] ; then noweboptions="$newopt" + else noweboptions="$noweboptions,$newopt" + fi ;; + # -nodelay) delay= ;; + -delay) delay="-delay" ; wrapper=none ;; + -t*) markopts="$markopts $1" ;; + -v) echo 'RCS version name $Name: $' 1>&2 + echo 'RCS id $Id: noweave.nw,v 1.7 2008/10/06 01:03:24 nr Exp $' 1>&2 + verbose=1 ;; + -) arg="$arg $1" ;; + -*) echo "$0: Unrecognized option '$1'" 1>&2 ; echo "Usage: $0 [options] [files]" 1>&2 + echo "Options recognized are:" 1>&2 + echo "-latex Emit LaTeX with headers and trailers (default)." 1>&2 + echo "-tex Emit plain TeX with headers and trailers." 1>&2 + echo "-html Emit HTML with headers and trailers." 1>&2 + echo "-latex+html Assume LaTeX in documentation, but use HTML in code." 1>&2 + # echo "-ascii Emit ASCII." 1>&2 + echo "-troff Emit troff (actually GNU groff)." 1>&2 + echo " -n Don't use any header or trailer." 1>&2 + echo "-markup frontend Parse input with frontend (e.g., numarkup)." 1>&2 + echo "-x Use the default cross-referencer (needs LaTeX or HTML)." 1>&2 + echo "-index Create index using identifiers defined in input files." 1>&2 + echo "-indexfrom defs Create index of identifers listed in file defs." 1>&2 + echo "-filter cmd Filter through 'cmd' before weaving; cmd could prettyprint" 1>&2 + echo " or perform other functions." 1>&2 + echo "-autodefs lang Source is in language 'lang'; find definitions automatically." 1>&2 + echo "-showautodefs List languages that can be used with -autodefs" 1>&2 + echo '-option opt Add \noweboptions{opt} to header (latex only)' 1>&2 + echo "-delay Delay markup until after first documentation chunk." 1>&2 + echo "-tk Expand tab stops every k columns" 1>&2 + echo "-t Copy tabs to the output" 1>&2 + echo "-v Print pipeline and RCS info on standard error" 1>&2; exit 1 ;; + *) arg="$arg $1" ;; + esac + shift +done +if [ -n "$using_xref" -a -n "$using_index" ]; then + echo "$0: you may not use -x with -index or -indexfrom (drop the -x)" 1>&2 + exit 1 +fi +if [ -z "$wrapper" ]; then wrapper=latex; fi +eval $pass2setcmd +shift +while [ $# -gt 0 ]; do + case $1 in + -filter) + newfilter="$2" ; shift ; if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi ;; + -x) newfilter="$LIB/noidx $docanchor $delay"; if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi ;; + -index) + newfilter="$LIB/finduses $noquote" ; if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi + newfilter="$LIB/noidx $docanchor $delay"; if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi ;; + -indexfrom) + newfilter="$LIB/finduses $noquote $2"; if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi + newfilter="$LIB/noidx $docanchor $delay"; if [ -z "$filtera" ]; then + filtera="$newfilter" + elif [ -z "$filterb" ]; then + filterb="$newfilter" + elif [ -z "$filterc" ]; then + filterc="$newfilter" + elif [ -z "$filterd" ]; then + filterd="$newfilter" + elif [ -z "$filtere" ]; then + filtere="$newfilter" + elif [ -z "$filterf" ]; then + filterf="$newfilter" + elif [ -z "$filterg" ]; then + filterg="$newfilter" + else + echo "$0: ran out of filters" 1>&2 + exit 1 + fi + shift ;; + esac + shift +done +( +header= +case $wrapper in + none) ;; + latex) header="@header $wrapper $noweboptions" + trailer="@trailer $wrapper" ;; + *) header="@header $wrapper$arg" + trailer="@trailer $wrapper" ;; +esac +if [ -n "$header" ]; then echo "$header"; fi +PATH="$PATH:$LIB" +export PATH +if [ -n "$verbose" ]; then + if [ -n "$header" -o -n "$trailer" ]; then + parenpfx="(" + if [ -n "$header" ]; then echo "(echo $header" 1>&2; parenpfx=" "; fi + echo "$parenpfx$markup" $markopts $arg 1>&2 + if [ -n "$trailer" ]; then echo " echo $trailer" 1>&2; fi + echo ") |" 1>&2 + else + echo $markup $markopts $arg '|' 1>&2 + fi + if [ -n "$filtera" ]; then echo $filtera '|' 1>&2; fi + if [ -n "$filterb" ]; then echo $filterb '|' 1>&2; fi + if [ -n "$filterc" ]; then echo $filterc '|' 1>&2; fi + if [ -n "$filterd" ]; then echo $filterd '|' 1>&2; fi + if [ -n "$filtere" ]; then echo $filtere '|' 1>&2; fi + if [ -n "$filterf" ]; then echo $filterf '|' 1>&2; fi + if [ -n "$filterg" ]; then echo $filterg '|' 1>&2; fi +fi +if [ -n "$verbose" ]; then echo $backend $noindex $delay $shortxref 1>&2; fi +if [ -z "$filtera" ]; then + $markup $markopts $arg +elif [ -z "$filterb" ]; then + $markup $markopts $arg | eval $filtera +elif [ -z "$filterc" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb +elif [ -z "$filterd" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc +elif [ -z "$filtere" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd +elif [ -z "$filterf" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd | eval $filtere +elif [ -z "$filterg" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd | eval $filtere | eval $filterf +else + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd | eval $filtere | eval $filterf | + eval $filterg +fi +if [ -n "$trailer" ]; then echo $trailer; fi +) | + $backend $noindex $delay $shortxref +exit $? diff --git a/web/noweb/src/shell/noweave.hpux b/web/noweb/src/shell/noweave.hpux new file mode 100644 index 0000000000..1314af4675 --- /dev/null +++ b/web/noweb/src/shell/noweave.hpux @@ -0,0 +1,63 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# options: -l == use LaTeX -x == generate LaTeX cross-reference goo + +# fix for backlash for HP-UX contributed by Gary Leavens. + +LIB=|LIBDIR| +latex=0 tex=1 xref=0 +args= +markopts= + +for i do + case $i in + -l) latex=1 ; tex=0 ;; + -n) latex=0 ; tex=0 ;; + -x) xref=1 ; if [ $tex -eq 1 ]; then tex=0 ; latex=1; fi ;; + -t*) markopts="$markopts $i" ;; + -*) echo "$0: Unrecognized argument '$i'" 1>&2 ; exit ;; + *) arg="$arg $i" ;; + esac +done + +if [ $tex -eq 1 ]; then echo '\\\c' ; echo input nwmac; fi +if [ $latex -eq 1 ]; then + echo '\\\c'; echo "documentstyle[noweb]{article}"; + echo '\\\c' ; echo 'begin{document}' +fi + +if [ $xref -eq 0 ]; then + $LIB/markup $markopts $arg +else + $LIB/markup $markopts $arg | $LIB/noxref +fi | nawk ' +BEGIN { code=0 ; quoting=0 } +/^@begin code/ { code=1 ; printf "\\begincode{%s}\n", substr($0, 13) } +/^@begin docs/ { printf "\\begindocs{%s}\n", substr($0, 13) } +/^@end code/ { code=0 ; printf "\\endcode\n" } +/^@end docs/ { printf "\\enddocs\n" } +/^@text / { line = substr($0, 7) + if (code || quoting) { + gsub("\\\\", "\\\\", line) + gsub("{", "\\{", line) ; gsub("}", "\\}", line) + } + printf "%s", line + } +/^@nl$/ { printf "\n" } +/^@defn / { gsub("\\[\\[", "\\code{}") ; gsub("\\]\\]", "\\edoc{}") + printf "\\moddef{%s}\\%sendmoddef", substr($0, 7), defns[substr($0, 7)] + defns[substr($0, 7)] = "plus" + } +/^@use / { gsub("\\[\\[", "\\code{}") ; gsub("\\]\\]", "\\edoc{}") + printf "\\LA{}%s\\RA{}", substr($0, 6) + } +/^@quote$/ { quoting = 1 ; printf "\\code{}" } +/^@endquote$/ { quoting = 0 ; printf "\\edoc{}" } +/^@file / { printf "\\filename{%s}\n", substr($0, 7) } +/^@literal / { printf "%s", substr($0, 10) }' +status=$? +if [ $tex -eq 1 ]; then echo '\\\c' ; echo bye; fi +if [ $latex -eq 1 ]; then echo '\\\c'; echo 'end{document}' ; fi +exit $status diff --git a/web/noweb/src/shell/noweave.nr b/web/noweb/src/shell/noweave.nr new file mode 100644 index 0000000000..ba67721748 --- /dev/null +++ b/web/noweb/src/shell/noweave.nr @@ -0,0 +1,77 @@ +#!/bin/sh +# +# Copyright 1991, 1992, 1993 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# options: -l == use LaTeX -x == generate LaTeX cross-reference goo + +LIB=|LIBDIR| +latex=0 tex=1 xref=0 +args= +markopts= + +for i do + case $i in + -l) latex=1 ; tex=0 ;; + -n) latex=0 ; tex=0 ;; + -x) xref=1 ; if [ $tex -eq 1 ]; then tex=0 ; latex=1; fi ;; + -t*) markopts="$markopts $i" ;; + -*) echo "$0: Unrecognized argument '$i'" 1>&2 ; exit ;; + *) arg="$arg $i" ;; + esac +done + +if [ $tex -eq 1 ]; then echo -n \\ ; echo input nwmac; fi +if [ $latex -eq 1 ]; then + echo -n \\; echo "documentstyle[noweb]{article}"; + for i in oddsidemargin evensidemargin; do + echo -n \\; echo -n "advance" ; echo -n \\ ; echo "$i -0.4in" + done + echo -n \\ ; echo 'begin{document}' +fi + +if [ $xref -eq 0 ]; then + $LIB/markup $markopts $arg +else + $LIB/markup $markopts $arg | $LIB/noxref +fi | nawk ' +BEGIN { code=0 ; quoting=0 } +/^@begin code/ { code=1 ; printf "\\begincode{%s}\n", substr($0, 13) } +/^@begin docs/ { printf "\\begindocs{%s}\n", substr($0, 13) } +/^@end code/ { code=0 ; printf "\\endcode\n" } +/^@end docs/ { printf "\\enddocs\n" } +/^@text / { line = substr($0, 7) + if (code || quoting) { + gsub("\\\\", "\\\\", line) + gsub("{", "\\{", line) ; gsub("}", "\\}", line) + } + printf "%s", line + } +/^@nl$/ { printf "\n" } +/^@defn / { name = substr($0, 7) + printf "\\moddef{%s}\\%sendmoddef", convquotes(name), defns[name] + defns[name] = "plus" + } +/^@use / { printf "\\LA{}%s\\RA{}", convquotes(substr($0, 6)) } +/^@quote$/ { quoting = 1 ; printf "\\code{}" } +/^@endquote$/ { quoting = 0 ; printf "\\edoc{}" } +/^@file / { printf "\\filename{%s}\n", substr($0, 7) } +/^@literal / { printf "%s", substr($0, 10) } +function convquotes(s, r, i) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "\\code{}" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + r = r substr(s, 1, i-1+RLENGTH-2) "\\edoc{}" + s = substr(s, i+RLENGTH) + } else { + r = r s "\\edoc{}" + s = "" + } + } + return r s +}' +status=$? +if [ $tex -eq 1 ]; then echo -n \\ ; echo bye; fi +if [ $latex -eq 1 ]; then echo -n \\; echo 'end{document}' ; fi +exit $status diff --git a/web/noweb/src/shell/noweave.nw b/web/noweb/src/shell/noweave.nw new file mode 100644 index 0000000000..d2e9cb0dbe --- /dev/null +++ b/web/noweb/src/shell/noweave.nw @@ -0,0 +1,564 @@ +\section{Weaving a {\tt noweb} file into a \TeX file} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991-1997 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# $Id: noweave.nw,v 1.7 2008/10/06 01:03:24 nr Exp $ +@ +Here's the organization of the source: +<<noweave>>= +#!/bin/sh +<<copyright notice>> +<<initialization>> +<<scan options and arguments>> +<<emit markup on standard output>> | + $backend $noindex $delay $shortxref +exit $? +<<if verbose, show back end>>= +if [ -n "$verbose" ]; then echo $backend $noindex $delay $shortxref 1>&2; fi +@ +The first item of initialization is to locate the {\tt noweb} library. +<<initialization>>= +LIB=|LIBDIR| +@ +<<initialization>>= +markup=$LIB/markup backend=$LIB/totex +wrapper= delay= args= markopts= noweboptions= autodefs= verbose= shortxref= +noquote="-noquote" docanchor= noindex="-noindex" +filtera= filterb= filterc= filterd= filtere= filterf= filterg= +# following supported by change in totex back end +noquote= +@ +I make two passes over the arguments so as not to require that options +be given in a certain order +<<scan options and arguments>>= +pass2setcmd="set ignore" +for i do + pass2setcmd="$pass2setcmd '$i'" +done +arg= +while [ $# -gt 0 ]; do + case $1 in + <<first pass {\tt noweave} options>> + -) arg="$arg $1" ;; + -*) echo "$0: Unrecognized option '$1'" 1>&2 ; <<show usage>>; exit 1 ;; + *) arg="$arg $1" ;; + esac + shift +done +<<insist first-pass options are self-consistent>> +if [ -z "$wrapper" ]; then wrapper=latex; fi +eval $pass2setcmd +shift +while [ $# -gt 0 ]; do + case $1 in + <<second pass {\tt noweave} options>> + esac + shift +done +<<old scan options and arguments>>= +pass2args="$*" +while [ $# -gt 0 ]; do + case $1 in + <<first pass {\tt noweave} options>> + -*) echo "$0: Unrecognized option '$1'" 1>&2 ; <<show usage>>; exit 1 ;; + *) arg="$arg $1" ;; + esac + shift +done +if [ -z "$wrapper" ]; then wrapper=latex; fi +if [ -n "$pass2args" ]; then + set ignoreme $pass2args + shift + while [ $# -gt 0 ]; do + case $1 in + <<second pass {\tt noweave} options>> + esac + shift + done +fi +<<add [[$newopt]] to [[noweboptions]]>>= +if [ -z "$noweboptions" ] ; then noweboptions="$newopt" +else noweboptions="$noweboptions,$newopt" +fi +<<first pass {\tt noweave} options>>= +-latex) if [ "$wrapper" != "none" ]; then wrapper=latex; fi ;; +-tex) wrapper=tex ;; +-html) if [ "$wrapper" != "none" ]; then wrapper=html; fi; + backend="$LIB/tohtml -localindex" + noquote=""; docanchor="-docanchor 10" ;; +-latex+html) if [ "$wrapper" != "none" ]; then wrapper=latex; fi; + backend="$LIB/tohtml -localindex -raw" + noquote=""; docanchor="-docanchor 10" ;; +-ascii) wrapper=none + backend="$LIB/toascii" ;; +-troff) wrapper=mm + backend="$LIB/toroff" ;; +-n) wrapper=none ;; +-backend) backend="$2" ; shift ;; +-markup) markup="$2" ; shift ;; +@ +Note some versions of echo break on [[echo "-n..."]], echoing nothing +at all. The leading space is claimed to prevent this problem. +<<option printout for usage>>= +echo "-latex Emit LaTeX with headers and trailers (default)." 1>&2 +echo "-tex Emit plain TeX with headers and trailers." 1>&2 +echo "-html Emit HTML with headers and trailers." 1>&2 +echo "-latex+html Assume LaTeX in documentation, but use HTML in code." 1>&2 +# echo "-ascii Emit ASCII." 1>&2 +echo "-troff Emit troff (actually GNU groff)." 1>&2 +echo " -n Don't use any header or trailer." 1>&2 +echo "-markup frontend Parse input with frontend (e.g., numarkup)." 1>&2 +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-latex +Emit LaTeX, including wrapper in +.B article +style with the +.B noweb +package and page style. (Default) +.TP +.B \-tex +Emit plain TeX, including wrapper with +.B nwmac +macros. +.TP +.B \-html +Emit HTML, using HTML wrapper. +The output is uninteresting without \fB-index\fP or \fB-x\fP. +The tags \fB<nowebchunks>\fP and \fB<nowebindex>\fP, on lines by themselves, +produce a list of chunks and an index of identifiers, respectively. +If these tags are not present, the list and index are placed at the end of the file. +.TP +.B \-latex+html +Assume documentation chunks are LaTeX, but generate HTML for code chunks, +suitably marked so conversion with +.I latex2html(1) +yields reasonable output. +A LaTeX wrapper is implied, but can be turned off with \fB-n\fP. +.I Use of this option is +.B deprecated; +use +.B \-html +with +.B "\-filter l2h" +instead. +<<noweave man page option table>>= +.TP +.B \-troff +Emit +.IR troff (1) +markup (with no wrapper). +The result should be processed with +.IR noroff (1). +Bug reports for +.B \-troff +to Arnold Robbins +.B <arnold@skeeve.com>. +<<bogus noweave man page option table>>= +.TP +.B \-ascii +Emit ASCII (with no wrapper). +Bug reports for +.B \-ascii +to Phil Bewig +.B <pbewig@netcom.com>. +<<noweave man page option table>>= +.TP +.B \-n +Don't use any wrapper (header or trailer). +This option is useful when \fInoweave\fP's output will be +a part of a larger document. +See also +.B \-delay. +@ \fi +A common bug seems to be using both [[-x]] and [[-index]] on the same +command line, so I complain about it. +<<insist first-pass options are self-consistent>>= +if [ -n "$using_xref" -a -n "$using_index" ]; then + echo "$0: you may not use -x with -index or -indexfrom (drop the -x)" 1>&2 + exit 1 +fi +<<initialization>>= +using_index= using_xref= +@ +<<first pass {\tt noweave} options>>= +-filter) shift ;; +-x) using_xref=1 ;; +-index) noindex= ; using_index=1 ;; +-indexfrom) shift; noindex= ; using_index=1 ;; +<<second pass {\tt noweave} options>>= +-filter) + newfilter="$2" ; shift ; <<add [[$newfilter]] (could overflow)>> ;; +-x) newfilter="$LIB/noidx $docanchor $delay"; <<add [[$newfilter]] (could overflow)>> ;; +-index) + newfilter="$LIB/finduses $noquote" ; <<add [[$newfilter]] (could overflow)>> + newfilter="$LIB/noidx $docanchor $delay"; <<add [[$newfilter]] (could overflow)>> ;; +-indexfrom) + newfilter="$LIB/finduses $noquote $2"; <<add [[$newfilter]] (could overflow)>> + newfilter="$LIB/noidx $docanchor $delay"; <<add [[$newfilter]] (could overflow)>> + shift ;; +<<option printout for usage>>= +echo "-x Use the default cross-referencer (needs LaTeX or HTML)." 1>&2 +echo "-index Create index using identifiers defined in input files." 1>&2 +echo "-indexfrom defs Create index of identifers listed in file defs." 1>&2 +echo "-filter cmd Filter through 'cmd' before weaving; cmd could prettyprint" 1>&2 +echo " or perform other functions." 1>&2 +@ \iffalse +<<noweave man page indexing options>>= +.TP +.B \-x +For +.I LaTeX, +add a page number to each chunk name identifying the location of that +chunk's definition, and emit cross-reference information relating definitions and uses. +For +.I HTML, +create hypertext links between uses and definitions of chunks. +When +.B noweave -x +is used with +.I LaTeX, +the control sequence +.B "\\\\nowebchunks" +expands to a sorted list of all code chunks. +.TP +.B \-index +Build cross-reference information (or hypertext links) for defined identifiers. +Definitions are those found in the input files by +.B \-autodefs +.I language +or by +.BR \-filter btdefn. +Requires +.I LaTeX +or +.I HTML. +.B \-index +implies +.B \-x; +including both will generate strange-looking output. +.I noweave +does not generate +cross-references to identifiers that appear in quoted code (\fB@[[\fP...\fB@]]\fP), +but it does generate hypertext links. +When +.B noweave -index +is used with +.I LaTeX, +the control sequence +.B "\\\\nowebindex" +expands to an index of identifiers. +.TP +.B \-indexfrom \fIindex\fP +Like +.B \-index, +but the identifiers to be indexed are taken from file \fIindex\fP. +See +.I noindex(1). +<<noweave man page option table>>= +.TP +.B \-filter \fIcmd\fP +Filters the +.I noweb +source through +.I cmd +after converting it to tool form and before converting to +.I TeX. +.I noweave +looks for +.I cmd +first on the user's +.B PATH, +then in +.B |LIBDIR|. +Such filters +can be used to add features to +.I noweave; +for an example, see +.B |LIBDIR|/noxref.krom. +.I Noweave +supports up to four filters; one can get more by shell trickery, +for example, \fB-filter "icon.filter | noidx"\fP. +The \fB-autodefs\fP, +\fB-x\fP, \fB-index\fP, and \fB-indexfrom\fP options are implemented as filters. +Filters are executed with the shell's +.B eval +command, so +.I cmd +should be quoted accordingly. +<<description of -markup option>> +@ \fi +Note that it would be appropriate to look for autodefs +using [[[ -x $newfilter ]]], +but that stupid DEC Ultrix doesn't support [[test -x]], so the best I can +do in a portable way is [[test -r]]. +<<first pass {\tt noweave} options>>= +-autodefs) newfilter="$LIB/autodefs.$2" + if [ -r $newfilter ]; then <<add [[$newfilter]] (could overflow)>> + else echo "$0: don't know how to find definitions for '$2'" 1>&2; exit 1 + fi + shift + ;; +-showautodefs) <<print all legal [[-autodefs]] or complain>>; exit 1 ;; +<<option printout for usage>>= +echo "-autodefs lang Source is in language 'lang'; find definitions automatically." 1>&2 +echo "-showautodefs List languages that can be used with -autodefs" 1>&2 +@ \iffalse +<<noweave man page indexing options>>= +.TP +.B \-autodefs \fIlang\fP +Discover identifier definitions automatically. +Code in chunks must be in language \fIlang\fP. +Permissible \fIlang\fPs vary but may include +.B tex +or +.B icon. +Useless without +.B \-index, +which it must precede. +.TP +.B \-showautodefs +Show values of \fIlang\fP usable with \fB-autodefs\fP. +@ \fi +Same note as above regarding [[test -x]] vs [[test -r]]. +<<print all legal [[-autodefs]] or complain>>= +foundautodefs= +for i in $LIB/autodefs.*; do + if [ -r $i ]; then + echo "This `basename $0` supports -autodefs $i" | + sed "s@$LIB/autodefs\.@@" 1>&2 + foundautodefs=$i + fi +done +if [ -z "$foundautodefs" ]; then + echo "This `basename $0` does not support -autodefs" +fi +@ +Here's an embarrassing hack --- if we spot \verb+-option shortxref+ +or \verb+-option longxref+ on the +command line, we pass something suitable to the back end, in case we're doing +HTML. +<<first pass {\tt noweave} options>>= +-option) newopt="$2"; shift + if [ "X$newopt" = "Xshortxref" ]; then shortxref="-shortxref"; fi + if [ "X$newopt" = "Xlongxref" ]; then shortxref="-longxref"; fi + <<add [[$newopt]] to [[noweboptions]]>> ;; +<<option printout for usage>>= +echo '-option opt Add \noweboptions{opt} to header (latex only)' 1>&2 +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-option \fIopt\fP +Adds \fB\enoweboptions{\fP\fIopt\fP\fB}\fP to the +.I LaTeX +header. +See +.I nowebstyle(1) +for values of +.I opt. +Normally useful only with the +.B \-latex +option, but +.B "\-option longxref" +works black magic with +.B \-html. +@ \fi +<<first pass {\tt noweave} options>>= +# -nodelay) delay= ;; +-delay) delay="-delay" ; wrapper=none ;; +<<option printout for usage>>= +echo "-delay Delay markup until after first documentation chunk." 1>&2 +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-delay +By default, +.I noweave +puts file-name and other information into the output before the first chunk +of the program. +.B \-delay +delays that information until after the first documentation chunk, making +act a little bit like the +.I WEB +``limbo.'' +The option is typically used to enable a user to put a specialized +.I LaTeX +.B "\\\\documentclass" +command and other preamble material in the first documentation chunk (i.e., +.I before +the first @ sign). +This option also forces trailing cross-referencing information to be emitted +just before the final chunk, instead of at the end of the document; +the final chunk is expected to contain +.B "\\\\end{document}." +The +.B \-delay +option implies the +.B \-n +option. +@ \fi +% .TP +% .B \-nodelay +% Don't delay, put file-name and other information right after header. (Default) +% @ \fi +<<first pass {\tt noweave} options>>= +-t*) markopts="$markopts $1" ;; +<<option printout for usage>>= +echo "-tk Expand tab stops every k columns" 1>&2 +echo "-t Copy tabs to the output" 1>&2 +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-t\fIk\fP +Expand tabs with stops every \fIk\fP columns. +(Default is to expand every 8 columns.) +.TP +.B \-t +Copy tabs to the output. +@ \fi +<<first pass {\tt noweave} options>>= +-v) echo 'RCS version name $Name: $' 1>&2 + echo 'RCS id $Id: noweave.nw,v 1.7 2008/10/06 01:03:24 nr Exp $' 1>&2 + verbose=1 ;; +<<option printout for usage>>= +echo "-v Print pipeline and RCS info on standard error" 1>&2 +@ \iffalse +<<noweave man page option table>>= +.TP +.B \-v +Print the pipeline and RCS info on standard error. +@ \fi +\iffalse +<<man page: WEAVING section>>= +Output from \fInoweave\fP can +be used in \fITeX\fP documents that +.B "\\\\input nwmac," +in \fILaTeX\fP documents that use the +.B noweb +package (see \fInowebstyle(1)\fP), +and in \fIHTML\fP documents to be browsed with +.I Mosaic(1). +.I Noweave +treats code chunks somewhat like +.I LaTeX list environments. +If the ``\fB@ \fP'' that terminates a code chunk is followed immediately by text, +that text follows the code chunk without a paragraph break. +If the rest of the line is blank, +.I noweave +puts +.I TeX +into ``vertical mode,'' and later text starts a fresh, indented paragraph. +.PP +No page breaks occur in the middle of code chunks unless necessary to avoid +an overfull vbox. +The documentation chunk immediately preceding a code chunk appears on +the same page as that code chunk unless doing so would violate the previous rule. +.PP +.I Noweave +inserts no extra newlines in its \fITeX\fP output, so the line numbers given +in +.I TeX +error messages are the same as those in the input file. +.PP +.I noweave +has +options that dictate choice of +formatter +and that support different formatting idioms and tools. +Basic options are described here; options related to index +and cross-reference information are described in the +INDEXING AND CROSS-REFERENCE section. +<<noweave man page option table>> +@ +<<man page: INDEXING AND CROSS-REFERENCE section>>= + +@ \fi +<<add [[$newfilter]] (could overflow)>>= +if [ -z "$filtera" ]; then + filtera="$newfilter" +elif [ -z "$filterb" ]; then + filterb="$newfilter" +elif [ -z "$filterc" ]; then + filterc="$newfilter" +elif [ -z "$filterd" ]; then + filterd="$newfilter" +elif [ -z "$filtere" ]; then + filtere="$newfilter" +elif [ -z "$filterf" ]; then + filterf="$newfilter" +elif [ -z "$filterg" ]; then + filterg="$newfilter" +else + echo "$0: ran out of filters" 1>&2 + exit 1 +fi +<<show usage>>= +echo "Usage: $0 [options] [files]" 1>&2 +echo "Options recognized are:" 1>&2 +<<option printout for usage>> +@ +To avoid inserting any extra newlines into the output, +I use [[@literal]]to insert headers and trailers. +The altered [[PATH]] provides a convenience to people who want to +use simple names to refer to filters in [[$LIB]]. +<<emit markup on standard output>>= +( +header= +case $wrapper in + none) ;; + latex) header="@header $wrapper $noweboptions" + trailer="@trailer $wrapper" ;; + *) header="@header $wrapper$arg" + trailer="@trailer $wrapper" ;; +esac +if [ -n "$header" ]; then echo "$header"; fi +PATH="$PATH:$LIB" +export PATH +<<if verbose, make noise about pipeline>> +<<if verbose, show back end>> +if [ -z "$filtera" ]; then + $markup $markopts $arg +elif [ -z "$filterb" ]; then + $markup $markopts $arg | eval $filtera +elif [ -z "$filterc" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb +elif [ -z "$filterd" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc +elif [ -z "$filtere" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd +elif [ -z "$filterf" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd | eval $filtere +elif [ -z "$filterg" ]; then + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd | eval $filtere | eval $filterf +else + $markup $markopts $arg | eval $filtera | eval $filterb | eval $filterc | + eval $filterd | eval $filtere | eval $filterf | + eval $filterg +fi +if [ -n "$trailer" ]; then echo $trailer; fi +) +<<if verbose, make noise about pipeline>>= +if [ -n "$verbose" ]; then + if [ -n "$header" -o -n "$trailer" ]; then + parenpfx="(" + if [ -n "$header" ]; then echo "(echo $header" 1>&2; parenpfx=" "; fi + echo "$parenpfx$markup" $markopts $arg 1>&2 + if [ -n "$trailer" ]; then echo " echo $trailer" 1>&2; fi + echo ") |" 1>&2 + else + echo $markup $markopts $arg '|' 1>&2 + fi + if [ -n "$filtera" ]; then echo $filtera '|' 1>&2; fi + if [ -n "$filterb" ]; then echo $filterb '|' 1>&2; fi + if [ -n "$filterc" ]; then echo $filterc '|' 1>&2; fi + if [ -n "$filterd" ]; then echo $filterd '|' 1>&2; fi + if [ -n "$filtere" ]; then echo $filtere '|' 1>&2; fi + if [ -n "$filterf" ]; then echo $filterf '|' 1>&2; fi + if [ -n "$filterg" ]; then echo $filterg '|' 1>&2; fi +fi diff --git a/web/noweb/src/shell/noweave.sgi b/web/noweb/src/shell/noweave.sgi new file mode 100644 index 0000000000..49988461a1 --- /dev/null +++ b/web/noweb/src/shell/noweave.sgi @@ -0,0 +1,74 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# options: -l == use LaTeX -x == generate LaTeX cross-reference goo + +LIB=|LIBDIR| +latex=0 tex=1 xref=0 +args= +markopts= + +for i do + case $i in + -l) latex=1 ; tex=0 ;; + -n) latex=0 ; tex=0 ;; + -x) xref=1 ; if [ $tex -eq 1 ]; then tex=0 ; latex=1; fi ;; + -t*) markopts="$markopts $i" ;; + -*) echo "$0: Unrecognized argument '$i'" 1>&2 ; exit ;; + *) arg="$arg $i" ;; + esac +done + +if [ $tex -eq 1 ]; then echo "\\input nwmac"; fi +if [ $latex -eq 1 ]; then + echo "\\documentstyle[noweb]{article}"; + echo '\\begin{document}' +fi + +if [ $xref -eq 0 ]; then + $LIB/markup $markopts $arg +else + $LIB/markup $markopts $arg | $LIB/noxref +fi | nawk ' +BEGIN { code=0 ; quoting=0 } +/^@begin code/ { code=1 ; printf "\\begincode{%s}\n", substr($0, 13) } +/^@begin docs/ { printf "\\begindocs{%s}\n", substr($0, 13) } +/^@end code/ { code=0 ; printf "\\endcode\n" } +/^@end docs/ { printf "\\enddocs\n" } +/^@text / { line = substr($0, 7) + if (code || quoting) { + gsub("\\\\", "\\\\", line) + gsub("{", "\\{", line) ; gsub("}", "\\}", line) + } + printf "%s", line + } +/^@nl$/ { printf "\n" } +/^@defn / { name = substr($0, 7) + printf "\\moddef{%s}\\%sendmoddef", convquotes(name), defns[name] + defns[name] = "plus" + } +/^@use / { printf "\\LA{}%s\\RA{}", convquotes(substr($0, 6)) } +/^@quote$/ { quoting = 1 ; printf "\\code{}" } +/^@endquote$/ { quoting = 0 ; printf "\\edoc{}" } +/^@file / { printf "\\filename{%s}\n", substr($0, 7) } +/^@literal / { printf "%s", substr($0, 10) } +function convquotes(s, r, i) { + r = "" + while (i = index(s, "[[")) { + r = r substr(s, 1, i-1) "\\code{}" + s = substr(s, i+2) + if (i = match(s, "\\]\\]+")) { + r = r substr(s, 1, i-1+RLENGTH-2) "\\edoc{}" + s = substr(s, i+RLENGTH) + } else { + r = r s "\\edoc{}" + s = "" + } + } + return r s +}' +status=$? +if [ $tex -eq 1 ]; then echo "\\bye"; fi +if [ $latex -eq 1 ]; then echo '\\end{document}' ; fi +exit $status diff --git a/web/noweb/src/shell/noweave.simple b/web/noweb/src/shell/noweave.simple new file mode 100644 index 0000000000..b5a55e9f5a --- /dev/null +++ b/web/noweb/src/shell/noweave.simple @@ -0,0 +1,48 @@ +#!/bin/sh + +LIB=|LIBDIR| +awk=nawk + +for i do + case "$i" in + -*) ;; # ignore + *) files="$files $i" ;; + esac +done + +$LIB/markup $files | $awk ' +BEGIN { code=0 + print "\\documentstyle{article}" + print "\\newcommand{\\fragment}[1]{{\\sl$\\langle$#1\\/$\\rangle$}}" + print "\\begin{document}" + } +END { if (code) print "\\end{trivlist}" + print "\\end{document}" + } +/^@quote$/ { printf "\\verb@"} +/^@endquote$/ { printf "@" } +/^@begin code/ { if (!code) print "\\begin{trivlist}\\raggedright\\obeylines\\leftskip=2em\\small\\item[]%"; code=1 } +/^@end code/ { } +/^@begin docs/ { if (code) print "\\end{trivlist}"; code=0 } +/^@end docs/ { } +/^@defn / { gsub(/\[\[/, "\\verb@"); gsub(/]]/, "@") + name = substr($0,7) + printf "\\hspace{-2em}" + printf "\\fragment{%s}", name + defs[name] += 1 + if (defs[name] > 1) + printf "$+\\!\\!\\equiv$" + else + printf "$\\equiv$" + printf "\\index{\\fragment{%s}}", name + } +/^@use / { gsub(/\[\[/, "\\verb@"); gsub(/]]/, "@") + name = substr($0,6) + printf "\\fragment{%s}", name + printf "\\index{\\fragment{%s}}", name + } +/^@literal / { printf "%s", substr($0, 10) } +/^@nl$/ { print ""} +/^@text / { if (code) printf "\\verb@%s@", substr($0,7) + else printf "%s", substr($0,7) }' + diff --git a/web/noweb/src/shell/noweb b/web/noweb/src/shell/noweb new file mode 100644 index 0000000000..0264a46905 --- /dev/null +++ b/web/noweb/src/shell/noweb @@ -0,0 +1,34 @@ +#!/bin/sh +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +# $Id: noweb.nw,v 1.3 1997/09/13 20:29:56 nr Exp $ +# $Name: $ +LIB=|LIBDIR| +markup=$LIB/markup mntopt=-L status=0 tex=1 output=1 +while [ $# -gt 0 ]; do + case $1 in + -to|-ot) tex= output= ; shift ;; + -t) tex= ; shift ;; + -o) output= ; shift ;; + -L*) mntopt="$1" ; shift ;; + -markup) markup="$2" ; shift; shift ;; + -*) echo "Unrecognized option $1" 1>&2; exit 1 ;; + *) break ;; + esac +done +if [ $# -eq 0 ]; then echo "Usage: $0 [-L[fmt] -t -o] file [...]" 1>&2; exit 1; fi + +for source do + if [ -n "$output" ]; then + PATH="$PATH:$LIB" $markup -t "$source" | $LIB/mnt -t8 "$mntopt" -all || status=1 + fi + if [ -n "$tex" ]; then + texname=`echo "$source" | sed '/\./s/\.[^.]*$//'` + texname="$texname.tex" + PATH="$PATH:$LIB" $markup "$source" | $LIB/finduses -noquote | $LIB/noidx -delay | + nawk '{print} + /^@defn [^ ]*$/ { print "@literal \\let\\nwnotused=\\nwoutput{}" }' | + $LIB/totex -delay | cpif $texname || status=1 + fi +done +exit $status diff --git a/web/noweb/src/shell/noweb.nw b/web/noweb/src/shell/noweb.nw new file mode 100644 index 0000000000..1f258cb755 --- /dev/null +++ b/web/noweb/src/shell/noweb.nw @@ -0,0 +1,74 @@ +\section{Generating all outputs from a {\tt noweb} file} +The copyright applies both to the {\tt noweb} source and to the +generated shell script. +<<copyright notice>>= +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. +@ +Here's the organization of the source: +<<noweb>>= +#!/bin/sh +<<copyright notice>> +# $Id: noweb.nw,v 1.3 1997/09/13 20:29:56 nr Exp $ +# $Name: $ +LIB=|LIBDIR| +markup=$LIB/markup mntopt=-L status=0 tex=1 output=1 +<<consume options, setting [[tex]], [[output]], [[mntopt]], and [[markup]]>> +if [ $# -eq 0 ]; then <<complain of no args and exit>>; fi + +for source do + if [ -n "$output" ]; then + PATH="$PATH:$LIB" $markup -t "$source" | $LIB/mnt -t8 "$mntopt" -all || status=1 + fi + if [ -n "$tex" ]; then + <<write {\TeX} output>> || status=1 + fi +done +exit $status +@ +{\tt noweb} could be modified to follow two Knuth-like and {\tt nuweb}-like conventions: +\begin{itemize} +\item +Treat multiple white space within chunk names as single blanks, and +ignore leading and trailing whitespace. +\item +Permit chunk names to be abbreviated by writing an initial subsequence +of the name followed by ``[[...]]''. +\end{itemize} +The modification is simple; just add these two programs to the +pipeline. +([[disambiguate]] is an Icon program found in the [[icon]] directory +of the [[noweb]] distribution.) +<<compress multiple blanks in uses and defns>>= +sed -e '/^@[ud][se][ef][ n]/s/[ ][ ]*/ /g' \ + -e '/^@[ud][se][ef][ n]/s/ $//' +<<disambiguate uses and defns ending in three dots>>= +$LIB/disambiguate +@ +When writing {\TeX} output, I call [[$markup]] a second time to expand tabs. +%% timings on nuweb +% markup 0.4u 0.1s +% noxref 3.9u 1.0s +% nawk 6.5u 0.2s +<<write {\TeX} output>>= +texname=`echo "$source" | sed '/\./s/\.[^.]*$//'` +texname="$texname.tex" +PATH="$PATH:$LIB" $markup "$source" | $LIB/finduses -noquote | $LIB/noidx -delay | +nawk '{print} +/^@defn [^ ]*$/ { print "@literal \\let\\nwnotused=\\nwoutput{}" }' | +$LIB/totex -delay | cpif $texname +<<consume options, setting [[tex]], [[output]], [[mntopt]], and [[markup]]>>= +while [ $# -gt 0 ]; do + case $1 in + -to|-ot) tex= output= ; shift ;; + -t) tex= ; shift ;; + -o) output= ; shift ;; + -L*) mntopt="$1" ; shift ;; + -markup) markup="$2" ; shift; shift ;; + -*) echo "Unrecognized option $1" 1>&2; exit 1 ;; + *) break ;; + esac +done +<<complain of no args and exit>>= +echo "Usage: $0 [-L[fmt] -t -o] file [...]" 1>&2; exit 1 +@ diff --git a/web/noweb/src/shell/nuweb2noweb b/web/noweb/src/shell/nuweb2noweb new file mode 100644 index 0000000000..cf10ed2cc8 --- /dev/null +++ b/web/noweb/src/shell/nuweb2noweb @@ -0,0 +1,7 @@ +#!/bin/sh + +# convert nuweb to noweb + +LIB=|LIBDIR| + +$LIB/numarkup "$@" | $LIB/unmarkup diff --git a/web/noweb/src/shell/roff.nw b/web/noweb/src/shell/roff.nw new file mode 100644 index 0000000000..24ddee69f3 --- /dev/null +++ b/web/noweb/src/shell/roff.nw @@ -0,0 +1,731 @@ +.nr N 4 +.nr O 1i +.nr P 2 +.nr W 6.5i +.S 18 18 +'ce +Converting noweb markup to troff markup +.sp 0.5i +.S 12 15 +.PH "'Converting noweb markup to troff markup''%'" +.P +Toroff is a noweb backend for formatting text with troff +markup. Toroff was written by Phil Bewig ([[pbewig@netcom.com]]) +during April, 1996, and contributed to Norman Ramsey's noweb +literate programming system. Liam Quin ([[lee@sq.com]]) provided technical +assistance with troff. +Norman Ramsey made the code easier to port and made it conform with +his idea of the noweb philosophy. +Arnold Robbins ([[arnold@skeeve.com]]) modified it for use with +GNU troff (hopefully improving portability even more), +improved the flexibility of the noroff script, +and added extra explanation about the gory troff details. +.P +The high-level view of toroff is that it comes in two parts. +The first part is an ordinary back end, which produces +troff augmented with special comments, using the [[.tm]] macro. +The second part, [[noroff]], runs troff, with all of its +pre-processors and post-processors. +[[noroff]] also processes the +comments in to a [[tags]] file. +Moreover, [[noroff]] gathers from the tags file the +cross-referencing information gathered on the previous formatting run. +The trick that makes the whole thing work is that the +troff markup added by toroff causes cross-referencing +information to be written to standard error during the formatting run; +the [[noroff]] script cleverly gathers this material into +the [[tags]] file, +which can be used as input to the next +formatting run. +As with LaTeX, the cross-referencing information is always one +formatter run behind, and to get a consistent document you must keep +running [[noroff]] until you reach a fixed point. +([[noroff]] ought to provide some help for this, to show when the tags +file has changed, for example.) +.HU "The shell wrapper" +Toroff is implemented as an awk program in a shell wrapper, and +uses a macro package to control the final formatting. In the following +discussion, a sample macro package, suitable for use with either [[-mm]] +or [[-ms]], is presented alongside the awk code which calls the +macros. Users may wish to modify the sample macros to suit local +preferences. Many do-nothing macros are provided as user exits so +knowledgeable users can modify document formatting without changing +the awk program. +<<toroff>>= +#!/bin/sh +LIB=|LIBDIR| +AWK=nawk +<<process arguments>> +<<invoke awk program>> +@ .P +Processing arguments is simple. +The only effect of the [[-delay]] flag is to control placement of +the ``begin document'' document macro; +the noroff script supplies the command to source the noroff macros +as the very first line. This avoids groff warnings about undefined +strings in the generated markup for the indexing and cross-references. +<<process arguments>>= +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toroff" 1>&2; + exit 1;; + esac +done +@ %def delay noindex +.P +Invoking the awk program is hard, because the program uses both +single quotes and double quotes, so neither can be used to protect the +other, and quoting each quote is ugly. The pragmatic solution is to +copy the awk program into a temporary file, using a shell here-document. +<<invoke awk program>>= +awkfile=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +trap 'rm -f $awkfile' 0 1 2 10 14 15 +cat > $awkfile << 'EOF' +<<awk program>> +EOF +$AWK -f $awkfile -v delay=$delay noindex=$noindex +@ %def awkfile tagsfile +.HU "The main program" +The overall structure of the awk program is simple. The [[tags]] +file is processed in the [[BEGIN]] action, and the noweb source is +processed by awk's pattern-action loop. +<<awk program>>= +<<read and process noweb source>> +<<functions>> +@ .HU "Beginning and ending the document" +The [[-delay]] option allows the initial document chunk to be processed +before invoking the ``begin document'' macro, +so that necessary initialization may +be performed. The only commands which are useful in such an initial chunk +are bare troff commands. The delay is handled by special patterns for +the initial document chunk. Because several noweb files can be +processed in the same formatting run, there can be several document +chunks numbered zero. The later ones are not treated specially, by the +simple expedient of turning off [[delay]] after the first one. This +code also handles the [[trailer]] at the end of the document. +<<read and process noweb source>>= +/^@begin docs 0$/ { if (delay) next } +/^@end docs 0$/ { if (delay) { <<start document>>; delay = 0; next } } +/^@header m/ { if (!delay) { <<start document>> } } +/^@trailer/ { print ".ENDOFDOCUMENT" } +<<tmac.w>>= +.de ENDOFDOCUMENT +.. +<<start document>>= +printf ".BEGINNINGOFDOCUMENT\n" +<<tmac.w>>= +.de BEGINNINGOFDOCUMENT +.. +@ .HU "Labels, references, and section numbers" +Here is the code to number code chunks in the page-number-and-letter +style used by noweb. The current page number is always stored in +troff's [[%]] number register, and we will arrange an +auto-incrementing number register [[SECTIONLETTER]] which is +reset at each top-of-page and is formatted as lower-case alphabetic +characters using troff's [[.af]] request. The top-of-page trap is +sprung one unit below the top of the page in order not to interfere with +the top-of-page macros of [[-mm]] or [[-ms]]. +<<read and process noweb source>>= +/^@xref label/ { lastxreflabel = $3 } +/^@xref ref/ { lastxrefref = tag(substr($0, 11)) } +@ %def lastxreflabel lastxrefref +<<tmac.w>>= +.de RESETLETTER \" reset letter at top of page +.nr SECTIONLETTER 0 1 \" count code sections on same page +.af SECTIONLETTER a \" ... formatted as lower-case alpha +.. +.wh 1u RESETLETTER \" trap just below top of page +@ .P +Note that the link information on the previous chunk shows that this +chunk uses the [[code]] variable, when in fact [[code]] only appears +as text within a comment. This error is an artifact of the language +independence of noweb. +@ .HU "Beginning and ending chunks" +Except for the initial document chunk, which was processed above, all +beginnings and endings of documentation and code chunks are processed +here. Variable [[code]] is [[0]] in text chunks, [[1]] in code +chunks and [[2]] in quoted code. The argument to +the [[.ENDCODECHUNK]] macro is the label which was in effect when the +code chunk was started, which is stored in the variable [[lastdefnlabel]]. +<<read and process noweb source>>= +/^@begin docs/ { printf ".BEGINDOCCHUNK\n" } +/^@end docs/ { printf ".ENDDOCCHUNK\n" } +/^@begin code/ { code = 1; printf ".BEGINCODECHUNK\n" } +/^@end code/ { code = 0; printf ".ENDCODECHUNK %s\n", lastdefnlabel } +@ %def code +.P +The macros related to document chunks are simple: [[.BEGINDOCCHUNK]] does +nothing, and [[.ENDDOCCHUNK]] merely flushes any unprinted output. +<<tmac.w>>= +.de BEGINDOCCHUNK +.. +.de ENDDOCCHUNK +.br +.. +@ .P +A code chunk uses a font, type size, leading, fill and adjust modes, +indent and tab stops which are distinct from those in the documentation. +These environment variables must be saved on entering a code chunk, +appropriate values for a code chunk must be established, and the +original values restored at the end of a code chunk. +<<tmac.w>>= +.de BEGINCODECHUNK +.sp 0.4v +<<save environment>> +<<set local environment>> +.di CODECHUNK +.. +.de ENDCODECHUNK +<<read back code chunk from diversion>> +<<restore environment>> +.. +@ .P +The code below replaces the built-in [[.ta]] tab-setting command with a +custom tab-setting command that allows tab stops to be reset and later +recalled. +(A.R.: I don't see a use for this; furthermore this saves +the new settings in [[ta_saved]], it doesn't save the old ones.) +<<tmac.w>>= +.rn ta real_ta +.de ta +.ds ta_saved \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.real_ta \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +@ .P +The font, type size and leading require two saved values, so +that both the current value and the previous value can be restored. +For those not intimately familiar with troff, [[.f]] is the current +font number, [[.s]] is the current point size, [[.v]] is the current +vertical spacing, [[.u]] is 1 or 0 for fill/no-fill, [[.j]] is the +current adjust mode, and [[.i]] is the current indent mode. +<<save environment>>= +.nr OLDft \\n(.f +.ft P +.nr PREVft \\n(.f +.nr OLDps \\n(.s +.ps +.nr PREVps \\n(.s +.nr OLDvs \\n(.v +.vs +.nr PREVvs \\n(.v +.nr OLDfi \\n(.u +.nr OLDad \\n(.j +.nr OLDin \\n(.in +@ .P +Code is printed in a constant-width font at 80% the size of document text. +Code is collected in fill mode with explicit breaks after each line; +although it might seem more natural to collect lines of code in no-fill +mode, that is not possible here because uses of code chunks must be able +to appear on the same line as actual code. +<<set local environment>>= +.ft CW +.ps \\n(.s*4/5 +.vs \\n(.vu*4u/5u +.in +0.5i +.real_ta 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i +.fi +@ .P +In order to prevent page breaks within code chunks, each code chunk +is read into a diversion and a page break is issued if the code chunk +is too big to fit on the current page. After the diversion is read back +in, when it is known what page the code is printed on, the location of +the code chunk is written using a [[.tm]] command. +(The [[dn]] number register is the height of the most recent diversion, +and [[.t]] is the distance to the next trap.) +<<read back code chunk from diversion>>= +.br \" flush last line of code +.di \" end diversion +.if \\n(dn>\\n(.t .bp \" force form feed if too big +.nf \" no fill mode -- already formatted +.in -0.5i \" don't re-indent when re-reading text +.CODECHUNK \" output body of diversion +.tm ###TAG### \\$1 \\n[%]\\n+[SECTIONLETTER] \" write tag info +.rm CODECHUNK \" reset diversion for next code chunk +@ .P +Finally, here is the code to restore the environment that existed before +the beginning of the code chunk. +<<restore environment>>= +.ft \\n[PREVft] +.ft \\n[OLDft] +.ps \\n[PREVps] +.ps \\n[OLDps] +.vs \\n[PREVvs]u +.vs \\n[OLDvs]u +.if \\n[OLDfi] .fi +.ad \\n[OLDad] +.in \\n[OLDin]u +.real_ta +@ .HU "Text, code, and quoted code" +In text chunks, text is copied from input to output basically unchanged. +However, when quoted code is included in a line of text, the quoted code +is processed by separate macros and it is possible that the continuation +of the line of text after the quoted code begins with a character which +is special to troff at the beginning of a line: a space, period, or +single quote. To prevent problems in this case, variable [[text]] is +set to [[0]] at the beginning of each line and incremented whenever text +is written, and each text after the first is guarded by a non-printing +null character. +<<read and process noweb source>>= +/^@text/ && code == 0 { s = substr($0, 7) + if (text++) printf "\\&" + printf "%s", substr($0, 7) } +/^@nl/ && code != 1 { text = 0; printf "\n" } +@ %def text +.P +In code chunks, backslashes are replaced with troff's [[\e]] escape +sequence and a non-printing null character is added at the beginning of +each text to guard leading periods. If your macro package issues a [[.ec]] +command, this code will have to change; fortunately, both [[-mm]] and +[[-ms]] are nicely-behaved with respect to escape characters. +<<read and process noweb source>>= +/^@text/ && code != 0 { s = substr($0, 7) + gsub(/\\/, "\\e", s) + printf "\\&%s\\c\n", s } +@ .P +Dealing with newlines in code chunks is surprisingly hard. We would like +to print lines of code in [[nofill]] mode, but since noweb's markup +and filter programs may split lines of code, we are forced to use +[[fill]] mode and deal with newlines ourselves. Further, if a line is +too long to print on a single line, it must be split, and the continuation +line right-justified instead of left-justified. All of this processing is +accomplished by calling a macro at each newline; the macro plants a +page-position trap at the next line so that any continuation line will be +right-justified. +(The [[.dt]] command defines a trap for use within the current +diversion, whereas the more common [[.wh]] is for a particular +page position.) +<<read and process noweb source>>= +/^@nl/ && code == 1 { printf ".NEWLINE\n" } +<<tmac.w>>= +.de LINETRAP +.dt \\n[TRAPplace]u \"cancel current trap +'ad r \" right-adjust continuation lines +.. +.de NEWLINE +.dt \\n[TRAPplace]u \" cancel current trap +\& \" end continued word +.br \" flush output +<<plant newline trap>> +.. +@ .P +The newline trap is planted after each definition line, for the first +line of code in the code chunk, and again after each newline in the +code chunk. +(The [[.d]] register indicates the current vertical position +within the diversion.) +<<plant newline trap>>= +.nr TRAPplace \\n(.du+1u \" location of next trap +.dt \\n[TRAPplace]u LINETRAP \" plant trap at next line +.ad l \" left-adjust first line +@ .P +Quoted code in a documentation chunk is printed within its own macros, +which must not cause a break. +<<read and process noweb source>>= +/^@quote/ { code = 2; printf "\\c\n.BEGINQUOTEDCODE\n" } +/^@endquote/ { code = 0; text++; printf ".ENDQUOTEDCODE\n" } +<<tmac.w>>= +.de BEGINQUOTEDCODE +.nr SAVEft \\n(.f +.ft P +.nr SAVEftP \\n(.f +.ft CW +.. +.de ENDQUOTEDCODE +.ft \\n[SAVEftP] +.ft \\n[SAVEft] +.. +@ .HU "Definitions and uses of code chunks" +Definitions and uses of code chunks are handled below. Variable +[[defn[name]]] is set to a plus sign after a definition is printed, so +that continuations of the definition are properly identified. Variable +[[lastxrefref]] is the tag associated with the most-recently-seen +cross-reference label, and refers to the section number of the original +definition of the code chunk. +<<read and process noweb source>>= +/^@defn/ { name = convquote(substr($0, 7)) + lastdefnlabel = lastxreflabel + if (! (name in defn)) + defn[name] = "\\(==" + printf ".DEFINITION %s \"%s\" %s %s\n", + tag(lastdefnlabel), name, lastxrefref, defn[name] + defn[name] = "\\(pl\\(==" } +/^@use/ { name = convquote(substr($0, 6)) + printf "\\c\n" + printf ".USE \"%s\" %s\n", name, lastxrefref } +@ %def name defn lastdefnlabel +@ The original version of toroff used [[\(oG]] and [[\(cG]] for +the double angle bracket characters. These appear to be non-standard, +thus we define our own strings to take their place. (A.R.) +<<tmac.w>>= +.ds o< <\h'-0.1m'< +.ds c> >\h'-0.1m'> +.de DEFINITION +.ti -0.5i +\\fR\\(sc\\$1 \\*(o<\\$2 \\$3\\*(c>\\$4\\fP +<<plant newline trap>> +.. +.de USE +\\fR\\*(o<\\$1 \\$2\\*(c>\\fP\c \" section name and original number +.. +@ .P +Processing of quoted code within definitions and uses is performed by the +[[convquote]] function. This must be processed differently than quoted +code within text because the noweb markup program doesn't emit +[[@quote]] .\|.\|. [[@endquote]] markers within definitions and uses. And, +because the subject string is used as an argument to a macro, it would not +possible to call the [[.BEGINQUOTEDCODE]] and [[.ENDQUOTEDCODE]] macros, +even if noweb did emit the markers within definitions and uses. +.P +The original version just did [[gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s)]], +but this does not catch the case of something like \f(CW[\&[a[i\&]\&]\&]\fP. +A more complicated loop using [[match()]] is in order. +It loops through the string, picking it apart, and replacing +each quoted code section individually. +<<functions>>= +function convquote(s, out, front, mid, tail) { + gsub(/\[\[/, "\\*[BEGINCONVQUOTE]", s) + # gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s) + out = "" + mid = "\\*[ENDCONVQUOTE]" + while (match(s, /\]\]+/) != 0) { + # RLENGTH is length of match, want to remove last two chars + # RSTART is where sequence of ]s begins + tail = substr(s, RSTART + RLENGTH) + if (RLENGTH == 2) # easy + front = substr(s, 1, RSTART - 1) + else + front = substr(s, 1, RSTART - 1 + RLENGTH - 2) + out = out front mid + s = tail + } + out = out s + return out } +# my test program for the revised function - ADR +# BEGIN { str = "abc[[foo[i]]]]]]]]junk" +# print str +# print convquote(str) +# str2 = "nothing here" +# print str2 +# print convquote(str2) +# str3 = "abc[[foo[i]]]]]]]]junk[[bar[i]]more stuff[[baz]]" +# print str3 +# print convquote(str3) +# } +@ %def convquote +<<tmac.w>>= +.ds BEGINCONVQUOTE \f[CW] +.ds ENDCONVQUOTE \fP +@ .HU "Cross-referencing and indexing" +The code for producing the ``hypertext links'' at the end of each +code section is given below: for each type, the beginning of the message +is printed, items are accumulated in a list, and the list is printed +after the number of items in the list is known. The first time any of +the cross-reference or identifier-index messages appears, it is necessary +to reset the point size and leading to the small font used for this +material, which is 80% of the size of code and 64% of the size of text. +All of these lines are part of the diversion which collects a +code chunk. First is the code to report definitions and uses of code. +<<read and process noweb source>>= +/^@xref begindefs/ { <<checkcode>>; printf ".XREFDEFS\n" } +/^@xref beginuses/ { <<checkcode>>; printf ".XREFUSES\n" } +/^@xref notused/ { <<checkcode>>; printf ".XREFNOTUSED\n" } +/^@xref (def|use)item/ { printf ".ADDLIST %s\n", tag($3) } +/^@xref end(def|use)s/ { printf ".PRINTLIST\n"; code = 1 } +<<tmac.w>>= +.de XREFDEFS +This definition continued in +.. +.de XREFUSES +This code used in +.. +.de XREFNOTUSED +This code is not used in this document. +.br +.. +@ .P +The code to report the definitions of identifiers appears below. The +[[if]] in [[@index isused]] prevents index definitions from pointing +to themselves. +<<read and process noweb source>>= +/^@index begindefs/ && !noindex { <<checkcode>>; printf ".INDEXDEF\n" } +/^@index isused/ && !noindex { + if (tag($3) != lastxrefref) printf ".ADDLIST %s\n", tag($3) } +/^@index defitem/ && !noindex { printf ".DEFITEM %s\n.PRINTLIST\n", $3 } +<<tmac.w>>= +.de INDEXDEF +Defines: +.br +.. +.de DEFITEM +.ti +1m +\\*[BEGINCONVQUOTE]\\$1\\*[ENDCONVQUOTE], +.if \\n[NLIST]>0 used in +.. +@ .P +Finally, here is the code to report uses of identifiers. +<<read and process noweb source>>= +/^@index beginuses/ && !noindex { <<checkcode>>; printf ".INDEXUSE\n" } +/^@index isdefined/ && !noindex { lastuse = tag($3) } +/^@index useitem/ && !noindex { + printf ".ADDLIST \"\\*[BEGINCONVQUOTE]%s\\*[ENDCONVQUOTE] %s\"\n", + $3, lastuse } +/^@index enduses/ && !noindex { printf ".PRINTLIST\n" } +@ %def lastuse +<<tmac.w>>= +.de INDEXUSE +Uses +.. +@ .P +The macros [[.ADDLIST s]], which adds string [[s]] to a queued list +waiting to be printed, and [[.PRINTLIST]], which prints the list, +appropriately formatted with commas, are described below. These macros +make use of some interesting troff idioms. [[LIST]] is an array of +strings; the [[n]]-th string in [[LIST]] can be set to [[s]] by +[[.ds LISTn s]]. The expression [[.nr n \\$1]] converts the string +passed as the first argument to a macro to the number [[n]]. Loops +are implemented in troff as recursive macros, as in [[.PRINTITEM]]. +<<tmac.w>>= +.nr NLIST 0 1 \" initialize list index to 0 with auto-increment 1 +.de ADDLIST +.ds LIST\\n+[NLIST] \\$1 +.. +.de PRINTITEM +.nr PLIST \\$1 +.nr PLISTPLUS \\n[PLIST]+1 +.if \\n[NLIST]-\\n[PLIST]<0 not used in this document. +.if \\n[NLIST]-\\n[PLIST]=0 \\*[LIST\\n[PLIST]]. +.if \\n[NLIST]-\\n[PLIST]=1 \ + \\*[LIST\\n[PLIST]] and \\*[LIST\\n[PLISTPLUS]]. +.if \\n[NLIST]-\\n[PLIST]>1 \{ \\*[LIST\\n[PLIST]], +. PRINTITEM 1+\\n[PLIST] \} +.. +.de PRINTLIST +.PRINTITEM 1 +.br +.nr NLIST 0 1 \" re-initialize for next list +.. +@ .P +This code and macro reduces the font size in which the cross-reference +and identifier index information is printed at the end of a code chunk. +The [[code]] variable is set to [[0]] to ensure that [[.STARTXREF]] +is performed only once at the end of each code chunk. This code also +resets the adjustment mode, which was changed to left-adjustment or +right-adjustment by the [[LINETRAP]] macro, and cancels the [[NEWLINE]] +trap at [[TRAPplace]]. +<<checkcode>>= +if (code) { code = 0; printf ".STARTXREF\n" } +<<tmac.w>>= +.de STARTXREF +.ps \\n(.s*4/5 +.vs \\n(.vu*4u/5u +.ft \\n[OLDft] +.ad \\n[OLDad] +.dt \\n[TRAPplace]u +.sp 0.4v +.. +@ .HU "Collecting the lists of chunks and identifiers" +Collecting the lists of chunks and identifiers takes two passes over the +file, because the section numbers which tags refer to aren't known on +the first pass. Therefore, the strategy on the first pass is to write +the chunk and identifier index entries to the [[tags]] file on standard +error, and actually prepare the lists when reading the [[tags]] file on +the second pass. Thus, the first pass code shown here merely copies +the chunk and identifier index entries from the noweb intermediate +file to the [[tags]] file using troff's [[.tm]] command. The +first section below handles chunks, the second section handles +identifiers. +<<read and process noweb source>>= +/^@xref beginchunks/ { printf ".tm ###BEGINCHUNKS###\n" } +/^@xref chunkbegin/ { printf ".tm ###CHUNKBEGIN### %s\n", + substr($0, length($3) + 19) } +/^@xref chunkuse/ { printf ".tm ###CHUNKUSE### %s\n", $3 } +/^@xref chunkdefn/ { printf ".tm ###CHUNKDEFN### %s\n", $3 } +/^@xref chunkend/ { printf ".tm ###CHUNKEND###\n" } +/^@xref endchunks/ { printf ".tm ###ENDCHUNKS###\n" } +<<read and process noweb source>>= +/^@index beginindex/ { printf ".tm ###BEGININDEX###\n" } +/^@index entrybegin/ { printf ".tm ###ENTRYBEGIN### %s\n", + substr($0, length($3) + 20) } +/^@index entryuse/ { printf ".tm ###ENTRYUSE### %s\n", $3 } +/^@index entrydefn/ { printf ".tm ###ENTRYDEFN### %s\n", $3 } +/^@index entryend/ { printf ".tm ###ENTRYEND###\n" } +/^@index endindex/ { printf ".tm ###ENDINDEX###\n" } +@ .HU "The \f[CW]tags\fP file" +The [[tags]] file is re-created at each formatter run by the troff +idiom (some people would call it a trick) of capturing the output of +troff's [[.tm]] command, which writes to the standard error, in a +file via command-line redirection. The code below uses an awk idiom; the +[[sub]] simultaneously tests for a match and deletes the matched text. +<<action for [[tags]] line>>= +{ + if (sub(/^###TAG### / , "")) tags[$1] = $2 + else if (sub(/^###BEGINCHUNKS###/, "")) printf ".de CLIST\n.CLISTBEGIN\n" + else if (sub(/^###CHUNKBEGIN### /, "")) { name = convquote($0) + chunkuse = chunkdefn = "" } + else if (sub(/^###CHUNKUSE### / , "")) chunkuse = chunkuse " " tag($0) + else if (sub(/^###CHUNKDEFN### / , "")) chunkdefn = chunkdefn " " tag($0) + else if (sub(/^###CHUNKEND###/ , "")) + printf ".CITEM \"%s\" \"%s\" \"%s\"\n", name, chunkdefn, chunkuse + else if (sub(/^###ENDCHUNKS###/ , "")) printf ".CLISTEND\n..\n" + else if (sub(/^###BEGININDEX###/ , "")) printf ".de ILIST\n.ILISTBEGIN\n" + else if (sub(/^###ENTRYBEGIN### /, "")) { name = convquote($0) + entryuse = entrydefn = "" } + else if (sub(/^###ENTRYUSE### / , "")) entryuse = entryuse " " tag($0) + else if (sub(/^###ENTRYDEFN### / , "")) entrydefn = entrydefn " " tag($0) + else if (sub(/^###ENTRYEND###/ , "")) { + for (i = 1; i <= split(entrydefn, entryarray); i++) + sub(entryarray[i], "\\*[BEGINDEFN]&\\*[ENDDEFN]", entryuse) + printf ".IITEM \"%s\" \"%s\"\n", name, entryuse } + else if (sub(/^###ENDINDEX###/ , "")) printf ".ILISTEND\n..\n" +} +@ %def tags chunkuse chunkdefn entryuse entrydefn entryarray +.P +The [[sub]] within the [[ENTRYEND]] causes definitions of identifiers +to be italicized, according to the following defined strings. +<<tmac.w>>= +.ds BEGINDEFN \fI +.ds ENDDEFN \fP +@ .P +Lookup of the section number corresponding to a particular tag is +performed by the [[tag]] function. +<<functions>>= +function tag(s) { if (s in tags) return tags[s]; else return "???" } +@ %def tag +@ .P +To use the tags, we have to read the tags file from the back end. +The tags file name is the basename of the file, with the suffix +replaced with [[".nwt"]] (see the toroff script, below). +<<awk program>>= +/^@file / { + if (tagsfile == "") { + tagsfile = substr($0, 7) + sub(".*/", "", tagsfile) + sub(/\.[^.]*$/, "", tagsfile) + tagsfile = tagsfile ".nwt" + while (getline <tagsfile > 0) <<action for [[tags]] line>> + } +} +@ +.HU "Using \\f[CW]toroff\fP" +Toroff is one element of the normal noweave pipeline. Using +toroff in its full generality is hard. The sample program shown +below +allows the user to specify the macro package (although it defaults to +[[-mgm]]), +and allows the user to specify troff pre-processors, +post-processors and options. +(Command line options to [[groff]] can be used to invoke tbl, pic, etc.) +See the section below on how to generate the final formatted document. +.P +To avoid a race condition reading the tags file +(troff is writing to it while the awk program is reading it), +we first copy it for +use by the awk program, and then remove the copy. +The location of [[macrodir]] is likely to require customization. +<<noroff>>= +#!/bin/sh +# +# noroff -- run troff using tags file trick + +ROFF="groff" +AWK=nawk +macrodir=|LIBDIR| +LIB=|LIBDIR| + +opts= + +if [ $# -eq 0 ]; then + echo "Usage: noroff [groff-arguments] files" 1>&2 + exit 1 +fi + +while [ $# -gt 0 ] +do + case $1 in + -*) opts="$opts $1" + shift + ;; + *) # end of options + break; + ;; + esac +done + +if [ "$opts" = "" ] +then + # no options, default to -mm + # groff already defaults to -Tps + opts="-mm" +fi +# otherwise assume user passed in all the arguments they want + +base="`basename $1 | sed '/\./s/\.[^.]*$//'`" +tagsfile="$base.nwt" +(echo ".so $macrodir/tmac.w" +if [ -r "$tagsfile" ]; then + tagstemp=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } + cp $tagsfile $tagstemp + $AWK '<<action for [[tags]] line>> + <<functions>>' $tagstemp + rm -f $tagstemp + fi + cat "$@") | +($ROFF $opts 2>$tagsfile) +sed '/^###[A-Z][A-Z]*###/d' $tagsfile >&2 +@ %def macrodir +@ .HU "Macros for indexing chunks and identifiers" +Toroff creates macros [[.CLIST]] and [[.ILIST]] which insert the +lists of chunks and identifiers, respectively, in the output. These +macros, in turn, call other macros which format the lists. The macros +below cause each item to be written on a separate line, with continuation +lines indented one-quarter inch. +<<tmac.w>>= +.de CLISTBEGIN +.in +0.25i +.. +.de CITEM +.ti -0.25i +.ie '\\$3'' \\*(o<\\$1 \\$2\\*(c> Not used in this document. +.el \\*(o<\\$1 \\$2\\*(c> \\$3 +.br +.. +.de CLISTEND +.in -0.25i +.. +<<tmac.w>>= +.de ILISTBEGIN +.in +0.25i +.. +.de IITEM +.ti -0.25i +\\f[CW]\\$1\\fP: \\$2 +.br +.. +.de ILISTEND +.in -0.25i +.. +@ .HU "Using \f[CW]noweave -troff\fP and \f[CW]noroff\fP" +To produce the final document, +the tags file is needed for both the [[noweave]] +and the [[noroff]] steps. This creates a bit of a boot-strapping +problem, since it is troff that first creates the tags file. +The way to do it is to run [[noweave]] and [[noroff]] in turn, twice. +For example, to make a printable version of this document, you would +run the programs like this. +<<Example run>>= +noweave -index -troff roff.nw > roff.tr +noroff roff.tr > /dev/null # ignore warnings about CLIST and ILIST +noweave -index -troff roff.nw > roff.tr +noroff roff.tr > roff.ps # print this one +@ .HU "Index of chunks and identifiers" +The automatically-generated index of chunks and identifiers for +toroff is shown below. +.S 10 12 +.sp +.CLIST +.sp +.2C +.ILIST diff --git a/web/noweb/src/shell/tmac.w b/web/noweb/src/shell/tmac.w new file mode 100644 index 0000000000..3c75c52b62 --- /dev/null +++ b/web/noweb/src/shell/tmac.w @@ -0,0 +1,171 @@ +.de ENDOFDOCUMENT +.. +.de BEGINNINGOFDOCUMENT +.. +.de RESETLETTER \" reset letter at top of page +.nr SECTIONLETTER 0 1 \" count code sections on same page +.af SECTIONLETTER a \" ... formatted as lower-case alpha +.. +.wh 1u RESETLETTER \" trap just below top of page +.de BEGINDOCCHUNK +.. +.de ENDDOCCHUNK +.br +.. +.de BEGINCODECHUNK +.sp 0.4v +.nr OLDft \\n(.f +.ft P +.nr PREVft \\n(.f +.nr OLDps \\n(.s +.ps +.nr PREVps \\n(.s +.nr OLDvs \\n(.v +.vs +.nr PREVvs \\n(.v +.nr OLDfi \\n(.u +.nr OLDad \\n(.j +.nr OLDin \\n(.in +.ft CW +.ps \\n(.s*4/5 +.vs \\n(.vu*4u/5u +.in +0.5i +.real_ta 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i +.fi +.di CODECHUNK +.. +.de ENDCODECHUNK +.br \" flush last line of code +.di \" end diversion +.if \\n(dn>\\n(.t .bp \" force form feed if too big +.nf \" no fill mode -- already formatted +.in -0.5i \" don't re-indent when re-reading text +.CODECHUNK \" output body of diversion +.tm ###TAG### \\$1 \\n[%]\\n+[SECTIONLETTER] \" write tag info +.rm CODECHUNK \" reset diversion for next code chunk +.ft \\n[PREVft] +.ft \\n[OLDft] +.ps \\n[PREVps] +.ps \\n[OLDps] +.vs \\n[PREVvs]u +.vs \\n[OLDvs]u +.if \\n[OLDfi] .fi +.ad \\n[OLDad] +.in \\n[OLDin]u +.real_ta +.. +.rn ta real_ta +.de ta +.ds ta_saved \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.real_ta \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +.de LINETRAP +.dt \\n[TRAPplace]u \"cancel current trap +'ad r \" right-adjust continuation lines +.. +.de NEWLINE +.dt \\n[TRAPplace]u \" cancel current trap +\& \" end continued word +.br \" flush output +.nr TRAPplace \\n(.du+1u \" location of next trap +.dt \\n[TRAPplace]u LINETRAP \" plant trap at next line +.ad l \" left-adjust first line +.. +.de BEGINQUOTEDCODE +.nr SAVEft \\n(.f +.ft P +.nr SAVEftP \\n(.f +.ft CW +.. +.de ENDQUOTEDCODE +.ft \\n[SAVEftP] +.ft \\n[SAVEft] +.. +.ds o< <\h'-0.1m'< +.ds c> >\h'-0.1m'> +.de DEFINITION +.ti -0.5i +\\fR\\(sc\\$1 \\*(o<\\$2 \\$3\\*(c>\\$4\\fP +.nr TRAPplace \\n(.du+1u \" location of next trap +.dt \\n[TRAPplace]u LINETRAP \" plant trap at next line +.ad l \" left-adjust first line +.. +.de USE +\\fR\\*(o<\\$1 \\$2\\*(c>\\fP\c \" section name and original number +.. +.ds BEGINCONVQUOTE \f[CW] +.ds ENDCONVQUOTE \fP +.de XREFDEFS +This definition continued in +.. +.de XREFUSES +This code used in +.. +.de XREFNOTUSED +This code is not used in this document. +.br +.. +.de INDEXDEF +Defines: +.br +.. +.de DEFITEM +.ti +1m +\\*[BEGINCONVQUOTE]\\$1\\*[ENDCONVQUOTE], +.if \\n[NLIST]>0 used in +.. +.de INDEXUSE +Uses +.. +.nr NLIST 0 1 \" initialize list index to 0 with auto-increment 1 +.de ADDLIST +.ds LIST\\n+[NLIST] \\$1 +.. +.de PRINTITEM +.nr PLIST \\$1 +.nr PLISTPLUS \\n[PLIST]+1 +.if \\n[NLIST]-\\n[PLIST]<0 not used in this document. +.if \\n[NLIST]-\\n[PLIST]=0 \\*[LIST\\n[PLIST]]. +.if \\n[NLIST]-\\n[PLIST]=1 \ + \\*[LIST\\n[PLIST]] and \\*[LIST\\n[PLISTPLUS]]. +.if \\n[NLIST]-\\n[PLIST]>1 \{ \\*[LIST\\n[PLIST]], +. PRINTITEM 1+\\n[PLIST] \} +.. +.de PRINTLIST +.PRINTITEM 1 +.br +.nr NLIST 0 1 \" re-initialize for next list +.. +.de STARTXREF +.ps \\n(.s*4/5 +.vs \\n(.vu*4u/5u +.ft \\n[OLDft] +.ad \\n[OLDad] +.dt \\n[TRAPplace]u +.sp 0.4v +.. +.ds BEGINDEFN \fI +.ds ENDDEFN \fP +.de CLISTBEGIN +.in +0.25i +.. +.de CITEM +.ti -0.25i +.ie '\\$3'' \\*(o<\\$1 \\$2\\*(c> Not used in this document. +.el \\*(o<\\$1 \\$2\\*(c> \\$3 +.br +.. +.de CLISTEND +.in -0.25i +.. +.de ILISTBEGIN +.in +0.25i +.. +.de IITEM +.ti -0.25i +\\f[CW]\\$1\\fP: \\$2 +.br +.. +.de ILISTEND +.in -0.25i +.. diff --git a/web/noweb/src/shell/toroff b/web/noweb/src/shell/toroff new file mode 100644 index 0000000000..ea5212c315 --- /dev/null +++ b/web/noweb/src/shell/toroff @@ -0,0 +1,135 @@ +#!/bin/sh +LIB=|LIBDIR| +AWK=nawk +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toroff" 1>&2; + exit 1;; + esac +done +awkfile=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +trap 'rm -f $awkfile' 0 1 2 10 14 15 +cat > $awkfile << 'EOF' +/^@begin docs 0$/ { if (delay) next } +/^@end docs 0$/ { if (delay) { printf ".BEGINNINGOFDOCUMENT\n"; delay = 0; next } } +/^@header m/ { if (!delay) { printf ".BEGINNINGOFDOCUMENT\n" } } +/^@trailer/ { print ".ENDOFDOCUMENT" } +/^@xref label/ { lastxreflabel = $3 } +/^@xref ref/ { lastxrefref = tag(substr($0, 11)) } +/^@begin docs/ { printf ".BEGINDOCCHUNK\n" } +/^@end docs/ { printf ".ENDDOCCHUNK\n" } +/^@begin code/ { code = 1; printf ".BEGINCODECHUNK\n" } +/^@end code/ { code = 0; printf ".ENDCODECHUNK %s\n", lastdefnlabel } +/^@text/ && code == 0 { s = substr($0, 7) + if (text++) printf "\\&" + printf "%s", substr($0, 7) } +/^@nl/ && code != 1 { text = 0; printf "\n" } +/^@text/ && code != 0 { s = substr($0, 7) + gsub(/\\/, "\\e", s) + printf "\\&%s\\c\n", s } +/^@nl/ && code == 1 { printf ".NEWLINE\n" } +/^@quote/ { code = 2; printf "\\c\n.BEGINQUOTEDCODE\n" } +/^@endquote/ { code = 0; text++; printf ".ENDQUOTEDCODE\n" } +/^@defn/ { name = convquote(substr($0, 7)) + lastdefnlabel = lastxreflabel + if (! (name in defn)) + defn[name] = "\\(==" + printf ".DEFINITION %s \"%s\" %s %s\n", + tag(lastdefnlabel), name, lastxrefref, defn[name] + defn[name] = "\\(pl\\(==" } +/^@use/ { name = convquote(substr($0, 6)) + printf "\\c\n" + printf ".USE \"%s\" %s\n", name, lastxrefref } +/^@xref begindefs/ { if (code) { code = 0; printf ".STARTXREF\n" }; printf ".XREFDEFS\n" } +/^@xref beginuses/ { if (code) { code = 0; printf ".STARTXREF\n" }; printf ".XREFUSES\n" } +/^@xref notused/ { if (code) { code = 0; printf ".STARTXREF\n" }; printf ".XREFNOTUSED\n" } +/^@xref (def|use)item/ { printf ".ADDLIST %s\n", tag($3) } +/^@xref end(def|use)s/ { printf ".PRINTLIST\n"; code = 1 } +/^@index begindefs/ && !noindex { if (code) { code = 0; printf ".STARTXREF\n" }; printf ".INDEXDEF\n" } +/^@index isused/ && !noindex { + if (tag($3) != lastxrefref) printf ".ADDLIST %s\n", tag($3) } +/^@index defitem/ && !noindex { printf ".DEFITEM %s\n.PRINTLIST\n", $3 } +/^@index beginuses/ && !noindex { if (code) { code = 0; printf ".STARTXREF\n" }; printf ".INDEXUSE\n" } +/^@index isdefined/ && !noindex { lastuse = tag($3) } +/^@index useitem/ && !noindex { + printf ".ADDLIST \"\\*[BEGINCONVQUOTE]%s\\*[ENDCONVQUOTE] %s\"\n", + $3, lastuse } +/^@index enduses/ && !noindex { printf ".PRINTLIST\n" } +/^@xref beginchunks/ { printf ".tm ###BEGINCHUNKS###\n" } +/^@xref chunkbegin/ { printf ".tm ###CHUNKBEGIN### %s\n", + substr($0, length($3) + 19) } +/^@xref chunkuse/ { printf ".tm ###CHUNKUSE### %s\n", $3 } +/^@xref chunkdefn/ { printf ".tm ###CHUNKDEFN### %s\n", $3 } +/^@xref chunkend/ { printf ".tm ###CHUNKEND###\n" } +/^@xref endchunks/ { printf ".tm ###ENDCHUNKS###\n" } +/^@index beginindex/ { printf ".tm ###BEGININDEX###\n" } +/^@index entrybegin/ { printf ".tm ###ENTRYBEGIN### %s\n", + substr($0, length($3) + 20) } +/^@index entryuse/ { printf ".tm ###ENTRYUSE### %s\n", $3 } +/^@index entrydefn/ { printf ".tm ###ENTRYDEFN### %s\n", $3 } +/^@index entryend/ { printf ".tm ###ENTRYEND###\n" } +/^@index endindex/ { printf ".tm ###ENDINDEX###\n" } +function convquote(s, out, front, mid, tail) { + gsub(/\[\[/, "\\*[BEGINCONVQUOTE]", s) + # gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s) + out = "" + mid = "\\*[ENDCONVQUOTE]" + while (match(s, /\]\]+/) != 0) { + # RLENGTH is length of match, want to remove last two chars + # RSTART is where sequence of ]s begins + tail = substr(s, RSTART + RLENGTH) + if (RLENGTH == 2) # easy + front = substr(s, 1, RSTART - 1) + else + front = substr(s, 1, RSTART - 1 + RLENGTH - 2) + out = out front mid + s = tail + } + out = out s + return out } +# my test program for the revised function - ADR +# BEGIN { str = "abc[[foo[i]]]]]]]]junk" +# print str +# print convquote(str) +# str2 = "nothing here" +# print str2 +# print convquote(str2) +# str3 = "abc[[foo[i]]]]]]]]junk[[bar[i]]more stuff[[baz]]" +# print str3 +# print convquote(str3) +# } +function tag(s) { if (s in tags) return tags[s]; else return "???" } +/^@file / { + if (tagsfile == "") { + tagsfile = substr($0, 7) + sub(".*/", "", tagsfile) + sub(/\.[^.]*$/, "", tagsfile) + tagsfile = tagsfile ".nwt" + while (getline <tagsfile > 0) { + if (sub(/^###TAG### / , "")) tags[$1] = $2 + else if (sub(/^###BEGINCHUNKS###/, "")) printf ".de CLIST\n.CLISTBEGIN\n" + else if (sub(/^###CHUNKBEGIN### /, "")) { name = convquote($0) + chunkuse = chunkdefn = "" } + else if (sub(/^###CHUNKUSE### / , "")) chunkuse = chunkuse " " tag($0) + else if (sub(/^###CHUNKDEFN### / , "")) chunkdefn = chunkdefn " " tag($0) + else if (sub(/^###CHUNKEND###/ , "")) + printf ".CITEM \"%s\" \"%s\" \"%s\"\n", name, chunkdefn, chunkuse + else if (sub(/^###ENDCHUNKS###/ , "")) printf ".CLISTEND\n..\n" + else if (sub(/^###BEGININDEX###/ , "")) printf ".de ILIST\n.ILISTBEGIN\n" + else if (sub(/^###ENTRYBEGIN### /, "")) { name = convquote($0) + entryuse = entrydefn = "" } + else if (sub(/^###ENTRYUSE### / , "")) entryuse = entryuse " " tag($0) + else if (sub(/^###ENTRYDEFN### / , "")) entrydefn = entrydefn " " tag($0) + else if (sub(/^###ENTRYEND###/ , "")) { + for (i = 1; i <= split(entrydefn, entryarray); i++) + sub(entryarray[i], "\\*[BEGINDEFN]&\\*[ENDDEFN]", entryuse) + printf ".IITEM \"%s\" \"%s\"\n", name, entryuse } + else if (sub(/^###ENDINDEX###/ , "")) printf ".ILISTEND\n..\n" + } + } +} +EOF +$AWK -f $awkfile -v delay=$delay noindex=$noindex diff --git a/web/noweb/src/shell/toroff.nw b/web/noweb/src/shell/toroff.nw new file mode 100644 index 0000000000..b9cdf5d29b --- /dev/null +++ b/web/noweb/src/shell/toroff.nw @@ -0,0 +1,617 @@ +.nr N 4 +.nr O 1i +.nr P 2 +.nr W 6.5i +.fp 1 PR +.fp 2 PI +.fp 3 PB +@ .ft 1 +.S 18 18 +'ce +Converting noweb markup to troff markup +.sp 0.5i +.S 12 15 +.PH "'Converting noweb markup to troff markup''%'" +.P +Toroff is a noweb backend for formatting text with troff +markup. Toroff was written by Phil Bewig ([[pbewig@netcom.com]]) +during April, 1996, and contributed to Norman Ramsey's noweb +literate programming system. Liam Quin ([[lee@sq.com]]) provided technical +assistance with troff. +Norman Ramsey made the code easier to port and made it conform with +his idea of the noweb philosophy. +.P +The high-level view of toroff is that it comes in two parts. +The first part is an ordinary back end, which produces +troff augmented with special comments, using the [[.tm]] macro. +The second part, [[noroff]], runs troff, with all of its +pre-processors and post-processors. +[[noroff]] also processes the +comments in to a [[tags]] file. +Moreover, [[noroff]] gathers from the tags file the +cross-referencing information gathered on the previous formatting run. +The trick that makes the whole thing work is that the +troff markup added by toroff causes cross-referencing +information to be written to standard error during the formatting run; +the [[noroff]] script cleverly gathers this material into +the [[tags]] file, +which can be used as input to the next +formatting run. +As with LaTeX, the cross-referencing information is always one +formatter run behind, and to get a consistent document you must keep +running [[noroff]] until you reach a fixed point. +([[noroff]] ought to provide some help for this, to show when the tags +file has changed, for example.) +.HU "The shell wrapper" +Toroff is implemented as an awk program in a shell wrapper, and +uses a macro package to control the final formatting. In the following +discussion, a sample macro package, suitable for use with either [[-mm]] +or [[-ms]], is presented alongside the awk code which calls the +macros. Users may wish to modify the sample macros to suit local +preferences. Many do-nothing macros are provided as user exits so +knowledgeable users can modify document formatting without changing +the awk program. +<<toroff>>= +#!/bin/sh +<<process arguments>> +<<invoke awk program>> +@ .P +Processing arguments is simple. +<<process arguments>>= +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toroff" 1>&2; + exit 1;; + esac +done +@ %def delay noindex +.P +Invoking the awk program is hard, because the program uses both +single quotes and double quotes, so neither can be used to protect the +other, and quoting each quote is ugly. The pragmatic solution is to +copy the awk program into a temporary file, using a shell here-document. +<<invoke awk program>>= +export awkfile="$(LIB/nwmktemp)" +trap 'rm -f $awkfile' 0 1 2 10 14 15 +cat > $awkfile << 'EOF' +<<awk program>> +EOF +awk -f $awkfile -v tagsfile=$tagsfile delay=$delay noindex=$noindex macrodir=|LIBDIR| +@ %def awkfile tagsfile +.HU "The main program" +The overall structure of the awk program is simple. The [[tags]] +file is processed in the [[BEGIN]] action, and the noweb source is +processed by awk's pattern-action loop. +<<awk program>>= +<<read and process noweb source>> +<<functions>> +@ .HU "Beginning and ending the document" +The [[-delay]] option allows the initial document chunk to be processed +before reading the macro packages, so that necessary initialization may +be performed. The only commands which are useful in such an initial chunk +are bare troff commands. The delay is handled by special patterns for +the initial document chunk. Because several noweb files can be +processed in the same formatting run, there can be several document +chunks numbered zero. The later ones are not treated specially, by the +simple expedient of turning off [[delay]] after the first one. This +code also handles the [[trailer]] at the end of the document. +<<read and process noweb source>>= +/^@begin docs 0$/ { if (delay) next } +/^@end docs 0$/ { if (delay) { <<load macro package>>; delay = 0; next } } +/^@header m/ { macros = substr($0, 10) + if (!delay) { <<load macro package>> } } +/^@trailer/ { print ".ENDOFDOCUMENT" } +@ %def macros +<<tmac.w>>= +.de ENDOFDOCUMENT +.. +@ .P +The location of [[macrodir]] is likely to require customization. +<<load macro package>>= +printf ".so %s/tmac.%s\n", macrodir, macros +printf ".so %s/tmac.w\n", macrodir +printf ".BEGINNINGOFDOCUMENT\n" +@ %def macrodir +<<tmac.w>>= +.de BEGINNINGOFDOCUMENT +.. +@ .HU "Labels, references, and section numbers" +Here is the code to number code chunks in the page-number-and-letter +style used by noweb. The current page number is always stored in +troff's [[%]] number register, and we will arrange an +auto-incrementing number register [[SECTIONLETTER]] which is +reset at each top-of-page and is formatted as lower-case alphabetic +characters using troff's [[.af]] request. The top-of-page trap is +sprung one unit below the top of the page in order not to interfere with +the top-of-page macros of [[-mm]] or [[-ms]]. +<<read and process noweb source>>= +/^@xref label/ { lastxreflabel = $3 } +/^@xref ref/ { lastxrefref = tag(substr($0, 11)) } +@ %def lastxreflabel lastxrefref +<<tmac.w>>= +.de RESETLETTER \" reset letter at top of page +.nr SECTIONLETTER 0 1 \" count code sections on same page +.af SECTIONLETTER a \" ... formatted as lower-case alpha +.. +.wh 1u RESETLETTER \" trap just below top of page +@ .P +Note that the link information on the previous chunk shows that this +chunk uses the [[code]] variable, when in fact [[code]] only appears +as text within a comment. This error is an artifact of the language +independence of noweb. +@ .HU "Beginning and ending chunks" +Except for the initial document chunk, which was processed above, all +beginnings and endings of documentation and code chunks are processed +here. Variable [[code]] is [[0]] in text chunks, [[1]] in code +chunks and [[2]] in quoted code. The argument to +the [[.ENDCODECHUNK]] macro is the label which was in effect when the +code chunk was started, which is stored in the variable [[lastdefnlabel]]. +<<read and process noweb source>>= +/^@begin docs/ { printf ".BEGINDOCCHUNK\n" } +/^@end docs/ { printf ".ENDDOCCHUNK\n" } +/^@begin code/ { code = 1; printf ".BEGINCODECHUNK\n" } +/^@end code/ { code = 0; printf ".ENDCODECHUNK %s\n", lastdefnlabel } +@ %def code +.P +The macros related to document chunks are simple: [[.BEGINDOCCHUNK]] does +nothing, and [[.ENDDOCCHUNK]] merely flushes any unprinted output. +<<tmac.w>>= +.de BEGINDOCCHUNK +.. +.de ENDDOCCHUNK +.br +.. +@ .P +A code chunk uses a font, type size, leading, fill and adjust modes, +indent and tab stops which are distinct from those in the documentation. +These environment variables must be saved on entering a code chunk, +appropriate values for a code chunk must be established, and the +original values restored at the end of a code chunk. +<<tmac.w>>= +.de BEGINCODECHUNK +.sp 0.4v +<<save environment>> +<<set local environment>> +.di CODECHUNK +.. +.de ENDCODECHUNK +<<read back code chunk from diversion>> +<<restore environment>> +.. +@ .P +The code below replaces the built-in [[.ta]] tab-setting command with a +custom tab-setting command that allows tab stops to be reset and later +recalled. +<<tmac.w>>= +.rn ta real_ta +.de ta +.ds ta_saved \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.real_ta \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +@ .P +The font, type size and leading require two saved values, so +that both the current value and the previous value can be restored. +<<save environment>>= +.nr OLDft \\n(.f +.ft P +.nr PREVft \\n(.f +.nr OLDps \\n(.s +.ps +.nr PREVps \\n(.s +.nr OLDvs \\n(.v +.vs +.nr PREVvs \\n(.v +.nr OLDfi \\n(.u +.nr OLDad \\n(.j +.nr OLDin \\n(.in +@ .P +Code is printed in a constant-width font at 80% the size of document text. +Code is collected in fill mode with explicit breaks after each line; +although it might seem more natural to collect lines of code in no-fill +mode, that is not possible here because uses of code chunks must be able +to appear on the same line as actual code. +<<set local environment>>= +.ft CW +.ps \\n(.s*4/5 +.vs \\n(.vu*4u/5u +.in +0.5i +.real_ta 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i +.fi +@ .P +In order to prevent page breaks within code chunks, each code chunk +is read into a diversion and a page break is issued if the code chunk +is too big to fit on the current page. After the diversion is read back +in, when it is known what page the code is printed on, the location of +the code chunk is written using a [[.tm]] command. +<<read back code chunk from diversion>>= +.br \" flush last line of code +.di \" end diversion +.if \\n(dn>\\n(.t .bp \" force form feed if too big +.nf \" no fill mode -- already formatted +.in -0.5i \" don't re-indent when re-reading text +.CODECHUNK \" output body of diversion +.tm ###TAG### \\$1 \\n[%]\\n+[SECTIONLETTER] \" write tag info +.rm CODECHUNK \" reset diversion for next code chunk +@ .P +Finally, here is the code to restore the environment that existed before +the beginning of the code chunk. +<<restore environment>>= +.ft \\n[PREVft] +.ft \\n[OLDft] +.ps \\n[PREVps] +.ps \\n[OLDps] +.vs \\n[PREVvs]u +.vs \\n[OLDvs]u +.if \\n[OLDfi] .fi +.ad \\n[OLDad]u +.in \\n[OLDin]u +.real_ta \\*[ta_saved] +@ .HU "Text, code, and quoted code" +In text chunks, text is copied from input to output basically unchanged. +However, when quoted code is included in a line of text, the quoted code +is processed by separate macros and it is possible that the continuation +of the line of text after the quoted code begins with a character which +is special to troff at the beginning of a line: a space, period, or +single quote. To prevent problems in this case, variable [[text]] is +set to [[0]] at the beginning of each line and incremented whenever text +is written, and each text after the first is guarded by a non-printing +null character. +<<read and process noweb source>>= +/^@text/ && code == 0 { s = substr($0, 7) + if (text++) printf "\\&" + printf "%s", substr($0, 7) } +/^@nl/ && code != 1 { text = 0; printf "\n" } +@ %def text +.P +In code chunks, backslashes are replaced with troff's [[\e]] escape +sequence and a non-printing null character is added at the beginning of +each text to guard leading periods. If your macro package issues a [[.ec]] +command, this code will have to change; fortunately, both [[-mm]] and +[[-ms]] are nicely-behaved with respect to escape characters. +<<read and process noweb source>>= +/^@text/ && code != 0 { s = substr($0, 7) + gsub(/\\/, "\\e", s) + printf "\\&%s\\c\n", s } +@ .P +Dealing with newlines in code chunks is surprisingly hard. We would like +to print lines of code in [[nofill]] mode, but since noweb's markup +and filter programs may split lines of code, we are forced to use +[[fill]] mode and deal with newlines ourselves. Further, if a line is +too long to print on a single line, it must be split, and the continuation +line right-justified instead of left-justified. All of this processing is +accomplished by calling a macro at each newline; the macro plants a +page-position trap at the next line so that any continuation line will be +right-justified. +<<read and process noweb source>>= +/^@nl/ && code == 1 { printf ".NEWLINE\n" } +<<tmac.w>>= +.de LINETRAP +.dt \\n[TRAPplace]u \"cancel current trap +'ad r \" right-adjust continuation lines +.. +.de NEWLINE +.dt \\n[TRAPplace]u \" cancel current trap +\& \" end continued word +.br \" flush output +<<plant newline trap>> +.. +@ .P +The newline trap is planted after each definition line, for the first +line of code in the code chunk, and again after each newline in the +code chunk. +<<plant newline trap>>= +.nr TRAPplace \\n(.du+1u \" location of next trap +.dt \\n[TRAPplace]u LINETRAP \" plant trap at next line +.ad l \" left-adjust first line +@ .P +Quoted code in a documentation chunk is printed within its own macros, +which must not cause a break. +<<read and process noweb source>>= +/^@quote/ { code = 2; printf "\\c\n.BEGINQUOTEDCODE\n" } +/^@endquote/ { code = 0; text++; printf ".ENDQUOTEDCODE\n" } +<<tmac.w>>= +.de BEGINQUOTEDCODE +.nr SAVEft \\n(.f +.ft P +.nr SAVEftP \\n(.f +.ft CW +.. +.de ENDQUOTEDCODE +.ft \\n[SAVEftP] +.ft \\n[SAVEft] +.. +@ .HU "Definitions and uses of code chunks" +Definitions and uses of code chunks are handled below. Variable +[[defn[name]]] is set to a plus sign after a definition is printed, so +that continuations of the definition are properly identified. Variable +[[lastxrefref]] is the tag associated with the most-recently-seen +cross-reference label, and refers to the section number of the original +definition of the code chunk. +<<read and process noweb source>>= +/^@defn/ { name = convquote(substr($0, 7)) + lastdefnlabel = lastxreflabel + printf ".DEFINITION %s \"%s\" %s %s\n", + tag(lastdefnlabel), name, lastxrefref, defn[name] + defn[name] = "\\(pl" } +/^@use/ { name = convquote(substr($0, 6)) + printf "\c\n" + printf ".USE \"%s\" %s\n", name, lastxrefref } +@ %def name defn lastdefnlabel +<<tmac.w>>= +.de DEFINITION +.ti -0.5i +\\fR\\(sc\\$1 \\(oG\\$2 \\$3\\(cG \\$4\\(==\\fP +<<plant newline trap>> +.. +.de USE +\\fR\\(oG\\$1 \\$2\\(cG\\fP\c \" section name and original number +.. +@ .P +Processing of quoted code within definitions and uses is performed by the +[[convquote]] function. This must be processed differently than quoted +code within text because the noweb markup program doesn't emit +[[@quote]] .\|.\|. [[@endquote]] markers within definitions and uses. And, +because the subject string is used as an argument to a macro, it would not +possible to call the [[.BEGINQUOTEDCODE]] and [[.ENDQUOTEDCODE]] macros, +even if noweb did emit the markers within definitions and uses. +<<functions>>= +function convquote(s) { + gsub(/\[\[/, "\\*[BEGINCONVQUOTE]", s) + gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s); return s } +@ %def convquote +<<tmac.w>>= +.ds BEGINCONVQUOTE \f[CW] +.ds ENDCONVQUOTE \fP +@ .HU "Cross-referencing and indexing" +The code for producing the \(odhypertext links\(cd at the end of each +code section is given below: for each type, the beginning of the message +is printed, items are accumulated in a list, and the list is printed +after the number of items in the list is known. The first time any of +the cross-reference or identifier-index messages appears, it is necessary +to reset the point size and leading to the small font used for this +material, which is 80% of the size of code and 64% of the size of text. +All of these lines are part of the diversion which collects a +code chunk. First is the code to report definitions and uses of code. +<<read and process noweb source>>= +/^@xref begindefs/ { <<checkcode>>; printf ".XREFDEFS\n" } +/^@xref beginuses/ { <<checkcode>>; printf ".XREFUSES\n" } +/^@xref notused/ { <<checkcode>>; printf ".XREFNOTUSED\n" } +/^@xref (def|use)item/ { printf ".ADDLIST %s\n", tag($3) } +/^@xref end(def|use)s/ { printf ".PRINTLIST\n" } +<<tmac.w>>= +.de XREFDEFS +This definition continued in +.. +.de XREFUSES +This code used in +.. +.de XREFNOTUSED +This code not used in this document. +.br +.. +@ .P +The code to report the definitions of identifiers appears below. The +[[if]] in [[@index isused]] prevents index definitions from pointing +to themselves. +<<read and process noweb source>>= +/^@index begindefs/ && !noindex { <<checkcode>>; printf ".INDEXDEF\n" } +/^@index isused/ && !noindex { + if (tag($3) != lastxrefref) printf ".ADDLIST %s\n", tag($3) } +/^@index defitem/ && !noindex { printf ".DEFITEM %s\n.PRINTLIST\n", $3 } +<<tmac.w>>= +.de INDEXDEF +Defines: +.br +.. +.de DEFITEM +.ti +1m +\\*[BEGINCONVQUOTE]\\$1\\*[ENDCONVQUOTE], +.if \\n[NLIST]>0 used in +.. +@ .P +Finally, here is the code to report uses of identifiers. +<<read and process noweb source>>= +/^@index beginuses/ && !noindex { <<checkcode>>; printf ".INDEXUSE\n" } +/^@index isdefined/ && !noindex { lastuse = tag($3) } +/^@index useitem/ && !noindex { + printf ".ADDLIST \"\\*[BEGINCONVQUOTE]%s\\*[ENDCONVQUOTE] %s\"\n", + $3, lastuse } +/^@index enduses/ && !noindex { printf ".PRINTLIST\n" } +@ %def lastuse +<<tmac.w>>= +.de INDEXUSE +Uses +.. +@ .P +The macros [[.ADDLIST s]], which adds string [[s]] to a queued list +waiting to be printed, and [[.PRINTLIST]], which prints the list, +appropriately formatted with commas, are described below. These macros +make use of some interesting troff idioms. [[LIST]] is an array of +strings; the [[n]]-th string in [[LIST]] can be set to [[s]] by +[[.ds LISTn s]]. The expression [[.nr n \\$1]] converts the string +passed as the first argument to a macro to the number [[n]]. Loops +are implemented in troff as recursive macros, as in [[.PRINTITEM]]. +<<tmac.w>>= +.nr NLIST 0 1 \" initialize list index to 0 with auto-increment 1 +.de ADDLIST +.ds LIST\\n+[NLIST] \\$1 +.. +.de PRINTITEM +.nr PLIST \\$1 +.nr PLISTPLUS \\n[PLIST]+1 +.if \\n[NLIST]-\\n[PLIST]<0 not used in this document. +.if \\n[NLIST]-\\n[PLIST]=0 \\*[LIST\\n[PLIST]]. +.if \\n[NLIST]-\\n[PLIST]=1 \ + \\*[LIST\\n[PLIST]] and \\*[LIST\\n[PLISTPLUS]]. +.if \\n[NLIST]-\\n[PLIST]>1 \{ \\*[LIST\\n[PLIST]], +. PRINTITEM 1+\\n[PLIST] \} +.. +.de PRINTLIST +.PRINTITEM 1 +.br +.nr NLIST 0 1 \" re-initialize for next list +.. +@ .P +This code and macro reduces the font size in which the cross-reference +and identifier index information is printed at the end of a code chunk. +The [[code]] variable is set to [[0]] to ensure that [[.STARTXREF]] +is performed only once at the end of each code chunk. This code also +resets the adjustment mode, which was changed to left-adjustment or +right-adjustment by the [[LINETRAP]] macro, and cancels the [[NEWLINE]] +trap at [[TRAPplace]]. +<<checkcode>>= +if (code) { code = 0; printf ".STARTXREF\n" } +<<tmac.w>>= +.de STARTXREF +.ps \\n(.s*4/5 +.vs \\n(.vu*4u/5u +.ft \\n[OLDft] +.ad \\n[OLDad]u +.dt \\n[TRAPplace]u +.sp 0.4v +.. +@ .HU "Collecting the lists of chunks and identifiers" +Collecting the lists of chunks and identifiers takes two passes over the +file, because the section numbers which tags refer to aren't known on +the first pass. Therefore, the strategy on the first pass is to write +the chunk and identifier index entries to the [[tags]] file on standard +error, and actually prepare the lists when reading the [[tags]] file on +the second pass. Thus, the first pass code shown here merely copies +the chunk and identifier index entries from the noweb intermediate +file to the [[tags]] file using troff's [[.tm]] command. The +first section below handles chunks, the second section handles +identifiers. +<<read and process noweb source>>= +/^@xref beginchunks/ { printf ".tm ###BEGINCHUNKS###\n" } +/^@xref chunkbegin/ { printf ".tm ###CHUNKBEGIN### %s\n", + substr($0, length($3) + 19) } +/^@xref chunkuse/ { printf ".tm ###CHUNKUSE### %s\n", $3 } +/^@xref chunkdefn/ { printf ".tm ###CHUNKDEFN### %s\n", $3 } +/^@xref chunkend/ { printf ".tm ###CHUNKEND###\n" } +/^@xref endchunks/ { printf ".tm ###ENDCHUNKS###\n" } +<<read and process noweb source>>= +/^@index beginindex/ { printf ".tm ###BEGININDEX###\n" } +/^@index entrybegin/ { printf ".tm ###ENTRYBEGIN### %s\n", + substr($0, length($3) + 20) } +/^@index entryuse/ { printf ".tm ###ENTRYUSE### %s\n", $3 } +/^@index entrydefn/ { printf ".tm ###ENTRYDEFN### %s\n", $3 } +/^@index entryend/ { printf ".tm ###ENTRYEND###\n" } +/^@index endindex/ { printf ".tm ###ENDINDEX###\n" } +@ .HU "The \f[CW]tags\fP file" +The [[tags]] file is re-created at each formatter run by the troff +idiom (some people would call it a trick) of capturing the output of +troff's [[.tm]] command, which writes to the standard error, in a +file via command-line redirection. The code below uses an awk idiom; the +[[sub]] simultaneously tests for a match and deletes the matched text. +<<process [[tags]] file>>= +{ + if (sub(/^###TAG### / , "")) tags[$1] = $2 + else if (sub(/^###BEGINCHUNKS###/, "")) printf ".de CLIST\n.CLISTBEGIN\n" + else if (sub(/^###CHUNKBEGIN### /, "")) { name = convquote($0) + chunkuse = chunkdefn = "" } + else if (sub(/^###CHUNKUSE### / , "")) chunkuse = chunkuse " " tag($0) + else if (sub(/^###CHUNKDEFN### / , "")) chunkdefn = chunkdefn " " tag($0) + else if (sub(/^###CHUNKEND###/ , "")) + printf ".CITEM \"%s\" \"%s\" \"%s\"\n", name, chunkdefn, chunkuse + else if (sub(/^###ENDCHUNKS###/ , "")) printf ".CLISTEND\n..\n" + else if (sub(/^###BEGININDEX###/ , "")) printf ".de ILIST\n.ILISTBEGIN\n" + else if (sub(/^###ENTRYBEGIN### /, "")) { name = convquote($0) + entryuse = entrydefn = "" } + else if (sub(/^###ENTRYUSE### / , "")) entryuse = entryuse " " tag($0) + else if (sub(/^###ENTRYDEFN### / , "")) entrydefn = entrydefn " " tag($0) + else if (sub(/^###ENTRYEND###/ , "")) { + for (i = 1; i <= split(entrydefn, entryarray); i++) + sub(entryarray[i], "\\*[BEGINDEFN]&\\*[ENDDEFN]", entryuse) + printf ".IITEM \"%s\" \"%s\"\n", name, entryuse } + else if (sub(/^###ENDINDEX###/ , "")) printf ".ILISTEND\n..\n" +} +<<tag functions>> +@ %def tags chunkuse chunkdefn entryuse entrydefn entryarray +.P +The [[sub]] within the [[ENTRYEND]] causes definitions of identifiers +to be italicized, according to the following defined strings. +<<tmac.w>>= +.ds BEGINDEFN \fI +.ds ENDDEFN \fP +@ .P +Lookup of the section number corresponding to a particular tag is +performed by the [[tag]] function. +<<tag functions>>= +function tag(s) { if (s in tags) return tags[s]; else return "???" } +@ %def tag +.HU "Using \\f[CW]toroff\fP" +Toroff is one element of the normal noweave pipeline. Using +toroff in its full generality is hard. The sample program shown +below was used by the author during the development of toroff and +is an example of the use of toroff. A real noweave should +allow the user to specify the macro package rather than defaulting to +[[-mm]], allow the user to select the noweb filters which are used, +allow the user to specify the [[-delay]] and [[-noindex]] switches to +toroff, allow the user to specify troff pre-processors, +post-processors and options, and eliminate the temp output from tee +which allowed the author to peek at the pipeline and find out why +toroff wasn't acting as expected. Given the shell script shown +below, toroff could be woven by issuing the command [[xweave toroff.nw]] +twice in succession and printed by copying [[toroff.ps]] to a PostScript +printer. +<<noroff>>= +#!/bin/sh +# +# noroff -- run troff using tags file trick + +ROFF="psroff -t" # produce postscript on stdout +AWK=nawk + +if [ $# -ne 1 ]; then + echo "Usage: noroff file" 1>&2 + exit 1 +fi +base="`echo $1 | sed '/\./s/\.[^.]*$//'`" +tagsfile="$base.nwt" +(if [ -r "$tagsfile" ] then $AWK '<<process [[tags]] file>>' $tagsfile; fi + cat $1) | +($ROFF -Tps 2>$tagsfile) +sed '/^###[A-Z][A-Z]*###/d' $tagsfile >&2 +@ .HU "Index of chunks and identifiers" +Toroff creates macros [[.CLIST]] and [[.ILIST]] which insert the +lists of chunks and identifiers, respectively, in the output. These +macros, in turn, call other macros which format the lists. The macros +below cause each item to be written on a separate line, with continuation +lines indented one-quarter inch. +<<tmac.w>>= +.de CLISTBEGIN +.in +0.25i +.. +.de CITEM +.ti -0.25i +.ie '\\$3'' \\(oG\\$1 \\$2\\(cG Not used in this document. +.el \\(oG\\$1 \\$2\\(cG \\$3 +.br +.. +.de CLISTEND +.in -0.25i +.. +<<tmac.w>>= +.de ILISTBEGIN +.in +0.25i +.. +.de IITEM +.ti -0.25i +\\f[CW]\\$1\\fP: \\$2 +.br +.. +.de ILISTEND +.in -0.25i +.. +@ .P +The automatically-generated index of chunks and identifiers for +toroff is shown below. +.S 10 12 +.sp +.CLIST +.sp +.2C +.ILIST diff --git a/web/noweb/src/shell/unmarkup b/web/noweb/src/shell/unmarkup new file mode 100644 index 0000000000..a6ce6cc347 --- /dev/null +++ b/web/noweb/src/shell/unmarkup @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +nawk ' +/^@file / { +# if (file) printf "EndOf%s\n", file +# file = substr($0,7); +# printf "diff %s - <<'"'EndOf%s'"'\n", file, file +} + +END { +# if (file) printf "EndOf%s\n", file +} + +/^@begin docs 0$/ { next } +/^@begin docs / { printf "@ " } +/^@end docs / { } + +/^@begin code / { } +/^@defn / { printf "<<%s>>=", substr($0,7) } + +/^@text $/ {next} +/^@text / { + gsub("<<", "@<<"); + gsub(">>", "@>>"); + printf "%s", substr($0,7) +} +/^@quote$/ { printf("[[") } +/^@endquote$/ { printf("]]") } +/^@nl$/ { printf "\n"} + +/^@use / { printf "<<%s>>", substr($0,6) }' "$@" | sed 's/^@ $/@/' diff --git a/web/noweb/src/solmake b/web/noweb/src/solmake new file mode 100755 index 0000000000..3e406cfdde --- /dev/null +++ b/web/noweb/src/solmake @@ -0,0 +1,3 @@ +#!/bin/sh +if [ $# -eq 0 ]; then set all install; fi # "$@" breaks make for empty args +make CC="cc" LIBSRC=awk BIN=/usr/local/noweb/bin LIB=/usr/local/noweb/lib MAN=/usr/local/noweb/man TEXINPUTS=/usr/local/noweb/tex ELISP=$HOME/emacs "$@" diff --git a/web/noweb/src/testmake b/web/noweb/src/testmake new file mode 100644 index 0000000000..074b899d84 --- /dev/null +++ b/web/noweb/src/testmake @@ -0,0 +1,3 @@ +#!/bin/sh +if [ $# -eq 0 ]; then set all install; fi # "$@" breaks make for empty args +/usr/bin/make CC="gcc -ansi -pedantic -O -Wall -Werror" LIBSRC=awk BIN=/usr/local/noweb-test/bin LIB=/usr/local/noweb-test/lib MAN=/usr/local/noweb-test/man TEXINPUTS=/usr/local/tex/inputs ELISP=/dev/null "$@" diff --git a/web/noweb/src/tex/Makefile b/web/noweb/src/tex/Makefile new file mode 100644 index 0000000000..e70616104e --- /dev/null +++ b/web/noweb/src/tex/Makefile @@ -0,0 +1,28 @@ +SHELL=/bin/sh +NAME="name of version checked in" +CIMSG="message for version checked in" +RCSFILES=support.nw Makefile + +all: nwmac.tex noweb.sty +source: nwmac.tex noweb.sty +touch: nwmac.tex noweb.sty + touch nwmac.tex noweb.sty +boot: + touch nwmac.tex noweb.sty + +checkin: + ci -l $(CINAME) $(CIMSG) $(RCSFILES) + +nwmac.tex: support.nw + notangle -Rnwmac.tex support.nw > nwmac.tex + +noweb.sty: support.nw + notangle -Rnoweb.sty support.nw > noweb.sty + +support.tex: support.nw + noweave -delay -x support.nw > support.tex + +clean: + /bin/rm -f *~ *.dvi *.aux *.log *.blg *.bbl *.toc + /bin/rm -f support.tex +clobber: clean diff --git a/web/noweb/src/tex/multlang.nw b/web/noweb/src/tex/multlang.nw new file mode 100644 index 0000000000..b5b7851b51 --- /dev/null +++ b/web/noweb/src/tex/multlang.nw @@ -0,0 +1,1785 @@ +% -*- mode: Noweb; noweb-code-mode: tex-mode -*- +\documentclass[twoside]{article} +\usepackage{noweb} +\pagestyle{noweb} +\noweboptions{longchunks,smallcode} +\title{{\TeX} and {\LaTeX} support for {\tt noweb}\\ +{\small Multilingual version for {\LaTeX}}} +\author{Norman Ramsay\\ +{\small (with changes by Miguel Filgueiras)}} +\newcommand{\stylehook}{\marginpar{\raggedright\sl Style hook}} +\begin{document} +\maketitle +\tableofcontents +@ + +\begin{quote} +{\bf Explanatory note} on the changes made by Miguel Filgueiras +(DCC-FCUP \& LIACC, Universidade do Porto)\\ +The changes provide multilingual support. The +English wording for text inserted by {\tt noweb.sty} was replaced by +macros that are then defined in a specific language depending on the +(apparently standard) {\LaTeX} macro [[\languagename]]. This macro is +defined by the {\tt babel} package, and therefore if this package is +included with the appropriate language name {\em before} the inclusion +of the {\tt noweb} package, all the wording will appear in that language. +Note however that at present there is only support for English +(preserving the original text), Portuguese, German and French. The latter is +faulty: the translations might be not the best ones, and there are +bugs in the implementation that I could not solve (I am no experienced +{\TeX} user). Other languages can easily be accomodated by inserting +new translations in subsection~\ref{subsection:langdeps}. +\end{quote} + +This document describes the {\TeX} code that supports {\tt noweave} +and {\tt noweb}. Those interested in customizing their output should +focus on Section~\ref{section:sty}. +Hooks you can easily use (apart from those provided by +[[\noweboptions]]) are indicated by marginal notes. +This file contains both plain {\TeX} and {\LaTeX} support: +<<nwmac.tex>>= +% nwmac.tex -- plain TeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +<<noweb.sty>>= +% noweb-LANGUAGE.sty -- LaTeX support for noweb +% DON'T read or edit this file! Use .../support-multiling.nw instead. +@ + +\section{Basic {\TeX} support for {\tt noweb}} +This basic code is used for both {\TeX} and {\LaTeX}. +The first step is to define [[\codehsize]], which is the width in +which code is set, and [[\codemargin]], which is the amount by which +it is indented.\stylehook +<<kernel>>= +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=0pt +@ %def \codehsize \codemargin +[[\defspace]] is the space we would like on the right of navigational info +that appears on definition lines, so that it lines up with the text above +and below. +<<kernel>>= +\newdimen\nwdefspace +\nwdefspace=\codehsize +% need to use \textwidth in {\LaTeX} to handle styles with +% non-standard margins (David Bruce). Don't know why we sometimes +% wanted \hsize. 27 August 1997. +%% \advance\nwdefspace by -\hsize\relax +\ifx\textwidth\undefined + \advance\nwdefspace by -\hsize\relax +\else + \advance\nwdefspace by -\textwidth\relax +\fi +@ +Most code is set in an environment in which [[\setupcode]] has been +executed. +In this environment, only [[\]], [[{]], and [[}]] have their usual +categories; every other character represents itself. +Appropriate [[\chardef]]s ensure that the special characters can be +escaped with a backslash. +<<kernel>>= +\chardef\other=12 +\def\setupcode{% + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \catcode`\"=\other % fixes problem with german.sty + \obeyspaces\Tt +} +\let\nwlbrace=\{ +\let\nwrbrace=\} +@ %def \setupcode +[[\eatline]] is used to consume newlines that should be ignored, +for example, the newlines at the end of [[@ %def ]]{\em identifiers} lines. +I can't remember what [[\startline]] or [[\newlines]] are for; I don't +think {\tt noweave} ever emits them. +<<kernel>>= +{\catcode`\^^M=\active % make CR an active character + \gdef\newlines{\catcode`\^^M=\active % make CR an active character + \def^^M{\par\startline}}% + \gdef\eatline#1^^M{\relax}% +} +%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write +\def\startline{\noindent\hskip\parindent\ignorespaces} +\def\nwnewline{\ifvmode\else\hfil\break\fi} +@ %def \startline \eatline \nwnewline + +Within a code environment, it may be necessary to restore the +category codes in order to set a module (chunk) name. +This hack doesn't properly restore [["]] for use in {\tt german.sty}. +<<kernel>>= +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \let\{\nwlbrace + \let\}\nwrbrace + % bad news --- don't know what catcode to give " + \Rm} +@ %def \setupmodname +@ \sublabel{ref:fred} +@ +Setting up the space code has to be done differently for {\TeX} and +{\LaTeX}, so as not to screw up {\LaTeX}'s \texttt{verbatim} package. +(Fix from Rafael Laboissiere.) +<<nwmac.tex>>= +{\obeyspaces\global\let =\ } % from texbook, p 381 +<<noweb.sty>>= +{\obeyspaces\AtBeginDocument{\global\let =\ }} % from texbook, p 381 +@ + +{\tt noweave} brackets uses of chunk names with [[\LA]] and [[\RA]], which +handle the angle brackets, font, and environment. + +As it stands, chunk names can be broken across lines (or pages). This +could result in unnecessary page breaks in code +(c.f.~p.~\pageref{sec:pagebreaking}). [[\let\\maybehbox=\mbox]] to +\stylehook +avoid breaking them +(or to make them work in math mode); this is done in code chunks, but could be done +in general. +<<kernel>>= +\def\LA{\begingroup\maybehbox\bgroup\setupmodname\It$\langle$} +\def\RA{\/$\rangle$\egroup\endgroup} +\def\code{\leavevmode\begingroup\setupcode\newlines} +\def\edoc{\endgroup} +\let\maybehbox\relax +@ %def \LA \RA \maybehbox + +[[\equivbox]] and [[\plusequivbox]] are used to set the +``\unhcopy\equivbox'' +and ``\unhcopy\plusequivbox'' that open a chunk definition or its +continuation. +{\tt noweave} brackets definitions of chunk names with [[\moddef]] and +either [[\endmoddef]] or [[\plusendmoddef]]. +<<kernel>>= +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\ifmmode\equiv\else\unhcopy\equivbox\fi + \nobreak\hfill\nobreak} +\def\plusendmoddef{\RA\ifmmode\mathord{+}\mathord{\equiv}\else\unhcopy\plusequivbox\fi + \nobreak\hfill\nobreak} +@ %def \equivbox \plusequivbox \moddef \endmoddef \plusendmoddef +@ +Within a code environment, margin tags might be used to mark sub-page +numbers in the margins, separated by [[\nwmarginglue]].\stylehook +The interaction with [[\moddef]] involves tricky kerning. +The tag itself is displayed using [[\nwthemargintag]] +<<noweb.sty>>= +\def\nwopt@nomargintag{\let\nwmargintag=\@gobble} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\llap{##1\kern\nwmarginglue\kern\codemargin}}} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\kern-\codemargin\nwthemargintag{##1}\kern\codemargin}} +\def\nwthemargintag#1{\llap{#1\kern\nwmarginglue}} +\nwopt@margintag +\newdimen\nwmarginglue +\nwmarginglue=0.3in +@ %def \nwmargintag margintag nomargintag \nwmarginglue +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B margintag +Put the sub-page number (tag) of each code-chunk definition in the +left margin. (Default) +.TP +.B nomargintag +Don't use margin tags. +@ \fi +[[\nwtagstyle]] determines the style in which tags are displayed.\stylehook +<<noweb.sty>>= +\def\nwtagstyle{\footnotesize\Rm} +@ +<<kernel>>= +\def\chunklist{% +\errhelp{I changed \chunklist to \nowebchunks. +I'll try to avoid such incompatible changes in the future.}% +\errmessage{Use \string\nowebchunks\space instead of \string\chunklist}} +\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}} +\def\nowebindex{\message{<Warning: You need noweave -index to use \string\nowebindex>}} +@ %def \chunklist \nowebchunks \nowebindex +@ We have to be careful with font-changing in the presence of +different font-selection schemes. In the \LaTeX{} New Font Selection +Scheme something like [[\it\tt]] will attempt to use an italic +typewriter font. Thus we define new commands like [[\Tt]] which will +work with both the Plain and old and new \LaTeX{} schemes. (Note that +NFSS will be standard in the next version of \LaTeX.) A problem with +these definitions arises with NFSS: in math mode the won't work +unless the {\tt oldlfont} backwards-compatibility option is in effect. +For the moment, you can get round this by using [[\mbox]]. + +If you wanted code set in a different font, you could re-define +[[\Tt]].\stylehook{} [\LaTeX2e actually behaves like OFSS, but the +extra [[\reset@font]] does no harm.] +<<kernel>>= +% here is support for the new-style (capitalized) font-changing commands +% thanks to Dave Love +\ifx\documentstyle\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % plain +\else\ifx\selectfont\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % LaTeX OFSS +\else % LaTeX NFSS + \def\Rm{\reset@font\rm} + \def\It{\reset@font\it} + \def\Tt{\reset@font\tt} + \def\Bf{\reset@font\bf} +\fi\fi +\ifx\reset@font\undefined \let\reset@font=\relax \fi +@ %def \Rm \Tt \It \Bf +@ +\clearpage +\section{The {\tt noweb} document-style option for {\LaTeX}} +\label{section:sty} +{\LaTeX} support begins with the kernel shown above. +<<noweb.sty>>= +<<kernel>> +@ +\subsection{Support for noweb options} +<<noweb.sty>>= +\def\noweboptions#1{% + \def\@nwoptionlist{#1}% + \@for\@nwoption:=\@nwoptionlist\do{% + \@ifundefined{nwopt@\@nwoption}{% + \@latexerr{There is no such noweb option as '\@nwoption'}\@eha}{% + \csname nwopt@\@nwoption\endcsname}}} +@ %def \noweboptions +\subsection{Adjusting placement of code on the page} +{\LaTeX} requires a larger [[\codehsize]] because +code is indented by [[\codemargin]].\stylehook +<<noweb.sty>>= +\codemargin=10pt +\advance\codehsize by \codemargin % make room for indentation of code +\advance\nwdefspace by \codemargin % and fix adjustment for def/use +\def\setcodemargin#1{% + \advance\codehsize by -\codemargin % make room for indentation of code + \advance\nwdefspace by -\codemargin % and fix adjustment for def/use + \codemargin=#1 + \advance\codehsize by \codemargin % make room for indentation of code + \advance\nwdefspace by \codemargin % and fix adjustment for + % def/use +} +@ +[[\noweboptions{shift}]] is used to shift the whole page left to make room for +the wide code lines. +It may be emitted by {\tt noweave -shift}, or it might be given by a user. +<<noweb.sty>>= +\def\nwopt@shift{% + \dimen@=-0.8in + \if@twoside % Values for two-sided printing: + \advance\evensidemargin by \dimen@ + \else % Values for one-sided printing: + \advance\evensidemargin by \dimen@ + \advance\oddsidemargin by \dimen@ + \fi +% \advance \marginparwidth -\dimen@ +} +\let\nwopt@noshift\@empty +@ %def shift noshift + +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B shift +Shift text to the left so that long code lines won't extend +off the right-hand side of the page. +@ \fi + +\subsection{Page-breaking strategy}\label{sec:pagebreaking} + +We want to insert penalties aiming for: +\begin{enumerate} +\item +No page breaks in the middle of a code chunk unless necessary to avoid +an overfull vbox; +\item +Documentation immediately preceding a code chunk should appear on +the same page as that code chunk unless doing so would violate rule 1. +\end{enumerate} +[[\filbreak]] is useful for this sort of thing (see {\em The \TeX + book\/}) and is used to encourage breaks at the right places between +chunks. Appropriate penalties are inserted elsewhere, between code +lines in particular. + +\subsection{Environments for setting code} + +[[\nwbegincode]] and [[\nwendcode]] are used by {\tt noweave} to bracket +code chunks. +The [[webcode]] environment is intended for users who want to paste +{\tt noweave} output into papers. + +The definition of [[\nwbegincode]] is based on the verbatim +implementation in {\tt verbatim.sty}, which will, presumably be in the +next version of \LaTeX\@. One thing it does differently, apart from +the catcode changes is setting [[\linewidth]]; this will avoid some +overfull hboxen when the code lines are too long, but the lines won't +be broken anyhow (even within chunk names because of the +[[\maybehbox]] definition). +<<noweb.sty>>= +\def\nwbegincode#1{% + \begingroup + <<[[\nwbegincode]] separation and penalties>> + \@begincode } +\def\nwendcode{\endtrivlist \endgroup \filbreak} % keeps code on 1 page + +\newenvironment{webcode}{% + \@begincode +}{% + \endtrivlist} +@ %def \nwbegincode \nwendcode webcode +@ This is just common code between [[\nwbegincode]] and [[webcode]]. +<<noweb.sty>>= +\def\@begincode{% + <<[[\trivlist]] clich\'e (\`a la {\Tt verbatim})>> + \linewidth\codehsize + <<[[\obeylines]] setup>> + <<zap ligatures, fix spaces>> + \nowebsize \setupcode + \let\maybehbox\mbox } +@ %def \@begincode +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B smallcode +Set code in +.I LaTeX +.B "\\\\small" +font instead of +.B "\\\\normalsize." +Similar options exist for all the +.I LaTeX +size-changing commands. +@ \fi +@ [[\nowebsize]] governs the size at which code is set; users who want +to minimize code can [[\let\nowebsize=\small]]. +Slitex users should try +\begin{quote} +[[\def\nowebsize{\normalsize\baselineskip=20pt \parskip=5pt }]] +\end{quote} +to avoid code lines that are too far apart. +[[\nwcodetopsep]] is +the glue placed before code chunks.\stylehook +<<noweb.sty>>= + \newskip\nwcodetopsep \nwcodetopsep = 3pt plus 1.2pt minus 1pt + \let\nowebsize=\normalsize + \def\nwopt@tinycode{\let\nowebsize=\tiny} + \def\nwopt@footnotesizecode{\let\nowebsize=\footnotesize} + \def\nwopt@scriptsizecode{\let\nowebsize=\scriptsize} + \def\nwopt@smallcode{\let\nowebsize=\small} + \def\nwopt@normalsizecode{\let\nowebsize=\normalsize} + \def\nwopt@largecode{\let\nowebsize=\large} + \def\nwopt@Largecode{\let\nowebsize=\Large} + \def\nwopt@LARGEcode{\let\nowebsize=\LARGE} + \def\nwopt@hugecode{\let\nowebsize=\huge} + \def\nwopt@Hugecode{\let\nowebsize=\Huge} +@ %def \nwcodetopsep \nowebsize smallcode +@ Maybe the penalties ought to be parameters\dots +<<[[\nwbegincode]] separation and penalties>>= + \topsep \nwcodetopsep + \@beginparpenalty \@highpenalty + \@endparpenalty -\@highpenalty +@ +The [[\trivlist]] clich\'e isn't quite a clich\'e because we adjust +[[\leftskip]] for indentation by [[\codemargin]] and adjust +[[\rightskip]] to allow lines up to [[\codehsize]] long without +overfull boxen +($\mbox{[[\codehsize]]}=\mbox{[[\hsize]]}+\mbox{[[\rightskip]]}$). +Note that [[\hsize]] isn't altered. +<<[[\trivlist]] clich\'e (\`a la {\Tt verbatim})>>= + \trivlist \item[]% + \leftskip\@totalleftmargin \advance\leftskip\codemargin + \rightskip\hsize \advance\rightskip -\codehsize + \parskip\z@ \parindent\z@ \parfillskip\@flushglue +@ The penalty inserted between verbatim lines would normally be +[[\interlinepenalty]], but we want to prohibit breaks there. +@ +Note the bug lurking somewhere in this code, as reported by Steven Ooms: +\begin{quote} +I have some lay-out +problems in the documentation chunks. When using the (La)TeX commands +[[\hline]] or [[\vtop]] the right margin is always extended far beyond the page +margin after the first code chunk has been typeset. I'm still looking for +the exact cause of it, but to me it seems that LaTeX supposes for those +commands that the line width for the documentation chunk is as large as that +for code chunks, which isn't true in reality. +\end{quote} +@ +<<[[\obeylines]] setup>>= + \@@par + \def\par{\leavevmode\null \@@par \penalty\nwcodepenalty}% + \obeylines +@ [[\nwcodepenalty]] is the penalty for breaking between lines in a +code chunk. If you set it to 10000, code will never be broken across +pages.\stylehook{} I guess this should be settable in [[\noweboptions]]. +<<noweb.sty>>= +\newcount\nwcodepenalty \nwcodepenalty=\@highpenalty +@ %def \nwcodepenalty +The cursing chunk accounts for the addition of a mess of characters +to those reset by [[\@noligs]] in \LaTeX2e. +<<zap ligatures, fix spaces>>= + \@noligs <<make all those damn active characters ``other''>> + \setupcode \frenchspacing \@vobeyspaces +@ +We can't make [[`]] ``other,'' because then we'll get ligatures. +(Why Don put these ligatures in the [[\tt]] font I wish I knew.) +But we'll step on all the others. +<<make all those damn active characters ``other''>>= + \ifx\verbatim@nolig@list\undefined\else + \let\do=\nw@makeother \verbatim@nolig@list \do@noligs\` + \fi +<<noweb.sty>>= +\def\nw@makeother#1{\catcode`#1=12 } +@ +{\tt noweave} uses [[\nwbegindocs{nnn}]] and [[\nwenddocs]] to bracket +documentation chunks. +If a documentation chunk does not continue the current paragraph, +{\tt noweave} inserts [[\nwdocspar]], which uses +[[\filbreak]] in an attempt to keep the documentation chunk on the +same page as the code chunk that follows it. (The code chunk will +have another [[\filbreak]] after it---see [[\nwbegincode]].) +[[\nwbegindocs]] doesn't start a +new paragraph if the previous chunk didn't end one, i.e.\ didn't enter +vmode; if it does start a new one, it's only indented by the use of +[[\nwdocspar]]. +<<noweb.sty>>= +\def\nwbegindocs#1{\ifvmode\noindent\fi} +\let\nwenddocs=\relax +\let\nwdocspar=\filbreak +@ %def \nwbegindocs \nwdocspar \nwenddocs +The page-breaking strategy implies ragged bottom pages, so we should +turn it on in general (this is relevant for the {\tt report} style): +<<noweb.sty>>= +\raggedbottom +<<nwmac.tex>>= +\def\nwdocspar{\par\semifilbreak} +@ + +{\tt noweave} doesn't bracket quoted code with [[\code]] and [[\edoc]] any more. +It probably should do something nifty, just to make {\TeX} hackers happy, but it doesn't. +<<noweb.sty>>= +\def\code{\leavevmode\begingroup\setupcode\@vobeyspaces\obeylines} +\let\edoc=\endgroup +@ +\subsection{The {\tt noweb} page style} +Headers contain file name, date, and page number. +{\tt noweave} emits [[\nwfilename{]]{\em name}[[}]] for each new file. +In the {\tt noweb} page style, new files cause page breaks; +otherwise they are ignored. +<<noweb.sty>>= +\newdimen\@original@textwidth +\def\ps@noweb{% + \@original@textwidth=\textwidth + \let\@mkboth\@gobbletwo + \def\@oddfoot{}\def\@evenfoot{}% No feet. + \if@twoside % If two-sided printing. + \def\@evenhead{\hbox to \@original@textwidth{% + \Rm \thepage\qquad{\Tt\leftmark}\hfil\today}}% Left heading. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \else % If one-sided printing. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \let\@evenhead\@oddhead + \fi + \let\chaptermark\@gobble + \let\sectionmark\@gobble + \let\subsectionmark\@gobble + \let\subsubsectionmark\@gobble + \let\paragraphmark\@gobble + \let\subparagraphmark\@gobble + \def\nwfilename{\begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@filename} + \def\nw@filename##1{\endgroup\markboth{##1}{##1}\let\nw@filename=\nw@laterfilename}% +} +\def\nw@laterfilename#1{\endgroup\clearpage \markboth{#1}{#1}} +\let\nwfilename=\@gobble +@ %def \@original@textwidth \ps@noweb \nwfilename +@ +\subsection{Chunk cross-reference} +[[\nwalsodefined]], [[\nwused]], and [[\nwnotused]] are emitted by the {\tt +noweb} cross-referencers. (What arguments?) +If unused chunks are output chunks, a filter can slip in +[[\let\nwnotused\nwoutput]]. +The style uses [[\nwcodecomment]] for all annotations that follow code +chunks. +Fiddling with it can change the appearance of the output. +Note that [[\nwcodecomment]] is used after [[\nwbegincode]], with +[[\obeylines]] in efect. Since linebreaking can occur here, we need +to change the [[\interlinepenalty]]. +A little vertical space ([[\nwcodecommentsep]]\stylehook) appears before the first +comment. + +We firkled with [[\rightskip]] in [[\nwbegincode]] above; now we want to +reset it so that paragraphs are the normal width ([[\textwidth]], +possibly less [[\codemargin]]) and set ragged right. This is done as +usuall by making [[\rightskip]] naturally zero but stretchable. +<<noweb.sty>>= +\def\nwcodecomment#1{\@@par\penalty\nwcodepenalty + <<add [[\nwcodecommentsep]] if this is the first [[\nwcodecomment]]>>% + \hspace{-\codemargin}{% + \rightskip=0pt plus1in + \interlinepenalty\nwcodepenalty + \let\\\relax\footnotesize\Rm #1\@@par\penalty\nwcodepenalty}} +@ %def \nwcodecomment +This stuff is used at the end of a chunk. +<<noweb.sty>>= +\def\@nwalsodefined#1{\nwcodecomment{\@nwlangdepdef\ \nwpageprep\ \@pagesl{#1}.}} +\def\@nwused#1{\nwcodecomment{\@nwlangdepcud\ \nwpageprep\ \@pagesl{#1}.}} +\def\@nwnotused#1{\nwcodecomment{\@nwlangdeprtc.}} +\def\nwoutput#1{\nwcodecomment{\@nwlangdepcwf\ {\Tt \@stripstar#1*\stripped}.}} +\def\@stripstar#1*#2\stripped{#1} +@ %def \nwalsodefined \nwused \nwnotused \nwoutput +<<add [[\nwcodecommentsep]] if this is the first [[\nwcodecomment]]>>= +\if@firstnwcodecomment + \vskip\nwcodecommentsep\penalty\nwcodepenalty\@firstnwcodecommentfalse +\fi +@ +This stuff on the definition line. +Note the hooks\stylehook{} for pointer styles. +<<noweb.sty>>= +\newcommand{\nwprevdefptr}[1]{% + \mbox{$\mathord{\triangleleft}\,\mathord{\mbox{\subpageref{#1}}}$}} +\newcommand{\nwnextdefptr}[1]{% + \mbox{$\mathord{\mbox{\subpageref{#1}}}\,\mathord{\triangleright}$}} + +\newcommand{\@nwprevnextdefs}[2]{% + {\nwtagstyle + \ifx\relax#1\else ~~\nwprevdefptr{#1}\fi + \ifx\relax#2\else ~~\nwnextdefptr{#2}\fi}} +\newcommand{\@nwusesondefline}[1]{{\nwtagstyle~~(\@pagenumsl{#1})}} +\newcommand{\@nwstartdeflinemarkup}{\nobreak\hskip 1.5em plus 1fill\nobreak} +\newcommand{\@nwenddeflinemarkup}{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak} +@ +And here are the options we use to choose one or the other. +<<noweb.sty>>= +\def\nwopt@longxref{% + \let\nwalsodefined\@nwalsodefined + \let\nwused\@nwused + \let\nwnotused\@nwnotused + \let\nwprevnextdefs\@gobbletwo + \let\nwusesondefline\@gobble + \let\nwstartdeflinemarkup\relax + \let\nwenddeflinemarkup\relax +} +\def\nwopt@shortxref{% + \let\nwalsodefined\@gobble + \let\nwused\@gobble + \let\nwnotused\@gobble + \let\nwprevnextdefs\@nwprevnextdefs + \let\nwusesondefline\@nwusesondefline + \let\nwstartdeflinemarkup\@nwstartdeflinemarkup + \let\nwenddeflinemarkup\@nwenddeflinemarkup +} +\nwopt@shortxref % to hell with backward compatibility! +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B longxref, shortxref +Use either long, Knuth-style chunk cross-reference, or short, +Hanson-style chunk cross-reference. +The former uses small paragraphs after the chunk; +the latter uses symbols on the definition line. +Defaults to +.B shortxref. +@ \fi +<<noweb.sty>>= +\newskip\nwcodecommentsep \nwcodecommentsep=3pt plus 1pt minus 1pt +\newif\if@firstnwcodecomment\@firstnwcodecommenttrue +@ +\subsection{Page ranges} +The goal is to combine sub-page numbers in a way that makes sense. +Multiple sub-pages of one page become that page, and individual +pages are combined into ranges. +(A range may be only one page.) +<<noweb.sty>>= +\newcount\@nwlopage\newcount\@nwhipage % range lo..hi-1 +\newcount\@nwlosub % subpage of lo +\newcount\@nwhisub % subpage of hi +\def\@nwfirstpage#1#2#3{% subpage page xref-tag + \@nwlopage=#2 \@nwlosub=#1 + \def\@nwloxreftag{#3}% + \advance\@nwpagecount by \@ne + <<$\mbox{[[\@nwhipage]]} := \mbox{[[\@nwlopage]]}+1$>>} +\def\@nwnextpage#1#2#3{% subpage page xref-tag + \ifnum\@nwhipage=#2 + \advance\@nwhipage by \@ne + \advance\@nwpagecount by \@ne + \@nwhisub=#1 + \def\@nwhixreftag{#3}\else + \ifnum#2<\@nwlopage <<new range starting with [[#2]]>>\else + \ifnum#2>\@nwhipage <<new range starting with [[#2]]>>\else + \@nwlosub=0 \@nwhisub=0 + \fi\fi\fi + } +<<new range starting with [[#2]]>>= +<<add range to range list>>\@nwfirstpage{#1}{#2}{#3} +<<$\mbox{[[\@nwhipage]]} := \mbox{[[\@nwlopage]]}+1$>>= +\@nwhipage=\@nwlopage\advance\@nwhipage by \@ne +<<add range to range list>>= +<<set [[\@tempa]] to page range(s), marked with [[\\]]>>% +\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa +<<set [[\@tempa]] to page range(s), marked with [[\\]]>>= +\advance\@nwhipage by \m@ne +\ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% +\else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else <<use rules from Chicago style manual>>% + \fi +\fi +<<use rules from Chicago style manual>>= +\ifnum\@nwlopage<110 <<normal range>>\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage <<normal range>>\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + <<normal range>>% + \else <<normal range>>% + \fi + \fi +\fi +<<normal range>>= +\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}} +<<noweb.sty>>= +\newcount\@nwpagetemp +@ +The sequence [[\@pagesl]] makes a range of pages from a list of labels. +[[\subpages]] works from a list of [[{{subpage}{page}}]]. +<<noweb.sty>>= +\newcount\@nwpagecount +\def\@nwfirstpagel#1{% label + \@ifundefined{r@#1}{<<warn of undefined reference to [[#1]] and add page ??>>}{% + \edef\@tempa{\noexpand\@nwfirstpage\subpagepair{#1}{#1}}\@tempa}} +\def\@nwnextpagel#1{% label + \@ifundefined{r@#1}{<<warn of undefined reference to [[#1]] and add page ??>>}{% + \edef\@tempa{\noexpand\@nwnextpage\subpagepair{#1}{#1}}\@tempa}} +<<noweb.sty>>= +\def\@pagesl#1{% list of labels + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + <<add range to range list>>\def\\##1{\@nwhyperpagenum##1}% + \nwpageword\ifnum\@nwpagecount=1 \else s\fi~\commafy{\nw@pages}} +\def\@nwhyperpagenum#1#2{\nwhyperreference{#2}{#1}} + +\def\@pagenumsl#1{% list of labels -- doesn't include word `pages', commas, or `and' + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + <<add range to range list>>% + \def\\##1{\@nwhyperpagenum##1\let\\=\@nwpagenumslrest}\nw@pages} +\def\@nwpagenumslrest#1{~\@nwhyperpagenum#1} +<<noweb.sty>>= +\def\subpages#1{% list of {{subpage}{page}} + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\edef\@tempa{\noexpand\@nwfirstpage##1{}}\@tempa + \def\\####1{\edef\@tempa{\noexpand\@nwnextpage####1}\@tempa}}#1% + <<add range to range list>>\def\\##1{\@firstoftwo##1}% + \nwpageword\ifnum\@nwpagecount=1 \else s\fi~\commafy{\nw@pages}} +\def\@nwaddrange{<<add range to range list>>} +@ +[[\nwpageword]] and [[\nwpageprep]] let you change the wording of the cross-reference +information. +<<noweb.sty>>= +\def\nwpageword{\@nwlangdepchk} % chunk, was page +\def\nwpageprep{\@nwlangdepin} % in, was on +<<warn of undefined reference to [[#1]] and add page ??>>= +<<warn of undefined reference to [[#1]]>>% +\nwix@cons\nw@pages{\\{\bf ??}} +@ +\subsection{Sub-page references} + +This is the wonderful code that Dave Love provided to make page +references like 7a, 7b, and so on. + +This code +provides a mechanism for defining `page +sub-references' using [[\sublabel{foo}]] referenced with +[[\subpageref{foo}]]. Sub-references will be numbered like these real +examples: \subpageref{ref:foo}, \subpageref{ref:bar}, +\subpageref{ref:baz}\sublabel{ref:foo}\sublabel{ref:bar}\sublabel{ref:baz} +etc.\ unless there is only one on the page, in which case the letter +will be dropped like this: \subpageref{ref:fred}. + +To be able to use [[\subpageref]] we must define the label with +[[\sublabel]], used like label. (Using +[[\ref]] with a label defined by [[\sublabel]] will +produce the sub-reference number, by the way, and [[\pageref]] +works as expected.) Note that +[[\subpageref]] is robust and [[\ref]] and [[\pageref]] are redefined to be +robust also, as they will be in future \LaTeX{} releases. +Incidentally, these expand to the relevant text plus [[\null]]---you +might want to strip this off, e.g.\ for sorting lists. + + +There are various ways we could attack this task (which is made +non-trivial by the well-known asynchrony of (La)\TeX's output +routine), but +they all must depend on hacks in the [[.aux]] file or a similar one. +Joachim Schrod's [[fnpag.sty]] does the same sort of thing differently +to this \LaTeX-specific approach. See [[latex.tex]] for enlightenment +on the cross-referencing mechanism and the \LaTeX{} internals used +below. [DL: The internals change in \LaTeX2e compared with +\LaTeX~2.09. The code here still works, though.] +@ +The new-style {\LaTeX} page-reference macros all work the same way: +if the thing is undefined, barf. Otherwise, do the specified thing. +We need to handle the fact that the expansion of the label may be two +items or five items, depending on whether hypertext is used. +Since we're only ever interested in the first two items, we use a +hack---the ``do the specified thing'' must be defined as +\mbox{[[\def\dome#1#2#3\\{...}]]} where [[...]] uses only the first two parameters. +<<noweb.sty>>= +\newcommand\nw@genericref[2]{% what to do, name of ref + \expandafter\nw@g@nericref\csname r@#2\endcsname#1{#2}} +\newcommand\nw@g@nericref[3]{% control sequence, what to do, name + \ifx#1\relax + \ref{#3}% trigger the standard `undefined ref' mechanisms + \else + \expandafter#2#1.\\% + \fi} +@ Much of what we want can be done by pulling out the first, second, +or first and second elements of a ref. +<<noweb.sty>>= +\def\nw@selectone#1#2#3\\{#1} +\def\nw@selecttwo#1#2#3\\{#2} +\def\nw@selectonetwo#1#2#3\\{{#1}{#2}} +@ +The [[\subpageref]] macro first does a normal [[\pageref]]. If the +reference is actually defined, it then goes on to check whether the +control sequence [[2on]]\LA{}{page referenced}\RA{} is defined and sets the +[[\ref]] value to get [[a]] etc.\ if so. The magic, of course, is in +defining the [[2on]] bit appropriately. +[[\subpageref]] also tries to include the right hyperstuff for xhdvi. +<<noweb.sty>>= +\newcommand{\subpageref}[1]{% + \nwhyperreference{#1}{\nw@genericref\@subpageref{#1}}} +\def\@subpageref#1#2#3\\{% + \@ifundefined{2on#2}{#2}{\nwthepagenum{#1}{#2}}} +@ %def \subpageref +[[\subpagepair]] produces a [[{subpage}{page}]] pair. +<<noweb.sty>>= +\newcommand{\subpagepair}[1]{% % produces {subpage}{page} + \@ifundefined{r@#1}% + {{0}{0}}% + {\nw@genericref\@subpagepair{#1}}} +\def\@subpagepair#1#2#3\\{% + \@ifundefined{2on#2}{{0}{#2}}{{#1}{#2}}} +@ +[[\sublabel]] is like the [[\label]] command, except that it writes +[[\newsublabel]] onto the [[.aux]] file rather than [[\newlabel]]. +For hyperreferencing, all labels must be hypertext +anchors, for which we use [[\nwblindhyperanchor]]. +<<noweb.sty>>= +\newcommand{\sublabel}[1]{% + \nwblindhyperanchor{#1}% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ %def \sublabel +[[\nosublabel]] creates a label with a sub-page part of~0. +<<noweb.sty>>= +\newcommand{\nosublabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newlabel{#1}{{0}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ %def \nosublabel +@ +[[\newsublabel]] is the macro that does the important work. It is called with the +same sort of arguments as [[\newlabel]]: the first argument is the +label name and the second is [[{<<ref value>>}{<<page number>>}]]. +(Note that the only definition here which needs to be +global is the one which is, and that [[\global]] is redefined by +[[\enddocument]], which will bite you if you use it\dots) +<<noweb.sty>>= +<<definition of [[\newsublabel]]>> +@ +Before we create a [[\newsublabel]] for the first time, we set the +proper trailers. +<<definition of [[\newsublabel]]>>= +\newcommand\newsublabel{% + \nw@settrailers + \global\let\newsublabel\@newsublabel + \@newsublabel} +@ First we extract the page number into [[\this@page]]. +<<definition of [[\newsublabel]]>>= +\newcommand{\@newsublabel}[2]{% + \edef\this@page{\@cdr#2\@nil}% +@ %def \newsublabel +@ +Then we see whether it's changed from the value of [[\last@page]] +which was stashed away by the last [[\newsublabel]] (or is [[\relax]] if +this is the first one). If the page has changed, we reset the +counter [[\sub@page]] telling us how many sub-labels there have been +on the page. +<<definition of [[\newsublabel]]>>= + \ifx\this@page\last@page\else + \sub@page=\z@ + \fi + \edef\last@page{\this@page} + \advance\sub@page by \@ne +@ +If we've had at least two on the page, we define the +[[2on]]\LA{}{page no.}\RA{} macro to indicate the fact. +<<definition of [[\newsublabel]]>>= + \ifnum\sub@page=\tw@ + \global\@namedef{2on\this@page}{}% + \fi +@ +\nextchunklabel{cl1}\nextchunklabel{cl2} +Then we write a normal [[\newlabel]] with the sub-reference as the +normal reference value in the second argument. +Unfortunately, if we want hypertext support, the second argument of +[[\newlabel]] gets complicated. +It is either +\begin{itemize} +\item +[[{<<ref value>>}{<<page number>>}]], when normal {\LaTeX} is +running, or +\item +[[{<<ref value>>}{<<page number>>}{<<text>>}}{<<hyper category>>}{<<URL>>}]], +when the \texttt{hyperref} package is running. +(We actually detect this by looking for the \texttt{nameref} package, +because that's the one that changes the use of labels.) +\end{itemize} +We unify these two things by producing +[[{<<ref value>>}{<<page number>>}\nw@labeltrailers]] + +We may have pending labels in support of [[\nextchunklabel]], as defined in +chunk~\subpageref{chunklabel}. +Because we want to define all of the ``pending sublabels'' in exactly +the same way, we do something a bit odd---we make the current label a +pending label as well. +<<definition of [[\newsublabel]]>>= + \pendingsublabel{#1}% + \edef\@tempa##1{\noexpand\newlabel{##1}% + {{\number\sub@page}{\this@page}\nw@labeltrailers}}% + \pending@sublabels + \def\pending@sublabels{}} +@ +We can't use [[\@ifpackageloaded]] to see if \texttt{nameref} is +loaded, because that is restricted to the preamble, and +[[\newsublabel]] goes into the [[.aux]] file, which is executed after +the whole document is processed. +We therefore test for [[\@secondoffive]]. +This is lame, but what else can we do? +<<noweb.sty>>= +\newcommand\nw@settrailers{% -- won't work on first run + \@ifpackageloaded{nameref}% + {\gdef\nw@labeltrailers{{}{}{}}}% + {\gdef\nw@labeltrailers{}}} +\renewcommand\nw@settrailers{% + \@ifundefined{@secondoffive}% + {\gdef\nw@labeltrailers{}}% + {\gdef\nw@labeltrailers{{}{}{}}}} +@ +Now we keep track of those pending guys.\nextchunklabel{chunklabel} +The goal here is to save them up until they're all equivalent to the +label on the next chunk. +We have to control expansion so chunks like \subpageref{cl1} +(\subpageref{cl2}) can be labelled twice. +<<noweb.sty>>= +\newcommand{\nextchunklabel}[1]{% + \nwblindhyperanchor{#1}% % looks slightly bogus --- nr + \@bsphack\if@filesw {\let\thepage\relax + \edef\@tempa{\write\@auxout{\string\pendingsublabel{#1}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\newcommand\pendingsublabel[1]{% + \def\@tempa{\noexpand\@tempa}% + \edef\pending@sublabels{\noexpand\@tempa{#1}\pending@sublabels}} +\def\pending@sublabels{} +<<man page: noweb style control sequences>>= +.PP \" .TP will not work with the backslashes on the next line. Period. +\fB\\nextchunklabel{l}\fP +.RS +Associates label \fBl\fP +with the sub-page reference of the next code chunk. +Can be used in for concise chunk cross-reference with, e.g., +\fBchunk~\\subpageref{l}\fP. +.RE +@ +We need to define these. +<<noweb.sty>>= +\def\last@page{\relax} +\newcount\sub@page +@ %def \last@page \sub@page +@ +We no longer use Rainer's new expandable definitions of [[\ref]] and +[[\pageref]] to minimise the risk of nasty surprises; enough time has +elapsed that this should no longer be necessary. +<<old noweb.sty>>= +% RmS 92/08/14: made \ref and \pageref robust +\def\ref#1{\@ifundefined{r@#1}{{\bf ??}<<warn of undefined reference to [[#1]]>>}% + {\expandafter\expandafter\expandafter + \@car\csname r@#1\endcsname\@nil\null}} +\def\pageref#1{\@ifundefined{r@#1}{{\bf ??}<<warn of undefined reference to [[#1]]>>}% + {\expandafter\expandafter\expandafter + \@cdr\csname r@#1\endcsname\@nil\null}} +\def\@refpair#1{\@ifundefined{r@#1}{{0}{0}<<warn of undefined reference to [[#1]]>>}% + {\@nameuse{r@#1}}} +<<warn of undefined reference to [[#1]]>>= +\@warning{Reference `#1' on page \thepage \space undefined} +@ %def \ref \pageref +@ + +Here a a couple of hooks for formatting sub-page numbers, +which can be alphabetic, numeric, or omitted.\stylehook +<<noweb.sty>>= +\def\@alphasubpagenum#1#2{#2\ifnum#1=0 \else\@alph{#1}\fi} +\def\@nosubpagenum#1#2{#2} +\def\@numsubpagenum#1#2{#2\ifnum#1=0 \else.\@arabic{#1}\fi} +\def\nwopt@nosubpage{\let\nwthepagenum=\@nosubpagenum\nwopt@nomargintag} +\def\nwopt@numsubpage{\let\nwthepagenum=\@numsubpagenum} +\def\nwopt@alphasubpage{\let\nwthepagenum=\@alphasubpagenum} +\nwopt@alphasubpage +@ %def \nwthepagenum nosubpage numsubpage alphasubpage +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B alphasubpage, numsubpage, nosubpage +Number chunks by the number of the page on which they appear, +followed by an alphabetic (numeric, not used) ``sub-page'' indicator. +Defaults to +.B alphasubpage. +.B nosubpage +implies +.B nomargintag. +@ \fi +@ + +\subsection{{\tt WEB}-like chunk numbering} +Here's a righteous hack: we get the effect of WEB-like chunk numbers +just by redefining [[\sublabel]] to use a counter instead of the current page number. +Since the numbers are all distinct, no sub-page number is ever used. +<<noweb.sty>>= +\newcount\nw@chunkcount +\nw@chunkcount=\@ne +\newcommand{\weblabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\number\nw@chunkcount}}}}% + \expandafter}\@tempa + \global\advance\nw@chunkcount by \@ne + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\def\nwopt@webnumbering{\let\sublabel=\weblabel\def\nwpageword{chunk}\def\nwpageprep{in}} +@ %def \weblabel webnumbering +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B webnumbering +Number chunks consecutively, in +.I WEB +style, instead of using sub-page numbers. +@ \fi +@ +\subsection{Indexing (identifier cross-reference) support} + +\subsubsection{Tracking definitions and uses} +All index definitions and uses are associated with +a label defined with [[\sublabel]] or [[\nosublabel]]. +Either the label is the [[\sublabel]] of the code chunk in which the definition or use +appears, or it is a [[\nosublabel]] appearing in the middle of a +documentation chunk. +<<noweb.sty>>= +% \nwindexdefn{printable name}{identifying label}{label of chunk} +% \nwindexuse{printable name}{identifying label}{label of chunk} + +\def\nwindexdefn#1#2#3{\@auxix{\protect\nwixd}{#2}{#3}} +\def\nwindexuse#1#2#3{\@auxix{\protect\nwixu}{#2}{#3}} + +\def\@auxix#1#2#3{% {marker}{id label}{subpage label} + \@bsphack\if@filesw {\let\nwixd\relax\let\nwixu\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string\nwixadd{#1}{#2}{#3}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ %def \nwindexdefn \nwindexuse \@auxix +<<noweb.sty>>= +% \nwixadd{marker}{idlabel}{subpage label} +\def\nwixadd#1#2#3{% + \@ifundefined{nwixl@#2}% + {\global\@namedef{nwixl@#2}{#1{#3}}}% + {\expandafter\nwix@cons\csname nwixl@#2\endcsname{#1{#3}}}} +@ %def \nwixadd +@ +\subsubsection{Subscripted identifiers} +We use either explicit subscripts or hyperlinks to point identifiers +to their definitions. +<<noweb.sty>>= +\def\@nwsubscriptident#1#2{\mbox{$\mbox{#1}_{\mathrm{\subpageref{#2}}}$}} +\def\@nwnosubscriptident#1#2{#1} +\def\@nwhyperident#1#2{\leavevmode\nwhyperreference{#2}{#1}} +@ +We can use subscripts, hyperlinks, or nothing on all identifiers. +<<noweb.sty>>= +\def\nwopt@subscriptidents{% + \let\nwlinkedidentq\@nwsubscriptident + \let\nwlinkedidentc\@nwsubscriptident +} +\def\nwopt@nosubscriptidents{% + \let\nwlinkedidentq\@nwnosubscriptident + \let\nwlinkedidentc\@nwnosubscriptident +} +\def\nwopt@hyperidents{% + \let\nwlinkedidentq\@nwhyperident + \let\nwlinkedidentc\@nwhyperident +} +\def\nwopt@nohyperidents{% + \let\nwlinkedidentq\@nwnosubscriptident + \let\nwlinkedidentc\@nwnosubscriptident +} +@ +We can change only identifiers appearing in quoted code. +<<noweb.sty>>= +\def\nwopt@subscriptquotedidents{% + \let\nwlinkedidentq\@nwsubscriptident +} +\def\nwopt@nosubscriptquotedidents{% + \let\nwlinkedidentq\@nwnosubscriptident +} +\def\nwopt@hyperquotedidents{% + \let\nwlinkedidentq\@nwhyperident +} +\def\nwopt@nohyperquotedidents{% + \let\nwlinkedidentq\@nwnosubscriptident +} +@ +The default is to hyperlink everything. +<<noweb.sty>>= +\nwopt@hyperidents +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B subscriptidents, nosubscriptidents, hyperidents, nohyperidents +Controls subscripting of identifiers in code, including quoted code. +Selecting +.B subscriptidents +means an identifier appearing in a code chunk (or in quoted code +within a documentation +chunk) will be subscripted with the chunk number of its definition. +.B hyperidents +means such identifiers will be hyperlinked to their definitions, +provided of course that a hypertext package like +.B hyperref +is loaded. +.B nosubscriptidents +and +.B nohyperidents +are equivalent, and they turn off such markings. +The default is +.B hyperidents. +.TP +.B subscriptquotedidents, nosubscriptquotedidents, hyperquotedidents, nohyperquotedidents +Controls linking of identifiers as above, but applies only to uses of +identifiers +in quoted code. +@ \fi +@ +\subsubsection{Writing lists with commas and ``and''} +You get one of +\begin{itemize} +\item ``$a$'' +\item ``$a$ and $b$'' +\item ``$a$, $\ldots$, $b$, and $c$'' +\end{itemize} +Plus [[\\]] is applied to each element of the list. +<<noweb.sty>>= +\newcount\@commacount +\def\commafy#1{% + {\nwix@listcount{#1}\@commacount=\nwix@counter + \let\@comma@each=\\% + \ifcase\@commacount\let\\=\@comma@each\or\let\\=\@comma@each\or + \def\\{\def\\{ \@nwlangdepand\ \@comma@each}\@comma@each}\else + \def\\{\def\\{, % + \advance\@commacount by \m@ne + \ifnum\@commacount=1 \@nwlangdepand~\fi\@comma@each}\@comma@each}\fi + #1}} +@ %def \@commacount \commafy + +\subsubsection{New, improved index code} +There are two kinds of lists. +One kind is a generic list in which elements are preceded by [[\\]]. +If the elements are index elements, they are {\em[[{]]printable +identifier[[}{]]label[[}]]} pairs. +The other kind is a list of sub-page labels, in which each +element is preceded by either [[\nwixd]] or [[\nwixu]]. +<<noweb.sty>>= +\def\nwix@cons#1#2{% {list}{\marker{element}} + {\toks0=\expandafter{#1}\def\@tempa{#2}\toks2=\expandafter{\@tempa}% + \xdef#1{\the\toks0 \the\toks2 }}} +@ %def \nwix@cons +The reference list for an identifier labelled {\em id} +is always called [[\nwixl@]]{\em id}. +Most applications will work with reference lists by applying [[\\]] +either to the defs or to the uses. +<<noweb.sty>>= +\def\nwix@uses#1{% {label} + \def\nwixu{\\}\let\nwixd\@gobble\@nameuse{nwixl@#1}} +\def\nwix@defs#1{% {label} + \def\nwixd{\\}\let\nwixu\@gobble\@nameuse{nwixl@#1}} +@ %def \nwix@uses \nwix@defs + +Some applications count uses to see whether there is any need to +display information. +<<noweb.sty>>= +\newcount\nwix@counter +\def\nwix@listcount#1{% {list with \\} + {\count@=0 + \def\\##1{\advance\count@ by \@ne }% + #1\global\nwix@counter=\count@ }} +\def\nwix@usecount#1{\nwix@listcount{\nwix@uses{#1}}} +\def\nwix@defcount#1{\nwix@listcount{\nwix@defs{#1}}} +@ %def \nwix@listcount \nwix@usecount \nwix@defcount +@ +\subsubsection{Supporting a mini-index at the end of each chunk} +When displaying identifiers used, show the identifier and its +definitions. +<<noweb.sty>>= +\def\nwix@id@defs#1{% index pair + {{\Tt \@car#1\@nil}% + \def\\##1{~\subpageref{##1}}\nwix@defs{\@cdr#1\@nil}}} +\def\nwidentuses#1{% list of index pairs + \nwcodecomment{\@nwlangdepuss\ \let\\=\nwix@id@defs\commafy{#1}.}} +@ %def \nwidentuses \nwix@id@defs +The definitions section is a bit more complex, because it is omitted +if none of the identifiers defined is ever used. +<<noweb.sty>>= +\def\nwix@totaluses#1{% list of index pairs + {\count@=0 + \def\\##1{\nwix@usecount{\@cdr##1\@nil}\advance\count@ by\nwix@counter}% + #1\global\nwix@counter\count@ }} +\def\nwix@id@uses#1#2{% {ident}{label} + \nwix@usecount{#2}\ifnum\nwix@counter>0 + {\advance\leftskip by \codemargin + \nwcodecomment{{\Tt #1}, \@nwlangdepusd\ \nwpageprep\ \@pagesl{\nwix@uses{#2}}.}}% + \else + \ifnw@hideunuseddefs\else + {\advance\leftskip by \codemargin \nwcodecomment{{\Tt #1}, \@nwlangdepnvu.}}% + \fi + \fi} +\def\nwidentdefs#1{% list of index pairs + \ifnw@hideunuseddefs\nwix@totaluses{#1}\else\nwix@listcount{#1}\fi + \ifnum\nwix@counter>0 + \nwcodecomment{\@nwlangdepdfs:}% + {\def\\##1{\nwix@id@uses ##1}#1}% + \fi} +<<noweb.sty>>= +\newif\ifnw@hideunuseddefs\nw@hideunuseddefsfalse +\def\nwopt@hideunuseddefs{\nw@hideunuseddefstrue} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B hideunuseddefs +Omit defined but unused identifiers from +the local identifier cross-reference (Preston Briggs). +@ \fi +<<noweb.sty>>= +\def\nwopt@noidentxref{% + \let\nwidentdefs\@gobble + \let\nwidentuses\@gobble} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B noidentxref +Omit the local identifier cross-reference which follows each code chunk. +@ \fi + +\subsubsection{Support for chunk and identifier indices} +The index in the back shows absolutely all the pages. +<<noweb.sty>>= +\def\nw@underlinedefs{% {list with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} + +\def\nw@indexline#1#2{% + {\indent {\Tt #1}: \nw@underlinedefs\@nameuse{nwixl@#2}\par}} + +\newenvironment{thenowebindex}{\parindent=-10pt \parskip=\z@ + \advance\leftskip by 10pt + \advance\rightskip by 0pt plus1in\par\@afterindenttrue + \def\\##1{\nw@indexline##1}}{} +@ +The information comes from the list [[nwisx@i]]. +<<noweb.sty>>= +\def\nowebindex{% + \@ifundefined{nwixs@i}% + {\@warning{The \string\nowebindex\space is empty}}% + {\begin{thenowebindex}\@nameuse{nwixs@i}\end{thenowebindex}}} +@ +Here's a more efficient version for the external case: +<<noweb.sty>>= +\def\nowebindex@external{% + {\let\nwixadds@c=\@gobble + \def\nwixadds@i##1{\nw@indexline##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebindex}\@input{\jobname.nwi}\end{thenowebindex}}} +@ +That list ([[nwisx@i]]) is created by calls to [[\nwixlogsorted{i}]]. +<<noweb.sty>>= +\def\nwixlogsorted#1#2{% list data + \@bsphack\if@filesw + \toks0={#2}\immediate\write\@auxout{\string\nwixadds{#1}{\the\toks0}} + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ +[[nwixs@c]] and [[nwixs@i]] are sorted lists of chunks and identifiers, respectively. +<<noweb.sty>>= +\def\nwixadds#1#2{% + \@ifundefined{nwixs@#1}% + {\global\@namedef{nwixs@#1}{\\{#2}}}% + {\expandafter\nwix@cons\csname nwixs@#1\endcsname{\\{#2}}}} +\let\nwixaddsx=\@gobbletwo +@ +If an external index is used, we need a [[.nwi]] file, +[[\nwixadds]] is to be ignored, and we use [[\nwixaddsx]]. +<<noweb.sty>>= +\def\nwopt@externalindex{% + \ifx\nwixadds\@gobbletwo % already called + \else + \let\nwixaddsx=\nwixadds \let\nwixadds=\@gobbletwo + \let\nowebindex=\nowebindex@external + \let\nowebchunks=\nowebchunks@external + \fi} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B externalindex +Use an index generated with +.I noindex(1) +(q.v.). +@ \fi +@ +<<noweb.sty>>= +\def\nowebchunks{% + \@ifundefined{nwixs@c}% + {\@warning{The are no \string\nowebchunks}}% + {\begin{thenowebchunks}\@nameuse{nwixs@c}\end{thenowebchunks}}} +\def\nowebchunks@external{% + {\let\nwixadds@i=\@gobble + \def\nwixadds@c##1{\nw@onechunk##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebchunks}\@input{\jobname.nwi}\end{thenowebchunks}}} + \@namedef{r@nw@notdef}{{0}{(\@nwlangdepnvd)}} +<<noweb.sty>>= +\def\nw@chunkunderlinedefs{% {list of labels with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} +<<noweb.sty>>= +\def\nw@onechunk#1#2#3{% {name}{label of first definition}{list with \nwixd, \nwixu} + \@ifundefined{r@#2}{}{% + \indent\LA #1~{\nwtagstyle\subpageref{#2}}\RA + \if@nwlongchunks{~\nw@chunkunderlinedefs#3}\fi\par}} +<<noweb.sty>>= +\newenvironment{thenowebchunks}{\vskip3pt + \parskip=\z@\parindent=-10pt \advance\leftskip by 10pt + \advance\rightskip by 0pt plus10pt \@afterindenttrue + \def\\##1{\nw@onechunk##1}}{} +<<noweb.sty>>= +\newif\if@nwlongchunks +\@nwlongchunksfalse +\let\nwopt@longchunks\@nwlongchunkstrue +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B longchunks +When expanding +.B "\\\\nowebchunks," +show page numbers of definitions and uses of each chunk. +@ \fi + +@ +\subsection{Support for hypertext} +There are two sets of support for hypertext. + Balasubramanian Narasimhan wrote initial support for \texttt{hyper.sty}. +<<noweb.sty>>= +\newcommand\@nw@hyper@ref{\hyperreference} % naras +\newcommand\@nw@hyper@anc{\blindhyperanchor} % naras +@ Norman Ramsey wrote support for the \texttt{hyperrref} package (May + 1998). +<<noweb.sty>>= +\newcommand\@nw@hyperref@ref[2]{\hyperlink{noweb.#1}{#2}} % nr +\newcommand\@nw@hyperref@anc[1]{\hypertarget{noweb.#1}{\relax}} % nr +%%\renewcommand\@nw@hyperref@ref[2]{{#2}} % nr +%%\renewcommand\@nw@hyperref@anc[1]{} % nr +@ We define the independent macros [[\nwhyperreference]] and + [[\nwblindhyperanchor]], +which test for the presence of one of these two packages, +redefine themselve accordingly, and re-invoke themselves. +<<noweb.sty>>= +\newcommand\nwhyperreference{% + \@ifundefined{hyperlink} + {\@ifundefined{hyperreference} + {\global\let\nwhyperreference\@gobble} + {\global\let\nwhyperreference\@nw@hyper@ref}} + {\global\let\nwhyperreference\@nw@hyperref@ref}% + \nwhyperreference +} + +\newcommand\nwblindhyperanchor{% + \@ifundefined{hyperlink} + {\@ifundefined{hyperreference} + {\global\let\nwblindhyperanchor\@gobble} + {\global\let\nwblindhyperanchor\@nw@hyper@anc}} + {\global\let\nwblindhyperanchor\@nw@hyperref@anc}% + \nwblindhyperanchor +} +@ +\subsection{Support for hypertext translation to HTML} +<<noweb.sty>>= +\newcommand\nwanchorto{% + \begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@anchorto} +\newcommand\nw@anchorto[1]{\endgroup\def\nw@next{#1}\nw@anchortofin} +\newcommand\nw@anchortofin[1]{#1\footnote{See URL \texttt{\nw@next}.}} +\let\nwanchorname\@gobble +<<man page: noweb style control sequences>>= +.PP +.B "\\\\nwanchorto{URL}{anchor text}" +.RS +Creates a link to the given URL with the given anchor text. +Implemented in +.I latex(1) +using footnotes, but +.I sl2h(1) +translates this to +.B "<a href=URL>anchor text</a>" +.RE +.PP +.B "\\\\nwanchorname{name}{anchor text}" +.RS +Creates an anchor point for a hyperlink. +Implemented in +.I latex(1) +using +.B "\\\\label", +but +.I sl2h(1) +translates this to +.B "<a name=name>anchor text</a>" +.RE +@ +This lets us hide stuff intended for use only when converting to HTML: +<<noweb.sty>>= +\newif\ifhtml +\htmlfalse +<<man page: noweb style control sequences>>= +.PP +.B "\\\\ifhtml ... \\\\fi" +.RS +Text between +.B "\\\\ifhtml" +and +.B "\\\\fi" +is ignored by +.I latex(1), +but +.I sl2h(1) +and the +.I l2h +noweb filter translate the text into HTML. +.RE +@ +\subsection{Support for Prettyprinting} +The following macro can be redefined to allow custom typesetting of +identifiers in the index and mini-indices. +<<noweb.sty>>= +\let\nwixident=\relax +@ %def \nwixident +@ +The following macros can be redefined to typeset `[[\]]', `[[{]]' and +`[[}]]' correctly in non-typewriter fonts. +The problem is that the built-in {\LaTeX} [[\{]] tries to produce a +math symbol, which doesn't exist in the typewriter font, so we get a +brace in the wrong font and a warning. Most unpleasant. +Noweave therefore attempts to emit [[\nwlbrace]] and [[\nwrbrace]] +wherever it believes braces should appear. +The standard noweb style is to set code in typewriter font, and so the +standard definitions just select the proper characters from that font. + People setting code in +fonts other than typewriter are responsible for redefining those +macros to work in their environment. +<<noweb.sty>>= +\def\nwbackslash{\char92} +\def\nwlbrace{\char123} +\def\nwrbrace{\char125} +<<nwmac.tex>>= +\def\nwbackslash{\char92} +\def\nwlbrace{\char123} +\def\nwrbrace{\char125} +@ +\subsection{Language-dependent macros} + +\label{subsection:langdeps} + +The language-dependent macros are defined here in each supported +language in a different subsubsection. + +The choice of language depends on testing the [[\languagename]] +macro. There must be a more elegant way of coding the tests below\ldots +<<noweb.sty>>= +<<language support>> +\ifx\languagename\undefined % default is English + \nwoptions{english} +\else + \@ifundefined{nwopt@\languagename} + {\nwoptions{english}} + {\expandafter\nwoptions\expandafter{\languagename}} +\fi +@ +\subsubsection{Support for English} + +This describes the original English text. +<<language support>>= +\def\nwopt@english{% + \def\@nwlangdepdef{This definition is continued}% + \def\@nwlangdepcud{This code is used}% + \def\@nwlangdeprtc{Root chunk (not used in this document)}% + \def\@nwlangdepcwf{This code is written to file}% + \def\@nwlangdepchk{chunk}% + \def\@nwlangdepin{in}% + \def\@nwlangdepand{and}% + \def\@nwlangdepuss{Uses}% + \def\@nwlangdepusd{used}% + \def\@nwlangdepnvu{never used}% + \def\@nwlangdepdfs{Defines}% + \def\@nwlangdepnvd{never defined}% +} +\let\nwopt@american\nwopt@english +@ + +@ +\subsubsection{Support for Portuguese} + +This contains the text in Portuguese. +<<language support>>= +\def\nwopt@portuges{% + \def\@nwlangdepdef{Defini\c{c}\~ao continuada em}% + % This definition is continued + \def\@nwlangdepcud{C\'odigo usado em}% + % This code is used + \def\@nwlangdeprtc{Fragmento de topo (sem uso no documento)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Este c\'odigo foi escrito no ficheiro}% + % This code is written to file + \def\@nwlangdepchk{fragmento}% + % chunk + \def\@nwlangdepin{no(s)}% + % in + \def\@nwlangdepand{e}% + % and + \def\@nwlangdepuss{Usa}% + % Uses + \def\@nwlangdepusd{usado}% + % used + \def\@nwlangdepnvu{nunca usado}% + % never used + \def\@nwlangdepdfs{Define}% + % Defines + \def\@nwlangdepnvd{nunca definido}% + % never defined +} +@ +\subsubsection{Support for French} + +This is a tentative translation to French that should probably be +corrected by some French native speaker\ldots + +There are problems with using accents: on the [[\@nwlangdepnvd]] macro +(which apparently is not used in the context of [[\nwcodecomment]]), +and in some other macros (\LaTeX{} complains about missing +[[\endcsname]]). This should be fixed by someone with experience in +using \TeX\ldots +<<language support>>= +\def\nwopt@frenchb{% + \def\@nwlangdepdef{Cette d\'efinition suit dans}% + % This definition is continued + \def\@nwlangdepcud{Ce code est employ\'e dans}% + % This code is used + \def\@nwlangdeprtc{Morceau racine (pas employ\'e dans ce document)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Ce code est \'ecrit dans le fichier}% + % This code is written to file + \def\@nwlangdepchk{morceau}% + % chunk + \def\@nwlangdepin{dans le(s)}% + % in + \def\@nwlangdepand{et}% + % and + \def\@nwlangdepuss{Il emploie}% + % Uses + \def\@nwlangdepusd{employ\'{e}}% + % used + \def\@nwlangdepnvu{jamais employ\'{e}}% + % never used + \def\@nwlangdepdfs{Il d\'{e}fine}% + % Defines + % Cannot use the accent here: \def\@nwlangdepnvd{jamais d\'{e}fini}% + \def\@nwlangdepnvd{jamais defini}% + % never defined +} +\let\nwopt@french\nwopt@frenchb +@ +\subsubsection{Support for German} + +This is a translation to German by Sabine Broda (DCC-FCUP \& LIACC, +Universidade do Porto). +<<language support>>= +\def\nwopt@german{% + \def\@nwlangdepdef{Diese Definition wird fortgesetzt}% + % This definition is continued + \def\@nwlangdepcud{Dieser Code wird benutzt}% + % This code is used + \def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Dieser Code schreibt man zum File}% + % This code is written to file + \def\@nwlangdepchk{Teil}% + % chunk + \def\@nwlangdepin{im}% + % in + \def\@nwlangdepand{und}% + % and + \def\@nwlangdepuss{Benutztt}% + % Uses + \def\@nwlangdepusd{benutzt}% + % used + \def\@nwlangdepnvu{nicht benutzt}% + % never used + \def\@nwlangdepdfs{Definiert}% + % Defines + \def\@nwlangdepnvd{nicht definiert}% + % never defined +} +\let\nwopt@ngerman\nwopt@german +@ +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B english, french, german, portuges +Write cross-reference information in the language specified. +Defaults to +.B english. +@ \fi +@ +\clearpage +\section{The {\tt nwmac} macros for use with plain {\TeX}} + +First we make [[@]] a letter so that we can use `private' macro names. +<<nwmac.tex>>= +\catcode`\@=11 +<<nwmac.tex>>= +% scale cmbx10 instead of using cmbx12 because {\LaTeX} does, so fonts exist +\font\twlbf=cmbx10 scaled \magstep1 +\font\frtbf=cmbx10 scaled \magstep2 +% These fonts don't work with xdvi! + +\advance\hoffset 0.5 true in +\advance\hsize -1.5 true in +\newdimen\textsize +\textsize=\hsize +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +@ %def \textsize \today +<<nwmac.tex>>= +\long\def\ifundefined#1#2#3{% + \expandafter\ifx\csname#1\endcsname\relax + #2% + \else#3% + \fi} + +\ifundefined{myheadline} + {\headline={\hbox to \textsize{\tentt\firstmark\hfil\tenrm\today\hbox + to 4em{\hss\folio}}\hss}} + {\expandafter\headline\expandafter{\myheadline}} + +\ifundefined{myfootline} + {\footline={\hfil}} + {\expandafter\footline\expandafter{\myfootline}} +@ %def \ifundefined +<<nwmac.tex>>= +\def\semifilbreak{\vskip0pt plus1.5in\penalty-200\vskip0pt plus -1.5in} +\raggedbottom +@ %def \semifilbreak +<<nwmac.tex>>= +% +% \chapcenter macro to produce nice centered chapter titles +% +\def\chapcenter{\leftskip=0.5 true in plus 4em minus 0.5 true in + \rightskip=\leftskip + \parfillskip=0pt \spaceskip=.3333em \xspaceskip=.5em + \pretolerance=9999 \tolerance=9999 + \hyphenpenalty=9999 \exhyphenpenalty=9999} +@ %def \chapcenter +<<nwmac.tex>>= +% \startsection{LEVEL}{INDENT}{BEFORESKIP}{AFTERSKIP}{STYLE}{HEADING} +% #1 #2 #3 #4 #5 #6 +% +% LEVEL: depth; e.g. part=0 chapter=1 sectino=2... +% INDENT: indentation of heading from left margin +% BEFORESKIP: skip before header +% AFTERSKIP: skip after header +% STYLE: style of heading; e.g.\bf +% HEADING: heading of the sectino +% +\def\startsection#1#2#3#4#5#6{\par\vskip#3 plus 2in + \penalty-200\vskip 0pt plus -2in + \noindent{\leftskip=#2 \rightskip=0.5true in plus 4em minus 0.5 true in + \hyphenpenalty=9999 \exhyphenpenalty=9999 + #5#6\par}\vskip#4% + {\def\code##1{[[}\def\edoc##1{]]}\message{[#6]}} + \settocparms{#1} + \def\themodtitle{#6} +%%%% {\def\code{\string\code}\def\edoc{\string\edoc}% + \edef\next{\noexpand\write\cont{\tocskip + \tocline{\hskip\tocindent\tocstyle\relax\themodtitle} + {\noexpand\the\pageno}}}\next % write to toc + %} +} +@ %def \startsection +<<nwmac.tex>>= +\def\settocparms#1{ + \count@=#1 + \ifnum\count@<1 + \def\tocskip{\vskip3ptplus1in\penalty-100 + \vskip0ptplus-1in}% + \def\tocstyle{\bf} + \def\tocindent{0pt} + \else + \def\tocskip{} + \def\tocstyle{\rm} + \dimen@=2em \advance\count@ by \m@ne \dimen@=\count@\dimen@ + \edef\tocindent{\the\dimen@} + \fi +} +@ %def \settocparms +<<nwmac.tex>>= +\def\tocline#1#2{\line{{\ignorespaces#1}\leaders\hbox to .5em{.\hfil}\hfil + \hbox to1.5em{\hss#2}}} +@ %def \tocline +<<nwmac.tex>>= +\def\section#1{\par \vskip3ex\noindent {\bf #1}\par\nobreak\vskip1ex\nobreak} +\def\chapter#1{\vfil\eject\startsection{0}{0pt}{6ex}{3ex}{\frtbf\chapcenter}{#1}} +\def\section#1{\startsection{1}{0pt}{4ex}{2ex}{\twlbf}{#1}} +\def\subsection#1{\startsection{2}{0pt}{2ex}{1ex}{\bf}{#1}} +\def\subsubsection#1{\startsection{3}{0pt}{1ex}{0.5ex}{\it}{#1}} +\def\paragraph#1{\startsection{4}{0pt}{1.5ex}{0ex}{\it}{#1}} + +<<kernel>> + +\def\nwfilename#1{\vfil\eject\mark{#1}} + +\def\nwbegindocs#1{\filbreak} +\def\nwenddocs{\par} +\def\nwbegincode#1{\par\nobreak + \begingroup\setupcode\newlines\parindent=0pt\parskip=0pt + \let\oendmoddef=\endmoddef \let\oplusendmoddef=\plusendmoddef + \def\endmoddef{\oendmoddef\par}\def\plusendmoddef{\oplusendmoddef\par}% + \hsize=\codehsize\noindent\bchack} +\def\nwendcode{\endgroup} +{\catcode`\^^M=\active % make CR an active character + \gdef\bchack#1^^M{\relax#1}% +} +@ %def \section \chapter \section \subsection \subsubsection \paragraph +@ %def \nwbegindocs \nwenddocs \nwbegincode \nwendcode \nwfilename +<<nwmac.tex>>= +\edef\contentsfile{\jobname.toc } % file that gets table of contents info +\def\readcontents{\expandafter\input \contentsfile} + +\newwrite\cont +\openout\cont=\contentsfile +\write\cont{\string\catcode`\string\@=11}% a hack to make contents +@ %def \readcontents \cont +<<nwmac.tex>>= + % take stuff in plain.tex +\def\bye{% + \write\cont{}% ensure that the contents file isn't empty + \closeout\cont + \vfil\eject\pageno=-1 % new page causes contents to be really closed + \topofcontents\readcontents\botofcontents + \vfil\eject\end} +\def\topofcontents{\vfil\mark{{\bf Contents}}} +\def\botofcontents{} +@ %def \bye \topofcontents \botofcontents +<<nwmac.tex>>= +\let\em=\it +% used to produce an itemized (bulleted) list in plain {\TeX} +% such lists can be nested +% mostly useful with WEB + +% Usage: +% \itemize +% \item First thing +% \item second thing +% \enditemize + +\newcount\listlevel +\listlevel=0 +\newdimen\itemwidth +\itemwidth=3em + +\def\itemize{\begingroup\advance\listlevel by1 + \def\item{\par\noindent + \raise2pt\llap{$\scriptstyle\bullet$\ }\ignorespaces}% + \def\nameditem##1{\par\noindent + \llap{\rlap{##1}\hskip\itemwidth}\ignorespaces}% + \par\advance\leftskip by\itemwidth\advance\rightskip by0.5\itemwidth} +\def\enditemize{\par\endgroup\noindent\ignorespaces} + +\let\begindocument=\relax +@ %def \listlevel \itemwidth \itemize \enditemize +Finally we make [[@]] `other' again. +<<nwmac.tex>>= +\catcode`\@=12 + +@ +\section{Chunks} \nowebchunks +\twocolumn[\section{Index}] +\nowebindex* +\end{document} + + + diff --git a/web/noweb/src/tex/n.sty b/web/noweb/src/tex/n.sty new file mode 100644 index 0000000000..b27eaa2ad8 --- /dev/null +++ b/web/noweb/src/tex/n.sty @@ -0,0 +1,159 @@ +\def\semifilbreak{\vskip0pt plus1.5in\penalty-200\vskip0pt plus -1.5in} +\let\filbreak=\semifilbreak +\raggedbottom +\def\begindocument{\begin{document}} + +\def\LL{<<} +\def\GG{>>} +\def\LLS{[[} +\def\RRS{]]} + +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=10pt +\advance\codehsize by \codemargin + +\begingroup \catcode `|=0 \catcode `[= 1 +\catcode`]=2 \catcode `\{=12 \catcode `\}=12 +\catcode`\\=12 |gdef|@xbegincode#1|endcode[#1|@xendcode] +|gdef|@xwebcode#1\end{webcode}[#1|end[webcode]] +|endgroup + + +\def\@gobble#1{} + +\def\@begincode{\par\linewidth\codehsize\hsize\codehsize\textwidth\codehsize +\trivlist \item[]\if@minipage\else\vskip\parskip\fi +\leftskip\@totalleftmargin\rightskip\z@ +\advance\leftskip\codemargin +\parindent\z@\parfillskip\@flushglue\parskip\z@ +\@tempswafalse \def\par{\if@tempswa\hbox{}\fi\@tempswatrue\@@par} +\obeylines \tt \catcode``=13 \@noligs \setupcode} + +\def\begincode#1{\begingroup\@begincode \frenchspacing\@vobeyspaces \@xbegincode} +\def\@xendcode{\endtrivlist\endgroup} + +\def\webcode{\@webcode \frenchspacing\@vobeyspaces \@xwebcode} +\let\endwebcode=\endtrivlist + + +\chardef\other=12 +\def\setupcode{ + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \obeyspaces \@vobeyspaces\tt +} +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \rm} +\def\LA{\begingroup\setupmodname\it$\langle${}} +\def\RA{\/$\rangle$\endgroup} +\def\code{\leavevmode\begingroup\setupcode} +\def\edoc{\endgroup} + +\def\begindocs#1{\relax} +\def\enddocs{\relax} +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\unhcopy\equivbox} +\def\plusendmoddef{\RA\unhcopy\plusequivbox} + +\def\filename#1{\vfil\eject\mark{#1}} + + +\let\chunklist=\relax + +%%%%%%%%%%%%%%%% following code supports cross-referencing +% lists of page references cause all the trouble + + +\def\alsodefined#1{\hspace{-\parindent}{\footnotesize\rm + This definition is continued on \pages{#1}.}} +\def\used#1{\hspace{-\parindent}{\footnotesize\rm + This code is used on \pages{#1}.}} +\def\notused{\hspace{-\parindent}{\footnotesize\rm + Root module (not used in this document).}} + + +\def\empty{} +\def\lop#1\to#2{\expandafter\lopoff#1\lopoff#1#2} +\long\def\lopoff\\#1#2\lopoff#3#4{\def#4{#1}\def#3{#2}} +\def\loop#1\repeat{\def\iterate{#1\expandafter\iterate\fi}% + \iterate \let\iterate\relax} + +\def\thepageref#1{\@ifundefined{r@#1}{0\@warning + {Reference `#1' on page \thepage \space + undefined}}{\@nameuse{r@#1}}} + +\newcount\lastpage +\newcount\thispage +\newcount\pagesout + +\def\dosetpage#1#2#3\@nil{\thispage=#2\relax} + +\def\setthispage#1\done{% + \@ifundefined{r@#1}{{\bf ??}\@warning + {Reference `#1' on page \thepage \space + undefined}\thispage=0}{\edef\@tempa{\@nameuse{r@#1}}\expandafter + \dosetpage\@tempa\@nil}} + +\def\setpagenumbers#1{% + \def\thepages{#1}% + \def\pagenumbers{}% + \lastpage=-1 + \loop\ifx\thepages\empty\else + \lop\thepages\to\lastref + \expandafter\setthispage\lastref\done + \ifnum\thispage=\lastpage \else + \edef\pagenumbers{\pagenumbers\noexpand\\{\the\thispage}}% + \lastpage=\thispage + \fi + \repeat} + +\def\pages#1{% + \setpagenumbers{#1}% + \expandafter\dopages\pagenumbers\done +} + +\def\dopages#1\done{% + \def\thepages{#1}% + \lop\thepages\to\lastref + \lastpage=\lastref + \ifx\thepages\empty page \else pages \fi + \the\lastpage + \pagesout=1 + \loop\ifx\thepages\empty\else + \lop\thepages\to\lastref + \thispage=\lastref + \ifx\thepages\empty + \ifnum\pagesout>1 , \else{} \fi + and + \else , + \fi + \the\thispage + \advance\pagesout by 1 + \lastpage=\thispage + \repeat} diff --git a/web/noweb/src/tex/noweb.sty b/web/noweb/src/tex/noweb.sty new file mode 100644 index 0000000000..ea294c0a1c --- /dev/null +++ b/web/noweb/src/tex/noweb.sty @@ -0,0 +1,969 @@ +% noweb.sty -- LaTeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +{\obeyspaces\AtBeginDocument{\global\let =\ }} % from texbook, p 381 +\def\nwopt@nomargintag{\let\nwmargintag=\@gobble} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\llap{##1\kern\nwmarginglue\kern\codemargin}}} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\kern-\codemargin\nwthemargintag{##1}\kern\codemargin}} +\def\nwthemargintag#1{\llap{#1\kern\nwmarginglue}} +\nwopt@margintag +\newdimen\nwmarginglue +\nwmarginglue=0.3in +\def\nwtagstyle{\footnotesize\Rm} +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=0pt +\newdimen\nwdefspace +\nwdefspace=\codehsize +% need to use \textwidth in {\LaTeX} to handle styles with +% non-standard margins (David Bruce). Don't know why we sometimes +% wanted \hsize. 27 August 1997. +%% \advance\nwdefspace by -\hsize\relax +\ifx\textwidth\undefined + \advance\nwdefspace by -\hsize\relax +\else + \advance\nwdefspace by -\textwidth\relax +\fi +\chardef\other=12 +\def\setupcode{% + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \catcode`\"=\other % fixes problem with german.sty + \obeyspaces\Tt +} +\def\nwendquote{\relax\ifhmode\spacefactor=1000 \fi} +{\catcode`\^^M=\active % make CR an active character + \gdef\newlines{\catcode`\^^M=\active % make CR an active character + \def^^M{\par\startline}}% + \gdef\eatline#1^^M{\relax}% +} +%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write +\def\startline{\noindent\hskip\parindent\ignorespaces} +\def\nwnewline{\ifvmode\else\hfil\break\leavevmode\hbox{}\fi} +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \let\{\lbrace + \let\}\rbrace + % bad news --- don't know what catcode to give " + \Rm} +\def\LA{\begingroup\maybehbox\bgroup\setupmodname\It$\langle$} +\def\RA{\/$\rangle$\egroup\endgroup} +\def\code{\leavevmode\begingroup\setupcode\newlines} +\def\edoc{\endgroup} +\let\maybehbox\relax +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\ifmmode\equiv\else\unhcopy\equivbox\fi + \nobreak\hfill\nobreak} +\def\plusendmoddef{\RA\ifmmode\mathord{+}\mathord{\equiv}\else\unhcopy\plusequivbox\fi + \nobreak\hfill\nobreak} +\def\chunklist{% +\errhelp{I changed \chunklist to \nowebchunks. +I'll try to avoid such incompatible changes in the future.}% +\errmessage{Use \string\nowebchunks\space instead of \string\chunklist}} +\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}} +\def\nowebindex{\message{<Warning: You need noweave -index to use \string\nowebindex>}} +% here is support for the new-style (capitalized) font-changing commands +% thanks to Dave Love +\ifx\documentstyle\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % plain +\else\ifx\selectfont\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % LaTeX OFSS +\else % LaTeX NFSS + \def\Rm{\reset@font\rm} + \def\It{\reset@font\it} + \def\Tt{\reset@font\tt} + \def\Bf{\reset@font\bf} +\fi\fi +\ifx\reset@font\undefined \let\reset@font=\relax \fi +\def\nwbackslash{\char92} +\def\nwlbrace{\char123} +\def\nwrbrace{\char125} +\def\noweboptions#1{% + \def\@nwoptionlist{#1}% + \@for\@nwoption:=\@nwoptionlist\do{% + \@ifundefined{nwopt@\@nwoption}{% + \@latexerr{There is no such noweb option as '\@nwoption'}\@eha}{% + \csname nwopt@\@nwoption\endcsname}}} +\codemargin=10pt +\advance\codehsize by \codemargin % make room for indentation of code +\advance\nwdefspace by \codemargin % and fix adjustment for def/use +\def\setcodemargin#1{% + \advance\codehsize by -\codemargin % make room for indentation of code + \advance\nwdefspace by -\codemargin % and fix adjustment for def/use + \codemargin=#1 + \advance\codehsize by \codemargin % make room for indentation of code + \advance\nwdefspace by \codemargin % and fix adjustment for + % def/use +} +\def\nwopt@shift{% + \dimen@=-0.8in + \if@twoside % Values for two-sided printing: + \advance\evensidemargin by \dimen@ + \else % Values for one-sided printing: + \advance\evensidemargin by \dimen@ + \advance\oddsidemargin by \dimen@ + \fi +% \advance \marginparwidth -\dimen@ +} +\let\nwopt@noshift\@empty +\def\nwbegincode#1{% + \begingroup + \topsep \nwcodetopsep + \@beginparpenalty \@highpenalty + \@endparpenalty -\@highpenalty + \@begincode } +\def\nwendcode{\endtrivlist \endgroup \filbreak} % keeps code on 1 page + +\newenvironment{webcode}{% + \@begincode +}{% + \endtrivlist} +\def\@begincode{% + \trivlist \item[]% + \leftskip\@totalleftmargin \advance\leftskip\codemargin + \rightskip\hsize \advance\rightskip -\codehsize + \parskip\z@ \parindent\z@ \parfillskip\@flushglue + \linewidth\codehsize + \@@par + \def\par{\leavevmode\null \@@par \penalty\nwcodepenalty}% + \obeylines + \@noligs \ifx\verbatim@nolig@list\undefined\else + \let\do=\nw@makeother \verbatim@nolig@list \do@noligs\` + \fi + \setupcode \frenchspacing \@vobeyspaces + \nowebsize \setupcode + \let\maybehbox\mbox } + \newskip\nwcodetopsep \nwcodetopsep = 3pt plus 1.2pt minus 1pt + \let\nowebsize=\normalsize + \def\nwopt@tinycode{\let\nowebsize=\tiny} + \def\nwopt@footnotesizecode{\let\nowebsize=\footnotesize} + \def\nwopt@scriptsizecode{\let\nowebsize=\scriptsize} + \def\nwopt@smallcode{\let\nowebsize=\small} + \def\nwopt@normalsizecode{\let\nowebsize=\normalsize} + \def\nwopt@largecode{\let\nowebsize=\large} + \def\nwopt@Largecode{\let\nowebsize=\Large} + \def\nwopt@LARGEcode{\let\nowebsize=\LARGE} + \def\nwopt@hugecode{\let\nowebsize=\huge} + \def\nwopt@Hugecode{\let\nowebsize=\Huge} +\newcount\nwcodepenalty \nwcodepenalty=\@highpenalty +\def\nw@makeother#1{\catcode`#1=12 } +\def\nwbegindocs#1{\ifvmode\noindent\fi} +\let\nwenddocs=\relax +\let\nwdocspar=\filbreak +\def\@nwsemifilbreak#1{\vskip0pt plus#1\penalty-200\vskip0pt plus -#1} +\newdimen\nwbreakcodespace +\nwbreakcodespace=0.2in % by default, leave no more than this on a page +\def\nwopt@breakcode{% + \def\nwdocspar{\@nwsemifilbreak{0.2in}}% + \def\nwendcode{\endtrivlist\endgroup} % ditches filbreak +} +\raggedbottom +\def\code{\leavevmode\begingroup\setupcode\@vobeyspaces\obeylines} +\let\edoc=\endgroup +\newdimen\@original@textwidth +\def\ps@noweb{% + \@original@textwidth=\textwidth + \let\@mkboth\@gobbletwo + \def\@oddfoot{}\def\@evenfoot{}% No feet. + \if@twoside % If two-sided printing. + \def\@evenhead{\hbox to \@original@textwidth{% + \Rm \thepage\qquad{\Tt\leftmark}\hfil\today}}% Left heading. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \else % If one-sided printing. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \let\@evenhead\@oddhead + \fi + \let\chaptermark\@gobble + \let\sectionmark\@gobble + \let\subsectionmark\@gobble + \let\subsubsectionmark\@gobble + \let\paragraphmark\@gobble + \let\subparagraphmark\@gobble + \def\nwfilename{\begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@filename} + \def\nw@filename##1{\endgroup\markboth{##1}{##1}\let\nw@filename=\nw@laterfilename}% +} +\def\nw@laterfilename#1{\endgroup\clearpage \markboth{#1}{#1}} +\let\nwfilename=\@gobble +\def\nwcodecomment#1{\@@par\penalty\nwcodepenalty + \if@firstnwcodecomment + \vskip\nwcodecommentsep\penalty\nwcodepenalty\@firstnwcodecommentfalse + \fi% + \hspace{-\codemargin}{% + \rightskip=0pt plus1in + \interlinepenalty\nwcodepenalty + \let\\\relax\footnotesize\Rm #1\@@par\penalty\nwcodepenalty}} +\def\@nwalsodefined#1{\nwcodecomment{\@nwlangdepdef\ \nwpageprep\ \@pagesl{#1}.}} +\def\@nwused#1{\nwcodecomment{\@nwlangdepcud\ \nwpageprep\ \@pagesl{#1}.}} +\def\@nwnotused#1{\nwcodecomment{\@nwlangdeprtc.}} +\def\nwoutput#1{\nwcodecomment{\@nwlangdepcwf\ {\Tt \@stripstar#1*\stripped}.}} +\def\@stripstar#1*#2\stripped{#1} +\newcommand{\nwprevdefptr}[1]{% + \mbox{$\mathord{\triangleleft}\,\mathord{\mbox{\subpageref{#1}}}$}} +\newcommand{\nwnextdefptr}[1]{% + \mbox{$\mathord{\mbox{\subpageref{#1}}}\,\mathord{\triangleright}$}} + +\newcommand{\@nwprevnextdefs}[2]{% + {\nwtagstyle + \ifx\relax#1\else ~~\nwprevdefptr{#1}\fi + \ifx\relax#2\else ~~\nwnextdefptr{#2}\fi}} +\newcommand{\@nwusesondefline}[1]{{\nwtagstyle~~(\@pagenumsl{#1})}} +\newcommand{\@nwstartdeflinemarkup}{\nobreak\hskip 1.5em plus 1fill\nobreak} +\newcommand{\@nwenddeflinemarkup}{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak} +\def\nwopt@longxref{% + \let\nwalsodefined\@nwalsodefined + \let\nwused\@nwused + \let\nwnotused\@nwnotused + \let\nwprevnextdefs\@gobbletwo + \let\nwusesondefline\@gobble + \let\nwstartdeflinemarkup\relax + \let\nwenddeflinemarkup\relax +} +\def\nwopt@shortxref{% + \let\nwalsodefined\@gobble + \let\nwused\@gobble + \let\nwnotused\@gobble + \let\nwprevnextdefs\@nwprevnextdefs + \let\nwusesondefline\@nwusesondefline + \let\nwstartdeflinemarkup\@nwstartdeflinemarkup + \let\nwenddeflinemarkup\@nwenddeflinemarkup +} +\def\nwopt@noxref{% + \let\nwalsodefined\@gobble + \let\nwused\@gobble + \let\nwnotused\@gobble + \let\nwprevnextdefs\@gobbletwo + \let\nwusesondefline\@gobble + \let\nwstartdeflinemarkup\relax + \let\nwenddeflinemarkup\relax +} +\nwopt@shortxref % to hell with backward compatibility! +\newskip\nwcodecommentsep \nwcodecommentsep=3pt plus 1pt minus 1pt +\newif\if@firstnwcodecomment\@firstnwcodecommenttrue +\newcount\@nwlopage\newcount\@nwhipage % range lo..hi-1 +\newcount\@nwlosub % subpage of lo +\newcount\@nwhisub % subpage of hi +\def\@nwfirstpage#1#2#3{% subpage page xref-tag + \@nwlopage=#2 \@nwlosub=#1 + \def\@nwloxreftag{#3}% + \advance\@nwpagecount by \@ne + \@nwhipage=\@nwlopage\advance\@nwhipage by \@ne } +\def\@nwnextpage#1#2#3{% subpage page xref-tag + \ifnum\@nwhipage=#2 + \advance\@nwhipage by \@ne + \advance\@nwpagecount by \@ne + \@nwhisub=#1 + \def\@nwhixreftag{#3}\else + \ifnum#2<\@nwlopage \advance\@nwhipage by \m@ne + \ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% + \else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \fi + \fi + \fi% + \fi + \fi% + \edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\@nwfirstpage{#1}{#2}{#3}\else + \ifnum#2>\@nwhipage \advance\@nwhipage by \m@ne + \ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% + \else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \fi + \fi + \fi% + \fi + \fi% + \edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\@nwfirstpage{#1}{#2}{#3}\else + \@nwlosub=0 \@nwhisub=0 + \fi\fi\fi + } +\newcount\@nwpagetemp +\newcount\@nwpagecount +\def\@nwfirstpagel#1{% label + \@ifundefined{r@#1}{\@warning{Reference `#1' on page \thepage \space undefined}% + \nwix@cons\nw@pages{\\{\bf ??}}}{% + \edef\@tempa{\noexpand\@nwfirstpage\subpagepair{#1}{#1}}\@tempa}} +\def\@nwnextpagel#1{% label + \@ifundefined{r@#1}{\@warning{Reference `#1' on page \thepage \space undefined}% + \nwix@cons\nw@pages{\\{\bf ??}}}{% + \edef\@tempa{\noexpand\@nwnextpage\subpagepair{#1}{#1}}\@tempa}} +\def\@pagesl#1{% list of labels + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + \advance\@nwhipage by \m@ne + \ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% + \else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \fi + \fi + \fi% + \fi + \fi% + \edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\def\\##1{\@nwhyperpagenum##1}% + \ifnum\@nwpagecount=1 \nwpageword \else \nwpagesword\fi~\commafy{\nw@pages}} +\def\@nwhyperpagenum#1#2{\nwhyperreference{#2}{#1}} + +\def\@pagenumsl#1{% list of labels -- doesn't include word `pages', commas, or `and' + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + \advance\@nwhipage by \m@ne + \ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% + \else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \fi + \fi + \fi% + \fi + \fi% + \edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa% + \def\\##1{\@nwhyperpagenum##1\let\\=\@nwpagenumslrest}\nw@pages} +\def\@nwpagenumslrest#1{~\@nwhyperpagenum#1} +\def\subpages#1{% list of {{subpage}{page}} + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\edef\@tempa{\noexpand\@nwfirstpage##1{}}\@tempa + \def\\####1{\edef\@tempa{\noexpand\@nwnextpage####1}\@tempa}}#1% + \advance\@nwhipage by \m@ne + \ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% + \else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \fi + \fi + \fi% + \fi + \fi% + \edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\def\\##1{\@firstoftwo##1}% + \ifnum\@nwpagecount=1 \nwpageword \else \nwpagesword\fi~\commafy{\nw@pages}} +\def\@nwaddrange{\advance\@nwhipage by \m@ne + \ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% + \else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}% + \fi + \fi + \fi% + \fi + \fi% + \edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa} +\def\nwpageword{\@nwlangdepchk} % chunk, was page +\def\nwpagesword{\@nwlangdepchks} % chunk, was page +\def\nwpageprep{\@nwlangdepin} % in, was on +\newcommand\nw@genericref[2]{% what to do, name of ref + \expandafter\nw@g@nericref\csname r@#2\endcsname#1{#2}} +\newcommand\nw@g@nericref[3]{% control sequence, what to do, name + \ifx#1\relax + \ref{#3}% trigger the standard `undefined ref' mechanisms + \else + \expandafter#2#1.\\% + \fi} +\def\nw@selectone#1#2#3\\{#1} +\def\nw@selecttwo#1#2#3\\{#2} +\def\nw@selectonetwo#1#2#3\\{{#1}{#2}} +\newcommand{\subpageref}[1]{% + \nwhyperreference{#1}{\nw@genericref\@subpageref{#1}}} +\def\@subpageref#1#2#3\\{% + \@ifundefined{2on#2}{#2}{\nwthepagenum{#1}{#2}}} +\newcommand{\subpagepair}[1]{% % produces {subpage}{page} + \@ifundefined{r@#1}% + {{0}{0}}% + {\nw@genericref\@subpagepair{#1}}} +\def\@subpagepair#1#2#3\\{% + \@ifundefined{2on#2}{{0}{#2}}{{#1}{#2}}} +\newcommand{\sublabel}[1]{% + \leavevmode % needed to make \@bsphack work + \@bsphack + \nwblindhyperanchor{#1}% + \if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\newcommand{\nosublabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newlabel{#1}{{0}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\newcommand\newsublabel{% + \nw@settrailers + \global\let\newsublabel\@newsublabel + \@newsublabel} +\newcommand{\@newsublabel}[2]{% + \edef\this@page{\@cdr#2\@nil}% + \ifx\this@page\last@page\else + \sub@page=\z@ + \fi + \edef\last@page{\this@page} + \advance\sub@page by \@ne + \ifnum\sub@page=\tw@ + \global\@namedef{2on\this@page}{}% + \fi + \pendingsublabel{#1}% + \edef\@tempa##1{\noexpand\newlabel{##1}% + {{\number\sub@page}{\this@page}\nw@labeltrailers}}% + \pending@sublabels + \def\pending@sublabels{}} +\newcommand\nw@settrailers{% -- won't work on first run + \@ifpackageloaded{nameref}% + {\gdef\nw@labeltrailers{{}{}{}}}% + {\gdef\nw@labeltrailers{}}} +\renewcommand\nw@settrailers{% + \@ifundefined{@secondoffive}% + {\gdef\nw@labeltrailers{}}% + {\gdef\nw@labeltrailers{{}{}{}}}} +\newcommand{\nextchunklabel}[1]{% + \nwblindhyperanchor{#1}% % looks slightly bogus --- nr + \@bsphack\if@filesw {\let\thepage\relax + \edef\@tempa{\write\@auxout{\string\pendingsublabel{#1}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\newcommand\pendingsublabel[1]{% + \def\@tempa{\noexpand\@tempa}% + \edef\pending@sublabels{\noexpand\@tempa{#1}\pending@sublabels}} +\def\pending@sublabels{} +\def\last@page{\relax} +\newcount\sub@page +\def\@alphasubpagenum#1#2{#2\ifnum#1=0 \else\@alph{#1}\fi} +\def\@nosubpagenum#1#2{#2} +\def\@numsubpagenum#1#2{#2\ifnum#1=0 \else.\@arabic{#1}\fi} +\def\nwopt@nosubpage{\let\nwthepagenum=\@nosubpagenum\nwopt@nomargintag} +\def\nwopt@numsubpage{\let\nwthepagenum=\@numsubpagenum} +\def\nwopt@alphasubpage{\let\nwthepagenum=\@alphasubpagenum} +\nwopt@alphasubpage +\newcount\@nwalph@n +\let\@nwalph@d\@tempcnta +\let\@nwalph@bound\@tempcntb +\def\@nwlongalph#1{{% + \@nwalph@n=#1\advance\@nwalph@n by-1 + \@nwalph@bound=26 + \loop\ifnum\@nwalph@n<\@nwalph@bound\else + \advance\@nwalph@n by -\@nwalph@bound + \multiply\@nwalph@bound by 26 + \repeat + \loop\ifnum\@nwalph@bound>1 + \divide\@nwalph@bound by 26 + \@nwalph@d=\@nwalph@n\divide\@nwalph@d by \@nwalph@bound + % d := d * bound ; n -:= d; d := d / bound --- saves a temporary + \multiply\@nwalph@d by \@nwalph@bound + \advance\@nwalph@n by -\@nwalph@d + \divide\@nwalph@d by \@nwalph@bound + \advance\@nwalph@d by 1 \@alph{\@nwalph@d}% + \repeat +}} +\newcount\nw@chunkcount +\nw@chunkcount=\@ne +\newcommand{\weblabel}[1]{% + \@bsphack + \nwblindhyperanchor{#1}% + \if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\number\nw@chunkcount}}}}% + \expandafter}\@tempa + \global\advance\nw@chunkcount by \@ne + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\def\nwopt@webnumbering{% + \let\sublabel=\weblabel + \def\nwpageword{chunk}\def\nwpagesword{chunks}% + \def\nwpageprep{in}} +% \nwindexdefn{printable name}{identifying label}{label of chunk} +% \nwindexuse{printable name}{identifying label}{label of chunk} + +\def\nwindexdefn#1#2#3{\@auxix{\protect\nwixd}{#2}{#3}} +\def\nwindexuse#1#2#3{\@auxix{\protect\nwixu}{#2}{#3}} + +\def\@auxix#1#2#3{% {marker}{id label}{subpage label} + \@bsphack\if@filesw {\let\nwixd\relax\let\nwixu\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string\nwixadd{#1}{#2}{#3}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +% \nwixadd{marker}{idlabel}{subpage label} +\def\nwixadd#1#2#3{% + \@ifundefined{nwixl@#2}% + {\global\@namedef{nwixl@#2}{#1{#3}}}% + {\expandafter\nwix@cons\csname nwixl@#2\endcsname{#1{#3}}}} +\def\@nwsubscriptident#1#2{\mbox{$\mbox{#1}_{\mathrm{\subpageref{#2}}}$}} +\def\@nwnosubscriptident#1#2{#1} +\def\@nwhyperident#1#2{\leavevmode\nwhyperreference{#2}{#1}} +\def\nwopt@subscriptidents{% + \let\nwlinkedidentq\@nwsubscriptident + \let\nwlinkedidentc\@nwsubscriptident +} +\def\nwopt@nosubscriptidents{% + \let\nwlinkedidentq\@nwnosubscriptident + \let\nwlinkedidentc\@nwnosubscriptident +} +\def\nwopt@hyperidents{% + \let\nwlinkedidentq\@nwhyperident + \let\nwlinkedidentc\@nwhyperident +} +\def\nwopt@nohyperidents{% + \let\nwlinkedidentq\@nwnosubscriptident + \let\nwlinkedidentc\@nwnosubscriptident +} +\def\nwopt@subscriptquotedidents{% + \let\nwlinkedidentq\@nwsubscriptident +} +\def\nwopt@nosubscriptquotedidents{% + \let\nwlinkedidentq\@nwnosubscriptident +} +\def\nwopt@hyperquotedidents{% + \let\nwlinkedidentq\@nwhyperident +} +\def\nwopt@nohyperquotedidents{% + \let\nwlinkedidentq\@nwnosubscriptident +} +\nwopt@hyperidents +\newcount\@commacount +\def\commafy#1{% + {\nwix@listcount{#1}\@commacount=\nwix@counter + \let\@comma@each=\\% + \ifcase\@commacount\let\\=\@comma@each\or\let\\=\@comma@each\or + \def\\{\def\\{ \@nwlangdepand\ \@comma@each}\@comma@each}\else + \def\\{\def\\{, % + \advance\@commacount by \m@ne + \ifnum\@commacount=1 \@nwlangdepand~\fi\@comma@each}\@comma@each}\fi + #1}} +\def\nwix@cons#1#2{% {list}{\marker{element}} + {\toks0=\expandafter{#1}\def\@tempa{#2}\toks2=\expandafter{\@tempa}% + \xdef#1{\the\toks0 \the\toks2 }}} +\def\nwix@uses#1{% {label} + \def\nwixu{\\}\let\nwixd\@gobble\@nameuse{nwixl@#1}} +\def\nwix@defs#1{% {label} + \def\nwixd{\\}\let\nwixu\@gobble\@nameuse{nwixl@#1}} +\newcount\nwix@counter +\def\nwix@listcount#1{% {list with \\} + {\count@=0 + \def\\##1{\advance\count@ by \@ne }% + #1\global\nwix@counter=\count@ }} +\def\nwix@usecount#1{\nwix@listcount{\nwix@uses{#1}}} +\def\nwix@defcount#1{\nwix@listcount{\nwix@defs{#1}}} +\def\nwix@id@defs#1{% index pair + {{\Tt \@car#1\@nil}% + \def\\##1{\nwix@defs@space\subpageref{##1}}\nwix@defs{\@cdr#1\@nil}}} + % useful above to change ~ into something that can break +% this option is undocumented because I think breakdefs is always right +\def\nwopt@breakdefs{\def\nwix@defs@space{\penalty200\ }} +\def\nwopt@nobreakdefs{\def\nwix@defs@space{~}} % old code +\nwopt@breakdefs +\def\nwidentuses#1{% list of index pairs + \nwcodecomment{\@nwlangdepuss\ \let\\=\nwix@id@defs\commafy{#1}.}} +\def\nwix@totaluses#1{% list of index pairs + {\count@=0 + \def\\##1{\nwix@usecount{\@cdr##1\@nil}\advance\count@ by\nwix@counter}% + #1\global\nwix@counter\count@ }} +\def\nwix@id@uses#1#2{% {ident}{label} + \nwix@usecount{#2}\ifnum\nwix@counter>0 + {\advance\leftskip by \codemargin + \nwcodecomment{{\Tt #1}, \@nwlangdepusd\ \nwpageprep\ \@pagesl{\nwix@uses{#2}}.}}% + \else + \ifnw@hideunuseddefs\else + {\advance\leftskip by \codemargin \nwcodecomment{{\Tt #1}, \@nwlangdepnvu.}}% + \fi + \fi} +\def\nwidentdefs#1{% list of index pairs + \ifnw@hideunuseddefs\nwix@totaluses{#1}\else\nwix@listcount{#1}\fi + \ifnum\nwix@counter>0 + \nwcodecomment{\@nwlangdepdfs:}% + {\def\\##1{\nwix@id@uses ##1}#1}% + \fi} +\newif\ifnw@hideunuseddefs\nw@hideunuseddefsfalse +\def\nwopt@hideunuseddefs{\nw@hideunuseddefstrue} +\def\nwopt@noidentxref{% + \let\nwidentdefs\@gobble + \let\nwidentuses\@gobble} +\def\nw@underlinedefs{% {list with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} + +\def\nw@indexline#1#2{% + {\indent {\Tt #1}: \nw@underlinedefs\@nameuse{nwixl@#2}\par}} + +\newenvironment{thenowebindex}{\parindent=-10pt \parskip=\z@ + \advance\leftskip by 10pt + \advance\rightskip by 0pt plus1in\par\@afterindenttrue + \def\\##1{\nw@indexline##1}}{} +\def\nowebindex{% + \@ifundefined{nwixs@i}% + {\@warning{The \string\nowebindex\space is empty}}% + {\begin{thenowebindex}\@nameuse{nwixs@i}\end{thenowebindex}}} +\def\nowebindex@external{% + {\let\nwixadds@c=\@gobble + \def\nwixadds@i##1{\nw@indexline##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebindex}\@input{\jobname.nwi}\end{thenowebindex}}} +\def\nwixlogsorted#1#2{% list data + \@bsphack\if@filesw + \toks0={#2}\immediate\write\@auxout{\string\nwixadds{#1}{\the\toks0}} + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\def\nwixadds#1#2{% + \@ifundefined{nwixs@#1}% + {\global\@namedef{nwixs@#1}{\\{#2}}}% + {\expandafter\nwix@cons\csname nwixs@#1\endcsname{\\{#2}}}} +\let\nwixaddsx=\@gobbletwo +\def\nwopt@externalindex{% + \ifx\nwixadds\@gobbletwo % already called + \else + \let\nwixaddsx=\nwixadds \let\nwixadds=\@gobbletwo + \let\nowebindex=\nowebindex@external + \let\nowebchunks=\nowebchunks@external + \fi} +\def\nowebchunks{% + \@ifundefined{nwixs@c}% + {\@warning{The are no \string\nowebchunks}}% + {\begin{thenowebchunks}\@nameuse{nwixs@c}\end{thenowebchunks}}} +\def\nowebchunks@external{% + {\let\nwixadds@i=\@gobble + \def\nwixadds@c##1{\nw@onechunk##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebchunks}\@input{\jobname.nwi}\end{thenowebchunks}}} + \@namedef{r@nw@notdef}{{0}{(\@nwlangdepnvd)}} +\def\nw@chunkunderlinedefs{% {list of labels with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} +\def\nw@onechunk#1#2#3{% {name}{label of first definition}{list with \nwixd, \nwixu} + \@ifundefined{r@#2}{}{% + \indent\LA #1~{\nwtagstyle\subpageref{#2}}\RA + \if@nwlongchunks{~\nw@chunkunderlinedefs#3}\fi\par}} +\newenvironment{thenowebchunks}{\vskip3pt + \parskip=\z@\parindent=-10pt \advance\leftskip by 10pt + \advance\rightskip by 0pt plus10pt \@afterindenttrue + \def\\##1{\nw@onechunk##1}}{} +\newif\if@nwlongchunks +\@nwlongchunksfalse +\let\nwopt@longchunks\@nwlongchunkstrue +\newcommand\@nw@hyper@ref{\hyperreference} % naras +\newcommand\@nw@hyper@anc{\blindhyperanchor} % naras +\newcommand\@nw@hyperref@ref[2]{\hyperlink{noweb.#1}{#2}} % nr +\newcommand\@nw@hyperref@anc[1]{\hypertarget{noweb.#1}{\relax}} % nr +%%\renewcommand\@nw@hyperref@ref[2]{{#2}} % nr +%%\renewcommand\@nw@hyperref@anc[1]{} % nr +\newcommand\nwhyperreference{% + \@ifundefined{hyperlink} + {\@ifundefined{hyperreference} + {\global\let\nwhyperreference\@gobble} + {\global\let\nwhyperreference\@nw@hyper@ref}} + {\global\let\nwhyperreference\@nw@hyperref@ref}% + \nwhyperreference +} + +\newcommand\nwblindhyperanchor{% + \@ifundefined{hyperlink} + {\@ifundefined{hyperreference} + {\global\let\nwblindhyperanchor\@gobble} + {\global\let\nwblindhyperanchor\@nw@hyper@anc}} + {\global\let\nwblindhyperanchor\@nw@hyperref@anc}% + \nwblindhyperanchor +} +\newcommand\nwanchorto{% + \begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@anchorto} +\newcommand\nw@anchorto[1]{\endgroup\def\nw@next{#1}\nw@anchortofin} +\newcommand\nw@anchortofin[1]{#1\footnote{See URL \texttt{\nw@next}.}} +\let\nwanchorname\@gobble +\newif\ifhtml +\htmlfalse +\let\nwixident=\relax +\def\nwopt@english{% + \def\@nwlangdepdef{This definition is continued}% + \def\@nwlangdepcud{This code is used}% + \def\@nwlangdeprtc{Root chunk (not used in this document)}% + \def\@nwlangdepcwf{This code is written to file}% + \def\@nwlangdepchk{chunk}% + \def\@nwlangdepchks{chunks}% + \def\@nwlangdepin{in}% + \def\@nwlangdepand{and}% + \def\@nwlangdepuss{Uses}% + \def\@nwlangdepusd{used}% + \def\@nwlangdepnvu{never used}% + \def\@nwlangdepdfs{Defines}% + \def\@nwlangdepnvd{never defined}% +} +\let\nwopt@american\nwopt@english +\def\nwopt@icelandic{% + \def\@nwlangdepdef{This definition is continued}% + \def\@nwlangdepcud{This code is used}% + \def\@nwlangdeprtc{Root chunk (not used in this document)}% + \def\@nwlangdepcwf{This code is written to file}% + \def\@nwlangdepchk{kóða}% + \def\@nwlangdepchks{kóðum}% + \def\@nwlangdepin{í}% + \def\@nwlangdepand{og}% + \def\@nwlangdepuss{Notar}% + \def\@nwlangdepusd{notað}% + \def\@nwlangdepnvu{hvergi notað}% + \def\@nwlangdepdfs{Skilgreinir}% + \def\@nwlangdepnvd{hvergi skilgreint}% +} +\def\nwopt@portuges{% + \def\@nwlangdepdef{Defini\c{c}\~ao continuada em}% + % This definition is continued + \def\@nwlangdepcud{C\'odigo usado em}% + % This code is used + \def\@nwlangdeprtc{Fragmento de topo (sem uso no documento)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Este c\'odigo foi escrito no ficheiro}% + % This code is written to file + \def\@nwlangdepchk{fragmento}% + % chunk + \def\@nwlangdepchks{fragmentos}% + % chunks + \def\@nwlangdepin{no(s)}% + % in + \def\@nwlangdepand{e}% + % and + \def\@nwlangdepuss{Usa}% + % Uses + \def\@nwlangdepusd{usado}% + % used + \def\@nwlangdepnvu{nunca usado}% + % never used + \def\@nwlangdepdfs{Define}% + % Defines + \def\@nwlangdepnvd{nunca definido}% + % never defined +} +\def\nwopt@frenchb{% + \def\@nwlangdepdef{Suite de la d\'efinition}% + % This definition is continued + \def\@nwlangdepcud{Ce code est employ\'e}% + % This code is used + \def\@nwlangdeprtc{Morceau racine (pas employ\'e dans ce document)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Ce code est \'ecrit dans le fichier}% + % This code is written to file + \def\@nwlangdepchk{le morceau}% + % chunk + \def\@nwlangdepchks{les morceaux}% + % chunks + \def\@nwlangdepin{dans}% + % in + \def\@nwlangdepand{et}% + % and + \def\@nwlangdepuss{Utilise}% + % Uses + \def\@nwlangdepusd{utilis\'{e}}% + % used + \def\@nwlangdepnvu{jamais employ\'{e}}% + % never used + \def\@nwlangdepdfs{D\'{e}finit}% + % Defines + % Cannot use the accent here: \def\@nwlangdepnvd{jamais d\'{e}fini}% + \def\@nwlangdepnvd{jamais defini}% + % never defined +} +\let\nwopt@french\nwopt@frenchb +\def\nwopt@german{% + \def\@nwlangdepdef{Diese Definition wird fortgesetzt}% + % This definition is continued + \def\@nwlangdepcud{Dieser Code wird benutzt}% + % This code is used + \def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Geh\"ort in die Datei}% + % This code is written to file + \def\@nwlangdepchk{Abschnitt}% + % chunk + \def\@nwlangdepchks{den Abschnitten}% + % chunks + \def\@nwlangdepin{in}% + % in + \def\@nwlangdepand{und}% + % and + \def\@nwlangdepuss{Benutzt}% + % Uses + \def\@nwlangdepusd{benutzt}% + % used + \def\@nwlangdepnvu{nicht benutzt}% + % never used + \def\@nwlangdepdfs{Definiert}% + % Defines + \def\@nwlangdepnvd{nicht definiert}% + % never defined +} +\def\nwopt@german{% + \def\@nwlangdepdef{Diese Definition wird fortgesetzt}% + % This definition is continued + \def\@nwlangdepcud{Dieser Code wird benutzt}% + % This code is used + \def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Dieser Code erzeugt die Datei} + % This code generates the file + \def\@nwlangdepchk{Teil}% + % chunk + \def\@nwlangdepchks{Teils}% + % chunks + \def\@nwlangdepin{im}% + % in + \def\@nwlangdepand{und}% + % and + \def\@nwlangdepuss{Benutzt}% + % Uses + \def\@nwlangdepusd{benutzt}% + % used + \def\@nwlangdepnvu{nicht benutzt}% + % never used + \def\@nwlangdepdfs{Definiert}% + % Defines + \def\@nwlangdepnvd{nicht definiert}% + % never defined +} +\let\nwopt@ngerman\nwopt@german +\ifx\languagename\undefined % default is English + \noweboptions{english} +\else + \@ifundefined{nwopt@\languagename} + {\noweboptions{english}} + {\expandafter\noweboptions\expandafter{\languagename}} +\fi diff --git a/web/noweb/src/tex/nwkernel.tex b/web/noweb/src/tex/nwkernel.tex new file mode 100644 index 0000000000..f196628254 --- /dev/null +++ b/web/noweb/src/tex/nwkernel.tex @@ -0,0 +1,83 @@ +\def\LL{<<} +\def\GG{>>} +\def\LLS{[[} +\def\RRS{]]} + +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=0pt + +\chardef\other=12 +\def\setupcode{% + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \obeyspaces\Tt +} +{\catcode`\^^M=\active % make CR an active character + \gdef\newlines{\catcode`\^^M=\active % make CR an active character + \def^^M{\par\startline}}% + \gdef\eatline#1^^M{\relax}% +} +%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write +\def\startline{\noindent\hskip\parindent\ignorespaces} + +{\obeyspaces\global\let =\ } % from texbook, p 381 +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \rm} +\def\LA{\begingroup\setupmodname\it$\langle${}} +\def\RA{\/$\rangle$\endgroup} +\def\code{\leavevmode\begingroup\setupcode\newlines} +\def\edoc{\endgroup} + +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\unhcopy\equivbox} +\def\plusendmoddef{\RA\unhcopy\plusequivbox} + +\def\filename#1{\vfil\eject\mark{#1}} + +\def\chunklist{% +\expandafter\errhelp\expandafter{I changed \noexpand\chunklist\space +to \noexpand\nowebchunks. I'll try to avoid such incompatible changes +in the future.}% +\errmessage{Use \string\nowebchunks\space instead of \chunklist.}} +\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}} +\def\nowebindex{\message{<Warning: You need noweave -idx to use \string\nowebindex>}} + +% here is support for the new-style (capitalized) font-changing commands +% thanks to Dave Love +\ifx\documentstyle\undefined + \let\Rm=\rm \let\Tt=\tt % plain +\else\ifx\selectfont\undefined + \let\Rm=\rm \let\Tt=\tt % LaTeX OFSS +\else % LaTeX NFSS + \def\Rm{\fontfamily\rmdefault \fontseries\default@series + \fontshape\default@shape \selectfont} + \def\Tt{\fontfamily\ttdefault \fontseries\default@series + \fontshape\default@shape \selectfont} +\fi +\fi diff --git a/web/noweb/src/tex/nwmac.tex b/web/noweb/src/tex/nwmac.tex new file mode 100644 index 0000000000..ec54fbce9d --- /dev/null +++ b/web/noweb/src/tex/nwmac.tex @@ -0,0 +1,240 @@ +% nwmac.tex -- plain TeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +{\obeyspaces\global\let =\ } % from texbook, p 381 +\def\nwdocspar{\par\semifilbreak} +\def\nwstartdeflinemarkup{\nobreak\hskip 1.5em plus 1fill\nobreak} +\def\nwenddeflinemarkup{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak} +\catcode`\@=11 +% scale cmbx10 instead of using cmbx12 because {\LaTeX} does, so fonts exist +\font\twlbf=cmbx10 scaled \magstep1 +\font\frtbf=cmbx10 scaled \magstep2 +% These fonts don't work with xdvi! + +\advance\hoffset 0.5 true in +\advance\hsize -1.5 true in +\newdimen\textsize +\textsize=\hsize +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\long\def\ifundefined#1#2#3{% + \expandafter\ifx\csname#1\endcsname\relax + #2% + \else#3% + \fi} + +\ifundefined{myheadline} + {\headline={\hbox to \textsize{\tentt\firstmark\hfil\tenrm\today\hbox + to 4em{\hss\folio}}\hss}} + {\expandafter\headline\expandafter{\myheadline}} + +\ifundefined{myfootline} + {\footline={\hfil}} + {\expandafter\footline\expandafter{\myfootline}} +\def\semifilbreak{\vskip0pt plus1.5in\penalty-200\vskip0pt plus -1.5in} +\raggedbottom +% +% \chapcenter macro to produce nice centered chapter titles +% +\def\chapcenter{\leftskip=0.5 true in plus 4em minus 0.5 true in + \rightskip=\leftskip + \parfillskip=0pt \spaceskip=.3333em \xspaceskip=.5em + \pretolerance=9999 \tolerance=9999 + \hyphenpenalty=9999 \exhyphenpenalty=9999} +% \startsection{LEVEL}{INDENT}{BEFORESKIP}{AFTERSKIP}{STYLE}{HEADING} +% #1 #2 #3 #4 #5 #6 +% +% LEVEL: depth; e.g. part=0 chapter=1 sectino=2... +% INDENT: indentation of heading from left margin +% BEFORESKIP: skip before header +% AFTERSKIP: skip after header +% STYLE: style of heading; e.g.\bf +% HEADING: heading of the sectino +% +\def\startsection#1#2#3#4#5#6{\par\vskip#3 plus 2in + \penalty-200\vskip 0pt plus -2in + \noindent{\leftskip=#2 \rightskip=0.5true in plus 4em minus 0.5 true in + \hyphenpenalty=9999 \exhyphenpenalty=9999 + #5#6\par}\vskip#4% + {\def\code##1{[[}\def\edoc##1{]]}\message{[#6]}} + \settocparms{#1} + \def\themodtitle{#6} +%%%% {\def\code{\string\code}\def\edoc{\string\edoc}% + \edef\next{\noexpand\write\cont{\tocskip + \tocline{\hskip\tocindent\tocstyle\relax\themodtitle} + {\noexpand\the\pageno}}}\next % write to toc + %} +} +\def\settocparms#1{ + \count@=#1 + \ifnum\count@<1 + \def\tocskip{\vskip3ptplus1in\penalty-100 + \vskip0ptplus-1in}% + \def\tocstyle{\bf} + \def\tocindent{0pt} + \else + \def\tocskip{} + \def\tocstyle{\rm} + \dimen@=2em \advance\count@ by \m@ne \dimen@=\count@\dimen@ + \edef\tocindent{\the\dimen@} + \fi +} +\def\tocline#1#2{\line{{\ignorespaces#1}\leaders\hbox to .5em{.\hfil}\hfil + \hbox to1.5em{\hss#2}}} +\def\section#1{\par \vskip3ex\noindent {\bf #1}\par\nobreak\vskip1ex\nobreak} +\def\chapter#1{\vfil\eject\startsection{0}{0pt}{6ex}{3ex}{\frtbf\chapcenter}{#1}} +\def\section#1{\startsection{1}{0pt}{4ex}{2ex}{\twlbf}{#1}} +\def\subsection#1{\startsection{2}{0pt}{2ex}{1ex}{\bf}{#1}} +\def\subsubsection#1{\startsection{3}{0pt}{1ex}{0.5ex}{\it}{#1}} +\def\paragraph#1{\startsection{4}{0pt}{1.5ex}{0ex}{\it}{#1}} + +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=0pt +\newdimen\nwdefspace +\nwdefspace=\codehsize +% need to use \textwidth in {\LaTeX} to handle styles with +% non-standard margins (David Bruce). Don't know why we sometimes +% wanted \hsize. 27 August 1997. +%% \advance\nwdefspace by -\hsize\relax +\ifx\textwidth\undefined + \advance\nwdefspace by -\hsize\relax +\else + \advance\nwdefspace by -\textwidth\relax +\fi +\chardef\other=12 +\def\setupcode{% + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \catcode`\"=\other % fixes problem with german.sty + \obeyspaces\Tt +} +\def\nwendquote{\relax\ifhmode\spacefactor=1000 \fi} +{\catcode`\^^M=\active % make CR an active character + \gdef\newlines{\catcode`\^^M=\active % make CR an active character + \def^^M{\par\startline}}% + \gdef\eatline#1^^M{\relax}% +} +%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write +\def\startline{\noindent\hskip\parindent\ignorespaces} +\def\nwnewline{\ifvmode\else\hfil\break\leavevmode\hbox{}\fi} +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \let\{\lbrace + \let\}\rbrace + % bad news --- don't know what catcode to give " + \Rm} +\def\LA{\begingroup\maybehbox\bgroup\setupmodname\It$\langle$} +\def\RA{\/$\rangle$\egroup\endgroup} +\def\code{\leavevmode\begingroup\setupcode\newlines} +\def\edoc{\endgroup} +\let\maybehbox\relax +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\ifmmode\equiv\else\unhcopy\equivbox\fi + \nobreak\hfill\nobreak} +\def\plusendmoddef{\RA\ifmmode\mathord{+}\mathord{\equiv}\else\unhcopy\plusequivbox\fi + \nobreak\hfill\nobreak} +\def\chunklist{% +\errhelp{I changed \chunklist to \nowebchunks. +I'll try to avoid such incompatible changes in the future.}% +\errmessage{Use \string\nowebchunks\space instead of \string\chunklist}} +\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}} +\def\nowebindex{\message{<Warning: You need noweave -index to use \string\nowebindex>}} +% here is support for the new-style (capitalized) font-changing commands +% thanks to Dave Love +\ifx\documentstyle\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % plain +\else\ifx\selectfont\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % LaTeX OFSS +\else % LaTeX NFSS + \def\Rm{\reset@font\rm} + \def\It{\reset@font\it} + \def\Tt{\reset@font\tt} + \def\Bf{\reset@font\bf} +\fi\fi +\ifx\reset@font\undefined \let\reset@font=\relax \fi +\def\nwbackslash{\char92} +\def\nwlbrace{\char123} +\def\nwrbrace{\char125} + +\def\nwfilename#1{\vfil\eject\mark{#1}} + +\def\nwbegindocs#1{\filbreak} +\def\nwenddocs{\par} +\def\nwbegincode#1{\par\nobreak + \begingroup\setupcode\newlines\parindent=0pt\parskip=0pt + %\let\oendmoddef=\endmoddef \let\oplusendmoddef=\plusendmoddef + %\def\endmoddef{\oendmoddef\par}\def\plusendmoddef{\oplusendmoddef\par}% + \let\onwenddeflinemarkup=\nwenddeflinemarkup + \def\nwenddeflinemarkup{\onwenddeflinemarkup\par}% + \hsize=\codehsize\noindent\bchack} +\def\nwendcode{\endgroup} +{\catcode`\^^M=\active % make CR an active character + \gdef\bchack#1^^M{\relax#1}% +} +\edef\contentsfile{\jobname.toc } % file that gets table of contents info +\def\readcontents{\expandafter\input \contentsfile} + +\newwrite\cont +\openout\cont=\contentsfile +\write\cont{\string\catcode`\string\@=11}% a hack to make contents + % take stuff in plain.tex +\def\bye{% + \write\cont{}% ensure that the contents file isn't empty + \closeout\cont + \vfil\eject\pageno=-1 % new page causes contents to be really closed + \topofcontents\readcontents\botofcontents + \vfil\eject\end} +\def\topofcontents{\vfil\mark{{\bf Contents}}} +\def\botofcontents{} +\let\em=\it +% used to produce an itemized (bulleted) list in plain {\TeX} +% such lists can be nested +% mostly useful with WEB + +% Usage: +% \itemize +% \item First thing +% \item second thing +% \enditemize + +\newcount\listlevel +\listlevel=0 +\newdimen\itemwidth +\itemwidth=3em + +\def\itemize{\begingroup\advance\listlevel by1 + \def\item{\par\noindent + \raise2pt\llap{$\scriptstyle\bullet$\ }\ignorespaces}% + \def\nameditem##1{\par\noindent + \llap{\rlap{##1}\hskip\itemwidth}\ignorespaces}% + \par\advance\leftskip by\itemwidth\advance\rightskip by0.5\itemwidth} +\def\enditemize{\par\endgroup\noindent\ignorespaces} + +\let\begindocument=\relax +\catcode`\@=12 diff --git a/web/noweb/src/tex/support.nw b/web/noweb/src/tex/support.nw new file mode 100644 index 0000000000..8db76dbaff --- /dev/null +++ b/web/noweb/src/tex/support.nw @@ -0,0 +1,1993 @@ +% -*- mode: Noweb; noweb-code-mode: tex-mode -*- +\documentclass[twoside]{article} +\usepackage[hypertex]{hyperref} +\usepackage{url} +\usepackage{noweb} +\pagestyle{noweb} +\noweboptions{longchunks,smallcode} +\title{{\TeX} and {\LaTeX} support for {\tt noweb}} +\author{Norman Ramsey} +\newcommand{\stylehook}{\marginpar{\raggedright\sl Style hook}} +\begin{document} +\maketitle +\tableofcontents +@ +This document describes the {\TeX} code that supports {\tt noweave} +and {\tt noweb}. Those interested in customizing their output should +focus on Section~\ref{section:sty}. +Hooks you can easily use (apart from those provided by +[[\noweboptions]]) are indicated by marginal notes. +This file contains both plain {\TeX} and {\LaTeX} support: +<<nwmac.tex>>= +% nwmac.tex -- plain TeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +<<noweb.sty>>= +% noweb.sty -- LaTeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +@ + +\section{Basic {\TeX} support for {\tt noweb}} +This basic code is used for both {\TeX} and {\LaTeX}. +The first step is to define [[\codehsize]], which is the width in +which code is set, and [[\codemargin]], which is the amount by which +it is indented.\stylehook +<<kernel>>= +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=0pt +@ +[[\defspace]] is the space we would like on the right of navigational info +that appears on definition lines, so that it lines up with the text above +and below. +<<kernel>>= +\newdimen\nwdefspace +\nwdefspace=\codehsize +% need to use \textwidth in {\LaTeX} to handle styles with +% non-standard margins (David Bruce). Don't know why we sometimes +% wanted \hsize. 27 August 1997. +%% \advance\nwdefspace by -\hsize\relax +\ifx\textwidth\undefined + \advance\nwdefspace by -\hsize\relax +\else + \advance\nwdefspace by -\textwidth\relax +\fi +@ +Most code is set in an environment in which [[\setupcode]] has been +executed. +In this environment, only [[\]], [[{]], and [[}]] have their usual +categories; every other character represents itself. +Appropriate [[\chardef]]s ensure that the special characters can be +escaped with a backslash. +<<kernel>>= +\chardef\other=12 +\def\setupcode{% + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \catcode`\"=\other % fixes problem with german.sty + \obeyspaces\Tt +} +@ +[[\nwendquote]] is called after quoted code. +It resets the spacefactor +<<kernel>>= +\def\nwendquote{\relax\ifhmode\spacefactor=1000 \fi} +@ +[[\eatline]] is used to consume newlines that should be ignored, +for example, the newlines at the end of [[@ %def ]]{\em identifiers} lines. +I can't remember what [[\startline]] or [[\newlines]] are for; I don't +think {\tt noweave} ever emits them. +<<kernel>>= +{\catcode`\^^M=\active % make CR an active character + \gdef\newlines{\catcode`\^^M=\active % make CR an active character + \def^^M{\par\startline}}% + \gdef\eatline#1^^M{\relax}% +} +%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write +\def\startline{\noindent\hskip\parindent\ignorespaces} +\def\nwnewline{\ifvmode\else\hfil\break\leavevmode\hbox{}\fi} +@ + +Within a code environment, it may be necessary to restore the +category codes in order to set a module (chunk) name. +This hack doesn't properly restore [["]] for use in {\tt german.sty}. +<<kernel>>= +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \let\{\lbrace + \let\}\rbrace + % bad news --- don't know what catcode to give " + \Rm} +@ \sublabel{ref:fred} +@ +Setting up the space code has to be done differently for {\TeX} and +{\LaTeX}, so as not to screw up {\LaTeX}'s \texttt{verbatim} package. +(Fix from Rafael Laboissiere.) +<<nwmac.tex>>= +{\obeyspaces\global\let =\ } % from texbook, p 381 +<<noweb.sty>>= +{\obeyspaces\AtBeginDocument{\global\let =\ }} % from texbook, p 381 +@ + +{\tt noweave} brackets uses of chunk names with [[\LA]] and [[\RA]], which +handle the angle brackets, font, and environment. + +As it stands, chunk names can be broken across lines (or pages). This +could result in unnecessary page breaks in code +(c.f.~p.~\pageref{sec:pagebreaking}). [[\let\\maybehbox=\mbox]] to +\stylehook +avoid breaking them +(or to make them work in math mode); this is done in code chunks, but could be done +in general. +<<kernel>>= +\def\LA{\begingroup\maybehbox\bgroup\setupmodname\It$\langle$} +\def\RA{\/$\rangle$\egroup\endgroup} +\def\code{\leavevmode\begingroup\setupcode\newlines} +\def\edoc{\endgroup} +\let\maybehbox\relax +@ + +[[\equivbox]] and [[\plusequivbox]] are used to set the +``\unhcopy\equivbox'' +and ``\unhcopy\plusequivbox'' that open a chunk definition or its +continuation. +{\tt noweave} brackets definitions of chunk names with [[\moddef]] and +either [[\endmoddef]] or [[\plusendmoddef]]. +<<kernel>>= +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\ifmmode\equiv\else\unhcopy\equivbox\fi + \nobreak\hfill\nobreak} +\def\plusendmoddef{\RA\ifmmode\mathord{+}\mathord{\equiv}\else\unhcopy\plusequivbox\fi + \nobreak\hfill\nobreak} +@ +Within a code environment, margin tags might be used to mark sub-page +numbers in the margins, separated by [[\nwmarginglue]].\stylehook +The interaction with [[\moddef]] involves tricky kerning. +The tag itself is displayed using [[\nwthemargintag]] +<<noweb.sty>>= +\def\nwopt@nomargintag{\let\nwmargintag=\@gobble} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\llap{##1\kern\nwmarginglue\kern\codemargin}}} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\kern-\codemargin\nwthemargintag{##1}\kern\codemargin}} +\def\nwthemargintag#1{\llap{#1\kern\nwmarginglue}} +\nwopt@margintag +\newdimen\nwmarginglue +\nwmarginglue=0.3in +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B margintag +Put the sub-page number (tag) of each code-chunk definition in the +left margin. (Default) +.TP +.B nomargintag +Don't use margin tags. +@ \fi +[[\nwtagstyle]] determines the style in which tags are displayed.\stylehook +<<noweb.sty>>= +\def\nwtagstyle{\footnotesize\Rm} +@ +<<kernel>>= +\def\chunklist{% +\errhelp{I changed \chunklist to \nowebchunks. +I'll try to avoid such incompatible changes in the future.}% +\errmessage{Use \string\nowebchunks\space instead of \string\chunklist}} +\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}} +\def\nowebindex{\message{<Warning: You need noweave -index to use \string\nowebindex>}} +@ We have to be careful with font-changing in the presence of +different font-selection schemes. In the \LaTeX{} New Font Selection +Scheme something like [[\it\tt]] will attempt to use an italic +typewriter font. Thus we define new commands like [[\Tt]] which will +work with both the Plain and old and new \LaTeX{} schemes. (Note that +NFSS will be standard in the next version of \LaTeX.) A problem with +these definitions arises with NFSS: in math mode the won't work +unless the {\tt oldlfont} backwards-compatibility option is in effect. +For the moment, you can get round this by using [[\mbox]]. + +If you wanted code set in a different font, you could re-define +[[\Tt]].\stylehook{} [\LaTeX2e actually behaves like OFSS, but the +extra [[\reset@font]] does no harm.] +<<kernel>>= +% here is support for the new-style (capitalized) font-changing commands +% thanks to Dave Love +\ifx\documentstyle\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % plain +\else\ifx\selectfont\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % LaTeX OFSS +\else % LaTeX NFSS + \def\Rm{\reset@font\rm} + \def\It{\reset@font\it} + \def\Tt{\reset@font\tt} + \def\Bf{\reset@font\bf} +\fi\fi +\ifx\reset@font\undefined \let\reset@font=\relax \fi +@ +\clearpage +\section{The {\tt noweb} document-style option for {\LaTeX}} +\label{section:sty} +{\LaTeX} support begins with the kernel shown above. +<<noweb.sty>>= +<<kernel>> +@ +\subsection{Support for noweb options} +<<noweb.sty>>= +\def\noweboptions#1{% + \def\@nwoptionlist{#1}% + \@for\@nwoption:=\@nwoptionlist\do{% + \@ifundefined{nwopt@\@nwoption}{% + \@latexerr{There is no such noweb option as '\@nwoption'}\@eha}{% + \csname nwopt@\@nwoption\endcsname}}} +@ +\subsection{Adjusting placement of code on the page} +{\LaTeX} requires a larger [[\codehsize]] because +code is indented by [[\codemargin]].\stylehook +<<noweb.sty>>= +\codemargin=10pt +\advance\codehsize by \codemargin % make room for indentation of code +\advance\nwdefspace by \codemargin % and fix adjustment for def/use +\def\setcodemargin#1{% + \advance\codehsize by -\codemargin % make room for indentation of code + \advance\nwdefspace by -\codemargin % and fix adjustment for def/use + \codemargin=#1 + \advance\codehsize by \codemargin % make room for indentation of code + \advance\nwdefspace by \codemargin % and fix adjustment for + % def/use +} +@ +[[\noweboptions{shift}]] is used to shift the whole page left to make room for +the wide code lines. +It may be emitted by {\tt noweave -shift}, or it might be given by a user. +<<noweb.sty>>= +\def\nwopt@shift{% + \dimen@=-0.8in + \if@twoside % Values for two-sided printing: + \advance\evensidemargin by \dimen@ + \else % Values for one-sided printing: + \advance\evensidemargin by \dimen@ + \advance\oddsidemargin by \dimen@ + \fi +% \advance \marginparwidth -\dimen@ +} +\let\nwopt@noshift\@empty +@ + +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B shift +Shift text to the left so that long code lines won't extend +off the right-hand side of the page. +@ \fi + +\subsection{Page-breaking strategy}\label{sec:pagebreaking} + +We want to insert penalties aiming for: +\begin{enumerate} +\item +No page breaks in the middle of a code chunk unless necessary to avoid +an overfull vbox; +\item +Documentation immediately preceding a code chunk should appear on +the same page as that code chunk unless doing so would violate rule 1. +\end{enumerate} +[[\filbreak]] is useful for this sort of thing (see {\em The \TeX + book\/}) and is used to encourage breaks at the right places between +chunks. Appropriate penalties are inserted elsewhere, between code +lines in particular. + +\subsection{Environments for setting code} + +[[\nwbegincode]] and [[\nwendcode]] are used by {\tt noweave} to bracket +code chunks. +The [[webcode]] environment is intended for users who want to paste +{\tt noweave} output into papers. + +The definition of [[\nwbegincode]] is based on the verbatim +implementation in {\tt verbatim.sty}, which will, presumably be in the +next version of \LaTeX\@. One thing it does differently, apart from +the catcode changes is setting [[\linewidth]]; this will avoid some +overfull hboxen when the code lines are too long, but the lines won't +be broken anyhow (even within chunk names because of the +[[\maybehbox]] definition). +<<noweb.sty>>= +\def\nwbegincode#1{% + \begingroup + <<[[\nwbegincode]] separation and penalties>> + \@begincode } +\def\nwendcode{\endtrivlist \endgroup \filbreak} % keeps code on 1 page + +\newenvironment{webcode}{% + \@begincode +}{% + \endtrivlist} +@ This is just common code between [[\nwbegincode]] and [[webcode]]. +<<noweb.sty>>= +\def\@begincode{% + <<[[\trivlist]] clich\'e (\`a la {\Tt verbatim})>> + \linewidth\codehsize + <<[[\obeylines]] setup>> + <<zap ligatures, fix spaces>> + \nowebsize \setupcode + \let\maybehbox\mbox } +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B smallcode +Set code in +.I LaTeX +.B "\\\\small" +font instead of +.B "\\\\normalsize." +Similar options exist for all the +.I LaTeX +size-changing commands. +@ \fi +@ [[\nowebsize]] governs the size at which code is set; users who want +to minimize code can [[\let\nowebsize=\small]]. +Slitex users should try +\begin{quote} +[[\def\nowebsize{\normalsize\baselineskip=20pt \parskip=5pt }]] +\end{quote} +to avoid code lines that are too far apart. +[[\nwcodetopsep]] is +the glue placed before code chunks.\stylehook +<<noweb.sty>>= + \newskip\nwcodetopsep \nwcodetopsep = 3pt plus 1.2pt minus 1pt + \let\nowebsize=\normalsize + \def\nwopt@tinycode{\let\nowebsize=\tiny} + \def\nwopt@footnotesizecode{\let\nowebsize=\footnotesize} + \def\nwopt@scriptsizecode{\let\nowebsize=\scriptsize} + \def\nwopt@smallcode{\let\nowebsize=\small} + \def\nwopt@normalsizecode{\let\nowebsize=\normalsize} + \def\nwopt@largecode{\let\nowebsize=\large} + \def\nwopt@Largecode{\let\nowebsize=\Large} + \def\nwopt@LARGEcode{\let\nowebsize=\LARGE} + \def\nwopt@hugecode{\let\nowebsize=\huge} + \def\nwopt@Hugecode{\let\nowebsize=\Huge} +@ Maybe the penalties ought to be parameters\dots +<<[[\nwbegincode]] separation and penalties>>= + \topsep \nwcodetopsep + \@beginparpenalty \@highpenalty + \@endparpenalty -\@highpenalty +@ +The [[\trivlist]] clich\'e isn't quite a clich\'e because we adjust +[[\leftskip]] for indentation by [[\codemargin]] and adjust +[[\rightskip]] to allow lines up to [[\codehsize]] long without +overfull boxen +($\mbox{[[\codehsize]]}=\mbox{[[\hsize]]}+\mbox{[[\rightskip]]}$). +Note that [[\hsize]] isn't altered. +<<[[\trivlist]] clich\'e (\`a la {\Tt verbatim})>>= + \trivlist \item[]% + \leftskip\@totalleftmargin \advance\leftskip\codemargin + \rightskip\hsize \advance\rightskip -\codehsize + \parskip\z@ \parindent\z@ \parfillskip\@flushglue +@ The penalty inserted between verbatim lines would normally be +[[\interlinepenalty]], but we want to prohibit breaks there. +@ +Note the bug lurking somewhere in this code, as reported by Steven Ooms: +\begin{quote} +I have some lay-out +problems in the documentation chunks. When using the (La)TeX commands +[[\hline]] or [[\vtop]] the right margin is always extended far beyond the page +margin after the first code chunk has been typeset. I'm still looking for +the exact cause of it, but to me it seems that LaTeX supposes for those +commands that the line width for the documentation chunk is as large as that +for code chunks, which isn't true in reality. +\end{quote} +@ +<<[[\obeylines]] setup>>= + \@@par + \def\par{\leavevmode\null \@@par \penalty\nwcodepenalty}% + \obeylines +@ [[\nwcodepenalty]] is the penalty for breaking between lines in a +code chunk. If you set it to 10000, code will never be broken across +pages.\stylehook{} I guess this should be settable in [[\noweboptions]]. +<<noweb.sty>>= +\newcount\nwcodepenalty \nwcodepenalty=\@highpenalty +@ +The cursing chunk accounts for the addition of a mess of characters +to those reset by [[\@noligs]] in \LaTeX2e. +<<zap ligatures, fix spaces>>= + \@noligs <<make all those damn active characters ``other''>> + \setupcode \frenchspacing \@vobeyspaces +@ +We can't make [[`]] ``other,'' because then we'll get ligatures. +(Why Don put these ligatures in the [[\tt]] font I wish I knew.) +But we'll step on all the others. +<<make all those damn active characters ``other''>>= + \ifx\verbatim@nolig@list\undefined\else + \let\do=\nw@makeother \verbatim@nolig@list \do@noligs\` + \fi +<<noweb.sty>>= +\def\nw@makeother#1{\catcode`#1=12 } +@ +{\tt noweave} uses [[\nwbegindocs{nnn}]] and [[\nwenddocs]] to bracket +documentation chunks. +If a documentation chunk does not continue the current paragraph, +{\tt noweave} inserts [[\nwdocspar]], which uses +[[\filbreak]] in an attempt to keep the documentation chunk on the +same page as the code chunk that follows it. (The code chunk will +have another [[\filbreak]] after it---see [[\nwbegincode]].) +[[\nwbegindocs]] doesn't start a +new paragraph if the previous chunk didn't end one, i.e.\ didn't enter +vmode; if it does start a new one, it's only indented by the use of +[[\nwdocspar]]. +<<noweb.sty>>= +\def\nwbegindocs#1{\ifvmode\noindent\fi} +\let\nwenddocs=\relax +\let\nwdocspar=\filbreak +@ +Some people don't like it that noweb leaves so much white space. +The secret, undocument style option [[breakcode]] will break up code +in order to use less white space. +The parameter [[\nwbreakcodespace]] controls how much white space to +leave. +<<noweb.sty>>= +\def\@nwsemifilbreak#1{\vskip0pt plus#1\penalty-200\vskip0pt plus -#1} +\newdimen\nwbreakcodespace +\nwbreakcodespace=0.2in % by default, leave no more than this on a page +\def\nwopt@breakcode{% + \def\nwdocspar{\@nwsemifilbreak{0.2in}}% + \def\nwendcode{\endtrivlist\endgroup} % ditches filbreak +} +@ +The page-breaking strategy implies ragged bottom pages, so we should +turn it on in general (this is relevant for the {\tt report} style): +<<noweb.sty>>= +\raggedbottom +<<nwmac.tex>>= +\def\nwdocspar{\par\semifilbreak} +@ + +{\tt noweave} doesn't bracket quoted code with [[\code]] and [[\edoc]] any more. +It probably should do something nifty, just to make {\TeX} hackers happy, but it doesn't. +<<noweb.sty>>= +\def\code{\leavevmode\begingroup\setupcode\@vobeyspaces\obeylines} +\let\edoc=\endgroup +@ +\subsection{The {\tt noweb} page style} +Headers contain file name, date, and page number. +{\tt noweave} emits [[\nwfilename{]]{\em name}[[}]] for each new file. +In the {\tt noweb} page style, new files cause page breaks; +otherwise they are ignored. +<<noweb.sty>>= +\newdimen\@original@textwidth +\def\ps@noweb{% + \@original@textwidth=\textwidth + \let\@mkboth\@gobbletwo + \def\@oddfoot{}\def\@evenfoot{}% No feet. + \if@twoside % If two-sided printing. + \def\@evenhead{\hbox to \@original@textwidth{% + \Rm \thepage\qquad{\Tt\leftmark}\hfil\today}}% Left heading. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \else % If one-sided printing. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \let\@evenhead\@oddhead + \fi + \let\chaptermark\@gobble + \let\sectionmark\@gobble + \let\subsectionmark\@gobble + \let\subsubsectionmark\@gobble + \let\paragraphmark\@gobble + \let\subparagraphmark\@gobble + \def\nwfilename{\begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@filename} + \def\nw@filename##1{\endgroup\markboth{##1}{##1}\let\nw@filename=\nw@laterfilename}% +} +\def\nw@laterfilename#1{\endgroup\clearpage \markboth{#1}{#1}} +\let\nwfilename=\@gobble +@ +\subsection{Chunk cross-reference} +[[\nwalsodefined]], [[\nwused]], and [[\nwnotused]] are emitted by the {\tt +noweb} cross-referencers. (What arguments?) +If unused chunks are output chunks, a filter can slip in +[[\let\nwnotused\nwoutput]]. +The style uses [[\nwcodecomment]] for all annotations that follow code +chunks. +Fiddling with it can change the appearance of the output. +Note that [[\nwcodecomment]] is used after [[\nwbegincode]], with +[[\obeylines]] in efect. Since linebreaking can occur here, we need +to change the [[\interlinepenalty]]. +A little vertical space ([[\nwcodecommentsep]]\stylehook) appears before the first +comment. + +We firkled with [[\rightskip]] in [[\nwbegincode]] above; now we want to +reset it so that paragraphs are the normal width ([[\textwidth]], +possibly less [[\codemargin]]) and set ragged right. This is done as +usuall by making [[\rightskip]] naturally zero but stretchable. +<<noweb.sty>>= +\def\nwcodecomment#1{\@@par\penalty\nwcodepenalty + <<add [[\nwcodecommentsep]] if this is the first [[\nwcodecomment]]>>% + \hspace{-\codemargin}{% + \rightskip=0pt plus1in + \interlinepenalty\nwcodepenalty + \let\\\relax\footnotesize\Rm #1\@@par\penalty\nwcodepenalty}} +@ +This stuff is used at the end of a chunk. +<<noweb.sty>>= +\def\@nwalsodefined#1{\nwcodecomment{\@nwlangdepdef\ \nwpageprep\ \@pagesl{#1}.}} +\def\@nwused#1{\nwcodecomment{\@nwlangdepcud\ \nwpageprep\ \@pagesl{#1}.}} +\def\@nwnotused#1{\nwcodecomment{\@nwlangdeprtc.}} +\def\nwoutput#1{\nwcodecomment{\@nwlangdepcwf\ {\Tt \@stripstar#1*\stripped}.}} +\def\@stripstar#1*#2\stripped{#1} +<<add [[\nwcodecommentsep]] if this is the first [[\nwcodecomment]]>>= +\if@firstnwcodecomment + \vskip\nwcodecommentsep\penalty\nwcodepenalty\@firstnwcodecommentfalse +\fi +@ +This stuff on the definition line. +Note the hooks\stylehook{} for pointer styles. +<<noweb.sty>>= +\newcommand{\nwprevdefptr}[1]{% + \mbox{$\mathord{\triangleleft}\,\mathord{\mbox{\subpageref{#1}}}$}} +\newcommand{\nwnextdefptr}[1]{% + \mbox{$\mathord{\mbox{\subpageref{#1}}}\,\mathord{\triangleright}$}} + +\newcommand{\@nwprevnextdefs}[2]{% + {\nwtagstyle + \ifx\relax#1\else ~~\nwprevdefptr{#1}\fi + \ifx\relax#2\else ~~\nwnextdefptr{#2}\fi}} +\newcommand{\@nwusesondefline}[1]{{\nwtagstyle~~(\@pagenumsl{#1})}} +\newcommand{\@nwstartdeflinemarkup}{\nobreak\hskip 1.5em plus 1fill\nobreak} +\newcommand{\@nwenddeflinemarkup}{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak} +<<nwmac.tex>>= +\def\nwstartdeflinemarkup{\nobreak\hskip 1.5em plus 1fill\nobreak} +\def\nwenddeflinemarkup{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak} +@ +And here are the options we use to choose one, the other, or neither. +<<noweb.sty>>= +\def\nwopt@longxref{% + \let\nwalsodefined\@nwalsodefined + \let\nwused\@nwused + \let\nwnotused\@nwnotused + \let\nwprevnextdefs\@gobbletwo + \let\nwusesondefline\@gobble + \let\nwstartdeflinemarkup\relax + \let\nwenddeflinemarkup\relax +} +\def\nwopt@shortxref{% + \let\nwalsodefined\@gobble + \let\nwused\@gobble + \let\nwnotused\@gobble + \let\nwprevnextdefs\@nwprevnextdefs + \let\nwusesondefline\@nwusesondefline + \let\nwstartdeflinemarkup\@nwstartdeflinemarkup + \let\nwenddeflinemarkup\@nwenddeflinemarkup +} +\def\nwopt@noxref{% + \let\nwalsodefined\@gobble + \let\nwused\@gobble + \let\nwnotused\@gobble + \let\nwprevnextdefs\@gobbletwo + \let\nwusesondefline\@gobble + \let\nwstartdeflinemarkup\relax + \let\nwenddeflinemarkup\relax +} +\nwopt@shortxref % to hell with backward compatibility! +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B longxref, shortxref, noxref +Choose a style for chunk cross-reference. +Long style uses small paragraphs after each chunk, as in Knuth. +Short style uses symbols on the definition line, as in Hanson. +.B noxref +provides no chunk cross-reference. +Defaults to +.B shortxref. +@ \fi +<<noweb.sty>>= +\newskip\nwcodecommentsep \nwcodecommentsep=3pt plus 1pt minus 1pt +\newif\if@firstnwcodecomment\@firstnwcodecommenttrue +@ +\subsection{Page ranges} +The goal is to combine sub-page numbers in a way that makes sense. +Multiple sub-pages of one page become that page, and individual +pages are combined into ranges. +(A range may be only one page.) +<<noweb.sty>>= +\newcount\@nwlopage\newcount\@nwhipage % range lo..hi-1 +\newcount\@nwlosub % subpage of lo +\newcount\@nwhisub % subpage of hi +\def\@nwfirstpage#1#2#3{% subpage page xref-tag + \@nwlopage=#2 \@nwlosub=#1 + \def\@nwloxreftag{#3}% + \advance\@nwpagecount by \@ne + <<$\mbox{[[\@nwhipage]]} := \mbox{[[\@nwlopage]]}+1$>>} +\def\@nwnextpage#1#2#3{% subpage page xref-tag + \ifnum\@nwhipage=#2 + \advance\@nwhipage by \@ne + \advance\@nwpagecount by \@ne + \@nwhisub=#1 + \def\@nwhixreftag{#3}\else + \ifnum#2<\@nwlopage <<new range starting with [[#2]]>>\else + \ifnum#2>\@nwhipage <<new range starting with [[#2]]>>\else + \@nwlosub=0 \@nwhisub=0 + \fi\fi\fi + } +<<new range starting with [[#2]]>>= +<<add range to range list>>\@nwfirstpage{#1}{#2}{#3} +<<$\mbox{[[\@nwhipage]]} := \mbox{[[\@nwlopage]]}+1$>>= +\@nwhipage=\@nwlopage\advance\@nwhipage by \@ne +<<add range to range list>>= +<<set [[\@tempa]] to page range(s), marked with [[\\]]>>% +\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa +<<set [[\@tempa]] to page range(s), marked with [[\\]]>>= +\advance\@nwhipage by \m@ne +\ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}}% +\else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + {\@nwloxreftag}}% + \noexpand\noexpand\noexpand\\% + {{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}} + {\@nwhixreftag}}}% + \else <<use rules from Chicago style manual>>% + \fi +\fi +<<use rules from Chicago style manual>>= +\ifnum\@nwlopage<110 <<normal range>>\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage <<normal range>>\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + <<normal range>>% + \else <<normal range>>% + \fi + \fi +\fi +<<normal range>>= +\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}} +<<noweb.sty>>= +\newcount\@nwpagetemp +@ +The sequence [[\@pagesl]] makes a range of pages from a list of labels. +[[\subpages]] works from a list of [[{{subpage}{page}}]]. +<<noweb.sty>>= +\newcount\@nwpagecount +\def\@nwfirstpagel#1{% label + \@ifundefined{r@#1}{<<warn of undefined reference to [[#1]] and add page ??>>}{% + \edef\@tempa{\noexpand\@nwfirstpage\subpagepair{#1}{#1}}\@tempa}} +\def\@nwnextpagel#1{% label + \@ifundefined{r@#1}{<<warn of undefined reference to [[#1]] and add page ??>>}{% + \edef\@tempa{\noexpand\@nwnextpage\subpagepair{#1}{#1}}\@tempa}} +<<noweb.sty>>= +\def\@pagesl#1{% list of labels + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + <<add range to range list>>\def\\##1{\@nwhyperpagenum##1}% + \ifnum\@nwpagecount=1 \nwpageword \else \nwpagesword\fi~\commafy{\nw@pages}} +\def\@nwhyperpagenum#1#2{\nwhyperreference{#2}{#1}} + +\def\@pagenumsl#1{% list of labels -- doesn't include word `pages', commas, or `and' + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + <<add range to range list>>% + \def\\##1{\@nwhyperpagenum##1\let\\=\@nwpagenumslrest}\nw@pages} +\def\@nwpagenumslrest#1{~\@nwhyperpagenum#1} +<<noweb.sty>>= +\def\subpages#1{% list of {{subpage}{page}} + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\edef\@tempa{\noexpand\@nwfirstpage##1{}}\@tempa + \def\\####1{\edef\@tempa{\noexpand\@nwnextpage####1}\@tempa}}#1% + <<add range to range list>>\def\\##1{\@firstoftwo##1}% + \ifnum\@nwpagecount=1 \nwpageword \else \nwpagesword\fi~\commafy{\nw@pages}} +\def\@nwaddrange{<<add range to range list>>} +@ +[[\nwpageword]], [[\nwpagesword]], and [[\nwpageprep]] let you change +the wording of the cross-reference information. +<<noweb.sty>>= +\def\nwpageword{\@nwlangdepchk} % chunk, was page +\def\nwpagesword{\@nwlangdepchks} % chunk, was page +\def\nwpageprep{\@nwlangdepin} % in, was on +<<warn of undefined reference to [[#1]] and add page ??>>= +<<warn of undefined reference to [[#1]]>>% +\nwix@cons\nw@pages{\\{\bf ??}} +@ +\subsection{Sub-page references} + +This is the wonderful code that Dave Love provided to make page +references like 7a, 7b, and so on. + +This code +provides a mechanism for defining `page +sub-references' using [[\sublabel{foo}]] referenced with +[[\subpageref{foo}]]. Sub-references will be numbered like these real +examples: \subpageref{ref:foo}, \subpageref{ref:bar}, +\subpageref{ref:baz}\sublabel{ref:foo}\sublabel{ref:bar}\sublabel{ref:baz} +etc.\ unless there is only one on the page, in which case the letter +will be dropped like this: \subpageref{ref:fred}. + +To be able to use [[\subpageref]] we must define the label with +[[\sublabel]], used like label. (Using +[[\ref]] with a label defined by [[\sublabel]] will +produce the sub-reference number, by the way, and [[\pageref]] +works as expected.) Note that +[[\subpageref]] is robust and [[\ref]] and [[\pageref]] are redefined to be +robust also, as they will be in future \LaTeX{} releases. +Incidentally, these expand to the relevant text plus [[\null]]---you +might want to strip this off, e.g.\ for sorting lists. + + +There are various ways we could attack this task (which is made +non-trivial by the well-known asynchrony of (La)\TeX's output +routine), but +they all must depend on hacks in the [[.aux]] file or a similar one. +Joachim Schrod's [[fnpag.sty]] does the same sort of thing differently +to this \LaTeX-specific approach. See [[latex.tex]] for enlightenment +on the cross-referencing mechanism and the \LaTeX{} internals used +below. [DL: The internals change in \LaTeX2e compared with +\LaTeX~2.09. The code here still works, though.] +@ +The new-style {\LaTeX} page-reference macros all work the same way: +if the thing is undefined, barf. Otherwise, do the specified thing. +We need to handle the fact that the expansion of the label may be two +items or five items, depending on whether hypertext is used. +Since we're only ever interested in the first two items, we use a +hack---the ``do the specified thing'' must be defined as +\mbox{[[\def\dome#1#2#3\\{...}]]} where [[...]] uses only the first two parameters. +<<noweb.sty>>= +\newcommand\nw@genericref[2]{% what to do, name of ref + \expandafter\nw@g@nericref\csname r@#2\endcsname#1{#2}} +\newcommand\nw@g@nericref[3]{% control sequence, what to do, name + \ifx#1\relax + \ref{#3}% trigger the standard `undefined ref' mechanisms + \else + \expandafter#2#1.\\% + \fi} +@ Much of what we want can be done by pulling out the first, second, +or first and second elements of a ref. +<<noweb.sty>>= +\def\nw@selectone#1#2#3\\{#1} +\def\nw@selecttwo#1#2#3\\{#2} +\def\nw@selectonetwo#1#2#3\\{{#1}{#2}} +@ +The [[\subpageref]] macro first does a normal [[\pageref]]. If the +reference is actually defined, it then goes on to check whether the +control sequence [[2on]]\LA{}{page referenced}\RA{} is defined and sets the +[[\ref]] value to get [[a]] etc.\ if so. The magic, of course, is in +defining the [[2on]] bit appropriately. +[[\subpageref]] also tries to include the right hyperstuff for xhdvi. +<<noweb.sty>>= +\newcommand{\subpageref}[1]{% + \nwhyperreference{#1}{\nw@genericref\@subpageref{#1}}} +\def\@subpageref#1#2#3\\{% + \@ifundefined{2on#2}{#2}{\nwthepagenum{#1}{#2}}} +@ +[[\subpagepair]] produces a [[{subpage}{page}]] pair. +<<noweb.sty>>= +\newcommand{\subpagepair}[1]{% % produces {subpage}{page} + \@ifundefined{r@#1}% + {{0}{0}}% + {\nw@genericref\@subpagepair{#1}}} +\def\@subpagepair#1#2#3\\{% + \@ifundefined{2on#2}{{0}{#2}}{{#1}{#2}}} +@ +[[\sublabel]] is like the [[\label]] command, except that it writes +[[\newsublabel]] onto the [[.aux]] file rather than [[\newlabel]]. +For hyperreferencing, all labels must be hypertext +anchors, for which we use [[\nwblindhyperanchor]]. +<<noweb.sty>>= +\newcommand{\sublabel}[1]{% + \leavevmode % needed to make \@bsphack work + \@bsphack + \nwblindhyperanchor{#1}% + \if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ +[[\nosublabel]] creates a label with a sub-page part of~0. +<<noweb.sty>>= +\newcommand{\nosublabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newlabel{#1}{{0}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ +[[\newsublabel]] is the macro that does the important work. It is called with the +same sort of arguments as [[\newlabel]]: the first argument is the +label name and the second is [[{<<ref value>>}{<<page number>>}]]. +(Note that the only definition here which needs to be +global is the one which is, and that [[\global]] is redefined by +[[\enddocument]], which will bite you if you use it\dots) +<<noweb.sty>>= +<<definition of [[\newsublabel]]>> +@ +Before we create a [[\newsublabel]] for the first time, we set the +proper trailers. +<<definition of [[\newsublabel]]>>= +\newcommand\newsublabel{% + \nw@settrailers + \global\let\newsublabel\@newsublabel + \@newsublabel} +@ First we extract the page number into [[\this@page]]. +<<definition of [[\newsublabel]]>>= +\newcommand{\@newsublabel}[2]{% + \edef\this@page{\@cdr#2\@nil}% +@ +Then we see whether it's changed from the value of [[\last@page]] +which was stashed away by the last [[\newsublabel]] (or is [[\relax]] if +this is the first one). If the page has changed, we reset the +counter [[\sub@page]] telling us how many sub-labels there have been +on the page. +<<definition of [[\newsublabel]]>>= + \ifx\this@page\last@page\else + \sub@page=\z@ + \fi + \edef\last@page{\this@page} + \advance\sub@page by \@ne +@ +If we've had at least two on the page, we define the +[[2on]]\LA{}{page no.}\RA{} macro to indicate the fact. +<<definition of [[\newsublabel]]>>= + \ifnum\sub@page=\tw@ + \global\@namedef{2on\this@page}{}% + \fi +@ +\nextchunklabel{cl1}\nextchunklabel{cl2} +Then we write a normal [[\newlabel]] with the sub-reference as the +normal reference value in the second argument. +Unfortunately, if we want hypertext support, the second argument of +[[\newlabel]] gets complicated. +It is either +\begin{itemize} +\item +[[{<<ref value>>}{<<page number>>}]], when normal {\LaTeX} is +running, or +\item +[[{<<ref value>>}{<<page number>>}{<<text>>}}{<<hyper category>>}{<<URL>>}]], +when the \texttt{hyperref} package is running. +(We actually detect this by looking for the \texttt{nameref} package, +because that's the one that changes the use of labels.) +\end{itemize} +We unify these two things by producing +[[{<<ref value>>}{<<page number>>}\nw@labeltrailers]] + +We may have pending labels in support of [[\nextchunklabel]], as defined in +chunk~\subpageref{chunklabel}. +Because we want to define all of the ``pending sublabels'' in exactly +the same way, we do something a bit odd---we make the current label a +pending label as well. +<<definition of [[\newsublabel]]>>= + \pendingsublabel{#1}% + \edef\@tempa##1{\noexpand\newlabel{##1}% + {{\number\sub@page}{\this@page}\nw@labeltrailers}}% + \pending@sublabels + \def\pending@sublabels{}} +@ +We can't use [[\@ifpackageloaded]] to see if \texttt{nameref} is +loaded, because that is restricted to the preamble, and +[[\newsublabel]] goes into the [[.aux]] file, which is executed after +the whole document is processed. +We therefore test for [[\@secondoffive]]. +This is lame, but what else can we do? +<<noweb.sty>>= +\newcommand\nw@settrailers{% -- won't work on first run + \@ifpackageloaded{nameref}% + {\gdef\nw@labeltrailers{{}{}{}}}% + {\gdef\nw@labeltrailers{}}} +\renewcommand\nw@settrailers{% + \@ifundefined{@secondoffive}% + {\gdef\nw@labeltrailers{}}% + {\gdef\nw@labeltrailers{{}{}{}}}} +@ +Now we keep track of those pending guys.\nextchunklabel{chunklabel} +The goal here is to save them up until they're all equivalent to the +label on the next chunk. +We have to control expansion so chunks like \subpageref{cl1} +(\subpageref{cl2}) can be labelled twice. +<<noweb.sty>>= +\newcommand{\nextchunklabel}[1]{% + \nwblindhyperanchor{#1}% % looks slightly bogus --- nr + \@bsphack\if@filesw {\let\thepage\relax + \edef\@tempa{\write\@auxout{\string\pendingsublabel{#1}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\newcommand\pendingsublabel[1]{% + \def\@tempa{\noexpand\@tempa}% + \edef\pending@sublabels{\noexpand\@tempa{#1}\pending@sublabels}} +\def\pending@sublabels{} +<<man page: noweb style control sequences>>= +.PP \" .TP will not work with the backslashes on the next line. Period. +\fB\\nextchunklabel{l}\fP +.RS +Associates label \fBl\fP +with the sub-page reference of the next code chunk. +Can be used in for concise chunk cross-reference with, e.g., +\fBchunk~\\subpageref{l}\fP. +.RE +@ +We need to define these. +<<noweb.sty>>= +\def\last@page{\relax} +\newcount\sub@page +@ +We no longer use Rainer's new expandable definitions of [[\ref]] and +[[\pageref]] to minimise the risk of nasty surprises; enough time has +elapsed that this should no longer be necessary. +<<old noweb.sty>>= +% RmS 92/08/14: made \ref and \pageref robust +\def\ref#1{\@ifundefined{r@#1}{{\bf ??}<<warn of undefined reference to [[#1]]>>}% + {\expandafter\expandafter\expandafter + \@car\csname r@#1\endcsname\@nil\null}} +\def\pageref#1{\@ifundefined{r@#1}{{\bf ??}<<warn of undefined reference to [[#1]]>>}% + {\expandafter\expandafter\expandafter + \@cdr\csname r@#1\endcsname\@nil\null}} +\def\@refpair#1{\@ifundefined{r@#1}{{0}{0}<<warn of undefined reference to [[#1]]>>}% + {\@nameuse{r@#1}}} +<<warn of undefined reference to [[#1]]>>= +\@warning{Reference `#1' on page \thepage \space undefined} +@ + +Here a a couple of hooks for formatting sub-page numbers, +which can be alphabetic, numeric, or omitted.\stylehook +<<noweb.sty>>= +\def\@alphasubpagenum#1#2{#2\ifnum#1=0 \else\@alph{#1}\fi} +\def\@nosubpagenum#1#2{#2} +\def\@numsubpagenum#1#2{#2\ifnum#1=0 \else.\@arabic{#1}\fi} +\def\nwopt@nosubpage{\let\nwthepagenum=\@nosubpagenum\nwopt@nomargintag} +\def\nwopt@numsubpage{\let\nwthepagenum=\@numsubpagenum} +\def\nwopt@alphasubpage{\let\nwthepagenum=\@alphasubpagenum} +\nwopt@alphasubpage +@ +In rare cases, there may be more than 26 chunks on a page. +In such a case, we need a sub-page numbering scheme that can go beyond +``a to z.'' +The scheme I have chosen is ``a to z, then aa to zz, then aaa to zzz, +etc.'' +The conversion requires a bit of thought because it is \emph{not} an +ordinary conversion of integer to string as we usually think of such +things. +The problem is that the meaning of the letters depends on the +position; the letter~a acts like a zero in some positions or a one in +others. + +The solution I have implemented uses a variable [[bound]] which is +always equal to $26^k$ for some~$k$. +If we write the recurrence $B_k = B_{k-1} + 26^k$, with $B_0 = 0$, we +then use a string of~$k$ letters to represent numbers between +$B_{k-1}$~and~$B_k$. +Within that string, a's are 0's, and so on up to z's which are 25's, +and we use standard integer-conversion methods to encode $n-B_{k-1}$. + +The following Icon implementation may be more perspicuous than the +{\TeX} code actually used. +Here the variable [[bound]] is $26^k$, with $k=1$ initially, and +[[n]]~is $n-B_{k-1}$. +The first loop finds the right~$k$, and the second does the usual +string conversion. +<<Icon code for subpage numbering>>= +procedure alphastring(n) + bound := 26 + + while n >= bound do { + # invariant: bound = 26^(k+1) & n is initial n - B_k + n -:= bound + bound *:= 26 + } + + while bound > 1 do { + bound /:= 26 + d := integer(n / bound) + n -:= d * bound + writes(&lcase[d+1]) + } +end +@ +Here's {\TeX} code to achieve the same end. +The entire macro body is enclosed in braces, so that it can be used +with [[\loop]] without picking up the wrong [[\repeat]]. +<<noweb.sty>>= +\newcount\@nwalph@n +\let\@nwalph@d\@tempcnta +\let\@nwalph@bound\@tempcntb +\def\@nwlongalph#1{{% + \@nwalph@n=#1\advance\@nwalph@n by-1 + \@nwalph@bound=26 + \loop\ifnum\@nwalph@n<\@nwalph@bound\else + \advance\@nwalph@n by -\@nwalph@bound + \multiply\@nwalph@bound by 26 + \repeat + \loop\ifnum\@nwalph@bound>1 + \divide\@nwalph@bound by 26 + \@nwalph@d=\@nwalph@n\divide\@nwalph@d by \@nwalph@bound + % d := d * bound ; n -:= d; d := d / bound --- saves a temporary + \multiply\@nwalph@d by \@nwalph@bound + \advance\@nwalph@n by -\@nwalph@d + \divide\@nwalph@d by \@nwalph@bound + \advance\@nwalph@d by 1 \@alph{\@nwalph@d}% + \repeat +}} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B alphasubpage, numsubpage, nosubpage +Number chunks by the number of the page on which they appear, +followed by an alphabetic (numeric, not used) ``sub-page'' indicator. +Defaults to +.B alphasubpage. +.B nosubpage +implies +.B nomargintag. +@ \fi +@ + +\subsection{{\tt WEB}-like chunk numbering} +Here's a righteous hack: we get the effect of WEB-like chunk numbers +just by redefining [[\sublabel]] to use a counter instead of the current page number. +Since the numbers are all distinct, no sub-page number is ever used. +<<noweb.sty>>= +\newcount\nw@chunkcount +\nw@chunkcount=\@ne +\newcommand{\weblabel}[1]{% + \@bsphack + \nwblindhyperanchor{#1}% + \if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\number\nw@chunkcount}}}}% + \expandafter}\@tempa + \global\advance\nw@chunkcount by \@ne + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\def\nwopt@webnumbering{% + \let\sublabel=\weblabel + \def\nwpageword{chunk}\def\nwpagesword{chunks}% + \def\nwpageprep{in}} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B webnumbering +Number chunks consecutively, in +.I WEB +style, instead of using sub-page numbers. +@ \fi +@ +\subsection{Indexing (identifier cross-reference) support} + +\subsubsection{Tracking definitions and uses} +All index definitions and uses are associated with +a label defined with [[\sublabel]] or [[\nosublabel]]. +Either the label is the [[\sublabel]] of the code chunk in which the definition or use +appears, or it is a [[\nosublabel]] appearing in the middle of a +documentation chunk. +<<noweb.sty>>= +% \nwindexdefn{printable name}{identifying label}{label of chunk} +% \nwindexuse{printable name}{identifying label}{label of chunk} + +\def\nwindexdefn#1#2#3{\@auxix{\protect\nwixd}{#2}{#3}} +\def\nwindexuse#1#2#3{\@auxix{\protect\nwixu}{#2}{#3}} + +\def\@auxix#1#2#3{% {marker}{id label}{subpage label} + \@bsphack\if@filesw {\let\nwixd\relax\let\nwixu\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string\nwixadd{#1}{#2}{#3}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +<<noweb.sty>>= +% \nwixadd{marker}{idlabel}{subpage label} +\def\nwixadd#1#2#3{% + \@ifundefined{nwixl@#2}% + {\global\@namedef{nwixl@#2}{#1{#3}}}% + {\expandafter\nwix@cons\csname nwixl@#2\endcsname{#1{#3}}}} +@ +\subsubsection{Subscripted identifiers} +We use either explicit subscripts or hyperlinks to point identifiers +to their definitions. +<<noweb.sty>>= +\def\@nwsubscriptident#1#2{\mbox{$\mbox{#1}_{\mathrm{\subpageref{#2}}}$}} +\def\@nwnosubscriptident#1#2{#1} +\def\@nwhyperident#1#2{\leavevmode\nwhyperreference{#2}{#1}} +@ +We can use subscripts, hyperlinks, or nothing on all identifiers. +<<noweb.sty>>= +\def\nwopt@subscriptidents{% + \let\nwlinkedidentq\@nwsubscriptident + \let\nwlinkedidentc\@nwsubscriptident +} +\def\nwopt@nosubscriptidents{% + \let\nwlinkedidentq\@nwnosubscriptident + \let\nwlinkedidentc\@nwnosubscriptident +} +\def\nwopt@hyperidents{% + \let\nwlinkedidentq\@nwhyperident + \let\nwlinkedidentc\@nwhyperident +} +\def\nwopt@nohyperidents{% + \let\nwlinkedidentq\@nwnosubscriptident + \let\nwlinkedidentc\@nwnosubscriptident +} +@ +We can change only identifiers appearing in quoted code. +<<noweb.sty>>= +\def\nwopt@subscriptquotedidents{% + \let\nwlinkedidentq\@nwsubscriptident +} +\def\nwopt@nosubscriptquotedidents{% + \let\nwlinkedidentq\@nwnosubscriptident +} +\def\nwopt@hyperquotedidents{% + \let\nwlinkedidentq\@nwhyperident +} +\def\nwopt@nohyperquotedidents{% + \let\nwlinkedidentq\@nwnosubscriptident +} +@ +The default is to hyperlink everything. +<<noweb.sty>>= +\nwopt@hyperidents +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B subscriptidents, nosubscriptidents, hyperidents, nohyperidents +Controls subscripting of identifiers in code, including quoted code. +Selecting +.B subscriptidents +means an identifier appearing in a code chunk (or in quoted code +within a documentation +chunk) will be subscripted with the chunk number of its definition. +.B hyperidents +means such identifiers will be hyperlinked to their definitions, +provided of course that a hypertext package like +.B hyperref +is loaded. +.B nosubscriptidents +and +.B nohyperidents +are equivalent, and they turn off such markings. +The default is +.B hyperidents. +.TP +.B subscriptquotedidents, nosubscriptquotedidents, hyperquotedidents, nohyperquotedidents +Controls linking of identifiers as above, but applies only to uses of +identifiers +in quoted code. +@ \fi +@ +\subsubsection{Writing lists with commas and ``and''} +You get one of +\begin{itemize} +\item ``$a$'' +\item ``$a$ and $b$'' +\item ``$a$, $\ldots$, $b$, and $c$'' +\end{itemize} +Plus [[\\]] is applied to each element of the list. +<<noweb.sty>>= +\newcount\@commacount +\def\commafy#1{% + {\nwix@listcount{#1}\@commacount=\nwix@counter + \let\@comma@each=\\% + \ifcase\@commacount\let\\=\@comma@each\or\let\\=\@comma@each\or + \def\\{\def\\{ \@nwlangdepand\ \@comma@each}\@comma@each}\else + \def\\{\def\\{, % + \advance\@commacount by \m@ne + \ifnum\@commacount=1 \@nwlangdepand~\fi\@comma@each}\@comma@each}\fi + #1}} +@ + +\subsubsection{New, improved index code} +There are two kinds of lists. +One kind is a generic list in which elements are preceded by [[\\]]. +If the elements are index elements, they are {\em[[{]]printable +identifier[[}{]]label[[}]]} pairs. +The other kind is a list of sub-page labels, in which each +element is preceded by either [[\nwixd]] or [[\nwixu]]. +<<noweb.sty>>= +\def\nwix@cons#1#2{% {list}{\marker{element}} + {\toks0=\expandafter{#1}\def\@tempa{#2}\toks2=\expandafter{\@tempa}% + \xdef#1{\the\toks0 \the\toks2 }}} +@ +The reference list for an identifier labelled {\em id} +is always called [[\nwixl@]]{\em id}. +Most applications will work with reference lists by applying [[\\]] +either to the defs or to the uses. +<<noweb.sty>>= +\def\nwix@uses#1{% {label} + \def\nwixu{\\}\let\nwixd\@gobble\@nameuse{nwixl@#1}} +\def\nwix@defs#1{% {label} + \def\nwixd{\\}\let\nwixu\@gobble\@nameuse{nwixl@#1}} +@ + +Some applications count uses to see whether there is any need to +display information. +<<noweb.sty>>= +\newcount\nwix@counter +\def\nwix@listcount#1{% {list with \\} + {\count@=0 + \def\\##1{\advance\count@ by \@ne }% + #1\global\nwix@counter=\count@ }} +\def\nwix@usecount#1{\nwix@listcount{\nwix@uses{#1}}} +\def\nwix@defcount#1{\nwix@listcount{\nwix@defs{#1}}} +@ +\subsubsection{Supporting a mini-index at the end of each chunk} +When displaying identifiers used, show the identifier and its +definitions. +<<noweb.sty>>= +\def\nwix@id@defs#1{% index pair + {{\Tt \@car#1\@nil}% + \def\\##1{\nwix@defs@space\subpageref{##1}}\nwix@defs{\@cdr#1\@nil}}} + % useful above to change ~ into something that can break +% this option is undocumented because I think breakdefs is always right +\def\nwopt@breakdefs{\def\nwix@defs@space{\penalty200\ }} +\def\nwopt@nobreakdefs{\def\nwix@defs@space{~}} % old code +\nwopt@breakdefs +\def\nwidentuses#1{% list of index pairs + \nwcodecomment{\@nwlangdepuss\ \let\\=\nwix@id@defs\commafy{#1}.}} +@ +\iffalse +<<undocumented -- man page: [[\noweboptions]]>>= +.TP +.B breakdefs, nobreakdefs +.BR breakdefs , +which is the default, +permits long lists of definitions to be broken in identifier cross-reference. +Useful if identifier cross-reference produces lots of overfull hboxes. +.B nobreakdefs +is the old behavior, which should never be needed. +@ \fi +@ +The definitions section is a bit more complex, because it is omitted +if none of the identifiers defined is ever used. +<<noweb.sty>>= +\def\nwix@totaluses#1{% list of index pairs + {\count@=0 + \def\\##1{\nwix@usecount{\@cdr##1\@nil}\advance\count@ by\nwix@counter}% + #1\global\nwix@counter\count@ }} +\def\nwix@id@uses#1#2{% {ident}{label} + \nwix@usecount{#2}\ifnum\nwix@counter>0 + {\advance\leftskip by \codemargin + \nwcodecomment{{\Tt #1}, \@nwlangdepusd\ \nwpageprep\ \@pagesl{\nwix@uses{#2}}.}}% + \else + \ifnw@hideunuseddefs\else + {\advance\leftskip by \codemargin \nwcodecomment{{\Tt #1}, \@nwlangdepnvu.}}% + \fi + \fi} +\def\nwidentdefs#1{% list of index pairs + \ifnw@hideunuseddefs\nwix@totaluses{#1}\else\nwix@listcount{#1}\fi + \ifnum\nwix@counter>0 + \nwcodecomment{\@nwlangdepdfs:}% + {\def\\##1{\nwix@id@uses ##1}#1}% + \fi} +<<noweb.sty>>= +\newif\ifnw@hideunuseddefs\nw@hideunuseddefsfalse +\def\nwopt@hideunuseddefs{\nw@hideunuseddefstrue} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B hideunuseddefs +Omit defined but unused identifiers from +the local identifier cross-reference (Preston Briggs). +@ \fi +<<noweb.sty>>= +\def\nwopt@noidentxref{% + \let\nwidentdefs\@gobble + \let\nwidentuses\@gobble} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B noidentxref +Omit the local identifier cross-reference which follows each code chunk. +@ \fi + +\subsubsection{Support for chunk and identifier indices} +The index in the back shows absolutely all the pages. +<<noweb.sty>>= +\def\nw@underlinedefs{% {list with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} + +\def\nw@indexline#1#2{% + {\indent {\Tt #1}: \nw@underlinedefs\@nameuse{nwixl@#2}\par}} + +\newenvironment{thenowebindex}{\parindent=-10pt \parskip=\z@ + \advance\leftskip by 10pt + \advance\rightskip by 0pt plus1in\par\@afterindenttrue + \def\\##1{\nw@indexline##1}}{} +@ +The information comes from the list [[nwisx@i]]. +<<noweb.sty>>= +\def\nowebindex{% + \@ifundefined{nwixs@i}% + {\@warning{The \string\nowebindex\space is empty}}% + {\begin{thenowebindex}\@nameuse{nwixs@i}\end{thenowebindex}}} +@ +Here's a more efficient version for the external case: +<<noweb.sty>>= +\def\nowebindex@external{% + {\let\nwixadds@c=\@gobble + \def\nwixadds@i##1{\nw@indexline##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebindex}\@input{\jobname.nwi}\end{thenowebindex}}} +@ +That list ([[nwisx@i]]) is created by calls to [[\nwixlogsorted{i}]]. +<<noweb.sty>>= +\def\nwixlogsorted#1#2{% list data + \@bsphack\if@filesw + \toks0={#2}\immediate\write\@auxout{\string\nwixadds{#1}{\the\toks0}} + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ +[[nwixs@c]] and [[nwixs@i]] are sorted lists of chunks and identifiers, respectively. +<<noweb.sty>>= +\def\nwixadds#1#2{% + \@ifundefined{nwixs@#1}% + {\global\@namedef{nwixs@#1}{\\{#2}}}% + {\expandafter\nwix@cons\csname nwixs@#1\endcsname{\\{#2}}}} +\let\nwixaddsx=\@gobbletwo +@ +If an external index is used, we need a [[.nwi]] file, +[[\nwixadds]] is to be ignored, and we use [[\nwixaddsx]]. +<<noweb.sty>>= +\def\nwopt@externalindex{% + \ifx\nwixadds\@gobbletwo % already called + \else + \let\nwixaddsx=\nwixadds \let\nwixadds=\@gobbletwo + \let\nowebindex=\nowebindex@external + \let\nowebchunks=\nowebchunks@external + \fi} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B externalindex +Use an index generated with +.I noindex(1) +(q.v.). +@ \fi +@ +<<noweb.sty>>= +\def\nowebchunks{% + \@ifundefined{nwixs@c}% + {\@warning{The are no \string\nowebchunks}}% + {\begin{thenowebchunks}\@nameuse{nwixs@c}\end{thenowebchunks}}} +\def\nowebchunks@external{% + {\let\nwixadds@i=\@gobble + \def\nwixadds@c##1{\nw@onechunk##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebchunks}\@input{\jobname.nwi}\end{thenowebchunks}}} + \@namedef{r@nw@notdef}{{0}{(\@nwlangdepnvd)}} +<<noweb.sty>>= +\def\nw@chunkunderlinedefs{% {list of labels with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} +<<noweb.sty>>= +\def\nw@onechunk#1#2#3{% {name}{label of first definition}{list with \nwixd, \nwixu} + \@ifundefined{r@#2}{}{% + \indent\LA #1~{\nwtagstyle\subpageref{#2}}\RA + \if@nwlongchunks{~\nw@chunkunderlinedefs#3}\fi\par}} +<<noweb.sty>>= +\newenvironment{thenowebchunks}{\vskip3pt + \parskip=\z@\parindent=-10pt \advance\leftskip by 10pt + \advance\rightskip by 0pt plus10pt \@afterindenttrue + \def\\##1{\nw@onechunk##1}}{} +<<noweb.sty>>= +\newif\if@nwlongchunks +\@nwlongchunksfalse +\let\nwopt@longchunks\@nwlongchunkstrue +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B longchunks +When expanding +.B "\\\\nowebchunks," +show page numbers of definitions and uses of each chunk. +@ \fi + +@ +\subsection{Support for hypertext} +There are two sets of support for hypertext. + Balasubramanian Narasimhan wrote initial support for \texttt{hyper.sty}. +<<noweb.sty>>= +\newcommand\@nw@hyper@ref{\hyperreference} % naras +\newcommand\@nw@hyper@anc{\blindhyperanchor} % naras +@ Norman Ramsey wrote support for the \texttt{hyperrref} package (May + 1998). +<<noweb.sty>>= +\newcommand\@nw@hyperref@ref[2]{\hyperlink{noweb.#1}{#2}} % nr +\newcommand\@nw@hyperref@anc[1]{\hypertarget{noweb.#1}{\relax}} % nr +%%\renewcommand\@nw@hyperref@ref[2]{{#2}} % nr +%%\renewcommand\@nw@hyperref@anc[1]{} % nr +@ We define the independent macros [[\nwhyperreference]] and + [[\nwblindhyperanchor]], +which test for the presence of one of these two packages, +redefine themselve accordingly, and re-invoke themselves. +<<noweb.sty>>= +\newcommand\nwhyperreference{% + \@ifundefined{hyperlink} + {\@ifundefined{hyperreference} + {\global\let\nwhyperreference\@gobble} + {\global\let\nwhyperreference\@nw@hyper@ref}} + {\global\let\nwhyperreference\@nw@hyperref@ref}% + \nwhyperreference +} + +\newcommand\nwblindhyperanchor{% + \@ifundefined{hyperlink} + {\@ifundefined{hyperreference} + {\global\let\nwblindhyperanchor\@gobble} + {\global\let\nwblindhyperanchor\@nw@hyper@anc}} + {\global\let\nwblindhyperanchor\@nw@hyperref@anc}% + \nwblindhyperanchor +} +@ +\subsection{Support for hypertext translation to HTML} +<<noweb.sty>>= +\newcommand\nwanchorto{% + \begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@anchorto} +\newcommand\nw@anchorto[1]{\endgroup\def\nw@next{#1}\nw@anchortofin} +\newcommand\nw@anchortofin[1]{#1\footnote{See URL \texttt{\nw@next}.}} +\let\nwanchorname\@gobble +<<man page: noweb style control sequences>>= +.PP +.B "\\\\nwanchorto{URL}{anchor text}" +.RS +Creates a link to the given URL with the given anchor text. +Implemented in +.I latex(1) +using footnotes, but +.I sl2h(1) +translates this to +.B "<a href=URL>anchor text</a>" +.RE +.PP +.B "\\\\nwanchorname{name}{anchor text}" +.RS +Creates an anchor point for a hyperlink. +Implemented in +.I latex(1) +using +.B "\\\\label", +but +.I sl2h(1) +translates this to +.B "<a name=name>anchor text</a>" +.RE +@ +This lets us hide stuff intended for use only when converting to HTML: +<<noweb.sty>>= +\newif\ifhtml +\htmlfalse +<<man page: noweb style control sequences>>= +.PP +.B "\\\\ifhtml ... \\\\fi" +.RS +Text between +.B "\\\\ifhtml" +and +.B "\\\\fi" +is ignored by +.I latex(1), +but +.I sl2h(1) +and the +.I l2h +noweb filter translate the text into HTML. +.RE +@ +\subsection{Support for Prettyprinting} +The following macro can be redefined to allow custom typesetting of +identifiers in the index and mini-indices. +<<noweb.sty>>= +\let\nwixident=\relax +@ +@ +The following macros can be redefined to typeset `[[\]]', `[[{]]' and +`[[}]]' correctly in non-typewriter fonts. +The problem is that the built-in {\LaTeX} [[\{]] tries to produce a +math symbol, which doesn't exist in the typewriter font, so we get a +brace in the wrong font and a warning. Most unpleasant. +Noweave therefore attempts to emit [[\nwlbrace]] and [[\nwrbrace]] +wherever it believes braces should appear. +The standard noweb style is to set code in typewriter font, and so the +standard definitions just select the proper characters from that font. + People setting code in +fonts other than typewriter are responsible for redefining those +macros to work in their environment. +<<kernel>>= +\def\nwbackslash{\char92} +\def\nwlbrace{\char123} +\def\nwrbrace{\char125} +@ +\subsection{Language-dependent macros} + +Miguel Filgueiras +(DCC-FCUP \& LIACC, Universidade do Porto) provided some changes to +add multilingual support for the words Noweb uses in indexing and +cross-reference. +He inserted macros that are defined by, e.g., [[\noweboptions{english}]]. +The Noweb package uses the +(apparently standard) {\LaTeX} macro [[\languagename]] to select a +language at load time. +If the \texttt{babel} package is +loaded (with the appropriate language name) before Noweb is loaded, +the Noweb package will select language appropriately, provided the +language is one of those Noweb supports. +Mr.~Filgueiras provided support for English, +Portuguese, German, and French. +He notes that the French is +faulty; the translations may be poor, and there are +bugs in the implementation that he could not solve. + +\label{subsection:langdeps} + +The language-dependent macros are defined here in each supported +language in a different subsubsection. + +The choice of language depends on testing the [[\languagename]] +macro. There must be a more elegant way of coding the tests below\ldots +<<noweb.sty>>= +<<language support>> +\ifx\languagename\undefined % default is English + \noweboptions{english} +\else + \@ifundefined{nwopt@\languagename} + {\noweboptions{english}} + {\expandafter\noweboptions\expandafter{\languagename}} +\fi +@ +\subsubsection{Support for English} + +This describes the original English text. +<<language support>>= +\def\nwopt@english{% + \def\@nwlangdepdef{This definition is continued}% + \def\@nwlangdepcud{This code is used}% + \def\@nwlangdeprtc{Root chunk (not used in this document)}% + \def\@nwlangdepcwf{This code is written to file}% + \def\@nwlangdepchk{chunk}% + \def\@nwlangdepchks{chunks}% + \def\@nwlangdepin{in}% + \def\@nwlangdepand{and}% + \def\@nwlangdepuss{Uses}% + \def\@nwlangdepusd{used}% + \def\@nwlangdepnvu{never used}% + \def\@nwlangdepdfs{Defines}% + \def\@nwlangdepnvd{never defined}% +} +\let\nwopt@american\nwopt@english +@ + +@ +\subsubsection{Partial support for Icelandic} + +Partial translation of noweb into Icelandic courtesy of +Johann ``Myrkraverk'' Oskarsson" \verb+<johann@myrkraverk.com>+. +<<language support>>= +\def\nwopt@icelandic{% + \def\@nwlangdepdef{This definition is continued}% + \def\@nwlangdepcud{This code is used}% + \def\@nwlangdeprtc{Root chunk (not used in this document)}% + \def\@nwlangdepcwf{This code is written to file}% + \def\@nwlangdepchk{kóða}% + \def\@nwlangdepchks{kóðum}% + \def\@nwlangdepin{í}% + \def\@nwlangdepand{og}% + \def\@nwlangdepuss{Notar}% + \def\@nwlangdepusd{notað}% + \def\@nwlangdepnvu{hvergi notað}% + \def\@nwlangdepdfs{Skilgreinir}% + \def\@nwlangdepnvd{hvergi skilgreint}% +} +@ +\subsubsection{Support for Portuguese} + +This contains the text in Portuguese. +<<language support>>= +\def\nwopt@portuges{% + \def\@nwlangdepdef{Defini\c{c}\~ao continuada em}% + % This definition is continued + \def\@nwlangdepcud{C\'odigo usado em}% + % This code is used + \def\@nwlangdeprtc{Fragmento de topo (sem uso no documento)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Este c\'odigo foi escrito no ficheiro}% + % This code is written to file + \def\@nwlangdepchk{fragmento}% + % chunk + \def\@nwlangdepchks{fragmentos}% + % chunks + \def\@nwlangdepin{no(s)}% + % in + \def\@nwlangdepand{e}% + % and + \def\@nwlangdepuss{Usa}% + % Uses + \def\@nwlangdepusd{usado}% + % used + \def\@nwlangdepnvu{nunca usado}% + % never used + \def\@nwlangdepdfs{Define}% + % Defines + \def\@nwlangdepnvd{nunca definido}% + % never defined +} +@ +\subsubsection{Support for French} + +This is a tentative translation to French. +NR has made some corrections, and it has been reviewed by Fr\'ed\'eric +Lin\'e, but some errors may remain. + +There are problems with using accents: on the [[\@nwlangdepnvd]] macro +(which apparently is not used in the context of [[\nwcodecomment]]), +and in some other macros (\LaTeX{} complains about missing +[[\endcsname]]). This should be fixed by someone with experience in +using \TeX\ldots +<<language support>>= +\def\nwopt@frenchb{% + \def\@nwlangdepdef{Suite de la d\'efinition}% + % This definition is continued + \def\@nwlangdepcud{Ce code est employ\'e}% + % This code is used + \def\@nwlangdeprtc{Morceau racine (pas employ\'e dans ce document)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Ce code est \'ecrit dans le fichier}% + % This code is written to file + \def\@nwlangdepchk{le morceau}% + % chunk + \def\@nwlangdepchks{les morceaux}% + % chunks + \def\@nwlangdepin{dans}% + % in + \def\@nwlangdepand{et}% + % and + \def\@nwlangdepuss{Utilise}% + % Uses + \def\@nwlangdepusd{utilis\'{e}}% + % used + \def\@nwlangdepnvu{jamais employ\'{e}}% + % never used + \def\@nwlangdepdfs{D\'{e}finit}% + % Defines + % Cannot use the accent here: \def\@nwlangdepnvd{jamais d\'{e}fini}% + \def\@nwlangdepnvd{jamais defini}% + % never defined +} +\let\nwopt@french\nwopt@frenchb +@ +\subsubsection{Support for German} + +This is a translation to German by Sabine Broda (DCC-FCUP \& LIACC, +Universidade do Porto) with revisions by Christian Lindig and further +revisions by Pascal Schmitt. +<<language support>>= +\def\nwopt@german{% + \def\@nwlangdepdef{Diese Definition wird fortgesetzt}% + % This definition is continued + \def\@nwlangdepcud{Dieser Code wird benutzt}% + % This code is used + \def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Geh\"ort in die Datei}% + % This code is written to file + \def\@nwlangdepchk{Abschnitt}% + % chunk + \def\@nwlangdepchks{den Abschnitten}% + % chunks + \def\@nwlangdepin{in}% + % in + \def\@nwlangdepand{und}% + % and + \def\@nwlangdepuss{Benutzt}% + % Uses + \def\@nwlangdepusd{benutzt}% + % used + \def\@nwlangdepnvu{nicht benutzt}% + % never used + \def\@nwlangdepdfs{Definiert}% + % Defines + \def\@nwlangdepnvd{nicht definiert}% + % never defined +} +@ +Here is a revised version, because the version above did not satisy +(explanations below). +<<language support>>= +\def\nwopt@german{% + \def\@nwlangdepdef{Diese Definition wird fortgesetzt}% + % This definition is continued + \def\@nwlangdepcud{Dieser Code wird benutzt}% + % This code is used + \def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}% + % Root chunk (not used in this document) + \def\@nwlangdepcwf{Dieser Code erzeugt die Datei} + % This code generates the file + \def\@nwlangdepchk{Teil}% + % chunk + \def\@nwlangdepchks{Teils}% + % chunks + \def\@nwlangdepin{im}% + % in + \def\@nwlangdepand{und}% + % and + \def\@nwlangdepuss{Benutzt}% + % Uses + \def\@nwlangdepusd{benutzt}% + % used + \def\@nwlangdepnvu{nicht benutzt}% + % never used + \def\@nwlangdepdfs{Definiert}% + % Defines + \def\@nwlangdepnvd{nicht definiert}% + % never defined +} +\let\nwopt@ngerman\nwopt@german +@ Explanations: +\begin{quote} +Changed the [[\@nwlangdepcwf]] yet again. +Johannes Wiedersich \url{<johannes@physik.blm.tu-muenchen.de>} writes: +\begin{quote} +The German latex files contain +\begin{quote} +Dieser Code schreibt man zum File +\end{quote} +this is not correct (or understandable) German and not a correct +translation of the English ``This code is written to file.'' +A better translation would be +\begin{quote} +Dieser Code geht in Datei +\end{quote} +I would suggest to use either +\begin{itemize} +\item Dieser Code schreibt den File +\item Dieser Code erzeugt die Datei +\end{itemize} +The first is closer to a literal translation of the English text; the +second option is closer to the \emph{meaning} of the English text and to +what +actually happens. (This code generates the file) +\end{quote} +The old explanation was: +\begin{quote} +``Diese Code geht in Datei (x)'' isn't perfect either; the best +translation would be ``Dieser Code wird in Datei (x) geschrieben'' but +this would require to put the file name into the middle of the sentence. +\begin{verbatim} + \def\@nwlangdepcwf{Dieser Code geht in Datei}% +\end{verbatim} +The [[\@nwlangdepchks]] macro is problematic because it has to work +together with [[\@nwlangdepin]]. It would be best to emit: +\begin{verbatim} + benutzt in den Teilen 1b und 2. + benutzt im Teil 1. +\end{verbatim} +This would require to switch between ``in'' and ``im''. Since ``im'' is a +compund of ``in dem'', I suggest to use an article for the singular, too, +and to emit: +\begin{verbatim} + benutzt in den Teilen 1b und 2. + benutzt in dem Teil 1. +\end{verbatim} +Hence: +\begin{verbatim} + \def\@nwlangdepchks{den Teilen} + \def\@nwlangdepchk{dem Teil} +\end{verbatim} +\end{quote} +\end{quote} +@ +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B english, french, german, portuges, icelandic +Write cross-reference information in the language specified. +Defaults to +.B english. +@ \fi +@ +\clearpage +\section{The {\tt nwmac} macros for use with plain {\TeX}} + +First we make [[@]] a letter so that we can use `private' macro names. +<<nwmac.tex>>= +\catcode`\@=11 +<<nwmac.tex>>= +% scale cmbx10 instead of using cmbx12 because {\LaTeX} does, so fonts exist +\font\twlbf=cmbx10 scaled \magstep1 +\font\frtbf=cmbx10 scaled \magstep2 +% These fonts don't work with xdvi! + +\advance\hoffset 0.5 true in +\advance\hsize -1.5 true in +\newdimen\textsize +\textsize=\hsize +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +<<nwmac.tex>>= +\long\def\ifundefined#1#2#3{% + \expandafter\ifx\csname#1\endcsname\relax + #2% + \else#3% + \fi} + +\ifundefined{myheadline} + {\headline={\hbox to \textsize{\tentt\firstmark\hfil\tenrm\today\hbox + to 4em{\hss\folio}}\hss}} + {\expandafter\headline\expandafter{\myheadline}} + +\ifundefined{myfootline} + {\footline={\hfil}} + {\expandafter\footline\expandafter{\myfootline}} +<<nwmac.tex>>= +\def\semifilbreak{\vskip0pt plus1.5in\penalty-200\vskip0pt plus -1.5in} +\raggedbottom +<<nwmac.tex>>= +% +% \chapcenter macro to produce nice centered chapter titles +% +\def\chapcenter{\leftskip=0.5 true in plus 4em minus 0.5 true in + \rightskip=\leftskip + \parfillskip=0pt \spaceskip=.3333em \xspaceskip=.5em + \pretolerance=9999 \tolerance=9999 + \hyphenpenalty=9999 \exhyphenpenalty=9999} +<<nwmac.tex>>= +% \startsection{LEVEL}{INDENT}{BEFORESKIP}{AFTERSKIP}{STYLE}{HEADING} +% #1 #2 #3 #4 #5 #6 +% +% LEVEL: depth; e.g. part=0 chapter=1 sectino=2... +% INDENT: indentation of heading from left margin +% BEFORESKIP: skip before header +% AFTERSKIP: skip after header +% STYLE: style of heading; e.g.\bf +% HEADING: heading of the sectino +% +\def\startsection#1#2#3#4#5#6{\par\vskip#3 plus 2in + \penalty-200\vskip 0pt plus -2in + \noindent{\leftskip=#2 \rightskip=0.5true in plus 4em minus 0.5 true in + \hyphenpenalty=9999 \exhyphenpenalty=9999 + #5#6\par}\vskip#4% + {\def\code##1{[[}\def\edoc##1{]]}\message{[#6]}} + \settocparms{#1} + \def\themodtitle{#6} +%%%% {\def\code{\string\code}\def\edoc{\string\edoc}% + \edef\next{\noexpand\write\cont{\tocskip + \tocline{\hskip\tocindent\tocstyle\relax\themodtitle} + {\noexpand\the\pageno}}}\next % write to toc + %} +} +<<nwmac.tex>>= +\def\settocparms#1{ + \count@=#1 + \ifnum\count@<1 + \def\tocskip{\vskip3ptplus1in\penalty-100 + \vskip0ptplus-1in}% + \def\tocstyle{\bf} + \def\tocindent{0pt} + \else + \def\tocskip{} + \def\tocstyle{\rm} + \dimen@=2em \advance\count@ by \m@ne \dimen@=\count@\dimen@ + \edef\tocindent{\the\dimen@} + \fi +} +<<nwmac.tex>>= +\def\tocline#1#2{\line{{\ignorespaces#1}\leaders\hbox to .5em{.\hfil}\hfil + \hbox to1.5em{\hss#2}}} +<<nwmac.tex>>= +\def\section#1{\par \vskip3ex\noindent {\bf #1}\par\nobreak\vskip1ex\nobreak} +\def\chapter#1{\vfil\eject\startsection{0}{0pt}{6ex}{3ex}{\frtbf\chapcenter}{#1}} +\def\section#1{\startsection{1}{0pt}{4ex}{2ex}{\twlbf}{#1}} +\def\subsection#1{\startsection{2}{0pt}{2ex}{1ex}{\bf}{#1}} +\def\subsubsection#1{\startsection{3}{0pt}{1ex}{0.5ex}{\it}{#1}} +\def\paragraph#1{\startsection{4}{0pt}{1.5ex}{0ex}{\it}{#1}} + +<<kernel>> + +\def\nwfilename#1{\vfil\eject\mark{#1}} + +\def\nwbegindocs#1{\filbreak} +\def\nwenddocs{\par} +\def\nwbegincode#1{\par\nobreak + \begingroup\setupcode\newlines\parindent=0pt\parskip=0pt + %\let\oendmoddef=\endmoddef \let\oplusendmoddef=\plusendmoddef + %\def\endmoddef{\oendmoddef\par}\def\plusendmoddef{\oplusendmoddef\par}% + \let\onwenddeflinemarkup=\nwenddeflinemarkup + \def\nwenddeflinemarkup{\onwenddeflinemarkup\par}% + \hsize=\codehsize\noindent\bchack} +\def\nwendcode{\endgroup} +{\catcode`\^^M=\active % make CR an active character + \gdef\bchack#1^^M{\relax#1}% +} +<<nwmac.tex>>= +\edef\contentsfile{\jobname.toc } % file that gets table of contents info +\def\readcontents{\expandafter\input \contentsfile} + +\newwrite\cont +\openout\cont=\contentsfile +\write\cont{\string\catcode`\string\@=11}% a hack to make contents +<<nwmac.tex>>= + % take stuff in plain.tex +\def\bye{% + \write\cont{}% ensure that the contents file isn't empty + \closeout\cont + \vfil\eject\pageno=-1 % new page causes contents to be really closed + \topofcontents\readcontents\botofcontents + \vfil\eject\end} +\def\topofcontents{\vfil\mark{{\bf Contents}}} +\def\botofcontents{} +<<nwmac.tex>>= +\let\em=\it +% used to produce an itemized (bulleted) list in plain {\TeX} +% such lists can be nested +% mostly useful with WEB + +% Usage: +% \itemize +% \item First thing +% \item second thing +% \enditemize + +\newcount\listlevel +\listlevel=0 +\newdimen\itemwidth +\itemwidth=3em + +\def\itemize{\begingroup\advance\listlevel by1 + \def\item{\par\noindent + \raise2pt\llap{$\scriptstyle\bullet$\ }\ignorespaces}% + \def\nameditem##1{\par\noindent + \llap{\rlap{##1}\hskip\itemwidth}\ignorespaces}% + \par\advance\leftskip by\itemwidth\advance\rightskip by0.5\itemwidth} +\def\enditemize{\par\endgroup\noindent\ignorespaces} + +\let\begindocument=\relax +@ +Finally we make [[@]] `other' again. +<<nwmac.tex>>= +\catcode`\@=12 +@ +\section{Chunks} \nowebchunks +\twocolumn[\section{Index}] +\nowebindex* +@ +\end{document} + + diff --git a/web/noweb/src/tex/support.nw.orig b/web/noweb/src/tex/support.nw.orig new file mode 100644 index 0000000000..c706a0b4cb --- /dev/null +++ b/web/noweb/src/tex/support.nw.orig @@ -0,0 +1,1301 @@ +% -*- mode: Noweb; noweb-code-mode: tex-mode -*- +\documentstyle[noweb,twoside]{article} +\pagestyle{noweb} +\noweboptions{longchunks,smallcode} +\title{{\TeX} support for {\tt noweb}} +\author{Norman Ramsey} +\newcommand{\stylehook}{\marginpar{\raggedright\sl Style hook}} +\begin{document} +\maketitle +\tableofcontents +@ +This document describes the {\TeX} code that supports {\tt noweave} +and {\tt noweb}. Those interested in customizing their output should +focus on Section~\ref{section:sty}. +Hooks you can easily use (apart from those provided by +[[\noweboptions]]) are indicated by marginal notes. +This file contains both plain {\TeX} and {\LaTeX} support: +<<nwmac.tex>>= +% nwmac.tex -- plain TeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +<<noweb.sty>>= +% noweb.sty -- LaTeX support for noweb +% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead. +@ + +\section{Basic {\TeX} support for {\tt noweb}} +This basic code is used for both {\TeX} and {\LaTeX}. +The first step is to define [[\codehsize]], which is the width in +which code is set, and [[\codemargin]], which is the amount by which +it is indented.\stylehook +<<kernel>>= +% make \hsize in code sufficient for 88 columns +\setbox0=\hbox{\tt m} +\newdimen\codehsize +\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why +\newdimen\codemargin +\codemargin=0pt +@ %def \codehsize \codemargin +[[\defspace]] is the space we would like on the right of navigational info +that appears on definition lines, so that it lines up with the text above +and below. +<<kernel>>= +\newdimen\nwdefspace +\nwdefspace=\codehsize +%% \advance\nwdefspace by -\textwidth\relax +\advance\nwdefspace by -\hsize\relax +@ +Most code is set in an environment in which [[\setupcode]] has been +executed. +In this environment, only [[\]], [[{]], and [[}]] have their usual +categories; every other character represents itself. +Appropriate [[\chardef]]s ensure that the special characters can be +escaped with a backslash. +<<kernel>>= +\chardef\other=12 +\def\setupcode{% + \chardef\\=`\\ + \chardef\{=`\{ + \chardef\}=`\} + \catcode`\$=\other + \catcode`\&=\other + \catcode`\#=\other + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \catcode`\"=\other % fixes problem with german.sty + \obeyspaces\Tt +} +\let\nwlbrace=\{ +\let\nwrbrace=\} +@ %def \setupcode +[[\eatline]] is used to consume newlines that should be ignored, +for example, the newlines at the end of [[@ %def ]]{\em identifiers} lines. +I can't remember what [[\startline]] or [[\newlines]] are for; I don't +think {\tt noweave} ever emits them. +<<kernel>>= +{\catcode`\^^M=\active % make CR an active character + \gdef\newlines{\catcode`\^^M=\active % make CR an active character + \def^^M{\par\startline}}% + \gdef\eatline#1^^M{\relax}% +} +%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write +\def\startline{\noindent\hskip\parindent\ignorespaces} +\def\nwnewline{\ifvmode\else\hfil\break\fi} +@ %def \startline \eatline \nwnewline + +Within a code environment, it may be necessary to restore the +category codes in order to set a module (chunk) name. +This hack doesn't properly restore [["]] for use in {\tt german.sty}. +<<kernel>>= +{\obeyspaces\global\let =\ } % from texbook, p 381 +\def\setupmodname{% + \catcode`\$=3 + \catcode`\&=4 + \catcode`\#=6 + \catcode`\%=14 + \catcode`\~=13 + \catcode`\_=8 + \catcode`\^=7 + \catcode`\ =10 + \catcode`\^^M=5 + \let\{\nwlbrace + \let\}\nwrbrace + % bad news --- don't know what catcode to give " + \Rm} +@ %def \setupmodname +@ \sublabel{ref:fred} + +{\tt noweave} brackets uses of chunk names with [[\LA]] and [[\RA]], which +handle the angle brackets, font, and environment. + +As it stands, chunk names can be broken across lines (or pages). This +could result in unnecessary page breaks in code +(c.f.~p.~\pageref{sec:pagebreaking}). [[\let\\maybehbox=\mbox]] to +\stylehook +avoid breaking them +(or to make them work in math mode); this is done in code chunks, but could be done +in general. +<<kernel>>= +\def\LA{\begingroup\maybehbox\bgroup\setupmodname\It$\langle$} +\def\RA{\/$\rangle$\egroup\endgroup} +\def\code{\leavevmode\begingroup\setupcode\newlines} +\def\edoc{\endgroup} +\let\maybehbox\relax +@ %def \LA \RA \maybehbox + +[[\equivbox]] and [[\plusequivbox]] are used to set the +``\unhcopy\equivbox'' +and ``\unhcopy\plusequivbox'' that open a chunk definition or its +continuation. +{\tt noweave} brackets definitions of chunk names with [[\moddef]] and +either [[\endmoddef]] or [[\plusendmoddef]]. +<<kernel>>= +\newbox\equivbox +\setbox\equivbox=\hbox{$\equiv$} +\newbox\plusequivbox +\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$} +% \moddef can't have an argument because there might be \code...\edoc +\def\moddef{\leavevmode\kern-\codemargin\LA} +\def\endmoddef{\RA\ifmmode\equiv\else\unhcopy\equivbox\fi + \nobreak\hfill\nobreak} +\def\plusendmoddef{\RA\ifmmode\mathord{+}\mathord{\equiv}\else\unhcopy\plusequivbox\fi + \nobreak\hfill\nobreak} +@ %def \equivbox \plusequivbox \moddef \endmoddef \plusendmoddef +@ +Within a code environment, margin tags might be used to mark sub-page +numbers in the margins, separated by [[\nwmarginglue]].\stylehook +The interaction with [[\moddef]] involves tricky kerning. +The tag itself is displayed using [[\nwthemargintag]] +<<noweb.sty>>= +\def\nwopt@nomargintag{\let\nwmargintag=\@gobble} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\llap{##1\kern\nwmarginglue\kern\codemargin}}} +\def\nwopt@margintag{% + \def\nwmargintag##1{\leavevmode\kern-\codemargin\nwthemargintag{##1}\kern\codemargin}} +\def\nwthemargintag#1{\llap{#1\kern\nwmarginglue}} +\nwopt@margintag +\newdimen\nwmarginglue +\nwmarginglue=0.3in +@ %def \nwmargintag margintag nomargintag \nwmarginglue +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B margintag +Put the sub-page number (tag) of each code-chunk definition in the +left margin. (Default) +.TP +.B nomargintag +Don't use margin tags. +@ \fi +[[\nwtagstyle]] determines the style in which tags are displayed.\stylehook +<<noweb.sty>>= +\def\nwtagstyle{\footnotesize\Rm} +@ +<<kernel>>= +\def\chunklist{% +\errhelp{I changed \chunklist to \nowebchunks. +I'll try to avoid such incompatible changes in the future.}% +\errmessage{Use \string\nowebchunks\space instead of \string\chunklist}} +\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}} +\def\nowebindex{\message{<Warning: You need noweave -index to use \string\nowebindex>}} +@ %def \chunklist \nowebchunks \nowebindex +@ We have to be careful with font-changing in the presence of +different font-selection schemes. In the \LaTeX{} New Font Selection +Scheme something like [[\it\tt]] will attempt to use an italic +typewriter font. Thus we define new commands like [[\Tt]] which will +work with both the Plain and old and new \LaTeX{} schemes. (Note that +NFSS will be standard in the next version of \LaTeX.) A problem with +these definitions arises with NFSS: in math mode the won't work +unless the {\tt oldlfont} backwards-compatibility option is in effect. +For the moment, you can get round this by using [[\mbox]]. + +If you wanted code set in a different font, you could re-define +[[\Tt]].\stylehook{} [\LaTeX2e actually behaves like OFSS, but the +extra [[\reset@font]] does no harm.] +<<kernel>>= +% here is support for the new-style (capitalized) font-changing commands +% thanks to Dave Love +\ifx\documentstyle\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % plain +\else\ifx\selectfont\undefined + \let\Rm=\rm \let\It=\it \let\Tt=\tt % LaTeX OFSS +\else % LaTeX NFSS + \def\Rm{\reset@font\rm} + \def\It{\reset@font\it} + \def\Tt{\reset@font\tt} + \def\Bf{\reset@font\bf} +\fi\fi +\ifx\reset@font\undefined \let\reset@font=\relax \fi +@ %def \Rm \Tt \It \Bf +@ +\clearpage +\section{The {\tt noweb} document-style option for {\LaTeX}} +\label{section:sty} +{\LaTeX} support begins with the kernel shown above. +<<noweb.sty>>= +<<kernel>> +@ +\subsection{Support for noweb options} +<<noweb.sty>>= +\def\noweboptions#1{% + \def\@nwoptionlist{#1}% + \@for\@nwoption:=\@nwoptionlist\do{% + \@ifundefined{nwopt@\@nwoption}{% + \@latexerr{There is no such noweb option as '\@nwoption'}\@eha}{% + \csname nwopt@\@nwoption\endcsname}}} +@ %def \noweboptions +\subsection{Adjusting placement of code on the page} +{\LaTeX} requires a larger [[\codehsize]] because +code is indented by [[\codemargin]].\stylehook +<<noweb.sty>>= +\codemargin=10pt +\advance\codehsize by \codemargin % make room for indentation of code +\advance\nwdefspace by \codemargin % and fix adjustment for def/use +@ +[[\noweboptions{shift}]] is used to shift the whole page left to make room for +the wide code lines. +It may be emitted by {\tt noweave -shift}, or it might be given by a user. +<<noweb.sty>>= +\def\nwopt@shift{% + \dimen@=-0.8in + \if@twoside % Values for two-sided printing: + \advance\evensidemargin by \dimen@ + \else % Values for one-sided printing: + \advance\evensidemargin by \dimen@ + \advance\oddsidemargin by \dimen@ + \fi +% \advance \marginparwidth -\dimen@ +} +\let\nwopt@noshift\@empty +@ %def shift noshift + +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B shift +Shift text to the left so that long code lines won't extend +off the right-hand side of the page. +@ \fi + +\subsection{Page-breaking strategy}\label{sec:pagebreaking} + +We want to insert penalties aiming for: +\begin{enumerate} +\item +No page breaks in the middle of a code chunk unless necessary to avoid +an overfull vbox; +\item +Documentation immediately preceding a code chunk should appear on +the same page as that code chunk unless doing so would violate rule 1. +\end{enumerate} +[[\filbreak]] is useful for this sort of thing (see {\em The \TeX + book\/}) and is used to encourage breaks at the right places between +chunks. Appropriate penalties are inserted elsewhere, between code +lines in particular. + +\subsection{Environments for setting code} + +[[\nwbegincode]] and [[\nwendcode]] are used by {\tt noweave} to bracket +code chunks. +The [[webcode]] environment is intended for users who want to paste +{\tt noweave} output into papers. + +The definition of [[\nwbegincode]] is based on the verbatim +implementation in {\tt verbatim.sty}, which will, presumably be in the +next version of \LaTeX\@. One thing it does differently, apart from +the catcode changes is setting [[\linewidth]]; this will avoid some +overfull hboxen when the code lines are too long, but the lines won't +be broken anyhow (even within chunk names because of the +[[\maybehbox]] definition). +<<noweb.sty>>= +\def\nwbegincode#1{% + \begingroup + <<[[\nwbegincode]] separation and penalties>> + \@begincode } +\def\nwendcode{\endtrivlist \endgroup \filbreak} % keeps code on 1 page + +\newenvironment{webcode}{% + \@begincode +}{% + \endtrivlist} +@ %def \nwbegincode \nwendcode webcode +@ This is just common code between [[\nwbegincode]] and [[webcode]]. +<<noweb.sty>>= +\def\@begincode{% + <<[[\trivlist]] clich\'e (\`a la {\Tt verbatim})>> + \linewidth\codehsize + <<[[\obeylines]] setup>> + <<zap ligatures, fix spaces>> + \nowebsize \setupcode + \let\maybehbox\mbox } +@ %def \@begincode +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B smallcode +Set code in +.I LaTeX +.B "\\\\small" +font instead of +.B "\\\\normalsize." +@ \fi +@ [[\nowebsize]] governs the size at which code is set; users who want +to minimize code can [[\let\nowebsize=\small]]. +Slitex users should try +\begin{quote} +[[\def\nowebsize{\normalsize\baselineskip=20pt \parskip=5pt }]] +\end{quote} +to avoid code lines that are too far apart. +[[\nwcodetopsep]] is +the glue placed before code chunks.\stylehook +<<noweb.sty>>= + \newskip\nwcodetopsep \nwcodetopsep = 3pt plus 1.2pt minus 1pt + \let\nowebsize=\normalsize + \def\nwopt@smallcode{\let\nowebsize=\small} +@ %def \nwcodetopsep \nowebsize smallcode +@ Maybe the penalties ought to be parameters\dots +<<[[\nwbegincode]] separation and penalties>>= + \topsep \nwcodetopsep + \@beginparpenalty \@highpenalty + \@endparpenalty -\@highpenalty +@ +The [[\trivlist]] clich\'e isn't quite a clich\'e because we adjust +[[\leftskip]] for indentation by [[\codemargin]] and adjust +[[\rightskip]] to allow lines up to [[\codehsize]] long without +overfull boxen +($\mbox{[[\codehsize]]}=\mbox{[[\hsize]]}+\mbox{[[\rightskip]]}$). +Note that [[\hsize]] isn't altered. +<<[[\trivlist]] clich\'e (\`a la {\Tt verbatim})>>= + \trivlist \item[]% + \leftskip\@totalleftmargin \advance\leftskip\codemargin + \rightskip\hsize \advance\rightskip -\codehsize + \parskip\z@ \parindent\z@ \parfillskip\@flushglue +@ The penalty inserted between verbatim lines would normally be +[[\interlinepenalty]], but we want to prohibit breaks there. +@ +Note the bug lurking somewhere in this code, as reported by Steven Ooms: +\begin{quote} +I have some lay-out +problems in the documentation chunks. When using the (La)TeX commands +[[\hline]] or [[\vtop]] the right margin is always extended far beyond the page +margin after the first code chunk has been typeset. I'm still looking for +the exact cause of it, but to me it seems that LaTeX supposes for those +commands that the line width for the documentation chunk is as large as that +for code chunks, which isn't true in reality. +\end{quote} +@ +<<[[\obeylines]] setup>>= + \@@par + \def\par{\leavevmode\null \@@par \penalty\nwcodepenalty}% + \obeylines +@ [[\nwcodepenalty]] is the penalty for breaking between lines in a +code chunk. If you set it to 10000, code will never be broken across +pages.\stylehook{} I guess this should be settable in [[\noweboptions]]. +<<noweb.sty>>= +\newcount\nwcodepenalty \nwcodepenalty=\@highpenalty +@ %def \nwcodepenalty +The cursing chunk accounts for the addition of a mess of characters +to those reset by [[\@noligs]] in \LaTeX2e. +<<zap ligatures, fix spaces>>= + \@noligs <<make all those damn active characters ``other''>> + \setupcode \frenchspacing \@vobeyspaces +@ +We can't make [[`]] ``other,'' because then we'll get ligatures. +(Why Don put these ligatures in the [[\tt]] font I wish I knew.) +But we'll step on all the others. +<<make all those damn active characters ``other''>>= + \ifx\verbatim@nolig@list\undefined\else + \let\do=\nw@makeother \verbatim@nolig@list \do@noligs\` + \fi +<<noweb.sty>>= +\def\nw@makeother#1{\catcode`#1=12 } +@ +{\tt noweave} uses [[\nwbegindocs{nnn}]] and [[\nwenddocs]] to bracket +documentation chunks. +If a documentation chunk does not continue the current paragraph, +{\tt noweave} inserts [[\nwdocspar]], which uses +[[\filbreak]] in an attempt to keep the documentation chunk on the +same page as the code chunk that follows it. (The code chunk will +have another [[\filbreak]] after it---see [[\nwbegincode]].) +[[\nwbegindocs]] doesn't start a +new paragraph if the previous chunk didn't end one, i.e.\ didn't enter +vmode; if it does start a new one, it's only indented by the use of +[[\nwdocspar]]. +<<noweb.sty>>= +\def\nwbegindocs#1{\ifvmode\noindent\fi} +\let\nwenddocs=\relax +\let\nwdocspar=\filbreak +@ %def \nwbegindocs \nwdocspar \nwenddocs +The page-breaking strategy implies ragged bottom pages, so we should +turn it on in general (this is relevant for the {\tt report} style): +<<noweb.sty>>= +\raggedbottom +<<nwmac.tex>>= +\def\nwdocspar{\par\semifilbreak} +@ + +{\tt noweave} doesn't bracket quoted code with [[\code]] and [[\edoc]] any more. +It probably should do something nifty, just to make {\TeX} hackers happy, but it doesn't. +<<noweb.sty>>= +\def\code{\leavevmode\begingroup\setupcode\@vobeyspaces\obeylines} +\let\edoc=\endgroup +@ +\subsection{The {\tt noweb} page style} +Headers contain file name, date, and page number. +{\tt noweave} emits [[\nwfilename{]]{\em name}[[}]] for each new file. +In the {\tt noweb} page style, new files cause page breaks; +otherwise they are ignored. +<<noweb.sty>>= +\newdimen\@original@textwidth +\def\ps@noweb{% + \@original@textwidth=\textwidth + \let\@mkboth\@gobbletwo + \def\@oddfoot{}\def\@evenfoot{}% No feet. + \if@twoside % If two-sided printing. + \def\@evenhead{\hbox to \@original@textwidth{% + \Rm \thepage\qquad{\Tt\leftmark}\hfil\today}}% Left heading. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \else % If one-sided printing. + \def\@oddhead{\hbox to \@original@textwidth{% + \Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading. + \let\@evenhead\@oddhead + \fi + \let\chaptermark\@gobble + \let\sectionmark\@gobble + \let\subsectionmark\@gobble + \let\subsubsectionmark\@gobble + \let\paragraphmark\@gobble + \let\subparagraphmark\@gobble + \def\nwfilename{\begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@filename} + \def\nw@filename##1{\endgroup\markboth{##1}{##1}\let\nw@filename=\nw@laterfilename}% +} +\def\nw@laterfilename#1{\endgroup\clearpage \markboth{#1}{#1}} +\let\nwfilename=\@gobble +@ %def \@original@textwidth \ps@noweb \nwfilename +@ +\subsection{Chunk cross-reference} +[[\nwalsodefined]], [[\nwused]], and [[\nwnotused]] are emitted by the {\tt +noweb} cross-referencers. (What arguments?) +If unused chunks are output chunks, a filter can slip in +[[\let\nwnotused\nwoutput]]. +The style uses [[\nwcodecomment]] for all annotations that follow code +chunks. +Fiddling with it can change the appearance of the output. +Note that [[\nwcodecomment]] is used after [[\nwbegincode]], with +[[\obeylines]] in efect. Since linebreaking can occur here, we need +to change the [[\interlinepenalty]]. +A little vertical space ([[\nwcodecommentsep]]\stylehook) appears before the first +comment. + +We firkled with [[\rightskip]] in [[\nwbegincode]] above; now we want to +reset it so that paragraphs are the normal width ([[\textwidth]], +possibly less [[\codemargin]]) and set ragged right. This is done as +usuall by making [[\rightskip]] naturally zero but stretchable. +<<noweb.sty>>= +\def\nwcodecomment#1{\@@par\penalty\nwcodepenalty + <<add [[\nwcodecommentsep]] if this is the first [[\nwcodecomment]]>>% + \hspace{-\codemargin}{% + \rightskip=0pt plus1in + \interlinepenalty\nwcodepenalty + \let\\\relax\footnotesize\Rm #1\@@par\penalty\nwcodepenalty}} +@ %def \nwcodecomment +This stuff is used at the end of a chunk. +<<noweb.sty>>= +\def\@nwalsodefined#1{\nwcodecomment{This definition is continued \nwpageprep\ \@pagesl{#1}.}} +\def\@nwused#1{\nwcodecomment{This code is used \nwpageprep\ \@pagesl{#1}.}} +\def\@nwnotused#1{\nwcodecomment{Root chunk (not used in this document).}} +\def\nwoutput#1{\nwcodecomment{This code is written to file {\Tt \@stripstar#1*\stripped}.}} +\def\@stripstar#1*#2\stripped{#1} +@ %def \nwalsodefined \nwused \nwnotused \nwoutput +<<add [[\nwcodecommentsep]] if this is the first [[\nwcodecomment]]>>= +\if@firstnwcodecomment + \vskip\nwcodecommentsep\penalty\nwcodepenalty\@firstnwcodecommentfalse +\fi +@ +This stuff on the definition line. +Note the hooks\stylehook{} for pointer styles. +<<noweb.sty>>= +\newcommand{\nwprevdefptr}[1]{% + \mbox{$\mathord{\triangleleft}\,\mathord{\mbox{\subpageref{#1}}}$}} +\newcommand{\nwnextdefptr}[1]{% + \mbox{$\mathord{\mbox{\subpageref{#1}}}\,\mathord{\triangleright}$}} + +\newcommand{\@nwprevnextdefs}[2]{% + {\nwtagstyle + \ifx\relax#1\else ~~\nwprevdefptr{#1}\fi + \ifx\relax#2\else ~~\nwnextdefptr{#2}\fi}} +\newcommand{\@nwusesondefline}[1]{{\nwtagstyle~~(\@pagenumsl{#1})}} +\newcommand{\@nwstartdeflinemarkup}{\nobreak\hskip 1.5em plus 1fill\nobreak} +\newcommand{\@nwenddeflinemarkup}{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak} +@ +And here are the options we use to choose one or the other. +<<noweb.sty>>= +\def\nwopt@longxref{% + \let\nwalsodefined\@nwalsodefined + \let\nwused\@nwused + \let\nwnotused\@nwnotused + \let\nwprevnextdefs\@gobbletwo + \let\nwusesondefline\@gobble + \let\nwstartdeflinemarkup\relax + \let\nwenddeflinemarkup\relax +} +\def\nwopt@shortxref{% + \let\nwalsodefined\@gobble + \let\nwused\@gobble + \let\nwnotused\@gobble + \let\nwprevnextdefs\@nwprevnextdefs + \let\nwusesondefline\@nwusesondefline + \let\nwstartdeflinemarkup\@nwstartdeflinemarkup + \let\nwenddeflinemarkup\@nwenddeflinemarkup +} +\nwopt@shortxref % to hell with backward compatibility! +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B longxref, shortxref +Use either long, Knuth-style chunk cross-reference, or short, +Hanson-style chunk cross-reference. +The former uses small paragraphs after the chunk; +the latter uses symbols on the definition line. +Defaults to +.B shortxref. +@ \fi +<<noweb.sty>>= +\newskip\nwcodecommentsep \nwcodecommentsep=3pt plus 1pt minus 1pt +\newif\if@firstnwcodecomment\@firstnwcodecommenttrue +@ +\subsection{Page ranges} +The goal is to combine sub-page numbers in a way that makes sense. +Multiple sub-pages of one page become that page, and individual +pages are combined into ranges. +(A range may be only one page.) +<<noweb.sty>>= +\newcount\@nwlopage\newcount\@nwhipage % range lo..hi-1 +\newcount\@nwlosub % subpage of lo +\newcount\@nwhisub % subpage of hi +\def\@nwfirstpage#1#2{% subpage page + \@nwlopage=#2 \@nwlosub=#1 + \advance\@nwpagecount by \@ne + <<$\mbox{[[\@nwhipage]]} := \mbox{[[\@nwlopage]]}+1$>>} +\def\@nwnextpage#1#2{% subpage page + \ifnum\@nwhipage=#2 + \advance\@nwhipage by \@ne \advance\@nwpagecount by \@ne \@nwhisub=#1 \else + \ifnum#2<\@nwlopage <<new range starting with [[#2]]>>\else + \ifnum#2>\@nwhipage <<new range starting with [[#2]]>>\else + \@nwlosub=0 \@nwhisub=0 + \fi\fi\fi + } +<<new range starting with [[#2]]>>= +<<add range to range list>>\@nwfirstpage{#1}{#2} +<<$\mbox{[[\@nwhipage]]} := \mbox{[[\@nwlopage]]}+1$>>= +\@nwhipage=\@nwlopage\advance\@nwhipage by \@ne +<<add range to range list>>= +<<set [[\@tempa]] to page range(s), marked with [[\\]]>>% +\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa +<<set [[\@tempa]] to page range(s), marked with [[\\]]>>= +\advance\@nwhipage by \m@ne +\ifnum\@nwhipage=\@nwlopage + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}}% +\else + \count@=\@nwhipage \advance\count@ by \m@ne + \ifnum\count@=\@nwlopage % consecutive pages + \edef\@tempa{\noexpand\noexpand\noexpand\\% + {\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}% + \noexpand\noexpand\noexpand\\% + {\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}}% + \else <<use rules from Chicago style manual>>% + \fi +\fi +<<use rules from Chicago style manual>>= +\ifnum\@nwlopage<110 <<normal range>>\else + \count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100 + \ifnum\count@=\@nwlopage <<normal range>>\else + \count@=\@nwlopage \divide\count@ by 100 + \@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100 + \ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi + \multiply\@nwpagetemp by 100 + \advance \@nwhipage by -\@nwpagetemp + <<normal range>>% + \else <<normal range>>% + \fi + \fi +\fi +<<normal range>>= +\edef\@tempa{\noexpand\noexpand\noexpand\\{\number\@nwlopage--\number\@nwhipage}} +<<noweb.sty>>= +\newcount\@nwpagetemp +@ +The sequence [[\@pagesl]] makes a range of pages from a list of labels. +[[\subpages]] works from a list of [[{{subpage}{page}}]]. +<<noweb.sty>>= +\newcount\@nwpagecount +\def\@nwfirstpagel#1{% label + \@ifundefined{r@#1}{<<warn of undefined reference to [[#1]] and add page ??>>}{% + \edef\@tempa{\noexpand\@nwfirstpage\subpagepair{#1}}\@tempa}} +\def\@nwnextpagel#1{% label + \@ifundefined{r@#1}{<<warn of undefined reference to [[#1]] and add page ??>>}{% + \edef\@tempa{\noexpand\@nwnextpage\subpagepair{#1}}\@tempa}} +\def\@pagesl#1{% list of labels + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + <<add range to range list>>\let\\=\relax + \nwpageword\ifnum\@nwpagecount=1 \else s\fi~\commafy{\nw@pages}} +\def\@pagenumsl#1{% list of labels -- doesn't include word `pages', commas, or `and' + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1% + <<add range to range list>>\def\\{\let\\=~}\nw@pages} +\def\subpages#1{% list of {{subpage}{page}} + \gdef\nw@pages{}\@nwpagecount=0 + \def\\##1{\edef\@tempa{\noexpand\@nwfirstpage##1}\@tempa + \def\\####1{\edef\@tempa{\noexpand\@nwnextpage####1}\@tempa}}#1% + <<add range to range list>>\let\\=\relax + \nwpageword\ifnum\@nwpagecount=1 \else s\fi~\commafy{\nw@pages}} +\def\@nwaddrange{<<add range to range list>>} +@ +[[\nwpageword]] and [[\nwpageprep]] let you change the wording of the cross-reference +information. +<<noweb.sty>>= +\def\nwpageword{chunk} % was page +\def\nwpageprep{in} % was on +<<warn of undefined reference to [[#1]] and add page ??>>= +<<warn of undefined reference to [[#1]]>>% +\nwix@cons\nw@pages{\\{\bf ??}} +@ +\subsection{Sub-page references} +This is the wonderful code that Dave Love provided to make page +references like 7a, 7b, and so on. + +This code +provides a mechanism for defining `page +sub-references' using [[\sublabel{foo}]] referenced with +[[\subpageref{foo}]]. Sub-references will be numbered like these real +examples: \subpageref{ref:foo}, \subpageref{ref:bar}, +\subpageref{ref:baz}\sublabel{ref:foo}\sublabel{ref:bar}\sublabel{ref:baz} +etc.\ unless there is only one on the page, in which case the letter +will be dropped like this: \subpageref{ref:fred}. + +To be able to use [[\subpageref]] we must define the label with +[[\sublabel]], used like label. (Using +[[\ref]] with a label defined by [[\sublabel]] will +produce the sub-reference number, by the way, and [[\pageref]] +works as expected.) Note that +[[\subpageref]] is robust and [[\ref]] and [[\pageref]] are redefined to be +robust also, as they will be in future \LaTeX{} releases. +Incidentally, these expand to the relevant text plus [[\null]]---you +might want to strip this off, e.g.\ for sorting lists. + + +There are various ways we could attack this task (which is made +non-trivial by the well-known asynchrony of (La)\TeX's output +routine), but +they all must depend on hacks in the [[.aux]] file or a similar one. +Joachim Schrod's [[fnpag.sty]] does the same sort of thing differently +to this \LaTeX-specific approach. See [[latex.tex]] for enlightenment +on the cross-referencing mechanism and the \LaTeX{} internals used +below. [DL: The internals change in \LaTeX2e compared with +\LaTeX~2.09. The code here still works, though.] + +@ +The [[\subpageref]] macro first does a normal [[\pageref]]. If the +reference is actually defined, it then goes on to check whether the +control sequence [[2on]]\LA{}{page referenced}\RA{} is defined and sets the +[[\ref]] value to get [[a]] etc.\ if so. The magic, of course, is in +defining the [[2on]] bit appropriately. +[[\subpageref]] also tries to include the right hyperstuff for xhdvi. +<<noweb.sty>>= +\newcommand{\subpageref}[1]{% + \nwhyperreference{#1}{% + \@ifundefined{r@#1}% + {\pageref{#1}}% + {\@ifundefined{2on\@pageref{#1}}% + {\pageref{#1}}% + {\expandafter\expandafter\expandafter\nwthepagenum\csname r@#1\endcsname}}}} +@ %def \subpageref +[[\subpagepair]] produces a [[{subpage}{page}]] pair. +<<noweb.sty>>= +\newcommand{\subpagepair}[1]{% % produces {subpage}{page} + \@ifundefined{r@#1}% + {{0}{0}}% + {\@ifundefined{2on\@pageref{#1}}% + {{0}{\expandafter\expandafter\expandafter\@cdr\csname r@#1\endcsname\@nil}}% + {\@nameuse{r@#1}}}} +@ +[[\@pageref]] is like [[\pageref]], but it expands to [[\relax]] without a warning +if the reference is undefined. +<<noweb.sty>>= +\def\@pageref#1{\expandafter\expandafter\expandafter + \@cdr\csname r@#1\endcsname\@nil} +@ %def \@pageref +@ +[[\sublabel]] is like the [[\label]] command, except that it writes +[[\newsublabel]] onto the [[.aux]] file rather than [[\newlabel]]. +For hyperreferencing, all labels must be hypertext +anchors, for which we use [[\nwblindhyperanchor]]. +<<noweb.sty>>= +\newcommand{\sublabel}[1]{% + \nwblindhyperanchor{#1}% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ %def \sublabel +[[\nosublabel]] creates a label with a sub-page part of~0. +<<noweb.sty>>= +\newcommand{\nosublabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newlabel{#1}{{0}{\thepage}}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ %def \sublabel +@ +[[\newsublabel]] is the macro that does the important work. It is called with the +same sort of arguments as [[\newlabel]]: the first argument is the +label name and the second is [[{]]\LA ref value[[}{]]page number\RA[[}]]. +(Note that the only definition here which needs to be +global is the one which is, and that [[\global]] is redefined by +[[\enddocument]], which will bite you if you use it\dots) +<<noweb.sty>>= +<<definition of [[\newsublabel]]>> +@ +First we extract the page number into [[\this@page]]. +<<definition of [[\newsublabel]]>>= +\newcommand{\newsublabel}[2]{% + \edef\this@page{\@cdr#2\@nil}% +@ %def \newsublabel +@ +Then we see whether it's changed from the value of [[\last@page]] +which was stashed away by the last [[\newsublabel]] (or is [[\relax]] if +this is the first one). If the page has changed, we reset the +counter [[\sub@page]] telling us how many sub-labels there have been +on the page. +<<definition of [[\newsublabel]]>>= + \ifx\this@page\last@page\else + \sub@page=\z@ + \fi + \edef\last@page{\this@page} + \advance\sub@page by \@ne +@ +If we've had at least two on the page, we define the +[[2on]]\LA{}{page no.}\RA{} macro to indicate the fact. +<<definition of [[\newsublabel]]>>= + \ifnum\sub@page=\tw@ + \global\@namedef{2on\this@page}{}% + \fi +@ +Then we write a normal [[\newlabel]] with the sub-reference as the +normal reference value in the second argument. +<<definition of [[\newsublabel]]>>= + \edef\@tempa{\noexpand\newlabel{#1}% + {{\number\sub@page}{\this@page}}}% + \@tempa} +@ +We need to define these. +<<noweb.sty>>= +\def\last@page{\relax} +\newcount\sub@page +@ %def \last@page \sub@page +@ +We use Rainer's new expandable definitions of [[\ref]] and +[[\pageref]] to minimise the risk of nasty surprises. +<<noweb.sty>>= +% RmS 92/08/14: made \ref and \pageref robust +\def\ref#1{\@ifundefined{r@#1}{{\bf ??}<<warn of undefined reference to [[#1]]>>}% + {\expandafter\expandafter\expandafter + \@car\csname r@#1\endcsname\@nil\null}} +\def\pageref#1{\@ifundefined{r@#1}{{\bf ??}<<warn of undefined reference to [[#1]]>>}% + {\expandafter\expandafter\expandafter + \@cdr\csname r@#1\endcsname\@nil\null}} +\def\@refpair#1{\@ifundefined{r@#1}{{0}{0}<<warn of undefined reference to [[#1]]>>}% + {\@nameuse{r@#1}}} +<<warn of undefined reference to [[#1]]>>= +\@warning{Reference `#1' on page \thepage \space undefined} +@ %def \ref \pageref +@ + +Here a a couple of hooks for formatting sub-page numbers, +which can be alphbetic, numeric, or omitted.\stylehook +<<noweb.sty>>= +\def\@alphasubpagenum#1#2{#2\ifnum#1=0 \else\@alph{#1}\fi} +\def\@nosubpagenum#1#2{#2} +\def\@numsubpagenum#1#2{#2\ifnum#1=0 \else.\@arabic{#1}\fi} +\def\nwopt@nosubpage{\let\nwthepagenum=\@nosubpagenum\nwopt@nomargintag} +\def\nwopt@numsubpage{\let\nwthepagenum=\@numsubpagenum} +\def\nwopt@alphasubpage{\let\nwthepagenum=\@alphasubpagenum} +\nwopt@alphasubpage +@ %def \nwthepagenum nosubpage numsubpage alphasubpage +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B alphasubpage, numsubpage, nosubpage +Number chunks by the number of the page on which they appear, +followed by an alphabetic (numeric, not used) ``sub-page'' indicator. +Defaults to +.B alphasubpage. +.B nosubpage +implies +.B nomargintag. +@ \fi +@ + +\subsection{{\tt WEB}-like chunk numbering} +Here's a righteous hack: we get the effect of WEB-like chunk numbers +just by redefining [[\sublabel]] to use a counter instead of the current page number. +Since the numbers are all distinct, no sub-page number is ever used. +<<noweb.sty>>= +\newcount\nw@chunkcount +\nw@chunkcount=\@ne +\newcommand{\weblabel}[1]{% + \@bsphack\if@filesw {\let\thepage\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string + \newsublabel{#1}{{}{\number\nw@chunkcount}}}}% + \expandafter}\@tempa + \global\advance\nw@chunkcount by \@ne + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +\def\nwopt@webnumbering{\let\sublabel=\weblabel\def\nwpageword{chunk}\def\nwpageprep{in}} +@ %def \weblabel webnumbering +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B webnumbering +Number chunks consecutively, in +.I WEB +style, instead of using sub-page numbers. +@ \fi +@ +\subsection{Indexing (identifier cross-reference) support} + +\subsubsection{Tracking definitions and uses} +All index definitions and uses are associated with +a label defined with [[\sublabel]] or [[\nosublabel]]. +Either the label is the [[\sublabel]] of the code chunk in which the definition or use +appears, or it is a [[\nosublabel]] appearing in the middle of a +documentation chunk. +<<noweb.sty>>= +% \nwindexdefn{printable name}{identifying label}{label of chunk} +% \nwindexuse{printable name}{identifying label}{label of chunk} + +\def\nwindexdefn#1#2#3{\@auxix{\protect\nwixd}{#2}{#3}} +\def\nwindexuse#1#2#3{\@auxix{\protect\nwixu}{#2}{#3}} + +\def\@auxix#1#2#3{% {marker}{id label}{subpage label} + \@bsphack\if@filesw {\let\nwixd\relax\let\nwixu\relax + \def\protect{\noexpand\noexpand\noexpand}% + \edef\@tempa{\write\@auxout{\string\nwixadd{#1}{#2}{#3}}}% + \expandafter}\@tempa + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ %def \nwindexdefn \nwindexuse \@auxix +<<noweb.sty>>= +% \nwixadd{marker}{idlabel}{subpage label} +\def\nwixadd#1#2#3{% + \@ifundefined{nwixl@#2}% + {\global\@namedef{nwixl@#2}{#1{#3}}}% + {\expandafter\nwix@cons\csname nwixl@#2\endcsname{#1{#3}}}} +@ %def \nwixadd +@ +\subsubsection{Writing lists with commas and ``and''} +You get one of +\begin{itemize} +\item ``$a$'' +\item ``$a$ and $b$'' +\item ``$a$, $\ldots$, $b$, and $c$'' +\end{itemize} +Plus [[\\]] is applied to each element of the list. +<<noweb.sty>>= +\newcount\@commacount +\def\commafy#1{% + {\nwix@listcount{#1}\@commacount=\nwix@counter + \let\@comma@each=\\% + \ifcase\@commacount\let\\=\@comma@each\or\let\\=\@comma@each\or + \def\\{\def\\{ and \@comma@each}\@comma@each}\else + \def\\{\def\\{, % + \advance\@commacount by \m@ne + \ifnum\@commacount=1 and~\fi\@comma@each}\@comma@each}\fi + #1}} +@ %def \@commacount \commafy + +\subsubsection{New, improved index code} +There are two kinds of lists. +One kind is a generic list in which elements are preceded by [[\\]]. +If the elements are index elements, they are {\em[[{]]printable +identifier[[}{]]label[[}]]} pairs. +The other kind is a list of sub-page labels, in which each +element is preceded by either [[\nwixd]] or [[\nwixu]]. +<<noweb.sty>>= +\def\nwix@cons#1#2{% {list}{\marker{element}} + {\toks0=\expandafter{#1}\def\@tempa{#2}\toks2=\expandafter{\@tempa}% + \xdef#1{\the\toks0 \the\toks2 }}} +@ %def \nwix@cons +The reference list for an identifier labelled {\em id} +is always called [[\nwixl@]]{\em id}. +Most applications will work with reference lists by applying [[\\]] +either to the defs or to the uses. +<<noweb.sty>>= +\def\nwix@uses#1{% {label} + \def\nwixu{\\}\let\nwixd\@gobble\@nameuse{nwixl@#1}} +\def\nwix@defs#1{% {label} + \def\nwixd{\\}\let\nwixu\@gobble\@nameuse{nwixl@#1}} +@ %def \nwix@uses \nwix@defs + +Some applications count uses to see whether there is any need to +display information. +<<noweb.sty>>= +\newcount\nwix@counter +\def\nwix@listcount#1{% {list with \\} + {\count@=0 + \def\\##1{\advance\count@ by \@ne }% + #1\global\nwix@counter=\count@ }} +\def\nwix@usecount#1{\nwix@listcount{\nwix@uses{#1}}} +\def\nwix@defcount#1{\nwix@listcount{\nwix@defs{#1}}} +@ %def \nwix@listcount \nwix@usecount \nwix@defcount +@ +\subsubsection{Supporting a mini-index at the end of each chunk} +When displaying identifiers used, show the identifier and its +definitions. +<<noweb.sty>>= +\def\nwix@id@defs#1{% index pair + {{\Tt \@car#1\@nil}% + \def\\##1{~\subpageref{##1}}\nwix@defs{\@cdr#1\@nil}}} +\def\nwidentuses#1{% list of index pairs + \nwcodecomment{Uses \let\\=\nwix@id@defs\commafy{#1}.}} +@ %def \nwidentuses \nwix@id@defs +The definitions section is a bit more complex, because it is omitted +if none of the identifiers defined is ever used. +<<noweb.sty>>= +\def\nwix@totaluses#1{% list of index pairs + {\count@=0 + \def\\##1{\nwix@usecount{\@cdr##1\@nil}\advance\count@ by\nwix@counter}% + #1\global\nwix@counter\count@ }} +\def\nwix@id@uses#1#2{% {ident}{label} + \nwix@usecount{#2}\ifnum\nwix@counter>0 + {\advance\leftskip by \codemargin + \nwcodecomment{{\Tt #1}, used \nwpageprep\ \@pagesl{\nwix@uses{#2}}.}}% + \else + \ifnw@hideunuseddefs\else + {\advance\leftskip by \codemargin \nwcodecomment{{\Tt #1}, never used.}}% + \fi + \fi} +\def\nwidentdefs#1{% list of index pairs + \ifnw@hideunuseddefs\nwix@totaluses{#1}\else\nwix@listcount{#1}\fi + \ifnum\nwix@counter>0 + \nwcodecomment{Defines:}% + {\def\\##1{\nwix@id@uses ##1}#1}% + \fi} +<<noweb.sty>>= +\newif\ifnw@hideunuseddefs\nw@hideunuseddefsfalse +\def\nwopt@hideunuseddefs{\nw@hideunuseddefstrue} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B hideunuseddefs +Omit defined but unused identifiers from +the local identifier cross-reference (Preston Briggs). +@ \fi +<<noweb.sty>>= +\def\nwopt@noidentxref{% + \let\nwidentdefs\@gobble + \let\nwidentuses\@gobble} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B noidentxref +Omit the local identifier cross-reference which follows each code chunk. +@ \fi + +\subsubsection{Support for chunk and identifier indices} +The index in the back shows absolutely all the pages. +<<noweb.sty>>= +\def\nw@underlinedefs{% {list with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} + +\def\nw@indexline#1#2{% + {\indent {\Tt #1}: \nw@underlinedefs\@nameuse{nwixl@#2}\par}} + +\newenvironment{thenowebindex}{\parindent=-10pt \parskip=\z@ + \advance\leftskip by 10pt + \advance\rightskip by 0pt plus1in\par\@afterindenttrue + \def\\##1{\nw@indexline##1}}{} +@ +The information comes from the list [[nwisx@i]]. +<<noweb.sty>>= +\def\nowebindex{% + \@ifundefined{nwixs@i}% + {\@warning{The \string\nowebindex\space is empty}}% + {\begin{thenowebindex}\@nameuse{nwixs@i}\end{thenowebindex}}} +@ +Here's a more efficient version for the external case: +<<noweb.sty>>= +\def\nowebindex@external{% + {\let\nwixadds@c=\@gobble + \def\nwixadds@i##1{\nw@indexline##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebindex}\@input{\jobname.nwi}\end{thenowebindex}}} +@ +That list ([[nwisx@i]]) is created by calls to [[\nwixlogsorted{i}]]. +<<noweb.sty>>= +\def\nwixlogsorted#1#2{% list data + \@bsphack\if@filesw + \toks0={#2}\immediate\write\@auxout{\string\nwixadds{#1}{\the\toks0}} + \if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack} +@ +[[nwixs@c]] and [[nwixs@i]] are sorted lists of chunks and identifiers, respectively. +<<noweb.sty>>= +\def\nwixadds#1#2{% + \@ifundefined{nwixs@#1}% + {\global\@namedef{nwixs@#1}{\\{#2}}}% + {\expandafter\nwix@cons\csname nwixs@#1\endcsname{\\{#2}}}} +\let\nwixaddsx=\@gobbletwo +@ +If an external index is used, we need a [[.nwi]] file, +[[\nwixadds]] is to be ignored, and we use [[\nwixaddsx]]. +<<noweb.sty>>= +\def\nwopt@externalindex{% + \ifx\nwixadds\@gobbletwo % already called + \else + \let\nwixaddsx=\nwixadds \let\nwixadds=\@gobbletwo + \let\nowebindex=\nowebindex@external + \let\nowebchunks=\nowebchunks@external + \fi} +@ +\iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B externalindex +Use an index generated with +.I noindex(1) +(q.v.). +@ \fi +@ +<<noweb.sty>>= +\def\nowebchunks{% + \@ifundefined{nwixs@c}% + {\@warning{The are no \string\nowebchunks}}% + {\begin{thenowebchunks}\@nameuse{nwixs@c}\end{thenowebchunks}}} +\def\nowebchunks@external{% + {\let\nwixadds@i=\@gobble + \def\nwixadds@c##1{\nw@onechunk##1}% + \def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}% + \begin{thenowebchunks}\@input{\jobname.nwi}\end{thenowebchunks}}} + \@namedef{r@nw@notdef}{{0}{(never defined)}} +<<noweb.sty>>= +\def\nw@chunkunderlinedefs{% {list of labels with \nwixd, \nwixu} + \let\\=\relax\def\nw@comma{, } + \def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}% + \def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}} +<<noweb.sty>>= +\def\nw@onechunk#1#2#3{% {name}{label of first definition}{list with \nwixd, \nwixu} + \@ifundefined{r@#2}{}{% + \indent\LA #1~{\nwtagstyle\subpageref{#2}}\RA + \if@nwlongchunks{~\nw@chunkunderlinedefs#3}\fi\par}} +<<noweb.sty>>= +\newenvironment{thenowebchunks}{\vskip3pt + \parskip=\z@\parindent=-10pt \advance\leftskip by 10pt + \advance\rightskip by 0pt plus10pt \@afterindenttrue + \def\\##1{\nw@onechunk##1}}{} +<<noweb.sty>>= +\newif\if@nwlongchunks +\@nwlongchunksfalse +\let\nwopt@longchunks\@nwlongchunkstrue +@ \iffalse +<<man page: [[\noweboptions]]>>= +.TP +.B longchunks +When expanding +.B "\\\\nowebchunks," +show page numbers of definitions and uses of each chunk. +@ \fi + +@ +\subsection{support for hypertext with {\tt hyper.sty}} +Initial support courtesy of Balasubramanian Narasimhan. +<<noweb.sty>>= +\newcommand\nwhyperreference[2]{% + \@ifundefined{hyperreference}% + {#2\global\let\nwhyperreference\@gobble}% + {\hyperreference{#1}{#2}\global\def\nwhyperreference\hyperreference}} +\newcommand\nwblindhyperanchor[1]{% + \@ifundefined{blindhyperanchor}% + {\global\let\nwblindhyperanchor\@gobble}% + {\blindhyperanchor{#1}\global\def\nwblindhyperanchor\blindhyperanchor}} +@ +\subsection{Support for hypertext translation to HTML} +<<noweb.sty>>= +\newcommand\nwanchorto{% + \begingroup\let\do\@makeother\dospecials + \catcode`\{=1 \catcode`\}=2 \nw@anchorto} +\newcommand\nw@anchorto[1]{\def\nw@next{#1}\endgroup\nw@anchortofin} +\newcommand\nw@anchortofin[1]{#1\footnote{See URL \texttt{\nw@next}.}} +\let\nwanchorname\@gobble +@ +\clearpage +\section{The {\tt nwmac} macros for use with plain {\TeX}} + +First we make [[@]] a letter so that we can use `private' macro names. +<<nwmac.tex>>= +\catcode`\@=11 +<<nwmac.tex>>= +% scale cmbx10 instead of using cmbx12 because {\LaTeX} does, so fonts exist +\font\twlbf=cmbx10 scaled \magstep1 +\font\frtbf=cmbx10 scaled \magstep2 +% These fonts don't work with xdvi! + +\advance\hoffset 0.5 true in +\advance\hsize -1.5 true in +\newdimen\textsize +\textsize=\hsize +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +@ %def \textsize \today +<<nwmac.tex>>= +\long\def\ifundefined#1#2#3{% + \expandafter\ifx\csname#1\endcsname\relax + #2% + \else#3% + \fi} + +\ifundefined{myheadline} + {\headline={\hbox to \textsize{\tentt\firstmark\hfil\tenrm\today\hbox + to 4em{\hss\folio}}\hss}} + {\expandafter\headline\expandafter{\myheadline}} + +\ifundefined{myfootline} + {\footline={\hfil}} + {\expandafter\footline\expandafter{\myfootline}} +@ %def \ifundefined +<<nwmac.tex>>= +\def\semifilbreak{\vskip0pt plus1.5in\penalty-200\vskip0pt plus -1.5in} +\raggedbottom +@ %def \semifilbreak +<<nwmac.tex>>= +% +% \chapcenter macro to produce nice centered chapter titles +% +\def\chapcenter{\leftskip=0.5 true in plus 4em minus 0.5 true in + \rightskip=\leftskip + \parfillskip=0pt \spaceskip=.3333em \xspaceskip=.5em + \pretolerance=9999 \tolerance=9999 + \hyphenpenalty=9999 \exhyphenpenalty=9999} +@ %def \chapcenter +<<nwmac.tex>>= +% \startsection{LEVEL}{INDENT}{BEFORESKIP}{AFTERSKIP}{STYLE}{HEADING} +% #1 #2 #3 #4 #5 #6 +% +% LEVEL: depth; e.g. part=0 chapter=1 sectino=2... +% INDENT: indentation of heading from left margin +% BEFORESKIP: skip before header +% AFTERSKIP: skip after header +% STYLE: style of heading; e.g.\bf +% HEADING: heading of the sectino +% +\def\startsection#1#2#3#4#5#6{\par\vskip#3 plus 2in + \penalty-200\vskip 0pt plus -2in + \noindent{\leftskip=#2 \rightskip=0.5true in plus 4em minus 0.5 true in + \hyphenpenalty=9999 \exhyphenpenalty=9999 + #5#6\par}\vskip#4% + {\def\code##1{[[}\def\edoc##1{]]}\message{[#6]}} + \settocparms{#1} + \def\themodtitle{#6} +%%%% {\def\code{\string\code}\def\edoc{\string\edoc}% + \edef\next{\noexpand\write\cont{\tocskip + \tocline{\hskip\tocindent\tocstyle\relax\themodtitle} + {\noexpand\the\pageno}}}\next % write to toc + %} +} +@ %def \startsection +<<nwmac.tex>>= +\def\settocparms#1{ + \count@=#1 + \ifnum\count@<1 + \def\tocskip{\vskip3ptplus1in\penalty-100 + \vskip0ptplus-1in}% + \def\tocstyle{\bf} + \def\tocindent{0pt} + \else + \def\tocskip{} + \def\tocstyle{\rm} + \dimen@=2em \advance\count@ by \m@ne \dimen@=\count@\dimen@ + \edef\tocindent{\the\dimen@} + \fi +} +@ %def \settocparms +<<nwmac.tex>>= +\def\tocline#1#2{\line{{\ignorespaces#1}\leaders\hbox to .5em{.\hfil}\hfil + \hbox to1.5em{\hss#2}}} +@ %def \tocline +<<nwmac.tex>>= +\def\section#1{\par \vskip3ex\noindent {\bf #1}\par\nobreak\vskip1ex\nobreak} +\def\chapter#1{\vfil\eject\startsection{0}{0pt}{6ex}{3ex}{\frtbf\chapcenter}{#1}} +\def\section#1{\startsection{1}{0pt}{4ex}{2ex}{\twlbf}{#1}} +\def\subsection#1{\startsection{2}{0pt}{2ex}{1ex}{\bf}{#1}} +\def\subsubsection#1{\startsection{3}{0pt}{1ex}{0.5ex}{\it}{#1}} +\def\paragraph#1{\startsection{4}{0pt}{1.5ex}{0ex}{\it}{#1}} + +<<kernel>> + +\def\nwfilename#1{\vfil\eject\mark{#1}} + +\def\nwbegindocs#1{\filbreak} +\def\nwenddocs{\par} +\def\nwbegincode#1{\par\nobreak + \begingroup\setupcode\newlines\parindent=0pt\parskip=0pt + \let\oendmoddef=\endmoddef \let\oplusendmoddef=\plusendmoddef + \def\endmoddef{\oendmoddef\par}\def\plusendmoddef{\oplusendmoddef\par}% + \hsize=\codehsize\noindent\bchack} +\def\nwendcode{\endgroup} +{\catcode`\^^M=\active % make CR an active character + \gdef\bchack#1^^M{\relax#1}% +} +@ %def \section \chapter \section \subsection \subsubsection \paragraph +@ %def \nwbegindocs \nwenddocs \nwbegincode \nwendcode \nwfilename +<<nwmac.tex>>= +\edef\contentsfile{\jobname.toc } % file that gets table of contents info +\def\readcontents{\expandafter\input \contentsfile} + +\newwrite\cont +\openout\cont=\contentsfile +\write\cont{\string\catcode`\string\@=11}% a hack to make contents +@ %def \readcontents \cont +<<nwmac.tex>>= + % take stuff in plain.tex +\def\bye{% + \write\cont{}% ensure that the contents file isn't empty + \closeout\cont + \vfil\eject\pageno=-1 % new page causes contents to be really closed + \topofcontents\readcontents\botofcontents + \vfil\eject\end} +\def\topofcontents{\vfil\mark{{\bf Contents}}} +\def\botofcontents{} +@ %def \bye \topofcontents \botofcontents +<<nwmac.tex>>= +\let\em=\it +% used to produce an itemized (bulleted) list in plain {\TeX} +% such lists can be nested +% mostly useful with WEB + +% Usage: +% \itemize +% \item First thing +% \item second thing +% \enditemize + +\newcount\listlevel +\listlevel=0 +\newdimen\itemwidth +\itemwidth=3em + +\def\itemize{\begingroup\advance\listlevel by1 + \def\item{\par\noindent + \raise2pt\llap{$\scriptstyle\bullet$\ }\ignorespaces}% + \def\nameditem##1{\par\noindent + \llap{\rlap{##1}\hskip\itemwidth}\ignorespaces}% + \par\advance\leftskip by\itemwidth\advance\rightskip by0.5\itemwidth} +\def\enditemize{\par\endgroup\noindent\ignorespaces} + +\let\begindocument=\relax +@ %def \listlevel \itemwidth \itemize \enditemize +Finally we make [[@]] `other' again. +<<nwmac.tex>>= +\catcode`\@=12 +@ +\section{Chunks} \nowebchunks +\twocolumn[\section{Index}] +\nowebindex* +@ +\end{document} + + diff --git a/web/noweb/src/tex/support.pdf b/web/noweb/src/tex/support.pdf Binary files differnew file mode 100644 index 0000000000..4d10d9ba20 --- /dev/null +++ b/web/noweb/src/tex/support.pdf diff --git a/web/noweb/src/tex/support.ps b/web/noweb/src/tex/support.ps new file mode 100644 index 0000000000..4d7263eeea --- /dev/null +++ b/web/noweb/src/tex/support.ps @@ -0,0 +1,6156 @@ +%!PS-Adobe-2.0 +%%Creator: dvips(k) 5.994 Copyright 2014 Radical Eye Software +%%Title: support.dvi +%%CreationDate: Wed Dec 2 20:33:50 2015 +%%Pages: 7 +%%PageOrder: Ascend +%%BoundingBox: 0 0 612 792 +%%DocumentFonts: CMR10 CMTT10 CMBX12 CMR7 CMR8 CMSY9 CMTI9 CMSY10 CMMI8 +%%+ CMTT9 CMTI10 CMSL10 CMMI7 CMMI10 CMSY7 CMTT12 +%%DocumentPaperSizes: Letter +%%EndComments +%DVIPSWebPage: (www.radicaleye.com) +%DVIPSCommandLine: dvips -pp 18-24 support +%DVIPSParameters: dpi=600 +%DVIPSSource: TeX output 2015.12.02:2032 +%%BeginProcSet: tex.pro 0 0 +%! +/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S +N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72 +mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0 +0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{ +landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize +mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[ +matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round +exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{ +statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0] +N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin +/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array +/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2 +array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N +df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A +definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get +}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub} +B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr +1 add N}if}B/CharBuilder{save 3 1 roll S A/base get 2 index get S +/BitMaps get S get/Cd X pop/ctr 0 N Cdx 0 Cx Cy Ch sub Cx Cw add Cy +setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx sub Cy .1 sub]{Ci}imagemask +restore}B/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn +/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put +}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{ +bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A +mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{ +SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{ +userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X +1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4 +index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N +/dir 0 def/dyy{/dir 0 def}B/dyt{/dir 1 def}B/dty{/dir 2 def}B/dtt{/dir 3 +def}B/p{dir 2 eq{-90 rotate show 90 rotate}{dir 3 eq{-90 rotate show 90 +rotate}{show}ifelse}ifelse}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 +N/Ry 0 N/V{}B/RV/v{/Ry X/Rx X V}B statusdict begin/product where{pop +false[(Display)(NeXT)(LaserWriter 16/600)]{A length product length le{A +length product exch 0 exch getinterval eq{pop true exit}if}{pop}ifelse} +forall}{false}ifelse end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{ +BDot}imagemask grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat +{BDot}imagemask grestore}}ifelse B/QV{gsave newpath transform round exch +round exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 +rlineto fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B +/M{S p delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M} +B/g{0 M}B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p +-3 w}B/n{p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{ +0 S rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end + +%%EndProcSet +%%BeginProcSet: texps.pro 0 0 +%! +TeXDict begin/rf{findfont dup length 1 add dict begin{1 index/FID ne 2 +index/UniqueID ne and{def}{pop pop}ifelse}forall[1 index 0 6 -1 roll +exec 0 exch 5 -1 roll VResolution Resolution div mul neg 0 0]FontType 0 +ne{/Metrics exch def dict begin Encoding{exch dup type/integertype ne{ +pop pop 1 sub dup 0 le{pop}{[}ifelse}{FontMatrix 0 get div Metrics 0 get +div def}ifelse}forall Metrics/Metrics currentdict end def}{{1 index type +/nametype eq{exit}if exch pop}loop}ifelse[2 index currentdict end +definefont 3 -1 roll makefont/setfont cvx]cvx def}def/ObliqueSlant{dup +sin S cos div neg}B/SlantFont{4 index mul add}def/ExtendFont{3 -1 roll +mul exch}def/ReEncodeFont{CharStrings rcheck{/Encoding false def dup[ +exch{dup CharStrings exch known not{pop/.notdef/Encoding true def}if} +forall Encoding{]exch pop}{cleartomark}ifelse}if/Encoding exch def}def +end + +%%EndProcSet +%%BeginProcSet: special.pro 0 0 +%! +TeXDict begin/SDict 200 dict N SDict begin/@SpecialDefaults{/hs 612 N +/vs 792 N/ho 0 N/vo 0 N/hsc 1 N/vsc 1 N/ang 0 N/CLIP 0 N/rwiSeen false N +/rhiSeen false N/letter{}N/note{}N/a4{}N/legal{}N}B/@scaleunit 100 N +/@hscale{@scaleunit div/hsc X}B/@vscale{@scaleunit div/vsc X}B/@hsize{ +/hs X/CLIP 1 N}B/@vsize{/vs X/CLIP 1 N}B/@clip{/CLIP 2 N}B/@hoffset{/ho +X}B/@voffset{/vo X}B/@angle{/ang X}B/@rwi{10 div/rwi X/rwiSeen true N}B +/@rhi{10 div/rhi X/rhiSeen true N}B/@llx{/llx X}B/@lly{/lly X}B/@urx{ +/urx X}B/@ury{/ury X}B/magscale true def end/@MacSetUp{userdict/md known +{userdict/md get type/dicttype eq{userdict begin md length 10 add md +maxlength ge{/md md dup length 20 add dict copy def}if end md begin +/letter{}N/note{}N/legal{}N/od{txpose 1 0 mtx defaultmatrix dtransform S +atan/pa X newpath clippath mark{transform{itransform moveto}}{transform{ +itransform lineto}}{6 -2 roll transform 6 -2 roll transform 6 -2 roll +transform{itransform 6 2 roll itransform 6 2 roll itransform 6 2 roll +curveto}}{{closepath}}pathforall newpath counttomark array astore/gc xdf +pop ct 39 0 put 10 fz 0 fs 2 F/|______Courier fnt invertflag{PaintBlack} +if}N/txpose{pxs pys scale ppr aload pop por{noflips{pop S neg S TR pop 1 +-1 scale}if xflip yflip and{pop S neg S TR 180 rotate 1 -1 scale ppr 3 +get ppr 1 get neg sub neg ppr 2 get ppr 0 get neg sub neg TR}if xflip +yflip not and{pop S neg S TR pop 180 rotate ppr 3 get ppr 1 get neg sub +neg 0 TR}if yflip xflip not and{ppr 1 get neg ppr 0 get neg TR}if}{ +noflips{TR pop pop 270 rotate 1 -1 scale}if xflip yflip and{TR pop pop +90 rotate 1 -1 scale ppr 3 get ppr 1 get neg sub neg ppr 2 get ppr 0 get +neg sub neg TR}if xflip yflip not and{TR pop pop 90 rotate ppr 3 get ppr +1 get neg sub neg 0 TR}if yflip xflip not and{TR pop pop 270 rotate ppr +2 get ppr 0 get neg sub neg 0 S TR}if}ifelse scaleby96{ppr aload pop 4 +-1 roll add 2 div 3 1 roll add 2 div 2 copy TR .96 dup scale neg S neg S +TR}if}N/cp{pop pop showpage pm restore}N end}if}if}N/normalscale{ +Resolution 72 div VResolution 72 div neg scale magscale{DVImag dup scale +}if 0 setgray}N/psfts{S 65781.76 div N}N/startTexFig{/psf$SavedState +save N userdict maxlength dict begin/magscale true def normalscale +currentpoint TR/psf$ury psfts/psf$urx psfts/psf$lly psfts/psf$llx psfts +/psf$y psfts/psf$x psfts currentpoint/psf$cy X/psf$cx X/psf$sx psf$x +psf$urx psf$llx sub div N/psf$sy psf$y psf$ury psf$lly sub div N psf$sx +psf$sy scale psf$cx psf$sx div psf$llx sub psf$cy psf$sy div psf$ury sub +TR/showpage{}N/erasepage{}N/setpagedevice{pop}N/copypage{}N/p 3 def +@MacSetUp}N/doclip{psf$llx psf$lly psf$urx psf$ury currentpoint 6 2 roll +newpath 4 copy 4 2 roll moveto 6 -1 roll S lineto S lineto S lineto +closepath clip newpath moveto}N/endTexFig{end psf$SavedState restore}N +/@beginspecial{SDict begin/SpecialSave save N gsave normalscale +currentpoint TR @SpecialDefaults count/ocount X/dcount countdictstack N} +N/@setspecial{CLIP 1 eq{newpath 0 0 moveto hs 0 rlineto 0 vs rlineto hs +neg 0 rlineto closepath clip}if ho vo TR hsc vsc scale ang rotate +rwiSeen{rwi urx llx sub div rhiSeen{rhi ury lly sub div}{dup}ifelse +scale llx neg lly neg TR}{rhiSeen{rhi ury lly sub div dup scale llx neg +lly neg TR}if}ifelse CLIP 2 eq{newpath llx lly moveto urx lly lineto urx +ury lineto llx ury lineto closepath clip}if/showpage{}N/erasepage{}N +/setpagedevice{pop}N/copypage{}N newpath}N/@endspecial{count ocount sub{ +pop}repeat countdictstack dcount sub{end}repeat grestore SpecialSave +restore end}N/@defspecial{SDict begin}N/@fedspecial{end}B/li{lineto}B +/rl{rlineto}B/rc{rcurveto}B/np{/SaveX currentpoint/SaveY X N 1 +setlinecap newpath}N/st{stroke SaveX SaveY moveto}N/fil{fill SaveX SaveY +moveto}N/ellipse{/endangle X/startangle X/yrad X/xrad X/savematrix +matrix currentmatrix N TR xrad yrad scale 0 0 1 startangle endangle arc +savematrix setmatrix}N end + +%%EndProcSet +%%BeginFont: CMSY7 +%!PS-AdobeFont-1.0: CMSY7 003.002 +%%Title: CMSY7 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMSY7. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMSY7 known{/CMSY7 findfont dup/UniqueID known{dup +/UniqueID get 5096648 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMSY7 def +/FontBBox {-15 -951 1251 782 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMSY7.) readonly def +/FullName (CMSY7) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 0 /minus put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CD06DFE1BE899059C588357426D7A0 +7B684C079A47D271426064AD18CB9750D8A986D1D67C1B2AEEF8CE785CC19C81 +DE96489F740045C5E342F02DA1C9F9F3C167651E646F1A67CF379789E311EF91 +511D0F605B045B279357D6FC8537C233E7AEE6A4FDBE73E75A39EB206D20A6F6 +1021961B748D419EBEEB028B592124E174CA595C108E12725B9875544955CFFD +028B698EF742BC8C19F979E35B8E99CADDDDC89CC6C59733F2A24BC3AF36AD86 +1319147A4A219ECB92D0D9F6228B51A97C29547000FCC8A4D9DAFF1B3EA76067 +C5493B69F73B89C8B61804A34FCEC826343337CCDFFCE17BF343EA8034BF95AA +14C56862C2C052569AFB236E1F1795F05150C8F28DFEF6BF4BCBACB678D00036 +30EE84FEB44B1A8438185EB45654E6853C1159B073E54292D135F0961A64E8A5 +AAE49C4BA9C44156C123426212120F99F3E8B7425752A5FE384AAEF755A8464B +51F015F9E2967477D57B22627D75CEF8AAAF0AEBD504EB46D0289DFC8D86C972 +F042BD88A90A53613DD93D8A7A8460E63D85F6C15C000C0AAEE4BD5130B6E668 +8C9B3F3FFD804745DA1D5EC0AB85C96E1724FA67F9324C59275415182AB48D57 +9722DCF602396AD4B5C075A5A89A5D005C9FE11273E5FBDDD1800F11BBDF6AEC +6711C5633A73AC5DF038BA521AC492E138F7FFC7C5438FFD32FEAA1128C66E83 +0D3AA40665F05E62D7EF00B1B0596162C402A34B6BAE6300D43F3DFCC84860F5 +C0F0F1CE28FC60642BBFE9BC9102E80146774CDC88F9C250DE762D24A3484BCD +1D26B6D9FE981CA5AAB2A4BEDC528115043DC18D7105735D7528C2C5DD89A812 +75B5D7B2E5A586FBB0C061E708F92C1552F64A296490BD0F20243986A4707FF9 +8AB3C917B8DB92F19DCA6B9D4A1DB57515E51DD85D5C9D2CAF7A036AA3F9E9B1 +5B5E099CC05A9126AB274C17D75CB4FAF78052366D2F21EDAADF84B22A2D645A +3E65C4BC0F540B5D9609D88DD0E4CBEEF87C16447D43A5F98528FD45ADD10DE6 +41AEC411FD6929308F0E4F48A8D9C9EE386E920D41C1CC98A52073011DF5BD28 +5683F280B5CF7F27DC50930C81D344FF5A8A9258A207D2531AC21A735B14155B +C22C752DD22AA33C52D6D4D053B3E46FD4C9129068DFF52695A3A9184D04E8EC +93696A3FEC3AEB3814D9015EC14C22EC3ABD5070E8C28A3B42F5596D948212B4 +AFB9978A0A361135C9E18CBDC98E0D1E8BDC17E25DDB3D52E86127E5AAECC55D +FEE61693190E378978EF1BBD4D1AF005D511C7607CCFA4BCBD3EC427CAD82809 +B725B25AE8A03EE88F80A7732A571A2317E0B6A0D072EE8CE2EB9E033CDCC899 +B64CF4FA1C708A885442062F08D3D8DAF44C066EE278714D1486EB709D327865 +A483F62709E89D08291F044325208EBA758DD459481334F5D9AE3BB61B3020F2 +A4538CFC2C94BE84C920BE80806FDCEE394230730E049333A7E16509207514FD +695B5E0AEA9E4A9737311AA0B33B15F6769FF865D1ACB63DC6201C3F1062A3FD +1B446C1857460745917A36289DD57C94FE6240F4A40FBDFC10E91B91B79029D9 +9F1B9C74E8E5AA011A0ECBEC660230AD5929F01D0325D15FDC0040406F124021 +02AE176F4C98BAC1706F03C2B5B40F325A50CA4683B2BB4605E68E72D0CBDC2D +96B3BBCDD01201B650A7E7744D58D1E36D81FBF72E0A875FF29B4C109A1950FC +9621B18D58806392EEE9841794DFD39E3C4E20D45384FE07F9D445F143B922D1 +AB350AA6DFC51FCF767B141A392D6A8B633AACBCEC9F56A0CF40AB08020EE63E +08CC0BE01B40E86388A65F5869F2F4D022DD4B912031CB8CEDEDFC2473772569 +5B28F66AB74CD7902A0061AA3547D13C7F0C6EEEA7B0BD316694A94E4D672520 +EA044AB28D8D01076C486CE456EDA1811F7ACA75D27473080D27D3E681E35FC6 +447046120C6CC4C17674F0F051570A79DCA74848F3F300B58B19018430D99858 +CA5504084D6BB74CFDB635B6866974A9AF05DF201C69352B2663B0623E7828B9 +5EC5FFA8D8F10A7C28000F8C679B180067D5481D6315BF1C4194EB171C8F3CE2 +4CE319975B9E948D907F9F7EEAF07089844391555F329E331D52FF114668B8A4 +80704B3C6AC0CCAA2F5D043CE44E65EDA89A0CA854CFDCB11D549B7FA72EDB90 +D35353C34A771B1FAF96F83FCA5258AAB65384BAFFCE448690C1432A1F749C20 +5817205185F973FA098BA856584753E75EBEBF387FC155202885F5B67117DD7E +70D1CD887183C5573B6FB607D4F6CC9F8B94B09B3F3AEC2EF1E6A320CF6D0112 +63046321941D1FB3F2140B59370AA9387E24D579D389A166A10C989497FE9549 +34E1AC2E546CC06C5308460DBEF3E1AEEB6CBB0FFDAC458E61DE3391480CF5CD +34A647D4DE15B81131B7D1F9EED4C6837A32E89B0EAAD6A05F5F67518655E5DB +224D4833CEC60D5DBBDB8A03FB1A9730589BB4F0FF56191D17E73B9562E0C356 +B188882B36F9505F6F42EB2644FEE125C2A7D12227ABC8ADB924E88B0A9E8DC2 +79762523B0B88DBBE6AC7968A46BD9E9F0C3F03F5F64724CA07782195F01F130 +30DBE895C212E0EE20162D863F46A674D85232FA0DEE69A8DF019794AF6873AD +9CC2A5EEEF9393313CA519BF95C08ADF7A75B6F53EDCDC39851D20E58B97CA57 +A7523717AA1821DEA94C8A9F8B82346B16D92D15AEDC16F0011A45A44B09DE47 +08CBA46E8511D0C5CC83F952EEFA4ACFA7F3D7FA5E113EF6B70E5ABA6F1AD3B1 +E4D3B15AC6D5C3BC70A3946F411A7D965D6FA9D7B6C6ECE19B2C29A2FF476251 +EBF0CF3BF658A1D896323706172746F58B2DE49F8B7E431E20304A42694CCF73 +11C4E9E96260CC442E2938A1E27EE6744C7CAB01634C8210CE40488B9CBD757C +4277B5E3E43C7560291D945F9128AF1F85924003418F96458ADDC5BB8EC431D5 +AC9093D20DEA69B92454613BC1A82DAD4FBF8E56084494D9D2FFABD82A7C9847 +171FE36B265B546F3072B0923840E6C6BB12CA53E05A99F0E8FD4F5109782746 +7CAB9B35B68050230736AE624B7862D1244C7D9BE4D1CAAE21B123D1E8372377 +F1FEF269A9A2EDF02CE0CC8BF92FD7EF09556987B8A3BF6D8C0A663DB6B9742B +E9AC61A449106AF1EA7ACAD40AC6F59427CC51865E6A90CF2AEED8D6037BA70E +4ADDAF622CDE877C98C3B2006B4721FC9BA18E30F0752BD4ACE36221F5CD1497 +8FEDA5D643BE2EE007970A68E53D85975116E6CC09F0039A09EBAF0CA4B0EED6 +A485CC0B69E526033FD1C1190BC5686739CE13D1AE8EBCABC01FCFF26141867C +44ED291196E546369129B9F759FDD7DC21BAF0A528FC34BA9FA8937813953644 +C539F9DA4E55E83DB3D6DA309C562DA1330B157957B18F7618544AB738E25F16 +F0517CD13C1F11BB8EA056BDC575D77CDC526EF497639DD89C2098660C5C45B2 +D7CF715AC5E76847E0D3178360DAC1BAF6ACAEE72453B845B9F86621C166857B +029CEF5AFE29D1EDB4CA3AD7D008B7550A779E0066D7312DD6C7AFE1C0BFFF25 +7B062B0DF30032EA2A2FE3CC46C96A3A0BA1888D1D2B05424A59ABE3EE928ED8 +B67F507EFA78AE128F58B54634C7F534B3D0F4AFC23E38FB56EB39CFA425FD37 +848545EAD03EDC5A9E796CEFB345F527615C785963F536972EBD9CFC4A6A4A07 +5A31A508CA147FBB762ADD198CE36DF86730FCE2B643D1E7DF0BDE800DE7AF89 +44A36B04193E44231E08919EE91A8B559646DC4DFAFF0AD891890A0A88FFA8EF +B066BCB7AFCA409C51889E7FEB33F19A3CB1268BD0EA74AF29C1401BABD16F87 +ACF7DD65A8513DA9995C5092C36A774BC4260113360D29AF7ADF5D22B5B58E7E +A9BEFC33B9A91D2C397B27A81087376CB623318A8362C3FA9CBE3026675723C2 +E711910DDB328E0EE3FCE219F44FE528B70E58B8E6CDB4AAB48237DD933D9639 +E9D4F9EADAA8D46537D964D75C27F210B0C2473CB60D65F61BBD91ADE01576BF +77C49E31936138B0FBA066BE910DE1B1F0E4FFB5E81038E8656ABFF08DFD923E +6BA2AFEDCE6998BBF7045393C34811501586A4846E5B942C8E99D4C481D3AE60 +2796ADBB5242D59F1116EBB828014BD903EF58B223DFD18BFBAAE4D348876B06 +CAC10B7AF0DC270E6702A3F75D4DCEF872F2CDB9470AC9A1DC1ABCB55636D26F +9CB6BF27A0DAEB1F62AFEC12F55F78C9B59AC6DA9DB4B45444B0C582DB4DB8A4 +B31EF4AFB77988E92FC0B257374B4408406490D9AFCC495316D6C08BEC9A76C7 +12371E14417711EF802FB7151B3F6A2580C97527C9C3A0FAAB8D62FD992AA18F +EBEB36F7910186CD5F70A55DFE932757C299D9D2289796769A00A0C6ABD18F82 +E0D4E95D6477E67B4C012DBBD098FE20E2F15C412DD2AD5471A65EACE05B3A1A +0C9C430BAEF4887F2CCE668116B87FFD9DAB4B9B3605CD26E6B12488058AFA30 +8843791A95BB322DF5C47387F3EC72343855D6B23D72144EB5EB5157B8B238FD +6C71DDDA64C9539F66A7DD569FFF43DBE4A8F0608A3CBD354DD9BAB5E3C756DD +92C3C1B3E169D86A2230299432488BC04A87E08A80809F9968676DF9157B1C91 +27C664ABCCBA9997FAD8966F766B325086899D1FE44581FE07C97688B3E15B0C +234A22646C32BB965B9BFD2CD34854D1488AAF021E169BF9CA9665CF040E25A8 +16156C80A2F47397CD370AAEDA731E0D14FBEE1E51A17DB972D96DBCCE33F937 +5CDBF1A650BF1D3536BA4CB7A1CACFD5CB457E2368A660A62AC26E64A631B2BA +6B08EBE42E02D9B1B2E95BF9F0A6B59C96A122968FD46A4D17BA3D018CCBA0F9 +80BA3C1E6C683111AFF79303CF64F1D2CCBD7571C6E09DD9B27B8E101BE219F0 +E075880A0E367885AC94143E777DAE455B990383100EADF786300602C2CE28F2 +4F44662FDF03BD39A5181912D8F1243C36FF88882CFC4B34C1D4EBBC01D96A7D +9CE5303042D1B21042E4FEAA455F22A01333FCAD7E4AACA5D3A5386331985F6B +9B247EC6310BB07507321BEF3E4ECFC3B915AAA6E029B3999644C987640863B0 +5DCF58CE479497AFAD1208FEFD1796E74467E9F7867C313A3412E6923F4C9144 +C69EFA17965056DF043DB465BF2F1E191706D3AAB47E6AD5C9767E4A73B29F2D +E2E579D0262237568F82B360ADB6D0219B7535EFD02DD0688CDD23D84FC4F308 +5D2D0010B1A9F4F0321A00C154672D21708B66B91ADCF98BAC7A2F94848E9A4E +86CC82EDD0399BD9F13E43359E71F80086B9B0C3B6D08831D4479ED83E7892C4 +90C477BD1F06DFEBBF60F26516EECDEFE4787EEA8683754F2B257D0BAA607DBA +35EC6D1618C2FDF3881827F92D793ECF152D761F2423A96210F582DC9B90120F +26A33025414716A5E6F56D712E31BABE5047EC4855B767AC63D793995C9E074B +6E35C7E5255FBF4C3F17E7AD7B2A6C5F7459794FC94306B581536910F244BF5A +3158E821CE75F4B0565EBE985DF24DAA92F9C1D848EEC6B88E21FB6C51125872 +1752F7352291960E5BD36F78AABBCF6DAA4D07AF56E4B6058AAB13D41BCDAA14 +C0D63C6807FCD0E2B4B9CC892F224843173A75DC53A8F0FA396959C2E2CFE3F5 +9B1C8B62797F34E7A0BFCF0787C73FEF98442234A617CF161829498035D30B29 +ADFEAABD0B496E8A2E764D22DB7737F950FC5982F1C5F4FD414C1B0202F40FBA +62C81B8F0E836CD73D79366FD62388B437B81FC673442EE34BF27454F72A08F3 +389E60CE28A050601A42FB4491C60DC02EC008E6B9DD2495522BBEC7293E2923 +120584E88412DA7137397B41A28706B1CC6BB0C80709A2A4BA79822D245757A4 +3EE454198942ED2316FAEB981F7615E642167620EBDDC5B271E273216EB119C6 +4F2F0412F0BA6E3BA396217597575C6739194E1F839232FF088FDDFD3695A5CB +9A0E220389938596D8BDB183138E1F73F64512E4FAB5E1328F9B42364E3113B8 +004BE2CA0B074EE271BBE0260D31CE555D535C16EBB528747EBAFFF253E659DA +3A377CBE0B296276AACF0294CF90FDAADB4EAD5E2F600E5B2A018DEFB86FF61C +84296480A425687CCE37D671472537E897AFD4B8C6A6175E1ADDF9AD24DFC5C3 +A73E18AC2D9B28BDA2F17D51DB3521945850DAF0EE48B0FAC271544C1B4F3B2D +53BFC8DE32BA366FB1FEC0DD6C0B1FEA374CBE2B96F5B235A1D83A240DB442C7 +1460980A3E5B96AE3D5784DE2C2DFFA671E0A856DB2FF4130E5905F3D5338856 +C11A468D867D0C6EC585F1AD3E7164B8598BB59973B9A952FAE819F052A6554D +EDC342BCCB0525905D1D27ECB9EE43847B69AE116F494CB2DBBAFB2773F1A3E1 +C75FBDF8D66FA5AB4005757D631A0D9424FCDA91A1D2AC6FCE7CC7A23E84C65B +3E92BC684F23467DCF8521E0E27CF1441C487EC6E3BCA0AB54BB137E83776009 +833D772FD225E88A8BD992FD69819B3BA90BAAD1DDF16E4326190CC4BF9C30F2 +AF7CA1FB38E6387D9745FC5E176B248B1581BF7A4CA2FCA8E423DF340EAE29AA +7E07A25FF838F67378F9A6A9A0B404E01E86E64FEF71DD3D540D4711AEB1974D +E2E0D485DAFFC74BA6B8E9AFDA245BC8997BB39BB6BD52B496A09C68F7A8E900 +8DB3007643416040FCEC85B407EA0A946827771FBBEE49A3DA5542CC5173A31A +0280AB8E922C23C1BDD88D70627EF124633C318E7C9ACBC14AE216BFD41C0B6B +3A0161757913CA1F7B6626963C09936A52E73DD9B3D86DEEE73C0293A646FCF1 +21D4C33DFF1671DA7A53E77E20233EDE51571549AACB7968602CD03EE67ACACA +B231661CA9DA2BEC5795A83DFAF675E9B052C8BDD51490F7874C91EF5ED2E0A6 +BE9CBABB98A950F7E55DDA3823036437C11F614E27DA5BB8BC6D955FFE54B825 +0201275C2C49A3908BEF1DB3D87792DDFFED23DE7FD9CFC284F6255C77E54A39 +C2FDBCD28F2938E4CC135829AC1867CAA5705674062C9639FEEFBE49D6108091 +7C58585B80464F7E69966D7933C7019BF336B88B9E0E7073A85EAF297B71B303 +31EEE9121347A482D28CCE942AF53E94F88A97EF2F1860A92CE29A14495D67B7 +D37E207D42F3891E0423F5BCFFCAAC057FEC683696ED6FEEFA65C8FB6F1312C5 +24A1130192B4179F3B08DA1C951D988894E7FE7CFC28C56992A1CA82BF8BDBDA +E021F16E630FF67201BA4DF5F3F4D6AA65B8347FC1575C142C6C1868E8472BD2 +CF191137AE1B36F32FD84DCAD50644AD55EBA2694C93BDF984A5C9E7C92B73A0 +26769F00831537266FD2E711AB3F8AFC5F3FDA3C9E6439FFC48C3D1B5527FC56 +1FEDE991E66E8465C0E395EAD0A22A2FDC001E449AB9C5E0EF187A1DE9B74696 +BEB6A525DBF3A60DA2FBF1579150DEE1C5D1B6F55FF2708CE23289803CE123BD +C81E25DB96551A13AD713D5C7BFDD3F2E1D5C12463A195442B51909CC1724E50 +A1F6F4EADB3B7355908F36F88521F333C4E7C70B094209D1F883B961DFAC32BC +8C5A2CAF77CA5E6AAB714CC0AF2B42FFF6F73301FC71AFFA9B33A2153F55C2DB +C1C111874DEC37CB746BEC9A3A9A37A2DD098CE7C66B0FE38460ACD77A47D53C +1550F857FFB733B5A8D02FB56790A09190B29CCB4F4A3058B1C82F0CC5E1B2EB +2F8E06F2DE531E1EB81326A8EF0F82843A4AC59D267EEE45730895752820BA93 +A129C22A78C1AB28BCF67AD5DF372FECC9EE6719A02E499FD5CA866688E86089 +7EE8E5912087E0C4588DE38428114785E0CFEDB1E2EE24CC067D107DFDF1E2BD +B1C4F9C6B740F3DEA0BD315581004E851ED5A9F66C4F9E95DE97D355DB06F482 +A43B565F1255A85710B15A281E2F034B1C23FEE6CDF3A043780CB6AB18A016F1 +9EAFE545CA5A5B5AAE2459D69D2151E99D029FB5C1649B9DA784BFDF7D177385 +4D8B16B9922D149FFF6B4F99311D52BEC9A9FC098E7192180DBB38767DA9B9C6 +E8CFC98615219EF3AD4A8157D14C72BA3F91C8B78381383E0BCA1A5319749E08 +D67D1208C693A6640D1BD6B9285AC0D3110CBF8F7747AD12585141C3248D0BB5 +BDE892F91A4B3291F21F30294693518E4629A3ADD7C8640E424FF615602C603E +1E14DCB3B17D34C090BEAD2A523E260A599522329B698729A635CFB15BE7E79E +6A34DAC7C3ED57340821A4E7A0C7F88F64BAA69BB80DFA8B659F4756878E3044 +E1DB7EE4FE60074AF97818B3D36CF51110191B4E10655F52CC5D11F56517CFF9 +04BA2676F4147DC8192C98C555B001D54301853030B2CF0C068ABE831158B1A3 +F30593ABD51F5996CE493E0622323FE93B292844521051C1D3799C07DC879EE7 +9D047DE1C7127E2DC6F94E30C34FF87FA03DB3A3D597697C133CFC5A9448E067 +51777DCFEC80352A92D9DEC0A2F40580A0242C063CD1086D38FF5C6FA1BDB0A5 +3EDB5499AA2322CC73A38FC826DC202679AF4A7B571509B715CB8BD1D0C5D1E5 +9F5B2D6DF5E9 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMMI10 +%!PS-AdobeFont-1.0: CMMI10 003.002 +%%Title: CMMI10 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMMI10. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMMI10 known{/CMMI10 findfont dup/UniqueID known{dup +/UniqueID get 5087385 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMMI10 def +/FontBBox {-32 -250 1048 750 }readonly def +/PaintType 0 def +/FontInfo 10 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMMI10.) readonly def +/FullName (CMMI10) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +/ascent 750 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 66 /B put +dup 107 /k put +dup 110 /n put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3C05EF98F858322DCEA45E0874C5 +45D25FE192539D9CDA4BAA46D9C431465E6ABF4E4271F89EDED7F37BE4B31FB4 +7934F62D1F46E8671F6290D6FFF601D4937BF71C22D60FB800A15796421E3AA7 +72C500501D8B10C0093F6467C553250F7C27B2C3D893772614A846374A85BC4E +BEC0B0A89C4C161C3956ECE25274B962C854E535F418279FE26D8F83E38C5C89 +974E9A224B3CBEF90A9277AF10E0C7CAC8DC11C41DC18B814A7682E5F0248674 +11453BC81C443407AF41AF8A831A85A700CFC65E2181BCBFBC7878DFBD546AC2 +1EF6CC527FEEA044B7C8E686367E920F575AD585387358FFF41BCB212922791C +7B0BD3BED7C6D8F3D9D52D0F181CD4D164E75851D04F64309D810A0DEA1E257B +0D7633CEFE93FEF9D2FB7901453A46F8ACA007358D904E0189AE7B7221545085 +EDD3D5A3CEACD6023861F13C8A345A68115425E94B8FDCCEC1255454EC3E7A37 +404F6C00A3BCCF851B929D4FE66B6D8FD1C0C80130541609759F18EF07BCD133 +78CBC4A0D8A796A2574260C6A952CA73D9EB5C28356F5C90D1A59DC788762BFF +A1B6F0614958D09751C0DB2309406F6B4489125B31C5DD365B2F140CB5E42CEE +88BE11C7176E6BBC90D24E40956279FBDC9D89A6C4A1F4D27EC57F496602FBC4 +C854143903A53EF1188D117C49F8B6F2498B4698C25F2C5E8D8BD833206F88FC +BD5B495EB993A26B6055BD0BBA2B3DDFD462C39E022D4A1760C845EA448DED88 +98C44BAAB85CD0423E00154C4741240EB3A2290B67144A4C80C88BE3D59AD760 +E553DAC4E8BA00B06398B1D0DFE96FB89449D4AE18CE8B27AFE75D2B84EFDB44 +143FD887F8FB364D000651912E40B0BAEDDA5AD57A3BC0E411E1AD908C77DCE3 +981985F98E258A9BB3A1B845FC4A21BCC54559E51BC0E6C22F0C38540F8C9490 +88A0E23EA504FA79F8960CC9D58611C519D3ACDC63FB2FBCAE6674357D7F2285 +4BCC9F54D3DA421D744D3A341DA3B494BB526C0734E1A8FC71501745399F7683 +FD17EC3044419A88C3979FD2ABA5B0130907B145A8462AAF0A9B511D2C8A7C7F +347FF6AC057E6512902BFD2918E2CD31DE615F5D643764E900B60287670AE18F +FDE15545D8BC69591A8CBBB275AFFC9B14BD68DF0AAB32268FB84844D4DBC7BB +C591C1AC5102C50A9C7BAAA848DA88B0519F0F5F0813BF055CF0E3C86F633A04 +B779D2E8E656DB1E09A66A85FE21CA8BA5523F472A229E83F2C4E91ABA46C733 +F3C7B5775B06C97782BC225C46385BEBDC61572458EFC5CF4190AB7A9C1C92DA +29F84BAACF552089195966E3AD9E57CC914D20B6962BE80429A16D4DF1ECAA66 +36C4343FADF0B2B48F12E2EB8443C4AA29D00949255F3968617F98B8ABD4CC12 +048B838EE243A21AC808BD295195E4AE9027005F52258BFCA915C8D9AED9A2C0 +80814F79CF943FBE3594C530A22A92E11BE80FCEC1684C4F56712D5846B0749C +9B54A979B315222F209DEE72583B03093EC38F7C5B9F9BCB21DBE8EDDAE9BE8B +75ACE6B12A31083AC8348EC84D1D29D2297A266284B7E9734E207DAF59A25F4E +4AA38509E993C5394FED76E6A2F25462685C4C86C6E8CFC9863338EC1428BDFC +74616BB1BC8948B0ED4C87C15B4405F3A7796F9DB3798FFFE8BD0A94E834817B +D5E9812E308D0CC920470A6F2CD088FCB80462BF7CB3F039A7DF3DAF5B2B5355 +E083A385CD2EAF0FC181E40E96DD7E9AB9EF5C7E6866A13B8A54718E950FE097 +EF0951A357114F18CE9933D28B3A77AA71E3CE884661F13284BCED5D5FD1A86D +543E588FF473DC2CF9A4DC312500135F29C2D0174B32018C8DBD40EF9A232883 +710A1F2AB2CD11312300ACDF789A9B7B93D2035D81D1C84984D92D78A53A00C6 +EDA94B24BBAC1AD17774A4E07E6F74ABD90415965616AD540C8ECD8C3A44EE4F +7F4F6BB6238C5062D63FA59B7BF08BE93FAEA70A2AB08FBEAAF7DBF56B95FD93 +03CA406543BA6C9527D0DF01F5108D31A51778A5EB1C93F27B72B46146A353A2 +01CACBC829603B9989A87CF64528682CCBA0562A8165B185C58A5C6BB72F5E89 +500ACCAAB8ECEFBB2640E99EAEEC4EA979AA793D013D61D8ACF8784FF8D9398F +F6A252A709324FB39509F0B3A4E725E82F53543383C6765BE556CC897C758208 +AA3AD37B0406E4A79F8F0A6C1983FC73E71CD858C0DB66ED66D5D992978614EE +1EA91EBE191E082EBA1FC040AF19A2202575C2EBEB8058833E3520FA03D2F915 +85C1ED337E457B9FEEB0C6EF2735EFDA6E0D05FA641BCF698AC6B97751E8306C +4DF00A39B8581FF53DB8F8525FDB196D85950906CCB59B8EF171349AA3B567B1 +6A00819947A995FB383C3C1709C9A2C113B2E40BB832B7D4A0FBA0B16A2C455F +55809CC425C403E9668DC66BE45B71A81C332FD4DB279D22A2959962304A8F18 +085893DAC61317D24A8F198FDAB95F3B86F0AFD35047B868A9A17037A2829A02 +BAB042F75F349E197A7EED41984C2859754CAFD0251439921C248B463B516951 +2E1322C80D73F9CBCAA63A585450275AC2492E4D3FB78E800F788254DB5E610D +CF788DF5C70FF99892BCDF16133E34B24B77C8F097F546B87C603DDB8998B66E +BACB68BA27462AF54AA405682EC96D701F0D474DECD5F95CA2102DF639EB169E +D518162C2BAE45FF698B6DE15FC6E7DE48C336C40A670FD26952A6BAB09115E1 +991F0073419F2CC2A1C08BE91096936AA0C37E4ED3CCCEE235476074B8FF1125 +6BDE3701F85532D8BB64CCC927CC335281C95EA689706F0AC717DC2CF680C754 +E5EFD7FA4BB8880B2B727A964C876D4A223069D4E6001771F0E23EAD2A4BBC80 +E76675297B2EF05F52BF4E71B3EE2BE3048CF088C79540113C66AE98B2FD3CB1 +B0741A215FD070882C52765009D7D711DAA2508F19AE7DDA15229A856AC49BC3 +4DDF40814FF96500E4B9B02D412E94623C5FDCC76C0FB8E42DF56A904FE49D65 +1DA7C53901B2EA71AB658A464D3ABDE27D9DB8D9E0B48F64E61A2495AD5D8DAB +B5E72424AD017DF37964AF911BD7FA21A5EB4775DC8E95EF0C0EB856B00D89D7 +8172A1DE8530767D317B8256103E53CFB877E10686A04F5A08F8DC58D843DEBA +FD5F40597588663D103689F6EB3EB14D06E18C8078F2538B43E712DF491FC5C6 +AF639256C8C6134B64D560D8476DEA6329D995E46CC4BC78841C59E73648B47E +BFA7DE0846422F738454AE77E822A083405289247BD7C478BE4974F742CD6051 +E99FBB1D1B3FBABFEE855174734EE45E87D0AADF32B1283B911162A9955847FD +38944D70584FAA6B1A7191C5C134B73F98EB632B69E2F0C0F94156787C34C8A3 +7622A029D58F9626B74F8A8A1F3803E0BC20E0EADEB1E99B70F1BD9F980FB751 +2A842843DE42EB142A84D5D3138629AE9EAF6F3479C423E8829C8816FA6EFA27 +DCE5580E65AA9854B1C64163DC318420CD993C15BFD76A8BA1182860A6B03D6D +22B8CF43CFE6C8AB27C64842E239CAE707D3086BADDE1D7C94E3BC96319470D6 +8D26915C575CFDD03271D6BB9DE86A0EB6EEA6E768B224A626C62A9AB48A6EDB +44F70BB5AF991CDF9736D65933E81CC57A78F623F33EC9AF535F2F25FA4EEC90 +D50DB7E87F31E971A75A33A301CA6013EEC5A4E179D695B33DADF2C98364434A +42926776000B610E17524162253F6FA638D6581C18F99EA0BD1D2E24D2424ADF +C05010D08192485153DD03930C7BF45237593E484F9851E6D464FA10FECA5D9E +0C8CCC97DE029030900CDBB491C5CF226DBF903CFE7735D939C3FDF3A20B70CE +66579B28B99313FEE914E295388C7BC8E055A2E54EA3A8206D3C8F4F7C0BA5E6 +E519419FD8CE215F7B8E9BEC604A9E3FE272A0328A24E31997C8A91E0946BCF1 +6943A97CBED2AB9FC636B49828BBB8B89E0BBC2653796431224895ABA5DAC41E +1854BD9764E86147FD7624F736F40DE3B7582EDDFD15C2BDE3F22B5A54D7DF10 +B87A1301CE85CFC061689A890A321412A13314AE96DCD3EDA75035FDD8F4AB9B +897A2C68263A68457032C469987970648BA2D88B1C5375DFEAA35A917B8A952E +EE670427942AEDB3CB599C5746180E392837D371E15D860620ABDB6AA7772C40 +A5E346661673ACA530BE3D8E3FFB895E5DA3DC23B1B43C080C77F7E47847F0F3 +F3AA5CA9E4BF75FC5EBD18D19F21A7DAA3B11CABC6E4070A15F7DBC8B05EB6AA +A02EF1B078EB66D61D6AFE41DA9B36FE7EC9EF94D1EA26282A9871E2CACB3126 +2AD49C2D9B50A6E47D8F2CCAD50992D1B430979A45FD9E76182A19964BB2A1F6 +51779A2B258DC1DF4C2F3074621286831F3848AC152DDD2BA561E6586ADA88D3 +598A2CE2CD048F027CE0008B828BD915887D7785341E8305DF2346ADB76BE99F +87B02173BDC334E9221C8DF54114A6B24C1C5340299512FA6C8C51AB4C8778CE +178CEF531C6D1B5FF0A1BE8EFF767F959BD4C345C52699A29A17B2A230842BF6 +4B011217D6D24EDAC3F6D53482786F1CA33169B90ECD499407D37CE9B70DDF78 +7B7547B32952535BA9ACD1E244447AE3FCED3AF28717083CF9590A09780984D6 +AF0743C82AE4FB3E2BB2856A4153A3967A023FFC35382D6C22D84A924900B6A6 +3DDD400E6D2418DA6C27F2FA34C075C902B89EBAE658B3C9A18EEE449DA5A379 +337DE95CB7AB3F0970CF1A5D8FAD8090E495570FDFB2FBBA79244780D8035547 +C5A55BB21A2270F724BF5D442CDC5BB9F09BE0CAE59B1C2270F0BDACE698F2C5 +DE8F66BFB9634904B161F5BA2B1950048300D69BABD312D58D89C4ED527AF7BA +7DA2478EDC2CDEE3473DD8A8ED9D891CD1FC21F23013228BB3281B71FCE959BD +6F8E9059D682A7FCC5265A0620992D4FA8D78377EB34CE3ECA070EE3707239BC +98907DB0120CE42ABA32CF97127E28382BDDFD685674279F588D4F951216C355 +821361790F64C2CC720DE97E8ECB57326C43EE47367628E05769E106868B54F4 +C33C9951908DF6FC4F5ED2C7787BD8FA591BBB3E9C6C1DA94CC5E38D9B20C886 +7D237572FF46DD896A4D6163408EA6CEFAC398EE041EAE29D577E75326CA17A6 +B072D47A7B13EC441CE6DAA042ECD02134CBFA6809A435050413817193DAEB16 +A5882C8AEA44BCF36E74E9ECCDFE7E19FF5A5DD7A94E5AB4F8702C3DA7F42325 +23C808670A0490F5B373DADE40814FF9650241D3D69C91FBC5ECE728F827D9BF +C928602E05477903449E079164CA39859C4BCA60C579F490AA455F82B5050BB3 +969AFB478E0D4A257B3356EA3CD62051FCE6C6B1929CFF85BFDF166BEF658E10 +3A55E007F38EBBB248B3F0B8ED1925106B499B762E45113AE1AC9DE09644C84B +9C08034B297314EE69BC32DB6E7D7FB9913CE5AC17E7335979E9DCCE2BAB3725 +1976155551F9706A576FE0E3ADCCF72C87683291528ECB749CB0ED291966E239 +B5E3630676BD409E08F85BC1AEC9A2D4135376284A96EA24431243BD6FE8B966 +95F11A4BB53F392E0AEFEA623064FF8A7002367B0A515635CB2D2DDFB9B4A8D7 +FE721754E81BBA548848A235B91AD4E4F7DB19CCE2F61D277FC00AB956EB93BE +44AB4970CA56BF59506C94ED160FB1E25D3DF2988A532BDB787BFB8539D22986 +FDC378AC31444E63C4727FEE121A43751043849E6DCAC5B59D0FC703AAFBBFD4 +E8B7C268F21615AD02CE9DABEFA27B5FE6A6441B619539CAB1F810F1263447AA +633F5DAF483752EF1A0421740E3A811D2D2898CBF53E7F686C9223FD7235F02D +6F90D2D48CC20AB87778DE3C6FB335E0F0EC20B5DC5B65223FE117526DE2C72F +FE839DF93CB2A7D66CD900CB325F891E311BEC932F703FB4FEFA29DB8B9C88DD +375EC71B3D58C7BC59ADA91971A3BDA1ADEA629CE6CC92BD542CDDFAA7706FB2 +6CDDE2DF07E56D6741916AE8E8744339816F3E6C38062747AA9FDA2A2678A6B7 +EFEA870AA3A4D71B25EE3013EAB1DBA34401B867C7A41AE51E0421D41D3BB83C +E120C8FEABA6E5DEC53A689C21426D4BBCB68CB37568761C360E6D4E3596FB7D +F4DEC7918E58C0293D12D6DDA7E9DCDAAD7C939F55CD1BC4A228B31E9A904156 +DA6B40B08E6ACE674618B768DD681C772A3E55FE096CF949CF3B0460ABDCD891 +D17B37B355B29AB5137899C036F31DA026244FA25FB798FBE5105BDA29F46538 +D3D3AC1001A7BCECE64DE94FFE6C354166A0F97256137BDFA07F6E22A3D1D2F4 +9588DBAE95E895BC5E64DDCBBAA8D0A22C229B42CB717FC711E7E9DF793DF80B +9F14754585A3C7E17F37B32924B9F9870DA8635E3E18BD1DCD81EDF01834D9C6 +B33F23C956C2FCBFA47D84422F583459D827D1E120B97694D12F1F54D02379C0 +D288F7104F3FFCF4F76E3494F4ACBD1BE3A15543CC680924C78A473F8E311ADF +8FE00A04C6C393DE61AD3EDA5BC031E2353076A2489391B52632387CA28A7B93 +FBB065A6EF3658AE80B1ADA47E9B2539E73A71FA75645F85ED8ECC257FB4CF26 +B6C912DE9D0F9899E70BECCB934AD32CF49A093371A9F73DE6255EBC39DE1E7F +00D0CBDABD4D0383977E694890E71FBE5C376BE5F3A80C28987417504F515C50 +909F3D31178BB9B1D085BE514F71B910A9085BD6122DDC72A150BFE266920E49 +5661BCB4BAB51D6DEFE32B616963DBD989FCDD1637B294CE4E288655FBEFA1BF +7F25BBF8CF17C2D5FD161A7C2CC9CC7490D9BF15A1D35B3BFA43ADE256E88BDA +BD490D92907C57BAC408A575EC84D6AEE070148C7C9A91C03B09FDBD792E8FF0 +C0B886AAD2EDD86541E5E579359D40E3AC312ACD3D8FD49F71BD533DDF8859B1 +BAF17F1884E331DD07CEEF93B71D492AEBAADF7A263450A7A72210CE630A0D37 +BF024BDC09ACC882816B8C22C62AE38A3A8D0F6EBC2B1B2C0B8161A8B076DD5D +4B779C0788546BB4CF57332230D237856B00D79C28A7C01D11F44B7304F69075 +94B97A745DA43D1BE561372CE611C345A843834E46AD9DDB16CABCD3FA33D6F1 +F6B5C0497F5EE5400B305CDC16A7EC286AA4D45D0EEBB9DA06AC9C5294D68EC9 +E4DC3CA2B92CE8FC0526184A86EDC7AB34D67E60AC12D9CA8FD300235EC968BA +92C6FBDA47572BC5600F25249F60AD287CBDAE980E747FCBE7EE5CD323E733F0 +63553B494D3DDEB9CC1480B5C3BB79A28E419AA65B18CB297AB383419E890E2A +CE6F98C9900CCB4675280A10CF060B8D220DDA1BE55DFA65715EABCC1AFAA271 +B1F8732341613E17B231231A0D24D4D7FC198AE04D89A99C4536217769C6FBD9 +5EE24A6302F97438F7C0E311C878F674B4477A5ADA3952CDE4055AC408B8174E +86F8FB797646DFFFE0ECA25D1BAB9A9F71F3926D3D85AA63E7A8C931D71E79E0 +AF1EAC26FADE468F4FF7F3861D14C10E3BE1F9EAFD6D3A544E8108D5DAB5B180 +3950C74818BC8AF4758A108F462EF1826647A49667F5E482038C54716856D9BC +35F29922846D2148F92F943E951D7438C73D6A60459A8003174036C64E1629CD +155D47FD04B03C023AD67CD5A70C98AB556EEAB8C48169706E5B352F6505D580 +AC945171BFE62E81F8F500438AC3B64D857BA5BC54C2C4BBB237F8FA51296255 +E66A92A61FE13FDE781D393557EB72CEBAD86511035F775FAC39A0479CCD400F +226709118F887F47CC2ECC8F79816D4A945B2845F50AFD62D8C9A9BBF4739496 +9E644BC9F7B04803B7EE75A09EAE94365F6F374B4FCEB0B506C76297564B9B6B +8B812BC3A33929AA94692572B010E6210AEAA312BDFC88BF302244AB9D587A9B +919823FD01DE12438D960944D1977800FEB49E638C32E5B188B1CA033E0C37EE +A142F746367888AA119535F0CCAF7EAA461B790EB089D2D6962E28A398439BB7 +9C9943654D7A2D765B46BC0DD1F915327F369162E1BA1BA83110B93F442905E0 +523BFF5E279508A98568CD5CFD18FABBE9D17265A9081E7BF64155A2CE3C0DF7 +88D00671AD65654709589BAD7EA65BBA811387ABA5CA0BC3F66D3D48597A0D1D +2C268375DF47CCF62166262AE4840AB03BF49BE67A05EF66328EC729F03CA5FF +AD3937FC053E223303565DC771ACF32E63DFB96D5030E787961D72D02C195C66 +B48E9AF0309DC169CFE8D16E2818DA94693A18F027DEA0D91051800EE6C54285 +AB0594D87D05EB4CB44FFC094DA0072AE7D4BF2F4F9BB812FED256B937BC2574 +8C529F97AB9CCACD8DDF1ACABDF29591D77A705747ACD59DC3C4BE35B13F0833 +7FF3A11D9B3FF1932159BF8FCE44C4986C74340A346D7195D155FD47AD519E27 +A24A098E364C1480B4B05F3457ED452CDBCD3F7AF61A81528C14848265F0A18C +7203506FD03909A7E604F895F2183F01DB04E4D8BE928E09C85D8DDF4DEAFE49 +7E1A21B2C7152B07D00436033D6DE4AB442D376B3C8BF57A96657B85161744DA +2F22B614B670F17D123D214CA49B150FABD18506E52E4A1A00AC0E1ADA70F91E +A467D951A69E1CADC5C479FC6B8859DC1A36DF86FC1BB235B2AD62A38C4FF38F +26C706147C089ECD5D37E276E7339EA7FEE5079F53E7A9AC898EF5A39E2037F9 +EEC0C7C4F51BF3FE55AE2B88DCEB8AFB9DEFB5D3E6056EA31F761F64BE1D761A +8EBE10171819DAF38E783EEDD57578549EDA769F9C7D8F652225F2BA0D72D743 +4A13C9567714B61603E4D93C9F23532BB8E7CC9F63435B61C3811DC4A6D6C359 +86D8828B5389063FF96A486AC3CCFB412E785B6A4CCDFC340EB6C1EC0A77FC2C +03A8BCC71688CFB2EEAE00456188708B45036C4210757CAAFA9EDCCDC63E8584 +23C49BA147A185131B43C01778DE397E38643B4450EC4BA1C1BE46B056654C79 +888F3FA6AF36D54F56A9180B3A4C5EC8978770E99A3FAAF6C9DBF6843BFAE603 +37F5419DD223827EB96063F6BA9B8F9DDD3037E73D2749B4FF52C5C8CF63E849 +E780FA9A99CB76408E62714D7A37213EBB1DC54962A852946D53E91B0AE7A5A0 +87292EC59E086A340CE80AE721DB7AF4BBADBC0C558884B60F944852A8D48851 +3D3F9176C2B0C27486494875374BB24F55A03A2C921BCC1908725681A015F38B +589CB46F957493BAF77CF528C444DF73E2759B6FA95BDEBAB7A9243B1746E4D2 +93F180E56F2DDA2AFAD203A018FC3D18FB810776007AC8565B845A18B70A099E +BB0DC8801714879EB0793979FE28F957B4CC77BDDD949D63EF772EA7CCCCB8F2 +2E7C0E11B59A02D4423F53C8179B1E28F757E053BB7826E8322377060F32BDD6 +49AA2A81A208672C28FEFD241226735921BA68E8184BB2E47AED8C27212B5BA4 +5C55A9C78A7BF36BE9849A4CD113E499483F9EA2214DE3 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMMI7 +%!PS-AdobeFont-1.0: CMMI7 003.002 +%%Title: CMMI7 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMMI7. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMMI7 known{/CMMI7 findfont dup/UniqueID known{dup +/UniqueID get 5087382 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMMI7 def +/FontBBox {-1 -250 1171 750 }readonly def +/PaintType 0 def +/FontInfo 10 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMMI7.) readonly def +/FullName (CMMI7) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +/ascent 750 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 107 /k put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3C05EF98F858322DCEA45E0874C5 +45D25FE192539D9CDA4BAA46D9C431465E6ABF4E4271F89EDED7F37BE4B31FB4 +7934F62D1F46E8671F6290D6FFF601D4937BF71C22D60FB800A15796421E3AA7 +72C500501D8B10C0093F6467C553250F7C27B2C3D893772614A846374A85BC4E +BEC0B0A89C4C161C3956ECE25274B962C854E535F418279FE26D8F83E38C5C89 +974E9A224B3CBEF90A9277AF10E0C7CAC8DC11C41DC18B814A7682E5F0248674 +11453BC81C443407AF41AF8A831A85A700CFC65E2181BCBFBBAAB71645535A2B +6F0F22458E1429F4A67307E01F0BCF6F337E0E2AD89658D880B04C26306F8179 +C8121B958459B923AC3B05B594D8AB95F75870019130442FD29578D44F5690BC +7281357A5041C8A809A59D0DEE108E2A07D406656BC74A9F3317CB887E712318 +46B2ECAA341F8692ACC2D14ABABDFBCAC6F35858355F1D3228B0223EC73AC56F +3C987464DB829F243E304F4C59CDE3EF6EB53A4EF9BA91510CB89A3407261F58 +A2AE66880BA98FC1EF546112892494C85A2C39F9DCCAC5766725894A7AA148E9 +42360AE64BF3A4F1F9F0A0D0C1AAFDC4D50C52233AA595B7D0CE557D4A010D86 +6E6B76A7E9523E8A6633DA9348BC3F59302F72F492A30782AE7EF220516893D3 +DE836CDE311DED9262AF01C506040541EE84AAC539B404B23033EF56D4BCE6BE +B05F79CD633FE75C6728114D2749E39FD7454050F67763AB636377BA8E1867C3 +996C7D7D4A4A02BC49D1AD7FF174C1F49F1F205BC9D5AE42BCB02CF8554E8F5A +D1876C9285B6CCD7B8C165F75843B0AA11D8462B57077AFE75BAD086E9D9F91E +30ACFF91776132F3CACAD1CA5E08B17B36A0E45ACBAC52393B9AF9089BD821D9 +CD5A9CD9BECA59F7445D63DECC1B4502D299DB85B6E2EE7C69A1DAB91E22A3A5 +89B524FA20AF6005E7A586B90A2C6E5A93C9EFA4ABEF5F7E4C7B81363FE8D2B3 +0AD637FA863DE787581ADD7CBE463F7866C40F4E280260ED0E9C8453E5C7E668 +FFF058B9742DD3F131C264F8FA102CD0DA05F3114D13D34D422799181453FE23 +2FC6EFB01BE420C930B879D671F3DFB036197874725220644A5A52DFB467BB75 +8089E4F40CE9401777B9FE1D0AEE02E782A6EB2A185A454AE9394094CDFE7CFA +C03C23A78EAF242E4F811E4C83B59EF4DC5ACE4AD37B41616B46C263358710B2 +6137314545CA6CE89119B42A3518EC85C68DC07D26839C68B1FF55C4A9CD518B +A1FB32F9C475BB6110839FCCB94156E7B3648F27245A00D2966FC4DDE3996BFA +F463A663CB6935B596B1582ED0ABBC648AAA8A86068BF0038001C753C8BAFA0D +2058041DFA720B528E2D4B16196DB1CF30C779D3F4800FE662D5B60B208341F2 +A66EFCB8448C2FCD12DF0DD899911A8BD96C9B670054D328790E5D388518B146 +8CE92E368EB1DB3CAAFCA4834CC9D9D9DCC80FB1F34F39DACDE643052C977A7E +A95C5FA8DFED9B4DCE769E4E46256D6DA8FB18FD7FA4E4CED5D486803538F3B4 +6D3F5B3C03184F5C26C66DBB4C724918EBB6A89C4602E4EDDA81EEE2BD18B683 +FDB459F2CE0A9CED23DC208EAA8BEDB304B00E093DEE926A7B32FDB2EC70DD85 +94B9137856DDDABB402B2C76DBA87149051ADC6007018EBDD571BE1D092EBD95 +76D4E063AD7D5F62E6C26EDB88D38678F2806A1F4900B0ABC4ED034A818119A4 +E618F1A902315BC98F26775E59555A3DCEA1D0F8B20A9084920ECBE3F7F245AC +1182A40B518B194669D95DE968542BFF80FDC89669BC256C44CB66A2AB8CD7A9 +E42C69956CCB6BDE8C09AD22EF3196939B3B84EB23A6E071A36D702909E019FF +058F27562441EB5CAE87A4407F67C4390810BE89BBE867D636468E73677B84C8 +5A1228DD7DC8EADA221B1BAD5F43E832F20ADE7ADBFF170AB306F5B711816FD1 +39B7882556E30F002977FB88D8B28826A75DE0D20354A2D41F2DA8578376F7DD +F27B0F59D4DDDF5790E11E3957491DC74EEB7625CA49FAD90FA47AD8E0BDE824 +FF326A84846A47A21B70FA549BEE307F9C6970009F963B49A504F0115777826F +1D81203F655C242FFF15BA97E3BDDFBF435B10E74CE8543C98966223818839B3 +6BF3BC63F882B0AD0FDACA8C56A570277952E1D83F18BEDF084D2AC004E2B09D +70DE1740D7D220E92B54D2FD0DDEAF1E08C41FD321A8D474982DD105B23166A7 +AA9E0129DC88065B1E0F9382BEB4B4E1DAAE3EA5489BDCA921AD5A8175F2841F +9400478DFA99C5E5553F383882664D73FBDFA29BF32E52C28DCE80DAF4839434 +022FA515679DBC13FE98968D2894DF5DD69C49BD23D00F5D858B69D1F220F968 +F0700E13873579B3CFB658972098DC61F1DD580105BC27795DB4AF11A871CCD6 +2E1B9AF7F0DAAD4CE315379A7B42CECB983DAC5A2B9426B4E5E0A7F7978504C1 +DD7E30063AE3CBDFB24EA2BCCDC478AB82084FD30A4793F4707D9F8F9647B413 +F8A5C5AC6D5EA0E35628CE1096A434FB8286F4617CB4D0AD30A4A0B255A5A356 +25AA5A947FD3C4FA44B4AA80BAB44C48CC1E2C6D0A711365A37A58C3483D07ED +301A83D2650A2E8CBA9EE62FF5C2736EC82C1402959F64527F9B640619F112D9 +8E0F4A8A3078C72ACF3F34AD855AA4008C96E30D9E8C414607C34E06E29AC5B9 +2EE5DDB823E8C3EEE6A8DE228313D476A7F39B5DFBFBDEDDF7C45C1C88EE6D01 +7FB4F7BB2CBBD5DF7F0CBD98DC287FA6940FBFE1B3B136613A3CF16634CA7B90 +53D5FD5776515EFF5D37F8FCC62D8BEC8EE2216503D54D6F2032D3C2BF861E15 +FD1B45B71576F15852EEA65DD372E911EF4CC18283CD2FF4196A3F1A9D81137F +F1820EC604D6C61AF318C6C5AB6DA1EDF305CADEF7CC0183B86D31310A09972C +A4BC37D110C77ECCA614D1A281EE1C2040B4A5ECB31A3FC61760F608E44332D1 +D2C53C7891B505A3020E9E4915F3618588FCEC80B9ECC5E637D8D0F3C94B1F2A +C53FC46CAE0AFAA7E12266C212A73AAE60199752C042BD55A5DF1CD07FBDB830 +C83E7832D8554AD9C9CAEEC7CED1DAEE622090897641CF2E5B34A353D83264D4 +4687522DB290D3BA927BA315EA5D25B0D7B69350C6C180AB0C322B05E01F7C7D +F2F48651567F0C1B49AF3950E43C94D78F7B184BF2946B924BC4279AED28F3A0 +17A7D8B235698A516D3FB5DF0B18A422B2410C385E7E9439C6D60917EB3299AD +E31471616251FA40C9FA098109BB31A54D9C03B2F12947E4E9252A0851B81C4D +F39E7FC44752504B589C3911571B1D3EC3BD1E1807F99CED1DB20270E483A805 +CA2A016E7283550D1B1D35C226FAB63F983CED41A4D02A2F228FA9EF065027B3 +CC69D6F2E278C0A2D238D3A37154B0D22281F62C61D9182A69657B027BBDED64 +11E261E47620602F865221A534C5A32E2BF5B93A187911A146F2E96538B47DBB +7BFA7EF406FE940F4DAD17E6E4B80C4F031D71F65657C2F5C8233EEAC68DE8A7 +E1FC3055C122C1795D0C71A0284F89A9BF04837F61C9E08DB42644A490C97D34 +A5D3CEE475B8D578205005A0D68AF94AD27C0E855BB8EDB74775690A4EDD6543 +BCC10CF13283D6FA8A7CF3FE6C4F96470A11FF0B0160D3F9816B13B0BAE0D8F9 +B84C7631063FE658D13D108D6FE24A89799FABA72E6A6D1C943922CBE676C1B6 +11A4106ECB4F1A7F8A84B2783C2E6A109C58D63FC0B74D8C8A1CB62D527441AE +E656D94B1AA8581B4F07B653ED6486AAE1F8ADB30FA8D8914AF24721C74B0908 +D84F2EBB91144ED4BD7EF533F2584048DEE37E17CDE5FBC2992A6F924FEBAF07 +B626F988599DECDAB43C931CFECF99FC6EBB72F8E542765C26295902DFF60B7C +7B9ADDB4858BC9D808B7F0909690CF8DFBC59A786D48B891937C31A219842A43 +234425B4963062DB4C4E9F534C77F4243408805B5A6B8BBF428632CA4AC03A7A +E336DD181CE0CF3E742079E2919EAFABE16A63299771BF276EFA8D85C920F995 +5B9D4E8F1ADFCC5C29AA89BF90C186C5DE7679906B2FD4DB279D245D27D08837 +D3A8D541FE37415B706EC585C05804108C1D938E543B8B63E275EE85CE9DD843 +0A8B9163144B77DA1A552A25D5E77E94F29CF252BE9950F4E627D5F72536B6F3 +3278D4A45D10759F16AE42BAE8460865FEE84537F8EC9BF4813570E883B826FD +1ABF3F4E66DB6FEF8366E07BCF290EA67D39C9D81B2A7EA48E0A228FE3D5AA50 +1A56CCBF229C9AF2537A8FA70EEF41096ACED34CC7BEECA4EA1F23B39FBC39D8 +CCEA93E63F508CBE6722C11467A3D0D5C4C52031DE43C449333E4295104651CE +E13B821D7904653346067E971BE0042C571ABF40C3A1079A675FE4264B784D46 +1B8FAA4CDE9851C4EBF69ADF51A7B68CC8706C08D13A44909D4C1D78DB0E0B2D +0E0318304B229DD2FDC968027CDFF65722059C62154304D6F9C3F06DE22914EE +928B7D1BF1FC7E74B4D882998D59BC086AA2D4EAD0AE39F6B75B5A3FB9994506 +E21731E1A15F0F2D12F88724BA72898197A80FDAC00243A3038871EBD2F2BAB1 +C616278BB78490CB86F552CBE5DD0862F3793D72C68AC16AF8E38FE1A523A5FA +9B0428745B1455671CFA1F6BFBCCF9CA23C833113C2948E7A6AEFFF1A83509FF +C559BB5EE7F92BB43F7F37A371E661C826F63DD0C1B25E34A8119E71EC82FB66 +23C7B126FB6554E7560B1B69F2EDBB742F3B20D1648C151C37A8570CBD330A9E +7592A8607D2D727F3AAA0FF2057DF4E2A4C7D3B658C6CED38824A770420D89E7 +F6AD385DBCE9C9A9095CF0042052A67AB804A6675BB9373A99390CBDFB715984 +A069DE543E4C6ADD7F1EC7A15392EF834EAB4584679A43443953427DB13E6959 +0F2F5061C99C6D00FA5327FDB5330AEDE19A53DE3AE092634DC6AEEAF63A5BED +990F8A117AEB1CA0E7F7DBE02CB3D86465F1613B976D1CF6F3A1E69740A2FDC8 +062ACC45EDA6B863B60015F276860FB79C31D28F97A799568E66D0A8757B2C41 +E939337B467303041D0F4C59390B2E41E5F298F275DCC699D27C459ED4D5ADBD +02539F00095D7E1872862142B46BE06513D3EB1A406E6BAA64BE795122100F09 +C37E5D1834218EC1D11B031C7DFC9F5AB071A8F4DC08203821366959E9191D4B +289682D915AF28CE5858F83338DC51B6B0DD052A181D9133FBA50CF18F70EE65 +C33726A0450EBA9D0E0C3662AF6C2121AB7911AA9880D6BB6811D6D7515888E7 +199A0E632104059A88C9D85B19BB35EDF4AB95E1515BB2339572928BD5FE8CBD +2D4DAF55DCFE29FBC4C3D56336277BA0C9A889A129F9FA7052AD1420B8705163 +1A808EC1284C888D78CEA2B4BAB71AD76289F5F4986008FA9BF328E8537E6C91 +E11DBDD8447E1C9ACE18DB0EC3D5742C264C8EFA445C5D16C2930FB43669774F +A2CA52144D99EFA8FC427DB4128CD4C036A8C611B087335C780740FAA419D39B +5DD68EA89C95275F9254D947EB3683D0130255269B10C6CFF29EA0BE484C9949 +96188FCB747618A8044E2E37DFFD2DB8ABB621B34DC024259340677095B6937A +78EDCF508AC91D4CEFD872AD73F50582DC8807143CEB9F109C84DC5DA30B64E2 +E56DE973088A9D32583D6946DB4F3523902FB1781D993B89D5F56D79D5D98CC1 +7FEE73FC3A7D1BCCE90179AE450829E228B4DEAD3B2B4C79A400CFF899AB26F9 +048B0875EBC871AD23BA96F88CDA8B87FE5809A13889A6AC349ABB25E54ACAA9 +C213C5DE2D01BCB9CC0D7BBD384D23AE12E289FF8FDF1F611F5E14D4B20B15A3 +42D9B3B37A83A9CA39B5DB6C8316C51B70F211530A56CFE54D63E88169CF5233 +D1A7B2388025B3EBD2BEE0716C3A2D589EBC7A42B3DA602AC4E2FD9C9052C922 +711E44408DEEA1FE0C9FD50A39AD46D437F61F284A2EFD42EF158EDD71A1486D +4865D6B5E20E60F4F4FC3D646909FF1EE2D7573665E4CD8340A1B232CAC0202C +C35BA9BB3D2267C7E78518F6711633F888EBEF72DC750AC2CB362D528CFC8B2E +A1AE1C05456F50EED8CAA768DEF47FF85C4322F02D7F9D188C6F285C674EF589 +251B0B913339FD701FDB281338D96704ED7ED908BC113B4275A24D058955890B +12CCDD5572D63688426B0E1E9A40D6AAECFA5555C1CF9DBEF8C04CE1E5A63F14 +969D39B6DAE8A91F6AF4CD1E2DA89A4661DA34E272B6032C442C031F081F5DF5 +858F4620885773D8A2B2F5EB6DDA74C1408DF279900450E4A3E80BA9A9B1295E +F24EDC3F6EFD81A741EF74B0202820516C4FB720687BDD915EB2396128C3B262 +20E3075DA153D6FD36E1C05B855929DAA4DE694B6F15EF2145C63250B24B031A +4CF0AFDB225E91D99828B83BD90F1702D3906D45872587A3A116B138AD9627CE +E778A949C392202823C670FDBC56F1896FFFFBCF52C4B400F67BA36B5FCE44A5 +F18EEB8ADFC088C99DFF8E0A593E81A5ACA2E3693005F723C7D3E0AE2BDD3805 +8C6007A00542DEB2539709558A88B21003CE4B2C7817AF207ED576B25A41DEA0 +FC55A459BEB00ADB01309B35920F04F84B7B64F95AA99EBCB843A06CED900D99 +97BEFD7CCB9F4D85876F10160C8D63E2FDE82B7A8D945F37CC9933ABE0FD1D76 +268296B1A5AB06B2E814691128771694224781171DC6266BCC290FCE1AB59416 +85530368115BABD4F1DE45952918D1945D51EB713C283DAE8EDD559F437CD886 +A4B1DA6120D685C284673A3EE489FC1AE4297A3623B339B7D886B6B4B8F9F4A3 +7BF85E320A52FDC6323B51879B98A14C33C567BC069D9B44616514EE1BE36F90 +EC5FA33E1B6B0A46945D876EF0085E74935DF2560A03321861A752E59742B9FC +5C501FBC64BFB1602459885B63873DC857ED37F8BE1A9C6E9517B9BF5A6161BD +DEB6DB0381FFB34A8A96AB4AD48BEC40D4C198ABC599C3758AFF638AA75BBDA4 +8545D5F95FA426FB25587301A43E176F6CED7851E815AD907F2443E70740DD2D +4FBD5D978B9B37F59D6DCF0ADD0F90825DD23558FCB858513602C8BC82BFA383 +7AA6DCEA4009961D06DF233C5381A7F9541259926446B2F03664BC5978A1B6CD +EA6EBC9FE6100A65959513EEE32E69D47B55BAF30A893D77142F943982019C01 +715CE29923795EA01C58A798979939B507C5B29A32881877EF7EF0C5CB3DE591 +6B9A6C3F3FFA847F396A396F078860B59850BA4CA3115CA2376AEE6B30C05DC1 +6F9DB6781ED0F9D45D10E096C33B1B7CD12A9D57C6E49AD833C4B093DC82811F +16B3BD902BE764A1680831EC5A6C1CED84AE0DC0A65678EA5270BF20931E6409 +7AA44EACB22CCA11098F8A51096BE83A1ABA56C9EED4195D5CCF24FDAD92E823 +C439DAAFBFD652157D728F2754F28304710D3CB33763156D76A259D446647A11 +493FAC70DD28063A4CDDA162F72542368E1AC2826C4BFF7109208F66371910C1 +068F21779FC39DE03AECF1C9FB2F417930C22791961D801284DCC89B0833B6A8 +D63F153ACBFB7B7D547924613BBCCAED37D90BAC5B0264ED31C7B9DA5A2BC620 +9B20CA48424D0FF58905BCD6190BF4B5FC6ECCA1BCEF13426920197CAB41C4E6 +E82E8EE7BCB23C6BA6F8B58001533B225ED721D6CE3D6E89116EC33CAA6E905A +649F8C6A1AA187A48E20DB864596481976216DB78F0F57543DFAE3CDC0A6FC77 +2CAA49442527A5D94DC54BE93C875690CBE52EAA4EDD9F2A511361BC0F0807EE +96AD0D26B62D809E82EC14EDB158EF48A748A6FE0C3A7EE5D4479B35425F35AD +3EC7444F6FA75CEA5011AD571078293448A33C7647611CAEE87974B0A756DAC9 +4E1BA78DEE477FA59AD50BF5C52E068A5E044A4A4994D5B24CC5045F768A3C51 +D4F65E2A5AFD271A7666C6835E28C60751EE528C0742433165AFBE71562A3016 +F59676D56B0B5F7E4984D664BC3ADDAF24B4205752EE21D4B57057A943018466 +09C3FA5D2C5BCBFC22A643586BC9E7A965DC34C0A7D76A470B0602AE45106417 +0701ACD2C764DDE218B924E38B5A13CB82678372E743A8B3CC300BCBBB878978 +D9847F0640A031D5E76B5AD07699C3B2FC6C1DBBF79938BA649C152FFB2B5BBA +D18B9570670B99907506494F362B124790A17D4F415D8447ECF70B67DBD46643 +91AD465A1852B804CBE65206EAAA38FFD2B4180AF00DBC6291C9474BAE831466 +A9837BEE55AA0E15812D87E7033BF2CEF3BE10ECF668D3B57D6EF1D4335152CB +08DA4049A65A51BCE4BC82BC80EC26012417012D6C86EE5293F1343F6AEFFC30 +998F5CE34DE6ABF67EEDFF71511FC9462AA30A36A54C347BF852749FF906C73C +7350511DF420128BD2E6441C853BFF55899534D93A31C4B721622A9AC308CC38 +057091623FF825987AFA03ECD117ED711F193BB07457CA841A6CBDDAEA94E3E5 +921B7307A98A7A6FCD90CD09AB21868D1562FB2992ECA158DF5E9E5E5C66D549 +EDAADEC517CE9D10A93F0A132481B887AF55B32F5871FA9FF1605339B00EC64C +E10570DA68796CD8EEBDB75C548442C10B3D8B07698C3569F4D385DBDE0267A7 +8B7EF2791FF56DEDCCCC80F2B114F08F1AFC29B5166F9CBB2CF7C0716D862195 +B39BCF6A81928E3C83497F03B43B0CE1F73DAA641E19DA487A5238FAAD459330 +28244382F21C57CD2364EEC72281433B4A591E35C1AFEBD1E40431913A7537AF +CDEB0201EAC9CB87B28E1B2D6A24EB2E8FAB1C8967F7CCDF91E1B5FDFF977F31 +32D31D +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMTT12 +%!PS-AdobeFont-1.0: CMTT12 003.002 +%%Title: CMTT12 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMTT12. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMTT12 known{/CMTT12 findfont dup/UniqueID known{dup +/UniqueID get 5000833 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMTT12 def +/FontBBox {-1 -234 524 695 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMTT12.) readonly def +/FullName (CMTT12) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle 0 def +/isFixedPitch true def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 66 /B put +dup 69 /E put +dup 87 /W put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE32340DC6F28AF40857E4451976E7 +5182433CF9F333A38BD841C0D4E68BF9E012EB32A8FFB76B5816306B5EDF7C99 +8B3A16D9B4BC056662E32C7CD0123DFAEB734C7532E64BBFBF5A60336E646716 +EFB852C877F440D329172C71F1E5D59CE9473C26B8AEF7AD68EF0727B6EC2E0C +02CE8D8B07183838330C0284BD419CBDAE42B141D3D4BE492473F240CEED931D +46E9F999C5CB3235E2C6DAAA2C0169E1991BEAEA0D704BF49CEA3E98E8C2361A +4B60D020D325E4C2450F3BCF59223103D20DB6943DE1B57D05DA0555DF933BB0 +7B42D264831116C06C79335D519461E7B0E870A6715E3D74A08D1BCF86E3BCC3 +A43FC6BAD1C68BD9D4AFCC06D845FD1F1E70D7A47F0BBCAECE8396E04591E5E3 +4797F646AFEEB7DB548183F0B74C9BB6BA2AA04E7F5950EC8AE97C741D4B2C5C +A8E7A8DF5A36A30B5A7592D95E1DBC63EF33C92FE459792CED29E2B8B6919251 +75EF62089BD7D44A6E1F9B62EC802FBE62B821DA1C3B2DDED45D27964AD29ED0 +9FB7868F3A8FEADA87A8E42D52C1EB7229D7C79B60BDA263F2BDB025AE14A507 +098FA274206BACFB4A0A7257D5998EE8F0FDCA79CB61DD1FC59DADD11E16BF02 +ECDFD706CDA1E72054D4EB55AF7BA9F19955886BC0BD6E0E3FE3769C94AF3581 +DFB2BCD67FE2892AF07E858A01280194D8DD7332B3D0A585C87FAB056C2EAA9B +5AD48D1C9F00CEF8EF0D1408DBE1C03D04B231D7B8D5D998FE0CD7EE19828EF2 +F988EBF6DDBFEE00F04A4A1F4E1A55DED7EF3AACEAB5005F1962C724A017C914 +2936E2E0DF26A55ACD7DD836C6035CBF07981C1BCE3615064F0540A1034C69B4 +E3908E76EF8925D486DF0B4A8E1F02D8AA99585A7C31847AB9382F83880C1C21 +C496AB2DF8E7BD4643B28B704B5F6B53429D3EE940A79135F5BF0396E5B46F23 +42AF406C26D12BEA7A41F332AEB75DF43C15334CF4651A99F602036946B1B91D +4BB0D2E51C20216D892C8173241AC8FD15A37C3CDD8AB4FB67D8565AFA61C068 +95E3D6E46D7C09BBD09428207D506AD43C693F3C3D787F6A5C39084AE45E81C9 +830900DB50DAD10A17E118FB5E9680B5194716A788FF7514A1167DD1A305FE62 +C7DBA30E569DD3565AC9C80B112E221E3878624A015F6224597D02C9A07B368C +F9FDC817988D12CB00ABFAB288C20A59AE40E7A75C235938C47F77261BED14BB +6574F633BC4DE9E8AEABC46E0965AD69748C315B6DB5F264D5E7635BE37E23FB +873E2488086BA27275142C774A4A241F92E8ACFF0F25E81E2565642D88F77DE7 +03BCCBC437BB1C9EDA969E92CF3423203EEDE585A9371A00BC54DD695BA12EC2 +461AD673A27B24AD38903C82D0208B10D78F01780B31793DC3588AB9F19E7123 +D6B46ABA3A72F6F7261CA2274AA3EB9EB8B21E190EF9E63BD18362B550CE5AEF +2C549D287572DD717264EE11BAF82DAE986C13C6ABE4780078244D7BE7119E94 +325AD0BAF860F539FE738FF07ADB87F9B8BDE6EFD70FB3A2BD48C8A83E4C0A86 +A64BE3F5659B7A5513C93663D3F178AEF685E424E3E9E91115ACE1358EBD3CF3 +25D9966DBAE54B4E5F84D18147F528FEDA20FAC9B8E97D9F31901754EC8D1EE8 +45260CD817B090729DEEEA881960AA2328BB6DD66E4B7E2165642AE3E0297DA0 +C9BC42F199B383F0128A5009E74775ED366DFF7F8D82ADAF14C417B98CDE1454 +8332341365A96C5D3436F63BE7FC1339109D6F3D387C032A1C7463DA969F08F0 +3D034A8CCE155D86B9208B64881343F5EAB503D8B379F34757601515FC1AF7E4 +E21C8811F9A46AD130B06112D9ECF232C79C55A32F9A32678C3C7A65F759A425 +F7D24B44C9E6DCBBF406E7E1E0F6D8A2A3B8EE331C4E9E650017FCF9EEBDF3B0 +CCC4E491357BCDA073AE32350D04721294519EEAE9A7B5FC45BEDA547F25251B +4A5B130943F11D8505C3F75AA569CFE3936452238CF1B8CB4FAE036866EB9749 +AEAE038114576702BC45058F9B1E2CB9FEFCB766F811A29A81C4AC9B462675F2 +849B39199A37D6755E761111DEE4C0FC168709AD4EE609CCAE8B6018BF802FE7 +D89ACA409C3AD4C2ED4A37CCF6C1926B36D17DDB27100C5B8A3B1F84DE92223F +34BA6EA2732B8CEE09BE17C4BAA0181CF2CB0D24EE3764C0242849F8924E4173 +C2D32E3DAEAEB1EBC7E98795FC19F7E0C833F441CE0215EEDB5F21 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMBX12 +%!PS-AdobeFont-1.0: CMBX12 003.002 +%%Title: CMBX12 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMBX12. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMBX12 known{/CMBX12 findfont dup/UniqueID known{dup +/UniqueID get 5000769 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMBX12 def +/FontBBox {-53 -251 1139 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMBX12.) readonly def +/FullName (CMBX12) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Bold) readonly def +/ItalicAngle 0 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 45 /hyphen put +dup 46 /period put +dup 50 /two put +dup 56 /eight put +dup 57 /nine put +dup 83 /S put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 101 /e put +dup 102 /f put +dup 103 /g put +dup 104 /h put +dup 105 /i put +dup 107 /k put +dup 108 /l put +dup 109 /m put +dup 110 /n put +dup 112 /p put +dup 114 /r put +dup 115 /s put +dup 117 /u put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA +0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93 +51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71 +7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551 +E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078 +0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273 +C01924195A181D03F5054A93B71E5065F8D92FE23794D2D43A151FEE81296FBE +0CF37DF6A338C826464BA5198991445EC4BE80971DB687336AE8F74B516E333D +2D8AB74D362C559AAE6ACFAE49AEEF4F52E28C869222C1301D041E7A0BC1B608 +1BF728EF9E98F3A12EB2714E7F16B14E055FE1FA0EEFB058860ACADEDA9D0E4C +42E3C6F1E4869471BFAA3760175F3FBD842755A9D7847EBF605F18293B42F557 +FBE2715002669091BB033E1AAD657532F34F7C66E4F04D63ABB07E6CB9D9AEAE +78EDE8B79DD9BC87A1FF445EAA05B5572BB880E69F4DE1F82D7F0E9980AB0C18 +22C448B0B1722D3CC33C56FF287CECB80658B3AF5E7675BE82CEFF3DAD5942EE +A03C955FF979E41E54BCFB5316A9AB8945C403A73180D0961416EC9C92F49811 +4B91BC4C788392994587517718521E416D469F69952149FF7F9224377EBA1065 +4A727BF806A112A7B45B0A1BA1D5A23683960575368D9EAC8C04753BF7465AF7 +95F25C258C63E4FDFFD0B412FD381946AA38C0B961652BCEC30322C47BF4755D +9F91880688AF066E32FFB22E1A52DE741307AD3ED830D6BAA1D1F562919666DC +5E8FD9862AC8600B0AE0BC7FC779252AAC57248744ACC8A8AAFA836BCF09B0DF +9253DFBB1CB77EA8A59D42D1B18FF25E9AED72FA62FEC3F126F030F5D7DED9C3 +CF60FE890BA4A48E39E687BFFAEAB96AE542A6387F6624486037C8924002A511 +BEE5FBFD780AC1D4BEC3FBC47A930BAD0280D444259528B6C565DE11DE36BB65 +9BADC55C1EDA1A80458E98896D782DFB5C137897419602809F9BF8CA39F00C68 +EFB9E076FB324C2963F23CBFED28B9EF70EAA4E4B903225D1F199A7162AB239A +D92D71C18B1B682D04C6A48926275BCB16D413B2A0E953E1257E0B12D8B717CE +2EC84CFBC046A4338A69F454A469B12118E562B4F56C5FFB3CA5D357513E6FFE +947A564B229C7FD873057D5C7CDF03E958294A1003B37D8DF565A70A00A3734B +0138AE5277D383D10C2BD853EF806D3CCDC47739F0E374A3DF3B63638B949ED6 +4EC25869DC1C0B1F4DBDFFCC97382841D8F10F3635C792139A1EC462FDBA379C +BE0990CA2E70FE73137AFBBF30CA54954D7E7377CC50BDD780DDD4C7FDC77AD2 +F3EB1169F14A0041F18160F43C24FAF556DB5D621709FBC544CE55424F7446D4 +6AC07A51C8CD5161AB0AD5084A96FB35D77F1CA155147DEF8D7A590EA6939514 +D4A226588295CE0007BA8A550895511C8D80BBE5CDFB8A50D249C3BDCA974415 +F5557914A9B805782F399E4078DDB6264F1A49A9A5BA45E284A5196E9828EBA8 +481D357B8D9E6ECA631A6204439FDFACE7D7E6A2392726107CB7D2517CD19A24 +FBE592C119626DB221BBB635B6EB84845C16A9585282E34958B961F4A543AF9D +419B6A9105BF185FC767712D923437BE08A9C0EB92AB6792DBDC671029B6FCA6 +7F717FCE379C0F3B51C6CF042A762ED04898FBB4B0105C3C4ADDDC18C51BAA3B +70A93666669547081D9246732CFF74C83EE90DA17F5B4F8BAF47FE4D81590988 +2858C9B96071341FA0A0D23BDD4947FC9BC2297913CFBD4FD6CA4303AB3179AE +0203F1BD502065F90CE9BEA3B52DAFE4A29446082EA0E6B1D7AF1F31D0AD02CC +9A7FACE2CA86E5FE0F6A425B28A5940ECA306891CECDB3CFC7A5BBC76B5D9E8A +C754379ADE80B4D72CE493010317BF21A0CF4A0A55C1246218839DCA3F4D626D +1F4161D38F54AD5142C1CEE95C61D8BB10FAD4B772F4955777AFDE8AE5A837C2 +A2BBB11D0BF5DA2E63D0B75ED421DBA9C789B281B01846B65DC572BA69591969 +21265DB722AE86BD8CAA3D887C975A617ACEDDFB7AAB341F47532AC0F354A530 +7662C089DA3939588774FFA16FC4A52555DED6D6F51DE718BF5F345C23C90198 +17B77CB8B5D53A5CE7A79F3E286B6A59F3F6178AC8BF15C0A15C1A8A95D03B60 +30EBE53DE328CE085CD9A1D49C69AA299C5B58B24334A546F6E274C1B534DC8F +3289553F560C2F81E413ADB92FA0E7DD1C2F39D5FD268EBA97AB7335ECF28257 +96B4EADB7D0778706CB41C7E9C882760E7670936774A1088FFB2011115FDADB3 +B69EBD5108760762521C25C968C3E282DC3400001AC8FB1EA27FF643E3025950 +1D617BB8BB321281708E496277E11DD3AE0023DA9F25AD06B39C7CF527FED27B +57397E88D3DF70EE4FCCEFC8A0927D6B05517E571B3E70ECC99F3CBA32CCD4DE +B8BF22626B6C94FE65598A88AB90D238461EBD9A098DADEA4091AF1CDD7560EC +8E1B9BC2321686E1759E6B8A270C8CB4A254F7368039602EAEAB86ED21CDED91 +8F2DB9889F46981C494C7EAF5E819B91C129F0740B8002B510014985E5791F59 +B16879CC6521D8E9F1C4C1890AC85A78022BE614BEFF318AB2616F0C3F02405E +BB425D1555472A2642BA7686E431DC3FB8A1688B76660D9957C3FDE8D58109AC +21B1234C9DDF3F0FAF93BCF7B2F88A001F23162E1A13E5E9118D51B485B70A91 +D0CBC39CF44413FD8686D9030782DAB58064F5B987E0402AF5B264B17BD31BD4 +FDF63951BECD73ACA6138854EF35B062D01F33073850D9C09A818828C581241F +A625AB3638081DD0F00F946BE5450D38489CECEA4E66B4D85CC8AE0157E2AEE4 +A22A9313829F24D573101D84CC1784D1CED7DFAD5DD966601370C6CCBB723082 +A86BBAF0A5D867D0D2E3CA16E14E5109A29EF02649C47E12E88B3B397D65CACA +DEB9940B92100744D686066F8250FF30E5F13D81428EE238A2E4E07ACE0F5C38 +7D79D4A336D0D26AF9C2B84088ED8ECDF94A1E3FADB45AFDAB46CAD6FF950B0F +07AA2CDF82374DA76C56D29C80138841EB13F0D02ADD32F88B23E282ECC845F9 +BB9AAECE9CDC644AC2D49577A92307A83A99434F6493156DF25DBF0FCF2EC21E +8C50A312C3D19E0609C0038554CF4FEF3ACEB7A833FD54B06EF0D617C2971C89 +E4C06075B09B84A4F78A82152B9A9C540B1D881313C2C74F20ED064A9606EC2C +B56D7BB4797F1EEF4A9B13579CCF311FA4A4DFA62D80FDB7F535CC6526D1AAE5 +45C008EAF024B48C377522F74D939A475970533E645B1BFA81997549AFF26F67 +2AAE6C2EFA357DB3B525276EF330905688777057F4E4CBF584520A534A8587E5 +5A8360891E75A15205E8ADAC4A4E5A6E27D0C4A7D492216E4BC023AB027F37AF +A8DC7579BA50204D5F45A51460C5BD8A5A7F87668CA6451137F2F59E117BBE28 +5C40820882A5546FA76F0CF49F8A6EC445F0647CC3227C400F56E7E9B84A6975 +E85E243CC1666DBAFF4E07EEAF3AF71BDACB30DAEA792F2B8504CAB071544F01 +5D66243D529C479D276FE22F7E275D9E7FA9C6EECA18716B2F213916E32C1D94 +6E32397B41AC6779543218E506569E3544803BBF9B404A983EBA62A494187B30 +8D3DFA4E1237A2E5E08224A60492C09ADAD8775B7CDB830520829BA164209ACB +BCDEB2D574CEBFB7AE4BE72DF4EB1945FEF2458761AD8DCC0D378AEB7DA002C6 +9C14A665DAAA532B0ABA98D7BFB5A6151FF6703385AF7AE8FD315A492FCCDBCB +B825707F9566B3B4943A3C61C3DEFDC31A843A2D67AB06891F3E110DD8C73D3B +B5E4151B51D9F13905D7D94DB9ABBFCAF35F43B6EEE256B1A80ED6D1739D8D5E +8C767F6F0E8704C5345D028A2A6DAFD9BB7AA048B8B895FE9423A7ACE858BADD +595CB074A128DAFE08FDFFD6BDAC0114159A702FDCBF8013804B0CAEAD7AF38E +FAF086A3248AD4FCA1401A85AE2F72E3E6956DC0996FE8ADB18F89B14A208A15 +13F81AF73D0DB72F78C4DA634ADE3C73756CAE6AF2E149C26316DFD93370BE1A +FB4A79F77A67C07CB0A53C78367F21661D4AFE9E27328E077B522B50FD9AE2E3 +DA087BE481515B5DD7BF894A96A84A6C78874100505B7DDE1D22EFCE8D58B3AB +313AB5495F72E2CA4E6AE22C0CB854302B9990372F1661D9F0A517F90686F248 +C5643008B3D29F7296E5C8FD4049886662EFDD4106E17C879F5D41CE84F87E89 +F6A3117C968B95A35940CC29C43E1E0DEF51C1E46B676301F40D59615C3F73DD +DE37B72FF7105DB84227DA5241583272AB1C3CD97AE11C1EE98FFDB5E5F44844 +8FC41BEA5C54B26341AFF6830D9D0A5A2901B0653D8BD0746838194D240FF753 +E99750D3383373F453723D86BE97B571B8B84D8696089B5CFDD53E6C562A2197 +A8C4FB0CC690C27761A816B441029D3D306245052E0C41B53025D8CB7267CFE3 +C17FDFE348E765326F91AEB700CC49162DF748171214252CBC821493DD01AA20 +417D66DF47EBEFFF3E9BB2B0A2BE7D9B8C68BD570FC2EB0FA54CECC318F04C43 +19598BDE93F2F13DC7847354C99059AB20593EE51E94F9D4E9241869D605AAF4 +9D9B5FD88C3798A039A67993C5EC68B6326B132E647F67EACCA7F7AE7F718D85 +12666E90D7C73EF210E344964A38228B236679A2B18F5E081234CAA2458F8D83 +3F0CA308D19663CB12EB904076EF88E556407C33C9380A6A3D68A9EFE65387C1 +A1BCD2D26DFD2AC0881EC30E81C0A4E76C244A2BD822EE88C4A60B480D107E68 +90E419A1F512E865BA922A7830909BC2611A80931CB2E9344529586726614D94 +3AC5200FB9FF68AD9686506C5EFA8788C0AD0251AFE7F95E84683380CDB421C5 +B1A783B6D5F3A6BD1BC1C14B363DB01C87C0796DCDD5BECF41A1A9F43183CF6B +82C2AE49F0BFDC5DEF7729F2E638EE6EA9E4D059EB9BB1B992AD8C82D501A550 +1BF73CBBFE740179B54E193E84A55DCD61B343C1852780FFB44248FC9426AC94 +AA2B3FE20FBA30F6C4D1E0FF3EDCDD8C0F57CCB50CDB0EFE2E04A8927E239C1D +9B026C7929BB48461D4D695FFC766C8A0E545B1BCC2AA068D1865333108E7985 +2D93F9B00EA0A90939D0D3840D59B6CC0CE2C147B2E1A9A4F14270FE3ACF51D5 +99F7349106165AD627CBBB0ABA01ECC6D3A14C1DC1ED23A9DB9865BB4396C51A +31ECD001EAC94B33C34E29C5611148EF3E55DD61813470B8F3CE32564C749414 +3C93C77EA5A3538A0B5AE3FC4DA32813B06772E0E48E25BB39F3F6FDCC077E86 +F86FA50E18FD19EB2F37311CE87F18F3BC85CE7FD71CA92D5C3264E34E04A2E5 +70C79D99F54D6C6D9D527AE45EBB48411221134587D2253E7C8ED7658EDCA34E +5E768DD14E0200470F73C44D006CE8CB35DE1CA3EC10ADC668B0662A7774C891 +84EC95A31DD872F0728D9F65CA80940080E04630BE4DEC77A2C49E3913C39978 +BF145F8832AF2C4385EBCDB15F9D32C22CBA0CF950877717D6F1591D7C0B8047 +8C9BFCB16AF7124ED83137695F3D69228DB633053208C29E0ABA1B06A7FB3EE7 +5625CB44927E2DA6E038A6E62DEBDA2D96A03177982D8FA33BAAF4426E05F4B7 +9C1748B3FF7691F9888E7FF864A10B9DF761A41E6B5CFAD2BDD7E1C4924AC97B +F4B352705316DD1A58637CC12D71C18A5CA691AB2AA8F171590EC24582B1123E +94D4DC587D8F99E18A711776BF4013C96446BFECFEE4C809EA94B169088024DE +0CBD20199A915AA406F0BD5F3D63D1467C49B4691AEBBB35ED6624F2D7BB74BC +E80FD92B9FD04DD9C2BE9B6FD29EC7EC07FAB447511C61DD299C783BC09AE2A4 +7B3CBCA6A20C6631D06D0B2E2482A50612BB7C29B7E7D0A205EB0E8436702581 +596BC996ABD58CD8D5BAAE4B1478195CAFF98FE0141287296C4EFB8D2E7A8442 +F0A3AA9F9264329982532295A176BA1867EF732BBAC49AF485D9D0F7130F617E +7F7DEEF935874D55A22240F8EDE4F247D5F73481373A392D40A8076BD91079E1 +1CE5998BA13D48D56B49A92B4A18430E316405D2E2E391B496A1934671FF1785 +AF42BA3B2D14B8E04014437FD194455C50289DFBA61B5C377BCBDADA48E82DEE +4E70EF5E9DC03064907BCB8BE4D59DE069FB0C0CB140DA54708E630767313F9F +744594AD8A499CFEF733E640A11FD74E46A749F9C7D18D49251BF85C6EB4668D +67598C31A8F90922FEAEAD4B83B6E7184567DC798E4BA1C4C9B3461A478D63CA +054F13B502DACB674EB49D6BB935E5EC82BF99FDA7D47C581AD7F940DF4FC6FA +6C6D25D647033AC69505F0CAC58DE99087F365531A6283CB89CB644688963C3B +8B2203A94294E58739EF23C7803630A1F9121D62BE1977DE2F41687C8CAF87FE +CBD7AD3B98E0D95C8C6E1A7CCB0E09465AA874DC90A0F5DB2C5E7C130297FD39 +EFE63B0350B5139D09E6864D22C3F1150B29196E40EEF9723E71158B7ECFB8E4 +C426FEDCD439420B7F1C251FADA347C9A2C49738B5A17922E1EA93CA7B125B76 +57449EAA9C1D591CAD327D0E98EF2D44D614EE9ED49DD31ACAC0B956620B6BA5 +5BF6D08CA7541059D5ED2EF00AE2EE95488F5645BF6837D9241C0D3959B7580F +C9ECB2BCF3E65C07D52EC9CFB21C11CD4C883E44C173214C900C44D2E1E43DD1 +CE8DFE3DA93C38B548BC4EC46FF91F30CFB97525E1FD4E77686433B20BABF8D2 +848C1CDF1BCF185CFD7A81D2D4BB826E837E2AF35CFC4F419F698DB0C43E9F9C +B0FB628AC9A3CBE9B1FF4A067016E70333E78B32AB2D89C483834B31F5808FDB +77492E099F1504DABCA5722C7860CDCEDB2DDEB512FFCC7D287F4945FD711F28 +87BC3D36173566B81FC2C1290C717A09697DAC6072408E20926D39270121CE58 +3EF97CE12EDD7F87F2C8CFE36C3C0400869C0D813B71C425343EE0CDF717BDD8 +409D5297D0F8F7FDEB0257C0A391F5635E0DB1116058942FF3E7C94D5F2873A7 +A3B0ADAFC3835AF2BE474E6741319BC6695FB37F59AEE388F81F6E66F910000B +72E6BA7531B4378CEFEEDC79CCF4947BA1703823B5AB4F4AD73D9615C66C489D +99D68E49C9BF765B7FC547BAB9640D51D5A7A2396507AB5A4DFF3D14F52422CD +8FCFEAA06A56C6C7FFCD29C9A7A59DDD2A909A9363FE5F1E9629616D25ED38CB +E754C059E4379318CC491C3B1A90128693AC53F80F8210FAEA7EE638902A7D3C +82B95B3F5AE340EC1B648DBB9FB679D6E80B7F426D8671FE7136D97F51E2D2F3 +C9CE9183E4061CA40091A2A70DBB9ECBB19CE3F65ADD0FB346B54BAB182E2CD0 +EAF4C0F402C25573FB344EA771B297BEB615FCD0595172E84ED2A62FF8962634 +23C19076C2A9ECEED5135994EB397303A9619C76DC55E032DA83FBA441BD484A +59F70A5110A8927F6239A14D4E223E189A5462E4A92EAEFFA4B961A2A32B320F +C2B4E8C1821FA67A655B5042C15E4DE1FB3652B55078DB123573C4E986B19DB0 +1C5131F3DFAB271C30A5476B4A19D8FC922E31879C34BAED94C07A4841B8209C +403369FB8E842610D1EB4662B6171A4465FD0E819964F62EC5B0ADC92F08CF90 +1DE0B410FFBAD16F6D355E8AD72CCF67961EDB6CDA82398021007C2D0462E893 +75EB0710AE4A6CDD15077C9DEFC5774EF4A657734D703CE42174259B58E5277E +0DF26BF59AF8D1A3E7DC12E3C12AA4B67CF35B19962F6950C2020B698D971B35 +82FF84E72F72FBB0C54A112BADBAE6C4CAA358BDE6A705AB59332C3850CA3D25 +C7564499BC1319121CE0D93218210C68080AFF33420E3CB3A48BF9EB66BC07C8 +A79D8CD8E78C200FF7CFA3DAED0B9E87E6141C88B436D8FCBA50AC195FCBB9BC +9512B95FE3A37FFAAB39850FCEBD4D50A243EA416E73F53B4B00F3B6EAE0CA06 +0693AFFEF215D00BFCAD02E45496D7C8F5E99EB9096FC4300D038C1AFD31EC4C +5ACA6B72C1BE7204E37A4CBBCB1EC26AB87F2FF82DE20601025169A5FBD2D060 +62B5B2DBC288C79C33B596832AA18D730AD572C6EDFABCBD36DEA87C0F323C3D +6E537AD3B43C6F3A905597570A8C6B0B4A5E08C08A430B643B6ED36DCAA09B41 +FEFDB90E10C8A8B74EB358437D0CEBB9E03109C593BAC09CA996278246E7CA38 +4F311F23DC5A7F7DB374E0E3D066C874EEE28CFA47054DC41A389203BFB3D601 +BC9A4817F8DAC250A102E7404CA135B33DD66F8FF40D106F52081FBA3E79CE29 +FCA5C502B38C86D8D507AEF30436472D2381DEA09B8B3BCB109BCD72332A9483 +E61ECF0F89A85BC37B8C10FF3F7C0A84FB8D0F4A7898D3F8B06202FF8DCD698B +56544751347E9AA35209B18D3D416E5E034E36D9C5FC0B33B7AA0D46F3A3F925 +7FCE00D42215F7C2717D78C7BEC199043BCC3D4CEAAD87BFB1ED2F719ABED65B +EB6A783BD5E0935B9A5FA7F899D25750536B99A0113F16C45E5CD4DDD975EA74 +596C6E35ECDDACE357F9EBD01798566169C00EA22408D0F4FF55AC05313E7D74 +038FB6B5EC916B21D5F65E1B3E95160A471CB7DBDA69610467149176EE154B5C +7754CB9B0197B63A089ACAA2F261E02A726B1ED33D19A1FBA9796366A8A8A68B +8CAE6C135DE5112639D055A8FE22F1DF2524610F4BB4A81B08DF745EB2C005BC +3C75F49F3B81945768DF7387E169B97F2D97D237DA0E9333F57CA9B88C746B9B +7B4400828A0B8F207A9F3365F426085F664F120A823F5D4E9BE39309B588E413 +B2BB052FEEA82898611E212539408722E7BEE17F1C0DD5548283853F124CE54F +DB4F95273C48599D3BFD0658FD23A688710946173DA830B62ED1EBE7F99DFBD6 +C6B89A3DCE2972FFF9FED7FB2A8BB9FE8FF631AE353ED88860A0EB1309226B5B +E7CCF92B139FBD8D3760486151E6E1D8A8FB29AB75C41E6F9BBEB6F1919CCDDE +8DD5AB6A65107EB060295E849D2D6E60580AD2C02DF12FC823DF9F935757A77E +3F5E4BDF7EEFD9EE03ED598DF438A929A368E4AF8D03BE3B79D5B92925EDCFEC +4CF10B3566E183E6607A9D5D8A8B584D34EE7C673955B3DC1AAD1EDBC150ABEC +E085377B9E8F43224A9D0C3AA793555C3F66D99F7FCF0C62FBC5BED46EB0052F +DB8A8CCE5715D224FA06816AF233D30285FAA25CD1995AB1C6DF94BB44011D24 +BA389EC53E7FDFA26F06BE5000E1F3E7B1A485830238DF86A0826526743E3B11 +487CEDFC450401FFDE8FC4E8355C878915A3A93996C2DAA81A81A7E6D2AB3FCA +3F2841D2CF158601213FBB40EA4FAB368774A0E639FC78E8C2AFC5E1B08F9D1A +70226CC188C5A317CE739326C15DE95846595FAB5F4469132B50168311C90013 +BA9F32CBC89D7776FC977774A310184D7C9250D953931DBF3D695A193EF2083B +EFC7486F753F84F4C01FD31CC2344D2F4DCAC01ED5D94278AA9DF63A87BDDFC6 +5E3297C73F8338C6189EB1A766894F262A1AAB9337B079ED5C7298C66163DB28 +549BAB63F3D657DA44C6E832B8FB2A57460EEF0AAD362629D1ADE07AF89B14BA +4331C2E521EAF3BA71F1FDF6B74B5A905B990AEBD3B2B6F8FE69B65CFD4FE59B +19B74B739BB59A605FA5E70D833F055E493BD8D244D1BE00036D00D4340E8776 +F51F81D25E7CE1066F96D3968894E949DE618685C02EF0B5060E88D848BF9409 +AC34F2219E4F2AF2CF219326664044EA6A3F7747839BC470680F16EAC85D694F +D67A4660D3D7AB03FCE66079CCCEEFD07D03F9DF346CD15DB284DCB490764677 +3AFCD9710DE1EA4AC39CEAD6CF59C6E95B2938A6899096AD94E2C804E74AD781 +89B3C6767FCAA5BB25A0C22BF93A29C2CEC62C300DFD95D8129E70AB4623178D +F35B4B52E4EB1A354A9671CFBB2B6D543171CC9CA8985F15BB94DE4C76809E29 +711E14A3CF8EB7179E2AB1E6DD1E56FEDAF53561D04A852C999042AE4D014639 +9F2036D05EB66C514BBA47E0D1615E43213CF3270E28CA59C8D671FC93E6DC4F +DC7895041DABEA2274A2916FD80A8AB435235EFA0C42ADC3A7E560BE0E19D026 +C6F6EBE1519C4E38568EAAE27B2A68F13FE94727E05E4DFC5F9B45B9F946AECA +954CB8B160665B6593E31BB27998A29F510AA5C149050F995246D48CFD2E3046 +4D61CC66D7E1EB08C253C081351D47D7A625D0922B6AE8FE0EDBC35732699C79 +2C8FCF0D9303A7DE0D34D1A28D6FAF68177FB23E919A8CFDC30B947D419E0741 +96D6A3DF749E5B27F4EE58749352C396A2161EE2F7F145203A4AF7B53149A98C +BA98B1D9553584918AB2A4B7D83991FF1EE5A4AEAA5B722B5D60C918973629C6 +A22B23D2574425D472778983A2AB0B07FB0B076E31F277C8AC19927F4287361C +DF7B786E8B5F21FA43A0DC05C3C7613A61D191F2EA60FBEE725F3944FF81F342 +E1E3C6DD4909DA4E0E6FF680976175F722F7BCB6D41819E4A1E2C7D3A0AAD672 +E5470D581598C43427BAE4E06A3EAA331A2E4D8CC30978D629CB0513E3FB9EE8 +4D70B881F54151D0357E5CE28EE4ABC81450ACD3918D26A168AA5429C8A08E4B +6A3C8173C94EA22FEAD8EED6B843CFCBB5CD1CF96E970B5D125D5595CFF29830 +385526E233037BC2DCE87245EAD5F912C0D5625BF26C432C7210871670FED94F +73DB2EE957A6D2B7A03303C1824D6439B643AAE4F75B779D1AC78344BDB2F332 +B98CD271ADF18ECA8FB51E3257EF3900F7B24206B187E37AD646F1086282D211 +F7CDAE2A29873876593088CCCCD4D9D8EA663BB05D3FD6CF8C4CA27EF3428921 +EC8448168C3C60F3C1DC2E36CB6DBBAC24835F9C2DDE4D662D99AE0DA7A33317 +91355449BCE5F7490990E57B733E1C39E70F4B67E2DD738BB3C16F9FB5A6624A +60137EF56B60A3CF80ABFC7BA4363F3940F8A4DA65E81DD5E7D72359B95913EC +28CC664981A26AA9EDC90F7CFEE0679A58456C63DBAE2808C4A4983640F3406A +35F48EBF829EB1329B49BC4C470FAA0D9A5E61E23278413205453DAF9BBC7E0A +D22FDC15F38EFD7076F6C1F7BBAF779F368CCED9BF54D3808233FDC5BB600287 +D19B06F5ED0AD1889F2A4F5A658EEFE3FE5A74E86A5E845A4CCE45D9A274484C +F89C02A1E51DA1728D0742EC26BFAF66263B30C478A09F35E26EA7C1EF870B64 +12B516B91B35E02138D6EE2A757E6F9C216EAF7E3FCA7F7A80CBEF35627737A8 +F40FCC27C17180A83115E6B70A263EE4D2207DE81FA2F938E4286EE77A6D4BC5 +A77914F2ECAA92FA1E3DF6F45658325948CD5B74D11795E98CBC2F2182B8F020 +D9EB376C6DCB7EC18BBD9C9B2C710C7FD9BDA0641A4EEF9EE4176C140E9E61C0 +9D9F0B593D95C07176B81EC1D8CE5376302121F1A329DE5BDFB5C20A0EE2EEA2 +54D7A69E6E431813F099FEAF668338C1A5483BBA74C9D29885E3BD512559A45D +A97DBD1E9E9F16F9D54E681DE1C4198F6CDB9767568FCD71741B3A788E148586 +709037283014BCDB17CC1BAFEF9F91284DC52CED762A10D4C869EF73819E9BFC +288D0BB896223767038DF7B4BA241FE3D148BA62D68D50B107C165FD3E4A69C3 +D8E971BBD5FE576EF4036533875B5B12706A5137F839A486E5461354ABFA8B77 +03D121E0170F8EC24E36AB94B1A0C099DC0F223FCE1466126BDF37F4E1165093 +3924E765E540ECA2526E5E6AEBF713469A5E67DAE66E158169F4D6D933BD4F51 +3B2038C449C79C9900239E39022F14D67DB752C61E14FD66F2BD1FC8AD40740D +516E062A2B53C1E4CEDBA43EA20839DD9C4741209849E8B71E2841C739A97B07 +3D90E720F1D41294C70E4B430529548FD74DEA5AD2846785B6015D91ED97C5D2 +6C197C7C84CD088106D0C91CD5B23E9B8D86ED4A3450C9B079FF2965D0A11D82 +A382B0B7321E10CECC525BABD7A43CD00B082D568F34A8ACA756C5F12D045B4F +FB6240E5246039B39E2C3AABED3B3AAD5BC47B353057A58F4217E0BB460458BB +C0D090CD43820FCF13EA2CC91C36EEE43CB4152E4F6D122F378130B64B2F4664 +129EC23770B7A162A8CBCA6FA4EDF9DAA6671EACF2D9C5BE02295DD0447C59CF +E0E790625C84EB1CE5FEF409CA896BBC8B981011BC7698A755AE654AB2B0187F +F092098B3C104538CF26A1AF3716934028ABB362C09ADF8063C7B8057A481A38 +1DECB03FB03FE41678CC6D75B9D1E1FEF7329CB09CA82018BB9F5D33FB335645 +4A16A97F4321D699EA18AAB1413314EEFC39C5405B91759431745622114E849B +DCF9FF349BB11BA4D227648A6CFF161E98E25034EEFF2A7F72BAB61BC765630B +2CAD74461FF85DBEB32E750F734912666C75C305D6F07CC4FE71B15BFC0128B2 +719E33ADF72AB2B1528E517E97CF9EE8750B04AFBAAE9E58238A37C327F195B3 +99F0E3E4F8B964B78DF90CB2B3BC926B031A4209C341960792610CD8B958ACA7 +49FA47E55571F0334688976E5807BE58F1535DA47ACE85D976B30CE5AE91CDA1 +AC9C4AD4A36BE9CD8A5B11ED444388A68F4AAE04E89B89D6691A09C8E15894C7 +512536BE6BBFAFE7A0D2B0306C883B97B3B2CB9369704CE85FE0E309B218DBE7 +C06C139242F7571FCBF7008080BF067A0934C7F9A9FF577FF67E17D67C1141AB +B9AD2614C479F5BC6F4BC1A6BF6264C58DB2E36847EA434779B0825AF7345AE2 +7B0E2F4A15C7E6EA8E4C8F9FD614ACEECB3C3B156CEAF8CB34DD42A8A6A3ED1F +FC3A6991C6F133E306B79218ABDD593A33FB748FE49FC7880A20FB8466D0991F +C7C00758FFB9436C68A97D01A35C2B77E1083CE5AFB6EFD685EB7B20E0168FC4 +FA5E0A199760353647C3B0BBC1F7C06D6536D4F92D2E2A234B816A3152E9724A +F427972BC2517988DAC491CDC5E4D29181EE9EFF07ACA0FE458F7A087F4C96ED +BB8855AD5532EBBF25187C1CCBB9AD56C1AE3023409DC1668F04D9191C1B0747 +3919F51C55E69EBF5DDFD161C962D98F654C9B535112823C4AEFE42B26BB7020 +BFE73E41416EA9638210A73D40BE188E9DAD0C9B661EF212EA9F39AB2A0475C2 +50DB96C6834EB95833C1FD4D42CC4A5523529CDEAE9DF244F94FBCA4CA09ADDB +0B8154C1113AB1688AD38B763B9B36CAAA77490F88D98A893BF642836CEAF6E9 +8835C11A4D2AD2F8EB20E269583C466D96B21AFF8BA75D7E766DB15475E31FA1 +8BA5BFC3C4E96C9D7581061424231B3C21003B6250CB71A9033181564261E8A1 +AE0FD34B0A9FABF1FA2D9B428B932120CB0B80ED3FEFD08A3BADBA4784313146 +9CBDF52AD39C81249626ED19E9200C9608C93F5EDA48842E6BD74311D5DB3F5B +4C743C7D8A12A49A6DECDD0477595B789AAC43350914F15B86A89720D30B767F +B13CBA0D5FE0A0C22C2C44DEEAAB911B169F58039AB084A94B3D3839A8BBADBC +9D557250A4F46B05C28AC1F5F6628E06D15740F4359127479DBBC85AEBF1B8C4 +3759BD0D4A4C98715EF4212EAC7E40D15D4363A495506B030AD90B0E662504A1 +40FCAE6FDAF99CEB0747D1CB287B2D482A2163EA3F860FDB1FB45D476C8F1C01 +13CD22AA1EBE854C399AD2B546D2DD5E8AD912B51F09E3F41B9CCF36A11486DB +0310C647B06815AE4EAE035C8E9566A28D9AEC9E6E4C827AE857BC3D8B793463 +74FC41FC35073D0F5F79E444C0B82A1EEC7274C61E6F3CE12AAFE50693FEC720 +8C8E2CC2C0883D861521E463A52061CD16F2DA18115C0C939FC2EB4B1E6F48D4 +7F29A30F6287911365FC5BD74A5E4A0D9B60C379BB755A4AD5C5969B12EC270E +AC38B54CEF0E9D381D5F6D9B8AEB37A67943C8DDC5B4051B217BDA995A504326 +72645CB04CE748648C2EEDA0735B5C11F0ED35A9C09FE0A022E65BC0C22F78DD +9B563A53D33ADABA1394 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMTI10 +%!PS-AdobeFont-1.0: CMTI10 003.002 +%%Title: CMTI10 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMTI10. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMTI10 known{/CMTI10 findfont dup/UniqueID known{dup +/UniqueID get 5000828 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMTI10 def +/FontBBox {-35 -250 1124 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMTI10.) readonly def +/FullName (CMTI10) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 45 /hyphen put +dup 46 /period put +dup 76 /L put +dup 82 /R put +dup 85 /U put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 100 /d put +dup 101 /e put +dup 102 /f put +dup 103 /g put +dup 104 /h put +dup 108 /l put +dup 109 /m put +dup 110 /n put +dup 111 /o put +dup 112 /p put +dup 114 /r put +dup 116 /t put +dup 117 /u put +dup 118 /v put +dup 120 /x put +dup 121 /y put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE32340DC6F28AF40857E4451976E7 +5182433CF9F333A38BD841C0D4E68BF9E012EB32A8FFB76B5816306B5EDF7C99 +8B3A16D9B4BC056662E32C7CD0123DFAEB734C7532E64BBFBF5A60336E646716 +EFB852C877F440D329172C71F1E5D59CE9473C26B8AEF7AD68EF0727B6EC2E0C +02CE8D8B07183838330C0284BD419CBDAE42B141D3D4BE492473F240CEED931D +46E9F999C5CB3235E2C6DAAA2C0169E1991BEAEA0D704BF49CEA3E98E8C2361A +4B60D020D325E4C2450F3BCF59223103D20DB6943DE1B57C5FD29DA32D34C95E +2AB2ADB3F60EEB0600C8ADE15A2380DE10AC5AAD585FBD13097B1A7E8E210D4A +EE96785449E07F0C8EBC2EC5EFBFD0897DFDC15E5BFAC9584D8DE95C5AB288CD +8AD8B9BEF0B8E5F887B3B0B331542FC8184DCCB753DB6ACEEF98B85756B988DF +CAF1AE0DBE7D37D5F44A2E760AAE3A5197C27B15E32275A64946C3E4D0476FD2 +7FDE148C788DD2106F7C825E270588AC05B57E625AB17BDD02306F9E5FC851DC +32A5A6EDC43C770A71419B2C0C8074EF3F222C8A2097CD81A91F333A521B3A09 +482A4FE1CB231CE344AD126AA284C3280AAC3AD162CF0EE241BFB4C8F20502FF +118507F5D1B5FD898571015E73E5CF2281085072E00D401F6F59761EEC3E8381 +1F26F75DB66C504AB6BABA87D121B1E7040A07AA2FE01F80DBC246CC03C4B2DC +C2A715980C52B7F96BC1A78FCC7F4F52EEED5F705E08FC1E5BBFCAD121FA88AA +8EBE58172C162AF409DBB0728F14923ED02A65EA24E5D52B6AD07777455A70A4 +61833D3789C719BA92E901232599767E423D5AD9C807670BE0E7B5CFF8256A20 +C7BF7214FFE0342809570F5966A2C43E784F35015D9040BA34FEAB6A6F089504 +3A40A9E9D711A2721D3F4998371430FB3C94BFC619559B97D49627BB630F4B70 +9D0A8FE4E916235335C3962F3CFDB04C4A3CF714DB5E260F4E66FFF2F27CEF2A +D4AA26BBCAED23B8BDC98F8F453BA27AD7758537561E766B82DC3032E92A9EB0 +125D98A22C5466AF069BF72A9BFA052A8628FEC6A6AD0B711DFFEDE3AA2D7CE8 +34EA487038EF50F953B8B4471CBA6FC3C53877EC1BC94582B1123EDF44B4056A +30F49394BDE22CDAD7F01951C7013D26979277D18EFA594E8F4F2B5E615187D9 +39E842EC28461B9ABA52020A127D2CB9002A673A435B13C10602EEFDBBA6BD49 +9DDEAB9E68D655443A5C2492BA061C1391A51592BA8C353A6F6A0708E8860184 +2B5D031D2CAB87D618E9F6F7A0BF3F66B3FD5A25BB91F7F1F5F99CFF56EFF4FF +0A35C55658001ED2E97B26C869292F6274D433A5443179DBB8EE987196306348 +3F9E87C6422AFFDD30080C9AC4EE7FE5E2DCBFEE4974331F4AAE479FD8806D4D +9C2B85FC69EB0453AD827A1E767E5C484BDFBF5C8D6E2B3C96298B390F22D757 +802643A79D5E29CF3AEDF0E12CFBECA4663444FC87F2027571DBA9ECF688BF28 +FF0DDB3AEDBA0FB28447CB4B5D5205F40C1E7A525FD7373392EEFFD910AC82D0 +98E71660A1B3227C4A2592F3E853CA4CDF64DF19A52582E167234F4036FAAAB9 +5446BE102DE2BF43E82F0112C2A20F15A3F92C6571AC761665A905362C4F8BDF +AC8705519C99862CD9C0D75113C4AB5FBB83C880E46B82715B5628890D9103AD +A2329638B95D93C4DECDC5E6C588C9D5183EE6FC28FAF9825F02DCA567306D93 +5440987A81B51EE7291107A08F201C609FEF91A8F0587E8B13D4BAF74A5A6815 +DE9E4441F46AF8E1DDDFA2D611C889614040B144A5EC064DEE4638C04EAB2E37 +4CA8F50FB8C4D65BB296DCCCD39F1F554CFBED96670A91F515CA10EF896874BC +8EF48C6447752C70FF5A06F928DB55586354076773BFF7E94C4C3A7A1C1F421B +A9B4E3936EC26E0C19BBBFC90F021E877F54B62108F6DD1C7F6D5B8E64FC9362 +E173F01BF2904B7E5A08B3543611562C2714099DE7D4FA330DB148B560A9601F +42A84452811CE213DCE782A0D7809CFD954D6BC1EBF2BA4D1B18F50FA8174C96 +3E0120E266AD5DDB40B3F6798AC28CDC5C3C4BC34583528F5B5DC8A222B80B59 +A3A93DC715D061EC6915E6E6E21A25425C25E8747C60F170D61047108826F96F +7830E220C108B441B6EA3198E33C49BAD8D43086E49F5A2BC7958A1A8CD011C4 +49045193394696EC3DDD0BE084E8F2E9F0B9496F035C0DEC1CE11409DF566428 +D50043CFF5CDD1092F6E0807E660B68163BCA738E8D98FC6EE3F713164CD204C +0BA84FFF4F33F47BC31750B448603D7ADB9AE92FA91AEBBBEC0DCD66980E6955 +CEB425ED07115B24E40F53B29B9D840842EAC691B4F591F866DF27556474B485 +1C6F53DD72499847109B16C7093984A6B8487D4F3870DD517945CD90E648C1BB +8A6861E540FCF9D75B984B5009B5CC760CBE297042C240DD624111670B703388 +6FE6FC0E89C6B4C88F51DFF3913D0CC1FB4770C8CBEADD4B86393605C0B6C468 +83CA5594754411B6FC331EF56D7CD6D247FAE42E966583C29239A8F862348D29 +60B177984B6B957E733DB4D275015691D91443BBB13C2DA96097A29733CDB284 +42F89C85A7A743338C9DD3BBC4EE53F695E5163E6E1ABE5791ABF100B198B9B2 +1C21E2FA2FB4AFE7F9BB2D381260CDD3A2CC05BF513AA1E80ED69FA27BC5ED5A +21445BF00BC2F997B356D94AF13736C6D3B0613EB6F4CD96A685FEB672661DCA +206105EDC3CA07900676EB2FAB37F48D2E8207BDE1463894DA3C5B1488AC1EE9 +D39DAF691648048F5D7A384B8927F8DA2BE3602669F71D80686E427F395134E7 +7ADCC611BA91AD4B7A0237213C60CF2C905359C90795230344FC3C50A22BD44B +55B2044792509F50F5C21F53D9F9E9F063ADBED3AB99E2613B23334FE8DF70B4 +6120F2EDF69F50BE793EE145B9FF9C73179DE640FC2ACEB5C6617F918CEEB762 +4CD81E665B2E544864D13230B058717B207D3CC5D6647D5343DB4D0356082392 +871EFFA896631A7E0D6477942B632074A9A4EF7B09D4701B1639BAAB4E03A40E +9B54A7A4F845CD63F88831EBFA4FB847847CB98F3455CB5957F2E0A0F5623645 +DBB5C5564C7F8B117D6E27E65C0F3EA81AE67B4AE4B201E7C4FB0A8364FE53F5 +41A7CE8F834C2C4B322809B353A5E63BBA7BF3B7DC1A85EA700BD287C2BD3FC8 +2832B0BB4695FC937FF5EF06FCD87DCE6DE793C2B1EE10E6450352C17726155F +220D550B1759E15AB2C1D5968E52C8080CD280E99D3CCC0E80C2EF8BBFD96001 +A226FEED7311EFB4B67F424B557A877379A15BCA54780F0CD2CCA00400B9B39D +981C6B552AFD2506D1B23618FA9AE6D8143CD7198A8482CB416CCE62B992347F +337D505A4078713BBD91E5535BD58EF0351EBDCD749CC24D4AD39F8CECD7D6C8 +139756680A4C03A58B3374CEC658D30160AE4863A3938A891BB59CBE02BB451B +1BA4B2B6E68AB61DEB85F95E3C909B8B66E220B9F18280161C279F10F7093CDC +100A53D542F071CC0A5AF834DC1D18738F5DD62A5573E884E1FFD22BD810828A +1EA47F8218C15A2E97CBC609927DA3CC2B802EA4A0D7EB57627C135E3B065905 +F97597D818A2C5CC6F328AD25AD11FA50F1E4FE637980B7474D6F85A521892FB +72989AABEBE02A2D0EFE88A6F67AC29F5D8DDFEDAAF465C439983C6B84389FF7 +A6434462BEB7B07DBE4BBA61ACD4A60C55B5C0AAE527DE381DFECA2E6BAFDC8D +310364ECB42CAFF72BA93C067B2F02D1CA7C34AE7CDC46787A0E234C8BE8A928 +7A6F3DDE0338FAD532A9886E8E3525B85DD39364AB03EC4C0DD25DC179CC1989 +1BE232E387E857C78332D834679195E10F1E7B87B7966DA3B2238F53D1E13FE2 +8F55ED6A92A750C7250C9B91E29796621E7E9520373214D7DA81B2875A986D33 +80382AFF6DE1F829F048E57664D9C4ACE91E4684A51023943A4964AB5657D610 +3A5405EFD4CFD1EBA684243E15093C9667797BB47617B66054EE02C41FFEC45C +C1BAE8AD56B00D323FCB1D2744F061FA16E161988741A319B1564E04BA210996 +4F9F02A3268CABE450D166A763F5284954564A1C86B76544C5F5ACDFE0D758DB +865A1CFCF9FE8CD5F9C3B2998C56468FD52DF8EE60C6935A3D221EAEC7714E3B +301371C7DDA0B03A2416238F2B47BAD3A2C5021C886DF51C695AF9C87A864B48 +3BB3FE0B355EED5454B59B25A0D8A1B8CBD356C24F64D9B55E16C30C011365C9 +1E0380753BA3EDC0868788D5F50B9353D0227BCEE1BE36998B2622C0759BD66B +E4444250589F9CEDE766D8B940770CB6B89503E925B35C00CBEC2873D2DC4A29 +0823FB7A3717B69A7DEDBAAECC067949932728E89BEECAA91DE3AF9BF070B9C0 +30EEFA8C0A55C8388CAA2F0515915C98E67FA095BB98967D14B0DCAFA9622E4E +2E0EBFC768D80585ACDF28D8A5C2B6EE2FE7AAF62FFB90F569F84A0903996DF0 +C1D5723366C436E4088F3E2BB9B47F9789052A71CF5C49908CDC1DDA194BFB89 +14D7E3D7D4D72A150FD6FFD8303E9DE5A97A71B808B8BDF2AE466F31BF5D7A4A +44F81230BBE2B456A221E2F72A8B59F8FEA8D31F8A005A5BD93B9F49CFDC3DCC +CE2B67090460F632271C7157BDC2F05BC2749FD562FC28682A616A52D1B67654 +DF78B7843A9EC26A7DE2EB168F874904C2915B97534B2D4D9F74A9573A771D34 +9F7BC855E8F794621BF6AD471BCC347E2DF5F620F5C209E33A4CBF1EA85AEA87 +4492A77342DD33EF615FF34037D660B713C908786D9022051B825226545827A3 +2AD1B05D654DB6E6D261B4E8AF0933AD1F0FCFC7201E1A7C1B4199F160C38676 +21ABA2DDF1CEB655B3EC3226E0B122976EEA998F7A5241F062E54AD1DFD6ED26 +47C99A439E0AE95415059179867CDD3F0FF751F3141309F40E00A6C7C28433E4 +F649BCD5DAA64177580E05C495EE7BCBCC5FBF104DAF360CC2711386655B26F9 +D349D887EEB32ADE595241560FD5924A1745A22E6A01DB9C285EF14596EBFF0F +03F36EB2E0A7C3864F819EF7B0855121292D49482F046A55CD7271FE03F02EA5 +886864D9D8EC22A68C23089EAEFFF03DED6484D8C341861EF8B6FD3C5BDF5AC8 +352DA4E13A1E30D0CB71E090E9CFB9AB2CAFD0CA7C34AE7D8E3B2EB4666834BD +9CCD1AC2108348AFEF6071796F4BB2FFA4A67ED917E76A109FA2DC2A30D744A0 +9AE653A748C1D18FB52595D84E87F1C1FB6B2F32667FE203262C66627AEFFED3 +92B23861E5EB238BB4EDCE09DAE1C65BAFC198CDD1B45D42CDF93E16BB82D35F +821E9E49067E966AFAB2AB52928F8DD6359984071FC37AA652FB834A09E5BD93 +3AFAE161140E74C6531E413E8FBBFC42BFE8A464B71EB1D8CAA93B33D7BCC3B0 +47C7EEFCD3E9FCF26FF9441DD9BDE68D77AD7251C06BBB9A2103049E8827CAF0 +F26BEF33F656A690235DEEC623CC519AFA82DE2AE16FB99F780FD7D8290DA40B +9B604AEF36B529FD184239E7D50561A07428D28E51B55546590A1AEAD4B7F2B1 +AB8C5B9022C1FA03E33F8F409B24911AB8BFCF6EF4A8E415263C789F89063E71 +C0910DC20347469380B7FC1EEB87D4CED7F4A361E58B61C91AFCABA35C03F978 +B9FB5257C31657EE48504C355CE893FE3C553274C641DBC4004F5D5B879CC5ED +D3F21F867F6DF054127067DE86189F0B59A1B90FDABCDFEE61423609D888EEFD +F4A1367129962110C651D9481CEDDB8C5C2576A59AED64E95F7ED042AEAE2F7E +81AC0C408E593DC30DCAC334EDE9EE27D932B98F040DDCD195D6155607DD2038 +970EB78221A94C52BD4F0EAC65F1FC10E5DAA93C17266F351669CAE56F42B68C +6D01E1EA03AE554D63CE76D800FDD9CFD89F80A241EAEFF7EDFA41794EA25CE7 +97BD5028464D2CD45B53834B4AEF8BF0B9E7C6ECDEACEC887E8790A47A93F668 +A9095E5FA1116A122C0E5B74E2226C654D3187C6CFD8807917820423DA3EC1DE +AA020EEEF2280C44A15209EE2F3FC1776875308CEAD38571E7BF889F287E4594 +971A83605E0B4169D4A23EE790515223DF8724054EDAD905F57918FC0BC64F96 +514B4BF7DC9BA79E763C22C977FB6146B10D26FEA1BAA7BAF21312F78D1625A7 +8E242D743471DB5821408AB786E4A7EA9D35E30E85533C617689F95758FB2C7C +392E759C299DCCE36689686DE0C4DCE32649493650BA194A6208C5EAB670B170 +3F2C70BF0EF0E3BE2FB0A79224FF4ECECD6BB3388C6D06867A0E5E3DB93C1B2F +464C23E44D3132E7D4086E3B59B1D13F49EB4772DEDF8EDC4F603217233FB7BE +C13C28648E9AA51D53F11FB896839F97AEDD8834BCA53CB0021AE91FD8E95E2E +F8A094093AF556B9639F508A401542B06821FF9DE1A745FE9AC5CACD5E8E1053 +911442FC15CA5333751ABFE2C617D38FA1DC332BFEF44AE569DC631C93EC54D6 +261583A695F5A392867A57F59B741EFCD2DCFECBC55D1EA5F2317601C9DFE9ED +D1EA466210FFA905A8F85BD58B98991BEA58DFD1CDED5C9B086D42CCE632DADA +147941917B879139E016B0DDEB8446BA017FC8EE5A354533D667B0835F5D027D +C2D580C16B80B3D05CC92C0465CAE077729F0A15B2DAFC89DCD349B3F81D0516 +C65526EB5C10E45A8A85D716EE35FB9AB201FD7C89ADE5AD925A174169DA20FB +61E96C73A143DF964C20589EF24A0FCFE6195317F2FA0D2249C0D8E649C3D9AD +FF13332EA2E4C9CD36D8443EC8F027B61CEF92C6A6B72DD4ACBACC16E429A9A3 +F5F29C1631360E32F8C1C93ACB22F810B86D2969A7480F486F62F8488BEEC74C +2C1AF13BB92BC578E8CD30BEA6BC8CB68ED730F54CED0167605FA76AD7B7E88C +7AE7688E598F91C471BD65A542E96D64B1EAF19FB4F1234308C48C2DC86E2193 +11ABDB4C6189C6F201627C693691A86DD07FF55C30FDB3F72381E09C6080FD7C +9182762E5001E30F52A216E0B71E4D2D4E2F3B20F95DF3A11FDB2D2B5B5FAA66 +C46226D5E0C77066349770514E5675550FAC9394FB27CD2C2F974F1FD58C04A3 +1EF53A8AB3B2202CCA1CEFA66228E1480A0709436C44BD3319C40CF888AE4692 +5DBBB52B15CF3A518F627F672135A24D5DB9B2EBEF04C860AECF231EBB5A3BF5 +6DCCD5E72FE4B6DD29E896691868A7DE4120AD06AC573F5608B8449B38E71CA0 +EB5CDA3F942482EA7973661170F81DC88D54DD5B92323F46F833DFA757107E9E +F62A47CC50FAA1B68ED535C3E0E1073532A05ED339C8D70B3B9864808ABACD23 +AA95E9FDA43D54C66A675FA074E0A5B8777D3C07850A09087F36852B5351F35D +8BC4DDFCA35CF29CD5E3DE118A741FAC4DED36847F2E2C6CFE08669301722D94 +376F540982958074E7F1383C409652F6C99DA39FE90B38221E75BC1ECB93ABF6 +B00F410A0C5651DB418566AB350FDA1789AFD88286AF3BCB42B98386F7BC144B +02DEB8940D20A6B3062F0C4244EABC50923390064F1D027A8BACC3DE45156E56 +4A942D1B87F1C4A76B0D4D6801AE792CCAE3009BF25368B31B6AD5476FBD3BFF +9759EF463EF5E78E10B7BF64005B2ABE0E8813950A08A1808587A98E0021D0DD +751AD515E8278F1A0759E85D8A084490BBB0F8206484AA36388B1013643D3198 +3509078847BDAE08E76FA5BF3E3A73C323CE093DCC148E3C02C2DE1E26C94D5A +40EC8308ECB02FF7DD04EC1005A2A0DC74D4E587F10A3EF349E828F69FD38962 +2F0C74D5DAB3ED6CC9F97008ACCE74C086A503948DEF1AAF58FC8BEC703CD360 +D32098A56AC776B1BD08442052A2A4EF6C8798F7CDC102AF1A2009657254762A +0793F79A39DCD6ADBAA5EC84A7ED6018BBE727E5D477893D84F157074B24C13E +8D4881C7DF8ADC13EBA0D89745EF93B7616EC5355600BB0D2B630AABA3CF2946 +AFFD0B2B724EF0F28393F2034B2E69DA5061426805353EB4D80E20739BC4C510 +6C45275B8261DCBA10DE1D104B12F46ACD230977EE7D7D1D35D2814139E38C4B +CA6937CCFA653349B1EF64A98457F7B4B5D8F2978F16ECCEF7054905863AA46E +DD524CB33459220C71E9EFA7845A3A760A507B3D3ABC525B35930B613710A13D +098832C58EBBC8B0CA6AD516E6385792C59220331D0922A1F6F838A8DE13C337 +900462F952EABBDC2EB1FBF94A66186C177501453CD3FE3582073DD86F04406B +41B6AEB440DA475E13240445D46726A6D45185D56BAB8807CEC8A8F7CE1AD149 +7CE2E1BB5DE4E5B9592241DD136479A65905FD0062C91DFF7349874BFEA5D9EA +2F610ADB9AE7757B2307A1BB9D6797D9F9C4844A59841C7C7682105E23A374BC +A91885E7410F56F60C29AB8B417E2D6092F8BB70A2DD5DEDD4BA1077D7CC62FD +EA43428C6F79C332342E15F75B08A1ED360B3511F823E75AD49BA7AE63B19238 +2AFE8FAC2715E2FDC895E95036D23127557837506A3B542B0E4651CE2B89C252 +31EE8ADC26E2C04E8E30A9CA12F066CE01953BE7867171FF6C7E834742C36C3B +58E74E4B482CB85FD4D24DB03D753F260A585D552CDC9E1941446F2F5B45FF24 +2DA4932B973139F328E7E92828B900BFD398B6F41DAA0D6861C66AA7F5E3299C +87A5925CE0E0F9E09AAE0792954A1F2C0AAA8288DEEFFE579E38A3CE8A943EB4 +55322A87C1634074EBEC25F724DC1BCC1BC10458CA6C4395659B0DB6B612C151 +557CC669D8DC37769E59A5AC6BF061C79FEE265DBB59520EB8FFEA273601D1E8 +2984B8AE31AE343F37D03E2BF97DC48AFE50BB6138C7B9F9B5E28672A37BD8F5 +8F8C98DC43DB22C6537028798198E2D3B0453ED72487267D653DD50F1BBBDA92 +833A987A95FC1F275B90B581B4BB62B6863A4CFAE37F715EDF3EA5A33679FEB6 +4847ABB4B3D170C275B9F1AC3156D731198DACE0B051674E85B758500AC9FBEE +ECC75EBBD85F8D62AAA328FB09C6526F853077AEF7EFBFC2B6A29D6D508B1E19 +EAFA4C67EEE44045B9F15B9762B3DDF5CE5C18B23A5C2F73A1F6DF7F8679AB78 +843AA41FD2A7DC02B45B729EB76C66A89F5F76E5C4A0C0563B1EC5E75D72EE35 +A7F1FC89216B60D82F6F2B8DBE85E4FF4D63712C689E696F60B52AB622C2A4F9 +37C380775EDB72638D3F81F61D8D730640A031D5E76B5A50B324FF8CB6D27941 +08BBD40548A9CC009B87404941B1C18BF3AC52BD2CD78C5BF77DE0552ADA3E14 +BC0B82F2CC37AE635B8762953D0CE34CB2B48DB39EAD4D6A8939194DB0FB0DE4 +2097ACABC52E9DB146BAA3A8A02B7C530BED308AC87039858CAD24FFB5D67500 +0565199BC15C451E1A15CFD9B77CA5DCB240685CF1CDC59423D16196A1E3266F +07BDAF48BB644BCCE0B7AAEDB13362CCB1538C5D06638D680963B578D2574388 +D48FE717B30B53AB4C9D9AEDCF0B554E5B60C93FDCCF55E13D39D1CFDD9459D5 +6F76AE0F5DCBB866F3148AE22AC1F787677CDC3C2F1C7E8F26A28904EB2D5986 +B5BD875EE980E0393405F629498E1D77C0C11DDA2EC873CC2BD4C28604E04467 +79A2AF636279A8B02475144354DC5F63288B1C652C79BE189723641634CA6C9B +6C1C7A790B80DF1048260298DB6F05509581C1D43FFD59C34F8CB805BDB780C5 +02C3AC7D3CF9940D140DB3ACF03150A9821273E36336E975D68FDF0EDB3A2C78 +66C9DD71593AF85079B20B98B5E873C5C5EA0B218CCEE64FF6DADA390B4641F2 +7001DBFD5615A35253D968BEC1722CE2A13645590B04C5D3AD5C7BE95535B486 +F8C805A2EE1D38F2EA58597C4526D5861D0184F4956DB9D9BEEF25BF02427893 +F786226FB30141D2358C0C3FCFE8EF0A4AAFB5CA8C59221CF0CE2BF5C2478B9C +D66C6DD022C3DFA2EBD3E4BCD12A3E6DA962B53AA3B1732162C5A829AF0EC535 +D0777DACA2C8796A78BD525785E5034C5A4CB78AEB1AF1861BBCA30B1F839E62 +930257A5340527D9658BB19271DE0E17E64F46D9A250A2A18E0FD974135FB8E3 +A225C505B9C7416EB7A6786D306C964659BD0E3ACAC93C4FFB01307D91B1550A +20A090C72AED05B4A74F5532A769FBF7A7ED2D55C9AA222C7DBA8D2CDBA530E9 +795CBE9515C3B0D00062C0C5D7494F83CD31B99110EC6E12E6EBA5C28FAC2B5E +3D99FA2ED03C476476D21348AA585EDC3D08F9B4EA6CF01B74E0D2C8EF28E6D3 +B8552454698A220A4A107E01940961F1589EEC9C87A286ADA62CD96931421DD9 +8CFA4A0B38F9518C4F050B07BFA39842B562CAD6FDB60F649E7A76356865E5A8 +D3081EC10A2CAFF9CBD106A8632651B80CD64CB48043322A44F3076A505460B1 +3C00053D8F1ACAC4984F6B3DB8D1C871BF609DEFAD7EA90D57E257F4D2C388A6 +D994C6FC93ECF13F2C8B305876FE263A3BD1ED1D3661BC841E70680FBA10C178 +8EDA8105D3421CF0AEA5A31B5CFA65DF3DB858BC36E275B8656734D5659F76ED +E3B8954FD46E296154A383239148B3069CFF8B4CD411F2B81567DD56554994A9 +2A1C2C7B62FB84CC343F14B2C5627C3CA50A06A0D9C94E03557DE89E869D4596 +1C0E072F283E9B213E23B3CD838CAD85CBE529946A965AD3FF5D4ABC65FE6E91 +47CDB9AF7757DE581BB0743F8B71766581D27A5ECA31F634ACED04E12DB04D91 +D14DE0D2C4EAC162549105DAAB7DF61175F015000AC20414F1EB604CB7A7F5FB +C4EF86D19878D5E75120D6B916573B635DF0A3262BD990B0849E50D3AD0BDFF0 +A19CE86495C760D926DE543CE80CAB8F407965642C10C65E78AB7757E75DF93A +9F3628CFA73A322D4268A399AEAEC4BB868798AFFFF6890EEDA14F2D8FC95796 +F73B45039389176F42752C0561AC2AD9F7A2C4E7195D34CA26568F78981B480F +94D87A519BAA52E8D1C847E3E12B493D30939636AF8ED7ADBFAE1CA3AC4844A6 +C796E47157E4F6ABEEE0FAF205B105063D1114DE7B470793CE391C20D7EF6830 +6F71A881A2483C28B3360A04AA5DDC6CAD239AB33E8DE6B672C42288383069BA +7DCC02A63EE07DDC62BB8D893CD4C5DD231F0CEC9B2B68B30873B09BB1B95756 +95FF12691E4DE6D9C1B6B62A0ECDFB5FCA3AC89533C3FF2BB0C5BC048B3BB144 +A9642D2C23A5D100AC74C8169BF950EBD0D623D3B12B7DD0D9DC432FE752F91B +8C1EFEDE847B9FB6853A07F7BF96E10A2ECEAD2CA8E55B40B72B160D4853B77A +8FB1E30E32B72D49BA04EAB452ECB769881E9D94F0A8AF6EA265639DD47D6451 +89162EB4A741E46AD7E08DA67E610796EF90A51188989A94BCA73D14BF668E2D +32773E101247108A342F71845FDD11512574F2F2D56150AF7C3572CC752736CB +92928424F58C1C4C20E47FA196EC5FC46BDE3437A2F7FA284297B52A2D2259D5 +58B4C3406EDA877765CE1205D2BBE509B11B3FC976A928DCCE64D468E938BF7C +A245ABF829EDEDAF65612B50D3F2CF76635A42AF3579A2827D09B8EFC3ABB94D +FA04BBD63184293319074E6CDBF497FA69D3FD282B7D2E96C436A4E3C447B6FA +6C7B5DB4598D8AF04ECE6BFE118EE80B22797CACAC11C15042648E53474E43DA +3C3A2B1359ABCB9D94EC36E7D6C8E24D3D8F8F633255E3A9254B82D9EF6D214E +683E22EDA23418648CFB576421E575F08F325CB37867E3AA8EF77DB1C13494A9 +A5EC6259B53D3B37E9D1C8B0C91BD846636B19AF26D5BA6114E210FFFF32ABFB +BD5A4723E4FFF0A5D3BE63A806F74C8051795C7A4F03D85DE8D9BA387C0457B5 +FA1521EA8C71E8D27A07A276545E647B2FD802D19E5AA9A6D6CF9E33C0339627 +5282159105563D522B6357BE2383E7A539EF7ED32BCCB636650DB281E3AFED99 +24237FF46781B603CA1F5C99BECE3EBDD52EC1DB3F02532BE24AF706F238765B +91ABE3A9FBBAD9547E7AA9FF5891A024551FE45B24D62B6BE6E7E0A899F6C4FF +817F72762ECC0EE633CC6900A9CB3A5DD25629DBF2DCBEA6360429BE1D9B6D5A +3669931B132141921F090557E1CBB726595E2DA54DD26E2C38E4A35820448945 +40DACA669362F4FF28E16E71D472E75457D34214E9CE049FCADAB97A5A6511FA +CBAD12DA3C2BD2F0316D4B542A9921D4DD4585A61714AB81694941D9E0BE5C1E +7F892FECAE54BB038DE31324AA2CBFFBB43A1B2E27D6A96BDC99DABEAE2459B1 +0FF2231B065D29FE56757238CB33A266FC66F61E4F1F96D2F3AB38B8770EB897 +228FEFFCA3E7A5ACF2EDC8A8BC218134571455E76CC07CCB3A4A706826B0B6A0 +07D6446CDD5E5E97ACE3DC8B3A7694FBA645AFAE422C85CBBBF455909CD77A93 +18EB022D050F6F2799A4A5418CDEE457623903078AA403EEC9AE7B60C091C4EC +97DA30CFFAD9CBD1316668D05BEA9A5F9059DCA98025E1AA0A792D2D33CADC36 +6359D6230F194BBE663942D40F9A0F8D1670FDFE26270EC7AC1762034529A52C +47B14AF203ABD5891099397BD9401FF9A7D5DE4F31CDDA34F87091FD5F6B1E5D +6A1899B61E79564407868CDFAD67BE6F150663A988FCA5D9159931E645D774E4 +6FF777BF3EB95462CC696280E0E9F1C2CF39DB04679555FC360FDC844B3E2BEC +8D75EBB8FCE89DCB8928AE97D30D3F185C552FEF86D4121A51E75D05F43E9F28 +E994280672DE8A5AC107C35C6060A56CDFEB7391FB52B41D3678EC6F242CDC11 +4E03E17133FB5D132D9FF5CDD23752CEACFC20ED2F844316F5098E4EE9328F3A +F2B650DABB0E06F4AE0824B3E4624A0959EDB6BC263235E02FBD18A4E5EE04E0 +5CEDB0088B247EDC96EF9B86A03FE4B544492AA975489BD8C3D40743C93F158D +F9F1E58B9890BD115CBA34F09395B06D0B2C307EB64BE00ACB6201C2B808AF86 +2B3EF4E1C75716370621BBA370165D177BDF364311E93BDC67DF49A4565BF2F6 +C8AAADE63A712E8B3B5084B2E49F61980731CB694EA3BD6F79B88EEEDC82D8FE +155DCDF072C4A17C872DE72F20D4CB2AA612F6AED5EFCDF58A554F43360DE449 +3255A9912ABDD92A83B49EC2722E3A4BD5E08B1A24CA1553E4FC4D1FFC34B7CE +0F020D35651B0CDFD5D6FFB70502A24193CF4D5FB7D22C44C58841372603E761 +332DA6BF791FB89271570798950884F0AF4396120AA987D25C1CA7DC02943C0F +A7A77E392CE93BD636F067C8DAE51ECD82B9A049609C7382435FC9ECA0219F34 +7F65220E077F3B1E925FD83533F74288B5496A7368F701EA40FD2BA8D2F04F0F +4646F8B76ADB7F8104678F009576543408B417925E91B65C9FF25586875E91A6 +709F662F43F77971E0DBDBAA22B7EBA487FEB39A4E693943F0F0D1D2704A3709 +0340AAD38D6DC5184E07AF236BC69EDB3E4B83CB94C8619224746169F38D2BDF +3EC3947BD5D1682F7378CCC006FDB84508CBA7A642430AA85F83FAFB2A80D9E0 +74512C212182415D48D027E62AAFB8A165790167966CA801B111AA5DEF0430B3 +3D15CB081D7ABE5A0BDC3540197C2E6682B89B1FD45AE6E1BA5FCB957F0F3291 +87E7CBF10CA0F10F9E8BA2741B431A868A4A8D5EC423985F63F76FE4ECEAC41B +6FF8CF3518F1321E421BF53AE99716B485BF4B901287B73A5F6CC4D2D0837F6D +D14F00AB998ED630CEDE548C5EF7ADE1C37C68558B1AC33538C11A6AC9373C4F +DCB66B6D97BACBC54DE1B7AC735F1DFA83BAD86B759210C4441AD07DC5147F20 +D4233E8E97E0EE5B054E35BB1E4A4E68F4C74465FB77CCA13AB7290E9851AE1B +A17ADAA7CE1F0B63D474CEB5B5D3DA322C939650F35B0F908E9DE9E5ED9AB420 +A1F9E43B8763A0530D8BBB8C07A9CF7E3E07C4D00E12F96D4F68900350D63A4B +0A8C5FB0FBB7228B41838D1486D332BCA58B400689D740CE2682AF3AD7826E50 +079BB9496723C61485A6E5EFFC056B29F8AF0B64412C697C333884391B38B6C7 +408F8AACB7120CFD98C353D76E289139172279A92773BFEEB09BB4AFF479C950 +42B4E88B99BF458CC6E0EB7D460ED56C3982D5E9066F3775C55FCE598823EA20 +A214EB658F9EF8EE6078D2A391DDD9821296D4600013FF666495F5A5313218BC +5337AD549709C833FA694F785B4E6AF78BBE74C17CEFD4414BB362426825BCB8 +60634977571B622979F5B4102BFB408098230C07851A97EA9E56E946AAC5E47B +C6C3AC81F391448FB84E269FE7243E955115C18F230D42E97EB4C274F81D6FBC +8B21C1DD0A6822A171825EFE0572659E8DE69A8A739485DB06DC8A16AE326FFA +9CA4629C82380B2708A9579E1C2B5B53FEE640F06B44E17DBE3520F4D238B7ED +A1CA4AD4F963211905E1C9D60447863BBD344AB5621E3379B8D9717EAB4CBD52 +2B85AA6685DF7B9129A91621DFB11ADF765247DC04615321D0D1440E1D0CDF21 +00712AC98A1F58929B42CBFA5AC8535FB2B81E5749A4CE8CBA59746C4467EA5A +0A05D863C921DD5601397837A656967D0647BC4CC6B35EE6EE23FF367E93C336 +22FABA3A80751635AB1F6AD0EEAAD69BE680BF12103ABFCB7398106471058277 +9C1A8610CBA2A76BC8CD2D94DC8E95800FC62BAE58E5C8B5CB181071AEA1E84F +2CD1956FBE305AE0D7BEF76425C7819C936626B5D0123A49A204E734ADE5BDDF +F7024E8AEEBAE8EE85AFEB67521EADF982EF932AA773FB07C563CCBA5ACB5364 +DBB8E7F84A7B2EEFFD2FE243797BB9C6BC0870E52C36949262CFD5C98D334D6C +A1B13DAF18238032DD0825D7F05E8C2FB5924133A4BB590376834D651F8CAB2E +87EAD8212BCC62F158DEA0F1E48472963BC4BF68ECC10D5E65463D3E01DEED52 +A6A2D4FF67809BC5D03C69E8C70BFAE98814420EF2C0573CE0342FBF5A68AB2C +5981066208FF196DCB116701B63CFD8C48D5618C2FFE2151F2593A8947EA210F +1CD0CCB2339CEEF26A2AE15F857AD3E26298C29FFC7B1C9640C7DBC67FD59947 +14FAF860E02EC2A848FE450F55A67EB12D2062B867FF1DE6C2D24A39E51DB73E +AFDF18418B241DCB72ACDA8B527753B9B6300FB788DAD04BCE94D110F20E56B5 +BA61CE7BE66F10E80E169E2A544F9ECDE8D7185675405E6E48568E1320D540FC +F31CB04891164E15CE93A8ECB1CC7915F3E1BA6F91D32E6999521654EA26C012 +552E67FC012A61C89BC8DE53DA536C8462459D897B1F656FECD5A7622402F713 +C0A33D7CB9CD363A215AC1C21B46775C479FA205425A2049483BCA38E1F614FD +8503A7F277882759468F1BFF2C1ADEFF982185FE27925603F796EBEE9F3E0A2D +2FC083B1C3D4006E7DB6A215655EF488054E938F0FC4D2E0433A5D7024A998E0 +F09808DFC02CBBA68698C9067428B2F84E34FA9D456CA12D8559CC81CF4C49A7 +74AB3B9AFBE43304A309E4DA0ACBE2061129D366417A2C8D9B7E895B51A99714 +FAEE1CFCCD96D2389F492FCF79AF6C78233D5CD3224DDF47C4927491A734C92D +9F8047AB7760C636594B6DED76463078CD68DAC39C64EE36578978AE8B3553E4 +30B136CBD81CFC829D42E35779BD7ABAABF02F1D2D4207E50FCF163BDF416A76 +A48139FB526693673A41D4BF8E7A76E6555CCA7AD22904A80393010C5ED9EC12 +A48A3B438AFCB0A5C1C60FDF12B8D0476232C5BFFC5FA125368F843F3A10DDA3 +A2B67DE64CA97EB68304846ADA7EBEEB7C3E9ECC2D69E924B7FADD31CB23A828 +D30AFC8E895DB50845FB6F7310F51DAF4053E70E809C6ABDBF9737B24BC9C22F +DC119CED118CF6452F57BCA4F04FFF21FEF4E9F30E36FD2FD5576B22E21508BF +28BE5E31FB9D3F8F10FC009867D53EA8F60AB24DC7BE968570A3C7131A73F3A2 +5BFF440EB52F4B43D39A5886715A2C733FA5FF0B601747A4908AA2F6E281A682 +A09D8314C65FA4AFC75800136EFD83F7E6FFA5B8D0B3A0DB4E2824F8BF6218E3 +2F492ECCB90697EC67CBF945F6A4FFD7460593BAAB9E8FCF3B6906366BFFCDE4 +82AE89E002A38F18FD74D1469E88C061613707D658F59EF0C341273E5A1E79D1 +0573151A69EA500C27ABCB4BBC85899D71DA327AD2B7F4EF31710F8B8F12A277 +8520BCDDAE8223C19497C0067DEBC8A9DBD637D3819CA070410BBAAB518253EF +0E0D2C96F6B898AFDA200EAF949B1006D43F77DB0B744F35AB4D50586EBA5037 +4CB2B0EA370F76FF066508A0A63CFB392C37B9EAE7A6B80392C52B8526CA48A9 +BEB5354E304A23F846E28F5034C5BAC1821B7C0C79D4F91A89F7B352F3F3BA2A +EC4890792C0A7C90661F9238D9067951AEC21A496F2CEE542B47378C18CB914A +69922B408BF4B18B245769DAE9BD8FB79EB371BD11B1DF165AFF60692CF3DDF4 +C4B7807CBA6D25E25F3A1CA5A8E7EB271E69A5DCC410C9B29BE0EBE7744186E6 +86FE9FDA54032A3D52F6062C3A6479932D30CEB52C477426F087E8F44CCF79D7 +53B64FC9637F2293C79BA75C7E649610586878A140545FD666CEB7F2F238F696 +E5366740D421686433B3B10FDDE6BBC46FA5AE88C339F5D14E10AD4600B1087D +EF3726F005372A04627086493DD788EC639E8833F009909A03C766C43D37BFF8 +5B6F2A +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMSL10 +%!PS-AdobeFont-1.0: CMSL10 003.002 +%%Title: CMSL10 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMSL10. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMSL10 known{/CMSL10 findfont dup/UniqueID known{dup +/UniqueID get 5000798 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMSL10 def +/FontBBox {-62 -250 1123 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMSL10.) readonly def +/FullName (CMSL10) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -9.46 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 83 /S put +dup 101 /e put +dup 104 /h put +dup 107 /k put +dup 108 /l put +dup 111 /o put +dup 116 /t put +dup 121 /y put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE32340DC6F28AF40857E4451976E7 +5182433CF9F333A38BD841C0D4E68BF9E012EB32A8FFB76B5816306B5EDF7C99 +8B3A16D9B4BC056662E32C7CD0123DFAEB734C7532E64BBFBF5A60336E646716 +EFB852C877F440D329172C71F1E5D59CE9473C26B8AEF7AD68EF0727B6EC2E0C +02CE8D8B07183838330C0284BD419CBDAE42B141D3D4BE492473F240CEED931D +46E9F999C5CB3235E2C6DAAA2C0169E1991BEAEA0D704BF49CEA3E98E8C2361A +4B60D020D325E4C2450F3BCF59223103D20DB6943DE1BA6FC8D4362C3CE32E0D +DCE118A7394CB72B56624142B74A3863C1D054C7CB14F89CBAFF08A4162FC384 +7FEDA760DD8E09028C461D7C8C765390E13667DD233EA2E20063634941F668C0 +C14657504A30C0C298F341B0EC9D1247E084CC760B7D4F27874744CDC5D76814 +25E2367955EA15B0B5CD2C4A0B21F3653FCC70D32D6AC6E28FB470EB246D6ED5 +7872201EF784EE43930DC4801FC99043C93D789F5ED9A09946EC104C430B5581 +299CB76590919D5538B16837F966CF6B213D6E40238F55B4E0F715DBD2A8B8B8 +80A4B633D128EB01BB783569E827F83AF61665C0510C7EA8E6FC89A30B0BC0EB +5A53E5E67EF62D8855F6606E421BD351916549C569C7368AAFB714E22A023584 +8B1D6B52FC6F635E44058690002C6BA02CEC21C54CC8875B408A8BB84F445894 +5D6B3E4841CA20AF852A660FE9C832F773691DC6F7197FF3DEAEE97418A5ED2F +F2AE65300416227CD3BB03C29003C770CD7D2A7A2E4C1DCA193651C2CDDBF93B +966938788694BFB562AB0010268955FC3555E5984CCAB0A9B7590C77C9BC713E +A29E5BD7193A4E971D1752DDD0F0AA4648E7E87BBCE66A1E836C715C408B07A5 +9EB56BEFD4596706CF839BA4CFA90CAD4038C1E006B51913279A2C31FBEE5BD4 +A7D74F9103CE6124F5B439CB860987DF44FE17EF88EF1BF62C67060D25696BCD +94ADF08F04E349CEBDF9D3389D870D94CC05E393B3F4362A13A6A672EE5E8F5A +DFE7046AFE3EBAEA58FFEBA4A47BF61F92E2003756DA643CCF2C9DFCCAB62669 +E3C2A18D690B64D907F50BCA155A85E47C3A6954C6FF7ACA36D8DFCE777B7929 +5F5D5F787B9C247ABF13D6D7B4A8F06BA25CCB342F8A5071325CDA86AD71BA23 +8A9695C7D1D50D0AAC267AB7CDBA7AAF46A264B7B081B7E79AD937FEE4969FD5 +155A99E652461EFFB4BD010E5885631E2B2497D6B8C43CE77D7D47FE201DD46E +4482FFDCE150A1183C22C004A0AF0E1F42AA6804E038E1DFC8B0A3CE26B52038 +44D2E7F759DA5C252489E5525963D68BC27C82247BEB18818C7D4CF0BC5CC97D +8C701034B8DF798DD4CE36C3F8B1FD40B2DA14EA75583852875031AF8C909EE0 +04495FDCD04B05A5EFEBA56A8CAC1F57F1B8AB91FB25C81CD51EE69D6E0F52CC +A0E12CF7E3187D67DF71A599FFD895FAA7BF80E2E6B96592BE77AE96905BAF0F +F547355A36C443797DDA7C414AA606CF9153E03450B77D1BA4088D739DF55F07 +111B9E11AF37F45B6EDE6D7AC126E05886A57C83886DA87761BE600DEECD1344 +8A82BD652BE7ABFE6A0F50ED7C6F4EE12CDFD80CA7A5518692F267C51C3FE76C +567BB8DDBE09A2AF901F79AD02B435287CB8057B3D5EE6655071F67B00438728 +C4C3EBD648BAF650993AFE5E2B29074A99ED0FB725D9B8CE8B0292B08A280214 +C3AF252BEEAD30C88F72E322FAC3E9D78A1038F5DFC41F7BF1AE3744A0677094 +51B77C2D630B67853FE5E975A395C06A4D4DA744040B272C2B88D8B7ED3A2C01 +66F503C9DFD3C7DDAC865900D2A4F2CDF517F449851DB1963468D0266D7A3E58 +9F6B2A1843E6444274F16A9930302DACD8D2BC4588765099A86BCCD8A31DF0E6 +2853114DFF2D19F812F19AE6C2E419D7AC1BC024D1195074FD0C6717BFB389A4 +4D5428E7BB2E4F9E9FDEDED7BDCBDD3460805AEA0B5F6460C2FDF19273CE5BA7 +5D3AAE0DB94C6AFA8339646191C23B0149E7CBF136FC4C844E025A38935DF256 +0A0A6466A45EE8B9B23B6A055856FB084F87C73BA28F1883E3B184CD813C72F9 +233B78CA4E125ABD26F29B92CD9DF39D6FDC2A217E2B6B45D9B0A4D536790A5D +BC0903069565A442FA7466414D948AC432C6B75D8D0E1DBB217CA3DC38A52DEF +62E9D5AE9E753956C13819D93148C7683BE4F71B80BC066D8C19FC807FB1C086 +B49215DCF56A91A42089F0D063B9981925691F7DDE3237403AC714F5CC3ACA88 +DB2F1DD205578C00472FD70C8BA4F752E3923ACF3164D442A6B639902ED060D0 +C5777BC20F9A3BDA60FA3BC986C38136FBD2E8F910E32EF36377C9CC187F4AFA +CCEC423DB925B378522B748BDF12D523804CABA83CB5A7ED69FAB9AAB75EE8FC +38D9866E3754C4E2F2B9AEFA804044D878DED0E114EA0E9682FCF38F6628E63D +FE1C1B5615E54FAE8684566EDC4B616F76EEFD6207E0386F06D3BFFA26425F24 +303CC7C8A8D7021E7D09B202616988287838C3DBCE3179B4FB5C726E603A47F2 +8248CB508F327D1291CF3F08F7C88298DC2D0F778D24304EFCF6E074182BF5B1 +8E6551811FD6991971692108E289B61053D6DCBA2925B3903E8916EBD09D97A2 +C6D08E89DE4C0CDF7185E1E00DF456B249F0BFC686E04FDAAD2772DC2C39DD53 +9C23A41471267F53A87E5C2B8CBCDB66CE0B9844BC506428E6150B48D2FA6363 +4FDB2CEDFBAE0B7DBCE4D83E29B2955F8966272CB865EDB360C8A8C19EC62A29 +03066483E4083524A1E8D80FE3867BC1AA91753C26ACBE8489AB0E3330206212 +93E07ED473DBF457EB8489E66FB4B8ED8A9EA8911CF9308CFE3E6D6F36810EE8 +91CCB11BD548617B2C683C354452B9229E7C9E68828BBEC324420DF7C188CCE0 +FBB514547553A7E9B38AC265783891F42DA472388569C8E7594F7E8810895A27 +06E456902A8D9F65CA808F1FD475D011C4572F8A654BA01D67942226A663D179 +95149FFF41A9F55AE84EEB9A6A39C017D7E4FD6EFEEE7FF3CE847CDB064A4954 +9DCD273B810E0F259501BA4003A3EC1ABA6E13D24C0B57FF82D6DF077833B6A2 +7EA54801BA81DB961C261689C0887FAD83771E55D3D137AFBB21779397E11972 +6C6CA922F45AFA5C0526863A5AD8B9C0775CCBA17FFD37A44CED4710884DBC31 +5C9D3F5441595B86CF7CA2EEE42AE87896E9E60EBF5F35C2B7FDBF9A9CDAE262 +3F48396F0F741E9DDF1D4FEF75E68AFB020D06CC29B3A7B2ED819D1AABC12B91 +CA2A65F1AFDDA2F3FB322E0268DBBA024663E49EFF076455338FE31A16B04EC1 +797EAB0B49AFFB906A0690A1E8E2F5314773E1CCFFF43E6FB3875AC907F0C5D0 +DCB9BCC127014D472463560CA0CB1C2CE614D94177C7A52A5B089316689C8112 +CA57E35D716D956DBF9013B1E5B9626456B1433C8C15FA906458F957133B9E19 +8D46DC3AC015F7602538C2AE3927C6DDBACF38E59220C2F5AF36B68DE9117C51 +04CF7DF32B1AF55B87D1D8A5F4BCFEC66F63B32B6548DEDA3AAB06C5310E4757 +78AFF947DA22809B360FE535506A554DDDE5A6F2411246653710ECE5CD3185BE +730520A766C47E1ED01890059882BE1432586864E1A86A7F586438C8DD35C00F +021A741ED47E0F16DB6070ED0C50038632CA4AC2975578A8372A080CC0447C79 +CEABDF2BCD5E78564247B0F0025F556DA8FB62125227849EACFB724A4AE3EF57 +90C07A5B27D2E59425F56BF8AD84C5F5310FEB1BC73D536339FC2E6A5BE2DAFD +97FC835E0D52F680F80ACA37DB498AACF152B9B44626CD89E3302C3EE1623EE0 +F998FA78305960AAB9F483F731F5F67A8C963C23DB8E48FB804EF8B86FAFE7F9 +4C09641915FA7E3930AC922682313408BC1607C76751CEEAFD660206A39CF394 +40ABE2A313AB7D5FD6444E219DC5C26734D322BA268D330AC17959A390D6C8E7 +3A155095BDD66516DAD5D65519A7FB871ECDA77061EFB21F359158B4470EF79B +362C35C06B85C9A9505C8361939C6AC013F2CFE8EEF46FD8CB4452AAB3EF1FA7 +DC066557BADC2ADDDF7DDC2A0E1DD4A357E27A2073427EACF9B9035DA5272136 +7DF37E26D96ED4B2ACD60596E039BCB15E259C72FEB3344E3EEE3D4F17DF4233 +04C1416BCADE80BD483DD8C9AF979E1C7D50C4CF015870703F88B92C4FE46AB8 +DE6717B55C460C805B391B84333097E116F4A51F631FAFAB34CFC925BEE8B72B +C9FD5F5A79D8F2295FBFAE649DC6AB47794AC7D73431FFE5BE992F2B5AC67049 +B5208251C0E442385A9FACF25E3A98D7F5D4C2A1ABDC600AABE84769CA83350F +9B87F71CEAD3600E02FF9AC03C1B5C21C84F911511A0CF0111BAC7605EE31229 +3C526A79D943D92E1CC3C38ABE82D560CFD4172F318030852A5FCC0534B8B3FE +D7365987C8B48A072907B26CDC2108130A33233E8E0BB5FDF14FB55098A10EA2 +B51AD9EFB119F82B08D256D396D3263FBD9DBF172D43A90ACD1A31F3E89E8571 +74BE98B9560E2CD661A2F93C69FEA3FF26B00772AE2C2C24B98D3D122EA2AA8A +44652CCDF4EF4F01CA7D62A976E23E8A86291F43BFAF38FD9C325E70F9C36CB5 +A181DAD30156E98339E6A0498D3420B7BB3B4E651A9090D4A17604AE386273A8 +3D4AE8CC18345E6E19DF06BA848F203F74B161D6A8882991CBA7385F308696A1 +BEEB0130D938A764B98A2001A38489B1334025EA848CA44A116D64926D460D64 +01159E77EA7ED9ECE7BA77635BE564A4ED89315BDFF54ACE6AA1A26591D13CD4 +6D6425CA7933769B842192858D10998509396829263290A3A7CFEBBDA3EE6CDD +DF1E492AECDFF7941B53573F01F623CA0A5ECC9D05A3D0954F7AE8CE94AC3B2A +CD4E27519B2E16F033EB732AA024BBAF74626DB55DC74B1FDDB07FAE98B4AC5C +683CFD8744F361838D343B657EBF52DEEE7AEA7565C5BEEFE455DDDBC4DCCA7D +87D6D769C5ECCF14118A14A85A86865777C8E28F953160D5E82844AE54D541DF +550D5F1519E183E0C42BE88F0458CE8087F2CD4B1B49A8E9E3D127C4A4CB74A6 +2E73BF4CC317781D03FF04BC36AC0E4AF99E2ACAD20F6F8029DE8A035DAB40DB +17D237850BCDD05931FF4B0FE2D0B79EC5A88FE0236271CCB075BD194AA25AFB +3FB93A5206F61A14602E4EB6F1C31C654527CE0C02D04314DF9AFD710D0EBB9E +F8721B97F5FB18E27507E1F800B5509A58A1A8296C72B7B73F99B6CFE42E9C2F +B63B3555475E562672645CD374BCDE937A9B05A157FB3E74C8297507253E957B +1A9DC421946734CEFA3D5EE357DAC7E9DE17A5BDDEF6B2D2A740BC58128FC514 +61154664412BA1C05209EC992A77B7CA45AB7C0EEBF590A5B5652866008CDEF7 +124A3003AE6A7CF9DF3C72750CBD281358CD2FF25B162B78CBB971DB3477F8D2 +ECA3EE9CBC90323B2C236E375337EA0848CD7CB5781A2B0A42DE7E4D99DB2746 +0B26796CEE129D23C76794B7CE21C13C7D4A998B752C8CF43A4821B736EBE246 +D2A2BD7BA3351FBCD1B0A501EC1EAABE60D06DA2FE39BE1F0AD629769FDDC933 +F9D02F9686EC8C2D7455C26AF4DD3F6860B2289E3A30E1C254AD17D731CB73B2 +BF4DFE90CAEECE3ED0CD3FB4C8F4C7BE1C056AB4E9B95781A8968E3CC1010003 +75DFBC4AB9F6B27C5A9AD88D94441A8ADF09EB275E5F0E5E6F3BFEA0FA8C308A +8593ABA0645ECA8FDC3F0E264B35D4B0DDB86B93CD8A047FC409E18196B501C3 +B003622999C47BAC04FD1ABD8AD359C977766E9643EF3BD6385306B08EE3E13E +7DA5A06AE33D17A3D574C6390DB6E9429754B210F0C349C359559C7EAA2350BD +F61D4D8A92B1AF697BC620FA0351E67E0D9F41A95A47EE0BF210C2C48691901F +F905F65693DCB85BE412F097480F6A7266AE0A928729DA0F691CBFFF3B276EA7 +322BCD2206D96E3DAFDFB992CA8F2955F0E8B882729DFF840569D12E4DA1775E +523AA734552AAB6F2F16B89B39F1A3FF0E07EA08D13E612F201716C67F327017 +6C041760DA30374434808273062C1FFA2C47B3FB578807BC26537F542040FF77 +66C995EF3E8B08B09FCD3EE89C30F157158A739606D2CEAA26694A4F1CEA6633 +B54933141CB85C60AB262E2D4E824A3B85C2BEF810DD774F296AB37D0BAE7182 +5648CD18556ACB124246A75474B232D712C2358908B5D9A76F82C626BFDE01A1 +093B8FA6AA0B32F2CDEF737B28BC0448FF816DDB5812131DA0DD5979D77C3838 +B978CC3F6778A4BFCE9A7087EFB19749285AE4C92B99A6649DA349A2E0889D72 +6D4FC664522F06C8C4D86D30BA43ED4E42211217D01636A4E17E2A132D26F394 +EC34EA12D84594AED9C6CDBBC0908860F39B240FA7D7B3003DB10322498691CF +A294C0FC7ACC0BAD1EED3E9D60AAE3F7429695892D1A21CEBF062C6129B33966 +8B2EF6E932F9891DE6028B81C5E9B23278D35B7F0D83989BCBA25E20E9D503DE +144DC485F09A4EFA1268AC5E4B551C5B2F1D51E9B9B9C0FEE585204F869D0BE0 +7287D7570A12940A47C1F51AC6134F03B415C30E147C49F89228855D093EE55F +172711F37776E97A99CC4B36E2F10713E36FB279FD3FA5A0EB9F3938F42E2BB9 +254EB8F0C0F30391735019E02BFDA21D9813C6A22279B898EAF01AA892B14DC6 +5912B9275167AB46EBC420836CC1A5F38A4EB47C039A7BCA62BC3FCE4199FC71 +011DD6E5FFA0F3D7F04AC02AF91B9249B9F993AE346572329DA852115BEF8460 +B94690E790003586F473F37EAB5AC2922F5F663EE2C3C0C336A8DB71650631AC +0A923A389AC911CB215EC2EC7D50CF8AEFD59EBFFA53A9F1FFB7E6215F17093E +3975F186FE23BB5FA5474C11408FABD223E1E6F62035B5A5C1AEFD8899F00FFB +E729C2D5FD551E80716CEA4E8281660286A802AAE8D5834F37F2EAC46297E57E +993B09251DD7789D3467417E393B7DEABD06676B96241B0E43ED1A1A9FC3B12E +0D34B2B0792B79AA648FE9450C3B209FB6D7D91F50C52A5DAB0BC81A8B698BD9 +18946EFF691912D7348D48FE68CD876FC6F71F81165D0C3272DA1A992308D9E0 +ED6D0A4DAD679AF495F62B78D462B463BD4A40931172290C615B3B3B6B47E45F +CEBB85E0A6AB6832067CA6D403C239530D07F199788AA4DD52553836851C5228 +1072406F6D7323A334E7A7FCA588897C4FBA6D4F7DEB65525EFB74E539C988C3 +A685A98752F7198E77E456A545F0D23A1BEF81EF58B02D289CF980A3F17BEC8A +6F83DD90C4A917EB0E5E2B444A608E2E9D2FF80620E16AC1D7775C0A10C1299B +BEE0E1AB24C50647E5CA1DA65CFF3B2C295F0644CA7826E1DC6FADEA93D66A20 +DE852F20AD224D28DB900519EB1569837139C833F24B799F7EBE3FDC14235323 +1D0BCD4991C861F38DF413A5A5588B73AEC3BBFDB885CE17BB3E97B4E6A79761 +93EC8418C2BC4725CD61B5E30C07352F647C3FD50083878C13CFAC241DDCB082 +E53703D182068727F9EB6FACEC25F6D901D7309ED7370867E34E267519E22D62 +4FC7093448BD0D6B1C43D318A3E14C92032325C132AE0FF7ED707E1FA4A955FB +F5224BE0045CB14ECC321D0F333FE24EEFCC504F7C756451D7693C3E6CA87526 +4912E1B6DB935BDE76FBFAFCA4ED473F1D2618812CFF25A6859C626A216603C1 +361BE3E071FCFEC2D4BF2FEBDE07DBD56A1BFF8303901168FA06488BA6B76F36 +95B0A90D7724E9ADB567C2ADC65CF3482CF47FD1D16F70AA19A97D0F9EFC611C +AEA5E1ACCDA7FB2DF05E9480936281484BC329F0B771775E73F7FD72FE3F45F0 +50ADBD03932B38F37A8F0A66B2F739EA3AC8811C8F514E68C5643E4AFF485C81 +88475A523D7FCCA5C8809BD49846C77795A38DC6406082000236A4D2628B5932 +AB7916D44EC2210CB941B4C12515E844F0D26CDF0AEAD63BF1491BB50ED4D6D2 +739930E80E26E9B425C25033121899AD22102AE6868648BABB7D5985611C95E3 +5B8D365C7D034BA22E904A0ADB0132CE38288C127BBEA0825E600D18383A6854 +EE28FD1B2F57AF704854AEECF985EEFAE0521DB38B0A7511C803CE8AE0D0BC02 +50744F806B8D771AEA2192F982A46222D0BBAFECE94AD9364FF06C1A0743139E +E707CDB1F328794258E7B65C8DEED54A3603A207E9293F1D0AE9380C346ED826 +FB86BF48CD0CCCEA4AE48394690C580DF1BC1D18D5D5263A2B4E2F4E76E97E2D +7B116321428E4418EBFC552EA884495FA49D0BA43E50451601EC4C2ABE08BF6B +4D89C8002D2D6B2E6D776FF2B4E19BF8114A8ABFA814C3D19FE883BA4CFF5F55 +E2699B224EDA1D73C4FE549E6FDBF7F03B55E169CC7B3C114716B076CD2127CD +EDEC692864A8F284F068355D23DE0B72098F15F36BE977CAE1C93707E9B016EF +35CD7C9E3C0D2E280E9FA779FC88EB4D8869AE19372C67590D4C50B186CB4957 +4255CDFC8626DF97A3BB3CD57E6F5C0753EB791BEB1D89D9D08A0CDC20B03887 +53AF5658F82A3189C68BE1CE912E2DF8AD9DD14DC19B73374D3A8D91AC5C0BA3 +5B4E02A20F0CD2AD2BF9CEFD5BAA6DA6F67683240FB37F982E4BF94D01EE4B87 +48849EEC3605F4F238EEC0CE5F8F0404B54EA14197C4512D693E7049FE2554FD +D6597924287E5F7BE637A45BCA0B656E72B4DBF038F34C200410A5F13D8F41FE +E4F82A0E8AC403CF55A7CAF1592C8F3677AD309BD02D7F1A024EF4D12D76A4DA +3B1D7D70E5F37F30DAA980F14794B958BEC5E965947CA8701511A24C51B068A8 +E16E87E6E0122772F1B7889B606BB34C3292D202790E3E44D08B2DD1524CC5E4 +C5B5A0F0CAC3E959911358176443781BE957B29766D3F6C4F3F00E472DE7FF45 +C0823A0ECD41DD85D331FD6382EEF94A49C025AC9343CED0EDC96B340B9556F2 +FFFD21DBF54879C80C3AC86A8C60F036818300CBC2BB89D244EBA39909FCDD25 +00DE6123A2EAE20617D6AD25352BC89188D9A0A637899845684FD148F6D05B43 +1BE58EDEFDAE5F13B8F490D8AB779DEE843349F10385DBB9B9AA1FFB29771131 +174FA14C1F2FED29A9144C17DD38F24E2DDEF79AB75ADAE4751BEED51EF8001C +5E746F2B18E06D788F423D51F80AEB1757E191DA795F73EA9D3745CBDB8402E1 +1D68CC70E9D870B1C13F1B6571426E682ECDF0760703AC3CFF878F886817CFE0 +91B4C254052177B6803EF7D2AE1627051931AB0F40897063F7E63A531F17369C +9200AB01F2BD55CFB0593E7B3F0C95ADBE357B47DFCA6851FE0BAFCACB518174 +C1DC98147B32E53025C75BE70696B8BCD0197B7B55F9D3F86B3514B70C6E84ED +37E14AE8E7B028FB7690B49C0935FF8C4E7BE20FF62924F8FF1E63087E80AA08 +7A4813ADD73264EC452EB4756E3A6F6EA483B670D26AAE5C01635E5BCF7B68E3 +4A5EAE7163959BF4B6CF07676410526339F2227EC18F3AC749994B9CBDEE72B5 +637CA69C3A9977A371A8F3976F89C396F5AC146ABCA81D1EF54D63ECC741B8F5 +74F10D8F45621C4D4387F603BF2C6F28C186CC1B459092A6DDB2BA582B46F966 +BED9A58C136B21C4E5A8AA454E7F9E3A959269241FC28B17C4ED41FA25C0DF04 +7A9E19F35C8581C352D4938757B80637916554ADB3ADA7CC900A43EF5EAE9B7F +FEEC9F03526E71B41AB44EACF4E3FAA590373E5F9B79AFC4710BC3605A617385 +5D0E4ED5CCB5316D786A1A26A0CA791C7BA57E6D095BCF85376FF355336A70A4 +A2D0B8897CB47DED9DC84DFA177703D22D5C28EAC8703784882E961EB913E8C8 +3D61D4BFB90D80375D6AC9F865BDC55D002901133240A64036A8C5C3C17E81C7 +0CB2DBE2F4033941ADAEC107E9AEC1A0392FE24EE04C9FF9F55C1F5060B9C11E +952145CB32E36484E89553FB76133B17D4D8B8CB93E0E1925A7F35D6EB9F442F +B7D750722FDD21A48FABB0CBD361283F1E169BE8046008A03C94DEA662B7342E +177AD179F908A16912757AB45A236827DF7A9E62B91BFFCC2293861AE948EC6B +118AC7781E06285B4DB4383BD10B11B6158D8FC5AF66DD3BFDBE2B672A475155 +18A1E12364CCC7ED102F178B45CB26EF751052E5F24D63F4B89F83DA2D3F1647 +080F68DE59FFD51F95C863EAFCC8A23CDAE9F68895D768DCDA1E8DA17AAE7718 +3C14FA5BF5FE32A6287E118FCDC09B33E6E2FEE973B4A4F7926245A24A341A78 +43F2BDF8ADE02E6A2CE8525A3B4AFD65047B07F719D53A568D5F681A036859B6 +36A782FE709EBEB5D4664171F86D877EA8874B6ED980AED7965211F0B19F77F1 +4EFD94AC40971FF56B7986B447CC4D4AADA4C3D8789AA4AF6E6123BE7481E1E1 +583BA88794C57BC119352C57F3D4647AB7AE2C2D62D15168B275BB266EB7171E +69DA6272F0C3CF9A44BF9361112CFFF0B3D5AFC45E6BA6AC8180786CF5E37D44 +0B450E00B37A91F28773FDDB55E7D6ACCDE2F23A812238EFA885DF3596629B41 +5399AEFC1983609BA4BBF0FF0567F6225364C66986F3F2A0110CF44025E049BA +67A0094237BFC2A2D12709540CD2E9BFA05FE1BEFF6B502C644C3B0B8161A8B0 +76C96D707C71BAF3C2BEE325B8EF357365F561F181BE78A739F12A2DE0719F3B +052E743C1D4541E7086700 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMTT9 +%!PS-AdobeFont-1.0: CMTT9 003.002 +%%Title: CMTT9 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMTT9. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMTT9 known{/CMTT9 findfont dup/UniqueID known{dup +/UniqueID get 5000831 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMTT9 def +/FontBBox {-6 -233 542 698 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMTT9.) readonly def +/FullName (CMTT9) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle 0 def +/isFixedPitch true def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 34 /quotedbl put +dup 35 /numbersign put +dup 37 /percent put +dup 38 /ampersand put +dup 39 /quoteright put +dup 40 /parenleft put +dup 41 /parenright put +dup 42 /asterisk put +dup 43 /plus put +dup 44 /comma put +dup 45 /hyphen put +dup 46 /period put +dup 47 /slash put +dup 48 /zero put +dup 49 /one put +dup 50 /two put +dup 51 /three put +dup 52 /four put +dup 54 /six put +dup 56 /eight put +dup 57 /nine put +dup 58 /colon put +dup 59 /semicolon put +dup 60 /less put +dup 61 /equal put +dup 62 /greater put +dup 63 /question put +dup 64 /at put +dup 65 /A put +dup 66 /B put +dup 67 /C put +dup 69 /E put +dup 80 /P put +dup 82 /R put +dup 83 /S put +dup 84 /T put +dup 91 /bracketleft put +dup 92 /backslash put +dup 93 /bracketright put +dup 94 /asciicircum put +dup 95 /underscore put +dup 96 /quoteleft put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 100 /d put +dup 101 /e put +dup 102 /f put +dup 103 /g put +dup 104 /h put +dup 105 /i put +dup 107 /k put +dup 108 /l put +dup 109 /m put +dup 110 /n put +dup 111 /o put +dup 112 /p put +dup 113 /q put +dup 114 /r put +dup 115 /s put +dup 116 /t put +dup 117 /u put +dup 118 /v put +dup 119 /w put +dup 120 /x put +dup 121 /y put +dup 122 /z put +dup 123 /braceleft put +dup 125 /braceright put +dup 126 /asciitilde put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA +0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93 +51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71 +7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551 +E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078 +0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273 +C01924195A181D03F5054A93B71E5065F8D92FE23794DDF2E6BABDA4215500A0 +42D1A3D0D02C0C98BB1D6ED0B7791274C38B038FC7921FF1FB8FAE7258C09259 +4B8E1BD9EDCEDE9ADAD9BD9598EEA9691589649A9A21539161E374075BEE3457 +689F308A4A7AC9F2FE4B301A6C36B0442FB92E3B002623493DC087800B5A0521 +0DB96A23175AC584DE166F59142779F26FEE9783E28DE49FC3A8D6583EE63FBA +610DA773CA18ACE6F64A4867A1A7817120ABF9DE4D17782866E6CB6B65A9F6D8 +3667C8D3E61E5356E35343FDD4C6436DF73934470916CB5F0ECEA6BFF092E735 +C7C355B56189D1DD5715EC97E50145FFC17BB1497315A9585D713A7A6DFC7933 +995468EFD0F59E3C15865B87925A3F2930E20D5A35970E2C44F1629FA16E00EE +EE21EFC50D49F5BC02300D0A7BB85E649CB4E2E828C8B1C5469463013E71D723 +2CB11BCBAC191AC751A2AF7FC228395CE9472DC1809052012AEC2CD66695DAF0 +4CA04234F0187F4116C93F59A7F1F8123DE87F111853B785A20CA8B49B3B0CEC +B11AD345E1A11578D2EFEB0536D125237086CC8CD9F34A5137AC5DDFD8746014 +D74AAE8239B81ACF65F379CF2153B06A238A2D767F294CAE0D79228F0B7D45CE +510AC9657A1776202FEF42F96D476E7DF407786AEA12DEA0013D3B4C5D0640F5 +BC5BB72C34066270399CE595827175B23B25072723BD24E07F6BCD9EF0175DEF +93714BAA53960F81103CFB731CED4A267B53727BCA3C97B0BA5004055D4EF0EC +F725658E53AC86E4061B489AD4154915C3981B3B703E1E2A8D390CCECCA99385 +45EBE35441B062D7D12DAB2B31569387187D74A4043FD71F1C6D352EAE0F6757 +4345FBFB6DB15CAE47CAC4BAE47AECAE5FF5EC19057DCEFA1B23F47364ABDF47 +088A7C6A2AE26B10459B6D41CB69182FD1472F326CE3A15B59255D1DE3B616D8 +9D1F12561038839781E657C896B8C58A32DF5AEA23732A0966D96C68C988ED7A +09B7E2C8F9F3D0D56879764781566299A4EDD3588BDF70E3D924D25074F30988 +E35BDD827AE4D0B4A06F55A9976BF0DB3C0B1D09CD08E8CB168B50617691638C +0EC1A791C228177D4FFB021EC3DF5082CA3487AD2EFC8DE9466A690ADDB4C52A +FE2A6DB4CC275CD33D9136E735279FBB2008D59E667905EBB04326EC33C98B2C +94744B7F540D86E90DED64572ECF1EAD3A58EC101642B245A9C7232DC8FB8741 +03F97883BB32FB955C22F878FA0FD114451A3B3859B0B5537AFAB73AEC7DB2BF +409E1FB41D473714F6BEA73CB085139879FA31710E01915C2938C37BAD6D7D71 +45B897E00857D3931A489EAC7B42BCE4E65F73F67FE027CE482DC47598ABCB95 +39E98DA8ECA3E23F0799D5963ABA6E2984DEACBE7B46B40ADC6213E0F4D08971 +58F68C946C748E4B4217CBA2391BE2086C9758F4E32C9B6413E48D84D33A6E85 +84747029C0A9C9B92841D217A902BA8EB333999D62FDA9F82BFC8ED11F67988A +0CAE42182E414A9766AFFF4B046A09D476F8E3F15A8C7829BEE982D8350BDF5F +F215F2BBBF68D4B567BAB798B9604C79306C475926E9FEC0F07A99F43473C6FD +B15AC29C3D07FEBAD1BAFF75AAF2FBE94F104F1DBF838044FAD94B661B06AECD +D9AEBD02B60CA4546DD6B5B5C1A3833ED07845671CEFCA8955CE0DE5DB8FC93B +3306683CBFB8E5B79A863DE78D455DE9D592043C2686F88A43140F8B9F3B553B +7047420E93E753829F8D47AC7621CFE3626F271E31F0019CC02D0B57F67BB47D +8CFB63E902EA3231C00EC66EEC0D30FE8394558BD3535C888C4CEFC6EB72E737 +712ADC6300162D5D79BEE0CA1F6E4127A0BC90656C01692F6D82C85550AFC97E +C2693E379160FDB9636FA41AE9C75B7F6643B05971C6D67CE30971D590FC07B3 +E0B36B4D1C7F25110B5DA2130D574FA292B47322975A2BADBDB39AAE69BDDBDA +A880F9AAB580117708C79204DFFDC08BF4A48919B5C22228845CE8C3109E93AC +2479E523B8A1C12A6E541118F121DC6B4EAED83491A03192D5C3A2A45D1A2467 +757E7B377C635CF5CAE11A7CB49D49F3A1BB2286090B5F0E4F89869D1771D50C +54B5C5E091E3048A2C194F0ED00DD64FB95BAC6FA9D61ECD093ED416DA3A4981 +DB07CFF17C4F55C62DF628EBFF06FAC3F3D3F91C30EBB34052BE1A08F5EDA4B9 +08977197950A282B84E21D43C64BE3AE4BCE22C70E7D392DE09D89B7F23351AD +6AD37225C12BA79EC9951F5DA1E505DB26200190ADE0E549305B7530CB86EFD2 +A896F13A97E51754F70B609CB4511CEFC38BA579C071E9510A49982389980DC5 +336D6C4A2DB100DFEC4055C7AA9C55880F94FBEA9EB280BEF66CB8E1E38A359D +E5AFB12B540CD599085ADDA7FC2C72E7C873015773FFEECA2C596B75BC39A3EB +3C43FA2E53C0D7993042F3D652BCC483E48B7F6C94C3FF6D38E276086A6AE67A +E5A571B9C72E0D7824E0BC2ADF51A393B9E334649F786EC1923C854382B89627 +1B9E701AE5A6C42E672B2C6A33C8BBCA8F69B9061E787D6B92183F20CF4C3903 +FF5417427B84798C82BE28D2C81624E3920CA61EC9EADB364B5A6E50E49A1A72 +A9A090A1FCD84814B8B2708AD787D2B5015DA1305874F58C5EB62F843685FCB6 +465FCA80176CAB2B2FE65E0A270BCE1E3DB97564BEDFAE5CA44395A8DF4505C0 +3E103CC3B914359B2870DA6CD30382EAE8949131CFE31E9E75C3E47A3834BB32 +CF183D4A8B9001710D0A11390C9DAD116196568591D38C2AF4ADD852F31494EF +573462759A35415900360882739789D6B89ACEFA251C5ED90ED704DD7C3C80CA +9F6CDED69537D201D520C99E69EEAD5D3C0EB84C166660B3C190166D93EDFE6D +15BCB6DC5CDCA825E48D33845CC2FB15291AAB823F25CF8BB0A1EAED8BEC524D +D9CA016027141FAC9D35B64FB9C224552F29EF6B32497254E319090E698FD8A5 +15491CDFE1B988C79A0E3B9D01E12FF084E9FA86CCAE02A3EE6F2917B61A2CC1 +64B8CAF309D1AB48A34227A7729DFF99CB6EC282E3FAEDD2673779AA7E4C1789 +D93FDC37FE95F087C5F88F53D30A2DA9C913BF205FC6BDD060A40184F4AAEB3C +D080D63B89CA3DEFF310D09EF0A83F3914BD5B7932980ECE139EF0313C20B4C8 +576EE0FE3F28FAF4D3CE7CD0890BC824A85B8EF4636BDF1EF1BB519F93D36540 +ED09FAF93FD71992CA2CE2E83F5355162ECEB32AD218092F45D5A61A44E67135 +EF0453589CECDC6962D0E8DA7E7567603BAF50B2C8F1CA65EA5320984E7D69AC +9A7D3D7F92565D79E8C9DD2D92CCA7DE9CD058545E9F98AA47904D70E1897099 +3C4C852B3BA131DDD348433C336BDF5FBDFB62120DDEAEB3255E3207B0C84A0A +1ECF9EC869DB9BFA3693B03FCB27C5A5D3CDD62630DEDE91B4DD5B9784BF0BDD +FC6EEC3FA7ACA9E15FAE47CDD9B7FCD2BF0EFA10716F08C0AF25FF67CB6F9598 +C607D2FCA452417D2C69DC808A9441A66492394C3450BD30632AE739EAD654BA +4343459CA36B6D5B2C12C39495952F2EF93D82C73E33236785A79609E260C4E0 +CF3A3C950DE71DDC3939D42DB1CB1CA917CEAD56979A70F8F3B207C805319FA7 +3C000AE2B21D711A6D78C7BFB901334DC06F59EAB6D94B507734C27971F8458D +D00193645AB92FB8FE163D5C51AE4F40BDB4F2C51691E76EE0636F071F37AAA9 +BA78BD12459CA499210EB0CE2F8BD317387797C33F5933AE7A6264DA06B4A6A6 +1188326147A16B205D1F965872DED7D8EDB3294FAD2FCDF0D423329E9CCF879D +4E0B966D509F45527F7609DD09694D286F6FF7535EF8971B7DFBAF608A19D442 +C133207EB1152ABBD11C455D0977F66A9B73E51381D1CA4B66E87C0C7175A63D +80C699A052F00C41DAEF42E7A40E07B1B14107AB0787E24E17C1462960E3C54C +AE73BE4924464FB177EC62F116B2822842541543EFF7ABDDEE197D6BD8F8D4E6 +59175D8C5957550B70BE775AD52FFF6E7C00DA7CDC16E1DF7446BB5D8FD82647 +3E9F87D5EA365C82A2D991321ECB14A9E3AEADC5A56665DF7072D6DAE402BCB6 +14D92B17F9E063E4E9D8D239C91F5C7C0BCD2FBD936C9D4A0B57659420343B59 +B395BBD1AB5B6003F653699D57E7581F9813CC98D4F072FB78899D6DECC42D34 +F2787EDEA64058B46C4BFAA2BB96E9BE5CACE8D91E4C080ADFC0FA0D4A29C6B8 +54FEA9E11DBCF53D9CA40A21AE5076451EDAB3593E56B6D453DC8EAB8C78B588 +34D4C4F36861B5649BC1E9F3091E704BDA7613ED45C911DFECA74EEA05165191 +825F95A947CAF382FBAF01F3B8B041ACCDF39718D7DC5BA6CA12BB20EEE96439 +BF2E2628AA3BD2C91998E6247A690FCB0CC95F286F427345CC4F1115BA3A6E54 +4743355F2CC991CBDFF5725902C1F5A6DEFDC8638A26EA456C33C27773D6214F +66536CD2E44FD253531732D5A8C44B336B1BB47B0477350EB8CF74889B93402E +2356A9CAAFCA562315D8E0B3F42F08932CB87BA2499A875AFA08D11DA73B38AF +F46D03B7F639A8D7BF88CF07FFF4E91716DCCE6E2CCAB60A64D5E40EFD8B336A +1BFCC4CB04F49DE1FBDE7AA5B2092A6EDBD913D161A3271AB6411622D0E14416 +37F81E0102F5B0F2F9A2B27819E4BACD7C50E29D6291AE5B0973C657761545A6 +741729620EF2BF1046B3913399C10982EE5F4142CF461EA31042E432CC79A1A1 +39C607D22E45A6DEC008CB4BF6007CDE9DD5802B49A62C8E02A6D448B64177CC +887AD71D171B99E7ABE2085B37D90B3BD8513995D9A57F53184DA474F6DB5E49 +B73E04CC214EA5398DF7D7541F94E623E8687B511640457A48A68E9D9D6584CD +15B57CC044D8091C771D175F2EEDD411099BC8F7B4317DC503BB5E405AEEB526 +5E6E1B1F2705275D274E012A98F66075CEB90AFC648B964DDC0E9C4AE7B24CE1 +80B051022E5781A533A21DCFB97893847D685137EAD85BA708A7E118C72FA839 +A9E460B5D17365A0AF1F53A98319FB64A5819B087F554BC056C4BE44113A5404 +BEF759F890C1CA5E7AE156F4F8106FDB4F8DFCCC640976983EADB30976344048 +2A86D7B2AF4A01CA736B98D52ACE392AD4BECE7E61C710B08B66F01857CA460B +B8376E257113E10F6DEDF14CE2A4E6A99ECBCD302C36CADB713D849EAE9EB598 +F29DC98531D793B79F83091F9B136809E006F34E423D528CC4309AFFB3EEB47B +9A9DE4D5B25CE953345C326BCBE2B4912641780637783084D3D12693F8135483 +CBB0AC4EE0B5610D7CEB7DF205830BDB9BB404DC1B28FB0824CC187B26C19A91 +DA0025EC739BF3993700101D042DED86D67F5FB87912CFC51AA7DF53F2162D62 +6314A2CE13810D0B8D81F45771391A236422CFA0F35F7A0CDF14ACB2724AA57B +7C2C28D53029B1146558610E0CFBBF72A85AB9BA308F846228F299F13F68E8F7 +D963B2EE9EF7D4C21690632B640BDDAD0556EFA4EFBF035F13377ABB5CBC280B +9E0C12AACB153C93351E5BA95A7D149010E204950A59C7FC6581D9703468C1E9 +EFAE37E7E6ACB892B3F8D1248D9A4A72F642FECC5E0B25C15EEB921EDDE84D12 +0E524FE6133C4921FF4921242392C12FBE69744D53739F7E849C1B96C4020AB2 +1FF10DEA608F111749E2FBD8DBCB17F353DCB3075B4F4B8186963EFE95A76A10 +85AA5BB6DB4095291974221829A8E436680F4860E01C3843BE5BB3101D0869C0 +EFCE08D187BC04F58C7A450A59093680A0F09E8E3F12DF5223E7EAFEFA01978F +D8354753A68022CC92C71F2CA732DADAA8A466D4AAE5999B0DC077715671F518 +E6277741F44AE798EE50DF44CCF71FCF8BC71F76374005FEBC4883C6EDA854B0 +88C0C2B476709AA809ECE41AE786DB1A32B3FBBCC14921673578D3514C8CA842 +E1FF90BE33F7B93ADF6BFB8B1AFBBD080783BEF056A6BFAEF676F7BF9F2DFCC8 +01D255A9F0391951210D60D4D4DCA93AA858B38C0D7B8FD740D5FC6F277C2A68 +54CC2DE1F40B6347201FCA2A0A91822708D820CE645C3E4E5A09FE25721AB33A +97871ED448F38FC5A349D81F402B34461D840D5768BFC6849439AB6115104F78 +B87115B1DAE12542EA898F86ACE247709817850B067F537E6137196101D46DD2 +D842EA03EF4501E34074E8458E638ACC4EB349A7430AB035BEF2DD4CE00554F9 +18F9FE32A55AC1E7E50D64AAFDA278D77A7149C59DC5B1E3064A4B281A54C9CE +A5EA94ABEAE4C6D5674C208ABC72563976487136AF2E21F835BEFD232D7F0D13 +1D19932367F51D5379934DA7F1635AC51EE5CEBFA63D4D32F018DEF13624EE62 +31DAE68A08DBE3B4FDAAFC75291C8C6CC7A657E3C7453C7D1461A36E88E633D5 +408253B673AD87A9FB2D0F56DF1305916D14D5DD62051E27BCE09CEE9A1F14AF +1D7164BA5FB6E6EC8D38750F7E28BE330909F303ECDEE692E347DE13C8C2F82E +29C8BE6EFD76546F362A12A1C2DC12389EA95ACB4DCBE95620F0C193EAD91B33 +BAAC5801AE827B9AB3FCE5D11D1D7854F8FA8A31670119CC0CA98628F801838B +AAC7EF90AC5466BE69CE3E3CD9951A5EB9AC08014285422F6DA6F6E221BB30F8 +0042A11F2E4B765BB0D142AD52F4D85785EA71B2E1CE20728B9E9306CE93268D +99B822A5AB5232EC7E26EE1160850AD3905864A01357F22722B6A54D4EBE58CE +480EAD9FBF068EE965AC4B5FD2FA8CCB91ECFC6E90B9C49268CA0B0FDAD23ADC +D5A74B41149BB08454054C451AD0DA4CCF8B60F2EBD061AA03A011D548B6B481 +FAB00AF9225BB5463F27FD67333FB51F8664536267E95CFAA0BE3BC1B8F889CB +587A3A4FA2B45864F07E11372C9507A625C0030EF7030A0B4D931BCC48F6DD51 +A4D1F63FDC4B59C1CB18E6242E9F4B4B8AD9755B870FE60D640181FB7EB8120C +C56F51DC8C47FCC6318C2145EDCBEFA7BC4253315BA67FD2B3D4AF6A9F3F229C +AB75B592EADE15B1FB5FDBA1C0F786BD21A51506B7A2E42C2D086BA6F84D1B3D +AC7531545F0B01346831FF36A52CAC1E390F99AEDC265B44B0FC9C581BBA6BE4 +48B723811EBCAEA5FEFAEA7E5B987F2C7B3E9A65D2D14A7B74F099401C57E367 +385352D0776D2A908F7A5A2E4D4160946C5591397877025C8C387CA413EFED56 +8B142E8341E349DB4DBA422A4FEE56A573972A0C66590175158E48850A9F7F38 +4B95726787B8F969FDBC97491CC81CABC976CD00A27D1DFCA7CF467A956C1C6C +839817AEF8794B6151FAE9261119DD5DB787DC9D3B420FD325ED6599FACADE0C +320D54C2E0D296537E22C1783670A9D9BECAEC63853EC2F05A990260DC189D63 +7CCC0BDDF2CF7585071ABAC14630666737041194D0777EA4292AE60BD7F7100E +DB568C90F0D899EA006CA423CFFD6EC70A5D3D8AC43C747DBAD3B02219E47D8D +DE030631F4678C357A58ECC52782B31B50CFD44EC33F41585E51B27E3997D33F +461BEF897220AEC80007F13C5A1EE3A0430CA899047DF944831F8B010A7DE74A +BFD26001472DC00CDC9F17CC435F61ADAD4E9AE062ED477FC621FDDF9242C449 +1BB3F77FDD1519A251B663A693D84B42BF0962F537757F38CE5C5D56B98AB10A +3B70C8AE8D52DCAFCEC22E7B09D3C4EFDA1841C74CA975E4F8294F7BDC796500 +0ABE197ED3737A65F7BAE601C91DB3983EAE11DA3EA18ABBBA3650DC361C2E77 +EF9F97618B0C337A906FF39926D2B0B7883ABBA650816C4C6B34EEA836994EEA +AFEDDE56E0099D0E09EB88EB093544B9BF4871200746A0409C475FC4232A38D8 +F3105B0FF44E4F132378DD12D9E796412FD0F9478322215E9F59E69396C35AC4 +097C4995B2C3BAB2DD04B1A7097DE16DFDD76465E79ADEEBA90489ADD0914EBA +53E11A43ECB11D072C68D2131BE1C7C43CB9DD5FBA0A67BA43D6851AD4CD3BC7 +39AE2E22CCC183A56CEB71D4F9F578518E376426E42B6390426A8434B5A83E78 +77A5B9963BAECD5FA5521C2A29418764E4EC1A72462B04957F823E2817A7F8D0 +1512919889500024B1C42EC107E8B8533C0B314EE4E23313A4C1BDB009A20114 +2248276B7E4F8924750C8361FDB1FA328B434499FE5DF14B96C1F5D03F1F2702 +C77057AF82D3982C5EF8EA40D0B923249FD9810464A9EF6D1A99402DDF08120F +FE77568235A193F5DAF57231E5095D4EF505A0FCD8E538D53C5E23E226B487CC +568F94438B050A4ACB53340A4054B02E8F4D8BB42E72FFD1124BFA91C22F3F76 +3F3015809D6EA1CDBF7827ECF8893F550132A618BCA2B9CEC7A329460CF59E2E +3F0E70D05F42D3E37130429C984D24F49529F647CFCB675C7CB79749E35DBA8E +E8A0B56EE8FD852899BA52B789CF2A5D4F1C948FD5F5C57DC8475E65C83FC690 +28EDD11C5A7149D68B924EF92D73A70D37AFBB433D1563A034361A4415C1722B +7599BA7ABFB5D0189D9071AD6FB28ED80984B44BE2C04371BFC57AC36B918096 +0C4D04ED59C111A6468D2DAB84018930FBF7D9E7B3B43B5349C4B3B7E8542409 +B5EC9BE8C9C04FEB470E28D3E2DA70EEFB29BDDC4ABDBAD78386FABF443FDB1E +9458FAA05E9362979CB79CF51BF3FDBFF05F8B410D76AF9D749C161E5A224793 +9094F212E87A697BA5BCADB2F6F8510242AF911BBAF8FA5C17172A713FC97711 +D1D1C506B49B6FF85A713D3DB9DA5CB6FD031CBC44A898B16C18B8EF65366132 +4006962C078E68558FE7F2879518335A2E23034A7B8A6771E95A869B11D908B6 +2530CE5551A6E270F91BFB06EF448C22A346B7D011F62241A55B7F7E886C21B8 +E41BB806659B0DF80DE854F883BAF1E21902619AFB6B248DDC7FF08AE56706EA +1CBA3C9CC443721F7E365E0CB81C1D0B453A1FF8162B5DFC6C4FB7D613D7B93F +E29E4B38DF51B1CF584013D6BB70D422848585317384B2FE34B0A3E7B091B8AF +86FA106BBEE6B59FDE82656847C78E629FAB784FD7C610FBF9F0D6B763748D4D +C90D2B3EDB4CC1B269137D8FA881FDECCBDE9FF84F599911A4A8C777624EC2FD +40EB0A8EE71E6F9E99F0315B549A7E51FDBA35B26CDDF634BDEEF6F561DB4298 +A80F2B7B3BDD68B2D13EF31DA56B1E609CF5ECE6E43DBC72B344872C7A729F70 +1E846C63164A69E30112E34BC0BD001F97EF9BFB5BFDCE7E7DD10197EDA39BDA +3859C778E31A9D0D4F6772DA9FEE3CC4BF32A40E607EEA5AB07359EFC5E44C16 +610C9F5D8915B05E06D934D6173B4671B4BC19F871500AC983540103495D18B9 +123793DECF15C3CD213D5D57A2669E531D44635EB81E999755FC6111B8166C2F +A6482D10F456A26D7D6EA00D07F542F6F2466DC6A688FF2E4B595F95C9C1059B +0462324ADF96BDD8C1BA32ADE212F9A779D7C9A87A60A98AA539878CDB2C9CD2 +D05F1474A16CDDE4D3BF0077B86DFD856FE19A9D56F0B30D8D2944E103DDD9F5 +477F6439724B553AE611BCC2422D59F5F8833C9401D4833917FC46036755080C +06365D46020CA44A5F0E968B0C590E87CEDFAE3AF6F04E603B921391789EE9BB +E647B340C98BC48D1F95164DA5A769652DF1AAFE1DFB1DCD5BCBC33C744E29ED +6BC02DF741FAFC241B94E76C507C04141C0A5A4604C18FB6E15549FBA306FC5E +40690EBA17551BBAD0DFC3F0B11EB92C3DE5471FF5D5E295473FA870E538F218 +A8C8B728851F072BAB4ABA19FD87F630CB7F7F3633DCB5A011D6B96B40BB6221 +FCA90142C929BFA81548D93A4BF8A45FDE9FE4263108E6435BBF67B764506DA6 +6981B41C87035623AB8DDEC1FDB046D18DDB2BA476F2968B59A85A295B41A6C9 +B3593FADD77A7F2D70BEDD7A0C5A378C76D9D39DC672DC5733AEC958CBFAA7ED +8F5F9812DF246EE3040E09CD1B0C94F2D876B17818FA33A9CA5B5DAB4A546D3D +B85EBAC2B452F6E780BFE9E703CB8AA0135094671FE92F6607B04E0C965A741C +1041B8A59CAF3778C7327181ED2CB210ADDBB5D2C0496D40268E7C5B41206419 +A02D73CCFFD3B2F6856F00598577E8E2D12AEEF7A22B1639E54E8D59997FE5A3 +763CDF1006D3E37BF67EB1C8C1EC35BC4862F26220382D3FFA3ADCCD25887758 +9CDE8E6380CE3B5844FBFEB1C1B4B12B1E6DACD6952BEBC9D7BC39DCFBB8B36B +10D8791174B20CE7FDDB84E43F7B8431E9CE957D7774EF445540FB72FA772D17 +DC0C04101CFBAE78D08A21B108391523B024D9300564FBA12E1601A64C5BE673 +5E3938A25A7C6C94D7A3E789B9DD3F4F5205AD538CE134F6F0A38065A870932F +DB33CD22E129F00F092296359E066E4444FE8E19768A1320C1A96BA6268EC037 +1F105785749DAB69BB9A5FE6FD492CCCCCC7E91445B7A47A0FC675D47E45EA68 +97FEBBC25FE0335C0C41CFBD53A6DDB7EFD2845B039BE0463D2D73DAC5AEF9D7 +5056B8400576CD9D8E0E2FAED625395A9EF46AC3D8756AFA777F42385C62D7E4 +DE47177893F8C30100E3A95A88DA9CD787AF10B98A373882CD9FC8A8F3215EAC +2065C13B807345F777423E7CF5AFAC338DA83C60E1B8B44CE09F41E4489AB271 +125C0A495B91C549E5345BBA213B3DF67CBDCF87260BDA6DCF88A65D484B5DA4 +92932B8D03284DCA19410F948D3FAE378D63192A150D1F68B2BE085FFAA40C9B +B689B6D734D965CA949273AA7ACA492050348D623924EDE7A7414BF639EB111D +1F77CAA169A6EBCB15182FFE72C7B0C8EBC18E2506971F1E3118FE002CACE569 +7BBB01B27459E9170094FDD8784E5C2067E4206F8D4FC644E412D7370C16E9D8 +3EAD9C2BFB193CD6AF8188993F3FAB7DDAC6D0BD19E0C9C968742603F4FDB07E +45617BD2C4F2EC9E3A40C4467CC7AE5E50BD38A875CAB904B4479555E6D6C829 +4A6E66272CD25D5BF05C7D9F8105EEB6AD2532820E34E3635684E15C7172E179 +52BA7737D4A9C3D1CABC39FA756406669E34C6A03642969DF0E842E49E39C39A +91959907ACF346087E175AFDD421CB74BD26BE5EA6E9777CA6BC9871AA031DA1 +E05B54210DB0231E2209165A78D15BBF8F086973F099DD446A80B36577675B73 +5BA4F1B1EF400EF783F4B4D0ED4D70B881F54878E8AAE9E455D662DC9AD78F82 +3B871065B632D737F0BE672B0F77E6E03980B2745E7031B71671B3D983AD2E0A +F83C1F91E427DDC07BA100D742828E6097D5F600CD7F07F7C2A283B094C7F12C +701922BA37950B4A81DE30CD9C66CC0514F24F0D9F56951F3123AAB586AB7EA5 +DEE197A79B3D6123BAC5EE9F5D1BD504FC7B06782458D16DDF0936CE420C6B30 +35C358C846C460C055ED42F1BCB54610EC7F9D0D3A719DE29C38BE710E8E9518 +9929C046883F6245458610A696C18122E377E4FB9FED6451937A547B050C36BF +63666A74D9BA9D65BAEDD10AB3AAC2689B52CD9A667DFB34D5956360F32A54FF +FD93997A857B979B231D246CE252A1D3B9F34DCCDD81BDE75FF25E935A614069 +343EA6B342C19CD922DF9EAA855DFA8B0E80636B04608AB3DD475F4EC97BF542 +CE8A2B6650F43E29378B0156CD886043122E743802EBEE6DFA509AE0B8F4B61D +22BFFD4EFECF595A0950F092841233ABA4D628B95DD364801DFD444D3C52338E +75091C0775630715298559160A24B4C2B49EAB6787FD591F764C6B5C97A10A70 +9502B3C35C47B8DAFA92F702A93C872DC424EAE7E67C894B5202CBA85C8429D1 +BC070C316C43FC50A8046C4A3C21DEC9668F8571F8C23DC032F0FFF91A263532 +8FA97D6A38F194E93DC1BF725F574FFF409BDEAA98DC2736461164B6631FB91E +1E2C40939F73A093152DBBFDEF61F8D0107867E262B64295C617B42443C65102 +35189F6B19D2BD6F3404A966445B8709DF077C9AEAE7F4458DDC0D3CF9AB95C6 +AC29FBE021E17A61F547BB75FE3A610A0ADEED819A5BFC19BEA087A2E4D31221 +DED7AA13FDD13235C6670504CC7E932320DF993C5A8B675DE2BF020DE3203D04 +5B51FDD0EF2C3F7E6EFA239CA76EC343146AFCEDA5402FDE629D304A4123B305 +DD23755EEC8C7081535363ED0F948364BEA64CA6D4AD9D6F8761D903011119E8 +84B548D9E49152879FD7DDBEABCAEFF8791B2939996F56647F70ECB6103C1567 +8BB4000C944C7AFEB7C8A529C3E984DBF854B58AAA3209E8E366877855A449BA +44E0D0D3F672F9DFACABA24E8472E3DE46050A4CEBAAE39DB4A5BDD77C3BCB24 +EB90D2796BC94683935F209762D1FBDFE1C7D140E46C49521942056EC5FA0C8D +377CE689C8DB405C44643DB2586D91A8EAA0A749EF6055AC0EDC45E9C4427098 +80A0CA45D1441EA5B25679B1C2EBAA747396B9C255642EF309196522C038FF7E +2DF37C60243B99D69832E0C6BCF662380891C3C2D89269E888FBA1E2E247FD18 +1E1B090CB6829119A7F54E7473B686979326F7B0856632A54FC32311049346BB +C99C15441B2ECB026B36DB0BBA0E2FDDFBB6CF6BE997669611267B266E533EFA +F212119A39E2D343934E3301A051E66CA8CACC988418E0C18DE48845BDD45173 +F916D909270CD6D2076406A91F12A96A16A0DDCD5FBD9128B5D970E7A762A173 +10155B53D25ABCCA982CCD59BE0ADD089C2C14E044910B1BBD6F07C04F86C4FA +63FF940A1540535C2F0450CF7995DC6A9BC9C8FFD50166649E3C7949BBF66DFC +0B3E576B35D048C4DFAF41B77E0C8B05733933D6B0EC509DFC856CDC26F2F833 +7E2E58ED0545CC2D3F91B040136660B2E5C8F75A8FBD3AECFCC3CF3C4AEFEB70 +9BB571CBD6D5E0CA3818A8F08F2A3CA0969CF73CF8C043B69F37A4443E575E05 +D9360A2AE0135F3C9639D76B13B8D8EB64CCE253996A3033EFD35EE716C9B475 +258F7369B04E939DB01849839A64BD50825B9920F9A09639824AB0D3E54D5ED2 +A710AC5AC35FCBFF1A7242B8AFC94CED8E7B141D21781B5E9B97CF1804F229F6 +250F79AEC28FA27C2008F7E7B6463504A1EA635567F3E91C80CE1E6E68859489 +436C06E599AE578B9658771164B1C8F8BCD23BAA8BA947FE553B4732F9FA2ED3 +1AE7F577BE287E672C035AB8E72F8DB5E31465076B7F2EAE0BAB0944BCB75C27 +FE822D07AF694958420D7F15F3A36ACF7A1C9A92FABC46BF297B523381A3D910 +B00C03DB2ECE60F63DE0E1C2DAA756F337D7C2E961EFE7730ED8844E0E95868E +20ADA11055A4CC2C4632832574690CCE7A8462EA4D0A2EF4F8C354D2F3034DAD +9A0B1DCEF4BB70D29D598F527D333A0D3A73D1DFEC7B043774CBF2B49C055462 +2820092AC798A3887D577280F038301F44440ADF5F5915E1D560A5E1E3AC31BC +ED5A53CA33ED0ABC1A65D3135D7A4C7AA26DB122E83F83A5BD4E16A79BD79055 +934E49D59702A6B3E3562212DB0D2FD4ACCF75B3A59D2F196E1277A61CA1ACD4 +53BD0AF9857EBAA8250D5985B171A446D0B15E364FC43F6D468FA073B85F892A +3071651E480775B0365C76B5FBD1BA770FFDDA7EFEB032090E6192DC4A98F8C5 +A0A5533F22B82159995DFEBE6F68A163A5EA2763619EF56D64FEF9727533B061 +0C306D3CA6C585EE0491C8D95A5E340B8A2373393FADFCE32F4D00957662E6A4 +32895DFEFC20206F49D863E4C21AFEFC9858193250949D12A2611C56EF4A16F7 +9386D8DF7D1F5651ECF8EF632D5AB35D204F4C94088E9087EF80ECED40191E8F +B3EC55E81D38CC91BF432C44DD33CD58CE860DB6D27669861CBEEF9412412999 +1D4F1A70F412173F348423E55977550B4C8D04461FE539B0A69050A1D46567C9 +924261D14204E9BB65FC448A5D51C4C1AB09203B4CE157160F1219189FF54777 +4F2D513745403C7A9D9395A11171377DA8A05EFF2CBE0BA9FA54C6109D43E9E9 +51B5EB5A7338F48AC520296BFD2EE545C5DDBC359300F48062A4E36B35F3F293 +E48DBB621DC1057A12D01C9A4E2AD5D9DACDB718D61E2B926044F08622A9599B +FECD9E31AFBE2AD3DC4A6C1D4037AEEEFCA5DD3A3A6F8FC12F906AAC34EC89CC +F92C44477983A10FB7CE6CA3606FAF25258A6FB58A9FB1C0EFE4D1E725C0B9FB +DA6094A41A29317EB96EF0E7D6FF340F3AA197A3B9175D7E50FE0C62722D3246 +DE458E4BE4DC7208EF619801D3D73547075E9752B2F2301259930CC3B5E8618E +C4FCC4954EB0ADC590F78F68AA9B5B2D1CAAB63B269C140AF8D161FA8E78B7D6 +BF33268011CFBFF47721DC7E04D7A216B3739FF116283000D454CF7CE04B0F16 +3793087399AE2CD49690C44F8CD2B0DBA5DF316B57F3AA9A66A0F65768BDC385 +3EB6BC0F5FE11B4CA86499BC1001B0BFBABEBC9AB3E5229FC92738D87874CF58 +557E726D6DA8BF12029E24E2AF6168E3F13D7F1417E6DF7D8B39787A77B8C2F6 +EB60E78575AE12E42BB9AF9ED2AFB6F3265720BAC268D1C6331DD05B4569A4D3 +CF7F5D5646855CEB8F0F792235480A76AEEEDAE699E47201EE91B876215E6F8C +DC05CE3BB69C07BAA0BAA8CFD3FE8598F71FA32619C55DE2D81665AE5853C70E +462FB90A5996C26A0E4DECA41B66FC114D21069AF9DF16464A91EDEAFFCE8183 +594685DFCDA6C29B9EF44EA9AC718863FB3DE47DB97F9E0B42C1F2D4D86B1625 +83C3181D4EA935C0C307876AE65353EF54A66A10B0E9EC655BD8E2037B7DA1C1 +E5C376E9C56050228C56CE35E812EDB26DD94C7C42E9B5EE7EF9B2A7EFEFACA3 +04DF83C1777476FC200DA78DE9A2814D8CB8E1413C907E828903C137F525215A +C8FA7EC814658105A0A020E4538482F7B347FEC0063BCA81C516171AC6108BE1 +588999F7F459E5B21E2BB4F54C8F2D6FB9F4D651A849B078D2DC108E6A2EFE4F +CFD6D4B29C00DE49573135553DEF1C32CDC0C16627823D5996416932EF4189E2 +E6DC38FD749A32AD2AA3EAE7DA8477F5AA151CBCA3B1C804E80AC00050BA8027 +5F5F4984F20F9631B9A3DE81567B1EDCE9B1B218F9ADE39D9FC85B24C801750B +BD84EC20AAAFA54D68C00905A29D33212E01BB2AC2C8A2025A31A5F5E8D1EF38 +E4279B55A1CD38A36120440D7F4AB8EFD4497BB27C99A12A3DF6301BF4400F5A +89913C1CBC2FAA8B370AF6463FE4886729B7C1BD6C9740B7CA8B64950BE7119A +675EDB295962427256D2922D6FD40E9FFF971AFFC185773DF868C82593CD9FA6 +EEF84D9322151C1BE8969E0E15EDF3B9CDE50836B37383E40EA13A5C432E462B +B0B741B4C411A98E326467D77DE336AB42836754AD83EF5B8B09A55325D048C9 +E80C5E02552915000A2CE9636B68C71642FBCF7C5DFC155BD3FD9B9AA9BAD862 +0A097070549C472D3ACA26290A16128634D4541B0FF4FA4D67679318D6CAED47 +3FD289DFDDFA33F4BA65EFEFB82D98D9884C2D7CF0ADE7596058C2BC49EC141B +4C13075BDC71E17BC7D96B4C47DC30F66572066DAFA6E8467481D16E8AF6FDED +CB7BA0D353501C8A9163F8CC471DA83D713FD9F1E654B69341A5CC3E9638FD5F +F02E58B0884D454FB4D02AFE75357AF11B4B430002D0A469781F1281C22BA8B9 +4A17EA7A28ACD013495C823EBBB3650FBAE5A4B714746318895A3DABF932D845 +AB1E57FE5CA00564405DFEBC143DBE82F038DE1CCA1E8B154A11B45200693D9E +FD2EFDAAA3896DC08ECAC86A429D2F3D8FF0BF7FCAEC65FB0B7EE8C39F140849 +A60170A5DC981D6280169C7ECD54C7F71DC00C81290E7186F29EA5AFFE65300F +7124408987B57421B9B57F792915C8D2DE198DA2F943E0641A8704FC0B84ACE2 +D580685AB944A02676AC7ECF825A291AFB0708B1E3BCF2504442BD2EE16F987F +187981A2F76F919D4C53D458C2D733868ACD95657C6674ACD513E543A4E271EB +364E84131E6D4FF01001D9D70BEBBA40552E83B960B624C72D8629CE99225EE8 +A3B77487BE3637B1423E0F8B6B7C766ECF066B22AE841997CEB5E64127B7D17C +5E6295355A68BFE772282E54E773465B1C49200BE925A30E4F31971B59622D37 +48C33E5C72FCDBAB5735CB0FBE9D48B1268B7AFB245DE6EEE4BA544DB637AB9E +E9B6FAA22C2463754C653AC25C884347D940A4640229C21513E3BF1FFAB272E0 +0CF09EC78001C34C2D41AC1CFD967BB6DF6CE713D194F2BA8DBB0A3F9F2F2DD0 +044AA1910950B4ABBEFFE14BEA5B6460BB853CE329605E8652FD64E94C74FE2D +C237E18A42C47DD0FA1E2BCA549B40AB4FE0008005CA8066E09327082866B12F +2294C1EECDB9AC0F47148D6B0B480E34D300EE501D2AF01A5CC0B11102030648 +B65B330AC0E7744C7230D8BD1988F9C750072F89D9D677F3E087BC8FE6A74868 +05DD1C7C091D8B3CA2D593B23BB8EDD2159446D9E826DD030398AE3D62EA2834 +8896A27E343B095D6202528BA2C58D676727C1AEB5191571105177B4F1E8F1C5 +653D62804830BFBCD4BF178A70DF1A14EE96F73038F7C3833BEB8D1BEFAA3113 +1949237E082D4F6A7F68C021B8D6F65084F61554C754A35589C3870342A5310E +67E85B0C98F3F5060ABC03604957D553F90FC5DAADC5DF81DAC7EB1201C7C7BD +3C9A52B53D74216E5244082D4B24A6E9D119F956C17E4D17E6A1920B5F641066 +620BA4D2E4AC10DBF95DA79D949C6CC64769816BE58B23D2A50E91C8A9691DFC +CED29714ACDDE0A2AA4A5E62568F29BDA67287EBFD6236D0905D6725BE102BD8 +E28FA3650260BCD7F8E44096DBD33A76D830A58436E6CD6A496F6B1F5C79CFE2 +C3DE376CC015E02D932BC9191C3777C7EDE1229D9E7D5EB2E5E44B4EEDAB9B29 +B394168CEBC2F4282ABF08F8387D42D228C6892828054A80F487899343055849 +49664B6FDF576A852AD24BC0953ED6267263ED9F72837D547506C2B56D614203 +774BFB3857657D49CBA22C988FCCFD52BA914CF7E6C8A514A9DBF72043B4DAB6 +ED56B47E0310F23F0A3584B764314DEDEFEF2677D65602DDD0C29C92C33A95BB +4FBF3116FD9557CD3EEF24D8E8176B4DD30FF414130A3F44DABD3D30A024D7DA +E5C9720E1C9B82EDDC4030D68C7668A8EB567CC78BEAC75D3F0B036928740825 +96E95DED2ACB9E4CB113B912CC6FEB30FC68C7775AE9E763469E7061234BC403 +51A7A17AD09B384E3A73460B6CF69D5DB94122927B446C0B4DB3A6FF8EA9E844 +478EE9D4AC9B542431279EC9AA11A16AC9A4C0A13B30B83AD4FE82077C026A57 +B048A170769FA46D735B6615D8C7848D8DBAEEE82FAE5BCF7DE73AC18EDA48CD +AA2E64B002C91214A2A784A527D88169C62D731B49DCA9E613556C2CC6C6E6D9 +3CDE03DCE88B5C8D37E7BE8AAEE50EF2B0068D171A4C0FF96F901EE8083E8BD0 +0B108257F9905DB23F39060AA70D43EF8473EC053D5AEF7AD81B5087958B974D +E2CA39119DAB442E0B0274BDDCD1C0BD1D2176F52754AB2602CB2C7FC62A47A5 +0559E793DA77C6B0823BE94C6D0E95079E487D13A02B3A93204F028B5973CC4A +1AD9E476461F2A2BC5B8602D0AB6CB603D9A6F4B24009FA9C361B256CFCCA237 +8CBD9A6E0C091A7A2F6B6EACD9DFA2795392DF3B1E1D14B2A32A7DCF8D0A4DD5 +D8B56B79C4A085791BB013B79AC01AE83CCDC0C00C971C327A97CB2F0E7E55B6 +55D80CB0E361EBACB0195BDAF934535E9CA33F834B13DBDFCF5EB71E5E756C72 +A5CAA60D609B4DEFB90FC156BFBBBCAC174B375C6256D5A28EB7D7FD7F8A98F6 +2DB730ACDC04BD58E87A6F04698015AC46A043FDE0682882D72412345E486AB6 +ACD6F2BA5B044E9BD395FE2FD7E7488204EBED26C6C5F7EF1179C5CC6D18A08F +F8B17CB299947AD34AF2D33DB26477ACB9FA0CBE01EEF447F105F522788CAC1C +A3539B412647F41D58D1732EC4922FCD3CC5CEE1754DC0580695EBF753873809 +B3BCD4EE5483410C3151E3D4AAD78B57683EFE3690D514192587305EFE935D2F +D77B2DBAD1EE92693F415603D0723E98F8420CC1DBC41FADE03226DCDEFEFE0E +FB1404543D5863B4D0F64F6864A041893A9298751E54FD208DAED41D8290E208 +EFACAB9D3871527AE3CCAC9B28D9B1B08935533B8287D90A2B9F7E08225BBFF2 +249ADD0D7651AB852B7C4548402582DBB27529167111B5E30935227190EF5748 +817E859F3D76B88802ABE17ED74DF24D0E560BA287175E3C614BE1C95824BE65 +2572996AE849F08D830473FC7F51A3111A26D551D872B007318637AFEAD2E916 +60FF7CA1E4684455C20E1D627CF89A3765E7D1ED00BAB0EADF403BCD3BC89209 +DD5309DC1D05A1A4BD4BD6B945772B863CEDD6522FB638B8E6548C4FC2322F60 +B113F497D495BF71F985D1A0B280784C32E29E9D4DD35014BE29D3611867EAE1 +7E0692D2F8D4BC242CD384BE4027DEBD5886BAD327BBFBCE0511F0535FFD030D +748D483526EEAED2AA840A307EA19B3FD0655CEE6C3A925F16E2FAD4862E0DBB +97EAAACD2EFD85814420487E092F08BA99555A911DD39F0AEFE1981B2510B6B1 +0B7AF374ED7861E0391C94112650DE905AAC7EF92E9E010B38333F65FEA8847B +7EBC40A70415F0CB15F3003E8B5C1705E2A4FCF3F7718B56541CAE264E122A38 +EDAE23A9A2690A90CEDB2DB223E6E1F7AE9E38FCC059DF6B0013D0CE31A35893 +3C39B5A9BADDED772D5968ADCEA22995E59A0ACF3F886B5695F0533E8378D74F +BEA1C7B17ACACD2361F7AC0F36FC445D2DFC346A3D4F5B88950EF9DA684FAE26 +2E43D2D05623029213372FFE4039D587FEBD7EE50F9A2609B4CDC05F78D35DFB +995100EC27271E987D90D2E8EF483CF76CDA5A09A0B7B592C97AAD4D7B7154E3 +4DE7DD07FE23D5DE182AB5C3A9D7DAD4849B7E058457429EE565B4C4AFA40FF9 +78077B5266F65623160C692D066209E6F0CFB058CB1A08E456EB8C62966FBEE8 +989D3AD6D51A81A28DCB4EC5C55AF5C223728557BE8CCC1A811C8FB75DF43A7E +35B9C83A20C9E94EEA6AF1192097D093F03A4FC3C35F89436393C9853312846C +E0A9BA692F47E4AABABDC09630634BF94F7253D25B036961B748F790B34F3E6A +1978882C87D6718702E4251BF144F1D8510A4BA9A265F950C09BB111213E7386 +78C6F174C81E98D54774343C6FF3F54B21DD7097DCBD7EDA23A69CE3AF596620 +79E005ADBF394C0E3D96AA330E2D68A5062643E7EB522F4CDC59276BFFAB8EA3 +760767161EE5412BDDEFBB4966BFFC21D75887B13732B9CCA30211EE73F1086A +E29A739124B0D0EB7EC9AC78D380063A2240FCCFA6434B8EF2D64D852EFD9231 +8C35BA8C19EED41ADCD2DCFB924072A999995D4DD9DF0E664584DEEED64AB198 +2D5F9944411C7CD31247FE83C4CE102EA6C5F73536705BAB703F39841E382526 +A9F18F3BD704C67FE7EA8EA61DB1B0497436E6A289D8A85A7A903F4411CD67A4 +17C05D6EE36681FAC99971423EDE0856CC9F13D0B931DD6FA82A6FDD8E9CFB56 +3795E838B5EB8C8716CCF4EDE9FD9BFF0D39C3E8D99065E5045CB8F037FCB2E2 +703D973E1499BE0487F2A0E5D758C32ADE9004684A3A576DD2514B703849134A +3DEEEE8E31514BA84EBB23A9FF63C955FCBD40894577C788A1BA27D9714D7EC4 +557F6940964E91DF8E192EE892E482A53AD81BBB859DC6C3FCA2957974D5C140 +3EA94C9B96CCFC88C5059C863CCCAAF237F53C3DBD7D1903E5ABE3582E0D26E4 +56907ACAD4D56E6EFE2D6CAF5D26502A8AA3DB4D378DB9908BE2E33493DD268B +47DAD87228F0930C33C77ED7BEA44A3BFE91E667A371BC1720C6C89474A29ABB +D33E86AE38DA1AA9AEAFA7CFA70D9D720D7AA09442080E1F44BAE5E044ABEB57 +E2B9C5333741D37EEB99CED5D803ADA5C8181761F45B9B18D21F391CCE4CE451 +BB675ACE66159FC6C19853B24492A62DD89C24F399701B60C76081970055D616 +670AAC77F66B0EB583FB5D1F8F48F5FDF6E57AF484FC4C32C67F2A46CCE6D823 +D659733F4995E8EA17CCCF0DBD6204AA45A2B3DD69C47DB7E2EB8B1B8984379F +35B128224AADBBB3E3456CD547BCAB3C78DC5F55048242F1A973C9D538D660F7 +4E724D0A41844571AD9141CE949F16BF4E5D93A16DAF98DB46AB26DF3A4ED09A +4AD6A0CAE90FC05117DE2B8A839F03E3EB7DD6B7E26A3E069992E9C0C1F0D680 +C2C302DC6FF5630C82F3E33B2F066A226366280B5C6A15679673A022CCA11709 +331AB13F81F6166F7F4949D9A29C9056C9147F537F889BB84F36547E859FF4F8 +A30B9CE9A94597F764886F4BD4CBD8835C565EFE6AC212A20E02B8F341408C1D +5B56E7DBD96C4FAC43EF699277DC398086AD6148C71D082065296926EA00E0DC +01C4AF201365B754B14DD0DC8C039AA68934BA8F00FD019710D68BBBC84A6ED9 +92956E21CC9F47AF37E0F68633CCCED3ACE0F7C559B11B356CDB8F9BD36BD64C +9F38132B706E69BFC0E312F601BCFD379151D9B5BBD8BDFDB1C9913179AA9EFA +5956768BCF44984356B8086CBC2F3FBD743B12BA81BCC001BF3893A6F5CF8046 +4718F2AC850A03DA5022472CF32997B526849577744CC541303A1C4DA1FD252C +43EE0DE89849CC4E2E15FD570B7336E36A182A898F77C592DEC60E9E0B278F43 +6E4EAA822AB6DF0CA390B1D991ACFBC30315D83785254F9F28277E58D739CD13 +360DFD764F9116F6063EF8F4A820D97E7E58E3B44D901B3209A3CE17763BB567 +585ACEB22B4F93254096C3CEC75013D2AC8DA569AFDCB286A19F7A0EFF0FBD2B +BFFB5163DB87F0F37F3CA7AC66573FABDF753D9CEBAA361D0029A44309C13B2C +601E79D78EAA1DD899EEF9B59E1F83F04FBBBCF4FD29156B9C10DA4CCF8F1FFA +DC449BECCB5761D24FED596C9D34B093576880A985993EB40576F2C1952469BF +5F17C62046B4494D36D84456280884478A09D8FA2203B8F2A6E6D574C3FC4F79 +55A0BB010B62423B767C2086D47F2E2DE9B95724CC6938E2B0EDB8D458B3C92C +3F3F27DB16C58D321A721437476CF4BBE989E1119619E601A6D9147955211C56 +7ABFBAF46C5ACC007AF4DB6A7FF446AB40527D59888D3D0FB9C3140DC146F2FB +F36EF680CE68ACE95BEF6C5CF1ED0EF226FFF525FFBB5BBBD3BCAB9842E422EC +D4284E08E3ECE1B5FBCDB3F536F6EBDCC1A41DF9438F8F9BF10E3BF7BC49B141 +2DD9C623CF65D29EBD67F96BA81CB2DC859834A45788DA0B12A698F5F97C060C +286C2CFFE0E31FCC409A8DCC570468977E2F3F5FF411A4EF1F20224E64441C55 +6CC36A5438BB15AB826F7297B08D2315718C02B690E3AA2D689D2D8AC1AEFA27 +62C1E4CF3114254159D5C8D894CC50E692CF863E6C347CA86F048C7502DEF227 +5F3E83CCE897C69DB5C3E7C4B7CD9DB0E5B777F4CD86E7CF03B35E39BDD525DE +A11F632DF68B65D863F30ACE081905DE99FEE687964825C1E7F14511981EC483 +B8897A72E3E4AA3EE892E66895D8C1857EEABA65D2C33F199C3645346B2C7169 +EFB7F8C3D490C6DDCB66B2B212C67BF20BEFF7B471B5AE96E17B920D9343AAB7 +6B109BA92449C6DF6E591A1FC48914B2A2602B6AB1B219E30D022050067565FF +AFA0F554E5A7140E47C00FAEFB8D547BB1714320149241AAD482CBED45EE84C6 +7964F341A263D447E0605C3F12640C6175B7263704D1884CA414FB718348AB0C +FB0EEF1189C0304A5DE23153AB7E9C8F9B894F06D04B2700070FEA16813E3171 +8A0053710772A1394B2492687E308D6122DB6165E222167541EBECD68E6AC145 +E1DF1550805C08337B1017979878A4ED5EF85398EDAF84E93EC30525BB2DA6FF +B1A4802EFC994E55474CBAA878A38339B483FE5D6C85F0C7CD4AB7CCE0E1CE02 +D0BF17A127EA17FC048D19D616CBF282A9CE2BA572DC0DDE126C3434AE270E11 +4FF9AF7B63E817DE371EEF9102B37E69FBDDA4E2789F3C37C6DDA28A9BDB40EF +4AB5A776CEB1196C25F4C8D7BE6C44FEEFC515677D49072266FDDA714B863916 +A833887224AF788B37DAE91542ED1D95C7BE294B64D18AD2032440B29ADCD4B6 +1783E47CD4B1F396BE67B76BEEA7B6232BA11619B001A4EB5BF988A99594E1DA +A5D7C27762F704CF92802F4FC738C3BD38B2EE2BF518AB86A2059F8550B6595B +F191B97559DADF54BA85B472D1BF4C0C4B4F1C3B3824FD295A97228099DE5E6D +6FFF4A97107AD28799EDAE4167DAACCE7571D6DD5D38B11BA7DA7B18334BBF94 +C2360EC14916462890CBC982C3B3CEB8781EBB045FC3152258CDB5F6EC680BBF +7D0C1A40D817ADFD9F2C33F466F1B785673725F2D4A0DD3DD53504EFD89F1433 +6207D0630CC3B3B03E7EF02307ECE3FF85E0E09D1BE9F1557A5DB22B3AAD31CC +8DCC461A6A10C339F3AF621E6DAC +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMMI8 +%!PS-AdobeFont-1.0: CMMI8 003.002 +%%Title: CMMI8 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMMI8. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMMI8 known{/CMMI8 findfont dup/UniqueID known{dup +/UniqueID get 5087383 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMMI8 def +/FontBBox {-24 -250 1110 750 }readonly def +/PaintType 0 def +/FontInfo 10 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMMI8.) readonly def +/FullName (CMMI8) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +/ascent 750 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 46 /triangleright put +dup 47 /triangleleft put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3C05EF98F858322DCEA45E0874C5 +45D25FE192539D9CDA4BAA46D9C431465E6ABF4E4271F89EDED7F37BE4B31FB4 +7934F62D1F46E8671F6290D6FFF601D4937BF71C22D60FB800A15796421E3AA7 +72C500501D8B10C0093F6467C553250F7C27B2C3D893772614A846374A85BC4E +BEC0B0A89C4C161C3956ECE25274B962C854E535F418279FE26D8F83E38C5C89 +974E9A224B3CBEF90A9277AF10E0C7CAC8DC11C41DC18B814A7682E5F0248674 +11453BC81C443407AF41AF8A831A85A700CFC65E2181BCBFBA9B440A6DD72BF8 +97084C906B05FAD969086ED21AF0AA1471613182B26117D7494DD9F9270EF3ED +8DA4D957225F75D060237B6DAAD5A0AE3E702B3D1C437835B93B8AF1F9E7D966 +E739CF3AD5E256F90286A34069E5BB4122F94F18F3485658D0D25B938522A879 +8215A417CA2CBD20F71C5C5FCDE21EEA7BB27876D93BA667868A419287FE59BC +F538980597DBBA743DBBDBEBC61E3286DA7977833DC8BFC5E52FF5DF5EFD9A92 +D070EB769E31E760A50FDE012DC0057835E8B9B046FCC83F1A0C40326AFB4E3A +0CC3BFA35FCC64E32854F32EB7DF10A19F95830136BBB8139DE1663B7FD790CE +464EA431AC109FCA0E03F3E0D355FAE20AC8774D6B1CE233C27680C77DDA7356 +560A27C75993E8C980CD1E3B0683F7E8A05119B3AD567DAB4851B66E418687B7 +F9B21B3BEF607918D5973421B68E65DFD8B6C8DFDCF1CAFE2637D365148EBCE3 +FA4CC00052A2A522205EA3AE3461CEE02042E1A3F11467CB6C8C849B200CCE3D +0BC188EC7B934CBBC0AE2BF5DEA228181DBF0F774119F313516E7D97FF532621 +9278F856C166CA6547504F34991D588A0631A5CD06363F3FEE9FA0772C783447 +ECD0A200929CB58EBFB6B72008E4082B5D14AA560C24915B9463A92F38237886 +C35CBB2D4DD6D0CA8C1D4EC46093041C6181C2F6586EE3E7D4E647A107B6DB23 +DAD9AB5A0C2905455FE58075EFF6B48597078BFCCDD84812B98986F34987CE49 +7EFB19814F2A58B0233A59331F6F8EB66401F04EE7B1ECAD9BC90A2BCEBE213D +DDDB1F75C83609ED6A669A0CED58B2E269E76ECF73616D94F13CF827C9BF354A +E82202988DCFE856786B8AE569AFF3105B55C72C58D310FFC0E10B2ABAC8DB06 +40D5F72E54770E9DED1AF4616008595B8481E3D9AF4191CC9A5BFD9DDD01C9F1 +FE7165D21E488DB40879E863D470CB31CA06E5B5F1F8C3CCE04B697CEB0F3557 +ECAA358D2EC2B370519CE06138FA702314BA01F1F33881825EAE1230098BB3C9 +59666983275CA4E8D9DB34979F86535577E79393A72F84B0F768FE8C92692907 +15E9FE9894E98A0EBEA490CBC8C7E5A9F3E43B24C2C5A4BCD71DAAD3CC0B8B82 +AC13933543E295C163F61C9FD18371CB514493F90BF7FB460C029B8DD2E2BF05 +FD66B451DF277864DE1EE42100BF29E01A50258C2758F3EDE211BB3457B8243C +20BE72983FD6FA2581C5A953D94381E32E80D6D6095F2E93A5455C101BA71E8C +E560D4694E4C167EFA25FB1E9D214AEA745CE34CAA5468FAEF8F6BDB6C6BE8F4 +3D58836C26A2392E4C4DECE284A90DDB3858A16D6135FED655A600929DE71605 +6CA32F6851A2A6F71A9DF3D5D657593BB729CBCA2F4B059365B7263DC08AB211 +9C547096E6427F6AA53CB2EB87DF0AFE2ABCDBD15D7EF228D3396413B83C6B4A +79E41F9BA55A2688F62A10472675E5658F151F9FD6634EC94EC0682C17448024 +CC1633077C07A93E4DA8749D974FB8F4332B5DECF97D749C10DB60D4C90ACBFA +E65AE928C88BAE19234690EEABDB30BEDCEF2660D7464D5071058C30C572A2BC +7DEE5384BD7614A4BEC4C84E18CF7EC81C810256E8CE6520466C033E2A36D3D3 +5D6074B3857415011D8D9D49A474D994571CDBB89AF92BEA879BEBAF67663F5C +17ACAE809C2231EDD0A76641BA52FA7B19A2798D54A4A9B62C42F9905851229F +2CEE0191C8AA5AC12BB0CE9E5E3E862683AB57DBB4AAD6AC0FA8BA4F408D41E0 +755F72B82B7C18EC6B13995BF7AFD66AF4BA0EA7523DA8B75EE751744EBA9CA4 +4E8BC1FB37734503A5B24FB9F2C2D07A47CFC477F02413D55BD7DC180B0344E8 +50248801FA6BE26C97F397797F5F9DF762967E7CD92CCB8B2E587C92177619A4 +BF8046CBC72C6E69DC78B8CB6B7381A290080EF59F5B9F29C1167B261C932E9D +010D2D14BB425D157F22BC0305770AECC5BC80000F8CCFB9930255A68F299ED9 +D3B5B83A2CC00E3305EB281E1A7054734661B175C6CA0AF168790985F173DF03 +A8693B677BAFE23C3CF833FF6463B136FC370E4F0C29E322DBEF637F62C33CD9 +B0A8338FD67EC628E3BF2FCBF7CF0347D5CBA1DBE6DE878DD670176B85F69EF2 +3C5CCA1BD2B8A385F113EF1CE522F5A6AE053B9C1E39408C9459DE3E7FE2C4ED +77F026B0081BB80D40185458139C16333EA27F43EF1204BFBF80BC5301B2A3AD +B10F7EFBB4F5B7E04DA1167F68BB6D4049440B0F57385FF0A95E72760C6A12F8 +1335BB31CB74081FBAA319180DC00113CF50CC5A41D2E751E055DA1429CD75BB +0060C21CED634FDA106C49A12B356129D010E29F2919301AA7F80222AF3905ED +672FF85C9897A70241E8DDB9A53034B6BB44E140D9E739848E7A782F24B98AC8 +00DA09EBE4532787E5CF3ED815705F659D8E52DC2C2D4949374A3BF192BEEB99 +1D9A90A4F3250BF8A1FD40D91F5B34AF2CC561FD02FED712500B24330D87DA9E +4AA46B6E34BCB40B1F73A1DDE07E002B9478E9651D2BF85E67585B6ED812BE03 +A594874A235B1C1840C4BF4BA6D863583D8C12DB49EF7F8CC24DCBB6B21FBCA9 +378F3E2DC406291AB324571F76E0805DF8826090F0E8B50599CA58D124653D16 +16C782B01C91A6F3DA6346482E3163D28D95EA49866360147296838A3FD0CC53 +920F91D895F839CB61FFD2FBA296CA4C6304EEE579782AE5FD33D0FA652BA7E2 +CEC7C01DD0D3E0D56E6177EE5F609A396F7FC8EADABB465DBA7F384E215C4DCB +E64F807A50A461545107F9C3F7D7CC7D33E6EBD6D9228B1DCBFEF160703E6129 +0DCED8D45DD54E2A36E698A616E7906A50901E453BDB2A363EB77144E9EA6F2B +6BD927495EB0EBA5755165707CCFBF8759CE5856881117C7F3EF6D494EDDA7EF +E499BCA56C86467AC573DA9C2724FCC74BEB331E736FB093DCB67DAD42296655 +415D110F2729BD1D55E5C9CCE2E724116F45FB2E66AE0F790258851A5C808762 +68B8A110BD326F8D3EC45004E7CC08DA42F6CB80A6B6E7C286F139534A275BCD +2F812993DD9C9A1AEB5E7E4BDB4805DFF3A7030263AB060C9B74F0C25C5B9005 +965284884450CC2815DF28D5F9B0496DC7A3AA85E1E42741E1538797175C28D9 +FD904699C771FB066397FFDEE8E8DD1ABBDF67E6BFEF95BB700A7C1BA91354C5 +42EC3864F6E19B379E79A1CC3C786C0DA146C6B0B8E507ED58DBB1F12F613A98 +0E1F8967991427A22ED323901C4B83336CD343212131E8B59C2F5D232702ACC5 +7891BFD4EBA5D0FA35AEF9F3520CA82D121BF6885BBDAF15248A9E4649ADB94D +0735CC4D14E1D7275427D00C8E709579612F7F74DB6FC218C10C278CC63E2AE2 +37EC996B10C0229D687F0DB5E38A8C4DAFB3DD8A9E7ED37186FEFC97790A1EA6 +636A88FA9FB4D282234BAAD301A1F3AD33F252C5EEC49410562FC52809CEC466 +A0F6D148E9AF19D6DA2337C8283FBFF6005C37AAEB0B7F7217A8DC6F949B9984 +72DEF163E4D5ECE4288404448C96A7FF0AC76F732D50AD63A1D286C9180E80E7 +C218B1F48E3034FCABA6BF262CEECC284AC29E9F3CA1CFC1639A681ED66C1FBA +666F073D45C84A286E05FF809D4764FE819B6A330E73695CCF2F448B4D4EB4B3 +F63E94EC289807A2F9A1159CF328C002B467B19D6E9454CCE36FC19E0A214190 +B251818DD456EF658B0398E275514B72D9C1DA5F806EABCF1DD56BC025D69FC8 +A0C2FAAC1892B64D2AF79EA2F57F103CA623E440307600D50E783FAA998EBD40 +51D23A0CEFF8D8649B48B982DC38D613F882DCCAE5F51233A641B3CFD783F830 +D984F116DEA3ED8F0D3369AE629A006BAD4523F8E3C7C6B39A6C972508B67AE9 +32613F28CCFFC4BBC86CF31A0C25C786554F7A1F3DE97F5CFD1A941F775067A4 +784385E2D02EE1FF886701B1E87D966D3F500E15591A5012E645837FE2DBE3E6 +A3D375C6CA0ADBF96B33EC3FCFFFD888D7344B31D40427B8A8BED0FEC6FBE038 +1FB5F0714C4B5A0E607E215B5B7F76ACF0FEAA4C9790EB7E13C0E3933B7C63FE +5B934EA34F4B741C3667BF1735C685CECA63507E6FB9EB06AA010311F12AC1AB +4CE3FE8D1EA1EDB3C700BEBA516FC71D740B1CA1A60D4578003973CC3EE21DB1 +58FB1CF7E2EAEB2A4A6C742EBC3575EE6378531C6EFA6E6986E68B8E25CEEA67 +A59623FC1ED2ADDA9D72DBA627D179E47DC7F5551E07EA4D54ADB6CC8109D340 +7279F288E552EFD79C17DA3431E53EED66D16F24BF86468C2FE7EFF421560500 +12FB048D6CE2F370BE4E560F8B4AA12362ACFEBC839351C1D5100C625B14CFDC +747B66082D4AD5474A63EA0054E9C3E6295AF6B133348487B0471395857F4B73 +4BF8337DCE2FE2E1A4EAD7E7BEDC822BDDCE42B79B308C11897C98E3ADE253CD +09CEEEC0CB1DB66AB072E36E1E04911F40B535B0FD85982C21B8A587D65C38D2 +DBC5A07A0A26DFFF7460F10781069490AC1B611CF7312A14B4AA6005A4582C5D +336BCC30EB47749193BE8D457A43F54204B070DF5AC2057B6437E23705C7FE8F +7BB150560F7044BE3E48EFDDA539FEEFB0D2A7856CD4E405FCE0F5EB190D91AE +578E2EDEB9ECA218573BB1A8EF116043A27DD17A4047BCCC7C5F3C563A910778 +45ABCA32C7347E6180ACC86F9D665FF025DD8AF514FC3724B5C3510F3C37E0AC +5101D1667C6ED4E8F37F06CC2BDF66CB5A9FB7C52CAD26344FD1557571336A1E +1E340EBA149B4EB99016D1A411FB874914AAB2A415CE3F5FDFBBF5AFD7959B9F +CB127BDC68D2A2F3F07FF3D4FF32046C0371CD2E68A6471E46B08413FC3C7A80 +A107EEE57979DB387B2206D2810DB310B7232B2DAA385256C8A58964B512003F +A0C24ED21809E2576229627278118107B9C32345C1EE8C0CFB452CA362379369 +31320DEB5371037AFAD093B61E8AC7A6DCF7D49C7F8EC32DC0ECEAFD7E892810 +039570D2956289B15E078C2545911BF535F72F7DAC619BBDEEFA855BBAA81704 +18F7D351B0936357085A32157AD8E27438A58B2397D69264E748B0B8D01B33F4 +D04DC59326A7DED39E247A1C1A1AE49382BDBDE9478A1CB48F88BDF14A268B40 +A40B9FBFC4C87FD3DF1EB2464C3C14E36CA41E09EE0A9B75FEB0769F9ECEB1BA +EBF73B818427FACDBC33BB95B9654F31C59A766E931C698A8608F15290FCDBD3 +5C535D9036A19CB7B55BF54E96F9B2206DC71624E2E55FE632FDFDEC8757AEA3 +1D83D190ABED5E7A7AAE2F41FCEBC7C18626BF58F9E9F02FBAE0C8AA85E9DB21 +A3D8907522DCBAE4923C6A2A09FD2F08FE32215C544AB577B337D929E625E704 +E041C2381AFCFEA37F3133B6CA20093EFD457C772E428325E56C9CBCC447EF9A +05A8C3F28017DD4FFACC51B38E4896C5044266EAB4EB7C13FE855E790DCF8A17 +B61B1D30DD866BC57397EF6297C4891451FD6A5C6AD6D7446F58F56A68650908 +224D9F4C31C6906FD29BB51DC947465B808438E6260325752808963C808A4AAD +60422ADD62CAF315F6AE92FACEC55D5B682089AC0BC051CE1E2C06A3874736CF +0DB5F7C8F178479E4F11665402781D80397C75456F5CDF0A4F382A19EC6AD64F +71A9275264800E178F212269154DD8352167C57EBC0A38BE794AAD1601C8E541 +7E1AB8E969A76E1EB4092644958FEA2AD29635E70C4DFE2EB0D9B3E1644FAAD9 +B27AD5466EFAC724718962B62E7B8C32F412B69DFFEB792587D571FB5C591D95 +4CD441662CD1B07595E245FA537FA9EB5A20A97E5C9251EED22C9961B48B25ED +85BB7524F635F9CBA3714C6D60A6BF920C45A64F4C366C1F9D22F53084997C9A +EFE2D79FBE3347111F5093E271DB7E3770B35D253DAF93653F6A23FA145AD775 +AF11E188EA0428137D9A14542E3EDA6F7B2E5AA86C9F3D3649A85ED2F020C696 +01A339FE6D7E42BC548C8F92A4E3809C67A986C99418772403D16D0E8662595A +1F37563671D6DA0F36CAC99DAA8FEA215DF7D45E61314915A30A22FCA86A50D5 +2FF2EF08E240F9FAC030D92BDFBE40F1972DF413E6B452024CD11792BFDAA2D7 +C82716528AD4B3D637BB43E748336DCC86A952BE96F1EA423E31340FCACDC1EB +02EE932F58734AF3A5B2279361B63F1D824EE3BA9F4D2EC7B33A300A1CE8CA43 +24616444176DB8099D85AC68329B1F85E4B5B16F3B396FE2AE7774F3065D0203 +AA140DC128D6F935C44733EF585F89E8639A2096A225A2E5E49D447D8AF9FD44 +CF6C1BAD5C5E5262AECC5543EC8199B00B72BE32A0F110F64A1D0D5CCEF38FD1 +155D6198E9A343702F8ECF5052333272CAC2FE016681E12745CBE14E1065EFD5 +407DA3686080989F6F6D650A9F1EB3A813B070C30C0D3B4A1E9C206E2E4DFD51 +D8DCBE9AECF956640A2E79F1B4FD0EB8E0449AE1B8FFEBC43275743B4D7F6605 +0673B61EB3189E74F51F3780A91E6A5C6464C8CF7D563D9958D46F39B1A12087 +6BBD4898BA9ABA468AE1F24115891FD3CBC2195F75958E26DF8BF1B93F7B521A +C12112237AB23A8E5A7B7D0DC4C53692B35F3CD813EB463C0BD3A6486B0476C6 +3B36DA71FE512E5745D097FD4AF5D056E434DEE2AF926B2EE79F7FC4FEFD4130 +BB4B4BE01E5C720325A4884507CB51CBA4FFB615B78A4182444F0ECBE4161A58 +E86FE1DA2E39C2BECBCF1F1D7B9B776A26078FC252128FA8108CB83F673CFD37 +CCDA493234FB93E1550EF8D2DC049ED95B00A8A57834B024B277D3DF062E748C +B61F183F2D72AD075474F8165528CE75E4F40B38B0FAAE45751C1907F8D31619 +E88EAB02EEED415F3EE3BC5BECC6AF565D34E0BA2958FF337A2B06012DD1858E +C53DE52C108BD5AAB76C882198C72CDCC958D68EA8FD26F76F04EC1A08B2AC3F +A6D0E8724D2656555DBC0C8C42A3E22ACA7E1BC8E9F897D9AB692E0FB9EC32EC +59E31CCA4516A3C3BFD5411BAC3DEDCE374D48681CE7D67DEAB93F5B5C5290AC +FEB29C5EA2C98095692873D36C7DA24847B66F31E4CA4C7AE5C79D7CE4F0532B +78620582E3731A2A6533A03E7155B33E7CD142FE79F72721862EDB24959B9783 +F834CB616FFCB2A23497BA6D99AE34DC459A2F7B3E4DA2B54BED118ADCD92178 +66C40F4E60F6E1327D5DBCA645A2A7C770807E6D7E47E1265C753F8793BD2D1E +BDCD749CC24D4AF9315A93F01180A0F9A7F420DA1B87664DA5FD967131273271 +9DCC45C3D57EB9B8AF14771E8E751D88B98D2FFDC72F5011D402EC34FD010ACF +D3B0660304725191D64FEE106253FCB3470F1A16EB7B45C1489D3534BF94F740 +C2781DAFA5E8A9E7B25A85BD7935DF3ADDE08C960E283D8FC3976FDB4085DBB4 +B6B35FB239C28C785B18BE4FC98F3A5F410F562DB5FCA04E8074E4E790F4265E +F88117B3D0833AFAE6E8B8A71D7731BA6F14FD6F217EDA3F8CC687A494FC3914 +B84FDC37C8C335AB1E7E0BEC7FB6B7A595C50CF8F0080C8D461BCB8B579A5155 +F963B6587873FA31C3A6572740C63EFBE58A2EBB723B7517D2A243F6CB08A038 +54F4DF0F6692022B2EE8C6F6B73735ED3166BAC58D9216A06EA6FC7B63B20031 +D0F0F99D83D9030B413C2360DD2C553E34BD67851B743C3FDA676AD63C5BD759 +9131358C6BCDF05FCC048F4EBB9005899ACDD8E9EC9BB8C5A08E83485047D263 +0ED69B4D1869A38068FDA03524022A1D32FA2AE0BF728D2A654E52B6A6C90A3A +725F86627D7C3EC5AF5AC512976D35FE42AACA3FECB401788D0BFFD9F4743BB2 +EC5B4E7891F216DCA5A69E917A171E0069A03FB214ED307DE947225049D46E0C +4707503F09811A597A9113921AAC23AB1CAA9866F81A02BDF349FAB129F23E86 +E384C043053055938D42ACBF9F0EE86CEBEB011BD5BB7D5930759026189A89A9 +B624BC5B352DAD55CFCB8BE6CF6F215CCF778E33BBE1B009AF1164AEF12E6291 +751E80E442FCEDD8DD6752CBE0E64AAE1C9C5BEDBFF946CE8D7834A341ACDE6D +3744C8A0C37EED863F57BD52BB891022295C56FCBE4123D95A13DC2981E906AB +3A0E488D716B15D8C119353B99D6F71407DE884477D5C0185FBE439F9DB2A096 +6A4471E9486865DB9F8916785BAC2F6D68C48106F8C627E0A4798B1D8D03C824 +2BB8050C1434241EDB339EBC59D4CDADCD043B5D746959298F2C5D76D4A57059 +3843A6981E6747DA2CFB54F0E39A144C4F7B95B1E3F1F77E0BA2DAACF2BDFC77 +B10AE7CAD85CA900834C28D00F5D710A955F0802D7082362A4B709A9F2D0780B +3DD79EB0AAC51F2BBD695A035880D43735DC093BB8E1F201B48C7E62BAEBF384 +C6FF853421961C6FC140123CD3C6EFE6AFDBB990A4F57ACAE0D1AB11171033C0 +F3A18AD4E1A6D27E0F5825FDD93A97E9F01F899BC6AEEBC0 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMSY10 +%!PS-AdobeFont-1.0: CMSY10 003.002 +%%Title: CMSY10 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMSY10. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMSY10 known{/CMSY10 findfont dup/UniqueID known{dup +/UniqueID get 5096651 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMSY10 def +/FontBBox {-29 -960 1116 775 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMSY10.) readonly def +/FullName (CMSY10) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 0 /minus put +dup 15 /bullet put +dup 17 /equivalence put +dup 104 /angbracketleft put +dup 105 /angbracketright put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CD06DFE1BE899059C588357426D7A0 +7B684C079A47D271426064AD18CB9750D8A986D1D67C1B2AEEF8CE785CC19C81 +DE96489F740045C5E342F02DA1C9F9F3C167651E646F1A67CF379789E311EF91 +511D0F605B045B279357D6FC8537C233E7AEE6A4FDBE73E75A39EB206D20A6F6 +1021961B748D419EBEEB028B592124E174CA595C108E12725B9875544955CFFD +028B698EF742BC8C19F979E35B8E99CADDDDC89CC6C59733F2A24BC3AF36AD86 +1319147A4A219ECB92D0D9F6228B51A97C29547000FCC8A581BE543D73F1FED4 +3D08C53693138003C01E1D216B185179E1856E2A05AA6C66AABB68B7E4409021 +91AA9D8E4C5FBBDA55F1BB6BC679EABA06BE9795DB920A6343CE934B04D75DF2 +E0C30B8FD2E475FE0D66D4AA65821864C7DD6AC9939A04094EEA832EAD33DB7A +11EE8D595FB0E543D0E80D31D584B97879B3C7B4A85CC6358A41342D70AD0B97 +C14123421FE8A7D131FB0D03900B392FDA0ABAFC25E946D2251F150EC595E857 +D17AE424DB76B431366086F377B2A0EEFD3909E3FA35E51886FC318989C1EF20 +B6F5990F1D39C22127F0A47BC8461F3AFDF87D9BDA4B6C1D1CFD7513F1E3C3D3 +93BEF764AA832316343F9FE869A720E4AA87AE76FA87A833BBC5892DE05B867F +10FA225E233BCFA9BB51F46A6DF22ADCEACC01C3CD1F54C9AEFA25E92EFAC00D +7E2BA427C25483BA42A199F4D2E43DFCE79A7156F7417ACF78E41FCA91E6C9EF +B933450D851B73A6AB6AEA7EE4C710CB5C14270D1674FA334686653793FCB31B +491E870D3C2BC654D2C1DE463EC9BA29D7371AA1078800EF93D3F66263A2EBBB +F5723697BF7448BD0D2E301544BECF497FD475B85DFEF52AF4F8F8BE445CABE6 +019318806D10C5952157FF8F8286C1EE701545C8F60EFA854EAE66835A2046A6 +915D395F1E0366EFE0C0391583FE001FF16D82A2E2DA5F57754A2C6F69306E36 +356ECF8EFC3F1188AD6FCD2427E0580C97A5B69B4E0E09B85EEDE142F5ADD2F0 +5DE51D6DB72B127412A0D57106C19CA493048A4F815129ABE767D51715B1515D +9C21067CB5BC88741B7298C83EAE36A866DFA87D8981F179B1C31292F56BBB64 +3C430779468AAF07C8A8B4934E1E775FE3F35186BD1FA6EE3689C1C750678AF1 +FBF9B23195A124C5C991FE670AC0C86FD39D2B07B9A319E74EFD498B45820252 +720ECDF7294F7B0B137CEB86D33BFCEB8606985A3260FD669E461C8BE94216C5 +D434FD8854F44EE66E5A289A9F9E32BC36AF645D53F96652602BAED418C8D726 +BD04A1B4617551FE4DEF54083D414F7DCE004E6BB2DC9C2EF7CE232B254BA2C5 +7DCBD36C2072ED46FF711F121A701E2284BF1B718B3164382B8F453D68FA0377 +DFE106503B8401D4DB87F5402A3AC9A442FA060B0610A9524D530C7157C26B56 +AC970FCC1D5655FFFFA39246E6420CF97D08ADFB7B05822679BD40C638DDF0E7 +A97BFE8918B611A145AC965C203F1428812F9D340AF499B3A915B22BE798594E +0F520109FC81E452180AE45B170FF999C5FC2761C6CECD8742A5A6FC97F16743 +AD4EFCC6572A6D3F3E4E330C5CB2FF6FEA48A5B64DD3DBE943BD9918D4A18E18 +CBCF598AEFBB6AB3CD2CBC9BFD6099272F6543F3E532E0E21E614BD2880B1023 +0AC234CB705827BF016DB84E00E8C255FDEFA0101A842929540B7B4AA8A089BD +5EFF05B72356B6BC3727817823B5CDBB1B963103000D7F2A4E2A1472FC3E614B +5CBCB6D6D784023173DEFEBFA8F9ED87EC1A0A9EE98CA59CFC964CF943DC683F +E9E00DA718C4425A705A69D99988EC6F152525C790912C2E46A2381A569424AB +54DF4798BC2D7E7A361E7991641D4B756CE2A7FF4A2848927092C59C2C4B8809 +E13AB84FB6B111E680D7FB9F2FFC2C5C66B0B501E4447C2E46C10E2F6124476F +A140C404CFE2DC9E0199BF61E035CEB481D438139A9630934E541D261FFD2906 +4CAD99E20655FA746AFB81EDBB5601F5FD6B1D6832A01D585E2C55053F6A7378 +4DAACCAC7608DBDADAAE732D66B3E7F87E79756337C1A961E53A4651BE7C77F4 +038B89C87F650C54A2A90EB7F1D525BB353F33318551EE8D84A6A83C718EA5A4 +B2AC0F7306B1E095819B87015A90CA3ED739B09061782C28CDB36BA4BD5E5308 +5CBB70414E4112193DAC4A1FA30996327230D1E021F3CD8115E12D239D93FFDC +B645910EB29E40D830E7BAF2DB255FD7C4E776557BB38157917D993EAC245837 +A3B515147043574157B8342D829C7228CCEA843ABC89D1785A9672A5923FC4CD +2F3FF27E6FCACF84E2D3136CA2C0FD3EF1EE7354CD04C38B5FB874553646ED2D +CEDF7E362EADD04B18051F20A8FB0DE18E152385B9D05F98A3A7EF177824E246 +455ABE69E2F700EB78185CCFC07E3B4C6FA301112528D977367D30D0D5D59EDE +FAEB706DDC970A9E296236C725B2B55B09B9C336B8E23CBA5FB8692D56F33B03 +16294E5FC7FAA42E96395A57CE51CA8DDD77442F142E2E576B778373FB31C81C +16840BB422CA827E30A81829648BDF1CA36700EA32AD888D097C1FE0A05B2D9F +483AEE40269DF09AF0D1AD3DF80C45DDC59C2A03FBB661C79B87853737C6D352 +67626B657321B16198DBD6DB98A092F17878AE4698121E1006E53D6F9B0A3BE2 +3FB68828EF854A0CDBAA68B37ABCA6AD4A3D809AAF0BAB1697A81FE59C98C472 +1E33CD70A75A22C249DD11D76C2575ED3370A25892A16D2FD569CDA70C130770 +93F493C7D47D6F9A5424A7A542BAD726BFC3AB225DCEBBE6AC4BE006F8C7C0EA +051424B08305BF2D951AB2986AAFEA04E078CA79B399585BFF0F1ADCED02E15B +8765EB6BF6A8E4D0901EFF2C3AA104924EAD9637A35D877E0C51A3C37DA78CD4 +8643C8CE6DCDDE3F116A6C2390F948E5371BEB5AD2E87B41C5F01FB5C196C436 +6E256A88D082E3F46E4EFFBF605B2EFF1E9D9AD5EE4DDC323A137CD9451EDEE0 +06F7D82898D71FAF2362C0FCF1F726F97F820305B7CE20728CA08C63575083A7 +84BA28B7DE2B916432475510E274C12FFD1660A717F51DACFDF0A102D85224E0 +D6DB607BB72569ABB8A7BC6A10354CBBC01732EFE35B72062DF269CB25EA3DE6 +DC603B04C90C5912D2C38D7A5ACDCDD3F6F116D884F0D8C528F69D5D47BA20DB +0A9E585C7D8CC3C324FE8A1DF150279F7E8FB43BDB720E624E5E9918032C02CD +8020636AE5C38DA2484B7F4B34163E0D0A561B43B80E97746DC05C871AB620EC +C5D47101ECED4A7E25F291184BEF8B80024AA7BB456C1B83A907652B331DEA34 +754226C39C6889EBEEFDAD081E01EF8FE47751987667836FDE4C8BB8A3FD4406 +1E643B4EA37BD370734D1A2DB17C2F4B74B4ED75098B433601F75A88C9A37A05 +CCB157EF6E32023BFA33973F3E655A4D58289136996FCFA61EEABD70791B6523 +1FF5DE71AB8A17038923118A5EED8D59C4C58D246FFA9BB26472346B40C8741F +153D19CAFF20DD2A86C6DB89154A630FB1761929FC3F0448EE2F089C1C953E02 +905BA8DE75D101A982A611056C4B237596C10951DD98BAB838B742D3CF7DE718 +617DB72E5268583223E37E029D1C8FD3F1D21690151F76B76C52C725CA135CA2 +8666553E863CE188BFC9B99AF56AC2DB5BFEBEB12FB563D00244EB89E478657A +98AF2E1223C1ABC25A4500E8119B86EB3C26B8A2F3505A3E5610F89B7C34E278 +53FA0A54A7F46D84A35EFEC36AE660A9E3C37EE3864106702DE5AF6C45ABF64B +888A4A51323138CE77DB935576FE6B4824B6942DF80625098CE1B5B32B234F1D +052A9D6039697118A9D793793775D8729D8574A2E74D7109C7B7E23BC5E2E87A +CA8E019203952A4892544E1AD3D4EDD22971611358AB230E9A2ABDF00A288501 +A01B67C42B33F6B78C39562DB50F4663B922D9BE0D8A150311AE44B83C1F129F +07337323E9A23211EE58E16043E127C6F9574019179F5635648A011266677B56 +B5D0201A4E1470B952A1579B57AB2329CD4C615395023C653F784D36B5EE3672 +10D191F29EA508CE84763CA4CE7C2C5229E38E241255A5CABCD6C7CBAED901A2 +CA53B5E24111921CDDF83578D33D463D70EDACA0E470D8F592303FB6BFD68B4D +3F3BE2D7C5EC8BBF10C90111A33E205F2649B56E8443F6FAA6C721C66575AE12 +D4C40F1F46CF9E9DA675AB5D5840D938780CD9E4AD6736ECBEB6A4397613586F +849B51048AC5F9405E03E14540A5E5582F61CDCDB57EDDF95A8C6705F433EE16 +648F098C03DED8A2AD94AE3DE202D629B9422ABB031318D48F2C85F9DBFA17BE +84708AA3B6C9F81F4508F7A5CB7B6646AB8722ECF817877B77D473F577556DAA +2BA0ABACFCF5DEA7498C47328E873019A956FBB250FD9D8885D21D368FA70CBD +2709D2DA44EE7A9869963EAB48789541906DE49FAE785ECE1F18A22C7E7ED204 +9768896B78E9EB7A2BD6EEC1B26083940656ECD689D92942CC8AF05CBF82AED0 +B45A7DF4DD7AA6526FB597322560B9ED3087A65B5EEF1371C328A021411BFE3B +D9B5088B2F1AAE381FFED52D2D1E02CD0DA78683E3B06171CBE94BE9760005D7 +135893D7CC2DB097F6AC664D9594CF1C650F84DA80D2EDE04802DBA33CE3DAFE +EB7A37E8AEFA4FDA6252FF21E8673DD98E67124D5DBC7BACF361E57077B71939 +C1D1FB923E4E35C075CD1BCBE0E80DAEA1320D55B43EAB45D9B26C366B278782 +7519FDC482D98839BF0DF2E7C3A56A1C1A3FC0E57A75CA414F6536C1FE8EB7A0 +4ADFEE3BEDA0F53BE8CF5F64230784A797133E8CD46BCCB3BF38BCE38A73CCE2 +9E073ADE792F7128231DDD1F63E6156ADB2609C200837C2E8A2D93D2A7BC9171 +050C709A71E44E32B1B03C92EB5CF1D3BAB1C38E027DC4ED9AED633D98CD7486 +3F773ACF8AE332631CF2ABE6D606607593FE862ADE31803964E3F4DC3CE3A271 +C76BDD95C87CDB3B87BC26FC7A16D567EEC62E6FF0D471B4853DB8A94D4CACF8 +843824F818083F10E88D52FC4253E8203292CB40F1414AE7E51DD7347007C342 +CD70E8E9F2D2A13D71213B841DDEAAB208AD9EA644591C15DEB084165F9DF24B +B91D3BBEEC2E34E38EF16A0C3F00700A7BDCBBFED2EC0D09601AD6538288DB50 +3478B051B5E16B604A0341FE621A58718D960D699D3FAD284310DCF54EB13175 +19A75A539EE98E804AEA24689D3540F0F12951A3C01FACCE9A7BAF4D0DAFA946 +FF65A4D2A4C39969607272C6886F44E90ABE27CA3A1F12A29D9B32E60E8E34F0 +17C5FE43D0E69A99A922D98909B2BBCD145E59A5E7F5426B3988F73B09A525F6 +8BD4915663C1301323180E760BE81CB874B020FDA3AE63340E4261E4F3E4949B +CC0966BDC4426190BE9F5D77F76A72AD925662E5FE1CEF9CCAB68F0BD33DA003 +F11EB91AC4502FBD6AE48DA0F9D07C35B96B103E379B8A83A05FE728F1716194 +1F650F75BEBADB2E3810388F3E2DC7B19F1BA9E32925F2FD9F19F4E8701F3E4E +4069125D7C401144740691E7A460021A47B1E27997FC1DDABEC5BD0EE0B20194 +2D579C7D6727AA124083242BDA46D8E116E2751C5F298851A62B60AEBE82A929 +9B9F2492BA35690D1EFD16215B8EF14E7A3803B93C28FA41D971B05B6AF3B593 +E74AD1E68A5FCE12A86E63B78BFEA87D3949FD164F12277A4688BE96356791CB +8671C49365608F3EDECC109321AF92B4C29CAF073DA3A7D73E913D0D83FAC5EB +BD884D4C686056404DAAAD6F82F94F803FA1FB0DD8908D1DF08FB87A8BB83027 +04DE0CBB1C6FEB6B517FBD7CF065120079E608CE41893C2BC96A347826CCDFD5 +C69E161217F2127A59F1A6F22037641613F191F22D5B4CDCBCC2EE5615623404 +ABA7BE6C5FE475481615B2AC1A2412E54688DD21E44CC9AF5F16E634AFCA389C +4D740B7B51BB141BFAD1080E7C726C1606A28ED492E6BDE9F800EFACD1513909 +84E98CEB6A0B7A2A6F3E1D1DCC3B2552795E0932673E59ECC56DDD37A1D52BA6 +C3F0E905978AB568941A163F4CE3AAB5C5B16F86016EC47BA6F3F7AAAA77C3B6 +09C8C3ABDB6D514A76ECD37C37AA88B5860630B3406B494F7725975596F84777 +D9CF48686EC9C5DBCC1D78513F591C7C10AB9D153B3D41426B7BF668B0D04503 +56BCB686258462C1DC61095724B9F3312316262FD7C1AEC6E54DE7E5A7BD8EFF +035299B8FD8A4A7B0F51404F4A760F4D8B4C0FB7A32FA4B2383AB6E9C78FDEDB +FE6A5788D38A6701B123630C2A6D820A684166FBBC83DB17069494FBD411B333 +CB37E2491C5BD035A33867A6D3A3D420CC31ACF43AA07182CAAE67E40EC63663 +B678F71D4C6E0EC3A0AAF904CD3AA66E0DE5E3CDE049E94249B39A1C06E3CE9A +F974B2484BB2CDA14282B9511E505B3C89F9C802218AE40D1A7541335C5736DD +CD565D4B9F4CC78F3A393737EDB4FBD0DA299E21CCFEBA5478EEF013F0552A8B +0BB11FF46CCDB784E8BDCF730A16363E66572049E42C695886EAB42A9AD9094C +B635DF4B5B9BD9B9AE8455DFA3EEFC77653190F9A8B1E93B7281C2A21EA7DDA9 +33484745BDF7E3DD63C7AC66C286C9A5A698A5E4D7A91710B7FF943FB23609B6 +4B442F83CB795788FAB5E9CF3F75D5487DA26170E4561C7941C910B088C3B86D +F844B0F340CF82786A3FCF347048463EBD2006281A816627065DDA6CD4D3AC5E +2024BC96C7D896381BBB567951E7A1F29D4E95351298B000D29E5F3D0448CB5A +CFDAE1BADE9403B90371C3A07D208948AFA022A69C519434B6813086ADF518D5 +88E0B92072A44BA1B3EBB630A13B7AB90992E85B6D67361C8D96F3E0D826FF37 +17B67E4B1EB7BADFD98D7F4FD17BECE740ADF13C141EBF0A91CB105DABB32FE0 +55086D56A0D358841D15FD349E6B95512E4EDF4C430216FF85C2ABE995E4B40A +A6044CC8820AD885C07E052B3F91C2E9A1D163BFFD210F7BE95B923E2500DB50 +2075106DB541C267BD450B25B670CE80BCD068D4DBFF2D82634175B61FBD3BC3 +406131F44C7D6F18D375D1F2270829DDF29DC14DBB58A30AC193245D18DE91F8 +AB88AB548D8138605BB5A50073295534E314366E26665AE70482B890E4101D6B +60E4F3B37ABCA1346DAAE8FDB8DD9C832EFF3E73BA470E2BACE7B8515CB43388 +C27AF99FF9322175CF8D4947E6B3846AFF5163E972156847F58A66660EC8A3A6 +5FB47C9F637B4CBB4C73B6A080B0CF6FD1E9665E92032540570FFCC747C67C50 +822811AADC404BC7ECD1673E8AA6C3A2F1D82F39430B58C29145E2F1B679C46E +94EDC711883F1E4EA84117A54757E8895A40401A26E1437B39A2F65CAADD6E02 +D71FA8AF7453668DC613F326A3344F74AD7AC67569AF399385500ABDA5EDD3BA +343CC5EDD4B558467626850E752B9959FEF1454E53E7A3DCBC2255AD8F6AB4FE +894455118A61C58840CB68A925ACCAD75CEACE863D806916228F0614191A1CD5 +DC9BAE256018615AA3725834519449B0A88B4F396654E74099C007930ADB1327 +DD119BF799FE3B0B223E1EDA04FE2DA7A1C879143E1C33B6C6344F4BA033AD6F +8E88C33DEF1977796B454BAB2494C930F492A518E8198C708A75FFEF8C49C324 +A718AB59B889DED521229E741FFE53F98EBE88B0405AD523254FD3FA4BBE96DA +DA1C27C1C979A0DD4E61C3B1F4C4DE01E42F1C4435EECFC02D97994BC8AF5270 +E7CB1458D76ED0229C5FFB4A23B8716018F9050970895D51722CDE8F2EA3D947 +DFF374D84915D5C5D16463A6FFCD079D1ED416C4347BF831FF0C4ADFB61295DC +4D5785BB0852BF472CFC97EC174491CAF961AB90629F055E75DAA6D9898E8653 +5BCF379816CAE46FEA62E7BE8E9B953466E51828172C4DBD0E1BBAD1CE28B5B1 +02B3E36403BE80B49A47446A6677FCED438F01D60EB10F478C89528FA337D0D8 +88D3FC123C076507ACDAF783A9A6E24ED73BF24B6E0F11C13E532DE5F70B15A0 +657F5ED27D204449A841ED19E01432CFFE928E921321113780D036D34F2797DE +D4459CFD15BB117B5C9745EF3CD2B296D91FAD48C80B136D94476967E255F808 +AD2B5D522ADEC64176833756510391815A1D4A8DA1D0AEE7CAD36A1D161889F2 +3347D5B6BC503300FDDD48F594F391D5FB42C42113C538E707C16EE24A3F375E +7C506E8F49CE50FF9DEF3B4A4C1BEB3848EAA3477349833BA22D2A9012287D8B +A8C4CB4307A1188ACC0E6E9338E1559BE5FAFF381BD82A6C71C267409468B3C0 +2C1A29F4281D565836EAE57F680490FEA4A952FF64C8CD11C377C294DCD1EC25 +CEFB2B6DCE959D0208F85B6E32E9B44FD455F9B134A5306D95EA29F37BB8B86D +9E592159338E1293F449380E13C21AE42E6D6952083BFD432F72DFB7B6F9257F +5784C683A6E9ACD72334E0EA8060A81E14EE32300055040E24B49810DFA1468D +A962DE1D1AEE09B49109257898F155A63A83D514996DCD2F96BC0F52796267DD +DA6229F5E9024F78B02154C27EFDB9B6E09B131C9E9E4DB41A0FAEDD93A17E66 +11A2D58C918F665AB77ECF4D059CA0E6273FC77765C3E943F31A237BDEF1A735 +FFD31EAB88550DE10F9A77034ACB155D085947312A063D6DA7080C457601646F +7B4FF501B0B33B163A70EFC77CCF6D2EF060F21F46CC928AD877FD2F3937F1AC +057CF070D3B4F9A86EEB6B24B8A287AB9C097D47695869C8EB52B84D815AA6AB +B06AD85F81B6747D34D833BBF91E669FAC3BA3DE04F09554A6188672888BB9B3 +3C1895ADFD24456F4FF3CD018AA87B2DC35DAA7589C72261E6D0035832655F73 +71B09F8D98AA894DFAE3D6BB5D0B6C03C4F25B4EAA40BBA61CC1C35729895503 +56EDA3F81E74AC0032BB5B000673620827777926AAC56BB52E01D31B9C47B5A4 +C6A04A785B4AAFECC11A6E3D4CA140F51B032BA39F3B9CD77EF7AF250B732B16 +2598F78AA005D8938E7B04FAAD2DDCEE87E6E4F578174BECB4A94D1BE792D097 +2034095A4ADDD24CC24C1379307DDA53F0C34F45FBC99E32EC90994B249611BE +AEE1806D80BD022A8C491EF89EA5A98559DE4FBF8CDF929F2F1A8DC2FFEE2485 +5C14CE2737C81C907D873D79EB8EE5DFFBD72BD797C67436E9DAA85E7B5F8802 +3EA116F59E5FC8100279917BABDD466C6D1CA83AF9493CDCF2AED9AE83E02043 +F0E73245BD8640ED6A7660C9B541DC48F7AB182DBA353832521D2D5DF2AEB915 +044ABF160FDD0723D713D1563EE0FE854FCF2F8F5C3768C0F2544D718C7BF104 +E7DF8D12E9AA2D700D660460D3532E23E41FBC47ADE174D2048E2E5D4B82BF80 +3ED36A14D6ACB1C04F822ABFAEB37CF47299562411DBBACF +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMTI9 +%!PS-AdobeFont-1.0: CMTI9 003.002 +%%Title: CMTI9 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMTI9. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMTI9 known{/CMTI9 findfont dup/UniqueID known{dup +/UniqueID get 5000827 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMTI9 def +/FontBBox {-35 -250 1148 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMTI9.) readonly def +/FullName (CMTI9) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 12 /fi put +dup 46 /period put +dup 58 /colon put +dup 73 /I put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 100 /d put +dup 101 /e put +dup 102 /f put +dup 103 /g put +dup 105 /i put +dup 108 /l put +dup 109 /m put +dup 110 /n put +dup 111 /o put +dup 112 /p put +dup 113 /q put +dup 114 /r put +dup 115 /s put +dup 116 /t put +dup 117 /u put +dup 119 /w put +dup 121 /y put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE32340DC6F28AF40857E4451976E7 +5182433CF9F333A38BD841C0D4E68BF9E012EB32A8FFB76B5816306B5EDF7C99 +8B3A16D9B4BC056662E32C7CD0123DFAEB734C7532E64BBFBF5A60336E646716 +EFB852C877F440D329172C71F1E5D59CE9473C26B8AEF7AD68EF0727B6EC2E0C +02CE8D8B07183838330C0284BD419CBDAE42B141D3D4BE492473F240CEED931D +46E9F999C5CB3235E2C6DAAA2C0169E1991BEAEA0D704BF49CEA3E98E8C2361A +4B60D020D325E4C2450F3BCF59223103D20DB6943DE1B57C50F9043B95BDF77C +1450E422362CCD41FB4AF30F73B66A85AC8DDEAFA65B4FCF0033D034E111830C +ACB981C2AC41AC32863D02E6D9CA81F8D407DE4AB20B4CB6418B8067D694DA02 +6E76FF50A7197281B7E8E700C4A8824220374DE1CA077D50A8B751391E35FAAE +E0AB0604164A16D476EDD5E33B08639DCA14796F27A20EAA52D0BD8B61065AAD +083E4AAF626C2575C56F87AAC636FDC2E5ABF623F2D916D3CF5CE8703D297151 +0A664F40B144A2F3900D053B709210AB8678EF35301AEF3696CF17A5B41B588F +539243F32FD78B62A0D80B17F1028CBD7234BA7513A4809B584092B95583CF1B +664929C33846E8F59A43220F79D8BE0DA051D17D09C1E9DBA6C60BCF99BAE773 +3E9439791901546793B3CDF014E6AD9AA169483DB87A6CE4104A8B25AC117504 +8D6DF6052EF491D510F67D730CA315915E77290A40D3F8EB8F004114BB64C5EE +9F1E18A47A785A04D4EE3FCEA794EB3438CBCAE35242782053277EFD6B67F6AF +97D892DA958EDBFAFDDEBEAC4451EF8E00D6D0E7E60C112F74999096428AFC5F +507D0DEAC778A81F8F47FBB1FD8C22FD22DF2AD0C9BDEFF2633F180E0186A9AB +B0680E661CF083B8DC5635D7DBD69A17E6BF1E86F7C19FF7E48EAE8D35F0B698 +F990DC16CAE9D6E9D1C323FDD183096BE0EF3BCE379CE5E5CB0F58E4516C01B1 +55004C20A6F2BF430D59368AD4F6D398B9A27B89DBCF70BB8A36AA24496CA621 +0D28748646257C95B113209FA00B4525C582A8B223C64B658DA11CA08CB64F12 +8BF230A57B4DC2FD0AA8C486D7B151C9713B12ABDF174ABDF52EC8034B0858CC +6D389B503A320AE42AF3BBE33135032FE83E6B7AE0D713BA6A56230A50C22FB8 +597A5F21CDF98D70423E8AF8E3CBCE4756DEB5695D3A5E7C47E6EE62F4D06BC6 +C9978DEFFF773BA6CB9B92D2B2638FA3A1D92806EAF9A9A9D6DE09D81FBD8622 +AA7AE1FE01A1390427D270287D4A436A544D4F568640100F94518C113CCAA34B +28791F62F1C5B6207165A847AE2A9F1E3AF8B96203DDAEC2AF8F3450CA2FFB5D +E55DA538F39D6A5F13963857E2CE6D75E510E8287D4DDFE5FF5CDCC0FF51E346 +B8717B28181DC56315751FC861DE7CAFECE8E350CECE6B324879ABC8D8206ED7 +51D1D299BACD2F7F6EC2F8D3836EED6CCF09C205A7A76B82FE1AAFE0833C7693 +FC7E22D373C10C34BC0B57382312646763F593AA91D90F6CBDB096CAF9F52C12 +CB6E75237566C5D528DB58B92D0C7E16D6BB2D097C1FE565A07076209AC4DEE2 +BB6147D06D65F403B6919D65D1C3D9D0FFFC0CEA7FE0EE002AA0EFBFC120A724 +CCE85B3419C44ED7216E4C714FC85F72CA933D3707D2EDC6AC02335D4770B48E +0B64716C0E1B9DC8A4DA1BCF7B47FE7B37F9630E55E9868AD41AC770C5036E78 +4D8B24AF41E90CE8358F97CCC43295371B73EF7E86BD1647EAB2E493EE912F74 +EBDBB5CAC34EC1044114FDFEEE2FA8E3574F4D1E3EFB38C3BCDC341A0E53044C +0A19D619CB789E8C10100E413B8057373E6D77709B8019841DF04C8B27700FA8 +B21E2955C9ADD9CBCECB52727BFF0E48029D46C695028CB3A7EA779EA69C8763 +2BA38F274B4217F4D5C942042ABC9E4C1627D63C0BA9B1E03E9E3C7C8CB2812F +503564C0C02A6F2041D4CD59BFD6E465CD060D655C38FC5499F06E0323A4F359 +37061EA5248B3AD809D16446E67B0267A3A929FBEB0A93DFEFEA3E8D5B7C564B +95AA458F532CBA8770DD90AE6D3A0BED3BA140A4AB6261CE41967038861D947F +20BCD353B32618537EACA0F1CA5C2708D67D46B6F9B6D4F12081485AF0699628 +05AE00F008AA816DB07710E06AE2DBFA830C34851E5C63A7E9E02404ADF1DCA7 +2185507F0CEAF858389E4F8DC3A1283FBEC373FB4A1720BA71F993D432425C2E +D0FB5A6FBC1E1904B959838FCCD085AF1778A5E3EF29166C6660B797C8ADAFF7 +1EEDE9BD371E11CEE77D70008706E0BF2B6DA250F9C6A92BF91ADB7D32CA17D9 +776CE695179FC5D1686E86E2B4C3E34D2741CDA86C0F343E7CDD52AD3059A8C8 +134C7DAE20076E5EE5990CC56EF8AF66131243212C40480BBFDEA97FE0558ED5 +7AECD669BA1AD9A50F1C0B21D75AD5A11FF9F577153C12F9789EDDF81FB683B1 +F66244F6FB2DC1FF2B3BC8CDC6990E6C4CB835E552E2AC9A7540DDCFAD1F931C +44815C59118C7EB3C4CED4DE0D45C311D69576D5E6DAE9DF8265EC6E566DDBA7 +22655E765C074082F346AB496F08F4E598D6D35472629273128157FA8608DD96 +0CC4A3B97B430B1DF8EA4851CD54E742AC0170215F86D4612129F2CC3874D047 +F485BE77D2FF57B8899E1E667D41E519948F0E60C5247F5483D20CB108BDDDA4 +27F15C14A35FDC3CEA924E051D711D8D4EE36E7735E6DB443DBBD000A4CE0635 +2727EF9C1AFC95E876A46FE001EFF903D64A2C1F00D734D32FF4A6C8E3956DF5 +5E676815AA165DAA9E0AF2D56EFFF32BEC9897F45E40909F2F39BAF85A354420 +5E6CBBC6626B98FF1A9BCC1F76D9AB0348C8E7540E42EEF3F3351006CDBFEB01 +0FED065EBE147B5E36BB659D6AA429E6BF8121F147554BC0959E6F45C6990AE4 +66A74CEBDEDD67A559C3E4937F50D3B51E7DACCA53A2DB98306E7AA86D21977A +A4CB7592590C94C8479530239945471408FA1F774CAD14F58AFFB59D8CE596B0 +8DDC2E8CEAC94156D5FC69D70F2F779774503F168C48594C63F3180E8C93F423 +7F1F1D44CCC5E5F766D7BECC470F880611C545FB3F4C0E171A30516DBD6477C6 +2C478B4E3C2AC2241C68124C4570D476B1A18E7D093C97D16999E06CC1C8C485 +15AFA4836718A40733ED145DFA4B80D3FC3AA074665316786E65706DA958BB71 +67A4EE35FA629FE738CEC51D4F51431BCF8B14804F2C83453566253847077644 +257AFB4D3D13CE77C9CD592F6C913F5AA3064E88779966EDCA52B2D9469FE48A +C8F6E5036AC9B0671508DA664E3F24BCE551D22434DBDF58196254D7D2C7C308 +1A6822F2F21BDAE029F7F776C1FA7BA9CD94CA7D919B4E7BCB9B885FD767CD9E +96CC1604B95F0A8715837D7C27BBA08663FBC0DA33C2962577451DB48A4B76F4 +8A4B66E7D2A2E465789D1E6B6D5F20413D84A47E5788CFA97BE43ED05FA176B1 +F535C65F8FE60C72BE326737154602688AB71A8F12622648031414AB80C46289 +1EAE582B933C87122DBDF3E3F1C4F4F5175E88AD0EC90B33CFC13DA07CBE8C49 +EB1E86E0DCA68AF0BE3814595A4B2A4113FC8654A032BF00DAE293B4F2CCECC6 +C0E804B658720C67DC156B4785D9A15274F2BF38748FAC7936275CDF42915CBD +FB21A739B80CE81B3897539492F113D589DA8A6C7B38621C8354625DC8B40FC0 +7CFF21FD2D09E41AC9E99CB18FB9C92BB735C16FDEE7A8BB75A2258E76195AD2 +1A88B0D64C130BE88CB2FEE3C723A46516DCE8DC6692EE27C6D8469FF511B7EA +F1B7EBA054708112981C79CFDCDD40BC55A1778FB3FDFC0AAA7EDC425FC2938D +694802705011B724423608C7BC1E803D344C9B5BDE13005D06C3F27DE0371EA4 +0F836D5C0D6C0BE2D27700B08ABD09783D329EDD00415387C97C04F89607DCAB +62463E6A37E36C3A1CD92158353D45B443428177D1AE501F1CC0B50C100166B2 +98C4824CAD58B3A396D097E2850B39130E743CF3CF59259A174586B9361D8F23 +6DFDDAAC5B40CBA0495A3971399A95BAAC66CE5B472669E2C31ED0F2BE60E787 +503EB379D332055EBF839B25802E076B1BA8DE3EC59782335AC80D1CB950948A +5EEA00C5F5829EB5293E05B982032207D41DDA035B3CAF26802A61751B17D0E5 +170C99F6A0EE0EABB1576814EC3B4317A89C7F11022A0695FA3C2B2B80A2F73F +6A3FBD3A2B8C4820E7705CCEE368CE580434F3EBD2E9DBC162CB482278531546 +74355C299E321C47D1C4C6D48C5100A4B8B2A16DD422AC3365B37A79D936FB24 +786E9AD00E8E2666438B096BEF243BEC5C1AE35A9612D08CF7776E458265FC58 +0D67659A4D5AB175ADB10F662536C8A9447DCF5173E620320A43B2AA5D5B5C34 +78265F88656D2125C6E694AE4750193B05ED2A6DB4CD5A81E48B9912E25710DF +C66A0FCB7920479DBBF739D4BC746BB03C2F1FF89288DD807E805957EFB929CB +248F64A431C995F0300C0F836EBE1678C28DEA1F45034A4057520D17A25180A8 +693BB0FE3FE6308A332A7E54A674B497471E2D13756CB0B991C4A4F9F4851A9C +49DDFB13416722223559BF7BBE8F1499BC93DC8BAC757E42E25369B182811ABC +FE4F1CBA5E5BC15ACB6344EBC575C2F443A508A46ADDEBF7C34D88702FE46A8E +D96C91AEA3EB989909A2B22E86FA80721E68A1385239B4883DC065A0C08ECCB3 +693B0D13CA411A78C13BEE866D8AF8A634B81283B77FF62EEF25C5D48CCB10DB +BFA10E3066C116662395A6AF0B71D23C4047EE0352F4E02CCF28F959FD4394EF +43F0761B73B8D726B69D5111CF94B720D461E0AE42C974F2EDA6F7883BCC35FD +093304225534D8043D934772B0FEEE6427D0481A6CD8BD614669B875E1DCC6E8 +EC423D9A0DA290D8571E97CB6CAEF274DF823767AF4E0576588CF2ADC239FDC6 +437910EC0C2F01B4DC773E73C7FDE6E37D3E784E0E5B655600FBF3FBCC1FDCB4 +CF356017BE2A663DD7983A46482A4C1B42A067381D58AB1B332CAB8E5C83FC78 +1F0D9DBACEAAD372991BE3074D9911CBAA95E776008644F203D6B2EEAAFCDC57 +C53DE48816934BF9D70F90F4186F6268522D260512492305533C1B0FB23D4726 +6ACE20B872517CA01EF5EE275CCDC3BFE45C19FF225550E4E73B15E9BEE3CF21 +1594CC1773CDAF0C659F3EDC2EA43C25D00AA68DA8D39DC46A1676099496DFD7 +1155BCBC439742A80EC917BDB6E7593639CF1217C0265BA3183209DDDAF50E71 +FA061C8978583D93EBD97C70A890230AA7123947B197E9536F0DA91373DECFD9 +BD4E1C12B086B12077EBA19BA50B786AAE9244C60020F406AFF705CF029B43E8 +D70CDD5DC7A61A75858E03657264392D9CAEA8C515DD98C2CB9E84BDBC6A62D2 +36CA59D774BD889D64E6C9A0E043D04744D3D807D3A1B1B25266F0D9953266DC +662424A93FFEF40F47097F48D1904A1CC00030155AA3D9B07D281E055E6D033D +745856D9465D7349797A07396AE9026EE96DB8BAAA081D64EE4D86ADD8C4D1C1 +CE8EDA25CF3242047E18E4C9870320052ADF3EA543D32E6EDC93264DCC2E5A1F +458369CC5CD73DDE165D5BF594C81FC992F35DF29A57464A0C6D72D5E7A92535 +3CA14943640A67B23935409206FAAA6A11F598BE24373E7BF54A2B3C34551080 +1B4DB4E3135AD0B61CB1C12A5154013E11B33393A1C16D74B16BB4D8E7B893FA +E6AA0C843BA5DC50FBE07407D8B16ED2963E36D021EB433609053BD19ACA31AC +53B1360A48F71D972E669D5357C22A33A7211F3380DF41069AFC7ABCB3300D72 +FA1E9D6F2291E147988003554D2DCE165B5F316A390500D0273E83453381B13A +95D6E6F3300F36155573B8FB32A1691F6A9AAFC39541BAE0A4153C9F6B181B89 +43D246EFEB5A2B5F66FC60A44E2E1A6D972C53B1E930CAA18A5B58820D8D2EF6 +C1E798E9B744A31FB980C77D36728AEE6E682EA668F32E0C3EC70BCADC32A8D9 +A2597F1A4AB9565FEE420056E8A8B0004C006AD981E94A84CB30ED6FC9642D65 +0FADC7C9950BD962E0D86543EED6DE476E29A525183E52A8C97769E2FBC4149C +53A13C54341A9F554F0178211FA1E4731BFB84C11E6B776335F1F42864C70E9F +4C3F71F7DED7FB704A05FAEF7A2186B2C9C2AE9A3BEA55EC7F3334699BCD3D70 +A675D3D1EBD9958BFE62267CAC06FA97421994C17D60879D8B847B39D5379B2C +079AD7BDB6FDDC56106127147297932F788184B0BBBEA5657DC92E4D6B097F30 +97149A14677113EC8929C69FC93CCE56C068C015F46029110C7DB91DFE31E06C +CBB475E860D068ED6BE3B15B5C6F6F0D1D5FE6E7E4E47392C46FAA9CE932F928 +FF628944653962EBFE4EE5AF7522BACB33ED77387B0247C146B81F7A860B84D0 +D372252C89556DABA34913010DBA3576A6E13E3A184CD18D53FC758DDA762338 +AF195F1F261D5F10AE0E733ECB98BA2F81F7B456A75D862C69D623CBDC50BE99 +E3D1ED35ED4AEBD6CE39723F459AD508863C020FEE61980A17D41BBE0A54E120 +1EB69BE8C1198663596A3678F11BB8F28D7BF5F713AAF61866B6C172EAD9D25F +02289C719F2C121A09DE6D8E47001FA41002DE821E739567F5D45F5C65D2A0D2 +65D011472F8AA705C7BC3E0580BF4E2E007EA98AEAF9AFD2F25FB20B08EE26E2 +E8F94A39F6240409F44E85FFAC9A46A2EA23A5A5F8BB0DBE50A7A1A9A6034D32 +B777C38B88F25D392E59AFEC304C13CB1407C70B7DEC884B5B7AC97440CC198A +A949ABBCBABD1442C67E1828CE582D77F616A49BA12BD006CD541F707ED05737 +FEDED2D912D3C1083AA726C21C396EC6A36EB6B9219F2BE7EF62D09E487B3FD0 +9748121C531F7CD54DD98622C4C945E5B04ED2FD66D20873BB42C5391627B074 +7DF90B81ECC6AA0743F25247978CC09C28C4F878E29A5070E4023BCE95FE0ACC +CC01D0EE219FA8344E8F6D7D43475678DEC28FCA370A417D2192DD293B13E507 +F20AEEC2B825BD73A6678698858CC4B3DFA40DC96536735CFBBC2CEB5CA6A396 +C8D387BCB4D6031DD45563921409C33F3BFFE220D7A255AD2B422AC9DDFE7A13 +AFFAE4D39E8F285A4877D1FD83FF8BB9BAAD29C4C4DC7CEEB139D4CC253859B7 +24221EBA02F074C94543E32176588F50D0ABB9B0AB449E21E01377E96A487B39 +E45385A6A12DE9067750598BD5A12DEE276612012DC4226B14B0B289C9C8EB08 +49B5FBCA42865EE9ADECA117AF8B2BE60A2F825F51B3FF32DE769AF8A9F0708D +7BA0C614E8B1CAFB429B6407B3BDB582FC49D3794A0BA6932824A1FE44429731 +5F1C8D2DEE93EFEE11C29B1C020196EEC515336D8FD1FBD73FA4F2B454066970 +1657B1EF286BB2DE8E5ACBBF40C992F9A22255B3D842E68346CF43C568E1DC7E +389CB45E387321424D7950AD8834BA43A128309ACFB55FA8D30C0A4E13A06862 +0BA93765AD3744B7F0502EFE5D457D7CA7E988968EF34E2AEF0D1C5C1252BA48 +E4AFEBD828CD1DCB34738801458A8E4F9970DFDDF7973961E756ED7DAD161736 +0F774F441671DB46F07E28F57A1EC5FAFA529685CC5D419FA0ADDF1030B3F21F +AF395EF405CFC8FDD30B79B80CFC1708602680E9985C38474971F5A24FD0C415 +CAF1F98938F27BB1C805F0617DFB86783BE6F3AE6265DE523D2781E94B042D16 +36DCBA12FAC0BA0B2CAB48F31FE24BCC5B032D3987DE21BE7E191D578E9E5644 +81BC38C95513F04C8F1BB7DD82DA3909DC75CD509C0A5EF8226E0EAC4BAD4D64 +FD2D6BD98699B1B147B03FB3235F4F16F367D9EF10EEEF02093C40B9C6905716 +E43CBE3B6814EC7DC6358B72A2DBDB3A22F65E1B9CDDD4A639ED9FF36E021E07 +F5C45A74E31E755CD1F75CD2432D2E369537D3D738D643624E43CB52A1675A2A +2501397B11A902B26BFA828E9A744AB40CE3C143C758C6969350BA53BB8CDE8C +B21FF1336AAB3DA651DA4E822AC8E7B26B484955D115689E5B47F1FC0A88D9E1 +C58559306D908BEFBAAC4411B52396F19D4585B4DA54EA8929B11DC4428F158C +AA3AB1B6C5736A7F52A3424F1B7995A97253C8F822C11204E340635D13BBEF95 +108334977E7FE842A2159F2B9C85558031F5D1B00F3C41E627B1DB9876EE658C +50AB296BCF6987C92C60EA329075C391FE8162A7ED4E48FEEF9A7ABD6C26A1A2 +C667A8B8CD2647A9CFA8C792FFE0C2F0DE0C0E93D1A7B33E8E7872F061FD9655 +F39311738856E18EA0D00B495CB2457719FEA94CAB84DB6F5FC372411D7A0AB2 +ADE0DE96EFD4C911DFDA9E71FDCEAFF5D560F3F5452B64A5DBFC767F42C2E275 +BD357515CA488B6396DBB3F64411CA16977271074F78E59A64D710D418B800ED +C6B89803DB4C5C203539D1F5B72F010F93C7C4AD5B083D12593C3FB192661D2D +D29EF941C0FFE601698FDCE92045973835637CF0AB9DFC595937BF7D781331E6 +78F335BFC3F213A9C80773876849A5DE2BA1F785DA4970B01E04E68CE7F78DDB +940917DE35255E56DF93FACBD3F533FD420D0ED3AFC2AC3E45FA9253689BB1D9 +19F38AD55414836234C1D0AF2E1C25912F3ACFE7CCBD43225EED8810DA3CEB40 +E498C288573E42AC5CF17A119164CE2B3B8316210EC5A325D3C195A551AED7EC +8E818836C4D468C25182A67B415B1E7A4671532E2E5B0A48DC0572515421DF03 +8C3111B7941E16AF76C4DB3B4966CD94B2771D9ED98561F8F63DA8A7729A9A44 +1B1ACAE266EF483DBA4330352BB78C9A633973E74FF825496C2B2460B7433B1B +871A62E67A4A0C875EAB2584287CF19765247632CD042A8F461F1BB23CDB247C +335FD5EC01BA73BEA6683515BE8D8B581B08C36F80672D305901FC7EE293A718 +10AB9DC8F570C0E788408EDA4BD73805C778942149309120287B89F798D9E2B3 +5A27C13E8525BFA0932328B2DDCBB108152A94203F0DF156FD7DE126F223B9F7 +71787CC4910E27135F97E2B11B3AA69E2B6D2B5124BEFA9DEE221AE8D7DD7BC3 +52F345D77C923FCEA0CD12087871E25268727E7DAD97B14F98D369CB482E2087 +6085299F47729D78B4528F7595CF6FE9D00FB307D1C77921ED2E6BB9A2A89B25 +C0106697F84A40F3C69A77AD00B7BFC242095FCC24AE45E00A8D5BC61CFB0153 +27141353885C248820C49E9FA54BFD9F9AE8E9D60E06A3E1955B9E3970B83B53 +A8AE3201615EA63E30417C943F5A83D81FC8827E4B6213C3B344E5D336C31BA0 +F17562408B68E55CF6FA1A764101BAE523C472ECF817D5BB88CC063E9EA87B17 +05F8452F5EC322023BE3FFB2E4CAA480DD197489E5C01F53CB69981E00E5A8C7 +F7DFD97D8373808FBB4F98A9081F4BAF812F4E2ED6C2570DF157AD19D1784999 +10258144701B92605229AF97A54E15518170417775913FE55AE0E85B4F80A001 +4721E49FD1A564DDECB23829604F294E84C61D88558615EF1A68C240D886C2A3 +90A61444B7C0994A062874CF6C06282107EE224D32E120073B5A828FED32A0C3 +FC07BD8A70F8C0EB84DDBF7A4CAEB2551A09B9083E86F578C06D114C3BAA3AE2 +B585B6BF554B2BF64B8E2FCBB727E61597736886CD9943AC0866AC714834A884 +A4957BED0BDCD5F023C5CA15453F8431F7ED284E0C90863E3C0BC7E4C535305A +DD7183F445D18A816B2E3F0E0C8A34D5FCFD2B18F0CC2EC83451D1D23384AE00 +DECB0C1520958D503EB5FA250D20C30854B6AA35582485F048BF06AA5F6965E9 +63D2ADF0F587B232DCCB7369D94EBFC519F5562DF8014A8421FE88C1BE962D2C +02842016F9D3556FF339DA87CBA6F4A23318447C4F5CFB55552241E0D701E0A4 +90321FA5F1975FDACCDDF298CCFB76912BD7225AD2A064E1E61800F2C1CBC8F8 +9464C38F7E68A338859B54C917DE5CC4FA18B4F9BD2B5DB074D0AB289F48927C +4D5CD97F9359C701C79DBFBDF6D4708311CFF6365E3D93CE0750B17F9150B6BE +B454F18A3C4DEAE8D31E97643024602025FA25E374A2DCE0E76777E5DA8A64C6 +70DBC1162D156E60DF2A1ABA1192574B8FF0972BA80B3B732FDE78FE0AF06A0B +3E3A704AC798375574D4B0D36B329A4BBE2F4A57DB9349CAF380494C0E6FD6BD +EEA4C61899D8520E88AC2D124B798C550F2ABBACEA3BE57CEAA9144CB9652B6F +EC4E36F3B7C2A8E94287FAF6FA3B03DF1C84E15E26B6614B29DBF107C977EEDB +6F57C612ADBA4FDD7928F4C10DF79C4DAF719B67E961EB4C1A87B5C27CDA9682 +875B17E0A772D42BE6DCF11C447252B419C2EDCEE0815287DDC8CA6B6750D122 +6D5A551C7FA86C310BABA8B0C048944B78099AF16165D61D3D7A6EFF411500A2 +E3491108AF707FBF9CE3D668E41BF7C97504F5407DF2F31C3D5C976E5CB237CD +664C4986AFFD1AE09C6717B9FD725A778D7E4D4F09C67B880209E00B442A3734 +E22D2543574126AFB1AF1AE80D2C6483E53FA9272FFB4FF099C4B6C637FBF78C +2A5A36D3CB222F44F9CA9450471CFB17EEBEA64FDB46F35D3B48B91286135EA9 +5B1906F18001263D83956A8CD566B5A4FD4AEF61E92877FA7CE884083339D8E7 +B13099D241E662D916EEC85CD11598D05FFE815376B28EFA41A5B382B6AC3420 +2A6F805357E35B4B1291E29D5C9AC84FCBCD4489C9E33CE354125D924F64AA44 +8C50679A3AB7EEB0476E6DA95B0DB0A29C002F2A85CDA30489CF1FD53842E7FE +D4A024FEF55B06A6036AA45C5E37C17A4C498A9CA5F409B03B98880271C5393E +2AFC361E2A3840F78ABA1574F47E6CA9A878AC634061D1BD0E971E9CF2415CEE +DA51246D949EC798E2346D46130450A74AB0AEED24D534081F297FEEAAFFF7A5 +3009B7D530214CA1FBCF0499E0C5D4FCDD763A8020312AEBFA27DC0FC248F881 +C67E58E44EAFEEE0CA12CADF526FC5B7738EE0E828B08418BDA620151D15457E +C7B364C6D30EC4A8FE66021A927DEBBC2C65E57056B005D8D7D85D8D5E34E616 +B2C1D23E04E9AFA303DEA1CDD0A541C7C798C8DA1206CA034E238687B4F5AD3F +D2CCB1ECC6C69EBD5E77361A35D3E8E19E2D0BE39967278F4770D12AA218DC53 +679E4E9E6C40147AD34C48F156B61341ADB0776657C17E9C65769F501D24FC84 +211A2270F82F63D1528159CC82D5B258CAAE9F3D5B05077E581AD28562D06423 +1CA125502DA74E81878B6B75D0F33A7D61531F2EE2D04CEBD6C4E7D2B69449D0 +B5D3F38E2044618556EE622B5BFD352907D3F961DB8D05B5E7D191639699ED01 +F8184FB49BC0678FF6D5A199877FD3CAF6E4B4B1DC7B5BE243A19FDB1B8C1CF3 +D96DB50B3EB40D76FD0C6405B7DC5B64C2DFF8F8552871B4724008F0D935626E +9F168B5D0A789BF29A46A33B5BA22B317766B100C21EBB2E771415614D036958 +841BD00A85CA9FCEC57DE410FDAFEA71DD6F35FEDD6FD4C1F0C596F8FB501885 +10A1ADAB1A04AB4C2BBDEAC9DBE5FF9A99BBEA6E1DD628E22BBC8F511DFC25EA +94C178C929ECD10076C89559CE5937B5B97327412ED1CA9211C019AE5AF7A5E6 +8D0E973F62ED545EC5400A358B8E1CC75861F77E3C444C037C87788BFFD92534 +DA0E0876FA196C0D612D9C31A042026757C430C559A1F4CBBA32DD21B9410FF1 +412626227653770A58644381BB75D82AEA161CA59CBE1A1522FE71A0A1E3EB8E +C92493EDD13B3BFC4C8CA37785310C76E31EAAF25AC3645DAA1889BA8D497134 +29FAEED6FA819AB75BF92355FE503141938B9D9598DE9750BC545A8E44102897 +C4AC2FB63C93972E53588B299B61B584B1E8D97EB57EAD2DBE607449E683FA4C +499873BA7809F4B20C8491E1250E626A02DAF6EBCDFC5EA497B320EB42C452B7 +B01B28FD82C84DE5DCFF74AE05A7800EBD5D045958B23F4D5A78C17F7C7DB66F +914379982CD52CD940C94E53F5283E7D5B7D5E36644657390037BE2758D4FEE0 +59A258B4B12C3F58791E626A5B91BBDAC63D81D02D7A077A61E0E1354F0A7525 +7E32CACCA3EA70C776924CC60A09BEA9A64EDABC1F59C8BEAFBC878677AEF15B +0CB68D588A96D8A16AF46795ED408BA514B9046411A34EEFDD4DAF58B3C5D861 +38D5169E6984DD68C7EC5BD656898F101674EF8935C10BE7BEE24A29D2E33B1C +58AB1ADBCD7CC421CC0EDA4A499B575A2AC7E8789770F17A138774F354A6534E +0041BAAC7529BBE4A3F7952BCF4E5AA41B9F4875A6C9273986EF5E4BEE8E7223 +B69AF09FF47614E5BFF2A20C33F85AAC3757023C02002491C88CC7E57DC15E4A +F234577D89E96BE19729594FA3769192031CAF26986168CCA55069567229DC90 +7DB302C8B5DFF54E1B883DBB1CC9F5BE25F3AFCCCAB0CE7DBA07A4BFAC9A42D1 +17CB7AFC5DBF8B64C244048500916718F1AA29C4F70C3DA2AF8132BAC978F3B1 +57B0C9609D6154B6B29B23B326086B9BA19F34690B76BF4A5350D5C464E55ED0 +A2B3D135132736AB06D1FBA716EFD67DE10CC169CB7262E8BFD0B74A441F74BC +EA002D2EF2892FAAB07D9B7C029909361FE3464A3A9EA730C7919F730674C320 +A162B4A37A85E5C093D11BFB3BA3C489EC7607C9778C05EA753781D676DED75D +802D4408313C9194D97B4BEB8939C29A7F25D4204D590C43FC57B88D218CC8FA +887609140475C83D84214E34B83E65D57D70169EF86BE2517ABCB65889AAEF38 +1E7E3C9FFB255FF53605841D2014635C984367750D8D28D88D5A3247F5761225 +11C3C0224D8D9E8969C0A22601A57C18BA2D075F69D354C65F896BEEECB90EB0 +A9676043C02F1F0F5555884F71BEA5DF08E84ADCE651EC874D9C6FABEFBD26FD +6D92B7BF2644A859C267217185665F143C2B7BB983CFDA3A5642D33A2FB235DA +BCB3700070AE0F169100E539C238299417CC3BF096979E48FE902420AA988C95 +D8B496FFC01D25FC8BC97CD471E43CBB828CD85F2E9CBD5EFA8F45520F358A7C +D841826312AFB98EF4D00750BA52CF0162E4B488AC30E505A0FFBAC9999A557A +EB434B55535BC652DD78D78D3C5EF788FB8BD0A7BCBA60C51FDCE574CEAE2B5B +69BF792E765BC5CA46CA40DB9CF1313AA81A7470971A76CB9D076DC9785E2A39 +232E9AF8B2255B1DFAC67F6CF1934C425D85F50F3CF73EF17621BCD9BDF7748B +DA2D174EEBAB313EA65D3CB43DDE773B1232DC4C7C2595712BB62D6B8EAEF9D9 +C711AD792E50005DF0718AFF4F3A1A611D473926C79502BF7BEFCA405B1F3CD8 +E3A900AB22FBC0007B0CFB1ED6E06D6BA4297E003129CEBE0AA212BE8C4A5563 +733C75EA3FF09AB5889542158190511B7ADF466B174207B8F6F0145EB114AB55 +39F422E5EA4F60E6C69F8EE550095B57D086918B21511C3A8C3260E8A89890DC +30C51BAF770DFD44D9B2E16DAEE8A2AA1306975591E098FD4B6D73CBE79BB8F5 +5E2194C3A1A92FE0AFB51673BB078799F07B7BEE5DF73AF655F3F1427A64FFD8 +F9A3CACB4FDC14B3E8DD327794F944DE33AD067E11A80A1E2B88039C01C42BB1 +6D4162D442EA830AA62EAE7D83FBAB29FE6A7742B3FD1D5229811ADBB647AB04 +2BC5C01CB2E72944082A0A75AD13552BCD9E244503FDBDB53001F53A45F7296D +DD15D2D78597F4F141F4735999BF8E24C80E4E74598C74925517CBB569E279E4 +B6E600A5D9573BEF2D0A0EEFDC861F15CF6F42243C23441398CD57EB41E8F7D9 +E9E389FC3DAA464D31ECAEF89264FB4C0EBDF043B20F2B82BC18537AA2709030 +E478AB2427EB5CAA208D6327DE1DC64F0069726767BB8DC6FDE268C495581813 +6EA8D7477A2DA7869DED5EC93C3F4DFB0B7CCB96DD83B1444C3AA888FF6E44A6 +7FAA892E626A34EC44D5B56F9DBE3DF36E4972A582D5657C1CEF084DC368C827 +0C04509775BE9125262CA557911A37C20EB5CCB6BBD2761319282A69AEE9AF3E +12AC1164C10D2FB7CF16A33C2613A77515AE605D3071FBD5AFB5B0959E3BF642 +A844F8D6759ED8634F775C3BB5B56EE0CD9DDBED2D84845AED1816986A78CB8B +EE63AE48F2EA606DC42CB04160952D6240F73C886A88B5DE0FAA91A1D441A89A +25EDA3F27A8E5A21A392C76D9D4F41679617E98D4AE394EB1071CA0EE5ED3E5F +B4EC7E726BCF69725CF5C67AF7C9C09405063E9843D9BBDD96C4D031C6C59B08 +9D381EA53669E2ACBDCF7AD987A82B8A5E8E5378D0F0A7165BE3EA926120734D +679A5DE2021D185118A7817AC6BFDA4A52B342FBFF9B52748F0C20695375AFF6 +E210BC3BE6FC91E591A7419E27FA75D3D8E2A74C5667E4956CCAF8871FE28B02 +DD668464E379E7FA4655A826DC4F3E8133DFD39AD0A0AC9689D15F9AF0FC4181 +D128EC3353647FAE021225D33A1B6EBA00D209F4AE0425CFF818A98593F8826D +49189F3A8C6A7A772FD068FDBEA2E30750F0C8141D07AB4213D71C64A464E1ED +5E19DFD72DED66F0B9BA26DC217D190DDE4C0512A729FA391B1538689138D805 +E828019853D8FFD93DBB33C8B5A29368607246DEC8A1A267F02E115C86B2C5B2 +B64342CE7BE2D7283D02E25FEE780BBAD6E383CF9E8DD9D042039058550FFB80 +AF7704C67501A95FC997FA11787EAED8AC6CB2614062A7DDFA6105F20DDFA000 +4A35A81B947818DC33A595562528C22CAB25AB135FD86A58571DE03E8A6B3CE8 +7D6807B430EC5B98494F1CEB5BB94801DA67C77DB4FD680938A23D6A56392DF9 +C920EDBD39F3EAA975BD31A522C01031D823CB997AAFC92DD92B2C881C69E238 +20B58C7434C5DEB7AA1214F4DB18AF28351A354BF7738DB9AADF6AE2CD6D5D11 +928ECA3E975BE10E3ED6CC70C89FD1E256DA5FBEB3BF72D2E5F99E64ECEAF77B +A7BCF7DF578BC6314531D8BC497DF6964A0C5DD319555E20B0DA59916177E0B4 +C3B0D49F9947FDE1C2634D0B99A3959330C10BA5BC02954912556409C787E37E +B83ACD16A991F47739E406B5E1 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMSY9 +%!PS-AdobeFont-1.0: CMSY9 003.002 +%%Title: CMSY9 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMSY9. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMSY9 known{/CMSY9 findfont dup/UniqueID known{dup +/UniqueID get 5096650 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMSY9 def +/FontBBox {-29 -958 1146 777 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMSY9.) readonly def +/FullName (CMSY9) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle -14.04 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 104 /angbracketleft put +dup 105 /angbracketright put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CD06DFE1BE899059C588357426D7A0 +7B684C079A47D271426064AD18CB9750D8A986D1D67C1B2AEEF8CE785CC19C81 +DE96489F740045C5E342F02DA1C9F9F3C167651E646F1A67CF379789E311EF91 +511D0F605B045B279357D6FC8537C233E7AEE6A4FDBE73E75A39EB206D20A6F6 +1021961B748D419EBEEB028B592124E174CA595C108E12725B9875544955CFFD +028B698EF742BC8C19F979E35B8E99CADDDDC89CC6C59733F2A24BC3AF36AD86 +1319147A4A219ECB92D0D9F6228B51A97C29547000FCC8A580F0834F28818EBF +F29D3B2C7C9AFD969BA6D3B7429E222AF78F6DE00CE2C8008A5966422936DC5A +DE549B458EB41026E123FE75E703E046F665B7DC78298A145548DEF8D579E008 +39279A5208EAF898A357DF3FD9CE4450811F20B454D86B2947BEBB11EABFC62B +187B12458E022CDBAA6EDF4A89D79300F635D8CD973E35FA8D9B1240B3D30D72 +4F30444BDF0B51ABA15A181A3196A6CDF9CD1D38D64C83E745A767446641991E +23EAE067609DC2E84B44D923CC98407812813D5AEF0EA21E560B31BD77D735BD +E35EAA564A570DD3294C703E16BB9F251D54407269C8B23DEAD063018D6EB748 +4204A415AAC8384025FA922E7074FA7B8A6C31FD3761E7B2737D4D9C9992B7B6 +DBD0CFDBD87D42E1A451FC5C7EFAAEC65D10D278470DDF42DFABC836CF15D042 +6900E0DCDD36E7153BA186B8358E74F91A1D43D62EB0B821C0E97FE63F1586D0 +98F01E8F6B6F2A435343A25CDD80D3AEB42DC8E5AE02F8837B84EE5EB508B02B +6980D2349984FE1A0D3ED12355D556BA483CA078B7921FB336EDAFC22AE05C1C +5614F5025B890009272DA8C838B81913C872A54F7E932237FB1BC2CE5D64CD04 +2D320C8E2E610E9EE36EAC7C28C833F8A14048E751B61D0CEB342CBAAB34D104 +311B421B8CE4D903D2E2518BA8B734E2637E2CA55426C4A87EEBCE637F8D71F1 +54626C52083262A5A9A3B49142948BC1AC0B7698811AE81FC3D3B1C14E6C274E +CFD6C480514F3649D7725A0A14C82C9D537B1C5A9ADC816340B8B4E484AF55AA +905EA936A83FE2BE788D084469008F407447E0077417096333F163CE94CD3098 +4B9712EE23120829AE2695EA63975492DB2615214B11C87ED0ADB776ED4BA0B1 +40D09D1A617D4F57EE1A9B05C8D5F42AECB9C6AE67757DE6307B1A9C467123DA +773239976AB0EA052B23F263031ACABEC9EEC7A43ACD0DC2D7AD66664FB0D937 +6A0F463A173E8B90B2596DD323D2350435D9D31B638112D70492F44769FC0C6B +40A291FE78AA598CAFC19E5AD195BE7264F0EC29305D36C7AB8F2A733797D75D +94C8F7AAFC49C13C108F514745A394F460294C6F3F44162BBFA878F773A5115F +062E934253B786DA2B9A6E8E843E06B97075BE99F21B330D6684998C5C340B72 +09D69B655097354CB682D42120ABF5109E5522ABEC94D05E7211DACE8737A1A9 +0BD3EC16F48798E67C7C85560D79366504BBFE5F7F5EC1B05B9F68F98149A02E +E53F321C213456D802C01871E9024F965258F71215F40EA71057C15E349F06A3 +A016BF1B7E44DC4766FC92EE500EBF7C47D36B1EA34981EE6FE38FF4903466A8 +C9020B91CFC731642003AAEB145EC0F13D6281B0838BB4236C220F6F467F7DCB +C79BB9CD5B6A180DD2ED7F94DA6B5CB833E62E898BE832DF601BFBC8148F9430 +C8AC459F23EA2536E301C9DFD3FFBAF4D01E7853B60A69B5D012DCC0054C0BA2 +1293D3B4EEDF7C5C78118138544A60FB1A500F72BA39E63EF5D97D68369850E6 +9B6336DA577128DE68FEB71877360D97204B5F3C06E808BF95AA6D6013524BCC +85CBA9FD2CFEF383645E7E960E7AD471A6A3B8B8C3A0A0B8464836499309D82F +52004FCDB68997BAD9E4B5B881634FE1213283B6B19308BB67F2364F7ECD5534 +467EF07CBE71989FFCBC6224417C0710EB3EF2676B9DE846E7FFB1AA6608EC9A +76C31724A19ABB2AD08CC102F5DE8E4E513F90A53F06D440A66B000CCAF6AD12 +6F4B161B56FA5FAED65A24FA183B84F4B45407AA69624068EE0E3E47BBE973CA +C72137823F1D676131C8CD5070092CD73FC2A93E0480310E21CFB14C031206AB +411694AFE7B639ACF35C28705A115E98BC69C66F6C58E9E79902453798D76267 +29B780AFB6B895B34CBEA51530E8300569EE4D500527BA294282FED81F730A46 +6ED19AA5AD45852739882216EE937FC45D75116BBDBA6FF4B4423B398E383CCD +A7D705B8BA85076133E823300C3EFC9EB9399575F5F6BBF868265304B299AD77 +B6B4EEA04C83E163E8CE2BFACA65F9DAAE3AD2B0F8DA1F986E6A12DDB2AFACD2 +217E37B093F46FF3E20E587A9490401C2833F5844763FE2C528E151214E8CA95 +018E1D8D47C9EB5FB4F0EA2A6D064680DEFD2C5489298F83B58EA7FA8CB019E0 +671028C59C3E007DD86CD247889ECFA6AD6DA2433B02180DCE6ECF9E495D0976 +D063A61B007233DED5E3F351D790F26D4011582735A69F37E7CCBB0AF254B925 +B378522B748BD9BF152E2187DEA12BB168C42AD8D67D18567393B35DB2AD8403 +5A7093EB59FC276525B8796FE81944F7F03349BB077110E182A61754ADECECD8 +A5255DBADB523F3DC251A7D97F17DC76281CC632FAEB382A972D60A15FDDA57E +2F42AA338D889B47F6A3C1FADAFA8D11D63EF8D0C8C0FCE61FBD8AD34EB8DB3B +722BD86CE5B1268F704ACE82A39466DC2D96C9849B9422567FA5E728B17FAF61 +59A77E9B3289DB807DF54D076139E3DFE176C3131FA4540C1542AE5AD6200FCD +B92D5DAA77DD4D3022049B6EA2A037FE96E0CEBB1608C663F1373246FA74D70A +BAE7BD37F3AC23E003204FA8270B4514DE1FDC5401B69CBC98A67A5CAB5505F2 +87C242B6ECC86E88EEF44B9C79DA5F88CFADAE8B4CF437A7CBD5E0C01A9BC479 +E92EA48C808820C567E74D62ED7597FCEC53DE9A0B3EBB9A3FB50AEACAB82BF9 +2AA56D029357AAE016582592972ED01C0ADE565E87607F17BCF4983E3321F606 +ED664B9516D404B277C2C1B0F873D469D64A9744D9C1897125FD471E71BE060B +A3A617DC8D812E184664BD83EC74EEB5197611380F2D859E1A61A14404493D04 +B5537C04541E557CB5039905F37E72B762EBD2321633E12C38C409338AD5AAD3 +1A507BB2B8F5235F0614D0CEC2BE44CAD504C9B2CD3E45437A493A33B9BF8A1C +3FE66B1E673449A54E8E3C3470688E94EE4404C888583F7C96B43A592D586D25 +8AC1F206EE5FBF3AFF7EDF9BA91EEC6BABD573D455E0AD6802E7E23A662D4F12 +FDDA00EA054CD331DF86C099C4BE8AB4EAF5D8EC1BFEFCF50302BE61E6FF26D8 +1E88C544570E8D6F7D875466C70AA91D6CCD4DF4BBA34FC67191D1692D3FB76A +66F114152956A5E38345E7495BEB1A389B7D0A59624ABA784F77C1BF7728E556 +F9D97D71858D61624C22C6914A3A995BD75922A2C37EF2CA91F923A4561CD3D5 +2DCEACD9C3A65A9FAFFFA4822E79D6D89D6366DA6362DB16FF061840582DA35D +E4EE8308040C2DA5F298944EED470D6E4E1FF58DFDDA96C20C85E2DF2652E12B +2EFB8F890D301E8CE777678FA786E552A62A60DFC369AF4BDC069F5457FC86F5 +1FB1A38A2222EEE86FE1AC9A8227663AE33217C1F381115CCD04D2F381A27E97 +36D356DC64615AD63E1952B37AD4F967317D76A6DEF039CE8446AA634086BFB7 +7B0B34F595B61ACEB346F51ED10ADDB7B852B5B2BCFE416D0435A7CAA4C9D9A6 +152D9AD938F39E9756AC752F1604D884992BAD768C720C45479F5AD8E53A4832 +1B44D6109DE4460A662DA6081465E2BE02ACBEB52259E046767DA24E1416FF44 +D5AA1D303CC4249510D703CC6CD0FD7DDB078A91089FD86ACE1721F875F019B9 +82D7C5B2530567F2306F47DE90EADDCD12AA1C693B1982827DC36A74C86474B7 +0A8A2E084CA403AF654E2F8488806B943E38ABF544ECD45FECDC441DD970B180 +FF3FFF1A50D5E22216B240A527F95DA1E43770A3867F225DD2A8AF01A965E06E +A063997C4CDC0B16B52F9B9C8B0DBA94F88F26A7A01E596DCF8F33DA3DA5B02D +2ACF771F25A1372F521A80B93DB0337886FCAC0A2E8FAE0D7E1E0C31EB43AE3D +B4C2020F322A9857A9EADFA7782CA70DE7421B9A83DE1873EED8D096701370B7 +7EC33B9C104E9CC16DF8C80ED24D1F91677E355DF037621ABBB75E09ACDBEB82 +30F1AFB82FAEC4D9E2DA93303F72C1078679EDDA35ABF2AB38DFF091AEB273CC +5A7B29C0164977160CA75F62BC20474C7C1F127AE7E4DE865FBCEF852698AB80 +B61340CAB6F86017FEF570104E46B6FB3320E78D387CC58DF1C4FAD8570C0126 +B6F54AB5BCD47904A9255366AC9C88576456D3CF605822269CC65979EC3CC740 +AE3D09257BA3AC7A364A7F5EECA3E658AA20B53423553C416D98FD8100D80A30 +A1B19F029B7473070D7C60CDE51BBEEFD2B45B1C82F4FE32A5BB6BB5BC32BBB8 +F600ABE893B83B806A5435AE2B40E5CEA9068C6ABBCE73C9B3533520439F28F5 +E171DB1DD90B2EC04A1F36767B2B97D4EC0FBF217D4E80ADA5383BADCD7E066F +5F4B23738A14F19AC7D765119AAC9AB6CD0A8AD4EAF8955CB9F5BDB0156CACFF +236D24E8A12AEBA549FAA9D7053D10AB7094B5D9B86FC7375E245EA8D7FC64B9 +859A1A37C006E0449AA8DB8B89730348C71C7E014A2EF8D871AAA856407C33C9 +392720173DA60850938D0537564455C3957631AC8F8C0B3B29BF6163828796E6 +0944C3000B990715188203B32BC4E3FCDF173A4A9C78753AB7D76BB9A4C2DA4E +3EC638753D6FF6356EBB07D0A9E8D3F50EA5451FB6C836B9E5EBB8F1331C8CA0 +B0CD271AF316502CBE87F652B5ACCD43176AAA32C6530C69E77E3C85DA496F3C +06980ACA739DECB5D986622AD81F204579218660EA791774C2D11878DE23F858 +BBC4ABC7EBA01B7FEBB32BC914FEA8E5720731FD43F8A5CC53954D13455827EE +9C7BADAE8322E77EE7413D6C86E6D378FF8044F50F529E765A1CAC29E8B1D91E +CD40F70F2D4890E0FD04502933212D5AB176DB143831958605C1667B8841E770 +CB4D513DF0C3182C23CA653A7A714B2A8DE29D6B203F5A9481E0CF3FF073FC6F +476B0B205136DA43650A940035A970584DD7D06B7CDA8E960186158B75FE3917 +9B1D0FFE45EE1606C875DF635B80F5A9E5B67A6ED748F52A53A710A45AC20BCF +FC96755E685A3B0712D5E09D860986C61C631A8D2A505C57D57C1D4AE78C552C +DC5C9EE15B82A4C3020331B78FDEFDE53974C391F375E8118EB7B2D23DFA3F15 +389043EB6F33842C82B896073760197B13D3DAC172D77779DF517AE31BAC43BF +5BD6F75B59AAA59C7EB81B776FADB720282DC06BBC9C33AF5AC0BC59F6A0044E +3898A4B5868EF32BC4F980DD863E8EDF6DE3E753A7B3E39F8E8BCB3D3BF14EDE +3395E73BD07440FCE88FB28A96878E823C1E5519650F2054EC2CF733EB8AEB0D +510E5659A3179C3FA8E31EED621BE0EA648D76C370C3CBD8043BE206F28699D7 +5871DA483AA5D3580CD55902C85DB9EF5CF5D3191738FE64DB42410F5AB8ACFC +CE07E524BECA5633B082F4793002A4A7215AD1BA31159A30D72AC60EDD0B2BAF +73C9934C85C5372B47BCD3ECEE463CADC05CE8352EC1399ABE2465AA53E5965E +79400336B1F5D1401EE2DA5807F9701D1086DE505F6AD4E64DD4F3E5E90CD64D +FDD25E6FA03B679597CFB8C5C1F03641CEB7B9B40787E70BC6B063621382E3D5 +1287365426E44A2EA208C26155CDF782F404CA7B3C6D379103EF4A51CA3F1343 +22B04A0B55CE5A7633175838B9E50480E9B80446CD3B541362E7A10BB3C58A42 +88C0FF580B4A0296073F5B6505E4FCCA5D4BA2F7E8D59BB04EC9ED4E664BC2BB +8754777594DB26DA0985663D321CCD13FB261BE8A771470DDDA783493E452A87 +D0AD01AFF8EDCB7BB159F1408FBCC52F126C46234CE6BEF241B49472102598D6 +91FF2DCBA2CD0E6658A61DB3591EA02E4DF202954B655C5A3C34B14CB60CF9C9 +2A6683AB282DFD79B33FF3A6AA524582F174F11A5568159083496B242ACD2770 +0ACE8473D363AC11250E5DCDCD3CB2281B71FCEB8AAC1367D0714796AF3BF01E +80BD4B05A0A45E6CFA6C672850B9DF113F5EB15923ED885A0E4488D4914B62A2 +FAFB6A63F8AF0CF0DA498E1BA68C4150B6E59CBA80BD17026FA09920006D2075 +BC6F1010D0A10648A20D8ABF8137BFCD6A5DB3606F6B3CA51E8A4645D32E5966 +6DFF7537F65DA4268A86BCA5791C21F5FA9F4C2F1F6C1AFC4D4BADFA63609C4F +5F5158608E6FFD9C5BF00646C3AAB983DDA5E37A9C2EA81796B161A06201FB6F +8EACE87008EE7A008BFBA69AAC04D73A8C0447FADA0249D8861ED405537E23E7 +BE68C4349FD64A14E3A53D6C6484E704BE4AD5FA0269F56D3B752A070D9872FD +A5CEE543B413F13CC4CFABCE885BA4533EA0969FDADC14DE6846B330552CFD60 +A75BC8C9DDA76102E48DB6B81975A1F3FBE182CEF410841CC6164AFA0CE51FD6 +8E8E6F0971277052E21126909B5EE51508306B886E945C11DFFE1C91BD0D79E4 +554CC7D6A774C9B98610A1E49C3326D62F157CA9D89B56699F32DBB9D49C716D +CBDEC052133F8E18E8F3D7A59008398AF3E4E1192CFFE4D0B7D2ECC8DE2ABC1D +4DF7F2EA5EC0DD756CB2FF9F95FF10E506463D3037BABD221C08506FA603DE1C +DC45A5998921C4023E501DBA494C1F41C0955A761AC3D5B13A88EC4F0BF40756 +BAE8645EA76027B8AF74726521DC129B9AED4BAE67A511EA56EE9D0D8D30C7F6 +FA8BB890375A263A0BC4E9CBBBB8F8F8613A43B10E964F8CBD6BAA2B0BAA70C4 +4CFAF6EBF4882B610AE13689823D37B046FD26DA85BD96B896CBDAB6662573D0 +2CB87D45A86A016146513441BF7A748F1E9C2AC7172460EA628BFB22F9797905 +F6DF6749F13C7F234A2CE4829D41E0F53EF3C5F52165297AAC4B5AF4747A9F53 +60370B4C0110DBB42C25308F489A856B6664371FC2126BAF4710790021255653 +CACDE1B9ED6AF5C696C3D8BEA4698DD9045B3004A5C4C03EA994A5445C9D4EB9 +865266850047E28F2D82A08617E9283FE637CFAE4F7F261CC2D616A8C746EF23 +7360553A6B434201944C04B1522533C61F615A20BA3678AF8D4BAD1FB548D82C +72D41699FB96EF40ABBABDEE633F05B4445BFC4E6E661DCBC8EC473BD2BBAF7F +75F7FC86CC7B1F3A2343EDEDF6B5F7E98A0BC4443E21611F4D807FE7F597A37A +A4DAF8A8E6D04FEFB320E0E25C61E156A72A3E4A69EDA04B0E3B72B69FC19E63 +AA33A682663A8F37F4FFC1533CCA2A80DAA675DD723A7E13D9DAE3BB0F5F3E07 +99BAB9B9BCFB29B73FC76EFA02F4F632690651DAAEC2B3688B636F0FCB156047 +F8EF7239716BE5D3DB2FD1DAEEC11DC2D65CFBEDC1C950CA737A04FCCCA509C4 +7E47664E160322366F70CCC641B9B49192AB839E0AC609AB2FDD3783095293C3 +11C56740FB50D23AE9FEAF66DB13F35367B562F15161AA270EE44713881B2387 +7889FD10198AD4D750390811714DAE7A8DC6F5770224E93408F8CCD4F8712A9B +3C583C983D98CCA7FF9DDDEF8AFA292781B1D7089336CA631413EA0728353E8B +3650EDED7ABB6E5DB5B85014A3A9C4C76EBB664A5B3E0F3CB2ECFBB6294F5C4F +69EA3AC8C071B122B129A28E04FE989DA9670EB6246806A516517DDBC52DC001 +4C027D51EF1F98506F21E63D57FEF267B913F1ED4770D892239222F3A9035C35 +FFA38D1AF3ED9A8C02235D7422695D30A2F801389DADF34CDC489EEB38089B6B +026D884EE1C24FE5785C383B988FFFEB5459A1644CA44D353503AE117E87F956 +393DEF6C47D4E1C5B1A33835C9B8E1B98809DFEE43C4D6DBEF1C2B1ADE4B8F13 +3B5D224C25927433D5738B0FE019CB88B1F373E4AC2BDB5BD86887FCFC50FA1F +3E01CC550BE5FA0703E1B89486C67E8A16C560DBC051028078BEF85B349D3058 +55D633040BF5853FE9E50A671CA0A088959B2AACA1B614DBDBD906FBCB6A6B13 +7ED6C5BF448F92E9EB110ECAEADA36CADF4B6D79703CA2EA9BBCC810A6E3532F +63C155FB86CBF6A6049D730B4729C613BD759AF3742399C6B118C1D2B38FBC4A +2324BCABE5841102D7040970F36DCEE10B8D8708CC440A7C71AB86921F94B67E +7E100DEED6F4DE49CABF3B3C9E4678EA45E69E5CC2BDBB2D29AFD22F1127C146 +E8728C97F121DEDE3F7C6E368F76B4CD91124AA35192E16FD57698646E72D031 +9876C6733B33A84E0C4EB52BC1000463EEEF0635E8D7924984F5E994EDC9818D +156F673EA49DAEE7A1E5CBDC4665D3DA38E7F2925558ED1AF7ADD097308BA4F7 +78D09384458319B19B7A9C0A0423681702EF99FFC41D2F73181E7C9EDDEA3970 +EDFF05591BAD5B4A267307FBB6890E1DBB3ED7D2436094D1327FDD1B8EBE6504 +255327C4A4AD2681461B50CE36D7E6C9A93C99C07063F9E880194CB257C6A3F1 +264527D028F22AE80843231A051F0F1230BB0C743B7E220F8250EF543381E24B +CB94A4F700E33B6AFA826A83E4BCBDA6ADF3299E22D4043B82D2E1767A1E9223 +B850213D2F79FE60AF64970F4F8E667F1B21A210A49FE974E2145B81EA5282FD +93478D9DE9FAE7BCF57021970B04C5CF799F253E73D31EAA85936601B45F178F +F154662CE6EC160D3A089F1DBCEC490878D77A0C5D553F12C29DB63351691AC3 +2DDE29375891CAD6F174919ACE0A3624E38913FE8C +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMR8 +%!PS-AdobeFont-1.0: CMR8 003.002 +%%Title: CMR8 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMR8. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMR8 known{/CMR8 findfont dup/UniqueID known{dup +/UniqueID get 5000791 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMR8 def +/FontBBox {-36 -250 1070 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMR8.) readonly def +/FullName (CMR8) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle 0 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 12 /fi put +dup 40 /parenleft put +dup 41 /parenright put +dup 45 /hyphen put +dup 48 /zero put +dup 49 /one put +dup 50 /two put +dup 51 /three put +dup 52 /four put +dup 53 /five put +dup 55 /seven put +dup 56 /eight put +dup 57 /nine put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 100 /d put +dup 101 /e put +dup 110 /n put +dup 114 /r put +dup 118 /v put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA +0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93 +51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71 +7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551 +E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078 +0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273 +C01924195A181D03F5054A93B71E5065F8D92FE23794D2DB9928A7C95D3A6E9B +8E92F84CA0AA44461D2F4FA0F8B81C6F5B7BE98C9712BE166610465CF689DFAF +27C875C029C0116DE61C21DA0092D029E7DBEDFDDEE3D67E6936623AB53FA2AF +18BEDDD7AC19A19CADB6ED6CA7A26E6044BE414FFF59C0B98D5819A6B881F9AB +7AD0D03BDD5CD309C67811D5CF0B93F6FDC9AE64F74ED4E81F2E18D880BD842A +DAFD0BDF06300201C6946087FC0B999447BC370200BFB8CA420B668B32EBC242 +6DB1546A7164CF55B332FE9D239B65F532B69EF9F4F93828A44C8F5C526126F8 +B7A369114CA68D4F98638121F4E17F351723D9F1A902FCF087F94AFD23876517 +2D15B482AF2D68C3F31FFA864E031596E597882578AC7FB0DAE2A713B065B374 +3E2E72519ED6D50CBCA40A7275A7109A4F3ED8A4566AD8832890D3D1F4899850 +9B757442B7EA355175CD5D6D8B4152ED2D7EEB4CE30F174FF672140354046A45 +7098EC45B9DF3DF5CF7B417E201DA88308CEF4CED8E8903AF24FB8DD0187352D +25738519ECBC70304F8F620CC45D2586619205DA3955696FAFFE2082402B3502 +CB682F410DE5FFE80A4DA3D3BCF02E35BD577D0DE55E7B8A33B7A2FD5136B5DD +A0BCB61F8E7F4363C21F890CF287304DDB8FCE7FE207C0D160B81E7EA662BED2 +DFF8C444E19C91E72254257CD87240A70F1A964FA54ED9ECF27E27A57DACC3DE +EABB92C085030870C6CF5C40B6E47F5C0AEB30E84A73ECDABB2D754EF6EA28BB +16EBD6636BC288E62F4A38BFB55F5F4DD20FDD77D767F6CB52F9513E8EB75413 +07F1877B2C01278675177499E4E8EB09F2657821613F5C7643FC064293EC6E9E +B519FFAEEA36B19C9D1302CF91FCBF87FCB57C5F995CB6712BB3D8681EB6F05B +B2A4195A3C73CB4ABCCFB958EAC533BD89560D2790CDE1444C0F2E4EF27A529C +F01052964E56F6D76A190E5FF45934BB711A3406284AF130D4DC0D8112BB3752 +762CA0200CA262359D4F54C0CCFA9A50DE18C7DB14419E2990ADDC4A54B94978 +D9174CA39434022FA77FB30179EF805E2189C35919F5EBE215EE2A00B4407826 +CE56329C5586D8B414770BA5D45513C3AF1931D632FCE69B4CA504944E03362C +74A1177C6398A61A12DAA0F156543E2A8E9969C4308B7ACC21A5ECAC8F172541 +1B1316A88C0C163E574FFD3CD22FF08488662FCF2F9344BC25D02146F36CA6F9 +E2D0130C654B7485EEA9A110A33AA0C769121F81821E9A2BD062FAC158359D44 +3F9D9947200EF1EDDD5860F10438B162A69683957300C75AF7546C70C97AB2EE +37EAAF0089E2623F787F252569B06C665FDB45EC9681C0774ACFBA76B98C4E89 +7EB12AA5F8798FFC110B49C25E3A483ABE83B0BCC6DF0578403ADC369E013762 +C9D08FC94D949BAE636ACA9F36F4E3F02296775A062077B011A705B6F1784D36 +A926622CB3847533D7ACB24A4EBABB14593B5D8E1DAE2BFEF8A51835C8D4E76D +7543C126A4271C59A5881A5AF89331694F84489CA66725995DC3070F306EA447 +CF30F63CD476A46D528EC1FFBFB8EACFA2BEEDCF54C92CE2BD26DEA5827186BD +3A4D1709415CEE7D51D671357B4A5D11E835F63521B9824EE5282E58F05A8ACC +FD249461181A38C2F47BAC4E79BE368D64F886AA493C61CBCB2ED401C8AFBA61 +59CA6F6216D941A92AC52ACB3D7ECC28D6A58EF4CC70BA6DE23E80937AB38E89 +6F05FDD15B954C0826636267EDAF9F2BB466BF79D2E10EED9B04297E6BC93069 +79581ADD1A9D9FAE9306F46AC95B98C60A2E53D60CF1AA4069BE301E17E25070 +F98DD67BD8642B1D07571A32766072E48BF27E1576FFEED300D7313A358A823B +49C8F135961B7E259095C9BB67F996CE0B90E95344F203922F47E11753F70D38 +2ECB615403490310CEE6C03AFA97DA2F47ED47125D110FA69725BA0018F6A40B +29A307FDB3E52322A77A0102E6F57654CF1E96A134D13860D83AFA0A41112D3F +2247A09ACF7D06713BE443FA27C7E7220E875965D53030FE7D2D62EFD2F1DB87 +5FB091FEAF599BA8C5167525899E578AB341BFE2BC4E53A047093168AE189237 +EA55F055514EFA939DAE9E859CB5FBCF37D99484F44FE5AA5FA386B28BB642F5 +5DBAF059A50FE96C7C6D834531D64F1F2E99AB2E96EE74D149178B1C0618495E +293973D9A03E1790654B67C0882376ABEC17D74785B3737D81644F28B3BC6FFF +F92FE29126995A07E0BC5EF3A4B93789A103C428943E045B8D1A5063AE71E806 +568D48072E53DEA85253B01DF0BB7367A6BE4DD7BE514AD74E3F77C825ABA405 +64DAFA25EAFF8F63344B5F6B523629776CEB090B546469F6A6008DE43072DD3C +DEF51F62731037D1FBD0C038A1E9B669849EB3BEBA281624F13D20B61917A109 +A0A7871A73F7BAA18077360B38A4625C5DB9AB9E43BDEEB856FD0E2D3AA2E075 +267B978B9EB47F2369302E87DBD5D5B422830BEC32411FE75D584C58650EFB1D +136FEB92B94BF8939FD63AFB7349C7511E5E46AA7324F8B1FFCA9C2A9E9720C0 +A720918E8E860F137567D386AC29870FD990BD69465B3A3D2A0ECF2753578AD7 +80DC87EBB319EB5AFE0B6F6FF8616EA30C51425FE3ECBC5F8D0B0BEFDEF32FA7 +D168B4E85C804B7326A0942CFDE732B1171C643452B7099B31649CA2C38B62FB +46EBDF7180004C549B53F88021D029452C2B37D8C565BCDB0B11541039A13C0A +E45D4B68C7907B8BF08C6F41F564B62BB554235D50330E78DD02795516D969C9 +66119D718798120442CB7EB9877FF84EC69DAE25F8559DCE3BD8042959F695F8 +2F99845B1B5680DDCF181D806CC4903E077D1FF5E60918EB34C0B1E028422B71 +CA63EFBF3F4F3CD813CE831EB54265A555BDD35AD7D723F9CFBDAB29C54F8AFF +2D35C6A3299E0A2DB470C7B141B1E3E10DABB7873AE302926BA8743278FAA8C0 +DC6174501D6A289CF980A3F55F2DD5C3A514E7E7F13133C35D2697D64C25130C +DB78FC997968D6B3BC929E8A31B6D212C5128E4412632BC52B3A1049F7F2F61B +C74AE9A6AD19B9E2E240617E2882F7D29ED3A4279439107AF9AEBEE47CE85DE5 +CE9595A96A118ACF1EB1F5929930321AF7732E351E18C6AD378508E37B4C327B +0E06AAE21278AFA9255AFE5C022034DA2968D260879B4B38E7EE2E11A593DC3F +CE71ABA050C004473324CAB6F3C50E85DEDA3E9A27388D8FD3A8F6E42A79670E +F7549CFAD4CCB337A6E0BAA4846ABCA059F1E1933CF11DC0FFBFF550CC4A1B47 +CF7BCE0875FA747AA854534960F757884505A5AEE0330179A9547A4AE3E68479 +7A457DE83326DC30B67F27CFD4AB697601CEE352F72F0966B3CEE3EA24683BEF +6D23AD51B8432C3F0DD0D0F80791E1091F38988B7A54E466A9AC7810DE8B7893 +6B0AA6356597891D56190A7660BC7F657BC559E0525D41EC228078F2FBF89C6C +72D666DAD838CBF0861FBF0A1D4ECC069AA49DFBAE5C56B781A1D5D79DAAC256 +13E3F9B928A2394FC71691E4355642764459714412D6F8EF803FC5F7353822DE +6CCBB8FBE5AA1F2C7F4D384039D85E7728527DF9FE0239E2CF8BCB7411C000B7 +1FE660AE6A2A19229E5E8776CC83EFF3C27403935756463EB4721C51FE0B1197 +86C2F17842A0FB639F28083DFD4F1E86D7D3BEFA922514ABF489C5CCE93D6F72 +D2EAAE14F6CBA2BE4BBE7D7EA8EA19DB3A87350D4A52064137C3D15A5B05B03B +70B1DA7328D10713B83974C390C3270AF5A9A47C0BFBFABB9F31063B0CCFBB10 +0F236C74446688198EFF039110F6FF42FA9F82D463AD3958B5FD205BDF85DE20 +FE3F0C7AEEF350AEE6DBC1DE2E2DA4F4599956F59D6F121F7086DC120416E180 +52DBBC4E56C09746938698860F30007091E1CC0351B43990E47208ED495310F5 +7BA9C6AB3CA10A3F1B318FD47C1CE3B9FF1304321F9623E32D315AA9CE64B35B +F841E6C62B5B2488A311C94937879E5E0E170FA77AF0AC75C5E6E9F3E8F825AA +09C1702682E14FDFA72D27901C5BDE009B1E52E8C4511C6F6336251BD45261F7 +401CA3DAE7C4B0CAEB91B9954BF4A97C48ECE7FAD401351D59DDAE9DA94E2335 +74A2B880E4749D3D7026CB5299F16C204B6E00A20A6619C34922C7D3FB50F127 +3157CFC08DCC5164C8023CD1B6C3556C73CB8E4ADA845339CA9BABA1457ECEE6 +ECB9849DF1F0FEBC89E5F97C92978A500196520839CEBA6C0FD2E3D27BB4B4F0 +93CB2BB565F4627C6DB62DD0E084E627D69B5DEF42EF094381B62C0D67EFD197 +301B132420F51A41561E6106870147E0D597078435BE3819ACF0DE28AD779847 +F3D2CF667DA06955D53E0204CEA2935E9E984E76963D3079EC092031E2A10E61 +1227E5EE6770DD4D745A52655369EBA06A19BD7D95BBA271E488241199D1008E +36EA99F8DFD2A9F87B06B070158B466AA4C6EA3BA77DB0F853F0BF9A304EA291 +34069714368E0B94DFCBA3BE5EDB6C8204DFA7EAF5C3406F60A7056407D1BF6C +CB85C1F432F97D821F5518BBA79AF8453A568FB2C2D025A70CEC75F46C545011 +ACE3A99B2582793BA1DC655230AE2EFD24DE20A01D4A441AFFAB7771F223FA6B +9169849E727E494247F67D6E1EA9DCA06A082FE2094BD548AD7F08B565145634 +E7ED832FEC1378306DDC796303392ADB0CBA130B63B38ED57B7828B47732853A +893E8836FE19CCF27002AE92C2B2CACFDF8A42F1B8066E033B965D2E9157FDF8 +E1264B40813C1A4CE424274AA3528A4F09B3B53DD4D23789A68B3D17BC1398AE +0ADA2C2168427A49846DE0216908C2FFFEF4F13C1ECA12AD341E238EE46E6DC2 +B71B54C52659632911F901660261E493AE2483D64E119D9924489779B62BC9FB +A052E822FD8D83178E09ADC825DF0DA07FCE7AD68EEB29FAA275A13691B4A5A5 +B0BC0499CD6307610CD6209583C1152C559A2760823F8DC0B9B990BFFE7B7E9F +3969B968AFEAADB9FC0F1410EBBAA0DB979CF153F0B8C978405F8E6F2B6406D7 +AAFBF4A655A15DD6D1E9A7EAE10EF89264659B09283F50B734236885FC09FBE5 +98D780012FA77FCB19F15BDC522CC7312546C0730EF5225DEA8C22A3BC6554EF +4FE73B9AEB5C2F7DBD474221760E5F539A064AC450591BCF3499E3968F2CBD6B +F15BA2B37080A4129B66D4C2188524F025414F14DB3F96049A8B0E5EB2BBE7A1 +AD64A988FE875FE4FE5186BB4F5DDA16983CB052D474B7D72F3E8965663EB50E +015C72407C3437142D3D7DBC055FA627139488DBC5A0F98D805C2143D99F491A +167E07AF60EC9F17C36289368D740B632CB919A0E74C412B76CE7A5906D5200F +9E79CEB9C65ADA3A0F23E8947E834AE7A329A9F0AA7A6BF545B1D7B4666C6522 +CFF268634EA06DB3A82D91A4C0A9B227E79961212881A54A6762C335DE7E0831 +130C45D94394D21C049B9D189ED955438C2151514F17BFC67E431DD9A8349202 +2F616AEC1C7B19F63D5000EB4771370924BD4B9053FE78B5E4A244B9A149D66D +A8BF3B398396D2233E92E4A5FDC70FAADEADAFD255193D688842DBA865CF6154 +C9348D590F3FEB135D4B7BD4D76A52CB140888247CAFAB25ED51F4D187041CA0 +ABD956F83A5661CEC171B52AF92F9ADE27973B560C802E1E0FF51C4003D1289A +CDD09F8EDA8AFDFF666D35418CEADF3B0BE298F0D1E5C8E024D6A2017A7E71F3 +3A9FEC9930F1118101E040339F9D41379170928DDF5B5875212B271DC843F612 +E0C21C67263186E3D6929160464D4D5C8928E14D0845762C36FFBDE548188E20 +3B6BAFE5EECA0385142F01216FB8A90C43A472C1D4447FE5C7C78CC088FC72E7 +3FAFA062C338BDE8A430FDF1951B107D8D73FF9376FACDE5900BA362C66F8C1D +947F9545C5C13A53E4479B1C1A50472C05E8F8C266C6D4F4EB08E97B3B1BA972 +26973B844545089C5732322BCC9A5A8FC972FA0D7DB8BD85D2F515ADE65DA479 +0224F7EA2276CFED0B75B2C23AE7377F86F1F6F205D6FE19377D87E782143697 +984E731F83CA888199CEB425643C259D4FB8B58DD69A96085198306494BB497E +FE7C9954EF35B679BBE3847A9C73507874F71FC97665E2A58BA41407A1745247 +44A79B588D969D11CE4B863CDA655DAA53CEA5C3C263B345E782006CE9831D49 +603D2D95DE9E370D617F5928BA416C362BB2B4DEF16A5D44BD24B34257765F3B +6223B3F9B54DAED69A90C7050AB97B06693D253C6894CBD7B497DA449F1D9B7C +D91B421891EC0724F59C82B9CB288DC42F2D2D7A7F22EE3D910E15953D7766AE +276DABED3820390BAF2700C4653E1C77FE63DB71A66D93ED293E25B8412A1EFF +809554BF04ED0DE83F7F190883ED793803CAD2C34A66524D3A580ACDF3C13B22 +08F18905E7A4A16DA9ED2A112462FB9FFE481EC2069E484E8BBFC19D594153B7 +3DED4C11762223B7586483B06BC164D824D1A6FCAE80A35DE0DB8B33396771DF +76DC5C05578EF1BE00A70BAF3D951A01C87328DB2B0DAD6E1B4C21F37D1BC0C5 +A929BDE5EADF20DA60C4DE2E3C151005814F24824D33B95F700E09A0207EB602 +3EF60DEB1622B91DB99A855A8F1DA96358F05CFCEDBDDDFC8446AE3391BEEC41 +966E594E28D052DD5ADA49DFF65E79540EBE5329DFD86C23CC800F95221B9C18 +CBBF941D2FA47EF1EF59A89DB5DD188E75EE94AD2A79E2221107E5992C00D531 +2E00B544895A9204656867E3DE9D4CDB64B920B5CCA9A73E6514B36CABAE01BF +94C15603B86780190595560F792E5EF01650074EA4A9BBC6ED284B9AC2020641 +DCBCEE0ED27FE58171DFE104EEE4202759E594159DF45113C00236127A46FB35 +9EC705F21C0E456C1F0F924594C09AC64D4377C5FEEF764BA4A09ABA8D09DEB1 +FC13B0CD202B2F04CF5D73DEAB65C36C2FA7C0DC236BEEF6D23BFFC9C493DC8E +1831F19EEF81EEDD976E43BAC6B5CED13F901DE59835FC75490EA528A72CEB77 +24C38B258EC38B9E6B97F85CA8C10D8809BBE55A6FAA12456FCAC786942E123C +06D1E55F7ED04400088BEC968BC5081DC7A1B1B65166E7821679F76694F235FC +6854C8776AF855B83445D9FF919B1D80E98DE0741D06D6C5EEDB3E3EA6392530 +F1BA817737D8162F7B3A36AC2A03190CDEC654383E31934C3E0A012B639532C6 +26FEBE9B412F1C92D1943B7C18CEF510729D501349644C97F087F2F840074AE6 +D8CD0FB2E620FFC908BFCD938B675A0A4A687F7FBE8F3DD06A62D7B6DE7DF3E2 +49D367D60B10061EA86CD512F5A1BE8950D83C62695E130128E0037B62552D17 +064319BBB9B1FAB9D79705E5D68AAE9B36EA14BF1A59A863BDB8DAD9AB5D7B8A +E30E2B499F952D65877C8E38EDD7DB29F9579D09E629AC188DB6A6403AB4BA3A +D358B3770D727A2B77D84B6C9EC17E29D88E3421F9B7D2D822EB78BB8BB50692 +8C46DD6F9BBEF2E848A2B5669B200019802AD19661537A84D3514AEC5AA47445 +2C791E01DCEDF18D9506367241255FFADEEA6183F51A9F42448A7DE413C08359 +52DAD2A60FD606AFE14702BD3B0EC448720FE63438D020DEDFCDE3582FC31DF1 +17B25FC152789D2F17FD60B8209D292D2152DCF8D28B5ADC04F6659BBB746CDF +145163361823CA343763AA951C640B5D4A99B7787105A1609EDD6A596EFC3F6F +2AEE2476FB2855F4E5AD91E1779E88751EAAFDDBC082E579A0E8C8B7AE5CC5A2 +E0CC83A9ADBEA1F7EA19CCCB813219196F9653441D5D99D6ECA2ECB2B1D2E211 +B7812E89AAD716A148C5E9680DC50EE792CD6FDD52CF1E3480229AE086F5581E +8F42C1DDCEDAC02652AD32B78F7531B73CA9EA39DF033F8FA90DBA3D31126330 +0AB0844D233E4751A88881FD5D5E7CBA740B6A4A544179C5B0C83453E1C0F6D5 +2568ADC4161A59C422FF30204C334F1D3E8E7A1D220FA70EA9CBF0B38D6DAA11 +63CDF1CA131F5338E07126051E2E0D4B643A36A44F1986461CDF084B59B57868 +59662242E4FB89185EE0B4DFF28E8B1DF45EA09FACD50E35DA75FA891762F85E +8BE3A69D94C97D5FA7F1C90D70B5187331B37D797C28A3A5C25871D190E4758A +89DC70366AD98D5D74F5DBA665F885113CD2E239B65D0E985B783590AEA21D14 +51567BF58497ECE9A3564909A644B2C84AA91D2504539D2F42E0EA32252D1F31 +3345668AA407C5671DDA8C83EFFA0E1BF70A3E67336D832623965E9D223A8BD1 +CE1DF03648ADCB088425C49699BE53783482DA365EFC083512A78B6B0F373987 +2C5348ECB4D1A1960284B7FF4CBC01B07533677BB5AA0BF32C55DB940434CCD4 +F2B24B9773964EDA3C20A8237D399DEB865AF23CFDF06D991E1E96D3EFC1E057 +E2A0D97CCFC53A0C5596AAED8DCB731C9C5C81D980C780E76EA96D1E4905DB6E +26475138BDBC004B452B68A32411B6FB34C048C52C0B65B89933A49371BD37A9 +06D1C1418F1B8CA575EDA2DE68B57A4E79AC10D7E43DDDAB913B310E2B599A58 +A7F275D00B81BF8DE6E50E797A4898A98B749549892331659BE31CF3C9496272 +4BB2345ABC77014514817B58A97DABF697FE70D5F015B39B2EE1BE8C35886B1C +D9B5F1F77B282AE5AED5B613AD9552070DFDAC86256688EC321EC2285C060AA0 +03676DE1181E1217321583B42DE5E0F954F5494F9BEA15DC51DE50D432166080 +7AC8C2F535E5625D8A9B5826536C67AE552242FD284A66E5B083DE9B6A778DB6 +877368FFD8EA70A4FFBFDEC00D655846FFC1FD8E5FD499BB73C93BC0C6E794CD +29334FD3DEEA6D853BACAC8AD3753B8498F4C8AAB207AF5D624D8780AC3F30F9 +A54771EAA474C6BFA593154E5CD9B78EE41E7BAFF988D195B3AE56FB8D852059 +40CCC78B1A79C50868EA8AF396D52570DE77BDBAFEB799FF74A63B510A2E8749 +B2CB7B1B71B4EDB9B5B3A68DFB4D2F7E0AD77CBB6DA95CC9E0AA4EE20241F941 +2DD2CACEE2B37C19A4B54193DCBEF65DF7708F698736C15EBEBF093D2F8EAAB6 +BE26151101B71C41F1F677B732915998E1DE360712E0C3E63FF3AB58D7F8D078 +FB54D50A7BC3C041B29EE521879E4F5FBD18A12F5D0C7F427BC9BD281881EAE4 +A6F6DB18047051C6AA870BB9E8E6ECD8690D090E22072FC2A5067826A6D8B96B +C1A32DA9B2134CE9AFF49EC3AF6850C6F3930B2133BD330F846636B428D6AACB +6F0D9EEA42E13DD5562A8D95FBF80B99CB4EC433F1C33132A7DC4592081FD456 +93D61804C90540D905F2189DA06CB1BB0ADC366924BAEA8382363A4901915DE8 +62C750E831DAB0EB4444861BF5243C17502D00811593E1CACC2FE6DE16AC2CAD +5E00ADD0608472FBAB9BCB232377222D8A0508B0A541C69154B4B9CA3F2AD62D +4F892A43D191266E3A2ABD7A9492B14DF90F2941B5491E7E7151BD96E0F78627 +DFF3A84D743E9D92F25542A0469B42BF1C8EA00CDF68C8A024CBED39547C6A39 +7DB8608DCC79EA30B7D36D652A5B06B5C93C463A4D7E48F5117FBD607CC626B4 +CB6C015E88D9B5C9F4254D09C3018BFAC614F30A7DE44C9E5B30A11A134D3744 +0F7EF48B75BA0E779E7803B632A0AC8933F66673A376CE1A28F553C43DA6152A +0EA402E6305B03AB910C2CC37FD064B73985F5DB45011E6EDC35E5331AB68A37 +5715DF50928951B539D5E374ADD338590A2CF39DE99F5CD263CCCD356ACDAB34 +E3383A7F538A57D0869D4497CFD90953A0025887674EECC4146A8069064B139B +8989B1EF82E5429ED9401634F0DE690FBE0FBDB1594A386BA88766D3F7A7D17E +AC45D4170E0C56C4C35339BEE77CBB26A3CF7B37C05068B506DDB665CDBDD9CE +E85BA3896DCB2CC908B7C003B086C333AABDD14C519B48699D197DA89D5477C4 +48F83AA9BDCF086F1952D74F99D4A60B0E2FA5E06355B46DFAD11C407D5C4162 +7F4BF0E7CE49FC03B2C7346478DDA96BBBA1A38DB52E9378F701BF708F7D4FDD +278957F5445199583CF73DF14F35EC2544650AC01C0C33F5E5DCAACF68FBB31D +4DD6B5E9682051DFC31500FB17E266E100C7F5CE7F8EA93DAA1A8D8EF1A28858 +31E4C66FA2D4F3E482B1F62546DD571E36EE5BD536660D6A4AD5CEB4EE0EE701 +698C93E04B57E61664E8BB7E69AE822478DE28125012178A49E1A759B225F641 +549B1F14ED44A23C954E81671137C23186D702D625A371A5F80317D4A218414C +536B3A4202C62C818CE71391F3AC8BD93BB80317BDE1752F51A3BFF271C5C21A +F12D5C816B1C245AC2D26BE75644C0B82C2C07E306834CC487A5CCB8C94E0471 +BC012B5CAEE8BA91F11FD2CA7D86580C2D590C651A72A26B83CDC0058E0EF7ED +5681EA2FD864724306B1F3DEE1A601EB87E05E93F3650FB210AE703A705DBF53 +4916ABB67B46FB4116531C7F40ADCCEC1A6C8E9FEB1BC1F7732F83BE2A888601 +D0E6911C79183F9124AC9CF4F31CD3485AEC755A40C4DC80B74A79DA3434A1C8 +1B2E9ED10B4DA5802F25CD1E5BB0CD6685897049C8CCE17CD8970AAB0E3D6D2F +500C2997F487877DA0A9735DEF7F67C044456A6A4E4371AB920D6EDFEE78CD51 +77F6248B1FDFADB47533C3694CB6DE02380967300210FA0B06EBD55D54E5CBD2 +970D4643F5A60DC071A828546857A0C4FB83DD4C948617B0F0EDA1AC0CFBA888 +6A78B63352F21615A6343A7C3FDAAF6848EEACD2B6D6E1FC644C744F1C2E1369 +ABDE8DAF5E3528152866502C4FB7032C34DBDE182EBBF93DF99517E76CCEB19E +43ACBAC49E86C16CD431B2E68C2BB926C9F755D2CB0BFC9938666FBF80BF8829 +DF784993709E797B9ABD970E36214BCC5FFF7E7A7EB0E921C183C3835AD001DD +D2EF2AE8D0B5D001A34E381EFF55E63E1AD43EF232CF94D2E107BFC50034F7F9 +936E377CA06B36C5667C25722B7A80CA89B7553A1701EE6D3B9575849BE414B3 +C6F6AE2F5DB222EEFC38569BA78710BB26FC8516603A292F8179757F095FE65F +05597EF07E0B7A5909B07BD533C4EEDFD4AD869AD9D84E8A42477B6F4E707D7B +79CF00FA9EFF0C5A489B5CAE986D8E1FCE08345198DA4DB78C5FCDD0893D6DF6 +E9331C3094CED900015CEBD4CD059442D286D77B710E173CB8221A80244171F1 +B2974B01F6E9B1DCD9EE250BAF26506BB2F118CB98A3A99D85D3E09918F43099 +61513F46D19DDEE823CC7B12D664D2C9016445144AC2DD7ECC80EF843202A611 +3C67E91239A22AC72EA04C13D2A4BCAB6DC4BBD6C49CA7D894204C9E0BF414F2 +4B650ACAF14D69C96429B9BC62298B87BFA9BDA2DAEE4BF42A6A73A11B962008 +45CDD672ABDD8BA7A6598486D63BFD46C04BA5B007445BD2B71D5A5AD1E6F392 +08F58DE71EB89143D3F316EEF75BBDCADFDE6FDBEC4CBB64F2279F66D621CFC6 +1F8D88A0980700CC2F3B1A5701D7AE546D3D4A360286F455B62790DF6D9D2546 +8C1EE14572E5C013CF85B946F3FE1A1F0F4A403BF9111D58353B12BABE98C2D5 +63FF720807F95D97C421180D24DD4D4F2AEC6D99016AF3E33779311B17172B01 +FA7C0F3A7B8EC4BF38B0A0E7399CFD11C65CD907FD48B55808D9C4FDAC57CF45 +1E58F77209456EA680D4E74F695B4BDDD29A1615DA9CE03A5F5F7A3CA6317825 +73CBAC89C61E60BBBF1B090546594A57498B3F584F21066327922289B150B8C7 +318936988DB4E35812DFCB8F2C0A02E9763166CE566F852D0757977E7A7F8B84 +A030E13847635ADF2B4D1C6368A0472B3A516153E2EE4209223E988B400CB227 +0447B5944C9C502217559C8651A8D7AA7CF1695EF3FBE0931126DCD23F232A51 +A69B9A9ED5F1D978C6D4CF9A006E297A90D87E2EC77CA791518DFDFF3D665FFB +DA1834511766EF8E5E672FD4E27EBDE82142A1A12B73303F12F1564030FD93AC +D96EB70DC48DCCC1804FCD8BDBFD180A4633B858ADE127C552C971AEAEADB4BD +B22A02CE396BD5E07FCFA134DB88986A34E32EC1484860ADDA97866DD32F1F20 +51C5F17FA3C78DD79EEA2340AC38770FB9273EC3E6CF9C03DCCC03A5DFDD4481 +5E89BFC46910E7C2C556151E59AE021D93D8A5ECEC62E1E0B1079C1C895D7089 +A18F7DCA3BBA4482AA9C5F09EEFB4D5D7AA92847E197CE278AEC8E2FD2BBFEA2 +DA4764AFE97C07617969CDC578AB5F6BE1176C2FD926B193E72531624ED5C476 +E7C223D314A0041886420714CE9ED7BA13D73909EA77AD7E86D248A7DB86882E +17937277DEE01371DFD0EF3D994F220360D509F35C41FC80D67F16B316CBB572 +9862FBF0DA654069CF638F2865293A0A96CC9AD701EF788A7D7791C1D75AAA7A +BDC95F2D4C797D741899B4D0DA785F58D22141BA06D3C1404D9CC76D380BD879 +893AA129940AEA69AF82D0A581378629884F7BC3B2C15489B51C2B4415CFFE3F +3B858DDFEF06D7B21EF57CC2945D930E5451A5E95B386EA84D352250C1E08FF1 +524418AEDEC6D1A4C3DEBC200D2987F8929F2F868186863D7000078C7A4FA294 +AF1F8260D52E204CF4771457261B4BD8BEE78C7DBCE058CCF5F03BC3F5CEDA66 +3115FBC00E91E5C321504EFAAB3CC9A801318A9DA2A85CB4FA60AE1CD7DB0A83 +EB96A79F92C80B768BE7C34941D956325A3BC0F0E215B52D0E00F13ACB9C2689 +4843948C16B7BEB12FB8A9726AF4480DF108EF4BCD26839BB1579154F5D2A1BE +99C7EBA628DBAA7404A78C22D580BD801CCB8FC9E69A5659E9D2B20D93A3EDD8 +B758D384D343CDCE2EBB2CEB23BC04375A9ED4E1DAF8E8D1C602A6B73EAA3E56 +69D1BB0208A83E16FD429CC21B1E1E0E719C63A059EF45693C7F296D2D1E155D +F1B4CCC347461B6E0EBE60D8BB00D6B7686B771F403A10127831D6F37F7B1A85 +D1B91E9F966E1ACE78992FA31B2AA0B913E2F04D98CD97BD4022EA78B5AF95D9 +AEC3AE766E28BA89D850762F4B3C0FF4E4A5A1986F30866878635BC0445C931B +138DAE189587FCB3B935EFEC081A959740D284674AE01F82F072279D2F41382D +B441112FB8D3FA8FBDF2E2C0DF150E0FA44EEF94181720CE4CE12F1F483A0CB5 +D0A7BB1DDDED38FEA893DCDDDEE01FD1733758FF4F2E49E27342C1CF51B0C6F1 +8FE3F4DEF13BEBA1A1693554DF91A27993C8EBA88966696361F7A58EE1CACA35 +27D786067F21334B63E3BBCB3C098305731BACBDAC07385C015CC4966077 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMR7 +%!PS-AdobeFont-1.0: CMR7 003.002 +%%Title: CMR7 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMR7. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMR7 known{/CMR7 findfont dup/UniqueID known{dup +/UniqueID get 5000790 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMR7 def +/FontBBox {-27 -250 1122 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMR7.) readonly def +/FullName (CMR7) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle 0 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 48 /zero put +dup 49 /one put +dup 65 /A put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA +0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93 +51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71 +7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551 +E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078 +0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273 +C01924195A181D03F5054A93B71E5065F8D92FE23794D2DB981ABA2ACC9A23A5 +3E152596AF52983541F86D859FC064A0E3D5FC6647C3CAB83AD4F31DDA35019C +CDB9E3DD3FEBD4C2B36BA3CF6E6C7DA85E25D8A31A9BAD39BDF31FD0D1790707 +9DE6A078E8A409D8295F642DF492AC4F86AC84383B0F4C6BAA7C22AD5A898A71 +D6CB34D2CD12266C486B75E75A69C14819DD9BB8159088E04D4717E576B8482D +BDA52110AC8B8A80E4E9D58F470EEBD3CF44A1E1EE8DA318FFF3611B02534FC9 +F4018C7C57E80570D2F634D98BE5D5EC6D95051157F0EA94A3D12BE0B4B79939 +F82F8D73136D3337C44E314B0B16CB030D9A12E01FB667105F334C3EE965E5A3 +D410D2F1531547A4497C355AEEB295CD3C5334BEE5232992960B757594B89F3E +52095042DBE6B4DA3C3AD50CA95EA9EBADA10630B500CF1FCCA7D60306743681 +7E428D33B7F7C40B425CD58E4CD8AB474BCE6A307BC6C6EBC15A8A96E0E2977E +A33389154536F5C5D8CF036D07F24094E779E5ACBE5502C92892F10F4C6DB627 +C7EC4C7BF20B39418A8A85D7FD9B0EAAFD871DDD41F93BDE5FE619AFB8711824 +DE890E62C1969A6FE28DD3578AF43D58A728FAFF0B9FAA640962C8F35A26F76C +67F3548D6DB54A25CEB368B47F97EA2B0C4D7C0E7894A4F0C823C6C1922CF9DC +10E05600556F1C7C9AFB33A2DB6F8730F70D6BF94B1FB0887451F2FFEEF3584F +DFADCFA9A2D4846B8F0E51620E1327D994CDF973B837D10C90FF76DE22B47CD5 +EE3183898D156861AB4DFAD34A1E3FA260B8164E6680BF58413A553E88F6100B +C4F4E8E972C81A5F88A7DBCDC308B4C3581BCDE13877B976B1F84330839FE5CF +C78551620EB803DF94A5C921F8EE24F7EF8FC4C3E1653514212631F54F90E3DC +E9EAF96E998F340C4F729ECF7AB430FDB7C0BE3DF2C0D23015820E28B743CAD7 +7F0AE95413C3EEABBC69E852F53EE1DC260D7F1E712BECEF2F18437DB23D8E74 +2902AAFBC733AC5BAA452DD6F3671859AD836C8564E99CDC4183D8495AFD99D6 +1F0D65B6588CE7546717911E25BDCA6C2649E3A7466A3E2DA7C7994A30AB4449 +672EFD00632EFA8629C1AFB7D53D801028F77C864869FE636213A69173003EA6 +BE1ABA95EB07B13D1594BEFCC95ECB0A9CFA9892EE0677D6B6C250855762B7A7 +8E4E022640F93169DFA0303A0D5E73BF3E0F4D4AAD10FD7E4EB20532BA30371F +E9F480F9513432946F9828AFB5D4AEAFA5829B2CB544E5EB634C4537EF7DF08A +A1CFD94A52DCF0E7CE4C5EFFB01E6D50558B75DB4C8D5512B06080F27BE62E01 +2EEA6A0357441401458C842D3DD4C35B8F561D816B336216CE0C14BF77648AF5 +E33912CF95872A1E1AB9A18980A0B29A881D13397C15E1CBA5D3E0B27943EBE2 +F3003D15EB446BCFC1C231832475D5B7AA19E4CFDE119D6CD62D053C6D29C333 +5F729791D17B3F7108074EEF4D1BD101CB33E01004532CB0D716D2E54D169C6E +80163E70C0E9081F31A1ECBAE079D2A518B790B0CB2CD03DFD034A0F4788E800 +B0CD2DC1FAFDD487C2F381EBAB2A2F3F3AF82021B211DC9CD2FBA6A1BB3D4AEA +4C7F3D9A5C21DFF284CCB827D205A69638E98D5DD8E36AFC1A4481B5CB2A2E8F +D6C838DA6F81990F5ED928DC7457501B5C979FF4CD20A830896A460C5DB13D56 +A3B2B5D9B292374A9BF392894DD99FCD6A1E655AB395E839F074D1596488700C +4E2891C8AEEF66568E82A8B826F9A28FF84D4D9BDA21F638EAF96880B4EBE0D8 +081982F34831A03BEE81FC177700C2360D2A48915EC40D5FE85B400E175D5AF1 +067FA0097904FB647757BB44B4042D30D1557BD0F7922D731142FD682139CEB7 +58CA4C8C240A0B86B1888CACC507E24E04020BF1882BD9B4CAECFA97DB24D7F5 +AD64C69454027F198BA35881B94EE9159A2D73E450C3BDAED66B886D6DEBC84B +653E165176228F88993F12A170775A8D7038BDF2FE8DC1F7B98BDC02D1E6686E +9B834F6C0AD90780B17DFE25F0A4E470CBA84E73F2D22BEE09A040F14CFA2C14 +0FDA5A5149B5FAFFE49F55EEFC43831BC43A8326FEE9C7F469C0FC3B000884FA +41DA7318EB57262CB96FC4EC7F16CA07FE1C3BE8C2DBC8A8135953D6DDF20BDF +75A2B6D26074FCE752BD32FB9F5CA797775E8DB9BB9786B469A3CD65A0D9DDDA +C2A166E454A94860EEF5B5C12172DDFC576A03F6E6F8A735FF21A3E9CCB4CAA1 +3064893487697986A42CB5888B2B0A79FA3C74E8187BDDF7BEAB884B70B8D4AA +AC6615745AEB906E08BF831CFDE222F58D02B428D55E9D5A3CDE74E42D8A2CB7 +E1A3A9439B678AD438793ABBEB72B21C58981DAF3EDCE4BB93D95F4A1E943BBC +B3A012DE92FED4F232A3A7D60CE60B605151F9C7C18A5C653E5D6D15E5B49A63 +73E7A339504D0ACC74B8B116EA88C3EBA2CC631AAB29F761E5F062966AD2FD28 +7FFE52FA8A115DBE23E471094FFB3CBAFBDF11B7E9058313F2D069B2CE98A962 +64645738F02A31E2F2AC11628724034ADBCEE012721EBF0A567893411F950410 +B20754A7510D041FFA6144AC9CC46D846B82581F20BBD001D34D9764010824BE +61C30D05E5C5D100A24F1917F01799CF5BC4E50FCECFEA732CB50196825F0E08 +8A1EC868C6D4357857EE2957E081A0E4372E31A8ABEF23C3F2EA0FEE57DE4D08 +61C570175C41AA0C7A3A579ADF593F18B4AE3782D2552E4E0759C32E059EE741 +2D8191E381731769F6648B3581CAF11DAE46471896666F18F02918B0860BDA3C +BD5DE777672447C23C62ACFC2611ED5239D6A266FDA6031EBC5A530C1A2FF7A6 +B4380B9A4C877267854AD1F1677CB5433F28894ADF93D39EAB94541A8D232E08 +22D082D0951A60F62B87DC028714EC74133A4D65F7D0D1296C0E189C4A42AA98 +28E8AE7ECBB9FC8DFABCC6EEB1E9FB06227F90808EF31331CCC5D4C9A6182181 +047902DC9FD0444FB94B60FC74F3B677758088CE6A159D940C5CF682335E756A +8BACF06AD7225D49B0002392C889B0FE2C71311D2596F4903D12FA20BA2FFE25 +A0804B4BC282929BE31E0F46B34532CB5795A65218CFAE21F390792DA67775C7 +B91A2BF4C16DE4F6551DAE3A5827F616BE9040EE6B1008DA2F99A01EF66D697A +6CD1A44E0A15D1F39EA8025E886A68A1E9C334327C7703EE721E497CA924AC90 +7723106D913C5ED4BA4FC743CEA8D0F5172526107DA65775C0B1B77179D336C2 +9B09B608D80B1A1E87CA1A84A833A00D980D919BFF56F6390E9D5B45E9935CF5 +E69D003564462F750F7DCE02DC23CC215A0696B74D8BD3156A392A94F557655E +00BFAA035647568ED66157FACC585E411F7F428569C147DC43F6E4FDE693D0F3 +9917BEFEDF61FB980B85515FF6424824E2D995B05CA1E5D3E8BD8D3281DB7CE4 +E54923E84058FFC0A8A2C491327D0F87CE4C352B724167CEE224DABA3B95757E +4A419594BE4F92E78BA6D35D4C93D31ECC3134B24A45DC32445725BB044F09A3 +AA8C31EFC0A2944ACE2F2CE054CF24DB350FB3C71115518C24BDC0F7E54250AF +9D3378D38480E1CB9029F31570C619A28F065CA4FED5665EDB96712ABEB33B9B +4232C00C1B0215F08D53F7E430887035AC25BEAF06942FD1B6C442253C887AB7 +D694C1A6115C8990B4CAF1E81DD1FDDD6B03C00055BE956BE7FD8A4E1049AE69 +EDA8593CBA8C4A41E046C689FBBF9F1B64E5856A7FB1C61EC815A56DE2A8ED33 +41F370B8203D4E5B19C63AE9E6E0D26F4F3814B5AF48AD30EC9B8402C941FDD9 +722FCAFC638FBB835F83DC77F93D367266FA7DFFFCB567EF82B1695AB4D94D09 +B18AC041811027229DF431F5CB2BBF6ACCE9D500C8F075A74590641C1A607C56 +D2B8624797BCD9C91C3177818691FBB4744EDB6056464A0B95B8D63F7C22309B +82D6126E2057BCC9FE5566D96B7A9B201A09B0D3252A5494C8CA2C8BA8A13C29 +37EF2A882D61DA708C279F663D88A8E2999A0F3B6F98C49901A7631BF7708B67 +54D0B4C52BF4BE0DA0439E6763A7C9D639AD4092E77B13D3510DAE1475C978AC +796F9B2AAD3BFF35C5A3E19B5E2BF704B3BBDF68CE48BA4FA2496D60E58888EA +28AE12D00E9F0816FAC190590A865BB58569A91BF0345D01230ABA361442006D +BA2C90EC2036BBAB79EBAFC3F217DBD5854C519235F9627A1C3C71D21ED38AEF +0BB40F3B86BB9F09A3F309473D8757AB7E638DC1C59A7F9BCD49DE4107A2E54F +422767FB94048987847205584309397F554744690ACFFDF5902FE5DB355930B8 +71863217830DD7A563B0B3A4025ACE75B0E777B4414B62A13B50C54E0E6D47E9 +D43BF769B9411B74E1069BF71BA873B4B8973EC9BA492A5DEA58D267872BB246 +10AA67B143D0E2223FFB4991E583E629413CC894C3FA4869B72D19CE1A0CEC8C +0FF5E5A3EC1FCB7D3C4289813F0D249A11B55104BD60B2A89BEF44CC77CCDA9A +065B8B83B4F4253AA1D535290DCFAA4773452D110D2B3370F9E2FE5432B54A9E +644EB3BA9BFF62347F376839024CD5EF3C5DFD30F412DD5474B7933E6A1AB63B +4B12F2417C72D0543C26A263AEA53E5BAEBD67E23553A72E949DEC556BEB5D09 +C4D7A89B14FE4EC68D0E3E9D65A64B285E53590F418EDA8175113CA375A29930 +DDCF4C71ABB26CEB800C2C2B253AC1F53651C88A56ABE5A74F3B54CB4FFDDB92 +60AD7272BA25EC2F6FB759AA6E1E7964FB55AD09F4EB25DE45FD01833947BD05 +6266AA8ABB7DD792941C7A070FCF3A4636FBF8921C70298D42FE92F079DBA2AD +6149D9CF9EF7264DE6DFCD4429949B15EA90B596340713BD61926DDB2BB23BE8 +F9DE38A31620A817420A245946E551463960A8C5C7295E3B3D6A59BCDF5E472A +40B7A2CDDAA43CD8AAFC411D037142579D11054A903E102DF0D0C7B5BB854DBA +F3F086AF991F7F5D5C730F8F9AF213F25786F3EC0E54530FF912F4876FDE16B6 +A07D0DC4FC46EC6363BCB68B83ACC448B801EC43FDD2F8BE0E93D809FF81E38E +176AE17C67C85FEA58EC95435434C49A950AA955D8B20989C550AB1F1C31B7FF +99422E1F48FB7D6F327C6DBC4695A03903DB275B94CB39386E46579271870A25 +21823E75C377E9D5B46655E8CD8F986372CF8BA846423E26582315A9D19E0BF5 +305C32B2A0EAC3ECB275B1D8BE11A37ADF524944219D94EA2C5DBDA768828B6D +775DA8CDB09E0570E4ADDF462EFD8D3FA3F86B1DEECDFFB699AF6507257C1879 +16FC615868C2D51F03CD57BA38D42995D9164B257441210084DC409B6EE4C119 +0B2E17B0A8D5326DD0010E4A325D5F77BF935693BC90A00A28C7B5F74817DA39 +F47A41E32F4F92AA04D30D810F7B1484EB53AD8CFC8CE8928B570314E0F713F8 +AF127227190F9C16BB73D2A217FF801C391A29095DA5E4974D137A0CAA7DE702 +E20DD4755B1D78739756A5E7EC3542B96AD6844199FFA2F5F2E9C64E2DA4FB2A +ED79869F745C59D235438251BC2E6D26112AAED20E06021D1AB896EE1F1DD2EB +437FBD4A25E42245C5A647493FCC9922E6DD7AF57D5D482921D1CBD6F0F02949 +C27777144751C1E72F4EE2BC343D4AE7A8A8758123B54FB1A026144C643651EF +0907A376945E19A8FC7F98A034832A5820A481B0823F980F59623E0511593FEA +BDE6EFBCC0383242CBD4954027B075B21F10472059A480D6E5ED01C3B07461CE +9810251A5C5643EC7403130C2246E8616CEA25EAC7A0076731FEA8CC43BCE3BE +933FCE61067F5FD402E67E2B9DAD954AA77C5BC86BC5E4BCE2ED676D8D8EC7D0 +ABC5C86D82180B9D5D7451C71B5149B6B67883578DE9909317928C0A92E3205E +F23015400A1763A6FBF67FDE3318AD2696685A1832FC31CF38589EBC7CA1C818 +60D2B2211E04EFCCEA88D9A9082E82951EEB123924A267CB03C48889032F2892 +4227E217FA28F87E01CBF27BF1EA60641A4238258CB7AA355908FE36D90F5CAD +FE992D03A33E47CA9AEBEFDA57793F39DC6A9E85D5B289F6B862B35DBCF82E43 +5CD6A862F6FFAC36478C384C3BDB0148CB1FEDF55969C776E77917635B5A65EB +F2AD351D21CD3822D43289FE8EB0FED58182997097C7E9F4373553AE1CA92083 +EDE3BBE6C3BC7009D15AB5FEC6A59E9FD1BCC7B2099CA15FEF083B9CBF7B890E +CDDDE6BA0AFF306C76500C945DC91BD533FF9A585CEEDEF79238C54E6168001E +26FEB29E523EE501BFA4F60B782B1499B07084C35A2434B4D29D3D8E2C8F945F +A9922443B68D07DF7EAA1F4CDEFFC438B597D8943E231B5216808A85F30EDC81 +9DF5DD22F54A45335B4C2203887475F39D247F0E7347BACFEAF220ED82F9263A +6488E73C1910023E505FDEB143006C1A351D441AC57F9D52D2C6D63D78C75605 +999885676BBBAD56074298E0BFDACBA1830BA58E87F436CC670EE8EB1870154D +72DDBBF3794F8CAAA3F1E11DE29752DD99EAC695838A19BB67A1FA3829B6E0BC +5301610A0351AAA749F456AE31ADD87D6ABADCDD1FB3CE81C3713F48780DF407 +530CB284B2AC709F52EE7AD647DEF9FA4D2A867CCEF728F3D40CF34C28D21527 +10160B3DAFB5FE16AFC9D36C6EC4021FC189005862082BEA60AC72B63AD27D72 +FAF3C2D89DA2648FC4C65104A069212D87144E8533CD86A6D73DC7CD9DBA25CE +7DA53B000266F3871B24663C77723703315C5E4A89DFCDBAB384AE7EB2F455AE +AB191FED406F7F6EC9E5B8276EF5C4CBA041AC7E8BCEC7CAE840154BDCA3232F +15711ABD1E867A434E9787CA0A6D1F197597DA27ED2402CB2D84ED082E8D3A39 +81E6EB270DCA4E7A90E2BEBD3CBB3A2BE3CAB926192D7292CC16845B6399A543 +BCFD224BB52F21352732DB5154FA3442733066CDC3E186D8AA97CD801DFBE43A +116C86889BE198DA88CA978B8C40ACB67E8F7BA499DE68A6FF0DC72C3D00BA1A +B378B39610F15CA026F95ED8155CE3FFFFA2E2FEB352DBE14CEE1669F2387B70 +55B91185FBBED764266215D518716EDA3DFC9E5DB6B148A553E75AE5E38E1CFC +6EF47B314D54CF24BC13856F4F7C976BB91D143DE32FF49BFFC87E17885A1893 +BA1B8E441B08EFC04F7D103C1FFBB665194B3D0920473740C55FB1C50EBCF717 +A2359B687FCEAD65616EE89A68F8D91AFACAA0B238EE4AF0279AF5BE5294C3DE +A7E1F5E6248C0210E7D40683F04B12A933C746ECB517CF94BBCC6E4CF49AC715 +D8005AFECBDFB7A6B417DB8A28F8E9EAF39CEC1CA64DF37A5E66A76C26F721F8 +A63B003A040A62F87DCF61B298F960D510BEFA453F118E59E7DE8CA3DD002EF0 +127EAF733D5C61B5132348D280F84D159809CC71A3C6F7373BBFD8D6EF715D34 +0016DEFF14AA5F960BF1BB9AC304A1823722843547BB4CA5EA4C41C6C2701C8F +7BDC810443F9DF34BA469A3260009B799871BAF8523C8763544DCD0B382D44C5 +F75046AFF85F0B5A3188C2EE786CEEE5496A5AF4BCB0B429CAFC403FB983EFE3 +61FD9F52ADFC38E07A0FD7BACBA530D2E4DAB2592AA9564843E7E2305047F060 +C5FE4243FA8FDF1B5D4F61ACA7850A604FBC6D6970959752695C90F78961B4E2 +C8CFA41082B1A37405AABCEE5BA3DC2B9EA76F486117B84728EC6D8AE6379CCB +402C2AA89078EC992C00D53151E9D82C65643F549A572A20F05107A41BE5AC57 +8EDE92AE20B05E2D0C98151CC92D5389A675DFE39DF546A33A84A4C534337ADE +B17C34E09145B37CE1EB1D10D42CC8D6E6B127A3809F7202381FDB88D42084CD +0AEAEB8A8288CB56870EA2BE9D0B9DC8291021CA561E2BA388DA3494E433E0EE +5E69DA51D0AC505C9F71562D3E9750F23CF14D2C8ECF0692FBBCB4A92B48B4B0 +AA2163A2EA883B9272CE7F3630C6CD3865D78C49FB54B677E4E09581B1991D83 +972DECD70AA818D27095A01A1645ADBCD7A5C6BA0828C09A2136D215BD19ED26 +9654807E60D2CACFD440352DF3805E35F8E16D7BE240671D2714A9A870526855 +AE1913A0A914E19B73C1970203ADFC23A8968413067A158380172ABEF962CC52 +5D735B435A0404CCEB570936F54ADF38BA30FA263B3FF106392435C95131F1E2 +1DC1E374FE78B21FAA46D29F5AAC759BD400C843E221718C4EE130D50B8D87A6 +26E1B97D88E1536B3BC263484034C155E5D6F9D1BEB05C198B29D3043EA2FC61 +84BF86DA4F9066618F15A6C0751FCD68BCC17B918ABED0830B0C98F8ACF58610 +0D83491E05675346195B8276AF6FDFBC78D0131E3FB8443829E2D9886E0F12EA +DE094AA2EC7E8A9A7084B7876192BEE3940BDEB199E6C10DC536B22B32F3BB5B +1C2BA05E38BAC59991DB98ECB101A40DC768D5535E585FDE1BE847828B0447AE +C0E610F07775F492D53CBF9B4EED354B844D252E48AD3A48E22274196903F418 +FE5CB587CBF33C0C7A94C97800BC038FF9D303EB4EFFBBB3D9D7A8497AFFCA78 +25590E71928565A72C86CD863B7F1304913A38B77A912C5226E868190E120980 +88129DD2ED8E0D57B09FC0D75AD6696746A3F59BAB0A8D7273CAA84D88BEE20A +113C2751677D24BEB7F342952EF38480B01FACDF23F5708C01AE2087520DFFC7 +622CC32CF8A4E17CF052CAC90C3E5747091012780EC9AF4EBF243FE28CA5C82B +B9063700DA3B396B1981C1DAB3C8798A6F88C0D71B8F4AB36D5C4AB2F2B4B333 +0EF21A802562B464EBDE5199AC7F3071117206F9939EC14224035AA41DF92482 +5AEFF299796BC14771306DA6FD140974D2E0A851B35775F0E91C47 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMR10 +%!PS-AdobeFont-1.0: CMR10 003.002 +%%Title: CMR10 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMR10. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup +/UniqueID get 5000793 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMR10 def +/FontBBox {-40 -250 1009 750 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMR10.) readonly def +/FullName (CMR10) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle 0 def +/isFixedPitch false def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 11 /ff put +dup 12 /fi put +dup 34 /quotedblright put +dup 39 /quoteright put +dup 40 /parenleft put +dup 41 /parenright put +dup 43 /plus put +dup 44 /comma put +dup 45 /hyphen put +dup 46 /period put +dup 48 /zero put +dup 49 /one put +dup 50 /two put +dup 51 /three put +dup 52 /four put +dup 53 /five put +dup 54 /six put +dup 55 /seven put +dup 56 /eight put +dup 57 /nine put +dup 58 /colon put +dup 59 /semicolon put +dup 61 /equal put +dup 63 /question put +dup 66 /B put +dup 68 /D put +dup 69 /E put +dup 70 /F put +dup 72 /H put +dup 73 /I put +dup 74 /J put +dup 76 /L put +dup 77 /M put +dup 78 /N put +dup 79 /O put +dup 82 /R put +dup 83 /S put +dup 84 /T put +dup 85 /U put +dup 87 /W put +dup 88 /X put +dup 91 /bracketleft put +dup 92 /quotedblleft put +dup 93 /bracketright put +dup 96 /quoteleft put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 100 /d put +dup 101 /e put +dup 102 /f put +dup 103 /g put +dup 104 /h put +dup 105 /i put +dup 106 /j put +dup 107 /k put +dup 108 /l put +dup 109 /m put +dup 110 /n put +dup 111 /o put +dup 112 /p put +dup 113 /q put +dup 114 /r put +dup 115 /s put +dup 116 /t put +dup 117 /u put +dup 118 /v put +dup 119 /w put +dup 120 /x put +dup 121 /y put +dup 122 /z put +dup 124 /emdash put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA +0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93 +51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71 +7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551 +E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078 +0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273 +C01924195A181D03F5054A93B71E5065F8D92FE23794D2DB9B8591E5F01442D8 +569672CF86B91C3F79C5DDC97C190EE0082814A5B5A2A5E77C790F087E729079 +24A5AC880DDED58334DD5E8DC6A0B2BD4F04B17334A74BF8FF5D88B7B678A04A +2255C050CB39A389106B0C672A1912AFA86A49EFD02E61E6509E50EE35E67944 +8FC63D91C3D2794B49A0C2993832BC4CDC8F7BD7575AD61BCDF42E2E421AA93E +3FF9E4FAD980256D8B377043A07FC75D6169338028692CCA8CD1FE92FD60AD26 +D57B7519B80A8F8DCE9CEE5CDF720AF268D3C14099498A843D76E3B6C0328F24 +D36EFE7F5C4E5B5C612786200C8DE3A41EE5F1FFAF4097653CFCDC8F4FD32E0B +03EDB3E413283B9EFB0AC33B055617005BC9B0057FD68C52D1B0E67F0C571685 +767F2AA85ADE4E0104A1C777733D5E318A22A9944336E5B98D965E50D31F357A +8B6EA5A0EA98E1B027CE68C2EDB149EDDD04ED74A1B3D206D471A0C11C11449B +DE190BBFEBC08C9E1B7513B43DA3134D6B11A2516E6E86B67F68C970A320D05E +94FEC57FB347606DF89989C33482BD09D011C55AA920319E7B26A205D3D0F004 +22466F09C0482A164CFB27EF6ED2B040ECCC3DCAF345B5A73676F193D43123B7 +72FD6CFC5E37930E61EBD5A6307E4DE70194E6384EC0D79DB6AD86D3B319A31C +8B0589D0FE28241D8ACE280D0530EE99C80723E560BB72AE9D53F4713181F491 +344B06D3027BA4E9E94D4305BE1D817197C54C8FF56CD6964165F6448ECC8A8A +64B48B4F0FD69299A137589E2491A283509B21A3A5772F75B7602A9F60AE559B +07A58436D04222C73EAEA72DE9A5A441F88D27C11F4F91255EFE280E91A4ACAC +1E98A4E5E6C57B9AE86FD218C3CD8F24A4104156A80F13821384E529783C52C8 +78B94AB3A0096090867ED32E8A30980E737922037F75F062BD83BF4F5929BC51 +CC22AEE2DBBAAA001CFFBFF41D258424FAD888FFF1BEAB796A44E3126159E120 +7E4025C676CF94888A1971AEF8B6764B3AF4A92D36FAF6FC56FD049710EE3782 +BC2CD84FE2473F133BE03C1346B875463F126DCAB15C7A9BCC9A727D23611462 +4E8D2BFD2466600285D79518712B8681ABCD69608E6AA9578F7BD771EC36E01A +5A17BC17E375020ECA59B43790ABEB9DF5F4FBBEF807E5699EFEAC563E1ACC5D +EFA336E75DE6D8248E9381BB110884FDC89C2F9A41EBBC9A8A1F98E6A41F68BE +EE30E25CA148C1EFF42DFF8C214A6537AB11F260B8C329A4947B5FC8DC9C5622 +4DF7BF4FBFB00380D47BABB03BC30627AA74103E553F55278F538EDD8C1E64CE +0F1398CA0AB5A86630139B4A7E8FC02804CAFF3830114640AE50D2FDA3B561B5 +C63AD7EE3347804CBB40FB1E77A6C89735DD870351C3A1811591AB493251B904 +314F65791963C0412377C1D02362C5E9655F1C3D4803CD379A8EF24C48218C2E +DF1165840462BF37DDE1B8D5FF09FA2C3B261E2F1A65ECFBE5D4EAD43B52C029 +EEB3948CB8A252CBAF545C8FA1C31E920E23A12DD7222CEF2D2A513BD758EA13 +DA33BF5FBF1D734653EB83DA2D374A5B9A0CE316F24EE375D6DF6BDA49954C2E +DB25A88821193636119D469BA66E5DAA9C92520FD4F84426A4E54273FA469084 +7517817A6EE3E21176D333825E88046F50B3CF6938AF9BA79A2F51398239EB91 +1A2D07F7FCD948427FF62F40FF95E39FE1A1AA8451411563FD5388472251C155 +69BDE9283B41900B21EB1190D06E6B13B7794FED020D2C1BDD205AE77B084BCE +EF628249398B496DE85B406FC2E1939EF00DFC84C07E26CF72EC401BAAE756E5 +7F6673216E7560D1C2A723CB405EE5CA474A07F61B81F8836482F73DC9516D67 +CE0CB770EAD755B6B356198B4B97EBB29C63456953270CCC8D5650C1D006E69D +38DE2DFEAB27DAD50A817F0D645D30AF5B75A7B53CBD3D2B8D87BD0A7E525AF3 +22F7ADDFCE31716914C2318260C2E2B4664893921B68C5A93334A361D94A759C +0D7B146D6FD94F0442D672BDA0F6432E18F3C5DFA37ADA378D95B75F413C9ED1 +BB5C606A3EC7DFB3F796F59B0478C13FD1900381EFE0BB5242D5B5D34D03AF1D +4BDC93EAF8020E26CA23C8B0E7DDEBBC6762A557067A4CE05A524188A8F02E2F +3625DA38DFCF381727887F5646A3995A8A38A5FB1E5D5EBB395FDD0B7C8E71AD +B48EEDB62AB2CE99D121435EFBBFCEEA69AE9ED8238B60CC7288DE33C766CDFE +15B767B4AE2E6CE0965E77272AC9F86023DA620548CFAC85BC751C44218A29C9 +849F1C2DCBDFAD895B54E51A569952ED50F82DC8A19F367E7E44643854EFD6B3 +FCAEB04E55E4661C82D31E2932611748480EF61FB2FBFB0CFB940BEA81AFCD84 +4C6A6332D7A600170E38A8EAFCD4F93DC153C43175434C86BC747348FAC61B76 +1FEC9027C1A193E55C80F1F20B5317AA0A05AAA36AE235F6E49F06E570FEE798 +84857D7552EA92EF3EFAD52DE39C2F8F43C59E3A957B7B926FC95FC4B60186DF +7F3523EE2AB74E294C8C4BCD8B4975E84849E0FBDA6C0B0F24A636DFA578B122 +CF97BC5089E21E9F5298D1C9F30CB8BAFF6A3A11BB4D9A0A5CF2B18D055C44CA +4FD4D8FE1AF3630907DE7E585AA811F9CD11FB2C8FC791851D651009FA5DF20B +3C33FD2FF848A9E3F5652BD294965A332DD3F246C91B0ADA34017FF2451D1394 +F9C3C95AAC6EC8062BE98E8914D51DA6A164AD13938693D446044859D03A949D +F9AC5DF4A000CDA98BB516D762CB9F6D44B5268FD0C26E88BC4A760C0F75A140 +DEBDECA4F511128B7D2805872160C55236F0A0FA7637FF0D4E94AC079CD3C8A7 +D03A5A56F26B0438B577C46011A10532FEBCAD14FBD6032E224F45691A726886 +56F305231EB2FCDF59C8BBFCB5DBD2D093A0E84D62AC93A2312CA69295E937C4 +8DBA1802B85F54B5E7E6D6216A918F911FF705D3B5CF055F1D873B96283A0B53 +59344D910CD396D883F6F7836BA65FAB4393A773A8F6BC298069E5BA38210EED +49C9D920F718E3FCE692527DC7CCE6963BF744F2C91BC5952564196D60574E86 +87A0FAB21F2DB2BD5A51D7FBD8FC19946D24E5A228462C4772F978E650ADCE3B +8D66B9C21279C531CA1C3A8ECE3420BB65837287A7222CC3673A2A5F8BBFDB60 +C719CD073EF9A23675198462C7C87B24CC92D6AEE5C25AC63855CC3281494342 +D28F3D2FDE0C183486769A4FD5B0143193D31FCB2C2A14E487BBD96D0BADBB64 +D1B56021C363A795BF10E2DB448261C363A54A4AC1182B470C457AA82DF3F5D1 +F4B329806141EBD53CAE309319B94133D7EBDC2D0453A905ADD207364371E178 +0A95C2686E3B34C4A978BFC0EE968C39ABA00889BC5149162C2B54483D44FD3B +5CFF41F611C7E03B94945F414560E874D7CF27FFD0630890D7D7EA66CBD15448 +229059E1C436BB33D69552B5367AB5D53591C4678D0C704DD3EA23F5D9E8A7AC +17D003C19E333E726FFFA2961F33C70F429085F7BFE3E2510F59B78F58B19CB4 +01B48E184BAD9020FECCE3AF52048A056981DAEA02AE78197E65855DDB170616 +F54278395D9EA50DC83761AE759F9CDEF9E1948E7002414FC05286ED793E6662 +3347F2A9AF8917493D7305B92CF93E8E9185F70015F5594084298A6C2F9FD3C0 +689F262AC9FEDC9B89577ECDE92F08D3142209FBCE7B5C0A840CC767BCA56C20 +4E4E545E2BE4D21C53855CEE4CD0AB35D1A604C0FFFF77DBAE4289752276559F +A05FEE65F45ECAF44E95E23FAB6052195C7948AF0B1126482D4E02D72BF8AB03 +DE0F1A632F7672AD9DDE70EDC82AA993678A82BEAD0BC2649C4707FD8509810D +364B5C6FE0E10772E95288C622C2F06C634F4DF8C7FD1432BC9310D5F24FEE3F +7AB324863D6DABAA1576E70643CA79EF4D7DF4105093D66CEE0F3B87D2164A7F +26EA05F5C4645B22D3E1BFD2219657712C168FD90DE801FB0F32759E80DEC1E1 +43CEEB19FED12D757205043FC98FEC62D6A8D8B97BC083B4A0E985AF7850D6FD +8716B9957C1C35A0675BC53DF672C425C79F43FDABAEE7D63F092CF271C9A9D7 +C41F40C4189510987887942E60A412B3EEC84C9A6E1AC7D54D528F5604B72C08 +94B7882621A5BF1F325B92FF96B80878CC550D1AE4D8196E41CB1251856609A5 +C4D3BD05A922D0D45E039D9450DEF8490A3E924E41434194910BF60BA1B08BE1 +B41824345627745541A4F1703E956328F6227D11C74946B38CFB096139979E56 +4E723B889B44C6D78673868C89912F8B4F0B4B485F1587A637B630F92E6072D5 +7F3B44EA6FD96BBD4FC28A6C1D90805E3BE3E42A7BC9C880762966C55BC04E01 +204D083AE976FAE6F37C94F27E68F8C0F28D52B17F6C0FD7C9150701FD78F8CE +B8E8DC9260E3974005EB5CA728171F482D765016C94D4ADFE4A42EF42212BC56 +7E4EEEE8B0D2A7856CD4E44F55C0BAB762F92CB8D64C17022D4BF3A47C12F5E6 +279FC23101FEE93753653CE8CEDC3B75C9CCB29BF1D4554C6120DE8EE750FCBB +E38B5D915206974962E320362E59B3F21B3AB1875703191043D03284D4467346 +CFF2F98CEB4845B73ED8E003E0DC94251B73E13A9B51A3F1430BCF6A21EB9B7A +65E17FA411F53BE6432F1506232B8159E008FA257F884A4A01AC53BE91754D78 +BF14A5B0FBFB9C31BF4908355F8A762052968DF526D118708CCB0B7CB5BEE285 +6DAB6CD2E3934178E60BECB11AAB5478623CF6C50C92F8BB5D1A583609028FA7 +B8A53B791BDC9EF76A124F3F7641857E4BEA0837CB36176EC9A522EA7F41B8D3 +63C37D1145367BD300F17B54522A834BBB74DE12BF9EB26ACE6F24A046D58F89 +4D4B7DF74875F1A0C1C9D97BE0849593D7B398EB4B00BEBC8C8D1497B6EF831A +A35380FFB7F1AFA4D888AA52C9482E8B1755CC209905F98F40D95B44D4DCBCB6 +67423D1BC2F3560FF0A8B4F0CAC352A4EE2C1D946E45AAEC8A6AD40303F3382C +DF0756BFA3B1ED64C169E56ED1C760F2FF0E24DC5C9F41306EF8D2628153D30A +5DCB0791126BEFD4947D7EF08301FE015F2B0008DFFCBF9F2D4D859FD43EC7D9 +C5BE237E9BF6665B7B1BEBB362F0C0C3A8D86010B9C97FA741C97C2E0513386C +9C26C235B14DD2A58BFDAC7B5F63DB4DA6D5D37D0098175A9071590E1DF66A3D +B8173A047C29D7D35557F06132CC920B5460B8AFC11D23D09A4E45D089F5EB51 +963FA1A6256E359D485107FD143B2BF21FDE9DA5744BC2615E86C31C89470CF0 +D06C6397D9FCCB316EA9989430240759D2C4945D941F159FC02327F34B042BAB +B5C3A47C78E8C1A6FBCD396B1A51CC4B020B8AD401841EDABACECDB482D6EC5B +72D2BFEB4556720FADD49D07307C8B22ACB7E310CA4151A85C71EEF70E8D15DE +B3B00F26E0E166C14647A65ADA228A3D1C89025BE059306565DB1B1EFC37D358 +8C1EB024254AFD049BA977BD4C2C605050E17940A89D0D4C5D963E792320F5DB +3706682E03D25D9E02487247819551465092CC22B6B56E93F3AB528038FEC3F0 +668F866707A19B0463BE706EC729D2EE1653AAC7E29BD25BFB3241D4792F5152 +ED415B4E7FA92C2EE5A22E27E8B75542C492E56D811C192E95542A6FE0BFE5A5 +69273C2ABED4300D491B92D2AECDD278404CB84B1BB1BD7AFEC858215837D118 +C0E928BE7E07CFEEB51A6D21375B772B8248C994564014015232A0DA4BEA1754 +3274F407FED0837A236371F1A32056240F2015B1E7F4B2CA72C6B58610A66F13 +407CFFBA5E0A2893C1F572D50F51286E9133B5A84239C9493B0574E77D281D01 +11D00683354A000C9700EAFBC1FD104EA19DFCB87470190E7E2CE26E3A6FD0FF +2620B87B82AC8686B6206B530F17E9348BC7D04B948348802CE53A312443DB87 +4DBBA5313A6A2A8DAB8A1CC9A594FF8C299281C0A261C8CB2226B732FBEEDE40 +2C6ACC74A1A61379E2E1CD5548CD908268A32FA83D8504C442EA0E183ADBF7FF +9FD09C037AB03516ECCA93FF048235BD11A25DB07F164512A079C5392AC7F889 +CE96AE5C8D9580BCAFCC087C35E76EED1A671E87C12E3045E15A687134736DF8 +DA984772AFD189D68571A2ED7256F1E204230E41D3D9DD876F938951714A3973 +0CA9310489F8E807C1C7A4E51AEA5BC030610A5D7263FF7E0F9FDE3E5E37A362 +5B919000BD94D978583B942EB79CF2BEAC33FEBC9A67272EB10865BA8FB75FD7 +9D280AB59F91B96C16C982DE848D76D8FA8620DFD7C80B7DEAE7264350D6FB3A +EF04794DA3305844A7CF718F6D1A4A3AFF6826173A076A1372ABFC54ED3AC6C2 +09C9287FC830556CA694E21CA5342ECA7B10C90AFC4783D841D7B1E34FA3DB7A +2B706F3E21B0FBAB23E7257962FC3BC309CEA2C7239A9D6B44CC96825115ABD2 +AF9A2566D2F3382C01569FBDB94C8D664A5DA0F7DC3DD140CA77C743D7BC1420 +324ECF9E4780280EB119885E96A6C619CE3C0C8E1E264E2DEB137E5DC8149786 +486D65667ECF47B1A1E20E9E6E4FC8323E0BC8E61BDD3BCDFC6575C69C03E31A +EFFC290472CBBD049DE3F840AEE37A2486034240F80E75D8A79E0762377DF660 +52B12EAA16D678990B11A9BFBC03C1D4FCDA9FD4FFBB3E88352438102F10B7C5 +9F04C013B6575B5E948FAB58EA691984A0E54E6B9F3F505FFFEF74D06FA1CDF3 +4B8A95904C8A2763AA8AF5B71D00F5DE09DC1CDF87A08B6D181453063E14C12D +B7BB3775A6E2A901636273D9EEB833EA8CF20FD83AE899E28DADE10EEEC20BD7 +BD93085A4B1AC80AC1AE8280C14767F1A487BD066007A0D050317BD081131A14 +6EA0898ED59E46DA7B6254BDCCBC660686E2EDA0E77A705A653733BB5C5497D0 +B130359F866CF293FB6EF0C2AC5BAA2DB0DED045E2DED3A2612D078333260359 +16CF0CCB272D34767EA069E0F0B0D42327A18529D72E890EDA6195C2688438ED +E9ACDBEED41E81CA8EB5E43C2B09CE266EFCA03F2D7FF57F12B06F9E54FCC6A6 +546676F6FFC5B8B7D3F0982B6FF0D21D949309F0C0B175CC1D0976F8C55C6AED +6E821C39041E22D91AB30922F2B2EC2746BC7DAB484991542FBC82D87B487507 +559AB466F73EE23C2D3194DC5CE4C9AE66D3164613AC5CBB3DB501B64DA7C91B +C7ED2EE9027FC0906820B35D4F2CF66C4F9CE4A884B7C07155BCA884ECA5EB3A +ABB83F84DB1F5639599DC7D3F51241AB5D95C3BCB7AB1EC90B4BC989F74FB354 +04B2D7366A34D335A47B8C00C05CB423482BF6C7970A95545424A08AFF9A035B +7F83F52B65A9799CE76E303B85664B624C65E9CA58184C7BE2BB9D9C86A4DE5A +8165EE3DA2E652B5022EE7893896BABD88931DE1D538F615787645DF5ACBBA0B +A8E5B899A37321AA7D4B283AC9234978C2DD81813A1EE5DB6EC170DAC1B6EF02 +94892635B498765C07A38D2E9DB0B7581B11056C28278F89B0E60998379C07EB +C0EAEDC32AA69B8B836F92A61AFD35688315B2C3F860632FC13E4BDFB63214BC +41CC6859EAB3AC3034449213CAB99FA1D216563419CD6D6CE4E1B56F33E6C654 +7AA9DCB5B05FC068DF02AC32408C8010AD004F6CCA9887830927F8CBCD49CDB5 +18CAC1EAFF815FF2F6F527F936948201565003022C6C7390B4E3C2B219FB4F76 +9F12BD25CA7B3B61D1A2F8DFEE795D04D5428B42FB66E0C254AF7B7A10CEF7FD +E5ADA5E217BE24851180E9A1700FBA66C7D2B0D7BFDE4F4EED1D24B821A40947 +5620363657F6D048E651A689822CF815E72FC8AE9D835BE31D1DD8B54C9A717F +4DC319B4B59AE073936EA40B070524C7E71D5A7B64436DA107749746B516E29F +E3BBCB8F8C473E706670E11E5B221716F315FF097CD1841D0069FA69EA1898FF +9F9EC2518C77806A19730C97F54BEAD604548D553D4A6EDB247853225E24E7E9 +89D71F6BC94DB986467E755CCC99069B313F5745B02B4BB608A39F0A0A732B87 +7EA2DED68219754BF1FBCA350327572D769C962EF9242132D93A5C8E9725D8D3 +AAAEC15ED0F43F9A1290E91C463895BD12E8A831DAD661EF23CB9AD2C26CFB26 +71B72D15270CD8CB1D59CA2D9DE884C96BB923B1EC8208BD7DCEAB0FC2293629 +2AA1F72C6B470701C381E335C2364CFCEBACDD20CA36182CA7E70AFF5B14B02B +75CBD0738E35DAD8E60CB3DA4DDED557FF9ED386DA52D10615E700751C842FBC +85D89C0E713F556814E051C6CEFE7E83C2F379B88A801DDA565BE73013DD56D9 +44D97798564787AE6D5A6A9FB777EEB850C7481B4A353EB9279731512C9B95D3 +2ACA3A0D44585E55AFD968135F6492A9381FA2D126CF50373AB7D47B509AE6F5 +C4B100BE8334ABE2268DAFB7983BF339540A8D8280F3576CDCD4F21408AA7D95 +D50A7C1599E62D4FCAB001D5F9DA84FA530B88F2F8746157E7A60FE8610C87C2 +9592E30AC21163023C7898F2DA2A54906F2221C56AD7A9DF6C2C6A95EBD915BE +E2AE4770EC9BAF986358B661FF1620D17527EF0EC2614EFC317D9A778DB1DD2B +545FF4B9938D2EE9029BD66526CBA334ECDDF677ECD1AC125C7750AB21E30695 +7C52294854F30847A269652851601B1FC9768FFB51F71E20BC676767B2D94549 +A80ED3563868A3DE6E265309F863B934C287ED175678E0F25358166DAFF2D796 +F2E0F86528870DB2BE07044913F0BAAE0F953D5B8A1120F4024D2A720C278345 +70C10B78932757C3682BF74A67FB418B338E79C6B136D0ECB93786E1FD810166 +4C2DFD660D65C6BBE76370700AFCC9BC635A5FAB41D757E1BC4DFD8F20C1887A +7B273A60D565A1FEAC20B3C4C48029F584506640142839B3DBDA5DEB0F755D09 +43B00B3A2FCD87F3A42856BAB4BE44C2FF31014776B3EEF3693145CB14BD2C56 +58C938C2C2489B7067DB31C6F5575564C791F77E215AE12B4AF3CB8D98621983 +A4CA131B6EB15B4271DD9E7B96DF61AE7EAB1C06612200007DE374B7E7CA79C6 +A9DBE8CB7D79D8B639E5A57A779636760C9A55D27FB8FC94F99D4B3778E50274 +88D49AF7FD67872618D9D139C5501915A412A013385761EAA33500D6497B38B5 +81077B9C5A19177BBF30C439E46FAB8D7A009B4DB2FA0DAAACBFEF0A04B27897 +DE463F4C241B9F161DCA2427929E4EA5529ECEB3758E9DFD2D15945CD05B9EF2 +8A272C4C23D8608E72E44379F895D1AD1DCD36CB4D62885608F8D108455510C6 +AB9398D0189A358F18CAC251ADDF3FE3E42C71FE0DB768E1FB8EF40078DBAB97 +125E03ACF2426421F0093C6045DAB1420587CC73A0D514B7D3ACE3A39E29AFA2 +43A214842D1606259688DA0C6F26C51D0AD9C7ED63B8F7759EB99B8A98A04263 +5A55CF2155C5F41C3B07F3C460F00B5229B48D5EA93302FD5FE881F9994498C7 +E9D5E1600A5BD3C596AEF8B61021BC372B7C47A04C7004E4D3B74E0CA329F6D2 +295CFF06365DF060A4893ABFC2682172E2A6D124DBAF4E194DBD94ADD389BFC6 +49DF6EC04984F549F4FF7DCDA5DD9F9B3695368BF85CE0F607A4F4D772BA52D1 +7FC66E06ACCBEBC07AFE7DC3807891D039AECEAC3F500A09579E660C7318D732 +3F9B6752AD3F194F0C8ACCC73F7D8B3CD0113C25175B14083CDDB3ABFE914A9A +EED19BD229B255F7783ED0E15235B3B2C4B38FC9EBCA27F674FFCDA9F5A15540 +9FA23355E3276C61F812E6638F90CF4F43A855888A638140C0F45C6B35F7A256 +EFE0E6ADF8C7F0FE81B63E3EEB4A0DD6688DCC1B6B66FD190AD3F6147CFB5C9A +4A7184FB2F524760C29BA3C89C2AA19CC28F17C34BA4C8AD3C47EFE73881B40E +293796A9A5143464DF9B406DA49C68D134B3DD2B06D394E6C79B375C6994340F +76EB9B0BB959ECE8C805B15CC28A61DEDA9859E0173DE412A2681BE0218448DF +EDF16CF2DA1B7481AAC5DDF82AC3089F2C51AFB51ABAE624EF50E7F23B3A436E +45F5EE5880A5A96C3AE145E3B8A7E61BCC2DA35104BB526053ACC5C0FF6206B2 +953C9350E57657B370295985EF99E3302B40CEB35592BBEB7BC05AE1507D2778 +94E405957E67C432D34DABD05059ACEEC8AEDB587E6A46E7CBA12017897EACAF +AA3DDECEEFA90DBC98EECE835F2F6E5FBF31E2D2931F8CF6627BC3064CE6959C +766387E4A2F56EEF9FB83BA93C278215C47FAB2B41C082A1C20DC98478567A18 +C2DFE4478A73544BBC44CAC862797FC5C2B18DE5F42B0018F834AEDEF448A10F +F12E1781DAE8248A7455393D0D97ABBE2E2E9FCB67D7526CFBD3C7467A15E6BD +BEF0EFC91D679657FB5CEC58F75A0BBD8CEA4AD8DA1FC52F0BC9FFD1E42DFDDC +FA6178FB486D2D2A220C388EBC504889C67669FBA4230BEEEA3E262B509B4CE7 +CB5A55AF22AF3C69EFDDEE428C46AFBF0AA67F5ED49775FBEEFB96FF6D01778C +C5985647EB401680CF16AC19B5BC04DF4D12AC512D1DD5A68A0C3496BCA7687A +16FB1F681EB443F4EB52D4C75A6CA7AB79DFF9245BE238DDAC4B809B80408BB9 +B61C2DDD1F2CAE35374EC47A70CB07838843899C72172DDDB3DA4865E338647E +078F1EF7C777E48060E5CE655612567DA35A999399968B2BEC3A2B111D6B34D2 +C6B10BD404BDBC4B8FB8AAA3591FA9981CA6AAB29CCA2414C2B1A5F8186EF42A +E32E572F6642EA1E449C26554EA066DCD8F1133B9EDA096C2843F871B6C390BE +93C0CB8ADF6E3BDDBCAE30B6296B96C78BD218AB6DFD16AA490F16C1E18A8641 +C7F54804ED676411B1B59CC34939C0BE1F33A0DB7564F097B4A2ED921E31D52D +639073FFF921E16BD26A00ADCB8C1E42A8910C7734E5ECEC6E72A40B22AB73B6 +5CC25F03931C2CFA408F85B4D29AB9C567AE579D727DAD1E967D50AC31CBA670 +0277DE0ED8432147C93CF8F1596A86867D4639287B9957F65713AB604224E142 +F3A043B9421EEFE7E6DA53823342F34F3350A3780BC16E827FE47C41AAC23F2D +D9BF5AC0E79C0B211C49E28ECE61D2B94E0C5EF66E5F42D98E68D617E788B5CA +C2AEAF593E9B59BCE097374D182906892912A95F4B6F698497C8889F77AA93C7 +7D64A0E6B52AF6683CF900534C8CAFB1077E4268598D1D0657BD087F75314904 +09D02BE3018D0C19A4165AC73F32D01DEDBCAA4053F737146AA15FB862F01E69 +17E4EB64ED996274CFB2E5004AA42F8D865B2C35B2875A44EF7DAF6E35501BB9 +AD0A6699F823D28BC89DFA79F106BB7E60303CDBE5BC714C92EC61C534209942 +BCFB67217D04F6A23965F6DC10C0EE79A7012A045FF72BF9FAAEFC95CF03E794 +D875023D6F9A18B3DC87B157B8F3CCFAF2F1EB9A22C914AE451D1748F08D486D +E001FF37A35C1813F56BB1A0E289C7A9694E495F0E14BE41FAB4331BFDA9466C +2612E15C9A68B32DBAED4B5C07434A54C5CA0440A10C2051826470772BD2F655 +410122D1A37119EAD017076FD65E4B00FD203DAF0C11C0552660DA97D3209091 +D8139E740F5AD8FBAC1FDFB0A68E7670C87FEB4412FC80CDBA62F8B74F50BCBA +6E236B1704C8D6D2EBD3E1D3B565C3923F9D7B9BBADC7D610DB32C86FF6F9DBF +0F357951D907D1505553F11ACB57DFC241F8D19D65B9FBA531A8C0372D42FAB8 +C427C2CAD01CFDAAD7B363AAEF5CA4DAFF86201F0F01001D38DB57F959FD977B +DF2844565A6DC515C88CCA391DCC7B08A445684F4A277165A13A1D0CB694E7C8 +C7CB3110415B0408AABAE20A1B96489B78A821417CE269C40120B99667816FAF +2DAF22C825D485A52F6AF342C057523F44EABD3EF7B704F7641BC26DF8EA9F95 +F99CE2D3DFF09825B9E1E1F316EF8F367ECF03F6F5BC7948E4FD982349D6FE77 +0FD2E54A5B4B6A3BC26AC08DA9888699BE9823778291A59372D14545FCCE25C2 +923372C0A870FCD92547EDA25B5E2BBC2F802F0033CB68EFCACFFD2C035D9C06 +B2177120996E76C827664C5AE756C5FBE2DB836CB4A4CEB8A220485DC4D2B13B +7A8C38C2A06CEA17CE26737D36FF31765E5003A98CAEBE7C450BBA19A48A47CF +3212920CAAF3A770152AF6F7C07916C7B7569E6BA4AE0AB2EB0ED5F63317937B +9B17F18B32D0C89C77B79F371440077ADF646EA9D2299BFA0DBB57C313C49E7E +3427B1460681115D615AA45056CF69D59D1090E822EB97AAF4E70942F58DE62D +AE2EAF5159FB19BAE7C86D4649B9442628098D9565A56B696080FA9F6CD4A9C4 +0AB2A4B0E918D19A709B47F75CEC33A0C021E45BE7B53A1DE5101D757346E2C5 +DE26C569001E24261089F5E10980957FD338384130B535D0579D727DAD1E967D +5F815151F9820F0D260B7DEBA7DF72F17F6C4C051E2458227728783073726990 +5CD09517F295E1898623DE34BFB42F0FCCBA55B1F6CBBAC98BDD1EE923FF42C1 +00B4DB7E3E09CEDBCE4EA84E09999380DF3F8385F90897F3FFA02D3F4C699647 +E9F07C3A279C43814EDB2482F7EBEA50E943D9C01EF1C9EFC24137351407FBF9 +68CD67B7D4330C8C8147CE26A24E0C4976ADB5A5CDC35ACEC705FDEBB03C004F +BE28B38F1E68BEBF387FC143B4C9892DF4A062BEA11998D025B544A987BAA117 +D60F48685EEE1AF58852B50FD32B243CB4B55833D01438DCE417472A0B5E268C +7D1B25F8131379A057266489CCAE6F1190BBC0B763C1FEA38E96377C1E4B2817 +754EC8722B1EA719D085F6AFAB86C65B6A8D711C64AB80BF7B342AACEFFE5556 +1063927D13E50DDEDF8CBB58AAB27C30814DA78FF368F112736EC85AE93AE8D4 +C245CF768BAB03DC70130158933F88571D558050D5A88CC53F346D5AB105D5CA +D2E44E849CAFE86506A9C3B59D1F0962E6FC4D8197803D15F7C2717D78854024 +5DF65D66AFDF95F76F5872C8AFBFE191EB944319F4933B2FAAB2060A1915AFE5 +1BCAC814488E736517865BECAAE5A48793207229A24FAA992C1D6200033392BB +DB3D0B723C67D825C49ADD3BB95D63F6DC7072B13822F92578128F4AD2C7BAD0 +FC2E01686E24B05B42FCBCB7B2C1048DBE7BF16D11F1C34AA293C19D89799F7A +C8BB5E75C231F0E1622C9BA10958335ABE90053C9BD61070EC98250D08BCA727 +66F10190E08A2A223755C72742DBAD656C5BB4F116812EEA91BB3D6E443D2CD4 +1875D1A219CA187E5FF1D329A8BC083A45113E80E8D9DF7873E6659F47AC95F9 +91220FFE65073F62299CF20B6063555BE3E334901C00A30394308284A3E244F2 +C42ED3FAE633B429E8F3023F48C5025DE93673C137A935A1B0EF37AE7129C720 +BF4FF1E4452D978AF89056ADA748CB87773840E118EC1DCFD6CA5E9B77999CCF +571915A94E41658421AFFFFB4EC9F94B770FFED4AEF7C46F2061254456019220 +2FF635D58D5D1C28C6B52EEEFDD43CB555CBF65BFE589C091F0606E2FCF87D6D +0EEC6452CE4D506EC6CBD6547412B8D8C4F012B4F092D1E63DBD452980C7AB48 +4DC3A3C8C0D12252FCCBD4EC21F59F44180F6EB8DA2332BA9A5DED80CE64990D +6A97DBB3B72C720CF8E1F30B09B83386CFBB2EF6D8406E19B31EB035C42C067B +096A73E127D5BC58D1A82D057CCC187531FAE16B073626E584546B7850ADCE5B +BDFD0DA309D7581EE6EB2DBD340FDCBD86E40E7804EE590A5DACD4E91FE2D0DD +FC09D7862767B57464C5EF92636705A9C7B3FC252E08BCF3057F888F54CE0DEB +26C91E4E57A1A5273B7CB1A49988E9FAA1EECD9346D3B288DE374D9FB6AEBC14 +B084A2545E15EE0E468FD87DE5ABF4BA02BC6C4C25B66B9479CB897631CA8493 +BB9AFA640614F0A86292BA30D828327C211A616BD0638BC2274A7CCFC1EA6C38 +81667517BD76162E9276D6881DA8999E911F7294CA962A22ADC887BF087209A3 +A5A959B82CC52AFBFC34824C9984DA488D5E9E64A628D5D0056115A25CE175E9 +9A923F5516665E9C17D5F005F153C8CE47AE8ECEABAD1629D430B9F4D28F1E64 +E11D1489635AB07303DD83B3F1B6756342D6BA59435FD61116B8F9E24B8BAA4E +E996B6619E3CB09E8064717F604ACA4FE2E0A3A750ABA221B26208E12D921C66 +41AB7576A1AE107FE53EC270D17D41BC43895F2B01178C7725595895F46CC592 +E56F45CB4C899D9ABEED131C985E6F4AEE81BBB1913853E55FA3DFE34BA86679 +0F9972FFB060ABC6A7D9063472315A746794062F9180B2623318EB48586FC2DA +B409124E2BD6B1156C222648913A22F039CACA201AB57345833638272237BDFB +C04824EA76B096244714F373BECBBE5E86A04B3D31C8B44880F25A8276DF50B0 +C1ACFC2B1A615F95F314C5AF1A57251D2A955ED793855A16F1381D94E0E97061 +B238223F0DBDFE93047E4035BF7D4DA8274500733B87CF49DEABFDF003B07A68 +A8600B4F00F9DD10C8D37E7BDCC2C0BC28405ED3B6EDC6A6C9AEC1888C582ED8 +5B3CC0680C332C551DD2DF2089238C6159DD16D9EE21571AEBD3763653DDB4A0 +4D22F63C8533D2E05DDD8AC5364B1CC415315741C9887F579D42D6608AE6084D +9835094CAA3C155E92AD1EAB7758B4CDEC1A8DC6DB3F3E54B5DE3D7C5F23EF43 +A751B40CC3F8A6DBADF13131953FCED0C3886995CA4575D3B471C556D2657667 +04678ADA7247CE99FB0F66E2A60C309431D9F6B0BD01DBDC71FDD1DE1F4F8A25 +3ACC932CEC1E38ACDE8AAF593D632FF2ED520270890AEF3D3CE4E0BC52C5AAE3 +0746F59F84BE98E507C46EF4D10F18898D24CD382B7E75264C5098160CC6447A +4CD11D118CDB8D1F3010EFC7278FC26ED12465F6D9BFD74BAC09BD290C162C53 +95B416A6918E8971482F526B707C0753ECE6A8C55322AC28E3A3B6E1238732DF +256327FD2888811D0837595A91424EF29E7231C39BE8FBE0EA9FAF4A8FBFE8DC +ABEDE883D6524EC75A84AC186EAF98ADC5B10DA69322622163CD749E353B6C84 +8158F6E3A9F9EFF3B0CF9648F4DEF93E81EDBCECCC339FFB62E31836C6962DCF +80037E4BEDA967967FE5CC5EA60D9FD37AD26EA95E8FA0434A047258AD98F3B4 +18A61188C03BAC8D8AD51EDA50E80FE7865E994B9975EF0083D0C9615D7BCFF5 +331B2997773DDB4A330E470EBA08F2F4836E8D580D8A2F865454A84FEF051853 +377B17E1283DDE5ECDE97178603FA9EBD9C1FE39EEAD02AE8ABE63944B4B6CB2 +340E88B57A7CEFE62A44EB2835176DEA064FFF8270BF0D9A7ED8D4C1E06BF234 +D0D649F4F3A8082C5D91442EA1030C972B9A79477E0927333270284318982C24 +F0A71220FDBF2AB9515902F3812C9C0079707BB4530F1FAC4A2E1838B04A8E20 +0D8419B14F3A4A87F8471741633F3B87C6D45939256AB4B891EDFCD2CA183C50 +B604879445C29119BDFCA860D013438E7F26167CC9A2F3141405210546045642 +856EDC288E441AD5B4268731259BE5D1FE1332BBD38D1B59885E0D18921F11B9 +03274CA50671012290AB8FA479CF97D7EA59E01D86848014CD32A032C7254D68 +551D85BF0753762B588768915B17B4C91F7FD91BE5C026E2C3897DDFEB08D225 +79A9FC289A43EA57773443F534C7D18D61B4D7AB093225A7C9611FD15A99CD2D +0A21CDD65C8708638245EBE2DB4887ADA7053D9D2CD33E0C118018DDC24CEEF5 +8B5875B74EE824AFFBAEFE1370B4ACFE296F3B9C4C5EDF8004A99D31AED321FC +39388733CAB053A1D78A8A725B3A694355A674B58DA25ED0536A4900FF89639B +91E402B3C3598BE0CB8FA915401280EBFFA6A8104EB919F8C9700AE8EEA275F2 +E8BEB3A37A688317F9A385794EFADE63785689CF7E478D6BC16C6DA386E9F2A2 +429307CFAA43E2D61619C0CC037D3E3726F1498EB85B23610F59D57CDA6C34E2 +6F07E268993ED3352BF506337CE21FD303BE1361F7198F83986EB56A641FCEFB +4B76031FADAE7674D79FCA655A1503347BAFE8AD00FFA31DC69C79C833C586A4 +AD9D7645DD1E3AA3087628B158E42DF0A0C4C915C80F25D5D665AF05813A3E4B +A043583521DFADEA37106760AD0C70FC72ACE8D492052FEF54BE8E74520C4F1B +1C04032F255FE37DE4CE4894605A091E0D9419D01194C44BFDB6D34E4FAF2EEF +F0AB21AF916CB2A6B863F93DC3E4B366B4EA45EB2B9E47B549CBC1B13FEDFF2F +C691CEDC39740E919F49A2FACBC5A35049750ABC0EB4C0AD801524A63533F107 +0173D7AF2FFFDFCC1A7FE6EADB8D86B1082B42208E6E973800CD706A6C784419 +8FD0BBB8FEA43440B9BEAEAC0218EC27CCEEB7144D40FA05B77E34871A6BCAB4 +82FE7FE70150B339B55CCB387E8A6CF7E95B2A5A21A68D0EAE75EFADF0FB56EA +F751038129F248AF0BB72343BC9048457DDC5177D0BAF900EAA03D36D6FB9A4F +56D08FB880A8C9A21192CE1F1A5BAF6CBC894C5E0EAF0B695A69070FBC6CD4DF +D341E267BA704AD90BC5FF3CB9D30C14B79AE4D4E4DFB67EF8067360BC8214CA +BB4B0C9B49F79B80E935DE652A2AB2C84497E72226A7B7425BF66A5C926E6A68 +8A04D29C9A36C026FA06B370050CEDF59CD9B3EDEBF3FD22D3C90002074D7BC7 +60835DA02CA369CDD4AE6D2195D179D9310D16055924E050D06D537F33ED580F +28D76752721C74D858164D8FC0033CEEF644EDF90A0AAEB3C499C930F1BF4907 +37EAF1A017092EFC40D79C9C720734292256649C9F904F42513437D203E22A50 +BB0D8236E59FB71CD5DB6FFB13F91127227A6174E243568AD20F059504826773 +D84C8AD4BBAB15EC0349AF3BE29021CF990CC65AABD53E74EEC34119A1B0DB38 +C5209792461AA86DC4D65535C7EAE3BC25BF39F3EF743FB0E75576B1F603A1DF +4594215573DC3DAB17C022F4A4CD704E8EAFC757AF914192475873C1547244F5 +4DA91A078306ED495A5DEA22D2698EC1D2B9387A5EA7AD2993C09351066BC5C9 +A5111B294B94E1A8F3B9C258177908F23739958D30A164069024D2AE85BE0DF7 +C21A77A182B5CC741F9AF94C0C9953DC29B3D81869E3E5E69A316098DAD5B1DB +F7E3F32DD6E22CFCE32055D00556D6EEFB36539AA57C5D08390806116CC86790 +DFB96F01A296444ACE64A841B0237194B10FE3EC3E84573D373B81323A7E861E +1CC46AB4BD74424A3CD1FCEF4C5843D039BBFADC6136865A606B6262EA85AF9F +A3CC025FC1B6600AE53D05B3BD73F6BE22FDF77ECD6E5CE190F5F21C4B84FCBA +6A40BD458A7232D85F96A682E5443FFB5E3E6B76AE055E797D619AEB458B0205 +97121D4B90F4152C9C25890B4BC7DF0CB67F65552BDA022CD58BC8397266FFDE +C1ED46D44B9F93A9DC9E12D205D1F89C5B497B457B7A036DC1DD8C75FBA69A29 +C8F2FCFBE3D74874A75E7EAE47EFA775822B585E548123D43FA67FD2D1911DD0 +6CC6978CA4F591AFF111064021A64CEA4AB766D232B0C5A68FAAB6743FAF8E58 +6C693D961A27CE0A5B9741E1F61269472272E6511BD0987BA64B65C6DC86640C +1859EA6438ED59888220A32C610DDEFF3EF5DE0F439265F9D42243C4DA942F2C +5EDC5C9F9C11527D8B3E21F481ACCE202FE511AE531D71A6EC7BC39A7C529D0E +63DA2CD368D9AE3FF675A299C7B4D1FA34F9E167165A114733B85228251557EB +EE20F03069DB8684036FEF56F99123DB8230EF416EB68D3BF8F718AD8B94C2E5 +37D91B850228DDD02D1884ED2010021EE9D4B863DCC5D61CDB88EADB67B1040B +FD55CC8C3A5B342F1414604AA1AC9AE8113B263520A01618E298A1A5D8C46C37 +DAE4481AAE4F381DC6B1507CCBB8B2CDFD97E267AD009F305538A6648CBA0A70 +057E28C8870ABF1B72CD197FDE167B689AC7BD7D3797082BC93371B4C23DC404 +47A277E6FF7C3EFB2AEB98E44CB0031F034F50670AC101669B359F7A10441FA0 +800F872716F44C621645582ECA81CED557AE92B0DF16A308087B8B8D8954EE05 +BA7ED4B1156079CCDB6FF66B612A95116625DE37FDEC0B90B3928D9C3DF11309 +04479ACA31ED9EE17A750FD6A8016786A77545367E852DD0F46561F664ED1EA3 +F0E669411F157591E50F9D1B1DEF073D3A260F22DB546DC16D6DFE6F4B456773 +F4D13C3862A6B2EE20595DC64251CB6D3BB8BC6278AF3D0D259D05CCBBD0F688 +82B3C0992529F8DEF8D0F6BD82CB447626C887467696CD1E9BA2BF47E7508709 +2460FFB83A86D84F0A751C3F9901F361264684AE0AC0F9FEA6BB562A1C0FD7CE +D8D760E195230C053110E99E0672257A5639AFDEF9C4E34068475EF1CE6BF97E +C995EF438E3D5BE3CFFD21DA216DAF570A8CD8DEAC1B30893F43BCAC8BFE6550 +DB6D169944B1542AF9A0B2EB036015ACFCB7D62669E199C2B255C0AE1E0CF222 +4CF329DFD5F2D53868E95E8A78986821E72EF7D17D26C8239C1890F28DD23E33 +FC4C92F5E1E89CEEA780A9DDA9062AE5F80CBA446A7F288994D41078237E593D +B7B840F762371695CDA9DF5BFA90F60655A4B88CF96A37498861868884D8E02F +3595C7921940795D0F15DE5A8409F3BA99098C7412DFAFA2F26E5D323E7AFB4F +4541366C1B920CDC52FB0A2CBB0AC45F9927BCD6E4ADED9A5489199CAEF855FD +4A8BC0E548E892D76D8B75BF1A108156F74366795971AEFEE7670B7A39CA1552 +E89EF90BF737D4C975FEF101211F4F761AC8F70A0144EA87FDDC1E8BF49683D1 +7D0D51AA6E86154357C77F1B78DE041F1BF9599DC3195FFC78A8780CEBB0158F +6001CFE13E042EDF6972F5D06DB20652AA6CE9E3E5A21FA947144DD58A3442FC +0F0042F5E47606BD840D7371986D677BB87437D9C97A424A3974D6D97AB303A4 +0DECF43EA78DE33DFEEB9145503F885B765AD4955638186CA4F55AF3E9B071D4 +B836C3C77BCCF4489D851537B4CD4E5D196D3D05A234AC7BC9A532EA0F25419A +08AF8435058F55EF559E488F4844AF7997525636676337AF6D8D9331DF5F087A +B4883C86310E3E65B053A2EAB96D39CEFF4C9532A795EA8BB6E2418B36026AFE +DE1C4E52EC3640F6D5F23AF1D14AE9D273298F1B1C3D7979264FF712A4E20834 +96AB90E0ED75972B380931985FE9C1641F769B7A1954A5FEF49139F1AE318A27 +EFC90DD05D7FE975DD3A073572D6CAF0D4917FA7FE2AF0930D16E3F7364F214F +17CDF089CB4BFA150301D56462FFD8C4C9414A1F683D0370E3E6B0907A9FFF19 +500A005BAF537B6A91DE9CAB8E51AE7BFD3C6B73BACFC947AA7B8A808E22FA93 +743CCB9D4F642F23B9DDAFA35E3614E9C9BEFD09F46DD2712F71AFDEEEA2E82B +804191018E5A65F5E165CFDD0E14C2B051D376D442CA6E43C9CC4118E61925B3 +769D863B82BCF85BF3EFE70270DE3FDF909A9DB5ADF166EE9BB2D29749392C66 +BA7C9AD2E24E51A2C0258D4C441A53B1EE19698ADDFFB9833D3C9D10AAFF6121 +ECBC6A16182F0317884E5CA98021045DC09C95C909CCDB0E0306C8820A1BCA34 +9528DDDD57A02F859D083DEC4C8C6E28D00EA549504D01F412B16AAEFE8DADB5 +03D3B24DD87427492B094B7AACCACD9E55C8E16031A64261AE9A0D7CB7993129 +A29E4A528B2D15B044803683CD5C24C3939441555B6975C2A8B3E2267176F58F +5EB1C339A8D1754C15A5FDF504080B09B8D5D6018BF1F4B3D8FE9D8870D12AEA +3CFD66391670B7B5F611B100E07CE804A9E7D9A5C2FB1A60EC0560A6DA86E96F +46022D9E5B93F43CB14F0CD0941DFDDF4DDEA62A0010A79542412280120A17E7 +02E0050E364CC32D330CCC121589249EC305A68039F667A5DA58D60B0E625A18 +7FA7B90061907F55BAE3574A9D772AA053D9D1705990A5DC1E5113997C96188D +2F7B63F8612F2A45E17AC21E9427FA0C4255219EB64F733E3E3D1DD8D18A9F58 +A952C4E8E4FDDAF1DA768D6743737C35F8FB7898CA25712F62239D4A650EF9A6 +4AA4B69FBBE017B67E29D6232A43943E987B92B09F9EBCDBE100B0960591BD47 +5238A62A20C11A3BF8A1F3857328C3E7BA4B0F40C941D59CFA44DE816E854BAD +F70B669294E6AF347D70CAAF7BDE1859E09677DBA4EA9A6DB36E14C4BC3CAA56 +32516A18359F40A84990C9E278DEE775DDE33D6C6E104E7A76806C7594C54B5F +718F451D7FCDDA4FBEF2FF119CD1EB2D55E5476B69CF6EBF77D05AF9D6FC80D8 +1C5B675331F0019ED8EEF2E3C027B56F1C6012D6B4B1A1DA6E2350CFC58D1795 +9E2B1499F73DD578D489C72EE95EDB90E01B06A8EA0BA3CCBE33D28E58B973F3 +D71AFBDD41AF1F7A056C15C262E992EDB879115075A57C599238F52B78BABE43 +0303F224DBF60B36354510FACBE7E5156E6AA3CECFFC5CCF48CA29B1108C6C5C +5A7CFEE51E47A249C75A38E620C87E92E3DC5838776CFE165CDA7BFCB476C1F4 +58305C255A14F3A84578295A355A43E13AEA820A43F42F26151CD1710873D437 +508304F71C1FA2FDDE2D5533D3792FE4DE5C438501913690654B4F7670D032EB +AD1F53A474BC082BD504D5112BC2271037F50605F58F499D1534A3EFD0FB932A +9A0624481EA99CE04C57BA83BBE8D2AEAD228B5EDD0790C6088CB853D92C06B3 +38995BC172FFEF7A37671DFFEE978B871CBC663ED60DE0B1A2AC93198185FA51 +0C10BF0F9598F59271791CB3EFBBF5C08E70184542C525EBFA2B22DE3E524A57 +3FC68C72442769E3E4731553077A92B3D033E401095ECB26A10E2615D9155253 +92BA319CC9A711C7933BAB39371B30A8FB443653C37D598566883ADE04C277D1 +D1EC22F89046923DA61C4B5E03DFD6EF04EA845A628937C0C25E765E12AEAA2E +C4F028403F799161597FE4FA051A5ECAC74563856B3A3E835964D82F59D64F21 +D18102A6499D8C264FD4545CDBD1427C46AB54C18963228A17C5A59A4CB4C771 +559D529DE20DCD85D07A1AB3D63D35F84C7D06B450FB7987D28259089DC25508 +D76AB9058149F84A664C3FAF32D3C004AF0C84A283FD63A8BDD18A2275690D75 +46385766936AE0BCEE9F8C6AF8AE73AB15D12B0A605C40F2E566AF3B632DC7DA +3A9D18BF26BA6B64D93F31CBDB7E352FB767C759881B4EB03199075366901AE9 +0C61C7BB0B72ED422B49713D1AF8F2FFFF9C49C84B39B412A199D30C290DD2BC +8967A7A7A700EFDD6329E6C5584ADCC7543353AD55D1B8FC1EDE2219AECAC5A2 +6C5031697281BDF13D53708338DE9775B41FAF2AF2ACD22BDA6493DFAF8F8B73 +AF180FFDD56E75069FBEAFB7AC6200F14897359E98D539FD8B088876358EB94D +2C29153200E5C47EEFA6BE1E6F71BBADFA869B6336E886EE01BFEF1CFE57E6AC +92DB4A4D91D96765F16D3A311EDF8F0F44E9337555997A0010B518B0CC90DB0C +0FAD6A672963FB31F1DCFA08A59549F35E179F5D565ED992232BCA51C3BB0F88 +51AC1AD4216915BC24B9ADAF9908227AE1AEBD3B81C848220000C94ED1617EFD +12A926067EA52FA5DA67EE9C2743DB307304C8AD0052D5B11747385F4C1BA93E +78DB9B866EB3035865F16C806C1A44BFE06EEA76016B3ACF627A22C321C6AA81 +06E3B94FD07505EAE331566320A9E368C04BC4204B66CBF82F787C787D722518 +6D2A5A64503129E79A132528A337EDC55D4CABC17CB1460A56FF4F02C70DB561 +A4D076155155214A25F155C4EB9A797850B7190C140DCC53F507F085450F0887 +310774EEA9CF0BD4651D69FB954DF151101B69B542D41B838F81399EAFC8AF5C +D569B1A404DA2F98A572733EE654CA7BBDD2D446E3D40964F0D30742C7883AAF +1476A0E9C7465222EA57FBD2943AFE78EE700EFC8BDA7190BDDB608558B7DD9A +23B1E4DF43C30982C55A524734596CAEBC3B250BBF5AC0208B23908447EF0FB7 +EFF98FB1AB570C0CF07DB4AED85AD8EDA4ECB1989B30DF86E8720B08512353F6 +4D242CF9E6EC3EC6E34693E50B537F67A4E77D55F767A7540D60BEB17D88A3DB +DAC1179E7B5224364E1FDA539BB21E7E163FB12DC3F8BD4D9F6187977ED1E12B +33F3F882336F7BEC0E1E11045A12474749678D461718C4CC7185ED32A1B618C6 +C3AFC454F7D47BE6460571CCAD7ED584B77300D9C9083D11E9D40A2414485E4D +BAE521E20A27D2F7EBEDFE0BF9969EAE775801EB87A72FC4612E9C2AD4C28FD8 +D1E9110E45D39C95F521E872672C6399C9AE8A3066205FF915FAB0B0BA16431B +DEEA4E521292475C6AAE3FD9EF2FB8F16DC9C49880B2C717E04DD2907BDD7B0B +04F8D33BD28C75356E13B1EF69F35E265DCEE5C98116B35E0084DBF39726C50D +A72B9ACD42F9445D7D29E1C77CD50BB334D4569477FAAD2381C3F91EB2E9778F +1D5269ED9638F4096B2E81CD57AA26BF414EBC285CA934B61A2E640E1E5178EA +AA884464A3214F547C4DA207783A0EC0A731E9154D2465214BAF466DC6B5BD09 +A06615F0116E4827A6D61A2937B123F095C401FE5F85D56AE581C38DAD0541F0 +C737D6CFD32CCFA3439352B5BA031244990AE5FBEDDA56B9D8177E8A808DC3E4 +BAA54CEAD5DD8E4D3B5BC51F2D006C4201F09E9C4FF12499B07A53AE6F1E15B8 +019419744822A61DC40AA7E226E3A3F0F85BDBC7B0780342216A85995B1465E9 +C0B2CA74153169202557823519C6DEE75EF454B245F100590239C6570810AE9A +CC519E2492D1F812CCD05C470D45F1CC4589FF5FA056A3A6A30B38AB7589B18A +4CCBAF971D9D435F95543E6F5CCBC57809367E0BDB1097B67A4BCB91D378E110 +D1726653BC8D911E85428D9D0FDED249033968A382BA8406D7FE1CD7326AE12B +896F41B4660A75B6EA950E7D5A469F4A749BC0C19CC250D40F83942D899AE43E +8AF0EDD860E413FE7FA4B292DFF210C382C307C3EC17B8055F54054D63EA1E87 +EA4F5A7D4FCCE61924C77CEBA4EC2628598382BD26B23C4B565B31FD04E12BFA +3A0B18E888937B6B97A5F7D1222DDA8868454B81CAB34505D8A7D7F3F5470BD1 +BB561AF3F05EA21AA56DC1E79EF12309349B8E525C1529969846670A9E4C4E43 +5632C46413FDC2F9945837B42EF3A2E775C968DE303BEDEDBB48C4E30233E2DF +A7E2CF06B0D2F2768D164B1B9529CB4A9FC0722AD961D12AEC0C68563B5213BA +067775E651894BFBC428A2CE4A1235E6C37AAC9595603AA8D700D34471FC0058 +892E10A0EC34E4F78CBFE934041E8E5BA70C3D24792D912D726DB197D1C0454C +CC6ECC7C0C8BEFE4B298F398780D76790424ECB8B009CD4E7B8A60AC372559BB +A79DC34D08B87F6E2473F5B44E03F6265E426E60F15BA068B354AAE4C2D88ED1 +2268A67B193B82C2D7C1F510F6529D208FC561E7F1DB8552CD18E811F2B381AA +6761DD096D68C094046DF846688227EEF12BA037D756CECBA796F065447BAA26 +A959D465B74109BA18E508CAB49D295173B5D955548ECA177CB80DCB703474F0 +281D0555CBD696611751272E6742E294D4B0045FF108193DBEAF90C16D564E91 +AA0AA9EAE96CD37A672D2FEAADD043EE72F1E674A874525ED7F0C45059476C7F +69C39F459C3D8D864AB4BE8A5119B439351FBA13EC5D7F02B01CB922571B8E5E +DB07E936748E9D65D20C7806A8074B75D9EF83F8C2BF48818373C3B3214B39C7 +68FA9C93C5F3B73489CB5A31CA693453638DB01A7896DE78C5A305C157537F82 +7E04BC73BD758AB4914844FC04009631B43372B56E8C15E550DF331AB070EFF0 +629FE2D7A79A36A152AD8FF0D2600EB9C98EBBB72B496AD659E1A373D5BD3854 +EC046E43E2C96BC80C5B421E2B10C644CED50169D6A8A07C0CAAA57932CC4E61 +3410D466BDFAEABFF0FF17941BF7819BB6D00C06E353F7A17094462AAA8D5320 +2D7E49A392C1896C373FCD801D3E9B376A8D918AD36B8C7148476C7BF260DDF0 +E375D4B59EFC7D1871960900F68F873B37925BE02371F6889B051B7031B050FD +0096B0E418FEA14D0BE256287BBA25ADF44E706DE035D33834CB17B8FCED456C +7E845E76F8404DE2292BDD35C115D5CB56BB9EBD0A84B11CF537F2A8BA7FC974 +20DC53FAFBD227E0221B7FBB529030AAA9911B80C7E91CB25DEAB06A72D76BC3 +7540AC7E91E32A2A3CD92B5E8775148364FC0B9B134103ED2A4F22E83806B655 +176F255AC04DF8C2D4A254FD26CD38B7F50790C3B45CEDA5A7D17F5427057169 +3C3CD88527AE000647D67230E246A650FC8A6A9F55CA85AAB1AB3E8BB5F9CB60 +A2B2B7D3DEB2719023C6C64A0D77BF899B6A952E6FA97C2599DC4F90709E0975 +18A9C1185BF00CA2DD872330A7FE09BA82E2BF2E7CF82722A0C43D6705F07494 +1D0A2496F48B83647ECCF95703513B781AD65B4199A3F4CDA28B39FCE9F4BD6C +2E13812BE03D9F3E8F809460D73C25EA81DF6CD676AFC9B1AB66C25BACE37D09 +FC6E78EEEAD585F53DEC03F54C675830DCB17B65A516449B0E9C8E1C8DAE3D55 +BA2557E9698D4C1C4F72AD082A8F26528F990CFF4119875791E5FB3569A7FF3C +5D04AB6665FDAC036157FE49104B1D4557B9A3BE087C3FFA72ADC15D75E4900F +102A8CB40284E332B5F89DF3FFAA26AD98BA67CCEC68AAC18FF68F77F3836B60 +6CC19287478BED9FD32836EEA8358353D09C1716AA25B9512BCA51F1CA84250C +D1B028438226AC5045ABA3C9A1A92AE40AACF659B913971504697770AD1423DB +F2768BFDE03A0147DA585CF3477702B21DFB57FD9BB39E39B55F9E11F9FA1553 +50AFD3ECB1DE71E39473670D16754711CE3946DEB9D673214687A0DC5E409340 +83E0D0A07C10C6EAEC45B18D2F281917960FD385E9C9D1D73B4C80FB6028E6A8 +785DF08047236D86AEB76093F4F5C830D8F3249A346F2A7F1CF380715F431F55 +30037A9981B7B744D97BC0011D8DEBA049FD140530EA8920C04B03D5A186BA9C +49DFC4A2A61BFD6C1396CAA0740408D13986BD3FE2F1BD3076319672DD9C7A9D +F1C287AAB3F198D03799FF72F5DABBC0C294AEC4092C1B220F542115D722369D +C1A58EE387AC12DEC80512EC3E0AF70270D0E2806ABCF9170613D352116A2889 +73A116B0DBFE39B54ADF106BC33438C8BB8FE5A2499C58DDFBE35F2F700F2A35 +884BB49D25591CA4D3F2B2ABF24352755A14C4E5F213EEDC40B112ABAFBD8295 +37C2BD49A7EF58777C4C28CA13007768AB00751731F98E1B8955ED4D2421564A +E5D0DB68744ADDBB342B4F8FF0B594E476F52B2BF41170C80952730BEC40B51F +65A2DEEA7A4A4D459F2C4718D344BB50E6C875CEAF26D5CC6D07DF77590107EF +78DDB78A6C6A55A49553DC3F56D3B0C1BEC40DDF02D74D79A3188CAD6B2B7322 +DE4F5F28203627DB3B77394F05DBA511E62AF03B84BB819B441F1E5DC62F5E72 +2EACFDD4F6A6E300E7D7A75FE0AE861B7C54D9824305E80B49463945B1B0F001 +A06BC3D82B467198028B02AC42139F8829BA9972B1C7AA6B02368F37B0E8B418 +CCF7AFB508E3B585FE408E9C1F0428C6A3D4CE846491EE5FEB4FA9813DF4866E +311D5B69922DAB995A2ECA6F000CF9266CA87C27E63CD61EC39804DE1C0F569E +D04E7E4996953F36B0E995BA662327B9FF8FB6CCAA7C1990815929F2AF0637EC +1D91D932FEA8A730E2F9FE49C5DE524D03D03911CA09AC38ED6E2EAAA2E99670 +0989F6A1B61F4E56992349480D7E27041A73D4C90C204723A8764E84444A4894 +C908BC6B69003B81421AFD07E991E2BA8F1B198405B8F3565D1687A40A9A7261 +9C812885B4C8E4411059BBE9A0C0FB2A67B3659A2B00CD8BCADE424C090E3CE5 +4056B7762A2161890703174AB88ECA83429C3814E967B8A5F1BE5CC8BB95CFF3 +7EB52EE4D294785945796C8DFF48A85DB0FFAD3E348D2F868A84B3EFC50AD76F +091C808F14C210700636981D68B49C328C5F7C3D6AE4E7F6F9602DD4F5D737BC +1ABD48E915A1B46F949C417251B1B32871A659480A0E78072171EAD52886F3B1 +9D6D6FFD83CB255475B698AA1DDD51348AA52FE3F7395657BA2F0445C046BAEA +DDF883D4F6B533408F3D73324752B520A8564B9CAB43555F6A2B9715194556DE +F6D53F9D165CB6298146519D26A4B05694507BE29DBDC8139659C80D8B8757F4 +C50320B5D07DD3D060B394871D38F5E2A67909122B08F0E48194627B8486C64A +A7A31D4B65F13DA134347E9E38C0C8AA0BF0CB613A192552CB0E963E1377852C +932F62749619C737E71C5B243CD3E60ACB8F4F89A37A113C5DB4A2B80B01E42B +7B5ADC7111FC1BD334421A79B9AF7F1672CC4A341F6715C21B56B8AE1B540BD6 +24E93A8C4AADE37C635A0541D27B4FE8325A90141467A71207F6CA1E1EE5C7AB +3C4107EE7E72A847B84BB6399326E807A7ED6009037E658B2C7CB9065D354CB1 +EA800A31C497E9F0E04AFB4AE80B97C0E20FF9D5828DF37B16B5423867F5E5F9 +9AB5E2B9CB419FAFDF118CA69E2BDC5161F3E242FC9A212F0E1D3EF8C00FBD03 +A718534C1751AE465DBC9C52CFEC73B49703EB6584E33CD6F6133FF1AAC8DBE6 +E37065B637C7690CFED68DA923180E4E1E1B7EF0C780B37E18D1148DE9498E91 +65051B7019E0AEE66A61B454CFF9D8A31592530C4D35C0118B4037EACACE4EB5 +E74513ECEF747A9C37DC36742920692ABFBEC85CB40D92864ABC17E825460DCE +15581B05BADC7ED84A06E9A85931E7383289D2F4C27CE63BEE24DBECFD760E0C +B475268963D497D6203AAD86821BC70BAFB1E5D4D1443CA164735A60F8B61190 +C4EE112A3F42A9EF8FBC10FCE90617275E3EB5D6E3F045F5F2C4B00757F8AE06 +FAE75483A6E34D48B51C6FEF6AE91B2EB0ADD0875EE526CFD5E239686AC97298 +8F583EEACE380541C8CE731D7E57B9D7B2E3B843B62522E425D69B2D0856262F +30CA9BAC14727810A4911973FAF47CB8E82E5F311D9B3B2835620C1E04161D7C +97F9DB42021C0098BD1B6D408E4233758C8379980F766CA73627F819F4575DF7 +2BA28939CCB167494B0756B0BA7151422E0251FDAC40E8B97D39FA1FCE4977EC +0CC9D3D768CCAA7F74311BDDC5AF1F67756B41EC9A2B42A61C6210D3E032AF3F +ED5F06E3E9AABC8D0549993477667253A7AD0D8887E64FE301512B2DEBEE360F +36D35E249DD79744B38A9BE74976471767D977758047C94BC472B84D556BAA89 +C14EB7861F4C66C047F62BB5E7C7DACA79083B83297F997A26B787070D4E64D1 +A6206E66D78042F49F687BAD1174364936C2832B48252D4B7109C4D89F74FB61 +33581595F4628167ADDF33643CC512EBEB561943265467094DBE4E42463BB068 +0596DDF52110083DE5D314869C9999CDDF0A1C4CB64D52701697CDA9B2B2ACE6 +9FEB83472527BF73DA442497B141527CC4A8E7E16A5E98DD9D2BDE0BEE73F0D5 +C40BBF6D856819A8D8E940F7239EEB012C211169CF3E22496DA4A3F4517E98CD +D1F643F5E691DB9C65790C225FA2EA55B8295498F19FD44E8AF057F7DA3F7DB4 +70D2B9FB295A57ABC9ADBEC0D448A5EFF0EA278F4B6D4FE579916E2402977CAB +ACF70FC79F65939B07A261DF3542B4F4958DBA6E8596C7BEA437353B309C2C80 +B30F64E74B82E2A0DC7061BE6AB3EDD9859C05972260976176FF0DAE3DFB08CB +634860289B4B1FA3A15B34FD89E32F29A0C68EFEA0EF0BB908D0858C71A0C89B +CCEF8A03CABE7D68526846C8F5DEA18BC3044E8A88D0435E0F64BA4B8172C86A +632ABAC884E331E5BAC8A78A081683086076DA8C7DC1FD3D12D91A839750EE4E +3B793EAFA48C27531C8E56823FA8CC62D568035C30A37B16A1131E9AA1D82FBC +A6AC9B56353F15FEF4756A8E8056ACE512CD7B4E9FE6B4CAFE23E8BEF99C182F +503FB5B912473E6C1F1C8C74E1454474EBB9346DADED3294C61D997FD8D5378B +01DBDE07481C7D9FDBE4994B2013122273F2F25BD36AA58D4D63DF00810D792B +5C90E240F76ABCB06B018C929E15B4F54EF11A00930D2EF39551E0FFDDA45375 +2FF8E1AE6700BC669518B02610B7F7B859638FDAC8F4814286ED75B776FA639E +B6AADC3ED88B58200AB680F1B25C4FD7E079C3AB654066C666DFE0F52087FFD4 +82993F0EA8BB87F37D25305F67EED67F18E93C2BB76942FA8C6D4E7365385530 +849BD2E11AEF151F68A9EEB5E944C8B4EE357B448D7432D62D32609B7356F03D +613FA7BC221DA83F26121BAB44F8DD0A79006E4D4D361A20E831CB3ADCC3271D +FBE71974AB048E748A8073030F81352E876AA6A1AF623DF6A543C7539DC3953A +B5957E258564826D4468DC0799DEA65C4ECEA2CD876D4DB7608CDDA893AFEA9D +ED0DDAB30E5A27168CBF9441EFBAE959EE99E13F7D7DECE56A82BE04F9FDF591 +0E96F3C09EF7F1A2008B7DAA48BEE9CA111C29EF778E361AE82231AE2A51221C +7B1D5CCBB7C5EEDF806209675F99F065FC0CAD2E194B02B9DC531A95B683C550 +F7908B461F5C8F2A458FA87FBA13A3D735AC75D8D7787DCE6337A1AB30ED5F09 +DEF32E147E505795F9C339DA9A8C77BE60EAA79623CB627B219E92FC9BD04F84 +671F5C9A02E5E15F491CECC15A4F33F4982E464FA1F237DBE7E9E01F378F7114 +CC5E09383319BB794875E43423052FDD30FFEE467D614B9C9F249C73C7F6B2E2 +26E4D82F09F035F3D42CDBBA74401CBBF4331F51135ECFD2CBB49647D09F58CC +F00A78D223D6650D4C2A460CDBDF78E7C664F984EA37B12485CDC165BBAB3BF7 +2818A4A6B70E668E65FC028E3EDFDCF36D10E5733297D2160242D9FDF865BF8A +D681E75AD45B2A916684D4584BD5862AC7FE470ACAE0DF18970C906A62F931FE +56DE8FE533D8595D38D903C71CC818C6E1B4191D5FAB2B106616CF67B20160EC +8BD4399672F2B27D8BC65C18CA69FDA610CA10D0F750E4EB4F634E4174E68C7D +19CEEB238DFCCEEA67294785C2D64F03DB37060C501A6A40EB07C1E98AE63163 +50C61E12FF1112FAD1DFEB3CBBCE42813BF0F1FFB369441C4FA4928A6484C92F +78068EC74A29506F0D3827E864D2C6C883715C599CA674F025B9765DF102036A +221820AF2A68C5934E382E7C31F99F6BD97924814E2C36E71DEA049D54D3DFCB +3AF2C0BE356653795EAA7E115710C23175E29EDAEF2D44C59919D0E80F7EE044 +C6EC960B3306E72EA8FAF860AD61A0A63656051E25C2529B6B0590058EB96FB6 +D02500428DFD5FF181C215E5D921F5438433D3A85B41A468D6B2CDAA6F36D5CD +16C1FD67D482EF5F4CE0121AEE2BCC7934FEC104F05EFEC44D1DD3DA08377CCF +B59F10BB0530FED3393B3F218D8FBF0645A7585A955A8B88F084FCCE03B0D431 +44AE84458EEE695FD5D41383340DDA01A92DC36090AB92A467F70B881FFA6D2A +34D59BAF4BBD77322926418514522326E4C308EEEFD7CD1910FE800E01A1B8EE +9BB2CD087A7900457F237930144DECA8032A3A7E77684FBA48C7D8C8079F1A59 +4C2D30C2E34E73E6ABF21683007EF0884DE8154B4797EC313B8B0F7FE5E9D0D8 +8FF490888A9CCCAAC8957AF4DE9F4C23E19E6144C4B03B359EE91FA75CF773FC +A857D61BF38FB137CC947EDBD8EE9CE217BCB2A266AC3EC95F6A03CF779821FD +3C21DEABEB020F39BCAD3E36CBC15D96889D1D326139F85FB6C243011CFC73F9 +C305D55ADB0CD458273476DB22AA2B2C8E7CE950B361463A88CA88CBA6586E29 +39D34E9F19EF2FB66AFE46B918836B85522580827B7B52B6547806D6996DBF07 +3F00F1FF9EAE4FB3CA81FAD1A35532 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +%%BeginFont: CMTT10 +%!PS-AdobeFont-1.0: CMTT10 003.002 +%%Title: CMTT10 +%Version: 003.002 +%%CreationDate: Mon Jul 13 16:17:00 2009 +%%Creator: David M. Jones +%Copyright: Copyright (c) 1997, 2009 American Mathematical Society +%Copyright: (<http://www.ams.org>), with Reserved Font Name CMTT10. +% This Font Software is licensed under the SIL Open Font License, Version 1.1. +% This license is in the accompanying file OFL.txt, and is also +% available with a FAQ at: http://scripts.sil.org/OFL. +%%EndComments +FontDirectory/CMTT10 known{/CMTT10 findfont dup/UniqueID known{dup +/UniqueID get 5000832 eq exch/FontType get 1 eq and}{pop false}ifelse +{save true}{false}ifelse}{false}ifelse +11 dict begin +/FontType 1 def +/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def +/FontName /CMTT10 def +/FontBBox {-4 -233 537 696 }readonly def +/PaintType 0 def +/FontInfo 9 dict dup begin +/version (003.002) readonly def +/Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050<http://www.ams.org>\051, with Reserved Font Name CMTT10.) readonly def +/FullName (CMTT10) readonly def +/FamilyName (Computer Modern) readonly def +/Weight (Medium) readonly def +/ItalicAngle 0 def +/isFixedPitch true def +/UnderlinePosition -100 def +/UnderlineThickness 50 def +end readonly def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +dup 35 /numbersign put +dup 46 /period put +dup 49 /one put +dup 50 /two put +dup 51 /three put +dup 64 /at put +dup 92 /backslash put +dup 97 /a put +dup 98 /b put +dup 99 /c put +dup 100 /d put +dup 101 /e put +dup 102 /f put +dup 103 /g put +dup 104 /h put +dup 105 /i put +dup 107 /k put +dup 108 /l put +dup 109 /m put +dup 110 /n put +dup 111 /o put +dup 112 /p put +dup 114 /r put +dup 115 /s put +dup 116 /t put +dup 117 /u put +dup 118 /v put +dup 119 /w put +dup 120 /x put +dup 121 /y put +dup 123 /braceleft put +dup 125 /braceright put +readonly def +currentdict end +currentfile eexec +D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA +0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93 +51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71 +7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551 +E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078 +0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273 +C01924195A181D03F5054A93B71E5065F8D92FE23794DDF2E5ECEBA191DB82B3 +7A69521B0C4D40495B5D9CE7A3AF33D17EE69979B82B715BAD8A5904C5DE0260 +6C15950CCF6E188A0CDF841EB68E5A2F88253E382140F87C87E55C9EA93B8C89 +14A36CDF630D6BE7CD36DBDCE22B21778E8648B97B7EC6742EB5114BDF0454B0 +0EA7B1FE236C84C0E5308C871F67B973892890557AA12E00B2C20C71F516C397 +3F3BBD14A1D0149CA064391056E45E9470FC7F6F556ABC82653B3C8049AB5CF4 +BA83C8F2158C236B2FFD4208846013BAF4165E8BB8D334C8FF2E8D74AF5DAB2F +D44788869B08399421AAA900ECC6A2D594641C121660D4B5F512938994C18DD0 +FCD9B008F68F0351D21ED735B2740CB1E0C1CCD25EB548C35B844601D98828DB +556F71D07E081A593FF12DAF83676492A0FFE16E95717A07082B43A966C1EE8F +8A59E1255E1705C43A23CF29A5E4A6547C93F1680A870EE7BAD8CF74D838CD5E +F806911D8FE4262ED8E7F5BC58B92C9C6D74F8AD45FBB021EC7E97393018B9DB +B1B84E7B243ADB05ADD3F1DB3692ADC5D47FEC7DF93080669E63281F1576B673 +125EDF08016664BE73364F65389F7C3B66623AD1754ECBEF9E5CE6948D933787 +A5674279ACB2EBECD3B4E6361419AB32028A27670C9F3E18B746A10B00AF6D77 +4EC00E3BE521C02A99AE5BAA98F793EB1228952BE67934B91472E01AF7B816BC +56D7F19F631A1927846D800C107B1E9CBFF9D2DD513B4A8CE2E0DFD77B1ED178 +E43FA7052765E9FAF89989D490D8FEF6C536EC0D4AE27A74F474B98DA9E6B92F +15E063DB260571979A5DE2423920CE1F59F56EB11E00E3BB9D466A8263E1E385 +2014BEFDA8D1EA3EDA04BE32AEE6CD15C5C010A1DF7F705A2C0C18E87C8DCCE9 +05D9163181CBA56C0FAC8C06A2990554C8E759D076B01BBEADE3B5FB8B551390 +6C8E4A2A1C6E7D9C708614626F3770C0AB7DD2027469C77975C27576065862AD +04E5E50CEBE907E3E991FA0C627302C0E207B4D5992BEBAB5853AD1C0D271728 +C76F40A79392ACCA7358F948AC65DC823CFDA59E1FF69CEBB6B7EC3CF21669E4 +70D999508F9C49E2D9F8818CA53C977D93E15FBBBAF75B1E84F0BA62BCC4BAFA +4EEC82D804C8A8C0210F3E5E258BB1F6921AF02BA9861BAD5C3D5FC8CEFABA8A +A607E547B802096F7AEB09FBA99C83C9A494B94408DD607CA6561A6E6660C473 +62CF8D35F31D052F6C6C8138A8E1430CBA7EA6973D6D510C1A06B3FBD79D9364 +240C1A00272DA44B89A9FE8D5BF36DC1B5EBB4A78ADBE9C5EDB485F093D9517D +69E1AC9A8E6C9D7C324E3797CFEAD9A18E82E03F69B2CED7D5DDCD1A218BF2E2 +ED2293AE999FE2A4B5213A10083EE0407BCF8007670B8C737EAB30311C868D84 +121149ACB4A27F3ED6C0C181C98AAAF51B105F264B5672D7F745131ABAB5BEA4 +0C9B43C0DD9116D6DC61F90BE72018F290D26D5E9D341055CAF09C9F45333CDB +D45B7954271767F638EEC499F7B53C2CC5774EA7A7F024C4CABFB93D9CB1856A +0C671A4ECA7C62EA5242648A84E7F3AFB9547A0AFC29593CFCE6D8B873A78157 +D337CABD291431C0A2CE1F37E0CD7340567AC206FF98E4B5A6410F70F750451C +550EFB54AA259A1B236CA9CB730D2CEF125EC65D959441F7CC9768F777B44844 +CC9842A307C72B740680ACBBF6AA35FA7A94825069BF7696ED81A371A9E5475A +9D997F2DFAD339AADF797F7E03E654234455AC3D17702A420EE0A597BA31BDE4 +FEB8DBA7C61D311CC90441A620164DC22DC2D373973EF84CC553453AB1B3337F +7B39983B8DFFB3A9425F119B45C1CD37A76F905777B3154CA6200792F1759D06 +E017890F4041A385F2238E3C48B6C8EE6F5258463FDBFF7AC762F6C4363926D6 +50F004D473B7B7F73CA686B559C2885F1AA761653C727A77D73431E9D110E76A +2E55C68CD50F43997C9B2FC4710F8C8540909829E215678E63BB8363C4B8AF05 +9986102BB36580D9CA95CD216B7C321822CB41B2E0422CD077F3B55E0246FDB2 +44D5976F67296B5B0BE4B06F6E43535C21164E6C5089C3E9BA2D6B30888C57DE +49DC8D9D46C0D5EDC47ACF2C03B72DE3B69512508539019B759280BABEA12BC9 +385308A0395C4CD33182A10A5A229743379C2075D82D8BFCE4A66E1AA087A091 +8F5372684FA5037D1B92D50CD9CB4F50AD4F8EE7D51F1C9E63C721CB5B9BD011 +6F0A8DD4FDCD2B008F223A1036D90F0F3B252487DE7898F9AFBB3A9D9CD49E0C +EF4ADAD5155A98D2125ED5A3D3907F67301649519419F33CD942E8DDEAC1BDA0 +E90C431B198F646766A8FA9F8D1561B57E126EF604838C0C1966655CF31FB7EB +C8CCC434FC1C96046D38203E1791EC824A3D7AED85C029288D4608CA7668A2BE +484C99639F121845B22EEFCE0A3B808261921AA042AE19E641769E91277BEC29 +4594082CCB3058F90FAC4A700A8A827ACA00FCF574ABC8EB7DBCECD97F2B22C0 +0AA19E8739B81AF8C6F621D69B8E6F29BAE233FBA655A0AF5BDFD7F5C6B9167C +6BC7AB693D45EF2AD999F5DA3CEFA39BA48A17EE6D9F2C4DAB91AE3F0044DC3F +5D5506CE4675AA928B0092D6F173644F91295216D8BBB14CDDE0AD524A4D545C +1B5E284A3BF0396664081CFB4F186A84A0D24D61E82F4767C1E55A0642720CF3 +909FA1AB8EAB78030B59BEA067DEDBD2F1D0340E790AB2777DB18248521934A8 +BB38A58B7F633DEA4291B0D5D13E9A882C974697CC6D3B49E030C94EA29B5506 +CC29C44D01B4751B453A46A9F6BF3BF135AE87A4CE232AF57B66578310DE41E0 +2A6AC422117F1963C4D7CC306BD25A6E724E51921779F22F029733122E23E2F0 +CB340008813ABB104380C80A492B3FC6D0BB07CB8D8409E9576891EF6E5C9D08 +EB8320DFA31BAFFBD336D0C2BBC3D3B2D30368B9860768FC080D30569C7F7811 +0EBEDA2962476113625EEB555490B8CE4C5F99D74ED10F738C61854CFF8B41C6 +9402E56BE8856144A1A05D0B05F4CB7EF728B2F4F5A439F18C3B68CEFA41E59A +D8308ADC92EC1289DC84CF48D2CDEFF509A145BF945E1E00D552D329EBD2A7C4 +21D58082CC8FA790E981F4AC8EAB99950678FD3A7DA3DF13778681B208DD71A0 +7C3CBD0664B37C9EDC6B601D79A2C51FB54DAEE849F93209793849104E722D3F +52DFAF7047EEEDDFE744787A5801E4AC2C3D58EC5DDC15FCEE03990C53B0C57A +FC54F125A04C8E4A0ADAA725808C587E7DAFB9F784FA2875689979D316DC22BD +AA36B306A1ABCF907B63C6476737B746099973CAEA8C1E2C5C41F27E0F7DE8D7 +F0D942E34E92F43FE902653D4D2EBB6F3B9F7928B1550A82AF234D45D028F429 +067652BD3D391BF423AE72B9CB1E8D91E898161BE3A7849D456A861A2046711E +E934DC59442AE7D81661CE8EF727D8D7DDC0270E937E40F896AEAE6171661431 +C1025C53172F9D366834BA0054FBFD84503FBAE328B6FDEA180F8EA35B1DA937 +5CC3B8F00C206908C2FFFFA6A7AC6915D15EA44BDCF29E2BFCFD4A849535F19B +0D307C696BE8205C7D84B9C77F02EF27D911056EDBB4080E4D3ED72788666CAD +CD91B0ECE27A177DB23320A7FA9C31408B4D02D2A4B1CC6DDE1A6CAC3D8EC1EC +2226EC98E51046D1EC26FA20EE62D24747D83CF4941DCE5CCEEC0DBE387149CD +E05B19FFCAFC0D117F9A3E60DCD4C815228D98EF95EB559AD0ACC0D50FFDF714 +56C3C812EA5ADBB013BBD956A7C4CC0ED7D3E25D5C9AF5E626F18297F75D4957 +F5B0B33379114B903FE98BCF35C3FF76FEE1D9AEB711F2962276531F7380EE3F +E368720E0292A170A15C5539B1FC7BB954EE2624B504CB8C805B8D31AC38307F +0513606F09211AE64DAC447693B2A0AD15E9A64C34F5A911ECD0ABCA90E9791D +67C6BD202B0858EF96E7722305B8AC02B01AB1706CC6AE875A8DDD15EE349046 +EAA65005E7866B506EDFB7A5A2AFD5C9E9DCC821A79EE9C1EA2C7BBA32A40BC7 +CEC26DB1AC473C8C3960ACEC581B37D6569E8C8C42950BAB7930B65E1570E3F8 +9A7FA719F1DCFDA45A3BF2AAB32C9A93BA3552608A61C623DE59BCB346E87EF5 +9CF025A87803161221C5C1C6F6B3403712C76E9D755C7BD68D7F2DC03C14CDF0 +C1BBED1D648B905B4B17037B7263C1EA7A7F06FAAC4E09E08483A8D714C19861 +327CD9C32DDF850302DD6DDE24912D00C22ECDF3CDFB18FA831A41A7488EC203 +F564CFE30D506F0829A96D35A7E09C3DCD107D589B627A15B55C5D6649126BEC +60B88C55ECCBB4E680265D9EAB4CE22965D3B1AF759B01ACB0D0E6C92B6B4EFD +A81E6A648708979487FC591CF09631310D46891423F4EC159A73E30D8DD147A4 +B0EACF6D45D18CD16CEB8176F03ABCB41F2234747B9733C8FAF34AE5D43D3BA5 +0CE0FACFC9B087F84FB6C68678BC6E76022B1526D6E5B3A48EC1A110BD75F45F +1C4DC6D39F254976453F57DF873B7D635C80C42026DE020E5BAFE0DA0D54D1E1 +DC634D2621BA184347E5252F645A6A1DB7657C48124186F0E4C644077457C24D +55753C651A9A7B6349867641464B515B821349C795A645420508673B93750D0C +7A3B33EB1F09782033742AE8F3A23FC02284E6C03818FADD1731361542E3FA3E +75B8D52B668C3E18A4AE967D0FC3157083D952AFB8144D549E69EAAC51C279C5 +E5D88A0D9D53013DFFB4352A1598FF84DCDE6FA32FC377306B9B92C0F96EE149 +8CD55E7B2445B86CCA7A547FA732D52D59025129FD8C6333AC0DF4F0CFF6287E +F2036D5DBBB3B91B92F12FEBE0B61A313A4DB5A9CF0BB3DDB781A56FEBFFACCB +8CB9D1D3DBDBC4CB6AAE6769E470582403CB920630221B68BCB625CD4605FA8F +D3D5B7A1A28D15E44B38E92E906C138E72C15B86F64C38E23BF0440052A8C914 +54397F49DBED99D0AF7CEA3B0A05FF37C2D7EAE1412567E6776333237C31E3C0 +49949EC8BFD6E0F6446CE2D4DCD2C1524A288818CC5D159BF8463A847AE4A2B9 +CC8C58F822804B81B13BF4F2DEB6229C4F51F093075581791D02C36A13B855A0 +34900AA7CD4F1A797652656FE3A8425A38F421C4CC0ACA1CDD44FA6B31219276 +1CDE1CD63D6A58CE705CB56CCA1260F9B86E989019071563A9B4C274A87558CA +6EF1660D574EDA276801F0057740E2C3B80D253D697736484D892CE1AB128B8A +DECD69712F5E70E895FBAA927E8194D792A04AB6CE205E04E38A433BBB793FB4 +E8BBC4279D58A223C6673D909D6AFECD246E66A52F4CB35E5931D24C828489BD +4ECAF621A220D8ECF702BEB01C4FC7510197D3F6D15321EC87175ADBA6434ECD +2B5A306E91375CAD22CD94301763E4A8B981472890422C5488FCD523C9CB17DC +ED22FBF12D5F7525D0D6BCFE8CE85B0DFB1D6F989C267FFBA0A996D309E4A934 +3DB54A9D29C88B9D55D7300DA3D46419256C5A07A2A529A8DE8BD1727281F5FE +97033D861E0531B14E811378EC1AF1CC7EE9BA2B07D935843D3053F673979F8C +FAFD59D555B56CE338F606747238B22BD62C42BB7238FEA335678D474A643570 +A9E7B4970E8C541CE9DBC7BF70ED7BA33639D6744A18379455029E934C95E2EF +639C4848CE9A0879B51649FAB023A71782444B451F92A34CB8A124270CCF86D4 +D18EEF5C1D2B2A29012613851C49F50702D63BACF95EE2AB4D72B375E0A62615 +E0991E130A67ECBA9E05329B740708F1CB148724C3A6E5E3AEC1F88EBCA398D2 +1CA8827C977D72734310233176D1AE26C55CF2CEACA62223315C28FCF6305C7E +A22414D4739A059F552F1F9372CCCA5FED4F9AC987942848EB498900269511F3 +F408CBEA0659B954F5F1B18AE4FB270213646F9B28AE4439D2BA2D3E0AAAA780 +5E530E4EFC8A060EB979E12191044509DA0C14397AFF949E12DC970658D5EAF5 +4EA963F5BC1407A32F3837CA6A24B7F3D60EB8E6222B702E25ED903F9D21AE50 +664A095009BDEAF4B78DAF94E5A55D48366CABF07791A1684B2F54EA69070844 +4F031AF8DF416C2D3679F8BA038B0DC9DD0400CA6B34667BCBBC07E62C1668A8 +35A8C57C9048A7227E672E89681B54D662079A189A9E96A3CA96D8DD10189B04 +1DA49BA2729F1CA585B1BD5C467295285D52E47CA904235A1A3E48EFAE9EB6F6 +01374125CE89D53C276858668CF45D2F092DDCAA52418E0BB94C2B8266B4D88A +5D911507BB1DDA3D8F6E7C14A91CA11AE799EC42E993098E18CADA70BD2A1D82 +2C39326C6E3F9E84CD9758B9AE43D79BF99E6A0CD713E95B3D9B7DB90D127DE0 +DAFEBF850CAAACBD860B5DEF2082F1ADA64B44B193C4A1417BE221FDCA36456C +BE5934C8CE3ED55AE3A11697C2D682B7D0F72D48976451D205783BE25DBD2507 +39C14FFB4BB828DFD187104F38A7F11D5F0698C11E8C1D4F107CACE573FDC4B1 +C56FDAE47024D6FD16A2FEABB434CA320300FC4B6C1B6CA08F76C60B7C08A665 +99F404DBA8A2A1EB18EF6750E4EC186E31561A3F080BA6562967546715859481 +7BA782940F5C5D06626D6F6A412CA7C13820EC7C1DF23E15E5829F698CF617BE +D940523E4EE4ADECEC48C24297DBAD528BA1DCE7AC335A1D15D55415B108EFC8 +6D45030D27B3EA63B2B4CD771DBE66AE0218ABB1153D4B7482289D1313CEF184 +5C960B1E3C3C953912CC6F4521D1E15636C1545EEE457EFB87B88C9E43CC2F38 +6BC4BC96969F4FF28ABB06F4454C01CEF1B6DC538F1E832FC1666D977E5A881B +F72F1B4C7DD4BE167A5535F1163A0706F9A0B26400178DF8A128FB5EBE6A7B81 +E478AD183EC06622B591337B9F1872AAEA356F4FC67EE767B34CB5A4D90702D9 +39FB846947F4096FB3DCF16EC81455164783BA0B5D723060DAFF411B68307E81 +7BEA1D9A47A5AA3D648E618C83C60F060029E6EC4D46B045FA7415BAB2AD0AA5 +ED9C729C24136F6AF61E6409C0B5CA760B16225641E268A68CFB8260BBEAFC77 +6626EBD97195E77CAB425CFB0096D805D9EE699E41680D095AE9FA10122A7882 +2F00F495C9EB2102DF0D3E61833BC0A2E468C5CF7AB430FDB7C0BE3DF2C0D230 +1580BAA25D65F599378D873165482A1FBB224AEA89C6BCCFBDBA42AE1C5DCF41 +06969F585CD3B737D1388D6359F5468D88FCD2279BDB270F6A858FB7D2ABDEFE +5EE8FB79FA437F8F50237B92C307B73B0DCB808D07A9C3255CB9B3B17039CE5A +288103D05D132863FB522A02CEE3839EF9AF7F07D99732F0B8B384745369FB3E +7901166478F4A16076A1504C5E98D17408494E270BBF4470ED12B4332422679F +759F1D93984D7E506D16950DB6C2682FE1379EFFA6F6C95DD71F6E55BE3EF6AF +E0CB25388EEB436E6527806FC75484133F6E561DEB979D5C1FFEFDAF2A6D964E +03BAE0BD593C2992AD84569C81050F7A793C5263E50C2F50B98C4CC703EAE17A +6AEDAACE312DAFAF5278D125B6EFC5587484F61DAFF46B87B7C9B1EEDECA4859 +314A9A9E2248467DE1E54D90DD671660B9040B3E0DD982260822177EFD757266 +74A16C83A7FB168016A320D3DF3BD7726F1F4EC90EE5DFE810C96B099FD4368D +906AE4699049EFD37E8EF058D4B97BF71106445AADD4FC6E90615A0066823A36 +673B8DE32322BBE861AE251226B4385AB28702831270DBD25D666FBB0AD7B96E +A44E891EA1EAF0F87013AFC982E33D67A28E96E0C9CB99B9E4192536830D9901 +931A8CAFA41289633B20BA3BD7AA3414B6DA8D57CCF2FBE39920CC06361F075B +CC40335DB9A0071CFF77F6B7BB47F3100DBDC9C4A58C2B81EC99E8E966AF3390 +E3FBCC28BA1D79961C8A1584266454DF772FBA99664D74D4A89FC82FFEDFCFE1 +4C9E4A04291E803D142E37E7ACA66AB279378F2F192FFB2B5BBAD18B95F03136 +2CB594A3D6D3F8576B90A6C4DAD6D6C8EE07AF682F925F01D0B26CBA347C03BE +F3B0585CF4539FDC66915E22117078CC94D621F31DCB3E021998A5D6EE94CA4B +E214D07517283D56973D8E4367392BF6C1150DEBF459D141AE0941C1C8C5CFBE +E735D796E365A1B0F60BB4CF2801EAFE4889EE5F338D3C4885368281B3C95CCE +251C28A90D318A8A0384439B38D63B94757252062EA44E88509FDD2E75FAAB71 +7329622828B2785C1A8B26351BC7448C1719C88FE99BCB73F7DEA427FBFCDF4F +00EE079B0C712F7D2C8DF98D4830A9D8C8B70A8C5D54BC1DF3171135278BED55 +DA1CF0E696B12935EB59B606AA3C0CC50C7A259AF32F4C81D9B39470D9993CBC +8644403D2833B34AF40511654F96BCE0064860F39849DC62E4A0693F75308FF5 +FF450EC05F142D5E300D3850C66D432BA581D5D8DFA97D901589C53181CB5057 +7FA8B2C95DF751A861DD1A614B2F32ABD284607F40B3462FB05C79DCBB586B43 +B853D7F77AF5ED7AF2A913FB2CE66241C5102CAEC3992BEEF19A4F760E98EAF5 +063E373A084CD6829DC18D063618B4AE443B35B63BFA956368397A69D41DD715 +6C5ED849CFCB2825CCB577C1669626E2C87DE60D69559B3CB98FAA07F1893EE9 +DAAAE01D549A252D77E79AD37BF64540DAF7FDF2FDF4017F94D9D89F1B6F249B +CAAC5BF817A1979163F3A316640E382B501A6BE18C334DED469CA5643FD07D8F +6E37275108F7168F45BFB5024D4FFBDB140741D2C8AA4AF19F22592587A05FF0 +A324A0DD415F312D653513ADE6EDE9E560BA16573295F6B3A0427A15A585A40D +5265A4CB711011332AAE28B05BE6E4B11B1B3C178225B2108D94089E6544907F +DDC2496C2624F6F7E45CD885C5E032FFEA7C337BE414E421FF8AD3D0959B08F6 +6B8931D93180F66A13E86ABB8C7F755D67838B8E8B5A35E6BF9BF3098535D75A +3A8880D5E2B1D9C87CDF16AE86047B3D6CB83D9A7A3702A2428BBA8209D50F64 +7728F58AFB5CE3F8E8387EE3F2ABFBA17B3F0E6D549E7C6D13BFC785759F32C5 +B1C8A525B1CED15790CF637B59BAD929E3E3E52223066404D53CEC7C42236ED0 +E509521FAD3CCADF68D695660BC6A6F743E66DDE0A4A395F7F17F5D533E82963 +B831C3F3A7BDC6A6F5F62F5F7F9A3C75C12C7E401DCAB10411FB36B96894B6B7 +765205D82B57A53D0148BCB96CE67468ED168A7EE08120D79E30FEDB84C86D4D +1A103694E049DB8929F2E389124FBA0DBA9E4CC099AE441600E63F70978D0C2E +0B21352A2113D96B50649491592406CF1EE84B5AD03265040AB41503D2FBE78C +1B0726B6027551614D756C17F939CBC1FD3EE28B99BC31FF9BB31C292002F1BF +67BA8B632D158A299E13335EF69FEDFBD28BBF5773AD17D0ED5623F1E578AEA4 +FF9F8E856950CC7A10CA01238A7371B619AE8C08F51628F39E7173F60745F218 +8401AE34EBF1BD24B442988AE5DCE4EC0DA8F922D3DBFB6E2C47B27FBF000735 +2C6EFBACD68367409253A4DDBB04501562CC2543F368881E5062A25F890C7711 +1A6C3C5AD462F825F1D340D8AED4AF056CC26E592C0337EA2F1DCFDD91F00F82 +11642DE4E37B381ADD4976C0C850E31B6EF51DD65E1DECC56BAF0D22F7A4EB91 +276AB6567430CD58BB1F3F685149BC5AD0083EBD3AD39976C921024B55FC6DDD +3478009D95A97CB0F2A1FF263C72291E18515F5CC3BB29A6221395B2F35826AA +AA4737FDC0FC2D92581F94F9BCE5D19845D63A6ED2CB38988BA640D58DE8CE81 +01F19825E14967E7A55169B3CAC1DABDD00842BC207CDC793D00147328BBED17 +149ECEC72C43393CC83C631D0D28F471170004020424DD239C2AC9F94F2FABE7 +64897C9DC88630F7BF75C57F4AAF4D3ECFA5F9FB8D26B63043DA049712C6FE4E +B0FF5CAE2FC836E039D8B86D8EE8C8572A5A86DC4B174C826D4766165D19889D +4DB469E5C2EF55D8CDE91101660FCE91FE3DCFC3E1A07E4DE822D40D801D30B1 +357C3432A6CCE1D12F8744ED2863C2466291818516B1E6D7A194951D56CDCDD0 +C9B584109998C520F77AA5036DFF5A96D446083741766ADBEEAF795E282850F1 +8C898E8F37C083280929534199C14B7B552BDCECB7055E177EE41030FD3A1CCF +760E35553432256BFE41FBDA131BA12C40C5F7D9DCA52F5EE23CD23B496822E9 +B21788714891F9DB6545C649F44580845F0A4FA80E73FB8DA6D0FC305210A825 +5DF2373418EE13A579F1220C70899B2E62D89D13286C05F5D528A3CB3F59C131 +1A1AF5C8284CF9950AF7366D8A74D8331365679DE5E7CDFD746B9A4A6BBDD784 +11D194A831BD236F58E72C5616F83D8F5C1B8D4599BA7A9E0ECB0CB937C9F2E6 +72137F55E1B5E0B69A5316CDA1E30D1E71543DB184C2E548F1DF919F519EC7B7 +BEDA27431534700371469A49A9C05D6FABB7A8C644DE5DF971F9A24136315988 +08E0F91DBA758A23E515E9F684CF2C0A7AD89DB843535B0E997279491F908C00 +F74B44248A555F141201EC7FB955035FC8F3399F07B6CD6533FDB8CC5D17CBF0 +E1965CAF317CEEDE31EBA1C6E0DC5C4E9D5A481B25B04AB440CFF283A8553117 +26073A68E158806140838FFF0514FD20B90E7E8F11F5616A8373AADD91993E70 +7C32B6E22C2CAFC254B52413A107BF5B596BAF902B8D616FBA9B877B857DA390 +49F5887F6C75EA22E2C1A23A50CB0EC9013F7EA391C8922ED288DA4ED0C81666 +7516E7E3F7F3B3373C705FDF1C081D047168874FEA3887BCDFA1A09BA01C45E3 +02E6AAC33B6CDC29A962B53429412C3D66F1E02B41F17FE2382DF77C1684B0D3 +E51503D325E952136D61A9EEF500AB67EE667FF07F36234721A11BA9B4017D9A +660E622E62C3475A1C98D66300E1FDB8482A076F1723C4518B79BBCB1DC80EE2 +D09164A1DCF844D57357F1D6589EF03BB4B1BFF0C177615608C2A0C807FEA4D4 +DC6A82081B8032DE5AF3E1EEFFD04AAC59E875D13C10DD240A21B4F175A5CDDB +CC03DCF36B6022D43B58232347B555C67BABD3EE2A40F3897F1D8835371FDBD6 +E426E6B04531458D723247F0BF5A617AE7B4C8EF396B04CF1A4720F1EA94B0F2 +C2454DC34DAB3FCC3302E6BA45B2FCA41AB79B36F142A6A7953943B44606B0C7 +EBFD09516EFE933F952196793B75CE75E57BC7624179282A410C80D676617862 +F9C56B4DACFD70645D35FFC78868514279C15E8AAFE06FBE2A5ED6C2DCBC742C +4B7209B9EAF2EA3B2E72B9F3B14ACC3B319B1D62DF16326E75953EDC48EFCD91 +1C30A634A48F72896B7B47BA02C30369B8F9EE380BCBDD4F20A1C8FCACE8AE52 +3B68836DF8A9543D153B5E77F40AC0360A6E2B7F1FA0D0F1B319E79663F450F5 +7035DA505D9A3DD59A9E8CD6C12E185D852C92A08DD15D260DBA3E67C09DC15F +26CD796ADF01F3119AED62E359A48AB069BE0EA117C5B0FEF0DC96C25E2C18CA +5B8FB7B662817E90756AF11DEEA5883D9FAE82653A5E65D52D45E3233960F713 +75426BD675398F37F07DBF89FC0F3AE320D947C87A2B10BB524C5470D10BFFF9 +4A4E2DA7806785CDE96281ABD608EE815051EA20F8E57ED173F89C206A2B09AF +45882A9DDBAFB0017E80A7700C391E4FD27977185A8B4322EC38AA33C2D93ECB +5620B390317A8F1B09CDAD182600A6698CF245A4D45B5B76A4D08E7E5E869CC2 +E4CA0981664C32ECF2F1822911E071C63BBC66158FD7D7812296539BA7C31675 +C4B716C873FD80F29BEA2716AC280A53F9DE61EC5F3845928632F4DF438E68DB +17787FE2CC03E85EF2B1BABFBAF651A40A88E360EA60CC04A7A8D1B91B12F872 +1A3A61902E0CAB263F76A76F79616A2A596B25C59F645A071D806F3C39F1535C +CB185DB05E7C563A6DC4624FD1A83AF7FC0D7133726B07D1F278270C5A9D9BBE +E69772974A459A2FCA1CCCE2ED308B81492E93A7780FB2B3C22D0E5D68FB74CA +94014661F0339E9E1A40B1D04B845A4D2714CFB1875951BA0A8E6A07A25663CE +13FB5E8CF4F999B020D19D1E5FE24369E3AA6359C3B3CCAAC604F1736CBB20B9 +9AF2E6F4713FC58FAF9E38C4C6B474F2204D3A77C55D2E45CF46E997D17B5C60 +08B4B17316610E9BC17E50C796BC3523A9D83E38D854B81EFD29349816620A75 +6CA29721D678846BC6C65502483750B28EB30D34B2FDA6BC6BAF3B2B28F516B3 +975711BCACBED6266EDCE6B20C412AC54E39B1423FC67A3D59F64348D98CEAA6 +9A34F681AB20F6C06DB63F3F387D156F9D1546488B7E05AB638F07B6605F250C +07FE80094577650D6A3F82969F5BBB6BAF26D528024CE229ABB64B9CBFF6AE79 +F0F968DB81D9CC59778086538A046C1B940420835F784E0145354CE8DEFBB97A +F75725D14EF908D955AC950C499E633035383ED226E3A3224FEE7AD9165C69D0 +09868B1309C04C1ED26B6A7D33055D00BAC28F1E5CAC7262445D7D874E36956A +24FBD3B8DBAC9A060E87E0D036FC9665322AEFD997AD21C556AB21C203669838 +CBB12A3D0802535BAC9DBFF126237FCBA2D4B60883088FD383BB87E9958C52EA +A3D600918105C4CDF1845667CF685341B805F1A7E1CDB16A09691383F1855A10 +E2678B1C4C7307234A6285B56CDE41DCE65FCAF3BBDE54CEA5A52CE57E11DFBF +69D2E8DF226E83E8EC0EF0E60363DAE304F2498F905D1DABA516CF74A5A8E2A1 +584E01A6EDBD034C8EDE1A82EC3E280FEB59E2E6369FEBFD2BC4CD8D05667459 +0C3697F3E3BAD563678E81ADF67288DD32CAFB65AA99783F8F6A63E2E07197A2 +7209F6A11F381A7545A1BA05B7BB392C111309D0A63B2873EF684ACDE33C5FCC +944AFEAA6E8E69A5E44A9A7B2117684F58A97A9131BF06BA09C217DC261E2244 +26829868822AA9359994B391B27788995028666A29D5FD68532C677E59117567 +627A4DF9145AF22F806494FFC27269CAB8B08C51D7629298F50A091F41748857 +B385FBB3CED7191A19DC5E4D4FB2BA9D37FDA9C768523EC1247EBD9B98B2625B +FB80CABCD03F5A3C930F41E86F2D4EEB994CA2C18DD995B5459CDE2A6ABCF256 +1726283B41C4EB205BBCFD67CD2562E2A871944035155E7DFEC876F4B7F6FF76 +71762B5DDCFBA9E11C510B9FC9908D6663D901D68C17E081C34167BEF07BB2A6 +1B6A40FCFFE464D62E5C6F628ECFEE53A22A30C543E2BD840196F8D7DBDCA800 +5BCF5F7528F3D39E0D60348B70EFE43FAEBE2E0D47DE0E4E7844270A99CC889C +1E4A9267FCBDA85EF72549C844DA97004D20FB179DBB8EDA1B3FDD92A1AE86D2 +3CAB75084B7D4EE6A27D3DC3BCE432FC2993CE2932A2E89899A4A077269FFAB8 +7318902EE9A9C9505955B813FA1F6171A1D35AB2CF77138A6D9A1A41423D03F3 +7F7600892835B1FA16A74AFB67F025842B0F91F6E4AB53B0BD8566263367A561 +415721A652D2C3412A1EFEAC8AED986697F8244AD17B07C957F928BCCC1BD72B +4DB1E66A93B44CA2A30C2E46FC57CF4D55766F66EF5AC705CADE7BD8578D3BB5 +EF47101AD8ABA57CCADAC9C53B99815E08882E77E02730130363EA2EF59A7253 +E8312A325FDC1EB6BD747E32081AF6880A271A5B1AFA8E1C4D071E640A5BA92C +95614EF4EA5E9185633E267ADB2E4586892C9C567CDADBB769BCF198C25285E2 +2E20A533129F47501D65638D29FAF9ADAA602844E15DDBD03582F0A6CF157C0C +FE50F537E67620069A36B7B91710F188F23E52FD74F0957603B9245A31526000 +9233FDE0B38C481E1A53B8DBBF2FE2A8CBEEC83B78149E526FC0CD89F0DAAC20 +608444AE6E75B19DE1C9B5CB3988CD8F362B7BDA46D806C97DF1028D8B5B25E4 +A61E68F3DF8AF978D2ED0A25B1061CC574E4D29E005C9F35A0DA574A2C3EE7D7 +CF8D40CB651BD13043E4506A5B8B9C56FDEAED8FBA4046C3E642F83C9CF03A3C +8FF90BB5296B4AE8C6E0088367E0125B37DF11D02F6D2A639E1FD66B020CFE42 +518757C384D99C90EF1B0D81D9B53BD3DE3BF0BA0B903FA03EA9604BAA44D2BE +A1951EEE0ECF5EA0AF51A623EEFEAD415C924443F9886C45C8994723EC77E907 +DF43FA7E2F190F4B70436AFFB925EDE9109D9864283D3CAF5125A7ABDC37BA30 +A96395683A19F42B9C12EE3C21EA6410022600836AE036D88C256CABDC6EF2DA +D937BE4F77DA3ED55B40B6933EC6E9221476DFFFA64A0188DDA74A21D2532C00 +4F779ECCB83DC5C87B5208A118AD263F5A510470B5C385D274B35443F5C06ED2 +CB93084BCF486F753F84F4C01FDF84B17C0BF80310F89160EA4D1DC1A52944E0 +555DFF4BA876F5889EB9AADB23DD2024756539AEA0325E223140975B78F6EE80 +B581C290A3420F9A1B8BB21523552A9E62F08FE0BCBF7BDE0C56297C56DBB1BE +8A35FFD3110CBF8F7747A4CFA6030D3D196285DFF0CDD072EE70029F2325CEB3 +8088F15BE649C2CEDF8D67F72DA6A9A1D690ED77929881CB26D4C3846B17CA6B +DD4C05FEE4C423F823703540AAC656822889FD2E38290D4528EA6AD80E25F641 +D5D776055109EF86115303DFAEB310E68C561AF086E5DC702F120142CE4A7D42 +DE893A73886C7BF83766B8A1E09C2D166AEBF0AA4275DA7DAB2E7DE262A23AB4 +89284B834A2000C1C807707D5630AD29549A52FF52077E5446D07CA8A24394E9 +2CBFF4EFB16B30AEEF0B944525140DEA2D469507AC1F15BA263FDB122C036F86 +00B4E36B14638DD89336C2FC82CF7350E5A5E1BBE3C548241FA4F06F5F86F5E0 +8A906E192FB9997A16CF2ADDF70746FA566266FDE5D1C1B91E2CD581F9794D5F +94D1CBFA8CC180A4E19DFA42141548F528DA3B0014F1D9624D5E70B149BE6CE1 +EDEA0DBB26BE07DF735113650984BEC3FA70A36E6F2E4E3C4FF5DA234EB54F4C +A78A041A144C9B2443699CE8BB85E474869B5225BC8D0F4506A4268D145329DB +91FBEE574312F0C7BE9923A47A7B55A6448710F206F3A228B1ADF7C8348A3803 +84286BDE38BB139030D26F190CCC768581AA72EC3A0092814536BAA00D094FD5 +12DD9E356F1CC5C3C29101945236DAF39C27F0B4A11C90BEE90AF69777019AC1 +A2F4B442D6E3EB923F7F4CFCFA234FE41EC91F03B5A92CC5258690944DB38016 +5F356EB3DF908675CC724366DE229766E3D78E1778C8C7C0D12720744F510D8A +49BF32EAB3BACA019189E4ED61437D8C76BD97B10AA0B900DCA5B54A7149FE39 +25399EA05A83F57C8E54C362B0CBFEC272980D5681641839DFFF911FC1509110 +3E00C252B495DF08D0300BE1C4D84025ABE4F968A7D6E953930884C22638AC35 +B7B94C071496017C150A252B22387DF4D23F042CC517D77AE42F8ABAA9B5C7B9 +D6BFB7701AA8CE5B44A3FA8F58737C55E8C217A4BAE45E683A985215318A7A3A +CDC54AB53E829A539D44582A1CA8A4BD21AFDF42DDA691F48B1CB0AEE9BE1338 +FC641F20C9910985DAA523FE050774364C5B4376D74EC0EA9A1270ABC4D76ECD +990C5EC8DC9056D180EAFBA13E99C6D77144E4E06DF2B3E10B9EB1E0B503176A +2447CB9FFB483F6364850720211C653774AEC3660F1B2BAA482AC819E18A6D16 +851E534AAB2F1444486DFD9DFC5ACEFC0E9345D64C654CA707CE1618DC6BE3FD +98C5EF080215B0CB8A514C81258C1BE2EE1010AEC7B204CD1D0DD11FCD427849 +DE3B036C8A719D47EB18EC5C9707E0256CD19F39CFE05822FBFBF63428F4254D +3DE1C18ECA322A3D44D5D30100040ED28F3F820A2B3A15D3B5F6833888114BD9 +88DCFB58D2369A15C488F5808AD5E13C992278AE204AC187E6D1627365E9F141 +CC3CD4193AF378E033F6C91E37DE65E19AF7BBC215D7074EFB6349E3F406B5F0 +90521CBF66E343141487D102D18F5C06330C47CC701AE4FCE6EC297F6B5826FA +C76F9CE853800AC96E07D9EDF0FCBF28291A1B543015F3E32D6D2D673FC46FE4 +3EA144B6E9915FBDE8C91E846779FFB93EF69E54758F6F368E58EF4A1EE9576E +25EB201C3802ACA4B737FAF0B92EEAF523C7C234C94AA37EF8778992B577B146 +483F0A0D3B6B6D7691DD1DA6EB8E21514E6B9F40D95B923DDFC475187E5123A3 +73C85829FC33C96D86FD8B0276CC4C56292B781AD317E5C3C9FEFDD32CD93A36 +32DEC7BA3C1EBE2E1C0898F81ADC92F6F4B72E9A1184815E8B79F8673150E70C +0BCBD7D0DA1F1021D1D4C0CBC1CCA263E84E4BD09590AEDB2563DAE8847F7FB6 +57DB2E3459085625329F3F636B6A5047D139E125D21E2D17B7037E27D7ABF80B +4B9690415BB4D69729C3F13667C3D8937E74E0887A66891FE0B0CFCAF8BC0A0C +4106A7D334F1A616A3C1899E6EC8824C806571A51434DD466DBFDBE4F019F314 +9853CA6DCA237C2A05DE93119ED1DD34F829B60FDB9F56F8B41741B0C44D636D +0CA3A0744751548355CE366331E340DC42F2E66AAA8E40FBE2E026563F2DEC29 +9709BC66DE7F5B134159DD0375405E3CE88FA97EB0D813C349AD2EE573E99736 +345CFEF2F253DEA8B5756A8FCB78BB74B19369FC053C5A8DC60C33736ACB54AD +DC52B3B94EFA4DFD838E2858372D8A310D09D1C496C8BC7399D3040310D9F099 +57940A33C69ADDD451AA37676871ECCB0BD2A6A69FCC9890D00F524C71CE1557 +D7FF74B057EE34A986DADB16BE5FB59B9B930FEAE5E5F5459E419528B283E03E +A08106480820871D59721687C404297B83B5414E2BD14C780B462FC084E2F51C +484F283CAC27B239354C0FC2FAECF5C12009D3C4BF291048FCCDD592DD81711C +C219EE04E50AD0275F4533F790599B688E1947D433619FDBA01EA446CFDEEAB0 +3A5AA71650A84AF48917598E4ECF5C7081436C604A7DE64DEB36C56B707DE3DE +D95187D6459B49670CDBB2635908DBDE705D9DD7FE3322AB1BF1BA1960E11578 +718979205CB6108A6A46A5A55286FCAD7F86556D7A49C6954FDAA232CAD227DF +D921E5A7942DEE8D1D89AFBDAD4B81E11A494AC6BB72BC6215D8FD83A27A66CB +0CE60EFAD0EF6FD4DB38252674DB0AAC08A66CB97FD3037695B811E901D4006B +E67DA576D0211E2B3BC143897A231913D1BD0CE51B44C827CC57CC8F43CAC301 +7D97913F5CBBED9F9D005E0D8A1F26CABEC36370BAA55D4975108F5C64FCAD4B +FDB1BCE523D485CE8953AFD38EA64F8D7A1DE5CEEEE43AA49DC31996A948F18B +63372E8DA264DC7493620124F2F30380ECDBB4ADF18D5787A018ABB788CDE354 +2CFFE2A4C80CF62AEDAADB693E45ADC0FB299EB8CBEEDC306E63F68DE7B2EF94 +0D1FADDF6D16B088E87105C84BEA4E42E1448F09EDBF01BEA566A1F2BE1B31F5 +0348FC7DB471BDADC389B4948FC0CA4F6546AF743F1EDF2A4EAEB530FEE2E016 +0629B457D86163B2D9D54717D6F1141CA43102F830FD6D852C3D3FB64318F31D +F929D3C64EDDE62FEB3836A54E9D4FA80975B087E8C2B17511C00E78E3870FE7 +C836FB9B585439780CE08451BDE3FE9D6C95C702F57041FE9422AA44843CEA21 +012923B3CFE053735520B025E2B7D38FD1F653FC +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +{restore}if +%%EndFont +TeXDict begin 40258431 52099146 1000 600 600 (support.dvi) +@start /Fa 255[52{}1 58.1154 /CMSY7 rf /Fb 145[50 2[43 +40[63 66[{}3 83.022 /CMMI10 rf /Fc 148[35 107[{}1 58.1154 +/CMMI7 rf /Fd 168[51 17[51 2[51 66[{}3 99.6264 /CMTT12 +rf /Fe 138[62 1[44 46 1[62 1[62 93 31 59 1[31 62 56 34 +51 1[50 62 54 13[62 25[56 56 5[56 3[31 37 45[{}22 99.6264 +/CMBX12 rf /Ff 134[40 39 1[38 45 28 1[35 1[42 42 47 68 +21 3[42 38 25 38 42 38 38 42 11[62 2[61 5[52 29[25 30 +45[{}24 83.022 /CMTI10 rf /Fg 134[44 4[32 4[42 2[23 44 +2[46 2[37 17[46 83[{}8 83.022 /CMSL10 rf /Fh 129[39 39 +1[39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 +1[39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 6[39 39 +39 1[39 10[39 1[39 39 39 39 39 39 39 39 39 39 39 39 1[39 +1[39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 1[39 +39 34[{}70 74.7198 /CMTT9 rf /Fi 208[35 35 46[{}2 66.4176 +/CMMI8 rf /Fj 150[32 32 86[65 1[42 14[65{}5 83.022 /CMSY10 +rf /Fk 134[37 1[51 1[41 25 31 32 35 39 39 43 63 20 2[24 +1[35 24 35 39 35 35 39 23[30 14[24 11[24 33[43 12[{}24 +74.7198 /CMTI9 rf /Fl 150[30 30 104[{}2 74.7198 /CMSY9 +rf /Fm 137[37 3[28 3[39 8[31 39 31 39 35 39[35 35 35 +1[35 35 35 35 35 35 2[24 3[27 27 27[39 12[{}21 66.4176 +/CMR8 rf /Fn 190[49 15[33 33 48[{}3 58.1154 /CMR7 rf +/Fo 131[83 1[37 44 44 60 44 46 32 33 33 44 46 42 46 69 +23 44 25 23 46 42 25 37 46 37 46 42 23 2[23 42 23 2[62 +85 1[62 60 46 61 2[65 62 76 52 1[43 30 62 1[54 57 63 +1[59 2[39 1[65 1[23 23 42 42 42 42 42 42 42 42 42 42 +1[23 28 23 65 1[32 32 23 4[42 21[46 48 11[{}72 83.022 +/CMR10 rf /Fp 130[44 1[44 1[44 44 44 44 44 44 44 44 1[44 +44 44 44 44 44 1[44 44 44 44 44 44 44 44 44 4[44 27[44 +12[44 44 44 2[44 10[44 35[{}32 83.022 /CMTT10 rf end +%%EndProlog +%%BeginSetup +%%Feature: *Resolution 600dpi +TeXDict begin +%%BeginPaperSize: Letter +letter +%%EndPaperSize + end +%%EndSetup +%%Page: 18 1 +TeXDict begin 18 0 bop 739 232 a Fo(18)165 b Fp(support.nw)1530 +b Fo(Decem)n(b)r(er)28 b(2,)f(2015)739 523 y Fe(2.8)112 +b(Sub-page)40 b(references)739 676 y Fo(This)34 b(is)g(the)h(w)n +(onderful)f(co)r(de)g(that)h(Da)n(v)n(e)e(Lo)n(v)n(e)g(pro)n(vided)g +(to)h(mak)n(e)g(page)f(references)739 776 y(lik)n(e)27 +b(7a,)g(7b,)g(and)h(so)f(on.)863 876 y(This)42 b(co)r(de)g(pro)n(vides) +f(a)h(mec)n(hanism)f(for)h(de\014ning)g(`page)f(sub-references')g +(using)739 975 y Fp(\\sublabel{foo})34 b Fo(referenced)k(with)j +Fp(\\subpageref{foo)o(})p Fo(.)67 b(Sub-references)38 +b(will)i(b)r(e)739 1075 y(n)n(um)n(b)r(ered)31 b(lik)n(e)g(these)g +(real)g(examples:)44 b(18a,)31 b(18b,)g(18c)g(etc.)g(unless)g(there)g +(is)h(only)f(one)739 1174 y(on)c(the)h(page,)f(in)h(whic)n(h)f(case)g +(the)h(letter)g(will)g(b)r(e)g(dropp)r(ed)f(lik)n(e)g(this:)37 +b(4b.)863 1274 y(T)-7 b(o)33 b(b)r(e)g(able)f(to)h(use)f +Fp(\\subpageref)d Fo(w)n(e)j(m)n(ust)h(de\014ne)g(the)g(lab)r(el)g +(with)g Fp(\\sublabel)p Fo(,)739 1374 y(used)h(lik)n(e)g(lab)r(el.)58 +b(\(Using)35 b Fp(\\ref)d Fo(with)j(a)g(lab)r(el)f(de\014ned)h(b)n(y)f +Fp(\\sublabel)d Fo(will)k(pro)r(duce)739 1473 y(the)27 +b(sub-reference)f(n)n(um)n(b)r(er,)h(b)n(y)f(the)h(w)n(a)n(y)-7 +b(,)26 b(and)h Fp(\\pageref)d Fo(w)n(orks)h(as)h(exp)r(ected.\))37 +b(Note)739 1573 y(that)27 b Fp(\\subpageref)c Fo(is)k(robust)f(and)h +Fp(\\ref)f Fo(and)h Fp(\\pageref)c Fo(are)k(rede\014ned)f(to)h(b)r(e)h +(robust)739 1673 y(also,)f(as)h(they)g(will)h(b)r(e)f(in)h(future)g(L) +1858 1656 y Fn(A)1895 1673 y Fo(T)1941 1690 y(E)1986 +1673 y(X)g(releases.)37 b(Inciden)n(tally)-7 b(,)29 b(these)f(expand)g +(to)g(the)739 1772 y(relev)-5 b(an)n(t)25 b(text)i(plus)f +Fp(\\null)p Fo(|y)n(ou)d(migh)n(t)j(w)n(an)n(t)g(to)g(strip)g(this)g +(o\013,)h(e.g.)f(for)f(sorting)g(lists.)863 1872 y(There)h(are)f(v)-5 +b(arious)25 b(w)n(a)n(ys)g(w)n(e)h(could)g(attac)n(k)f(this)h(task)g +(\(whic)n(h)g(is)g(made)g(non-trivial)739 1971 y(b)n(y)i(the)g(w)n +(ell-kno)n(wn)e(async)n(hron)n(y)g(of)i(\(La\)T)2163 +1989 y(E)2209 1971 y(X's)g(output)h(routine\),)f(but)g(they)g(all)g(m)n +(ust)739 2071 y(dep)r(end)e(on)g(hac)n(ks)f(in)h(the)g +Fp(.aux)f Fo(\014le)h(or)f(a)g(similar)g(one.)36 b(Joac)n(him)25 +b(Sc)n(hro)r(d's)g Fp(fnpag.sty)739 2171 y Fo(do)r(es)39 +b(the)g(same)g(sort)f(of)h(thing)h(di\013eren)n(tly)f(to)g(this)g(L) +2539 2154 y Fn(A)2576 2171 y Fo(T)2622 2189 y(E)2668 +2171 y(X-sp)r(eci\014c)h(approac)n(h.)69 b(See)739 2270 +y Fp(latex.tex)18 b Fo(for)j(enligh)n(tenmen)n(t)h(on)g(the)g +(cross-referencing)d(mec)n(hanism)i(and)h(the)g(L)3412 +2253 y Fn(A)3449 2270 y Fo(T)3495 2288 y(E)3541 2270 +y(X)739 2370 y(in)n(ternals)38 b(used)h(b)r(elo)n(w.)71 +b([DL:)40 b(The)f(in)n(ternals)f(c)n(hange)g(in)h(L)2743 +2353 y Fn(A)2780 2370 y Fo(T)2826 2388 y(E)2872 2370 +y(X2e)g(compared)f(with)739 2470 y(L)761 2453 y Fn(A)798 +2470 y Fo(T)844 2487 y(E)890 2470 y(X)28 b(2.09.)35 b(The)28 +b(co)r(de)f(here)g(still)h(w)n(orks,)e(though.])863 2569 +y(The)35 b(new-st)n(yle)e(L)1434 2552 y Fn(A)1471 2569 +y Fo(T)1517 2587 y(E)1563 2569 y(X)h(page-reference)e(macros)g(all)i(w) +n(ork)f(the)h(same)g(w)n(a)n(y:)48 b(if)35 b(the)739 +2669 y(thing)29 b(is)g(unde\014ned,)h(barf.)40 b(Otherwise,)28 +b(do)h(the)g(sp)r(eci\014ed)g(thing.)41 b(W)-7 b(e)30 +b(need)f(to)f(handle)739 2768 y(the)19 b(fact)g(that)g(the)g(expansion) +f(of)h(the)g(lab)r(el)g(ma)n(y)f(b)r(e)h(t)n(w)n(o)f(items)h(or)f +(\014v)n(e)g(items,)j(dep)r(ending)739 2868 y(on)41 b(whether)g(h)n(yp) +r(ertext)g(is)h(used.)78 b(Since)41 b(w)n(e're)g(only)g(ev)n(er)f(in)n +(terested)h(in)h(the)f(\014rst)739 2968 y(t)n(w)n(o)e(items,)k(w)n(e)d +(use)g(a)f(hac)n(k|the)h(\\do)f(the)h(sp)r(eci\014ed)g(thing")g(m)n +(ust)g(b)r(e)h(de\014ned)f(as)739 3067 y Fp(\\def\\dome#1#2#3\\)o(\\{)o +(...)o(})21 b Fo(where)28 b Fp(...)e Fo(uses)h(only)g(the)h(\014rst)g +(t)n(w)n(o)e(parameters.)449 3184 y Fm(18d)181 b Fl(h)p +Fk(noweb.sty)29 b Fm(2b)p Fl(i)p Fo(+)p Fj(\021)1906 +b Fi(/)12 b Fm(17c)48 b(18e)13 b Fi(.)822 3275 y Fh +(\\newcommand\\nw@genericref[2]{)q(\045)45 b(what)40 +b(to)g(do,)g(name)g(of)g(ref)900 3366 y(\\expandafter\\nw@g@nericref\\) +q(csnam)q(e)45 b(r@#2\\endcsname#1{#2}})822 3458 y +(\\newcommand\\nw@g@nericref[3]{)q(\045)g(control)c(sequence,)g(what)f +(to)g(do,)g(name)900 3549 y(\\ifx#1\\relax)979 3640 y(\\ref{#3}\045)h +(trigger)g(the)f(standard)h(`undefined)g(ref')g(mechanisms)900 +3731 y(\\else)979 3823 y(\\expandafter#2#1.\\\\\045)900 +3914 y(\\fi})739 4039 y Fo(Muc)n(h)25 b(of)h(what)f(w)n(e)h(w)n(an)n(t) +f(can)g(b)r(e)h(done)f(b)n(y)g(pulling)h(out)f(the)h(\014rst,)g +(second,)f(or)g(\014rst)g(and)739 4138 y(second)i(elemen)n(ts)g(of)h(a) +f(ref.)457 4255 y Fm(18e)181 b Fl(h)p Fk(noweb.sty)29 +b Fm(2b)p Fl(i)p Fo(+)p Fj(\021)1894 b Fi(/)12 b Fm(18d)48 +b(19a)13 b Fi(.)822 4346 y Fh(\\def\\nw@selectone#1#2#3\\\\{#1})822 +4437 y(\\def\\nw@selecttwo#1#2#3\\\\{#2})822 4529 y +(\\def\\nw@selectonetwo#1#2#3\\\\{)q({#1}{)q(#2}})p eop +end +%%Page: 19 2 +TeXDict begin 19 1 bop 291 232 a Fo(Decem)n(b)r(er)27 +b(2,)g(2015)1533 b Fp(support.nw)162 b Fo(19)415 523 +y(The)33 b Fp(\\subpageref)28 b Fo(macro)j(\014rst)h(do)r(es)g(a)h +(normal)e Fp(\\pageref)p Fo(.)49 b(If)32 b(the)h(reference)f(is)291 +623 y(actually)21 b(de\014ned,)j(it)f(then)g(go)r(es)f(on)g(to)g(c)n +(hec)n(k)g(whether)g(the)h(con)n(trol)e(sequence)h Fp(2on)p +Fj(h)p Ff(p)l(age)291 722 y(r)l(efer)l(enc)l(e)l(d)9 +b Fj(i)34 b Fo(is)f(de\014ned)h(and)f(sets)g(the)h Fp(\\ref)e +Fo(v)-5 b(alue)34 b(to)f(get)g Fp(a)g Fo(etc.)h(if)g(so.)54 +b(The)34 b(magic,)291 822 y(of)g(course,)h(is)f(in)g(de\014ning)h(the)f +Fp(2on)f Fo(bit)i(appropriately)-7 b(.)55 b Fp(\\subpageref)30 +b Fo(also)j(tries)h(to)291 922 y(include)28 b(the)g(righ)n(t)e(h)n(yp)r +(erstu\013)i(for)f(xhdvi.)5 1038 y Fm(19a)181 b Fl(h)p +Fk(noweb.sty)28 b Fm(2b)p Fl(i)q Fo(+)o Fj(\021)1899 +b Fi(/)12 b Fm(18e)48 b(19b)13 b Fi(.)374 1129 y Fh +(\\newcommand{\\subpageref}[1]{\045)452 1220 y +(\\nwhyperreference{#1}{\\nw@gen)q(eric)q(ref\\@)q(subp)q(ager)q(ef{#1) +q(}}})374 1312 y(\\def\\@subpageref#1#2#3\\\\{\045)452 +1403 y(\\@ifundefined{2on#2}{#2}{\\nwt)q(hepa)q(genum)q({#1})q({#2})q +(}})415 1528 y Fp(\\subpagepair)23 b Fo(pro)r(duces)k(a)g +Fp({subpage}{page})21 b Fo(pair.)1 1644 y Fm(19b)181 +b Fl(h)p Fk(noweb.sty)28 b Fm(2b)p Fl(i)q Fo(+)o Fj(\021)1903 +b Fi(/)12 b Fm(19a)48 b(19c)13 b Fi(.)374 1735 y Fh +(\\newcommand{\\subpagepair}[1]{\045)85 b(\045)39 b(produces)i +({subpage}{page})452 1826 y(\\@ifundefined{r@#1}\045)531 +1918 y({{0}{0}}\045)531 2009 y({\\nw@genericref\\@subpagepair{#1)q(}}}) +374 2100 y(\\def\\@subpagepair#1#2#3\\\\{\045)452 2192 +y(\\@ifundefined{2on#2}{{0}{#2}})q({{#1)q(}{#2})q(}})415 +2316 y Fp(\\sublabel)20 b Fo(is)j(lik)n(e)g(the)h Fp(\\label)d +Fo(command,)j(except)f(that)h(it)g(writes)f Fp(\\newsublabel)291 +2416 y Fo(on)n(to)h(the)i Fp(.aux)d Fo(\014le)j(rather)e(than)h +Fp(\\newlabel)p Fo(.)33 b(F)-7 b(or)25 b(h)n(yp)r(erreferencing,)f(all) +h(lab)r(els)g(m)n(ust)291 2516 y(b)r(e)j(h)n(yp)r(ertext)f(anc)n(hors,) +f(for)h(whic)n(h)g(w)n(e)h(use)f Fp(\\nwblindhyperanch)o(or)o +Fo(.)9 2632 y Fm(19c)181 b Fl(h)p Fk(noweb.sty)28 b Fm(2b)p +Fl(i)q Fo(+)o Fj(\021)1891 b Fi(/)12 b Fm(19b)48 b(19d)13 +b Fi(.)374 2723 y Fh(\\newcommand{\\sublabel}[1]{\045)452 +2814 y(\\leavevmode)42 b(\045)d(needed)i(to)f(make)g(\\@bsphack)h(work) +452 2906 y(\\@bsphack)452 2997 y(\\nwblindhyperanchor{#1}\045)452 +3088 y(\\if@filesw)h({\\let\\thepage\\relax)491 3180 +y(\\def\\protect{\\noexpand\\noexp)q(and\\)q(noexp)q(and})q(\045)491 +3271 y(\\edef\\@tempa{\\write\\@auxout{)q(\\str)q(ing)609 +3362 y(\\newsublabel{#1}{{}{\\thepage})q(}}}\045)491 +3454 y(\\expandafter}\\@tempa)491 3545 y(\\if@nobreak)g +(\\ifvmode\\nobreak\\fi\\fi\\fi\\@es)q(phac)q(k})415 +3670 y Fp(\\nosublabel)23 b Fo(creates)k(a)g(lab)r(el)g(with)i(a)e +(sub-page)f(part)h(of)h(0.)1 3786 y Fm(19d)181 b Fl(h)p +Fk(noweb.sty)28 b Fm(2b)p Fl(i)q Fo(+)o Fj(\021)1903 +b Fi(/)12 b Fm(19c)48 b(20a)13 b Fi(.)374 3877 y Fh +(\\newcommand{\\nosublabel}[1]{\045)452 3968 y(\\@bsphack\\if@filesw)44 +b({\\let\\thepage\\relax)491 4060 y(\\def\\protect{\\noexpand\\noexp)q +(and\\)q(noexp)q(and})q(\045)491 4151 y +(\\edef\\@tempa{\\write\\@auxout{)q(\\str)q(ing)609 4242 +y(\\newlabel{#1}{{0}{\\thepage}}})q(}\045)491 4334 y +(\\expandafter}\\@tempa)491 4425 y(\\if@nobreak)e +(\\ifvmode\\nobreak\\fi\\fi\\fi\\@es)q(phac)q(k})p eop +end +%%Page: 20 3 +TeXDict begin 20 2 bop 739 232 a Fo(20)165 b Fp(support.nw)1530 +b Fo(Decem)n(b)r(er)28 b(2,)f(2015)863 523 y Fp(\\newsublabel)d +Fo(is)j(the)i(macro)d(that)j(do)r(es)e(the)i(imp)r(ortan)n(t)e(w)n +(ork.)37 b(It)28 b(is)g(called)g(with)739 623 y(the)f(same)f(sort)f(of) +i(argumen)n(ts)e(as)h Fp(\\newlabel)p Fo(:)33 b(the)27 +b(\014rst)f(argumen)n(t)f(is)i(the)g(lab)r(el)f(name)739 +722 y(and)42 b(the)g(second)g(is)g Fp({)p Fj(h)p Ff(r)l(ef)h(value)g +Fm(\(nev)n(er)37 b(de\014ned\))p Fj(i)p Fp(}{)p Fj(h)p +Ff(p)l(age)46 b(numb)l(er)c Fm(\(nev)n(er)37 b(de\014ned\))p +Fj(i)p Fp(})p Fo(.)739 822 y(\(Note)25 b(that)g(the)h(only)e +(de\014nition)h(here)g(whic)n(h)f(needs)h(to)g(b)r(e)g(global)f(is)h +(the)g(one)f(whic)n(h)h(is,)739 922 y(and)i(that)h Fp(\\global)c +Fo(is)j(rede\014ned)g(b)n(y)g Fp(\\enddocument)p Fo(,)c(whic)n(h)k +(will)h(bite)g(y)n(ou)e(if)i(y)n(ou)e(use)739 1021 y(it.)14 +b(.)g(.)g(\))453 1137 y Fm(20a)181 b Fl(h)p Fk(noweb.sty)29 +b Fm(2b)p Fl(i)p Fo(+)p Fj(\021)1890 b Fi(/)13 b Fm(19d)47 +b(21b)13 b Fi(.)822 1229 y Fl(h)p Fk(de\014nition)28 +b(of)f Fh(\\newsublabel)j Fm(20b)p Fl(i)863 1353 y Fo(Before)19 +b(w)n(e)h(create)f(a)g Fp(\\newsublabel)c Fo(for)20 b(the)g(\014rst)g +(time,)i(w)n(e)d(set)h(the)h(prop)r(er)d(trailers.)449 +1469 y Fm(20b)181 b Fl(h)p Fk(de\014nition)28 b(of)f +Fh(\\newsublabel)j Fm(20b)p Fl(i)q Fj(\021)1341 b Fm(\(20a\))49 +b(20c)13 b Fi(.)822 1561 y Fh(\\newcommand\\newsublabel{\045)900 +1652 y(\\nw@settrailers)900 1743 y(\\global\\let\\newsublabel\\@ne)q +(wsubl)q(abel)900 1835 y(\\@newsublabel})739 1959 y Fo(First)27 +b(w)n(e)h(extract)e(the)i(page)f(n)n(um)n(b)r(er)h(in)n(to)f +Fp(\\this@page)p Fo(.)457 2076 y Fm(20c)181 b Fl(h)p +Fk(de\014nition)28 b(of)f Fh(\\newsublabel)j Fm(20b)p +Fl(i)q Fo(+)o Fj(\021)1066 b Fm(\(20a\))48 b Fi(/)13 +b Fm(20b)47 b(20d)13 b Fi(.)822 2167 y Fh +(\\newcommand{\\@newsublabel}[2])q({\045)900 2258 y +(\\edef\\this@page{\\@cdr#2\\@ni)q(l}\045)863 2383 y +Fo(Then)29 b(w)n(e)f(see)f(whether)i(it's)f(c)n(hanged)f(from)h(the)h +(v)-5 b(alue)28 b(of)g Fp(\\last@page)c Fo(whic)n(h)k(w)n(as)739 +2482 y(stashed)k(a)n(w)n(a)n(y)f(b)n(y)h(the)h(last)g +Fp(\\newsublabel)28 b Fo(\(or)k(is)g Fp(\\relax)f Fo(if)i(this)g(is)g +(the)g(\014rst)f(one\).)739 2582 y(If)f(the)g(page)f(has)g(c)n(hanged,) +g(w)n(e)g(reset)g(the)h(coun)n(ter)f Fp(\\sub@page)d +Fo(telling)j(us)h(ho)n(w)f(man)n(y)739 2682 y(sub-lab)r(els)d(there)g +(ha)n(v)n(e)g(b)r(een)h(on)f(the)h(page.)449 2798 y Fm(20d)181 +b Fl(h)p Fk(de\014nition)28 b(of)f Fh(\\newsublabel)j +Fm(20b)p Fl(i)q Fo(+)o Fj(\021)1081 b Fm(\(20a\))49 b +Fi(/)12 b Fm(20c)48 b(20e)13 b Fi(.)900 2889 y Fh +(\\ifx\\this@page\\last@page\\el)q(se)979 2980 y(\\sub@page=\\z@)900 +3072 y(\\fi)900 3163 y(\\edef\\last@page{\\this@page})900 +3254 y(\\advance\\sub@page)43 b(by)d(\\@ne)863 3379 y +Fo(If)28 b(w)n(e'v)n(e)e(had)h(at)g(least)f(t)n(w)n(o)h(on)f(the)i +(page,)e(w)n(e)h(de\014ne)g(the)g Fp(2on)p Fj(h)p Ff(p)l(age)j(no.)p +Fj(i)e Fo(macro)d(to)739 3479 y(indicate)j(the)g(fact.)457 +3595 y Fm(20e)181 b Fl(h)p Fk(de\014nition)28 b(of)f +Fh(\\newsublabel)j Fm(20b)p Fl(i)q Fo(+)o Fj(\021)1070 +b Fm(\(20a\))48 b Fi(/)12 b Fm(20d)48 b(21a)13 b Fi(.)900 +3686 y Fh(\\ifnum\\sub@page=\\tw@)979 3778 y +(\\global\\@namedef{2on\\this@page)q(}{}\045)900 3869 +y(\\fi)p eop end +%%Page: 21 4 +TeXDict begin 21 3 bop 291 232 a Fo(Decem)n(b)r(er)27 +b(2,)g(2015)1533 b Fp(support.nw)162 b Fo(21)415 523 +y(Then)36 b(w)n(e)f(write)g(a)g(normal)f Fp(\\newlabel)e +Fo(with)k(the)f(sub-reference)f(as)h(the)h(normal)291 +623 y(reference)d(v)-5 b(alue)34 b(in)h(the)g(second)f(argumen)n(t.)56 +b(Unfortunately)-7 b(,)36 b(if)f(w)n(e)f(w)n(an)n(t)g(h)n(yp)r(ertext) +291 722 y(supp)r(ort,)27 b(the)h(second)f(argumen)n(t)g(of)g +Fp(\\newlabel)d Fo(gets)j(complicated.)37 b(It)28 b(is)f(either)415 +888 y Fj(\017)41 b Fp({)p Fj(h)p Ff(r)l(ef)f(value)h +Fm(\(nev)n(er)34 b(de\014ned\))p Fj(i)p Fp(}{)p Fj(h)p +Ff(p)l(age)43 b(numb)l(er)c Fm(\(nev)n(er)34 b(de\014ned\))p +Fj(i)p Fp(})p Fo(,)44 b(when)39 b(normal)498 988 y(L)520 +971 y Fn(A)557 988 y Fo(T)603 1006 y(E)649 988 y(X)28 +b(is)f(running,)h(or)415 1154 y Fj(\017)41 b Fp({)p Fj(h)p +Ff(r)l(ef)d(value)g Fm(\(nev)n(er)31 b(de\014ned\))p +Fj(i)p Fp(}{)p Fj(h)p Ff(p)l(age)41 b(numb)l(er)36 b +Fm(\(nev)n(er)c(de\014ned\))p Fj(i)p Fp(}{)p Fj(h)p Ff(text)38 +b Fm(\(nev)n(er)32 b(de-)498 1254 y(\014ned\))p Fj(i)p +Fp(}}{)p Fj(h)p Ff(hyp)l(er)f(c)l(ate)l(gory)g Fm(\(nev)n(er)24 +b(de\014ned\))p Fj(i)p Fp(}{)p Fj(h)p Ff(URL)30 b Fm(\(nev)n(er)25 +b(de\014ned\))p Fj(i)p Fp(})p Fo(,)k(when)f(the)498 1353 +y Fp(hyperref)c Fo(pac)n(k)-5 b(age)26 b(is)h(running.)36 +b(\(W)-7 b(e)28 b(actually)f(detect)g(this)h(b)n(y)f(lo)r(oking)f(for)h +(the)498 1453 y Fp(nameref)e Fo(pac)n(k)-5 b(age,)26 +b(b)r(ecause)h(that's)h(the)g(one)f(that)h(c)n(hanges)e(the)i(use)g(of) +f(lab)r(els.\))291 1619 y(W)-7 b(e)26 b(unify)g(these)g(t)n(w)n(o)f +(things)h(b)n(y)f(pro)r(ducing)g Fp({)p Fj(h)p Ff(r)l(ef)k(value)f +Fm(\(nev)n(er)23 b(de\014ned\))p Fj(i)p Fp(}{)p Fj(h)p +Ff(p)l(age)31 b(num-)291 1719 y(b)l(er)e Fm(\(nev)n(er)c(de\014ned\))p +Fj(i)p Fp(}\\nw@labeltrailers)415 1818 y Fo(W)-7 b(e)29 +b(ma)n(y)g(ha)n(v)n(e)f(p)r(ending)h(lab)r(els)g(in)g(supp)r(ort)g(of)g +Fp(\\nextchunklabel)o Fo(,)24 b(as)k(de\014ned)i(in)291 +1918 y(c)n(h)n(unk)c(22a.)35 b(Because)26 b(w)n(e)g(w)n(an)n(t)g(to)g +(de\014ne)h(all)g(of)f(the)h(\\p)r(ending)g(sublab)r(els")f(in)h +(exactly)291 2017 y(the)d(same)f(w)n(a)n(y)-7 b(,)23 +b(w)n(e)h(do)f(something)h(a)f(bit)i(o)r(dd|w)n(e)e(mak)n(e)g(the)h +(curren)n(t)f(lab)r(el)h(a)g(p)r(ending)291 2117 y(lab)r(el)j(as)g(w)n +(ell.)5 2233 y Fm(21a)181 b Fl(h)p Fk(de\014nition)27 +b(of)h Fh(\\newsublabel)i Fm(20b)p Fl(i)p Fo(+)p Fj(\021)1277 +b Fm(\(20a\))49 b Fi(/)12 b Fm(20e)452 2325 y Fh +(\\pendingsublabel{#1}\045)452 2416 y +(\\edef\\@tempa##1{\\noexpand\\new)q(labe)q(l{##1)q(}\045)531 +2507 y({{\\number\\sub@page}{\\this@page})q(\\nw@l)q(abel)q(trai)q +(lers})q(}\045)452 2599 y(\\pending@sublabels)452 2690 +y(\\def\\pending@sublabels{}})415 2814 y Fo(W)-7 b(e)45 +b(can't)g(use)g Fp(\\@ifpackageloade)o(d)39 b Fo(to)45 +b(see)f(if)52 b Fp(nameref)42 b Fo(is)j(loaded,)j(b)r(ecause)291 +2914 y(that)33 b(is)g(restricted)g(to)g(the)g(pream)n(ble,)h(and)f +Fp(\\newsublabel)28 b Fo(go)r(es)33 b(in)n(to)g(the)g +Fp(.aux)f Fo(\014le,)291 3014 y(whic)n(h)27 b(is)h(executed)f(after)h +(the)g(whole)f(do)r(cumen)n(t)h(is)g(pro)r(cessed.)36 +b(W)-7 b(e)28 b(therefore)e(test)i(for)291 3113 y Fp(\\@secondoffive)p +Fo(.)j(This)c(is)h(lame,)f(but)h(what)g(else)f(can)g(w)n(e)h(do?)1 +3230 y Fm(21b)181 b Fl(h)p Fk(noweb.sty)28 b Fm(2b)p +Fl(i)q Fo(+)o Fj(\021)1899 b Fi(/)12 b Fm(20a)48 b(22a)13 +b Fi(.)374 3321 y Fh(\\newcommand\\nw@settrailers{\045)45 +b(--)40 b(won't)g(work)g(on)g(first)g(run)452 3412 y +(\\@ifpackageloaded{nameref}\045)570 3504 y +({\\gdef\\nw@labeltrailers{{}{}{})q(}}\045)570 3595 y +({\\gdef\\nw@labeltrailers{}}})374 3686 y +(\\renewcommand\\nw@settrailers{\045)452 3778 y +(\\@ifundefined{@secondoffive}\045)570 3869 y +({\\gdef\\nw@labeltrailers{}}\045)570 3960 y +({\\gdef\\nw@labeltrailers{{}{}{})q(}}})p eop end +%%Page: 22 5 +TeXDict begin 22 4 bop 739 232 a Fo(22)165 b Fp(support.nw)1530 +b Fo(Decem)n(b)r(er)28 b(2,)f(2015)863 523 y(No)n(w)f(w)n(e)g(k)n(eep)g +(trac)n(k)f(of)h(those)g(p)r(ending)h(guys.)36 b(The)26 +b(goal)f(here)h(is)g(to)g(sa)n(v)n(e)f(them)i(up)739 +623 y(un)n(til)j(they're)f(all)g(equiv)-5 b(alen)n(t)29 +b(to)g(the)h(lab)r(el)f(on)g(the)h(next)f(c)n(h)n(unk.)42 +b(W)-7 b(e)29 b(ha)n(v)n(e)f(to)i(con)n(trol)739 722 +y(expansion)c(so)h(c)n(h)n(unks)g(lik)n(e)h(21a)e(\(21a\))h(can)g(b)r +(e)h(lab)r(elled)f(t)n(wice.)453 839 y Fm(22a)181 b Fl(h)p +Fk(noweb.sty)29 b Fm(2b)p Fl(i)p Fo(+)p Fj(\021)1898 +b Fi(/)12 b Fm(21b)48 b(22c)13 b Fi(.)822 930 y Fh +(\\newcommand{\\nextchunklabel}[)q(1]{\045)900 1021 y +(\\nwblindhyperanchor{#1}\045)124 b(\045)39 b(looks)h(slightly)h(bogus) +g(---)f(nr)900 1112 y(\\@bsphack\\if@filesw)k({\\let\\thepage\\relax) +1057 1204 y(\\edef\\@tempa{\\write\\@auxout{)q(\\str)q(ing\\p)q(endi)q +(ngsu)q(blabe)q(l{#1)q(}}}\045)1057 1295 y(\\expandafter}\\@tempa)940 +1386 y(\\if@nobreak)d(\\ifvmode\\nobreak\\fi\\fi\\fi\\@e)q(spha)q(ck}) +822 1478 y(\\newcommand\\pendingsublabel[1)q(]{\045)900 +1569 y(\\def\\@tempa{\\noexpand\\@temp)q(a}\045)900 1660 +y(\\edef\\pending@sublabels{\\no)q(expan)q(d\\@t)q(empa{)q(#1}\\)q +(pend)q(ing@s)q(ubla)q(bels})q(})822 1752 y(\\def\\pending@sublabels{}) +449 1910 y Fm(22b)181 b Fl(h)p Fk(man)27 b(p)l(age:)h(noweb)h(style)f +(c)l(ontr)l(ol)h(se)l(quenc)l(es)h Fm(22b)p Fl(i)q Fj(\021)1099 +b Fm(32b)13 b Fi(.)822 2001 y Fh(.PP)40 b(\\")g(.TP)f(will)i(not)e +(work)i(with)f(the)g(backslashes)h(on)f(the)g(next)g(line.)g(Period.) +822 2092 y(\\fB\\\\nextchunklabel{l}\\fP)822 2183 y(.RS)822 +2275 y(Associates)h(label)g(\\fBl\\fP)822 2366 y(with)f(the)g(sub-page) +h(reference)g(of)f(the)g(next)g(code)g(chunk.)822 2457 +y(Can)g(be)g(used)g(in)f(for)h(concise)h(chunk)f(cross-reference)j +(with,)d(e.g.,)822 2549 y(\\fBchunk~\\\\subpageref{l}\\fP.)822 +2640 y(.RE)863 2781 y Fo(W)-7 b(e)28 b(need)g(to)g(de\014ne)f(these.) +457 2897 y Fm(22c)181 b Fl(h)p Fk(noweb.sty)29 b Fm(2b)p +Fl(i)p Fo(+)p Fj(\021)1898 b Fi(/)12 b Fm(22a)48 b(23a)13 +b Fi(.)822 2989 y Fh(\\def\\last@page{\\relax})822 3080 +y(\\newcount\\sub@page)863 3205 y Fo(W)-7 b(e)24 b(no)e(longer)g(use)h +(Rainer's)f(new)h(expandable)f(de\014nitions)h(of)g Fp(\\ref)f +Fo(and)g Fp(\\pageref)739 3304 y Fo(to)g(minimise)h(the)g(risk)f(of)g +(nast)n(y)g(surprises;)g(enough)g(time)h(has)f(elapsed)g(that)h(this)f +(should)739 3404 y(no)27 b(longer)g(b)r(e)h(necessary)-7 +b(.)449 3520 y Fm(22d)181 b Fl(h)p Fk(old)27 b(noweb.sty)i +Fm(22d)p Fl(i)q Fj(\021)822 3611 y Fh(\045)39 b(RmS)h(92/08/14:)i(made) +e(\\ref)g(and)g(\\pageref)h(robust)822 3703 y +(\\def\\ref#1{\\@ifundefined{r@#1)q(}{{\\b)q(f)k(??})p +Fl(h)p Fk(warn)29 b(of)e(unde\014ne)l(d)i(r)l(efer)l(enc)l(e)g(to)g +Fh(#1)e Fm(22e)p Fl(i)q Fh(}\045)979 3794 y +({\\expandafter\\expandafter\\expa)q(ndaf)q(ter)1018 +3885 y(\\@car\\csname)42 b(r@#1\\endcsname\\@nil\\null}})822 +3977 y(\\def\\pageref#1{\\@ifundefined{)q(r@#1})q({{\\b)q(f)j(??})p +Fl(h)p Fk(warn)29 b(of)e(unde\014ne)l(d)i(r)l(efer)l(enc)l(e)g(to)f +Fh(#1)g Fm(22e)p Fl(i)q Fh(}\045)1018 4068 y +({\\expandafter\\expandafter\\exp)q(anda)q(fter)1057 +4159 y(\\@cdr\\csname)42 b(r@#1\\endcsname\\@nil\\null}})822 +4251 y(\\def\\@refpair#1{\\@ifundefined)q({r@#1)q(}{{0)q(}{0})p +Fl(h)p Fk(wa)q(rn)33 b(of)28 b(unde\014ne)l(d)h(r)l(efer)l(enc)l(e)g +(to)f Fh(#1)g Fm(22e)p Fl(i)q Fh(}\045)979 4342 y({\\@nameuse{r@#1}}}) +457 4500 y Fm(22e)181 b Fl(h)p Fk(warn)28 b(of)f(unde\014ne)l(d)i(r)l +(efer)l(enc)l(e)h(to)e Fh(#1)g Fm(22e)p Fl(i)p Fj(\021)1192 +b Fm(\(17d)25 b(22d\))822 4591 y Fh(\\@warning{Reference)43 +b(`#1')e(on)e(page)h(\\thepage)h(\\space)g(undefined})p +eop end +%%Page: 23 6 +TeXDict begin 23 5 bop 291 232 a Fo(Decem)n(b)r(er)27 +b(2,)g(2015)1533 b Fp(support.nw)162 b Fo(23)415 523 +y(Here)35 b(a)g(a)g(couple)h(of)f(ho)r(oks)g(for)g(formatting)g +(sub-page)f(n)n(um)n(b)r(ers,)j(whic)n(h)e(can)h(b)r(e)291 +623 y(alphab)r(etic,)27 b(n)n(umeric,)g(or)g(omitted.)-2267 +b Fg(St)n(yle)27 b(ho)r(ok)5 739 y Fm(23a)181 b Fl(h)p +Fk(noweb.sty)28 b Fm(2b)p Fl(i)q Fo(+)o Fj(\021)1903 +b Fi(/)12 b Fm(22c)48 b(24a)13 b Fi(.)374 830 y Fh +(\\def\\@alphasubpagenum#1#2{#2\\if)q(num#)q(1=0)45 b +(\\else\\@alph{#1}\\fi})374 922 y(\\def\\@nosubpagenum#1#2{#2})374 +1013 y(\\def\\@numsubpagenum#1#2{#2\\ifnu)q(m#1=)q(0)g +(\\else.\\@arabic{#1}\\fi})374 1104 y +(\\def\\nwopt@nosubpage{\\let\\nwthe)q(page)q(num=\\)q(@nos)q(ubpa)q +(genum)q(\\nwo)q(pt@no)q(marg)q(inta)q(g})374 1196 y +(\\def\\nwopt@numsubpage{\\let\\nwth)q(epag)q(enum=)q(\\@nu)q(msub)q +(pagen)q(um})374 1287 y(\\def\\nwopt@alphasubpage{\\let\\nw)q(thep)q +(agenu)q(m=\\@)q(alph)q(asubp)q(agen)q(um})374 1378 y +(\\nwopt@alphasubpage)415 1503 y Fo(In)28 b(rare)e(cases,)g(there)i(ma) +n(y)f(b)r(e)g(more)g(than)h(26)e(c)n(h)n(unks)h(on)h(a)f(page.)36 +b(In)27 b(suc)n(h)h(a)f(case,)291 1602 y(w)n(e)21 b(need)i(a)e +(sub-page)g(n)n(um)n(b)r(ering)h(sc)n(heme)f(that)i(can)e(go)h(b)r(ey)n +(ond)f(\\a)h(to)g(z.")34 b(The)22 b(sc)n(heme)291 1702 +y(I)31 b(ha)n(v)n(e)g(c)n(hosen)g(is)g(\\a)g(to)h(z,)g(then)h(aa)e(to)g +(zz,)i(then)f(aaa)e(to)i(zzz,)h(etc.")48 b(The)32 b(con)n(v)n(ersion) +291 1802 y(requires)c(a)h(bit)g(of)h(though)n(t)f(b)r(ecause)g(it)g(is) +h Ff(not)37 b Fo(an)29 b(ordinary)e(con)n(v)n(ersion)g(of)j(in)n(teger) +e(to)291 1901 y(string)h(as)g(w)n(e)h(usually)f(think)i(of)f(suc)n(h)f +(things.)44 b(The)30 b(problem)g(is)g(that)g(the)g(meaning)g(of)291 +2001 y(the)22 b(letters)g(dep)r(ends)h(on)e(the)i(p)r(osition;)h(the)e +(letter)g(a)g(acts)g(lik)n(e)g(a)f(zero)g(in)i(some)e(p)r(ositions)291 +2100 y(or)26 b(a)h(one)h(in)g(others.)415 2200 y(The)22 +b(solution)g(I)g(ha)n(v)n(e)f(implemen)n(ted)i(uses)e(a)h(v)-5 +b(ariable)21 b Fp(bound)f Fo(whic)n(h)i(is)g(alw)n(a)n(ys)e(equal)291 +2300 y(to)30 b(26)479 2270 y Fc(k)549 2300 y Fo(for)g(some)g +Fb(k)s Fo(.)46 b(If)30 b(w)n(e)h(write)f(the)h(recurrence)d +Fb(B)2044 2312 y Fc(k)2113 2300 y Fo(=)g Fb(B)2269 2312 +y Fc(k)q Fa(\000)p Fn(1)2415 2300 y Fo(+)20 b(26)2584 +2270 y Fc(k)2624 2300 y Fo(,)31 b(with)g Fb(B)2933 2312 +y Fn(0)2998 2300 y Fo(=)c(0,)291 2399 y(w)n(e)k(then)i(use)f(a)f +(string)g(of)h Fb(k)j Fo(letters)d(to)g(represen)n(t)f(n)n(um)n(b)r +(ers)g(b)r(et)n(w)n(een)h Fb(B)2704 2411 y Fc(k)q Fa(\000)p +Fn(1)2862 2399 y Fo(and)g Fb(B)3091 2411 y Fc(k)3132 +2399 y Fo(.)291 2499 y(Within)f(that)f(string,)g(a's)g(are)f(0's,)h +(and)g(so)f(on)h(up)h(to)f(z's)g(whic)n(h)g(are)f(25's,)g(and)h(w)n(e)g +(use)291 2599 y(standard)c(in)n(teger-con)n(v)n(ersion)e(metho)r(ds)k +(to)f(enco)r(de)h Fb(n)18 b Fj(\000)g Fb(B)2241 2611 +y Fc(k)q Fa(\000)p Fn(1)2367 2599 y Fo(.)415 2698 y(The)28 +b(follo)n(wing)e(Icon)i(implemen)n(tation)g(ma)n(y)f(b)r(e)h(more)e(p)r +(erspicuous)h(than)h(the)g(T)3046 2716 y(E)3093 2698 +y(X)291 2798 y(co)r(de)23 b(actually)h(used.)36 b(Here)23 +b(the)i(v)-5 b(ariable)23 b Fp(bound)f Fo(is)i(26)2058 +2768 y Fc(k)2098 2798 y Fo(,)h(with)f Fb(k)i Fo(=)d(1)g(initially)-7 +b(,)25 b(and)f Fp(n)g Fo(is)291 2897 y Fb(n)15 b Fj(\000)f +Fb(B)498 2909 y Fc(k)q Fa(\000)p Fn(1)624 2897 y Fo(.)37 +b(The)26 b(\014rst)g(lo)r(op)f(\014nds)h(the)h(righ)n(t)e +Fb(k)s Fo(,)h(and)g(the)h(second)e(do)r(es)h(the)g(usual)g(string)291 +2997 y(con)n(v)n(ersion.)1 3113 y Fm(23b)181 b Fl(h)p +Fk(Ic)l(on)27 b(c)l(o)l(de)i(for)e(subp)l(age)j(numb)l(ering)e +Fm(23b)p Fl(i)q Fj(\021)374 3205 y Fh(procedure)41 b(alphastring\(n\)) +452 3296 y(bound)f(:=)g(26)452 3479 y(while)g(n)g(>=)g(bound)g(do)g({) +531 3570 y(#)f(invariant:)i(bound)g(=)e(26^\(k+1\))i(&)f(n)f(is)h +(initial)h(n)e(-)h(B_k)531 3661 y(n)f(-:=)h(bound)531 +3753 y(bound)g(*:=)g(26)452 3844 y(})452 4027 y(while)g(bound)h(>)e(1)h +(do)f({)531 4118 y(bound)h(/:=)g(26)531 4209 y(d)f(:=)h(integer\(n)h(/) +e(bound\))531 4301 y(n)g(-:=)h(d)f(*)h(bound)531 4392 +y(writes\(&lcase[d+1]\))452 4483 y(})374 4575 y(end)p +eop end +%%Page: 24 7 +TeXDict begin 24 6 bop 739 232 a Fo(24)165 b Fp(support.nw)1530 +b Fo(Decem)n(b)r(er)28 b(2,)f(2015)863 523 y(Here's)22 +b(T)1156 541 y(E)1203 523 y(X)h(co)r(de)f(to)g(ac)n(hiev)n(e)f(the)i +(same)f(end.)36 b(The)22 b(en)n(tire)g(macro)g(b)r(o)r(dy)g(is)h +(enclosed)739 623 y(in)39 b(braces,)h(so)e(that)h(it)g(can)f(b)r(e)h +(used)f(with)h Fp(\\loop)e Fo(without)i(pic)n(king)f(up)h(the)g(wrong) +739 722 y Fp(\\repeat)p Fo(.)453 839 y Fm(24a)181 b Fl(h)p +Fk(noweb.sty)29 b Fm(2b)p Fl(i)p Fo(+)p Fj(\021)1894 +b Fi(/)12 b Fm(23a)48 b(24b)13 b Fi(.)822 930 y Fh +(\\newcount\\@nwalph@n)822 1021 y(\\let\\@nwalph@d\\@tempcnta)822 +1112 y(\\let\\@nwalph@bound\\@tempcntb)822 1204 y +(\\def\\@nwlongalph#1{{\045)900 1295 y(\\@nwalph@n=#1\\advance\\@nwal)q +(ph@n)46 b(by-1)900 1386 y(\\@nwalph@bound=26)900 1478 +y(\\loop\\ifnum\\@nwalph@n<\\@nwa)q(lph@b)q(ound)q(\\else)1018 +1569 y(\\advance\\@nwalph@n)d(by)d(-\\@nwalph@bound)1018 +1660 y(\\multiply\\@nwalph@bound)45 b(by)39 b(26)900 +1752 y(\\repeat)900 1843 y(\\loop\\ifnum\\@nwalph@bound>1)979 +1934 y(\\divide\\@nwalph@bound)44 b(by)c(26)979 2026 +y(\\@nwalph@d=\\@nwalph@n\\divide\\@)q(nwal)q(ph@d)46 +b(by)39 b(\\@nwalph@bound)979 2117 y(\045)g(d)h(:=)f(d)h(*)f(bound)i(;) +e(n)h(-:=)g(d;)f(d)h(:=)f(d)h(/)f(bound)i(---)f(saves)g(a)f(temporary) +979 2208 y(\\multiply\\@nwalph@d)k(by)d(\\@nwalph@bound)979 +2300 y(\\advance\\@nwalph@n)j(by)d(-\\@nwalph@d)979 2391 +y(\\divide\\@nwalph@d)j(by)d(\\@nwalph@bound)979 2482 +y(\\advance\\@nwalph@d)j(by)d(1)f(\\@alph{\\@nwalph@d}\045)900 +2574 y(\\repeat)822 2665 y(}})739 2922 y Fe(2.9)112 b +Fd(WEB)p Fe(-lik)m(e)39 b(c)m(h)m(unk)f(n)m(um)m(b)s(ering)739 +3076 y Fo(Here's)29 b(a)h(righ)n(teous)e(hac)n(k:)41 +b(w)n(e)29 b(get)h(the)g(e\013ect)h(of)e(WEB-lik)n(e)g(c)n(h)n(unk)h(n) +n(um)n(b)r(ers)f(just)i(b)n(y)739 3175 y(rede\014ning)20 +b Fp(\\sublabel)d Fo(to)j(use)g(a)g(coun)n(ter)g(instead)g(of)h(the)g +(curren)n(t)e(page)h(n)n(um)n(b)r(er.)34 b(Since)739 +3275 y(the)28 b(n)n(um)n(b)r(ers)f(are)g(all)g(distinct,)h(no)g +(sub-page)e(n)n(um)n(b)r(er)h(is)h(ev)n(er)e(used.)449 +3391 y Fm(24b)181 b Fl(h)p Fk(noweb.sty)29 b Fm(2b)p +Fl(i)p Fo(+)p Fj(\021)1898 b Fi(/)12 b Fm(24a)48 b(25a)13 +b Fi(.)822 3482 y Fh(\\newcount\\nw@chunkcount)822 3574 +y(\\nw@chunkcount=\\@ne)822 3665 y(\\newcommand{\\weblabel}[1]{\045)900 +3756 y(\\@bsphack)900 3848 y(\\nwblindhyperanchor{#1}\045)900 +3939 y(\\if@filesw)42 b({\\let\\thepage\\relax)940 4030 +y(\\def\\protect{\\noexpand\\noexpand)q(\\noe)q(xpand)q(}\045)940 +4122 y(\\edef\\@tempa{\\write\\@auxout{\\st)q(ring)1057 +4213 y(\\newsublabel{#1}{{}{\\number\\)q(nw@c)q(hunkc)q(ount)q(}}}})q +(\045)940 4304 y(\\expandafter}\\@tempa)940 4396 y +(\\global\\advance\\nw@chunkcount)j(by)40 b(\\@ne)940 +4487 y(\\if@nobreak)h(\\ifvmode\\nobreak\\fi\\fi\\fi\\@e)q(spha)q(ck}) +822 4578 y(\\def\\nwopt@webnumbering{\045)900 4670 y +(\\let\\sublabel=\\weblabel)900 4761 y(\\def\\nwpageword{chunk}\\def\\) +q(nwpag)q(eswo)q(rd{ch)q(unks)q(}\045)900 4852 y +(\\def\\nwpageprep{in}})p eop end +%%Trailer + +userdict /end-hook known{end-hook}if +%%EOF diff --git a/web/noweb/src/xdoc/Makefile b/web/noweb/src/xdoc/Makefile new file mode 100644 index 0000000000..402b3cfbcf --- /dev/null +++ b/web/noweb/src/xdoc/Makefile @@ -0,0 +1,110 @@ +WWW=$(HOME)/www/noweb +SHELL=/bin/sh +.SUFFIXES: .1 .7 .txt .ps +.1.txt: ; nroff -man $*.1 > $*.txt +.1.ps: ; psroff -t -man $*.1 > $*.ps +.7.txt: ; nroff -man $*.7 > $*.txt +.7.ps: ; psroff -t -man $*.7 > $*.ps + +MANPAGES=notangle.1 cpif.1 noweb.1 nodefs.1 noroots.1 noindex.1 \ + nowebstyle.7 nowebfilters.7 nuweb2noweb.1 sl2h.1 htmltoc.1 \ + noroff.1 +TXTPAGES=notangle.txt cpif.txt noweb.txt nodefs.txt noroots.txt noindex.txt \ + nowebstyle.txt nowebfilters.txt nuweb2noweb.txt sl2h.txt htmltoc.txt \ + noroff.txt +SRCS=$(MANPAGES) $(TXTPAGES) + +NAME="name of version checked in" +CIMSG="message for version checked in" +RCSFILES=*.nw *.tex Makefile +DOVERSION=./doversion + +all: $(SRCS) +source: $(SRCS) +touch: $(SRCS) + touch $(SRCS) +boot: + touch $(SRCS) + +www: $(WWW)/onepage.ps $(WWW)/guide.ps $(WWW)/guide.html + +checkin: + ci -l $(CINAME) $(CIMSG) $(RCSFILES) + +$(WWW)/onepage.ps: onepage.ps + cp onepage.ps $(WWW) + +$(WWW)/guide.ps: guide.ps + cp guide.ps $(WWW) + +$(WWW)/guide.html: guide.html + cp guide.html $(WWW) + +notangle.1: manpage.nw ../shell/noweave.nw docdate.nw + notangle -t8 -Rnotangle.1 manpage.nw ../shell/noweave.nw docdate.nw | $(DOVERSION) > notangle.1 + +nowebstyle.7: manpage.nw ../tex/support.nw docdate.nw + notangle -t8 -Rnowebstyle.7 manpage.nw ../tex/support.nw docdate.nw | $(DOVERSION) > nowebstyle.7 + +nowebfilters.7: nowebfilters.nw docdate.nw + notangle -t8 nowebfilters.nw docdate.nw | $(DOVERSION) > nowebfilters.7 + +noweb.1: manpage.nw docdate.nw + notangle -t8 -Rnoweb.1 manpage.nw docdate.nw | $(DOVERSION) > noweb.1 + +nodefs.1: nodefs.nw docdate.nw + notangle -t8 nodefs.nw docdate.nw | $(DOVERSION) > nodefs.1 + +noroots.1: noroots.nw docdate.nw + notangle -t8 noroots.nw docdate.nw | $(DOVERSION) > noroots.1 + +noindex.1: noindex.nw docdate.nw + notangle -t8 noindex.nw docdate.nw | $(DOVERSION) > noindex.1 + +sl2h.1: sl2h.nw docdate.nw + notangle -t8 sl2h.nw docdate.nw | $(DOVERSION) > sl2h.1 + +htmltoc.1: htmltoc.nw docdate.nw + notangle -t8 htmltoc.nw docdate.nw | $(DOVERSION) > htmltoc.1 + +cpif.1: manpage.nw docdate.nw + notangle -t8 -Rcpif.1 manpage.nw docdate.nw | $(DOVERSION) > cpif.1 + +nuweb2noweb.1: manpage.nw docdate.nw + notangle -t8 -Rnuweb2noweb.1 manpage.nw docdate.nw | $(DOVERSION) > nuweb2noweb.1 + +noroff.1: noroff.nw docdate.nw + notangle -t8 noroff.nw docdate.nw | $(DOVERSION) > noroff.1 + +wc.tex: ../../examples/wc.nw + (cd ../../examples; noweave -n -index wc.nw) > wc.tex + +techrep.dvi: techrep.tex wc.tex + latex '\scrollmode \input '"techrep" + while grep -s 'Rerun to get cross-references right' techrep.log; do latex '\scrollmode \input '"techrep"; done + +onepage.dvi: onepage.tex + latex '\scrollmode \input '"onepage" + while grep -s 'Rerun to get cross-references right' onepage.log; do latex '\scrollmode \input '"onepage"; done + +onepage.ps: onepage.dvi + dvips -P cmz -o onepage.ps onepage + +guide.dvi: guide.tex + latex '\scrollmode \input '"guide" + while grep -s 'Rerun to get cross-references right' guide.log; do latex '\scrollmode \input '"guide"; done + +guide.ps: guide.dvi + dvips -P cmz -o guide.ps guide + +guide.uu: guide.ps + gzip < guide.ps > guide.ps.gz + uuencode guide.ps.gz < guide.ps.gz > guide.uu + +guide.html: guide.dvi + sl2h guide.tex | htmltoc > guide.html + +clean: ; /bin/rm -f *.dvi *.log *.blg *~ wc.tex *.ps *.gz *.uu *.html +clobber: clean + rm -f *.1 *.7 *.txt + diff --git a/web/noweb/src/xdoc/chicago.bst b/web/noweb/src/xdoc/chicago.bst new file mode 100644 index 0000000000..925f9a2e0e --- /dev/null +++ b/web/noweb/src/xdoc/chicago.bst @@ -0,0 +1,1653 @@ +%%% ==================================================================== +%%% @BibTeX-style-file{ +%%% author = "Glenn Paulley", +%%% version = "4", +%%% date = "28 August 1992", +%%% time = "10:23:39 199", +%%% filename = "chicago.bst", +%%% address = "Data Structuring Group +%%% Department of Computer Science +%%% University of Waterloo +%%% Waterloo, Ontario, Canada +%%% N2L 3G1", +%%% telephone = "(519) 885-1211", +%%% FAX = "(519) 885-1208", +%%% checksum = "26323 1654 5143 37417", +%%% email = "gnpaulle@bluebox.uwaterloo.ca", +%%% codetable = "ISO/ASCII", +%%% keywords = "", +%%% supported = "yes", +%%% abstract = "A BibTeX bibliography style that follows the +%%% `B' reference style of the 13th Edition of +%%% the Chicago Manual of Style. A detailed +%%% feature list is given below.", +%%% docstring = "The checksum field above contains a CRC-16 +%%% checksum as the first value, followed by the +%%% equivalent of the standard UNIX wc (word +%%% count) utility output of lines, words, and +%%% characters. This is produced by Robert +%%% Solovay's checksum utility.", +%%% } +%%% ==================================================================== +% +% "Chicago" BibTeX style, chicago.bst +% =================================== +% +% BibTeX `chicago' style file for BibTeX version 0.99c, LaTeX version 2.09 +% Place it in a file called chicago.bst in the BibTeX search path. +% You need to include chicago.sty as a \documentstyle option. +% (Placing it in the same directory as the LaTeX document should also work.) +% This "chicago" style is based on newapa.bst (American Psych. Assoc.) +% found at ymir.claremont.edu. +% +% Citation format: (author-last-name year) +% (author-last-name and author-last-name year) +% (author-last-name, author-last-name, and author-last-name year) +% (author-last-name et al. year) +% (author-last-name) +% author-last-name (year) +% (author-last-name and author-last-name) +% (author-last-name et al.) +% (year) or (year,year) +% year or year,year +% +% Reference list ordering: alphabetical by author or whatever passes +% for author in the absence of one. +% +% This BibTeX style has support for abbreviated author lists and for +% year-only citations. This is done by having the citations +% actually look like +% +% \citeauthoryear{full-author-info}{abbrev-author-info}{year} +% +% The LaTeX style has to have the following (or similar) +% +% \let\@internalcite\cite +% \def\fullcite{\def\citeauthoryear##1##2##3{##1, ##3}\@internalcite} +% \def\fullciteA{\def\citeauthoryear##1##2##3{##1}\@internalcite} +% \def\shortcite{\def\citeauthoryear##1##2##3{##2, ##3}\@internalcite} +% \def\shortciteA{\def\citeauthoryear##1##2##3{##2}\@internalcite} +% \def\citeyear{\def\citeauthoryear##1##2##3{##3}\@internalcite} +% +% These TeX macro definitions are found in chicago.sty. Additional +% commands to manipulate different components of a citation can be defined +% so that, for example, you can list author's names without parentheses +% if using a citation as a noun or object in a sentence. +% +% This file was originally copied from newapa.bst at ymir.claremont.edu. +% +% Features of chicago.bst: +% ======================= +% +% - full names used in citations, but abbreviated citations are available +% (see above) +% - if an entry has a "month", then the month and year are also printed +% as part of that bibitem. +% - all conjunctions use "and" instead of "\&" +% - major modification from Chicago Manual of Style (13th ed.) is that +% only the first author in a reference appears last name first- +% additional authors appear as J. Q. Public. +% - pages are listed as "pp. xx-xx" in all entry types except +% article entries. +% - book, inbook, and manual use "location: publisher" (or organization) +% for address and publisher. All other types list publishers separately. +% - "pp." are used to identify page numbers for all entry types except +% articles. +% - organization is used as a citation label if neither author nor editor +% is present (for manuals). +% - "et al." is used for long author and editor lists, or when "others" +% is used. +% +% Modifications and bug fixes from newapa.bst: +% =========================================== +% +% - added month, year to bib entries if month is present +% - fixed bug with In proceedings, added necessary comma after title +% - all conjunctions changed to "and" from "\&" +% - fixed bug with author labels in my.full.label: "et al." now is +% generated when "others" is an author name +% - major modification from Chicago Manual of Style (13th ed.) is that +% only the first author in a reference appears last name first- +% additional authors appear as J. Q. Public. +% - pages are listed as "pp. xx-xx" in all entry types except +% article entries. Unnecessary (IMHO) "()" around page numbers +% were removed, and page numbers now don't end with a period. +% - created chicago.sty for use with this bibstyle (required). +% - fixed bugs in FUNCTION {format.vol.num.pages} for missing volume, +% number, and /or pages. Renamed to format.jour.vol. +% - fixed bug in formatting booktitles: additional period an error if +% book has a volume. +% - fixed bug: editors usually given redundant period before next clause +% (format.editors.dot) removed. +% - added label support for organizations, if both author and editor +% are missing (from alpha.bst). If organization is too long, then +% the key field is used for abbreviated citations. +% - In proceedings or books of several volumes, no comma was written +% between the "Volume x" and the page numbers (this was intentional +% in newapa.bst). Fixed. +% - Some journals may not have volumes/numbers, only month/year (eg. +% IEEE Computer). Fixed bug in article style that assumed volume/number +% was always present. +% +% Original documentation for newapa.sty: +% ===================================== +% +% This version was made by modifying the master file made by +% Oren Patashnik (PATASHNIK@SCORE.STANFORD.EDU), and the 'named' BibTeX +% style of Peter F. Patel-Schneider. +% +% Copyright (C) 1985, all rights reserved. +% Copying of this file is authorized only if either +% (1) you make absolutely no changes to your copy, including name, or +% (2) if you do make changes, you name it something other than 'newapa.bst'. +% There are undoubtably bugs in this style. If you make bug fixes, +% improvements, etc. please let me know. My e-mail address is: +% spencer@cgrg.ohio.state.edu or 71160.3141@compuserve.com +% +% This style was made from 'plain.bst', 'named.bst', and 'apalike.bst', +% with lots of tweaking to make it look like APA style, along with tips +% from Young Ryu and Brian Reiser's modifications of 'apalike.bst'. + +ENTRY + { address + author + booktitle + chapter + edition + editor + howpublished + institution + journal + key + month + note + number + organization + pages + publisher + school + series + title + type + volume + year + } + {} + { label.year extra.label sort.year sort.label } + +INTEGERS { output.state before.all mid.sentence after.sentence after.block } + +FUNCTION {init.state.consts} +{ #0 'before.all := + #1 'mid.sentence := + #2 'after.sentence := + #3 'after.block := +} + +STRINGS { s t u } + +FUNCTION {output.nonnull} +{ 's := + output.state mid.sentence = + { ", " * write$ } + { output.state after.block = + { add.period$ write$ + newline$ + "\newblock " write$ + } + { output.state before.all = + 'write$ + { add.period$ " " * write$ } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +% Use a colon to separate output. Used only for address/publisher +% combination in book/inbook types, address/institution for manuals, +% and organization:publisher for proceedings (inproceedings). +% +FUNCTION {output.nonnull.colon} +{ 's := + output.state mid.sentence = + { ": " * write$ } + { output.state after.block = + { add.period$ write$ + newline$ + "\newblock " write$ + } + { output.state before.all = + 'write$ + { add.period$ " " * write$ } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION {output} +{ duplicate$ empty$ + 'pop$ + 'output.nonnull + if$ +} + +FUNCTION {output.colon} +{ duplicate$ empty$ + 'pop$ + 'output.nonnull.colon + if$ +} + +FUNCTION {output.check} +{ 't := + duplicate$ empty$ + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull + if$ +} + +FUNCTION {output.check.colon} +{ 't := + duplicate$ empty$ + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull.colon + if$ +} + +FUNCTION {output.year.check} +{ year empty$ + { "empty year in " cite$ * warning$ } + { write$ + " (" year * extra.label * + month empty$ + { ")" * } + { ", " * month * ")" * } + if$ + mid.sentence 'output.state := + } + if$ +} + + +FUNCTION {fin.entry} +{ add.period$ + write$ + newline$ +} + +FUNCTION {new.block} +{ output.state before.all = + 'skip$ + { after.block 'output.state := } + if$ +} + +FUNCTION {new.sentence} +{ output.state after.block = + 'skip$ + { output.state before.all = + 'skip$ + { after.sentence 'output.state := } + if$ + } + if$ +} + +FUNCTION {not} +{ { #0 } + { #1 } + if$ +} + +FUNCTION {and} +{ 'skip$ + { pop$ #0 } + if$ +} + +FUNCTION {or} +{ { pop$ #1 } + 'skip$ + if$ +} + +FUNCTION {new.block.checka} +{ empty$ + 'skip$ + 'new.block + if$ +} + +FUNCTION {new.block.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.block + if$ +} + +FUNCTION {new.sentence.checka} +{ empty$ + 'skip$ + 'new.sentence + if$ +} + +FUNCTION {new.sentence.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.sentence + if$ +} + +FUNCTION {field.or.null} +{ duplicate$ empty$ + { pop$ "" } + 'skip$ + if$ +} + +% +% Emphasize the top string on the stack. +% +FUNCTION {emphasize} +{ duplicate$ empty$ + { pop$ "" } + { "{\em " swap$ * "}" * } + if$ +} + +% +% Emphasize the top string on the stack, but add a trailing space. +% +FUNCTION {emphasize.space} +{ duplicate$ empty$ + { pop$ "" } + { "{\em " swap$ * "\/}" * } + if$ +} + +INTEGERS { nameptr namesleft numnames } +% +% Format bibliographical entries with the first author last name first, +% and subsequent authors with initials followed by last name. +% All names are formatted in this routine. +% +FUNCTION {format.names} +{ 's := + #1 'nameptr := % nameptr = 1; + s num.names$ 'numnames := % numnames = num.name$(s); + numnames 'namesleft := + { namesleft #0 > } + + { nameptr #1 = + {s nameptr "{vv~}{ll}{, jj}{, f.}" format.name$ 't := } + {s nameptr "{f.~}{vv~}{ll}{, jj}" format.name$ 't := } + if$ + nameptr #1 > + { namesleft #1 > + { ", " * t * } + { numnames #2 > + { "," * } + 'skip$ + if$ + t "others" = + { " et~al." * } + { " and " * t * } % from Chicago Manual of Style + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := % nameptr += 1; + namesleft #1 - 'namesleft := % namesleft =- 1; + } + while$ +} + +FUNCTION {my.full.label} +{ 's := + #1 'nameptr := % nameptr = 1; + s num.names$ 'numnames := % numnames = num.name$(s); + numnames 'namesleft := + { namesleft #0 > } + + { s nameptr "{vv~}{ll}" format.name$ 't := % get the next name + nameptr #1 > + { namesleft #1 > + { ", " * t * } + { numnames #2 > + { "," * } + 'skip$ + if$ + t "others" = + { " et~al." * } + { " and " * t * } % from Chicago Manual of Style + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := % nameptr += 1; + namesleft #1 - 'namesleft := % namesleft =- 1; + } + while$ + +} + +FUNCTION {format.names.fml} +% +% Format names in "familiar" format, with first initial followed by +% last name. Like format.names, ALL names are formatted. +% +{ 's := + #1 'nameptr := % nameptr = 1; + s num.names$ 'numnames := % numnames = num.name$(s); + numnames 'namesleft := + { namesleft #0 > } + + { s nameptr "{f.~}{vv~}{ll}{, jj}" format.name$ 't := + + nameptr #1 > + { namesleft #1 > + { ", " * t * } + { numnames #2 > + { "," * } + 'skip$ + if$ + t "others" = + { " et~al." * } + { " and " * t * } +% { " \& " * t * } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := % nameptr += 1; + namesleft #1 - 'namesleft := % namesleft =- 1; + } + while$ +} + +FUNCTION {format.authors} +{ author empty$ + { "" } + { author format.names } + if$ +} + +FUNCTION {format.key} +{ empty$ + { key field.or.null } + { "" } + if$ +} + +% +% Format editor names for use in the "in" types: inbook, incollection, +% inproceedings: first initial, then last names. When editors are the +% LABEL for an entry, then format.editor is used which lists editors +% by last name first. +% +FUNCTION {format.editors.fml} +{ editor empty$ + { "" } + { editor format.names.fml + editor num.names$ #1 > + { " (Eds.)" * } + { " (Ed.)" * } + if$ + } + if$ +} + +% +% Format editor names for use in labels, last names first. +% +FUNCTION {format.editors} +{ editor empty$ + { "" } + { editor format.names + editor num.names$ #1 > + { " (Eds.)" * } + { " (Ed.)" * } + if$ + } + if$ +} + +FUNCTION {format.title} +{ title empty$ + { "" } + { title "t" change.case$ } + if$ +} + +% Note that the APA style requres case changes +% in article titles. The following does not +% change cases. If you perfer it, uncomment the +% following and comment out the above. + +%FUNCTION {format.title} +%{ title empty$ +% { "" } +% { title } +% if$ +%} + +FUNCTION {n.dashify} +{ 't := + "" + { t empty$ not } + { t #1 #1 substring$ "-" = + { t #1 #2 substring$ "--" = not + { "--" * + t #2 global.max$ substring$ 't := + } + { { t #1 #1 substring$ "-" = } + { "-" * + t #2 global.max$ substring$ 't := + } + while$ + } + if$ + } + { t #1 #1 substring$ * + t #2 global.max$ substring$ 't := + } + if$ + } + while$ +} + +FUNCTION {format.btitle} +{ edition empty$ + { title emphasize } + { title empty$ + { title emphasize } + { volume empty$ % gnp - check for volume, then don't need period + { "{\em " title * "\/} (" * edition * " ed.)" * "." * } + { "{\em " title * "\/} (" * edition * " ed.)" * } + if$ + } + if$ + } + if$ +} + +FUNCTION {format.emphasize.booktitle} +{ edition empty$ + { booktitle emphasize } + { booktitle empty$ + { booktitle emphasize } + { volume empty$ % gnp - extra period an error if book has a volume + { "{\em " booktitle * "\/} (" * edition * " ed.)" * "." *} + { "{\em " booktitle * "\/} (" * edition * " ed.)" * } + if$ + } + if$ + } + if$ + } + + +FUNCTION {tie.or.space.connect} +{ duplicate$ text.length$ #3 < + { "~" } + { " " } + if$ + swap$ * * +} + +FUNCTION {either.or.check} +{ empty$ + 'pop$ + { "can't use both " swap$ * " fields in " * cite$ * warning$ } + if$ +} + +FUNCTION {format.bvolume} +{ volume empty$ + { "" } + { "Volume" volume tie.or.space.connect % gnp - changed to mixed case + series empty$ + 'skip$ + { " of " * series emphasize * } + if$ + "volume and number" number either.or.check + } + if$ +} + +FUNCTION {format.number.series} +{ volume empty$ + { number empty$ + { series field.or.null } + { output.state mid.sentence = + { "Number" } % gnp - changed to mixed case always + { "Number" } + if$ + number tie.or.space.connect + series empty$ + { "there's a number but no series in " cite$ * warning$ } + { " in " * series * } + if$ + } + if$ + } + { "" } + if$ +} + +INTEGERS { multiresult } + +FUNCTION {multi.page.check} +{ 't := + #0 'multiresult := + { multiresult not + t empty$ not + and + } + { t #1 #1 substring$ + duplicate$ "-" = + swap$ duplicate$ "," = + swap$ "+" = + or or + { #1 'multiresult := } + { t #2 global.max$ substring$ 't := } + if$ + } + while$ + multiresult +} + +FUNCTION {format.pages} +{ pages empty$ + { "" } + { pages multi.page.check + { "pp.\ " pages n.dashify tie.or.space.connect } % gnp - removed () + { "pp.\ " pages tie.or.space.connect } + if$ + } + if$ +} + +% By Young (and Spencer) +% GNP - fixed bugs with missing volume, number, and/or pages +% +% Format journal, volume, number, pages for article types. +% +FUNCTION {format.jour.vol} +{ journal empty$ + { "no journal in " cite$ * warning$ + "" } + { journal emphasize.space } + if$ + number empty$ + { volume empty$ + { "no number and no volume in " cite$ * warning$ + "" * } + { "~{\em " * Volume * "}" * } + if$ + } + { volume empty$ + {"no volume for " cite$ * warning$ + "~(" * number * ")" * } + { "~" * + volume emphasize.space + "(" * number * ")" * * } + if$ + } + if$ + pages empty$ + {"page numbers missing in " cite$ * warning$ + "" * } % gnp - place a null string on the stack for output + { duplicate$ empty$ + { pop$ format.pages } + { ", " * pages n.dashify * } % gnp - removed pp. for articles + if$ + } + if$ +} + +FUNCTION {format.chapter.pages} +{ chapter empty$ + 'format.pages + { type empty$ + { "Chapter" } % gnp - changed to mixed case + { type "t" change.case$ } + if$ + chapter tie.or.space.connect + pages empty$ + {"page numbers missing in " cite$ * warning$} % gnp - added check + { ", " * format.pages * } + if$ + } + if$ +} + +FUNCTION {format.in.ed.booktitle} +{ booktitle empty$ + { "" } + { editor empty$ + { "In " format.emphasize.booktitle * } + { "In " format.editors.fml * ", " * format.emphasize.booktitle * } + if$ + } + if$ +} + +FUNCTION {format.thesis.type} +{ type empty$ + 'skip$ + { pop$ + type "t" change.case$ + } + if$ +} + +FUNCTION {format.tr.number} +{ type empty$ + { "Technical Report" } + 'type + if$ + number empty$ + { "t" change.case$ } + { number tie.or.space.connect } + if$ +} + +FUNCTION {format.article.crossref} +{ "See" + "\citeN{" * crossref * "}" * +} + +FUNCTION {format.crossref.editor} +{ editor #1 "{vv~}{ll}" format.name$ + editor num.names$ duplicate$ + #2 > + { pop$ " et~al." * } + { #2 < + 'skip$ + { editor #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = + { " et~al." * } + { " and " * editor #2 "{vv~}{ll}" format.name$ * } + if$ + } + if$ + } + if$ +} + +FUNCTION {format.book.crossref} +{ volume empty$ + { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ + "In " + } + { "Volume" volume tie.or.space.connect % gnp - changed to mixed case + " of " * + } + if$ + editor empty$ + editor field.or.null author field.or.null = + or + { key empty$ + { series empty$ + { "need editor, key, or series for " cite$ * " to crossref " * + crossref * warning$ + "" * + } + { "{\em " * series * "\/}" * } + if$ + } + { key * } + if$ + } + { format.crossref.editor * } + if$ + " \citeN{" * crossref * "}" * +} + +FUNCTION {format.incoll.inproc.crossref} +{ "See" + " \citeN{" * crossref * "}" * +} + +% format.lab.names: +% +% determines "short" names for the abbreviated author information. +% "Long" labels are created in calc.label, using the routine my.full.label +% to format author and editor fields. +% +% There are 4 cases for labels. (n=3 in the example) +% a) one author Foo +% b) one to n Foo, Bar and Baz +% c) use of "and others" Foo, Bar et al. +% d) more than n Foo et al. +% +FUNCTION {format.lab.names} +{ 's := + s num.names$ 'numnames := + numnames #2 > % change number to number of others allowed before + % forcing "et al". + { s #1 "{vv~}{ll}" format.name$ " et~al." * } + { + numnames #1 - 'namesleft := + #2 'nameptr := + s #1 "{vv~}{ll}" format.name$ + { namesleft #0 > } + { nameptr numnames = + { s nameptr "{ff }{vv }{ll}{ jj}" format.name$ "others" = + { " et~al." * } + { " and " * s nameptr "{vv~}{ll}" format.name$ * } + if$ + } + { ", " * s nameptr "{vv~}{ll}" format.name$ * } + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ + } + if$ +} + +FUNCTION {author.key.label} +{ author empty$ + { key empty$ + { "no key, author in " cite$ * warning$ + cite$ #1 #3 substring$ } + 'key + if$ + } + { author format.lab.names } + if$ +} + +FUNCTION {editor.key.label} +{ editor empty$ + { key empty$ + { "no key, editor in " cite$ * warning$ + cite$ #1 #3 substring$ } + 'key + if$ + } + { editor format.lab.names } + if$ +} + +FUNCTION {author.key.organization.label} +% +% added - gnp. Provide label formatting by organization if author is null. +% +{ author empty$ + { organization empty$ + { key empty$ + { "no key, author or organization in " cite$ * warning$ + cite$ #1 #3 substring$ } + 'key + if$ + } + { organization } + if$ + } + { author format.lab.names } + if$ +} + +FUNCTION {editor.key.organization.label} +% +% added - gnp. Provide label formatting by organization if editor is null. +% +{ editor empty$ + { organization empty$ + { key empty$ + { "no key, editor or organization in " cite$ * warning$ + cite$ #1 #3 substring$ } + 'key + if$ + } + { organization } + if$ + } + { editor format.lab.names } + if$ +} + +FUNCTION {author.editor.key.label} +{ author empty$ + { editor empty$ + { key empty$ + { "no key, author, or editor in " cite$ * warning$ + cite$ #1 #3 substring$ } + 'key + if$ + } + { editor format.lab.names } + if$ + } + { author format.lab.names } + if$ +} + +FUNCTION {calc.label} +% +% Changed - GNP. See also author.organization.sort, editor.organization.sort +% Form label for BibTeX entry. The classification of which fields are used +% for which type of entry (book, inbook, etc.) are taken from alpha.bst. +% The change here from newapa is to also include organization as a +% citation label if author or editor is missing. +% +{ type$ "book" = + type$ "inbook" = + or + 'author.editor.key.label + { type$ "proceedings" = + 'editor.key.organization.label + { type$ "manual" = + 'author.key.organization.label + 'author.key.label + if$ + } + if$ + } + if$ + + author empty$ % generate the full label citation information. + { editor empty$ + { organization empty$ + { "no author, editor, or organization in " cite$ * warning$ + "??" } + { organization } + if$ + } + { editor my.full.label } + if$ + } + { author my.full.label } + if$ + +% leave label on the stack, to be popped when required. + + "}{" * swap$ * "}{" * +% year field.or.null purify$ #-1 #4 substring$ * +% +% save the year for sort processing afterwards (adding a, b, c, etc.) +% + year field.or.null purify$ #-1 #4 substring$ + 'label.year := +} + +FUNCTION {output.bibitem} +{ newline$ + + "\bibitem[\protect\citeauthoryear{" write$ + calc.label write$ + sort.year write$ + "}]{" write$ + + cite$ write$ + "}" write$ + newline$ + "" + before.all 'output.state := +} + +FUNCTION {article} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + crossref missing$ + { format.jour.vol output + } + { format.article.crossref output.nonnull + format.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {book} +{ output.bibitem + author empty$ + { format.editors + "author and editor" output.check } + { format.authors + output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + output.year.check % added + new.block + format.btitle + "title" output.check + crossref missing$ + { format.bvolume output + new.block + format.number.series output + new.sentence + address output + publisher "publisher" output.check.colon + } + { new.block + format.book.crossref output.nonnull + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {booklet} +{ output.bibitem + format.authors output + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + howpublished output + address output + new.block + note output + fin.entry +} + +FUNCTION {inbook} +{ output.bibitem + author empty$ + { format.editors + "author and editor" output.check + } + { format.authors output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + output.year.check % added + new.block + format.btitle + "title" output.check + crossref missing$ + { format.bvolume output + format.chapter.pages + "chapter and pages" output.check + new.block + format.number.series output + new.sentence + address output + publisher + "publisher" output.check.colon + } + { format.chapter.pages "chapter and pages" output.check + new.block + format.book.crossref output.nonnull + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {incollection} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + crossref missing$ + { format.in.ed.booktitle + "booktitle" output.check + format.bvolume output + format.number.series output + format.chapter.pages output % gnp - was special.output.nonnull +% left out comma before page numbers + new.sentence + address output + publisher "publisher" output.check.colon + } + { format.incoll.inproc.crossref + output.nonnull + format.chapter.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {inproceedings} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + crossref missing$ + { format.in.ed.booktitle + "booktitle" output.check + format.bvolume output + format.number.series output + address output + format.pages output + new.sentence + organization output + publisher output.colon + } + { format.incoll.inproc.crossref output.nonnull + format.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {conference} { inproceedings } + +FUNCTION {manual} +{ output.bibitem + author empty$ + { editor empty$ + { organization "organization" output.check + organization format.key output } % if all else fails, use key + { format.editors "author and editor" output.check } + if$ + } + { format.authors output.nonnull } + if$ + output.year.check % added + new.block + format.btitle + "title" output.check + organization address new.block.checkb +% Reversed the order of "address" and "organization", added the ":". + address output + organization "organization" output.check.colon +% address output +% ":" output +% organization output + new.block + note output + fin.entry +} + +FUNCTION {mastersthesis} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + "Master's thesis" format.thesis.type output.nonnull + school "school" output.check + address output + new.block + note output + fin.entry +} + +FUNCTION {misc} +{ output.bibitem + format.authors output + author format.key output % added + output.year.check % added + title howpublished new.block.checkb + format.title output + new.block + howpublished output + new.block + note output + fin.entry +} + +FUNCTION {phdthesis} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.btitle + "title" output.check + new.block + "Ph.\ D. thesis" format.thesis.type output.nonnull + school "school" output.check + address output + new.block + note output + fin.entry +} + +FUNCTION {proceedings} +{ output.bibitem + editor empty$ + { organization output + organization format.key output } % gnp - changed from author format.key + { format.editors output.nonnull } + if$ +% author format.key output % gnp - removed (should be either +% editor or organization + output.year.check % added (newapa) + new.block + format.btitle + "title" output.check + format.bvolume output + format.number.series output + address output + new.sentence + organization output + publisher output.colon + new.block + note output + fin.entry +} + +FUNCTION {techreport} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + format.tr.number output.nonnull + institution + "institution" output.check + address output + new.block + note output + fin.entry +} + +FUNCTION {unpublished} +{ output.bibitem + format.authors + "author" output.check + author format.key output % added + output.year.check % added + new.block + format.title + "title" output.check + new.block + note "note" output.check + fin.entry +} + +FUNCTION {default.type} { misc } + +MACRO {jan} {"January"} + +MACRO {feb} {"February"} + +MACRO {mar} {"March"} + +MACRO {apr} {"April"} + +MACRO {may} {"May"} + +MACRO {jun} {"June"} + +MACRO {jul} {"July"} + +MACRO {aug} {"August"} + +MACRO {sep} {"September"} + +MACRO {oct} {"October"} + +MACRO {nov} {"November"} + +MACRO {dec} {"December"} + +MACRO {acmcs} {"ACM Computing Surveys"} + +MACRO {acta} {"Acta Informatica"} + +MACRO {ai} {"Artificial Intelligence"} + +MACRO {cacm} {"Communications of the ACM"} + +MACRO {ibmjrd} {"IBM Journal of Research and Development"} + +MACRO {ibmsj} {"IBM Systems Journal"} + +MACRO {ieeese} {"IEEE Transactions on Software Engineering"} + +MACRO {ieeetc} {"IEEE Transactions on Computers"} + +MACRO {ieeetcad} + {"IEEE Transactions on Computer-Aided Design of Integrated Circuits"} + +MACRO {ipl} {"Information Processing Letters"} + +MACRO {jacm} {"Journal of the ACM"} + +MACRO {jcss} {"Journal of Computer and System Sciences"} + +MACRO {scp} {"Science of Computer Programming"} + +MACRO {sicomp} {"SIAM Journal on Computing"} + +MACRO {tocs} {"ACM Transactions on Computer Systems"} + +MACRO {tods} {"ACM Transactions on Database Systems"} + +MACRO {tog} {"ACM Transactions on Graphics"} + +MACRO {toms} {"ACM Transactions on Mathematical Software"} + +MACRO {toois} {"ACM Transactions on Office Information Systems"} + +MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"} + +MACRO {tcs} {"Theoretical Computer Science"} + +READ + +FUNCTION {sortify} +{ purify$ + "l" change.case$ +} + +INTEGERS { len } + +FUNCTION {chop.word} +{ 's := + 'len := + s #1 len substring$ = + { s len #1 + global.max$ substring$ } + 's + if$ +} + + + +FUNCTION {sort.format.names} +{ 's := + #1 'nameptr := + "" + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { nameptr #1 > + { " " * } + 'skip$ + if$ + s nameptr "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" format.name$ 't := + nameptr numnames = t "others" = and + { " et~al" * } + { t sortify * } + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {sort.format.title} +{ 't := + "A " #2 + "An " #3 + "The " #4 t chop.word + chop.word + chop.word + sortify + #1 global.max$ substring$ +} + +FUNCTION {author.sort} +{ author empty$ + { key empty$ + { "to sort, need author or key in " cite$ * warning$ + "" } + { key sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {editor.sort} +{ editor empty$ + { key empty$ + { "to sort, need editor or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ +} + +FUNCTION {author.editor.sort} +{ author empty$ + { "missing author in " cite$ * warning$ + editor empty$ + { key empty$ + { "to sort, need author, editor, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {author.organization.sort} +% +% added - GNP. Stack author or organization for sorting (from alpha.bst). +% Unlike alpha.bst, we need entire names, not abbreviations +% +{ author empty$ + { organization empty$ + { key empty$ + { "to sort, need author, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { organization sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {editor.organization.sort} +% +% added - GNP. Stack editor or organization for sorting (from alpha.bst). +% Unlike alpha.bst, we need entire names, not abbreviations +% +{ editor empty$ + { organization empty$ + { key empty$ + { "to sort, need editor, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { organization sortify } + if$ + } + { editor sort.format.names } + if$ +} + +FUNCTION {presort} +% +% Presort creates the bibentry's label via a call to calc.label, and then +% sorts the entries based on entry type. Chicago.bst adds support for +% including organizations as the sort key; the following is stolen from +% alpha.bst. +% +{ calc.label sortify % recalculate bibitem label + year field.or.null purify$ #-1 #4 substring$ * % add year + " " + * + type$ "book" = + type$ "inbook" = + or + 'author.editor.sort + { type$ "proceedings" = + 'editor.organization.sort + { type$ "manual" = + 'author.organization.sort + 'author.sort + if$ + } + if$ + } + if$ + #1 entry.max$ substring$ % added for newapa + 'sort.label := % added for newapa + sort.label % added for newapa + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} + +ITERATE {presort} + +SORT % by label, year, author/editor, title + +STRINGS { last.label next.extra } + +INTEGERS { last.extra.num } + +FUNCTION {initialize.extra.label.stuff} +{ #0 int.to.chr$ 'last.label := + "" 'next.extra := + #0 'last.extra.num := +} + +FUNCTION {forward.pass} +% +% Pass through all entries, comparing current entry to last one. +% Need to concatenate year to the stack (done by calc.label) to determine +% if two entries are the same (see presort) +% +{ last.label + calc.label year field.or.null purify$ #-1 #4 substring$ * % add year + #1 entry.max$ substring$ = % are they equal? + { last.extra.num #1 + 'last.extra.num := + last.extra.num int.to.chr$ 'extra.label := + } + { "a" chr.to.int$ 'last.extra.num := + "" 'extra.label := + calc.label year field.or.null purify$ #-1 #4 substring$ * % add year + #1 entry.max$ substring$ 'last.label := % assign to last.label + } + if$ +} + +FUNCTION {reverse.pass} +{ next.extra "b" = + { "a" 'extra.label := } + 'skip$ + if$ + label.year extra.label * 'sort.year := + extra.label 'next.extra := +} + +EXECUTE {initialize.extra.label.stuff} + +ITERATE {forward.pass} + +REVERSE {reverse.pass} + +FUNCTION {bib.sort.order} +{ sort.label + " " + * + year field.or.null sortify + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} + +ITERATE {bib.sort.order} + +SORT % by sort.label, year, title --- giving final bib. order. + +FUNCTION {begin.bib} + +{ preamble$ empty$ + 'skip$ + { preamble$ write$ newline$ } + if$ + "\begin{thebibliography}{}" write$ newline$ +} + + +EXECUTE {begin.bib} + +EXECUTE {init.state.consts} + +ITERATE {call.type$} + +FUNCTION {end.bib} +{ newline$ + "\end{thebibliography}" write$ newline$ +} + +EXECUTE {end.bib} + diff --git a/web/noweb/src/xdoc/chicago.sty b/web/noweb/src/xdoc/chicago.sty new file mode 100644 index 0000000000..db39103502 --- /dev/null +++ b/web/noweb/src/xdoc/chicago.sty @@ -0,0 +1,264 @@ +% -*- LaTeX -*- +%%% ==================================================================== +%%% @LaTeX-style-file{ +%%% author = "Glenn Paulley", +%%% version = "4", +%%% date = "31 August 1992", +%%% time = "09:42:44 199", +%%% filename = "chicago.sty", +%%% address = "Data Structuring Group +%%% Department of Computer Science +%%% University of Waterloo +%%% Waterloo, Ontario, Canada +%%% N2L 3G1", +%%% telephone = "(519) 885-1211", +%%% FAX = "(519) 885-1208", +%%% checksum = "44674 264 1050 10394", +%%% email = "gnpaulle@bluebox.uwaterloo.ca", +%%% codetable = "ISO/ASCII", +%%% keywords = "", +%%% supported = "yes", +%%% abstract = "Contains the LaTeX style command definitions +%%% for the Chicago BibTeX styles chicago.bst and +%%% chicagoa.bst. For details, see below.", +%%% docstring = "The checksum field above contains a CRC-16 +%%% checksum as the first value, followed by the +%%% equivalent of the standard UNIX wc (word +%%% count) utility output of lines, words, and +%%% characters. This is produced by Robert +%%% Solovay's checksum utility.", +%%% } +%%% ==================================================================== +% +% chicago.sty: Style file for use with bibtex style chicago.bst, for +% bibliographies formatted according to the 13th Edition of the Chicago +% Manual of Style. +% +% 'newapa.bst' was made from 'plain.bst', 'named.bst', and 'apalike.bst', +% with lots of tweaking to make it look like APA style, along with tips +% from Young Ryu and Brian Reiser's modifications of 'apalike.bst'. +% newapa.sty formed the basis of this style, chicago.sty. Author-date +% references in newapa.bst formed the basis for chicago.bst. Chicagoa.bst +% supports annotations. +% +% Version 4 (August, 1992): +% - fixed chicago.bst and chicagoa.bst to handle long author lists in +% sorting +% - fixed chicago.bst and chicagoa.bst so that missing page numbers in +% ``article'' entries are handled correctly +% - modified chicago.sty to format entries with 2nd and subsequent lines +% indented. +% +% Citation format: (author-last-name year) +% (author-last-name and author-last-name year) +% (author-last-name et al. year) +% (author-last-name) +% author-last-name +% author-last-name (year) +% (author-last-name and author-last-name) +% (author-last-name et al.) +% (year) or (year,year) +% year or year,year +% +% Reference list ordering: alphabetical by author or whatever passes +% for author in the absence of one. +% +% This BibTeX style has support for abbreviated author lists and for +% year-only citations. This is done by having the citations +% actually look like +% +% \citeauthoryear{full-author-info}{abbrev-author-info}{year} +% +% The LaTeX style has to have the following (or similar) +% +% \let\@internalcite\cite +% \def\fullcite{\def\citeauthoryear##1##2##3{##1, ##3}\@internalcite} +% \def\fullciteA{\def\citeauthoryear##1##2##3{##1}\@internalcite} +% \def\shortcite{\def\citeauthoryear##1##2##3{##2, ##3}\@internalcite} +% \def\shortciteA{\def\citeauthoryear##1##2##3{##2}\@internalcite} +% \def\citeyear{\def\citeauthoryear##1##2##3{##3}\@internalcite} +% +% ------------------------------------------------------------------------- +% This file implements citations for the ``chicago'' bibliography style. +% Place it in a file called chicago.sty in the TeX search path. +%(Placing it in the same directory as the LaTeX document should also work.) +% +% This file is a modification of the ``newapa'' LaTeX style, +% originally adapted by Steven Spencer from the ``apalike'' LaTeX style. +% It was originally modified by Stephen N. Spencer, with further +% modifications by Young U. Ryu. +% +% The ``chicago'' BibTeX bibliography style creates citations with labels: +% \citeauthoryear{author-info}{abbrev. author-info}{year} +% +% These labels are processed by the following LaTeX commands: +% +% \cite{key} +% which produces citations with full author list and year. +% eg. (Brown 1978; Jarke, Turner, Stohl, et al. 1985) +% \citeNP{key} +% which produces citations with full author list and year, but without +% enclosing parentheses: +% eg. Brown 1978; Jarke, Turner and Stohl 1985 +% \citeA{key} +% which produces citations with only the full author list. +% eg. (Brown; Jarke, Turner and Stohl) +% \citeANP{key} +% which produces citations with only the full author list, without +% parentheses eg. Brown; Jarke, Turner and Stohl +% \citeN{key} +% which produces citations with the full author list and year, but +% can be used as nouns in a sentence; no parentheses appear around +% the author names, but only around the year. +% eg. Shneiderman (1978) states that...... +% \citeN should only be used for a single citation. +% \shortcite{key} +% which produces citations with abbreviated author list and year. +% \shortciteNP{key} +% which produces citations with abbreviated author list and year. +% \shortciteA{key} +% which produces only the abbreviated author list. +% \shortciteANP{key} +% which produces only the abbreviated author list. +% \shortciteN{key} +% which produces the abbreviated author list and year, with only the +% year in parentheses. Use with only one citation. +% \citeyear{key} +% which produces the year information only, within parentheses. +% \citeyearNP{key} +% which produces the year information only. +% +% Abbreviated author lists use the ``et al.'' construct. +% +% `NP' means `no parentheses'. +% +% This LaTeX style file must be used with the ``chicago'' or ``chicagoa'' +% (annotated chicago style) BibTeX styles. +% +\typeout{Using Chicago Manual of Style bibliography: 31 August 1992} +% +% ------------------------------------------------------------------------- +% +% Citation macros. +% +\let\@internalcite\cite +% +\def\cite{\def\@citeseppen{-1000}% + \def\@cite##1##2{(##1\if@tempswa , ##2\fi)}% + \def\citeauthoryear##1##2##3{##1 ##3}\@internalcite} +\def\citeNP{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% + \def\citeauthoryear##1##2##3{##1 ##3}\@internalcite} +\def\citeN{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2)\else{)}\fi}% + \def\citeauthoryear##1##2##3{##1 (##3}\@citedata} +\def\citeA{\def\@citeseppen{-1000}% + \def\@cite##1##2{(##1\if@tempswa , ##2\fi)}% + \def\citeauthoryear##1##2##3{##1}\@internalcite} +\def\citeANP{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% + \def\citeauthoryear##1##2##3{##1}\@internalcite} +% +\def\shortcite{\def\@citeseppen{-1000}% + \def\@cite##1##2{(##1\if@tempswa , ##2\fi)}% + \def\citeauthoryear##1##2##3{##2 ##3}\@internalcite} +\def\shortciteNP{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% + \def\citeauthoryear##1##2##3{##2 ##3}\@internalcite} +\def\shortciteN{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2)\else{)}\fi}% + \def\citeauthoryear##1##2##3{##2 (##3}\@citedata} +\def\shortciteA{\def\@citeseppen{-1000}% + \def\@cite##1##2{(##1\if@tempswa , ##2\fi)}% + \def\citeauthoryear##1##2##3{##2}\@internalcite} +\def\shortciteANP{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% + \def\citeauthoryear##1##2##3{##2}\@internalcite} +% +\def\citeyear{\def\@citeseppen{-1000}% + \def\@cite##1##2{(##1\if@tempswa , ##2\fi)}% + \def\citeauthoryear##1##2##3{##3}\@citedata} +\def\citeyearNP{\def\@citeseppen{-1000}% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% + \def\citeauthoryear##1##2##3{##3}\@citedata} + +% +% \@citedata and \@citedatax: +% +% Place commas in-between citations in the same \citeyear, \citeyearNP, +% \citeN, or \shortciteN command. +% Use something like \citeN{ref1,ref2,ref3} and \citeN{ref4} for a list. +% +\def\@citedata{% + \@ifnextchar [{\@tempswatrue\@citedatax}% + {\@tempswafalse\@citedatax[]}% +} + +\def\@citedatax[#1]#2{% +\if@filesw\immediate\write\@auxout{\string\citation{#2}}\fi% + \def\@citea{}\@cite{\@for\@citeb:=#2\do% + {\@citea\def\@citea{, }\@ifundefined% by Young + {b@\@citeb}{{\bf ?}% + \@warning{Citation `\@citeb' on page \thepage \space undefined}}% +{\csname b@\@citeb\endcsname}}}{#1}}% + +% don't box citations, separate with ; and a space +% also, make the penalty between citations negative: a good place to break. +% +\def\@citex[#1]#2{% +\if@filesw\immediate\write\@auxout{\string\citation{#2}}\fi% + \def\@citea{}\@cite{\@for\@citeb:=#2\do% + {\@citea\def\@citea{; }\@ifundefined% by Young + {b@\@citeb}{{\bf ?}% + \@warning{Citation `\@citeb' on page \thepage \space undefined}}% +{\csname b@\@citeb\endcsname}}}{#1}}% + +% (from apalike.sty) +% No labels in the bibliography. +% +\def\@biblabel#1{} + +% (from apalike.sty) +% Set length of hanging indentation for bibliography entries. +% +\newlength{\bibhang} +\setlength{\bibhang}{2em} + +% Indent second and subsequent lines of bibliographic entries. Stolen +% from openbib.sty: \newblock is set to {}. + +\newdimen\bibindent +\bibindent=1.5em +\@ifundefined{refname}% + {\@ifundefined{chapter}% + {\newcommand{\refname}{References}}% + {\newcommand{\refname}{Bibliography}}% + }% + {}% +\@ifundefined{chapter}% + {\def\thebibliography#1{\section*{\refname\@mkboth + {\uppercase{\refname}}{\uppercase{\refname}}}\list + {[\arabic{enumi}]}{\settowidth\labelwidth{[#1]} + \leftmargin\labelwidth + \advance\leftmargin\labelsep + \advance\leftmargin\bibindent + \itemindent -\bibindent + \listparindent \itemindent + \parsep \z@ + \usecounter{enumi}} + \def\newblock{} + \sloppy + \sfcode`\.=1000\relax}} + {\def\thebibliography#1{\chapter*{\refname\@mkboth + {\uppercase{\refname}}{\uppercase{\refname}}}\list + {[\arabic{enumi}]}{\settowidth\labelwidth{[#1]} + \leftmargin\labelwidth + \advance\leftmargin\labelsep + \advance\leftmargin\bibindent + \itemindent -\bibindent + \listparindent \itemindent + \parsep \z@ + \usecounter{enumi}} + \def\newblock{} + \sloppy + \sfcode`\.=1000\relax}} diff --git a/web/noweb/src/xdoc/cpif.1 b/web/noweb/src/xdoc/cpif.1 new file mode 100644 index 0000000000..bf714ea7e9 --- /dev/null +++ b/web/noweb/src/xdoc/cpif.1 @@ -0,0 +1,38 @@ +.TH CPIF 1 "local 10/40/2008" +.SH NAME +cpif \- selectively update files +.SH SYNOPSIS +.B cpif +[\fB\-eq\fP|\fB\-ne\fP] file ... +.SH DESCRIPTION +.I cpif +is an auxiliary tool for use with +.IR notangle (1) +and +.IR make (1). +.I cpif +compares its standard input to each of the named files, +overwriting the file if the comparison succeeds or if +the named file does not exist. +If neither +.B \-eq +or +.B \-ne +is specified, the default is +.BR \-ne . +Typical usage is in the following default rule, suitable for use in a Makefile. +.br + .nw.h: ; notangle \-Rheader $*.nw | cpif $*.h +.br +.SH SEE ALSO +.IR notangle (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/cpif.txt b/web/noweb/src/xdoc/cpif.txt new file mode 100644 index 0000000000..91931c01d5 --- /dev/null +++ b/web/noweb/src/xdoc/cpif.txt @@ -0,0 +1,33 @@ +CPIF(1) General Commands Manual CPIF(1) + + + +NNAAMMEE + cpif - selectively update files + +SSYYNNOOPPSSIISS + ccppiiff [--eeqq|--nnee] file ... + +DDEESSCCRRIIPPTTIIOONN + _c_p_i_f is an auxiliary tool for use with _n_o_t_a_n_g_l_e(1) and _m_a_k_e(1). _c_p_i_f + compares its standard input to each of the named files, overwriting the + file if the comparison succeeds or if the named file does not exist. + If neither --eeqq or --nnee is specified, the default is --nnee. Typical usage + is in the following default rule, suitable for use in a Makefile. + .nw.h: ; notangle -Rheader $*.nw | cpif $*.h + +SSEEEE AALLSSOO + _n_o_t_a_n_g_l_e(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 CPIF(1) diff --git a/web/noweb/src/xdoc/docdate.nw b/web/noweb/src/xdoc/docdate.nw new file mode 100644 index 0000000000..ee8bf4178c --- /dev/null +++ b/web/noweb/src/xdoc/docdate.nw @@ -0,0 +1,13 @@ +<<noweb documentation date>>= +10/40/2008 +<<AUTHOR section>>= +.SH VERSION +This man page is from +.I noweb +version $Name: v2_12 $. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/doversion b/web/noweb/src/xdoc/doversion new file mode 100755 index 0000000000..bc8ceef2a6 --- /dev/null +++ b/web/noweb/src/xdoc/doversion @@ -0,0 +1,2 @@ +#!/bin/sh +sed '/^version $Name:/s/_/./g;/^version $Name:/s/ $\././;/^version $Name:/s/$Name: v//' diff --git a/web/noweb/src/xdoc/guide.bbl b/web/noweb/src/xdoc/guide.bbl new file mode 100644 index 0000000000..7056121fd3 --- /dev/null +++ b/web/noweb/src/xdoc/guide.bbl @@ -0,0 +1,26 @@ +\begin{thebibliography}{} + +\bibitem[\protect\citeauthoryear{Aho, Kernighan, and Weinberger}{Aho + et~al.}{1988}]{aho:awk} +Aho, A.~V., B.~W. Kernighan, and P.~J. Weinberger (1988). +\newblock {\em The {AWK} Programming Language}. +\newblock Reading, MA: Addison-Wesley. + +\bibitem[\protect\citeauthoryear{Fraser and Hanson}{Fraser and + Hanson}{1995}]{fraser:retargetable:book} +Fraser, C.~W. and D.~R. Hanson (1995). +\newblock {\em A Retargetable {C} Compiler: Design and Implementation}. +\newblock Redwood City, CA: Benjamin/Cummings. + +\bibitem[\protect\citeauthoryear{Kernighan and Pike}{Kernighan and + Pike}{1984}]{kernighan:unix} +Kernighan, B.~W. and R.~Pike (1984). +\newblock {\em The {UNIX} Programming Environment}. +\newblock Englewood Cliffs, NJ: Prentice-Hall. + +\bibitem[\protect\citeauthoryear{Ramsey}{Ramsey}{1994}]{ramsey:simplified} +Ramsey, N. (1994, September). +\newblock Literate programming simplified. +\newblock {\em IEEE Software\/}~{\em 11\/}(5), 97--105. + +\end{thebibliography} diff --git a/web/noweb/src/xdoc/guide.tex b/web/noweb/src/xdoc/guide.tex new file mode 100644 index 0000000000..cd1a84c2c2 --- /dev/null +++ b/web/noweb/src/xdoc/guide.tex @@ -0,0 +1,1162 @@ +\documentclass{article} +\usepackage{chicago,array,tabularx,afterpage} +% chicago bibliography style is available from CTAN; others are standard + +\setlength{\extrarowheight}{1pt} + +\title{The {\tt noweb} Hacker's Guide} +\author{Norman Ramsey\thanks{Author's current address is Department of +Computer Science, Tufts University, Medford, MA 02155, USA; +send email to {\tt nr@cs.tufts.edu}.}\\Department of Computer Science\\ +Princeton University} +\date{September 1992\\(Revised August 1994, December 1997)} + +\setcounter{secnumdepth}{0} +\setcounter{tocdepth}{3} +\clubpenalty=10000 +\widowpenalty=10000 + +\newcommand\kw[1]{\texttt{@#1}} +\newcommand\kws[2]{\kw{#1}\hbox{\thinspace}\ldots~\kw{#2}} +\newcommand\ikw[1]{\kw{index~#1}} +\newcommand\ikws[2]{\ikw{#1}\hbox{\thinspace}\ldots~\ikw{#2}} +\newcommand\xkw[1]{\kw{xref~#1}} +\newcommand\xkws[2]{\xkw{#1}\hbox{\thinspace}\ldots~\xkw{#2}} + +% l2h argblock kw <tt>@ </tt> +% l2h argblock kws <tt>@ ...#</tt>@ +% l2h argblock ikw <tt>@index# </tt> +% l2h argblock ikws <tt>@index# ...#</tt>@index# +% l2h argblock xkw <tt>@xref# </tt> +% l2h argblock xkws <tt>@xref# ...#</tt>@xref# + +\newcommand\ltxlabel{\relax} +\let\ltxlabel=\label + +% l2h let ltxlabel label + +\renewcommand\label{{\rm\it label\/}} +\newcommand\tag{{\rm\it tag\/}} +\newcommand\ident{{\rm\it ident\/}} + +% l2h substitution label <i>label</i> +% l2h substitution tag <i>tag</i> +% l2h substitution ident <i>ident</i> + + +% title in a table + +\newcommand\ttitle[1]{\noalign{\medskip}\multicolumn{2}{c}{#1}\\\noalign{\smallskip}} + +% l2h argblock ttitle <br><center> </center><br> + +% figure hacking + +\newcommand\topfigrule{% + \vbox to 0pt{ + \vskip 5pt + \centerline{\vrule height 1pt depth 0pt width 3in} + \vss}} + +\newcommand\botfigrule{% + \vbox to 0pt{ + \vss + \centerline{\vrule height 1pt depth 0pt width 3in} + \vskip 5pt}} + +\begin{document} + +\maketitle + +\begin{abstract} +{\tt Noweb} is unique among literate-programming tools in its +pipelined architecture, which makes it easy for users to change +its behavior or to add new features, without even recompiling. +This guide describes the representation used in the +pipeline and the behavior of the existing pipeline stages. +Ordinary users will find nothing of interest here; the guide is +addressed to those who want to change or extend {\tt noweb}. +\end{abstract} + +\clearpage + +\tableofcontents +\listoftables + +\newpage + +\section{Introduction} + +\citeN{ramsey:simplified} describes {\tt {\tt noweb}} from a user's +point of view, showing its simplicity and examples of its use. +The {\tt {\tt noweb}} tools are implemented as {\em pipelines}. +Each pipeline begins with the {\tt noweb} +source file. Successive stages of the pipeline implement simple +transformations of the source, until the desired result emerges from +the end of the pipeline. Figures +\ref{fig:pipe-notangle}~and~\ref{fig:pipe-noweave} on +page~\pageref{fig:pipe-notangle} show pipelines for +{\tt notangle} and {\tt noweave}. +Pipelines are responsible for {\tt {\tt noweb}}'s +extensibility, which enables its users to create new literate-programming +features without having to write their own tools. +This document explains how to change or extend {\tt noweb} by +inserting or removing pipeline stages. +Readers should be familiar with the {\tt {\tt noweb}} man pages, which +describe the structure of {\tt {\tt noweb}} source files. + +{\tt Markup}, which is the first stage in +every pipeline, converts {\tt noweb} source to a representation easily +manipulated by common Unix tools like {\tt sed} and {\tt awk}, simplifying +the construction of later pipeline stages. Middle stages add +information to the representation. {\tt notangle}'s final stage converts to +code; {\tt noweave}'s final stages convert to TeX, LaTeX or HTML. +Middle stages are called {\em filters}, by analogy with Unix filters. +Final stages are called {\em back ends}, by analogy with +compilers---they don't transform {\tt {\tt noweb}}'s intermediate +representation; they emit something else. + +\section{The pipeline representation} + +In the pipeline, every line begins with an at sign and one of the +keywords shown in Table~\ref{table:keywords}. +The structural keywords +represent the {\tt noweb} source syntax directly. +They must appear in particular orders that reflect the +structure of the source. +The tagging keywords can be inserted +essentially anywhere (within reason), and with some exceptions, they are not generated +by {\tt markup}. +The wrapper keywords +mark the beginning and end of file, +and they carry information about what formatters are supposed to do in +the way of leading and trailing boilerplate. +They are used by {\tt noweave} but not by {\tt notangle}, and they are +inserted directly by the {\tt noweave} shell script, not by {\tt markup}. + +\begin{table}[t] +\noindent +\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +% l2h macro ttitle 1 </td><td></td></tr><tr><td colspan=2 align=center><b>#1</b></td></tr><tr><td> +\ttitle{Structural keywords} +\hline +@begin {\rm\it kind} $n$&Start a chunk\\ +@end {\rm\it kind} $n$&End a chunk\\ +@text {\rm\it string}&{\rm\it string} appeared in a chunk\\ +@nl&A newline appeared in a chunk\\ +@defn {\rm\it name}&The code chunk named {\rm\it name} is being defined\\ +@use {\rm\it name}&A reference to code chunk named {\rm\it name}\\ +@quote&Start of quoted code in a documentation chunk\\ +@endquote&End of quoted code in a documentation chunk\\ +\hline +\ttitle{Tagging keywords} +\hline +@file {\rm\it filename}&Name of the file from which the chunks came\\ +@line $n$&Next text line came from source line $n$ in current file\\ +@language {\rm\it language}&Programming language in which code is written\\ +@index \ldots&Index information.\\ +@xref \ldots&Cross-reference information.\\ +\hline +\end{tabularx}\\ +\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +\ttitle{Wrapper keywords} +\hline +@header {\rm\it formatter options}& + First line, identifying formatter and options\\ +@trailer {\rm\it formatter}&Last line, identifying formatter.\\ +\hline +%\end{tabularx}\\ +%\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +\ttitle{Error keyword} +\hline +@fatal {\rm\it stagename} {\rm\it message}& + A fatal error has occurred.\\ +\hline +%\end{tabularx}\\ +%\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +\ttitle{Lying, cheating, stealing keyword} +\hline +@literal {\rm\it text}& + Copy {\it text} to output.\\ +\hline +\end{tabularx} + +\caption{Keywords used in {\tt noweb}'s pipeline representation} +\ltxlabel{table:keywords} + +\end{table} + +\subsection{Structural keywords} + +The structural keywords represent the chunks in the {\tt noweb} source. +Each chunk is bracketed by a \kws{begin}{end} pair, +and the {\it kind} of chunk is either {\tt docs} or {\tt code}. +The \kw{begin} and \kw{end} are numbered; within a single file, +numbers must be monotonically increasing, but they need not be +consecutive. +Filters may change chunk numbers at will. + +Depending on its kind, a chunk may contain {\em documentation} or {\em +code}. +Documentation may contain text and newlines, represented by \kw{text} +and \kw{nl}. +It may also contain {\em quoted code} bracketed by +\kws{quote}{endquote}. +Every \kw{quote} must be terminated by an \kw{endquote} within the +same chunk. +Quoted code corresponds to the \verb+[[+\ldots \verb+]]+ construct in +the {\tt noweb} source. + +Code, whether it appears in quoted code or in a code chunk, +may contain text and newlines, and also definitions and uses of +code chunks, marked with \kw{defn} and \kw{use}. +The first structural keyword in any code chunk must be \kw{defn}. +\kw{defn} may be preceded or followed by tagging keywords, but the +next structural keyword +must be \kw{nl}; +together, the \kw{defn} and \kw{nl} +represent the initial \verb+<<chunk name>>=+ +that starts the chunk (including the terminating newline). + +A few facts follow from what's already stated above, but are probably +worth noting explicitly: +\begin{itemize} +\item +Quoted code may not appear in code, nor may it appear in +\kw{defn} or \kw{use}. +{\tt noweave} back ends are encouraged to give \verb+[[+\ldots +\verb+]]+ special treatment when it appears in \verb+defn+ or +\verb+use+, so that the text contained therein is treated as if it +were quoted code. +\item +The text in chunks may be distributed among as many \kw{text} +keywords as desirable. Any number of empty \kw{text} keywords are +permitted. In particular, it is not realistic to expect that a single +line will be represented in a single \kw{text} (see the discussion of +{\tt finduses} on page~\pageref{finduses}). +\item +{\tt markup} will sometimes emit \kw{use} within +\kws{quote}{endquote}, for example from a source like \verb+[[<<chunk name>>]]+. +\item +No two chunks have the same number. +\item +Because later filters can change chunk numbers, no filter should +plant references to chunk numbers anywhere in the pipeline. +\end{itemize} + +\subsection{Tagging keywords} + +The structural keywords carry all the code and documentation that +appears in a {\tt noweb} source file. +The tagging keywords carry information about that code or +documentation. +The \kw{file} keyword carries the name of the source file from which the +following lines come. +The \kw{line} keyword give the line number of the next \kw{text} line +within the current file (as determined by the most recent \kw{file} +keyword). +The only guarantee about where these appear is that {\tt markup} +introduces each new source file with a \kw{file} that appears between +chunks. +Most filters ignore \kw{file} and \kw{line}, but {\tt nt} +respects them, so that {\tt +notangle} can properly mark line numbers if some {\tt noweb} +filter starts moving lines around. + +\subsubsection{Programming languages} + +To support automatic indexing or prettyprinting, it's possible to +indicate the programming language in which a chunk is written. +The \kw{language} keyword may appear at most once between +each \kw{begin~code} and \kw{end code} pair. +Standard values of \kw{language} and their associated meanings are: +\begin{quote} +\begin{tabularx}{\textwidth}{@{}>{\ttfamily}lX@{}} +\texttt{awk}&awk\\ +\texttt{c}&C\\ +\texttt{c++}&C$++$\\ +\texttt{caml}&CAML\\ +\texttt{html}&HTML\\ +\texttt{icon}&Icon\\ +\texttt{latex}&{\LaTeX} source\\ +\texttt{lisp}&Lisp or Scheme\\ +\texttt{make}&A Makefile\\ +\texttt{m3}&Modula-3\\ +\texttt{ocaml}&Objective CAML\\ +\texttt{perl}&A perl script\\ +\texttt{python}&Python\\ +\texttt{sh}&A shell script\\ +\texttt{sml}&Standard ML\\ +\texttt{tex}&plain {\TeX}\\ +\texttt{tcl}&tcl\\ +\end{tabularx} +\end{quote} +If the \kw{language} keyword catches on, it may be useful to create an +automatic registry on the World-Wide Web. + +I have made it impossible to place \kw{language} information directly +in a \texttt{noweb} source file. +My intent is that tools will identify the language of the root chunks +using any of several methods: conventional names of chunks, being told +on a command line, or identifying the language by looking at the +content of the chunks. +(Of these methods, the most practical is to name the root chunks after +the files to which they will be extracted, and to use the same naming +conventions as \texttt{make} to figure out what the contents are.) +A \texttt{noweb} filter will tag non-root chunks with the appropriate +\kw{language} by propagating information from uses to definitions. + + + + +\subsubsection{Indexing and cross-reference concepts} + + +The index and cross-reference commands use \label s, \ident s, and \tag s. +A \label\ is a unique string generated to refer to some element of a +literate program. +They serve as labels or ``anchor points'' for back ends that are +capable of implementing their own cross-reference. +So, for example, the {\LaTeX} back end uses labels as arguments to \verb+\label+ +and \verb+\ref+, and the HTML back end uses labels to name and refer +to anchors. +Labels never contain white space, which simplifies parsing. +The standard filters cross-reference at the chunk level, so that each +label refers to a particular code chunk, and all references to that +chunk use the same label. + +An \ident\ refers to a source-language identifier. +{\tt Noweb}'s concept of identifier is general; an identifier is +an arbitrary string. +It can even contain whitespace. +Identifiers are used as keys in the index; references to the same +string are assumed to denote the same identifier. + +{\rm\it Tag\/}s are the strings used to identify components for +cross-reference in the final document. +For example, Classic {\tt WEB} uses consecutive ``section numbers'' to +refer to chunks. +{\tt Noweb}, by default, uses ``sub-page references,'' e.g., ``24b'' +for the second chunk appearing on page~24. +The HTML back end doesn't use any tags at all; instead, it +implements cross-referencing using the ``hot link'' mechanism. + +The final step of cross-referencing involves generating tags and +associating a tag with each label. +All the existing back ends rely on a document formatter to do this +job, but that strategy might be worth changing. +Computing tags within a {\tt noweb} filter could be lots easier than +doing it in a formatter. +For example, a filter that computed sub-page numbers by grubbing in +{\tt .aux} files would be pretty easy to write, and it would eliminate +a lot of squirrely {\LaTeX} code. + + +\subsubsection{Index information} + +I've divided the index keywords into several groups. +There seems to be a plethora of keywords, but most of them are +straightforward representations of parts of a document produced by +{\tt noweave}. Readers may want to have a sample of {\tt noweave}'s +output handy when studying this and the next section. + +\begin{table} +\begin{center} +\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +\ttitle{Definitions, uses, and {\tt @ \%def}} +\hline +@index defn \ident&The current chunk contains a definition of \ident\\ +@index localdefn \ident&The current chunk contains a definition of + \ident, which is not to be visible outside this file\\ +@index use \ident&The current chunk contains a use of \ident\\ +@index nl&A newline that is part of markup, not part of the chunk\\ +\hline +\ttitle{Identifiers defined in a chunk} +\hline +@index begindefs&Start list of identifiers defined in this chunk\\ +@index isused \label& + The identifier named in the following \ikw{defitem} is used in + the chunk labelled by \label\\ +@index defitem \ident& + \ident\ is defined in this chunk, and it is used in all the + chunks named in the immediately preceding \ikw{isused}.\\ +@index enddefs&End list of identifiers defined in this chunk\\ +\hline +\ttitle{Identifiers used in a chunk} +\hline +@index beginuses&Start list of identifiers used in this chunk\\ +@index isdefined \label& + The identifier named in the following \ikw{useitem} is defined in + the chunk labelled by \label\\ +@index useitem \ident& + \ident\ is used in this chunk, and it is defined in each of the + chunks named in the immediately preceding \ikw{isdefined}.\\ +@index enduses&End list of identifiers used in this chunk\\ +\hline +\end{tabularx}\\ +\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +\ttitle{The index of identifiers} +\hline +@index beginindex&Start of the index of identifiers\\ +@index entrybegin \label\ \ident& + Beginning of the entry for \ident, whose first definition is + found at \label\\ +@index entryuse \label& + A use of the identifer named in the last \ikw{entrybegin} + occurs at the chunk labelled with \label.\\ +@index entrydefn \label& + A definition of the identifer named in the last \ikw{entrybegin} + occurs at the chunk labelled with \label.\\ +@index entryend& + End of the entry started by the last \ikw{entrybegin}\\ +@index endindex&End of the index of identifiers\\ +\hline +\end{tabularx} +\end{center} +\caption{Indexing keywords} +\ltxlabel{tab:index} +\vskip -5pt +\end{table} + + + +\paragraph{Definitions, uses, and {\tt @ \%def}} + +\ikw{defn}, \ikw{use}, and \ikw{nl} are the only +\kw{index} keywords that appear in {\tt markup}'s output, and thus +which can appear in any program. +They may appear only within the boundaries of a code chunk (\kws{begin +code}{end code}). +\ikw{defn} and \ikw{use} simply indicate that the current chunk +contains a definition or use of the identifier \ident\ which follows +the keyword. +The placement of \ikw{defn} need not bear a relationship to the +text of the definition, but \ikw{use} is normally followed by a +\kw{text} that contains the source-code text identified as the +use.% +\footnote{This property can't hold when one identifier is a prefix of +another; see the description of {\tt finduses} on page~\pageref{finduses}.} + +Instances of \ikw{defn} normally come from one of two sources: either a +language-dependent recognizer of definitions, or a hand-written +\verb+@ %def+ line.% +\footnote{The \texttt{@ \char`\%def} notation has been deprecated +since version~2.10.} +In the latter case, the line is terminated by a newline that is +neither part of a code chunk nor part of a documentation chunk. +To keep line numbers accurate, that newline can't just be abandoned, +but neither can it be represented by \kw{nl} in a documentation or +code chunk. +The solution is the \ikw{nl} keyword, which serves no purpose other +than to keep track of these newlines, so that back ends can produce +accurate line numbers. + +Following a suggestion by Oren Ben-Kiki, +\ikw{localdefn} indicates a definition that is not to be visible +outside the current file. +It may be produced by a language-dependent recognizer or other filter. +Because I have questions about the need for \ikw{localdefn}, there is +officially no way to cause {\tt markup} to produce it. + +\paragraph{Identifiers defined in a chunk} + +The keywords from \ikw{begindefs} to \ikw{enddefs} are used to represent +a more complex data structure giving the list of identifiers defined +in a code chunk. +The constellation represents a list of identifiers; one \ikw{defitem} +appears for each identifier. +The group also tells in what other chunks each identifier is used; +those chunks are listed by \ikw{isused} keywords which appear just +before \ikw{defitem}. +The labels in these keywords appear in the order of the corresponding +code chunks, and there are no duplicates. + +These keywords can appear anywhere inside a code chunk, but +filters are encouraged to keep these keywords together. +The standard +filters guarantee that only +\ikw{isused} and \ikw{defitem} appear between \ikw{begindefs} and +\ikw{enddefs}. +The standard filters put them at the end of the code chunk, which +simplifies translation by the {\LaTeX} back end, but that strategy +might change in the future. + + +It should go without saying, but the keywords in these and all similar +groups (including some \kw{xref} groups) must be properly structured. +That is to say: +\begin{enumerate} +\item +Every \ikw{begindefs} must have a matching \ikw{enddefs} within the +same code chunk. +\item +\ikw{isused} and \ikw{defitem} may appear only between matching +\ikw{begindefs} and \ikw{enddefs}. +\item +The damn things can't be nested. +\end{enumerate} + + +\paragraph{Identifiers used in a chunk} + + +The keywords from \ikw{beginuses} to \ikw{enduses} are the dual of +\ikw{begindef} to \ikw{enddef}; +the structure lists the identifiers used in the current code chunk, +with cross-references to the definitions. +Similar interpretations and restrictions apply. +Note that an identifier can be defined in more than one chunk, +although we expect that to be an unusual event. +{\hfuzz=1.2pt\par} + +\paragraph{The index of identifiers} + +Keywords \ikw{beginindex} to \ikw{endindex} represent the +complete index of all the identifiers used in the document. +Each entry in the index is bracketed by \ikws{entrybegin}{entryend}. +An entry provides the name of the identifier, plus the labels of all the +chunks in which the identifier is defined or used. +The label of the first defining chunk +is given at the beginning of the entry so that back ends needn't +search for it. +{\hfuzz=4.9pt\par} + +Filters are encouraged to keep these keywords together. +The standard filters put them almost at the very end of the {\tt +noweb} file, just before the optional \kw{trailer}. + + + +\subsubsection{Cross-reference information} + +\newcommand\anchor{{\rmfamily\textit{anchor}}} + +% l2h substitution anchor <i>anchor</i> + +The most basic function of the cross-referencing keywords is to +associate labels and pointers (cross-references) with elements of the +document, which is done with the \xkw{ref} and \xkw{label} keywords. +The other \kw{xref} keywords all express chunk cross-reference +information that is emitted directly by one or more back ends. + +Chunk cross-reference introduces the idea of +an {\anchor}, which is a label that refers to an ``interesting point'' +we identify with the +beginning of a code chunk. +The anchor is the place we expect to turn when we want to know about a +code chunk; +its exact value and interpretation depend on the back end being used. +The standard {\LaTeX} back end uses the sub-page number of the +defining chunk as the anchor, but the standard HTML back end uses some +\kw{text} from the documentation chunk preceding the code chunk. + + +\begin{table} +\begin{center} +\begin{tabularx}{\textwidth}{|>{\tt}l>{\raggedright\arraybackslash}X|} +\ttitle{Basic cross-reference} +\hline +@xref label \label&Associates \label\ with tagged item.\\ +@xref ref \label& + Cross-reference from tagged item to item associated with \label.\\ +\hline +\ttitle{Linking previous and next definitions of a code chunk} +\hline +@xref prevdef \label& + The \kw{defn} from the previous definition of this chunk is + associated with \label.\\ +@xref nextdef \label& + The \kw{defn} from the next definition of this chunk is + associated with \label.\\ +\hline +\ttitle{Continued definitions of the current chunk} +\hline +@xref begindefs&Start ``This definition is continued in \ldots''\\ +@xref defitem \label&Gives the label of a chunk in which the +definition of the current chunk is continued.\\ +@xref enddefs&Ends the list of chunks where definition is continued.\\ +\hline +\ttitle{Chunks where this code is used} +\hline +@xref beginuses&Start ``This code is used in \ldots''\\ +@xref useitem \label&Gives the label of a chunk in which this + chunk is used.\\ +@xref enduses&Ends the list of chunks in which this code is used.\\ +@xref notused {\rm\it name}& + Indicates that this chunk isn't used anywhere in this document.\\ +\hline +\ttitle{The list of chunks} +\hline +@xref beginchunks&Start of the list of chunks\\ +@xref chunkbegin \label\ {\it name}& + Beginning of the entry for chunk {\it name}, whose {\anchor} + is found at \label.\\ +@xref chunkuse \label& + The chunk is used in the chunk labelled with \label.\\ +@xref chunkdefn \label& + The chunk is defined in the chunk labelled with \label.\\ +@xref chunkend&End of the entry started by the last \xkw{chunkbegin}\\ +@xref endchunks&End of the list of chunks\\ +\hline +\ttitle{Converting labels to tags} +\hline +@xref tag \label\ \tag&Associates \label\ with \tag.\\ +\hline +\end{tabularx} +\end{center} +\vskip -4pt +\caption{Cross-referencing keywords} +\ltxlabel{tab:xref} +\vskip -3pt +\end{table} + + + +\paragraph{Basic cross-reference} + +\xkw{label} and \xkw{ref} are named by analogy with the {\LaTeX} +\verb+\label+ and \verb+\ref+ commands. +\xkw{label} is used to associate a \label\ with a succeeding item. +Items that can be so labelled include +\begin{quote} +\begin{tabularx}{\linewidth}{>{\tt}l>{\raggedright\arraybackslash}X} +@defn&Labels the code chunk that begins with this \rlap{\kw{defn}.}\\ + % cheating the line breaker +@use&Labels this particular use.\\ +@index defn&Labels this definition of an identifier.\\ +@index use&Labels this use of an identifier.\\ +@text&Typically labels part of a documentation chunk.\\ +@end docs&Typically labels an empty documentation chunk.\\ +\end{tabularx} +\end{quote} +I haven't made up my mind whether this should be the complete set, but +these are the ones used by the standard filters. +Most back ends use the chunk as the basic unit of cross-reference, so +the labels of \kw{defn} are the ones that are most often used. +The HTML back end, however, does something a little different---it +uses labels that refer to documentation preceding a chunk, because the +typical HTML browser (Mosaic) places the label% +\footnote{The HTML terminology calls a label an ``anchor.''} +at the top of the screen, and using the label of the \kw{defn} would +lose the documentation immediately preceding a chunk. +The labels used by this back end usually point to \kw{text}, but they +may point to \kw{end docs} when no text is available. + + +\xkw{ref} is used to associate a reference with a succeeding item. +Such items include +\begin{quote} +\begin{tabularx}{\linewidth}{l>{\raggedright\arraybackslash}X} +{\tt @defn}, {\tt @use}&Refers to the label used as an {\anchor} for this chunk.\\ +\vtop{\hbox{\strut{\tt @index defn},}\hbox{\strut{\tt @index use}}}& + Refers to the label used as an {\anchor} for the first + chunk in which this identifier is defined.\\ +\end{tabularx} +\end{quote} + + +\paragraph{Linking previous and next definitions of a code chunk} + +\xkw{prevdef} and \xkw{nextdef} may appear anywhere in a code chunk, +and they give the labels of the preceding and succeeding definitions +of that code chunk, if any. +Standard filters currently put them at the beginning of the code +chunk, following the initial \kw{defn}, so the information can be used +on the \kw{defn} line, +\`a la \citeN{fraser:retargetable:book}. + + +\paragraph{Continued definitions of the current chunk} + +The keywords ranging from \xkw{begindefs} to \xkw{enddefs} appear in the first +definition of each code chunk. +They provide the information needed by the ``This definition is +continued in \ldots'' message printed by the standard {\LaTeX} back +end. +They can appear anywhere in a code chunk, but standard filters put +them after all the \kw{text} and \kw{nl}s, so that back ends can just +print out text. + + +\paragraph{Chunks where this code is used} + +The keywords from \xkw{beginuses} to \xkw{enduses} are the dual of +\xkw{begindefs} to \xkw{enddefs}; they show where the current chunk is +used. +As with \xkws{begindefs}{enddefs}, they appear only in the first +definition of any code chunk, and they come at the end. +Sometimes, as with root chunks, the code isn't used anywhere, in which +case \xkw{notused} appears instead of \xkws{beginuses}{enduses}. +The name of the current chunk appears as an argument to \xkw{notused} +because some back ends may want to print a special message for unused +chunks---they might be written to files, for example. + +\paragraph{The list of chunks} + +The list of chunks, which is defined by the keywords +\xkws{beginchunks}{endchunks}, is the +analog of the index of identifiers, but it lists all the code chunks +in the document, not all the identifiers. + +Filters are encouraged to keep these keywords together. +The standard filters put them at the end of the {\tt +noweb} file, just before the index of identifiers. + + +\paragraph{Converting labels to tags} + +None of the existing back ends actually computes tags; they all use +formatting engines to do the job. +The {\LaTeX} back end uses an elaborate macro package to compute +sub-page numbers, and the HTML back end arranges for ``hot links'' to +be used instead of textual tags. +Some people have argued that literate-programming tools shouldn't require +elaborate macro packages, that they should use the basic facilities +provided by a formatter. Nuweb, for example, uses standard {\LaTeX} +commands only, but goes digging through {\tt .aux} files to find +labels and compute sub-page numbers. +Doing this kind of computation in a real programming language is much +easier than doing it with {\TeX} macros, and I expect that one day +{\tt noweb} will have a tag-computing filter, the results of +which will be expressed using the \xkw{tag} keyword. + +The rules governing \xkw{tag} are that it can appear anywhere. +None of the standard filters or back ends does anything with it. + + + +\subsection{Wrapper keywords} + +The wrapper keywords, \kw{header} and \kw{trailer}, are anomalous in +that they're not generated by {\tt markup} or by any of the +standard filters; instead they're inserted by the {\tt noweave} shell +script at the very beginning and end of file. +The standard {\TeX}, {\LaTeX}, and HTML back ends use them to provide +preamble and postamble markup, i.e., boilerplate that usually has to +surround a document. +They're not required (sometimes you don't want that boilerplate), but +when they appear they must be the very first and last lines in the +file, and the formatter names must match. + +\subsection{Error keyword} + +The error keyword \kw{fatal} signifies that a fatal error as +occurred. +The pipeline stage originating such an error gives its own name and a +message, and it also writes a message to standard error. +Filters seeing \kw{fatal} must copy it to their output and terminate +themselves with error status. +Back ends seeing \kw{fatal} must terminate themselves with error +status. (They should not write anything to standard error since that +will have been done.) + +Using \kw{fatal} enables shell scripts to detect +that something has gone wrong even if the only exit status they have +access to is the +exit status of the last stage in a pipeline. + +\subsection{Lying, cheating, stealing keyword} + +The \kw{literal} keyword is used to hack output directly into \texttt{noweave} +back ends, like \texttt{totex} and \texttt{tohtml}. +These back ends simply copy the text to their output. +Tangling back ends ignore \kw{literal}. +The \kw{literal} keyword is used by Master Hackers who are too lazy to +write new back ends. +Its use is deprecated. +It should not exist. +But it will be retained forever in the name of Backward Compatibility. + + + + +\section{Standard filters} + +All the standard filters, unless otherwise noted, read the {\tt noweb} +keyword format on standard input and write it on standard output. +Some filters may also use auxiliary files. + +\subsection{\tt markup} + +Strictly speaking, {\tt markup} is a front end, not a filter, but I +discuss it along with filters because it generates the output that is +massaged by all the filters. +{\tt markup}'s output represents a sequence of files. +Each file is represented by a ``{\tt @file~{\rm\it filename}}'' line, +followed by a sequence of chunks. +{\tt markup} numbers chunks consecutively, starting at~0. +It also recognizes and undoes the escape sequence for double brackets, +e.g.~converting ``{\tt @<<}'' to ``{\tt <<}''. +The only tagging keywords found in its output are \ikw{defn} or +\ikw{nl}; despite what's written about it, \ikw{use} never appears. + +\subsection{\tt autodefs.*} + +I've written half a dozen language-dependent filters that use simple +heuristics (``fuzzy parsing'' if you prefer) to try to identify +interesting definitions of identifiers. +Many of these doubtless rely on my own idiosyncratic coding styles, +but all of them provide good value for little effort. +None of them does anything more complicated than scan individual +\kw{text} lines in code chunks, spitting out \ikw{defn} +and \ikw{localdefn} lines after +the \kw{text} line whenever it thinks it's found something. +All the filters are written in Icon and use a central core defined in +\verb+icon/defns.nw+. +The C filter is the most complicated; it actually tries to understand +parts of the C grammar for declarations. +None of these filters has any command-line options. + +\subsection{\tt finduses} + +\ltxlabel{finduses} + +Using code contributed by Preston Briggs, this filter makes two passes +over its input. +The first pass reads in all the \ikw{defn} and \ikw{localdefn} lines and builds an +Aho-Corasick recognizer for the identifiers named therein. +The second pass copies the input, searching for these identifiers in +each \kw{text} line that is code. +When it finds an identifier, {\tt finduses} breaks the \kw{text} line +into pieces, inserting \ikw{use} immediately before the \kw{text} +piece that contains the identifier just found.% +\footnote{The behavior described would duplicate \kw{text} pieces +whenever one identifier was a prefix of another. +This event is rare, and probably undesirable, but it can happen if, +for example, the C$++$ names {\tt MyClass} and {\tt MyClass::Function} +are both considered identifiers. +In this case, whatever identifier is found first is emitted first, and +only the unemitted pieces of longer identifiers are emitted.} +{\tt finduses} assumes that previous filters will not have broken +\kw{text} lines in the middle of identifiers. + + +The \verb+-noquote+ command-line option prevents {\tt finduses} from +searching for uses in quoted code. +If {\tt finduses} is given arguments, it takes those arguments to be +file names, and it reads lists of identifiers (one per line) from the +files so named, rather than from its input. +This technique enables {\tt finduses} to make a single pass over its +input; {\tt noweave} uses it to implement the {\tt -indexfrom} option. + +{\tt finduses} shouldn't be run before filters which, like the {\tt +autodefs} filters, expect one line to be represented in a single +\kw{text}. +Filters (or back ends) that have to be run late, like +prettyprinters, should be prepared to deal with lines broken into +pieces and with \kw{index} and \kw{xref} tags intercalated. + +\subsection{\tt noidx} + +{\tt noidx} computes all the index and cross-reference information +represented by the \kw{index} and \kw{xref} keywords. + +The {\tt -delay} command-line option delays heading material until +after the first chunk, and brings trailing material before the last +chunk. +In particular, it causes +the list of chunks and the index of identifiers to be emitted before +the last chunk. + +The {\tt -docanchor $n$} option sets the anchor for a code chunk to be +either: +\begin{enumerate} +\item +If a documentation chunk precedes the code chunk and is $n$ or more lines long, $n$ +lines from the end of that documentation chunk. +\item +If a documentation chunk precedes the code chunk and is fewer than $n$ +lines long, at the beginning of that documentation chunk. +\item +If no documentation chunk precedes the code chunk, at the beginning of +the code chunk, just as if {\tt -docanchor} had not been used. +\end{enumerate} +This option is used to create anchors suitable for the HTML back end. + + +\section{Standard back ends} + +\subsection{\tt nt} + +The {\tt nt} back end implements {\tt notangle}. +It extracts the program defined by a single code chunk (expanding all +uses to form their definitions) and writes that program on standard +output. +Its command-line options are: +\begin{quote} +\begin{tabularx}{\linewidth}{lX} +\tt -t&Turn off expansion of tabs.\\ +\tt -t$n$&Expand tabs on $n$-column boundaries.\\ +\tt -R{\rmfamily\textit{name}}&Expand the code chunk named \textit{name}.\\ +\tt -L{\rmfamily\textit{format}}&Use \textit{format} as the format string + to emit line-number information. +\end{tabularx} +\end{quote} +See the man page for {\tt notangle} for details on the operation of +{\tt nt}. + + +\subsection{\tt mnt} + +{\tt mnt} (for Multiple NoTangle) +is a back end that can extract several code chunks from a +single document in a single pass. It is used to make the {\tt noweb} +shell script more efficient. +In addition to the {\tt -t} and {\tt -L} options recognized by {\tt +nt}, it recognizes {\tt -all} as an instruction to extract and write +to files all of the code chunks that conform to the rules set out in +the {\tt noweb} man page. +It also accepts arguments, as well as options; each argument is taken +to be the name of a code chunk that should be emitted to the file of +the same name. +Unlike {\tt nt}, {\tt mnt} has the function of {\tt cpif} built +in---it writes to a temporary file, then overwrites an existing file +only if the temporary file is different. + + + +\subsection{\tt tohtml} + +This back end emits HTML. +It uses the formatter {\tt html} with \kw{header} and \kw{trailer} to +emit suitable HTML boilerplate. +For other formatters (like {\tt none}) it emits no header or trailer. +Its command-line options are: +\begin{quote} +\begin{tabularx}{\linewidth}{lX} +\tt -delay&Accepted, for compatibility with other back ends, but ignored.\\ +\tt -localindex&Produces local identifier cross-reference after each code chunk.\\ +\tt -raw&Wraps text generated for code chunks in a {\LaTeX} {\tt rawhtml} +environment, making the whole document suitable for processing with +{\tt latex2html}.\\ +\end{tabularx} +\end{quote} + + +\subsection{\tt totex} + +{\tt totex} implements both the plain {\TeX} and {\LaTeX} back ends, +using \kw{header tex} and \kw{header latex} to distinguish them. +When using a {\LaTeX} header, {\tt totex} places the optional text +following the header inside a \verb+\noweboptions+ command. + +On the command line, the {\tt -delay} option makes {\tt totex} delay +filename markup until after the first documentation chunk; this +behavior makes the first documentation chunk a ``limbo'' +chunk, which can usefully contain commands like \verb+\documentclass+. +The {\tt -noindex} option suppresses output relating to the index of +identifiers; it is used to implement {\tt noweave -x}. +{\hfuzz=1.2pt\par} + +\subsection{\tt unmarkup} + +{\tt unmarkup} attempts to be the inverse of markup---a document +already in the pipeline is converted back to {\tt noweb} source form. +This back end is useful primarily for trying to convert other literate +programs to {\tt noweb} form. +It might also be used to capture and edit the output of an automatic +definition recognizer. + +\section{Standard commands} + + +\begin{figure}[t] +\noindent +\begin{tabbing} +XXl\=XXl\=XXl\=XXl\=XXl\=XXl\=XXl\=XXl\={}\kill +\>\+{\tt markup}: Convert to pipeline representation\+\\ +{\tt nt:} Extract desired chunk to standard output +\end{tabbing} +\caption{Stages in pipeline for {\tt notangle}} +\ltxlabel{fig:pipe-notangle} + +\noindent +\begin{tabbing} +XXl\=XXl\=XXl\=XXl\=XXl\=XXl\=XXl\=XXl\={}\kill +\>\+{\tt markup}: Convert to pipeline representation\+\\ +{\tt autodefs.c}: Find definitions in C code\+\\ +{\tt finduses -noquote}: Find uses of defined identifiers\+\\ +{\tt noidx}: Add index and cross-reference information\+\\ +{\tt totex}: Convert to {\LaTeX} +\end{tabbing} +\caption{Stages in pipeline for {\tt noweave -index -autodefs c}} +\ltxlabel{fig:pipe-noweave} +\end{figure} + + +The standard commands are all written as Bourne shell scripts~\cite{kernighan:unix}. +They assemble Unix pipelines using {\tt markup} and the filters and +back ends described above. They are documented in man pages, and +there is no sense in repeating that material here. +I do show two sample pipelines in +Figures \ref{fig:pipe-notangle}~and~\ref{fig:pipe-noweave}. +The source code is available in the {\tt shell} directory for those +who want to explore further. + +\begin{figure}[p] +\begin{verbatim} +awk 'BEGIN { line = 0; capture = 0 + format = sprintf("'"$format"'",'"$width"') + } +function comment(s) { + '"$subst"' + return sprintf(format,s) +} + +function grab(s) { + if (capture==0) print + else holding[line] = holding[line] s +} + +/^@end doc/ { capture = 0; holding[++line] = "" ; next } +/^@begin doc/ { capture = 1; next } + +/^@text / { grab(substr($0,7)); next} +/^@quote$/ { grab("[[") ; next} +/^@endquote$/ { grab("]]") ; next} + +/^@nl$/ { if (capture !=0 ) { + holding[++line] = "" + } else if (defn_pending != 0) { + print "@nl" + for (i=0; i<=line && holding[i] ~ /^ *$/; i++) i=i + for (; i<=line; i++) + printf "@text %s\n@nl\n", comment(holding[i]) + line = 0; holding[0] = "" + defn_pending = 0 + } else print + next + } + +/^@defn / { holding[line] = holding[line] "<"substr($0,7)">=" + print ; defn_pending = 1 ; next } +{ print }' +\end{verbatim} +\caption{{\tt awk} command used to transform documentation to comments} + +\smallskip +\noindent +\verb+$subst+, \verb+$format+, and \verb+$width+ are shell variables used +to adapt the script for different languages. +executing \verb+$subst+ eliminates comment-end markers (if any) from +the documentation, and the initial \verb+sprintf+ that creates the +{\tt awk} +variable \verb+format+ gives the format used to print a line of +documentation as a comment. + +\ltxlabel{fig:nountangle} +\end{figure} + +\afterpage{\clearpage} % force figures out + + +\section{Examples} + +I don't give examples of the pipeline +representation; it's best just to +play with the existing filters. +In particular, +\begin{quote} +{\tt noweave -v} {\it options} {\it inputs} {\tt >/dev/null} +\end{quote} +prints (on standard error) the pipeline used by {\tt noweave} +to implement any set of {\it options}. +In this section, I give examples of a few nonstandard filters I've +thrown together for one purpose or another. + +{\hfuzz=6.8pt +This one-line {\tt sed} command makes {\tt noweb} treat two chunk names as +identical if they differ only in their representation of whitespace: +\begin{verbatim} + sed -e '/^@use /s/[ \t][ \t]*/ /g' -e '/^@defn /s/[ \t][ \t]*/ /g' +\end{verbatim} +\par} + +This little filter, a Bourne shell script +written in {\tt awk}~\cite{aho:awk}, +makes the definition of an empty chunk (\verb+<<>>=+) +stand for a continuation of the previous chunk definition. +\begin{verbatim} +awk 'BEGIN { lastdefn = "@defn " } +/^@defn $/ { print lastdefn; next } +/^@defn / { lastdefn = $0 } +{ print }' "$@" +\end{verbatim} + + + +To share programs with colleagues who don't enjoy literate +programming, I use a filter, shown in Figure~\ref{fig:nountangle}, that +places each line of documentation in a comment and moves it to +the succeeding code chunk. +With this filter, \verb+notangle+ +transforms a literate +program into a traditional commented program, without loss of +information and with only a modest penalty in readability. + + +As a demonstration, and to help convert nuweb programs to {\tt +noweb}, I wrote a +a 55-line Icon program that makes it possible to abbreviate chunk names +using a trailing ellipsis, as in {\tt WEB}; it appears in the {\tt +noweb} distribution as +\verb+icon/disambiguate.nw+. + +Kostas Oikonomou of AT\&T Bell Labs and Conrado Martinez-Parra of +the Univ.\ Politecnica de Catalunya in Barcelona have written filters +that add prettyprinting to {\tt noweb}. +Oikonomou's filters prettyprint Icon and Object-Oriented Turing; +Martinez-Parra's filter prettyprints a variant of Dijkstra's language +of guarded commands. +These filters are in the noweb distribution in the \verb+contrib+ directory. + +It's also possible to do useful or amusing things by writing new back +ends. +Figure~\ref{fig:nocount} shows an {\tt awk} script that gives a count of the +number of lines of code and of documentation in a group of {\tt noweb} +files. + +\begin{figure}[!b] +\begin{verbatim} +BEGIN { bogus = "this is total bogosity" + codecount[bogus] = -1; docscount[bogus] = -1 + } +/^@file / { thisfile = $2 ; files[thisfile] = 0 } +/^@begin code/ { code = 1 } +/^@begin docs/ { code = 0 } +/^@nl/ { + if (code == 0) + docscount[thisfile]++ + else + codecount[thisfile]++ +} +END { + printf " Code Docs Both File\n" + for (file in files) { + printf "%5d %5d %5d %s\n", + codecount[file], docscount[file], + codecount[file]+docscount[file], file + totalcode += codecount[file] + totaldocs += docscount[file] + } + printf "%5d %5d %5d %s\n", + totalcode, totaldocs, totalcode+totaldocs, "Total" +} +\end{verbatim} +\caption{Back end for counting lines of code and documentation} +\ltxlabel{fig:nocount} + +\smallskip + +\noindent +The \verb+BEGIN+ code forces \verb+codecount+ and +\verb+docscount+ to be associative arrays; without it the increment +operator would fail. +\end{figure} + + + +\bibliographystyle{chicago} +\bibliography{web,ramsey,cs} + +\end{document} diff --git a/web/noweb/src/xdoc/htmltoc.1 b/web/noweb/src/xdoc/htmltoc.1 new file mode 100644 index 0000000000..60bf98a1ce --- /dev/null +++ b/web/noweb/src/xdoc/htmltoc.1 @@ -0,0 +1,42 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +htmltoc \- add table of contents to HTML document +.SH SYNOPSIS +\fBhtmltoc\fP [\fB\-2345\fP] [file] +.SH DESCRIPTION +.I htmltoc +adds a table of contents to the HTML document named on the command +line or provided as standard input. +The table of contents is inserted between +.B <tableofcontents> +and +.B </tableofcontents>, +which must appear on lines by themselves. +Any text between those two markers is +.I discarded. +These markers are inserted automatically by the +.IR sl2h (1) +filter. +.PP +The table of contents includes only headers at levels 2, 3, 4, and 5. +This can be changed by using a command-line +option, e.g., \fB\-123456789\P to include all possible headers. +.SH BUGS +.I htmltoc +can't cope with a header that is split across multiple lines, +even though this is legal HTML. +.SH SEE ALSO +.IR nowebfilters (7), +.IR sl2h (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + +.PP +Stephen Parker provided a preliminary version. diff --git a/web/noweb/src/xdoc/htmltoc.nw b/web/noweb/src/xdoc/htmltoc.nw new file mode 100644 index 0000000000..7e4e4d56d5 --- /dev/null +++ b/web/noweb/src/xdoc/htmltoc.nw @@ -0,0 +1,34 @@ +<<*>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +htmltoc \- add table of contents to HTML document +.SH SYNOPSIS +\fBhtmltoc\fP [\fB\-2345\fP] [file] +.SH DESCRIPTION +.I htmltoc +adds a table of contents to the HTML document named on the command +line or provided as standard input. +The table of contents is inserted between +.B <tableofcontents> +and +.B </tableofcontents>, +which must appear on lines by themselves. +Any text between those two markers is +.I discarded. +These markers are inserted automatically by the +.IR sl2h (1) +filter. +.PP +The table of contents includes only headers at levels 2, 3, 4, and 5. +This can be changed by using a command-line +option, e.g., \fB\-123456789\P to include all possible headers. +.SH BUGS +.I htmltoc +can't cope with a header that is split across multiple lines, +even though this is legal HTML. +.SH SEE ALSO +.IR nowebfilters (7), +.IR sl2h (1) +<<AUTHOR section>> +.PP +Stephen Parker provided a preliminary version. diff --git a/web/noweb/src/xdoc/htmltoc.txt b/web/noweb/src/xdoc/htmltoc.txt new file mode 100644 index 0000000000..2339f73e63 --- /dev/null +++ b/web/noweb/src/xdoc/htmltoc.txt @@ -0,0 +1,43 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + htmltoc - add table of contents to HTML document + +SSYYNNOOPPSSIISS + hhttmmllttoocc [--22334455] [file] + +DDEESSCCRRIIPPTTIIOONN + _h_t_m_l_t_o_c adds a table of contents to the HTML document named on the com†+ mand line or provided as standard input. The table of contents is + inserted between <<ttaabblleeooffccoonntteennttss>> and <<//ttaabblleeooffccoonntteennttss>>,, which must + appear on lines by themselves. Any text between those two markers is + _d_i_s_c_a_r_d_e_d_. These markers are inserted automatically by the _s_l_2_h(1) + filter. + + The table of contents includes only headers at levels 2, 3, 4, and 5. + This can be changed by using a command-line option, e.g., --112233445566778899PP + ttoo iinncclluuddee aallll ppoossssiibbllee hheeaaddeerrss.. + +BBUUGGSS + _h_t_m_l_t_o_c can't cope with a header that is split across multiple lines, + even though this is legal HTML. + +SSEEEE AALLSSOO + _n_o_w_e_b_f_i_l_t_e_r_s(7), _s_l_2_h(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + Stephen Parker provided a preliminary version. + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/ieee.tex b/web/noweb/src/xdoc/ieee.tex new file mode 100644 index 0000000000..800a9d6121 --- /dev/null +++ b/web/noweb/src/xdoc/ieee.tex @@ -0,0 +1,901 @@ +% The formatter used is LaTeX + +% symbols that could be defined +% +% \dsp double space, figures on figure pages +% \tr Princeton TR +% \wbn use consecutive `{\tt WEB} numbering' + + + + +\ifx\tr\undefined +\documentstyle[11pt,noweb,multicol]{article} +\else +\documentstyle[11pt,noweb,multicol,tr]{article} +\fi + +\title{Literate-Programming Tools\\Can Be Simple and Extensible} +\author{Norman Ramsey\thanks{Author's current address is Bellcore, 445 +South Street, Morristown, NJ 07960; +send email to {\tt norman@bellcore.com}.}\\Department of Computer Science, +Princeton University} + +\ifx\wbn\undefined\else \noweboptions{webnumbering}\fi + +\ifx\dsp\undefined\else +\makeatletter +\let\@oldfigure=\figure +\def\figure{\@oldfigure[p]} +\makeatother +\fi + + + +\ifx\tr\undefined +\date{November 1993} +\else +\reportno{CS-TR-351-91} +\date{October 1991\\Revised August 1992, November 1993} +\fi + +\setcounter{secnumdepth}{0} +\clubpenalty=10000 +\widowpenalty=10000 +\makeatletter +\def\refno#1{\nocite{#1}\@ifundefined + {b@#1}{{\bf ?}\@warning + {Reference number `#1' on page \thepage \space undefined}}% +{\hbox{\csname b@#1\endcsname}}} +\makeatother + +\def\remark#1{\marginpar{\raggedright\hbadness=10000\footnotesize\it #1}} +% \def\remark#1{\relax} +\ifx\dsp\undefined\else\renewcommand{\baselinestretch}{1.6}\fi + +\begin{document} + +\maketitle + +\begin{abstract} +When it was introduced, literate programming meant {\tt WEB}. +Desire to use {\tt WEB} with languages other than Pascal led to the +implementation of many versions. +{\tt WEB} is complex, and the difficulty of using {\tt WEB} creates +an artificial barrier to +experimentation with literate programming. +{\tt noweb} provides much of the functionality of +{\tt WEB}, with a fraction of the complexity. +{\tt noweb} is independent of the target programming language, and its +formatter-dependent part is 60~lines. +{\tt noweb} is extensible, because it uses two +representations of programs: one easily edited by authors and one +easily manipulated by tools. + +This paper explains how to use the {\tt noweb} tools and gives +examples of their use. +It sketches the implementation of the tools and describes how new +tools are added to the set. +Because +{\tt WEB} and {\tt noweb} overlap, but each does some things that the +other cannot, +this paper enumerates the differences. +\end{abstract} + +\ifx\tr\undefined +\begin{center}\small +{\bf Key words:} +literate~programming, readability, programming~environments +\end{center} +\fi + +\section{Introduction} +When literate programming was +introduced, it was synonymous with {\tt WEB}, a tool +for writing literate Pascal programs~\cite[Chapter~4]{knuth:literate:book}. +The idea attracted attention; several examples of literate +programs were published, and a special forum was created to discuss literate +programming~% +\cite{denning:announcing}. % ,gries:pearls,knuth:literate:book,thimbleby:review +{\tt WEB} was adapted to programming languages other than +Pascal~\cite{levy:cweb,ramsey:building,thimbleby:cweb}. % guntermann:cweb,sewell:mangle, +% +With experience, many {\tt WEB} users became dissatisfied~\cite{ramsey:literate}. +Some found {\tt WEB} not worth the trouble, as did +one author of the program appearing in Appendix~C +of Reference~\refno{sewell:weaving}. +Others built their own systems for literate + programming. % ~\cite{hamilton:expanding,cvw:loom}. +The literate-programming forum was dropped, on the grounds that +literate programming had become the province of those who could build +their own tools~\cite{cvw:assessment}. + +{\tt WEB} programmers + interleave source code and descriptive text in a single document. +When using {\tt WEB}, a programmer divides the source code into +{\em modules}. +Each module has a documentation part and a code part, and +modules may be written in any order. +The programmer is encouraged to choose an order that helps explain the program. +The code parts are like macro definitions; they have names, and they contain +both code and references to other modules. +A {\tt WEB} file represents a single program; +{\tt TANGLE} extracts that program from the {\tt WEB} source. +One special module has a code part with no name, and {\tt TANGLE} +expands the code part of that module to extract the program. +{\tt WEAVE} converts {\tt WEB} source to +{\TeX} input, from which {\TeX} can produce high-quality typeset +documentation of the program. + + +{\tt WEB} is a complex tool. +In addition to enabling programmers to present pieces of a program in +any order, it expands three kinds of macros, prettyprints code, +evaluates some constant expressions, +provides an integer representation for string literals, and implements +a simple form of version control. +The manual for the original version documents 27 ``control sequences''~\cite{knuth:web}. +The versions for languages other than Pascal offer slightly different +functions and different sets of control sequences. +Significant effort is required to make {\tt WEB} usable with a new +programming language, even when using a tool designed for that +purpose~\cite{ramsey:building}. + +{\tt WEB}'s shortcomings make it difficult to explore the {\em idea} +of literate programming; too much effort is required to master the +{\em tool}. +I designed a new tool that is +simple, extensible, and independent of the target programming language. +{\tt noweb} is designed +around one idea: writing named chunks of code in any order, with +interleaved documentation. +Like {\tt WEB}, and like all literate-programming tools, it can +be used to write a program in pieces and to present those pieces in +an order that helps explain the program. +{\tt noweb}'s value lies in its simplicity, which shows that the idea +of literate programming does not require the complexity of {\tt WEB}. + +Users who could not find a satisfactory literate-programming tool +have had no option other than to write their own tools. +{\tt noweb} reduces the need for new tools because it is designed to +be extended. +It uses Unix pipes to enable users to change existing +behavior or to add new features {without} recompiling {\tt noweb}. +Even language-dependent features, like +prettyprinting and automatic generation of an index, have been added +to {\tt noweb} without recompiling. + + + +\section{{\tt noweb}} % , a minimal literate-programming system} +A {\tt noweb} file contains program source code interleaved with documentation. +When {\tt notangle} is given a {\tt noweb} file, it writes the program + on standard output. +When {\tt noweave} is given a {\tt noweb} file, it reads the {\tt +noweb} source and produces, on standard output, {\TeX} source for +typeset documentation. +Figure~\ref{transforms} shows how to use {\tt notangle} and {\tt +noweave} to produce code and documentation for a C~program contained +in the {\tt noweb} file {\tt foo.nw}. + +\begin{figure} +\footnotesize +\setlength{\unitlength}{2pt} +\begin{picture}(170,80)(0,-40) +\tt +\put(20,12.5){\makebox(0,0)[l]{\ \tt notangle foo.nw > foo.c}} +\put(20,-12.5){\makebox(0,0)[l]{\ \tt noweave foo.nw > foo.tex}} + +\put(0,-5){\framebox(30,10){foo.nw}} +\put(15,5){\vector(1,2){7.5}} +\put(10,20){\framebox(30,10){foo.c}} +\put(15,-5){\vector(1,-2){7.5}} +\put(10,-30){\framebox(30,10){foo.tex}} + +\put(40,-25){\vector(1,0){35}} +\put(57.5,-23.5){\makebox(0,0)[b]{latex foo}} +\put(75,-30){\framebox(30,10){foo.dvi}} +\put(105,-25){\vector(1,0){35}} +\put(122.5,-23.5){\makebox(0,0)[b]{\strut \tt dvi foo}} + % \shortstack{\strut dvi\\\strut \rm driver}}} +\put(140,-25){\makebox(0,0)[l]{\rm\strut \ Typeset documentation for +{\tt foo}}} + +\put(40,25){\vector(1,0){35}} +\put(57.5,26.5){\makebox(0,0)[b]{cc -c foo.c}} +\put(75,20){\framebox(30,10){foo.o}} +\put(105,25){\vector(1,0){35}} +\put(122.5,26.5){\makebox(0,0)[b]{ld foo.o {\ldots}}} +\put(140,25){\makebox(0,0)[l]{\rm\strut \ Executable \tt a.out}} + + + +\end{picture} + +\caption{Using {\tt noweb} to build code and documentation} +\label{transforms} +\end{figure} + +A {\tt noweb} file is a sequence of {\em chunks}, which may appear in any order. +A chunk may contain code or documentation. +Documentation chunks begin with a line that starts with an at sign ({\tt @}) +followed by a space or newline. +They have no names. +Code chunks begin with +\begin{quote} +\tt <<{\rm\it chunk name}>>= +\end{quote} +on a line by itself. +The double left angle bracket ({\tt <<}) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not mark the beginning of a +chunk, it is assumed to be the first line of a documentation chunk. + +Documentation chunks contain text that is ignored by {\tt notangle} +and copied verbatim to standard output by +{\tt noweave} (except for quoted code). +{\tt noweave} can work with {\LaTeX}, or it can use +% insert a reference to +a plain-{\TeX} macro package, supplied with {\tt noweb}, that defines +commands like \verb+\chapter+ and \verb+\section+. + +Code chunks contain program source code and references to other code +chunks. +Several code chunks may have the same name; {\tt notangle} +concatenates their definitions to produce a single chunk, just as + {\tt TANGLE} does. +Code chunk definitions are like macro definitions; +{\tt notangle} extracts a program by expanding one chunk (by default +the chunk named \verb+<<*>>+). +The definition of that chunk contains references to other chunks, +which are themselves expanded, and so on. +{\tt notangle}'s output is readable; it preserves the indentation of expanded +chunks with respect to the chunks in which they appear. + +Code may be quoted within documentation chunks by placing double +square brackets around it ({\tt [[}$\ldots${\tt]]}). +These double square brackets are ignored by {\tt notangle}, but they +are used by {\tt noweave} to give code special typographic +treatment. + +If double left and right angle brackets are not paired, they are +treated as literal ``{\tt<<}'' and ``{\tt>>}''. Users can force any +such brackets, even paired brackets, to be treated as literal by +using a preceding at sign (e.g.~``{\tt @<<}''). + +Any line beginning with ``{\tt@ }'' terminates a code chunk. +If such a line has the form +``{\tt@~\char`\%def {\rm\it identifiers}}'', +it also means that the preceding chunk defines the identifiers listed +in {\it identifiers}. +This notation provides a way of marking definitions manually when +no automatic marking is available. + +Figure~\ref{sample-input} shows a fragment of a {\tt noweb} program +that computes prime numbers. +The program is derived from the example used in +Reference~\refno{knuth:literate:book}, Chapter~4, and Figure~\ref{sample-input} should +be compared with Figure~2b of that paper. +Figure~\ref{noweave-output} shows the program after processing by {\tt +noweave} and {\LaTeX}. +Figure~\ref{notangle-output} shows the beginning of the program as +extracted by {\tt notangle}. +A complete example program accompanies this paper. + +\begin{figure} +\begin{verbatim} +@ This program has no input, because we want to keep it +simple. The result of the program will be to produce a +list of the first thousand prime numbers, and this list +will appear on the [[output]] file. + +Since there is no input, we declare the value [[m = 1000]] +as a compile-time constant. The program itself is capable +of generating the first [[m]] prime numbers for any +positive [[m]], as long as the computer's finite +limitations are not exceeded. +<<program to print the first thousand prime numbers>>= +program print_primes(output); + const m = 1000; + <<other constants of the program>> + var <<variables of the program>> + begin <<print the first [[m]] prime numbers>> + end. +@ %def print_primes m +\end{verbatim} +\caption{Sample {\tt noweb} input, from prime number program} +\label{sample-input} +\end{figure} + + + +\begin{figure} +\begin{quote} +\nwbegindocs{4} + +This program has no input, because we want to keep it simple. +The result of the program will be to produce a list of the first +thousand prime numbers, and this list will appear on the \code{}output\edoc{} +file. + +Since there is no input, we declare the value \code{}m = 1000\edoc{} as a +compile-time constant. +The program itself is capable of generating the first \code{}m\edoc{} prime +numbers for any positive \code{}m\edoc{}, as long as the computer's finite +limitations are not exceeded. +\nwenddocs +\nwbegincode{5} +\moddef{program to print the first thousand prime numbers}\endmoddef +program print_primes(output); + const m = 1000; + \LA{}other constants of the program\RA{} + var \LA{}variables of the program\RA{} + begin \LA{}print the first \code{}m\edoc{} prime numbers\RA{} + end. +\nwendcode +\end{quote} +\caption{Output produced by {\tt noweave} and {\LaTeX} from Figure~\protect\ref{sample-input}} +\label{noweave-output} +\end{figure} + +\section{Using {\tt noweb}}% experience with noweb? + +Experimenting with \verb+noweb+ is easy. +{\tt noweb} has little syntax: definition and use of code chunks, marking of +documentation chunks, listing of identifiers defined, +quoting of code, and quoting of brackets. +\verb+noweb+ can be used with any programming language, and its +manual fits on three pages. + +\begin{figure} +\begin{verbatim} +program print_primes(output); + const m = 1000; + rr = 50; + cc = 4; + ww = 10; + ord_max = 30; { p_ord_max squared must exceed p_m } + var p: array [1..m] of integer; + { the first m prime numbers, in increasing order } + page_number: integer; +\end{verbatim} +\unskip +\hbox{\hskip2em\tt\vdots} +\caption{Part of primes program as written by {\tt notangle}} +\label{notangle-output} +\end{figure} + + +On a large project, it is essential that compilers and other tools be +able to refer to locations in the \verb+noweb+ source, even though +they work with \verb+notangle+'s output~\cite{ramsey:literate}. +Giving \verb+notangle+ the \verb+-L+ option makes it emit pragmas +that inform compilers of the placement of lines +in the \verb+noweb+ source. +It also preserves the columns in which +tokens appear. +If \verb+notangle+ is not given the \verb+-L+ option, it respects the +indentation of its input, making its output easy to read. + +Cross-referencing of chunks and identifiers makes large programs +easier to understand. +\verb+noweave -x+ uses {\LaTeX} +to show on what pages each chunk is defined and used. +\verb+noweave -index+ adds a local identifier cross-reference after +each code chunk. +The example program accompanying this article shows full cross-reference +information. + + +{\tt WEB} files map one to one with both programs and documents. +The mapping of \verb+noweb+ files to programs is many to many; the +mapping of files to documents is many to one. +Source files are combined by listing their names on \verb+notangle+'s +or \verb+noweave+'s command line. +Many programs may be extracted from one source by specifying the names +of different root chunks, using \verb+notangle+'s \verb+-R+ command-line +option. + +The simplest example of a one-to-many mapping of programs is that of putting +C~header and program in a single {\tt noweb} file. +The header comes from the root chunk \LA{}header\RA{}, and the program +from the default root chunk, \LA{}*\RA{}. +The following Unix commands extract header \verb+foo.h+ and program +\verb+foo.c+ from {\tt noweb} file \verb+foo.nw+: +% This trick is explained on pages 265--266 of Reference~\refno{kernighan:unix}.} +\begin{quote} +\begin{verbatim} +notangle -L foo.nw > foo.c +notangle -Rheader foo.nw | cpif -ne foo.h +\end{verbatim} +\end{quote} +The \verb+>+ in the first command directs \verb+notangle+'s output to +the file \verb+foo.c+. +The \verb+|+ in the second command directs \verb+notangle+'s output to +the \verb+cpif+ program, which is distributed with {\tt noweb}. +\verb+cpif -ne foo.h+ compares its input to the contents of file +\verb+foo.h+; if they differ, the input replaces \verb+foo.h+. +This trick avoids touching the file \verb+foo.h+ when its contents +have not changed, which avoids triggering unnecessary recompilations. + +A more interesting example is using {\tt noweb} to +interleave different languages in one source file. +I wrote an \verb+awk+ +script that read a machine description and emitted a disassembler for +that machine, and I used {\tt noweb} to combine the script and description +in a single file, so I could place each part of the input next to the +code that processed that input. +The machine description was in the root chunk \LA{}opcodes +table\RA{}, and the \verb+awk+ script in the default root chunk. +The processing steps were: +\begin{verbatim} +notangle opcodes.nw > opcodes.awk +notangle -R'opcodes table' opcodes.nw | +awk -f opcodes.awk > disassem.sml +\end{verbatim} +The first line extracts the {\tt awk} program into file {\tt opcodes.awk}. +The second line extracts the machine description and pipes it to the +third line, which uses the {\tt awk} program to turn it into a +dissassembler. +The disassembler is written to file {\tt disassem.sml}. + +Many-to-one mapping of source to program can be used to obtain +effects similar to those of Ada or Modula-3 generics. +Figure~\ref{generic-example} shows generic C code that supports lists. +The code can be +``instantiated'' by combining it with another \verb+noweb+ file. +\verb+pair_list.nw+, shown in Figure~\ref{pair-list-example}, specifies + lists of integer pairs. +The two are combined by applying \verb+notangle+ to them both: +\begin{verbatim} +notangle pair_list.nw generic_list.nw > pair_list.c +\end{verbatim} +{\tt noweb} has no parameter mechanism, so the ``generic'' code must +refer to a fixed set of symbols, and it cannot be checked for errors +except by compiling \verb+pair_list.c+. +These restrictions make {\tt noweb} a poor approximation to real +generics, but useful nevertheless. + +\begin{figure} +\ifx\dsp\undefined\else\renewcommand{\baselinestretch}{1.3}\fi +\nwbegindocs{0} +This list code supports circularly-linked lists represented by a +pointer to the last element. It is intended to be combined with other +{\tt noweb} code that defines \LA{}fields of a list element\RA{} (the fields +found in an element of a list) and that uses \LA{}list +declarations\RA{} and \LA{}list definitions\RA. +\nwenddocs +\nwbegincode{1} +\moddef{list declarations}\endmoddef +typedef struct list \{ + \LA{}fields of a list element\RA{} + struct list *_link; +\} *List; + +extern List singleton(void); /* singleton list, uninitialized fields */ +extern List append(List, List); /* destructively append two lists */ +#define last(l) (l) +#define head(l) ((l) ? (l)->next : 0) +#define forlist(p,l) for (p=head(l); p; p=(p==last(l) ? 0 : p->next)) +\nwendcode +\nwbegincode{2} +\moddef{list definitions}\endmoddef +List append (List left, List right) \{ + List temp; + if (left == 0) return right; + if (right == 0) return left; + temp = left->_link; left->_link = right->_link; right->_link = temp; + return right; +\} +\nwendcode +\unskip\hbox{\hskip2em\vdots} +\caption{Generic code for implementing lists in C} +\label{generic-example} +\end{figure} + +\begin{figure} +\nwbegincode{1} +\moddef{*}\endmoddef +\LA{}list declarations\RA{} +\LA{}list definitions\RA{} +\nwendcode +\nwbegincode{2} +\moddef{fields of a list element}\endmoddef +int x; +int y; +\nwendcode +\caption{Program to instantiate lists of integer pairs} +\label{pair-list-example} +\end{figure} + +\iffalse +I have used \verb+noweb+ for small programs written in various +languages, including C, Icon, \verb+awk+, and Modula-3. +Larger projects have included a code generator for +Standard ML of New Jersey (written in Standard ML) and +a multi-architecture debugger, written in Modula-3, C, and +assembly language. +A colleague used \verb+noweb+ to write an experimental file +system in C++. + +\unskip +\vskip0pt plus12pt\penalty-200\vskip0pt plus-12pt +\noindent The sizes of these programs are % try for better page break +\nobreak +\begin{quote}\nobreak +\catcode`\?=\active\def?{\phantom{0}} +\catcode`\*=\active\def*{\phantom{,}} +\leavevmode\rlap{\begin{tabular}{lcc} +Program&Documentation lines&Total lines\\ +\noalign{\smallskip} +{\tt markup} and {\tt nt}&?*400&?1,200\\ +ML code generator&?*900&?2,600\\ +Debugger&1,400&11,000\\ +File system&4,400&27,000 +\end{tabular}} +\end{quote} + +\else + +I myself have used \verb+noweb+ for code written in various +languages, including assembly language, \verb+awk+, C, Icon, Modula-3, +Promela, +Standard~ML, and \TeX.% +\footnote{Readers interested in another example of {\tt noweb} can see +a published version of one of these programs \cite{ramsey:correctness}.} +These projects have ranged in size from a few hundred to twenty +thousand lines of code. +Other users around the world have undertaken +{\tt noweb} projects of comparable size in C$++$, Modula-2, occam, Parallel C, +perl, Prolog, and Scheme. +Two colleagues are using {\tt noweb} to write a book describing the +design and implementation of a retargetable C compiler. + +\fi + +\section{Representation of {\tt noweb} files} + +The \verb+noweb+ syntax is easy to read, write, and edit, but it is +not easily manipulated by programs. +To make it easy to extend \verb+noweb+, I have written +\verb+markup+, which converts \verb+noweb+ source to + a representation that is easily manipulated +by commonly used Unix tools like {\tt sed} and {\tt awk}. +In this representation, +every line begins with {\tt @} and a key word. +The possibilities are: +\begin{quote} +\leavevmode\rlap{\begin{tabular}{ll} +\tt @begin {\rm\it kind} $n$&Start a chunk\\ +\tt @end {\rm\it kind} $n$&End a chunk\\ +\tt @text {\rm\it string}&{\rm\it string} appeared in a chunk\\ +\tt @nl&A newline appeared in a chunk\\ +\tt @defn {\rm\it name}&The code chunk named {\rm\it name} is being defined\\ +\tt @use {\rm\it name}&A reference to code chunk named {\rm\it name}\\ +\tt @quote&Start of quoted code in a documentation chunk\\ +\tt @endquote&End of quoted code in a documentation chunk\\ +\tt @file {\rm\it filename}&Name of the file from which the chunks came\\ +\tt @line $n$&Next text line came from source line $n$ in current file\\ +\tt @index defn {\rm\it ident}&The current chunk contains a definition of {\rm\it ident}\\ +\tt @index use {\rm\it ident}&The current chunk contains a use of {\rm\it ident}\\ +\tt @index nl {\rm\it ident}&A newline that is part of markup, not part of the chunk\\ +\tt @literal {\rm\it text}&{\tt noweave} copies {\rm\it text} to output\\ +\end{tabular}} +\end{quote} +{\tt markup} numbers each chunk, starting at~0. +It also recognizes and undoes the escape sequence for double brackets, +e.g.~converting ``{\tt @<<}'' to ``{\tt <<}''. +{\tt markup}'s output represents a sequence of files. +Each file is represented by a ``{\tt @file~{\rm\it filename}}'' line, +followed by a sequence of chunks. + +The representation of a documentation chunk is +\begin{quote} +\begin{tabular}{ll} +\tt @begin docs $n$&where $n$ is the chunk number.\\ +{\it docline}&repeated an arbitrary number of times.\\ +\tt @end docs $n$ +\end{tabular} +\end{quote} +where {\it docline} may be {\tt @text}, {\tt @nl}, {\tt @quote}, +{\tt @endquote}, {\tt @file}, {\tt @line}, or {\tt @index}. +Every {\tt @nl} or {\tt @index~nl} corresponds to a newline in the original file. +{\tt markup} guarantees that quotes are balanced and not nested. + +\vskip0pt plus12pt\penalty-200\vskip0pt plus-12pt +The representation of a code chunk is +\begin{quote} +\leavevmode\rlap{\begin{tabular}{ll} +\tt @begin code $n$&where $n$ is the chunk number.\\ +\tt @defn {\rm\it name}&name of this chunk.\\ +\tt @nl&The newline following {\tt <<{\rm\it name}>>=} in the original file\\ +{\it codeline}&repeated an arbitrary number of times.\\ +\tt @end code $n$ +\end{tabular}} +\end{quote} +where {\it codeline} may be {\tt @text}, {\tt @nl}, {\tt @use}, +{\tt @file}, {\tt @line}, or {\tt @index}. + +The \verb+noweb+ tools are implemented by piping the output of +\verb+markup+ to other programs. +\verb+notangle+ is a Unix shell script that builds a +pipeline between \verb+markup+ and \verb+nt+, which reads +and expands definitions of code chunks. +\verb+noweave+ pipes the output of \verb+markup+ to a 60-line +\verb+awk+~script that inserts appropriate {\TeX} or {\LaTeX} +formatting commands. + +\section{Extending {\tt noweb}} + +Having a format easily read by programs makes {\tt noweb} extensible; +one can manipulate literate programs using Unix shell scripts and +filters. +{\tt notangle} and {\tt noweave} have {\tt -filter} options that can +be used to insert filters into their pipelines without changing any +code. +These filters manipulate the representation described in the previous +section. +The results are converted to {\TeX} by {\tt noweave} and to code by +{\tt notangle}. + + +Filters can be used to change the way {\tt noweb} works; for example, +a one-line {\tt sed} script makes {\tt noweb} treat two chunk names as +identical if they differ only in their representation of whitespace. +A 55-line Icon program makes it possible to abbreviate chunk names +using a trailing ellipsis, as in {\tt WEB}. +To share programs with colleagues who don't enjoy literate +programming, I use a filter that +places each line of documentation in a comment and moves it to +the succeeding code chunk. +With this filter, \verb+notangle+ +transforms a literate +program into a traditional commented program, without loss of +information and with only a modest penalty in readability. +Figure~\ref{nountangle-output} shows the results of applying +the filter to the prime-number program shown in +Figure~\ref{sample-input}. + + + + +\begin{figure} +\begin{webcode}\parindent=0pt +\{ This program has no input, because we want to keep it \} +\{ simple. The result of the program will be to produce a \} +\{ list of the first thousand prime numbers, and this list \} +\{ will appear on the [[output]] file. \} +\hbox{\hskip2em\tt\vdots} +\{ <program to print the first thousand prime numbers>= \} +program print_primes(output); + const m = 1000; + \{ \\section-The output phase- \} + \{ \} + \{ <other constants of the program>= \} + rr = 50; + cc = 4; + ww = 10; + \{ <other constants of the program>= \} + ord_max = 30; \{ p_ord_max squared must exceed p_m \} + var \{ How should table [[p]] be represented? Two possibilities \} + \{ suggest themselves: We could construct a sufficiently \} +\hbox{\hskip2em\tt\vdots} +\end{webcode} +\caption{Output produced from +Figure~\protect\ref{sample-input} by filter converting documentation +chunks into comments.} +\label{nountangle-output} +\end{figure} + + + +Filters can be used to add significant features. +\verb+noweave+'s cross-reference and indexing features use two +filters, one that finds uses of defined identifiers and one that +uses \verb+@literal+ to insert {\LaTeX} cross-reference +commands. +In most cases, programmers must mark identifier definitions +by hand, using ``{\tt @~\%def}'', +but +in some cases, a third, language-dependent filter can be used to +mark identifier definitions, +making index generation completely automatic. +Kostas Oikonomou of AT\&T Bell Labs and Conrado Martinez-Parra of +the Univ.\ Politecnica de Catalunya in Barcelona have written filters +that add prettyprinting to {\tt noweb}. +Oikonomou's filters prettyprint Icon and Object-Oriented Turing; +Martinez-Parra's filter prettyprints a variant of Dijkstra's language +of guarded commands. + + +\section{Comparing {\tt WEB} and {\tt noweb}} + +Unlike {\tt WEB}, +\verb+noweb+ is independent of the target programming language. +{\tt WEB} tools can be generated for many programming languages, +but those languages must be lexically similar to C. +For example, {\tt WEB} can't handle the \verb+awk+ regular-expression +notation ``\verb+/+\ldots\verb+/+''; every such expression must quoted +using {\tt WEB}'s ``verbatim'' control sequence. +The effort required to +generate {\tt WEB} tools is significant; the prospective user must +write a specification of several hundred +lines. + +Being independent of the target programming language makes {\tt noweb} +simpler, but it also means that {\tt noweb} can do less. +Most of the differences between {\tt WEB} and {\tt noweb} arise +because {\tt WEB} has language-dependent features that are not present +in \verb+noweb+. +These features include +prettyprinting, +typesetting comments using {\TeX}, +expanding macros, +evaluating constant expressions, +and +converting string literals to indices into a ``string pool.'' +Among these features, +\verb+noweb+ users are most likely to miss prettyprinting. + + +Some differences arise because {\tt WEB} and \verb+noweb+ implement +similar features differently. +{\tt WEB}'s original {\tt TANGLE} removed white space and folded +lines to fill each line with tokens, +making its output unreadable~\cite[Chapter~4, Figure~3]{knuth:literate:book}. +Later adaptations preserved line breaks but removed other white space. +By default, \verb+notangle+ preserves whitespace and maintains +indentation when expanding chunks. +It can therefore be used with +languages like Miranda and +Haskell, in which indentation is significant. +{\tt TANGLE} cannot. + +{\tt WEB}'s {\tt WEAVE} assigns a number to each chunk, and its index +and cross-reference +information refer to chunk numbers, not page numbers. +\verb+noweb+ uses {\LaTeX} to emit index and cross-reference information that +refer to page numbers. +Anyone who has read a large literate program will +appreciate the difference. + +{\tt WEB} works poorly with \LaTeX; \LaTeX\ constructs cannot +be used in {\tt WEB} source, and getting {\tt WEAVE} output +to work in \LaTeX\ documents requires tedious adjustments by hand. +\verb+noweb+ works with both plain {\TeX} and {\LaTeX}. +Both {\tt WEAVE} and {\tt noweave} depend on the text formatter in two +ways: the source of the program itself, and the supporting macros. +{\tt WEAVE}'s source (written using {\tt WEB} for C) is +several thousand lines long, and the formatting code is not isolated. +{\tt noweave}'s source is a 200-line shell script, and it uses a +60-line {\tt awk} program to generate {\TeX}. +Both +{\tt WEAVE} and {\tt noweave} use about 200 lines of supporting macros +for plain {\TeX}. +\verb+noweb+ uses another 300 lines to support {\LaTeX}, primarily +because the page-based cross-reference mechanism is complex. + +{\tt noweb} has +two features that weren't in the original {\tt WEB}, but that appeared in +some of {\tt WEB}'s later adaptations; +{\tt notangle} can extract more than one program from +a single source file, and it can give a compiler the original locations +of source lines. +{\tt noweb} has a related feature not found in the {\tt WEB} variants; +{\tt noweave} adds no newlines to its output, making it easy to find +the sources of {\TeX} or {\LaTeX} errors. +For example, an error on line~634 of a generated +{\TeX} file is caused by a problem on line~634 of the +corresponding {\tt noweb} file. + +Reviewers have had many expectations of literate-programming +tools~\cite{thimbleby:review,cvw:assessment}. +The most important is {\em verisimilitude}: a single +input should produce both compilable program and publishable document, +warranting the correctness of the document. +Others include flexible order of elaboration, +ability to develop program and documentation concurrently in one +place, cross-references, and indexing. +Both {\tt WEB} and {\tt noweb} +satisfy all these expectations, although in both cases automatic +indexing is available for a limited set of programming languages. + + +\section{Discussion} + +{\tt WEB} takes the monolithic approach to literate programming---it +does everything. +{\tt noweb}'s approach is to compose +simple tools that manipulate files in the {\tt noweb} format. +Existing Unix tools provide some of the {\tt WEB} features that +aren't found in \verb+noweb+. +Unix supplies two macro processors: the C +preprocessor and the \verb+m4+ macro processor. +\verb+xstr+ extracts string literals. +\verb+patch+ provides a form of version control similar to {\tt WEB}'s +change files. +Few of {\tt WEB}'s remaining features will be missed; for example, many compilers +evaluate constant expressions at compile time. +Experience with {\tt WEB} has suggested that prettyprinting may be +more trouble than it is worth~\cite{ramsey:literate}. + +{\tt noweb} was developed on Unix and uses Unix tools like {\tt +awk} and the Bourne shell, but the only Unix feature it really needs +is the pipeline. +All components written in {\tt awk} have been duplicated in Icon; +an Icon implementation is freely available from the University of Arizona, and +it runs on a wide variety of hardware and operating-system platforms. +{\tt noweb} can be ported to non-Unix platforms provided they have +pipelines or an emulation thereof, ANSI~C, and either {\tt awk} or Icon. +For example, +Lee Wittenberg of Kean College ported {\tt noweb} to MS-DOS by +rewriting the shell scripts as DOS batch files. + +Indexing and cross-referencing make +{\tt noweb} less simple than it could be. +Complex {\LaTeX} code is needed to compute page numbers for use in +cross-reference lists and in the index. +The ability to use page numbers justifies this complexity, especially +since the complexity can be hidden from most users. +Users do need to understand the {\LaTeX} code if they want to customize the +appearance of their {\tt noweb} documents while retaining {\tt +noweb}'s use of page numbers for cross-reference. +Most literate-programming tools +forbid customization, but not all users will accept such a restriction. +I have compromised between simplicity and customizability by adding +{\LaTeX} options for a dozen of the most commonly requested +customizations; users can choose from among these options without +understanding {\tt noweb}'s {\LaTeX} code. + + +Four things distinguish {\tt noweb} from previous work. +{\tt noweb} takes as simple as possible a view of literate programming +and the tools needed to implement it. +Instead of relying on a generator or re-implementation to support +different programming languages, {\tt noweb} is independent of the +target programming language. +{\tt noweave}'s dependence on its typesetter is small and isolated, +instead of being distributed throughout a large implementation. +Users have extended {\tt noweb} without recompiling its source code. + +Experimenting with {\tt noweb} is easy because the tools are simple +and they work with any language. +If the experiment is unsatisfying, it is easy to abandon, because +\verb+notangle+'s output, unlike {\tt TANGLE}'s, is readable. +\verb+noweb+ is simpler than {\tt WEB}, and it is easier to use and +understand, but it does less. +I argue, however, that the benefit of {\tt WEB}'s extra features is +outweighed by cost of the extra complexity, +making \verb+noweb+ better for writing literate programs. + +\verb+noweb+ can be obtained by +anonymous {\tt ftp} from CTAN, +the Comprehensive {\TeX} +Archive Network, in directory {\tt web/noweb}. CTAN replicas appear +on +hosts {\tt ftp.shsu.edu}, {\tt ftp.tex.ac.uk}, and {\tt ftp.uni-stuttgart.de}. + +\section{Acknowledgements} +Mark Weiser's invaluable encouragement provided the impetus for me to +write this paper, which I did while visiting the Computer +Science Laboratory of the Xerox Palo Alto Research Center. +David Hanson suggested and provided the \verb+cpif+ program. +Preston Briggs developed many of the ideas used in \verb+noweb+'s +indexing, and he contributed code used in one of the pipeline stages. +Dave Love provided much-needed {\LaTeX} expertise. +Comments from David Hanson and from the anonymous referees stimulated +me to improve the paper. +The development of {\tt noweb} was supported by a Fannie and John Hertz +Foundation Fellowship. + +This paper was submitted in August, 1991. + +\bibliographystyle{abbrv} +\bibliography{web,cs,ramsey,ml} + +\clearpage +\noweboptions{shift} % needed to make room for wordcount example + +\input wc + +\end{document} diff --git a/web/noweb/src/xdoc/manpage.nw b/web/noweb/src/xdoc/manpage.nw new file mode 100644 index 0000000000..76cc6afcf1 --- /dev/null +++ b/web/noweb/src/xdoc/manpage.nw @@ -0,0 +1,681 @@ +<<notangle.1>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +notangle, noweave, nountangle \- noweb, a literate-programming tool +.SH SYNOPSIS +.B notangle +[\fB\-R\fProotname ...] [\fB\-filter\fP command] +[\fB\-L\fP[format]] [file] ... +.br +\fBnountangle\fP +[\fB\-ml\fP|\fB\-m3\fP|\fB\-c\fP|\fB\-c++\fP|\fB\-awk\fP|\fB\-tex\fP|\fB\-f77\fP|\fB\-f90\fP|\fB\-lisp\fP|\fB\-matlab\fP] +[\fB\-R\fProotname ...] [\fB\-filter\fP command] [\fB\-w\fPwidth] [file] ... +.br +\fBnoweave\fP [options] [file] ... +.SH DESCRIPTION +.I Noweb +is a literate-programming tool like Knuth's +.I WEB, +only simpler. +A +.I noweb +file contains program source code interleaved with documentation. +When +.I notangle +is given a +.I noweb +file, it writes the program on standard output. +When +.I noweave +is given a +.I noweb +file, it reads the +.I noweb +source and produces, on standard output, \fILaTeX\fP, \fITeX\fP, \fItroff\fP, +or \fIHTML\fP +source for typeset documentation. +.I nountangle +converts a literate program into an ordinary program by +turning interleaved documentation into comments. +The file name `\-' refers to standard input. +.SH FORMAT OF NOWEB FILES +<<paragraph describing markup of chunks>> +<<paragraph describing markup of chunks>>= +A +.I noweb +file is a sequence of +.IR chunks , +which may appear in any order. +A chunk may contain code or documentation. +Documentation chunks begin with a line that starts with an at sign (@) +followed by a space or newline. +They have no names. +Code chunks begin with +.br +@<<\fIchunk name\fP@>>= +.br +on a line by itself. +The double left angle bracket (@<<) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not mark the beginning of a +chunk, it is assumed to be the first line of a documentation chunk. +@ +<<notangle.1>>= +.PP +Documentation chunks contain text that is ignored by +.I notangle +and copied verbatim to +standard output +by +.I noweave +(except for quoted code). +.I noweave +can work with \fILaTeX\fP, plain \fITeX\fP, \fItroff\fP or \fIHTML\fP. +With plain \fITeX\fP, it inserts a reference to a +.I TeX +macro package, \fInwmac\fP, which defines commands like +.B \echapter +and +.B \esection. +.PP +Code chunks contain program source code and references to other code +chunks. +Several code chunks may have the same name; +.I notangle +concatenates their definitions to produce a single chunk, just as does +.IR tangle (1). +Code chunk definitions are like macro definitions; +.I notangle +extracts a program by expanding one chunk (by default, the chunk named +\fB@<<\fP*\fB@>>\fP). +The definition of that chunk contains references to other chunks, which are +themselves expanded, and so on. +\fInotangle\fP's output is readable; it preserves the indentation of expanded +chunks with respect to the chunks in which they appear. +.PP +Code may be quoted within documentation +chunks by placing double square brackets +(\fB[[\fI...\fB]]\fR) around it. +These double square brackets are ignored by +.I notangle, +but they may be used by +.I noweave +to give the code special typographic treatment, e.g., hypertext links. +If quoted code ends with three or more square brackets, +.I noweave +chooses the rightmost pair, so that, for example, \fB[[a[i]]]\fR is +parsed correctly. +The names of +code chunks may appear within quoted code unless that quoted code is itself +part of the name of a code chunk. +.PP +<<paragraph on escaping angle brackets>> +<<paragraph on escaping angle brackets>>= +In code, noweb treats unpaired +double left or right angle brackets as literal \fB@<<\fP and \fB@>>\fP. +To force any +such brackets, even paired brackets or brackets in documentation, +to be treated as literal, use a preceding at sign (e.g. \fB@@<<\fP). +<<old paragraph on escaping angle brackets>>= +If the double left or right angle bracket is to be used +in code or documentation, +not as part of a chunk name, it should be preceded by an at sign +(e.g. \fB@@<<\fP), although the at sign is not required in code if there is no +matching double bracket. +<<notangle.1>>= +.PP +<<paragraph on escaping initial at sign>> +<<paragraph on escaping initial at sign>>= +Some programming or formatting languages may require a single \fB@\fP sign in +the first column. +Noweb users may achieve this effect by putting a doubled \fB@@\fP in the first column; +in this position only, it stands for a single \fB@\fP sign. +<<paragraph on marking identifier definitions (no longer used)>>= +Any line beginning with `\fB@ \fP' terminates a code chunk, +but if the line has the form +.br +\fB@ %def \fP\fIidentifiers\fP +.br +it is taken to mean that the preceding chunk defines the identifiers listed +in +.IR identifiers . +This list contains identifiers separated by whitespace; +any sequence of nonwhite characters may be an identifier. +.I noweb +uses a simple heuristic to avoid recognizing identifiers +that are substrings of other identifiers. +.I "This feature is deprecated." +.I "Instead, I recommend that you use the" +.B btdefn +.I "filter described on the nowebfilters(7) man page." +<<notangle.1>>= +.SH TANGLING +.I notangle +and +.I nountangle +accept the same set of options, although some options have effects only on one +or the other. +The options are: +.TP +.B \-R\fIname\fR +Expand the \fB@<<\fIname\fB@>>\fR code chunk. +The +.B \-R +option can be repeated, +in which case each chunk is written to the output. +If no +.B \-R +option is given, expand the chunk named \fB@<<\fP*\fB@>>\fP. +.TP +.B \-L\fIformat\fR +Emit line number indications at chunk boundaries. +A line number indication identifies the source of the line that follows it. +In +.IR format , +.B "%F" +indicates the name of the source file, +.B "%L" +indicates the line number of the source file, +.B "%N" +indicates a newline, +and +.B "%%" +indicates a percent sign. +A sign and digit may be inserted between the percent sign and the `\fBL\fP', +in which case the line number will be adjusted by that amount. +If +.I format +is omitted, the default format is that accepted by the C preprocessor: +`\fB#line %L "%F"%N\fR'. +When using the \fB\-L\fIformat\fR option, +.I notangle +ensures that all text appears in the same column in input and output. +.I nountangle +ignores this option. +.IP " " +Common format strings include: +.ta \w'zzzSunyFORTRANxxxx'u +.nf + C \fB\-L'#line %L "%F"%N'\fP + Sun FORTRAN \fB\-L'\\# %L "%F"%N'\fP + Icon \fB\-L'#line %\-1L "%F"%N'\fP + Modula-3 \fB\-L'<*LINE %L "%F" *>%N'\fP + SML/NJ \fB\-L'(*#line %L "%F"*)'\fP +.fi +.IP " " +To solve the converse problem, that is, to get noweb to do something sensible +with \fB#line\fP in its input, see the \fBsharpline\fP filter in the examples +directory. +.TP +.B \-t\fIk\fP +Copy tabs untouched from input to output, and use tabs for indentation, +assuming stops every \fIk\fP columns. +By default, tabs are expanded to spaces with stops every 8 columns. +.TP +.B \-filter \fIcmd\fP +Filter the +.I noweb +source through +.I cmd +after converting it to tool form and before tangling. +.I notangle +looks for +.I cmd +first on the user's +.BR PATH , +then in +.BR |LIBDIR| . +Such filters +can be used to add features to +.IR notangle ; +for an example see +.BR |LIBDIR|/emptydefn . +For experts only. +<<description of -markup option>> +.TP +.B "\-awk | \-c | \-icn | \-icon | \-ml | \-m3 | \-pascal | \-f77 | \-f90 | \-tex" +When +.I nountangle +transforms documentation chunks into comments, use the comment format of the language +named. +.B \-c +is the default. +.I notangle +ignores these options. +.TP +.B \-w\fIn\fP +When +.I nountangle +transforms documentation chunks into comments, create comments on lines of width \fIn\fP. +.I notangle +ignores this option. +.SH WEAVING +<<man page: WEAVING section>> +.SH INDEXING AND CROSS-REFERENCE +<<man page: INDEXING AND CROSS-REFERENCE section>> +When used with +.IR LaTeX , +.IR troff , +or +.I HTML, +.I noweave +can provide indexing and cross-reference information for chunks and for +programming-language identifiers. +Identifier definitions may be marked by hand using backticks (`); the +\fB-filter btdefn\fP option recognizes these markings. +For some languages, definitions may be found automatically using the +\fB-autodefs\fP option. +This section describes the indexing and cross-reference options; +it might well be skipped on first reading. +<<noweave man page indexing options>> +.SH ERROR MESSAGES +If +.I notangle +or +.I noweave +encounters a chunk name within documentation, it assumes that this +indicates an error, usually misspelling ``@<<name@>>=''. +Other error messages should be self-explanatory. +.PP +It is incorrect to refer to a chunk that is never +defined, but it is OK for chunks to be defined and not used. +.SH EXAMPLES +If you have trouble digesting this man page, you're not alone. +Here are a few examples to get you started. +I'll assume you have a +.B foo.nw +file with a C program in chunk +.B @<<foo.c@>> +and a header file in chunk +.BR @<<foo.h@>> , +and that your documentation is marked up using +.IR latex(1) . +I'll show you how to build things using the most common options. +.PP +To rebuild your C source, try +.RS +.B "notangle \-L \-Rfoo.c foo.nw > foo.c" +.RE +To rebuild your header file, try +.RS +.B "notangle \-Rfoo.h foo.nw | cpif foo.h" +.RE +There are two compromises here. +Omitting \fB\-L\fP keeps \fB#line\fP out of your header file, +and using \fBcpif\fP prevents the command from rewriting +.B foo.h +unless the contents have changed. Thus, this is good code to put in a Makefile rule. +.PP +To build a printed document, run +.RS +.B "noweave \-autodefs c \-index foo.nw > foo.tex" +.RE +If you have your own preamble, containing \fB\\documentclass\fP and all, +you will also need the \fB\-delay\fP option. +.PP +To build a web page, run +.RS +.B "noweave \-filter l2h \-autodefs c \-index \-html foo.nw | htmltoc > foo.html" +.RE +Have fun! +.SH FILES +.PP +.ta \w'|TEXINPUTS|nwkernel.texxxxx'u +.nf +|LIBDIR|/markup markup preprocessor +|LIBDIR|/unmarkup inverts markup +|LIBDIR|/nt notangle proper +|LIBDIR|/finduses find uses of identifiers for index +|LIBDIR|/noidx generate index and cross-reference info +|LIBDIR|/toroff back end to emit \fItroff\fP +|LIBDIR|/totex back end to emit \fITeX\fP or \fILaTeX\fP +|LIBDIR|/tohtml back end to emit HTML +|TEXINPUTS|/nwmac.tex formatting \fITeX\fP macros +|TEXINPUTS|/noweb.sty use in \fILaTeX\fP documents; see \fInowebstyle(7)\fP +.fi +.SH SEE ALSO +.PP +.IR cpif (1), +.IR nodefs (1), +.IR noroots (1), +.IR noweb (1), +.IR noindex (1), +.IR noroff (1), +.IR nowebstyle (7), +.IR nowebfilters (7) +.SH BUGS +.I notangle +and +.I nountangle +fail if names used on the command line contain single quotes. +.PP +Ignoring unused chunks can cause problems; +if a chunk has +multiple definitions and one is misspelled, +the misspelled definition is silently ignored. +.IR noroots (1) +can be used to catch this mistake. +\".PP +\".I noweb +\"requires the new version of +\".IR awk , +\"assumed to be called +\".IR nawk . +\"DEC +\".I nawk +\"has a bug in that that causes +\".B noweave +\"to fail to translate braces correctly. +\"GNU +\".I gawk +\"is reported to work. +\".PP +\"DEC +\".I sh +\"has a bug that causes the +\".B \-filter +\"option to fail whenever the filter command contains more than one word. +.PP +The +.I "-L" +option of +.I notangle +puts an implicit initial newline in the format string. +.PP +<<paragraph about bogus latex pagestyles>> +.PP +.IR latex2html (1) +mangles some source files. +.PP +.I noweave +has too many options, and this man page is too long. +<<AUTHOR section>> +<<description of -markup option>>= +.TP +.B "\-markup \fIparser\fP" +Use +.I parser +to parse the input file. +Enables use of noweb tools on files in other formats; +for example, the +.B numarkup +parser understands +.IR nuweb (1) +format. +See +.IR nowebfilters (7) +for more information. +For experts only. +<<paragraph about bogus latex pagestyles>>= +The default +.I LaTeX +pagestyles don't set the width of the boxes containing headers and footers. +Since +.I noweb +code paragraphs are extra wide, this +.I LaTeX +bug sometimes results in extra-wide headers and footers. +The remedy is to redefine the relevant +.B ps@* +commands; +.B ps@noweb +in +.B noweb.sty +can be used as an example. +@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +<<noweb.1>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +noweb \- a simple literate-programming tool +.SH SYNOPSIS +.B noweb +[\fB-t\fP] +[\fB-o\fP] +[\fB-L\fP\fIformat\fP] +[\fB-markup\fP \fIparser\fP] +[file] ... +.SH DESCRIPTION +.I Noweb +is a literate-programming tool like +.I FunnelWEB +or +.IR nuweb , +only simpler. +A +.I noweb +file contains program source code interleaved with documentation. +When +.I noweb +is invoked, it writes the program source code +to the output files mentioned in the noweb file, and it writes a +.I TeX +file for typeset documentation. +.PP +The +.IR noweb (1) +command is for people who don't like reading man pages or who are switching from +.IR nuweb . +To get the most out of +.IR noweb , +use +.IR notangle (1) +and +.IR noweave (1) +instead. +.SH FORMAT OF NOWEB FILES +<<paragraph describing markup of chunks>> +.PP +Documentation chunks contain text that is copied verbatim to the +.I TeX +file (except for quoted code). +.I noweb +works with \fILaTeX\fP; the first documentation chunk must contain +a +.I LaTeX +.B "\\\\documentclass" +command, it must contain +.B "\\\\usepackage{noweb}" +in the preamble, and finally +it must also contain a +.I LaTeX +.B "\\\\begin{document}" +command. +.PP +Code chunks contain program source code and references to other code +chunks. +Several code chunks may have the same name; +.I noweb +concatenates their definitions to produce a single chunk, just as +other literate-programming tools do. +.I noweb +looks for chunks that are defined but not used in the source file. +If the name of such a chunk contains no spaces, the chunk is +an ``output file;'' +.I noweb +expands it and writes the result onto the file of the same name. +A code-chunk definition is like a macro definition; +it contains references to other chunks, which are +themselves expanded, and so on. +\fInoweb\fP's output is readable; it preserves the indentation of expanded +chunks with respect to the chunks in which they appear. +.PP +If a star (\fB*\fP) is appended to the name of an output file, +.I noweb +includes line-number information as specified by the \fB\-L\fP\fIformat\fP +option (or for C if no \fB\-L\fP\fIformat\fP option is given). +The name itself may not contain shell metacharacters. +.PP +Code may be quoted within documentation +chunks by placing double square brackets +(\fB[[\fI...\fB]]\fR) around it. +These double square brackets are used to +give the code special typographic treatment in the +.I TeX +file. +If quoted code ends with three or more square brackets, +.I noweb +chooses the rightmost pair, so that, for example, \fB[[a[i]]]\fR is +parsed correctly. +.PP +<<paragraph on escaping angle brackets>> +.SH OPTIONS +.TP +.B \-t +Suppress generation of a +.I TeX +file. +.TP +.B \-o +Suppress generation of output files. +.TP +.B \-L\fIformat\fR +Use +.I format +to format line-number information for starred output files. +(If the option is omitted, a format suitable for C is used.) +.I format +is as defined by +.IR notangle (1); +<<description of -markup option>> +.SH BUGS +.PP +Ignoring unused chunks whose names contain spaces +sometimes causes problems, especially in the case when a chunk has +multiple definitions and one is misspelled; +the misspelled definition will be silently ignored. +.IR noroots (1) +can be used as a sanity checker to catch this sort of mistake. +.PP +.I noweb +is intended for users who don't want the power or the complexity of +command-line options. +More sophisticated users should avoid +.I noweb +and use +.I noweave +and +.I notangle +instead. +If the design were better, we could all use the same commands. +.PP +.I noweb +requires the new version of +.IR awk , +assumed to be called +.IR nawk . +DEC +.I nawk +has a bug in that that causes problems with braces in +.I TeX +output. +GNU +.I gawk +is reported to work. +.PP +<<paragraph about bogus latex pagestyles>> +.SH SEE ALSO +.PP +.IR notangle (1), +.IR noweave (1), +.IR noroots (1), +.IR nountangle (1), +.IR nowebstyle (7), +.IR nowebfilters (7), +.IR nuweb2noweb (1) +.br +Norman Ramsey, +.I "Literate programming simplified, IEEE Software" +11(5):97-105, September 1994. +<<AUTHOR section>> +<<nowebstyle.7>>= +.TH NOWEB 7 "local <<noweb documentation date>>" +.SH NAME +nowebstyle \- \fILaTeX\fP package for \fInoweb\fP +.SH SYNOPSIS +.B "\\\\usepackage{noweb}" +.SH DESCRIPTION +When +.I noweave +generates code for +.IR LaTeX , +the +.B noweb +package is required. +In addition to the necessary arcana, it +defines a +.B webcode +environment in which you can place fragments of +code, and in which +only backslash and curly braces have their special meanings; +all other characters are taken literally. +It also defines a +.B noweb +pagestyle. +Finally, it defines a +.B "\\\\noweboptions{...}" +command that can be used to tinker with the appearance of the output. +Options are separated by commas and include: +<<man page: [[\noweboptions]]>> +.SH CONTROL SEQUENCES +The noweb package describes a gazillion special control sequences. +Only a few are listed here. +<<man page: noweb style control sequences>> +.SH SEE ALSO +.IR noweave (1), +.IR noweb (1) +<<AUTHOR section>> +@ +<<cpif.1>>= +.TH CPIF 1 "local <<noweb documentation date>>" +.SH NAME +cpif \- selectively update files +.SH SYNOPSIS +.B cpif +[\fB\-eq\fP|\fB\-ne\fP] file ... +.SH DESCRIPTION +.I cpif +is an auxiliary tool for use with +.IR notangle (1) +and +.IR make (1). +.I cpif +compares its standard input to each of the named files, +overwriting the file if the comparison succeeds or if +the named file does not exist. +If neither +.B \-eq +or +.B \-ne +is specified, the default is +.BR \-ne . +Typical usage is in the following default rule, suitable for use in a Makefile. +.br + .nw.h: ; notangle \-Rheader $*.nw | cpif $*.h +.br +.SH SEE ALSO +.IR notangle (1) +<<AUTHOR section>> +@ +<<nuweb2noweb.1>>= +.TH NOWEB 1 "<<noweb documentation date>>" +.SH NAME +nuweb2noweb \- convert nuweb files to noweb form +.SH SYNOPSIS +.B nuweb2noweb +file ... +.SH DESCRIPTION +.I Nuweb2noweb +converts a file or files in +.I nuweb +format to +.I noweb +format, writing the results on standard output. +Input is from the file(s) named on the command line. +.SH SEE ALSO +.PP +.IR noweb (1), +.IR nuweb (1) +<<AUTHOR section>> +@ + diff --git a/web/noweb/src/xdoc/nodefs.1 b/web/noweb/src/xdoc/nodefs.1 new file mode 100644 index 0000000000..168cc83949 --- /dev/null +++ b/web/noweb/src/xdoc/nodefs.1 @@ -0,0 +1,54 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +nodefs \- find definitions in noweb file +.SH SYNOPSIS +.B nodefs +[\fB\-auto\fP \fIlang\fP] +[\fB\-showauto\fP] +file ... +.SH DESCRIPTION +.I nodefs +is an auxiliary tool that produces output for use with the +.B \-indexfrom +option of +.IR noweave (1). +.I nodefs +searches a +.I noweb +file for identifier definitions, printing the identifiers on standard output, +one per line. +If the +.B \-auto \fIlang\fP +option is given, +.I nodefs +tries to discover definitions automatically, assuming the program +is written in language \fIlang\fP. +The +.B \-showauto +option displays permissible values of \fIlang\fP, which vary but may include +.B tex +or +.BR icon . +.PP +For a complete description of using +.I nodefs +together with +.IR noweave (1) +and +.IR noindex (1), +see the man page for +.IR noindex (1). +.SH SEE ALSO +.IR noweave (1), +.IR noweb (1), +.IR noindex (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/nodefs.nw b/web/noweb/src/xdoc/nodefs.nw new file mode 100644 index 0000000000..8053920755 --- /dev/null +++ b/web/noweb/src/xdoc/nodefs.nw @@ -0,0 +1,46 @@ +<<*>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +nodefs \- find definitions in noweb file +.SH SYNOPSIS +.B nodefs +[\fB\-auto\fP \fIlang\fP] +[\fB\-showauto\fP] +file ... +.SH DESCRIPTION +.I nodefs +is an auxiliary tool that produces output for use with the +.B \-indexfrom +option of +.IR noweave (1). +.I nodefs +searches a +.I noweb +file for identifier definitions, printing the identifiers on standard output, +one per line. +If the +.B \-auto \fIlang\fP +option is given, +.I nodefs +tries to discover definitions automatically, assuming the program +is written in language \fIlang\fP. +The +.B \-showauto +option displays permissible values of \fIlang\fP, which vary but may include +.B tex +or +.BR icon . +.PP +For a complete description of using +.I nodefs +together with +.IR noweave (1) +and +.IR noindex (1), +see the man page for +.IR noindex (1). +.SH SEE ALSO +.IR noweave (1), +.IR noweb (1), +.IR noindex (1) +<<AUTHOR section>> diff --git a/web/noweb/src/xdoc/nodefs.txt b/web/noweb/src/xdoc/nodefs.txt new file mode 100644 index 0000000000..9725333d28 --- /dev/null +++ b/web/noweb/src/xdoc/nodefs.txt @@ -0,0 +1,37 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + nodefs - find definitions in noweb file + +SSYYNNOOPPSSIISS + nnooddeeffss [--aauuttoo _l_a_n_g] [--sshhoowwaauuttoo] file ... + +DDEESSCCRRIIPPTTIIOONN + _n_o_d_e_f_s is an auxiliary tool that produces output for use with the + --iinnddeexxffrroomm option of _n_o_w_e_a_v_e(1). _n_o_d_e_f_s searches a _n_o_w_e_b file for + identifier definitions, printing the identifiers on standard output, + one per line. If the --aauuttoo _l_a_n_g option is given, _n_o_d_e_f_s tries to dis†+ cover definitions automatically, assuming the program is written in + language _l_a_n_g. The --sshhoowwaauuttoo option displays permissible values of + _l_a_n_g, which vary but may include tteexx or iiccoonn. + + For a complete description of using _n_o_d_e_f_s together with _n_o_w_e_a_v_e(1) and + _n_o_i_n_d_e_x(1), see the man page for _n_o_i_n_d_e_x(1). + +SSEEEE AALLSSOO + _n_o_w_e_a_v_e(1), _n_o_w_e_b(1), _n_o_i_n_d_e_x(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/noindex.1 b/web/noweb/src/xdoc/noindex.1 new file mode 100644 index 0000000000..7e7dbb5a16 --- /dev/null +++ b/web/noweb/src/xdoc/noindex.1 @@ -0,0 +1,171 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +noindex \- build external index for noweb document +.SH SYNOPSIS +.B noindex \fIbasename\fP\fR[\fP.tex\fR] +.SH DESCRIPTION +.I noindex +looks through +.I LaTeX +.B .aux +files for identifiers that should go in a +.I noweb +external index. +It sorts all identifiers and writes the results on +.I basename\fB.nwi\fP. +.SH NOWEB INDEXING STRATEGIES +A +.I noweb +program consists of one or more files. +In the simple case, these files are run through +.I noweave +together, to produce a single +.I LaTeX +file. +.I "noweave \-index" +suffices to produce an index and cross-referencing information; +neither +.I nodefs +nor +.I noindex +is required. +.PP +When a +.I noweb +program +consists of several source files, it is often better to run each source file +through +.I noweave +to produce its own +.I LaTeX +file, then use +.I noindex +to produce an external index. +This technique has several advantages: +.IP +The line numbers in the +.I LaTeX +files correspond to the line numbers in the source files, so it is easier +to diagnose +.I LaTeX +errors. +.IP +The +.I LaTeX +.B "\\\\includeonly" +feature can be used, making it possible to format parts of large programs while retaining +complete cross-reference information. +.IP +When used with +.IR make (1), +the technique +avoids running +.I noweave +over source files that have not changed. +.IP +Using the external index places fewer demands on \fILaTeX\fP's memory, +making it read its +.B .aux +files much more quickly. +.PP +The disadvantages are that +.I nodefs +and +.I noindex +are needed for full cross-referencing and a properly sorted index. +.SH EXAMPLE +This example assumes a +.I noweb +program of three source files: +.B a.nw , +.B b.nw , +and +.BR c.nw . +The file +.B doc.tex +is assumed to contain +.I LaTeX +boilerplate, including the commands +.IP +\\noweboptions{externalindex} +.nf +\\include{a} +\\include{b} +\\include{c} +.fi +.PP +The first sequence of steps is to create a file listing all the +identifiers defined anywhere in +.BR a , +.BR b , +or +.BR c . +.IP +nodefs a.nw > a.defs +.nf +nodefs b.nw > b.defs +nodefs c.nw > c.defs +sort \-u a.defs b.defs c.defs | cpif all.defs +.fi +.PP +Using +.I "sort \-u" +and +.IR cpif (1) +avoids changing +.B all.defs +unless the set of identifiers changes. +This technique, used in a Makefile, avoids unnecessary rebuilding. +.PP +The next series of steps is to create +.I LaTeX +files with full cross-reference information for all identifiers. +.IP +noweave \-n \-indexfrom all.defs a.nw > a.tex +.nf +noweave \-n \-indexfrom all.defs b.nw > b.tex +noweave \-n \-indexfrom all.defs c.nw > c.tex +.fi +.PP +The final steps run +.I LaTeX +once to create +.B .aux +files, then +.I noindex +to create the index, +then +.I LaTeX +again to format the complete document. +.IP +latex doc +.nf +noindex doc +latex doc +.fi +.PP +In a Makefile, +.I noindex +can be run before every invocation of +.I LaTeX. +.SH BUGS +.I noindex +is distributed in awk and Icon versions. +The awk version is slow and does a poorer job sorting. +.PP +There is no comparable machinery to make it possible to use multiple files +with the HTML back end. +.SH SEE ALSO +.IR noweave (1), +.IR nodefs (1), +.IR cpif (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/noindex.nw b/web/noweb/src/xdoc/noindex.nw new file mode 100644 index 0000000000..e51b77cab2 --- /dev/null +++ b/web/noweb/src/xdoc/noindex.nw @@ -0,0 +1,163 @@ +<<*>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +noindex \- build external index for noweb document +.SH SYNOPSIS +.B noindex \fIbasename\fP\fR[\fP.tex\fR] +.SH DESCRIPTION +.I noindex +looks through +.I LaTeX +.B .aux +files for identifiers that should go in a +.I noweb +external index. +It sorts all identifiers and writes the results on +.I basename\fB.nwi\fP. +.SH NOWEB INDEXING STRATEGIES +A +.I noweb +program consists of one or more files. +In the simple case, these files are run through +.I noweave +together, to produce a single +.I LaTeX +file. +.I "noweave \-index" +suffices to produce an index and cross-referencing information; +neither +.I nodefs +nor +.I noindex +is required. +.PP +When a +.I noweb +program +consists of several source files, it is often better to run each source file +through +.I noweave +to produce its own +.I LaTeX +file, then use +.I noindex +to produce an external index. +This technique has several advantages: +.IP +The line numbers in the +.I LaTeX +files correspond to the line numbers in the source files, so it is easier +to diagnose +.I LaTeX +errors. +.IP +The +.I LaTeX +.B "\\\\includeonly" +feature can be used, making it possible to format parts of large programs while retaining +complete cross-reference information. +.IP +When used with +.IR make (1), +the technique +avoids running +.I noweave +over source files that have not changed. +.IP +Using the external index places fewer demands on \fILaTeX\fP's memory, +making it read its +.B .aux +files much more quickly. +.PP +The disadvantages are that +.I nodefs +and +.I noindex +are needed for full cross-referencing and a properly sorted index. +.SH EXAMPLE +This example assumes a +.I noweb +program of three source files: +.B a.nw , +.B b.nw , +and +.BR c.nw . +The file +.B doc.tex +is assumed to contain +.I LaTeX +boilerplate, including the commands +.IP +\\noweboptions{externalindex} +.nf +\\include{a} +\\include{b} +\\include{c} +.fi +.PP +The first sequence of steps is to create a file listing all the +identifiers defined anywhere in +.BR a , +.BR b , +or +.BR c . +.IP +nodefs a.nw > a.defs +.nf +nodefs b.nw > b.defs +nodefs c.nw > c.defs +sort \-u a.defs b.defs c.defs | cpif all.defs +.fi +.PP +Using +.I "sort \-u" +and +.IR cpif (1) +avoids changing +.B all.defs +unless the set of identifiers changes. +This technique, used in a Makefile, avoids unnecessary rebuilding. +.PP +The next series of steps is to create +.I LaTeX +files with full cross-reference information for all identifiers. +.IP +noweave \-n \-indexfrom all.defs a.nw > a.tex +.nf +noweave \-n \-indexfrom all.defs b.nw > b.tex +noweave \-n \-indexfrom all.defs c.nw > c.tex +.fi +.PP +The final steps run +.I LaTeX +once to create +.B .aux +files, then +.I noindex +to create the index, +then +.I LaTeX +again to format the complete document. +.IP +latex doc +.nf +noindex doc +latex doc +.fi +.PP +In a Makefile, +.I noindex +can be run before every invocation of +.I LaTeX. +.SH BUGS +.I noindex +is distributed in awk and Icon versions. +The awk version is slow and does a poorer job sorting. +.PP +There is no comparable machinery to make it possible to use multiple files +with the HTML back end. +.SH SEE ALSO +.IR noweave (1), +.IR nodefs (1), +.IR cpif (1) +<<AUTHOR section>> diff --git a/web/noweb/src/xdoc/noindex.txt b/web/noweb/src/xdoc/noindex.txt new file mode 100644 index 0000000000..a3823e4489 --- /dev/null +++ b/web/noweb/src/xdoc/noindex.txt @@ -0,0 +1,103 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + noindex - build external index for noweb document + +SSYYNNOOPPSSIISS + nnooiinnddeexx _b_a_s_e_n_a_m_e[..tteexx] + +DDEESSCCRRIIPPTTIIOONN + _n_o_i_n_d_e_x looks through _L_a_T_e_X ..aauuxx files for identifiers that should go + in a _n_o_w_e_b external index. It sorts all identifiers and writes the + results on _b_a_s_e_n_a_m_e..nnwwii_. + +NNOOWWEEBB IINNDDEEXXIINNGG SSTTRRAATTEEGGIIEESS + A _n_o_w_e_b program consists of one or more files. In the simple case, + these files are run through _n_o_w_e_a_v_e together, to produce a single _L_a_T_e_X + file. _n_o_w_e_a_v_e _-_i_n_d_e_x suffices to produce an index and cross-referenc†+ ing information; neither _n_o_d_e_f_s nor _n_o_i_n_d_e_x is required. + + When a _n_o_w_e_b program consists of several source files, it is often bet†+ ter to run each source file through _n_o_w_e_a_v_e to produce its own _L_a_T_e_X + file, then use _n_o_i_n_d_e_x to produce an external index. This technique + has several advantages: + + The line numbers in the _L_a_T_e_X files correspond to the line num†+ bers in the source files, so it is easier to diagnose _L_a_T_e_X + errors. + + The _L_a_T_e_X \\iinncclluuddeeoonnllyy feature can be used, making it possible + to format parts of large programs while retaining complete + cross-reference information. + + When used with _m_a_k_e(1), the technique avoids running _n_o_w_e_a_v_e + over source files that have not changed. + + Using the external index places fewer demands on _L_a_T_e_X's memory, + making it read its ..aauuxx files much more quickly. + + The disadvantages are that _n_o_d_e_f_s and _n_o_i_n_d_e_x are needed for full + cross-referencing and a properly sorted index. + +EEXXAAMMPPLLEE + This example assumes a _n_o_w_e_b program of three source files: aa..nnww ,, bb..nnww + ,, and cc..nnww. The file ddoocc..tteexx is assumed to contain _L_a_T_e_X boilerplate, + including the commands + + \noweboptions{externalindex} + \include{a} + \include{b} + \include{c} + + The first sequence of steps is to create a file listing all the identi†+ fiers defined anywhere in aa, bb, or cc. + + nodefs a.nw > a.defs + nodefs b.nw > b.defs + nodefs c.nw > c.defs + sort -u a.defs b.defs c.defs | cpif all.defs + + Using _s_o_r_t _-_u and _c_p_i_f(1) avoids changing aallll..ddeeffss unless the set of + identifiers changes. This technique, used in a Makefile, avoids unnec†+ essary rebuilding. + + The next series of steps is to create _L_a_T_e_X files with full cross-ref†+ erence information for all identifiers. + + noweave -n -indexfrom all.defs a.nw > a.tex + noweave -n -indexfrom all.defs b.nw > b.tex + noweave -n -indexfrom all.defs c.nw > c.tex + + The final steps run _L_a_T_e_X once to create ..aauuxx files, then _n_o_i_n_d_e_x to + create the index, then _L_a_T_e_X again to format the complete document. + + latex doc + noindex doc + latex doc + + In a Makefile, _n_o_i_n_d_e_x can be run before every invocation of _L_a_T_e_X_. + +BBUUGGSS + _n_o_i_n_d_e_x is distributed in awk and Icon versions. The awk version is + slow and does a poorer job sorting. + + There is no comparable machinery to make it possible to use multiple + files with the HTML back end. + +SSEEEE AALLSSOO + _n_o_w_e_a_v_e(1), _n_o_d_e_f_s(1), _c_p_i_f(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/noroff.1 b/web/noweb/src/xdoc/noroff.1 new file mode 100644 index 0000000000..7d082f1f2e --- /dev/null +++ b/web/noweb/src/xdoc/noroff.1 @@ -0,0 +1,57 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +noroff \- format woven \fItroff\fP documentation +.SH SYNOPSIS +.B noroff +.RI [ " groff options " ] +files ... +.SH DESCRIPTION +.I Noroff +formats documents that have been woven with +.IR "noweave \-troff" . +It passes on any options (flags) to +.IR groff (1), +and treats any remaining non-option arguments as files to be processed. +The +.I groff +options allow the easy invocation of the +.IR gpic , +.IR gtbl , +and/or +.IR geqn , +preprocessors. +.PP +If no options are supplied, it defaults to using the +.B \-mm +macros, with PostScript output. +.SH BUGS +The shell script is probably still not as robust as it needs to be. +.SH SEE ALSO +.IR groff (1), +.IR noweave (1), +.IR nodefs (1), +.IR cpif (1) +.br +.I "Converting noweb markup to troff markup" +in the file +.B src/shell/roff.nw +in the +.I noweb +distribution. +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + +.PP +.I Noroff +was written by Phil Bewig for SoftQuad +.IR troff , +and enhanced and ported to GNU +.I troff +by Arnold Robbins. diff --git a/web/noweb/src/xdoc/noroff.nw b/web/noweb/src/xdoc/noroff.nw new file mode 100644 index 0000000000..142fbb229b --- /dev/null +++ b/web/noweb/src/xdoc/noroff.nw @@ -0,0 +1,49 @@ +<<*>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +noroff \- format woven \fItroff\fP documentation +.SH SYNOPSIS +.B noroff +.RI [ " groff options " ] +files ... +.SH DESCRIPTION +.I Noroff +formats documents that have been woven with +.IR "noweave \-troff" . +It passes on any options (flags) to +.IR groff (1), +and treats any remaining non-option arguments as files to be processed. +The +.I groff +options allow the easy invocation of the +.IR gpic , +.IR gtbl , +and/or +.IR geqn , +preprocessors. +.PP +If no options are supplied, it defaults to using the +.B \-mm +macros, with PostScript output. +.SH BUGS +The shell script is probably still not as robust as it needs to be. +.SH SEE ALSO +.IR groff (1), +.IR noweave (1), +.IR nodefs (1), +.IR cpif (1) +.br +.I "Converting noweb markup to troff markup" +in the file +.B src/shell/roff.nw +in the +.I noweb +distribution. +<<AUTHOR section>> +.PP +.I Noroff +was written by Phil Bewig for SoftQuad +.IR troff , +and enhanced and ported to GNU +.I troff +by Arnold Robbins. diff --git a/web/noweb/src/xdoc/noroff.txt b/web/noweb/src/xdoc/noroff.txt new file mode 100644 index 0000000000..6693710890 --- /dev/null +++ b/web/noweb/src/xdoc/noroff.txt @@ -0,0 +1,42 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + noroff - format woven _t_r_o_f_f documentation + +SSYYNNOOPPSSIISS + nnoorrooffff [ _g_r_o_f_f _o_p_t_i_o_n_s ] files ... + +DDEESSCCRRIIPPTTIIOONN + _N_o_r_o_f_f formats documents that have been woven with _n_o_w_e_a_v_e _-_t_r_o_f_f. It + passes on any options (flags) to _g_r_o_f_f(1), and treats any remaining + non-option arguments as files to be processed. The _g_r_o_f_f options allow + the easy invocation of the _g_p_i_c, _g_t_b_l, and/or _g_e_q_n, preprocessors. + + If no options are supplied, it defaults to using the --mmmm macros, with + PostScript output. + +BBUUGGSS + The shell script is probably still not as robust as it needs to be. + +SSEEEE AALLSSOO + _g_r_o_f_f(1), _n_o_w_e_a_v_e(1), _n_o_d_e_f_s(1), _c_p_i_f(1) + _C_o_n_v_e_r_t_i_n_g _n_o_w_e_b _m_a_r_k_u_p _t_o _t_r_o_f_f _m_a_r_k_u_p in the file ssrrcc//sshheellll//rrooffff..nnww + in the _n_o_w_e_b distribution. + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + _N_o_r_o_f_f was written by Phil Bewig for SoftQuad _t_r_o_f_f, and enhanced and + ported to GNU _t_r_o_f_f by Arnold Robbins. + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/noroots.1 b/web/noweb/src/xdoc/noroots.1 new file mode 100644 index 0000000000..f08c3d127d --- /dev/null +++ b/web/noweb/src/xdoc/noroots.1 @@ -0,0 +1,30 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +noroots \- print roots of a noweb file +.SH SYNOPSIS +.B noroots +[file] ... +.SH DESCRIPTION +.I noroots +finds the roots of a +.I noweb +file: it prints the names of all code chunks that are defined but not used. +See +.IR noweb (1) +for an explanation of +the format of +.I noweb +files. +.SH SEE ALSO +.IR noweb (1), +.IR notangle (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/noroots.nw b/web/noweb/src/xdoc/noroots.nw new file mode 100644 index 0000000000..1db4b3264c --- /dev/null +++ b/web/noweb/src/xdoc/noroots.nw @@ -0,0 +1,22 @@ +<<*>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +noroots \- print roots of a noweb file +.SH SYNOPSIS +.B noroots +[file] ... +.SH DESCRIPTION +.I noroots +finds the roots of a +.I noweb +file: it prints the names of all code chunks that are defined but not used. +See +.IR noweb (1) +for an explanation of +the format of +.I noweb +files. +.SH SEE ALSO +.IR noweb (1), +.IR notangle (1) +<<AUTHOR section>> diff --git a/web/noweb/src/xdoc/noroots.txt b/web/noweb/src/xdoc/noroots.txt new file mode 100644 index 0000000000..91cace55a4 --- /dev/null +++ b/web/noweb/src/xdoc/noroots.txt @@ -0,0 +1,30 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + noroots - print roots of a noweb file + +SSYYNNOOPPSSIISS + nnoorroooottss [file] ... + +DDEESSCCRRIIPPTTIIOONN + _n_o_r_o_o_t_s finds the roots of a _n_o_w_e_b file: it prints the names of all + code chunks that are defined but not used. See _n_o_w_e_b(1) for an expla†+ nation of the format of _n_o_w_e_b files. + +SSEEEE AALLSSOO + _n_o_w_e_b(1), _n_o_t_a_n_g_l_e(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/notangle.1 b/web/noweb/src/xdoc/notangle.1 new file mode 100644 index 0000000000..870d63e8e7 --- /dev/null +++ b/web/noweb/src/xdoc/notangle.1 @@ -0,0 +1,641 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +notangle, noweave, nountangle \- noweb, a literate-programming tool +.SH SYNOPSIS +.B notangle +[\fB\-R\fProotname ...] [\fB\-filter\fP command] +[\fB\-L\fP[format]] [file] ... +.br +\fBnountangle\fP +[\fB\-ml\fP|\fB\-m3\fP|\fB\-c\fP|\fB\-c++\fP|\fB\-awk\fP|\fB\-tex\fP|\fB\-f77\fP|\fB\-f90\fP|\fB\-lisp\fP|\fB\-matlab\fP] +[\fB\-R\fProotname ...] [\fB\-filter\fP command] [\fB\-w\fPwidth] [file] ... +.br +\fBnoweave\fP [options] [file] ... +.SH DESCRIPTION +.I Noweb +is a literate-programming tool like Knuth's +.I WEB, +only simpler. +A +.I noweb +file contains program source code interleaved with documentation. +When +.I notangle +is given a +.I noweb +file, it writes the program on standard output. +When +.I noweave +is given a +.I noweb +file, it reads the +.I noweb +source and produces, on standard output, \fILaTeX\fP, \fITeX\fP, \fItroff\fP, +or \fIHTML\fP +source for typeset documentation. +.I nountangle +converts a literate program into an ordinary program by +turning interleaved documentation into comments. +The file name `\-' refers to standard input. +.SH FORMAT OF NOWEB FILES +A +.I noweb +file is a sequence of +.IR chunks , +which may appear in any order. +A chunk may contain code or documentation. +Documentation chunks begin with a line that starts with an at sign (@) +followed by a space or newline. +They have no names. +Code chunks begin with +.br +<<\fIchunk name\fP>>= +.br +on a line by itself. +The double left angle bracket (<<) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not mark the beginning of a +chunk, it is assumed to be the first line of a documentation chunk. +.PP +Documentation chunks contain text that is ignored by +.I notangle +and copied verbatim to +standard output +by +.I noweave +(except for quoted code). +.I noweave +can work with \fILaTeX\fP, plain \fITeX\fP, \fItroff\fP or \fIHTML\fP. +With plain \fITeX\fP, it inserts a reference to a +.I TeX +macro package, \fInwmac\fP, which defines commands like +.B \echapter +and +.B \esection. +.PP +Code chunks contain program source code and references to other code +chunks. +Several code chunks may have the same name; +.I notangle +concatenates their definitions to produce a single chunk, just as does +.IR tangle (1). +Code chunk definitions are like macro definitions; +.I notangle +extracts a program by expanding one chunk (by default, the chunk named +\fB<<\fP*\fB>>\fP). +The definition of that chunk contains references to other chunks, which are +themselves expanded, and so on. +\fInotangle\fP's output is readable; it preserves the indentation of expanded +chunks with respect to the chunks in which they appear. +.PP +Code may be quoted within documentation +chunks by placing double square brackets +(\fB[[\fI...\fB]]\fR) around it. +These double square brackets are ignored by +.I notangle, +but they may be used by +.I noweave +to give the code special typographic treatment, e.g., hypertext links. +If quoted code ends with three or more square brackets, +.I noweave +chooses the rightmost pair, so that, for example, \fB[[a[i]]]\fR is +parsed correctly. +The names of +code chunks may appear within quoted code unless that quoted code is itself +part of the name of a code chunk. +.PP +In code, noweb treats unpaired +double left or right angle brackets as literal \fB<<\fP and \fB>>\fP. +To force any +such brackets, even paired brackets or brackets in documentation, +to be treated as literal, use a preceding at sign (e.g. \fB@<<\fP). +.PP +Some programming or formatting languages may require a single \fB@\fP sign in +the first column. +Noweb users may achieve this effect by putting a doubled \fB@@\fP in the first column; +in this position only, it stands for a single \fB@\fP sign. +.SH TANGLING +.I notangle +and +.I nountangle +accept the same set of options, although some options have effects only on one +or the other. +The options are: +.TP +.B \-R\fIname\fR +Expand the \fB<<\fIname\fB>>\fR code chunk. +The +.B \-R +option can be repeated, +in which case each chunk is written to the output. +If no +.B \-R +option is given, expand the chunk named \fB<<\fP*\fB>>\fP. +.TP +.B \-L\fIformat\fR +Emit line number indications at chunk boundaries. +A line number indication identifies the source of the line that follows it. +In +.IR format , +.B "%F" +indicates the name of the source file, +.B "%L" +indicates the line number of the source file, +.B "%N" +indicates a newline, +and +.B "%%" +indicates a percent sign. +A sign and digit may be inserted between the percent sign and the `\fBL\fP', +in which case the line number will be adjusted by that amount. +If +.I format +is omitted, the default format is that accepted by the C preprocessor: +`\fB#line %L "%F"%N\fR'. +When using the \fB\-L\fIformat\fR option, +.I notangle +ensures that all text appears in the same column in input and output. +.I nountangle +ignores this option. +.IP " " +Common format strings include: +.ta \w'zzzSunyFORTRANxxxx'u +.nf + C \fB\-L'#line %L "%F"%N'\fP + Sun FORTRAN \fB\-L'\\# %L "%F"%N'\fP + Icon \fB\-L'#line %\-1L "%F"%N'\fP + Modula-3 \fB\-L'<*LINE %L "%F" *>%N'\fP + SML/NJ \fB\-L'(*#line %L "%F"*)'\fP +.fi +.IP " " +To solve the converse problem, that is, to get noweb to do something sensible +with \fB#line\fP in its input, see the \fBsharpline\fP filter in the examples +directory. +.TP +.B \-t\fIk\fP +Copy tabs untouched from input to output, and use tabs for indentation, +assuming stops every \fIk\fP columns. +By default, tabs are expanded to spaces with stops every 8 columns. +.TP +.B \-filter \fIcmd\fP +Filter the +.I noweb +source through +.I cmd +after converting it to tool form and before tangling. +.I notangle +looks for +.I cmd +first on the user's +.BR PATH , +then in +.BR |LIBDIR| . +Such filters +can be used to add features to +.IR notangle ; +for an example see +.BR |LIBDIR|/emptydefn . +For experts only. +.TP +.B "\-markup \fIparser\fP" +Use +.I parser +to parse the input file. +Enables use of noweb tools on files in other formats; +for example, the +.B numarkup +parser understands +.IR nuweb (1) +format. +See +.IR nowebfilters (7) +for more information. +For experts only. +.TP +.B "\-awk | \-c | \-icn | \-icon | \-ml | \-m3 | \-pascal | \-f77 | \-f90 | \-tex" +When +.I nountangle +transforms documentation chunks into comments, use the comment format of the language +named. +.B \-c +is the default. +.I notangle +ignores these options. +.TP +.B \-w\fIn\fP +When +.I nountangle +transforms documentation chunks into comments, create comments on lines of width \fIn\fP. +.I notangle +ignores this option. +.SH WEAVING +Output from \fInoweave\fP can +be used in \fITeX\fP documents that +.B "\\\\input nwmac," +in \fILaTeX\fP documents that use the +.B noweb +package (see \fInowebstyle(1)\fP), +and in \fIHTML\fP documents to be browsed with +.I Mosaic(1). +.I Noweave +treats code chunks somewhat like +.I LaTeX list environments. +If the ``\fB@ \fP'' that terminates a code chunk is followed immediately by text, +that text follows the code chunk without a paragraph break. +If the rest of the line is blank, +.I noweave +puts +.I TeX +into ``vertical mode,'' and later text starts a fresh, indented paragraph. +.PP +No page breaks occur in the middle of code chunks unless necessary to avoid +an overfull vbox. +The documentation chunk immediately preceding a code chunk appears on +the same page as that code chunk unless doing so would violate the previous rule. +.PP +.I Noweave +inserts no extra newlines in its \fITeX\fP output, so the line numbers given +in +.I TeX +error messages are the same as those in the input file. +.PP +.I noweave +has +options that dictate choice of +formatter +and that support different formatting idioms and tools. +Basic options are described here; options related to index +and cross-reference information are described in the +INDEXING AND CROSS-REFERENCE section. +.TP +.B \-latex +Emit LaTeX, including wrapper in +.B article +style with the +.B noweb +package and page style. (Default) +.TP +.B \-tex +Emit plain TeX, including wrapper with +.B nwmac +macros. +.TP +.B \-html +Emit HTML, using HTML wrapper. +The output is uninteresting without \fB-index\fP or \fB-x\fP. +The tags \fB<nowebchunks>\fP and \fB<nowebindex>\fP, on lines by themselves, +produce a list of chunks and an index of identifiers, respectively. +If these tags are not present, the list and index are placed at the end of the file. +.TP +.B \-latex+html +Assume documentation chunks are LaTeX, but generate HTML for code chunks, +suitably marked so conversion with +.I latex2html(1) +yields reasonable output. +A LaTeX wrapper is implied, but can be turned off with \fB-n\fP. +.I Use of this option is +.B deprecated; +use +.B \-html +with +.B "\-filter l2h" +instead. +.TP +.B \-troff +Emit +.IR troff (1) +markup (with no wrapper). +The result should be processed with +.IR noroff (1). +Bug reports for +.B \-troff +to Arnold Robbins +.B <arnold@skeeve.com>. +.TP +.B \-n +Don't use any wrapper (header or trailer). +This option is useful when \fInoweave\fP's output will be +a part of a larger document. +See also +.B \-delay. +.TP +.B \-filter \fIcmd\fP +Filters the +.I noweb +source through +.I cmd +after converting it to tool form and before converting to +.I TeX. +.I noweave +looks for +.I cmd +first on the user's +.B PATH, +then in +.B |LIBDIR|. +Such filters +can be used to add features to +.I noweave; +for an example, see +.B |LIBDIR|/noxref.krom. +.I Noweave +supports up to four filters; one can get more by shell trickery, +for example, \fB-filter "icon.filter | noidx"\fP. +The \fB-autodefs\fP, +\fB-x\fP, \fB-index\fP, and \fB-indexfrom\fP options are implemented as filters. +Filters are executed with the shell's +.B eval +command, so +.I cmd +should be quoted accordingly. +.TP +.B "\-markup \fIparser\fP" +Use +.I parser +to parse the input file. +Enables use of noweb tools on files in other formats; +for example, the +.B numarkup +parser understands +.IR nuweb (1) +format. +See +.IR nowebfilters (7) +for more information. +For experts only. +.TP +.B \-option \fIopt\fP +Adds \fB\enoweboptions{\fP\fIopt\fP\fB}\fP to the +.I LaTeX +header. +See +.I nowebstyle(1) +for values of +.I opt. +Normally useful only with the +.B \-latex +option, but +.B "\-option longxref" +works black magic with +.B \-html. +.TP +.B \-delay +By default, +.I noweave +puts file-name and other information into the output before the first chunk +of the program. +.B \-delay +delays that information until after the first documentation chunk, making +act a little bit like the +.I WEB +``limbo.'' +The option is typically used to enable a user to put a specialized +.I LaTeX +.B "\\\\documentclass" +command and other preamble material in the first documentation chunk (i.e., +.I before +the first @ sign). +This option also forces trailing cross-referencing information to be emitted +just before the final chunk, instead of at the end of the document; +the final chunk is expected to contain +.B "\\\\end{document}." +The +.B \-delay +option implies the +.B \-n +option. +.TP +.B \-t\fIk\fP +Expand tabs with stops every \fIk\fP columns. +(Default is to expand every 8 columns.) +.TP +.B \-t +Copy tabs to the output. +.TP +.B \-v +Print the pipeline and RCS info on standard error. +.SH INDEXING AND CROSS-REFERENCE + +When used with +.IR LaTeX , +.IR troff , +or +.I HTML, +.I noweave +can provide indexing and cross-reference information for chunks and for +programming-language identifiers. +Identifier definitions may be marked by hand using backticks (`); the +\fB-filter btdefn\fP option recognizes these markings. +For some languages, definitions may be found automatically using the +\fB-autodefs\fP option. +This section describes the indexing and cross-reference options; +it might well be skipped on first reading. +.TP +.B \-x +For +.I LaTeX, +add a page number to each chunk name identifying the location of that +chunk's definition, and emit cross-reference information relating definitions and uses. +For +.I HTML, +create hypertext links between uses and definitions of chunks. +When +.B noweave -x +is used with +.I LaTeX, +the control sequence +.B "\\\\nowebchunks" +expands to a sorted list of all code chunks. +.TP +.B \-index +Build cross-reference information (or hypertext links) for defined identifiers. +Definitions are those found in the input files by +.B \-autodefs +.I language +or by +.BR \-filter btdefn. +Requires +.I LaTeX +or +.I HTML. +.B \-index +implies +.B \-x; +including both will generate strange-looking output. +.I noweave +does not generate +cross-references to identifiers that appear in quoted code (\fB@[[\fP...\fB@]]\fP), +but it does generate hypertext links. +When +.B noweave -index +is used with +.I LaTeX, +the control sequence +.B "\\\\nowebindex" +expands to an index of identifiers. +.TP +.B \-indexfrom \fIindex\fP +Like +.B \-index, +but the identifiers to be indexed are taken from file \fIindex\fP. +See +.I noindex(1). +.TP +.B \-autodefs \fIlang\fP +Discover identifier definitions automatically. +Code in chunks must be in language \fIlang\fP. +Permissible \fIlang\fPs vary but may include +.B tex +or +.B icon. +Useless without +.B \-index, +which it must precede. +.TP +.B \-showautodefs +Show values of \fIlang\fP usable with \fB-autodefs\fP. +.SH ERROR MESSAGES +If +.I notangle +or +.I noweave +encounters a chunk name within documentation, it assumes that this +indicates an error, usually misspelling ``<<name>>=''. +Other error messages should be self-explanatory. +.PP +It is incorrect to refer to a chunk that is never +defined, but it is OK for chunks to be defined and not used. +.SH EXAMPLES +If you have trouble digesting this man page, you're not alone. +Here are a few examples to get you started. +I'll assume you have a +.B foo.nw +file with a C program in chunk +.B <<foo.c>> +and a header file in chunk +.BR <<foo.h>> , +and that your documentation is marked up using +.IR latex(1) . +I'll show you how to build things using the most common options. +.PP +To rebuild your C source, try +.RS +.B "notangle \-L \-Rfoo.c foo.nw > foo.c" +.RE +To rebuild your header file, try +.RS +.B "notangle \-Rfoo.h foo.nw | cpif foo.h" +.RE +There are two compromises here. +Omitting \fB\-L\fP keeps \fB#line\fP out of your header file, +and using \fBcpif\fP prevents the command from rewriting +.B foo.h +unless the contents have changed. Thus, this is good code to put in a Makefile rule. +.PP +To build a printed document, run +.RS +.B "noweave \-autodefs c \-index foo.nw > foo.tex" +.RE +If you have your own preamble, containing \fB\\documentclass\fP and all, +you will also need the \fB\-delay\fP option. +.PP +To build a web page, run +.RS +.B "noweave \-filter l2h \-autodefs c \-index \-html foo.nw | htmltoc > foo.html" +.RE +Have fun! +.SH FILES +.PP +.ta \w'|TEXINPUTS|nwkernel.texxxxx'u +.nf +|LIBDIR|/markup markup preprocessor +|LIBDIR|/unmarkup inverts markup +|LIBDIR|/nt notangle proper +|LIBDIR|/finduses find uses of identifiers for index +|LIBDIR|/noidx generate index and cross-reference info +|LIBDIR|/toroff back end to emit \fItroff\fP +|LIBDIR|/totex back end to emit \fITeX\fP or \fILaTeX\fP +|LIBDIR|/tohtml back end to emit HTML +|TEXINPUTS|/nwmac.tex formatting \fITeX\fP macros +|TEXINPUTS|/noweb.sty use in \fILaTeX\fP documents; see \fInowebstyle(7)\fP +.fi +.SH SEE ALSO +.PP +.IR cpif (1), +.IR nodefs (1), +.IR noroots (1), +.IR noweb (1), +.IR noindex (1), +.IR noroff (1), +.IR nowebstyle (7), +.IR nowebfilters (7) +.SH BUGS +.I notangle +and +.I nountangle +fail if names used on the command line contain single quotes. +.PP +Ignoring unused chunks can cause problems; +if a chunk has +multiple definitions and one is misspelled, +the misspelled definition is silently ignored. +.IR noroots (1) +can be used to catch this mistake. +\".PP +\".I noweb +\"requires the new version of +\".IR awk , +\"assumed to be called +\".IR nawk . +\"DEC +\".I nawk +\"has a bug in that that causes +\".B noweave +\"to fail to translate braces correctly. +\"GNU +\".I gawk +\"is reported to work. +\".PP +\"DEC +\".I sh +\"has a bug that causes the +\".B \-filter +\"option to fail whenever the filter command contains more than one word. +.PP +The +.I "-L" +option of +.I notangle +puts an implicit initial newline in the format string. +.PP +The default +.I LaTeX +pagestyles don't set the width of the boxes containing headers and footers. +Since +.I noweb +code paragraphs are extra wide, this +.I LaTeX +bug sometimes results in extra-wide headers and footers. +The remedy is to redefine the relevant +.B ps@* +commands; +.B ps@noweb +in +.B noweb.sty +can be used as an example. +.PP +.IR latex2html (1) +mangles some source files. +.PP +.I noweave +has too many options, and this man page is too long. +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/notangle.txt b/web/noweb/src/xdoc/notangle.txt new file mode 100644 index 0000000000..a8496e9a0c --- /dev/null +++ b/web/noweb/src/xdoc/notangle.txt @@ -0,0 +1,361 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + notangle, noweave, nountangle - noweb, a literate-programming tool + +SSYYNNOOPPSSIISS + nnoottaannggllee [--RRrootname ...] [--ffiilltteerr command] [--LL[format]] [file] ... + nnoouunnttaannggllee [--mmll|--mm33|--cc|--cc++++|--aawwkk|--tteexx|--ff7777|--ff9900|--lliisspp|--mmaattllaabb] [--RRroot†+ name ...] [--ffiilltteerr command] [--wwwidth] [file] ... + nnoowweeaavvee [options] [file] ... + +DDEESSCCRRIIPPTTIIOONN + _N_o_w_e_b is a literate-programming tool like Knuth's _W_E_B_, only simpler. A + _n_o_w_e_b file contains program source code interleaved with documentation. + When _n_o_t_a_n_g_l_e is given a _n_o_w_e_b file, it writes the program on standard + output. When _n_o_w_e_a_v_e is given a _n_o_w_e_b file, it reads the _n_o_w_e_b source + and produces, on standard output, _L_a_T_e_X, _T_e_X, _t_r_o_f_f, or _H_T_M_L source for + typeset documentation. _n_o_u_n_t_a_n_g_l_e converts a literate program into an + ordinary program by turning interleaved documentation into comments. + The file name `-' refers to standard input. + +FFOORRMMAATT OOFF NNOOWWEEBB FFIILLEESS + A _n_o_w_e_b file is a sequence of _c_h_u_n_k_s, which may appear in any order. A + chunk may contain code or documentation. Documentation chunks begin + with a line that starts with an at sign (@) followed by a space or new†+ line. They have no names. Code chunks begin with + <<_c_h_u_n_k _n_a_m_e>>= + on a line by itself. The double left angle bracket (<<) must be in the + first column. Chunks are terminated by the beginning of another chunk, + or by end of file. If the first line in the file does not mark the + beginning of a chunk, it is assumed to be the first line of a documen†+ tation chunk. + + Documentation chunks contain text that is ignored by _n_o_t_a_n_g_l_e and + copied verbatim to standard output by _n_o_w_e_a_v_e (except for quoted code). + _n_o_w_e_a_v_e can work with _L_a_T_e_X, plain _T_e_X, _t_r_o_f_f or _H_T_M_L. With plain _T_e_X, + it inserts a reference to a _T_e_X macro package, _n_w_m_a_c, which defines + commands like \\cchhaapptteerr and \\sseeccttiioonn.. + + Code chunks contain program source code and references to other code + chunks. Several code chunks may have the same name; _n_o_t_a_n_g_l_e concate†+ nates their definitions to produce a single chunk, just as does _t_a_n_†+ _g_l_e(1). Code chunk definitions are like macro definitions; _n_o_t_a_n_g_l_e + extracts a program by expanding one chunk (by default, the chunk named + <<<<*>>>>). The definition of that chunk contains references to other + chunks, which are themselves expanded, and so on. _n_o_t_a_n_g_l_e's output is + readable; it preserves the indentation of expanded chunks with respect + to the chunks in which they appear. + + Code may be quoted within documentation chunks by placing double square + brackets ([[[[_._._.]]]]) around it. These double square brackets are ignored + by _n_o_t_a_n_g_l_e_, but they may be used by _n_o_w_e_a_v_e to give the code special + typographic treatment, e.g., hypertext links. If quoted code ends with + three or more square brackets, _n_o_w_e_a_v_e chooses the rightmost pair, so + that, for example, [[[[aa[[ii]]]]]] is parsed correctly. The names of code + chunks may appear within quoted code unless that quoted code is itself + part of the name of a code chunk. + + In code, noweb treats unpaired double left or right angle brackets as + literal <<<< and >>>>. To force any such brackets, even paired brackets or + brackets in documentation, to be treated as literal, use a preceding at + sign (e.g. @@<<<<). + + Some programming or formatting languages may require a single @@ sign in + the first column. Noweb users may achieve this effect by putting a + doubled @@@@ in the first column; in this position only, it stands for a + single @@ sign. + +TTAANNGGLLIINNGG + _n_o_t_a_n_g_l_e and _n_o_u_n_t_a_n_g_l_e accept the same set of options, although some + options have effects only on one or the other. The options are: + + --RR_n_a_m_e Expand the <<<<_n_a_m_e>>>> code chunk. The --RR option can be repeated, + in which case each chunk is written to the output. If no --RR + option is given, expand the chunk named <<<<*>>>>. + + --LL_f_o_r_m_a_t + Emit line number indications at chunk boundaries. A line number + indication identifies the source of the line that follows it. + In _f_o_r_m_a_t, %%FF indicates the name of the source file, %%LL indi†+ cates the line number of the source file, %%NN indicates a new†+ line, and %%%% indicates a percent sign. A sign and digit may be + inserted between the percent sign and the `LL', in which case the + line number will be adjusted by that amount. If _f_o_r_m_a_t is omit†+ ted, the default format is that accepted by the C preprocessor: + `##lliinnee %%LL ""%%FF""%%NN'. When using the --LL_f_o_r_m_a_t option, _n_o_t_a_n_g_l_e + ensures that all text appears in the same column in input and + output. _n_o_u_n_t_a_n_g_l_e ignores this option. + + Common format strings include: + C --LL''##lliinnee %%LL ""%%FF""%%NN'' + Sun FORTRAN --LL''\\## %%LL ""%%FF""%%NN'' + Icon --LL''##lliinnee %%--11LL ""%%FF""%%NN'' + Modula-3 --LL''<<**LLIINNEE %%LL ""%%FF"" **>>%%NN'' + SML/NJ --LL''((**##lliinnee %%LL ""%%FF""**))'' + + To solve the converse problem, that is, to get noweb to do some†+ thing sensible with ##lliinnee in its input, see the sshhaarrpplliinnee filter + in the examples directory. + + --tt_k Copy tabs untouched from input to output, and use tabs for + indentation, assuming stops every _k columns. By default, tabs + are expanded to spaces with stops every 8 columns. + + --ffiilltteerr _c_m_d + Filter the _n_o_w_e_b source through _c_m_d after converting it to tool + form and before tangling. _n_o_t_a_n_g_l_e looks for _c_m_d first on the + user's PPAATTHH, then in ||LLIIBBDDIIRR||. Such filters can be used to add + features to _n_o_t_a_n_g_l_e; for an example see ||LLIIBBDDIIRR||//eemmppttyyddeeffnn. + For experts only. + + --mmaarrkkuupp _p_a_r_s_e_r + Use _p_a_r_s_e_r to parse the input file. Enables use of noweb tools + on files in other formats; for example, the nnuummaarrkkuupp parser + understands _n_u_w_e_b(1) format. See _n_o_w_e_b_f_i_l_t_e_r_s(7) for more + information. For experts only. + + --aawwkk || --cc || --iiccnn || --iiccoonn || --mmll || --mm33 || --ppaassccaall || --ff7777 || --ff9900 || --tteexx + When _n_o_u_n_t_a_n_g_l_e transforms documentation chunks into comments, + use the comment format of the language named. --cc is the + default. _n_o_t_a_n_g_l_e ignores these options. + + --ww_n When _n_o_u_n_t_a_n_g_l_e transforms documentation chunks into comments, + create comments on lines of width _n. _n_o_t_a_n_g_l_e ignores this + option. + +WWEEAAVVIINNGG + Output from _n_o_w_e_a_v_e can be used in _T_e_X documents that \\iinnppuutt nnwwmmaacc,, in + _L_a_T_e_X documents that use the nnoowweebb package (see _n_o_w_e_b_s_t_y_l_e_(_1_)), and in + _H_T_M_L documents to be browsed with _M_o_s_a_i_c_(_1_)_. _N_o_w_e_a_v_e treats code + chunks somewhat like _L_a_T_e_X _l_i_s_t _e_n_v_i_r_o_n_m_e_n_t_s_. If the ``@@ '' that ter†+ minates a code chunk is followed immediately by text, that text follows + the code chunk without a paragraph break. If the rest of the line is + blank, _n_o_w_e_a_v_e puts _T_e_X into ``vertical mode,'' and later text starts a + fresh, indented paragraph. + + No page breaks occur in the middle of code chunks unless necessary to + avoid an overfull vbox. The documentation chunk immediately preceding + a code chunk appears on the same page as that code chunk unless doing + so would violate the previous rule. + + _N_o_w_e_a_v_e inserts no extra newlines in its _T_e_X output, so the line num†+ bers given in _T_e_X error messages are the same as those in the input + file. + + _n_o_w_e_a_v_e has options that dictate choice of formatter and that support + different formatting idioms and tools. Basic options are described + here; options related to index and cross-reference information are + described in the INDEXING AND CROSS-REFERENCE section. + + --llaatteexx Emit LaTeX, including wrapper in aarrttiiccllee style with the nnoowweebb + package and page style. (Default) + + --tteexx Emit plain TeX, including wrapper with nnwwmmaacc macros. + + --hhttmmll Emit HTML, using HTML wrapper. The output is uninteresting + without --iinnddeexx or --xx. The tags <<nnoowweebbcchhuunnkkss>> and <<nnoowweebbiinnddeexx>>, + on lines by themselves, produce a list of chunks and an index of + identifiers, respectively. If these tags are not present, the + list and index are placed at the end of the file. + + --llaatteexx++hhttmmll + Assume documentation chunks are LaTeX, but generate HTML for + code chunks, suitably marked so conversion with _l_a_t_e_x_2_h_t_m_l_(_1_) + yields reasonable output. A LaTeX wrapper is implied, but can + be turned off with --nn. _U_s_e _o_f _t_h_i_s _o_p_t_i_o_n _i_s ddeepprreeccaatteedd;; use + --hhttmmll with --ffiilltteerr ll22hh instead. + + --ttrrooffff Emit _t_r_o_f_f(1) markup (with no wrapper). The result should be + processed with _n_o_r_o_f_f(1). Bug reports for --ttrrooffff to Arnold Rob†+ bins <<aarrnnoolldd@@sskkeeeevvee..ccoomm>>.. + + --nn Don't use any wrapper (header or trailer). This option is use†+ ful when _n_o_w_e_a_v_e's output will be a part of a larger document. + See also --ddeellaayy.. + + --ffiilltteerr _c_m_d + Filters the _n_o_w_e_b source through _c_m_d after converting it to tool + form and before converting to _T_e_X_. _n_o_w_e_a_v_e looks for _c_m_d first + on the user's PPAATTHH,, then in ||LLIIBBDDIIRR||.. Such filters can be used + to add features to _n_o_w_e_a_v_e_; for an example, see ||LLIIBBâ€â€ + DDIIRR||//nnooxxrreeff..kkrroomm.. _N_o_w_e_a_v_e supports up to four filters; one can + get more by shell trickery, for example, --ffiilltteerr ""iiccoonn..ffiilltteerr || + nnooiiddxx"". The --aauuttooddeeffss, --xx, --iinnddeexx, and --iinnddeexxffrroomm options are + implemented as filters. Filters are executed with the shell's + eevvaall command, so _c_m_d should be quoted accordingly. + + --mmaarrkkuupp _p_a_r_s_e_r + Use _p_a_r_s_e_r to parse the input file. Enables use of noweb tools + on files in other formats; for example, the nnuummaarrkkuupp parser + understands _n_u_w_e_b(1) format. See _n_o_w_e_b_f_i_l_t_e_r_s(7) for more + information. For experts only. + + --ooppttiioonn _o_p_t + Adds \\nnoowweebbooppttiioonnss{{_o_p_t}} to the _L_a_T_e_X header. See _n_o_w_e_b_s_t_y_l_e_(_1_) + for values of _o_p_t_. Normally useful only with the --llaatteexx option, + but --ooppttiioonn lloonnggxxrreeff works black magic with --hhttmmll.. + + --ddeellaayy By default, _n_o_w_e_a_v_e puts file-name and other information into + the output before the first chunk of the program. --ddeellaayy delays + that information until after the first documentation chunk, mak†+ ing act a little bit like the _W_E_B ``limbo.'' The option is typ†+ ically used to enable a user to put a specialized _L_a_T_e_X \\ddooccuuâ€â€ + mmeennttccllaassss command and other preamble material in the first docu†+ mentation chunk (i.e., _b_e_f_o_r_e the first @ sign). This option + also forces trailing cross-referencing information to be emitted + just before the final chunk, instead of at the end of the docu†+ ment; the final chunk is expected to contain \\eenndd{{ddooccuummeenntt}}.. + The --ddeellaayy option implies the --nn option. + + --tt_k Expand tabs with stops every _k columns. (Default is to expand + every 8 columns.) + + --tt Copy tabs to the output. + + --vv Print the pipeline and RCS info on standard error. + +IINNDDEEXXIINNGG AANNDD CCRROOSSSS--RREEFFEERREENNCCEE + When used with _L_a_T_e_X, _t_r_o_f_f, or _H_T_M_L_, _n_o_w_e_a_v_e can provide indexing and + cross-reference information for chunks and for programming-language + identifiers. Identifier definitions may be marked by hand using back†+ ticks (`); the --ffiilltteerr bbttddeeffnn option recognizes these markings. For + some languages, definitions may be found automatically using the + --aauuttooddeeffss option. This section describes the indexing and cross-refer†+ ence options; it might well be skipped on first reading. + + --xx For _L_a_T_e_X_, add a page number to each chunk name identifying the + location of that chunk's definition, and emit cross-reference + information relating definitions and uses. For _H_T_M_L_, create + hypertext links between uses and definitions of chunks. When + nnoowweeaavvee --xx is used with _L_a_T_e_X_, the control sequence \\nnoowweebbcchhuunnkkss + expands to a sorted list of all code chunks. + + --iinnddeexx Build cross-reference information (or hypertext links) for + defined identifiers. Definitions are those found in the input + files by --aauuttooddeeffss _l_a_n_g_u_a_g_e or by --ffiilltteerrbtdefn. Requires _L_a_T_e_X + or _H_T_M_L_. --iinnddeexx implies --xx;; including both will generate + strange-looking output. _n_o_w_e_a_v_e does not generate cross-refer†+ ences to identifiers that appear in quoted code (@@[[[[...@@]]]]), but + it does generate hypertext links. When nnoowweeaavvee --iinnddeexx is used + with _L_a_T_e_X_, the control sequence \\nnoowweebbiinnddeexx expands to an index + of identifiers. + + --iinnddeexxffrroomm _i_n_d_e_x + Like --iinnddeexx,, but the identifiers to be indexed are taken from + file _i_n_d_e_x. See _n_o_i_n_d_e_x_(_1_)_. + + --aauuttooddeeffss _l_a_n_g + Discover identifier definitions automatically. Code in chunks + must be in language _l_a_n_g. Permissible _l_a_n_gs vary but may + include tteexx or iiccoonn.. Useless without --iinnddeexx,, which it must pre†+ cede. + + --sshhoowwaauuttooddeeffss + Show values of _l_a_n_g usable with --aauuttooddeeffss. + +EERRRROORR MMEESSSSAAGGEESS + If _n_o_t_a_n_g_l_e or _n_o_w_e_a_v_e encounters a chunk name within documentation, it + assumes that this indicates an error, usually misspelling + ``<<name>>=''. Other error messages should be self-explanatory. + + It is incorrect to refer to a chunk that is never defined, but it is OK + for chunks to be defined and not used. + +EEXXAAMMPPLLEESS + If you have trouble digesting this man page, you're not alone. Here + are a few examples to get you started. I'll assume you have a ffoooo..nnww + file with a C program in chunk <<<<ffoooo..cc>>>> and a header file in chunk + <<<<ffoooo..hh>>>>, and that your documentation is marked up using _l_a_t_e_x_(_1_). + I'll show you how to build things using the most common options. + + To rebuild your C source, try + nnoottaannggllee --LL --RRffoooo..cc ffoooo..nnww >> ffoooo..cc + To rebuild your header file, try + nnoottaannggllee --RRffoooo..hh ffoooo..nnww || ccppiiff ffoooo..hh + There are two compromises here. Omitting --LL keeps ##lliinnee out of your + header file, and using ccppiiff prevents the command from rewriting ffoooo..hh + unless the contents have changed. Thus, this is good code to put in a + Makefile rule. + + To build a printed document, run + nnoowweeaavvee --aauuttooddeeffss cc --iinnddeexx ffoooo..nnww >> ffoooo..tteexx + If you have your own preamble, containing \\ddooccuummeennttccllaassss and all, you + will also need the --ddeellaayy option. + + To build a web page, run + nnoowweeaavvee --ffiilltteerr ll22hh --aauuttooddeeffss cc --iinnddeexx --hhttmmll ffoooo..nnww || hhttmmllttoocc >> + ffoooo..hhttmmll + Have fun! + +FFIILLEESS + |LIBDIR|/markup markup preprocessor + |LIBDIR|/unmarkup inverts markup + |LIBDIR|/nt notangle proper + |LIBDIR|/finduses find uses of identifiers for index + |LIBDIR|/noidx generate index and cross-reference info + |LIBDIR|/toroff back end to emit _t_r_o_f_f + |LIBDIR|/totex back end to emit _T_e_X or _L_a_T_e_X + |LIBDIR|/tohtml back end to emit HTML + |TEXINPUTS|/nwmac.tex formatting _T_e_X macros + |TEXINPUTS|/noweb.sty use in _L_a_T_e_X documents; see _n_o_w_e_b_s_t_y_l_e_(_7_) + +SSEEEE AALLSSOO + _c_p_i_f(1), _n_o_d_e_f_s(1), _n_o_r_o_o_t_s(1), _n_o_w_e_b(1), _n_o_i_n_d_e_x(1), _n_o_r_o_f_f(1), _n_o_w_e_b_†+ _s_t_y_l_e(7), _n_o_w_e_b_f_i_l_t_e_r_s(7) + +BBUUGGSS + _n_o_t_a_n_g_l_e and _n_o_u_n_t_a_n_g_l_e fail if names used on the command line contain + single quotes. + + Ignoring unused chunks can cause problems; if a chunk has multiple def†+ initions and one is misspelled, the misspelled definition is silently + ignored. _n_o_r_o_o_t_s(1) can be used to catch this mistake. + + + + + + + + + + + + + + + + + + + + + + The _-_L option of _n_o_t_a_n_g_l_e puts an implicit initial newline in the for†+ mat string. + + The default _L_a_T_e_X pagestyles don't set the width of the boxes contain†+ ing headers and footers. Since _n_o_w_e_b code paragraphs are extra wide, + this _L_a_T_e_X bug sometimes results in extra-wide headers and footers. + The remedy is to redefine the relevant ppss@@** commands; ppss@@nnoowweebb in + nnoowweebb..ssttyy can be used as an example. + + _l_a_t_e_x_2_h_t_m_l(1) mangles some source files. + + _n_o_w_e_a_v_e has too many options, and this man page is too long. + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/noweb.1 b/web/noweb/src/xdoc/noweb.1 new file mode 100644 index 0000000000..44f2847eee --- /dev/null +++ b/web/noweb/src/xdoc/noweb.1 @@ -0,0 +1,221 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +noweb \- a simple literate-programming tool +.SH SYNOPSIS +.B noweb +[\fB-t\fP] +[\fB-o\fP] +[\fB-L\fP\fIformat\fP] +[\fB-markup\fP \fIparser\fP] +[file] ... +.SH DESCRIPTION +.I Noweb +is a literate-programming tool like +.I FunnelWEB +or +.IR nuweb , +only simpler. +A +.I noweb +file contains program source code interleaved with documentation. +When +.I noweb +is invoked, it writes the program source code +to the output files mentioned in the noweb file, and it writes a +.I TeX +file for typeset documentation. +.PP +The +.IR noweb (1) +command is for people who don't like reading man pages or who are switching from +.IR nuweb . +To get the most out of +.IR noweb , +use +.IR notangle (1) +and +.IR noweave (1) +instead. +.SH FORMAT OF NOWEB FILES +A +.I noweb +file is a sequence of +.IR chunks , +which may appear in any order. +A chunk may contain code or documentation. +Documentation chunks begin with a line that starts with an at sign (@) +followed by a space or newline. +They have no names. +Code chunks begin with +.br +<<\fIchunk name\fP>>= +.br +on a line by itself. +The double left angle bracket (<<) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not mark the beginning of a +chunk, it is assumed to be the first line of a documentation chunk. +.PP +Documentation chunks contain text that is copied verbatim to the +.I TeX +file (except for quoted code). +.I noweb +works with \fILaTeX\fP; the first documentation chunk must contain +a +.I LaTeX +.B "\\\\documentclass" +command, it must contain +.B "\\\\usepackage{noweb}" +in the preamble, and finally +it must also contain a +.I LaTeX +.B "\\\\begin{document}" +command. +.PP +Code chunks contain program source code and references to other code +chunks. +Several code chunks may have the same name; +.I noweb +concatenates their definitions to produce a single chunk, just as +other literate-programming tools do. +.I noweb +looks for chunks that are defined but not used in the source file. +If the name of such a chunk contains no spaces, the chunk is +an ``output file;'' +.I noweb +expands it and writes the result onto the file of the same name. +A code-chunk definition is like a macro definition; +it contains references to other chunks, which are +themselves expanded, and so on. +\fInoweb\fP's output is readable; it preserves the indentation of expanded +chunks with respect to the chunks in which they appear. +.PP +If a star (\fB*\fP) is appended to the name of an output file, +.I noweb +includes line-number information as specified by the \fB\-L\fP\fIformat\fP +option (or for C if no \fB\-L\fP\fIformat\fP option is given). +The name itself may not contain shell metacharacters. +.PP +Code may be quoted within documentation +chunks by placing double square brackets +(\fB[[\fI...\fB]]\fR) around it. +These double square brackets are used to +give the code special typographic treatment in the +.I TeX +file. +If quoted code ends with three or more square brackets, +.I noweb +chooses the rightmost pair, so that, for example, \fB[[a[i]]]\fR is +parsed correctly. +.PP +In code, noweb treats unpaired +double left or right angle brackets as literal \fB<<\fP and \fB>>\fP. +To force any +such brackets, even paired brackets or brackets in documentation, +to be treated as literal, use a preceding at sign (e.g. \fB@<<\fP). +.SH OPTIONS +.TP +.B \-t +Suppress generation of a +.I TeX +file. +.TP +.B \-o +Suppress generation of output files. +.TP +.B \-L\fIformat\fR +Use +.I format +to format line-number information for starred output files. +(If the option is omitted, a format suitable for C is used.) +.I format +is as defined by +.IR notangle (1); +.TP +.B "\-markup \fIparser\fP" +Use +.I parser +to parse the input file. +Enables use of noweb tools on files in other formats; +for example, the +.B numarkup +parser understands +.IR nuweb (1) +format. +See +.IR nowebfilters (7) +for more information. +For experts only. +.SH BUGS +.PP +Ignoring unused chunks whose names contain spaces +sometimes causes problems, especially in the case when a chunk has +multiple definitions and one is misspelled; +the misspelled definition will be silently ignored. +.IR noroots (1) +can be used as a sanity checker to catch this sort of mistake. +.PP +.I noweb +is intended for users who don't want the power or the complexity of +command-line options. +More sophisticated users should avoid +.I noweb +and use +.I noweave +and +.I notangle +instead. +If the design were better, we could all use the same commands. +.PP +.I noweb +requires the new version of +.IR awk , +assumed to be called +.IR nawk . +DEC +.I nawk +has a bug in that that causes problems with braces in +.I TeX +output. +GNU +.I gawk +is reported to work. +.PP +The default +.I LaTeX +pagestyles don't set the width of the boxes containing headers and footers. +Since +.I noweb +code paragraphs are extra wide, this +.I LaTeX +bug sometimes results in extra-wide headers and footers. +The remedy is to redefine the relevant +.B ps@* +commands; +.B ps@noweb +in +.B noweb.sty +can be used as an example. +.SH SEE ALSO +.PP +.IR notangle (1), +.IR noweave (1), +.IR noroots (1), +.IR nountangle (1), +.IR nowebstyle (7), +.IR nowebfilters (7), +.IR nuweb2noweb (1) +.br +Norman Ramsey, +.I "Literate programming simplified, IEEE Software" +11(5):97-105, September 1994. +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/noweb.txt b/web/noweb/src/xdoc/noweb.txt new file mode 100644 index 0000000000..3b9964fe4c --- /dev/null +++ b/web/noweb/src/xdoc/noweb.txt @@ -0,0 +1,123 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + noweb - a simple literate-programming tool + +SSYYNNOOPPSSIISS + nnoowweebb [--tt] [--oo] [--LL_f_o_r_m_a_t] [--mmaarrkkuupp _p_a_r_s_e_r] [file] ... + +DDEESSCCRRIIPPTTIIOONN + _N_o_w_e_b is a literate-programming tool like _F_u_n_n_e_l_W_E_B or _n_u_w_e_b, only sim†+ pler. A _n_o_w_e_b file contains program source code interleaved with docu†+ mentation. When _n_o_w_e_b is invoked, it writes the program source code to + the output files mentioned in the noweb file, and it writes a _T_e_X file + for typeset documentation. + + The _n_o_w_e_b(1) command is for people who don't like reading man pages or + who are switching from _n_u_w_e_b. To get the most out of _n_o_w_e_b, use _n_o_t_a_n_†+ _g_l_e(1) and _n_o_w_e_a_v_e(1) instead. + +FFOORRMMAATT OOFF NNOOWWEEBB FFIILLEESS + A _n_o_w_e_b file is a sequence of _c_h_u_n_k_s, which may appear in any order. A + chunk may contain code or documentation. Documentation chunks begin + with a line that starts with an at sign (@) followed by a space or new†+ line. They have no names. Code chunks begin with + <<_c_h_u_n_k _n_a_m_e>>= + on a line by itself. The double left angle bracket (<<) must be in the + first column. Chunks are terminated by the beginning of another chunk, + or by end of file. If the first line in the file does not mark the + beginning of a chunk, it is assumed to be the first line of a documen†+ tation chunk. + + Documentation chunks contain text that is copied verbatim to the _T_e_X + file (except for quoted code). _n_o_w_e_b works with _L_a_T_e_X; the first docu†+ mentation chunk must contain a _L_a_T_e_X \\ddooccuummeennttccllaassss command, it must + contain \\uusseeppaacckkaaggee{{nnoowweebb}} in the preamble, and finally it must also + contain a _L_a_T_e_X \\bbeeggiinn{{ddooccuummeenntt}} command. + + Code chunks contain program source code and references to other code + chunks. Several code chunks may have the same name; _n_o_w_e_b concatenates + their definitions to produce a single chunk, just as other literate- + programming tools do. _n_o_w_e_b looks for chunks that are defined but not + used in the source file. If the name of such a chunk contains no spa†+ ces, the chunk is an ``output file;'' _n_o_w_e_b expands it and writes the + result onto the file of the same name. A code-chunk definition is like + a macro definition; it contains references to other chunks, which are + themselves expanded, and so on. _n_o_w_e_b's output is readable; it pre†+ serves the indentation of expanded chunks with respect to the chunks in + which they appear. + + If a star (**) is appended to the name of an output file, _n_o_w_e_b includes + line-number information as specified by the --LL_f_o_r_m_a_t option (or for C + if no --LL_f_o_r_m_a_t option is given). The name itself may not contain shell + metacharacters. + + Code may be quoted within documentation chunks by placing double square + brackets ([[[[_._._.]]]]) around it. These double square brackets are used to + give the code special typographic treatment in the _T_e_X file. If quoted + code ends with three or more square brackets, _n_o_w_e_b chooses the right†+ most pair, so that, for example, [[[[aa[[ii]]]]]] is parsed correctly. + + In code, noweb treats unpaired double left or right angle brackets as + literal <<<< and >>>>. To force any such brackets, even paired brackets or + brackets in documentation, to be treated as literal, use a preceding at + sign (e.g. @@<<<<). + +OOPPTTIIOONNSS + --tt Suppress generation of a _T_e_X file. + + --oo Suppress generation of output files. + + --LL_f_o_r_m_a_t + Use _f_o_r_m_a_t to format line-number information for starred output + files. (If the option is omitted, a format suitable for C is + used.) _f_o_r_m_a_t is as defined by _n_o_t_a_n_g_l_e(1); + + --mmaarrkkuupp _p_a_r_s_e_r + Use _p_a_r_s_e_r to parse the input file. Enables use of noweb tools + on files in other formats; for example, the nnuummaarrkkuupp parser + understands _n_u_w_e_b(1) format. See _n_o_w_e_b_f_i_l_t_e_r_s(7) for more + information. For experts only. + +BBUUGGSS + Ignoring unused chunks whose names contain spaces sometimes causes + problems, especially in the case when a chunk has multiple definitions + and one is misspelled; the misspelled definition will be silently + ignored. _n_o_r_o_o_t_s(1) can be used as a sanity checker to catch this sort + of mistake. + + _n_o_w_e_b is intended for users who don't want the power or the complexity + of command-line options. More sophisticated users should avoid _n_o_w_e_b + and use _n_o_w_e_a_v_e and _n_o_t_a_n_g_l_e instead. If the design were better, we + could all use the same commands. + + _n_o_w_e_b requires the new version of _a_w_k, assumed to be called _n_a_w_k. DEC + _n_a_w_k has a bug in that that causes problems with braces in _T_e_X output. + GNU _g_a_w_k is reported to work. + + The default _L_a_T_e_X pagestyles don't set the width of the boxes contain†+ ing headers and footers. Since _n_o_w_e_b code paragraphs are extra wide, + this _L_a_T_e_X bug sometimes results in extra-wide headers and footers. + The remedy is to redefine the relevant ppss@@** commands; ppss@@nnoowweebb in + nnoowweebb..ssttyy can be used as an example. + +SSEEEE AALLSSOO + _n_o_t_a_n_g_l_e(1), _n_o_w_e_a_v_e(1), _n_o_r_o_o_t_s(1), _n_o_u_n_t_a_n_g_l_e(1), _n_o_w_e_b_s_t_y_l_e(7), + _n_o_w_e_b_f_i_l_t_e_r_s(7), _n_u_w_e_b_2_n_o_w_e_b(1) + Norman Ramsey, _L_i_t_e_r_a_t_e _p_r_o_g_r_a_m_m_i_n_g _s_i_m_p_l_i_f_i_e_d_, _I_E_E_E _S_o_f_t_w_a_r_e + 11(5):97-105, September 1994. + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/nowebfilters.7 b/web/noweb/src/xdoc/nowebfilters.7 new file mode 100644 index 0000000000..f15261134c --- /dev/null +++ b/web/noweb/src/xdoc/nowebfilters.7 @@ -0,0 +1,211 @@ +.TH NOWEB 7 "local 10/40/2008" +.SH NAME +nowebfilters \- filters and parsers for use with noweb +.SH SYNOPSIS +[\fBnoweave\fP|\fBnotangle\fP|\fBnoweb\fP] +[\fB\-filter\fP \fIfilter\fP|\fB\-markup\fP \fIparser\fP] ... +.SH DESCRIPTION +This man page documents the various filters and parsers that are +supplied with +.IR noweb (1). +A filter, which transforms the pipeline representation, is used with the +.B \-filter +option. +A parser, which reads a source file and emits the pipeline representation, +is used with the +.B \-markup +option. +Not all filters and parsers are available at all installations. +.SH FILTERS +.TP +.B autodefs.* +.I noweave's +.B \-autodefs +and +.B \-showautodefs +options use these filters, which automatically find defined identifiers. +The +.B * +stands for the name of a programming language. +(Icon installation only.) +.TP +.B btdefn +The +.B btdefn +filter uses Dave Hanson's convention to mark definitions. +In code chunks, each defining instances of an identifier should be preceded by +a single backtick character (ASCII 140). +The +.B btdefn +filter removes these backticks from the code and converts them to definitions, +thus: +.BR "`definition". +It, not +.BR "@ %def" , +should be used with both +.IR notangle (1) +and +.IR noweave (1), +at least for programs written in C-like languages. +.TP +.B disambiguate +The +.B disambiguate +filter makes it possible to abbreviate chunk names using a trailing ellipsis +(three periods), in the style of WEB and +.IR nuweb (1). +.TP +.BI docs2comments " options" +The +.B docs2comments +filter converts documentation chunks to comments in code chunks. +Options include +.BR "\-all" , +which converts all documentation chunks into comments; +.B "\-one" +converts only one documentation chunk preceding each code chunk. +The +.BI "\-w" k +option formats comments +.I k +columns wide. +.IP +Other options control the language and style of the comment. +Languages available include +.BR "\-awk" , +.BR "\-c" , +.BR "\-c++" , +.BR "\-f77" , +.BR "\-f90" , +.BR "\-icn" , +.BR "\-icon" , +.BR "\-lisp" , +.BR "\-m3" , +.BR "\-ml" , +.BR "\-ocamlweb" , +.BR "\-pascal" , +.BR "\-scm" , +and +.BR "\-tex" . +.IP +Norman recommends using +.BR notangle (1) +with +.B docstocomments +instead of +.BR nountangle (1). +(Icon installation only.) +.TP +.B elide \fIpatterns\fP +The +.B elide +filter +removes from the stream any code chunk matching +any of the arguments. +Each argument is a pattern in which +.B ? +matches any single character and +.B * matches any sequence of characters. +.TP +.B emptydefn +The +.B emptydefn +filter makes an empty definition (\fB<<>>=\fP) an abbreviation for +a continuation of the previous definition. +.TP +\fBl2h\fP [\fB\-show\-unknowns\fP] [\fB\-html\-quotes\fP] +The +.B l2h +filter converts LaTeX to HTML in documentation chunks. +If the \fB\-show\-unknowns\fP options is given, unrecognized LaTeX control +sequences appear in bold in the output. +If the \fB\-html\-quotes\fP option is given, +.B l2h +converts LaTeX double-quote ligatures into HTML 4 double-quote entities. +.B l2h +uses the LaTeX-generated .toc and .bbl files to help create table of contents +and bibliography. +(Icon installation only.) +.TP +.B pipedocs\fI command\fP +The +.B pipedocs +filter runs each documentation chunk through an invocation of +.IR command , +which may be any Unix command. +The Noweb library includes the command +.BR h2a , +which uses +.IR lynx (1) +to convert HTML to +ASCII. +.TP +.BI xchunks " files" +The +.B xchunks +filter pulls in code chunks from an external source. +The +.I files +argument lists one or more noweb files, which are used only for their +definitions of code chunks. +Empty chunk definitions in the main document are replaced with +the external definitions of the same name. +This filter makes it possible to include the same code chunks +in multiple documents, or even the same code chunks multiple +times in the same document. +(Icon installation only.) +.SH PARSERS +A parser converts a source file to the noweb pipeline format, +interpreting the ``markup'' in that file. +By default, the tools use the standard noweb markup, but some installations +may support nuweb markup as well. +Parsers include +.TP +.B markup +Converts standard +.IR noweb (1) +markup to pipeline format. +.TP +.B numarkup +Converts +.IR nuweb (1) +markup to noweb pipeline format. +It is not part of the standard installation, but it can be found in \fInoweb\fP's +.B contrib/norman +directory. +.SH FILES +All filters and parsers are stored in +.B |LIBDIR| +.SH BUGS +It is possible to tell +.B l2h +about new control sequences by putting special TeX comments in one's +documentation chunks, but there's no documentation. +Use the source, Luke. +.PP +.B numarkup +loses information, e.g., about the proper treatment of tabs. +It also doesn't compensate for \fInotangle\fP's newline-removal +heuristic, which means results on +.I nuweb +files may differ subtly from those produced by +.I nuweb. +.PP +Many filters are not available in installations that use Awk instead of Icon. +.SH SEE ALSO +.IR noweave (1), +.IR notangle (1), +.IR noweb (1) +.br +The +.I "Noweb Hacker's Guide" +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/nowebfilters.nw b/web/noweb/src/xdoc/nowebfilters.nw new file mode 100644 index 0000000000..14b2bb49fe --- /dev/null +++ b/web/noweb/src/xdoc/nowebfilters.nw @@ -0,0 +1,203 @@ +<<*>>= +.TH NOWEB 7 "local <<noweb documentation date>>" +.SH NAME +nowebfilters \- filters and parsers for use with noweb +.SH SYNOPSIS +[\fBnoweave\fP|\fBnotangle\fP|\fBnoweb\fP] +[\fB\-filter\fP \fIfilter\fP|\fB\-markup\fP \fIparser\fP] ... +.SH DESCRIPTION +This man page documents the various filters and parsers that are +supplied with +.IR noweb (1). +A filter, which transforms the pipeline representation, is used with the +.B \-filter +option. +A parser, which reads a source file and emits the pipeline representation, +is used with the +.B \-markup +option. +Not all filters and parsers are available at all installations. +.SH FILTERS +.TP +.B autodefs.* +.I noweave's +.B \-autodefs +and +.B \-showautodefs +options use these filters, which automatically find defined identifiers. +The +.B * +stands for the name of a programming language. +(Icon installation only.) +.TP +.B btdefn +The +.B btdefn +filter uses Dave Hanson's convention to mark definitions. +In code chunks, each defining instances of an identifier should be preceded by +a single backtick character (ASCII 140). +The +.B btdefn +filter removes these backticks from the code and converts them to definitions, +thus: +.BR "`definition". +It, not +.BR "@ %def" , +should be used with both +.IR notangle (1) +and +.IR noweave (1), +at least for programs written in C-like languages. +.TP +.B disambiguate +The +.B disambiguate +filter makes it possible to abbreviate chunk names using a trailing ellipsis +(three periods), in the style of WEB and +.IR nuweb (1). +.TP +.BI docs2comments " options" +The +.B docs2comments +filter converts documentation chunks to comments in code chunks. +Options include +.BR "\-all" , +which converts all documentation chunks into comments; +.B "\-one" +converts only one documentation chunk preceding each code chunk. +The +.BI "\-w" k +option formats comments +.I k +columns wide. +.IP +Other options control the language and style of the comment. +Languages available include +.BR "\-awk" , +.BR "\-c" , +.BR "\-c++" , +.BR "\-f77" , +.BR "\-f90" , +.BR "\-icn" , +.BR "\-icon" , +.BR "\-lisp" , +.BR "\-m3" , +.BR "\-ml" , +.BR "\-ocamlweb" , +.BR "\-pascal" , +.BR "\-scm" , +and +.BR "\-tex" . +.IP +Norman recommends using +.BR notangle (1) +with +.B docstocomments +instead of +.BR nountangle (1). +(Icon installation only.) +.TP +.B elide \fIpatterns\fP +The +.B elide +filter +removes from the stream any code chunk matching +any of the arguments. +Each argument is a pattern in which +.B ? +matches any single character and +.B * matches any sequence of characters. +.TP +.B emptydefn +The +.B emptydefn +filter makes an empty definition (\fB@<<@>>=\fP) an abbreviation for +a continuation of the previous definition. +.TP +\fBl2h\fP [\fB\-show\-unknowns\fP] [\fB\-html\-quotes\fP] +The +.B l2h +filter converts LaTeX to HTML in documentation chunks. +If the \fB\-show\-unknowns\fP options is given, unrecognized LaTeX control +sequences appear in bold in the output. +If the \fB\-html\-quotes\fP option is given, +.B l2h +converts LaTeX double-quote ligatures into HTML 4 double-quote entities. +.B l2h +uses the LaTeX-generated .toc and .bbl files to help create table of contents +and bibliography. +(Icon installation only.) +.TP +.B pipedocs\fI command\fP +The +.B pipedocs +filter runs each documentation chunk through an invocation of +.IR command , +which may be any Unix command. +The Noweb library includes the command +.BR h2a , +which uses +.IR lynx (1) +to convert HTML to +ASCII. +.TP +.BI xchunks " files" +The +.B xchunks +filter pulls in code chunks from an external source. +The +.I files +argument lists one or more noweb files, which are used only for their +definitions of code chunks. +Empty chunk definitions in the main document are replaced with +the external definitions of the same name. +This filter makes it possible to include the same code chunks +in multiple documents, or even the same code chunks multiple +times in the same document. +(Icon installation only.) +.SH PARSERS +A parser converts a source file to the noweb pipeline format, +interpreting the ``markup'' in that file. +By default, the tools use the standard noweb markup, but some installations +may support nuweb markup as well. +Parsers include +.TP +.B markup +Converts standard +.IR noweb (1) +markup to pipeline format. +.TP +.B numarkup +Converts +.IR nuweb (1) +markup to noweb pipeline format. +It is not part of the standard installation, but it can be found in \fInoweb\fP's +.B contrib/norman +directory. +.SH FILES +All filters and parsers are stored in +.B |LIBDIR| +.SH BUGS +It is possible to tell +.B l2h +about new control sequences by putting special TeX comments in one's +documentation chunks, but there's no documentation. +Use the source, Luke. +.PP +.B numarkup +loses information, e.g., about the proper treatment of tabs. +It also doesn't compensate for \fInotangle\fP's newline-removal +heuristic, which means results on +.I nuweb +files may differ subtly from those produced by +.I nuweb. +.PP +Many filters are not available in installations that use Awk instead of Icon. +.SH SEE ALSO +.IR noweave (1), +.IR notangle (1), +.IR noweb (1) +.br +The +.I "Noweb Hacker's Guide" +<<AUTHOR section>> diff --git a/web/noweb/src/xdoc/nowebfilters.txt b/web/noweb/src/xdoc/nowebfilters.txt new file mode 100644 index 0000000000..485674ffdc --- /dev/null +++ b/web/noweb/src/xdoc/nowebfilters.txt @@ -0,0 +1,131 @@ +NOWEB(7) Miscellaneous Information Manual NOWEB(7) + + + +NNAAMMEE + nowebfilters - filters and parsers for use with noweb + +SSYYNNOOPPSSIISS + [nnoowweeaavvee|nnoottaannggllee|nnoowweebb] [--ffiilltteerr _f_i_l_t_e_r|--mmaarrkkuupp _p_a_r_s_e_r] ... + +DDEESSCCRRIIPPTTIIOONN + This man page documents the various filters and parsers that are sup†+ plied with _n_o_w_e_b(1). A filter, which transforms the pipeline represen†+ tation, is used with the --ffiilltteerr option. A parser, which reads a + source file and emits the pipeline representation, is used with the + --mmaarrkkuupp option. Not all filters and parsers are available at all + installations. + +FFIILLTTEERRSS + aauuttooddeeffss..** + _n_o_w_e_a_v_e_'_s --aauuttooddeeffss and --sshhoowwaauuttooddeeffss options use these filters, + which automatically find defined identifiers. The ** stands for + the name of a programming language. (Icon installation only.) + + bbttddeeffnn The bbttddeeffnn filter uses Dave Hanson's convention to mark defini†+ tions. In code chunks, each defining instances of an identifier + should be preceded by a single backtick character (ASCII 140). + The bbttddeeffnn filter removes these backticks from the code and con†+ verts them to definitions, thus: ``ddeeffiinniittiioonn. It, not @@ %%ddeeff, + should be used with both _n_o_t_a_n_g_l_e(1) and _n_o_w_e_a_v_e(1), at least + for programs written in C-like languages. + + ddiissaammbbiigguuaattee + The ddiissaammbbiigguuaattee filter makes it possible to abbreviate chunk + names using a trailing ellipsis (three periods), in the style of + WEB and _n_u_w_e_b(1). + + ddooccss22ccoommmmeennttss _o_p_t_i_o_n_s + The ddooccss22ccoommmmeennttss filter converts documentation chunks to com†+ ments in code chunks. Options include --aallll, which converts all + documentation chunks into comments; --oonnee converts only one docu†+ mentation chunk preceding each code chunk. The --ww_k option for†+ mats comments _k columns wide. + + Other options control the language and style of the comment. + Languages available include --aawwkk, --cc, --cc++++, --ff7777, --ff9900, --iiccnn, + --iiccoonn, --lliisspp, --mm33, --mmll, --ooccaammllwweebb, --ppaassccaall, --ssccmm, and --tteexx. + + Norman recommends using nnoottaannggllee(1) with ddooccssttooccoommmmeennttss instead + of nnoouunnttaannggllee(1). (Icon installation only.) + + eelliiddee _p_a_t_t_e_r_n_s + The eelliiddee filter removes from the stream any code chunk matching + any of the arguments. Each argument is a pattern in which ?? + matches any single character and ** mmaattcchheess aannyy sseeqquueennccee ooff cchhaarrâ€â€ + aacctteerrss.. + + eemmppttyyddeeffnn + The eemmppttyyddeeffnn filter makes an empty definition (<<<<>>>>==) an abbre†+ viation for a continuation of the previous definition. + + ll22hh [--sshhooww--uunnkknnoowwnnss] [--hhttmmll--qquuootteess] + The ll22hh filter converts LaTeX to HTML in documentation chunks. + If the --sshhooww--uunnkknnoowwnnss options is given, unrecognized LaTeX con†+ trol sequences appear in bold in the output. If the + --hhttmmll--qquuootteess option is given, ll22hh converts LaTeX double-quote + ligatures into HTML 4 double-quote entities. ll22hh uses the + LaTeX-generated .toc and .bbl files to help create table of con†+ tents and bibliography. (Icon installation only.) + + ppiippeeddooccss _c_o_m_m_a_n_d + The ppiippeeddooccss filter runs each documentation chunk through an + invocation of _c_o_m_m_a_n_d, which may be any Unix command. The Noweb + library includes the command hh22aa, which uses _l_y_n_x(1) to convert + HTML to ASCII. + + xxcchhuunnkkss _f_i_l_e_s + The xxcchhuunnkkss filter pulls in code chunks from an external source. + The _f_i_l_e_s argument lists one or more noweb files, which are used + only for their definitions of code chunks. Empty chunk defini†+ tions in the main document are replaced with the external defi†+ nitions of the same name. This filter makes it possible to + include the same code chunks in multiple documents, or even the + same code chunks multiple times in the same document. (Icon + installation only.) + +PPAARRSSEERRSS + A parser converts a source file to the noweb pipeline format, inter†+ preting the ``markup'' in that file. By default, the tools use the + standard noweb markup, but some installations may support nuweb markup + as well. Parsers include + + mmaarrkkuupp Converts standard _n_o_w_e_b(1) markup to pipeline format. + + nnuummaarrkkuupp + Converts _n_u_w_e_b(1) markup to noweb pipeline format. It is not + part of the standard installation, but it can be found in + _n_o_w_e_b's ccoonnttrriibb//nnoorrmmaann directory. + +FFIILLEESS + All filters and parsers are stored in ||LLIIBBDDIIRR|| + +BBUUGGSS + It is possible to tell ll22hh about new control sequences by putting spe†+ cial TeX comments in one's documentation chunks, but there's no docu†+ mentation. Use the source, Luke. + + nnuummaarrkkuupp loses information, e.g., about the proper treatment of tabs. + It also doesn't compensate for _n_o_t_a_n_g_l_e's newline-removal heuristic, + which means results on _n_u_w_e_b files may differ subtly from those pro†+ duced by _n_u_w_e_b_. + + Many filters are not available in installations that use Awk instead of + Icon. + +SSEEEE AALLSSOO + _n_o_w_e_a_v_e(1), _n_o_t_a_n_g_l_e(1), _n_o_w_e_b(1) + The _N_o_w_e_b _H_a_c_k_e_r_'_s _G_u_i_d_e + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(7) diff --git a/web/noweb/src/xdoc/nowebstyle.7 b/web/noweb/src/xdoc/nowebstyle.7 new file mode 100644 index 0000000000..fbc8612049 --- /dev/null +++ b/web/noweb/src/xdoc/nowebstyle.7 @@ -0,0 +1,180 @@ +.TH NOWEB 7 "local 10/40/2008" +.SH NAME +nowebstyle \- \fILaTeX\fP package for \fInoweb\fP +.SH SYNOPSIS +.B "\\\\usepackage{noweb}" +.SH DESCRIPTION +When +.I noweave +generates code for +.IR LaTeX , +the +.B noweb +package is required. +In addition to the necessary arcana, it +defines a +.B webcode +environment in which you can place fragments of +code, and in which +only backslash and curly braces have their special meanings; +all other characters are taken literally. +It also defines a +.B noweb +pagestyle. +Finally, it defines a +.B "\\\\noweboptions{...}" +command that can be used to tinker with the appearance of the output. +Options are separated by commas and include: +.TP +.B margintag +Put the sub-page number (tag) of each code-chunk definition in the +left margin. (Default) +.TP +.B nomargintag +Don't use margin tags. +.TP +.B shift +Shift text to the left so that long code lines won't extend +off the right-hand side of the page. +.TP +.B smallcode +Set code in +.I LaTeX +.B "\\\\small" +font instead of +.B "\\\\normalsize." +Similar options exist for all the +.I LaTeX +size-changing commands. +.TP +.B longxref, shortxref, noxref +Choose a style for chunk cross-reference. +Long style uses small paragraphs after each chunk, as in Knuth. +Short style uses symbols on the definition line, as in Hanson. +.B noxref +provides no chunk cross-reference. +Defaults to +.B shortxref. +.TP +.B alphasubpage, numsubpage, nosubpage +Number chunks by the number of the page on which they appear, +followed by an alphabetic (numeric, not used) ``sub-page'' indicator. +Defaults to +.B alphasubpage. +.B nosubpage +implies +.B nomargintag. +.TP +.B webnumbering +Number chunks consecutively, in +.I WEB +style, instead of using sub-page numbers. +.TP +.B subscriptidents, nosubscriptidents, hyperidents, nohyperidents +Controls subscripting of identifiers in code, including quoted code. +Selecting +.B subscriptidents +means an identifier appearing in a code chunk (or in quoted code +within a documentation +chunk) will be subscripted with the chunk number of its definition. +.B hyperidents +means such identifiers will be hyperlinked to their definitions, +provided of course that a hypertext package like +.B hyperref +is loaded. +.B nosubscriptidents +and +.B nohyperidents +are equivalent, and they turn off such markings. +The default is +.B hyperidents. +.TP +.B subscriptquotedidents, nosubscriptquotedidents, hyperquotedidents, nohyperquotedidents +Controls linking of identifiers as above, but applies only to uses of +identifiers +in quoted code. +.TP +.B hideunuseddefs +Omit defined but unused identifiers from +the local identifier cross-reference (Preston Briggs). +.TP +.B noidentxref +Omit the local identifier cross-reference which follows each code chunk. +.TP +.B externalindex +Use an index generated with +.I noindex(1) +(q.v.). +.TP +.B longchunks +When expanding +.B "\\\\nowebchunks," +show page numbers of definitions and uses of each chunk. +.TP +.B english, french, german, portuges, icelandic +Write cross-reference information in the language specified. +Defaults to +.B english. +.SH CONTROL SEQUENCES +The noweb package describes a gazillion special control sequences. +Only a few are listed here. +.PP \" .TP will not work with the backslashes on the next line. Period. +\fB\\nextchunklabel{l}\fP +.RS +Associates label \fBl\fP +with the sub-page reference of the next code chunk. +Can be used in for concise chunk cross-reference with, e.g., +\fBchunk~\\subpageref{l}\fP. +.RE +.PP +.B "\\\\nwanchorto{URL}{anchor text}" +.RS +Creates a link to the given URL with the given anchor text. +Implemented in +.I latex(1) +using footnotes, but +.I sl2h(1) +translates this to +.B "<a href=URL>anchor text</a>" +.RE +.PP +.B "\\\\nwanchorname{name}{anchor text}" +.RS +Creates an anchor point for a hyperlink. +Implemented in +.I latex(1) +using +.B "\\\\label", +but +.I sl2h(1) +translates this to +.B "<a name=name>anchor text</a>" +.RE +.PP +.B "\\\\ifhtml ... \\\\fi" +.RS +Text between +.B "\\\\ifhtml" +and +.B "\\\\fi" +is ignored by +.I latex(1), +but +.I sl2h(1) +and the +.I l2h +noweb filter translate the text into HTML. +.RE +.SH SEE ALSO +.IR noweave (1), +.IR noweb (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/nowebstyle.txt b/web/noweb/src/xdoc/nowebstyle.txt new file mode 100644 index 0000000000..b822675a51 --- /dev/null +++ b/web/noweb/src/xdoc/nowebstyle.txt @@ -0,0 +1,123 @@ +NOWEB(7) Miscellaneous Information Manual NOWEB(7) + + + +NNAAMMEE + nowebstyle - _L_a_T_e_X package for _n_o_w_e_b + +SSYYNNOOPPSSIISS + \\uusseeppaacckkaaggee{{nnoowweebb}} + +DDEESSCCRRIIPPTTIIOONN + When _n_o_w_e_a_v_e generates code for _L_a_T_e_X, the nnoowweebb package is required. + In addition to the necessary arcana, it defines a wweebbccooddee environment + in which you can place fragments of code, and in which only backslash + and curly braces have their special meanings; all other characters are + taken literally. It also defines a nnoowweebb pagestyle. Finally, it + defines a \\nnoowweebbooppttiioonnss{{......}} command that can be used to tinker with + the appearance of the output. Options are separated by commas and + include: + + mmaarrggiinnttaagg + Put the sub-page number (tag) of each code-chunk definition in + the left margin. (Default) + + nnoommaarrggiinnttaagg + Don't use margin tags. + + sshhiifftt Shift text to the left so that long code lines won't extend off + the right-hand side of the page. + + ssmmaallllccooddee + Set code in _L_a_T_e_X \\ssmmaallll font instead of \\nnoorrmmaallssiizzee.. Similar + options exist for all the _L_a_T_e_X size-changing commands. + + lloonnggxxrreeff,, sshhoorrttxxrreeff,, nnooxxrreeff + Choose a style for chunk cross-reference. Long style uses + small paragraphs after each chunk, as in Knuth. Short style + uses symbols on the definition line, as in Hanson. nnooxxrreeff pro†+ vides no chunk cross-reference. Defaults to sshhoorrttxxrreeff.. + + aallpphhaassuubbppaaggee,, nnuummssuubbppaaggee,, nnoossuubbppaaggee + Number chunks by the number of the page on which they appear, + followed by an alphabetic (numeric, not used) ``sub-page'' indi†+ cator. Defaults to aallpphhaassuubbppaaggee.. nnoossuubbppaaggee implies nnoommaarrggiinnâ€â€ + ttaagg.. + + wweebbnnuummbbeerriinngg + Number chunks consecutively, in _W_E_B style, instead of using sub- + page numbers. + + ssuubbssccrriippttiiddeennttss,, nnoossuubbssccrriippttiiddeennttss,, hhyyppeerriiddeennttss,, nnoohhyyppeerriiddeennttss + Controls subscripting of identifiers in code, including quoted + code. Selecting ssuubbssccrriippttiiddeennttss means an identifier appearing + in a code chunk (or in quoted code within a documentation chunk) + will be subscripted with the chunk number of its definition. + hhyyppeerriiddeennttss means such identifiers will be hyperlinked to their + definitions, provided of course that a hypertext package like + hhyyppeerrrreeff is loaded. nnoossuubbssccrriippttiiddeennttss and nnoohhyyppeerriiddeennttss are + equivalent, and they turn off such markings. The default is + hhyyppeerriiddeennttss.. + + ssuubbssccrriippttqquuootteeddiiddeennttss,, nnoossuubbssccrriippttqquuootteeddiiddeennttss,, hhyyppeerrqquuootteeddiiddeennttss,, + nnoohhyyppeerrqquuootteeddiiddeennttss + Controls linking of identifiers as above, but applies only to + uses of identifiers in quoted code. + + hhiiddeeuunnuusseeddddeeffss + Omit defined but unused identifiers from the local identifier + cross-reference (Preston Briggs). + + nnooiiddeennttxxrreeff + Omit the local identifier cross-reference which follows each + code chunk. + + eexxtteerrnnaalliinnddeexx + Use an index generated with _n_o_i_n_d_e_x_(_1_) (q.v.). + + lloonnggcchhuunnkkss + When expanding \\nnoowweebbcchhuunnkkss,, show page numbers of definitions + and uses of each chunk. + + eenngglliisshh,, ffrreenncchh,, ggeerrmmaann,, ppoorrttuuggeess,, iicceellaannddiicc + Write cross-reference information in the language specified. + Defaults to eenngglliisshh.. + +CCOONNTTRROOLL SSEEQQUUEENNCCEESS + The noweb package describes a gazillion special control sequences. + Only a few are listed here. + + \\nneexxttcchhuunnkkllaabbeell{{ll}} + Associates label ll with the sub-page reference of the next code + chunk. Can be used in for concise chunk cross-reference with, + e.g., cchhuunnkk~~\\ssuubbppaaggeerreeff{{ll}}. + + \\nnwwaanncchhoorrttoo{{UURRLL}}{{aanncchhoorr tteexxtt}} + Creates a link to the given URL with the given anchor text. + Implemented in _l_a_t_e_x_(_1_) using footnotes, but _s_l_2_h_(_1_) translates + this to <<aa hhrreeff==UURRLL>>aanncchhoorr tteexxtt<<//aa>> + + \\nnwwaanncchhoorrnnaammee{{nnaammee}}{{aanncchhoorr tteexxtt}} + Creates an anchor point for a hyperlink. Implemented in + _l_a_t_e_x_(_1_) using \\llaabbeell ,, but _s_l_2_h_(_1_) translates this to <<aa + nnaammee==nnaammee>>aanncchhoorr tteexxtt<<//aa>> + + \\iiffhhttmmll ...... \\ffii + Text between \\iiffhhttmmll and \\ffii is ignored by _l_a_t_e_x_(_1_)_, but _s_l_2_h_(_1_) + and the _l_2_h noweb filter translate the text into HTML. + +SSEEEE AALLSSOO + _n_o_w_e_a_v_e(1), _n_o_w_e_b(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(7) diff --git a/web/noweb/src/xdoc/nuweb2noweb.1 b/web/noweb/src/xdoc/nuweb2noweb.1 new file mode 100644 index 0000000000..4205a06e63 --- /dev/null +++ b/web/noweb/src/xdoc/nuweb2noweb.1 @@ -0,0 +1,28 @@ +.TH NOWEB 1 "10/40/2008" +.SH NAME +nuweb2noweb \- convert nuweb files to noweb form +.SH SYNOPSIS +.B nuweb2noweb +file ... +.SH DESCRIPTION +.I Nuweb2noweb +converts a file or files in +.I nuweb +format to +.I noweb +format, writing the results on standard output. +Input is from the file(s) named on the command line. +.SH SEE ALSO +.PP +.IR noweb (1), +.IR nuweb (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/nuweb2noweb.txt b/web/noweb/src/xdoc/nuweb2noweb.txt new file mode 100644 index 0000000000..98d08d56e1 --- /dev/null +++ b/web/noweb/src/xdoc/nuweb2noweb.txt @@ -0,0 +1,30 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + nuweb2noweb - convert nuweb files to noweb form + +SSYYNNOOPPSSIISS + nnuuwweebb22nnoowweebb file ... + +DDEESSCCRRIIPPTTIIOONN + _N_u_w_e_b_2_n_o_w_e_b converts a file or files in _n_u_w_e_b format to _n_o_w_e_b format, + writing the results on standard output. Input is from the file(s) + named on the command line. + +SSEEEE AALLSSOO + _n_o_w_e_b(1), _n_u_w_e_b(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/onepage.tex b/web/noweb/src/xdoc/onepage.tex new file mode 100644 index 0000000000..3eb228f888 --- /dev/null +++ b/web/noweb/src/xdoc/onepage.tex @@ -0,0 +1,197 @@ +\documentclass{article} +\usepackage{alltt,multicol,array,tabularx} +\pagestyle{empty} +\usepackage{vmargin} +\setpapersize{USletter} +\setmarginsrb{0.75in}{0.75in}{0.75in}{0.75in}{0pt}{0pt}{0pt}{0pt} + + +\newenvironment{mytt}{\alltt\def\\{\(\backslash\)}}{\endalltt} +\def\.{\char`\\} +\def\{{\char`\{} +\def\}{\char`\}} + +\setcounter{secnumdepth}{0} + + +\newcommand\code[1]{$\langle$\emph{#1}$\rangle$} + +\begin{document} +\raggedright + +\parskip=0.5\baselineskip plus 0.2\baselineskip +\parindent=0pt + +\hbox{} +\kern-1in +\advance\voffset by0.5in + +\begin{center} +{\Large A One-Page Guide to Using \texttt{noweb} with {\LaTeX}} + +\smallskip + +{Norman Ramsey\\after Lee Wittenberg and Carl Gregory} +\end{center} + + +\begin{multicols}{2}[\section{The \texttt{noweb} file \texttt{foo.nw}}] + +\vbox{ +\centerline{\large\textbf{Documentation}} + +\verb+@+ in column~1 of a line by itself starts a documentation chunk. + +Text is in {\LaTeX}; use \verb+[[+\ldots \verb+]]+ for embedded code +fragments. + +\texttt{noweave} automatically wraps file in +\begin{quote} +{\ttfamily\begin{tabular}{|l|}\hline +\.documentclass\{article\}\\ +\.usepackage\{noweb\}\\ +\.begin\{document\}\\ +~~\vdots\\ +\.end\{document\}\\ +\hline +\end{tabular}} +\end{quote} +To avoid wrapper, put \verb+\documentclass+ and +\verb+\usepackage+ in your source and use \texttt{noweave~-delay}. +} + +\vskip 0pt plus1in +\penalty-500 + +\vbox{ +\centerline{\large\textbf{Code}} + +\verb+<<+\ldots \emph{name of chunk}\ldots \verb+>>=+ in column~1 of a +line by itself starts a code chunk. + +Code is in any language; use \verb+<<+\ldots \verb+>>+ to include +other code chunks by reference. + +\emph{You can write multiple code chunks with the same name}; they are +concatenated. + +Name chunks inside functions with verbs, e.g., +\code{copy all lines from \texttt{\upshape in} to \texttt{\upshape +out}, adding markup}. + +Name chunks holding declarations with nouns, e.g., \code{private +function prototypes} or \code{definitions of exported functions}. + +Name top-level ``root'' chunks after the files they are to +represent, e.g., \verb+<<foo.c>>=+ or \verb+<<foo.h>>=+. +} +\vskip 0pt plus3in + +\end{multicols} + + +\emph{For emacs users}: +A first line of \verb+% -*- mode: Noweb; noweb-code-mode: c-mode -*-+ +is useful (to set editing mode for code chunks). +The Unix \texttt{teTeX} distribution comes with helpful Info files +that cover {\LaTeX} and \texttt{dvips}. + + + + +\section{Typical Commands} + +\noindent +\begin{tabularx}\textwidth{@{}>{\ttfamily}l>{\raggedright\arraybackslash}X} +notangle -L -Rfoo.c foo.nw > foo.c&Extract file \texttt{foo.c}, with \texttt{\#line}\\ +notangle -Rfoo.h foo.nw | cpif foo.h&Extract file \texttt{foo.h} if it +has changed (without \texttt{\#line}).\\ +\small noweave -filter l2h -index -html foo.nw > foo.html& + Create HTML, converting documentation from {\LaTeX}.\\ +noweave -x foo.nw > foo.tex& Create {\LaTeX} document with +cross-reference, no index.\\ +latex foo; latex foo& Process {\LaTeX} into ``device-independent'' +(\texttt{dvi}) file.\\ +latex '\.scrollmode \.input foo'& Alternate {\LaTeX} command for use in +Makefile.\\ +dvips -o foo.ps foo&Convert \texttt{foo.dvi} into PostScript.\\ +\end{tabularx} + + +\begin{multicols}{2}[\section{Minimal {\LaTeX}}] + +\emph{Special characters}\hfil\break +Not to be typed in text; OK in code\hfil\break +\hbox{\qquad \verb=% \ { } $ & # ^ _ ~=} + +\emph{Escapes for specials}\hfil\break +\hbox{\qquad \verb=\% $\backslash$ \{ \} \$ \& \# \_=} + +\emph{Blank line} is new paragraph. + +Write \verb+``quotes''+ to get ``quotes.'' Never \verb+"quotes"+. + +\newenvironment{sample}[1]{\begin{tabular}{@{}l>{\ttfamily}l}\emph{#1}}{\end{tabular}} + +\begin{sample}{Titles} +&\.title\{\ldots\}\\ +&\.author\{\ldots\}\\ +&\.maketitle\\ +\end{sample} + + + +\begin{sample}{Sectioning} +&\.section\{\ldots\}\\ +&\.subsection\{\ldots\}\\ +&\.subsubsection\{\ldots\}\\ +\end{sample} + +\begin{sample}{Fonts} +&\.emph\{{\normalfont\emph{emphasis}}\}\\ +&\.textbf\{{\normalfont\textbf{bold}}\}\\ +&\.texttt\{{\normalfont\texttt{typewriter}}\}\\ +&\.textit\{{\normalfont\textit{italics}}\}\\ +&\.textsf\{{\normalfont\textsf{sans serif}}\}\\ +&\.textsc\{{\normalfont\textsc{small caps}}\}\\ +\end{sample} + +\newdimen\onecol +\onecol=0.25\textwidth +\advance\onecol by -0.33in + + +{\noindent\parskip=0pt +\begin{tabular}{@{}lp{\onecol}p{\onecol}} +\emph{Lists}& +\texttt{List of items} +\par\kern-0.8\baselineskip +\begin{verbatim} +\begin{itemize} +\item one +\item two +\item three +\end{itemize} +\end{verbatim} +\par\kern-2.5\baselineskip\hbox{} +& List of items +\begin{itemize} +\item one +\item two +\item three +\end{itemize} +\par\kern-2.5\baselineskip\hbox{} +\\ +&\multicolumn2{l}{Also \texttt{\.begin\{enumerate\}} for numbered lists.} +\end{tabular}} + +Get the right version; you want {\LaTeX}2$\varepsilon$, not {\LaTeX}~2.09. + + +\end{multicols} + + +\end{document} + + + diff --git a/web/noweb/src/xdoc/representation.tex b/web/noweb/src/xdoc/representation.tex new file mode 100644 index 0000000000..a4c74c4405 --- /dev/null +++ b/web/noweb/src/xdoc/representation.tex @@ -0,0 +1,90 @@ +\documentstyle[11pt]{article} +\title{Representation used by the {\tt noweb} tools} +\author{Norman Ramsey} +\date{October 1991} +\setcounter{secnumdepth}{0} + +\makeatletter +\def\refno#1{\nocite{#1}\@ifundefined + {b@#1}{{\bf ?}\@warning + {Reference number `#1' on page \thepage \space undefined}}% +{\hbox{\csname b@#1\endcsname}}} +\makeatother + +\def\remark#1{\marginpar{\raggedright\hbadness=10000\footnotesize\it #1}} +% \def\remark#1{\relax} + +\begin{document} + +%\maketitle +\section{Representation used by the {\tt noweb} tools} + +{\tt Markup} converts a file from the human syntax to the format used +by the {\tt noweb} tools, and +{\tt unmarkup} is its inverse. +This document is not complete by itself, but should + provide enough information so programmers can examine the +output of {\tt markup} and write tools that manipulate that output. +Study of the existing tools, especially {\tt nountangle} and {\tt +unmarkup}, is recommended. + +Every line begins with {\tt @} and a key word. +The possible lines are: +\begin{quote} +\begin{tabular}{ll} +\tt @begin {\it kind} $n$&Start a chunk\\ +\tt @end {\it kind} $n$&End a chunk\\ +\tt @text {\it string}&{\it string} appeared in a chunk\\ +\tt @nl&A newline\\ +\tt @defn {\it name}&The code chunk named {\it name} is being defined\\ +\tt @use {\it name}&A reference to code chunk named {\it name}\\ +\tt @quote&Start of quoted code in a documentation chunk\\ +\tt @endquote&End of quoted code in a documentation chunk\\ +\tt @file {\it filename}&Name of the file from which the chunks came\\ +\tt @literal {\it text}&Used internally by {\tt noxref} and {\tt noweave}\\ +\end{tabular} +\end{quote} + +{\tt Markup} expands tabs on input (tab stops are every 8~columns). +It numbers each chunk, starting at~0. +It also recognizes and undoes all the appropriate escape sequences, +for example converting {\tt @<<} to {\tt <<}. + +{\tt Markup}'s output is a sequence of files. +Each file is represented by a ``{\tt @file~{\it filename}}'' line, +followed by a sequence of chunks. + +The representation of a documentation chunk is +\begin{quote} +\begin{tabular}{ll} +\tt @begin docs $n$&where $n$ is the chunk number.\\ +{\it docline}&repeated an arbitrary number of times.\\ +\tt @end docs $n$ +\end{tabular} +\end{quote} +where {\it docline} may be {\tt @text}, {\tt @nl}, {\tt @quote}, or +{\tt @endquote}. +Every {\tt @nl} corresponds to the corresponding newline in the +human-readable form. +Quotes must be balanced and may not be nested. + +The representation of a code chunk is +\begin{quote} +\begin{tabular}{ll} +\tt @begin code $n$&where $n$ is the chunk number.\\ +\tt @defn {\it name}&name of this chunk.\\ +\tt @nl&The newline following {\tt <<{\it name}>>=} in the original file\\ +{\it codeline}&repeated an arbitrary number of times.\\ +\tt @end code $n$ +\end{tabular} +\end{quote} +where {\it codeline} may be {\tt @text}, {\tt @nl}, or {\tt @use}. + +{\tt noxref} may add lines of the form +\begin{quote} +\tt @literal {\it text} +\end{quote} +These lines are used to pass literal {\LaTeX} code through the second +stage of {\tt noweave} without any alteration. + +\end{document} diff --git a/web/noweb/src/xdoc/sl2h.1 b/web/noweb/src/xdoc/sl2h.1 new file mode 100644 index 0000000000..de5144657e --- /dev/null +++ b/web/noweb/src/xdoc/sl2h.1 @@ -0,0 +1,40 @@ +.TH NOWEB 1 "local 10/40/2008" +.SH NAME +sl2h \- simple latex to HTML converter +.SH SYNOPSIS +\fBsl2h\fP [\fB\-show\-unknowns\fP] [\fB\-html\-quotes\fP] [file ...] +.SH DESCRIPTION +.I sl2h +is a simple converter from +.IR latex (1) +format to HTML +(which can be used with, e.g., \fInetscape\fP(1) or \fIMosaic\fP(1)). +If the \fB\-show\-unknowns\fP options is given, unrecognized +.I LaTeX +control +sequences appear in bold in the output. +If the \fB\-html\-quotes\fP option is given, +.B sl2h +converts LaTeX double-quote ligatures into HTML 4 double-quote entities. +.I sl2h +uses the LaTeX-generated .toc and .bbl files to help create table of contents +and bibliography. +.SH BUGS +It is possible to tell +.B sl2h +about new control sequences by putting special TeX comments in one's +documentation chunks, but there's no documentation. +Use the source, Luke. +.SH SEE ALSO +.IR nowebfilters (7), +.IR htmltoc (1) +.SH VERSION +This man page is from +.I noweb +version 2.12. +.SH AUTHOR +Norman Ramsey, Tufts University. +Internet address \fBNorman.Ramsey@tufts.edu\fP. +.br +Noweb home page at \fBhttp://www.cs.tufts.edu/~nr/noweb\fP. + diff --git a/web/noweb/src/xdoc/sl2h.nw b/web/noweb/src/xdoc/sl2h.nw new file mode 100644 index 0000000000..13c0db484f --- /dev/null +++ b/web/noweb/src/xdoc/sl2h.nw @@ -0,0 +1,32 @@ +<<*>>= +.TH NOWEB 1 "local <<noweb documentation date>>" +.SH NAME +sl2h \- simple latex to HTML converter +.SH SYNOPSIS +\fBsl2h\fP [\fB\-show\-unknowns\fP] [\fB\-html\-quotes\fP] [file ...] +.SH DESCRIPTION +.I sl2h +is a simple converter from +.IR latex (1) +format to HTML +(which can be used with, e.g., \fInetscape\fP(1) or \fIMosaic\fP(1)). +If the \fB\-show\-unknowns\fP options is given, unrecognized +.I LaTeX +control +sequences appear in bold in the output. +If the \fB\-html\-quotes\fP option is given, +.B sl2h +converts LaTeX double-quote ligatures into HTML 4 double-quote entities. +.I sl2h +uses the LaTeX-generated .toc and .bbl files to help create table of contents +and bibliography. +.SH BUGS +It is possible to tell +.B sl2h +about new control sequences by putting special TeX comments in one's +documentation chunks, but there's no documentation. +Use the source, Luke. +.SH SEE ALSO +.IR nowebfilters (7), +.IR htmltoc (1) +<<AUTHOR section>> diff --git a/web/noweb/src/xdoc/sl2h.txt b/web/noweb/src/xdoc/sl2h.txt new file mode 100644 index 0000000000..026157c740 --- /dev/null +++ b/web/noweb/src/xdoc/sl2h.txt @@ -0,0 +1,39 @@ +NOWEB(1) General Commands Manual NOWEB(1) + + + +NNAAMMEE + sl2h - simple latex to HTML converter + +SSYYNNOOPPSSIISS + ssll22hh [--sshhooww--uunnkknnoowwnnss] [--hhttmmll--qquuootteess] [file ...] + +DDEESSCCRRIIPPTTIIOONN + _s_l_2_h is a simple converter from _l_a_t_e_x(1) format to HTML (which can be + used with, e.g., _n_e_t_s_c_a_p_e(1) or _M_o_s_a_i_c(1)). If the --sshhooww--uunnkknnoowwnnss + options is given, unrecognized _L_a_T_e_X control sequences appear in bold + in the output. If the --hhttmmll--qquuootteess option is given, ssll22hh converts + LaTeX double-quote ligatures into HTML 4 double-quote entities. _s_l_2_h + uses the LaTeX-generated .toc and .bbl files to help create table of + contents and bibliography. + +BBUUGGSS + It is possible to tell ssll22hh about new control sequences by putting spe†+ cial TeX comments in one's documentation chunks, but there's no docu†+ mentation. Use the source, Luke. + +SSEEEE AALLSSOO + _n_o_w_e_b_f_i_l_t_e_r_s(7), _h_t_m_l_t_o_c(1) + +VVEERRSSIIOONN + This man page is from _n_o_w_e_b version 2.12. + +AAUUTTHHOORR + Norman Ramsey, Tufts University. Internet address NNoorrmmaann..RRaammâ€â€ + sseeyy@@ttuuffttss..eedduu. + Noweb home page at hhttttpp::////wwwwww..ccss..ttuuffttss..eedduu//~~nnrr//nnoowweebb. + + + + + local 10/40/2008 NOWEB(1) diff --git a/web/noweb/src/xdoc/techrep.bbl b/web/noweb/src/xdoc/techrep.bbl new file mode 100644 index 0000000000..84381e2975 --- /dev/null +++ b/web/noweb/src/xdoc/techrep.bbl @@ -0,0 +1,68 @@ +\begin{thebibliography}{10} + +\bibitem{denning:announcing} +P.~J. Denning. +\newblock Announcing literate programming. +\newblock {\em Communications of the ACM}, 30(7):593, July 1987. + +\bibitem{guntermann:cweb} +K.~Guntermann and J.~Schrod. +\newblock {{\tt WEB}} adapted to {C}. +\newblock {\em {TUG}boat}, 7(3):134--137, Oct. 1986. + +\bibitem{knuth:web} +D.~E. Knuth. +\newblock The {{\tt WEB}} system of structured documentation. +\newblock Technical Report 980, Stanford Computer Science, Stanford, + California, Sept. 1983. + +\bibitem{knuth:literate:book} +D.~E. Knuth. +\newblock {\em Literate Programming}. +\newblock Stanford University, Stanford, California, 1992. + +\bibitem{levy:cweb} +S.~Levy. +\newblock {\tt WEB} adapted to {C}, another approach. +\newblock {\em TUGBoat}, 8(1):12--13, Apr. 1987. + +\bibitem{ramsey:building} +N.~Ramsey. +\newblock {L}iterate programming: {\hskip 0pt plus 0.5em}{W}eaving a + language-independent {{\tt WEB}}. +\newblock {\em Communications of the ACM}, 32(9):1051--1055, Sept. 1989. + +\bibitem{ramsey:correctness} +N.~Ramsey. +\newblock Correctness of trap-based breakpoint implementations. +\newblock In {\em Proceedings of the 21st ACM Symposium on the Principles of + Programming Languages}, pages 15--24, Portland, Oregon, Jan. 1994. + +\bibitem{ramsey:literate} +N.~Ramsey and C.~Marceau. +\newblock Literate programming on a team project. +\newblock {\em Software---Practice \& Experience}, 21(7):677--683, July 1991. + +\bibitem{sewell:weaving} +W.~Sewell. +\newblock {\em Weaving a Program: Literate Programming in {\tt WEB}}. +\newblock Van Nostrand Reinhold, New York, 1989. + +\bibitem{thimbleby:cweb} +H.~Thimbleby. +\newblock Experiences of `literate programming' using cweb (a variant of + {K}nuth's {WEB}). +\newblock {\em Computer Journal}, 29(3):201--211, 1986. + +\bibitem{thimbleby:review} +H.~Thimbleby. +\newblock A review of {Donald} {C}. {L}indsay's text file difference utility, + {{\em diff}}. +\newblock {\em Communications of the ACM}, 32(6):752--755, June 1989. + +\bibitem{cvw:assessment} +C.~J. Van~Wyk. +\newblock Literate programming: An assessment. +\newblock {\em Communications of the ACM}, 33(3):361--365, March 1990. + +\end{thebibliography} diff --git a/web/noweb/src/xdoc/techrep.tex b/web/noweb/src/xdoc/techrep.tex new file mode 100644 index 0000000000..da88461018 --- /dev/null +++ b/web/noweb/src/xdoc/techrep.tex @@ -0,0 +1,901 @@ +% The formatter used is LaTeX + +% symbols that could be defined +% +% \dsp double space, figures on figure pages +% \tr Princeton TR +% \wbn use consecutive `{\tt WEB} numbering' + + + + +\ifx\tr\undefined +\documentstyle[11pt,noweb,multicol]{article} +\else +\documentstyle[11pt,noweb,multicol,tr]{article} +\fi + +\title{Literate-Programming Tools\\Can Be Simple and Extensible} +\author{Norman Ramsey\thanks{Author's current address is Bellcore, 445 +South Street, Morristown, NJ 07960; +send email to {\tt norman@bellcore.com}.}\\Department of Computer Science, +Princeton University} + +\ifx\wbn\undefined\else \noweboptions{webnumbering}\fi + +\ifx\dsp\undefined\else +\makeatletter +\let\@oldfigure=\figure +\def\figure{\@oldfigure[p]} +\makeatother +\fi + + + +\ifx\tr\undefined +\date{November 1993} +\else +\reportno{CS-TR-351-91} +\date{October 1991\\Revised August 1992, November 1993} +\fi + +\setcounter{secnumdepth}{0} +\clubpenalty=10000 +\widowpenalty=10000 +\makeatletter +\def\refno#1{\nocite{#1}\@ifundefined + {b@#1}{{\bf ?}\@warning + {Reference number `#1' on page \thepage \space undefined}}% +{\hbox{\csname b@#1\endcsname}}} +\makeatother + +\def\remark#1{\marginpar{\raggedright\hbadness=10000\footnotesize\it #1}} +% \def\remark#1{\relax} +\ifx\dsp\undefined\else\renewcommand{\baselinestretch}{1.6}\fi + +\begin{document} + +\maketitle + +\begin{abstract} +When it was introduced, literate programming meant {\tt WEB}. +Desire to use {\tt WEB} with languages other than Pascal led to the +implementation of many versions. +{\tt WEB} is complex, and the difficulty of using {\tt WEB} creates +an artificial barrier to +experimentation with literate programming. +{\tt noweb} provides much of the functionality of +{\tt WEB}, with a fraction of the complexity. +{\tt noweb} is independent of the target programming language, and its +formatter-dependent part is 60~lines. +{\tt noweb} is extensible, because it uses two +representations of programs: one easily edited by authors and one +easily manipulated by tools. + +This paper explains how to use the {\tt noweb} tools and gives +examples of their use. +It sketches the implementation of the tools and describes how new +tools are added to the set. +Because +{\tt WEB} and {\tt noweb} overlap, but each does some things that the +other cannot, +this paper enumerates the differences. +\end{abstract} + +\ifx\tr\undefined +\begin{center}\small +{\bf Key words:} +literate~programming, readability, programming~environments +\end{center} +\fi + +\section{Introduction} +When literate programming was +introduced, it was synonymous with {\tt WEB}, a tool +for writing literate Pascal programs~\cite[Chapter~4]{knuth:literate:book}. +The idea attracted attention; several examples of literate +programs were published, and a special forum was created to discuss literate +programming~% +\cite{denning:announcing}. % ,gries:pearls,knuth:literate:book,thimbleby:review +{\tt WEB} was adapted to programming languages other than +Pascal~\cite{levy:cweb,ramsey:building,thimbleby:cweb}. % guntermann:cweb,sewell:mangle, +% +With experience, many {\tt WEB} users became dissatisfied~\cite{ramsey:literate}. +Some found {\tt WEB} not worth the trouble, as did +one author of the program appearing in Appendix~C +of Reference~\refno{sewell:weaving}. +Others built their own systems for literate + programming. % ~\cite{hamilton:expanding,cvw:loom}. +The literate-programming forum was dropped, on the grounds that +literate programming had become the province of those who could build +their own tools~\cite{cvw:assessment}. + +{\tt WEB} programmers + interleave source code and descriptive text in a single document. +When using {\tt WEB}, a programmer divides the source code into +{\em modules}. +Each module has a documentation part and a code part, and +modules may be written in any order. +The programmer is encouraged to choose an order that helps explain the program. +The code parts are like macro definitions; they have names, and they contain +both code and references to other modules. +A {\tt WEB} file represents a single program; +{\tt TANGLE} extracts that program from the {\tt WEB} source. +One special module has a code part with no name, and {\tt TANGLE} +expands the code part of that module to extract the program. +{\tt WEAVE} converts {\tt WEB} source to +{\TeX} input, from which {\TeX} can produce high-quality typeset +documentation of the program. + + +{\tt WEB} is a complex tool. +In addition to enabling programmers to present pieces of a program in +any order, it expands three kinds of macros, prettyprints code, +evaluates some constant expressions, +provides an integer representation for string literals, and implements +a simple form of version control. +The manual for the original version documents 27 ``control sequences''~\cite{knuth:web}. +The versions for languages other than Pascal offer slightly different +functions and different sets of control sequences. +Significant effort is required to make {\tt WEB} usable with a new +programming language, even when using a tool designed for that +purpose~\cite{ramsey:building}. + +{\tt WEB}'s shortcomings make it difficult to explore the {\em idea} +of literate programming; too much effort is required to master the +{\em tool}. +I designed a new tool that is +simple, extensible, and independent of the target programming language. +{\tt noweb} is designed +around one idea: writing named chunks of code in any order, with +interleaved documentation. +Like {\tt WEB}, and like all literate-programming tools, it can +be used to write a program in pieces and to present those pieces in +an order that helps explain the program. +{\tt noweb}'s value lies in its simplicity, which shows that the idea +of literate programming does not require the complexity of {\tt WEB}. + +Users who could not find a satisfactory literate-programming tool +have had no option other than to write their own tools. +{\tt noweb} reduces the need for new tools because it is designed to +be extended. +It uses Unix pipes to enable users to change existing +behavior or to add new features {without} recompiling {\tt noweb}. +Even language-dependent features, like +prettyprinting and automatic generation of an index, have been added +to {\tt noweb} without recompiling. + + + +\section{{\tt noweb}} % , a minimal literate-programming system} +A {\tt noweb} file contains program source code interleaved with documentation. +When {\tt notangle} is given a {\tt noweb} file, it writes the program + on standard output. +When {\tt noweave} is given a {\tt noweb} file, it reads the {\tt +noweb} source and produces, on standard output, {\TeX} source for +typeset documentation. +Figure~\ref{transforms} shows how to use {\tt notangle} and {\tt +noweave} to produce code and documentation for a C~program contained +in the {\tt noweb} file {\tt foo.nw}. + +\begin{figure} +\footnotesize +\setlength{\unitlength}{2pt} +\begin{picture}(170,80)(0,-40) +\tt +\put(20,12.5){\makebox(0,0)[l]{\ \tt notangle foo.nw > foo.c}} +\put(20,-12.5){\makebox(0,0)[l]{\ \tt noweave foo.nw > foo.tex}} + +\put(0,-5){\framebox(30,10){foo.nw}} +\put(15,5){\vector(1,2){7.5}} +\put(10,20){\framebox(30,10){foo.c}} +\put(15,-5){\vector(1,-2){7.5}} +\put(10,-30){\framebox(30,10){foo.tex}} + +\put(40,-25){\vector(1,0){35}} +\put(57.5,-23.5){\makebox(0,0)[b]{latex foo}} +\put(75,-30){\framebox(30,10){foo.dvi}} +\put(105,-25){\vector(1,0){35}} +\put(122.5,-23.5){\makebox(0,0)[b]{\strut \tt dvi foo}} + % \shortstack{\strut dvi\\\strut \rm driver}}} +\put(140,-25){\makebox(0,0)[l]{\rm\strut \ Typeset documentation for +{\tt foo}}} + +\put(40,25){\vector(1,0){35}} +\put(57.5,26.5){\makebox(0,0)[b]{cc -c foo.c}} +\put(75,20){\framebox(30,10){foo.o}} +\put(105,25){\vector(1,0){35}} +\put(122.5,26.5){\makebox(0,0)[b]{ld foo.o {\ldots}}} +\put(140,25){\makebox(0,0)[l]{\rm\strut \ Executable \tt a.out}} + + + +\end{picture} + +\caption{Using {\tt noweb} to build code and documentation} +\label{transforms} +\end{figure} + +A {\tt noweb} file is a sequence of {\em chunks}, which may appear in any order. +A chunk may contain code or documentation. +Documentation chunks begin with a line that starts with an at sign ({\tt @}) +followed by a space or newline. +They have no names. +Code chunks begin with +\begin{quote} +\tt <<{\rm\it chunk name}>>= +\end{quote} +on a line by itself. +The double left angle bracket ({\tt <<}) must be in the first column. +Chunks are terminated by the beginning of another chunk, or by end of file. +If the first line in the file does not mark the beginning of a +chunk, it is assumed to be the first line of a documentation chunk. + +Documentation chunks contain text that is ignored by {\tt notangle} +and copied verbatim to standard output by +{\tt noweave} (except for quoted code). +{\tt noweave} can work with {\LaTeX}, or it can use +% insert a reference to +a plain-{\TeX} macro package, supplied with {\tt noweb}, that defines +commands like \verb+\chapter+ and \verb+\section+. + +Code chunks contain program source code and references to other code +chunks. +Several code chunks may have the same name; {\tt notangle} +concatenates their definitions to produce a single chunk, just as + {\tt TANGLE} does. +Code chunk definitions are like macro definitions; +{\tt notangle} extracts a program by expanding one chunk (by default +the chunk named \verb+<<*>>+). +The definition of that chunk contains references to other chunks, +which are themselves expanded, and so on. +{\tt notangle}'s output is readable; it preserves the indentation of expanded +chunks with respect to the chunks in which they appear. + +Code may be quoted within documentation chunks by placing double +square brackets around it ({\tt [[}$\ldots${\tt]]}). +These double square brackets are ignored by {\tt notangle}, but they +are used by {\tt noweave} to give code special typographic +treatment. + +If double left and right angle brackets are not paired, they are +treated as literal ``{\tt<<}'' and ``{\tt>>}''. Users can force any +such brackets, even paired brackets, to be treated as literal by +using a preceding at sign (e.g.~``{\tt @<<}''). + +Any line beginning with ``{\tt@ }'' terminates a code chunk. +If such a line has the form +``{\tt@~\char`\%def {\rm\it identifiers}}'', +it also means that the preceding chunk defines the identifiers listed +in {\it identifiers}. +This notation provides a way of marking definitions manually when +no automatic marking is available.% +\footnote{Starting with version~2.10, this form is deprecated; new +code should use \texttt{-filter~btdefn}.} + +Figure~\ref{sample-input} shows a fragment of a {\tt noweb} program +that computes prime numbers. +The program is derived from the example used in +Reference~\refno{knuth:literate:book}, Chapter~4, and Figure~\ref{sample-input} should +be compared with Figure~2b of that paper. +Figure~\ref{noweave-output} shows the program after processing by {\tt +noweave} and {\LaTeX}. +Figure~\ref{notangle-output} shows the beginning of the program as +extracted by {\tt notangle}. +A complete example program accompanies this paper. + +\begin{figure} +\begin{verbatim} +@ This program has no input, because we want to keep it +simple. The result of the program will be to produce a +list of the first thousand prime numbers, and this list +will appear on the [[output]] file. + +Since there is no input, we declare the value [[m = 1000]] +as a compile-time constant. The program itself is capable +of generating the first [[m]] prime numbers for any +positive [[m]], as long as the computer's finite +limitations are not exceeded. +<<program to print the first thousand prime numbers>>= +program print_primes(output); + const m = 1000; + <<other constants of the program>> + var <<variables of the program>> + begin <<print the first [[m]] prime numbers>> + end. +\end{verbatim} +\caption{Sample {\tt noweb} input, from prime number program} +\label{sample-input} +\end{figure} + + + +\begin{figure} +\begin{quote} +\nwbegindocs{4} + +This program has no input, because we want to keep it simple. +The result of the program will be to produce a list of the first +thousand prime numbers, and this list will appear on the \code{}output\edoc{} +file. + +Since there is no input, we declare the value \code{}m = 1000\edoc{} as a +compile-time constant. +The program itself is capable of generating the first \code{}m\edoc{} prime +numbers for any positive \code{}m\edoc{}, as long as the computer's finite +limitations are not exceeded. +\nwenddocs +\nwbegincode{5} +\moddef{program to print the first thousand prime numbers}\endmoddef +program print_primes(output); + const m = 1000; + \LA{}other constants of the program\RA{} + var \LA{}variables of the program\RA{} + begin \LA{}print the first \code{}m\edoc{} prime numbers\RA{} + end. +\nwendcode +\end{quote} +\caption{Output produced by {\tt noweave} and {\LaTeX} from Figure~\protect\ref{sample-input}} +\label{noweave-output} +\end{figure} + +\section{Using {\tt noweb}}% experience with noweb? + +Experimenting with \verb+noweb+ is easy. +{\tt noweb} has little syntax: definition and use of code chunks, marking of +documentation chunks, listing of identifiers defined, +quoting of code, and quoting of brackets. +\verb+noweb+ can be used with any programming language, and its +manual fits on three pages. + +\begin{figure} +\begin{verbatim} +program print_primes(output); + const m = 1000; + rr = 50; + cc = 4; + ww = 10; + ord_max = 30; { p_ord_max squared must exceed p_m } + var p: array [1..m] of integer; + { the first m prime numbers, in increasing order } + page_number: integer; +\end{verbatim} +\unskip +\hbox{\hskip2em\tt\vdots} +\caption{Part of primes program as written by {\tt notangle}} +\label{notangle-output} +\end{figure} + + +On a large project, it is essential that compilers and other tools be +able to refer to locations in the \verb+noweb+ source, even though +they work with \verb+notangle+'s output~\cite{ramsey:literate}. +Giving \verb+notangle+ the \verb+-L+ option makes it emit pragmas +that inform compilers of the placement of lines +in the \verb+noweb+ source. +It also preserves the columns in which +tokens appear. +If \verb+notangle+ is not given the \verb+-L+ option, it respects the +indentation of its input, making its output easy to read. + +Cross-referencing of chunks and identifiers makes large programs +easier to understand. +\verb+noweave -x+ uses {\LaTeX} +to show on what pages each chunk is defined and used. +\verb+noweave -index+ adds a local identifier cross-reference after +each code chunk. +The example program accompanying this article shows full cross-reference +information. + + +{\tt WEB} files map one to one with both programs and documents. +The mapping of \verb+noweb+ files to programs is many to many; the +mapping of files to documents is many to one. +Source files are combined by listing their names on \verb+notangle+'s +or \verb+noweave+'s command line. +Many programs may be extracted from one source by specifying the names +of different root chunks, using \verb+notangle+'s \verb+-R+ command-line +option. + +The simplest example of a one-to-many mapping of programs is that of putting +C~header and program in a single {\tt noweb} file. +The header comes from the root chunk \LA{}header\RA{}, and the program +from the default root chunk, \LA{}*\RA{}. +The following Unix commands extract header \verb+foo.h+ and program +\verb+foo.c+ from {\tt noweb} file \verb+foo.nw+: +% This trick is explained on pages 265--266 of Reference~\refno{kernighan:unix}.} +\begin{quote} +\begin{verbatim} +notangle -L foo.nw > foo.c +notangle -Rheader foo.nw | cpif -ne foo.h +\end{verbatim} +\end{quote} +The \verb+>+ in the first command directs \verb+notangle+'s output to +the file \verb+foo.c+. +The \verb+|+ in the second command directs \verb+notangle+'s output to +the \verb+cpif+ program, which is distributed with {\tt noweb}. +\verb+cpif -ne foo.h+ compares its input to the contents of file +\verb+foo.h+; if they differ, the input replaces \verb+foo.h+. +This trick avoids touching the file \verb+foo.h+ when its contents +have not changed, which avoids triggering unnecessary recompilations. + +A more interesting example is using {\tt noweb} to +interleave different languages in one source file. +I wrote an \verb+awk+ +script that read a machine description and emitted a disassembler for +that machine, and I used {\tt noweb} to combine the script and description +in a single file, so I could place each part of the input next to the +code that processed that input. +The machine description was in the root chunk \LA{}opcodes +table\RA{}, and the \verb+awk+ script in the default root chunk. +The processing steps were: +\begin{verbatim} +notangle opcodes.nw > opcodes.awk +notangle -R'opcodes table' opcodes.nw | +awk -f opcodes.awk > disassem.sml +\end{verbatim} +The first line extracts the {\tt awk} program into file {\tt opcodes.awk}. +The second line extracts the machine description and pipes it to the +third line, which uses the {\tt awk} program to turn it into a +dissassembler. +The disassembler is written to file {\tt disassem.sml}. + +Many-to-one mapping of source to program can be used to obtain +effects similar to those of Ada or Modula-3 generics. +Figure~\ref{generic-example} shows generic C code that supports lists. +The code can be +``instantiated'' by combining it with another \verb+noweb+ file. +\verb+pair_list.nw+, shown in Figure~\ref{pair-list-example}, specifies + lists of integer pairs. +The two are combined by applying \verb+notangle+ to them both: +\begin{verbatim} +notangle pair_list.nw generic_list.nw > pair_list.c +\end{verbatim} +{\tt noweb} has no parameter mechanism, so the ``generic'' code must +refer to a fixed set of symbols, and it cannot be checked for errors +except by compiling \verb+pair_list.c+. +These restrictions make {\tt noweb} a poor approximation to real +generics, but useful nevertheless. + +\begin{figure} +\ifx\dsp\undefined\else\renewcommand{\baselinestretch}{1.3}\fi +\nwbegindocs{0} +This list code supports circularly-linked lists represented by a +pointer to the last element. It is intended to be combined with other +{\tt noweb} code that defines \LA{}fields of a list element\RA{} (the fields +found in an element of a list) and that uses \LA{}list +declarations\RA{} and \LA{}list definitions\RA. +\nwenddocs +\nwbegincode{1} +\moddef{list declarations}\endmoddef +typedef struct list \{ + \LA{}fields of a list element\RA{} + struct list *_link; +\} *List; + +extern List singleton(void); /* singleton list, uninitialized fields */ +extern List append(List, List); /* destructively append two lists */ +#define last(l) (l) +#define head(l) ((l) ? (l)->next : 0) +#define forlist(p,l) for (p=head(l); p; p=(p==last(l) ? 0 : p->next)) +\nwendcode +\nwbegincode{2} +\moddef{list definitions}\endmoddef +List append (List left, List right) \{ + List temp; + if (left == 0) return right; + if (right == 0) return left; + temp = left->_link; left->_link = right->_link; right->_link = temp; + return right; +\} +\nwendcode +\unskip\hbox{\hskip2em\vdots} +\caption{Generic code for implementing lists in C} +\label{generic-example} +\end{figure} + +\begin{figure} +\nwbegincode{1} +\moddef{*}\endmoddef +\LA{}list declarations\RA{} +\LA{}list definitions\RA{} +\nwendcode +\nwbegincode{2} +\moddef{fields of a list element}\endmoddef +int x; +int y; +\nwendcode +\caption{Program to instantiate lists of integer pairs} +\label{pair-list-example} +\end{figure} + +\iffalse +I have used \verb+noweb+ for small programs written in various +languages, including C, Icon, \verb+awk+, and Modula-3. +Larger projects have included a code generator for +Standard ML of New Jersey (written in Standard ML) and +a multi-architecture debugger, written in Modula-3, C, and +assembly language. +A colleague used \verb+noweb+ to write an experimental file +system in C++. + +\unskip +\vskip0pt plus12pt\penalty-200\vskip0pt plus-12pt +\noindent The sizes of these programs are % try for better page break +\nobreak +\begin{quote}\nobreak +\catcode`\?=\active\def?{\phantom{0}} +\catcode`\*=\active\def*{\phantom{,}} +\leavevmode\rlap{\begin{tabular}{lcc} +Program&Documentation lines&Total lines\\ +\noalign{\smallskip} +{\tt markup} and {\tt nt}&?*400&?1,200\\ +ML code generator&?*900&?2,600\\ +Debugger&1,400&11,000\\ +File system&4,400&27,000 +\end{tabular}} +\end{quote} + +\else + +I myself have used \verb+noweb+ for code written in various +languages, including assembly language, \verb+awk+, C, Icon, Modula-3, +Promela, +Standard~ML, and \TeX.% +\footnote{Readers interested in another example of {\tt noweb} can see +a published version of one of these programs \cite{ramsey:correctness}.} +These projects have ranged in size from a few hundred to twenty +thousand lines of code. +Other users around the world have undertaken +{\tt noweb} projects of comparable size in C$++$, Modula-2, occam, Parallel C, +perl, Prolog, and Scheme. +Two colleagues are using {\tt noweb} to write a book describing the +design and implementation of a retargetable C compiler. + +\fi + +\section{Representation of {\tt noweb} files} + +The \verb+noweb+ syntax is easy to read, write, and edit, but it is +not easily manipulated by programs. +To make it easy to extend \verb+noweb+, I have written +\verb+markup+, which converts \verb+noweb+ source to + a representation that is easily manipulated +by commonly used Unix tools like {\tt sed} and {\tt awk}. +In this representation, +every line begins with {\tt @} and a key word. +The possibilities are: +\begin{quote} +\leavevmode\rlap{\begin{tabular}{ll} +\tt @begin {\rm\it kind} $n$&Start a chunk\\ +\tt @end {\rm\it kind} $n$&End a chunk\\ +\tt @text {\rm\it string}&{\rm\it string} appeared in a chunk\\ +\tt @nl&A newline appeared in a chunk\\ +\tt @defn {\rm\it name}&The code chunk named {\rm\it name} is being defined\\ +\tt @use {\rm\it name}&A reference to code chunk named {\rm\it name}\\ +\tt @quote&Start of quoted code in a documentation chunk\\ +\tt @endquote&End of quoted code in a documentation chunk\\ +\tt @file {\rm\it filename}&Name of the file from which the chunks came\\ +\tt @line $n$&Next text line came from source line $n$ in current file\\ +\tt @index defn {\rm\it ident}&The current chunk contains a definition of {\rm\it ident}\\ +\tt @index use {\rm\it ident}&The current chunk contains a use of {\rm\it ident}\\ +\tt @index nl {\rm\it ident}&A newline that is part of markup, not part of the chunk\\ +\tt @literal {\rm\it text}&{\tt noweave} copies {\rm\it text} to output\\ +\end{tabular}} +\end{quote} +{\tt markup} numbers each chunk, starting at~0. +It also recognizes and undoes the escape sequence for double brackets, +e.g.~converting ``{\tt @<<}'' to ``{\tt <<}''. +{\tt markup}'s output represents a sequence of files. +Each file is represented by a ``{\tt @file~{\rm\it filename}}'' line, +followed by a sequence of chunks. + +The representation of a documentation chunk is +\begin{quote} +\begin{tabular}{ll} +\tt @begin docs $n$&where $n$ is the chunk number.\\ +{\it docline}&repeated an arbitrary number of times.\\ +\tt @end docs $n$ +\end{tabular} +\end{quote} +where {\it docline} may be {\tt @text}, {\tt @nl}, {\tt @quote}, +{\tt @endquote}, {\tt @file}, {\tt @line}, or {\tt @index}. +Every {\tt @nl} or {\tt @index~nl} corresponds to a newline in the original file. +{\tt markup} guarantees that quotes are balanced and not nested. + +\vskip0pt plus12pt\penalty-200\vskip0pt plus-12pt +The representation of a code chunk is +\begin{quote} +\leavevmode\rlap{\begin{tabular}{ll} +\tt @begin code $n$&where $n$ is the chunk number.\\ +\tt @defn {\rm\it name}&name of this chunk.\\ +\tt @nl&The newline following {\tt <<{\rm\it name}>>=} in the original file\\ +{\it codeline}&repeated an arbitrary number of times.\\ +\tt @end code $n$ +\end{tabular}} +\end{quote} +where {\it codeline} may be {\tt @text}, {\tt @nl}, {\tt @use}, +{\tt @file}, {\tt @line}, or {\tt @index}. + +The \verb+noweb+ tools are implemented by piping the output of +\verb+markup+ to other programs. +\verb+notangle+ is a Unix shell script that builds a +pipeline between \verb+markup+ and \verb+nt+, which reads +and expands definitions of code chunks. +\verb+noweave+ pipes the output of \verb+markup+ to a 60-line +\verb+awk+~script that inserts appropriate {\TeX} or {\LaTeX} +formatting commands. + +\section{Extending {\tt noweb}} + +Having a format easily read by programs makes {\tt noweb} extensible; +one can manipulate literate programs using Unix shell scripts and +filters. +{\tt notangle} and {\tt noweave} have {\tt -filter} options that can +be used to insert filters into their pipelines without changing any +code. +These filters manipulate the representation described in the previous +section. +The results are converted to {\TeX} by {\tt noweave} and to code by +{\tt notangle}. + + +Filters can be used to change the way {\tt noweb} works; for example, +a one-line {\tt sed} script makes {\tt noweb} treat two chunk names as +identical if they differ only in their representation of whitespace. +A 55-line Icon program makes it possible to abbreviate chunk names +using a trailing ellipsis, as in {\tt WEB}. +To share programs with colleagues who don't enjoy literate +programming, I use a filter that +places each line of documentation in a comment and moves it to +the succeeding code chunk. +With this filter, \verb+notangle+ +transforms a literate +program into a traditional commented program, without loss of +information and with only a modest penalty in readability. +Figure~\ref{nountangle-output} shows the results of applying +the filter to the prime-number program shown in +Figure~\ref{sample-input}. + + + + +\begin{figure} +\begin{webcode}\parindent=0pt +\{ This program has no input, because we want to keep it \} +\{ simple. The result of the program will be to produce a \} +\{ list of the first thousand prime numbers, and this list \} +\{ will appear on the [[output]] file. \} +\hbox{\hskip2em\tt\vdots} +\{ <program to print the first thousand prime numbers>= \} +program print_primes(output); + const m = 1000; + \{ \\section-The output phase- \} + \{ \} + \{ <other constants of the program>= \} + rr = 50; + cc = 4; + ww = 10; + \{ <other constants of the program>= \} + ord_max = 30; \{ p_ord_max squared must exceed p_m \} + var \{ How should table [[p]] be represented? Two possibilities \} + \{ suggest themselves: We could construct a sufficiently \} +\hbox{\hskip2em\tt\vdots} +\end{webcode} +\caption{Output produced from +Figure~\protect\ref{sample-input} by filter converting documentation +chunks into comments.} +\label{nountangle-output} +\end{figure} + + + +Filters can be used to add significant features. +\verb+noweave+'s cross-reference and indexing features use two +filters, one that finds uses of defined identifiers and one that +uses \verb+@literal+ to insert {\LaTeX} cross-reference +commands. +In many cases, a third, language-dependent filter can be used to +mark identifier definitions, +making index generation completely automatic; +otherwise, the \verb+btdefn+ filter can be used to interpret +hand-place backticks (`) as marking definitions. +Kostas Oikonomou of AT\&T Bell Labs and Conrado Martinez-Parra of +the Univ.\ Politecnica de Catalunya in Barcelona have written filters +that add prettyprinting to {\tt noweb}. +Oikonomou's filters prettyprint Icon and Object-Oriented Turing; +Martinez-Parra's filter prettyprints a variant of Dijkstra's language +of guarded commands. + + +\section{Comparing {\tt WEB} and {\tt noweb}} + +Unlike {\tt WEB}, +\verb+noweb+ is independent of the target programming language. +{\tt WEB} tools can be generated for many programming languages, +but those languages must be lexically similar to C. +For example, {\tt WEB} can't handle the \verb+awk+ regular-expression +notation ``\verb+/+\ldots\verb+/+''; every such expression must quoted +using {\tt WEB}'s ``verbatim'' control sequence. +The effort required to +generate {\tt WEB} tools is significant; the prospective user must +write a specification of several hundred +lines. + +Being independent of the target programming language makes {\tt noweb} +simpler, but it also means that {\tt noweb} can do less. +Most of the differences between {\tt WEB} and {\tt noweb} arise +because {\tt WEB} has language-dependent features that are not present +in \verb+noweb+. +These features include +prettyprinting, +typesetting comments using {\TeX}, +expanding macros, +evaluating constant expressions, +and +converting string literals to indices into a ``string pool.'' +Among these features, +\verb+noweb+ users are most likely to miss prettyprinting. + + +Some differences arise because {\tt WEB} and \verb+noweb+ implement +similar features differently. +{\tt WEB}'s original {\tt TANGLE} removed white space and folded +lines to fill each line with tokens, +making its output unreadable~\cite[Chapter~4, Figure~3]{knuth:literate:book}. +Later adaptations preserved line breaks but removed other white space. +By default, \verb+notangle+ preserves whitespace and maintains +indentation when expanding chunks. +It can therefore be used with +languages like Miranda and +Haskell, in which indentation is significant. +{\tt TANGLE} cannot. + +{\tt WEB}'s {\tt WEAVE} assigns a number to each chunk, and its index +and cross-reference +information refer to chunk numbers, not page numbers. +\verb+noweb+ uses {\LaTeX} to emit index and cross-reference information that +refer to page numbers. +Anyone who has read a large literate program will +appreciate the difference. + +{\tt WEB} works poorly with \LaTeX; \LaTeX\ constructs cannot +be used in {\tt WEB} source, and getting {\tt WEAVE} output +to work in \LaTeX\ documents requires tedious adjustments by hand. +\verb+noweb+ works with both plain {\TeX} and {\LaTeX}. +Both {\tt WEAVE} and {\tt noweave} depend on the text formatter in two +ways: the source of the program itself, and the supporting macros. +{\tt WEAVE}'s source (written using {\tt WEB} for C) is +several thousand lines long, and the formatting code is not isolated. +{\tt noweave}'s source is a 200-line shell script, and it uses a +60-line {\tt awk} program to generate {\TeX}. +Both +{\tt WEAVE} and {\tt noweave} use about 200 lines of supporting macros +for plain {\TeX}. +\verb+noweb+ uses another 300 lines to support {\LaTeX}, primarily +because the page-based cross-reference mechanism is complex. + +{\tt noweb} has +two features that weren't in the original {\tt WEB}, but that appeared in +some of {\tt WEB}'s later adaptations; +{\tt notangle} can extract more than one program from +a single source file, and it can give a compiler the original locations +of source lines. +{\tt noweb} has a related feature not found in the {\tt WEB} variants; +{\tt noweave} adds no newlines to its output, making it easy to find +the sources of {\TeX} or {\LaTeX} errors. +For example, an error on line~634 of a generated +{\TeX} file is caused by a problem on line~634 of the +corresponding {\tt noweb} file. + +Reviewers have had many expectations of literate-programming +tools~\cite{thimbleby:review,cvw:assessment}. +The most important is {\em verisimilitude}: a single +input should produce both compilable program and publishable document, +warranting the correctness of the document. +Others include flexible order of elaboration, +ability to develop program and documentation concurrently in one +place, cross-references, and indexing. +Both {\tt WEB} and {\tt noweb} +satisfy all these expectations, although in both cases automatic +indexing is available for a limited set of programming languages. + + +\section{Discussion} + +{\tt WEB} takes the monolithic approach to literate programming---it +does everything. +{\tt noweb}'s approach is to compose +simple tools that manipulate files in the {\tt noweb} format. +Existing Unix tools provide some of the {\tt WEB} features that +aren't found in \verb+noweb+. +Unix supplies two macro processors: the C +preprocessor and the \verb+m4+ macro processor. +\verb+xstr+ extracts string literals. +\verb+patch+ provides a form of version control similar to {\tt WEB}'s +change files. +Few of {\tt WEB}'s remaining features will be missed; for example, many compilers +evaluate constant expressions at compile time. +Experience with {\tt WEB} has suggested that prettyprinting may be +more trouble than it is worth~\cite{ramsey:literate}. + +{\tt noweb} was developed on Unix and uses Unix tools like {\tt +awk} and the Bourne shell, but the only Unix feature it really needs +is the pipeline. +All components written in {\tt awk} have been duplicated in Icon; +an Icon implementation is freely available from the University of Arizona, and +it runs on a wide variety of hardware and operating-system platforms. +{\tt noweb} can be ported to non-Unix platforms provided they have +pipelines or an emulation thereof, ANSI~C, and either {\tt awk} or Icon. +For example, +Lee Wittenberg of Kean College ported {\tt noweb} to MS-DOS by +rewriting the shell scripts as DOS batch files. + +Indexing and cross-referencing make +{\tt noweb} less simple than it could be. +Complex {\LaTeX} code is needed to compute page numbers for use in +cross-reference lists and in the index. +The ability to use page numbers justifies this complexity, especially +since the complexity can be hidden from most users. +Users do need to understand the {\LaTeX} code if they want to customize the +appearance of their {\tt noweb} documents while retaining {\tt +noweb}'s use of page numbers for cross-reference. +Most literate-programming tools +forbid customization, but not all users will accept such a restriction. +I have compromised between simplicity and customizability by adding +{\LaTeX} options for a dozen of the most commonly requested +customizations; users can choose from among these options without +understanding {\tt noweb}'s {\LaTeX} code. + + +Four things distinguish {\tt noweb} from previous work. +{\tt noweb} takes as simple as possible a view of literate programming +and the tools needed to implement it. +Instead of relying on a generator or re-implementation to support +different programming languages, {\tt noweb} is independent of the +target programming language. +{\tt noweave}'s dependence on its typesetter is small and isolated, +instead of being distributed throughout a large implementation. +Users have extended {\tt noweb} without recompiling its source code. + +Experimenting with {\tt noweb} is easy because the tools are simple +and they work with any language. +If the experiment is unsatisfying, it is easy to abandon, because +\verb+notangle+'s output, unlike {\tt TANGLE}'s, is readable. +\verb+noweb+ is simpler than {\tt WEB}, and it is easier to use and +understand, but it does less. +I argue, however, that the benefit of {\tt WEB}'s extra features is +outweighed by cost of the extra complexity, +making \verb+noweb+ better for writing literate programs. + +\verb+noweb+ can be obtained by +anonymous {\tt ftp} from CTAN, +the Comprehensive {\TeX} +Archive Network, in directory {\tt web/noweb}. CTAN replicas appear +on +hosts {\tt ftp.shsu.edu}, {\tt ftp.tex.ac.uk}, and {\tt ftp.uni-stuttgart.de}. + +\section{Acknowledgements} +Mark Weiser's invaluable encouragement provided the impetus for me to +write this paper, which I did while visiting the Computer +Science Laboratory of the Xerox Palo Alto Research Center. +David Hanson suggested and provided the \verb+cpif+ program. +Preston Briggs developed many of the ideas used in \verb+noweb+'s +indexing, and he contributed code used in one of the pipeline stages. +Dave Love provided much-needed {\LaTeX} expertise. +Comments from David Hanson and from the anonymous referees stimulated +me to improve the paper. +The development of {\tt noweb} was supported by a Fannie and John Hertz +Foundation Fellowship. + +This paper was submitted in August, 1991. + +\bibliographystyle{abbrv} +\bibliography{web,cs,ramsey,ml} + +\clearpage +\noweboptions{shift} % needed to make room for wordcount example + +\input wc + +\end{document} diff --git a/web/noweb/test/regression b/web/noweb/test/regression new file mode 100755 index 0000000000..c66eb0cb71 --- /dev/null +++ b/web/noweb/test/regression @@ -0,0 +1,82 @@ +#!/usr/bin/env lua5.1-courseware + +require 'osutil' + +local posix = require 'courseware-posix' + +local stringf = string.format +local function runf(...) return os.execute(string.format(...)) end +local function eprintf(...) return io.stderr:write(string.format(...)) end + +local backends = { 'toascii', 'toroff', 'unmarkup', 'nt', 'tohtml', 'totex' } + +local filters = { 'noidx' } + +local fields = (require 'flags').parser() + :string('old', '/usr/lib/noweb'):help('installation directory of old version') + :numarg(1) + :parse(arg) + +local newdir = assert(arg[1]) + +local function outfiles(command) + local tmp = os.capture 'mktemp' + local tmp2 = os.capture 'mktemp' + runf('%s > %s 2> %s', command, tmp, tmp2) + return tmp, tmp2 +end + +local options = { nt = '-R*' } + +local pipelines = { } +do + local function extend(c, i) + local n = i == nil and #pipelines or i < 0 and #pipelines + 1 + i or i + assert(pipelines[n]) + local next = stringf('%s | %s', pipelines[n], stringf('$L/%s %s', c, options[c] or '')) + table.insert(pipelines, next) + end + + table.insert(pipelines, '$L/markup %s') + for _, f in ipairs(filters) do + if posix.access(stringf('%s/%s', newdir, f)) then + extend(f) + end + end + for i = 1, #pipelines do + for _, b in ipairs(backends) do + extend(b, i) + end + end +end + +local sources = os.capture('echo $HOME/noweb/dist/src/c/*.nw') + + +for _, p in ipairs(pipelines) do + eprintf('%s\n', p) + for f in sources:gmatch '%S+' do + local oldc = stringf(p:gsub('%$L', fields.old), f) + local newc = stringf(p:gsub('%$L', newdir), f) + local oldout, o2 = outfiles(oldc) + local newout, n2 = outfiles(newc) + + local rc = runf('cmp -s %s %s', oldout, newout) + local rc2 = runf('cmp -s %s %s', o2, n2) + if rc ~= 0 or rc2 ~= 0 then + eprintf('FAILED: %s\n %s\n', oldc, newc) + if rc2 ~= 0 then + runf('colordiff -u %s %s | head -40', o2, n2) + end + if rc ~= 0 then + runf('colordiff -u %s %s | head -40', oldout, newout) + end + os.exit(1) + else + os.remove(oldout) + os.remove(newout) + end + end +end + + |