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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
|
*-- doindex.sno 0.1
*-- Guido Milanese 2005
*-- guido.milanese@unicatt.it
*-- Requires:
* acc.inc
* extrepl.inc
* ioerror.inc
* Please set tabstop=3 to read
* Initializations
&MAXLNGTH = 2000000
* If no arguments are given
(
+ (~(gt((size(host(2,4))),0)))
+ (terminal = "No file entered. Usage: doindex file.tex")
+ (terminal = "Help: doindex -help")
+ ) :s(end)
* If help is required
(
+ ((host(2,4)) ? "-h")
+ (terminal = "Usage: doindex file.tex")
+ (terminal = "To exclude words containing non-English characters:"
+ " doindex file.tex -noacc")
+ (terminal = "Further options: please read the README file or the other documentation files")
+ ) :s(end)
********************************************
** External functions
********************************************
* Accents
(
+ (((Accopt = host(2,5)) (Accopt ? "-noacc")) (Accopt = "en")),
+ (Accopt = "XX")
+ )
* all or english
-include "acc.inc"
(acc(Accopt) ? break('-') . accmi len(1) rem . accma )
acc = accmi accma
* Extended replace
-include "extrepl.inc"
* Input-output
-include "ioerror.inc"
***********************************
* Internal functions
***********************************
* Index adapt.
define("iadapt(Datum)IL,ILK,A,I,Newdatum")
Sep = '/' :(iadapt_end)
iadapt
Datum ? Sep :f(iadapt_rt)
IL = Table()
ILK = 0
ILK ILK = ILK + 1
(
+ (Datum ? break(Sep) . IL[ILK] len(1) rem . Datum)
+ ) :s(ILK)
A = rsort(IL)
I = 0
iadapt_p I = I + 1
Newdatum = Newdatum '!' A[I,2] :S(iadapt_p)F(iadapt_nof)
iadapt_nof
Datum = Datum Newdatum
iadapt_rt iadapt = Datum :(return)
iadapt_end
***************************
* Index Member
* Modified version of Member.inc (gm)
* Uses iadapt
define("imember(Datum,List)I,Check,First")
Sep = '/' :(imember_end)
imember
I = 0
imember_bg I = I + 1
First = &lcase &ucase ;* gives a faked value
Check = List[I,2] :f(freturn)
* With '@' or Sep
(
+ (List[I,2] ? break('@' Sep) . First)
+ (ident(First,Datum))
+ ) :s(imember_rt)
* Simple
ident(List[I,2],Datum) :s(imember_rt)f(imember_bg)
imember_rt imember = iadapt(List[I,2]) :(return)
imember_end
**********************************
*-- Redoin
*-- Remove double index entries
define("redoin(Pass)dindex1,dindex2") :(redoin_end)
redoin
redoin_lp ((Pass ? Dindex . VDindex) (ident(dindex1,dindex2))
+ (Pass ? VDindex = dindex1)) :s(redoin_lp)
redoin_rt redoin = Pass :(return)
redoin_end
******************************************
* Patterns
******************************************
PNWord = span(&ucase &lcase acc)
PIWord = ("\index" bal)
PWord = PIWOrd | PNWord
Dindex = (("\index{" break('}') len(1)) . dindex1)
+ (("\index{" break('}') len(1)) . dindex2)
***********************************
* Read LaTeX file
LATEXEXT = (".tex" | ".latex" | ".TEX" | ".LATEX")
lfile = host(2,4) ;* LaTeX file input
lfile ? '' (arb . file) LATEXEXT
ifile = file ".wls"
ofile = file "-ind.tex" ;* output
*-- Input files. Using ERRORS
( (~(input("l_f",1, "B " &MAXLNGTH,lfile))) (ioerror("in",lfile)) ) :s(end)
( (~(input("i_f",2, &MAXLNGTH,ifile))) (ioerror("in",ifile)) ) :s(end)
*-- File di output
output("w_f",3, "B " &MAXLNGTH,ofile) :f(end)
************************************
* MAIN PROGRAM
************************************
* Text
((Text = l_f) (Terminal = "Read file " lfile ' [' size(Text) ' chars]')) :f(end)
* List
L = TABLE()
LK = 0
LK LK = LK + 1
L[LK] = i_f :s(LK)
* Converts to Array
A = convert(L,"ARRAY")
* Masks () and {}
Text = extrepl(Text,'(', "&0028;")
Text = extrepl(Text,')', "&0029;")
Text = replace(Text,'{}','()')
alfa
Text2 = Text2 Previous Item
Text ? ('' (arb . Previous) (PWord . Item)) rem . Text :f(omega)
(
+ ((Item ? PIWord) ( )),
+ ((Item ? PNWord) ( (Indexitem = imember(Item,A)) (Item = Item "\index(" Indexitem ")")))
+ ) :(alfa)
omega
Text2 = Text2 Text
* Replaces original () and {}
Text2 = replace(Text2,"()","{}")
Text2 = extrepl(Text2,"&0028;",'(')
Text2 = extrepl(Text2,"&0029;",')')
Text2 = redoin(Text2)
((w_f = Text2) (Terminal = "Written file " ofile ' [' size(Text2) ' chars]')) :f(end)
end
******************************************
COMMENT
(Please set tabstop=3 to read)
** Initializationa and external include files
&MAXLNGTH = 2000000
should be enough for any normal LaTeX file
-include "acc.inc"
to use with non English texts. To exclude it, use the option "-noacc" (only
words with no accented letters will be indexed)
-include "extrepl.inc"
makes possibile to change single chars with a string or vice-versa.
-include "ioerror.inc"
checks if input-output is correct
** Internal functions
* "Index Member" checks if a word is included in the list of pre-indexed words.
There are 3 possible situations in the list:
(1) the item has '@' as in "cats@cat"
(2) the item has a Sep as in "cats/animals"
(3) the item is a simple word
In the first or second case, the word being checked must be identical to the first part
of the list item.
E.g., "cats" must be identical, in "cats@cat" to "cats".
"cats" must be identical, in "cats/animals" to "cats"
In the final case, the list item must be identical to the checked word.
* "Index adapt" changes a sequence like
"cats/mammals/animals"
to this form
"animals!mammals!cats"
If there is no TAB in the sequence, nothing happens.
The trick is done so:
(1) a table IL is initialized
(2) Datum up to TAB is saved in IL[ILK] (loop)
(3) The table is sorted reversed --> array.
This array contains therefore the words from the last to the first
(4) This array is scanned and the new form is produced.
* "Redoin" removes double index entries from a file
*** Patterns:
A word can be part of an index sequence or not (this is important, because
the files may contain already some indexed words):
PNWord = span(&ucase &lcase acc) : Normal word
PIWord = ("\index" bal) : Indexed word sequence
PWord = PIWOrd | PNWord : Both
DIndex is defined using break.
*** Input-output
Quite obvious. Extensions accepted are 'tex' and 'latex', both uppercase and
lowercase.
Calling the function IOERROR makes it possible to check if a file is
really available.
The LIST file must have the same name, with extension wls.
*** Reading the List
The List file must have this form:
animals
dogs/animals
cats
house/nouns/english/languages
sleeping@sleep
évita/Italian/foreign words
ça/French/foreign words
drücken/German/foreign words
where each word is listed as such or as part of a structured index. This
syntax seems more natural than the opposite one used by makeindex:
for example a DOG is part of the class ANIMALS. This way it is easy to change
indexes and adapt them to changing needs. The output of
dogs animals
will be
\index{animals!dogs}
** Temporary protection of {} and ()
Unfortunately, the very solid Snobol BAL function supports only () pairs. So it is
necessary: (1) to protect all the existing () -- they are changed into a
XML-Unicode-like string ; (2) to chane all the {} pairs into () pairs. At the
end of work, the reverse process is performed.
** Core of the program
We must build a new text, called Text2, that is done adding:
* the previous condition of Text2 (null string at the first loop)
* the new portion of text being extracted at each loop, devided into:
* Previous = what is before the word found
* Item = the word
For example, in "<<This": '<<' is Previous, 'This' is Item.
Previous is not analyzed any more, Item is sent to the following analysis
process.
Code:
*******
alfa
Text2 = Text2 Previous Item
*******
Now the procedure to extract single words:
In Text, what is found before a Word, and call it Previous. a Word is found
if corresponds to the pattern PWord, and saved as Item. What remains is
called again Text, in order to make the loop possible. If the pattern
matching fails, it means that Text is exausted, and we leave the loop.
Code:
Text ? ('' (arb . Previous) (PWord . Item)) rem . Text :f(omega)
Now the scanning of Item. The code says:
if Item is an indexed sequence (as "\index{rose}"), do not do anything
if Item is a word, check if it is a member of the list of words to be
indexed passed to the program. If so, all the procedures of IMEMBER and
IADAPT will be called. If it is not a member of the list, IMEMBER fails an
nothing happens.
Code:
*********
(
+ ((Item ? PIWord) ( )),
+ ((Item ? PNWord) ( (Indexitem = imember(Item,A)) (Item = Item "\index(" Indexitem ")")))
+ ) :(alfa)
*********
At the end, Text2 receives the final portion of Text (if any). For example, if a
text end with the string "animals." the string "animal" will be recognized as a
Word, but the remaining protion, i.e. the final full stop, will be not
analyzed any further. Therefore, this portion of thext must be added.
Code:
********
omega
Text2 = Text2 Text
********
Possible double index entries are removed.
Finally, the replacement od () and {} pairs is performed, and Text2 written in
the output file, with some additional info to the user.
Genoa, Italy, 26th of January 2005
|