-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnguhgloss.sty
299 lines (264 loc) · 9.77 KB
/
nguhgloss.sty
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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Typesetting Macros
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% =====================================================================
%% \gloss : Format a 2, 4, or 5-line gloss.
%% =====================================================================
%%
%% Example:
%%
%% \gloss {
%% Caesar | pisc-em | ēm-it
%% Caesar.nom | fish-acc.sg | buy-3sg.past
%% }
%%
%% A | delimits columns. You don’t need to line up the columns manually;
%% this was only done here for demonstration purposes. There is also a
%% 4/5-line format if you want to include the original spelling, pronunciation
%% and translation:
%%
%% \gloss {
%% Caesar piscem ēmit.
%% Caesar | pisc-em | ēm-it
%% ˈkae.sar | pis.kẽˑ | ˈeː.mɪt % You can also omit this line.
%% Caesar.nom | fish-acc.sg | buy-3sg.past
%% ‘Caesar bought a fish.’
%% }
%%
%% The first line (the text) is typeset in bold+italic, the second line
%% in italic, any other lines in roman.
%%
%% =====================================================================
%% \multigloss : Format a gloss across multiple lines
%% =====================================================================
%%
%% Example:
%%
%% \multigloss {
%% Caesar | pisc-em | ēm-it
%% Caesar.nom | fish-acc.sg | buy-3sg.past
%% }
%%
%%
%% The syntax of this is identical to \gloss, except that only the 2-line
%% format is supported. Unlike \gloss, which always typesets everything in
%% a single line, this will automatically break the gloss into multiple lines
%% if it would run into the page margin. Use this for longer glosses.
%%
%% You can also put the gloss on multiple lines in the source code; this
%% doesn’t have any effect on the output, but may help to make it more
%% readable:
%%
%% \multigloss {
%% Caesar | pisc-em
%% Caesar.nom | fish-acc.sg
%%
%% ēm-it
%% buy-3sg.past
%% }
%%
%%
%% The format of the first and second line of a multigloss can be set using
%% \MultiglossFormatFirst and \MultiglossFormatSecond, respectively:
%%
%% \MultiglossFormatFirst{\ttfamily} % Format first line in typewriter font.
%% \MultiglossFormatSecond{\itshape} % Format second line in italics.
%%
\NeedsTeXFormat{LaTeX2e}
\ProvidesExplPackage{nguhgloss}{2024/06/11}{v1.0}{Package for typesetting glosses}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internals
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\cs_generate_variant:Nn \seq_set_split_keep_spaces:Nnn { Nnx }
%% Variables.
\tl_new:N \g__nguh_multigloss_first_tl
\tl_new:N \g__nguh_multigloss_second_tl
%% Initial values.
\tl_gset:Nn \g__nguh_multigloss_first_tl { \itshape }
\tl_gset:Nn \g__nguh_multigloss_second_tl { \scshape }
%% End a paragraph.
\cs_new:Npn \__nguh_end_par: {
\ifvmode\else
\unskip
\par
\fi
}
\cs_new:Npn \__nguh_gloss_insert_table_header: {
%% Generating columns doesn’t seem to work, so this hack will do. If you
%% need a gloss with more columns than this, I suggest you pause and take
%% a moment to reflect on your life choices.
\begin {tabular} { @{} *{100}l }
}
\cs_new:Npn \__nguh_gloss_table_start: {
\__nguh_end_par:
\addvspace { 8pt }
%\vtop{\iffalse}\fi
\noindent
}
\cs_new:Npn \__nguh_gloss_table_end: {
\end {tabular}
\ifvmode\else\par\fi
\addvspace { 8pt }
%\iffalse{\fi}
\everypar { \setbox\z@\lastbox \everypar{} }
}
%% Rescan a token list.
\cs_new:Npn \__nguh_gloss_rescan:n #1 {
{ \tl_rescan:nn {} { \ignorespaces #1 } }
}
%% Format a single line of a gloss.
%%
%% The line number must be stored in \g_tempb_int.
%%
%% This takes the line #1 and formats it, inserting the contents of #2
%% at the start of each line, and #3 at the beginning of each column.
%% #3 can be a macro that takes one argument, in which case it will be
%% passed the contents of the column.
\cs_new:Npn \__nguh_gloss_format_line:nnn #1 #2 #3 {
#2
\seq_set_split:Nnn \l_tmpa_seq { | } { #1 }
\bool_set_true:N \l_tmpa_bool
\seq_map_inline:Nn \l_tmpa_seq {
\bool_if:NF \l_tmpa_bool { & }
\bool_set_false:N \l_tmpa_bool
#3 { \__nguh_gloss_rescan:n {##1} }
}
}
%% Format a gloss.
%%
%% This iterates over all lines in #1 and calls #2 on it.
\cs_new:Npn \__nguh_gloss_format:nn #1 #2 {
\int_gset:Nn \g_tmpb_int { 1 }
\tl_map_inline:nn { #1 } {
\tl_if_blank:nF { ##1 } {
%% Insert \cr or table header if this is the first line. We
%% need to emit this here, as otherwise, TeX will encounter
%% unexpandable tokens before the formatter (#2) is executed,
%% which will start the cell before any actual content has
%% been inserted; this means we can no longer tell TeX to
%% \omit the cell header, which causes \multicolumn to break
%% horribly.
%%
%% By emitting the table header here, we ensure that instead,
%% the formatter is the first thing TeX gets to see after the
%% initial table header and after every \cr.
\int_compare:nNnTF { \g_tmpb_int } > { 1 } { \\ } { \__nguh_gloss_insert_table_header: }
#2 { ##1 }
\int_gincr:N \g_tmpb_int
}
}
}
%% Two lines means object language + gloss.
\cs_new:Npn \__nguh_gloss_two_lines:n #1 {
\cs_gset:Npn \__formatter:n ##1 {
\__nguh_gloss_format_line:nnn { ##1 } {} {
\int_compare:nNnTF { \g_tmpb_int } = { 1 } { \itshape } { \scshape }
}
}
\__nguh_gloss_format:nn { #1 } { \__formatter:n }
}
%% Full gloss (text, object language, pronunciation, gloss, and translation).
\cs_new:Npn \__nguh_gloss_five_lines:n #1 {
\cs_gset:Npn \__formatter_i:n ##1 { \multicolumn {100} {@{}l} {\itshape\bfseries\__nguh_gloss_rescan:n {##1}} }
\cs_gset:Npn \__formatter_ii:n ##1 { \__nguh_gloss_format_line:nnn {##1} {} {\itshape} }
\cs_gset:Npn \__formatter_iii:n ##1 { \__nguh_gloss_format_line:nnn {##1} {} {} }
\cs_gset:Npn \__formatter_iv:n ##1 { \__nguh_gloss_format_line:nnn {##1} {} {\scshape} }
\cs_gset:Npn \__formatter_v:n ##1 { \multicolumn {100} {@{}l} {\__nguh_gloss_rescan:n {##1}} }
\__nguh_gloss_format:nn { #1 } {
\cs:w __formatter_ \int_to_roman:n \g_tmpb_int :n \cs_end:
}
}
%% Full gloss w/o IPA (text, object language, gloss, and translation).
\cs_new:Npn \__nguh_gloss_four_lines:n #1 {
\cs_gset:Npn \__formatter_i:n ##1 { \multicolumn {100} {@{}l} {\itshape\bfseries \__nguh_gloss_rescan:n {##1} } }
\cs_gset:Npn \__formatter_ii:n ##1 { \__nguh_gloss_format_line:nnn {##1} {} {\itshape} }
\cs_gset:Npn \__formatter_iii:n ##1 { \__nguh_gloss_format_line:nnn {##1} {} {\scshape} }
\cs_gset:Npn \__formatter_iv:n ##1 { \multicolumn {100} {@{}l} {\__nguh_gloss_rescan:n {##1}} }
\__nguh_gloss_format:nn { #1 } {
\cs:w __formatter_ \int_to_roman:n \g_tmpb_int :n \cs_end:
}
}
%% Process two words. Used by \multigloss.
\cs_new:Npn \__nguh_multigloss_word:nn #1#2 {
\allowbreak
\hbox {
\begin{tabular}{@{}l}
\tl_use:N \g__nguh_multigloss_first_tl \__nguh_gloss_rescan:n {#1} \\
\noalign{\vskip-6pt}
\tl_use:N \g__nguh_multigloss_second_tl \__nguh_gloss_rescan:n {#2} \\
\end{tabular}
}
\space
}
%% Process two lines. Used by \multigloss.
\cs_new:Npn \__nguh_multigloss:NN #1 #2 {
\ifvmode\noindent\leavevmode\fi
%% Split the lines into words.
\seq_set_split_keep_spaces:Nnx \l_tmpa_seq { | } { #1 }
\seq_set_split_keep_spaces:Nnx \l_tmpb_seq { | } { #2 }
%% Iterate over each word in the two lines.
\seq_mapthread_function:NNN \l_tmpa_seq \l_tmpb_seq \__nguh_multigloss_word:nn
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Interface
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\NewDocumentCommand \gloss {
> { \exp_args:Nx \SplitList { \iow_char:N \^^M } } +v
} {
\__nguh_gloss_table_start:
%% Count lines.
\int_gset:Nn \g_tmpa_int { 0 }
\tl_map_inline:nn { #1 } {
\tl_if_blank:nF { ##1 } {
\int_gincr:N \g_tmpa_int
}
}
%% Dispatch the appropriate number of lines.
\int_case:nnF { \g_tmpa_int } {
2 { \__nguh_gloss_two_lines:n { #1 } }
4 { \__nguh_gloss_four_lines:n { #1 } }
5 { \__nguh_gloss_five_lines:n { #1 } }
} {
%% Any other line count is an error.
\msg_new:nnn { gloss } { too-many-lines } {
Unexpected~number~of~lines~in~gloss:~expected~2/4/5~got~\int_use:N \g_tmpa_int
}
\msg_error:nn { gloss } { too-many-lines }
}
%% Close the table.
\__nguh_gloss_table_end:
}
%% Typeset two-line glosses across multiple lines.
\NewDocumentCommand \multigloss {
> { \exp_args:Nx \SplitList { \iow_char:N \^^M } } +v
} {
\__nguh_end_par:
\group_begin:
\linespread { 1.5 } \selectfont
\raggedright
\begin{sloppypar}
%% Iterate over each line, two lines at a time.
\bool_gset_true:N \g_tmpa_bool
\tl_map_inline:nn { #1 } {
%% Ignore empty lines entirely.
\tl_if_blank:nF { ##1 } {
\bool_if:NTF \g_tmpa_bool {
\tl_gset:Nn \g_tmpa_tl { ##1 }
} {
\tl_gset:Nn \g_tmpb_tl { ##1 }
\__nguh_multigloss:NN \g_tmpa_tl \g_tmpb_tl
}
%% Flip.
\bool_gset_inverse:N \g_tmpa_bool
}
}
\end{sloppypar}
\group_end:
}
\NewDocumentCommand \MultiglossFormatFirst { m } {
\tl_gset:Nn \g__nguh_multigloss_first_tl { #1 }
}
\NewDocumentCommand \MultiglossFormatSecond { m } {
\tl_gset:Nn \g__nguh_multigloss_second_tl { #1 }
}