diff options
Diffstat (limited to 'src/core-commands.c')
| -rw-r--r-- | src/core-commands.c | 106 |
1 files changed, 85 insertions, 21 deletions
diff --git a/src/core-commands.c b/src/core-commands.c index 6362d95..f80cd59 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -55,6 +55,89 @@ static teco_state_t *teco_state_control_input(teco_machine_main_t *ctx, gunichar static teco_state_t *teco_state_ctlc_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error); static teco_state_t *teco_state_endcond(teco_machine_main_t *ctx, const gchar *cmd, GError **error); +static teco_state_t teco_state_number; + +static gboolean +teco_state_number_end_of_macro(teco_machine_main_t *ctx, GError **error) +{ + teco_undo_guint(ctx->number.radix) = 0; /* default */ + + if (ctx->flags.mode > TECO_MODE_NORMAL) + return TRUE; + + teco_expressions_push(ctx->number.value); + teco_undo_int(ctx->number.value) = 0; + return TRUE; +} + +static teco_state_t * +teco_state_number_input(teco_machine_main_t *ctx, gunichar chr, GError **error) +{ + /* + * Hexadecimal constants (@[0-9][0-9a-zA-Z]*) are terminated by any non-hexadecimal character. + * This means you may have to append a space or braces to stop parsing if the next + * command would begin with such a letter (e.g. `@0ff D`). + */ + gboolean end_of_number = chr <= 0xFF && (ctx->number.radix == 16 ? !g_ascii_isxdigit(chr) + : !g_ascii_isdigit(chr)); + if (end_of_number) { + teco_state_number_end_of_macro(ctx, error); + return teco_state_start_input(ctx, chr, error); + } + + if (!ctx->number.radix) { + if (teco_machine_main_eval_at(ctx)) + /* @[0-9] starts a hexadecimal constant */ + teco_undo_guint(ctx->number.radix) = 16; + else if (!ctx->number.value && chr == '0') + /* a leading zero starts an octal constant */ + teco_undo_guint(ctx->number.radix) = 8; + } + + if (ctx->flags.mode > TECO_MODE_NORMAL) + return &teco_state_number; + + teco_int_t radix = ctx->number.radix; + if (!radix) { + /* + * The ^R radix is only considered if we do not + * syntactically signify the radix (octal or hex constants). + * + * FIXME: We could just access qreg->integer here since + * we can assume that "^R" is a plain register. + */ + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + qreg->vtable->get_integer(qreg, &radix, NULL); + } + + chr = teco_ascii_toupper(chr); + guint digit = 'A' <= chr && chr <= 'F' ? chr-'A'+10 : chr-'0'; + if (digit >= radix) { + g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, + "Digit \"%c\" not allowed in radix %" TECO_INT_FORMAT, + (gchar)chr, radix); + return NULL; + } + + teco_undo_int(ctx->number.value) = ctx->number.value*radix + digit; + + return &teco_state_number; +} + +/* + * FIXME: This has to be a "command" since we chain to teco_state_start_input() + * and want case-folding to work properly. + * On the downside, this will also case-fold the hexadecimal constants (A-F). + * There would be cumbersome workarounds with custom process_edit_cmd callbacks, + * though. + */ +static TECO_DEFINE_STATE_START(teco_state_number, + .input_cb = (teco_state_input_cb_t)teco_state_number_input, + .end_of_macro_cb = (teco_state_end_of_macro_cb_t)teco_state_number_end_of_macro, + .style = SCE_SCITECO_NUMBER, + .is_start = FALSE +); + /** * Translate buffer range arguments from the expression stack to * a from-position and length in bytes. @@ -760,32 +843,13 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error) } switch (chr) { - /*$ 0 1 2 3 4 5 6 7 8 9 digit number - * [n]0|1|2|3|4|5|6|7|8|9 -> n*Radix+X -- Append digit - * - * Integer constants in \*(ST may be thought of and are - * technically sequences of single-digit commands. - * These commands take one argument from the stack - * (0 is implied), multiply it with the current radix - * (2, 8, 10, 16, ...), add the digit's value and - * return the resultant integer. - * - * The command-like semantics of digits may be abused - * in macros, for instance to append digits to computed - * integers. - * It is not an error to append a digit greater than the - * current radix - this may be changed in the future. - */ case '0' ... '9': - if (ctx->flags.modifier_at || - (ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) { + if (ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon) { gchar cmd[] = {chr, '\0'}; teco_error_modifier_set(error, cmd); return NULL; } - if (ctx->flags.mode == TECO_MODE_NORMAL) - teco_expressions_add_digit(chr, ctx->qreg_table_locals->radix); - return &teco_state_start; + return teco_state_number_input(ctx, chr, error); case '*': /* |
