texmathcodes.c /size: 10 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    We support the traditional math codes as well as larger ones suitable for \UNICODE\ input and
10    fonts.
11
12*/
13
14/*tex the |0xFFFFFFFF| is a flag value. */
15
16# define MATHCODESTACK   8
17# define MATHCODESTEP    8
18# define MATHCODEDEFAULT 0xFFFFFFFF
19# define MATHCODEACTIVE  0xFFFFFFFE
20
21/*tex Delcodes are also went larger. */
22
23# define DELCODESTACK   4
24# define DELCODESTEP    4
25# define DELCODEDEFAULT 0xFFFFFFFF
26
27typedef struct mathcode_state_info {
28    sa_tree mathcode_head;
29    sa_tree delcode_head;
30} mathcode_state_info;
31
32static mathcode_state_info lmt_mathcode_state = {
33    .mathcode_head = NULL,
34    .delcode_head  = NULL,
35};
36
37/*tex
38
39    We now get lots of helpers for definitions and printing. The storage model that we use is
40    different because we can have many more so we need to be sparse. Therefore we use trees.
41
42*/
43
44# define print_hex_digit_one(A) do { \
45    if ((A) >= 10) { \
46        tex_print_char('A' + (A) - 10); \
47    } else { \
48        tex_print_char('0' + (A)); \
49    } \
50} while (0)
51
52# define print_hex_digit_two(A) do { \
53    print_hex_digit_one((A) / 16); \
54    print_hex_digit_one((A) % 16); \
55} while (0)
56
57# define print_hex_digit_four(A) do { \
58    print_hex_digit_two((A) / 256); \
59    print_hex_digit_two((A) % 256); \
60} while (0)
61
62# define print_hex_digit_six(A) do { \
63    print_hex_digit_two( (A) / 65536); \
64    print_hex_digit_two(((A) % 65536) / 256); \
65    print_hex_digit_two( (A)          % 256); \
66} while (0)
67
68/* 0xFFFFF is plenty for math */
69
70mathcodeval tex_mathchar_from_integer(int value, int extcode)
71{
72    mathcodeval mval;
73    if (extcode == tex_mathcode) {
74        mval.class_value = math_old_class_part(value);
75        mval.family_value = math_old_family_part(value);
76        mval.character_value = math_old_character_part(value);
77    } else {
78        mval.class_value = math_class_part(value);
79        mval.family_value = math_family_part(value);
80        mval.character_value = math_character_part(value);
81    }
82    return mval;
83}
84
85mathcodeval tex_mathchar_from_spec(int value)
86{
87    mathcodeval mval = tex_no_math_code();
88    if (value) {
89        mval.class_value = math_spec_class(value);
90        mval.family_value = math_spec_family(value);
91        mval.character_value = math_spec_character(value);
92    }
93    return mval;
94}
95
96void tex_show_mathcode_value(mathcodeval mval, int extcode) /* todo: format option */
97{
98    tex_print_char('"');
99    if (extcode == tex_mathcode) {
100        print_hex_digit_one(math_old_class_mask(mval.class_value));
101        print_hex_digit_one(math_old_family_mask(mval.family_value));
102        print_hex_digit_two(math_old_character_mask(mval.character_value));
103    } else {
104        print_hex_digit_two(mval.class_value);
105        tex_print_char('"');
106        print_hex_digit_two(mval.family_value);
107        tex_print_char('"');
108        print_hex_digit_six(mval.character_value);
109    }
110}
111
112static void tex_aux_show_mathcode(int n)
113{
114    mathcodeval mval = tex_get_math_code(n);
115    tex_print_format("%eUmathcode%i=", n);
116    tex_show_mathcode_value(mval, umath_mathcode);
117}
118
119static void tex_aux_unsave_mathcode(int level)
120{
121    if (lmt_mathcode_state.mathcode_head->stack) {
122        while (lmt_mathcode_state.mathcode_head->sa_stack_ptr > 0 && abs(lmt_mathcode_state.mathcode_head->stack[lmt_mathcode_state.mathcode_head->sa_stack_ptr].level) >= level) {
123            sa_stack_item item = lmt_mathcode_state.mathcode_head->stack[lmt_mathcode_state.mathcode_head->sa_stack_ptr];
124            if (item.level > 0) {
125                sa_rawset_item_4(lmt_mathcode_state.mathcode_head, item.code, item.value_1);
126                if (tracing_restores_par > 1) {
127                    tex_begin_diagnostic();
128                    tex_print_str("{restoring ");
129                    tex_aux_show_mathcode(item.code);
130                    tex_print_char('}');
131                    tex_end_diagnostic();
132                }
133            }
134            (lmt_mathcode_state.mathcode_head->sa_stack_ptr)--;
135        }
136    }
137}
138
139mathcodeval tex_no_math_code(void)
140{
141    return (mathcodeval) { 0, 0, 0 };
142}
143
144void tex_set_math_code(int n, mathcodeval v, int level)
145{
146    sa_tree_item item;
147    if (v.class_value == active_math_class_value && v.family_value == 0 && v.character_value == 0) {
148        item.uint_value = MATHCODEACTIVE;
149    } else if (v.class_value == 0 && v.family_value == 0) {
150        /*tex This is rather safe because we don't decide on it. */
151        item.uint_value = MATHCODEDEFAULT;
152    } else {
153        item.math_code_value.class_value = v.class_value;
154        item.math_code_value.family_value = v.family_value;
155        item.math_code_value.character_value = v.character_value;
156    }
157    sa_set_item_4(lmt_mathcode_state.mathcode_head, n, item, level);
158    if (tracing_assigns_par > 1) {
159        tex_begin_diagnostic();
160        tex_print_str("{assigning ");
161        tex_aux_show_mathcode(n);
162        tex_print_char('}');
163        tex_end_diagnostic();
164    }
165}
166
167mathcodeval tex_get_math_code(int n)
168{
169    sa_tree_item item;
170    mathcodeval m = tex_no_math_code();
171    sa_get_item_4(lmt_mathcode_state.mathcode_head, n, &item);
172    if (item.uint_value == MATHCODEDEFAULT) {
173        m.character_value = n;
174    } else if (item.uint_value == MATHCODEACTIVE) {
175        m.class_value = active_math_class_value;
176    } else if (item.math_code_value.class_value == active_math_class_value) {
177        m.class_value = active_math_class_value;
178        m.character_value = n;
179    } else {
180        m.class_value = (short) item.math_code_value.class_value;
181        m.family_value = (short) item.math_code_value.family_value;
182        m.character_value = item.math_code_value.character_value;
183    }
184    return m;
185}
186
187int tex_get_math_code_number(int n) /* should be unsigned */
188{
189    mathcodeval d = tex_get_math_code(n);
190    return math_packed_character(d.class_value, d.family_value, d.character_value);
191}
192
193static void tex_aux_initialize_mathcode(void)
194{
195    lmt_mathcode_state.mathcode_head = sa_new_tree(mathcode_sparse_identifier, MATHCODESTACK, MATHCODESTEP, 4, (sa_tree_item) { .uint_value = MATHCODEDEFAULT });
196}
197
198static void tex_aux_dump_mathcode(dumpstream f)
199{
200    sa_dump_tree(f, lmt_mathcode_state.mathcode_head);
201}
202
203static void tex_aux_undump_mathcode(dumpstream f)
204{
205    lmt_mathcode_state.mathcode_head = sa_undump_tree(f);
206}
207
208static void tex_aux_show_delcode(int n)
209{
210    delcodeval dval = tex_get_del_code(n);
211    tex_print_format("%eUdelcode=", n);
212    if (tex_has_del_code(dval)) {
213        tex_print_char('"');
214        print_hex_digit_two(dval.small.family_value);
215        print_hex_digit_six(dval.small.character_value);
216    } else {
217        tex_print_str("-1");
218    }
219}
220
221static void tex_aux_unsave_delcode(int level)
222{
223    if (lmt_mathcode_state.delcode_head->stack) {
224        while (lmt_mathcode_state.delcode_head->sa_stack_ptr > 0 && abs(lmt_mathcode_state.delcode_head->stack[lmt_mathcode_state.delcode_head->sa_stack_ptr].level) >= level) {
225            sa_stack_item item = lmt_mathcode_state.delcode_head->stack[lmt_mathcode_state.delcode_head->sa_stack_ptr];
226            if (item.level > 0) {
227                sa_rawset_item_8(lmt_mathcode_state.delcode_head, item.code, item.value_1, item.value_2);
228                if (tracing_restores_par > 1) {
229                    tex_begin_diagnostic();
230                    tex_print_str("{restoring ");
231                    tex_aux_show_delcode(item.code);
232                    tex_print_char('}');
233                    tex_end_diagnostic();
234                }
235            }
236            (lmt_mathcode_state.delcode_head->sa_stack_ptr)--;
237        }
238    }
239}
240
241void tex_set_del_code(int n, delcodeval v, int level)
242{
243    sa_tree_item v1, v2; /* seldom all zero */
244    v1.math_code_value.class_value = v.small.class_value;
245    v1.math_code_value.family_value = v.small.family_value;
246    v1.math_code_value.character_value = v.small.character_value;
247    v2.math_code_value.class_value = v.large.class_value;
248    v2.math_code_value.family_value = v.large.family_value;
249    v2.math_code_value.character_value = v.large.character_value;
250    /*tex Always global! */
251    sa_set_item_8(lmt_mathcode_state.delcode_head, n, v1, v2, level);
252    if (tracing_assigns_par > 1) {
253        tex_begin_diagnostic();
254        tex_print_str("{assigning ");
255        tex_aux_show_delcode(n);
256        tex_print_char('}');
257        tex_end_diagnostic();
258    }
259}
260
261int tex_has_del_code(delcodeval d)
262{
263    return d.small.family_value >= 0;
264}
265
266delcodeval tex_no_del_code(void)
267{
268    return (delcodeval) { { 0, -1, 0 }, { 0, 0, 0 } };
269}
270
271delcodeval tex_get_del_code(int n)
272{
273    sa_tree_item v1, v2;
274    delcodeval d = { { 0, -1, 0 }, { 0, 0, 0} };
275    sa_get_item_8(lmt_mathcode_state.delcode_head, n, &v1, &v2);
276    if (v1.uint_value != DELCODEDEFAULT) {
277        d.small.class_value = (short) v1.math_code_value.class_value;
278        d.small.family_value = (short) v1.math_code_value.family_value;
279        d.small.character_value = v1.math_code_value.character_value;
280        d.large.class_value = (short) v2.math_code_value.class_value;
281        d.large.family_value = (short) v2.math_code_value.family_value;
282        d.large.character_value = v2.math_code_value.character_value;
283    }
284    return d;
285}
286
287/*tex */
288
289mathdictval tex_no_dict_code(void) 
290{
291    return (mathdictval) { 0, 0, 0 };
292}
293
294
295/*tex  This really only works for old-style delcodes! */
296
297int tex_get_del_code_number(int n)
298{
299    delcodeval d = tex_get_del_code(n);
300    if (tex_has_del_code(d)) {
301        return ((d.small.family_value * 256  + d.small.character_value) * 4096 +
302                (d.large.family_value * 256) + d.large.character_value);
303    } else {
304        return -1;
305    }
306}
307
308static void tex_aux_initialize_delcode(void)
309{
310    lmt_mathcode_state.delcode_head = sa_new_tree(delcode_sparse_identifier, DELCODESTACK, DELCODESTEP, 8, (sa_tree_item) { .uint_value = DELCODEDEFAULT });
311}
312
313static void tex_aux_dump_delcode(dumpstream f)
314{
315    sa_dump_tree(f, lmt_mathcode_state.delcode_head);
316}
317
318static void tex_aux_undump_delcode(dumpstream f)
319{
320    lmt_mathcode_state.delcode_head = sa_undump_tree(f);
321}
322
323void tex_unsave_math_codes(int grouplevel)
324{
325    tex_aux_unsave_mathcode(grouplevel);
326    tex_aux_unsave_delcode(grouplevel);
327}
328
329void tex_initialize_math_codes(void)
330{
331    tex_aux_initialize_mathcode();
332    tex_aux_initialize_delcode();
333    /*tex This might become optional: */
334    tex_set_default_math_codes();
335}
336
337void tex_free_math_codes(void)
338{
339    sa_destroy_tree(lmt_mathcode_state.mathcode_head);
340    sa_destroy_tree(lmt_mathcode_state.delcode_head);
341}
342
343void tex_dump_math_codes(dumpstream f)
344{
345    tex_aux_dump_mathcode(f);
346    tex_aux_dump_delcode(f);
347}
348
349void tex_undump_math_codes(dumpstream f)
350{
351    tex_aux_undump_mathcode(f);
352    tex_aux_undump_delcode(f);
353}
354