diff options
Diffstat (limited to 'source/luametatex/source/tex/texmarks.c')
-rw-r--r-- | source/luametatex/source/tex/texmarks.c | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texmarks.c b/source/luametatex/source/tex/texmarks.c new file mode 100644 index 000000000..060c5f579 --- /dev/null +++ b/source/luametatex/source/tex/texmarks.c @@ -0,0 +1,346 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" + +/*tex + + A control sequence that has been |\def|'ed by the user is expanded by \TEX's |macro_call| + procedure. + + Before we get into the details of |macro_call|, however, let's consider the treatment of + primitives like |\topmark|, since they are essentially macros without parameters. The token + lists for such marks are kept in five global arrays of pointers; we refer to the individual + entries of these arrays by symbolic macros |top_mark|, etc. The value of |top_mark (x)|, etc. + is either |null| or a pointer to the reference count of a token list. + + The variable |biggest_used_mark| is an aid to try and keep the code somehwat efficient without + too much extra work: it registers the highest mark class ever instantiated by the user, so the + loops in |fire_up| and |vsplit| do not have to traverse the full range |0 .. biggest_mark|. + + Watch out: zero is always valid and the good old single mark! + + Todo: class -> index + +*/ + +mark_state_info lmt_mark_state = { + .data = NULL, + .min_used = -1, + .max_used = -1, + .mark_data = { + .minimum = min_mark_size, + .maximum = max_mark_size, + .size = memory_data_unset, + .step = stp_mark_size, + .allocated = 0, + .itemsize = sizeof(mark_record), + .top = 0, + .ptr = 0, + .initial = memory_data_unset, + .offset = 0, + }, +}; + +void tex_initialize_marks(void) +{ + /* allocated: minimum + 1 */ + lmt_mark_state.data = aux_allocate_clear_array(sizeof(mark_record), lmt_mark_state.mark_data.minimum, 1); + if (lmt_mark_state.data) { + lmt_mark_state.mark_data.allocated = sizeof(mark_record) * lmt_mark_state.mark_data.minimum; + lmt_mark_state.mark_data.top = lmt_mark_state.mark_data.minimum; + } +} + +void tex_reset_mark(halfword m) +{ + if (m >= lmt_mark_state.mark_data.top) { + int step = lmt_mark_state.mark_data.step; + int size = lmt_mark_state.mark_data.top; + /* regular stepwise bump */ + while (m >= size) { + size += step; + } + /* last resort */ + if (size > lmt_mark_state.mark_data.maximum) { + size = m; + } + if (size <= lmt_mark_state.mark_data.maximum) { + mark_record *tmp = aux_reallocate_array(lmt_mark_state.data, sizeof(mark_record), (size_t) size, 1); + if (tmp) { + lmt_mark_state.data = tmp; + memset(&lmt_mark_state.data[lmt_mark_state.mark_data.top], 0, sizeof(mark_record) * (size - lmt_mark_state.mark_data.top)); + lmt_mark_state.mark_data.top = size; + lmt_mark_state.mark_data.allocated = sizeof(mark_record) * ((size_t) size); + } else { + tex_overflow_error("marks", size); + } + } else { + tex_overflow_error("marks", lmt_mark_state.mark_data.maximum); + } + } + if (m > lmt_mark_state.mark_data.ptr) { + lmt_mark_state.mark_data.ptr = m; + } + tex_wipe_mark(m); +} + +halfword tex_get_mark(halfword m, halfword s) +{ + if (s >= 0 && s <= last_unique_mark_code) { + return lmt_mark_state.data[m][s]; + } else { + return null; + } +} + +void tex_set_mark(halfword m, halfword s, halfword v) +{ + if (s >= 0 && s <= last_unique_mark_code) { + if (lmt_mark_state.data[m][s]) { + tex_delete_token_reference(lmt_mark_state.data[m][s]); + } + if (v) { + tex_add_token_reference(v); + } + lmt_mark_state.data[m][s] = v; + } +} + +int tex_valid_mark(halfword m) { + if (m >= lmt_mark_state.mark_data.top) { + tex_reset_mark(m); + } + return m < lmt_mark_state.mark_data.top; +} + +halfword tex_new_mark(quarterword subtype, halfword class, halfword ptr) +{ + halfword mark = tex_new_node(mark_node, subtype); + mark_index(mark) = class; + mark_ptr(mark) = ptr; + if (lmt_mark_state.min_used < 0) { + lmt_mark_state.min_used = class; + lmt_mark_state.max_used = class; + } else { + if (class < lmt_mark_state.min_used) { + lmt_mark_state.min_used = class; + } + if (class > lmt_mark_state.max_used) { + lmt_mark_state.max_used = class; + } + } + tex_set_mark(class, current_marks_code, ptr); + return mark; +} + +static void tex_aux_print_mark(const char *s, halfword t) +{ + if (t) { + tex_print_token_list(s, token_link(t)); + } +} + +void tex_show_marks() +{ + if (tracing_marks_par > 0 && lmt_mark_state.min_used >= 0) { + tex_begin_diagnostic(); + for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) { + if (tex_has_mark(m)) { + tex_print_format("[mark: class %i, page state]",m); + tex_aux_print_mark("top", tex_get_mark(m, top_marks_code)); + tex_aux_print_mark("first", tex_get_mark(m, first_marks_code)); + tex_aux_print_mark("bot", tex_get_mark(m, bot_marks_code)); + tex_aux_print_mark("split first", tex_get_mark(m, split_first_marks_code)); + tex_aux_print_mark("split bot", tex_get_mark(m, split_bot_marks_code)); + tex_aux_print_mark("current", tex_get_mark(m, current_marks_code)); + } + } + tex_end_diagnostic(); + } +} + +void tex_update_top_marks() +{ + if (lmt_mark_state.min_used >= 0) { + for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) { + halfword bot = tex_get_mark(m, bot_marks_code); + if (bot) { + tex_set_mark(m, top_marks_code, bot); + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: class %i, top becomes bot]", m); + tex_aux_print_mark(NULL, bot); + tex_end_diagnostic(); + } + tex_delete_mark(m, first_marks_code); + } + } + } +} + +void tex_update_first_and_bot_mark(halfword n) +{ + halfword index = mark_index(n); + halfword ptr = mark_ptr(n); + if (node_subtype(n) == reset_mark_value_code) { + /*tex Work in progress. */ + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: index %i, reset]", index); + tex_end_diagnostic(); + } + tex_reset_mark(index); + } else { + /*tex Update the values of |first_mark| and |bot_mark|. */ + halfword first = tex_get_mark(index, first_marks_code); + if (! first) { + tex_set_mark(index, first_marks_code, ptr); + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: index %i, first becomes mark]", index); + tex_aux_print_mark(NULL, ptr); + tex_end_diagnostic(); + } + } + tex_set_mark(index, bot_marks_code, ptr); + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: index %i, bot becomes mark]", index); + tex_aux_print_mark(NULL, ptr); + tex_end_diagnostic(); + } + } +} + +void tex_update_first_marks(void) +{ + if (lmt_mark_state.min_used >= 0) { + for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) { + halfword top = tex_get_mark(m, top_marks_code); + halfword first = tex_get_mark(m, first_marks_code); + if (top && ! first) { + tex_set_mark(m, first_marks_code, top); + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: class %i, first becomes top]", m); + tex_aux_print_mark(NULL, top); + tex_end_diagnostic(); + } + } + } + } +} + +void tex_update_split_mark(halfword n) +{ + halfword index = mark_index(n); + halfword ptr = mark_ptr(n); + if (node_subtype(n) == reset_mark_value_code) { + tex_reset_mark(index); + } else { + if (tex_get_mark(index, split_first_marks_code)) { + tex_set_mark(index, split_bot_marks_code, ptr); + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: index %i, split bot becomes mark]", index); + tex_aux_print_mark(NULL, tex_get_mark(index, split_bot_marks_code)); + tex_end_diagnostic(); + } + } else { + tex_set_mark(index, split_first_marks_code, ptr); + tex_set_mark(index, split_bot_marks_code, ptr); + if (tracing_marks_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[mark: index %i, split first becomes mark]", index); + tex_aux_print_mark(NULL, tex_get_mark(index, split_first_marks_code)); + tex_print_format("[mark: index %i, split bot becomes split first]", index); + tex_aux_print_mark(NULL, tex_get_mark(index, split_bot_marks_code)); + tex_end_diagnostic(); + } + } + } +} + + +void tex_delete_mark(halfword m, int what) +{ + switch (what) { + case top_mark_code : what = top_marks_code; + case first_mark_code : what = first_marks_code; + case bot_mark_code : what = bot_marks_code; + case split_first_mark_code: what = split_first_marks_code; + case split_bot_mark_code : what = split_bot_marks_code; + } + tex_set_mark(m, what, null); +} + +halfword tex_get_some_mark(halfword chr, halfword val) +{ + switch (chr) { + case top_mark_code : val = top_marks_code; + case first_mark_code : val = first_marks_code; + case bot_mark_code : val = bot_marks_code; + case split_first_mark_code: val = split_first_marks_code; + case split_bot_mark_code : val = split_bot_marks_code; + } + return tex_get_mark(val, chr); +} + +void tex_wipe_mark(halfword m) +{ + for (int what = 0; what <= last_unique_mark_code; what++) { + tex_set_mark(m, what, null); + } +} + +int tex_has_mark(halfword m) +{ + for (int what = 0; what <= last_unique_mark_code; what++) { + if (lmt_mark_state.data[m][what]) { + return 1; + } + } + return 0; +} + +/*tex + + The |make_mark| procedure has been renamed, because if the current chr code is 1, then the + actual command was |\clearmarks|, which did not generate a mark node but instead destroyed the + current mark related tokenlists. We now have proper reset nodes. + +*/ + +void tex_run_mark(void) +{ + halfword class = 0; + halfword code = cur_chr; + switch (code) { + case set_marks_code: + case clear_marks_code: + case flush_marks_code: + class = tex_scan_mark_number(); + break; + } + if (tex_valid_mark(class)) { + quarterword subtype = set_mark_value_code; + halfword ptr = null; + switch (code) { + case set_marks_code: + case set_mark_code: + ptr = tex_scan_toks_expand(0, NULL, 0); + break; + case clear_marks_code: + tex_wipe_mark(class); + return; + case flush_marks_code: + subtype = reset_mark_value_code; + break; + } + tex_tail_append(tex_new_mark(subtype, class, ptr)); + } else { + /* error already issued */ + } +} |