diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core-commands.c | 106 | ||||
| -rw-r--r-- | src/core-commands.h | 1 | ||||
| -rw-r--r-- | src/expressions.c | 17 | ||||
| -rw-r--r-- | src/expressions.h | 2 | ||||
| -rw-r--r-- | src/lexer.c | 63 | ||||
| -rw-r--r-- | src/parser.h | 14 |
6 files changed, 136 insertions, 67 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 '*': /* diff --git a/src/core-commands.h b/src/core-commands.h index 6a31b92..d2200d9 100644 --- a/src/core-commands.h +++ b/src/core-commands.h @@ -62,7 +62,6 @@ void teco_undo_change_dir_to_current(void); extern teco_state_t teco_state_changedir; extern teco_state_t teco_state_condcommand; -extern teco_state_t teco_state_control; extern teco_state_t teco_state_ascii; extern teco_state_t teco_state_ecommand; diff --git a/src/expressions.c b/src/expressions.c index 9561c46..95b1040 100644 --- a/src/expressions.c +++ b/src/expressions.c @@ -135,23 +135,6 @@ teco_expressions_pop_num_calc(teco_int_t *ret, teco_int_t imply, GError **error) } void -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*radix + (n < 0 ? -1 : 1)*((gint)digit - '0')); -} - -void teco_expressions_push_op(teco_operator_t op) { g_array_append_val(teco_operators, op); diff --git a/src/expressions.h b/src/expressions.h index 3ef0faf..37aef70 100644 --- a/src/expressions.h +++ b/src/expressions.h @@ -116,8 +116,6 @@ 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, teco_qreg_t *radix); - void teco_expressions_push_op(teco_operator_t op); gboolean teco_expressions_push_calc(teco_operator_t op, GError **error); diff --git a/src/lexer.c b/src/lexer.c index 25ea8f2..2eae8c4 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -29,30 +29,30 @@ #include "core-commands.h" #include "lexer.h" +/** + * Get style after parsing character. + * + * By default, this is the parser state's style \b before + * inputting the character. + */ static teco_style_t teco_lexer_getstyle(teco_view_t *view, teco_machine_main_t *machine, gunichar chr) { - teco_style_t style = machine->parent.current->style; - - /* - * FIXME: At least this special workaround for numbers might be - * unnecessary once we get a special parser state for parsing numbers. - */ - if (machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START && - chr <= 0xFF) { - if (g_ascii_isdigit(chr)) - style = SCE_SCITECO_NUMBER; - else if (strchr(",+-*/#&()", chr)) - style = SCE_SCITECO_OPERATOR; - } else if (machine->parent.current == &teco_state_control) { - /* - * Two-character operators must always begin with caret - * They get a separate style, so we can extend it back to - * the caret in teco_lexter_step. - */ - if (strchr("*/#", chr)) - style = SCE_SCITECO_OPERATOR2; + teco_style_t old_style = machine->parent.current->style; + + if (chr <= 0xFF) { + if (machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START && + strchr(",+-*/#&()", chr)) + old_style = SCE_SCITECO_OPERATOR; + else if (machine->parent.current == &teco_state_control && + strchr("*/#", chr)) + /* + * Two-character operators must always begin with caret + * They get a separate style, so we can extend it back to + * the caret in teco_lexer_step(). + */ + old_style = SCE_SCITECO_OPERATOR2; } /* @@ -81,16 +81,23 @@ teco_lexer_getstyle(teco_view_t *view, teco_machine_main_t *machine, * True comments also begin with `!`, so make sure they are highlighted * already from the second character. * This is then extended back by one character in teco_lexer_step(). + * Numbers must also be highlighted from the first character. */ switch (machine->parent.current->style) { case SCE_SCITECO_COMMENT: case SCE_SCITECO_LABEL: + case SCE_SCITECO_NUMBER: return machine->parent.current->style; default: break; } - return style; + /* + * All non-digits automatically terminate the teco_state_number state, + * so always report the new style (so that a command after a number is + * highlighted properly). + */ + return old_style == SCE_SCITECO_NUMBER ? SCE_SCITECO_COMMAND : old_style; } static void @@ -113,6 +120,7 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, gssize old_pc = machine->macro_pc; teco_style_t style = SCE_SCITECO_DEFAULT; + guint radix = 10; /* if style == SCE_SCITECO_NUMBER */ /* * g_utf8_get_char_validated() sometimes(?) returns -2 for "\0". @@ -136,6 +144,7 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, (escape_char == '{' ? 1 : 0); style = teco_lexer_getstyle(view, machine, chr); + radix = machine->number.radix; /* * Apply folding. This currently folds only {...} string arguments @@ -171,8 +180,10 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, * rewrite the lexer against the ILexer5 interface, which requires C++. */ if ((escape_char == '{' || machine->expectstring.machine.escape_char == '{') && - teco_view_ssm(view, SCI_GETPROPERTYINT, (uptr_t)"lexer.sciteco.macrodef", TRUE)) + teco_view_ssm(view, SCI_GETPROPERTYINT, (uptr_t)"lexer.sciteco.macrodef", TRUE)) { style = teco_lexer_getstyle(view, macrodef_machine, chr); + radix = macrodef_machine->number.radix; + } } *cur_col += machine->macro_pc - old_pc; @@ -180,9 +191,10 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, /* * True comments begin with `!*` or `!!`, but only the second character gets * the correct style by default, so we extend it backwards. - * The same is true for two-letter operators. + * The same is true for two-letter operators and hexadecimal constants. */ - if (style == SCE_SCITECO_COMMENT || style == SCE_SCITECO_OPERATOR2) + if (style == SCE_SCITECO_COMMENT || style == SCE_SCITECO_OPERATOR2 || + (style == SCE_SCITECO_NUMBER && radix == 16)) old_pc--; teco_view_ssm(view, SCI_STARTSTYLING, start+old_pc, 0); @@ -196,8 +208,7 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, *safe_col = -1; /* no clean state by default */ } - if (style != SCE_SCITECO_INVALID && - machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START && + if (style != SCE_SCITECO_INVALID && machine->parent.current->is_start && !machine->flags.modifier_at) /* clean parser state */ *safe_col = *cur_col; diff --git a/src/parser.h b/src/parser.h index 0c389cc..dc43b1c 100644 --- a/src/parser.h +++ b/src/parser.h @@ -433,6 +433,19 @@ void teco_machine_stringbuilding_escape(teco_machine_stringbuilding_t *ctx, cons void teco_machine_stringbuilding_clear(teco_machine_stringbuilding_t *ctx); /** + * Persistent state for teco_state_number_input(). + */ +typedef struct { + /** + * Syntactially determined radix (0, 8 or 16). + * In case of 0, "^R" determines the radix (10 by default). + */ + guint radix; + /** accumulator for parsing numbers */ + teco_int_t value; +} teco_machine_number_t; + +/** * Peristent state for teco_state_expectstring_input(). * * This is part of the main machine instead of being a global variable, @@ -523,6 +536,7 @@ struct teco_machine_main_t { * but it's hard to prevent memory leaks if putting them into * a common union. */ + teco_machine_number_t number; teco_machine_expectstring_t expectstring; /** * State machine for parsing Q-reg specifications. |
