aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2026-10-25 02:39:11 +0100
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2026-10-25 11:38:05 +0100
commite0bf13622b0922252439e3fa8b43df9877678d75 (patch)
tree629c040970a3c6869a10bf3c74a853e5eed2d908
parentc4146a9e57a897c5393e1c4999110fd95208f43f (diff)
numbers are separate parser states and there are syntactic octal and hexadecimal constants nowHEADmaster
* Instead of every digit being a stack-modifying standalone command, there is a single continuous parser state (teco_state_number) now - even though it has certain unique characteristics. They are consequently documented in the ARITHMETICS AND EXPRESSIONS chapter of sciteco(7). * A number will therefore be rubbed out entirely by CTRL+W, including a possible @-modifier. * (!) Spaces within constants are no longer ignored, but will push separate numbers to the stack instead (`1 2 3` pushes 3 numbers). The significance of the `,` operator is thus further reduced. * (!) You can no longer "append" to numbers on the stack as in `Qa001` - this was never actually used and caused more confusion. * (!) Digits outside the current radix are no longer accepted. E.g. digit 2 after `2^R`. This feature was also never used. * (!) Octal constants are supported with leading zeros as in C. This works regardless of the current radix set via `8^R` or `^O`. It remains to see whether this is actually useful or is rather annoying (since every constant beginning with 0 is automatically octal, you cannot left-pad arbitrary numbers with 0). * Hexadecimal constants are supported by at-modifying them as in `@0ff`. They must still begin with a digit 0-9 and since A-F are valid SciTECO commands, you may need additional spaces to separate hexadecimal constants from regular commands as in `@0ff D`. During syntax highlighting the number style is extended back to the at-sign. Note: Hexadecimal constants couldn't have been realized via `16^R` (or some shortcut) since that depends on runtime state, but we must know during parsing whether symbols A-F are part of the number of regular SciTECO commands. * The new hexadecimal constants are used in the color schemes now. Thanks to them, we no longer need workarounds like the "color.rgb" macro. * Since numbers don't have a dedicated terminator character, but any letter may terminate it, the number state must behave similar to a start state. Consequently, hexadecimal constants will currently be auto-case-folded if you enabled this feature. I recommend not to write hex constants in upper-case, though. Perhaps this should be worked around with a custom process_edit_cmd() callback. * Test suite updated
-rw-r--r--TODO33
-rw-r--r--doc/sciteco.7.template40
-rw-r--r--lib/color.tes74
-rw-r--r--lib/colors/solarized.tes32
-rw-r--r--src/core-commands.c106
-rw-r--r--src/core-commands.h1
-rw-r--r--src/expressions.c17
-rw-r--r--src/expressions.h2
-rw-r--r--src/lexer.c63
-rw-r--r--src/parser.h14
-rw-r--r--tests/testsuite.at9
11 files changed, 234 insertions, 157 deletions
diff --git a/TODO b/TODO
index 514ad4f..6c7f0f6 100644
--- a/TODO
+++ b/TODO
@@ -274,30 +274,11 @@ Features:
be disabled in parser-terminator-mode).
Q-Reg specs should support alternative balanced escapes as well
for symmetry.
- * Numbers could be separate states instead of stack operating
- commands. The current behaviour has few benefits.
- If a number is a regular command that stops parsing at the
- first invalid character in the current radix, we could write
- hexadcimal constants like 16^R0BEEF^D (still clumsy...).
- On the other hand, the radix is runtime state and parsing
- must not depend on runtime state in SciTECO to ensure
- parseability of the language.
- We could use the @ modifier though, so a hexadecimal constant
- must always begin with @0-@9.
- @1e\ would be enough to insert the escape character.
- If the number starts with a non-decimal digit, you will have to
- prefix it with 0, as in @0FF. If it is followed by a command A-F,
- you can add an additional space or even brace the expression as in
- `@0FF A` or `(@0FF)A`.
- The same could be done with a caret: `^0FF`.
- Alternatively, ^P would still be available as a single-key-press
- hex-constant prefix.
- Octal constants could begin with 0 as in C, e.g. 0666
- * Furthermore, this opens the possibility of floating point
- numbers. The "." command does not take arguments, so it
- could be part of the number syntax. This disallows constructs
- like "23." to push 23 and Dot which have to be replaced by
- "23,.".
+ * Floating point numbers constants.
+ The "." command does not take arguments, so it
+ could be part of the number syntax. This disallows constructs
+ like "23." to push 23 and Dot which have to be replaced by
+ "23,.".
* In the most simple case, all TECO numbers could be
floats/doubles with division/modulo having integer semantics.
A separate floating point division operator could be introduced
@@ -307,11 +288,7 @@ Features:
The operator decides how to interpret the arguments
and the return type. This is good since we'd need less operators
and it's good to keep true integers around.
- * Having a separate number parser state will slightly simplify
- number syntax highlighting (see teco_lexer_getstyle()).
* The first application of floats could be for U[lexer.font].
- * ^H as shortcut for 16^R. ^H is backspace, but it won't be
- necessary to type very often.
* Key macro masking flag for the beginning of the command
line. May be useful e.g. for solarized's F5 key (i.e. function
key macros that need to terminate the command line as they
diff --git a/doc/sciteco.7.template b/doc/sciteco.7.template
index 0ff3f51..fd909f9 100644
--- a/doc/sciteco.7.template
+++ b/doc/sciteco.7.template
@@ -1009,7 +1009,6 @@ preference are supported.
Since the same stack is used for arithmetics and command arguments,
arithmetics and arbitrary code may be freely mixed like in
expression-centered languages and some classic TECOs.
-In fact, in \*(ST digits are basically stack-operating commands.
For the sake of macro portability all integers are 64-bit signed
integers regardless of the host's architecture.
The integer storage size may be changed at \*(ST build time
@@ -1029,6 +1028,37 @@ binary (bit by bit) negation for instance using the
.B ^_
command.
.
+.SS Constants
+.SCITECO_TOPIC constants decimal octal hexadecimal
+.
+All integer constants have to begin with a digit from 0 to 9.
+Constants beginning with 0 are octal constants where every digit
+represents 3 bits \(em similar to C (e.g. \(lq033\(rq is decimal 27).
+Digits 8 and 9 are not allowed in octal constants.
+If the constant is at-modified, the subsequent constant is hexadecimal
+(e.g. \(lq@0ff\(rq is decimal 255).
+It must still always begin with a digit from 0 to 9 and ends with any
+symbol other than 0-9 or A-F.
+Since many \*(ST commands begin with letters A-F, you may have to separate
+hexadecimal constants with an explicit space or even braces from its
+subsequent command (e.g. as in \(lq@0ff D\(rq, which is equivalent to
+\(lq255D\(rq).
+.LP
+All remaining numbers (not at-modified and beginning with 1-9) are
+interpreted according to the current radix, as configured by the
+\fB^R\fP, \fB^D\fP and \fB^O\fP commands.
+The default radix is 10, so all remaining constants are usually
+decimal.
+Digits larger than or equal to the current radix are not allowed in
+constants.
+.LP
+A complete constant is pushed on the number stack.
+\# Mention the minus operator?
+\# It's not technically part of the number syntax.
+Since whitespace characters will also terminate integer constants
+you can push a sequence of integers by space-separating them
+(i.e. \(lq0 1 2 3\(rq is equivalent to \(lq0,1,2,3\(rq).
+.
.SS Operators
.SCITECO_TOPIC operators
.
@@ -1115,9 +1145,10 @@ governing braces might be made stricter in a future release of \*(ST.
Another construct that may be used as an argument barrier to explicitly
separate arguments is the comma (\(lq,\(rq).
It is obligatory when trying to push a sequence of number constants
-like in \(lq1,2\(rq but is optional in many contexts where it is
+like in \(lq1,2\(rq (although \(lq1 2\(rq will also work)
+but is optional in many contexts where it is
mandatory in classic TECO, like in \(lq.ZD\(rq.
-I recommend to use it as much as possible in code where clarity
+It is recommended to use it as much as possible in code where clarity
matters.
.LP
The arithmetic evaluation order established by the operator precedence
@@ -1137,9 +1168,6 @@ default value if the argument stack is empty so that expressions like
However in front of opening brackets, like in \(lq-(1+2)\(rq, it is
roughly equivalent to \(lq-1*(1+2)\(rq so that values calculated in
brackets can be easily negated.
-In front of digits the prefix sign specifies whether the digit is
-added or subtracted from the value on the stack so that in front of
-numbers the result is a negative number.
.
.
.SH BUFFER RING
diff --git a/lib/color.tes b/lib/color.tes
index a45c646..c2a8691 100644
--- a/lib/color.tes
+++ b/lib/color.tes
@@ -1,57 +1,51 @@
-!* <r,g,b>M[color.rgb] -> Scintilla color *!
-@[color.rgb]{
- U.bU.gU.r
- (Q.r # Q.g*256 # Q.b*256*256)
-}
-
0EJ-1"=
!*
- * The Curses RGB values are hardcoded by Scinterm
+ * The Curses BGR values are hardcoded by Scinterm
* and do not necessarily correspond to the renditions by
* terminal emulators.
*!
!* These 8 colors should be available on every system *!
- 000,000,000:M[color.rgb]U[color.black]
- 128,000,000:M[color.rgb]U[color.red]
- 000,128,000:M[color.rgb]U[color.green]
- 128,128,000:M[color.rgb]U[color.yellow]
- 000,000,128:M[color.rgb]U[color.blue]
- 128,000,128:M[color.rgb]U[color.magenta]
- 000,128,128:M[color.rgb]U[color.cyan]
- 192,192,192:M[color.rgb]U[color.white]
+ @0000000U[color.black]
+ @0000080U[color.red]
+ @0008000U[color.green]
+ @0008080U[color.yellow]
+ @0800000U[color.blue]
+ @0800080U[color.magenta]
+ @0808000U[color.cyan]
+ @0c0c0c0U[color.white]
!* Light color variants, might not be available on every terminal *!
- 064,064,064:M[color.rgb]U[color.lblack]
- 255,000,000:M[color.rgb]U[color.lred]
- 000,255,000:M[color.rgb]U[color.lgreen]
- 255,255,000:M[color.rgb]U[color.lyellow]
- 000,000,255:M[color.rgb]U[color.lblue]
- 255,000,255:M[color.rgb]U[color.lmagenta]
- 000,255,255:M[color.rgb]U[color.lcyan]
- 255,255,255:M[color.rgb]U[color.lwhite]
+ @0404040U[color.lblack]
+ @00000ffU[color.lred]
+ @000ff00U[color.lgreen]
+ @000ffffU[color.lyellow]
+ @0ff0000U[color.lblue]
+ @0ff00ffU[color.lmagenta]
+ @0ffff00U[color.lcyan]
+ @0ffffffU[color.lwhite]
|
!*
- * The GTK RGB values are the ones used by common terminal
+ * The GTK BGR values are the ones used by common terminal
* emulators. This should ensure that color schemes based
* on the 16 default colors look similar on Curses and GTK.
*!
- 000,000,000:M[color.rgb]U[color.black]
- 205,000,000:M[color.rgb]U[color.red]
- 000,205,000:M[color.rgb]U[color.green]
- 205,205,000:M[color.rgb]U[color.yellow]
- 000,000,238:M[color.rgb]U[color.blue]
- 205,000,205:M[color.rgb]U[color.magenta]
- 000,205,205:M[color.rgb]U[color.cyan]
- 229,229,229:M[color.rgb]U[color.white]
+ @0000000U[color.black]
+ @00000cdU[color.red]
+ @000cd00U[color.green]
+ @000cdcdU[color.yellow]
+ @0ee0000U[color.blue]
+ @0cd00cdU[color.magenta]
+ @0cdcd00U[color.cyan]
+ @0e5e5e5U[color.white]
- 127,127,127:M[color.rgb]U[color.lblack]
- 255,000,000:M[color.rgb]U[color.lred]
- 000,255,000:M[color.rgb]U[color.lgreen]
- 255,255,000:M[color.rgb]U[color.lyellow]
- 092,092,255:M[color.rgb]U[color.lblue]
- 255,000,255:M[color.rgb]U[color.lmagenta]
- 000,255,255:M[color.rgb]U[color.lcyan]
- 255,255,255:M[color.rgb]U[color.lwhite]
+ @07f7f7fU[color.lblack]
+ @00000ffU[color.lred]
+ @000ff00U[color.lgreen]
+ @000ffffU[color.lyellow]
+ @0ff5c5cU[color.lblue]
+ @0ff00ffU[color.lmagenta]
+ @0ffff00U[color.lcyan]
+ @0ffffffU[color.lwhite]
'
!* <[[flags,]bg,]fg,style>M[color.set] *!
diff --git a/lib/colors/solarized.tes b/lib/colors/solarized.tes
index aeb9cb9..529df74 100644
--- a/lib/colors/solarized.tes
+++ b/lib/colors/solarized.tes
@@ -19,22 +19,22 @@
*!
0,3EJ -1,-1,7EJ !* no palette & no default colors *!
-000,043,054:M[color.rgb]U[color.base03]
-007,054,066:M[color.rgb]U[color.base02]
-088,110,117:M[color.rgb]U[color.base01]
-101,123,131:M[color.rgb]U[color.base00]
-131,148,150:M[color.rgb]U[color.base0]
-147,161,161:M[color.rgb]U[color.base1]
-238,232,213:M[color.rgb]U[color.base2]
-253,246,227:M[color.rgb]U[color.base3]
-181,137,000:M[color.rgb]U[color.yellow]
-203,075,022:M[color.rgb]U[color.orange]
-220,050,047:M[color.rgb]U[color.red]
-211,054,130:M[color.rgb]U[color.magenta]
-108,113,196:M[color.rgb]U[color.violet]
-038,139,210:M[color.rgb]U[color.blue]
-042,161,152:M[color.rgb]U[color.cyan]
-133,153,000:M[color.rgb]U[color.green]
+@0362b00U[color.base03]
+@0423607U[color.base02]
+@0756e58U[color.base01]
+@0837b65U[color.base00]
+@0969483U[color.base0]
+@0a1a193U[color.base1]
+@0d5e8eeU[color.base2]
+@0e3f6fdU[color.base3]
+@00089b5U[color.yellow]
+@0164bcbU[color.orange]
+@02f32dcU[color.red]
+@08236d3U[color.magenta]
+@0c4716cU[color.violet]
+@0d28b26U[color.blue]
+@098a12aU[color.cyan]
+@0009985U[color.green]
:Q[solarized.light]"< 0U[solarized.light] '
@[solarized.light]{
diff --git a/src/core-commands.c b/src/core-commands.c
index 6362d95..f80cd59 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -55,6 +55,89 @@ static teco_state_t *teco_state_control_input(teco_machine_main_t *ctx, gunichar
static teco_state_t *teco_state_ctlc_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
static teco_state_t *teco_state_endcond(teco_machine_main_t *ctx, const gchar *cmd, GError **error);
+static teco_state_t teco_state_number;
+
+static gboolean
+teco_state_number_end_of_macro(teco_machine_main_t *ctx, GError **error)
+{
+ teco_undo_guint(ctx->number.radix) = 0; /* default */
+
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return TRUE;
+
+ teco_expressions_push(ctx->number.value);
+ teco_undo_int(ctx->number.value) = 0;
+ return TRUE;
+}
+
+static teco_state_t *
+teco_state_number_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
+{
+ /*
+ * Hexadecimal constants (@[0-9][0-9a-zA-Z]*) are terminated by any non-hexadecimal character.
+ * This means you may have to append a space or braces to stop parsing if the next
+ * command would begin with such a letter (e.g. `@0ff D`).
+ */
+ gboolean end_of_number = chr <= 0xFF && (ctx->number.radix == 16 ? !g_ascii_isxdigit(chr)
+ : !g_ascii_isdigit(chr));
+ if (end_of_number) {
+ teco_state_number_end_of_macro(ctx, error);
+ return teco_state_start_input(ctx, chr, error);
+ }
+
+ if (!ctx->number.radix) {
+ if (teco_machine_main_eval_at(ctx))
+ /* @[0-9] starts a hexadecimal constant */
+ teco_undo_guint(ctx->number.radix) = 16;
+ else if (!ctx->number.value && chr == '0')
+ /* a leading zero starts an octal constant */
+ teco_undo_guint(ctx->number.radix) = 8;
+ }
+
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return &teco_state_number;
+
+ teco_int_t radix = ctx->number.radix;
+ if (!radix) {
+ /*
+ * The ^R radix is only considered if we do not
+ * syntactically signify the radix (octal or hex constants).
+ *
+ * FIXME: We could just access qreg->integer here since
+ * we can assume that "^R" is a plain register.
+ */
+ teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
+ qreg->vtable->get_integer(qreg, &radix, NULL);
+ }
+
+ chr = teco_ascii_toupper(chr);
+ guint digit = 'A' <= chr && chr <= 'F' ? chr-'A'+10 : chr-'0';
+ if (digit >= radix) {
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Digit \"%c\" not allowed in radix %" TECO_INT_FORMAT,
+ (gchar)chr, radix);
+ return NULL;
+ }
+
+ teco_undo_int(ctx->number.value) = ctx->number.value*radix + digit;
+
+ return &teco_state_number;
+}
+
+/*
+ * FIXME: This has to be a "command" since we chain to teco_state_start_input()
+ * and want case-folding to work properly.
+ * On the downside, this will also case-fold the hexadecimal constants (A-F).
+ * There would be cumbersome workarounds with custom process_edit_cmd callbacks,
+ * though.
+ */
+static TECO_DEFINE_STATE_START(teco_state_number,
+ .input_cb = (teco_state_input_cb_t)teco_state_number_input,
+ .end_of_macro_cb = (teco_state_end_of_macro_cb_t)teco_state_number_end_of_macro,
+ .style = SCE_SCITECO_NUMBER,
+ .is_start = FALSE
+);
+
/**
* Translate buffer range arguments from the expression stack to
* a from-position and length in bytes.
@@ -760,32 +843,13 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
}
switch (chr) {
- /*$ 0 1 2 3 4 5 6 7 8 9 digit number
- * [n]0|1|2|3|4|5|6|7|8|9 -> n*Radix+X -- Append digit
- *
- * Integer constants in \*(ST may be thought of and are
- * technically sequences of single-digit commands.
- * These commands take one argument from the stack
- * (0 is implied), multiply it with the current radix
- * (2, 8, 10, 16, ...), add the digit's value and
- * return the resultant integer.
- *
- * The command-like semantics of digits may be abused
- * in macros, for instance to append digits to computed
- * integers.
- * It is not an error to append a digit greater than the
- * current radix - this may be changed in the future.
- */
case '0' ... '9':
- if (ctx->flags.modifier_at ||
- (ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) {
+ if (ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon) {
gchar cmd[] = {chr, '\0'};
teco_error_modifier_set(error, cmd);
return NULL;
}
- if (ctx->flags.mode == TECO_MODE_NORMAL)
- teco_expressions_add_digit(chr, ctx->qreg_table_locals->radix);
- return &teco_state_start;
+ return teco_state_number_input(ctx, chr, error);
case '*':
/*
diff --git a/src/core-commands.h b/src/core-commands.h
index 6a31b92..d2200d9 100644
--- a/src/core-commands.h
+++ b/src/core-commands.h
@@ -62,7 +62,6 @@ void teco_undo_change_dir_to_current(void);
extern teco_state_t teco_state_changedir;
extern teco_state_t teco_state_condcommand;
-extern teco_state_t teco_state_control;
extern teco_state_t teco_state_ascii;
extern teco_state_t teco_state_ecommand;
diff --git a/src/expressions.c b/src/expressions.c
index 9561c46..95b1040 100644
--- a/src/expressions.c
+++ b/src/expressions.c
@@ -135,23 +135,6 @@ teco_expressions_pop_num_calc(teco_int_t *ret, teco_int_t imply, GError **error)
}
void
-teco_expressions_add_digit(gunichar digit, teco_qreg_t *qreg)
-{
- /*
- * FIXME: We could just access qreg->integer here since
- * we can assume that "^R" is a plain register.
- */
- assert(qreg != NULL);
- teco_int_t radix = 10;
- qreg->vtable->get_integer(qreg, &radix, NULL);
-
- teco_int_t n = teco_expressions_args() > 0 ? teco_expressions_pop_num(0) : 0;
-
- /* use g_unichar_digit_value()? */
- teco_expressions_push(n*radix + (n < 0 ? -1 : 1)*((gint)digit - '0'));
-}
-
-void
teco_expressions_push_op(teco_operator_t op)
{
g_array_append_val(teco_operators, op);
diff --git a/src/expressions.h b/src/expressions.h
index 3ef0faf..37aef70 100644
--- a/src/expressions.h
+++ b/src/expressions.h
@@ -116,8 +116,6 @@ teco_int_t teco_expressions_peek_num(guint index);
teco_int_t teco_expressions_pop_num(guint index);
gboolean teco_expressions_pop_num_calc(teco_int_t *ret, teco_int_t imply, GError **error);
-void teco_expressions_add_digit(gunichar digit, teco_qreg_t *radix);
-
void teco_expressions_push_op(teco_operator_t op);
gboolean teco_expressions_push_calc(teco_operator_t op, GError **error);
diff --git a/src/lexer.c b/src/lexer.c
index 25ea8f2..2eae8c4 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -29,30 +29,30 @@
#include "core-commands.h"
#include "lexer.h"
+/**
+ * Get style after parsing character.
+ *
+ * By default, this is the parser state's style \b before
+ * inputting the character.
+ */
static teco_style_t
teco_lexer_getstyle(teco_view_t *view, teco_machine_main_t *machine,
gunichar chr)
{
- teco_style_t style = machine->parent.current->style;
-
- /*
- * FIXME: At least this special workaround for numbers might be
- * unnecessary once we get a special parser state for parsing numbers.
- */
- if (machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START &&
- chr <= 0xFF) {
- if (g_ascii_isdigit(chr))
- style = SCE_SCITECO_NUMBER;
- else if (strchr(",+-*/#&()", chr))
- style = SCE_SCITECO_OPERATOR;
- } else if (machine->parent.current == &teco_state_control) {
- /*
- * Two-character operators must always begin with caret
- * They get a separate style, so we can extend it back to
- * the caret in teco_lexter_step.
- */
- if (strchr("*/#", chr))
- style = SCE_SCITECO_OPERATOR2;
+ teco_style_t old_style = machine->parent.current->style;
+
+ if (chr <= 0xFF) {
+ if (machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START &&
+ strchr(",+-*/#&()", chr))
+ old_style = SCE_SCITECO_OPERATOR;
+ else if (machine->parent.current == &teco_state_control &&
+ strchr("*/#", chr))
+ /*
+ * Two-character operators must always begin with caret
+ * They get a separate style, so we can extend it back to
+ * the caret in teco_lexer_step().
+ */
+ old_style = SCE_SCITECO_OPERATOR2;
}
/*
@@ -81,16 +81,23 @@ teco_lexer_getstyle(teco_view_t *view, teco_machine_main_t *machine,
* True comments also begin with `!`, so make sure they are highlighted
* already from the second character.
* This is then extended back by one character in teco_lexer_step().
+ * Numbers must also be highlighted from the first character.
*/
switch (machine->parent.current->style) {
case SCE_SCITECO_COMMENT:
case SCE_SCITECO_LABEL:
+ case SCE_SCITECO_NUMBER:
return machine->parent.current->style;
default:
break;
}
- return style;
+ /*
+ * All non-digits automatically terminate the teco_state_number state,
+ * so always report the new style (so that a command after a number is
+ * highlighted properly).
+ */
+ return old_style == SCE_SCITECO_NUMBER ? SCE_SCITECO_COMMAND : old_style;
}
static void
@@ -113,6 +120,7 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine,
gssize old_pc = machine->macro_pc;
teco_style_t style = SCE_SCITECO_DEFAULT;
+ guint radix = 10; /* if style == SCE_SCITECO_NUMBER */
/*
* g_utf8_get_char_validated() sometimes(?) returns -2 for "\0".
@@ -136,6 +144,7 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine,
(escape_char == '{' ? 1 : 0);
style = teco_lexer_getstyle(view, machine, chr);
+ radix = machine->number.radix;
/*
* Apply folding. This currently folds only {...} string arguments
@@ -171,8 +180,10 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine,
* rewrite the lexer against the ILexer5 interface, which requires C++.
*/
if ((escape_char == '{' || machine->expectstring.machine.escape_char == '{') &&
- teco_view_ssm(view, SCI_GETPROPERTYINT, (uptr_t)"lexer.sciteco.macrodef", TRUE))
+ teco_view_ssm(view, SCI_GETPROPERTYINT, (uptr_t)"lexer.sciteco.macrodef", TRUE)) {
style = teco_lexer_getstyle(view, macrodef_machine, chr);
+ radix = macrodef_machine->number.radix;
+ }
}
*cur_col += machine->macro_pc - old_pc;
@@ -180,9 +191,10 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine,
/*
* True comments begin with `!*` or `!!`, but only the second character gets
* the correct style by default, so we extend it backwards.
- * The same is true for two-letter operators.
+ * The same is true for two-letter operators and hexadecimal constants.
*/
- if (style == SCE_SCITECO_COMMENT || style == SCE_SCITECO_OPERATOR2)
+ if (style == SCE_SCITECO_COMMENT || style == SCE_SCITECO_OPERATOR2 ||
+ (style == SCE_SCITECO_NUMBER && radix == 16))
old_pc--;
teco_view_ssm(view, SCI_STARTSTYLING, start+old_pc, 0);
@@ -196,8 +208,7 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine,
*safe_col = -1; /* no clean state by default */
}
- if (style != SCE_SCITECO_INVALID &&
- machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START &&
+ if (style != SCE_SCITECO_INVALID && machine->parent.current->is_start &&
!machine->flags.modifier_at)
/* clean parser state */
*safe_col = *cur_col;
diff --git a/src/parser.h b/src/parser.h
index 0c389cc..dc43b1c 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -433,6 +433,19 @@ void teco_machine_stringbuilding_escape(teco_machine_stringbuilding_t *ctx, cons
void teco_machine_stringbuilding_clear(teco_machine_stringbuilding_t *ctx);
/**
+ * Persistent state for teco_state_number_input().
+ */
+typedef struct {
+ /**
+ * Syntactially determined radix (0, 8 or 16).
+ * In case of 0, "^R" determines the radix (10 by default).
+ */
+ guint radix;
+ /** accumulator for parsing numbers */
+ teco_int_t value;
+} teco_machine_number_t;
+
+/**
* Peristent state for teco_state_expectstring_input().
*
* This is part of the main machine instead of being a global variable,
@@ -523,6 +536,7 @@ struct teco_machine_main_t {
* but it's hard to prevent memory leaks if putting them into
* a common union.
*/
+ teco_machine_number_t number;
teco_machine_expectstring_t expectstring;
/**
* State machine for parsing Q-reg specifications.
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 587c3a3..897b4f4 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -44,6 +44,13 @@ m4_define([TE_MODIFIER], [m4_format([%c], 7)])
AT_BANNER([Language features])
+AT_SETUP([Number constants])
+TE_CHECK([[23:=]], 0, [23], ignore)
+TE_CHECK([[1 2= =]], 0, ignore, ignore)
+TE_CHECK([[033-27"N(0/0)']], 0, ignore, ignore)
+TE_CHECK([[@0ff-255"N(0/0)']], 0, ignore, ignore)
+AT_CLEANUP
+
AT_SETUP([Number stack])
TE_CHECK([[2%a,%a - 3"N(0/0)' $]], 0, ignore, ignore)
# It's not quite clear what would be the best semantics for comma:
@@ -66,6 +73,8 @@ AT_CLEANUP
AT_SETUP([Radix])
TE_CHECK([[0^R]], 1, ignore, ignore)
TE_CHECK([[0U.^R]], 1, ignore, ignore)
+# non-binary digits:
+TE_CHECK([[2^R 23=]], 1, ignore, ignore)
TE_CHECK([[23 (2^R)\^D .-5"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP