aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core-commands.c106
-rw-r--r--src/core-commands.h1
-rw-r--r--src/expressions.c17
-rw-r--r--src/expressions.h2
-rw-r--r--src/lexer.c63
-rw-r--r--src/parser.h14
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.