diff options
Diffstat (limited to 'src/core-commands.c')
-rw-r--r-- | src/core-commands.c | 224 |
1 files changed, 180 insertions, 44 deletions
diff --git a/src/core-commands.c b/src/core-commands.c index 7222a9f..2ccb8ec 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -67,6 +67,23 @@ gboolean teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_mach ##__VA_ARGS__ \ ) +/** + * @class TECO_DEFINE_STATE_START + * @implements TECO_DEFINE_STATE_COMMAND + * @ingroup states + * + * Base state for everything where a new command can begin + * (the start state itself and all lookahead states). + */ +#define TECO_DEFINE_STATE_START(NAME, ...) \ + TECO_DEFINE_STATE_COMMAND(NAME, \ + .end_of_macro_cb = NULL, /* Allowed at the end of a macro! */ \ + .is_start = TRUE, \ + .keymacro_mask = TECO_KEYMACRO_MASK_START | TECO_KEYMACRO_MASK_CASEINSENSITIVE, \ + ##__VA_ARGS__ \ + ) + +static teco_state_t *teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error); static teco_state_t *teco_state_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error); static teco_state_t *teco_state_ctlc_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error); @@ -543,34 +560,173 @@ teco_state_start_cmdline_pop(teco_machine_main_t *ctx, GError **error) g_set_error_literal(error, TECO_ERROR, TECO_ERROR_CMDLINE, ""); } -/*$ "=" print - * <n>= -- Show value as message +/** + * Print number from stack in the given radix. + * + * It must be popped manually, so we can call it multiple times + * on the same number. + */ +static gboolean +teco_print(guint radix, GError **error) +{ + if (!teco_expressions_eval(FALSE, error)) + return FALSE; + if (!teco_expressions_args()) { + teco_error_argexpected_set(error, "="); + return FALSE; + } + /* + * FIXME: There should be a raw output function, + * also to allow output without trailing LF. + * Perhaps repurpose teco_expressions_format(). + */ + const gchar *fmt = "%" TECO_INT_MODIFIER "d"; + switch (radix) { + case 8: fmt = "%" TECO_INT_MODIFIER "o"; break; + case 16: fmt = "%" TECO_INT_MODIFIER "X"; break; + } + teco_interface_msg(TECO_MSG_USER, fmt, teco_expressions_peek_num(0)); + return TRUE; +} + +/*$ "=" "==" "===" print + * <n>= -- Print integer as message + * <n>== + * <n>=== * * Shows integer <n> as a message in the message line and/or * on the console. - * It is currently always formatted as a decimal integer and - * shown with the user-message severity. + * One \(lq=\(rq formats the integer as a signed decimal number, + * \(lq==\(rq formats as an unsigned octal number and + * \(lq===\(rq as an unsigned hexadecimal number. + * It is logged with the user-message severity. * The command fails if <n> is not given. + * + * A noteworthy quirk is that \(lq==\(rq and \(lq===\(rq + * will print 2 or 3 numbers in succession when executed + * from interactive mode at the end of the command line + * in order to guarantee immediate feedback. + * + * If you want to print multiple values from the stack, + * you have to put the \(lq=\(rq into a pass-through loop + * or separate the commands with whitespace. */ -/** - * @todo perhaps care about current radix - * @todo colon-modifier to suppress line-break on console? +/* + * In order to imitate TECO-11 closely, we apply the lookahead + * strategy -- `=` and `==` are not executed immediately but only + * when a non-`=` character is parsed (cf. `$$` and `^C^C`). + * However, this would be very annoying during interactive + * execution, therefore we still print the number immediately + * and perhaps multiple times: + * Typing `===` prints the number first in decimal, + * then octal and finally in hexadecimal. + * This won't happen e.g. in a loop that is closed on the command-line. + * + * FIXME: Support colon-modifier to suppress line-break on console. */ -static void -teco_state_start_print(teco_machine_main_t *ctx, GError **error) +TECO_DECLARE_STATE(teco_state_print_decimal); +TECO_DECLARE_STATE(teco_state_print_octal); + +static gboolean +teco_state_print_decimal_initial(teco_machine_main_t *ctx, GError **error) { - if (!teco_expressions_eval(FALSE, error)) - return; - if (!teco_expressions_args()) { - teco_error_argexpected_set(error, "="); - return; + if (ctx->flags.mode > TECO_MODE_NORMAL || !teco_cmdline_is_executing(ctx)) + return TRUE; + /* + * Interactive invocation: + * don't yet pop number as we may have to print it repeatedly + */ + return teco_print(10, error); +} + +static teco_state_t * +teco_state_print_decimal_input(teco_machine_main_t *ctx, gunichar chr, GError **error) +{ + if (chr == '=') + return &teco_state_print_octal; + + if (ctx->flags.mode == TECO_MODE_NORMAL) { + if (!teco_cmdline_is_executing(ctx) && !teco_print(10, error)) + return NULL; + teco_expressions_pop_num(0); } - teco_int_t v; - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - teco_interface_msg(TECO_MSG_USER, "%" TECO_INT_FORMAT, v); + return teco_state_start_input(ctx, chr, error); +} + +/* + * Due to the deferred nature of `=`, + * it is valid to end in this state as well. + */ +static gboolean +teco_state_print_decimal_end_of_macro(teco_machine_main_t *ctx, GError **error) +{ + if (teco_cmdline_is_executing(ctx) || ctx->flags.mode > TECO_MODE_NORMAL) + return TRUE; + if (!teco_print(10, error)) + return FALSE; + teco_expressions_pop_num(0); + return TRUE; +} + +TECO_DEFINE_STATE_START(teco_state_print_decimal, + .initial_cb = (teco_state_initial_cb_t)teco_state_print_decimal_initial, + .end_of_macro_cb = (teco_state_end_of_macro_cb_t) + teco_state_print_decimal_end_of_macro +); + +static gboolean +teco_state_print_octal_initial(teco_machine_main_t *ctx, GError **error) +{ + if (ctx->flags.mode > TECO_MODE_NORMAL || !teco_cmdline_is_executing(ctx)) + return TRUE; + /* + * Interactive invocation: + * don't yet pop number as we may have to print it repeatedly + */ + return teco_print(8, error); +} + +static teco_state_t * +teco_state_print_octal_input(teco_machine_main_t *ctx, gunichar chr, GError **error) +{ + if (chr == '=') { + if (ctx->flags.mode == TECO_MODE_NORMAL) { + if (!teco_print(16, error)) + return NULL; + teco_expressions_pop_num(0); + } + return &teco_state_start; + } + + if (ctx->flags.mode == TECO_MODE_NORMAL) { + if (!teco_cmdline_is_executing(ctx) && !teco_print(8, error)) + return NULL; + teco_expressions_pop_num(0); + } + return teco_state_start_input(ctx, chr, error); +} + +/* + * Due to the deferred nature of `==`, + * it is valid to end in this state as well. + */ +static gboolean +teco_state_print_octal_end_of_macro(teco_machine_main_t *ctx, GError **error) +{ + if (teco_cmdline_is_executing(ctx) || ctx->flags.mode > TECO_MODE_NORMAL) + return TRUE; + if (!teco_print(8, error)) + return FALSE; + teco_expressions_pop_num(0); + return TRUE; } +TECO_DEFINE_STATE_START(teco_state_print_octal, + .initial_cb = (teco_state_initial_cb_t)teco_state_print_octal_initial, + .end_of_macro_cb = (teco_state_end_of_macro_cb_t) + teco_state_print_octal_end_of_macro +); + /*$ A * [n]A -> code -- Get character code from buffer * -A -> code @@ -651,6 +807,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error) .modifier_colon = 1}, ['X'] = {&teco_state_copytoqreg, .modifier_at = TRUE, .modifier_colon = 1}, + ['='] = {&teco_state_print_decimal}, /* * Arithmetics @@ -703,7 +860,6 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error) .modifier_colon = 1}, ['D'] = {&teco_state_start, teco_state_start_delete_chars, .modifier_colon = 1}, - ['='] = {&teco_state_start, teco_state_start_print}, ['A'] = {&teco_state_start, teco_state_start_get} }; @@ -899,11 +1055,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error) teco_ascii_toupper(chr), error); } -TECO_DEFINE_STATE_COMMAND(teco_state_start, - .end_of_macro_cb = NULL, /* Allowed at the end of a macro! */ - .is_start = TRUE, - .keymacro_mask = TECO_KEYMACRO_MASK_START | TECO_KEYMACRO_MASK_CASEINSENSITIVE -); +TECO_DEFINE_STATE_START(teco_state_start); /*$ "F<" ":F<" * F< -- Go to loop start or jump to beginning of macro @@ -1787,14 +1939,8 @@ teco_state_escape_end_of_macro(teco_machine_t *ctx, GError **error) return teco_expressions_discard_args(error); } -TECO_DEFINE_STATE_COMMAND(teco_state_escape, - .end_of_macro_cb = teco_state_escape_end_of_macro, - /* - * The state should behave like teco_state_start - * when it comes to function key macro masking. - */ - .is_start = TRUE, - .keymacro_mask = TECO_KEYMACRO_MASK_START | TECO_KEYMACRO_MASK_CASEINSENSITIVE +TECO_DEFINE_STATE_START(teco_state_escape, + .end_of_macro_cb = teco_state_escape_end_of_macro ); /* @@ -1827,18 +1973,8 @@ teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error) return TRUE; } -TECO_DEFINE_STATE_COMMAND(teco_state_ctlc, - .initial_cb = (teco_state_initial_cb_t)teco_state_ctlc_initial, - /* - * At the end of a macro, "return" is allowed, but basically a no-op. - */ - .end_of_macro_cb = NULL, - /* - * The state should behave like teco_state_start - * when it comes to function key macro masking. - */ - .is_start = TRUE, - .keymacro_mask = TECO_KEYMACRO_MASK_START | TECO_KEYMACRO_MASK_CASEINSENSITIVE +TECO_DEFINE_STATE_START(teco_state_ctlc, + .initial_cb = (teco_state_initial_cb_t)teco_state_ctlc_initial ); static teco_state_t * |