As I had trouble finding it, perhaps others too. Here's the link:
http://www.euroforth.org/ef25/papers/
There is no link from the main page.
dxf <dxforth@gmail.com> writes:
As I had trouble finding it, perhaps others too. Here's the link:
http://www.euroforth.org/ef25/papers/
There is no link from the main page.
Thank you. As it happens, yesterday I created the post-conference proceedings that includes a late paper and the slides that were
provided by their authors (not that many; apparently many authors are
content with the prospect of their presentation being preserved on
video). I have now updated various links for the post-conference
state (link from www.euroforth.org to proceedings, and from the
proceedings to the euro.theforth.net page).
Unfortunately, the videos are not yet available. Gerald Wodni has not
yet had the time to process them. He mentioned something like "after January" or somesuch.
I think that submitting slides has not just the advantage that they
are published earlier in this case (or at all in the 2023 case, where
the audio was so problematic that most videos were not published), but
also that one can read them faster than watch a video; the videos have
the audio track and interactive demos in addition to the text and
graphics of the slides, though.
- anton
On 15-01-2026 13:04, Anton Ertl wrote:...
A few observations concerning the IMHO most interesting paper,
"Code-Copying Compilation in Production":
3. Commercial compilers (partly) using conventional compilers (see TF,
fig. 4.7) - that was new to me;
4. GCC -O1 outperforming GCC -O3 on some benchmarks. That's new to me
too. I might experiment with that one;
5. I added GCC extension support to 4tH in version 3.62.0. At the
time, it improved performance by about 25%. By accident I found out
that was no longer true. switch() based was faster. I didn't know
there had been changes in that regard to GCC.
Hans Bezemer <the.beez.speaks@gmail.com> writes:
5. I added GCC extension support to 4tH in version 3.62.0. At the
time, it improved performance by about 25%. By accident I found out
that was no longer true. switch() based was faster. I didn't know
there had been changes in that regard to GCC.
If you mean the goto *a feature, these days you might try using tail
calls instead. GCC and LLVM both now support a musttail attribute that ensures this optimization, or signals a compile-time error if it can't.
https://lwn.net/Articles/1033373/
The tail call method however, requires an entirely different
VM. That's a lot of work for about 10% performance improvement - that
may not even last for a single GCC update. And requires two VM's to maintain..
Hans Bezemer <the.beez.speaks@gmail.com> writes:
The tail call method however, requires an entirely different
VM. That's a lot of work for about 10% performance improvement - that
may not even last for a single GCC update. And requires two VM's to
maintain..
You'd have to change the VM but on the other hand, it's a documented and supported feature of both GCC and Clang, and other compilers might get
it too. I wouldn't worry about it vanishing with the next GCC update.
Hans Bezemer <the.beez.speaks@gmail.com> writes:
5. I added GCC extension support to 4tH in version 3.62.0. At the
time, it improved performance by about 25%. By accident I found out
that was no longer true. switch() based was faster. I didn't know
there had been changes in that regard to GCC.
If you mean the goto *a feature, these days you might try using tail
calls instead. GCC and LLVM both now support a musttail attribute that >ensures this optimization, or signals a compile-time error if it can't.
https://lwn.net/Articles/1033373/
On 17-01-2026 08:10, Paul Rubin wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
5. I added GCC extension support to 4tH in version 3.62.0. At the
time, it improved performance by about 25%. By accident I found out
that was no longer true. switch() based was faster. I didn't know
there had been changes in that regard to GCC.
The tail call method however, requires an entirely different VM.
Hans Bezemer <the.beez.speaks@gmail.com> writes:
5. I added GCC extension support to 4tH in version 3.62.0. At the
time, it improved performance by about 25%. By accident I found out
that was no longer true. switch() based was faster. I didn't know
there had been changes in that regard to GCC.
If you mean the goto *a feature, these days you might try using tail
calls instead. GCC and LLVM both now support a musttail attribute that ensures this optimization, or signals a compile-time error if it can't.
https://lwn.net/Articles/1033373/
for the tail call version it was changed to
RELOAD() opcode func=(opcode)tbl[*ip++]; __attribute__((musttail))
return func(ip, tbl, TOP, FTOP, sp, rp, fp, lp)
and for the tailcall version
mov rax, qword ptr [r13 + 8*rax]
rex64 jmp rax
It also turns out that the musttail attribute is not necessary
It will generate a tailcall aanyway. The difference is that with
musttail it will report an error if it cannot do the tailcall.
Unfortunately GCC does not recognize preserve_none and uses the stack
for some parameters
// VM8 C variant using tailcalls ...
#define FUNC __attribute__((preserve_none)) void
If you pass an address a as a tail call is it approximately equal
to coroutines:
peter <peter.noreply@tin.it> writes:
for the tail call version it was changed to
RELOAD() opcode func=(opcode)tbl[*ip++]; __attribute__((musttail))
return func(ip, tbl, TOP, FTOP, sp, rp, fp, lp)
You could possibly use "inline RELOAD() { .... ;}" instead of the macro.
and for the tailcall version
mov rax, qword ptr [r13 + 8*rax]
rex64 jmp rax
I wonder why the tailcall version didn't combine the mov with the jmp
like the other version did.
It also turns out that the musttail attribute is not necessary
It will generate a tailcall aanyway. The difference is that with
musttail it will report an error if it cannot do the tailcall.
Yes, TCO has been present since the beginning but it's been
opportunistic rather than something you can rely on.
Unfortunately GCC does not recognize preserve_none and uses the stack
for some parameters
Oh that's interesting. I half remember there being some other feature
for that, but who knows. Does -fwhole-program help?
albert@spenarnc.xs4all.nl writes:
If you pass an address a as a tail call is it approximately equal
to coroutines:
No I don't think so. The tail call is just a jump to that address
(changes the program counter). A coroutine jump also has to change the
stack pointer. See the section "Knuth's coroutines" here:
https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
Some Forths have a CO primitive that I think is similar. There is
something like it on the Greenarrays processor.
On Fri, 16 Jan 2026 23:10:24 -0800
Paul Rubin <no.email@nospam.invalid> wrote:
The VM was written from the begining in X64 assembler (13 years ago)
4 years ago I also implemented the VM i C to simplify porting to ARM64.
At that time the asm version was about 10% faster then the generated
C code, today the speed is about the same. C compilers have improved.
Much more important is the __attribute__((preserve_none)) before
each function. This indicated that more registers will be used to pass >parameters. As seen above I pass 8 parameters to each function and
they need to be in registers to match the asmbler written code.
This is done automatically in the goto version as everything is in
one function there.
In the end it is more how you like to write your VM, as one function
or one for each "opcode".
Unfortunately GCC does not recognize preserve_none and uses the stack
for some parameters
peter <peter.noreply@tin.it> writes:
and for the tailcall version
mov rax, qword ptr [r13 + 8*rax]
rex64 jmp rax
I wonder why the tailcall version didn't combine the mov with the jmp
like the other version did.
Yes, TCO has been present since the beginning but it's been
opportunistic rather than something you can rely on.
Unfortunately GCC does not recognize preserve_none and uses the stack
for some parameters
Oh that's interesting. I half remember there being some other feature
for that, but who knows.
Does -fwhole-program help?
Hans Bezemer <the.beez.speaks@gmail.com> writes:
On 15-01-2026 13:04, Anton Ertl wrote:...
A few observations concerning the IMHO most interesting paper,
"Code-Copying Compilation in Production":
3. Commercial compilers (partly) using conventional compilers (see TF,
fig. 4.7) - that was new to me;
All Forth compilers I know work at the text interpretation level as
the "Forth compiler" of Thinking Forth, Figure 4.7.
4. GCC -O1 outperforming GCC -O3 on some benchmarks. That's new to me
too. I might experiment with that one;
I have analyzed it for bubblesort. There the problem is that gcc -O3 auto-vectorizes the pair of loads and the pair of stores (when the two elements are swapped). As a result, if a pair is stored in one
iteration, the next iteration loads a pair that overlaps the
previously stored pair. This means that the hardware cannot use its
fast path in store-to-load forwarding, and leads to a huge slowdown.
For a benchmark that has been around for over 40 years.
In addition, the code generated by gcc -O3 also executes several
additonal instructions per iteration, so I doubt that it would be
faster even if the store-to-load forwarding problem did not exist.
For fib, I have also looked at the generated code, but have not
understood it well enough to see why the code generated by gcc -O3 is
slower.
- anton
I've done my thing, compiled 4tH with optimizations -O3 till -O0.
I thought, let's make this simple and execute ALL benchmarks I got. Some
of them have become useless, though for the simple reason hardware has >become that much better.
But still, here it is. Overall, the performance consistently
deteriorates, aka -O3 gives the best performance.
Hans Bezemer <the.beez.speaks@gmail.com> writes:
I've done my thing, compiled 4tH with optimizations -O3 till -O0.
I thought, let's make this simple and execute ALL benchmarks I got. Some >>of them have become useless, though for the simple reason hardware has >>become that much better.
But still, here it is. Overall, the performance consistently
deteriorates, aka -O3 gives the best performance.
Which compiler and which hardware?
For a random program, I would expect higher optimization levels to
produe faster code. For a Forth system and these recent gccs, the >auto-vectorization of adjacent memory accesses may lead to similar
problems as in the C bubble-sort benchmark. In Gforth, this actually
happens unless we disable vectorization (which we normally do), and, >moreover, with the vectorized code, gcc introduces additional
inefficiencies (see below).
Here's the output of ./gforth-fast onebench.fs compiled from the
current development version with gcc-12.2 and running on a Ryzen 5800X >(numbers are times, lower is better):
sieve bubble matrix fib fft gcc options
0.025 0.023 0.013 0.033 0.016 -O2
0.025 0.023 0.013 0.037 0.016 -O3 -fno-tree-vectorize (gforth default) 0.404 0.418 0.377 0.472 0.244 -O3 (with auto vectorization)
0.145 0.122 0.124 0.122 0.073 gforth default, using --no-dynamic
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
I've done my thing, compiled 4tH with optimizations -O3 till -O0.
I thought, let's make this simple and execute ALL benchmarks I got. Some >>of them have become useless, though for the simple reason hardware has >>become that much better.
But still, here it is. Overall, the performance consistently >>deteriorates, aka -O3 gives the best performance.
Which compiler and which hardware?
For a random program, I would expect higher optimization levels to
produe faster code. For a Forth system and these recent gccs, the >auto-vectorization of adjacent memory accesses may lead to similar
problems as in the C bubble-sort benchmark. In Gforth, this actually >happens unless we disable vectorization (which we normally do), and, >moreover, with the vectorized code, gcc introduces additional >inefficiencies (see below).
Here's the output of ./gforth-fast onebench.fs compiled from the
current development version with gcc-12.2 and running on a Ryzen 5800X >(numbers are times, lower is better):
sieve bubble matrix fib fft gcc options
0.025 0.023 0.013 0.033 0.016 -O2
0.025 0.023 0.013 0.037 0.016 -O3 -fno-tree-vectorize (gforth default) 0.404 0.418 0.377 0.472 0.244 -O3 (with auto vectorization)
0.145 0.122 0.124 0.122 0.073 gforth default, using --no-dynamic
I have now also tried it with gcc-14.2, and that produces better code. Results from a Xeon E-2388G (Rocket Lake):
sieve bubble matrix fib fft gcc options
0.032 0.032 0.015 0.037 0.014 -O2
0.035 0.032 0.015 0.037 0.014 -O3 -fno-tree-vectorize (gforth default)
0.033 0.034 0.016 0.032 0.014 -O3 (with auto vectorization)
The code for ROT and 2SWAP does not use auto-vectorization, and the
code for 2! uses auto-vectorization in a way that reduces the
instruction count:
-O3 (auto-vectorized) -O3 -fno-tree-vectorize
add $0x8,%rbx add $0x8,%rbx
movq 0x8(%r13),%xmm0 mov 0x10(%r13),%rax
add $0x18,%r13 mov 0x8(%r13),%rdx
movhps -0x8(%r13),%xmm0 add $0x18,%r13
movups %xmm0,(%r8) mov %rdx,(%r8)
mov 0x0(%r13),%r8 mov %rax,0x8(%r8)
mov (%rbx),%rax mov 0x0(%r13),%r8
jmp *%rax mov (%rbx),%rax
jmp *%rax
And the common tail with all these move instructions is gone.
- anton
On Sat, 24 Jan 2026 16:47:16 GMT
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
I have now also tried it with gcc-14.2, and that produces better code.
Results from a Xeon E-2388G (Rocket Lake):
sieve bubble matrix fib fft gcc options
0.032 0.032 0.015 0.037 0.014 -O2
0.035 0.032 0.015 0.037 0.014 -O3 -fno-tree-vectorize (gforth default)
0.033 0.034 0.016 0.032 0.014 -O3 (with auto vectorization)
The code for ROT and 2SWAP does not use auto-vectorization, and the
code for 2! uses auto-vectorization in a way that reduces the
instruction count:
-O3 (auto-vectorized) -O3 -fno-tree-vectorize
add $0x8,%rbx add $0x8,%rbx
movq 0x8(%r13),%xmm0 mov 0x10(%r13),%rax
add $0x18,%r13 mov 0x8(%r13),%rdx
movhps -0x8(%r13),%xmm0 add $0x18,%r13
movups %xmm0,(%r8) mov %rdx,(%r8)
mov 0x0(%r13),%r8 mov %rax,0x8(%r8)
mov (%rbx),%rax mov 0x0(%r13),%r8
jmp *%rax mov (%rbx),%rax
jmp *%rax
And the common tail with all these move instructions is gone.
- anton
What does your C code looks like? I could not get clang or gcc to auto vectories
with my existing code
UNS64 *tmp64 = (UNS64*)TOP;
tmp64[0] = sp[0];
tmp64[1] = sp[1];
TOP = sp[2];
sp += 3;
typedef UNS64 v2u64 __attribute__((vector_size(16))) __attribute__((aligned(8)));
peter <peter.noreply@tin.it> writes:
On Sat, 24 Jan 2026 16:47:16 GMT
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
I have now also tried it with gcc-14.2, and that produces better code.
Results from a Xeon E-2388G (Rocket Lake):
sieve bubble matrix fib fft gcc options
0.032 0.032 0.015 0.037 0.014 -O2
0.035 0.032 0.015 0.037 0.014 -O3 -fno-tree-vectorize (gforth default) >> 0.033 0.034 0.016 0.032 0.014 -O3 (with auto vectorization)
The code for ROT and 2SWAP does not use auto-vectorization, and the
code for 2! uses auto-vectorization in a way that reduces the
instruction count:
-O3 (auto-vectorized) -O3 -fno-tree-vectorize
add $0x8,%rbx add $0x8,%rbx
movq 0x8(%r13),%xmm0 mov 0x10(%r13),%rax
add $0x18,%r13 mov 0x8(%r13),%rdx
movhps -0x8(%r13),%xmm0 add $0x18,%r13
movups %xmm0,(%r8) mov %rdx,(%r8)
mov 0x0(%r13),%r8 mov %rax,0x8(%r8)
mov (%rbx),%rax mov 0x0(%r13),%r8
jmp *%rax mov (%rbx),%rax
jmp *%rax
And the common tail with all these move instructions is gone.
- anton
What does your C code looks like? I could not get clang or gcc to auto vectories
with my existing code
UNS64 *tmp64 = (UNS64*)TOP;
tmp64[0] = sp[0];
tmp64[1] = sp[1];
TOP = sp[2];
sp += 3;
Gforth's source code for 2! is:
2! ( w1 w2 a_addr -- ) core two_store
""Store @i{w2} into the cell at @i{c-addr} and @i{w1} into the next cell."" a_addr[0] = w2;
a_addr[1] = w1;
A generator produces the following from that, which is passed to gcc:
LABEL(two_store) /* 2! ( w1 w2 a_addr -- ) S1 -- S1 */
/* Store @i{w2} into the cell at @i{c-addr} and @i{w1} into the next cell. */ NAME("2!")
ip += 1;
LABEL1(two_store)
{
DEF_CA
MAYBE_UNUSED Cell w1;
MAYBE_UNUSED Cell w2;
MAYBE_UNUSED Cell * a_addr;
NEXT_P0;
vm_Cell2w(sp[2],w1);
vm_Cell2w(sp[1],w2);
vm_Cell2a_(spTOS,a_addr);
#ifdef VM_DEBUG
if (vm_debug) {
fputs(" w1=", vm_out); printarg_w(w1);
fputs(" w2=", vm_out); printarg_w(w2);
fputs(" a_addr=", vm_out); printarg_a_(a_addr);
}
#endif
sp += 3;
{
#line 1815 "prim"
a_addr[0] = w2;
a_addr[1] = w1;
#line 10136 "prim-fast.i"
}
#ifdef VM_DEBUG
if (vm_debug) {
fputs(" -- ", vm_out); fputc('\n', vm_out);
}
#endif
NEXT_P1;
spTOS = sp[0];
LABEL2(two_store)
NAME1("l2-two_store")
NEXT_P1_5;
LABEL3(two_store)
NAME1("l3-two_store")
DO_GOTO;
}
There are a lot of macros in this code, and I fear that expanding them
makes the code even less readable, but the essence for the
auto-vectorized part is something like:
w1 = sp[2];
w2 = sp[1];
a_addr = spTOS;
sp += 3;
a_addr[0] = w2;
a_addr[1] = w1;
spTOS = sp[0];
My guess is that in your code the compiler expected that sp[1] might
alias with tmp64[0], and therefore did not vectorize the loads and the stores, whereas in the Gforth code, the loads both happen first, and
then the two stores, and gcc can vectorize that. I doubt that there
is a big benefit from that, though.
typedef UNS64 v2u64 __attribute__((vector_size(16))) __attribute__((aligned(8)));
I'll have to remember the aligned attribute for future games with gcc explicit vectorization.
- anton
On Mon, 26 Jan 2026 19:24:43 GMT...
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
peter <peter.noreply@tin.it> writes:
On Sat, 24 Jan 2026 16:47:16 GMT
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
The code for ROT and 2SWAP does not use auto-vectorization, and the
code for 2! uses auto-vectorization in a way that reduces the
instruction count:
-O3 (auto-vectorized) -O3 -fno-tree-vectorize
add $0x8,%rbx add $0x8,%rbx
movq 0x8(%r13),%xmm0 mov 0x10(%r13),%rax
add $0x18,%r13 mov 0x8(%r13),%rdx
movhps -0x8(%r13),%xmm0 add $0x18,%r13
movups %xmm0,(%r8) mov %rdx,(%r8)
mov 0x0(%r13),%r8 mov %rax,0x8(%r8)
mov (%rbx),%rax mov 0x0(%r13),%r8
jmp *%rax mov (%rbx),%rax
jmp *%rax
UNS64 *tmp64 = (UNS64*)TOP;
UNS64 d0=sp[0];
UNS64 d1=sp[1];
tmp64[0] = d0;
tmp64[1] = d1;
TOP = sp[2];
sp += 3;
made the compiler (clang-21 in this case) generate the expected code
typedef UNS64 v2u64 __attribute__((vector_size(16))) __attribute__((aligned(8)));
I'll have to remember the aligned attribute for future games with gcc
explicit vectorization.
Without that it will generate the opcodes that needs 16 byte alignment
peter <peter.noreply@tin.it> writes:
On Mon, 26 Jan 2026 19:24:43 GMT...
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
peter <peter.noreply@tin.it> writes:
On Sat, 24 Jan 2026 16:47:16 GMT
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
The code for ROT and 2SWAP does not use auto-vectorization, and the
code for 2! uses auto-vectorization in a way that reduces the
instruction count:
-O3 (auto-vectorized) -O3 -fno-tree-vectorize
add $0x8,%rbx add $0x8,%rbx
movq 0x8(%r13),%xmm0 mov 0x10(%r13),%rax
add $0x18,%r13 mov 0x8(%r13),%rdx
movhps -0x8(%r13),%xmm0 add $0x18,%r13
movups %xmm0,(%r8) mov %rdx,(%r8)
mov 0x0(%r13),%r8 mov %rax,0x8(%r8)
mov (%rbx),%rax mov 0x0(%r13),%r8
jmp *%rax mov (%rbx),%rax
jmp *%rax
UNS64 *tmp64 = (UNS64*)TOP;
UNS64 d0=sp[0];
UNS64 d1=sp[1];
tmp64[0] = d0;
tmp64[1] = d1;
TOP = sp[2];
sp += 3;
made the compiler (clang-21 in this case) generate the expected code
The auto-vectorized implementation of 2! above should perform ok,
because it loads each stack item separately, and the wide movups is
only used for the stores. If there is a wide load from the stack
involved, I expect a significant slowdown, because the stack items
usually have been stored recently, and narrow-store-to-wide-load
forwarding is a slow path on recent (and presumably also older) CPU
cores: https://www.complang.tuwien.ac.at/anton/stwlf/
typedef UNS64 v2u64 __attribute__((vector_size(16))) __attribute__((aligned(8)));
I'll have to remember the aligned attribute for future games with gcc
explicit vectorization.
Without that it will generate the opcodes that needs 16 byte alignment
Yes. Until now I worked around that by using memcpy to a vector
variable, but this approach is much more convenient.
- anton--
I always wonder, is this relevant to the industrial applications of
gforth or gforth based programs that are sold commercially?
albert@spenarnc.xs4all.nl writes:
If you pass an address a as a tail call is it approximately equal
to coroutines:
No I don't think so. The tail call is just a jump to that address
(changes the program counter). A coroutine jump also has to change the
stack pointer. See the section "Knuth's coroutines" here:
https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
Some Forths have a CO primitive that I think is similar. There is
something like it on the Greenarrays processor.
Never having looked at coroutines before I tried implementing the
examples in the above paper. The resulting code is: ...
https://doi.org/10.1145/1462166.1462167
albert@spenarnc.xs4all.nl writes:
If you pass an address a as a tail call is it approximately equal
to coroutines:
No I don't think so. The tail call is just a jump to that address
(changes the program counter). A coroutine jump also has to change the stack pointer. See the section "Knuth's coroutines" here:
https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
Some Forths have a CO primitive that I think is similar. There is
something like it on the Greenarrays processor.
Gerry Jackson <do-not-use@swldwa.uk> writes:
Never having looked at coroutines before I tried implementing the
examples in the above paper. The resulting code is: ...
It would take me a while to understand that, but [:] is cool (haven't
seen it before),
and it lets you do something similar to protothreads.
Paul Rubin <no.email@nospam.invalid> writes:
For coroutines in general you should also read this:
https://doi.org/10.1145/1462166.1462167
It discusses "stackful" coroutines where each coroutine has a separate
call stack that is preserved across coroutine jumps. It's similar to
Forth's cooperative multitasking.
https://doi.org/10.1145/1462166.1462167
Ehh, that article is very theoretical. I had forgotten what it was
like, or maybe confused it with a different article. The below is
probably more readable:
https://www.lua.org/doc/jucs04.pdf
I've never heard of protothreads let alone used them. Apparently they
Thanks for the link, very interesting. According to that paper the
Forth implementation in my first post is an asymmetric coroutine as
are Lua coroutines which they justify in an appendix,
For the first point, in my example data local to the parser is
retained but in global variables. That can be made local by putting
them in a separate wordlist. Using Forth local variables doesn't make
sense when [:] is used. If it did make sense then local variable
values wouldn't be retained.
The re-entry point has to be called by name. I would think that makes
it more readable.
6. Nested colon definitions e.g. a possible use - unsure of its utility.
: a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
b 2 3
b is effectively a nested colon definition - forbidden by the standard
Gerry
Gerry Jackson <do-not-use@swldwa.uk> writes:
I've never heard of protothreads let alone used them. Apparently they
They are basically a fancier version of Simon Tatham's preprocessor
scheme. In fact I confused the two. See:
https://dunkels.com/adam/pt/index.html
Thanks for the link, very interesting. According to that paper the
Forth implementation in my first post is an asymmetric coroutine as
are Lua coroutines which they justify in an appendix,
I think there's a big difference, which is that in Lua, you can call something as a coroutine, and it can yield from several levels deep in function calls and later resume where it left off.
couldn't do that, but it gained the ability later. It's how
asynchronous i/o works in Python. It also lets you implement
multitasking, by having the tasks avoid any blocking operations and
yield frequently to not hog the cpu.
Forth also often implements something like that, with a cooperative multitasker API where you PAUSE every so often, and communicate through mailboxes rather than with return values. It's equivalent though.
The re-entry point has to be called by name. I would think that makes
it more readable.
Does that mean the caller has to know where the coroutine returns from? That's more bookkeeping.
In article <10qunhm$1nnbt$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
<SNIP>
6. Nested colon definitions e.g. a possible use - unsure of its utility.
: a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
b 2 3
b is effectively a nested colon definition - forbidden by the standard
The term forbidden sneaks in probably by implementors that have
absolutely no intention to implement this.
Normal it would be phrased as,
"A standard Forth is not required to accommodate nested definitions.
A program relying on such has an environmental dependancy"
Note that [: .. ;] is in fact nesting definitions. 1]
In ciforth you can load an extension { } [ ]
Your example
: a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
then becomes
: a 1 . [ : b 2 . 3 . ; ] b drop 4 . ;
1]--
Definition or word, or dictionary entry.
Definition: a dictionary entry is a logical part of the dictionary that bundles the properties of a word/definition.
Definition: It is identified by a handle, the dea (dictionary entry address) CDFLN SX
It has a code address: for executing the word, native code.
It has a data address: contains a pointer to data (buffer, ITC, DTC,)
It has a flag address: individual bits identify immediate smudged etc.
It has a link address: that defines a relation its wordlist
It has a name address: contains a (pointer to) the name
Extra fields can be added by an implementation.
Example a source address: points to the source of a word^H^H^H^H d.e..
Each and all of this fields can be modified without disturbing
the identity of a dea, e.g.
"drop" ALLOCATE$ ' DROP >NFA !
It makes no sense to have a word without a name, old view.
OTOH a d.e. where the name address contains a null pointer
makes perfect sense, new view.
Using dea as an xt :
EXECUTE ( dea -- ?? ) jump to the address found via the cfa.
This can be accomplished while the d.e. is not (yet) linked
into a wordlist, or floating in ALLOCATEd space, or temporary
in a spare dictionary area that is about to be destroyed,
or even if the "word" has no name.
Groetjes Albert
I think there's a big difference, which is that in Lua, you can call >something as a coroutine, and it can yield from several levels deep in >function calls and later resume where it left off. Python originally >couldn't do that, but it gained the ability later. It's how
asynchronous i/o works in Python. It also lets you implement
multitasking, by having the tasks avoid any blocking operations and
yield frequently to not hog the cpu.
On 06/04/2026 12:51, albert@spenarnc.xs4all.nl wrote:
In article <10qunhm$1nnbt$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
<SNIP>
6. Nested colon definitions e.g. a possible use - unsure of its utility. >>> : a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;The term forbidden sneaks in probably by implementors that have
b 2 3
b is effectively a nested colon definition - forbidden by the standard >>
absolutely no intention to implement this.
Hmm I just scanned the Forth 2012 document to see if nested definition
were ambiguous or had an environmental dependency and I couldn't find >anything. I always assumed it wasn't permited - perhaps it is.
--
Gerry
On 06/04/2026 12:51, albert@spenarnc.xs4all.nl wrote:
In article <10qunhm$1nnbt$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
<SNIP>
6. Nested colon definitions e.g. a possible use - unsure of its utility. >>> : a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
b 2 3
b is effectively a nested colon definition - forbidden by the
standard
The term forbidden sneaks in probably by implementors that have
absolutely no intention to implement this.
Hmm I just scanned the Forth 2012 document to see if nested definition
were ambiguous or had an environmental dependency and I couldn't find anything. I always assumed it wasn't permited - perhaps it is.
It is in section 3.4.5, in particular the 2nd paragraph:
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program >shall not execute any defining word, :NONAME, or any definition that >allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >certain architectures.
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Krishna Myneni <krishna.myneni@ccreweb.org> writes:...
It is in section 3.4.5, in particular the 2nd paragraph:
3.4.5
Compilation
While the compilation of the current definition is suspended, a program
shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for
certain architectures.
I think it was existing practice: Most systems did not support nested definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
On 4/6/26 4:20 PM, Gerry Jackson wrote:
On 06/04/2026 12:51, albert@spenarnc.xs4all.nl wrote:
In article <10qunhm$1nnbt$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
<SNIP>
6. Nested colon definitions e.g. a possible use - unsure of its
utility.
: a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
b 2 3
b is effectively a nested colon definition - forbidden by the
standard
The term forbidden sneaks in probably by implementors that have
absolutely no intention to implement this.
Hmm I just scanned the Forth 2012 document to see if nested definition
were ambiguous or had an environmental dependency and I couldn't find
anything. I always assumed it wasn't permited - perhaps it is.
It is in section 3.4.5, in particular the 2nd paragraph:
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program shall not execute any defining word, :NONAME, or any definition that allocates dictionary data space.
On 07/04/2026 12:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
On the contrary, Ruvim posted some code that is a standard program and
which distinguises between a parsing TO and one that sets a flag for a >following VALUE to act on.
I can't find the post but the gist of it was (I think):
1 value v1
2 value v2 immediate
: test to v2 v1 ;
Running test with
3 test
A parsing TO will set v2 to 3
A flagging TO will execute v2 during compilation of test because it is >immediate. So test will set v1 to 3 leaving v2 unchanged.
--
Gerry
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested definitions when Forth-94 was standardized, and I guess that most
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program
shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for
certain architectures.
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
On 07/04/2026 15:28, Krishna Myneni wrote:
On 4/6/26 4:20 PM, Gerry Jackson wrote:
On 06/04/2026 12:51, albert@spenarnc.xs4all.nl wrote:
In article <10qunhm$1nnbt$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
<SNIP>
6. Nested colon definitions e.g. a possible use - unsure of its
utility.
: a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
b 2 3
b is effectively a nested colon definition - forbidden by the >>>>> standard
The term forbidden sneaks in probably by implementors that have
absolutely no intention to implement this.
Hmm I just scanned the Forth 2012 document to see if nested definition
were ambiguous or had an environmental dependency and I couldn't find
anything. I always assumed it wasn't permited - perhaps it is.
It is in section 3.4.5, in particular the 2nd paragraph:
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program
shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
Thanks I missed that. Those statements would seem to rule out quotations >except that it could be argued that the definition of [: and ;] does not >suspend compilation - its part of the enclosing colon definition.
--
Gerry
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program
shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for
certain architectures.
definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
--
Gerry
In article <10r3nfo$33464$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 12:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
On the contrary, Ruvim posted some code that is a standard program and
which distinguises between a parsing TO and one that sets a flag for a
following VALUE to act on.
I can't find the post but the gist of it was (I think):
1 value v1
2 value v2 immediate
: test to v2 v1 ;
Running test with
3 test
A parsing TO will set v2 to 3
A flagging TO will execute v2 during compilation of test because it is
immediate. So test will set v1 to 3 leaving v2 unchanged.
No it doesn't. It leaves garbage on the stack during compilation,
leading to mostly an error.
I leave it up to the reader whether this counts as a standard program.
I overlooked this clever example.As did everybody else until Ruvim devised it.
So I guess my VALUE is non compliant, proven by a contrived test.
It still makes no sense to forbid a flagging implementation.
(Also VALUE's don't make sense, anyway.)
Krishna Myneni <krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program
shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for
certain architectures.
I think it was existing practice: Most systems did not support nested definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
- anton
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During >>>> the compilation of the current definition, a program shall not execute >>>> any defining word, :NONAME, or any definition that allocates dictionary >>>> data space. The compilation of the current definition may be suspended >>>> using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program >>>> shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >>>> certain architectures.
definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During >>>> the compilation of the current definition, a program shall not execute >>>> any defining word, :NONAME, or any definition that allocates dictionary >>>> data space. The compilation of the current definition may be suspended >>>> using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program >>>> shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >>>> certain architectures.
definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
( This is the infamous Pythogarean triangle. )--
On 4/8/26 6:16 AM, albert@spenarnc.xs4all.nl wrote:
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During >>>>> the compilation of the current definition, a program shall not execute >>>>> any defining word, :NONAME, or any definition that allocates
dictionary
data space. The compilation of the current definition may be suspended >>>>> using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a
program
shall not execute any defining word, :NONAME, or any definition that >>>>> allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >>>>> certain architectures.
definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
I assume you can also compile :NONAME definitions while the current definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ;
ok
3 4 hyp^2 .
25 ok
Incidentally, is the scope of your definition of SQ limited to the definition of hyp*2 in your example above? Or is SQ added to the
dictionary like a regular definition?
In article <10r5atl$3g8d5$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 15:28, Krishna Myneni wrote:
On 4/6/26 4:20 PM, Gerry Jackson wrote:
On 06/04/2026 12:51, albert@spenarnc.xs4all.nl wrote:
In article <10qunhm$1nnbt$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
<SNIP>
6. Nested colon definitions e.g. a possible use - unsure of its
utility.
: a 1 . [: [:] b 2 . 3 . ;] drop 4 . ;
b 2 3
b is effectively a nested colon definition - forbidden by the >>>>>> standard
The term forbidden sneaks in probably by implementors that have
absolutely no intention to implement this.
Hmm I just scanned the Forth 2012 document to see if nested definition >>>> were ambiguous or had an environmental dependency and I couldn't find
anything. I always assumed it wasn't permited - perhaps it is.
It is in section 3.4.5, in particular the 2nd paragraph:
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During
the compilation of the current definition, a program shall not execute
any defining word, :NONAME, or any definition that allocates dictionary
data space. The compilation of the current definition may be suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program
shall not execute any defining word, :NONAME, or any definition that
allocates dictionary data space.
Thanks I missed that. Those statements would seem to rule out quotations
except that it could be argued that the definition of [: and ;] does not
suspend compilation - its part of the enclosing colon definition.
"part of the" a really Jezuitic argument.
It also forbids
: there 3 ;
: test .. [ ' three COMPILE, ] ... ;
a technique that should be kosher.
It is more proof that the term "forbid" has no place in the wording of the restriction. An implementor of [: has options he sees fit, includingMy system has unconstrained nesting but it doesn't need any change to [
- skipping over an area, that is unused by the current definition,
- temperarily compiling to an alternative HERE, transporting back later
to the regular HERE. ( FAR-DP SWAP-DP TRIM approach use for my OO).
After the current definition finishes copy bake to a new HERE.
- using ALLOCATEd space, or
- invoking a c-compiler.
[: ;] renamed { } to ( include :NONAME ) is readily implemented
by the four nestings of the apocalypse:
: { (( (s ({) ; IMMEDIATE
: } >R (}) s) )) R> 'LITERAL EXECUTE ; IMMEDIATE
With a refurbished [ ] it implements unconstrained nesting :
: doit .. [ .. { ..} .. { .. [ .. ] .. } .. ] .. ;
(The four brackets comprise one screen. Only words
from the ciforth kernel needed. So in a proper architecture
it is straightforward. )
On 07/04/2026 12:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
On the contrary, Ruvim posted some code that is a standard program and
which distinguises between a parsing TO and one that sets a flag for a following VALUE to act on.
On 4/8/26 6:16 AM, albert@spenarnc.xs4all.nl wrote:
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During >>>>> the compilation of the current definition, a program shall not execute >>>>> any defining word, :NONAME, or any definition that allocates dictionary >>>>> data space. The compilation of the current definition may be suspended >>>>> using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program >>>>> shall not execute any defining word, :NONAME, or any definition that >>>>> allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >>>>> certain architectures.
definitions when Forth-94 was standardized, and I guess that most
systems do not support general nesting to this day, and when they do,
the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
I assume you can also compile :NONAME definitions while the current >definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ;
ok
3 4 hyp^2 .
25 ok
Incidentally, is the scope of your definition of SQ limited to the
definition of hyp*2 in your example above? Or is SQ added to the
dictionary like a regular definition?
----
Krishna
In article <10r60qk$3nqft$1@dont-email.me>,
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
On 4/8/26 6:16 AM, albert@spenarnc.xs4all.nl wrote:Actually you can't. After -nesting- the brackets use the data stack extensively.
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested >>>>> definitions when Forth-94 was standardized, and I guess that most
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During >>>>>> the compilation of the current definition, a program shall not execute >>>>>> any defining word, :NONAME, or any definition that allocates dictionary >>>>>> data space. The compilation of the current definition may be suspended >>>>>> using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program >>>>>> shall not execute any defining word, :NONAME, or any definition that >>>>>> allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >>>>>> certain architectures.
systems do not support general nesting to this day, and when they do, >>>>> the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
I assume you can also compile :NONAME definitions while the current
definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ;
ok
3 4 hyp^2 .
25 ok
You enter an isolated world between [ and ] .
Your example could become
"
WANT -nesting- CASE-INSENSITIVE :NONAME
: hyp^2 [ :NONAME dup * ; CONSTANT NONAME ] NONAME dup >r execute swap r> execute + ;
"
On 08/04/2026 17:47, Krishna Myneni wrote:
On 4/8/26 6:16 AM, albert@spenarnc.xs4all.nl wrote:
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested >>>>> definitions when Forth-94 was standardized, and I guess that most
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions.
During
the compilation of the current definition, a program shall not
execute
any defining word, :NONAME, or any definition that allocates
dictionary
data space. The compilation of the current definition may be
suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a
program
shall not execute any defining word, :NONAME, or any definition that >>>>>> allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do >>>>>> for
certain architectures.
systems do not support general nesting to this day, and when they do, >>>>> the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
I assume you can also compile :NONAME definitions while the current
definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ;
ok
3 4 hyp^2 .
25 ok
Works on my system. Both HYP^2 and SQ are added to the current wordlist.
...
On 4/9/26 6:01 AM, albert@spenarnc.xs4all.nl wrote:No, but you don't use nesting normally.
In article <10r60qk$3nqft$1@dont-email.me>,
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
On 4/8/26 6:16 AM, albert@spenarnc.xs4all.nl wrote:Actually you can't. After -nesting- the brackets use the data stack extensively.
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested >>>>>> definitions when Forth-94 was standardized, and I guess that most
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions. During >>>>>>> the compilation of the current definition, a program shall not execute >>>>>>> any defining word, :NONAME, or any definition that allocates dictionary >>>>>>> data space. The compilation of the current definition may be suspended >>>>>>> using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a program >>>>>>> shall not execute any defining word, :NONAME, or any definition that >>>>>>> allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do for >>>>>>> certain architectures.
systems do not support general nesting to this day, and when they do, >>>>>> the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
I assume you can also compile :NONAME definitions while the current
definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ;
ok
3 4 hyp^2 .
25 ok
You enter an isolated world between [ and ] .
Your example could become
"
WANT -nesting- CASE-INSENSITIVE :NONAME
: hyp^2 [ :NONAME dup * ; CONSTANT NONAME ] NONAME dup >r execute swap r> execute + ;
"
So you can never use "[ ... ] LITERAL" when nesting is in effect in
ciforth?
--
Krishna
On 4/8/26 3:48 PM, Gerry Jackson wrote:
On 08/04/2026 17:47, Krishna Myneni wrote:
On 4/8/26 6:16 AM, albert@spenarnc.xs4all.nl wrote:
In article <10r5bgp$3gdvd$1@dont-email.me>,
Gerry Jackson <do-not-use@swldwa.uk> wrote:
On 07/04/2026 17:12, Anton Ertl wrote:
Krishna Myneni<krishna.myneni@ccreweb.org> writes:
It is in section 3.4.5, in particular the 2nd paragraph:I think it was existing practice: Most systems did not support nested >>>>>> definitions when Forth-94 was standardized, and I guess that most
3.4.5
Compilation
A program shall not attempt to nest compilation of definitions.
During
the compilation of the current definition, a program shall not
execute
any defining word, :NONAME, or any definition that allocates
dictionary
data space. The compilation of the current definition may be
suspended
using [ (left-bracket) and resumed using ] (right-bracket).
While the compilation of the current definition is suspended, a
program
shall not execute any defining word, :NONAME, or any definition that >>>>>>> allocates dictionary data space.
It seems entirely frivolous to me, but maybe it is difficult to do >>>>>>> for
certain architectures.
systems do not support general nesting to this day, and when they do, >>>>>> the syntax is system-dependent.
I agree except that a few systems I tried compiled existing syntax
e.g.
: foo ... [ : bar ... ; ] ... ;
but the results didn't work
You didn't try ciforth.
~/PROJECT/ciforths/ciforth: lina -a
WANT -nesting-
[ : ISN'T UNIQUE
] : ISN'T UNIQUE
-nesting- : (WARNING) NOT PRESENT, THOUGH WANTED
OK
: hyp*2 [ : SQ DUP * ; ] SQ SWAP SQ + ;
OK
3 4 hyp*2 .
25 OK
...
I assume you can also compile :NONAME definitions while the current
definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ;
ok
3 4 hyp^2 .
25 ok
Works on my system. Both HYP^2 and SQ are added to the current wordlist.
...
Thanks, Gerry. I was going to suggest that maybe we can propose to get
the reference to :NONAME removed from the Forth 20xx standard statement
in 3.4.5 Compilation,
"While the compilation of the current definition is suspended, a
program shall not execute any defining word, :NONAME, or any definition
that allocates dictionary data space."
There may not be enough common practice, though.
--
Krishna
In article <10r84e9$9tt0$1@dont-email.me>,...
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
No, but you don't use nesting normally.Actually you can't. After -nesting- the brackets use the data stack extensively.
I assume you can also compile :NONAME definitions while the current
definition is suspended in ciforth e.g.
: hyp^2 [ :NONAME dup * ; ] literal dup >r execute swap r> execute + ; >>>> ok
3 4 hyp^2 .
25 ok
You enter an isolated world between [ and ] .
Your example could become
"
WANT -nesting- CASE-INSENSITIVE :NONAME
: hyp^2 [ :NONAME dup * ; CONSTANT NONAME ] NONAME dup >r execute swap r> execute + ;
"
So you can never use "[ ... ] LITERAL" when nesting is in effect in
ciforth?
I use extra facilities parsimonily.
I never do those LITERAL trick anyway.
In article <10r84nr$9tt0$2@dont-email.me>,....
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
"While the compilation of the current definition is suspended, a
program shall not execute any defining word, :NONAME, or any definition
that allocates dictionary data space."
There may not be enough common practice, though.
I propose a different view. A denotation can occupy (dictionary) space, e.g. a string.
"AAPESTRING" reserves space, and leaves a pointer to the data structure.
Like wise
{ DUP * } defines a behaviour and leaves a pointer. There is actually
not much difference.
On 4/9/26 9:41 AM, albert@spenarnc.xs4all.nl wrote:
In article <10r84nr$9tt0$2@dont-email.me>,....
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
"While the compilation of the current definition is suspended, a
program shall not execute any defining word, :NONAME, or any definition
that allocates dictionary data space."
There may not be enough common practice, though.
I propose a different view. A denotation can occupy (dictionary) space, e.g. >> a string.
"AAPESTRING" reserves space, and leaves a pointer to the data structure.
Like wise
{ DUP * } defines a behaviour and leaves a pointer. There is actually
not much difference.
The notation " ... }" conflicts directly with the Forth Scientific
Library notation for arrays of different types, unless "}" is simply a >delimiter for a parsing word. In your usage, I don't think "{" is a
parsing word.
I remember pointing this out when "{ ... }" was proposed for a newer
version of locals and the notation was changed.
--
Krishna
In article <10r8rgf$i0s4$2@dont-email.me>,
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
On 4/9/26 9:41 AM, albert@spenarnc.xs4all.nl wrote:
In article <10r84nr$9tt0$2@dont-email.me>,....
Krishna Myneni <krishna.myneni@ccreweb.org> wrote:
"While the compilation of the current definition is suspended, a
program shall not execute any defining word, :NONAME, or any definition >>>> that allocates dictionary data space."
There may not be enough common practice, though.
I propose a different view. A denotation can occupy (dictionary) space, e.g.
a string.
"AAPESTRING" reserves space, and leaves a pointer to the data structure. >>> Like wise
{ DUP * } defines a behaviour and leaves a pointer. There is actually
not much difference.
The notation " ... }" conflicts directly with the Forth Scientific
Library notation for arrays of different types, unless "}" is simply a
delimiter for a parsing word. In your usage, I don't think "{" is a
parsing word.
I remember pointing this out when "{ ... }" was proposed for a newer
version of locals and the notation was changed.
We run out of brackets. Maybe abandon ( ) for comments, and use only \ .
Then ( ) is freed for math formulaes.
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of. Why, you ask? Because every name basically returns a pointer - unless we DOES> something different.
Sure, you can have different ways to store a value, but a parsing word? Really? And all of a sudden it should do different things depending on type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler figures out what the most optimal way of handling this retrieval or assignment is. Is the address known at compile time, we handle it like a VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some smart@$$ "TO" even less. What a waste of code.. And what a horrible design.
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of. Why, you ask? Because every name basically returns a pointer - unless we DOES> something different.
Sure, you can have different ways to store a value, but a parsing word? Really? And all of a sudden it should do different things depending on
type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler
figures out what the most optimal way of handling this retrieval or assignment is. Is the address known at compile time, we handle it like a VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some
smart@$$ "TO" even less. What a waste of code.. And what a horrible design.
Hans Bezemer
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of.
Why, you ask? Because every name basically returns a pointer - unless we >DOES> something different.
Sure, you can have different ways to store a value, but a parsing word? >Really? And all of a sudden it should do different things depending on
type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler
figures out what the most optimal way of handling this retrieval or >assignment is. Is the address known at compile time, we handle it like a >VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some
smart@$$ "TO" even less. What a waste of code.. And what a horrible design.
Hans Bezemer
In article <nnd$5fbdae9b$6428335d@7ddbf5ece7eb9625>,
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of.
Why, you ask? Because every name basically returns a pointer - unless we
DOES> something different.
Sure, you can have different ways to store a value, but a parsing word?
Really? And all of a sudden it should do different things depending on
type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler
figures out what the most optimal way of handling this retrieval or
assignment is. Is the address known at compile time, we handle it like a
VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some
smart@$$ "TO" even less. What a waste of code.. And what a horrible design.
In ciforth you can do this:
Prefixes ("recognizers") are supposed to be parsing the remainder
lf tne word, like $DEADBEAT
Abuse follows.
\ In the minimal wordlist, not masking regular @ !
'ONLY >WID CURRENT !
: @ NAME EVALUATE @ ; PREFIX IMMEDIATE
: ! NAME EVALUATE ! ; PREFIX IMMEDIATE
DEFINITINS \ Snap back.
VARIABLE AAP
12 !AAP
@AAP .
12 OK
13 !AAP
@AAP .
13 OK
Just saying.
(I don't recommend this, by the way.)
On 12/04/2026 6:03 am, albert@spenarnc.xs4all.nl wrote:
In article <nnd$5fbdae9b$6428335d@7ddbf5ece7eb9625>,
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:In ciforth you can do this:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of. >>> Why, you ask? Because every name basically returns a pointer - unless we >>> DOES> something different.
Sure, you can have different ways to store a value, but a parsing word?
Really? And all of a sudden it should do different things depending on
type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler
figures out what the most optimal way of handling this retrieval or
assignment is. Is the address known at compile time, we handle it like a >>> VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some
smart@$$ "TO" even less. What a waste of code.. And what a horrible design. >>
Prefixes ("recognizers") are supposed to be parsing the remainder
lf tne word, like $DEADBEAT
Abuse follows.
\ In the minimal wordlist, not masking regular @ !
'ONLY >WID CURRENT !
: @ NAME EVALUATE @ ; PREFIX IMMEDIATE
: ! NAME EVALUATE ! ; PREFIX IMMEDIATE
DEFINITINS \ Snap back.
VARIABLE AAP
12 !AAP
@AAP .
12 OK
13 !AAP
@AAP .
13 OK
Just saying.
(I don't recommend this, by the way.)
AFAICT the majority of HLL's use 'self-fetching' variables. Setting a >variable required something like := which corresponds to forth's TO.
IMO local variables in forth was less about 'stack juggling' than
simulating the 'look and feel' code of other languages. Forth locals
code uses a fair number of TO's. Hugh once suggested locals ought to
have used @ ! . This would have been very forth-like - but completely
misses what locals aficionados were trying to do!
For me at least, VALUEs are an 'almost CONSTANT' in which TO rarely gets >used. Without that distinction I'd find it hard justifying VALUEs - or >indeed know when to use them. By clearly stating their intent and usage, >they provide semantic value to written code and the language in general.
But I'm not sure ANS ever did say - leaving users to muddle it out for >themselves. Locals' use of 'values' and TO only muddied the waters.
On 12/04/2026 6:03 am, albert@spenarnc.xs4all.nl wrote:
In article <nnd$5fbdae9b$6428335d@7ddbf5ece7eb9625>,
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:In ciforth you can do this:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of. >>> Why, you ask? Because every name basically returns a pointer - unless we >>> DOES> something different.
Sure, you can have different ways to store a value, but a parsing word?
Really? And all of a sudden it should do different things depending on
type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler
figures out what the most optimal way of handling this retrieval or
assignment is. Is the address known at compile time, we handle it like a >>> VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some
smart@$$ "TO" even less. What a waste of code.. And what a horrible design. >>
Prefixes ("recognizers") are supposed to be parsing the remainder
lf tne word, like $DEADBEAT
Abuse follows.
\ In the minimal wordlist, not masking regular @ !
'ONLY >WID CURRENT !
: @ NAME EVALUATE @ ; PREFIX IMMEDIATE
: ! NAME EVALUATE ! ; PREFIX IMMEDIATE
DEFINITINS \ Snap back.
VARIABLE AAP
12 !AAP
@AAP .
12 OK
13 !AAP
@AAP .
13 OK
Just saying.
(I don't recommend this, by the way.)
AFAICT the majority of HLL's use 'self-fetching' variables. Setting a variable required something like := which corresponds to forth's TO.
IMO local variables in forth was less about 'stack juggling' than
simulating the 'look and feel' code of other languages.
Without that distinction I'd find it hard justifying VALUEs - or
indeed know when to use them. By clearly stating their intent and usage, they provide semantic value to written code and the language in general.
But I'm not sure ANS ever did say - leaving users to muddle it out for themselves. Locals' use of 'values' and TO only muddied the waters.
For me at least, VALUEs are an 'almost CONSTANT' in which TO rarely
gets used.
AFAICT the majority of HLL's use 'self-fetching' variables.
Forth locals
code uses a fair number of TO's. Hugh once suggested locals ought to
have used @ ! . This would have been very forth-like - but completely misses what locals aficionados were trying to do!
For me at least, VALUEs are an 'almost CONSTANT' in which TO rarely gets
used.
On 12-04-2026 12:13:
For me at least, VALUEs are an 'almost CONSTANT' in which TO rarely gets >>> used.
In a sense, CONSTANTs are *almost* VALUEs:
10 constant ten ok
16 ' ten ! ok
ten . 16 ok
It won't work like that in 4tH (because ' ten would return "10"), but it works fine in most Forths since '78.
Using this definition:
: reconstant state @ if postpone ['] postpone ! else ' ! then ; immediate
.. you could write:
16 RECONSTANT ten
.. and you'd *almost* have a VALUE. Cool, huh? :-)
On 12/04/2026 6:03 am, albert@spenarnc.xs4all.nl wrote:
In article <nnd$5fbdae9b$6428335d@7ddbf5ece7eb9625>,
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 07-04-2026 13:35, albert@spenarnc.xs4all.nl wrote:In ciforth you can do this:
A similar situation applies to "TO must scan". It turns out there
is no standard program that can detect this. It steers implementation
towards a scanning TO.
Frankly, I consider VALUE one of these Forth errors we never got rid of. >>> Why, you ask? Because every name basically returns a pointer - unless we >>> DOES> something different.
Sure, you can have different ways to store a value, but a parsing word?
Really? And all of a sudden it should do different things depending on
type? Yuk!
Personally, I do VALUE and TO. But only for VALUE. And the compiler
figures out what the most optimal way of handling this retrieval or
assignment is. Is the address known at compile time, we handle it like a >>> VALUE, otherwise like a VARIABLE.
And yeah, we have FTO and 2TO. I don't like it - but I like some
smart@$$ "TO" even less. What a waste of code.. And what a horrible design. >>
Prefixes ("recognizers") are supposed to be parsing the remainder
lf tne word, like $DEADBEAT
Abuse follows.
\ In the minimal wordlist, not masking regular @ !
'ONLY >WID CURRENT !
: @ NAME EVALUATE @ ; PREFIX IMMEDIATE
: ! NAME EVALUATE ! ; PREFIX IMMEDIATE
DEFINITINS \ Snap back.
VARIABLE AAP
12 !AAP
@AAP .
12 OK
13 !AAP
@AAP .
13 OK
Just saying.
(I don't recommend this, by the way.)
AFAICT the majority of HLL's use 'self-fetching' variables. Setting a
variable required something like := which corresponds to forth's TO.
IMO local variables in forth was less about 'stack juggling' than
simulating the 'look and feel' code of other languages. Forth locals
code uses a fair number of TO's. Hugh once suggested locals ought to
have used @ ! . This would have been very forth-like - but completely
misses what locals aficionados were trying to do!
For me at least, VALUEs are an 'almost CONSTANT' in which TO rarely gets >used. Without that distinction I'd find it hard justifying VALUEs - or >indeed know when to use them. By clearly stating their intent and usage, >they provide semantic value to written code and the language in general.
But I'm not sure ANS ever did say - leaving users to muddle it out for >themselves. Locals' use of 'values' and TO only muddied the waters.
On 13/04/2026 1:39 am, Hans Bezemer wrote:
On 12-04-2026 12:13:
For me at least, VALUEs are an 'almost CONSTANT' in which TO rarely gets >>>> used.
In a sense, CONSTANTs are *almost* VALUEs:
10 constant ten ok
16 ' ten ! ok
ten . 16 ok
It won't work like that in 4tH (because ' ten would return "10"), but it works fine in most Forths since '78.
It probably needs a >BODY . Forth-in-ROM then no cigar. Inlining compilers?
Do away with CONSTANTs and keep VALUEs ? :)
| Sysop: | DaiTengu |
|---|---|
| Location: | Appleton, WI |
| Users: | 1,113 |
| Nodes: | 10 (0 / 10) |
| Uptime: | 492335:46:10 |
| Calls: | 14,238 |
| Files: | 186,312 |
| D/L today: |
3,563 files (1,160M bytes) |
| Messages: | 2,514,866 |