aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmdline.c2
-rw-r--r--src/core-commands.c209
-rw-r--r--src/doc.c2
-rw-r--r--src/expressions.c214
-rw-r--r--src/expressions.h76
-rw-r--r--src/glob.c4
-rw-r--r--src/goto-commands.c2
-rw-r--r--src/lsp.c2
-rw-r--r--src/main.c4
-rw-r--r--src/move-commands.c26
-rw-r--r--src/parser.c10
-rw-r--r--src/parser.h6
-rw-r--r--src/qreg-commands.c42
-rw-r--r--src/qreg.c116
-rw-r--r--src/qreg.h68
-rw-r--r--src/ring.c6
-rw-r--r--src/ring.h4
-rw-r--r--src/sciteco.h44
-rw-r--r--src/search.c20
-rw-r--r--src/spawn.c6
-rw-r--r--src/stdio-commands.c64
-rw-r--r--src/symbols.c6
-rw-r--r--src/undo.c1
-rw-r--r--src/undo.h3
-rw-r--r--src/view.c2
25 files changed, 633 insertions, 306 deletions
diff --git a/src/cmdline.c b/src/cmdline.c
index 1abf8df..c9fcf75 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -442,7 +442,7 @@ teco_cmdline_keymacro(const gchar *name, gssize name_len, GError **error)
teco_qreg_t *macro_reg = teco_qreg_table_find(&teco_qreg_table_globals, macro_name, sizeof(macro_name));
if (macro_reg) {
teco_int_t macro_mask;
- if (!macro_reg->vtable->get_integer(macro_reg, &macro_mask, error))
+ if (!teco_qreg_get_integer(macro_reg, &macro_mask, error))
return TECO_KEYMACRO_ERROR;
/*
diff --git a/src/core-commands.c b/src/core-commands.c
index f80cd59..583142b 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -61,11 +61,17 @@ static gboolean
teco_state_number_end_of_macro(teco_machine_main_t *ctx, GError **error)
{
teco_undo_guint(ctx->number.radix) = 0; /* default */
+ guint divisor = ctx->number.divisor;
+ teco_undo_guint(ctx->number.divisor) = 0; /* default */
if (ctx->flags.mode > TECO_MODE_NORMAL)
return TRUE;
- teco_expressions_push(ctx->number.value);
+ if (divisor)
+ /* floating point number */
+ teco_expressions_push((teco_float_t)ctx->number.value / divisor);
+ else
+ teco_expressions_push(ctx->number.value);
teco_undo_int(ctx->number.value) = 0;
return TRUE;
}
@@ -74,6 +80,19 @@ static teco_state_t *
teco_state_number_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
{
/*
+ * If you have several dots, the second one will push the current buffer position.
+ * E.g. 23.0. is equivalent to 23.0,.
+ */
+ if (chr == '.' && !ctx->number.divisor) {
+ /* floating point number */
+ if (ctx->number.radix == 8 && ctx->number.value == 0)
+ /* otherwise 0.1 would be parsed in octal */
+ teco_undo_guint(ctx->number.radix) = 10;
+ teco_undo_guint(ctx->number.divisor) = 1;
+ return &teco_state_number;
+ }
+
+ /*
* 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`).
@@ -98,17 +117,15 @@ teco_state_number_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
return &teco_state_number;
teco_int_t radix = ctx->number.radix;
- if (!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
+ * FIXME: We could just access qreg->number.i 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);
- }
+ teco_qreg_get_integer(ctx->qreg_table_locals->radix, &radix, NULL);
chr = teco_ascii_toupper(chr);
guint digit = 'A' <= chr && chr <= 'F' ? chr-'A'+10 : chr-'0';
@@ -120,6 +137,8 @@ teco_state_number_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
}
teco_undo_int(ctx->number.value) = ctx->number.value*radix + digit;
+ if (ctx->number.divisor)
+ teco_undo_guint(ctx->number.divisor) *= radix;
return &teco_state_number;
}
@@ -168,7 +187,7 @@ teco_get_range_args(const gchar *cmd, teco_int_t *from_glyphs_ret, gsize *from_r
if (teco_expressions_args() <= 1) {
teco_int_t line;
- if (!teco_expressions_pop_num_calc(&line, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&line, teco_num_sign, error))
return FALSE;
from = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
@@ -191,9 +210,9 @@ teco_get_range_args(const gchar *cmd, teco_int_t *from_glyphs_ret, gsize *from_r
if (len_glyphs_ret)
*len_glyphs_ret = teco_interface_bytes2glyphs_absdot(from+len);
} else {
- teco_int_t to_glyphs = teco_expressions_pop_num(0);
+ teco_int_t to_glyphs = teco_expressions_pop_int(0);
gssize to = teco_interface_glyphs2bytes_absdot(to_glyphs);
- teco_int_t from_glyphs = teco_expressions_pop_num(0);
+ teco_int_t from_glyphs = teco_expressions_pop_int(0);
from = teco_interface_glyphs2bytes_absdot(from_glyphs);
len = to - from;
@@ -342,13 +361,18 @@ teco_state_start_range(teco_machine_main_t *ctx, GError **error)
* Backslash pops a value from the stack, formats it
* according to the current radix and inserts it in the
* current buffer or Q-Register at dot.
- * If <n> is omitted (empty stack), it does the reverse -
- * it reads from the current buffer position an integer
- * in the current radix and pushes it onto the stack.
- * Dot is not changed when reading integers.
+ * Floating point numbers are formatted without an exponent
+ * and 6 significant digits (precision), but whout trailing
+ * zeros.
+ *
+ * If <n> is omitted (empty stack), it does the reverse \(em
+ * it reads from the current buffer position a number
+ * in the current radix and pushes it onto the stack
+ * (either as an integer or float).
+ * Dot is not changed when reading numbers.
*
* In other words, the command serializes or deserializes
- * integers as ASCII characters.
+ * numbers as ASCII characters.
*/
static void
teco_state_start_backslash(teco_machine_main_t *ctx, GError **error)
@@ -357,10 +381,14 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error)
return;
if (teco_expressions_args()) {
+ gboolean is_float;
+ teco_number_t number = teco_expressions_pop_num(0, &is_float);
+
gchar buffer[TECO_EXPRESSIONS_FORMAT_LEN];
- gchar *str = teco_expressions_format(buffer,
- teco_expressions_pop_num(0),
- ctx->qreg_table_locals->radix);
+ const gchar *str;
+ str = is_float ? teco_expressions_format_float(buffer, number.f, 6)
+ : teco_expressions_format_int(buffer, number.i,
+ ctx->qreg_table_locals->radix);
g_assert(*str != '\0');
gsize len = strlen(str);
@@ -380,15 +408,17 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error)
teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)str);
teco_ring_dirtify();
} else {
- teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
- assert(qreg != NULL);
- teco_int_t radix;
- if (!qreg->vtable->get_integer(qreg, &radix, error))
- return;
+ teco_int_t radix = 10;
+ teco_qreg_get_integer(ctx->qreg_table_locals->radix, &radix, NULL);
+ /*
+ * FIXME: Should we also support octal constants automatically when seeing
+ * a leading 0 or even hexadecimal via @[0-9]?
+ */
uptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
gchar c = (gchar)teco_interface_ssm(SCI_GETCHARAT, pos, 0);
- teco_int_t v = 0;
+ teco_int_t value = 0;
+ guint divisor = 0;
gint sign = 1;
if (c == '-') {
@@ -396,20 +426,31 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error)
sign = -1;
}
- for (;;) {
+ for (;; pos++) {
c = teco_ascii_toupper((gchar)teco_interface_ssm(SCI_GETCHARAT, pos, 0));
+
+ if (c == '.') {
+ /* floating point number */
+ divisor = 1;
+ continue;
+ }
+
if (c >= '0' && c <= '0' + MIN(radix, 10) - 1)
- v = (v*radix) + (c - '0');
+ value = (value*radix) + (c - '0');
else if (c >= 'A' &&
c <= 'A' + MIN(radix - 10, 26) - 1)
- v = (v*radix) + 10 + (c - 'A');
+ value = (value*radix) + 10 + (c - 'A');
else
break;
- pos++;
+ if (divisor)
+ divisor *= radix;
}
- teco_expressions_push(sign * v);
+ if (divisor)
+ teco_expressions_push(sign * ((teco_float_t)value / divisor));
+ else
+ teco_expressions_push(sign * value);
}
}
@@ -420,7 +461,7 @@ static void
teco_state_start_loop_open(teco_machine_main_t *ctx, GError **error)
{
teco_loop_context_t lctx;
- if (!teco_expressions_pop_num_calc(&lctx.counter, -1, error))
+ if (!teco_expressions_pop_int_calc(&lctx.counter, -1, error))
return;
lctx.brace_level = teco_brace_level;
lctx.pass_through = teco_machine_main_eval_colon(ctx) > 0;
@@ -547,7 +588,7 @@ teco_state_start_break(teco_machine_main_t *ctx, GError **error)
return;
teco_bool_t rc;
- if (!teco_expressions_pop_num_calc(&rc, v, error))
+ if (!teco_expressions_pop_int_calc(&rc, v, error))
return;
if (teco_machine_main_eval_colon(ctx) > 0)
rc = ~rc;
@@ -718,7 +759,7 @@ teco_state_start_get(teco_machine_main_t *ctx, GError **error)
if (!teco_expressions_eval(FALSE, error))
return;
if ((teco_expressions_args() || teco_num_sign < 0) &&
- !teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ !teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
gssize get_pos = teco_interface_glyphs2bytes_reldot(v);
@@ -1252,7 +1293,8 @@ TECO_DEFINE_STATE_EXPECTDIR(teco_state_changedir,
static teco_state_t *
teco_state_condcommand_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
{
- teco_int_t value = 0;
+ teco_number_t value = {0};
+ gboolean is_float = FALSE;
gboolean result = TRUE;
switch (ctx->flags.mode) {
@@ -1275,13 +1317,16 @@ teco_state_condcommand_input(teco_machine_main_t *ctx, gunichar chr, GError **er
teco_error_argexpected_set(error, "\"");
return NULL;
}
- value = teco_expressions_pop_num(0);
+ value = teco_expressions_pop_num(0, &is_float);
break;
default:
break;
}
+ /* value casted to integer */
+ teco_int_t value_i = is_float ? (teco_int_t)value.f : value.i;
+
switch (teco_ascii_toupper(chr)) {
case '~':
if (ctx->flags.mode == TECO_MODE_NORMAL)
@@ -1289,61 +1334,61 @@ teco_state_condcommand_input(teco_machine_main_t *ctx, gunichar chr, GError **er
break;
case 'A':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = g_unichar_isalpha(value);
+ result = g_unichar_isalpha(value_i);
break;
case 'C':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = g_unichar_isalnum(value) ||
- value == '.' || value == '$' || value == '_';
+ result = g_unichar_isalnum(value_i) ||
+ value_i == '.' || value_i == '$' || value_i == '_';
break;
case 'D':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = g_unichar_isdigit(value);
+ result = g_unichar_isdigit(value_i);
break;
case 'I':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = G_IS_DIR_SEPARATOR(value);
+ result = G_IS_DIR_SEPARATOR(value_i);
break;
case 'S':
case 'T':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = teco_is_success(value);
+ result = teco_is_success(value_i);
break;
case 'F':
case 'U':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = teco_is_failure(value);
+ result = teco_is_failure(value_i);
break;
case 'E':
case '=':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = value == 0;
+ result = is_float ? (value.f == 0) : (value.i == 0);
break;
case 'G':
case '>':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = value > 0;
+ result = is_float ? (value.f > 0) : (value.i > 0);
break;
case 'L':
case '<':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = value < 0;
+ result = is_float ? (value.f < 0) : (value.i < 0);
break;
case 'N':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = value != 0;
+ result = is_float ? (value.f != 0) : (value.i != 0);
break;
case 'R':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = g_unichar_isalnum(value);
+ result = g_unichar_isalnum(value_i);
break;
case 'V':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = g_unichar_islower(value);
+ result = g_unichar_islower(value_i);
break;
case 'W':
if (ctx->flags.mode == TECO_MODE_NORMAL)
- result = g_unichar_isupper(value);
+ result = g_unichar_isupper(value_i);
break;
default:
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
@@ -1414,7 +1459,7 @@ teco_state_control_negate(teco_machine_main_t *ctx, GError **error)
return;
}
- teco_expressions_push(~teco_expressions_pop_num(0));
+ teco_expressions_push(~teco_expressions_pop_int(0));
}
static void
@@ -1443,7 +1488,7 @@ teco_state_control_octal(teco_machine_main_t *ctx, GError **error)
{
teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
assert(qreg != NULL);
- if (!qreg->vtable->set_integer(qreg, 8, NULL))
+ if (!teco_qreg_set_integer(qreg, 8, NULL))
return;
}
@@ -1455,7 +1500,7 @@ teco_state_control_decimal(teco_machine_main_t *ctx, GError **error)
{
teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
assert(qreg != NULL);
- if (!qreg->vtable->set_integer(qreg, 10, NULL))
+ if (!teco_qreg_set_integer(qreg, 10, NULL))
return;
}
@@ -1479,14 +1524,13 @@ teco_state_control_radix(teco_machine_main_t *ctx, GError **error)
teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
assert(qreg != NULL);
- teco_int_t radix;
+ teco_int_t radix = 10;
if (!teco_expressions_args()) {
- if (!qreg->vtable->get_integer(qreg, &radix, error))
- return;
+ teco_qreg_get_integer(qreg, &radix, NULL);
teco_expressions_push(radix);
} else {
- if (!qreg->vtable->set_integer(qreg, teco_expressions_pop_num(0), error))
+ if (!teco_qreg_set_integer(qreg, teco_expressions_pop_int(0), error))
return;
}
}
@@ -1538,7 +1582,7 @@ teco_state_control_glyphs2bytes(teco_machine_main_t *ctx, GError **error)
*/
res = teco_interface_ssm(colon_modified ? SCI_GETLENGTH : SCI_GETCURRENTPOS, 0, 0);
} else {
- teco_int_t pos = teco_expressions_pop_num(0);
+ teco_int_t pos = teco_expressions_pop_int(0);
/* negative values for invalid indexes are passed down. */
res = colon_modified ? teco_interface_bytes2glyphs_absdot(pos)
: teco_interface_glyphs2bytes_absdot(pos);
@@ -1591,7 +1635,7 @@ teco_state_control_last_range(teco_machine_main_t *ctx, GError **error)
{
teco_int_t n;
- if (!teco_expressions_pop_num_calc(&n, 0, error))
+ if (!teco_expressions_pop_int_calc(&n, 0, error))
return;
if (n < 0 || n >= teco_ranges_count) {
teco_error_subpattern_set(error, "^Y");
@@ -1628,13 +1672,13 @@ teco_state_control_last_length(teco_machine_main_t *ctx, GError **error)
* There is little use in supporting n^S for n != 0.
* This is just for consistency with ^Y.
*
- * We do not use teco_expressions_pop_num_calc(),
+ * We do not use teco_expressions_pop_int_calc(),
* so as not to reset the sign prefix.
*/
if (!teco_expressions_eval(FALSE, error))
return;
if (teco_expressions_args() > 0)
- n = teco_expressions_pop_num(0);
+ n = teco_expressions_pop_int(0);
if (n < 0 || n >= teco_ranges_count) {
teco_error_subpattern_set(error, "^S");
return;
@@ -1685,6 +1729,10 @@ teco_state_control_date(teco_machine_main_t *ctx, GError **error)
static void
teco_state_control_time(teco_machine_main_t *ctx, GError **error)
{
+ /*
+ * FIXME: Should we return the seconds as floats or
+ * at least timestamps converted to seconds?
+ */
switch (teco_machine_main_eval_colon(ctx)) {
case 0:
teco_expressions_push(time(NULL) % (60*60*24));
@@ -1694,7 +1742,7 @@ teco_state_control_time(teco_machine_main_t *ctx, GError **error)
break;
case 2:
/*
- * NOTE: Might not be reliable if TECO_INTEGER==32.
+ * NOTE: Might not be reliable if TECO_NUMBER==32.
*/
teco_expressions_push(g_get_monotonic_time());
break;
@@ -1722,20 +1770,23 @@ teco_state_control_time(teco_machine_main_t *ctx, GError **error)
static void
teco_state_control_refresh(teco_machine_main_t *ctx, GError **error)
{
- teco_int_t ms;
+ teco_number_t ms;
+ gboolean is_float;
- if (!teco_expressions_pop_num_calc(&ms, 10, error))
+ if (!teco_expressions_pop_num_calc(&ms, &is_float, 10, error))
return;
+ if (!is_float)
+ ms.f = ms.i;
- while (ms > 0 && !teco_interface_is_interrupted()) {
+ while (ms.f > 0 && !teco_interface_is_interrupted()) {
/*
* UNIX' usleep() would also be interrupted by
* SIGINT, but polling for interruptions is
* probably precise enough.
* We need this as a fallback anyway.
*/
- g_usleep(MIN(ms*1000, TECO_POLL_INTERVAL));
- ms -= TECO_POLL_INTERVAL/1000;
+ g_usleep(MIN(ms.f*1000, TECO_POLL_INTERVAL));
+ ms.f -= TECO_POLL_INTERVAL/1000;
}
teco_interface_unfold();
@@ -2164,8 +2215,8 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
teco_expressions_push(teco_ed);
} else {
teco_int_t on, off;
- if (!teco_expressions_pop_num_calc(&on, 0, error) ||
- !teco_expressions_pop_num_calc(&off, ~(teco_int_t)0, error))
+ if (!teco_expressions_pop_int_calc(&on, 0, error) ||
+ !teco_expressions_pop_int_calc(&off, ~(teco_int_t)0, error))
return;
teco_undo_int(teco_ed) = (teco_ed & ~off) | on;
}
@@ -2377,14 +2428,14 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
static teco_int_t caret_x = 0;
teco_int_t property;
- if (!teco_expressions_pop_num_calc(&property, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&property, teco_num_sign, error))
return;
if (teco_expressions_args() > 0) {
/*
* Set property
*/
- teco_int_t value = teco_expressions_pop_num(0);
+ teco_int_t value = teco_expressions_pop_int(0);
switch (property) {
case EJ_MEMORY_LIMIT:
@@ -2430,7 +2481,7 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
case EJ_DEFAULT_COLORS: {
teco_int_t fore;
- if (!teco_expressions_pop_num_calc(&fore, -1, error))
+ if (!teco_expressions_pop_int_calc(&fore, -1, error))
return;
teco_interface_set_default_colors(fore, value);
break;
@@ -2566,7 +2617,7 @@ teco_state_ecommand_eol(teco_machine_main_t *ctx, GError **error)
teco_int_t eol_mode;
if (teco_machine_main_eval_colon(ctx) > 0) {
- switch (teco_expressions_pop_num(0)) {
+ switch (teco_expressions_pop_int(0)) {
case '\r':
eol_mode = SC_EOL_CR;
break;
@@ -2575,7 +2626,7 @@ teco_state_ecommand_eol(teco_machine_main_t *ctx, GError **error)
eol_mode = SC_EOL_LF;
break;
}
- if (teco_expressions_pop_num(0) == '\r') {
+ if (teco_expressions_pop_int(0) == '\r') {
eol_mode = SC_EOL_CRLF;
break;
}
@@ -2586,7 +2637,7 @@ teco_state_ecommand_eol(teco_machine_main_t *ctx, GError **error)
return;
}
} else {
- eol_mode = teco_expressions_pop_num(0);
+ eol_mode = teco_expressions_pop_int(0);
switch (eol_mode) {
case SC_EOL_CRLF:
case SC_EOL_CR:
@@ -2735,7 +2786,7 @@ teco_state_ecommand_encoding(teco_machine_main_t *ctx, GError **error)
/*
* Set code page
*/
- teco_int_t new_cp = teco_expressions_pop_num(0);
+ teco_int_t new_cp = teco_expressions_pop_int(0);
if (old_cp == SC_CP_UTF8 && new_cp == SC_CP_UTF8)
return;
@@ -2936,7 +2987,7 @@ teco_state_ecommand_exit(teco_machine_main_t *ctx, GError **error)
return;
} else {
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
guint id;
if (teco_is_failure(v) && (id = teco_ring_get_first_dirty())) {
@@ -3047,7 +3098,7 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error)
if (teco_interface_ssm(SCI_GETCODEPAGE, 0, 0) == SC_CP_UTF8) {
/* detect possible errors before introducing side effects */
for (gint i = args; i > 0; i--) {
- teco_int_t chr = teco_expressions_peek_num(i-1);
+ teco_int_t chr = teco_expressions_peek_int(i-1);
if (chr < 0 || !g_unichar_validate(chr)) {
teco_error_codepoint_set(error, "I");
return FALSE;
@@ -3056,21 +3107,21 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error)
for (gint i = args; i > 0; i--) {
/* 4 bytes should be enough, but we better follow the documentation */
gchar buf[6];
- gsize len = g_unichar_to_utf8(teco_expressions_peek_num(i-1), buf);
+ gsize len = g_unichar_to_utf8(teco_expressions_peek_int(i-1), buf);
teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)buf);
added_len += len;
}
} else {
/* everything else is a single-byte encoding */
for (gint i = args; i > 0; i--) {
- teco_int_t chr = teco_expressions_peek_num(i-1);
+ teco_int_t chr = teco_expressions_peek_int(i-1);
if (chr < 0 || chr > 0xFF) {
teco_error_codepoint_set(error, "I");
return FALSE;
}
}
for (gint i = args; i > 0; i--) {
- gchar chr = (gchar)teco_expressions_peek_num(i-1);
+ gchar chr = (gchar)teco_expressions_peek_int(i-1);
teco_interface_ssm(SCI_ADDTEXT, 1, (sptr_t)&chr);
}
added_len = args;
@@ -3082,7 +3133,7 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error)
/* This is done only now because it can _theoretically_ fail. */
for (gint i = 0; i < args; i++)
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
/* this way we remember how many numeric arguments were given */
teco_undo_int(teco_ranges[0].to) = teco_ranges[0].from + args;
diff --git a/src/doc.c b/src/doc.c
index 40217f4..14d1479 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -160,7 +160,7 @@ teco_doc_undo_set_string(teco_doc_t *ctx)
*/
teco_doc_update(ctx, teco_qreg_view);
- if (teco_qreg_current && teco_qreg_current->must_undo && // FIXME
+ if (teco_qreg_current && teco_qreg_current->flags.must_undo && // FIXME
ctx == &teco_qreg_current->string)
/* load old document into view */
teco_doc_undo_edit(&teco_qreg_current->string);
diff --git a/src/expressions.c b/src/expressions.c
index 95b1040..e4dc5ad 100644
--- a/src/expressions.c
+++ b/src/expressions.c
@@ -18,7 +18,12 @@
#include "config.h"
#endif
+#include <string.h>
+/* type-generic math macros */
+#include <tgmath.h>
+
#include <glib.h>
+#include <glib/gprintf.h>
#include "sciteco.h"
#include "error.h"
@@ -33,7 +38,7 @@
*/
static GArray *teco_numbers;
-TECO_DEFINE_ARRAY_UNDO_INSERT_VAL(teco_numbers, teco_int_t);
+TECO_DEFINE_ARRAY_UNDO_INSERT_VAL(teco_numbers, teco_number_t);
TECO_DEFINE_ARRAY_UNDO_REMOVE_INDEX(teco_numbers);
static GArray *teco_operators;
@@ -46,7 +51,7 @@ static gboolean teco_expressions_calc(GError **error);
static void __attribute__((constructor))
teco_expressions_init(void)
{
- teco_numbers = g_array_sized_new(FALSE, FALSE, sizeof(teco_int_t), 1024);
+ teco_numbers = g_array_sized_new(FALSE, FALSE, sizeof(teco_number_t), 1024);
teco_operators = g_array_sized_new(FALSE, FALSE, sizeof(teco_operator_t), 1024);
}
@@ -60,16 +65,19 @@ teco_expressions_precedence(teco_operator_t op)
gint teco_num_sign = 1;
void
-teco_expressions_push_int(teco_int_t number)
+teco_expressions_push_num(teco_number_t number, gboolean is_float)
{
while (teco_operators->len > 0 && teco_expressions_peek_op(0) == TECO_OP_NEW)
teco_expressions_pop_op(0);
- teco_expressions_push_op(TECO_OP_NUMBER);
+ teco_expressions_push_op(is_float ? TECO_OP_FLOAT : TECO_OP_INTEGER);
if (teco_num_sign < 0) {
teco_set_num_sign(1);
- number *= -1;
+ if (is_float)
+ number.f *= -1.0;
+ else
+ number.i *= -1;
}
g_array_append_val(teco_numbers, number);
@@ -77,14 +85,14 @@ teco_expressions_push_int(teco_int_t number)
}
/** Peek into the numbers stack */
-teco_int_t
+teco_number_t
teco_expressions_peek_num(guint index)
{
- return g_array_index(teco_numbers, teco_int_t, teco_numbers->len - 1 - index);
+ return g_array_index(teco_numbers, teco_number_t, teco_numbers->len - 1 - index);
}
/**
- * Pop a value from the number stack.
+ * Pop a number from the number stack.
*
* This must only be called if you are sure that the number at the
* given index exists and is an argument, i.e. only after calling
@@ -92,13 +100,13 @@ teco_expressions_peek_num(guint index)
* If you are unsure or want to imply a value,
* use teco_expressions_pop_num_calc() instead.
*/
-teco_int_t
-teco_expressions_pop_num(guint index)
+teco_number_t
+teco_expressions_pop_num(guint index, gboolean *is_float)
{
- teco_int_t n = 0;
- G_GNUC_UNUSED teco_operator_t op = teco_expressions_pop_op(0);
+ teco_number_t n = {0};
+ teco_operator_t op = teco_expressions_pop_op(0);
- g_assert(op == TECO_OP_NUMBER);
+ g_assert(op == TECO_OP_INTEGER || op == TECO_OP_FLOAT);
if (teco_numbers->len > 0) {
n = teco_expressions_peek_num(index);
@@ -106,6 +114,8 @@ teco_expressions_pop_num(guint index)
g_array_remove_index(teco_numbers, teco_numbers->len - 1 - index);
}
+ if (is_float)
+ *is_float = op == TECO_OP_FLOAT;
return n;
}
@@ -115,20 +125,28 @@ teco_expressions_pop_num(guint index)
* This resolves operations and allows implying values
* if there isn't any argument on the stack.
*
- * @param ret Where to store the number
- * @param imply The fallback value if there is no argument
+ * @param ret Where to store the number (can be NULL)
+ * @param is_float Whether the number in ret is a teco_float_t (can be NULL).
+ * @param imply The fallback integer if there is no argument
* @param error A GError
* @return FALSE if an error occurred
*/
gboolean
-teco_expressions_pop_num_calc(teco_int_t *ret, teco_int_t imply, GError **error)
+teco_expressions_pop_num_calc(teco_number_t *ret, gboolean *is_float, teco_int_t imply, GError **error)
{
if (!teco_expressions_eval(FALSE, error))
return FALSE;
if (teco_num_sign < 0)
teco_set_num_sign(1);
- teco_int_t v = teco_expressions_args() > 0 ? teco_expressions_pop_num(0) : imply;
+ teco_number_t v;
+ if (teco_expressions_args() > 0) {
+ v = teco_expressions_pop_num(0, is_float);
+ } else {
+ v.i = imply;
+ if (is_float)
+ *is_float = FALSE;
+ }
if (ret)
*ret = v;
return TRUE;
@@ -183,87 +201,146 @@ teco_expressions_pop_op(guint index)
static gboolean
teco_expressions_calc(GError **error)
{
- teco_int_t result;
-
- if (!teco_operators->len || teco_expressions_peek_op(0) != TECO_OP_NUMBER) {
+ if (!teco_operators->len ||
+ (teco_expressions_peek_op(0) != TECO_OP_INTEGER && teco_expressions_peek_op(0) != TECO_OP_FLOAT)) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
"Missing right operand");
return FALSE;
}
- teco_int_t vright = teco_expressions_pop_num(0);
+ gboolean vright_is_float;
+ teco_number_t vright = teco_expressions_pop_num(0, &vright_is_float);
+
teco_operator_t op = teco_expressions_pop_op(0);
- if (!teco_operators->len || teco_expressions_peek_op(0) != TECO_OP_NUMBER) {
+
+ if (!teco_operators->len ||
+ (teco_expressions_peek_op(0) != TECO_OP_INTEGER && teco_expressions_peek_op(0) != TECO_OP_FLOAT)) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
"Missing left operand");
return FALSE;
}
- teco_int_t vleft = teco_expressions_pop_num(0);
+ gboolean vleft_is_float;
+ teco_number_t vleft = teco_expressions_pop_num(0, &vleft_is_float);
switch (op) {
- case TECO_OP_POW:
- if (!vright) {
- result = vleft < 0 ? -1 : 1;
+ case TECO_OP_POW: {
+ if (vleft_is_float || vright_is_float) {
+ teco_expressions_push(pow(vleft_is_float ? vleft.f : vleft.i,
+ vright_is_float ? vright.f : vright.i));
break;
}
- if (vright < 0) {
- if (!vleft) {
+
+ if (!vright.i) {
+ teco_expressions_push(vleft.i < 0 ? -1 : 1);
+ break;
+ }
+ if (vright.i < 0) {
+ if (!vleft.i) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
"Negative power of 0 is not defined");
return FALSE;
}
- result = ABS(vleft) == 1 ? vleft : 0;
+ teco_expressions_push(ABS(vleft.i) == 1 ? vleft.i : 0);
break;
}
- result = 1;
+ teco_int_t result = 1;
for (;;) {
- if (vright & 1)
- result *= vleft;
- vright >>= 1;
- if (!vright)
+ if (vright.i & 1)
+ result *= vleft.i;
+ vright.i >>= 1;
+ if (!vright.i)
break;
- vleft *= vleft;
+ vleft.i *= vleft.i;
}
+ teco_expressions_push(result);
break;
+ }
+
case TECO_OP_MUL:
- result = vleft * vright;
+ if (vleft_is_float || vright_is_float) {
+ teco_expressions_push((vleft_is_float ? vleft.f : vleft.i) *
+ (vright_is_float ? vright.f : vright.i));
+ break;
+ }
+
+ teco_expressions_push(vleft.i * vright.i);
break;
+
case TECO_OP_DIV:
- if (!vright) {
+ if (!(vright_is_float ? vright.f : vright.i)) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
"Division by zero");
return FALSE;
}
- result = vleft / vright;
+
+ if (vleft_is_float || vright_is_float) {
+ teco_expressions_push((vleft_is_float ? vleft.f : vleft.i) /
+ (vright_is_float ? vright.f : vright.i));
+ break;
+ }
+
+ teco_expressions_push(vleft.i / vright.i);
break;
+
case TECO_OP_MOD:
- if (!vright) {
+ if (!(vright_is_float ? vright.f : vright.i)) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
"Remainder of division by zero");
return FALSE;
}
- result = vleft % vright;
+
+ if (vleft_is_float || vright_is_float) {
+ teco_expressions_push(fmod(vleft_is_float ? vleft.f : vleft.i,
+ vright_is_float ? vright.f : vright.i));
+ break;
+ }
+
+ teco_expressions_push(vleft.i % vright.i);
break;
+
case TECO_OP_ADD:
- result = vleft + vright;
+ if (vleft_is_float || vright_is_float) {
+ teco_expressions_push((vleft_is_float ? vleft.f : vleft.i) +
+ (vright_is_float ? vright.f : vright.i));
+ break;
+ }
+
+ teco_expressions_push(vleft.i + vright.i);
break;
+
case TECO_OP_SUB:
- result = vleft - vright;
+ if (vleft_is_float || vright_is_float) {
+ teco_expressions_push((vleft_is_float ? vleft.f : vleft.i) -
+ (vright_is_float ? vright.f : vright.i));
+ break;
+ }
+
+ teco_expressions_push(vleft.i - vright.i);
break;
+
case TECO_OP_AND:
- result = vleft & vright;
+ teco_expressions_push((vleft_is_float ? (teco_int_t)vleft.f : vleft.i) &
+ (vright_is_float ? (teco_int_t)vright.f : vright.i));
break;
+
case TECO_OP_XOR:
- result = vleft ^ vright;
+ /*
+ * As floats are casted to integers first, discarding the mantissa,
+ * you cannot use XOR to compare two floats for equality.
+ */
+ teco_expressions_push((vleft_is_float ? (teco_int_t)vleft.f : vleft.i) ^
+ (vright_is_float ? (teco_int_t)vright.f : vright.i));
break;
+
case TECO_OP_OR:
- result = vleft | vright;
+ teco_expressions_push((vleft_is_float ? (teco_int_t)vleft.f : vleft.i) |
+ (vright_is_float ? (teco_int_t)vright.f : vright.i));
break;
+
default:
/* shouldn't happen */
g_assert_not_reached();
}
- teco_expressions_push(result);
return TRUE;
}
@@ -311,7 +388,9 @@ teco_expressions_args(void)
{
guint n = 0;
- while (n < teco_operators->len && teco_expressions_peek_op(n) == TECO_OP_NUMBER)
+ while (n < teco_operators->len &&
+ (teco_expressions_peek_op(n) == TECO_OP_INTEGER ||
+ teco_expressions_peek_op(n) == TECO_OP_FLOAT))
n++;
return n;
@@ -322,7 +401,8 @@ teco_expressions_first_op(void)
{
for (guint i = 0; i < teco_operators->len; i++) {
switch (teco_expressions_peek_op(i)) {
- case TECO_OP_NUMBER:
+ case TECO_OP_INTEGER:
+ case TECO_OP_FLOAT:
case TECO_OP_NEW:
break;
default:
@@ -339,7 +419,7 @@ teco_expressions_discard_args(GError **error)
if (!teco_expressions_eval(FALSE, error))
return FALSE;
for (guint i = teco_expressions_args(); i; i--)
- if (!teco_expressions_pop_num_calc(NULL, 0, error))
+ if (!teco_expressions_pop_num_calc(NULL, NULL, 0, error))
return FALSE;
return TRUE;
}
@@ -360,10 +440,15 @@ teco_expressions_brace_open(void)
gboolean
teco_expressions_brace_return(guint keep_braces, guint args, GError **error)
{
- g_autofree teco_int_t *return_numbers = g_new(teco_int_t, args);
+ typedef struct {
+ teco_number_t number;
+ gboolean is_float;
+ } teco_number_tagged_t;
+
+ g_autofree teco_number_tagged_t *return_numbers = g_new(teco_number_tagged_t, args);
for (guint i = args; i; i--)
- return_numbers[i-1] = teco_expressions_pop_num(0);
+ return_numbers[i-1].number = teco_expressions_pop_num(0, &return_numbers[i-1].is_float);
teco_undo_guint(teco_brace_level);
@@ -375,7 +460,7 @@ teco_expressions_brace_return(guint keep_braces, guint args, GError **error)
}
for (guint i = 0; i < args; i++)
- teco_expressions_push(return_numbers[i]);
+ teco_expressions_push_num(return_numbers[i].number, return_numbers[i].is_float);
return TRUE;
}
@@ -410,11 +495,11 @@ teco_expressions_clear(void)
* @return A pointer into buffer to the beginning of the formatted number.
*/
gchar *
-teco_expressions_format(gchar *buffer, teco_int_t number, teco_qreg_t *qreg)
+teco_expressions_format_int(gchar *buffer, teco_int_t number, teco_qreg_t *qreg)
{
assert(qreg != NULL);
teco_int_t radix = 10;
- qreg->vtable->get_integer(qreg, &radix, NULL);
+ teco_qreg_get_integer(qreg, &radix, NULL);
gchar *p = buffer + TECO_EXPRESSIONS_FORMAT_LEN;
@@ -424,7 +509,7 @@ teco_expressions_format(gchar *buffer, teco_int_t number, teco_qreg_t *qreg)
do {
*--p = '0' + ABS(v % radix);
if (*p > '9')
- *p += 'A' - '9' - 1;
+ *p += 'a' - '9' - 1;
} while ((v /= radix));
if (number < 0)
*--p = '-';
@@ -432,6 +517,25 @@ teco_expressions_format(gchar *buffer, teco_int_t number, teco_qreg_t *qreg)
return p;
}
+gchar *
+teco_expressions_format_float(gchar *buffer, teco_float_t number, guint precision)
+{
+ /*
+ * Do not print in scientific notation (with an exponent) - we cannot
+ * write such constants in SciTECO, so we don't want to format them either.
+ */
+ g_sprintf(buffer, "%.*" TECO_FLOAT_MODIFIER "f", precision, number);
+ gchar *p = buffer + strlen(buffer);
+ /*
+ * On the other hand, this prints a fixed number of trailing zeros, so we
+ * strip these afterwards.
+ */
+ while (p > buffer && (p[-1] == '0' || p[-1] == '.'))
+ p--;
+ *p = '\0';
+ return buffer;
+}
+
static void TECO_DEBUG_CLEANUP
teco_expressions_cleanup(void)
{
diff --git a/src/expressions.h b/src/expressions.h
index 37aef70..1c1b4e9 100644
--- a/src/expressions.h
+++ b/src/expressions.h
@@ -79,7 +79,8 @@ typedef enum {
TECO_OP_NIL = 0x00,
TECO_OP_NEW,
TECO_OP_BRACE,
- TECO_OP_NUMBER,
+ TECO_OP_INTEGER,
+ TECO_OP_FLOAT,
/*
* Real operators
*/
@@ -102,7 +103,23 @@ teco_set_num_sign(gint sign)
teco_undo_gint(teco_num_sign) = sign;
}
-void teco_expressions_push_int(teco_int_t number);
+teco_operator_t teco_expressions_peek_op(guint index);
+teco_operator_t teco_expressions_pop_op(guint index);
+void teco_expressions_push_op(teco_operator_t op);
+
+void teco_expressions_push_num(teco_number_t number, gboolean is_float);
+
+static inline void
+teco_expressions_push_int(teco_int_t number)
+{
+ teco_expressions_push_num((teco_number_t){.i = number}, FALSE);
+}
+
+static inline void
+teco_expressions_push_float(teco_float_t number)
+{
+ teco_expressions_push_num((teco_number_t){.f = number}, TRUE);
+}
/** Push characters of a C-string. */
static inline void
@@ -112,11 +129,46 @@ teco_expressions_push_str(const gchar *str)
teco_expressions_push_int(*str++);
}
-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);
+teco_number_t teco_expressions_peek_num(guint index);
+
+static inline teco_int_t
+teco_expressions_peek_int(guint index)
+{
+ teco_number_t number = teco_expressions_peek_num(index);
+ return teco_expressions_peek_op(index) == TECO_OP_FLOAT ? (teco_int_t)number.f : number.i;
+}
+
+teco_number_t teco_expressions_pop_num(guint index, gboolean *is_float);
+
+static inline teco_int_t
+teco_expressions_pop_int(guint index)
+{
+ gboolean is_float;
+ teco_number_t number = teco_expressions_pop_num(index, &is_float);
+ return is_float ? (teco_int_t)number.f : number.i;
+}
+
+gboolean teco_expressions_pop_num_calc(teco_number_t *ret, gboolean *is_float,
+ teco_int_t imply, GError **error);
+
+/**
+ * Pop an integer from the number stack.
+ *
+ * This automatically casts floats to integers.
+ *
+ * @see teco_expressions_pop_num_calc
+ */
+static inline gboolean
+teco_expressions_pop_int_calc(teco_int_t *ret, teco_int_t imply, GError **error)
+{
+ teco_number_t number;
+ gboolean is_float;
+ if (!teco_expressions_pop_num_calc(&number, &is_float, imply, error))
+ return FALSE;
+ *ret = is_float ? (teco_int_t)number.f : number.i;
+ return TRUE;
+}
-void teco_expressions_push_op(teco_operator_t op);
gboolean teco_expressions_push_calc(teco_operator_t op, GError **error);
/*
@@ -124,12 +176,10 @@ gboolean teco_expressions_push_calc(teco_operator_t op, GError **error);
*/
#define teco_expressions_push(X) \
(_Generic((X), default : teco_expressions_push_int, \
+ teco_float_t : teco_expressions_push_float, \
char * : teco_expressions_push_str, \
const char * : teco_expressions_push_str)(X))
-teco_operator_t teco_expressions_peek_op(guint index);
-teco_operator_t teco_expressions_pop_op(guint index);
-
gboolean teco_expressions_eval(gboolean pop_brace, GError **error);
guint teco_expressions_args(void);
@@ -146,8 +196,12 @@ gboolean teco_expressions_brace_close(GError **error);
void teco_expressions_clear(void);
-/** Maximum size required to format a number if radix == 2 */
+/**
+ * Maximum size required to format a teco_int_t if radix == 2.
+ * It's also sufficient for formatting a teco_float_t.
+ */
#define TECO_EXPRESSIONS_FORMAT_LEN \
(1 + sizeof(teco_int_t)*8 + 1)
-gchar *teco_expressions_format(gchar *buffer, teco_int_t number, teco_qreg_t *radix);
+gchar *teco_expressions_format_int(gchar *buffer, teco_int_t number, teco_qreg_t *radix);
+gchar *teco_expressions_format_float(gchar *buffer, teco_float_t number, guint precision);
diff --git a/src/glob.c b/src/glob.c
index c1efe47..830471e 100644
--- a/src/glob.c
+++ b/src/glob.c
@@ -486,7 +486,7 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro
teco_int_t teco_test_mode;
- if (!teco_expressions_pop_num_calc(&teco_test_mode, 0, error))
+ if (!teco_expressions_pop_int_calc(&teco_test_mode, 0, error))
return NULL;
switch (teco_test_mode) {
/*
@@ -604,7 +604,7 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro
undo__teco_interface_ssm(SCI_DELETERANGE, pos, added_len);
}
- if (!glob_reg->vtable->set_integer(glob_reg, teco_bool(matching), error))
+ if (!teco_qreg_set_integer(glob_reg, teco_bool(matching), error))
return NULL;
return &teco_state_start;
diff --git a/src/goto-commands.c b/src/goto-commands.c
index a9ff3c2..c69a9b4 100644
--- a/src/goto-commands.c
+++ b/src/goto-commands.c
@@ -131,7 +131,7 @@ teco_state_goto_done(teco_machine_main_t *ctx, teco_string_t str, GError **error
}
teco_int_t value;
- if (!teco_expressions_pop_num_calc(&value, 0, error))
+ if (!teco_expressions_pop_int_calc(&value, 0, error))
return NULL;
gboolean colon_modified = teco_machine_main_eval_colon(ctx) > 0;
diff --git a/src/lsp.c b/src/lsp.c
index fe805c7..ee9f945 100644
--- a/src/lsp.c
+++ b/src/lsp.c
@@ -1474,7 +1474,7 @@ teco_state_lsp_lookup_done(teco_machine_main_t *ctx, teco_string_t str, GError *
}
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, 0, error))
+ if (!teco_expressions_pop_int_calc(&v, 0, error))
return NULL;
if (v < 0) {
diff --git a/src/main.c b/src/main.c
index 43f3dc4..1be85dc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -546,7 +546,7 @@ main(int argc, char **argv)
}
g_clear_error(&error);
- if (!teco_expressions_pop_num_calc(&ret, EXIT_SUCCESS, &error) ||
+ if (!teco_expressions_pop_int_calc(&ret, EXIT_SUCCESS, &error) ||
!teco_ed_hook(TECO_ED_HOOK_QUIT, &error))
goto cleanup;
goto cleanup;
@@ -586,7 +586,7 @@ main(int argc, char **argv)
if (teco_ed & TECO_ED_EXIT) {
/* exit was requested using the EX command */
- if (!teco_expressions_pop_num_calc(&ret, EXIT_SUCCESS, &error) ||
+ if (!teco_expressions_pop_int_calc(&ret, EXIT_SUCCESS, &error) ||
!teco_ed_hook(TECO_ED_HOOK_QUIT, &error))
goto cleanup;
goto cleanup;
diff --git a/src/move-commands.c b/src/move-commands.c
index 868bb19..3a31b6b 100644
--- a/src/move-commands.c
+++ b/src/move-commands.c
@@ -55,7 +55,7 @@ teco_state_start_jump(teco_machine_main_t *ctx, GError **error)
{
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, 0, error))
+ if (!teco_expressions_pop_int_calc(&v, 0, error))
return;
gssize next_pos = teco_interface_glyphs2bytes_absdot(v);
@@ -107,7 +107,7 @@ teco_state_start_move(teco_machine_main_t *ctx, GError **error)
{
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
teco_bool_t rc = teco_move_chars(v);
@@ -132,7 +132,7 @@ teco_state_start_reverse(teco_machine_main_t *ctx, GError **error)
{
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
teco_bool_t rc = teco_move_chars(-v);
@@ -191,7 +191,7 @@ teco_state_start_line(teco_machine_main_t *ctx, GError **error)
{
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
teco_bool_t rc = teco_move_lines(v);
@@ -217,7 +217,7 @@ teco_state_start_back(teco_machine_main_t *ctx, GError **error)
{
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
teco_bool_t rc = teco_move_lines(-v);
@@ -388,7 +388,7 @@ teco_state_start_words(teco_machine_main_t *ctx, const gchar *cmd, gint factor,
return &teco_state_start;
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return NULL;
sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
@@ -458,7 +458,7 @@ teco_state_start_delete_words(teco_machine_main_t *ctx, const gchar *cmd, gint f
return &teco_state_start;
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return NULL;
v *= factor;
@@ -504,7 +504,7 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
from = pos;
if (by_lines) {
teco_int_t line;
- if (!teco_expressions_pop_num_calc(&line, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&line, teco_num_sign, error))
return FALSE;
line += teco_interface_ssm(SCI_LINEFROMPOSITION, from, 0);
len = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0) - from;
@@ -519,7 +519,7 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
teco_current_doc_set_dot(teco_interface_bytes2glyphs_reldot(len));
} else {
teco_int_t len_glyphs;
- if (!teco_expressions_pop_num_calc(&len_glyphs, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&len_glyphs, teco_num_sign, error))
return FALSE;
gssize to = teco_interface_glyphs2bytes_reldot(len_glyphs);
rc = teco_bool(to >= 0);
@@ -533,9 +533,9 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
from -= len;
}
} else {
- teco_int_t to_glyphs = teco_expressions_pop_num(0);
+ teco_int_t to_glyphs = teco_expressions_pop_int(0);
gssize to = teco_interface_glyphs2bytes_absdot(to_glyphs);
- teco_int_t from_glyphs = teco_expressions_pop_num(0);
+ teco_int_t from_glyphs = teco_expressions_pop_int(0);
from = teco_interface_glyphs2bytes_absdot(from_glyphs);
len = to - from;
rc = teco_bool(len >= 0 && from >= 0 && to >= 0);
@@ -656,7 +656,7 @@ teco_state_control_lines2glyphs(teco_machine_main_t *ctx, GError **error)
* FIXME: Could be optimized by directly calling
* SCI_LINEFROMINDEXPOSITION.
*/
- pos = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_num(0));
+ pos = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_int(0));
if (pos < 0) {
teco_error_range_set(error, "^Q");
return;
@@ -667,7 +667,7 @@ teco_state_control_lines2glyphs(teco_machine_main_t *ctx, GError **error)
} else {
teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v, teco_num_sign, error))
return;
sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
diff --git a/src/parser.c b/src/parser.c
index 45e1e3c..231ad3e 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -792,15 +792,17 @@ teco_state_stringbuilding_ctle_num_input(teco_machine_stringbuilding_t *ctx, gun
/* parse-only mode */
return &teco_state_stringbuilding_start;
- teco_int_t value;
- if (!qreg->vtable->get_integer(qreg, &value, error))
+ teco_number_t value;
+ gboolean is_float;
+ if (!qreg->vtable->get_number(qreg, &value, &is_float, error))
return NULL;
/*
* NOTE: Numbers can always be safely formatted as null-terminated strings.
*/
gchar buffer[TECO_EXPRESSIONS_FORMAT_LEN];
- const gchar *num = teco_expressions_format(buffer, value, ctx->qreg_table_locals->radix);
+ const gchar *num = is_float ? teco_expressions_format_float(buffer, value.f, 6)
+ : teco_expressions_format_int(buffer, value.i, ctx->qreg_table_locals->radix);
teco_machine_stringbuilding_append(ctx, num, strlen(num));
return &teco_state_stringbuilding_start;
@@ -830,7 +832,7 @@ teco_state_stringbuilding_ctle_u_input(teco_machine_stringbuilding_t *ctx, gunic
return &teco_state_stringbuilding_start;
teco_int_t value;
- if (!qreg->vtable->get_integer(qreg, &value, error))
+ if (!teco_qreg_get_integer(qreg, &value, error))
return NULL;
if (!teco_machine_stringbuilding_append_c(ctx, value)) {
diff --git a/src/parser.h b/src/parser.h
index dc43b1c..c134d94 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -443,6 +443,12 @@ typedef struct {
guint radix;
/** accumulator for parsing numbers */
teco_int_t value;
+ /**
+ * if not null, value/divisor is the floating point number
+ * FIXME: Perhaps roll this into a bitfield with radix.
+ * The entire teco_machine_number_t could be undone.
+ */
+ guint divisor;
} teco_machine_number_t;
/**
diff --git a/src/qreg-commands.c b/src/qreg-commands.c
index 0c4c8d4..14cc95f 100644
--- a/src/qreg-commands.c
+++ b/src/qreg-commands.c
@@ -294,7 +294,7 @@ teco_state_queryqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
if (teco_expressions_args() > 0) {
/* Query character from Q-Register string */
- teco_int_t pos = teco_expressions_pop_num(0);
+ teco_int_t pos = teco_expressions_pop_int(0);
if (pos < 0) {
teco_error_range_set(error, "Q");
return NULL;
@@ -305,12 +305,13 @@ teco_state_queryqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
return NULL;
teco_expressions_push(c);
} else {
- /* Query integer */
- teco_int_t value;
+ /* Query number */
+ teco_number_t value;
+ gboolean is_float;
- if (!qreg->vtable->get_integer(qreg, &value, error))
+ if (!qreg->vtable->get_number(qreg, &value, &is_float, error))
return NULL;
- teco_expressions_push(value);
+ teco_expressions_push_num(value, is_float);
}
return &teco_state_start;
@@ -407,7 +408,7 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
start = buffer = g_malloc(args*6);
for (gint i = args; i > 0; i--) {
- teco_int_t chr = teco_expressions_peek_num(i-1);
+ teco_int_t chr = teco_expressions_peek_int(i-1);
if (chr < 0 || !g_unichar_validate(chr)) {
teco_error_codepoint_set(error, "^U");
return NULL;
@@ -416,12 +417,12 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
}
/* we pop only now since we had to peek in reverse order */
for (gint i = 0; i < args; i++)
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
} else {
buffer = g_malloc(args);
for (gint i = 0; i < args; i++) {
- teco_int_t chr = teco_expressions_pop_num(0);
+ teco_int_t chr = teco_expressions_pop_int(0);
if (chr < 0 || chr > 0xFF) {
teco_error_codepoint_set(error, "^U");
return NULL;
@@ -607,9 +608,12 @@ teco_state_setqreginteger_got_register(teco_machine_main_t *ctx, teco_qreg_t *qr
if (!teco_expressions_eval(FALSE, error))
return NULL;
if (teco_expressions_args() || teco_num_sign < 0) {
- teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error) ||
- !qreg->vtable->set_integer(qreg, v, error))
+ teco_number_t v;
+ gboolean is_float;
+
+ if (!teco_expressions_pop_num_calc(&v, &is_float, teco_num_sign, error))
+ return NULL;
+ if (!qreg->vtable->set_number(qreg, v, is_float, error))
return NULL;
if (teco_machine_main_eval_colon(ctx) > 0)
@@ -654,13 +658,19 @@ teco_state_increaseqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- teco_int_t value, add;
+ teco_number_t value, add;
+ gboolean value_is_float, add_is_float;
- if (!qreg->vtable->get_integer(qreg, &value, error) ||
- !teco_expressions_pop_num_calc(&add, teco_num_sign, error) ||
- !qreg->vtable->set_integer(qreg, value += add, error))
+ if (!qreg->vtable->get_number(qreg, &value, &value_is_float, error) ||
+ !teco_expressions_pop_num_calc(&add, &add_is_float, teco_num_sign, error))
+ return NULL;
+ if (value_is_float)
+ value.f += add_is_float ? add.f : add.i;
+ else
+ value.i += add_is_float ? (teco_int_t)add.f : add.i;
+ if (!qreg->vtable->set_number(qreg, value, value_is_float, error))
return NULL;
- teco_expressions_push(value);
+ teco_expressions_push_num(value, value_is_float);
return &teco_state_start;
}
diff --git a/src/qreg.c b/src/qreg.c
index d0f3e6b..7d9a83b 100644
--- a/src/qreg.c
+++ b/src/qreg.c
@@ -58,6 +58,12 @@ teco_qreg_t *teco_qreg_current = NULL;
*/
teco_qreg_table_t teco_qreg_table_globals;
+typedef struct teco_qreg_flags_t teco_qreg_flags_t;
+TECO_DEFINE_UNDO_SCALAR(teco_qreg_flags_t);
+
+#define teco_undo_qreg_flags(VAR) \
+ (*teco_undo_object_teco_qreg_flags_t_push(&(VAR)))
+
/** @private @static @memberof teco_qreg_t */
static teco_qreg_t *
teco_qreg_new(teco_qreg_vtable_t *vtable, const gchar *name, gsize len)
@@ -103,7 +109,7 @@ teco_qreg_execute(teco_qreg_t *qreg, teco_qreg_table_t *qreg_table_locals, GErro
void
teco_qreg_undo_set_eol_mode(teco_qreg_t *qreg)
{
- if (!qreg->must_undo)
+ if (!qreg->flags.must_undo)
return;
/*
@@ -112,7 +118,7 @@ teco_qreg_undo_set_eol_mode(teco_qreg_t *qreg)
*/
teco_doc_update(&qreg->string, teco_qreg_view);
- if (teco_qreg_current && teco_qreg_current->must_undo) // FIXME
+ if (teco_qreg_current && teco_qreg_current->flags.must_undo) // FIXME
teco_doc_undo_edit(&teco_qreg_current->string);
undo__teco_view_ssm(teco_qreg_view, SCI_SETEOLMODE,
@@ -136,18 +142,23 @@ teco_qreg_set_eol_mode(teco_qreg_t *qreg, gint mode)
}
static gboolean
-teco_qreg_plain_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error)
+teco_qreg_plain_set_number(teco_qreg_t *qreg, teco_number_t value, gboolean is_float, GError **error)
{
- if (qreg->must_undo) // FIXME
- teco_undo_int(qreg->integer);
- qreg->integer = value;
+ if (qreg->flags.must_undo) { // FIXME
+ teco_undo_number(qreg->number);
+ teco_undo_qreg_flags(qreg->flags);
+ }
+ qreg->number = value;
+ qreg->flags.is_float = is_float;
return TRUE;
}
static gboolean
-teco_qreg_plain_get_integer(teco_qreg_t *qreg, teco_int_t *ret, GError **error)
+teco_qreg_plain_get_number(teco_qreg_t *qreg, teco_number_t *ret,
+ gboolean *is_float, GError **error)
{
- *ret = qreg->integer;
+ *ret = qreg->number;
+ *is_float = qreg->flags.is_float;
return TRUE;
}
@@ -162,7 +173,7 @@ teco_qreg_plain_set_string(teco_qreg_t *qreg, const gchar *str, gsize len,
static gboolean
teco_qreg_plain_undo_set_string(teco_qreg_t *qreg, GError **error)
{
- if (qreg->must_undo) // FIXME
+ if (qreg->flags.must_undo) // FIXME
teco_doc_undo_set_string(&qreg->string);
return TRUE;
}
@@ -177,14 +188,14 @@ teco_qreg_plain_append_string(teco_qreg_t *qreg, const gchar *str, gsize len, GE
if (!len)
return TRUE;
- if (qreg->must_undo) { // FIXME
+ if (qreg->flags.must_undo) { // FIXME
/*
* Necessary, so that upon rubout the
* string's parameters are restored.
*/
teco_doc_update(&qreg->string, teco_qreg_view);
- if (teco_qreg_current && teco_qreg_current->must_undo) // FIXME
+ if (teco_qreg_current && teco_qreg_current->flags.must_undo) // FIXME
teco_doc_undo_edit(&teco_qreg_current->string);
teco_doc_undo_reset(&qreg->string);
@@ -208,7 +219,7 @@ teco_qreg_plain_append_string(teco_qreg_t *qreg, const gchar *str, gsize len, GE
* so that teco_doc_edit() always initializes the document and
* the default codepage.
*/
- if (qreg->must_undo) // FIXME
+ if (qreg->flags.must_undo) // FIXME
teco_doc_undo_edit(&qreg->string);
return TRUE;
@@ -273,7 +284,7 @@ teco_qreg_plain_exchange_string(teco_qreg_t *qreg, teco_doc_t *src, GError **err
static gboolean
teco_qreg_plain_undo_exchange_string(teco_qreg_t *qreg, teco_doc_t *src, GError **error)
{
- if (qreg->must_undo) // FIXME
+ if (qreg->flags.must_undo) // FIXME
teco_doc_undo_exchange(&qreg->string);
teco_doc_undo_exchange(src);
return TRUE;
@@ -301,7 +312,7 @@ teco_qreg_plain_undo_edit(teco_qreg_t *qreg, GError **error)
*/
teco_doc_update(&qreg->string, teco_qreg_view);
- if (!qreg->must_undo) // FIXME
+ if (!qreg->flags.must_undo) // FIXME
return TRUE;
undo__teco_interface_info_update_qreg(qreg);
@@ -363,8 +374,8 @@ teco_qreg_plain_save(teco_qreg_t *qreg, const gchar *filename, GError **error)
* These store their string part as teco_docs.
*/
#define TECO_INIT_QREG(...) { \
- .set_integer = teco_qreg_plain_set_integer, \
- .get_integer = teco_qreg_plain_get_integer, \
+ .set_number = teco_qreg_plain_set_number, \
+ .get_number = teco_qreg_plain_get_number, \
.set_string = teco_qreg_plain_set_string, \
.undo_set_string = teco_qreg_plain_undo_set_string, \
.append_string = teco_qreg_plain_append_string, \
@@ -391,9 +402,12 @@ teco_qreg_plain_new(const gchar *name, gsize len)
/* see also teco_state_start_jump() */
static gboolean
-teco_qreg_dot_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error)
+teco_qreg_dot_set_number(teco_qreg_t *qreg, teco_number_t value, gboolean is_float, GError **error)
{
- gssize pos = teco_interface_glyphs2bytes_absdot(value);
+ if (is_float)
+ value.i = value.f;
+
+ gssize pos = teco_interface_glyphs2bytes_absdot(value.i);
if (pos < 0) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_MOVE,
"Attempt to move pointer off page when setting Q-Register \":\"");
@@ -405,16 +419,17 @@ teco_qreg_dot_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error)
teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), 0);
teco_interface_ssm(SCI_GOTOPOS, pos, 0);
- teco_current_doc_set_dot(value);
+ teco_current_doc_set_dot(value.i);
return TRUE;
}
-/* see also teco_state_start_dot() */
static gboolean
-teco_qreg_dot_get_integer(teco_qreg_t *qreg, teco_int_t *ret, GError **error)
+teco_qreg_dot_get_number(teco_qreg_t *qreg, teco_number_t *ret,
+ gboolean *is_float, GError **error)
{
- *ret = teco_current_doc_get_dot();
+ ret->i = teco_current_doc_get_dot();
+ *is_float = FALSE;
return TRUE;
}
@@ -423,8 +438,8 @@ teco_qreg_t *
teco_qreg_dot_new(void)
{
static teco_qreg_vtable_t vtable = TECO_INIT_QREG(
- .set_integer = teco_qreg_dot_set_integer,
- .get_integer = teco_qreg_dot_get_integer
+ .set_number = teco_qreg_dot_set_number,
+ .get_number = teco_qreg_dot_get_number
);
/*
@@ -435,18 +450,18 @@ teco_qreg_dot_new(void)
}
static gboolean
-teco_qreg_radix_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error)
+teco_qreg_radix_set_number(teco_qreg_t *qreg, teco_number_t value, gboolean is_float, GError **error)
{
- if (value < 2) {
- g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Invalid radix");
+ if (is_float)
+ value.i = value.f;
+
+ if (value.i < 2) {
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Invalid radix %" TECO_INT_FORMAT, value.i);
return FALSE;
}
- if (qreg->must_undo) // FIXME
- teco_undo_int(qreg->integer);
- qreg->integer = value;
- return TRUE;
+ return teco_qreg_plain_set_number(qreg, value, FALSE, error);
}
/** @static @memberof teco_qreg_t */
@@ -454,11 +469,11 @@ static teco_qreg_t *
teco_qreg_radix_new(void)
{
static teco_qreg_vtable_t vtable = TECO_INIT_QREG(
- .set_integer = teco_qreg_radix_set_integer
+ .set_number = teco_qreg_radix_set_number
);
teco_qreg_t *qreg = teco_qreg_new(&vtable, "\x12", 1); /* ^R */
- qreg->integer = 10;
+ qreg->number.i = 10;
return qreg;
}
@@ -514,7 +529,7 @@ teco_qreg_external_undo_exchange_string(teco_qreg_t *qreg, teco_doc_t *src, GErr
{
if (!qreg->vtable->undo_set_string(qreg, error))
return FALSE;
- if (qreg->must_undo) // FIXME
+ if (qreg->flags.must_undo) // FIXME
teco_doc_undo_set_string(src);
return TRUE;
}
@@ -537,7 +552,7 @@ teco_qreg_external_get_character(teco_qreg_t *qreg, teco_int_t position,
/*
* Make sure that the -1/-2 error values are preserved.
* The sign bit in UCS-4/UTF-32 is unused, so this will even
- * suffice if TECO_INTEGER == 32.
+ * suffice if TECO_NUMBER == 32.
*/
*chr = *p ? (gint32)g_utf8_get_char_validated(p, -1) : 0;
return TRUE;
@@ -622,16 +637,18 @@ teco_qreg_external_save(teco_qreg_t *qreg, const gchar *filename, GError **error
* NOTE: The integer-component is currently unused on the "*" special register.
*/
static gboolean
-teco_qreg_bufferinfo_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error)
+teco_qreg_bufferinfo_set_number(teco_qreg_t *qreg, teco_number_t value, gboolean is_float, GError **error)
{
return teco_current_doc_undo_edit(error) &&
- teco_ring_edit(value, error);
+ teco_ring_edit(is_float ? (teco_int_t)value.f : value.i, error);
}
static gboolean
-teco_qreg_bufferinfo_get_integer(teco_qreg_t *qreg, teco_int_t *ret, GError **error)
+teco_qreg_bufferinfo_get_number(teco_qreg_t *qreg, teco_number_t *ret,
+ gboolean *is_float, GError **error)
{
- *ret = teco_ring_get_id(teco_ring_current);
+ ret->i = teco_ring_get_id(teco_ring_current);
+ *is_float = FALSE;
return TRUE;
}
@@ -690,8 +707,8 @@ teco_qreg_t *
teco_qreg_bufferinfo_new(void)
{
static teco_qreg_vtable_t vtable = TECO_INIT_QREG_EXTERNAL(
- .set_integer = teco_qreg_bufferinfo_set_integer,
- .get_integer = teco_qreg_bufferinfo_get_integer,
+ .set_number = teco_qreg_bufferinfo_set_number,
+ .get_number = teco_qreg_bufferinfo_get_number,
.set_string = teco_qreg_bufferinfo_set_string,
.undo_set_string = teco_qreg_bufferinfo_undo_set_string,
.append_string = teco_qreg_bufferinfo_append_string,
@@ -944,7 +961,7 @@ teco_qreg_clipboard_new(const gchar *name)
* This is configurable via the integer cell.
*/
if (qreg->head.name.len == 1)
- qreg->integer = 'C';
+ qreg->number.i = 'C';
return qreg;
}
@@ -987,7 +1004,7 @@ TECO_DEFINE_UNDO_CALL(teco_qreg_table_remove, teco_qreg_t *);
static inline void
teco_qreg_table_undo_remove(teco_qreg_t *qreg)
{
- if (qreg->must_undo)
+ if (qreg->flags.must_undo)
undo__teco_qreg_table_remove(qreg);
}
@@ -1169,8 +1186,11 @@ teco_qreg_table_clear(teco_qreg_table_t *table)
}
typedef struct {
- teco_int_t integer;
+ teco_number_t number;
teco_doc_t string;
+
+ /** whether the number cell is an integer or float */
+ gboolean is_float;
} teco_qreg_stack_entry_t;
static inline void
@@ -1199,11 +1219,11 @@ TECO_DEFINE_UNDO_CALL(teco_qreg_stack_remove_last);
gboolean
teco_qreg_stack_push(teco_qreg_t *qreg, GError **error)
{
- teco_qreg_stack_entry_t entry;
+ teco_qreg_stack_entry_t entry = {0};
g_auto(teco_string_t) string = {NULL, 0};
guint codepage;
- if (!qreg->vtable->get_integer(qreg, &entry.integer, error) ||
+ if (!qreg->vtable->get_number(qreg, &entry.number, &entry.is_float, error) ||
!qreg->vtable->get_string(qreg, &string.data, &string.len, &codepage, error))
return FALSE;
teco_doc_init(&entry.string);
@@ -1247,7 +1267,7 @@ teco_qreg_stack_pop(teco_qreg_t *qreg, GError **error)
teco_qreg_stack_entry_t *entry;
entry = &g_array_index(teco_qreg_stack, teco_qreg_stack_entry_t, teco_qreg_stack->len-1);
- if (!qreg->vtable->set_integer(qreg, entry->integer, error))
+ if (!qreg->vtable->set_number(qreg, entry->number, entry->is_float, error))
return FALSE;
/* exchange document ownership between stack entry and Q-Register */
diff --git a/src/qreg.h b/src/qreg.h
index 1eb2cf8..cbd6b2c 100644
--- a/src/qreg.h
+++ b/src/qreg.h
@@ -54,9 +54,15 @@ extern teco_view_t *teco_qreg_view;
* FIXME: We can probably get rid of all the separate undo callbacks/methods.
*/
typedef const struct {
+ /*
+ * FIXME: perhaps there should only be a set_number() callback with some
+ * teco_qreg_set_integer() shortcut.
+ */
/* already pushes undo tokens */
- gboolean (*set_integer)(teco_qreg_t *qreg, teco_int_t value, GError **error);
- gboolean (*get_integer)(teco_qreg_t *qreg, teco_int_t *ret, GError **error);
+ gboolean (*set_number)(teco_qreg_t *qreg, teco_number_t value,
+ gboolean is_float, GError **error);
+ gboolean (*get_number)(teco_qreg_t *qreg, teco_number_t *ret,
+ gboolean *is_float, GError **error);
gboolean (*set_string)(teco_qreg_t *qreg, const gchar *str, gsize len,
guint codepage, GError **error);
@@ -100,26 +106,48 @@ struct teco_qreg_t {
teco_qreg_vtable_t *vtable;
- teco_int_t integer;
+ teco_number_t number;
teco_doc_t string;
- /**
- * Whether to generate undo tokens (unnecessary for registers
- * in local qreg tables in macro invocations).
- *
- * @fixme Every QRegister has this field, but it only differs
- * between local and global QRegisters. This wastes space.
- * Or by deferring any decision about undo token creation to a layer
- * that knows which table it is accessing.
- * On the other hand, we will need another flag like
- * teco_qreg_current_must_undo.
- *
- * Otherwise, it might be possible to use a least significant bit
- * in one of the pointers...
- */
- gboolean must_undo;
+ struct teco_qreg_flags_t {
+ /**
+ * Whether to generate undo tokens (unnecessary for registers
+ * in local qreg tables in macro invocations).
+ *
+ * @fixme Every QRegister has this field, but it only differs
+ * between local and global QRegisters. This wastes space.
+ * Or by deferring any decision about undo token creation to a layer
+ * that knows which table it is accessing.
+ * On the other hand, we will need another flag like
+ * teco_qreg_current_must_undo.
+ *
+ * Otherwise, it might be possible to use a least significant bit
+ * in one of the pointers...
+ */
+ guint must_undo : 1;
+ /** whether the number cell is integer or float_val */
+ guint is_float : 1;
+ } flags;
};
+static inline gboolean
+teco_qreg_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error)
+{
+ return qreg->vtable->set_number(qreg, (teco_number_t){.i = value}, FALSE, error);
+}
+
+static inline gboolean
+teco_qreg_get_integer(teco_qreg_t *qreg, teco_int_t *ret, GError **error)
+{
+ teco_number_t number;
+ gboolean is_float;
+
+ if (!qreg->vtable->get_number(qreg, &number, &is_float, error))
+ return FALSE;
+ *ret = is_float ? (teco_int_t)number.f : number.i;
+ return TRUE;
+}
+
teco_qreg_t *teco_qreg_plain_new(const gchar *name, gsize len);
teco_qreg_t *teco_qreg_dot_new(void);
teco_qreg_t *teco_qreg_bufferinfo_new(void);
@@ -175,7 +203,7 @@ void teco_qreg_table_init_locals(teco_qreg_table_t *table, gboolean must_undo);
static inline teco_qreg_t *
teco_qreg_table_insert(teco_qreg_table_t *table, teco_qreg_t *qreg)
{
- qreg->must_undo = table->must_undo; // FIXME
+ qreg->flags.must_undo = table->must_undo; // FIXME
return (teco_qreg_t *)teco_rb3str_insert(&table->tree, TRUE, &qreg->head);
}
@@ -242,7 +270,7 @@ teco_qreg_table_get_integer(teco_qreg_table_t *table, const gchar *name,
return FALSE;
}
- return reg->vtable->get_integer(reg, ret, error);
+ return teco_qreg_get_integer(reg, ret, error);
}
teco_qreg_t *teco_qreg_table_edit_name(teco_qreg_table_t *table, const gchar *name,
diff --git a/src/ring.c b/src/ring.c
index d7643d0..f450e3a 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -539,7 +539,7 @@ teco_state_edit_file_initial(teco_machine_main_t *ctx, GError **error)
return TRUE;
teco_int_t id;
- if (!teco_expressions_pop_num_calc(&id, -1, error))
+ if (!teco_expressions_pop_int_calc(&id, -1, error))
return FALSE;
ctx->flags.allow_filename = TRUE;
@@ -688,7 +688,7 @@ teco_state_save_file_done(teco_machine_main_t *ctx, teco_string_t str, GError **
*/
teco_buffer_t *buffer = teco_ring_current;
if (teco_expressions_args() > 0) {
- teco_int_t id = teco_expressions_pop_num(0);
+ teco_int_t id = teco_expressions_pop_int(0);
buffer = teco_ring_find(id);
if (!buffer) {
teco_error_invalidbuf_set(error, id);
@@ -835,7 +835,7 @@ teco_state_ecommand_close(teco_machine_main_t *ctx, GError **error)
teco_buffer_t *buffer;
gboolean force;
if (teco_expressions_args() > 0) {
- teco_int_t id = teco_expressions_pop_num(0);
+ teco_int_t id = teco_expressions_pop_int(0);
buffer = teco_ring_find(ABS(id));
if (!buffer) {
teco_error_invalidbuf_set(error, ABS(id));
diff --git a/src/ring.h b/src/ring.h
index f5606d1..7f2db7c 100644
--- a/src/ring.h
+++ b/src/ring.h
@@ -159,7 +159,7 @@ teco_current_doc_must_undo(void)
* If there's no currently edited Q-Register
* we must be editing the current buffer
*/
- return !teco_qreg_current || teco_qreg_current->must_undo;
+ return !teco_qreg_current || teco_qreg_current->flags.must_undo;
}
/** update dot in the current document (Q-Register or buffer) */
@@ -167,7 +167,7 @@ static inline void
teco_current_doc_set_dot(teco_int_t dot)
{
if (teco_qreg_current) {
- if (teco_qreg_current->must_undo)
+ if (teco_qreg_current->flags.must_undo)
teco_undo_int(teco_qreg_current->string.dot);
teco_qreg_current->string.dot = dot;
} else {
diff --git a/src/sciteco.h b/src/sciteco.h
index 88078c2..9d34d50 100644
--- a/src/sciteco.h
+++ b/src/sciteco.h
@@ -23,19 +23,49 @@
#include <Scintilla.h>
-#if TECO_INTEGER == 32
+#if TECO_NUMBER == 32
+
typedef gint32 teco_int_t;
-#define TECO_INT_MODIFIER G_GINT32_MODIFIER
-#define TECO_INT_FORMAT G_GINT32_FORMAT
-#elif TECO_INTEGER == 64
+#define TECO_INT_MODIFIER G_GINT32_MODIFIER
+#define TECO_INT_FORMAT G_GINT32_FORMAT
+
+typedef gfloat teco_float_t;
+#define TECO_FLOAT_MODIFIER ""
+#define TECO_FLOAT_FORMAT "g"
+
+#elif TECO_NUMBER == 64
+
typedef gint64 teco_int_t;
-#define TECO_INT_MODIFIER G_GINT64_MODIFIER
-#define TECO_INT_FORMAT G_GINT64_FORMAT
+#define TECO_INT_MODIFIER G_GINT64_MODIFIER
+#define TECO_INT_FORMAT G_GINT64_FORMAT
+
+typedef gdouble teco_float_t;
+#define TECO_FLOAT_MODIFIER "l"
+#define TECO_FLOAT_FORMAT "lg"
+
#else
-#error Invalid TECO integer storage size
+
+#error Invalid TECO number storage size
+
#endif
/**
+ * A number cell - either integer or float.
+ * The tagging is stored outside (either in
+ * a flag or on the operator stack).
+ */
+typedef union {
+ teco_int_t i;
+ teco_float_t f;
+} teco_number_t;
+
+/*
+ * FIXME: The storage size of float and double are not guaranteed
+ * to be 32-bit and 64-bit.
+ */
+//G_STATIC_ASSERT(sizeof(teco_number_t) == sizeof(teco_int_t));
+
+/**
* A TECO boolean - this differs from C booleans.
* See teco_is_success()/teco_is_failure().
*/
diff --git a/src/search.c b/src/search.c
index 31a551e..0b59db5 100644
--- a/src/search.c
+++ b/src/search.c
@@ -102,12 +102,12 @@ teco_state_control_search_mode(teco_machine_main_t *ctx, GError **error)
teco_bool_t search_mode;
if (!teco_expressions_args() && teco_num_sign > 0) {
- if (!reg->vtable->get_integer(reg, &search_mode, error))
+ if (!teco_qreg_get_integer(reg, &search_mode, error))
return;
teco_expressions_push(search_mode);
} else {
- if (!teco_expressions_pop_num_calc(&search_mode, teco_num_sign, error) ||
- !reg->vtable->set_integer(reg, search_mode, error))
+ if (!teco_expressions_pop_int_calc(&search_mode, teco_num_sign, error) ||
+ !teco_qreg_set_integer(reg, search_mode, error))
return;
}
}
@@ -126,11 +126,11 @@ teco_state_search_initial(teco_machine_main_t *ctx, GError **error)
teco_search_parameters.pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
teco_int_t v1, v2;
- if (!teco_expressions_pop_num_calc(&v2, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v2, teco_num_sign, error))
return FALSE;
if (teco_expressions_args()) {
/* TODO: optional count argument? */
- v1 = teco_expressions_pop_num(0);
+ v1 = teco_expressions_pop_int(0);
if (v1 <= v2) {
teco_search_parameters.count = 1;
teco_search_parameters.from = teco_interface_glyphs2bytes_absdot(v1);
@@ -969,7 +969,7 @@ teco_state_search_process(teco_machine_main_t *ctx, teco_string_t str, gsize new
teco_qreg_t *search_reg = teco_qreg_table_find(&teco_qreg_table_globals, "_", 1);
g_assert(search_reg != NULL);
- if (!search_reg->vtable->set_integer(search_reg, TECO_FAILURE, error))
+ if (!teco_qreg_set_integer(search_reg, TECO_FAILURE, error))
return FALSE;
g_autoptr(teco_machine_qregspec_t) qreg_machine;
@@ -1063,7 +1063,7 @@ teco_state_search_process(teco_machine_main_t *ctx, teco_string_t str, gsize new
teco_ring_current = buffer;
}
- if (!search_reg->vtable->set_integer(search_reg, teco_bool(!count), error))
+ if (!teco_qreg_set_integer(search_reg, teco_bool(!count), error))
return FALSE;
if (!count)
@@ -1105,7 +1105,7 @@ teco_state_search_done(teco_machine_main_t *ctx, teco_string_t str, GError **err
}
teco_int_t search_state;
- if (!search_reg->vtable->get_integer(search_reg, &search_state, error))
+ if (!teco_qreg_get_integer(search_reg, &search_state, error))
return FALSE;
if (teco_machine_main_eval_colon(ctx) > 0)
@@ -1221,11 +1221,11 @@ teco_state_search_all_initial(teco_machine_main_t *ctx, GError **error)
teco_int_t v1, v2;
- if (!teco_expressions_pop_num_calc(&v2, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&v2, teco_num_sign, error))
return FALSE;
if (teco_expressions_args()) {
/* TODO: optional count argument? */
- v1 = teco_expressions_pop_num(0);
+ v1 = teco_expressions_pop_int(0);
if (v1 <= v2) {
teco_search_parameters.count = 1;
teco_search_parameters.from_buffer = teco_ring_find(v1);
diff --git a/src/spawn.c b/src/spawn.c
index 933d8f4..3761460 100644
--- a/src/spawn.c
+++ b/src/spawn.c
@@ -215,7 +215,7 @@ teco_state_execute_initial(teco_machine_main_t *ctx, GError **error)
teco_int_t line;
teco_spawn_ctx.from = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- if (!teco_expressions_pop_num_calc(&line, teco_num_sign, error))
+ if (!teco_expressions_pop_int_calc(&line, teco_num_sign, error))
return FALSE;
line += teco_interface_ssm(SCI_LINEFROMPOSITION, teco_spawn_ctx.from, 0);
teco_spawn_ctx.to = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0);
@@ -232,8 +232,8 @@ teco_state_execute_initial(teco_machine_main_t *ctx, GError **error)
default:
/* pipe and replace character range */
- teco_spawn_ctx.to = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_num(0));
- teco_spawn_ctx.from = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_num(0));
+ teco_spawn_ctx.to = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_int(0));
+ teco_spawn_ctx.from = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_int(0));
rc = teco_bool(teco_spawn_ctx.from <= teco_spawn_ctx.to &&
teco_spawn_ctx.from >= 0 && teco_spawn_ctx.to >= 0);
}
diff --git a/src/stdio-commands.c b/src/stdio-commands.c
index 34179c3..4bed7fd 100644
--- a/src/stdio-commands.c
+++ b/src/stdio-commands.c
@@ -18,6 +18,8 @@
#include "config.h"
#endif
+#include <string.h>
+
#include <glib.h>
#include "sciteco.h"
@@ -59,17 +61,31 @@ teco_print(teco_machine_main_t *ctx, guint radix, GError **error)
return FALSE;
}
- /*
- * teco_expressions_format() cannot easily be used
- * to format __unsigned__ integers.
- */
- 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_number_t number = teco_expressions_peek_num(0);
+ gboolean is_float = teco_expressions_peek_op(0) == TECO_OP_FLOAT;
+
+ gchar buf[TECO_EXPRESSIONS_FORMAT_LEN];
+ gint len;
+
+ if (is_float) {
+ /*
+ * FIXME: Perhaps we should support a custom formatter
+ * for octal and hexadecimals?
+ */
+ teco_expressions_format_float(buf, number.f, 6);
+ len = strlen(buf);
+ } else {
+ /*
+ * teco_expressions_format_int() cannot easily be used
+ * to format __unsigned__ integers.
+ */
+ 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;
+ }
+ len = g_snprintf(buf, sizeof(buf), fmt, number.i);
}
- gchar buf[32];
- gint len = g_snprintf(buf, sizeof(buf), fmt, teco_expressions_peek_num(0));
g_assert(len > 0);
if (!teco_machine_main_eval_colon(ctx))
buf[len++] = '\n';
@@ -79,18 +95,20 @@ teco_print(teco_machine_main_t *ctx, guint radix, GError **error)
}
/*$ "=" "==" "===" ":=" ":==" ":===" "print number"
- * <n>= -- Print integer as message
+ * <n>= -- Print number as message
* <n>==
* <n>===
* <n>:=
* <n>:==
* <n>:===
*
- * Shows integer <n> as a message in the message line and/or
+ * Shows integer or float <n> as a message in the message line and/or
* on the console.
- * One \(lq=\(rq formats the integer as a signed decimal number,
- * \(lq==\(rq formats as an unsigned octal number and
+ * One \(lq=\(rq formats the number as a signed decimal,
+ * \(lq==\(rq formats as an unsigned octal and
* \(lq===\(rq as an unsigned hexadecimal number.
+ * Floating point numbers will currently always be formatted in
+ * decimal with a precision of 6 and without an exponent.
* It is logged with the user-message severity.
* The command fails if <n> is not given.
*
@@ -144,7 +162,7 @@ teco_state_print_decimal_input(teco_machine_main_t *ctx, gunichar chr, GError **
if (ctx->flags.mode == TECO_MODE_NORMAL) {
if (G_LIKELY(!is_executing) && !teco_print(ctx, 10, error))
return NULL;
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
}
return teco_state_start_input(ctx, chr, error);
}
@@ -162,7 +180,7 @@ teco_state_print_decimal_end_of_macro(teco_machine_main_t *ctx, GError **error)
return TRUE;
if (!teco_print(ctx, 10, error))
return FALSE;
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
return TRUE;
}
@@ -195,7 +213,7 @@ teco_state_print_octal_input(teco_machine_main_t *ctx, gunichar chr, GError **er
if (ctx->flags.mode == TECO_MODE_NORMAL) {
if (!teco_print(ctx, 16, error))
return NULL;
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
}
return &teco_state_start;
}
@@ -203,7 +221,7 @@ teco_state_print_octal_input(teco_machine_main_t *ctx, gunichar chr, GError **er
if (ctx->flags.mode == TECO_MODE_NORMAL) {
if (G_LIKELY(!is_executing) && !teco_print(ctx, 8, error))
return NULL;
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
}
return teco_state_start_input(ctx, chr, error);
}
@@ -221,7 +239,7 @@ teco_state_print_octal_end_of_macro(teco_machine_main_t *ctx, GError **error)
return TRUE;
if (!teco_print(ctx, 8, error))
return FALSE;
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
return TRUE;
}
@@ -260,7 +278,7 @@ teco_state_print_string_done(teco_machine_main_t *ctx, teco_string_t str, GError
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- if (!teco_expressions_pop_num_calc(&type, TECO_MSG_USER, error))
+ if (!teco_expressions_pop_int_calc(&type, TECO_MSG_USER, error))
return NULL;
if (type < TECO_MSG_USER || type > TECO_MSG_MAX) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
@@ -398,7 +416,7 @@ teco_state_control_typeout(teco_machine_main_t *ctx, GError **error)
gchar *p = buf+args;
for (gint i = 0; i < args; i++) {
- teco_int_t chr = teco_expressions_pop_num(0);
+ teco_int_t chr = teco_expressions_pop_int(0);
if (chr < 0 || chr > 0xFF) {
teco_error_codepoint_set(error, "^T");
return;
@@ -415,7 +433,7 @@ teco_state_control_typeout(teco_machine_main_t *ctx, GError **error)
gchar *p = buf;
for (gint i = args; i > 0; i--) {
- teco_int_t chr = teco_expressions_peek_num(i-1);
+ teco_int_t chr = teco_expressions_peek_int(i-1);
if (chr < 0 || !g_unichar_validate(chr)) {
teco_error_codepoint_set(error, "^T");
return;
@@ -424,7 +442,7 @@ teco_state_control_typeout(teco_machine_main_t *ctx, GError **error)
}
/* we pop only now since we had to peek in reverse order */
for (gint i = 0; i < args; i++)
- teco_expressions_pop_num(0);
+ teco_expressions_pop_num(0, NULL);
teco_interface_msg_literal(TECO_MSG_USER, buf, p-buf);
}
diff --git a/src/symbols.c b/src/symbols.c
index dd5856e..5feaba0 100644
--- a/src/symbols.c
+++ b/src/symbols.c
@@ -242,7 +242,7 @@ teco_state_scintilla_symbols_done(teco_machine_main_t *ctx, teco_string_t str, G
return NULL;
}
- ctx->scintilla.iMessage = teco_expressions_pop_num(0);
+ ctx->scintilla.iMessage = teco_expressions_pop_int(0);
}
return &teco_state_scintilla_lparam;
@@ -494,12 +494,12 @@ teco_state_scintilla_lparam_done(teco_machine_main_t *ctx, teco_string_t str, GE
teco_int_t v;
if (!ctx->scintilla.wParam) {
- if (!teco_expressions_pop_num_calc(&v, 0, error))
+ if (!teco_expressions_pop_int_calc(&v, 0, error))
return NULL;
ctx->scintilla.wParam = v;
}
if (!lParam) {
- if (!teco_expressions_pop_num_calc(&v, 0, error))
+ if (!teco_expressions_pop_int_calc(&v, 0, error))
return NULL;
lParam = v;
}
diff --git a/src/undo.c b/src/undo.c
index 18f90c5..ab103a1 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -36,6 +36,7 @@ TECO_DEFINE_UNDO_SCALAR(guint);
TECO_DEFINE_UNDO_SCALAR(gsize);
TECO_DEFINE_UNDO_SCALAR(gssize);
TECO_DEFINE_UNDO_SCALAR(teco_int_t);
+TECO_DEFINE_UNDO_SCALAR(teco_number_t);
TECO_DEFINE_UNDO_SCALAR(gboolean);
TECO_DEFINE_UNDO_SCALAR(gconstpointer);
diff --git a/src/undo.h b/src/undo.h
index 476ca45..0b77026 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -188,6 +188,9 @@ TECO_DECLARE_UNDO_SCALAR(gssize);
TECO_DECLARE_UNDO_SCALAR(teco_int_t);
#define teco_undo_int(VAR) (*teco_undo_object_teco_int_t_push(&(VAR)))
+TECO_DECLARE_UNDO_SCALAR(teco_number_t);
+#define teco_undo_number(VAR) (*teco_undo_object_teco_number_t_push(&(VAR)))
+
TECO_DECLARE_UNDO_SCALAR(gboolean);
#define teco_undo_gboolean(VAR) (*teco_undo_object_gboolean_push(&(VAR)))
diff --git a/src/view.c b/src/view.c
index d866a5e..35e5e10 100644
--- a/src/view.c
+++ b/src/view.c
@@ -825,7 +825,7 @@ teco_view_get_character(teco_view_t *ctx, gsize pos, gsize len)
/*
* Make sure that the -1/-2 error values are preserved.
* The sign bit in UCS-4/UTF-32 is unused, so this will even
- * suffice if TECO_INTEGER == 32.
+ * suffice if TECO_NUMBER == 32.
*/
gint32 rc = g_utf8_get_char_validated(buf, -1);
return rc < 0 ? rc-1 : rc;