3 <style|<tuple|book|fangle|header-book|tmdoc-keyboard>>
6 <hide-preamble|<assign|LyX|<macro|L<space|-0.1667em><move|Y|0fn|-0.25em><space|-0.125em>X>><assign|par-first|0fn><assign|par-par-sep|0.5fn>>
8 <doc-data|<doc-title|fangle>|<doc-author-data|<author-name|Sam
9 Liddicott>|<\author-address>
11 </author-address>>|<doc-date|August 2009>>
13 <section*|Introduction>
15 <name|Fangle> is a tool for fangled literate programming. Newfangled is
16 defined as <em|New and often needlessly novel> by
17 <name|TheFreeDictionary.com>.
19 In this case, fangled means yet another not-so-new<footnote|but improved.>
20 method for literate programming.
22 <name|Literate Programming> has a long history starting with the great
23 <name|Donald Knuth> himself, whose literate programming tools seem to make
24 use of as many escape sequences for semantic markup as <TeX> (also by
27 <name|Norman Ramsey> wrote the <name|Noweb> set of tools
28 (<verbatim|notangle>, <verbatim|noweave> and <verbatim|noroots>) and
29 helpfully reduced the amount of magic character sequences to pretty much
30 just <verbatim|\<less\>\<less\>>, <verbatim|\<gtr\>\<gtr\>> and
31 <verbatim|@>, and in doing so brought the wonders of literate programming
34 While using the <LyX> editor for <LaTeX> editing I had various troubles
35 with the noweb tools, some of which were my fault, some of which were
36 noweb's fault and some of which were <LyX>'s fault.
38 <name|Noweb> generally brought literate programming to the masses through
39 removing some of the complexity of the original literate programming, but
40 this would be of no advantage to me if the <LyX> / <LaTeX> combination
41 brought more complications in their place.
43 <name|Fangle> was thus born (originally called <name|Newfangle>) as an awk
44 replacement for notangle, adding some important features, like better
45 integration with <LyX> and <LaTeX> (and later <TeXmacs>), multiple output
46 format conversions, and fixing notangle bugs like indentation when using -L
49 Significantly, fangle is just one program which replaces various programs
50 in <name|Noweb>. Noweave is done away with and implemented directly as
51 <LaTeX> macros, and noroots is implemented as a function of the untangler
54 Fangle is written in awk for portability reasons, awk being available for
55 most platforms. A Python version<\footnote>
56 hasn't anyone implemented awk in python yet?
57 </footnote> was considered for the benefit of <LyX> but a scheme version
58 for <TeXmacs> will probably materialise first; as <TeXmacs> macro
59 capabilities help make edit-time and format-time rendering of fangle chunks
60 simple enough for my weak brain.
62 As an extension to many literate-programming styles, Fangle permits code
63 chunks to take parameters and thus operate somewhat like C pre-processor
64 macros, or like C++ templates. Name parameters (or even local
65 <em|variables> in the callers scope) are anticipated, as parameterized
66 chunks <emdash> useful though they are <emdash> are hard to comprehend in
67 the literate document.
69 <section*|License><new-page*><label|License>
71 Fangle is licensed under the GPL 3 (or later).
73 This doesn't mean that sources generated by fangle must be licensed under
76 This doesn't mean that you can't use or distribute fangle with sources of
77 an incompatible license, but it means you must make the source of fangle
80 As fangle is currently written in awk, an interpreted language, this should
83 <\nf-chunk|gpl3-copyright>
84 <item># fangle - fully featured notangle replacement in awk
88 <item># Copyright (C) 2009-2010 Sam Liddicott
89 \<less\>sam@liddicott.com\<gtr\>
93 <item># This program is free software: you can redistribute it and/or
96 <item># it under the terms of the GNU General Public License as published
99 <item># the Free Software Foundation, either version 3 of the License, or
101 <item># (at your option) any later version.
105 <item># This program is distributed in the hope that it will be useful,
107 <item># but WITHOUT ANY WARRANTY; without even the implied warranty of
109 <item># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \ See the
111 <item># GNU General Public License for more details.
115 <item># You should have received a copy of the GNU General Public License
117 <item># along with this program. \ If not, see
118 \<less\>http://www.gnu.org/licenses/\<gtr\>.
121 <\table-of-contents|toc>
122 Introduction <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
123 <no-break><pageref|auto-1>
125 License <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
126 <no-break><pageref|auto-2>
128 <vspace*|2fn><with|font-series|bold|math-font-series|bold|font-size|1.19|I<space|2spc>Using
129 Fangle> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
130 <no-break><pageref|auto-3><vspace|1fn>
132 <vspace*|1fn><with|font-series|bold|math-font-series|bold|1<space|2spc>Introduction
133 to Literate Programming> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
134 <no-break><pageref|auto-4><vspace|0.5fn>
136 <vspace*|1fn><with|font-series|bold|math-font-series|bold|2<space|2spc>Running
137 Fangle> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
138 <no-break><pageref|auto-5><vspace|0.5fn>
140 2.1<space|2spc>Listing roots <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
141 <no-break><pageref|auto-6>
143 2.2<space|2spc>Extracting roots <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
144 <no-break><pageref|auto-7>
146 2.3<space|2spc>Formatting the document
147 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
148 <no-break><pageref|auto-8>
150 <vspace*|1fn><with|font-series|bold|math-font-series|bold|3<space|2spc>Using
151 Fangle with L<rsup|<space|-0.8spc><move|A|0fn|-0.1fn>><space|-0.2spc>T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X>
152 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
153 <no-break><pageref|auto-9><vspace|0.5fn>
155 <vspace*|1fn><with|font-series|bold|math-font-series|bold|4<space|2spc>Using
156 Fangle with L<space|-0.1667em><move|Y|0fn|-0.25em><space|-0.125em>X>
157 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
158 <no-break><pageref|auto-10><vspace|0.5fn>
160 4.1<space|2spc>Installing the L<space|-0.1667em><move|Y|0fn|-0.25em><space|-0.125em>X
161 module <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
162 <no-break><pageref|auto-11>
164 4.2<space|2spc>Obtaining a decent mono font
165 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
166 <no-break><pageref|auto-12>
168 <with|par-left|1.5fn|4.2.1<space|2spc>txfonts
169 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
170 <no-break><pageref|auto-13>>
172 <with|par-left|1.5fn|4.2.2<space|2spc>ams pmb
173 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
174 <no-break><pageref|auto-14>>
176 <with|par-left|1.5fn|4.2.3<space|2spc>Luximono
177 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
178 <no-break><pageref|auto-15>>
180 4.3<space|2spc>Formatting your Lyx document
181 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
182 <no-break><pageref|auto-16>
184 <with|par-left|1.5fn|4.3.1<space|2spc>Customising the listing appearance
185 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
186 <no-break><pageref|auto-17>>
188 <with|par-left|1.5fn|4.3.2<space|2spc>Global customisations
189 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
190 <no-break><pageref|auto-18>>
192 4.4<space|2spc>Configuring the build script
193 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
194 <no-break><pageref|auto-19>
196 <with|par-left|1.5fn|4.4.1<space|2spc>...
197 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
198 <no-break><pageref|auto-20>>
200 <vspace*|1fn><with|font-series|bold|math-font-series|bold|5<space|2spc>Using
201 Fangle with T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X<rsub|<space|-0.4spc><move|<resize|M<space|-0.2spc>A<space|-0.4spc>CS||||0.5fn>|0fn|-0.1fn>>>
202 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
203 <no-break><pageref|auto-21><vspace|0.5fn>
205 <vspace*|1fn><with|font-series|bold|math-font-series|bold|6<space|2spc>Fangle
206 with Makefiles> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
207 <no-break><pageref|auto-22><vspace|0.5fn>
209 6.1<space|2spc>A word about makefiles formats
210 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
211 <no-break><pageref|auto-23>
213 6.2<space|2spc>Extracting Sources <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
214 <no-break><pageref|auto-24>
216 <with|par-left|1.5fn|6.2.1<space|2spc>Converting from
217 L<space|-0.1667em><move|Y|0fn|-0.25em><space|-0.125em>X to
218 L<rsup|<space|-0.8spc><move|A|0fn|-0.1fn>><space|-0.2spc>T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X
219 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
220 <no-break><pageref|auto-25>>
222 <with|par-left|1.5fn|6.2.2<space|2spc>Converting from
223 T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X<rsub|<space|-0.4spc><move|<resize|M<space|-0.2spc>A<space|-0.4spc>CS||||0.5fn>|0fn|-0.1fn>>
224 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
225 <no-break><pageref|auto-26>>
227 6.3<space|2spc>Extracting Program Source
228 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
229 <no-break><pageref|auto-27>
231 6.4<space|2spc>Extracting Source Files
232 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
233 <no-break><pageref|auto-28>
235 6.5<space|2spc>Extracting Documentation
236 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
237 <no-break><pageref|auto-29>
239 <with|par-left|1.5fn|6.5.1<space|2spc>Formatting
240 T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X
241 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
242 <no-break><pageref|auto-30>>
244 <with|par-left|3fn|6.5.1.1<space|2spc>Running pdflatex
245 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
246 <no-break><pageref|auto-31>>
248 <with|par-left|1.5fn|6.5.2<space|2spc>Formatting
249 T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X<rsub|<space|-0.4spc><move|<resize|M<space|-0.2spc>A<space|-0.4spc>CS||||0.5fn>|0fn|-0.1fn>>
250 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
251 <no-break><pageref|auto-32>>
253 <with|par-left|1.5fn|6.5.3<space|2spc>Building the Documentation as a
254 Whole <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
255 <no-break><pageref|auto-33>>
257 6.6<space|2spc>Other helpers <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
258 <no-break><pageref|auto-34>
260 6.7<space|2spc>Boot-strapping the extraction
261 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
262 <no-break><pageref|auto-35>
264 6.8<space|2spc>Incorporating Makefile.inc into existing projects
265 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
266 <no-break><pageref|auto-36>
268 <with|par-left|6fn|Example <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
269 <no-break><pageref|auto-37><vspace|0.15fn>>
271 <vspace*|2fn><with|font-series|bold|math-font-series|bold|font-size|1.19|II<space|2spc>Source
272 Code> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
273 <no-break><pageref|auto-38><vspace|1fn>
275 <vspace*|1fn><with|font-series|bold|math-font-series|bold|7<space|2spc>Fangle
276 awk source code> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
277 <no-break><pageref|auto-39><vspace|0.5fn>
279 7.1<space|2spc>AWK tricks <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
280 <no-break><pageref|auto-40>
282 7.2<space|2spc>Catching errors <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
283 <no-break><pageref|auto-41>
285 <vspace*|1fn><with|font-series|bold|math-font-series|bold|8<space|2spc>L<rsup|<space|-0.8spc><move|A|0fn|-0.1fn>><space|-0.2spc>T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X
286 and lstlistings> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
287 <no-break><pageref|auto-42><vspace|0.5fn>
289 8.1<space|2spc>Additional lstlstings parameters
290 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
291 <no-break><pageref|auto-43>
293 8.2<space|2spc>Parsing chunk arguments
294 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
295 <no-break><pageref|auto-44>
297 8.3<space|2spc>Expanding parameters in the text
298 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
299 <no-break><pageref|auto-45>
301 <vspace*|1fn><with|font-series|bold|math-font-series|bold|9<space|2spc>Language
302 Modes & Quoting> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
303 <no-break><pageref|auto-46><vspace|0.5fn>
305 9.1<space|2spc>Modes <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
306 <no-break><pageref|auto-47>
308 <with|par-left|1.5fn|9.1.1<space|2spc>Modes to keep code together
309 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
310 <no-break><pageref|auto-48>>
312 <with|par-left|1.5fn|9.1.2<space|2spc>Modes affect included chunks
313 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
314 <no-break><pageref|auto-49>>
316 9.2<space|2spc>Language Mode Definitions
317 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
318 <no-break><pageref|auto-50>
320 <with|par-left|1.5fn|9.2.1<space|2spc>Backslash
321 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
322 <no-break><pageref|auto-51>>
324 <with|par-left|1.5fn|9.2.2<space|2spc>Strings
325 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
326 <no-break><pageref|auto-52>>
328 <with|par-left|1.5fn|9.2.3<space|2spc>Parentheses, Braces and Brackets
329 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
330 <no-break><pageref|auto-53>>
332 <with|par-left|1.5fn|9.2.4<space|2spc>Customizing Standard Modes
333 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
334 <no-break><pageref|auto-54>>
336 <with|par-left|1.5fn|9.2.5<space|2spc>Comments
337 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
338 <no-break><pageref|auto-55>>
340 <with|par-left|1.5fn|9.2.6<space|2spc>Regex
341 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
342 <no-break><pageref|auto-56>>
344 <with|par-left|1.5fn|9.2.7<space|2spc>Perl
345 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
346 <no-break><pageref|auto-57>>
348 <with|par-left|1.5fn|9.2.8<space|2spc>sh
349 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
350 <no-break><pageref|auto-58>>
352 9.3<space|2spc>Some tests <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
353 <no-break><pageref|auto-59>
355 9.4<space|2spc>A non-recursive mode tracker
356 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
357 <no-break><pageref|auto-60>
359 <with|par-left|1.5fn|9.4.1<space|2spc>Constructor
360 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
361 <no-break><pageref|auto-61>>
363 <with|par-left|1.5fn|9.4.2<space|2spc>Management
364 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
365 <no-break><pageref|auto-62>>
367 <with|par-left|1.5fn|9.4.3<space|2spc>Tracker
368 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
369 <no-break><pageref|auto-63>>
371 <with|par-left|3fn|9.4.3.1<space|2spc>One happy chunk
372 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
373 <no-break><pageref|auto-64>>
375 <with|par-left|3fn|9.4.3.2<space|2spc>Tests
376 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
377 <no-break><pageref|auto-65>>
379 9.5<space|2spc>Escaping and Quoting <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
380 <no-break><pageref|auto-66>
382 <vspace*|1fn><with|font-series|bold|math-font-series|bold|10<space|2spc>Recognizing
383 Chunks> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
384 <no-break><pageref|auto-67><vspace|0.5fn>
386 10.1<space|2spc>Chunk start <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
387 <no-break><pageref|auto-68>
389 <with|par-left|1.5fn|10.1.1<space|2spc>T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X<rsub|<space|-0.4spc><move|<resize|M<space|-0.2spc>A<space|-0.4spc>CS||||0.5fn>|0fn|-0.1fn>>
390 hackery <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
391 <no-break><pageref|auto-69>>
393 <with|par-left|1.5fn|10.1.2<space|2spc>lstlistings
394 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
395 <no-break><pageref|auto-70>>
397 <with|par-left|1.5fn|10.1.3<space|2spc>T<rsub|<space|-0.4spc><move|<resize|<with|math-level|0|E>||||0.5fn>|0fn|-0.1fn>><space|-0.4spc>X<rsub|<space|-0.4spc><move|<resize|M<space|-0.2spc>A<space|-0.4spc>CS||||0.5fn>|0fn|-0.1fn>>
398 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
399 <no-break><pageref|auto-71>>
401 <with|par-left|1.5fn|10.1.4<space|2spc>Noweb
402 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
403 <no-break><pageref|auto-72>>
405 10.2<space|2spc>Chunk end <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
406 <no-break><pageref|auto-73>
408 <with|par-left|1.5fn|10.2.1<space|2spc>lstlistings
409 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
410 <no-break><pageref|auto-74>>
412 <with|par-left|1.5fn|10.2.2<space|2spc>noweb
413 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
414 <no-break><pageref|auto-75>>
416 10.3<space|2spc>Chunk contents <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
417 <no-break><pageref|auto-76>
419 <with|par-left|1.5fn|10.3.1<space|2spc>lstlistings
420 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
421 <no-break><pageref|auto-77>>
423 <vspace*|1fn><with|font-series|bold|math-font-series|bold|11<space|2spc>Processing
424 Options> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
425 <no-break><pageref|auto-78><vspace|0.5fn>
427 <vspace*|1fn><with|font-series|bold|math-font-series|bold|12<space|2spc>Generating
428 the Output> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
429 <no-break><pageref|auto-79><vspace|0.5fn>
431 12.1<space|2spc>Assembling the Chunks
432 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
433 <no-break><pageref|auto-80>
435 <with|par-left|1.5fn|12.1.1<space|2spc>Chunk Parts
436 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
437 <no-break><pageref|auto-81>>
439 <vspace*|1fn><with|font-series|bold|math-font-series|bold|13<space|2spc>Storing
440 Chunks> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
441 <no-break><pageref|auto-82><vspace|0.5fn>
443 <vspace*|1fn><with|font-series|bold|math-font-series|bold|14<space|2spc>getopt>
444 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
445 <no-break><pageref|auto-83><vspace|0.5fn>
447 <vspace*|1fn><with|font-series|bold|math-font-series|bold|15<space|2spc>Fangle
448 LaTeX source code> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
449 <no-break><pageref|auto-84><vspace|0.5fn>
451 15.1<space|2spc>fangle module <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
452 <no-break><pageref|auto-85>
454 <with|par-left|1.5fn|15.1.1<space|2spc>The Chunk style
455 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
456 <no-break><pageref|auto-86>>
458 <with|par-left|1.5fn|15.1.2<space|2spc>The chunkref style
459 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
460 <no-break><pageref|auto-87>>
462 15.2<space|2spc>Latex Macros <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
463 <no-break><pageref|auto-88>
465 <with|par-left|1.5fn|15.2.1<space|2spc>The chunk command
466 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
467 <no-break><pageref|auto-89>>
469 <with|par-left|3fn|15.2.1.1<space|2spc>Chunk parameters
470 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
471 <no-break><pageref|auto-90>>
473 <with|par-left|1.5fn|15.2.2<space|2spc>The noweb styled caption
474 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
475 <no-break><pageref|auto-91>>
477 <with|par-left|1.5fn|15.2.3<space|2spc>The chunk counter
478 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
479 <no-break><pageref|auto-93>>
481 <with|par-left|1.5fn|15.2.4<space|2spc>Cross references
482 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
483 <no-break><pageref|auto-94>>
485 <with|par-left|1.5fn|15.2.5<space|2spc>The end
486 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
487 <no-break><pageref|auto-95>>
489 <vspace*|1fn><with|font-series|bold|math-font-series|bold|16<space|2spc>Extracting
490 fangle> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
491 <no-break><pageref|auto-96><vspace|0.5fn>
493 16.1<space|2spc>Extracting from Lyx <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
494 <no-break><pageref|auto-97>
496 16.2<space|2spc>Extracting documentation
497 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
498 <no-break><pageref|auto-98>
500 16.3<space|2spc>Extracting from the command line
501 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
502 <no-break><pageref|auto-99>
504 16.4<space|2spc>Testing <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
505 <no-break><pageref|auto-100>
507 <vspace*|2fn><with|font-series|bold|math-font-series|bold|font-size|1.19|III<space|2spc>Tests>
508 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
509 <no-break><pageref|auto-101><vspace|1fn>
511 <vspace*|1fn><with|font-series|bold|math-font-series|bold|17<space|2spc>Chunk
512 Parameters> <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
513 <no-break><pageref|auto-102><vspace|0.5fn>
515 <vspace*|1fn><with|font-series|bold|math-font-series|bold|18<space|2spc>Compile-log-lyx>
516 <datoms|<macro|x|<repeat|<arg|x>|<with|font-series|medium|<with|font-size|1|<space|0.2fn>.<space|0.2fn>>>>>|<htab|5mm>>
517 <no-break><pageref|auto-103><vspace|0.5fn>
522 <chapter|Introduction to Literate Programming>
524 Todo: Should really follow on from a part-0 explanation of what literate
527 <chapter|Running Fangle>
529 Fangle is a replacement for <name|noweb>, which consists of
530 <verbatim|notangle>, <verbatim|noroots> and <verbatim|noweave>.
532 Like <verbatim|notangle> and <verbatim|noroots>, <verbatim|fangle> can read
533 multiple named files, or from stdin.
535 <section|Listing roots>
537 The -r option causes fangle to behave like noroots.
539 <code*|fangle -r filename.tex>
541 will print out the fangle roots of a tex file.\
543 Unlike the <verbatim|noroots> command, the printed roots are not enclosed
544 in angle brackets e.g. <verbatim|\<less\>\<less\>name\<gtr\>\<gtr\>>,
545 unless at least one of the roots is defined using the <verbatim|notangle>
546 notation <verbatim|\<less\>\<less\>name\<gtr\>\<gtr\>=>.
548 Also, unlike noroots, it prints out all roots --- not just those that are
549 not used elsewhere. I find that a root not being used doesn't make it
550 particularly top level <emdash> and so-called top level roots could also be
551 included in another root as well.\
553 My convention is that top level roots to be extracted begin with
554 <verbatim|./> and have the form of a filename.
556 Makefile.inc, discussed in <reference|makefile.inc>, can automatically
557 extract all such sources prefixed with <verbatim|./>
559 <section|Extracting roots>
561 notangle's <verbatim|-R> and <verbatim|-L> options are supported.
563 If you are using <LyX> or <LaTeX>, the standard way to extract a file would
566 <verbatim|fangle -R./Makefile.inc fangle.tex \<gtr\> ./Makefile.inc>
568 If you are using <TeXmacs>, the standard way to extract a file would
571 <verbatim|fangle -R./Makefile.inc fangle.txt \<gtr\> ./Makefile.inc>
573 <TeXmacs> users would obtain the text file with a <em|verbatim> export from
574 <TeXmacs> which can be done on the command line with <verbatim|texmacs -s
575 -c fangle.tm fangle.txt -q>
577 Unlike the <verbatim|noroots> command, the <verbatim|<verbatim|-L>> option
578 to generate C pre-preocessor <verbatim|#file> style line-number
579 directives,does not break indenting of the generated file..
581 Also, thanks to mode tracking (described in <reference|modes>) the
582 <verbatim|-L> option does not interrupt (and break) multi-line C macros
585 This does mean that sometimes the compiler might calculate the source line
586 wrongly when generating error messages in such cases, but there isn't any
587 other way around if multi-line macros include other chunks.
589 Future releases will include a mapping file so that line/character
590 references from the C compiler can be converted to the correct part of the
593 <section|Formatting the document>
595 The noweave replacement built into the editing and formatting environment
596 for <TeXmacs>, <LyX> (which uses <LaTeX>), and even for raw <LaTeX>.
598 Use of fangle with <TeXmacs>, <LyX> and <LaTeX> are explained the the next
601 <chapter|Using Fangle with <LaTeX>>
603 Because the noweave replacement is impemented in <LaTeX>, there is no
604 processing stage required before running the <LaTeX> command. Of course,
605 <LaTeX> may need running two or more times, so that the code chunk
606 references can be fully calculated.
608 The formatting is managed by a set of macros shown in
609 <reference|latex-source>, and can be included with:
611 <verbatim|\\usepackage{fangle.sty}>
613 Norman Ramsay's origial <filename|noweb.sty> package is currently required
614 as it is used for formatting the code chunk captions.
616 The <filename|listings.sty> package is required, and is used for formatting
617 the code chunks and syntax highlighting.
619 The <filename|xargs.sty> package is also required, and makes writing
620 <LaTeX> macro so much more pleasant.
622 <todo|Add examples of use of Macros>
624 <chapter|Using Fangle with <LyX>>
626 <LyX> uses the same <LaTeX> macros shown in <reference|latex-source> as
627 part of a <LyX> module file <filename|fangle.module>, which automatically
628 includes the macros in the document pre-amble provided that the fangle
629 <LyX> module is used in the document.
631 <section|Installing the <LyX> module>
633 Copy <filename|fangle.module> to your <LyX> layouts directory, which for
634 unix users will be <filename|~/.lyx/layouts>
636 In order to make the new literate styles availalble, you will need to
637 reconfigure <LyX> by clicking Tools-\<gtr\>Reconfigure, and then re-start
640 <section|Obtaining a decent mono font>
642 The syntax high-lighting features of <name|lstlistings> makes use of bold;
643 however a mono-space tt font is used to typeset the listings. Obtaining a
644 <with|font-family|tt|<strong|bold> tt font> can be impossibly difficult and
645 amazingly easy. I spent many hours at it, following complicated
646 instructions from those who had spend many hours over it, and was finally
647 delivered the simple solution on the lyx mailing list.
651 The simple way was to add this to my preamble:
654 \\usepackage{txfonts}
656 \\renewcommand{\\ttdefault}{txtt}
663 The next simplest way was to use ams poor-mans-bold, by adding this to the
669 %\\renewcommand{\\ttdefault}{txtt}
671 %somehow make \\pmb be the command for bold, forgot how, sorry, above
675 It works, but looks wretched on the dvi viewer.
677 <subsection|Luximono>
679 The lstlistings documention suggests using Luximono.
681 Luximono was installed according to the instructions in Ubuntu Forums
682 thread 1159181<\footnote>
683 http://ubuntuforums.org/showthread.php?t=1159181
684 </footnote> with tips from miknight<\footnote>
685 http://miknight.blogspot.com/2005/11/how-to-install-luxi-mono-font-in.html
686 </footnote> stating that <verbatim|sudo updmap --enable MixedMap ul9.map>
687 is required. It looks fine in PDF and PS view but still looks rotten in dvi
690 <section|Formatting your Lyx document>
692 It is not necessary to base your literate document on any of the original
693 <LyX> literate classes; so select a regular class for your document type.
695 Add the new module <em|Fangle Literate Listings> and also <em|Logical
696 Markup> which is very useful.
698 In the drop-down style listbox you should notice a new style defined,
701 When you wish to insert a literate chunk, you enter it's plain name in the
702 Chunk style, instead of the old <name|noweb> method that uses
703 <verbatim|\<less\>\<less\>name\<gtr\>\<gtr\>=> type tags. In the line (or
704 paragraph) following the chunk name, you insert a listing with:
705 Insert-\<gtr\>Program Listing.
707 Inside the white listing box you can type (or paste using
708 <kbd|shift+ctrl+V>) your listing. There is no need to use <kbd|ctrl+enter>
709 at the end of lines as with some older <LyX> literate techniques --- just
710 press enter as normal.
712 <subsection|Customising the listing appearance>
714 The code is formatted using the <name|lstlistings> package. The chunk style
715 doesn't just define the chunk name, but can also define any other chunk
716 options supported by the lstlistings package <verbatim|\\lstset> command.
717 In fact, what you type in the chunk style is raw latex. If you want to set
718 the chunk language without having to right-click the listing, just add
719 <verbatim|,lanuage=C> after the chunk name. (Currently the language will
720 affect all subsequent listings, so you may need to specify
721 <verbatim|,language=> quite a lot).
723 <todo|so fix the bug>
725 Of course you can do this by editing the listings box advanced properties
726 by right-clicking on the listings box, but that takes longer, and you can't
727 see at-a-glance what the advanced settings are while editing the document;
728 also advanced settings apply only to that box --- the chunk settings apply
729 through the rest of the document<\footnote>
730 It ought to apply only to subsequent chunks of the same name. I'll fix
734 <todo|So make sure they only apply to chunks of that name>
736 <subsection|Global customisations>
738 As lstlistings is used to set the code chunks, it's <verbatim|\\lstset>
739 command can be used in the pre-amble to set some document wide settings.
741 If your source has many words with long sequences of capital letters, then
742 <verbatim|columns=fullflexible> may be a good idea, or the capital letters
743 will get crowded. (I think lstlistings ought to use a slightly smaller font
744 for captial letters so that they still fit).
746 The font family <verbatim|\\ttfamily> looks more normal for code, but has
747 no bold (an alternate typewriter font is used).\
749 With <verbatim|\\ttfamily>, I must also specify
750 <verbatim|columns=fullflexible> or the wrong letter spacing is used.
752 In my <LaTeX> pre-amble I usually specialise my code format with:
754 <\nf-chunk|document-preamble>
757 <item>numbers=left, stepnumber=1, numbersep=5pt,
759 <item>breaklines=false,
761 <item>basicstyle=\\footnotesize\\ttfamily,
763 <item>numberstyle=\\tiny,
767 <item>columns=fullflexible,
769 <item>numberfirstline=true
776 <section|Configuring the build script>
778 You can invoke code extraction and building from the <LyX> menu option
779 Document-\<gtr\>Build Program.
781 First, make sure you don't have a conversion defined for Lyx-\<gtr\>Program
783 From the menu Tools-\<gtr\>Preferences, add a conversion from
784 Latex(Plain)-\<gtr\>Program as:
787 set -x ; fangle -Rlyx-build $$i \|\
789 \ \ env LYX_b=$$b LYX_i=$$i LYX_o=$$o LYX_p=$$p LYX_r=$$r bash
792 (But don't cut-n-paste it from this document or you may be be pasting a
793 multi-line string which will break your lyx preferences file).\
795 I hope that one day, <LyX> will set these into the environment when calling
798 You may also want to consider adding options to this conversion...
800 <verbatim|parselog=/usr/share/lyx/scripts/listerrors>
802 ...but if you do you will lose your stderr<\footnote>
803 There is some bash plumbing to get a copy of stderr but this footnote is
807 Now, a shell script chunk called <filename|lyx-build> will be extracted and
808 run whenever you choose the Document-\<gtr\>Build Program menu item.
810 This document was originally managed using <LyX> and lyx-build script for
811 this document is shown here for historical reference.\
814 lyx -e latex fangle.lyx && \\
816 \ \ fangle fangle.lyx \<gtr\> ./autoboot
819 This looks simple enough, but as mentioned, fangle has to be had from
820 somewhere before it can be extracted.
824 When the lyx-build chunk is executed, the current directory will be a
825 temporary directory, and <verbatim|LYX_SOURCE> will refer to the tex file
826 in this temporary directory. This is unfortunate as our makefile wants to
827 run from the project directory where the Lyx file is kept.
829 We can extract the project directory from <verbatim|$$r>, and derive the
830 probable Lyx filename from the noweb file that Lyx generated.
832 <\nf-chunk|lyx-build-helper>
833 <item>PROJECT_DIR="$LYX_r"
835 <item>LYX_SRC="$PROJECT_DIR/${LYX_i%.tex}.lyx"
837 <item>TEX_DIR="$LYX_p"
839 <item>TEX_SRC="$TEX_DIR/$LYX_i"
842 And then we can define a lyx-build fragment similar to the autoboot
845 <\nf-chunk|lyx-build>
848 <item>=\<less\>\\chunkref{lyx-build-helper}\<gtr\>
850 <item>cd $PROJECT_DIR \|\| exit 1
854 <item>#/usr/bin/fangle -filter ./notanglefix-filter \\
856 <item># \ -R./Makefile.inc "../../noweb-lyx/noweb-lyx3.lyx" \\
858 <item># \ \| sed '/NOWEB_SOURCE=/s/=.*/=samba4-dfs.lyx/' \\
860 <item># \ \<gtr\> ./Makefile.inc
864 <item>#make -f ./Makefile.inc fangle_sources
869 <chapter|Using Fangle with <TeXmacs>>
871 <todo|Write this chapter>
873 <chapter|Fangle with Makefiles><label|makefile.inc>
875 Here we describe a <filename|Makefile.inc> that you can include in your own
876 Makefiles, or glue as a recursive make to other projects.
878 <filename|Makefile.inc> will cope with extracting all the other source
879 files from this or any specified literate document and keeping them up to
882 It may also be included by a <verbatim|Makefile> or <verbatim|Makefile.am>
883 defined in a literate document to automatically deal with the extraction of
884 source files and documents during normal builds.
886 Thus, if <verbatim|Makefile.inc> is included into a main project makefile
887 it add rules for the source files, capable of extracting the source files
888 from the literate document.
890 <section|A word about makefiles formats>
892 Whitespace formatting is very important in a Makefile. The first character
893 of each action line must be a TAB.\
896 target: pre-requisite
903 This requires that the literate programming environment have the ability to
904 represent a TAB character in a way that fangle will generate an actual TAB
907 We also adopt a convention that code chunks whose names beginning with
908 <verbatim|./> should always be automatically extracted from the document.
909 Code chunks whose names do not begin with <verbatim|./> are for internal
910 reference. Such chunks may be extracted directly, but will not be
911 automatically extracted by this Makefile.
913 <section|Extracting Sources>
915 Our makefile has two parts; variables must be defined before the targets
918 As we progress through this chapter, explaining concepts, we will be adding
919 lines to <nf-ref|Makefile.inc-vars|> and <nf-ref|Makefile.inc-targets|>
920 which are included in <nf-ref|./Makefile.inc|> below.
922 <\nf-chunk|./Makefile.inc>
923 <item><nf-ref|Makefile.inc-vars|>
925 <item><nf-ref|Makefile.inc-targets|>
928 We first define a placeholder for <verbatim|LITERATE_SOURCE> to hold the
929 name of this document. This will normally be passed on the command line.
931 <\nf-chunk|Makefile.inc-vars>
932 <item>LITERATE_SOURCE=
935 Fangle cannot process <LyX> or <TeXmacs> documents directly, so the first
936 stage is to convert these to more suitable text based formats<\footnote>
937 <LyX> and <TeXmacs> formats are text-based, but not suitable for fangle
940 <subsection|Converting from <LyX> to <LaTeX>><label|Converting-from-Lyx>
942 The first stage will always be to convert the <LyX> file to a <LaTeX> file.
943 Fangle must run on a <TeX> file because the <LyX> command
944 <verbatim|server-goto-file-line><\footnote>
945 The Lyx command <verbatim|server-goto-file-line> is used to position the
946 Lyx cursor at the compiler errors.
947 </footnote> requries that the line number provided be a line of the <TeX>
948 file and always maps this the line in the <LyX> docment. We use
949 <verbatim|server-goto-file-line> when moving the cursor to error lines
950 during compile failures.
952 The command <verbatim|lyx -e literate fangle.lyx> will produce
953 <verbatim|fangle.tex>, a <TeX> file; so we define a make target to be the
954 same as the <LyX> file but with the <verbatim|.tex> extension.
956 The <verbatim|EXTRA_DIST> is for automake support so that the <TeX> files
957 will automaticaly be distributed with the source, to help those who don't
958 have <LyX> installed.
960 <\nf-chunk|Makefile.inc-vars>
961 <item>TEX_SOURCE=$(LYX_SOURCE:.lyx=.tex)
963 <item>EXTRA_DIST+=$(TEX_SOURCE)
966 We then specify that the <TeX> source is to be generated from the <LyX>
969 <\nf-chunk|Makefile.inc-targets>
970 <item>$(TEX_SOURCE): $(LYX_SOURCE)
972 <item><nf-tab>lyx -e latex $\<less\>
976 <item><nf-tab>rm -f -- $(TEX_SOURCE)
978 <item>clean: clean_tex
981 <subsection|Converting from <TeXmacs>><label|Converting-from-Lyx>
983 Fangle cannot process <TeXmacs> files directly<\footnote>
984 but this is planned when <TeXmacs> uses xml as it's native format
985 </footnote>, but must first convert them to text files.
987 The command <verbatim|texmacs -c fangle.tm fangle.txt -q> will produce
988 <verbatim|fangle.txt>, a text file; so we define a make target to be the
989 same as the <TeXmacs> file but with the <verbatim|.txt> extension.
991 The <verbatim|EXTRA_DIST> is for automake support so that the <TeX> files
992 will automaticaly be distributed with the source, to help those who don't
993 have <LyX> installed.
995 <\nf-chunk|Makefile.inc-vars>
996 <item>TXT_SOURCE=$(LITERATE_SOURCE:.tm=.txt)
998 <item>EXTRA_DIST+=$(TXT_SOURCE)
1001 <todo|Add loop around each $\<less\> so multiple targets can be specified>
1003 <\nf-chunk|Makefile.inc-targets>
1004 <item>$(TXT_SOURCE): $(LITERATE_SOURCE)
1006 <item><nf-tab>texmacs -c $\<less\> $(TXT_SOURCE) -q
1010 <item><nf-tab>rm -f -- $(TXT_SOURCE)
1012 <item>clean: clean_txt
1015 <section|Extracting Program Source>
1017 The program source is extracted using fangle, which is designed to operate
1018 on text or a <LaTeX> documents<\footnote>
1019 <LaTeX> documents are just slightly special text documents
1022 <\nf-chunk|Makefile.inc-vars>
1023 <item>FANGLE_SOURCE=$(TEX_SOURCE) $(TXT_SOURCE)
1026 The literate document can result in any number of source files, but not all
1027 of these will be changed each time the document is updated. We certainly
1028 don't want to update the timestamps of these files and cause the whole
1029 source tree to be recompiled just because the literate explanation was
1030 revised. We use <verbatim|CPIF> from the <em|Noweb> tools to avoid updating
1031 the file if the content has not changed, but should probably write our own.
1033 However, if a source file is not updated, then the fangle file will always
1034 have a newer time-stamp and the makefile would always re-attempt to extact
1035 a newer source file which would be a waste of time.
1037 Because of this, we use a stamp file which is always updated each time the
1038 sources are fully extracted from the <LaTeX> document. If the stamp file is
1039 newer than the document, then we can avoid an attempt to re-extract any of
1040 the sources. Because this stamp file is only updated when extraction is
1041 complete, it is safe for the user to interrupt the build-process
1044 We use <verbatim|echo> rather than <verbatim|touch> to update the stamp
1045 file beause the <verbatim|touch> command does not work very well over an
1046 <verbatim|sshfs>mount \ that I was using.
1048 <\nf-chunk|Makefile.inc-vars>
1049 <item>FANGLE_SOURCE_STAMP=$(FANGLE_SOURCE).stamp
1052 <\nf-chunk|Makefile.inc-targets>
1053 <item>$(FANGLE_SOURCE_STAMP): $(FANGLE_SOURCE) \\
1055 <item><nf-tab> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $(FANGLE_SOURCES) ; \\
1057 <item><nf-tab>echo -n \<gtr\> $(FANGLE_SOURCE_STAMP)
1061 <item><nf-tab>rm -f $(FANGLE_SOURCE_STAMP)
1063 <item>clean: clean_stamp
1066 <section|Extracting Source Files>
1068 We compute <verbatim|FANGLE_SOURCES> to hold the names of all the source
1069 files defined in the document. We compute this only once, by means of
1070 <verbatim|:=> in assignent. The sed deletes the any
1071 <verbatim|\<less\>\<less\>> and <verbatim|\<gtr\>\<gtr\>> which may
1072 surround the roots names (for compatibility with Noweb's noroots command).
1074 As we use chunk names beginning with <filename|./> to denote top level
1075 fragments that should be extracted, we filter out all fragments that do not
1076 begin with <filename|./>
1079 <verbatim|FANGLE_PREFIX> is set to <verbatim|./> by default, but whatever
1080 it may be overridden to, the prefix is replaced by a literal
1081 <verbatim|./> before extraction so that files will be extracted in the
1082 current directory whatever the prefix. This helps namespace or
1083 sub-project prefixes like <verbatim|documents:> for chunks like
1084 <verbatim|documents:docbook/intro.xml>
1087 <todo|This doesn't work though, because it loses the full name and doesn't
1088 know what to extact!>
1090 <\nf-chunk|Makefile.inc-vars>
1091 <item>FANGLE_PREFIX:=\\.\\/
1093 <item>FANGLE_SOURCES:=$(shell \\
1095 <item> \ fangle -r $(FANGLE_SOURCE) \|\\
1097 <item> \ sed -e 's/^[\<less\>][\<less\>]//;s/[\<gtr\>][\<gtr\>]$$//;/^$(FANGLE_PREFIX)/!d'
1100 <item> \ \ \ \ \ -e 's/^$(FANGLE_PREFIX)/\\.\\//' )
1103 The target below, <verbatim|echo_fangle_sources> is a helpful debugging
1104 target and shows the names of the files that would be extracted.
1106 <\nf-chunk|Makefile.inc-targets>
1107 <item>.PHONY: echo_fangle_sources
1109 <item>echo_fangle_sources: ; @echo $(FANGLE_SOURCES)
1112 We define a convenient target called <verbatim|fangle_sources> so that
1113 <verbatim|make -f fangle_sources> will re-extract the source if the
1114 literate document has been updated.\
1116 <\nf-chunk|Makefile.inc-targets>
1117 <item>.PHONY: fangle_sources
1119 <item>fangle_sources: $(FANGLE_SOURCE_STAMP)
1122 And also a convenient target to remove extracted sources.
1124 <\nf-chunk|Makefile.inc-targets>
1125 <item>.PHONY: clean_fangle_sources
1127 <item>clean_fangle_sources: ; \\
1129 <item> \ \ \ \ \ \ \ rm -f -- $(FANGLE_SOURCE_STAMP) $(FANGLE_SOURCES)
1132 We now look at the extraction of the source files.
1134 This makefile macro <verbatim|if_extension> takes 4 arguments: the filename
1135 <verbatim|$(1)>, some extensions to match <verbatim|$(2)> and a shell
1136 command to return if the filename does match the exensions <verbatim|$(3)>,
1137 and a shell command to return if it does not match the extensions
1140 <\nf-chunk|Makefile.inc-vars>
1141 <item>if_extension=$(if $(findstring $(suffix $(1)),$(2)),$(3),$(4))
1144 For some source files like C files, we want to output the line number and
1145 filename of the original <LaTeX> document from which the source
1147 I plan to replace this option with a separate mapping file so as not to
1148 pollute the generated source, and also to allow a code pretty-printing
1149 reformatter like <verbatim|indent> be able to re-format the file and
1150 adjust for changes through comparing the character streams.
1153 To make this easier we define the file extensions for which we want to do
1156 <\nf-chunk|Makefile.inc-vars>
1157 <item>C_EXTENSIONS=.c .h
1160 We can then use the <verbatim|if_extensions> macro to define a macro which
1161 expands out to the <verbatim|-L> option if fangle is being invoked in a C
1162 source file, so that C compile errors will refer to the line number in the
1165 <\nf-chunk|Makefile.inc-vars>
1168 <item>nf_line=-L -T$(TABS)
1170 <item>fangle=fangle $(call if_extension,$(2),$(C_EXTENSIONS),$(nf_line))
1174 We can use a similar trick to define an indent macro which takes just the
1175 filename as an argument and can return a pipeline stage calling the indent
1176 command. Indent can be turned off with <verbatim|make fangle_sources
1179 <\nf-chunk|Makefile.inc-vars>
1180 <item>indent_options=-npro -kr -i8 -ts8 -sob -l80 -ss -ncs
1182 <item>indent=$(call if_extension,$(1),$(C_EXTENSIONS), \| indent
1186 We now define the pattern for extracting a file. The files are written
1187 using noweb's <verbatim|cpif> so that the file timestamp will not be
1188 touched if the contents haven't changed. This avoids the need to rebuild
1189 the entire project because of a typographical change in the documentation,
1190 or if none or a few C source files have changed.
1192 <\nf-chunk|Makefile.inc-vars>
1193 <item>fangle_extract=@mkdir -p $(dir $(1)) && \\
1195 <item> \ $(call fangle,$(2),$(1)) \<gtr\> "$(1).tmp" && \\
1197 <item> \ cat "$(1).tmp" $(indent) \| cpif "$(1)" \\
1199 <item> \ && rm -- "$(1).tmp" \|\| \\
1201 <item> \ (echo error newfangling $(1) from $(2) ; exit 1)
1204 We define a target which will extract or update all sources. To do this we
1205 first defined a makefile template that can do this for any source file in
1206 the <LaTeX> document.
1208 <\nf-chunk|Makefile.inc-vars>
1209 <item>define FANGLE_template
1213 <item><nf-tab>$$(call fangle_extract,$(1),$(2))
1215 <item> \ FANGLE_TARGETS+=$(1)
1220 We then enumerate the discovered <verbatim|FANGLE_SOURCES> to generate a
1221 makefile rule for each one using the makefile template we defined above.
1223 <\nf-chunk|Makefile.inc-targets>
1224 <item>$(foreach source,$(FANGLE_SOURCES),\\
1226 <item> \ $(eval $(call FANGLE_template,$(source),$(FANGLE_SOURCE))) \\
1231 These will all be built with <verbatim|FANGLE_SOURCE_STAMP>.
1233 We also remove the generated sources on a make distclean.
1235 <\nf-chunk|Makefile.inc-targets>
1236 <item>_distclean: clean_fangle_sources
1239 <section|Extracting Documentation>
1241 We then identify the intermediate stages of the documentation and their
1242 build and clean targets.
1244 <subsection|Formatting <TeX>>
1246 <subsubsection|Running pdflatex>
1248 We produce a pdf file from the tex file.
1250 <\nf-chunk|Makefile.inc-vars>
1251 <item>FANGLE_PDF=$(TEX_SOURCE:.tex=.pdf)
1254 We run pdflatex twice to be sure that the contents and aux files are up to
1255 date. We certainly are <em|required> to run pdflatex at least twice if
1256 these files do not exist.
1258 <\nf-chunk|Makefile.inc-targets>
1259 <item>$(FANGLE_PDF): $(TEX_SOURCE)
1261 <item><nf-tab>pdflatex $\<less\> && pdflatex $\<less\>
1267 <item><nf-tab>rm -f -- $(FANGLE_PDF) $(TEX_SOURCE:.tex=.toc) \\
1269 <item><nf-tab> \ $(TEX_SOURCE:.tex=.log) $(TEX_SOURCE:.tex=.aux)
1272 <subsection|Formatting <TeXmacs>>
1274 <TeXmacs> can produce a PDF file directly.
1276 <\nf-chunk|Makefile.inc-vars>
1277 <item>FANGLE_PDF=$(TEX_SOURCE:.tm=.pdf)
1281 Outputting the PDF may not be enough to update the links and page
1284 we need to update twice, generate a pdf, update twice mode and generate a
1287 Basically the PDF export of <TeXmacs> is pretty rotten and doesn't work
1288 properly from the CLI
1291 <\nf-chunk|Makefile.inc-targets>
1292 <item>$(FANGLE_PDF): $(TEXMACS_SOURCE)
1294 <item><nf-tab>texmacs -c $(TEXMACS_SOURCE) $\<less\> -q
1300 <item><nf-tab>rm -f -- $(FANGLE_PDF)
1303 <subsection|Building the Documentation as a Whole>
1305 Currently we only build pdf as a final format, but <verbatim|FANGLE_DOCS>
1306 may later hold other output formats.
1308 <\nf-chunk|Makefile.inc-vars>
1309 <item>FANGLE_DOCS=$(FANGLE_PDF)
1312 We also define <verbatim|fangle_docs> as a convenient phony target.
1314 <\nf-chunk|Makefile.inc-targets>
1315 <item>.PHONY: fangle_docs
1317 <item>fangle_docs: $(FANGLE_DOCS)
1319 <item>docs: fangle_docs
1322 And define a convenient <verbatim|clean_fangle_docs> which we add to the
1323 regular clean target
1325 <\nf-chunk|Makefile.inc-targets>
1326 <item>.PHONEY: clean_fangle_docs
1328 <item>clean_fangle_docs: clean_tex clean_pdf
1330 <item>clean: clean_fangle_docs
1334 <item>distclean_fangle_docs: clean_tex clean_fangle_docs
1336 <item>distclean: clean distclean_fangle_docs
1339 <section|Other helpers>
1341 If <filename|Makefile.inc> is included into <filename|Makefile>, then
1342 extracted files can be updated with this command:
1344 <verbatim|make fangle_sources>
1348 <verbatim|make -f Makefile.inc fangle_sources>
1350 <section|Boot-strapping the extraction>
1352 As well as having the makefile extract or update the source files as part
1353 of it's operation, it also seems convenient to have the makefile
1354 re-extracted itself from <em|this> document.
1356 It would also be convenient to have the code that extracts the makefile
1357 from this document to also be part of this document, however we have to
1358 start somewhere and this unfortunately requires us to type at least a few
1359 words by hand to start things off.
1361 Therefore we will have a minimal root fragment, which, when extracted, can
1362 cope with extracting the rest of the source. This shell script fragment can
1363 do that. It's name is <verbatim|*> <emdash> out of regard for <name|Noweb>,
1364 but when extracted might better be called <verbatim|autoupdate>.
1373 <item>MAKE_SRC="${1:-${NW_LYX:-../../noweb-lyx/noweb-lyx3.lyx}}"
1375 <item>MAKE_SRC=`dirname "$MAKE_SRC"`/`basename "$MAKE_SRC" .lyx`
1377 <item>NOWEB_SRC="${2:-${NOWEB_SRC:-$MAKE_SRC.lyx}}"
1379 <item>lyx -e latex $MAKE_SRC
1383 <item>fangle -R./Makefile.inc ${MAKE_SRC}.tex \\
1385 <item> \ \| sed "/FANGLE_SOURCE=/s/^/#/;T;aNOWEB_SOURCE=$FANGLE_SRC" \\
1387 <item> \ \| cpif ./Makefile.inc
1391 <item>make -f ./Makefile.inc fangle_sources
1394 The general Makefile can be invoked with <filename|./autoboot> and can also
1395 be included into any automake file to automatically re-generate the source
1398 The <em|autoboot> can be extracted with this command:
1401 lyx -e latex fangle.lyx && \\
1403 \ \ fangle fangle.lyx \<gtr\> ./autoboot
1406 This looks simple enough, but as mentioned, fangle has to be had from
1407 somewhere before it can be extracted.
1409 On a unix system this will extract <filename|fangle.module> and the
1410 <filename|fangle> awk script, and run some basic tests.\
1412 <todo|cross-ref to test chapter when it is a chapter all on its own>
1414 <section|Incorporating Makefile.inc into existing projects>
1416 If you are writing a literate module of an existing non-literate program
1417 you may find it easier to use a slight recursive make instead of directly
1418 including <verbatim|Makefile.inc> in the projects makefile.\
1420 This way there is less chance of definitions in <verbatim|Makefile.inc>
1421 interfering with definitions in the main makefile, or with definitions in
1422 other <verbatim|Makefile.inc> from other literate modules of the same
1425 To do this we add some <em|glue> to the project makefile that invokes
1426 Makefile.inc in the right way. The glue works by adding a <verbatim|.PHONY>
1427 target to call the recursive make, and adding this target as an additional
1428 pre-requisite to the existing targets.
1430 <paragraph|Example>Sub-module of existing system
1432 In this example, we are building <verbatim|module.so> as a literate module
1433 of a larger project.
1435 We will show the sort glue that can be inserted into the projects Makefile
1436 <emdash> or more likely <emdash> a regular Makefile included in or invoked
1437 by the projects Makefile.
1439 <\nf-chunk|makefile-glue>
1440 <item>module_srcdir=modules/module
1442 <item>MODULE_SOURCE=module.tm
1444 <item>MODULE_STAMP=$(MODULE_SOURCE).stamp
1447 The existing build system may already have a build target for
1448 <filename|module.o>, but we just add another pre-requisite to that. In this
1449 case we use <filename|module.tm.stamp> as a pre-requisite, the stamp file's
1450 modified time indicating when all sources were extracted<\footnote>
1451 If the projects build system does not know how to build the module from
1452 the extracted sources, then just add build actions here as normal.
1455 <\nf-chunk|makefile-glue>
1456 <item>$(module_srcdir)/module.o: $(module_srcdir)/$(MODULE_STAMP)
1459 The target for this new pre-requisite will be generated by a recursive make
1460 using <filename|Makefile.inc> which will make sure that the source is up to
1461 date, before it is built by the main projects makefile.
1463 <\nf-chunk|makefile-glue>
1464 <item>$(module_srcdir)/$(MODULE_STAMP): $(module_srcdir)/$(MODULE_SOURCE)
1466 <item><nf-tab>$(MAKE) -C $(module_srcdir) -f Makefile.inc fangle_sources
1467 LITERATE_SOURCE=$(MODULE_SOURCE)
1470 We can do similar glue for the docs, clean and distclean targets. In this
1471 example the main prject was using a double colon for these targets, so we
1472 must use the same in our glue.
1474 <\nf-chunk|makefile-glue>
1475 <item>docs:: docs_module
1477 <item>.PHONY: docs_module
1481 <item><nf-tab>$(MAKE) -C $(module_srcdir) -f Makefile.inc docs
1482 LITERATE_SOURCE=$(MODULE_SOURCE)
1486 <item>clean:: clean_module
1488 <item>.PHONEY: clean_module
1492 <item><nf-tab>$(MAKE) -C $(module_srcdir) -f Makefile.inc clean
1493 LITERATE_SOURCE=$(MODULE_SOURCE)
1497 <item>distclean:: distclean_module
1499 <item>.PHONY: distclean_module
1501 <item>distclean_module:
1503 <item><nf-tab>$(MAKE) -C $(module_srcdir) -f Makefile.inc distclean
1504 LITERATE_SOURCE=$(MODULE_SOURCE)
1507 We could do similarly for install targets to install the generated docs.
1511 <chapter|Fangle awk source code>
1513 We use the copyright notice from chapter <reference|License>.
1515 <\nf-chunk|./fangle>
1516 <item>#! /usr/bin/awk -f
1518 <item># <nf-ref|gpl3-copyright|>
1521 We also use code from <person|Arnold Robbins> public domain getopt (1993
1522 revision) defined in <reference|getopt>, and naturally want to attribute
1525 <\nf-chunk|./fangle>
1526 <item># NOTE: Arnold Robbins public domain getopt for awk is also used:
1528 <item><nf-ref|getopt.awk-header|>
1530 <item><nf-ref|getopt.awk-getopt()|>
1535 And include the following chunks (which are explained further on) to make
1538 <\nf-chunk|./fangle>
1539 <item><nf-ref|helper-functions|>
1541 <item><nf-ref|mode-tracker|>
1543 <item><nf-ref|parse_chunk_args|>
1545 <item><nf-ref|chunk-storage-functions|>
1547 <item><nf-ref|output_chunk_names()|>
1549 <item><nf-ref|output_chunks()|>
1551 <item><nf-ref|write_chunk()|>
1553 <item><nf-ref|expand_chunk_args()|>
1557 <item><nf-ref|begin|>
1559 <item><nf-ref|recognize-chunk|>
1564 <section|AWK tricks>
1566 The portable way to erase an array in awk is to split the empty string, so
1567 we define a fangle macro that can split an array, like this:
1569 <\nf-chunk|awk-delete-array>
1570 <item>split("", <nf-arg|ARRAY>);
1571 </nf-chunk|awk|<tuple|ARRAY>>
1573 For debugging it is sometimes convenient to be able to dump the contents of
1574 an array to <verbatim|stderr>, and so this macro is also useful.
1576 <\nf-chunk|dump-array>
1577 <item>print "\\nDump: <nf-arg|ARRAY>\\n--------\\n" \<gtr\>
1580 <item>for (_x in <nf-arg|ARRAY>) {
1582 <item> \ print _x "=" <nf-arg|ARRAY>[_x] "\\n" \<gtr\> "/dev/stderr";
1586 <item>print "========\\n" \<gtr\> "/dev/stderr";
1587 </nf-chunk|awk|<tuple|ARRAY>>
1589 <section|Catching errors>
1591 Fatal errors are issued with the error function:
1594 <item>function error(message)
1598 <item> \ print "ERROR: " FILENAME ":" FNR " " message \<gtr\>
1606 and likewise for non-fatal warnings:
1609 <item>function warning(message)
1613 <item> \ print "WARNING: " FILENAME ":" FNR " " message \<gtr\>
1616 <item> \ warnings++;
1621 and debug output too:
1624 <item>function debug_log(message)
1628 <item> \ print "DEBUG: " FILENAME ":" FNR " " message \<gtr\>
1634 <todo|append=helper-functions>
1636 <\nf-chunk|helper-functions>
1637 <item><nf-ref|error()|>
1640 <chapter|<LaTeX> and lstlistings>
1642 <todo|Split LyX and TeXmacs parts>
1644 For <LyX> and <LaTeX>, the <verbatim|lstlistings> package is used to format
1645 the lines of code chunks. You may recal from chapter XXX that arguments to
1646 a chunk definition are pure <LaTeX> code. This means that fangle needs to
1647 be able to parse <LaTeX> a little.
1649 <LaTeX> arguments to <verbatim|lstlistings> macros are a comma seperated
1650 list of key-value pairs, and values containing commas are enclosed in
1651 <verbatim|{> braces <verbatim|}> (which is to be expected for <LaTeX>).
1653 A sample expressions is:
1655 <verbatim|name=thomas, params={a, b}, something, something-else>
1657 but we see that this is just a simpler form of this expression:
1659 <verbatim|name=freddie, foo={bar=baz, quux={quirk, a=fleeg}}, etc>
1661 We may consider that we need a function that can parse such <LaTeX>
1662 expressions and assign the values to an AWK associated array, perhaps using
1663 a recursive parser into a multi-dimensional hash<\footnote>
1664 as AWK doesn't have nested-hash support
1665 </footnote>, resulting in:
1667 <tabular|<tformat|<cwith|2|6|1|2|cell-lborder|0.5pt>|<cwith|2|6|1|2|cell-rborder|0.5pt>|<cwith|2|6|1|2|cell-bborder|0.5pt>|<cwith|2|6|1|2|cell-tborder|0.5pt>|<cwith|1|1|1|2|cell-lborder|0.5pt>|<cwith|1|1|1|2|cell-rborder|0.5pt>|<cwith|1|1|1|2|cell-bborder|0.5pt>|<cwith|1|1|1|2|cell-tborder|0.5pt>|<table|<row|<cell|key>|<cell|value>>|<row|<cell|a[name]>|<cell|freddie>>|<row|<cell|a[foo,
1668 bar]>|<cell|baz>>|<row|<cell|a[foo, quux,
1669 quirk]>|<cell|>>|<row|<cell|a[foo, quux,
1670 a]>|<cell|fleeg>>|<row|<cell|a[etc]>|<cell|>>>>>
1672 Yet, also, on reflection it seems that sometimes such nesting is not
1673 desirable, as the braces are also used to delimit values that contain
1674 commas --- we may consider that
1676 <verbatim|name={williamson, freddie}>
1678 should assign <verbatim|williamson, freddie> to <verbatim|name>.
1680 In fact we are not so interested in the detail so as to be bothered by
1681 this, which turns out to be a good thing for two reasons. Firstly <TeX> has
1682 a malleable parser with no strict syntax, and secondly whether or not
1683 <verbatim|williamson> and <verbatim|freddie> should count as two items will
1684 be context dependant anyway.
1686 We need to parse this latex for only one reason; which is that we are
1687 extending lstlistings to add some additional arguments which will be used
1688 to express chunk parameters and other chunk options.
1690 <section|Additional lstlstings parameters>
1692 Further on we define a <verbatim|\\Chunk> <LaTeX> macro whose arguments
1693 will consist of a the chunk name, optionally followed by a comma and then a
1694 comma separated list of arguments. In fact we will just need to prefix
1695 <verbatim|name=> to the arguments to in order to create valid lstlistings
1698 There will be other arguments supported too;\
1701 <item*|params>As an extension to many literate-programming styles, fangle
1702 permits code chunks to take parameters and thus operate somewhat like C
1703 pre-processor macros, or like C++ templates. Chunk parameters are
1704 declared with a chunk argument called params, which holds a semi-colon
1705 separated list of parameters, like this:
1707 <verbatim|achunk,language=C,params=name;address>
1709 <item*|addto>a named chunk that this chunk is to be included into. This
1710 saves the effort of having to declare another listing of the named chunk
1711 merely to include this one.
1714 Function get_chunk_args() will accept two paramters, text being the text to
1715 parse, and values being an array to receive the parsed values as described
1716 above. The optional parameter path is used during recursion to build up the
1717 multi-dimensional array path.
1719 <\nf-chunk|./fangle>
1720 <item>=\<less\>\\chunkref{get_chunk_args()}\<gtr\>
1723 <\nf-chunk|get_chunk_args()>
1724 <item>function get_chunk_args(text, values,
1726 <item> \ # optional parameters
1728 <item> \ path, # hierarchical precursors
1730 <item> \ # local vars
1735 The strategy is to parse the name, and then look for a value. If the value
1736 begins with a brace <verbatim|{>, then we recurse and consume as much of
1737 the text as necessary, returning the remaining text when we encounter a
1738 leading close-brace <verbatim|}>. This being the strategy --- and executed
1739 in a loop --- we realise that we must first look for the closing brace
1740 (perhaps preceded by white space) in order to terminate the recursion, and
1741 returning remaining text.
1743 <\nf-chunk|get_chunk_args()>
1746 <item> \ split("", next_chunk_args);
1748 <item> \ while(length(text)) {
1750 <item> \ \ \ if (match(text, "^ *}(.*)", a)) {
1752 <item> \ \ \ \ \ return a[1];
1756 <item> \ \ \ =\<less\>\\chunkref{parse-chunk-args}\<gtr\>
1760 <item> \ return text;
1765 We can see that the text could be inspected with this regex:
1767 <\nf-chunk|parse-chunk-args>
1768 <item>if (! match(text, " *([^,=]*[^,= ]) *(([,=]) *(([^,}]*) *,*
1771 <item> \ return text;
1776 and that <verbatim|a> will have the following values:
1778 <tabular|<tformat|<cwith|2|7|1|2|cell-lborder|0.5pt>|<cwith|2|7|1|2|cell-rborder|0.5pt>|<cwith|2|7|1|2|cell-bborder|0.5pt>|<cwith|2|7|1|2|cell-tborder|0.5pt>|<cwith|1|1|1|2|cell-lborder|0.5pt>|<cwith|1|1|1|2|cell-rborder|0.5pt>|<cwith|1|1|1|2|cell-bborder|0.5pt>|<cwith|1|1|1|2|cell-tborder|0.5pt>|<table|<row|<cell|a[n]>|<cell|assigned
1779 text>>|<row|<cell|1>|<cell|freddie>>|<row|<cell|2>|<cell|=freddie,
1780 foo={bar=baz, quux={quirk, a=fleeg}}, etc>>|<row|<cell|3>|<cell|=>>|<row|<cell|4>|<cell|freddie,
1781 foo={bar=baz, quux={quirk, a=fleeg}}, etc>>|<row|<cell|5>|<cell|freddie>>|<row|<cell|6>|<cell|,
1782 foo={bar=baz, quux={quirk, a=fleeg}}, etc>>>>>
1784 <verbatim|a[3]> will be either <verbatim|=> or <verbatim|,> and signify
1785 whether the option named in <verbatim|a[1]> has a value or not
1788 If the option does have a value, then if the expression
1789 <verbatim|substr(a[4],1,1)> returns a brace <verbatim|{> it will signify
1790 that we need to recurse:
1792 <\nf-chunk|parse-chunk-args>
1795 <item>if (a[3] == "=") {
1797 <item> \ if (substr(a[4],1,1) == "{") {
1799 <item> \ \ \ text = get_chunk_args(substr(a[4],2), values, path name
1804 <item> \ \ \ values[path name]=a[5];
1806 <item> \ \ \ text = a[6];
1812 <item> \ values[path name]="";
1814 <item> \ text = a[2];
1819 We can test this function like this:
1821 <\nf-chunk|gca-test.awk>
1822 <item>=\<less\>\\chunkref{get_chunk_args()}\<gtr\>
1826 <item> \ SUBSEP=".";
1830 <item> \ print get_chunk_args("name=freddie, foo={bar=baz, quux={quirk,
1831 a=fleeg}}, etc", a);
1833 <item> \ for (b in a) {
1835 <item> \ \ \ print "a[" b "] =\<gtr\> " a[b];
1842 which should give this output:
1844 <\nf-chunk|gca-test.awk-results>
1845 <item>a[foo.quux.quirk] =\<gtr\>\
1847 <item>a[foo.quux.a] =\<gtr\> fleeg
1849 <item>a[foo.bar] =\<gtr\> baz
1851 <item>a[etc] =\<gtr\>\
1853 <item>a[name] =\<gtr\> freddie
1856 <section|Parsing chunk arguments><label|Chunk Arguments>
1858 Arguments to paramterized chunks are expressed in round brackets as a comma
1859 separated list of optional arguments. For example, a chunk that is defined
1862 <verbatim|\\Chunk{achunk, params=name ; address}>
1864 could be invoked as:
1866 <verbatim|\\chunkref{achunk}(John Jones, jones@example.com)>
1868 An argument list may be as simple as in <verbatim|\\chunkref{pull}(thing,
1869 otherthing)> or as complex as:
1871 <verbatim|\\chunkref{pull}(things[x, y], get_other_things(a, "(all)"))>
1873 --- which for all it's commas and quotes and parenthesis represents only
1874 two parameters: <verbatim|things[x, y]> and <verbatim|get_other_things(a,
1877 If we simply split parameter list on commas, then the comma in
1878 <verbatim|things[x,y]> would split into two seperate arguments:
1879 <verbatim|things[x> and <verbatim|y]>--- neither of which make sense on
1882 One way to prevent this would be by refusing to split text between matching
1883 delimiters, such as <verbatim|[>, <verbatim|]>, <verbatim|(>, <verbatim|)>,
1884 <verbatim|{>, <verbatim|}> and most likely also <verbatim|">, <verbatim|">
1885 and <verbatim|'>, <verbatim|'>. Of course this also makes it impossible to
1886 pass such mis-matched code fragments as parameters, but I think that it
1887 would be hard for readers to cope with authors who would pass such code
1888 unbalanced fragments as chunk parameters<\footnote>
1889 I know that I couldn't cope with users doing such things, and although
1890 the GPL3 license prevents me from actually forbidding anyone from trying,
1891 if they want it to work they'll have to write the code themselves and not
1892 expect any support from me.
1895 Unfortunately, the full set of matching delimiters may vary from language
1896 to language. In certain C++ template contexts, <verbatim|\<less\>> and
1897 <verbatim|\<gtr\>> would count as delimiters, and yet in other contexts
1900 This puts me in the unfortunate position of having to parse-somewhat all
1901 programming languages without knowing what they are!
1903 However, if this universal mode-tracking is possible, then parsing the
1904 arguments would be trivial. Such a mode tracker is described in chapter
1905 <reference|modes> and used here with simplicity.
1907 <\nf-chunk|parse_chunk_args>
1908 <item>function parse_chunk_args(language, text, values, mode,
1910 <item> \ # local vars
1912 <item> \ c, context, rest)
1916 <item> \ =\<less\>\\chunkref{new-mode-tracker}(context, language,
1919 <item> \ rest = mode_tracker(context, text, values);
1921 <item> \ # extract values
1923 <item> \ for(c=1; c \<less\>= context[0, "values"]; c++) {
1925 <item> \ \ \ values[c] = context[0, "values", c];
1929 <item> \ return rest;
1934 <section|Expanding parameters in the text>
1936 Within the body of the chunk, the parameters are referred to with:
1937 <verbatim|${name}> and <verbatim|${address}>. There is a strong case that a
1938 <LaTeX> style notation should be used, like <verbatim|\\param{name}> which
1939 would be expressed in the listing as <verbatim|=\<less\>\\param{name}\<gtr\>>
1940 and be rendered as <verbatim|<nf-arg|name>>. Such notation would make me go
1941 blind, but I do intend to adopt it.
1943 We therefore need a function <verbatim|expand_chunk_args> which will take a
1944 block of text, a list of permitted parameters, and the arguments which must
1945 substitute for the parameters.\
1947 Here we split the text on <verbatim|${> which means that all parts except
1948 the first will begin with a parameter name which will be terminated by
1949 <verbatim|}>. The split function will consume the literal <verbatim|${> in
1952 <\nf-chunk|expand_chunk_args()>
1953 <item>function expand_chunk_args(text, params, args, \
1955 <item> \ p, text_array, next_text, v, t, l)
1959 <item> \ if (split(text, text_array, "\\\\${")) {
1961 <item> \ \ \ <nf-ref|substitute-chunk-args|>
1967 <item> \ return text;
1972 First, we produce an associative array of substitution values indexed by
1973 parameter names. This will serve as a cache, allowing us to look up the
1974 replacement values as we extract each name.
1976 <\nf-chunk|substitute-chunk-args>
1977 <item>for(p in params) {
1979 <item> \ v[params[p]]=args[p];
1984 We accumulate substituted text in the variable text. As the first part of
1985 the split function is the part before the delimiter --- which is
1986 <verbatim|${> in our case --- this part will never contain a parameter
1987 reference, so we assign this directly to the result kept in
1990 <\nf-chunk|substitute-chunk-args>
1991 <item>text=text_array[1];
1994 We then iterate over the remaining values in the array<\footnote>
1995 I don't know why I think that it will enumerate the array in order, but
1997 </footnote><todo|fix or prove it>, and substitute each reference for it's
2000 <\nf-chunk|substitute-chunk-args>
2001 <item>for(t=2; t in text_array; t++) {
2003 <item> \ =\<less\>\\chunkref{substitute-chunk-arg}\<gtr\>
2008 After the split on <verbatim|${> a valid parameter reference will consist
2009 of valid parameter name terminated by a close-brace <verbatim|}>. A valid
2010 character name begins with the underscore or a letter, and may contain
2011 letters, digits or underscores.
2013 A valid looking reference that is not actually the name of a parameter will
2014 be and not substituted. This is good because there is nothing to substitute
2015 anyway, and it avoids clashes when writing code for languages where
2016 <verbatim|${...}> is a valid construct --- such constructs will not be
2017 interfered with unless the parameter name also matches.
2019 <\nf-chunk|substitute-chunk-arg>
2020 <item>if (match(text_array[t], "^([a-zA-Z_][a-zA-Z0-9_]*)}", l) &&
2022 <item> \ \ \ l[1] in v)\
2026 <item> \ text = text v[l[1]] substr(text_array[t], length(l[1])+2);
2030 <item> \ text = text "${" text_array[t];
2035 <chapter|Language Modes & Quoting><label|modes>
2039 <verbatim|lstlistings> and <verbatim|fangle> both recognize source
2040 languages, and perform some basic parsing. <verbatim|lstlistings> can
2041 detect strings and comments within a language definition and perform
2042 suitable rendering, such as italics for comments, and visible-spaces within
2045 Fangle similarly can recognize strings, and comments, etc, within a
2046 language, so that any chunks included with <verbatim|\\chunkref> can be
2047 suitably escape or quoted.
2049 <subsection|Modes to keep code together>
2051 As an example, in the C language there are a few parse modes, affecting the
2052 interpretation of characters.
2054 One parse mode is the strings mode. The string mode is commenced by an
2055 un-escaped quotation mark <verbatim|"> and terminated by the same. Within
2056 the string mode, only one additional mode can be commenced, it is the
2057 backslash mode <verbatim|\\>, which is always terminated after the folloing
2060 Another mode is <verbatim|[> which is terminated by a <verbatim|]> (unless
2061 it occurs in a string).
2063 Consider this fragment of C code:
2067 <math|things<wide|<around|[|x, y|]>|\<wide-overbrace\>><rsup|1. [ mode>,
2068 get_other_things<wide|<around|(|a, <wide*|<text|"><around|(|all|)><text|">|\<wide-underbrace\>><rsub|3.
2069 " mode>|)>|\<wide-overbrace\>><rsup|2. ( mode>>
2073 Mode nesting prevents the close parenthesis in the quoted string (part 3)
2074 from terminating the parenthesis mode (part 2).
2076 Each language has a set of modes, the default mode being the null mode.
2077 Each mode can lead to other modes.
2079 <subsection|Modes affect included chunks>
2081 For instance, consider this chunk with language=perl:
2083 <nf-chunk|example-perl|print "hello world $0\\n";|perl|>
2085 If it were included in a chunk with <verbatim|language=sh>, like this:
2087 <nf-chunk|example-sh|perl -e "=\<less\>\\chunkref{example-perl}\<gtr\>"|sh|>
2089 fangle would <em|want> to generate output like this:
2091 <verbatim|perl -e "print \\"hello world \\$0\\\\n\\";" >
2093 See that the double quote <verbatim|">, back-slash <verbatim|\\> and
2094 <verbatim|$> have been quoted with a back-slash to protect them from shell
2097 If that were then included in a chunk with language=make, like this:
2099 <\nf-chunk|example-makefile>
2100 <item>target: pre-req
2102 <item><htab|5mm>=\<less\>\\chunkref{example-sh}\<gtr\>
2105 We would need the output to look like this --- note the <verbatim|$$>:
2110 \ \ \ \ \ \ \ \ perl -e "print \\"hello world \\$$0\\\\n\\";"
2113 In order to make this work, we need to define a mode-tracker supporting
2114 each language, that can detect the various quoting modes, and provide a
2115 transformation that must be applied to any included text so that included
2116 text will be interpreted correctly after any interpolation that it may be
2117 subject to at run-time.
2119 For example, the sed transformation for text to be inserted into shell
2120 double-quoted strings would be something like:
2122 <verbatim|s/\\\\/\\\\\\\\/g;s/$/\\\\$/g;s/"/\\\\"/g;>
2124 which protects <verbatim|\\ $ ">.
2126 <todo|I don't think this example is true>The mode tracker must also track
2127 nested mode-changes, as in this sh example.
2129 <verbatim|echo "hello `id ...`">
2131 <phantom|<verbatim|echo "hello `id >><math|\<uparrow\>>
2133 Any characters inserted at the point marked <math|\<uparrow\>> would need
2134 to be escaped, including <verbatim|`> <verbatim|\|> <verbatim|*> among
2135 others. First it would need escaping for the back-ticks <verbatim|`>, and
2136 then for the double-quotes <verbatim|">.
2138 <todo|MAYBE>Escaping need not occur if the format and mode of the included
2139 chunk matches that of the including chunk.
2141 As each chunk is output a new mode tracker for that language is initialized
2142 in it's normal state. As text is output for that chunk the output mode is
2143 tracked. When a new chunk is included, a transformation appropriate to that
2144 mode is selected and pushed onto a stack of transformations. Any text to be
2145 output is first passed through this stack of transformations.
2147 It remains to consider if the chunk-include function should return it's
2148 generated text so that the caller can apply any transformations (and
2149 formatting), or if it should apply the stack of transformations itself.
2151 Note that the transformed text should have the property of not being able
2152 to change the mode in the current chunk.
2154 <todo|Note chunk parameters should probably also be transformed>
2156 <section|Language Mode Definitions>
2158 All modes are stored in a single multi-dimensional hash. The first index is
2159 the language, and the second index is the mode-identifier. The third
2160 indexes are terminators, and optionally, submodes, and delimiters.
2162 A useful set of mode definitions for a nameless general C-type language is
2163 shown here. (Don't be confused by the double backslash escaping needed in
2164 awk. One set of escaping is for the string, and the second set of escaping
2168 TODO: Add =\<less\>\\mode{}\<gtr\> command which will allow us to signify
2171 \ regex and thus fangle will quote it for us.
2174 Submodes are entered by the characters \ <verbatim|"> <verbatim|'>
2175 <verbatim|{> <verbatim|(> <verbatim|[> <verbatim|/*>
2177 <\nf-chunk|common-mode-definitions>
2178 <item>modes[${language}, "", \ "submodes"]="\\\\\\\\\|\\"\|'\|{\|\\\\(\|\\\\[";
2179 </nf-chunk||<tuple|language>>
2181 In the default mode, a comma surrounded by un-important white space is a
2182 delimiter of language items<\footnote>
2183 whatever a <em|language item> might be
2186 <\nf-chunk|common-mode-definitions>
2187 <item>modes[${language}, "", \ "delimiters"]=" *, *";
2188 </nf-chunk||language>
2190 and should pass this test:<todo|Why do the tests run in ?(? mode and not ??
2193 <\nf-chunk|test:mode-definitions>
2194 <item>parse_chunk_args("c-like", "1,2,3", a, "");
2196 <item>if (a[1] != "1") e++;
2198 <item>if (a[2] != "2") e++;
2200 <item>if (a[3] != "3") e++;
2202 <item>if (length(a) != 3) e++;
2204 <item>=\<less\>\\chunkref{pca-test.awk:summary}\<gtr\>
2208 <item>parse_chunk_args("c-like", "joe, red", a, "");
2210 <item>if (a[1] != "joe") e++;
2212 <item>if (a[2] != "red") e++;
2214 <item>if (length(a) != 2) e++;
2216 <item>=\<less\>\\chunkref{pca-test.awk:summary}\<gtr\>
2220 <item>parse_chunk_args("c-like", "${colour}", a, "");
2222 <item>if (a[1] != "${colour}") e++;
2224 <item>if (length(a) != 1) e++;
2226 <item>=\<less\>\\chunkref{pca-test.awk:summary}\<gtr\>
2229 Nested modes are identified by a backslash, a double or single quote,
2230 various bracket styles or a <verbatim|/*> comment.
2232 For each of these sub-modes modes we must also identify at a mode
2233 terminator, and any sub-modes or delimiters that may be entered<\footnote>
2234 Because we are using the sub-mode characters as the mode identifier it
2235 means we can't currently have a mode character dependant on it's context;
2236 i.e. <verbatim|{> can't behave differently when it is inside
2240 <subsection|Backslash>
2242 The backslash mode has no submodes or delimiters, and is terminated by any
2243 character. Note that we are not so much interested in evaluating or
2244 interpolating content as we are in delineating content. It is no matter
2245 that a double backslash (<verbatim|\\\\>) may represent a single backslash
2246 while a backslash-newline may represent white space, but it does matter
2247 that the newline in a backslash newline should not be able to terminate a C
2248 pre-processor statement; and so the newline will be consumed by the
2249 backslash however it is to be interpreted.
2251 <\nf-chunk|common-mode-definitions>
2252 <item>modes[${language}, "\\\\", "terminators"]=".";
2255 <subsection|Strings>
2257 Common languages support two kinds of strings quoting, double quotes and
2260 In a string we have one special mode, which is the backslash. This may
2261 escape an embedded quote and prevent us thinking that it should terminate
2264 <\nf-chunk|mode:common-string>
2265 <item>modes[${language}, ${quote}, "submodes"]="\\\\\\\\";
2266 </nf-chunk||<tuple|language|quote>>
2268 Otherwise, the string will be terminated by the same character that
2271 <\nf-chunk|mode:common-string>
2272 <item>modes[${language}, ${quote}, "terminators"]=${quote};
2273 </nf-chunk||language>
2275 In C type languages, certain escape sequences exist in strings. We need to
2276 define mechanism to enclode any chunks included in this mode using those
2277 escape sequences. These are expressed in two parts, s meaning search, and r
2280 The first substitution is to replace a backslash with a double backslash.
2281 We do this first as other substitutions may introduce a backslash which we
2282 would not then want to escape again here.
2284 Note: Backslashes need double-escaping in the search pattern but not in the
2285 replacement string, hence we are replacing a literal <verbatim|\\> with a
2286 literal <verbatim|\\\\>.
2288 <\nf-chunk|mode:common-string>
2289 <item>escapes[${language}, ${quote}, ++escapes[${language}, ${quote}],
2292 <item>escapes[${language}, ${quote}, \ \ escapes[${language}, ${quote}],
2294 </nf-chunk||language>
2296 If the quote character occurs in the text, it should be preceded by a
2297 backslash, otherwise it would terminate the string unexpectedly.
2299 <\nf-chunk|mode:common-string>
2300 <item>escapes[${language}, ${quote}, ++escapes[${language}, ${quote}],
2303 <item>escapes[${language}, ${quote}, \ \ escapes[${language}, ${quote}],
2304 "r"]="\\\\" ${quote};
2305 </nf-chunk||language>
2307 Any newlines in the string, must be replaced by <verbatim|\\n>.
2309 <\nf-chunk|mode:common-string>
2310 <item>escapes[${language}, ${quote}, ++escapes[${language}, ${quote}],
2313 <item>escapes[${language}, ${quote}, \ \ escapes[${language}, ${quote}],
2315 </nf-chunk||language>
2317 For the common modes, we define this string handling for double and single
2320 <\nf-chunk|common-mode-definitions>
2321 <item>=\<less\>\\chunkref{mode:common-string}(${language},
2322 "\\textbackslash{}"")\<gtr\>
2324 <item>=\<less\>\\chunkref{mode:common-string}(${language}, "'")\<gtr\>
2327 Working strings should pass this test:
2329 <\nf-chunk|test:mode-definitions>
2330 <item>parse_chunk_args("c-like", "say \\"I said, \\\\\\"Hello, how are
2331 you\\\\\\".\\", for me", a, "");
2333 <item>if (a[1] != "say \\"I said, \\\\\\"Hello, how are you\\\\\\".\\"")
2336 <item>if (a[2] != "for me") e++;
2338 <item>if (length(a) != 2) e++;
2340 <item>=\<less\>\\chunkref{pca-test.awk:summary}\<gtr\>
2343 <subsection|Parentheses, Braces and Brackets>
2345 Where quotes are closed by the same character, parentheses, brackets and
2346 braces are closed by an alternate character.
2348 <\nf-chunk|mode:common-brackets>
2349 <item>modes[<nf-arg|language>, <nf-arg|open>, \ "submodes"
2350 ]="\\\\\\\\\|\\"\|{\|\\\\(\|\\\\[\|'\|/\\\\*";
2352 <item>modes[<nf-arg|language>, <nf-arg|open>, \ "delimiters"]=" *, *";
2354 <item>modes[<nf-arg|language>, <nf-arg|open>,
2355 \ "terminators"]=<nf-arg|close>;
2356 </nf-chunk||<tuple|language|open|close>>
2358 Note that the open is NOT a regex but the close token IS. <todo|When we can
2359 quote regex we won't have to put the slashes in here>
2361 <\nf-chunk|common-mode-definitions>
2362 <item>=\<less\>\\chunkref{mode:common-brackets}(${language}, "{",
2365 <item>=\<less\>\\chunkref{mode:common-brackets}(${language}, "[",
2366 "\\textbackslash{}\\textbackslash{}]")\<gtr\>
2368 <item>=\<less\>\\chunkref{mode:common-brackets}(${language}, "(",
2369 "\\textbackslash{}\\textbackslash{})")\<gtr\>
2372 <subsection|Customizing Standard Modes>
2374 <\nf-chunk|mode:add-submode>
2375 <item>modes[${language}, ${mode}, "submodes"] = modes[${language},
2376 ${mode}, "submodes"] "\|" ${submode};
2377 </nf-chunk||<tuple|language|mode|submode>>
2379 <\nf-chunk|mode:add-escapes>
2380 <item>escapes[${language}, ${mode}, ++escapes[${language}, ${mode}],
2383 <item>escapes[${language}, ${mode}, \ \ escapes[${language}, ${mode}],
2385 </nf-chunk||<tuple|language|mode|search|replace>>
2389 <subsection|Comments>
2391 We can define <verbatim|/* comment */> style comments and
2392 <verbatim|//comment> style comments to be added to any language:
2394 <\nf-chunk|mode:multi-line-comments>
2395 <item>=\<less\>\\chunkref{mode:add-submode}(${language}, "",
2396 "/\\textbackslash{}\\textbackslash{}*")\<gtr\>
2398 <item>modes[${language}, "/*", "terminators"]="\\\\*/";
2399 </nf-chunk||<tuple|language>>
2401 <\nf-chunk|mode:single-line-slash-comments>
2402 <item>=\<less\>\\chunkref{mode:add-submode}(${language}, "", "//")\<gtr\>
2404 <item>modes[${language}, "//", "terminators"]="\\n";
2406 <item>=\<less\>\\chunkref{mode:add-escapes}(${language}, "//",
2407 "\\textbackslash{}n", "\\textbackslash{}n//")\<gtr\>
2408 </nf-chunk||language>
2410 We can also define <verbatim|# comment> style comments (as used in awk and
2411 shell scripts) in a similar manner.
2413 <todo|I'm having to use # for hash and \textbackslash{} for \ and have
2414 hacky work-arounds in the parser for now>
2416 <\nf-chunk|mode:add-hash-comments>
2417 <item>=\<less\>\\chunkref{mode:add-submode}(${language}, "",
2420 <item>modes[${language}, "#", "terminators"]="\\n";
2422 <item>=\<less\>\\chunkref{mode:add-escapes}(${language}, "\\#",
2423 "\\textbackslash{}n", "\\textbackslash{}n\\#")\<gtr\>
2424 </nf-chunk||<tuple|language>>
2426 In C, the <verbatim|#> denotes pre-processor directives which can be
2429 <\nf-chunk|mode:add-hash-defines>
2430 <item>=\<less\>\\chunkref{mode:add-submode}(${language}, "",
2433 <item>modes[${language}, "#", "submodes" ]="\\\\\\\\";
2435 <item>modes[${language}, "#", "terminators"]="\\n";
2437 <item>=\<less\>\\chunkref{mode:add-escapes}(${language}, "\\#",
2438 "\\textbackslash{}n", "\\textbackslash{}\\textbackslash{}\\textbackslash{}\\textbackslash{}\\textbackslash{}n")\<gtr\>
2439 </nf-chunk||<tuple|language>>
2441 <\nf-chunk|mode:quote-dollar-escape>
2442 <item>escapes[${language}, ${quote}, ++escapes[${language}, ${quote}],
2445 <item>escapes[${language}, ${quote}, \ \ escapes[${language}, ${quote}],
2447 </nf-chunk||<tuple|language|quote>>
2449 We can add these definitions to various languages
2451 <\nf-chunk|mode-definitions>
2452 <item><nf-ref|common-mode-definitions|<tuple|"c-like">>
2456 <item><nf-ref|common-mode-definitions|<tuple|"c">>
2458 <item>=\<less\>\\chunkref{mode:multi-line-comments}("c")\<gtr\>
2460 <item>=\<less\>\\chunkref{mode:single-line-slash-comments}("c")\<gtr\>
2462 <item>=\<less\>\\chunkref{mode:add-hash-defines}("c")\<gtr\>
2466 <item>=\<less\>\\chunkref{common-mode-definitions}("awk")\<gtr\>
2468 <item>=\<less\>\\chunkref{mode:add-hash-comments}("awk")\<gtr\>
2470 <item>=\<less\>\\chunkref{mode:add-naked-regex}("awk")\<gtr\>
2473 The awk definitions should allow a comment block like this:
2475 <nf-chunk|test:comment-quote|<item># Comment:
2476 =\<less\>\\chunkref{test:comment-text}\<gtr\>|awk|>
2478 <\nf-chunk|test:comment-text>
2479 <item>Now is the time for
2481 <item>the quick brown fox to bring lemonade
2486 to come out like this:
2488 <\nf-chunk|test:comment-quote:result>
2489 <item># Comment: Now is the time for
2491 <item>#the quick brown fox to bring lemonade
2496 The C definition for such a block should have it come out like this:
2498 <\nf-chunk|test:comment-quote:C-result>
2499 <item># Comment: Now is the time for\\
2501 <item>the quick brown fox to bring lemonade\\
2508 This pattern is incomplete, but meant to detect naked regular expressions
2509 in awk and perl; e.g. <verbatim|/.*$/>, however required capabilities are
2512 Current it only detects regexes anchored with ^ as used in fangle.
2514 For full regex support, modes need to be named not after their starting
2515 character, but some other more fully qualified name.
2517 <\nf-chunk|mode:add-naked-regex>
2518 <item>=\<less\>\\chunkref{mode:add-submode}(${language}, "",
2519 "/\\textbackslash{}\\textbackslash{}\\^")\<gtr\>
2521 <item>modes[${language}, "/^", "terminators"]="/";
2522 </nf-chunk||<tuple|language>>
2526 <\nf-chunk|mode-definitions>
2527 <item>=\<less\>\\chunkref{common-mode-definitions}("perl")\<gtr\>
2529 <item>=\<less\>\\chunkref{mode:multi-line-comments}("perl")\<gtr\>
2531 <item>=\<less\>\\chunkref{mode:add-hash-comments}("perl")\<gtr\>
2534 Still need to add add <verbatim|s/>, submode <verbatim|/>, terminate both
2535 with <verbatim|//>. This is likely to be impossible as perl regexes can
2540 <\nf-chunk|mode-definitions>
2541 <item>=\<less\>\\chunkref{common-mode-definitions}("sh")\<gtr\>
2543 <item>#\<less\>\\chunkref{mode:common-string}("sh",
2544 "\\textbackslash{}"")\<gtr\>
2546 <item>#\<less\>\\chunkref{mode:common-string}("sh", "'")\<gtr\>
2548 <item>=\<less\>\\chunkref{mode:add-hash-comments}("sh")\<gtr\>
2550 <item>=\<less\>\\chunkref{mode:quote-dollar-escape}("sh", "\\"")\<gtr\>
2553 <section|Some tests>
2555 Also, the parser must return any spare text at the end that has not been
2556 processed due to a mode terminator being found.
2558 <\nf-chunk|test:mode-definitions>
2559 <item>rest = parse_chunk_args("c-like", "1, 2, 3) spare", a, "(");
2561 <item>if (a[1] != 1) e++;
2563 <item>if (a[2] != 2) e++;
2565 <item>if (a[3] != 3) e++;
2567 <item>if (length(a) != 3) e++;
2569 <item>if (rest != " spare") e++;
2571 <item>=\<less\>\\chunkref{pca-test.awk:summary}\<gtr\>
2574 We must also be able to parse the example given earlier.
2576 <\nf-chunk|test:mode-definitions>
2577 <item>parse_chunk_args("c-like", "things[x, y], get_other_things(a,
2578 \\"(all)\\"), 99", a, "(");
2580 <item>if (a[1] != "things[x, y]") e++;
2582 <item>if (a[2] != "get_other_things(a, \\"(all)\\")") e++;
2584 <item>if (a[3] != "99") e++;
2586 <item>if (length(a) != 3) e++;
2588 <item>=\<less\>\\chunkref{pca-test.awk:summary}\<gtr\>
2591 <section|A non-recursive mode tracker>
2593 <subsection|Constructor>
2595 The mode tracker holds its state in a stack based on a numerically indexed
2596 hash. This function, when passed an empty hash, will intialize it.
2598 <\nf-chunk|new_mode_tracker()>
2599 <item>function new_mode_tracker(context, language, mode) {
2601 <item> \ context[""] = 0;
2603 <item> \ context[0, "language"] = language;
2605 <item> \ context[0, "mode"] = mode;
2610 Because awk functions cannot return an array, we must create the array
2611 first and pass it in, so we have a fangle macro to do this:
2613 <\nf-chunk|new-mode-tracker>
2614 <item><nf-ref|awk-delete-array|<tuple|context>>
2616 <item>new_mode_tracker(${context}, ${language}, ${mode});
2617 </nf-chunk|awk|<tuple|context|language|mode>>
2619 <subsection|Management>
2621 And for tracking modes, we dispatch to a mode-tracker action based on the
2624 <\nf-chunk|mode_tracker>
2625 <item>function push_mode_tracker(context, language, mode,
2627 <item> \ # local vars
2633 <item> \ if (! ("" in context)) {
2635 <item> \ \ \ <nf-ref|new-mode-tracker|<tuple|context|language|mode>>
2639 <item> \ \ \ top = context[""];
2641 <item> \ \ \ if (context[top, "language"] == language && mode=="") mode =
2642 context[top, "mode"];
2646 <item> \ \ \ context[top, "language"] = language;
2648 <item> \ \ \ context[top, "mode"] = mode;
2650 <item> \ \ \ context[""] = top;
2657 <\nf-chunk|mode_tracker>
2658 <item>function dump_mode_tracker(context, \
2664 <item> \ for(c=0; c \<less\>= context[""]; c++) {
2666 <item> \ \ \ printf(" %2d \ \ %s:%s\\n", c, context[c, "language"],
2667 context[c, "mode"]) \<gtr\> "/dev/stderr";
2669 <item> \ \ \ for(d=1; ( (c, "values", d) in context); d++) {
2671 <item> \ \ \ \ \ printf(" \ \ %2d %s\\n", d, context[c, "values", d])
2672 \<gtr\> "/dev/stderr";
2681 <\nf-chunk|mode_tracker>
2682 <item>function finalize_mode_tracker(context)
2686 <item> \ if ( ("" in context) && context[""] != 0) return 0;
2693 This implies that any chunk must be syntactically whole; for instance, this
2696 <\nf-chunk|test:whole-chunk>
2699 <item> \ =\<less\>\\chunkref{test:say-hello}\<gtr\>
2704 <\nf-chunk|test:say-hello>
2705 <item>print "hello";
2708 But this is not fine; the chunk <nf-ref|test:hidden-else|> is not properly
2711 <\nf-chunk|test:partial-chunk>
2714 <item> \ =\<less\>\\chunkref{test:hidden-else}\<gtr\>
2719 <\nf-chunk|test:hidden-else>
2720 <item> \ print "I'm fine";
2724 <item> \ print "I'm not";
2727 These tests will check for correct behaviour:
2729 <\nf-chunk|test:cromulence>
2730 <item>echo Cromulence test
2732 <item>passtest $FANGLE -Rtest:whole-chunk $TEX_SRC &\<gtr\>/dev/null \|\|
2733 ( echo "Whole chunk failed" && exit 1 )
2735 <item>failtest $FANGLE -Rtest:partial-chunk $TEX_SRC &\<gtr\>/dev/null
2736 \|\| ( echo "Partial chunk failed" && exit 1 )
2739 <subsection|Tracker>
2741 We must avoid recursion as a language construct because we intend to employ
2742 mode-tracking to track language mode of emitted code, and the code is
2743 emitted from a function which is itself recursive, so instead we implement
2744 psuedo-recursion using our own stack based on a hash.
2746 <\nf-chunk|mode_tracker()>
2747 <item>function mode_tracker(context, text, values,\
2749 <item> \ # optional parameters
2751 <item> \ # local vars
2753 <item> \ mode, submodes, language,
2755 <item> \ cindex, c, a, part, item, name, result, new_values, new_mode,\
2757 <item> \ delimiters, terminators)
2762 We could be re-commencing with a valid context, so we need to setup the
2763 state according to the last context.
2765 <\nf-chunk|mode_tracker()>
2766 <item> \ cindex = context[""] + 0;
2768 <item> \ mode = context[cindex, "mode"];
2770 <item> \ language = context[cindex, "language" ];
2773 First we construct a single large regex combining the possible sub-modes
2774 for the current mode along with the terminators for the current mode.
2776 <\nf-chunk|parse_chunk_args-reset-modes>
2777 <item> \ submodes=modes[language, mode, "submodes"];
2781 <item> \ if ((language, mode, "delimiters") in modes) {
2783 <item> \ \ \ delimiters = modes[language, mode, "delimiters"];
2785 <item> \ \ \ if (length(submodes)\<gtr\>0) submodes = submodes "\|";
2787 <item> \ \ \ submodes=submodes delimiters;
2789 <item> \ } else delimiters="";
2791 <item> \ if ((language, mode, "terminators") in modes) {
2793 <item> \ \ \ terminators = modes[language, mode, "terminators"];
2795 <item> \ \ \ if (length(submodes)\<gtr\>0) submodes = submodes "\|";
2797 <item> \ \ \ submodes=submodes terminators;
2799 <item> \ } else terminators="";
2802 If we don't find anything to match on --- probably because the language is
2803 not supported --- then we return the entire text without matching anything.
2805 <\nf-chunk|parse_chunk_args-reset-modes>
2806 <item> if (! length(submodes)) return text;
2809 <\nf-chunk|mode_tracker()>
2810 <item>=\<less\>\\chunkref{parse_chunk_args-reset-modes}\<gtr\>
2813 We then iterate the text (until there is none left) looking for sub-modes
2814 or terminators in the regex.
2816 <\nf-chunk|mode_tracker()>
2817 <item> \ while((cindex \<gtr\>= 0) && length(text)) {
2819 <item> \ \ \ if (match(text, "(" submodes ")", a)) {
2822 A bug that creeps in regularly during development is bad regexes of zero
2823 length which result in an infinite loop (as no text is consumed), so I
2824 catch that right away with this test.
2826 <\nf-chunk|mode_tracker()>
2827 <item> \ \ \ \ \ if (RLENGTH\<less\>1) {
2829 <item> \ \ \ \ \ \ \ error(sprintf("Internal error, matched zero length
2830 submode, should be impossible - likely regex computation error\\n" \\
2832 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "Language=%s\\nmode=%s\\nmatch=%s\\n",
2833 language, mode, submodes));
2838 part is defined as the text up to the sub-mode or terminator, and this is
2839 appended to item --- which is the current text being gathered. If a mode
2840 has a delimiter, then item is reset each time a delimiter is found.
2842 <math|<wide|<with|mode|prog|"><wide*|hello|\<wide-underbrace\>><rsub|item>,
2843 <wide*|there|\<wide-underbrace\>><rsub|item><with|mode|prog|">|\<wide-overbrace\>><rsup|item>,
2844 \ <wide|he said.|\<wide-overbrace\>><rsup|item>>
2846 <\nf-chunk|mode_tracker()>
2847 <item> \ \ \ \ \ part = substr(text, 1, RSTART -1);
2849 <item> \ \ \ \ \ item = item part;
2852 We must now determine what was matched. If it was a terminator, then we
2853 must restore the previous mode.
2855 <\nf-chunk|mode_tracker()>
2856 <item> \ \ \ \ \ if (match(a[1], "^" terminators "$")) {
2858 <item>#printf("%2d EXIT \ MODE [%s] by [%s] [%s]\\n", cindex, mode, a[1],
2859 text) \<gtr\> "/dev/stderr"
2861 <item> \ \ \ \ \ \ \ context[cindex, "values", ++context[cindex,
2864 <item> \ \ \ \ \ \ \ delete context[cindex];
2866 <item> \ \ \ \ \ \ \ context[""] = --cindex;
2868 <item> \ \ \ \ \ \ \ if (cindex\<gtr\>=0) {
2870 <item> \ \ \ \ \ \ \ \ \ mode = context[cindex, "mode"];
2872 <item> \ \ \ \ \ \ \ \ \ language = context[cindex, "language"];
2874 <item> \ \ \ \ \ \ \ \ \ =\<less\>\\chunkref{parse_chunk_args-reset-modes}\<gtr\>
2876 <item> \ \ \ \ \ \ \ }
2878 <item> \ \ \ \ \ \ \ item = item a[1];
2880 <item> \ \ \ \ \ \ \ text = substr(text, 1 + length(part) +
2886 If a delimiter was matched, then we must store the current item in the
2887 parsed values array, and reset the item.
2889 <\nf-chunk|mode_tracker()>
2890 <item> \ \ \ \ \ else if (match(a[1], "^" delimiters "$")) {
2892 <item> \ \ \ \ \ \ \ if (cindex==0) {
2894 <item> \ \ \ \ \ \ \ \ \ context[cindex, "values", ++context[cindex,
2897 <item> \ \ \ \ \ \ \ \ \ item = "";
2899 <item> \ \ \ \ \ \ \ } else {
2901 <item> \ \ \ \ \ \ \ \ \ item = item a[1];
2903 <item> \ \ \ \ \ \ \ }
2905 <item> \ \ \ \ \ \ \ text = substr(text, 1 + length(part) +
2911 otherwise, if a new submode is detected (all submodes have terminators), we
2912 must create a nested parse context until we find the terminator for this
2915 <\nf-chunk|mode_tracker()>
2916 <item> else if ((language, a[1], "terminators") in modes) {
2918 <item> \ \ \ \ \ \ \ #check if new_mode is defined
2920 <item> \ \ \ \ \ \ \ item = item a[1];
2922 <item>#printf("%2d ENTER MODE [%s] in [%s]\\n", cindex, a[1], text)
2923 \<gtr\> "/dev/stderr"
2925 <item> \ \ \ \ \ \ \ text = substr(text, 1 + length(part) +
2928 <item> \ \ \ \ \ \ \ context[""] = ++cindex;
2930 <item> \ \ \ \ \ \ \ context[cindex, "mode"] = a[1];
2932 <item> \ \ \ \ \ \ \ context[cindex, "language"] = language;
2934 <item> \ \ \ \ \ \ \ mode = a[1];
2936 <item> \ \ \ \ \ \ \ =\<less\>\\chunkref{parse_chunk_args-reset-modes}\<gtr\>
2938 <item> \ \ \ \ \ } else {
2940 <item> \ \ \ \ \ \ \ error(sprintf("Submode '%s' set unknown mode in
2941 text: %s\\nLanguage %s Mode %s\\n", a[1], text, language, mode));
2943 <item> \ \ \ \ \ \ \ text = substr(text, 1 + length(part) +
2951 In the final case, we parsed to the end of the string. If the string was
2952 entire, then we should have no nested mode context, but if the string was
2953 just a fragment we may have a mode context which must be preserved for the
2954 next fragment. Todo: Consideration ought to be given if sub-mode strings
2955 are split over two fragments.
2957 <\nf-chunk|mode_tracker()>
2960 <item> \ \ \ \ \ context[cindex, "values", ++context[cindex, "values"]] =
2963 <item> \ \ \ \ \ text = "";
2965 <item> \ \ \ \ \ item = "";
2973 <item> \ context["item"] = item;
2977 <item> \ if (length(item)) context[cindex, "values", ++context[cindex,
2980 <item> \ return text;
2985 <subsubsection|One happy chunk>
2987 All the mode tracker chunks are referred to here:
2989 <\nf-chunk|mode-tracker>
2990 <item><nf-ref|new_mode_tracker()|>
2992 <item><nf-ref|mode_tracker()|>
2995 <subsubsection|Tests>
2997 We can test this function like this:
2999 <\nf-chunk|pca-test.awk>
3000 <item>=\<less\>\\chunkref{error()}\<gtr\>
3002 <item>=\<less\>\\chunkref{mode-tracker}\<gtr\>
3004 <item>=\<less\>\\chunkref{parse_chunk_args()}\<gtr\>
3008 <item> \ SUBSEP=".";
3010 <item> \ =\<less\>\\chunkref{mode-definitions}\<gtr\>
3014 <item> \ =\<less\>\\chunkref{test:mode-definitions}\<gtr\>
3019 <\nf-chunk|pca-test.awk:summary>
3022 <item> \ printf "Failed " e
3024 <item> \ for (b in a) {
3026 <item> \ \ \ print "a[" b "] =\<gtr\> " a[b];
3032 <item> \ print "Passed"
3041 which should give this output:
3043 <\nf-chunk|pca-test.awk-results>
3044 <item>a[foo.quux.quirk] =\<gtr\>\
3046 <item>a[foo.quux.a] =\<gtr\> fleeg
3048 <item>a[foo.bar] =\<gtr\> baz
3050 <item>a[etc] =\<gtr\>\
3052 <item>a[name] =\<gtr\> freddie
3055 <section|Escaping and Quoting>
3057 For the time being and to get around <TeXmacs> inability to export a
3058 <kbd|TAB> character, the right arrow whose UTF-8 sequence is ...
3062 Another special character is used, the left-arrow with UTF-8 sequence 0xE2
3063 0x86 0xA4 is used to strip any preceding white space as a way of un-tabbing
3064 and removing indent that has been applied <emdash> this is important for
3065 bash here documents, and the like. It's a filthy hack.
3067 <todo|remove the hack>
3069 <\nf-chunk|mode_tracker>
3072 <item>function untab(text) {
3074 <item> \ gsub("[[:space:]]*\\xE2\\x86\\xA4","", text);
3076 <item> \ return text;
3081 Each nested mode can optionally define a set of transforms to be applied to
3082 any text that is included from another language.
3084 This code can perform transforms
3086 <\nf-chunk|mode_tracker>
3087 <item>function transform_escape(s, r, text,
3089 <item> \ \ \ # optional
3093 <item> \ \ \ \ \ \ \ # local vars
3095 <item> \ \ \ \ \ \ \ c)
3099 <item> \ for(c=1; c \<less\>= max && (c in s); c++) {
3101 <item> \ \ \ gsub(s[c], r[c], text);
3105 <item> \ return text;
3110 This function must append from index c onwards, and escape transforms from
3111 the supplied context, and return c + number of new transforms.
3113 <\nf-chunk|mode_tracker>
3114 <item>function mode_escaper(context, s, r, src,
3116 <item> \ c, cp, cpl)
3120 <item> \ for(c = context[""]; c \<gtr\>= 0; c--) {
3122 <item> \ \ \ if ( (context[c, "language"], context[c, "mode"]) in
3125 <item> \ \ \ \ \ cpl = escapes[context[c, "language"], context[c,
3128 <item> \ \ \ \ \ for (cp = 1; cp \<less\>= cpl; cp ++) {
3130 <item> \ \ \ \ \ \ \ ++src;
3132 <item> \ \ \ \ \ \ \ s[src] = escapes[context[c, "language"], context[c,
3135 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ r[src]
3136 = escapes[context[c, "language"], context[c, "mode"], cp, "r"];
3144 <item> \ return src;
3148 <item>function dump_escaper(c, s, r, cc) {
3150 <item> \ for(cc=1; cc\<less\>=c; cc++) {
3152 <item> \ \ \ printf("%2d s[%s] r[%s]\\n", cc, s[cc], r[cc]) \<gtr\>
3160 <\nf-chunk|test:escapes>
3161 <item>echo escapes test
3163 <item>passtest $FANGLE -Rtest:comment-quote $TEX_SRC &\<gtr\>/dev/null
3164 \|\| ( echo "Comment-quote failed" && exit 1 )
3167 <chapter|Recognizing Chunks>
3169 Fangle recognizes noweb chunks, but as we also want better <LaTeX>
3170 integration we will recognize any of these:
3173 <item>notangle chunks matching the pattern
3174 <verbatim|^\<less\>\<less\>.*?\<gtr\>\<gtr\>=>
3176 <item>chunks beginning with <verbatim|\\begin{lstlistings}>, possibly
3177 with <verbatim|\\Chunk{...}> on the previous line
3179 <item>an older form I have used, beginning with
3180 <verbatim|\\begin{Chunk}[options]> --- also more suitable for plain
3181 <LaTeX> users<\footnote>
3182 Is there such a thing as plain <LaTeX>?
3186 <section|Chunk start>
3188 The variable chunking is used to signify that we are processing a code
3189 chunk and not document. In such a state, input lines will be assigned to
3190 the current chunk; otherwise they are ignored.
3192 <subsection|<TeXmacs> hackery>
3194 We don't handle <TeXmacs> files natively but instead emit unicode character
3195 sequences to mark up the text-export file which we work on.
3197 These hacks detect such sequences and retro-fit in the old <TeX> parsing.
3199 <\nf-chunk|recognize-chunk>
3204 <item># \ gsub("\\n*$","");
3206 <item># \ gsub("\\n", " ");
3212 <item>/\\xE2\\x86\\xA6/ {
3214 <item> \ gsub("\\\\xE2\\\\x86\\\\xA6", "\\x09");
3220 <item>/\\xE2\\x80\\x98/ {
3222 <item> \ gsub("\\\\xE2\\\\x80\\\\x98", "`");
3228 <item>/\\xE2\\x89\\xA1/ {
3230 <item> \ if (match($0, "^ *([^[ ]* \|)\<less\>([^[
3231 ]*)\\\\[[0-9]*\\\\][(](.*)[)].*, lang=([^ ]*)", line)) {
3233 <item> \ \ \ next_chunk_name=line[2];
3235 <item> \ \ \ gsub(",",";",line[3]);
3237 <item> \ \ \ params="params=" line[3];
3239 <item> \ \ \ if ((line[4])) {
3241 <item> \ \ \ \ \ params = params ",language=" line[4]
3245 <item> \ \ \ get_chunk_args(params, next_chunk_args);
3247 <item> \ \ \ new_chunk(next_chunk_name, next_chunk_args);
3249 <item> \ \ \ texmacs_chunking = 1;
3253 <item>#print "Unexpected\
3268 <subsection|lstlistings>
3270 Our current scheme is to recognize the new lstlisting chunks, but these may
3271 be preceded by a <verbatim|\\Chunk> command which in <LyX> is a more
3272 convenient way to pass the chunk name to the
3273 <verbatim|\\begin{lstlistings}> command, and a more visible way to specify
3274 other <verbatim|lstset> settings.
3276 The arguments to the <verbatim|\\Chunk> command are a name, and then a
3277 comma-seperated list of key-value pairs after the manner of
3278 <verbatim|\\lstset>. (In fact within the <LaTeX> <verbatim|\\Chunk> macro
3279 (section <reference|sub:The-chunk-command>) the text <verbatim|name=> is
3280 prefixed to the argument which is then literally passed to
3281 <verbatim|\\lstset>).
3283 <\nf-chunk|recognize-chunk>
3284 <item>/^\\\\Chunk{/ {
3286 <item> \ if (match($0, "^\\\\\\\\Chunk{ *([^ ,}]*),?(.*)}", line)) {
3288 <item> \ \ \ next_chunk_name = line[1];
3290 <item> \ \ \ get_chunk_args(line[2], next_chunk_args);
3299 We also make a basic attempt to parse the name out of the
3300 <verbatim|\\lstlistings[name=chunk-name]> text, otherwise we fall back to
3301 the name found in the previous chunk command. This attempt is very basic
3302 and doesn't support commas or spaces or square brackets as part of the
3303 chunkname. We also recognize <verbatim|\\begin{Chunk}> which is convenient
3304 for some users<\footnote>
3305 but not yet supported in the <LaTeX> macros
3308 <\nf-chunk|recognize-chunk>
3309 <item>/^\\\\begin{lstlisting}\|^\\\\begin{Chunk}/ {
3311 <item> \ if (match($0, "}.*[[,] *name= *{? *([^], }]*)", line)) {
3313 <item> \ \ \ new_chunk(line[1]);
3317 <item> \ \ \ new_chunk(next_chunk_name, next_chunk_args);
3321 <item> \ chunking=1;
3328 <subsection|<TeXmacs>>
3332 <\nf-chunk|recognize-chunk>
3335 <item>/^ *\\\|____________*/ && texmacs_chunking {
3337 <item> \ active_chunk="";
3339 <item> \ texmacs_chunking=0;
3341 <item> \ chunking=0;
3345 <item>/^ *\\\|\\/\\\\/ && texmacs_chunking {
3347 <item> \ texmacs_chunking=0;
3349 <item> \ chunking=0;
3351 <item> \ active_chunk="";
3355 <item>texmacs_chunk=0;
3357 <item>/^ *[1-9][0-9]* *\\\| / {
3359 <item> \ if (texmacs_chunking) {
3361 <item> \ \ \ chunking=1;
3363 <item> \ \ \ texmacs_chunk=1;
3365 <item> \ \ \ gsub("^ *[1-9][0-9]* *\\\\\| ", "")
3371 <item>/^ *\\.\\/\\\\/ && texmacs_chunking {
3377 <item>/^ *__*$/ && texmacs_chunking {
3385 <item>texmacs_chunking {
3387 <item> \ if (! texmacs_chunk) {
3389 <item> \ \ \ # must be a texmacs continued line
3391 <item> \ \ \ chunking=1;
3393 <item> \ \ \ texmacs_chunk=1;
3399 <item>! texmacs_chunk {
3401 <item># \ texmacs_chunking=0;
3403 <item> \ chunking=0;
3414 We recognize notangle style chunks too:
3416 <\nf-chunk|recognize-chunk>
3417 <item>/^[\<less\>]\<less\>.*[\<gtr\>]\<gtr\>=/ {
3419 <item> \ if (match($0, "^[\<less\>]\<less\>(.*)[\<gtr\>]\<gtr\>= *$",
3422 <item> \ \ \ chunking=1;
3424 <item> \ \ \ notangle_mode=1;
3426 <item> \ \ \ new_chunk(line[1]);
3437 Likewise, we need to recognize when a chunk ends.
3439 <subsection|lstlistings>
3441 The <verbatim|e> in <verbatim|[e]nd{lislisting}> is surrounded by square
3442 brackets so that when this document is processed, this chunk doesn't
3443 terminate early when the lstlistings package recognizes it's own
3444 end-string!<\footnote>
3445 This doesn't make sense as the regex is anchored with ^, which this line
3446 does not begin with!
3449 <\nf-chunk|recognize-chunk>
3450 <item>/^\\\\[e]nd{lstlisting}\|^\\\\[e]nd{Chunk}/ {
3452 <item> \ chunking=0;
3454 <item> \ active_chunk="";
3463 <\nf-chunk|recognize-chunk>
3466 <item> \ chunking=0;
3468 <item> \ active_chunk="";
3473 All other recognizers are only of effect if we are chunking; there's no
3474 point in looking at lines if they aren't part of a chunk, so we just ignore
3475 them as efficiently as we can.
3477 <\nf-chunk|recognize-chunk>
3478 <item>! chunking { next; }
3481 <section|Chunk contents>
3483 Chunk contents are any lines read while <verbatim|chunking> is true. Some
3484 chunk contents are special in that they refer to other chunks, and will be
3485 replaced by the contents of these chunks when the file is generated.
3487 <label|sub:ORS-chunk-text>We add the output record separator <verbatim|ORS>
3488 to the line now, because we will set <verbatim|ORS> to the empty string
3489 when we generate the output<\footnote>
3490 So that we can partial print lines using <verbatim|print> instead of
3491 <verbatim|printf>. <todo|This does't make sense>
3494 <\nf-chunk|recognize-chunk>
3495 <item>length(active_chunk) {
3497 <item> \ =\<less\>\\chunkref{process-chunk-tabs}\<gtr\>
3499 <item> \ =\<less\>\\chunkref{process-chunk}\<gtr\>
3504 If a chunk just consisted of plain text, we could handle the chunk like
3507 <\nf-chunk|process-chunk-simple>
3508 <item>chunk_line(active_chunk, $0 ORS);
3511 but in fact a chunk can include references to other chunks. Chunk includes
3512 are traditionally written as <verbatim|\<less\>\<less\>chunk-name\<gtr\>\<gtr\>>
3513 but we support other variations, some of which are more suitable for
3514 particular editing systems.
3516 However, we also process tabs at this point, a tab at input can be replaced
3517 by a number of spaces defined by the <verbatim|tabs> variable, set by the
3518 <verbatim|-T> option. Of course this is poor tab behaviour, we should
3519 probably have the option to use proper counted tab-stops and process this
3522 <\nf-chunk|process-chunk-tabs>
3523 <item>if (length(tabs)) {
3525 <item> \ gsub("\\t", tabs);
3530 <subsection|lstlistings><label|sub:lst-listings-includes>
3532 If <verbatim|\\lstset{escapeinside={=\<less\>}{\<gtr\>}}> is set, then we
3533 can use <verbatim|=\<less\>\\chunkref{chunk-name}\<gtr\>> in listings. The
3534 sequence <verbatim|=\<less\>> was chosen because:
3537 <item>it is a better mnemonic than <verbatim|\<less\>\<less\>chunk-name\<gtr\>\<gtr\>>
3538 in that the <verbatim|=> sign signifies equivalence or substitutability.
3540 <item>and because <verbatim|=\<less\>> is not valid in C or any language
3543 <item>and also because lstlistings doesn't like <verbatim|\<gtr\>\<gtr\>>
3544 as an end delimiter for the <em|texcl> escape, so we must make do with a
3545 single <verbatim|\<gtr\>> which is better complemented by
3546 <verbatim|=\<less\>> than by <verbatim|\<less\>\<less\>>.
3549 Unfortunately the <verbatim|=\<less\>...\<gtr\>> that we use re-enters a
3550 <LaTeX> parsing mode in which some characters are special, e.g. <verbatim|#
3551 \\> and so these cause trouble if used in arguments to
3552 <verbatim|\\chunkref>. At some point I must fix the <LaTeX> command
3553 <verbatim|\\chunkref> so that it can accept these literally, but until
3554 then, when writing chunkref argumemts that need these characters, I must
3555 use the forms <verbatim|\\textbackslash{}> and <verbatim|\\#>; so I also
3556 define a hacky chunk <verbatim|delatex> to be used further on whose purpose
3557 it is to remove these from any arguments parsed by fangle.
3562 <item>gsub("\\\\\\\\#", "#", ${text});
3564 <item>gsub("\\\\\\\\textbackslash{}", "\\\\", ${text});
3566 <item>gsub("\\\\\\\\\\\\^", "^", ${text});
3567 </nf-chunk||<tuple|text>>
3569 As each chunk line may contain more than one chunk include, we will split
3570 out chunk includes in an iterative fashion<\footnote>
3571 Contrary to our use of split when substituting parameters in chapter
3572 <reference|Here-we-split>
3575 First, as long as the chunk contains a <verbatim|\\chunkref> command we
3576 take as much as we can up to the first <verbatim|\\chunkref> command.
3578 <\nf-chunk|process-chunk>
3583 <item>while(match(chunk,"(\\xC2\\xAB)([^\\xC2]*) [^\\xC2]*\\xC2\\xBB",
3586 <item> \ \ \ \ \ match(chunk,\
3588 <item> \ \ \ \ \ \ \ \ \ \ \ "([=]\<less\>\\\\\\\\chunkref{([^}\<gtr\>]*)}(\\\\(.*\\\\)\|)\<gtr\>\|\<less\>\<less\>([a-zA-Z_][-a-zA-Z0-9_]*)\<gtr\>\<gtr\>)",\
3590 <item> \ \ \ \ \ \ \ \ \ \ \ line)\\
3594 <item> \ chunklet = substr(chunk, 1, RSTART - 1);
3597 We keep track of the indent count, by counting the number of literal
3598 characters found. We can then preserve this indent on each output line when
3599 multi-line chunks are expanded.
3601 We then process this first part literal text, and set the chunk which is
3602 still to be processed to be the text after the <verbatim|\\chunkref>
3603 command, which we will process next as we continue around the loop.
3605 <\nf-chunk|process-chunk>
3606 <item> \ indent += length(chunklet);
3608 <item> \ chunk_line(active_chunk, chunklet);
3610 <item> \ chunk = substr(chunk, RSTART + RLENGTH);
3613 We then consider the type of chunk command we have found, whether it is the
3614 fangle style command beginning with <verbatim|=\<less\>> the older notangle
3615 style beginning with <verbatim|\<less\>\<less\>>.
3617 Fangle chunks may have parameters contained within square brackets. These
3618 will be matched in <verbatim|line[3]> and are considered at this stage of
3619 processing to be part of the name of the chunk to be included.
3621 <\nf-chunk|process-chunk>
3622 <item> \ if (substr(line[1], 1, 1) == "=") {
3624 <item> \ \ \ # chunk name up to }
3626 <item> \ \ \ \ \ \ \ =\<less\>\\chunkref{delatex}(line[3])\<gtr\>
3628 <item> \ \ \ chunk_include(active_chunk, line[2] line[3], indent);
3630 <item> \ } else if (substr(line[1], 1, 1) == "\<less\>") {
3632 <item> \ \ \ chunk_include(active_chunk, line[4], indent);
3634 <item> \ } else if (line[1] == "\\xC2\\xAB") {
3636 <item> \ \ \ chunk_include(active_chunk, line[2], indent);
3640 <item> \ \ \ error("Unknown chunk fragment: " line[1]);
3647 The loop will continue until there are no more chunkref statements in the
3648 text, at which point we process the final part of the chunk.
3650 <\nf-chunk|process-chunk>
3653 <item>chunk_line(active_chunk, chunk);
3656 <label|lone-newline>We add the newline character as a chunklet on it's own,
3657 to make it easier to detect new lines and thus manage indentation when
3658 processing the output.
3660 <\nf-chunk|process-chunk>
3661 <item>chunk_line(active_chunk, "\\n");
3666 We will also permit a chunk-part number to follow in square brackets, so
3667 that <verbatim|=\<less\>\\chunkref{chunk-name[1]}\<gtr\>> will refer to the
3668 first part only. This can make it easy to include a C function prototype in
3669 a header file, if the first part of the chunk is just the function
3670 prototype without the trailing semi-colon. The header file would include
3671 the prototype with the trailing semi-colon, like this:
3673 <verbatim|=\<less\>\\chunkref{chunk-name[1]}\<gtr\>>
3675 This is handled in section <reference|sub:Chunk-parts>
3677 We should perhaps introduce a notion of language specific chunk options; so
3678 that perhaps we could specify:
3680 <verbatim|=\<less\>\\chunkref{chunk-name[function-declaration]}>
3682 which applies a transform <verbatim|function-declaration> to the chunk ---
3683 which in this case would extract a function prototype from a function.
3686 <chapter|Processing Options>
3688 At the start, first we set the default options.
3690 <\nf-chunk|default-options>
3695 <item>notangle_mode=0;
3702 Then we use getopt the standard way, and null out ARGV afterwards in the
3705 <\nf-chunk|read-options>
3706 <item>Optind = 1 \ \ \ # skip ARGV[0]
3708 <item>while(getopt(ARGC, ARGV, "R:LdT:hr")!=-1) {
3710 <item> \ =\<less\>\\chunkref{handle-options}\<gtr\>
3714 <item>for (i=1; i\<less\>Optind; i++) { ARGV[i]=""; }
3717 This is how we handle our options:
3719 <\nf-chunk|handle-options>
3720 <item>if (Optopt == "R") root = Optarg;
3722 <item>else if (Optopt == "r") root="";
3724 <item>else if (Optopt == "L") linenos = 1;
3726 <item>else if (Optopt == "d") debug = 1;
3728 <item>else if (Optopt == "T") tabs = indent_string(Optarg+0);
3730 <item>else if (Optopt == "h") help();
3732 <item>else if (Optopt == "?") help();
3735 We do all of this at the beginning of the program
3740 <item> \ =\<less\>\\chunkref{constants}\<gtr\>
3742 <item> \ =\<less\>\\chunkref{mode-definitions}\<gtr\>
3744 <item> \ =\<less\>\\chunkref{default-options}\<gtr\>
3748 <item> \ =\<less\>\\chunkref{read-options}\<gtr\>
3753 And have a simple help function
3756 <item>function help() {
3758 <item> \ print "Usage:"
3760 <item> \ print " \ fangle [-L] -R\<less\>rootname\<gtr\> [source.tex
3763 <item> \ print " \ fangle -r [source.tex ...]"
3765 <item> \ print " \ If the filename, source.tex is not specified then
3770 <item> \ print "-L causes the C statement: #line \<less\>lineno\<gtr\>
3771 \\"filename\\"" to be issued"
3773 <item> \ print "-R causes the named root to be written to stdout"
3775 <item> \ print "-r lists all roots in the file (even those used
3783 <chapter|Generating the Output>
3785 We generate output by calling output_chunk, or listing the chunk names.
3787 <\nf-chunk|generate-output>
3788 <item>if (length(root)) output_chunk(root);
3790 <item>else output_chunk_names();
3793 We also have some other output debugging:
3795 <\nf-chunk|debug-output>
3798 <item> \ print "------ chunk names "
3800 <item> \ output_chunk_names();
3802 <item> \ print "====== chunks"
3804 <item> \ output_chunks();
3806 <item> \ print "++++++ debug"
3808 <item> \ for (a in chunks) {
3810 <item> \ \ \ print a "=" chunks[a];
3817 We do both of these at the end. We also set <verbatim|ORS=""> because each
3818 chunklet is not necessarily a complete line, and we already added
3819 <verbatim|ORS> to each input line in section
3820 <reference|sub:ORS-chunk-text>.
3825 <item> \ =\<less\>\\chunkref{debug-output}\<gtr\>
3829 <item> \ =\<less\>\\chunkref{generate-output}\<gtr\>
3834 We write chunk names like this. If we seem to be running in notangle
3835 compatibility mode, then we enclose the name like this
3836 <verbatim|\<less\>\<less\>name\<gtr\>\<gtr\>> the same way notangle does:
3838 <\nf-chunk|output_chunk_names()>
3839 <item>function output_chunk_names( \ \ c, prefix, suffix)\
3843 <item> \ if (notangle_mode) {
3845 <item> \ \ \ prefix="\<less\>\<less\>";
3847 <item> \ \ \ suffix="\<gtr\>\<gtr\>";
3851 <item> \ for (c in chunk_names) {
3853 <item> \ \ \ print prefix c suffix "\\n";
3860 This function would write out all chunks
3862 <\nf-chunk|output_chunks()>
3863 <item>function output_chunks( \ a)\
3867 <item> \ for (a in chunk_names) {
3869 <item> \ \ \ output_chunk(a);
3877 <item>function output_chunk(chunk) {
3879 <item> \ newline = 1;
3881 <item> \ lineno_needed = linenos;
3885 <item> \ write_chunk(chunk);
3892 <section|Assembling the Chunks>
3894 <verbatim|chunk_path> holds a string consisting of the names of all the
3895 chunks that resulted in this chunk being output. It should probably also
3896 contain the source line numbers at which each inclusion also occured.
3898 We first initialize the mode tracker for this chunk.
3900 <\nf-chunk|write_chunk()>
3901 <item>function write_chunk(chunk_name) {
3903 <item> \ =\<less\>\\chunkref{awk-delete-array}(context)\<gtr\>
3905 <item> \ return write_chunk_r(chunk_name, context);
3911 <item>function write_chunk_r(chunk_name, context, indent, tail,
3913 <item> \ # optional vars
3915 <item> \ <with|font-shape|italic|chunk_path>, chunk_args,\
3917 <item> \ s, r, src, new_src,\
3919 <item> \ # local vars
3921 <item> \ chunk_params, part, max_part, part_line, frag, max_frag, text,\
3923 <item> \ chunklet, only_part, call_chunk_args, new_context)
3927 <item> \ if (debug) debug_log("write_chunk_r(", chunk_name, ")");
3930 <subsection|Chunk Parts><label|sub:Chunk-parts>
3932 As mentioned in section <reference|sub:lstlistings-includes>, a chunk name
3933 may contain a part specifier in square brackets, limiting the parts that
3936 <\nf-chunk|write_chunk()>
3937 <item> \ if (match(chunk_name, "^(.*)\\\\[([0-9]*)\\\\]$",
3938 chunk_name_parts)) {
3940 <item> \ \ \ chunk_name = chunk_name_parts[1];
3942 <item> \ \ \ only_part = chunk_name_parts[2];
3947 We then create a mode tracker
3949 <\nf-chunk|write_chunk()>
3950 <item> =\<less\>\\chunkref{new-mode-tracker}(context, chunks[chunk_name,
3951 "language"], "")\<gtr\>
3954 We extract into <verbatim|chunk_params> the names of the parameters that
3955 this chunk accepts, whose values were (optionally) passed in
3956 <verbatim|chunk_args>.
3958 <\nf-chunk|write_chunk()>
3959 <item> split(chunks[chunk_name, "params"], chunk_params, " *; *");
3962 To assemble a chunk, we write out each part.
3964 <\nf-chunk|write_chunk()>
3965 <item> \ if (! (chunk_name in chunk_names)) {
3967 <item> \ \ \ error(sprintf(_"The root module
3968 \<less\>\<less\>%s\<gtr\>\<gtr\> was not defined.\\nUsed by: %s",\\
3970 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ chunk_name, chunk_path));
3976 <item> \ max_part = chunks[chunk_name, "part"];
3978 <item> \ for(part = 1; part \<less\>= max_part; part++) {
3980 <item> \ \ \ if (! only_part \|\| part == only_part) {
3982 <item> \ \ \ \ \ =\<less\>\\chunkref{write-part}\<gtr\>
3988 <item> \ if (! finalize_mode_tracker(context)) {
3990 <item> \ \ \ dump_mode_tracker(context);
3992 <item> \ \ \ error(sprintf(_"Module %s did not close context
3993 properly.\\nUsed by: %s\\n", chunk_name, chunk_path));
4000 A part can either be a chunklet of lines, or an include of another chunk.
4002 Chunks may also have parameters, specified in LaTeX style with braces after
4003 the chunk name --- looking like this in the document: chunkname{param1,
4004 param2}. Arguments are passed in square brackets:
4005 <verbatim|\\chunkref{chunkname}[arg1, arg2]>.
4007 Before we process each part, we check that the source position hasn't
4008 changed unexpectedly, so that we can know if we need to output a new
4009 file-line directive.
4011 <\nf-chunk|write-part>
4012 <item>=\<less\>\\chunkref{check-source-jump}\<gtr\>
4016 <item>chunklet = chunks[chunk_name, "part", part];
4018 <item>if (chunks[chunk_name, "part", part, "type"] == part_type_chunk) {
4020 <item> \ =\<less\>\\chunkref{write-included-chunk}\<gtr\>
4022 <item>} else if (chunklet SUBSEP "line" in chunks) {
4024 <item> \ =\<less\>\\chunkref{write-chunklets}\<gtr\>
4028 <item> \ # empty last chunklet
4033 To write an included chunk, we must detect any optional chunk arguments in
4034 parenthesis. Then we recurse calling <verbatim|write_chunk()>.
4036 <\nf-chunk|write-included-chunk>
4037 <item>if (match(chunklet, "^([^\\\\[\\\\(]*)\\\\((.*)\\\\)$",
4040 <item> \ chunklet = chunklet_parts[1];
4042 <item> \ parse_chunk_args("c-like", chunklet_parts[2], call_chunk_args,
4045 <item> \ for (c in call_chunk_args) {
4047 <item> \ \ \ call_chunk_args[c] = expand_chunk_args(call_chunk_args[c],
4048 chunk_params, chunk_args);
4054 <item> \ split("", call_chunk_args);
4058 <item># update the transforms arrays
4060 <item>new_src = mode_escaper(context, s, r, src);
4062 <item>=\<less\>\\chunkref{awk-delete-array}(new_context)\<gtr\>
4064 <item>write_chunk_r(chunklet, new_context,
4066 <item> \ \ \ \ \ \ \ \ \ \ \ chunks[chunk_name, "part", part, "indent"]
4069 <item> \ \ \ \ \ \ \ \ \ \ \ chunks[chunk_name, "part", part, "tail"],
4071 <item> \ \ \ \ \ \ \ \ \ \ \ chunk_path "\\n \ \ \ \ \ \ \ \ "
4074 <item> \ \ \ \ \ \ \ \ \ \ \ call_chunk_args,
4076 <item> \ \ \ \ \ \ \ \ \ \ \ s, r, new_src);
4079 Before we output a chunklet of lines, we first emit the file and line
4080 number if we have one, and if it is safe to do so.
4082 Chunklets are generally broken up by includes, so the start of a chunklet
4083 is a good place to do this. Then we output each line of the chunklet.
4085 When it is not safe, such as in the middle of a multi-line macro
4086 definition, <verbatim|lineno_suppressed> is set to true, and in such a case
4087 we note that we want to emit the line statement when it is next safe.
4089 <\nf-chunk|write-chunklets>
4090 <item>max_frag = chunks[chunklet, "line"];
4092 <item>for(frag = 1; frag \<less\>= max_frag; frag++) {
4094 <item> \ =\<less\>\\chunkref{write-file-line}\<gtr\>
4097 We then extract the chunklet text and expand any arguments.
4099 <\nf-chunk|write-chunklets>
4102 <item> \ text = chunks[chunklet, frag];
4106 <item> \ /* check params */
4108 <item> \ text = expand_chunk_args(text, chunk_params, chunk_args);
4111 If the text is a single newline (which we keep separate - see
4112 <reference|lone-newline>) then we increment the line number. In the case
4113 where this is the last line of a chunk and it is not a top-level chunk we
4114 replace the newline with an empty string --- because the chunk that
4115 included this chunk will have the newline at the end of the line that
4116 included this chunk.
4118 We also note by <verbatim|newline = 1> that we have started a new line, so
4119 that indentation can be managed with the following piece of text.
4121 <\nf-chunk|write-chunklets>
4124 <item> if (text == "\\n") {
4126 <item> \ \ \ lineno++;
4128 <item> \ \ \ if (part == max_part && frag == max_frag &&
4129 length(chunk_path)) {
4131 <item> \ \ \ \ \ text = "";
4133 <item> \ \ \ \ \ break;
4135 <item> \ \ \ } else {
4137 <item> \ \ \ \ \ newline = 1;
4142 If this text does not represent a newline, but we see that we are the first
4143 piece of text on a newline, then we prefix our text with the current
4147 <verbatim|newline> is a global output-state variable, but the
4148 <verbatim|indent> is not.
4151 <\nf-chunk|write-chunklets>
4152 <item> \ } else if (length(text) \|\| length(tail)) {
4154 <item> \ \ \ if (newline) text = indent text;
4156 <item> \ \ \ newline = 0;
4163 Tail will soon no longer be relevant once mode-detection is in place.
4165 <\nf-chunk|write-chunklets>
4166 <item> \ text = text tail;
4168 <item> \ mode_tracker(context, text);
4170 <item> \ print untab(transform_escape(s, r, text, src));
4173 If a line ends in a backslash --- suggesting continuation --- then we
4174 supress outputting file-line as it would probably break the continued
4177 <\nf-chunk|write-chunklets>
4178 <item> \ if (linenos) {
4180 <item> \ \ \ lineno_suppressed = substr(lastline, length(lastline)) ==
4188 Of course there is no point in actually outputting the source filename and
4189 line number (file-line) if they don't say anything new! We only need to
4190 emit them if they aren't what is expected, or if we we not able to emit one
4191 when they had changed.
4193 <\nf-chunk|write-file-line>
4194 <item>if (newline && lineno_needed && ! lineno_suppressed) {
4196 <item> \ filename = a_filename;
4198 <item> \ lineno = a_lineno;
4200 <item> \ print "#line " lineno " \\"" filename "\\"\\n"
4202 <item> \ lineno_needed = 0;
4207 We check if a new file-line is needed by checking if the source line
4208 matches what we (or a compiler) would expect.
4210 <\nf-chunk|check-source-jump>
4211 <item>if (linenos && (chunk_name SUBSEP "part" SUBSEP part SUBSEP
4212 "FILENAME" in chunks)) {
4214 <item> \ a_filename = chunks[chunk_name, "part", part, "FILENAME"];
4216 <item> \ a_lineno = chunks[chunk_name, "part", part, "LINENO"];
4218 <item> \ if (a_filename != filename \|\| a_lineno != lineno) {
4220 <item> \ \ \ lineno_needed++;
4227 <chapter|Storing Chunks>
4229 Awk has pretty limited data structures, so we will use two main hashes.
4230 Uninterrupted sequences of a chunk will be stored in chunklets and the
4231 chunklets used in a chunk will be stored in <verbatim|chunks>.
4233 <\nf-chunk|constants>
4234 <item>part_type_chunk=1;
4239 The params mentioned are not chunk parameters for parameterized chunks, as
4240 mentioned in <reference|Chunk Arguments>, but the lstlistings style
4241 parameters used in the <verbatim|\\Chunk> command<\footnote>
4242 The <verbatim|params> parameter is used to hold the parameters for
4243 parameterized chunks
4246 <\nf-chunk|chunk-storage-functions>
4247 <item>function new_chunk(chunk_name, params,
4249 <item> \ # local vars
4251 <item> \ p, append )
4255 <item> \ # HACK WHILE WE CHANGE TO ( ) for PARAM CHUNKS
4257 <item> \ gsub("\\\\(\\\\)$", "", chunk_name);
4259 <item> \ if (! (chunk_name in chunk_names)) {
4261 <item> \ \ \ if (debug) print "New chunk " chunk_name;
4263 <item> \ \ \ chunk_names[chunk_name];
4265 <item> \ \ \ for (p in params) {
4267 <item> \ \ \ \ \ chunks[chunk_name, p] = params[p];
4269 <item> \ \ \ \ \ if (debug) print "chunks[" chunk_name "," p "] = "
4274 <item> \ \ \ if ("append" in params) {
4276 <item> \ \ \ \ \ append=params["append"];
4278 <item> \ \ \ \ \ if (! (append in chunk_names)) {
4280 <item> \ \ \ \ \ \ \ warning("Chunk " chunk_name " is appended to chunk "
4281 append " which is not defined yet");
4283 <item> \ \ \ \ \ \ \ new_chunk(append);
4287 <item> \ \ \ \ \ chunk_include(append, chunk_name);
4289 <item> \ \ \ \ \ chunk_line(append, ORS);
4295 <item> \ active_chunk = chunk_name;
4297 <item> \ prime_chunk(chunk_name);
4302 <\nf-chunk|chunk-storage-functions>
4305 <item>function prime_chunk(chunk_name)
4309 <item> \ chunks[chunk_name, "part", ++chunks[chunk_name, "part"] ] = \\
4311 <item> \ \ \ \ \ \ \ \ chunk_name SUBSEP "chunklet" SUBSEP ""
4312 ++chunks[chunk_name, "chunklet"];
4314 <item> \ chunks[chunk_name, "part", chunks[chunk_name, "part"],
4315 "FILENAME"] = FILENAME;
4317 <item> \ chunks[chunk_name, "part", chunks[chunk_name, "part"], "LINENO"]
4324 <item>function chunk_line(chunk_name, line){
4326 <item> \ chunks[chunk_name, "chunklet", chunks[chunk_name, "chunklet"],
4328 <item> \ \ \ \ \ \ \ \ ++chunks[chunk_name, "chunklet",
4329 chunks[chunk_name, "chunklet"], "line"] \ ] = line;
4336 Chunk include represents a <em|chunkref> statement, and stores the
4337 requirement to include another chunk. The parameter indent represents the
4338 quanity of literal text characters that preceded this <em|chunkref>
4339 statement and therefore by how much additional lines of the included chunk
4342 <\nf-chunk|chunk-storage-functions>
4343 <item>function chunk_include(chunk_name, chunk_ref, indent, tail)
4347 <item> \ chunks[chunk_name, "part", ++chunks[chunk_name, "part"] ] =
4350 <item> \ chunks[chunk_name, "part", chunks[chunk_name, "part"], "type" ]
4353 <item> \ chunks[chunk_name, "part", chunks[chunk_name, "part"], "indent"
4354 ] = indent_string(indent);
4356 <item> \ chunks[chunk_name, "part", chunks[chunk_name, "part"], "tail" ]
4359 <item> \ prime_chunk(chunk_name);
4366 The indent is calculated by indent_string, which may in future convert some
4367 spaces into tab characters. This function works by generating a printf
4368 padded format string, like <verbatim|%22s> for an indent of 22, and then
4369 printing an empty string using that format.
4371 <\nf-chunk|chunk-storage-functions>
4372 <item>function indent_string(indent) {
4374 <item> \ return sprintf("%" indent "s", "");
4379 <chapter|getopt><label|cha:getopt>
4381 I use Arnold Robbins public domain getopt (1993 revision). This is probably
4382 the same one that is covered in chapter 12 of “Edition 3 of GAWK:
4383 Effective AWK Programming: A User's Guide for GNU Awk” but as that is
4384 licensed under the GNU Free Documentation License, Version 1.3, which
4385 conflicts with the GPL3, I can't use it from there (or it's accompanying
4386 explanations), so I do my best to explain how it works here.
4388 The getopt.awk header is:
4390 <\nf-chunk|getopt.awk-header>
4391 <item># getopt.awk --- do C library getopt(3) function in awk
4395 <item># Arnold Robbins, arnold@skeeve.com, Public Domain
4399 <item># Initial version: March, 1991
4401 <item># Revised: May, 1993
4406 The provided explanation is:
4408 <\nf-chunk|getopt.awk-notes>
4409 <item># External variables:
4411 <item># \ \ \ Optind -- index in ARGV of first nonoption argument
4413 <item># \ \ \ Optarg -- string value of argument to current option
4415 <item># \ \ \ Opterr -- if nonzero, print our own diagnostic
4417 <item># \ \ \ Optopt -- current option letter
4423 <item># \ \ \ -1 \ \ \ \ at end of options
4425 <item># \ \ \ ? \ \ \ \ \ for unrecognized option
4427 <item># \ \ \ \<less\>c\<gtr\> \ \ \ a character representing the current
4432 <item># Private Data:
4434 <item># \ \ \ _opti \ -- index in multi-flag option, e.g., -abc
4439 The function follows. The final two parameters, <verbatim|thisopt> and
4440 <verbatim|i> are local variables and not parameters --- as indicated by the
4441 multiple spaces preceding them. Awk doesn't care, the multiple spaces are a
4442 convention to help us humans.
4444 <\nf-chunk|getopt.awk-getopt()>
4445 <item>function getopt(argc, argv, options, \ \ \ thisopt, i)
4449 <item> \ \ \ if (length(options) == 0) \ \ \ # no options given
4451 <item> \ \ \ \ \ \ \ return -1
4453 <item> \ \ \ if (argv[Optind] == "--") { \ # all done
4455 <item> \ \ \ \ \ \ \ Optind++
4457 <item> \ \ \ \ \ \ \ _opti = 0
4459 <item> \ \ \ \ \ \ \ return -1
4461 <item> \ \ \ } else if (argv[Optind] !~ /^-[^: \\t\\n\\f\\r\\v\\b]/) {
4463 <item> \ \ \ \ \ \ \ _opti = 0
4465 <item> \ \ \ \ \ \ \ return -1
4469 <item> \ \ \ if (_opti == 0)
4471 <item> \ \ \ \ \ \ \ _opti = 2
4473 <item> \ \ \ thisopt = substr(argv[Optind], _opti, 1)
4475 <item> \ \ \ Optopt = thisopt
4477 <item> \ \ \ i = index(options, thisopt)
4479 <item> \ \ \ if (i == 0) {
4481 <item> \ \ \ \ \ \ \ if (Opterr)
4483 <item> \ \ \ \ \ \ \ \ \ \ \ printf("%c -- invalid option\\n",
4485 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ thisopt)
4486 \<gtr\> "/dev/stderr"
4488 <item> \ \ \ \ \ \ \ if (_opti \<gtr\>= length(argv[Optind])) {
4490 <item> \ \ \ \ \ \ \ \ \ \ \ Optind++
4492 <item> \ \ \ \ \ \ \ \ \ \ \ _opti = 0
4494 <item> \ \ \ \ \ \ \ } else
4496 <item> \ \ \ \ \ \ \ \ \ \ \ _opti++
4498 <item> \ \ \ \ \ \ \ return "?"
4503 At this point, the option has been found and we need to know if it takes
4506 <\nf-chunk|getopt.awk-getopt()>
4507 <item> \ \ \ if (substr(options, i + 1, 1) == ":") {
4509 <item> \ \ \ \ \ \ \ # get option argument
4511 <item> \ \ \ \ \ \ \ if (length(substr(argv[Optind], _opti + 1)) \<gtr\>
4514 <item> \ \ \ \ \ \ \ \ \ \ \ Optarg = substr(argv[Optind], _opti + 1)
4516 <item> \ \ \ \ \ \ \ else
4518 <item> \ \ \ \ \ \ \ \ \ \ \ Optarg = argv[++Optind]
4520 <item> \ \ \ \ \ \ \ _opti = 0
4524 <item> \ \ \ \ \ \ \ Optarg = ""
4526 <item> \ \ \ if (_opti == 0 \|\| _opti \<gtr\>= length(argv[Optind])) {
4528 <item> \ \ \ \ \ \ \ Optind++
4530 <item> \ \ \ \ \ \ \ _opti = 0
4534 <item> \ \ \ \ \ \ \ _opti++
4536 <item> \ \ \ return thisopt
4541 A test program is built in, too
4543 <\nf-chunk|getopt.awk-begin>
4546 <item> \ \ \ Opterr = 1 \ \ \ # default is to diagnose
4548 <item> \ \ \ Optind = 1 \ \ \ # skip ARGV[0]
4550 <item> \ \ \ # test program
4552 <item> \ \ \ if (_getopt_test) {
4554 <item> \ \ \ \ \ \ \ while ((_go_c = getopt(ARGC, ARGV, "ab:cd")) != -1)
4556 <item> \ \ \ \ \ \ \ \ \ \ \ printf("c = \<less\>%c\<gtr\>, optarg =
4557 \<less\>%s\<gtr\>\\n",
4559 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ _go_c,
4562 <item> \ \ \ \ \ \ \ printf("non-option arguments:\\n")
4564 <item> \ \ \ \ \ \ \ for (; Optind \<less\> ARGC; Optind++)
4566 <item> \ \ \ \ \ \ \ \ \ \ \ printf("\\tARGV[%d] = \<less\>%s\<gtr\>\\n",
4568 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Optind,
4576 The entire getopt.awk is made out of these chunks in order
4578 <\nf-chunk|getopt.awk>
4579 <item>=\<less\>\\chunkref{getopt.awk-header}\<gtr\>
4583 <item>=\<less\>\\chunkref{getopt.awk-notes}\<gtr\>
4585 <item>=\<less\>\\chunkref{getopt.awk-getopt()}\<gtr\>
4587 <item>=\<less\>\\chunkref{getopt.awk-begin}\<gtr\>
4590 Although we only want the header and function:
4593 <item># try: locate getopt.awk for the full original file
4595 <item># as part of your standard awk installation
4597 <item>=\<less\>\\chunkref{getopt.awk-header}\<gtr\>
4601 <item>=\<less\>\\chunkref{getopt.awk-getopt()}\<gtr\>
4604 <chapter|Fangle LaTeX source code><label|latex-source>
4606 <section|fangle module>
4608 Here we define a <LyX> <verbatim|.module> file that makes it convenient to
4609 use <LyX> for writing such literate programs.
4611 This file <verbatim|./fangle.module> can be installed in your personal
4612 <verbatim|.lyx/layouts> folder. You will need to Tools Reconfigure so that
4613 <LyX> notices it. It adds a new format Chunk, which should precede every
4614 listing and contain the chunk name.
4616 <\nf-chunk|./fangle.module>
4617 <item>#\\DeclareLyXModule{Fangle Literate Listings}
4619 <item>#DescriptionBegin
4621 <item># \ Fangle literate listings allow one to write
4623 <item># \ \ literate programs after the fashion of noweb, but without
4626 <item># \ \ to use noweave to generate the documentation. Instead the
4629 <item># \ \ package is extended in conjunction with the noweb package to
4632 <item># \ \ to code formating directly as latex.
4634 <item># \ The fangle awk script
4636 <item>#DescriptionEnd
4640 <item>=\<less\>\\chunkref{gpl3-copyright.hashed}\<gtr\>
4650 <item>=\<less\>\\chunkref{./fangle.sty}\<gtr\>
4656 <item>=\<less\>\\chunkref{chunkstyle}\<gtr\>
4660 <item>=\<less\>\\chunkref{chunkref}\<gtr\>
4661 </nf-chunk|lyx-module|>
4663 Because <LyX> modules are not yet a language supported by fangle or
4664 lstlistings, we resort to this fake awk chunk below in order to have each
4665 line of the GPL3 license commence with a #
4667 <\nf-chunk|gpl3-copyright.hashed>
4668 <item>#=\<less\>\\chunkref{gpl3-copyright}\<gtr\>
4673 <subsection|The Chunk style>
4675 The purpose of the <name|chunk> style is to make it easier for <LyX> users
4676 to provide the name to <verbatim|lstlistings>. Normally this requires
4677 right-clicking on the listing, choosing settings, advanced, and then typing
4678 <verbatim|name=chunk-name>. This has the further disadvantage that the name
4679 (and other options) are not generally visible during document editing.
4681 The chunk style is defined as a <LaTeX> command, so that all text on the
4682 same line is passed to the <verbatim|LaTeX> command <verbatim|Chunk>. This
4683 makes it easy to parse using <verbatim|fangle>, and easy to pass these
4684 options on to the listings package. The first word in a chunk section
4685 should be the chunk name, and will have <verbatim|name=> prepended to it.
4686 Any other words are accepted arguments to <verbatim|lstset>.
4688 We set PassThru to 1 because the user is actually entering raw latex.
4690 <\nf-chunk|chunkstyle>
4693 <item> \ LatexType \ \ \ \ \ \ \ \ \ \ \ \ Command
4695 <item> \ LatexName \ \ \ \ \ \ \ \ \ \ \ \ Chunk
4697 <item> \ Margin \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ First_Dynamic
4699 <item> \ LeftMargin \ \ \ \ \ \ \ \ \ \ \ Chunk:xxx
4701 <item> \ LabelSep \ \ \ \ \ \ \ \ \ \ \ \ \ xx
4703 <item> \ LabelType \ \ \ \ \ \ \ \ \ \ \ \ Static
4705 <item> \ LabelString \ \ \ \ \ \ \ \ \ \ "Chunk:"
4707 <item> \ Align \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Left
4709 <item> \ PassThru \ \ \ \ \ \ \ \ \ \ \ \ \ 1
4714 To make the label very visible we choose a larger font coloured red.
4716 <\nf-chunk|chunkstyle>
4719 <item> \ \ \ Family \ \ \ \ \ \ \ \ \ \ \ \ \ Sans
4721 <item> \ \ \ Size \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Large
4723 <item> \ \ \ Series \ \ \ \ \ \ \ \ \ \ \ \ \ Bold
4725 <item> \ \ \ Shape \ \ \ \ \ \ \ \ \ \ \ \ \ \ Italic
4727 <item> \ \ \ Color \ \ \ \ \ \ \ \ \ \ \ \ \ \ red
4734 <subsection|The chunkref style>
4736 We also define the Chunkref style which can be used to express cross
4737 references to chunks.
4739 <\nf-chunk|chunkref>
4740 <item>InsetLayout Chunkref
4742 <item> \ LyxType \ \ \ \ \ \ \ \ \ \ \ \ \ \ charstyle
4744 <item> \ LatexType \ \ \ \ \ \ \ \ \ \ \ \ Command
4746 <item> \ LatexName \ \ \ \ \ \ \ \ \ \ \ \ chunkref
4748 <item> \ PassThru \ \ \ \ \ \ \ \ \ \ \ \ \ 1
4750 <item> \ LabelFont \ \ \ \ \ \ \ \ \ \ \ \
4752 <item> \ \ \ Shape \ \ \ \ \ \ \ \ \ \ \ \ \ \ Italic
4754 <item> \ \ \ Color \ \ \ \ \ \ \ \ \ \ \ \ \ \ red
4761 <section|Latex Macros><label|sec:Latex-Macros>
4763 We require the listings, noweb and xargs packages. As noweb defines it's
4764 own <verbatim|\\code> environment, we re-define the one that <LyX> logical
4765 markup module expects here.
4767 <\nf-chunk|./fangle.sty>
4768 <item>\\usepackage{listings}%
4770 <item>\\usepackage{noweb}%
4772 <item>\\usepackage{xargs}%
4774 <item>\\renewcommand{\\code}[1]{\\texttt{#1}}%
4777 We also define a <verbatim|CChunk> macro, for use as:
4778 <verbatim|\\begin{CChunk}> which will need renaming to
4779 <verbatim|\\begin{Chunk}> when I can do this without clashing with
4782 <\nf-chunk|./fangle.sty>
4783 <item>\\lstnewenvironment{Chunk}{\\relax}{\\relax}%
4786 We also define a suitable <verbatim|\\lstset> of parameters that suit the
4787 literate programming style after the fashion of <name|noweave>.
4789 <\nf-chunk|./fangle.sty>
4790 <item>\\lstset{numbers=left, stepnumber=5, numbersep=5pt,
4792 <item> \ \ \ \ \ \ \ breaklines=false,basicstyle=\\ttfamily,
4794 <item> \ \ \ \ \ \ \ numberstyle=\\tiny, language=C}%
4797 We also define a notangle-like mechanism for escaping to <LaTeX> from the
4798 listing, and by which we can refer to other listings. We declare the
4799 <verbatim|=\<less\>...\<gtr\>> sequence to contain <LaTeX> code, and
4800 include another like this chunk: <verbatim|=\<less\>\\chunkref{chunkname}\<gtr\>>.
4801 However, because <verbatim|=\<less\>...\<gtr\>> is already defined to
4802 contain <LaTeX> code for this document --- this is a fangle document after
4803 all --- the code fragment below effectively contains the <LaTeX> code:
4804 <verbatim|}{>. To avoid problems with document generation, I had to declare
4805 an lstlistings property: <verbatim|escapeinside={}> for this listing only;
4806 which in <LyX> was done by right-clicking the listings inset, choosing
4807 settings-\<gtr\>advanced. Therefore <verbatim|=\<less\>> isn't interpreted
4808 literally here, in a listing when the escape sequence is already defined as
4809 shown... we need to somehow escape this representation...
4811 <\nf-chunk|./fangle.sty>
4812 <item>\\lstset{escapeinside={=\<less\>}{\<gtr\>}}%
4815 Although our macros will contain the <verbatim|@> symbol, they will be
4816 included in a <verbatim|\\makeatletter> section by <LyX>; however we keep
4817 the commented out <verbatim|\\makeatletter> as a reminder. The listings
4818 package likes to centre the titles, but noweb titles are specially
4819 formatted and must be left aligned. The simplest way to do this turned out
4820 to be by removing the definition of <verbatim|\\lst@maketitle>. This may
4821 interact badly if other listings want a regular title or caption. We
4822 remember the old maketitle in case we need it.
4824 <\nf-chunk|./fangle.sty>
4825 <item>%\\makeatletter
4827 <item>%somehow re-defining maketitle gives us a left-aligned title
4829 <item>%which is extactly what our specially formatted title needs!
4831 <item>\\global\\let\\fangle@lst@maketitle\\lst@maketitle%
4833 <item>\\global\\def\\lst@maketitle{}%
4836 <subsection|The chunk command><label|sub:The-chunk-command>
4838 Our chunk command accepts one argument, and calls <verbatim|\\ltset>.
4839 Although <verbatim|\\ltset> will note the name, this is erased when the
4840 next <verbatim|\\lstlisting> starts, so we make a note of this in
4841 <verbatim|\\lst@chunkname> and restore in in lstlistings Init hook.
4843 <\nf-chunk|./fangle.sty>
4844 <item>\\def\\Chunk#1{%
4846 <item> \ \\lstset{title={\\fanglecaption},name=#1}%
4848 <item> \ \\global\\edef\\lst@chunkname{\\lst@intname}%
4852 <item>\\def\\lst@chunkname{\\empty}%
4855 <subsubsection|Chunk parameters>
4857 Fangle permits parameterized chunks, and requires the paramters to be
4858 specified as listings options. The fangle script uses this, and although we
4859 don't do anything with these in the <LaTeX> code right now, we need to stop
4860 the listings package complaining.
4862 <\nf-chunk|./fangle.sty>
4863 <item>\\lst@Key{params}\\relax{\\def\\fangle@chunk@params{#1}}%
4866 As it is common to define a chunk which then needs appending to another
4867 chunk, and annoying to have to declare a single line chunk to manage the
4868 include, we support an append= option.
4870 <\nf-chunk|./fangle.sty>
4871 <item>\\lst@Key{append}\\relax{\\def\\fangle@chunk@append{#1}}%
4874 <subsection|The noweb styled caption>
4876 We define a public macro <verbatim|\\fanglecaption> which can be set as a
4877 regular title. By means of <verbatim|\\protect>, It expands to
4878 <verbatim|\\fangle@caption> at the appopriate time when the caption is
4881 <nf-chunk|./fangle.sty|\\def\\fanglecaption{\\protect\\fangle@caption}%||>
4884 22c <math|\<langle\>>some-chunk 19b<math|\<rangle\>><math|\<equiv\>>+
4885 \ \ <math|\<vartriangleleft\>>22b 24d<math|\<vartriangleright\>>
4889 In this example, the current chunk is 22c, and therefore the third chunk
4892 It's name is some-chunk.\
4894 The first chunk with this name (19b) occurs as the second chunk on page
4897 The previous chunk (22d) with the same name is the second chunk on page
4900 The next chunk (24d) is the fourth chunk on page 24.
4901 </big-figure|Noweb Heading<label|noweb heading>>
4903 The general noweb output format compactly identifies the current chunk, and
4904 references to the first chunk, and the previous and next chunks that have
4907 This means that we need to keep a counter for each chunk-name, that we use
4908 to count chunks of the same name.
4910 <subsection|The chunk counter>
4912 It would be natural to have a counter for each chunk name, but TeX would
4913 soon run out of counters<\footnote>
4914 ...soon did run out of counters and so I had to re-write the LaTeX macros
4915 to share a counter as described here.
4916 </footnote>, so we have one counter which we save at the end of a chunk and
4917 restore at the beginning of a chunk.
4919 <\nf-chunk|./fangle.sty>
4920 <item>\\newcounter{fangle@chunkcounter}%
4923 We construct the name of this variable to store the counter to be the text
4924 <verbatim|lst-chunk-> prefixed onto the chunks own name, and store it in
4925 <verbatim|\\chunkcount>.\
4927 We save the counter like this:
4929 <nf-chunk|save-counter|\\global\\expandafter\\edef\\csname
4930 \\chunkcount\\endcsname{\\arabic{fangle@chunkcounter}}%||>
4932 and restore the counter like this:
4934 <nf-chunk|restore-counter|\\setcounter{fangle@chunkcounter}{\\csname
4935 \\chunkcount\\endcsname}%||>
4937 If there does not already exist a variable whose name is stored in
4938 <verbatim|\\chunkcount>, then we know we are the first chunk with this
4939 name, and then define a counter.\
4941 Although chunks of the same name share a common counter, they must still be
4942 distinguished. We use is the internal name of the listing, suffixed by the
4943 counter value. So the first chunk might be <verbatim|something-1> and the
4944 second chunk be <verbatim|something-2>, etc.
4946 We also calculate the name of the previous chunk if we can (before we
4947 increment the chunk counter). If this is the first chunk of that name, then
4948 <verbatim|\\prevchunkname> is set to <verbatim|\\relax> which the noweb
4949 package will interpret as not existing.
4951 <\nf-chunk|./fangle.sty>
4952 <item>\\def\\fangle@caption{%
4954 <item> \ \\edef\\chunkcount{lst-chunk-\\lst@intname}%
4956 <item> \ \\@ifundefined{\\chunkcount}{%
4958 <item> \ \ \ \\expandafter\\gdef\\csname \\chunkcount\\endcsname{0}%
4960 <item> \ \ \ \\setcounter{fangle@chunkcounter}{\\csname
4961 \\chunkcount\\endcsname}%
4963 <item> \ \ \ \\let\\prevchunkname\\relax%
4967 <item> \ \ \ \\setcounter{fangle@chunkcounter}{\\csname
4968 \\chunkcount\\endcsname}%
4970 <item> \ \ \ \\edef\\prevchunkname{\\lst@intname-\\arabic{fangle@chunkcounter}}%
4975 After incrementing the chunk counter, we then define the name of this
4976 chunk, as well as the name of the first chunk.
4978 <\nf-chunk|./fangle.sty>
4979 <item> \ \\addtocounter{fangle@chunkcounter}{1}%
4981 <item> \ \\global\\expandafter\\edef\\csname
4982 \\chunkcount\\endcsname{\\arabic{fangle@chunkcounter}}%
4984 <item> \ \\edef\\chunkname{\\lst@intname-\\arabic{fangle@chunkcounter}}%
4986 <item> \ \\edef\\firstchunkname{\\lst@intname-1}%
4989 We now need to calculate the name of the next chunk. We do this by
4990 temporarily skipping the counter on by one; however there may not actually
4991 be another chunk with this name! We detect this by also defining a label
4992 for each chunk based on the chunkname. If there is a next chunkname then it
4993 will define a label with that name. As labels are persistent, we can at
4994 least tell the second time <LaTeX> is run. If we don't find such a defined
4995 label then we define <verbatim|\\nextchunkname> to <verbatim|\\relax>.
4997 <\nf-chunk|./fangle.sty>
4998 <item> \ \\addtocounter{fangle@chunkcounter}{1}%
5000 <item> \ \\edef\\nextchunkname{\\lst@intname-\\arabic{fangle@chunkcounter}}%
5002 <item> \ \\@ifundefined{r@label-\\nextchunkname}{\\let\\nextchunkname\\relax}{}%
5005 The noweb package requires that we define a <verbatim|\\sublabel> for every
5006 chunk, with a unique name, which is then used to print out it's navigation
5009 We also define a regular label for this chunk, as was mentioned above when
5010 we calculated <verbatim|\\nextchunkname>. This requires <LaTeX> to be run
5011 at least twice after new chunk sections are added --- but noweb requried
5014 <\nf-chunk|./fangle.sty>
5015 <item> \ \\sublabel{\\chunkname}%
5017 <item>% define this label for every chunk instance, so we
5019 <item>% can tell when we are the last chunk of this name
5021 <item> \ \\label{label-\\chunkname}%
5024 We also try and add the chunk to the list of listings, but I'm afraid we
5025 don't do very well. We want each chunk name listing once, with all of it's
5028 <\nf-chunk|./fangle.sty>
5029 <item> \ \\addcontentsline{lol}{lstlisting}{\\lst@name~[\\protect\\subpageref{\\chunkname}]}%
5032 We then call the noweb output macros in the same way that noweave generates
5033 them, except that we don't need to call <verbatim|\\nwstartdeflinemarkup>
5034 or <verbatim|\\nwenddeflinemarkup> <emdash> and if we do, it messes up the
5037 <\nf-chunk|./fangle.sty>
5038 <item> \ \\nwmargintag{%
5042 <item> \ \ \ \ \ \\nwtagstyle{}%
5044 <item> \ \ \ \ \ \\subpageref{\\chunkname}%
5054 <item> \ \ \ {\\lst@name}%
5058 <item> \ \ \ \ \ \\nwtagstyle{}\\/%
5060 <item> \ \ \ \ \ \\@ifundefined{fangle@chunk@params}{}{%
5062 <item> \ \ \ \ \ \ \ (\\fangle@chunk@params)%
5066 <item> \ \ \ \ \ [\\csname \\chunkcount\\endcsname]~%
5068 <item> \ \ \ \ \ \\subpageref{\\firstchunkname}%
5072 <item> \ \ \ \\@ifundefined{fangle@chunk@append}{}{%
5074 <item> \ \ \ \\ifx{}\\fangle@chunk@append{x}\\else%
5076 <item> \ \ \ \ \ \ \ ,~add~to~\\fangle@chunk@append%
5082 <item>\\global\\def\\fangle@chunk@append{}%
5084 <item>\\lstset{append=x}%
5090 <item> \ \\ifx\\relax\\prevchunkname\\endmoddef\\else\\plusendmoddef\\fi%
5092 <item>% \ \\nwstartdeflinemarkup%
5094 <item> \ \\nwprevnextdefs{\\prevchunkname}{\\nextchunkname}%
5096 <item>% \ \\nwenddeflinemarkup%
5101 Originally this was developed as a <verbatim|listings> aspect, in the Init
5102 hook, but it was found easier to affect the title without using a hook
5103 <emdash> <verbatim|\\lst@AddToHookExe{PreSet}> is still required to set the
5104 listings name to the name passed to the <verbatim|\\Chunk> command, though.
5106 <\nf-chunk|./fangle.sty>
5107 <item>%\\lst@BeginAspect{fangle}
5109 <item>%\\lst@Key{fangle}{true}[t]{\\lstKV@SetIf{#1}{true}}
5111 <item>\\lst@AddToHookExe{PreSet}{\\global\\let\\lst@intname\\lst@chunkname}
5113 <item>\\lst@AddToHook{Init}{}%\\fangle@caption}
5115 <item>%\\lst@EndAspect
5118 <subsection|Cross references>
5120 We define the <verbatim|\\chunkref> command which makes it easy to generate
5121 visual references to different code chunks, e.g.
5123 <block|<tformat|<table|<row|<cell|Macro>|<cell|Appearance>>|<row|<cell|<verbatim|\\chunkref{preamble}>>|<cell|>>|<row|<cell|<verbatim|\\chunkref[3]{preamble}>>|<cell|>>|<row|<cell|<verbatim|\\chunkref{preamble}[arg1,
5126 Chunkref can also be used within a code chunk to include another code
5127 chunk. The third optional parameter to chunkref is a comma sepatarated list
5128 of arguments, which will replace defined parameters in the chunkref.
5131 Darn it, if I have: <verbatim|=\<less\>\\chunkref{new-mode-tracker}[{chunks[chunk_name,
5132 "language"]},{mode}]\<gtr\>> the inner braces (inside [ ]) cause _ to
5133 signify subscript even though we have <verbatim|lst@ReplaceIn>
5136 <\nf-chunk|./fangle.sty>
5137 <item>\\def\\chunkref@args#1,{%
5139 <item> \ \\def\\arg{#1}%
5141 <item> \ \\lst@ReplaceIn\\arg\\lst@filenamerpl%
5145 <item> \ \\@ifnextchar){\\relax}{, \\chunkref@args}%
5149 <item>\\newcommand\\chunkref[2][0]{%
5151 <item> \ \\@ifnextchar({\\chunkref@i{#1}{#2}}{\\chunkref@i{#1}{#2}()}%
5155 <item>\\def\\chunkref@i#1#2(#3){%
5157 <item> \ \\def\\zero{0}%
5159 <item> \ \\def\\chunk{#2}%
5161 <item> \ \\def\\chunkno{#1}%
5163 <item> \ \\def\\chunkargs{#3}%
5165 <item> \ \\ifx\\chunkno\\zero%
5167 <item> \ \ \ \\def\\chunkname{#2-1}%
5171 <item> \ \ \ \\def\\chunkname{#2-\\chunkno}%
5175 <item> \ \\let\\lst@arg\\chunk%
5177 <item> \ \\lst@ReplaceIn\\chunk\\lst@filenamerpl%
5179 <item> \ \\LA{%\\moddef{%
5181 <item> \ \ \ {\\chunk}%
5185 <item> \ \ \ \ \ \\nwtagstyle{}\\/%
5187 <item> \ \ \ \ \ \\ifx\\chunkno\\zero%
5189 <item> \ \ \ \ \ \\else%
5191 <item> \ \ \ \ \ [\\chunkno]%
5193 <item> \ \ \ \ \ \\fi%
5195 <item> \ \ \ \ \ \\ifx\\chunkargs\\empty%
5197 <item> \ \ \ \ \ \\else%
5199 <item> \ \ \ \ \ \ \ (\\chunkref@args #3,)%
5201 <item> \ \ \ \ \ \\fi%
5203 <item> \ \ \ \ \ ~\\subpageref{\\chunkname}%
5209 <item> \ \\RA%\\endmoddef%
5214 <subsection|The end>
5216 <\nf-chunk|./fangle.sty>
5219 <item>%\\makeatother
5222 <chapter|Extracting fangle>
5224 <section|Extracting from Lyx>
5226 To extract from <LyX>, you will need to configure <LyX> as explained in
5227 section <reference|Configuring-the-build>.
5229 <label|lyx-build-script>And this lyx-build scrap will extract fangle for
5232 <\nf-chunk|lyx-build>
5239 <item>=\<less\>\\chunkref{lyx-build-helper}\<gtr\>
5241 <item>cd $PROJECT_DIR \|\| exit 1
5245 <item>/usr/local/bin/fangle -R./fangle $TEX_SRC \<gtr\> ./fangle
5247 <item>/usr/local/bin/fangle -R./fangle.module $TEX_SRC \<gtr\>
5252 <item>=\<less\>\\chunkref{test:helpers}\<gtr\>
5254 <item>export FANGLE=./fangle
5256 <item>export TMP=${TMP:-/tmp}
5258 <item>=\<less\>\\chunkref{test:run-tests}\<gtr\>
5260 <item># Now check that we can extract a fangle that also passes the
5263 <item>$FANGLE -R./fangle $TEX_SRC \<gtr\> ./new-fangle
5265 <item>export FANGLE=./new-fangle
5267 <item>=\<less\>\\chunkref{test:run-tests}\<gtr\>
5270 <\nf-chunk|test:run-tests>
5273 <item>$FANGLE -Rpca-test.awk $TEX_SRC \| awk -f - \|\| exit 1
5275 <item>=\<less\>\\chunkref{test:cromulence}\<gtr\>
5277 <item>=\<less\>\\chunkref{test:escapes}\<gtr\>
5279 <item>=\<less\>\\chunkref{test:chunk-params}\<gtr\>
5282 With a lyx-build-helper
5284 <\nf-chunk|lyx-build-helper>
5285 <item>PROJECT_DIR="$LYX_r"
5287 <item>LYX_SRC="$PROJECT_DIR/${LYX_i%.tex}.lyx"
5289 <item>TEX_DIR="$LYX_p"
5291 <item>TEX_SRC="$TEX_DIR/$LYX_i"
5294 <section|Extracting documentation>
5296 <\nf-chunk|./gen-www>
5297 <item>#python -m elyxer --css lyx.css $LYX_SRC \| \\
5299 <item># \ iconv -c -f utf-8 -t ISO-8859-1//TRANSLIT \| \\
5301 <item># \ sed 's/UTF-8"\\(.\\)\<gtr\>/ISO-8859-1"\\1\<gtr\>/' \<gtr\>
5302 www/docs/fangle.html
5306 <item>python -m elyxer --css lyx.css --iso885915 --html --destdirectory
5307 www/docs/fangle.e \\
5309 <item> \ \ \ \ \ \ fangle.lyx \<gtr\> www/docs/fangle.e/fangle.html
5313 <item>( mkdir -p www/docs/fangle && cd www/docs/fangle && \\
5315 <item> \ lyx -e latex ../../../fangle.lyx && \\
5317 <item> \ htlatex ../../../fangle.tex "xhtml,fn-in" && \\
5319 <item> \ sed -i -e 's/\<less\>!--l\\. [0-9][0-9]* *--\<gtr\>//g'
5326 <item>( mkdir -p www/docs/literate && cd www/docs/literate && \\
5328 <item> \ lyx -e latex ../../../literate.lyx && \\
5330 <item> \ htlatex ../../../literate.tex "xhtml,fn-in" && \\
5332 <item> \ sed -i -e 's/\<less\>!--l\\. [0-9][0-9]* *--\<gtr\>$//g'
5338 <section|Extracting from the command line>
5340 First you will need the tex output, then you can extract:
5342 <\nf-chunk|lyx-build-manual>
5343 <item>lyx -e latex fangle.lyx
5345 <item>fangle -R./fangle fangle.tex \<gtr\> ./fangle
5347 <item>fangle -R./fangle.module fangle.tex \<gtr\> ./fangle.module
5352 <\nf-chunk|test:helpers>
5357 <item> \ then echo "Passed"
5359 <item> \ else echo "Failed"
5361 <item> \ \ \ \ \ \ return 1
5373 <item> \ then echo "Passed"
5375 <item> \ else echo "Failed"
5377 <item> \ \ \ \ \ \ return 1
5386 <chapter|Chunk Parameters>
5388 <\nf-chunk|test:chunk-params:sub>
5389 <item>I see a ${THING},
5391 <item>a ${THING} of colour ${colour},\
5393 <item>and looking closer =\<less\>\\chunkref{test:chunk-params:sub:sub}(${colour})\<gtr\>
5394 </nf-chunk||<tuple|THING|colour>>
5396 <\nf-chunk|test:chunk-params:sub:sub>
5397 <item>a funny shade of ${colour}
5398 </nf-chunk||<tuple|colour>>
5400 <\nf-chunk|test:chunk-params:text>
5401 <item>What do you see? "=\<less\>\\chunkref{test:chunk-params:sub}(joe,
5407 Should generate output:
5409 <\nf-chunk|test:chunk-params:result>
5410 <item>What do you see? "I see a joe,
5412 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ a joe of colour red,\
5414 <item> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ and looking closer a funny shade
5420 And this chunk will perform the test:
5422 <\nf-chunk|test:chunk-params>
5423 <item>$FANGLE -Rtest:chunk-params:result $TEX_SRC \<gtr\> $TMP/answer
5426 <item>$FANGLE -Rtest:chunk-params:text $TEX_SRC \<gtr\> $TMP/result \|\|
5429 <item>passtest diff $TMP/answer $TMP/result \|\| (echo
5430 test:chunk-params:text failed ; exit 1)
5433 <chapter|Compile-log-lyx><label|Compile-log-lyx>
5435 <\nf-chunk|Chunk:./compile-log-lyx>
5438 <item># can't use gtkdialog -i, cos it uses the "source" command which
5439 ubuntu sh doesn't have
5445 <item> \ errors="/tmp/compile.log.$$"
5447 <item># \ if grep '^[^ ]*:\\( In \\\|[0-9][0-9]*: [^ ]*:\\)' \<gtr\>
5450 <item>if grep '^[^ ]*(\\([0-9][0-9]*\\)) *: *\\(error\\\|warning\\)'
5455 <item> \ \ \ sed -i -e 's/^[^ ]*[/\\\\]\\([^/\\\\]*\\)(\\([ 0-9][
5456 0-9]*\\)) *: */\\1:\\2\|\\2\|/' $errors
5458 <item> \ \ \ COMPILE_DIALOG='
5460 <item> \<less\>vbox\<gtr\>
5462 <item> \ \<less\>text\<gtr\>
5464 <item> \ \ \ \<less\>label\<gtr\>Compiler errors:\<less\>/label\<gtr\>
5466 <item> \ \<less\>/text\<gtr\>
5468 <item> \ \<less\>tree exported_column="0"\<gtr\>
5470 <item> \ \ \ \<less\>variable\<gtr\>LINE\<less\>/variable\<gtr\>
5472 <item> \ \ \ \<less\>height\<gtr\>400\<less\>/height\<gtr\>\<less\>width\<gtr\>800\<less\>/width\<gtr\>
5474 <item> \ \ \ \<less\>label\<gtr\>File \| Line \|
5475 Message\<less\>/label\<gtr\>
5477 <item> \ \ \ \<less\>action\<gtr\>'". $SELF ; "'lyxgoto
5478 $LINE\<less\>/action\<gtr\>
5480 <item> \ \ \ \<less\>input\<gtr\>'"cat $errors"'\<less\>/input\<gtr\>
5482 <item> \ \<less\>/tree\<gtr\>
5484 <item> \ \<less\>hbox\<gtr\>
5486 <item> \ \ \<less\>button\<gtr\>\<less\>label\<gtr\>Build\<less\>/label\<gtr\>
5488 <item> \ \ \ \ \<less\>action\<gtr\>lyxclient -c "LYXCMD:build-program"
5489 &\<less\>/action\<gtr\>
5491 <item> \ \ \<less\>/button\<gtr\>
5493 <item> \ \ \<less\>button ok\<gtr\>\<less\>/button\<gtr\>
5495 <item> \ \<less\>/hbox\<gtr\>
5497 <item> \<less\>/vbox\<gtr\>
5501 <item> \ \ \ export COMPILE_DIALOG
5503 <item> \ \ \ ( gtkdialog --program=COMPILE_DIALOG ; rm $errors ) &
5507 <item> \ \ \ rm $errors
5517 <item> \ file="${LINE%:*}"
5519 <item> \ line="${LINE##*:}"
5521 <item> \ extraline=`cat $file \| head -n $line \| tac \| sed
5522 '/^\\\\\\\\begin{lstlisting}/q' \| wc -l`
5524 <item> \ extraline=`expr $extraline - 1`
5526 <item> \ lyxclient -c "LYXCMD:command-sequence server-goto-file-row $file
5527 $line ; char-forward ; repeat $extraline paragraph-down ;
5528 paragraph-up-select"
5536 <item>if test -z "$COMPILE_DIALOG"
5538 <item>then main "$@"\
5548 <associate|info-flag|short>
5549 <associate|page-medium|paper>
5550 <associate|page-screen-height|982016tmpt>
5551 <associate|page-screen-margin|false>
5552 <associate|page-screen-width|1686528tmpt>
5553 <associate|preamble|false>
5554 <associate|sfactor|5>