summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/tclldoc/tools/sourcedtx.dtx
blob: 3b024427ab628083b7c1ace25db4e37e4e4048e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
%
% \iffalse
%<*driver>
\documentclass{tclldoc}
\begin{document}
\DocInput{sourcedtx.dtx}
\end{document}
%</driver>
% \fi
% 
% \title{The \textsf{sourcedtx} package}
% \author{Lars Hellstr\"om}
% \date{18 July 2003}
% \maketitle
% 
% \begin{abstract}
%   The \textsf{sourcedtx} package provides a \Tcllogo\ command 
%   |dtx::source| that makes it possible to |source| \Tcllogo\ code 
%   from a \textsf{doc}-style \texttt{.dtx} file without docstripping 
%   it first.
% \end{abstract}
% 
% 
% \begin{tcl}
%<*pkg>
namespace eval dtx {}
% \end{tcl}
% \setnamespace{dtx}
% 
% \begin{proc}{strip_string}
%   The |strip_string| procedure does the actual docstripping for 
%   |source_dtx|. The syntax is
%   \begin{quote}
%     |dtx::strip_string| \word{text} \word{option list}
%     \word{meta prefix}\regopt\ \word{interaction}\regopt
%   \end{quote}
%   where \word{text} is the string to docstrip and \word{option list} 
%   is the list of options to use. The \word{meta prefix} (by default 
%   two percent signs) is the string to use for the \textsc{docstrip} 
%   parameter \verb|\MetaPrefix|. The \word{interation} (by default 1) 
%   is a flag for the ``interaction'' level to use. |0| means error 
%   messages are simply written to |stderr|, whereas |1| means an error 
%   will be raised. (It is possible that \word{interaction} will be 
%   extended to a ``flag word'' where each bit controls some aspect of 
%   the interaction.)
%   
%   When errors are raised, the |errorCode| is set to a list with the 
%   format
%   \begin{quote}
%     |DOCSTRIP| \word{situation} \word{lineno}
%   \end{quote}
%   where \word{lineno} is the line number (starting at one) of the line 
%   where the error was detected. The \word{situation}s are described 
%   below, at the positions in the code where they can occur.
%   
%   \begin{tcl}
proc dtx::strip_string {text options {metaprefix %%} {interaction 1}} {
%   \end{tcl}
%   The |O| array has entires for precisely the specified options, so 
%   that an opion can be tested using |info exists|.
%   \begin{tcl}
   foreach option $options {set O($option) ""}
%   \end{tcl}
%   |stripped| is where the text that passes docstripping is collected.
%   \begin{tcl}
   set stripped ""
%   \end{tcl}
%   |block_stack| is the list of modules inside which the current line 
%   lies. |offlevel| is the number of modules that must be exited 
%   before code lines should once again be included. |verbatim| is a 
%   flag for whether verbatim mode is in force.
%   \begin{tcl}
   set block_stack [list]
   set offlevel 0
   set verbatim 0
%   \end{tcl}
%   |lineno| is the input line number counter, for use in error 
%   messages. 
%   \begin{tcl}
   set lineno 1
%   \end{tcl}
%   Here starts the main loop over lines in the \word{text}. It 
%   constitutes the majority of the procedure and is split in two 
%   parts. The smaller part handles lines in verbatim mode (unusual), 
%   the large part handles lines in normal mode (with comment lines, 
%   code lines, guard lines, and so on).
%   \begin{tcl}
   foreach line [split $text \n] {
      if {$verbatim} then {
         if {$line eq $endverbline} then {
            set verbatim 0
         } elseif {!$offlevel} then {
            append stripped $line \n
         }
      } else {
         switch -glob -- $line %%* {
            if {!$offlevel} then {
               append stripped $metaprefix\
                 [string range $line 2 end] \n
            }
         } %<<* {
            set endverbline "%[string range $line 3 end]"
            set verbatim 1
         } %<* {
            if {[regexp -- {^%<([*/+-]?)([^>]*)>(.*)$} $line ""\
              modifier expression line]} then {
%   \end{tcl}
%   There is a well-formed guard line. First the expression is 
%   evaluated, by converting it to an |expr| expression.
%   \begin{tcl}
               regsub -all -- {\\|\{|\}|\$|\[|\]| |;} $expression\
                 {\\&} E
               regsub -all -- {,} $E {|} E
               regsub -all -- {[^()|&!]+} $E {[info exists O(&)]} E
               set val [expr $E]
               switch -exact -- $modifier * {
                  lappend block_stack $expression
                  if {$offlevel || !$val} then {incr offlevel}
               } / {
                  if {![llength $block_stack]} then {
%   \end{tcl}
%   In this case there was no open block for this guard to end. That 
%   is a \describestring[error situation]{SPURIOUS}|SPURIOUS| 
%   \word{situation}.
%   \begin{tcl}
                     if {$interaction} then {
                        error "Spurious end block </$expression>\
                          ignored." ""\
                          [list DOCSTRIP SPURIOUS $lineno]
                     } else {
                        puts stderr "docstrip: Spurious end\
                          block </$expression> ignored on line\
                          $lineno."
                     }
                  } else {
                     if {[string compare $expression\
                       [lindex $block_stack end]]} then {
%   \end{tcl}
%   In this case the expression of the block being closed does not match 
%   the expression on the block on top of the stack. That is a 
%   \describestring[error situation]{MISMATCH}|MISMATCH| 
%   \word{situation}. \textsc{docstrip} by default raises an error and 
%   recovers by treating this situation as a typo.
%   \begin{tcl}
                        if {$interaction} then {
                           error "Found </$expression> instead of\
                             </[lindex $block_stack end]>." ""\
                             [list DOCSTRIP MISMATCH $lineno]
                        }
                        puts stderr "docstrip:\
                          Found </$expression> instead of\
                          </[lindex $block_stack end]> on line\
                          $lineno."
                     }
%   \end{tcl}
%   All that error processing makes it easy to lose track, but the 
%   following two lines are what does the real work for an end of block 
%   guard: pop a block off the stack and decrement the |offlevel|.
%   \begin{tcl}
                     if {$offlevel} then {incr offlevel -1}
                     set block_stack [lreplace $block_stack end end]
                  }
               } - {
                  if {!$offlevel && !$val} then {
                     append stripped $line \n
                  }
               } default {
                  if {!$offlevel && $val} then {
                     append stripped $line \n
                  }
               }
            } else {
%   \end{tcl}
%   In this case the line looks like a guard line, but there is no |>| 
%   terminating the guard expression. This is a 
%   \describestring[error situation]{BADGUARD}|BADGUARD| 
%   \word{situation}.
%   \begin{tcl}
               if {$interaction} then {
                  error "Malformed guard on line $lineno." ""\
                    [list DOCSTRIP BADGUARD $lineno]
               } else {
                  puts stderr "docstrip: Malformed guard\
                    on line $lineno:"
                  puts stderr $line
               }
            }
         } %* {}\
%   \end{tcl}
%   With comment lines, nothing is done. A line being the exact string 
%   |\endinput| terminates the stripping.
%   \begin{tcl}
         {\\endinput} {
           break
         } default {
%   \end{tcl}
%   Other lines are code lines. These are included or not, depending on 
%   the |offlevel|.
%   \begin{tcl}
            if {!$offlevel} then {append stripped $line \n}
         }
      }
      incr lineno
   }
   return $stripped
}
%   \end{tcl}
% \end{proc}
% 
% 
% \begin{proc}{source}
%   This procedure behaves as a docstripping |source| command: it reads 
%   a file, docstrips its contents in memory, and evaluates the result 
%   as a \Tcllogo\ script in the context of the caller. The syntax is
%   \begin{quote}
%     |dtx::source| \word{dtx-file} \word{options}
%   \end{quote}
%   where \word{dtx-file} is the file name and \word{options} is the 
%   list of options.
%   \begin{tcl}
proc dtx::source {name options} {
   set F [open $name r]
   set text [read $F]
   close $F
   uplevel 1 [dtx::strip_string $text $options #]
}
%</pkg>
%   \end{tcl}
% \end{proc}
% 
% 
\endinput