diff options
| author | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2026-11-01 20:08:10 +0100 |
|---|---|---|
| committer | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2026-11-01 20:44:46 +0100 |
| commit | 5a4537cb67c77daba87d820ddcb4bd7574a8080d (patch) | |
| tree | 61cb90b669827ad7f7521ce07d261a3bf597518a | |
| parent | 1975c0d93373c41f9aef943980cb783188603e6d (diff) | |
refactored several commands and states related to numbers into number-commands.cHEADmaster-fmsbw-cimaster
This includes teco_state_number, `\`, all operators, braces, comma,
`^_`, `^O`, `^D`, `^R`.
| -rw-r--r-- | README | 2 | ||||
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/core-commands.c | 369 | ||||
| -rw-r--r-- | src/number-commands.c | 403 | ||||
| -rw-r--r-- | src/number-commands.h | 44 |
5 files changed, 452 insertions, 367 deletions
@@ -46,6 +46,8 @@ Features more similar to other imperative languages. * Operator precedence in arithmetic expressions and an argument stack that may be modified by the user (commands may have more than two arguments) +* Floating point arithmetics: a SciTECO number is either a signed integer or + floating point number (dynamic typing) * Extended Q-Register namespace (arbitrary strings): Can be used to build libraries and can be abused as a data structure. * Quick access to the system clipboard via special `~` registers, even on ncurses! diff --git a/src/Makefile.am b/src/Makefile.am index eb532e3..d307227 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,7 @@ libsciteco_base_la_SOURCES = main.c sciteco.h list.h \ ring.c ring.h \ parser.c parser.h \ core-commands.c core-commands.h \ + number-commands.c number-commands.h \ move-commands.c move-commands.h \ stdio-commands.c stdio-commands.h \ search.c search.h \ diff --git a/src/core-commands.c b/src/core-commands.c index 583142b..b7e7a60 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -45,6 +45,7 @@ #include "memory.h" #include "eol.h" #include "qreg.h" +#include "number-commands.h" #include "stdio-commands.h" #include "qreg-commands.h" #include "goto-commands.h" @@ -55,108 +56,6 @@ 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 */ - guint divisor = ctx->number.divisor; - teco_undo_guint(ctx->number.divisor) = 0; /* default */ - - if (ctx->flags.mode > TECO_MODE_NORMAL) - return TRUE; - - 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; -} - -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`). - */ - 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->number.i here since - * we can assume that "^R" is a plain register. - */ - 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'; - 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; - if (ctx->number.divisor) - teco_undo_guint(ctx->number.divisor) *= radix; - - 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. @@ -232,76 +131,6 @@ teco_get_range_args(const gchar *cmd, teco_int_t *from_glyphs_ret, gsize *from_r return TRUE; } -/* - * NOTE: This needs some extra code in teco_state_start_input(). - */ -static void -teco_state_start_mul(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_MUL, error); -} - -static void -teco_state_start_div(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_DIV, error); -} - -static void -teco_state_start_plus(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_ADD, error); -} - -static void -teco_state_start_minus(teco_machine_main_t *ctx, GError **error) -{ - if (!teco_expressions_args()) - teco_set_num_sign(-teco_num_sign); - else - teco_expressions_push_calc(TECO_OP_SUB, error); -} - -static void -teco_state_start_and(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_AND, error); -} - -static void -teco_state_start_or(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_OR, error); -} - -static void -teco_state_start_brace_open(teco_machine_main_t *ctx, GError **error) -{ - if (teco_num_sign < 0) { - teco_set_num_sign(1); - if (!teco_expressions_eval(FALSE, error)) - return; - teco_expressions_push(-1); - if (!teco_expressions_push_calc(TECO_OP_MUL, error)) - return; - } - teco_expressions_brace_open(); -} - -static void -teco_state_start_brace_close(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_brace_close(error); -} - -static void -teco_state_start_comma(teco_machine_main_t *ctx, GError **error) -{ - if (!teco_expressions_eval(FALSE, error)) - return; - teco_expressions_push_op(TECO_OP_NEW); -} - /*$ "." dot * \&. -> dot -- Return buffer position * @@ -354,106 +183,6 @@ teco_state_start_range(teco_machine_main_t *ctx, GError **error) teco_expressions_push(teco_interface_bytes2glyphs_absdot(pos)); } -/*$ \[rs] - * n\\ -- Insert or read ASCII numbers - * \\ -> n - * - * 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. - * 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 - * numbers as ASCII characters. - */ -static void -teco_state_start_backslash(teco_machine_main_t *ctx, GError **error) -{ - if (!teco_expressions_eval(FALSE, 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]; - 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); - - teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot(); - /* - * We can assume that `len` is already in glyphs, - * i.e. formatted numbers will never use multi-byte/Unicode characters. - */ - teco_undo_int(teco_ranges[0].to) = teco_ranges[0].from + len; - teco_undo_guint(teco_ranges_count) = 1; - - teco_current_doc_set_dot(teco_ranges[0].to); - - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_DELETERANGE, - teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), len); - teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)str); - teco_ring_dirtify(); - } else { - 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 value = 0; - guint divisor = 0; - gint sign = 1; - - if (c == '-') { - pos++; - sign = -1; - } - - 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) - value = (value*radix) + (c - '0'); - else if (c >= 'A' && - c <= 'A' + MIN(radix - 10, 26) - 1) - value = (value*radix) + 10 + (c - 'A'); - else - break; - - if (divisor) - divisor *= radix; - } - - if (divisor) - teco_expressions_push(sign * ((teco_float_t)value / divisor)); - else - teco_expressions_push(sign * value); - } -} - /* * NOTE: This needs some extra code in teco_state_start_input(). */ @@ -1440,101 +1169,6 @@ teco_state_endcond(teco_machine_main_t *ctx, const gchar *cmd, GError **error) return &teco_state_start; } -/*$ ^_ negate - * n^_ -> ~n -- Binary negation - * - * Binary negates (complements) <n> and returns - * the result. - * Binary complements are often used to negate - * \*(ST booleans. - */ -static void -teco_state_control_negate(teco_machine_main_t *ctx, GError **error) -{ - if (!teco_expressions_eval(FALSE, error)) - return; - - if (!teco_expressions_args()) { - teco_error_argexpected_set(error, "^_"); - return; - } - - teco_expressions_push(~teco_expressions_pop_int(0)); -} - -static void -teco_state_control_pow(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_POW, error); -} - -static void -teco_state_control_mod(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_MOD, error); -} - -static void -teco_state_control_xor(teco_machine_main_t *ctx, GError **error) -{ - teco_expressions_push_calc(TECO_OP_XOR, error); -} - -/*$ ^O octal - * ^O -- Set radix to 8 (octal) - */ -static void -teco_state_control_octal(teco_machine_main_t *ctx, GError **error) -{ - teco_qreg_t *qreg = ctx->qreg_table_locals->radix; - assert(qreg != NULL); - if (!teco_qreg_set_integer(qreg, 8, NULL)) - return; -} - -/*$ ^D decimal - * ^D -- Set radix to 10 (decimal) - */ -static void -teco_state_control_decimal(teco_machine_main_t *ctx, GError **error) -{ - teco_qreg_t *qreg = ctx->qreg_table_locals->radix; - assert(qreg != NULL); - if (!teco_qreg_set_integer(qreg, 10, NULL)) - return; -} - -/*$ ^R radix - * radix^R -- Set and get radix - * ^R -> radix - * - * Set current radix to any value <radix> larger than or equal to 2. - * If <radix> is omitted, the command instead - * returns the current radix. - * - * An alternative way to access the radix is via the \(lq^R\(rq local Q-Register. - * Consequently, the radix is local to the current macro invocation frame, - * unless the macro call was colon-modified. - */ -static void -teco_state_control_radix(teco_machine_main_t *ctx, GError **error) -{ - if (!teco_expressions_eval(FALSE, error)) - return; - - teco_qreg_t *qreg = ctx->qreg_table_locals->radix; - assert(qreg != NULL); - teco_int_t radix = 10; - - if (!teco_expressions_args()) { - teco_qreg_get_integer(qreg, &radix, NULL); - teco_expressions_push(radix); - } else { - if (!teco_qreg_set_integer(qreg, teco_expressions_pop_int(0), error)) - return; - } -} - /*$ "^E" ":^E" glyphs2bytes bytes2glyphs * glyphs^E -> bytes -- Translate between glyph and byte indexes * bytes:^E -> glyphs @@ -1756,6 +1390,7 @@ teco_state_control_time(teco_machine_main_t *ctx, GError **error) * * First sleep <n> milliseconds before refreshing the view, * i.e. drawing it. + * <n> can be a floating point number. * By default it sleeps for 10ms. * This can be added to loops to make progress visible * in interactive mode. diff --git a/src/number-commands.c b/src/number-commands.c new file mode 100644 index 0000000..27cf8ba --- /dev/null +++ b/src/number-commands.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2012-2026 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib.h> + +#include "sciteco.h" +#include "undo.h" +#include "qreg.h" +#include "lexer.h" +#include "ring.h" +#include "parser.h" +#include "interface.h" +#include "expressions.h" +#include "core-commands.h" +#include "number-commands.h" + +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 */ + guint divisor = ctx->number.divisor; + teco_undo_guint(ctx->number.divisor) = 0; /* default */ + + if (ctx->flags.mode > TECO_MODE_NORMAL) + return TRUE; + + 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; +} + +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`). + */ + 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->number.i here since + * we can assume that "^R" is a plain register. + */ + 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'; + 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; + if (ctx->number.divisor) + teco_undo_guint(ctx->number.divisor) *= radix; + + 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 +); + +/* + * Transition table callbacks + */ + +/*$ \[rs] + * n\\ -- Insert or read ASCII numbers + * \\ -> n + * + * 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. + * 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 + * numbers as ASCII characters. + */ +void +teco_state_start_backslash(teco_machine_main_t *ctx, GError **error) +{ + if (!teco_expressions_eval(FALSE, 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]; + 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); + + teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot(); + /* + * We can assume that `len` is already in glyphs, + * i.e. formatted numbers will never use multi-byte/Unicode characters. + */ + teco_undo_int(teco_ranges[0].to) = teco_ranges[0].from + len; + teco_undo_guint(teco_ranges_count) = 1; + + teco_current_doc_set_dot(teco_ranges[0].to); + + if (teco_current_doc_must_undo()) + undo__teco_interface_ssm(SCI_DELETERANGE, + teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), len); + teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)str); + teco_ring_dirtify(); + } else { + 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 value = 0; + guint divisor = 0; + gint sign = 1; + + if (c == '-') { + pos++; + sign = -1; + } + + 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) + value = (value*radix) + (c - '0'); + else if (c >= 'A' && + c <= 'A' + MIN(radix - 10, 26) - 1) + value = (value*radix) + 10 + (c - 'A'); + else + break; + + if (divisor) + divisor *= radix; + } + + if (divisor) + teco_expressions_push(sign * ((teco_float_t)value / divisor)); + else + teco_expressions_push(sign * value); + } +} + +/* + * NOTE: This needs some extra code in teco_state_start_input(). + */ +void +teco_state_start_mul(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_MUL, error); +} + +void +teco_state_start_div(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_DIV, error); +} + +void +teco_state_start_plus(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_ADD, error); +} + +void +teco_state_start_minus(teco_machine_main_t *ctx, GError **error) +{ + if (!teco_expressions_args()) + teco_set_num_sign(-teco_num_sign); + else + teco_expressions_push_calc(TECO_OP_SUB, error); +} + +void +teco_state_start_and(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_AND, error); +} + +void +teco_state_start_or(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_OR, error); +} + +void +teco_state_start_brace_open(teco_machine_main_t *ctx, GError **error) +{ + if (teco_num_sign < 0) { + teco_set_num_sign(1); + if (!teco_expressions_eval(FALSE, error)) + return; + teco_expressions_push(-1); + if (!teco_expressions_push_calc(TECO_OP_MUL, error)) + return; + } + teco_expressions_brace_open(); +} + +void +teco_state_start_brace_close(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_brace_close(error); +} + +void +teco_state_start_comma(teco_machine_main_t *ctx, GError **error) +{ + if (!teco_expressions_eval(FALSE, error)) + return; + teco_expressions_push_op(TECO_OP_NEW); +} + +/*$ ^_ negate + * n^_ -> ~n -- Binary negation + * + * Binary negates (complements) <n> and returns + * the result. + * Binary complements are often used to negate + * \*(ST booleans. + */ +void +teco_state_control_negate(teco_machine_main_t *ctx, GError **error) +{ + if (!teco_expressions_eval(FALSE, error)) + return; + + if (!teco_expressions_args()) { + teco_error_argexpected_set(error, "^_"); + return; + } + + teco_expressions_push(~teco_expressions_pop_int(0)); +} + +void +teco_state_control_pow(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_POW, error); +} + +void +teco_state_control_mod(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_MOD, error); +} + +void +teco_state_control_xor(teco_machine_main_t *ctx, GError **error) +{ + teco_expressions_push_calc(TECO_OP_XOR, error); +} + +/*$ ^O octal + * ^O -- Set radix to 8 (octal) + */ +void +teco_state_control_octal(teco_machine_main_t *ctx, GError **error) +{ + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + g_assert(qreg != NULL); + if (!teco_qreg_set_integer(qreg, 8, NULL)) + return; +} + +/*$ ^D decimal + * ^D -- Set radix to 10 (decimal) + */ +void +teco_state_control_decimal(teco_machine_main_t *ctx, GError **error) +{ + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + g_assert(qreg != NULL); + if (!teco_qreg_set_integer(qreg, 10, NULL)) + return; +} + +/*$ ^R radix + * radix^R -- Set and get radix + * ^R -> radix + * + * Set current radix to any value <radix> larger than or equal to 2. + * If <radix> is omitted, the command instead + * returns the current radix. + * + * An alternative way to access the radix is via the \(lq^R\(rq local Q-Register. + * Consequently, the radix is local to the current macro invocation frame, + * unless the macro call was colon-modified. + */ +void +teco_state_control_radix(teco_machine_main_t *ctx, GError **error) +{ + if (!teco_expressions_eval(FALSE, error)) + return; + + teco_qreg_t *qreg = ctx->qreg_table_locals->radix; + g_assert(qreg != NULL); + teco_int_t radix = 10; + + if (!teco_expressions_args()) { + teco_qreg_get_integer(qreg, &radix, NULL); + teco_expressions_push(radix); + } else { + if (!teco_qreg_set_integer(qreg, teco_expressions_pop_int(0), error)) + return; + } +} diff --git a/src/number-commands.h b/src/number-commands.h new file mode 100644 index 0000000..79fa77b --- /dev/null +++ b/src/number-commands.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012-2026 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#pragma once + +#include <glib.h> + +#include "sciteco.h" +#include "parser.h" + +teco_state_t *teco_state_number_input(teco_machine_main_t *ctx, gunichar chr, GError **error); + +void teco_state_start_backslash(teco_machine_main_t *ctx, GError **error); + +void teco_state_start_mul(teco_machine_main_t *ctx, GError **error); +void teco_state_start_div(teco_machine_main_t *ctx, GError **error); +void teco_state_start_plus(teco_machine_main_t *ctx, GError **error); +void teco_state_start_minus(teco_machine_main_t *ctx, GError **error); +void teco_state_start_and(teco_machine_main_t *ctx, GError **error); +void teco_state_start_or(teco_machine_main_t *ctx, GError **error); +void teco_state_start_brace_open(teco_machine_main_t *ctx, GError **error); +void teco_state_start_brace_close(teco_machine_main_t *ctx, GError **error); +void teco_state_start_comma(teco_machine_main_t *ctx, GError **error); + +void teco_state_control_negate(teco_machine_main_t *ctx, GError **error); +void teco_state_control_pow(teco_machine_main_t *ctx, GError **error); +void teco_state_control_mod(teco_machine_main_t *ctx, GError **error); +void teco_state_control_xor(teco_machine_main_t *ctx, GError **error); +void teco_state_control_octal(teco_machine_main_t *ctx, GError **error); +void teco_state_control_decimal(teco_machine_main_t *ctx, GError **error); +void teco_state_control_radix(teco_machine_main_t *ctx, GError **error); |
