From 9425ad37ec95a40dc039169031259161c92cc217 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sun, 31 Aug 2025 02:24:11 +0300 Subject: support <:O>: if a label is not found, continue execution after the go-to statement * this is a SciTECO extension - it's not in TECO-11 * Allows for select-case-like constructs with default-clauses as in :Os.^EQa$ !* default *! !s.foo! !* ... *! !s.bar! !* ... *! * Consistent with nOlabel0,label1,...$ if is out of range. Unfortunately this form of computed goto is not applicable when "selecting" by strings or non-consecutive integers. * In order to continue after the <:O> statement, we must keep the program counter along with the label we were looking for. At the end of the macro, the PC is restored instead of throwing an error. * Since that would be very inefficient in loops - where potentially all iterations would result in rescanning till the end of the macro - we now store a completed-flag in the goto table. If it is set while trying to :O to an unknown label, we can just continue execution. --- src/parser.c | 75 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 28 deletions(-) (limited to 'src/parser.c') diff --git a/src/parser.c b/src/parser.c index 6d4cd60..a5e6e4f 100644 --- a/src/parser.c +++ b/src/parser.c @@ -186,41 +186,60 @@ teco_execute_macro(const gchar *macro, gsize macro_len, GError *tmp_error = NULL; - if (!teco_machine_main_step(¯o_machine, macro, macro_len, &tmp_error)) { - if (!g_error_matches(tmp_error, TECO_ERROR, TECO_ERROR_RETURN)) { - /* passes ownership of tmp_error */ - g_propagate_error(error, tmp_error); - goto error_cleanup; + for (;;) { + if (!teco_machine_main_step(¯o_machine, macro, macro_len, &tmp_error)) { + if (!g_error_matches(tmp_error, TECO_ERROR, TECO_ERROR_RETURN)) { + /* passes ownership of tmp_error */ + g_propagate_error(error, tmp_error); + goto error_cleanup; + } + g_error_free(tmp_error); + + /* + * Macro returned - handle like regular + * end of macro, even though some checks + * are unnecessary here. + * macro_pc will still point to the return PC. + */ + g_assert(macro_machine.parent.current == &teco_state_start); + + /* + * Discard all braces, except the current one. + */ + if (!teco_expressions_brace_return(parent_brace_level, teco_error_return_args, error)) + goto error_cleanup; + + /* + * Clean up the loop stack. + * We are allowed to return in loops. + * NOTE: This does not have to be undone. + */ + g_array_remove_range(teco_loop_stack, macro_machine.loop_stack_fp, + teco_loop_stack->len - macro_machine.loop_stack_fp); } - g_error_free(tmp_error); - /* - * Macro returned - handle like regular - * end of macro, even though some checks - * are unnecessary here. - * macro_pc will still point to the return PC. - */ - g_assert(macro_machine.parent.current == &teco_state_start); + if (G_LIKELY(teco_goto_backup_pc < 0)) + break; - /* - * Discard all braces, except the current one. - */ - if (!teco_expressions_brace_return(parent_brace_level, teco_error_return_args, error)) - goto error_cleanup; + /* continue after :Olabel$ */ + macro_machine.macro_pc = teco_goto_backup_pc; + /* macro could have ended in a "lookahead" state */ + macro_machine.parent.current = &teco_state_start; - /* - * Clean up the loop stack. - * We are allowed to return in loops. - * NOTE: This does not have to be undone. - */ - g_array_remove_range(teco_loop_stack, macro_machine.loop_stack_fp, - teco_loop_stack->len - macro_machine.loop_stack_fp); + 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 (macro_machine.parent.must_undo) + teco_undo_flags(macro_machine.flags); + macro_machine.flags.mode = TECO_MODE_NORMAL; + + /* no need to reparse everything in the future */ + macro_machine.goto_table.complete = TRUE; } if (G_UNLIKELY(teco_goto_skip_label.len > 0)) { - g_autofree gchar *label_printable = teco_string_echo(teco_goto_skip_label.data, teco_goto_skip_label.len); - g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Label \"%s\" not found", label_printable); + teco_error_label_set(error, teco_goto_skip_label.data, teco_goto_skip_label.len); goto error_attach; } -- cgit v1.2.3