diff options
Diffstat (limited to 'src/goto-commands.c')
| -rw-r--r-- | src/goto-commands.c | 120 |
1 files changed, 81 insertions, 39 deletions
diff --git a/src/goto-commands.c b/src/goto-commands.c index a0e6634..a9ff3c2 100644 --- a/src/goto-commands.c +++ b/src/goto-commands.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #include <glib.h> #include "sciteco.h" +#include "error.h" #include "string-utils.h" #include "expressions.h" #include "parser.h" @@ -34,17 +35,20 @@ #include "goto.h" #include "goto-commands.h" -TECO_DECLARE_STATE(teco_state_blockcomment); -TECO_DECLARE_STATE(teco_state_eolcomment); +static teco_state_t teco_state_blockcomment; +static teco_state_t teco_state_eolcomment; +/** + * In TECO_MODE_PARSE_ONLY_GOTO mode, we remain in parse-only mode + * until the given label is encountered. + */ teco_string_t teco_goto_skip_label = {NULL, 0}; - -static gboolean -teco_state_label_initial(teco_machine_main_t *ctx, GError **error) -{ - memset(&ctx->goto_label, 0, sizeof(ctx->goto_label)); - return TRUE; -} +/** + * The program counter to restore if the teco_goto_skip_label + * is \b not found (after :Olabel$). + * If smaller than 0 an error is thrown instead. + */ +gssize teco_goto_backup_pc = -1; /* * NOTE: The comma is theoretically not allowed in a label @@ -71,9 +75,10 @@ teco_state_label_input(teco_machine_main_t *ctx, gunichar chr, GError **error) teco_goto_table_undo_remove(&ctx->goto_table, ctx->goto_label.data, ctx->goto_label.len); if (teco_goto_skip_label.len > 0 && - !teco_string_cmp(&ctx->goto_label, teco_goto_skip_label.data, teco_goto_skip_label.len)) { + !teco_string_cmp(ctx->goto_label, teco_goto_skip_label.data, teco_goto_skip_label.len)) { teco_undo_string_own(teco_goto_skip_label); memset(&teco_goto_skip_label, 0, sizeof(teco_goto_skip_label)); + teco_undo_gssize(teco_goto_backup_pc) = -1; if (ctx->parent.must_undo) teco_undo_flags(ctx->flags); @@ -108,28 +113,37 @@ teco_state_label_input(teco_machine_main_t *ctx, gunichar chr, GError **error) } TECO_DEFINE_STATE(teco_state_label, - .initial_cb = (teco_state_initial_cb_t)teco_state_label_initial, - .style = SCE_SCITECO_LABEL + .style = SCE_SCITECO_LABEL, + .input_cb = (teco_state_input_cb_t)teco_state_label_input ); static teco_state_t * -teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +teco_state_goto_done(teco_machine_main_t *ctx, teco_string_t str, GError **error) { if (ctx->flags.mode > TECO_MODE_NORMAL) return &teco_state_start; + if (!str.len) { + /* you can still write @O/,/, though... */ + g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, + "No labels given for <O>"); + return NULL; + } + teco_int_t value; - if (!teco_expressions_pop_num_calc(&value, 1, error)) + if (!teco_expressions_pop_num_calc(&value, 0, error)) return NULL; + gboolean colon_modified = teco_machine_main_eval_colon(ctx) > 0; + /* * Find the comma-separated substring in str indexed by `value`. */ teco_string_t label = {NULL, 0}; - while (value > 0) { - label.data = label.data ? label.data+label.len+1 : str->data; - const gchar *p = label.data ? memchr(label.data, ',', str->len - (label.data - str->data)) : NULL; - label.len = p ? p - label.data : str->len - (label.data - str->data); + while (value >= 0) { + label.data = label.data ? label.data+label.len+1 : str.data; + const gchar *p = label.data ? memchr(label.data, ',', str.len - (label.data - str.data)) : NULL; + label.len = p ? p - label.data : str.len - (label.data - str.data); value--; @@ -137,19 +151,24 @@ teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError break; } - if (value == 0) { + if (value < 0 && label.len > 0) { gssize pc = teco_goto_table_find(&ctx->goto_table, label.data, label.len); if (pc >= 0) { ctx->macro_pc = pc; - } else { + } else if (!ctx->goto_table.complete) { /* skip till label is defined */ g_assert(teco_goto_skip_label.len == 0); undo__teco_string_truncate(&teco_goto_skip_label, 0); teco_string_init(&teco_goto_skip_label, label.data, label.len); + teco_undo_gssize(teco_goto_backup_pc) = colon_modified ? ctx->macro_pc : -1; if (ctx->parent.must_undo) teco_undo_flags(ctx->flags); ctx->flags.mode = TECO_MODE_PARSE_ONLY_GOTO; + } else if (!colon_modified) { + /* can happen if we previously executed a colon-modified go-to */ + teco_error_label_set(error, teco_goto_skip_label.data, teco_goto_skip_label.len); + return NULL; } } @@ -159,38 +178,52 @@ teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError /* in cmdline.c */ gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar chr, GError **error); -gboolean teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, +gboolean teco_state_goto_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error); -/*$ O +/*$ "O" ":O" goto * Olabel$ -- Go to label - * [n]Olabel1[,label2,...]$ + * :Olabel$ + * [n]Olabel0[,label1,...]$ * * Go to <label>. * The simple go-to command is a special case of the * computed go-to command. * A comma-separated list of labels may be specified * in the string argument. - * The label to jump to is selected by <n> (1 is <label1>, - * 2 is <label2>, etc.). - * If <n> is omitted, 1 is implied. + * The label to jump to is selected by <n> (0 is <label0>, + * 1 is <label1>, etc.). + * If <n> is omitted, 0 is implied. + * Computed go-tos can be used like switch-case statements + * other languages. * * If the label selected by <n> is does not exist in the - * list of labels, the command does nothing. + * list of labels or is empty, the command does nothing + * and execution continues normally. * Label definitions are cached in a table, so that * if the label to go to has already been defined, the * go-to command will jump immediately. * Otherwise, parsing continues until the <label> * is defined. * The command will yield an error if a label has - * not been defined when the macro or command-line - * is terminated. - * In the latter case, the user will not be able to - * terminate the command-line. + * not been defined when the macro is terminated. + * When jumping to a non-existent <label> in the + * command-line macro, you cannot practically terminate + * the command-line until defining the <label>. + * + * String building constructs are enabled in \fBO\fP + * which allows for a second kind of computed go-to, + * where the label name contains the value to select. + * When colon-modifying the \fBO\fP command, execution + * will continue after the command if the given <label> + * isn't found. + * This is useful to handle the \(lqdefault\(rq case + * when using computed go-tos of the second kind. */ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_goto, .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_goto_process_edit_cmd, - .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_goto_insert_completion + .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_goto_insert_completion, + .expectstring.done_cb = teco_state_goto_done ); /** @@ -211,25 +244,34 @@ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_goto, ) static teco_state_t * -teco_state_blockcomment_star_input(teco_machine_main_t *ctx, gunichar chr, GError **error) +teco_state_blockcomment_star_input(teco_machine_t *ctx, gunichar chr, GError **error) { return chr == '!' ? &teco_state_start : &teco_state_blockcomment; } -TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment_star); +static TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment_star, + .input_cb = teco_state_blockcomment_star_input +); static teco_state_t * -teco_state_blockcomment_input(teco_machine_main_t *ctx, gunichar chr, GError **error) +teco_state_blockcomment_input(teco_machine_t *ctx, gunichar chr, GError **error) { return chr == '*' ? &teco_state_blockcomment_star : &teco_state_blockcomment; } -TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment); +static TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment, + .input_cb = teco_state_blockcomment_input +); +/* + * `!!` line comments are inspired by TECO-64. + */ static teco_state_t * -teco_state_eolcomment_input(teco_machine_main_t *ctx, gunichar chr, GError **error) +teco_state_eolcomment_input(teco_machine_t *ctx, gunichar chr, GError **error) { return chr == '\n' ? &teco_state_start : &teco_state_eolcomment; } -TECO_DEFINE_STATE_COMMENT(teco_state_eolcomment); +static TECO_DEFINE_STATE_COMMENT(teco_state_eolcomment, + .input_cb = teco_state_eolcomment_input +); |
