WIP
[newfangle.git] / fangle.txt
blob332ed96ecadb53550b3c6a9174dd8ec5df93583f
3 fangle
5  Sam Liddicott
7 sam@liddicott.com
11 August 2009
17 Introduction 
18 Fangle is a tool for fangled literate programming. Newfangled is defined as New and often needlessly novel by TheFreeDictionary.com.
19 In this case, fangled means yet another not-so-new1. but improved.  ^1 method for literate programming.
20 Literate Programming has a long history starting with the great Donald Knuth himself, whose literate programming tools seem to make use of as many escape sequences for semantic markup as TeX (also by Donald Knuth).
21 Norman Ramsey wrote the Noweb set of tools (notangle, noweave and noroots) and helpfully reduced the amount of magic character sequences to pretty much just <<, >> and @, and in doing so brought the wonders of literate programming within my reach.
22 While using the L Y X editor for LaTeX editing I had various troubles with the noweb tools, some of which were my fault, some of which were noweb's fault and some of which were L Y X's fault.
23 Noweb generally brought literate programming to the masses through removing some of the complexity of the original literate programming, but this would be of no advantage to me if the L Y X / LaTeX combination brought more complications in their place.
24 Fangle was thus born (originally called Newfangle) as an awk replacement for notangle, adding some important features, like better integration with L Y X and LaTeX (and later TeXmacs), multiple output format conversions, and fixing notangle bugs like indentation when using -L for line numbers.
25 Significantly, fangle is just one program which replaces various programs in Noweb. Noweave is done away with and implemented directly as LaTeX macros, and noroots is implemented as a function of the untangler fangle.
26 Fangle is written in awk for portability reasons, awk being available for most platforms. A Python version2. hasn't anyone implemented awk in python yet?  ^2 was considered for the benefit of L Y X but a scheme version for TeXmacs will probably materialise first; as TeXmacs macro capabilities help make edit-time and format-time rendering of fangle chunks simple enough for my weak brain.
27 As an extension to many literate-programming styles, Fangle permits code chunks to take parameters and thus operate somewhat like C pre-processor macros, or like C++ templates. Name parameters (or even local variables in the callers scope) are anticipated, as parameterized chunks — useful though they are — are hard to comprehend in the literate document.
28 License 
29 Fangle is licensed under the GPL 3 (or later).
30 This doesn't mean that sources generated by fangle must be licensed under the GPL 3.
31 This doesn't mean that you can't use or distribute fangle with sources of an incompatible license, but it means you must make the source of fangle available too.
32 As fangle is currently written in awk, an interpreted language, this should not be too hard.
34 4a <gpl3-copyright[1](\v), lang=text> ≡ 
35       ________________________________________________________________________
36   1  | fangle - fully featured notangle replacement in awk
37   2  | 
38   3  | Copyright (C) 2009-2010 Sam Liddicott <sam@liddicott.com>
39   4  | 
40   5  | This program is free software: you can redistribute it and/or modify
41   6  | it under the terms of the GNU General Public License as published by
42   7  | the Free Software Foundation, either version 3 of the License, or
43   8  | (at your option) any later version.
44   9  | 
45   10  | This program is distributed in the hope that it will be useful,
46   11  | but WITHOUT ANY WARRANTY; without even the implied warranty of
47   12  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
48   13  | GNU General Public License for more details.
49   14  | 
50   15  | You should have received a copy of the GNU General Public License
51   16  | along with this program.  If not, see <http://www.gnu.org/licenses/>.
52      |________________________________________________________________________
53 Used by 35a, 37a, 87b 
55 Table of contents
56 Introduction   3
57 License   4
58 I Using Fangle   9
59 1 Introduction to Literate Programming   11
60 2 Running Fangle   13
61 2.1 Listing roots   13
62 2.2 Extracting roots   13
63 2.3 Formatting the document   13
64 3 Using Fangle with L^ A T_ E X   15
65 4 Using Fangle with L Y X   17
66 4.1 Installing the L Y X module   17
67 4.2 Obtaining a decent mono font   17
68 4.2.1 txfonts   17
69 4.2.2 ams pmb   17
70 4.2.3 Luximono   17
71 4.3 Formatting your Lyx document   18
72 4.3.1 Customising the listing appearance   18
73 4.3.2 Global customisations   18
74 4.4 Configuring the build script   19
75 4.4.1 ...   19
76 5 Using Fangle with T_ E X_( M A CS)   21
77 6 Fangle with Makefiles   23
78 6.1 A word about makefiles formats   23
79 6.2 Extracting Sources   23
80 6.2.1 Converting from L Y X to L^ A T_ E X   24
81 6.2.2 Converting from T_ E X_( M A CS)   24
82 6.3 Extracting Program Source   25
83 6.4 Extracting Source Files   25
84 6.5 Extracting Documentation   28
85 6.5.1 Formatting T_ E X   28
86 6.5.1.1 Running pdflatex   28
87 6.5.2 Formatting T_ E X_( M A CS)   28
88 6.5.3 Building the Documentation as a Whole   28
89 6.6 Other helpers   29
90 6.7 Boot-strapping the extraction   29
91 6.8 Incorporating Makefile.inc into existing projects   30
92 Example   30
93 II Source Code   33
94 7 Fangle Makefile   35
95 8 Fangle awk source code   37
96 8.1 AWK tricks   37
97 8.2 Catching errors   38
98 9 T_ E X_( M A CS) args   39
99 10 L^ A T_ E X and lstlistings   41
100 10.1 Additional lstlstings parameters   41
101 10.2 Parsing chunk arguments   43
102 10.3 Expanding parameters in the text   44
103 11 Language Modes & Quoting   47
104 11.1 Modes explanation   47
105 11.2 Modes affect included chunks   47
106 11.3 Language Mode Definitions   48
107 11.3.1 Backslash   49
108 11.3.2 Strings   49
109 11.3.3 Parentheses, Braces and Brackets   50
110 11.3.4 Customizing Standard Modes   51
111 11.3.5 Comments   51
112 11.3.6 Regex   52
113 11.3.7 Perl   53
114 11.3.8 sh   53
115 11.3.9 Make   53
116 11.4 Quoting scenarios   56
117 11.4.1 Direct quoting   56
118 11.5 Some tests   57
119 11.6 A non-recursive mode tracker   58
120 11.6.1 Constructor   58
121 11.6.2 Management   58
122 11.6.3 Tracker   60
123 11.6.3.1 One happy chunk   62
124 11.6.3.2 Tests   62
125 11.7 Escaping and Quoting   63
126 12 Recognizing Chunks   65
127 12.1 Chunk start   65
128 12.1.1 T_ E X_( M A CS)   65
129 12.1.2 lstlistings   66
130 12.2 Chunk Body   67
131 12.2.1 T_ E X_( M A CS)   67
132 12.2.2 Noweb   68
133 12.3 Chunk end   68
134 12.3.1 lstlistings   68
135 12.3.2 noweb   69
136 12.4 Chunk contents   69
137 12.4.1 lstlistings   70
138 13 Processing Options   73
139 14 Generating the Output   75
140 14.1 Assembling the Chunks   76
141 14.1.1 Chunk Parts   76
142 15 Storing Chunks   81
143 16 getopt   83
144 17 Fangle LaTeX source code   87
145 17.1 fangle module   87
146 17.1.1 The Chunk style   87
147 17.1.2 The chunkref style   88
148 17.2 Latex Macros   88
149 17.2.1 The chunk command   89
150 17.2.1.1 Chunk parameters   90
151 17.2.2 The noweb styled caption   90
152 17.2.3 The chunk counter   90
153 17.2.4 Cross references   93
154 17.2.5 The end   94
155 18 Extracting fangle   95
156 18.1 Extracting from Lyx   95
157 18.2 Extracting documentation   95
158 18.3 Extracting from the command line   96
159 III Tests   97
160 19 Tests   99
161 20 Chunk Parameters   101
162 20.1 L Y X   101
163 20.2 T_ E X_( M A CS)   101
164 21 Compile-log-lyx   103
165 Part I  Using Fangle 
166 Chapter 1Introduction to Literate Programming
167 Todo: Should really follow on from a part-0 explanation of what literate programming is.
168 Chapter 2Running Fangle
169 Fangle is a replacement for noweb, which consists of notangle, noroots and noweave.
170 Like notangle and noroots, fangle can read multiple named files, or from stdin.
171 2.1 Listing roots 
172 The -r option causes fangle to behave like noroots.
173 fangle -r filename.tex
174 will print out the fangle roots of a tex file. 
175 Unlike the noroots command, the printed roots are not enclosed in angle brackets e.g. <<name>>, unless at least one of the roots is defined using the notangle notation <<name>>=.
176 Also, unlike noroots, it prints out all roots --- not just those that are not used elsewhere. I find that a root not being used doesn't make it particularly top level — and so-called top level roots could also be included in another root as well. 
177 My convention is that top level roots to be extracted begin with ./ and have the form of a filename.
178 Makefile.inc, discussed in 6, can automatically extract all such sources prefixed with ./
179 2.2 Extracting roots 
180 notangle's -R and -L options are supported.
181 If you are using L Y X or LaTeX, the standard way to extract a file would be:
182 fangle -R./Makefile.inc fangle.tex > ./Makefile.inc
183 If you are using TeXmacs, the standard way to extract a file would similarly be:
184 fangle -R./Makefile.inc fangle.txt > ./Makefile.inc
185 TeXmacs users would obtain the text file with a verbatim export from TeXmacs which can be done on the command line with texmacs -s -c fangle.tm fangle.txt -q
186 Unlike the noroots command, the -L option to generate C pre-preocessor #file style line-number directives,does not break indenting of the generated file..
187 Also, thanks to mode tracking (described in 11) the -L option does not interrupt (and break) multi-line C macros either.
188 This does mean that sometimes the compiler might calculate the source line wrongly when generating error messages in such cases, but there isn't any other way around if multi-line macros include other chunks.
189 Future releases will include a mapping file so that line/character references from the C compiler can be converted to the correct part of the source document.
190 2.3 Formatting the document 
191 The noweave replacement built into the editing and formatting environment for TeXmacs, L Y X (which uses LaTeX), and even for raw LaTeX.
192 Use of fangle with TeXmacs, L Y X and LaTeX are explained the the next few chapters.
193 Chapter 3Using Fangle with LaTeX
194 Because the noweave replacement is impemented in LaTeX, there is no processing stage required before running the LaTeX command. Of course, LaTeX may need running two or more times, so that the code chunk references can be fully calculated.
195 The formatting is managed by a set of macros shown in 17, and can be included with:
196 \usepackage{fangle.sty}
197 Norman Ramsay's origial noweb.sty package is currently required as it is used for formatting the code chunk captions.
198 The listings.sty package is required, and is used for formatting the code chunks and syntax highlighting.
199 The xargs.sty package is also required, and makes writing LaTeX macro so much more pleasant.
200 To do: Add examples of use of Macros
202 Chapter 4Using Fangle with L Y X
203 L Y X uses the same LaTeX macros shown in 17 as part of a L Y X module file fangle.module, which automatically includes the macros in the document pre-amble provided that the fangle L Y X module is used in the document.
204 4.1 Installing the L Y X module 
205 Copy fangle.module to your L Y X layouts directory, which for unix users will be ~/.lyx/layouts
206 In order to make the new literate styles availalble, you will need to reconfigure L Y X by clicking Tools->Reconfigure, and then re-start L Y X.
207 4.2 Obtaining a decent mono font 
208 The syntax high-lighting features of lstlistings makes use of bold; however a mono-space tt font is used to typeset the listings. Obtaining a bold tt font can be impossibly difficult and amazingly easy. I spent many hours at it, following complicated instructions from those who had spend many hours over it, and was finally delivered the simple solution on the lyx mailing list.
209 4.2.1 txfonts 
210 The simple way was to add this to my preamble:
211 \usepackage{txfonts}
212 \renewcommand{\ttdefault}{txtt}
214 4.2.2 ams pmb 
215 The next simplest way was to use ams poor-mans-bold, by adding this to the pre-amble:
216 \usepackage{amsbsy}
217 %\renewcommand{\ttdefault}{txtt}
218 %somehow make \pmb be the command for bold, forgot how, sorry, above line not work
219 It works, but looks wretched on the dvi viewer.
220 4.2.3 Luximono 
221 The lstlistings documention suggests using Luximono.
222 Luximono was installed according to the instructions in Ubuntu Forums thread 11591811. http://ubuntuforums.org/showthread.php?t=1159181  ^1 with tips from miknight2. http://miknight.blogspot.com/2005/11/how-to-install-luxi-mono-font-in.html  ^2 stating that sudo updmap --enable MixedMap ul9.map is required. It looks fine in PDF and PS view but still looks rotten in dvi view.
223 4.3 Formatting your Lyx document 
224 It is not necessary to base your literate document on any of the original L Y X literate classes; so select a regular class for your document type.
225 Add the new module Fangle Literate Listings and also Logical Markup which is very useful.
226 In the drop-down style listbox you should notice a new style defined, called Chunk.
227 When you wish to insert a literate chunk, you enter it's plain name in the Chunk style, instead of the old noweb method that uses <<name>>= type tags. In the line (or paragraph) following the chunk name, you insert a listing with: Insert->Program Listing.
228 Inside the white listing box you can type (or paste using shift+ctrl+V) your listing. There is no need to use ctrl+enter at the end of lines as with some older L Y X literate techniques --- just press enter as normal.
229 4.3.1 Customising the listing appearance 
230 The code is formatted using the lstlistings package. The chunk style doesn't just define the chunk name, but can also define any other chunk options supported by the lstlistings package \lstset command. In fact, what you type in the chunk style is raw latex. If you want to set the chunk language without having to right-click the listing, just add ,lanuage=C after the chunk name. (Currently the language will affect all subsequent listings, so you may need to specify ,language= quite a lot).
231 To do: so fix the bug
233 Of course you can do this by editing the listings box advanced properties by right-clicking on the listings box, but that takes longer, and you can't see at-a-glance what the advanced settings are while editing the document; also advanced settings apply only to that box --- the chunk settings apply through the rest of the document3. It ought to apply only to subsequent chunks of the same name. I'll fix that later  ^3.
234 To do: So make sure they only apply to chunks of that name
236 4.3.2 Global customisations 
237 As lstlistings is used to set the code chunks, it's \lstset command can be used in the pre-amble to set some document wide settings.
238 If your source has many words with long sequences of capital letters, then columns=fullflexible may be a good idea, or the capital letters will get crowded. (I think lstlistings ought to use a slightly smaller font for captial letters so that they still fit).
239 The font family \ttfamily looks more normal for code, but has no bold (an alternate typewriter font is used). 
240 With \ttfamily, I must also specify columns=fullflexible or the wrong letter spacing is used.
241 In my LaTeX pre-amble I usually specialise my code format with:
243 19a <document-preamble[1](\v), lang=tex> ≡ 
244       ________________________________________________________________________
245   1  | \lstset{
246   2  | numbers=left, stepnumber=1, numbersep=5pt,
247   3  | breaklines=false,
248   4  | basicstyle=\footnotesize\ttfamily,
249   5  | numberstyle=\tiny,
250   6  | language=C,
251   7  | columns=fullflexible,
252   8  | numberfirstline=true
253   9  | }
254      |________________________________________________________________________
255 not used 
258 4.4 Configuring the build script 
259 You can invoke code extraction and building from the L Y X menu option Document->Build Program.
260 First, make sure you don't have a conversion defined for Lyx->Program
261 From the menu Tools->Preferences, add a conversion from Latex(Plain)->Program as:
262 set -x ; fangle -Rlyx-build $$i | 
263   env LYX_b=$$b LYX_i=$$i LYX_o=$$o LYX_p=$$p LYX_r=$$r bash
264 (But don't cut-n-paste it from this document or you may be be pasting a multi-line string which will break your lyx preferences file). 
265 I hope that one day, L Y X will set these into the environment when calling the build script.
266 You may also want to consider adding options to this conversion...
267 parselog=/usr/share/lyx/scripts/listerrors
268 ...but if you do you will lose your stderr4. There is some bash plumbing to get a copy of stderr but this footnote is too small  ^4.
269 Now, a shell script chunk called lyx-build will be extracted and run whenever you choose the Document->Build Program menu item.
270 This document was originally managed using L Y X and lyx-build script for this document is shown here for historical reference. 
271 lyx -e latex fangle.lyx && \
272   fangle fangle.lyx > ./autoboot
273 This looks simple enough, but as mentioned, fangle has to be had from somewhere before it can be extracted.
274 4.4.1 ... 
275 When the lyx-build chunk is executed, the current directory will be a temporary directory, and LYX_SOURCE will refer to the tex file in this temporary directory. This is unfortunate as our makefile wants to run from the project directory where the Lyx file is kept.
276 We can extract the project directory from $$r, and derive the probable Lyx filename from the noweb file that Lyx generated.
278 19b <lyx-build-helper[1](\v), lang=sh> ≡  95b⊳
279       ________________________________________________________________________
280   1  | PROJECT_DIR="$LYX_r"
281   2  | LYX_SRC="$PROJECT_DIR/${LYX_i%.tex}.lyx"
282   3  | TEX_DIR="$LYX_p"
283   4  | TEX_SRC="$TEX_DIR/$LYX_i"
284      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 20a
285 And then we can define a lyx-build fragment similar to the autoboot fragment
287 20a <lyx-build[1](\v), lang=sh> ≡  95a⊳
288       ________________________________________________________________________
289   1  | #! /bin/sh
290   2  | «lyx-build-helper 19b»
291   3  | cd $PROJECT_DIR || exit 1
292   4  | 
293   5  | #/usr/bin/fangle -filter ./notanglefix-filter \
294   6  | #  -R./Makefile.inc "../../noweb-lyx/noweb-lyx3.lyx" \
295   7  | #  | sed '/NOWEB_SOURCE=/s/=.*/=samba4-dfs.lyx/' \
296   8  | #  > ./Makefile.inc
297   9  | #
298   10  | #make -f ./Makefile.inc fangle_sources
299      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\not used
301 Chapter 5Using Fangle with TeXmacs
302 To do: Write this chapter
304 Chapter 6Fangle with Makefiles
305 Here we describe a Makefile.inc that you can include in your own Makefiles, or glue as a recursive make to other projects.
306 Makefile.inc will cope with extracting all the other source files from this or any specified literate document and keeping them up to date. 
307 It may also be included by a Makefile or Makefile.am defined in a literate document to automatically deal with the extraction of source files and documents during normal builds.
308 Thus, if Makefile.inc is included into a main project makefile it add rules for the source files, capable of extracting the source files from the literate document.
309 6.1 A word about makefiles formats 
310 Whitespace formatting is very important in a Makefile. The first character of each action line must be a TAB. 
311 target: pre-requisite
312 ↦action
313 ↦action
314 This requires that the literate programming environment have the ability to represent a TAB character in a way that fangle will generate an actual TAB character.
315 We also adopt a convention that code chunks whose names beginning with ./ should always be automatically extracted from the document. Code chunks whose names do not begin with ./ are for internal reference. Such chunks may be extracted directly, but will not be automatically extracted by this Makefile.
316 6.2 Extracting Sources 
317 Our makefile has two parts; variables must be defined before the targets that use them.
318 As we progress through this chapter, explaining concepts, we will be adding lines to <Makefile.inc-vars 23b> and <Makefile.inc-targets 24c> which are included in <./Makefile.inc 23a> below.
320 23a <./Makefile.inc[1](\v), lang=make> ≡ 
321       ________________________________________________________________________
322   1  | «Makefile.inc-vars 23b»
323   2  | «Makefile.inc-default-targets 28a»
324   3  | «Makefile.inc-targets 24c»
325      |________________________________________________________________________
326 not used 
328 We first define a placeholder for the tool fangle in case it cannot be found in the path.
330 23b <Makefile.inc-vars[1](\v), lang=make> ≡  24a⊳
331       ________________________________________________________________________
332   1  | FANGLE=fangle
333   2  | AWK=awk
334   3  | RUN_FANGLE=$(AWK) -f $(FANGLE)
335      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 23a
336 We also define a placeholder for LITERATE_SOURCE to hold the name of this document. This will normally be passed on the command line or set by the including makefile.
338 24a <Makefile.inc-vars[2](\v) ⇑23b, lang=> +≡ ⊲23b 24b▿
339      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
340   4  | #LITERATE_SOURCE=
341      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
342 Fangle cannot process L Y X or TeXmacs documents directly, so the first stage is to convert these to more suitable text based formats1. L Y X and TeXmacs formats are text-based, but not suitable for fangle  ^1.
343 6.2.1 Converting from L Y X to LaTeX 
344 The first stage will always be to convert the L Y X file to a LaTeX file. Fangle must run on a TeX file because the L Y X command server-goto-file-line2. The Lyx command server-goto-file-line is used to position the Lyx cursor at the compiler errors.  ^2 requries that the line number provided be a line of the TeX file and always maps this the line in the L Y X docment. We use server-goto-file-line when moving the cursor to error lines during compile failures.
345 The command lyx -e literate fangle.lyx will produce fangle.tex, a TeX file; so we define a make target to be the same as the L Y X file but with the .tex extension.
346 The EXTRA_DIST is for automake support so that the TeX files will automaticaly be distributed with the source, to help those who don't have L Y X installed.
348 24b <Makefile.inc-vars[3](\v) ⇑23b, lang=> +≡ ▵24a 24d▿
349      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
350   5  | LYX_SOURCE=$(LITERATE_SOURCE) # but only the .lyx files
351   6  | TEX_SOURCE=$(LYX_SOURCE:.lyx=.tex)
352   7  | EXTRA_DIST+=$(TEX_SOURCE)
353      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
354 We then specify that the TeX source is to be generated from the L Y X source.
356 24c <Makefile.inc-targets[1](\v), lang=make> ≡  25a⊳
357       ________________________________________________________________________
358   1  | .SUFFIXES: .tex .lyx
359   2  | .lyx.tex:
360   3  | ↦lyx -e latex $<
361   4  | clean_tex:
362   5  | ↦rm -f -- $(TEX_SOURCE)
363   6  | clean: clean_tex
364      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 23a
365 6.2.2 Converting from TeXmacs 
366 Fangle cannot process TeXmacs files directly3. but this is planned when TeXmacs uses xml as it's native format  ^3, but must first convert them to text files.
367 The command texmacs -c fangle.tm fangle.txt -q will produce fangle.txt, a text file; so we define a make target to be the same as the TeXmacs file but with the .txt extension.
368 The EXTRA_DIST is for automake support so that the TeX files will automaticaly be distributed with the source, to help those who don't have L Y X installed.
370 24d <Makefile.inc-vars[4](\v) ⇑23b, lang=> +≡ ▵24b 25b⊳
371      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
372   8  | TEXMACS_SOURCE=$(LITERATE_SOURCE) # but only the .tm files
373   9  | TXT_SOURCE=$(LITERATE_SOURCE:.tm=.txt)
374   10  | EXTRA_DIST+=$(TXT_SOURCE)
375      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
376 To do: Add loop around each $< so multiple targets can be specified
379 25a <Makefile.inc-targets[2](\v) ⇑24c, lang=> +≡ ⊲24c 25d▿
380      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
381   7  | .SUFFIXES: .txt .tm
382   8  | .tm.txt:
383   9  | ↦texmacs -s -c $< $@ -q
384   10  | .PHONEY: clean_txt
385   11  | clean_txt:
386   12  | ↦rm -f -- $(TXT_SOURCE)
387   13  | clean: clean_txt
388      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
389 6.3 Extracting Program Source 
390 The program source is extracted using fangle, which is designed to operate on text or a LaTeX documents4. LaTeX documents are just slightly special text documents  ^4.
392 25b <Makefile.inc-vars[5](\v) ⇑23b, lang=> +≡ ⊲24d 25c▿
393      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
394   11  | FANGLE_SOURCE=$(TXT_SOURCE)
395      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
396 The literate document can result in any number of source files, but not all of these will be changed each time the document is updated. We certainly don't want to update the timestamps of these files and cause the whole source tree to be recompiled just because the literate explanation was revised. We use CPIF from the Noweb tools to avoid updating the file if the content has not changed, but should probably write our own.
397 However, if a source file is not updated, then the fangle file will always have a newer time-stamp and the makefile would always re-attempt to extact a newer source file which would be a waste of time.
398 Because of this, we use a stamp file which is always updated each time the sources are fully extracted from the LaTeX document. If the stamp file is newer than the document, then we can avoid an attempt to re-extract any of the sources. Because this stamp file is only updated when extraction is complete, it is safe for the user to interrupt the build-process mid-extraction.
399 We use echo rather than touch to update the stamp file beause the touch command does not work very well over an sshfs mount  that I was using.
401 25c <Makefile.inc-vars[6](\v) ⇑23b, lang=> +≡ ▵25b 26a⊳
402      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
403   12  | FANGLE_SOURCE_STAMP=$(FANGLE_SOURCE).stamp
404      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
406 25d <Makefile.inc-targets[3](\v) ⇑24c, lang=> +≡ ▵25a 26b⊳
407      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
408   14  | $(FANGLE_SOURCE_STAMP): $(FANGLE_SOURCE) \
409   15  | ↦                $(FANGLE_SOURCES) ; \
410   16  | ↦echo -n > $(FANGLE_SOURCE_STAMP)
411   17  | clean_stamp:
412   18  | ↦rm -f $(FANGLE_SOURCE_STAMP)
413   19  | clean: clean_stamp
414      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
415 6.4 Extracting Source Files 
416 We compute FANGLE_SOURCES to hold the names of all the source files defined in the document. We compute this only once, by means of := in assignent. The sed deletes the any << and >> which may surround the roots names (for compatibility with Noweb's noroots command).
417 As we use chunk names beginning with ./ to denote top level fragments that should be extracted, we filter out all fragments that do not begin with ./
418 Note 1. FANGLE_PREFIX is set to ./ by default, but whatever it may be overridden to, the prefix is replaced by a literal ./ before extraction so that files will be extracted in the current directory whatever the prefix. This helps namespace or sub-project prefixes like documents: for chunks like documents:docbook/intro.xml 
419 To do: This doesn't work though, because it loses the full name and doesn't know what to extact!
422 26a <Makefile.inc-vars[7](\v) ⇑23b, lang=> +≡ ⊲25c 26e▿
423      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
424   13  | FANGLE_PREFIX:=\.\/
425   14  | FANGLE_SOURCES:=$(shell \
426   15  |   $(RUN_FANGLE) -r $(FANGLE_SOURCE) |\
427   16  |   sed -e 's/^[<][<]//;s/[>][>]$$//;/^$(FANGLE_PREFIX)/!d' \
428   17  |       -e 's/^$(FANGLE_PREFIX)/\.\//' )
429      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
430 The target below, echo_fangle_sources is a helpful debugging target and shows the names of the files that would be extracted.
432 26b <Makefile.inc-targets[4](\v) ⇑24c, lang=> +≡ ⊲25d 26c▿
433      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
434   20  | .PHONY: echo_fangle_sources
435   21  | echo_fangle_sources: ; @echo $(FANGLE_SOURCES)
436      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
437 We define a convenient target called fangle_sources so that make -f fangle_sources will re-extract the source if the literate document has been updated. 
439 26c <Makefile.inc-targets[5](\v) ⇑24c, lang=> +≡ ▵26b 26d▿
440      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
441   22  | .PHONY: fangle_sources
442   23  | fangle_sources: $(FANGLE_SOURCE_STAMP)
443      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
444 And also a convenient target to remove extracted sources.
446 26d <Makefile.inc-targets[6](\v) ⇑24c, lang=> +≡ ▵26c 27e⊳
447      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
448   24  | .PHONY: clean_fangle_sources
449   25  | clean_fangle_sources: ; \
450   26  |         rm -f -- $(FANGLE_SOURCE_STAMP) $(FANGLE_SOURCES)
451      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
452 We now look at the extraction of the source files.
453 This makefile macro if_extension takes 4 arguments: the filename $(1), some extensions to match $(2) and a shell command to return if the filename does match the exensions $(3), and a shell command to return if it does not match the extensions $(4).
455 26e <Makefile.inc-vars[8](\v) ⇑23b, lang=> +≡ ▵26a 26f⊳
456      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
457   18  | if_extension=$(if $(findstring $(suffix $(1)),$(2)),$(3),$(4))
458      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
459 For some source files like C files, we want to output the line number and filename of the original LaTeX document from which the source came5. I plan to replace this option with a separate mapping file so as not to pollute the generated source, and also to allow a code pretty-printing reformatter like indent be able to re-format the file and adjust for changes through comparing the character streams.  ^5.
460 To make this easier we define the file extensions for which we want to do this.
462 27a <Makefile.inc-vars[9](\v) ⇑23b, lang=> +≡ ⊲26e 27a▿
463      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
464   19  | C_EXTENSIONS=.c .h
465      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
466 We can then use the if_extensions macro to define a macro which expands out to the -L option if fangle is being invoked in a C source file, so that C compile errors will refer to the line number in the TeX document. 
468 27b <Makefile.inc-vars[10](\v) ⇑23b, lang=> +≡ ▵26f 27b▿
469      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
470   20  | TABS=8
471   21  | nf_line=-L -T$(TABS)
472   22  | fangle=$(RUN_FANGLE) $(call if_extension,$(2),$(C_EXTENSIONS),$(nf_line)) -R"$(2)" $(1)
473      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
474 We can use a similar trick to define an indent macro which takes just the filename as an argument and can return a pipeline stage calling the indent command. Indent can be turned off with make fangle_sources indent=
476 27c <Makefile.inc-vars[11](\v) ⇑23b, lang=> +≡ ▵27a 27c▿
477      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
478   23  | indent_options=-npro -kr -i8 -ts8 -sob -l80 -ss -ncs
479   24  | indent=$(call if_extension,$(1),$(C_EXTENSIONS), | indent $(indent_options))
480      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
481 We now define the pattern for extracting a file. The files are written using noweb's cpif so that the file timestamp will not be touched if the contents haven't changed. This avoids the need to rebuild the entire project because of a typographical change in the documentation, or if none or a few C source files have changed.
483 27d <Makefile.inc-vars[12](\v) ⇑23b, lang=> +≡ ▵27b 27d▿
484      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
485   25  | fangle_extract=@mkdir -p $(dir $(1)) && \
486   26  |   $(call fangle,$(2),$(1)) > "$(1).tmp" && \
487   27  |   cat "$(1).tmp" $(indent) | cpif "$(1)" \
488   28  |   && rm -f -- "$(1).tmp" || \
489   29  |   (echo error fangling $(1) from $(2) ; exit 1)
490      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
491 We define a target which will extract or update all sources. To do this we first defined a makefile template that can do this for any source file in the LaTeX document.
493 27e <Makefile.inc-vars[13](\v) ⇑23b, lang=> +≡ ▵27c 28b⊳
494      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
495   30  | define FANGLE_template
496   31  |   $(1): $(2)
497   32  | ↦$$(call fangle_extract,$(1),$(2))
498   33  |   FANGLE_TARGETS+=$(1)
499   34  | endef
500      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
501 We then enumerate the discovered FANGLE_SOURCES to generate a makefile rule for each one using the makefile template we defined above.
503 27f <Makefile.inc-targets[7](\v) ⇑24c, lang=> +≡ ⊲26d 27f▿
504      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
505   27  | $(foreach source,$(FANGLE_SOURCES),\
506   28  |   $(eval $(call FANGLE_template,$(source),$(FANGLE_SOURCE))) \
507   29  | )
508      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
509 These will all be built with FANGLE_SOURCE_STAMP.
510 We also remove the generated sources on a make distclean.
512 27g <Makefile.inc-targets[8](\v) ⇑24c, lang=> +≡ ▵27e 28c⊳
513      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
514   30  | _distclean: clean_fangle_sources
515      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
516 6.5 Extracting Documentation 
517 We then identify the intermediate stages of the documentation and their build and clean targets.
519 28a <Makefile.inc-default-targets[1](\v), lang=> ≡ 
520       ________________________________________________________________________
521   1  | .PHONEY : clean_pdf
522      |________________________________________________________________________
523 Used by 23a 
525 6.5.1 Formatting TeX 
526 6.5.1.1 Running pdflatex 
527 We produce a pdf file from the tex file.
529 28b <Makefile.inc-vars[14](\v) ⇑23b, lang=> +≡ ⊲27d 28d▿
530      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
531   35  | FANGLE_PDF+=$(TEX_SOURCE:.tex=.pdf)
532      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
533 We run pdflatex twice to be sure that the contents and aux files are up to date. We certainly are required to run pdflatex at least twice if these files do not exist.
535 28c <Makefile.inc-targets[9](\v) ⇑24c, lang=> +≡ ⊲27f 28e▿
536      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
537   31  | .SUFFIXES: .tex .pdf
538   32  | .tex.pdf:
539   33  | ↦pdflatex $< && pdflatex $<
540   34  | 
541   35  | clean_pdf_tex:
542   36  | ↦rm -f -- $(FANGLE_PDF) $(TEX_SOURCE:.tex=.toc) \
543   37  | ↦  $(TEX_SOURCE:.tex=.log) $(TEX_SOURCE:.tex=.aux)
544   38  | clean_pdf: clean_pdf_tex
545      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
546 6.5.2 Formatting TeXmacs 
547 TeXmacs can produce a PDF file directly.
549 28d <Makefile.inc-vars[15](\v) ⇑23b, lang=> +≡ ▵28b 28f⊳
550      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
551   36  | FANGLE_PDF+=$(LITERATE_SOURCE:.tm=.pdf)
552      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
553 To do: Outputting the PDF may not be enough to update the links and page references. I think
554 we need to update twice, generate a pdf, update twice mode and generate a new PDF.
555 Basically the PDF export of TeXmacs is pretty rotten and doesn't work properly from the CLI
558 28e <Makefile.inc-targets[10](\v) ⇑24c, lang=> +≡ ▵28c 29a⊳
559      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
560   39  | .SUFFIXES: .tm .pdf
561   40  | .tm.pdf:
562   41  | ↦texmacs -s -c $< $@ -q
563   42  | 
564   43  | clean_pdf_texmacs:
565   44  | ↦rm -f -- $(FANGLE_PDF)
566   45  | clean_pdf: clean_pdf_texmacs
567      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
568 6.5.3 Building the Documentation as a Whole 
569 Currently we only build pdf as a final format, but FANGLE_DOCS may later hold other output formats.
571 29a <Makefile.inc-vars[16](\v) ⇑23b, lang=> +≡ ⊲28d
572      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
573   37  | FANGLE_DOCS=$(FANGLE_PDF)
574      |________________________________________________________________________
577 We also define fangle_docs as a convenient phony target.
579 29b <Makefile.inc-targets[11](\v) ⇑24c, lang=> +≡ ⊲28e 29b▿
580      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
581   46  | .PHONY: fangle_docs
582   47  | fangle_docs: $(FANGLE_DOCS)
583   48  | docs: fangle_docs
584      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
585 And define a convenient clean_fangle_docs which we add to the regular clean target
587 29c <Makefile.inc-targets[12](\v) ⇑24c, lang=> +≡ ▵29a
588      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
589   49  | .PHONEY: clean_fangle_docs
590   50  | clean_fangle_docs: clean_tex clean_pdf
591   51  | clean: clean_fangle_docs
592   52  | 
593   53  | distclean_fangle_docs: clean_tex clean_fangle_docs
594   54  | distclean: clean distclean_fangle_docs
595      |________________________________________________________________________
598 6.6 Other helpers 
599 If Makefile.inc is included into Makefile, then extracted files can be updated with this command:
600 make fangle_sources
601 otherwise, with:
602 make -f Makefile.inc fangle_sources
603 6.7 Boot-strapping the extraction 
604 As well as having the makefile extract or update the source files as part of it's operation, it also seems convenient to have the makefile re-extracted itself from this document.
605 It would also be convenient to have the code that extracts the makefile from this document to also be part of this document, however we have to start somewhere and this unfortunately requires us to type at least a few words by hand to start things off.
606 Therefore we will have a minimal root fragment, which, when extracted, can cope with extracting the rest of the source. This shell script fragment can do that. It's name is * — out of regard for Noweb, but when extracted might better be called autoupdate.
607 To do: De-lyxify
610 29d <*[1](\v), lang=sh> ≡ 
611       ________________________________________________________________________
612   1  | #! /bin/sh
613   2  | 
614   3  | MAKE_SRC="${1:-${NW_LYX:-../../noweb-lyx/noweb-lyx3.lyx}}"
615   4  | MAKE_SRC=‘dirname "$MAKE_SRC"‘/‘basename "$MAKE_SRC" .lyx‘
616   5  | NOWEB_SRC="${2:-${NOWEB_SRC:-$MAKE_SRC.lyx}}"
617   6  | lyx -e latex $MAKE_SRC
618   7  | 
619   8  | fangle -R./Makefile.inc ${MAKE_SRC}.tex \
620   9  |   | sed "/FANGLE_SOURCE=/s/^/#/;T;aNOWEB_SOURCE=$FANGLE_SRC" \
621   10  |   | cpif ./Makefile.inc
622   11  | 
623   12  | make -f ./Makefile.inc fangle_sources
624      |________________________________________________________________________
625 not used 
627 The general Makefile can be invoked with ./autoboot and can also be included into any automake file to automatically re-generate the source files.
628 The autoboot can be extracted with this command:
629 lyx -e latex fangle.lyx && \
630   fangle fangle.lyx > ./autoboot
631 This looks simple enough, but as mentioned, fangle has to be had from somewhere before it can be extracted.
632 On a unix system this will extract fangle.module and the fangle awk script, and run some basic tests. 
633 To do: cross-ref to test chapter when it is a chapter all on its own
635 6.8 Incorporating Makefile.inc into existing projects 
636 If you are writing a literate module of an existing non-literate program you may find it easier to use a slight recursive make instead of directly including Makefile.inc in the projects makefile. 
637 This way there is less chance of definitions in Makefile.inc interfering with definitions in the main makefile, or with definitions in other Makefile.inc from other literate modules of the same project.
638 To do this we add some glue to the project makefile that invokes Makefile.inc in the right way. The glue works by adding a .PHONY target to call the recursive make, and adding this target as an additional pre-requisite to the existing targets.
639 Example Sub-module of existing system
640 In this example, we are building module.so as a literate module of a larger project.
641 We will show the sort glue that can be inserted into the projects Makefile — or more likely — a regular Makefile included in or invoked by the projects Makefile.
643 30a <makefile-glue[1](\v), lang=> ≡  30b▿
644       ________________________________________________________________________
645   1  | module_srcdir=modules/module
646   2  | MODULE_SOURCE=module.tm
647   3  | MODULE_STAMP=$(MODULE_SOURCE).stamp
648      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\not used
649 The existing build system may already have a build target for module.o, but we just add another pre-requisite to that. In this case we use module.tm.stamp as a pre-requisite, the stamp file's modified time indicating when all sources were extracted6. If the projects build system does not know how to build the module from the extracted sources, then just add build actions here as normal.  ^6.
651 30b <makefile-glue[2](\v) ⇑30a, lang=make> +≡ ▵30a 30c▿
652      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
653   4  | $(module_srcdir)/module.o: $(module_srcdir)/$(MODULE_STAMP)
654      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
655 The target for this new pre-requisite will be generated by a recursive make using Makefile.inc which will make sure that the source is up to date, before it is built by the main projects makefile.
657 30c <makefile-glue[3](\v) ⇑30a, lang=> +≡ ▵30b 31a⊳
658      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
659   5  | $(module_srcdir)/$(MODULE_STAMP): $(module_srcdir)/$(MODULE_SOURCE)
660   6  | ↦$(MAKE) -C $(module_srcdir) -f Makefile.inc fangle_sources LITERATE_SOURCE=$(MODULE_SOURCE)
661      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
662 We can do similar glue for the docs, clean and distclean targets. In this example the main prject was using a double colon for these targets, so we must use the same in our glue.
664 31a <makefile-glue[4](\v) ⇑30a, lang=> +≡ ⊲30c
665      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
666   7  | docs:: docs_module
667   8  | .PHONY: docs_module
668   9  | docs_module:
669   10  | ↦$(MAKE) -C $(module_srcdir) -f Makefile.inc docs LITERATE_SOURCE=$(MODULE_SOURCE)
670   11  | 
671   12  | clean:: clean_module
672   13  | .PHONEY: clean_module
673   14  | clean_module:
674   15  | ↦$(MAKE) -C $(module_srcdir) -f Makefile.inc clean LITERATE_SOURCE=$(MODULE_SOURCE)
675   16  | 
676   17  | distclean:: distclean_module
677   18  | .PHONY: distclean_module
678   19  | distclean_module:
679   20  | ↦$(MAKE) -C $(module_srcdir) -f Makefile.inc distclean LITERATE_SOURCE=$(MODULE_SOURCE)
680      |________________________________________________________________________
683 We could do similarly for install targets to install the generated docs.
684 Part II  Source Code 
685 Chapter 7Fangle Makefile
686 We use the copyright notice from chapter 2, and the Makefile.inc from chapter 6
688 35a <./Makefile[1](\v), lang=make> ≡ 
689       ________________________________________________________________________
690   1  | # «gpl3-copyright 4a»
691   2  | 
692   3  | «make-fix-make-shell 55c»
693   4  | 
694   5  | LITERATE_SOURCE=fangle.tm
695   6  | BINDIR=/usr/local/bin
696   7  | TEXMACS_DIR=/usr/share/texmacs/TeXmacs
697   8  | LYX_DIR=/usr/share/lyx
698   9  | 
699   10  | all: fangle_sources
700   11  | include Makefile.inc
701   12  | 
702   13  | fangle: test
703   14  | ./fangle: test
704   15  | 
705   16  | .PHONEY: test
706   17  | test: fangle.txt
707   18  | ↦$(RUN_FANGLE) -R"test:*" fangle.txt > test.sh
708   19  | ↦bash test.sh ; echo pass $$?
709   20  | 
710   21  | install-local: BINDIR=$$HOME/.local/bin
711   22  | install-local: TEXMACS_DIR=$$HOME/.TeXmacs
712   23  | install-local: LYX_DIR=$$HOME/.lyx
713   24  | install-local: install
714   25  | .PHONEY: install-local
715   26  | 
716   27  | install-system: install
717   28  | .PHONEY: install-local
718   29  | 
719   30  | install:
720   31  | ↦test -n "$(BINDIR)" -a -n "$(TEXMACS_DIR)"
721   32  | ↦mkdir -p "$(BINDIR)"
722   33  | ↦install fangle "$(BINDIR)"
723   34  | ↦mkdir -p "$(TEXMACS_DIR)/plugins/fangle"
724   35  | ↦mkdir -p "$(TEXMACS_DIR)/plugins/fangle/packages"
725   36  | ↦install fangle.ts "$(TEXMACS_DIR)/plugins/fangle/packages"
726   37  | ↦mkdir -p "$(TEXMACS_DIR)/plugins/fangle/progs"
727   38  | ↦install init-fangle.scm "$(TEXMACS_DIR)/plugins/fangle/progs"
728   39  | ↦mkdir -p "$(LYX_DIR)/modules"
729   40  | ↦install fangle.module "$(LYX_DIR)/modules"
730   41  | .PHONEY: install
731      |________________________________________________________________________
732 not used 
734 Chapter 8Fangle awk source code
735 We use the copyright notice from chapter 2.
737 37a <./fangle[1](\v), lang=awk> ≡  37b▿
738       ________________________________________________________________________
739   1  | #! /usr/bin/awk -f
740   2  | # «gpl3-copyright 4a»
741      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\not used
742 We also use code from Arnold Robbins public domain getopt (1993 revision) defined in 85a, and naturally want to attribute this appropriately.
744 37b <./fangle[2](\v) ⇑37a, lang=> +≡ ▵37a 37c▿
745      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
746   3  | # NOTE: Arnold Robbins public domain getopt for awk is also used:
747   4  | «getopt.awk-header 83a»
748   5  | «getopt.awk-getopt() 83c»
749   6  | 
750      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
751 And include the following chunks (which are explained further on) to make up the program:
753 37c <./fangle[3](\v) ⇑37a, lang=> +≡ ▵37b 42a⊳
754      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
755   7  | «helper-functions 38d»
756   8  | «mode-tracker 62b»
757   9  | «parse_chunk_args 44a»
758   10  | «chunk-storage-functions 81b»
759   11  | «output_chunk_names() 75d»
760   12  | «output_chunks() 75e»
761   13  | «write_chunk() 76a»
762   14  | «expand_chunk_args() 44b»
763   15  | 
764   16  | «begin 73d»
765   17  | «recognize-chunk 65a»
766   18  | «end 75c»
767      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
768 8.1 AWK tricks 
769 The portable way to erase an array in awk is to split the empty string, so we define a fangle macro that can split an array, like this:
771 37d <awk-delete-array[1](ARRAY\v\v), lang=awk> ≡ 
772       ________________________________________________________________________
773   1  | split("", ${ARRAY});
774      |________________________________________________________________________
775 Used by 58b, 76a 
777 For debugging it is sometimes convenient to be able to dump the contents of an array to stderr, and so this macro is also useful.
779 37e <dump-array[1](ARRAY\v\v), lang=awk> ≡ 
780       ________________________________________________________________________
781   1  | print "\nDump: ${ARRAY}\n--------\n" > "/dev/stderr";
782   2  | for (_x in ${ARRAY}) {
783   3  |   print _x "=" ${ARRAY}[_x] "\n" > "/dev/stderr";
784   4  | }
785   5  | print "========\n" > "/dev/stderr";
786      |________________________________________________________________________
787 not used 
789 8.2 Catching errors 
790 Fatal errors are issued with the error function:
792 38a <error()[1](\v), lang=awk> ≡  38b▿
793       ________________________________________________________________________
794   1  | function error(message)
795   2  | {
796   3  |   print "ERROR: " FILENAME ":" FNR " " message > "/dev/stderr";
797   4  |   exit 1;
798   5  | }
799      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 38d, 62c
800 and likewise for non-fatal warnings:
802 38b <error()[2](\v) ⇑38a, lang=awk> +≡ ▵38a 38c▿
803      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
804   6  | function warning(message)
805   7  | {
806   8  |   print "WARNING: " FILENAME ":" FNR " " message > "/dev/stderr";
807   9  |   warnings++;
808   10  | }
809      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
810 and debug output too:
812 38c <error()[3](\v) ⇑38a, lang=awk> +≡ ▵38b
813      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
814   11  | function debug_log(message)
815   12  | {
816   13  |   print "DEBUG: " FILENAME ":" FNR " " message > "/dev/stderr";
817   14  | }
818      |________________________________________________________________________
821 To do: append=helper-functions
824 38d <helper-functions[1](\v), lang=> ≡ 
825       ________________________________________________________________________
826   1  | «error() 38a»
827      |________________________________________________________________________
828 Used by 37a 
830 Chapter 9TeXmacs args
831 TeXmacs functions with arguments1. or function declarations with parameters  ^1 appear like this:
832 blah((I came, I saw, I conquered)<wide-overbrace>^(argument 1)(^K, )<wide-overbrace>^(sep.)(and then went home asd)<wide-overbrace>^(argument 3)(^K))<wide-overbrace>^(term.)_arguments
833 Arguments commence after the opening parenthesis. The first argument runs up till the next ^K. 
834 If the following character is a , then another argument follows. If the next character after the , is a space character, then it is also eaten. The fangle stylesheet emits ^K,Space as separators, but the fangle untangler will forgive a missing space.
835 If the following character is ) then this is a terminator and there are no more arguments.
837 39a <constants[1](\v), lang=> ≡  81a⊳
838       ________________________________________________________________________
839   1  | ARG_SEPARATOR=sprintf("%c", 11);
840      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 73d
841 To process the text in this fashion, we split the string on ^K
844 39b <get_chunk_args[1](\v), lang=> ≡ 
845       ________________________________________________________________________
846   1  | function get_texmacs_chunk_args(text, args,   a, done) {
847   2  |   split(text, args, ARG_SEPARATOR);
848   3  | 
849   4  |   done=0
850   5  |   for (a=1; (a in args); a++) if (a>1) {
851   6  |     if (args[a] == "" || substr(args[a], 1, 1) == ")") done=1;
852   7  |     if (done) {
853   8  |       delete args[a];
854   9  |       break;
855   10  |     }
856   11  | 
857   12  |     if (substr(args[a], 1, 2) == ", ") args[a]=substr(args[a], 3);
858   13  |     else if (substr(args[a], 1, 1) == ",") args[a]=substr(args[a], 2);  
859   14  |   }
860   15  | }
861      |________________________________________________________________________
862 not used 
864 Chapter 10LaTeX and lstlistings
865 To do: Split LyX and TeXmacs parts
867 For L Y X and LaTeX, the lstlistings package is used to format the lines of code chunks. You may recal from chapter XXX that arguments to a chunk definition are pure LaTeX code. This means that fangle needs to be able to parse LaTeX a little.
868 LaTeX arguments to lstlistings macros are a comma seperated list of key-value pairs, and values containing commas are enclosed in { braces } (which is to be expected for LaTeX).
869 A sample expressions is:
870 name=thomas, params={a, b}, something, something-else
871 but we see that this is just a simpler form of this expression:
872 name=freddie, foo={bar=baz, quux={quirk, a=fleeg}}, etc
873 We may consider that we need a function that can parse such LaTeX expressions and assign the values to an AWK associated array, perhaps using a recursive parser into a multi-dimensional hash1. as AWK doesn't have nested-hash support  ^1, resulting in:
874 key                 value
875 a[name]             freddie
876 a[foo, bar]         baz
877 a[foo, quux, quirk] 
878 a[foo, quux, a]     fleeg
879 a[etc]              
881 Yet, also, on reflection it seems that sometimes such nesting is not desirable, as the braces are also used to delimit values that contain commas --- we may consider that
882 name={williamson, freddie}
883 should assign williamson, freddie to name.
884 In fact we are not so interested in the detail so as to be bothered by this, which turns out to be a good thing for two reasons. Firstly TeX has a malleable parser with no strict syntax, and secondly whether or not williamson and freddie should count as two items will be context dependant anyway.
885 We need to parse this latex for only one reason; which is that we are extending lstlistings to add some additional arguments which will be used to express chunk parameters and other chunk options.
886 10.1 Additional lstlstings parameters 
887 Further on we define a \Chunk LaTeX macro whose arguments will consist of a the chunk name, optionally followed by a comma and then a comma separated list of arguments. In fact we will just need to prefix name= to the arguments to in order to create valid lstlistings arguments. 
888 There will be other arguments supported too; 
889 params.As an extension to many literate-programming styles, fangle permits code chunks to take parameters and thus operate somewhat like C pre-processor macros, or like C++ templates. Chunk parameters are declared with a chunk argument called params, which holds a semi-colon separated list of parameters, like this:
890 achunk,language=C,params=name;address
891 addto.a named chunk that this chunk is to be included into. This saves the effort of having to declare another listing of the named chunk merely to include this one. 
892 Function get_chunk_args() will accept two paramters, text being the text to parse, and values being an array to receive the parsed values as described above. The optional parameter path is used during recursion to build up the multi-dimensional array path.
894 42a <./fangle[4](\v) ⇑37a, lang=> +≡ ⊲37c
895      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
896   19  | «get_chunk_args() 42b»
897      |________________________________________________________________________
901 42b <get_chunk_args()[1](\v), lang=> ≡  42c▿
902       ________________________________________________________________________
903   1  | function get_tex_chunk_args(text, values,
904   2  |   # optional parameters
905   3  |   path, # hierarchical precursors
906   4  |   # local vars
907   5  |   a, name)
908      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 37a, 43b
909 The strategy is to parse the name, and then look for a value. If the value begins with a brace {, then we recurse and consume as much of the text as necessary, returning the remaining text when we encounter a leading close-brace }. This being the strategy --- and executed in a loop --- we realise that we must first look for the closing brace (perhaps preceded by white space) in order to terminate the recursion, and returning remaining text.
911 42c <get_chunk_args()[2](\v) ⇑42b, lang=> +≡ ▵42b
912      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
913   6  | {
914   7  |   split("", values);
915   8  |   while(length(text)) {
916   9  |     if (match(text, "^ *}(.*)", a)) {
917   10  |       return a[1];
918   11  |     }
919   12  |     «parse-chunk-args 42d»
920   13  |   }
921   14  |   return text;
922   15  | }
923      |________________________________________________________________________
926 We can see that the text could be inspected with this regex:
928 42d <parse-chunk-args[1](\v), lang=> ≡  43a⊳
929       ________________________________________________________________________
930   1  | if (! match(text, " *([^,=]*[^,= ]) *(([,=]) *(([^,}]*) *,* *(.*))|)$", a)) {
931   2  |   return text;
932   3  | }
933      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 42b
934 and that a will have the following values:
935 a[n] assigned text
936 1    freddie
937 2    =freddie, foo={bar=baz, quux={quirk, a=fleeg}}, etc
938 3    =
939 4    freddie, foo={bar=baz, quux={quirk, a=fleeg}}, etc
940 5    freddie
941 6    , foo={bar=baz, quux={quirk, a=fleeg}}, etc
943 a[3] will be either = or , and signify whether the option named in a[1] has a value or not (respectively).
944 If the option does have a value, then if the expression substr(a[4],1,1) returns a brace { it will signify that we need to recurse:
946 43a <parse-chunk-args[2](\v) ⇑42d, lang=> +≡ ⊲42d
947      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
948   4  | name=a[1];
949   5  | if (a[3] == "=") {
950   6  |   if (substr(a[4],1,1) == "{") {
951   7  |     text = get_tex_chunk_args(substr(a[4],2), values, path name SUBSEP);
952   8  |   } else {
953   9  |     values[path name]=a[5];
954   10  |     text = a[6];
955   11  |   }
956   12  | } else {
957   13  |   values[path name]="";
958   14  |   text = a[2];
959   15  | }
960      |________________________________________________________________________
963 We can test this function like this:
965 43b <gca-test.awk[1](\v), lang=> ≡ 
966       ________________________________________________________________________
967   1  | «get_chunk_args() 42b»
968   2  | BEGIN {
969   3  |   SUBSEP=".";
970   4  | 
971   5  |   print get_tex_chunk_args("name=freddie, foo={bar=baz, quux={quirk, a=fleeg}}, etc", a);
972   6  |   for (b in a) {
973   7  |     print "a[" b "] => " a[b];
974   8  |   }
975   9  | }
976      |________________________________________________________________________
977 not used 
979 which should give this output:
981 43c <gca-test.awk-results[1](\v), lang=> ≡ 
982       ________________________________________________________________________
983   1  | a[foo.quux.quirk] => 
984   2  | a[foo.quux.a] => fleeg
985   3  | a[foo.bar] => baz
986   4  | a[etc] => 
987   5  | a[name] => freddie
988      |________________________________________________________________________
989 not used 
991 10.2 Parsing chunk arguments 
992 Arguments to paramterized chunks are expressed in round brackets as a comma separated list of optional arguments. For example, a chunk that is defined with:
993 \Chunk{achunk, params=name ; address}
994 could be invoked as:
995 \chunkref{achunk}(John Jones, jones@example.com)
996 An argument list may be as simple as in \chunkref{pull}(thing, otherthing) or as complex as:
997 \chunkref{pull}(things[x, y], get_other_things(a, "(all)"))
998 --- which for all it's commas and quotes and parenthesis represents only two parameters: things[x, y] and get_other_things(a, "(all)").
999 If we simply split parameter list on commas, then the comma in things[x,y] would split into two seperate arguments: things[x and y]--- neither of which make sense on their own.
1000 One way to prevent this would be by refusing to split text between matching delimiters, such as [, ], (, ), {, } and most likely also ", " and ', '. Of course this also makes it impossible to pass such mis-matched code fragments as parameters, but I think that it would be hard for readers to cope with authors who would pass such code unbalanced fragments as chunk parameters2. I know that I couldn't cope with users doing such things, and although the GPL3 license prevents me from actually forbidding anyone from trying, if they want it to work they'll have to write the code themselves and not expect any support from me.  ^2.
1001 Unfortunately, the full set of matching delimiters may vary from language to language. In certain C++ template contexts, < and > would count as delimiters, and yet in other contexts they would not.
1002 This puts me in the unfortunate position of having to parse-somewhat all programming languages without knowing what they are!
1003 However, if this universal mode-tracking is possible, then parsing the arguments would be trivial. Such a mode tracker is described in chapter 11 and used here with simplicity.
1005 44a <parse_chunk_args[1](\v), lang=> ≡ 
1006       ________________________________________________________________________
1007   1  | function parse_chunk_args(language, text, values, mode,
1008   2  |   # local vars
1009   3  |   c, context, rest)
1010   4  | {
1011   5  |   «new-mode-tracker\v(context\v, language\v, mode\v) 58b»
1012   6  |   rest = mode_tracker(context, text, values);
1013   7  |   # extract values
1014   8  |   for(c=1; c <= context[0, "values"]; c++) {
1015   9  |     values[c] = context[0, "values", c];
1016   10  |   }
1017   11  |   return rest;
1018   12  | }
1019      |________________________________________________________________________
1020 Used by 37a 
1022 10.3 Expanding parameters in the text 
1023 Within the body of the chunk, the parameters are referred to with: ${name} and ${address}. There is a strong case that a LaTeX style notation should be used, like \param{name} which would be expressed in the listing as =<\param{name}> and be rendered as ${name}. Such notation would make me go blind, but I do intend to adopt it.
1024 We therefore need a function expand_chunk_args which will take a block of text, a list of permitted parameters, and the arguments which must substitute for the parameters. 
1025 Here we split the text on ${ which means that all parts except the first will begin with a parameter name which will be terminated by }. The split function will consume the literal ${ in each case.
1027 44b <expand_chunk_args()[1](\v), lang=> ≡ 
1028       ________________________________________________________________________
1029   1  | function expand_chunk_args(text, params, args,  
1030   2  |   p, text_array, next_text, v, t, l)
1031   3  | {
1032   4  |   if (split(text, text_array, "\\${")) {
1033   5  |     «substitute-chunk-args 45a»
1034   6  |   }
1035   7  | 
1036   8  |   return text;
1037   9  | }
1038      |________________________________________________________________________
1039 Used by 37a 
1041 First, we produce an associative array of substitution values indexed by parameter names. This will serve as a cache, allowing us to look up the replacement values as we extract each name.
1043 45a <substitute-chunk-args[1](\v), lang=> ≡  45b▿
1044       ________________________________________________________________________
1045   1  | for(p in params) {
1046   2  |   v[params[p]]=args[p];
1047   3  | }
1048      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 44b
1049 We accumulate substituted text in the variable text. As the first part of the split function is the part before the delimiter --- which is ${ in our case --- this part will never contain a parameter reference, so we assign this directly to the result kept in $text.
1051 45b <substitute-chunk-args[2](\v) ⇑45a, lang=> +≡ ▵45a 45c▿
1052      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1053   4  | text=text_array[1];
1054      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1055 We then iterate over the remaining values in the array, and substitute each reference for it's argument.
1057 45c <substitute-chunk-args[3](\v) ⇑45a, lang=> +≡ ▵45b
1058      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1059   5  | for(t=2; t in text_array; t++) {
1060   6  |   «substitute-chunk-arg 45d»
1061   7  | }
1062      |________________________________________________________________________
1065 After the split on ${ a valid parameter reference will consist of valid parameter name terminated by a close-brace }. A valid character name begins with the underscore or a letter, and may contain letters, digits or underscores.
1066 A valid looking reference that is not actually the name of a parameter will be and not substituted. This is good because there is nothing to substitute anyway, and it avoids clashes when writing code for languages where ${...} is a valid construct --- such constructs will not be interfered with unless the parameter name also matches.
1068 45d <substitute-chunk-arg[1](\v), lang=> ≡ 
1069       ________________________________________________________________________
1070   1  | if (match(text_array[t], "^([a-zA-Z_][a-zA-Z0-9_]*)}", l) &&
1071   2  |     l[1] in v) 
1072   3  | {
1073   4  |   text = text v[l[1]] substr(text_array[t], length(l[1])+2);
1074   5  | } else {
1075   6  |   text = text "${" text_array[t];
1076   7  | }
1077      |________________________________________________________________________
1078 Used by 45a 
1080 Chapter 11Language Modes & Quoting
1081 lstlistings and fangle both recognize source languages, and perform some basic parsing and syntax highlighting in the rendered document1. although lstlisting supports many more languages  ^1. lstlistings can detect strings and comments within a language definition and perform suitable rendering, such as italics for comments, and visible-spaces within strings.
1082 Fangle similarly can recognize strings, and comments, etc, within a language, so that any chunks included with \chunkref{a-chunk} or <a-chunk ?> can be suitably escape or quoted.
1083 11.1 Modes explanation 
1084 As an example, the C language has a few parse modes, which affect the interpretation of characters.
1085 One parse mode is the string mode. The string mode is commenced by an un-escaped quotation mark " and terminated by the same. Within the string mode, only one additional mode can be commenced, it is the backslash mode \, which is always terminated by the following character.
1086 Another mode is [ which is terminated by a ] (unless it occurs in a string).
1087 Consider this fragment of C code:
1088 do_something((things([x, y])<wide-overbrace>^(2. [ mode), get_other_things((a, "(all)"_(4. " mode)))<wide-overbrace>^(3. ( mode)))<wide-overbrace>^(1. ( mode)
1090 Mode nesting prevents the close parenthesis in the quoted string (part 4) from terminating the parenthesis mode (part 3).
1091 Each language has a set of modes, the default mode being the null mode. Each mode can lead to other modes.
1092 11.2 Modes affect included chunks 
1093 For instance, consider this chunk with language=perl:
1095 47a <test:example-perl[1](\v), lang=perl> ≡ 
1096       ________________________________________________________________________
1097   1  | print "hello world $0\n";
1098      |________________________________________________________________________
1099 Used by 47b 
1101 If it were included in a chunk with language=sh, like this:
1103 47b <test:example-sh[1](\v), lang=sh> ≡ 
1104       ________________________________________________________________________
1105   1  | perl -e "«test:example-perl 47a»"
1106      |________________________________________________________________________
1107 Used by 48b 
1109 we might want fangle would to generate output like this:
1111 48a <test:example-sh.result[1](\v), lang=sh> ≡ 
1112       ________________________________________________________________________
1113   1  | perl -e "print \"hello world \$0\\n\";"
1114      |________________________________________________________________________
1115 not used 
1117 See that the double quote ", back-slash \ and $ have been quoted with a back-slash to protect them from shell interpretation.
1118 If that were then included in a chunk with language=make, like this:
1120 48b <test:example-makefile[1](\v), lang=make> ≡ 
1121       ________________________________________________________________________
1122   1  | target: pre-req
1123   2  | ↦«test:example-sh 47b»
1124      |________________________________________________________________________
1125 not used 
1127 We would need the output to look like this --- note the $$ as the single $ has been makefile-quoted with another $.
1129 48c <test:example-makefile.result[1](\v), lang=make> ≡ 
1130       ________________________________________________________________________
1131   1  | target: pre-req
1132   2  | ↦perl -e "print \"hello world \$$0\\n\";"
1133      |________________________________________________________________________
1134 not used 
1136 11.3 Language Mode Definitions 
1137 In order to make this work, we must define a mode-tracker supporting each language, that can detect the various quoting modes, and provide a transformation that may be applied to any included text so that included text will be interpreted correctly after any interpolation that it may be subject to at run-time.
1138 For example, the sed transformation for text to be inserted into shell double-quoted strings would be something like:
1139 s/\\/\\\\/g;s/$/\\$/g;s/"/\\"/g;
1140 which would protect \ $ "
1141 All modes definitions are stored in a single multi-dimensional hash called modes:
1142 modes[language, mode, properties]
1143 The first index is the language, and the second index is the mode. The third indexes hold properties such as terminators, possible submodes, transformations, and so forth.
1145 48d <xmode:set-terminators[1](language\v, mode\v, terminators\v\v), lang=> ≡ 
1146       ________________________________________________________________________
1147   1  | modes["${language}", "${mode}", "terminators"]="${terminators}";
1148      |________________________________________________________________________
1149 not used 
1152 48e <xmode:set-submodes[1](language\v, mode\v, submodes\v\v), lang=> ≡ 
1153       ________________________________________________________________________
1154   1  | modes["${language}", "${mode}",  "submodes"]="${submodes}";
1155      |________________________________________________________________________
1156 not used 
1158 A useful set of mode definitions for a nameless general C-type language is shown here.
1159 Don't be confused by the double backslash escaping needed in awk. One set of escaping is for the string, and the second set of escaping is for the regex.
1160 To do: TODO: Add =<\mode{}> command which will allow us to signify that a string is
1161  regex and thus fangle will quote it for us.
1163 Sub-modes are identified by a backslash, a double or single quote, various bracket styles or a /* comment; specifically: \ " ' { ( [ /*
1164 For each of these sub-modes modes we must also identify at a mode terminator, and any sub-modes or delimiters that may be entered2. Because we are using the sub-mode characters as the mode identifier it means we can't currently have a mode character dependant on it's context; i.e. { can't behave differently when it is inside [.  ^2.
1166 49a <common-mode-definitions[1](language\v\v), lang=> ≡  49b▿
1167       ________________________________________________________________________
1168   1  | modes[${language}, "",  "submodes"]="\\\\|\"|'|{|\\(|\\[";
1169      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 52b
1170 In the default mode, a comma surrounded by un-important white space is a delimiter of language items3. whatever a language item might be  ^3. Delimiters are used so that fangle can parse and recognise arguments individually.
1172 49b <common-mode-definitions[2](language\v\v) ⇑49a, lang=> +≡ ▵49a 49d▿
1173      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1174   2  | modes[${language}, "",  "delimiters"]=" *, *";
1175      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1176 and should pass this test:
1177 To do: Why do the tests run in ?(? mode and not ?? mode
1180 49c <test:mode-definitions[1](\v), lang=> ≡  50g⊳
1181       ________________________________________________________________________
1182   1  | parse_chunk_args("c-like", "1,2,3", a, "");
1183   2  | if (a[1] != "1") e++;
1184   3  | if (a[2] != "2") e++;
1185   4  | if (a[3] != "3") e++;
1186   5  | if (length(a) != 3) e++;
1187   6  | «pca-test.awk:summary 62d»
1188   7  | 
1189   8  | parse_chunk_args("c-like", "joe, red", a, "");
1190   9  | if (a[1] != "joe") e++;
1191   10  | if (a[2] != "red") e++;
1192   11  | if (length(a) != 2) e++;
1193   12  | «pca-test.awk:summary 62d»
1194   13  | 
1195   14  | parse_chunk_args("c-like", "${colour}", a, "");
1196   15  | if (a[1] != "${colour}") e++;
1197   16  | if (length(a) != 1) e++;
1198   17  | «pca-test.awk:summary 62d»
1199      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 62c
1200 11.3.1 Backslash 
1201 The backslash mode has no submodes or delimiters, and is terminated by any character. Note that we are not so much interested in evaluating or interpolating content as we are in delineating content. It is no matter that a double backslash (\\) may represent a single backslash while a backslash-newline may represent white space, but it does matter that the newline in a backslash newline should not be able to terminate a C pre-processor statement; and so the newline will be consumed by the backslash terminator however it may uultimately be interpreted.
1203 49d <common-mode-definitions[3](language\v\v) ⇑49a, lang=> +≡ ▵49b 50f⊳
1204      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1205   3  | modes[${language}, "\\", "terminators"]=".";
1206      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1207 11.3.2 Strings 
1208 Common languages support two kinds of strings quoting, double quotes and single quotes.
1209 In a string we have one special mode, which is the backslash. This may escape an embedded quote and prevent us thinking that it should terminate the string.
1211 50a <mode:common-string[1](language\v, quote\v\v), lang=> ≡  50b▿
1212       ________________________________________________________________________
1213   1  | modes[${language}, ${quote}, "submodes"]="\\\\";
1214      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 49a
1215 Otherwise, the string will be terminated by the same character that commenced it.
1217 50b <mode:common-string[2](language\v, quote\v\v) ⇑50a, lang=> +≡ ▵50a 50c▿
1218      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1219   2  | modes[${language}, ${quote}, "terminators"]=${quote};
1220      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1221 In C type languages, certain escape sequences exist in strings. We need to define mechanism to enclode any chunks included in this mode using those escape sequences. These are expressed in two parts, s meaning search, and r meaning replace.
1222 The first substitution is to replace a backslash with a double backslash. We do this first as other substitutions may introduce a backslash which we would not then want to escape again here.
1223 Note: Backslashes need double-escaping in the search pattern but not in the replacement string, hence we are replacing a literal \ with a literal \\.
1225 50c <mode:common-string[3](language\v, quote\v\v) ⇑50a, lang=> +≡ ▵50b 50d▿
1226      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1227   3  | escapes[${language}, ${quote}, ++escapes[${language}, ${quote}], "s"]="\\\\";
1228   4  | escapes[${language}, ${quote},   escapes[${language}, ${quote}], "r"]="\\\\";
1229      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1230 If the quote character occurs in the text, it should be preceded by a backslash, otherwise it would terminate the string unexpectedly.
1232 50d <mode:common-string[4](language\v, quote\v\v) ⇑50a, lang=> +≡ ▵50c 50e▿
1233      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1234   5  | escapes[${language}, ${quote}, ++escapes[${language}, ${quote}], "s"]=${quote};
1235   6  | escapes[${language}, ${quote},   escapes[${language}, ${quote}], "r"]="\\" ${quote};
1236      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1237 Any newlines in the string, must be replaced by \n.
1239 50e <mode:common-string[5](language\v, quote\v\v) ⇑50a, lang=> +≡ ▵50d
1240      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1241   7  | escapes[${language}, ${quote}, ++escapes[${language}, ${quote}], "s"]="\n";
1242   8  | escapes[${language}, ${quote},   escapes[${language}, ${quote}], "r"]="\\n";
1243      |________________________________________________________________________
1246 For the common modes, we define this string handling for double and single quotes.
1248 50f <common-mode-definitions[4](language\v\v) ⇑49a, lang=> +≡ ⊲49d 51b⊳
1249      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1250   4  | «mode:common-string\v(${language}\v, "\""\v) 50a»
1251   5  | «mode:common-string\v(${language}\v, "'"\v) 50a»
1252      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1253 Working strings should pass this test:
1255 50g <test:mode-definitions[2](\v) ⇑49c, lang=> +≡ ⊲49c 57c⊳
1256      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1257   18  | parse_chunk_args("c-like", "say \"I said, \\\"Hello, how are you\\\".\", for me", a, "");
1258   19  | if (a[1] != "say \"I said, \\\"Hello, how are you\\\".\"") e++;
1259   20  | if (a[2] != "for me") e++;
1260   21  | if (length(a) != 2) e++;
1261   22  | «pca-test.awk:summary 62d»
1262      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1263 11.3.3 Parentheses, Braces and Brackets 
1264 Where quotes are closed by the same character, parentheses, brackets and braces are closed by an alternate character.
1266 51a <mode:common-brackets[1](language\v, open\v, close\v\v), lang=> ≡ 
1267       ________________________________________________________________________
1268   1  | modes[${language}, ${open},  "submodes" ]="\\\\|\"|{|\\(|\\[|'|/\\*";
1269   2  | modes[${language}, ${open},  "delimiters"]=" *, *";
1270   3  | modes[${language}, ${open},  "terminators"]=${close};
1271      |________________________________________________________________________
1272 Used by 49a, 52b 
1274 Note that the open is NOT a regex but the close token IS. 
1275 To do: When we can quote regex we won't have to put the slashes in here
1278 51b <common-mode-definitions[5](language\v\v) ⇑49a, lang=> +≡ ⊲50f
1279      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1280   6  | «mode:common-brackets\v(${language}\v, "{"\v, "}"\v) 51a»
1281   7  | «mode:common-brackets\v(${language}\v, "["\v, "\\]"\v) 51a»
1282   8  | «mode:common-brackets\v(${language}\v, "("\v, "\\)"\v) 51a»
1283      |________________________________________________________________________
1286 11.3.4 Customizing Standard Modes 
1288 51c <mode:add-submode[1](language\v, mode\v, submode\v\v), lang=> ≡ 
1289       ________________________________________________________________________
1290   1  | modes[${language}, ${mode}, "submodes"] = modes[${language}, ${mode}, "submodes"] "|" ${submode};
1291      |________________________________________________________________________
1292 Used by 51e, 51f, 51g, 51h, 52g 
1295 51d <mode:add-escapes[1](language\v, mode\v, search\v, replace\v\v), lang=> ≡ 
1296       ________________________________________________________________________
1297   1  | escapes[${language}, ${mode}, ++escapes[${language}, ${mode}], "s"]=${search};
1298   2  | escapes[${language}, ${mode},   escapes[${language}, ${mode}], "r"]=${replace};
1299      |________________________________________________________________________
1300 Used by 51f, 51g, 51h 
1303 11.3.5 Comments 
1304 We can define /* comment */ style comments and //comment style comments to be added to any language:
1306 51e <mode:multi-line-comments[1](language\v\v), lang=> ≡ 
1307       ________________________________________________________________________
1308   1  | «mode:add-submode\v(${language}\v, ""\v, "/\\*"\v) 51c»
1309   2  | modes[${language}, "/*", "terminators"]="\\*/";
1310      |________________________________________________________________________
1311 Used by 52b 
1314 51f <mode:single-line-slash-comments[1](language\v\v), lang=> ≡ 
1315       ________________________________________________________________________
1316   1  | «mode:add-submode\v(${language}\v, ""\v, "//"\v) 51c»
1317   2  | modes[${language}, "//", "terminators"]="\n";
1318   3  | «mode:add-escapes\v(${language}\v, "//"\v, "\n"\v, "\n//"\v) 51d»
1319      |________________________________________________________________________
1320 Used by 52b 
1322 We can also define # comment style comments (as used in awk and shell scripts) in a similar manner.
1323 To do: I'm having to use # for hash and ¯extbackslash{} for  and have hacky work-arounds in the parser for now
1326 51g <mode:add-hash-comments[1](language\v\v), lang=> ≡ 
1327       ________________________________________________________________________
1328   1  | «mode:add-submode\v(${language}\v, ""\v, "#"\v) 51c»
1329   2  | modes[${language}, "#", "terminators"]="\n";
1330   3  | «mode:add-escapes\v(${language}\v, "#"\v, "\n"\v, "\n#"\v) 51d»
1331      |________________________________________________________________________
1332 Used by 52b 
1334 In C, the # denotes pre-processor directives which can be multi-line
1336 51h <mode:add-hash-defines[1](language\v\v), lang=> ≡ 
1337       ________________________________________________________________________
1338   1  | «mode:add-submode\v(${language}\v, ""\v, "#"\v) 51c»
1339   2  | modes[${language}, "#", "submodes" ]="\\\\";
1340   3  | modes[${language}, "#", "terminators"]="\n";
1341   4  | «mode:add-escapes\v(${language}\v, "#"\v, "\n"\v, "\\\\\n"\v) 51d»
1342      |________________________________________________________________________
1343 Used by 52b 
1346 52a <mode:quote-dollar-escape[1](language\v, quote\v\v), lang=> ≡ 
1347       ________________________________________________________________________
1348   1  | escapes[${language}, ${quote}, ++escapes[${language}, ${quote}], "s"]="\\$";
1349   2  | escapes[${language}, ${quote},   escapes[${language}, ${quote}], "r"]="\\$";
1350      |________________________________________________________________________
1351 Used by 52b 
1353 We can add these definitions to various languages
1355 52b <mode-definitions[1](\v), lang=> ≡  53a⊳
1356       ________________________________________________________________________
1357   1  | «common-mode-definitions\v("c-like"\v) 49a»
1358   2  | 
1359   3  | «common-mode-definitions\v("c"\v) 49a»
1360   4  | «mode:multi-line-comments\v("c"\v) 51e»
1361   5  | «mode:single-line-slash-comments\v("c"\v) 51f»
1362   6  | «mode:add-hash-defines\v("c"\v) 51h»
1363   7  | 
1364   8  | «common-mode-definitions\v("awk"\v) 49a»
1365   9  | «mode:add-hash-comments\v("awk"\v) 51g»
1366   10  | «mode:add-naked-regex\v("awk"\v) 52g»
1367      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 62c, 73d
1368 The awk definitions should allow a comment block like this:
1370 52c <test:comment-quote[1](\v), lang=awk> ≡ 
1371       ________________________________________________________________________
1372   1  | # Comment: «test:comment-text 52d»
1373      |________________________________________________________________________
1374 not used 
1377 52d <test:comment-text[1](\v), lang=> ≡ 
1378       ________________________________________________________________________
1379   1  | Now is the time for
1380   2  | the quick brown fox to bring lemonade
1381   3  | to the party
1382      |________________________________________________________________________
1383 Used by 52c 
1385 to come out like this:
1387 52e <test:comment-quote:result[1](\v), lang=> ≡ 
1388       ________________________________________________________________________
1389   1  | # Comment: Now is the time for
1390   2  | #the quick brown fox to bring lemonade
1391   3  | #to the party
1392      |________________________________________________________________________
1393 not used 
1395 The C definition for such a block should have it come out like this:
1397 52f <test:comment-quote:C-result[1](\v), lang=> ≡ 
1398       ________________________________________________________________________
1399   1  | # Comment: Now is the time for\
1400   2  | the quick brown fox to bring lemonade\
1401   3  | to the party
1402      |________________________________________________________________________
1403 not used 
1405 11.3.6 Regex 
1406 This pattern is incomplete, but meant to detect naked regular expressions in awk and perl; e.g. /.*$/, however required capabilities are not present.
1407 Current it only detects regexes anchored with ^ as used in fangle.
1408 For full regex support, modes need to be named not after their starting character, but some other more fully qualified name.
1410 52g <mode:add-naked-regex[1](language\v\v), lang=> ≡ 
1411       ________________________________________________________________________
1412   1  | «mode:add-submode\v(${language}\v, ""\v, "/\\^"\v) 51c»
1413   2  | modes[${language}, "/^", "terminators"]="/";
1414      |________________________________________________________________________
1415 Used by 52b 
1417 11.3.7 Perl 
1419 53a <mode-definitions[2](\v) ⇑52b, lang=> +≡ ⊲52b 53b▿
1420      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1421   11  | «common-mode-definitions\v("perl"\v) 49a»
1422   12  | «mode:multi-line-comments\v("perl"\v) 51e»
1423   13  | «mode:add-hash-comments\v("perl"\v) 51g»
1424      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1425 Still need to add add s/, submode /, terminate both with //. This is likely to be impossible as perl regexes can contain perl.
1426 11.3.8 sh 
1427 Shell single-quote strings are different to other strings and have no escape characters. The only special character is the single quote ' which always closes the string. Therefore we cannot use <common-mode-definitions\v("sh"\v) 49a> but we will invoke most of it's definition apart from single-quote strings. 
1429 53b <mode-definitions[3](\v) ⇑52b, lang=awk> +≡ ▵53a 54a⊳
1430      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1431   14  | modes["sh", "",  "submodes"]="\\\\|\"|'|{|\\(|\\[|\\$\\(";
1432   15  | modes["sh", "\\", "terminators"]=".";
1433   16  | 
1434   17  | modes["sh", "\"", "submodes"]="\\\\|\\$\\(";
1435   18  | modes["sh", "\"", "terminators"]="\"";
1436   19  | escapes["sh", "\"", ++escapes["sh", "\""], "s"]="\\\\";
1437   20  | escapes["sh", "\"",   escapes["sh", "\""], "r"]="\\\\";
1438   21  | escapes["sh", "\"", ++escapes["sh", "\""], "s"]="\"";
1439   22  | escapes["sh", "\"",   escapes["sh", "\""], "r"]="\\" "\"";
1440   23  | escapes["sh", "\"", ++escapes["sh", "\""], "s"]="\n";
1441   24  | escapes["sh", "\"",   escapes["sh", "\""], "r"]="\\n";
1442   25  | 
1443   26  | modes["sh", "'", "terminators"]="'";
1444   27  | escapes["sh", "'", ++escapes["sh", "'"], "s"]="'";
1445   28  | escapes["sh", "'",   escapes["sh", "'"], "r"]="'\\'" "'";
1446   29  | «mode:common-brackets\v("sh"\v, "$("\v, "\\)"\v) 51a»
1447   30  | «mode:add-tunnel\v("sh"\v, "$("\v, ""\v) 53c»
1448   31  | «mode:common-brackets\v("sh"\v, "{"\v, "}"\v) 51a»
1449   32  | «mode:common-brackets\v("sh"\v, "["\v, "\\]"\v) 51a»
1450   33  | «mode:common-brackets\v("sh"\v, "("\v, "\\)"\v) 51a»
1451   34  | «mode:add-hash-comments\v("sh"\v) 51g»
1452   35  | «mode:quote-dollar-escape\v("sh"\v, "\""\v) 52a»
1453      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1454 The definition of add-tunnel is:
1456 53c <mode:add-tunnel[1](language\v, mode\v, tunnel\v\v), lang=> ≡ 
1457       ________________________________________________________________________
1458   1  | escapes[${language}, ${mode}, ++escapes[${language}, ${mode}], "tunnel"]=${tunnel};
1459      |________________________________________________________________________
1460 Used by 52b 
1462 11.3.9 Make 
1463 BUGS: makefile tab mode is terminated by newline, but chunks never end in a newline! So tab mode is never closed unless there is a trailing blank line!
1464 For makefiles, we currently recognize 2 modes: the null mode and ↦ mode, which is tabbed mode and contains the makefile recipie. 
1467 54a <mode-definitions[4](\v) ⇑52b, lang=awk> +≡ ⊲53b 54b▿
1468      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1469   36  | modes["make", "",  "submodes"]="↦";
1470      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1471 In the null mode the only escape is $ which must be converted to $$, and hash-style comments. POSIX requires that line-continuations extend hash-style comments and so fangle-style transformations to replicate the hash at the start of each line is not strictly required, however it is harmless, easier to read, and required by some implementations of make which do not implement POSIX requirements correctly.
1473 54b <mode-definitions[5](\v) ⇑52b, lang=awk> +≡ ▵54a 56a⊳
1474      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1475   37  | escapes["make", "", ++escapes["make", ""], "s"]="\\$";
1476   38  | escapes["make", "", escapes["make", ""], "r"]="$$";
1477   39  | «mode:add-hash-comments\v("make"\v) 51g»
1478      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1479 Tabbed mode is harder to manage, as the GNU Make Manual says in the section on splitting lines4. http://www.gnu.org/s/hello/manual/make/Splitting-Lines.html  ^4. There is no obvious way to escape a multi-line text that occurs as part of a makefile recipe.
1480 Traditionally, if the newline's in the shell script all occur at points of top-level shell syntax, then we could replace them with  ;\n↦and largely get the right effect.
1482 54c <test:make:1[1](\v), lang=make> ≡ 
1483       ________________________________________________________________________
1484   1  | all:
1485   2  | ↦echo making
1486   3  | ↦«test:make:1-inc\v($@) 54d»
1487      |________________________________________________________________________
1488 not used 
1493 54d <test:make:1-inc[1](target\v\v), lang=sh> ≡ 
1494       ________________________________________________________________________
1495   1  | if test "${target}" = "all"
1496   2  | then echo yes, all
1497   3  | else echo "${target}" | sed -e '/^\//{
1498   4  |                                  p;s/^/../
1499   5  |                        }'
1500   6  | fi
1501      |________________________________________________________________________
1502 Used by 54c, 56c 
1504 The two chunks above could reasonably produce something like this:
1506 54e <test:make:1.result.bad[1](\v), lang=make> ≡ 
1507       ________________________________________________________________________
1508   1  | all:
1509   2  | ↦echo making
1510   3  | ↦if test "$@" = "all" ;\
1511   4  | ↦then echo yes, all ;\
1512   5  | ↦else echo "$@" | sed -e '/^\//{ ;\
1513   6  | ↦                           p;s/^/../ ;\
1514   7  | ↦                         }' ;\
1515   8  | ↦fi
1516      |________________________________________________________________________
1517 not used 
1519 However ;\ is not a proper continuation inside a multi-line sed script. There is no simple continuation that fangle could use — and in any case it would depend on what type of quote marks were used in the bash that contained the sed. 
1520 We would prefer to use a more intuitive single backslash at the end of the line, giving these results.
1522 54f <test:make:1.result[1](\v), lang=make> ≡ 
1523       ________________________________________________________________________
1524   1  | all:
1525   2  | ↦echo making
1526   3  | ↦if test "$$@" = "all"\
1527   4  | ↦ then echo yes, all\
1528   5  | ↦ else echo "$$@" | sed -e '/^\//{\
1529   6  | ↦                                  p;s/^/../\
1530   7  | ↦                        }'\
1531   8  | ↦ fi
1532      |________________________________________________________________________
1533 not used 
1535 The difficulty lies in the way that make handles the recipe. Each line of the recipe is invoked as a separate shell command (using $(SHELL) -c) unless the last character of the line was a backslash. In such a case, the backslash and the newline and the nextline are handed to the shell (although the tab character that prefixes the next line is stripped).
1536 This behaviour makes it impossible to hand a newline character to the shell unless it is prefixed by a backslash. If an included shell fragment contained strings with literal newline characters then there would be no easy way to escape these and preserve the value of the string.
1537 A different style of makefile construction might be used — the recipe could be stored in a target specific variable5. http://www.gnu.org/s/hello/manual/make/Target_002dspecific.html  ^5 which contains the recipe with a more normal escape mechanism.
1538 A better solution is to use a shell helper that strips the back-slash which precedes the newline character and then passes the arguments to the normal shell.
1539 Because this is a simple operation and because bash is so flexible, this can be managed in a single line within the makefile itself.
1540 As a newline will only exist when preceded by the backslash, and as the purpose of the backash is to protect th newline, that is needed is to remove any backslash that is followed by a newline.
1541 Bash is capable of doing this with its pattern substitution. If A=123:=456:=789 then ${A//:=/=} will be 123=456=789. We don't want to just perform the substitution in a single variable but in fact in all of $@'', however bash will repeat substitution over all members of an array, so this is done automatically.
1542 In bash, $'\012' represents the newline character (expressed as an octal escape sequence), so this expression will replace backslash-newline with a single newline.
1544 55a <fix-requote-newline[1](\v), lang=sh> ≡ 
1545       ________________________________________________________________________
1546   1  | "${@//\\$'\012'/$'\012'}"
1547      |________________________________________________________________________
1548 Used by 55b 
1550 We use this as part of a larger statement which will invoke such a transformed command ine using any particular shell. The trailing -- prevents any options in the command line from being interpreted as options to our bash command — instead they will be transformed and passed to the inner shell which is invoked with exec so that our fixup-shell does not hang around longer than is needed.
1552 55b <fix-make-shell[1](shell\v\v), lang=sh> ≡ 
1553       ________________________________________________________________________
1554   1  | bash -c 'exec ${shell} «fix-requote-newline 55a»' --
1555      |________________________________________________________________________
1556 Used by 55c 
1558 We can then cinlude a line like this in our makefiles. We should rather pass $(SHELL) as the chunk argument than bash, but currently fangle will not track which nested-inclusion level the argument comes from and will quote the $ in $(SHELL) in the same way it quotes a $ that may occur in the bash script, so this would come out as $$(SHELL) and have the wrong effect.
1560 55c <make-fix-make-shell[1](\v), lang=> ≡ 
1561       ________________________________________________________________________
1562   1  | SHELL:=«fix-make-shell\v(bash\v) 55b»
1563      |________________________________________________________________________
1564 Used by 35a 
1566 The full escaped and quoted text with $(SHELL) and suitale for inclusion in a Makefile is:
1567 SHELL:=bash -c 'exec $(SHELL) "$${@//\\$$'\''\012'\''/$$'\''\012'\''}"' --
1568 Based on this, we just need to escape newlines (in tabbed mode) with a regular backslash:
1569 Note that terminators applies to literal, not included text, escapes apply to included, not literal text; also that the tab character is hard-wired into the pattern, and that the make variable .RECIPEPREFIX might change this to something else.
1571 56a <mode-definitions[6](\v) ⇑52b, lang=awk> +≡ ⊲54b
1572      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1573   40  | modes["make", "↦", "terminators"]="\\n";
1574   41  | escapes["make", "↦", ++escapes["make", "↦"], "s"]="\\n";
1575   42  | escapes["make", "↦",   escapes["make", "↦"], "r"]="\\\n↦";
1576      |________________________________________________________________________
1579 With this improved quoting, the test on 54c will actually produce this:
1581 56b <test:make:1.result-actual[1](\v), lang=make> ≡ 
1582       ________________________________________________________________________
1583   1  | all:
1584   2  | ↦echo making
1585   3  | ↦if test "$$@" = "all"\
1586   4  | ↦ then echo yes, all\
1587   5  | ↦ else echo not all\
1588   6  | ↦ fi
1589      |________________________________________________________________________
1590 not used 
1592 The chunk argument $@ has been quoted (which would have been fine if we were passing the name of a shell variable), and the other shell lines are (harmlessly) indented by 1 space as part of fangle indent-matching which should have taken into account the expanded tab size, and should generally take into account the expanded prefix of the line whose indent it is trying to match, but which in this case we want to have no effect at all!
1593 To do: The $@ was passed from a make fragment. In what cases should it be converted to $$@?
1594 Do we need to track the language of sources of arguments?
1596 A more ugly work-around until this problem can be solved would be to use this notation:
1598 56c <test:make:2[1](\v), lang=make> ≡ 
1599       ________________________________________________________________________
1600   1  | all:
1601   2  | ↦echo making
1602   3  | ↦ARG="$@"; «test:make:1-inc\v($ARG) 54d»
1603      |________________________________________________________________________
1604 not used 
1606 which produces this output which is more useful (because it works):
1608 56d <test:make:2.result[1](\v), lang=make> ≡ 
1609       ________________________________________________________________________
1610   1  | all:
1611   2  | ↦echo making
1612   3  | ↦ARG="$@"; if test "$$ARG" = "all"\
1613   4  | ↦           then echo yes, all\
1614   5  | ↦           else echo "$$ARG" | sed -e '/^\//{\
1615   6  | ↦                                            p;s/^/../\
1616   7  | ↦                                  }'\
1617   8  | ↦           fi
1618      |________________________________________________________________________
1619 not used 
1621 11.4 Quoting scenarios 
1622 11.4.1 Direct quoting 
1623 He we give examples of various quoting scenarios and discuss what the expected outcome might be and how this could be obtained.
1625 56e <test:q:1[1](\v), lang=sh> ≡ 
1626       ________________________________________________________________________
1627   1  | echo "$(«test:q:1-inc 57a»)"
1628      |________________________________________________________________________
1629 not used 
1632 57a <test:q:1-inc[1](\v), lang=sh> ≡ 
1633       ________________________________________________________________________
1634   1  | echo "hello"
1635      |________________________________________________________________________
1636 Used by 56e 
1638 Should this examples produce echo "$(echo "hello")" or echo "$(echo \"hello\")" ?
1639 This depends on what the author intended, but we must provde a way to express that intent.
1640 We might argue that as both chunks have lang=sh the intent must have been to quote the included chunk — but consider that this might be shell script that writes shell script.
1641 If <test:q:1-inc 57a> had lang=text then it certainly would have been right to quote it, which leads us to ask: in what ways can we reduce quoting if lang of the included chunk is compatible with the lang of the including chunk?
1642 If we take a completely nested approach then even though $( mode might do no quoting of it's own, " mode will still do it's own quoting. We need a model where the nested $( mode will prevent " from quoting.
1643 This leads rise to the tunneling feature. In bash, the $( gives rise to a new top-level parsing scenario, so we need to enter the null mode, and also ignore any quoting and then undo-this when the $( mode is terminated by the corresponding close ).
1644 We shall say that tunneling is when a mode in a language ignores other modes in the same language and arrives back at an earlier null mode of the same language.
1645 In example <test:q:1 56e> above, the nesting of modes is: null, ", $(
1646 When mode $( is commenced, the stack of nest modes will be traversed. If the null mode can be found in the same language, without the language varying, then a tunnel will be established so that the intervening modes, " in this case, can be skipped when the modes are enumerated to quote the texted being emitted.
1647 In such a case, the correct result would be:
1649 57b <test:q:1.result[1](\v), lang=sh> ≡ 
1650       ________________________________________________________________________
1651   1  | echo "$(echo "hello")"
1652      |________________________________________________________________________
1653 not used 
1655 11.5 Some tests 
1656 Also, the parser must return any spare text at the end that has not been processed due to a mode terminator being found.
1658 57c <test:mode-definitions[3](\v) ⇑49c, lang=> +≡ ⊲50g 57d▿
1659      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1660   23  | rest = parse_chunk_args("c-like", "1, 2, 3) spare", a, "(");
1661   24  | if (a[1] != 1) e++;
1662   25  | if (a[2] != 2) e++;
1663   26  | if (a[3] != 3) e++;
1664   27  | if (length(a) != 3) e++;
1665   28  | if (rest != " spare") e++;
1666   29  | «pca-test.awk:summary 62d»
1667      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1668 We must also be able to parse the example given earlier.
1670 57d <test:mode-definitions[4](\v) ⇑49c, lang=> +≡ ▵57c
1671      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1672   30  | parse_chunk_args("c-like", "things[x, y], get_other_things(a, \"(all)\"), 99", a, "(");
1673   31  | if (a[1] != "things[x, y]") e++;
1674   32  | if (a[2] != "get_other_things(a, \"(all)\")") e++;
1675   33  | if (a[3] != "99") e++;
1676   34  | if (length(a) != 3) e++;
1677   35  | «pca-test.awk:summary 62d»
1678      |________________________________________________________________________
1681 11.6 A non-recursive mode tracker 
1682 As each chunk is output a new mode tracker for that language is initialized in it's normal state. As text is output for that chunk the output mode is tracked. When a new chunk is included, a transformation appropriate to that mode is selected and pushed onto a stack of transformations. Any text to be output is passed through this stack of transformations.
1683 It remains to consider if the chunk-include function should return it's generated text so that the caller can apply any transformations (and formatting), or if it should apply the stack of transformations itself.
1684 Note that the transformed included text should have the property of not being able to change the mode in the current chunk.
1685 To do: Note chunk parameters should probably also be transformed
1687 11.6.1 Constructor 
1688 The mode tracker holds its state in a stack based on a numerically indexed hash. This function, when passed an empty hash, will intialize it.
1690 58a <new_mode_tracker()[1](\v), lang=> ≡ 
1691       ________________________________________________________________________
1692   1  | function new_mode_tracker(context, language, mode) {
1693   2  |   context[""] = 0;
1694   3  |   context[0, "language"] = language;
1695   4  |   context[0, "mode"] = mode;
1696   5  | }
1697      |________________________________________________________________________
1698 Used by 62b 
1700 Awk functions cannot return an array, but arrays are passed by reference. Because of this we must create the array first and pass it in, so we have a fangle macro to do this:
1702 58b <new-mode-tracker[1](context\v, language\v, mode\v\v), lang=awk> ≡ 
1703       ________________________________________________________________________
1704   1  | «awk-delete-array\v(${context}\v) 37d»
1705   2  | new_mode_tracker(${context}, ${language}, ${mode});
1706      |________________________________________________________________________
1707 Used by 44a, 58c 
1709 11.6.2 Management 
1710 And for tracking modes, we dispatch to a mode-tracker action based on the current language
1712 58c <mode_tracker[1](\v), lang=awk> ≡  59a⊳
1713       ________________________________________________________________________
1714   1  | function push_mode_tracker(context, language, mode,
1715   2  |   # local vars
1716   3  |   top)
1717   4  | {
1718   5  |   if (! ("" in context)) {
1719   6  |     «new-mode-tracker\v(context\v, language\v, mode\v) 58b»
1720   7  |     return;
1721   8  |   } else {
1722   9  |     top = context[""];
1723   10  | #    if (context[top, "language"] == language && mode=="") mode = context[top, "mode"];
1724   11  |     if (context[top, "language"] == language && context[top, "mode"] == mode) return top - 1;
1725   12  |     old_top = top;
1726   13  |     top++;
1727   14  |     context[top, "language"] = language;
1728   15  |     context[top, "mode"] = mode;
1729   16  |     context[""] = top;
1730   17  |   }
1731   18  |   return old_top;
1732   19  | }
1733      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\not used
1735 59a <mode_tracker[2](\v) ⇑58c, lang=> +≡ ⊲58c 59b▿
1736      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1737   20  | function dump_mode_tracker(context,  
1738   21  |   c, d)
1739   22  | {
1740   23  |   for(c=0; c <= context[""]; c++) {
1741   24  |     printf(" %2d   %s:%s\n", c, context[c, "language"], context[c, "mode"]) > "/dev/stderr";
1742   25  | #    for(d=1; ( (c, "values", d) in context); d++) {
1743   26  | #      printf("   %2d %s\n", d, context[c, "values", d]) > "/dev/stderr";
1744   27  | #    }
1745   28  |   }
1746   29  | }
1747      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1749 59b <mode_tracker[3](\v) ⇑58c, lang=> +≡ ▵59a 63b⊳
1750      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1751   30  | function pop_mode_tracker(context, context_origin)
1752   31  | {
1753   32  |   if ( (context_origin) && ("" in context) && context[""] != (1+context_origin) && context[""] != context_origin) {
1754   33  |     print "Context level: " context[""] ", origin: " context_origin "\n" > "/dev/stderr"
1755   34  |     return 0;
1756   35  |   }
1757   36  |   context[""] = context_origin;
1758   37  |   return 1;
1759   38  | }
1760      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1761 This implies that any chunk must be syntactically whole; for instance, this is fine:
1763 59c <test:whole-chunk[1](\v), lang=> ≡ 
1764       ________________________________________________________________________
1765   1  | if (1) {
1766   2  |   «test:say-hello 59d»
1767   3  | }
1768      |________________________________________________________________________
1769 not used 
1772 59d <test:say-hello[1](\v), lang=> ≡ 
1773       ________________________________________________________________________
1774   1  | print "hello";
1775      |________________________________________________________________________
1776 Used by 59c 
1778 But this is not fine; the chunk <test:hidden-else 59f> is not properly cromulent.
1780 59e <test:partial-chunk[1](\v), lang=> ≡ 
1781       ________________________________________________________________________
1782   1  | if (1) {
1783   2  |   «test:hidden-else 59f»
1784   3  | }
1785      |________________________________________________________________________
1786 not used 
1789 59f <test:hidden-else[1](\v), lang=> ≡ 
1790       ________________________________________________________________________
1791   1  |   print "I'm fine";
1792   2  | } else {
1793   3  |   print "I'm not";
1794      |________________________________________________________________________
1795 Used by 59e 
1797 These tests will check for correct behaviour:
1799 59g <test:cromulence[1](\v), lang=> ≡ 
1800       ________________________________________________________________________
1801   1  | echo Cromulence test
1802   2  | passtest $FANGLE -Rtest:whole-chunk $TXT_SRC &>/dev/null || ( echo "Whole chunk failed" && exit 1 )
1803   3  | failtest $FANGLE -Rtest:partial-chunk $TXT_SRC &>/dev/null || ( echo "Partial chunk failed" && exit 1 )
1804      |________________________________________________________________________
1805 Used by 99b 
1807 11.6.3 Tracker 
1808 We must avoid recursion as a language construct because we intend to employ mode-tracking to track language mode of emitted code, and the code is emitted from a function which is itself recursive, so instead we implement psuedo-recursion using our own stack based on a hash.
1810 60a <mode_tracker()[1](\v), lang=awk> ≡  60b▿
1811       ________________________________________________________________________
1812   1  | function mode_tracker(context, text, values, 
1813   2  |   # optional parameters
1814   3  |   # local vars
1815   4  |   mode, submodes, language,
1816   5  |   cindex, c, a, part, item, name, result, new_values, new_mode, 
1817   6  |   delimiters, terminators)
1818   7  | {
1819      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 62b
1820 We could be re-commencing with a valid context, so we need to setup the state according to the last context.
1822 60b <mode_tracker()[2](\v) ⇑60a, lang=> +≡ ▵60a 60e▿
1823      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1824   8  |   cindex = context[""] + 0;
1825   9  |   mode = context[cindex, "mode"];
1826   10  |   language = context[cindex, "language" ];
1827      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1828 First we construct a single large regex combining the possible sub-modes for the current mode along with the terminators for the current mode.
1830 60c <parse_chunk_args-reset-modes[1](\v), lang=> ≡  60d▿
1831       ________________________________________________________________________
1832   1  |   submodes=modes[language, mode, "submodes"];
1833   2  | 
1834   3  |   if ((language, mode, "delimiters") in modes) {
1835   4  |     delimiters = modes[language, mode, "delimiters"];
1836   5  |     if (length(submodes)>0) submodes = submodes "|";
1837   6  |     submodes=submodes delimiters;
1838   7  |   } else delimiters="";
1839   8  |   if ((language, mode, "terminators") in modes) {
1840   9  |     terminators = modes[language, mode, "terminators"];
1841   10  |     if (length(submodes)>0) submodes = submodes "|";
1842   11  |     submodes=submodes terminators;
1843   12  |   } else terminators="";
1844      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 60a
1845 If we don't find anything to match on --- probably because the language is not supported --- then we return the entire text without matching anything.
1847 60d <parse_chunk_args-reset-modes[2](\v) ⇑60c, lang=> +≡ ▵60c
1848      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1849   13  |  if (! length(submodes)) return text;
1850      |________________________________________________________________________
1854 60e <mode_tracker()[3](\v) ⇑60a, lang=> +≡ ▵60b 60f▿
1855      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1856   11  | «parse_chunk_args-reset-modes 60c»
1857      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1858 We then iterate the text (until there is none left) looking for sub-modes or terminators in the regex.
1860 60f <mode_tracker()[4](\v) ⇑60a, lang=> +≡ ▵60e 61a⊳
1861      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1862   12  |   while((cindex >= 0) && length(text)) {
1863   13  |     if (match(text, "(" submodes ")", a)) {
1864      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1865 A bug that creeps in regularly during development is bad regexes of zero length which result in an infinite loop (as no text is consumed), so I catch that right away with this test.
1867 61a <mode_tracker()[5](\v) ⇑60a, lang=> +≡ ⊲60f 61b▿
1868      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1869   14  |       if (RLENGTH<1) {
1870   15  |         error(sprintf("Internal error, matched zero length submode, should be impossible - likely regex computation error\n" \
1871   16  |                 "Language=%s\nmode=%s\nmatch=%s\n", language, mode, submodes));
1872   17  |       }
1873      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1874 part is defined as the text up to the sub-mode or terminator, and this is appended to item --- which is the current text being gathered. If a mode has a delimiter, then item is reset each time a delimiter is found.
1875 ("hello_item, there_item")<wide-overbrace>^item,  (he said.)<wide-overbrace>^item
1877 61b <mode_tracker()[6](\v) ⇑60a, lang=> +≡ ▵61a 61c▿
1878      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1879   18  |       part = substr(text, 1, RSTART -1);
1880   19  |       item = item part;
1881      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1882 We must now determine what was matched. If it was a terminator, then we must restore the previous mode.
1884 61c <mode_tracker()[7](\v) ⇑60a, lang=> +≡ ▵61b 61d▿
1885      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1886   20  |       if (match(a[1], "^" terminators "$")) {
1887   21  | #printf("%2d EXIT  MODE [%s] by [%s] [%s]\n", cindex, mode, a[1], text) > "/dev/stderr"
1888   22  |         context[cindex, "values", ++context[cindex, "values"]] = item;
1889   23  |         delete context[cindex];
1890   24  |         context[""] = --cindex;
1891   25  |         if (cindex>=0) {
1892   26  |           mode = context[cindex, "mode"];
1893   27  |           language = context[cindex, "language"];
1894   28  |           «parse_chunk_args-reset-modes 60c»
1895   29  |         }
1896   30  |         item = item a[1];
1897   31  |         text = substr(text, 1 + length(part) + length(a[1]));
1898   32  |       }
1899      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1900 If a delimiter was matched, then we must store the current item in the parsed values array, and reset the item.
1902 61d <mode_tracker()[8](\v) ⇑60a, lang=> +≡ ▵61c 61e▿
1903      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1904   33  |       else if (match(a[1], "^" delimiters "$")) {
1905   34  |         if (cindex==0) {
1906   35  |           context[cindex, "values", ++context[cindex, "values"]] = item;
1907   36  |           item = "";
1908   37  |         } else {
1909   38  |           item = item a[1];
1910   39  |         }
1911   40  |         text = substr(text, 1 + length(part) + length(a[1]));
1912   41  |       }
1913      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1914 otherwise, if a new submode is detected (all submodes have terminators), we must create a nested parse context until we find the terminator for this mode.
1916 61e <mode_tracker()[9](\v) ⇑60a, lang=> +≡ ▵61d 62a⊳
1917      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1918   42  |  else if ((language, a[1], "terminators") in modes) {
1919   43  |         #check if new_mode is defined
1920   44  |         item = item a[1];
1921   45  | #printf("%2d ENTER MODE [%s] in [%s]\n", cindex, a[1], text) > "/dev/stderr"
1922   46  |         text = substr(text, 1 + length(part) + length(a[1]));
1923   47  |         context[""] = ++cindex;
1924   48  |         context[cindex, "mode"] = a[1];
1925   49  |         context[cindex, "language"] = language;
1926   50  |         mode = a[1];
1927   51  |         «parse_chunk_args-reset-modes 60c»
1928   52  |       } else {
1929   53  |         error(sprintf("Submode '%s' set unknown mode in text: %s\nLanguage %s Mode %s\n", a[1], text, language, mode));
1930   54  |         text = substr(text, 1 + length(part) + length(a[1]));
1931   55  |       }
1932   56  |     }
1933      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1934 In the final case, we parsed to the end of the string. If the string was entire, then we should have no nested mode context, but if the string was just a fragment we may have a mode context which must be preserved for the next fragment. Todo: Consideration ought to be given if sub-mode strings are split over two fragments.
1936 62a <mode_tracker()[10](\v) ⇑60a, lang=> +≡ ⊲61e
1937      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
1938   57  | else {
1939   58  |       context[cindex, "values", ++context[cindex, "values"]] = item text;
1940   59  |       text = "";
1941   60  |       item = "";
1942   61  |     }
1943   62  |   }
1944   63  | 
1945   64  |   context["item"] = item;
1946   65  | 
1947   66  |   if (length(item)) context[cindex, "values", ++context[cindex, "values"]] = item;
1948   67  |   return text;
1949   68  | }
1950      |________________________________________________________________________
1953 11.6.3.1 One happy chunk 
1954 All the mode tracker chunks are referred to here:
1956 62b <mode-tracker[1](\v), lang=> ≡ 
1957       ________________________________________________________________________
1958   1  | «new_mode_tracker() 58a»
1959   2  | «mode_tracker() 60a»
1960      |________________________________________________________________________
1961 Used by 37a, 62c 
1963 11.6.3.2 Tests 
1964 We can test this function like this:
1966 62c <pca-test.awk[1](\v), lang=awk> ≡ 
1967       ________________________________________________________________________
1968   1  | «error() 38a»
1969   2  | «mode-tracker 62b»
1970   3  | «parse_chunk_args() ?»
1971   4  | BEGIN {
1972   5  |   SUBSEP=".";
1973   6  |   «mode-definitions 52b»
1974   7  | 
1975   8  |   «test:mode-definitions 49c»
1976   9  | }
1977      |________________________________________________________________________
1978 not used 
1981 62d <pca-test.awk:summary[1](\v), lang=awk> ≡ 
1982       ________________________________________________________________________
1983   1  | if (e) {
1984   2  |   printf "Failed " e
1985   3  |   for (b in a) {
1986   4  |     print "a[" b "] => " a[b];
1987   5  |   }
1988   6  | } else {
1989   7  |   print "Passed"
1990   8  | }
1991   9  | split("", a);
1992   10  | e=0;
1993      |________________________________________________________________________
1994 Used by 49c 
1996 which should give this output:
1998 63a <pca-test.awk-results[1](\v), lang=> ≡ 
1999       ________________________________________________________________________
2000   1  | a[foo.quux.quirk] => 
2001   2  | a[foo.quux.a] => fleeg
2002   3  | a[foo.bar] => baz
2003   4  | a[etc] => 
2004   5  | a[name] => freddie
2005      |________________________________________________________________________
2006 not used 
2008 11.7 Escaping and Quoting 
2009 For the time being and to get around TeXmacs inability to export a TAB character, the right arrow ↦ whose UTF-8 sequence is ...
2010 To do: complete
2012 Another special character is used, the left-arrow ↤ with UTF-8 sequence 0xE2 0x86 0xA4 is used to strip any preceding white space as a way of un-tabbing and removing indent that has been applied — this is important for bash here documents, and the like. It's a filthy hack.
2013 To do: remove the hack
2016 63b <mode_tracker[4](\v) ⇑58c, lang=> +≡ ⊲59b 63c▿
2017      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2019   39  | function untab(text) {
2020   40  |   gsub("[[:space:]]*\xE2\x86\xA4","", text);
2021   41  |   return text;
2022   42  | }
2023      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2024 Each nested mode can optionally define a set of transforms to be applied to any text that is included from another language.
2025 This code can perform transforms from index c downwards.
2027 63c <mode_tracker[5](\v) ⇑58c, lang=awk> +≡ ▵63b
2028      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2029   43  | function transform_escape(context, text, top,
2030   44  |   c, cp, cpl, s, r)
2031   45  | {
2032   46  |   for(c = top; c >= 0; c--) {
2033   47  |     if ( (context[c, "language"], context[c, "mode"]) in escapes) {
2034   48  |       cpl = escapes[context[c, "language"], context[c, "mode"]];
2035   49  |       for (cp = 1; cp <= cpl; cp ++) {
2036   50  |         s = escapes[context[c, "language"], context[c, "mode"], cp, "s"];
2037   51  |         r = escapes[context[c, "language"], context[c, "mode"], cp, "r"];
2038   52  |         if (length(s)) {
2039   53  |           gsub(s, r, text);
2040   54  |         }
2041   55  |         if ( (context[c, "language"], context[c, "mode"], cp, "t") in escapes ) {
2042   56  |           quotes[src, "t"] = escapes[context[c, "language"], context[c, "mode"], cp, "t"];
2043   57  |         }
2044   58  |       }
2045   59  |     }
2046   60  |   }
2047   61  |   return text;
2048   62  | }
2049   63  | function dump_escaper(quotes, r, cc) {
2050   64  |   for(cc=1; cc<=c; cc++) {
2051   65  |     printf("%2d s[%s] r[%s]\n", cc, quotes[cc, "s"], quotes[cc, "r"]) > "/dev/stderr"
2052   66  |   }
2053   67  | }
2054      |________________________________________________________________________
2058 64a <test:escapes[1](\v), lang=sh> ≡ 
2059       ________________________________________________________________________
2060   1  | echo escapes test
2061   2  | passtest $FANGLE -Rtest:comment-quote $TXT_SRC &>/dev/null || ( echo "Comment-quote failed" && exit 1 )
2062      |________________________________________________________________________
2063 Used by 99b 
2065 Chapter 12Recognizing Chunks
2066 Fangle recognizes noweb chunks, but as we also want better LaTeX integration we will recognize any of these:
2067 •  notangle chunks matching the pattern ^<<.*?>>=
2068 •  chunks beginning with \begin{lstlistings}, possibly with \Chunk{...} on the previous line
2069 •  an older form I have used, beginning with \begin{Chunk}[options] --- also more suitable for plain LaTeX users1. Is there such a thing as plain LaTeX?  ^1. 
2070 12.1 Chunk start 
2071 The variable chunking is used to signify that we are processing a code chunk and not document. In such a state, input lines will be assigned to the current chunk; otherwise they are ignored.
2072 12.1.1 TeXmacs 
2073 We don't handle TeXmacs files natively yet, but rather instead emit unicode character sequences to mark up the text-export file which we do process.
2074 These hacks detect the unicode character sequences and retro-fit in the old TeX parsing.
2075 We convert ↦ into a tab character.
2077 65a <recognize-chunk[1](\v), lang=> ≡  65b▿
2078       ________________________________________________________________________
2080   1  | #/\n/ {
2081   2  | #  gsub("\n*$","");
2082   3  | #  gsub("\n", " ");
2083   4  | #}
2084   5  | #===
2085   6  | /\xE2\x86\xA6/ {
2086   7  |   gsub("\\xE2\\x86\\xA6", "\x09");
2087   8  | }
2088      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 37a
2089 TeXmacs back-tick handling is obscure, and a cut-n-paste back-tick from a shell window comes out as a unicode sequence2. that won't export to html, except as a NULL character (literal 0x00)  ^2 that is fixed-up here.
2091 65b <recognize-chunk[2](\v) ⇑65a, lang=> +≡ ▵65a 66a⊳
2092      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2093   9  | 
2094   10  | /\xE2\x80\x98/ {
2095   11  |   gsub("\\xE2\\x80\\x98", "‘");
2096   12  | }
2097      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2098 In the TeXmacs output, the start of a chunk will appear like this:
2099   5b<example-chunk^K[1](arg1,^K arg2^K^K), lang=C> ≡
2100 We detect the the start of a TeXmacs chunk by detecting the ≡ symbol which occurs near the end of the line. We obtain the chunk name, the chunk parameters, and the chunk language.
2102 66a <recognize-chunk[3](\v) ⇑65a, lang=> +≡ ⊲65b 66b▿
2103      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2104   13  | 
2105   14  | /\xE2\x89\xA1/ {
2106   15  |   if (match($0, "^ *([^[ ]* |)<([^[ ]*)\\[[0-9]*\\][(](.*)[)].*, lang=([^ ]*)>", line)) {
2107   16  |     next_chunk_name=line[2];
2108   17  |     get_texmacs_chunk_args(line[3], next_chunk_params);
2109   18  |     gsub(ARG_SEPARATOR ",? ?", ";", line[3]);
2110   19  |     params = "params=" line[3];
2111   20  |     if ((line[4])) {
2112   21  |       params = params ",language=" line[4]
2113   22  |     }
2114   23  |     get_tex_chunk_args(params, next_chunk_opts);
2115   24  |     new_chunk(next_chunk_name, next_chunk_opts, next_chunk_params);
2116   25  |     texmacs_chunking = 1;
2117   26  |   } else {
2118   27  |     # warning(sprintf("Unexpected chunk match: %s\n", $_))
2119   28  |   }
2120   29  |   next;
2121   30  | }
2122      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2123 12.1.2 lstlistings 
2124 Our current scheme is to recognize the new lstlisting chunks, but these may be preceded by a \Chunk command which in L Y X is a more convenient way to pass the chunk name to the \begin{lstlistings} command, and a more visible way to specify other lstset settings.
2125 The arguments to the \Chunk command are a name, and then a comma-seperated list of key-value pairs after the manner of \lstset. (In fact within the LaTeX \Chunk macro (section 17.2.1) the text name= is prefixed to the argument which is then literally passed to \lstset).
2127 66b <recognize-chunk[4](\v) ⇑65a, lang=awk> +≡ ▵66a 66c▿
2128      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2129   31  | /^\\Chunk{/ {
2130   32  |   if (match($0, "^\\\\Chunk{ *([^ ,}]*),?(.*)}", line)) {
2131   33  |     next_chunk_name = line[1];
2132   34  |     get_tex_chunk_args(line[2], next_chunk_opts);
2133   35  |   }
2134   36  |   next;
2135   37  | }
2136      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2137 We also make a basic attempt to parse the name out of the \lstlistings[name=chunk-name] text, otherwise we fall back to the name found in the previous chunk command. This attempt is very basic and doesn't support commas or spaces or square brackets as part of the chunkname. We also recognize \begin{Chunk} which is convenient for some users3. but not yet supported in the LaTeX macros  ^3.
2139 66c <recognize-chunk[5](\v) ⇑65a, lang=> +≡ ▵66b 67a⊳
2140      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2141   38  | /^\\begin{lstlisting}|^\\begin{Chunk}/ {
2142   39  |   if (match($0, "}.*[[,] *name= *{? *([^], }]*)", line)) {
2143   40  |     new_chunk(line[1]);
2144   41  |   } else {
2145   42  |     new_chunk(next_chunk_name, next_chunk_opts);
2146   43  |   }
2147   44  |   chunking=1;
2148   45  |   next;
2149   46  | }
2150      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2151 12.2 Chunk Body 
2152 12.2.1 TeXmacs 
2153 A chunk body in TeXmacs ends with |________... if it is the final chunklet of a chunk, or if there are further chunklets it ends with |\/\/\/... which is a depiction of a jagged line of torn paper.
2155 67a <recognize-chunk[6](\v) ⇑65a, lang=> +≡ ⊲66c 67b▿
2156      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2157   47  | /^ *\|____________*/ && texmacs_chunking {
2158   48  |   active_chunk="";
2159   49  |   texmacs_chunking=0;
2160   50  |   chunking=0;
2161   51  | }
2162   52  | /^ *\|\/\\/ && texmacs_chunking {
2163   53  |   texmacs_chunking=0;
2164   54  |   chunking=0;
2165   55  |   active_chunk="";
2166   56  | }
2167      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2168 It has been observed that not every line of output when a TeXmacs chunk is active is a line of chunk. This may no longer be true, but we set a variable texmacs_chunk if the current line is a chunk line.
2169 Initially we set this to zero...
2171 67b <recognize-chunk[7](\v) ⇑65a, lang=> +≡ ▵67a 67c▿
2172      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2173   57  | texmacs_chunk=0;
2174      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2175 ...and then we look to see if the current line is a chunk line.
2176 TeXmacs lines look like this:   3 | main() { so we detect the lines by leading white space, digits, more whiter space and a vertical bar followed by at least once space.
2177 If we find such a line, we remove this line-header and set texmacs_chunk=1 as well as chunking=1
2179 67c <recognize-chunk[8](\v) ⇑65a, lang=> +≡ ▵67b 67d▿
2180      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2181   58  | /^ *[1-9][0-9]* *\| / {
2182   59  |   if (texmacs_chunking) {
2183   60  |     chunking=1;
2184   61  |     texmacs_chunk=1;
2185   62  |     gsub("^ *[1-9][0-9]* *\\| ", "")
2186   63  |   }
2187   64  | }
2188      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2189 When TeXmacs chunking, lines that commence with \/ or __ are not chunk content but visual framing, and are skipped.
2191 67d <recognize-chunk[9](\v) ⇑65a, lang=> +≡ ▵67c 68a⊳
2192      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2193   65  | /^ *\.\/\\/ && texmacs_chunking {
2194   66  |   next;
2195   67  | }
2196   68  | /^ *__*$/ && texmacs_chunking {
2197   69  |   next;
2198   70  | }
2199      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2200 Any other line when TeXmacs chunking is considered to be a line-wrapped line.
2202 68a <recognize-chunk[10](\v) ⇑65a, lang=> +≡ ⊲67d 68b▿
2203      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2204   71  | texmacs_chunking {
2205   72  |   if (! texmacs_chunk) {
2206   73  |     # must be a texmacs continued line
2207   74  |     chunking=1;
2208   75  |     texmacs_chunk=1;
2209   76  |   }
2210   77  | }
2211      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2212 This final chunklet seems bogus and probably stops L Y X working.
2214 68b <recognize-chunk[11](\v) ⇑65a, lang=> +≡ ▵68a 68c▿
2215      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2216   78  | ! texmacs_chunk {
2217   79  | #  texmacs_chunking=0;
2218   80  |   chunking=0;
2219   81  | }
2220      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2221 12.2.2 Noweb 
2222 We recognize notangle style chunks too:
2224 68c <recognize-chunk[12](\v) ⇑65a, lang=awk> +≡ ▵68b 68d▿
2225      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2226   82  | /^[<]<.*[>]>=/ {
2227   83  |   if (match($0, "^[<]<(.*)[>]>= *$", line)) {
2228   84  |     chunking=1;
2229   85  |     notangle_mode=1;
2230   86  |     new_chunk(line[1]);
2231   87  |     next;
2232   88  |   }
2233   89  | }
2234      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2235 12.3 Chunk end 
2236 Likewise, we need to recognize when a chunk ends.
2237 12.3.1 lstlistings 
2238 The e in [e]nd{lislisting} is surrounded by square brackets so that when this document is processed, this chunk doesn't terminate early when the lstlistings package recognizes it's own end-string!4. This doesn't make sense as the regex is anchored with ^, which this line does not begin with!  ^4
2240 68d <recognize-chunk[13](\v) ⇑65a, lang=> +≡ ▵68c 69a⊳
2241      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2242   90  | /^\\[e]nd{lstlisting}|^\\[e]nd{Chunk}/ {
2243   91  |   chunking=0;
2244   92  |   active_chunk="";
2245   93  |   next;
2246   94  | }
2247      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2248 12.3.2 noweb 
2250 69a <recognize-chunk[14](\v) ⇑65a, lang=> +≡ ⊲68d 69b▿
2251      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2252   95  | /^@ *$/ {
2253   96  |   chunking=0;
2254   97  |   active_chunk="";
2255   98  | }
2256      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2257 All other recognizers are only of effect if we are chunking; there's no point in looking at lines if they aren't part of a chunk, so we just ignore them as efficiently as we can.
2259 69b <recognize-chunk[15](\v) ⇑65a, lang=> +≡ ▵69a 69c▿
2260      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2261   99  | ! chunking { next; }
2262      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2263 12.4 Chunk contents 
2264 Chunk contents are any lines read while chunking is true. Some chunk contents are special in that they refer to other chunks, and will be replaced by the contents of these chunks when the file is generated.
2265 We add the output record separator ORS to the line now, because we will set ORS to the empty string when we generate the output5. So that we can partial print lines using print instead of printf. 
2266 To do: This does't make sense
2269 69c <recognize-chunk[16](\v) ⇑65a, lang=> +≡ ▵69b
2270      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2271   100  | length(active_chunk) {
2272   101  |   «process-chunk-tabs 69e»
2273   102  |   «process-chunk 70b»
2274   103  | }
2275      |________________________________________________________________________
2278 If a chunk just consisted of plain text, we could handle the chunk like this:
2280 69d <process-chunk-simple[1](\v), lang=> ≡ 
2281       ________________________________________________________________________
2282   1  | chunk_line(active_chunk, $0 ORS);
2283      |________________________________________________________________________
2284 not used 
2286 but in fact a chunk can include references to other chunks. Chunk includes are traditionally written as <<chunk-name>> but we support other variations, some of which are more suitable for particular editing systems.
2287 However, we also process tabs at this point. A tab at input can be replaced by a number of spaces defined by the tabs variable, set by the -T option. Of course this is poor tab behaviour, we should probably have the option to use proper counted tab-stops and process this on output.
2289 69e <process-chunk-tabs[1](\v), lang=> ≡ 
2290       ________________________________________________________________________
2291   1  | if (length(tabs)) {
2292   2  |   gsub("\t", tabs);
2293   3  | }
2294      |________________________________________________________________________
2295 Used by 65a 
2297 12.4.1 lstlistings 
2298 If \lstset{escapeinside={=<}{>}} is set, then we can use <chunk-name ?> in listings. The sequence =< was chosen because:
2299 1.it is a better mnemonic than <<chunk-name>> in that the = sign signifies equivalence or substitutability.
2300 2.and because =< is not valid in C or any language I can think of.
2301 3.and also because lstlistings doesn't like >> as an end delimiter for the texcl escape, so we must make do with a single > which is better complemented by =< than by <<. 
2302 Unfortunately the =<...> that we use re-enters a LaTeX parsing mode in which some characters are special, e.g. # \ and so these cause trouble if used in arguments to \chunkref. At some point I must fix the LaTeX command \chunkref so that it can accept these literally, but until then, when writing chunkref argumemts that need these characters, I must use the forms \textbackslash{} and \#; so I also define a hacky chunk delatex to be used further on whose purpose it is to remove these from any arguments parsed by fangle.
2304 70a <delatex[1](text\v\v), lang=> ≡ 
2305       ________________________________________________________________________
2306   1  | # FILTHY HACK
2307   2  | gsub("\\\\#", "#", ${text});
2308   3  | gsub("\\\\textbackslash{}", "\\", ${text});
2309   4  | gsub("\\\\\\^", "^", ${text});
2310      |________________________________________________________________________
2311 Used by 70b 
2313 As each chunk line may contain more than one chunk include, we will split out chunk includes in an iterative fashion6. Contrary to our use of split when substituting parameters in chapter ?  ^6.
2314 First, as long as the chunk contains a \chunkref command we take as much as we can up to the first \chunkref command.
2315 TeXmacs text output uses ⟨...⟩ which comes out as unicode sequences 0xC2 0xAB ... 0xC2 0xBB. Modern awk will interpret [^\xC2\xBB] as a single unicode character if LANG is set correctly to the sub-type UTF-8, e.g. LANG=en_GB.UTF-8, otherwise [^\xC2\xBB] will be treated as a two character negated match — but this should not interfere with the function.
2317 70b <process-chunk[1](\v), lang=> ≡  70c▿
2318       ________________________________________________________________________
2319   1  | chunk = $0;
2320   2  | indent = 0;
2321   3  | while(match(chunk,"(\xC2\xAB)([^\xC2\xBB]*) [^\xC2\xBB]*\xC2\xBB", line) ||
2322   4  |       match(chunk, 
2323   5  |             "([=]<\\\\chunkref{([^}>]*)}(\\(.*\\)|)>|<<([a-zA-Z_][-a-zA-Z0-9_]*)>>)", 
2324   6  |             line)\
2325   7  | ) {
2326   8  |   chunklet = substr(chunk, 1, RSTART - 1);
2327      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 65a
2328 We keep track of the indent count, by counting the number of literal characters found. We can then preserve this indent on each output line when multi-line chunks are expanded.
2329 We then process this first part literal text, and set the chunk which is still to be processed to be the text after the \chunkref command, which we will process next as we continue around the loop.
2331 70c <process-chunk[2](\v) ⇑70b, lang=> +≡ ▵70b 71a⊳
2332      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2333   9  |   indent += length(chunklet);
2334   10  |   chunk_line(active_chunk, chunklet);
2335   11  |   chunk = substr(chunk, RSTART + RLENGTH);
2336      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2337 We then consider the type of chunk command we have found, whether it is the fangle style command beginning with =< the older notangle style beginning with <<.
2338 Fangle chunks may have parameters contained within square brackets. These will be matched in line[3] and are considered at this stage of processing to be part of the name of the chunk to be included.
2340 71a <process-chunk[3](\v) ⇑70b, lang=> +≡ ⊲70c 71b▿
2341      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2342   12  |   if (substr(line[1], 1, 1) == "=") {
2343   13  |     # chunk name up to }
2344   14  |         «delatex\v(line[3]\v) 70a»
2345   15  |     chunk_include(active_chunk, line[2] line[3], indent);
2346   16  |   } else if (substr(line[1], 1, 1) == "<") {
2347   17  |     chunk_include(active_chunk, line[4], indent);
2348   18  |   } else if (line[1] == "\xC2\xAB") {
2349   19  |     chunk_include(active_chunk, line[2], indent);
2350   20  |   } else {
2351   21  |     error("Unknown chunk fragment: " line[1]);
2352   22  |   }
2353      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2354 The loop will continue until there are no more chunkref statements in the text, at which point we process the final part of the chunk.
2356 71b <process-chunk[4](\v) ⇑70b, lang=> +≡ ▵71a 71c▿
2357      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2358   23  | }
2359   24  | chunk_line(active_chunk, chunk);
2360      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2361 We add the newline character as a chunklet on it's own, to make it easier to detect new lines and thus manage indentation when processing the output.
2363 71c <process-chunk[5](\v) ⇑70b, lang=> +≡ ▵71b
2364      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2365   25  | chunk_line(active_chunk, "\n");
2366      |________________________________________________________________________
2369 We will also permit a chunk-part number to follow in square brackets, so that <chunk-name[1] ?> will refer to the first part only. This can make it easy to include a C function prototype in a header file, if the first part of the chunk is just the function prototype without the trailing semi-colon. The header file would include the prototype with the trailing semi-colon, like this:
2370 <chunk-name[1] ?>
2371 This is handled in section 14.1.1
2372 We should perhaps introduce a notion of language specific chunk options; so that perhaps we could specify:
2373 =<\chunkref{chunk-name[function-declaration]}
2374 which applies a transform function-declaration to the chunk --- which in this case would extract a function prototype from a function. 
2375 To do: Do it
2377 Chapter 13Processing Options
2378 At the start, first we set the default options.
2380 73a <default-options[1](\v), lang=> ≡ 
2381       ________________________________________________________________________
2382   1  | debug=0;
2383   2  | linenos=0;
2384   3  | notangle_mode=0;
2385   4  | root="*";
2386   5  | tabs = "";
2387      |________________________________________________________________________
2388 Used by 73d 
2390 Then we use getopt the standard way, and null out ARGV afterwards in the normal AWK fashion.
2392 73b <read-options[1](\v), lang=> ≡ 
2393       ________________________________________________________________________
2394   1  | Optind = 1    # skip ARGV[0]
2395   2  | while(getopt(ARGC, ARGV, "R:LdT:hr")!=-1) {
2396   3  |   «handle-options 73c»
2397   4  | }
2398   5  | for (i=1; i<Optind; i++) { ARGV[i]=""; }
2399      |________________________________________________________________________
2400 Used by 73d 
2402 This is how we handle our options:
2404 73c <handle-options[1](\v), lang=> ≡ 
2405       ________________________________________________________________________
2406   1  | if (Optopt == "R") root = Optarg;
2407   2  | else if (Optopt == "r") root="";
2408   3  | else if (Optopt == "L") linenos = 1;
2409   4  | else if (Optopt == "d") debug = 1;
2410   5  | else if (Optopt == "T") tabs = indent_string(Optarg+0);
2411   6  | else if (Optopt == "h") help();
2412   7  | else if (Optopt == "?") help();
2413      |________________________________________________________________________
2414 Used by 73b 
2416 We do all of this at the beginning of the program
2418 73d <begin[1](\v), lang=> ≡ 
2419       ________________________________________________________________________
2420   1  | BEGIN {
2421   2  |   «constants 39a»
2422   3  |   «mode-definitions 52b»
2423   4  |   «default-options 73a»
2424   5  | 
2425   6  |   «read-options 73b»
2426   7  | }
2427      |________________________________________________________________________
2428 Used by 37a 
2430 And have a simple help function
2432 73e <help()[1](\v), lang=> ≡ 
2433       ________________________________________________________________________
2434   1  | function help() {
2435   2  |   print "Usage:"
2436   3  |   print "  fangle [-L] -R<rootname> [source.tex ...]"
2437   4  |   print "  fangle -r [source.tex ...]"
2438   5  |   print "  If the filename, source.tex is not specified then stdin is used"
2439   6  |   print
2440   7  |   print "-L causes the C statement: #line <lineno> \"filename\"" to be issued"
2441   8  |   print "-R causes the named root to be written to stdout"
2442   9  |   print "-r lists all roots in the file (even those used elsewhere)"
2443   10  |   exit 1;
2444   11  | }
2445      |________________________________________________________________________
2446 not used 
2448 Chapter 14Generating the Output
2449 We generate output by calling output_chunk, or listing the chunk names.
2451 75a <generate-output[1](\v), lang=> ≡ 
2452       ________________________________________________________________________
2453   1  | if (length(root)) output_chunk(root);
2454   2  | else output_chunk_names();
2455      |________________________________________________________________________
2456 Used by 75c 
2458 We also have some other output debugging:
2460 75b <debug-output[1](\v), lang=> ≡ 
2461       ________________________________________________________________________
2462   1  | if (debug) {
2463   2  |   print "------ chunk names "
2464   3  |   output_chunk_names();
2465   4  |   print "====== chunks"
2466   5  |   output_chunks();
2467   6  |   print "++++++ debug"
2468   7  |   for (a in chunks) {
2469   8  |     print a "=" chunks[a];
2470   9  |   }
2471   10  | }
2472      |________________________________________________________________________
2473 Used by 75c 
2475 We do both of these at the end. We also set ORS="" because each chunklet is not necessarily a complete line, and we already added ORS to each input line in section 12.4.
2477 75c <end[1](\v), lang=> ≡ 
2478       ________________________________________________________________________
2479   1  | END {
2480   2  |   «debug-output 75b»
2481   3  |   ORS="";
2482   4  |   «generate-output 75a»
2483   5  | }
2484      |________________________________________________________________________
2485 Used by 37a 
2487 We write chunk names like this. If we seem to be running in notangle compatibility mode, then we enclose the name like this <<name>> the same way notangle does:
2489 75d <output_chunk_names()[1](\v), lang=> ≡ 
2490       ________________________________________________________________________
2491   1  | function output_chunk_names(   c, prefix, suffix) 
2492   2  | {
2493   3  |   if (notangle_mode) {
2494   4  |     prefix="<<";
2495   5  |     suffix=">>";
2496   6  |   }
2497   7  |   for (c in chunk_names) {
2498   8  |     print prefix c suffix "\n";
2499   9  |   }
2500   10  | }
2501      |________________________________________________________________________
2502 Used by 37a 
2504 This function would write out all chunks
2506 75e <output_chunks()[1](\v), lang=> ≡ 
2507       ________________________________________________________________________
2508   1  | function output_chunks(  a) 
2509   2  | {
2510   3  |   for (a in chunk_names) {
2511   4  |     output_chunk(a);
2512   5  |   }
2513   6  | }
2514   7  | 
2515   8  | function output_chunk(chunk) {
2516   9  |   newline = 1;
2517   10  |   lineno_needed = linenos;
2518   11  | 
2519   12  |   write_chunk(chunk);
2520   13  | }
2521   14  | 
2522      |________________________________________________________________________
2523 Used by 37a 
2525 14.1 Assembling the Chunks 
2526 chunk_path holds a string consisting of the names of all the chunks that resulted in this chunk being output. It should probably also contain the source line numbers at which each inclusion also occured.
2527 We first initialize the mode tracker for this chunk.
2529 76a <write_chunk()[1](\v), lang=awk> ≡  76b▿
2530       ________________________________________________________________________
2531   1  | function write_chunk(chunk_name) {
2532   2  |   «awk-delete-array\v(context\v) 37d»
2533   3  |   return write_chunk_r(chunk_name, context);
2534   4  | }
2535   5  | 
2536   6  | function write_chunk_r(chunk_name, context, indent, tail,
2537   7  |   # optional vars
2538   8  |   chunk_path, chunk_args, 
2539   9  |   # local vars
2540   10  |   context_origin,
2541   11  |   chunk_params, part, max_part, part_line, frag, max_frag, text, 
2542   12  |   chunklet, only_part, call_chunk_args, new_context)
2543   13  | {
2544   14  |   if (debug) debug_log("write_chunk_r(" chunk_name ")");
2545      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 37a
2546 14.1.1 Chunk Parts 
2547 As mentioned in section ?, a chunk name may contain a part specifier in square brackets, limiting the parts that should be emitted.
2549 76b <write_chunk()[2](\v) ⇑76a, lang=> +≡ ▵76a 76c▿
2550      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2551   15  |   if (match(chunk_name, "^(.*)\\[([0-9]*)\\]$", chunk_name_parts)) {
2552   16  |     chunk_name = chunk_name_parts[1];
2553   17  |     only_part = chunk_name_parts[2];
2554   18  |   }
2555      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2556 We then create a mode tracker
2558 76c <write_chunk()[3](\v) ⇑76a, lang=> +≡ ▵76b 77a⊳
2559      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2560   19  |   context_origin = context[""];
2561   20  |   new_context = push_mode_tracker(context, chunks[chunk_name, "language"], "");
2562      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2563 We extract into chunk_params the names of the parameters that this chunk accepts, whose values were (optionally) passed in chunk_args.
2565 77a <write_chunk()[4](\v) ⇑76a, lang=> +≡ ⊲76c 77b▿
2566      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2567   21  |   split(chunks[chunk_name, "params"], chunk_params, " *; *");
2568      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2569 To assemble a chunk, we write out each part.
2571 77b <write_chunk()[5](\v) ⇑76a, lang=> +≡ ▵77a
2572      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2573   22  |   if (! (chunk_name in chunk_names)) {
2574   23  |     error(sprintf(_"The root module <<%s>> was not defined.\nUsed by: %s",\
2575   24  |                   chunk_name, chunk_path));
2576   25  |   }
2577   26  | 
2578   27  |   max_part = chunks[chunk_name, "part"];
2579   28  |   for(part = 1; part <= max_part; part++) {
2580   29  |     if (! only_part || part == only_part) {
2581   30  |       «write-part 77c»
2582   31  |     }
2583   32  |   }
2584   33  |   if (! pop_mode_tracker(context, context_origin)) {
2585   34  |     dump_mode_tracker(context);
2586   35  |     error(sprintf(_"Module %s did not close context properly.\nUsed by: %s\n", chunk_name, chunk_path));
2587   36  |   }
2588   37  | }
2589      |________________________________________________________________________
2592 A part can either be a chunklet of lines, or an include of another chunk.
2593 Chunks may also have parameters, specified in LaTeX style with braces after the chunk name --- looking like this in the document: chunkname{param1, param2}. Arguments are passed in square brackets: \chunkref{chunkname}[arg1, arg2].
2594 Before we process each part, we check that the source position hasn't changed unexpectedly, so that we can know if we need to output a new file-line directive.
2596 77c <write-part[1](\v), lang=> ≡ 
2597       ________________________________________________________________________
2598   1  | «check-source-jump 79d»
2599   2  | 
2600   3  | chunklet = chunks[chunk_name, "part", part];
2601   4  | if (chunks[chunk_name, "part", part, "type"] == part_type_chunk) {
2602   5  |   «write-included-chunk 77d»
2603   6  | } else if (chunklet SUBSEP "line" in chunks) {
2604   7  |   «write-chunklets 78a»
2605   8  | } else {
2606   9  |   # empty last chunklet
2607   10  | }
2608      |________________________________________________________________________
2609 Used by 76a 
2611 To write an included chunk, we must detect any optional chunk arguments in parenthesis. Then we recurse calling write_chunk().
2613 77d <write-included-chunk[1](\v), lang=> ≡ 
2614       ________________________________________________________________________
2615   1  | if (match(chunklet, "^([^\\[\\(]*)\\((.*)\\)$", chunklet_parts)) {
2616   2  |   chunklet = chunklet_parts[1];
2617   3  | # hack
2618   4  | gsub(sprintf("%c",11), "", chunklet);
2619   5  | gsub(sprintf("%c",11), "", chunklet_parts[2]);
2620   6  |   parse_chunk_args("c-like", chunklet_parts[2], call_chunk_args, "(");
2621   7  |   for (c in call_chunk_args) {
2622   8  |     call_chunk_args[c] = expand_chunk_args(call_chunk_args[c], chunk_params, chunk_args);
2623   9  |   }
2624   10  | } else {
2625   11  |   split("", call_chunk_args);
2626   12  | }
2627   13  | 
2628   14  | write_chunk_r(chunklet, context,
2629   15  |             chunks[chunk_name, "part", part, "indent"] indent,
2630   16  |             chunks[chunk_name, "part", part, "tail"],
2631   17  |             chunk_path "\n         " chunk_name,
2632   18  |             call_chunk_args);
2633      |________________________________________________________________________
2634 Used by 77c 
2636 Before we output a chunklet of lines, we first emit the file and line number if we have one, and if it is safe to do so.
2637 Chunklets are generally broken up by includes, so the start of a chunklet is a good place to do this. Then we output each line of the chunklet.
2638 When it is not safe, such as in the middle of a multi-line macro definition, lineno_suppressed is set to true, and in such a case we note that we want to emit the line statement when it is next safe.
2640 78a <write-chunklets[1](\v), lang=> ≡  78b▿
2641       ________________________________________________________________________
2642   1  | max_frag = chunks[chunklet, "line"];
2643   2  | for(frag = 1; frag <= max_frag; frag++) {
2644   3  |   «write-file-line 79c»
2645      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 77c
2646 We then extract the chunklet text and expand any arguments.
2648 78b <write-chunklets[2](\v) ⇑78a, lang=> +≡ ▵78a 78c▿
2649      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2650   4  | 
2651   5  |   text = chunks[chunklet, frag];
2652   6  |  
2653   7  |   /* check params */
2654   8  |   text = expand_chunk_args(text, chunk_params, chunk_args);
2655      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2656 If the text is a single newline (which we keep separate - see 6) then we increment the line number. In the case where this is the last line of a chunk and it is not a top-level chunk we replace the newline with an empty string --- because the chunk that included this chunk will have the newline at the end of the line that included this chunk.
2657 We also note by newline = 1 that we have started a new line, so that indentation can be managed with the following piece of text.
2659 78c <write-chunklets[3](\v) ⇑78a, lang=> +≡ ▵78b 78d▿
2660      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2661   9  | 
2662   10  |  if (text == "\n") {
2663   11  |     lineno++;
2664   12  |     if (part == max_part && frag == max_frag && length(chunk_path)) {
2665   13  |       text = "";
2666   14  |       break;
2667   15  |     } else {
2668   16  |       newline = 1;
2669   17  |     }
2670      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2671 If this text does not represent a newline, but we see that we are the first piece of text on a newline, then we prefix our text with the current indent. 
2672 Note 1. newline is a global output-state variable, but the indent is not. 
2674 78d <write-chunklets[4](\v) ⇑78a, lang=> +≡ ▵78c 79a⊳
2675      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2676   18  |   } else if (length(text) || length(tail)) {
2677   19  |     if (newline) text = indent text;
2678   20  |     newline = 0;
2679   21  |   }
2680   22  | 
2681      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2682 Tail will soon no longer be relevant once mode-detection is in place.
2684 79a <write-chunklets[5](\v) ⇑78a, lang=> +≡ ⊲78d 79b▿
2685      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2686   23  |   text = text tail;
2687   24  |   mode_tracker(context, text);
2688   25  |   print untab(transform_escape(context, text, new_context));
2689      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2690 If a line ends in a backslash --- suggesting continuation --- then we supress outputting file-line as it would probably break the continued lines.
2692 79b <write-chunklets[6](\v) ⇑78a, lang=> +≡ ▵79a
2693      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2694   26  |   if (linenos) {
2695   27  |     lineno_suppressed = substr(lastline, length(lastline)) == "\\";
2696   28  |   }
2697   29  | }
2698      |________________________________________________________________________
2701 Of course there is no point in actually outputting the source filename and line number (file-line) if they don't say anything new! We only need to emit them if they aren't what is expected, or if we we not able to emit one when they had changed.
2703 79c <write-file-line[1](\v), lang=> ≡ 
2704       ________________________________________________________________________
2705   1  | if (newline && lineno_needed && ! lineno_suppressed) {
2706   2  |   filename = a_filename;
2707   3  |   lineno = a_lineno;
2708   4  |   print "#line " lineno " \"" filename "\"\n"
2709   5  |   lineno_needed = 0;
2710   6  | }
2711      |________________________________________________________________________
2712 Used by 78a 
2714 We check if a new file-line is needed by checking if the source line matches what we (or a compiler) would expect.
2716 79d <check-source-jump[1](\v), lang=> ≡ 
2717       ________________________________________________________________________
2718   1  | if (linenos && (chunk_name SUBSEP "part" SUBSEP part SUBSEP "FILENAME" in chunks)) {
2719   2  |   a_filename = chunks[chunk_name, "part", part, "FILENAME"];
2720   3  |   a_lineno = chunks[chunk_name, "part", part, "LINENO"];
2721   4  |   if (a_filename != filename || a_lineno != lineno) {
2722   5  |     lineno_needed++;
2723   6  |   }
2724   7  | }
2725      |________________________________________________________________________
2726 Used by 77c 
2728 Chapter 15Storing Chunks
2729 Awk has pretty limited data structures, so we will use two main hashes. Uninterrupted sequences of a chunk will be stored in chunklets and the chunklets used in a chunk will be stored in chunks.
2731 81a <constants[2](\v) ⇑39a, lang=> +≡ ⊲39a
2732      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2733   2  | part_type_chunk=1;
2734   3  | SUBSEP=",";
2735      |________________________________________________________________________
2738 The params mentioned are not chunk parameters for parameterized chunks, as mentioned in 10.2, but the lstlistings style parameters used in the \Chunk command1. The params parameter is used to hold the parameters for parameterized chunks  ^1.
2740 81b <chunk-storage-functions[1](\v), lang=> ≡  81c▿
2741       ________________________________________________________________________
2742   1  | function new_chunk(chunk_name, opts, args,
2743   2  |   # local vars
2744   3  |   p, append )
2745   4  | {
2746   5  |   # HACK WHILE WE CHANGE TO ( ) for PARAM CHUNKS
2747   6  |   gsub("\\(\\)$", "", chunk_name);
2748   7  |   if (! (chunk_name in chunk_names)) {
2749   8  |     if (debug) print "New chunk " chunk_name;
2750   9  |     chunk_names[chunk_name];
2751   10  |     for (p in opts) {
2752   11  |       chunks[chunk_name, p] = opts[p];
2753   12  |       if (debug) print "chunks[" chunk_name "," p "] = " opts[p];
2754   13  |     }
2755   14  |     for (p in args) {
2756   15  |       chunks[chunk_name, "params", p] = args[p];
2757   16  |     }
2758   17  |     if ("append" in opts) {
2759   18  |       append=opts["append"];
2760   19  |       if (! (append in chunk_names)) {
2761   20  |         warning("Chunk " chunk_name " is appended to chunk " append " which is not defined yet");
2762   21  |         new_chunk(append);
2763   22  |       }
2764   23  |       chunk_include(append, chunk_name);
2765   24  |       chunk_line(append, ORS);
2766   25  |     }
2767   26  |   }
2768   27  |   active_chunk = chunk_name;
2769   28  |   prime_chunk(chunk_name);
2770   29  | }
2771      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 37a
2773 81c <chunk-storage-functions[2](\v) ⇑81b, lang=> +≡ ▵81b 82a⊳
2774      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2775   30  | 
2776   31  | function prime_chunk(chunk_name)
2777   32  | {
2778   33  |   chunks[chunk_name, "part", ++chunks[chunk_name, "part"] ] = \
2779   34  |          chunk_name SUBSEP "chunklet" SUBSEP "" ++chunks[chunk_name, "chunklet"];
2780   35  |   chunks[chunk_name, "part", chunks[chunk_name, "part"], "FILENAME"] = FILENAME;
2781   36  |   chunks[chunk_name, "part", chunks[chunk_name, "part"], "LINENO"] = FNR + 1;
2782   37  | }
2783   38  | 
2784   39  | function chunk_line(chunk_name, line){
2785   40  |   chunks[chunk_name, "chunklet", chunks[chunk_name, "chunklet"],
2786   41  |          ++chunks[chunk_name, "chunklet", chunks[chunk_name, "chunklet"], "line"]  ] = line;
2787   42  | }
2788   43  | 
2789      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2790 Chunk include represents a chunkref statement, and stores the requirement to include another chunk. The parameter indent represents the quanity of literal text characters that preceded this chunkref statement and therefore by how much additional lines of the included chunk should be indented.
2792 82a <chunk-storage-functions[3](\v) ⇑81b, lang=> +≡ ⊲81c 82b▿
2793      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2794   44  | function chunk_include(chunk_name, chunk_ref, indent, tail)
2795   45  | {
2796   46  |   chunks[chunk_name, "part", ++chunks[chunk_name, "part"] ] = chunk_ref;
2797   47  |   chunks[chunk_name, "part", chunks[chunk_name, "part"], "type" ] = part_type_chunk;
2798   48  |   chunks[chunk_name, "part", chunks[chunk_name, "part"], "indent" ] = indent_string(indent);
2799   49  |   chunks[chunk_name, "part", chunks[chunk_name, "part"], "tail" ] = tail;
2800   50  |   prime_chunk(chunk_name);
2801   51  | }
2802   52  | 
2803      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2804 The indent is calculated by indent_string, which may in future convert some spaces into tab characters. This function works by generating a printf padded format string, like %22s for an indent of 22, and then printing an empty string using that format.
2806 82b <chunk-storage-functions[4](\v) ⇑81b, lang=> +≡ ▵82a
2807      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2808   53  | function indent_string(indent) {
2809   54  |   return sprintf("%" indent "s", "");
2810   55  | }
2811      |________________________________________________________________________
2814 Chapter 16getopt
2815 I use Arnold Robbins public domain getopt (1993 revision). This is probably the same one that is covered in chapter 12 of âĂIJEdition 3 of GAWK: Effective AWK Programming: A User's Guide for GNU AwkâĂİ but as that is licensed under the GNU Free Documentation License, Version 1.3, which conflicts with the GPL3, I can't use it from there (or it's accompanying explanations), so I do my best to explain how it works here.
2816 The getopt.awk header is:
2818 83a <getopt.awk-header[1](\v), lang=> ≡ 
2819       ________________________________________________________________________
2820   1  | # getopt.awk --- do C library getopt(3) function in awk
2821   2  | #
2822   3  | # Arnold Robbins, arnold@skeeve.com, Public Domain
2823   4  | #
2824   5  | # Initial version: March, 1991
2825   6  | # Revised: May, 1993
2826   7  | 
2827      |________________________________________________________________________
2828 Used by 37a, 84c, 85a 
2830 The provided explanation is:
2832 83b <getopt.awk-notes[1](\v), lang=> ≡ 
2833       ________________________________________________________________________
2834   1  | # External variables:
2835   2  | #    Optind -- index in ARGV of first nonoption argument
2836   3  | #    Optarg -- string value of argument to current option
2837   4  | #    Opterr -- if nonzero, print our own diagnostic
2838   5  | #    Optopt -- current option letter
2839   6  | 
2840   7  | # Returns:
2841   8  | #    -1     at end of options
2842   9  | #    ?      for unrecognized option
2843   10  | #    <c>    a character representing the current option
2844   11  | 
2845   12  | # Private Data:
2846   13  | #    _opti  -- index in multi-flag option, e.g., -abc
2847   14  | 
2848      |________________________________________________________________________
2849 Used by 84c 
2851 The function follows. The final two parameters, thisopt and i are local variables and not parameters --- as indicated by the multiple spaces preceding them. Awk doesn't care, the multiple spaces are a convention to help us humans.
2853 83c <getopt.awk-getopt()[1](\v), lang=> ≡  84a⊳
2854       ________________________________________________________________________
2855   1  | function getopt(argc, argv, options,    thisopt, i)
2856   2  | {
2857   3  |     if (length(options) == 0)    # no options given
2858   4  |         return -1
2859   5  |     if (argv[Optind] == "--") {  # all done
2860   6  |         Optind++
2861   7  |         _opti = 0
2862   8  |         return -1
2863   9  |     } else if (argv[Optind] !~ /^-[^: \t\n\f\r\v\b]/) {
2864   10  |         _opti = 0
2865   11  |         return -1
2866   12  |     }
2867   13  |     if (_opti == 0)
2868   14  |         _opti = 2
2869   15  |     thisopt = substr(argv[Optind], _opti, 1)
2870   16  |     Optopt = thisopt
2871   17  |     i = index(options, thisopt)
2872   18  |     if (i == 0) {
2873   19  |         if (Opterr)
2874   20  |             printf("%c -- invalid option\n",
2875   21  |                                   thisopt) > "/dev/stderr"
2876   22  |         if (_opti >= length(argv[Optind])) {
2877   23  |             Optind++
2878   24  |             _opti = 0
2879   25  |         } else
2880   26  |             _opti++
2881   27  |         return "?"
2882   28  |     }
2883      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 37a, 84c, 85a
2884 At this point, the option has been found and we need to know if it takes any arguments.
2886 84a <getopt.awk-getopt()[2](\v) ⇑83c, lang=> +≡ ⊲83c
2887      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
2888   29  |     if (substr(options, i + 1, 1) == ":") {
2889   30  |         # get option argument
2890   31  |         if (length(substr(argv[Optind], _opti + 1)) > 0)
2891   32  |             Optarg = substr(argv[Optind], _opti + 1)
2892   33  |         else
2893   34  |             Optarg = argv[++Optind]
2894   35  |         _opti = 0
2895   36  |     } else
2896   37  |         Optarg = ""
2897   38  |     if (_opti == 0 || _opti >= length(argv[Optind])) {
2898   39  |         Optind++
2899   40  |         _opti = 0
2900   41  |     } else
2901   42  |         _opti++
2902   43  |     return thisopt
2903   44  | }
2904      |________________________________________________________________________
2907 A test program is built in, too
2909 84b <getopt.awk-begin[1](\v), lang=> ≡ 
2910       ________________________________________________________________________
2911   1  | BEGIN {
2912   2  |     Opterr = 1    # default is to diagnose
2913   3  |     Optind = 1    # skip ARGV[0]
2914   4  |     # test program
2915   5  |     if (_getopt_test) {
2916   6  |         while ((_go_c = getopt(ARGC, ARGV, "ab:cd")) != -1)
2917   7  |             printf("c = <%c>, optarg = <%s>\n",
2918   8  |                                        _go_c, Optarg)
2919   9  |         printf("non-option arguments:\n")
2920   10  |         for (; Optind < ARGC; Optind++)
2921   11  |             printf("\tARGV[%d] = <%s>\n",
2922   12  |                                     Optind, ARGV[Optind])
2923   13  |     }
2924   14  | }
2925      |________________________________________________________________________
2926 Used by 84c 
2928 The entire getopt.awk is made out of these chunks in order
2930 84c <getopt.awk[1](\v), lang=> ≡ 
2931       ________________________________________________________________________
2932   1  | «getopt.awk-header 83a»
2933   2  | 
2934   3  | «getopt.awk-notes 83b»
2935   4  | «getopt.awk-getopt() 83c»
2936   5  | «getopt.awk-begin 84b»
2937      |________________________________________________________________________
2938 not used 
2940 Although we only want the header and function:
2942 85a <getopt[1](\v), lang=> ≡ 
2943       ________________________________________________________________________
2944   1  | # try: locate getopt.awk for the full original file
2945   2  | # as part of your standard awk installation
2946   3  | «getopt.awk-header 83a»
2947   4  | 
2948   5  | «getopt.awk-getopt() 83c»
2949      |________________________________________________________________________
2950 not used 
2952 Chapter 17Fangle LaTeX source code
2953 17.1 fangle module 
2954 Here we define a L Y X .module file that makes it convenient to use L Y X for writing such literate programs.
2955 This file ./fangle.module can be installed in your personal .lyx/layouts folder. You will need to Tools Reconfigure so that L Y X notices it. It adds a new format Chunk, which should precede every listing and contain the chunk name.
2957 87a <./fangle.module[1](\v), lang=lyx-module> ≡ 
2958       ________________________________________________________________________
2959   1  | #\DeclareLyXModule{Fangle Literate Listings}
2960   2  | #DescriptionBegin
2961   3  | #  Fangle literate listings allow one to write
2962   4  | #   literate programs after the fashion of noweb, but without having
2963   5  | #   to use noweave to generate the documentation. Instead the listings
2964   6  | #   package is extended in conjunction with the noweb package to implement
2965   7  | #   to code formating directly as latex.
2966   8  | #  The fangle awk script
2967   9  | #DescriptionEnd
2968   10  | 
2969   11  | «gpl3-copyright.hashed 87b»
2970   12  | 
2971   13  | Format 11
2972   14  | 
2973   15  | AddToPreamble
2974   16  | «./fangle.sty 88d»
2975   17  | EndPreamble
2976   18  | 
2977   19  | «chunkstyle 88a»
2978   20  | 
2979   21  | «chunkref 88c»
2980      |________________________________________________________________________
2981 not used 
2983 Because L Y X modules are not yet a language supported by fangle or lstlistings, we resort to this fake awk chunk below in order to have each line of the GPL3 license commence with a #
2985 87b <gpl3-copyright.hashed[1](\v), lang=awk> ≡ 
2986       ________________________________________________________________________
2987   1  | #«gpl3-copyright 4a»
2988   2  | 
2989      |________________________________________________________________________
2990 Used by 87a 
2992 17.1.1 The Chunk style 
2993 The purpose of the chunk style is to make it easier for L Y X users to provide the name to lstlistings. Normally this requires right-clicking on the listing, choosing settings, advanced, and then typing name=chunk-name. This has the further disadvantage that the name (and other options) are not generally visible during document editing.
2994 The chunk style is defined as a LaTeX command, so that all text on the same line is passed to the LaTeX command Chunk. This makes it easy to parse using fangle, and easy to pass these options on to the listings package. The first word in a chunk section should be the chunk name, and will have name= prepended to it. Any other words are accepted arguments to lstset.
2995 We set PassThru to 1 because the user is actually entering raw latex.
2997 88a <chunkstyle[1](\v), lang=> ≡  88b▿
2998       ________________________________________________________________________
2999   1  | Style Chunk
3000   2  |   LatexType             Command
3001   3  |   LatexName             Chunk
3002   4  |   Margin                First_Dynamic
3003   5  |   LeftMargin            Chunk:xxx
3004   6  |   LabelSep              xx
3005   7  |   LabelType             Static
3006   8  |   LabelString           "Chunk:"
3007   9  |   Align                 Left
3008   10  |   PassThru              1
3009   11  | 
3010      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 87a
3011 To make the label very visible we choose a larger font coloured red.
3013 88b <chunkstyle[2](\v) ⇑88a, lang=> +≡ ▵88a
3014      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3015   12  |   LabelFont
3016   13  |     Family              Sans
3017   14  |     Size                Large
3018   15  |     Series              Bold
3019   16  |     Shape               Italic
3020   17  |     Color               red
3021   18  |   EndFont
3022   19  | End
3023      |________________________________________________________________________
3026 17.1.2 The chunkref style 
3027 We also define the Chunkref style which can be used to express cross references to chunks.
3029 88c <chunkref[1](\v), lang=> ≡ 
3030       ________________________________________________________________________
3031   1  | InsetLayout Chunkref
3032   2  |   LyxType               charstyle
3033   3  |   LatexType             Command
3034   4  |   LatexName             chunkref
3035   5  |   PassThru              1
3036   6  |   LabelFont             
3037   7  |     Shape               Italic
3038   8  |     Color               red
3039   9  |   EndFont
3040   10  | End
3041      |________________________________________________________________________
3042 Used by 87a 
3044 17.2 Latex Macros 
3045 We require the listings, noweb and xargs packages. As noweb defines it's own \code environment, we re-define the one that L Y X logical markup module expects here.
3047 88d <./fangle.sty[1](\v), lang=tex> ≡  89a⊳
3048       ________________________________________________________________________
3049   1  | \usepackage{listings}%
3050   2  | \usepackage{noweb}%
3051   3  | \usepackage{xargs}%
3052   4  | \renewcommand{\code}[1]{\texttt{#1}}%
3053      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 87a
3054 We also define a CChunk macro, for use as: \begin{CChunk} which will need renaming to \begin{Chunk} when I can do this without clashing with \Chunk.
3056 89a <./fangle.sty[2](\v) ⇑88d, lang=> +≡ ⊲88d 89b▿
3057      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3058   5  | \lstnewenvironment{Chunk}{\relax}{\relax}%
3059      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3060 We also define a suitable \lstset of parameters that suit the literate programming style after the fashion of noweave.
3062 89b <./fangle.sty[3](\v) ⇑88d, lang=> +≡ ▵89a 89c▿
3063      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3064   6  | \lstset{numbers=left, stepnumber=5, numbersep=5pt,
3065   7  |         breaklines=false,basicstyle=\ttfamily,
3066   8  |         numberstyle=\tiny, language=C}%
3067      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3068 We also define a notangle-like mechanism for escaping to LaTeX from the listing, and by which we can refer to other listings. We declare the =<...> sequence to contain LaTeX code, and include another like this chunk: <chunkname ?>. However, because =<...> is already defined to contain LaTeX code for this document --- this is a fangle document after all --- the code fragment below effectively contains the LaTeX code: }{. To avoid problems with document generation, I had to declare an lstlistings property: escapeinside={} for this listing only; which in L Y X was done by right-clicking the listings inset, choosing settings->advanced. Therefore =< isn't interpreted literally here, in a listing when the escape sequence is already defined as shown... we need to somehow escape this representation...
3070 89c <./fangle.sty[4](\v) ⇑88d, lang=> +≡ ▵89b 89d▿
3071      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3072   9  | \lstset{escapeinside={=<}{>}}%
3073      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3074 Although our macros will contain the @ symbol, they will be included in a \makeatletter section by L Y X; however we keep the commented out \makeatletter as a reminder. The listings package likes to centre the titles, but noweb titles are specially formatted and must be left aligned. The simplest way to do this turned out to be by removing the definition of \lst@maketitle. This may interact badly if other listings want a regular title or caption. We remember the old maketitle in case we need it.
3076 89d <./fangle.sty[5](\v) ⇑88d, lang=> +≡ ▵89c 89e▿
3077      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3078   10  | %\makeatletter
3079   11  | %somehow re-defining maketitle gives us a left-aligned title
3080   12  | %which is extactly what our specially formatted title needs!
3081   13  | \global\let\fangle@lst@maketitle\lst@maketitle%
3082   14  | \global\def\lst@maketitle{}%
3083      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3084 17.2.1 The chunk command 
3085 Our chunk command accepts one argument, and calls \ltset. Although \ltset will note the name, this is erased when the next \lstlisting starts, so we make a note of this in \lst@chunkname and restore in in lstlistings Init hook.
3087 89e <./fangle.sty[6](\v) ⇑88d, lang=> +≡ ▵89d 90a⊳
3088      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3089   15  | \def\Chunk#1{%
3090   16  |   \lstset{title={\fanglecaption},name=#1}%
3091   17  |   \global\edef\lst@chunkname{\lst@intname}%
3092   18  | }%
3093   19  | \def\lst@chunkname{\empty}%
3094      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3095 17.2.1.1 Chunk parameters 
3096 Fangle permits parameterized chunks, and requires the paramters to be specified as listings options. The fangle script uses this, and although we don't do anything with these in the LaTeX code right now, we need to stop the listings package complaining.
3098 90a <./fangle.sty[7](\v) ⇑88d, lang=> +≡ ⊲89e 90b▿
3099      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3100   20  | \lst@Key{params}\relax{\def\fangle@chunk@params{#1}}%
3101      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3102 As it is common to define a chunk which then needs appending to another chunk, and annoying to have to declare a single line chunk to manage the include, we support an append= option.
3104 90b <./fangle.sty[8](\v) ⇑88d, lang=> +≡ ▵90a 90c▿
3105      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3106   21  | \lst@Key{append}\relax{\def\fangle@chunk@append{#1}}%
3107      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3108 17.2.2 The noweb styled caption 
3109 We define a public macro \fanglecaption which can be set as a regular title. By means of \protect, It expands to \fangle@caption at the appopriate time when the caption is emitted.
3111 90c <./fangle.sty[9](\v) ⇑88d, lang=> +≡ ▵90b 90d▿
3112      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3113 \def\fanglecaption{\protect\fangle@caption}%
3114      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3115 22c ⟨some-chunk 19b⟩≡+   ⊲22b 24d⊳
3117 In this example, the current chunk is 22c, and therefore the third chunk on page 22.
3118 It's name is some-chunk. 
3119 The first chunk with this name (19b) occurs as the second chunk on page 19.
3120 The previous chunk (22d) with the same name is the second chunk on page 22.
3121 The next chunk (24d) is the fourth chunk on page 24.
3123 Figure 1. Noweb Heading
3125 The general noweb output format compactly identifies the current chunk, and references to the first chunk, and the previous and next chunks that have the same name.
3126 This means that we need to keep a counter for each chunk-name, that we use to count chunks of the same name.
3127 17.2.3 The chunk counter 
3128 It would be natural to have a counter for each chunk name, but TeX would soon run out of counters1. ...soon did run out of counters and so I had to re-write the LaTeX macros to share a counter as described here.  ^1, so we have one counter which we save at the end of a chunk and restore at the beginning of a chunk.
3130 90d <./fangle.sty[10](\v) ⇑88d, lang=> +≡ ▵90c 91c⊳
3131      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3132   22  | \newcounter{fangle@chunkcounter}%
3133      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3134 We construct the name of this variable to store the counter to be the text lst-chunk- prefixed onto the chunks own name, and store it in \chunkcount. 
3135 We save the counter like this:
3137 91a <save-counter[1](\v), lang=> ≡ 
3138       ________________________________________________________________________
3139 \global\expandafter\edef\csname \chunkcount\endcsname{\arabic{fangle@chunkcounter}}%
3140      |________________________________________________________________________
3141 not used 
3143 and restore the counter like this:
3145 91b <restore-counter[1](\v), lang=> ≡ 
3146       ________________________________________________________________________
3147 \setcounter{fangle@chunkcounter}{\csname \chunkcount\endcsname}%
3148      |________________________________________________________________________
3149 not used 
3151 If there does not already exist a variable whose name is stored in \chunkcount, then we know we are the first chunk with this name, and then define a counter. 
3152 Although chunks of the same name share a common counter, they must still be distinguished. We use is the internal name of the listing, suffixed by the counter value. So the first chunk might be something-1 and the second chunk be something-2, etc.
3153 We also calculate the name of the previous chunk if we can (before we increment the chunk counter). If this is the first chunk of that name, then \prevchunkname is set to \relax which the noweb package will interpret as not existing.
3155 91c <./fangle.sty[11](\v) ⇑88d, lang=> +≡ ⊲90d 91d▿
3156      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3157   23  | \def\fangle@caption{%
3158   24  |   \edef\chunkcount{lst-chunk-\lst@intname}%
3159   25  |   \@ifundefined{\chunkcount}{%
3160   26  |     \expandafter\gdef\csname \chunkcount\endcsname{0}%
3161   27  |     \setcounter{fangle@chunkcounter}{\csname \chunkcount\endcsname}%
3162   28  |     \let\prevchunkname\relax%
3163   29  |   }{%
3164   30  |     \setcounter{fangle@chunkcounter}{\csname \chunkcount\endcsname}%
3165   31  |     \edef\prevchunkname{\lst@intname-\arabic{fangle@chunkcounter}}%
3166   32  |   }%
3167      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3168 After incrementing the chunk counter, we then define the name of this chunk, as well as the name of the first chunk.
3170 91d <./fangle.sty[12](\v) ⇑88d, lang=> +≡ ▵91c 91e▿
3171      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3172   33  |   \addtocounter{fangle@chunkcounter}{1}%
3173   34  |   \global\expandafter\edef\csname \chunkcount\endcsname{\arabic{fangle@chunkcounter}}%
3174   35  |   \edef\chunkname{\lst@intname-\arabic{fangle@chunkcounter}}%
3175   36  |   \edef\firstchunkname{\lst@intname-1}%
3176      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3177 We now need to calculate the name of the next chunk. We do this by temporarily skipping the counter on by one; however there may not actually be another chunk with this name! We detect this by also defining a label for each chunk based on the chunkname. If there is a next chunkname then it will define a label with that name. As labels are persistent, we can at least tell the second time LaTeX is run. If we don't find such a defined label then we define \nextchunkname to \relax.
3179 91e <./fangle.sty[13](\v) ⇑88d, lang=> +≡ ▵91d 92a⊳
3180      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3181   37  |   \addtocounter{fangle@chunkcounter}{1}%
3182   38  |   \edef\nextchunkname{\lst@intname-\arabic{fangle@chunkcounter}}%
3183   39  |   \@ifundefined{r@label-\nextchunkname}{\let\nextchunkname\relax}{}%
3184      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3185 The noweb package requires that we define a \sublabel for every chunk, with a unique name, which is then used to print out it's navigation hints.
3186 We also define a regular label for this chunk, as was mentioned above when we calculated \nextchunkname. This requires LaTeX to be run at least twice after new chunk sections are added --- but noweb requried that anyway.
3188 92a <./fangle.sty[14](\v) ⇑88d, lang=> +≡ ⊲91e 92b▿
3189      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3190   40  |   \sublabel{\chunkname}%
3191   41  | % define this label for every chunk instance, so we
3192   42  | % can tell when we are the last chunk of this name
3193   43  |   \label{label-\chunkname}%
3194      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3195 We also try and add the chunk to the list of listings, but I'm afraid we don't do very well. We want each chunk name listing once, with all of it's references.
3197 92b <./fangle.sty[15](\v) ⇑88d, lang=> +≡ ▵92a 92c▿
3198      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3199   44  |   \addcontentsline{lol}{lstlisting}{\lst@name~[\protect\subpageref{\chunkname}]}%
3200      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3201 We then call the noweb output macros in the same way that noweave generates them, except that we don't need to call \nwstartdeflinemarkup or \nwenddeflinemarkup — and if we do, it messes up the output somewhat.
3203 92c <./fangle.sty[16](\v) ⇑88d, lang=> +≡ ▵92b 92d▿
3204      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3205   45  |   \nwmargintag{%
3206   46  |     {%
3207   47  |       \nwtagstyle{}%
3208   48  |       \subpageref{\chunkname}%
3209   49  |     }%
3210   50  |   }%
3211   51  | %
3212   52  |   \moddef{%
3213   53  |     {\lst@name}%
3214   54  |     {%
3215   55  |       \nwtagstyle{}\/%
3216   56  |       \@ifundefined{fangle@chunk@params}{}{%
3217   57  |         (\fangle@chunk@params)%
3218   58  |       }%
3219   59  |       [\csname \chunkcount\endcsname]~%
3220   60  |       \subpageref{\firstchunkname}%
3221   61  |     }%
3222   62  |     \@ifundefined{fangle@chunk@append}{}{%
3223   63  |     \ifx{}\fangle@chunk@append{x}\else%
3224   64  |         ,~add~to~\fangle@chunk@append%
3225   65  |     \fi%
3226   66  |     }%
3227   67  | \global\def\fangle@chunk@append{}%
3228   68  | \lstset{append=x}%
3229   69  |   }%
3230   70  | %
3231   71  |   \ifx\relax\prevchunkname\endmoddef\else\plusendmoddef\fi%
3232   72  | %  \nwstartdeflinemarkup%
3233   73  |   \nwprevnextdefs{\prevchunkname}{\nextchunkname}%
3234   74  | %  \nwenddeflinemarkup%
3235   75  | }%
3236      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3237 Originally this was developed as a listings aspect, in the Init hook, but it was found easier to affect the title without using a hook — \lst@AddToHookExe{PreSet} is still required to set the listings name to the name passed to the \Chunk command, though.
3239 92d <./fangle.sty[17](\v) ⇑88d, lang=> +≡ ▵92c 93a⊳
3240      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3241   76  | %\lst@BeginAspect{fangle}
3242   77  | %\lst@Key{fangle}{true}[t]{\lstKV@SetIf{#1}{true}}
3243   78  | \lst@AddToHookExe{PreSet}{\global\let\lst@intname\lst@chunkname}
3244   79  | \lst@AddToHook{Init}{}%\fangle@caption}
3245   80  | %\lst@EndAspect
3246      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3247 17.2.4 Cross references 
3248 We define the \chunkref command which makes it easy to generate visual references to different code chunks, e.g.
3249 Macro                           Appearance
3250 \chunkref{preamble}             
3251 \chunkref[3]{preamble}          
3252 \chunkref{preamble}[arg1, arg2] 
3254 Chunkref can also be used within a code chunk to include another code chunk. The third optional parameter to chunkref is a comma sepatarated list of arguments, which will replace defined parameters in the chunkref.
3255 Note 1. Darn it, if I have: =<\chunkref{new-mode-tracker}[{chunks[chunk_name, "language"]},{mode}]> the inner braces (inside [ ]) cause _ to signify subscript even though we have lst@ReplaceIn 
3257 93a <./fangle.sty[18](\v) ⇑88d, lang=> +≡ ⊲92d 94a⊳
3258      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3259   81  | \def\chunkref@args#1,{%
3260   82  |   \def\arg{#1}%
3261   83  |   \lst@ReplaceIn\arg\lst@filenamerpl%
3262   84  |   \arg%
3263   85  |   \@ifnextchar){\relax}{, \chunkref@args}%
3264   86  | }%
3265   87  | \newcommand\chunkref[2][0]{%
3266   88  |   \@ifnextchar({\chunkref@i{#1}{#2}}{\chunkref@i{#1}{#2}()}%
3267   89  | }%
3268   90  | \def\chunkref@i#1#2(#3){%
3269   91  |   \def\zero{0}%
3270   92  |   \def\chunk{#2}%
3271   93  |   \def\chunkno{#1}%
3272   94  |   \def\chunkargs{#3}%
3273   95  |   \ifx\chunkno\zero%
3274   96  |     \def\chunkname{#2-1}%
3275   97  |   \else%
3276   98  |     \def\chunkname{#2-\chunkno}%
3277   99  |   \fi%
3278   100  |   \let\lst@arg\chunk%
3279   101  |   \lst@ReplaceIn\chunk\lst@filenamerpl%
3280   102  |   \LA{%\moddef{%
3281   103  |     {\chunk}%
3282   104  |     {%
3283   105  |       \nwtagstyle{}\/%
3284   106  |       \ifx\chunkno\zero%
3285   107  |       \else%
3286   108  |       [\chunkno]%
3287   109  |       \fi%
3288   110  |       \ifx\chunkargs\empty%
3289   111  |       \else%
3290   112  |         (\chunkref@args #3,)%
3291   113  |       \fi%
3292   114  |       ~\subpageref{\chunkname}%
3293   115  |     }%
3294   116  |   }%
3295   117  |   \RA%\endmoddef%
3296   118  | }%
3297      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3298 17.2.5 The end 
3300 94a <./fangle.sty[19](\v) ⇑88d, lang=> +≡ ⊲93a
3301      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3302   119  | %
3303   120  | %\makeatother
3304      |________________________________________________________________________
3307 Chapter 18Extracting fangle
3308 18.1 Extracting from Lyx 
3309 To extract from L Y X, you will need to configure L Y X as explained in section ?.
3310 And this lyx-build scrap will extract fangle for me.
3312 95a <lyx-build[2](\v) ⇑20a, lang=sh> +≡ ⊲20a
3313      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3314   11  | #! /bin/sh
3315   12  | set -x
3316   13  | 
3317   14  | «lyx-build-helper 19b»
3318   15  | cd $PROJECT_DIR || exit 1
3319   16  | 
3320   17  | /usr/local/bin/fangle -R./fangle $TEX_SRC > ./fangle
3321   18  | /usr/local/bin/fangle -R./fangle.module $TEX_SRC > ./fangle.module
3322   19  | 
3323   20  | export FANGLE=./fangle
3324   21  | export TMP=${TMP:-/tmp}
3325   22  | «test:* 99a»
3326      |________________________________________________________________________
3329 With a lyx-build-helper
3331 95b <lyx-build-helper[2](\v) ⇑19b, lang=sh> +≡ ⊲19b
3332      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3333   5  | PROJECT_DIR="$LYX_r"
3334   6  | LYX_SRC="$PROJECT_DIR/${LYX_i%.tex}.lyx"
3335   7  | TEX_DIR="$LYX_p"
3336   8  | TEX_SRC="$TEX_DIR/$LYX_i"
3337   9  | TXT_SRC="$TEX_SRC"
3338      |________________________________________________________________________
3341 18.2 Extracting documentation 
3343 95c <./gen-www[1](\v), lang=> ≡ 
3344       ________________________________________________________________________
3345   1  | #python -m elyxer --css lyx.css $LYX_SRC | \
3346   2  | #  iconv -c -f utf-8 -t ISO-8859-1//TRANSLIT | \
3347   3  | #  sed 's/UTF-8"\(.\)>/ISO-8859-1"\1>/' > www/docs/fangle.html
3348   4  | 
3349   5  | python -m elyxer --css lyx.css --iso885915 --html --destdirectory www/docs/fangle.e \
3350   6  |        fangle.lyx > www/docs/fangle.e/fangle.html
3351   7  | 
3352   8  | ( mkdir -p www/docs/fangle && cd www/docs/fangle && \
3353   9  |   lyx -e latex ../../../fangle.lyx && \
3354   10  |   htlatex ../../../fangle.tex "xhtml,fn-in" && \
3355   11  |   sed -i -e 's/<!--l\. [0-9][0-9]* *-->//g' fangle.html
3356   12  | )
3357   13  | 
3358   14  | ( mkdir -p www/docs/literate && cd www/docs/literate && \
3359   15  |   lyx -e latex ../../../literate.lyx && \
3360   16  |   htlatex ../../../literate.tex "xhtml,fn-in" && \
3361   17  |   sed -i -e 's/<!--l\. [0-9][0-9]* *-->$//g' literate.html
3362   18  | )
3363      |________________________________________________________________________
3364 not used 
3366 18.3 Extracting from the command line 
3367 First you will need the tex output, then you can extract:
3369 96a <lyx-build-manual[1](\v), lang=sh> ≡ 
3370       ________________________________________________________________________
3371   1  | lyx -e latex fangle.lyx
3372   2  | fangle -R./fangle fangle.tex > ./fangle
3373   3  | fangle -R./fangle.module fangle.tex > ./fangle.module
3374      |________________________________________________________________________
3375 not used 
3378 Part III  Tests 
3379 Chapter 19Tests
3381 99a <test:*[1](\v), lang=> ≡ 
3382       ________________________________________________________________________
3383   1  | #! /bin/bash
3384   2  | 
3385   3  | export SRC="${SRC:-./fangle.tm}"
3386   4  | export FANGLE="${FANGLE:-./fangle}"
3387   5  | export TMP="${TMP:-/tmp}"
3388   6  | export TESTDIR="$TMP/$USER/fangle.tests"
3389   7  | export TXT_SRC="${TXT_SRC:-$TESTDIR/fangle.txt}"
3390   8  | export AWK="${AWK:-awk}"
3391   9  | export RUN_FANGLE="${RUN_FANGLE:-$AWK -f}"
3392   10  | 
3393   11  | fangle() {
3394   12  |   ${AWK} -f ${FANGLE} "$@"
3395   13  | }
3396   14  | 
3397   15  | mkdir -p "$TESTDIR"
3398   16  | 
3399   17  | tm -s -c "$SRC" "$TXT_SRC" -q
3400   18  | 
3401   19  | «test:helpers 100a»
3402   20  | run_tests() {
3403   21  |   «test:run-tests 99b»
3404   22  | }
3405   23  | 
3406   24  | # test current fangle
3407   25  | echo Testing current fangle
3408   26  | run_tests
3409   27  | 
3410   28  | # extract new fangle
3411   29  | echo testing new fangle
3412   30  | fangle -R./fangle "$TXT_SRC" > "$TESTDIR/fangle"
3413   31  | export FANGLE="$TESTDIR/fangle"
3414   32  | run_tests
3415   33  | 
3416   34  | # Now check that it can extract a fangle that also passes the tests!
3417   35  | echo testing if new fangle can generate itself
3418   36  | fangle -R./fangle "$TXT_SRC" > "$TESTDIR/fangle.new"
3419   37  | passtest diff -bwu "$FANGLE" "$TESTDIR/fangle.new"
3420   38  | export FANGLE="$TESTDIR/fangle.new"
3421   39  | run_tests
3422      |________________________________________________________________________
3423 Used by 20a 
3426 99b <test:run-tests[1](\v), lang=sh> ≡ 
3427       ________________________________________________________________________
3428   1  | # run tests
3429   2  | fangle -Rpca-test.awk $TXT_SRC | awk -f - || exit 1
3430   3  | «test:cromulence 59g»
3431   4  | «test:escapes 64a»
3432   5  | «test:test-chunk\v(test:example-sh\v) 100b»
3433   6  | «test:test-chunk\v(test:example-makefile\v) 100b»
3434   7  | «test:test-chunk\v(test:q:1\v) 100b»
3435   8  | «test:test-chunk\v(test:make:1\v) 100b»
3436   9  | «test:test-chunk\v(test:make:2\v) 100b»
3437   10  | «test:chunk-params 101e»
3438      |________________________________________________________________________
3439 Used by 99a 
3442 100a <test:helpers[1](\v), lang=> ≡ 
3443       ________________________________________________________________________
3444   1  | passtest() {
3445   2  |   if "$@"
3446   3  |   then echo "Passed $TEST"
3447   4  |   else echo "Failed $TEST"
3448   5  |        return 1
3449   6  |   fi
3450   7  | }
3451   8  | 
3452   9  | failtest() {
3453   10  |   if ! "$@"
3454   11  |   then echo "Passed $TEST"
3455   12  |   else echo "Failed $TEST"
3456   13  |        return 1
3457   14  |   fi
3458   15  | }
3459      |________________________________________________________________________
3460 Used by 99a 
3462 This chunk will render a named chunk and compare it to another rendered nameed chunk
3464 100b <test:test-chunk[1](chunk\v\v), lang=sh> ≡ 
3465       ________________________________________________________________________
3466   1  | «test:test-chunk-result\v(${chunk}\v, ${chunk}.result\v) 100c»
3467      |________________________________________________________________________
3468 Used by 99b 
3471 100c <test:test-chunk-result[1](chunk\v, result\v\v), lang=sh> ≡ 
3472       ________________________________________________________________________
3473   1  | TEST="${result}" passtest diff -u --label "EXPECTED: ${result}" <( fangle -R${result} $TXT_SRC ) \
3474   2  |                                  --label "ACTUAL: ${chunk}" <( fangle -R${chunk} $TXT_SRC )
3475      |________________________________________________________________________
3476 Used by 100b, 101e 
3478 Chapter 20Chunk Parameters
3479 20.1 L Y X 
3481 101a <test:lyx:chunk-params:sub[1](THING\v, colour\v\v), lang=> ≡ 
3482       ________________________________________________________________________
3483   1  | I see a ${THING},
3484   2  | a ${THING} of colour ${colour}, 
3485   3  | and looking closer =<\chunkref{test:lyx:chunk-params:sub:sub}(${colour})>
3486      |________________________________________________________________________
3487 not used 
3490 101b <test:lyx:chunk-params:sub:sub[1](colour\v\v), lang=> ≡ 
3491       ________________________________________________________________________
3492   1  | a funny shade of ${colour}
3493      |________________________________________________________________________
3494 not used 
3497 101c <test:lyx:chunk-params:text[1](\v), lang=> ≡ 
3498       ________________________________________________________________________
3499   1  | What do you see? "=<\chunkref{test:lyx:chunk-params:sub}(joe, red)>"
3500   2  | Well, fancy!
3501      |________________________________________________________________________
3502 not used 
3504 Should generate output:
3506 101d <test:lyx:chunk-params:result[1](\v), lang=> ≡ 
3507       ________________________________________________________________________
3508   1  | What do you see? "I see a joe,
3509   2  |                   a joe of colour red, 
3510   3  |                   and looking closer a funny shade of red"
3511   4  | Well, fancy!
3512      |________________________________________________________________________
3513 not used 
3515 And this chunk will perform the test:
3517 101e <test:chunk-params[1](\v), lang=> ≡  102b⊳
3518       ________________________________________________________________________
3519   1  | «test:test-chunk-result\v(test:lyx:chunk-params:text\v, test:lyx:chunk-params:result\v) 100c» || exit 1
3520      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\Used by 99b
3521 20.2 TeXmacs 
3523 101f <test:chunk-params:sub[1](THING\v, colour\v\v), lang=> ≡ 
3524       ________________________________________________________________________
3525   1  | I see a ${THING},
3526   2  | a ${THING} of colour ${colour}, 
3527   3  | and looking closer «test:chunk-params:sub:sub\v(${colour}\v) 101g»
3528      |________________________________________________________________________
3529 Used by 101h 
3532 101g <test:chunk-params:sub:sub[1](colour\v\v), lang=> ≡ 
3533       ________________________________________________________________________
3534   1  | a funny shade of ${colour}
3535      |________________________________________________________________________
3536 Used by 101f 
3539 101h <test:chunk-params:text[1](\v), lang=> ≡ 
3540       ________________________________________________________________________
3541   1  | What do you see? "«test:chunk-params:sub\v(joe\v, red\v) 101f»"
3542   2  | Well, fancy!
3543      |________________________________________________________________________
3544 not used 
3546 Should generate output:
3548 102a <test:chunk-params:result[1](\v), lang=> ≡ 
3549       ________________________________________________________________________
3550   1  | What do you see? "I see a joe,
3551   2  |                   a joe of colour red, 
3552   3  |                   and looking closer a funny shade of red"
3553   4  | Well, fancy!
3554      |________________________________________________________________________
3555 not used 
3557 And this chunk will perform the test:
3559 102b <test:chunk-params[2](\v) ⇑101e, lang=> +≡ ⊲101e
3560      ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
3561   2  | «test:test-chunk-result\v(test:chunk-params:text\v, test:chunk-params:result\v) 100c» || exit 1
3562      |________________________________________________________________________
3565 Chapter 21Compile-log-lyx
3567 103a <Chunk:./compile-log-lyx[1](\v), lang=sh> ≡ 
3568       ________________________________________________________________________
3569   1  | #! /bin/sh
3570   2  | # can't use gtkdialog -i, cos it uses the "source" command which ubuntu sh doesn't have
3571   3  | 
3572   4  | main() {
3573   5  |   errors="/tmp/compile.log.$$"
3574   6  | #  if grep '^[^ ]*:\( In \|[0-9][0-9]*: [^ ]*:\)' > $errors
3575   7  | if grep '^[^ ]*(\([0-9][0-9]*\)) *: *\(error\|warning\)' > $errors
3576   8  |   then
3577   9  |     sed -i -e 's/^[^ ]*[/\\]\([^/\\]*\)(\([ 0-9][ 0-9]*\)) *: */\1:\2|\2|/' $errors
3578   10  |     COMPILE_DIALOG='
3579   11  |  <vbox>
3580   12  |   <text>
3581   13  |     <label>Compiler errors:</label>
3582   14  |   </text>
3583   15  |   <tree exported_column="0">
3584   16  |     <variable>LINE</variable>
3585   17  |     <height>400</height><width>800</width>
3586   18  |     <label>File | Line | Message</label>
3587   19  |     <action>'". $SELF ; "'lyxgoto $LINE</action>
3588   20  |     <input>'"cat $errors"'</input>
3589   21  |   </tree>
3590   22  |   <hbox>
3591   23  |    <button><label>Build</label>
3592   24  |      <action>lyxclient -c "LYXCMD:build-program" &</action>
3593   25  |    </button>
3594   26  |    <button ok></button>
3595   27  |   </hbox>
3596   28  |  </vbox>
3597   29  | '
3598   30  |     export COMPILE_DIALOG
3599   31  |     ( gtkdialog --program=COMPILE_DIALOG ; rm $errors ) &
3600   32  |   else
3601   33  |     rm $errors
3602   34  |   fi
3603   35  | }
3604   36  | 
3605   37  | lyxgoto() {
3606   38  |   file="${LINE%:*}"
3607   39  |   line="${LINE##*:}"
3608   40  |   extraline=‘cat $file | head -n $line | tac | sed '/^\\\\begin{lstlisting}/q' | wc -l‘
3609   41  |   extraline=‘expr $extraline - 1‘
3610   42  |   lyxclient -c "LYXCMD:command-sequence server-goto-file-row $file $line ; char-forward ; repeat $extraline paragraph-down ; paragraph-up-select"
3611   43  | }
3612   44  | 
3613   45  | SELF="$0"
3614   46  | if test -z "$COMPILE_DIALOG"
3615   47  | then main "$@" 
3616   48  | fi
3617      |________________________________________________________________________
3618 not used