diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2024-11-22 16:59:07 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2024-11-23 02:33:49 +0300 |
commit | 1cfe37610253c20a4fcb0d937c29e70894ecc4f5 (patch) | |
tree | ac05844c25fc0918e8fd451d8912fd7f1964acb7 /src | |
parent | 07b52f78680858683acb4e40b158f8926285cae4 (diff) | |
download | sciteco-1cfe37610253c20a4fcb0d937c29e70894ecc4f5.tar.gz |
the search mode and current radix are mapped to __local__ Q-Registers ^X and ^R now (refs #17)
* This way the search mode and radix are local to the current macro frame,
unless the macro was invoked with :Mq.
If colon-modified, you can reproduce the same effect by calling
[.^X 0^X ... ].^X
* The radix register is cached in the Q-Reg table as an optimization.
This could be done with the other "special" registers as well, but at the
cost of larger stack frames.
* In order to allow constructs like [.^X typed with upcarets,
the Q-Register specification syntax has been extended:
^c is the corresponding control code instead of the register "^".
Diffstat (limited to 'src')
-rw-r--r-- | src/core-commands.c | 55 | ||||
-rw-r--r-- | src/expressions.c | 25 | ||||
-rw-r--r-- | src/expressions.h | 15 | ||||
-rw-r--r-- | src/main.c | 7 | ||||
-rw-r--r-- | src/parser.c | 4 | ||||
-rw-r--r-- | src/qreg-commands.c | 5 | ||||
-rw-r--r-- | src/qreg.c | 43 | ||||
-rw-r--r-- | src/qreg.h | 7 | ||||
-rw-r--r-- | src/search.c | 27 |
9 files changed, 144 insertions, 44 deletions
diff --git a/src/core-commands.c b/src/core-commands.c index ca0245b..ba7aaa8 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -196,7 +196,8 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error) return; gchar buffer[TECO_EXPRESSIONS_FORMAT_LEN]; - gchar *str = teco_expressions_format(buffer, value); + gchar *str = teco_expressions_format(buffer, value, + ctx->qreg_table_locals->radix); g_assert(*str != '\0'); teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); @@ -207,6 +208,12 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error) if (teco_current_doc_must_undo()) undo__teco_interface_ssm(SCI_UNDO, 0, 0); } else { + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + assert(qreg != NULL); + teco_int_t radix; + if (!qreg->vtable->get_integer(qreg, &radix, error)) + return; + uptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); gchar c = (gchar)teco_interface_ssm(SCI_GETCHARAT, pos, 0); teco_int_t v = 0; @@ -219,11 +226,11 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error) for (;;) { c = teco_ascii_toupper((gchar)teco_interface_ssm(SCI_GETCHARAT, pos, 0)); - if (c >= '0' && c <= '0' + MIN(teco_radix, 10) - 1) - v = (v*teco_radix) + (c - '0'); + if (c >= '0' && c <= '0' + MIN(radix, 10) - 1) + v = (v*radix) + (c - '0'); else if (c >= 'A' && - c <= 'A' + MIN(teco_radix - 10, 26) - 1) - v = (v*teco_radix) + 10 + (c - 'A'); + c <= 'A' + MIN(radix - 10, 26) - 1) + v = (v*radix) + 10 + (c - 'A'); else break; @@ -1164,7 +1171,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error) */ case '0' ... '9': if (ctx->mode == TECO_MODE_NORMAL) - teco_expressions_add_digit(chr); + teco_expressions_add_digit(chr, ctx->qreg_table_locals->radix); return &teco_state_start; case '*': @@ -1710,7 +1717,11 @@ teco_state_control_exit(teco_machine_main_t *ctx, GError **error) static void teco_state_control_octal(teco_machine_main_t *ctx, GError **error) { - teco_set_radix(8); + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + assert(qreg != NULL); + if (!qreg->vtable->undo_set_integer(qreg, error) || + !qreg->vtable->set_integer(qreg, 8, NULL)) + return; } /*$ ^D decimal @@ -1719,7 +1730,11 @@ teco_state_control_octal(teco_machine_main_t *ctx, GError **error) static void teco_state_control_decimal(teco_machine_main_t *ctx, GError **error) { - teco_set_radix(10); + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + assert(qreg != NULL); + if (!qreg->vtable->undo_set_integer(qreg, error) || + !qreg->vtable->set_integer(qreg, 10, NULL)) + return; } /*$ ^R radix @@ -1729,19 +1744,35 @@ teco_state_control_decimal(teco_machine_main_t *ctx, GError **error) * Set current radix to arbitrary value <radix>. * If <radix> is omitted, the command instead * returns the current radix. + * + * An alternative way to access the radix is via the "^R" local Q-Register. + * Consequently, the radix is local to the current macro invocation frame, + * unless the macro call was colon-modified. */ static void teco_state_control_radix(teco_machine_main_t *ctx, GError **error) { if (!teco_expressions_eval(FALSE, error)) return; + + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + assert(qreg != NULL); + teco_int_t radix; + if (!teco_expressions_args()) { - teco_expressions_push(teco_radix); + if (!qreg->vtable->get_integer(qreg, &radix, error)) + return; + teco_expressions_push(radix); } else { - teco_int_t v; - if (!teco_expressions_pop_num_calc(&v, 0, error)) + /* + * FIXME: We should restrict the allowed values. + * 0^R 23\ crashes for instance. + * The ^R register should consequently also be "special". + */ + if (!teco_expressions_pop_num_calc(&radix, 0, error) || + !qreg->vtable->undo_set_integer(qreg, error) || + !qreg->vtable->set_integer(qreg, radix, error)) return; - teco_set_radix(v); } } diff --git a/src/expressions.c b/src/expressions.c index ee6b4dc..25213e9 100644 --- a/src/expressions.c +++ b/src/expressions.c @@ -23,6 +23,7 @@ #include "sciteco.h" #include "error.h" #include "undo.h" +#include "qreg.h" #include "expressions.h" /* @@ -57,7 +58,6 @@ teco_expressions_precedence(teco_operator_t op) } gint teco_num_sign = 1; -gint teco_radix = 10; void teco_expressions_push_int(teco_int_t number) @@ -114,12 +114,20 @@ teco_expressions_pop_num_calc(teco_int_t *ret, teco_int_t imply, GError **error) } void -teco_expressions_add_digit(gunichar digit) +teco_expressions_add_digit(gunichar digit, teco_qreg_t *qreg) { + /* + * FIXME: We could just access qreg->integer here since + * we can assume that "^R" is a plain register. + */ + assert(qreg != NULL); + teco_int_t radix = 10; + qreg->vtable->get_integer(qreg, &radix, NULL); + teco_int_t n = teco_expressions_args() > 0 ? teco_expressions_pop_num(0) : 0; /* use g_unichar_digit_value()? */ - teco_expressions_push(n*teco_radix + (n < 0 ? -1 : 1)*((gint)digit - '0')); + teco_expressions_push(n*radix + (n < 0 ? -1 : 1)*((gint)digit - '0')); } void @@ -378,21 +386,26 @@ teco_expressions_clear(void) * @param buffer The output buffer of at least TECO_EXPRESSIONS_FORMAT_LEN characters. * The output string will be null-terminated. * @param number The number to format. + * @param table The local Q-Register table that contains the appropriate radix register (^R). * @return A pointer into buffer to the beginning of the formatted number. */ gchar * -teco_expressions_format(gchar *buffer, teco_int_t number) +teco_expressions_format(gchar *buffer, teco_int_t number, teco_qreg_t *qreg) { + assert(qreg != NULL); + teco_int_t radix = 10; + qreg->vtable->get_integer(qreg, &radix, NULL); + gchar *p = buffer + TECO_EXPRESSIONS_FORMAT_LEN; teco_int_t v = ABS(number); *--p = '\0'; do { - *--p = '0' + (v % teco_radix); + *--p = '0' + (v % radix); if (*p > '9') *p += 'A' - '9' - 1; - } while ((v /= teco_radix)); + } while ((v /= radix)); if (number < 0) *--p = '-'; diff --git a/src/expressions.h b/src/expressions.h index 68d8ddb..caea1d7 100644 --- a/src/expressions.h +++ b/src/expressions.h @@ -19,6 +19,7 @@ #include <glib.h> #include "sciteco.h" +#include "qreg.h" #include "undo.h" /** @@ -101,14 +102,6 @@ teco_set_num_sign(gint sign) teco_undo_gint(teco_num_sign) = sign; } -extern gint teco_radix; - -static inline void -teco_set_radix(gint r) -{ - teco_undo_gint(teco_radix) = r; -} - void teco_expressions_push_int(teco_int_t number); /** Push characters of a C-string. */ @@ -123,7 +116,7 @@ teco_int_t teco_expressions_peek_num(guint index); teco_int_t teco_expressions_pop_num(guint index); gboolean teco_expressions_pop_num_calc(teco_int_t *ret, teco_int_t imply, GError **error); -void teco_expressions_add_digit(gunichar digit); +void teco_expressions_add_digit(gunichar digit, teco_qreg_t *radix); void teco_expressions_push_op(teco_operator_t op); gboolean teco_expressions_push_calc(teco_operator_t op, GError **error); @@ -155,8 +148,8 @@ gboolean teco_expressions_brace_close(GError **error); void teco_expressions_clear(void); -/** Maximum size required to format a number if teco_radix == 2 */ +/** Maximum size required to format a number if radix == 2 */ #define TECO_EXPRESSIONS_FORMAT_LEN \ (1 + sizeof(teco_int_t)*8 + 1) -gchar *teco_expressions_format(gchar *buffer, teco_int_t number); +gchar *teco_expressions_format(gchar *buffer, teco_int_t number, teco_qreg_t *radix); @@ -390,6 +390,11 @@ main(int argc, char **argv) teco_qreg_view = teco_view_new(); teco_view_setup(teco_qreg_view); + /* + * FIXME: "_" and "-" should perhaps be in the local Q-Reg table, so you don't + * have to back them up on the Q-Reg stack in portable macros. + * DEC TECO has them in the global table, though. + */ /* search string and status register */ teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_plain_new("_", 1)); /* replacement string register */ @@ -402,7 +407,7 @@ main(int argc, char **argv) teco_initialize_environment(); teco_qreg_table_t local_qregs; - teco_qreg_table_init(&local_qregs, TRUE); + teco_qreg_table_init_locals(&local_qregs, TRUE); if (!teco_ring_edit_by_name(NULL, &error)) { g_fprintf(stderr, "Error editing unnamed file: %s\n", diff --git a/src/parser.c b/src/parser.c index 4ee6a90..9477b9a 100644 --- a/src/parser.c +++ b/src/parser.c @@ -176,7 +176,7 @@ teco_execute_macro(const gchar *macro, gsize macro_len, teco_qreg_table_t macro_locals; if (!qreg_table_locals) - teco_qreg_table_init(¯o_locals, FALSE); + teco_qreg_table_init_locals(¯o_locals, FALSE); guint parent_brace_level = teco_brace_level; @@ -670,7 +670,7 @@ teco_state_stringbuilding_ctle_num_input(teco_machine_stringbuilding_t *ctx, gun * NOTE: Numbers can always be safely formatted as null-terminated strings. */ gchar buffer[TECO_EXPRESSIONS_FORMAT_LEN]; - const gchar *num = teco_expressions_format(buffer, value); + const gchar *num = teco_expressions_format(buffer, value, ctx->qreg_table_locals->radix); teco_machine_stringbuilding_append(ctx, num, strlen(num)); return &teco_state_stringbuilding_start; diff --git a/src/qreg-commands.c b/src/qreg-commands.c index d593af8..89618da 100644 --- a/src/qreg-commands.c +++ b/src/qreg-commands.c @@ -654,7 +654,7 @@ teco_state_macro_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, return NULL; } else { g_auto(teco_qreg_table_t) table; - teco_qreg_table_init(&table, FALSE); + teco_qreg_table_init_locals(&table, FALSE); if (!teco_qreg_execute(qreg, &table, error)) return NULL; @@ -711,7 +711,8 @@ teco_state_macrofile_done(teco_machine_main_t *ctx, const teco_string_t *str, GE return NULL; } else { g_auto(teco_qreg_table_t) table; - teco_qreg_table_init(&table, FALSE); + teco_qreg_table_init_locals(&table, FALSE); + if (!teco_execute_file(filename, &table, error)) return NULL; } @@ -893,6 +893,7 @@ teco_qreg_clipboard_new(const gchar *name) void teco_qreg_table_init(teco_qreg_table_t *table, gboolean must_undo) { + memset(table, 0, sizeof(*table)); rb3_reset_tree(&table->tree); table->must_undo = must_undo; @@ -903,6 +904,20 @@ teco_qreg_table_init(teco_qreg_table_t *table, gboolean must_undo) teco_qreg_table_insert(table, teco_qreg_plain_new(&q, sizeof(q))); } +/** @memberof teco_qreg_table_t */ +void +teco_qreg_table_init_locals(teco_qreg_table_t *table, gboolean must_undo) +{ + teco_qreg_table_init(table, must_undo); + + /* search mode ("^X") */ + teco_qreg_table_insert(table, teco_qreg_plain_new("\x18", 1)); + /* numeric radix ("^R") */ + table->radix = teco_qreg_plain_new("\x12", 1); + table->radix->vtable->set_integer(table->radix, 10, NULL); + teco_qreg_table_insert(table, table->radix); +} + static inline void teco_qreg_table_remove(teco_qreg_t *reg) { @@ -1208,7 +1223,7 @@ teco_ed_hook(teco_ed_hook_t type, GError **error) * since it runs all destructors. */ g_auto(teco_qreg_table_t) locals; - teco_qreg_table_init(&locals, FALSE); + teco_qreg_table_init_locals(&locals, FALSE); teco_qreg_t *qreg = teco_qreg_table_find(&teco_qreg_table_globals, "ED", 2); if (!qreg) { @@ -1297,6 +1312,7 @@ struct teco_machine_qregspec_t { */ TECO_DECLARE_STATE(teco_state_qregspec_start); TECO_DECLARE_STATE(teco_state_qregspec_start_global); +TECO_DECLARE_STATE(teco_state_qregspec_caret); TECO_DECLARE_STATE(teco_state_qregspec_firstchar); TECO_DECLARE_STATE(teco_state_qregspec_secondchar); TECO_DECLARE_STATE(teco_state_qregspec_string); @@ -1364,10 +1380,10 @@ TECO_DEFINE_STATE(teco_state_qregspec_start, static teco_state_t * teco_state_qregspec_start_global_input(teco_machine_qregspec_t *ctx, gunichar chr, GError **error) { - /* - * FIXME: Disallow space characters? - */ switch (chr) { + case '^': + return &teco_state_qregspec_caret; + case '#': return &teco_state_qregspec_firstchar; @@ -1397,6 +1413,25 @@ TECO_DEFINE_STATE(teco_state_qregspec_start_global, ); static teco_state_t * +teco_state_qregspec_caret_input(teco_machine_qregspec_t *ctx, gunichar chr, GError **error) +{ + chr = teco_ascii_toupper(chr); + if (chr < '@' || chr > '_') { + teco_error_syntax_set(error, chr); + return NULL; + } + + if (!ctx->parse_only) { + if (ctx->parent.must_undo) + undo__teco_string_truncate(&ctx->name, ctx->name.len); + teco_string_append_wc(&ctx->name, TECO_CTL_KEY(chr)); + } + return teco_state_qregspec_done(ctx, error); +} + +TECO_DEFINE_STATE_CASEINSENSITIVE(teco_state_qregspec_caret); + +static teco_state_t * teco_state_qregspec_firstchar_input(teco_machine_qregspec_t *ctx, gunichar chr, GError **error) { /* @@ -144,9 +144,16 @@ struct teco_qreg_table_t { * b) The top-level local register table. */ gboolean must_undo; + + /** + * The radix register in this local Q-Register table or NULL. + * This is an optimization to avoid frequent table lookups. + */ + teco_qreg_t *radix; }; void teco_qreg_table_init(teco_qreg_table_t *table, gboolean must_undo); +void teco_qreg_table_init_locals(teco_qreg_table_t *table, gboolean must_undo); /** @memberof teco_qreg_table_t */ static inline teco_qreg_t * diff --git a/src/search.c b/src/search.c index ec62d02..2059da3 100644 --- a/src/search.c +++ b/src/search.c @@ -52,8 +52,6 @@ TECO_DEFINE_UNDO_OBJECT_OWN(parameters, teco_search_parameters_t, /* don't delet */ static teco_search_parameters_t teco_search_parameters; -static teco_bool_t teco_search_mode = TECO_FAILURE; /* case-insensitive */ - /*$ ^X search-mode * mode^X -- Set or get search mode flag * -^X @@ -65,17 +63,29 @@ static teco_bool_t teco_search_mode = TECO_FAILURE; /* case-insensitive */ * searches. * "-^X" is equivalent to "-1^X" and also enables case-sensitive searches. * Searches are case-insensitive by default. + * + * An alternative way to access the search mode is via the "^X" local Q-Register. + * Consequently, the search mode is local to the current macro invocation frame, + * unless the macro call was colon-modified. */ void teco_state_control_search_mode(teco_machine_main_t *ctx, GError **error) { if (!teco_expressions_eval(FALSE, error)) return; + + teco_qreg_t *reg = teco_qreg_table_find(ctx->qreg_table_locals, "\x18", 1); /* ^X */ + g_assert(reg != NULL); + teco_bool_t search_mode; + if (!teco_expressions_args() && teco_num_sign > 0) { - teco_expressions_push(teco_search_mode); + if (!reg->vtable->get_integer(reg, &search_mode, error)) + return; + teco_expressions_push(search_mode); } else { - teco_undo_int(teco_search_mode); - if (!teco_expressions_pop_num_calc(&teco_search_mode, teco_num_sign, error)) + if (!teco_expressions_pop_num_calc(&search_mode, teco_num_sign, error) || + !reg->vtable->undo_set_integer(reg, error) || + !reg->vtable->set_integer(reg, search_mode, error)) return; } } @@ -629,7 +639,12 @@ teco_state_search_process(teco_machine_main_t *ctx, const teco_string_t *str, gs /* FIXME: Should G_REGEX_OPTIMIZE be added under certain circumstances? */ GRegexCompileFlags flags = G_REGEX_MULTILINE | G_REGEX_DOTALL; - if (teco_is_failure(teco_search_mode)) + teco_qreg_t *reg = teco_qreg_table_find(ctx->qreg_table_locals, "\x18", 1); /* ^X */ + g_assert(reg != NULL); + teco_bool_t search_mode; + if (!reg->vtable->get_integer(reg, &search_mode, error)) + return FALSE; + if (teco_is_failure(search_mode)) flags |= G_REGEX_CASELESS; /* this is set in teco_state_search_initial() */ |